| layout(builtin=15) float4 sk_FragCoord; |
| |
| // Color conversion functions used in gradient interpolation, based on |
| // https://www.w3.org/TR/css-color-4/#color-conversion-code |
| // TODO(skia:13108): For all of these, we can eliminate any linear math at the beginning |
| // (by removing the corresponding linear math at the end of the CPU code). |
| $pure half3 $css_Lab_to_XYZ(half3 Lab) { |
| const half k = 24389 / 27.0; |
| const half e = 216 / 24389.0; |
| |
| half3 f; |
| f[1] = (Lab[0] + 16) / 116; |
| f[0] = (Lab[1] / 500) + f[1]; |
| f[2] = f[1] - (Lab[2] / 200); |
| |
| half3 f_cubed = pow(f, half3(3)); |
| |
| half3 xyz = half3( |
| f_cubed[0] > e ? f_cubed[0] : (116 * f[0] - 16) / k, |
| Lab[0] > k * e ? f_cubed[1] : Lab[0] / k, |
| f_cubed[2] > e ? f_cubed[2] : (116 * f[2] - 16) / k |
| ); |
| |
| const half3 D50 = half3(0.3457 / 0.3585, 1.0, (1.0 - 0.3457 - 0.3585) / 0.3585); |
| return xyz * D50; |
| } |
| |
| // Also used by OKLCH -> OKLab |
| $pure half3 $css_LCH_to_Lab(half3 LCH) { |
| return half3( |
| LCH[0], |
| LCH[1] * cos(radians(LCH[2])), |
| LCH[1] * sin(radians(LCH[2])) |
| ); |
| } |
| |
| $pure half3 $css_LCH_to_XYZ(half3 LCH) { |
| return $css_Lab_to_XYZ($css_LCH_to_Lab(LCH)); |
| } |
| |
| $pure half3 $css_OKLab_to_XYZ(half3 OKLab) { |
| const half3x3 LMStoXYZ = half3x3( |
| 1.2268798733741557, -0.5578149965554813, 0.28139105017721583, |
| -0.04057576262431372, 1.1122868293970594, -0.07171106666151701, |
| -0.07637294974672142, -0.4214933239627914, 1.5869240244272418 |
| ); |
| // TODO(skia:13794): Switch this back to half3x3 when it no longer triggers sk_Caps errors |
| const float3x3 OKLabToLMS = half3x3( |
| 0.99999999845051981432, 0.39633779217376785678, 0.21580375806075880339, |
| 1.0000000088817607767, -0.1055613423236563494, -0.063854174771705903402, |
| 1.0000000546724109177, -0.089484182094965759684, -1.2914855378640917399 |
| ); |
| half3 LMSnl = half3(OKLabToLMS * OKLab); |
| return LMStoXYZ * pow(LMSnl, half3(3)); |
| } |
| |
| $pure half3 $css_OKLCH_to_XYZ(half3 OKLCH) { |
| return $css_OKLab_to_XYZ($css_LCH_to_Lab(OKLCH)); |
| } |
| |
| // TODO(skia:13108): Use our optimized version (though it has different range) |
| // Doing so might require fixing (re-deriving?) the math for the HWB version below |
| $pure half3 $css_hsl_to_sRGB(half3 hsl) { |
| hsl.x = mod(hsl.x, 360); |
| if (hsl.x < 0) { |
| hsl.x += 360; |
| } |
| |
| hsl.yz /= 100; |
| |
| half3 k = mod(half3(0, 8, 4) + hsl.x/30, 12); |
| half a = hsl.y * min(hsl.z, 1 - hsl.z); |
| return hsl.z - a * clamp(min(k - 3, 9 - k), -1, 1); |
| } |
| |
| $pure half3 $css_hwb_to_sRGB(half3 hwb) { |
| hwb.yz /= 100; |
| if (hwb.y + hwb.z >= 1) { |
| half gray = hwb.y / (hwb.y + hwb.z); |
| return half3(gray); |
| } |
| half3 rgb = $css_hsl_to_sRGB(half3(hwb.x, 100, 50)); |
| rgb *= (1 - hwb.y - hwb.z); |
| rgb += hwb.y; |
| return rgb; |
| } |