Implement an explicit binary search-based analytic gradient colorizer

Provides a reasonably flexible fragment processor that defines another
colorizer implementation for gradients. It can support up to 8
interpolation intervals (which is 16 colors if every stop is a hard stop
or 9 colors if every stop is a smooth transition). It
supports mixtures of hard and smooth stops. It is conditionally compiled
into versions specific to the interval count (so it can produce up to
8 shader variants).

The GrGradientShader controller does not remove the single and dual
interval colorizers, which are useful specializations of this explicit
binary search colorizer. Similarly, since it can only handle up to 8
intervals, the texture colorizer is still used as a fallback.

Currently it does not employ capabilities detection to determine if the
hardware can support the number of required uniforms, which can become
substantial for the larger gradient configurations.

Bug: chromium:796479, chromium:729727, chromium:696603, chromium:543625, chromium:414254
Change-Id: Ia1f735a5019766ae4796cc22964b2913db34b95b
Reviewed-on: https://skia-review.googlesource.com/155080
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
Michael Ludwig 2018-09-24 10:11:01 -04:00 committed by Skia Commit-Bot
parent 4fb073ac73
commit dcc85fc610
9 changed files with 866 additions and 0 deletions

178
gm/analytic_gradients.cpp Normal file
View File

@ -0,0 +1,178 @@
/*
* 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 GM presents a variety of gradients meant to test the correctness of the analytic
* GrUnrolledBinaryGradientColorizer, which can handle arbitrary gradients with 1 to 8 interpolation
* intervals. These intervals can be either hardstops or smooth color transitions.
*
* It produces an image similar to that of GM_hardstop_gradients, but is arranged as follows:
*
* | Clamp |
* |________________|
* | M1 M2 M3 M4 |
* ___________|________________|
* 1 |
* 2 |
* 3 |
* 4 |
* 5 |
* 6 |
* 7 |
* 8 |
* The M-modes are different ways of interlveaving hardstops with smooth transitions:
* - M1 = All smooth transitions
* - M2 = All hard stops
* - M5 = Alternating smooth then hard
* - M6 = Alternating hard then smooth
*
* Only clamping is tested since this is focused more on within the interpolation region behavior,
* compared to overall behavior.
*/
#include "gm.h"
#include "SkGradientShader.h"
// All positions must be divided by the target interval count, which will produce the expected
// normalized position array for that interval number (assuming an appropriate color count is
// provided).
const int M1_POSITIONS[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
const int M2_POSITIONS[] = { 0, 1,1, 2,2, 3,3, 4,4, 5,5, 6,6, 7,7, 8 };
const int M3_POSITIONS[] = { 0, 1, 2,2, 3, 4,4, 5, 6,6, 7, 8 };
const int M4_POSITIONS[] = { 0, 1,1, 2, 3,3, 4, 5,5, 6, 7,7, 8 };
// Color count = index of first occurrence of interval count value in Mx_POSITIONS array.
const int INT1_COLOR_COUNTS[] = { 2, 2, 2, 2 };
const int INT2_COLOR_COUNTS[] = { 3, 4, 3, 4 };
const int INT3_COLOR_COUNTS[] = { 4, 6, 5, 5 };
const int INT4_COLOR_COUNTS[] = { 5, 8, 6, 7 };
const int INT5_COLOR_COUNTS[] = { 6, 10, 8, 8 };
const int INT6_COLOR_COUNTS[] = { 7, 12, 9, 10 };
const int INT7_COLOR_COUNTS[] = { 8, 14, 11, 11 };
const int INT8_COLOR_COUNTS[] = { 9, 16, 12, 13 };
// Cycle through defined colors for positions 0 through 8.
const SkColor COLORS[] = {
SK_ColorDKGRAY,
SK_ColorRED,
SK_ColorYELLOW,
SK_ColorGREEN,
SK_ColorCYAN,
SK_ColorBLUE,
SK_ColorMAGENTA,
SK_ColorBLACK,
SK_ColorLTGRAY
};
const int* INTERVAL_COLOR_COUNTS[] = {
INT1_COLOR_COUNTS,
INT2_COLOR_COUNTS,
INT3_COLOR_COUNTS,
INT4_COLOR_COUNTS,
INT5_COLOR_COUNTS,
INT6_COLOR_COUNTS,
INT7_COLOR_COUNTS,
INT8_COLOR_COUNTS
};
const int COLOR_COUNT = SK_ARRAY_COUNT(COLORS);
const int* M_POSITIONS[] = {
M1_POSITIONS,
M2_POSITIONS,
M3_POSITIONS,
M4_POSITIONS
};
const int WIDTH = 500;
const int HEIGHT = 500;
const int NUM_ROWS = 8;
const int NUM_COLS = 4;
const int CELL_WIDTH = WIDTH / NUM_COLS;
const int CELL_HEIGHT = HEIGHT / NUM_ROWS;
const int PAD_WIDTH = 3;
const int PAD_HEIGHT = 3;
const int RECT_WIDTH = CELL_WIDTH - (2 * PAD_WIDTH);
const int RECT_HEIGHT = CELL_HEIGHT - (2 * PAD_HEIGHT);
static void shade_rect(SkCanvas* canvas, sk_sp<SkShader> shader, int cellRow, int cellCol) {
SkPaint paint;
paint.setShader(shader);
canvas->save();
canvas->translate(SkIntToScalar(cellCol * CELL_WIDTH + PAD_WIDTH),
SkIntToScalar(cellRow * CELL_HEIGHT + PAD_HEIGHT));
const SkRect rect = SkRect::MakeWH(SkIntToScalar(RECT_WIDTH), SkIntToScalar(RECT_HEIGHT));
canvas->drawRect(rect, paint);
canvas->restore();
}
class AnalyticGradientShaderGM : public skiagm::GM {
public:
AnalyticGradientShaderGM() {
}
protected:
SkString onShortName() override {
return SkString("analytic_gradients");
}
SkISize onISize() override {
return SkISize::Make(1024, 512);
}
void onDraw(SkCanvas* canvas) override {
const SkPoint points[2] = { SkPoint::Make(0, 0), SkPoint::Make(RECT_WIDTH, 0.0) };
for (int cellRow = 0; cellRow < NUM_ROWS; cellRow++) {
// Each interval has 4 different color counts, one per mode
const int* colorCounts = INTERVAL_COLOR_COUNTS[cellRow]; // Has len = 4
for (int cellCol = 0; cellCol < NUM_COLS; cellCol++) {
// create_gradient_points(cellRow, cellCol, points);
// Get the color count dependent on interval and mode
int colorCount = colorCounts[cellCol];
// Get the positions given the mode
const int* layout = M_POSITIONS[cellCol];
// Collect positions and colors specific to the interval+mode normalizing the
// position based on the interval count (== cellRow+1)
SkAutoSTMalloc<4, SkColor> colors(colorCount);
SkAutoSTMalloc<4, SkScalar> positions(colorCount);
int j = 0;
for (int i = 0; i < colorCount; i++) {
positions[i] = SkIntToScalar(layout[i]) / (cellRow + 1);
colors[i] = COLORS[j % COLOR_COUNT];
j++;
}
auto shader = SkGradientShader::MakeLinear(
points,
colors.get(),
positions.get(),
colorCount,
SkShader::kClamp_TileMode,
0,
nullptr);
shade_rect(canvas, shader, cellRow, cellCol);
}
}
}
private:
typedef skiagm::GM INHERITED;
};
DEF_GM(return new AnalyticGradientShaderGM;)

View File

@ -16,6 +16,7 @@ gm_sources = [
"$_gm/all_bitmap_configs.cpp",
"$_gm/alpha_image.cpp",
"$_gm/alphagradients.cpp",
"$_gm/analytic_gradients.cpp",
"$_gm/animatedGif.cpp",
"$_gm/androidblendmodes.cpp",
"$_gm/animatedimageblurs.cpp",

View File

@ -406,6 +406,8 @@ skia_gpu_sources = [
"$_src/gpu/gradients/GrSingleIntervalGradientColorizer.h",
"$_src/gpu/gradients/GrTextureGradientColorizer.cpp",
"$_src/gpu/gradients/GrTextureGradientColorizer.h",
"$_src/gpu/gradients/GrUnrolledBinaryGradientColorizer.cpp",
"$_src/gpu/gradients/GrUnrolledBinaryGradientColorizer.h",
"$_src/gpu/gradients/GrLinearGradientLayout.cpp",
"$_src/gpu/gradients/GrLinearGradientLayout.h",
"$_src/gpu/gradients/GrRadialGradientLayout.cpp",

View File

@ -49,6 +49,7 @@ skia_gpu_processor_sources = [
"$_src/gpu/gradients/GrDualIntervalGradientColorizer.fp",
"$_src/gpu/gradients/GrSingleIntervalGradientColorizer.fp",
"$_src/gpu/gradients/GrTextureGradientColorizer.fp",
"$_src/gpu/gradients/GrUnrolledBinaryGradientColorizer.fp",
"$_src/gpu/gradients/GrLinearGradientLayout.fp",
"$_src/gpu/gradients/GrRadialGradientLayout.fp",
"$_src/gpu/gradients/GrSweepGradientLayout.fp",

View File

@ -144,6 +144,7 @@ public:
kGrTiledGradientEffect_ClassID,
kGrTwoPointConicalGradientLayout_ClassID,
kGrUnpremulInputFragmentProcessor_ClassID,
kGrUnrolledBinaryGradientColorizer_ClassID,
kGrYUVtoRGBEffect_ClassID,
kHighContrastFilterEffect_ClassID,
kInstanceProcessor_ClassID,

View File

@ -18,6 +18,7 @@
#include "GrDualIntervalGradientColorizer.h"
#include "GrSingleIntervalGradientColorizer.h"
#include "GrTextureGradientColorizer.h"
#include "GrUnrolledBinaryGradientColorizer.h"
#include "GrGradientBitmapCache.h"
#include "SkGr.h"
@ -99,6 +100,17 @@ static std::unique_ptr<GrFragmentProcessor> make_colorizer(const GrColor4f* colo
positions[offset + 1]);
}
// The single and dual intervals are a specialized case of the unrolled binary search colorizer
// which can analytically render gradients of up to 8 intervals (up to 9 or 16 colors depending
// on how many hard stops are inserted).
std::unique_ptr<GrFragmentProcessor> unrolled = GrUnrolledBinaryGradientColorizer::Make(
colors + offset, positions + offset, count);
if (unrolled) {
return unrolled;
}
// Otherwise fall back to a rasterized gradient sampled by a texture, which can handle
// arbitrary gradients (the only downside being sampling resolution).
return make_textured_colorizer(colors + offset, positions + offset, count, premul, args);
}

View File

@ -0,0 +1,380 @@
/*
* 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 GrUnrolledBinaryGradientColorizer.fp; do not modify.
**************************************************************************************************/
#include "GrUnrolledBinaryGradientColorizer.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLProgramBuilder.h"
#include "GrTexture.h"
#include "SkSLCPP.h"
#include "SkSLUtil.h"
class GrGLSLUnrolledBinaryGradientColorizer : public GrGLSLFragmentProcessor {
public:
GrGLSLUnrolledBinaryGradientColorizer() {}
void emitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
const GrUnrolledBinaryGradientColorizer& _outer =
args.fFp.cast<GrUnrolledBinaryGradientColorizer>();
(void)_outer;
auto intervalCount = _outer.intervalCount();
(void)intervalCount;
auto scale0_1 = _outer.scale0_1();
(void)scale0_1;
auto scale2_3 = _outer.scale2_3();
(void)scale2_3;
auto scale4_5 = _outer.scale4_5();
(void)scale4_5;
auto scale6_7 = _outer.scale6_7();
(void)scale6_7;
auto scale8_9 = _outer.scale8_9();
(void)scale8_9;
auto scale10_11 = _outer.scale10_11();
(void)scale10_11;
auto scale12_13 = _outer.scale12_13();
(void)scale12_13;
auto scale14_15 = _outer.scale14_15();
(void)scale14_15;
auto bias0_1 = _outer.bias0_1();
(void)bias0_1;
auto bias2_3 = _outer.bias2_3();
(void)bias2_3;
auto bias4_5 = _outer.bias4_5();
(void)bias4_5;
auto bias6_7 = _outer.bias6_7();
(void)bias6_7;
auto bias8_9 = _outer.bias8_9();
(void)bias8_9;
auto bias10_11 = _outer.bias10_11();
(void)bias10_11;
auto bias12_13 = _outer.bias12_13();
(void)bias12_13;
auto bias14_15 = _outer.bias14_15();
(void)bias14_15;
auto thresholds1_7 = _outer.thresholds1_7();
(void)thresholds1_7;
auto thresholds9_13 = _outer.thresholds9_13();
(void)thresholds9_13;
fScale0_1Var = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
kDefault_GrSLPrecision, "scale0_1");
if (intervalCount > 1) {
fScale2_3Var = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
kDefault_GrSLPrecision, "scale2_3");
}
if (intervalCount > 2) {
fScale4_5Var = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
kDefault_GrSLPrecision, "scale4_5");
}
if (intervalCount > 3) {
fScale6_7Var = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
kDefault_GrSLPrecision, "scale6_7");
}
if (intervalCount > 4) {
fScale8_9Var = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
kDefault_GrSLPrecision, "scale8_9");
}
if (intervalCount > 5) {
fScale10_11Var = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf4_GrSLType, kDefault_GrSLPrecision, "scale10_11");
}
if (intervalCount > 6) {
fScale12_13Var = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf4_GrSLType, kDefault_GrSLPrecision, "scale12_13");
}
if (intervalCount > 7) {
fScale14_15Var = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf4_GrSLType, kDefault_GrSLPrecision, "scale14_15");
}
fBias0_1Var = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
kDefault_GrSLPrecision, "bias0_1");
if (intervalCount > 1) {
fBias2_3Var = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
kDefault_GrSLPrecision, "bias2_3");
}
if (intervalCount > 2) {
fBias4_5Var = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
kDefault_GrSLPrecision, "bias4_5");
}
if (intervalCount > 3) {
fBias6_7Var = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
kDefault_GrSLPrecision, "bias6_7");
}
if (intervalCount > 4) {
fBias8_9Var = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType,
kDefault_GrSLPrecision, "bias8_9");
}
if (intervalCount > 5) {
fBias10_11Var = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf4_GrSLType, kDefault_GrSLPrecision, "bias10_11");
}
if (intervalCount > 6) {
fBias12_13Var = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf4_GrSLType, kDefault_GrSLPrecision, "bias12_13");
}
if (intervalCount > 7) {
fBias14_15Var = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf4_GrSLType, kDefault_GrSLPrecision, "bias14_15");
}
fThresholds1_7Var = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf4_GrSLType, kDefault_GrSLPrecision, "thresholds1_7");
fThresholds9_13Var = args.fUniformHandler->addUniform(
kFragment_GrShaderFlag, kHalf4_GrSLType, kDefault_GrSLPrecision, "thresholds9_13");
fragBuilder->codeAppendf(
"half t = %s.x;\nhalf4 scale, bias;\nif (%d <= 4 || t < %s.w) {\n if (%d <= 2 "
"|| t < %s.y) {\n if (%d <= 1 || t < %s.x) {\n scale = %s;\n "
" bias = %s;\n } else {\n scale = %s;\n bias = "
"%s;\n }\n } else {\n if (%d <= 3 || t < %s.z) {\n "
"scale = %s;\n bias = %s;\n } else {\n scale = %s;\n "
" bias = %s;\n }\n }\n} else {\n if (%d <= 6 || t < %s.y) "
"{\n if (%d <= 5 || t < ",
args.fInputColor, _outer.intervalCount(),
args.fUniformHandler->getUniformCStr(fThresholds1_7Var), _outer.intervalCount(),
args.fUniformHandler->getUniformCStr(fThresholds1_7Var), _outer.intervalCount(),
args.fUniformHandler->getUniformCStr(fThresholds1_7Var),
args.fUniformHandler->getUniformCStr(fScale0_1Var),
args.fUniformHandler->getUniformCStr(fBias0_1Var),
fScale2_3Var.isValid() ? args.fUniformHandler->getUniformCStr(fScale2_3Var)
: "half4(0)",
fBias2_3Var.isValid() ? args.fUniformHandler->getUniformCStr(fBias2_3Var)
: "half4(0)",
_outer.intervalCount(), args.fUniformHandler->getUniformCStr(fThresholds1_7Var),
fScale4_5Var.isValid() ? args.fUniformHandler->getUniformCStr(fScale4_5Var)
: "half4(0)",
fBias4_5Var.isValid() ? args.fUniformHandler->getUniformCStr(fBias4_5Var)
: "half4(0)",
fScale6_7Var.isValid() ? args.fUniformHandler->getUniformCStr(fScale6_7Var)
: "half4(0)",
fBias6_7Var.isValid() ? args.fUniformHandler->getUniformCStr(fBias6_7Var)
: "half4(0)",
_outer.intervalCount(), args.fUniformHandler->getUniformCStr(fThresholds9_13Var),
_outer.intervalCount());
fragBuilder->codeAppendf(
"%s.x) {\n scale = %s;\n bias = %s;\n } else {\n "
" scale = %s;\n bias = %s;\n }\n } else {\n if "
"(%d <= 7 || t < %s.z) {\n scale = %s;\n bias = %s;\n "
"} else {\n scale = %s;\n bias = %s;\n }\n "
"}\n}\n%s = t * scale + bias;\n",
args.fUniformHandler->getUniformCStr(fThresholds9_13Var),
fScale8_9Var.isValid() ? args.fUniformHandler->getUniformCStr(fScale8_9Var)
: "half4(0)",
fBias8_9Var.isValid() ? args.fUniformHandler->getUniformCStr(fBias8_9Var)
: "half4(0)",
fScale10_11Var.isValid() ? args.fUniformHandler->getUniformCStr(fScale10_11Var)
: "half4(0)",
fBias10_11Var.isValid() ? args.fUniformHandler->getUniformCStr(fBias10_11Var)
: "half4(0)",
_outer.intervalCount(), args.fUniformHandler->getUniformCStr(fThresholds9_13Var),
fScale12_13Var.isValid() ? args.fUniformHandler->getUniformCStr(fScale12_13Var)
: "half4(0)",
fBias12_13Var.isValid() ? args.fUniformHandler->getUniformCStr(fBias12_13Var)
: "half4(0)",
fScale14_15Var.isValid() ? args.fUniformHandler->getUniformCStr(fScale14_15Var)
: "half4(0)",
fBias14_15Var.isValid() ? args.fUniformHandler->getUniformCStr(fBias14_15Var)
: "half4(0)",
args.fOutputColor);
}
private:
void onSetData(const GrGLSLProgramDataManager& pdman,
const GrFragmentProcessor& _proc) override {
const GrUnrolledBinaryGradientColorizer& _outer =
_proc.cast<GrUnrolledBinaryGradientColorizer>();
{
pdman.set4fv(fScale0_1Var, 1, (_outer.scale0_1()).fRGBA);
if (fScale2_3Var.isValid()) {
pdman.set4fv(fScale2_3Var, 1, (_outer.scale2_3()).fRGBA);
}
if (fScale4_5Var.isValid()) {
pdman.set4fv(fScale4_5Var, 1, (_outer.scale4_5()).fRGBA);
}
if (fScale6_7Var.isValid()) {
pdman.set4fv(fScale6_7Var, 1, (_outer.scale6_7()).fRGBA);
}
if (fScale8_9Var.isValid()) {
pdman.set4fv(fScale8_9Var, 1, (_outer.scale8_9()).fRGBA);
}
if (fScale10_11Var.isValid()) {
pdman.set4fv(fScale10_11Var, 1, (_outer.scale10_11()).fRGBA);
}
if (fScale12_13Var.isValid()) {
pdman.set4fv(fScale12_13Var, 1, (_outer.scale12_13()).fRGBA);
}
if (fScale14_15Var.isValid()) {
pdman.set4fv(fScale14_15Var, 1, (_outer.scale14_15()).fRGBA);
}
pdman.set4fv(fBias0_1Var, 1, (_outer.bias0_1()).fRGBA);
if (fBias2_3Var.isValid()) {
pdman.set4fv(fBias2_3Var, 1, (_outer.bias2_3()).fRGBA);
}
if (fBias4_5Var.isValid()) {
pdman.set4fv(fBias4_5Var, 1, (_outer.bias4_5()).fRGBA);
}
if (fBias6_7Var.isValid()) {
pdman.set4fv(fBias6_7Var, 1, (_outer.bias6_7()).fRGBA);
}
if (fBias8_9Var.isValid()) {
pdman.set4fv(fBias8_9Var, 1, (_outer.bias8_9()).fRGBA);
}
if (fBias10_11Var.isValid()) {
pdman.set4fv(fBias10_11Var, 1, (_outer.bias10_11()).fRGBA);
}
if (fBias12_13Var.isValid()) {
pdman.set4fv(fBias12_13Var, 1, (_outer.bias12_13()).fRGBA);
}
if (fBias14_15Var.isValid()) {
pdman.set4fv(fBias14_15Var, 1, (_outer.bias14_15()).fRGBA);
}
pdman.set4fv(fThresholds1_7Var, 1,
reinterpret_cast<const float*>(&(_outer.thresholds1_7())));
pdman.set4fv(fThresholds9_13Var, 1,
reinterpret_cast<const float*>(&(_outer.thresholds9_13())));
}
}
UniformHandle fScale0_1Var;
UniformHandle fScale2_3Var;
UniformHandle fScale4_5Var;
UniformHandle fScale6_7Var;
UniformHandle fScale8_9Var;
UniformHandle fScale10_11Var;
UniformHandle fScale12_13Var;
UniformHandle fScale14_15Var;
UniformHandle fBias0_1Var;
UniformHandle fBias2_3Var;
UniformHandle fBias4_5Var;
UniformHandle fBias6_7Var;
UniformHandle fBias8_9Var;
UniformHandle fBias10_11Var;
UniformHandle fBias12_13Var;
UniformHandle fBias14_15Var;
UniformHandle fThresholds1_7Var;
UniformHandle fThresholds9_13Var;
};
GrGLSLFragmentProcessor* GrUnrolledBinaryGradientColorizer::onCreateGLSLInstance() const {
return new GrGLSLUnrolledBinaryGradientColorizer();
}
void GrUnrolledBinaryGradientColorizer::onGetGLSLProcessorKey(const GrShaderCaps& caps,
GrProcessorKeyBuilder* b) const {
b->add32((int32_t)fIntervalCount);
}
bool GrUnrolledBinaryGradientColorizer::onIsEqual(const GrFragmentProcessor& other) const {
const GrUnrolledBinaryGradientColorizer& that = other.cast<GrUnrolledBinaryGradientColorizer>();
(void)that;
if (fIntervalCount != that.fIntervalCount) return false;
if (fScale0_1 != that.fScale0_1) return false;
if (fScale2_3 != that.fScale2_3) return false;
if (fScale4_5 != that.fScale4_5) return false;
if (fScale6_7 != that.fScale6_7) return false;
if (fScale8_9 != that.fScale8_9) return false;
if (fScale10_11 != that.fScale10_11) return false;
if (fScale12_13 != that.fScale12_13) return false;
if (fScale14_15 != that.fScale14_15) return false;
if (fBias0_1 != that.fBias0_1) return false;
if (fBias2_3 != that.fBias2_3) return false;
if (fBias4_5 != that.fBias4_5) return false;
if (fBias6_7 != that.fBias6_7) return false;
if (fBias8_9 != that.fBias8_9) return false;
if (fBias10_11 != that.fBias10_11) return false;
if (fBias12_13 != that.fBias12_13) return false;
if (fBias14_15 != that.fBias14_15) return false;
if (fThresholds1_7 != that.fThresholds1_7) return false;
if (fThresholds9_13 != that.fThresholds9_13) return false;
return true;
}
GrUnrolledBinaryGradientColorizer::GrUnrolledBinaryGradientColorizer(
const GrUnrolledBinaryGradientColorizer& src)
: INHERITED(kGrUnrolledBinaryGradientColorizer_ClassID, src.optimizationFlags())
, fIntervalCount(src.fIntervalCount)
, fScale0_1(src.fScale0_1)
, fScale2_3(src.fScale2_3)
, fScale4_5(src.fScale4_5)
, fScale6_7(src.fScale6_7)
, fScale8_9(src.fScale8_9)
, fScale10_11(src.fScale10_11)
, fScale12_13(src.fScale12_13)
, fScale14_15(src.fScale14_15)
, fBias0_1(src.fBias0_1)
, fBias2_3(src.fBias2_3)
, fBias4_5(src.fBias4_5)
, fBias6_7(src.fBias6_7)
, fBias8_9(src.fBias8_9)
, fBias10_11(src.fBias10_11)
, fBias12_13(src.fBias12_13)
, fBias14_15(src.fBias14_15)
, fThresholds1_7(src.fThresholds1_7)
, fThresholds9_13(src.fThresholds9_13) {}
std::unique_ptr<GrFragmentProcessor> GrUnrolledBinaryGradientColorizer::clone() const {
return std::unique_ptr<GrFragmentProcessor>(new GrUnrolledBinaryGradientColorizer(*this));
}
static const int kMaxIntervals = 8;
std::unique_ptr<GrFragmentProcessor> GrUnrolledBinaryGradientColorizer::Make(
const GrColor4f* colors, const SkScalar* positions, int count) {
// Depending on how the positions resolve into hard stops or regular stops, the number of
// intervals specified by the number of colors/positions can change. For instance, a plain
// 3 color gradient is two intervals, but a 4 color gradient with a hard stop is also
// two intervals. At the most extreme end, an 8 interval gradient made entirely of hard
// stops has 16 colors.
if (count > 16) {
// Definitely cannot represent this gradient configuration
return nullptr;
}
// The raster implementation also uses scales and biases, but since they must be calculated
// after the dst color space is applied, it limits our ability to cache their values.
GrColor4f scales[kMaxIntervals];
GrColor4f biases[kMaxIntervals];
SkScalar thresholds[kMaxIntervals];
int intervalCount = 0;
for (int i = 0; i < count - 1; i++) {
if (intervalCount >= kMaxIntervals) {
// Already reached kMaxIntervals, and haven't run out of color stops so this
// gradient cannot be represented by this shader.
return nullptr;
}
SkScalar t0 = positions[i];
SkScalar t1 = positions[i + 1];
SkScalar dt = t1 - t0;
// If the interval is empty, skip to the next interval. This will automatically create
// distinct hard stop intervals as needed. It also protects against malformed gradients
// that have repeated hard stops at the very beginning that are effectively unreachable.
if (SkScalarNearlyZero(dt)) {
continue;
}
auto c0 = Sk4f::Load(colors[i].fRGBA);
auto c1 = Sk4f::Load(colors[i + 1].fRGBA);
auto scale = (c1 - c0) / dt;
auto bias = c0 - t0 * scale;
scale.store(scales + intervalCount);
bias.store(biases + intervalCount);
thresholds[intervalCount] = t1;
intervalCount++;
}
// For isEqual to make sense, set the unused values to something consistent
for (int i = intervalCount; i < kMaxIntervals; i++) {
scales[i] = GrColor4f::TransparentBlack();
biases[i] = GrColor4f::TransparentBlack();
thresholds[i] = 0.0;
}
return std::unique_ptr<GrFragmentProcessor>(new GrUnrolledBinaryGradientColorizer(
intervalCount, scales[0], scales[1], scales[2], scales[3], scales[4], scales[5],
scales[6], scales[7], biases[0], biases[1], biases[2], biases[3], biases[4], biases[5],
biases[6], biases[7],
SkRect::MakeLTRB(thresholds[0], thresholds[1], thresholds[2], thresholds[3]),
SkRect::MakeLTRB(thresholds[4], thresholds[5], thresholds[6], 0.0)));
}

View File

@ -0,0 +1,181 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
// Unrolled gradient code supporting up to 8 intervals that produces code
// targeting a specific interval count.
// Assumed to be between 1 and 8.
layout(key) in int intervalCount;
layout(ctype=GrColor4f) in uniform half4 scale0_1;
layout(ctype=GrColor4f, when=intervalCount > 1) in uniform half4 scale2_3;
layout(ctype=GrColor4f, when=intervalCount > 2) in uniform half4 scale4_5;
layout(ctype=GrColor4f, when=intervalCount > 3) in uniform half4 scale6_7;
layout(ctype=GrColor4f, when=intervalCount > 4) in uniform half4 scale8_9;
layout(ctype=GrColor4f, when=intervalCount > 5) in uniform half4 scale10_11;
layout(ctype=GrColor4f, when=intervalCount > 6) in uniform half4 scale12_13;
layout(ctype=GrColor4f, when=intervalCount > 7) in uniform half4 scale14_15;
layout(ctype=GrColor4f) in uniform half4 bias0_1;
layout(ctype=GrColor4f, when=intervalCount > 1) in uniform half4 bias2_3;
layout(ctype=GrColor4f, when=intervalCount > 2) in uniform half4 bias4_5;
layout(ctype=GrColor4f, when=intervalCount > 3) in uniform half4 bias6_7;
layout(ctype=GrColor4f, when=intervalCount > 4) in uniform half4 bias8_9;
layout(ctype=GrColor4f, when=intervalCount > 5) in uniform half4 bias10_11;
layout(ctype=GrColor4f, when=intervalCount > 6) in uniform half4 bias12_13;
layout(ctype=GrColor4f, when=intervalCount > 7) in uniform half4 bias14_15;
// The 7 threshold positions that define the boundaries of the 8 intervals (excluding t = 0, and t =
// 1) are packed into two half4's instead of having up to 7 separate scalar uniforms. For low
// interval counts, the extra components are ignored in the shader, but the uniform simplification
// is worth it. It is assumed thresholds are provided in increasing value, mapped as:
// - thresholds1_7.x = boundary between (0,1) and (2,3) -> 1_2
// - .y = boundary between (2,3) and (4,5) -> 3_4
// - .z = boundary between (4,5) and (6,7) -> 5_6
// - .w = boundary between (6,7) and (8,9) -> 7_8
// - thresholds9_13.x = boundary between (8,9) and (10,11) -> 9_10
// - .y = boundary between (10,11) and (12,13) -> 11_12
// - .z = boundary between (12,13) and (14,15) -> 13_14
// - .w = unused
in uniform half4 thresholds1_7;
in uniform half4 thresholds9_13;
void main() {
half t = sk_InColor.x;
half4 scale, bias;
// Explicit binary search for the proper interval that t falls within. The interval count
// checks are converted into constant expressions in the C++ generated SkSL, which are then
// optimized to the minimal number of branches for the specific interval count.
// thresholds1_7.w is mid point for intervals (0,7) and (8,15)
if (intervalCount <= 4 || t < thresholds1_7.w) {
// thresholds1_7.y is mid point for intervals (0,3) and (4,7)
if (intervalCount <= 2 || t < thresholds1_7.y) {
// thresholds1_7.x is mid point for intervals (0,1) and (2,3)
if (intervalCount <= 1 || t < thresholds1_7.x) {
scale = scale0_1;
bias = bias0_1;
} else {
scale = scale2_3;
bias = bias2_3;
}
} else {
// thresholds1_7.z is mid point for intervals (4,5) and (6,7)
if (intervalCount <= 3 || t < thresholds1_7.z) {
scale = scale4_5;
bias = bias4_5;
} else {
scale = scale6_7;
bias = bias6_7;
}
}
} else {
// thresholds9_13.y is mid point for intervals (8,11) and (12,15)
if (intervalCount <= 6 || t < thresholds9_13.y) {
// thresholds9_13.x is mid point for intervals (8,9) and (10,11)
if (intervalCount <= 5 || t < thresholds9_13.x) {
// interval 8-9
scale = scale8_9;
bias = bias8_9;
} else {
// interval 10-11
scale = scale10_11;
bias = bias10_11;
}
} else {
// thresholds9_13.z is mid point for intervals (12,13) and (14,15)
if (intervalCount <= 7 || t < thresholds9_13.z) {
// interval 12-13
scale = scale12_13;
bias = bias12_13;
} else {
// interval 14-15
scale = scale14_15;
bias = bias14_15;
}
}
}
sk_OutColor = t * scale + bias;
}
//////////////////////////////////////////////////////////////////////////////
@make {
static std::unique_ptr<GrFragmentProcessor> Make(const GrColor4f* colors,
const SkScalar* positions,
int count);
}
@cppEnd {
static const int kMaxIntervals = 8;
std::unique_ptr<GrFragmentProcessor> GrUnrolledBinaryGradientColorizer::Make(
const GrColor4f* colors, const SkScalar* positions, int count) {
// Depending on how the positions resolve into hard stops or regular stops, the number of
// intervals specified by the number of colors/positions can change. For instance, a plain
// 3 color gradient is two intervals, but a 4 color gradient with a hard stop is also
// two intervals. At the most extreme end, an 8 interval gradient made entirely of hard
// stops has 16 colors.
if (count > 16) {
// Definitely cannot represent this gradient configuration
return nullptr;
}
// The raster implementation also uses scales and biases, but since they must be calculated
// after the dst color space is applied, it limits our ability to cache their values.
GrColor4f scales[kMaxIntervals];
GrColor4f biases[kMaxIntervals];
SkScalar thresholds[kMaxIntervals];
int intervalCount = 0;
for (int i = 0; i < count - 1; i++) {
if (intervalCount >= kMaxIntervals) {
// Already reached kMaxIntervals, and haven't run out of color stops so this
// gradient cannot be represented by this shader.
return nullptr;
}
SkScalar t0 = positions[i];
SkScalar t1 = positions[i + 1];
SkScalar dt = t1 - t0;
// If the interval is empty, skip to the next interval. This will automatically create
// distinct hard stop intervals as needed. It also protects against malformed gradients
// that have repeated hard stops at the very beginning that are effectively unreachable.
if (SkScalarNearlyZero(dt)) {
continue;
}
auto c0 = Sk4f::Load(colors[i].fRGBA);
auto c1 = Sk4f::Load(colors[i + 1].fRGBA);
auto scale = (c1 - c0) / dt;
auto bias = c0 - t0 * scale;
scale.store(scales + intervalCount);
bias.store(biases + intervalCount);
thresholds[intervalCount] = t1;
intervalCount++;
}
// For isEqual to make sense, set the unused values to something consistent
for (int i = intervalCount; i < kMaxIntervals; i++) {
scales[i] = GrColor4f::TransparentBlack();
biases[i] = GrColor4f::TransparentBlack();
thresholds[i] = 0.0;
}
return std::unique_ptr<GrFragmentProcessor>(new GrUnrolledBinaryGradientColorizer(
intervalCount, scales[0], scales[1], scales[2], scales[3], scales[4], scales[5],
scales[6], scales[7], biases[0], biases[1], biases[2], biases[3], biases[4],
biases[5], biases[6], biases[7],
SkRect::MakeLTRB(thresholds[0], thresholds[1], thresholds[2], thresholds[3]),
SkRect::MakeLTRB(thresholds[4], thresholds[5], thresholds[6], 0.0)));
}
}

View File

@ -0,0 +1,110 @@
/*
* 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 GrUnrolledBinaryGradientColorizer.fp; do not modify.
**************************************************************************************************/
#ifndef GrUnrolledBinaryGradientColorizer_DEFINED
#define GrUnrolledBinaryGradientColorizer_DEFINED
#include "SkTypes.h"
#include "GrFragmentProcessor.h"
#include "GrCoordTransform.h"
class GrUnrolledBinaryGradientColorizer : public GrFragmentProcessor {
public:
int32_t intervalCount() const { return fIntervalCount; }
const GrColor4f& scale0_1() const { return fScale0_1; }
const GrColor4f& scale2_3() const { return fScale2_3; }
const GrColor4f& scale4_5() const { return fScale4_5; }
const GrColor4f& scale6_7() const { return fScale6_7; }
const GrColor4f& scale8_9() const { return fScale8_9; }
const GrColor4f& scale10_11() const { return fScale10_11; }
const GrColor4f& scale12_13() const { return fScale12_13; }
const GrColor4f& scale14_15() const { return fScale14_15; }
const GrColor4f& bias0_1() const { return fBias0_1; }
const GrColor4f& bias2_3() const { return fBias2_3; }
const GrColor4f& bias4_5() const { return fBias4_5; }
const GrColor4f& bias6_7() const { return fBias6_7; }
const GrColor4f& bias8_9() const { return fBias8_9; }
const GrColor4f& bias10_11() const { return fBias10_11; }
const GrColor4f& bias12_13() const { return fBias12_13; }
const GrColor4f& bias14_15() const { return fBias14_15; }
const SkRect& thresholds1_7() const { return fThresholds1_7; }
const SkRect& thresholds9_13() const { return fThresholds9_13; }
static std::unique_ptr<GrFragmentProcessor> Make(const GrColor4f* colors,
const SkScalar* positions,
int count);
GrUnrolledBinaryGradientColorizer(const GrUnrolledBinaryGradientColorizer& src);
std::unique_ptr<GrFragmentProcessor> clone() const override;
const char* name() const override { return "UnrolledBinaryGradientColorizer"; }
private:
GrUnrolledBinaryGradientColorizer(int32_t intervalCount,
GrColor4f scale0_1,
GrColor4f scale2_3,
GrColor4f scale4_5,
GrColor4f scale6_7,
GrColor4f scale8_9,
GrColor4f scale10_11,
GrColor4f scale12_13,
GrColor4f scale14_15,
GrColor4f bias0_1,
GrColor4f bias2_3,
GrColor4f bias4_5,
GrColor4f bias6_7,
GrColor4f bias8_9,
GrColor4f bias10_11,
GrColor4f bias12_13,
GrColor4f bias14_15,
SkRect thresholds1_7,
SkRect thresholds9_13)
: INHERITED(kGrUnrolledBinaryGradientColorizer_ClassID, kNone_OptimizationFlags)
, fIntervalCount(intervalCount)
, fScale0_1(scale0_1)
, fScale2_3(scale2_3)
, fScale4_5(scale4_5)
, fScale6_7(scale6_7)
, fScale8_9(scale8_9)
, fScale10_11(scale10_11)
, fScale12_13(scale12_13)
, fScale14_15(scale14_15)
, fBias0_1(bias0_1)
, fBias2_3(bias2_3)
, fBias4_5(bias4_5)
, fBias6_7(bias6_7)
, fBias8_9(bias8_9)
, fBias10_11(bias10_11)
, fBias12_13(bias12_13)
, fBias14_15(bias14_15)
, fThresholds1_7(thresholds1_7)
, fThresholds9_13(thresholds9_13) {}
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
bool onIsEqual(const GrFragmentProcessor&) const override;
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
int32_t fIntervalCount;
GrColor4f fScale0_1;
GrColor4f fScale2_3;
GrColor4f fScale4_5;
GrColor4f fScale6_7;
GrColor4f fScale8_9;
GrColor4f fScale10_11;
GrColor4f fScale12_13;
GrColor4f fScale14_15;
GrColor4f fBias0_1;
GrColor4f fBias2_3;
GrColor4f fBias4_5;
GrColor4f fBias6_7;
GrColor4f fBias8_9;
GrColor4f fBias10_11;
GrColor4f fBias12_13;
GrColor4f fBias14_15;
SkRect fThresholds1_7;
SkRect fThresholds9_13;
typedef GrFragmentProcessor INHERITED;
};
#endif