1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsSVGGradientFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,671 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// Main header first: 1.10 +#include "nsSVGGradientFrame.h" 1.11 +#include <algorithm> 1.12 + 1.13 +// Keep others in (case-insensitive) order: 1.14 +#include "gfxPattern.h" 1.15 +#include "mozilla/dom/SVGGradientElement.h" 1.16 +#include "mozilla/dom/SVGStopElement.h" 1.17 +#include "nsContentUtils.h" 1.18 +#include "nsSVGEffects.h" 1.19 +#include "nsSVGAnimatedTransformList.h" 1.20 +#include "gfxColor.h" 1.21 + 1.22 +// XXX Tight coupling with content classes ahead! 1.23 + 1.24 +using namespace mozilla; 1.25 +using namespace mozilla::dom; 1.26 + 1.27 +//---------------------------------------------------------------------- 1.28 +// Helper classes 1.29 + 1.30 +class MOZ_STACK_CLASS nsSVGGradientFrame::AutoGradientReferencer 1.31 +{ 1.32 +public: 1.33 + AutoGradientReferencer(nsSVGGradientFrame *aFrame 1.34 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.35 + : mFrame(aFrame) 1.36 + { 1.37 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.38 + // Reference loops should normally be detected in advance and handled, so 1.39 + // we're not expecting to encounter them here 1.40 + NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!"); 1.41 + mFrame->mLoopFlag = true; 1.42 + } 1.43 + ~AutoGradientReferencer() { 1.44 + mFrame->mLoopFlag = false; 1.45 + } 1.46 +private: 1.47 + nsSVGGradientFrame *mFrame; 1.48 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.49 +}; 1.50 + 1.51 +//---------------------------------------------------------------------- 1.52 +// Implementation 1.53 + 1.54 +nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext) : 1.55 + nsSVGGradientFrameBase(aContext), 1.56 + mLoopFlag(false), 1.57 + mNoHRefURI(false) 1.58 +{ 1.59 +} 1.60 + 1.61 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGGradientFrame) 1.62 + 1.63 +//---------------------------------------------------------------------- 1.64 +// nsIFrame methods: 1.65 + 1.66 +nsresult 1.67 +nsSVGGradientFrame::AttributeChanged(int32_t aNameSpaceID, 1.68 + nsIAtom* aAttribute, 1.69 + int32_t aModType) 1.70 +{ 1.71 + if (aNameSpaceID == kNameSpaceID_None && 1.72 + (aAttribute == nsGkAtoms::gradientUnits || 1.73 + aAttribute == nsGkAtoms::gradientTransform || 1.74 + aAttribute == nsGkAtoms::spreadMethod)) { 1.75 + nsSVGEffects::InvalidateDirectRenderingObservers(this); 1.76 + } else if (aNameSpaceID == kNameSpaceID_XLink && 1.77 + aAttribute == nsGkAtoms::href) { 1.78 + // Blow away our reference, if any 1.79 + Properties().Delete(nsSVGEffects::HrefProperty()); 1.80 + mNoHRefURI = false; 1.81 + // And update whoever references us 1.82 + nsSVGEffects::InvalidateDirectRenderingObservers(this); 1.83 + } 1.84 + 1.85 + return nsSVGGradientFrameBase::AttributeChanged(aNameSpaceID, 1.86 + aAttribute, aModType); 1.87 +} 1.88 + 1.89 +//---------------------------------------------------------------------- 1.90 + 1.91 +uint16_t 1.92 +nsSVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault) 1.93 +{ 1.94 + const nsSVGEnum& thisEnum = 1.95 + static_cast<dom::SVGGradientElement*>(mContent)->mEnumAttributes[aIndex]; 1.96 + 1.97 + if (thisEnum.IsExplicitlySet()) 1.98 + return thisEnum.GetAnimValue(); 1.99 + 1.100 + AutoGradientReferencer gradientRef(this); 1.101 + 1.102 + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); 1.103 + return next ? next->GetEnumValue(aIndex, aDefault) : 1.104 + static_cast<dom::SVGGradientElement*>(aDefault)-> 1.105 + mEnumAttributes[aIndex].GetAnimValue(); 1.106 +} 1.107 + 1.108 +uint16_t 1.109 +nsSVGGradientFrame::GetGradientUnits() 1.110 +{ 1.111 + // This getter is called every time the others are called - maybe cache it? 1.112 + return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS); 1.113 +} 1.114 + 1.115 +uint16_t 1.116 +nsSVGGradientFrame::GetSpreadMethod() 1.117 +{ 1.118 + return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD); 1.119 +} 1.120 + 1.121 +const nsSVGAnimatedTransformList* 1.122 +nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault) 1.123 +{ 1.124 + nsSVGAnimatedTransformList *thisTransformList = 1.125 + static_cast<dom::SVGGradientElement*>(mContent)->GetAnimatedTransformList(); 1.126 + 1.127 + if (thisTransformList && thisTransformList->IsExplicitlySet()) 1.128 + return thisTransformList; 1.129 + 1.130 + AutoGradientReferencer gradientRef(this); 1.131 + 1.132 + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); 1.133 + return next ? next->GetGradientTransformList(aDefault) : 1.134 + static_cast<const dom::SVGGradientElement*>(aDefault) 1.135 + ->mGradientTransform.get(); 1.136 +} 1.137 + 1.138 +gfxMatrix 1.139 +nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource, 1.140 + const gfxRect *aOverrideBounds) 1.141 +{ 1.142 + gfxMatrix bboxMatrix; 1.143 + 1.144 + uint16_t gradientUnits = GetGradientUnits(); 1.145 + if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) { 1.146 + NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 1.147 + "Unknown gradientUnits type"); 1.148 + // objectBoundingBox is the default anyway 1.149 + 1.150 + gfxRect bbox = 1.151 + aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aSource); 1.152 + bboxMatrix = 1.153 + gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); 1.154 + } 1.155 + 1.156 + const nsSVGAnimatedTransformList* animTransformList = 1.157 + GetGradientTransformList(mContent); 1.158 + if (!animTransformList) 1.159 + return bboxMatrix; 1.160 + 1.161 + gfxMatrix gradientTransform = 1.162 + animTransformList->GetAnimValue().GetConsolidationMatrix(); 1.163 + return bboxMatrix.PreMultiply(gradientTransform); 1.164 +} 1.165 + 1.166 +dom::SVGLinearGradientElement* 1.167 +nsSVGGradientFrame::GetLinearGradientWithLength(uint32_t aIndex, 1.168 + dom::SVGLinearGradientElement* aDefault) 1.169 +{ 1.170 + // If this was a linear gradient with the required length, we would have 1.171 + // already found it in nsSVGLinearGradientFrame::GetLinearGradientWithLength. 1.172 + // Since we didn't find the length, continue looking down the chain. 1.173 + 1.174 + AutoGradientReferencer gradientRef(this); 1.175 + 1.176 + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); 1.177 + return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault; 1.178 +} 1.179 + 1.180 +dom::SVGRadialGradientElement* 1.181 +nsSVGGradientFrame::GetRadialGradientWithLength(uint32_t aIndex, 1.182 + dom::SVGRadialGradientElement* aDefault) 1.183 +{ 1.184 + // If this was a radial gradient with the required length, we would have 1.185 + // already found it in nsSVGRadialGradientFrame::GetRadialGradientWithLength. 1.186 + // Since we didn't find the length, continue looking down the chain. 1.187 + 1.188 + AutoGradientReferencer gradientRef(this); 1.189 + 1.190 + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); 1.191 + return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault; 1.192 +} 1.193 + 1.194 +//---------------------------------------------------------------------- 1.195 +// nsSVGPaintServerFrame methods: 1.196 + 1.197 +//helper 1.198 +static void GetStopInformation(nsIFrame* aStopFrame, 1.199 + float *aOffset, 1.200 + nscolor *aStopColor, 1.201 + float *aStopOpacity) 1.202 +{ 1.203 + nsIContent* stopContent = aStopFrame->GetContent(); 1.204 + MOZ_ASSERT(stopContent && stopContent->IsSVG(nsGkAtoms::stop)); 1.205 + 1.206 + static_cast<SVGStopElement*>(stopContent)-> 1.207 + GetAnimatedNumberValues(aOffset, nullptr); 1.208 + 1.209 + *aOffset = mozilla::clamped(*aOffset, 0.0f, 1.0f); 1.210 + *aStopColor = aStopFrame->StyleSVGReset()->mStopColor; 1.211 + *aStopOpacity = aStopFrame->StyleSVGReset()->mStopOpacity; 1.212 +} 1.213 + 1.214 +already_AddRefed<gfxPattern> 1.215 +nsSVGGradientFrame::GetPaintServerPattern(nsIFrame *aSource, 1.216 + const gfxMatrix& aContextMatrix, 1.217 + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, 1.218 + float aGraphicOpacity, 1.219 + const gfxRect *aOverrideBounds) 1.220 +{ 1.221 + uint16_t gradientUnits = GetGradientUnits(); 1.222 + MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX || 1.223 + gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE); 1.224 + if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { 1.225 + // Set mSource for this consumer. 1.226 + // If this gradient is applied to text, our caller will be the glyph, which 1.227 + // is not an element, so we need to get the parent 1.228 + mSource = aSource->GetContent()->IsNodeOfType(nsINode::eTEXT) ? 1.229 + aSource->GetParent() : aSource; 1.230 + } 1.231 + 1.232 + nsAutoTArray<nsIFrame*,8> stopFrames; 1.233 + GetStopFrames(&stopFrames); 1.234 + 1.235 + uint32_t nStops = stopFrames.Length(); 1.236 + 1.237 + // SVG specification says that no stops should be treated like 1.238 + // the corresponding fill or stroke had "none" specified. 1.239 + if (nStops == 0) { 1.240 + nsRefPtr<gfxPattern> pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0)); 1.241 + return pattern.forget(); 1.242 + } 1.243 + 1.244 + if (nStops == 1 || GradientVectorLengthIsZero()) { 1.245 + // The gradient paints a single colour, using the stop-color of the last 1.246 + // gradient step if there are more than one. 1.247 + float stopOpacity = stopFrames[nStops-1]->StyleSVGReset()->mStopOpacity; 1.248 + nscolor stopColor = stopFrames[nStops-1]->StyleSVGReset()->mStopColor; 1.249 + 1.250 + nsRefPtr<gfxPattern> pattern = new gfxPattern( 1.251 + gfxRGBA(NS_GET_R(stopColor)/255.0, 1.252 + NS_GET_G(stopColor)/255.0, 1.253 + NS_GET_B(stopColor)/255.0, 1.254 + NS_GET_A(stopColor)/255.0 * 1.255 + stopOpacity * aGraphicOpacity)); 1.256 + return pattern.forget(); 1.257 + } 1.258 + 1.259 + // Get the transform list (if there is one). We do this after the returns 1.260 + // above since this call can be expensive when "gradientUnits" is set to 1.261 + // "objectBoundingBox" (since that requiring a GetBBox() call). 1.262 + gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds); 1.263 + 1.264 + if (patternMatrix.IsSingular()) { 1.265 + return nullptr; 1.266 + } 1.267 + 1.268 + // revert the vector effect transform so that the gradient appears unchanged 1.269 + if (aFillOrStroke == &nsStyleSVG::mStroke) { 1.270 + patternMatrix.Multiply(nsSVGUtils::GetStrokeTransform(aSource).Invert()); 1.271 + } 1.272 + 1.273 + patternMatrix.Invert(); 1.274 + 1.275 + nsRefPtr<gfxPattern> gradient = CreateGradient(); 1.276 + if (!gradient || gradient->CairoStatus()) 1.277 + return nullptr; 1.278 + 1.279 + uint16_t aSpread = GetSpreadMethod(); 1.280 + if (aSpread == SVG_SPREADMETHOD_PAD) 1.281 + gradient->SetExtend(gfxPattern::EXTEND_PAD); 1.282 + else if (aSpread == SVG_SPREADMETHOD_REFLECT) 1.283 + gradient->SetExtend(gfxPattern::EXTEND_REFLECT); 1.284 + else if (aSpread == SVG_SPREADMETHOD_REPEAT) 1.285 + gradient->SetExtend(gfxPattern::EXTEND_REPEAT); 1.286 + 1.287 + gradient->SetMatrix(patternMatrix); 1.288 + 1.289 + // setup stops 1.290 + float lastOffset = 0.0f; 1.291 + 1.292 + for (uint32_t i = 0; i < nStops; i++) { 1.293 + float offset, stopOpacity; 1.294 + nscolor stopColor; 1.295 + 1.296 + GetStopInformation(stopFrames[i], &offset, &stopColor, &stopOpacity); 1.297 + 1.298 + if (offset < lastOffset) 1.299 + offset = lastOffset; 1.300 + else 1.301 + lastOffset = offset; 1.302 + 1.303 + gradient->AddColorStop(offset, 1.304 + gfxRGBA(NS_GET_R(stopColor)/255.0, 1.305 + NS_GET_G(stopColor)/255.0, 1.306 + NS_GET_B(stopColor)/255.0, 1.307 + NS_GET_A(stopColor)/255.0 * 1.308 + stopOpacity * aGraphicOpacity)); 1.309 + } 1.310 + 1.311 + return gradient.forget(); 1.312 +} 1.313 + 1.314 +// Private (helper) methods 1.315 + 1.316 +nsSVGGradientFrame * 1.317 +nsSVGGradientFrame::GetReferencedGradient() 1.318 +{ 1.319 + if (mNoHRefURI) 1.320 + return nullptr; 1.321 + 1.322 + nsSVGPaintingProperty *property = static_cast<nsSVGPaintingProperty*> 1.323 + (Properties().Get(nsSVGEffects::HrefProperty())); 1.324 + 1.325 + if (!property) { 1.326 + // Fetch our gradient element's xlink:href attribute 1.327 + dom::SVGGradientElement*grad = static_cast<dom::SVGGradientElement*>(mContent); 1.328 + nsAutoString href; 1.329 + grad->mStringAttributes[dom::SVGGradientElement::HREF].GetAnimValue(href, grad); 1.330 + if (href.IsEmpty()) { 1.331 + mNoHRefURI = true; 1.332 + return nullptr; // no URL 1.333 + } 1.334 + 1.335 + // Convert href to an nsIURI 1.336 + nsCOMPtr<nsIURI> targetURI; 1.337 + nsCOMPtr<nsIURI> base = mContent->GetBaseURI(); 1.338 + nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, 1.339 + mContent->GetCurrentDoc(), base); 1.340 + 1.341 + property = 1.342 + nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty()); 1.343 + if (!property) 1.344 + return nullptr; 1.345 + } 1.346 + 1.347 + nsIFrame *result = property->GetReferencedFrame(); 1.348 + if (!result) 1.349 + return nullptr; 1.350 + 1.351 + nsIAtom* frameType = result->GetType(); 1.352 + if (frameType != nsGkAtoms::svgLinearGradientFrame && 1.353 + frameType != nsGkAtoms::svgRadialGradientFrame) 1.354 + return nullptr; 1.355 + 1.356 + return static_cast<nsSVGGradientFrame*>(result); 1.357 +} 1.358 + 1.359 +nsSVGGradientFrame * 1.360 +nsSVGGradientFrame::GetReferencedGradientIfNotInUse() 1.361 +{ 1.362 + nsSVGGradientFrame *referenced = GetReferencedGradient(); 1.363 + if (!referenced) 1.364 + return nullptr; 1.365 + 1.366 + if (referenced->mLoopFlag) { 1.367 + // XXXjwatt: we should really send an error to the JavaScript Console here: 1.368 + NS_WARNING("gradient reference loop detected while inheriting attribute!"); 1.369 + return nullptr; 1.370 + } 1.371 + 1.372 + return referenced; 1.373 +} 1.374 + 1.375 +void 1.376 +nsSVGGradientFrame::GetStopFrames(nsTArray<nsIFrame*>* aStopFrames) 1.377 +{ 1.378 + nsIFrame *stopFrame = nullptr; 1.379 + for (stopFrame = mFrames.FirstChild(); stopFrame; 1.380 + stopFrame = stopFrame->GetNextSibling()) { 1.381 + if (stopFrame->GetType() == nsGkAtoms::svgStopFrame) { 1.382 + aStopFrames->AppendElement(stopFrame); 1.383 + } 1.384 + } 1.385 + if (aStopFrames->Length() > 0) { 1.386 + return; 1.387 + } 1.388 + 1.389 + // Our gradient element doesn't have stops - try to "inherit" them 1.390 + 1.391 + AutoGradientReferencer gradientRef(this); 1.392 + nsSVGGradientFrame* next = GetReferencedGradientIfNotInUse(); 1.393 + if (!next) { 1.394 + return; 1.395 + } 1.396 + 1.397 + return next->GetStopFrames(aStopFrames); 1.398 +} 1.399 + 1.400 +// ------------------------------------------------------------------------- 1.401 +// Linear Gradients 1.402 +// ------------------------------------------------------------------------- 1.403 + 1.404 +#ifdef DEBUG 1.405 +void 1.406 +nsSVGLinearGradientFrame::Init(nsIContent* aContent, 1.407 + nsIFrame* aParent, 1.408 + nsIFrame* aPrevInFlow) 1.409 +{ 1.410 + NS_ASSERTION(aContent->IsSVG(nsGkAtoms::linearGradient), 1.411 + "Content is not an SVG linearGradient"); 1.412 + 1.413 + nsSVGLinearGradientFrameBase::Init(aContent, aParent, aPrevInFlow); 1.414 +} 1.415 +#endif /* DEBUG */ 1.416 + 1.417 +nsIAtom* 1.418 +nsSVGLinearGradientFrame::GetType() const 1.419 +{ 1.420 + return nsGkAtoms::svgLinearGradientFrame; 1.421 +} 1.422 + 1.423 +nsresult 1.424 +nsSVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID, 1.425 + nsIAtom* aAttribute, 1.426 + int32_t aModType) 1.427 +{ 1.428 + if (aNameSpaceID == kNameSpaceID_None && 1.429 + (aAttribute == nsGkAtoms::x1 || 1.430 + aAttribute == nsGkAtoms::y1 || 1.431 + aAttribute == nsGkAtoms::x2 || 1.432 + aAttribute == nsGkAtoms::y2)) { 1.433 + nsSVGEffects::InvalidateDirectRenderingObservers(this); 1.434 + } 1.435 + 1.436 + return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, 1.437 + aAttribute, aModType); 1.438 +} 1.439 + 1.440 +//---------------------------------------------------------------------- 1.441 + 1.442 +float 1.443 +nsSVGLinearGradientFrame::GetLengthValue(uint32_t aIndex) 1.444 +{ 1.445 + dom::SVGLinearGradientElement* lengthElement = 1.446 + GetLinearGradientWithLength(aIndex, 1.447 + static_cast<dom::SVGLinearGradientElement*>(mContent)); 1.448 + // We passed in mContent as a fallback, so, assuming mContent is non-null, the 1.449 + // return value should also be non-null. 1.450 + NS_ABORT_IF_FALSE(lengthElement, 1.451 + "Got unexpected null element from GetLinearGradientWithLength"); 1.452 + const nsSVGLength2 &length = lengthElement->mLengthAttributes[aIndex]; 1.453 + 1.454 + // Object bounding box units are handled by setting the appropriate 1.455 + // transform in GetGradientTransform, but we need to handle user 1.456 + // space units as part of the individual Get* routines. Fixes 323669. 1.457 + 1.458 + uint16_t gradientUnits = GetGradientUnits(); 1.459 + if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { 1.460 + return nsSVGUtils::UserSpace(mSource, &length); 1.461 + } 1.462 + 1.463 + NS_ASSERTION( 1.464 + gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 1.465 + "Unknown gradientUnits type"); 1.466 + 1.467 + return length.GetAnimValue(static_cast<SVGSVGElement*>(nullptr)); 1.468 +} 1.469 + 1.470 +dom::SVGLinearGradientElement* 1.471 +nsSVGLinearGradientFrame::GetLinearGradientWithLength(uint32_t aIndex, 1.472 + dom::SVGLinearGradientElement* aDefault) 1.473 +{ 1.474 + dom::SVGLinearGradientElement* thisElement = 1.475 + static_cast<dom::SVGLinearGradientElement*>(mContent); 1.476 + const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; 1.477 + 1.478 + if (length.IsExplicitlySet()) { 1.479 + return thisElement; 1.480 + } 1.481 + 1.482 + return nsSVGLinearGradientFrameBase::GetLinearGradientWithLength(aIndex, 1.483 + aDefault); 1.484 +} 1.485 + 1.486 +bool 1.487 +nsSVGLinearGradientFrame::GradientVectorLengthIsZero() 1.488 +{ 1.489 + return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) == 1.490 + GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) && 1.491 + GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) == 1.492 + GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); 1.493 +} 1.494 + 1.495 +already_AddRefed<gfxPattern> 1.496 +nsSVGLinearGradientFrame::CreateGradient() 1.497 +{ 1.498 + float x1, y1, x2, y2; 1.499 + 1.500 + x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1); 1.501 + y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1); 1.502 + x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2); 1.503 + y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); 1.504 + 1.505 + nsRefPtr<gfxPattern> pattern = new gfxPattern(x1, y1, x2, y2); 1.506 + return pattern.forget(); 1.507 +} 1.508 + 1.509 +// ------------------------------------------------------------------------- 1.510 +// Radial Gradients 1.511 +// ------------------------------------------------------------------------- 1.512 + 1.513 +#ifdef DEBUG 1.514 +void 1.515 +nsSVGRadialGradientFrame::Init(nsIContent* aContent, 1.516 + nsIFrame* aParent, 1.517 + nsIFrame* aPrevInFlow) 1.518 +{ 1.519 + NS_ASSERTION(aContent->IsSVG(nsGkAtoms::radialGradient), 1.520 + "Content is not an SVG radialGradient"); 1.521 + 1.522 + nsSVGRadialGradientFrameBase::Init(aContent, aParent, aPrevInFlow); 1.523 +} 1.524 +#endif /* DEBUG */ 1.525 + 1.526 +nsIAtom* 1.527 +nsSVGRadialGradientFrame::GetType() const 1.528 +{ 1.529 + return nsGkAtoms::svgRadialGradientFrame; 1.530 +} 1.531 + 1.532 +nsresult 1.533 +nsSVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID, 1.534 + nsIAtom* aAttribute, 1.535 + int32_t aModType) 1.536 +{ 1.537 + if (aNameSpaceID == kNameSpaceID_None && 1.538 + (aAttribute == nsGkAtoms::r || 1.539 + aAttribute == nsGkAtoms::cx || 1.540 + aAttribute == nsGkAtoms::cy || 1.541 + aAttribute == nsGkAtoms::fx || 1.542 + aAttribute == nsGkAtoms::fy)) { 1.543 + nsSVGEffects::InvalidateDirectRenderingObservers(this); 1.544 + } 1.545 + 1.546 + return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, 1.547 + aAttribute, aModType); 1.548 +} 1.549 + 1.550 +//---------------------------------------------------------------------- 1.551 + 1.552 +float 1.553 +nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex) 1.554 +{ 1.555 + dom::SVGRadialGradientElement* lengthElement = 1.556 + GetRadialGradientWithLength(aIndex, 1.557 + static_cast<dom::SVGRadialGradientElement*>(mContent)); 1.558 + // We passed in mContent as a fallback, so, assuming mContent is non-null, 1.559 + // the return value should also be non-null. 1.560 + NS_ABORT_IF_FALSE(lengthElement, 1.561 + "Got unexpected null element from GetRadialGradientWithLength"); 1.562 + return GetLengthValueFromElement(aIndex, *lengthElement); 1.563 +} 1.564 + 1.565 +float 1.566 +nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex, float aDefaultValue) 1.567 +{ 1.568 + dom::SVGRadialGradientElement* lengthElement = 1.569 + GetRadialGradientWithLength(aIndex, nullptr); 1.570 + 1.571 + return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement) 1.572 + : aDefaultValue; 1.573 +} 1.574 + 1.575 +float 1.576 +nsSVGRadialGradientFrame::GetLengthValueFromElement(uint32_t aIndex, 1.577 + dom::SVGRadialGradientElement& aElement) 1.578 +{ 1.579 + const nsSVGLength2 &length = aElement.mLengthAttributes[aIndex]; 1.580 + 1.581 + // Object bounding box units are handled by setting the appropriate 1.582 + // transform in GetGradientTransform, but we need to handle user 1.583 + // space units as part of the individual Get* routines. Fixes 323669. 1.584 + 1.585 + uint16_t gradientUnits = GetGradientUnits(); 1.586 + if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { 1.587 + return nsSVGUtils::UserSpace(mSource, &length); 1.588 + } 1.589 + 1.590 + NS_ASSERTION( 1.591 + gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, 1.592 + "Unknown gradientUnits type"); 1.593 + 1.594 + return length.GetAnimValue(static_cast<SVGSVGElement*>(nullptr)); 1.595 +} 1.596 + 1.597 +dom::SVGRadialGradientElement* 1.598 +nsSVGRadialGradientFrame::GetRadialGradientWithLength(uint32_t aIndex, 1.599 + dom::SVGRadialGradientElement* aDefault) 1.600 +{ 1.601 + dom::SVGRadialGradientElement* thisElement = 1.602 + static_cast<dom::SVGRadialGradientElement*>(mContent); 1.603 + const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; 1.604 + 1.605 + if (length.IsExplicitlySet()) { 1.606 + return thisElement; 1.607 + } 1.608 + 1.609 + return nsSVGRadialGradientFrameBase::GetRadialGradientWithLength(aIndex, 1.610 + aDefault); 1.611 +} 1.612 + 1.613 +bool 1.614 +nsSVGRadialGradientFrame::GradientVectorLengthIsZero() 1.615 +{ 1.616 + return GetLengthValue(dom::SVGRadialGradientElement::ATTR_R) == 0; 1.617 +} 1.618 + 1.619 +already_AddRefed<gfxPattern> 1.620 +nsSVGRadialGradientFrame::CreateGradient() 1.621 +{ 1.622 + float cx, cy, r, fx, fy; 1.623 + 1.624 + cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX); 1.625 + cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY); 1.626 + r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R); 1.627 + // If fx or fy are not set, use cx/cy instead 1.628 + fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx); 1.629 + fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy); 1.630 + 1.631 + if (fx != cx || fy != cy) { 1.632 + // The focal point (fFx and fFy) must be clamped to be *inside* - not on - 1.633 + // the circumference of the gradient or we'll get rendering anomalies. We 1.634 + // calculate the distance from the focal point to the gradient center and 1.635 + // make sure it is *less* than the gradient radius. 1.636 + // 1/128 is the limit of the fractional part of cairo's 24.8 fixed point 1.637 + // representation divided by 2 to ensure that we get different cairo 1.638 + // fractions 1.639 + double dMax = std::max(0.0, r - 1.0/128); 1.640 + float dx = fx - cx; 1.641 + float dy = fy - cy; 1.642 + double d = sqrt((dx * dx) + (dy * dy)); 1.643 + if (d > dMax) { 1.644 + double angle = atan2(dy, dx); 1.645 + fx = (float)(dMax * cos(angle)) + cx; 1.646 + fy = (float)(dMax * sin(angle)) + cy; 1.647 + } 1.648 + } 1.649 + 1.650 + nsRefPtr<gfxPattern> pattern = new gfxPattern(fx, fy, 0, cx, cy, r); 1.651 + return pattern.forget(); 1.652 +} 1.653 + 1.654 +// ------------------------------------------------------------------------- 1.655 +// Public functions 1.656 +// ------------------------------------------------------------------------- 1.657 + 1.658 +nsIFrame* 1.659 +NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell, 1.660 + nsStyleContext* aContext) 1.661 +{ 1.662 + return new (aPresShell) nsSVGLinearGradientFrame(aContext); 1.663 +} 1.664 + 1.665 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGLinearGradientFrame) 1.666 + 1.667 +nsIFrame* 1.668 +NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell, 1.669 + nsStyleContext* aContext) 1.670 +{ 1.671 + return new (aPresShell) nsSVGRadialGradientFrame(aContext); 1.672 +} 1.673 + 1.674 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGRadialGradientFrame)