Implement dual interval gradients
Adds a new colorizer implementation that supports gradients with two
interpolation intervals. The two intervals can share a middle color
to represent the usual 3-color gradient, or can have different colors
to represent a hard stop at an arbitrary point.
Bug: skia:
Change-Id: I8c73705e83b99e28ad5c834230ced4e3b7b9d1c4
Reviewed-on: https://skia-review.googlesource.com/150700
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/gradients/GrGradientShader.cpp b/src/gpu/gradients/GrGradientShader.cpp
index 9e6fefa..6e67646 100644
--- a/src/gpu/gradients/GrGradientShader.cpp
+++ b/src/gpu/gradients/GrGradientShader.cpp
@@ -15,6 +15,7 @@
#include "GrSweepGradientLayout.h"
#include "GrTwoPointConicalGradientLayout.h"
+#include "GrDualIntervalGradientColorizer.h"
#include "GrSingleIntervalGradientColorizer.h"
#include "SkGradientShaderPriv.h"
@@ -22,23 +23,21 @@
// 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) {
+static std::unique_ptr<GrFragmentProcessor> make_colorizer(const GrColor4f* colors,
+ const SkScalar* positions,
+ int count) {
// 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]);
+ // SkGradientShaderBase guarantees that pos[0] == 0 by adding a dummy
+ bool bottomHardStop = SkScalarNearlyEqual(positions[0], positions[1]);
+ // The same is true for pos[end] == 1
+ bool topHardStop = SkScalarNearlyEqual(positions[count - 2],
+ positions[count - 1]);
int offset = 0;
- int count = shader.fColorCount;
if (bottomHardStop) {
offset += 1;
count--;
@@ -47,10 +46,21 @@
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
+ // Two remaining colors means a single interval from 0 to 1
+ // (but it may have originally been a 3 or 4 color gradient with 1-2 hard stops at the ends)
if (count == 2) {
return GrSingleIntervalGradientColorizer::Make(colors[offset], colors[offset + 1]);
+ } else if (count == 3) {
+ // Must be a dual interval gradient, where the middle point is at offset+1 and the two
+ // intervals share the middle color stop.
+ return GrDualIntervalGradientColorizer::Make(colors[offset], colors[offset + 1],
+ colors[offset + 1], colors[offset + 2],
+ positions[offset + 1]);
+ } else if (count == 4 && SkScalarNearlyEqual(positions[offset + 1], positions[offset + 2])) {
+ // Two separate intervals that join at the same threshold position
+ return GrDualIntervalGradientColorizer::Make(colors[offset], colors[offset + 1],
+ colors[offset + 2], colors[offset + 3],
+ positions[offset + 1]);
}
return nullptr;
@@ -79,8 +89,25 @@
}
}
+ // SkGradientShader stores positions implicitly when they are evenly spaced, but the getPos()
+ // implementation performs a branch for every position index. Since the shader conversion
+ // requires lots of position tests, calculate all of the positions up front if needed.
+ SkTArray<SkScalar, true> implicitPos;
+ SkScalar* positions;
+ if (shader.fOrigPos) {
+ positions = shader.fOrigPos;
+ } else {
+ implicitPos.reserve(shader.fColorCount);
+ SkScalar posScale = SkScalarFastInvert(shader.fColorCount - 1);
+ for (int i = 0 ; i < shader.fColorCount; i++) {
+ implicitPos.push_back(SkIntToScalar(i) * posScale);
+ }
+ positions = implicitPos.begin();
+ }
+
// All gradients are colorized the same way, regardless of layout
- std::unique_ptr<GrFragmentProcessor> colorizer = make_colorizer(shader, args, colors.get());
+ std::unique_ptr<GrFragmentProcessor> colorizer = make_colorizer(colors.get(), positions,
+ shader.fColorCount);
if (colorizer == nullptr) {
return nullptr;
}