Bug 809603. Simplify border radius drawing. r=roc
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Mon, 12 Nov 2012 17:55:26 -0800
changeset 113051 0e05ac6ac23d0d71ecdd304c3bb0300e13a3d734
parent 113050 34e2424d10442b234b713f04b596c884160753d7
child 113052 039875aeaf2b4d6c7096a38eec630e5b6f2b74c5
push id17911
push userjmuizelaar@mozilla.com
push dateTue, 13 Nov 2012 01:58:00 +0000
treeherdermozilla-inbound@0e05ac6ac23d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs809603
milestone19.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 809603. Simplify border radius drawing. r=roc This adds a RoundedRect class that we use to do the offsetting. This class can probably also be used elsewhere. With this patch we handle the Gmail checkboxes in a single draw instead of in pieces like we currently do.
gfx/thebes/Makefile.in
gfx/thebes/RoundedRect.h
layout/base/nsCSSRenderingBorders.cpp
--- a/gfx/thebes/Makefile.in
+++ b/gfx/thebes/Makefile.in
@@ -49,16 +49,17 @@ EXPORTS	= \
 	gfxUtils.h \
 	gfxUserFontSet.h \
 	nsSurfaceTexture.h \
 	gfxBaseSharedMemorySurface.h \
 	gfxSharedImageSurface.h \
 	gfxSharedQuartzSurface.h \
 	gfxReusableSurfaceWrapper.h \
 	gfxSVGGlyphs.h \
+	RoundedRect.h \
 	$(NULL)
 
 # gfxSVGGlyphs needs nsDOMParser.h
 LOCAL_INCLUDES += \
 	-I$(topsrcdir)/content/base/public \
 	-I$(topsrcdir)/content/xml/document/src \
 	$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/RoundedRect.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include "gfxRect.h"
+
+namespace mozilla {
+/* A rounded rectangle abstraction.
+ *
+ * This can represent a rectangle with a different pair of radii on each corner.
+ *
+ * Note: CoreGraphics and Direct2D only support rounded rectangle with the same
+ * radii on all corners. However, supporting CSS's border-radius requires the extra flexibility. */
+struct RoundedRect {
+    RoundedRect(gfxRect &aRect, gfxCornerSizes &aCorners) : rect(aRect), corners(aCorners) { }
+    void Deflate(gfxFloat aTopWidth, gfxFloat aBottomWidth, gfxFloat aLeftWidth, gfxFloat aRightWidth) {
+        // deflate the internal rect
+        rect.x += aLeftWidth;
+        rect.y += aTopWidth;
+        rect.width = gfx::gfx_max(0., rect.width - aLeftWidth - aRightWidth);
+        rect.height = gfx::gfx_max(0., rect.height - aTopWidth - aBottomWidth);
+
+        corners.sizes[NS_CORNER_TOP_LEFT].width  = gfx::gfx_max(0., corners.sizes[NS_CORNER_TOP_LEFT].width - aLeftWidth);
+        corners.sizes[NS_CORNER_TOP_LEFT].height = gfx::gfx_max(0., corners.sizes[NS_CORNER_TOP_LEFT].height - aTopWidth);
+
+        corners.sizes[NS_CORNER_TOP_RIGHT].width  = gfx::gfx_max(0., corners.sizes[NS_CORNER_TOP_RIGHT].width - aRightWidth);
+        corners.sizes[NS_CORNER_TOP_RIGHT].height = gfx::gfx_max(0., corners.sizes[NS_CORNER_TOP_RIGHT].height - aTopWidth);
+
+        corners.sizes[NS_CORNER_BOTTOM_LEFT].width  = gfx::gfx_max(0., corners.sizes[NS_CORNER_BOTTOM_LEFT].width - aLeftWidth);
+        corners.sizes[NS_CORNER_BOTTOM_LEFT].height = gfx::gfx_max(0., corners.sizes[NS_CORNER_BOTTOM_LEFT].height - aBottomWidth);
+
+        corners.sizes[NS_CORNER_BOTTOM_RIGHT].width  = gfx::gfx_max(0., corners.sizes[NS_CORNER_BOTTOM_RIGHT].width - aRightWidth);
+        corners.sizes[NS_CORNER_BOTTOM_RIGHT].height = gfx::gfx_max(0., corners.sizes[NS_CORNER_BOTTOM_RIGHT].height - aBottomWidth);
+    }
+    gfxRect rect;
+    gfxCornerSizes corners;
+};
+
+} // namespace mozilla
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -22,16 +22,17 @@
 #include "nsITheme.h"
 #include "nsThemeConstants.h"
 #include "nsIServiceManager.h"
 #include "nsLayoutUtils.h"
 #include "nsINameSpaceManager.h"
 #include "nsBlockFrame.h"
 #include "sampler.h"
 #include "nsExpirationTracker.h"
+#include "RoundedRect.h"
 
 #include "gfxContext.h"
 
 #include "nsCSSRenderingBorders.h"
 
 #include "mozilla/gfx/2D.h"
 #include "gfx2DGlue.h"
 
@@ -1796,17 +1797,16 @@ nsCSSBorderRenderer::DrawBorders()
 
   if (allBordersSameWidth && mBorderWidths[0] == 0.0) {
     // Some of the allBordersSameWidth codepaths depend on the border
     // width being greater than zero.
     return;
   }
 
   bool allBordersSolid;
-  bool noCornerOutsideCenter = true;
 
   // First there's a couple of 'special cases' that have specifically optimized
   // drawing paths, when none of these can be used we move on to the generalized
   // border drawing code.
   if (allBordersSame &&
       mCompositeColors[0] == NULL &&
       allBordersSameWidth &&
       mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID &&
@@ -1843,50 +1843,44 @@ nsCSSBorderRenderer::DrawBorders()
     mContext->NewPath();
     mContext->Rectangle(rect);
     mContext->Stroke();
     return;
   }
 
   
   if (allBordersSame &&
-      allBordersSameWidth &&
       mCompositeColors[0] == NULL &&
       mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID &&
-      !mAvoidStroke)
+      !mAvoidStroke &&
+      !mNoBorderRadius)
   {
-    NS_FOR_CSS_CORNERS(i) {
-      if (mBorderRadii[i].width <= mBorderWidths[0]) {
-        noCornerOutsideCenter = false;
-      }
-      if (mBorderRadii[i].height <= mBorderWidths[0]) {
-        noCornerOutsideCenter = false;
-      }
-    }
+    // Relatively simple case.
+    SetupStrokeStyle(NS_SIDE_TOP);
 
-    // We can only do a stroke here if all border radii centers are inside the
-    // inner rect, otherwise we get rendering artifacts.
+    RoundedRect borderInnerRect(mOuterRect, mBorderRadii);
+    borderInnerRect.Deflate(mBorderWidths[NS_SIDE_TOP],
+                      mBorderWidths[NS_SIDE_BOTTOM],
+                      mBorderWidths[NS_SIDE_LEFT],
+                      mBorderWidths[NS_SIDE_RIGHT]);
 
-    if (noCornerOutsideCenter) {
-      // Relatively simple case.
-      SetupStrokeStyle(NS_SIDE_TOP);
-      mOuterRect.Deflate(mBorderWidths[0] / 2.0);
-      NS_FOR_CSS_CORNERS(corner) {
-        if (mBorderRadii.sizes[corner].height == 0 || mBorderRadii.sizes[corner].width == 0) {
-          continue;
-        }
-        mBorderRadii.sizes[corner].width -= mBorderWidths[0] / 2;
-        mBorderRadii.sizes[corner].height -= mBorderWidths[0] / 2;
-      }
-
-      mContext->NewPath();
-      mContext->RoundedRectangle(mOuterRect, mBorderRadii);
-      mContext->Stroke();
-      return;
-    }
+    // Instead of stroking we just use two paths: an inner and an outer.
+    // This allows us to draw borders that we couldn't when stroking. For example,
+    // borders with a border width >= the border radius. (i.e. when there are
+    // square corners on the inside)
+    //
+    // Further, this approach can be more efficient because the backend
+    // doesn't need to compute an offset curve to stroke the path. We know that
+    // the rounded parts are elipses we can offset exactly and can just compute
+    // a new cubic approximation.
+    mContext->NewPath();
+    mContext->RoundedRectangle(mOuterRect, mBorderRadii, true);
+    mContext->RoundedRectangle(borderInnerRect.rect, borderInnerRect.corners, false);
+    mContext->Fill();
+    return;
   }
 
   bool hasCompositeColors;
 
   allBordersSolid = AllBordersSolid(&hasCompositeColors);
   // This leaves the border corners non-interpolated for single width borders.
   // Doing this is slightly faster and shouldn't be a problem visually.
   if (allBordersSolid &&