Bug 895135. Avoid normalizing gradients to a smaller interval than 0..1. r=roc
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Sat, 20 Jul 2013 08:58:10 -0400
changeset 151655 9497f8c1a115b35c444e9f00784adae5cd15df2a
parent 151654 68bcfde4e82a9ea5b675e0072d495f0f60e95023
child 151656 db5e3bb3205d3cbf08ecae9dbac5daad8f506677
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs895135
milestone25.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 895135. Avoid normalizing gradients to a smaller interval than 0..1. r=roc Previously if we had all the stops at the same location, we'd end up putting them all at 0. This makes things harder for backends that implement gradients with a lookup table because they don't have room for multiples entries at the zero location. If instead we leave the stops at the same location on the 0-1 line we'll have a better chance of drawing them correctly.
layout/base/nsCSSRendering.cpp
layout/reftests/css-gradients/reftest.list
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -2257,43 +2257,53 @@ nsCSSRendering::PaintGradient(nsPresCont
     firstStop = 0;
   }
 
   double lastStop = stops[stops.Length() - 1].mPosition;
   // Cairo gradients must have stop positions in the range [0, 1]. So,
   // stop positions will be normalized below by subtracting firstStop and then
   // multiplying by stopScale.
   double stopScale;
+  double stopOrigin = firstStop;
+  double stopEnd = lastStop;
   double stopDelta = lastStop - firstStop;
   bool zeroRadius = aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR &&
                       (radiusX < 1e-6 || radiusY < 1e-6);
   if (stopDelta < 1e-6 || lineLength < 1e-6 || zeroRadius) {
     // Stops are all at the same place. Map all stops to 0.0.
     // For repeating radial gradients, or for any radial gradients with
     // a zero radius, we need to fill with the last stop color, so just set
     // both radii to 0.
-    stopScale = 0.0;
     if (aGradient->mRepeating || zeroRadius) {
       radiusX = radiusY = 0.0;
     }
+    stopDelta = 0.0;
     lastStop = firstStop;
-  } else {
-    stopScale = 1.0/stopDelta;
   }
 
+  // Don't normalize non-repeating or degenerate gradients below 0..1
+  // This keeps the gradient line as large as the box and doesn't
+  // lets us avoiding having to get padding correct for stops
+  // at 0 and 1
+  if (!aGradient->mRepeating || stopDelta == 0.0) {
+    stopOrigin = std::min(stopOrigin, 0.0);
+    stopEnd = std::max(stopEnd, 1.0);
+  }
+  stopScale = 1.0/(stopEnd - stopOrigin);
+
   // Create the gradient pattern.
   nsRefPtr<gfxPattern> gradientPattern;
   bool forceRepeatToCoverTiles = false;
   if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
     // Compute the actual gradient line ends we need to pass to cairo after
     // stops have been normalized.
-    gfxPoint gradientStart = lineStart + (lineEnd - lineStart)*firstStop;
-    gfxPoint gradientEnd = lineStart + (lineEnd - lineStart)*lastStop;
-
-    if (stopScale == 0.0) {
+    gfxPoint gradientStart = lineStart + (lineEnd - lineStart)*stopOrigin;
+    gfxPoint gradientEnd = lineStart + (lineEnd - lineStart)*stopEnd;
+
+    if (stopDelta == 0.0) {
       // Stops are all at the same place. For repeating gradients, this will
       // just paint the last stop color. We don't need to do anything.
       // For non-repeating gradients, this should render as two colors, one
       // on each "side" of the gradient line segment, which is a point. All
       // our stops will be at 0.0; we just need to set the direction vector
       // correctly.
       gradientEnd = gradientStart + (lineEnd - lineStart);
     }
@@ -2312,19 +2322,19 @@ nsCSSRendering::PaintGradient(nsPresCont
       forceRepeatToCoverTiles = true;
     }
   } else {
     NS_ASSERTION(firstStop >= 0.0,
                   "Negative stops not allowed for radial gradients");
 
     // To form an ellipse, we'll stretch a circle vertically, if necessary.
     // So our radii are based on radiusX.
-    double innerRadius = radiusX*firstStop;
-    double outerRadius = radiusX*lastStop;
-    if (stopScale == 0.0) {
+    double innerRadius = radiusX*stopOrigin;
+    double outerRadius = radiusX*stopEnd;
+    if (stopDelta == 0.0) {
       // Stops are all at the same place.  See above (except we now have
       // the inside vs. outside of an ellipse).
       outerRadius = innerRadius + 1;
     }
     gradientPattern = new gfxPattern(lineStart.x, lineStart.y, innerRadius,
                                       lineStart.x, lineStart.y, outerRadius);
     if (radiusX != radiusY) {
       // Stretch the circles into ellipses vertically by setting a transform
@@ -2337,17 +2347,17 @@ nsCSSRendering::PaintGradient(nsPresCont
       matrix.Scale(1.0, radiusX/radiusY);
       matrix.Translate(-lineStart);
       gradientPattern->SetMatrix(matrix);
     }
   }
   if (gradientPattern->CairoStatus())
     return;
 
-  if (stopScale == 0.0) {
+  if (stopDelta == 0.0) {
     // Non-repeating gradient with all stops in same place -> just add
     // first stop and last stop, both at position 0.
     // Repeating gradient with all stops in the same place, or radial
     // gradient with radius of 0 -> just paint the last stop color.
     // We use firstStop offset to keep |stops| with same units (will later normalize to 0).
     gfxRGBA firstColor(stops[0].mColor);
     gfxRGBA lastColor(stops.LastElement().mColor);
     stops.Clear();
@@ -2367,32 +2377,32 @@ nsCSSRendering::PaintGradient(nsPresCont
     // which is a lookup table used to evaluate the gradient. This surface can use
     // much memory (ram and/or GPU ram) and can be expensive to create. So we cache it.
     // The cache key correlates 1:1 with the arguments for CreateGradientStops (also the implied backend type)
     // Note that GradientStop is a simple struct with a stop value (while GradientStops has the surface).
     nsTArray<gfx::GradientStop> rawStops(stops.Length());
     rawStops.SetLength(stops.Length());
     for(uint32_t i = 0; i < stops.Length(); i++) {
       rawStops[i].color = gfx::Color(stops[i].mColor.r, stops[i].mColor.g, stops[i].mColor.b, stops[i].mColor.a);
-      rawStops[i].offset =  stopScale * (stops[i].mPosition - firstStop);
+      rawStops[i].offset =  stopScale * (stops[i].mPosition - stopOrigin);
     }
     GradientCacheData* cached = gGradientCache->Lookup(rawStops, isRepeat, backendType);
     mozilla::RefPtr<mozilla::gfx::GradientStops> gs = cached ? cached->mStops : nullptr;
     if (!gs) {
       // CreateGradientStops is expensive (possibly lazily)
       gs = ctx->GetDrawTarget()->CreateGradientStops(rawStops.Elements(), stops.Length(), isRepeat ? gfx::EXTEND_REPEAT : gfx::EXTEND_CLAMP);
       cached = new GradientCacheData(gs, GradientCacheKey(rawStops, isRepeat, backendType));
       if (!gGradientCache->RegisterEntry(cached)) {
         delete cached;
       }
     }
     gradientPattern->SetColorStops(gs);
   } else {
     for (uint32_t i = 0; i < stops.Length(); i++) {
-      double pos = stopScale*(stops[i].mPosition - firstStop);
+      double pos = stopScale*(stops[i].mPosition - stopOrigin);
       gradientPattern->AddColorStop(pos, stops[i].mColor);
     }
     // Set repeat mode. Default cairo extend mode is PAD.
     if (isRepeat) {
       gradientPattern->SetExtend(gfxPattern::EXTEND_REPEAT);
     }
   }
 
--- a/layout/reftests/css-gradients/reftest.list
+++ b/layout/reftests/css-gradients/reftest.list
@@ -11,20 +11,20 @@ fuzzy-if(!contentSameGfxBackendAsCanvas,
 == linear-diagonal-2b.html linear-diagonal-2-ref.html
 == linear-diagonal-2c.html linear-diagonal-2-ref.html
 == linear-diagonal-3a.html linear-diagonal-3-ref.html
 == linear-diagonal-3b.html linear-diagonal-3-ref.html
 == linear-diagonal-3c.html linear-diagonal-3-ref.html
 == linear-diagonal-4a.html linear-diagonal-4-ref.html
 == linear-diagonal-4b.html linear-diagonal-4-ref.html
 == linear-diagonal-4c.html linear-diagonal-4-ref.html
-== linear-diagonal-5a.html linear-diagonal-5-ref.html
-== linear-diagonal-6a.html linear-diagonal-6-ref.html
-== linear-diagonal-7a.html linear-diagonal-7-ref.html
-== linear-diagonal-8a.html linear-diagonal-8-ref.html
+fuzzy(2,11356) == linear-diagonal-5a.html linear-diagonal-5-ref.html
+fuzzy(2,11657) == linear-diagonal-6a.html linear-diagonal-6-ref.html
+fuzzy(2,11298) == linear-diagonal-7a.html linear-diagonal-7-ref.html
+fuzzy(2,11443) == linear-diagonal-8a.html linear-diagonal-8-ref.html
 == linear-diagonal-9a.html linear-diagonal-9-ref.html
 fuzzy(1,800000) == linear-flipped-1.html linear-flipped-1-ref.html
 == linear-position-1a.html linear-position-1-ref.html
 == linear-repeat-1a.html linear-repeat-1-ref.html
 fails-if(d2d) == linear-repeat-1b.html linear-repeat-1-ref.html # bug 582236
 == linear-repeat-1c.html linear-repeat-1-ref.html
 fails-if(d2d) == linear-repeat-1d.html linear-repeat-1-ref.html # bug 582236
 == linear-repeat-1e.html linear-repeat-1-ref.html
@@ -45,35 +45,35 @@ fuzzy-if(!contentSameGfxBackendAsCanvas,
 fuzzy-if(!contentSameGfxBackendAsCanvas,1,88500) fuzzy-if(azureSkiaGL,2,89700) == linear-vertical-1e.html linear-vertical-1-ref.html
 == linear-vertical-subpixel-1.html linear-vertical-subpixel-1-ref.html
 == linear-viewport.html linear-viewport-ref.html
 == linear-zero-length-1a.html linear-zero-length-1-ref.html
 == linear-zero-length-1b.html linear-zero-length-1-ref.html
 == linear-zero-length-1c.html linear-zero-length-1-ref.html
 == nostops.html about:blank
 == onestop.html about:blank
-fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,51018) fuzzy-if(azureSkiaGL,2,88024) random-if(d2d) == radial-1a.html radial-1-ref.html
-fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,51018) fuzzy-if(azureSkiaGL,2,88024) random-if(d2d) == radial-1b.html radial-1-ref.html
-fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,51018) fuzzy-if(azureSkiaGL,2,88024) random-if(d2d) == radial-1c.html radial-1-ref.html
-fuzzy-if(!contentSameGfxBackendAsCanvas,3,7860) fuzzy-if(azureSkiaGL,2,90000) == radial-2a.html radial-2-ref.html
-fuzzy-if(!contentSameGfxBackendAsCanvas,3,7860) fuzzy-if(azureSkiaGL,2,90000) == radial-2b.html radial-2-ref.html
-fuzzy-if(!contentSameGfxBackendAsCanvas,3,7860) fuzzy-if(azureSkiaGL,2,90000) == radial-2c.html radial-2-ref.html
-fuzzy-if(!contentSameGfxBackendAsCanvas,3,7860) fuzzy-if(azureSkiaGL,2,90000) == radial-2d.html radial-2-ref.html
-fuzzy-if(!contentSameGfxBackendAsCanvas,3,7860) fuzzy-if(azureSkiaGL,2,90000) == radial-2e.html radial-2-ref.html
-fuzzy-if(!contentSameGfxBackendAsCanvas,3,7860) fuzzy-if(azureSkiaGL,2,90000) == radial-2f.html radial-2-ref.html
+fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,87755) fuzzy-if(azureSkiaGL,2,88024) random-if(d2d) == radial-1a.html radial-1-ref.html
+fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,87755) fuzzy-if(azureSkiaGL,2,88024) random-if(d2d) == radial-1b.html radial-1-ref.html
+fuzzy-if(!contentSameGfxBackendAsCanvas,1,5884) fuzzy-if(cocoaWidget,9,87755) fuzzy-if(azureSkiaGL,2,88024) random-if(d2d) == radial-1c.html radial-1-ref.html
+fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2a.html radial-2-ref.html
+fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2b.html radial-2-ref.html
+fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2c.html radial-2-ref.html
+fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2d.html radial-2-ref.html
+fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2e.html radial-2-ref.html
+fuzzy(3,7860) fuzzy-if(cocoaWidget,5,89041) fuzzy-if(azureSkiaGL,2,90000) == radial-2f.html radial-2-ref.html
 == radial-position-1a.html radial-position-1-ref.html
 == radial-position-1b.html radial-position-1-ref.html
 == radial-shape-closest-corner-1a.html radial-shape-closest-corner-1-ref.html
-== radial-shape-closest-corner-1b.html radial-shape-closest-corner-1-ref.html
+fuzzy(1,190) fuzzy-if(cocoaWidget,3,460) == radial-shape-closest-corner-1b.html radial-shape-closest-corner-1-ref.html
 == radial-shape-closest-corner-1c.html radial-shape-closest-corner-1-ref.html
 fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,5) fuzzy-if(Android,17,3880) == radial-shape-closest-side-1a.html radial-shape-closest-side-1-ref.html
 fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,5) fuzzy-if(Android,17,3880) == radial-shape-closest-side-1b.html radial-shape-closest-side-1-ref.html
 fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,5) fuzzy-if(Android,17,3880) == radial-shape-closest-side-1c.html radial-shape-closest-side-1-ref.html
 == radial-shape-farthest-corner-1a.html radial-shape-farthest-corner-1-ref.html
-fails-if(cocoaWidget&&/x86-/.test(xulRuntime.XPCOMABI)||gtk2Widget&&/x86_64-/.test(xulRuntime.XPCOMABI)) == radial-shape-farthest-corner-1b.html radial-shape-farthest-corner-1-ref.html
+fails-if(cocoaWidget&&/x86-/.test(xulRuntime.XPCOMABI)||gtk2Widget&&/x86_64-/.test(xulRuntime.XPCOMABI)) fuzzy(1,1561) fuzzy-if(cocoaWidget,3,1082) == radial-shape-farthest-corner-1b.html radial-shape-farthest-corner-1-ref.html
 == radial-shape-farthest-corner-1c.html radial-shape-farthest-corner-1-ref.html
 fuzzy-if(Android,17,13320) == radial-shape-farthest-side-1a.html radial-shape-farthest-side-1-ref.html
 fuzzy-if(Android,17,13320) == radial-shape-farthest-side-1b.html radial-shape-farthest-side-1-ref.html
 fuzzy-if(Android,17,13320) == radial-shape-farthest-side-1c.html radial-shape-farthest-side-1-ref.html
 == radial-size-1a.html radial-size-1-ref.html
 == radial-size-1b.html radial-size-1-ref.html
 == radial-zero-length-1a.html radial-zero-length-1-ref.html
 == radial-zero-length-1b.html radial-zero-length-1-ref.html
@@ -93,17 +93,17 @@ fuzzy-if(d2d,124,2612) == repeating-radi
 fuzzy-if(d2d,124,2612) == repeating-radial-1c.html repeating-radial-1-ref.html
 == repeating-radial-1d.html repeating-radial-1-ref.html
 fuzzy-if(d2d,124,2612) == repeating-radial-1e.html repeating-radial-1-ref.html
 == repeating-radial-1f.html repeating-radial-1-ref.html
 == repeating-radial-2a.html repeating-radial-2-ref.html
 == repeating-radial-2b.html repeating-radial-2-ref.html
 == twostops-1a.html twostops-1-ref.html
 == twostops-1b.html twostops-1-ref.html
-fails-if(OSX==10.6) == twostops-1c.html twostops-1-ref.html # bug 524173
+== twostops-1c.html twostops-1-ref.html
 == twostops-1d.html twostops-1-ref.html
 == twostops-1e.html twostops-1-ref.html
 == twostops-1f.html twostops-1-ref.html
 == twostops-1g.html twostops-1-ref.html
 
 # from http://www.xanthir.com/:4bhipd by way of http://a-ja.net/newgrad.html
 fuzzy-if(!contentSameGfxBackendAsCanvas,1,20000) fuzzy-if(azureSkiaGL,8,20000) == aja-linear-1a.html aja-linear-1-ref.html
 fails-if(!d2d) == aja-linear-1b.html aja-linear-1-ref.html # bug 526694
@@ -122,17 +122,17 @@ fuzzy-if(!contentSameGfxBackendAsCanvas,
 fuzzy-if(!contentSameGfxBackendAsCanvas,2,20000) fuzzy-if(azureSkiaGL,8,20000) == aja-linear-4b.html aja-linear-4-ref.html
 fuzzy-if(!contentSameGfxBackendAsCanvas,2,20000) fuzzy-if(azureSkiaGL,8,20000) == aja-linear-5a.html aja-linear-5-ref.html
 fuzzy-if(!contentSameGfxBackendAsCanvas,2,16477) fuzzy-if(azureSkiaGL,8,20000) == aja-linear-6a.html aja-linear-6-ref.html # bug 526708
 fails == aja-linear-6b.html aja-linear-6-ref.html # bug 522607
 skip-if(B2G) == height-dependence-1.html height-dependence-1-ref.html
 skip-if(B2G) fuzzy-if(cocoaWidget,1,40000) == height-dependence-2.html height-dependence-2-ref.html
 skip-if(B2G) == height-dependence-3.html height-dependence-3-ref.html
 
-fails-if(d2d) == linear-onestopposition-1.html linear-onestopposition-1-ref.html # bug 638664
-== linear-onestopposition-1.html linear-onestopposition-1-ref2.html
-fails-if(d2d) fails-if(cocoaWidget) == radial-onestopposition-1a.html radial-onestopposition-1-ref.html # bug 638664
-fails-if(d2d) fails-if(cocoaWidget) == radial-onestopposition-1b.html radial-onestopposition-1-ref.html # bug 638664
-fails-if(d2d) fails-if(cocoaWidget) == radial-onestopposition-1c.html radial-onestopposition-1-ref.html # bug 638664
+== linear-onestopposition-1.html linear-onestopposition-1-ref.html
+fuzzy-if(d2d,47,400) == linear-onestopposition-1.html linear-onestopposition-1-ref2.html # d2d interpolates the hard stop
+== radial-onestopposition-1a.html radial-onestopposition-1-ref.html
+== radial-onestopposition-1b.html radial-onestopposition-1-ref.html
+== radial-onestopposition-1c.html radial-onestopposition-1-ref.html
 == repeating-linear-onestopposition-1.html orange-square.html
 == repeating-radial-onestopposition-1a.html orange-square.html
 == repeating-radial-onestopposition-1b.html orange-square.html
 == repeating-radial-onestopposition-1c.html orange-square.html