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