зеркало из https://github.com/mozilla/gecko-dev.git
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
This commit is contained in:
Родитель
7ee2eed504
Коммит
2c5b4e5e64
|
@ -180,7 +180,6 @@ DOMSVGPathSeg::IndexIsValid()
|
|||
} \
|
||||
AutoChangePathSegNotifier notifier(this); \
|
||||
InternalItem()[1+index] = float(a##propName); \
|
||||
InvalidateCachedList(); \
|
||||
} else { \
|
||||
mArgs[index] = float(a##propName); \
|
||||
} \
|
||||
|
|
|
@ -209,10 +209,6 @@ protected:
|
|||
*/
|
||||
float* InternalItem();
|
||||
|
||||
void InvalidateCachedList() {
|
||||
mList->InternalList().mCachedPath = nullptr;
|
||||
}
|
||||
|
||||
virtual float* PtrToMemberArgs() = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Path>
|
||||
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> drawTarget =
|
||||
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
|
||||
RefPtr<PathBuilder> builder =
|
||||
drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
|
||||
mCachedPath = BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 0);
|
||||
}
|
||||
|
||||
return mCachedPath;
|
||||
RefPtr<DrawTarget> drawTarget =
|
||||
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
|
||||
RefPtr<PathBuilder> builder =
|
||||
drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
|
||||
return BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 0);
|
||||
}
|
||||
|
||||
static double
|
||||
|
|
|
@ -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<Path> ToPathForLengthOrPositionMeasuring() const;
|
||||
TemporaryRef<Path> BuildPathForMeasuring() const;
|
||||
|
||||
TemporaryRef<Path> 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<float> mData;
|
||||
mutable RefPtr<gfx::Path> mCachedPath;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -70,14 +70,14 @@ SVGPathElement::PathLength()
|
|||
float
|
||||
SVGPathElement::GetTotalLength()
|
||||
{
|
||||
RefPtr<Path> flat = GetPathForLengthOrPositionMeasuring();
|
||||
RefPtr<Path> flat = GetOrBuildPathForMeasuring();
|
||||
return flat ? flat->ComputeLength() : 0.f;
|
||||
}
|
||||
|
||||
already_AddRefed<nsISVGPoint>
|
||||
SVGPathElement::GetPointAtLength(float distance, ErrorResult& rv)
|
||||
{
|
||||
RefPtr<Path> path = GetPathForLengthOrPositionMeasuring();
|
||||
RefPtr<Path> path = GetOrBuildPathForMeasuring();
|
||||
if (!path) {
|
||||
rv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
|
@ -305,9 +305,9 @@ SVGPathElement::IsAttributeMapped(const nsIAtom* name) const
|
|||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
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> path = GetPathForLengthOrPositionMeasuring();
|
||||
RefPtr<Path> path = GetOrBuildPathForMeasuring();
|
||||
if (!path) {
|
||||
// The path is empty or invalid so its length must be zero and
|
||||
// we know that 0 / authorsPathLengthEstimate = 0.
|
||||
|
|
|
@ -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<Path>
|
||||
GetPathForLengthOrPositionMeasuring() MOZ_OVERRIDE;
|
||||
virtual TemporaryRef<Path> GetOrBuildPathForMeasuring() MOZ_OVERRIDE;
|
||||
|
||||
// nsIContent interface
|
||||
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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<mozilla::dom
|
|||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGPathGeometryElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
|
||||
const nsAttrValue* aValue, bool aNotify)
|
||||
{
|
||||
if (mCachedPath &&
|
||||
aNamespaceID == kNameSpaceID_None &&
|
||||
AttributeDefinesGeometry(aName)) {
|
||||
mCachedPath = nullptr;
|
||||
}
|
||||
return nsSVGPathGeometryElementBase::AfterSetAttr(aNamespaceID, aName,
|
||||
aValue, aNotify);
|
||||
}
|
||||
|
||||
bool
|
||||
nsSVGPathGeometryElement::AttributeDefinesGeometry(const nsIAtom *aName)
|
||||
{
|
||||
|
@ -61,7 +75,30 @@ nsSVGPathGeometryElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
|
|||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
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<PathBuilder> builder = aDrawTarget.CreatePathBuilder(aFillRule);
|
||||
RefPtr<Path> path = BuildPath(builder);
|
||||
if (cacheable && NS_SVGPathCachingEnabled()) {
|
||||
mCachedPath = path;
|
||||
}
|
||||
return path.forget();
|
||||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
nsSVGPathGeometryElement::GetOrBuildPathForMeasuring()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -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<mozilla::dom::NodeInfo>& 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<Path> 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<Path> BuildPath(PathBuilder* aBuilder = nullptr) = 0;
|
||||
virtual mozilla::TemporaryRef<Path> BuildPath(PathBuilder* aBuilder) = 0;
|
||||
|
||||
virtual mozilla::TemporaryRef<Path> 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<Path> GetOrBuildPathForMeasuring();
|
||||
|
||||
/**
|
||||
* Returns the current computed value of the CSS property 'fill-rule' for
|
||||
* this element.
|
||||
*/
|
||||
FillRule GetFillRule();
|
||||
|
||||
protected:
|
||||
mutable mozilla::RefPtr<Path> mCachedPath;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Reference for dynamic changes to fill-rule</title>
|
||||
<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=932762 -->
|
||||
|
||||
<!-- for p1 -->
|
||||
<rect x="50" y="100" width="150" height="50"/>
|
||||
<rect x="100" y="50" width="50" height="150"/>
|
||||
|
||||
<!-- for p2 -->
|
||||
<rect x="250" y="100" width="50" height="50"/>
|
||||
<rect x="350" y="100" width="50" height="50"/>
|
||||
<rect x="300" y="50" width="50" height="50"/>
|
||||
<rect x="300" y="150" width="50" height="50"/>
|
||||
|
||||
</svg>
|
||||
|
После Ширина: | Высота: | Размер: 641 B |
|
@ -0,0 +1,25 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
|
||||
<title>Testcase for dynamic changes to fill-rule</title>
|
||||
<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=932762 -->
|
||||
<script>
|
||||
|
||||
function doTest() {
|
||||
document.getElementById("p1").removeAttribute("style");
|
||||
document.getElementById("p2").setAttribute("style", "fill-rule: evenodd;");
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
window.addEventListener("MozReftestInvalidate", doTest, false);
|
||||
window.setTimeout(doTest, 4000); // fallback for running outside reftest
|
||||
|
||||
</script>
|
||||
<path id="p1" style="fill-rule: evenodd;"
|
||||
d="M100,50 l0,150 50,0 0,-100 -100,0 0,50 150,0 0,-50 -50,0 0,-50 z"/>
|
||||
<path id="p2"
|
||||
d="M300,50 l0,150 50,0 0,-100 -100,0 0,50 150,0 0,-50 -50,0 0,-50 z"/>
|
||||
</svg>
|
||||
|
После Ширина: | Высота: | Размер: 921 B |
|
@ -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
|
||||
|
|
|
@ -4805,7 +4805,7 @@ SVGTextFrame::GetTextPath(nsIFrame* aTextPathFrame)
|
|||
nsSVGPathGeometryElement *element =
|
||||
static_cast<nsSVGPathGeometryElement*>(pathFrame->GetContent());
|
||||
|
||||
RefPtr<Path> path = element->GetPathForLengthOrPositionMeasuring();
|
||||
RefPtr<Path> path = element->GetOrBuildPathForMeasuring();
|
||||
if (!path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -66,10 +66,8 @@ nsSVGClipPathFrame::ApplyClipOrPaintClipMask(nsRenderingContext* aContext,
|
|||
gfx->CurrentMatrix().PreMultiply(toChildsUserSpace).NudgeToIntegers();
|
||||
if (!newMatrix.IsSingular()) {
|
||||
gfx->SetMatrix(newMatrix);
|
||||
RefPtr<PathBuilder> builder =
|
||||
gfx->GetDrawTarget()->CreatePathBuilder(
|
||||
nsSVGUtils::ToFillRule(pathFrame->StyleSVG()->mClipRule));
|
||||
clipPath = pathElement->BuildPath(builder);
|
||||
clipPath = pathElement->GetOrBuildPath(*gfx->GetDrawTarget(),
|
||||
nsSVGUtils::ToFillRule(pathFrame->StyleSVG()->mClipRule));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,6 +158,25 @@ nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
|
|||
// nsDisplayOpacity display list item, so DLBI won't invalidate for us.
|
||||
InvalidateFrame();
|
||||
}
|
||||
|
||||
nsSVGPathGeometryElement* element =
|
||||
static_cast<nsSVGPathGeometryElement*>(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> drawTarget =
|
||||
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
|
||||
RefPtr<PathBuilder> builder =
|
||||
drawTarget->CreatePathBuilder(fillRule);
|
||||
RefPtr<Path> path = content->BuildPath(builder);
|
||||
RefPtr<Path> 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<nsSVGPathGeometryElement*>(mContent)->GeometryDependsOnCoordCtx() ||
|
||||
StyleSVG()->mStrokeWidth.HasPercent()) {
|
||||
static_cast<nsSVGPathGeometryElement*>(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<PathBuilder> builder = tmpDT->CreatePathBuilder(fillRule);
|
||||
RefPtr<Path> pathInUserSpace = element->BuildPath(builder);
|
||||
RefPtr<Path> 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<PathBuilder> 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<PathBuilder> builder = drawTarget->CreatePathBuilder(fillRule);
|
||||
if (!builder) {
|
||||
return;
|
||||
}
|
||||
nsSVGPathGeometryElement* element =
|
||||
static_cast<nsSVGPathGeometryElement*>(mContent);
|
||||
|
||||
RefPtr<Path> path =
|
||||
static_cast<nsSVGPathGeometryElement*>(mContent)->BuildPath(builder);
|
||||
RefPtr<Path> 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<PathBuilder> builder =
|
||||
path->TransformedCopyToBuilder(ToMatrix(userToOuterSVG), fillRule);
|
||||
path = builder->Finish();
|
||||
}
|
||||
GeneralPattern strokePattern;
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче