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/. */
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
;
38 //----------------------------------------------------------------------
41 SVGPatternFrame::SVGPatternFrame(ComputedStyle
* aStyle
,
42 nsPresContext
* aPresContext
)
43 : SVGPaintServerFrame(aStyle
, aPresContext
, kClassID
),
48 NS_IMPL_FRAMEARENA_HELPERS(SVGPatternFrame
)
50 NS_QUERYFRAME_HEAD(SVGPatternFrame
)
51 NS_QUERYFRAME_ENTRY(SVGPatternFrame
)
52 NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame
)
54 //----------------------------------------------------------------------
57 nsresult
SVGPatternFrame::AttributeChanged(int32_t aNameSpaceID
,
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::InvalidateRenderingObservers(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);
77 // And update whoever references us
78 SVGObserverUtils::InvalidateRenderingObservers(this);
81 return SVGPaintServerFrame::AttributeChanged(aNameSpaceID
, aAttribute
,
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
);
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() {
107 // Do we know our rendering parent?
110 return mSource
->GetCanvasTM();
113 // We get here when geometry in the <pattern> container is updated
117 // -------------------------------------------------------------------------
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
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(nsIFrame
* aSource
,
153 const StyleSVGPaint
nsStyleSVG::*aFillOrStroke
,
154 uint16_t aPatternUnits
,
155 const gfxMatrix
& patternTransform
,
156 const gfxRect
& bbox
, const gfxRect
& callerBBox
,
157 const Matrix
& callerCTM
) {
158 // We really want the pattern matrix to handle translations
159 gfxFloat minx
= bbox
.X();
160 gfxFloat miny
= bbox
.Y();
162 if (aPatternUnits
== SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
163 minx
+= callerBBox
.X();
164 miny
+= callerBBox
.Y();
167 double scale
= 1.0 / MaxExpansion(callerCTM
);
168 auto patternMatrix
= patternTransform
;
169 patternMatrix
.PreScale(scale
, scale
);
170 patternMatrix
.PreTranslate(minx
, miny
);
172 // revert the vector effect transform so that the pattern appears unchanged
173 if (aFillOrStroke
== &nsStyleSVG::mStroke
) {
174 gfxMatrix userToOuterSVG
;
175 if (SVGUtils::GetNonScalingStrokeTransform(aSource
, &userToOuterSVG
)) {
176 patternMatrix
*= userToOuterSVG
;
180 return ToMatrix(patternMatrix
);
183 static nsresult
GetTargetGeometry(gfxRect
* aBBox
,
184 const SVGAnimatedViewBox
& aViewBox
,
185 uint16_t aPatternContentUnits
,
186 uint16_t aPatternUnits
, nsIFrame
* aTarget
,
187 const Matrix
& aContextMatrix
,
188 const gfxRect
* aOverrideBounds
) {
192 : SVGUtils::GetBBox(aTarget
, SVGUtils::eUseFrameBoundsForOuterSVG
|
193 SVGUtils::eBBoxIncludeFillGeometry
);
196 if (IncludeBBoxScale(aViewBox
, aPatternContentUnits
, aPatternUnits
) &&
197 (aBBox
->Width() <= 0 || aBBox
->Height() <= 0)) {
198 return NS_ERROR_FAILURE
;
201 // OK, now fix up the bounding box to reflect user coordinates
202 // We handle device unit scaling in pattern matrix
203 float scale
= MaxExpansion(aContextMatrix
);
205 return NS_ERROR_FAILURE
;
211 void SVGPatternFrame::PaintChildren(DrawTarget
* aDrawTarget
,
212 SVGPatternFrame
* aPatternWithChildren
,
213 nsIFrame
* aSource
, float aGraphicOpacity
,
214 imgDrawingParams
& aImgParams
) {
215 nsIFrame
* firstKid
= aPatternWithChildren
->mFrames
.FirstChild();
217 gfxContext
ctx(aDrawTarget
);
218 gfxGroupForBlendAutoSaveRestore
autoGroupForBlend(&ctx
);
220 if (aGraphicOpacity
!= 1.0f
) {
221 autoGroupForBlend
.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA
,
225 // OK, now render -- note that we use "firstKid", which
226 // we got at the beginning because it takes care of the
227 // referenced pattern situation for us
229 if (aSource
->IsSVGGeometryFrame()) {
230 // Set the geometrical parent of the pattern we are rendering
231 aPatternWithChildren
->mSource
= static_cast<SVGGeometryFrame
*>(aSource
);
234 // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
235 // give back a clear surface if there's a loop
236 if (!aPatternWithChildren
->HasAnyStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER
)) {
237 AutoSetRestorePaintServerState
paintServer(aPatternWithChildren
);
238 for (nsIFrame
* kid
= firstKid
; kid
; kid
= kid
->GetNextSibling()) {
239 gfxMatrix tm
= *(aPatternWithChildren
->mCTM
);
241 // The CTM of each frame referencing us can be different
242 ISVGDisplayableFrame
* SVGFrame
= do_QueryFrame(kid
);
244 SVGFrame
->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED
);
245 tm
= SVGUtils::GetTransformMatrixInUserSpace(kid
) * tm
;
248 SVGUtils::PaintFrameWithEffects(kid
, ctx
, tm
, aImgParams
);
252 aPatternWithChildren
->mSource
= nullptr;
255 already_AddRefed
<SourceSurface
> SVGPatternFrame::PaintPattern(
256 const DrawTarget
* aDrawTarget
, Matrix
* patternMatrix
,
257 const Matrix
& aContextMatrix
, nsIFrame
* aSource
,
258 StyleSVGPaint
nsStyleSVG::*aFillOrStroke
, float aGraphicOpacity
,
259 const gfxRect
* aOverrideBounds
, imgDrawingParams
& aImgParams
) {
262 * Set the content geometry stuff
263 * Calculate our bbox (using x,y,width,height & patternUnits &
266 * Calculate the content transformation matrix
267 * Get our children (we may need to get them from another Pattern)
268 * Call SVGPaint on all of our children
272 SVGPatternFrame
* patternWithChildren
= GetPatternWithChildren();
273 if (!patternWithChildren
) {
274 // Either no kids or a bad reference
278 const SVGAnimatedViewBox
& viewBox
= GetViewBox();
280 uint16_t patternContentUnits
=
281 GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS
);
282 uint16_t patternUnits
= GetEnumValue(SVGPatternElement::PATTERNUNITS
);
285 * Get the content geometry information. This is a little tricky --
286 * our parent is probably a <defs>, but we are rendering in the context
287 * of some geometry source. Our content geometry information needs to
288 * come from our rendering parent as opposed to our content parent. We
289 * get that information from aSource, which is passed to us from the
292 * There are three "geometries" that we need:
293 * 1) The bounding box for the pattern. We use this to get the
294 * width and height for the surface, and as the return to
296 * 2) The transformation matrix for the pattern. This is not *quite*
297 * the same as the canvas transformation matrix that we will
298 * provide to our rendering children since we "fudge" it a little
299 * to get the renderer to handle the translations correctly for us.
300 * 3) The CTM that we return to our children who make up the pattern.
303 // Get all of the information we need from our "caller" -- i.e.
304 // the geometry that is being rendered with a pattern
306 if (NS_FAILED(GetTargetGeometry(&callerBBox
, viewBox
, patternContentUnits
,
307 patternUnits
, aSource
, aContextMatrix
,
312 // Construct the CTM that we will provide to our children when we
313 // render them into the tile.
314 gfxMatrix ctm
= ConstructCTM(viewBox
, patternContentUnits
, patternUnits
,
315 callerBBox
, aContextMatrix
, aSource
);
316 if (ctm
.IsSingular()) {
320 if (patternWithChildren
->mCTM
) {
321 *patternWithChildren
->mCTM
= ctm
;
323 patternWithChildren
->mCTM
= MakeUnique
<gfxMatrix
>(ctm
);
326 // Get the bounding box of the pattern. This will be used to determine
327 // the size of the surface, and will also be used to define the bounding
328 // box for the pattern tile.
330 GetPatternRect(patternUnits
, callerBBox
, aContextMatrix
, aSource
);
331 if (bbox
.Width() <= 0.0 || bbox
.Height() <= 0.0) {
335 // Get the pattern transform
336 auto patternTransform
= GetPatternTransform();
338 // Get the transformation matrix that we will hand to the renderer's pattern
341 GetPatternMatrix(aSource
, aFillOrStroke
, patternUnits
, patternTransform
,
342 bbox
, callerBBox
, aContextMatrix
);
343 if (patternMatrix
->IsSingular()) {
347 // Now that we have all of the necessary geometries, we can
348 // create our surface.
349 gfxSize scaledSize
= bbox
.Size() * MaxExpansion(ToMatrix(patternTransform
));
351 bool resultOverflows
;
352 IntSize surfaceSize
=
353 SVGUtils::ConvertToSurfaceSize(scaledSize
, &resultOverflows
);
355 // 0 disables rendering, < 0 is an error
356 if (surfaceSize
.width
<= 0 || surfaceSize
.height
<= 0) {
360 gfxFloat patternWidth
= bbox
.Width();
361 gfxFloat patternHeight
= bbox
.Height();
363 if (resultOverflows
|| patternWidth
!= surfaceSize
.width
||
364 patternHeight
!= surfaceSize
.height
) {
365 // scale drawing to pattern surface size
366 patternWithChildren
->mCTM
->PostScale(surfaceSize
.width
/ patternWidth
,
367 surfaceSize
.height
/ patternHeight
);
369 // and rescale pattern to compensate
370 patternMatrix
->PreScale(patternWidth
/ surfaceSize
.width
,
371 patternHeight
/ surfaceSize
.height
);
374 RefPtr
<DrawTarget
> dt
= aDrawTarget
->CreateSimilarDrawTargetWithBacking(
375 surfaceSize
, SurfaceFormat::B8G8R8A8
);
376 if (!dt
|| !dt
->IsValid()) {
379 dt
->ClearRect(Rect(0, 0, surfaceSize
.width
, surfaceSize
.height
));
381 PaintChildren(dt
, patternWithChildren
, aSource
, aGraphicOpacity
, aImgParams
);
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,
391 SVGPatternFrame
* SVGPatternFrame::GetPatternWithChildren() {
392 // Do we have any children ourselves?
393 if (!mFrames
.IsEmpty()) {
397 // No, see if we chain to someone who does
399 // Before we recurse, make sure we'll break reference loops and over long
401 static int16_t sRefChainLengthCounter
= AutoReferenceChainGuard::noChain
;
402 AutoReferenceChainGuard
refChainGuard(this, &mLoopFlag
,
403 &sRefChainLengthCounter
);
404 if (MOZ_UNLIKELY(!refChainGuard
.Reference())) {
405 // Break reference chain
409 SVGPatternFrame
* next
= GetReferencedPattern();
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
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
]
437 SVGPatternFrame
* next
= GetReferencedPattern();
438 return next
? next
->GetEnumValue(aIndex
, aDefault
)
439 : static_cast<SVGPatternElement
*>(aDefault
)
440 ->mEnumAttributes
[aIndex
]
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
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
) {
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()) {
486 // Before we recurse, make sure we'll break reference loops and over long
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()) {
510 // Before we recurse, make sure we'll break reference loops and over long
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()) {
534 // Before we recurse, make sure we'll break reference loops and over long
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() {
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
,
565 pattern
->mStringAttributes
[SVGPatternElement::XLINK_HREF
].GetAnimValue(
568 this->mNoHRefURI
= aHref
.IsEmpty();
571 // We don't call SVGObserverUtils::RemoveTemplateObserver and set
572 // `mNoHRefURI = false` on failure since we want to be invalidated if the ID
573 // specified by our href starts resolving to a different/valid element.
575 return do_QueryFrame(SVGObserverUtils::GetAndObserveTemplate(this, GetHref
));
578 gfxRect
SVGPatternFrame::GetPatternRect(uint16_t aPatternUnits
,
579 const gfxRect
& aTargetBBox
,
580 const Matrix
& aTargetCTM
,
582 // We need to initialize our box
583 float x
, y
, width
, height
;
585 // Get the pattern x,y,width, and height
586 const SVGAnimatedLength
*tmpX
, *tmpY
, *tmpHeight
, *tmpWidth
;
587 tmpX
= GetLengthValue(SVGPatternElement::ATTR_X
);
588 tmpY
= GetLengthValue(SVGPatternElement::ATTR_Y
);
589 tmpHeight
= GetLengthValue(SVGPatternElement::ATTR_HEIGHT
);
590 tmpWidth
= GetLengthValue(SVGPatternElement::ATTR_WIDTH
);
592 if (aPatternUnits
== SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
593 x
= SVGUtils::ObjectSpace(aTargetBBox
, tmpX
);
594 y
= SVGUtils::ObjectSpace(aTargetBBox
, tmpY
);
595 width
= SVGUtils::ObjectSpace(aTargetBBox
, tmpWidth
);
596 height
= SVGUtils::ObjectSpace(aTargetBBox
, tmpHeight
);
598 if (aTarget
->IsTextFrame()) {
599 aTarget
= aTarget
->GetParent();
601 float scale
= MaxExpansion(aTargetCTM
);
602 x
= SVGUtils::UserSpace(aTarget
, tmpX
) * scale
;
603 y
= SVGUtils::UserSpace(aTarget
, tmpY
) * scale
;
604 width
= SVGUtils::UserSpace(aTarget
, tmpWidth
) * scale
;
605 height
= SVGUtils::UserSpace(aTarget
, tmpHeight
) * scale
;
608 return gfxRect(x
, y
, width
, height
);
611 gfxMatrix
SVGPatternFrame::ConstructCTM(const SVGAnimatedViewBox
& aViewBox
,
612 uint16_t aPatternContentUnits
,
613 uint16_t aPatternUnits
,
614 const gfxRect
& callerBBox
,
615 const Matrix
& callerCTM
,
617 if (aTarget
->IsTextFrame()) {
618 aTarget
= aTarget
->GetParent();
620 nsIContent
* targetContent
= aTarget
->GetContent();
621 SVGViewportElement
* ctx
= nullptr;
622 gfxFloat scaleX
, scaleY
;
624 // The objectBoundingBox conversion must be handled in the CTM:
625 if (IncludeBBoxScale(aViewBox
, aPatternContentUnits
, aPatternUnits
)) {
626 scaleX
= callerBBox
.Width();
627 scaleY
= callerBBox
.Height();
629 if (targetContent
->IsSVGElement()) {
630 ctx
= static_cast<SVGElement
*>(targetContent
)->GetCtx();
632 scaleX
= scaleY
= MaxExpansion(callerCTM
);
635 if (!aViewBox
.IsExplicitlySet()) {
636 return gfxMatrix(scaleX
, 0.0, 0.0, scaleY
, 0.0, 0.0);
638 const SVGViewBox
& viewBox
= aViewBox
.GetAnimValue();
640 if (viewBox
.height
<= 0.0f
|| viewBox
.width
<= 0.0f
) {
641 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
644 float viewportWidth
, viewportHeight
;
645 if (targetContent
->IsSVGElement()) {
646 // If we're dealing with an SVG target only retrieve the context once.
647 // Calling the nsIFrame* variant of GetAnimValue would look it up on
650 GetLengthValue(SVGPatternElement::ATTR_WIDTH
)->GetAnimValue(ctx
);
652 GetLengthValue(SVGPatternElement::ATTR_HEIGHT
)->GetAnimValue(ctx
);
654 // No SVG target, call the nsIFrame* variant of GetAnimValue.
656 GetLengthValue(SVGPatternElement::ATTR_WIDTH
)->GetAnimValue(aTarget
);
658 GetLengthValue(SVGPatternElement::ATTR_HEIGHT
)->GetAnimValue(aTarget
);
661 if (viewportWidth
<= 0.0f
|| viewportHeight
<= 0.0f
) {
662 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
665 Matrix tm
= SVGContentUtils::GetViewBoxTransform(
666 viewportWidth
* scaleX
, viewportHeight
* scaleY
, viewBox
.x
, viewBox
.y
,
667 viewBox
.width
, viewBox
.height
, GetPreserveAspectRatio());
669 return ThebesMatrix(tm
);
672 //----------------------------------------------------------------------
673 // SVGPaintServerFrame methods:
674 already_AddRefed
<gfxPattern
> SVGPatternFrame::GetPaintServerPattern(
675 nsIFrame
* aSource
, const DrawTarget
* aDrawTarget
,
676 const gfxMatrix
& aContextMatrix
, StyleSVGPaint
nsStyleSVG::*aFillOrStroke
,
677 float aGraphicOpacity
, imgDrawingParams
& aImgParams
,
678 const gfxRect
* aOverrideBounds
) {
679 if (aGraphicOpacity
== 0.0f
) {
680 return do_AddRef(new gfxPattern(DeviceColor()));
685 RefPtr
<SourceSurface
> surface
=
686 PaintPattern(aDrawTarget
, &pMatrix
, ToMatrix(aContextMatrix
), aSource
,
687 aFillOrStroke
, aGraphicOpacity
, aOverrideBounds
, aImgParams
);
693 RefPtr
<gfxPattern
> pattern
= new gfxPattern(surface
, pMatrix
);
699 pattern
->SetExtend(ExtendMode::REPEAT
);
700 return pattern
.forget();
703 } // namespace mozilla
705 // -------------------------------------------------------------------------
707 // -------------------------------------------------------------------------
709 nsIFrame
* NS_NewSVGPatternFrame(mozilla::PresShell
* aPresShell
,
710 mozilla::ComputedStyle
* aStyle
) {
711 return new (aPresShell
)
712 mozilla::SVGPatternFrame(aStyle
, aPresShell
->GetPresContext());