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