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