1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsSVGMaskFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,320 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// Main header first: 1.10 +#include "nsSVGMaskFrame.h" 1.11 + 1.12 +// Keep others in (case-insensitive) order: 1.13 +#include "gfxContext.h" 1.14 +#include "gfxImageSurface.h" 1.15 +#include "nsRenderingContext.h" 1.16 +#include "nsSVGEffects.h" 1.17 +#include "mozilla/dom/SVGMaskElement.h" 1.18 + 1.19 +using namespace mozilla::dom; 1.20 + 1.21 +/** 1.22 + * Byte offsets of channels in a native packed gfxColor or cairo image surface. 1.23 + */ 1.24 +#ifdef IS_BIG_ENDIAN 1.25 +#define GFX_ARGB32_OFFSET_A 0 1.26 +#define GFX_ARGB32_OFFSET_R 1 1.27 +#define GFX_ARGB32_OFFSET_G 2 1.28 +#define GFX_ARGB32_OFFSET_B 3 1.29 +#else 1.30 +#define GFX_ARGB32_OFFSET_A 3 1.31 +#define GFX_ARGB32_OFFSET_R 2 1.32 +#define GFX_ARGB32_OFFSET_G 1 1.33 +#define GFX_ARGB32_OFFSET_B 0 1.34 +#endif 1.35 + 1.36 +// c = n / 255 1.37 +// c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)) * 255 + 0.5 1.38 +static const uint8_t gsRGBToLinearRGBMap[256] = { 1.39 + 0, 0, 0, 0, 0, 0, 0, 1, 1.40 + 1, 1, 1, 1, 1, 1, 1, 1, 1.41 + 1, 1, 2, 2, 2, 2, 2, 2, 1.42 + 2, 2, 3, 3, 3, 3, 3, 3, 1.43 + 4, 4, 4, 4, 4, 5, 5, 5, 1.44 + 5, 6, 6, 6, 6, 7, 7, 7, 1.45 + 8, 8, 8, 8, 9, 9, 9, 10, 1.46 + 10, 10, 11, 11, 12, 12, 12, 13, 1.47 + 13, 13, 14, 14, 15, 15, 16, 16, 1.48 + 17, 17, 17, 18, 18, 19, 19, 20, 1.49 + 20, 21, 22, 22, 23, 23, 24, 24, 1.50 + 25, 25, 26, 27, 27, 28, 29, 29, 1.51 + 30, 30, 31, 32, 32, 33, 34, 35, 1.52 + 35, 36, 37, 37, 38, 39, 40, 41, 1.53 + 41, 42, 43, 44, 45, 45, 46, 47, 1.54 + 48, 49, 50, 51, 51, 52, 53, 54, 1.55 + 55, 56, 57, 58, 59, 60, 61, 62, 1.56 + 63, 64, 65, 66, 67, 68, 69, 70, 1.57 + 71, 72, 73, 74, 76, 77, 78, 79, 1.58 + 80, 81, 82, 84, 85, 86, 87, 88, 1.59 + 90, 91, 92, 93, 95, 96, 97, 99, 1.60 +100, 101, 103, 104, 105, 107, 108, 109, 1.61 +111, 112, 114, 115, 116, 118, 119, 121, 1.62 +122, 124, 125, 127, 128, 130, 131, 133, 1.63 +134, 136, 138, 139, 141, 142, 144, 146, 1.64 +147, 149, 151, 152, 154, 156, 157, 159, 1.65 +161, 163, 164, 166, 168, 170, 171, 173, 1.66 +175, 177, 179, 181, 183, 184, 186, 188, 1.67 +190, 192, 194, 196, 198, 200, 202, 204, 1.68 +206, 208, 210, 212, 214, 216, 218, 220, 1.69 +222, 224, 226, 229, 231, 233, 235, 237, 1.70 +239, 242, 244, 246, 248, 250, 253, 255 1.71 +}; 1.72 + 1.73 +static void 1.74 +ComputesRGBLuminanceMask(uint8_t *aData, 1.75 + int32_t aStride, 1.76 + const nsIntRect &aRect, 1.77 + float aOpacity) 1.78 +{ 1.79 + for (int32_t y = aRect.y; y < aRect.YMost(); y++) { 1.80 + for (int32_t x = aRect.x; x < aRect.XMost(); x++) { 1.81 + uint8_t *pixel = aData + aStride * y + 4 * x; 1.82 + uint8_t a = pixel[GFX_ARGB32_OFFSET_A]; 1.83 + 1.84 + uint8_t luminance; 1.85 + if (a) { 1.86 + /* sRGB -> intensity (unpremultiply cancels out the 1.87 + * (a/255.0) multiplication with aOpacity */ 1.88 + luminance = 1.89 + static_cast<uint8_t> 1.90 + ((pixel[GFX_ARGB32_OFFSET_R] * 0.2125 + 1.91 + pixel[GFX_ARGB32_OFFSET_G] * 0.7154 + 1.92 + pixel[GFX_ARGB32_OFFSET_B] * 0.0721) * 1.93 + aOpacity); 1.94 + } else { 1.95 + luminance = 0; 1.96 + } 1.97 + memset(pixel, luminance, 4); 1.98 + } 1.99 + } 1.100 +} 1.101 + 1.102 +static void 1.103 +ComputeLinearRGBLuminanceMask(uint8_t *aData, 1.104 + int32_t aStride, 1.105 + const nsIntRect &aRect, 1.106 + float aOpacity) 1.107 +{ 1.108 + for (int32_t y = aRect.y; y < aRect.YMost(); y++) { 1.109 + for (int32_t x = aRect.x; x < aRect.XMost(); x++) { 1.110 + uint8_t *pixel = aData + aStride * y + 4 * x; 1.111 + uint8_t a = pixel[GFX_ARGB32_OFFSET_A]; 1.112 + 1.113 + uint8_t luminance; 1.114 + // unpremultiply 1.115 + if (a) { 1.116 + if (a != 255) { 1.117 + pixel[GFX_ARGB32_OFFSET_B] = 1.118 + (255 * pixel[GFX_ARGB32_OFFSET_B]) / a; 1.119 + pixel[GFX_ARGB32_OFFSET_G] = 1.120 + (255 * pixel[GFX_ARGB32_OFFSET_G]) / a; 1.121 + pixel[GFX_ARGB32_OFFSET_R] = 1.122 + (255 * pixel[GFX_ARGB32_OFFSET_R]) / a; 1.123 + } 1.124 + 1.125 + /* sRGB -> linearRGB -> intensity */ 1.126 + luminance = 1.127 + static_cast<uint8_t> 1.128 + ((gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_R]] * 1.129 + 0.2125 + 1.130 + gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_G]] * 1.131 + 0.7154 + 1.132 + gsRGBToLinearRGBMap[pixel[GFX_ARGB32_OFFSET_B]] * 1.133 + 0.0721) * (a / 255.0) * aOpacity); 1.134 + } else { 1.135 + luminance = 0; 1.136 + } 1.137 + memset(pixel, luminance, 4); 1.138 + } 1.139 + } 1.140 +} 1.141 + 1.142 +static void 1.143 +ComputeAlphaMask(uint8_t *aData, 1.144 + int32_t aStride, 1.145 + const nsIntRect &aRect, 1.146 + float aOpacity) 1.147 +{ 1.148 + for (int32_t y = aRect.y; y < aRect.YMost(); y++) { 1.149 + for (int32_t x = aRect.x; x < aRect.XMost(); x++) { 1.150 + uint8_t *pixel = aData + aStride * y + 4 * x; 1.151 + uint8_t luminance = pixel[GFX_ARGB32_OFFSET_A] * aOpacity; 1.152 + memset(pixel, luminance, 4); 1.153 + } 1.154 + } 1.155 +} 1.156 + 1.157 +//---------------------------------------------------------------------- 1.158 +// Implementation 1.159 + 1.160 +nsIFrame* 1.161 +NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.162 +{ 1.163 + return new (aPresShell) nsSVGMaskFrame(aContext); 1.164 +} 1.165 + 1.166 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGMaskFrame) 1.167 + 1.168 +already_AddRefed<gfxPattern> 1.169 +nsSVGMaskFrame::ComputeMaskAlpha(nsRenderingContext *aContext, 1.170 + nsIFrame* aParent, 1.171 + const gfxMatrix &aMatrix, 1.172 + float aOpacity) 1.173 +{ 1.174 + // If the flag is set when we get here, it means this mask frame 1.175 + // has already been used painting the current mask, and the document 1.176 + // has a mask reference loop. 1.177 + if (mInUse) { 1.178 + NS_WARNING("Mask loop detected!"); 1.179 + return nullptr; 1.180 + } 1.181 + AutoMaskReferencer maskRef(this); 1.182 + 1.183 + SVGMaskElement *mask = static_cast<SVGMaskElement*>(mContent); 1.184 + 1.185 + uint16_t units = 1.186 + mask->mEnumAttributes[SVGMaskElement::MASKUNITS].GetAnimValue(); 1.187 + gfxRect bbox; 1.188 + if (units == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 1.189 + bbox = nsSVGUtils::GetBBox(aParent); 1.190 + } 1.191 + 1.192 + gfxRect maskArea = nsSVGUtils::GetRelativeRect(units, 1.193 + &mask->mLengthAttributes[SVGMaskElement::ATTR_X], bbox, aParent); 1.194 + 1.195 + gfxContext *gfx = aContext->ThebesContext(); 1.196 + 1.197 + // Get the clip extents in device space: 1.198 + gfx->Save(); 1.199 + nsSVGUtils::SetClipRect(gfx, aMatrix, maskArea); 1.200 + gfx->IdentityMatrix(); 1.201 + gfxRect clipExtents = gfx->GetClipExtents(); 1.202 + clipExtents.RoundOut(); 1.203 + gfx->Restore(); 1.204 + 1.205 + bool resultOverflows; 1.206 + gfxIntSize surfaceSize = 1.207 + nsSVGUtils::ConvertToSurfaceSize(gfxSize(clipExtents.Width(), 1.208 + clipExtents.Height()), 1.209 + &resultOverflows); 1.210 + 1.211 + // 0 disables mask, < 0 is an error 1.212 + if (surfaceSize.width <= 0 || surfaceSize.height <= 0) 1.213 + return nullptr; 1.214 + 1.215 + if (resultOverflows) 1.216 + return nullptr; 1.217 + 1.218 + nsRefPtr<gfxImageSurface> image = 1.219 + new gfxImageSurface(surfaceSize, gfxImageFormat::ARGB32); 1.220 + if (!image || image->CairoStatus()) 1.221 + return nullptr; 1.222 + 1.223 + // We would like to use gfxImageSurface::SetDeviceOffset() to position 1.224 + // 'image'. However, we need to set the same matrix on the temporary context 1.225 + // and pattern that we create below as is currently set on 'gfx'. 1.226 + // Unfortunately, any device offset set by SetDeviceOffset() is affected by 1.227 + // the transform passed to the SetMatrix() calls, so to avoid that we account 1.228 + // for the device offset in the transform rather than use SetDeviceOffset(). 1.229 + gfxMatrix matrix = 1.230 + gfx->CurrentMatrix() * gfxMatrix().Translate(-clipExtents.TopLeft()); 1.231 + 1.232 + nsRefPtr<nsRenderingContext> tmpCtx(new nsRenderingContext); 1.233 + tmpCtx->Init(this->PresContext()->DeviceContext(), image); 1.234 + tmpCtx->ThebesContext()->SetMatrix(matrix); 1.235 + 1.236 + mMaskParent = aParent; 1.237 + if (mMaskParentMatrix) { 1.238 + *mMaskParentMatrix = aMatrix; 1.239 + } else { 1.240 + mMaskParentMatrix = new gfxMatrix(aMatrix); 1.241 + } 1.242 + 1.243 + for (nsIFrame* kid = mFrames.FirstChild(); kid; 1.244 + kid = kid->GetNextSibling()) { 1.245 + // The CTM of each frame referencing us can be different 1.246 + nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); 1.247 + if (SVGFrame) { 1.248 + SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); 1.249 + } 1.250 + nsSVGUtils::PaintFrameWithEffects(tmpCtx, nullptr, kid); 1.251 + } 1.252 + 1.253 + uint8_t *data = image->Data(); 1.254 + int32_t stride = image->Stride(); 1.255 + nsIntRect rect(0, 0, surfaceSize.width, surfaceSize.height); 1.256 + 1.257 + if (StyleSVGReset()->mMaskType == NS_STYLE_MASK_TYPE_LUMINANCE) { 1.258 + if (StyleSVG()->mColorInterpolation == 1.259 + NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) { 1.260 + ComputeLinearRGBLuminanceMask(data, stride, rect, aOpacity); 1.261 + } else { 1.262 + ComputesRGBLuminanceMask(data, stride, rect, aOpacity); 1.263 + } 1.264 + } else { 1.265 + ComputeAlphaMask(data, stride, rect, aOpacity); 1.266 + } 1.267 + 1.268 + nsRefPtr<gfxPattern> retval = new gfxPattern(image); 1.269 + retval->SetMatrix(matrix); 1.270 + return retval.forget(); 1.271 +} 1.272 + 1.273 +nsresult 1.274 +nsSVGMaskFrame::AttributeChanged(int32_t aNameSpaceID, 1.275 + nsIAtom* aAttribute, 1.276 + int32_t aModType) 1.277 +{ 1.278 + if (aNameSpaceID == kNameSpaceID_None && 1.279 + (aAttribute == nsGkAtoms::x || 1.280 + aAttribute == nsGkAtoms::y || 1.281 + aAttribute == nsGkAtoms::width || 1.282 + aAttribute == nsGkAtoms::height|| 1.283 + aAttribute == nsGkAtoms::maskUnits || 1.284 + aAttribute == nsGkAtoms::maskContentUnits)) { 1.285 + nsSVGEffects::InvalidateDirectRenderingObservers(this); 1.286 + } 1.287 + 1.288 + return nsSVGMaskFrameBase::AttributeChanged(aNameSpaceID, 1.289 + aAttribute, aModType); 1.290 +} 1.291 + 1.292 +#ifdef DEBUG 1.293 +void 1.294 +nsSVGMaskFrame::Init(nsIContent* aContent, 1.295 + nsIFrame* aParent, 1.296 + nsIFrame* aPrevInFlow) 1.297 +{ 1.298 + NS_ASSERTION(aContent->IsSVG(nsGkAtoms::mask), 1.299 + "Content is not an SVG mask"); 1.300 + 1.301 + nsSVGMaskFrameBase::Init(aContent, aParent, aPrevInFlow); 1.302 +} 1.303 +#endif /* DEBUG */ 1.304 + 1.305 +nsIAtom * 1.306 +nsSVGMaskFrame::GetType() const 1.307 +{ 1.308 + return nsGkAtoms::svgMaskFrame; 1.309 +} 1.310 + 1.311 +gfxMatrix 1.312 +nsSVGMaskFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) 1.313 +{ 1.314 + NS_ASSERTION(mMaskParentMatrix, "null parent matrix"); 1.315 + 1.316 + SVGMaskElement *mask = static_cast<SVGMaskElement*>(mContent); 1.317 + 1.318 + return nsSVGUtils::AdjustMatrixForUnits( 1.319 + mMaskParentMatrix ? *mMaskParentMatrix : gfxMatrix(), 1.320 + &mask->mEnumAttributes[SVGMaskElement::MASKCONTENTUNITS], 1.321 + mMaskParent); 1.322 +} 1.323 +