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