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