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