• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <unordered_map>
17 #include <string>
18 
19 static std::unordered_map<std::string, std::string> extModuleData = {
20     {"sksl_frag.sksl", R"(
21 // defines built-in interfaces supported by SkSL fragment shaders
22 
23 // See "enum SpvBuiltIn_" in ./spirv.h
24 layout(builtin=15) in float4 sk_FragCoord;
25 layout(builtin=17) in bool sk_Clockwise;  // Similar to gl_FrontFacing, but defined in device space.
26 layout(builtin=20) in uint sk_SampleMaskIn;
27 layout(builtin=10020) out uint sk_SampleMask;
28 
29 layout(location=0,index=0,builtin=10001) out half4 sk_FragColor;
30 layout(builtin=10008) in half4 sk_LastFragColor;
31 layout(location=0,index=1,builtin=10012) out half4 sk_SecondaryFragColor;
32 )"},
33     {"sksl_vert.sksl", R"(
34 // defines built-in interfaces supported by SkSL vertex shaders
35 
36 out sk_PerVertex {
37     layout(builtin=0) float4 sk_Position;
38     layout(builtin=1) float sk_PointSize;
39 };
40 
41 layout(builtin=42) in int sk_VertexID;
42 layout(builtin=43) in int sk_InstanceID;
43 )"},
44     {"sksl_gpu.sksl", R"(
45 // Not exposed in shared module
46 
47 $pure $genIType mix($genIType x, $genIType y, $genBType a);
48 $pure $genBType mix($genBType x, $genBType y, $genBType a);
49 $pure $genType fma($genType a, $genType b, $genType c);
50 $pure $genHType fma($genHType a, $genHType b, $genHType c);
51       $genType frexp($genType x, out $genIType exp);
52       $genHType frexp($genHType x, out $genIType exp);
53 $pure $genType ldexp($genType x, in $genIType exp);
54 $pure $genHType ldexp($genHType x, in $genIType exp);
55 
56 $pure uint packSnorm2x16(float2 v);
57 $pure uint packUnorm4x8(float4 v);
58 $pure uint packSnorm4x8(float4 v);
59 $pure float2 unpackSnorm2x16(uint p);
60 $pure float4 unpackUnorm4x8(uint p);
61 $pure float4 unpackSnorm4x8(uint p);
62 $pure uint packHalf2x16(float2 v);
63 $pure float2 unpackHalf2x16(uint v);
64 
65 $pure $genIType bitCount($genIType value);
66 $pure $genIType bitCount($genUType value);
67 $pure $genIType findLSB($genIType value);
68 $pure $genIType findLSB($genUType value);
69 $pure $genIType findMSB($genIType value);
70 $pure $genIType findMSB($genUType value);
71 
72 $pure half4 sample(sampler2D s, float2 P);
73 $pure half4 sample(sampler2D s, float3 P);
74 $pure half4 sample(sampler2D s, float3 P, float bias);
75 
76 $pure half4 sample(samplerExternalOES s, float2 P);
77 $pure half4 sample(samplerExternalOES s, float2 P, float bias);
78 
79 $pure half4 sample(sampler2DRect s, float2 P);
80 $pure half4 sample(sampler2DRect s, float3 P);
81 
82 $pure half4 sampleLod(sampler2D s, float2 P, float lod);
83 $pure half4 sampleLod(sampler2D s, float3 P, float lod);
84 
85 $pure half4 sampleGrad(sampler2D s, float2, float2 dPdx, float2 dPdy);
86 
87 // Currently we do not support the generic types of loading subpassInput so we have some explicit
88 // versions that we currently use
89 $pure half4 subpassLoad(subpassInput subpass);
90 $pure half4 subpassLoad(subpassInputMS subpass, int sample);
91 
92 /** Atomically loads the value from `a` and returns it. */
93 $pure uint atomicLoad(atomicUint a);
94 
95 /** Atomically stores the value of `value` to `a` */
96 void atomicStore(atomicUint a, uint value);
97 
98 /**
99  * Performs an atomic addition of `value` to the contents of `a` and returns the original contents
100  * of `a` from before the addition occurred.
101  */
102 uint atomicAdd(atomicUint a, uint value);
103 
104 // Definitions of functions implementing all of the SkBlendMode blends.
105 
106 $pure half4 blend_clear(half4 src, half4 dst) { return half4(0); }
107 
108 $pure half4 blend_src(half4 src, half4 dst) { return src; }
109 
110 $pure half4 blend_dst(half4 src, half4 dst) { return dst; }
111 
112 $pure half4 blend_src_over(half4 src, half4 dst) { return src + (1 - src.a)*dst; }
113 
114 $pure half4 blend_dst_over(half4 src, half4 dst) { return (1 - dst.a)*src + dst; }
115 
116 $pure half4 blend_src_in(half4 src, half4 dst) { return src*dst.a; }
117 
118 $pure half4 blend_dst_in(half4 src, half4 dst) { return dst*src.a; }
119 
120 $pure half4 blend_src_out(half4 src, half4 dst) { return (1 - dst.a)*src; }
121 
122 $pure half4 blend_dst_out(half4 src, half4 dst) { return (1 - src.a)*dst; }
123 
124 $pure half4 blend_src_atop(half4 src, half4 dst) { return dst.a*src + (1 - src.a)*dst; }
125 
126 $pure half4 blend_dst_atop(half4 src, half4 dst)  { return  (1 - dst.a) * src + src.a*dst; }
127 
128 $pure half4 blend_xor(half4 src, half4 dst) { return (1 - dst.a)*src + (1 - src.a)*dst; }
129 
130 // This multi-purpose Porter-Duff blend function can perform any of the twelve blends above,
131 // when passed one of the following values for BlendOp:
132 // - Clear:          0*src +        0*dst = (0 +  0*dstA)*src + (0 +  0*srcA)*dst = (0,  0,  0,  0)
133 // - Src:            1*src +        0*dst = (1 +  0*dstA)*src + (0 +  0*srcA)*dst = (1,  0,  0,  0)
134 // - Dst:            0*src +        1*dst = (0 +  0*dstA)*src + (1 +  0*srcA)*dst = (0,  1,  0,  0)
135 // - SrcOver:        1*src + (1-srcA)*dst = (1 +  0*dstA)*src + (1 + -1*srcA)*dst = (1,  1,  0, -1)
136 // - DstOver: (1-dstA)*src +        1*dst = (1 + -1*dstA)*src + (1 +  0*srcA)*dst = (1,  1, -1,  0)
137 // - SrcIn:       dstA*src +        0*dst = (0 +  1*dstA)*src + (0 +  0*srcA)*dst = (0,  0,  1,  0)
138 // - DstIn:          0*src +     srcA*dst = (0 +  0*dstA)*src + (0 +  1*srcA)*dst = (0,  0,  0,  1)
139 // - SrcOut:  (1-dstA)*src +        0*dst = (1 + -1*dstA)*src + (0 +  0*srcA)*dst = (1,  0, -1,  0)
140 // - DstOut:         0*src + (1-srcA)*dst = (0 +  0*dstA)*src + (1 + -1*srcA)*dst = (0,  1,  0, -1)
141 // - SrcATop:     dstA*src + (1-srcA)*dst = (0 +  1*dstA)*src + (1 + -1*srcA)*dst = (0,  1,  1, -1)
142 // - DstATop: (1-dstA)*src +     srcA*dst = (1 + -1*dstA)*src + (0 +  1*srcA)*dst = (1,  0, -1,  1)
143 // - Xor:     (1-dstA)*src + (1-srcA)*dst = (1 + -1*dstA)*src + (1 + -1*srcA)*dst = (1,  1, -1, -1)
144 $pure half4 blend_porter_duff(half4 blendOp, half4 src, half4 dst) {
145     // The supported blend modes all have coefficients that are of the form (C + S*alpha), where
146     // alpha is the other color's alpha channel. C can be 0 or 1, S can be -1, 0, or 1.
147     half2 coeff = blendOp.xy + blendOp.zw * half2(dst.a, src.a);
148     return src * coeff.x + dst * coeff.y;
149 }
150 
151 $pure half4 blend_plus(half4 src, half4 dst) { return min(src + dst, 1); }
152 
153 $pure half4 blend_modulate(half4 src, half4 dst) { return src*dst; }
154 
155 $pure half4 blend_screen(half4 src, half4 dst) { return src + (1 - src)*dst; }
156 
157 $pure half $blend_overlay_component(half2 s, half2 d) {
158     return (2*d.x <= d.y) ? 2*s.x*d.x
159                           : s.y*d.y - 2*(d.y - d.x)*(s.y - s.x);
160 }
161 
162 $pure half4 blend_overlay(half4 src, half4 dst) {
163     half4 result = half4($blend_overlay_component(src.ra, dst.ra),
164                          $blend_overlay_component(src.ga, dst.ga),
165                          $blend_overlay_component(src.ba, dst.ba),
166                          src.a + (1 - src.a)*dst.a);
167     result.rgb += dst.rgb*(1 - src.a) + src.rgb*(1 - dst.a);
168     return result;
169 }
170 
171 $pure half4 blend_overlay(half flip, half4 a, half4 b) {
172     return blend_overlay(bool(flip) ? b : a, bool(flip) ? a : b);
173 }
174 
175 $pure half4 blend_lighten(half4 src, half4 dst) {
176     half4 result = blend_src_over(src, dst);
177     result.rgb = max(result.rgb, (1 - dst.a)*src.rgb + dst.rgb);
178     return result;
179 }
180 
181 $pure half4 blend_darken(half mode /* darken: 1, lighten: -1 */, half4 src, half4 dst) {
182     half4 a = blend_src_over(src, dst);
183     half3 b = (1 - dst.a) * src.rgb + dst.rgb;  // DstOver.rgb
184     a.rgb = mode * min(a.rgb * mode, b.rgb * mode);
185     return a;
186 }
187 
188 $pure half4 blend_darken(half4 src, half4 dst) {
189    return blend_darken(1, src, dst);
190 }
191 
192 // A useful constant to check against when dividing a half-precision denominator.
193 // Denormal half floats (values less than this) will compare not-equal to 0 but can easily cause the
194 // division to overflow to infinity. Even regular values can overflow given the low maximum value.
195 // For instance, any value x > ~3.998 will overflow when divided by $kMinNormalHalf. This is a
196 // reasonable value even for wide gamut colors being input to these blend functions, but the
197 // most correct denominator check is to treat anything with `denom < x/F16_MAX` as division by 0.
198 const half $kMinNormalHalf = 1.0 / (1 << 14);
199 
200 const half $kGuardedDivideEpsilon = sk_Caps.mustGuardDivisionEvenAfterExplicitZeroCheck
201                                         ? 0.00000001
202                                         : 0.0;
203 
204 $pure inline half $guarded_divide(half n, half d) {
205     return n / (d + $kGuardedDivideEpsilon);
206 }
207 
208 $pure inline half3 $guarded_divide(half3 n, half d) {
209     return n / (d + $kGuardedDivideEpsilon);
210 }
211 
212 $pure half $color_dodge_component(half2 s, half2 d) {
213     // The following is a single flow of control implementation of:
214     //     if (d.x == 0) {
215     //         return s.x*(1 - d.y);
216     //     } else {
217     //         half delta = s.y - s.x;
218     //         if (delta == 0) {
219     //             return s.y*d.y + s.x*(1 - d.y) + d.x*(1 - s.y);
220     //         } else {
221     //             delta = min(d.y, $guarded_divide(d.x*s.y, delta));
222     //             return delta*s.y + s.x*(1 - d.y) + d.x*(1 - s.y);
223     //         }
224     //     }
225     //
226     // When d.x == 0, then dxScale forces delta to 0 and simplifying the return value to s.x*(1-d.y)
227     // When s.y-s.x == 0, the mix selects d.y and min(d.y, d.y) leaves delta = d.y
228     // Otherwise the mix selects the delta expression in the final else branch.
229     half dxScale = d.x == 0 ? 0 : 1;
230     half delta = dxScale * min(d.y, abs(s.y-s.x) >= $kMinNormalHalf
231                                             ? $guarded_divide(d.x*s.y, s.y-s.x)
232                                             : d.y);
233     return delta*s.y + s.x*(1 - d.y) + d.x*(1 - s.y);
234 }
235 
236 $pure half4 blend_color_dodge(half4 src, half4 dst) {
237     return half4($color_dodge_component(src.ra, dst.ra),
238                  $color_dodge_component(src.ga, dst.ga),
239                  $color_dodge_component(src.ba, dst.ba),
240                  src.a + (1 - src.a)*dst.a);
241 }
242 
243 $pure half $color_burn_component(half2 s, half2 d) {
244     half dyTerm = d.y == d.x ? d.y : 0;
245     half delta = abs(s.x) >= $kMinNormalHalf
246                         ? d.y - min(d.y, $guarded_divide((d.y - d.x)*s.y, s.x))
247                         : dyTerm;
248     return delta*s.y + s.x*(1 - d.y) + d.x*(1 - s.y);
249 }
250 
251 $pure half4 blend_color_burn(half4 src, half4 dst) {
252     return half4($color_burn_component(src.ra, dst.ra),
253                  $color_burn_component(src.ga, dst.ga),
254                  $color_burn_component(src.ba, dst.ba),
255                  src.a + (1 - src.a)*dst.a);
256 }
257 
258 $pure half4 blend_hard_light(half4 src, half4 dst) {
259     return blend_overlay(dst, src);
260 }
261 
262 $pure half $soft_light_component(half2 s, half2 d) {
263     if (2*s.x <= s.y) {
264         return $guarded_divide(d.x*d.x*(s.y - 2*s.x), d.y) + (1 - d.y)*s.x + d.x*(-s.y + 2*s.x + 1);
265     } else if (4.0 * d.x <= d.y) {
266         half DSqd = d.x*d.x;
267         half DCub = DSqd*d.x;
268         half DaSqd = d.y*d.y;
269         half DaCub = DaSqd*d.y;
270         return $guarded_divide(DaSqd*(s.x - d.x*(3*s.y - 6*s.x - 1)) + 12*d.y*DSqd*(s.y - 2*s.x)
271                                - 16*DCub * (s.y - 2*s.x) - DaCub*s.x, DaSqd);
272     } else {
273         return d.x*(s.y - 2*s.x + 1) + s.x - sqrt(d.y*d.x)*(s.y - 2*s.x) - d.y*s.x;
274     }
275 }
276 
277 $pure half4 blend_soft_light(half4 src, half4 dst) {
278     return (dst.a == 0) ? src : half4($soft_light_component(src.ra, dst.ra),
279                                       $soft_light_component(src.ga, dst.ga),
280                                       $soft_light_component(src.ba, dst.ba),
281                                       src.a + (1 - src.a)*dst.a);
282 }
283 
284 $pure half4 blend_difference(half4 src, half4 dst) {
285     return half4(src.rgb + dst.rgb - 2*min(src.rgb*dst.a, dst.rgb*src.a),
286                  src.a + (1 - src.a)*dst.a);
287 }
288 
289 $pure half4 blend_exclusion(half4 src, half4 dst) {
290     return half4(dst.rgb + src.rgb - 2*dst.rgb*src.rgb, src.a + (1 - src.a)*dst.a);
291 }
292 
293 $pure half4 blend_multiply(half4 src, half4 dst) {
294     return half4((1 - src.a)*dst.rgb + (1 - dst.a)*src.rgb + src.rgb*dst.rgb,
295                  src.a + (1 - src.a)*dst.a);
296 }
297 
298 $pure half $blend_color_luminance(half3 color) { return dot(half3(0.3, 0.59, 0.11), color); }
299 
300 $pure half3 $blend_set_color_luminance(half3 hueSatColor, half alpha, half3 lumColor) {
301     half lum = $blend_color_luminance(lumColor);
302     half3 result = lum - $blend_color_luminance(hueSatColor) + hueSatColor;
303     half minComp = min(min(result.r, result.g), result.b);
304     half maxComp = max(max(result.r, result.g), result.b);
305     if (minComp < 0 && lum != minComp) {
306         result = lum + (result - lum) * $guarded_divide(lum, (lum - minComp) + $kMinNormalHalf);
307     }
308     if (maxComp > alpha && maxComp != lum) {
309         result = lum +
310                  $guarded_divide((result - lum) * (alpha - lum), (maxComp - lum) + $kMinNormalHalf);
311     }
312     return result;
313 }
314 
315 $pure half $blend_color_saturation(half3 color) {
316     return max(max(color.r, color.g), color.b) - min(min(color.r, color.g), color.b);
317 }
318 
319 $pure half3 $blend_set_color_saturation(half3 color, half3 satColor) {
320     half mn = min(min(color.r, color.g), color.b);
321     half mx = max(max(color.r, color.g), color.b);
322 
323     return (mx > mn) ? ((color - mn) * $blend_color_saturation(satColor)) / (mx - mn)
324                      : half3(0);
325 }
326 
327 $pure half4 blend_hslc(half2 flipSat, half4 src, half4 dst) {
328     half alpha = dst.a * src.a;
329     half3 sda = src.rgb * dst.a;
330     half3 dsa = dst.rgb * src.a;
331     half3 l = bool(flipSat.x) ? dsa : sda;
332     half3 r = bool(flipSat.x) ? sda : dsa;
333     if (bool(flipSat.y)) {
334         l = $blend_set_color_saturation(l, r);
335         r = dsa;
336     }
337     return half4($blend_set_color_luminance(l, alpha, r) + dst.rgb - dsa + src.rgb - sda,
338                  src.a + dst.a - alpha);
339 }
340 
341 $pure half4 blend_hue(half4 src, half4 dst) {
342     return blend_hslc(half2(0, 1), src, dst);
343 }
344 
345 $pure half4 blend_saturation(half4 src, half4 dst) {
346     return blend_hslc(half2(1), src, dst);
347 }
348 
349 $pure half4 blend_color(half4 src, half4 dst)  {
350     return blend_hslc(half2(0), src, dst);
351 }
352 
353 $pure half4 blend_luminosity(half4 src, half4 dst) {
354     return blend_hslc(half2(1, 0), src, dst);
355 }
356 
357 $pure float2 proj(float3 p) { return p.xy / p.z; }
358 
359 // Implement cross() as a determinant to communicate our intent more clearly to the compiler.
360 // NOTE: Due to precision issues, it might be the case that cross(a, a) != 0.
361 $pure float cross_length_2d(float2 a, float2 b) {
362     return determinant(float2x2(a, b));
363 }
364 
365 $pure half cross_length_2d(half2 a, half2 b) {
366     return determinant(half2x2(a, b));
367 }
368 
369 $pure float2 perp(float2 v) {
370     return float2(-v.y, v.x);
371 }
372 
373 $pure half2 perp(half2 v) {
374     return half2(-v.y, v.x);
375 }
376 
377 // Returns a bias given a scale factor, such that 'scale * (dist + bias)' converts the distance to
378 // a per-pixel coverage value, automatically widening the visible coverage ramp for subpixel
379 // dimensions. The 'scale' must already be equal to the narrowest dimension of the shape and clamped
380 // to [0, 1.0].
381 $pure float coverage_bias(float scale) {
382     return 1.0 - 0.5 * scale;
383 }
384 
385 // add the support of textureSize
386 int2 textureSize(sampler2D x, int y);
387 
388 // add the support of textureGather
389 half4 sampleGather(sampler2D s, float2 p);
390 half4 sampleGather(sampler2D s, float2 p, int comp);
391 
392 // add the support of nonuniformEXT
393 uint nonuniformEXT(uint x);
394 )"},
395     {"sksl_public.sksl", R"(
396 // SkSL intrinsics that are not part of GLSL
397 
398 // Color space transformation, between the working (destination) space and fixed (known) spaces:
399 $pure half3 toLinearSrgb(half3 color);
400 $pure half3 fromLinearSrgb(half3 color);
401 
402 // SkSL intrinsics that reflect Skia's C++ object model:
403       half4 $eval(float2 coords, shader s);
404       half4 $eval(half4 color, colorFilter f);
405       half4 $eval(half4 src, half4 dst, blender b);
406 )"},
407     {"sksl_rt_shader.sksl", R"(
408 layout(builtin=15) float4 sk_FragCoord;
409 
410 //--- Luma ------------------------------------------------------------------------
411 
412 half4 sk_luma(half3 color) {
413     return saturate(dot(half3(0.2126, 0.7152, 0.0722), color)).000r;
414 }
415 
416 //--- Decal ------------------------------------------------------------------------
417 
418 half4 sk_decal(shader image, float2 coord, float4 decalBounds) {
419     half4 d = half4(decalBounds - coord.xyxy) * half4(-1, -1, 1, 1);
420     d = saturate(d + 0.5);
421     return (d.x * d.y * d.z * d.w) * image.eval(coord);
422 }
423 
424 //--- Displacement -----------------------------------------------------------------
425 
426 half4 sk_displacement(shader displMap,
427                       shader colorMap,
428                       float2 coord,
429                       half2 scale,
430                       half4 xSelect,  // Only one of RGBA will be 1, the rest are 0
431                       half4 ySelect) {
432     half4 displColor = unpremul(displMap.eval(coord));
433     half2 displ = half2(dot(displColor, xSelect), dot(displColor, ySelect));
434     displ = scale * (displ - 0.5);
435     return colorMap.eval(coord + displ);
436 }
437 
438 //--- Magnifier --------------------------------------------------------------------
439 
440 half4 sk_magnifier(shader src, float2 coord, float4 lensBounds, float4 zoomXform,
441                    float2 invInset) {
442     float2 zoomCoord = zoomXform.xy + zoomXform.zw*coord;
443     // edgeInset is the smallest distance to the lens bounds edges, in units of "insets".
444     float2 edgeInset = min(coord - lensBounds.xy, lensBounds.zw - coord) * invInset;
445 
446     // The equations for 'weight' ensure that it is 0 along the outside of
447     // lensBounds so it seams with any un-zoomed, un-filtered content. The zoomed
448     // content fills a rounded rectangle that is 1 "inset" in from lensBounds with
449     // circular corners with radii equal to the inset distance. Outside of this
450     // region, there is a non-linear weighting to compress the un-zoomed content
451     // to the zoomed content. The critical zone about each corner is limited
452     // to 2x"inset" square.
453     float weight = all(lessThan(edgeInset, float2(2.0)))
454         // Circular distortion weighted by distance to inset corner
455         ? (2.0 - length(2.0 - edgeInset))
456         // Linear zoom, or single-axis compression outside of the inset
457         // area (if delta < 1)
458         : min(edgeInset.x, edgeInset.y);
459 
460     // Saturate before squaring so that negative weights are clamped to 0
461     // before squaring
462     weight = saturate(weight);
463     return src.eval(mix(coord, zoomCoord, weight*weight));
464 }
465 
466 //--- High Contrast ----------------------------------------------------------------
467 
468 $pure half3 $high_contrast_rgb_to_hsl(half3 c) {
469     half mx = max(max(c.r,c.g),c.b),
470          mn = min(min(c.r,c.g),c.b),
471           d = mx-mn,
472        invd = 1.0 / d,
473      g_lt_b = c.g < c.b ? 6.0 : 0.0;
474 
475     // We'd prefer to write these tests like `mx == c.r`, but on some GPUs, max(x,y) is
476     // not always equal to either x or y. So we use long form, c.r >= c.g && c.r >= c.b.
477     half h = (1/6.0) * (mx == mn                 ? 0.0 :
478         /*mx==c.r*/     c.r >= c.g && c.r >= c.b ? invd * (c.g - c.b) + g_lt_b :
479         /*mx==c.g*/     c.g >= c.b               ? invd * (c.b - c.r) + 2.0
480         /*mx==c.b*/                              : invd * (c.r - c.g) + 4.0);
481     half sum = mx+mn,
482            l = sum * 0.5,
483            s = mx == mn ? 0.0
484                         : d / (l > 0.5 ? 2.0 - sum : sum);
485     return half3(h,s,l);
486 }
487 
488 half3 sk_high_contrast(half3 color, half grayscale, half invertStyle, half contrast) {
489     if (grayscale == 1) {
490         color = dot(half3(0.2126, 0.7152, 0.0722), color).rrr;
491     }
492     if (invertStyle == 1) {  // brightness
493         color = 1.0 - color;
494     } else if (invertStyle == 2) {  // lightness
495         color = $high_contrast_rgb_to_hsl(color);
496         color.b = 1 - color.b;
497         color = $hsl_to_rgb(color);
498     }
499     return saturate(mix(half3(0.5), color, contrast));
500 }
501 
502 //--- Normal -----------------------------------------------------------------------
503 
504 $pure half3 $normal_filter(half3 alphaC0, half3 alphaC1, half3 alphaC2, half negSurfaceDepth) {
505     // The right column (or bottom row) terms of the Sobel filter. The left/top is just the
506     // negative, and the middle row/column is all 0s so those instructions are skipped.
507     const half3 kSobel = 0.25 * half3(1,2,1);
508     half3 alphaR0 = half3(alphaC0.x, alphaC1.x, alphaC2.x);
509     half3 alphaR2 = half3(alphaC0.z, alphaC1.z, alphaC2.z);
510     half nx = dot(kSobel, alphaC2) - dot(kSobel, alphaC0);
511     half ny = dot(kSobel, alphaR2) - dot(kSobel, alphaR0);
512     return normalize(half3(negSurfaceDepth * half2(nx, ny), 1));
513 }
514 
515 half4 sk_normal(shader alphaMap, float2 coord, float4 edgeBounds, half negSurfaceDepth) {
516    half3 alphaC0 = half3(
517         alphaMap.eval(clamp(coord + float2(-1,-1), edgeBounds.LT, edgeBounds.RB)).a,
518         alphaMap.eval(clamp(coord + float2(-1, 0), edgeBounds.LT, edgeBounds.RB)).a,
519         alphaMap.eval(clamp(coord + float2(-1, 1), edgeBounds.LT, edgeBounds.RB)).a);
520    half3 alphaC1 = half3(
521         alphaMap.eval(clamp(coord + float2( 0,-1), edgeBounds.LT, edgeBounds.RB)).a,
522         alphaMap.eval(clamp(coord + float2( 0, 0), edgeBounds.LT, edgeBounds.RB)).a,
523         alphaMap.eval(clamp(coord + float2( 0, 1), edgeBounds.LT, edgeBounds.RB)).a);
524    half3 alphaC2 = half3(
525         alphaMap.eval(clamp(coord + float2( 1,-1), edgeBounds.LT, edgeBounds.RB)).a,
526         alphaMap.eval(clamp(coord + float2( 1, 0), edgeBounds.LT, edgeBounds.RB)).a,
527         alphaMap.eval(clamp(coord + float2( 1, 1), edgeBounds.LT, edgeBounds.RB)).a);
528 
529    half mainAlpha = alphaC1.y; // offset = (0,0)
530    return half4($normal_filter(alphaC0, alphaC1, alphaC2, negSurfaceDepth), mainAlpha);
531 }
532 
533 //--- Lighting ---------------------------------------------------------------------
534 
535 $pure half3 $surface_to_light(half lightType, half3 lightPos, half3 lightDir, half3 coord) {
536     // Spot (> 0) and point (== 0) have the same equation
537     return lightType >= 0 ? normalize(lightPos - coord)
538                           : lightDir;
539 }
540 
541 $pure half $spotlight_scale(half3 lightDir, half3 surfaceToLight, half cosCutoffAngle,
542                             half spotFalloff) {
543     const half kConeAAThreshold = 0.016;
544     const half kConeScale = 1.0 / kConeAAThreshold;
545 
546     half cosAngle = -dot(surfaceToLight, lightDir);
547     if (cosAngle < cosCutoffAngle) {
548         return 0.0;
549     } else {
550         half scale = pow(cosAngle, spotFalloff);
551         return (cosAngle < cosCutoffAngle + kConeAAThreshold)
552                     ? scale * (cosAngle - cosCutoffAngle) * kConeScale
553                     : scale;
554     }
555 }
556 
557 $pure half4 $compute_lighting(half3 color, half shininess, half materialType, half lightType,
558                               half3 normal, half3 lightDir, half3 surfaceToLight,
559                               half cosCutoffAngle, half spotFalloff) {
560     // Point and distant light color contributions are constant, but
561     // spotlights fade based on the angle away from its direction.
562     if (lightType > 0) {
563         color *= $spotlight_scale(lightDir, surfaceToLight, cosCutoffAngle, spotFalloff);
564     }
565 
566     // Diffuse and specular reflections scale the light's color differently
567     if (materialType == 0) {
568         half coeff = dot(normal, surfaceToLight);
569         color = saturate(coeff * color);
570         return half4(color, 1.0);
571     } else {
572         half3 halfDir = normalize(surfaceToLight + half3(0, 0, 1));
573         half coeff = pow(dot(normal, halfDir), shininess);
574         color = saturate(coeff * color);
575         return half4(color, max(max(color.r, color.g), color.b));
576     }
577 }
578 
579 half4 sk_lighting(shader normalMap, float2 coord,
580                   half depth, half shininess, half materialType, half lightType,
581                   half3 lightPos, half spotFalloff,
582                   half3 lightDir, half cosCutoffAngle,
583                   half3 lightColor) {
584     half4 normalAndA = normalMap.eval(coord);
585     half3 surfaceToLight = $surface_to_light(lightType, lightPos, lightDir,
586                                              half3(coord, depth * normalAndA.a));
587     return $compute_lighting(lightColor, shininess, materialType, lightType, normalAndA.xyz,
588                              lightDir, surfaceToLight, cosCutoffAngle, spotFalloff);
589 }
590 
591 //--- Arithmetic Blend -------------------------------------------------------------
592 
593 half4 sk_arithmetic_blend(half4 src, half4 dst, half4 k, half pmClamp) {
594     half4 color = saturate(k.x * src * dst + k.y * src + k.z * dst + k.w);
595     color.rgb = min(color.rgb, max(color.a, pmClamp));
596     return color;
597 }
598 
599 //--- Sparse Morphology ------------------------------------------------------------
600 
601 half4 sk_sparse_morphology(shader child, float2 coord, half2 offset, half flip) {
602     half4 aggregate = max(flip * child.eval(coord + offset),
603                           flip * child.eval(coord - offset));
604     return flip * aggregate;
605 }
606 
607 //--- Linear Morphology ------------------------------------------------------------
608 
609 half4 sk_linear_morphology(shader child, float2 coord, half2 offset, half flip, int radius) {
610 
611     // KEEP IN SYNC WITH CONSTANT IN `SkMorphologyImageFilter.cpp`
612     const int kMaxLinearRadius = 14;
613 
614     half4 aggregate = flip * child.eval(coord); // case 0 only needs a single sample
615     half2 delta = offset;
616     for (int i = 1; i <= kMaxLinearRadius; ++i) {
617         if (i > radius) break;
618         aggregate = max(aggregate, max(flip * child.eval(coord + delta),
619                                        flip * child.eval(coord - delta)));
620         delta += offset;
621     }
622     return flip * aggregate;
623 }
624 
625 //--- Overdraw ---------------------------------------------------------------------
626 
627 half4 sk_overdraw(half  alpha,  half4 color0, half4 color1, half4 color2,
628                   half4 color3, half4 color4, half4 color5) {
629    return alpha < (0.5 / 255.) ? color0
630         : alpha < (1.5 / 255.) ? color1
631         : alpha < (2.5 / 255.) ? color2
632         : alpha < (3.5 / 255.) ? color3
633         : alpha < (4.5 / 255.) ? color4
634                                : color5;
635 }
636 )"},
637     {"sksl_shared.sksl", R"(
638 // Intrinsics that are available to public SkSL (SkRuntimeEffect)
639 
640 // See "The OpenGL ES Shading Language, Section 8"
641 
642 // 8.1 : Angle and Trigonometry Functions
643 $pure $genType  radians($genType  degrees);
644 $pure $genHType radians($genHType degrees);
645 $pure $genType  degrees($genType  radians);
646 $pure $genHType degrees($genHType radians);
647 
648 $pure $genType  sin($genType  angle);
649 $pure $genHType sin($genHType angle);
650 $pure $genType  cos($genType  angle);
651 $pure $genHType cos($genHType angle);
652 $pure $genType  tan($genType  angle);
653 $pure $genHType tan($genHType angle);
654 
655 $pure $genType  asin($genType  x);
656 $pure $genHType asin($genHType x);
657 $pure $genType  acos($genType  x);
658 $pure $genHType acos($genHType x);
659 $pure $genType  atan($genType  y, $genType  x);
660 $pure $genHType atan($genHType y, $genHType x);
661 $pure $genType  atan($genType  y_over_x);
662 $pure $genHType atan($genHType y_over_x);
663 
664 // 8.1 : Angle and Trigonometry Functions (GLSL ES 3.0)
665 $pure $es3 $genType  sinh($genType x);
666 $pure $es3 $genHType sinh($genHType x);
667 $pure $es3 $genType  cosh($genType x);
668 $pure $es3 $genHType cosh($genHType x);
669 $pure $es3 $genType  tanh($genType x);
670 $pure $es3 $genHType tanh($genHType x);
671 $pure $es3 $genType  asinh($genType x);
672 $pure $es3 $genHType asinh($genHType x);
673 $pure $es3 $genType  acosh($genType x);
674 $pure $es3 $genHType acosh($genHType x);
675 $pure $es3 $genType  atanh($genType x);
676 $pure $es3 $genHType atanh($genHType x);
677 
678 // 8.2 : Exponential Functions
679 $pure $genType  pow($genType  x, $genType  y);
680 $pure $genHType pow($genHType x, $genHType y);
681 $pure $genType  exp($genType  x);
682 $pure $genHType exp($genHType x);
683 $pure $genType  log($genType  x);
684 $pure $genHType log($genHType x);
685 $pure $genType  exp2($genType  x);
686 $pure $genHType exp2($genHType x);
687 $pure $genType  log2($genType  x);
688 $pure $genHType log2($genHType x);
689 
690 $pure $genType  sqrt($genType  x);
691 $pure $genHType sqrt($genHType x);
692 $pure $genType  inversesqrt($genType  x);
693 $pure $genHType inversesqrt($genHType x);
694 
695 // 8.3 : Common Functions
696 $pure $genType  abs($genType  x);
697 $pure $genHType abs($genHType x);
698 $pure $genType  sign($genType  x);
699 $pure $genHType sign($genHType x);
700 $pure $genType  floor($genType  x);
701 $pure $genHType floor($genHType x);
702 $pure $genType  ceil($genType  x);
703 $pure $genHType ceil($genHType x);
704 $pure $genType  fract($genType  x);
705 $pure $genHType fract($genHType x);
706 $pure $genType  mod($genType  x, float     y);
707 $pure $genType  mod($genType  x, $genType  y);
708 $pure $genHType mod($genHType x, half      y);
709 $pure $genHType mod($genHType x, $genHType y);
710 
711 $pure $genType  min($genType  x, $genType  y);
712 $pure $genType  min($genType  x, float     y);
713 $pure $genHType min($genHType x, $genHType y);
714 $pure $genHType min($genHType x, half      y);
715 $pure $genType  max($genType  x, $genType  y);
716 $pure $genType  max($genType  x, float     y);
717 $pure $genHType max($genHType x, $genHType y);
718 $pure $genHType max($genHType x, half      y);
719 $pure $genType  clamp($genType  x, $genType  minVal, $genType  maxVal);
720 $pure $genType  clamp($genType  x, float     minVal, float     maxVal);
721 $pure $genHType clamp($genHType x, $genHType minVal, $genHType maxVal);
722 $pure $genHType clamp($genHType x, half      minVal, half      maxVal);
723 $pure $genType  saturate($genType  x);  // SkSL extension
724 $pure $genHType saturate($genHType x);  // SkSL extension
725 $pure $genType  mix($genType  x, $genType  y, $genType a);
726 $pure $genType  mix($genType  x, $genType  y, float a);
727 $pure $genHType mix($genHType x, $genHType y, $genHType a);
728 $pure $genHType mix($genHType x, $genHType y, half a);
729 $pure $genType  step($genType  edge, $genType x);
730 $pure $genType  step(float     edge, $genType x);
731 $pure $genHType step($genHType edge, $genHType x);
732 $pure $genHType step(half      edge, $genHType x);
733 $pure $genType  smoothstep($genType  edge0, $genType  edge1, $genType  x);
734 $pure $genType  smoothstep(float     edge0, float     edge1, $genType  x);
735 $pure $genHType smoothstep($genHType edge0, $genHType edge1, $genHType x);
736 $pure $genHType smoothstep(half      edge0, half      edge1, $genHType x);
737 
738 // 8.3 : Common Functions (GLSL ES 3.0)
739 $pure $es3 $genIType abs($genIType x);
740 $pure $es3 $genIType sign($genIType x);
741 $pure $es3 $genIType floatBitsToInt ($genType  value);
742 $pure $es3 $genUType floatBitsToUint($genType  value);
743 $pure $es3 $genType  intBitsToFloat ($genIType value);
744 $pure $es3 $genType  uintBitsToFloat($genUType value);
745 $pure $es3 $genType  trunc($genType  x);
746 $pure $es3 $genHType trunc($genHType x);
747 $pure $es3 $genType  round($genType  x);
748 $pure $es3 $genHType round($genHType x);
749 $pure $es3 $genType  roundEven($genType  x);
750 $pure $es3 $genHType roundEven($genHType x);
751 $pure $es3 $genIType min($genIType x, $genIType y);
752 $pure $es3 $genIType min($genIType x, int y);
753 $pure $es3 $genUType min($genUType x, $genUType y);
754 $pure $es3 $genUType min($genUType x, uint y);
755 $pure $es3 $genIType max($genIType x, $genIType y);
756 $pure $es3 $genIType max($genIType x, int y);
757 $pure $es3 $genUType max($genUType x, $genUType y);
758 $pure $es3 $genUType max($genUType x, uint y);
759 $pure $es3 $genIType clamp($genIType x, $genIType minVal, $genIType maxVal);
760 $pure $es3 $genIType clamp($genIType x, int minVal, int maxVal);
761 $pure $es3 $genUType clamp($genUType x, $genUType minVal, $genUType maxVal);
762 $pure $es3 $genUType clamp($genUType x, uint minVal, uint maxVal);
763 $pure $es3 $genType  mix($genType  x, $genType  y, $genBType a);
764 $pure $es3 $genHType mix($genHType x, $genHType y, $genBType a);
765 
766 // 8.3 : Common Functions (GLSL ES 3.0) -- cannot be used in constant-expressions
767 $pure $es3 $genBType isnan($genType  x);
768 $pure $es3 $genBType isnan($genHType x);
769 $pure $es3 $genBType isinf($genType  x);
770 $pure $es3 $genBType isinf($genHType x);
771       $es3 $genType  modf($genType  x, out $genType  i);
772       $es3 $genHType modf($genHType x, out $genHType i);
773 
774 // 8.4 : Floating-Point Pack and Unpack Functions (GLSL ES 3.0)
775 $pure $es3 uint packUnorm2x16(float2 v);
776 $pure $es3 float2 unpackUnorm2x16(uint p);
777 
778 // 8.5 : Geometric Functions
779 $pure float length($genType  x);
780 $pure half  length($genHType x);
781 $pure float distance($genType  p0, $genType  p1);
782 $pure half  distance($genHType p0, $genHType p1);
783 $pure float dot($genType  x, $genType  y);
784 $pure half  dot($genHType x, $genHType y);
785 $pure float3 cross(float3 x, float3 y);
786 $pure half3  cross(half3  x, half3  y);
787 $pure $genType  normalize($genType  x);
788 $pure $genHType normalize($genHType x);
789 $pure $genType  faceforward($genType  N, $genType  I, $genType  Nref);
790 $pure $genHType faceforward($genHType N, $genHType I, $genHType Nref);
791 $pure $genType  reflect($genType  I, $genType  N);
792 $pure $genHType reflect($genHType I, $genHType N);
793 $pure $genType  refract($genType  I, $genType  N, float eta);
794 $pure $genHType refract($genHType I, $genHType N, half eta);
795 
796 // 8.6 : Matrix Functions
797 $pure $squareMat  matrixCompMult($squareMat  x, $squareMat  y);
798 $pure $squareHMat matrixCompMult($squareHMat x, $squareHMat y);
799 $pure $es3 $mat   matrixCompMult($mat x, $mat y);
800 $pure $es3 $hmat  matrixCompMult($hmat x, $hmat y);
801 
802 // 8.6 : Matrix Functions (GLSL 1.4, poly-filled by SkSL as needed)
803 $pure $squareMat  inverse($squareMat  m);
804 $pure $squareHMat inverse($squareHMat m);
805 
806 // 8.6 : Matrix Functions (GLSL ES 3.0)
807 $pure $es3 float       determinant($squareMat m);
808 $pure $es3 half        determinant($squareHMat m);
809 $pure $es3 $squareMat  transpose($squareMat  m);
810 $pure $es3 $squareHMat transpose($squareHMat m);
811 $pure $es3 float2x3    transpose(float3x2 m);
812 $pure $es3 half2x3     transpose(half3x2  m);
813 $pure $es3 float2x4    transpose(float4x2 m);
814 $pure $es3 half2x4     transpose(half4x2  m);
815 $pure $es3 float3x2    transpose(float2x3 m);
816 $pure $es3 half3x2     transpose(half2x3  m);
817 $pure $es3 float3x4    transpose(float4x3 m);
818 $pure $es3 half3x4     transpose(half4x3  m);
819 $pure $es3 float4x2    transpose(float2x4 m);
820 $pure $es3 half4x2     transpose(half2x4  m);
821 $pure $es3 float4x3    transpose(float3x4 m);
822 $pure $es3 half4x3     transpose(half3x4  m);
823 $pure $es3 $squareMat  outerProduct($vec   c, $vec   r);
824 $pure $es3 $squareHMat outerProduct($hvec  c, $hvec  r);
825 $pure $es3 float2x3    outerProduct(float3 c, float2 r);
826 $pure $es3 half2x3     outerProduct(half3  c, half2  r);
827 $pure $es3 float3x2    outerProduct(float2 c, float3 r);
828 $pure $es3 half3x2     outerProduct(half2  c, half3  r);
829 $pure $es3 float2x4    outerProduct(float4 c, float2 r);
830 $pure $es3 half2x4     outerProduct(half4  c, half2  r);
831 $pure $es3 float4x2    outerProduct(float2 c, float4 r);
832 $pure $es3 half4x2     outerProduct(half2  c, half4  r);
833 $pure $es3 float3x4    outerProduct(float4 c, float3 r);
834 $pure $es3 half3x4     outerProduct(half4  c, half3  r);
835 $pure $es3 float4x3    outerProduct(float3 c, float4 r);
836 $pure $es3 half4x3     outerProduct(half3  c, half4  r);
837 
838 // 8.7 : Vector Relational Functions
839 $pure $bvec lessThan($vec  x, $vec  y);
840 $pure $bvec lessThan($hvec x, $hvec y);
841 $pure $bvec lessThan($ivec x, $ivec y);
842 $pure $bvec lessThan($svec x, $svec y);
843 $pure $bvec lessThanEqual($vec  x, $vec  y);
844 $pure $bvec lessThanEqual($hvec x, $hvec y);
845 $pure $bvec lessThanEqual($ivec x, $ivec y);
846 $pure $bvec lessThanEqual($svec x, $svec y);
847 $pure $bvec greaterThan($vec  x, $vec  y);
848 $pure $bvec greaterThan($hvec x, $hvec y);
849 $pure $bvec greaterThan($ivec x, $ivec y);
850 $pure $bvec greaterThan($svec x, $svec y);
851 $pure $bvec greaterThanEqual($vec  x, $vec  y);
852 $pure $bvec greaterThanEqual($hvec x, $hvec y);
853 $pure $bvec greaterThanEqual($ivec x, $ivec y);
854 $pure $bvec greaterThanEqual($svec x, $svec y);
855 $pure $bvec equal($vec  x, $vec  y);
856 $pure $bvec equal($hvec x, $hvec y);
857 $pure $bvec equal($ivec x, $ivec y);
858 $pure $bvec equal($svec x, $svec y);
859 $pure $bvec equal($bvec x, $bvec y);
860 $pure $bvec notEqual($vec  x, $vec  y);
861 $pure $bvec notEqual($hvec x, $hvec y);
862 $pure $bvec notEqual($ivec x, $ivec y);
863 $pure $bvec notEqual($svec x, $svec y);
864 $pure $bvec notEqual($bvec x, $bvec y);
865 
866 $pure $es3 $bvec lessThan($usvec x, $usvec y);
867 $pure $es3 $bvec lessThan($uvec x, $uvec y);
868 $pure $es3 $bvec lessThanEqual($uvec x, $uvec y);
869 $pure $es3 $bvec lessThanEqual($usvec x, $usvec y);
870 $pure $es3 $bvec greaterThan($uvec x, $uvec y);
871 $pure $es3 $bvec greaterThan($usvec x, $usvec y);
872 $pure $es3 $bvec greaterThanEqual($uvec x, $uvec y);
873 $pure $es3 $bvec greaterThanEqual($usvec x, $usvec y);
874 $pure $es3 $bvec equal($uvec x, $uvec y);
875 $pure $es3 $bvec equal($usvec x, $usvec y);
876 $pure $es3 $bvec notEqual($uvec x, $uvec y);
877 $pure $es3 $bvec notEqual($usvec x, $usvec y);
878 
879 $pure bool  any($bvec x);
880 $pure bool  all($bvec x);
881 $pure $bvec not($bvec x);
882 
883 // 8.9 : Fragment Processing Functions (GLSL ES 3.0)
884 $pure $es3 $genType  dFdx($genType p);
885 $pure $es3 $genType  dFdy($genType p);
886 $pure $es3 $genHType dFdx($genHType p);
887 $pure $es3 $genHType dFdy($genHType p);
888 $pure $es3 $genType  fwidth($genType p);
889 $pure $es3 $genHType fwidth($genHType p);
890 
891 
892 // SkSL utility functions
893 
894 // The max() guards against division by zero when the incoming color is transparent black
895 $pure half4  unpremul(half4  color) { return half4 (color.rgb / max(color.a, 0.0001), color.a); }
896 $pure float4 unpremul(float4 color) { return float4(color.rgb / max(color.a, 0.0001), color.a); }
897 
898 // Similar, but used for polar-space CSS colors
899 $export $pure half4 $unpremul_polar(half4 color) {
900     return half4(color.r, color.gb / max(color.a, 0.0001), color.a);
901 }
902 
903 // Convert RGBA -> HSLA (including unpremul).
904 //
905 // Based on work by Sam Hocevar, Emil Persson, and Ian Taylor [1][2][3].  High-level ideas:
906 //
907 //   - minimize the number of branches by sorting and computing the hue phase in parallel (vec4s)
908 //
909 //   - trade the third sorting branch for a potentially faster std::min and leaving 2nd/3rd
910 //     channels unsorted (based on the observation that swapping both the channels and the bias sign
911 //     has no effect under abs)
912 //
913 //   - use epsilon offsets for denominators, to avoid explicit zero-checks
914 //
915 // An additional trick we employ is deferring premul->unpremul conversion until the very end: the
916 // alpha factor gets naturally simplified for H and S, and only L requires a dedicated unpremul
917 // division (so we trade three divs for one).
918 //
919 // [1] http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
920 // [2] http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
921 // [3] http://www.chilliant.com/rgb2hsv.html
922 
923 $export $pure half4 $rgb_to_hsl(half3 c, half a) {
924     half4 p = (c.g < c.b) ? half4(c.bg, -1,  2/3.0)
925                           : half4(c.gb,  0, -1/3.0);
926     half4 q = (c.r < p.x) ? half4(p.x, c.r, p.yw)
927                           : half4(c.r, p.x, p.yz);
928 
929     // q.x  -> max channel value
930     // q.yz -> 2nd/3rd channel values (unsorted)
931     // q.w  -> bias value dependent on max channel selection
932 
933     const half kEps = 0.0001;
934     half pmV = q.x;
935     half pmC = pmV - min(q.y, q.z);
936     half pmL = pmV - pmC * 0.5;
937     half   H = abs(q.w + (q.y - q.z) / (pmC * 6 + kEps));
938     half   S = pmC / (a + kEps - abs(pmL * 2 - a));
939     half   L = pmL / (a + kEps);
940 
941     return half4(H, S, L, a);
942 }
943 
944 // Convert HSLA -> RGBA (including clamp and premul).
945 //
946 // Based on work by Sam Hocevar, Emil Persson, and Ian Taylor [1][2][3].
947 //
948 // [1] http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
949 // [2] http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
950 // [3] http://www.chilliant.com/rgb2hsv.html
951 
952 $export $pure half3 $hsl_to_rgb(half3 hsl) {
953     half      C = (1 - abs(2 * hsl.z - 1)) * hsl.y;
954     half3     p = hsl.xxx + half3(0, 2/3.0, 1/3.0);
955     half3     q = saturate(abs(fract(p) * 6 - 3) - 1);
956 
957     return (q - 0.5) * C + hsl.z;
958 }
959 
960 $export $pure half4 $hsl_to_rgb(half3 hsl, half a) {
961     return saturate(half4($hsl_to_rgb(hsl) * a, a));
962 }
963 
964 // Color conversion functions used in gradient interpolation, based on
965 // https://www.w3.org/TR/css-color-4/#color-conversion-code
966 // TODO(skia:13108): For all of these, we can eliminate any linear math at the beginning
967 // (by removing the corresponding linear math at the end of the CPU code).
968 $export $pure half3 $css_lab_to_xyz(half3 lab) {
969     const half k = 24389 / 27.0;
970     const half e = 216 / 24389.0;
971 
972     half3 f;
973     f[1] = (lab[0] + 16) / 116;
974     f[0] = (lab[1] / 500) + f[1];
975     f[2] = f[1] - (lab[2] / 200);
976 
977     half3 f_cubed = pow(f, half3(3));
978 
979     half3 xyz = half3(
980         f_cubed[0] > e ? f_cubed[0] : (116 * f[0] - 16) / k,
981         lab[0] > k * e ? f_cubed[1] : lab[0] / k,
982         f_cubed[2] > e ? f_cubed[2] : (116 * f[2] - 16) / k
983     );
984 
985     const half3 D50 = half3(0.3457 / 0.3585, 1.0, (1.0 - 0.3457 - 0.3585) / 0.3585);
986     return xyz * D50;
987 }
988 
989 // Skia stores all polar colors with hue in the first component, so this "LCH -> Lab" transform
990 // actually takes "HCL". This is also used to do the same polar transform for OkHCL to OkLAB.
991 // See similar comments & logic in SkGradientShaderBase.cpp.
992 $pure half3 $css_hcl_to_lab(half3 hcl) {
993     return half3(
994         hcl[2],
995         hcl[1] * cos(radians(hcl[0])),
996         hcl[1] * sin(radians(hcl[0]))
997     );
998 }
999 
1000 $export $pure half3 $css_hcl_to_xyz(half3 hcl) {
1001     return $css_lab_to_xyz($css_hcl_to_lab(hcl));
1002 }
1003 
1004 $export $pure half3 $css_oklab_to_linear_srgb(half3 oklab) {
1005     half l_ = oklab.x + 0.3963377774 * oklab.y + 0.2158037573 * oklab.z,
1006          m_ = oklab.x - 0.1055613458 * oklab.y - 0.0638541728 * oklab.z,
1007          s_ = oklab.x - 0.0894841775 * oklab.y - 1.2914855480 * oklab.z;
1008 
1009     half l = l_*l_*l_,
1010          m = m_*m_*m_,
1011          s = s_*s_*s_;
1012 
1013     return half3(
1014         +4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
1015         -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
1016         -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s
1017     );
1018 }
1019 
1020 $export $pure half3 $css_okhcl_to_linear_srgb(half3 okhcl) {
1021     return $css_oklab_to_linear_srgb($css_hcl_to_lab(okhcl));
1022 }
1023 
1024 $export $pure half3 $css_oklab_gamut_map_to_linear_srgb(half3 oklab) {
1025     // Constants for the normal vector of the plane formed by white, black, and
1026     // the specified vertex of the gamut.
1027     const half2 normal_R = half2(0.409702, -0.912219);
1028     const half2 normal_M = half2(-0.397919, -0.917421);
1029     const half2 normal_B = half2(-0.906800, 0.421562);
1030     const half2 normal_C = half2(-0.171122, 0.985250);
1031     const half2 normal_G = half2(0.460276, 0.887776);
1032     const half2 normal_Y = half2(0.947925, 0.318495);
1033 )"
1034 R"(
1035     // For the triangles formed by white (W) or black (K) with the vertices
1036     // of Yellow and Red (YR), Red and Magenta (RM), etc, the constants to be
1037     // used to compute the intersection of a line of constant hue and luminance
1038     // with that plane.
1039     const half  c0_YR = 0.091132;
1040     const half2 cW_YR = half2(0.070370, 0.034139);
1041     const half2 cK_YR = half2(0.018170, 0.378550);
1042     const half  c0_RM = 0.113902;
1043     const half2 cW_RM = half2(0.090836, 0.036251);
1044     const half2 cK_RM = half2(0.226781, 0.018764);
1045     const half  c0_MB = 0.161739;
1046     const half2 cW_MB = half2(-0.008202, -0.264819);
1047     const half2 cK_MB = half2( 0.187156, -0.284304);
1048     const half  c0_BC = 0.102047;
1049     const half2 cW_BC = half2(-0.014804, -0.162608);
1050     const half2 cK_BC = half2(-0.276786,  0.004193);
1051     const half  c0_CG = 0.092029;
1052     const half2 cW_CG = half2(-0.038533, -0.001650);
1053     const half2 cK_CG = half2(-0.232572, -0.094331);
1054     const half  c0_GY = 0.081709;
1055     const half2 cW_GY = half2(-0.034601, -0.002215);
1056     const half2 cK_GY = half2( 0.012185,  0.338031);
1057 
1058     half2 ab = oklab.yz;
1059 
1060     // Find the planes to intersect with and set the constants based on those
1061     // planes.
1062     half c0;
1063     half2 cW;
1064     half2 cK;
1065     if (dot(ab, normal_R) < 0.0) {
1066         if (dot(ab, normal_G) < 0.0) {
1067             if (dot(ab, normal_C) < 0.0) {
1068                 c0 = c0_BC; cW = cW_BC; cK = cK_BC;
1069             } else {
1070                 c0 = c0_CG; cW = cW_CG; cK = cK_CG;
1071             }
1072         } else {
1073             if (dot(ab, normal_Y) < 0.0) {
1074                 c0 = c0_GY; cW = cW_GY; cK = cK_GY;
1075             } else {
1076                 c0 = c0_YR; cW = cW_YR; cK = cK_YR;
1077             }
1078         }
1079     } else {
1080         if (dot(ab, normal_B) < 0.0) {
1081             if (dot(ab, normal_M) < 0.0) {
1082                 c0 = c0_RM; cW = cW_RM; cK = cK_RM;
1083             } else {
1084                 c0 = c0_MB; cW = cW_MB; cK = cK_MB;
1085             }
1086         } else {
1087             c0 = c0_BC; cW = cW_BC; cK = cK_BC;
1088         }
1089     }
1090 
1091     // Perform the intersection.
1092     half alpha = 1.0;
1093 
1094     // Intersect with the plane with white.
1095     half w_denom = dot(cW, ab);
1096     if (w_denom > 0.0) {
1097         half one_minus_L = 1.0 - oklab.r;
1098         half w_num = c0*one_minus_L;
1099         if (w_num < w_denom) {
1100             alpha = min(alpha, w_num / w_denom);
1101         }
1102     }
1103 
1104     // Intersect with the plane with black.
1105     half k_denom = dot(cK, ab);
1106     if (k_denom > 0.0) {
1107         half L = oklab.r;
1108         half k_num = c0*L;
1109         if (k_num < k_denom) {
1110             alpha = min(alpha,  k_num / k_denom);
1111         }
1112     }
1113 
1114     // Attenuate the ab coordinate by alpha.
1115     oklab.yz *= alpha;
1116 
1117     return $css_oklab_to_linear_srgb(oklab);
1118 }
1119 
1120 $export $pure half3 $css_okhcl_gamut_map_to_linear_srgb(half3 okhcl) {
1121     return $css_oklab_gamut_map_to_linear_srgb($css_hcl_to_lab(okhcl));
1122 }
1123 
1124 // TODO(skia:13108): Use our optimized version (though it has different range)
1125 // Doing so might require fixing (re-deriving?) the math for the HWB version below
1126 $export $pure half3 $css_hsl_to_srgb(half3 hsl) {
1127     hsl.x = mod(hsl.x, 360);
1128     if (hsl.x < 0) {
1129         hsl.x += 360;
1130     }
1131 
1132     hsl.yz /= 100;
1133 
1134     half3 k = mod(half3(0, 8, 4) + hsl.x/30, 12);
1135     half a = hsl.y * min(hsl.z, 1 - hsl.z);
1136     return hsl.z - a * clamp(min(k - 3, 9 - k), -1, 1);
1137 }
1138 
1139 $export $pure half3 $css_hwb_to_srgb(half3 hwb) {
1140     half3 rgb;
1141     hwb.yz /= 100;
1142     if (hwb.y + hwb.z >= 1) {
1143         // Emit grayscale
1144         rgb = half3(hwb.y / (hwb.y + hwb.z));
1145     } else {
1146         rgb = $css_hsl_to_srgb(half3(hwb.x, 100, 50));
1147         rgb *= (1 - hwb.y - hwb.z);
1148         rgb += hwb.y;
1149     }
1150     return rgb;
1151 }
1152 
1153 /*
1154  * The actual output color space of this function depends on the input color space
1155  * (it might be sRGB, linear sRGB, or linear XYZ). The actual space is what's stored
1156  * in the gradient/SkColor4fXformer's fIntermediateColorSpace.
1157  */
1158 $export $pure half4 $interpolated_to_rgb_unpremul(half4 color, int colorSpace, int doUnpremul) {
1159     const int kDestination   = 0;
1160     const int kSRGBLinear    = 1;
1161     const int kLab           = 2;
1162     const int kOKLab         = 3;
1163     const int kOKLabGamutMap = 4;
1164     const int kLCH           = 5;
1165     const int kOKLCH         = 6;
1166     const int kOKLCHGamutMap = 7;
1167     const int kSRGB          = 8;
1168     const int kHSL           = 9;
1169     const int kHWB           = 10;
1170 
1171     if (bool(doUnpremul)) {
1172         switch (colorSpace) {
1173             case kLab:
1174             case kOKLab:
1175             case kOKLabGamutMap: color = unpremul(color); break;
1176             case kLCH:
1177             case kOKLCH:
1178             case kOKLCHGamutMap:
1179             case kHSL:
1180             case kHWB: color = $unpremul_polar(color); break;
1181         }
1182     }
1183     switch (colorSpace) {
1184         case kLab:           color.rgb = $css_lab_to_xyz(color.rgb); break;
1185         case kOKLab:         color.rgb = $css_oklab_to_linear_srgb(color.rgb); break;
1186         case kOKLabGamutMap: color.rgb = $css_oklab_gamut_map_to_linear_srgb(color.rgb); break;
1187         case kLCH:           color.rgb = $css_hcl_to_xyz(color.rgb); break;
1188         case kOKLCH:         color.rgb = $css_okhcl_to_linear_srgb(color.rgb); break;
1189         case kOKLCHGamutMap: color.rgb = $css_okhcl_gamut_map_to_linear_srgb(color.rgb); break;
1190         case kHSL:           color.rgb = $css_hsl_to_srgb(color.rgb); break;
1191         case kHWB:           color.rgb = $css_hwb_to_srgb(color.rgb); break;
1192     }
1193     return color;
1194 }
1195 )"}
1196 };
1197