diff --git a/dom/canvas/CanvasPath.h b/dom/canvas/CanvasPath.h index 64ceb73d1..e31b375cc 100644 --- a/dom/canvas/CanvasPath.h +++ b/dom/canvas/CanvasPath.h @@ -51,6 +51,9 @@ public: void Arc(double x, double y, double radius, double startAngle, double endAngle, bool anticlockwise, ErrorResult& error); + void Ellipse(double x, double y, double radiusX, double radiusY, + double rotation, double startAngle, double endAngle, + bool anticlockwise, ErrorResult& error); void LineTo(const gfx::Point& aPoint); void BezierTo(const gfx::Point& aCP1, diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 7cf67d04a..dd70cbb8d 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -2498,8 +2498,11 @@ CanvasRenderingContext2D::UpdateFilter() // static bool -ValidateRect(double& aX, double& aY, double& aWidth, double& aHeight) +ValidateRect(double& aX, double& aY, double& aWidth, double& aHeight, bool aIsZeroSizeValid) { + if (!aIsZeroSizeValid && (aWidth == 0.0 || aHeight == 0.0)) { + return false; + } // bug 1018527 // The values of canvas API input are in double precision, but Moz2D APIs are @@ -2530,7 +2533,8 @@ void CanvasRenderingContext2D::ClearRect(double x, double y, double w, double h) { - if(!ValidateRect(x, y, w, h)) { + // Do not allow zeros - it's a no-op at that point per spec. + if (!ValidateRect(x, y, w, h, false)) { return; } @@ -2547,7 +2551,7 @@ CanvasRenderingContext2D::FillRect(double x, double y, double w, { const ContextState &state = CurrentState(); - if(!ValidateRect(x, y, w, h)) { + if(!ValidateRect(x, y, w, h, true)) { return; } @@ -2625,7 +2629,7 @@ CanvasRenderingContext2D::StrokeRect(double x, double y, double w, return; } - if(!ValidateRect(x, y, w, h)) { + if(!ValidateRect(x, y, w, h, true)) { return; } @@ -2991,7 +2995,7 @@ CanvasRenderingContext2D::Arc(double x, double y, double r, double startAngle, double endAngle, bool anticlockwise, ErrorResult& error) { - if (r < 0.0) { + if (MOZ_UNLIKELY(r < 0.0)) { error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); return; } @@ -3021,6 +3025,22 @@ CanvasRenderingContext2D::Rect(double x, double y, double w, double h) } } +void +CanvasRenderingContext2D::Ellipse(double aX, double aY, double aRadiusX, double aRadiusY, + double aRotation, double aStartAngle, double aEndAngle, + bool aAnticlockwise, ErrorResult& aError) +{ + if (MOZ_UNLIKELY(aRadiusX < 0.0 || aRadiusY < 0.0)) { + aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return; + } + + EnsureWritablePath(); + + ArcToBezier(this, Point(aX, aY), Size(aRadiusX, aRadiusY), aStartAngle, aEndAngle, + aAnticlockwise, aRotation); +} + void CanvasRenderingContext2D::EnsureWritablePath() { @@ -4402,8 +4422,8 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& image, MOZ_ASSERT(optional_argc == 0 || optional_argc == 2 || optional_argc == 6); if (optional_argc == 6) { - if (!ValidateRect(sx, sy, sw, sh) || - !ValidateRect(dx, dy, dw, dh)) { + if (!ValidateRect(sx, sy, sw, sh, true) || + !ValidateRect(dx, dy, dw, dh, true)) { return; } } @@ -5918,6 +5938,22 @@ CanvasPath::Arc(double x, double y, double radius, ArcToBezier(this, Point(x, y), Size(radius, radius), startAngle, endAngle, anticlockwise); } +void +CanvasPath::Ellipse(double x, double y, double radiusX, double radiusY, + double rotation, double startAngle, double endAngle, + bool anticlockwise, ErrorResult& error) +{ + if (MOZ_UNLIKELY(radiusX < 0.0 || radiusY < 0.0)) { + error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return; + } + + EnsurePathBuilder(); + + ArcToBezier(this, Point(x, y), Size(radiusX, radiusY), startAngle, endAngle, + anticlockwise, rotation); +} + void CanvasPath::LineTo(const gfx::Point& aPoint) { diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index ece78e589..5ff594a50 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -347,6 +347,9 @@ public: void Rect(double x, double y, double w, double h); void Arc(double x, double y, double radius, double startAngle, double endAngle, bool anticlockwise, mozilla::ErrorResult& error); + void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY, + double aRotation, double aStartAngle, double aEndAngle, + bool aAnticlockwise, ErrorResult& aError); void GetMozCurrentTransform(JSContext* cx, JS::MutableHandle result, diff --git a/dom/webidl/CanvasRenderingContext2D.webidl b/dom/webidl/CanvasRenderingContext2D.webidl index eaf74a182..dd758b2e1 100644 --- a/dom/webidl/CanvasRenderingContext2D.webidl +++ b/dom/webidl/CanvasRenderingContext2D.webidl @@ -298,7 +298,9 @@ interface CanvasPathMethods { [Throws, LenientFloat] void arc(double x, double y, double radius, double startAngle, double endAngle, optional boolean anticlockwise = false); -// NOT IMPLEMENTED [LenientFloat] void ellipse(double x, double y, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, boolean anticlockwise); + + [Throws, LenientFloat] + void ellipse(double x, double y, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, optional boolean anticlockwise = false); }; interface CanvasGradient { diff --git a/gfx/2d/PathHelpers.h b/gfx/2d/PathHelpers.h index 1eb2d1c79..a5090485e 100644 --- a/gfx/2d/PathHelpers.h +++ b/gfx/2d/PathHelpers.h @@ -34,7 +34,8 @@ inline void PartialArcToBezier(T* aSink, const Size& aRadius, const Point& aStartPoint, const Point& aEndPoint, const Point& aStartOffset, const Point& aEndOffset, - Float aKappaFactor = kKappaFactor) + Float aKappaFactor = kKappaFactor, + const Matrix& aTransform = Matrix()) { Float kappaX = aKappaFactor * aRadius.width; Float kappaY = aKappaFactor * aRadius.height; @@ -45,7 +46,7 @@ inline void PartialArcToBezier(T* aSink, Point cp2 = aEndPoint + Point(aEndOffset.y * kappaX, -aEndOffset.x * kappaY); - aSink->BezierTo(cp1, cp2, aEndPoint); + aSink->BezierTo(aTransform * cp1, aTransform * cp2, aTransform * aEndPoint); } /** @@ -90,7 +91,8 @@ inline void AcuteArcToBezier(T* aSink, template void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius, - float aStartAngle, float aEndAngle, bool aAntiClockwise) + float aStartAngle, float aEndAngle, bool aAntiClockwise, + float aRotation = 0.0f) { Float sweepDirection = aAntiClockwise ? -1.0f : 1.0f; @@ -111,23 +113,24 @@ void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius, Float currentStartAngle = aStartAngle; Point currentStartOffset(cosf(aStartAngle), sinf(aStartAngle)); - Point currentStartPoint(aOrigin.x + currentStartOffset.x * aRadius.width, - aOrigin.y + currentStartOffset.y * aRadius.height); - - aSink->LineTo(currentStartPoint); + Point currentStartPoint(currentStartOffset.x * aRadius.width, + currentStartOffset.y * aRadius.height); + Matrix transform(cosf(aRotation), sinf(aRotation), -sinf(aRotation), cosf(aRotation), aOrigin.x, aOrigin.y); + aSink->LineTo(transform * currentStartPoint); while (arcSweepLeft > 0) { Float currentEndAngle = currentStartAngle + std::min(arcSweepLeft, Float(M_PI / 2.0f)) * sweepDirection; Point currentEndOffset(cosf(currentEndAngle), sinf(currentEndAngle)); - Point currentEndPoint(aOrigin.x + currentEndOffset.x * aRadius.width, - aOrigin.y + currentEndOffset.y * aRadius.height); + Point currentEndPoint(currentEndOffset.x * aRadius.width, + currentEndOffset.y * aRadius.height); PartialArcToBezier(aSink, aRadius, currentStartPoint, currentEndPoint, currentStartOffset, currentEndOffset, - ComputeKappaFactor(currentEndAngle - currentStartAngle)); + ComputeKappaFactor(currentEndAngle - currentStartAngle), + transform); // We guarantee here the current point is the start point of the next // curve segment.