1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsSVGPatternFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,740 @@ 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 "nsSVGPatternFrame.h" 1.11 + 1.12 +// Keep others in (case-insensitive) order: 1.13 +#include "gfx2DGlue.h" 1.14 +#include "gfxContext.h" 1.15 +#include "gfxMatrix.h" 1.16 +#include "gfxPattern.h" 1.17 +#include "gfxPlatform.h" 1.18 +#include "mozilla/gfx/2D.h" 1.19 +#include "nsContentUtils.h" 1.20 +#include "nsGkAtoms.h" 1.21 +#include "nsISVGChildFrame.h" 1.22 +#include "nsRenderingContext.h" 1.23 +#include "nsStyleContext.h" 1.24 +#include "nsSVGEffects.h" 1.25 +#include "nsSVGPathGeometryFrame.h" 1.26 +#include "mozilla/dom/SVGPatternElement.h" 1.27 +#include "nsSVGUtils.h" 1.28 +#include "nsSVGAnimatedTransformList.h" 1.29 +#include "SVGContentUtils.h" 1.30 +#include "gfxColor.h" 1.31 + 1.32 +using namespace mozilla; 1.33 +using namespace mozilla::dom; 1.34 +using namespace mozilla::gfx; 1.35 + 1.36 +//---------------------------------------------------------------------- 1.37 +// Helper classes 1.38 + 1.39 +class MOZ_STACK_CLASS nsSVGPatternFrame::AutoPatternReferencer 1.40 +{ 1.41 +public: 1.42 + AutoPatternReferencer(nsSVGPatternFrame *aFrame 1.43 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.44 + : mFrame(aFrame) 1.45 + { 1.46 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.47 + // Reference loops should normally be detected in advance and handled, so 1.48 + // we're not expecting to encounter them here 1.49 + NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!"); 1.50 + mFrame->mLoopFlag = true; 1.51 + } 1.52 + ~AutoPatternReferencer() { 1.53 + mFrame->mLoopFlag = false; 1.54 + } 1.55 +private: 1.56 + nsSVGPatternFrame *mFrame; 1.57 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.58 +}; 1.59 + 1.60 +//---------------------------------------------------------------------- 1.61 +// Implementation 1.62 + 1.63 +nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext* aContext) : 1.64 + nsSVGPatternFrameBase(aContext), 1.65 + mLoopFlag(false), 1.66 + mNoHRefURI(false) 1.67 +{ 1.68 +} 1.69 + 1.70 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame) 1.71 + 1.72 +//---------------------------------------------------------------------- 1.73 +// nsIFrame methods: 1.74 + 1.75 +nsresult 1.76 +nsSVGPatternFrame::AttributeChanged(int32_t aNameSpaceID, 1.77 + nsIAtom* aAttribute, 1.78 + int32_t aModType) 1.79 +{ 1.80 + if (aNameSpaceID == kNameSpaceID_None && 1.81 + (aAttribute == nsGkAtoms::patternUnits || 1.82 + aAttribute == nsGkAtoms::patternContentUnits || 1.83 + aAttribute == nsGkAtoms::patternTransform || 1.84 + aAttribute == nsGkAtoms::x || 1.85 + aAttribute == nsGkAtoms::y || 1.86 + aAttribute == nsGkAtoms::width || 1.87 + aAttribute == nsGkAtoms::height || 1.88 + aAttribute == nsGkAtoms::preserveAspectRatio || 1.89 + aAttribute == nsGkAtoms::viewBox)) { 1.90 + nsSVGEffects::InvalidateDirectRenderingObservers(this); 1.91 + } 1.92 + 1.93 + if (aNameSpaceID == kNameSpaceID_XLink && 1.94 + aAttribute == nsGkAtoms::href) { 1.95 + // Blow away our reference, if any 1.96 + Properties().Delete(nsSVGEffects::HrefProperty()); 1.97 + mNoHRefURI = false; 1.98 + // And update whoever references us 1.99 + nsSVGEffects::InvalidateDirectRenderingObservers(this); 1.100 + } 1.101 + 1.102 + return nsSVGPatternFrameBase::AttributeChanged(aNameSpaceID, 1.103 + aAttribute, aModType); 1.104 +} 1.105 + 1.106 +#ifdef DEBUG 1.107 +void 1.108 +nsSVGPatternFrame::Init(nsIContent* aContent, 1.109 + nsIFrame* aParent, 1.110 + nsIFrame* aPrevInFlow) 1.111 +{ 1.112 + NS_ASSERTION(aContent->IsSVG(nsGkAtoms::pattern), "Content is not an SVG pattern"); 1.113 + 1.114 + nsSVGPatternFrameBase::Init(aContent, aParent, aPrevInFlow); 1.115 +} 1.116 +#endif /* DEBUG */ 1.117 + 1.118 +nsIAtom* 1.119 +nsSVGPatternFrame::GetType() const 1.120 +{ 1.121 + return nsGkAtoms::svgPatternFrame; 1.122 +} 1.123 + 1.124 +//---------------------------------------------------------------------- 1.125 +// nsSVGContainerFrame methods: 1.126 + 1.127 +// If our GetCanvasTM is getting called, we 1.128 +// need to return *our current* transformation 1.129 +// matrix, which depends on our units parameters 1.130 +// and X, Y, Width, and Height 1.131 +gfxMatrix 1.132 +nsSVGPatternFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) 1.133 +{ 1.134 + if (mCTM) { 1.135 + return *mCTM; 1.136 + } 1.137 + 1.138 + // Do we know our rendering parent? 1.139 + if (mSource) { 1.140 + // Yes, use it! 1.141 + return mSource->GetCanvasTM(aFor, aTransformRoot); 1.142 + } 1.143 + 1.144 + // We get here when geometry in the <pattern> container is updated 1.145 + return gfxMatrix(); 1.146 +} 1.147 + 1.148 +// ------------------------------------------------------------------------- 1.149 +// Helper functions 1.150 +// ------------------------------------------------------------------------- 1.151 + 1.152 +/** Calculate the maximum expansion of a matrix */ 1.153 +static float 1.154 +MaxExpansion(const Matrix &aMatrix) 1.155 +{ 1.156 + // maximum expansion derivation from 1.157 + // http://lists.cairographics.org/archives/cairo/2004-October/001980.html 1.158 + // and also implemented in cairo_matrix_transformed_circle_major_axis 1.159 + double a = aMatrix._11; 1.160 + double b = aMatrix._12; 1.161 + double c = aMatrix._21; 1.162 + double d = aMatrix._22; 1.163 + double f = (a * a + b * b + c * c + d * d) / 2; 1.164 + double g = (a * a + b * b - c * c - d * d) / 2; 1.165 + double h = a * c + b * d; 1.166 + return sqrt(f + sqrt(g * g + h * h)); 1.167 +} 1.168 + 1.169 +// The SVG specification says that the 'patternContentUnits' attribute "has no effect if 1.170 +// attribute ‘viewBox’ is specified". We still need to include a bbox scale 1.171 +// if the viewBox is specified and _patternUnits_ is set to or defaults to 1.172 +// objectBoundingBox though, since in that case the viewBox is relative to the bbox 1.173 +static bool 1.174 +IncludeBBoxScale(const nsSVGViewBox& aViewBox, 1.175 + uint32_t aPatternContentUnits, uint32_t aPatternUnits) 1.176 +{ 1.177 + return (!aViewBox.IsExplicitlySet() && 1.178 + aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) || 1.179 + (aViewBox.IsExplicitlySet() && 1.180 + aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX); 1.181 +} 1.182 + 1.183 +// Given the matrix for the pattern element's own transform, this returns a 1.184 +// combined matrix including the transforms applicable to its target. 1.185 +static gfxMatrix 1.186 +GetPatternMatrix(uint16_t aPatternUnits, 1.187 + const gfxMatrix &patternTransform, 1.188 + const gfxRect &bbox, 1.189 + const gfxRect &callerBBox, 1.190 + const Matrix &callerCTM) 1.191 +{ 1.192 + // We really want the pattern matrix to handle translations 1.193 + gfxFloat minx = bbox.X(); 1.194 + gfxFloat miny = bbox.Y(); 1.195 + 1.196 + if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 1.197 + minx += callerBBox.X(); 1.198 + miny += callerBBox.Y(); 1.199 + } 1.200 + 1.201 + float scale = 1.0f / MaxExpansion(callerCTM); 1.202 + gfxMatrix patternMatrix = patternTransform; 1.203 + patternMatrix.Scale(scale, scale); 1.204 + patternMatrix.Translate(gfxPoint(minx, miny)); 1.205 + 1.206 + return patternMatrix; 1.207 +} 1.208 + 1.209 +static nsresult 1.210 +GetTargetGeometry(gfxRect *aBBox, 1.211 + const nsSVGViewBox &aViewBox, 1.212 + uint16_t aPatternContentUnits, 1.213 + uint16_t aPatternUnits, 1.214 + nsIFrame *aTarget, 1.215 + const Matrix &aContextMatrix, 1.216 + const gfxRect *aOverrideBounds) 1.217 +{ 1.218 + *aBBox = aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aTarget); 1.219 + 1.220 + // Sanity check 1.221 + if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) && 1.222 + (aBBox->Width() <= 0 || aBBox->Height() <= 0)) { 1.223 + return NS_ERROR_FAILURE; 1.224 + } 1.225 + 1.226 + // OK, now fix up the bounding box to reflect user coordinates 1.227 + // We handle device unit scaling in pattern matrix 1.228 + float scale = MaxExpansion(aContextMatrix); 1.229 + if (scale <= 0) { 1.230 + return NS_ERROR_FAILURE; 1.231 + } 1.232 + aBBox->Scale(scale); 1.233 + return NS_OK; 1.234 +} 1.235 + 1.236 +nsresult 1.237 +nsSVGPatternFrame::PaintPattern(gfxASurface** surface, 1.238 + gfxMatrix* patternMatrix, 1.239 + const gfxMatrix &aContextMatrix, 1.240 + nsIFrame *aSource, 1.241 + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, 1.242 + float aGraphicOpacity, 1.243 + const gfxRect *aOverrideBounds) 1.244 +{ 1.245 + /* 1.246 + * General approach: 1.247 + * Set the content geometry stuff 1.248 + * Calculate our bbox (using x,y,width,height & patternUnits & 1.249 + * patternTransform) 1.250 + * Create the surface 1.251 + * Calculate the content transformation matrix 1.252 + * Get our children (we may need to get them from another Pattern) 1.253 + * Call SVGPaint on all of our children 1.254 + * Return 1.255 + */ 1.256 + *surface = nullptr; 1.257 + 1.258 + // Get the first child of the pattern data we will render 1.259 + nsIFrame* firstKid = GetPatternFirstChild(); 1.260 + if (!firstKid) 1.261 + return NS_ERROR_FAILURE; // Either no kids or a bad reference 1.262 + 1.263 + const nsSVGViewBox& viewBox = GetViewBox(); 1.264 + 1.265 + uint16_t patternContentUnits = 1.266 + GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS); 1.267 + uint16_t patternUnits = 1.268 + GetEnumValue(SVGPatternElement::PATTERNUNITS); 1.269 + 1.270 + /* 1.271 + * Get the content geometry information. This is a little tricky -- 1.272 + * our parent is probably a <defs>, but we are rendering in the context 1.273 + * of some geometry source. Our content geometry information needs to 1.274 + * come from our rendering parent as opposed to our content parent. We 1.275 + * get that information from aSource, which is passed to us from the 1.276 + * backend renderer. 1.277 + * 1.278 + * There are three "geometries" that we need: 1.279 + * 1) The bounding box for the pattern. We use this to get the 1.280 + * width and height for the surface, and as the return to 1.281 + * GetBBox. 1.282 + * 2) The transformation matrix for the pattern. This is not *quite* 1.283 + * the same as the canvas transformation matrix that we will 1.284 + * provide to our rendering children since we "fudge" it a little 1.285 + * to get the renderer to handle the translations correctly for us. 1.286 + * 3) The CTM that we return to our children who make up the pattern. 1.287 + */ 1.288 + 1.289 + // Get all of the information we need from our "caller" -- i.e. 1.290 + // the geometry that is being rendered with a pattern 1.291 + gfxRect callerBBox; 1.292 + if (NS_FAILED(GetTargetGeometry(&callerBBox, 1.293 + viewBox, 1.294 + patternContentUnits, patternUnits, 1.295 + aSource, 1.296 + ToMatrix(aContextMatrix), 1.297 + aOverrideBounds))) 1.298 + return NS_ERROR_FAILURE; 1.299 + 1.300 + // Construct the CTM that we will provide to our children when we 1.301 + // render them into the tile. 1.302 + gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits, 1.303 + callerBBox, ToMatrix(aContextMatrix), aSource); 1.304 + if (ctm.IsSingular()) { 1.305 + return NS_ERROR_FAILURE; 1.306 + } 1.307 + 1.308 + // Get the pattern we are going to render 1.309 + nsSVGPatternFrame *patternFrame = 1.310 + static_cast<nsSVGPatternFrame*>(firstKid->GetParent()); 1.311 + if (patternFrame->mCTM) { 1.312 + *patternFrame->mCTM = ctm; 1.313 + } else { 1.314 + patternFrame->mCTM = new gfxMatrix(ctm); 1.315 + } 1.316 + 1.317 + // Get the bounding box of the pattern. This will be used to determine 1.318 + // the size of the surface, and will also be used to define the bounding 1.319 + // box for the pattern tile. 1.320 + gfxRect bbox = GetPatternRect(patternUnits, callerBBox, ToMatrix(aContextMatrix), aSource); 1.321 + if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) { 1.322 + return NS_ERROR_FAILURE; 1.323 + } 1.324 + 1.325 + // Get the pattern transform 1.326 + gfxMatrix patternTransform = GetPatternTransform(); 1.327 + 1.328 + // revert the vector effect transform so that the pattern appears unchanged 1.329 + if (aFillOrStroke == &nsStyleSVG::mStroke) { 1.330 + patternTransform.Multiply(nsSVGUtils::GetStrokeTransform(aSource).Invert()); 1.331 + } 1.332 + 1.333 + // Get the transformation matrix that we will hand to the renderer's pattern 1.334 + // routine. 1.335 + *patternMatrix = GetPatternMatrix(patternUnits, patternTransform, 1.336 + bbox, callerBBox, ToMatrix(aContextMatrix)); 1.337 + if (patternMatrix->IsSingular()) { 1.338 + return NS_ERROR_FAILURE; 1.339 + } 1.340 + 1.341 + // Now that we have all of the necessary geometries, we can 1.342 + // create our surface. 1.343 + gfxRect transformedBBox = patternTransform.TransformBounds(bbox); 1.344 + 1.345 + bool resultOverflows; 1.346 + IntSize surfaceSize = 1.347 + nsSVGUtils::ConvertToSurfaceSize( 1.348 + transformedBBox.Size(), &resultOverflows).ToIntSize(); 1.349 + 1.350 + // 0 disables rendering, < 0 is an error 1.351 + if (surfaceSize.width <= 0 || surfaceSize.height <= 0) 1.352 + return NS_ERROR_FAILURE; 1.353 + 1.354 + gfxFloat patternWidth = bbox.Width(); 1.355 + gfxFloat patternHeight = bbox.Height(); 1.356 + 1.357 + if (resultOverflows || 1.358 + patternWidth != surfaceSize.width || 1.359 + patternHeight != surfaceSize.height) { 1.360 + // scale drawing to pattern surface size 1.361 + gfxMatrix tempTM = 1.362 + gfxMatrix(surfaceSize.width / patternWidth, 0.0f, 1.363 + 0.0f, surfaceSize.height / patternHeight, 1.364 + 0.0f, 0.0f); 1.365 + patternFrame->mCTM->PreMultiply(tempTM); 1.366 + 1.367 + // and rescale pattern to compensate 1.368 + patternMatrix->Scale(patternWidth / surfaceSize.width, 1.369 + patternHeight / surfaceSize.height); 1.370 + } 1.371 + 1.372 + nsRefPtr<gfxASurface> tmpSurface = 1.373 + gfxPlatform::GetPlatform()->CreateOffscreenSurface(surfaceSize, 1.374 + gfxContentType::COLOR_ALPHA); 1.375 + if (!tmpSurface || tmpSurface->CairoStatus()) 1.376 + return NS_ERROR_FAILURE; 1.377 + 1.378 + nsRefPtr<nsRenderingContext> context(new nsRenderingContext()); 1.379 + context->Init(aSource->PresContext()->DeviceContext(), tmpSurface); 1.380 + gfxContext* gfx = context->ThebesContext(); 1.381 + 1.382 + // Fill with transparent black 1.383 + gfx->SetOperator(gfxContext::OPERATOR_CLEAR); 1.384 + gfx->Paint(); 1.385 + gfx->SetOperator(gfxContext::OPERATOR_OVER); 1.386 + 1.387 + if (aGraphicOpacity != 1.0f) { 1.388 + gfx->Save(); 1.389 + gfx->PushGroup(gfxContentType::COLOR_ALPHA); 1.390 + } 1.391 + 1.392 + // OK, now render -- note that we use "firstKid", which 1.393 + // we got at the beginning because it takes care of the 1.394 + // referenced pattern situation for us 1.395 + 1.396 + if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) { 1.397 + // Set the geometrical parent of the pattern we are rendering 1.398 + patternFrame->mSource = static_cast<nsSVGPathGeometryFrame*>(aSource); 1.399 + } 1.400 + 1.401 + // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can 1.402 + // give back a clear surface if there's a loop 1.403 + if (!(patternFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)) { 1.404 + patternFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER); 1.405 + for (nsIFrame* kid = firstKid; kid; 1.406 + kid = kid->GetNextSibling()) { 1.407 + // The CTM of each frame referencing us can be different 1.408 + nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); 1.409 + if (SVGFrame) { 1.410 + SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); 1.411 + } 1.412 + nsSVGUtils::PaintFrameWithEffects(context, nullptr, kid); 1.413 + } 1.414 + patternFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER); 1.415 + } 1.416 + 1.417 + patternFrame->mSource = nullptr; 1.418 + 1.419 + if (aGraphicOpacity != 1.0f) { 1.420 + gfx->PopGroupToSource(); 1.421 + gfx->Paint(aGraphicOpacity); 1.422 + gfx->Restore(); 1.423 + } 1.424 + 1.425 + // caller now owns the surface 1.426 + tmpSurface.forget(surface); 1.427 + return NS_OK; 1.428 +} 1.429 + 1.430 +/* Will probably need something like this... */ 1.431 +// How do we handle the insertion of a new frame? 1.432 +// We really don't want to rerender this every time, 1.433 +// do we? 1.434 +nsIFrame* 1.435 +nsSVGPatternFrame::GetPatternFirstChild() 1.436 +{ 1.437 + // Do we have any children ourselves? 1.438 + nsIFrame* kid = mFrames.FirstChild(); 1.439 + if (kid) 1.440 + return kid; 1.441 + 1.442 + // No, see if we chain to someone who does 1.443 + AutoPatternReferencer patternRef(this); 1.444 + 1.445 + nsSVGPatternFrame* next = GetReferencedPatternIfNotInUse(); 1.446 + if (!next) 1.447 + return nullptr; 1.448 + 1.449 + return next->GetPatternFirstChild(); 1.450 +} 1.451 + 1.452 +uint16_t 1.453 +nsSVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault) 1.454 +{ 1.455 + nsSVGEnum& thisEnum = 1.456 + static_cast<SVGPatternElement *>(mContent)->mEnumAttributes[aIndex]; 1.457 + 1.458 + if (thisEnum.IsExplicitlySet()) 1.459 + return thisEnum.GetAnimValue(); 1.460 + 1.461 + AutoPatternReferencer patternRef(this); 1.462 + 1.463 + nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse(); 1.464 + return next ? next->GetEnumValue(aIndex, aDefault) : 1.465 + static_cast<SVGPatternElement *>(aDefault)-> 1.466 + mEnumAttributes[aIndex].GetAnimValue(); 1.467 +} 1.468 + 1.469 +nsSVGAnimatedTransformList* 1.470 +nsSVGPatternFrame::GetPatternTransformList(nsIContent* aDefault) 1.471 +{ 1.472 + nsSVGAnimatedTransformList *thisTransformList = 1.473 + static_cast<SVGPatternElement *>(mContent)->GetAnimatedTransformList(); 1.474 + 1.475 + if (thisTransformList && thisTransformList->IsExplicitlySet()) 1.476 + return thisTransformList; 1.477 + 1.478 + AutoPatternReferencer patternRef(this); 1.479 + 1.480 + nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse(); 1.481 + return next ? next->GetPatternTransformList(aDefault) : 1.482 + static_cast<SVGPatternElement *>(aDefault)->mPatternTransform.get(); 1.483 +} 1.484 + 1.485 +gfxMatrix 1.486 +nsSVGPatternFrame::GetPatternTransform() 1.487 +{ 1.488 + nsSVGAnimatedTransformList* animTransformList = 1.489 + GetPatternTransformList(mContent); 1.490 + if (!animTransformList) 1.491 + return gfxMatrix(); 1.492 + 1.493 + return animTransformList->GetAnimValue().GetConsolidationMatrix(); 1.494 +} 1.495 + 1.496 +const nsSVGViewBox & 1.497 +nsSVGPatternFrame::GetViewBox(nsIContent* aDefault) 1.498 +{ 1.499 + const nsSVGViewBox &thisViewBox = 1.500 + static_cast<SVGPatternElement *>(mContent)->mViewBox; 1.501 + 1.502 + if (thisViewBox.IsExplicitlySet()) 1.503 + return thisViewBox; 1.504 + 1.505 + AutoPatternReferencer patternRef(this); 1.506 + 1.507 + nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse(); 1.508 + return next ? next->GetViewBox(aDefault) : 1.509 + static_cast<SVGPatternElement *>(aDefault)->mViewBox; 1.510 +} 1.511 + 1.512 +const SVGAnimatedPreserveAspectRatio & 1.513 +nsSVGPatternFrame::GetPreserveAspectRatio(nsIContent *aDefault) 1.514 +{ 1.515 + const SVGAnimatedPreserveAspectRatio &thisPar = 1.516 + static_cast<SVGPatternElement *>(mContent)->mPreserveAspectRatio; 1.517 + 1.518 + if (thisPar.IsExplicitlySet()) 1.519 + return thisPar; 1.520 + 1.521 + AutoPatternReferencer patternRef(this); 1.522 + 1.523 + nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse(); 1.524 + return next ? next->GetPreserveAspectRatio(aDefault) : 1.525 + static_cast<SVGPatternElement *>(aDefault)->mPreserveAspectRatio; 1.526 +} 1.527 + 1.528 +const nsSVGLength2 * 1.529 +nsSVGPatternFrame::GetLengthValue(uint32_t aIndex, nsIContent *aDefault) 1.530 +{ 1.531 + const nsSVGLength2 *thisLength = 1.532 + &static_cast<SVGPatternElement *>(mContent)->mLengthAttributes[aIndex]; 1.533 + 1.534 + if (thisLength->IsExplicitlySet()) 1.535 + return thisLength; 1.536 + 1.537 + AutoPatternReferencer patternRef(this); 1.538 + 1.539 + nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse(); 1.540 + return next ? next->GetLengthValue(aIndex, aDefault) : 1.541 + &static_cast<SVGPatternElement *>(aDefault)->mLengthAttributes[aIndex]; 1.542 +} 1.543 + 1.544 +// Private (helper) methods 1.545 +nsSVGPatternFrame * 1.546 +nsSVGPatternFrame::GetReferencedPattern() 1.547 +{ 1.548 + if (mNoHRefURI) 1.549 + return nullptr; 1.550 + 1.551 + nsSVGPaintingProperty *property = static_cast<nsSVGPaintingProperty*> 1.552 + (Properties().Get(nsSVGEffects::HrefProperty())); 1.553 + 1.554 + if (!property) { 1.555 + // Fetch our pattern element's xlink:href attribute 1.556 + SVGPatternElement *pattern = static_cast<SVGPatternElement *>(mContent); 1.557 + nsAutoString href; 1.558 + pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(href, pattern); 1.559 + if (href.IsEmpty()) { 1.560 + mNoHRefURI = true; 1.561 + return nullptr; // no URL 1.562 + } 1.563 + 1.564 + // Convert href to an nsIURI 1.565 + nsCOMPtr<nsIURI> targetURI; 1.566 + nsCOMPtr<nsIURI> base = mContent->GetBaseURI(); 1.567 + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, 1.568 + mContent->GetCurrentDoc(), base); 1.569 + 1.570 + property = 1.571 + nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty()); 1.572 + if (!property) 1.573 + return nullptr; 1.574 + } 1.575 + 1.576 + nsIFrame *result = property->GetReferencedFrame(); 1.577 + if (!result) 1.578 + return nullptr; 1.579 + 1.580 + nsIAtom* frameType = result->GetType(); 1.581 + if (frameType != nsGkAtoms::svgPatternFrame) 1.582 + return nullptr; 1.583 + 1.584 + return static_cast<nsSVGPatternFrame*>(result); 1.585 +} 1.586 + 1.587 +nsSVGPatternFrame * 1.588 +nsSVGPatternFrame::GetReferencedPatternIfNotInUse() 1.589 +{ 1.590 + nsSVGPatternFrame *referenced = GetReferencedPattern(); 1.591 + if (!referenced) 1.592 + return nullptr; 1.593 + 1.594 + if (referenced->mLoopFlag) { 1.595 + // XXXjwatt: we should really send an error to the JavaScript Console here: 1.596 + NS_WARNING("pattern reference loop detected while inheriting attribute!"); 1.597 + return nullptr; 1.598 + } 1.599 + 1.600 + return referenced; 1.601 +} 1.602 + 1.603 +gfxRect 1.604 +nsSVGPatternFrame::GetPatternRect(uint16_t aPatternUnits, 1.605 + const gfxRect &aTargetBBox, 1.606 + const Matrix &aTargetCTM, 1.607 + nsIFrame *aTarget) 1.608 +{ 1.609 + // We need to initialize our box 1.610 + float x,y,width,height; 1.611 + 1.612 + // Get the pattern x,y,width, and height 1.613 + const nsSVGLength2 *tmpX, *tmpY, *tmpHeight, *tmpWidth; 1.614 + tmpX = GetLengthValue(SVGPatternElement::ATTR_X); 1.615 + tmpY = GetLengthValue(SVGPatternElement::ATTR_Y); 1.616 + tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT); 1.617 + tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH); 1.618 + 1.619 + if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 1.620 + x = nsSVGUtils::ObjectSpace(aTargetBBox, tmpX); 1.621 + y = nsSVGUtils::ObjectSpace(aTargetBBox, tmpY); 1.622 + width = nsSVGUtils::ObjectSpace(aTargetBBox, tmpWidth); 1.623 + height = nsSVGUtils::ObjectSpace(aTargetBBox, tmpHeight); 1.624 + } else { 1.625 + float scale = MaxExpansion(aTargetCTM); 1.626 + x = nsSVGUtils::UserSpace(aTarget, tmpX) * scale; 1.627 + y = nsSVGUtils::UserSpace(aTarget, tmpY) * scale; 1.628 + width = nsSVGUtils::UserSpace(aTarget, tmpWidth) * scale; 1.629 + height = nsSVGUtils::UserSpace(aTarget, tmpHeight) * scale; 1.630 + } 1.631 + 1.632 + return gfxRect(x, y, width, height); 1.633 +} 1.634 + 1.635 +gfxMatrix 1.636 +nsSVGPatternFrame::ConstructCTM(const nsSVGViewBox& aViewBox, 1.637 + uint16_t aPatternContentUnits, 1.638 + uint16_t aPatternUnits, 1.639 + const gfxRect &callerBBox, 1.640 + const Matrix &callerCTM, 1.641 + nsIFrame *aTarget) 1.642 +{ 1.643 + gfxMatrix tCTM; 1.644 + SVGSVGElement *ctx = nullptr; 1.645 + nsIContent* targetContent = aTarget->GetContent(); 1.646 + 1.647 + // The objectBoundingBox conversion must be handled in the CTM: 1.648 + if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) { 1.649 + tCTM.Scale(callerBBox.Width(), callerBBox.Height()); 1.650 + } else { 1.651 + if (targetContent->IsSVG()) { 1.652 + ctx = static_cast<nsSVGElement*>(targetContent)->GetCtx(); 1.653 + } 1.654 + float scale = MaxExpansion(callerCTM); 1.655 + tCTM.Scale(scale, scale); 1.656 + } 1.657 + 1.658 + if (!aViewBox.IsExplicitlySet()) { 1.659 + return tCTM; 1.660 + } 1.661 + const nsSVGViewBoxRect viewBoxRect = aViewBox.GetAnimValue(); 1.662 + 1.663 + if (viewBoxRect.height <= 0.0f || viewBoxRect.width <= 0.0f) { 1.664 + return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular 1.665 + } 1.666 + 1.667 + float viewportWidth, viewportHeight; 1.668 + if (targetContent->IsSVG()) { 1.669 + // If we're dealing with an SVG target only retrieve the context once. 1.670 + // Calling the nsIFrame* variant of GetAnimValue would look it up on 1.671 + // every call. 1.672 + viewportWidth = 1.673 + GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(ctx); 1.674 + viewportHeight = 1.675 + GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(ctx); 1.676 + } else { 1.677 + // No SVG target, call the nsIFrame* variant of GetAnimValue. 1.678 + viewportWidth = 1.679 + GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(aTarget); 1.680 + viewportHeight = 1.681 + GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(aTarget); 1.682 + } 1.683 + 1.684 + if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) { 1.685 + return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular 1.686 + } 1.687 + 1.688 + Matrix tm = SVGContentUtils::GetViewBoxTransform( 1.689 + viewportWidth, viewportHeight, 1.690 + viewBoxRect.x, viewBoxRect.y, 1.691 + viewBoxRect.width, viewBoxRect.height, 1.692 + GetPreserveAspectRatio()); 1.693 + 1.694 + return ThebesMatrix(tm) * tCTM; 1.695 +} 1.696 + 1.697 +//---------------------------------------------------------------------- 1.698 +// nsSVGPaintServerFrame methods: 1.699 + 1.700 +already_AddRefed<gfxPattern> 1.701 +nsSVGPatternFrame::GetPaintServerPattern(nsIFrame *aSource, 1.702 + const gfxMatrix& aContextMatrix, 1.703 + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, 1.704 + float aGraphicOpacity, 1.705 + const gfxRect *aOverrideBounds) 1.706 +{ 1.707 + if (aGraphicOpacity == 0.0f) { 1.708 + nsRefPtr<gfxPattern> pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0)); 1.709 + return pattern.forget(); 1.710 + } 1.711 + 1.712 + // Paint it! 1.713 + nsRefPtr<gfxASurface> surface; 1.714 + gfxMatrix pMatrix; 1.715 + nsresult rv = PaintPattern(getter_AddRefs(surface), &pMatrix, aContextMatrix, 1.716 + aSource, aFillOrStroke, aGraphicOpacity, aOverrideBounds); 1.717 + 1.718 + if (NS_FAILED(rv)) { 1.719 + return nullptr; 1.720 + } 1.721 + 1.722 + pMatrix.Invert(); 1.723 + 1.724 + nsRefPtr<gfxPattern> pattern = new gfxPattern(surface); 1.725 + 1.726 + if (!pattern || pattern->CairoStatus()) 1.727 + return nullptr; 1.728 + 1.729 + pattern->SetMatrix(pMatrix); 1.730 + pattern->SetExtend(gfxPattern::EXTEND_REPEAT); 1.731 + return pattern.forget(); 1.732 +} 1.733 + 1.734 +// ------------------------------------------------------------------------- 1.735 +// Public functions 1.736 +// ------------------------------------------------------------------------- 1.737 + 1.738 +nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell, 1.739 + nsStyleContext* aContext) 1.740 +{ 1.741 + return new (aPresShell) nsSVGPatternFrame(aContext); 1.742 +} 1.743 +