From 799ef86fc1b9c2e666e98c88c85dffd87150426f Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Sat, 4 Oct 2014 12:13:30 +0100 Subject: [PATCH] Bug 932762, part 2 - Make SVG elements cache their Moz2D path data to speed up rendering, hit-testing, clipPath clipping, bbox calculations and animation/text along a path. r=longsonr --- content/svg/content/src/DOMSVGPathSeg.cpp | 1 - content/svg/content/src/DOMSVGPathSeg.h | 4 -- content/svg/content/src/DOMSVGPathSegList.cpp | 3 -- .../src/SVGMotionSMILAnimationFunction.cpp | 4 +- content/svg/content/src/SVGPathData.cpp | 19 +++----- content/svg/content/src/SVGPathData.h | 10 ++--- content/svg/content/src/SVGPathElement.cpp | 10 ++--- content/svg/content/src/SVGPathElement.h | 3 +- content/svg/content/src/nsSVGElement.cpp | 7 +++ content/svg/content/src/nsSVGElement.h | 2 + .../content/src/nsSVGPathGeometryElement.cpp | 39 ++++++++++++++++- .../content/src/nsSVGPathGeometryElement.h | 43 ++++++++++++++++++- .../reftests/svg/dynamic-fill-rule-01-ref.svg | 20 +++++++++ layout/reftests/svg/dynamic-fill-rule-01.svg | 25 +++++++++++ layout/reftests/svg/reftest.list | 1 + layout/svg/SVGTextFrame.cpp | 2 +- layout/svg/nsSVGClipPathFrame.cpp | 6 +-- layout/svg/nsSVGPathGeometryFrame.cpp | 41 ++++++++++++------ layout/svg/nsSVGUtils.cpp | 10 +++++ layout/svg/nsSVGUtils.h | 1 + modules/libpref/init/all.js | 3 ++ 21 files changed, 196 insertions(+), 58 deletions(-) create mode 100644 layout/reftests/svg/dynamic-fill-rule-01-ref.svg create mode 100644 layout/reftests/svg/dynamic-fill-rule-01.svg diff --git a/content/svg/content/src/DOMSVGPathSeg.cpp b/content/svg/content/src/DOMSVGPathSeg.cpp index d139e29e24d..c0f785fff65 100644 --- a/content/svg/content/src/DOMSVGPathSeg.cpp +++ b/content/svg/content/src/DOMSVGPathSeg.cpp @@ -180,7 +180,6 @@ DOMSVGPathSeg::IndexIsValid() } \ AutoChangePathSegNotifier notifier(this); \ InternalItem()[1+index] = float(a##propName); \ - InvalidateCachedList(); \ } else { \ mArgs[index] = float(a##propName); \ } \ diff --git a/content/svg/content/src/DOMSVGPathSeg.h b/content/svg/content/src/DOMSVGPathSeg.h index ac1986a9709..388356af803 100644 --- a/content/svg/content/src/DOMSVGPathSeg.h +++ b/content/svg/content/src/DOMSVGPathSeg.h @@ -209,10 +209,6 @@ protected: */ float* InternalItem(); - void InvalidateCachedList() { - mList->InternalList().mCachedPath = nullptr; - } - virtual float* PtrToMemberArgs() = 0; #ifdef DEBUG diff --git a/content/svg/content/src/DOMSVGPathSegList.cpp b/content/svg/content/src/DOMSVGPathSegList.cpp index 63177e7626e..b639c951506 100644 --- a/content/svg/content/src/DOMSVGPathSegList.cpp +++ b/content/svg/content/src/DOMSVGPathSegList.cpp @@ -383,7 +383,6 @@ DOMSVGPathSegList::InsertItemBefore(DOMSVGPathSeg& aNewItem, domItem->ToSVGPathSegEncodedData(segAsRaw); InternalList().mData.InsertElementsAt(internalIndex, segAsRaw, 1 + argCount); - InternalList().mCachedPath = nullptr; mItems.InsertElementAt(aIndex, ItemProxy(domItem.get(), internalIndex)); // This MUST come after the insertion into InternalList(), or else under the @@ -440,7 +439,6 @@ DOMSVGPathSegList::ReplaceItem(DOMSVGPathSeg& aNewItem, bool ok = !!InternalList().mData.ReplaceElementsAt( internalIndex, 1 + oldArgCount, segAsRaw, 1 + newArgCount); - InternalList().mCachedPath = nullptr; if (!ok) { aError.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; @@ -495,7 +493,6 @@ DOMSVGPathSegList::RemoveItem(uint32_t aIndex, MaybeRemoveItemFromAnimValListAt(aIndex, argCount); InternalList().mData.RemoveElementsAt(internalIndex, 1 + argCount); - InternalList().mCachedPath = nullptr; mItems.RemoveElementAt(aIndex); UpdateListIndicesFromIndex(aIndex, -(argCount + 1)); diff --git a/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp b/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp index 0d05e793078..abe6b16b493 100644 --- a/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp +++ b/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp @@ -227,7 +227,7 @@ SVGMotionSMILAnimationFunction:: bool ok = path.GetDistancesFromOriginToEndsOfVisibleSegments(&mPathVertices); if (ok && mPathVertices.Length()) { - mPath = pathElem->GetPathForLengthOrPositionMeasuring(); + mPath = pathElem->GetOrBuildPathForMeasuring(); } } } @@ -252,7 +252,7 @@ SVGMotionSMILAnimationFunction::RebuildPathAndVerticesFromPathAttr() return; } - mPath = path.ToPathForLengthOrPositionMeasuring(); + mPath = path.BuildPathForMeasuring(); bool ok = path.GetDistancesFromOriginToEndsOfVisibleSegments(&mPathVertices); if (!ok || !mPathVertices.Length()) { mPath = nullptr; diff --git a/content/svg/content/src/SVGPathData.cpp b/content/svg/content/src/SVGPathData.cpp index facf77df8a0..87c228d22b4 100644 --- a/content/svg/content/src/SVGPathData.cpp +++ b/content/svg/content/src/SVGPathData.cpp @@ -37,7 +37,6 @@ SVGPathData::CopyFrom(const SVGPathData& rhs) // Yes, we do want fallible alloc here return NS_ERROR_OUT_OF_MEMORY; } - mCachedPath = nullptr; mData = rhs.mData; return NS_OK; } @@ -72,7 +71,6 @@ SVGPathData::SetValueFromString(const nsAString& aValue) // the first error. We still return any error though so that callers know if // there's a problem. - mCachedPath = nullptr; nsSVGPathDataParser pathParser(aValue, this); return pathParser.Parse() ? NS_OK : NS_ERROR_DOM_SYNTAX_ERR; } @@ -85,7 +83,6 @@ SVGPathData::AppendSeg(uint32_t aType, ...) if (!mData.SetLength(newLength)) { return NS_ERROR_OUT_OF_MEMORY; } - mCachedPath = nullptr; mData[oldLength] = SVGPathSegUtils::EncodeType(aType); va_list args; @@ -510,7 +507,7 @@ SVGPathData::BuildPath(PathBuilder* builder, } TemporaryRef -SVGPathData::ToPathForLengthOrPositionMeasuring() const +SVGPathData::BuildPathForMeasuring() const { // Since the path that we return will not be used for painting it doesn't // matter what we pass to CreatePathBuilder as aFillRule. Hawever, we do want @@ -520,15 +517,11 @@ SVGPathData::ToPathForLengthOrPositionMeasuring() const // pass as aStrokeWidth doesn't matter (since it's only used to determine the // length of those extra little lines). - if (!mCachedPath) { - RefPtr drawTarget = - gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); - RefPtr builder = - drawTarget->CreatePathBuilder(FillRule::FILL_WINDING); - mCachedPath = BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 0); - } - - return mCachedPath; + RefPtr drawTarget = + gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); + RefPtr builder = + drawTarget->CreatePathBuilder(FillRule::FILL_WINDING); + return BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 0); } static double diff --git a/content/svg/content/src/SVGPathData.h b/content/svg/content/src/SVGPathData.h index 4c1c4a2dfea..3fd26b6bc07 100644 --- a/content/svg/content/src/SVGPathData.h +++ b/content/svg/content/src/SVGPathData.h @@ -163,7 +163,7 @@ public: * ApproximateZeroLengthSubpathSquareCaps can insert if we have square-caps. * See the comment for that function for more info on that. */ - TemporaryRef ToPathForLengthOrPositionMeasuring() const; + TemporaryRef BuildPathForMeasuring() const; TemporaryRef BuildPath(PathBuilder* aBuilder, uint8_t aCapStyle, @@ -193,7 +193,6 @@ protected: nsresult CopyFrom(const SVGPathData& rhs); float& operator[](uint32_t aIndex) { - mCachedPath = nullptr; return mData[aIndex]; } @@ -202,14 +201,12 @@ protected: * increased, in which case the list will be left unmodified. */ bool SetLength(uint32_t aLength) { - mCachedPath = nullptr; return mData.SetLength(aLength); } nsresult SetValueFromString(const nsAString& aValue); void Clear() { - mCachedPath = nullptr; mData.Clear(); } @@ -223,11 +220,10 @@ protected: nsresult AppendSeg(uint32_t aType, ...); // variable number of float args - iterator begin() { mCachedPath = nullptr; return mData.Elements(); } - iterator end() { mCachedPath = nullptr; return mData.Elements() + mData.Length(); } + iterator begin() { return mData.Elements(); } + iterator end() { return mData.Elements() + mData.Length(); } FallibleTArray mData; - mutable RefPtr mCachedPath; }; diff --git a/content/svg/content/src/SVGPathElement.cpp b/content/svg/content/src/SVGPathElement.cpp index 786754fb8fa..25bc7e82525 100644 --- a/content/svg/content/src/SVGPathElement.cpp +++ b/content/svg/content/src/SVGPathElement.cpp @@ -70,14 +70,14 @@ SVGPathElement::PathLength() float SVGPathElement::GetTotalLength() { - RefPtr flat = GetPathForLengthOrPositionMeasuring(); + RefPtr flat = GetOrBuildPathForMeasuring(); return flat ? flat->ComputeLength() : 0.f; } already_AddRefed SVGPathElement::GetPointAtLength(float distance, ErrorResult& rv) { - RefPtr path = GetPathForLengthOrPositionMeasuring(); + RefPtr path = GetOrBuildPathForMeasuring(); if (!path) { rv.Throw(NS_ERROR_FAILURE); return nullptr; @@ -305,9 +305,9 @@ SVGPathElement::IsAttributeMapped(const nsIAtom* name) const } TemporaryRef -SVGPathElement::GetPathForLengthOrPositionMeasuring() +SVGPathElement::GetOrBuildPathForMeasuring() { - return mD.GetAnimValue().ToPathForLengthOrPositionMeasuring(); + return mD.GetAnimValue().BuildPathForMeasuring(); } //---------------------------------------------------------------------- @@ -340,7 +340,7 @@ SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor) if (mPathLength.IsExplicitlySet()) { float authorsPathLengthEstimate = mPathLength.GetAnimValue(); if (authorsPathLengthEstimate > 0) { - RefPtr path = GetPathForLengthOrPositionMeasuring(); + RefPtr path = GetOrBuildPathForMeasuring(); if (!path) { // The path is empty or invalid so its length must be zero and // we know that 0 / authorsPathLengthEstimate = 0. diff --git a/content/svg/content/src/SVGPathElement.h b/content/svg/content/src/SVGPathElement.h index 8bbf8cca8c7..10e49a1f4df 100644 --- a/content/svg/content/src/SVGPathElement.h +++ b/content/svg/content/src/SVGPathElement.h @@ -57,8 +57,7 @@ public: * ApproximateZeroLengthSubpathSquareCaps can insert if we have square-caps. * See the comment for that function for more info on that. */ - virtual TemporaryRef - GetPathForLengthOrPositionMeasuring() MOZ_OVERRIDE; + virtual TemporaryRef GetOrBuildPathForMeasuring() MOZ_OVERRIDE; // nsIContent interface virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE; diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp index 977be816c73..df0e5be5c0d 100644 --- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -15,6 +15,7 @@ #include "nsICSSDeclaration.h" #include "nsIDocument.h" #include "nsIDOMMutationEvent.h" +#include "nsSVGPathGeometryElement.h" #include "mozilla/InternalMutationEvent.h" #include "nsError.h" #include "nsIPresShell.h" @@ -1605,6 +1606,8 @@ nsSVGElement::DidChangeLength(uint8_t aAttrEnum, void nsSVGElement::DidAnimateLength(uint8_t aAttrEnum) { + ClearAnyCachedPath(); + nsIFrame* frame = GetPrimaryFrame(); if (frame) { @@ -1850,6 +1853,8 @@ nsSVGElement::DidAnimatePointList() NS_ABORT_IF_FALSE(GetPointListAttrName(), "Animating non-existent path data?"); + ClearAnyCachedPath(); + nsIFrame* frame = GetPrimaryFrame(); if (frame) { @@ -1885,6 +1890,8 @@ nsSVGElement::DidAnimatePathSegList() NS_ABORT_IF_FALSE(GetPathDataAttrName(), "Animating non-existent path data?"); + ClearAnyCachedPath(); + nsIFrame* frame = GetPrimaryFrame(); if (frame) { diff --git a/content/svg/content/src/nsSVGElement.h b/content/svg/content/src/nsSVGElement.h index 9745325dbf6..d8794e2e4cb 100644 --- a/content/svg/content/src/nsSVGElement.h +++ b/content/svg/content/src/nsSVGElement.h @@ -33,6 +33,7 @@ class nsSVGIntegerPair; class nsSVGLength2; class nsSVGNumber2; class nsSVGNumberPair; +class nsSVGPathGeometryElement; class nsSVGString; class nsSVGViewBox; @@ -313,6 +314,7 @@ public: return mClassAnimAttr; } + virtual void ClearAnyCachedPath() {} virtual nsIDOMNode* AsDOMNode() MOZ_FINAL MOZ_OVERRIDE { return this; } virtual bool IsTransformable() { return false; } diff --git a/content/svg/content/src/nsSVGPathGeometryElement.cpp b/content/svg/content/src/nsSVGPathGeometryElement.cpp index 01669fec1ae..37c8c648874 100644 --- a/content/svg/content/src/nsSVGPathGeometryElement.cpp +++ b/content/svg/content/src/nsSVGPathGeometryElement.cpp @@ -8,6 +8,7 @@ #include "gfxPlatform.h" #include "mozilla/gfx/2D.h" #include "nsComputedDOMStyle.h" +#include "nsSVGUtils.h" #include "nsSVGLength2.h" #include "SVGContentUtils.h" @@ -22,6 +23,19 @@ nsSVGPathGeometryElement::nsSVGPathGeometryElement(already_AddRefed *aMarks) } TemporaryRef -nsSVGPathGeometryElement::GetPathForLengthOrPositionMeasuring() +nsSVGPathGeometryElement::GetOrBuildPath(const DrawTarget& aDrawTarget, + FillRule aFillRule) +{ + // We only cache the path if it matches the backend used for screen painting: + bool cacheable = aDrawTarget.GetBackendType() == + gfxPlatform::GetPlatform()->GetContentBackend(); + + // Checking for and returning mCachedPath before checking the pref means + // that the pref is only live on page reload (or app restart for SVG in + // chrome). The benefit is that we avoid causing a CPU memory cache miss by + // looking at the global variable that the pref's stored in. + if (cacheable && mCachedPath) { + return mCachedPath; + } + RefPtr builder = aDrawTarget.CreatePathBuilder(aFillRule); + RefPtr path = BuildPath(builder); + if (cacheable && NS_SVGPathCachingEnabled()) { + mCachedPath = path; + } + return path.forget(); +} + +TemporaryRef +nsSVGPathGeometryElement::GetOrBuildPathForMeasuring() { return nullptr; } diff --git a/content/svg/content/src/nsSVGPathGeometryElement.h b/content/svg/content/src/nsSVGPathGeometryElement.h index 7b24e9bdc01..7c9603be7e8 100644 --- a/content/svg/content/src/nsSVGPathGeometryElement.h +++ b/content/svg/content/src/nsSVGPathGeometryElement.h @@ -31,6 +31,7 @@ typedef mozilla::dom::SVGGraphicsElement nsSVGPathGeometryElementBase; class nsSVGPathGeometryElement : public nsSVGPathGeometryElementBase { protected: + typedef mozilla::gfx::DrawTarget DrawTarget; typedef mozilla::gfx::FillRule FillRule; typedef mozilla::gfx::Float Float; typedef mozilla::gfx::Path Path; @@ -39,6 +40,17 @@ protected: public: explicit nsSVGPathGeometryElement(already_AddRefed& aNodeInfo); + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) MOZ_OVERRIDE; + + /** + * Causes this element to discard any Path object that GetOrBuildPath may + * have cached. + */ + virtual void ClearAnyCachedPath() MOZ_OVERRIDE MOZ_FINAL { + mCachedPath = nullptr; + } + virtual bool AttributeDefinesGeometry(const nsIAtom *aName); /** @@ -57,17 +69,44 @@ public: /** * Returns a Path that can be used to paint, hit-test or calculate bounds for + * this element. May return nullptr if there is no [valid] path. The path + * that is created may be cached and returned on subsequent calls. + */ + virtual mozilla::TemporaryRef GetOrBuildPath(const DrawTarget& aDrawTarget, + FillRule fillRule); + + /** + * The same as GetOrBuildPath, but bypasses the cache (neither returns any + * previously cached Path, nor caches the Path that in does return). * this element. May return nullptr if there is no [valid] path. */ - virtual mozilla::TemporaryRef BuildPath(PathBuilder* aBuilder = nullptr) = 0; + virtual mozilla::TemporaryRef BuildPath(PathBuilder* aBuilder) = 0; - virtual mozilla::TemporaryRef GetPathForLengthOrPositionMeasuring(); + /** + * Returns a Path that can be used to measure the length of this elements + * path, or to find the position at a given distance along it. + * + * This is currently equivalent to calling GetOrBuildPath, but it may not be + * in the future. The reason for this function to be separate from + * GetOrBuildPath is because SVGPathData::BuildPath inserts small lines into + * the path if zero length subpaths are encountered, in order to implement + * the SVG specifications requirements that zero length subpaths should + * render circles/squares if stroke-linecap is round/square, respectively. + * In principle these inserted lines could interfere with path measurement, + * so we keep callers that are looking to do measurement separate in case we + * run into problems with the inserted lines negatively affecting measuring + * for content. + */ + virtual mozilla::TemporaryRef GetOrBuildPathForMeasuring(); /** * Returns the current computed value of the CSS property 'fill-rule' for * this element. */ FillRule GetFillRule(); + +protected: + mutable mozilla::RefPtr mCachedPath; }; #endif diff --git a/layout/reftests/svg/dynamic-fill-rule-01-ref.svg b/layout/reftests/svg/dynamic-fill-rule-01-ref.svg new file mode 100644 index 00000000000..a31a09a7cbe --- /dev/null +++ b/layout/reftests/svg/dynamic-fill-rule-01-ref.svg @@ -0,0 +1,20 @@ + + + Reference for dynamic changes to fill-rule + + + + + + + + + + + + + + diff --git a/layout/reftests/svg/dynamic-fill-rule-01.svg b/layout/reftests/svg/dynamic-fill-rule-01.svg new file mode 100644 index 00000000000..9fb12f4ae0f --- /dev/null +++ b/layout/reftests/svg/dynamic-fill-rule-01.svg @@ -0,0 +1,25 @@ + + + Testcase for dynamic changes to fill-rule + + + + + + diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index 09ea02eafc4..b3e62a31c82 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -78,6 +78,7 @@ fuzzy-if(Android,4,87) == dynamic-clipPath-01.svg pass.svg == dynamic-feFlood-01.svg pass.svg asserts(0-1) == dynamic-feImage-01.svg pass.svg # intermittent assertions (bug 886080) == dynamic-fill-01.svg dynamic-fill-01-ref.svg +== dynamic-fill-rule-01.svg dynamic-fill-rule-01-ref.svg fuzzy-if(d2d,1,10000) == dynamic-filter-contents-01a.svg dynamic-filter-contents-01-ref.svg fuzzy-if(d2d,1,10000) == dynamic-filter-contents-01b.svg dynamic-filter-contents-01-ref.svg == dynamic-gradient-contents-01.svg pass.svg diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp index 45c6b743920..f52d123381f 100644 --- a/layout/svg/SVGTextFrame.cpp +++ b/layout/svg/SVGTextFrame.cpp @@ -4805,7 +4805,7 @@ SVGTextFrame::GetTextPath(nsIFrame* aTextPathFrame) nsSVGPathGeometryElement *element = static_cast(pathFrame->GetContent()); - RefPtr path = element->GetPathForLengthOrPositionMeasuring(); + RefPtr path = element->GetOrBuildPathForMeasuring(); if (!path) { return nullptr; } diff --git a/layout/svg/nsSVGClipPathFrame.cpp b/layout/svg/nsSVGClipPathFrame.cpp index f718cc2b741..6f4416e829d 100644 --- a/layout/svg/nsSVGClipPathFrame.cpp +++ b/layout/svg/nsSVGClipPathFrame.cpp @@ -66,10 +66,8 @@ nsSVGClipPathFrame::ApplyClipOrPaintClipMask(nsRenderingContext* aContext, gfx->CurrentMatrix().PreMultiply(toChildsUserSpace).NudgeToIntegers(); if (!newMatrix.IsSingular()) { gfx->SetMatrix(newMatrix); - RefPtr builder = - gfx->GetDrawTarget()->CreatePathBuilder( - nsSVGUtils::ToFillRule(pathFrame->StyleSVG()->mClipRule)); - clipPath = pathElement->BuildPath(builder); + clipPath = pathElement->GetOrBuildPath(*gfx->GetDrawTarget(), + nsSVGUtils::ToFillRule(pathFrame->StyleSVG()->mClipRule)); } } } diff --git a/layout/svg/nsSVGPathGeometryFrame.cpp b/layout/svg/nsSVGPathGeometryFrame.cpp index 6715bff3b25..6ae36142a9f 100644 --- a/layout/svg/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/nsSVGPathGeometryFrame.cpp @@ -158,6 +158,25 @@ nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) // nsDisplayOpacity display list item, so DLBI won't invalidate for us. InvalidateFrame(); } + + nsSVGPathGeometryElement* element = + static_cast(mContent); + + if (aOldStyleContext->PeekStyleSVG()) { + if ((StyleSVG()->mStrokeLinecap != + aOldStyleContext->PeekStyleSVG()->mStrokeLinecap) && + element->Tag() == nsGkAtoms::path) { + // If the stroke-linecap changes to or from "butt" then our element + // needs to update its cached Moz2D Path, since SVGPathData::BuildPath + // decides whether or not to insert little lines into the path for zero + // length subpaths base on that property. + element->ClearAnyCachedPath(); + } else if (StyleSVG()->mFillRule != + aOldStyleContext->PeekStyleSVG()->mFillRule) { + // Moz2D Path objects are fill-rule specific. + element->ClearAnyCachedPath(); + } + } } } @@ -288,9 +307,7 @@ nsSVGPathGeometryFrame::GetFrameForPoint(const gfxPoint& aPoint) // so that we get more consistent/backwards compatible results? RefPtr drawTarget = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); - RefPtr builder = - drawTarget->CreatePathBuilder(fillRule); - RefPtr path = content->BuildPath(builder); + RefPtr path = content->GetOrBuildPath(*drawTarget, fillRule); if (!path) { return nullptr; // no path, so we don't paint anything that can be hit } @@ -410,6 +427,7 @@ nsSVGPathGeometryFrame::NotifySVGChanged(uint32_t aFlags) // mRect. if (static_cast(mContent)->GeometryDependsOnCoordCtx() || StyleSVG()->mStrokeWidth.HasPercent()) { + static_cast(mContent)->ClearAnyCachedPath(); nsSVGUtils::ScheduleReflowSVG(this); } } @@ -454,8 +472,7 @@ nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace, FillRule fillRule = StyleSVG()->mFillRule == NS_STYLE_FILL_RULE_NONZERO ? FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD; - RefPtr builder = tmpDT->CreatePathBuilder(fillRule); - RefPtr pathInUserSpace = element->BuildPath(builder); + RefPtr pathInUserSpace = element->GetOrBuildPath(*tmpDT, fillRule); if (!pathInUserSpace) { return bbox; } @@ -463,7 +480,7 @@ nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace, if (aToBBoxUserspace.IsIdentity()) { pathInBBoxSpace = pathInUserSpace; } else { - builder = + RefPtr builder = pathInUserSpace->TransformedCopyToBuilder(aToBBoxUserspace, fillRule); pathInBBoxSpace = builder->Finish(); if (!pathInBBoxSpace) { @@ -663,13 +680,10 @@ nsSVGPathGeometryFrame::Render(gfxContext* aContext, nsSVGUtils::ToFillRule(renderMode == SVGAutoRenderState::NORMAL ? StyleSVG()->mFillRule : StyleSVG()->mClipRule); - RefPtr builder = drawTarget->CreatePathBuilder(fillRule); - if (!builder) { - return; - } + nsSVGPathGeometryElement* element = + static_cast(mContent); - RefPtr path = - static_cast(mContent)->BuildPath(builder); + RefPtr path = element->GetOrBuildPath(*drawTarget, fillRule); if (!path) { return; } @@ -715,7 +729,8 @@ nsSVGPathGeometryFrame::Render(gfxContext* aContext, gfxMatrix outerSVGToUser = userToOuterSVG; outerSVGToUser.Invert(); aContext->Multiply(outerSVGToUser); - builder = path->TransformedCopyToBuilder(ToMatrix(userToOuterSVG), fillRule); + RefPtr builder = + path->TransformedCopyToBuilder(ToMatrix(userToOuterSVG), fillRule); path = builder->Finish(); } GeneralPattern strokePattern; diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index 1bc40d0a08a..05a071f88d2 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -57,10 +57,17 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::gfx; +static bool sSVGPathCachingEnabled; static bool sSVGDisplayListHitTestingEnabled; static bool sSVGDisplayListPaintingEnabled; static bool sSVGNewGetBBoxEnabled; +bool +NS_SVGPathCachingEnabled() +{ + return sSVGPathCachingEnabled; +} + bool NS_SVGDisplayListHitTestingEnabled() { @@ -137,6 +144,9 @@ SVGAutoRenderState::IsPaintingToWindow(DrawTarget* aDrawTarget) void nsSVGUtils::Init() { + Preferences::AddBoolVarCache(&sSVGPathCachingEnabled, + "svg.path-caching.enabled"); + Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled, "svg.display-lists.hit-testing.enabled"); diff --git a/layout/svg/nsSVGUtils.h b/layout/svg/nsSVGUtils.h index 8e7bf533ffb..b3e4f79a33b 100644 --- a/layout/svg/nsSVGUtils.h +++ b/layout/svg/nsSVGUtils.h @@ -78,6 +78,7 @@ class SourceSurface; #define SVG_HIT_TEST_CHECK_MRECT 0x04 +bool NS_SVGPathCachingEnabled(); bool NS_SVGDisplayListHitTestingEnabled(); bool NS_SVGDisplayListPaintingEnabled(); bool NS_SVGNewGetBBoxEnabled(); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index ebb5a8e8b99..af2c7c316f7 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -2291,6 +2291,9 @@ pref("dom.ipc.plugins.unloadTimeoutSecs", 30); pref("dom.ipc.processCount", 1); +// Enable caching of Moz2D Path objects for SVG geometry elements +pref("svg.path-caching.enabled", true); + // Enable the use of display-lists for SVG hit-testing and painting. pref("svg.display-lists.hit-testing.enabled", true); pref("svg.display-lists.painting.enabled", true);