Reland "Reland "Implement an explicit binary search-based analytic gradient colorizer""

This is a reland of f065907ccc0ae5e9258443b5a1bbabeef181e965

3rd time's the charm:

The new analytic gradient shader was sporadically triggering violations of the coverage as alpha
compatibility optimization. Unfortunately, even when using the same device and random seed for the
test, the bots did not always reproduce the error. However, we identified the likely cause of the
violation.

The test requires that all output channels are less than the input alpha, which it uses to validate
whether or not the shader is modulating its values by the input alpha. This test does not pass if
the RGB values are greater than 1. The original version of the analytic gradient shader used half4s
for its scale and bias values. Given the threshold limit for hardstops of 0.00024 (SkNearlyZero),
a very small interval that is not treated as a hardstop can create a scale or bias of over 4000.
This moves into the very imprecise region of 16-bit floats, making it plausible that the gradient
outputs colors greater than 1, due to rounding. The kicker is that the random test generation for
stop locations does not use a uniform distribution, but is instead biased towards the remaining
interval, which increases the likelihood of generating a small interval that is not treated as a
hard stop. We are keeping this behavior since ill-conditioned gradients are useful in testing.

Original change's description:
> Reland "Implement an explicit binary search-based analytic gradient colorizer"
>
> This reverts commit 9461dcf1306a9a9517e1545cf6d16bde05261280.
>
> Reason for revert: Fixes for ANGLE's incorrect shader behavior
>
> Original change's description:
> > Revert "Implement an explicit binary search-based analytic gradient colorizer"
> >
> > This reverts commit dcc85fc61008f61daef4313846bdd62877fd596d.
> >
> > Reason for revert: ANGLE is frequently corrupted, particularly radial_gradient4 and mixershader
> >
> > Original change's description:
> > > Implement an explicit binary search-based analytic gradient colorizer
> > >
> > > Provides a reasonably flexible fragment processor that defines another
> > > colorizer implementation for gradients. It can support up to 8
> > > interpolation intervals (which is 16 colors if every stop is a hard stop
> > > or 9 colors if every stop is a smooth transition). It
> > > supports mixtures of hard and smooth stops. It is conditionally compiled
> > > into versions specific to the interval count (so it can produce up to
> > > 8 shader variants).
> > >
> > > The GrGradientShader controller does not remove the single and dual
> > > interval colorizers, which are useful specializations of this explicit
> > > binary search colorizer. Similarly, since it can only handle up to 8
> > > intervals, the texture colorizer is still used as a fallback.
> > >
> > > Currently it does not employ capabilities detection to determine if the
> > > hardware can support the number of required uniforms, which can become
> > > substantial for the larger gradient configurations.
> > >
> > > Bug: chromium:796479, chromium:729727, chromium:696603, chromium:543625, chromium:414254
> > > Change-Id: Ia1f735a5019766ae4796cc22964b2913db34b95b
> > > Reviewed-on: https://skia-review.googlesource.com/155080
> > > Commit-Queue: Michael Ludwig <michaelludwig@google.com>
> > > Reviewed-by: Brian Osman <brianosman@google.com>
> >
> > TBR=bsalomon@google.com,brianosman@google.com,michaelludwig@google.com
> >
> > Change-Id: I351a387f0528e4c2db2d47ab2e5d6b336991fb98
> > No-Presubmit: true
> > No-Tree-Checks: true
> > No-Try: true
> > Bug: chromium:796479, chromium:729727, chromium:696603, chromium:543625, chromium:414254
> > Reviewed-on: https://skia-review.googlesource.com/156541
> > Reviewed-by: Michael Ludwig <michaelludwig@google.com>
> > Commit-Queue: Michael Ludwig <michaelludwig@google.com>
>
> TBR=bsalomon@google.com,brianosman@google.com,michaelludwig@google.com
>
> Change-Id: I2aca36307d88c26905d860ec29417ec68c6037cc
> Bug: chromium:796479, chromium:729727, chromium:696603, chromium:543625, chromium:414254
> Reviewed-on: https://skia-review.googlesource.com/156542
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>
> Commit-Queue: Michael Ludwig <michaelludwig@google.com>

Bug: chromium:796479, chromium:729727, chromium:696603, chromium:543625, chromium:414254
Change-Id: I2d050624781c77cdd160291cadbadac602b48bde
Reviewed-on: https://skia-review.googlesource.com/c/157569
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gm/analytic_gradients.cpp b/gm/analytic_gradients.cpp
new file mode 100644
index 0000000..df84b19
--- /dev/null
+++ b/gm/analytic_gradients.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * This GM presents a variety of gradients meant to test the correctness of the analytic
+ * GrUnrolledBinaryGradientColorizer, which can handle arbitrary gradients with 1 to 8 interpolation
+ * intervals. These intervals can be either hardstops or smooth color transitions.
+ *
+ * It produces an image similar to that of GM_hardstop_gradients, but is arranged as follows:
+ *
+ *            | Clamp          |
+ *            |________________|
+ *            | M1  M2  M3  M4 |
+ * ___________|________________|
+ *      1     |
+ *      2     |
+ *      3     |
+ *      4     |
+ *      5     |
+ *      6     |
+ *      7     |
+ *      8     |
+ * The M-modes are different ways of interlveaving hardstops with smooth transitions:
+ *   - M1 = All smooth transitions
+ *   - M2 = All hard stops
+ *   - M5 = Alternating smooth then hard
+ *   - M6 = Alternating hard then smooth
+ *
+ * Only clamping is tested since this is focused more on within the interpolation region behavior,
+ * compared to overall behavior.
+ */
+
+#include "gm.h"
+
+#include "SkGradientShader.h"
+
+// All positions must be divided by the target interval count, which will produce the expected
+// normalized position array for that interval number (assuming an appropriate color count is
+// provided).
+const int M1_POSITIONS[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+const int M2_POSITIONS[] = { 0, 1,1, 2,2, 3,3, 4,4, 5,5, 6,6, 7,7, 8 };
+const int M3_POSITIONS[] = { 0, 1, 2,2, 3, 4,4, 5, 6,6, 7, 8 };
+const int M4_POSITIONS[] = { 0, 1,1, 2, 3,3, 4, 5,5, 6, 7,7, 8 };
+
+// Color count = index of first occurrence of interval count value in Mx_POSITIONS array.
+const int INT1_COLOR_COUNTS[] = { 2, 2, 2, 2 };
+const int INT2_COLOR_COUNTS[] = { 3, 4, 3, 4 };
+const int INT3_COLOR_COUNTS[] = { 4, 6, 5, 5 };
+const int INT4_COLOR_COUNTS[] = { 5, 8, 6, 7 };
+const int INT5_COLOR_COUNTS[] = { 6, 10, 8, 8 };
+const int INT6_COLOR_COUNTS[] = { 7, 12, 9, 10 };
+const int INT7_COLOR_COUNTS[] = { 8, 14, 11, 11 };
+const int INT8_COLOR_COUNTS[] = { 9, 16, 12, 13 };
+
+// Cycle through defined colors for positions 0 through 8.
+const SkColor COLORS[] = {
+    SK_ColorDKGRAY,
+    SK_ColorRED,
+    SK_ColorYELLOW,
+    SK_ColorGREEN,
+    SK_ColorCYAN,
+    SK_ColorBLUE,
+    SK_ColorMAGENTA,
+    SK_ColorBLACK,
+    SK_ColorLTGRAY
+};
+
+const int* INTERVAL_COLOR_COUNTS[] = {
+    INT1_COLOR_COUNTS,
+    INT2_COLOR_COUNTS,
+    INT3_COLOR_COUNTS,
+    INT4_COLOR_COUNTS,
+    INT5_COLOR_COUNTS,
+    INT6_COLOR_COUNTS,
+    INT7_COLOR_COUNTS,
+    INT8_COLOR_COUNTS
+};
+const int COLOR_COUNT = SK_ARRAY_COUNT(COLORS);
+
+const int* M_POSITIONS[] = {
+    M1_POSITIONS,
+    M2_POSITIONS,
+    M3_POSITIONS,
+    M4_POSITIONS
+};
+
+const int WIDTH  = 500;
+const int HEIGHT = 500;
+
+const int NUM_ROWS = 8;
+const int NUM_COLS = 4;
+
+const int CELL_WIDTH  = WIDTH  / NUM_COLS;
+const int CELL_HEIGHT = HEIGHT / NUM_ROWS;
+
+const int PAD_WIDTH  = 3;
+const int PAD_HEIGHT = 3;
+
+const int RECT_WIDTH  = CELL_WIDTH  - (2 * PAD_WIDTH);
+const int RECT_HEIGHT = CELL_HEIGHT - (2 * PAD_HEIGHT);
+
+static void shade_rect(SkCanvas* canvas, sk_sp<SkShader> shader, int cellRow, int cellCol) {
+    SkPaint paint;
+    paint.setShader(shader);
+
+    canvas->save();
+    canvas->translate(SkIntToScalar(cellCol * CELL_WIDTH + PAD_WIDTH),
+                      SkIntToScalar(cellRow * CELL_HEIGHT + PAD_HEIGHT));
+
+    const SkRect rect = SkRect::MakeWH(SkIntToScalar(RECT_WIDTH), SkIntToScalar(RECT_HEIGHT));
+    canvas->drawRect(rect, paint);
+    canvas->restore();
+}
+
+class AnalyticGradientShaderGM : public skiagm::GM {
+public:
+    AnalyticGradientShaderGM() {
+
+    }
+
+protected:
+    SkString onShortName() override {
+        return SkString("analytic_gradients");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(1024, 512);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        const SkPoint points[2] = { SkPoint::Make(0, 0), SkPoint::Make(RECT_WIDTH, 0.0) };
+
+        for (int cellRow = 0; cellRow < NUM_ROWS; cellRow++) {
+            // Each interval has 4 different color counts, one per mode
+            const int* colorCounts = INTERVAL_COLOR_COUNTS[cellRow]; // Has len = 4
+
+            for (int cellCol = 0; cellCol < NUM_COLS; cellCol++) {
+                // create_gradient_points(cellRow, cellCol, points);
+
+                // Get the color count dependent on interval and mode
+                int colorCount = colorCounts[cellCol];
+                // Get the positions given the mode
+                const int* layout = M_POSITIONS[cellCol];
+
+                // Collect positions and colors specific to the interval+mode normalizing the
+                // position based on the interval count (== cellRow+1)
+                SkAutoSTMalloc<4, SkColor> colors(colorCount);
+                SkAutoSTMalloc<4, SkScalar> positions(colorCount);
+                int j = 0;
+                for (int i = 0; i < colorCount; i++) {
+                    positions[i] = SkIntToScalar(layout[i]) / (cellRow + 1);
+                    colors[i] = COLORS[j % COLOR_COUNT];
+                    j++;
+                }
+
+                auto shader = SkGradientShader::MakeLinear(
+                                points,
+                                colors.get(),
+                                positions.get(),
+                                colorCount,
+                                SkShader::kClamp_TileMode,
+                                0,
+                                nullptr);
+
+                shade_rect(canvas, shader, cellRow, cellCol);
+            }
+        }
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+DEF_GM(return new AnalyticGradientShaderGM;)