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 212055 ef327b5ec34df79e9b6b8e4582ec146052fda530
parent 212054 49a9a330ce39c3122aeb81527710f9a1560dfbc7
child 212056 6f2c1e191d9decba8f2e70df1d3ef677b5455863
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs1026596
milestone33.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 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