1 /*
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 "src/gpu/ganesh/gradients/GrGradientShader.h"
9
10 #include "include/core/SkColorSpace.h"
11 #include "include/gpu/GrRecordingContext.h"
12 #include "src/base/SkMathPriv.h"
13 #include "src/core/SkColorSpacePriv.h"
14 #include "src/core/SkRuntimeEffectPriv.h"
15 #include "src/gpu/ganesh/GrCaps.h"
16 #include "src/gpu/ganesh/GrColor.h"
17 #include "src/gpu/ganesh/GrColorInfo.h"
18 #include "src/gpu/ganesh/GrColorSpaceXform.h"
19 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
20 #include "src/gpu/ganesh/SkGr.h"
21 #include "src/gpu/ganesh/effects/GrMatrixEffect.h"
22 #include "src/gpu/ganesh/effects/GrSkSLFP.h"
23 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
24 #include "src/gpu/ganesh/gradients/GrGradientBitmapCache.h"
25
26 using Vec4 = skvx::Vec<4, float>;
27
28 // Intervals smaller than this (that aren't hard stops) on low-precision-only devices force us to
29 // use the textured gradient
30 static const SkScalar kLowPrecisionIntervalLimit = 0.01f;
31
32 // Each cache entry costs 1K or 2K of RAM. Each bitmap will be 1x256 at either 32bpp or 64bpp.
33 static const int kMaxNumCachedGradientBitmaps = 32;
34 static const int kGradientTextureSize = 256;
35
36 // NOTE: signature takes raw pointers to the color/pos arrays and a count to make it easy for
37 // MakeColorizer to transparently take care of hard stops at the end points of the gradient.
38 // TODO(skia:13108): This code is totally wrong when using a non-destination interpolation space.
39 // We need to plumb the interpolation information to the gradient cache, and actually do the work
40 // to convert back to destination space before filling out the bitmap (otherwise we might have
41 // values that don't fit in 8888). This will also mean that the top-level colorizer factory will
42 // need to return whether or not the interpolated -> dst transform is still necessary.
make_textured_colorizer(const SkPMColor4f * colors,const SkScalar * positions,int count,bool premul,const GrFPArgs & args)43 static std::unique_ptr<GrFragmentProcessor> make_textured_colorizer(const SkPMColor4f* colors,
44 const SkScalar* positions, int count, bool premul, const GrFPArgs& args) {
45 static GrGradientBitmapCache gCache(kMaxNumCachedGradientBitmaps, kGradientTextureSize);
46
47 // Use 8888 or F16, depending on the destination config.
48 // TODO: Use 1010102 for opaque gradients, at least if destination is 1010102?
49 SkColorType colorType = kRGBA_8888_SkColorType;
50 if (GrColorTypeIsWiderThan(args.fDstColorInfo->colorType(), 8)) {
51 auto f16Format = args.fContext->priv().caps()->getDefaultBackendFormat(
52 GrColorType::kRGBA_F16, GrRenderable::kNo);
53 if (f16Format.isValid()) {
54 colorType = kRGBA_F16_SkColorType;
55 }
56 }
57 SkAlphaType alphaType = premul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
58
59 SkBitmap bitmap;
60 gCache.getGradient(colors, positions, count, colorType, alphaType, &bitmap);
61 SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width()));
62 SkASSERT(bitmap.isImmutable());
63
64 auto view = std::get<0>(GrMakeCachedBitmapProxyView(
65 args.fContext, bitmap, /*label=*/"MakeTexturedColorizer", GrMipmapped::kNo));
66 if (!view) {
67 SkDebugf("Gradient won't draw. Could not create texture.");
68 return nullptr;
69 }
70
71 auto m = SkMatrix::Scale(view.width(), 1.f);
72 return GrTextureEffect::Make(std::move(view), alphaType, m, GrSamplerState::Filter::kLinear);
73 }
74
75
make_single_interval_colorizer(const SkPMColor4f & start,const SkPMColor4f & end)76 static std::unique_ptr<GrFragmentProcessor> make_single_interval_colorizer(const SkPMColor4f& start,
77 const SkPMColor4f& end) {
78 static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
79 "uniform half4 start;"
80 "uniform half4 end;"
81 "half4 main(float2 coord) {"
82 // Clamping and/or wrapping was already handled by the parent shader so the output
83 // color is a simple lerp.
84 "return mix(start, end, half(coord.x));"
85 "}"
86 );
87 return GrSkSLFP::Make(effect, "SingleIntervalColorizer", /*inputFP=*/nullptr,
88 GrSkSLFP::OptFlags::kNone,
89 "start", start,
90 "end", end);
91 }
92
make_dual_interval_colorizer(const SkPMColor4f & c0,const SkPMColor4f & c1,const SkPMColor4f & c2,const SkPMColor4f & c3,float threshold)93 static std::unique_ptr<GrFragmentProcessor> make_dual_interval_colorizer(const SkPMColor4f& c0,
94 const SkPMColor4f& c1,
95 const SkPMColor4f& c2,
96 const SkPMColor4f& c3,
97 float threshold) {
98 static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
99 "uniform float4 scale[2];"
100 "uniform float4 bias[2];"
101 "uniform half threshold;"
102
103 "half4 main(float2 coord) {"
104 "half t = half(coord.x);"
105
106 "float4 s, b;"
107 "if (t < threshold) {"
108 "s = scale[0];"
109 "b = bias[0];"
110 "} else {"
111 "s = scale[1];"
112 "b = bias[1];"
113 "}"
114
115 "return half4(t * s + b);"
116 "}"
117 );
118
119 // Derive scale and biases from the 4 colors and threshold
120 Vec4 vc0 = Vec4::Load(c0.vec());
121 Vec4 vc1 = Vec4::Load(c1.vec());
122 Vec4 vc2 = Vec4::Load(c2.vec());
123 Vec4 vc3 = Vec4::Load(c3.vec());
124
125 const Vec4 scale[2] = {(vc1 - vc0) / threshold,
126 (vc3 - vc2) / (1 - threshold)};
127 const Vec4 bias[2] = {vc0,
128 vc2 - threshold * scale[1]};
129 return GrSkSLFP::Make(effect, "DualIntervalColorizer", /*inputFP=*/nullptr,
130 GrSkSLFP::OptFlags::kNone,
131 "scale", SkSpan(scale),
132 "bias", SkSpan(bias),
133 "threshold", threshold);
134 }
135
136 // The "unrolled" colorizer contains hand-written nested ifs which perform a binary search.
137 // This works on ES2 hardware that doesn't support non-constant array indexes.
138 // However, to keep code size under control, we are limited to a small number of stops.
139 static constexpr int kMaxUnrolledColorCount = 16;
140 static constexpr int kMaxUnrolledIntervalCount = kMaxUnrolledColorCount / 2;
141
make_unrolled_colorizer(int intervalCount,const SkPMColor4f * scale,const SkPMColor4f * bias,SkRect thresholds1_7,SkRect thresholds9_13)142 static std::unique_ptr<GrFragmentProcessor> make_unrolled_colorizer(int intervalCount,
143 const SkPMColor4f* scale,
144 const SkPMColor4f* bias,
145 SkRect thresholds1_7,
146 SkRect thresholds9_13) {
147 SkASSERT(intervalCount >= 1 && intervalCount <= 8);
148
149 static SkOnce once[kMaxUnrolledIntervalCount];
150 static const SkRuntimeEffect* effects[kMaxUnrolledIntervalCount];
151
152 once[intervalCount - 1]([intervalCount] {
153 SkString sksl;
154
155 // The 7 threshold positions that define the boundaries of the 8 intervals (excluding t = 0,
156 // and t = 1) are packed into two half4s instead of having up to 7 separate scalar uniforms.
157 // For low interval counts, the extra components are ignored in the shader, but the uniform
158 // simplification is worth it. It is assumed thresholds are provided in increasing value,
159 // mapped as:
160 // - thresholds1_7.x = boundary between (0,1) and (2,3) -> 1_2
161 // - .y = boundary between (2,3) and (4,5) -> 3_4
162 // - .z = boundary between (4,5) and (6,7) -> 5_6
163 // - .w = boundary between (6,7) and (8,9) -> 7_8
164 // - thresholds9_13.x = boundary between (8,9) and (10,11) -> 9_10
165 // - .y = boundary between (10,11) and (12,13) -> 11_12
166 // - .z = boundary between (12,13) and (14,15) -> 13_14
167 // - .w = unused
168 sksl.append("uniform half4 thresholds1_7, thresholds9_13;");
169
170 // With the current hardstop detection threshold of 0.00024, the maximum scale and bias
171 // values will be on the order of 4k (since they divide by dt). That is well outside the
172 // precision capabilities of half floats, which can lead to inaccurate gradient calculations
173 sksl.appendf("uniform float4 scale[%d];", intervalCount);
174 sksl.appendf("uniform float4 bias[%d];", intervalCount);
175
176 // Explicit binary search for the proper interval that t falls within. The interval
177 // count checks are constant expressions, which are then optimized to the minimal number
178 // of branches for the specific interval count.
179 sksl.appendf(
180 "half4 main(float2 coord) {"
181 "half t = half(coord.x);"
182 "float4 s, b;"
183 // thresholds1_7.w is mid point for intervals (0,7) and (8,15)
184 "if (%d <= 4 || t < thresholds1_7.w) {"
185 // thresholds1_7.y is mid point for intervals (0,3) and (4,7)
186 "if (%d <= 2 || t < thresholds1_7.y) {"
187 // thresholds1_7.x is mid point for intervals (0,1) and (2,3)
188 "if (%d <= 1 || t < thresholds1_7.x) {"
189 "%s" // s = scale[0]; b = bias[0];
190 "} else {"
191 "%s" // s = scale[1]; b = bias[1];
192 "}"
193 "} else {"
194 // thresholds1_7.z is mid point for intervals (4,5) and (6,7)
195 "if (%d <= 3 || t < thresholds1_7.z) {"
196 "%s" // s = scale[2]; b = bias[2];
197 "} else {"
198 "%s" // s = scale[3]; b = bias[3];
199 "}"
200 "}"
201 "} else {"
202 // thresholds9_13.y is mid point for intervals (8,11) and (12,15)
203 "if (%d <= 6 || t < thresholds9_13.y) {"
204 // thresholds9_13.x is mid point for intervals (8,9) and (10,11)
205 "if (%d <= 5 || t < thresholds9_13.x) {"
206 "%s"
207 "} else {"
208 "%s" // s = scale[5]; b = bias[5];
209 "}"
210 "} else {"
211 // thresholds9_13.z is mid point for intervals (12,13) and (14,15)
212 "if (%d <= 7 || t < thresholds9_13.z) {"
213 "%s" // s = scale[6]; b = bias[6];
214 "} else {"
215 "%s" // s = scale[7]; b = bias[7];
216 "}"
217 "}"
218 "}"
219 "return t * s + b;"
220 "}"
221 , intervalCount,
222 intervalCount,
223 intervalCount,
224 (intervalCount <= 0) ? "" : "s = scale[0]; b = bias[0];",
225 (intervalCount <= 1) ? "" : "s = scale[1]; b = bias[1];",
226 intervalCount,
227 (intervalCount <= 2) ? "" : "s = scale[2]; b = bias[2];",
228 (intervalCount <= 3) ? "" : "s = scale[3]; b = bias[3];",
229 intervalCount,
230 intervalCount,
231 (intervalCount <= 4) ? "" : "s = scale[4]; b = bias[4];",
232 (intervalCount <= 5) ? "" : "s = scale[5]; b = bias[5];",
233 intervalCount,
234 (intervalCount <= 6) ? "" : "s = scale[6]; b = bias[6];",
235 (intervalCount <= 7) ? "" : "s = scale[7]; b = bias[7];");
236
237 auto result = SkRuntimeEffect::MakeForShader(std::move(sksl));
238 SkASSERTF(result.effect, "%s", result.errorText.c_str());
239 effects[intervalCount - 1] = result.effect.release();
240 });
241
242 return GrSkSLFP::Make(effects[intervalCount - 1], "UnrolledBinaryColorizer",
243 /*inputFP=*/nullptr, GrSkSLFP::OptFlags::kNone,
244 "thresholds1_7", thresholds1_7,
245 "thresholds9_13", thresholds9_13,
246 "scale", SkSpan(scale, intervalCount),
247 "bias", SkSpan(bias, intervalCount));
248 }
249
250 // The "looping" colorizer uses a real loop to binary-search the array of gradient stops.
251 static constexpr int kMaxLoopingColorCount = 128;
252 static constexpr int kMaxLoopingIntervalCount = kMaxLoopingColorCount / 2;
253
make_looping_colorizer(int intervalCount,const SkPMColor4f * scale,const SkPMColor4f * bias,const SkScalar * thresholds)254 static std::unique_ptr<GrFragmentProcessor> make_looping_colorizer(int intervalCount,
255 const SkPMColor4f* scale,
256 const SkPMColor4f* bias,
257 const SkScalar* thresholds) {
258 SkASSERT(intervalCount >= 1 && intervalCount <= kMaxLoopingIntervalCount);
259 SkASSERT((intervalCount & 3) == 0); // intervals are required to come in groups of four
260 int intervalChunks = intervalCount / 4;
261 int cacheIndex = (size_t)intervalChunks - 1;
262
263 struct EffectCacheEntry {
264 SkOnce once;
265 const SkRuntimeEffect* effect;
266 };
267
268 static EffectCacheEntry effectCache[kMaxLoopingIntervalCount / 4];
269 SkASSERT(cacheIndex >= 0 && cacheIndex < (int)std::size(effectCache));
270 EffectCacheEntry* cacheEntry = &effectCache[cacheIndex];
271
272 cacheEntry->once([intervalCount, intervalChunks, cacheEntry] {
273 SkString sksl;
274
275 // Binary search for the interval that `t` falls within. We can precalculate the number of
276 // loop iterations we need, and we know `t` will always be in range, so we can just loop a
277 // fixed number of times and can be guaranteed to have found the proper element.
278 //
279 // Threshold values are stored in half4s to keep them compact, so the last two rounds of
280 // binary search are hand-unrolled to allow them to use swizzles.
281 //
282 // Note that this colorizer is also designed to handle the case of exactly 4 intervals (a
283 // single chunk). In this case, the binary search for-loop will optimize away entirely, as
284 // it can be proven to execute zero times. We also optimize away the calculation of `4 *
285 // chunk` near the end via an if statement, as the result will always be in chunk 0.
286 int loopCount = SkNextLog2(intervalChunks);
287 sksl.appendf(
288 "#version 300\n" // important space to separate token.
289 "uniform half4 thresholds[%d];"
290 "uniform float4 scale[%d];"
291 "uniform float4 bias[%d];"
292
293 "half4 main(float2 coord) {"
294 "half t = half(coord.x);"
295
296 // Choose a chunk from thresholds via binary search in a loop.
297 "int low = 0;"
298 "int high = %d;"
299 "int chunk = %d;"
300 "for (int loop = 0; loop < %d; ++loop) {"
301 "if (t < thresholds[chunk].w) {"
302 "high = chunk;"
303 "} else {"
304 "low = chunk + 1;"
305 "}"
306 "chunk = (low + high) / 2;"
307 "}"
308
309 // Choose the final position via explicit 4-way binary search.
310 "int pos;"
311 "if (t < thresholds[chunk].y) {"
312 "pos = (t < thresholds[chunk].x) ? 0 : 1;"
313 "} else {"
314 "pos = (t < thresholds[chunk].z) ? 2 : 3;"
315 "}"
316 "if (%d > 0) {"
317 "pos += 4 * chunk;"
318 "}"
319 "return t * scale[pos] + bias[pos];"
320 "}"
321 , /* thresholds: */ intervalChunks,
322 /* scale: */ intervalCount,
323 /* bias: */ intervalCount,
324 /* high: */ intervalChunks - 1,
325 /* chunk: */ (intervalChunks - 1) / 2,
326 /* loopCount: */ loopCount,
327 /* if (loopCount > 0): */ loopCount);
328
329 auto result = SkRuntimeEffect::MakeForShader(std::move(sksl));
330 SkASSERTF(result.effect, "%s", result.errorText.c_str());
331 cacheEntry->effect = result.effect.release();
332 });
333
334 return GrSkSLFP::Make(cacheEntry->effect, "LoopingBinaryColorizer",
335 /*inputFP=*/nullptr, GrSkSLFP::OptFlags::kNone,
336 "thresholds", SkSpan((const SkV4*)thresholds, intervalChunks),
337 "scale", SkSpan(scale, intervalCount),
338 "bias", SkSpan(bias, intervalCount));
339 }
340
341 // Converts an input array of {colors, positions} into an array of {scales, biases, thresholds}.
342 // The length of the result array may differ from the input due to hard-stops or empty intervals.
build_intervals(int inputLength,const SkPMColor4f * inColors,const SkScalar * inPositions,int outputLength,SkPMColor4f * outScales,SkPMColor4f * outBiases,SkScalar * outThresholds)343 int build_intervals(int inputLength,
344 const SkPMColor4f* inColors,
345 const SkScalar* inPositions,
346 int outputLength,
347 SkPMColor4f* outScales,
348 SkPMColor4f* outBiases,
349 SkScalar* outThresholds) {
350 // Depending on how the positions resolve into hard stops or regular stops, the number of
351 // intervals specified by the number of colors/positions can change. For instance, a plain
352 // 3 color gradient is two intervals, but a 4 color gradient with a hard stop is also
353 // two intervals. At the most extreme end, an 8 interval gradient made entirely of hard
354 // stops has 16 colors.
355 int intervalCount = 0;
356 for (int i = 0; i < inputLength - 1; i++) {
357 if (intervalCount >= outputLength) {
358 // Already reached our output limit, and haven't run out of color stops. This gradient
359 // cannot be represented without more intervals.
360 return 0;
361 }
362
363 SkScalar t0 = inPositions[i];
364 SkScalar t1 = inPositions[i + 1];
365 SkScalar dt = t1 - t0;
366 // If the interval is empty, skip to the next interval. This will automatically create
367 // distinct hard stop intervals as needed. It also protects against malformed gradients
368 // that have repeated hard stops at the very beginning that are effectively unreachable.
369 if (SkScalarNearlyZero(dt)) {
370 continue;
371 }
372
373 Vec4 c0 = Vec4::Load(inColors[i].vec());
374 Vec4 c1 = Vec4::Load(inColors[i + 1].vec());
375 Vec4 scale = (c1 - c0) / dt;
376 Vec4 bias = c0 - t0 * scale;
377
378 scale.store(outScales + intervalCount);
379 bias.store(outBiases + intervalCount);
380 outThresholds[intervalCount] = t1;
381 intervalCount++;
382 }
383 return intervalCount;
384 }
385
make_unrolled_binary_colorizer(const SkPMColor4f * colors,const SkScalar * positions,int count)386 static std::unique_ptr<GrFragmentProcessor> make_unrolled_binary_colorizer(
387 const SkPMColor4f* colors, const SkScalar* positions, int count) {
388 if (count > kMaxUnrolledColorCount) {
389 // Definitely cannot represent this gradient configuration
390 return nullptr;
391 }
392
393 SkPMColor4f scales[kMaxUnrolledIntervalCount];
394 SkPMColor4f biases[kMaxUnrolledIntervalCount];
395 SkScalar thresholds[kMaxUnrolledIntervalCount] = {};
396 int intervalCount = build_intervals(count, colors, positions,
397 kMaxUnrolledIntervalCount, scales, biases, thresholds);
398 if (intervalCount <= 0) {
399 return nullptr;
400 }
401
402 SkRect thresholds1_7 = {thresholds[0], thresholds[1], thresholds[2], thresholds[3]},
403 thresholds9_13 = {thresholds[4], thresholds[5], thresholds[6], 0.0};
404
405 return make_unrolled_colorizer(intervalCount, scales, biases, thresholds1_7, thresholds9_13);
406 }
407
make_looping_binary_colorizer(const SkPMColor4f * colors,const SkScalar * positions,int count)408 static std::unique_ptr<GrFragmentProcessor> make_looping_binary_colorizer(const SkPMColor4f* colors,
409 const SkScalar* positions,
410 int count) {
411 if (count > kMaxLoopingColorCount) {
412 // Definitely cannot represent this gradient configuration
413 return nullptr;
414 }
415
416 SkPMColor4f scales[kMaxLoopingIntervalCount];
417 SkPMColor4f biases[kMaxLoopingIntervalCount];
418 SkScalar thresholds[kMaxLoopingIntervalCount] = {};
419 int intervalCount = build_intervals(count, colors, positions,
420 kMaxLoopingIntervalCount, scales, biases, thresholds);
421 if (intervalCount <= 0) {
422 return nullptr;
423 }
424
425 // We round up the number of intervals to the next power of two. This reduces the number of
426 // unique shaders and doesn't require any additional GPU processing power, but this does waste a
427 // handful of uniforms.
428 int roundedSize = std::max(4, SkNextPow2(intervalCount));
429 SkASSERT(roundedSize <= kMaxLoopingIntervalCount);
430 for (; intervalCount < roundedSize; ++intervalCount) {
431 thresholds[intervalCount] = thresholds[intervalCount - 1];
432 scales[intervalCount] = scales[intervalCount - 1];
433 biases[intervalCount] = biases[intervalCount - 1];
434 }
435
436 return make_looping_colorizer(intervalCount, scales, biases, thresholds);
437 }
438
439 // Analyze the shader's color stops and positions and chooses an appropriate colorizer to represent
440 // the gradient.
make_colorizer(const SkPMColor4f * colors,const SkScalar * positions,int count,bool premul,const GrFPArgs & args)441 static std::unique_ptr<GrFragmentProcessor> make_colorizer(const SkPMColor4f* colors,
442 const SkScalar* positions,
443 int count,
444 bool premul,
445 const GrFPArgs& args) {
446 // If there are hard stops at the beginning or end, the first and/or last color should be
447 // ignored by the colorizer since it should only be used in a clamped border color. By detecting
448 // and removing these stops at the beginning, it makes optimizing the remaining color stops
449 // simpler.
450
451 // SkGradientShaderBase guarantees that pos[0] == 0 by adding a default value.
452 bool bottomHardStop = SkScalarNearlyEqual(positions[0], positions[1]);
453 // The same is true for pos[end] == 1
454 bool topHardStop = SkScalarNearlyEqual(positions[count - 2], positions[count - 1]);
455
456 if (bottomHardStop) {
457 colors++;
458 positions++;
459 count--;
460 }
461 if (topHardStop) {
462 count--;
463 }
464
465 // Two remaining colors means a single interval from 0 to 1
466 // (but it may have originally been a 3 or 4 color gradient with 1-2 hard stops at the ends)
467 if (count == 2) {
468 return make_single_interval_colorizer(colors[0], colors[1]);
469 }
470
471 const GrShaderCaps* caps = args.fContext->priv().caps()->shaderCaps();
472 auto intervalsExceedPrecisionLimit = [&]() -> bool {
473 // The remaining analytic colorizers use scale*t+bias, and the scale/bias values can become
474 // quite large when thresholds are close (but still outside the hardstop limit). If float
475 // isn't 32-bit, output can be incorrect if the thresholds are too close together. However,
476 // the analytic shaders are higher quality, so they can be used with lower precision
477 // hardware when the thresholds are not ill-conditioned.
478 if (!caps->fFloatIs32Bits) {
479 // Could run into problems. Check if thresholds are close together (with a limit of .01,
480 // so that scales will be less than 100, which leaves 4 decimals of precision on
481 // 16-bit).
482 for (int i = 0; i < count - 1; i++) {
483 SkScalar dt = SkScalarAbs(positions[i] - positions[i + 1]);
484 if (dt <= kLowPrecisionIntervalLimit && dt > SK_ScalarNearlyZero) {
485 return true;
486 }
487 }
488 }
489 return false;
490 };
491
492 auto makeDualIntervalColorizer = [&]() -> std::unique_ptr<GrFragmentProcessor> {
493 // The dual-interval colorizer uses the same principles as the binary-search colorizer, but
494 // is limited to exactly 2 intervals.
495 if (count == 3) {
496 // Must be a dual interval gradient, where the middle point is at 1 and the
497 // two intervals share the middle color stop.
498 return make_dual_interval_colorizer(colors[0], colors[1],
499 colors[1], colors[2],
500 positions[1]);
501 }
502 if (count == 4 && SkScalarNearlyEqual(positions[1], positions[2])) {
503 // Two separate intervals that join at the same threshold position
504 return make_dual_interval_colorizer(colors[0], colors[1],
505 colors[2], colors[3],
506 positions[1]);
507 }
508 // The gradient can't be represented in only two intervals.
509 return nullptr;
510 };
511
512 int binaryColorizerLimit = caps->fNonconstantArrayIndexSupport ? kMaxLoopingColorCount
513 : kMaxUnrolledColorCount;
514 if ((count <= binaryColorizerLimit) && !intervalsExceedPrecisionLimit()) {
515 // The dual-interval colorizer uses the same principles as the binary-search colorizer, but
516 // is limited to exactly 2 intervals.
517 std::unique_ptr<GrFragmentProcessor> colorizer = makeDualIntervalColorizer();
518 if (colorizer) {
519 return colorizer;
520 }
521 // Attempt to create an analytic colorizer that uses a binary-search loop.
522 colorizer = caps->fNonconstantArrayIndexSupport
523 ? make_looping_binary_colorizer(colors, positions, count)
524 : make_unrolled_binary_colorizer(colors, positions, count);
525 if (colorizer) {
526 return colorizer;
527 }
528 }
529
530 // Otherwise fall back to a rasterized gradient sampled by a texture, which can handle
531 // arbitrary gradients. (This has limited sampling resolution, and always blurs hard-stops.)
532 return make_textured_colorizer(colors, positions, count, premul, args);
533 }
534
535 // This top-level effect implements clamping on the layout coordinate and requires specifying the
536 // border colors that are used when outside the clamped boundary. Gradients with the
537 // SkTileMode::kClamp should use the colors at their first and last stop (after adding default stops
538 // for t=0,t=1) as the border color. This will automatically replicate the edge color, even when
539 // there is a hard stop.
540 //
541 // The SkTileMode::kDecal can be produced by specifying transparent black as the border colors,
542 // regardless of the gradient's stop colors.
make_clamped_gradient(std::unique_ptr<GrFragmentProcessor> colorizer,std::unique_ptr<GrFragmentProcessor> gradLayout,SkPMColor4f leftBorderColor,SkPMColor4f rightBorderColor,bool colorsAreOpaque)543 static std::unique_ptr<GrFragmentProcessor> make_clamped_gradient(
544 std::unique_ptr<GrFragmentProcessor> colorizer,
545 std::unique_ptr<GrFragmentProcessor> gradLayout,
546 SkPMColor4f leftBorderColor,
547 SkPMColor4f rightBorderColor,
548 bool colorsAreOpaque) {
549 static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
550 "uniform shader colorizer;"
551 "uniform shader gradLayout;"
552
553 "uniform half4 leftBorderColor;" // t < 0.0
554 "uniform half4 rightBorderColor;" // t > 1.0
555
556 "uniform int layoutPreservesOpacity;" // specialized
557
558 "half4 main(float2 coord) {"
559 "half4 t = gradLayout.eval(coord);"
560 "half4 outColor;"
561
562 // If t.x is below 0, use the left border color without invoking the child processor.
563 // If any t.x is above 1, use the right border color. Otherwise, t is in the [0, 1]
564 // range assumed by the colorizer FP, so delegate to the child processor.
565 "if (!bool(layoutPreservesOpacity) && t.y < 0) {"
566 // layout has rejected this fragment (rely on sksl to remove this branch if the
567 // layout FP preserves opacity is false)
568 "outColor = half4(0);"
569 "} else if (t.x < 0) {"
570 "outColor = leftBorderColor;"
571 "} else if (t.x > 1.0) {"
572 "outColor = rightBorderColor;"
573 "} else {"
574 // Always sample from (x, 0), discarding y, since the layout FP can use y as a
575 // side-channel.
576 "outColor = colorizer.eval(t.x0);"
577 "}"
578 "return outColor;"
579 "}"
580 );
581
582 // If the layout does not preserve opacity, remove the opaque optimization,
583 // but otherwise respect the provided color opacity state (which should take
584 // into account the opacity of the border colors).
585 bool layoutPreservesOpacity = gradLayout->preservesOpaqueInput();
586 GrSkSLFP::OptFlags optFlags = GrSkSLFP::OptFlags::kCompatibleWithCoverageAsAlpha;
587 if (colorsAreOpaque && layoutPreservesOpacity) {
588 optFlags |= GrSkSLFP::OptFlags::kPreservesOpaqueInput;
589 }
590
591 return GrSkSLFP::Make(effect, "ClampedGradient", /*inputFP=*/nullptr, optFlags,
592 "colorizer", GrSkSLFP::IgnoreOptFlags(std::move(colorizer)),
593 "gradLayout", GrSkSLFP::IgnoreOptFlags(std::move(gradLayout)),
594 "leftBorderColor", leftBorderColor,
595 "rightBorderColor", rightBorderColor,
596 "layoutPreservesOpacity",
597 GrSkSLFP::Specialize<int>(layoutPreservesOpacity));
598 }
599
make_tiled_gradient(const GrFPArgs & args,std::unique_ptr<GrFragmentProcessor> colorizer,std::unique_ptr<GrFragmentProcessor> gradLayout,bool mirror,bool colorsAreOpaque)600 static std::unique_ptr<GrFragmentProcessor> make_tiled_gradient(
601 const GrFPArgs& args,
602 std::unique_ptr<GrFragmentProcessor> colorizer,
603 std::unique_ptr<GrFragmentProcessor> gradLayout,
604 bool mirror,
605 bool colorsAreOpaque) {
606 static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
607 "uniform shader colorizer;"
608 "uniform shader gradLayout;"
609
610 "uniform int mirror;" // specialized
611 "uniform int layoutPreservesOpacity;" // specialized
612 "uniform int useFloorAbsWorkaround;" // specialized
613
614 "half4 main(float2 coord) {"
615 "half4 t = gradLayout.eval(coord);"
616
617 "if (!bool(layoutPreservesOpacity) && t.y < 0) {"
618 // layout has rejected this fragment (rely on sksl to remove this branch if the
619 // layout FP preserves opacity is false)
620 "return half4(0);"
621 "} else {"
622 "if (bool(mirror)) {"
623 "half t_1 = t.x - 1;"
624 "half tiled_t = t_1 - 2 * floor(t_1 * 0.5) - 1;"
625 "if (bool(useFloorAbsWorkaround)) {"
626 // At this point the expected value of tiled_t should between -1 and 1, so
627 // this clamp has no effect other than to break up the floor and abs calls
628 // and make sure the compiler doesn't merge them back together.
629 "tiled_t = clamp(tiled_t, -1, 1);"
630 "}"
631 "t.x = abs(tiled_t);"
632 "} else {"
633 // Simple repeat mode
634 "t.x = fract(t.x);"
635 "}"
636
637 // Always sample from (x, 0), discarding y, since the layout FP can use y as a
638 // side-channel.
639 "half4 outColor = colorizer.eval(t.x0);"
640 "return outColor;"
641 "}"
642 "}"
643 );
644
645 // If the layout does not preserve opacity, remove the opaque optimization,
646 // but otherwise respect the provided color opacity state (which should take
647 // into account the opacity of the border colors).
648 bool layoutPreservesOpacity = gradLayout->preservesOpaqueInput();
649 GrSkSLFP::OptFlags optFlags = GrSkSLFP::OptFlags::kCompatibleWithCoverageAsAlpha;
650 if (colorsAreOpaque && layoutPreservesOpacity) {
651 optFlags |= GrSkSLFP::OptFlags::kPreservesOpaqueInput;
652 }
653 const bool useFloorAbsWorkaround =
654 args.fContext->priv().caps()->shaderCaps()->fMustDoOpBetweenFloorAndAbs;
655
656 return GrSkSLFP::Make(effect, "TiledGradient", /*inputFP=*/nullptr, optFlags,
657 "colorizer", GrSkSLFP::IgnoreOptFlags(std::move(colorizer)),
658 "gradLayout", GrSkSLFP::IgnoreOptFlags(std::move(gradLayout)),
659 "mirror", GrSkSLFP::Specialize<int>(mirror),
660 "layoutPreservesOpacity",
661 GrSkSLFP::Specialize<int>(layoutPreservesOpacity),
662 "useFloorAbsWorkaround",
663 GrSkSLFP::Specialize<int>(useFloorAbsWorkaround));
664 }
665
make_interpolated_to_dst(std::unique_ptr<GrFragmentProcessor> gradient,const SkGradientShader::Interpolation & interpolation,SkColorSpace * intermediateColorSpace,const GrColorInfo & dstInfo,bool allOpaque)666 static std::unique_ptr<GrFragmentProcessor> make_interpolated_to_dst(
667 std::unique_ptr<GrFragmentProcessor> gradient,
668 const SkGradientShader::Interpolation& interpolation,
669 SkColorSpace* intermediateColorSpace,
670 const GrColorInfo& dstInfo,
671 bool allOpaque) {
672 using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
673
674 // If these values change, you will need to edit sksl_shared
675 static_assert(static_cast<int>(ColorSpace::kDestination) == 0);
676 static_assert(static_cast<int>(ColorSpace::kSRGBLinear) == 1);
677 static_assert(static_cast<int>(ColorSpace::kLab) == 2);
678 static_assert(static_cast<int>(ColorSpace::kOKLab) == 3);
679 static_assert(static_cast<int>(ColorSpace::kLCH) == 4);
680 static_assert(static_cast<int>(ColorSpace::kOKLCH) == 5);
681 static_assert(static_cast<int>(ColorSpace::kSRGB) == 6);
682 static_assert(static_cast<int>(ColorSpace::kHSL) == 7);
683 static_assert(static_cast<int>(ColorSpace::kHWB) == 8);
684
685 static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForColorFilter,
686 "uniform int colorSpace;" // specialized
687 "uniform int do_unpremul;" // specialized
688
689 "half4 main(half4 color) {"
690 "return $interpolated_to_rgb_unpremul(color, colorSpace, do_unpremul);"
691 "}"
692 );
693
694 // Are we interpreting premul colors? We use this later to decide if we need to inject a final
695 // premultiplication step.
696 bool inputPremul = static_cast<bool>(interpolation.fInPremul);
697
698 switch (interpolation.fColorSpace) {
699 case ColorSpace::kLab:
700 case ColorSpace::kOKLab:
701 case ColorSpace::kLCH:
702 case ColorSpace::kOKLCH:
703 case ColorSpace::kHSL:
704 case ColorSpace::kHWB:
705 // In these exotic spaces, unpremul the colors if necessary (no need to do this if
706 // they're all opaque), and then convert them to the intermediate SkColorSpace
707 gradient = GrSkSLFP::Make(effect, "GradientCS", std::move(gradient),
708 GrSkSLFP::OptFlags::kAll,
709 "colorSpace", GrSkSLFP::Specialize<int>(
710 static_cast<int>(interpolation.fColorSpace)),
711 "do_unpremul", GrSkSLFP::Specialize<int>(
712 inputPremul && !allOpaque));
713 // We just forced the colors back to unpremul. Remember that for below
714 inputPremul = false;
715 break;
716 default:
717 break;
718 }
719
720 // Now transform from intermediate to destination color space. There are two tricky things here:
721 // 1) Normally, we'd pass dstInfo to the transform effect. However, if someone is rendering to
722 // a non-color managed surface (nullptr dst color space), and they chose to interpolate in
723 // any of the exotic spaces, that transform would do nothing, and leave the colors in
724 // whatever intermediate space we chose. That could even be something like XYZ, which will
725 // produce nonsense. So, in this particular case, we break Skia's rules, and treat a null
726 // destination as sRGB.
727 SkColorSpace* dstColorSpace = dstInfo.colorSpace() ? dstInfo.colorSpace() : sk_srgb_singleton();
728
729 // 2) Alpha type: We already tweaked our idea of "inputPremul" above -- if we interpolated in a
730 // non-RGB space, then we had to unpremul the colors to get proper conversion back to RGB.
731 // Our final goal is to emit premul colors, but under certain conditions we don't need to do
732 // anything to achieve that: i.e. its interpolating already premul colors (inputPremul) or
733 // all the colors have a = 1, in which case premul is a no op. Note that this allOpaque check
734 // is more permissive than SkGradientShaderBase's isOpaque(), since we can optimize away the
735 // make-premul op for two point conical gradients (which report false for isOpaque).
736 SkAlphaType intermediateAlphaType = inputPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
737 SkAlphaType dstAlphaType = kPremul_SkAlphaType;
738
739 // If all the colors were opaque, then we don't need to do any premultiplication. We describe
740 // all the colors as *unpremul*, though. That will eliminate any extra unpremul/premul pair
741 // that would be injected if we have to do a color-space conversion here.
742 if (allOpaque) {
743 intermediateAlphaType = dstAlphaType = kUnpremul_SkAlphaType;
744 }
745
746 return GrColorSpaceXformEffect::Make(std::move(gradient),
747 intermediateColorSpace, intermediateAlphaType,
748 dstColorSpace, dstAlphaType);
749 }
750
751 namespace GrGradientShader {
752
753 // Combines the colorizer and layout with an appropriately configured top-level effect based on the
754 // gradient's tile mode
MakeGradientFP(const SkGradientShaderBase & shader,const GrFPArgs & args,const SkShaderBase::MatrixRec & mRec,std::unique_ptr<GrFragmentProcessor> layout,const SkMatrix * overrideMatrix)755 std::unique_ptr<GrFragmentProcessor> MakeGradientFP(const SkGradientShaderBase& shader,
756 const GrFPArgs& args,
757 const SkShaderBase::MatrixRec& mRec,
758 std::unique_ptr<GrFragmentProcessor> layout,
759 const SkMatrix* overrideMatrix) {
760 // No shader is possible if a layout couldn't be created, e.g. a layout-specific Make() returned
761 // null.
762 if (layout == nullptr) {
763 return nullptr;
764 }
765
766 // Some two-point conical gradients use a custom matrix here. Otherwise, use
767 // SkGradientShaderBase's matrix;
768 if (!overrideMatrix) {
769 overrideMatrix = &shader.getGradientMatrix();
770 }
771 bool success;
772 std::tie(success, layout) = mRec.apply(std::move(layout), *overrideMatrix);
773 if (!success) {
774 return nullptr;
775 }
776
777 // Convert all colors into destination space and into SkPMColor4fs, and handle
778 // premul issues depending on the interpolation mode
779 bool inputPremul = shader.interpolateInPremul();
780 bool allOpaque = true;
781 SkColor4fXformer xformedColors(&shader, args.fDstColorInfo->colorSpace());
782 const SkPMColor4f* colors = xformedColors.fColors.begin();
783
784 for (int i = 0; i < shader.fColorCount; i++) {
785 if (allOpaque && !SkScalarNearlyEqual(colors[i].fA, 1.0)) {
786 allOpaque = false;
787 }
788 }
789
790 // SkGradientShader stores positions implicitly when they are evenly spaced, but the getPos()
791 // implementation performs a branch for every position index. Since the shader conversion
792 // requires lots of position tests, calculate all of the positions up front if needed.
793 SkTArray<SkScalar, true> implicitPos;
794 SkScalar* positions;
795 if (shader.fPositions) {
796 positions = shader.fPositions;
797 } else {
798 implicitPos.reserve_back(shader.fColorCount);
799 SkScalar posScale = SK_Scalar1 / (shader.fColorCount - 1);
800 for (int i = 0 ; i < shader.fColorCount; i++) {
801 implicitPos.push_back(SkIntToScalar(i) * posScale);
802 }
803 positions = implicitPos.begin();
804 }
805
806 // All gradients are colorized the same way, regardless of layout
807 std::unique_ptr<GrFragmentProcessor> colorizer = make_colorizer(
808 colors, positions, shader.fColorCount, inputPremul, args);
809 if (colorizer == nullptr) {
810 return nullptr;
811 }
812
813 // All tile modes are supported (unless something was added to SkShader)
814 std::unique_ptr<GrFragmentProcessor> gradient;
815 switch(shader.getTileMode()) {
816 case SkTileMode::kRepeat:
817 gradient = make_tiled_gradient(args, std::move(colorizer), std::move(layout),
818 /* mirror */ false, allOpaque);
819 break;
820 case SkTileMode::kMirror:
821 gradient = make_tiled_gradient(args, std::move(colorizer), std::move(layout),
822 /* mirror */ true, allOpaque);
823 break;
824 case SkTileMode::kClamp:
825 // For the clamped mode, the border colors are the first and last colors, corresponding
826 // to t=0 and t=1, because SkGradientShaderBase enforces that by adding color stops as
827 // appropriate. If there is a hard stop, this grabs the expected outer colors for the
828 // border.
829 gradient = make_clamped_gradient(std::move(colorizer), std::move(layout),
830 colors[0], colors[shader.fColorCount - 1],
831 allOpaque);
832 break;
833 case SkTileMode::kDecal:
834 // Even if the gradient colors are opaque, the decal borders are transparent so
835 // disable that optimization
836 gradient = make_clamped_gradient(std::move(colorizer), std::move(layout),
837 SK_PMColor4fTRANSPARENT, SK_PMColor4fTRANSPARENT,
838 /* colorsAreOpaque */ false);
839 break;
840 }
841
842 // If interpolation space is different than destination, wrap the colorizer in a conversion.
843 // This also handles any final premultiplication, etc.
844 return make_interpolated_to_dst(std::move(gradient),
845 shader.fInterpolation,
846 xformedColors.fIntermediateColorSpace.get(),
847 *args.fDstColorInfo,
848 allOpaque);
849 }
850
MakeLinear(const SkLinearGradient & shader,const GrFPArgs & args,const SkShaderBase::MatrixRec & mRec)851 std::unique_ptr<GrFragmentProcessor> MakeLinear(const SkLinearGradient& shader,
852 const GrFPArgs& args,
853 const SkShaderBase::MatrixRec& mRec) {
854 // We add a tiny delta to t. When gradient stops are set up so that a hard stop in a vertically
855 // or horizontally oriented gradient falls exactly at a column or row of pixel centers we can
856 // get slightly different interpolated t values along the column/row. By adding the delta
857 // we will consistently get the color to the "right" of the stop. Of course if the hard stop
858 // falls at X.5 - delta then we still could get inconsistent results, but that is much less
859 // likely. crbug.com/938592
860 // If/when we add filtering of the gradient this can be removed.
861 static const SkRuntimeEffect* effect = SkMakeRuntimeEffect(SkRuntimeEffect::MakeForShader,
862 "half4 main(float2 coord) {"
863 "return half4(half(coord.x) + 0.00001, 1, 0, 0);" // y = 1 for always valid
864 "}"
865 );
866 // The linear gradient never rejects a pixel so it doesn't change opacity
867 auto fp = GrSkSLFP::Make(effect, "LinearLayout", /*inputFP=*/nullptr,
868 GrSkSLFP::OptFlags::kPreservesOpaqueInput);
869 return MakeGradientFP(shader, args, mRec, std::move(fp));
870 }
871
872 #if GR_TEST_UTILS
RandomParams(SkRandom * random)873 RandomParams::RandomParams(SkRandom* random) {
874 // Set color count to min of 2 so that we don't trigger the const color optimization and make
875 // a non-gradient processor.
876 fColorCount = random->nextRangeU(2, kMaxRandomGradientColors);
877 fUseColors4f = random->nextBool();
878
879 // if one color, omit stops, otherwise randomly decide whether or not to
880 if (fColorCount == 1 || (fColorCount >= 2 && random->nextBool())) {
881 fStops = nullptr;
882 } else {
883 fStops = fStopStorage;
884 }
885
886 // if using SkColor4f, attach a random (possibly null) color space (with linear gamma)
887 if (fUseColors4f) {
888 fColorSpace = GrTest::TestColorSpace(random);
889 }
890
891 SkScalar stop = 0.f;
892 for (int i = 0; i < fColorCount; ++i) {
893 if (fUseColors4f) {
894 fColors4f[i].fR = random->nextUScalar1();
895 fColors4f[i].fG = random->nextUScalar1();
896 fColors4f[i].fB = random->nextUScalar1();
897 fColors4f[i].fA = random->nextUScalar1();
898 } else {
899 fColors[i] = random->nextU();
900 }
901 if (fStops) {
902 fStops[i] = stop;
903 stop = i < fColorCount - 1 ? stop + random->nextUScalar1() * (1.f - stop) : 1.f;
904 }
905 }
906 fTileMode = static_cast<SkTileMode>(random->nextULessThan(kSkTileModeCount));
907 }
908 #endif
909
910 } // namespace GrGradientShader
911