Bug 787947. Avoid scaling by 0 when snapping gradient tiles. r=jrmuizel
☠☠ backed out by 8af00f1609b9 ☠ ☠
authorRobert O'Callahan <robert@ocallahan.org>
Tue, 25 Sep 2012 15:25:48 +1200
changeset 109346 a5c50066ecbb2f9f91c8a53a3d0ee198a3045f1e
parent 109345 ee9f796b8416072d1387a625ff07dd613c80b907
child 109347 a85c0f30cdfae5b2283516537b89374165b39810
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersjrmuizel
bugs787947
milestone18.0a1
Bug 787947. Avoid scaling by 0 when snapping gradient tiles. r=jrmuizel
layout/base/nsCSSRendering.cpp
layout/reftests/bugs/787947-1-ref.html
layout/reftests/bugs/787947-1.html
layout/reftests/bugs/reftest.list
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -2285,16 +2285,17 @@ nsCSSRendering::PaintGradient(nsPresCont
   // destination, but after pixel-snapping tiles may not all be the same size.
   nsRect dirty;
   if (!dirty.IntersectRect(aDirtyRect, aFillArea))
     return;
 
   gfxRect areaToFill =
     nsLayoutUtils::RectToGfxRect(aFillArea, appUnitsPerPixel);
   gfxMatrix ctm = ctx->CurrentMatrix();
+  bool isCTMPreservingAxisAlignedRectangles = ctm.PreservesAxisAlignedRectangles();
 
   // xStart/yStart are the top-left corner of the top-left tile.
   nscoord xStart = FindTileStart(dirty.x, aOneCellArea.x, aOneCellArea.width);
   nscoord yStart = FindTileStart(dirty.y, aOneCellArea.y, aOneCellArea.height);
   nscoord xEnd = pattern->mCoversTile ? xStart + aOneCellArea.width : dirty.XMost();
   nscoord yEnd = pattern->mCoversTile ? yStart + aOneCellArea.height : dirty.YMost();
 
   // x and y are the top-left corner of the tile to draw
@@ -2304,37 +2305,40 @@ nsCSSRendering::PaintGradient(nsPresCont
       gfxRect tileRect = nsLayoutUtils::RectToGfxRect(
                       nsRect(x, y, aOneCellArea.width, aOneCellArea.height),
                       appUnitsPerPixel);
       // The actual area to fill with this tile is the intersection of this
       // tile with the overall area we're supposed to be filling
       gfxRect fillRect =
         pattern->mCoversTile ? areaToFill : tileRect.Intersect(areaToFill);
       ctx->NewPath();
-      // If we can snap the gradient tile and fill rects, do so, but make sure
-      // that the gradient is scaled precisely to the tile rect.
-      gfxRect fillRectSnapped = fillRect;
-      // Don't snap the tileRect directly since that would lose information
-      // about the orientation of the current transform (i.e. vertical or
-      // horizontal flipping). Instead snap the corners independently so if
-      // the CTM has a flip, our Scale() below preserves the flip.
-      gfxPoint tileRectSnappedTopLeft = tileRect.TopLeft();
-      gfxPoint tileRectSnappedBottomRight = tileRect.BottomRight();
-      if (ctx->UserToDevicePixelSnapped(fillRectSnapped, true) &&
-          ctx->UserToDevicePixelSnapped(tileRectSnappedTopLeft, true) &&
-          ctx->UserToDevicePixelSnapped(tileRectSnappedBottomRight, true)) {
+      // Try snapping the fill rect. Snap its top-left and bottom-right
+      // independently to preserve the orientation.
+      gfxPoint snappedFillRectTopLeft = fillRect.TopLeft();
+      gfxPoint snappedFillRectBottomRight = fillRect.BottomRight();
+      if (isCTMPreservingAxisAlignedRectangles &&
+          ctx->UserToDevicePixelSnapped(snappedFillRectTopLeft, true) &&
+          ctx->UserToDevicePixelSnapped(snappedFillRectBottomRight, true)) {
+        if (snappedFillRectTopLeft.x == snappedFillRectBottomRight.x ||
+            snappedFillRectTopLeft.y == snappedFillRectBottomRight.y) {
+          // Nothing to draw; avoid scaling by zero and other weirdness that
+          // could put the context in an error state.
+          continue;
+        }
+        // Set the context's transform to the transform that maps fillRect to
+        // snappedFillRect. The part of the gradient that was going to
+        // exactly fill fillRect will fill snappedFillRect instead.
         ctx->IdentityMatrix();
-        ctx->Rectangle(fillRectSnapped);
-        ctx->Translate(tileRectSnappedTopLeft);
-        ctx->Scale((tileRectSnappedBottomRight.x - tileRectSnappedTopLeft.x)/tileRect.width,
-                   (tileRectSnappedBottomRight.y - tileRectSnappedTopLeft.y)/tileRect.height);
-      } else {
-        ctx->Rectangle(fillRect);
-        ctx->Translate(tileRect.TopLeft());
+        ctx->Translate(snappedFillRectTopLeft);
+        ctx->Scale((snappedFillRectBottomRight.x - snappedFillRectTopLeft.x)/fillRect.width,
+                   (snappedFillRectBottomRight.y - snappedFillRectTopLeft.y)/fillRect.height);
+        ctx->Translate(-fillRect.TopLeft());
       }
+      ctx->Rectangle(fillRect);
+      ctx->Translate(tileRect.TopLeft());
       ctx->SetPattern(pattern->mPattern);
       ctx->Fill();
       ctx->SetMatrix(ctm);
     }
   }
   // If we could not put the gradient in the gradient cache, make sure to
   // release its resources so we don't leak.
   if (!gradientRegistered) {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/787947-1-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE HTML>
+<body style="background:white;">
+<div style="position:absolute; left:10px; top:10px; width:100px; height:100px; border:1px solid black;"></div>
+<p style="position:absolute; left:10px; z-index:2">Hello
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/787947-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE HTML>
+<body style="background:white;">
+<div style="position:absolute; left:10px; top:10px; width:100px; height:100px; border:1px solid black; background-image:linear-gradient(30deg, white, rgba(255,255,255,0)); background-size:0.3px 100px;"></div>
+<p style="position:absolute; left:10px; z-index:2">Hello
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1714,8 +1714,9 @@ fuzzy-if(true,17,5859) == 759036-2.html 
 == 776265-1a.html 776265-1-ref.html
 == 776265-1b.html 776265-1-ref.html
 == 776265-1c.html 776265-1-ref.html
 == 776265-1d.html 776265-1-ref.html
 == 776265-2a.html 776265-2-ref.html
 == 776265-2b.html 776265-2-ref.html
 == 776265-2c.html 776265-2-ref.html
 == 776265-2d.html 776265-2-ref.html
+== 787947-1.html 787947-1-ref.html