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