Reland "Base Gradient FP Refactor"
This reverts commit 1ea5656a285bcfef445d6f69eaab477e68595b54.
Reason for revert: Fixed google3 build failure
Original change's description:
> Revert "Base Gradient FP Refactor"
>
> This reverts commit 10f7a1e07554a362aef979d32ba288a009bdff90.
>
> Reason for revert: broke google3 roll
> Original change's description:
> > Base Gradient FP Refactor
> >
> > --
> >
> > Redefines how gradients will be written in the GPU back-end:
> >
> > They are split into three fragment processor components: master, layout, and colorizer.
> > The layout FP is responsible for converting the fragment position into an interpolant value, t.
> > Each high-level gradient--such as linear, radial, etc.--are implemented solely in a layout FP.
> > The colorizer FP is responsible for converting t into a color.
> > The master FP invokes the layout, clamps t into the proper domain, and then invokes the colorizer.
> > GrGradientShader provides factory functions to create FP graphs from SkGradientShader instances.
> > This pattern is documented in gpu/gradients/README.md.
> >
> > Goals for current CL
> > ====================
> >
> > Outline the FP components by providing .fp implementations for the simplest gradients.
> > Defines a two-color single interval colorizer and a linear gradient layout, and the master effect.
> > A MakeLinear() factory function is provided that can convert SkGradientShaders that fit these constraints.
> > SkLinearGradient first attempts to use the new system, falling back to the original GrGradientEffect.
> >
> > Future CLs
> > ==========
> >
> > To keep the CL reviews manageable, additional dependent CLs will be added that gradually replace past functionality.
> > A CL for each layout will be defined.
> > CLs for the different analytic colorizer cases and the textured gradient case will be defined.
> > Once the new system supports all current layouts and colorizer capabilities, all old GPU gradient code will be removed.
> > After this clean-up, analytic colorization can hopefully be expanded to reduce the usage of textured gradients.
> >
> > Bug: skia:
> > Change-Id: Iafe7b8b4071491a71c473babcd7bedda659150c1
> > Reviewed-on: https://skia-review.googlesource.com/148120
> > Commit-Queue: Michael Ludwig <michaelludwig@google.com>
> > Reviewed-by: Brian Salomon <bsalomon@google.com>
>
> TBR=bsalomon@google.com,michaelludwig@google.com
>
> Change-Id: Ib735e323795ac8874cb00b007a915786b50517a6
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: skia:
> Reviewed-on: https://skia-review.googlesource.com/153600
> Reviewed-by: Cary Clark <caryclark@google.com>
> Commit-Queue: Cary Clark <caryclark@google.com>
TBR=bsalomon@google.com,caryclark@google.com,michaelludwig@google.com
Change-Id: Ibf6ffbcb1af0dfbdac7317151aeb08f18f84c7fd
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:
Reviewed-on: https://skia-review.googlesource.com/153887
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/gradients/GrGradientShader.cpp b/src/gpu/gradients/GrGradientShader.cpp
new file mode 100644
index 0000000..83397a0
--- /dev/null
+++ b/src/gpu/gradients/GrGradientShader.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGradientShader.h"
+
+#include "GrClampedGradientEffect.h"
+#include "GrTiledGradientEffect.h"
+
+#include "GrLinearGradientLayout.h"
+#include "GrSingleIntervalGradientColorizer.h"
+
+#include "SkGradientShaderPriv.h"
+#include "GrColor.h"
+
+// Analyze the shader's color stops and positions and chooses an appropriate colorizer to represent
+// the gradient.
+static std::unique_ptr<GrFragmentProcessor> make_colorizer(const SkGradientShaderBase& shader,
+ const GrFPArgs& args, const GrColor4f* colors) {
+ // If there are hard stops at the beginning or end, the first and/or last color should be
+ // ignored by the colorizer since it should only be used in a clamped border color. By detecting
+ // and removing these stops at the beginning, it makes optimizing the remaining color stops
+ // simpler.
+
+ // SkGradientShaderBase guarantees that fOrigPos[0] == 0 by adding a dummy
+ bool bottomHardStop = shader.fOrigPos && SkScalarNearlyEqual(shader.fOrigPos[0],
+ shader.fOrigPos[1]);
+ // The same is true for fOrigPos[end] == 1
+ bool topHardStop = shader.fOrigPos &&
+ SkScalarNearlyEqual(shader.fOrigPos[shader.fColorCount - 2],
+ shader.fOrigPos[shader.fColorCount - 1]);
+
+ int offset = 0;
+ int count = shader.fColorCount;
+ if (bottomHardStop) {
+ offset += 1;
+ count--;
+ }
+ if (topHardStop) {
+ count--;
+ }
+
+ // Currently only supports 2-color single intervals. However, when the gradient has hard stops
+ // and is clamped, certain 3 or 4 color gradients are equivalent to a two color interval
+ if (count == 2) {
+ return GrSingleIntervalGradientColorizer::Make(colors[offset], colors[offset + 1]);
+ }
+
+ return nullptr;
+}
+
+// Combines the colorizer and layout with an appropriately configured master effect based on the
+// gradient's tile mode
+static std::unique_ptr<GrFragmentProcessor> make_gradient(const SkGradientShaderBase& shader,
+ const GrFPArgs& args, std::unique_ptr<GrFragmentProcessor> layout) {
+ // No shader is possible if a layout couldn't be created, e.g. a layout-specific Make() returned
+ // null.
+ if (layout == nullptr) {
+ return nullptr;
+ }
+
+ // Convert all colors into destination space and into GrColor4fs, and handle
+ // premul issues depending on the interpolation mode
+ bool inputPremul = shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag;
+ SkAutoSTMalloc<4, GrColor4f> colors(shader.fColorCount);
+ SkColor4fXformer xformedColors(shader.fOrigColors4f, shader.fColorCount,
+ shader.fColorSpace.get(), args.fDstColorSpaceInfo->colorSpace());
+ for (int i = 0; i < shader.fColorCount; i++) {
+ colors[i] = GrColor4f::FromSkColor4f(xformedColors.fColors[i]);
+ if (inputPremul) {
+ colors[i] = colors[i].premul();
+ }
+ }
+
+ // All gradients are colorized the same way, regardless of layout
+ std::unique_ptr<GrFragmentProcessor> colorizer = make_colorizer(shader, args, colors.get());
+ if (colorizer == nullptr) {
+ return nullptr;
+ }
+
+ // All tile modes are supported (unless something was added to SkShader)
+ std::unique_ptr<GrFragmentProcessor> master;
+ switch(shader.getTileMode()) {
+ case SkShader::kRepeat_TileMode:
+ master = GrTiledGradientEffect::Make(std::move(colorizer), std::move(layout),
+ /* mirror */ false);
+ break;
+ case SkShader::kMirror_TileMode:
+ master = GrTiledGradientEffect::Make(std::move(colorizer), std::move(layout),
+ /* mirror */ true);
+ break;
+ case SkShader::kClamp_TileMode:
+ // For the clamped mode, the border colors are the first and last colors, corresponding
+ // to t=0 and t=1, because SkGradientShaderBase enforces that by adding color stops as
+ // appropriate. If there is a hard stop, this grabs the expected outer colors for the
+ // border.
+ master = GrClampedGradientEffect::Make(std::move(colorizer), std::move(layout),
+ colors[0], colors[shader.fColorCount - 1]);
+ break;
+ case SkShader::kDecal_TileMode:
+ master = GrClampedGradientEffect::Make(std::move(colorizer), std::move(layout),
+ GrColor4f::TransparentBlack(),
+ GrColor4f::TransparentBlack());
+ break;
+ }
+
+ if (master == nullptr) {
+ // Unexpected tile mode
+ return nullptr;
+ }
+
+ if (!inputPremul) {
+ // When interpolating unpremul colors, the output of the gradient
+ // effect fp's will also be unpremul, so wrap it to ensure its premul.
+ // - this is unnecessary when interpolating premul colors since the
+ // output color is premul by nature
+ master = GrFragmentProcessor::PremulOutput(std::move(master));
+ }
+
+ return GrFragmentProcessor::MulChildByInputAlpha(std::move(master));
+}
+
+namespace GrGradientShader {
+
+std::unique_ptr<GrFragmentProcessor> MakeLinear(const SkLinearGradient& shader,
+ const GrFPArgs& args) {
+ return make_gradient(shader, args, GrLinearGradientLayout::Make(shader, args));
+}
+
+}