Bug 1090494, part 1 - Add a RectCornerRadii struct to Moz2D's PathHelpers.h and add variants of AppendRoundedRectToPath and MakePathForRoundedRect that use it. r=mattwoodrow
authorJonathan Watt <jwatt@jwatt.org>
Thu, 30 Oct 2014 09:34:09 +0000
changeset 213072 8fff7a04f3221954330139121bb9b3a7513724a6
parent 213071 32a7f74c5c10f0c53ca47b4a73fe4d61fbcafd04
child 213073 71cf4fde05c6bc27ca61d541d7419e9bf27e6789
push id27738
push usercbook@mozilla.com
push dateThu, 30 Oct 2014 13:46:07 +0000
treeherdermozilla-central@1aa1b23d799e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1090494
milestone36.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 1090494, part 1 - Add a RectCornerRadii struct to Moz2D's PathHelpers.h and add variants of AppendRoundedRectToPath and MakePathForRoundedRect that use it. r=mattwoodrow
gfx/2d/PathHelpers.cpp
gfx/2d/PathHelpers.h
--- a/gfx/2d/PathHelpers.cpp
+++ b/gfx/2d/PathHelpers.cpp
@@ -8,18 +8,17 @@
 namespace mozilla {
 namespace gfx {
 
 UserDataKey sDisablePixelSnapping;
 
 void
 AppendRoundedRectToPath(PathBuilder* aPathBuilder,
                         const Rect& aRect,
-                        // paren's needed due to operator precedence:
-                        const Size(& aCornerRadii)[4],
+                        const RectCornerRadii& aRadii,
                         bool aDrawClockwise)
 {
   // For CW drawing, this looks like:
   //
   //  ...******0**      1    C
   //              ****
   //                  ***    2
   //                     **
@@ -101,70 +100,67 @@ AppendRoundedRectToPath(PathBuilder* aPa
 
   twoFloats *cornerMults = aDrawClockwise ? cwCornerMults : ccwCornerMults;
 
   Point cornerCoords[] = { aRect.TopLeft(), aRect.TopRight(),
                            aRect.BottomRight(), aRect.BottomLeft() };
 
   Point pc, p0, p1, p2, p3;
 
-  // The indexes of the corners:
-  const int kTopLeft = 0, kTopRight = 1;
-
   if (aDrawClockwise) {
-    aPathBuilder->MoveTo(Point(aRect.X() + aCornerRadii[kTopLeft].width,
+    aPathBuilder->MoveTo(Point(aRect.X() + aRadii[RectCorner::TopLeft].width,
                                aRect.Y()));
   } else {
-    aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aCornerRadii[kTopRight].width,
+    aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aRadii[RectCorner::TopRight].width,
                                aRect.Y()));
   }
 
   for (int i = 0; i < 4; ++i) {
     // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
     int c = aDrawClockwise ? ((i+1) % 4) : ((4-i) % 4);
 
     // i+2 and i+3 respectively.  These are used to index into the corner
     // multiplier table, and were deduced by calculating out the long form
     // of each corner and finding a pattern in the signs and values.
     int i2 = (i+2) % 4;
     int i3 = (i+3) % 4;
 
     pc = cornerCoords[c];
 
-    if (aCornerRadii[c].width > 0.0 && aCornerRadii[c].height > 0.0) {
-      p0.x = pc.x + cornerMults[i].a * aCornerRadii[c].width;
-      p0.y = pc.y + cornerMults[i].b * aCornerRadii[c].height;
+    if (aRadii[c].width > 0.0 && aRadii[c].height > 0.0) {
+      p0.x = pc.x + cornerMults[i].a * aRadii[c].width;
+      p0.y = pc.y + cornerMults[i].b * aRadii[c].height;
 
-      p3.x = pc.x + cornerMults[i3].a * aCornerRadii[c].width;
-      p3.y = pc.y + cornerMults[i3].b * aCornerRadii[c].height;
+      p3.x = pc.x + cornerMults[i3].a * aRadii[c].width;
+      p3.y = pc.y + cornerMults[i3].b * aRadii[c].height;
 
-      p1.x = p0.x + alpha * cornerMults[i2].a * aCornerRadii[c].width;
-      p1.y = p0.y + alpha * cornerMults[i2].b * aCornerRadii[c].height;
+      p1.x = p0.x + alpha * cornerMults[i2].a * aRadii[c].width;
+      p1.y = p0.y + alpha * cornerMults[i2].b * aRadii[c].height;
 
-      p2.x = p3.x - alpha * cornerMults[i3].a * aCornerRadii[c].width;
-      p2.y = p3.y - alpha * cornerMults[i3].b * aCornerRadii[c].height;
+      p2.x = p3.x - alpha * cornerMults[i3].a * aRadii[c].width;
+      p2.y = p3.y - alpha * cornerMults[i3].b * aRadii[c].height;
 
       aPathBuilder->LineTo(p0);
       aPathBuilder->BezierTo(p1, p2, p3);
     } else {
       aPathBuilder->LineTo(pc);
     }
   }
 
   aPathBuilder->Close();
 }
 
 void
 AppendEllipseToPath(PathBuilder* aPathBuilder,
                     const Point& aCenter,
                     const Size& aDimensions)
 {
-  Size halfDim = aDimensions / 2.0;
+  Size halfDim = aDimensions / 2.f;
   Rect rect(aCenter - Point(halfDim.width, halfDim.height), aDimensions);
-  Size radii[] = { halfDim, halfDim, halfDim, halfDim };
+  RectCornerRadii radii(halfDim.width, halfDim.height);
 
   AppendRoundedRectToPath(aPathBuilder, rect, radii);
 }
 
 bool
 SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
                                   const DrawTarget& aDrawTarget)
 {
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_PATHHELPERS_H_
 #define MOZILLA_GFX_PATHHELPERS_H_
 
 #include "2D.h"
 #include "mozilla/Constants.h"
+#include "mozilla/TypedEnum.h"
 #include "UserData.h"
 
 namespace mozilla {
 namespace gfx {
 
 template <typename T>
 void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
                  float aStartAngle, float aEndAngle, bool aAntiClockwise)
@@ -124,41 +125,133 @@ void EllipseToBezier(T* aSink, const Poi
     // cos(x+pi/2) == -sin(x)
     // sin(x+pi/2) == cos(x)
     Float tmp = cosStartAngle;
     cosStartAngle = -sinStartAngle;
     sinStartAngle = tmp;
   }
 }
 
+// We can't use MOZ_BEGIN_ENUM_CLASS here because that prevents the enum
+// values from being used for indexing. Wrapping the enum in a struct does at
+// least gives us name scoping.
+struct RectCorner {
+  enum {
+    // This order is important since AppendRoundedRectToPath and other code
+    // depends on it!
+    TopLeft = 0,
+    TopRight = 1,
+    BottomRight = 2,
+    BottomLeft = 3,
+    Count = 4
+  };
+};
+
+struct RectCornerRadii {
+  Size radii[RectCorner::Count];
+
+  RectCornerRadii() {}
+
+  explicit RectCornerRadii(Float radius) {
+    for (int i = 0; i < RectCorner::Count; i++) {
+      radii[i].SizeTo(radius, radius);
+    }
+  }
+
+  explicit RectCornerRadii(Float radiusX, Float radiusY) {
+    for (int i = 0; i < RectCorner::Count; i++) {
+      radii[i].SizeTo(radiusX, radiusY);
+    }
+  }
+
+  RectCornerRadii(Float tl, Float tr, Float br, Float bl) {
+    radii[RectCorner::TopLeft].SizeTo(tl, tl);
+    radii[RectCorner::TopRight].SizeTo(tr, tr);
+    radii[RectCorner::BottomRight].SizeTo(br, br);
+    radii[RectCorner::BottomLeft].SizeTo(bl, bl);
+  }
+
+  RectCornerRadii(const Size& tl, const Size& tr,
+                  const Size& br, const Size& bl) {
+    radii[RectCorner::TopLeft] = tl;
+    radii[RectCorner::TopRight] = tr;
+    radii[RectCorner::BottomRight] = br;
+    radii[RectCorner::BottomLeft] = bl;
+  }
+
+  const Size& operator[](size_t aCorner) const {
+    return radii[aCorner];
+  }
+
+  Size& operator[](size_t aCorner) {
+    return radii[aCorner];
+  }
+
+  void Scale(Float aXScale, Float aYScale) {
+    for (int i = 0; i < RectCorner::Count; i++) {
+      radii[i].Scale(aXScale, aYScale);
+    }
+  }
+
+  const Size TopLeft() const { return radii[RectCorner::TopLeft]; }
+  Size& TopLeft() { return radii[RectCorner::TopLeft]; }
+
+  const Size TopRight() const { return radii[RectCorner::TopRight]; }
+  Size& TopRight() { return radii[RectCorner::TopRight]; }
+
+  const Size BottomRight() const { return radii[RectCorner::BottomRight]; }
+  Size& BottomRight() { return radii[RectCorner::BottomRight]; }
+
+  const Size BottomLeft() const { return radii[RectCorner::BottomLeft]; }
+  Size& BottomLeft() { return radii[RectCorner::BottomLeft]; }
+};
+
 /**
  * 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
  *                 left edge and draw clockwise. If set to false the path will
  *                 start at the right of the top left edge and draw counter-
  *                 clockwise.
  */
 GFX2D_API void AppendRoundedRectToPath(PathBuilder* aPathBuilder,
                                        const Rect& aRect,
-                                       const Size(& aCornerRadii)[4],
+                                       const RectCornerRadii& aRadii,
                                        bool aDrawClockwise = true);
 
 inline TemporaryRef<Path> MakePathForRoundedRect(const DrawTarget& aDrawTarget,
                                                  const Rect& aRect,
+                                                 const RectCornerRadii& aRadii,
+                                                 bool aDrawClockwise = true)
+{
+  RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+  AppendRoundedRectToPath(builder, aRect, aRadii, aDrawClockwise);
+  return builder->Finish();
+}
+
+inline void AppendRoundedRectToPath(PathBuilder* aPathBuilder,
+                                    const Rect& aRect,
+                                    const Size(& aCornerRadii)[4],
+                                    bool aDrawClockwise = true) {
+  RectCornerRadii radii(aCornerRadii[0], aCornerRadii[1],
+                        aCornerRadii[2], aCornerRadii[3]);
+  AppendRoundedRectToPath(aPathBuilder, aRect, radii, aDrawClockwise);
+}
+
+inline TemporaryRef<Path> MakePathForRoundedRect(const DrawTarget& aDrawTarget,
+                                                 const Rect& aRect,
                                                  const Size(& aCornerRadii)[4],
-                                                 bool aDrawClockwise = true)
-{
-  RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
-  AppendRoundedRectToPath(builder, aRect, aCornerRadii, aDrawClockwise);
-  return builder->Finish();
+                                                 bool aDrawClockwise = true) {
+  RectCornerRadii radii(aCornerRadii[0], aCornerRadii[1],
+                        aCornerRadii[2], aCornerRadii[3]);
+  return MakePathForRoundedRect(aDrawTarget, aRect, radii, aDrawClockwise);
 }
 
 /**
  * Appends a path represending an ellipse to the path being built by
  * aPathBuilder.
  *
  * The ellipse extends aDimensions.width / 2.0 in the horizontal direction
  * from aCenter, and aDimensions.height / 2.0 in the vertical direction.