Implement 2-pt conical gradient layout
Add an FP that provides the two-point conical gradient effect. The
majority of the new FP's shader code is a straight forward port from
SkTwoPointConicalGradient_gpu.cpp. This FP is bulkier because of the
extensive calculations that go on in its overridden Make function.
To support 2-pt conical gradient's behavior of writing transparent
black in invalid areas of the conical interpolation, the contract of
gradient layout FPs has been updated to provide a flag in the y
component as to whether or not the fragment should be rejected.
A separate channel was used since negative values and large values are
perfectly reasonable for the untiled gradient layout to return (before
the value is then constrained into [0, 1]). It also seemed better to
avoid returning a problematic value like infinity or NaN.
Bug: skia:
Change-Id: I37373bb5aebd89cac8905602e699ad19f0f5ac82
Reviewed-on: https://skia-review.googlesource.com/148988
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/gradients/GrTwoPointConicalGradientLayout.cpp b/src/gpu/gradients/GrTwoPointConicalGradientLayout.cpp
new file mode 100644
index 0000000..cd67661
--- /dev/null
+++ b/src/gpu/gradients/GrTwoPointConicalGradientLayout.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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 file was autogenerated from GrTwoPointConicalGradientLayout.fp; do not modify.
+ **************************************************************************************************/
+#include "GrTwoPointConicalGradientLayout.h"
+#include "glsl/GrGLSLFragmentProcessor.h"
+#include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLProgramBuilder.h"
+#include "GrTexture.h"
+#include "SkSLCPP.h"
+#include "SkSLUtil.h"
+class GrGLSLTwoPointConicalGradientLayout : public GrGLSLFragmentProcessor {
+public:
+ GrGLSLTwoPointConicalGradientLayout() {}
+ void emitCode(EmitArgs& args) override {
+ GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
+ const GrTwoPointConicalGradientLayout& _outer =
+ args.fFp.cast<GrTwoPointConicalGradientLayout>();
+ (void)_outer;
+ auto gradientMatrix = _outer.gradientMatrix();
+ (void)gradientMatrix;
+ auto type = _outer.type();
+ (void)type;
+ auto isRadiusIncreasing = _outer.isRadiusIncreasing();
+ (void)isRadiusIncreasing;
+ auto isFocalOnCircle = _outer.isFocalOnCircle();
+ (void)isFocalOnCircle;
+ auto isWellBehaved = _outer.isWellBehaved();
+ (void)isWellBehaved;
+ auto isSwapped = _outer.isSwapped();
+ (void)isSwapped;
+ auto isNativelyFocal = _outer.isNativelyFocal();
+ (void)isNativelyFocal;
+ auto focalParams = _outer.focalParams();
+ (void)focalParams;
+ fFocalParamsVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType,
+ kDefault_GrSLPrecision, "focalParams");
+ SkString sk_TransformedCoords2D_0 = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]);
+ fragBuilder->codeAppendf(
+ "half2 p = half2(%s);\nhalf t = -1.0;\nhalf v = 1.0;\n@switch (%d) {\n case "
+ "1:\n {\n half r0_2 = %s.y;\n t = r0_2 - p.y * p.y;\n "
+ " if (t >= 0.0) {\n t = float(p.x) + sqrt(float(t));\n "
+ " } else {\n v = -1.0;\n }\n }\n "
+ "break;\n case 0:\n {\n half r0 = %s.x;\n @if (%s) "
+ "{\n t = length(p) - r0;\n } else {\n t = "
+ "-length(p) - r0;\n ",
+ sk_TransformedCoords2D_0.c_str(), (int)_outer.type(),
+ args.fUniformHandler->getUniformCStr(fFocalParamsVar),
+ args.fUniformHandler->getUniformCStr(fFocalParamsVar),
+ (_outer.isRadiusIncreasing() ? "true" : "false"));
+ fragBuilder->codeAppendf(
+ " }\n }\n break;\n case 2:\n {\n half invR1 = "
+ "%s.x;\n half fx = %s.y;\n half x_t = -1.0;\n @if "
+ "(%s) {\n x_t = dot(p, p) / p.x;\n } else if (%s) {\n "
+ " x_t = length(p) - p.x * invR1;\n } else {\n "
+ "half temp = p.x * p.x - p.y * p.y;\n if (temp >= 0.0) {\n "
+ " @if (%s || !%s) {\n x_t = "
+ "half(-sqrt(float(temp)) - float(p.x * invR1",
+ args.fUniformHandler->getUniformCStr(fFocalParamsVar),
+ args.fUniformHandler->getUniformCStr(fFocalParamsVar),
+ (_outer.isFocalOnCircle() ? "true" : "false"),
+ (_outer.isWellBehaved() ? "true" : "false"),
+ (_outer.isSwapped() ? "true" : "false"),
+ (_outer.isRadiusIncreasing() ? "true" : "false"));
+ fragBuilder->codeAppendf(
+ "));\n } else {\n x_t = "
+ "half(sqrt(float(temp)) - float(p.x * invR1));\n }\n "
+ " }\n }\n @if (!%s) {\n if (float(x_t) <= "
+ "0.0) {\n v = -1.0;\n }\n }\n "
+ " @if (%s) {\n @if (%s) {\n t = x_t;\n "
+ " } else {\n t = x_t + fx;\n }\n "
+ " } else {\n @if (%s) {",
+ (_outer.isWellBehaved() ? "true" : "false"),
+ (_outer.isRadiusIncreasing() ? "true" : "false"),
+ (_outer.isNativelyFocal() ? "true" : "false"),
+ (_outer.isNativelyFocal() ? "true" : "false"));
+ fragBuilder->codeAppendf(
+ "\n t = -x_t;\n } else {\n t "
+ "= -x_t + fx;\n }\n }\n @if (%s) {\n "
+ " t = 1.0 - t;\n }\n }\n break;\n}\n%s = half4(t, v, "
+ "0.0, 0.0);\n",
+ (_outer.isSwapped() ? "true" : "false"), args.fOutputColor);
+ }
+
+private:
+ void onSetData(const GrGLSLProgramDataManager& pdman,
+ const GrFragmentProcessor& _proc) override {
+ const GrTwoPointConicalGradientLayout& _outer =
+ _proc.cast<GrTwoPointConicalGradientLayout>();
+ {
+ const SkPoint& focalParamsValue = _outer.focalParams();
+ if (fFocalParamsPrev != focalParamsValue) {
+ fFocalParamsPrev = focalParamsValue;
+ pdman.set2f(fFocalParamsVar, focalParamsValue.fX, focalParamsValue.fY);
+ }
+ }
+ }
+ SkPoint fFocalParamsPrev = SkPoint::Make(SK_FloatNaN, SK_FloatNaN);
+ UniformHandle fFocalParamsVar;
+};
+GrGLSLFragmentProcessor* GrTwoPointConicalGradientLayout::onCreateGLSLInstance() const {
+ return new GrGLSLTwoPointConicalGradientLayout();
+}
+void GrTwoPointConicalGradientLayout::onGetGLSLProcessorKey(const GrShaderCaps& caps,
+ GrProcessorKeyBuilder* b) const {
+ b->add32((int32_t)fType);
+ b->add32((int32_t)fIsRadiusIncreasing);
+ b->add32((int32_t)fIsFocalOnCircle);
+ b->add32((int32_t)fIsWellBehaved);
+ b->add32((int32_t)fIsSwapped);
+ b->add32((int32_t)fIsNativelyFocal);
+}
+bool GrTwoPointConicalGradientLayout::onIsEqual(const GrFragmentProcessor& other) const {
+ const GrTwoPointConicalGradientLayout& that = other.cast<GrTwoPointConicalGradientLayout>();
+ (void)that;
+ if (fGradientMatrix != that.fGradientMatrix) return false;
+ if (fType != that.fType) return false;
+ if (fIsRadiusIncreasing != that.fIsRadiusIncreasing) return false;
+ if (fIsFocalOnCircle != that.fIsFocalOnCircle) return false;
+ if (fIsWellBehaved != that.fIsWellBehaved) return false;
+ if (fIsSwapped != that.fIsSwapped) return false;
+ if (fIsNativelyFocal != that.fIsNativelyFocal) return false;
+ if (fFocalParams != that.fFocalParams) return false;
+ return true;
+}
+GrTwoPointConicalGradientLayout::GrTwoPointConicalGradientLayout(
+ const GrTwoPointConicalGradientLayout& src)
+ : INHERITED(kGrTwoPointConicalGradientLayout_ClassID, src.optimizationFlags())
+ , fGradientMatrix(src.fGradientMatrix)
+ , fType(src.fType)
+ , fIsRadiusIncreasing(src.fIsRadiusIncreasing)
+ , fIsFocalOnCircle(src.fIsFocalOnCircle)
+ , fIsWellBehaved(src.fIsWellBehaved)
+ , fIsSwapped(src.fIsSwapped)
+ , fIsNativelyFocal(src.fIsNativelyFocal)
+ , fFocalParams(src.fFocalParams)
+ , fCoordTransform0(src.fCoordTransform0) {
+ this->addCoordTransform(&fCoordTransform0);
+}
+std::unique_ptr<GrFragmentProcessor> GrTwoPointConicalGradientLayout::clone() const {
+ return std::unique_ptr<GrFragmentProcessor>(new GrTwoPointConicalGradientLayout(*this));
+}
+
+// .fp files do not let you reference outside enum definitions, so we have to explicitly map
+// between the two compatible enum defs
+GrTwoPointConicalGradientLayout::Type convert_type(SkTwoPointConicalGradient::Type type) {
+ switch (type) {
+ case SkTwoPointConicalGradient::Type::kRadial:
+ return GrTwoPointConicalGradientLayout::Type::kRadial;
+ case SkTwoPointConicalGradient::Type::kStrip:
+ return GrTwoPointConicalGradientLayout::Type::kStrip;
+ case SkTwoPointConicalGradient::Type::kFocal:
+ return GrTwoPointConicalGradientLayout::Type::kFocal;
+ }
+ SkDEBUGFAIL("Should not be reachable");
+ return GrTwoPointConicalGradientLayout::Type::kRadial;
+}
+
+std::unique_ptr<GrFragmentProcessor> GrTwoPointConicalGradientLayout::Make(
+ const SkTwoPointConicalGradient& grad, const GrFPArgs& args) {
+ GrTwoPointConicalGradientLayout::Type grType = convert_type(grad.getType());
+
+ // The focalData struct is only valid if isFocal is true
+ const SkTwoPointConicalGradient::FocalData& focalData = grad.getFocalData();
+ bool isFocal = grType == Type::kFocal;
+
+ // Calculate optimization switches from gradient specification
+ bool isFocalOnCircle = isFocal && focalData.isFocalOnCircle();
+ bool isWellBehaved = isFocal && focalData.isWellBehaved();
+ bool isSwapped = isFocal && focalData.isSwapped();
+ bool isNativelyFocal = isFocal && focalData.isNativelyFocal();
+
+ // Type-specific calculations: isRadiusIncreasing, focalParams, and the gradient matrix.
+ // However, all types start with the total inverse local matrix calculated from the shader
+ // and args
+ bool isRadiusIncreasing;
+ SkPoint focalParams; // really just a 2D tuple
+ SkMatrix matrix;
+
+ // Initialize the base matrix
+ if (!grad.totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix)->invert(&matrix)) {
+ return nullptr;
+ }
+
+ if (isFocal) {
+ isRadiusIncreasing = (1 - focalData.fFocalX) > 0;
+
+ focalParams.set(1.0 / focalData.fR1, focalData.fFocalX);
+
+ matrix.postConcat(grad.getGradientMatrix());
+ } else if (grType == Type::kRadial) {
+ SkScalar dr = grad.getDiffRadius();
+ isRadiusIncreasing = dr >= 0;
+
+ SkScalar r0 = grad.getStartRadius() / dr;
+ focalParams.set(r0, r0 * r0);
+
+ // GPU radial matrix is different from the original matrix, since we map the diff radius
+ // to have |dr| = 1, so manually compute the final gradient matrix here.
+
+ // Map center to (0, 0)
+ matrix.postTranslate(-grad.getStartCenter().fX, -grad.getStartCenter().fY);
+
+ // scale |diffRadius| to 1
+ matrix.postScale(1 / dr, 1 / dr);
+ } else { // kStrip
+ isRadiusIncreasing = false; // kStrip doesn't use this flag
+
+ SkScalar r0 = grad.getStartRadius() / grad.getCenterX1();
+ focalParams.set(r0, r0 * r0);
+
+ matrix.postConcat(grad.getGradientMatrix());
+ }
+
+ return std::unique_ptr<GrFragmentProcessor>(new GrTwoPointConicalGradientLayout(
+ matrix, grType, isRadiusIncreasing, isFocalOnCircle, isWellBehaved, isSwapped,
+ isNativelyFocal, focalParams));
+}