blob: 6e676460c4d23ac4bec896c9dbd7c1854d840612 [file] [log] [blame]
Michael Ludwig4f94ef62018-09-12 15:22:16 -04001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrGradientShader.h"
9
10#include "GrClampedGradientEffect.h"
11#include "GrTiledGradientEffect.h"
12
13#include "GrLinearGradientLayout.h"
Michael Ludwig4089df82018-09-12 15:22:37 -040014#include "GrRadialGradientLayout.h"
Michael Ludwig24d438b2018-09-12 15:22:50 -040015#include "GrSweepGradientLayout.h"
Michael Ludwig8f685082018-09-12 15:23:01 -040016#include "GrTwoPointConicalGradientLayout.h"
Michael Ludwig4089df82018-09-12 15:22:37 -040017
Michael Ludwig0495f7a2018-09-12 15:23:33 -040018#include "GrDualIntervalGradientColorizer.h"
Michael Ludwig4f94ef62018-09-12 15:22:16 -040019#include "GrSingleIntervalGradientColorizer.h"
20
21#include "SkGradientShaderPriv.h"
22#include "GrColor.h"
23
24// Analyze the shader's color stops and positions and chooses an appropriate colorizer to represent
25// the gradient.
Michael Ludwig0495f7a2018-09-12 15:23:33 -040026static std::unique_ptr<GrFragmentProcessor> make_colorizer(const GrColor4f* colors,
27 const SkScalar* positions,
28 int count) {
Michael Ludwig4f94ef62018-09-12 15:22:16 -040029 // If there are hard stops at the beginning or end, the first and/or last color should be
30 // ignored by the colorizer since it should only be used in a clamped border color. By detecting
31 // and removing these stops at the beginning, it makes optimizing the remaining color stops
32 // simpler.
33
Michael Ludwig0495f7a2018-09-12 15:23:33 -040034 // SkGradientShaderBase guarantees that pos[0] == 0 by adding a dummy
35 bool bottomHardStop = SkScalarNearlyEqual(positions[0], positions[1]);
36 // The same is true for pos[end] == 1
37 bool topHardStop = SkScalarNearlyEqual(positions[count - 2],
38 positions[count - 1]);
Michael Ludwig4f94ef62018-09-12 15:22:16 -040039
40 int offset = 0;
Michael Ludwig4f94ef62018-09-12 15:22:16 -040041 if (bottomHardStop) {
42 offset += 1;
43 count--;
44 }
45 if (topHardStop) {
46 count--;
47 }
48
Michael Ludwig0495f7a2018-09-12 15:23:33 -040049 // Two remaining colors means a single interval from 0 to 1
50 // (but it may have originally been a 3 or 4 color gradient with 1-2 hard stops at the ends)
Michael Ludwig4f94ef62018-09-12 15:22:16 -040051 if (count == 2) {
52 return GrSingleIntervalGradientColorizer::Make(colors[offset], colors[offset + 1]);
Michael Ludwig0495f7a2018-09-12 15:23:33 -040053 } else if (count == 3) {
54 // Must be a dual interval gradient, where the middle point is at offset+1 and the two
55 // intervals share the middle color stop.
56 return GrDualIntervalGradientColorizer::Make(colors[offset], colors[offset + 1],
57 colors[offset + 1], colors[offset + 2],
58 positions[offset + 1]);
59 } else if (count == 4 && SkScalarNearlyEqual(positions[offset + 1], positions[offset + 2])) {
60 // Two separate intervals that join at the same threshold position
61 return GrDualIntervalGradientColorizer::Make(colors[offset], colors[offset + 1],
62 colors[offset + 2], colors[offset + 3],
63 positions[offset + 1]);
Michael Ludwig4f94ef62018-09-12 15:22:16 -040064 }
65
66 return nullptr;
67}
68
69// Combines the colorizer and layout with an appropriately configured master effect based on the
70// gradient's tile mode
71static std::unique_ptr<GrFragmentProcessor> make_gradient(const SkGradientShaderBase& shader,
72 const GrFPArgs& args, std::unique_ptr<GrFragmentProcessor> layout) {
73 // No shader is possible if a layout couldn't be created, e.g. a layout-specific Make() returned
74 // null.
75 if (layout == nullptr) {
76 return nullptr;
77 }
78
79 // Convert all colors into destination space and into GrColor4fs, and handle
80 // premul issues depending on the interpolation mode
81 bool inputPremul = shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Flag;
82 SkAutoSTMalloc<4, GrColor4f> colors(shader.fColorCount);
83 SkColor4fXformer xformedColors(shader.fOrigColors4f, shader.fColorCount,
84 shader.fColorSpace.get(), args.fDstColorSpaceInfo->colorSpace());
85 for (int i = 0; i < shader.fColorCount; i++) {
86 colors[i] = GrColor4f::FromSkColor4f(xformedColors.fColors[i]);
87 if (inputPremul) {
88 colors[i] = colors[i].premul();
89 }
90 }
91
Michael Ludwig0495f7a2018-09-12 15:23:33 -040092 // SkGradientShader stores positions implicitly when they are evenly spaced, but the getPos()
93 // implementation performs a branch for every position index. Since the shader conversion
94 // requires lots of position tests, calculate all of the positions up front if needed.
95 SkTArray<SkScalar, true> implicitPos;
96 SkScalar* positions;
97 if (shader.fOrigPos) {
98 positions = shader.fOrigPos;
99 } else {
100 implicitPos.reserve(shader.fColorCount);
101 SkScalar posScale = SkScalarFastInvert(shader.fColorCount - 1);
102 for (int i = 0 ; i < shader.fColorCount; i++) {
103 implicitPos.push_back(SkIntToScalar(i) * posScale);
104 }
105 positions = implicitPos.begin();
106 }
107
Michael Ludwig4f94ef62018-09-12 15:22:16 -0400108 // All gradients are colorized the same way, regardless of layout
Michael Ludwig0495f7a2018-09-12 15:23:33 -0400109 std::unique_ptr<GrFragmentProcessor> colorizer = make_colorizer(colors.get(), positions,
110 shader.fColorCount);
Michael Ludwig4f94ef62018-09-12 15:22:16 -0400111 if (colorizer == nullptr) {
112 return nullptr;
113 }
114
115 // All tile modes are supported (unless something was added to SkShader)
116 std::unique_ptr<GrFragmentProcessor> master;
117 switch(shader.getTileMode()) {
118 case SkShader::kRepeat_TileMode:
119 master = GrTiledGradientEffect::Make(std::move(colorizer), std::move(layout),
120 /* mirror */ false);
121 break;
122 case SkShader::kMirror_TileMode:
123 master = GrTiledGradientEffect::Make(std::move(colorizer), std::move(layout),
124 /* mirror */ true);
125 break;
126 case SkShader::kClamp_TileMode:
127 // For the clamped mode, the border colors are the first and last colors, corresponding
128 // to t=0 and t=1, because SkGradientShaderBase enforces that by adding color stops as
129 // appropriate. If there is a hard stop, this grabs the expected outer colors for the
130 // border.
131 master = GrClampedGradientEffect::Make(std::move(colorizer), std::move(layout),
132 colors[0], colors[shader.fColorCount - 1]);
133 break;
134 case SkShader::kDecal_TileMode:
135 master = GrClampedGradientEffect::Make(std::move(colorizer), std::move(layout),
136 GrColor4f::TransparentBlack(),
137 GrColor4f::TransparentBlack());
138 break;
139 }
140
141 if (master == nullptr) {
142 // Unexpected tile mode
143 return nullptr;
144 }
145
146 if (!inputPremul) {
147 // When interpolating unpremul colors, the output of the gradient
148 // effect fp's will also be unpremul, so wrap it to ensure its premul.
149 // - this is unnecessary when interpolating premul colors since the
150 // output color is premul by nature
151 master = GrFragmentProcessor::PremulOutput(std::move(master));
152 }
153
154 return GrFragmentProcessor::MulChildByInputAlpha(std::move(master));
155}
156
157namespace GrGradientShader {
158
159std::unique_ptr<GrFragmentProcessor> MakeLinear(const SkLinearGradient& shader,
160 const GrFPArgs& args) {
161 return make_gradient(shader, args, GrLinearGradientLayout::Make(shader, args));
162}
163
Michael Ludwig4089df82018-09-12 15:22:37 -0400164std::unique_ptr<GrFragmentProcessor> MakeRadial(const SkRadialGradient& shader,
165 const GrFPArgs& args) {
166 return make_gradient(shader,args, GrRadialGradientLayout::Make(shader, args));
167}
168
Michael Ludwig24d438b2018-09-12 15:22:50 -0400169std::unique_ptr<GrFragmentProcessor> MakeSweep(const SkSweepGradient& shader,
170 const GrFPArgs& args) {
171 return make_gradient(shader,args, GrSweepGradientLayout::Make(shader, args));
172}
173
Michael Ludwig8f685082018-09-12 15:23:01 -0400174std::unique_ptr<GrFragmentProcessor> MakeConical(const SkTwoPointConicalGradient& shader,
175 const GrFPArgs& args) {
176 return make_gradient(shader, args, GrTwoPointConicalGradientLayout::Make(shader, args));
177}
178
Michael Ludwig4f94ef62018-09-12 15:22:16 -0400179}