Flutter Engine
The Flutter Engine
SkGradientBaseShader.cpp
Go to the documentation of this file.
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
9
14#include "include/core/SkData.h"
26#include "src/base/SkVx.h"
37
38#include <algorithm>
39#include <cmath>
40#include <optional>
41#include <utility>
42
43using namespace skia_private;
44
46 // Bits 29:31 used for various boolean flags
47 kHasPosition_GSF = 0x80000000,
49 kHasColorSpace_GSF = 0x20000000,
50
51 // Bits 12:28 unused
52
53 // Bits 8:11 for fTileMode
56
57 // Bits 4:7 for fInterpolation.fColorSpace
60
61 // Bits 1:3 for fInterpolation.fHueMethod
64
65 // Bit 0 for fInterpolation.fInPremul
67};
68
70 sk_bzero(this, sizeof(*this));
72}
74
76 uint32_t flags = 0;
77 if (fPositions) {
79 }
80 sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
81 if (colorSpaceData) {
83 }
86 }
87 SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
88 flags |= ((uint32_t)fTileMode << kTileModeShift_GSF);
93
94 buffer.writeUInt(flags);
95
96 // If we injected implicit first/last stops at construction time, omit those when serializing:
97 int colorCount = fColorCount;
98 const SkColor4f* colors = fColors;
99 const SkScalar* positions = fPositions;
101 colorCount--;
102 colors++;
103 if (positions) {
104 positions++;
105 }
106 }
108 colorCount--;
109 }
110
111 buffer.writeColor4fArray(colors, colorCount);
112 if (colorSpaceData) {
113 buffer.writeDataAsByteArray(colorSpaceData.get());
114 }
115 if (positions) {
116 buffer.writeScalarArray(positions, colorCount);
117 }
118}
119
120template <int N, typename T, bool MEM_MOVE>
122 if (!buffer.validateCanReadN<T>(count)) {
123 return false;
124 }
125
126 array->resize_back(count);
127 return true;
128}
129
131 SkMatrix* legacyLocalMatrix) {
132 // New gradient format. Includes floating point color, color space, densely packed flags
133 uint32_t flags = buffer.readUInt();
134
136
143
144 fColorCount = buffer.getArrayCount();
145
146 if (!(validate_array(buffer, fColorCount, &fColorStorage) &&
147 buffer.readColor4fArray(fColorStorage.begin(), fColorCount))) {
148 return false;
149 }
150 fColors = fColorStorage.begin();
151
153 sk_sp<SkData> data = buffer.readByteArrayAsData();
154 fColorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr;
155 } else {
156 fColorSpace = nullptr;
157 }
159 if (!(validate_array(buffer, fColorCount, &fPositionStorage) &&
160 buffer.readScalarArray(fPositionStorage.begin(), fColorCount))) {
161 return false;
162 }
163 fPositions = fPositionStorage.begin();
164 } else {
165 fPositions = nullptr;
166 }
168 SkASSERT(buffer.isVersionLT(SkPicturePriv::Version::kNoShaderLocalMatrix));
169 buffer.readMatrix(legacyLocalMatrix);
170 } else {
171 *legacyLocalMatrix = SkMatrix::I();
172 }
173 return buffer.isValid();
174}
175
176////////////////////////////////////////////////////////////////////////////////////////////
177
179 : fPtsToUnit(ptsToUnit)
183 , fColorsAreOpaque(true) {
184 fPtsToUnit.getType(); // Precache so reads are threadsafe.
185 SkASSERT(desc.fColorCount > 1);
186
187 fInterpolation = desc.fInterpolation;
188
189 SkASSERT((unsigned)desc.fTileMode < kSkTileModeCount);
190 fTileMode = desc.fTileMode;
191
192 /* Note: we let the caller skip the first and/or last position.
193 i.e. pos[0] = 0.3, pos[1] = 0.7
194 In these cases, we insert entries to ensure that the final data
195 will be bracketed by [0, 1].
196 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
197
198 Thus colorCount (the caller's value, and fColorCount (our value) may
199 differ by up to 2. In the above example:
200 colorCount = 2
201 fColorCount = 4
202 */
203 fColorCount = desc.fColorCount;
204
205 // Check if we need to add in start and/or end position/colors
206 if (desc.fPositions) {
207 fFirstStopIsImplicit = desc.fPositions[0] > 0;
208 fLastStopIsImplicit = desc.fPositions[desc.fColorCount - 1] != SK_Scalar1;
210 }
211
212 size_t storageSize =
213 fColorCount * (sizeof(SkColor4f) + (desc.fPositions ? sizeof(SkScalar) : 0));
214 fColors = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
215 fPositions = desc.fPositions ? reinterpret_cast<SkScalar*>(fColors + fColorCount) : nullptr;
216
217 // Now copy over the colors, adding the duplicates at t=0 and t=1 as needed
220 *colors++ = desc.fColors[0];
221 }
222 for (int i = 0; i < desc.fColorCount; ++i) {
223 colors[i] = desc.fColors[i];
224 fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1);
225 }
227 colors += desc.fColorCount;
228 *colors = desc.fColors[desc.fColorCount - 1];
229 }
230
231 if (desc.fPositions) {
232 SkScalar prev = 0;
233 SkScalar* positions = fPositions;
234 *positions++ = prev; // force the first pos to 0
235
236 int startIndex = fFirstStopIsImplicit ? 0 : 1;
237 int count = desc.fColorCount + fLastStopIsImplicit;
238
239 bool uniformStops = true;
240 const SkScalar uniformStep = desc.fPositions[startIndex] - prev;
241 for (int i = startIndex; i < count; i++) {
242 // Pin the last value to 1.0, and make sure pos is monotonic.
243 float curr = 1.0f;
244 if (i != desc.fColorCount) {
245 curr = SkTPin(desc.fPositions[i], prev, 1.0f);
246
247 // If a value is clamped to 1.0 before the last stop, the last stop
248 // actually isn't implicit if we thought it was.
249 if (curr == 1.0f && fLastStopIsImplicit) {
250 fLastStopIsImplicit = false;
251 }
252 }
253
254 uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev);
255
256 *positions++ = prev = curr;
257 }
258
259 if (uniformStops) {
260 // If the stops are uniform, treat them as implicit.
261 fPositions = nullptr;
262 } else {
263 // Remove duplicate stops with more than two of the same stop,
264 // keeping the leftmost and rightmost stop colors.
265 // i.e. 0, 0, 0, 0.2, 0.2, 0.3, 0.3, 0.3, 1, 1
266 // w/ clamp 0, 0, 0.2, 0.2, 0.3, 0.3, 1, 1
267 // w/o clamp 0, 0.2, 0.2, 0.3, 0.3, 1
268 int i = 0;
269 int dedupedColorCount = 0;
270 for (int j = 1; j <= fColorCount; j++) {
271 // We can compare the current positions at i and j since once these fPosition
272 // values are overwritten, our i and j pointers will be past the overwritten values.
273 if (j == fColorCount || fPositions[i] != fPositions[j]) {
274 bool dupStop = j - i > 1;
275
276 // Ignore the leftmost stop (i) if it is a non-clamp tilemode with
277 // a duplicate stop on t = 0.
278 bool ignoreLeftmost = dupStop && fTileMode != SkTileMode::kClamp
279 && fPositions[i] == 0;
280 if (!ignoreLeftmost) {
281 fPositions[dedupedColorCount] = fPositions[i];
282 fColors[dedupedColorCount] = fColors[i];
283 dedupedColorCount++;
284 }
285
286 // Include the rightmost stop (j-1) only if the stop has a duplicate,
287 // ignoring the rightmost stop if it is a non-clamp tilemode with t = 1.
288 bool ignoreRightmost = fTileMode != SkTileMode::kClamp
289 && fPositions[j - 1] == 1;
290 if (dupStop && !ignoreRightmost) {
291 fPositions[dedupedColorCount] = fPositions[j - 1];
292 fColors[dedupedColorCount] = fColors[j - 1];
293 dedupedColorCount++;
294 }
295 i = j;
296 }
297 }
298 fColorCount = dedupedColorCount;
299 }
300 }
301}
302
304
306 size_t stop,
307 SkPMColor4f Fs,
308 SkPMColor4f Bs) {
309 (ctx->fs[0])[stop] = Fs.fR;
310 (ctx->fs[1])[stop] = Fs.fG;
311 (ctx->fs[2])[stop] = Fs.fB;
312 (ctx->fs[3])[stop] = Fs.fA;
313
314 (ctx->bs[0])[stop] = Bs.fR;
315 (ctx->bs[1])[stop] = Bs.fG;
316 (ctx->bs[2])[stop] = Bs.fB;
317 (ctx->bs[3])[stop] = Bs.fA;
318}
319
321 add_stop_color(ctx, stop, {0, 0, 0, 0}, color);
322}
323
324// Calculate a factor F and a bias B so that color = F*t + B when t is in range of
325// the stop. Assume that the distance between stops is 1/gapCount.
327 float gapCount,
328 size_t stop,
329 SkPMColor4f c_l,
330 SkPMColor4f c_r) {
331 // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
332 SkPMColor4f Fs = {
333 (c_r.fR - c_l.fR) * gapCount,
334 (c_r.fG - c_l.fG) * gapCount,
335 (c_r.fB - c_l.fB) * gapCount,
336 (c_r.fA - c_l.fA) * gapCount,
337 };
338 SkPMColor4f Bs = {
339 c_l.fR - Fs.fR * (stop / gapCount),
340 c_l.fG - Fs.fG * (stop / gapCount),
341 c_l.fB - Fs.fB * (stop / gapCount),
342 c_l.fA - Fs.fA * (stop / gapCount),
343 };
344 add_stop_color(ctx, stop, Fs, Bs);
345}
346
347// For each stop we calculate a bias B and a scale factor F, such that
348// for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
350 size_t stop,
351 float t_l,
352 float c_scale,
353 SkPMColor4f c_l,
354 SkPMColor4f c_r) {
355 // See note about Clankium's old compiler in init_stop_evenly().
356 SkPMColor4f Fs = {
357 (c_r.fR - c_l.fR) * c_scale,
358 (c_r.fG - c_l.fG) * c_scale,
359 (c_r.fB - c_l.fB) * c_scale,
360 (c_r.fA - c_l.fA) * c_scale,
361 };
362 SkPMColor4f Bs = {
363 c_l.fR - Fs.fR * t_l,
364 c_l.fG - Fs.fG * t_l,
365 c_l.fB - Fs.fB * t_l,
366 c_l.fA - Fs.fA * t_l,
367 };
368 ctx->ts[stop] = t_l;
369 add_stop_color(ctx, stop, Fs, Bs);
370}
371
373 SkArenaAlloc* alloc,
374 const SkPMColor4f* pmColors,
375 const SkScalar* positions,
376 int count) {
377 // The two-stop case with stops at 0 and 1.
378 if (count == 2 && positions == nullptr) {
379 const SkPMColor4f c_l = pmColors[0], c_r = pmColors[1];
380
381 // See F and B below.
383 (skvx::float4::Load(c_r.vec()) - skvx::float4::Load(c_l.vec())).store(ctx->f);
384 (skvx::float4::Load(c_l.vec())).store(ctx->b);
385
386 p->append(SkRasterPipelineOp::evenly_spaced_2_stop_gradient, ctx);
387 } else {
388 auto* ctx = alloc->make<SkRasterPipeline_GradientCtx>();
389
390 // Note: In order to handle clamps in search, the search assumes a stop conceptully placed
391 // at -inf. Therefore, the max number of stops is fColorCount+1.
392 for (int i = 0; i < 4; i++) {
393 // Allocate at least at for the AVX2 gather from a YMM register.
394 ctx->fs[i] = alloc->makeArray<float>(std::max(count + 1, 8));
395 ctx->bs[i] = alloc->makeArray<float>(std::max(count + 1, 8));
396 }
397
398 if (positions == nullptr) {
399 // Handle evenly distributed stops.
400
401 size_t stopCount = count;
402 float gapCount = stopCount - 1;
403
404 SkPMColor4f c_l = pmColors[0];
405 for (size_t i = 0; i < stopCount - 1; i++) {
406 SkPMColor4f c_r = pmColors[i + 1];
407 init_stop_evenly(ctx, gapCount, i, c_l, c_r);
408 c_l = c_r;
409 }
410 add_const_color(ctx, stopCount - 1, c_l);
411
412 ctx->stopCount = stopCount;
413 p->append(SkRasterPipelineOp::evenly_spaced_gradient, ctx);
414 } else {
415 // Handle arbitrary stops.
416
417 ctx->ts = alloc->makeArray<float>(count + 1);
418
419 // Remove the default stops inserted by SkGradientBaseShader::SkGradientBaseShader
420 // because they are naturally handled by the search method.
421 int firstStop;
422 int lastStop;
423 if (count > 2) {
424 firstStop = pmColors[0] != pmColors[1] ? 0 : 1;
425 lastStop = pmColors[count - 2] != pmColors[count - 1] ? count - 1 : count - 2;
426 } else {
427 firstStop = 0;
428 lastStop = 1;
429 }
430
431 size_t stopCount = 0;
432 float t_l = positions[firstStop];
433 SkPMColor4f c_l = pmColors[firstStop];
434 add_const_color(ctx, stopCount++, c_l);
435 // N.B. lastStop is the index of the last stop, not one after.
436 for (int i = firstStop; i < lastStop; i++) {
437 float t_r = positions[i + 1];
438 SkPMColor4f c_r = pmColors[i + 1];
439 SkASSERT(t_l <= t_r);
440 if (t_l < t_r) {
441 float c_scale = sk_ieee_float_divide(1, t_r - t_l);
442 if (SkIsFinite(c_scale)) {
443 init_stop_pos(ctx, stopCount, t_l, c_scale, c_l, c_r);
444 stopCount += 1;
445 }
446 }
447 t_l = t_r;
448 c_l = c_r;
449 }
450
451 ctx->ts[stopCount] = t_l;
452 add_const_color(ctx, stopCount++, c_l);
453
454 ctx->stopCount = stopCount;
455 p->append(SkRasterPipelineOp::gradient, ctx);
456 }
457 }
458}
459
461 SkArenaAlloc* alloc,
462 bool colorsAreOpaque,
463 const Interpolation& interpolation,
464 const SkColorSpace* intermediateColorSpace,
465 const SkColorSpace* dstColorSpace) {
467 bool colorIsPremul = static_cast<bool>(interpolation.fInPremul);
468
469 // If we interpolated premul colors in any of the special color spaces, we need to unpremul
470 if (colorIsPremul && !colorsAreOpaque) {
471 switch (interpolation.fColorSpace) {
472 case ColorSpace::kLab:
473 case ColorSpace::kOKLab:
474 case ColorSpace::kOKLabGamutMap:
475 p->append(SkRasterPipelineOp::unpremul);
476 colorIsPremul = false;
477 break;
478 case ColorSpace::kLCH:
479 case ColorSpace::kOKLCH:
480 case ColorSpace::kOKLCHGamutMap:
481 case ColorSpace::kHSL:
482 case ColorSpace::kHWB:
483 p->append(SkRasterPipelineOp::unpremul_polar);
484 colorIsPremul = false;
485 break;
486 default:
487 break;
488 }
489 }
490
491 // Convert colors in exotic spaces back to their intermediate SkColorSpace
492 switch (interpolation.fColorSpace) {
493 case ColorSpace::kLab: p->append(SkRasterPipelineOp::css_lab_to_xyz); break;
494 case ColorSpace::kOKLab: p->append(SkRasterPipelineOp::css_oklab_to_linear_srgb); break;
495 case ColorSpace::kOKLabGamutMap:
496 p->append(SkRasterPipelineOp::css_oklab_gamut_map_to_linear_srgb);
497 break;
498 case ColorSpace::kLCH: p->append(SkRasterPipelineOp::css_hcl_to_lab);
499 p->append(SkRasterPipelineOp::css_lab_to_xyz); break;
500 case ColorSpace::kOKLCH: p->append(SkRasterPipelineOp::css_hcl_to_lab);
501 p->append(SkRasterPipelineOp::css_oklab_to_linear_srgb); break;
502 case ColorSpace::kOKLCHGamutMap:
503 p->append(SkRasterPipelineOp::css_hcl_to_lab);
504 p->append(SkRasterPipelineOp::css_oklab_gamut_map_to_linear_srgb);
505 break;
506 case ColorSpace::kHSL: p->append(SkRasterPipelineOp::css_hsl_to_srgb); break;
507 case ColorSpace::kHWB: p->append(SkRasterPipelineOp::css_hwb_to_srgb); break;
508 default: break;
509 }
510
511 // Now transform from intermediate to destination color space.
512 // See comments in GrGradientShader.cpp about the decisions here.
513 if (!dstColorSpace) {
514 dstColorSpace = sk_srgb_singleton();
515 }
516 SkAlphaType intermediateAlphaType = colorIsPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
517 // TODO(skia:13108): Get dst alpha type correctly
518 SkAlphaType dstAlphaType = kPremul_SkAlphaType;
519
520 if (colorsAreOpaque) {
521 intermediateAlphaType = dstAlphaType = kUnpremul_SkAlphaType;
522 }
523
525 intermediateColorSpace, intermediateAlphaType, dstColorSpace, dstAlphaType)
526 ->apply(p);
527}
528
530 const SkShaders::MatrixRec& mRec) const {
532 SkArenaAlloc* alloc = rec.fAlloc;
533 SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
534
535 std::optional<SkShaders::MatrixRec> newMRec = mRec.apply(rec, fPtsToUnit);
536 if (!newMRec.has_value()) {
537 return false;
538 }
539
540 SkRasterPipeline_<256> postPipeline;
541
542 this->appendGradientStages(alloc, p, &postPipeline);
543
544 switch (fTileMode) {
546 p->append(SkRasterPipelineOp::mirror_x_1);
547 break;
549 p->append(SkRasterPipelineOp::repeat_x_1);
550 break;
552 decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
553 decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1);
554 // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask
555 p->append(SkRasterPipelineOp::decal_x, decal_ctx);
556 [[fallthrough]];
557
559 if (!fPositions) {
560 // We clamp only when the stops are evenly spaced.
561 // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1.
562 // In that case, we must make sure we're using the general "gradient" stage,
563 // which is the only stage that will correctly handle unclamped t.
564 p->append(SkRasterPipelineOp::clamp_x_1);
565 }
566 break;
567 }
568
569 // Transform all of the colors to destination color space, possibly premultiplied
570 SkColor4fXformer xformedColors(this, rec.fDstCS);
572 xformedColors.fColors.begin(),
573 xformedColors.fPositions,
574 xformedColors.fColors.size());
575 AppendInterpolatedToDstStages(p, alloc, fColorsAreOpaque, fInterpolation,
576 xformedColors.fIntermediateColorSpace.get(), rec.fDstCS);
577
578 if (decal_ctx) {
579 p->append(SkRasterPipelineOp::check_decal_mask, decal_ctx);
580 }
581
582 p->extend(postPipeline);
583
584 return true;
585}
586
588 return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal);
589}
590
592 // We just compute an average color. There are several things we could do better:
593 // 1) We already have a different average_gradient_color helper later in this file, that weights
594 // contribution by the relative size of each band.
595 // 2) Colors should be converted to some standard color space! These could be in any space.
596 // 3) Do we want to average in the source space, sRGB, or some linear space?
597 SkColor4f color{0, 0, 0, 1};
598 for (int i = 0; i < fColorCount; ++i) {
599 color.fR += fColors[i].fR;
600 color.fG += fColors[i].fG;
601 color.fB += fColors[i].fB;
602 }
603 const float scale = 1.0f / fColorCount;
604 color.fR *= scale;
605 color.fG *= scale;
606 color.fB *= scale;
607 *lum = color;
608 return true;
609}
610
612 SkColorSpace* dst) {
614 switch (cs) {
615 case ColorSpace::kDestination:
616 return sk_ref_sp(dst);
617
618 // css-color-4 allows XYZD50 and XYZD65. For gradients, those are redundant. Interpolating
619 // in any linear RGB space, (regardless of white point), gives the same answer.
620 case ColorSpace::kSRGBLinear:
622
623 case ColorSpace::kSRGB:
624 case ColorSpace::kHSL:
625 case ColorSpace::kHWB:
626 return SkColorSpace::MakeSRGB();
627
628 case ColorSpace::kLab:
629 case ColorSpace::kLCH:
630 // Conversion to Lab (and LCH) starts with XYZD50
632
633 case ColorSpace::kOKLab:
634 case ColorSpace::kOKLabGamutMap:
635 case ColorSpace::kOKLCH:
636 case ColorSpace::kOKLCHGamutMap:
637 // The "standard" conversion to these spaces starts with XYZD65. That requires extra
638 // effort to conjure. The author also has reference code for going directly from linear
639 // sRGB, so we use that.
640 // TODO(skia:13108): Even better would be to have an LMS color space, because the first
641 // part of the conversion is a matrix multiply, which could be absorbed into the
642 // color space xform.
644 }
646}
647
650
651static SkPMColor4f srgb_to_hsl(SkPMColor4f rgb, bool* hueIsPowerless) {
652 float mx = std::max({rgb.fR, rgb.fG, rgb.fB});
653 float mn = std::min({rgb.fR, rgb.fG, rgb.fB});
654 float hue = 0, sat = 0, light = (mn + mx) / 2;
655 float d = mx - mn;
656
657 if (d != 0) {
658 sat = (light == 0 || light == 1) ? 0 : (mx - light) / std::min(light, 1 - light);
659 if (mx == rgb.fR) {
660 hue = (rgb.fG - rgb.fB) / d + (rgb.fG < rgb.fB ? 6 : 0);
661 } else if (mx == rgb.fG) {
662 hue = (rgb.fB - rgb.fR) / d + 2;
663 } else {
664 hue = (rgb.fR - rgb.fG) / d + 4;
665 }
666
667 hue *= 60;
668 }
669 if (sat == 0) {
670 *hueIsPowerless = true;
671 }
672 return {hue, sat * 100, light * 100, rgb.fA};
673}
674
675static SkPMColor4f srgb_to_hwb(SkPMColor4f rgb, bool* hueIsPowerless) {
676 SkPMColor4f hsl = srgb_to_hsl(rgb, hueIsPowerless);
677 float white = std::min({rgb.fR, rgb.fG, rgb.fB});
678 float black = 1 - std::max({rgb.fR, rgb.fG, rgb.fB});
679 return {hsl.fR, white * 100, black * 100, rgb.fA};
680}
681
682static SkPMColor4f xyzd50_to_lab(SkPMColor4f xyz, bool* /*hueIsPowerless*/) {
683 constexpr float D50[3] = {0.3457f / 0.3585f, 1.0f, (1.0f - 0.3457f - 0.3585f) / 0.3585f};
684
685 constexpr float e = 216.0f / 24389;
686 constexpr float k = 24389.0f / 27;
687
689 for (int i = 0; i < 3; ++i) {
690 float v = xyz[i] / D50[i];
691 f[i] = (v > e) ? std::cbrtf(v) : (k * v + 16) / 116;
692 }
693
694 return {(116 * f[1]) - 16, 500 * (f[0] - f[1]), 200 * (f[1] - f[2]), xyz.fA};
695}
696
697// The color space is technically LCH, but we produce HCL, so that all polar spaces have hue in the
698// first component. This simplifies the hue handling for HueMethod and premul/unpremul.
699static SkPMColor4f xyzd50_to_hcl(SkPMColor4f xyz, bool* hueIsPowerless) {
700 SkPMColor4f Lab = xyzd50_to_lab(xyz, hueIsPowerless);
701 float hue = sk_float_radians_to_degrees(atan2f(Lab[2], Lab[1]));
702 float chroma = sqrtf(Lab[1] * Lab[1] + Lab[2] * Lab[2]);
703 // The LCH math produces small-ish (but not tiny) chroma values for achromatic colors:
704 constexpr float kMaxChromaForPowerlessHue = 1e-2f;
705 if (chroma <= kMaxChromaForPowerlessHue) {
706 *hueIsPowerless = true;
707 }
708 return {hue >= 0 ? hue : hue + 360, chroma, Lab[0], xyz.fA};
709}
710
711// https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab
712static SkPMColor4f lin_srgb_to_oklab(SkPMColor4f rgb, bool* /*hueIsPowerless*/) {
713 float l = 0.4122214708f * rgb.fR + 0.5363325363f * rgb.fG + 0.0514459929f * rgb.fB;
714 float m = 0.2119034982f * rgb.fR + 0.6806995451f * rgb.fG + 0.1073969566f * rgb.fB;
715 float s = 0.0883024619f * rgb.fR + 0.2817188376f * rgb.fG + 0.6299787005f * rgb.fB;
716 l = std::cbrtf(l);
717 m = std::cbrtf(m);
718 s = std::cbrtf(s);
719 return {0.2104542553f * l + 0.7936177850f * m - 0.0040720468f * s,
720 1.9779984951f * l - 2.4285922050f * m + 0.4505937099f * s,
721 0.0259040371f * l + 0.7827717662f * m - 0.8086757660f * s,
722 rgb.fA};
723}
724
725// The color space is technically OkLCH, but we produce HCL, so that all polar spaces have hue in
726// the first component. This simplifies the hue handling for HueMethod and premul/unpremul.
727static SkPMColor4f lin_srgb_to_okhcl(SkPMColor4f rgb, bool* hueIsPowerless) {
728 SkPMColor4f OKLab = lin_srgb_to_oklab(rgb, hueIsPowerless);
729 float hue = sk_float_radians_to_degrees(atan2f(OKLab[2], OKLab[1]));
730 float chroma = sqrtf(OKLab[1] * OKLab[1] + OKLab[2] * OKLab[2]);
731 // The OKLCH math produces very small chroma values for achromatic colors:
732 constexpr float kMaxChromaForPowerlessHue = 1e-6f;
733 if (chroma <= kMaxChromaForPowerlessHue) {
734 *hueIsPowerless = true;
735 }
736 return {hue >= 0 ? hue : hue + 360, chroma, OKLab[0], rgb.fA};
737}
738
740 return {hsl.fR, hsl.fG * hsl.fA, hsl.fB * hsl.fA, hsl.fA};
741}
742
744 return {rgb.fR * rgb.fA, rgb.fG * rgb.fA, rgb.fB * rgb.fA, rgb.fA};
745}
746
749 switch (cs) {
750 case ColorSpace::kLCH:
751 case ColorSpace::kOKLCH:
752 case ColorSpace::kHSL:
753 case ColorSpace::kHWB:
754 return true;
755 default:
756 return false;
757 }
758}
759
760// Given `colors` in `src` color space, an interpolation space, and a `dst` color space,
761// we are doing several things. First, some definitions:
762//
763// The interpolation color space is "special" if it can't be represented as an SkColorSpace. This
764// applies to any color space that isn't an RGB space, like Lab or HSL. These need special handling
765// because we have to run bespoke code to do the conversion (before interpolation here, and after
766// interpolation in the backend shader/pipeline).
767//
768// The interpolation color space is "polar" if it involves hue (HSL, HWB, LCH, Oklch). These need
769// special handling, becuase hue is never premultiplied, and because HueMethod comes into play.
770//
771// 1) Pick an `intermediate` SkColorSpace. If the interpolation color space is not "special",
772// (kDestination, kSRGB, etc... ), then `intermediate` is exact. Otherwise, `intermediate` is the
773// RGB space that prepares us to do the final conversion. For example, conversion to Lab starts
774// with XYZD50, so `intermediate` will be XYZD50 if we're actually interpolating in Lab.
775// 2) Transform all colors to the `intermediate` color space, leaving them unpremultiplied.
776// 3) If the interpolation color space is "special", transform the colors to that space.
777// 4) If the interpolation color space is "polar", adjust the angles to respect HueMethod.
778// 5) If premul interpolation is requested, apply that. For "polar" interpolated colors, don't
779// premultiply hue, only the other two channels. Note that there are four polar spaces.
780// Two have hue as the first component, and two have it as the third component. To reduce
781// complexity, we always store hue in the first component, swapping it with luminance for
782// LCH and Oklch. The backend code (eg, shaders) needs to know about this.
785 bool forceExplicitPositions) {
788
789 int colorCount = shader->fColorCount;
790 const SkGradientShader::Interpolation interpolation = shader->fInterpolation;
791
792 // 0) Copy the shader's position pointer. Certain interpolation modes might force us to add
793 // new stops, in which case we'll allocate & edit the positions.
794 fPositions = shader->fPositions;
795
796 // 1) Determine the color space of our intermediate colors.
798
799 // 2) Convert all colors to the intermediate color space
801
802 auto dstInfo = info.makeColorSpace(fIntermediateColorSpace);
803 auto srcInfo = info.makeColorSpace(shader->fColorSpace);
804
805 fColors.reset(colorCount);
807 fColors.begin(),
808 info.minRowBytes(),
809 srcInfo,
810 shader->fColors,
811 info.minRowBytes()));
812
813 // 3) Transform to the interpolation color space (if it's special)
814 ConvertColorProc convertFn = nullptr;
815 switch (interpolation.fColorSpace) {
816 case ColorSpace::kHSL: convertFn = srgb_to_hsl; break;
817 case ColorSpace::kHWB: convertFn = srgb_to_hwb; break;
818 case ColorSpace::kLab: convertFn = xyzd50_to_lab; break;
819 case ColorSpace::kLCH: convertFn = xyzd50_to_hcl; break;
820 case ColorSpace::kOKLab: convertFn = lin_srgb_to_oklab; break;
821 case ColorSpace::kOKLabGamutMap: convertFn = lin_srgb_to_oklab; break;
822 case ColorSpace::kOKLCH: convertFn = lin_srgb_to_okhcl; break;
823 case ColorSpace::kOKLCHGamutMap: convertFn = lin_srgb_to_okhcl; break;
824 default: break;
825 }
826
827 skia_private::STArray<4, bool> hueIsPowerless;
828 bool anyPowerlessHue = false;
829 hueIsPowerless.push_back_n(colorCount, false);
830 if (convertFn) {
831 for (int i = 0; i < colorCount; ++i) {
832 fColors[i] = convertFn(fColors[i], hueIsPowerless.data() + i);
833 anyPowerlessHue = anyPowerlessHue || hueIsPowerless[i];
834 }
835 }
836
837 if (anyPowerlessHue) {
838 // In theory, if we knew we were just going to adjust the existing colors (without adding
839 // new ones), we could do it all in-place. To keep things simple, we always generate the
840 // new colors in separate storage.
841 ColorStorage newColors;
842 PositionStorage newPositions;
843
844 for (int i = 0; i < colorCount; ++i) {
845 const SkPMColor4f& curColor = fColors[i];
846 float curPos = shader->getPos(i);
847
848 if (!hueIsPowerless[i]) {
849 newColors.push_back(curColor);
850 newPositions.push_back(curPos);
851 continue;
852 }
853
854 auto colorWithHueFrom = [](const SkPMColor4f& color, const SkPMColor4f& hueColor) {
855 // If we have any powerless hue, then all colors are already in (some) polar space,
856 // and they all store their hue in the red channel.
857 return SkPMColor4f{hueColor.fR, color.fG, color.fB, color.fA};
858 };
859
860 // In each case, we might be copying a powerless (invalid) hue from the neighbor, but
861 // that should be fine, as it will match that neighbor perfectly, and any hue is ok.
862 if (i != 0) {
863 newPositions.push_back(curPos);
864 newColors.push_back(colorWithHueFrom(curColor, fColors[i - 1]));
865 }
866 if (i != colorCount - 1) {
867 newPositions.push_back(curPos);
868 newColors.push_back(colorWithHueFrom(curColor, fColors[i + 1]));
869 }
870 }
871
872 fColors.swap(newColors);
873 fPositionStorage.swap(newPositions);
875 colorCount = fColors.size();
876 }
877
878 // 4) For polar colors, adjust hue values to respect the hue method. We're using a trick here...
879 // The specification looks at adjacent colors, and adjusts one or the other. Because we store
880 // the stops in uniforms (and our backend conversions normalize the hue angle), we can
881 // instead always apply the adjustment to the *second* color. That lets us keep a running
882 // total, and do a single pass across all the colors to respect the requested hue method,
883 // without needing to do any extra work per-pixel.
884 if (color_space_is_polar(interpolation.fColorSpace)) {
885 float delta = 0;
886 for (int i = 0; i < colorCount - 1; ++i) {
887 float h1 = fColors[i].fR;
888 float& h2 = fColors[i + 1].fR;
889 h2 += delta;
890 switch (interpolation.fHueMethod) {
891 case HueMethod::kShorter:
892 if (h2 - h1 > 180) {
893 h2 -= 360; // i.e. h1 += 360
894 delta -= 360;
895 } else if (h2 - h1 < -180) {
896 h2 += 360;
897 delta += 360;
898 }
899 break;
900 case HueMethod::kLonger:
901 if ((i == 0 && shader->fFirstStopIsImplicit) ||
902 (i == colorCount - 2 && shader->fLastStopIsImplicit)) {
903 // Do nothing. We don't want to introduce a full revolution for these stops
904 // Full rationale at skbug.com/13941
905 } else if (0 < h2 - h1 && h2 - h1 < 180) {
906 h2 -= 360; // i.e. h1 += 360
907 delta -= 360;
908 } else if (-180 < h2 - h1 && h2 - h1 <= 0) {
909 h2 += 360;
910 delta += 360;
911 }
912 break;
913 case HueMethod::kIncreasing:
914 if (h2 < h1) {
915 h2 += 360;
916 delta += 360;
917 }
918 break;
919 case HueMethod::kDecreasing:
920 if (h1 < h2) {
921 h2 -= 360; // i.e. h1 += 360;
922 delta -= 360;
923 }
924 break;
925 }
926 }
927 }
928
929 // 5) Apply premultiplication
930 PremulColorProc premulFn = nullptr;
931 if (static_cast<bool>(interpolation.fInPremul)) {
932 switch (interpolation.fColorSpace) {
933 case ColorSpace::kHSL:
934 case ColorSpace::kHWB:
935 case ColorSpace::kLCH:
936 case ColorSpace::kOKLCH:
937 premulFn = premul_polar;
938 break;
939 default:
940 premulFn = premul_rgb;
941 break;
942 }
943 }
944
945 if (premulFn) {
946 for (int i = 0; i < colorCount; ++i) {
947 fColors[i] = premulFn(fColors[i]);
948 }
949 }
950
951 // Ganesh requires that the positions be explicit (rather than implicitly evenly spaced)
952 if (forceExplicitPositions && !fPositions) {
954 float posScale = 1.0f / (colorCount - 1);
955 for (int i = 0; i < colorCount; i++) {
956 fPositionStorage.push_back(i * posScale);
957 }
959 }
960}
961
963 const float ONE_OVER_255 = 1.f / 255;
964 for (int i = 0; i < count; ++i) {
965 fColors4f.push_back({SkColorGetR(colors[i]) * ONE_OVER_255,
966 SkColorGetG(colors[i]) * ONE_OVER_255,
967 SkColorGetB(colors[i]) * ONE_OVER_255,
968 SkColorGetA(colors[i]) * ONE_OVER_255});
969 }
970}
971
973 if (info) {
974 if (info->fColorCount >= fColorCount) {
975 if (info->fColors) {
976 for (int i = 0; i < fColorCount; ++i) {
977 info->fColors[i] = this->getLegacyColor(i);
978 }
979 }
980 if (info->fColorOffsets) {
981 for (int i = 0; i < fColorCount; ++i) {
982 info->fColorOffsets[i] = this->getPos(i);
983 }
984 }
985 }
986 info->fColorCount = fColorCount;
987 info->fTileMode = fTileMode;
988
989 info->fGradientFlags =
991 }
992}
993
994// Return true if these parameters are valid/legal/safe to construct a gradient
995//
997 int count,
998 SkTileMode tileMode,
999 const Interpolation& interpolation) {
1000 return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount &&
1001 (unsigned)interpolation.fColorSpace < Interpolation::kColorSpaceCount &&
1002 (unsigned)interpolation.fHueMethod < Interpolation::kHueMethodCount;
1003}
1004
1006 sk_sp<SkColorSpace> colorSpace,
1007 const SkScalar positions[],
1008 int colorCount,
1010 const Interpolation& interpolation)
1011 : fColors(colors)
1012 , fColorSpace(std::move(colorSpace))
1013 , fPositions(positions)
1014 , fColorCount(colorCount)
1015 , fTileMode(mode)
1016 , fInterpolation(interpolation) {
1017 SkASSERT(fColorCount > 1);
1018}
1019
1021 const SkScalar pos[],
1022 int colorCount) {
1023 // The gradient is a piecewise linear interpolation between colors. For a given interval,
1024 // the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that
1025 // intervals average color. The overall average color is thus the sum of each piece. The thing
1026 // to keep in mind is that the provided gradient definition may implicitly use p=0 and p=1.
1027 skvx::float4 blend(0.0f);
1028 for (int i = 0; i < colorCount - 1; ++i) {
1029 // Calculate the average color for the interval between pos(i) and pos(i+1)
1030 auto c0 = skvx::float4::Load(&colors[i]);
1031 auto c1 = skvx::float4::Load(&colors[i + 1]);
1032
1033 // when pos == null, there are colorCount uniformly distributed stops, going from 0 to 1,
1034 // so pos[i + 1] - pos[i] = 1/(colorCount-1)
1035 SkScalar w;
1036 if (pos) {
1037 // Match position fixing in SkGradientShader's constructor, clamping positions outside
1038 // [0, 1] and forcing the sequence to be monotonic
1039 SkScalar p0 = SkTPin(pos[i], 0.f, 1.f);
1040 SkScalar p1 = SkTPin(pos[i + 1], p0, 1.f);
1041 w = p1 - p0;
1042
1043 // And account for any implicit intervals at the start or end of the positions
1044 if (i == 0) {
1045 if (p0 > 0.0f) {
1046 // The first color is fixed between p = 0 to pos[0], so 0.5*(ci + cj)*(pj - pi)
1047 // becomes 0.5*(c + c)*(pj - 0) = c * pj
1048 auto c = skvx::float4::Load(&colors[0]);
1049 blend += p0 * c;
1050 }
1051 }
1052 if (i == colorCount - 2) {
1053 if (p1 < 1.f) {
1054 // The last color is fixed between pos[n-1] to p = 1, so 0.5*(ci + cj)*(pj - pi)
1055 // becomes 0.5*(c + c)*(1 - pi) = c * (1 - pi)
1056 auto c = skvx::float4::Load(&colors[colorCount - 1]);
1057 blend += (1.f - p1) * c;
1058 }
1059 }
1060 } else {
1061 w = 1.f / (colorCount - 1);
1062 }
1063
1064 blend += 0.5f * w * (c1 + c0);
1065 }
1066
1067 SkColor4f avg;
1068 blend.store(&avg);
1069 return avg;
1070}
1071
1072// Except for special circumstances of clamped gradients, every gradient shape--when degenerate--
1073// can be mapped to the same fallbacks. The specific shape factories must account for special
1074// clamped conditions separately, this will always return the last color for clamped gradients.
1076 const SkScalar pos[],
1077 int colorCount,
1078 sk_sp<SkColorSpace> colorSpace,
1079 SkTileMode mode) {
1080 switch (mode) {
1081 case SkTileMode::kDecal:
1082 // normally this would reject the area outside of the interpolation region, so since
1083 // inside region is empty when the radii are equal, the entire draw region is empty
1084 return SkShaders::Empty();
1087 // repeat and mirror are treated the same: the border colors are never visible,
1088 // but approximate the final color as infinite repetitions of the colors, so
1089 // it can be represented as the average color of the gradient.
1091 std::move(colorSpace));
1092 case SkTileMode::kClamp:
1093 // Depending on how the gradient shape degenerates, there may be a more specialized
1094 // fallback representation for the factories to use, but this is a reasonable default.
1095 return SkShaders::Color(colors[colorCount - 1], std::move(colorSpace));
1096 }
1097 SkDEBUGFAIL("Should not be reached");
1098 return nullptr;
1099}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
float c_scale
sk_bzero(glyphs, sizeof(glyphs))
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
int count
Definition: FontMgrTest.cpp:50
SkPoint pos
static float prev(float f)
kUnpremul_SkAlphaType
SkAlphaType
Definition: SkAlphaType.h:26
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkRGBA4f< kPremul_SkAlphaType > SkPMColor4f
Definition: SkColorData.h:376
SkColorSpace * sk_srgb_singleton()
@ kRGBA_F32_SkColorType
pixel using C float for red, green, blue, alpha; in 128-bit word
Definition: SkColorType.h:40
#define SkColorGetR(color)
Definition: SkColor.h:65
SkRGBA4f< kUnpremul_SkAlphaType > SkColor4f
Definition: SkColor.h:426
#define SkColorGetG(color)
Definition: SkColor.h:69
uint32_t SkColor
Definition: SkColor.h:37
#define SkColorGetA(color)
Definition: SkColor.h:61
#define SkColorGetB(color)
Definition: SkColor.h:73
bool SkConvertPixels(const SkImageInfo &dstInfo, void *dstPixels, size_t dstRB, const SkImageInfo &srcInfo, const void *srcPixels, size_t srcRB)
static float SkBits2Float(uint32_t bits)
Definition: SkFloatBits.h:48
static uint32_t SkFloat2Bits(float value)
Definition: SkFloatBits.h:41
static bool SkIsFinite(T x, Pack... values)
static constexpr float sk_ieee_float_divide(float numer, float denom)
static constexpr float sk_float_radians_to_degrees(float radians)
static void add_const_color(SkRasterPipeline_GradientCtx *ctx, size_t stop, SkPMColor4f color)
static void init_stop_evenly(SkRasterPipeline_GradientCtx *ctx, float gapCount, size_t stop, SkPMColor4f c_l, SkPMColor4f c_r)
static void add_stop_color(SkRasterPipeline_GradientCtx *ctx, size_t stop, SkPMColor4f Fs, SkPMColor4f Bs)
static SkPMColor4f srgb_to_hwb(SkPMColor4f rgb, bool *hueIsPowerless)
static SkPMColor4f lin_srgb_to_okhcl(SkPMColor4f rgb, bool *hueIsPowerless)
static SkPMColor4f xyzd50_to_lab(SkPMColor4f xyz, bool *)
SkPMColor4f(*)(SkPMColor4f, bool *) ConvertColorProc
static SkPMColor4f srgb_to_hsl(SkPMColor4f rgb, bool *hueIsPowerless)
static bool color_space_is_polar(SkGradientShader::Interpolation::ColorSpace cs)
GradientSerializationFlags
@ kHasColorSpace_GSF
@ kTileModeShift_GSF
@ kInterpolationColorSpaceMask_GSF
@ kHasLegacyLocalMatrix_GSF
@ kInterpolationColorSpaceShift_GSF
@ kInterpolationHueMethodShift_GSF
@ kHasPosition_GSF
@ kInterpolationInPremul_GSF
@ kInterpolationHueMethodMask_GSF
@ kTileModeMask_GSF
SkPMColor4f(*)(SkPMColor4f) PremulColorProc
static bool validate_array(SkReadBuffer &buffer, size_t count, STArray< N, T, MEM_MOVE > *array)
static SkColor4f average_gradient_color(const SkColor4f colors[], const SkScalar pos[], int colorCount)
static SkPMColor4f xyzd50_to_hcl(SkPMColor4f xyz, bool *hueIsPowerless)
static void init_stop_pos(SkRasterPipeline_GradientCtx *ctx, size_t stop, float t_l, float c_scale, SkPMColor4f c_l, SkPMColor4f c_r)
static SkPMColor4f premul_polar(SkPMColor4f hsl)
static SkPMColor4f premul_rgb(SkPMColor4f rgb)
static SkPMColor4f lin_srgb_to_oklab(SkPMColor4f rgb, bool *)
static sk_sp< SkColorSpace > intermediate_color_space(SkGradientShader::Interpolation::ColorSpace cs, SkColorSpace *dst)
static bool apply(Pass *pass, SkRecord *record)
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:107
#define SK_Scalar1
Definition: SkScalar.h:18
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
SkTileMode
Definition: SkTileMode.h:13
static constexpr int kSkTileModeCount
Definition: SkTileMode.h:39
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
SI void store(P *ptr, const T &val)
T * makeArray(size_t count)
Definition: SkArenaAlloc.h:181
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
Definition: SkArenaAlloc.h:120
static sk_sp< SkColorSpace > MakeSRGB()
static sk_sp< SkColorSpace > Deserialize(const void *data, size_t length)
static sk_sp< SkColorSpace > MakeRGB(const skcms_TransferFunction &transferFn, const skcms_Matrix3x3 &toXYZ)
sk_sp< SkData > serialize() const
static sk_sp< SkColorSpace > MakeSRGBLinear()
bool unflatten(SkReadBuffer &, SkMatrix *legacyLocalMatrix)
static bool ValidGradient(const SkColor4f colors[], int count, SkTileMode tileMode, const Interpolation &interpolation)
SkColor getLegacyColor(int i) const
bool isOpaque() const override
SkGradientBaseShader(const Descriptor &desc, const SkMatrix &ptsToUnit)
virtual void appendGradientStages(SkArenaAlloc *alloc, SkRasterPipeline *tPipeline, SkRasterPipeline *postPipeline) const =0
bool onAsLuminanceColor(SkColor4f *) const override
SkScalar getPos(int i) const
static sk_sp< SkShader > MakeDegenerateGradient(const SkColor4f colors[], const SkScalar pos[], int colorCount, sk_sp< SkColorSpace > colorSpace, SkTileMode mode)
void flatten(SkWriteBuffer &) const override
bool appendStages(const SkStageRec &, const SkShaders::MatrixRec &) const override
void commonAsAGradient(GradientInfo *) const
sk_sp< SkColorSpace > fColorSpace
bool interpolateInPremul() const
static void AppendGradientFillStages(SkRasterPipeline *p, SkArenaAlloc *alloc, const SkPMColor4f *colors, const SkScalar *positions, int count)
static void AppendInterpolatedToDstStages(SkRasterPipeline *p, SkArenaAlloc *alloc, bool colorsAreOpaque, const Interpolation &interpolation, const SkColorSpace *intermediateColorSpace, const SkColorSpace *dstColorSpace)
SkTileMode getTileMode() const
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
TypeMask getType() const
Definition: SkMatrix.h:207
std::optional< MatrixRec > apply(const SkStageRec &rec, const SkMatrix &postInv={}) const
T * get() const
Definition: SkRefCnt.h:303
T * reset(size_t count)
Definition: SkTemplates.h:356
T * push_back_n(int n)
Definition: SkTArray.h:267
void resize_back(int newCount)
Definition: SkTArray.h:343
void reset(int n)
Definition: SkTArray.h:144
int size() const
Definition: SkTArray.h:421
void swap(TArray &that)
Definition: SkTArray.h:358
void reserve_exact(int n)
Definition: SkTArray.h:181
DlColor color
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
struct MyStruct s
FlutterSemanticsFlag flags
static SkColor blend(SkColor dst, SkColor src, void(*mode)(float, float, float, float *, float *, float *))
Definition: hsl.cpp:142
static float sat(float r, float g, float b)
Definition: hsl.cpp:51
static void hue(float dr, float dg, float db, float *sr, float *sg, float *sb)
Definition: hsl.cpp:92
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float lum(float r, float g, float b)
Definition: hsl.cpp:52
static float min(float r, float g, float b)
Definition: hsl.cpp:48
static constexpr skcms_Matrix3x3 kXYZ
Definition: SkColorSpace.h:99
static constexpr skcms_TransferFunction kLinear
Definition: SkColorSpace.h:51
PODArray< SkColor > colors
Definition: SkRecords.h:276
SK_API sk_sp< SkShader > Color(SkColor)
SK_API sk_sp< SkShader > Empty()
ColorSpace
Definition: image.h:16
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
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 mode
Definition: switches.h:228
dst
Definition: cp.py:12
Definition: ref_ptr.h:256
SkScalar w
#define T
Definition: precompiler.cc:65
const Scalar scale
SkColor4fXformer(const SkGradientBaseShader *shader, SkColorSpace *dst, bool forceExplicitPositions=false)
PositionStorage fPositionStorage
sk_sp< SkColorSpace > fIntermediateColorSpace
SkColorConverter(const SkColor *colors, int count)
skia_private::STArray< 2, SkColor4f > fColors4f
static constexpr int kColorSpaceCount
static constexpr int kHueMethodCount
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
const float * vec() const
Definition: SkColor.h:308
float fB
blue component
Definition: SkColor.h:265
float fR
red component
Definition: SkColor.h:263
float fG
green component
Definition: SkColor.h:264
float fA
alpha component
Definition: SkColor.h:266
int fColorCount
In-out parameter, specifies passed size.
Definition: SkShaderBase.h:243
SkScalar * fColorOffsets
The unit offset for color transitions.
Definition: SkShaderBase.h:248
SkRasterPipeline * fPipeline
Definition: SkEffectPriv.h:21
SkColorSpace * fDstCS
Definition: SkEffectPriv.h:24
SkArenaAlloc * fAlloc
Definition: SkEffectPriv.h:22
Definition: SkVx.h:83
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition: SkVx.h:109
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63