gecko-dev/layout/svg/nsSVGGradientFrame.cpp
Andrew Osmond 91b071ed14 Bug 1618345 - Enforce proper color management by splitting gfx::Color into sRGBColor and DeviceColor types. r=jrmuizel
gfx::Color is currently misused in many places. The DrawTargets expect
the color space to be in device space, e.g. what we are actually going
to draw using. Everything sitting above generally deals with sRGB, as
specified in CSS. Sometimes we missed the conversion from sRGB to device
space when issuing draw calls, and similarly sometimes we converted the
color to device space twice.

This patch splits the type in two. sRGBColor and DeviceColor now
represent sRGB and device color spaces respectively. DrawTarget only
accepts DeviceColor, and one can get a DeviceColor from an sRGBColor via
the ToDeviceColor helper API. The reftests now pass with color
management enabled for everything (e.g. CSS) instead of just tagged
raster images.

There will be a follow up patch to enable color management everywhere by
default on all supported platforms.

Differential Revision: https://phabricator.services.mozilla.com/D64771

--HG--
extra : moz-landing-system : lando
2020-03-09 14:16:17 +00:00

612 lines
23 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "AutoReferenceChainGuard.h"
#include "gfxPattern.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/SVGGradientElement.h"
#include "mozilla/dom/SVGGradientElementBinding.h"
#include "mozilla/dom/SVGStopElement.h"
#include "mozilla/dom/SVGUnitTypesBinding.h"
#include "nsContentUtils.h"
#include "SVGObserverUtils.h"
#include "SVGAnimatedTransformList.h"
// XXX Tight coupling with content classes ahead!
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::dom::SVGGradientElement_Binding;
using namespace mozilla::dom::SVGUnitTypes_Binding;
using namespace mozilla::gfx;
//----------------------------------------------------------------------
// Implementation
nsSVGGradientFrame::nsSVGGradientFrame(ComputedStyle* aStyle,
nsPresContext* aPresContext, ClassID aID)
: nsSVGPaintServerFrame(aStyle, aPresContext, aID),
mSource(nullptr),
mLoopFlag(false),
mNoHRefURI(false) {}
//----------------------------------------------------------------------
// nsIFrame methods:
nsresult nsSVGGradientFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType) {
if (aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::gradientUnits ||
aAttribute == nsGkAtoms::gradientTransform ||
aAttribute == nsGkAtoms::spreadMethod)) {
SVGObserverUtils::InvalidateDirectRenderingObservers(this);
} else if ((aNameSpaceID == kNameSpaceID_XLink ||
aNameSpaceID == kNameSpaceID_None) &&
aAttribute == nsGkAtoms::href) {
// Blow away our reference, if any
SVGObserverUtils::RemoveTemplateObserver(this);
mNoHRefURI = false;
// And update whoever references us
SVGObserverUtils::InvalidateDirectRenderingObservers(this);
}
return nsSVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
}
//----------------------------------------------------------------------
uint16_t nsSVGGradientFrame::GetEnumValue(uint32_t aIndex,
nsIContent* aDefault) {
const SVGAnimatedEnumeration& thisEnum =
static_cast<dom::SVGGradientElement*>(GetContent())
->mEnumAttributes[aIndex];
if (thisEnum.IsExplicitlySet()) {
return thisEnum.GetAnimValue();
}
// Before we recurse, make sure we'll break reference loops and over long
// reference chains:
static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
&sRefChainLengthCounter);
if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
// Break reference chain
return static_cast<dom::SVGGradientElement*>(aDefault)
->mEnumAttributes[aIndex]
.GetAnimValue();
}
nsSVGGradientFrame* next = GetReferencedGradient();
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 SVGAnimatedTransformList* nsSVGGradientFrame::GetGradientTransformList(
nsIContent* aDefault) {
SVGAnimatedTransformList* thisTransformList =
static_cast<dom::SVGGradientElement*>(GetContent())
->GetAnimatedTransformList();
if (thisTransformList && thisTransformList->IsExplicitlySet())
return thisTransformList;
// Before we recurse, make sure we'll break reference loops and over long
// reference chains:
static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
&sRefChainLengthCounter);
if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
// Break reference chain
return static_cast<const dom::SVGGradientElement*>(aDefault)
->mGradientTransform.get();
}
nsSVGGradientFrame* next = GetReferencedGradient();
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, nsSVGUtils::eUseFrameBoundsForOuterSVG |
nsSVGUtils::eBBoxIncludeFillGeometry);
bboxMatrix =
gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
}
const SVGAnimatedTransformList* animTransformList =
GetGradientTransformList(GetContent());
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.
// Before we recurse, make sure we'll break reference loops and over long
// reference chains:
static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
&sRefChainLengthCounter);
if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
// Break reference chain
return aDefault;
}
nsSVGGradientFrame* next = GetReferencedGradient();
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.
// Before we recurse, make sure we'll break reference loops and over long
// reference chains:
static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
&sRefChainLengthCounter);
if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
// Break reference chain
return aDefault;
}
nsSVGGradientFrame* next = GetReferencedGradient();
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);
const nsStyleSVGReset* styleSVGReset = aStopFrame->StyleSVGReset();
*aOffset = mozilla::clamped(*aOffset, 0.0f, 1.0f);
*aStopColor = styleSVGReset->mStopColor.CalcColor(aStopFrame);
*aStopOpacity = styleSVGReset->mStopOpacity;
}
already_AddRefed<gfxPattern> nsSVGGradientFrame::GetPaintServerPattern(
nsIFrame* aSource, const DrawTarget* aDrawTarget,
const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::*aFillOrStroke,
float aGraphicOpacity, imgDrawingParams& aImgParams,
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()->IsText() ? 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(DeviceColor());
return do_AddRef(new gfxPattern(DeviceColor()));
}
if (nStops == 1 || GradientVectorLengthIsZero()) {
auto lastStopFrame = stopFrames[nStops - 1];
auto svgReset = lastStopFrame->StyleSVGReset();
// The gradient paints a single colour, using the stop-color of the last
// gradient step if there are more than one.
float stopOpacity = svgReset->mStopOpacity;
nscolor stopColor = svgReset->mStopColor.CalcColor(lastStopFrame);
sRGBColor stopColor2 = sRGBColor::FromABGR(stopColor);
stopColor2.a *= stopOpacity * aGraphicOpacity;
return do_AddRef(new gfxPattern(ToDeviceColor(stopColor2)));
}
// 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) {
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;
sRGBColor stopColor2 = sRGBColor::FromABGR(stopColor);
stopColor2.a *= stopOpacity * aGraphicOpacity;
gradient->AddColorStop(offset, ToDeviceColor(stopColor2));
}
return gradient.forget();
}
// Private (helper) methods
nsSVGGradientFrame* nsSVGGradientFrame::GetReferencedGradient() {
if (mNoHRefURI) {
return nullptr;
}
auto GetHref = [this](nsAString& aHref) {
dom::SVGGradientElement* grad =
static_cast<dom::SVGGradientElement*>(this->GetContent());
if (grad->mStringAttributes[dom::SVGGradientElement::HREF]
.IsExplicitlySet()) {
grad->mStringAttributes[dom::SVGGradientElement::HREF].GetAnimValue(aHref,
grad);
} else {
grad->mStringAttributes[dom::SVGGradientElement::XLINK_HREF].GetAnimValue(
aHref, grad);
}
this->mNoHRefURI = aHref.IsEmpty();
};
nsIFrame* tframe = SVGObserverUtils::GetAndObserveTemplate(this, GetHref);
if (tframe) {
LayoutFrameType frameType = tframe->Type();
if (frameType == LayoutFrameType::SVGLinearGradient ||
frameType == LayoutFrameType::SVGRadialGradient) {
return static_cast<nsSVGGradientFrame*>(tframe);
}
// We don't call SVGObserverUtils::RemoveTemplateObserver and set
// `mNoHRefURI = false` here since we want to be invalidated if the ID
// specified by our href starts resolving to a different/valid element.
}
return nullptr;
}
void nsSVGGradientFrame::GetStopFrames(nsTArray<nsIFrame*>* aStopFrames) {
nsIFrame* stopFrame = nullptr;
for (stopFrame = mFrames.FirstChild(); stopFrame;
stopFrame = stopFrame->GetNextSibling()) {
if (stopFrame->IsSVGStopFrame()) {
aStopFrames->AppendElement(stopFrame);
}
}
if (aStopFrames->Length() > 0) {
return;
}
// Our gradient element doesn't have stops - try to "inherit" them
// Before we recurse, make sure we'll break reference loops and over long
// reference chains:
static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
&sRefChainLengthCounter);
if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
// Break reference chain
return;
}
nsSVGGradientFrame* next = GetReferencedGradient();
if (next) {
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 */
nsresult nsSVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType) {
if (aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::x1 || aAttribute == nsGkAtoms::y1 ||
aAttribute == nsGkAtoms::x2 || aAttribute == nsGkAtoms::y2)) {
SVGObserverUtils::InvalidateDirectRenderingObservers(this);
}
return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
}
//----------------------------------------------------------------------
float nsSVGLinearGradientFrame::GetLengthValue(uint32_t aIndex) {
dom::SVGLinearGradientElement* lengthElement = GetLinearGradientWithLength(
aIndex, static_cast<dom::SVGLinearGradientElement*>(GetContent()));
// 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 SVGAnimatedLength& 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<SVGViewportElement*>(nullptr));
}
dom::SVGLinearGradientElement*
nsSVGLinearGradientFrame::GetLinearGradientWithLength(
uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) {
dom::SVGLinearGradientElement* thisElement =
static_cast<dom::SVGLinearGradientElement*>(GetContent());
const SVGAnimatedLength& 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 */
nsresult nsSVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType) {
if (aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::r || aAttribute == nsGkAtoms::cx ||
aAttribute == nsGkAtoms::cy || aAttribute == nsGkAtoms::fx ||
aAttribute == nsGkAtoms::fy)) {
SVGObserverUtils::InvalidateDirectRenderingObservers(this);
}
return nsSVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
}
//----------------------------------------------------------------------
float nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex) {
dom::SVGRadialGradientElement* lengthElement = GetRadialGradientWithLength(
aIndex, static_cast<dom::SVGRadialGradientElement*>(GetContent()));
// 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 SVGAnimatedLength& 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<SVGViewportElement*>(nullptr));
}
dom::SVGRadialGradientElement*
nsSVGRadialGradientFrame::GetRadialGradientWithLength(
uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) {
dom::SVGRadialGradientElement* thisElement =
static_cast<dom::SVGRadialGradientElement*>(GetContent());
const SVGAnimatedLength& 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);
double dx = fx - cx;
double dy = fy - cy;
double d = std::sqrt((dx * dx) + (dy * dy));
if (d > dMax) {
double angle = std::atan2(dy, dx);
fx = float(dMax * std::cos(angle)) + cx;
fy = float(dMax * std::sin(angle)) + cy;
}
}
RefPtr<gfxPattern> pattern = new gfxPattern(fx, fy, fr, cx, cy, r);
return pattern.forget();
}
// -------------------------------------------------------------------------
// Public functions
// -------------------------------------------------------------------------
nsIFrame* NS_NewSVGLinearGradientFrame(mozilla::PresShell* aPresShell,
ComputedStyle* aStyle) {
return new (aPresShell)
nsSVGLinearGradientFrame(aStyle, aPresShell->GetPresContext());
}
NS_IMPL_FRAMEARENA_HELPERS(nsSVGLinearGradientFrame)
nsIFrame* NS_NewSVGRadialGradientFrame(mozilla::PresShell* aPresShell,
ComputedStyle* aStyle) {
return new (aPresShell)
nsSVGRadialGradientFrame(aStyle, aPresShell->GetPresContext());
}
NS_IMPL_FRAMEARENA_HELPERS(nsSVGRadialGradientFrame)