Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Namespaces | Typedefs | Functions | Variables
GrGradientShader.cpp File Reference
#include "src/gpu/ganesh/gradients/GrGradientShader.h"
#include "include/core/SkColorSpace.h"
#include "include/gpu/GrRecordingContext.h"
#include "include/private/SkColorData.h"
#include "src/base/SkMathPriv.h"
#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkRuntimeEffectPriv.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrColor.h"
#include "src/gpu/ganesh/GrColorInfo.h"
#include "src/gpu/ganesh/GrColorSpaceXform.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
#include "src/gpu/ganesh/SkGr.h"
#include "src/gpu/ganesh/effects/GrMatrixEffect.h"
#include "src/gpu/ganesh/effects/GrSkSLFP.h"
#include "src/gpu/ganesh/effects/GrTextureEffect.h"
#include "src/gpu/ganesh/gradients/GrGradientBitmapCache.h"
#include "src/shaders/gradients/SkGradientBaseShader.h"

Go to the source code of this file.

Namespaces

namespace  GrGradientShader
 

Typedefs

using Vec4 = skvx::Vec< 4, float >
 

Functions

static std::unique_ptr< GrFragmentProcessormake_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 std::unique_ptr< GrFragmentProcessormake_single_interval_colorizer (const SkPMColor4f &start, const SkPMColor4f &end)
 
static std::unique_ptr< GrFragmentProcessormake_dual_interval_colorizer (const SkPMColor4f &c0, const SkPMColor4f &c1, const SkPMColor4f &c2, const SkPMColor4f &c3, float threshold)
 
static std::unique_ptr< GrFragmentProcessormake_unrolled_colorizer (int intervalCount, const SkPMColor4f *scale, const SkPMColor4f *bias, SkRect thresholds1_7, SkRect thresholds9_13)
 
static std::unique_ptr< GrFragmentProcessormake_looping_colorizer (int intervalCount, const SkPMColor4f *scale, const SkPMColor4f *bias, const SkScalar *thresholds)
 
int build_intervals (int inputLength, const SkPMColor4f *inColors, const SkScalar *inPositions, int outputLength, SkPMColor4f *outScales, SkPMColor4f *outBiases, SkScalar *outThresholds)
 
static std::unique_ptr< GrFragmentProcessormake_unrolled_binary_colorizer (const SkPMColor4f *colors, const SkScalar *positions, int count)
 
static std::unique_ptr< GrFragmentProcessormake_looping_binary_colorizer (const SkPMColor4f *colors, const SkScalar *positions, int count)
 
static std::unique_ptr< GrFragmentProcessormake_uniform_colorizer (const SkPMColor4f *colors, const SkScalar *positions, int count, bool premul, const GrFPArgs &args)
 
static std::unique_ptr< GrFragmentProcessormake_clamped_gradient (std::unique_ptr< GrFragmentProcessor > colorizer, std::unique_ptr< GrFragmentProcessor > gradLayout, SkPMColor4f leftBorderColor, SkPMColor4f rightBorderColor, bool colorsAreOpaque)
 
static std::unique_ptr< GrFragmentProcessormake_tiled_gradient (const GrFPArgs &args, std::unique_ptr< GrFragmentProcessor > colorizer, std::unique_ptr< GrFragmentProcessor > gradLayout, bool mirror, bool colorsAreOpaque)
 
static std::unique_ptr< GrFragmentProcessormake_interpolated_to_dst (std::unique_ptr< GrFragmentProcessor > gradient, const SkGradientShader::Interpolation &interpolation, SkColorSpace *intermediateColorSpace, const GrColorInfo &dstInfo, bool allOpaque)
 
static GrFPResult GrGradientShader::apply_matrix (std::unique_ptr< GrFragmentProcessor > fp, const SkShaders::MatrixRec &rec, const SkMatrix &postInv)
 
std::unique_ptr< GrFragmentProcessorGrGradientShader::MakeGradientFP (const SkGradientBaseShader &shader, const GrFPArgs &args, const SkShaders::MatrixRec &mRec, std::unique_ptr< GrFragmentProcessor > layout, const SkMatrix *overrideMatrix)
 
std::unique_ptr< GrFragmentProcessorGrGradientShader::MakeLinear (const SkLinearGradient &shader, const GrFPArgs &args, const SkShaders::MatrixRec &mRec)
 

Variables

static const SkScalar kLowPrecisionIntervalLimit = 0.01f
 
static const int kMaxNumCachedGradientBitmaps = 32
 
static const int kGradientTextureSize = 256
 
static constexpr int kMaxUnrolledColorCount = 16
 
static constexpr int kMaxUnrolledIntervalCount = kMaxUnrolledColorCount / 2
 
static constexpr int kMaxLoopingColorCount = 128
 
static constexpr int kMaxLoopingIntervalCount = kMaxLoopingColorCount / 2
 

Typedef Documentation

◆ Vec4

using Vec4 = skvx::Vec<4, float>

Definition at line 32 of file GrGradientShader.cpp.

Function Documentation

◆ build_intervals()

int build_intervals ( int  inputLength,
const SkPMColor4f inColors,
const SkScalar inPositions,
int  outputLength,
SkPMColor4f outScales,
SkPMColor4f outBiases,
SkScalar outThresholds 
)

Definition at line 360 of file GrGradientShader.cpp.

366 {
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}
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:101
float SkScalar
Definition extension.cpp:12
const Scalar scale
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

◆ make_clamped_gradient()

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

Definition at line 560 of file GrGradientShader.cpp.

565 {
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}
SkRuntimeEffect * SkMakeRuntimeEffect(SkRuntimeEffect::Result(*make)(SkString, const SkRuntimeEffect::Options &), const char *sksl, SkRuntimeEffect::Options options=SkRuntimeEffect::Options{})
static GrIgnoreOptFlags IgnoreOptFlags(std::unique_ptr< GrFragmentProcessor > child)
Definition GrSkSLFP.h:96
static std::unique_ptr< GrSkSLFP > Make(const SkRuntimeEffect *effect, const char *name, std::unique_ptr< GrFragmentProcessor > inputFP, OptFlags optFlags, Args &&... args)
Definition GrSkSLFP.h:158
static Result MakeForShader(SkString sksl, const Options &)

◆ make_dual_interval_colorizer()

static std::unique_ptr< GrFragmentProcessor > make_dual_interval_colorizer ( const SkPMColor4f c0,
const SkPMColor4f c1,
const SkPMColor4f c2,
const SkPMColor4f c3,
float  threshold 
)
static

Definition at line 110 of file GrGradientShader.cpp.

114 {
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}
const float * vec() const
Definition SkColor.h:308

◆ make_interpolated_to_dst()

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

Definition at line 683 of file GrGradientShader.cpp.

688 {
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}
kUnpremul_SkAlphaType
SkAlphaType
Definition SkAlphaType.h:26
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
SkColorSpace * sk_srgb_singleton()
SkColorSpace * colorSpace() const
static std::unique_ptr< GrFragmentProcessor > Make(std::unique_ptr< GrFragmentProcessor > child, SkColorSpace *src, SkAlphaType srcAT, SkColorSpace *dst, SkAlphaType dstAT)
static Result MakeForColorFilter(SkString sksl, const Options &)
ColorSpace
Definition image.h:16

◆ make_looping_binary_colorizer()

static std::unique_ptr< GrFragmentProcessor > make_looping_binary_colorizer ( const SkPMColor4f colors,
const SkScalar positions,
int  count 
)
static

Definition at line 425 of file GrGradientShader.cpp.

427 {
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}
int count
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_looping_colorizer(int intervalCount, const SkPMColor4f *scale, const SkPMColor4f *bias, const SkScalar *thresholds)
#define SkASSERT(cond)
Definition SkAssert.h:116
static int SkNextPow2(int value)
Definition SkMathPriv.h:272

◆ make_looping_colorizer()

static std::unique_ptr< GrFragmentProcessor > make_looping_colorizer ( int  intervalCount,
const SkPMColor4f scale,
const SkPMColor4f bias,
const SkScalar thresholds 
)
static

Definition at line 271 of file GrGradientShader.cpp.

274 {
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}
#define SkASSERTF(cond, fmt,...)
Definition SkAssert.h:117
static int SkNextLog2(uint32_t value)
Definition SkMathPriv.h:238
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:550
GAsyncResult * result
Definition SkM44.h:98

◆ make_single_interval_colorizer()

static std::unique_ptr< GrFragmentProcessor > make_single_interval_colorizer ( const SkPMColor4f start,
const SkPMColor4f end 
)
static

Definition at line 93 of file GrGradientShader.cpp.

94 {
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}
glong glong end

◆ make_textured_colorizer()

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

Definition at line 44 of file GrGradientShader.cpp.

52 {
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(
60 GrColorType::kRGBA_F16, GrRenderable::kNo);
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
82 auto view = std::get<0>(GrMakeCachedBitmapProxyView(
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}
static const int kMaxNumCachedGradientBitmaps
static const int kGradientTextureSize
static constexpr bool GrColorTypeIsWiderThan(GrColorType colorType, int n)
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)
constexpr bool SkIsPow2(T value)
Definition SkMath.h:51
static std::unique_ptr< GrFragmentProcessor > Make(GrSurfaceProxyView, SkAlphaType, const SkMatrix &=SkMatrix::I(), GrSamplerState::Filter=GrSamplerState::Filter::kNearest, GrSamplerState::MipmapMode mipmapMode=GrSamplerState::MipmapMode::kNone)
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition SkMatrix.h:75
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args

◆ make_tiled_gradient()

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

Definition at line 617 of file GrGradientShader.cpp.

622 {
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}
static unsigned mirror(SkFixed fx, int max)

◆ make_uniform_colorizer()

static std::unique_ptr< GrFragmentProcessor > make_uniform_colorizer ( const SkPMColor4f colors,
const SkScalar positions,
int  count,
bool  premul,
const GrFPArgs args 
)
static

Definition at line 458 of file GrGradientShader.cpp.

462 {
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) {
485 return make_single_interval_colorizer(colors[0], colors[1]);
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.
515 return make_dual_interval_colorizer(colors[0], colors[1],
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
521 return make_dual_interval_colorizer(colors[0], colors[1],
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
540 ? make_looping_binary_colorizer(colors, positions, count)
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}
static std::unique_ptr< GrFragmentProcessor > make_looping_binary_colorizer(const SkPMColor4f *colors, const SkScalar *positions, int count)
static std::unique_ptr< GrFragmentProcessor > make_dual_interval_colorizer(const SkPMColor4f &c0, const SkPMColor4f &c1, const SkPMColor4f &c2, const SkPMColor4f &c3, float threshold)
static constexpr int kMaxUnrolledColorCount
static std::unique_ptr< GrFragmentProcessor > make_unrolled_binary_colorizer(const SkPMColor4f *colors, const SkScalar *positions, int count)
static std::unique_ptr< GrFragmentProcessor > make_single_interval_colorizer(const SkPMColor4f &start, const SkPMColor4f &end)
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
PODArray< SkColor > colors
Definition SkRecords.h:276
bool fNonconstantArrayIndexSupport

◆ make_unrolled_binary_colorizer()

static std::unique_ptr< GrFragmentProcessor > make_unrolled_binary_colorizer ( const SkPMColor4f colors,
const SkScalar positions,
int  count 
)
static

Definition at line 403 of file GrGradientShader.cpp.

404 {
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}
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 kMaxUnrolledIntervalCount

◆ make_unrolled_colorizer()

static std::unique_ptr< GrFragmentProcessor > make_unrolled_colorizer ( int  intervalCount,
const SkPMColor4f scale,
const SkPMColor4f bias,
SkRect  thresholds1_7,
SkRect  thresholds9_13 
)
static

Definition at line 159 of file GrGradientShader.cpp.

163 {
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}
void append(const char text[])
Definition SkString.h:203

Variable Documentation

◆ kGradientTextureSize

const int kGradientTextureSize = 256
static

Definition at line 40 of file GrGradientShader.cpp.

◆ kLowPrecisionIntervalLimit

const SkScalar kLowPrecisionIntervalLimit = 0.01f
static

Definition at line 36 of file GrGradientShader.cpp.

◆ kMaxLoopingColorCount

constexpr int kMaxLoopingColorCount = 128
staticconstexpr

Definition at line 268 of file GrGradientShader.cpp.

◆ kMaxLoopingIntervalCount

constexpr int kMaxLoopingIntervalCount = kMaxLoopingColorCount / 2
staticconstexpr

Definition at line 269 of file GrGradientShader.cpp.

◆ kMaxNumCachedGradientBitmaps

const int kMaxNumCachedGradientBitmaps = 32
static

Definition at line 39 of file GrGradientShader.cpp.

◆ kMaxUnrolledColorCount

constexpr int kMaxUnrolledColorCount = 16
staticconstexpr

Definition at line 156 of file GrGradientShader.cpp.

◆ kMaxUnrolledIntervalCount

constexpr int kMaxUnrolledIntervalCount = kMaxUnrolledColorCount / 2
staticconstexpr

Definition at line 157 of file GrGradientShader.cpp.