xref: /aosp_15_r20/external/skia/src/sksl/sksl_shared.sksl (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1// Intrinsics that are available to public SkSL (SkRuntimeEffect)
2
3// See "The OpenGL ES Shading Language, Section 8"
4
5// 8.1 : Angle and Trigonometry Functions
6$pure $genType  radians($genType  degrees);
7$pure $genHType radians($genHType degrees);
8$pure $genType  degrees($genType  radians);
9$pure $genHType degrees($genHType radians);
10
11$pure $genType  sin($genType  angle);
12$pure $genHType sin($genHType angle);
13$pure $genType  cos($genType  angle);
14$pure $genHType cos($genHType angle);
15$pure $genType  tan($genType  angle);
16$pure $genHType tan($genHType angle);
17
18$pure $genType  asin($genType  x);
19$pure $genHType asin($genHType x);
20$pure $genType  acos($genType  x);
21$pure $genHType acos($genHType x);
22$pure $genType  atan($genType  y, $genType  x);
23$pure $genHType atan($genHType y, $genHType x);
24$pure $genType  atan($genType  y_over_x);
25$pure $genHType atan($genHType y_over_x);
26
27// 8.1 : Angle and Trigonometry Functions (GLSL ES 3.0)
28$pure $es3 $genType  sinh($genType x);
29$pure $es3 $genHType sinh($genHType x);
30$pure $es3 $genType  cosh($genType x);
31$pure $es3 $genHType cosh($genHType x);
32$pure $es3 $genType  tanh($genType x);
33$pure $es3 $genHType tanh($genHType x);
34$pure $es3 $genType  asinh($genType x);
35$pure $es3 $genHType asinh($genHType x);
36$pure $es3 $genType  acosh($genType x);
37$pure $es3 $genHType acosh($genHType x);
38$pure $es3 $genType  atanh($genType x);
39$pure $es3 $genHType atanh($genHType x);
40
41// 8.2 : Exponential Functions
42$pure $genType  pow($genType  x, $genType  y);
43$pure $genHType pow($genHType x, $genHType y);
44$pure $genType  exp($genType  x);
45$pure $genHType exp($genHType x);
46$pure $genType  log($genType  x);
47$pure $genHType log($genHType x);
48$pure $genType  exp2($genType  x);
49$pure $genHType exp2($genHType x);
50$pure $genType  log2($genType  x);
51$pure $genHType log2($genHType x);
52
53$pure $genType  sqrt($genType  x);
54$pure $genHType sqrt($genHType x);
55$pure $genType  inversesqrt($genType  x);
56$pure $genHType inversesqrt($genHType x);
57
58// 8.3 : Common Functions
59$pure $genType  abs($genType  x);
60$pure $genHType abs($genHType x);
61$pure $genType  sign($genType  x);
62$pure $genHType sign($genHType x);
63$pure $genType  floor($genType  x);
64$pure $genHType floor($genHType x);
65$pure $genType  ceil($genType  x);
66$pure $genHType ceil($genHType x);
67$pure $genType  fract($genType  x);
68$pure $genHType fract($genHType x);
69$pure $genType  mod($genType  x, float     y);
70$pure $genType  mod($genType  x, $genType  y);
71$pure $genHType mod($genHType x, half      y);
72$pure $genHType mod($genHType x, $genHType y);
73
74$pure $genType  min($genType  x, $genType  y);
75$pure $genType  min($genType  x, float     y);
76$pure $genHType min($genHType x, $genHType y);
77$pure $genHType min($genHType x, half      y);
78$pure $genType  max($genType  x, $genType  y);
79$pure $genType  max($genType  x, float     y);
80$pure $genHType max($genHType x, $genHType y);
81$pure $genHType max($genHType x, half      y);
82$pure $genType  clamp($genType  x, $genType  minVal, $genType  maxVal);
83$pure $genType  clamp($genType  x, float     minVal, float     maxVal);
84$pure $genHType clamp($genHType x, $genHType minVal, $genHType maxVal);
85$pure $genHType clamp($genHType x, half      minVal, half      maxVal);
86$pure $genType  saturate($genType  x);  // SkSL extension
87$pure $genHType saturate($genHType x);  // SkSL extension
88$pure $genType  mix($genType  x, $genType  y, $genType a);
89$pure $genType  mix($genType  x, $genType  y, float a);
90$pure $genHType mix($genHType x, $genHType y, $genHType a);
91$pure $genHType mix($genHType x, $genHType y, half a);
92$pure $genType  step($genType  edge, $genType x);
93$pure $genType  step(float     edge, $genType x);
94$pure $genHType step($genHType edge, $genHType x);
95$pure $genHType step(half      edge, $genHType x);
96$pure $genType  smoothstep($genType  edge0, $genType  edge1, $genType  x);
97$pure $genType  smoothstep(float     edge0, float     edge1, $genType  x);
98$pure $genHType smoothstep($genHType edge0, $genHType edge1, $genHType x);
99$pure $genHType smoothstep(half      edge0, half      edge1, $genHType x);
100
101// 8.3 : Common Functions (GLSL ES 3.0)
102$pure $es3 $genIType abs($genIType x);
103$pure $es3 $genIType sign($genIType x);
104$pure $es3 $genIType floatBitsToInt ($genType  value);
105$pure $es3 $genUType floatBitsToUint($genType  value);
106$pure $es3 $genType  intBitsToFloat ($genIType value);
107$pure $es3 $genType  uintBitsToFloat($genUType value);
108$pure $es3 $genType  trunc($genType  x);
109$pure $es3 $genHType trunc($genHType x);
110$pure $es3 $genType  round($genType  x);
111$pure $es3 $genHType round($genHType x);
112$pure $es3 $genType  roundEven($genType  x);
113$pure $es3 $genHType roundEven($genHType x);
114$pure $es3 $genIType min($genIType x, $genIType y);
115$pure $es3 $genIType min($genIType x, int y);
116$pure $es3 $genUType min($genUType x, $genUType y);
117$pure $es3 $genUType min($genUType x, uint y);
118$pure $es3 $genIType max($genIType x, $genIType y);
119$pure $es3 $genIType max($genIType x, int y);
120$pure $es3 $genUType max($genUType x, $genUType y);
121$pure $es3 $genUType max($genUType x, uint y);
122$pure $es3 $genIType clamp($genIType x, $genIType minVal, $genIType maxVal);
123$pure $es3 $genIType clamp($genIType x, int minVal, int maxVal);
124$pure $es3 $genUType clamp($genUType x, $genUType minVal, $genUType maxVal);
125$pure $es3 $genUType clamp($genUType x, uint minVal, uint maxVal);
126$pure $es3 $genType  mix($genType  x, $genType  y, $genBType a);
127$pure $es3 $genHType mix($genHType x, $genHType y, $genBType a);
128
129// 8.3 : Common Functions (GLSL ES 3.0) -- cannot be used in constant-expressions
130$pure $es3 $genBType isnan($genType  x);
131$pure $es3 $genBType isnan($genHType x);
132$pure $es3 $genBType isinf($genType  x);
133$pure $es3 $genBType isinf($genHType x);
134      $es3 $genType  modf($genType  x, out $genType  i);
135      $es3 $genHType modf($genHType x, out $genHType i);
136
137// 8.4 : Floating-Point Pack and Unpack Functions (GLSL ES 3.0)
138$pure $es3 uint packUnorm2x16(float2 v);
139$pure $es3 float2 unpackUnorm2x16(uint p);
140
141// 8.5 : Geometric Functions
142$pure float length($genType  x);
143$pure half  length($genHType x);
144$pure float distance($genType  p0, $genType  p1);
145$pure half  distance($genHType p0, $genHType p1);
146$pure float dot($genType  x, $genType  y);
147$pure half  dot($genHType x, $genHType y);
148$pure float3 cross(float3 x, float3 y);
149$pure half3  cross(half3  x, half3  y);
150$pure $genType  normalize($genType  x);
151$pure $genHType normalize($genHType x);
152$pure $genType  faceforward($genType  N, $genType  I, $genType  Nref);
153$pure $genHType faceforward($genHType N, $genHType I, $genHType Nref);
154$pure $genType  reflect($genType  I, $genType  N);
155$pure $genHType reflect($genHType I, $genHType N);
156$pure $genType  refract($genType  I, $genType  N, float eta);
157$pure $genHType refract($genHType I, $genHType N, half eta);
158
159// 8.6 : Matrix Functions
160$pure $squareMat  matrixCompMult($squareMat  x, $squareMat  y);
161$pure $squareHMat matrixCompMult($squareHMat x, $squareHMat y);
162$pure $es3 $mat   matrixCompMult($mat x, $mat y);
163$pure $es3 $hmat  matrixCompMult($hmat x, $hmat y);
164
165// 8.6 : Matrix Functions (GLSL 1.4, poly-filled by SkSL as needed)
166$pure $squareMat  inverse($squareMat  m);
167$pure $squareHMat inverse($squareHMat m);
168
169// 8.6 : Matrix Functions (GLSL ES 3.0)
170$pure $es3 float       determinant($squareMat m);
171$pure $es3 half        determinant($squareHMat m);
172$pure $es3 $squareMat  transpose($squareMat  m);
173$pure $es3 $squareHMat transpose($squareHMat m);
174$pure $es3 float2x3    transpose(float3x2 m);
175$pure $es3 half2x3     transpose(half3x2  m);
176$pure $es3 float2x4    transpose(float4x2 m);
177$pure $es3 half2x4     transpose(half4x2  m);
178$pure $es3 float3x2    transpose(float2x3 m);
179$pure $es3 half3x2     transpose(half2x3  m);
180$pure $es3 float3x4    transpose(float4x3 m);
181$pure $es3 half3x4     transpose(half4x3  m);
182$pure $es3 float4x2    transpose(float2x4 m);
183$pure $es3 half4x2     transpose(half2x4  m);
184$pure $es3 float4x3    transpose(float3x4 m);
185$pure $es3 half4x3     transpose(half3x4  m);
186$pure $es3 $squareMat  outerProduct($vec   c, $vec   r);
187$pure $es3 $squareHMat outerProduct($hvec  c, $hvec  r);
188$pure $es3 float2x3    outerProduct(float3 c, float2 r);
189$pure $es3 half2x3     outerProduct(half3  c, half2  r);
190$pure $es3 float3x2    outerProduct(float2 c, float3 r);
191$pure $es3 half3x2     outerProduct(half2  c, half3  r);
192$pure $es3 float2x4    outerProduct(float4 c, float2 r);
193$pure $es3 half2x4     outerProduct(half4  c, half2  r);
194$pure $es3 float4x2    outerProduct(float2 c, float4 r);
195$pure $es3 half4x2     outerProduct(half2  c, half4  r);
196$pure $es3 float3x4    outerProduct(float4 c, float3 r);
197$pure $es3 half3x4     outerProduct(half4  c, half3  r);
198$pure $es3 float4x3    outerProduct(float3 c, float4 r);
199$pure $es3 half4x3     outerProduct(half3  c, half4  r);
200
201// 8.7 : Vector Relational Functions
202$pure $bvec lessThan($vec  x, $vec  y);
203$pure $bvec lessThan($hvec x, $hvec y);
204$pure $bvec lessThan($ivec x, $ivec y);
205$pure $bvec lessThan($svec x, $svec y);
206$pure $bvec lessThanEqual($vec  x, $vec  y);
207$pure $bvec lessThanEqual($hvec x, $hvec y);
208$pure $bvec lessThanEqual($ivec x, $ivec y);
209$pure $bvec lessThanEqual($svec x, $svec y);
210$pure $bvec greaterThan($vec  x, $vec  y);
211$pure $bvec greaterThan($hvec x, $hvec y);
212$pure $bvec greaterThan($ivec x, $ivec y);
213$pure $bvec greaterThan($svec x, $svec y);
214$pure $bvec greaterThanEqual($vec  x, $vec  y);
215$pure $bvec greaterThanEqual($hvec x, $hvec y);
216$pure $bvec greaterThanEqual($ivec x, $ivec y);
217$pure $bvec greaterThanEqual($svec x, $svec y);
218$pure $bvec equal($vec  x, $vec  y);
219$pure $bvec equal($hvec x, $hvec y);
220$pure $bvec equal($ivec x, $ivec y);
221$pure $bvec equal($svec x, $svec y);
222$pure $bvec equal($bvec x, $bvec y);
223$pure $bvec notEqual($vec  x, $vec  y);
224$pure $bvec notEqual($hvec x, $hvec y);
225$pure $bvec notEqual($ivec x, $ivec y);
226$pure $bvec notEqual($svec x, $svec y);
227$pure $bvec notEqual($bvec x, $bvec y);
228
229$pure $es3 $bvec lessThan($usvec x, $usvec y);
230$pure $es3 $bvec lessThan($uvec x, $uvec y);
231$pure $es3 $bvec lessThanEqual($uvec x, $uvec y);
232$pure $es3 $bvec lessThanEqual($usvec x, $usvec y);
233$pure $es3 $bvec greaterThan($uvec x, $uvec y);
234$pure $es3 $bvec greaterThan($usvec x, $usvec y);
235$pure $es3 $bvec greaterThanEqual($uvec x, $uvec y);
236$pure $es3 $bvec greaterThanEqual($usvec x, $usvec y);
237$pure $es3 $bvec equal($uvec x, $uvec y);
238$pure $es3 $bvec equal($usvec x, $usvec y);
239$pure $es3 $bvec notEqual($uvec x, $uvec y);
240$pure $es3 $bvec notEqual($usvec x, $usvec y);
241
242$pure bool  any($bvec x);
243$pure bool  all($bvec x);
244$pure $bvec not($bvec x);
245
246// 8.9 : Fragment Processing Functions (GLSL ES 3.0)
247$pure $es3 $genType  dFdx($genType p);
248$pure $es3 $genType  dFdy($genType p);
249$pure $es3 $genHType dFdx($genHType p);
250$pure $es3 $genHType dFdy($genHType p);
251$pure $es3 $genType  fwidth($genType p);
252$pure $es3 $genHType fwidth($genHType p);
253
254
255// SkSL utility functions
256
257// The max() guards against division by zero when the incoming color is transparent black
258$pure half4  unpremul(half4  color) { return half4 (color.rgb / max(color.a, 0.0001), color.a); }
259$pure float4 unpremul(float4 color) { return float4(color.rgb / max(color.a, 0.0001), color.a); }
260
261// Similar, but used for polar-space CSS colors
262$export $pure half4 $unpremul_polar(half4 color) {
263    return half4(color.r, color.gb / max(color.a, 0.0001), color.a);
264}
265
266// Convert RGBA -> HSLA (including unpremul).
267//
268// Based on work by Sam Hocevar, Emil Persson, and Ian Taylor [1][2][3].  High-level ideas:
269//
270//   - minimize the number of branches by sorting and computing the hue phase in parallel (vec4s)
271//
272//   - trade the third sorting branch for a potentially faster std::min and leaving 2nd/3rd
273//     channels unsorted (based on the observation that swapping both the channels and the bias sign
274//     has no effect under abs)
275//
276//   - use epsilon offsets for denominators, to avoid explicit zero-checks
277//
278// An additional trick we employ is deferring premul->unpremul conversion until the very end: the
279// alpha factor gets naturally simplified for H and S, and only L requires a dedicated unpremul
280// division (so we trade three divs for one).
281//
282// [1] http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
283// [2] http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
284// [3] http://www.chilliant.com/rgb2hsv.html
285
286$export $pure half4 $rgb_to_hsl(half3 c, half a) {
287    half4 p = (c.g < c.b) ? half4(c.bg, -1,  2/3.0)
288                          : half4(c.gb,  0, -1/3.0);
289    half4 q = (c.r < p.x) ? half4(p.x, c.r, p.yw)
290                          : half4(c.r, p.x, p.yz);
291
292    // q.x  -> max channel value
293    // q.yz -> 2nd/3rd channel values (unsorted)
294    // q.w  -> bias value dependent on max channel selection
295
296    const half kEps = 0.0001;
297    half pmV = q.x;
298    half pmC = pmV - min(q.y, q.z);
299    half pmL = pmV - pmC * 0.5;
300    half   H = abs(q.w + (q.y - q.z) / (pmC * 6 + kEps));
301    half   S = pmC / (a + kEps - abs(pmL * 2 - a));
302    half   L = pmL / (a + kEps);
303
304    return half4(H, S, L, a);
305}
306
307// Convert HSLA -> RGBA (including clamp and premul).
308//
309// Based on work by Sam Hocevar, Emil Persson, and Ian Taylor [1][2][3].
310//
311// [1] http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
312// [2] http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
313// [3] http://www.chilliant.com/rgb2hsv.html
314
315$export $pure half3 $hsl_to_rgb(half3 hsl) {
316    half      C = (1 - abs(2 * hsl.z - 1)) * hsl.y;
317    half3     p = hsl.xxx + half3(0, 2/3.0, 1/3.0);
318    half3     q = saturate(abs(fract(p) * 6 - 3) - 1);
319
320    return (q - 0.5) * C + hsl.z;
321}
322
323$export $pure half4 $hsl_to_rgb(half3 hsl, half a) {
324    return saturate(half4($hsl_to_rgb(hsl) * a, a));
325}
326
327// Color conversion functions used in gradient interpolation, based on
328// https://www.w3.org/TR/css-color-4/#color-conversion-code
329// TODO(skia:13108): For all of these, we can eliminate any linear math at the beginning
330// (by removing the corresponding linear math at the end of the CPU code).
331$export $pure half3 $css_lab_to_xyz(half3 lab) {
332    const half k = 24389 / 27.0;
333    const half e = 216 / 24389.0;
334
335    half3 f;
336    f[1] = (lab[0] + 16) / 116;
337    f[0] = (lab[1] / 500) + f[1];
338    f[2] = f[1] - (lab[2] / 200);
339
340    half3 f_cubed = pow(f, half3(3));
341
342    half3 xyz = half3(
343        f_cubed[0] > e ? f_cubed[0] : (116 * f[0] - 16) / k,
344        lab[0] > k * e ? f_cubed[1] : lab[0] / k,
345        f_cubed[2] > e ? f_cubed[2] : (116 * f[2] - 16) / k
346    );
347
348    const half3 D50 = half3(0.3457 / 0.3585, 1.0, (1.0 - 0.3457 - 0.3585) / 0.3585);
349    return xyz * D50;
350}
351
352// Skia stores all polar colors with hue in the first component, so this "LCH -> Lab" transform
353// actually takes "HCL". This is also used to do the same polar transform for OkHCL to OkLAB.
354// See similar comments & logic in SkGradientShaderBase.cpp.
355$pure half3 $css_hcl_to_lab(half3 hcl) {
356    return half3(
357        hcl[2],
358        hcl[1] * cos(radians(hcl[0])),
359        hcl[1] * sin(radians(hcl[0]))
360    );
361}
362
363$export $pure half3 $css_hcl_to_xyz(half3 hcl) {
364    return $css_lab_to_xyz($css_hcl_to_lab(hcl));
365}
366
367$export $pure half3 $css_oklab_to_linear_srgb(half3 oklab) {
368    half l_ = oklab.x + 0.3963377774 * oklab.y + 0.2158037573 * oklab.z,
369         m_ = oklab.x - 0.1055613458 * oklab.y - 0.0638541728 * oklab.z,
370         s_ = oklab.x - 0.0894841775 * oklab.y - 1.2914855480 * oklab.z;
371
372    half l = l_*l_*l_,
373         m = m_*m_*m_,
374         s = s_*s_*s_;
375
376    return half3(
377        +4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
378        -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
379        -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s
380    );
381}
382
383$export $pure half3 $css_okhcl_to_linear_srgb(half3 okhcl) {
384    return $css_oklab_to_linear_srgb($css_hcl_to_lab(okhcl));
385}
386
387$export $pure half3 $css_oklab_gamut_map_to_linear_srgb(half3 oklab) {
388    // Constants for the normal vector of the plane formed by white, black, and
389    // the specified vertex of the gamut.
390    const half2 normal_R = half2(0.409702, -0.912219);
391    const half2 normal_M = half2(-0.397919, -0.917421);
392    const half2 normal_B = half2(-0.906800, 0.421562);
393    const half2 normal_C = half2(-0.171122, 0.985250);
394    const half2 normal_G = half2(0.460276, 0.887776);
395    const half2 normal_Y = half2(0.947925, 0.318495);
396
397    // For the triangles formed by white (W) or black (K) with the vertices
398    // of Yellow and Red (YR), Red and Magenta (RM), etc, the constants to be
399    // used to compute the intersection of a line of constant hue and luminance
400    // with that plane.
401    const half  c0_YR = 0.091132;
402    const half2 cW_YR = half2(0.070370, 0.034139);
403    const half2 cK_YR = half2(0.018170, 0.378550);
404    const half  c0_RM = 0.113902;
405    const half2 cW_RM = half2(0.090836, 0.036251);
406    const half2 cK_RM = half2(0.226781, 0.018764);
407    const half  c0_MB = 0.161739;
408    const half2 cW_MB = half2(-0.008202, -0.264819);
409    const half2 cK_MB = half2( 0.187156, -0.284304);
410    const half  c0_BC = 0.102047;
411    const half2 cW_BC = half2(-0.014804, -0.162608);
412    const half2 cK_BC = half2(-0.276786,  0.004193);
413    const half  c0_CG = 0.092029;
414    const half2 cW_CG = half2(-0.038533, -0.001650);
415    const half2 cK_CG = half2(-0.232572, -0.094331);
416    const half  c0_GY = 0.081709;
417    const half2 cW_GY = half2(-0.034601, -0.002215);
418    const half2 cK_GY = half2( 0.012185,  0.338031);
419
420    half2 ab = oklab.yz;
421
422    // Find the planes to intersect with and set the constants based on those
423    // planes.
424    half c0;
425    half2 cW;
426    half2 cK;
427    if (dot(ab, normal_R) < 0.0) {
428        if (dot(ab, normal_G) < 0.0) {
429            if (dot(ab, normal_C) < 0.0) {
430                c0 = c0_BC; cW = cW_BC; cK = cK_BC;
431            } else {
432                c0 = c0_CG; cW = cW_CG; cK = cK_CG;
433            }
434        } else {
435            if (dot(ab, normal_Y) < 0.0) {
436                c0 = c0_GY; cW = cW_GY; cK = cK_GY;
437            } else {
438                c0 = c0_YR; cW = cW_YR; cK = cK_YR;
439            }
440        }
441    } else {
442        if (dot(ab, normal_B) < 0.0) {
443            if (dot(ab, normal_M) < 0.0) {
444                c0 = c0_RM; cW = cW_RM; cK = cK_RM;
445            } else {
446                c0 = c0_MB; cW = cW_MB; cK = cK_MB;
447            }
448        } else {
449            c0 = c0_BC; cW = cW_BC; cK = cK_BC;
450        }
451    }
452
453    // Perform the intersection.
454    half alpha = 1.0;
455
456    // Intersect with the plane with white.
457    half w_denom = dot(cW, ab);
458    if (w_denom > 0.0) {
459        half one_minus_L = 1.0 - oklab.r;
460        half w_num = c0*one_minus_L;
461        if (w_num < w_denom) {
462            alpha = min(alpha, w_num / w_denom);
463        }
464    }
465
466    // Intersect with the plane with black.
467    half k_denom = dot(cK, ab);
468    if (k_denom > 0.0) {
469        half L = oklab.r;
470        half k_num = c0*L;
471        if (k_num < k_denom) {
472            alpha = min(alpha,  k_num / k_denom);
473        }
474    }
475
476    // Attenuate the ab coordinate by alpha.
477    oklab.yz *= alpha;
478
479    return $css_oklab_to_linear_srgb(oklab);
480}
481
482$export $pure half3 $css_okhcl_gamut_map_to_linear_srgb(half3 okhcl) {
483    return $css_oklab_gamut_map_to_linear_srgb($css_hcl_to_lab(okhcl));
484}
485
486// TODO(skia:13108): Use our optimized version (though it has different range)
487// Doing so might require fixing (re-deriving?) the math for the HWB version below
488$export $pure half3 $css_hsl_to_srgb(half3 hsl) {
489    hsl.x = mod(hsl.x, 360);
490    if (hsl.x < 0) {
491        hsl.x += 360;
492    }
493
494    hsl.yz /= 100;
495
496    half3 k = mod(half3(0, 8, 4) + hsl.x/30, 12);
497    half a = hsl.y * min(hsl.z, 1 - hsl.z);
498    return hsl.z - a * clamp(min(k - 3, 9 - k), -1, 1);
499}
500
501$export $pure half3 $css_hwb_to_srgb(half3 hwb) {
502    half3 rgb;
503    hwb.yz /= 100;
504    if (hwb.y + hwb.z >= 1) {
505        // Emit grayscale
506        rgb = half3(hwb.y / (hwb.y + hwb.z));
507    } else {
508        rgb = $css_hsl_to_srgb(half3(hwb.x, 100, 50));
509        rgb *= (1 - hwb.y - hwb.z);
510        rgb += hwb.y;
511    }
512    return rgb;
513}
514
515/*
516 * The actual output color space of this function depends on the input color space
517 * (it might be sRGB, linear sRGB, or linear XYZ). The actual space is what's stored
518 * in the gradient/SkColor4fXformer's fIntermediateColorSpace.
519 */
520$export $pure half4 $interpolated_to_rgb_unpremul(half4 color, int colorSpace, int doUnpremul) {
521    const int kDestination   = 0;
522    const int kSRGBLinear    = 1;
523    const int kLab           = 2;
524    const int kOKLab         = 3;
525    const int kOKLabGamutMap = 4;
526    const int kLCH           = 5;
527    const int kOKLCH         = 6;
528    const int kOKLCHGamutMap = 7;
529    const int kSRGB          = 8;
530    const int kHSL           = 9;
531    const int kHWB           = 10;
532
533    if (bool(doUnpremul)) {
534        switch (colorSpace) {
535            case kLab:
536            case kOKLab:
537            case kOKLabGamutMap: color = unpremul(color); break;
538            case kLCH:
539            case kOKLCH:
540            case kOKLCHGamutMap:
541            case kHSL:
542            case kHWB: color = $unpremul_polar(color); break;
543        }
544    }
545    switch (colorSpace) {
546        case kLab:           color.rgb = $css_lab_to_xyz(color.rgb); break;
547        case kOKLab:         color.rgb = $css_oklab_to_linear_srgb(color.rgb); break;
548        case kOKLabGamutMap: color.rgb = $css_oklab_gamut_map_to_linear_srgb(color.rgb); break;
549        case kLCH:           color.rgb = $css_hcl_to_xyz(color.rgb); break;
550        case kOKLCH:         color.rgb = $css_okhcl_to_linear_srgb(color.rgb); break;
551        case kOKLCHGamutMap: color.rgb = $css_okhcl_gamut_map_to_linear_srgb(color.rgb); break;
552        case kHSL:           color.rgb = $css_hsl_to_srgb(color.rgb); break;
553        case kHWB:           color.rgb = $css_hwb_to_srgb(color.rgb); break;
554    }
555    return color;
556}
557