Bug 637852. Part 14: Try to use snappable rects to draw solid borders instead of using stroke, when a scaling transform is present. r=joe
authorRobert O'Callahan <robert@ocallahan.org>
Thu, 23 Jun 2011 00:11:28 +1200
changeset 71803 9c05bcab628fb6f0aeea60612c32e77ecc68f2fd
parent 71802 2c7a42271f31aba0507fbec85a6622dba44f45a3
child 71804 9ae31ef1ae05802ac28a5d800b262b5926c1a143
push idunknown
push userunknown
push dateunknown
reviewersjoe
bugs637852
milestone7.0a1
Bug 637852. Part 14: Try to use snappable rects to draw solid borders instead of using stroke, when a scaling transform is present. r=joe The goal here is to avoid antialiased border edges when drawing into high-resolution ThebesLayers. This fixes test failures.
layout/base/nsCSSRenderingBorders.cpp
layout/base/nsCSSRenderingBorders.h
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -192,16 +192,17 @@ nsCSSBorderRenderer::nsCSSBorderRenderer
                 mBorderStyles[0] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[0] : 0,
                 mBorderStyles[1] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[1] : 0,
                 mBorderStyles[2] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[2] : 0));
 
   ComputeBorderCornerDimensions(mOuterRect, mInnerRect, mBorderRadii, &mBorderCornerDimensions);
 
   mOneUnitBorder = CheckFourFloatsEqual(mBorderWidths, 1.0);
   mNoBorderRadius = AllCornersZeroSize(mBorderRadii);
+  mAvoidStroke = PR_FALSE;
 }
 
 /* static */ void
 nsCSSBorderRenderer::ComputeInnerRadii(const gfxCornerSizes& aRadii,
                                        const gfxFloat *aBorderSizes,
                                        gfxCornerSizes *aInnerRadiiRet)
 {
   gfxCornerSizes& iRadii = *aInnerRadiiRet;
@@ -555,17 +556,18 @@ nsCSSBorderRenderer::FillSolidBorder(con
   }
 
   // If we're asked to draw all sides of an equal-sized border,
   // stroking is fastest.  This is a fairly common path, but partial
   // sides is probably second in the list -- there are a bunch of
   // common border styles, such as inset and outset, that are
   // top-left/bottom-right split.
   if (aSides == SIDE_BITS_ALL &&
-      CheckFourFloatsEqual(aBorderSizes, aBorderSizes[0]))
+      CheckFourFloatsEqual(aBorderSizes, aBorderSizes[0]) &&
+      !mAvoidStroke)
   {
     gfxRect r(aOuterRect);
     r.Deflate(aBorderSizes[0] / 2.0);
     mContext->SetLineWidth(aBorderSizes[0]);
 
     mContext->NewPath();
     mContext->Rectangle(r);
     mContext->Stroke();
@@ -630,17 +632,17 @@ nsCSSBorderRenderer::FillSolidBorder(con
     r[NS_SIDE_BOTTOM].x += aBorderSizes[NS_SIDE_LEFT];
     r[NS_SIDE_BOTTOM].width -= aBorderSizes[NS_SIDE_LEFT];
   }
 
   // Filling these one by one is faster than filling them all at once.
   for (PRUint32 i = 0; i < 4; i++) {
     if (aSides & (1 << i)) {
       mContext->NewPath();
-      mContext->Rectangle(r[i]);
+      mContext->Rectangle(r[i], PR_TRUE);
       mContext->Fill();
     }
   }
 }
 
 gfxRGBA
 MakeBorderColor(const gfxRGBA& aColor, const gfxRGBA& aBackgroundColor, BorderColorStyle aBorderColorStyle)
 {
@@ -1386,17 +1388,23 @@ nsCSSBorderRenderer::DrawBorders()
   mInnerRect.Round();
 
   gfxMatrix mat = mContext->CurrentMatrix();
 
   // Clamp the CTM to be pixel-aligned; we do this only
   // for translation-only matrices now, but we could do it
   // if the matrix has just a scale as well.  We should not
   // do it if there's a rotation.
-  if (!mat.HasNonTranslation()) {
+  if (mat.HasNonTranslation()) {
+    if (!mat.HasNonAxisAlignedTransform()) {
+      // Scale + transform. Avoid stroke fast-paths so that we have a chance
+      // of snapping to pixel boundaries.
+      mAvoidStroke = PR_TRUE;
+    }
+  } else {
     mat.x0 = floor(mat.x0 + 0.5);
     mat.y0 = floor(mat.y0 + 0.5);
     mContext->SetMatrix(mat);
   }
 
   PRBool allBordersSameWidth = AllBordersSameWidth();
 
   if (allBordersSameWidth && mBorderWidths[0] == 0.0) {
@@ -1410,34 +1418,36 @@ nsCSSBorderRenderer::DrawBorders()
 
   // 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 &&
-      mNoBorderRadius)
+      mNoBorderRadius &&
+      !mAvoidStroke)
   {
     // Very simple case.
     SetupStrokeStyle(NS_SIDE_TOP);
     gfxRect rect = mOuterRect;
     rect.Deflate(mBorderWidths[0] / 2.0);
     mContext->NewPath();
     mContext->Rectangle(rect);
     mContext->Stroke();
     return;
   }
 
   if (allBordersSame &&
       mCompositeColors[0] == NULL &&
       allBordersSameWidth &&
       mBorderStyles[0] == NS_STYLE_BORDER_STYLE_DOTTED &&
       mBorderWidths[0] < 3 &&
-      mNoBorderRadius)
+      mNoBorderRadius &&
+      !mAvoidStroke)
   {
     // Very simple case. We draw this rectangular dotted borner without
     // antialiasing. The dots should be pixel aligned.
     SetupStrokeStyle(NS_SIDE_TOP);
     
     gfxFloat dash = mBorderWidths[0];
     mContext->SetDash(&dash, 1, 0.5);
     mContext->SetAntialiasMode(gfxContext::MODE_ALIASED);
@@ -1448,17 +1458,18 @@ nsCSSBorderRenderer::DrawBorders()
     mContext->Stroke();
     return;
   }
 
   
   if (allBordersSame &&
       allBordersSameWidth &&
       mCompositeColors[0] == NULL &&
-      mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID)
+      mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID &&
+      !mAvoidStroke)
   {
     NS_FOR_CSS_CORNERS(i) {
       if (mBorderRadii[i].width <= mBorderWidths[0]) {
         noCornerOutsideCenter = false;
       }
       if (mBorderRadii[i].height <= mBorderWidths[0]) {
         noCornerOutsideCenter = false;
       }
@@ -1490,31 +1501,34 @@ nsCSSBorderRenderer::DrawBorders()
 
   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 &&
       allBordersSameWidth &&
       mCompositeColors[0] == NULL &&
       mBorderWidths[0] == 1 &&
-      mNoBorderRadius)
+      mNoBorderRadius &&
+      !mAvoidStroke)
   {
     DrawSingleWidthSolidBorder();
     return;
   }
 
-  if (allBordersSolid && !hasCompositeColors)
+  if (allBordersSolid && !hasCompositeColors &&
+      !mAvoidStroke)
   {
     DrawNoCompositeColorSolidBorder();
     return;
   }
 
   if (allBordersSolid &&
       allBordersSameWidth &&
-      mNoBorderRadius)
+      mNoBorderRadius &&
+      !mAvoidStroke)
   {
     // Easy enough to deal with.
     DrawRectangularCompositeColors();
     return;
   }
 
   // If we have composite colors -and- border radius,
   // then use separate corners so we get OPERATOR_ADD for the corners.
--- a/layout/base/nsCSSRenderingBorders.h
+++ b/layout/base/nsCSSRenderingBorders.h
@@ -132,16 +132,17 @@ struct nsCSSBorderRenderer {
 
   // misc -- which sides to skip, the background color
   PRIntn mSkipSides;
   nscolor mBackgroundColor;
 
   // calculated values
   PRPackedBool mOneUnitBorder;
   PRPackedBool mNoBorderRadius;
+  PRPackedBool mAvoidStroke;
 
   // For all the sides in the bitmask, would they be rendered
   // in an identical color and style?
   PRBool AreBorderSideFinalStylesSame(PRUint8 aSides);
 
   // For the given style, is the given corner a solid color?
   PRBool IsSolidCornerStyle(PRUint8 aStyle, mozilla::css::Corner aCorner);