Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 "SVGPatternFrame.h"
// Keep others in (case-insensitive) order:
#include "AutoReferenceChainGuard.h"
#include "gfx2DGlue.h"
#include "gfxContext.h"
#include "gfxMatrix.h"
#include "gfxPattern.h"
#include "gfxPlatform.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/ISVGDisplayableFrame.h"
#include "mozilla/PresShell.h"
#include "mozilla/SVGContentUtils.h"
#include "mozilla/SVGGeometryFrame.h"
#include "mozilla/SVGObserverUtils.h"
#include "mozilla/SVGUtils.h"
#include "mozilla/dom/SVGPatternElement.h"
#include "mozilla/dom/SVGUnitTypesBinding.h"
#include "mozilla/gfx/2D.h"
#include "nsGkAtoms.h"
#include "nsIFrameInlines.h"
#include "SVGAnimatedTransformList.h"
using namespace mozilla::dom;
using namespace mozilla::dom::SVGUnitTypes_Binding;
using namespace mozilla::gfx;
using namespace mozilla::image;
namespace mozilla {
//----------------------------------------------------------------------
// Implementation
SVGPatternFrame::SVGPatternFrame(ComputedStyle* aStyle,
nsPresContext* aPresContext)
: SVGPaintServerFrame(aStyle, aPresContext, kClassID),
mSource(nullptr),
mLoopFlag(false),
mNoHRefURI(false) {}
NS_IMPL_FRAMEARENA_HELPERS(SVGPatternFrame)
NS_QUERYFRAME_HEAD(SVGPatternFrame)
NS_QUERYFRAME_ENTRY(SVGPatternFrame)
NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame)
//----------------------------------------------------------------------
// nsIFrame methods:
nsresult SVGPatternFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute,
int32_t aModType) {
if (aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::patternUnits ||
aAttribute == nsGkAtoms::patternContentUnits ||
aAttribute == nsGkAtoms::patternTransform ||
aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
aAttribute == nsGkAtoms::preserveAspectRatio ||
aAttribute == nsGkAtoms::viewBox)) {
SVGObserverUtils::InvalidateRenderingObservers(this);
}
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::InvalidateRenderingObservers(this);
}
return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
}
#ifdef DEBUG
void SVGPatternFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) {
NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::pattern),
"Content is not an SVG pattern");
SVGPaintServerFrame::Init(aContent, aParent, aPrevInFlow);
}
#endif /* DEBUG */
//----------------------------------------------------------------------
// SVGContainerFrame methods:
// If our GetCanvasTM is getting called, we
// need to return *our current* transformation
// matrix, which depends on our units parameters
// and X, Y, Width, and Height
gfxMatrix SVGPatternFrame::GetCanvasTM() {
if (mCTM) {
return *mCTM;
}
// Do we know our rendering parent?
if (mSource) {
// Yes, use it!
return mSource->GetCanvasTM();
}
// We get here when geometry in the <pattern> container is updated
return gfxMatrix();
}
// -------------------------------------------------------------------------
// Helper functions
// -------------------------------------------------------------------------
/** Calculate the maximum expansion of a matrix */
static float MaxExpansion(const Matrix& aMatrix) {
// maximum expansion derivation from
// and also implemented in cairo_matrix_transformed_circle_major_axis
double a = aMatrix._11;
double b = aMatrix._12;
double c = aMatrix._21;
double d = aMatrix._22;
double f = (a * a + b * b + c * c + d * d) / 2;
double g = (a * a + b * b - c * c - d * d) / 2;
double h = a * c + b * d;
return sqrt(f + sqrt(g * g + h * h));
}
// The SVG specification says that the 'patternContentUnits' attribute "has no
// effect if attribute ‘viewBox’ is specified". We still need to include a bbox
// scale if the viewBox is specified and _patternUnits_ is set to or defaults to
// objectBoundingBox though, since in that case the viewBox is relative to the
// bbox
static bool IncludeBBoxScale(const SVGAnimatedViewBox& aViewBox,
uint32_t aPatternContentUnits,
uint32_t aPatternUnits) {
return (!aViewBox.IsExplicitlySet() &&
aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) ||
(aViewBox.IsExplicitlySet() &&
aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
}
// Given the matrix for the pattern element's own transform, this returns a
// combined matrix including the transforms applicable to its target.
static Matrix GetPatternMatrix(nsIFrame* aSource,
const StyleSVGPaint nsStyleSVG::*aFillOrStroke,
uint16_t aPatternUnits,
const gfxMatrix& patternTransform,
const gfxRect& bbox, const gfxRect& callerBBox,
const Matrix& callerCTM) {
// We really want the pattern matrix to handle translations
gfxFloat minx = bbox.X();
gfxFloat miny = bbox.Y();
if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
minx += callerBBox.X();
miny += callerBBox.Y();
}
double scale = 1.0 / MaxExpansion(callerCTM);
auto patternMatrix = patternTransform;
patternMatrix.PreScale(scale, scale);
patternMatrix.PreTranslate(minx, miny);
// revert the vector effect transform so that the pattern appears unchanged
if (aFillOrStroke == &nsStyleSVG::mStroke) {
gfxMatrix userToOuterSVG;
if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
patternMatrix *= userToOuterSVG;
}
}
return ToMatrix(patternMatrix);
}
static nsresult GetTargetGeometry(gfxRect* aBBox,
const SVGAnimatedViewBox& aViewBox,
uint16_t aPatternContentUnits,
uint16_t aPatternUnits, nsIFrame* aTarget,
const Matrix& aContextMatrix,
const gfxRect* aOverrideBounds) {
*aBBox =
aOverrideBounds
? *aOverrideBounds
: SVGUtils::GetBBox(aTarget, SVGUtils::eUseFrameBoundsForOuterSVG |
SVGUtils::eBBoxIncludeFillGeometry);
// Sanity check
if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) &&
(aBBox->Width() <= 0 || aBBox->Height() <= 0)) {
return NS_ERROR_FAILURE;
}
// OK, now fix up the bounding box to reflect user coordinates
// We handle device unit scaling in pattern matrix
float scale = MaxExpansion(aContextMatrix);
if (scale <= 0) {
return NS_ERROR_FAILURE;
}
aBBox->Scale(scale);
return NS_OK;
}
void SVGPatternFrame::PaintChildren(DrawTarget* aDrawTarget,
SVGPatternFrame* aPatternWithChildren,
nsIFrame* aSource, float aGraphicOpacity,
imgDrawingParams& aImgParams) {
gfxContext ctx(aDrawTarget);
gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&ctx);
if (aGraphicOpacity != 1.0f) {
autoGroupForBlend.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
aGraphicOpacity);
}
// OK, now render -- note that we use "firstKid", which
// we got at the beginning because it takes care of the
// referenced pattern situation for us
if (aSource->IsSVGGeometryFrame()) {
// Set the geometrical parent of the pattern we are rendering
aPatternWithChildren->mSource = static_cast<SVGGeometryFrame*>(aSource);
}
// Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
// give back a clear surface if there's a loop
if (!aPatternWithChildren->HasAnyStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER)) {
AutoSetRestorePaintServerState paintServer(aPatternWithChildren);
for (auto* kid : aPatternWithChildren->mFrames) {
gfxMatrix tm = *(aPatternWithChildren->mCTM);
// The CTM of each frame referencing us can be different
ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
if (SVGFrame) {
SVGFrame->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED);
tm = SVGUtils::GetTransformMatrixInUserSpace(kid) * tm;
}
SVGUtils::PaintFrameWithEffects(kid, ctx, tm, aImgParams);
}
}
aPatternWithChildren->mSource = nullptr;
}
already_AddRefed<SourceSurface> SVGPatternFrame::PaintPattern(
const DrawTarget* aDrawTarget, Matrix* patternMatrix,
const Matrix& aContextMatrix, nsIFrame* aSource,
StyleSVGPaint nsStyleSVG::*aFillOrStroke, float aGraphicOpacity,
const gfxRect* aOverrideBounds, imgDrawingParams& aImgParams) {
/*
* General approach:
* Set the content geometry stuff
* Calculate our bbox (using x,y,width,height & patternUnits &
* patternTransform)
* Create the surface
* Calculate the content transformation matrix
* Get our children (we may need to get them from another Pattern)
* Call SVGPaint on all of our children
* Return
*/
SVGPatternFrame* patternWithChildren = GetPatternWithChildren();
if (!patternWithChildren) {
// Either no kids or a bad reference
return nullptr;
}
const SVGAnimatedViewBox& viewBox = GetViewBox();
uint16_t patternContentUnits =
GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS);
uint16_t patternUnits = GetEnumValue(SVGPatternElement::PATTERNUNITS);
/*
* Get the content geometry information. This is a little tricky --
* our parent is probably a <defs>, but we are rendering in the context
* of some geometry source. Our content geometry information needs to
* come from our rendering parent as opposed to our content parent. We
* get that information from aSource, which is passed to us from the
* backend renderer.
*
* There are three "geometries" that we need:
* 1) The bounding box for the pattern. We use this to get the
* width and height for the surface, and as the return to
* GetBBox.
* 2) The transformation matrix for the pattern. This is not *quite*
* the same as the canvas transformation matrix that we will
* provide to our rendering children since we "fudge" it a little
* to get the renderer to handle the translations correctly for us.
* 3) The CTM that we return to our children who make up the pattern.
*/
// Get all of the information we need from our "caller" -- i.e.
// the geometry that is being rendered with a pattern
gfxRect callerBBox;
if (NS_FAILED(GetTargetGeometry(&callerBBox, viewBox, patternContentUnits,
patternUnits, aSource, aContextMatrix,
aOverrideBounds))) {
return nullptr;
}
// Construct the CTM that we will provide to our children when we
// render them into the tile.
gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits,
callerBBox, aContextMatrix, aSource);
if (ctm.IsSingular()) {
return nullptr;
}
if (patternWithChildren->mCTM) {
*patternWithChildren->mCTM = ctm;
} else {
patternWithChildren->mCTM = MakeUnique<gfxMatrix>(ctm);
}
// Get the bounding box of the pattern. This will be used to determine
// the size of the surface, and will also be used to define the bounding
// box for the pattern tile.
gfxRect bbox =
GetPatternRect(patternUnits, callerBBox, aContextMatrix, aSource);
if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) {
return nullptr;
}
// Get the pattern transform
auto patternTransform = GetPatternTransform();
// Get the transformation matrix that we will hand to the renderer's pattern
// routine.
*patternMatrix =
GetPatternMatrix(aSource, aFillOrStroke, patternUnits, patternTransform,
bbox, callerBBox, aContextMatrix);
if (patternMatrix->IsSingular()) {
return nullptr;
}
// Now that we have all of the necessary geometries, we can
// create our surface.
gfxSize scaledSize = bbox.Size() * MaxExpansion(ToMatrix(patternTransform));
bool resultOverflows;
IntSize surfaceSize =
SVGUtils::ConvertToSurfaceSize(scaledSize, &resultOverflows);
// 0 disables rendering, < 0 is an error
if (surfaceSize.width <= 0 || surfaceSize.height <= 0) {
return nullptr;
}
gfxFloat patternWidth = bbox.Width();
gfxFloat patternHeight = bbox.Height();
if (resultOverflows || patternWidth != surfaceSize.width ||
patternHeight != surfaceSize.height) {
// scale drawing to pattern surface size
patternWithChildren->mCTM->PostScale(surfaceSize.width / patternWidth,
surfaceSize.height / patternHeight);
// and rescale pattern to compensate
patternMatrix->PreScale(patternWidth / surfaceSize.width,
patternHeight / surfaceSize.height);
}
RefPtr<DrawTarget> dt = aDrawTarget->CreateSimilarDrawTargetWithBacking(
surfaceSize, SurfaceFormat::B8G8R8A8);
if (!dt || !dt->IsValid()) {
return nullptr;
}
dt->ClearRect(Rect(0, 0, surfaceSize.width, surfaceSize.height));
PaintChildren(dt, patternWithChildren, aSource, aGraphicOpacity, aImgParams);
// caller now owns the surface
return dt->GetBackingSurface();
}
/* Will probably need something like this... */
// How do we handle the insertion of a new frame?
// We really don't want to rerender this every time,
// do we?
SVGPatternFrame* SVGPatternFrame::GetPatternWithChildren() {
// Do we have any children ourselves?
if (!mFrames.IsEmpty()) {
return this;
}
// No, see if we chain to someone who does
// 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 nullptr;
}
SVGPatternFrame* next = GetReferencedPattern();
if (!next) {
return nullptr;
}
return next->GetPatternWithChildren();
}
uint16_t SVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) {
SVGAnimatedEnumeration& thisEnum =
static_cast<SVGPatternElement*>(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<SVGPatternElement*>(aDefault)
->mEnumAttributes[aIndex]
.GetAnimValue();
}
SVGPatternFrame* next = GetReferencedPattern();
return next ? next->GetEnumValue(aIndex, aDefault)
: static_cast<SVGPatternElement*>(aDefault)
->mEnumAttributes[aIndex]
.GetAnimValue();
}
SVGAnimatedTransformList* SVGPatternFrame::GetPatternTransformList(
nsIContent* aDefault) {
SVGAnimatedTransformList* thisTransformList =
static_cast<SVGPatternElement*>(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<SVGPatternElement*>(aDefault)->mPatternTransform.get();
}
SVGPatternFrame* next = GetReferencedPattern();
return next ? next->GetPatternTransformList(aDefault)
: static_cast<SVGPatternElement*>(aDefault)
->mPatternTransform.get();
}
gfxMatrix SVGPatternFrame::GetPatternTransform() {
SVGAnimatedTransformList* animTransformList =
GetPatternTransformList(GetContent());
if (!animTransformList) {
return SVGUtils::GetTransformMatrixInUserSpace(this);
}
return animTransformList->GetAnimValue().GetConsolidationMatrix();
}
const SVGAnimatedViewBox& SVGPatternFrame::GetViewBox(nsIContent* aDefault) {
const SVGAnimatedViewBox& thisViewBox =
static_cast<SVGPatternElement*>(GetContent())->mViewBox;
if (thisViewBox.IsExplicitlySet()) {
return thisViewBox;
}
// 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<SVGPatternElement*>(aDefault)->mViewBox;
}
SVGPatternFrame* next = GetReferencedPattern();
return next ? next->GetViewBox(aDefault)
: static_cast<SVGPatternElement*>(aDefault)->mViewBox;
}
const SVGAnimatedPreserveAspectRatio& SVGPatternFrame::GetPreserveAspectRatio(
nsIContent* aDefault) {
const SVGAnimatedPreserveAspectRatio& thisPar =
static_cast<SVGPatternElement*>(GetContent())->mPreserveAspectRatio;
if (thisPar.IsExplicitlySet()) {
return thisPar;
}
// 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<SVGPatternElement*>(aDefault)->mPreserveAspectRatio;
}
SVGPatternFrame* next = GetReferencedPattern();
return next ? next->GetPreserveAspectRatio(aDefault)
: static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio;
}
const SVGAnimatedLength* SVGPatternFrame::GetLengthValue(uint32_t aIndex,
nsIContent* aDefault) {
const SVGAnimatedLength* thisLength =
&static_cast<SVGPatternElement*>(GetContent())->mLengthAttributes[aIndex];
if (thisLength->IsExplicitlySet()) {
return thisLength;
}
// 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<SVGPatternElement*>(aDefault)
->mLengthAttributes[aIndex];
}
SVGPatternFrame* next = GetReferencedPattern();
return next ? next->GetLengthValue(aIndex, aDefault)
: &static_cast<SVGPatternElement*>(aDefault)
->mLengthAttributes[aIndex];
}
// Private (helper) methods
SVGPatternFrame* SVGPatternFrame::GetReferencedPattern() {
if (mNoHRefURI) {
return nullptr;
}
auto GetHref = [this](nsAString& aHref) {
SVGPatternElement* pattern =
static_cast<SVGPatternElement*>(this->GetContent());
if (pattern->mStringAttributes[SVGPatternElement::HREF].IsExplicitlySet()) {
pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(aHref,
pattern);
} else {
pattern->mStringAttributes[SVGPatternElement::XLINK_HREF].GetAnimValue(
aHref, pattern);
}
this->mNoHRefURI = aHref.IsEmpty();
};
// We don't call SVGObserverUtils::RemoveTemplateObserver and set
// `mNoHRefURI = false` on failure since we want to be invalidated if the ID
// specified by our href starts resolving to a different/valid element.
return do_QueryFrame(SVGObserverUtils::GetAndObserveTemplate(this, GetHref));
}
gfxRect SVGPatternFrame::GetPatternRect(uint16_t aPatternUnits,
const gfxRect& aTargetBBox,
const Matrix& aTargetCTM,
nsIFrame* aTarget) {
// We need to initialize our box
float x, y, width, height;
// Get the pattern x,y,width, and height
const SVGAnimatedLength *tmpX, *tmpY, *tmpHeight, *tmpWidth;
tmpX = GetLengthValue(SVGPatternElement::ATTR_X);
tmpY = GetLengthValue(SVGPatternElement::ATTR_Y);
tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT);
tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH);
if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
x = SVGUtils::ObjectSpace(aTargetBBox, tmpX);
y = SVGUtils::ObjectSpace(aTargetBBox, tmpY);
width = SVGUtils::ObjectSpace(aTargetBBox, tmpWidth);
height = SVGUtils::ObjectSpace(aTargetBBox, tmpHeight);
} else {
if (aTarget->IsTextFrame()) {
aTarget = aTarget->GetParent();
}
float scale = MaxExpansion(aTargetCTM);
x = SVGUtils::UserSpace(aTarget, tmpX) * scale;
y = SVGUtils::UserSpace(aTarget, tmpY) * scale;
width = SVGUtils::UserSpace(aTarget, tmpWidth) * scale;
height = SVGUtils::UserSpace(aTarget, tmpHeight) * scale;
}
return gfxRect(x, y, width, height);
}
gfxMatrix SVGPatternFrame::ConstructCTM(const SVGAnimatedViewBox& aViewBox,
uint16_t aPatternContentUnits,
uint16_t aPatternUnits,
const gfxRect& callerBBox,
const Matrix& callerCTM,
nsIFrame* aTarget) {
if (aTarget->IsTextFrame()) {
aTarget = aTarget->GetParent();
}
nsIContent* targetContent = aTarget->GetContent();
SVGViewportElement* ctx = nullptr;
gfxFloat scaleX, scaleY;
// The objectBoundingBox conversion must be handled in the CTM:
if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) {
scaleX = callerBBox.Width();
scaleY = callerBBox.Height();
} else {
if (targetContent->IsSVGElement()) {
ctx = static_cast<SVGElement*>(targetContent)->GetCtx();
}
scaleX = scaleY = MaxExpansion(callerCTM);
}
if (!aViewBox.IsExplicitlySet()) {
return gfxMatrix(scaleX, 0.0, 0.0, scaleY, 0.0, 0.0);
}
const SVGViewBox& viewBox = aViewBox.GetAnimValue();
if (viewBox.height <= 0.0f || viewBox.width <= 0.0f) {
return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
}
float viewportWidth, viewportHeight;
if (targetContent->IsSVGElement()) {
// If we're dealing with an SVG target only retrieve the context once.
// Calling the nsIFrame* variant of GetAnimValue would look it up on
// every call.
viewportWidth =
GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(ctx);
viewportHeight =
GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(ctx);
} else {
// No SVG target, call the nsIFrame* variant of GetAnimValue.
viewportWidth =
GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(aTarget);
viewportHeight =
GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(aTarget);
}
if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
}
Matrix tm = SVGContentUtils::GetViewBoxTransform(
viewportWidth * scaleX, viewportHeight * scaleY, viewBox.x, viewBox.y,
viewBox.width, viewBox.height, GetPreserveAspectRatio());
return ThebesMatrix(tm);
}
//----------------------------------------------------------------------
// SVGPaintServerFrame methods:
already_AddRefed<gfxPattern> SVGPatternFrame::GetPaintServerPattern(
nsIFrame* aSource, const DrawTarget* aDrawTarget,
const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::*aFillOrStroke,
float aGraphicOpacity, imgDrawingParams& aImgParams,
const gfxRect* aOverrideBounds) {
if (aGraphicOpacity == 0.0f) {
return do_AddRef(new gfxPattern(DeviceColor()));
}
// Paint it!
Matrix pMatrix;
RefPtr<SourceSurface> surface =
PaintPattern(aDrawTarget, &pMatrix, ToMatrix(aContextMatrix), aSource,
aFillOrStroke, aGraphicOpacity, aOverrideBounds, aImgParams);
if (!surface) {
return nullptr;
}
RefPtr<gfxPattern> pattern = new gfxPattern(surface, pMatrix);
if (!pattern) {
return nullptr;
}
pattern->SetExtend(ExtendMode::REPEAT);
return pattern.forget();
}
} // namespace mozilla
// -------------------------------------------------------------------------
// Public functions
// -------------------------------------------------------------------------
nsIFrame* NS_NewSVGPatternFrame(mozilla::PresShell* aPresShell,
mozilla::ComputedStyle* aStyle) {
return new (aPresShell)
mozilla::SVGPatternFrame(aStyle, aPresShell->GetPresContext());
}