Bug 910138 - Part 1. Add support for new canvas ellipse method. r=roc, r=smaug
authorEthan Lin <ethlin@mozilla.com>
Tue, 15 Mar 2016 00:53:00 +0100
changeset 327062 1a5af4e551d442b67bf95d4a1df40882056db482
parent 327061 deabc24fe6ed9aa10ab6af4edb704365bae9d8a1
child 327063 0a779ce8d9708b12f816656485c4c2198ff99085
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, smaug
bugs910138
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 910138 - Part 1. Add support for new canvas ellipse method. r=roc, r=smaug
dom/canvas/CanvasPath.h
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
dom/webidl/CanvasRenderingContext2D.webidl
gfx/2d/PathHelpers.h
--- a/dom/canvas/CanvasPath.h
+++ b/dom/canvas/CanvasPath.h
@@ -46,16 +46,19 @@ public:
                      double cp2x, double cp2y,
                      double x, double y);
   void ArcTo(double x1, double y1, double x2, double y2, double radius,
              ErrorResult& error);
   void Rect(double x, double y, double w, double h);
   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,
                 const gfx::Point& aCP2,
                 const gfx::Point& aCP3);
 
   already_AddRefed<gfx::Path> GetPath(const CanvasWindingRule& aWinding,
                                   const gfx::DrawTarget* aTarget) const;
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -3041,16 +3041,32 @@ CanvasRenderingContext2D::Rect(double aX
     mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(aX + aW, aY));
     mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(aX + aW, aY + aH));
     mDSPathBuilder->LineTo(mTarget->GetTransform() * Point(aX, aY + aH));
     mDSPathBuilder->Close();
   }
 }
 
 void
+CanvasRenderingContext2D::Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
+                                  double aRotation, double aStartAngle, double aEndAngle,
+                                  bool aAnticlockwise, ErrorResult& aError)
+{
+  if (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()
 {
   EnsureTarget();
 
   if (mDSPathBuilder) {
     return;
   }
 
@@ -5937,16 +5953,32 @@ CanvasPath::Arc(double aX, double aY, do
   }
 
   EnsurePathBuilder();
 
   ArcToBezier(this, Point(aX, aY), Size(aRadius, aRadius), aStartAngle, aEndAngle, aAnticlockwise);
 }
 
 void
+CanvasPath::Ellipse(double x, double y, double radiusX, double radiusY,
+                    double rotation, double startAngle, double endAngle,
+                    bool anticlockwise, ErrorResult& error)
+{
+  if (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)
 {
   EnsurePathBuilder();
 
   mPathBuilder->LineTo(aPoint);
 }
 
 void
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -341,16 +341,19 @@ public:
              mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
   }
 
   void ArcTo(double aX1, double aY1, double aX2, double aY2, double aRadius,
              mozilla::ErrorResult& aError);
   void Rect(double aX, double aY, double aW, double aH);
   void Arc(double aX, double aY, double aRadius, double aStartAngle,
            double aEndAngle, bool aAnticlockwise, mozilla::ErrorResult& aError);
+  void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
+               double aRotation, double aStartAngle, double aEndAngle,
+               bool aAnticlockwise, ErrorResult& aError);
 
   void GetMozCurrentTransform(JSContext* aCx,
                               JS::MutableHandle<JSObject*> aResult,
                               mozilla::ErrorResult& aError) const;
   void SetMozCurrentTransform(JSContext* aCx,
                               JS::Handle<JSObject*> aCurrentTransform,
                               mozilla::ErrorResult& aError);
   void GetMozCurrentTransformInverse(JSContext* aCx,
--- a/dom/webidl/CanvasRenderingContext2D.webidl
+++ b/dom/webidl/CanvasRenderingContext2D.webidl
@@ -293,17 +293,19 @@ interface CanvasPathMethods {
   void arcTo(double x1, double y1, double x2, double y2, double radius); 
 // NOT IMPLEMENTED  [LenientFloat] void arcTo(double x1, double y1, double x2, double y2, double radiusX, double radiusY, double rotation);
 
   [LenientFloat]
   void rect(double x, double y, double w, double h);
 
   [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 {
   // opaque object
   [Throws]
   // addColorStop should take a double
   void addColorStop(float offset, DOMString color);
 };
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -29,28 +29,29 @@ inline Float ComputeKappaFactor(Float aA
  * Draws a partial arc <= 90 degrees given exact start and end points.
  * Assumes that it is continuing from an already specified start point.
  */
 template <typename T>
 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;
 
   Point cp1 =
     aStartPoint + Point(-aStartOffset.y * kappaX, aStartOffset.x * kappaY);
 
   Point cp2 =
     aEndPoint + Point(aEndOffset.y * kappaX, -aEndOffset.x * kappaY);
 
-  aSink->BezierTo(cp1, cp2, aEndPoint);
+  aSink->BezierTo(aTransform * cp1, aTransform * cp2, aTransform * aEndPoint);
 }
 
 /**
  * Draws an acute arc (<= 90 degrees) given exact start and end points.
  * Specialized version avoiding kappa calculation.
  */
 template <typename T>
 inline void AcuteArcToBezier(T* aSink,
@@ -85,17 +86,18 @@ inline void AcuteArcToBezier(T* aSink,
                              Float aStartAngle, Float aEndAngle)
 {
   AcuteArcToBezier(aSink, aOrigin, aRadius, aStartPoint, aEndPoint,
                    ComputeKappaFactor(aEndAngle - aStartAngle));
 }
 
 template <typename T>
 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;
 
   // Calculate the total arc we're going to sweep.
   Float arcSweepLeft = (aEndAngle - aStartAngle) * sweepDirection;
 
   // Clockwise we always sweep from the smaller to the larger angle, ccw
   // it's vice versa.
@@ -106,33 +108,34 @@ void ArcToBezier(T* aSink, const Point &
     aStartAngle = aEndAngle - arcSweepLeft * sweepDirection;
   } else if (arcSweepLeft > Float(2.0f * M_PI)) {
     // Sweeping more than 2 * pi is a full circle.
     arcSweepLeft = Float(2.0f * M_PI);
   }
 
   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.
     arcSweepLeft -= Float(M_PI / 2.0f);
     currentStartAngle = currentEndAngle;
     currentStartOffset = currentEndOffset;
     currentStartPoint = currentEndPoint;
   }