Bug 1074161 - Avoid creating a Moz2D Path object to draw SVG <rect>, <image> and <line> elements. r=mattwoodrow

This commit is contained in:
Jonathan Watt 2014-10-22 12:29:05 +01:00
parent f7a1b85cdf
commit 2a309ca31b
6 changed files with 137 additions and 12 deletions

View File

@ -106,6 +106,14 @@ SVGLineElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks) {
aMarks->AppendElement(nsSVGMark(x2, y2, angle, nsSVGMark::eEnd));
}
void
SVGLineElement::GetAsSimplePath(SimplePath* aSimplePath)
{
float x1, y1, x2, y2;
GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
aSimplePath->SetLine(x1, y1, x2, y2);
}
TemporaryRef<Path>
SVGLineElement::BuildPath(PathBuilder* aBuilder)
{

View File

@ -32,6 +32,7 @@ public:
// nsSVGPathGeometryElement methods:
virtual bool IsMarkable() MOZ_OVERRIDE { return true; }
virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) MOZ_OVERRIDE;
virtual void GetAsSimplePath(SimplePath* aSimplePath) MOZ_OVERRIDE;
virtual TemporaryRef<Path> BuildPath(PathBuilder* aBuilder) MOZ_OVERRIDE;
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const;

View File

@ -108,6 +108,28 @@ SVGRectElement::GetLengthInfo()
//----------------------------------------------------------------------
// nsSVGPathGeometryElement methods
void
SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath)
{
float x, y, width, height, rx, ry;
GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
if (width <= 0 || height <= 0) {
aSimplePath->Reset();
return;
}
rx = std::max(rx, 0.0f);
ry = std::max(ry, 0.0f);
if (rx != 0 || ry != 0) {
aSimplePath->Reset();
return;
}
aSimplePath->SetRect(x, y, width, height);
}
TemporaryRef<Path>
SVGRectElement::BuildPath(PathBuilder* aBuilder)
{

View File

@ -30,6 +30,7 @@ public:
virtual bool HasValidDimensions() const MOZ_OVERRIDE;
// nsSVGPathGeometryElement methods:
virtual void GetAsSimplePath(SimplePath* aSimplePath) MOZ_OVERRIDE;
virtual TemporaryRef<Path> BuildPath(PathBuilder* aBuilder = nullptr) MOZ_OVERRIDE;
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;

View File

@ -35,7 +35,9 @@ protected:
typedef mozilla::gfx::FillRule FillRule;
typedef mozilla::gfx::Float Float;
typedef mozilla::gfx::Path Path;
typedef mozilla::gfx::Point Point;
typedef mozilla::gfx::PathBuilder PathBuilder;
typedef mozilla::gfx::Rect Rect;
public:
explicit nsSVGPathGeometryElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
@ -67,6 +69,65 @@ public:
virtual bool IsMarkable();
virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks);
/**
* For use with GetAsSimplePath.
*/
class SimplePath
{
public:
SimplePath()
: mType(NONE)
{}
bool IsPath() const {
return mType != NONE;
}
void SetRect(Float x, Float y, Float width, Float height) {
mX = x; mY = y, mWidthOrX2 = width, mHeightOrY2 = height;
mType = RECT;
}
Rect AsRect() const {
MOZ_ASSERT(mType == RECT);
return Rect(mX, mY, mWidthOrX2, mHeightOrY2);
}
bool IsRect() const {
return mType == RECT;
}
void SetLine(Float x1, Float y1, Float x2, Float y2) {
mX = x1, mY = y1, mWidthOrX2 = x2, mHeightOrY2 = y2;
mType = LINE;
}
Point Point1() const {
MOZ_ASSERT(mType == LINE);
return Point(mX, mY);
}
Point Point2() const {
MOZ_ASSERT(mType == LINE);
return Point(mWidthOrX2, mHeightOrY2);
}
bool IsLine() const {
return mType == LINE;
}
void Reset() {
mType = NONE;
}
private:
enum Type {
NONE, RECT, LINE
};
Float mX, mY, mWidthOrX2, mHeightOrY2;
Type mType;
};
/**
* For some platforms there is significant overhead to creating and painting
* a Moz2D Path object. For Rects and lines it is better to get the path data
* using this method and then use the optimized DrawTarget methods for
* filling/stroking rects and lines.
*/
virtual void GetAsSimplePath(SimplePath* aSimplePath) {
aSimplePath->Reset();
}
/**
* 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

View File

@ -685,11 +685,6 @@ nsSVGPathGeometryFrame::Render(gfxContext* aContext,
nsSVGPathGeometryElement* element =
static_cast<nsSVGPathGeometryElement*>(mContent);
RefPtr<Path> path = element->GetOrBuildPath(*drawTarget, fillRule);
if (!path) {
return;
}
AntialiasMode aaMode =
(StyleSVG()->mShapeRendering == NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED ||
StyleSVG()->mShapeRendering == NS_STYLE_SHAPE_RENDERING_CRISPEDGES) ?
@ -702,12 +697,28 @@ nsSVGPathGeometryFrame::Render(gfxContext* aContext,
aContext->SetMatrix(aNewTransform);
if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
ColorPattern white(ToDeviceColor(Color(1.0f, 1.0f, 1.0f, 1.0f)));
drawTarget->Fill(path, white,
DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
// We don't complicate this code with GetAsSimplePath since the cost of
// masking will dwarf Path creation overhead anyway.
RefPtr<Path> path = element->GetOrBuildPath(*drawTarget, fillRule);
if (path) {
ColorPattern white(ToDeviceColor(Color(1.0f, 1.0f, 1.0f, 1.0f)));
drawTarget->Fill(path, white,
DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
}
return;
}
nsSVGPathGeometryElement::SimplePath simplePath;
RefPtr<Path> path;
element->GetAsSimplePath(&simplePath);
if (!simplePath.IsPath()) {
path = element->GetOrBuildPath(*drawTarget, fillRule);
if (!path) {
return;
}
}
gfxTextContextPaint *contextPaint =
(gfxTextContextPaint*)drawTarget->
GetUserData(&gfxTextContextPaint::sUserDataKey);
@ -716,8 +727,12 @@ nsSVGPathGeometryFrame::Render(gfxContext* aContext,
GeneralPattern fillPattern;
nsSVGUtils::MakeFillPatternFor(this, aContext, &fillPattern, contextPaint);
if (fillPattern.GetPattern()) {
drawTarget->Fill(path, fillPattern,
DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
DrawOptions drawOptions(1.0f, CompositionOp::OP_OVER, aaMode);
if (simplePath.IsRect()) {
drawTarget->FillRect(simplePath.AsRect(), fillPattern, drawOptions);
} else if (path) {
drawTarget->Fill(path, fillPattern, drawOptions);
}
}
}
@ -726,6 +741,15 @@ nsSVGPathGeometryFrame::Render(gfxContext* aContext,
// Account for vector-effect:non-scaling-stroke:
gfxMatrix userToOuterSVG;
if (nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
// A simple Rect can't be transformed with rotate/skew, so let's switch
// to using a real path:
if (!path) {
path = element->GetOrBuildPath(*drawTarget, fillRule);
if (!path) {
return;
}
simplePath.Reset();
}
// We need to transform the path back into the appropriate ancestor
// coordinate system, and paint it it that coordinate system, in order
// for non-scaled stroke to paint correctly.
@ -747,8 +771,16 @@ nsSVGPathGeometryFrame::Render(gfxContext* aContext,
if (strokeOptions.mLineWidth <= 0) {
return;
}
drawTarget->Stroke(path, strokePattern, strokeOptions,
DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
DrawOptions drawOptions(1.0f, CompositionOp::OP_OVER, aaMode);
if (simplePath.IsRect()) {
drawTarget->StrokeRect(simplePath.AsRect(), strokePattern,
strokeOptions, drawOptions);
} else if (simplePath.IsLine()) {
drawTarget->StrokeLine(simplePath.Point1(), simplePath.Point2(),
strokePattern, strokeOptions, drawOptions);
} else {
drawTarget->Stroke(path, strokePattern, strokeOptions, drawOptions);
}
}
}
}