Bug 1823463 - Make PathCommand as the specialization of GenericShapeCommand. r=emilio

Also, we don't have Unkonwn type, so we have to do some minor
refactoring in BuildPath(), and templatize this function so we can use
it for both shape() and path().

This patch doesn't change the behavior.

Note that we instantiate BuildPath() with CSSFloat for now. Once we
instantiate it for StyleAngle and LengthPercentage (i.e. shape()), we
have to tweak this function more. Let's do that in the next patch.

Differential Revision: https://phabricator.services.mozilla.com/D202883
This commit is contained in:
Boris Chiou 2024-03-18 21:20:29 +00:00
parent b9d9282f7f
commit 6605350dc6
11 changed files with 440 additions and 658 deletions

View File

@ -31,36 +31,14 @@ static inline bool IsMoveto(uint16_t aSegType) {
return aSegType == PATHSEG_MOVETO_ABS || aSegType == PATHSEG_MOVETO_REL;
}
static inline bool IsMoveto(StylePathCommand::Tag aSegType) {
return aSegType == StylePathCommand::Tag::MoveTo;
}
static inline bool IsValidType(uint16_t aSegType) {
return SVGPathSegUtils::IsValidType(aSegType);
}
static inline bool IsValidType(StylePathCommand::Tag aSegType) {
return aSegType != StylePathCommand::Tag::Unknown;
}
static inline bool IsClosePath(uint16_t aSegType) {
return aSegType == PATHSEG_CLOSEPATH;
}
static inline bool IsClosePath(StylePathCommand::Tag aSegType) {
return aSegType == StylePathCommand::Tag::ClosePath;
}
static inline bool IsCubicType(StylePathCommand::Tag aType) {
return aType == StylePathCommand::Tag::CurveTo ||
aType == StylePathCommand::Tag::SmoothCurveTo;
}
static inline bool IsQuadraticType(StylePathCommand::Tag aType) {
return aType == StylePathCommand::Tag::QuadBezierCurveTo ||
aType == StylePathCommand::Tag::SmoothQuadBezierCurveTo;
}
nsresult SVGPathData::CopyFrom(const SVGPathData& rhs) {
if (!mData.Assign(rhs.mData, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
@ -200,13 +178,13 @@ bool SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(
}
// We skip all moveto commands except for the initial moveto.
if (!cmd.IsMoveTo() || !firstMoveToIsChecked) {
if (!cmd.IsMove() || !firstMoveToIsChecked) {
if (!aOutput->AppendElement(state.length, fallible)) {
return false;
}
}
if (cmd.IsMoveTo() && !firstMoveToIsChecked) {
if (cmd.IsMove() && !firstMoveToIsChecked) {
firstMoveToIsChecked = true;
}
}
@ -550,6 +528,8 @@ already_AddRefed<Path> SVGPathData::BuildPath(PathBuilder* aBuilder,
return aBuilder->Finish();
}
#undef MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT
already_AddRefed<Path> 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
@ -576,15 +556,14 @@ already_AddRefed<Path> SVGPathData::BuildPathForMeasuring(
return BuildPath(aPath, builder, StyleStrokeLinecap::Butt, 0);
}
// We could simplify this function because this is only used by CSS motion path
// and clip-path, which don't render the SVG Path. i.e. The returned path is
// used as a reference.
/* static */
already_AddRefed<Path> SVGPathData::BuildPath(
Span<const StylePathCommand> aPath, PathBuilder* aBuilder,
StyleStrokeLinecap aStrokeLineCap, Float aStrokeWidth, const Point& aOffset,
float aZoomFactor) {
if (aPath.IsEmpty() || !aPath[0].IsMoveTo()) {
template <typename Angle, typename LP>
static already_AddRefed<Path> BuildPathInternal(
Span<const StyleGenericShapeCommand<Angle, LP>> aPath,
PathBuilder* aBuilder, StyleStrokeLinecap aStrokeLineCap,
Float aStrokeWidth, const Point& aOffset, float aZoomFactor) {
using Command = StyleGenericShapeCommand<Angle, LP>;
if (aPath.IsEmpty() || !aPath[0].IsMove()) {
return nullptr; // paths without an initial moveto are invalid
}
@ -592,14 +571,24 @@ already_AddRefed<Path> SVGPathData::BuildPath(
bool subpathHasLength = false; // visual length
bool subpathContainsNonMoveTo = false;
StylePathCommand::Tag segType = StylePathCommand::Tag::Unknown;
StylePathCommand::Tag prevSegType = StylePathCommand::Tag::Unknown;
const Command* seg = nullptr;
const Command* prevSeg = nullptr;
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
auto maybeApproximateZeroLengthSubpathSquareCaps =
[&](const Command* aPrevSeg, const Command* aSeg) {
if (!subpathHasLength && hasLineCaps && aStrokeWidth > 0 &&
subpathContainsNonMoveTo && aPrevSeg && aSeg &&
(!aPrevSeg->IsMove() || aSeg->IsClose())) {
ApproximateZeroLengthSubpathSquareCaps(aBuilder, segStart,
aStrokeWidth);
}
};
auto scale = [aOffset, aZoomFactor](const Point& p) {
return Point(p.x * aZoomFactor, p.y * aZoomFactor) + aOffset;
};
@ -608,41 +597,39 @@ already_AddRefed<Path> SVGPathData::BuildPath(
// then cp2 is its second control point. If the previous segment was a
// quadratic curve, then cp1 is its (only) control point.
for (const StylePathCommand& cmd : aPath) {
segType = cmd.tag;
switch (segType) {
case StylePathCommand::Tag::ClosePath:
for (const auto& cmd : aPath) {
seg = &cmd;
switch (cmd.tag) {
case Command::Tag::Close:
// set this early to allow drawing of square caps for "M{x},{y} Z":
subpathContainsNonMoveTo = true;
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
maybeApproximateZeroLengthSubpathSquareCaps(prevSeg, seg);
segEnd = pathStart;
aBuilder->Close();
break;
case StylePathCommand::Tag::MoveTo: {
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
const Point& p = cmd.move_to.point.ConvertsToGfxPoint();
pathStart = segEnd =
cmd.move_to.absolute == StyleIsAbsolute::Yes ? p : segStart + p;
case Command::Tag::Move: {
maybeApproximateZeroLengthSubpathSquareCaps(prevSeg, seg);
const Point& p = cmd.move.point.ToGfxPoint();
pathStart = segEnd = cmd.move.by_to == StyleByTo::To ? p : segStart + p;
aBuilder->MoveTo(scale(segEnd));
subpathHasLength = false;
break;
}
case StylePathCommand::Tag::LineTo: {
const Point& p = cmd.line_to.point.ConvertsToGfxPoint();
segEnd =
cmd.line_to.absolute == StyleIsAbsolute::Yes ? p : segStart + p;
case Command::Tag::Line: {
const Point& p = cmd.line.point.ToGfxPoint();
segEnd = cmd.line.by_to == StyleByTo::To ? p : segStart + p;
if (segEnd != segStart) {
subpathHasLength = true;
aBuilder->LineTo(scale(segEnd));
}
break;
}
case StylePathCommand::Tag::CurveTo:
cp1 = cmd.curve_to.control1.ConvertsToGfxPoint();
cp2 = cmd.curve_to.control2.ConvertsToGfxPoint();
segEnd = cmd.curve_to.point.ConvertsToGfxPoint();
case Command::Tag::CubicCurve:
cp1 = cmd.cubic_curve.control1.ToGfxPoint();
cp2 = cmd.cubic_curve.control2.ToGfxPoint();
segEnd = cmd.cubic_curve.point.ToGfxPoint();
if (cmd.curve_to.absolute == StyleIsAbsolute::No) {
if (cmd.cubic_curve.by_to == StyleByTo::By) {
cp1 += segStart;
cp2 += segStart;
segEnd += segStart;
@ -654,11 +641,11 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
case StylePathCommand::Tag::QuadBezierCurveTo:
cp1 = cmd.quad_bezier_curve_to.control1.ConvertsToGfxPoint();
segEnd = cmd.quad_bezier_curve_to.point.ConvertsToGfxPoint();
case Command::Tag::QuadCurve:
cp1 = cmd.quad_curve.control1.ToGfxPoint();
segEnd = cmd.quad_curve.point.ToGfxPoint();
if (cmd.quad_bezier_curve_to.absolute == StyleIsAbsolute::No) {
if (cmd.quad_curve.by_to == StyleByTo::By) {
cp1 += segStart;
segEnd += segStart; // set before setting tcp2!
}
@ -673,11 +660,11 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
case StylePathCommand::Tag::EllipticalArc: {
const auto& arc = cmd.elliptical_arc;
Point radii(arc.rx, arc.ry);
segEnd = arc.point.ConvertsToGfxPoint();
if (arc.absolute == StyleIsAbsolute::No) {
case Command::Tag::Arc: {
const auto& arc = cmd.arc;
const Point& radii = arc.radii.ToGfxPoint();
segEnd = arc.point.ToGfxPoint();
if (arc.by_to == StyleByTo::By) {
segEnd += segStart;
}
if (segEnd != segStart) {
@ -685,8 +672,10 @@ already_AddRefed<Path> SVGPathData::BuildPath(
if (radii.x == 0.0f || radii.y == 0.0f) {
aBuilder->LineTo(scale(segEnd));
} else {
SVGArcConverter converter(segStart, segEnd, radii, arc.angle,
arc.large_arc_flag._0, arc.sweep_flag._0);
const bool arc_is_large = arc.arc_size == StyleArcSize::Large;
const bool arc_is_cw = arc.arc_sweep == StyleArcSweep::Cw;
SVGArcConverter converter(segStart, segEnd, radii, arc.rotate,
arc_is_large, arc_is_cw);
while (converter.GetNextSegment(&cp1, &cp2, &segEnd)) {
aBuilder->BezierTo(scale(cp1), scale(cp2), scale(segEnd));
}
@ -694,11 +683,11 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
}
case StylePathCommand::Tag::HorizontalLineTo:
if (cmd.horizontal_line_to.absolute == StyleIsAbsolute::Yes) {
segEnd = Point(cmd.horizontal_line_to.x, segStart.y);
case Command::Tag::HLine:
if (cmd.h_line.by_to == StyleByTo::To) {
segEnd = Point(cmd.h_line.x, segStart.y);
} else {
segEnd = segStart + Point(cmd.horizontal_line_to.x, 0.0f);
segEnd = segStart + Point(cmd.h_line.x, 0.0f);
}
if (segEnd != segStart) {
@ -707,11 +696,11 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
case StylePathCommand::Tag::VerticalLineTo:
if (cmd.vertical_line_to.absolute == StyleIsAbsolute::Yes) {
segEnd = Point(segStart.x, cmd.vertical_line_to.y);
case Command::Tag::VLine:
if (cmd.v_line.by_to == StyleByTo::To) {
segEnd = Point(segStart.x, cmd.v_line.y);
} else {
segEnd = segStart + Point(0.0f, cmd.vertical_line_to.y);
segEnd = segStart + Point(0.0f, cmd.v_line.y);
}
if (segEnd != segStart) {
@ -720,12 +709,12 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
case StylePathCommand::Tag::SmoothCurveTo:
cp1 = IsCubicType(prevSegType) ? segStart * 2 - cp2 : segStart;
cp2 = cmd.smooth_curve_to.control2.ConvertsToGfxPoint();
segEnd = cmd.smooth_curve_to.point.ConvertsToGfxPoint();
case Command::Tag::SmoothCubic:
cp1 = prevSeg && prevSeg->IsCubicType() ? segStart * 2 - cp2 : segStart;
cp2 = cmd.smooth_cubic.control2.ToGfxPoint();
segEnd = cmd.smooth_cubic.point.ToGfxPoint();
if (cmd.smooth_curve_to.absolute == StyleIsAbsolute::No) {
if (cmd.smooth_cubic.by_to == StyleByTo::By) {
cp2 += segStart;
segEnd += segStart;
}
@ -736,18 +725,15 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
case StylePathCommand::Tag::SmoothQuadBezierCurveTo: {
cp1 = IsQuadraticType(prevSegType) ? segStart * 2 - cp1 : segStart;
case Command::Tag::SmoothQuad: {
cp1 = prevSeg && prevSeg->IsQuadraticType() ? segStart * 2 - cp1
: segStart;
// Convert quadratic curve to cubic curve:
tcp1 = segStart + (cp1 - segStart) * 2 / 3;
const Point& p =
cmd.smooth_quad_bezier_curve_to.point.ConvertsToGfxPoint();
const Point& p = cmd.smooth_quad.point.ToGfxPoint();
// set before setting tcp2!
segEnd =
cmd.smooth_quad_bezier_curve_to.absolute == StyleIsAbsolute::Yes
? p
: segStart + p;
segEnd = cmd.smooth_quad.by_to == StyleByTo::To ? p : segStart + p;
tcp2 = cp1 + (segEnd - cp1) / 3;
if (segEnd != segStart || segEnd != cp1) {
@ -756,24 +742,32 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
}
case StylePathCommand::Tag::Unknown:
MOZ_ASSERT_UNREACHABLE("Unacceptable path segment type");
return nullptr;
}
subpathContainsNonMoveTo = !IsMoveto(segType);
prevSegType = segType;
subpathContainsNonMoveTo = !cmd.IsMove();
prevSeg = seg;
segStart = segEnd;
}
MOZ_ASSERT(prevSegType == segType,
"prevSegType should be left at the final segType");
MOZ_ASSERT(prevSeg == seg, "prevSegType should be left at the final segType");
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
maybeApproximateZeroLengthSubpathSquareCaps(prevSeg, seg);
return aBuilder->Finish();
}
// We could simplify this function because this is only used by CSS motion path
// and clip-path, which don't render the SVG Path. i.e. The returned path is
// used as a reference.
/* static */
already_AddRefed<Path> SVGPathData::BuildPath(
Span<const StylePathCommand> aPath, PathBuilder* aBuilder,
StyleStrokeLinecap aStrokeLineCap, Float aStrokeWidth,
const gfx::Point& aOffset, float aZoomFactor) {
return BuildPathInternal(aPath, aBuilder, aStrokeLineCap, aStrokeWidth,
aOffset, aZoomFactor);
}
static double AngleOfVector(const Point& aVector) {
// C99 says about atan2 "A domain error may occur if both arguments are
// zero" and "On a domain error, the function returns an implementation-
@ -1135,48 +1129,44 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
uint32_t pathStartIndex = 0;
// info on previous segment:
StylePathCommand::Tag prevSegType = StylePathCommand::Tag::Unknown;
const StylePathCommand* prevSeg = nullptr;
Point prevSegEnd(0.0, 0.0);
float prevSegEndAngle = 0.0f;
Point prevCP; // if prev seg was a bezier, this was its last control point
StylePathCommand::Tag segType = StylePathCommand::Tag::Unknown;
for (const StylePathCommand& cmd : aPath) {
segType = cmd.tag;
Point& segStart = prevSegEnd;
Point segEnd;
float segStartAngle, segEndAngle;
switch (segType) // to find segStartAngle, segEnd and segEndAngle
switch (cmd.tag) // to find segStartAngle, segEnd and segEndAngle
{
case StylePathCommand::Tag::ClosePath:
case StylePathCommand::Tag::Close:
segEnd = pathStart;
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
case StylePathCommand::Tag::MoveTo: {
const Point& p = cmd.move_to.point.ConvertsToGfxPoint();
pathStart = segEnd =
cmd.move_to.absolute == StyleIsAbsolute::Yes ? p : segStart + p;
case StylePathCommand::Tag::Move: {
const Point& p = cmd.move.point.ToGfxPoint();
pathStart = segEnd = cmd.move.by_to == StyleByTo::To ? p : segStart + p;
pathStartIndex = aMarks->Length();
// If authors are going to specify multiple consecutive moveto commands
// with markers, me might as well make the angle do something useful:
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
}
case StylePathCommand::Tag::LineTo: {
const Point& p = cmd.line_to.point.ConvertsToGfxPoint();
segEnd =
cmd.line_to.absolute == StyleIsAbsolute::Yes ? p : segStart + p;
case StylePathCommand::Tag::Line: {
const Point& p = cmd.line.point.ToGfxPoint();
segEnd = cmd.line.by_to == StyleByTo::To ? p : segStart + p;
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
}
case StylePathCommand::Tag::CurveTo: {
Point cp1 = cmd.curve_to.control1.ConvertsToGfxPoint();
Point cp2 = cmd.curve_to.control2.ConvertsToGfxPoint();
segEnd = cmd.curve_to.point.ConvertsToGfxPoint();
case StylePathCommand::Tag::CubicCurve: {
Point cp1 = cmd.cubic_curve.control1.ToGfxPoint();
Point cp2 = cmd.cubic_curve.control2.ToGfxPoint();
segEnd = cmd.cubic_curve.point.ToGfxPoint();
if (cmd.curve_to.absolute == StyleIsAbsolute::No) {
if (cmd.cubic_curve.by_to == StyleByTo::By) {
cp1 += segStart;
cp2 += segStart;
segEnd += segStart;
@ -1189,11 +1179,11 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
segEnd, cp2 == segEnd ? (cp1 == cp2 ? segStart : cp1) : cp2);
break;
}
case StylePathCommand::Tag::QuadBezierCurveTo: {
Point cp1 = cmd.quad_bezier_curve_to.control1.ConvertsToGfxPoint();
segEnd = cmd.quad_bezier_curve_to.point.ConvertsToGfxPoint();
case StylePathCommand::Tag::QuadCurve: {
Point cp1 = cmd.quad_curve.control1.ToGfxPoint();
segEnd = cmd.quad_curve.point.ToGfxPoint();
if (cmd.quad_bezier_curve_to.absolute == StyleIsAbsolute::No) {
if (cmd.quad_curve.by_to == StyleByTo::By) {
cp1 += segStart;
segEnd += segStart; // set before setting tcp2!
}
@ -1203,16 +1193,15 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
segEndAngle = AngleOfVector(segEnd, cp1 == segEnd ? segStart : cp1);
break;
}
case StylePathCommand::Tag::EllipticalArc: {
const auto& arc = cmd.elliptical_arc;
float rx = arc.rx;
float ry = arc.ry;
float angle = arc.angle;
bool largeArcFlag = arc.large_arc_flag._0;
bool sweepFlag = arc.sweep_flag._0;
Point radii(arc.rx, arc.ry);
segEnd = arc.point.ConvertsToGfxPoint();
if (arc.absolute == StyleIsAbsolute::No) {
case StylePathCommand::Tag::Arc: {
const auto& arc = cmd.arc;
float rx = arc.radii.x;
float ry = arc.radii.y;
float angle = arc.rotate;
bool largeArcFlag = arc.arc_size == StyleArcSize::Large;
bool sweepFlag = arc.arc_sweep == StyleArcSweep::Cw;
segEnd = arc.point.ToGfxPoint();
if (arc.by_to == StyleByTo::By) {
segEnd += segStart;
}
@ -1246,30 +1235,32 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
largeArcFlag, sweepFlag, rx, ry);
break;
}
case StylePathCommand::Tag::HorizontalLineTo: {
if (cmd.horizontal_line_to.absolute == StyleIsAbsolute::Yes) {
segEnd = Point(cmd.horizontal_line_to.x, segStart.y);
case StylePathCommand::Tag::HLine: {
if (cmd.h_line.by_to == StyleByTo::To) {
segEnd = Point(cmd.h_line.x, segStart.y);
} else {
segEnd = segStart + Point(cmd.horizontal_line_to.x, 0.0f);
segEnd = segStart + Point(cmd.h_line.x, 0.0f);
}
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
}
case StylePathCommand::Tag::VerticalLineTo: {
if (cmd.vertical_line_to.absolute == StyleIsAbsolute::Yes) {
segEnd = Point(segStart.x, cmd.vertical_line_to.y);
case StylePathCommand::Tag::VLine: {
if (cmd.v_line.by_to == StyleByTo::To) {
segEnd = Point(segStart.x, cmd.v_line.y);
} else {
segEnd = segStart + Point(0.0f, cmd.vertical_line_to.y);
segEnd = segStart + Point(0.0f, cmd.v_line.y);
}
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
}
case StylePathCommand::Tag::SmoothCurveTo: {
Point cp1 = IsCubicType(prevSegType) ? segStart * 2 - prevCP : segStart;
Point cp2 = cmd.smooth_curve_to.control2.ConvertsToGfxPoint();
segEnd = cmd.smooth_curve_to.point.ConvertsToGfxPoint();
case StylePathCommand::Tag::SmoothCubic: {
const Point& cp1 = prevSeg && prevSeg->IsCubicType()
? segStart * 2 - prevCP
: segStart;
Point cp2 = cmd.smooth_cubic.control2.ToGfxPoint();
segEnd = cmd.smooth_cubic.point.ToGfxPoint();
if (cmd.smooth_curve_to.absolute == StyleIsAbsolute::No) {
if (cmd.smooth_cubic.by_to == StyleByTo::By) {
cp2 += segStart;
segEnd += segStart;
}
@ -1281,40 +1272,33 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
segEnd, cp2 == segEnd ? (cp1 == cp2 ? segStart : cp1) : cp2);
break;
}
case StylePathCommand::Tag::SmoothQuadBezierCurveTo: {
Point cp1 =
IsQuadraticType(prevSegType) ? segStart * 2 - prevCP : segStart;
segEnd =
cmd.smooth_quad_bezier_curve_to.absolute == StyleIsAbsolute::Yes
? cmd.smooth_quad_bezier_curve_to.point.ConvertsToGfxPoint()
: segStart + cmd.smooth_quad_bezier_curve_to.point
.ConvertsToGfxPoint();
case StylePathCommand::Tag::SmoothQuad: {
const Point& cp1 = prevSeg && prevSeg->IsQuadraticType()
? segStart * 2 - prevCP
: segStart;
segEnd = cmd.smooth_quad.by_to == StyleByTo::To
? cmd.smooth_quad.point.ToGfxPoint()
: segStart + cmd.smooth_quad.point.ToGfxPoint();
prevCP = cp1;
segStartAngle = AngleOfVector(cp1 == segStart ? segEnd : cp1, segStart);
segEndAngle = AngleOfVector(segEnd, cp1 == segEnd ? segStart : cp1);
break;
}
case StylePathCommand::Tag::Unknown:
// Leave any existing marks in aMarks so we have a visual indication of
// when things went wrong.
MOZ_ASSERT_UNREACHABLE("Unknown segment type - path corruption?");
return;
}
// Set the angle of the mark at the start of this segment:
if (aMarks->Length()) {
SVGMark& mark = aMarks->LastElement();
if (!IsMoveto(segType) && IsMoveto(prevSegType)) {
if (!cmd.IsMove() && prevSeg && prevSeg->IsMove()) {
// start of new subpath
pathStartAngle = mark.angle = segStartAngle;
} else if (IsMoveto(segType) && !IsMoveto(prevSegType)) {
} else if (cmd.IsMove() && !(prevSeg && prevSeg->IsMove())) {
// end of a subpath
if (prevSegType != StylePathCommand::Tag::ClosePath) {
if (!(prevSeg && prevSeg->IsClose())) {
mark.angle = prevSegEndAngle;
}
} else if (!(segType == StylePathCommand::Tag::ClosePath &&
prevSegType == StylePathCommand::Tag::ClosePath)) {
} else if (!(cmd.IsClose() && prevSeg && prevSeg->IsClose())) {
mark.angle =
SVGContentUtils::AngleBisect(prevSegEndAngle, segStartAngle);
}
@ -1327,19 +1311,18 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
static_cast<float>(segEnd.y), 0.0f,
SVGMark::eMid));
if (segType == StylePathCommand::Tag::ClosePath &&
prevSegType != StylePathCommand::Tag::ClosePath) {
if (cmd.IsClose() && !(prevSeg && prevSeg->IsClose())) {
aMarks->LastElement().angle = aMarks->ElementAt(pathStartIndex).angle =
SVGContentUtils::AngleBisect(segEndAngle, pathStartAngle);
}
prevSegType = segType;
prevSeg = &cmd;
prevSegEnd = segEnd;
prevSegEndAngle = segEndAngle;
}
if (aMarks->Length()) {
if (prevSegType != StylePathCommand::Tag::ClosePath) {
if (!(prevSeg && prevSeg->IsClose())) {
aMarks->LastElement().angle = prevSegEndAngle;
}
aMarks->LastElement().type = SVGMark::eEnd;

View File

@ -17,13 +17,13 @@
#include "mozilla/gfx/Types.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ServoStyleConsts.h"
#include "nsTArray.h"
#include <string.h>
namespace mozilla {
struct StylePathCommand;
struct SVGMark;
enum class StyleStrokeLinecap : uint8_t;

View File

@ -351,7 +351,7 @@ bool SVGPathElement::IsClosedLoop() const {
const nsStyleSVGReset* styleSVGReset = s->StyleSVGReset();
if (styleSVGReset->mD.IsPath()) {
isClosed = !styleSVGReset->mD.AsPath()._0.IsEmpty() &&
styleSVGReset->mD.AsPath()._0.AsSpan().rbegin()->IsClosePath();
styleSVGReset->mD.AsPath()._0.AsSpan().rbegin()->IsClose();
}
};

View File

@ -417,14 +417,13 @@ void SVGPathSegUtils::TraversePathSegment(const float* aData,
void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
SVGPathTraversalState& aState) {
switch (aCommand.tag) {
case StylePathCommand::Tag::ClosePath:
case StylePathCommand::Tag::Close:
TraverseClosePath(nullptr, aState);
break;
case StylePathCommand::Tag::MoveTo: {
const Point& p = aCommand.move_to.point.ConvertsToGfxPoint();
case StylePathCommand::Tag::Move: {
const Point& p = aCommand.move.point.ToGfxPoint();
aState.start = aState.pos =
aCommand.move_to.absolute == StyleIsAbsolute::Yes ? p
: aState.pos + p;
aCommand.move.by_to == StyleByTo::To ? p : aState.pos + p;
if (aState.ShouldUpdateLengthAndControlPoints()) {
// aState.length is unchanged, since move commands don't affect path=
// length.
@ -432,10 +431,10 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
}
break;
}
case StylePathCommand::Tag::LineTo: {
Point to = aCommand.line_to.absolute == StyleIsAbsolute::Yes
? aCommand.line_to.point.ConvertsToGfxPoint()
: aState.pos + aCommand.line_to.point.ConvertsToGfxPoint();
case StylePathCommand::Tag::Line: {
Point to = aCommand.line.by_to == StyleByTo::To
? aCommand.line.point.ToGfxPoint()
: aState.pos + aCommand.line.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
aState.length += CalcDistanceBetweenPoints(aState.pos, to);
aState.cp1 = aState.cp2 = to;
@ -443,14 +442,14 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
case StylePathCommand::Tag::CurveTo: {
const bool isRelative = aCommand.curve_to.absolute == StyleIsAbsolute::No;
case StylePathCommand::Tag::CubicCurve: {
const bool isRelative = aCommand.cubic_curve.by_to == StyleByTo::By;
Point to = isRelative
? aState.pos + aCommand.curve_to.point.ConvertsToGfxPoint()
: aCommand.curve_to.point.ConvertsToGfxPoint();
? aState.pos + aCommand.cubic_curve.point.ToGfxPoint()
: aCommand.cubic_curve.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
Point cp1 = aCommand.curve_to.control1.ConvertsToGfxPoint();
Point cp2 = aCommand.curve_to.control2.ConvertsToGfxPoint();
Point cp1 = aCommand.cubic_curve.control1.ToGfxPoint();
Point cp2 = aCommand.cubic_curve.control2.ToGfxPoint();
if (isRelative) {
cp1 += aState.pos;
cp2 += aState.pos;
@ -463,20 +462,15 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
case StylePathCommand::Tag::QuadBezierCurveTo: {
const bool isRelative =
aCommand.quad_bezier_curve_to.absolute == StyleIsAbsolute::No;
Point to =
isRelative
? aState.pos +
aCommand.quad_bezier_curve_to.point.ConvertsToGfxPoint()
: aCommand.quad_bezier_curve_to.point.ConvertsToGfxPoint();
case StylePathCommand::Tag::QuadCurve: {
const bool isRelative = aCommand.quad_curve.by_to == StyleByTo::By;
Point to = isRelative
? aState.pos + aCommand.quad_curve.point.ToGfxPoint()
: aCommand.quad_curve.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
Point cp =
isRelative
? aState.pos + aCommand.quad_bezier_curve_to.control1
.ConvertsToGfxPoint()
: aCommand.quad_bezier_curve_to.control1.ConvertsToGfxPoint();
Point cp = isRelative
? aState.pos + aCommand.quad_curve.control1.ToGfxPoint()
: aCommand.quad_curve.control1.ToGfxPoint();
aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
aState.cp1 = cp;
aState.cp2 = to;
@ -484,21 +478,22 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
case StylePathCommand::Tag::EllipticalArc: {
Point to =
aCommand.elliptical_arc.absolute == StyleIsAbsolute::Yes
? aCommand.elliptical_arc.point.ConvertsToGfxPoint()
: aState.pos + aCommand.elliptical_arc.point.ConvertsToGfxPoint();
case StylePathCommand::Tag::Arc: {
const auto& arc = aCommand.arc;
Point to = arc.by_to == StyleByTo::To
? arc.point.ToGfxPoint()
: aState.pos + arc.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
const auto& arc = aCommand.elliptical_arc;
float dist = 0;
Point radii(arc.rx, arc.ry);
Point radii = arc.radii.ToGfxPoint();
if (radii.x == 0.0f || radii.y == 0.0f) {
dist = CalcDistanceBetweenPoints(aState.pos, to);
} else {
Point bez[4] = {aState.pos, Point(0, 0), Point(0, 0), Point(0, 0)};
SVGArcConverter converter(aState.pos, to, radii, arc.angle,
arc.large_arc_flag._0, arc.sweep_flag._0);
const bool largeArcFlag = arc.arc_size == StyleArcSize::Large;
const bool sweepFlag = arc.arc_sweep == StyleArcSweep::Cw;
SVGArcConverter converter(aState.pos, to, radii, arc.rotate,
largeArcFlag, sweepFlag);
while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3])) {
dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier);
bez[0] = bez[3];
@ -510,10 +505,10 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
case StylePathCommand::Tag::HorizontalLineTo: {
Point to(aCommand.horizontal_line_to.absolute == StyleIsAbsolute::Yes
? aCommand.horizontal_line_to.x
: aState.pos.x + aCommand.horizontal_line_to.x,
case StylePathCommand::Tag::HLine: {
Point to(aCommand.h_line.by_to == StyleByTo::To
? aCommand.h_line.x
: aState.pos.x + aCommand.h_line.x,
aState.pos.y);
if (aState.ShouldUpdateLengthAndControlPoints()) {
aState.length += std::fabs(to.x - aState.pos.x);
@ -522,11 +517,10 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
case StylePathCommand::Tag::VerticalLineTo: {
Point to(aState.pos.x,
aCommand.vertical_line_to.absolute == StyleIsAbsolute::Yes
? aCommand.vertical_line_to.y
: aState.pos.y + aCommand.vertical_line_to.y);
case StylePathCommand::Tag::VLine: {
Point to(aState.pos.x, aCommand.v_line.by_to == StyleByTo::To
? aCommand.v_line.y
: aState.pos.y + aCommand.v_line.y);
if (aState.ShouldUpdateLengthAndControlPoints()) {
aState.length += std::fabs(to.y - aState.pos.y);
aState.cp1 = aState.cp2 = to;
@ -534,20 +528,16 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
case StylePathCommand::Tag::SmoothCurveTo: {
const bool isRelative =
aCommand.smooth_curve_to.absolute == StyleIsAbsolute::No;
Point to =
isRelative
? aState.pos + aCommand.smooth_curve_to.point.ConvertsToGfxPoint()
: aCommand.smooth_curve_to.point.ConvertsToGfxPoint();
case StylePathCommand::Tag::SmoothCubic: {
const bool isRelative = aCommand.smooth_cubic.by_to == StyleByTo::By;
Point to = isRelative
? aState.pos + aCommand.smooth_cubic.point.ToGfxPoint()
: aCommand.smooth_cubic.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
Point cp1 = aState.pos - (aState.cp2 - aState.pos);
Point cp2 =
isRelative
? aState.pos +
aCommand.smooth_curve_to.control2.ConvertsToGfxPoint()
: aCommand.smooth_curve_to.control2.ConvertsToGfxPoint();
Point cp2 = isRelative ? aState.pos +
aCommand.smooth_cubic.control2.ToGfxPoint()
: aCommand.smooth_cubic.control2.ToGfxPoint();
aState.length +=
(float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
aState.cp2 = cp2;
@ -556,12 +546,10 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
case StylePathCommand::Tag::SmoothQuadBezierCurveTo: {
Point to =
aCommand.smooth_quad_bezier_curve_to.absolute == StyleIsAbsolute::Yes
? aCommand.smooth_quad_bezier_curve_to.point.ConvertsToGfxPoint()
: aState.pos + aCommand.smooth_quad_bezier_curve_to.point
.ConvertsToGfxPoint();
case StylePathCommand::Tag::SmoothQuad: {
Point to = aCommand.smooth_quad.by_to == StyleByTo::To
? aCommand.smooth_quad.point.ToGfxPoint()
: aState.pos + aCommand.smooth_quad.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
Point cp = aState.pos - (aState.cp1 - aState.pos);
aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
@ -571,8 +559,6 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
case StylePathCommand::Tag::Unknown:
MOZ_ASSERT_UNREACHABLE("Unacceptable path segment type");
}
}
@ -704,8 +690,8 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
for (const StylePathCommand& cmd : aPath) {
switch (cmd.tag) {
case StylePathCommand::Tag::MoveTo: {
Point to = cmd.move_to.point.ConvertsToGfxPoint();
case StylePathCommand::Tag::Move: {
Point to = cmd.move.point.ToGfxPoint();
if (helper.idx != 0) {
// This is overly strict since empty moveto sequences such as "M 10 12
// M 3 2 M 0 0" render nothing, but I expect it won't make us miss a
@ -731,7 +717,7 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
return Nothing();
}
if (cmd.move_to.absolute == StyleIsAbsolute::No) {
if (cmd.move.by_to == StyleByTo::By) {
to = segStart + to;
}
@ -744,7 +730,7 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
break;
}
case StylePathCommand::Tag::ClosePath: {
case StylePathCommand::Tag::Close: {
if (!helper.Edge(segStart, pathStart)) {
return Nothing();
}
@ -754,9 +740,9 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
pathStart = segStart;
break;
}
case StylePathCommand::Tag::LineTo: {
Point to = cmd.line_to.point.ConvertsToGfxPoint();
if (cmd.line_to.absolute == StyleIsAbsolute::No) {
case StylePathCommand::Tag::Line: {
Point to = cmd.line.point.ToGfxPoint();
if (cmd.line.by_to == StyleByTo::By) {
to = segStart + to;
}
@ -766,9 +752,9 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
segStart = to;
break;
}
case StylePathCommand::Tag::HorizontalLineTo: {
Point to = gfx::Point(cmd.horizontal_line_to.x, segStart.y);
if (cmd.horizontal_line_to.absolute == StyleIsAbsolute::No) {
case StylePathCommand::Tag::HLine: {
Point to = gfx::Point(cmd.h_line.x, segStart.y);
if (cmd.h_line.by_to == StyleByTo::By) {
to.x += segStart.x;
}
@ -778,9 +764,9 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
segStart = to;
break;
}
case StylePathCommand::Tag::VerticalLineTo: {
Point to = gfx::Point(segStart.x, cmd.vertical_line_to.y);
if (cmd.horizontal_line_to.absolute == StyleIsAbsolute::No) {
case StylePathCommand::Tag::VLine: {
Point to = gfx::Point(segStart.x, cmd.v_line.y);
if (cmd.h_line.by_to == StyleByTo::By) {
to.y += segStart.y;
}

View File

@ -14,8 +14,8 @@
#include "nsDebug.h"
namespace mozilla {
struct StylePathCommand;
template <typename Angle, typename LP>
struct StyleGenericShapeCommand;
#define NS_SVG_PATH_SEG_MAX_ARGS 7
#define NS_SVG_PATH_SEG_FIRST_VALID_TYPE \
@ -264,8 +264,9 @@ class SVGPathSegUtils {
* Traverse the given path segment and update the SVGPathTraversalState
* object. This is identical to the above one but accepts StylePathCommand.
*/
static void TraversePathSegment(const StylePathCommand& aCommand,
SVGPathTraversalState& aState);
static void TraversePathSegment(
const StyleGenericShapeCommand<float, float>& aCommand,
SVGPathTraversalState& aState);
};
/// Detect whether the path represents a rectangle (for both filling AND
@ -280,7 +281,8 @@ class SVGPathSegUtils {
/// practice).
///
/// We could implement something similar for polygons.
Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath);
Maybe<gfx::Rect> SVGPathToAxisAlignedRect(
Span<const StyleGenericShapeCommand<float, float>> aPath);
} // namespace mozilla

View File

@ -434,7 +434,7 @@ Maybe<ResolvedMotionPathData> MotionPathUtils::ResolveMotionPath(
static inline bool IsClosedLoop(const StyleSVGPathData& aPathData) {
return !aPathData._0.AsSpan().empty() &&
aPathData._0.AsSpan().rbegin()->IsClosePath();
aPathData._0.AsSpan().rbegin()->IsClose();
}
// Create a path for "inset(0 round X)", where X is the value of border-radius
@ -466,8 +466,8 @@ static already_AddRefed<gfx::Path> BuildDefaultPathForURL(
return nullptr;
}
Array<const StylePathCommand, 1> array(StylePathCommand::MoveTo(
StyleCoordPair(gfx::Point{0.0, 0.0}), StyleIsAbsolute::No));
Array<const StylePathCommand, 1> array(StylePathCommand::Move(
StyleByTo::By, StyleCoordinatePair<StyleCSSFloat>{0.0, 0.0}));
return SVGPathData::BuildPath(array, aBuilder, StyleStrokeLinecap::Butt, 0.0);
}

View File

@ -1201,6 +1201,11 @@ inline nsRect StyleZoom::Unzoom(const nsRect& aValue) const {
UnzoomCoord(aValue.Width()), UnzoomCoord(aValue.Height()));
}
template <>
inline gfx::Point StyleCoordinatePair<StyleCSSFloat>::ToGfxPoint() const {
return gfx::Point(x, y);
}
} // namespace mozilla
#endif

View File

@ -891,12 +891,31 @@ pub enum ByTo {
To,
}
impl ByTo {
/// Return true if it is absolute, i.e. it is To.
#[inline]
pub fn is_abs(&self) -> bool {
matches!(self, ByTo::To)
}
/// Create ByTo based on the flag if it is absolute.
#[inline]
pub fn new(is_abs: bool) -> Self {
if is_abs {
Self::To
} else {
Self::By
}
}
}
/// Defines a pair of coordinates, representing a rightward and downward offset, respectively, from
/// a specified reference point. Percentages are resolved against the width or height,
/// respectively, of the reference box.
/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-coordinate-pair
#[allow(missing_docs)]
#[derive(
AddAssign,
Animate,
Clone,
ComputeSquaredDistance,
@ -916,8 +935,8 @@ pub enum ByTo {
)]
#[repr(C)]
pub struct CoordinatePair<LengthPercentage> {
x: LengthPercentage,
y: LengthPercentage,
pub x: LengthPercentage,
pub y: LengthPercentage,
}
impl<LengthPercentage> CoordinatePair<LengthPercentage> {
@ -936,6 +955,7 @@ impl<LengthPercentage> CoordinatePair<LengthPercentage> {
Copy,
Debug,
Deserialize,
FromPrimitive,
MallocSizeOf,
Parse,
PartialEq,
@ -958,11 +978,12 @@ pub enum ArcSweep {
impl Animate for ArcSweep {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
use num_traits::FromPrimitive;
// If an arc command has different <arc-sweep> between its starting and ending list, then
// the interpolated result uses cw for any progress value between 0 and 1.
(*self as i32)
.animate(&(*other as i32), procedure)
.map(|v| if v > 0 { ArcSweep::Cw } else { ArcSweep::Ccw })
.map(|v| ArcSweep::from_u8((v > 0) as u8).unwrap_or(ArcSweep::Ccw))
}
}
@ -980,6 +1001,7 @@ impl ComputeSquaredDistance for ArcSweep {
Copy,
Debug,
Deserialize,
FromPrimitive,
MallocSizeOf,
Parse,
PartialEq,
@ -1002,17 +1024,12 @@ pub enum ArcSize {
impl Animate for ArcSize {
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
use num_traits::FromPrimitive;
// If it has different <arc-size> keywords, then the interpolated result uses large for any
// progress value between 0 and 1.
(*self as i32)
.animate(&(*other as i32), procedure)
.map(|v| {
if v > 0 {
ArcSize::Large
} else {
ArcSize::Small
}
})
.map(|v| ArcSize::from_u8((v > 0) as u8).unwrap_or(ArcSize::Small))
}
}

View File

@ -5,10 +5,13 @@
//! Specified types for SVG Path.
use crate::parser::{Parse, ParserContext};
use crate::values::animated::{lists, Animate, Procedure, ToAnimatedZero};
use crate::values::animated::{lists, Animate, Procedure};
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::basic_shape::GenericShapeCommand;
use crate::values::generics::basic_shape::{ArcSize, ArcSweep, ByTo, CoordinatePair};
use crate::values::CSSFloat;
use cssparser::Parser;
use num_traits::FromPrimitive;
use std::fmt::{self, Write};
use std::iter::{Cloned, Peekable};
use std::slice;
@ -70,6 +73,7 @@ impl SVGPathData {
#[cfg(feature = "gecko")]
pub fn decode_from_f32_array(path: &[f32]) -> Result<Self, ()> {
use crate::gecko_bindings::structs::dom::SVGPathSeg_Binding::*;
use crate::values::generics::basic_shape::GenericShapeCommand::*;
let mut result: Vec<PathCommand> = Vec::new();
let mut i: usize = 0;
@ -80,85 +84,84 @@ impl SVGPathData {
let seg_type = path[i].to_bits() as u16;
i = i + 1;
match seg_type {
PATHSEG_CLOSEPATH => result.push(PathCommand::ClosePath),
PATHSEG_CLOSEPATH => result.push(Close),
PATHSEG_MOVETO_ABS | PATHSEG_MOVETO_REL => {
debug_assert!(i + 1 < path.len());
result.push(PathCommand::MoveTo {
result.push(Move {
point: CoordPair::new(path[i], path[i + 1]),
absolute: IsAbsolute::new(seg_type == PATHSEG_MOVETO_ABS),
by_to: ByTo::new(seg_type == PATHSEG_MOVETO_ABS),
});
i = i + 2;
},
PATHSEG_LINETO_ABS | PATHSEG_LINETO_REL => {
debug_assert!(i + 1 < path.len());
result.push(PathCommand::LineTo {
result.push(Line {
point: CoordPair::new(path[i], path[i + 1]),
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_ABS),
by_to: ByTo::new(seg_type == PATHSEG_LINETO_ABS),
});
i = i + 2;
},
PATHSEG_CURVETO_CUBIC_ABS | PATHSEG_CURVETO_CUBIC_REL => {
debug_assert!(i + 5 < path.len());
result.push(PathCommand::CurveTo {
result.push(CubicCurve {
control1: CoordPair::new(path[i], path[i + 1]),
control2: CoordPair::new(path[i + 2], path[i + 3]),
point: CoordPair::new(path[i + 4], path[i + 5]),
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS),
by_to: ByTo::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS),
});
i = i + 6;
},
PATHSEG_CURVETO_QUADRATIC_ABS | PATHSEG_CURVETO_QUADRATIC_REL => {
debug_assert!(i + 3 < path.len());
result.push(PathCommand::QuadBezierCurveTo {
result.push(QuadCurve {
control1: CoordPair::new(path[i], path[i + 1]),
point: CoordPair::new(path[i + 2], path[i + 3]),
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS),
by_to: ByTo::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS),
});
i = i + 4;
},
PATHSEG_ARC_ABS | PATHSEG_ARC_REL => {
debug_assert!(i + 6 < path.len());
result.push(PathCommand::EllipticalArc {
rx: path[i],
ry: path[i + 1],
angle: path[i + 2],
large_arc_flag: ArcFlag(path[i + 3] != 0.0f32),
sweep_flag: ArcFlag(path[i + 4] != 0.0f32),
result.push(Arc {
radii: CoordPair::new(path[i], path[i + 1]),
rotate: path[i + 2],
arc_size: ArcSize::from_u8((path[i + 3] != 0.0f32) as u8).unwrap(),
arc_sweep: ArcSweep::from_u8((path[i + 4] != 0.0f32) as u8).unwrap(),
point: CoordPair::new(path[i + 5], path[i + 6]),
absolute: IsAbsolute::new(seg_type == PATHSEG_ARC_ABS),
by_to: ByTo::new(seg_type == PATHSEG_ARC_ABS),
});
i = i + 7;
},
PATHSEG_LINETO_HORIZONTAL_ABS | PATHSEG_LINETO_HORIZONTAL_REL => {
debug_assert!(i < path.len());
result.push(PathCommand::HorizontalLineTo {
result.push(HLine {
x: path[i],
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS),
by_to: ByTo::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS),
});
i = i + 1;
},
PATHSEG_LINETO_VERTICAL_ABS | PATHSEG_LINETO_VERTICAL_REL => {
debug_assert!(i < path.len());
result.push(PathCommand::VerticalLineTo {
result.push(VLine {
y: path[i],
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS),
by_to: ByTo::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS),
});
i = i + 1;
},
PATHSEG_CURVETO_CUBIC_SMOOTH_ABS | PATHSEG_CURVETO_CUBIC_SMOOTH_REL => {
debug_assert!(i + 3 < path.len());
result.push(PathCommand::SmoothCurveTo {
result.push(SmoothCubic {
control2: CoordPair::new(path[i], path[i + 1]),
point: CoordPair::new(path[i + 2], path[i + 3]),
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS),
by_to: ByTo::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS),
});
i = i + 4;
},
PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS | PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL => {
debug_assert!(i + 1 < path.len());
result.push(PathCommand::SmoothQuadBezierCurveTo {
result.push(SmoothQuad {
point: CoordPair::new(path[i], path[i + 1]),
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS),
by_to: ByTo::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS),
});
i = i + 2;
},
@ -215,7 +218,7 @@ impl ToCss for SVGPathData {
{
let mut writer = SequenceWriter::new(dest, " ");
for command in self.commands() {
writer.item(command)?;
writer.write_item(|inner| command.to_css_for_svg(inner))?;
}
}
dest.write_char('"')
@ -268,79 +271,7 @@ impl ComputeSquaredDistance for SVGPathData {
/// points of the Bézier curve in the spec.
///
/// https://www.w3.org/TR/SVG11/paths.html#PathData
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedZero,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[allow(missing_docs)]
#[repr(C, u8)]
pub enum PathCommand {
/// The unknown type.
/// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN
Unknown,
/// The "moveto" command.
MoveTo {
point: CoordPair,
absolute: IsAbsolute,
},
/// The "lineto" command.
LineTo {
point: CoordPair,
absolute: IsAbsolute,
},
/// The horizontal "lineto" command.
HorizontalLineTo { x: CSSFloat, absolute: IsAbsolute },
/// The vertical "lineto" command.
VerticalLineTo { y: CSSFloat, absolute: IsAbsolute },
/// The cubic Bézier curve command.
CurveTo {
control1: CoordPair,
control2: CoordPair,
point: CoordPair,
absolute: IsAbsolute,
},
/// The smooth curve command.
SmoothCurveTo {
control2: CoordPair,
point: CoordPair,
absolute: IsAbsolute,
},
/// The quadratic Bézier curve command.
QuadBezierCurveTo {
control1: CoordPair,
point: CoordPair,
absolute: IsAbsolute,
},
/// The smooth quadratic Bézier curve command.
SmoothQuadBezierCurveTo {
point: CoordPair,
absolute: IsAbsolute,
},
/// The elliptical arc curve command.
EllipticalArc {
rx: CSSFloat,
ry: CSSFloat,
angle: CSSFloat,
large_arc_flag: ArcFlag,
sweep_flag: ArcFlag,
point: CoordPair,
absolute: IsAbsolute,
},
/// The "closepath" command.
ClosePath,
}
pub type PathCommand = GenericShapeCommand<CSSFloat, CSSFloat>;
/// For internal SVGPath normalization.
#[allow(missing_docs)]
@ -355,177 +286,157 @@ impl PathCommand {
///
/// See discussion: https://github.com/w3c/svgwg/issues/321
fn normalize(&self, state: &mut PathTraversalState) -> Self {
use self::PathCommand::*;
use crate::values::generics::basic_shape::GenericShapeCommand::*;
match *self {
Unknown => Unknown,
ClosePath => {
Close => {
state.pos = state.subpath_start;
ClosePath
Close
},
MoveTo {
mut point,
absolute,
} => {
if !absolute.is_yes() {
Move { by_to, mut point } => {
if !by_to.is_abs() {
point += state.pos;
}
state.pos = point;
state.subpath_start = point;
MoveTo {
Move {
by_to: ByTo::To,
point,
absolute: IsAbsolute::Yes,
}
},
LineTo {
mut point,
absolute,
} => {
if !absolute.is_yes() {
Line { by_to, mut point } => {
if !by_to.is_abs() {
point += state.pos;
}
state.pos = point;
LineTo {
Line {
by_to: ByTo::To,
point,
absolute: IsAbsolute::Yes,
}
},
HorizontalLineTo { mut x, absolute } => {
if !absolute.is_yes() {
HLine { by_to, mut x } => {
if !by_to.is_abs() {
x += state.pos.x;
}
state.pos.x = x;
HorizontalLineTo {
x,
absolute: IsAbsolute::Yes,
}
HLine { by_to: ByTo::To, x }
},
VerticalLineTo { mut y, absolute } => {
if !absolute.is_yes() {
VLine { by_to, mut y } => {
if !by_to.is_abs() {
y += state.pos.y;
}
state.pos.y = y;
VerticalLineTo {
y,
absolute: IsAbsolute::Yes,
}
VLine { by_to: ByTo::To, y }
},
CurveTo {
CubicCurve {
by_to,
mut point,
mut control1,
mut control2,
mut point,
absolute,
} => {
if !absolute.is_yes() {
if !by_to.is_abs() {
point += state.pos;
control1 += state.pos;
control2 += state.pos;
point += state.pos;
}
state.pos = point;
CurveTo {
CubicCurve {
by_to: ByTo::To,
point,
control1,
control2,
point,
absolute: IsAbsolute::Yes,
}
},
SmoothCurveTo {
mut control2,
QuadCurve {
by_to,
mut point,
absolute,
} => {
if !absolute.is_yes() {
control2 += state.pos;
point += state.pos;
}
state.pos = point;
SmoothCurveTo {
control2,
point,
absolute: IsAbsolute::Yes,
}
},
QuadBezierCurveTo {
mut control1,
mut point,
absolute,
} => {
if !absolute.is_yes() {
if !by_to.is_abs() {
point += state.pos;
control1 += state.pos;
point += state.pos;
}
state.pos = point;
QuadBezierCurveTo {
QuadCurve {
by_to: ByTo::To,
point,
control1,
point,
absolute: IsAbsolute::Yes,
}
},
SmoothQuadBezierCurveTo {
SmoothCubic {
by_to,
mut point,
absolute,
mut control2,
} => {
if !absolute.is_yes() {
if !by_to.is_abs() {
point += state.pos;
control2 += state.pos;
}
state.pos = point;
SmoothCubic {
by_to: ByTo::To,
point,
control2,
}
},
SmoothQuad { by_to, mut point } => {
if !by_to.is_abs() {
point += state.pos;
}
state.pos = point;
SmoothQuadBezierCurveTo {
SmoothQuad {
by_to: ByTo::To,
point,
absolute: IsAbsolute::Yes,
}
},
EllipticalArc {
rx,
ry,
angle,
large_arc_flag,
sweep_flag,
Arc {
by_to,
mut point,
absolute,
radii,
arc_sweep,
arc_size,
rotate,
} => {
if !absolute.is_yes() {
if !by_to.is_abs() {
point += state.pos;
}
state.pos = point;
EllipticalArc {
rx,
ry,
angle,
large_arc_flag,
sweep_flag,
Arc {
by_to: ByTo::To,
point,
absolute: IsAbsolute::Yes,
radii,
arc_sweep,
arc_size,
rotate,
}
},
}
}
}
impl ToCss for PathCommand {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
/// The serialization of the svg path.
fn to_css_for_svg<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use self::PathCommand::*;
use crate::values::generics::basic_shape::GenericShapeCommand::*;
match *self {
Unknown => dest.write_char('X'),
ClosePath => dest.write_char('Z'),
MoveTo { point, absolute } => {
dest.write_char(if absolute.is_yes() { 'M' } else { 'm' })?;
Close => dest.write_char('Z'),
Move { by_to, point } => {
dest.write_char(if by_to.is_abs() { 'M' } else { 'm' })?;
dest.write_char(' ')?;
point.to_css(dest)
},
LineTo { point, absolute } => {
dest.write_char(if absolute.is_yes() { 'L' } else { 'l' })?;
Line { by_to, point } => {
dest.write_char(if by_to.is_abs() { 'L' } else { 'l' })?;
dest.write_char(' ')?;
point.to_css(dest)
},
CurveTo {
CubicCurve {
by_to,
point,
control1,
control2,
point,
absolute,
} => {
dest.write_char(if absolute.is_yes() { 'C' } else { 'c' })?;
dest.write_char(if by_to.is_abs() { 'C' } else { 'c' })?;
dest.write_char(' ')?;
control1.to_css(dest)?;
dest.write_char(' ')?;
@ -533,63 +444,60 @@ impl ToCss for PathCommand {
dest.write_char(' ')?;
point.to_css(dest)
},
QuadBezierCurveTo {
control1,
QuadCurve {
by_to,
point,
absolute,
control1,
} => {
dest.write_char(if absolute.is_yes() { 'Q' } else { 'q' })?;
dest.write_char(if by_to.is_abs() { 'Q' } else { 'q' })?;
dest.write_char(' ')?;
control1.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
EllipticalArc {
rx,
ry,
angle,
large_arc_flag,
sweep_flag,
Arc {
by_to,
point,
absolute,
radii,
arc_sweep,
arc_size,
rotate,
} => {
dest.write_char(if absolute.is_yes() { 'A' } else { 'a' })?;
dest.write_char(if by_to.is_abs() { 'A' } else { 'a' })?;
dest.write_char(' ')?;
rx.to_css(dest)?;
radii.to_css(dest)?;
dest.write_char(' ')?;
ry.to_css(dest)?;
rotate.to_css(dest)?;
dest.write_char(' ')?;
angle.to_css(dest)?;
(arc_size as i32).to_css(dest)?;
dest.write_char(' ')?;
large_arc_flag.to_css(dest)?;
dest.write_char(' ')?;
sweep_flag.to_css(dest)?;
(arc_sweep as i32).to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
HorizontalLineTo { x, absolute } => {
dest.write_char(if absolute.is_yes() { 'H' } else { 'h' })?;
HLine { by_to, x } => {
dest.write_char(if by_to.is_abs() { 'H' } else { 'h' })?;
dest.write_char(' ')?;
x.to_css(dest)
},
VerticalLineTo { y, absolute } => {
dest.write_char(if absolute.is_yes() { 'V' } else { 'v' })?;
VLine { by_to, y } => {
dest.write_char(if by_to.is_abs() { 'V' } else { 'v' })?;
dest.write_char(' ')?;
y.to_css(dest)
},
SmoothCurveTo {
control2,
SmoothCubic {
by_to,
point,
absolute,
control2,
} => {
dest.write_char(if absolute.is_yes() { 'S' } else { 's' })?;
dest.write_char(if by_to.is_abs() { 'S' } else { 's' })?;
dest.write_char(' ')?;
control2.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
SmoothQuadBezierCurveTo { point, absolute } => {
dest.write_char(if absolute.is_yes() { 'T' } else { 't' })?;
SmoothQuad { by_to, point } => {
dest.write_char(if by_to.is_abs() { 'T' } else { 't' })?;
dest.write_char(' ')?;
point.to_css(dest)
},
@ -597,135 +505,8 @@ impl ToCss for PathCommand {
}
}
/// The path command absolute type.
#[allow(missing_docs)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedZero,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum IsAbsolute {
Yes,
No,
}
impl IsAbsolute {
/// Return true if this is IsAbsolute::Yes.
#[inline]
pub fn is_yes(&self) -> bool {
*self == IsAbsolute::Yes
}
/// Return Yes if value is true. Otherwise, return No.
#[inline]
fn new(value: bool) -> Self {
if value {
IsAbsolute::Yes
} else {
IsAbsolute::No
}
}
}
/// The path coord type.
#[allow(missing_docs)]
#[derive(
AddAssign,
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct CoordPair {
x: CSSFloat,
y: CSSFloat,
}
impl CoordPair {
/// Create a CoordPair.
#[inline]
pub fn new(x: CSSFloat, y: CSSFloat) -> Self {
CoordPair { x, y }
}
}
/// The EllipticalArc flag type.
#[derive(
Clone,
Copy,
Debug,
Deserialize,
MallocSizeOf,
PartialEq,
Serialize,
SpecifiedValueInfo,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct ArcFlag(bool);
impl ToCss for ArcFlag {
#[inline]
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
(self.0 as i32).to_css(dest)
}
}
impl Animate for ArcFlag {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
(self.0 as i32)
.animate(&(other.0 as i32), procedure)
.map(|v| ArcFlag(v > 0))
}
}
impl ComputeSquaredDistance for ArcFlag {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
(self.0 as i32).compute_squared_distance(&(other.0 as i32))
}
}
impl ToAnimatedZero for ArcFlag {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
// The 2 ArcFlags in EllipticalArc determine which one of the 4 different arcs will be
// used. (i.e. From 4 combinations). In other words, if we change the flag, we get a
// different arc. Therefore, we return *self.
// https://svgwg.org/svg2-draft/paths.html#PathDataEllipticalArcCommands
Ok(*self)
}
}
pub type CoordPair = CoordinatePair<CSSFloat>;
/// SVG Path parser.
struct PathParser<'a> {
@ -736,7 +517,7 @@ struct PathParser<'a> {
macro_rules! parse_arguments {
(
$parser:ident,
$abs:ident,
$by_to:ident,
$enum:ident,
[ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ]
) => {
@ -747,7 +528,9 @@ macro_rules! parse_arguments {
skip_comma_wsp(&mut $parser.chars);
let $other_para = $other_func(&mut $parser.chars)?;
)*
$parser.path.push(PathCommand::$enum { $para $(, $other_para)*, $abs });
$parser.path.push(
PathCommand::$enum { $by_to, $para $(, $other_para)* }
);
// End of string or the next character is a possible new command.
if !skip_wsp(&mut $parser.chars) ||
@ -785,23 +568,23 @@ impl<'a> PathParser<'a> {
}
let command = self.chars.next().unwrap();
let abs = if command.is_ascii_uppercase() {
IsAbsolute::Yes
let by_to = if command.is_ascii_uppercase() {
ByTo::To
} else {
IsAbsolute::No
ByTo::By
};
skip_wsp(&mut self.chars);
match command {
b'Z' | b'z' => self.parse_closepath(),
b'L' | b'l' => self.parse_lineto(abs),
b'H' | b'h' => self.parse_h_lineto(abs),
b'V' | b'v' => self.parse_v_lineto(abs),
b'C' | b'c' => self.parse_curveto(abs),
b'S' | b's' => self.parse_smooth_curveto(abs),
b'Q' | b'q' => self.parse_quadratic_bezier_curveto(abs),
b'T' | b't' => self.parse_smooth_quadratic_bezier_curveto(abs),
b'A' | b'a' => self.parse_elliptical_arc(abs),
b'L' | b'l' => self.parse_lineto(by_to),
b'H' | b'h' => self.parse_h_lineto(by_to),
b'V' | b'v' => self.parse_v_lineto(by_to),
b'C' | b'c' => self.parse_curveto(by_to),
b'S' | b's' => self.parse_smooth_curveto(by_to),
b'Q' | b'q' => self.parse_quadratic_bezier_curveto(by_to),
b'T' | b't' => self.parse_smooth_quadratic_bezier_curveto(by_to),
b'A' | b'a' => self.parse_elliptical_arc(by_to),
_ => return Err(()),
}?;
}
@ -817,12 +600,8 @@ impl<'a> PathParser<'a> {
skip_wsp(&mut self.chars);
let point = parse_coord(&mut self.chars)?;
let absolute = if command == b'M' {
IsAbsolute::Yes
} else {
IsAbsolute::No
};
self.path.push(PathCommand::MoveTo { point, absolute });
let by_to = if command == b'M' { ByTo::To } else { ByTo::By };
self.path.push(PathCommand::Move { by_to, point });
// End of string or the next character is a possible new command.
if !skip_wsp(&mut self.chars) || self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic())
@ -833,69 +612,74 @@ impl<'a> PathParser<'a> {
// If a moveto is followed by multiple pairs of coordinates, the subsequent
// pairs are treated as implicit lineto commands.
self.parse_lineto(absolute)
self.parse_lineto(by_to)
}
/// Parse "closepath" command.
fn parse_closepath(&mut self) -> Result<(), ()> {
self.path.push(PathCommand::ClosePath);
self.path.push(PathCommand::Close);
Ok(())
}
/// Parse "lineto" command.
fn parse_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, LineTo, [ point => parse_coord ])
fn parse_lineto(&mut self, by_to: ByTo) -> Result<(), ()> {
parse_arguments!(self, by_to, Line, [ point => parse_coord ])
}
/// Parse horizontal "lineto" command.
fn parse_h_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ])
fn parse_h_lineto(&mut self, by_to: ByTo) -> Result<(), ()> {
parse_arguments!(self, by_to, HLine, [ x => parse_number ])
}
/// Parse vertical "lineto" command.
fn parse_v_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ])
fn parse_v_lineto(&mut self, by_to: ByTo) -> Result<(), ()> {
parse_arguments!(self, by_to, VLine, [ y => parse_number ])
}
/// Parse cubic Bézier curve command.
fn parse_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, CurveTo, [
fn parse_curveto(&mut self, by_to: ByTo) -> Result<(), ()> {
parse_arguments!(self, by_to, CubicCurve, [
control1 => parse_coord, control2 => parse_coord, point => parse_coord
])
}
/// Parse smooth "curveto" command.
fn parse_smooth_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, SmoothCurveTo, [
fn parse_smooth_curveto(&mut self, by_to: ByTo) -> Result<(), ()> {
parse_arguments!(self, by_to, SmoothCubic, [
control2 => parse_coord, point => parse_coord
])
}
/// Parse quadratic Bézier curve command.
fn parse_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, QuadBezierCurveTo, [
fn parse_quadratic_bezier_curveto(&mut self, by_to: ByTo) -> Result<(), ()> {
parse_arguments!(self, by_to, QuadCurve, [
control1 => parse_coord, point => parse_coord
])
}
/// Parse smooth quadratic Bézier curveto command.
fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ])
fn parse_smooth_quadratic_bezier_curveto(&mut self, by_to: ByTo) -> Result<(), ()> {
parse_arguments!(self, by_to, SmoothQuad, [ point => parse_coord ])
}
/// Parse elliptical arc curve command.
fn parse_elliptical_arc(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
fn parse_elliptical_arc(&mut self, by_to: ByTo) -> Result<(), ()> {
// Parse a flag whose value is '0' or '1'; otherwise, return Err(()).
let parse_flag = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() {
Some(c) if c == b'0' || c == b'1' => Ok(ArcFlag(c == b'1')),
let parse_arc_size = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() {
Some(c) if c == b'1' => Ok(ArcSize::Large),
Some(c) if c == b'0' => Ok(ArcSize::Small),
_ => Err(()),
};
parse_arguments!(self, absolute, EllipticalArc, [
rx => parse_number,
ry => parse_number,
angle => parse_number,
large_arc_flag => parse_flag,
sweep_flag => parse_flag,
let parse_arc_sweep = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() {
Some(c) if c == b'1' => Ok(ArcSweep::Cw),
Some(c) if c == b'0' => Ok(ArcSweep::Ccw),
_ => Err(()),
};
parse_arguments!(self, by_to, Arc, [
radii => parse_coord,
rotate => parse_number,
arc_size => parse_arc_size,
arc_sweep => parse_arc_sweep,
point => parse_coord
])
}

View File

@ -243,8 +243,9 @@ where
Self { inner, separator }
}
/// Serialize the CSS Value with the specific serialization function.
#[inline]
fn write_item<F>(&mut self, f: F) -> fmt::Result
pub fn write_item<F>(&mut self, f: F) -> fmt::Result
where
F: FnOnce(&mut CssWriter<'b, W>) -> fmt::Result,
{

View File

@ -757,9 +757,8 @@ renaming_overrides_prefixing = true
}
"""
"CoordPair" = """
explicit StyleCoordPair(const gfx::Point& aPoint): x(aPoint.x), y(aPoint.y) {}
gfx::Point ConvertsToGfxPoint() const { return gfx::Point(x, y); }
"CoordinatePair" = """
inline gfx::Point ToGfxPoint() const;
"""
"TextOverflow" = """
@ -1049,6 +1048,11 @@ renaming_overrides_prefixing = true
}
"""
"GenericShapeCommand" = """
bool IsCubicType() const { return IsCubicCurve() || IsSmoothCubic(); }
bool IsQuadraticType() const { return IsQuadCurve() || IsSmoothQuad(); }
"""
"GenericContainIntrinsicSize" = """
bool HasAuto() const { return IsAutoLength() || IsAutoNone(); }
"""