Bug 588271 - Part 4: Add specialized rendering codepaths for the most common border types. r=vlad a=blocking-betan
authorBas Schouten <bschouten@mozilla.com>
Thu, 02 Dec 2010 14:17:58 +0100
changeset 58478 9987d0724a100d27241048079bf898fb42345443
parent 58477 8edf6bda5fc2dd756c3f0d2d548165b7379de365
child 58479 ae06445eeff1becaeb387c4be1c1378928fa49f4
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersvlad, blocking-betan
bugs588271
milestone2.0b8pre
Bug 588271 - Part 4: Add specialized rendering codepaths for the most common border types. r=vlad a=blocking-betan
layout/base/nsCSSRenderingBorders.cpp
layout/base/nsCSSRenderingBorders.h
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  *   Mozilla Corporation
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Vladimir Vukicevic <vladimir@pobox.com>
+ *   Bas Schouten <bschouten@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -68,16 +69,18 @@
 #include "nsCSSRenderingBorders.h"
 
 /**
  * nsCSSRendering::PaintBorder
  * nsCSSRendering::PaintOutline
  *   -> DrawBorders
  *
  * DrawBorders
+ *   -> Ability to use specialized approach?
+ *      |- Draw using specialized function
  *   |- separate corners?
  *   |- dashed side mask
  *   |
  *   -> can border be drawn in 1 pass? (e.g., solid border same color all around)
  *      |- DrawBorderSides with all 4 sides
  *   -> more than 1 pass?
  *      |- for each corner
  *         |- clip to DoCornerClipSubPath
@@ -180,17 +183,20 @@ nsCSSBorderRenderer::nsCSSBorderRenderer
     mBackgroundColor(aBackgroundColor)
 {
   if (!mCompositeColors) {
     static nsBorderColors * const noColors[4] = { NULL };
     mCompositeColors = &noColors[0];
   }
 
   mInnerRect = mOuterRect;
-  mInnerRect.Inset(mBorderWidths[0], mBorderWidths[1], mBorderWidths[2], mBorderWidths[3]);
+  mInnerRect.Inset(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,
+                   mBorderStyles[3] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[3] : 0);
 
   ComputeBorderCornerDimensions(mOuterRect, mInnerRect, mBorderRadii, &mBorderCornerDimensions);
 
   mOneUnitBorder = CheckFourFloatsEqual(mBorderWidths, 1.0);
   mNoBorderRadius = AllCornersZeroSize(mBorderRadii);
 }
 
 /* static */ void
@@ -1003,16 +1009,357 @@ nsCSSBorderRenderer::DrawDashedSide(mozi
   mContext->LineTo(end);
   mContext->SetLineWidth(borderWidth);
   mContext->SetColor(gfxRGBA(borderColor));
   //mContext->SetColor(gfxRGBA(1.0, 0.0, 0.0, 1.0));
   mContext->Stroke();
 }
 
 void
+nsCSSBorderRenderer::SetupStrokeStyle(mozilla::css::Side aSide)
+{
+  mContext->SetColor(gfxRGBA(mBorderColors[aSide]));
+  mContext->SetLineWidth(mBorderWidths[aSide]);
+}
+
+bool
+nsCSSBorderRenderer::AllBordersSameWidth()
+{
+  if (mBorderWidths[0] == mBorderWidths[1] &&
+      mBorderWidths[0] == mBorderWidths[2] &&
+      mBorderWidths[0] == mBorderWidths[3])
+  {
+    return true;
+  }
+
+  return false;
+}
+
+bool
+nsCSSBorderRenderer::AllBordersSolid(bool *aHasCompositeColors)
+{
+  *aHasCompositeColors = false;
+  NS_FOR_CSS_SIDES(i) {
+    if (mCompositeColors[i] != nsnull) {
+      *aHasCompositeColors = true;
+    }
+    if (mBorderStyles[i] == NS_STYLE_BORDER_STYLE_SOLID ||
+        mBorderStyles[i] == NS_STYLE_BORDER_STYLE_NONE ||
+        mBorderStyles[i] == NS_STYLE_BORDER_STYLE_HIDDEN)
+    {
+      continue;
+    }
+    return false;
+  }
+
+  return true;
+}
+
+bool IsVisible(int aStyle)
+{
+  if (aStyle != NS_STYLE_BORDER_STYLE_NONE &&
+      aStyle != NS_STYLE_BORDER_STYLE_HIDDEN) {
+        return true;
+  }
+  return false;
+}
+
+already_AddRefed<gfxPattern>
+nsCSSBorderRenderer::CreateCornerGradient(mozilla::css::Corner aCorner,
+                                          const gfxRGBA &aFirstColor,
+                                          const gfxRGBA &aSecondColor)
+{
+  typedef struct { gfxFloat a, b; } twoFloats;
+
+  const twoFloats gradientCoeff[4] = { { -1, +1 },
+                                       { -1, -1 },
+                                       { +1, -1 },
+                                       { +1, +1 } };
+  
+  // Sides which form the 'width' and 'height' for the calculation of the angle
+  // for our gradient.
+  const int cornerWidth[4] = { 3, 1, 1, 3 };
+  const int cornerHeight[4] = { 0, 0, 2, 2 };
+
+  gfxPoint cornerOrigin = mOuterRect.AtCorner(aCorner);
+
+  gfxPoint pat1, pat2;
+  pat1.x = cornerOrigin.x +
+    mBorderWidths[cornerHeight[aCorner]] * gradientCoeff[aCorner].a;
+  pat1.y = cornerOrigin.y +
+    mBorderWidths[cornerWidth[aCorner]]  * gradientCoeff[aCorner].b;
+  pat2.x = cornerOrigin.x -
+    mBorderWidths[cornerHeight[aCorner]] * gradientCoeff[aCorner].a;
+  pat2.y = cornerOrigin.y -
+    mBorderWidths[cornerWidth[aCorner]]  * gradientCoeff[aCorner].b;
+
+  float gradientOffset;
+  
+  if (mContext->OriginalSurface()->GetType() == gfxASurface::SurfaceTypeD2D ||
+      mContext->OriginalSurface()->GetType() == gfxASurface::SurfaceTypeQuartz)
+  {
+    // On quarz this doesn't do exactly the right thing, but it does do what
+    // most other browsers do and doing the 'right' thing seems to be
+    // hard with the quartz cairo backend.
+    gradientOffset = 0;
+  } else {
+    // When cairo does the gradient drawing this gives us pretty nice behavior!
+    gradientOffset = 0.25 / sqrt(pow(mBorderWidths[cornerHeight[aCorner]], 2) +
+                                 pow(mBorderWidths[cornerHeight[aCorner]], 2));
+  }
+
+  nsRefPtr<gfxPattern> pattern = new gfxPattern(pat1.x, pat1.y, pat2.x, pat2.y);
+  pattern->AddColorStop(0.5 - gradientOffset, gfxRGBA(aFirstColor));
+  pattern->AddColorStop(0.5 + gradientOffset, gfxRGBA(aSecondColor));
+
+  return pattern.forget();
+}
+
+typedef struct { gfxFloat a, b; } twoFloats;
+
+void
+nsCSSBorderRenderer::DrawSingleWidthSolidBorder()
+{
+  // Easy enough to deal with.
+  mContext->SetLineWidth(1);
+  gfxRect rect = mOuterRect;
+  rect.Inset(0.5);
+
+  const twoFloats cornerAdjusts[4] = { { +0.5,  0   },
+                                       {    0, +0.5 },
+                                       { -0.5,  0   },
+                                       {    0, -0.5 } };
+
+    
+  NS_FOR_CSS_SIDES(side) {
+    gfxPoint firstCorner = rect.CCWCorner(side);
+    firstCorner.x += cornerAdjusts[side].a;
+    firstCorner.y += cornerAdjusts[side].b;
+    gfxPoint secondCorner = rect.CWCorner(side);
+    secondCorner.x += cornerAdjusts[side].a;
+    secondCorner.y += cornerAdjusts[side].b;
+        
+    mContext->SetColor(gfxRGBA(mBorderColors[side]));
+    mContext->NewPath();
+    mContext->MoveTo(firstCorner);
+    mContext->LineTo(secondCorner);
+    mContext->Stroke();
+  }
+}
+
+void
+nsCSSBorderRenderer::DrawNoCompositeColorSolidBorder()
+{
+  const gfxFloat alpha = 0.55191497064665766025;
+
+  const twoFloats cornerMults[4] = { { -1,  0 },
+                                      {  0, -1 },
+                                      { +1,  0 },
+                                      {  0, +1 } };
+
+  const twoFloats centerAdjusts[4] = { { 0, +0.5 },
+                                        { -0.5, 0 },
+                                        { 0, -0.5 },
+                                        { +0.5, 0 } };
+
+  gfxPoint pc, pci, p0, p1, p2, p3, pd, p3i;
+
+  gfxCornerSizes innerRadii;
+  ComputeInnerRadii(mBorderRadii, mBorderWidths, &innerRadii);
+
+  gfxRect strokeRect = mOuterRect;
+  strokeRect.Inset(mBorderWidths[0] / 2.0, mBorderWidths[1] / 2.0,
+                    mBorderWidths[2] / 2.0, mBorderWidths[3] / 2.0);
+
+  NS_FOR_CSS_CORNERS(i) {
+      // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
+    mozilla::css::Corner c = mozilla::css::Corner((i+1) % 4);
+    mozilla::css::Corner prevCorner = mozilla::css::Corner(i);
+
+    // 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 i1 = (i+1) % 4;
+    int i2 = (i+2) % 4;
+    int i3 = (i+3) % 4;
+
+    pc = mOuterRect.AtCorner(c);
+    pci = mInnerRect.AtCorner(c);
+    mContext->SetLineWidth(mBorderWidths[i]);
+
+    nscolor firstColor, secondColor;
+    if (IsVisible(mBorderStyles[i]) && IsVisible(mBorderStyles[i1])) {
+      firstColor = mBorderColors[i];
+      secondColor = mBorderColors[i1];
+    } else if (IsVisible(mBorderStyles[i])) {
+      firstColor = mBorderColors[i];
+      secondColor = mBorderColors[i];
+    } else {
+      firstColor = mBorderColors[i1];
+      secondColor = mBorderColors[i1];
+    }
+
+    mContext->NewPath();
+
+    gfxPoint strokeStart, strokeEnd;
+
+    strokeStart.x = mOuterRect.AtCorner(prevCorner).x +
+      mBorderCornerDimensions[prevCorner].width * cornerMults[i2].a;
+    strokeStart.y = mOuterRect.AtCorner(prevCorner).y +
+      mBorderCornerDimensions[prevCorner].height * cornerMults[i2].b;
+
+    strokeEnd.x = pc.x + mBorderCornerDimensions[c].width * cornerMults[i].a;
+    strokeEnd.y = pc.y + mBorderCornerDimensions[c].height * cornerMults[i].b;
+
+    strokeStart.x += centerAdjusts[i].a * mBorderWidths[i];
+    strokeStart.y += centerAdjusts[i].b * mBorderWidths[i];
+    strokeEnd.x += centerAdjusts[i].a * mBorderWidths[i];
+    strokeEnd.y += centerAdjusts[i].b * mBorderWidths[i];
+
+    mContext->MoveTo(strokeStart);
+    mContext->LineTo(strokeEnd);
+    mContext->SetColor(gfxRGBA(mBorderColors[i]));
+    mContext->Stroke();
+
+    if (firstColor != secondColor) {
+      nsRefPtr<gfxPattern> pattern =
+        CreateCornerGradient(c, firstColor, secondColor);
+      mContext->SetPattern(pattern);
+    } else {
+      mContext->SetColor(firstColor);
+    }     
+    
+    if (mBorderRadii[c].width > 0 && mBorderRadii[c].height > 0) {
+      p0.x = pc.x + cornerMults[i].a * mBorderRadii[c].width;
+      p0.y = pc.y + cornerMults[i].b * mBorderRadii[c].height;
+
+      p3.x = pc.x + cornerMults[i3].a * mBorderRadii[c].width;
+      p3.y = pc.y + cornerMults[i3].b * mBorderRadii[c].height;
+
+      p1.x = p0.x + alpha * cornerMults[i2].a * mBorderRadii[c].width;
+      p1.y = p0.y + alpha * cornerMults[i2].b * mBorderRadii[c].height;
+
+      p2.x = p3.x - alpha * cornerMults[i3].a * mBorderRadii[c].width;
+      p2.y = p3.y - alpha * cornerMults[i3].b * mBorderRadii[c].height;
+
+      mContext->NewPath();
+            
+      gfxPoint cornerStart;
+      cornerStart.x = pc.x + cornerMults[i].a * mBorderCornerDimensions[c].width;
+      cornerStart.y = pc.y + cornerMults[i].b * mBorderCornerDimensions[c].height;
+
+      mContext->MoveTo(cornerStart);
+      mContext->LineTo(p0);
+
+      mContext->CurveTo(p1, p2, p3);
+            
+      gfxPoint outerCornerEnd;
+      outerCornerEnd.x = pc.x + cornerMults[i3].a * mBorderCornerDimensions[c].width;
+      outerCornerEnd.y = pc.y + cornerMults[i3].b * mBorderCornerDimensions[c].height;
+
+      mContext->LineTo(outerCornerEnd);
+
+      p0.x = pci.x + cornerMults[i].a * innerRadii[c].width;
+      p0.y = pci.y + cornerMults[i].b * innerRadii[c].height;
+
+      p3i.x = pci.x + cornerMults[i3].a * innerRadii[c].width;
+      p3i.y = pci.y + cornerMults[i3].b * innerRadii[c].height;
+
+      p1.x = p0.x + alpha * cornerMults[i2].a * innerRadii[c].width;
+      p1.y = p0.y + alpha * cornerMults[i2].b * innerRadii[c].height;
+
+      p2.x = p3i.x - alpha * cornerMults[i3].a * innerRadii[c].width;
+      p2.y = p3i.y - alpha * cornerMults[i3].b * innerRadii[c].height;
+      mContext->LineTo(p3i);
+      mContext->CurveTo(p2, p1, p0);
+      mContext->ClosePath();
+      mContext->Fill();
+    } else {
+      gfxPoint c1, c2, c3, c4;
+
+      c1.x = pc.x + cornerMults[i].a * mBorderCornerDimensions[c].width;
+      c1.y = pc.y + cornerMults[i].b * mBorderCornerDimensions[c].height;
+      c2 = pc;
+      c3.x = pc.x + cornerMults[i3].a * mBorderCornerDimensions[c].width;
+      c3.y = pc.y + cornerMults[i3].b * mBorderCornerDimensions[c].height;
+
+      mContext->NewPath();
+      mContext->MoveTo(c1);
+      mContext->LineTo(c2);
+      mContext->LineTo(c3);
+      mContext->LineTo(pci);
+      mContext->ClosePath();
+
+      mContext->Fill();
+    }
+  }
+}
+
+void
+nsCSSBorderRenderer::DrawRectangularCompositeColors()
+{
+  nsBorderColors *currentColors[4];
+  mContext->SetLineWidth(1);
+  memcpy(currentColors, mCompositeColors, sizeof(nsBorderColors*) * 4);
+  gfxRect rect = mOuterRect;
+  rect.Inset(0.5);
+
+  const twoFloats cornerAdjusts[4] = { { +0.5,  0   },
+                                        {    0, +0.5 },
+                                        { -0.5,  0   },
+                                        {    0, -0.5 } };
+
+  for (int i = 0; i < mBorderWidths[0]; i++) {
+    NS_FOR_CSS_SIDES(side) {
+      int sideNext = (side + 1) % 4;
+
+      gfxPoint firstCorner = rect.CCWCorner(side);
+      firstCorner.x += cornerAdjusts[side].a;
+      firstCorner.y += cornerAdjusts[side].b;
+      gfxPoint secondCorner = rect.CWCorner(side);
+      secondCorner.x -= cornerAdjusts[side].a;
+      secondCorner.y -= cornerAdjusts[side].b;
+        
+      gfxRGBA currentColor = gfxRGBA(currentColors[side]->mColor);
+
+      mContext->SetColor(currentColor);
+      mContext->NewPath();
+      mContext->MoveTo(firstCorner);
+      mContext->LineTo(secondCorner);
+      mContext->Stroke();
+
+      mContext->NewPath();
+      gfxPoint cornerTopLeft = rect.CWCorner(side);
+      cornerTopLeft.x -= 0.5;
+      cornerTopLeft.y -= 0.5;
+      mContext->Rectangle(gfxRect(cornerTopLeft, gfxSize(1, 1)));
+      gfxRGBA nextColor = gfxRGBA(currentColors[sideNext]->mColor);
+      gfxRGBA cornerColor((currentColor.r + nextColor.r) / 2.0,
+                          (currentColor.g + nextColor.g) / 2.0,
+                          (currentColor.b + nextColor.b) / 2.0,
+                          (currentColor.a + nextColor.a) / 2.0);
+      mContext->SetColor(cornerColor);
+      mContext->Fill();
+
+      if (side != 0) {
+        // We'll have to keep side 0 for the color averaging on side 3.
+        if (currentColors[side]->mNext) {
+          currentColors[side] = currentColors[side]->mNext;
+        }
+      }
+    }
+    // Now advance the color for side 0.
+    if (currentColors[0]->mNext) {
+      currentColors[0] = currentColors[0]->mNext;
+    }
+    rect.Inset(1);
+  }
+}
+
+void
 nsCSSBorderRenderer::DrawBorders()
 {
   PRBool forceSeparateCorners = PR_FALSE;
 
   // Examine the border style to figure out if we can draw it in one
   // go or not.
   PRBool tlBordersSame = AreBorderSideFinalStylesSame(SIDE_BIT_TOP | SIDE_BIT_LEFT);
   PRBool brBordersSame = AreBorderSideFinalStylesSame(SIDE_BIT_BOTTOM | SIDE_BIT_RIGHT);
@@ -1029,66 +1376,180 @@ nsCSSBorderRenderer::DrawBorders()
     // All borders are the same style, and the style is either none or hidden, or the color
     // is transparent.
     // This also checks if the first composite color is transparent, and there are
     // no others. It doesn't check if there are subsequent transparent ones, because
     // that would be very silly.
     return;
   }
 
+  // round mOuterRect and mInnerRect; they're already an integer
+  // number of pixels apart and should stay that way after
+  // rounding.
+  mOuterRect.Round();
+  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()) {
+    mat.x0 = floor(mat.x0 + 0.5);
+    mat.y0 = floor(mat.y0 + 0.5);
+    mContext->SetMatrix(mat);
+  }
+
+  PRBool allBordersSameWidth = AllBordersSameWidth();
+
+  PRBool 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 &&
+      mNoBorderRadius)
+  {
+    // Very simple case.
+    SetupStrokeStyle(NS_SIDE_TOP);
+    gfxRect rect = mOuterRect;
+    rect.Inset(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)
+  {
+    // 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);
+    gfxRect rect = mOuterRect;
+    rect.Inset(mBorderWidths[0] / 2.0);
+    mContext->NewPath();
+    mContext->Rectangle(rect);
+    mContext->Stroke();
+    return;
+  }
+
+  
+  if (allBordersSame &&
+      allBordersSameWidth &&
+      mCompositeColors[0] == NULL &&
+      mBorderStyles[0] == NS_STYLE_BORDER_STYLE_SOLID)
+  {
+    NS_FOR_CSS_CORNERS(i) {
+      if (mBorderRadii[i].width <= mBorderWidths[0]) {
+        noCornerOutsideCenter = false;
+      }
+      if (mBorderRadii[i].height <= mBorderWidths[0]) {
+        noCornerOutsideCenter = false;
+      }
+    }
+
+    // We can only do a stroke here if all border radii centers are inside the
+    // inner rect, otherwise we get rendering artifacts.
+
+    if (noCornerOutsideCenter) {
+      // Relatively simple case.
+      SetupStrokeStyle(NS_SIDE_TOP);
+      mOuterRect.Inset(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;
+    }
+  }
+
+  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 &&
+      allBordersSameWidth &&
+      mCompositeColors[0] == NULL &&
+      mBorderWidths[0] == 1 &&
+      mNoBorderRadius)
+  {
+    DrawSingleWidthSolidBorder();
+    return;
+  }
+
+  if (allBordersSolid && !hasCompositeColors)
+  {
+    DrawNoCompositeColorSolidBorder();
+    return;
+  }
+
+  if (allBordersSolid &&
+      allBordersSameWidth &&
+      mNoBorderRadius)
+  {
+    // 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.
   // Otherwise, we'll get artifacts as we draw stacked 1px-wide curves.
   if (allBordersSame && mCompositeColors[0] != nsnull && !mNoBorderRadius)
     forceSeparateCorners = PR_TRUE;
 
-  // round mOuterRect and mInnerRect; they're already an integer
-  // number of pixels apart and should stay that way after
-  // rounding.
-  mOuterRect.Round();
-  mInnerRect.Round();
-
   S(" mOuterRect: "), S(mOuterRect), SN();
   S(" mInnerRect: "), S(mInnerRect), SN();
   SF(" mBorderColors: 0x%08x 0x%08x 0x%08x 0x%08x\n", mBorderColors[0], mBorderColors[1], mBorderColors[2], mBorderColors[3]);
 
   // if conditioning the outside rect failed, then bail -- the outside
   // rect is supposed to enclose the entire border
   mOuterRect.Condition();
   if (mOuterRect.IsEmpty())
     return;
 
   mInnerRect.Condition();
+  PRIntn dashedSides = 0;
 
-  PRIntn dashedSides = 0;
   NS_FOR_CSS_SIDES(i) {
     PRUint8 style = mBorderStyles[i];
     if (style == NS_STYLE_BORDER_STYLE_DASHED ||
         style == NS_STYLE_BORDER_STYLE_DOTTED)
     {
       // pretend that all borders aren't the same; we need to draw
       // things separately for dashed/dotting
       allBordersSame = PR_FALSE;
       dashedSides |= (1 << i);
     }
   }
 
   SF(" allBordersSame: %d dashedSides: 0x%02x\n", allBordersSame, dashedSides);
 
-  // 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.
-  gfxMatrix mat = mContext->CurrentMatrix();
-  if (!mat.HasNonTranslation()) {
-    mat.x0 = floor(mat.x0 + 0.5);
-    mat.y0 = floor(mat.y0 + 0.5);
-    mContext->SetMatrix(mat);
-  }
-
   if (allBordersSame && !forceSeparateCorners) {
     /* Draw everything in one go */
     DrawBorderSides(SIDE_BITS_ALL);
     SN("---------------- (1)");
   } else {
     /* We have more than one pass to go.  Draw the corners separately from the sides. */
 
     /*
--- a/layout/base/nsCSSRenderingBorders.h
+++ b/layout/base/nsCSSRenderingBorders.h
@@ -114,16 +114,18 @@ struct nsCSSBorderRenderer {
 
   // the rectangle of the outside and the inside of the border
   gfxRect mOuterRect;
   gfxRect mInnerRect;
 
   // the style and size of the border
   const PRUint8* mBorderStyles;
   const gfxFloat* mBorderWidths;
+  PRUint8* mSanitizedStyles;
+  gfxFloat* mSanitizedWidths;
   gfxCornerSizes mBorderRadii;
 
   // colors
   const nscolor* mBorderColors;
   nsBorderColors* const* mCompositeColors;
 
   // core app units per pixel
   PRInt32 mAUPP;
@@ -192,16 +194,44 @@ struct nsCSSBorderRenderer {
   // present in the bitmask
   void DrawBorderSides (PRIntn aSides);
 
   // function used by the above to handle -moz-border-colors
   void DrawBorderSidesCompositeColors(PRIntn aSides, const nsBorderColors *compositeColors);
 
   // draw the given dashed side
   void DrawDashedSide (mozilla::css::Side aSide);
+  
+  // Setup the stroke style for a given side
+  void SetupStrokeStyle(mozilla::css::Side aSize);
+
+  // Analyze if all border sides have the same width.
+  bool AllBordersSameWidth();
+
+  // Analyze if all borders are 'solid' this also considers hidden or 'none'
+  // borders because they can be considered 'solid' borders of 0 width and
+  // with no color effect.
+  bool AllBordersSolid(bool *aHasCompositeColors);
+
+  // Create a gradient pattern that will handle the color transition for a
+  // corner.
+  already_AddRefed<gfxPattern> CreateCornerGradient(mozilla::css::Corner aCorner,
+                                                    const gfxRGBA &aFirstColor,
+                                                    const gfxRGBA &aSecondColor);
+
+  // Draw a solid color border that is uniformly the same width.
+  void DrawSingleWidthSolidBorder();
+
+  // Draw any border which is solid on all sides and does not use
+  // CompositeColors.
+  void DrawNoCompositeColorSolidBorder();
+
+  // Draw a solid border that has no border radius (i.e. is rectangular) and
+  // uses CompositeColors.
+  void DrawRectangularCompositeColors();
 
   // draw the entire border
   void DrawBorders ();
 
   // utility function used for background painting as well as borders
   static void ComputeInnerRadii(const gfxCornerSizes& aRadii,
                                 const gfxFloat *aBorderSizes,
                                 gfxCornerSizes *aInnerRadiiRet);