Bug 1022624 - Implement support for linear gradients with ExtendMode::REFLECT in DrawTargetCG. r=jrmuizel, a=lmandel
authorMarkus Stange <mstange@themasta.com>
Tue, 10 Jun 2014 22:26:32 +0200
changeset 208205 610390beb394f20bc2a86e44e65c920f231cd3f4
parent 208204 beb8b71d7b9a8b83a05242ce0a1c807d8946aa63
child 208206 f3007d054fe20d265b838f88fd70beee4aee76be
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel, lmandel
bugs1022624
milestone32.0a2
Bug 1022624 - Implement support for linear gradients with ExtendMode::REFLECT in DrawTargetCG. r=jrmuizel, a=lmandel
gfx/2d/DrawTargetCG.cpp
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -467,17 +467,18 @@ UpdateLinearParametersToIncludePoint(dou
 
 /**
  * Repeat the gradient line such that lines extended perpendicular to the
  * gradient line at both start and end would completely enclose the drawing
  * extents.
  */
 static void
 CalculateRepeatingGradientParams(CGPoint *aStart, CGPoint *aEnd,
-                                 CGRect aExtents, int *aRepeatCount)
+                                 CGRect aExtents, int *aRepeatStartFactor,
+                                 int *aRepeatEndFactor)
 {
   double t_min = INFINITY;
   double t_max = -INFINITY;
   double dx = aEnd->x - aStart->x;
   double dy = aEnd->y - aStart->y;
 
   double bounds_x1 = aExtents.origin.x;
   double bounds_y1 = aExtents.origin.y;
@@ -501,57 +502,67 @@ CalculateRepeatingGradientParams(CGPoint
   // cutting off a pixel. Extending the gradient repetitions is always safe.
   t_min = floor (t_min);
   t_max = ceil (t_max);
   aEnd->x = aStart->x + dx * t_max;
   aEnd->y = aStart->y + dy * t_max;
   aStart->x = aStart->x + dx * t_min;
   aStart->y = aStart->y + dy * t_min;
 
-  *aRepeatCount = t_max - t_min;
+  *aRepeatStartFactor = t_min;
+  *aRepeatEndFactor = t_max;
 }
 
 static void
-DrawLinearRepeatingGradient(CGContextRef cg, const LinearGradientPattern &aPattern, const CGRect &aExtents)
+DrawLinearRepeatingGradient(CGContextRef cg, const LinearGradientPattern &aPattern,
+                            const CGRect &aExtents, bool aReflect)
 {
   GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get());
   CGPoint startPoint = { aPattern.mBegin.x, aPattern.mBegin.y };
   CGPoint endPoint = { aPattern.mEnd.x, aPattern.mEnd.y };
 
-  int repeatCount = 1;
+  int repeatStartFactor = 0, repeatEndFactor = 1;
   // if we don't have a line then we can't extend it
   if (aPattern.mEnd.x != aPattern.mBegin.x ||
       aPattern.mEnd.y != aPattern.mBegin.y) {
     CalculateRepeatingGradientParams(&startPoint, &endPoint, aExtents,
-                                     &repeatCount);
+                                     &repeatStartFactor, &repeatEndFactor);
   }
 
+  int repeatCount = repeatEndFactor - repeatStartFactor;
+  uint32_t numStops = stops->mStops.size();
   double scale = 1./repeatCount;
 
   std::vector<CGFloat> colors;
   std::vector<CGFloat> offsets;
-  colors.reserve(stops->mStops.size()*repeatCount*4);
-  offsets.reserve(stops->mStops.size()*repeatCount);
+  colors.reserve(numStops*repeatCount*4);
+  offsets.reserve(numStops*repeatCount);
 
-  for (int j = 0; j < repeatCount; j++) {
-    for (uint32_t i = 0; i < stops->mStops.size(); i++) {
-      colors.push_back(stops->mStops[i].color.r);
-      colors.push_back(stops->mStops[i].color.g);
-      colors.push_back(stops->mStops[i].color.b);
-      colors.push_back(stops->mStops[i].color.a);
+  for (int j = repeatStartFactor; j < repeatEndFactor; j++) {
+    bool isReflected = aReflect && (j % 2) != 0;
+    for (uint32_t i = 0; i < numStops; i++) {
+      uint32_t stopIndex = isReflected ? numStops - i - 1 : i;
+      colors.push_back(stops->mStops[stopIndex].color.r);
+      colors.push_back(stops->mStops[stopIndex].color.g);
+      colors.push_back(stops->mStops[stopIndex].color.b);
+      colors.push_back(stops->mStops[stopIndex].color.a);
 
-      offsets.push_back((stops->mStops[i].offset + j)*scale);
+      CGFloat offset = stops->mStops[stopIndex].offset;
+      if (isReflected) {
+        offset = 1 - offset;
+      }
+      offsets.push_back((offset + (j - repeatStartFactor)) * scale);
     }
   }
 
   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
   CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace,
                                                                &colors.front(),
                                                                &offsets.front(),
-                                                               repeatCount*stops->mStops.size());
+                                                               repeatCount*numStops);
   CGColorSpaceRelease(colorSpace);
 
   CGContextDrawLinearGradient(cg, gradient, startPoint, endPoint,
                               kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
   CGGradientRelease(gradient);
 }
 
 static CGPoint CGRectTopLeft(CGRect a)
@@ -645,18 +656,18 @@ DrawGradient(CGContextRef cg, const Patt
       CGPoint endPoint   = { pat.mEnd.x,   pat.mEnd.y };
 
       // Canvas spec states that we should avoid drawing degenerate gradients (XXX: should this be in common code?)
       //if (startPoint.x == endPoint.x && startPoint.y == endPoint.y)
       //  return;
 
       CGContextDrawLinearGradient(cg, stops->mGradient, startPoint, endPoint,
                                   kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
-    } else if (stops->mExtend == ExtendMode::REPEAT) {
-      DrawLinearRepeatingGradient(cg, pat, aExtents);
+    } else if (stops->mExtend == ExtendMode::REPEAT || stops->mExtend == ExtendMode::REFLECT) {
+      DrawLinearRepeatingGradient(cg, pat, aExtents, stops->mExtend == ExtendMode::REFLECT);
     }
   } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
     const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
     CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(pat.mMatrix));
     GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
     if (stops->mExtend == ExtendMode::CLAMP) {
 
       // XXX: we should take the m out of the properties of RadialGradientPatterns