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