mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 930468 - Add a Moz2D version of SVGPathData::ConstructPath. r=heycam, r=Bas
This commit is contained in:
parent
fc5983466d
commit
c0517ff200
@ -7,7 +7,10 @@
|
|||||||
|
|
||||||
#include "gfx2DGlue.h"
|
#include "gfx2DGlue.h"
|
||||||
#include "gfxPlatform.h"
|
#include "gfxPlatform.h"
|
||||||
|
#include "mozilla/gfx/2D.h"
|
||||||
|
#include "mozilla/gfx/Types.h"
|
||||||
#include "mozilla/gfx/Point.h"
|
#include "mozilla/gfx/Point.h"
|
||||||
|
#include "mozilla/RefPtr.h"
|
||||||
#include "nsError.h"
|
#include "nsError.h"
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsSVGPathDataParser.h"
|
#include "nsSVGPathDataParser.h"
|
||||||
@ -241,6 +244,35 @@ ApproximateZeroLengthSubpathSquareCaps(const gfxPoint &aPoint, gfxContext *aCtx)
|
|||||||
aCtx->MoveTo(aPoint);
|
aCtx->MoveTo(aPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ApproximateZeroLengthSubpathSquareCaps(const Point& aPoint,
|
||||||
|
DrawTarget* aDT,
|
||||||
|
PathBuilder* aPB)
|
||||||
|
{
|
||||||
|
// Cairo's fixed point fractional part is 8 bits wide, so its device space
|
||||||
|
// coordinate granularity is 1/256 pixels. However, to prevent user space
|
||||||
|
// |aPoint| and |aPoint + tinyAdvance| being rounded to the same device
|
||||||
|
// coordinates, we double this for |tinyAdvance|:
|
||||||
|
|
||||||
|
Matrix currentTransform = aDT->GetTransform();
|
||||||
|
currentTransform.Invert();
|
||||||
|
Size tinyAdvance = currentTransform * Size(2.0/256.0, 0.0);
|
||||||
|
|
||||||
|
aPB->MoveTo(aPoint);
|
||||||
|
aPB->LineTo(aPoint + Point(tinyAdvance.width, tinyAdvance.height));
|
||||||
|
aPB->MoveTo(aPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT \
|
||||||
|
do { \
|
||||||
|
if (capsAreSquare && !subpathHasLength && subpathContainsNonArc && \
|
||||||
|
SVGPathSegUtils::IsValidType(prevSegType) && \
|
||||||
|
(!IsMoveto(prevSegType) || \
|
||||||
|
segType == PATHSEG_CLOSEPATH)) { \
|
||||||
|
ApproximateZeroLengthSubpathSquareCaps(segStart, aDT, builder); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
#define MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS \
|
#define MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS \
|
||||||
do { \
|
do { \
|
||||||
if (capsAreSquare && !subpathHasLength && subpathContainsNonArc && \
|
if (capsAreSquare && !subpathHasLength && subpathContainsNonArc && \
|
||||||
@ -251,6 +283,257 @@ ApproximateZeroLengthSubpathSquareCaps(const gfxPoint &aPoint, gfxContext *aCtx)
|
|||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
TemporaryRef<Path>
|
||||||
|
SVGPathData::ConstructPath(DrawTarget *aDT,
|
||||||
|
FillRule aFillRule,
|
||||||
|
CapStyle aCapStyle) const
|
||||||
|
{
|
||||||
|
if (mData.IsEmpty() || !IsMoveto(SVGPathSegUtils::DecodeType(mData[0]))) {
|
||||||
|
return nullptr; // paths without an initial moveto are invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<PathBuilder> builder = aDT->CreatePathBuilder(aFillRule);
|
||||||
|
|
||||||
|
bool capsAreSquare = aCapStyle == CAP_SQUARE;
|
||||||
|
bool subpathHasLength = false; // visual length
|
||||||
|
bool subpathContainsNonArc = false;
|
||||||
|
|
||||||
|
uint32_t segType = PATHSEG_UNKNOWN;
|
||||||
|
uint32_t prevSegType = PATHSEG_UNKNOWN;
|
||||||
|
Point pathStart(0.0, 0.0); // start point of [sub]path
|
||||||
|
Point segStart(0.0, 0.0);
|
||||||
|
Point segEnd;
|
||||||
|
Point cp1, cp2; // previous bezier's control points
|
||||||
|
Point tcp1, tcp2; // temporaries
|
||||||
|
|
||||||
|
// Regarding cp1 and cp2: If the previous segment was a cubic bezier curve,
|
||||||
|
// then cp2 is its second control point. If the previous segment was a
|
||||||
|
// quadratic curve, then cp1 is its (only) control point.
|
||||||
|
|
||||||
|
uint32_t i = 0;
|
||||||
|
while (i < mData.Length()) {
|
||||||
|
segType = SVGPathSegUtils::DecodeType(mData[i++]);
|
||||||
|
uint32_t argCount = SVGPathSegUtils::ArgCountForType(segType);
|
||||||
|
|
||||||
|
switch (segType)
|
||||||
|
{
|
||||||
|
case PATHSEG_CLOSEPATH:
|
||||||
|
// set this early to allow drawing of square caps for "M{x},{y} Z":
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
|
||||||
|
segEnd = pathStart;
|
||||||
|
builder->Close();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_MOVETO_ABS:
|
||||||
|
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
|
||||||
|
pathStart = segEnd = Point(mData[i], mData[i+1]);
|
||||||
|
builder->MoveTo(segEnd);
|
||||||
|
subpathHasLength = false;
|
||||||
|
subpathContainsNonArc = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_MOVETO_REL:
|
||||||
|
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
|
||||||
|
pathStart = segEnd = segStart + Point(mData[i], mData[i+1]);
|
||||||
|
builder->MoveTo(segEnd);
|
||||||
|
subpathHasLength = false;
|
||||||
|
subpathContainsNonArc = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_LINETO_ABS:
|
||||||
|
segEnd = Point(mData[i], mData[i+1]);
|
||||||
|
builder->LineTo(segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_LINETO_REL:
|
||||||
|
segEnd = segStart + Point(mData[i], mData[i+1]);
|
||||||
|
builder->LineTo(segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_CURVETO_CUBIC_ABS:
|
||||||
|
cp1 = Point(mData[i], mData[i+1]);
|
||||||
|
cp2 = Point(mData[i+2], mData[i+3]);
|
||||||
|
segEnd = Point(mData[i+4], mData[i+5]);
|
||||||
|
builder->BezierTo(cp1, cp2, segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_CURVETO_CUBIC_REL:
|
||||||
|
cp1 = segStart + Point(mData[i], mData[i+1]);
|
||||||
|
cp2 = segStart + Point(mData[i+2], mData[i+3]);
|
||||||
|
segEnd = segStart + Point(mData[i+4], mData[i+5]);
|
||||||
|
builder->BezierTo(cp1, cp2, segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_CURVETO_QUADRATIC_ABS:
|
||||||
|
cp1 = Point(mData[i], mData[i+1]);
|
||||||
|
// Convert quadratic curve to cubic curve:
|
||||||
|
tcp1 = segStart + (cp1 - segStart) * 2 / 3;
|
||||||
|
segEnd = Point(mData[i+2], mData[i+3]); // set before setting tcp2!
|
||||||
|
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||||
|
builder->BezierTo(tcp1, tcp2, segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart || segEnd != cp1);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_CURVETO_QUADRATIC_REL:
|
||||||
|
cp1 = segStart + Point(mData[i], mData[i+1]);
|
||||||
|
// Convert quadratic curve to cubic curve:
|
||||||
|
tcp1 = segStart + (cp1 - segStart) * 2 / 3;
|
||||||
|
segEnd = segStart + Point(mData[i+2], mData[i+3]); // set before setting tcp2!
|
||||||
|
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||||
|
builder->BezierTo(tcp1, tcp2, segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart || segEnd != cp1);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_ARC_ABS:
|
||||||
|
case PATHSEG_ARC_REL:
|
||||||
|
{
|
||||||
|
Point radii(mData[i], mData[i+1]);
|
||||||
|
segEnd = Point(mData[i+5], mData[i+6]);
|
||||||
|
if (segType == PATHSEG_ARC_REL) {
|
||||||
|
segEnd += segStart;
|
||||||
|
}
|
||||||
|
if (segEnd != segStart) {
|
||||||
|
if (radii.x == 0.0f || radii.y == 0.0f) {
|
||||||
|
builder->LineTo(segEnd);
|
||||||
|
} else {
|
||||||
|
nsSVGArcConverter converter(segStart, segEnd, radii, mData[i+2],
|
||||||
|
mData[i+3] != 0, mData[i+4] != 0);
|
||||||
|
while (converter.GetNextSegment(&cp1, &cp2, &segEnd)) {
|
||||||
|
builder->BezierTo(cp1, cp2, segEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PATHSEG_LINETO_HORIZONTAL_ABS:
|
||||||
|
segEnd = Point(mData[i], segStart.y);
|
||||||
|
builder->LineTo(segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_LINETO_HORIZONTAL_REL:
|
||||||
|
segEnd = segStart + Point(mData[i], 0.0f);
|
||||||
|
builder->LineTo(segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_LINETO_VERTICAL_ABS:
|
||||||
|
segEnd = Point(segStart.x, mData[i]);
|
||||||
|
builder->LineTo(segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_LINETO_VERTICAL_REL:
|
||||||
|
segEnd = segStart + Point(0.0f, mData[i]);
|
||||||
|
builder->LineTo(segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
||||||
|
cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - cp2 : segStart;
|
||||||
|
cp2 = Point(mData[i], mData[i+1]);
|
||||||
|
segEnd = Point(mData[i+2], mData[i+3]);
|
||||||
|
builder->BezierTo(cp1, cp2, segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
|
||||||
|
cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - cp2 : segStart;
|
||||||
|
cp2 = segStart + Point(mData[i], mData[i+1]);
|
||||||
|
segEnd = segStart + Point(mData[i+2], mData[i+3]);
|
||||||
|
builder->BezierTo(cp1, cp2, segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
|
||||||
|
cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - cp1 : segStart;
|
||||||
|
// Convert quadratic curve to cubic curve:
|
||||||
|
tcp1 = segStart + (cp1 - segStart) * 2 / 3;
|
||||||
|
segEnd = Point(mData[i], mData[i+1]); // set before setting tcp2!
|
||||||
|
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||||
|
builder->BezierTo(tcp1, tcp2, segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart || segEnd != cp1);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
|
||||||
|
cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - cp1 : segStart;
|
||||||
|
// Convert quadratic curve to cubic curve:
|
||||||
|
tcp1 = segStart + (cp1 - segStart) * 2 / 3;
|
||||||
|
segEnd = segStart + Point(mData[i], mData[i+1]); // changed before setting tcp2!
|
||||||
|
tcp2 = cp1 + (segEnd - cp1) / 3;
|
||||||
|
builder->BezierTo(tcp1, tcp2, segEnd);
|
||||||
|
if (!subpathHasLength) {
|
||||||
|
subpathHasLength = (segEnd != segStart || segEnd != cp1);
|
||||||
|
}
|
||||||
|
subpathContainsNonArc = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
NS_NOTREACHED("Bad path segment type");
|
||||||
|
return nullptr; // according to spec we'd use everything up to the bad seg anyway
|
||||||
|
}
|
||||||
|
i += argCount;
|
||||||
|
prevSegType = segType;
|
||||||
|
segStart = segEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
|
||||||
|
NS_ABORT_IF_FALSE(prevSegType == segType,
|
||||||
|
"prevSegType should be left at the final segType");
|
||||||
|
|
||||||
|
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
|
||||||
|
|
||||||
|
return builder->Finish();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SVGPathData::ConstructPath(gfxContext *aCtx) const
|
SVGPathData::ConstructPath(gfxContext *aCtx) const
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#include "nsIContent.h"
|
#include "nsIContent.h"
|
||||||
#include "nsINode.h"
|
#include "nsINode.h"
|
||||||
#include "nsIWeakReferenceUtils.h"
|
#include "nsIWeakReferenceUtils.h"
|
||||||
|
#include "mozilla/gfx/2D.h"
|
||||||
|
#include "mozilla/RefPtr.h"
|
||||||
#include "nsSVGElement.h"
|
#include "nsSVGElement.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
|
|
||||||
@ -80,6 +82,11 @@ class SVGPathData
|
|||||||
// nsSVGPathDataParserToInternal will not keep wrappers in sync, so consumers
|
// nsSVGPathDataParserToInternal will not keep wrappers in sync, so consumers
|
||||||
// are responsible for that!
|
// are responsible for that!
|
||||||
|
|
||||||
|
typedef gfx::DrawTarget DrawTarget;
|
||||||
|
typedef gfx::Path Path;
|
||||||
|
typedef gfx::FillRule FillRule;
|
||||||
|
typedef gfx::CapStyle CapStyle;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef const float* const_iterator;
|
typedef const float* const_iterator;
|
||||||
|
|
||||||
@ -154,6 +161,9 @@ public:
|
|||||||
ToPath(const gfxMatrix& aMatrix) const;
|
ToPath(const gfxMatrix& aMatrix) const;
|
||||||
|
|
||||||
void ConstructPath(gfxContext *aCtx) const;
|
void ConstructPath(gfxContext *aCtx) const;
|
||||||
|
TemporaryRef<Path> ConstructPath(DrawTarget* aDT,
|
||||||
|
FillRule aFillRule,
|
||||||
|
CapStyle aCapStyle) const;
|
||||||
|
|
||||||
const_iterator begin() const { return mData.Elements(); }
|
const_iterator begin() const { return mData.Elements(); }
|
||||||
const_iterator end() const { return mData.Elements() + mData.Length(); }
|
const_iterator end() const { return mData.Elements() + mData.Length(); }
|
||||||
|
Loading…
Reference in New Issue
Block a user