Color matrix effect rewritten as .fp
Also adds flexibility to unpremul the input, clamp the output, premul the
output or not.
Also fixes SkMatrix44 as a ctype.
The intent is to reuse this for rgb->yuv conversion in async rescale and
read.
Bug: skia:3962
Change-Id: I470d1cfebdbd79d8541b633c1747d510a5549ac4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/217128
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/core/SkColorFilter_Matrix.cpp b/src/core/SkColorFilter_Matrix.cpp
index 0a648e5..c5e0679 100644
--- a/src/core/SkColorFilter_Matrix.cpp
+++ b/src/core/SkColorFilter_Matrix.cpp
@@ -65,121 +65,13 @@
}
#if SK_SUPPORT_GPU
-#include "src/gpu/GrFragmentProcessor.h"
-#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
-#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
-#include "src/gpu/glsl/GrGLSLProgramDataManager.h"
-#include "src/gpu/glsl/GrGLSLUniformHandler.h"
-
-class ColorMatrixEffect : public GrFragmentProcessor {
-public:
- static std::unique_ptr<GrFragmentProcessor> Make(const float matrix[20]) {
- return std::unique_ptr<GrFragmentProcessor>(new ColorMatrixEffect(matrix));
- }
-
- const char* name() const override { return "Color Matrix"; }
-
- GR_DECLARE_FRAGMENT_PROCESSOR_TEST
-
- std::unique_ptr<GrFragmentProcessor> clone() const override { return Make(fMatrix); }
-
-private:
- class GLSLProcessor : public GrGLSLFragmentProcessor {
- public:
- // this class always generates the same code.
- static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*) {}
-
- void emitCode(EmitArgs& args) override {
- GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
- fMatrixHandle = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4x4_GrSLType,
- "ColorMatrix");
- fVectorHandle = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
- "ColorMatrixVector");
-
- GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
- // The max() is to guard against 0 / 0 during unpremul when the incoming color is
- // transparent black.
- fragBuilder->codeAppendf("\thalf nonZeroAlpha = max(%s.a, 0.00001);\n",
- args.fInputColor);
- fragBuilder->codeAppendf("\t%s = %s * half4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + "
- "%s;\n",
- args.fOutputColor,
- uniformHandler->getUniformCStr(fMatrixHandle),
- args.fInputColor,
- uniformHandler->getUniformCStr(fVectorHandle));
- fragBuilder->codeAppendf("\t%s = saturate(%s);\n",
- args.fOutputColor, args.fOutputColor);
- fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor);
- }
-
- protected:
- void onSetData(const GrGLSLProgramDataManager& uniManager,
- const GrFragmentProcessor& proc) override {
- const ColorMatrixEffect& cme = proc.cast<ColorMatrixEffect>();
- const float* m = cme.fMatrix;
- // The GL matrix is transposed from SkColorMatrix.
- float mt[] = {
- m[0], m[5], m[10], m[15],
- m[1], m[6], m[11], m[16],
- m[2], m[7], m[12], m[17],
- m[3], m[8], m[13], m[18],
- };
- float vec[] = {
- m[4], m[9], m[14], m[19],
- };
- uniManager.setMatrix4fv(fMatrixHandle, 1, mt);
- uniManager.set4fv(fVectorHandle, 1, vec);
- }
-
- private:
- GrGLSLProgramDataManager::UniformHandle fMatrixHandle;
- GrGLSLProgramDataManager::UniformHandle fVectorHandle;
-
- typedef GrGLSLFragmentProcessor INHERITED;
- };
-
- // We could implement the constant input->constant output optimization but haven't. Other
- // optimizations would be matrix-dependent.
- ColorMatrixEffect(const float matrix[20])
- : INHERITED(kColorMatrixEffect_ClassID, kNone_OptimizationFlags) {
- memcpy(fMatrix, matrix, sizeof(float) * 20);
- }
-
- GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
- return new GLSLProcessor;
- }
-
- virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps,
- GrProcessorKeyBuilder* b) const override {
- GLSLProcessor::GenKey(*this, caps, b);
- }
-
- bool onIsEqual(const GrFragmentProcessor& s) const override {
- const ColorMatrixEffect& cme = s.cast<ColorMatrixEffect>();
- return 0 == memcmp(fMatrix, cme.fMatrix, sizeof(fMatrix));
- }
-
- float fMatrix[20];
-
- typedef GrFragmentProcessor INHERITED;
-};
-
-GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect);
-
-#if GR_TEST_UTILS
-std::unique_ptr<GrFragmentProcessor> ColorMatrixEffect::TestCreate(GrProcessorTestData* d) {
- float colorMatrix[20];
- for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix); ++i) {
- colorMatrix[i] = d->fRandom->nextSScalar1();
- }
- return ColorMatrixEffect::Make(colorMatrix);
-}
-
-#endif
-
+#include "src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h"
std::unique_ptr<GrFragmentProcessor> SkColorFilter_Matrix::asFragmentProcessor(
GrRecordingContext*, const GrColorSpaceInfo&) const {
- return ColorMatrixEffect::Make(fMatrix);
+ return GrColorMatrixFragmentProcessor::Make(fMatrix,
+ /* premulInput = */ true,
+ /* clampRGBOutput = */ true,
+ /* premulOutput = */ true);
}
#endif
diff --git a/src/gpu/GrProcessor.h b/src/gpu/GrProcessor.h
index ef245f8..656f418 100644
--- a/src/gpu/GrProcessor.h
+++ b/src/gpu/GrProcessor.h
@@ -66,7 +66,6 @@
kCircleGeometryProcessor_ClassID,
kCircularRRectEffect_ClassID,
kClockwiseTestProcessor_ClassID,
- kColorMatrixEffect_ClassID,
kColorTableEffect_ClassID,
kComposeOneFragmentProcessor_ClassID,
kComposeTwoFragmentProcessor_ClassID,
@@ -94,6 +93,7 @@
kGrCircleBlurFragmentProcessor_ClassID,
kGrCircleEffect_ClassID,
kGrClampedGradientEffect_ClassID,
+ kGrColorMatrixFragmentProcessor_ClassID,
kGrColorSpaceXformEffect_ClassID,
kGrComposeLerpEffect_ClassID,
kGrComposeLerpRedEffect_ClassID,
diff --git a/src/gpu/effects/GrColorMatrixFragmentProcessor.fp b/src/gpu/effects/GrColorMatrixFragmentProcessor.fp
new file mode 100644
index 0000000..5bbfda7
--- /dev/null
+++ b/src/gpu/effects/GrColorMatrixFragmentProcessor.fp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+layout(ctype=SkMatrix44, tracked) in uniform half4x4 m;
+layout(ctype=SkRect, tracked) in uniform half4 v;
+layout(key) in bool unpremulInput;
+layout(key) in bool clampRGBOutput;
+layout(key) in bool premulOutput;
+
+@optimizationFlags {
+ kConstantOutputForConstantInput_OptimizationFlag
+}
+
+void main() {
+ half4 inputColor = sk_InColor;
+ @if (unpremulInput) {
+ // The max() is to guard against 0 / 0 during unpremul when the incoming color is
+ // transparent black.
+ half nonZeroAlpha = max(inputColor.a, 0.00001);
+ inputColor = half4(inputColor.rgb / nonZeroAlpha, nonZeroAlpha);
+ }
+ sk_OutColor = m * inputColor + v;
+ @if (clampRGBOutput) {
+ sk_OutColor = saturate(sk_OutColor);
+ } else {
+ sk_OutColor.a = saturate(sk_OutColor.a);
+ }
+ @if (premulOutput) {
+ sk_OutColor.rgb *= sk_OutColor.a;
+ }
+}
+
+@class {
+ SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& input) const override {
+ SkColor4f color;
+ if (unpremulInput) {
+ color = input.unpremul();
+ } else {
+ color.fR = input.fR;
+ color.fG = input.fG;
+ color.fB = input.fB;
+ color.fA = input.fA;
+ }
+ m.mapScalars(color.vec());
+ color.fR += v.fLeft;
+ color.fG += v.fTop;
+ color.fB += v.fRight;
+ color.fA += v.fBottom;
+ color.fA = SkTPin(color.fA, 0.f, 1.f);
+ if (clampRGBOutput) {
+ color.fR = SkTPin(color.fR, 0.f, 1.f);
+ color.fG = SkTPin(color.fG, 0.f, 1.f);
+ color.fB = SkTPin(color.fB, 0.f, 1.f);
+ }
+ if (premulOutput) {
+ return color.premul();
+ } else {
+ return {color.fR, color.fG, color.fB, color.fA};
+ }
+ }
+}
+
+@make {
+ static std::unique_ptr<GrFragmentProcessor> Make(const float matrix[20], bool unpremulInput, bool clampRGBOutput, bool premulOutput) {
+ const float m[] {
+ matrix[0], matrix[1], matrix[2], matrix[3],
+ matrix[5], matrix[6], matrix[7], matrix[8],
+ matrix[10], matrix[11], matrix[12], matrix[13],
+ matrix[15], matrix[16], matrix[17], matrix[18]
+ };
+ SkMatrix44 m44;
+ m44.setRowMajorf(m);
+ SkRect v4 = SkRect::MakeLTRB(matrix[4], matrix[9], matrix[14], matrix[19]);
+ return std::unique_ptr<GrFragmentProcessor>(new GrColorMatrixFragmentProcessor(m44, v4, unpremulInput, clampRGBOutput, premulOutput));
+ }
+}
+
+@test(d) {
+ float m[20];
+ for (int i = 0; i < 20; ++i) {
+ m[i] = d->fRandom->nextRangeScalar(-10.f, 10.f);
+ }
+ bool unpremul = d->fRandom->nextBool();
+ bool clampRGB = d->fRandom->nextBool();
+ bool premul = d->fRandom->nextBool();
+ return Make(m, unpremul, clampRGB, premul);
+}
diff --git a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
new file mode 100644
index 0000000..c0cf275
--- /dev/null
+++ b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**************************************************************************************************
+ *** This file was autogenerated from GrColorMatrixFragmentProcessor.fp; do not modify.
+ **************************************************************************************************/
+#include "GrColorMatrixFragmentProcessor.h"
+
+#include "include/gpu/GrTexture.h"
+#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
+#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
+#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
+#include "src/sksl/SkSLCPP.h"
+#include "src/sksl/SkSLUtil.h"
+class GrGLSLColorMatrixFragmentProcessor : public GrGLSLFragmentProcessor {
+public:
+ GrGLSLColorMatrixFragmentProcessor() {}
+ void emitCode(EmitArgs& args) override {
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ const GrColorMatrixFragmentProcessor& _outer =
+ args.fFp.cast<GrColorMatrixFragmentProcessor>();
+ (void)_outer;
+ auto m = _outer.m;
+ (void)m;
+ auto v = _outer.v;
+ (void)v;
+ auto unpremulInput = _outer.unpremulInput;
+ (void)unpremulInput;
+ auto clampRGBOutput = _outer.clampRGBOutput;
+ (void)clampRGBOutput;
+ auto premulOutput = _outer.premulOutput;
+ (void)premulOutput;
+ mVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4x4_GrSLType, "m");
+ vVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "v");
+ fragBuilder->codeAppendf(
+ "half4 inputColor = %s;\n@if (%s) {\n half nonZeroAlpha = max(inputColor.w, "
+ "1.0000000000000001e-05);\n inputColor = half4(inputColor.xyz / nonZeroAlpha, "
+ "nonZeroAlpha);\n}\n%s = %s * inputColor + %s;\n@if (%s) {\n %s = clamp(%s, "
+ "0.0, 1.0);\n} else {\n %s.w = clamp(%s.w, 0.0, 1.0);\n}\n@if (%s) {\n "
+ "%s.xyz *= %s.w;\n}\n",
+ args.fInputColor, (_outer.unpremulInput ? "true" : "false"), args.fOutputColor,
+ args.fUniformHandler->getUniformCStr(mVar),
+ args.fUniformHandler->getUniformCStr(vVar),
+ (_outer.clampRGBOutput ? "true" : "false"), args.fOutputColor, args.fOutputColor,
+ args.fOutputColor, args.fOutputColor, (_outer.premulOutput ? "true" : "false"),
+ args.fOutputColor, args.fOutputColor);
+ }
+
+private:
+ void onSetData(const GrGLSLProgramDataManager& pdman,
+ const GrFragmentProcessor& _proc) override {
+ const GrColorMatrixFragmentProcessor& _outer = _proc.cast<GrColorMatrixFragmentProcessor>();
+ {
+ const SkMatrix44& mValue = _outer.m;
+ if (mPrev != (mValue)) {
+ mPrev = mValue;
+ pdman.setSkMatrix44(mVar, mValue);
+ }
+ const SkRect& vValue = _outer.v;
+ if (vPrev.isEmpty() || vPrev != vValue) {
+ vPrev = vValue;
+ pdman.set4fv(vVar, 1, reinterpret_cast<const float*>(&vValue));
+ }
+ }
+ }
+ SkMatrix44 mPrev = SkMatrix44(SkMatrix44::kNaN_Constructor);
+ SkRect vPrev = SkRect::MakeEmpty();
+ UniformHandle mVar;
+ UniformHandle vVar;
+};
+GrGLSLFragmentProcessor* GrColorMatrixFragmentProcessor::onCreateGLSLInstance() const {
+ return new GrGLSLColorMatrixFragmentProcessor();
+}
+void GrColorMatrixFragmentProcessor::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+ GrProcessorKeyBuilder* b) const {
+ b->add32((int32_t)unpremulInput);
+ b->add32((int32_t)clampRGBOutput);
+ b->add32((int32_t)premulOutput);
+}
+bool GrColorMatrixFragmentProcessor::onIsEqual(const GrFragmentProcessor& other) const {
+ const GrColorMatrixFragmentProcessor& that = other.cast<GrColorMatrixFragmentProcessor>();
+ (void)that;
+ if (m != that.m) return false;
+ if (v != that.v) return false;
+ if (unpremulInput != that.unpremulInput) return false;
+ if (clampRGBOutput != that.clampRGBOutput) return false;
+ if (premulOutput != that.premulOutput) return false;
+ return true;
+}
+GrColorMatrixFragmentProcessor::GrColorMatrixFragmentProcessor(
+ const GrColorMatrixFragmentProcessor& src)
+ : INHERITED(kGrColorMatrixFragmentProcessor_ClassID, src.optimizationFlags())
+ , m(src.m)
+ , v(src.v)
+ , unpremulInput(src.unpremulInput)
+ , clampRGBOutput(src.clampRGBOutput)
+ , premulOutput(src.premulOutput) {}
+std::unique_ptr<GrFragmentProcessor> GrColorMatrixFragmentProcessor::clone() const {
+ return std::unique_ptr<GrFragmentProcessor>(new GrColorMatrixFragmentProcessor(*this));
+}
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrColorMatrixFragmentProcessor);
+#if GR_TEST_UTILS
+std::unique_ptr<GrFragmentProcessor> GrColorMatrixFragmentProcessor::TestCreate(
+ GrProcessorTestData* d) {
+ float m[20];
+ for (int i = 0; i < 20; ++i) {
+ m[i] = d->fRandom->nextRangeScalar(-10.f, 10.f);
+ }
+ bool unpremul = d->fRandom->nextBool();
+ bool clampRGB = d->fRandom->nextBool();
+ bool premul = d->fRandom->nextBool();
+ return Make(m, unpremul, clampRGB, premul);
+}
+#endif
diff --git a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h
new file mode 100644
index 0000000..3506e92
--- /dev/null
+++ b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**************************************************************************************************
+ *** This file was autogenerated from GrColorMatrixFragmentProcessor.fp; do not modify.
+ **************************************************************************************************/
+#ifndef GrColorMatrixFragmentProcessor_DEFINED
+#define GrColorMatrixFragmentProcessor_DEFINED
+#include "include/core/SkTypes.h"
+
+#include "src/gpu/GrCoordTransform.h"
+#include "src/gpu/GrFragmentProcessor.h"
+class GrColorMatrixFragmentProcessor : public GrFragmentProcessor {
+public:
+ SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& input) const override {
+ SkColor4f color;
+ if (unpremulInput) {
+ color = input.unpremul();
+ } else {
+ color.fR = input.fR;
+ color.fG = input.fG;
+ color.fB = input.fB;
+ color.fA = input.fA;
+ }
+ m.mapScalars(color.vec());
+ color.fR += v.fLeft;
+ color.fG += v.fTop;
+ color.fB += v.fRight;
+ color.fA += v.fBottom;
+ color.fA = SkTPin(color.fA, 0.f, 1.f);
+ if (clampRGBOutput) {
+ color.fR = SkTPin(color.fR, 0.f, 1.f);
+ color.fG = SkTPin(color.fG, 0.f, 1.f);
+ color.fB = SkTPin(color.fB, 0.f, 1.f);
+ }
+ if (premulOutput) {
+ return color.premul();
+ } else {
+ return {color.fR, color.fG, color.fB, color.fA};
+ }
+ }
+
+ static std::unique_ptr<GrFragmentProcessor> Make(const float matrix[20], bool unpremulInput,
+ bool clampRGBOutput, bool premulOutput) {
+ const float m[]{matrix[0], matrix[1], matrix[2], matrix[3], matrix[5], matrix[6],
+ matrix[7], matrix[8], matrix[10], matrix[11], matrix[12], matrix[13],
+ matrix[15], matrix[16], matrix[17], matrix[18]};
+ SkMatrix44 m44;
+ m44.setRowMajorf(m);
+ SkRect v4 = SkRect::MakeLTRB(matrix[4], matrix[9], matrix[14], matrix[19]);
+ return std::unique_ptr<GrFragmentProcessor>(new GrColorMatrixFragmentProcessor(
+ m44, v4, unpremulInput, clampRGBOutput, premulOutput));
+ }
+ GrColorMatrixFragmentProcessor(const GrColorMatrixFragmentProcessor& src);
+ std::unique_ptr<GrFragmentProcessor> clone() const override;
+ const char* name() const override { return "ColorMatrixFragmentProcessor"; }
+ SkMatrix44 m;
+ SkRect v;
+ bool unpremulInput;
+ bool clampRGBOutput;
+ bool premulOutput;
+
+private:
+ GrColorMatrixFragmentProcessor(SkMatrix44 m, SkRect v, bool unpremulInput, bool clampRGBOutput,
+ bool premulOutput)
+ : INHERITED(kGrColorMatrixFragmentProcessor_ClassID,
+ (OptimizationFlags)kConstantOutputForConstantInput_OptimizationFlag)
+ , m(m)
+ , v(v)
+ , unpremulInput(unpremulInput)
+ , clampRGBOutput(clampRGBOutput)
+ , premulOutput(premulOutput) {}
+ GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
+ void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
+ bool onIsEqual(const GrFragmentProcessor&) const override;
+ GR_DECLARE_FRAGMENT_PROCESSOR_TEST
+ typedef GrFragmentProcessor INHERITED;
+};
+#endif
diff --git a/src/gpu/glsl/GrGLSLProgramDataManager.cpp b/src/gpu/glsl/GrGLSLProgramDataManager.cpp
index 08aa70d..395e325 100644
--- a/src/gpu/glsl/GrGLSLProgramDataManager.cpp
+++ b/src/gpu/glsl/GrGLSLProgramDataManager.cpp
@@ -25,3 +25,8 @@
this->setMatrix3f(u, mt);
}
+void GrGLSLProgramDataManager::setSkMatrix44(UniformHandle u, const SkMatrix44& matrix) const {
+ float mt[16];
+ matrix.asColMajorf(mt);
+ this->setMatrix4f(u, mt);
+}
diff --git a/src/gpu/glsl/GrGLSLProgramDataManager.h b/src/gpu/glsl/GrGLSLProgramDataManager.h
index 7348082..5db4ae5 100644
--- a/src/gpu/glsl/GrGLSLProgramDataManager.h
+++ b/src/gpu/glsl/GrGLSLProgramDataManager.h
@@ -55,6 +55,8 @@
// convenience method for uploading a SkMatrix to a 3x3 matrix uniform
void setSkMatrix(UniformHandle, const SkMatrix&) const;
+ // convenience method for uploading a SkMatrix to a 4x4 matrix uniform
+ void setSkMatrix44(UniformHandle, const SkMatrix44&) const;
// for nvpr only
GR_DEFINE_RESOURCE_HANDLE_CLASS(VaryingHandle);
diff --git a/src/sksl/SkSLCPPUniformCTypes.cpp b/src/sksl/SkSLCPPUniformCTypes.cpp
index c34cdac..1bd7b4c 100644
--- a/src/sksl/SkSLCPPUniformCTypes.cpp
+++ b/src/sksl/SkSLCPPUniformCTypes.cpp
@@ -176,8 +176,8 @@
REGISTER(Layout::CType::kSkMatrix44, { "half4x4", "float4x4", "double4x4" },
"${pdman}.setSkMatrix44(${uniform}, ${var})", // to gpu
- "SkMatrix::MakeScale(SK_FloatNaN)", // default value
- "!${oldVar}.cheapEqualTo(${newVar})"), // dirty check
+ "SkMatrix44(SkMatrix44::kNaN_Constructor)", // default value
+ "${oldVar} != (${newVar})"), // dirty check
REGISTER(Layout::CType::kFloat, { "half", "float", "double" },
"${pdman}.set1f(${uniform}, ${var})", // to gpu