SVGGradientFrame.cpp (22646B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // Main header first: 8 #include "SVGGradientFrame.h" 9 10 #include <algorithm> 11 12 // Keep others in (case-insensitive) order: 13 #include "AutoReferenceChainGuard.h" 14 #include "SVGAnimatedTransformList.h" 15 #include "gfxPattern.h" 16 #include "gfxUtils.h" 17 #include "mozilla/PresShell.h" 18 #include "mozilla/SVGObserverUtils.h" 19 #include "mozilla/SVGUtils.h" 20 #include "mozilla/dom/SVGGradientElement.h" 21 #include "mozilla/dom/SVGGradientElementBinding.h" 22 #include "mozilla/dom/SVGStopElement.h" 23 #include "mozilla/dom/SVGUnitTypesBinding.h" 24 #include "nsContentUtils.h" 25 26 using namespace mozilla::dom; 27 using namespace mozilla::dom::SVGGradientElement_Binding; 28 using namespace mozilla::dom::SVGUnitTypes_Binding; 29 using namespace mozilla::gfx; 30 31 namespace mozilla { 32 33 //---------------------------------------------------------------------- 34 // Implementation 35 36 SVGGradientFrame::SVGGradientFrame(ComputedStyle* aStyle, 37 nsPresContext* aPresContext, ClassID aID) 38 : SVGPaintServerFrame(aStyle, aPresContext, aID), 39 mSource(nullptr), 40 mLoopFlag(false), 41 mNoHRefURI(false) {} 42 43 NS_QUERYFRAME_HEAD(SVGGradientFrame) 44 NS_QUERYFRAME_ENTRY(SVGGradientFrame) 45 NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame) 46 47 //---------------------------------------------------------------------- 48 // nsIFrame methods: 49 50 nsresult SVGGradientFrame::AttributeChanged(int32_t aNameSpaceID, 51 nsAtom* aAttribute, 52 AttrModType aModType) { 53 if (aNameSpaceID == kNameSpaceID_None && 54 (aAttribute == nsGkAtoms::gradientUnits || 55 aAttribute == nsGkAtoms::gradientTransform || 56 aAttribute == nsGkAtoms::spreadMethod)) { 57 SVGObserverUtils::InvalidateRenderingObservers(this); 58 } else if ((aNameSpaceID == kNameSpaceID_XLink || 59 aNameSpaceID == kNameSpaceID_None) && 60 aAttribute == nsGkAtoms::href) { 61 // Blow away our reference, if any 62 SVGObserverUtils::RemoveTemplateObserver(this); 63 mNoHRefURI = false; 64 // And update whoever references us 65 SVGObserverUtils::InvalidateRenderingObservers(this); 66 } 67 68 return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute, 69 aModType); 70 } 71 72 //---------------------------------------------------------------------- 73 74 uint16_t SVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) { 75 const SVGAnimatedEnumeration& thisEnum = 76 static_cast<dom::SVGGradientElement*>(GetContent()) 77 ->mEnumAttributes[aIndex]; 78 79 if (thisEnum.IsExplicitlySet()) { 80 return thisEnum.GetAnimValue(); 81 } 82 83 // Before we recurse, make sure we'll break reference loops and over long 84 // reference chains: 85 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 86 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 87 &sRefChainLengthCounter); 88 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 89 // Break reference chain 90 return static_cast<dom::SVGGradientElement*>(aDefault) 91 ->mEnumAttributes[aIndex] 92 .GetAnimValue(); 93 } 94 95 SVGGradientFrame* next = GetReferencedGradient(); 96 97 return next ? next->GetEnumValue(aIndex, aDefault) 98 : static_cast<dom::SVGGradientElement*>(aDefault) 99 ->mEnumAttributes[aIndex] 100 .GetAnimValue(); 101 } 102 103 uint16_t SVGGradientFrame::GetGradientUnits() { 104 // This getter is called every time the others are called - maybe cache it? 105 return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS); 106 } 107 108 uint16_t SVGGradientFrame::GetSpreadMethod() { 109 return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD); 110 } 111 112 SVGGradientFrame* SVGGradientFrame::GetGradientTransformFrame( 113 SVGGradientFrame* aDefault) { 114 if (!StyleDisplay()->mTransform.IsNone()) { 115 return this; 116 } 117 // Before we recurse, make sure we'll break reference loops and over long 118 // reference chains: 119 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 120 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 121 &sRefChainLengthCounter); 122 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 123 // Break reference chain 124 return aDefault; 125 } 126 127 if (SVGGradientFrame* next = GetReferencedGradient()) { 128 return next->GetGradientTransformFrame(aDefault); 129 } 130 return aDefault; 131 } 132 133 gfxMatrix SVGGradientFrame::GetGradientTransform( 134 nsIFrame* aSource, const gfxRect* aOverrideBounds) { 135 gfxMatrix bboxMatrix; 136 uint16_t gradientUnits = GetGradientUnits(); 137 if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) { 138 NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 139 "Unknown gradientUnits type"); 140 // objectBoundingBox is the default anyway 141 142 gfxRect bbox = aOverrideBounds 143 ? *aOverrideBounds 144 : SVGUtils::GetBBox( 145 aSource, SVGUtils::eUseFrameBoundsForOuterSVG | 146 SVGUtils::eBBoxIncludeFillGeometry); 147 bboxMatrix = 148 gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); 149 } 150 151 return bboxMatrix.PreMultiply( 152 SVGUtils::GetTransformMatrixInUserSpace(GetGradientTransformFrame(this))); 153 } 154 155 dom::SVGLinearGradientElement* SVGGradientFrame::GetLinearGradientWithLength( 156 uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) { 157 // If this was a linear gradient with the required length, we would have 158 // already found it in SVGLinearGradientFrame::GetLinearGradientWithLength. 159 // Since we didn't find the length, continue looking down the chain. 160 161 // Before we recurse, make sure we'll break reference loops and over long 162 // reference chains: 163 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 164 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 165 &sRefChainLengthCounter); 166 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 167 // Break reference chain 168 return aDefault; 169 } 170 171 SVGGradientFrame* next = GetReferencedGradient(); 172 return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault; 173 } 174 175 dom::SVGRadialGradientElement* SVGGradientFrame::GetRadialGradientWithLength( 176 uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) { 177 // If this was a radial gradient with the required length, we would have 178 // already found it in SVGRadialGradientFrame::GetRadialGradientWithLength. 179 // Since we didn't find the length, continue looking down the chain. 180 181 // Before we recurse, make sure we'll break reference loops and over long 182 // reference chains: 183 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 184 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 185 &sRefChainLengthCounter); 186 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 187 // Break reference chain 188 return aDefault; 189 } 190 191 SVGGradientFrame* next = GetReferencedGradient(); 192 return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault; 193 } 194 195 //---------------------------------------------------------------------- 196 // SVGPaintServerFrame methods: 197 198 // helpers 199 200 static ColorStop GetStopInformation(const nsIFrame* aStopFrame, 201 float aGraphicOpacity, 202 float& aLastPosition) { 203 nsIContent* stopContent = aStopFrame->GetContent(); 204 MOZ_ASSERT(stopContent && stopContent->IsSVGElement(nsGkAtoms::stop)); 205 206 float position; 207 static_cast<SVGStopElement*>(stopContent) 208 ->GetAnimatedNumberValues(&position, nullptr); 209 210 position = std::clamp(position, 0.0f, 1.0f); 211 212 if (position < aLastPosition) { 213 position = aLastPosition; 214 } else { 215 aLastPosition = position; 216 } 217 218 const auto* svgReset = aStopFrame->StyleSVGReset(); 219 220 sRGBColor stopColor = 221 sRGBColor::FromABGR(svgReset->mStopColor.CalcColor(aStopFrame)); 222 stopColor.a *= svgReset->mStopOpacity * aGraphicOpacity; 223 224 return ColorStop(position, false, 225 StyleAbsoluteColor::FromColor(stopColor.ToABGR())); 226 } 227 228 class MOZ_STACK_CLASS SVGColorStopInterpolator 229 : public ColorStopInterpolator<SVGColorStopInterpolator> { 230 public: 231 SVGColorStopInterpolator( 232 gfxPattern* aGradient, const nsTArray<ColorStop>& aStops, 233 const StyleColorInterpolationMethod& aStyleColorInterpolationMethod, 234 bool aExtend) 235 : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod, aExtend), 236 mGradient(aGradient) {} 237 238 void CreateStop(float aPosition, DeviceColor aColor) { 239 mGradient->AddColorStop(aPosition, aColor); 240 } 241 242 private: 243 gfxPattern* mGradient; 244 }; 245 246 already_AddRefed<gfxPattern> SVGGradientFrame::GetPaintServerPattern( 247 nsIFrame* aSource, const DrawTarget* aDrawTarget, 248 const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::* aFillOrStroke, 249 float aGraphicOpacity, imgDrawingParams& aImgParams, 250 const gfxRect* aOverrideBounds) { 251 uint16_t gradientUnits = GetGradientUnits(); 252 MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX || 253 gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE); 254 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { 255 // Set mSource for this consumer. 256 // If this gradient is applied to text, our caller will be the glyph, which 257 // is not an element, so we need to get the parent 258 mSource = aSource->IsTextFrame() ? aSource->GetParent() : aSource; 259 } 260 261 AutoTArray<ColorStop, 8> stops; 262 GetStops(&stops, aGraphicOpacity); 263 264 uint32_t nStops = stops.Length(); 265 266 // SVG specification says that no stops should be treated like 267 // the corresponding fill or stroke had "none" specified. 268 if (nStops == 0) { 269 return do_AddRef(new gfxPattern(DeviceColor())); 270 } 271 272 if (nStops == 1 || GradientVectorLengthIsZero()) { 273 // The gradient paints a single colour, using the stop-color of the last 274 // gradient step if there are more than one. 275 return do_AddRef(new gfxPattern(ToDeviceColor(stops.LastElement().mColor))); 276 } 277 278 // Get the transform list (if there is one). We do this after the returns 279 // above since this call can be expensive when "gradientUnits" is set to 280 // "objectBoundingBox" (since that requiring a GetBBox() call). 281 gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds); 282 if (patternMatrix.IsSingular()) { 283 return nullptr; 284 } 285 286 // revert any vector effect transform so that the gradient appears unchanged 287 if (aFillOrStroke == &nsStyleSVG::mStroke) { 288 gfxMatrix userToOuterSVG; 289 if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) { 290 patternMatrix *= userToOuterSVG; 291 } 292 } 293 294 if (!patternMatrix.Invert()) { 295 return nullptr; 296 } 297 298 RefPtr<gfxPattern> gradient = CreateGradient(); 299 if (!gradient) { 300 return nullptr; 301 } 302 303 uint16_t aSpread = GetSpreadMethod(); 304 if (aSpread == SVG_SPREADMETHOD_PAD) { 305 gradient->SetExtend(ExtendMode::CLAMP); 306 } else if (aSpread == SVG_SPREADMETHOD_REFLECT) { 307 gradient->SetExtend(ExtendMode::REFLECT); 308 } else if (aSpread == SVG_SPREADMETHOD_REPEAT) { 309 gradient->SetExtend(ExtendMode::REPEAT); 310 } 311 312 gradient->SetMatrix(patternMatrix); 313 314 if (StyleSVG()->mColorInterpolation == StyleColorInterpolation::Linearrgb) { 315 static constexpr auto interpolationMethod = StyleColorInterpolationMethod{ 316 StyleColorSpace::SrgbLinear, StyleHueInterpolationMethod::Shorter}; 317 SVGColorStopInterpolator interpolator(gradient, stops, interpolationMethod, 318 false); 319 interpolator.CreateStops(); 320 } else { 321 // setup standard sRGB stops 322 for (const auto& stop : stops) { 323 gradient->AddColorStop(stop.mPosition, ToDeviceColor(stop.mColor)); 324 } 325 } 326 327 return gradient.forget(); 328 } 329 330 // Private (helper) methods 331 332 float SVGGradientFrame::GetLengthValue(const SVGAnimatedLength& aLength) { 333 // Object bounding box units are handled by setting the appropriate 334 // transform in GetGradientTransform, but we need to handle user 335 // space units as part of the individual Get* routines. Fixes 323669. 336 337 uint16_t gradientUnits = GetGradientUnits(); 338 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { 339 return SVGUtils::UserSpace(mSource, &aLength); 340 } 341 342 NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 343 "Unknown gradientUnits type"); 344 345 if (aLength.IsPercentage()) { 346 // We want the percentage of the objectBoundingBox rather than 347 // the percentage of the nearest viewport. 348 return aLength.GetAnimValInSpecifiedUnits() / 100.0f; 349 } 350 return aLength.GetAnimValueWithZoom( 351 static_cast<dom::SVGElement*>(GetContent())); 352 } 353 354 SVGGradientFrame* SVGGradientFrame::GetReferencedGradient() { 355 if (mNoHRefURI) { 356 return nullptr; 357 } 358 359 auto GetHref = [this](nsAString& aHref) { 360 dom::SVGGradientElement* grad = 361 static_cast<dom::SVGGradientElement*>(this->GetContent()); 362 if (grad->mStringAttributes[dom::SVGGradientElement::HREF] 363 .IsExplicitlySet()) { 364 grad->mStringAttributes[dom::SVGGradientElement::HREF].GetAnimValue(aHref, 365 grad); 366 } else { 367 grad->mStringAttributes[dom::SVGGradientElement::XLINK_HREF].GetAnimValue( 368 aHref, grad); 369 } 370 this->mNoHRefURI = aHref.IsEmpty(); 371 }; 372 373 // We don't call SVGObserverUtils::RemoveTemplateObserver and set 374 // `mNoHRefURI = false` on failure since we want to be invalidated if the ID 375 // specified by our href starts resolving to a different/valid element. 376 377 return do_QueryFrame(SVGObserverUtils::GetAndObserveTemplate(this, GetHref)); 378 } 379 380 void SVGGradientFrame::GetStops(nsTArray<ColorStop>* aStops, 381 float aGraphicOpacity) { 382 float lastPosition = 0.0f; 383 for (const auto* stopFrame : mFrames) { 384 if (stopFrame->IsSVGStopFrame()) { 385 aStops->AppendElement( 386 GetStopInformation(stopFrame, aGraphicOpacity, lastPosition)); 387 } 388 } 389 if (!aStops->IsEmpty()) { 390 return; 391 } 392 393 // Our gradient element doesn't have stops - try to "inherit" them 394 395 // Before we recurse, make sure we'll break reference loops and over long 396 // reference chains: 397 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 398 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, 399 &sRefChainLengthCounter); 400 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 401 // Break reference chain 402 return; 403 } 404 405 SVGGradientFrame* next = GetReferencedGradient(); 406 if (next) { 407 next->GetStops(aStops, aGraphicOpacity); 408 } 409 } 410 411 // ------------------------------------------------------------------------- 412 // Linear Gradients 413 // ------------------------------------------------------------------------- 414 415 NS_QUERYFRAME_HEAD(SVGLinearGradientFrame) 416 NS_QUERYFRAME_ENTRY(SVGLinearGradientFrame) 417 NS_QUERYFRAME_TAIL_INHERITING(SVGGradientFrame) 418 419 #ifdef DEBUG 420 void SVGLinearGradientFrame::Init(nsIContent* aContent, 421 nsContainerFrame* aParent, 422 nsIFrame* aPrevInFlow) { 423 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::linearGradient), 424 "Content is not an SVG linearGradient"); 425 426 SVGGradientFrame::Init(aContent, aParent, aPrevInFlow); 427 } 428 #endif /* DEBUG */ 429 430 nsresult SVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID, 431 nsAtom* aAttribute, 432 AttrModType aModType) { 433 if (aNameSpaceID == kNameSpaceID_None && 434 (aAttribute == nsGkAtoms::x1 || aAttribute == nsGkAtoms::y1 || 435 aAttribute == nsGkAtoms::x2 || aAttribute == nsGkAtoms::y2)) { 436 SVGObserverUtils::InvalidateRenderingObservers(this); 437 } 438 439 return SVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); 440 } 441 442 //---------------------------------------------------------------------- 443 444 float SVGLinearGradientFrame::GetLengthValue(uint32_t aIndex) { 445 dom::SVGLinearGradientElement* lengthElement = GetLinearGradientWithLength( 446 aIndex, static_cast<dom::SVGLinearGradientElement*>(GetContent())); 447 // We passed in mContent as a fallback, so, assuming mContent is non-null, the 448 // return value should also be non-null. 449 MOZ_ASSERT(lengthElement, 450 "Got unexpected null element from GetLinearGradientWithLength"); 451 452 return GetLengthValue(lengthElement->mLengthAttributes[aIndex]); 453 } 454 455 dom::SVGLinearGradientElement* 456 SVGLinearGradientFrame::GetLinearGradientWithLength( 457 uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) { 458 dom::SVGLinearGradientElement* thisElement = 459 static_cast<dom::SVGLinearGradientElement*>(GetContent()); 460 const SVGAnimatedLength& length = thisElement->mLengthAttributes[aIndex]; 461 462 if (length.IsExplicitlySet()) { 463 return thisElement; 464 } 465 466 return SVGGradientFrame::GetLinearGradientWithLength(aIndex, aDefault); 467 } 468 469 bool SVGLinearGradientFrame::GradientVectorLengthIsZero() { 470 return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) == 471 GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) && 472 GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) == 473 GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); 474 } 475 476 already_AddRefed<gfxPattern> SVGLinearGradientFrame::CreateGradient() { 477 float x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1); 478 float y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1); 479 float x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2); 480 float y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); 481 482 return do_AddRef(new gfxPattern(x1, y1, x2, y2)); 483 } 484 485 // ------------------------------------------------------------------------- 486 // Radial Gradients 487 // ------------------------------------------------------------------------- 488 489 NS_QUERYFRAME_HEAD(SVGRadialGradientFrame) 490 NS_QUERYFRAME_ENTRY(SVGRadialGradientFrame) 491 NS_QUERYFRAME_TAIL_INHERITING(SVGGradientFrame) 492 493 #ifdef DEBUG 494 void SVGRadialGradientFrame::Init(nsIContent* aContent, 495 nsContainerFrame* aParent, 496 nsIFrame* aPrevInFlow) { 497 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::radialGradient), 498 "Content is not an SVG radialGradient"); 499 500 SVGGradientFrame::Init(aContent, aParent, aPrevInFlow); 501 } 502 #endif /* DEBUG */ 503 504 nsresult SVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID, 505 nsAtom* aAttribute, 506 AttrModType aModType) { 507 if (aNameSpaceID == kNameSpaceID_None && 508 (aAttribute == nsGkAtoms::r || aAttribute == nsGkAtoms::cx || 509 aAttribute == nsGkAtoms::cy || aAttribute == nsGkAtoms::fx || 510 aAttribute == nsGkAtoms::fy)) { 511 SVGObserverUtils::InvalidateRenderingObservers(this); 512 } 513 514 return SVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); 515 } 516 517 //---------------------------------------------------------------------- 518 519 float SVGRadialGradientFrame::GetLengthValue(uint32_t aIndex, 520 Maybe<float> aDefaultValue) { 521 dom::SVGRadialGradientElement* lengthElement = GetRadialGradientWithLength( 522 aIndex, aDefaultValue.isNothing() 523 ? static_cast<dom::SVGRadialGradientElement*>(GetContent()) 524 : nullptr); 525 526 // If we passed our content as a fallback, then assuming that is non-null, 527 // the return value should also be non-null. 528 MOZ_ASSERT(aDefaultValue.isSome() || lengthElement, 529 "Got unexpected null element from GetRadialGradientWithLength"); 530 531 return lengthElement 532 ? GetLengthValue(lengthElement->mLengthAttributes[aIndex]) 533 : aDefaultValue.value(); 534 } 535 536 dom::SVGRadialGradientElement* 537 SVGRadialGradientFrame::GetRadialGradientWithLength( 538 uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) { 539 dom::SVGRadialGradientElement* thisElement = 540 static_cast<dom::SVGRadialGradientElement*>(GetContent()); 541 const SVGAnimatedLength& length = thisElement->mLengthAttributes[aIndex]; 542 543 if (length.IsExplicitlySet()) { 544 return thisElement; 545 } 546 547 return SVGGradientFrame::GetRadialGradientWithLength(aIndex, aDefault); 548 } 549 550 bool SVGRadialGradientFrame::GradientVectorLengthIsZero() { 551 float cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX); 552 float cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY); 553 float r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R); 554 // If fx or fy are not set, use cx/cy instead 555 float fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx); 556 float fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy); 557 float fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR); 558 return cx == fx && cy == fy && r == fr; 559 } 560 561 already_AddRefed<gfxPattern> SVGRadialGradientFrame::CreateGradient() { 562 float cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX); 563 float cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY); 564 float r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R); 565 // If fx or fy are not set, use cx/cy instead 566 float fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx); 567 float fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy); 568 float fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR); 569 570 return do_AddRef(new gfxPattern(fx, fy, fr, cx, cy, r)); 571 } 572 573 } // namespace mozilla 574 575 // ------------------------------------------------------------------------- 576 // Public functions 577 // ------------------------------------------------------------------------- 578 579 nsIFrame* NS_NewSVGLinearGradientFrame(mozilla::PresShell* aPresShell, 580 mozilla::ComputedStyle* aStyle) { 581 return new (aPresShell) 582 mozilla::SVGLinearGradientFrame(aStyle, aPresShell->GetPresContext()); 583 } 584 585 nsIFrame* NS_NewSVGRadialGradientFrame(mozilla::PresShell* aPresShell, 586 mozilla::ComputedStyle* aStyle) { 587 return new (aPresShell) 588 mozilla::SVGRadialGradientFrame(aStyle, aPresShell->GetPresContext()); 589 } 590 591 namespace mozilla { 592 593 NS_IMPL_FRAMEARENA_HELPERS(SVGLinearGradientFrame) 594 NS_IMPL_FRAMEARENA_HELPERS(SVGRadialGradientFrame) 595 596 } // namespace mozilla