|
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 "nsSVGGradientFrame.h" |
|
8 #include <algorithm> |
|
9 |
|
10 // Keep others in (case-insensitive) order: |
|
11 #include "gfxPattern.h" |
|
12 #include "mozilla/dom/SVGGradientElement.h" |
|
13 #include "mozilla/dom/SVGStopElement.h" |
|
14 #include "nsContentUtils.h" |
|
15 #include "nsSVGEffects.h" |
|
16 #include "nsSVGAnimatedTransformList.h" |
|
17 #include "gfxColor.h" |
|
18 |
|
19 // XXX Tight coupling with content classes ahead! |
|
20 |
|
21 using namespace mozilla; |
|
22 using namespace mozilla::dom; |
|
23 |
|
24 //---------------------------------------------------------------------- |
|
25 // Helper classes |
|
26 |
|
27 class MOZ_STACK_CLASS nsSVGGradientFrame::AutoGradientReferencer |
|
28 { |
|
29 public: |
|
30 AutoGradientReferencer(nsSVGGradientFrame *aFrame |
|
31 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
32 : mFrame(aFrame) |
|
33 { |
|
34 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
35 // Reference loops should normally be detected in advance and handled, so |
|
36 // we're not expecting to encounter them here |
|
37 NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!"); |
|
38 mFrame->mLoopFlag = true; |
|
39 } |
|
40 ~AutoGradientReferencer() { |
|
41 mFrame->mLoopFlag = false; |
|
42 } |
|
43 private: |
|
44 nsSVGGradientFrame *mFrame; |
|
45 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
|
46 }; |
|
47 |
|
48 //---------------------------------------------------------------------- |
|
49 // Implementation |
|
50 |
|
51 nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext) : |
|
52 nsSVGGradientFrameBase(aContext), |
|
53 mLoopFlag(false), |
|
54 mNoHRefURI(false) |
|
55 { |
|
56 } |
|
57 |
|
58 NS_IMPL_FRAMEARENA_HELPERS(nsSVGGradientFrame) |
|
59 |
|
60 //---------------------------------------------------------------------- |
|
61 // nsIFrame methods: |
|
62 |
|
63 nsresult |
|
64 nsSVGGradientFrame::AttributeChanged(int32_t aNameSpaceID, |
|
65 nsIAtom* aAttribute, |
|
66 int32_t aModType) |
|
67 { |
|
68 if (aNameSpaceID == kNameSpaceID_None && |
|
69 (aAttribute == nsGkAtoms::gradientUnits || |
|
70 aAttribute == nsGkAtoms::gradientTransform || |
|
71 aAttribute == nsGkAtoms::spreadMethod)) { |
|
72 nsSVGEffects::InvalidateDirectRenderingObservers(this); |
|
73 } else if (aNameSpaceID == kNameSpaceID_XLink && |
|
74 aAttribute == nsGkAtoms::href) { |
|
75 // Blow away our reference, if any |
|
76 Properties().Delete(nsSVGEffects::HrefProperty()); |
|
77 mNoHRefURI = false; |
|
78 // And update whoever references us |
|
79 nsSVGEffects::InvalidateDirectRenderingObservers(this); |
|
80 } |
|
81 |
|
82 return nsSVGGradientFrameBase::AttributeChanged(aNameSpaceID, |
|
83 aAttribute, aModType); |
|
84 } |
|
85 |
|
86 //---------------------------------------------------------------------- |
|
87 |
|
88 uint16_t |
|
89 nsSVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault) |
|
90 { |
|
91 const nsSVGEnum& thisEnum = |
|
92 static_cast<dom::SVGGradientElement*>(mContent)->mEnumAttributes[aIndex]; |
|
93 |
|
94 if (thisEnum.IsExplicitlySet()) |
|
95 return thisEnum.GetAnimValue(); |
|
96 |
|
97 AutoGradientReferencer gradientRef(this); |
|
98 |
|
99 nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); |
|
100 return next ? next->GetEnumValue(aIndex, aDefault) : |
|
101 static_cast<dom::SVGGradientElement*>(aDefault)-> |
|
102 mEnumAttributes[aIndex].GetAnimValue(); |
|
103 } |
|
104 |
|
105 uint16_t |
|
106 nsSVGGradientFrame::GetGradientUnits() |
|
107 { |
|
108 // This getter is called every time the others are called - maybe cache it? |
|
109 return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS); |
|
110 } |
|
111 |
|
112 uint16_t |
|
113 nsSVGGradientFrame::GetSpreadMethod() |
|
114 { |
|
115 return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD); |
|
116 } |
|
117 |
|
118 const nsSVGAnimatedTransformList* |
|
119 nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault) |
|
120 { |
|
121 nsSVGAnimatedTransformList *thisTransformList = |
|
122 static_cast<dom::SVGGradientElement*>(mContent)->GetAnimatedTransformList(); |
|
123 |
|
124 if (thisTransformList && thisTransformList->IsExplicitlySet()) |
|
125 return thisTransformList; |
|
126 |
|
127 AutoGradientReferencer gradientRef(this); |
|
128 |
|
129 nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); |
|
130 return next ? next->GetGradientTransformList(aDefault) : |
|
131 static_cast<const dom::SVGGradientElement*>(aDefault) |
|
132 ->mGradientTransform.get(); |
|
133 } |
|
134 |
|
135 gfxMatrix |
|
136 nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource, |
|
137 const gfxRect *aOverrideBounds) |
|
138 { |
|
139 gfxMatrix bboxMatrix; |
|
140 |
|
141 uint16_t gradientUnits = GetGradientUnits(); |
|
142 if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) { |
|
143 NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, |
|
144 "Unknown gradientUnits type"); |
|
145 // objectBoundingBox is the default anyway |
|
146 |
|
147 gfxRect bbox = |
|
148 aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aSource); |
|
149 bboxMatrix = |
|
150 gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); |
|
151 } |
|
152 |
|
153 const nsSVGAnimatedTransformList* animTransformList = |
|
154 GetGradientTransformList(mContent); |
|
155 if (!animTransformList) |
|
156 return bboxMatrix; |
|
157 |
|
158 gfxMatrix gradientTransform = |
|
159 animTransformList->GetAnimValue().GetConsolidationMatrix(); |
|
160 return bboxMatrix.PreMultiply(gradientTransform); |
|
161 } |
|
162 |
|
163 dom::SVGLinearGradientElement* |
|
164 nsSVGGradientFrame::GetLinearGradientWithLength(uint32_t aIndex, |
|
165 dom::SVGLinearGradientElement* aDefault) |
|
166 { |
|
167 // If this was a linear gradient with the required length, we would have |
|
168 // already found it in nsSVGLinearGradientFrame::GetLinearGradientWithLength. |
|
169 // Since we didn't find the length, continue looking down the chain. |
|
170 |
|
171 AutoGradientReferencer gradientRef(this); |
|
172 |
|
173 nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); |
|
174 return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault; |
|
175 } |
|
176 |
|
177 dom::SVGRadialGradientElement* |
|
178 nsSVGGradientFrame::GetRadialGradientWithLength(uint32_t aIndex, |
|
179 dom::SVGRadialGradientElement* aDefault) |
|
180 { |
|
181 // If this was a radial gradient with the required length, we would have |
|
182 // already found it in nsSVGRadialGradientFrame::GetRadialGradientWithLength. |
|
183 // Since we didn't find the length, continue looking down the chain. |
|
184 |
|
185 AutoGradientReferencer gradientRef(this); |
|
186 |
|
187 nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); |
|
188 return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault; |
|
189 } |
|
190 |
|
191 //---------------------------------------------------------------------- |
|
192 // nsSVGPaintServerFrame methods: |
|
193 |
|
194 //helper |
|
195 static void GetStopInformation(nsIFrame* aStopFrame, |
|
196 float *aOffset, |
|
197 nscolor *aStopColor, |
|
198 float *aStopOpacity) |
|
199 { |
|
200 nsIContent* stopContent = aStopFrame->GetContent(); |
|
201 MOZ_ASSERT(stopContent && stopContent->IsSVG(nsGkAtoms::stop)); |
|
202 |
|
203 static_cast<SVGStopElement*>(stopContent)-> |
|
204 GetAnimatedNumberValues(aOffset, nullptr); |
|
205 |
|
206 *aOffset = mozilla::clamped(*aOffset, 0.0f, 1.0f); |
|
207 *aStopColor = aStopFrame->StyleSVGReset()->mStopColor; |
|
208 *aStopOpacity = aStopFrame->StyleSVGReset()->mStopOpacity; |
|
209 } |
|
210 |
|
211 already_AddRefed<gfxPattern> |
|
212 nsSVGGradientFrame::GetPaintServerPattern(nsIFrame *aSource, |
|
213 const gfxMatrix& aContextMatrix, |
|
214 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, |
|
215 float aGraphicOpacity, |
|
216 const gfxRect *aOverrideBounds) |
|
217 { |
|
218 uint16_t gradientUnits = GetGradientUnits(); |
|
219 MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX || |
|
220 gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE); |
|
221 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { |
|
222 // Set mSource for this consumer. |
|
223 // If this gradient is applied to text, our caller will be the glyph, which |
|
224 // is not an element, so we need to get the parent |
|
225 mSource = aSource->GetContent()->IsNodeOfType(nsINode::eTEXT) ? |
|
226 aSource->GetParent() : aSource; |
|
227 } |
|
228 |
|
229 nsAutoTArray<nsIFrame*,8> stopFrames; |
|
230 GetStopFrames(&stopFrames); |
|
231 |
|
232 uint32_t nStops = stopFrames.Length(); |
|
233 |
|
234 // SVG specification says that no stops should be treated like |
|
235 // the corresponding fill or stroke had "none" specified. |
|
236 if (nStops == 0) { |
|
237 nsRefPtr<gfxPattern> pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0)); |
|
238 return pattern.forget(); |
|
239 } |
|
240 |
|
241 if (nStops == 1 || GradientVectorLengthIsZero()) { |
|
242 // The gradient paints a single colour, using the stop-color of the last |
|
243 // gradient step if there are more than one. |
|
244 float stopOpacity = stopFrames[nStops-1]->StyleSVGReset()->mStopOpacity; |
|
245 nscolor stopColor = stopFrames[nStops-1]->StyleSVGReset()->mStopColor; |
|
246 |
|
247 nsRefPtr<gfxPattern> pattern = new gfxPattern( |
|
248 gfxRGBA(NS_GET_R(stopColor)/255.0, |
|
249 NS_GET_G(stopColor)/255.0, |
|
250 NS_GET_B(stopColor)/255.0, |
|
251 NS_GET_A(stopColor)/255.0 * |
|
252 stopOpacity * aGraphicOpacity)); |
|
253 return pattern.forget(); |
|
254 } |
|
255 |
|
256 // Get the transform list (if there is one). We do this after the returns |
|
257 // above since this call can be expensive when "gradientUnits" is set to |
|
258 // "objectBoundingBox" (since that requiring a GetBBox() call). |
|
259 gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds); |
|
260 |
|
261 if (patternMatrix.IsSingular()) { |
|
262 return nullptr; |
|
263 } |
|
264 |
|
265 // revert the vector effect transform so that the gradient appears unchanged |
|
266 if (aFillOrStroke == &nsStyleSVG::mStroke) { |
|
267 patternMatrix.Multiply(nsSVGUtils::GetStrokeTransform(aSource).Invert()); |
|
268 } |
|
269 |
|
270 patternMatrix.Invert(); |
|
271 |
|
272 nsRefPtr<gfxPattern> gradient = CreateGradient(); |
|
273 if (!gradient || gradient->CairoStatus()) |
|
274 return nullptr; |
|
275 |
|
276 uint16_t aSpread = GetSpreadMethod(); |
|
277 if (aSpread == SVG_SPREADMETHOD_PAD) |
|
278 gradient->SetExtend(gfxPattern::EXTEND_PAD); |
|
279 else if (aSpread == SVG_SPREADMETHOD_REFLECT) |
|
280 gradient->SetExtend(gfxPattern::EXTEND_REFLECT); |
|
281 else if (aSpread == SVG_SPREADMETHOD_REPEAT) |
|
282 gradient->SetExtend(gfxPattern::EXTEND_REPEAT); |
|
283 |
|
284 gradient->SetMatrix(patternMatrix); |
|
285 |
|
286 // setup stops |
|
287 float lastOffset = 0.0f; |
|
288 |
|
289 for (uint32_t i = 0; i < nStops; i++) { |
|
290 float offset, stopOpacity; |
|
291 nscolor stopColor; |
|
292 |
|
293 GetStopInformation(stopFrames[i], &offset, &stopColor, &stopOpacity); |
|
294 |
|
295 if (offset < lastOffset) |
|
296 offset = lastOffset; |
|
297 else |
|
298 lastOffset = offset; |
|
299 |
|
300 gradient->AddColorStop(offset, |
|
301 gfxRGBA(NS_GET_R(stopColor)/255.0, |
|
302 NS_GET_G(stopColor)/255.0, |
|
303 NS_GET_B(stopColor)/255.0, |
|
304 NS_GET_A(stopColor)/255.0 * |
|
305 stopOpacity * aGraphicOpacity)); |
|
306 } |
|
307 |
|
308 return gradient.forget(); |
|
309 } |
|
310 |
|
311 // Private (helper) methods |
|
312 |
|
313 nsSVGGradientFrame * |
|
314 nsSVGGradientFrame::GetReferencedGradient() |
|
315 { |
|
316 if (mNoHRefURI) |
|
317 return nullptr; |
|
318 |
|
319 nsSVGPaintingProperty *property = static_cast<nsSVGPaintingProperty*> |
|
320 (Properties().Get(nsSVGEffects::HrefProperty())); |
|
321 |
|
322 if (!property) { |
|
323 // Fetch our gradient element's xlink:href attribute |
|
324 dom::SVGGradientElement*grad = static_cast<dom::SVGGradientElement*>(mContent); |
|
325 nsAutoString href; |
|
326 grad->mStringAttributes[dom::SVGGradientElement::HREF].GetAnimValue(href, grad); |
|
327 if (href.IsEmpty()) { |
|
328 mNoHRefURI = true; |
|
329 return nullptr; // no URL |
|
330 } |
|
331 |
|
332 // Convert href to an nsIURI |
|
333 nsCOMPtr<nsIURI> targetURI; |
|
334 nsCOMPtr<nsIURI> base = mContent->GetBaseURI(); |
|
335 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, |
|
336 mContent->GetCurrentDoc(), base); |
|
337 |
|
338 property = |
|
339 nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty()); |
|
340 if (!property) |
|
341 return nullptr; |
|
342 } |
|
343 |
|
344 nsIFrame *result = property->GetReferencedFrame(); |
|
345 if (!result) |
|
346 return nullptr; |
|
347 |
|
348 nsIAtom* frameType = result->GetType(); |
|
349 if (frameType != nsGkAtoms::svgLinearGradientFrame && |
|
350 frameType != nsGkAtoms::svgRadialGradientFrame) |
|
351 return nullptr; |
|
352 |
|
353 return static_cast<nsSVGGradientFrame*>(result); |
|
354 } |
|
355 |
|
356 nsSVGGradientFrame * |
|
357 nsSVGGradientFrame::GetReferencedGradientIfNotInUse() |
|
358 { |
|
359 nsSVGGradientFrame *referenced = GetReferencedGradient(); |
|
360 if (!referenced) |
|
361 return nullptr; |
|
362 |
|
363 if (referenced->mLoopFlag) { |
|
364 // XXXjwatt: we should really send an error to the JavaScript Console here: |
|
365 NS_WARNING("gradient reference loop detected while inheriting attribute!"); |
|
366 return nullptr; |
|
367 } |
|
368 |
|
369 return referenced; |
|
370 } |
|
371 |
|
372 void |
|
373 nsSVGGradientFrame::GetStopFrames(nsTArray<nsIFrame*>* aStopFrames) |
|
374 { |
|
375 nsIFrame *stopFrame = nullptr; |
|
376 for (stopFrame = mFrames.FirstChild(); stopFrame; |
|
377 stopFrame = stopFrame->GetNextSibling()) { |
|
378 if (stopFrame->GetType() == nsGkAtoms::svgStopFrame) { |
|
379 aStopFrames->AppendElement(stopFrame); |
|
380 } |
|
381 } |
|
382 if (aStopFrames->Length() > 0) { |
|
383 return; |
|
384 } |
|
385 |
|
386 // Our gradient element doesn't have stops - try to "inherit" them |
|
387 |
|
388 AutoGradientReferencer gradientRef(this); |
|
389 nsSVGGradientFrame* next = GetReferencedGradientIfNotInUse(); |
|
390 if (!next) { |
|
391 return; |
|
392 } |
|
393 |
|
394 return next->GetStopFrames(aStopFrames); |
|
395 } |
|
396 |
|
397 // ------------------------------------------------------------------------- |
|
398 // Linear Gradients |
|
399 // ------------------------------------------------------------------------- |
|
400 |
|
401 #ifdef DEBUG |
|
402 void |
|
403 nsSVGLinearGradientFrame::Init(nsIContent* aContent, |
|
404 nsIFrame* aParent, |
|
405 nsIFrame* aPrevInFlow) |
|
406 { |
|
407 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::linearGradient), |
|
408 "Content is not an SVG linearGradient"); |
|
409 |
|
410 nsSVGLinearGradientFrameBase::Init(aContent, aParent, aPrevInFlow); |
|
411 } |
|
412 #endif /* DEBUG */ |
|
413 |
|
414 nsIAtom* |
|
415 nsSVGLinearGradientFrame::GetType() const |
|
416 { |
|
417 return nsGkAtoms::svgLinearGradientFrame; |
|
418 } |
|
419 |
|
420 nsresult |
|
421 nsSVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID, |
|
422 nsIAtom* aAttribute, |
|
423 int32_t aModType) |
|
424 { |
|
425 if (aNameSpaceID == kNameSpaceID_None && |
|
426 (aAttribute == nsGkAtoms::x1 || |
|
427 aAttribute == nsGkAtoms::y1 || |
|
428 aAttribute == nsGkAtoms::x2 || |
|
429 aAttribute == nsGkAtoms::y2)) { |
|
430 nsSVGEffects::InvalidateDirectRenderingObservers(this); |
|
431 } |
|
432 |
|
433 return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, |
|
434 aAttribute, aModType); |
|
435 } |
|
436 |
|
437 //---------------------------------------------------------------------- |
|
438 |
|
439 float |
|
440 nsSVGLinearGradientFrame::GetLengthValue(uint32_t aIndex) |
|
441 { |
|
442 dom::SVGLinearGradientElement* lengthElement = |
|
443 GetLinearGradientWithLength(aIndex, |
|
444 static_cast<dom::SVGLinearGradientElement*>(mContent)); |
|
445 // We passed in mContent as a fallback, so, assuming mContent is non-null, the |
|
446 // return value should also be non-null. |
|
447 NS_ABORT_IF_FALSE(lengthElement, |
|
448 "Got unexpected null element from GetLinearGradientWithLength"); |
|
449 const nsSVGLength2 &length = lengthElement->mLengthAttributes[aIndex]; |
|
450 |
|
451 // Object bounding box units are handled by setting the appropriate |
|
452 // transform in GetGradientTransform, but we need to handle user |
|
453 // space units as part of the individual Get* routines. Fixes 323669. |
|
454 |
|
455 uint16_t gradientUnits = GetGradientUnits(); |
|
456 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { |
|
457 return nsSVGUtils::UserSpace(mSource, &length); |
|
458 } |
|
459 |
|
460 NS_ASSERTION( |
|
461 gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, |
|
462 "Unknown gradientUnits type"); |
|
463 |
|
464 return length.GetAnimValue(static_cast<SVGSVGElement*>(nullptr)); |
|
465 } |
|
466 |
|
467 dom::SVGLinearGradientElement* |
|
468 nsSVGLinearGradientFrame::GetLinearGradientWithLength(uint32_t aIndex, |
|
469 dom::SVGLinearGradientElement* aDefault) |
|
470 { |
|
471 dom::SVGLinearGradientElement* thisElement = |
|
472 static_cast<dom::SVGLinearGradientElement*>(mContent); |
|
473 const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; |
|
474 |
|
475 if (length.IsExplicitlySet()) { |
|
476 return thisElement; |
|
477 } |
|
478 |
|
479 return nsSVGLinearGradientFrameBase::GetLinearGradientWithLength(aIndex, |
|
480 aDefault); |
|
481 } |
|
482 |
|
483 bool |
|
484 nsSVGLinearGradientFrame::GradientVectorLengthIsZero() |
|
485 { |
|
486 return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) == |
|
487 GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) && |
|
488 GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) == |
|
489 GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); |
|
490 } |
|
491 |
|
492 already_AddRefed<gfxPattern> |
|
493 nsSVGLinearGradientFrame::CreateGradient() |
|
494 { |
|
495 float x1, y1, x2, y2; |
|
496 |
|
497 x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1); |
|
498 y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1); |
|
499 x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2); |
|
500 y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); |
|
501 |
|
502 nsRefPtr<gfxPattern> pattern = new gfxPattern(x1, y1, x2, y2); |
|
503 return pattern.forget(); |
|
504 } |
|
505 |
|
506 // ------------------------------------------------------------------------- |
|
507 // Radial Gradients |
|
508 // ------------------------------------------------------------------------- |
|
509 |
|
510 #ifdef DEBUG |
|
511 void |
|
512 nsSVGRadialGradientFrame::Init(nsIContent* aContent, |
|
513 nsIFrame* aParent, |
|
514 nsIFrame* aPrevInFlow) |
|
515 { |
|
516 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::radialGradient), |
|
517 "Content is not an SVG radialGradient"); |
|
518 |
|
519 nsSVGRadialGradientFrameBase::Init(aContent, aParent, aPrevInFlow); |
|
520 } |
|
521 #endif /* DEBUG */ |
|
522 |
|
523 nsIAtom* |
|
524 nsSVGRadialGradientFrame::GetType() const |
|
525 { |
|
526 return nsGkAtoms::svgRadialGradientFrame; |
|
527 } |
|
528 |
|
529 nsresult |
|
530 nsSVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID, |
|
531 nsIAtom* aAttribute, |
|
532 int32_t aModType) |
|
533 { |
|
534 if (aNameSpaceID == kNameSpaceID_None && |
|
535 (aAttribute == nsGkAtoms::r || |
|
536 aAttribute == nsGkAtoms::cx || |
|
537 aAttribute == nsGkAtoms::cy || |
|
538 aAttribute == nsGkAtoms::fx || |
|
539 aAttribute == nsGkAtoms::fy)) { |
|
540 nsSVGEffects::InvalidateDirectRenderingObservers(this); |
|
541 } |
|
542 |
|
543 return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, |
|
544 aAttribute, aModType); |
|
545 } |
|
546 |
|
547 //---------------------------------------------------------------------- |
|
548 |
|
549 float |
|
550 nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex) |
|
551 { |
|
552 dom::SVGRadialGradientElement* lengthElement = |
|
553 GetRadialGradientWithLength(aIndex, |
|
554 static_cast<dom::SVGRadialGradientElement*>(mContent)); |
|
555 // We passed in mContent as a fallback, so, assuming mContent is non-null, |
|
556 // the return value should also be non-null. |
|
557 NS_ABORT_IF_FALSE(lengthElement, |
|
558 "Got unexpected null element from GetRadialGradientWithLength"); |
|
559 return GetLengthValueFromElement(aIndex, *lengthElement); |
|
560 } |
|
561 |
|
562 float |
|
563 nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex, float aDefaultValue) |
|
564 { |
|
565 dom::SVGRadialGradientElement* lengthElement = |
|
566 GetRadialGradientWithLength(aIndex, nullptr); |
|
567 |
|
568 return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement) |
|
569 : aDefaultValue; |
|
570 } |
|
571 |
|
572 float |
|
573 nsSVGRadialGradientFrame::GetLengthValueFromElement(uint32_t aIndex, |
|
574 dom::SVGRadialGradientElement& aElement) |
|
575 { |
|
576 const nsSVGLength2 &length = aElement.mLengthAttributes[aIndex]; |
|
577 |
|
578 // Object bounding box units are handled by setting the appropriate |
|
579 // transform in GetGradientTransform, but we need to handle user |
|
580 // space units as part of the individual Get* routines. Fixes 323669. |
|
581 |
|
582 uint16_t gradientUnits = GetGradientUnits(); |
|
583 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { |
|
584 return nsSVGUtils::UserSpace(mSource, &length); |
|
585 } |
|
586 |
|
587 NS_ASSERTION( |
|
588 gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, |
|
589 "Unknown gradientUnits type"); |
|
590 |
|
591 return length.GetAnimValue(static_cast<SVGSVGElement*>(nullptr)); |
|
592 } |
|
593 |
|
594 dom::SVGRadialGradientElement* |
|
595 nsSVGRadialGradientFrame::GetRadialGradientWithLength(uint32_t aIndex, |
|
596 dom::SVGRadialGradientElement* aDefault) |
|
597 { |
|
598 dom::SVGRadialGradientElement* thisElement = |
|
599 static_cast<dom::SVGRadialGradientElement*>(mContent); |
|
600 const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; |
|
601 |
|
602 if (length.IsExplicitlySet()) { |
|
603 return thisElement; |
|
604 } |
|
605 |
|
606 return nsSVGRadialGradientFrameBase::GetRadialGradientWithLength(aIndex, |
|
607 aDefault); |
|
608 } |
|
609 |
|
610 bool |
|
611 nsSVGRadialGradientFrame::GradientVectorLengthIsZero() |
|
612 { |
|
613 return GetLengthValue(dom::SVGRadialGradientElement::ATTR_R) == 0; |
|
614 } |
|
615 |
|
616 already_AddRefed<gfxPattern> |
|
617 nsSVGRadialGradientFrame::CreateGradient() |
|
618 { |
|
619 float cx, cy, r, fx, fy; |
|
620 |
|
621 cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX); |
|
622 cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY); |
|
623 r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R); |
|
624 // If fx or fy are not set, use cx/cy instead |
|
625 fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx); |
|
626 fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy); |
|
627 |
|
628 if (fx != cx || fy != cy) { |
|
629 // The focal point (fFx and fFy) must be clamped to be *inside* - not on - |
|
630 // the circumference of the gradient or we'll get rendering anomalies. We |
|
631 // calculate the distance from the focal point to the gradient center and |
|
632 // make sure it is *less* than the gradient radius. |
|
633 // 1/128 is the limit of the fractional part of cairo's 24.8 fixed point |
|
634 // representation divided by 2 to ensure that we get different cairo |
|
635 // fractions |
|
636 double dMax = std::max(0.0, r - 1.0/128); |
|
637 float dx = fx - cx; |
|
638 float dy = fy - cy; |
|
639 double d = sqrt((dx * dx) + (dy * dy)); |
|
640 if (d > dMax) { |
|
641 double angle = atan2(dy, dx); |
|
642 fx = (float)(dMax * cos(angle)) + cx; |
|
643 fy = (float)(dMax * sin(angle)) + cy; |
|
644 } |
|
645 } |
|
646 |
|
647 nsRefPtr<gfxPattern> pattern = new gfxPattern(fx, fy, 0, cx, cy, r); |
|
648 return pattern.forget(); |
|
649 } |
|
650 |
|
651 // ------------------------------------------------------------------------- |
|
652 // Public functions |
|
653 // ------------------------------------------------------------------------- |
|
654 |
|
655 nsIFrame* |
|
656 NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell, |
|
657 nsStyleContext* aContext) |
|
658 { |
|
659 return new (aPresShell) nsSVGLinearGradientFrame(aContext); |
|
660 } |
|
661 |
|
662 NS_IMPL_FRAMEARENA_HELPERS(nsSVGLinearGradientFrame) |
|
663 |
|
664 nsIFrame* |
|
665 NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell, |
|
666 nsStyleContext* aContext) |
|
667 { |
|
668 return new (aPresShell) nsSVGRadialGradientFrame(aContext); |
|
669 } |
|
670 |
|
671 NS_IMPL_FRAMEARENA_HELPERS(nsSVGRadialGradientFrame) |