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;)