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/. */
6
7 // Main header first:
8 #include "nsSVGPatternFrame.h"
9
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/gfx/2D.h"
18 #include "nsContentUtils.h"
19 #include "nsGkAtoms.h"
20 #include "nsSVGDisplayableFrame.h"
21 #include "nsStyleContext.h"
22 #include "SVGObserverUtils.h"
23 #include "SVGGeometryFrame.h"
24 #include "mozilla/dom/SVGPatternElement.h"
25 #include "mozilla/dom/SVGUnitTypesBinding.h"
26 #include "nsSVGUtils.h"
27 #include "nsSVGAnimatedTransformList.h"
28 #include "SVGContentUtils.h"
29
30 using namespace mozilla;
31 using namespace mozilla::dom;
32 using namespace mozilla::dom::SVGUnitTypesBinding;
33 using namespace mozilla::gfx;
34 using namespace mozilla::image;
35
36 //----------------------------------------------------------------------
37 // Implementation
38
nsSVGPatternFrame(nsStyleContext * aContext)39 nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext *aContext)
40 : nsSVGPaintServerFrame(aContext, kClassID),
41 mSource(nullptr),
42 mLoopFlag(false),
43 mNoHRefURI(false) {}
44
NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame)45 NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame)
46
47 //----------------------------------------------------------------------
48 // nsIFrame methods:
49
50 nsresult nsSVGPatternFrame::AttributeChanged(int32_t aNameSpaceID,
51 nsAtom *aAttribute,
52 int32_t aModType) {
53 if (aNameSpaceID == kNameSpaceID_None &&
54 (aAttribute == nsGkAtoms::patternUnits ||
55 aAttribute == nsGkAtoms::patternContentUnits ||
56 aAttribute == nsGkAtoms::patternTransform ||
57 aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
58 aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
59 aAttribute == nsGkAtoms::preserveAspectRatio ||
60 aAttribute == nsGkAtoms::viewBox)) {
61 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
62 }
63
64 if ((aNameSpaceID == kNameSpaceID_XLink ||
65 aNameSpaceID == kNameSpaceID_None) &&
66 aAttribute == nsGkAtoms::href) {
67 // Blow away our reference, if any
68 DeleteProperty(SVGObserverUtils::HrefAsPaintingProperty());
69 mNoHRefURI = false;
70 // And update whoever references us
71 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
72 }
73
74 return nsSVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute,
75 aModType);
76 }
77
78 #ifdef DEBUG
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)79 void nsSVGPatternFrame::Init(nsIContent *aContent, nsContainerFrame *aParent,
80 nsIFrame *aPrevInFlow) {
81 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::pattern),
82 "Content is not an SVG pattern");
83
84 nsSVGPaintServerFrame::Init(aContent, aParent, aPrevInFlow);
85 }
86 #endif /* DEBUG */
87
88 //----------------------------------------------------------------------
89 // nsSVGContainerFrame methods:
90
91 // If our GetCanvasTM is getting called, we
92 // need to return *our current* transformation
93 // matrix, which depends on our units parameters
94 // and X, Y, Width, and Height
GetCanvasTM()95 gfxMatrix nsSVGPatternFrame::GetCanvasTM() {
96 if (mCTM) {
97 return *mCTM;
98 }
99
100 // Do we know our rendering parent?
101 if (mSource) {
102 // Yes, use it!
103 return mSource->GetCanvasTM();
104 }
105
106 // We get here when geometry in the <pattern> container is updated
107 return gfxMatrix();
108 }
109
110 // -------------------------------------------------------------------------
111 // Helper functions
112 // -------------------------------------------------------------------------
113
114 /** Calculate the maximum expansion of a matrix */
MaxExpansion(const Matrix & aMatrix)115 static float MaxExpansion(const Matrix &aMatrix) {
116 // maximum expansion derivation from
117 // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
118 // and also implemented in cairo_matrix_transformed_circle_major_axis
119 double a = aMatrix._11;
120 double b = aMatrix._12;
121 double c = aMatrix._21;
122 double d = aMatrix._22;
123 double f = (a * a + b * b + c * c + d * d) / 2;
124 double g = (a * a + b * b - c * c - d * d) / 2;
125 double h = a * c + b * d;
126 return sqrt(f + sqrt(g * g + h * h));
127 }
128
129 // The SVG specification says that the 'patternContentUnits' attribute "has no
130 // effect if attribute ‘viewBox’ is specified". We still need to include a bbox
131 // scale if the viewBox is specified and _patternUnits_ is set to or defaults to
132 // objectBoundingBox though, since in that case the viewBox is relative to the
133 // bbox
IncludeBBoxScale(const nsSVGViewBox & aViewBox,uint32_t aPatternContentUnits,uint32_t aPatternUnits)134 static bool IncludeBBoxScale(const nsSVGViewBox &aViewBox,
135 uint32_t aPatternContentUnits,
136 uint32_t aPatternUnits) {
137 return (!aViewBox.IsExplicitlySet() &&
138 aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) ||
139 (aViewBox.IsExplicitlySet() &&
140 aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
141 }
142
143 // Given the matrix for the pattern element's own transform, this returns a
144 // combined matrix including the transforms applicable to its target.
GetPatternMatrix(uint16_t aPatternUnits,const Matrix & patternTransform,const gfxRect & bbox,const gfxRect & callerBBox,const Matrix & callerCTM)145 static Matrix GetPatternMatrix(uint16_t aPatternUnits,
146 const Matrix &patternTransform,
147 const gfxRect &bbox, const gfxRect &callerBBox,
148 const Matrix &callerCTM) {
149 // We really want the pattern matrix to handle translations
150 gfxFloat minx = bbox.X();
151 gfxFloat miny = bbox.Y();
152
153 if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
154 minx += callerBBox.X();
155 miny += callerBBox.Y();
156 }
157
158 float scale = 1.0f / MaxExpansion(callerCTM);
159 Matrix patternMatrix = patternTransform;
160 patternMatrix.PreScale(scale, scale);
161 patternMatrix.PreTranslate(minx, miny);
162
163 return patternMatrix;
164 }
165
GetTargetGeometry(gfxRect * aBBox,const nsSVGViewBox & aViewBox,uint16_t aPatternContentUnits,uint16_t aPatternUnits,nsIFrame * aTarget,const Matrix & aContextMatrix,const gfxRect * aOverrideBounds)166 static nsresult GetTargetGeometry(gfxRect *aBBox, const nsSVGViewBox &aViewBox,
167 uint16_t aPatternContentUnits,
168 uint16_t aPatternUnits, nsIFrame *aTarget,
169 const Matrix &aContextMatrix,
170 const gfxRect *aOverrideBounds) {
171 *aBBox = aOverrideBounds
172 ? *aOverrideBounds
173 : nsSVGUtils::GetBBox(aTarget,
174 nsSVGUtils::eUseFrameBoundsForOuterSVG |
175 nsSVGUtils::eBBoxIncludeFillGeometry);
176
177 // Sanity check
178 if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) &&
179 (aBBox->Width() <= 0 || aBBox->Height() <= 0)) {
180 return NS_ERROR_FAILURE;
181 }
182
183 // OK, now fix up the bounding box to reflect user coordinates
184 // We handle device unit scaling in pattern matrix
185 float scale = MaxExpansion(aContextMatrix);
186 if (scale <= 0) {
187 return NS_ERROR_FAILURE;
188 }
189 aBBox->Scale(scale);
190 return NS_OK;
191 }
192
PaintPattern(const DrawTarget * aDrawTarget,Matrix * patternMatrix,const Matrix & aContextMatrix,nsIFrame * aSource,nsStyleSVGPaint nsStyleSVG::* aFillOrStroke,float aGraphicOpacity,const gfxRect * aOverrideBounds,imgDrawingParams & aImgParams)193 already_AddRefed<SourceSurface> nsSVGPatternFrame::PaintPattern(
194 const DrawTarget *aDrawTarget, Matrix *patternMatrix,
195 const Matrix &aContextMatrix, nsIFrame *aSource,
196 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, float aGraphicOpacity,
197 const gfxRect *aOverrideBounds, imgDrawingParams &aImgParams) {
198 /*
199 * General approach:
200 * Set the content geometry stuff
201 * Calculate our bbox (using x,y,width,height & patternUnits &
202 * patternTransform)
203 * Create the surface
204 * Calculate the content transformation matrix
205 * Get our children (we may need to get them from another Pattern)
206 * Call SVGPaint on all of our children
207 * Return
208 */
209
210 nsSVGPatternFrame *patternWithChildren = GetPatternWithChildren();
211 if (!patternWithChildren) {
212 // Either no kids or a bad reference
213 return nullptr;
214 }
215 nsIFrame *firstKid = patternWithChildren->mFrames.FirstChild();
216
217 const nsSVGViewBox &viewBox = GetViewBox();
218
219 uint16_t patternContentUnits =
220 GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS);
221 uint16_t patternUnits = GetEnumValue(SVGPatternElement::PATTERNUNITS);
222
223 /*
224 * Get the content geometry information. This is a little tricky --
225 * our parent is probably a <defs>, but we are rendering in the context
226 * of some geometry source. Our content geometry information needs to
227 * come from our rendering parent as opposed to our content parent. We
228 * get that information from aSource, which is passed to us from the
229 * backend renderer.
230 *
231 * There are three "geometries" that we need:
232 * 1) The bounding box for the pattern. We use this to get the
233 * width and height for the surface, and as the return to
234 * GetBBox.
235 * 2) The transformation matrix for the pattern. This is not *quite*
236 * the same as the canvas transformation matrix that we will
237 * provide to our rendering children since we "fudge" it a little
238 * to get the renderer to handle the translations correctly for us.
239 * 3) The CTM that we return to our children who make up the pattern.
240 */
241
242 // Get all of the information we need from our "caller" -- i.e.
243 // the geometry that is being rendered with a pattern
244 gfxRect callerBBox;
245 if (NS_FAILED(GetTargetGeometry(&callerBBox, viewBox, patternContentUnits,
246 patternUnits, aSource, aContextMatrix,
247 aOverrideBounds))) {
248 return nullptr;
249 }
250
251 // Construct the CTM that we will provide to our children when we
252 // render them into the tile.
253 gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits,
254 callerBBox, aContextMatrix, aSource);
255 if (ctm.IsSingular()) {
256 return nullptr;
257 }
258
259 if (patternWithChildren->mCTM) {
260 *patternWithChildren->mCTM = ctm;
261 } else {
262 patternWithChildren->mCTM = new gfxMatrix(ctm);
263 }
264
265 // Get the bounding box of the pattern. This will be used to determine
266 // the size of the surface, and will also be used to define the bounding
267 // box for the pattern tile.
268 gfxRect bbox =
269 GetPatternRect(patternUnits, callerBBox, aContextMatrix, aSource);
270 if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) {
271 return nullptr;
272 }
273
274 // Get the pattern transform
275 Matrix patternTransform = ToMatrix(GetPatternTransform());
276
277 // revert the vector effect transform so that the pattern appears unchanged
278 if (aFillOrStroke == &nsStyleSVG::mStroke) {
279 gfxMatrix userToOuterSVG;
280 if (nsSVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
281 patternTransform *= ToMatrix(userToOuterSVG);
282 if (patternTransform.IsSingular()) {
283 NS_WARNING("Singular matrix painting non-scaling-stroke");
284 return nullptr;
285 }
286 }
287 }
288
289 // Get the transformation matrix that we will hand to the renderer's pattern
290 // routine.
291 *patternMatrix = GetPatternMatrix(patternUnits, patternTransform, bbox,
292 callerBBox, aContextMatrix);
293 if (patternMatrix->IsSingular()) {
294 return nullptr;
295 }
296
297 // Now that we have all of the necessary geometries, we can
298 // create our surface.
299 gfxRect transformedBBox =
300 ThebesRect(patternTransform.TransformBounds(ToRect(bbox)));
301
302 bool resultOverflows;
303 IntSize surfaceSize = nsSVGUtils::ConvertToSurfaceSize(transformedBBox.Size(),
304 &resultOverflows);
305
306 // 0 disables rendering, < 0 is an error
307 if (surfaceSize.width <= 0 || surfaceSize.height <= 0) {
308 return nullptr;
309 }
310
311 gfxFloat patternWidth = bbox.Width();
312 gfxFloat patternHeight = bbox.Height();
313
314 if (resultOverflows || patternWidth != surfaceSize.width ||
315 patternHeight != surfaceSize.height) {
316 // scale drawing to pattern surface size
317 gfxMatrix tempTM = gfxMatrix(surfaceSize.width / patternWidth, 0.0, 0.0,
318 surfaceSize.height / patternHeight, 0.0, 0.0);
319 patternWithChildren->mCTM->PreMultiply(tempTM);
320
321 // and rescale pattern to compensate
322 patternMatrix->PreScale(patternWidth / surfaceSize.width,
323 patternHeight / surfaceSize.height);
324 }
325
326 RefPtr<DrawTarget> dt = aDrawTarget->CreateSimilarDrawTarget(
327 surfaceSize, SurfaceFormat::B8G8R8A8);
328 if (!dt || !dt->IsValid()) {
329 return nullptr;
330 }
331 dt->ClearRect(Rect(0, 0, surfaceSize.width, surfaceSize.height));
332
333 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
334 MOZ_ASSERT(ctx); // already checked the draw target above
335
336 if (aGraphicOpacity != 1.0f) {
337 ctx->Save();
338 ctx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aGraphicOpacity);
339 }
340
341 // OK, now render -- note that we use "firstKid", which
342 // we got at the beginning because it takes care of the
343 // referenced pattern situation for us
344
345 if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) {
346 // Set the geometrical parent of the pattern we are rendering
347 patternWithChildren->mSource = static_cast<SVGGeometryFrame *>(aSource);
348 }
349
350 // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
351 // give back a clear surface if there's a loop
352 if (!(patternWithChildren->GetStateBits() &
353 NS_FRAME_DRAWING_AS_PAINTSERVER)) {
354 AutoSetRestorePaintServerState paintServer(patternWithChildren);
355 for (nsIFrame *kid = firstKid; kid; kid = kid->GetNextSibling()) {
356 // The CTM of each frame referencing us can be different
357 nsSVGDisplayableFrame *SVGFrame = do_QueryFrame(kid);
358 if (SVGFrame) {
359 SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
360 }
361 gfxMatrix tm = *(patternWithChildren->mCTM);
362 if (kid->GetContent()->IsSVGElement()) {
363 tm = static_cast<nsSVGElement *>(kid->GetContent())
364 ->PrependLocalTransformsTo(tm, eUserSpaceToParent);
365 }
366
367 nsSVGUtils::PaintFrameWithEffects(kid, *ctx, tm, aImgParams);
368 }
369 }
370
371 patternWithChildren->mSource = nullptr;
372
373 if (aGraphicOpacity != 1.0f) {
374 ctx->PopGroupAndBlend();
375 ctx->Restore();
376 }
377
378 // caller now owns the surface
379 return dt->Snapshot();
380 }
381
382 /* Will probably need something like this... */
383 // How do we handle the insertion of a new frame?
384 // We really don't want to rerender this every time,
385 // do we?
GetPatternWithChildren()386 nsSVGPatternFrame *nsSVGPatternFrame::GetPatternWithChildren() {
387 // Do we have any children ourselves?
388 if (!mFrames.IsEmpty()) return this;
389
390 // No, see if we chain to someone who does
391
392 // Before we recurse, make sure we'll break reference loops and over long
393 // reference chains:
394 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
395 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
396 &sRefChainLengthCounter);
397 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
398 // Break reference chain
399 return nullptr;
400 }
401
402 nsSVGPatternFrame *next = GetReferencedPattern();
403 if (!next) return nullptr;
404
405 return next->GetPatternWithChildren();
406 }
407
GetEnumValue(uint32_t aIndex,nsIContent * aDefault)408 uint16_t nsSVGPatternFrame::GetEnumValue(uint32_t aIndex,
409 nsIContent *aDefault) {
410 nsSVGEnum &thisEnum =
411 static_cast<SVGPatternElement *>(GetContent())->mEnumAttributes[aIndex];
412
413 if (thisEnum.IsExplicitlySet()) return thisEnum.GetAnimValue();
414
415 // Before we recurse, make sure we'll break reference loops and over long
416 // reference chains:
417 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
418 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
419 &sRefChainLengthCounter);
420 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
421 // Break reference chain
422 return static_cast<SVGPatternElement *>(aDefault)
423 ->mEnumAttributes[aIndex]
424 .GetAnimValue();
425 }
426
427 nsSVGPatternFrame *next = GetReferencedPattern();
428 return next ? next->GetEnumValue(aIndex, aDefault)
429 : static_cast<SVGPatternElement *>(aDefault)
430 ->mEnumAttributes[aIndex]
431 .GetAnimValue();
432 }
433
GetPatternTransformList(nsIContent * aDefault)434 nsSVGAnimatedTransformList *nsSVGPatternFrame::GetPatternTransformList(
435 nsIContent *aDefault) {
436 nsSVGAnimatedTransformList *thisTransformList =
437 static_cast<SVGPatternElement *>(GetContent())
438 ->GetAnimatedTransformList();
439
440 if (thisTransformList && thisTransformList->IsExplicitlySet())
441 return thisTransformList;
442
443 // Before we recurse, make sure we'll break reference loops and over long
444 // reference chains:
445 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
446 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
447 &sRefChainLengthCounter);
448 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
449 // Break reference chain
450 return static_cast<SVGPatternElement *>(aDefault)->mPatternTransform.get();
451 }
452
453 nsSVGPatternFrame *next = GetReferencedPattern();
454 return next ? next->GetPatternTransformList(aDefault)
455 : static_cast<SVGPatternElement *>(aDefault)
456 ->mPatternTransform.get();
457 }
458
GetPatternTransform()459 gfxMatrix nsSVGPatternFrame::GetPatternTransform() {
460 nsSVGAnimatedTransformList *animTransformList =
461 GetPatternTransformList(GetContent());
462 if (!animTransformList) return gfxMatrix();
463
464 return animTransformList->GetAnimValue().GetConsolidationMatrix();
465 }
466
GetViewBox(nsIContent * aDefault)467 const nsSVGViewBox &nsSVGPatternFrame::GetViewBox(nsIContent *aDefault) {
468 const nsSVGViewBox &thisViewBox =
469 static_cast<SVGPatternElement *>(GetContent())->mViewBox;
470
471 if (thisViewBox.IsExplicitlySet()) return thisViewBox;
472
473 // Before we recurse, make sure we'll break reference loops and over long
474 // reference chains:
475 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
476 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
477 &sRefChainLengthCounter);
478 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
479 // Break reference chain
480 return static_cast<SVGPatternElement *>(aDefault)->mViewBox;
481 }
482
483 nsSVGPatternFrame *next = GetReferencedPattern();
484 return next ? next->GetViewBox(aDefault)
485 : static_cast<SVGPatternElement *>(aDefault)->mViewBox;
486 }
487
GetPreserveAspectRatio(nsIContent * aDefault)488 const SVGAnimatedPreserveAspectRatio &nsSVGPatternFrame::GetPreserveAspectRatio(
489 nsIContent *aDefault) {
490 const SVGAnimatedPreserveAspectRatio &thisPar =
491 static_cast<SVGPatternElement *>(GetContent())->mPreserveAspectRatio;
492
493 if (thisPar.IsExplicitlySet()) return thisPar;
494
495 // Before we recurse, make sure we'll break reference loops and over long
496 // reference chains:
497 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
498 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
499 &sRefChainLengthCounter);
500 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
501 // Break reference chain
502 return static_cast<SVGPatternElement *>(aDefault)->mPreserveAspectRatio;
503 }
504
505 nsSVGPatternFrame *next = GetReferencedPattern();
506 return next
507 ? next->GetPreserveAspectRatio(aDefault)
508 : static_cast<SVGPatternElement *>(aDefault)->mPreserveAspectRatio;
509 }
510
GetLengthValue(uint32_t aIndex,nsIContent * aDefault)511 const nsSVGLength2 *nsSVGPatternFrame::GetLengthValue(uint32_t aIndex,
512 nsIContent *aDefault) {
513 const nsSVGLength2 *thisLength =
514 &static_cast<SVGPatternElement *>(GetContent())
515 ->mLengthAttributes[aIndex];
516
517 if (thisLength->IsExplicitlySet()) return thisLength;
518
519 // Before we recurse, make sure we'll break reference loops and over long
520 // reference chains:
521 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
522 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
523 &sRefChainLengthCounter);
524 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
525 // Break reference chain
526 return &static_cast<SVGPatternElement *>(aDefault)
527 ->mLengthAttributes[aIndex];
528 }
529
530 nsSVGPatternFrame *next = GetReferencedPattern();
531 return next ? next->GetLengthValue(aIndex, aDefault)
532 : &static_cast<SVGPatternElement *>(aDefault)
533 ->mLengthAttributes[aIndex];
534 }
535
536 // Private (helper) methods
GetReferencedPattern()537 nsSVGPatternFrame *nsSVGPatternFrame::GetReferencedPattern() {
538 if (mNoHRefURI) return nullptr;
539
540 nsSVGPaintingProperty *property =
541 GetProperty(SVGObserverUtils::HrefAsPaintingProperty());
542
543 if (!property) {
544 // Fetch our pattern element's href or xlink:href attribute
545 SVGPatternElement *pattern = static_cast<SVGPatternElement *>(GetContent());
546 nsAutoString href;
547 if (pattern->mStringAttributes[SVGPatternElement::HREF].IsExplicitlySet()) {
548 pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(href,
549 pattern);
550 } else {
551 pattern->mStringAttributes[SVGPatternElement::XLINK_HREF].GetAnimValue(
552 href, pattern);
553 }
554
555 if (href.IsEmpty()) {
556 mNoHRefURI = true;
557 return nullptr; // no URL
558 }
559
560 // Convert href to an nsIURI
561 nsCOMPtr<nsIURI> targetURI;
562 nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
563 nsContentUtils::NewURIWithDocumentCharset(
564 getter_AddRefs(targetURI), href, mContent->GetUncomposedDoc(), base);
565
566 property = SVGObserverUtils::GetPaintingProperty(
567 targetURI, this, SVGObserverUtils::HrefAsPaintingProperty());
568 if (!property) return nullptr;
569 }
570
571 nsIFrame *result = property->GetReferencedFrame();
572 if (!result) return nullptr;
573
574 LayoutFrameType frameType = result->Type();
575 if (frameType != LayoutFrameType::SVGPattern) return nullptr;
576
577 return static_cast<nsSVGPatternFrame *>(result);
578 }
579
GetPatternRect(uint16_t aPatternUnits,const gfxRect & aTargetBBox,const Matrix & aTargetCTM,nsIFrame * aTarget)580 gfxRect nsSVGPatternFrame::GetPatternRect(uint16_t aPatternUnits,
581 const gfxRect &aTargetBBox,
582 const Matrix &aTargetCTM,
583 nsIFrame *aTarget) {
584 // We need to initialize our box
585 float x, y, width, height;
586
587 // Get the pattern x,y,width, and height
588 const nsSVGLength2 *tmpX, *tmpY, *tmpHeight, *tmpWidth;
589 tmpX = GetLengthValue(SVGPatternElement::ATTR_X);
590 tmpY = GetLengthValue(SVGPatternElement::ATTR_Y);
591 tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT);
592 tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH);
593
594 if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
595 x = nsSVGUtils::ObjectSpace(aTargetBBox, tmpX);
596 y = nsSVGUtils::ObjectSpace(aTargetBBox, tmpY);
597 width = nsSVGUtils::ObjectSpace(aTargetBBox, tmpWidth);
598 height = nsSVGUtils::ObjectSpace(aTargetBBox, tmpHeight);
599 } else {
600 float scale = MaxExpansion(aTargetCTM);
601 x = nsSVGUtils::UserSpace(aTarget, tmpX) * scale;
602 y = nsSVGUtils::UserSpace(aTarget, tmpY) * scale;
603 width = nsSVGUtils::UserSpace(aTarget, tmpWidth) * scale;
604 height = nsSVGUtils::UserSpace(aTarget, tmpHeight) * scale;
605 }
606
607 return gfxRect(x, y, width, height);
608 }
609
ConstructCTM(const nsSVGViewBox & aViewBox,uint16_t aPatternContentUnits,uint16_t aPatternUnits,const gfxRect & callerBBox,const Matrix & callerCTM,nsIFrame * aTarget)610 gfxMatrix nsSVGPatternFrame::ConstructCTM(const nsSVGViewBox &aViewBox,
611 uint16_t aPatternContentUnits,
612 uint16_t aPatternUnits,
613 const gfxRect &callerBBox,
614 const Matrix &callerCTM,
615 nsIFrame *aTarget) {
616 SVGViewportElement *ctx = nullptr;
617 nsIContent *targetContent = aTarget->GetContent();
618 gfxFloat scaleX, scaleY;
619
620 // The objectBoundingBox conversion must be handled in the CTM:
621 if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) {
622 scaleX = callerBBox.Width();
623 scaleY = callerBBox.Height();
624 } else {
625 if (targetContent->IsSVGElement()) {
626 ctx = static_cast<nsSVGElement *>(targetContent)->GetCtx();
627 }
628 scaleX = scaleY = MaxExpansion(callerCTM);
629 }
630
631 if (!aViewBox.IsExplicitlySet()) {
632 return gfxMatrix(scaleX, 0.0, 0.0, scaleY, 0.0, 0.0);
633 }
634 const nsSVGViewBoxRect viewBoxRect = aViewBox.GetAnimValue();
635
636 if (viewBoxRect.height <= 0.0f || viewBoxRect.width <= 0.0f) {
637 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
638 }
639
640 float viewportWidth, viewportHeight;
641 if (targetContent->IsSVGElement()) {
642 // If we're dealing with an SVG target only retrieve the context once.
643 // Calling the nsIFrame* variant of GetAnimValue would look it up on
644 // every call.
645 viewportWidth =
646 GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(ctx);
647 viewportHeight =
648 GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(ctx);
649 } else {
650 // No SVG target, call the nsIFrame* variant of GetAnimValue.
651 viewportWidth =
652 GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(aTarget);
653 viewportHeight =
654 GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(aTarget);
655 }
656
657 if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
658 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
659 }
660
661 Matrix tm = SVGContentUtils::GetViewBoxTransform(
662 viewportWidth * scaleX, viewportHeight * scaleY, viewBoxRect.x,
663 viewBoxRect.y, viewBoxRect.width, viewBoxRect.height,
664 GetPreserveAspectRatio());
665
666 return ThebesMatrix(tm);
667 }
668
669 //----------------------------------------------------------------------
670 // nsSVGPaintServerFrame methods:
GetPaintServerPattern(nsIFrame * aSource,const DrawTarget * aDrawTarget,const gfxMatrix & aContextMatrix,nsStyleSVGPaint nsStyleSVG::* aFillOrStroke,float aGraphicOpacity,imgDrawingParams & aImgParams,const gfxRect * aOverrideBounds)671 already_AddRefed<gfxPattern> nsSVGPatternFrame::GetPaintServerPattern(
672 nsIFrame *aSource, const DrawTarget *aDrawTarget,
673 const gfxMatrix &aContextMatrix, nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
674 float aGraphicOpacity, imgDrawingParams &aImgParams,
675 const gfxRect *aOverrideBounds) {
676 if (aGraphicOpacity == 0.0f) {
677 return do_AddRef(new gfxPattern(Color()));
678 }
679
680 // Paint it!
681 Matrix pMatrix;
682 RefPtr<SourceSurface> surface =
683 PaintPattern(aDrawTarget, &pMatrix, ToMatrix(aContextMatrix), aSource,
684 aFillOrStroke, aGraphicOpacity, aOverrideBounds, aImgParams);
685
686 if (!surface) {
687 return nullptr;
688 }
689
690 RefPtr<gfxPattern> pattern = new gfxPattern(surface, pMatrix);
691
692 if (!pattern) {
693 return nullptr;
694 }
695
696 pattern->SetExtend(ExtendMode::REPEAT);
697 return pattern.forget();
698 }
699
700 // -------------------------------------------------------------------------
701 // Public functions
702 // -------------------------------------------------------------------------
703
NS_NewSVGPatternFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)704 nsIFrame *NS_NewSVGPatternFrame(nsIPresShell *aPresShell,
705 nsStyleContext *aContext) {
706 return new (aPresShell) nsSVGPatternFrame(aContext);
707 }
708