Bug 691187 - Prune zero-length segments for canvas strokes. r=lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D179462
This commit is contained in:
Andrew Osmond 2023-06-02 12:48:31 +00:00
parent 19abba01c7
commit 9975cde84f
23 changed files with 140 additions and 100 deletions

View file

@ -2955,7 +2955,7 @@ void CanvasRenderingContext2D::BeginPath() {
void CanvasRenderingContext2D::Fill(const CanvasWindingRule& aWinding) { void CanvasRenderingContext2D::Fill(const CanvasWindingRule& aWinding) {
EnsureUserSpacePath(aWinding); EnsureUserSpacePath(aWinding);
if (!mPath) { if (!mPath || mPath->IsEmpty()) {
return; return;
} }
@ -2991,7 +2991,7 @@ void CanvasRenderingContext2D::Fill(const CanvasPath& aPath,
} }
RefPtr<gfx::Path> gfxpath = aPath.GetPath(aWinding, mTarget); RefPtr<gfx::Path> gfxpath = aPath.GetPath(aWinding, mTarget);
if (!gfxpath) { if (!gfxpath || gfxpath->IsEmpty()) {
return; return;
} }
@ -3022,7 +3022,7 @@ void CanvasRenderingContext2D::Fill(const CanvasPath& aPath,
void CanvasRenderingContext2D::Stroke() { void CanvasRenderingContext2D::Stroke() {
EnsureUserSpacePath(); EnsureUserSpacePath();
if (!mPath) { if (!mPath || mPath->IsEmpty()) {
return; return;
} }
@ -3065,7 +3065,7 @@ void CanvasRenderingContext2D::Stroke(const CanvasPath& aPath) {
RefPtr<gfx::Path> gfxpath = RefPtr<gfx::Path> gfxpath =
aPath.GetPath(CanvasWindingRule::Nonzero, mTarget); aPath.GetPath(CanvasWindingRule::Nonzero, mTarget);
if (!gfxpath) { if (!gfxpath || gfxpath->IsEmpty()) {
return; return;
} }
@ -3219,6 +3219,10 @@ void CanvasRenderingContext2D::ArcTo(double aX1, double aY1, double aX2,
Point p1(aX1, aY1); Point p1(aX1, aY1);
Point p2(aX2, aY2); Point p2(aX2, aY2);
if (!p1.IsFinite() || !p2.IsFinite() || !std::isfinite(aRadius)) {
return;
}
// Execute these calculations in double precision to avoid cumulative // Execute these calculations in double precision to avoid cumulative
// rounding errors. // rounding errors.
double dir, a2, b2, c2, cosx, sinx, d, anx, any, bnx, bny, x3, y3, x4, y4, cx, double dir, a2, b2, c2, cosx, sinx, d, anx, any, bnx, bny, x3, y3, x4, y4, cx,
@ -3226,7 +3230,7 @@ void CanvasRenderingContext2D::ArcTo(double aX1, double aY1, double aX2,
bool anticlockwise; bool anticlockwise;
if (p0 == p1 || p1 == p2 || aRadius == 0) { if (p0 == p1 || p1 == p2 || aRadius == 0) {
LineTo(p1.x, p1.y); LineTo(p1);
return; return;
} }
@ -3234,7 +3238,7 @@ void CanvasRenderingContext2D::ArcTo(double aX1, double aY1, double aX2,
dir = (p2.x.value - p1.x.value) * (p0.y.value - p1.y.value) + dir = (p2.x.value - p1.x.value) * (p0.y.value - p1.y.value) +
(p2.y.value - p1.y.value) * (p1.x.value - p0.x.value); (p2.y.value - p1.y.value) * (p1.x.value - p0.x.value);
if (dir == 0) { if (dir == 0) {
LineTo(p1.x, p1.y); LineTo(p1);
return; return;
} }
@ -3285,8 +3289,16 @@ void CanvasRenderingContext2D::Rect(double aX, double aY, double aW,
double aH) { double aH) {
EnsureWritablePath(); EnsureWritablePath();
if (!std::isfinite(aX) || !std::isfinite(aY) || !std::isfinite(aW) ||
!std::isfinite(aH)) {
return;
}
if (mPathBuilder) { if (mPathBuilder) {
mPathBuilder->MoveTo(Point(aX, aY)); mPathBuilder->MoveTo(Point(aX, aY));
if (aW == 0 && aH == 0) {
return;
}
mPathBuilder->LineTo(Point(aX + aW, aY)); mPathBuilder->LineTo(Point(aX + aW, aY));
mPathBuilder->LineTo(Point(aX + aW, aY + aH)); mPathBuilder->LineTo(Point(aX + aW, aY + aH));
mPathBuilder->LineTo(Point(aX, aY + aH)); mPathBuilder->LineTo(Point(aX, aY + aH));
@ -3294,6 +3306,9 @@ void CanvasRenderingContext2D::Rect(double aX, double aY, double aW,
} else { } else {
mDSPathBuilder->MoveTo( mDSPathBuilder->MoveTo(
mTarget->GetTransform().TransformPoint(Point(aX, aY))); mTarget->GetTransform().TransformPoint(Point(aX, aY)));
if (aW == 0 && aH == 0) {
return;
}
mDSPathBuilder->LineTo( mDSPathBuilder->LineTo(
mTarget->GetTransform().TransformPoint(Point(aX + aW, aY))); mTarget->GetTransform().TransformPoint(Point(aX + aW, aY)));
mDSPathBuilder->LineTo( mDSPathBuilder->LineTo(
@ -6310,21 +6325,30 @@ void CanvasPath::ClosePath() {
void CanvasPath::MoveTo(double aX, double aY) { void CanvasPath::MoveTo(double aX, double aY) {
EnsurePathBuilder(); EnsurePathBuilder();
mPathBuilder->MoveTo(Point(ToFloat(aX), ToFloat(aY))); Point pos(ToFloat(aX), ToFloat(aY));
if (!pos.IsFinite()) {
return;
}
mPathBuilder->MoveTo(pos);
} }
void CanvasPath::LineTo(double aX, double aY) { void CanvasPath::LineTo(double aX, double aY) {
EnsurePathBuilder(); LineTo(Point(ToFloat(aX), ToFloat(aY)));
mPathBuilder->LineTo(Point(ToFloat(aX), ToFloat(aY)));
} }
void CanvasPath::QuadraticCurveTo(double aCpx, double aCpy, double aX, void CanvasPath::QuadraticCurveTo(double aCpx, double aCpy, double aX,
double aY) { double aY) {
EnsurePathBuilder(); EnsurePathBuilder();
mPathBuilder->QuadraticBezierTo(gfx::Point(ToFloat(aCpx), ToFloat(aCpy)), Point cp1(ToFloat(aCpx), ToFloat(aCpy));
gfx::Point(ToFloat(aX), ToFloat(aY))); Point cp2(ToFloat(aX), ToFloat(aY));
if (!cp1.IsFinite() || !cp2.IsFinite() ||
(cp1 == mPathBuilder->CurrentPoint() && cp1 == cp2)) {
return;
}
mPathBuilder->QuadraticBezierTo(cp1, cp2);
} }
void CanvasPath::BezierCurveTo(double aCp1x, double aCp1y, double aCp2x, void CanvasPath::BezierCurveTo(double aCp1x, double aCp1y, double aCp2x,
@ -6347,6 +6371,10 @@ void CanvasPath::ArcTo(double aX1, double aY1, double aX2, double aY2,
Point p1(aX1, aY1); Point p1(aX1, aY1);
Point p2(aX2, aY2); Point p2(aX2, aY2);
if (!p1.IsFinite() || !p2.IsFinite() || !std::isfinite(aRadius)) {
return;
}
// Execute these calculations in double precision to avoid cumulative // Execute these calculations in double precision to avoid cumulative
// rounding errors. // rounding errors.
double dir, a2, b2, c2, cosx, sinx, d, anx, any, bnx, bny, x3, y3, x4, y4, cx, double dir, a2, b2, c2, cosx, sinx, d, anx, any, bnx, bny, x3, y3, x4, y4, cx,
@ -6354,7 +6382,7 @@ void CanvasPath::ArcTo(double aX1, double aY1, double aX2, double aY2,
bool anticlockwise; bool anticlockwise;
if (p0 == p1 || p1 == p2 || aRadius == 0) { if (p0 == p1 || p1 == p2 || aRadius == 0) {
LineTo(p1.x, p1.y); LineTo(p1);
return; return;
} }
@ -6362,7 +6390,7 @@ void CanvasPath::ArcTo(double aX1, double aY1, double aX2, double aY2,
dir = (p2.x.value - p1.x.value) * (p0.y.value - p1.y.value) + dir = (p2.x.value - p1.x.value) * (p0.y.value - p1.y.value) +
(p2.y.value - p1.y.value) * (p1.x.value - p0.x.value); (p2.y.value - p1.y.value) * (p1.x.value - p0.x.value);
if (dir == 0) { if (dir == 0) {
LineTo(p1.x, p1.y); LineTo(p1);
return; return;
} }
@ -6397,7 +6425,17 @@ void CanvasPath::ArcTo(double aX1, double aY1, double aX2, double aY2,
} }
void CanvasPath::Rect(double aX, double aY, double aW, double aH) { void CanvasPath::Rect(double aX, double aY, double aW, double aH) {
EnsurePathBuilder();
if (!std::isfinite(aX) || !std::isfinite(aY) || !std::isfinite(aW) ||
!std::isfinite(aH)) {
return;
}
MoveTo(aX, aY); MoveTo(aX, aY);
if (aW == 0 && aH == 0) {
return;
}
LineTo(aX + aW, aY); LineTo(aX + aW, aY);
LineTo(aX + aW, aY + aH); LineTo(aX + aW, aY + aH);
LineTo(aX, aY + aH); LineTo(aX, aY + aH);
@ -6443,6 +6481,10 @@ void CanvasPath::Ellipse(double x, double y, double radiusX, double radiusY,
void CanvasPath::LineTo(const gfx::Point& aPoint) { void CanvasPath::LineTo(const gfx::Point& aPoint) {
EnsurePathBuilder(); EnsurePathBuilder();
if (!aPoint.IsFinite() || aPoint == mPathBuilder->CurrentPoint()) {
return;
}
mPathBuilder->LineTo(aPoint); mPathBuilder->LineTo(aPoint);
} }
@ -6450,6 +6492,11 @@ void CanvasPath::BezierTo(const gfx::Point& aCP1, const gfx::Point& aCP2,
const gfx::Point& aCP3) { const gfx::Point& aCP3) {
EnsurePathBuilder(); EnsurePathBuilder();
if (!aCP1.IsFinite() || !aCP2.IsFinite() || !aCP3.IsFinite() ||
(aCP1 == mPathBuilder->CurrentPoint() && aCP1 == aCP2 && aCP1 == aCP3)) {
return;
}
mPathBuilder->BezierTo(aCP1, aCP2, aCP3); mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
} }

View file

@ -337,11 +337,17 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
void MoveTo(double aX, double aY) override { void MoveTo(double aX, double aY) override {
EnsureWritablePath(); EnsureWritablePath();
mozilla::gfx::Point pos(ToFloat(aX), ToFloat(aY));
if (!pos.IsFinite()) {
return;
}
if (mPathBuilder) { if (mPathBuilder) {
mPathBuilder->MoveTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))); mPathBuilder->MoveTo(pos);
} else { } else {
mDSPathBuilder->MoveTo(mTarget->GetTransform().TransformPoint( mozilla::gfx::Point transformedPos =
mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)))); mTarget->GetTransform().TransformPoint(pos);
mDSPathBuilder->MoveTo(transformedPos);
} }
} }
@ -355,17 +361,25 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
double aY) override { double aY) override {
EnsureWritablePath(); EnsureWritablePath();
mozilla::gfx::Point cp1(ToFloat(aCpx), ToFloat(aCpy));
mozilla::gfx::Point cp2(ToFloat(aX), ToFloat(aY));
if (!cp1.IsFinite() || !cp2.IsFinite()) {
return;
}
if (mPathBuilder) { if (mPathBuilder) {
mPathBuilder->QuadraticBezierTo( if (cp1 == mPathBuilder->CurrentPoint() && cp1 == cp2) {
mozilla::gfx::Point(ToFloat(aCpx), ToFloat(aCpy)), return;
mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))); }
mPathBuilder->QuadraticBezierTo(cp1, cp2);
} else { } else {
mozilla::gfx::Matrix transform = mTarget->GetTransform(); mozilla::gfx::Matrix transform = mTarget->GetTransform();
mDSPathBuilder->QuadraticBezierTo( mozilla::gfx::Point transformedPos = transform.TransformPoint(cp1);
transform.TransformPoint( if (transformedPos == mDSPathBuilder->CurrentPoint() && cp1 == cp2) {
mozilla::gfx::Point(ToFloat(aCpx), ToFloat(aCpy))), return;
transform.TransformPoint( }
mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)))); mDSPathBuilder->QuadraticBezierTo(transformedPos,
transform.TransformPoint(cp2));
} }
} }
@ -501,22 +515,45 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
enum class Style : uint8_t { STROKE = 0, FILL, MAX }; enum class Style : uint8_t { STROKE = 0, FILL, MAX };
void LineTo(const mozilla::gfx::Point& aPoint) { void LineTo(const mozilla::gfx::Point& aPoint) {
if (!aPoint.IsFinite()) {
return;
}
if (mPathBuilder) { if (mPathBuilder) {
if (mPathBuilder->CurrentPoint() == aPoint) {
return;
}
mPathBuilder->LineTo(aPoint); mPathBuilder->LineTo(aPoint);
} else { } else {
mDSPathBuilder->LineTo(mTarget->GetTransform().TransformPoint(aPoint)); mozilla::gfx::Point transformedPt =
mTarget->GetTransform().TransformPoint(aPoint);
if (mDSPathBuilder->CurrentPoint() == transformedPt) {
return;
}
mDSPathBuilder->LineTo(transformedPt);
} }
} }
void BezierTo(const mozilla::gfx::Point& aCP1, void BezierTo(const mozilla::gfx::Point& aCP1,
const mozilla::gfx::Point& aCP2, const mozilla::gfx::Point& aCP2,
const mozilla::gfx::Point& aCP3) { const mozilla::gfx::Point& aCP3) {
if (!aCP1.IsFinite() || !aCP2.IsFinite() || !aCP3.IsFinite()) {
return;
}
if (mPathBuilder) { if (mPathBuilder) {
if (aCP1 == mPathBuilder->CurrentPoint() && aCP1 == aCP2 &&
aCP1 == aCP3) {
return;
}
mPathBuilder->BezierTo(aCP1, aCP2, aCP3); mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
} else { } else {
mozilla::gfx::Matrix transform = mTarget->GetTransform(); mozilla::gfx::Matrix transform = mTarget->GetTransform();
mDSPathBuilder->BezierTo(transform.TransformPoint(aCP1), mozilla::gfx::Point transformedPos = transform.TransformPoint(aCP1);
transform.TransformPoint(aCP2), if (transformedPos == mDSPathBuilder->CurrentPoint() && aCP1 == aCP2 &&
aCP1 == aCP3) {
return;
}
mDSPathBuilder->BezierTo(transformedPos, transform.TransformPoint(aCP2),
transform.TransformPoint(aCP3)); transform.TransformPoint(aCP3));
} }
} }

View file

@ -14252,7 +14252,7 @@ ctx.lineTo(50, 25);
ctx.closePath(); ctx.closePath();
ctx.stroke(); ctx.stroke();
todo_isPixel(ctx, 50,25, 0,255,0,255, 0); isPixel(ctx, 50,25, 0,255,0,255, 0);
} }
</script> </script>
@ -14324,7 +14324,7 @@ ctx.moveTo(50, 25);
ctx.bezierCurveTo(50, 25, 50, 25, 50, 25); ctx.bezierCurveTo(50, 25, 50, 25, 50, 25);
ctx.stroke(); ctx.stroke();
todo_isPixel(ctx, 50,25, 0,255,0,255, 0); isPixel(ctx, 50,25, 0,255,0,255, 0);
} }
</script> </script>
@ -14356,7 +14356,7 @@ ctx.moveTo(50, 25);
ctx.lineTo(50, 25); ctx.lineTo(50, 25);
ctx.stroke(); ctx.stroke();
todo_isPixel(ctx, 50,25, 0,255,0,255, 0); isPixel(ctx, 50,25, 0,255,0,255, 0);
} }
</script> </script>
@ -14389,7 +14389,7 @@ ctx.stroke();
ctx.strokeRect(50, 25, 0, 0); ctx.strokeRect(50, 25, 0, 0);
todo_isPixel(ctx, 50,25, 0,255,0,255, 0); isPixel(ctx, 50,25, 0,255,0,255, 0);
} }
</script> </script>

View file

@ -1007,6 +1007,8 @@ class Path : public external::AtomicRefCounted<Path> {
virtual Point ComputePointAtLength(Float aLength, Point* aTangent = nullptr); virtual Point ComputePointAtLength(Float aLength, Point* aTangent = nullptr);
virtual bool IsEmpty() const { return false; }
protected: protected:
Path(); Path();
void EnsureFlattenedPath(); void EnsureFlattenedPath();

View file

@ -134,6 +134,7 @@ void PathBuilderD2D::LineTo(const Point& aPoint) {
mSink->AddLine(D2DPoint(aPoint)); mSink->AddLine(D2DPoint(aPoint));
mCurrentPoint = aPoint; mCurrentPoint = aPoint;
mFigureEmpty = false;
} }
void PathBuilderD2D::BezierTo(const Point& aCP1, const Point& aCP2, void PathBuilderD2D::BezierTo(const Point& aCP1, const Point& aCP2,
@ -143,6 +144,7 @@ void PathBuilderD2D::BezierTo(const Point& aCP1, const Point& aCP2,
D2D1::BezierSegment(D2DPoint(aCP1), D2DPoint(aCP2), D2DPoint(aCP3))); D2D1::BezierSegment(D2DPoint(aCP1), D2DPoint(aCP2), D2DPoint(aCP3)));
mCurrentPoint = aCP3; mCurrentPoint = aCP3;
mFigureEmpty = false;
} }
void PathBuilderD2D::QuadraticBezierTo(const Point& aCP1, const Point& aCP2) { void PathBuilderD2D::QuadraticBezierTo(const Point& aCP1, const Point& aCP2) {
@ -151,6 +153,7 @@ void PathBuilderD2D::QuadraticBezierTo(const Point& aCP1, const Point& aCP2) {
D2D1::QuadraticBezierSegment(D2DPoint(aCP1), D2DPoint(aCP2))); D2D1::QuadraticBezierSegment(D2DPoint(aCP1), D2DPoint(aCP2)));
mCurrentPoint = aCP2; mCurrentPoint = aCP2;
mFigureEmpty = false;
} }
void PathBuilderD2D::Close() { void PathBuilderD2D::Close() {
@ -248,6 +251,7 @@ void PathBuilderD2D::Arc(const Point& aOrigin, Float aRadius, Float aStartAngle,
} }
mCurrentPoint = endPoint; mCurrentPoint = endPoint;
mFigureEmpty = false;
} }
void PathBuilderD2D::EnsureActive(const Point& aPoint) { void PathBuilderD2D::EnsureActive(const Point& aPoint) {
@ -269,8 +273,8 @@ already_AddRefed<Path> PathBuilderD2D::Finish() {
return nullptr; return nullptr;
} }
return MakeAndAddRef<PathD2D>(mGeometry, mFigureActive, mCurrentPoint, return MakeAndAddRef<PathD2D>(mGeometry, mFigureActive, mFigureEmpty,
mFillRule, mBackendType); mCurrentPoint, mFillRule, mBackendType);
} }
already_AddRefed<PathBuilder> PathD2D::CopyToBuilder(FillRule aFillRule) const { already_AddRefed<PathBuilder> PathD2D::CopyToBuilder(FillRule aFillRule) const {

View file

@ -56,6 +56,7 @@ class PathBuilderD2D : public PathBuilder {
RefPtr<ID2D1PathGeometry> mGeometry; RefPtr<ID2D1PathGeometry> mGeometry;
bool mFigureActive; bool mFigureActive;
bool mFigureEmpty = true;
FillRule mFillRule; FillRule mFillRule;
BackendType mBackendType; BackendType mBackendType;
}; };
@ -63,10 +64,11 @@ class PathBuilderD2D : public PathBuilder {
class PathD2D : public Path { class PathD2D : public Path {
public: public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathD2D, override) MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathD2D, override)
PathD2D(ID2D1PathGeometry* aGeometry, bool aEndedActive, PathD2D(ID2D1PathGeometry* aGeometry, bool aEndedActive, bool aIsEmpty,
const Point& aEndPoint, FillRule aFillRule, BackendType aBackendType) const Point& aEndPoint, FillRule aFillRule, BackendType aBackendType)
: mGeometry(aGeometry), : mGeometry(aGeometry),
mEndedActive(aEndedActive), mEndedActive(aEndedActive),
mIsEmpty(aIsEmpty),
mEndPoint(aEndPoint), mEndPoint(aEndPoint),
mFillRule(aFillRule), mFillRule(aFillRule),
mBackendType(aBackendType) {} mBackendType(aBackendType) {}
@ -93,6 +95,8 @@ class PathD2D : public Path {
virtual FillRule GetFillRule() const { return mFillRule; } virtual FillRule GetFillRule() const { return mFillRule; }
bool IsEmpty() const override { return mIsEmpty; }
ID2D1Geometry* GetGeometry() { return mGeometry; } ID2D1Geometry* GetGeometry() { return mGeometry; }
private: private:
@ -101,6 +105,7 @@ class PathD2D : public Path {
mutable RefPtr<ID2D1PathGeometry> mGeometry; mutable RefPtr<ID2D1PathGeometry> mGeometry;
bool mEndedActive; bool mEndedActive;
bool mIsEmpty;
Point mEndPoint; Point mEndPoint;
FillRule mFillRule; FillRule mFillRule;
BackendType mBackendType; BackendType mBackendType;

View file

@ -7,6 +7,7 @@
#include "PathSkia.h" #include "PathSkia.h"
#include "HelpersSkia.h" #include "HelpersSkia.h"
#include "PathHelpers.h" #include "PathHelpers.h"
#include "mozilla/UniquePtr.h"
#include "skia/include/core/SkPathUtils.h" #include "skia/include/core/SkPathUtils.h"
#include "skia/src/core/SkGeometry.h" #include "skia/src/core/SkGeometry.h"
@ -270,4 +271,11 @@ Maybe<Rect> PathSkia::AsRect() const {
} }
return Nothing(); return Nothing();
} }
bool PathSkia::IsEmpty() const {
// Move/Close/Done segments are not included in the mask so as long as any
// flag is set, we know that the path is non-empty.
return mPath.getSegmentMasks() != 0;
}
} // namespace mozilla::gfx } // namespace mozilla::gfx

View file

@ -95,6 +95,8 @@ class PathSkia : public Path {
const Matrix& aTransform, SkPath& aFillPath, const Matrix& aTransform, SkPath& aFillPath,
const Maybe<Rect>& aClipRect = Nothing()) const; const Maybe<Rect>& aClipRect = Nothing()) const;
bool IsEmpty() const override;
private: private:
friend class DrawTargetSkia; friend class DrawTargetSkia;

View file

@ -1,5 +0,0 @@
[2d.path.stroke.prune.arc.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[Zero-length line segments from arcTo and arc are removed before stroking]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.stroke.prune.closed.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[Zero-length line segments from closed paths are removed before stroking]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.stroke.prune.curve.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed before stroking]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.stroke.prune.line.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[Zero-length line segments from lineTo are removed before stroking]
expected: FAIL

View file

@ -1,5 +0,0 @@
[2d.path.stroke.prune.rect.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[Zero-length line segments from rect and strokeRect are removed before stroking]
expected: FAIL

View file

@ -1,4 +0,0 @@
[2d.path.stroke.prune.arc.html]
[Zero-length line segments from arcTo and arc are removed before stroking]
expected: FAIL

View file

@ -1,4 +0,0 @@
[2d.path.stroke.prune.arc.worker.html]
[Zero-length line segments from arcTo and arc are removed before stroking]
expected: FAIL

View file

@ -1,4 +0,0 @@
[2d.path.stroke.prune.closed.html]
[Zero-length line segments from closed paths are removed before stroking]
expected: FAIL

View file

@ -1,4 +0,0 @@
[2d.path.stroke.prune.closed.worker.html]
[Zero-length line segments from closed paths are removed before stroking]
expected: FAIL

View file

@ -1,4 +0,0 @@
[2d.path.stroke.prune.curve.html]
[Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed before stroking]
expected: FAIL

View file

@ -1,4 +0,0 @@
[2d.path.stroke.prune.curve.worker.html]
[Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed before stroking]
expected: FAIL

View file

@ -1,4 +0,0 @@
[2d.path.stroke.prune.line.html]
[Zero-length line segments from lineTo are removed before stroking]
expected: FAIL

View file

@ -1,4 +0,0 @@
[2d.path.stroke.prune.line.worker.html]
[Zero-length line segments from lineTo are removed before stroking]
expected: FAIL

View file

@ -1,4 +0,0 @@
[2d.path.stroke.prune.rect.html]
[Zero-length line segments from rect and strokeRect are removed before stroking]
expected: FAIL

View file

@ -1,4 +0,0 @@
[2d.path.stroke.prune.rect.worker.html]
[Zero-length line segments from rect and strokeRect are removed before stroking]
expected: FAIL