|
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #ifndef _GFXALPHARECOVERY_H_ |
|
7 #define _GFXALPHARECOVERY_H_ |
|
8 |
|
9 #include "mozilla/SSE.h" |
|
10 #include "gfxTypes.h" |
|
11 #include "nsRect.h" |
|
12 |
|
13 struct nsIntRect; |
|
14 class gfxImageSurface; |
|
15 |
|
16 class gfxAlphaRecovery { |
|
17 public: |
|
18 /** |
|
19 * Some SIMD fast-paths only can be taken if the relative |
|
20 * byte-alignment of images' pointers and strides meets certain |
|
21 * criteria. Aligning image pointers and strides by |
|
22 * |GoodAlignmentLog2()| below will ensure that fast-paths aren't |
|
23 * skipped because of misalignment. Fast-paths may still be taken |
|
24 * even if GoodAlignmentLog2() is not met, in some conditions. |
|
25 */ |
|
26 static uint32_t GoodAlignmentLog2() { return 4; /* for SSE2 */ } |
|
27 |
|
28 /* Given two surfaces of equal size with the same rendering, one onto a |
|
29 * black background and the other onto white, recovers alpha values from |
|
30 * the difference and sets the alpha values on the black surface. |
|
31 * The surfaces must have format RGB24 or ARGB32. |
|
32 * Returns true on success. |
|
33 */ |
|
34 static bool RecoverAlpha (gfxImageSurface *blackSurface, |
|
35 const gfxImageSurface *whiteSurface); |
|
36 |
|
37 #ifdef MOZILLA_MAY_SUPPORT_SSE2 |
|
38 /* This does the same as the previous function, but uses SSE2 |
|
39 * optimizations. Usually this should not be called directly. Be sure to |
|
40 * check mozilla::supports_sse2() before calling this function. |
|
41 */ |
|
42 static bool RecoverAlphaSSE2 (gfxImageSurface *blackSurface, |
|
43 const gfxImageSurface *whiteSurface); |
|
44 |
|
45 /** |
|
46 * A common use-case for alpha recovery is to paint into a |
|
47 * temporary "white image", then paint onto a subrect of the |
|
48 * surface, the "black image", into which alpha-recovered pixels |
|
49 * are eventually to be written. This function returns a rect |
|
50 * aligned so that recovering alpha for that rect will hit SIMD |
|
51 * fast-paths, if possible. It's not always possible to align |
|
52 * |aRect| so that fast-paths will be taken. |
|
53 * |
|
54 * The returned rect is always a superset of |aRect|. |
|
55 */ |
|
56 static nsIntRect AlignRectForSubimageRecovery(const nsIntRect& aRect, |
|
57 gfxImageSurface* aSurface); |
|
58 #else |
|
59 static nsIntRect AlignRectForSubimageRecovery(const nsIntRect& aRect, |
|
60 gfxImageSurface*) |
|
61 { return aRect; } |
|
62 #endif |
|
63 |
|
64 /** from cairo-xlib-utils.c, modified */ |
|
65 /** |
|
66 * Given the RGB data for two image surfaces, one a source image composited |
|
67 * with OVER onto a black background, and one a source image composited with |
|
68 * OVER onto a white background, reconstruct the original image data into |
|
69 * black_data. |
|
70 * |
|
71 * Consider a single color channel and a given pixel. Suppose the original |
|
72 * premultiplied color value was C and the alpha value was A. Let the final |
|
73 * on-black color be B and the final on-white color be W. All values range |
|
74 * over 0-255. |
|
75 * |
|
76 * Then B=C and W=(255*(255 - A) + C*255)/255. Solving for A, we get |
|
77 * A=255 - (W - C). Therefore it suffices to leave the black_data color |
|
78 * data alone and set the alpha values using that simple formula. It shouldn't |
|
79 * matter what color channel we pick for the alpha computation, but we'll |
|
80 * pick green because if we went through a color channel downsample the green |
|
81 * bits are likely to be the most accurate. |
|
82 * |
|
83 * This function needs to be in the header file since it's used by both |
|
84 * gfxRecoverAlpha.cpp and gfxRecoverAlphaSSE2.cpp. |
|
85 */ |
|
86 |
|
87 static inline uint32_t |
|
88 RecoverPixel(uint32_t black, uint32_t white) |
|
89 { |
|
90 const uint32_t GREEN_MASK = 0x0000FF00; |
|
91 const uint32_t ALPHA_MASK = 0xFF000000; |
|
92 |
|
93 /* |diff| here is larger when the source image pixel is more transparent. |
|
94 If both renderings are from the same source image composited with OVER, |
|
95 then the color values on white will always be greater than those on |
|
96 black, so |diff| would not overflow. However, overflow may happen, for |
|
97 example, when a plugin plays a video and the image is rapidly changing. |
|
98 If there is overflow, then behave as if we limit to the difference to |
|
99 >= 0, which will make the rendering opaque. (Without this overflow |
|
100 will make the rendering transparent.) */ |
|
101 uint32_t diff = (white & GREEN_MASK) - (black & GREEN_MASK); |
|
102 /* |diff| is 0xFFFFxx00 on overflow and 0x0000xx00 otherwise, so use this |
|
103 to limit the transparency. */ |
|
104 uint32_t limit = diff & ALPHA_MASK; |
|
105 /* The alpha bits of the result */ |
|
106 uint32_t alpha = (ALPHA_MASK - (diff << 16)) | limit; |
|
107 |
|
108 return alpha | (black & ~ALPHA_MASK); |
|
109 } |
|
110 }; |
|
111 |
|
112 #endif /* _GFXALPHARECOVERY_H_ */ |