1 /*
2 * Copyright 2022 Google LLC
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/shaders/gradients/SkGradientShaderBase.h"
9
10 #include "include/core/SkColorSpace.h"
11 #include "src/base/SkVx.h"
12 #include "src/core/SkColorSpacePriv.h"
13 #include "src/core/SkColorSpaceXformSteps.h"
14 #include "src/core/SkConvertPixels.h"
15 #include "src/core/SkMatrixProvider.h"
16 #include "src/core/SkRasterPipeline.h"
17 #include "src/core/SkReadBuffer.h"
18 #include "src/core/SkVM.h"
19 #include "src/core/SkWriteBuffer.h"
20
21 #if defined(SK_GRAPHITE)
22 #include "src/core/SkColorSpacePriv.h"
23 #include "src/gpu/graphite/KeyContext.h"
24 #include "src/gpu/graphite/KeyHelpers.h"
25 #include "src/gpu/graphite/PaintParamsKey.h"
26 #endif
27
28 #include <cmath>
29
30 enum GradientSerializationFlags {
31 // Bits 29:31 used for various boolean flags
32 kHasPosition_GSF = 0x80000000,
33 kHasLegacyLocalMatrix_GSF = 0x40000000,
34 kHasColorSpace_GSF = 0x20000000,
35
36 // Bits 12:28 unused
37
38 // Bits 8:11 for fTileMode
39 kTileModeShift_GSF = 8,
40 kTileModeMask_GSF = 0xF,
41
42 // Bits 4:7 for fInterpolation.fColorSpace
43 kInterpolationColorSpaceShift_GSF = 4,
44 kInterpolationColorSpaceMask_GSF = 0xF,
45
46 // Bits 1:3 for fInterpolation.fHueMethod
47 kInterpolationHueMethodShift_GSF = 1,
48 kInterpolationHueMethodMask_GSF = 0x7,
49
50 // Bit 0 for fInterpolation.fInPremul
51 kInterpolationInPremul_GSF = 0x1,
52 };
53
Descriptor()54 SkGradientShaderBase::Descriptor::Descriptor() {
55 sk_bzero(this, sizeof(*this));
56 fTileMode = SkTileMode::kClamp;
57 }
58 SkGradientShaderBase::Descriptor::~Descriptor() = default;
59
flatten(SkWriteBuffer & buffer) const60 void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
61 uint32_t flags = 0;
62 if (fPositions) {
63 flags |= kHasPosition_GSF;
64 }
65 sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
66 if (colorSpaceData) {
67 flags |= kHasColorSpace_GSF;
68 }
69 if (fInterpolation.fInPremul == Interpolation::InPremul::kYes) {
70 flags |= kInterpolationInPremul_GSF;
71 }
72 SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
73 flags |= ((uint32_t)fTileMode << kTileModeShift_GSF);
74 SkASSERT(static_cast<uint32_t>(fInterpolation.fColorSpace) <= kInterpolationColorSpaceMask_GSF);
75 flags |= ((uint32_t)fInterpolation.fColorSpace << kInterpolationColorSpaceShift_GSF);
76 SkASSERT(static_cast<uint32_t>(fInterpolation.fHueMethod) <= kInterpolationHueMethodMask_GSF);
77 flags |= ((uint32_t)fInterpolation.fHueMethod << kInterpolationHueMethodShift_GSF);
78
79 buffer.writeUInt(flags);
80
81 // If we injected implicit first/last stops at construction time, omit those when serializing:
82 int colorCount = fColorCount;
83 const SkColor4f* colors = fColors;
84 const SkScalar* positions = fPositions;
85 if (fFirstStopIsImplicit) {
86 colorCount--;
87 colors++;
88 if (positions) {
89 positions++;
90 }
91 }
92 if (fLastStopIsImplicit) {
93 colorCount--;
94 }
95
96 buffer.writeColor4fArray(colors, colorCount);
97 if (colorSpaceData) {
98 buffer.writeDataAsByteArray(colorSpaceData.get());
99 }
100 if (positions) {
101 buffer.writeScalarArray(positions, colorCount);
102 }
103 }
104
105 template <int N, typename T, bool MEM_MOVE>
validate_array(SkReadBuffer & buffer,size_t count,SkSTArray<N,T,MEM_MOVE> * array)106 static bool validate_array(SkReadBuffer& buffer, size_t count, SkSTArray<N, T, MEM_MOVE>* array) {
107 if (!buffer.validateCanReadN<T>(count)) {
108 return false;
109 }
110
111 array->resize_back(count);
112 return true;
113 }
114
unflatten(SkReadBuffer & buffer,SkMatrix * legacyLocalMatrix)115 bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer,
116 SkMatrix* legacyLocalMatrix) {
117 // New gradient format. Includes floating point color, color space, densely packed flags
118 uint32_t flags = buffer.readUInt();
119
120 fTileMode = (SkTileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
121
122 fInterpolation.fColorSpace = (Interpolation::ColorSpace)(
123 (flags >> kInterpolationColorSpaceShift_GSF) & kInterpolationColorSpaceMask_GSF);
124 fInterpolation.fHueMethod = (Interpolation::HueMethod)(
125 (flags >> kInterpolationHueMethodShift_GSF) & kInterpolationHueMethodMask_GSF);
126 fInterpolation.fInPremul = (flags & kInterpolationInPremul_GSF) ? Interpolation::InPremul::kYes
127 : Interpolation::InPremul::kNo;
128
129 fColorCount = buffer.getArrayCount();
130
131 if (!(validate_array(buffer, fColorCount, &fColorStorage) &&
132 buffer.readColor4fArray(fColorStorage.begin(), fColorCount))) {
133 return false;
134 }
135 fColors = fColorStorage.begin();
136
137 if (SkToBool(flags & kHasColorSpace_GSF)) {
138 sk_sp<SkData> data = buffer.readByteArrayAsData();
139 fColorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr;
140 } else {
141 fColorSpace = nullptr;
142 }
143 if (SkToBool(flags & kHasPosition_GSF)) {
144 if (!(validate_array(buffer, fColorCount, &fPositionStorage) &&
145 buffer.readScalarArray(fPositionStorage.begin(), fColorCount))) {
146 return false;
147 }
148 fPositions = fPositionStorage.begin();
149 } else {
150 fPositions = nullptr;
151 }
152 if (SkToBool(flags & kHasLegacyLocalMatrix_GSF)) {
153 SkASSERT(buffer.isVersionLT(SkPicturePriv::Version::kNoShaderLocalMatrix));
154 buffer.readMatrix(legacyLocalMatrix);
155 } else {
156 *legacyLocalMatrix = SkMatrix::I();
157 }
158 return buffer.isValid();
159 }
160
161 ////////////////////////////////////////////////////////////////////////////////////////////
162
SkGradientShaderBase(const Descriptor & desc,const SkMatrix & ptsToUnit)163 SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
164 : fPtsToUnit(ptsToUnit)
165 , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB())
166 , fFirstStopIsImplicit(false)
167 , fLastStopIsImplicit(false)
168 , fColorsAreOpaque(true) {
169 fPtsToUnit.getType(); // Precache so reads are threadsafe.
170 SkASSERT(desc.fColorCount > 1);
171
172 fInterpolation = desc.fInterpolation;
173
174 SkASSERT((unsigned)desc.fTileMode < kSkTileModeCount);
175 fTileMode = desc.fTileMode;
176
177 /* Note: we let the caller skip the first and/or last position.
178 i.e. pos[0] = 0.3, pos[1] = 0.7
179 In these cases, we insert entries to ensure that the final data
180 will be bracketed by [0, 1].
181 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
182
183 Thus colorCount (the caller's value, and fColorCount (our value) may
184 differ by up to 2. In the above example:
185 colorCount = 2
186 fColorCount = 4
187 */
188 fColorCount = desc.fColorCount;
189 // check if we need to add in start and/or end position/colors
190 if (desc.fPositions) {
191 fFirstStopIsImplicit = desc.fPositions[0] != 0;
192 fLastStopIsImplicit = desc.fPositions[desc.fColorCount - 1] != SK_Scalar1;
193 fColorCount += fFirstStopIsImplicit + fLastStopIsImplicit;
194 }
195
196 size_t storageSize =
197 fColorCount * (sizeof(SkColor4f) + (desc.fPositions ? sizeof(SkScalar) : 0));
198 fColors = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
199 fPositions = desc.fPositions ? reinterpret_cast<SkScalar*>(fColors + fColorCount) : nullptr;
200
201 // Now copy over the colors, adding the duplicates at t=0 and t=1 as needed
202 SkColor4f* colors = fColors;
203 if (fFirstStopIsImplicit) {
204 *colors++ = desc.fColors[0];
205 }
206 for (int i = 0; i < desc.fColorCount; ++i) {
207 colors[i] = desc.fColors[i];
208 fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1);
209 }
210 if (fLastStopIsImplicit) {
211 colors += desc.fColorCount;
212 *colors = desc.fColors[desc.fColorCount - 1];
213 }
214
215 if (desc.fPositions) {
216 SkScalar prev = 0;
217 SkScalar* positions = fPositions;
218 *positions++ = prev; // force the first pos to 0
219
220 int startIndex = fFirstStopIsImplicit ? 0 : 1;
221 int count = desc.fColorCount + fLastStopIsImplicit;
222
223 bool uniformStops = true;
224 const SkScalar uniformStep = desc.fPositions[startIndex] - prev;
225 for (int i = startIndex; i < count; i++) {
226 // Pin the last value to 1.0, and make sure pos is monotonic.
227 auto curr = (i == desc.fColorCount) ? 1 : SkTPin(desc.fPositions[i], prev, 1.0f);
228 uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev);
229
230 *positions++ = prev = curr;
231 }
232
233 // If the stops are uniform, treat them as implicit.
234 if (uniformStops) {
235 fPositions = nullptr;
236 }
237 }
238 }
239
~SkGradientShaderBase()240 SkGradientShaderBase::~SkGradientShaderBase() {}
241
add_stop_color(SkRasterPipeline_GradientCtx * ctx,size_t stop,SkPMColor4f Fs,SkPMColor4f Bs)242 static void add_stop_color(SkRasterPipeline_GradientCtx* ctx, size_t stop,
243 SkPMColor4f Fs, SkPMColor4f Bs) {
244 (ctx->fs[0])[stop] = Fs.fR;
245 (ctx->fs[1])[stop] = Fs.fG;
246 (ctx->fs[2])[stop] = Fs.fB;
247 (ctx->fs[3])[stop] = Fs.fA;
248
249 (ctx->bs[0])[stop] = Bs.fR;
250 (ctx->bs[1])[stop] = Bs.fG;
251 (ctx->bs[2])[stop] = Bs.fB;
252 (ctx->bs[3])[stop] = Bs.fA;
253 }
254
add_const_color(SkRasterPipeline_GradientCtx * ctx,size_t stop,SkPMColor4f color)255 static void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f color) {
256 add_stop_color(ctx, stop, { 0, 0, 0, 0 }, color);
257 }
258
259 // Calculate a factor F and a bias B so that color = F*t + B when t is in range of
260 // the stop. Assume that the distance between stops is 1/gapCount.
init_stop_evenly(SkRasterPipeline_GradientCtx * ctx,float gapCount,size_t stop,SkPMColor4f c_l,SkPMColor4f c_r)261 static void init_stop_evenly(SkRasterPipeline_GradientCtx* ctx, float gapCount, size_t stop,
262 SkPMColor4f c_l, SkPMColor4f c_r) {
263 // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
264 SkPMColor4f Fs = {
265 (c_r.fR - c_l.fR) * gapCount,
266 (c_r.fG - c_l.fG) * gapCount,
267 (c_r.fB - c_l.fB) * gapCount,
268 (c_r.fA - c_l.fA) * gapCount,
269 };
270 SkPMColor4f Bs = {
271 c_l.fR - Fs.fR*(stop/gapCount),
272 c_l.fG - Fs.fG*(stop/gapCount),
273 c_l.fB - Fs.fB*(stop/gapCount),
274 c_l.fA - Fs.fA*(stop/gapCount),
275 };
276 add_stop_color(ctx, stop, Fs, Bs);
277 }
278
279 // For each stop we calculate a bias B and a scale factor F, such that
280 // for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
init_stop_pos(SkRasterPipeline_GradientCtx * ctx,size_t stop,float t_l,float t_r,SkPMColor4f c_l,SkPMColor4f c_r)281 static void init_stop_pos(SkRasterPipeline_GradientCtx* ctx, size_t stop, float t_l, float t_r,
282 SkPMColor4f c_l, SkPMColor4f c_r) {
283 // See note about Clankium's old compiler in init_stop_evenly().
284 SkPMColor4f Fs = {
285 (c_r.fR - c_l.fR) / (t_r - t_l),
286 (c_r.fG - c_l.fG) / (t_r - t_l),
287 (c_r.fB - c_l.fB) / (t_r - t_l),
288 (c_r.fA - c_l.fA) / (t_r - t_l),
289 };
290 SkPMColor4f Bs = {
291 c_l.fR - Fs.fR*t_l,
292 c_l.fG - Fs.fG*t_l,
293 c_l.fB - Fs.fB*t_l,
294 c_l.fA - Fs.fA*t_l,
295 };
296 ctx->ts[stop] = t_l;
297 add_stop_color(ctx, stop, Fs, Bs);
298 }
299
AppendGradientFillStages(SkRasterPipeline * p,SkArenaAlloc * alloc,const SkPMColor4f * pmColors,const SkScalar * positions,int count)300 void SkGradientShaderBase::AppendGradientFillStages(SkRasterPipeline* p,
301 SkArenaAlloc* alloc,
302 const SkPMColor4f* pmColors,
303 const SkScalar* positions,
304 int count) {
305 // The two-stop case with stops at 0 and 1.
306 if (count == 2 && positions == nullptr) {
307 const SkPMColor4f c_l = pmColors[0],
308 c_r = pmColors[1];
309
310 // See F and B below.
311 auto ctx = alloc->make<SkRasterPipeline_EvenlySpaced2StopGradientCtx>();
312 (skvx::float4::Load(c_r.vec()) - skvx::float4::Load(c_l.vec())).store(ctx->f);
313 ( skvx::float4::Load(c_l.vec())).store(ctx->b);
314
315 p->append(SkRasterPipelineOp::evenly_spaced_2_stop_gradient, ctx);
316 } else {
317 auto* ctx = alloc->make<SkRasterPipeline_GradientCtx>();
318
319 // Note: In order to handle clamps in search, the search assumes a stop conceptully placed
320 // at -inf. Therefore, the max number of stops is fColorCount+1.
321 for (int i = 0; i < 4; i++) {
322 // Allocate at least at for the AVX2 gather from a YMM register.
323 ctx->fs[i] = alloc->makeArray<float>(std::max(count + 1, 8));
324 ctx->bs[i] = alloc->makeArray<float>(std::max(count + 1, 8));
325 }
326
327 if (positions == nullptr) {
328 // Handle evenly distributed stops.
329
330 size_t stopCount = count;
331 float gapCount = stopCount - 1;
332
333 SkPMColor4f c_l = pmColors[0];
334 for (size_t i = 0; i < stopCount - 1; i++) {
335 SkPMColor4f c_r = pmColors[i + 1];
336 init_stop_evenly(ctx, gapCount, i, c_l, c_r);
337 c_l = c_r;
338 }
339 add_const_color(ctx, stopCount - 1, c_l);
340
341 ctx->stopCount = stopCount;
342 p->append(SkRasterPipelineOp::evenly_spaced_gradient, ctx);
343 } else {
344 // Handle arbitrary stops.
345
346 ctx->ts = alloc->makeArray<float>(count + 1);
347
348 // Remove the default stops inserted by SkGradientShaderBase::SkGradientShaderBase
349 // because they are naturally handled by the search method.
350 int firstStop;
351 int lastStop;
352 if (count > 2) {
353 firstStop = pmColors[0] != pmColors[1] ? 0 : 1;
354 lastStop = pmColors[count - 2] != pmColors[count - 1] ? count - 1 : count - 2;
355 } else {
356 firstStop = 0;
357 lastStop = 1;
358 }
359
360 size_t stopCount = 0;
361 float t_l = positions[firstStop];
362 SkPMColor4f c_l = pmColors[firstStop];
363 add_const_color(ctx, stopCount++, c_l);
364 // N.B. lastStop is the index of the last stop, not one after.
365 for (int i = firstStop; i < lastStop; i++) {
366 float t_r = positions[i + 1];
367 SkPMColor4f c_r = pmColors[i + 1];
368 SkASSERT(t_l <= t_r);
369 if (t_l < t_r) {
370 init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r);
371 stopCount += 1;
372 }
373 t_l = t_r;
374 c_l = c_r;
375 }
376
377 ctx->ts[stopCount] = t_l;
378 add_const_color(ctx, stopCount++, c_l);
379
380 ctx->stopCount = stopCount;
381 p->append(SkRasterPipelineOp::gradient, ctx);
382 }
383 }
384 }
385
appendStages(const SkStageRec & rec,const MatrixRec & mRec) const386 bool SkGradientShaderBase::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
387 SkRasterPipeline* p = rec.fPipeline;
388 SkArenaAlloc* alloc = rec.fAlloc;
389 SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
390
391 std::optional<MatrixRec> newMRec = mRec.apply(rec, fPtsToUnit);
392 if (!newMRec.has_value()) {
393 return false;
394 }
395
396 SkRasterPipeline_<256> postPipeline;
397
398 this->appendGradientStages(alloc, p, &postPipeline);
399
400 switch(fTileMode) {
401 case SkTileMode::kMirror: p->append(SkRasterPipelineOp::mirror_x_1); break;
402 case SkTileMode::kRepeat: p->append(SkRasterPipelineOp::repeat_x_1); break;
403 case SkTileMode::kDecal:
404 decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
405 decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1);
406 // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask
407 p->append(SkRasterPipelineOp::decal_x, decal_ctx);
408 [[fallthrough]];
409
410 case SkTileMode::kClamp:
411 if (!fPositions) {
412 // We clamp only when the stops are evenly spaced.
413 // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1.
414 // In that case, we must make sure we're using the general "gradient" stage,
415 // which is the only stage that will correctly handle unclamped t.
416 p->append(SkRasterPipelineOp::clamp_x_1);
417 }
418 break;
419 }
420
421 // Transform all of the colors to destination color space, possibly premultiplied
422 SkColor4fXformer xformedColors(this, rec.fDstCS);
423 AppendGradientFillStages(p, alloc, xformedColors.fColors.begin(), fPositions, fColorCount);
424
425 using ColorSpace = Interpolation::ColorSpace;
426 bool colorIsPremul = this->interpolateInPremul();
427
428 // If we interpolated premul colors in any of the special color spaces, we need to unpremul
429 if (colorIsPremul && !fColorsAreOpaque) {
430 switch (fInterpolation.fColorSpace) {
431 case ColorSpace::kLab:
432 case ColorSpace::kOKLab:
433 p->append(SkRasterPipelineOp::unpremul);
434 colorIsPremul = false;
435 break;
436 case ColorSpace::kLCH:
437 case ColorSpace::kOKLCH:
438 case ColorSpace::kHSL:
439 case ColorSpace::kHWB:
440 p->append(SkRasterPipelineOp::unpremul_polar);
441 colorIsPremul = false;
442 break;
443 default: break;
444 }
445 }
446
447 // Convert colors in exotic spaces back to their intermediate SkColorSpace
448 switch (fInterpolation.fColorSpace) {
449 case ColorSpace::kLab: p->append(SkRasterPipelineOp::css_lab_to_xyz); break;
450 case ColorSpace::kOKLab: p->append(SkRasterPipelineOp::css_oklab_to_linear_srgb); break;
451 case ColorSpace::kLCH: p->append(SkRasterPipelineOp::css_hcl_to_lab);
452 p->append(SkRasterPipelineOp::css_lab_to_xyz); break;
453 case ColorSpace::kOKLCH: p->append(SkRasterPipelineOp::css_hcl_to_lab);
454 p->append(SkRasterPipelineOp::css_oklab_to_linear_srgb); break;
455 case ColorSpace::kHSL: p->append(SkRasterPipelineOp::css_hsl_to_srgb); break;
456 case ColorSpace::kHWB: p->append(SkRasterPipelineOp::css_hwb_to_srgb); break;
457 default: break;
458 }
459
460 // Now transform from intermediate to destination color space.
461 // See comments in GrGradientShader.cpp about the decisions here.
462 SkColorSpace* dstColorSpace = rec.fDstCS ? rec.fDstCS : sk_srgb_singleton();
463 SkAlphaType intermediateAlphaType = colorIsPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
464 // TODO(skia:13108): Get dst alpha type correctly
465 SkAlphaType dstAlphaType = kPremul_SkAlphaType;
466
467 if (fColorsAreOpaque) {
468 intermediateAlphaType = dstAlphaType = kUnpremul_SkAlphaType;
469 }
470
471 alloc->make<SkColorSpaceXformSteps>(xformedColors.fIntermediateColorSpace.get(),
472 intermediateAlphaType,
473 dstColorSpace,
474 dstAlphaType)
475 ->apply(p);
476
477 if (decal_ctx) {
478 p->append(SkRasterPipelineOp::check_decal_mask, decal_ctx);
479 }
480
481 p->extend(postPipeline);
482
483 return true;
484 }
485
486 // Color conversion functions used in gradient interpolation, based on
487 // https://www.w3.org/TR/css-color-4/#color-conversion-code
css_lab_to_xyz(skvm::Color lab)488 static skvm::Color css_lab_to_xyz(skvm::Color lab) {
489 constexpr float k = 24389 / 27.0f;
490 constexpr float e = 216 / 24389.0f;
491
492 skvm::F32 f[3];
493 f[1] = (lab.r + 16) * (1 / 116.0f);
494 f[0] = (lab.g * (1 / 500.0f)) + f[1];
495 f[2] = f[1] - (lab.b * (1 / 200.0f));
496
497 skvm::F32 f_cubed[3] = { f[0]*f[0]*f[0], f[1]*f[1]*f[1], f[2]*f[2]*f[2] };
498
499 skvm::F32 xyz[3] = {
500 skvm::select(f_cubed[0] > e, f_cubed[0], (116 * f[0] - 16) * (1 / k)),
501 skvm::select(lab.r > k * e , f_cubed[1], lab.r * (1 / k)),
502 skvm::select(f_cubed[2] > e, f_cubed[2], (116 * f[2] - 16) * (1 / k))
503 };
504
505 constexpr float D50[3] = { 0.3457f / 0.3585f, 1.0f, (1.0f - 0.3457f - 0.3585f) / 0.3585f };
506 return skvm::Color { xyz[0]*D50[0], xyz[1]*D50[1], xyz[2]*D50[2], lab.a };
507 }
508
509 // Skia stores all polar colors with hue in the first component, so this "LCH -> Lab" transform
510 // actually takes "HCL". This is also used to do the same polar transform for OkHCL to OkLAB.
css_hcl_to_lab(skvm::Color hcl)511 static skvm::Color css_hcl_to_lab(skvm::Color hcl) {
512 skvm::F32 hueRadians = hcl.r * (SK_FloatPI / 180);
513 return skvm::Color {
514 hcl.b,
515 hcl.g * approx_cos(hueRadians),
516 hcl.g * approx_sin(hueRadians),
517 hcl.a
518 };
519 }
520
css_hcl_to_xyz(skvm::Color hcl)521 static skvm::Color css_hcl_to_xyz(skvm::Color hcl) {
522 return css_lab_to_xyz(css_hcl_to_lab(hcl));
523 }
524
css_oklab_to_linear_srgb(skvm::Color oklab)525 static skvm::Color css_oklab_to_linear_srgb(skvm::Color oklab) {
526 skvm::F32 l_ = oklab.r + 0.3963377774f * oklab.g + 0.2158037573f * oklab.b,
527 m_ = oklab.r - 0.1055613458f * oklab.g - 0.0638541728f * oklab.b,
528 s_ = oklab.r - 0.0894841775f * oklab.g - 1.2914855480f * oklab.b;
529
530 skvm::F32 l = l_*l_*l_,
531 m = m_*m_*m_,
532 s = s_*s_*s_;
533
534 return skvm::Color {
535 +4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s,
536 -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s,
537 -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s,
538 oklab.a
539 };
540
541 }
542
css_okhcl_to_linear_srgb(skvm::Color okhcl)543 static skvm::Color css_okhcl_to_linear_srgb(skvm::Color okhcl) {
544 return css_oklab_to_linear_srgb(css_hcl_to_lab(okhcl));
545 }
546
mod_f(skvm::F32 x,float y)547 static skvm::F32 mod_f(skvm::F32 x, float y) {
548 return x - y * skvm::floor(x * (1 / y));
549 }
550
css_hsl_to_srgb(skvm::Color hsl)551 static skvm::Color css_hsl_to_srgb(skvm::Color hsl) {
552 hsl.r = mod_f(hsl.r, 360);
553 hsl.r = skvm::select(hsl.r < 0, hsl.r + 360, hsl.r);
554
555 hsl.g *= 0.01f;
556 hsl.b *= 0.01f;
557
558 skvm::F32 k[3] = {
559 mod_f(0 + hsl.r * (1 / 30.0f), 12),
560 mod_f(8 + hsl.r * (1 / 30.0f), 12),
561 mod_f(4 + hsl.r * (1 / 30.0f), 12),
562 };
563 skvm::F32 a = hsl.g * min(hsl.b, 1 - hsl.b);
564 return skvm::Color {
565 hsl.b - a * clamp(min(k[0] - 3, 9 - k[0]), -1, 1),
566 hsl.b - a * clamp(min(k[1] - 3, 9 - k[1]), -1, 1),
567 hsl.b - a * clamp(min(k[2] - 3, 9 - k[2]), -1, 1),
568 hsl.a
569 };
570 }
571
css_hwb_to_srgb(skvm::Color hwb,skvm::Builder * p)572 static skvm::Color css_hwb_to_srgb(skvm::Color hwb, skvm::Builder* p) {
573 hwb.g *= 0.01f;
574 hwb.b *= 0.01f;
575
576 skvm::F32 gray = hwb.g / (hwb.g + hwb.b);
577
578 skvm::Color rgb = css_hsl_to_srgb(skvm::Color{hwb.r, p->splat(100.0f), p->splat(50.0f), hwb.a});
579 rgb.r = rgb.r * (1 - hwb.g - hwb.b) + hwb.g;
580 rgb.g = rgb.g * (1 - hwb.g - hwb.b) + hwb.g;
581 rgb.b = rgb.b * (1 - hwb.g - hwb.b) + hwb.g;
582
583 skvm::I32 isGray = (hwb.g + hwb.b) >= 1;
584
585 return skvm::Color {
586 select(isGray, gray, rgb.r),
587 select(isGray, gray, rgb.g),
588 select(isGray, gray, rgb.b),
589 hwb.a
590 };
591 }
592
program(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color,const MatrixRec & mRec,const SkColorInfo & dstInfo,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const593 skvm::Color SkGradientShaderBase::program(skvm::Builder* p,
594 skvm::Coord device,
595 skvm::Coord local,
596 skvm::Color /*paint*/,
597 const MatrixRec& mRec,
598 const SkColorInfo& dstInfo,
599 skvm::Uniforms* uniforms,
600 SkArenaAlloc* alloc) const {
601 if (!mRec.apply(p, &local, uniforms, fPtsToUnit).has_value()) {
602 return {};
603 }
604
605 skvm::I32 mask = p->splat(~0);
606 skvm::F32 t = this->transformT(p,uniforms, local, &mask);
607
608 // Perhaps unexpectedly, clamping is handled naturally by our search, so we
609 // don't explicitly clamp t to [0,1]. That clamp would break hard stops
610 // right at 0 or 1 boundaries in kClamp mode. (kRepeat and kMirror always
611 // produce values in [0,1].)
612 switch(fTileMode) {
613 case SkTileMode::kClamp:
614 break;
615
616 case SkTileMode::kDecal:
617 mask &= (t == clamp01(t));
618 break;
619
620 case SkTileMode::kRepeat:
621 t = fract(t);
622 break;
623
624 case SkTileMode::kMirror: {
625 // t = | (t-1) - 2*(floor( (t-1)*0.5 )) - 1 |
626 // {-A-} {--------B-------}
627 skvm::F32 A = t - 1.0f,
628 B = floor(A * 0.5f);
629 t = abs(A - (B + B) - 1.0f);
630 } break;
631 }
632
633 // Transform our colors as we want them interpolated, in dst color space, possibly premul.
634 SkColor4fXformer xformedColors(this, dstInfo.colorSpace());
635 const SkPMColor4f* rgba = xformedColors.fColors.begin();
636
637 // Transform our colors into a scale factor f and bias b such that for
638 // any t between stops i and i+1, the color we want is mad(t, f[i], b[i]).
639 using F4 = skvx::Vec<4,float>;
640 struct FB { F4 f,b; };
641 skvm::Color color;
642
643 auto uniformF = [&](float x) { return p->uniformF(uniforms->pushF(x)); };
644
645 if (fColorCount == 2) {
646 // 2-stop gradients have colors at 0 and 1, and so must be evenly spaced.
647 SkASSERT(fPositions == nullptr);
648
649 // With 2 stops, we upload the single FB as uniforms and interpolate directly with t.
650 F4 lo = F4::Load(rgba + 0),
651 hi = F4::Load(rgba + 1);
652 F4 F = hi - lo,
653 B = lo;
654
655 auto T = clamp01(t);
656 color = {
657 T * uniformF(F[0]) + uniformF(B[0]),
658 T * uniformF(F[1]) + uniformF(B[1]),
659 T * uniformF(F[2]) + uniformF(B[2]),
660 T * uniformF(F[3]) + uniformF(B[3]),
661 };
662 } else {
663 // To handle clamps in search we add a conceptual stop at t=-inf, so we
664 // may need up to fColorCount+1 FBs and fColorCount t stops between them:
665 //
666 // FBs: [color 0] [color 0->1] [color 1->2] [color 2->3] ...
667 // stops: (-inf) t0 t1 t2 ...
668 //
669 // Both these arrays could end up shorter if any hard stops share the same t.
670 FB* fb = alloc->makeArrayDefault<FB>(fColorCount+1);
671 std::vector<float> stops; // TODO: SkSTArray?
672 stops.reserve(fColorCount);
673
674 // Here's our conceptual stop at t=-inf covering all t<=0, clamping to our first color.
675 float t_lo = this->getPos(0);
676 F4 color_lo = F4::Load(rgba);
677 fb[0] = { 0.0f, color_lo };
678 // N.B. No stops[] entry for this implicit -inf.
679
680 // Now the non-edge cases, calculating scale and bias between adjacent normal stops.
681 for (int i = 1; i < fColorCount; i++) {
682 float t_hi = this->getPos(i);
683 F4 color_hi = F4::Load(rgba + i);
684
685 // If t_lo == t_hi, we're on a hard stop, and transition immediately to the next color.
686 SkASSERT(t_lo <= t_hi);
687 if (t_lo < t_hi) {
688 F4 f = (color_hi - color_lo) / (t_hi - t_lo),
689 b = color_lo - f*t_lo;
690 stops.push_back(t_lo);
691 fb[stops.size()] = {f,b};
692 }
693
694 t_lo = t_hi;
695 color_lo = color_hi;
696 }
697 // Anything >= our final t clamps to our final color.
698 stops.push_back(t_lo);
699 fb[stops.size()] = { 0.0f, color_lo };
700
701 // We'll gather FBs from that array we just created.
702 skvm::Uniform fbs = uniforms->pushPtr(fb);
703
704 // Find the two stops we need to interpolate.
705 skvm::I32 ix;
706 if (fPositions == nullptr) {
707 // Evenly spaced stops... we can calculate ix directly.
708 ix = trunc(clamp(t * uniformF(stops.size() - 1) + 1.0f, 0.0f, uniformF(stops.size())));
709 } else {
710 // Starting ix at 0 bakes in our conceptual first stop at -inf.
711 // TODO: good place to experiment with a loop in skvm.... stops.size() can be huge.
712 ix = p->splat(0);
713 for (float stop : stops) {
714 // ix += (t >= stop) ? +1 : 0 ~~>
715 // ix -= (t >= stop) ? -1 : 0
716 ix -= (t >= uniformF(stop));
717 }
718 // TODO: we could skip any of the default stops GradientShaderBase's ctor added
719 // to ensure the full [0,1] span is covered. This linear search doesn't need
720 // them for correctness, and it'd be up to two fewer stops to check.
721 // N.B. we do still need those stops for the fPositions == nullptr direct math path.
722 }
723
724 // A scale factor and bias for each lane, 8 total.
725 // TODO: simpler, faster, tidier to push 8 uniform pointers, one for each struct lane?
726 ix = shl(ix, 3);
727 skvm::F32 Fr = gatherF(fbs, ix + 0);
728 skvm::F32 Fg = gatherF(fbs, ix + 1);
729 skvm::F32 Fb = gatherF(fbs, ix + 2);
730 skvm::F32 Fa = gatherF(fbs, ix + 3);
731
732 skvm::F32 Br = gatherF(fbs, ix + 4);
733 skvm::F32 Bg = gatherF(fbs, ix + 5);
734 skvm::F32 Bb = gatherF(fbs, ix + 6);
735 skvm::F32 Ba = gatherF(fbs, ix + 7);
736
737 // This is what we've been building towards!
738 color = {
739 t * Fr + Br,
740 t * Fg + Bg,
741 t * Fb + Bb,
742 t * Fa + Ba,
743 };
744 }
745
746 using ColorSpace = Interpolation::ColorSpace;
747 bool colorIsPremul = this->interpolateInPremul();
748
749 // If we interpolated premul colors in any of the special color spaces, we need to unpremul
750 if (colorIsPremul) {
751 switch (fInterpolation.fColorSpace) {
752 case ColorSpace::kLab:
753 case ColorSpace::kOKLab:
754 color = unpremul(color);
755 colorIsPremul = false;
756 break;
757 case ColorSpace::kLCH:
758 case ColorSpace::kOKLCH:
759 case ColorSpace::kHSL:
760 case ColorSpace::kHWB: {
761 // Avoid unpremuling hue
762 skvm::F32 hue = color.r;
763 color = unpremul(color);
764 color.r = hue;
765 colorIsPremul = false;
766 } break;
767 default: break;
768 }
769 }
770
771 // Convert colors in exotic spaces back to their intermediate SkColorSpace
772 switch (fInterpolation.fColorSpace) {
773 case ColorSpace::kLab: color = css_lab_to_xyz(color); break;
774 case ColorSpace::kOKLab: color = css_oklab_to_linear_srgb(color); break;
775 case ColorSpace::kLCH: color = css_hcl_to_xyz(color); break;
776 case ColorSpace::kOKLCH: color = css_okhcl_to_linear_srgb(color); break;
777 case ColorSpace::kHSL: color = css_hsl_to_srgb(color); break;
778 case ColorSpace::kHWB: color = css_hwb_to_srgb(color, p); break;
779 default: break;
780 }
781
782 // Now transform from intermediate to destination color space.
783 // See comments in GrGradientShader.cpp about the decisions here.
784 SkColorSpace* dstColorSpace = dstInfo.colorSpace() ? dstInfo.colorSpace() : sk_srgb_singleton();
785 SkAlphaType intermediateAlphaType = colorIsPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
786 SkAlphaType dstAlphaType = dstInfo.alphaType();
787
788 if (fColorsAreOpaque) {
789 intermediateAlphaType = dstAlphaType = kUnpremul_SkAlphaType;
790 }
791
792 color = SkColorSpaceXformSteps{xformedColors.fIntermediateColorSpace.get(),
793 intermediateAlphaType,
794 dstColorSpace,
795 dstAlphaType}
796 .program(p, uniforms, color);
797
798 return {
799 pun_to_F32(mask & pun_to_I32(color.r)),
800 pun_to_F32(mask & pun_to_I32(color.g)),
801 pun_to_F32(mask & pun_to_I32(color.b)),
802 pun_to_F32(mask & pun_to_I32(color.a)),
803 };
804 }
805
isOpaque() const806 bool SkGradientShaderBase::isOpaque() const {
807 return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal);
808 }
809
rounded_divide(unsigned numer,unsigned denom)810 static unsigned rounded_divide(unsigned numer, unsigned denom) {
811 return (numer + (denom >> 1)) / denom;
812 }
813
onAsLuminanceColor(SkColor * lum) const814 bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
815 // we just compute an average color.
816 // possibly we could weight this based on the proportional width for each color
817 // assuming they are not evenly distributed in the fPos array.
818 int r = 0;
819 int g = 0;
820 int b = 0;
821 const int n = fColorCount;
822 // TODO: use linear colors?
823 for (int i = 0; i < n; ++i) {
824 SkColor c = this->getLegacyColor(i);
825 r += SkColorGetR(c);
826 g += SkColorGetG(c);
827 b += SkColorGetB(c);
828 }
829 *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n));
830 return true;
831 }
832
intermediate_color_space(SkGradientShader::Interpolation::ColorSpace cs,SkColorSpace * dst)833 static sk_sp<SkColorSpace> intermediate_color_space(SkGradientShader::Interpolation::ColorSpace cs,
834 SkColorSpace* dst) {
835 using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
836 switch (cs) {
837 case ColorSpace::kDestination: return sk_ref_sp(dst);
838
839 // css-color-4 allows XYZD50 and XYZD65. For gradients, those are redundant. Interpolating
840 // in any linear RGB space, (regardless of white point), gives the same answer.
841 case ColorSpace::kSRGBLinear: return SkColorSpace::MakeSRGBLinear();
842
843 case ColorSpace::kSRGB:
844 case ColorSpace::kHSL:
845 case ColorSpace::kHWB: return SkColorSpace::MakeSRGB();
846
847 case ColorSpace::kLab:
848 case ColorSpace::kLCH:
849 // Conversion to Lab (and LCH) starts with XYZD50
850 return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, SkNamedGamut::kXYZ);
851
852 case ColorSpace::kOKLab:
853 case ColorSpace::kOKLCH:
854 // The "standard" conversion to these spaces starts with XYZD65. That requires extra
855 // effort to conjure. The author also has reference code for going directly from linear
856 // sRGB, so we use that.
857 // TODO(skia:13108): Even better would be to have an LMS color space, because the first
858 // part of the conversion is a matrix multiply, which could be absorbed into the
859 // color space xform.
860 return SkColorSpace::MakeSRGBLinear();
861 }
862 SkUNREACHABLE;
863 }
864
865 typedef SkPMColor4f (*ConvertColorProc)(SkPMColor4f);
866
srgb_to_hsl(SkPMColor4f rgb)867 static SkPMColor4f srgb_to_hsl(SkPMColor4f rgb) {
868 float mx = std::max({rgb.fR, rgb.fG, rgb.fB});
869 float mn = std::min({rgb.fR, rgb.fG, rgb.fB});
870 float hue = 0, sat = 0, light = (mn + mx) / 2;
871 float d = mx - mn;
872
873 if (d != 0) {
874 sat = (light == 0 || light == 1) ? 0 : (mx - light) / std::min(light, 1 - light);
875 if (mx == rgb.fR) {
876 hue = (rgb.fG - rgb.fB) / d + (rgb.fG < rgb.fB ? 6 : 0);
877 } else if (mx == rgb.fG) {
878 hue = (rgb.fB - rgb.fR) / d + 2;
879 } else {
880 hue = (rgb.fR - rgb.fG) / d + 4;
881 }
882
883 hue *= 60;
884 }
885 return { hue, sat * 100, light * 100, rgb.fA };
886 }
887
srgb_to_hwb(SkPMColor4f rgb)888 static SkPMColor4f srgb_to_hwb(SkPMColor4f rgb) {
889 SkPMColor4f hsl = srgb_to_hsl(rgb);
890 float white = std::min({rgb.fR, rgb.fG, rgb.fB});
891 float black = 1 - std::max({rgb.fR, rgb.fG, rgb.fB});
892 return { hsl.fR, white * 100, black * 100, rgb.fA };
893 }
894
xyzd50_to_lab(SkPMColor4f xyz)895 static SkPMColor4f xyzd50_to_lab(SkPMColor4f xyz) {
896 constexpr float D50[3] = { 0.3457f / 0.3585f, 1.0f, (1.0f - 0.3457f - 0.3585f) / 0.3585f };
897
898 constexpr float e = 216.0f / 24389;
899 constexpr float k = 24389.0f / 27;
900
901 SkPMColor4f f;
902 for (int i = 0; i < 3; ++i) {
903 float v = xyz[i] / D50[i];
904 f[i] = (v > e) ? std::cbrtf(v) : (k * v + 16) / 116;
905 }
906
907 return { (116 * f[1]) - 16, 500 * (f[0] - f[1]), 200 * (f[1] - f[2]), xyz.fA };
908 }
909
910 // The color space is technically LCH, but we produce HCL, so that all polar spaces have hue in the
911 // first component. This simplifies the hue handling for HueMethod and premul/unpremul.
xyzd50_to_hcl(SkPMColor4f xyz)912 static SkPMColor4f xyzd50_to_hcl(SkPMColor4f xyz) {
913 SkPMColor4f Lab = xyzd50_to_lab(xyz);
914 float hue = sk_float_radians_to_degrees(atan2f(Lab[2], Lab[1]));
915 return {hue >= 0 ? hue : hue + 360,
916 sqrtf(Lab[1] * Lab[1] + Lab[2] * Lab[2]),
917 Lab[0],
918 xyz.fA};
919 }
920
921 // https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab
lin_srgb_to_oklab(SkPMColor4f rgb)922 static SkPMColor4f lin_srgb_to_oklab(SkPMColor4f rgb) {
923 float l = 0.4122214708f * rgb.fR + 0.5363325363f * rgb.fG + 0.0514459929f * rgb.fB;
924 float m = 0.2119034982f * rgb.fR + 0.6806995451f * rgb.fG + 0.1073969566f * rgb.fB;
925 float s = 0.0883024619f * rgb.fR + 0.2817188376f * rgb.fG + 0.6299787005f * rgb.fB;
926 l = std::cbrtf(l);
927 m = std::cbrtf(m);
928 s = std::cbrtf(s);
929 return {
930 0.2104542553f*l + 0.7936177850f*m - 0.0040720468f*s,
931 1.9779984951f*l - 2.4285922050f*m + 0.4505937099f*s,
932 0.0259040371f*l + 0.7827717662f*m - 0.8086757660f*s,
933 rgb.fA
934 };
935 }
936
937 // The color space is technically OkLCH, but we produce HCL, so that all polar spaces have hue in
938 // the first component. This simplifies the hue handling for HueMethod and premul/unpremul.
lin_srgb_to_okhcl(SkPMColor4f rgb)939 static SkPMColor4f lin_srgb_to_okhcl(SkPMColor4f rgb) {
940 SkPMColor4f OKLab = lin_srgb_to_oklab(rgb);
941 float hue = sk_float_radians_to_degrees(atan2f(OKLab[2], OKLab[1]));
942 return {hue >= 0 ? hue : hue + 360,
943 sqrtf(OKLab[1] * OKLab[1] + OKLab[2] * OKLab[2]),
944 OKLab[0],
945 rgb.fA};
946 }
947
premul_polar(SkPMColor4f hsl)948 static SkPMColor4f premul_polar(SkPMColor4f hsl) {
949 return { hsl.fR, hsl.fG * hsl.fA, hsl.fB * hsl.fA, hsl.fA };
950 }
951
premul_rgb(SkPMColor4f rgb)952 static SkPMColor4f premul_rgb(SkPMColor4f rgb) {
953 return { rgb.fR * rgb.fA, rgb.fG * rgb.fA, rgb.fB * rgb.fA, rgb.fA };
954 }
955
color_space_is_polar(SkGradientShader::Interpolation::ColorSpace cs)956 static bool color_space_is_polar(SkGradientShader::Interpolation::ColorSpace cs) {
957 using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
958 switch (cs) {
959 case ColorSpace::kLCH:
960 case ColorSpace::kOKLCH:
961 case ColorSpace::kHSL:
962 case ColorSpace::kHWB:
963 return true;
964 default:
965 return false;
966 }
967 }
968
969 // Given `colors` in `src` color space, an interpolation space, and a `dst` color space,
970 // we are doing several things. First, some definitions:
971 //
972 // The interpolation color space is "special" if it can't be represented as an SkColorSpace. This
973 // applies to any color space that isn't an RGB space, like Lab or HSL. These need special handling
974 // because we have to run bespoke code to do the conversion (before interpolation here, and after
975 // interpolation in the backend shader/pipeline).
976 //
977 // The interpolation color space is "polar" if it involves hue (HSL, HWB, LCH, Oklch). These need
978 // special handling, becuase hue is never premultiplied, and because HueMethod comes into play.
979 //
980 // 1) Pick an `intermediate` SkColorSpace. If the interpolation color space is not "special",
981 // (kDestination, kSRGB, etc... ), then `intermediate` is exact. Otherwise, `intermediate` is the
982 // RGB space that prepares us to do the final conversion. For example, conversion to Lab starts
983 // with XYZD50, so `intermediate` will be XYZD50 if we're actually interpolating in Lab.
984 // 2) Transform all colors to the `intermediate` color space, leaving them unpremultiplied.
985 // 3) If the interpolation color space is "special", transform the colors to that space.
986 // 4) If the interpolation color space is "polar", adjust the angles to respect HueMethod.
987 // 5) If premul interpolation is requested, apply that. For "polar" interpolated colors, don't
988 // premultiply hue, only the other two channels. Note that there are four polar spaces.
989 // Two have hue as the first component, and two have it as the third component. To reduce
990 // complexity, we always store hue in the first component, swapping it with luminance for
991 // LCH and Oklch. The backend code (eg, shaders) needs to know about this.
SkColor4fXformer(const SkGradientShaderBase * shader,SkColorSpace * dst)992 SkColor4fXformer::SkColor4fXformer(const SkGradientShaderBase* shader, SkColorSpace* dst) {
993 using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
994 using HueMethod = SkGradientShader::Interpolation::HueMethod;
995
996 const int colorCount = shader->fColorCount;
997 const SkGradientShader::Interpolation interpolation = shader->fInterpolation;
998
999 // 1) Determine the color space of our intermediate colors
1000 fIntermediateColorSpace = intermediate_color_space(interpolation.fColorSpace, dst);
1001
1002 // 2) Convert all colors to the intermediate color space
1003 auto info = SkImageInfo::Make(colorCount, 1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType);
1004
1005 auto dstInfo = info.makeColorSpace(fIntermediateColorSpace);
1006 auto srcInfo = info.makeColorSpace(shader->fColorSpace);
1007
1008 fColors.reset(colorCount);
1009 SkAssertResult(SkConvertPixels(dstInfo, fColors.begin(), info.minRowBytes(),
1010 srcInfo, shader->fColors, info.minRowBytes()));
1011
1012 // 3) Transform to the interpolation color space (if it's special)
1013 ConvertColorProc convertFn = nullptr;
1014 switch (interpolation.fColorSpace) {
1015 case ColorSpace::kHSL: convertFn = srgb_to_hsl; break;
1016 case ColorSpace::kHWB: convertFn = srgb_to_hwb; break;
1017 case ColorSpace::kLab: convertFn = xyzd50_to_lab; break;
1018 case ColorSpace::kLCH: convertFn = xyzd50_to_hcl; break;
1019 case ColorSpace::kOKLab: convertFn = lin_srgb_to_oklab; break;
1020 case ColorSpace::kOKLCH: convertFn = lin_srgb_to_okhcl; break;
1021 default: break;
1022 }
1023
1024 if (convertFn) {
1025 for (int i = 0; i < colorCount; ++i) {
1026 fColors[i] = convertFn(fColors[i]);
1027 }
1028 }
1029
1030 // 4) For polar colors, adjust hue values to respect the hue method. We're using a trick here...
1031 // The specification looks at adjacent colors, and adjusts one or the other. Because we store
1032 // the stops in uniforms (and our backend conversions normalize the hue angle), we can
1033 // instead always apply the adjustment to the *second* color. That lets us keep a running
1034 // total, and do a single pass across all the colors to respect the requested hue method,
1035 // without needing to do any extra work per-pixel.
1036 if (color_space_is_polar(interpolation.fColorSpace)) {
1037 float delta = 0;
1038 for (int i = 0; i < colorCount - 1; ++i) {
1039 float h1 = fColors[i].fR;
1040 float& h2 = fColors[i+1].fR;
1041 h2 += delta;
1042 switch (interpolation.fHueMethod) {
1043 case HueMethod::kShorter:
1044 if (h2 - h1 > 180) {
1045 h2 -= 360; // i.e. h1 += 360
1046 delta -= 360;
1047 } else if (h2 - h1 < -180) {
1048 h2 += 360;
1049 delta += 360;
1050 }
1051 break;
1052 case HueMethod::kLonger:
1053 if ((i == 0 && shader->fFirstStopIsImplicit) ||
1054 (i == colorCount - 2 && shader->fLastStopIsImplicit)) {
1055 // Do nothing. We don't want to introduce a full revolution for these stops
1056 // Full rationale at skbug.com/13941
1057 } else if (0 < h2 - h1 && h2 - h1 < 180) {
1058 h2 -= 360; // i.e. h1 += 360
1059 delta -= 360;
1060 } else if (-180 < h2 - h1 && h2 - h1 <= 0) {
1061 h2 += 360;
1062 delta += 360;
1063 }
1064 break;
1065 case HueMethod::kIncreasing:
1066 if (h2 < h1) {
1067 h2 += 360;
1068 delta += 360;
1069 }
1070 break;
1071 case HueMethod::kDecreasing:
1072 if (h1 < h2) {
1073 h2 -= 360; // i.e. h1 += 360;
1074 delta -= 360;
1075 }
1076 break;
1077 }
1078 }
1079 }
1080
1081 // 5) Apply premultiplication
1082 ConvertColorProc premulFn = nullptr;
1083 if (static_cast<bool>(interpolation.fInPremul)) {
1084 switch (interpolation.fColorSpace) {
1085 case ColorSpace::kHSL:
1086 case ColorSpace::kHWB:
1087 case ColorSpace::kLCH:
1088 case ColorSpace::kOKLCH: premulFn = premul_polar; break;
1089 default: premulFn = premul_rgb; break;
1090 }
1091 }
1092
1093 if (premulFn) {
1094 for (int i = 0; i < colorCount; ++i) {
1095 fColors[i] = premulFn(fColors[i]);
1096 }
1097 }
1098 }
1099
SkColorConverter(const SkColor * colors,int count)1100 SkColorConverter::SkColorConverter(const SkColor* colors, int count) {
1101 const float ONE_OVER_255 = 1.f / 255;
1102 for (int i = 0; i < count; ++i) {
1103 fColors4f.push_back({ SkColorGetR(colors[i]) * ONE_OVER_255,
1104 SkColorGetG(colors[i]) * ONE_OVER_255,
1105 SkColorGetB(colors[i]) * ONE_OVER_255,
1106 SkColorGetA(colors[i]) * ONE_OVER_255 });
1107 }
1108 }
1109
commonAsAGradient(GradientInfo * info) const1110 void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
1111 if (info) {
1112 if (info->fColorCount >= fColorCount) {
1113 if (info->fColors) {
1114 for (int i = 0; i < fColorCount; ++i) {
1115 info->fColors[i] = this->getLegacyColor(i);
1116 }
1117 }
1118 if (info->fColorOffsets) {
1119 for (int i = 0; i < fColorCount; ++i) {
1120 info->fColorOffsets[i] = this->getPos(i);
1121 }
1122 }
1123 }
1124 info->fColorCount = fColorCount;
1125 info->fTileMode = fTileMode;
1126
1127 info->fGradientFlags =
1128 this->interpolateInPremul() ? SkGradientShader::kInterpolateColorsInPremul_Flag : 0;
1129 }
1130 }
1131
1132 // Return true if these parameters are valid/legal/safe to construct a gradient
1133 //
ValidGradient(const SkColor4f colors[],int count,SkTileMode tileMode,const Interpolation & interpolation)1134 bool SkGradientShaderBase::ValidGradient(const SkColor4f colors[], int count, SkTileMode tileMode,
1135 const Interpolation& interpolation) {
1136 return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount &&
1137 (unsigned)interpolation.fColorSpace < Interpolation::kColorSpaceCount &&
1138 (unsigned)interpolation.fHueMethod < Interpolation::kHueMethodCount;
1139 }
1140
Descriptor(const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar positions[],int colorCount,SkTileMode mode,const Interpolation & interpolation)1141 SkGradientShaderBase::Descriptor::Descriptor(const SkColor4f colors[],
1142 sk_sp<SkColorSpace> colorSpace,
1143 const SkScalar positions[],
1144 int colorCount,
1145 SkTileMode mode,
1146 const Interpolation& interpolation)
1147 : fColors(colors)
1148 , fColorSpace(std::move(colorSpace))
1149 , fPositions(positions)
1150 , fColorCount(colorCount)
1151 , fTileMode(mode)
1152 , fInterpolation(interpolation) {
1153 SkASSERT(fColorCount > 1);
1154 }
1155
average_gradient_color(const SkColor4f colors[],const SkScalar pos[],int colorCount)1156 static SkColor4f average_gradient_color(const SkColor4f colors[], const SkScalar pos[],
1157 int colorCount) {
1158 // The gradient is a piecewise linear interpolation between colors. For a given interval,
1159 // the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that
1160 // intervals average color. The overall average color is thus the sum of each piece. The thing
1161 // to keep in mind is that the provided gradient definition may implicitly use p=0 and p=1.
1162 skvx::float4 blend(0.0f);
1163 for (int i = 0; i < colorCount - 1; ++i) {
1164 // Calculate the average color for the interval between pos(i) and pos(i+1)
1165 auto c0 = skvx::float4::Load(&colors[i]);
1166 auto c1 = skvx::float4::Load(&colors[i + 1]);
1167
1168 // when pos == null, there are colorCount uniformly distributed stops, going from 0 to 1,
1169 // so pos[i + 1] - pos[i] = 1/(colorCount-1)
1170 SkScalar w;
1171 if (pos) {
1172 // Match position fixing in SkGradientShader's constructor, clamping positions outside
1173 // [0, 1] and forcing the sequence to be monotonic
1174 SkScalar p0 = SkTPin(pos[i], 0.f, 1.f);
1175 SkScalar p1 = SkTPin(pos[i + 1], p0, 1.f);
1176 w = p1 - p0;
1177
1178 // And account for any implicit intervals at the start or end of the positions
1179 if (i == 0) {
1180 if (p0 > 0.0f) {
1181 // The first color is fixed between p = 0 to pos[0], so 0.5*(ci + cj)*(pj - pi)
1182 // becomes 0.5*(c + c)*(pj - 0) = c * pj
1183 auto c = skvx::float4::Load(&colors[0]);
1184 blend += p0 * c;
1185 }
1186 }
1187 if (i == colorCount - 2) {
1188 if (p1 < 1.f) {
1189 // The last color is fixed between pos[n-1] to p = 1, so 0.5*(ci + cj)*(pj - pi)
1190 // becomes 0.5*(c + c)*(1 - pi) = c * (1 - pi)
1191 auto c = skvx::float4::Load(&colors[colorCount - 1]);
1192 blend += (1.f - p1) * c;
1193 }
1194 }
1195 } else {
1196 w = 1.f / (colorCount - 1);
1197 }
1198
1199 blend += 0.5f * w * (c1 + c0);
1200 }
1201
1202 SkColor4f avg;
1203 blend.store(&avg);
1204 return avg;
1205 }
1206
1207 // Except for special circumstances of clamped gradients, every gradient shape--when degenerate--
1208 // can be mapped to the same fallbacks. The specific shape factories must account for special
1209 // clamped conditions separately, this will always return the last color for clamped gradients.
MakeDegenerateGradient(const SkColor4f colors[],const SkScalar pos[],int colorCount,sk_sp<SkColorSpace> colorSpace,SkTileMode mode)1210 sk_sp<SkShader> SkGradientShaderBase::MakeDegenerateGradient(const SkColor4f colors[],
1211 const SkScalar pos[],
1212 int colorCount,
1213 sk_sp<SkColorSpace> colorSpace,
1214 SkTileMode mode) {
1215 switch(mode) {
1216 case SkTileMode::kDecal:
1217 // normally this would reject the area outside of the interpolation region, so since
1218 // inside region is empty when the radii are equal, the entire draw region is empty
1219 return SkShaders::Empty();
1220 case SkTileMode::kRepeat:
1221 case SkTileMode::kMirror:
1222 // repeat and mirror are treated the same: the border colors are never visible,
1223 // but approximate the final color as infinite repetitions of the colors, so
1224 // it can be represented as the average color of the gradient.
1225 return SkShaders::Color(
1226 average_gradient_color(colors, pos, colorCount), std::move(colorSpace));
1227 case SkTileMode::kClamp:
1228 // Depending on how the gradient shape degenerates, there may be a more specialized
1229 // fallback representation for the factories to use, but this is a reasonable default.
1230 return SkShaders::Color(colors[colorCount - 1], std::move(colorSpace));
1231 }
1232 SkDEBUGFAIL("Should not be reached");
1233 return nullptr;
1234 }
1235
ColorStopOptimizer(const SkColor4f * colors,const SkScalar * pos,int count,SkTileMode mode)1236 SkGradientShaderBase::ColorStopOptimizer::ColorStopOptimizer(const SkColor4f* colors,
1237 const SkScalar* pos,
1238 int count,
1239 SkTileMode mode)
1240 : fColors(colors)
1241 , fPos(pos)
1242 , fCount(count) {
1243
1244 if (!pos || count != 3) {
1245 return;
1246 }
1247
1248 if (SkScalarNearlyEqual(pos[0], 0.0f) &&
1249 SkScalarNearlyEqual(pos[1], 0.0f) &&
1250 SkScalarNearlyEqual(pos[2], 1.0f)) {
1251
1252 if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
1253 colors[0] == colors[1]) {
1254
1255 // Ignore the leftmost color/pos.
1256 fColors += 1;
1257 fPos += 1;
1258 fCount = 2;
1259 }
1260 } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
1261 SkScalarNearlyEqual(pos[1], 1.0f) &&
1262 SkScalarNearlyEqual(pos[2], 1.0f)) {
1263
1264 if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
1265 colors[1] == colors[2]) {
1266
1267 // Ignore the rightmost color/pos.
1268 fCount = 2;
1269 }
1270 }
1271 }
1272
1273 #if defined(SK_GRAPHITE)
1274 // Please see GrGradientShader.cpp::make_interpolated_to_dst for substantial comments
1275 // as to why this code is structured this way.
MakeInterpolatedToDst(const skgpu::graphite::KeyContext & keyContext,skgpu::graphite::PaintParamsKeyBuilder * builder,skgpu::graphite::PipelineDataGatherer * gatherer,const skgpu::graphite::GradientShaderBlocks::GradientData & gradData,const SkGradientShaderBase::Interpolation & interp,SkColorSpace * intermediateCS)1276 void SkGradientShaderBase::MakeInterpolatedToDst(
1277 const skgpu::graphite::KeyContext& keyContext,
1278 skgpu::graphite::PaintParamsKeyBuilder* builder,
1279 skgpu::graphite::PipelineDataGatherer* gatherer,
1280 const skgpu::graphite::GradientShaderBlocks::GradientData& gradData,
1281 const SkGradientShaderBase::Interpolation& interp,
1282 SkColorSpace* intermediateCS) {
1283 using ColorSpace = SkGradientShader::Interpolation::ColorSpace;
1284 using namespace skgpu::graphite;
1285
1286 bool inputPremul = static_cast<bool>(interp.fInPremul);
1287
1288 switch (interp.fColorSpace) {
1289 case ColorSpace::kLab:
1290 case ColorSpace::kOKLab:
1291 case ColorSpace::kLCH:
1292 case ColorSpace::kOKLCH:
1293 case ColorSpace::kHSL:
1294 case ColorSpace::kHWB:
1295 inputPremul = false;
1296 break;
1297 default:
1298 break;
1299 }
1300
1301 const SkColorInfo& dstColorInfo = keyContext.dstColorInfo();
1302
1303 SkColorSpace* dstColorSpace = dstColorInfo.colorSpace() ? dstColorInfo.colorSpace()
1304 : sk_srgb_singleton();
1305
1306 SkAlphaType intermediateAlphaType = inputPremul ? kPremul_SkAlphaType
1307 : kUnpremul_SkAlphaType;
1308
1309 ColorSpaceTransformBlock::ColorSpaceTransformData data(intermediateCS, intermediateAlphaType,
1310 dstColorSpace, dstColorInfo.alphaType());
1311
1312 // The gradient block and colorSpace conversion block need to be combined together
1313 // (via the colorFilterShader block) so that the localMatrix block can treat them as
1314 // one child.
1315 ColorFilterShaderBlock::BeginBlock(keyContext, builder, gatherer);
1316
1317 GradientShaderBlocks::BeginBlock(keyContext, builder, gatherer, gradData);
1318 builder->endBlock();
1319
1320 ColorSpaceTransformBlock::BeginBlock(keyContext, builder, gatherer, &data);
1321 builder->endBlock();
1322
1323 builder->endBlock();
1324 }
1325 #endif
1326