Runtime effect implementation of color cube filter

Bug: skia:10274
Change-Id: Ifb2ef8bf031e74d9d5c8183efe5aff4e6f3d2e7c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/292562
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/gm/runtimeshader.cpp b/gm/runtimeshader.cpp
index 749e040..6d0254a 100644
--- a/gm/runtimeshader.cpp
+++ b/gm/runtimeshader.cpp
@@ -237,3 +237,89 @@
     }
 };
 DEF_GM(return new SpiralRT;)
+
+class ColorCubeRT : public skiagm::GM {
+    sk_sp<SkImage> fMandrill, fMandrillSepia, fIdentityCube, fSepiaCube;
+    sk_sp<SkRuntimeEffect> fEffect;
+
+    void onOnceBeforeDraw() override {
+        fMandrill      = GetResourceAsImage("images/mandrill_256.png");
+        fMandrillSepia = GetResourceAsImage("images/mandrill_sepia.png");
+        fIdentityCube  = GetResourceAsImage("images/lut_identity.png");
+        fSepiaCube     = GetResourceAsImage("images/lut_sepia.png");
+
+        const char code[] = R"(
+            in shader input;
+            in shader color_cube;
+
+            uniform float size;
+            uniform float size_minus_1;
+
+            void main(float2 xy, inout half4 color) {
+                float4 c = float4(unpremul(sample(input, xy)));
+
+                // Map to cube coords:
+                float3 cubeCoords = float3(c.rg * size_minus_1 + 0.5, c.b * size_minus_1);
+
+                // Compute slice coordinate
+                float2 coords1 = float2(floor(cubeCoords.b) * size + cubeCoords.r, cubeCoords.g);
+                float2 coords2 = float2( ceil(cubeCoords.b) * size + cubeCoords.r, cubeCoords.g);
+
+                // Two bilinear fetches, plus a manual lerp for the third axis:
+                color = mix(sample(color_cube, coords1), sample(color_cube, coords2),
+                            half(fract(cubeCoords.b)));
+
+                // Premul again
+                color.rgb *= color.a;
+            }
+        )";
+        auto [effect, error] = SkRuntimeEffect::Make(SkString(code));
+        if (!effect) {
+            SkDebugf("runtime error %s\n", error.c_str());
+        }
+        fEffect = effect;
+    }
+
+    SkString onShortName() override { return SkString("color_cube_rt"); }
+
+    SkISize onISize() override { return {256 * 3, 256}; }
+
+    DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override {
+        if (canvas->getGrContext() == nullptr) {
+            // until SkSL can handle child processors on the raster backend
+            return DrawResult::kSkip;
+        }
+
+        // First we draw the unmodified image, and a copy that was sepia-toned in Photoshop:
+        canvas->drawImage(fMandrill,      0,   0);
+        canvas->drawImage(fMandrillSepia, 0, 256);
+
+        // LUT dimensions should be (kSize^2, kSize)
+        constexpr float kSize = 16.0f;
+
+        SkRuntimeShaderBuilder builder(fEffect);
+        builder.input("size")         = kSize;
+        builder.input("size_minus_1") = kSize - 1;
+        builder.child("input")      = fMandrill->makeShader();
+
+        // TODO: Move filter quality to the shader itself. We need to enforce at least kLow here
+        // so that we bilerp the color cube image.
+        SkPaint paint;
+        paint.setFilterQuality(kLow_SkFilterQuality);
+
+        // Now draw the image with an identity color cube - it should look like the original
+        builder.child("color_cube") = fIdentityCube->makeShader();
+        paint.setShader(builder.makeShader(nullptr, true));
+        canvas->translate(256, 0);
+        canvas->drawRect({ 0, 0, 256, 256 }, paint);
+
+        // ... and with a sepia-tone color cube. This should match the sepia-toned image.
+        builder.child("color_cube") = fSepiaCube->makeShader();
+        paint.setShader(builder.makeShader(nullptr, true));
+        canvas->translate(0, 256);
+        canvas->drawRect({ 0, 0, 256, 256 }, paint);
+
+        return DrawResult::kOk;
+    }
+};
+DEF_GM(return new ColorCubeRT;)
diff --git a/resources/images/lut_identity.png b/resources/images/lut_identity.png
new file mode 100644
index 0000000..00985ac
--- /dev/null
+++ b/resources/images/lut_identity.png
Binary files differ
diff --git a/resources/images/lut_sepia.png b/resources/images/lut_sepia.png
new file mode 100644
index 0000000..364fe8d
--- /dev/null
+++ b/resources/images/lut_sepia.png
Binary files differ
diff --git a/resources/images/mandrill_sepia.png b/resources/images/mandrill_sepia.png
new file mode 100644
index 0000000..a345f44
--- /dev/null
+++ b/resources/images/mandrill_sepia.png
Binary files differ