michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef _GFXALPHARECOVERY_H_ michael@0: #define _GFXALPHARECOVERY_H_ michael@0: michael@0: #include "mozilla/SSE.h" michael@0: #include "gfxTypes.h" michael@0: #include "nsRect.h" michael@0: michael@0: struct nsIntRect; michael@0: class gfxImageSurface; michael@0: michael@0: class gfxAlphaRecovery { michael@0: public: michael@0: /** michael@0: * Some SIMD fast-paths only can be taken if the relative michael@0: * byte-alignment of images' pointers and strides meets certain michael@0: * criteria. Aligning image pointers and strides by michael@0: * |GoodAlignmentLog2()| below will ensure that fast-paths aren't michael@0: * skipped because of misalignment. Fast-paths may still be taken michael@0: * even if GoodAlignmentLog2() is not met, in some conditions. michael@0: */ michael@0: static uint32_t GoodAlignmentLog2() { return 4; /* for SSE2 */ } michael@0: michael@0: /* Given two surfaces of equal size with the same rendering, one onto a michael@0: * black background and the other onto white, recovers alpha values from michael@0: * the difference and sets the alpha values on the black surface. michael@0: * The surfaces must have format RGB24 or ARGB32. michael@0: * Returns true on success. michael@0: */ michael@0: static bool RecoverAlpha (gfxImageSurface *blackSurface, michael@0: const gfxImageSurface *whiteSurface); michael@0: michael@0: #ifdef MOZILLA_MAY_SUPPORT_SSE2 michael@0: /* This does the same as the previous function, but uses SSE2 michael@0: * optimizations. Usually this should not be called directly. Be sure to michael@0: * check mozilla::supports_sse2() before calling this function. michael@0: */ michael@0: static bool RecoverAlphaSSE2 (gfxImageSurface *blackSurface, michael@0: const gfxImageSurface *whiteSurface); michael@0: michael@0: /** michael@0: * A common use-case for alpha recovery is to paint into a michael@0: * temporary "white image", then paint onto a subrect of the michael@0: * surface, the "black image", into which alpha-recovered pixels michael@0: * are eventually to be written. This function returns a rect michael@0: * aligned so that recovering alpha for that rect will hit SIMD michael@0: * fast-paths, if possible. It's not always possible to align michael@0: * |aRect| so that fast-paths will be taken. michael@0: * michael@0: * The returned rect is always a superset of |aRect|. michael@0: */ michael@0: static nsIntRect AlignRectForSubimageRecovery(const nsIntRect& aRect, michael@0: gfxImageSurface* aSurface); michael@0: #else michael@0: static nsIntRect AlignRectForSubimageRecovery(const nsIntRect& aRect, michael@0: gfxImageSurface*) michael@0: { return aRect; } michael@0: #endif michael@0: michael@0: /** from cairo-xlib-utils.c, modified */ michael@0: /** michael@0: * Given the RGB data for two image surfaces, one a source image composited michael@0: * with OVER onto a black background, and one a source image composited with michael@0: * OVER onto a white background, reconstruct the original image data into michael@0: * black_data. michael@0: * michael@0: * Consider a single color channel and a given pixel. Suppose the original michael@0: * premultiplied color value was C and the alpha value was A. Let the final michael@0: * on-black color be B and the final on-white color be W. All values range michael@0: * over 0-255. michael@0: * michael@0: * Then B=C and W=(255*(255 - A) + C*255)/255. Solving for A, we get michael@0: * A=255 - (W - C). Therefore it suffices to leave the black_data color michael@0: * data alone and set the alpha values using that simple formula. It shouldn't michael@0: * matter what color channel we pick for the alpha computation, but we'll michael@0: * pick green because if we went through a color channel downsample the green michael@0: * bits are likely to be the most accurate. michael@0: * michael@0: * This function needs to be in the header file since it's used by both michael@0: * gfxRecoverAlpha.cpp and gfxRecoverAlphaSSE2.cpp. michael@0: */ michael@0: michael@0: static inline uint32_t michael@0: RecoverPixel(uint32_t black, uint32_t white) michael@0: { michael@0: const uint32_t GREEN_MASK = 0x0000FF00; michael@0: const uint32_t ALPHA_MASK = 0xFF000000; michael@0: michael@0: /* |diff| here is larger when the source image pixel is more transparent. michael@0: If both renderings are from the same source image composited with OVER, michael@0: then the color values on white will always be greater than those on michael@0: black, so |diff| would not overflow. However, overflow may happen, for michael@0: example, when a plugin plays a video and the image is rapidly changing. michael@0: If there is overflow, then behave as if we limit to the difference to michael@0: >= 0, which will make the rendering opaque. (Without this overflow michael@0: will make the rendering transparent.) */ michael@0: uint32_t diff = (white & GREEN_MASK) - (black & GREEN_MASK); michael@0: /* |diff| is 0xFFFFxx00 on overflow and 0x0000xx00 otherwise, so use this michael@0: to limit the transparency. */ michael@0: uint32_t limit = diff & ALPHA_MASK; michael@0: /* The alpha bits of the result */ michael@0: uint32_t alpha = (ALPHA_MASK - (diff << 16)) | limit; michael@0: michael@0: return alpha | (black & ~ALPHA_MASK); michael@0: } michael@0: }; michael@0: michael@0: #endif /* _GFXALPHARECOVERY_H_ */