michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Main header first: michael@0: #include "nsSVGGradientFrame.h" michael@0: #include michael@0: michael@0: // Keep others in (case-insensitive) order: michael@0: #include "gfxPattern.h" michael@0: #include "mozilla/dom/SVGGradientElement.h" michael@0: #include "mozilla/dom/SVGStopElement.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsSVGEffects.h" michael@0: #include "nsSVGAnimatedTransformList.h" michael@0: #include "gfxColor.h" michael@0: michael@0: // XXX Tight coupling with content classes ahead! michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Helper classes michael@0: michael@0: class MOZ_STACK_CLASS nsSVGGradientFrame::AutoGradientReferencer michael@0: { michael@0: public: michael@0: AutoGradientReferencer(nsSVGGradientFrame *aFrame michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : mFrame(aFrame) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: // Reference loops should normally be detected in advance and handled, so michael@0: // we're not expecting to encounter them here michael@0: NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!"); michael@0: mFrame->mLoopFlag = true; michael@0: } michael@0: ~AutoGradientReferencer() { michael@0: mFrame->mLoopFlag = false; michael@0: } michael@0: private: michael@0: nsSVGGradientFrame *mFrame; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Implementation michael@0: michael@0: nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext) : michael@0: nsSVGGradientFrameBase(aContext), michael@0: mLoopFlag(false), michael@0: mNoHRefURI(false) michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSVGGradientFrame) michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsIFrame methods: michael@0: michael@0: nsresult michael@0: nsSVGGradientFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (aNameSpaceID == kNameSpaceID_None && michael@0: (aAttribute == nsGkAtoms::gradientUnits || michael@0: aAttribute == nsGkAtoms::gradientTransform || michael@0: aAttribute == nsGkAtoms::spreadMethod)) { michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: } else if (aNameSpaceID == kNameSpaceID_XLink && michael@0: aAttribute == nsGkAtoms::href) { michael@0: // Blow away our reference, if any michael@0: Properties().Delete(nsSVGEffects::HrefProperty()); michael@0: mNoHRefURI = false; michael@0: // And update whoever references us michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: } michael@0: michael@0: return nsSVGGradientFrameBase::AttributeChanged(aNameSpaceID, michael@0: aAttribute, aModType); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: uint16_t michael@0: nsSVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault) michael@0: { michael@0: const nsSVGEnum& thisEnum = michael@0: static_cast(mContent)->mEnumAttributes[aIndex]; michael@0: michael@0: if (thisEnum.IsExplicitlySet()) michael@0: return thisEnum.GetAnimValue(); michael@0: michael@0: AutoGradientReferencer gradientRef(this); michael@0: michael@0: nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); michael@0: return next ? next->GetEnumValue(aIndex, aDefault) : michael@0: static_cast(aDefault)-> michael@0: mEnumAttributes[aIndex].GetAnimValue(); michael@0: } michael@0: michael@0: uint16_t michael@0: nsSVGGradientFrame::GetGradientUnits() michael@0: { michael@0: // This getter is called every time the others are called - maybe cache it? michael@0: return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS); michael@0: } michael@0: michael@0: uint16_t michael@0: nsSVGGradientFrame::GetSpreadMethod() michael@0: { michael@0: return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD); michael@0: } michael@0: michael@0: const nsSVGAnimatedTransformList* michael@0: nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault) michael@0: { michael@0: nsSVGAnimatedTransformList *thisTransformList = michael@0: static_cast(mContent)->GetAnimatedTransformList(); michael@0: michael@0: if (thisTransformList && thisTransformList->IsExplicitlySet()) michael@0: return thisTransformList; michael@0: michael@0: AutoGradientReferencer gradientRef(this); michael@0: michael@0: nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); michael@0: return next ? next->GetGradientTransformList(aDefault) : michael@0: static_cast(aDefault) michael@0: ->mGradientTransform.get(); michael@0: } michael@0: michael@0: gfxMatrix michael@0: nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource, michael@0: const gfxRect *aOverrideBounds) michael@0: { michael@0: gfxMatrix bboxMatrix; michael@0: michael@0: uint16_t gradientUnits = GetGradientUnits(); michael@0: if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) { michael@0: NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, michael@0: "Unknown gradientUnits type"); michael@0: // objectBoundingBox is the default anyway michael@0: michael@0: gfxRect bbox = michael@0: aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aSource); michael@0: bboxMatrix = michael@0: gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); michael@0: } michael@0: michael@0: const nsSVGAnimatedTransformList* animTransformList = michael@0: GetGradientTransformList(mContent); michael@0: if (!animTransformList) michael@0: return bboxMatrix; michael@0: michael@0: gfxMatrix gradientTransform = michael@0: animTransformList->GetAnimValue().GetConsolidationMatrix(); michael@0: return bboxMatrix.PreMultiply(gradientTransform); michael@0: } michael@0: michael@0: dom::SVGLinearGradientElement* michael@0: nsSVGGradientFrame::GetLinearGradientWithLength(uint32_t aIndex, michael@0: dom::SVGLinearGradientElement* aDefault) michael@0: { michael@0: // If this was a linear gradient with the required length, we would have michael@0: // already found it in nsSVGLinearGradientFrame::GetLinearGradientWithLength. michael@0: // Since we didn't find the length, continue looking down the chain. michael@0: michael@0: AutoGradientReferencer gradientRef(this); michael@0: michael@0: nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); michael@0: return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault; michael@0: } michael@0: michael@0: dom::SVGRadialGradientElement* michael@0: nsSVGGradientFrame::GetRadialGradientWithLength(uint32_t aIndex, michael@0: dom::SVGRadialGradientElement* aDefault) michael@0: { michael@0: // If this was a radial gradient with the required length, we would have michael@0: // already found it in nsSVGRadialGradientFrame::GetRadialGradientWithLength. michael@0: // Since we didn't find the length, continue looking down the chain. michael@0: michael@0: AutoGradientReferencer gradientRef(this); michael@0: michael@0: nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); michael@0: return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsSVGPaintServerFrame methods: michael@0: michael@0: //helper michael@0: static void GetStopInformation(nsIFrame* aStopFrame, michael@0: float *aOffset, michael@0: nscolor *aStopColor, michael@0: float *aStopOpacity) michael@0: { michael@0: nsIContent* stopContent = aStopFrame->GetContent(); michael@0: MOZ_ASSERT(stopContent && stopContent->IsSVG(nsGkAtoms::stop)); michael@0: michael@0: static_cast(stopContent)-> michael@0: GetAnimatedNumberValues(aOffset, nullptr); michael@0: michael@0: *aOffset = mozilla::clamped(*aOffset, 0.0f, 1.0f); michael@0: *aStopColor = aStopFrame->StyleSVGReset()->mStopColor; michael@0: *aStopOpacity = aStopFrame->StyleSVGReset()->mStopOpacity; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsSVGGradientFrame::GetPaintServerPattern(nsIFrame *aSource, michael@0: const gfxMatrix& aContextMatrix, michael@0: nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, michael@0: float aGraphicOpacity, michael@0: const gfxRect *aOverrideBounds) michael@0: { michael@0: uint16_t gradientUnits = GetGradientUnits(); michael@0: MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX || michael@0: gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE); michael@0: if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { michael@0: // Set mSource for this consumer. michael@0: // If this gradient is applied to text, our caller will be the glyph, which michael@0: // is not an element, so we need to get the parent michael@0: mSource = aSource->GetContent()->IsNodeOfType(nsINode::eTEXT) ? michael@0: aSource->GetParent() : aSource; michael@0: } michael@0: michael@0: nsAutoTArray stopFrames; michael@0: GetStopFrames(&stopFrames); michael@0: michael@0: uint32_t nStops = stopFrames.Length(); michael@0: michael@0: // SVG specification says that no stops should be treated like michael@0: // the corresponding fill or stroke had "none" specified. michael@0: if (nStops == 0) { michael@0: nsRefPtr pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0)); michael@0: return pattern.forget(); michael@0: } michael@0: michael@0: if (nStops == 1 || GradientVectorLengthIsZero()) { michael@0: // The gradient paints a single colour, using the stop-color of the last michael@0: // gradient step if there are more than one. michael@0: float stopOpacity = stopFrames[nStops-1]->StyleSVGReset()->mStopOpacity; michael@0: nscolor stopColor = stopFrames[nStops-1]->StyleSVGReset()->mStopColor; michael@0: michael@0: nsRefPtr pattern = new gfxPattern( michael@0: gfxRGBA(NS_GET_R(stopColor)/255.0, michael@0: NS_GET_G(stopColor)/255.0, michael@0: NS_GET_B(stopColor)/255.0, michael@0: NS_GET_A(stopColor)/255.0 * michael@0: stopOpacity * aGraphicOpacity)); michael@0: return pattern.forget(); michael@0: } michael@0: michael@0: // Get the transform list (if there is one). We do this after the returns michael@0: // above since this call can be expensive when "gradientUnits" is set to michael@0: // "objectBoundingBox" (since that requiring a GetBBox() call). michael@0: gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds); michael@0: michael@0: if (patternMatrix.IsSingular()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // revert the vector effect transform so that the gradient appears unchanged michael@0: if (aFillOrStroke == &nsStyleSVG::mStroke) { michael@0: patternMatrix.Multiply(nsSVGUtils::GetStrokeTransform(aSource).Invert()); michael@0: } michael@0: michael@0: patternMatrix.Invert(); michael@0: michael@0: nsRefPtr gradient = CreateGradient(); michael@0: if (!gradient || gradient->CairoStatus()) michael@0: return nullptr; michael@0: michael@0: uint16_t aSpread = GetSpreadMethod(); michael@0: if (aSpread == SVG_SPREADMETHOD_PAD) michael@0: gradient->SetExtend(gfxPattern::EXTEND_PAD); michael@0: else if (aSpread == SVG_SPREADMETHOD_REFLECT) michael@0: gradient->SetExtend(gfxPattern::EXTEND_REFLECT); michael@0: else if (aSpread == SVG_SPREADMETHOD_REPEAT) michael@0: gradient->SetExtend(gfxPattern::EXTEND_REPEAT); michael@0: michael@0: gradient->SetMatrix(patternMatrix); michael@0: michael@0: // setup stops michael@0: float lastOffset = 0.0f; michael@0: michael@0: for (uint32_t i = 0; i < nStops; i++) { michael@0: float offset, stopOpacity; michael@0: nscolor stopColor; michael@0: michael@0: GetStopInformation(stopFrames[i], &offset, &stopColor, &stopOpacity); michael@0: michael@0: if (offset < lastOffset) michael@0: offset = lastOffset; michael@0: else michael@0: lastOffset = offset; michael@0: michael@0: gradient->AddColorStop(offset, michael@0: gfxRGBA(NS_GET_R(stopColor)/255.0, michael@0: NS_GET_G(stopColor)/255.0, michael@0: NS_GET_B(stopColor)/255.0, michael@0: NS_GET_A(stopColor)/255.0 * michael@0: stopOpacity * aGraphicOpacity)); michael@0: } michael@0: michael@0: return gradient.forget(); michael@0: } michael@0: michael@0: // Private (helper) methods michael@0: michael@0: nsSVGGradientFrame * michael@0: nsSVGGradientFrame::GetReferencedGradient() michael@0: { michael@0: if (mNoHRefURI) michael@0: return nullptr; michael@0: michael@0: nsSVGPaintingProperty *property = static_cast michael@0: (Properties().Get(nsSVGEffects::HrefProperty())); michael@0: michael@0: if (!property) { michael@0: // Fetch our gradient element's xlink:href attribute michael@0: dom::SVGGradientElement*grad = static_cast(mContent); michael@0: nsAutoString href; michael@0: grad->mStringAttributes[dom::SVGGradientElement::HREF].GetAnimValue(href, grad); michael@0: if (href.IsEmpty()) { michael@0: mNoHRefURI = true; michael@0: return nullptr; // no URL michael@0: } michael@0: michael@0: // Convert href to an nsIURI michael@0: nsCOMPtr targetURI; michael@0: nsCOMPtr base = mContent->GetBaseURI(); michael@0: nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, michael@0: mContent->GetCurrentDoc(), base); michael@0: michael@0: property = michael@0: nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty()); michael@0: if (!property) michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIFrame *result = property->GetReferencedFrame(); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: nsIAtom* frameType = result->GetType(); michael@0: if (frameType != nsGkAtoms::svgLinearGradientFrame && michael@0: frameType != nsGkAtoms::svgRadialGradientFrame) michael@0: return nullptr; michael@0: michael@0: return static_cast(result); michael@0: } michael@0: michael@0: nsSVGGradientFrame * michael@0: nsSVGGradientFrame::GetReferencedGradientIfNotInUse() michael@0: { michael@0: nsSVGGradientFrame *referenced = GetReferencedGradient(); michael@0: if (!referenced) michael@0: return nullptr; michael@0: michael@0: if (referenced->mLoopFlag) { michael@0: // XXXjwatt: we should really send an error to the JavaScript Console here: michael@0: NS_WARNING("gradient reference loop detected while inheriting attribute!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return referenced; michael@0: } michael@0: michael@0: void michael@0: nsSVGGradientFrame::GetStopFrames(nsTArray* aStopFrames) michael@0: { michael@0: nsIFrame *stopFrame = nullptr; michael@0: for (stopFrame = mFrames.FirstChild(); stopFrame; michael@0: stopFrame = stopFrame->GetNextSibling()) { michael@0: if (stopFrame->GetType() == nsGkAtoms::svgStopFrame) { michael@0: aStopFrames->AppendElement(stopFrame); michael@0: } michael@0: } michael@0: if (aStopFrames->Length() > 0) { michael@0: return; michael@0: } michael@0: michael@0: // Our gradient element doesn't have stops - try to "inherit" them michael@0: michael@0: AutoGradientReferencer gradientRef(this); michael@0: nsSVGGradientFrame* next = GetReferencedGradientIfNotInUse(); michael@0: if (!next) { michael@0: return; michael@0: } michael@0: michael@0: return next->GetStopFrames(aStopFrames); michael@0: } michael@0: michael@0: // ------------------------------------------------------------------------- michael@0: // Linear Gradients michael@0: // ------------------------------------------------------------------------- michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: nsSVGLinearGradientFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: NS_ASSERTION(aContent->IsSVG(nsGkAtoms::linearGradient), michael@0: "Content is not an SVG linearGradient"); michael@0: michael@0: nsSVGLinearGradientFrameBase::Init(aContent, aParent, aPrevInFlow); michael@0: } michael@0: #endif /* DEBUG */ michael@0: michael@0: nsIAtom* michael@0: nsSVGLinearGradientFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::svgLinearGradientFrame; michael@0: } michael@0: michael@0: nsresult michael@0: nsSVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (aNameSpaceID == kNameSpaceID_None && michael@0: (aAttribute == nsGkAtoms::x1 || michael@0: aAttribute == nsGkAtoms::y1 || michael@0: aAttribute == nsGkAtoms::x2 || michael@0: aAttribute == nsGkAtoms::y2)) { michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: } michael@0: michael@0: return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, michael@0: aAttribute, aModType); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: float michael@0: nsSVGLinearGradientFrame::GetLengthValue(uint32_t aIndex) michael@0: { michael@0: dom::SVGLinearGradientElement* lengthElement = michael@0: GetLinearGradientWithLength(aIndex, michael@0: static_cast(mContent)); michael@0: // We passed in mContent as a fallback, so, assuming mContent is non-null, the michael@0: // return value should also be non-null. michael@0: NS_ABORT_IF_FALSE(lengthElement, michael@0: "Got unexpected null element from GetLinearGradientWithLength"); michael@0: const nsSVGLength2 &length = lengthElement->mLengthAttributes[aIndex]; michael@0: michael@0: // Object bounding box units are handled by setting the appropriate michael@0: // transform in GetGradientTransform, but we need to handle user michael@0: // space units as part of the individual Get* routines. Fixes 323669. michael@0: michael@0: uint16_t gradientUnits = GetGradientUnits(); michael@0: if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { michael@0: return nsSVGUtils::UserSpace(mSource, &length); michael@0: } michael@0: michael@0: NS_ASSERTION( michael@0: gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, michael@0: "Unknown gradientUnits type"); michael@0: michael@0: return length.GetAnimValue(static_cast(nullptr)); michael@0: } michael@0: michael@0: dom::SVGLinearGradientElement* michael@0: nsSVGLinearGradientFrame::GetLinearGradientWithLength(uint32_t aIndex, michael@0: dom::SVGLinearGradientElement* aDefault) michael@0: { michael@0: dom::SVGLinearGradientElement* thisElement = michael@0: static_cast(mContent); michael@0: const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; michael@0: michael@0: if (length.IsExplicitlySet()) { michael@0: return thisElement; michael@0: } michael@0: michael@0: return nsSVGLinearGradientFrameBase::GetLinearGradientWithLength(aIndex, michael@0: aDefault); michael@0: } michael@0: michael@0: bool michael@0: nsSVGLinearGradientFrame::GradientVectorLengthIsZero() michael@0: { michael@0: return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) == michael@0: GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) && michael@0: GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) == michael@0: GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsSVGLinearGradientFrame::CreateGradient() michael@0: { michael@0: float x1, y1, x2, y2; michael@0: michael@0: x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1); michael@0: y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1); michael@0: x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2); michael@0: y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); michael@0: michael@0: nsRefPtr pattern = new gfxPattern(x1, y1, x2, y2); michael@0: return pattern.forget(); michael@0: } michael@0: michael@0: // ------------------------------------------------------------------------- michael@0: // Radial Gradients michael@0: // ------------------------------------------------------------------------- michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: nsSVGRadialGradientFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: NS_ASSERTION(aContent->IsSVG(nsGkAtoms::radialGradient), michael@0: "Content is not an SVG radialGradient"); michael@0: michael@0: nsSVGRadialGradientFrameBase::Init(aContent, aParent, aPrevInFlow); michael@0: } michael@0: #endif /* DEBUG */ michael@0: michael@0: nsIAtom* michael@0: nsSVGRadialGradientFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::svgRadialGradientFrame; michael@0: } michael@0: michael@0: nsresult michael@0: nsSVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (aNameSpaceID == kNameSpaceID_None && michael@0: (aAttribute == nsGkAtoms::r || michael@0: aAttribute == nsGkAtoms::cx || michael@0: aAttribute == nsGkAtoms::cy || michael@0: aAttribute == nsGkAtoms::fx || michael@0: aAttribute == nsGkAtoms::fy)) { michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: } michael@0: michael@0: return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, michael@0: aAttribute, aModType); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: float michael@0: nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex) michael@0: { michael@0: dom::SVGRadialGradientElement* lengthElement = michael@0: GetRadialGradientWithLength(aIndex, michael@0: static_cast(mContent)); michael@0: // We passed in mContent as a fallback, so, assuming mContent is non-null, michael@0: // the return value should also be non-null. michael@0: NS_ABORT_IF_FALSE(lengthElement, michael@0: "Got unexpected null element from GetRadialGradientWithLength"); michael@0: return GetLengthValueFromElement(aIndex, *lengthElement); michael@0: } michael@0: michael@0: float michael@0: nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex, float aDefaultValue) michael@0: { michael@0: dom::SVGRadialGradientElement* lengthElement = michael@0: GetRadialGradientWithLength(aIndex, nullptr); michael@0: michael@0: return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement) michael@0: : aDefaultValue; michael@0: } michael@0: michael@0: float michael@0: nsSVGRadialGradientFrame::GetLengthValueFromElement(uint32_t aIndex, michael@0: dom::SVGRadialGradientElement& aElement) michael@0: { michael@0: const nsSVGLength2 &length = aElement.mLengthAttributes[aIndex]; michael@0: michael@0: // Object bounding box units are handled by setting the appropriate michael@0: // transform in GetGradientTransform, but we need to handle user michael@0: // space units as part of the individual Get* routines. Fixes 323669. michael@0: michael@0: uint16_t gradientUnits = GetGradientUnits(); michael@0: if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { michael@0: return nsSVGUtils::UserSpace(mSource, &length); michael@0: } michael@0: michael@0: NS_ASSERTION( michael@0: gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, michael@0: "Unknown gradientUnits type"); michael@0: michael@0: return length.GetAnimValue(static_cast(nullptr)); michael@0: } michael@0: michael@0: dom::SVGRadialGradientElement* michael@0: nsSVGRadialGradientFrame::GetRadialGradientWithLength(uint32_t aIndex, michael@0: dom::SVGRadialGradientElement* aDefault) michael@0: { michael@0: dom::SVGRadialGradientElement* thisElement = michael@0: static_cast(mContent); michael@0: const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; michael@0: michael@0: if (length.IsExplicitlySet()) { michael@0: return thisElement; michael@0: } michael@0: michael@0: return nsSVGRadialGradientFrameBase::GetRadialGradientWithLength(aIndex, michael@0: aDefault); michael@0: } michael@0: michael@0: bool michael@0: nsSVGRadialGradientFrame::GradientVectorLengthIsZero() michael@0: { michael@0: return GetLengthValue(dom::SVGRadialGradientElement::ATTR_R) == 0; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsSVGRadialGradientFrame::CreateGradient() michael@0: { michael@0: float cx, cy, r, fx, fy; michael@0: michael@0: cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX); michael@0: cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY); michael@0: r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R); michael@0: // If fx or fy are not set, use cx/cy instead michael@0: fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx); michael@0: fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy); michael@0: michael@0: if (fx != cx || fy != cy) { michael@0: // The focal point (fFx and fFy) must be clamped to be *inside* - not on - michael@0: // the circumference of the gradient or we'll get rendering anomalies. We michael@0: // calculate the distance from the focal point to the gradient center and michael@0: // make sure it is *less* than the gradient radius. michael@0: // 1/128 is the limit of the fractional part of cairo's 24.8 fixed point michael@0: // representation divided by 2 to ensure that we get different cairo michael@0: // fractions michael@0: double dMax = std::max(0.0, r - 1.0/128); michael@0: float dx = fx - cx; michael@0: float dy = fy - cy; michael@0: double d = sqrt((dx * dx) + (dy * dy)); michael@0: if (d > dMax) { michael@0: double angle = atan2(dy, dx); michael@0: fx = (float)(dMax * cos(angle)) + cx; michael@0: fy = (float)(dMax * sin(angle)) + cy; michael@0: } michael@0: } michael@0: michael@0: nsRefPtr pattern = new gfxPattern(fx, fy, 0, cx, cy, r); michael@0: return pattern.forget(); michael@0: } michael@0: michael@0: // ------------------------------------------------------------------------- michael@0: // Public functions michael@0: // ------------------------------------------------------------------------- michael@0: michael@0: nsIFrame* michael@0: NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell, michael@0: nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsSVGLinearGradientFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSVGLinearGradientFrame) michael@0: michael@0: nsIFrame* michael@0: NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell, michael@0: nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsSVGRadialGradientFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSVGRadialGradientFrame)