Bug 1026596. Add ellipse to bezier helper and use it from SVG. r=bas
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Thu, 26 Jun 2014 16:42:12 -0400
changeset 191259 ef327b5ec34df79e9b6b8e4582ec146052fda530
parent 191258 49a9a330ce39c3122aeb81527710f9a1560dfbc7
child 191260 6f2c1e191d9decba8f2e70df1d3ef677b5455863
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbas
bugs1026596
milestone33.0a1
Bug 1026596. Add ellipse to bezier helper and use it from SVG. r=bas This is faster and more accurate. Specifically, the old code would produce non-monontic segments which was causing rasterization differences with skia
content/svg/content/src/SVGEllipseElement.cpp
gfx/2d/PathHelpers.h
--- a/content/svg/content/src/SVGEllipseElement.cpp
+++ b/content/svg/content/src/SVGEllipseElement.cpp
@@ -120,15 +120,15 @@ SVGEllipseElement::BuildPath()
   GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
 
   if (rx <= 0.0f || ry <= 0.0f) {
     return nullptr;
   }
 
   RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
 
-  ArcToBezier(pathBuilder.get(), Point(x, y), Size(rx, ry), 0, Float(2*M_PI), false);
+  EllipseToBezier(pathBuilder.get(), Point(x, y), Size(rx, ry));
 
   return pathBuilder->Finish();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -78,16 +78,61 @@ void ArcToBezier(T* aSink, const Point &
 
     aSink->BezierTo(cp1, cp2, currentEndPoint);
 
     arcSweepLeft -= Float(M_PI / 2.0f);
     currentStartAngle = currentEndAngle;
   }
 }
 
+/* This is basically the ArcToBezier with the parameters for drawing a circle
+ * inlined which vastly simplifies it and avoids a bunch of transcedental function
+ * calls which should make it faster. */
+template <typename T>
+void EllipseToBezier(T* aSink, const Point &aOrigin, const Size &aRadius)
+{
+  Point startPoint(aOrigin.x + aRadius.width,
+                   aOrigin.y);
+
+  aSink->LineTo(startPoint);
+
+  // Calculate kappa constant for partial curve. The sign of angle in the
+  // tangent will actually ensure this is negative for a counter clockwise
+  // sweep, so changing signs later isn't needed.
+  Float kappaFactor = (4.0f / 3.0f) * tan((M_PI/2.0f) / 4.0f);
+  Float kappaX = kappaFactor * aRadius.width;
+  Float kappaY = kappaFactor * aRadius.height;
+  Float cosStartAngle = 1;
+  Float sinStartAngle = 0;
+  for (int i = 0; i < 4; i++) {
+    // We guarantee here the current point is the start point of the next
+    // curve segment.
+    Point currentStartPoint(aOrigin.x + cosStartAngle * aRadius.width,
+                            aOrigin.y + sinStartAngle * aRadius.height);
+    Point currentEndPoint(aOrigin.x + -sinStartAngle * aRadius.width,
+                          aOrigin.y + cosStartAngle * aRadius.height);
+
+    Point tangentStart(-sinStartAngle, cosStartAngle);
+    Point cp1 = currentStartPoint;
+    cp1 += Point(tangentStart.x * kappaX, tangentStart.y * kappaY);
+
+    Point revTangentEnd(cosStartAngle, sinStartAngle);
+    Point cp2 = currentEndPoint;
+    cp2 += Point(revTangentEnd.x * kappaX, revTangentEnd.y * kappaY);
+
+    aSink->BezierTo(cp1, cp2, currentEndPoint);
+
+    // cos(x+pi/2) == -sin(x)
+    // sin(x+pi/2) == cos(x)
+    Float tmp = cosStartAngle;
+    cosStartAngle = -sinStartAngle;
+    sinStartAngle = tmp;
+  }
+}
+
 /**
  * Appends a path represending a rounded rectangle to the path being built by
  * aPathBuilder.
  *
  * aRect           The rectangle to append.
  * aCornerRadii    Contains the radii of the top-left, top-right, bottom-right
  *                 and bottom-left corners, in that order.
  * aDrawClockwise  If set to true, the path will start at the left of the top