Bug 1770025 [wpt PR 34116] - Update wpt metadata, a=testonly
[gecko.git] / layout / svg / SVGPatternFrame.cpp
blob9ba9139e363ae51b7e6e4259511a76b787242c34
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Main header first:
8 #include "SVGPatternFrame.h"
10 // Keep others in (case-insensitive) order:
11 #include "AutoReferenceChainGuard.h"
12 #include "gfx2DGlue.h"
13 #include "gfxContext.h"
14 #include "gfxMatrix.h"
15 #include "gfxPattern.h"
16 #include "gfxPlatform.h"
17 #include "mozilla/ComputedStyle.h"
18 #include "mozilla/ISVGDisplayableFrame.h"
19 #include "mozilla/PresShell.h"
20 #include "mozilla/SVGContentUtils.h"
21 #include "mozilla/SVGGeometryFrame.h"
22 #include "mozilla/SVGObserverUtils.h"
23 #include "mozilla/SVGUtils.h"
24 #include "mozilla/dom/SVGPatternElement.h"
25 #include "mozilla/dom/SVGUnitTypesBinding.h"
26 #include "mozilla/gfx/2D.h"
27 #include "nsGkAtoms.h"
28 #include "nsIFrameInlines.h"
29 #include "SVGAnimatedTransformList.h"
31 using namespace mozilla::dom;
32 using namespace mozilla::dom::SVGUnitTypes_Binding;
33 using namespace mozilla::gfx;
34 using namespace mozilla::image;
36 namespace mozilla {
38 //----------------------------------------------------------------------
39 // Implementation
41 SVGPatternFrame::SVGPatternFrame(ComputedStyle* aStyle,
42 nsPresContext* aPresContext)
43 : SVGPaintServerFrame(aStyle, aPresContext, kClassID),
44 mSource(nullptr),
45 mLoopFlag(false),
46 mNoHRefURI(false) {}
48 NS_IMPL_FRAMEARENA_HELPERS(SVGPatternFrame)
50 NS_QUERYFRAME_HEAD(SVGPatternFrame)
51 NS_QUERYFRAME_ENTRY(SVGPatternFrame)
52 NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame)
54 //----------------------------------------------------------------------
55 // nsIFrame methods:
57 nsresult SVGPatternFrame::AttributeChanged(int32_t aNameSpaceID,
58 nsAtom* aAttribute,
59 int32_t aModType) {
60 if (aNameSpaceID == kNameSpaceID_None &&
61 (aAttribute == nsGkAtoms::patternUnits ||
62 aAttribute == nsGkAtoms::patternContentUnits ||
63 aAttribute == nsGkAtoms::patternTransform ||
64 aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
65 aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
66 aAttribute == nsGkAtoms::preserveAspectRatio ||
67 aAttribute == nsGkAtoms::viewBox)) {
68 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
71 if ((aNameSpaceID == kNameSpaceID_XLink ||
72 aNameSpaceID == kNameSpaceID_None) &&
73 aAttribute == nsGkAtoms::href) {
74 // Blow away our reference, if any
75 SVGObserverUtils::RemoveTemplateObserver(this);
76 mNoHRefURI = false;
77 // And update whoever references us
78 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
81 return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute,
82 aModType);
85 #ifdef DEBUG
86 void SVGPatternFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
87 nsIFrame* aPrevInFlow) {
88 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::pattern),
89 "Content is not an SVG pattern");
91 SVGPaintServerFrame::Init(aContent, aParent, aPrevInFlow);
93 #endif /* DEBUG */
95 //----------------------------------------------------------------------
96 // SVGContainerFrame methods:
98 // If our GetCanvasTM is getting called, we
99 // need to return *our current* transformation
100 // matrix, which depends on our units parameters
101 // and X, Y, Width, and Height
102 gfxMatrix SVGPatternFrame::GetCanvasTM() {
103 if (mCTM) {
104 return *mCTM;
107 // Do we know our rendering parent?
108 if (mSource) {
109 // Yes, use it!
110 return mSource->GetCanvasTM();
113 // We get here when geometry in the <pattern> container is updated
114 return gfxMatrix();
117 // -------------------------------------------------------------------------
118 // Helper functions
119 // -------------------------------------------------------------------------
121 /** Calculate the maximum expansion of a matrix */
122 static float MaxExpansion(const Matrix& aMatrix) {
123 // maximum expansion derivation from
124 // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
125 // and also implemented in cairo_matrix_transformed_circle_major_axis
126 double a = aMatrix._11;
127 double b = aMatrix._12;
128 double c = aMatrix._21;
129 double d = aMatrix._22;
130 double f = (a * a + b * b + c * c + d * d) / 2;
131 double g = (a * a + b * b - c * c - d * d) / 2;
132 double h = a * c + b * d;
133 return sqrt(f + sqrt(g * g + h * h));
136 // The SVG specification says that the 'patternContentUnits' attribute "has no
137 // effect if attribute ‘viewBox’ is specified". We still need to include a bbox
138 // scale if the viewBox is specified and _patternUnits_ is set to or defaults to
139 // objectBoundingBox though, since in that case the viewBox is relative to the
140 // bbox
141 static bool IncludeBBoxScale(const SVGAnimatedViewBox& aViewBox,
142 uint32_t aPatternContentUnits,
143 uint32_t aPatternUnits) {
144 return (!aViewBox.IsExplicitlySet() &&
145 aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) ||
146 (aViewBox.IsExplicitlySet() &&
147 aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
150 // Given the matrix for the pattern element's own transform, this returns a
151 // combined matrix including the transforms applicable to its target.
152 static Matrix GetPatternMatrix(uint16_t aPatternUnits,
153 const Matrix& patternTransform,
154 const gfxRect& bbox, const gfxRect& callerBBox,
155 const Matrix& callerCTM) {
156 // We really want the pattern matrix to handle translations
157 gfxFloat minx = bbox.X();
158 gfxFloat miny = bbox.Y();
160 if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
161 minx += callerBBox.X();
162 miny += callerBBox.Y();
165 float scale = 1.0f / MaxExpansion(callerCTM);
166 Matrix patternMatrix = patternTransform;
167 patternMatrix.PreScale(scale, scale);
168 patternMatrix.PreTranslate(minx, miny);
170 return patternMatrix;
173 static nsresult GetTargetGeometry(gfxRect* aBBox,
174 const SVGAnimatedViewBox& aViewBox,
175 uint16_t aPatternContentUnits,
176 uint16_t aPatternUnits, nsIFrame* aTarget,
177 const Matrix& aContextMatrix,
178 const gfxRect* aOverrideBounds) {
179 *aBBox =
180 aOverrideBounds
181 ? *aOverrideBounds
182 : SVGUtils::GetBBox(aTarget, SVGUtils::eUseFrameBoundsForOuterSVG |
183 SVGUtils::eBBoxIncludeFillGeometry);
185 // Sanity check
186 if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) &&
187 (aBBox->Width() <= 0 || aBBox->Height() <= 0)) {
188 return NS_ERROR_FAILURE;
191 // OK, now fix up the bounding box to reflect user coordinates
192 // We handle device unit scaling in pattern matrix
193 float scale = MaxExpansion(aContextMatrix);
194 if (scale <= 0) {
195 return NS_ERROR_FAILURE;
197 aBBox->Scale(scale);
198 return NS_OK;
201 already_AddRefed<SourceSurface> SVGPatternFrame::PaintPattern(
202 const DrawTarget* aDrawTarget, Matrix* patternMatrix,
203 const Matrix& aContextMatrix, nsIFrame* aSource,
204 StyleSVGPaint nsStyleSVG::*aFillOrStroke, float aGraphicOpacity,
205 const gfxRect* aOverrideBounds, imgDrawingParams& aImgParams) {
207 * General approach:
208 * Set the content geometry stuff
209 * Calculate our bbox (using x,y,width,height & patternUnits &
210 * patternTransform)
211 * Create the surface
212 * Calculate the content transformation matrix
213 * Get our children (we may need to get them from another Pattern)
214 * Call SVGPaint on all of our children
215 * Return
218 SVGPatternFrame* patternWithChildren = GetPatternWithChildren();
219 if (!patternWithChildren) {
220 // Either no kids or a bad reference
221 return nullptr;
223 nsIFrame* firstKid = patternWithChildren->mFrames.FirstChild();
225 const SVGAnimatedViewBox& viewBox = GetViewBox();
227 uint16_t patternContentUnits =
228 GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS);
229 uint16_t patternUnits = GetEnumValue(SVGPatternElement::PATTERNUNITS);
232 * Get the content geometry information. This is a little tricky --
233 * our parent is probably a <defs>, but we are rendering in the context
234 * of some geometry source. Our content geometry information needs to
235 * come from our rendering parent as opposed to our content parent. We
236 * get that information from aSource, which is passed to us from the
237 * backend renderer.
239 * There are three "geometries" that we need:
240 * 1) The bounding box for the pattern. We use this to get the
241 * width and height for the surface, and as the return to
242 * GetBBox.
243 * 2) The transformation matrix for the pattern. This is not *quite*
244 * the same as the canvas transformation matrix that we will
245 * provide to our rendering children since we "fudge" it a little
246 * to get the renderer to handle the translations correctly for us.
247 * 3) The CTM that we return to our children who make up the pattern.
250 // Get all of the information we need from our "caller" -- i.e.
251 // the geometry that is being rendered with a pattern
252 gfxRect callerBBox;
253 if (NS_FAILED(GetTargetGeometry(&callerBBox, viewBox, patternContentUnits,
254 patternUnits, aSource, aContextMatrix,
255 aOverrideBounds))) {
256 return nullptr;
259 // Construct the CTM that we will provide to our children when we
260 // render them into the tile.
261 gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits,
262 callerBBox, aContextMatrix, aSource);
263 if (ctm.IsSingular()) {
264 return nullptr;
267 if (patternWithChildren->mCTM) {
268 *patternWithChildren->mCTM = ctm;
269 } else {
270 patternWithChildren->mCTM = MakeUnique<gfxMatrix>(ctm);
273 // Get the bounding box of the pattern. This will be used to determine
274 // the size of the surface, and will also be used to define the bounding
275 // box for the pattern tile.
276 gfxRect bbox =
277 GetPatternRect(patternUnits, callerBBox, aContextMatrix, aSource);
278 if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) {
279 return nullptr;
282 // Get the pattern transform
283 Matrix patternTransform = ToMatrix(GetPatternTransform());
285 // revert the vector effect transform so that the pattern appears unchanged
286 if (aFillOrStroke == &nsStyleSVG::mStroke) {
287 gfxMatrix userToOuterSVG;
288 if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
289 patternTransform *= ToMatrix(userToOuterSVG);
290 if (patternTransform.IsSingular()) {
291 NS_WARNING("Singular matrix painting non-scaling-stroke");
292 return nullptr;
297 // Get the transformation matrix that we will hand to the renderer's pattern
298 // routine.
299 *patternMatrix = GetPatternMatrix(patternUnits, patternTransform, bbox,
300 callerBBox, aContextMatrix);
301 if (patternMatrix->IsSingular()) {
302 return nullptr;
305 // Now that we have all of the necessary geometries, we can
306 // create our surface.
307 gfxRect transformedBBox =
308 ThebesRect(patternTransform.TransformBounds(ToRect(bbox)));
310 bool resultOverflows;
311 IntSize surfaceSize =
312 SVGUtils::ConvertToSurfaceSize(transformedBBox.Size(), &resultOverflows);
314 // 0 disables rendering, < 0 is an error
315 if (surfaceSize.width <= 0 || surfaceSize.height <= 0) {
316 return nullptr;
319 gfxFloat patternWidth = bbox.Width();
320 gfxFloat patternHeight = bbox.Height();
322 if (resultOverflows || patternWidth != surfaceSize.width ||
323 patternHeight != surfaceSize.height) {
324 // scale drawing to pattern surface size
325 gfxMatrix tempTM = gfxMatrix(surfaceSize.width / patternWidth, 0.0, 0.0,
326 surfaceSize.height / patternHeight, 0.0, 0.0);
327 patternWithChildren->mCTM->PreMultiply(tempTM);
329 // and rescale pattern to compensate
330 patternMatrix->PreScale(patternWidth / surfaceSize.width,
331 patternHeight / surfaceSize.height);
334 RefPtr<DrawTarget> dt = aDrawTarget->CreateSimilarDrawTargetWithBacking(
335 surfaceSize, SurfaceFormat::B8G8R8A8);
336 if (!dt || !dt->IsValid()) {
337 return nullptr;
339 dt->ClearRect(Rect(0, 0, surfaceSize.width, surfaceSize.height));
341 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
342 MOZ_ASSERT(ctx); // already checked the draw target above
344 if (aGraphicOpacity != 1.0f) {
345 ctx->Save();
346 ctx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aGraphicOpacity);
349 // OK, now render -- note that we use "firstKid", which
350 // we got at the beginning because it takes care of the
351 // referenced pattern situation for us
353 if (aSource->IsSVGGeometryFrameOrSubclass()) {
354 // Set the geometrical parent of the pattern we are rendering
355 patternWithChildren->mSource = static_cast<SVGGeometryFrame*>(aSource);
358 // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
359 // give back a clear surface if there's a loop
360 if (!patternWithChildren->HasAnyStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER)) {
361 AutoSetRestorePaintServerState paintServer(patternWithChildren);
362 for (nsIFrame* kid = firstKid; kid; kid = kid->GetNextSibling()) {
363 gfxMatrix tm = *(patternWithChildren->mCTM);
365 // The CTM of each frame referencing us can be different
366 ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
367 if (SVGFrame) {
368 SVGFrame->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED);
369 tm = SVGUtils::GetTransformMatrixInUserSpace(kid) * tm;
372 SVGUtils::PaintFrameWithEffects(kid, *ctx, tm, aImgParams);
376 patternWithChildren->mSource = nullptr;
378 if (aGraphicOpacity != 1.0f) {
379 ctx->PopGroupAndBlend();
380 ctx->Restore();
383 // caller now owns the surface
384 return dt->GetBackingSurface();
387 /* Will probably need something like this... */
388 // How do we handle the insertion of a new frame?
389 // We really don't want to rerender this every time,
390 // do we?
391 SVGPatternFrame* SVGPatternFrame::GetPatternWithChildren() {
392 // Do we have any children ourselves?
393 if (!mFrames.IsEmpty()) {
394 return this;
397 // No, see if we chain to someone who does
399 // Before we recurse, make sure we'll break reference loops and over long
400 // reference chains:
401 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
402 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
403 &sRefChainLengthCounter);
404 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
405 // Break reference chain
406 return nullptr;
409 SVGPatternFrame* next = GetReferencedPattern();
410 if (!next) {
411 return nullptr;
414 return next->GetPatternWithChildren();
417 uint16_t SVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) {
418 SVGAnimatedEnumeration& thisEnum =
419 static_cast<SVGPatternElement*>(GetContent())->mEnumAttributes[aIndex];
421 if (thisEnum.IsExplicitlySet()) {
422 return thisEnum.GetAnimValue();
425 // Before we recurse, make sure we'll break reference loops and over long
426 // reference chains:
427 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
428 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
429 &sRefChainLengthCounter);
430 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
431 // Break reference chain
432 return static_cast<SVGPatternElement*>(aDefault)
433 ->mEnumAttributes[aIndex]
434 .GetAnimValue();
437 SVGPatternFrame* next = GetReferencedPattern();
438 return next ? next->GetEnumValue(aIndex, aDefault)
439 : static_cast<SVGPatternElement*>(aDefault)
440 ->mEnumAttributes[aIndex]
441 .GetAnimValue();
444 SVGAnimatedTransformList* SVGPatternFrame::GetPatternTransformList(
445 nsIContent* aDefault) {
446 SVGAnimatedTransformList* thisTransformList =
447 static_cast<SVGPatternElement*>(GetContent())->GetAnimatedTransformList();
449 if (thisTransformList && thisTransformList->IsExplicitlySet())
450 return thisTransformList;
452 // Before we recurse, make sure we'll break reference loops and over long
453 // reference chains:
454 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
455 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
456 &sRefChainLengthCounter);
457 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
458 // Break reference chain
459 return static_cast<SVGPatternElement*>(aDefault)->mPatternTransform.get();
462 SVGPatternFrame* next = GetReferencedPattern();
463 return next ? next->GetPatternTransformList(aDefault)
464 : static_cast<SVGPatternElement*>(aDefault)
465 ->mPatternTransform.get();
468 gfxMatrix SVGPatternFrame::GetPatternTransform() {
469 SVGAnimatedTransformList* animTransformList =
470 GetPatternTransformList(GetContent());
471 if (!animTransformList) {
472 return gfxMatrix();
475 return animTransformList->GetAnimValue().GetConsolidationMatrix();
478 const SVGAnimatedViewBox& SVGPatternFrame::GetViewBox(nsIContent* aDefault) {
479 const SVGAnimatedViewBox& thisViewBox =
480 static_cast<SVGPatternElement*>(GetContent())->mViewBox;
482 if (thisViewBox.IsExplicitlySet()) {
483 return thisViewBox;
486 // Before we recurse, make sure we'll break reference loops and over long
487 // reference chains:
488 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
489 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
490 &sRefChainLengthCounter);
491 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
492 // Break reference chain
493 return static_cast<SVGPatternElement*>(aDefault)->mViewBox;
496 SVGPatternFrame* next = GetReferencedPattern();
497 return next ? next->GetViewBox(aDefault)
498 : static_cast<SVGPatternElement*>(aDefault)->mViewBox;
501 const SVGAnimatedPreserveAspectRatio& SVGPatternFrame::GetPreserveAspectRatio(
502 nsIContent* aDefault) {
503 const SVGAnimatedPreserveAspectRatio& thisPar =
504 static_cast<SVGPatternElement*>(GetContent())->mPreserveAspectRatio;
506 if (thisPar.IsExplicitlySet()) {
507 return thisPar;
510 // Before we recurse, make sure we'll break reference loops and over long
511 // reference chains:
512 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
513 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
514 &sRefChainLengthCounter);
515 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
516 // Break reference chain
517 return static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio;
520 SVGPatternFrame* next = GetReferencedPattern();
521 return next ? next->GetPreserveAspectRatio(aDefault)
522 : static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio;
525 const SVGAnimatedLength* SVGPatternFrame::GetLengthValue(uint32_t aIndex,
526 nsIContent* aDefault) {
527 const SVGAnimatedLength* thisLength =
528 &static_cast<SVGPatternElement*>(GetContent())->mLengthAttributes[aIndex];
530 if (thisLength->IsExplicitlySet()) {
531 return thisLength;
534 // Before we recurse, make sure we'll break reference loops and over long
535 // reference chains:
536 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
537 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
538 &sRefChainLengthCounter);
539 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
540 // Break reference chain
541 return &static_cast<SVGPatternElement*>(aDefault)
542 ->mLengthAttributes[aIndex];
545 SVGPatternFrame* next = GetReferencedPattern();
546 return next ? next->GetLengthValue(aIndex, aDefault)
547 : &static_cast<SVGPatternElement*>(aDefault)
548 ->mLengthAttributes[aIndex];
551 // Private (helper) methods
553 SVGPatternFrame* SVGPatternFrame::GetReferencedPattern() {
554 if (mNoHRefURI) {
555 return nullptr;
558 auto GetHref = [this](nsAString& aHref) {
559 SVGPatternElement* pattern =
560 static_cast<SVGPatternElement*>(this->GetContent());
561 if (pattern->mStringAttributes[SVGPatternElement::HREF].IsExplicitlySet()) {
562 pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(aHref,
563 pattern);
564 } else {
565 pattern->mStringAttributes[SVGPatternElement::XLINK_HREF].GetAnimValue(
566 aHref, pattern);
568 this->mNoHRefURI = aHref.IsEmpty();
571 nsIFrame* tframe = SVGObserverUtils::GetAndObserveTemplate(this, GetHref);
572 if (tframe) {
573 LayoutFrameType frameType = tframe->Type();
574 if (frameType == LayoutFrameType::SVGPattern) {
575 return static_cast<SVGPatternFrame*>(tframe);
577 // We don't call SVGObserverUtils::RemoveTemplateObserver and set
578 // `mNoHRefURI = false` here since we want to be invalidated if the ID
579 // specified by our href starts resolving to a different/valid element.
582 return nullptr;
585 gfxRect SVGPatternFrame::GetPatternRect(uint16_t aPatternUnits,
586 const gfxRect& aTargetBBox,
587 const Matrix& aTargetCTM,
588 nsIFrame* aTarget) {
589 // We need to initialize our box
590 float x, y, width, height;
592 // Get the pattern x,y,width, and height
593 const SVGAnimatedLength *tmpX, *tmpY, *tmpHeight, *tmpWidth;
594 tmpX = GetLengthValue(SVGPatternElement::ATTR_X);
595 tmpY = GetLengthValue(SVGPatternElement::ATTR_Y);
596 tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT);
597 tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH);
599 if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
600 x = SVGUtils::ObjectSpace(aTargetBBox, tmpX);
601 y = SVGUtils::ObjectSpace(aTargetBBox, tmpY);
602 width = SVGUtils::ObjectSpace(aTargetBBox, tmpWidth);
603 height = SVGUtils::ObjectSpace(aTargetBBox, tmpHeight);
604 } else {
605 float scale = MaxExpansion(aTargetCTM);
606 x = SVGUtils::UserSpace(aTarget, tmpX) * scale;
607 y = SVGUtils::UserSpace(aTarget, tmpY) * scale;
608 width = SVGUtils::UserSpace(aTarget, tmpWidth) * scale;
609 height = SVGUtils::UserSpace(aTarget, tmpHeight) * scale;
612 return gfxRect(x, y, width, height);
615 gfxMatrix SVGPatternFrame::ConstructCTM(const SVGAnimatedViewBox& aViewBox,
616 uint16_t aPatternContentUnits,
617 uint16_t aPatternUnits,
618 const gfxRect& callerBBox,
619 const Matrix& callerCTM,
620 nsIFrame* aTarget) {
621 SVGViewportElement* ctx = nullptr;
622 nsIContent* targetContent = aTarget->GetContent();
623 gfxFloat scaleX, scaleY;
625 // The objectBoundingBox conversion must be handled in the CTM:
626 if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) {
627 scaleX = callerBBox.Width();
628 scaleY = callerBBox.Height();
629 } else {
630 if (targetContent->IsSVGElement()) {
631 ctx = static_cast<SVGElement*>(targetContent)->GetCtx();
633 scaleX = scaleY = MaxExpansion(callerCTM);
636 if (!aViewBox.IsExplicitlySet()) {
637 return gfxMatrix(scaleX, 0.0, 0.0, scaleY, 0.0, 0.0);
639 const SVGViewBox& viewBox = aViewBox.GetAnimValue();
641 if (viewBox.height <= 0.0f || viewBox.width <= 0.0f) {
642 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
645 float viewportWidth, viewportHeight;
646 if (targetContent->IsSVGElement()) {
647 // If we're dealing with an SVG target only retrieve the context once.
648 // Calling the nsIFrame* variant of GetAnimValue would look it up on
649 // every call.
650 viewportWidth =
651 GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(ctx);
652 viewportHeight =
653 GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(ctx);
654 } else {
655 // No SVG target, call the nsIFrame* variant of GetAnimValue.
656 viewportWidth =
657 GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(aTarget);
658 viewportHeight =
659 GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(aTarget);
662 if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
663 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
666 Matrix tm = SVGContentUtils::GetViewBoxTransform(
667 viewportWidth * scaleX, viewportHeight * scaleY, viewBox.x, viewBox.y,
668 viewBox.width, viewBox.height, GetPreserveAspectRatio());
670 return ThebesMatrix(tm);
673 //----------------------------------------------------------------------
674 // SVGPaintServerFrame methods:
675 already_AddRefed<gfxPattern> SVGPatternFrame::GetPaintServerPattern(
676 nsIFrame* aSource, const DrawTarget* aDrawTarget,
677 const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::*aFillOrStroke,
678 float aGraphicOpacity, imgDrawingParams& aImgParams,
679 const gfxRect* aOverrideBounds) {
680 if (aGraphicOpacity == 0.0f) {
681 return do_AddRef(new gfxPattern(DeviceColor()));
684 // Paint it!
685 Matrix pMatrix;
686 RefPtr<SourceSurface> surface =
687 PaintPattern(aDrawTarget, &pMatrix, ToMatrix(aContextMatrix), aSource,
688 aFillOrStroke, aGraphicOpacity, aOverrideBounds, aImgParams);
690 if (!surface) {
691 return nullptr;
694 RefPtr<gfxPattern> pattern = new gfxPattern(surface, pMatrix);
696 if (!pattern) {
697 return nullptr;
700 pattern->SetExtend(ExtendMode::REPEAT);
701 return pattern.forget();
704 } // namespace mozilla
706 // -------------------------------------------------------------------------
707 // Public functions
708 // -------------------------------------------------------------------------
710 nsIFrame* NS_NewSVGPatternFrame(mozilla::PresShell* aPresShell,
711 mozilla::ComputedStyle* aStyle) {
712 return new (aPresShell)
713 mozilla::SVGPatternFrame(aStyle, aPresShell->GetPresContext());