| static const char *hb_gpu_fragment_wgsl = |
| "/*\n" |
| " * Copyright (C) 2026 Behdad Esfahbod\n" |
| " * Copyright (C) 2017 Eric Lengyel\n" |
| " *\n" |
| " * Based on the Slug algorithm by Eric Lengyel:\n" |
| " * https://github.com/EricLengyel/Slug\n" |
| " *\n" |
| " * This is part of HarfBuzz, a text shaping library.\n" |
| " *\n" |
| " * Permission is hereby granted, without written agreement and without\n" |
| " * license or royalty fees, to use, copy, modify, and distribute this\n" |
| " * software and its documentation for any purpose, provided that the\n" |
| " * above copyright notice and the following two paragraphs appear in\n" |
| " * all copies of this software.\n" |
| " *\n" |
| " * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR\n" |
| " * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES\n" |
| " * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN\n" |
| " * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\n" |
| " * DAMAGE.\n" |
| " *\n" |
| " * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,\n" |
| " * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND\n" |
| " * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS\n" |
| " * ON AN \"AS IS\" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO\n" |
| " * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.\n" |
| " */\n" |
| "\n" |
| "\n" |
| "/* Requires WGSL (WebGPU Shading Language). */\n" |
| "\n" |
| "\n" |
| "const HB_GPU_UNITS_PER_EM: f32 = 4.0;\n" |
| "const HB_GPU_INV_UNITS: f32 = 1.0 / 4.0;\n" |
| "\n" |
| "\n" |
| "fn hb_gpu_fetch (hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>,\n" |
| " offset: i32) -> vec4<i32>\n" |
| "{\n" |
| " return (*hb_gpu_atlas)[offset];\n" |
| "}\n" |
| "\n" |
| "\n" |
| "fn _hb_gpu_calc_root_code (y1: f32, y2: f32, y3: f32) -> u32\n" |
| "{\n" |
| " let i1 = bitcast<u32> (y1) >> 31u;\n" |
| " let i2 = bitcast<u32> (y2) >> 30u;\n" |
| " let i3 = bitcast<u32> (y3) >> 29u;\n" |
| "\n" |
| " var shift = (i2 & 2u) | (i1 & ~2u);\n" |
| " shift = (i3 & 4u) | (shift & ~4u);\n" |
| "\n" |
| " return (0x2E74u >> shift) & 0x0101u;\n" |
| "}\n" |
| "\n" |
| "fn _hb_gpu_solve_horiz_poly (a: vec2f, b: vec2f, p1: vec2f) -> vec2f\n" |
| "{\n" |
| " let ra = 1.0 / a.y;\n" |
| " let rb = 0.5 / b.y;\n" |
| "\n" |
| " let d = sqrt (max (b.y * b.y - a.y * p1.y, 0.0));\n" |
| " var t1 = (b.y - d) * ra;\n" |
| " var t2 = (b.y + d) * ra;\n" |
| "\n" |
| " if (a.y == 0.0) {\n" |
| " t1 = p1.y * rb;\n" |
| " t2 = t1;\n" |
| " }\n" |
| "\n" |
| " return vec2f ((a.x * t1 - b.x * 2.0) * t1 + p1.x,\n" |
| " (a.x * t2 - b.x * 2.0) * t2 + p1.x);\n" |
| "}\n" |
| "\n" |
| "fn _hb_gpu_solve_vert_poly (a: vec2f, b: vec2f, p1: vec2f) -> vec2f\n" |
| "{\n" |
| " let ra = 1.0 / a.x;\n" |
| " let rb = 0.5 / b.x;\n" |
| "\n" |
| " let d = sqrt (max (b.x * b.x - a.x * p1.x, 0.0));\n" |
| " var t1 = (b.x - d) * ra;\n" |
| " var t2 = (b.x + d) * ra;\n" |
| "\n" |
| " if (a.x == 0.0) {\n" |
| " t1 = p1.x * rb;\n" |
| " t2 = t1;\n" |
| " }\n" |
| "\n" |
| " return vec2f ((a.y * t1 - b.y * 2.0) * t1 + p1.y,\n" |
| " (a.y * t2 - b.y * 2.0) * t2 + p1.y);\n" |
| "}\n" |
| "\n" |
| "fn _hb_gpu_calc_coverage (xcov: f32, ycov: f32, xwgt: f32, ywgt: f32) -> f32\n" |
| "{\n" |
| " let coverage = max (abs (xcov * xwgt + ycov * ywgt) /\n" |
| " max (xwgt + ywgt, 1.0 / 65536.0),\n" |
| " min (abs (xcov), abs (ycov)));\n" |
| "\n" |
| " return clamp (coverage, 0.0, 1.0);\n" |
| "}\n" |
| "\n" |
| "/* Decoded glyph band info for a pixel position. */\n" |
| "struct _hb_gpu_glyph_info\n" |
| "{\n" |
| " glyphLoc: i32,\n" |
| " bandBase: i32,\n" |
| " bandIndex: vec2<i32>,\n" |
| " numHBands: i32,\n" |
| " numVBands: i32,\n" |
| " scale: vec2f,\n" |
| "}\n" |
| "\n" |
| "fn _hb_gpu_decode_glyph (renderCoord: vec2f, glyphLoc_: u32,\n" |
| " hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> _hb_gpu_glyph_info\n" |
| "{\n" |
| " var gi: _hb_gpu_glyph_info;\n" |
| " gi.glyphLoc = i32 (glyphLoc_);\n" |
| "\n" |
| " let header0 = hb_gpu_fetch (hb_gpu_atlas, gi.glyphLoc);\n" |
| " let header1 = hb_gpu_fetch (hb_gpu_atlas, gi.glyphLoc + 1);\n" |
| " let ext = vec4f (header0) * HB_GPU_INV_UNITS;\n" |
| " gi.numHBands = header1.r;\n" |
| " gi.numVBands = header1.g;\n" |
| " gi.scale = vec2f (f32 (header1.b), f32 (header1.a));\n" |
| "\n" |
| " let extSize = ext.zw - ext.xy;\n" |
| " let bandScale = vec2f (f32 (gi.numVBands), f32 (gi.numHBands)) / max (extSize, vec2f (1.0 / 65536.0));\n" |
| " let bandOffset = -ext.xy * bandScale;\n" |
| "\n" |
| " gi.bandIndex = clamp (vec2<i32> (renderCoord * bandScale + bandOffset),\n" |
| " vec2<i32> (0, 0),\n" |
| " vec2<i32> (gi.numVBands - 1, gi.numHBands - 1));\n" |
| "\n" |
| " gi.bandBase = gi.glyphLoc + 2;\n" |
| " return gi;\n" |
| "}\n" |
| "\n" |
| "/* Return pixels per em at this fragment.\n" |
| " *\n" |
| " * renderCoord: em-space sample position\n" |
| " * glyphLoc: texel offset of glyph blob in atlas\n" |
| " */\n" |
| "fn hb_gpu_ppem (renderCoord: vec2f, glyphLoc_: u32,\n" |
| " hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> f32\n" |
| "{\n" |
| " let gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_, hb_gpu_atlas);\n" |
| " let emsPerPixel = fwidth (renderCoord);\n" |
| " return min (gi.scale.x, gi.scale.y) /\n" |
| " max (emsPerPixel.x, emsPerPixel.y);\n" |
| "}\n" |
| "\n" |
| "/* Return per-pixel curve counts: (horizontal, vertical). */\n" |
| "fn _hb_gpu_curve_counts (renderCoord: vec2f, glyphLoc_: u32,\n" |
| " hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> vec2<i32>\n" |
| "{\n" |
| " let gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_, hb_gpu_atlas);\n" |
| " let hCount = hb_gpu_fetch (hb_gpu_atlas, gi.bandBase + gi.bandIndex.y).r;\n" |
| " let vCount = hb_gpu_fetch (hb_gpu_atlas, gi.bandBase + gi.numHBands + gi.bandIndex.x).r;\n" |
| " return vec2<i32> (hCount, vCount);\n" |
| "}\n" |
| "\n" |
| "/* Single-sample coverage in [0, 1]. */\n" |
| "fn _hb_gpu_render_single (renderCoord: vec2f, pixelsPerEm: vec2f, glyphLoc_: u32,\n" |
| " hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> f32\n" |
| "{\n" |
| "\n" |
| " let gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_, hb_gpu_atlas);\n" |
| " let glyphLoc = gi.glyphLoc;\n" |
| " let bandBase = gi.bandBase;\n" |
| " let numHBands = gi.numHBands;\n" |
| "\n" |
| " var xcov: f32 = 0.0;\n" |
| " var xwgt: f32 = 0.0;\n" |
| "\n" |
| " let hbandData = hb_gpu_fetch (hb_gpu_atlas, bandBase + gi.bandIndex.y);\n" |
| " let hCurveCount = hbandData.r;\n" |
| " /* Symmetric: choose rightward (desc) or leftward (asc) sort */\n" |
| " let hSplit = f32 (hbandData.a) * HB_GPU_INV_UNITS;\n" |
| " let hLeftRay = (renderCoord.x < hSplit);\n" |
| " var hDataOffset: i32;\n" |
| " if (hLeftRay) { hDataOffset = hbandData.b + 32768; }\n" |
| " else { hDataOffset = hbandData.g + 32768; }\n" |
| "\n" |
| " for (var ci: i32 = 0; ci < hCurveCount; ci++)\n" |
| " {\n" |
| " let curveOffset = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + hDataOffset + ci).r + 32768;\n" |
| "\n" |
| " let raw12 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset);\n" |
| " let raw3 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset + 1);\n" |
| "\n" |
| " let q12 = vec4f (raw12) * HB_GPU_INV_UNITS;\n" |
| " let q3 = vec2f (vec2<i32> (raw3.r, raw3.g)) * HB_GPU_INV_UNITS;\n" |
| "\n" |
| " let p12 = q12 - vec4f (renderCoord, renderCoord);\n" |
| " let p3 = q3 - renderCoord;\n" |
| "\n" |
| " if (hLeftRay) {\n" |
| " if (min (min (p12.x, p12.z), p3.x) * pixelsPerEm.x > 0.5) { break; }\n" |
| " } else {\n" |
| " if (max (max (p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) { break; }\n" |
| " }\n" |
| "\n" |
| " let code = _hb_gpu_calc_root_code (p12.y, p12.w, p3.y);\n" |
| " if (code != 0u)\n" |
| " {\n" |
| " let a = q12.xy - q12.zw * 2.0 + q3;\n" |
| " let b = q12.xy - q12.zw;\n" |
| " let r = _hb_gpu_solve_horiz_poly (a, b, p12.xy) * pixelsPerEm.x;\n" |
| " /* For leftward ray: saturate(0.5 - r) counts coverage from the left */\n" |
| " var cov: vec2f;\n" |
| " if (hLeftRay) { cov = clamp (vec2f (0.5) - r, vec2f (0.0), vec2f (1.0)); }\n" |
| " else { cov = clamp (r + vec2f (0.5), vec2f (0.0), vec2f (1.0)); }\n" |
| "\n" |
| " if ((code & 1u) != 0u)\n" |
| " {\n" |
| " xcov += cov.x;\n" |
| " xwgt = max (xwgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0));\n" |
| " }\n" |
| "\n" |
| " if (code > 1u)\n" |
| " {\n" |
| " xcov -= cov.y;\n" |
| " xwgt = max (xwgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0));\n" |
| " }\n" |
| " }\n" |
| " }\n" |
| "\n" |
| " var ycov: f32 = 0.0;\n" |
| " var ywgt: f32 = 0.0;\n" |
| "\n" |
| " let vbandData = hb_gpu_fetch (hb_gpu_atlas, bandBase + numHBands + gi.bandIndex.x);\n" |
| " let vCurveCount = vbandData.r;\n" |
| " let vSplit = f32 (vbandData.a) * HB_GPU_INV_UNITS;\n" |
| " let vLeftRay = (renderCoord.y < vSplit);\n" |
| " var vDataOffset: i32;\n" |
| " if (vLeftRay) { vDataOffset = vbandData.b + 32768; }\n" |
| " else { vDataOffset = vbandData.g + 32768; }\n" |
| "\n" |
| " for (var ci: i32 = 0; ci < vCurveCount; ci++)\n" |
| " {\n" |
| " let curveOffset = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + vDataOffset + ci).r + 32768;\n" |
| "\n" |
| " let raw12 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset);\n" |
| " let raw3 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset + 1);\n" |
| "\n" |
| " let q12 = vec4f (raw12) * HB_GPU_INV_UNITS;\n" |
| " let q3 = vec2f (vec2<i32> (raw3.r, raw3.g)) * HB_GPU_INV_UNITS;\n" |
| "\n" |
| " let p12 = q12 - vec4f (renderCoord, renderCoord);\n" |
| " let p3 = q3 - renderCoord;\n" |
| "\n" |
| " if (vLeftRay) {\n" |
| " if (min (min (p12.y, p12.w), p3.y) * pixelsPerEm.y > 0.5) { break; }\n" |
| " } else {\n" |
| " if (max (max (p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) { break; }\n" |
| " }\n" |
| "\n" |
| " let code = _hb_gpu_calc_root_code (p12.x, p12.z, p3.x);\n" |
| " if (code != 0u)\n" |
| " {\n" |
| " let a = q12.xy - q12.zw * 2.0 + q3;\n" |
| " let b = q12.xy - q12.zw;\n" |
| " let r = _hb_gpu_solve_vert_poly (a, b, p12.xy) * pixelsPerEm.y;\n" |
| " var cov: vec2f;\n" |
| " if (vLeftRay) { cov = clamp (vec2f (0.5) - r, vec2f (0.0), vec2f (1.0)); }\n" |
| " else { cov = clamp (r + vec2f (0.5), vec2f (0.0), vec2f (1.0)); }\n" |
| "\n" |
| " if ((code & 1u) != 0u)\n" |
| " {\n" |
| " ycov -= cov.x;\n" |
| " ywgt = max (ywgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0));\n" |
| " }\n" |
| "\n" |
| " if (code > 1u)\n" |
| " {\n" |
| " ycov += cov.y;\n" |
| " ywgt = max (ywgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0));\n" |
| " }\n" |
| " }\n" |
| " }\n" |
| "\n" |
| " return _hb_gpu_calc_coverage (xcov, ycov, xwgt, ywgt);\n" |
| "}\n" |
| "\n" |
| "/* Return coverage in [0, 1].\n" |
| " *\n" |
| " * renderCoord: em-space sample position\n" |
| " * glyphLoc: texel offset of glyph blob in atlas\n" |
| " * hb_gpu_atlas: storage buffer pointer to the atlas\n" |
| " */\n" |
| "fn hb_gpu_render (renderCoord: vec2f, glyphLoc_: u32,\n" |
| " hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> f32\n" |
| "{\n" |
| " let emsPerPixel = fwidth (renderCoord);\n" |
| " let pixelsPerEm = 1.0 / emsPerPixel;\n" |
| "\n" |
| " var c = _hb_gpu_render_single (renderCoord, pixelsPerEm, glyphLoc_, hb_gpu_atlas);\n" |
| "\n" |
| " let ppem = hb_gpu_ppem (renderCoord, glyphLoc_, hb_gpu_atlas);\n" |
| "\n" |
| " if (ppem < 16.0)\n" |
| " {\n" |
| " let d = emsPerPixel * (1.0 / 3.0);\n" |
| " let msaa = 0.25 *\n" |
| " (_hb_gpu_render_single (renderCoord + vec2f (-d.x, -d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas) +\n" |
| " _hb_gpu_render_single (renderCoord + vec2f ( d.x, -d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas) +\n" |
| " _hb_gpu_render_single (renderCoord + vec2f (-d.x, d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas) +\n" |
| " _hb_gpu_render_single (renderCoord + vec2f ( d.x, d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas));\n" |
| "\n" |
| " c = mix (c, msaa, smoothstep (16.0, 8.0, ppem));\n" |
| " }\n" |
| "\n" |
| " return c;\n" |
| "}\n" |
| "\n" |
| "/* Stem darkening for small sizes.\n" |
| " *\n" |
| " * coverage: output of hb_gpu_render\n" |
| " * brightness: foreground brightness in [0, 1]\n" |
| " * ppem: pixels per em at this fragment\n" |
| " */\n" |
| "fn hb_gpu_darken (coverage: f32, brightness: f32, ppem: f32) -> f32\n" |
| "{\n" |
| " return pow (coverage,\n" |
| " mix (pow (2.0, brightness - 0.5), 1.0,\n" |
| " smoothstep (8.0, 48.0, ppem)));\n" |
| "}\n" |
| ; |