Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | // Main header first: |
michael@0 | 7 | #include "nsSVGMaskFrame.h" |
michael@0 | 8 | |
michael@0 | 9 | // Keep others in (case-insensitive) order: |
michael@0 | 10 | #include "gfxContext.h" |
michael@0 | 11 | #include "gfxImageSurface.h" |
michael@0 | 12 | #include "nsRenderingContext.h" |
michael@0 | 13 | #include "nsSVGEffects.h" |
michael@0 | 14 | #include "mozilla/dom/SVGMaskElement.h" |
michael@0 | 15 | |
michael@0 | 16 | using namespace mozilla::dom; |
michael@0 | 17 | |
michael@0 | 18 | /** |
michael@0 | 19 | * Byte offsets of channels in a native packed gfxColor or cairo image surface. |
michael@0 | 20 | */ |
michael@0 | 21 | #ifdef IS_BIG_ENDIAN |
michael@0 | 22 | #define GFX_ARGB32_OFFSET_A 0 |
michael@0 | 23 | #define GFX_ARGB32_OFFSET_R 1 |
michael@0 | 24 | #define GFX_ARGB32_OFFSET_G 2 |
michael@0 | 25 | #define GFX_ARGB32_OFFSET_B 3 |
michael@0 | 26 | #else |
michael@0 | 27 | #define GFX_ARGB32_OFFSET_A 3 |
michael@0 | 28 | #define GFX_ARGB32_OFFSET_R 2 |
michael@0 | 29 | #define GFX_ARGB32_OFFSET_G 1 |
michael@0 | 30 | #define GFX_ARGB32_OFFSET_B 0 |
michael@0 | 31 | #endif |
michael@0 | 32 | |
michael@0 | 33 | // c = n / 255 |
michael@0 | 34 | // c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5 |
michael@0 | 35 | static const uint8_t gsRGBToLinearRGBMap[256] = { |
michael@0 | 36 | 0, 0, 0, 0, 0, 0, 0, 1, |
michael@0 | 37 | 1, 1, 1, 1, 1, 1, 1, 1, |
michael@0 | 38 | 1, 1, 2, 2, 2, 2, 2, 2, |
michael@0 | 39 | 2, 2, 3, 3, 3, 3, 3, 3, |
michael@0 | 40 | 4, 4, 4, 4, 4, 5, 5, 5, |
michael@0 | 41 | 5, 6, 6, 6, 6, 7, 7, 7, |
michael@0 | 42 | 8, 8, 8, 8, 9, 9, 9, 10, |
michael@0 | 43 | 10, 10, 11, 11, 12, 12, 12, 13, |
michael@0 | 44 | 13, 13, 14, 14, 15, 15, 16, 16, |
michael@0 | 45 | 17, 17, 17, 18, 18, 19, 19, 20, |
michael@0 | 46 | 20, 21, 22, 22, 23, 23, 24, 24, |
michael@0 | 47 | 25, 25, 26, 27, 27, 28, 29, 29, |
michael@0 | 48 | 30, 30, 31, 32, 32, 33, 34, 35, |
michael@0 | 49 | 35, 36, 37, 37, 38, 39, 40, 41, |
michael@0 | 50 | 41, 42, 43, 44, 45, 45, 46, 47, |
michael@0 | 51 | 48, 49, 50, 51, 51, 52, 53, 54, |
michael@0 | 52 | 55, 56, 57, 58, 59, 60, 61, 62, |
michael@0 | 53 | 63, 64, 65, 66, 67, 68, 69, 70, |
michael@0 | 54 | 71, 72, 73, 74, 76, 77, 78, 79, |
michael@0 | 55 | 80, 81, 82, 84, 85, 86, 87, 88, |
michael@0 | 56 | 90, 91, 92, 93, 95, 96, 97, 99, |
michael@0 | 57 | 100, 101, 103, 104, 105, 107, 108, 109, |
michael@0 | 58 | 111, 112, 114, 115, 116, 118, 119, 121, |
michael@0 | 59 | 122, 124, 125, 127, 128, 130, 131, 133, |
michael@0 | 60 | 134, 136, 138, 139, 141, 142, 144, 146, |
michael@0 | 61 | 147, 149, 151, 152, 154, 156, 157, 159, |
michael@0 | 62 | 161, 163, 164, 166, 168, 170, 171, 173, |
michael@0 | 63 | 175, 177, 179, 181, 183, 184, 186, 188, |
michael@0 | 64 | 190, 192, 194, 196, 198, 200, 202, 204, |
michael@0 | 65 | 206, 208, 210, 212, 214, 216, 218, 220, |
michael@0 | 66 | 222, 224, 226, 229, 231, 233, 235, 237, |
michael@0 | 67 | 239, 242, 244, 246, 248, 250, 253, 255 |
michael@0 | 68 | }; |
michael@0 | 69 | |
michael@0 | 70 | static void |
michael@0 | 71 | ComputesRGBLuminanceMask(uint8_t *aData, |
michael@0 | 72 | int32_t aStride, |
michael@0 | 73 | const nsIntRect &aRect, |
michael@0 | 74 | float aOpacity) |
michael@0 | 75 | { |
michael@0 | 76 | for (int32_t y = aRect.y; y < aRect.YMost(); y++) { |
michael@0 | 77 | for (int32_t x = aRect.x; x < aRect.XMost(); x++) { |
michael@0 | 78 | uint8_t *pixel = aData + aStride * y + 4 * x; |
michael@0 | 79 | uint8_t a = pixel[GFX_ARGB32_OFFSET_A]; |
michael@0 | 80 | |
michael@0 | 81 | uint8_t luminance; |
michael@0 | 82 | if (a) { |
michael@0 | 83 | /* sRGB -> intensity (unpremultiply cancels out the |
michael@0 | 84 | * (a/255.0) multiplication with aOpacity */ |
michael@0 | 85 | luminance = |
michael@0 | 86 | static_cast<uint8_t> |
michael@0 | 87 | ((pixel[GFX_ARGB32_OFFSET_R] * 0.2125 + |
michael@0 | 88 | pixel[GFX_ARGB32_OFFSET_G] * 0.7154 + |
michael@0 | 89 | pixel[GFX_ARGB32_OFFSET_B] * 0.0721) * |
michael@0 | 90 | aOpacity); |
michael@0 | 91 | } else { |
michael@0 | 92 | luminance = 0; |
michael@0 | 93 | } |
michael@0 | 94 | memset(pixel, luminance, 4); |
michael@0 | 95 | } |
michael@0 | 96 | } |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | static void |
michael@0 | 100 | ComputeLinearRGBLuminanceMask(uint8_t *aData, |
michael@0 | 101 | int32_t aStride, |
michael@0 | 102 | const nsIntRect &aRect, |
michael@0 | 103 | float aOpacity) |
michael@0 | 104 | { |
michael@0 | 105 | for (int32_t y = aRect.y; y < aRect.YMost(); y++) { |
michael@0 | 106 | for (int32_t x = aRect.x; x < aRect.XMost(); x++) { |
michael@0 | 107 | uint8_t *pixel = aData + aStride * y + 4 * x; |
michael@0 | 108 | uint8_t a = pixel[GFX_ARGB32_OFFSET_A]; |
michael@0 | 109 | |
michael@0 | 110 | uint8_t luminance; |
michael@0 | 111 | // unpremultiply |
michael@0 | 112 | if (a) { |
michael@0 | 113 | if (a != 255) { |
michael@0 | 114 | pixel[GFX_ARGB32_OFFSET_B] = |
michael@0 | 115 | (255 * pixel[GFX_ARGB32_OFFSET_B]) / a; |
michael@0 | 116 | pixel[GFX_ARGB32_OFFSET_G] = |
michael@0 | 117 | (255 * pixel[GFX_ARGB32_OFFSET_G]) / a; |
michael@0 | 118 | pixel[GFX_ARGB32_OFFSET_R] = |
michael@0 | 119 | (255 * pixel[GFX_ARGB32_OFFSET_R]) / a; |
michael@0 | 120 | } |
michael@0 | 121 | |
michael@0 | 122 | /* sRGB -> linearRGB -> intensity */ |
michael@0 | 123 | luminance = |
michael@0 | 124 | static_cast<uint8_t> |
michael@0 | 125 | ((gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_R]] * |
michael@0 | 126 | 0.2125 + |
michael@0 | 127 | gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_G]] * |
michael@0 | 128 | 0.7154 + |
michael@0 | 129 | gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_B]] * |
michael@0 | 130 | 0.0721) * (a / 255.0) * aOpacity); |
michael@0 | 131 | } else { |
michael@0 | 132 | luminance = 0; |
michael@0 | 133 | } |
michael@0 | 134 | memset(pixel, luminance, 4); |
michael@0 | 135 | } |
michael@0 | 136 | } |
michael@0 | 137 | } |
michael@0 | 138 | |
michael@0 | 139 | static void |
michael@0 | 140 | ComputeAlphaMask(uint8_t *aData, |
michael@0 | 141 | int32_t aStride, |
michael@0 | 142 | const nsIntRect &aRect, |
michael@0 | 143 | float aOpacity) |
michael@0 | 144 | { |
michael@0 | 145 | for (int32_t y = aRect.y; y < aRect.YMost(); y++) { |
michael@0 | 146 | for (int32_t x = aRect.x; x < aRect.XMost(); x++) { |
michael@0 | 147 | uint8_t *pixel = aData + aStride * y + 4 * x; |
michael@0 | 148 | uint8_t luminance = pixel[GFX_ARGB32_OFFSET_A] * aOpacity; |
michael@0 | 149 | memset(pixel, luminance, 4); |
michael@0 | 150 | } |
michael@0 | 151 | } |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | //---------------------------------------------------------------------- |
michael@0 | 155 | // Implementation |
michael@0 | 156 | |
michael@0 | 157 | nsIFrame* |
michael@0 | 158 | NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
michael@0 | 159 | { |
michael@0 | 160 | return new (aPresShell) nsSVGMaskFrame(aContext); |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | NS_IMPL_FRAMEARENA_HELPERS(nsSVGMaskFrame) |
michael@0 | 164 | |
michael@0 | 165 | already_AddRefed<gfxPattern> |
michael@0 | 166 | nsSVGMaskFrame::ComputeMaskAlpha(nsRenderingContext *aContext, |
michael@0 | 167 | nsIFrame* aParent, |
michael@0 | 168 | const gfxMatrix &aMatrix, |
michael@0 | 169 | float aOpacity) |
michael@0 | 170 | { |
michael@0 | 171 | // If the flag is set when we get here, it means this mask frame |
michael@0 | 172 | // has already been used painting the current mask, and the document |
michael@0 | 173 | // has a mask reference loop. |
michael@0 | 174 | if (mInUse) { |
michael@0 | 175 | NS_WARNING("Mask loop detected!"); |
michael@0 | 176 | return nullptr; |
michael@0 | 177 | } |
michael@0 | 178 | AutoMaskReferencer maskRef(this); |
michael@0 | 179 | |
michael@0 | 180 | SVGMaskElement *mask = static_cast<SVGMaskElement*>(mContent); |
michael@0 | 181 | |
michael@0 | 182 | uint16_t units = |
michael@0 | 183 | mask->mEnumAttributes[SVGMaskElement::MASKUNITS].GetAnimValue(); |
michael@0 | 184 | gfxRect bbox; |
michael@0 | 185 | if (units == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { |
michael@0 | 186 | bbox = nsSVGUtils::GetBBox(aParent); |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | gfxRect maskArea = nsSVGUtils::GetRelativeRect(units, |
michael@0 | 190 | &mask->mLengthAttributes[SVGMaskElement::ATTR_X], bbox, aParent); |
michael@0 | 191 | |
michael@0 | 192 | gfxContext *gfx = aContext->ThebesContext(); |
michael@0 | 193 | |
michael@0 | 194 | // Get the clip extents in device space: |
michael@0 | 195 | gfx->Save(); |
michael@0 | 196 | nsSVGUtils::SetClipRect(gfx, aMatrix, maskArea); |
michael@0 | 197 | gfx->IdentityMatrix(); |
michael@0 | 198 | gfxRect clipExtents = gfx->GetClipExtents(); |
michael@0 | 199 | clipExtents.RoundOut(); |
michael@0 | 200 | gfx->Restore(); |
michael@0 | 201 | |
michael@0 | 202 | bool resultOverflows; |
michael@0 | 203 | gfxIntSize surfaceSize = |
michael@0 | 204 | nsSVGUtils::ConvertToSurfaceSize(gfxSize(clipExtents.Width(), |
michael@0 | 205 | clipExtents.Height()), |
michael@0 | 206 | &resultOverflows); |
michael@0 | 207 | |
michael@0 | 208 | // 0 disables mask, < 0 is an error |
michael@0 | 209 | if (surfaceSize.width <= 0 || surfaceSize.height <= 0) |
michael@0 | 210 | return nullptr; |
michael@0 | 211 | |
michael@0 | 212 | if (resultOverflows) |
michael@0 | 213 | return nullptr; |
michael@0 | 214 | |
michael@0 | 215 | nsRefPtr<gfxImageSurface> image = |
michael@0 | 216 | new gfxImageSurface(surfaceSize, gfxImageFormat::ARGB32); |
michael@0 | 217 | if (!image || image->CairoStatus()) |
michael@0 | 218 | return nullptr; |
michael@0 | 219 | |
michael@0 | 220 | // We would like to use gfxImageSurface::SetDeviceOffset() to position |
michael@0 | 221 | // 'image'. However, we need to set the same matrix on the temporary context |
michael@0 | 222 | // and pattern that we create below as is currently set on 'gfx'. |
michael@0 | 223 | // Unfortunately, any device offset set by SetDeviceOffset() is affected by |
michael@0 | 224 | // the transform passed to the SetMatrix() calls, so to avoid that we account |
michael@0 | 225 | // for the device offset in the transform rather than use SetDeviceOffset(). |
michael@0 | 226 | gfxMatrix matrix = |
michael@0 | 227 | gfx->CurrentMatrix() * gfxMatrix().Translate(-clipExtents.TopLeft()); |
michael@0 | 228 | |
michael@0 | 229 | nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext); |
michael@0 | 230 | tmpCtx->Init(this->PresContext()->DeviceContext(), image); |
michael@0 | 231 | tmpCtx->ThebesContext()->SetMatrix(matrix); |
michael@0 | 232 | |
michael@0 | 233 | mMaskParent = aParent; |
michael@0 | 234 | if (mMaskParentMatrix) { |
michael@0 | 235 | *mMaskParentMatrix = aMatrix; |
michael@0 | 236 | } else { |
michael@0 | 237 | mMaskParentMatrix = new gfxMatrix(aMatrix); |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | for (nsIFrame* kid = mFrames.FirstChild(); kid; |
michael@0 | 241 | kid = kid->GetNextSibling()) { |
michael@0 | 242 | // The CTM of each frame referencing us can be different |
michael@0 | 243 | nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); |
michael@0 | 244 | if (SVGFrame) { |
michael@0 | 245 | SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); |
michael@0 | 246 | } |
michael@0 | 247 | nsSVGUtils::PaintFrameWithEffects(tmpCtx, nullptr, kid); |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | uint8_t *data = image->Data(); |
michael@0 | 251 | int32_t stride = image->Stride(); |
michael@0 | 252 | nsIntRect rect(0, 0, surfaceSize.width, surfaceSize.height); |
michael@0 | 253 | |
michael@0 | 254 | if (StyleSVGReset()->mMaskType == NS_STYLE_MASK_TYPE_LUMINANCE) { |
michael@0 | 255 | if (StyleSVG()->mColorInterpolation == |
michael@0 | 256 | NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) { |
michael@0 | 257 | ComputeLinearRGBLuminanceMask(data, stride, rect, aOpacity); |
michael@0 | 258 | } else { |
michael@0 | 259 | ComputesRGBLuminanceMask(data, stride, rect, aOpacity); |
michael@0 | 260 | } |
michael@0 | 261 | } else { |
michael@0 | 262 | ComputeAlphaMask(data, stride, rect, aOpacity); |
michael@0 | 263 | } |
michael@0 | 264 | |
michael@0 | 265 | nsRefPtr<gfxPattern> retval = new gfxPattern(image); |
michael@0 | 266 | retval->SetMatrix(matrix); |
michael@0 | 267 | return retval.forget(); |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | nsresult |
michael@0 | 271 | nsSVGMaskFrame::AttributeChanged(int32_t aNameSpaceID, |
michael@0 | 272 | nsIAtom* aAttribute, |
michael@0 | 273 | int32_t aModType) |
michael@0 | 274 | { |
michael@0 | 275 | if (aNameSpaceID == kNameSpaceID_None && |
michael@0 | 276 | (aAttribute == nsGkAtoms::x || |
michael@0 | 277 | aAttribute == nsGkAtoms::y || |
michael@0 | 278 | aAttribute == nsGkAtoms::width || |
michael@0 | 279 | aAttribute == nsGkAtoms::height|| |
michael@0 | 280 | aAttribute == nsGkAtoms::maskUnits || |
michael@0 | 281 | aAttribute == nsGkAtoms::maskContentUnits)) { |
michael@0 | 282 | nsSVGEffects::InvalidateDirectRenderingObservers(this); |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | return nsSVGMaskFrameBase::AttributeChanged(aNameSpaceID, |
michael@0 | 286 | aAttribute, aModType); |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | #ifdef DEBUG |
michael@0 | 290 | void |
michael@0 | 291 | nsSVGMaskFrame::Init(nsIContent* aContent, |
michael@0 | 292 | nsIFrame* aParent, |
michael@0 | 293 | nsIFrame* aPrevInFlow) |
michael@0 | 294 | { |
michael@0 | 295 | NS_ASSERTION(aContent->IsSVG(nsGkAtoms::mask), |
michael@0 | 296 | "Content is not an SVG mask"); |
michael@0 | 297 | |
michael@0 | 298 | nsSVGMaskFrameBase::Init(aContent, aParent, aPrevInFlow); |
michael@0 | 299 | } |
michael@0 | 300 | #endif /* DEBUG */ |
michael@0 | 301 | |
michael@0 | 302 | nsIAtom * |
michael@0 | 303 | nsSVGMaskFrame::GetType() const |
michael@0 | 304 | { |
michael@0 | 305 | return nsGkAtoms::svgMaskFrame; |
michael@0 | 306 | } |
michael@0 | 307 | |
michael@0 | 308 | gfxMatrix |
michael@0 | 309 | nsSVGMaskFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) |
michael@0 | 310 | { |
michael@0 | 311 | NS_ASSERTION(mMaskParentMatrix, "null parent matrix"); |
michael@0 | 312 | |
michael@0 | 313 | SVGMaskElement *mask = static_cast<SVGMaskElement*>(mContent); |
michael@0 | 314 | |
michael@0 | 315 | return nsSVGUtils::AdjustMatrixForUnits( |
michael@0 | 316 | mMaskParentMatrix ? *mMaskParentMatrix : gfxMatrix(), |
michael@0 | 317 | &mask->mEnumAttributes[SVGMaskElement::MASKCONTENTUNITS], |
michael@0 | 318 | mMaskParent); |
michael@0 | 319 | } |
michael@0 | 320 |