Files
PlantAI-dev/tools/pymol-env/lib/libharfbuzz-gpu.a
T

3406 lines
207 KiB
Plaintext
Raw Normal View History

2026-08-03 18:42:47 +08:00
!<arch>
/ 0 0 0 0 2042 `
@ ØØØØØØØØØØØØØØØØØØØØØØØ†à†à†à†à†à†à†à†à†à†à†à†à†à†à†à†à†à†à†à†à†à†à†à†àÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌhb_gpu_shader_source_ZN9hb_blob_t20recycle_data_destroyEPv_ZN13hb_gpu_draw_t11acc_move_toEdd_ZN13hb_gpu_draw_t11acc_line_toEdd_ZN13hb_gpu_draw_t12acc_conic_toEdddd_ZN13hb_gpu_draw_t14acc_close_pathEv_ZN13hb_gpu_draw_t12acc_cubic_toEddddddhb_gpu_draw_create_or_failhb_gpu_draw_referencehb_gpu_draw_destroyhb_gpu_draw_set_user_datahb_gpu_draw_get_user_datahb_gpu_draw_get_funcshb_gpu_draw_set_scalehb_gpu_draw_get_scalehb_gpu_draw_glyph_or_failhb_gpu_draw_glyphhb_gpu_draw_clearhb_gpu_draw_resethb_gpu_draw_recycle_blobhb_gpu_draw_shader_source_ZN11hb_vector_tI26hb_gpu_encode_curve_info_tLb0EE11resize_fullEibb_ZN11hb_vector_tIjLb0EE11resize_fullEibbhb_gpu_draw_encode_ZN9hb_blob_t20recycle_data_destroyEPv_ZN14hb_gpu_paint_t17fetch_color_stopsEP15hb_color_line_t_ZN32hb_gpu_paint_funcs_lazy_loader_t6createEvhb_gpu_paint_create_or_failhb_gpu_paint_referencehb_gpu_paint_destroyhb_gpu_paint_set_user_datahb_gpu_paint_get_user_datahb_gpu_paint_get_funcshb_gpu_paint_set_palettehb_gpu_paint_get_palettehb_gpu_paint_clear_custom_palette_colorshb_gpu_paint_set_custom_palette_colorhb_gpu_paint_set_scalehb_gpu_paint_get_scalehb_gpu_paint_glyph_or_failhb_gpu_paint_glyphhb_gpu_paint_clearhb_gpu_paint_encodehb_gpu_paint_resethb_gpu_paint_recycle_blobhb_gpu_paint_shader_source_ZN11hb_vector_tIP9hb_blob_tLb0EE4pushIJRS1_EEEPS1_DpOT__ZN11hb_vector_tIsLb0EE11resize_fullEibb_Z32_hb_ot_name_language_for_ms_codej_Z33_hb_ot_name_language_for_mac_codej_ZNK9hb_face_t15load_num_glyphsEv_ZNK9hb_face_t9load_upemEvendchar_strminus_1_hb_Null_AAT_Lookup_hb_Null_AAT_SettingName_hb_Null_OT_CmapSubtableLongGroup_hb_Null_OT_ClipRecord_hb_Null_OT_RangeRecord_hb_Null_OT_LangSys_hb_Null_OT_VarIdx_hb_Null_OT_Index_hb_CrapPool_hb_NullPool// 38 `
hb-gpu-draw.cc.o/
hb-gpu-paint.cc.o/
hb-gpu.cc.o/ 0 0 0 644 53056 `
ELF>€Ë@@/*
* Copyright (C) 2026 Behdad Esfahbod
* Copyright (C) 2017 Eric Lengyel
*
* The _hb_gpu_slug body below is based on the Slug algorithm by
* Eric Lengyel: https://github.com/EricLengyel/Slug
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Shared fragment-shader helpers for the hb-gpu renderers.
*
* Requires GLSL 3.30 or GLSL ES 3.00.
*
* For GLSL ES 3.00 / WebGL2, define HB_GPU_ATLAS_2D before
* including this source. The atlas is then a 2D isampler2D
* texture of known width (set via hb_gpu_atlas_width uniform)
* instead of an isamplerBuffer.
*
* Exports:
* hb_gpu_fetch (offset) atlas lookup
* _hb_gpu_slug (rc, pixelsPerEm, glyphLoc) MSAA-aware Slug
* coverage (caller supplies
* pixelsPerEm so it can be
* invoked from non-uniform
* control flow)
* hb_gpu_ppem (rc, glyphLoc) pixels per em at fragment
* _hb_gpu_curve_counts (rc, glyphLoc) debug: per-pixel curve counts
* hb_gpu_stem_darken (cov, brightness, ppem) thin-stroke contrast
* correction
*/
#ifndef HB_GPU_UNITS_PER_EM
#define HB_GPU_UNITS_PER_EM 4
#endif
#define HB_GPU_INV_UNITS float(1.0 / float(HB_GPU_UNITS_PER_EM))
#ifdef HB_GPU_ATLAS_2D
uniform highp isampler2D hb_gpu_atlas;
uniform int hb_gpu_atlas_width;
ivec4 hb_gpu_fetch (int offset)
{
return texelFetch (hb_gpu_atlas,
ivec2 (offset % hb_gpu_atlas_width,
offset / hb_gpu_atlas_width), 0);
}
#else
uniform isamplerBuffer hb_gpu_atlas;
ivec4 hb_gpu_fetch (int offset)
{
return texelFetch (hb_gpu_atlas, offset);
}
#endif
uint _hb_gpu_calc_root_code (float y1, float y2, float y3)
{
uint i1 = floatBitsToUint (y1) >> 31U;
uint i2 = floatBitsToUint (y2) >> 30U;
uint i3 = floatBitsToUint (y3) >> 29U;
uint shift = (i2 & 2U) | (i1 & ~2U);
shift = (i3 & 4U) | (shift & ~4U);
return (0x2E74U >> shift) & 0x0101U;
}
vec2 _hb_gpu_solve_horiz_poly (vec2 a, vec2 b, vec2 p1)
{
float ra = 1.0 / a.y;
float rb = 0.5 / b.y;
float d = sqrt (max (b.y * b.y - a.y * p1.y, 0.0));
float t1 = (b.y - d) * ra;
float t2 = (b.y + d) * ra;
if (a.y == 0.0)
t1 = t2 = p1.y * rb;
return vec2 ((a.x * t1 - b.x * 2.0) * t1 + p1.x,
(a.x * t2 - b.x * 2.0) * t2 + p1.x);
}
vec2 _hb_gpu_solve_vert_poly (vec2 a, vec2 b, vec2 p1)
{
float ra = 1.0 / a.x;
float rb = 0.5 / b.x;
float d = sqrt (max (b.x * b.x - a.x * p1.x, 0.0));
float t1 = (b.x - d) * ra;
float t2 = (b.x + d) * ra;
if (a.x == 0.0)
t1 = t2 = p1.x * rb;
return vec2 ((a.y * t1 - b.y * 2.0) * t1 + p1.y,
(a.y * t2 - b.y * 2.0) * t2 + p1.y);
}
float _hb_gpu_calc_coverage (float xcov, float ycov, float xwgt, float ywgt)
{
float coverage = max (abs (xcov * xwgt + ycov * ywgt) /
max (xwgt + ywgt, 1.0 / 65536.0),
min (abs (xcov), abs (ycov)));
return clamp (coverage, 0.0, 1.0);
}
/* Decoded glyph band info for a pixel position. */
struct _hb_gpu_glyph_info
{
int glyphLoc;
int bandBase;
ivec2 bandIndex;
int numHBands;
int numVBands;
vec2 scale;
};
_hb_gpu_glyph_info _hb_gpu_decode_glyph (vec2 renderCoord, uint glyphLoc_)
{
_hb_gpu_glyph_info gi;
gi.glyphLoc = int (glyphLoc_);
ivec4 header0 = hb_gpu_fetch (gi.glyphLoc);
ivec4 header1 = hb_gpu_fetch (gi.glyphLoc + 1);
vec4 ext = vec4 (header0) * HB_GPU_INV_UNITS;
gi.numHBands = header1.r;
gi.numVBands = header1.g;
gi.scale = vec2 (float (header1.b), float (header1.a));
vec2 extSize = ext.zw - ext.xy;
vec2 bandScale = vec2 (float (gi.numVBands), float (gi.numHBands)) / max (extSize, vec2 (1.0 / 65536.0));
vec2 bandOffset = -ext.xy * bandScale;
gi.bandIndex = clamp (ivec2 (renderCoord * bandScale + bandOffset),
ivec2 (0, 0),
ivec2 (gi.numVBands - 1, gi.numHBands - 1));
gi.bandBase = gi.glyphLoc + 2;
return gi;
}
/* Return pixels per em at this fragment. */
float hb_gpu_ppem (vec2 renderCoord, uint glyphLoc_)
{
_hb_gpu_glyph_info gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_);
vec2 emsPerPixel = fwidth (renderCoord);
return min (gi.scale.x, gi.scale.y) /
max (emsPerPixel.x, emsPerPixel.y);
}
/* Return per-pixel curve counts: (horizontal, vertical). */
ivec2 _hb_gpu_curve_counts (vec2 renderCoord, uint glyphLoc_)
{
_hb_gpu_glyph_info gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_);
int hCount = hb_gpu_fetch (gi.bandBase + gi.bandIndex.y).r;
int vCount = hb_gpu_fetch (gi.bandBase + gi.numHBands + gi.bandIndex.x).r;
return ivec2 (hCount, vCount);
}
/* Single-sample coverage in [0, 1]. */
float _hb_gpu_slug_single (vec2 renderCoord, vec2 pixelsPerEm, uint glyphLoc_)
{
_hb_gpu_glyph_info gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_);
int glyphLoc = gi.glyphLoc;
int bandBase = gi.bandBase;
int numHBands = gi.numHBands;
float xcov = 0.0;
float xwgt = 0.0;
ivec4 hbandData = hb_gpu_fetch (bandBase + gi.bandIndex.y);
int hCurveCount = hbandData.r;
/* Symmetric: choose rightward (desc) or leftward (asc) sort */
float hSplit = float (hbandData.a) * HB_GPU_INV_UNITS;
bool hLeftRay = (renderCoord.x < hSplit);
int hDataOffset = (hLeftRay ? hbandData.b : hbandData.g) + 32768;
for (int ci = 0; ci < hCurveCount; ci++)
{
int curveOffset = hb_gpu_fetch (glyphLoc + hDataOffset + ci).r + 32768;
ivec4 raw12 = hb_gpu_fetch (glyphLoc + curveOffset);
ivec4 raw3 = hb_gpu_fetch (glyphLoc + curveOffset + 1);
vec4 q12 = vec4 (raw12) * HB_GPU_INV_UNITS;
vec2 q3 = vec2 (raw3.rg) * HB_GPU_INV_UNITS;
vec4 p12 = q12 - vec4 (renderCoord, renderCoord);
vec2 p3 = q3 - renderCoord;
if (hLeftRay) {
if (min (min (p12.x, p12.z), p3.x) * pixelsPerEm.x > 0.5) break;
} else {
if (max (max (p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) break;
}
uint code = _hb_gpu_calc_root_code (p12.y, p12.w, p3.y);
if (code != 0U)
{
vec2 a = q12.xy - q12.zw * 2.0 + q3;
vec2 b = q12.xy - q12.zw;
vec2 r = _hb_gpu_solve_horiz_poly (a, b, p12.xy) * pixelsPerEm.x;
/* For leftward ray: saturate(0.5 - r) counts coverage from the left */
vec2 cov = hLeftRay ? clamp (vec2 (0.5) - r, 0.0, 1.0)
: clamp (r + vec2 (0.5), 0.0, 1.0);
if ((code & 1U) != 0U)
{
xcov += cov.x;
xwgt = max (xwgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0));
}
if (code > 1U)
{
xcov -= cov.y;
xwgt = max (xwgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0));
}
}
}
float ycov = 0.0;
float ywgt = 0.0;
ivec4 vbandData = hb_gpu_fetch (bandBase + numHBands + gi.bandIndex.x);
int vCurveCount = vbandData.r;
float vSplit = float (vbandData.a) * HB_GPU_INV_UNITS;
bool vLeftRay = (renderCoord.y < vSplit);
int vDataOffset = (vLeftRay ? vbandData.b : vbandData.g) + 32768;
for (int ci = 0; ci < vCurveCount; ci++)
{
int curveOffset = hb_gpu_fetch (glyphLoc + vDataOffset + ci).r + 32768;
ivec4 raw12 = hb_gpu_fetch (glyphLoc + curveOffset);
ivec4 raw3 = hb_gpu_fetch (glyphLoc + curveOffset + 1);
vec4 q12 = vec4 (raw12) * HB_GPU_INV_UNITS;
vec2 q3 = vec2 (raw3.rg) * HB_GPU_INV_UNITS;
vec4 p12 = q12 - vec4 (renderCoord, renderCoord);
vec2 p3 = q3 - renderCoord;
if (vLeftRay) {
if (min (min (p12.y, p12.w), p3.y) * pixelsPerEm.y > 0.5) break;
} else {
if (max (max (p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) break;
}
uint code = _hb_gpu_calc_root_code (p12.x, p12.z, p3.x);
if (code != 0U)
{
vec2 a = q12.xy - q12.zw * 2.0 + q3;
vec2 b = q12.xy - q12.zw;
vec2 r = _hb_gpu_solve_vert_poly (a, b, p12.xy) * pixelsPerEm.y;
vec2 cov = vLeftRay ? clamp (vec2 (0.5) - r, 0.0, 1.0)
: clamp (r + vec2 (0.5), 0.0, 1.0);
if ((code & 1U) != 0U)
{
ycov -= cov.x;
ywgt = max (ywgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0));
}
if (code > 1U)
{
ycov += cov.y;
ywgt = max (ywgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0));
}
}
}
return _hb_gpu_calc_coverage (xcov, ycov, xwgt, ywgt);
}
/* MSAA-aware Slug coverage. Caller supplies pixelsPerEm so this
* function can be invoked from non-uniform control flow (for
* example inside an op-stream branch in hb-gpu-paint-fragment.wgsl,
* where WGSL would otherwise reject an fwidth call). */
float _hb_gpu_slug (vec2 renderCoord, vec2 pixelsPerEm, uint glyphLoc_)
{
float c = _hb_gpu_slug_single (renderCoord, pixelsPerEm, glyphLoc_);
#ifndef HB_GPU_NO_MSAA
float ppem = hb_gpu_ppem (renderCoord, glyphLoc_);
if (ppem < 16.0)
{
vec2 emsPerPixel = 1.0 / pixelsPerEm;
vec2 d = emsPerPixel * (1.0 / 3.0);
float msaa = 0.25 *
(_hb_gpu_slug_single (renderCoord + vec2 (-d.x, -d.y), pixelsPerEm, glyphLoc_) +
_hb_gpu_slug_single (renderCoord + vec2 ( d.x, -d.y), pixelsPerEm, glyphLoc_) +
_hb_gpu_slug_single (renderCoord + vec2 (-d.x, d.y), pixelsPerEm, glyphLoc_) +
_hb_gpu_slug_single (renderCoord + vec2 ( d.x, d.y), pixelsPerEm, glyphLoc_));
c = mix (c, msaa, smoothstep (16.0, 8.0, ppem));
}
#endif
return c;
}
/* Stem darkening for small sizes.
*
* coverage: output of hb_gpu_draw / _hb_gpu_slug
* brightness: foreground brightness in [0, 1]
* ppem: pixels per em at this fragment
*/
float hb_gpu_stem_darken (float coverage, float brightness, float ppem)
{
return pow (coverage,
mix (pow (2.0, brightness - 0.5), 1.0,
smoothstep (8.0, 48.0, ppem)));
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Shared fragment-shader helpers for the hb-gpu renderers.
*
* Requires Metal Shading Language 2.0.
*/
#ifndef HB_GPU_UNITS_PER_EM
#define HB_GPU_UNITS_PER_EM 4
#endif
#define HB_GPU_INV_UNITS float(1.0 / float(HB_GPU_UNITS_PER_EM))
int4 hb_gpu_fetch (device const short4* hb_gpu_atlas, int offset)
{
return int4 (hb_gpu_atlas[offset]);
}
uint _hb_gpu_calc_root_code (float y1, float y2, float y3)
{
uint i1 = as_type<uint> (y1) >> 31U;
uint i2 = as_type<uint> (y2) >> 30U;
uint i3 = as_type<uint> (y3) >> 29U;
uint shift = (i2 & 2U) | (i1 & ~2U);
shift = (i3 & 4U) | (shift & ~4U);
return (0x2E74U >> shift) & 0x0101U;
}
float2 _hb_gpu_solve_horiz_poly (float2 a, float2 b, float2 p1)
{
float ra = 1.0 / a.y;
float rb = 0.5 / b.y;
float d = sqrt (max (b.y * b.y - a.y * p1.y, 0.0));
float t1 = (b.y - d) * ra;
float t2 = (b.y + d) * ra;
if (a.y == 0.0)
t1 = t2 = p1.y * rb;
return float2 ((a.x * t1 - b.x * 2.0) * t1 + p1.x,
(a.x * t2 - b.x * 2.0) * t2 + p1.x);
}
float2 _hb_gpu_solve_vert_poly (float2 a, float2 b, float2 p1)
{
float ra = 1.0 / a.x;
float rb = 0.5 / b.x;
float d = sqrt (max (b.x * b.x - a.x * p1.x, 0.0));
float t1 = (b.x - d) * ra;
float t2 = (b.x + d) * ra;
if (a.x == 0.0)
t1 = t2 = p1.x * rb;
return float2 ((a.y * t1 - b.y * 2.0) * t1 + p1.y,
(a.y * t2 - b.y * 2.0) * t2 + p1.y);
}
float _hb_gpu_calc_coverage (float xcov, float ycov, float xwgt, float ywgt)
{
float coverage = max (abs (xcov * xwgt + ycov * ywgt) /
max (xwgt + ywgt, 1.0 / 65536.0),
min (abs (xcov), abs (ycov)));
return clamp (coverage, 0.0, 1.0);
}
/* Decoded glyph band info for a pixel position. */
struct _hb_gpu_glyph_info
{
int glyphLoc;
int bandBase;
int2 bandIndex;
int numHBands;
int numVBands;
float2 scale;
};
_hb_gpu_glyph_info _hb_gpu_decode_glyph (float2 renderCoord, uint glyphLoc_,
device const short4* hb_gpu_atlas)
{
_hb_gpu_glyph_info gi;
gi.glyphLoc = int (glyphLoc_);
int4 header0 = hb_gpu_fetch (hb_gpu_atlas, gi.glyphLoc);
int4 header1 = hb_gpu_fetch (hb_gpu_atlas, gi.glyphLoc + 1);
float4 ext = float4 (header0) * HB_GPU_INV_UNITS;
gi.numHBands = header1.r;
gi.numVBands = header1.g;
gi.scale = float2 (float (header1.b), float (header1.a));
float2 extSize = ext.zw - ext.xy;
float2 bandScale = float2 (float (gi.numVBands), float (gi.numHBands)) / max (extSize, float2 (1.0 / 65536.0));
float2 bandOffset = -ext.xy * bandScale;
gi.bandIndex = clamp (int2 (renderCoord * bandScale + bandOffset),
int2 (0, 0),
int2 (gi.numVBands - 1, gi.numHBands - 1));
gi.bandBase = gi.glyphLoc + 2;
return gi;
}
/* Return pixels per em at this fragment.
*
* renderCoord: em-space sample position
* glyphLoc: texel offset of glyph blob in atlas
*/
float hb_gpu_ppem (float2 renderCoord, uint glyphLoc_,
device const short4* hb_gpu_atlas)
{
_hb_gpu_glyph_info gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_, hb_gpu_atlas);
float2 emsPerPixel = fwidth (renderCoord);
return min (gi.scale.x, gi.scale.y) /
max (emsPerPixel.x, emsPerPixel.y);
}
/* Return per-pixel curve counts: (horizontal, vertical). */
int2 _hb_gpu_curve_counts (float2 renderCoord, uint glyphLoc_,
device const short4* hb_gpu_atlas)
{
_hb_gpu_glyph_info gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_, hb_gpu_atlas);
int hCount = hb_gpu_fetch (hb_gpu_atlas, gi.bandBase + gi.bandIndex.y).r;
int vCount = hb_gpu_fetch (hb_gpu_atlas, gi.bandBase + gi.numHBands + gi.bandIndex.x).r;
return int2 (hCount, vCount);
}
/* Single-sample coverage in [0, 1]. */
float _hb_gpu_slug_single (float2 renderCoord, float2 pixelsPerEm, uint glyphLoc_,
device const short4* hb_gpu_atlas)
{
_hb_gpu_glyph_info gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_, hb_gpu_atlas);
int glyphLoc = gi.glyphLoc;
int bandBase = gi.bandBase;
int numHBands = gi.numHBands;
float xcov = 0.0;
float xwgt = 0.0;
int4 hbandData = hb_gpu_fetch (hb_gpu_atlas, bandBase + gi.bandIndex.y);
int hCurveCount = hbandData.r;
/* Symmetric: choose rightward (desc) or leftward (asc) sort */
float hSplit = float (hbandData.a) * HB_GPU_INV_UNITS;
bool hLeftRay = (renderCoord.x < hSplit);
int hDataOffset = (hLeftRay ? hbandData.b : hbandData.g) + 32768;
for (int ci = 0; ci < hCurveCount; ci++)
{
int curveOffset = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + hDataOffset + ci).r + 32768;
int4 raw12 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset);
int4 raw3 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset + 1);
float4 q12 = float4 (raw12) * HB_GPU_INV_UNITS;
float2 q3 = float2 (raw3.rg) * HB_GPU_INV_UNITS;
float4 p12 = q12 - float4 (renderCoord, renderCoord);
float2 p3 = q3 - renderCoord;
if (hLeftRay) {
if (min (min (p12.x, p12.z), p3.x) * pixelsPerEm.x > 0.5) break;
} else {
if (max (max (p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) break;
}
uint code = _hb_gpu_calc_root_code (p12.y, p12.w, p3.y);
if (code != 0U)
{
float2 a = q12.xy - q12.zw * 2.0 + q3;
float2 b = q12.xy - q12.zw;
float2 r = _hb_gpu_solve_horiz_poly (a, b, p12.xy) * pixelsPerEm.x;
/* For leftward ray: saturate(0.5 - r) counts coverage from the left */
float2 cov = hLeftRay ? clamp (float2 (0.5) - r, 0.0, 1.0)
: clamp (r + float2 (0.5), 0.0, 1.0);
if ((code & 1U) != 0U)
{
xcov += cov.x;
xwgt = max (xwgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0));
}
if (code > 1U)
{
xcov -= cov.y;
xwgt = max (xwgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0));
}
}
}
float ycov = 0.0;
float ywgt = 0.0;
int4 vbandData = hb_gpu_fetch (hb_gpu_atlas, bandBase + numHBands + gi.bandIndex.x);
int vCurveCount = vbandData.r;
float vSplit = float (vbandData.a) * HB_GPU_INV_UNITS;
bool vLeftRay = (renderCoord.y < vSplit);
int vDataOffset = (vLeftRay ? vbandData.b : vbandData.g) + 32768;
for (int ci = 0; ci < vCurveCount; ci++)
{
int curveOffset = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + vDataOffset + ci).r + 32768;
int4 raw12 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset);
int4 raw3 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset + 1);
float4 q12 = float4 (raw12) * HB_GPU_INV_UNITS;
float2 q3 = float2 (raw3.rg) * HB_GPU_INV_UNITS;
float4 p12 = q12 - float4 (renderCoord, renderCoord);
float2 p3 = q3 - renderCoord;
if (vLeftRay) {
if (min (min (p12.y, p12.w), p3.y) * pixelsPerEm.y > 0.5) break;
} else {
if (max (max (p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) break;
}
uint code = _hb_gpu_calc_root_code (p12.x, p12.z, p3.x);
if (code != 0U)
{
float2 a = q12.xy - q12.zw * 2.0 + q3;
float2 b = q12.xy - q12.zw;
float2 r = _hb_gpu_solve_vert_poly (a, b, p12.xy) * pixelsPerEm.y;
float2 cov = vLeftRay ? clamp (float2 (0.5) - r, 0.0, 1.0)
: clamp (r + float2 (0.5), 0.0, 1.0);
if ((code & 1U) != 0U)
{
ycov -= cov.x;
ywgt = max (ywgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0));
}
if (code > 1U)
{
ycov += cov.y;
ywgt = max (ywgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0));
}
}
}
return _hb_gpu_calc_coverage (xcov, ycov, xwgt, ywgt);
}
/* Return coverage in [0, 1].
*
* renderCoord: em-space sample position
* glyphLoc: texel offset of glyph blob in atlas
* hb_gpu_atlas: device pointer to the atlas buffer
*/
/* The MSAA-aware implementation. Caller supplies pixelsPerEm so
* this function can be invoked from non-uniform control flow (for
* example from a paint op-stream branch where a recomputed fwidth
* would be rejected by strict derivative-uniformity rules). */
float _hb_gpu_slug (float2 renderCoord, float2 pixelsPerEm, uint glyphLoc_,
device const short4* hb_gpu_atlas)
{
float c = _hb_gpu_slug_single (renderCoord, pixelsPerEm, glyphLoc_, hb_gpu_atlas);
#ifndef HB_GPU_NO_MSAA
float ppem = hb_gpu_ppem (renderCoord, glyphLoc_, hb_gpu_atlas);
if (ppem < 16.0)
{
float2 emsPerPixel = 1.0 / pixelsPerEm;
float2 d = emsPerPixel * (1.0 / 3.0);
float msaa = 0.25 *
(_hb_gpu_slug_single (renderCoord + float2 (-d.x, -d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas) +
_hb_gpu_slug_single (renderCoord + float2 ( d.x, -d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas) +
_hb_gpu_slug_single (renderCoord + float2 (-d.x, d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas) +
_hb_gpu_slug_single (renderCoord + float2 ( d.x, d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas));
c = mix (c, msaa, smoothstep (16.0, 8.0, ppem));
}
#endif
return c;
}
/* Stem darkening for small sizes.
*
* coverage: output of hb_gpu_draw
* brightness: foreground brightness in [0, 1]
* ppem: pixels per em at this fragment
*/
float hb_gpu_stem_darken (float coverage, float brightness, float ppem)
{
return pow (coverage,
mix (pow (2.0, brightness - 0.5), 1.0,
smoothstep (8.0, 48.0, ppem)));
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Shared fragment-shader helpers for the hb-gpu renderers.
*
* Requires WGSL (WebGPU Shading Language).
*/
const HB_GPU_UNITS_PER_EM: f32 = 4.0;
const HB_GPU_INV_UNITS: f32 = 1.0 / 4.0;
fn hb_gpu_fetch (hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>,
offset: i32) -> vec4<i32>
{
return (*hb_gpu_atlas)[offset];
}
fn _hb_gpu_calc_root_code (y1: f32, y2: f32, y3: f32) -> u32
{
let i1 = bitcast<u32> (y1) >> 31u;
let i2 = bitcast<u32> (y2) >> 30u;
let i3 = bitcast<u32> (y3) >> 29u;
var shift = (i2 & 2u) | (i1 & ~2u);
shift = (i3 & 4u) | (shift & ~4u);
return (0x2E74u >> shift) & 0x0101u;
}
fn _hb_gpu_solve_horiz_poly (a: vec2f, b: vec2f, p1: vec2f) -> vec2f
{
let ra = 1.0 / a.y;
let rb = 0.5 / b.y;
let d = sqrt (max (b.y * b.y - a.y * p1.y, 0.0));
var t1 = (b.y - d) * ra;
var t2 = (b.y + d) * ra;
if (a.y == 0.0) {
t1 = p1.y * rb;
t2 = t1;
}
return vec2f ((a.x * t1 - b.x * 2.0) * t1 + p1.x,
(a.x * t2 - b.x * 2.0) * t2 + p1.x);
}
fn _hb_gpu_solve_vert_poly (a: vec2f, b: vec2f, p1: vec2f) -> vec2f
{
let ra = 1.0 / a.x;
let rb = 0.5 / b.x;
let d = sqrt (max (b.x * b.x - a.x * p1.x, 0.0));
var t1 = (b.x - d) * ra;
var t2 = (b.x + d) * ra;
if (a.x == 0.0) {
t1 = p1.x * rb;
t2 = t1;
}
return vec2f ((a.y * t1 - b.y * 2.0) * t1 + p1.y,
(a.y * t2 - b.y * 2.0) * t2 + p1.y);
}
fn _hb_gpu_calc_coverage (xcov: f32, ycov: f32, xwgt: f32, ywgt: f32) -> f32
{
let coverage = max (abs (xcov * xwgt + ycov * ywgt) /
max (xwgt + ywgt, 1.0 / 65536.0),
min (abs (xcov), abs (ycov)));
return clamp (coverage, 0.0, 1.0);
}
/* Decoded glyph band info for a pixel position. */
struct _hb_gpu_glyph_info
{
glyphLoc: i32,
bandBase: i32,
bandIndex: vec2<i32>,
numHBands: i32,
numVBands: i32,
scale: vec2f,
}
fn _hb_gpu_decode_glyph (renderCoord: vec2f, glyphLoc_: u32,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> _hb_gpu_glyph_info
{
var gi: _hb_gpu_glyph_info;
gi.glyphLoc = i32 (glyphLoc_);
let header0 = hb_gpu_fetch (hb_gpu_atlas, gi.glyphLoc);
let header1 = hb_gpu_fetch (hb_gpu_atlas, gi.glyphLoc + 1);
let ext = vec4f (header0) * HB_GPU_INV_UNITS;
gi.numHBands = header1.r;
gi.numVBands = header1.g;
gi.scale = vec2f (f32 (header1.b), f32 (header1.a));
let extSize = ext.zw - ext.xy;
let bandScale = vec2f (f32 (gi.numVBands), f32 (gi.numHBands)) / max (extSize, vec2f (1.0 / 65536.0));
let bandOffset = -ext.xy * bandScale;
gi.bandIndex = clamp (vec2<i32> (renderCoord * bandScale + bandOffset),
vec2<i32> (0, 0),
vec2<i32> (gi.numVBands - 1, gi.numHBands - 1));
gi.bandBase = gi.glyphLoc + 2;
return gi;
}
/* Return pixels per em at this fragment.
*
* renderCoord: em-space sample position
* glyphLoc: texel offset of glyph blob in atlas
*/
fn hb_gpu_ppem (renderCoord: vec2f, glyphLoc_: u32,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> f32
{
let gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_, hb_gpu_atlas);
let emsPerPixel = fwidth (renderCoord);
return min (gi.scale.x, gi.scale.y) /
max (emsPerPixel.x, emsPerPixel.y);
}
/* Return per-pixel curve counts: (horizontal, vertical). */
fn _hb_gpu_curve_counts (renderCoord: vec2f, glyphLoc_: u32,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> vec2<i32>
{
let gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_, hb_gpu_atlas);
let hCount = hb_gpu_fetch (hb_gpu_atlas, gi.bandBase + gi.bandIndex.y).r;
let vCount = hb_gpu_fetch (hb_gpu_atlas, gi.bandBase + gi.numHBands + gi.bandIndex.x).r;
return vec2<i32> (hCount, vCount);
}
/* Single-sample coverage in [0, 1]. */
fn _hb_gpu_slug_single (renderCoord: vec2f, pixelsPerEm: vec2f, glyphLoc_: u32,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> f32
{
let gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_, hb_gpu_atlas);
let glyphLoc = gi.glyphLoc;
let bandBase = gi.bandBase;
let numHBands = gi.numHBands;
var xcov: f32 = 0.0;
var xwgt: f32 = 0.0;
let hbandData = hb_gpu_fetch (hb_gpu_atlas, bandBase + gi.bandIndex.y);
let hCurveCount = hbandData.r;
/* Symmetric: choose rightward (desc) or leftward (asc) sort */
let hSplit = f32 (hbandData.a) * HB_GPU_INV_UNITS;
let hLeftRay = (renderCoord.x < hSplit);
var hDataOffset: i32;
if (hLeftRay) { hDataOffset = hbandData.b + 32768; }
else { hDataOffset = hbandData.g + 32768; }
for (var ci: i32 = 0; ci < hCurveCount; ci++)
{
let curveOffset = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + hDataOffset + ci).r + 32768;
let raw12 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset);
let raw3 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset + 1);
let q12 = vec4f (raw12) * HB_GPU_INV_UNITS;
let q3 = vec2f (vec2<i32> (raw3.r, raw3.g)) * HB_GPU_INV_UNITS;
let p12 = q12 - vec4f (renderCoord, renderCoord);
let p3 = q3 - renderCoord;
if (hLeftRay) {
if (min (min (p12.x, p12.z), p3.x) * pixelsPerEm.x > 0.5) { break; }
} else {
if (max (max (p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) { break; }
}
let code = _hb_gpu_calc_root_code (p12.y, p12.w, p3.y);
if (code != 0u)
{
let a = q12.xy - q12.zw * 2.0 + q3;
let b = q12.xy - q12.zw;
let r = _hb_gpu_solve_horiz_poly (a, b, p12.xy) * pixelsPerEm.x;
/* For leftward ray: saturate(0.5 - r) counts coverage from the left */
var cov: vec2f;
if (hLeftRay) { cov = clamp (vec2f (0.5) - r, vec2f (0.0), vec2f (1.0)); }
else { cov = clamp (r + vec2f (0.5), vec2f (0.0), vec2f (1.0)); }
if ((code & 1u) != 0u)
{
xcov += cov.x;
xwgt = max (xwgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0));
}
if (code > 1u)
{
xcov -= cov.y;
xwgt = max (xwgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0));
}
}
}
var ycov: f32 = 0.0;
var ywgt: f32 = 0.0;
let vbandData = hb_gpu_fetch (hb_gpu_atlas, bandBase + numHBands + gi.bandIndex.x);
let vCurveCount = vbandData.r;
let vSplit = f32 (vbandData.a) * HB_GPU_INV_UNITS;
let vLeftRay = (renderCoord.y < vSplit);
var vDataOffset: i32;
if (vLeftRay) { vDataOffset = vbandData.b + 32768; }
else { vDataOffset = vbandData.g + 32768; }
for (var ci: i32 = 0; ci < vCurveCount; ci++)
{
let curveOffset = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + vDataOffset + ci).r + 32768;
let raw12 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset);
let raw3 = hb_gpu_fetch (hb_gpu_atlas, glyphLoc + curveOffset + 1);
let q12 = vec4f (raw12) * HB_GPU_INV_UNITS;
let q3 = vec2f (vec2<i32> (raw3.r, raw3.g)) * HB_GPU_INV_UNITS;
let p12 = q12 - vec4f (renderCoord, renderCoord);
let p3 = q3 - renderCoord;
if (vLeftRay) {
if (min (min (p12.y, p12.w), p3.y) * pixelsPerEm.y > 0.5) { break; }
} else {
if (max (max (p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) { break; }
}
let code = _hb_gpu_calc_root_code (p12.x, p12.z, p3.x);
if (code != 0u)
{
let a = q12.xy - q12.zw * 2.0 + q3;
let b = q12.xy - q12.zw;
let r = _hb_gpu_solve_vert_poly (a, b, p12.xy) * pixelsPerEm.y;
var cov: vec2f;
if (vLeftRay) { cov = clamp (vec2f (0.5) - r, vec2f (0.0), vec2f (1.0)); }
else { cov = clamp (r + vec2f (0.5), vec2f (0.0), vec2f (1.0)); }
if ((code & 1u) != 0u)
{
ycov -= cov.x;
ywgt = max (ywgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0));
}
if (code > 1u)
{
ycov += cov.y;
ywgt = max (ywgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0));
}
}
}
return _hb_gpu_calc_coverage (xcov, ycov, xwgt, ywgt);
}
/* Return coverage in [0, 1].
*
* renderCoord: em-space sample position
* glyphLoc: texel offset of glyph blob in atlas
* hb_gpu_atlas: storage buffer pointer to the atlas
*/
/* The MSAA-aware implementation. Caller supplies pixelsPerEm so
* this function can be invoked from non-uniform control flow (for
* example inside an op-stream branch in hb-gpu-paint-fragment.wgsl,
* where WGSL would otherwise reject an fwidth call). */
fn _hb_gpu_slug (renderCoord: vec2f, pixelsPerEm: vec2f, glyphLoc_: u32,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> f32
{
var c = _hb_gpu_slug_single (renderCoord, pixelsPerEm, glyphLoc_, hb_gpu_atlas);
/* Inline ppem from pixelsPerEm so we don't re-enter hb_gpu_ppem
* (which would call fwidth again -- rejected by WGSL when this
* function is invoked from non-uniform control flow). */
let gi_pp = _hb_gpu_decode_glyph (renderCoord, glyphLoc_, hb_gpu_atlas);
let ppem = min (gi_pp.scale.x, gi_pp.scale.y) *
min (pixelsPerEm.x, pixelsPerEm.y);
if (ppem < 16.0)
{
let emsPerPixel = 1.0 / pixelsPerEm;
let d = emsPerPixel * (1.0 / 3.0);
let msaa = 0.25 *
(_hb_gpu_slug_single (renderCoord + vec2f (-d.x, -d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas) +
_hb_gpu_slug_single (renderCoord + vec2f ( d.x, -d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas) +
_hb_gpu_slug_single (renderCoord + vec2f (-d.x, d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas) +
_hb_gpu_slug_single (renderCoord + vec2f ( d.x, d.y), pixelsPerEm, glyphLoc_, hb_gpu_atlas));
c = mix (c, msaa, smoothstep (16.0, 8.0, ppem));
}
return c;
}
/* Stem darkening for small sizes.
*
* coverage: output of hb_gpu_draw
* brightness: foreground brightness in [0, 1]
* ppem: pixels per em at this fragment
*/
fn hb_gpu_stem_darken (coverage: f32, brightness: f32, ppem: f32) -> f32
{
return pow (coverage,
mix (pow (2.0, brightness - 0.5), 1.0,
smoothstep (8.0, 48.0, ppem)));
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Shared fragment-shader helpers for the hb-gpu renderers.
*
* Requires Shader Model 5.0+.
*
* The caller must declare:
* StructuredBuffer<int4> hb_gpu_atlas : register(t0);
*/
#ifndef HB_GPU_UNITS_PER_EM
#define HB_GPU_UNITS_PER_EM 4
#endif
#define HB_GPU_INV_UNITS (1.0 / (float) HB_GPU_UNITS_PER_EM)
int4 hb_gpu_fetch (int offset)
{
return hb_gpu_atlas[offset];
}
uint _hb_gpu_calc_root_code (float y1, float y2, float y3)
{
uint i1 = asuint (y1) >> 31u;
uint i2 = asuint (y2) >> 30u;
uint i3 = asuint (y3) >> 29u;
uint shift = (i2 & 2u) | (i1 & ~2u);
shift = (i3 & 4u) | (shift & ~4u);
return (0x2E74u >> shift) & 0x0101u;
}
float2 _hb_gpu_solve_horiz_poly (float2 a, float2 b, float2 p1)
{
float ra = 1.0 / a.y;
float rb = 0.5 / b.y;
float d = sqrt (max (b.y * b.y - a.y * p1.y, 0.0));
float t1 = (b.y - d) * ra;
float t2 = (b.y + d) * ra;
if (a.y == 0.0)
t1 = t2 = p1.y * rb;
return float2 ((a.x * t1 - b.x * 2.0) * t1 + p1.x,
(a.x * t2 - b.x * 2.0) * t2 + p1.x);
}
float2 _hb_gpu_solve_vert_poly (float2 a, float2 b, float2 p1)
{
float ra = 1.0 / a.x;
float rb = 0.5 / b.x;
float d = sqrt (max (b.x * b.x - a.x * p1.x, 0.0));
float t1 = (b.x - d) * ra;
float t2 = (b.x + d) * ra;
if (a.x == 0.0)
t1 = t2 = p1.x * rb;
return float2 ((a.y * t1 - b.y * 2.0) * t1 + p1.y,
(a.y * t2 - b.y * 2.0) * t2 + p1.y);
}
float _hb_gpu_calc_coverage (float xcov, float ycov, float xwgt, float ywgt)
{
float coverage = max (abs (xcov * xwgt + ycov * ywgt) /
max (xwgt + ywgt, 1.0 / 65536.0),
min (abs (xcov), abs (ycov)));
return clamp (coverage, 0.0, 1.0);
}
struct _hb_gpu_glyph_info
{
int glyphLoc;
int bandBase;
int2 bandIndex;
int numHBands;
int numVBands;
float2 scale;
};
_hb_gpu_glyph_info _hb_gpu_decode_glyph (float2 renderCoord, uint glyphLoc_)
{
_hb_gpu_glyph_info gi;
gi.glyphLoc = (int) glyphLoc_;
int4 header0 = hb_gpu_fetch (gi.glyphLoc);
int4 header1 = hb_gpu_fetch (gi.glyphLoc + 1);
float4 ext = (float4) header0 * HB_GPU_INV_UNITS;
gi.numHBands = header1.r;
gi.numVBands = header1.g;
gi.scale = float2 ((float) header1.b, (float) header1.a);
float2 extSize = ext.zw - ext.xy;
float2 bandScale = float2 ((float) gi.numVBands, (float) gi.numHBands) / max (extSize, float2 (1.0 / 65536.0, 1.0 / 65536.0));
float2 bandOffset = -ext.xy * bandScale;
gi.bandIndex = clamp ((int2) (renderCoord * bandScale + bandOffset),
int2 (0, 0),
int2 (gi.numVBands - 1, gi.numHBands - 1));
gi.bandBase = gi.glyphLoc + 2;
return gi;
}
/* Return pixels per em at this fragment.
*
* renderCoord: em-space sample position
* glyphLoc: texel offset of glyph blob in atlas
*/
float hb_gpu_ppem (float2 renderCoord, uint glyphLoc_)
{
_hb_gpu_glyph_info gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_);
float2 emsPerPixel = fwidth (renderCoord);
return min (gi.scale.x, gi.scale.y) /
max (emsPerPixel.x, emsPerPixel.y);
}
int2 _hb_gpu_curve_counts (float2 renderCoord, uint glyphLoc_)
{
_hb_gpu_glyph_info gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_);
int hCount = hb_gpu_fetch (gi.bandBase + gi.bandIndex.y).r;
int vCount = hb_gpu_fetch (gi.bandBase + gi.numHBands + gi.bandIndex.x).r;
return int2 (hCount, vCount);
}
/* Single-sample coverage in [0, 1]. */
float _hb_gpu_slug_single (float2 renderCoord, float2 pixelsPerEm, uint glyphLoc_)
{
_hb_gpu_glyph_info gi = _hb_gpu_decode_glyph (renderCoord, glyphLoc_);
int glyphLoc = gi.glyphLoc;
int bandBase = gi.bandBase;
int numHBands = gi.numHBands;
float xcov = 0.0;
float xwgt = 0.0;
int4 hbandData = hb_gpu_fetch (bandBase + gi.bandIndex.y);
int hCurveCount = hbandData.r;
float hSplit = (float) hbandData.a * HB_GPU_INV_UNITS;
bool hLeftRay = (renderCoord.x < hSplit);
int hDataOffset = (hLeftRay ? hbandData.b : hbandData.g) + 32768;
for (int ci = 0; ci < hCurveCount; ci++)
{
int curveOffset = hb_gpu_fetch (glyphLoc + hDataOffset + ci).r + 32768;
int4 raw12 = hb_gpu_fetch (glyphLoc + curveOffset);
int4 raw3 = hb_gpu_fetch (glyphLoc + curveOffset + 1);
float4 q12 = (float4) raw12 * HB_GPU_INV_UNITS;
float2 q3 = (float2) raw3.rg * HB_GPU_INV_UNITS;
float4 p12 = q12 - float4 (renderCoord, renderCoord);
float2 p3 = q3 - renderCoord;
if (hLeftRay) {
if (min (min (p12.x, p12.z), p3.x) * pixelsPerEm.x > 0.5) break;
} else {
if (max (max (p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) break;
}
uint code = _hb_gpu_calc_root_code (p12.y, p12.w, p3.y);
if (code != 0u)
{
float2 a = q12.xy - q12.zw * 2.0 + q3;
float2 b = q12.xy - q12.zw;
float2 r = _hb_gpu_solve_horiz_poly (a, b, p12.xy) * pixelsPerEm.x;
float2 cov = hLeftRay ? clamp (float2 (0.5, 0.5) - r, 0.0, 1.0)
: clamp (r + float2 (0.5, 0.5), 0.0, 1.0);
if ((code & 1u) != 0u)
{
xcov += cov.x;
xwgt = max (xwgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0));
}
if (code > 1u)
{
xcov -= cov.y;
xwgt = max (xwgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0));
}
}
}
float ycov = 0.0;
float ywgt = 0.0;
int4 vbandData = hb_gpu_fetch (bandBase + numHBands + gi.bandIndex.x);
int vCurveCount = vbandData.r;
float vSplit = (float) vbandData.a * HB_GPU_INV_UNITS;
bool vLeftRay = (renderCoord.y < vSplit);
int vDataOffset = (vLeftRay ? vbandData.b : vbandData.g) + 32768;
for (int ci = 0; ci < vCurveCount; ci++)
{
int curveOffset = hb_gpu_fetch (glyphLoc + vDataOffset + ci).r + 32768;
int4 raw12 = hb_gpu_fetch (glyphLoc + curveOffset);
int4 raw3 = hb_gpu_fetch (glyphLoc + curveOffset + 1);
float4 q12 = (float4) raw12 * HB_GPU_INV_UNITS;
float2 q3 = (float2) raw3.rg * HB_GPU_INV_UNITS;
float4 p12 = q12 - float4 (renderCoord, renderCoord);
float2 p3 = q3 - renderCoord;
if (vLeftRay) {
if (min (min (p12.y, p12.w), p3.y) * pixelsPerEm.y > 0.5) break;
} else {
if (max (max (p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) break;
}
uint code = _hb_gpu_calc_root_code (p12.x, p12.z, p3.x);
if (code != 0u)
{
float2 a = q12.xy - q12.zw * 2.0 + q3;
float2 b = q12.xy - q12.zw;
float2 r = _hb_gpu_solve_vert_poly (a, b, p12.xy) * pixelsPerEm.y;
float2 cov = vLeftRay ? clamp (float2 (0.5, 0.5) - r, 0.0, 1.0)
: clamp (r + float2 (0.5, 0.5), 0.0, 1.0);
if ((code & 1u) != 0u)
{
ycov -= cov.x;
ywgt = max (ywgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0));
}
if (code > 1u)
{
ycov += cov.y;
ywgt = max (ywgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0));
}
}
}
return _hb_gpu_calc_coverage (xcov, ycov, xwgt, ywgt);
}
/* Return coverage in [0, 1].
*
* Caller must declare: StructuredBuffer<int4> hb_gpu_atlas
*
* renderCoord: em-space sample position
* glyphLoc: texel offset of glyph blob in atlas
*/
/* The MSAA-aware implementation. Caller supplies pixelsPerEm so
* this function can be invoked from non-uniform control flow (for
* example from a paint op-stream branch). */
float _hb_gpu_slug (float2 renderCoord, float2 pixelsPerEm, uint glyphLoc_)
{
float c = _hb_gpu_slug_single (renderCoord, pixelsPerEm, glyphLoc_);
#ifndef HB_GPU_NO_MSAA
float ppem = hb_gpu_ppem (renderCoord, glyphLoc_);
if (ppem < 16.0)
{
float2 emsPerPixel = 1.0 / pixelsPerEm;
float2 d = emsPerPixel * (1.0 / 3.0);
float msaa = 0.25 *
(_hb_gpu_slug_single (renderCoord + float2 (-d.x, -d.y), pixelsPerEm, glyphLoc_) +
_hb_gpu_slug_single (renderCoord + float2 ( d.x, -d.y), pixelsPerEm, glyphLoc_) +
_hb_gpu_slug_single (renderCoord + float2 (-d.x, d.y), pixelsPerEm, glyphLoc_) +
_hb_gpu_slug_single (renderCoord + float2 ( d.x, d.y), pixelsPerEm, glyphLoc_));
c = lerp (c, msaa, smoothstep (16.0, 8.0, ppem));
}
#endif
return c;
}
/* Stem darkening for small sizes.
*
* coverage: output of hb_gpu_draw
* brightness: foreground brightness in [0, 1]
* ppem: pixels per em at this fragment
*/
float hb_gpu_stem_darken (float coverage, float brightness, float ppem)
{
return pow (coverage,
lerp (pow (2.0, brightness - 0.5), 1.0,
smoothstep (8.0, 48.0, ppem)));
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Requires GLSL 3.30 or GLSL ES 3.00. */
/* Dilate a glyph vertex by half a pixel on screen.
*
* position: object-space vertex position (modified in place)
* texcoord: em-space sample coordinates (modified in place)
* normal: object-space outward normal at this vertex
* jac: inverse of the 2x2 linear part of the em-to-object transform,
* stored row-major as (j00, j01, j10, j11). Maps object-space
* displacements back to em-space for texcoord adjustment.
* For simple scaling with y-flip (the common case):
* em-to-object = [[s, 0], [0, -s]]
* jac = (1/s, 0, 0, -1/s)
* m: model-view-projection matrix
* viewport: viewport size in pixels
*/
void hb_gpu_dilate (inout vec2 position, inout vec2 texcoord,
vec2 normal, vec4 jac,
mat4 m, vec2 viewport)
{
vec2 n = normalize (normal);
vec4 clipPos = m * vec4 (position, 0.0, 1.0);
vec4 clipN = m * vec4 (n, 0.0, 0.0);
float s = clipPos.w;
float t = clipN.w;
float u = (s * clipN.x - t * clipPos.x) * viewport.x;
float v = (s * clipN.y - t * clipPos.y) * viewport.y;
float s2 = s * s;
float st = s * t;
float uv = u * u + v * v;
float denom = uv - st * st;
float d = abs (denom) > 1.0 / 16777216.0
? s2 * (st + sqrt (uv)) / denom
: 0.0;
vec2 dPos = d * normal;
position += dPos;
texcoord += vec2 (dot (dPos, jac.xy), dot (dPos, jac.zw));
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Requires Metal Shading Language 2.0. */
/* Dilate a glyph vertex by half a pixel on screen.
*
* position: object-space vertex position (modified in place)
* texcoord: em-space sample coordinates (modified in place)
* normal: object-space outward normal at this vertex
* jac: inverse of the 2x2 linear part of the em-to-object transform,
* stored row-major as (j00, j01, j10, j11). Maps object-space
* displacements back to em-space for texcoord adjustment.
* For simple scaling with y-flip (the common case):
* em-to-object = [[s, 0], [0, -s]]
* jac = (1/s, 0, 0, -1/s)
* m: model-view-projection matrix
* viewport: viewport size in pixels
*/
void hb_gpu_dilate (thread float2& position, thread float2& texcoord,
float2 normal, float4 jac,
float4x4 m, float2 viewport)
{
float2 n = normalize (normal);
float4 clipPos = m * float4 (position, 0.0, 1.0);
float4 clipN = m * float4 (n, 0.0, 0.0);
float s = clipPos.w;
float t = clipN.w;
float u = (s * clipN.x - t * clipPos.x) * viewport.x;
float v = (s * clipN.y - t * clipPos.y) * viewport.y;
float s2 = s * s;
float st = s * t;
float uv = u * u + v * v;
float denom = uv - st * st;
float d = abs (denom) > 1.0 / 16777216.0
? s2 * (st + sqrt (uv)) / denom
: 0.0;
float2 dPos = d * normal;
position += dPos;
texcoord += float2 (dot (dPos, jac.xy), dot (dPos, jac.zw));
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Requires WGSL (WebGPU Shading Language). */
/* Dilate a glyph vertex by half a pixel on screen.
*
* position: object-space vertex position (modified in place)
* texcoord: em-space sample coordinates (modified in place)
* normal: object-space outward normal at this vertex
* jac: inverse of the 2x2 linear part of the em-to-object transform,
* stored row-major as (j00, j01, j10, j11). Maps object-space
* displacements back to em-space for texcoord adjustment.
* For simple scaling with y-flip (the common case):
* em-to-object = [[s, 0], [0, -s]]
* jac = (1/s, 0, 0, -1/s)
* m: model-view-projection matrix
* viewport: viewport size in pixels
*
* Returns (new_position, new_texcoord).
*/
fn hb_gpu_dilate (position: vec2f, texcoord: vec2f,
normal: vec2f, jac: vec4f,
m: mat4x4f, viewport: vec2f) -> array<vec2f, 2>
{
let n = normalize (normal);
let clipPos = m * vec4f (position, 0.0, 1.0);
let clipN = m * vec4f (n, 0.0, 0.0);
let s = clipPos.w;
let t = clipN.w;
let u = (s * clipN.x - t * clipPos.x) * viewport.x;
let v = (s * clipN.y - t * clipPos.y) * viewport.y;
let s2 = s * s;
let st = s * t;
let uv = u * u + v * v;
let denom = uv - st * st;
var d: f32;
if (abs (denom) > 1.0 / 16777216.0) {
d = s2 * (st + sqrt (uv)) / denom;
} else {
d = 0.0;
}
let dPos = d * normal;
return array<vec2f, 2> (
position + dPos,
texcoord + vec2f (dot (dPos, jac.xy), dot (dPos, jac.zw))
);
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Requires Shader Model 5.0+. */
/* Dilate a glyph vertex by half a pixel on screen.
*
* position: object-space vertex position (modified in place)
* texcoord: em-space sample coordinates (modified in place)
* normal: object-space outward normal at this vertex
* jac: inverse of the 2x2 linear part of the em-to-object transform
* m: model-view-projection matrix
* viewport: viewport size in pixels
*/
void hb_gpu_dilate (inout float2 position, inout float2 texcoord,
float2 normal, float4 jac,
float4x4 m, float2 viewport)
{
float2 n = normalize (normal);
float4 clipPos = mul (m, float4 (position, 0.0, 1.0));
float4 clipN = mul (m, float4 (n, 0.0, 0.0));
float s = clipPos.w;
float t = clipN.w;
float u = (s * clipN.x - t * clipPos.x) * viewport.x;
float v = (s * clipN.y - t * clipPos.y) * viewport.y;
float s2 = s * s;
float st = s * t;
float uv = u * u + v * v;
float denom = uv - st * st;
float d = abs (denom) > 1.0 / 16777216.0
? s2 * (st + sqrt (uv)) / denom
: 0.0;
float2 dPos = d * normal;
position += dPos;
texcoord += float2 (dot (dPos, jac.xy), dot (dPos, jac.zw));
}
…ÿt<1ÀƒÿuVH�ƒþtJwwH�ƒþt<ƒþºH�HEÂÀH�ƒþtvƒþH�ºHEÂÃfDH�ƒþtíƒþºH�HEÂăþºH�HEÂÃGCC: (conda-forge gcc 14.3.0-19) 14.3.0 GNUÀÀzRx �¤ñÿ ¤hb-gpu.cchb_gpu_shader_sourceD)üÿÿÿÿÿÿÿ0ÄPCĪT¿kì ¼´›¬{ .symtab.strtab.shstrtab.text.data.bss.rodata.rela.text.hb_gpu_shader_source.comment.note.GNU-stack.note.gnu.property.rela.eh_frame@!@'@,@È9PȤ4@ÊÀ T0ôÈ)]Ém É0…PÉ0€@ØÊ
ۃx
 øÉ ðÊ�/0 0 0 0 644 44680 `
ELF>ˆš@@PO BCDEHƒþ†÷AVAUATI‰üUH‰õSH‰ÓA‹$H‰èM�\¬üHÑèH‰ÑHkÒ8I�„òD‹I‰ÒHkÒ8òL1Òf/È—Â1öf/Á@—Æ)òƒúu
E‰$f(ÈA‰Ê‰A‹H‰ÑHkÒ8òD1Òf/Á—Â1öf/È@—Æ)òƒú„ýA‹SüM‰à‰I�SüE‰SüA‹@I�xI‰ÁHkÀ8òD1Àf/È—À1Éf/Á—Á)ȃøÿtk�ff.„ff.„ff.„‹BüHƒêH‰ÁHkÀ8òD1Àf/È—À1öf/Á@—Æ)ðƒøtÒH9×s%‰D‰
A‹CüI‰ÂHkÀ8òLI‰øé_ÿÿÿ€D‰L)çHÿÍM�pHÁÿE‰KüH‰ÚH)ýI‰ýH9ïsvH‰þL‰çM‰ôèwþÿÿHƒý‡ˆþÿÿ[]A\A]A^ÉE‰A‹$H‰ÑHkÒ8òD‹I‰ÒHkÒ8òL1Òf/È—Â1öf/Á@—Æ)òƒú…ÀþÿÿE‰$f(ÈA‰Ê‰é®þÿÿH‰îL‰÷L‰íèþÿÿëˆÃHƒþ†íAVAUATI‰üUH‰õSH‰ÓA‹$H‰èM�\¬üHÑèH‰ÑHkÒ8I�„ò‹I‰ÒHkÒ8ò 1Òf/Á—Â1öf/È@—Æ)òƒúu
E‰$f(ÈA‰Ê‰A‹H‰ÑHkÒ8ò1Òf/È—Â1öf/Á@—Æ)òƒú„øA‹SüM‰à‰I�SüE‰SüA‹@I�xI‰ÁHkÀ8ò1Àf/Á—À1Éf/È—Á)ȃøÿtjf�ff.„ff.„ff.„‹BüHƒêH‰ÁHkÀ8ò1Àf/Á—À1öf/È@—Æ)ðƒøtÓH9×s‰D‰
A‹CüI‰ÂHkÀ8ò I‰øéaÿÿÿ�D‰L)çHÿÍM�pHÁÿE‰KüH‰ÚH)ýI‰ýH9ïstH‰þL‰çM‰ôèþÿÿHƒý‡�þÿÿ[]A\A]A^ÉE‰A‹$H‰ÑHkÒ8ò‹I‰ÒHkÒ8ò 1Òf/Á—Â1öf/È@—Æ)òƒú…ÇþÿÿE‰$f(ÈA‰Ê‰éµþÿÿH‰îL‰÷L‰íè þÿÿëŠÃHƒþ†÷AVAUATI‰üUH‰õSH‰ÓA‹$H‰èM�\¬üHÑèH‰ÑHkÒ8I�„òD‹I‰ÒHkÒ8òL1Òf/È—Â1öf/Á@—Æ)òƒúu
E‰$f(ÈA‰Ê‰A‹H‰ÑHkÒ8òD1Òf/Á—Â1öf/È@—Æ)òƒú„ýA‹SüM‰à‰I�SüE‰SüA‹@I�xI‰ÁHkÀ8òD1Àf/È—À1Éf/Á—Á)ȃøÿtk�ff.„ff.„ff.„‹BüHƒêH‰ÁHkÀ8òD1Àf/È—À1öf/Á@—Æ)ðƒøtÒH9×s%‰D‰
A‹CüI‰ÂHkÀ8òLI‰øé_ÿÿÿ€D‰L)çHÿÍM�pHÁÿE‰KüH‰ÚH)ýI‰ýH9ïsvH‰þL‰çM‰ôèwþÿÿHƒý‡ˆþÿÿ[]A\A]A^ÉE‰A‹$H‰ÑHkÒ8òD‹I‰ÒHkÒ8òL1Òf/È—Â1öf/Á@—Æ)òƒú…ÀþÿÿE‰$f(ÈA‰Ê‰é®þÿÿH‰îL‰÷L‰íèþÿÿëˆÃHƒþ†÷AVAUATI‰üUH‰õSH‰ÓA‹$H‰èM�\¬üHÑèH‰ÑHkÒ8I�„òD‹I‰ÒHkÒ8òL1Òf/Á—Â1öf/È@—Æ)òƒúu
E‰$f(ÈA‰Ê‰A‹H‰ÑHkÒ8òD1Òf/È—Â1öf/Á@—Æ)òƒú„ýA‹SüM‰à‰I�SüE‰SüA‹@I�xI‰ÁHkÀ8òD1Àf/Á—À1Éf/È—Á)ȃøÿtk�ff.„ff.„ff.„‹BüHƒêH‰ÁHkÀ8òD1Àf/Á—À1öf/È@—Æ)ðƒøtÒH9×s%‰D‰
A‹CüI‰ÂHkÀ8òLI‰øé_ÿÿÿ€D‰L)çHÿÍM�pHÁÿE‰KüH‰ÚH)ýI‰ýH9ïsvH‰þL‰çM‰ôèwþÿÿHƒý‡ˆþÿÿ[]A\A]A^ÉE‰A‹$H‰ÑHkÒ8òD‹I‰ÒHkÒ8òL1Òf/Á—Â1öf/È@—Æ)òƒú…ÀþÿÿE‰$f(ÈA‰Ê‰é®þÿÿH‰îL‰÷L‰íèþÿÿëˆÃSH‰ûH‹?ÿH‰ß[ÿ%AWfH~ÀfH~òflÓAVfoðfI~Èf(ÂAUATI‰ÄfH~øfoüUH’flýL‰ÅSfHnàI‰ÆI‰Õ‰ûHƒìXH‰T$fÖ $f(Ê4$fÉ)<$d$)t$0)T$)d$ ÿò-foT$f/胉ƒûuHƒÄX1À[]A\A]A^A_Ãf($ò
)T$@òfXÂfYÁfXD$0fXD$ fYÃf(ð)D$föf(Îÿf/w™fD( $foT$@ÿÃfInÄfDXL$ fo|$0‰ßf(fD(D$òDfD\ÊfA(èfA(ÈfÉfA(ðfM~ÇòL$@fD\ÏfDYËf(Úf\ßfAYÚfA\éD)L$0f(åfífXßf(ùfHnÍf(ÓfÛè\þÿÿfD(D$fD(„ÀfD(L$0„Ùþÿÿfo4$f(l$ fA(ØfInýfAXÙòL$@HƒÄX‰ßf\î[fInÇ]A\A]f(ÓfÛfAYêfXîfInöA^A_f(åfíéáýÿÿ�fo,$)T$f(ýf(Åfÿf(ÏÿòfoT$f/Ø‚?þÿÿHƒÄX¸[]A\A]A^A_ÃSH‹H…Ûu[Ã�1ÒH‰ØðH±uáÿH9ÃtâH‰ß[ÿ%ATf(ôfD(Àf(úUfõfDÁfûSH‰ûHƒì`�4ÿÿ‡þ‹GD‹W@‰õ�p9Ð�ûH‹KHHkÀ8‰sDH‹HÈxóo=@ˆh0Dp =óo=H‰H�=óo==H9Є‚s òsPòDC`ÿC4fD(ÎòDÂÈfATñfDUÈfDVÎòsXf(þòÂùfT÷fUùfVþf(ðòAÂðfTÆfAUðfD(ÁfVðòChòDÂÀfATÈfDUÀf(ÇòÂÃfDVÁfA(ÉòÂÊfTøfUÃfVÇf(úfDTÉòÂþfUÊfAVÉfT×fUþf(ófVúòAÂðf(ÑòÂÔfTÞfAUðfTÊfUÔfVófVÑf(ÈòÂÍfTÁfUÍfVÈf(ÄfÑòÂÇf(ÍòÂÎSPfTàfUÇfTéfUÎfVÄfVÍfÁC`HƒÄ`[]A\Ã÷Ò‰S@óoH‹óoH‰óoÆC8HƒÄ`[]A\Ã…Òx±A‰Ô9òƒöýÿÿ�D‰àÑèF�d A9ôrñA�ü$I’wˆD‰æHkö8…Ò…!Hƒ{H„òl$XH‰÷òd$Pò\$HòT$@òL$8òD$0)|$ D)D$)4$ÿf(4$fD(D$H…Àf(|$ H‰ÁòD$0òL$8òT$@ò\$Hòd$Pòl$X„‹SDH‹sHHkÒ8H…Òtuòl$XH‰Çòd$Pò\$HòT$@òL$8òD$0)|$ D)D$)4$ÿf(4$fD(D$f(|$ òD$0H‰ÁòL$8òT$@ò\$Hòd$Pòl$X‹CDH‰KHD‰c@�pé±üÿÿòl$XH‹{Hòd$Pò\$HòT$@òL$8òD$0)|$ D)D$)4$ÿf(4$fD(D$H…Àf(|$ H‰ÁòD$0òL$8òT$@ò\$Hòd$Pòl$X…nÿÿÿ‹C@D9àr‹CDH‹KH�péüÿÿ÷ЉC@é½ýÿÿfïífïä€~8H‰÷óZëóZâtWfV f.âf(úf(òfÿz
uf.ïzt6¶w0@„ötÆG0WfïÛfïÒóZÙóZÐf(Ïf(Æé„Ãfïífïä€~8H‰÷óZéóZàtWf.n(fv ºf(þf(Ö›ÀfÿEÂf(Ï„Àtf.f ›ÀE„Àu¶w0@„ötÆG0wf(Ùf(Âé�À8fÁtÆG0G À8f(àf(étZf.o(fw ºf(þf(Ö›ÀfÿEÂf(Ï„Àtf.G ›ÀE„Àu"¶w0@„ötÆG0wf(Ùf(Âé@À~8t"€~0uòFf.F òNzuf.N(z
uÀH‰÷é€~8ÁtgSH‰óHƒì€~0uò^f.^ òNz uf.N(zuZÐÆC0S HƒÄ[Ã@ZÐH‰ßf(Ã)$è€{8f($uÐëÖ„À8f(âf(ëtJfW f.âf(úf(òfÿz
uf.ßzt)¶w0@„ötÆG0Wf(Ùf(Ðf(Ïf(ÆéÃfDoêfDoÎfDoÐUfDlëfDlÏfDlÑSfDoÄfE(åfE(ñH‰ýfE\âfDlÅfE(ùfE\ðH�ìò%dH‹%(H‰œ$ø‰ófDL$`fA(ôföfA(þfA(Æf(ÞfÿfWf(ÏòAYÌòYÃòXÁf(ÈfT
f/á†wƒû
„ÆòÿÃH‰ïD)”$€D)Œ$à‰ÞfDYðfDYàfEXðfE\ÅfEXâfDYÀfA(ÎD)´$ÐD)¤$�òœ$˜fA(ÐfAXÕf\ÊfYÈfXÊfA\ÔfD(Âf(Ñ)Œ$ÀòŒ$ˆfDYÀfEXÄD)„$ fA\Ðò¤$ ò¬$¨fYÂò”$�fAXÀ)„$°ò„$€ò´$°ò¼$¸èFþÿÿH‹„$ødH+%(…æò´$à‰ÞH‰ïò¼$èò¤$Ðò¬$Øò”$Àòœ$Èò„$°òŒ$¸H�Ä[]éÔýÿÿ@fA(òfA(àfE(Ú1ÿföfäòE\ØD)$f(ÎfïöòDL$hò\Ìf(þD)d$PòDYÛD)t$@D)T$0òAYÌD)l$ D)L$òDXÙf(ÎòD^ØòfEÛfEYÞfEXØfA(ÛfA(ëD)\$pfA\ÚfA\éfYØfYèf(ÆfAXÚfAXéfA\ÝfA\èf(ÓfÛf(åfíèfDo$fDoL$„ÀfDol$ fDoT$0fD(t$@fD(d$PòD|$h„{ýÿÿH‹„$ødH+%(fD(\$pu_ò\$`fA(ÃfEÛH�ÄH‰ïfA(×fA(Ë[]éDH‹„$ødH+%(uòL$`H�ÄH‰ïfA(Ç[]éÿ€8t"€0uòGf.G òOzuf.O(z
uÀé€8fD(ÈfD(Ñf(ôf(ýtsf.w fG ¸f(àfD(ð›ÂfäEÐfD(ì„ÒtMf.o(›ÂEЄÒt>fA.ñ›ÂEЄÒt/f.é›ÂEЄÒt!f.ò›ÂEЄÒtf.ë›ÂD„ÀtÃ@f(æfD(ßòA\æòE\Ýf(ÌfA(ëòAYëòYÌòXÍfïíf/ÍvyòD%fE(Áò-òE\ÆòD^áfA(ÊòA\ÍòEYÃòYÌòD\ÁòEYÀòEYÄfA/èr0f(ÊfD(ÃòA\ÎòE\ÅòAYËòAYàò\ÌòYÉòAYÌf/és4fD(ÀfÀf(â1öf(Èf(ëfA(ÑfA(ÚfA(Àéf„f(Ïf(ÆéH‰÷óZÀóZíóZäóZÛóZÒóZÉéHƒì¾@¿ÿH‰ÂH…ÀtAò1À¹(H‰×óH«ÆB0BPòÆB8B`ÇÆBHÇB‹H‰ÐHƒÄÃH‰øH…ÿt ‹‹…ÒtðÿÃH…ÿ„/AUATUSH‰ûHƒì‹‹…À„ðÿ…ýH‹¿8ÿÇS!ÿÿH‹kH…í…õ‹ƒ(ÿÀƒøvǃ,H‹»0ÿ‹ƒÿÀƒøvǃH‹» ÿ‹ƒÿÀƒøvǃ H‹»ÿ‹ƒøÿÀƒøvǃüH‹»ÿ‹ƒèÿÀƒøvǃìH‹»ðÿ‹ƒØÿÀƒøvǃÜH‹»àÿ‹ƒÈÿÀƒøvǃÌH‹»Ðÿ‹ƒ¸ÿÀƒøvǃ¼H‹»Àÿ‹ƒ¨ÿÀƒøvǃ¬H‹»°ÿ‹ƒ˜ÿÀƒøvǃœH‹» ÿ‹ƒˆÿÀƒøvǃŒH‹»�ÿ‹CxÿÀƒøvÇC|H‹»€ÿ‹C@ÿÀƒøvÇCDH‹{HÿHƒÄH‰ß[]A\A]ÿ%DHƒÄ[]A\A]ÃDÀ‹E,…À„¥H‰ïÿ‹E,…ÀtA�PÿH‰ïH‰ÐH� RH‹U0H�ÊL‹bL‹j‰E,ÿM…ätÅL‰ïAÿÔH‰ïÿ‹E,…Àu¿‹E(ÿÀƒøvÇE,H‹}0ÿHÇE(H‰ïHÇE0ÿH‰ïÿH‰ïÿHÇCé\ýÿÿ�‹E(ÿÀƒøwHÇE(HÇE0ëÄ@H‹}0ÿëÞH…ÿ„W‹…À„MAWAVI‰ÖH�WAUE‰ÅATUH‰ÍSH‰óHƒì(L‹:M…ÿ„“M‰üH…Û„¿E…ít L‰ðH è„ÖfInÖfHnËL‰çflÊ) $ÿA‹L$,I‹|$0…É„HH‰ú1Àëf�ÿÀHƒÂ9Á„úH;uíH�@H�ÇH…Û„äE…ítgL‹kfo$H‰kL‰çL‹sÿM…ítL‰÷AÿÕA‹T$(1À…Òx1ÀH…Û•ÀHƒÄ([]A\A]A^A_ÃDHƒÄ(1À[]A\A]A^A_ÀL‰ç1Ûÿë´L‰çÿA‹L$,I‹t$0…É„H‰ò1Àëff.„ÿÀHƒÂ9Á„âH;uíH�@H�ÆH…À„Ì�QÿH‹hH‹XL‰çH‰ÑH�RH�ÖóoH‹RH‰PA‰L$,ÿH…ítH‰ßÿÕ¸é)ÿÿÿDH‰$¾8¿ÿI‰ÄH…À„ÿÿÿ1öH‰ÇÿH‹$L‰øIÇD$(IÇD$0ðL±"„!þÿÿA‹L$,…É„÷H‰$L‰çÿA‹D$,H‹$…À„o�HÿH‰$L‰çH‰ÈH�4II‹L$0H� ñH‹qL‹yA‰D$,H‰t$ÿM…ÿH‹$t§H‹|$Aÿ×H‹$ë™DA‹D$(�Q9Á�»H�IA‰T$,fo$$H�ÇH‰k#L‰çÿéþÿÿA‹D$(º…ÀË…­»‰ÞHkö…À…`H…ÿ„WH‰÷ÿH‰ÇH…À„TA‹T$,I‹t$0HkÒH…Òt ÿH‰ÇA‹L$,I‰|$0A‰\$(�Qéaÿÿÿf„L‰çÿécþÿÿf�1ÀÃ…Àx)‰Ã9Ѓ7ÿÿÿ‰ÙÑé�\ 9Órô�ûªªª
†_ÿÿÿ÷ÐA‰D$(óoH‹H�H‰éÿÿÿA‹D$(ÿÀƒøvH‰$I‹|$0AÇD$,ÿH‹$H‰$L‰çIÇD$(IÇD$0ÿH‹$H‰$L‰çÿL‰çÿH‹$éüÿÿA‹D$(ÿÀƒøvH‰$I‹|$0ÿH‹$IÇD$(IÇD$0ë®ÿH‰ÇH…À…ÈþÿÿA‹D$(9Ø‚ÿÿÿA‹L$,I‹|$0�Qé!þÿÿUSHƒìH…ÿtv‹…ÀtpH‹oH…ítgH‰óH‰ïÿ‹M,H‹u0…ÉtGH‰ò1Àë„ÿÀHƒÂ9Át.H;uñH�@H�ÆH…ÀtH‹XH‰ïÿHƒÄH‰Ø[]ÃDH‰ïÿ1ÛHƒÄH‰Ø[]ÃUSHƒìH‹H…ÛtH‰ÝHƒÄH‰è[]Ã�ÿ1É1ÒH�5H‰ÅH‰Çÿ1É1ÒH�5H‰ïÿ1É1ÒH�5H‰ïÿ1É1ÒH�5H‰ïÿ1É1ÒH�5H‰ïÿH‰ïÿH�=ÿH…ít/H‰ØðH±-„[ÿÿÿÿH9Å„=ÿÿÿH‰ïÿé/ÿÿÿÿH‰ÅH‰ØðH±-„#ÿÿÿH…í„ ÿÿÿë½fnÆfnÊfbÁfÖGpÃH…öt‹Gp‰H…Òt‹Gt‰ÃATUH‰õSH‰ûH‰ïHƒìdL‹$%(L‰d$A‰ÔH‰æH�T$ÿ‹T$‹4$H‰ßÿH‰ßÿH‰ÙH‰ïH‰ÂD‰æÿH‹T$dH+%(u HƒÄ[]A\Ãÿÿ%‹G@fïÀÆG0ÇG4ÆG8GG …Àx&òÇGDGPòG`ÃfD÷ЉG@ëÓHÇGpÿ%UH‰õSH‰ûHƒìH‹¿8ÿHǃ8H…ítÿH9ÅtH‰«8HƒÄ[]Ã/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Draw-renderer fragment shader entry. The heavy lifting (Slug
* coverage, MSAA, ppem, stem darkening) lives in the shared
* hb-gpu-fragment.glsl that must be prepended to this source;
* this file only adds the thin hb_gpu_draw() wrapper that lifts
* pixelsPerEm out of fwidth() at uniform control flow before
* calling the shared _hb_gpu_slug(). */
float hb_gpu_draw (vec2 renderCoord, uint glyphLoc_)
{
vec2 pixelsPerEm = 1.0 / fwidth (renderCoord);
return _hb_gpu_slug (renderCoord, pixelsPerEm, glyphLoc_);
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Draw-renderer fragment shader entry (Metal). Heavy lifting
* (Slug coverage, MSAA, ppem, stem darkening) lives in the shared
* hb-gpu-fragment.msl that must be prepended to this source. */
float hb_gpu_draw (float2 renderCoord, uint glyphLoc_,
device const short4* hb_gpu_atlas)
{
float2 pixelsPerEm = 1.0 / fwidth (renderCoord);
return _hb_gpu_slug (renderCoord, pixelsPerEm, glyphLoc_, hb_gpu_atlas);
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Draw-renderer fragment shader entry (WGSL). Heavy lifting
* (Slug coverage, MSAA, ppem, stem darkening) lives in the shared
* hb-gpu-fragment.wgsl that must be prepended to this source. */
fn hb_gpu_draw (renderCoord: vec2f, glyphLoc_: u32,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> f32
{
let pixelsPerEm = 1.0 / fwidth (renderCoord);
return _hb_gpu_slug (renderCoord, pixelsPerEm, glyphLoc_, hb_gpu_atlas);
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Draw-renderer fragment shader entry (HLSL). Heavy lifting
* (Slug coverage, MSAA, ppem, stem darkening) lives in the shared
* hb-gpu-fragment.hlsl that must be prepended to this source. */
float hb_gpu_draw (float2 renderCoord, uint glyphLoc_)
{
float2 pixelsPerEm = 1.0 / fwidth (renderCoord);
return _hb_gpu_slug (renderCoord, pixelsPerEm, glyphLoc_);
}
@À?À?à?à?€ÿÿÿÿÿÿÿê-�™—q=UUUUUUå?ð?Ð?ððÿ0CÿÿÿÿÿÿïàÁÀÿÿÿßA@ÿÿÿÿÿÿÿÿàÀÀÿß@…ÿt<1ÀƒÿuJH�ƒþt>wGH�ƒþt0ƒþºH�HEÂÀÿÎH�ºƒþHGÂÃf.„ƒþºH�HEÂÃ…öˆxAUATUH‰ýSHƒì‹…Àˆ�A‰Õ‰ó‰Â„Éu.9ðrr‹M9Ùs E„í…‰]HƒÄ¸[]A\A]À‹O9ÎA‰ÌDCæD9àrgÁêA9ÔsÀA�ü$I’w=E…ä…žH‹ÿ‹M1ÿé®f„‰ÑÑé�T9ÚrôA‰Ô�ú$I’v"÷ЉEHƒÄ1À[]A\A]ÀA�ü$I’wÞD‰æHkö8…ÀuLHƒ}tEH‰÷ÿH‰ÇH…À„ˆ‹UH‹uH‰ÑHkÒ8H…Òt2ÿ‹MH‰Çë$D‰æHkö8„H‹}ÿH‰ÇH…ÀtF‹MH‰}D‰eéßþÿÿfD‰Ú‰Ï)ÊHkÿ8H}kÒ8…Ò„Íþÿÿ1öÿéÀþÿÿf�1ÀÃD‹ED9à‚#ÿÿÿé•þÿÿ…öˆxAUATUH‰ýSHƒì‹…Àˆ÷A‰Õ‰ó‰Â„Éu.9ðrr‹M9Ùs E„í…‰]HƒÄ¸[]A\A]À‹O9ÎA‰ÌDCæD9à‚›ÁèA9Äs¼E…ä…ªH‹ÿ‹M1ÿé²ff.„‰ÑÑé�T9Úrô�úÿÿÿ?w]A‰Ôf„D‰æHÁæ…ÀudHƒ}t]H‰÷ÿH‰ÇH…À„¡‹UH‹uH‰ÑHÁâtMÿ‹MH‰Çë?A�üÿÿÿ?v¯÷ЉEHƒÄ1À[]A\A]ÃDD‰æHÁæH‹}ÿH‰ÇH…ÀtG‹MH‰}D‰eéàþÿÿ€‰ÚH‹E)ÊÁâH�<ˆ„Ñþÿÿ‰Ò1öÿéÂþÿÿ@1ÀÃD‹ED9àr�é™þÿÿAWAVI‰þAUATUSH�ìȶW8H…ö„H‰ð„Ò„ D‹W4E…Ò„·òOPf.
z„¢ó~òf(ÁfTÃf(ãf.Ї� òAfXf(ëf(ÄfTÃf.Ї; òAF`f(óf(èfTëf.Õ‡å
ò=f(éfTëf.ý‚*f(ìfTëf.ý‚f(èfTëf.ý‚òAvhfD(Ãf(îfTëf.Õv7òH,Öfïíf(ÖòD
fDUÆòH*êòÂÕfATÑòXêf(õfAVðfTÞf(éf.ûfî‚ òò\Áò\æf(Óf(ËfÂÕfÄfÂÈfTêfUÓfTÁfVÕfUËòfVÈf(ÂfÂÃfTÐfUÃfVÂf(ÑfÂÓfæÀfTÊfUÓfVÑfæÊflÁE‹fDE…äu(„ÿH‰Åé2f�fïÀE‹fDE…ätàA‹FxM‹nH…Àˆ«
A‹†ˆ…Àˆ� A‹†˜…Àˆh A‹†¨…Àˆ@ A‹†¸…Àˆ A‹†È…Àˆð
A‹†Ø…ÀˆÈ
A‹†è…Àˆ 
A‹†ø…Àˆx
A‹†…ÀˆP
A‹†…Àˆ(
A‹†(…Àˆí 1ÉI�~xºD‰æè„À„NòòAYFPó~òf(ãf(ÈfTËf.Ñv3òH,ÀfïÉò5fUàòH*Èf(éòÂèfTîò\ÍfVÌf(Áò,Àòf(ãòAYFX‰D$8f(ÈfTËf.Ñv3òH,ÀfïÉò5fUàòH*Èf(éòÂèfTîò\ÍfVÌf(Áò,Àòf(ãòAYF`‰D$@f(ÈfTËf.Ñv3òH,ÀfïÉf(èò5fUàòH*ÈòÂéfTîòXÍfVÌf(Áò,Àòf(ãòAYFh‰D$Hf(ÈfTËf.Ñv3òH,ÀfïÉf(èò5fUàòH*ÈòÂéfTîòXÍfVÌf(Á¿D$8fïÉò,ðD‰ãòHkÛ8I‹–€1Éò*È¿D$@L‰¬$˜fDo‰´$¤òYÈòL$ fïÉò*È¿D$HòYÈòL$fïÉò*È¿ÆJ�4+òYÈòL$0fïÉò*ÈL‰èòYÈòL$(€òDòpòxòDHfE(àfD(îf(ÆòDÂæfD(ßfA(éòEÂÙòEÂèfA(áòÂïfD.ÏfA(ÈfATÄfEUàfDVàfATãò@ fDUßfATÍfDVÜfDUî@›ÇfA(äfDVéf(ÏEùòÂàfTÍfAUéfD(ðfVéòH(DR$fDTôfAUäfD(àf.ùòEÂåfAVæfD(ðA›ÀfETôfEUåDEÁfEVæfAäfD(áD!Ç"fD.Æf(åòÂá@ˆz @›ÇfDTäEùfUåf.ðf(éòAÂëfAVäfD(áA›ÀDEÁfDTåHƒÀ8HƒÂ8fAUëfAVìD!Çfå@ˆzébØH9Æ…�þÿÿòt$(ò\t$fïÀ½A9ìò|$0ò\|$ AFìf/ƃífïÀf(î‰l$ò*ʼnl$dò^èòl$PfïÀf/ǃ¡fïÀf(ï‰l$ò*Åò^èòl$h‹t$1ÉI�¾ˆº)œ$€ò”$�ò|$xòt$pè„À„‹t$1ɺI�¾˜è„À„úD‹|$dI‹¶�òt$pò|$xJ�½ò”$�f(œ$€ƒøƒ1¨…x…À„>Æé6f„‹t$1ɺ)\$PI�¾¨òT$hè„Àtx‹t$1ɺI�¾¸è„Àt]òT$hf(\$P1À1ÒI‹¶°I‹Ž�N� ½ë$�ff.„ff.„HƒÀI9Á„:‰’ÃA‰×¶Ûsâ1íL‰÷ÿH�ÄÈH‰è[]A\A]A^A_Ãf�„Ò…¯ùÿÿëÔfDÇD$½ò|$héaþÿÿ„ÇD$ÇD$dòt$PéþÿÿD�Hÿ‰ÂH‰÷HÇDø‰ÈÁè‰Á1ÀóH«‰èM‹† H‰D$pHÁàƒøs¨…ü…Àt-AÆë'f.„�Hÿ‰ÂL‰ÇIÇDø‰ÈÁè‰Á1ÀóH«‹L$I‹†€fïíf(ãòDL$òDT$ �yÿ‹L$òD\$PHÃòDd$hÿÉë8fDHÇ@$ÿ€x!uf/ý‡&HÇ@,AÿHƒÀ8H9Äþÿÿ€x uÒf/õvÂòD@òE\ÁòE^ÃfA(ÀfTÄf.Ðv<òI,ÐfïÀòD-òH*Âf(ÈòAÂÈfATÍò\Áf(ÌfAUÈfVÁfD(Àò@òA\ÁòA^Ãf(ÈfTÌf.Ñv?òH,ÐfïÉòD5òH*ÊfD(éòDÂèfETîòA\ÍfD(ìfDUèfAVÍf(ÁòA,ÐE1É…ÒAHÑòD,ÈfnÂA9ùDOÏfAnÉfbÁfÖ@$A9ÑŒàþÿÿHcÒÿ–HÿÂ9P(}õéÊþÿÿòòA\ÊòA^Ìf(ÁfTÄf.Ðv?òH,ÑfïÀòD-òH*ÂfD(ÀòDÂÁfETÅòA\ÀfD(ÄfDUÁf(ÈfAVÈò@òA\ÂòA^ÄfD(ÀfDTÄfA.ÐvAòH,ÐfEïÀòD5òL*ÂfE(èòDÂèfETîòE\ÅfD(ìfDUèfA(ÀfAVÅò,ÑE1É…ÒAHÑòD,ÈfnÂA9ÉDOÉfAnÉfbÁfÖ@,A9ÑŒõýÿÿHcÒf�ff.„Aÿ�HÿÂ9P0}ôéÔýÿÿòH,Ðfïíf(øòDfUðòH*êòÂýfATøòXïfVîf(ÅéáôÿÿòH,ÔfïÀò=fUìòH*Âf(ðòÂôfT÷ò\Æf(àfVåé�ôÿÿòH,ÑfïÀò5fUáòH*Âf(èòÂéfTîò\Åf(ÈfVÌé9ôÿÿfïÀé×ûÿÿH‹t$pI‹ŽÀ1ÀI‹– HÁæH‰t$pë&ff.„ff.„HƒÀH9ð„g‰A’ÀE¶Àsãé|ûÿÿAdž,÷ÐA‰†(éúõÿÿ÷ÐAÇF|A‰FxéBõÿÿAdž÷ÐA‰†é¿õÿÿAdž ÷ÐA‰†é—õÿÿAdžü÷ÐA‰†øéoõÿÿAdžì÷ÐA‰†èéGõÿÿAdžÜ÷ÐA‰†ØéõÿÿAdžÌ÷ÐA‰†Èé÷ôÿÿAdž¼÷ÐA‰†¸éÏôÿÿAdž¬÷ÐA‰†¨é§ôÿÿAdžœ÷ÐA‰†˜éôÿÿAdžŒ÷ÐA‰†ˆéWôÿÿ‰ÀAÇAÇDüéûÿÿ‰ÀÇÇDüé¹úÿÿ1ÉI�¾ÈD‰þ‰\$xºL‰Œ$€D‰„$�òT$h)\$Pè„À„íùÿÿ1ÉI�¾ØºD‰þè„À„Ïùÿÿ1ÉI�¾èº‰Þè„À„²ùÿÿ1ÉI�¾øº‰Þè„À„•ùÿÿ‹t$1ɺI�¾è„À„vùÿÿ‹t$1ɺI�¾è„À„Wùÿÿ‹\$dI‹†°I‹¶f(\$P�SÿòT$hD‹T$xƒúD‹„$�L‹Œ$€†Ô
H�VüH)ÂHƒú†Ã
Áë1Ò‰ÙHÁáóoHƒÂH9ÑuîD‹\$dD‰ÙƒáüAöÃt‰ÊHÁâ‹<ÿÁ‰<HƒÂD9Ùrï�UÿM‹žÀI‹¾ ƒú†H
H�WüL)ÚHƒú†7
‰é1ÒÁéHÁáóAoHƒÂH9Êuí@öÅt‰éƒáü‰ÊHÁâA‹ÿÁ‰HƒÂ9érïI‹ž€D‰d$‰l$H‰D$PH�K$L‰\$hI‰Û„Hc;QDI‹®ÐI‹žà@ff.„ff.„‹–D�`D‰$–HÿÂD‰D…D‰ƒ9Q}äHcQ;Q 7I‹®ðI‹žf�ff.„‹—D�`D‰$—HÿÂD‰D…D‰ƒ9Q }ä‹D$AÿÀHƒÁ8A9À‚`ÿÿÿH‹D$PL‰ÛL‹\$hL‰Œ$¨D‹d$‹l$L‰¬$°J� I‹¶�L‰œ$€H‰L$hD‰¼$¸D‰”$¼D‰d$xI‰ô‰¬$�H‰ÅD‹EA‹†ÌE‹,$I‹¾ÐL‰ÁN�<…D9À‚°D)ÀD9èIGÅLÿ…À„›H‰ÆH‰ÚH‰D$H‰|$D‰D$PèH‹|$H‹D$‹L$PL�‡H�wL9Ösf@H9÷sT‹HkÀ8òdH‰ðëf�fÖ@üHƒèH9Çs3‹Püó~@üHkÒ8fpÀáòL1Òf/á—ÂE1Àf/ÌA—ÀD)ƒút¿HƒÆL9ÖržA‹†ÜI‹–à9È‚ª)ÈD9èAGÅI×…À„–A‰ÅL‰ÿH‰ÚL‰îèK�<¯I�wH9þsxfDI9÷s`‹HkÀ8ò$H‰ðë!„ff.„fÖ@üHƒèI9Çs/‹Püó~@üHkÒ8fpÀáò 1Òf/Ì—Â1Éf/á—Á)ʃútÃHƒÆH9þ‚ŽÿÿÿHƒÅIƒÄH9l$h…SþÿÿL‹œ$€H‹L$pL‹Œ$¨L‹¬$°D‹¼$¸LÙL‰ÝD‹”$¼I‹¶ H‰L$hL‰Œ$€L‰¬$¨I‰ôD‰¼$°D‰”$¸�D‹EA‹†ìE‹,$I‹¾ðL‰ÁN�<…D9À‚°D)ÀD9èIGÅLÿ…À„›H‰ÆH‰ÚH‰D$H‰|$D‰D$PèH‹|$H‹D$‹L$PL�‡H�wL9Ösf@H9÷sT‹HkÀ8òdH‰ðëf�fÖ@üHƒèH9Çs3‹Püó~@üHkÒ8fpÀáòL1Òf/á—ÂE1Àf/ÌA—ÀD)ƒút¿HƒÆL9ÖržA‹†üI‹–9È‚—)ÈD9èAGÅI×…À„ƒA‰ÅL‰ÿH‰ÚL‰îèK�<¯I�wH9þsefDI9÷sQ‹HkÀ8òdH‰ðëf�fÖ@üHƒèI9Çs0‹Püó~@üHkÒ8fpÀáòL1Òf/Ì—Â1Éf/á—Á)ʃútÂHƒÆH9þr¡H‹D$hHƒÅIƒÄH9Å…cþÿÿD‹¼$°D‹”$¸D‹d$x‹¬$�L‹Œ$€L‹¬$¨EׂLóÿÿ1ÒE…ÿC� ?˜Âˆ:óÿÿAƒüt$A�t$þI�EhHkö8I�¼5 �¶0HƒÀ8òH9ÇuòDâ‚óÿÿƒÂ‚þòÿÿ‹D$d�D‰D$hÁ‚êòÿÿ‰È‰L$В¶Ò=‡Ñòÿÿ…Ò…ÉòÿÿòòAYFPf(ãf(ÈfTËf.Ñv3òH,ÐfïÉò5fUàòH*Êf(éòÂèfTîò\ÍfVÌf(Áò-f/Å‚dòÿÿò%f/à‚RòÿÿòòAYFXf(óf(ÈfTËf.Ñv5òH,ÐfïÉfUðòDòH*Êf(ùòÂøfATøò\ÏfVÎf(Áf/Å‚óñÿÿf/à‚éñÿÿòòAYF`f(óf(ÈfTËf.Ñv5òH,ÐfïÉf(øòDfUðòH*ÊòÂùfATøòXÏfVÎf(Áf/Å‚Šñÿÿf/à‚€ñÿÿòòAYFhf(ÈfTËf.Ñv3òH,ÐfïÉf(Ðò5fUØòH*ÊòÂÑfTÖòXÊfVËf(Áf/Å‚'ñÿÿf/à‚ñÿÿM‹¾8Áà‰D$M…ÿtH�I9G(„ÕE1ÿ‹|$L‰L$xI‰ýÿH‰ÃH…À„ØðÿÿD‰l$PL‹L$xA‹Fpº€ÿÿ¹€ÿÿfnD$8fnL$Hfn\$dD‰æL‰L$x9ÐfÄD$@fÄŒ$¤LºÿfÄÝfbÁfoË9ÐI�¾(OÂA‹Vt9ÊfnÐLѹÿ9ÊOÑ1ÉfÄÒºfbÊflÁèL‹L$x„À„hM‹–0E1í‹L$L‰t$xL‰L$@H‹¬$˜M‰îL‰|$HL‰T$évòòYE‰D�y‰Œ$˜ÿò„$€òòYEÿò„$�òòYEÿòD$8òòYEÿòL$8ò”$�ò,À‹Œ$˜òœ$€òD,Áò,úò,ó·ÀE·ÀHÁàL À·ÿHÁà·öH øHÁàH ðH‰ËòòYE(ÿD‰øòD$8òòYE L�,ÃÿòL$8ò,ðò,Á·öÁà ðA�vD9æs}¶uhA‰E@„öutòòYEHÿò,ÀòòYEP‰D$8ÿ‹D$8ò,ðIÿÆfA‰uA�OHƒÅ8fA‰EE9æs\H‹D$€}0J�°…wþÿÿ�qÿA‰Ï‰0é1ÿÿÿA‰E1ö1Àë¼H‹\$p1ÒA‹ ‰ HƒÂH9Úuðéíõÿÿ1Ò‹ ‰ HƒÂI9ÑuñélõÿÿL‹t$x‹D$hE1äL‹T$òD$0‰D$I‹†°M‰óòXD$ òYL‹L$@H‰D$8I‹†�M‰ÖL‰L$0H‰D$@òD$ H‹D$8F‹< H‹D$@F‹ E…À„_I‹‹€D‰À1ÿE‰ÁM‹«ÐòD$ A�wÿI‹«à€B�?ÿÇA‹T•HkÒ8òT…ÀuéM€ff.„ÿÈ„3�0‹T•HkÒ8ò f/Êwâ9ljÂC×D9Êsf(ÂA‰ÑD9ÇušòYL‰\$xD‰D$hÿ‹L$D‹D$hD‰ÿò,ÐL‹\$x‰ÈA�4)Ïf.„ff.„D� ‰ÁÿÀG‹L�H� ËG‹ ŽÇAfA�Á€fD‰ E1ÉfD‰I9ðuÎE�,0‰ðA)÷DA�<‰ÁE1ÒÿÀ‹|½H� ËA‹<¾ÇAfD‰Qf�Ç€f‰9A9Åuз·L$f�Æ€E·À·ÖHÁàf�Á€H зÉHÁàH ÈHÁàL ÀJ‰DcIƒÄL9d$0tD‰l$éLþÿÿ‰úéãþÿÿI‹³ òD$(òXD$I‹ƒÀH‰t$‹t$dòYL‹|$HH‰D$ƒÆ‰t$ H‹t$pL‰|$8E‰ïHÆH‰t$0òD$(H‹D$D‹ H‹D$D‹E…À„I‹‹€D‰À1ÿE‰ÁM‹«ðòD$(A�t$ÿI‹«fDB�'ÿÇA‹T•HkÒ8òT…ÀuéSf�ÿÈ„I�0‹T•HkÒ8òLf/Êwá9ljÂC×D9Êsf(ÂA‰ÑD9Çu©òYL‰\$HD‰D$@ÿD‹D$@D‰çD‰øò,ÐL‹\$HD)ÿC�4ff.„ff.„D� ‰ÁÿÀG‹L�H� ËG‹ ŽÇAfA�Á€fD‰ E1ÉfD‰I9ðuÎE� 0‰ðA)ôDA�<‰ÁÿÀ‹|½H� ËA‹<¾ÇAf�Ç€f‰91ÿf‰yD9ÈuÒf�Æ€·ÂE·ÀHƒD$·ÖHÁàE�—€ÿÿHƒD$H ÐE·Ò‹T$ HÁàL ÐH‰ÖHÁàÿÆL À‰t$ H‹t$0H‰ÓH‹D$H9ðtE‰ÏéHþÿÿ‰úéÎþÿÿI‹«8L‹|$8M‰ÞIǃ8H…ítH�H9E(„J¿ÿH‰ÁH…À„H‰‹D$PºH‰ß‹t$L�‰AÿH‰Åé–éÿÿòòYD$(L‰\$HD‰D$@ÿD‹D$@D‰þE‰ùò,ÐL‹\$HéíþÿÿòòYD$ L‰\$xD‰D$hÿD‹l$D‹D$hò,ÐL‹\$xD‰îé¶üÿÿM‹o ‹t$A‹EI‹]‰D$P9ðƒ°‹t$L‰L$xH‰ß‰ðÑèƉö‰t$PI‰÷ÿL‹L$xH…ÀH‰ÃtEI‰EE‰}E1ÿéü÷ÿÿI‹†8H…Àt
H�H9P(tH‰ßÿé£èÿÿH‹@ H;ué
(A BBBA eÃÆÌÍÎDdøLŽB�B ŒD(†D0ƒt
(A BBBA cÃÆÌÍÎD¬LŽB�B ŒD(†D0ƒ|
(A BBBA eÃÆÌÍÎDôLŽB�B ŒD(†D0ƒ|
(A BBBA eÃÆÌÍÎ<AƒPxXlB�PŽO �B(ŒM0†J8ƒQ�U
8C0A(B BBBD 7
8G0F(B BXBN |8F0A(B BBBÔ5AƒM
B _Dô8BŒN†N ƒG€Æ
 AABA H
 AABD <qPqdxiŒ8$ qJƒG q
AE hÃÈYDܽP†Pƒm Ø
AAI ?
NAJ `
IAE $58}L `eD`x\ŒK�BŒA †A(ƒG0þ
(Dà AÆBÌBÍK D(A ABBFÃÆÌÍH0ƒ†Œ�|ì‡U�BŽI �E(ŒA0†D8ƒG`Ç
8A0A(B BBBF D
8C0A(B BBBH @ÃÆÌÍÎÏC`ƒ†Œ�Ž�0l�A†AƒD g
DAF ODA( ýA†AƒD S
DAB Ìà0ôzBŒA†D ƒJ0^
 AABA (<OP$dBA†DƒG sAAŒt\ ™J�BŒA †D(ƒD0p
(F ABBH h
(C ABBH °ÃÆÌÍH0ƒ†Œ�\•J�BŒA †D(ƒD0p
(F ABBH Â
(C ABBF XÃÆÌÍH0ƒ†Œ�P`ÐB�BŽE �B(ŒA0†A8ƒG€
8D0A(B BBBC ñÿMø ‹ 
É
 

lW5x“8·qqF8‹q Ï ½"$&,& (*+-/134579;=?@BDFu" œ²ºÀØîû$GijY�¡"5Æ$}î(e *)+=M`u‹-‡¥¸/�Ò1ýèý;_¡¾Å3Û4ñ5z 87J9O\;n=B‡™@t³"B™÷þ"D•'FÐ:@hb-gpu-draw.cc_Z13hb_qsort_loopIjZ18hb_gpu_draw_encodeEUlRKjS1_E_EvPT_mT0__Z13hb_qsort_loopIjZ18hb_gpu_draw_encodeEUlRKjS1_E0_EvPT_mT0__Z13hb_qsort_loopIjZ18hb_gpu_draw_encodeEUlRKjS1_E1_EvPT_mT0__Z13hb_qsort_loopIjZ18hb_gpu_draw_encodeEUlRKjS1_E2_EvPT_mT0__ZL32hb_gpu_cubic_farthest_fit_inside20hb_gpu_cu2qu_point_tS_S_S_dj.constprop.0_ZL26free_static_gpu_draw_funcsv_ZL21static_gpu_draw_funcs_ZL8acc_emitP13hb_gpu_draw_tbdddddd_ZL24hb_gpu_draw_quadratic_toP15hb_draw_funcs_tPvP15hb_draw_state_tffffS1__ZL19hb_gpu_draw_line_toP15hb_draw_funcs_tPvP15hb_draw_state_tffS1__ZL22hb_gpu_draw_close_pathP15hb_draw_funcs_tPvP15hb_draw_state_tS1__ZL19hb_gpu_draw_move_toP15hb_draw_funcs_tPvP15hb_draw_state_tffS1__ZL26hb_gpu_cubic_to_quadraticsP13hb_gpu_draw_t20hb_gpu_cu2qu_point_tS1_S1_S1_dj.constprop.0_ZL20hb_gpu_draw_cubic_toP15hb_draw_funcs_tPvP15hb_draw_state_tffffffS1__ZN9hb_blob_t20recycle_data_destroyEPv_GLOBAL_OFFSET_TABLE_hb_freehypothb_draw_funcs_get_emptyhb_draw_funcs_destroy_hb_NullPool_hb_CrapPoolhb_mallocmemcpyhb_realloc_ZN13hb_gpu_draw_t11acc_move_toEdd_ZN13hb_gpu_draw_t11acc_line_toEdd_ZN13hb_gpu_draw_t12acc_conic_toEdddd__stack_chk_fail_ZN13hb_gpu_draw_t14acc_close_pathEv_ZN13hb_gpu_draw_t12acc_cubic_toEddddddhb_gpu_draw_create_or_failhb_callochb_gpu_draw_referencehb_gpu_draw_destroyhb_blob_destroypthread_mutex_lockpthread_mutex_unlockpthread_mutex_destroyhb_gpu_draw_set_user_datapthread_mutex_inithb_gpu_draw_get_user_datahb_gpu_draw_get_funcshb_draw_funcs_createhb_draw_funcs_set_move_to_funchb_draw_funcs_set_line_to_funchb_draw_funcs_set_quadratic_to_funchb_draw_funcs_set_cubic_to_funchb_draw_funcs_set_close_path_funchb_draw_funcs_make_immutableatexithb_gpu_draw_set_s
)aüÿÿÿÿÿÿÿ)Küÿÿÿÿÿÿÿ+)düÿÿÿÿÿÿÿ1D1üÿÿÿÿÿÿÿ01$ E1¿k1‰)8üÿÿÿÿÿÿÿð)>üÿÿÿÿÿÿÿ)?üÿÿÿÿÿÿÿ6)@üÿÿÿÿÿÿÿu)güÿÿÿÿÿÿÿ„)8üÿÿÿÿÿÿÿ×)>üÿÿÿÿÿÿÿù)?üÿÿÿÿÿÿÿ5)@üÿÿÿÿÿÿÿs)güÿÿÿÿÿÿÿB1,R1üZ1<°1D1T1LŽ1Tâ)düÿÿÿÿÿÿÿÄfüÿÿÿÿÿÿÿÔ1\â1üê1<
1;1\h1–1\Ç1ñ1\"1X1$1lùhüÿÿÿÿÿÿÿhüÿÿÿÿÿÿÿžhüÿÿÿÿÿÿÿ¹hüÿÿÿÿÿÿÿ%)aüÿÿÿÿÿÿÿ« 1
1ª
1
1£ 1Ø 1 1%hüÿÿÿÿÿÿÿChüÿÿÿÿÿÿÿ`hüÿÿÿÿÿÿÿ}hüÿÿÿÿÿÿÿœhüÿÿÿÿÿÿÿ»hüÿÿÿÿÿÿÿEüÿÿÿÿÿÿÿüÿÿÿÿÿÿÿUüÿÿÿÿÿÿÿ üÿÿÿÿÿÿÿY1\‚1¬1|¾1„Ð1\þ191\g1¢1\Ë16üÿÿÿÿÿÿÿ6)>üÿÿÿÿÿÿÿÛhüÿÿÿÿÿÿÿ"1\:)jüÿÿÿÿÿÿÿK1\V)jüÿÿÿÿÿÿÿg1\r)jüÿÿÿÿÿÿÿ€1\‹)jüÿÿÿÿÿÿÿé1\ô)jüÿÿÿÿÿÿÿ1\)jüÿÿÿÿÿÿÿH1\S)jüÿÿÿÿÿÿÿ_1\n)jüÿÿÿÿÿÿÿ1Üú1\
)jüÿÿÿÿÿÿÿ)1Üû1\ )jüÿÿÿÿÿÿÿ:6üÿÿÿÿÿÿÿO)>üÿÿÿÿÿÿÿu6üÿÿÿÿÿÿÿ~)küÿÿÿÿÿÿÿŽ1\¤)jüÿÿÿÿÿÿÿÅ1\Û)jüÿÿÿÿÿÿÿ3)@üÿÿÿÿÿÿÿc6üÿÿÿÿÿÿÿr)8üÿÿÿÿÿÿÿ¥)8üÿÿÿÿÿÿÿ h°ø @ \ Øø@Th|�¤Ìà( <!P"d$|%�&ð'p(¤)Ð*ä+ø,,-@.T/h0�2¤34d5.symtab.strtab.shstrtab.text.data.bss.text._Z13hb_qsort_loopIjZ18hb_gpu_draw_encodeEUlRKjS1_E_EvPT_mT0_.text._Z13hb_qsort_loopIjZ18hb_gpu_draw_encodeEUlRKjS1_E0_EvPT_mT0_.text._Z13hb_qsort_loopIjZ18hb_gpu_draw_encodeEUlRKjS1_E1_EvPT_mT0_.text._Z13hb_qsort_loopIjZ18hb_gpu_draw_encodeEUlRKjS1_E2_EvPT_mT0_.rela.text._ZN9hb_blob_t20recycle_data_destroyEPv.rela.text._ZL32hb_gpu_cubic_farthest_fit_inside20hb_gpu_cu2qu_point_tS_S_S_dj.constprop.0.rela.text._ZL26free_static_gpu_draw_funcsv.rela.text._ZL8acc_emitP13hb_gpu_draw_tbdddddd.rela.text._ZL24hb_gpu_draw_quadratic_toP15hb_draw_funcs_tPvP15hb_draw_state_tffffS1_.rela.text._ZL19hb_gpu_draw_line_toP15hb_draw_funcs_tPvP15hb_draw_state_tffS1_.text._ZN13hb_gpu_draw_t11acc_move_toEdd.rela.text._ZN13hb_gpu_draw_t11acc_line_toEdd.rela.text._ZL22hb_gpu_draw_close_pathP15hb_draw_funcs_tPvP15hb_draw_state_tS1_.rela.text._ZL19hb_gpu_draw_move_toP15hb_draw_funcs_tPvP15hb_draw_state_tffS1_.rela.text._ZN13hb_gpu_draw_t12acc_conic_toEdddd.rela.text._ZL26hb_gpu_cubic_to_quadraticsP13hb_gpu_draw_t20hb_gpu_cu2qu_point_tS1_S1_S1_dj.constprop.0.rela.text._ZN13hb_gpu_draw_t14acc_close_pathEv.rela.text._ZN13hb_gpu_draw_t12acc_cubic_toEdddddd.rela.text._ZL20hb_gpu_draw_cubic_toP15hb_draw_funcs_tPvP15hb_draw_state_tffffffS1_.rela.text.hb_gpu_draw_create_or_fail.text.hb_gpu_draw_reference.rela.text.hb_gpu_draw_destroy.rela.text.hb_gpu_draw_set_user_data.rela.text.hb_gpu_draw_get_user_data.rela.text.hb_gpu_draw_get_funcs.text.hb_gpu_draw_set_scale.text.hb_gpu_draw_get_scale.rela.text.hb_gpu_draw_glyph_or_fail.rela.text.hb_gpu_draw_glyph.rela.text.hb_gpu_draw_clear.rela.text.hb_gpu_draw_reset.rela.text.hb_gpu_draw_recycle_blob.rodata.rela.text.hb_gpu_draw_shader_source.rela.text._ZN11hb_vector_tI26hb_gpu_encode_curve_info_tLb0EE11resize_fullEibb.rela.text._ZN11hb_vector_tIjLb0EE11resize_fullEibb.rela.text.hb_gpu_draw_encode.comment.note.GNU-stack.note.gnu.property.rela.eh_frame.group»@ M6»L Mf»X Mhd!d'h,h@opø@³h@÷p@@x;@8x0M r
N6 èoOÀ’Â/18 0 0 0 644 102832 `
ELF>0x@@fe
"#HIJKNOPQRSTUVWXYZ[\] !FGLMƆ]1Àˆ@…Àtÿȉ†@ˆ@ƒøwmHƒÀH‹~0‰T$¸óoF HÁàfo
H‰L$ÀHðH‰|$ØL$àfoL$¸@foD$ØÇD$ð@ óoD$ä@,‹†@ÿÀ‰†@ÃfDÿÀƆ]‰†@ÃUóäD(Ã(òSH‰óH�V(Hƒì8ó~ó~ä‹C(ó~VóD~NóDÛó~Ûó>H‹~YãóDÒó~ÒóDfóA~ÙóEÉD(÷óDYðóYþóAYóóAYÃó~äXâóÕóEXôó~äóAXòó~ÒD(èYÓó^óEXêH‹v ó~ÒXâ(ÓóYÑóAYØóEYÁóìfH~åóAYÉóDXò(×óAXÔóAXðóDXéóXÓ(Þ…Àt,H‹O8H‹G(H…ÉtH‹IA(ÍA(ÆÿÐH‰k4HƒÄ8[]ÃfDH‹O8óK8óC4H‹GH…ÉtH‹ H‰T$H‰t$H‰<$ól$,ód$(óDl$$óDt$ ó\$óT$ÿÐH‹C4ÇC(ól$,ód$(ó\$óT$H‰C,óDl$$óDt$ H‹T$H‹t$H‹<$é<ÿÿÿSH‰ûH‹?ÿH‰ß[ÿ%H‹~H…ÿtGATI‰ÌU‰ÕSH‰ó‰Öÿ…ÀtH‹{‰îÿA‰$¸[]A\ÃD[1À]A\Ãf„1ÀÃUH‰ÕSH‰óHƒìƒ¾@wpH‹¾pH…ÿtOÿH‹»p‹SL‹sHÿóoC H‹C0ƃ\H‹»pH‰ƒTƒDH‰}HƒÄ[]ÿ%�ÿH‰ƒpH‰ÇH…ÀuœÆƒ]1ÀHÇEHƒÄ[]ÃSH‹H…Ûu[Ã�1ÒH‰ØðH±uáÿH9ÃtâH‰ß[ÿ%SH‹H…Ûu[Ã�1ÒH‰ØðH±uáÿH9ÃtâH‰ß[ÿ%AUATUSH‰óHƒì‹F(H‹nL‹f …Àt:óF,.F4L�n(óN0z@u>.N8z8u6H‹M8H‹E0H…ÉtH‹I L‰êL‰æH‰ïÿÐfïÀÇC(C,HƒÄ[]A\A]ÃH‹M8H‹EH…ÉtH‹IL‰êL‰æH‰ïÿÐë¬UóÀóÉH�V(SH‰óHƒì(ó~ó~Àó~É‹C(H‹~YÂó~Vó~ÀXÂó~VH‹v YÊó~Àó~ÉXÁóØfH~Å(Ð…Àt*H‹O8H‹GH…ÉtH‹I(Ë(ÂÿÐH‰k4HƒÄ([]ÃfDH‹O8óK8óC4H‹GH…ÉtH‹ H‰T$H‰t$H‰<$ó\$óT$ÿÐH‹C4ÇC(ó\$óT$H‹T$H‹t$H‰C,H‹<$érÿÿÿUóÒ(àH�V(SóÛH‰óHƒì8ó~ó~Ò‹C(ó~vó~nó~Ûó~øóÀóDFH‹~Y×ó~þóDYÁóöóYÄó~ÒX×ó~ýYßóíó>óXÆóYüó~ÒóX~H‹v óYéó~ÛXÓóAXøóÚóXèfH~Õ…Àt-H‹O8H‹G H…ÉtH‹I(Í(ÇÿÐH‰k4HƒÄ8[]Ãf„H‹O8óK8óC4H‹GH…ÉtH‹ H‰T$H‰t$H‰|$ó\$,óT$(ól$$ó|$ ÿÐH‹C4ÇC(ó\$,óT$(ól$$ó|$ H‰C,H‹T$H‹t$H‹|$éUÿÿÿóÐAVóÉAUATUH‰õSó~ó~Ò‹F(YÂó~Vó~ÀXÂó~Ñó~NYÊó~Àó~ÉXÁfH~Ã…ÀuH‰]4[]A\A]A^ÃfDóF,.F4L�v(L‹fL‹n óN0zDuB.N8z<u:I‹L$8I‹D$0H…ÉtH‹I L‰òL‰îL‰çÿÐÇE(HÇE,H‰]4[]A\A]A^ÃI‹L$8I‹D$H…ÉtH‹IL‰òL‰îL‰çÿÐë¦UÛÉÒSÀóíH‰óóäËÂHƒì8‹F<‹V8�H9Ð�¤H‹{@óoS H�@‰K<H�ÇH‹S0óoH‰PH‹H‰H�H9ЄWó~S(ó~í[ YÕó~ìó~c Yåó~Òó~äXÔó~c0ó~ÒXÔ(ãÆãîYÌÛYØS0XËK HƒÄ8[]Ã…ÒˆÚ‰Õ9ʃJÿÿÿfD‰èÑè�l(9Írô�ýªªª
‡­‰èH�4@HÁæ…Ò…ËHƒ{@„À)D$ H‰÷d$l$) $ÿ( $(D$ H…Àó~l$ó~d$H‰Ç„½‹S<H‹s@HkÒH…Òt1)D$ d$l$) $ÿ( $(D$ ó~l$ó~d$H‰Ç‹C<H‰{@‰k8�Héƒþÿÿ÷Ò‰S8H‹óoH‰ƃ]HƒÄ8[]ÃH‹{@)D$ d$l$) $ÿ( $(D$ H…Àó~l$ó~d$H‰Çu„‹C89èr‹C<H‹{@�Héþÿÿ÷ЉC8é{ÿÿÿ‹V<…Ò„­U�Jÿ1íSH‰ÈH� IH‰óHƒìH‹~@…ÀIèH� ÏóoF H‹IH‰N09Õs<‹V8‰n<�Jƒùv.…Òx*9êr1‰ÑÁé9Ís=ªªª
…À$ÿ1Àë0÷Ò‰S8HƒÄ[]Ã@=ªªª
éHcÅH�4@HÁæÿH…ÀtH‰C@‰k8HƒÄ[]ÃDËC89ès½÷ЉC8ë¶AU1ÉI‰ý1ÒATUSHƒìdH‹,%(H‰l$H‰õ1öH‰ïÿ‰Ã‰D$A‹…x…Û޶…Àx{A‰Ä9ØrSI‹�€A‹…|9ØriA‰�|H�T$1öH‰ïÿ¸H‹T$dH+%(…HƒÄ[]A\A]ÃfDD‰âÑêF�d"A9ÜrñA�üUUUvX÷ÐA‰…x1À붉Ú)ÂH�@�RH�<�Áâtƒ‰Ò1öÿI‹�€émÿÿÿfD…ÀxÅAÇ…|1Àémÿÿÿf�D‰âH�4RHÁæ…ÀuYIƒ½€tOH‰÷ÿH‰ÁH…ÀtSA‹…|I‹µ€H�@HÁât H‰ÏÿH‰ÁI‰�€E‰¥xéãþÿÿ€I‹½€ÿH‰ÁH…ÀuÑA‹…xD9àƒ°þÿÿéÿÿÿÿSÿ1É1ÒH�5H‰ÃH‰Çÿ1É1ÒH‰ßH�5ÿ1É1ÒH‰ßH�5ÿ1É1ÒH‰ßH�5ÿ1É1ÒH‰ßH�5ÿ1É1ÒH‰ßH�5ÿ1É1ÒH‰ßH�5ÿ1É1ÒH‰ßH�5ÿ1É1ÒH‰ßH�5ÿ1ÉH‰ß1ÒH�5ÿ1ÉH‰ß1ÒH�5ÿH‰ß1É1ÒH�5ÿH‰ß1É1ÒH�5ÿ1É1ÒH�5H‰ßÿH‰ßÿH�=ÿH‰Ø[ÃHƒì¾�¿ÿH‰ÂH…À„Ã1À¹2H‰×¾€?óH«¹€?f‰r.H‹5f‰J"H‹
H�‚�H�ºPH‰0HƒÀ@H‰HÈHÇ@ÐH9Çuè(1ÀÇ‚@HÇ‚T‚Dfof‰‚\‚`fïÀHÇ‚pHÇ‚x‚€ÇÆBHÇB‹H‰ÐHƒÄÃH‰øH…ÿt ‹‹…ÒtðÿÃH…ÿ„AUATUH‰ýSHƒì‹‹…À„îðÿ…åH‹ÿH‹]p‹ElL�$ÃI9ÜtH‹;HƒÃÿI9ÜuîH‹½pÿH‹½ˆÿÇES!ÿÿH‹]H…Û… ‹…xÿÀƒøvÇ…|H‹½€ÿ‹EhÿÀƒøvÇElH‹}pÿ‹EPÿÀƒøvÇETH‹}Xÿ‹E8ÿÀƒøvÇE<H‹}@ÿHƒÄH‰ï[]A\A]ÿ%HƒÄ[]A\A]ÃDÀ‹C,…À„­H‰ßÿ‹C,…ÀtA�PÿH‰ßH‰ÐH� RH‹S0H�ÊL‹bL‹j‰C,ÿM…ätÅL‰ïAÿÔH‰ßÿ‹C,…Àu¿‹C(ÿÀƒøvÇC,H‹{0ÿHÇC(H‰ßHÇC0ÿH‰ßÿH‰ßÿHÇEé±þÿÿf„‹C(ÿÀƒøwHÇC(HÇC0ë¼H‹{0ÿëâH…ÿ„W‹…À„MAWAVI‰ÖH�WAUE‰ÅATUH‰ÍSH‰óHƒì(L‹:M…ÿ„“M‰üH…Û„¿E…ít L‰ðH è„ÖfInÖfHnËL‰çflÊ) $ÿA‹L$,I‹|$0…É„HH‰ú1Àëf�ÿÀHƒÂ9Á„úH;uíH�@H�ÇH…Û„äE…ítgL‹kfo$H‰kL‰çL‹sÿM…ítL‰÷AÿÕA‹T$(1À…Òx1ÀH…Û•ÀHƒÄ([]A\A]A^A_ÃDHƒÄ(1À[]A\A]A^A_ÀL‰ç1Ûÿë´L‰çÿA‹L$,I‹t$0…É„H‰ò1Àëff.„ÿÀHƒÂ9Á„âH;uíH�@H�ÆH…À„Ì�QÿH‹hH‹XL‰çH‰ÑH�RH�ÖóoH‹RH‰PA‰L$,ÿH…ítH‰ßÿÕ¸é)ÿÿÿDH‰$¾8¿ÿI‰ÄH…À„ÿÿÿ1öH‰ÇÿH‹$L‰øIÇD$(IÇD$0ðL±"„!þÿÿA‹L$,…É„÷H‰$L‰çÿA‹D$,H‹$…À„o�HÿH‰$L‰çH‰ÈH�4II‹L$0H� ñH‹qL‹yA‰D$,H‰t$ÿM…ÿH‹$t§H‹|$Aÿ×H‹$ë™DA‹D$(�Q9Á�»H�IA‰T$,fo$$H�ÇH‰k#L‰çÿéþÿÿA‹D$(º…ÀË…­»‰ÞHkö…À…`H…ÿ„WH‰÷ÿH‰ÇH…À„TA‹T$,I‹t$0HkÒH…Òt ÿH‰ÇA‹L$,I‰|$0A‰\$(�Qéaÿÿÿf„L‰çÿécþÿÿf�1ÀÃ…Àx)‰Ã9Ѓ7ÿÿÿ‰ÙÑé�\ 9Órô�ûªªª
†_ÿÿÿ÷ÐA‰D$(óoH‹H�H‰éÿÿÿA‹D$(ÿÀƒøvH‰$I‹|$0AÇD$,ÿH‹$H‰$L‰çIÇD$(IÇD$0ÿH‹$H‰$L‰çÿL‰çÿH‹$éüÿÿA‹D$(ÿÀƒøvH‰$I‹|$0ÿH‹$IÇD$(IÇD$0ë®ÿH‰ÇH…À…ÈþÿÿA‹D$(9Ø‚ÿÿÿA‹L$,I‹|$0�Qé!þÿÿUSHƒìH…ÿtv‹…ÀtpH‹oH…ítgH‰óH‰ïÿ‹M,H‹u0…ÉtGH‰ò1Àë„ÿÀHƒÂ9Át.H;uñH�@H�ÆH…ÀtH‹XH‰ïÿHƒÄH‰Ø[]ÃDH‰ïÿ1ÛHƒÄH‰Ø[]ÃUSHƒìH‹H…ÛtH‰ÝHƒÄH‰è[]Ã�èH‰ÅH…Àt$H‰ØðH±-tÚÿH9ÅtÀH‰ïÿëµÿH‰ÅH‰ØðH±-t­H…ít™ë̉wËGÃH‹H…ÿtÿ%�ÃSH‰ûHƒìH‹H…ÿtÿH‹{HƒÄ[ÿ%f.„‰T$ ‰t$ÿ‹t$‹T$ H…ÀH‰CH‰Çu¿HƒÄ1À[ÃfnÆfnÊfbÁfÖGHÃH…öt‹GH‰H…Òt‹GL‰ÃAUATUH‰õSH‰ûH‰ïHƒìdL‹$%(L‰d$A‰ÔH‰æH�T$ÿ‹T$‹4$H‰ßÿD‹kH‰ßÿA¹ÿH‰ÙH‰ïH‰ÂE‰èD‰æÿH‹T$dH+%(u HƒÄ[]A\A]ÃÿAUATUH‰õSH‰ûH‰ïHƒìdL‹$%(L‰d$A‰ÔH‰æH�T$ÿ‹T$‹4$H‰ßÿD‹kH‰ßÿA¹ÿH‰ÙH‰ïH‰ÂE‰èD‰æÿH‹D$dH+%(u HƒÄ[]A\A]ÃÿATUH‰ýS‹GP…ÀˆŽH‹]p‹ElÇETÇE`L�$ÃI9ÜtH‹;HƒÃÿI9Üuî‹Eh…Àxg1À(ÇElf‰…\‹E8Ç…@HÇE0E …ÀxCfoÇE<…`[]A\Ä÷ЉGPéhÿÿÿfD÷ЉEhë’f„÷ЉE8ë¶AWAVAUATUSH‰ûHƒìXH‰t$dH‹%(H‰D$H1À€¿]…�‹W`…Ò„"L‹gp‹Gl‹oTM�4ÄM9ô„‹E1í„I‹<$IƒÄÿAÅM9æuêAöÅ…P‹CTH‹³ˆA�DE‰D$H‰t$H…ötH�H9F(„ÒHÇD$‹|$I‰þÿI‰ÇH…À„D‰t$D‹slE…öˆ‚HÇ$A½E…ö…Z‹C`º€ÿÿD‹KTM�gAÇGH‹sXfA‰1ÀfA‰G‹ƒ`9ÐLºÿ9ÐOº€ÿÿfA‰G‹ƒd9ÐLºÿ9ÐOº€ÿÿfA‰G
‹ƒh9ÐLºÿ9ÐOº€ÿÿfA‰G ‹ƒlIÇG9ÐLºÿ9ÐOÂC� fA‰G…Òt
L‰çÿD‹KT1öE…É„·‰ðA¿Dfƒø�8f…ÀˆG�VÇD$0A·T‰Ñf�âÁéƒáf÷ÚÒÁà‰L$4H�L$@‰D$<ƒâ‰T$8H�T$0‹…Àt;�<0H‰øI�<|ÿÀM�D·E·ÁàD ÐD9ðsL‹$H˜A‹ƒA‰ÂAÁêfD‰fA‰HƒÂH9Ñu¶ƒÆ D9΂RÿÿÿC� M�dH‹Cp‹SlH� ÐI‰ÆH9ÁtZH‰\$(L‰|$ I‰Ïf.„I‹H‰ßÿ1öH‰ß‰ÅÿH‰ÆH…ít H‰êL‰çÿIƒÆIìM9÷uÇL‹|$ H‹\$(Hƒ|$t@‹ƒ`‹‹l‹³h‹“dfnÀfnÙ)Æ)ÊH‹D$fbÃfnÎfnÒfbÊflÁH‹«ˆHǃˆH…ítH�H9E(„¥¿ÿH‰ÁH…À„ÏL‰8‹D$ºL‰ÿ‹t$L�‰AÿH‰Åé¯H‹ƒˆH‰D$H…ÀtH‹D$H‹@(H�H9Є¿L‰ÿÿ„1íH‰ßÿH‹D$HdH+%(…¦HƒÄXH‰è[]A\A]A^A_ÀƒèfƒøwƒÆéKþÿÿ€H‹ƒˆH…Àt
H�H9P(t7L‰ÿÿ1íAƒýtˆH‹<$ÿéyÿÿÿ€ÿH‰Åédÿÿÿf�H‹@ L;8uÀëÇDL‹f ‹L$A‹D$M‹<$‰D$9ȃ’‹L$L‰ÿ‰ÈÑèÈBÁ‰Æ‰t$I‰öÿI‰ÇH…Àt&I‰$E‰t$HÇD$éúûÿÿD‰èE1íé™ûÿÿI‹$H‰D$é¿ûÿÿH‹D$L‹u H…ÀtL9øt H‰Çÿ‹D$M‰>A‰F‹D$L‰}‰EéÿÿÿM…ÿ„˜þÿÿD‹slE…öˆ9HÇD$é�ûÿÿ1Éff.„‰ÈÑè�LD9ñró�ùÿÿÿ?‡þÿÿ‰Î‰L$ 1ÿHÁæÿ‹L$ H…ÀH‰$„ÈD‰òÁât‰L$ H‹<$‰Ò1öÿ‹L$ Áí‰L$(D‰ðL‹,$ƒÅL‰|$ E1äI‰ß‰ëH‰ÅDD‰áL‰èE9ôse‰A;Ols=I‹GpJ�àH‹8IÿÄIƒÅÿÁèÃL9åuË‹L$(L‰ûL‹|$ D�iéÏúÿÿfDH‹H�
H‰H‰Èëµf.„‹H�5‰H‰ðë‡H‹ƒˆH‰D$H…À…2ýÿÿéAýÿÿH‹D$H‹@ L98…/ýÿÿé;ýÿÿÿSH‰ûHÇGHÇGH‹ÿHÇCH‰ß[ÿ%UH‰õSH‰ûHƒìH‹¿ˆÿHǃˆH…ítÿH9ÅtH‰«ˆHƒÄ[]Ã/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Paint-renderer fragment shader.
*
* Assumes the shared fragment helpers (hb-gpu-fragment.glsl) and
* the draw-renderer fragment helpers (hb-gpu-draw-fragment.glsl)
* are prepended to this source. The draw helper provides
* hb_gpu_draw() which this interpreter calls to compute clip-glyph
* coverage.
*/
/* Fetch the i'th stop of a gradient color line starting at @stops_base
* (2 texels per stop). Resolves is_foreground to @foreground. */
vec4 _hb_gpu_stop_color (int stops_base, int i, vec4 foreground, out float offset)
{
ivec4 a = hb_gpu_fetch (stops_base + i * 2);
offset = float (a.r) / 32767.0;
ivec4 b = hb_gpu_fetch (stops_base + i * 2 + 1);
if ((a.g & 1) != 0)
return vec4 (foreground.rgb, foreground.a * (float (b.a) / 32767.0));
return vec4 (b) / 32767.0;
}
/* Apply the color-line extend mode to a projected `t` value. */
float _hb_gpu_extend_t (float t, int extend)
{
if (extend == 1) { /* HB_PAINT_EXTEND_REPEAT */
return t - floor (t);
} else if (extend == 2) { /* HB_PAINT_EXTEND_REFLECT */
float u = t - 2.0 * floor (t * 0.5);
return u > 1.0 ? 2.0 - u : u;
}
return clamp (t, 0.0, 1.0); /* PAD (default) */
}
/* Walk stops starting at @stops_base and return the sampled color
* at @t. Same logic reused by all gradient subtypes. */
vec4 _hb_gpu_eval_stops (int stops_base, int stop_count, float t, vec4 foreground)
{
float off_prev;
vec4 col_prev = _hb_gpu_stop_color (stops_base, 0, foreground, off_prev);
if (t <= off_prev)
return col_prev;
for (int i = 1; i < stop_count; i++)
{
float off;
vec4 col = _hb_gpu_stop_color (stops_base, i, foreground, off);
if (t <= off)
{
float span = off - off_prev;
float f = span > 1e-6 ? (t - off_prev) / span : 0.0;
/* Interpolate in premultiplied space per OpenType COLR spec. */
vec4 p0 = vec4 (col_prev.rgb * col_prev.a, col_prev.a);
vec4 p1 = vec4 (col.rgb * col.a, col.a);
vec4 pm = mix (p0, p1, f);
return pm.a > 1e-6 ? vec4 (pm.rgb / pm.a, pm.a) : vec4 (0.0);
}
col_prev = col;
off_prev = off;
}
return col_prev;
}
/* Apply the stored 2x2 M^-1 (row-major i16 Q10) to @v. Scaling
* renderCoord deltas back into canonical gradient space. */
vec2 _hb_gpu_apply_minv (ivec4 m, vec2 v)
{
vec4 mf = vec4 (m) * (1.0 / 1024.0);
return vec2 (mf.x * v.x + mf.y * v.y,
mf.z * v.x + mf.w * v.y);
}
/* Sample a linear gradient whose param blob starts at @grad_base:
* texel 0: (p0_rendered.x, p0_rendered.y, d_canonical.x, d_canonical.y)
* texel 1: L^-1 as i16 Q10 (row-major)
* texels 2..: stops (2 texels each)
* Evaluate t in untransformed space. */
vec4 _hb_gpu_sample_linear (vec2 renderCoord, int grad_base,
int stop_count, int extend, vec4 foreground)
{
ivec4 t0 = hb_gpu_fetch (grad_base);
ivec4 m = hb_gpu_fetch (grad_base + 1);
vec2 p0_r = vec2 (float (t0.r), float (t0.g));
vec2 d = vec2 (float (t0.b), float (t0.a));
float denom = dot (d, d);
if (denom < 1e-6) return vec4 (0.0);
vec2 p = _hb_gpu_apply_minv (m, renderCoord - p0_r);
float t = dot (p, d) / denom;
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (grad_base + 2, stop_count, t, foreground);
}
/* Sample a two-circle radial gradient whose param blob starts at
* @grad_base:
* texel 0: (c0_rendered.x, c0_rendered.y, d_canonical.x, d_canonical.y)
* d = c1 - c0 in untransformed space
* texel 1: (r0, r1, _, _) in untransformed font units
* texel 2: L^-1 as i16 Q10 (row-major)
* texels 3..: stops (2 texels each)
* Solves |p - t*cd|^2 = (r0 + t*(r1-r0))^2 with p in untransformed
* space, so non-uniform scale / shear on the transform becomes a
* proper ellipse-in-rendered-space instead of a scalar-fudge. */
vec4 _hb_gpu_sample_radial (vec2 renderCoord, int grad_base,
int stop_count, int extend, vec4 foreground)
{
ivec4 t0 = hb_gpu_fetch (grad_base);
ivec4 t1 = hb_gpu_fetch (grad_base + 1);
ivec4 m = hb_gpu_fetch (grad_base + 2);
vec2 c0_r = vec2 (float (t0.r), float (t0.g));
vec2 cd = vec2 (float (t0.b), float (t0.a));
float r0 = float (t1.r);
float r1 = float (t1.g);
float dr = r1 - r0;
vec2 p = _hb_gpu_apply_minv (m, renderCoord - c0_r);
float A = dot (cd, cd) - dr * dr;
float B = -2.0 * (dot (p, cd) + r0 * dr);
float C = dot (p, p) - r0 * r0;
float t;
if (abs (A) > 1e-6)
{
float disc = B * B - 4.0 * A * C;
if (disc < 0.0) return vec4 (0.0);
float sq = sqrt (disc);
/* Prefer the larger root; fall back to the smaller if the
* larger gives a negative interpolated radius. */
float t1 = (-B + sq) / (2.0 * A);
float t2 = (-B - sq) / (2.0 * A);
t = (r0 + t1 * dr >= 0.0) ? t1 : t2;
}
else
{
if (abs (B) < 1e-6) return vec4 (0.0);
t = -C / B;
}
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (grad_base + 3, stop_count, t, foreground);
}
/* Sample a sweep gradient whose param blob starts at @grad_base:
* texel 0: (center_rendered.x, center_rendered.y, start_q14, end_q14)
* start/end are Q14 fractions of pi in untransformed space
* texel 1: L^-1 as i16 Q10 (row-major)
* texels 2..: stops (2 texels each) */
vec4 _hb_gpu_sample_sweep (vec2 renderCoord, int grad_base,
int stop_count, int extend, vec4 foreground)
{
ivec4 t0 = hb_gpu_fetch (grad_base);
ivec4 m = hb_gpu_fetch (grad_base + 1);
vec2 c_r = vec2 (float (t0.r), float (t0.g));
float a0 = float (t0.b) / 16384.0; /* fraction of pi */
float a1 = float (t0.a) / 16384.0;
float span = a1 - a0;
if (abs (span) < 1e-6) return vec4 (0.0);
vec2 p = _hb_gpu_apply_minv (m, renderCoord - c_r);
/* atan2 returns (-pi, pi]; normalize to [0, 2) fractions of pi. */
float ang = atan (p.y, p.x) / 3.14159265358979;
if (ang < 0.0) ang += 2.0;
float t = (ang - a0) / span;
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (grad_base + 2, stop_count, t, foreground);
}
/* Composite two premultiplied RGBA layers using one of the COLRv1
* compositing modes. Unsupported modes fall back to SRC_OVER.
* Values match hb_paint_composite_mode_t. */
vec4 _hb_gpu_composite (vec4 src, vec4 dst, int mode)
{
vec4 r = src + dst * (1.0 - src.a); /* SRC_OVER default */
/* Approximate unsupported COLRv1 modes with the nearest Porter-Duff
* mode we do implement. Better a recognizable rendering than a
* silent SRC_OVER fallback. DIFFERENCE / EXCLUSION / HSL_* are
* not similar enough to anything we have, so they still fall
* through to SRC_OVER below. */
if (mode == 14 || mode == 18 || mode == 19) mode = 23; /* OVERLAY / COLOR_BURN / HARD_LIGHT -> MULTIPLY */
else if (mode == 17 || mode == 20) mode = 13; /* COLOR_DODGE / SOFT_LIGHT -> SCREEN */
if (mode == 0) r = vec4 (0.0); /* CLEAR */
else if (mode == 1) r = src; /* SRC */
else if (mode == 2) r = dst; /* DST */
else if (mode == 4) r = dst + src * (1.0 - dst.a); /* DST_OVER */
else if (mode == 5) r = src * dst.a; /* SRC_IN */
else if (mode == 6) r = dst * src.a; /* DST_IN */
else if (mode == 7) r = src * (1.0 - dst.a); /* SRC_OUT */
else if (mode == 8) r = dst * (1.0 - src.a); /* DST_OUT */
else if (mode == 9) /* SRC_ATOP */
r = src * dst.a + dst * (1.0 - src.a);
else if (mode == 10) /* DST_ATOP */
r = dst * src.a + src * (1.0 - dst.a);
else if (mode == 11) /* XOR */
r = src * (1.0 - dst.a) + dst * (1.0 - src.a);
else if (mode == 12) /* PLUS */
r = min (src + dst, vec4 (1.0));
else if (mode == 13) { /* SCREEN (premul) */
r.rgb = src.rgb + dst.rgb - src.rgb * dst.rgb;
r.a = src.a + dst.a - src.a * dst.a;
}
else if (mode == 15) { /* DARKEN */
r.rgb = min (src.rgb * dst.a, dst.rgb * src.a)
+ src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
r.a = src.a + dst.a - src.a * dst.a;
}
else if (mode == 16) { /* LIGHTEN */
r.rgb = max (src.rgb * dst.a, dst.rgb * src.a)
+ src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
r.a = src.a + dst.a - src.a * dst.a;
}
else if (mode == 23) { /* MULTIPLY (premul) */
r.rgb = src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a)
+ src.rgb * dst.rgb;
r.a = src.a + dst.a - src.a * dst.a;
}
/* SRC_OVER (3) and DIFFERENCE / EXCLUSION / HSL_* (21, 22, 24-27)
* fall through to the SRC_OVER default. */
return r;
}
/* Wrap _hb_gpu_slug with a sub-glyph extents bail-out. Many
* paint layers cover a small region of the outer glyph quad; for
* fragments outside the layer's bbox (with an AA + MSAA-spread
* margin) the slug coverage is exactly 0, so we can skip the
* band/curve walk entirely. */
float _hb_gpu_slug_clipped (vec2 renderCoord, vec2 pixelsPerEm, uint glyphLoc_)
{
ivec4 header0 = hb_gpu_fetch (int (glyphLoc_));
vec4 ext = vec4 (header0) * HB_GPU_INV_UNITS;
vec2 margin = 2.0 / pixelsPerEm;
if (any (lessThan (renderCoord, ext.xy - margin)) ||
any (greaterThan (renderCoord, ext.zw + margin)))
return 0.0;
return _hb_gpu_slug (renderCoord, pixelsPerEm, glyphLoc_);
}
/* Combine slug coverages from all clip outlines on the current
* layer. Factored out of LAYER_SOLID and LAYER_GRADIENT so the
* shader has one set of inlined slug walks instead of two. flags
* bits: 0x100 = HAS_CLIP2; 0x200 = HAS_CLIP3 (HAS_CLIP3 implies
* HAS_CLIP2). */
float _hb_gpu_layer_coverage (vec2 renderCoord, vec2 pixelsPerEm,
int base, int flags,
int clip1_payload, int clip2_payload, int clip3_payload)
{
float cov = _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
uint (base + clip1_payload));
if ((flags & 0x100) != 0)
{
cov *= _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
uint (base + clip2_payload));
if ((flags & 0x200) != 0)
cov *= _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
uint (base + clip3_payload));
}
return cov;
}
/* Walks the paint blob's flat op stream and returns a
* premultiplied RGBA coverage value for the current fragment.
*
* glyphLoc: atlas texel offset of the paint-blob header.
* foreground: caller-supplied foreground color, used when an op
* sets the is_foreground flag.
*/
#define HB_GPU_PAINT_GROUP_DEPTH 4
vec4 hb_gpu_paint (vec2 renderCoord, uint glyphLoc, vec4 foreground,
out float coverage)
{
/* fwidth once, at uniform control flow: every per-layer
* coverage sample below uses this pre-computed pixelsPerEm via
* _hb_gpu_slug. */
vec2 pixelsPerEm = 1.0 / fwidth (renderCoord);
int base = int (glyphLoc);
ivec4 h0 = hb_gpu_fetch (base); /* (num_ops, _, _, _) */
ivec4 h2 = hb_gpu_fetch (base + 2); /* (ops_offset, _, _, _) */
int num_ops = h0.r;
int cursor = base + h2.r;
vec4 acc = vec4 (0.0);
vec4 group_stack[HB_GPU_PAINT_GROUP_DEPTH];
int sp = 0;
coverage = 0.0;
for (int i = 0; i < num_ops; i++)
{
ivec4 op = hb_gpu_fetch (cursor);
int op_type = op.r;
int aux = op.g;
int payload = (op.b << 16) | (op.a & 0xffff);
if (op_type == 0) /* LAYER_SOLID */
{
/* texel 1: (clip2_hi, clip2_lo, clip3_hi, clip3_lo) -- valid
* per HAS_CLIP2 / HAS_CLIP3 flag bits.
* texel 2: RGBA as signed Q15. */
ivec4 op2 = hb_gpu_fetch (cursor + 1);
int clip2_payload = (op2.r << 16) | (op2.g & 0xffff);
int clip3_payload = (op2.b << 16) | (op2.a & 0xffff);
ivec4 ct = hb_gpu_fetch (cursor + 2);
vec4 col = ((aux & 1) != 0)
? vec4 (foreground.rgb, foreground.a * (float (ct.a) / 32767.0))
: vec4 (ct) / 32767.0;
float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,
base, aux,
payload, clip2_payload, clip3_payload);
coverage = max (coverage, cov);
vec4 src = vec4 (col.rgb * col.a, col.a) * cov;
acc = src + acc * (1.0 - src.a);
cursor += 3;
}
else if (op_type == 1) /* LAYER_GRADIENT */
{
/* texel 1: (clip2_hi, clip2_lo, clip3_hi, clip3_lo) -- valid
* per HAS_CLIP2 / HAS_CLIP3 flag bits.
* texel 2: (grad_payload_hi, grad_payload_lo, extend, stop_count) */
ivec4 op2 = hb_gpu_fetch (cursor + 1);
int clip2_payload = (op2.r << 16) | (op2.g & 0xffff);
int clip3_payload = (op2.b << 16) | (op2.a & 0xffff);
ivec4 op3 = hb_gpu_fetch (cursor + 2);
int grad_payload = (op3.r << 16) | (op3.g & 0xffff);
int extend = op3.b;
int stop_count = op3.a;
int subtype = aux & 0xff;
vec4 col = vec4 (0.0);
if (subtype == 0) /* linear */
col = _hb_gpu_sample_linear (renderCoord,
base + grad_payload,
stop_count, extend, foreground);
else if (subtype == 1) /* radial */
col = _hb_gpu_sample_radial (renderCoord,
base + grad_payload,
stop_count, extend, foreground);
else if (subtype == 2) /* sweep */
col = _hb_gpu_sample_sweep (renderCoord,
base + grad_payload,
stop_count, extend, foreground);
float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,
base, aux,
payload, clip2_payload, clip3_payload);
coverage = max (coverage, cov);
vec4 src = vec4 (col.rgb * col.a, col.a) * cov;
acc = src + acc * (1.0 - src.a);
cursor += 3;
}
else if (op_type == 2) /* PUSH_GROUP */
{
if (sp < HB_GPU_PAINT_GROUP_DEPTH) {
group_stack[sp] = acc;
sp++;
}
acc = vec4 (0.0);
cursor += 1;
}
else if (op_type == 3) /* POP_GROUP */
{
if (sp > 0) {
sp--;
vec4 src = acc;
vec4 dst = group_stack[sp];
acc = _hb_gpu_composite (src, dst, aux);
}
cursor += 1;
}
else
{
break;
}
}
return acc;
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Paint-renderer fragment shader (Metal).
*
* Assumes the shared fragment helpers (hb-gpu-fragment.msl) and
* the draw-renderer fragment helpers (hb-gpu-draw-fragment.msl)
* are prepended to this source.
*/
/* Fetch the i'th stop (2 texels per stop), resolving is_foreground. */
static float4 _hb_gpu_stop_color (device const short4* hb_gpu_atlas,
int stops_base, int i, float4 foreground,
thread float &offset)
{
int4 a = int4 (hb_gpu_atlas[stops_base + i * 2]);
offset = float (a.r) / 32767.0;
int4 b = int4 (hb_gpu_atlas[stops_base + i * 2 + 1]);
if ((a.g & 1) != 0)
return float4 (foreground.rgb, foreground.a * (float (b.a) / 32767.0));
return float4 (b) / 32767.0;
}
static float _hb_gpu_extend_t (float t, int extend)
{
if (extend == 1) return t - floor (t);
if (extend == 2) {
float u = t - 2.0 * floor (t * 0.5);
return u > 1.0 ? 2.0 - u : u;
}
return clamp (t, 0.0, 1.0);
}
static float4 _hb_gpu_eval_stops (device const short4* hb_gpu_atlas,
int stops_base, int stop_count,
float t, float4 foreground)
{
float off_prev;
float4 col_prev = _hb_gpu_stop_color (hb_gpu_atlas, stops_base, 0, foreground, off_prev);
if (t <= off_prev) return col_prev;
for (int i = 1; i < stop_count; i++)
{
float off;
float4 col = _hb_gpu_stop_color (hb_gpu_atlas, stops_base, i, foreground, off);
if (t <= off)
{
float span = off - off_prev;
float f = span > 1e-6 ? (t - off_prev) / span : 0.0;
float4 p0 = float4 (col_prev.rgb * col_prev.a, col_prev.a);
float4 p1 = float4 (col.rgb * col.a, col.a);
float4 pm = mix (p0, p1, f);
return pm.a > 1e-6 ? float4 (pm.rgb / pm.a, pm.a) : float4 (0.0);
}
col_prev = col;
off_prev = off;
}
return col_prev;
}
/* Apply the stored 2x2 M^-1 (row-major i16 Q10) to a vector. */
static float2 _hb_gpu_apply_minv (int4 m, float2 v)
{
float4 mf = float4 (m) * (1.0 / 1024.0);
return float2 (mf.x * v.x + mf.y * v.y,
mf.z * v.x + mf.w * v.y);
}
static float4 _hb_gpu_sample_linear (float2 renderCoord, int grad_base,
int stop_count, int extend,
float4 foreground,
device const short4* hb_gpu_atlas)
{
int4 t0 = int4 (hb_gpu_atlas[grad_base]);
int4 m = int4 (hb_gpu_atlas[grad_base + 1]);
float2 p0_r = float2 (float (t0.r), float (t0.g));
float2 d = float2 (float (t0.b), float (t0.a));
float denom = dot (d, d);
if (denom < 1e-6) return float4 (0.0);
float2 p = _hb_gpu_apply_minv (m, renderCoord - p0_r);
float t = dot (p, d) / denom;
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (hb_gpu_atlas, grad_base + 2, stop_count, t, foreground);
}
static float4 _hb_gpu_sample_radial (float2 renderCoord, int grad_base,
int stop_count, int extend,
float4 foreground,
device const short4* hb_gpu_atlas)
{
int4 t0 = int4 (hb_gpu_atlas[grad_base]);
int4 t1 = int4 (hb_gpu_atlas[grad_base + 1]);
int4 m = int4 (hb_gpu_atlas[grad_base + 2]);
float2 c0_r = float2 (float (t0.r), float (t0.g));
float2 cd = float2 (float (t0.b), float (t0.a));
float r0 = float (t1.r);
float r1 = float (t1.g);
float dr = r1 - r0;
float2 p = _hb_gpu_apply_minv (m, renderCoord - c0_r);
float A = dot (cd, cd) - dr * dr;
float B = -2.0 * (dot (p, cd) + r0 * dr);
float C = dot (p, p) - r0 * r0;
float t;
if (abs (A) > 1e-6)
{
float disc = B * B - 4.0 * A * C;
if (disc < 0.0) return float4 (0.0);
float sq = sqrt (disc);
float t1r = (-B + sq) / (2.0 * A);
float t2r = (-B - sq) / (2.0 * A);
t = (r0 + t1r * dr >= 0.0) ? t1r : t2r;
}
else
{
if (abs (B) < 1e-6) return float4 (0.0);
t = -C / B;
}
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (hb_gpu_atlas, grad_base + 3, stop_count, t, foreground);
}
static float4 _hb_gpu_sample_sweep (float2 renderCoord, int grad_base,
int stop_count, int extend,
float4 foreground,
device const short4* hb_gpu_atlas)
{
int4 t0 = int4 (hb_gpu_atlas[grad_base]);
int4 m = int4 (hb_gpu_atlas[grad_base + 1]);
float2 c_r = float2 (float (t0.r), float (t0.g));
float a0 = float (t0.b) / 16384.0;
float a1 = float (t0.a) / 16384.0;
float span = a1 - a0;
if (abs (span) < 1e-6) return float4 (0.0);
float2 p = _hb_gpu_apply_minv (m, renderCoord - c_r);
float ang = atan2 (p.y, p.x) / 3.14159265358979;
if (ang < 0.0) ang += 2.0;
float t = (ang - a0) / span;
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (hb_gpu_atlas, grad_base + 2, stop_count, t, foreground);
}
static float4 _hb_gpu_composite (float4 src, float4 dst, int mode)
{
float4 r = src + dst * (1.0 - src.a); /* SRC_OVER default */
/* Mode numbers match hb_paint_composite_mode_t. SRC_OVER (3) is
* the default `r` above. */
/* Approximate unsupported modes with the nearest Porter-Duff mode
* we do implement. DIFFERENCE / EXCLUSION / HSL_* still fall
* through to SRC_OVER below. */
if (mode == 14 || mode == 18 || mode == 19) mode = 23; /* OVERLAY / COLOR_BURN / HARD_LIGHT -> MULTIPLY */
else if (mode == 17 || mode == 20) mode = 13; /* COLOR_DODGE / SOFT_LIGHT -> SCREEN */
if (mode == 0) r = float4 (0.0); /* CLEAR */
else if (mode == 1) r = src; /* SRC */
else if (mode == 2) r = dst; /* DST */
else if (mode == 4) r = dst + src * (1.0 - dst.a); /* DST_OVER */
else if (mode == 5) r = src * dst.a; /* SRC_IN */
else if (mode == 6) r = dst * src.a; /* DST_IN */
else if (mode == 7) r = src * (1.0 - dst.a); /* SRC_OUT */
else if (mode == 8) r = dst * (1.0 - src.a); /* DST_OUT */
else if (mode == 9) /* SRC_ATOP */
r = src * dst.a + dst * (1.0 - src.a);
else if (mode == 10) /* DST_ATOP */
r = dst * src.a + src * (1.0 - dst.a);
else if (mode == 11) /* XOR */
r = src * (1.0 - dst.a) + dst * (1.0 - src.a);
else if (mode == 12) /* PLUS */
r = min (src + dst, float4 (1.0));
else if (mode == 13) { /* SCREEN (premul) */
r.rgb = src.rgb + dst.rgb - src.rgb * dst.rgb;
r.a = src.a + dst.a - src.a * dst.a;
}
else if (mode == 15) { /* DARKEN */
r.rgb = min (src.rgb * dst.a, dst.rgb * src.a)
+ src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
r.a = src.a + dst.a - src.a * dst.a;
}
else if (mode == 16) { /* LIGHTEN */
r.rgb = max (src.rgb * dst.a, dst.rgb * src.a)
+ src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
r.a = src.a + dst.a - src.a * dst.a;
}
else if (mode == 23) { /* MULTIPLY (premul) */
r.rgb = src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a)
+ src.rgb * dst.rgb;
r.a = src.a + dst.a - src.a * dst.a;
}
/* SRC_OVER (3) and DIFFERENCE / EXCLUSION / HSL_* (21, 22, 24-27)
* fall through to the SRC_OVER default. */
return r;
}
/* Wrap _hb_gpu_slug with a sub-glyph extents bail-out. Many
* paint layers cover a small region of the outer glyph quad; for
* fragments outside the layer's bbox (with an AA + MSAA-spread
* margin) the slug coverage is exactly 0, so we can skip the
* band/curve walk entirely. */
float _hb_gpu_slug_clipped (float2 renderCoord, float2 pixelsPerEm, uint glyphLoc_,
device const short4* hb_gpu_atlas)
{
int4 header0 = hb_gpu_fetch (hb_gpu_atlas, int (glyphLoc_));
float4 ext = float4 (header0) * HB_GPU_INV_UNITS;
float2 margin = 2.0 / pixelsPerEm;
if (any (renderCoord < ext.xy - margin) ||
any (renderCoord > ext.zw + margin))
return 0.0;
return _hb_gpu_slug (renderCoord, pixelsPerEm, glyphLoc_, hb_gpu_atlas);
}
/* Combine slug coverages from all clip outlines on the layer.
* Factored out so the shader has one set of inlined slug walks
* instead of two (one per LAYER op type). flags bits: 0x100 =
* HAS_CLIP2; 0x200 = HAS_CLIP3 (HAS_CLIP3 implies HAS_CLIP2). */
static float
_hb_gpu_layer_coverage (float2 renderCoord, float2 pixelsPerEm,
int base, int flags,
int clip1_payload, int clip2_payload, int clip3_payload,
device const short4* hb_gpu_atlas)
{
float cov = _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
uint (base + clip1_payload), hb_gpu_atlas);
if ((flags & 0x100) != 0)
{
cov *= _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
uint (base + clip2_payload), hb_gpu_atlas);
if ((flags & 0x200) != 0)
cov *= _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
uint (base + clip3_payload), hb_gpu_atlas);
}
return cov;
}
#define HB_GPU_PAINT_GROUP_DEPTH 4
float4 hb_gpu_paint (float2 renderCoord, uint glyphLoc_, float4 foreground,
device const short4* hb_gpu_atlas,
thread float &coverage)
{
/* fwidth once, at uniform control flow. */
float2 pixelsPerEm = 1.0 / fwidth (renderCoord);
int base = int (glyphLoc_);
int4 h0 = int4 (hb_gpu_atlas[base]); /* (num_ops, _, _, _) */
int4 h2 = int4 (hb_gpu_atlas[base + 2]); /* (ops_offset, _, _, _) */
int num_ops = h0.r;
int cursor = base + h2.r;
float4 acc = float4 (0.0);
float4 group_stack[HB_GPU_PAINT_GROUP_DEPTH];
int sp = 0;
coverage = 0.0;
for (int i = 0; i < num_ops; i++)
{
int4 op = int4 (hb_gpu_atlas[cursor]);
int op_type = op.r;
int aux = op.g;
int payload = (op.b << 16) | (op.a & 0xffff);
if (op_type == 0) /* LAYER_SOLID */
{
/* texel 1: (clip2_hi, clip2_lo, clip3_hi, clip3_lo) -- valid
* per HAS_CLIP2 / HAS_CLIP3 flag bits.
* texel 2: RGBA as signed Q15. */
int4 op2 = int4 (hb_gpu_atlas[cursor + 1]);
int clip2_payload = (op2.r << 16) | (op2.g & 0xffff);
int clip3_payload = (op2.b << 16) | (op2.a & 0xffff);
int4 ct = int4 (hb_gpu_atlas[cursor + 2]);
float4 col = ((aux & 1) != 0)
? float4 (foreground.rgb, foreground.a * (float (ct.a) / 32767.0))
: float4 (ct) / 32767.0;
float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,
base, aux,
payload, clip2_payload, clip3_payload,
hb_gpu_atlas);
coverage = max (coverage, cov);
float4 src = float4 (col.rgb * col.a, col.a) * cov;
acc = src + acc * (1.0 - src.a);
cursor += 3;
}
else if (op_type == 1) /* LAYER_GRADIENT */
{
int4 op2 = int4 (hb_gpu_atlas[cursor + 1]);
int clip2_payload = (op2.r << 16) | (op2.g & 0xffff);
int clip3_payload = (op2.b << 16) | (op2.a & 0xffff);
int4 op3 = int4 (hb_gpu_atlas[cursor + 2]);
int grad_payload = (op3.r << 16) | (op3.g & 0xffff);
int extend = op3.b;
int stop_count = op3.a;
int subtype = aux & 0xff;
float4 col = float4 (0.0);
if (subtype == 0)
col = _hb_gpu_sample_linear (renderCoord,
base + grad_payload,
stop_count, extend, foreground,
hb_gpu_atlas);
else if (subtype == 1)
col = _hb_gpu_sample_radial (renderCoord,
base + grad_payload,
stop_count, extend, foreground,
hb_gpu_atlas);
else if (subtype == 2)
col = _hb_gpu_sample_sweep (renderCoord,
base + grad_payload,
stop_count, extend, foreground,
hb_gpu_atlas);
float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,
base, aux,
payload, clip2_payload, clip3_payload,
hb_gpu_atlas);
coverage = max (coverage, cov);
float4 src = float4 (col.rgb * col.a, col.a) * cov;
acc = src + acc * (1.0 - src.a);
cursor += 3;
}
else if (op_type == 2) /* PUSH_GROUP */
{
if (sp < HB_GPU_PAINT_GROUP_DEPTH) {
group_stack[sp] = acc;
sp++;
}
acc = float4 (0.0);
cursor += 1;
}
else if (op_type == 3) /* POP_GROUP */
{
if (sp > 0) {
sp--;
float4 src = acc;
float4 dst = group_stack[sp];
acc = _hb_gpu_composite (src, dst, aux);
}
cursor += 1;
}
else
{
break;
}
}
return acc;
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Paint-renderer fragment shader (WGSL).
*
* Assumes the shared fragment helpers (hb-gpu-fragment.wgsl) and
* the draw-renderer fragment helpers (hb-gpu-draw-fragment.wgsl)
* are prepended to this source.
*
* atlas is passed as an explicit storage-buffer pointer parameter,
* matching WGSL's scoping rules.
*/
fn _hb_gpu_stop_color (hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>,
stops_base: i32, i: i32, foreground: vec4f,
offset: ptr<function, f32>) -> vec4f
{
let a = hb_gpu_fetch (hb_gpu_atlas, stops_base + i * 2);
*offset = f32 (a.r) / 32767.0;
let b = hb_gpu_fetch (hb_gpu_atlas, stops_base + i * 2 + 1);
if ((a.g & 1) != 0) {
return vec4f (foreground.rgb, foreground.a * (f32 (b.a) / 32767.0));
}
return vec4f (b) / 32767.0;
}
fn _hb_gpu_extend_t (t: f32, extend: i32) -> f32
{
if (extend == 1) { return t - floor (t); }
if (extend == 2) {
let u = t - 2.0 * floor (t * 0.5);
if (u > 1.0) { return 2.0 - u; }
return u;
}
return clamp (t, 0.0, 1.0);
}
fn _hb_gpu_eval_stops (hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>,
stops_base: i32, stop_count: i32,
t: f32, foreground: vec4f) -> vec4f
{
var off_prev: f32;
var col_prev = _hb_gpu_stop_color (hb_gpu_atlas, stops_base, 0, foreground, &off_prev);
if (t <= off_prev) { return col_prev; }
for (var i: i32 = 1; i < stop_count; i = i + 1)
{
var off: f32;
let col = _hb_gpu_stop_color (hb_gpu_atlas, stops_base, i, foreground, &off);
if (t <= off)
{
let span = off - off_prev;
var f: f32 = 0.0;
if (span > 1e-6) { f = (t - off_prev) / span; }
let p0 = vec4f (col_prev.rgb * col_prev.a, col_prev.a);
let p1 = vec4f (col.rgb * col.a, col.a);
let pm = mix (p0, p1, f);
if (pm.a > 1e-6) { return vec4f (pm.rgb / pm.a, pm.a); }
return vec4f (0.0);
}
col_prev = col;
off_prev = off;
}
return col_prev;
}
/* Apply the stored 2x2 M^-1 (row-major i16 Q10) to a vector. */
fn _hb_gpu_apply_minv (m: vec4<i32>, v: vec2f) -> vec2f
{
let mf = vec4f (m) * (1.0 / 1024.0);
return vec2f (mf.x * v.x + mf.y * v.y,
mf.z * v.x + mf.w * v.y);
}
fn _hb_gpu_sample_linear (renderCoord: vec2f, grad_base: i32,
stop_count: i32, extend: i32, foreground: vec4f,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> vec4f
{
let t0 = hb_gpu_fetch (hb_gpu_atlas, grad_base);
let m = hb_gpu_fetch (hb_gpu_atlas, grad_base + 1);
let p0_r = vec2f (f32 (t0.r), f32 (t0.g));
let d = vec2f (f32 (t0.b), f32 (t0.a));
let denom = dot (d, d);
if (denom < 1e-6) { return vec4f (0.0); }
let p = _hb_gpu_apply_minv (m, renderCoord - p0_r);
var t = dot (p, d) / denom;
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (hb_gpu_atlas, grad_base + 2, stop_count, t, foreground);
}
fn _hb_gpu_sample_radial (renderCoord: vec2f, grad_base: i32,
stop_count: i32, extend: i32, foreground: vec4f,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> vec4f
{
let t0 = hb_gpu_fetch (hb_gpu_atlas, grad_base);
let t1 = hb_gpu_fetch (hb_gpu_atlas, grad_base + 1);
let m = hb_gpu_fetch (hb_gpu_atlas, grad_base + 2);
let c0_r = vec2f (f32 (t0.r), f32 (t0.g));
let cd = vec2f (f32 (t0.b), f32 (t0.a));
let r0 = f32 (t1.r);
let r1 = f32 (t1.g);
let dr = r1 - r0;
let p = _hb_gpu_apply_minv (m, renderCoord - c0_r);
let A = dot (cd, cd) - dr * dr;
let B = -2.0 * (dot (p, cd) + r0 * dr);
let C = dot (p, p) - r0 * r0;
var t: f32;
if (abs (A) > 1e-6)
{
let disc = B * B - 4.0 * A * C;
if (disc < 0.0) { return vec4f (0.0); }
let sq = sqrt (disc);
let t1r = (-B + sq) / (2.0 * A);
let t2r = (-B - sq) / (2.0 * A);
if (r0 + t1r * dr >= 0.0) { t = t1r; } else { t = t2r; }
}
else
{
if (abs (B) < 1e-6) { return vec4f (0.0); }
t = -C / B;
}
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (hb_gpu_atlas, grad_base + 3, stop_count, t, foreground);
}
fn _hb_gpu_sample_sweep (renderCoord: vec2f, grad_base: i32,
stop_count: i32, extend: i32, foreground: vec4f,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> vec4f
{
let t0 = hb_gpu_fetch (hb_gpu_atlas, grad_base);
let m = hb_gpu_fetch (hb_gpu_atlas, grad_base + 1);
let c_r = vec2f (f32 (t0.r), f32 (t0.g));
let a0 = f32 (t0.b) / 16384.0;
let a1 = f32 (t0.a) / 16384.0;
let span = a1 - a0;
if (abs (span) < 1e-6) { return vec4f (0.0); }
let p = _hb_gpu_apply_minv (m, renderCoord - c_r);
var ang = atan2 (p.y, p.x) / 3.14159265358979;
if (ang < 0.0) { ang = ang + 2.0; }
var t = (ang - a0) / span;
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (hb_gpu_atlas, grad_base + 2, stop_count, t, foreground);
}
fn _hb_gpu_composite (src: vec4f, dst: vec4f, mode_in: i32) -> vec4f
{
var r = src + dst * (1.0 - src.a); /* SRC_OVER default */
/* Mode numbers match hb_paint_composite_mode_t. Approximate
* unsupported modes with the nearest Porter-Duff mode we do
* implement; DIFFERENCE / EXCLUSION / HSL_* still fall through to
* SRC_OVER below. */
var mode = mode_in;
if (mode == 14 || mode == 18 || mode == 19) { mode = 23; } /* OVERLAY / COLOR_BURN / HARD_LIGHT -> MULTIPLY */
else if (mode == 17 || mode == 20) { mode = 13; } /* COLOR_DODGE / SOFT_LIGHT -> SCREEN */
if (mode == 0) { r = vec4f (0.0); } /* CLEAR */
else if (mode == 1) { r = src; } /* SRC */
else if (mode == 2) { r = dst; } /* DST */
else if (mode == 4) { r = dst + src * (1.0 - dst.a); } /* DST_OVER */
else if (mode == 5) { r = src * dst.a; } /* SRC_IN */
else if (mode == 6) { r = dst * src.a; } /* DST_IN */
else if (mode == 7) { r = src * (1.0 - dst.a); } /* SRC_OUT */
else if (mode == 8) { r = dst * (1.0 - src.a); } /* DST_OUT */
else if (mode == 9) { r = src * dst.a + dst * (1.0 - src.a); } /* SRC_ATOP */
else if (mode == 10) { r = dst * src.a + src * (1.0 - dst.a); } /* DST_ATOP */
else if (mode == 11) { r = src * (1.0 - dst.a) + dst * (1.0 - src.a); } /* XOR */
else if (mode == 12) { r = min (src + dst, vec4f (1.0)); } /* PLUS */
else if (mode == 13) { /* SCREEN (premul) */
r = vec4f (src.rgb + dst.rgb - src.rgb * dst.rgb,
src.a + dst.a - src.a * dst.a);
}
else if (mode == 15) { /* DARKEN */
r = vec4f (min (src.rgb * dst.a, dst.rgb * src.a)
+ src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a),
src.a + dst.a - src.a * dst.a);
}
else if (mode == 16) { /* LIGHTEN */
r = vec4f (max (src.rgb * dst.a, dst.rgb * src.a)
+ src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a),
src.a + dst.a - src.a * dst.a);
}
else if (mode == 23) { /* MULTIPLY (premul) */
r = vec4f (src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a)
+ src.rgb * dst.rgb,
src.a + dst.a - src.a * dst.a);
}
/* SRC_OVER (3) and DIFFERENCE / EXCLUSION / HSL_* (21, 22, 24-27)
* fall through to the SRC_OVER default. */
return r;
}
/* Wrap _hb_gpu_slug with a sub-glyph extents bail-out. Many
* paint layers cover a small region of the outer glyph quad; for
* fragments outside the layer's bbox (with an AA + MSAA-spread
* margin) the slug coverage is exactly 0, so we can skip the
* band/curve walk entirely. */
fn _hb_gpu_slug_clipped (renderCoord: vec2f, pixelsPerEm: vec2f, glyphLoc_: u32,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> f32
{
let header0 = hb_gpu_fetch (hb_gpu_atlas, i32 (glyphLoc_));
let ext = vec4f (header0) * HB_GPU_INV_UNITS;
let margin = 2.0 / pixelsPerEm;
if (any (renderCoord < ext.xy - margin) ||
any (renderCoord > ext.zw + margin)) {
return 0.0;
}
return _hb_gpu_slug (renderCoord, pixelsPerEm, glyphLoc_, hb_gpu_atlas);
}
/* Combine slug coverages from all clip outlines on the layer.
* Factored so the shader has one set of inlined slug walks
* instead of two (one per LAYER op type). flags bits: 0x100 =
* HAS_CLIP2; 0x200 = HAS_CLIP3 (HAS_CLIP3 implies HAS_CLIP2). */
fn _hb_gpu_layer_coverage (renderCoord: vec2f, pixelsPerEm: vec2f,
base: i32, flags: i32,
clip1_payload: i32, clip2_payload: i32, clip3_payload: i32,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>) -> f32
{
var cov = _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
u32 (base + clip1_payload), hb_gpu_atlas);
if ((flags & 0x100) != 0) {
cov = cov * _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
u32 (base + clip2_payload), hb_gpu_atlas);
if ((flags & 0x200) != 0) {
cov = cov * _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
u32 (base + clip3_payload), hb_gpu_atlas);
}
}
return cov;
}
const HB_GPU_PAINT_GROUP_DEPTH: i32 = 4;
fn hb_gpu_paint (renderCoord: vec2f, glyphLoc_: u32, foreground: vec4f,
hb_gpu_atlas: ptr<storage, array<vec4<i32>>, read>,
coverage: ptr<function, f32>) -> vec4f
{
/* Compute pixelsPerEm once here at uniform control flow. WGSL
* rejects fwidth inside a loop-conditional branch, so we call
* _hb_gpu_slug (the MSAA-aware implementation that takes a
* pre-computed pixelsPerEm) instead of the top-level
* hb_gpu_draw() which would re-call fwidth. */
let emsPerPixel = fwidth (renderCoord);
let pixelsPerEm = 1.0 / emsPerPixel;
let base = i32 (glyphLoc_);
let h0 = hb_gpu_fetch (hb_gpu_atlas, base); // (num_ops, _, _, _)
let h2 = hb_gpu_fetch (hb_gpu_atlas, base + 2); // (ops_offset, _, _, _)
let num_ops = h0.r;
var cursor = base + h2.r;
var acc = vec4f (0.0);
var group_stack: array<vec4f, HB_GPU_PAINT_GROUP_DEPTH>;
var sp: i32 = 0;
*coverage = 0.0;
for (var i: i32 = 0; i < num_ops; i = i + 1)
{
let op = hb_gpu_fetch (hb_gpu_atlas, cursor);
let op_type = op.r;
let aux = op.g;
let payload = (op.b << 16) | (op.a & 0xffff);
if (op_type == 0) { // LAYER_SOLID
// texel 1: (clip2_hi, clip2_lo, clip3_hi, clip3_lo) -- valid
// per HAS_CLIP2 / HAS_CLIP3 flag bits.
// texel 2: RGBA as signed Q15.
let op2 = hb_gpu_fetch (hb_gpu_atlas, cursor + 1);
let clip2_payload = (op2.r << 16) | (op2.g & 0xffff);
let clip3_payload = (op2.b << 16) | (op2.a & 0xffff);
let ct = hb_gpu_fetch (hb_gpu_atlas, cursor + 2);
var col: vec4f;
if ((aux & 1) != 0) {
col = vec4f (foreground.rgb, foreground.a * (f32 (ct.a) / 32767.0));
} else {
col = vec4f (ct) / 32767.0;
}
let cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,
base, aux,
payload, clip2_payload, clip3_payload,
hb_gpu_atlas);
*coverage = max (*coverage, cov);
let src = vec4f (col.rgb * col.a, col.a) * cov;
acc = src + acc * (1.0 - src.a);
cursor = cursor + 3;
} else if (op_type == 1) { // LAYER_GRADIENT
let op2 = hb_gpu_fetch (hb_gpu_atlas, cursor + 1);
let clip2_payload = (op2.r << 16) | (op2.g & 0xffff);
let clip3_payload = (op2.b << 16) | (op2.a & 0xffff);
let op3 = hb_gpu_fetch (hb_gpu_atlas, cursor + 2);
let grad_payload = (op3.r << 16) | (op3.g & 0xffff);
let extend = op3.b;
let stop_count = op3.a;
let subtype = aux & 0xff;
var col = vec4f (0.0);
if (subtype == 0) {
col = _hb_gpu_sample_linear (renderCoord,
base + grad_payload,
stop_count, extend, foreground,
hb_gpu_atlas);
} else if (subtype == 1) {
col = _hb_gpu_sample_radial (renderCoord,
base + grad_payload,
stop_count, extend, foreground,
hb_gpu_atlas);
} else if (subtype == 2) {
col = _hb_gpu_sample_sweep (renderCoord,
base + grad_payload,
stop_count, extend, foreground,
hb_gpu_atlas);
}
let cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,
base, aux,
payload, clip2_payload, clip3_payload,
hb_gpu_atlas);
*coverage = max (*coverage, cov);
let src = vec4f (col.rgb * col.a, col.a) * cov;
acc = src + acc * (1.0 - src.a);
cursor = cursor + 3;
} else if (op_type == 2) { // PUSH_GROUP
if (sp < HB_GPU_PAINT_GROUP_DEPTH) {
group_stack[sp] = acc;
sp = sp + 1;
}
acc = vec4f (0.0);
cursor = cursor + 1;
} else if (op_type == 3) { // POP_GROUP
if (sp > 0) {
sp = sp - 1;
let src = acc;
let dst = group_stack[sp];
acc = _hb_gpu_composite (src, dst, aux);
}
cursor = cursor + 1;
} else {
break;
}
}
return acc;
}
/*
* Copyright (C) 2026 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
/* Paint-renderer fragment shader (HLSL).
*
* Assumes the shared fragment helpers (hb-gpu-fragment.hlsl) and
* the draw-renderer fragment helpers (hb-gpu-draw-fragment.hlsl)
* are prepended to this source.
*/
float4 _hb_gpu_stop_color (int stops_base, int i, float4 foreground,
out float offset)
{
int4 a = hb_gpu_fetch (stops_base + i * 2);
offset = (float) a.r / 32767.0;
int4 b = hb_gpu_fetch (stops_base + i * 2 + 1);
if ((a.g & 1) != 0)
return float4 (foreground.rgb, foreground.a * ((float) b.a / 32767.0));
return (float4) b / 32767.0;
}
float _hb_gpu_extend_t (float t, int extend)
{
if (extend == 1) return t - floor (t);
if (extend == 2) {
float u = t - 2.0 * floor (t * 0.5);
return u > 1.0 ? 2.0 - u : u;
}
return clamp (t, 0.0, 1.0);
}
float4 _hb_gpu_eval_stops (int stops_base, int stop_count, float t, float4 foreground)
{
float off_prev;
float4 col_prev = _hb_gpu_stop_color (stops_base, 0, foreground, off_prev);
if (t <= off_prev) return col_prev;
for (int i = 1; i < stop_count; i++)
{
float off;
float4 col = _hb_gpu_stop_color (stops_base, i, foreground, off);
if (t <= off)
{
float span = off - off_prev;
float f = span > 1e-6 ? (t - off_prev) / span : 0.0;
float4 p0 = float4 (col_prev.rgb * col_prev.a, col_prev.a);
float4 p1 = float4 (col.rgb * col.a, col.a);
float4 pm = lerp (p0, p1, f);
return pm.a > 1e-6 ? float4 (pm.rgb / pm.a, pm.a) : float4 (0.0, 0.0, 0.0, 0.0);
}
col_prev = col;
off_prev = off;
}
return col_prev;
}
/* Apply the stored 2x2 M^-1 (row-major i16 Q10) to a vector. */
float2 _hb_gpu_apply_minv (int4 m, float2 v)
{
float4 mf = (float4) m * (1.0 / 1024.0);
return float2 (mf.x * v.x + mf.y * v.y,
mf.z * v.x + mf.w * v.y);
}
float4 _hb_gpu_sample_linear (float2 renderCoord, int grad_base,
int stop_count, int extend, float4 foreground)
{
int4 t0 = hb_gpu_fetch (grad_base);
int4 m = hb_gpu_fetch (grad_base + 1);
float2 p0_r = float2 ((float) t0.r, (float) t0.g);
float2 d = float2 ((float) t0.b, (float) t0.a);
float denom = dot (d, d);
if (denom < 1e-6) return float4 (0.0, 0.0, 0.0, 0.0);
float2 p = _hb_gpu_apply_minv (m, renderCoord - p0_r);
float t = dot (p, d) / denom;
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (grad_base + 2, stop_count, t, foreground);
}
float4 _hb_gpu_sample_radial (float2 renderCoord, int grad_base,
int stop_count, int extend, float4 foreground)
{
int4 t0 = hb_gpu_fetch (grad_base);
int4 t1 = hb_gpu_fetch (grad_base + 1);
int4 m = hb_gpu_fetch (grad_base + 2);
float2 c0_r = float2 ((float) t0.r, (float) t0.g);
float2 cd = float2 ((float) t0.b, (float) t0.a);
float r0 = (float) t1.r;
float r1 = (float) t1.g;
float dr = r1 - r0;
float2 p = _hb_gpu_apply_minv (m, renderCoord - c0_r);
float A = dot (cd, cd) - dr * dr;
float B = -2.0 * (dot (p, cd) + r0 * dr);
float C = dot (p, p) - r0 * r0;
float t;
if (abs (A) > 1e-6)
{
float disc = B * B - 4.0 * A * C;
if (disc < 0.0) return float4 (0.0, 0.0, 0.0, 0.0);
float sq = sqrt (disc);
float t1r = (-B + sq) / (2.0 * A);
float t2r = (-B - sq) / (2.0 * A);
t = (r0 + t1r * dr >= 0.0) ? t1r : t2r;
}
else
{
if (abs (B) < 1e-6) return float4 (0.0, 0.0, 0.0, 0.0);
t = -C / B;
}
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (grad_base + 3, stop_count, t, foreground);
}
float4 _hb_gpu_sample_sweep (float2 renderCoord, int grad_base,
int stop_count, int extend, float4 foreground)
{
int4 t0 = hb_gpu_fetch (grad_base);
int4 m = hb_gpu_fetch (grad_base + 1);
float2 c_r = float2 ((float) t0.r, (float) t0.g);
float a0 = (float) t0.b / 16384.0;
float a1 = (float) t0.a / 16384.0;
float span = a1 - a0;
if (abs (span) < 1e-6) return float4 (0.0, 0.0, 0.0, 0.0);
float2 p = _hb_gpu_apply_minv (m, renderCoord - c_r);
float ang = atan2 (p.y, p.x) / 3.14159265358979;
if (ang < 0.0) ang += 2.0;
float t = (ang - a0) / span;
t = _hb_gpu_extend_t (t, extend);
return _hb_gpu_eval_stops (grad_base + 2, stop_count, t, foreground);
}
float4 _hb_gpu_composite (float4 src, float4 dst, int mode)
{
float4 r = src + dst * (1.0 - src.a); /* SRC_OVER default */
/* Mode numbers match hb_paint_composite_mode_t. Approximate
* unsupported modes with the nearest Porter-Duff mode we do
* implement; DIFFERENCE / EXCLUSION / HSL_* still fall through to
* SRC_OVER below. */
if (mode == 14 || mode == 18 || mode == 19) mode = 23; /* OVERLAY / COLOR_BURN / HARD_LIGHT -> MULTIPLY */
else if (mode == 17 || mode == 20) mode = 13; /* COLOR_DODGE / SOFT_LIGHT -> SCREEN */
if (mode == 0) r = float4 (0.0, 0.0, 0.0, 0.0); /* CLEAR */
else if (mode == 1) r = src; /* SRC */
else if (mode == 2) r = dst; /* DST */
else if (mode == 4) r = dst + src * (1.0 - dst.a); /* DST_OVER */
else if (mode == 5) r = src * dst.a; /* SRC_IN */
else if (mode == 6) r = dst * src.a; /* DST_IN */
else if (mode == 7) r = src * (1.0 - dst.a); /* SRC_OUT */
else if (mode == 8) r = dst * (1.0 - src.a); /* DST_OUT */
else if (mode == 9) r = src * dst.a + dst * (1.0 - src.a); /* SRC_ATOP */
else if (mode == 10) r = dst * src.a + src * (1.0 - dst.a); /* DST_ATOP */
else if (mode == 11) r = src * (1.0 - dst.a) + dst * (1.0 - src.a); /* XOR */
else if (mode == 12) r = min (src + dst, float4 (1.0, 1.0, 1.0, 1.0)); /* PLUS */
else if (mode == 13) { /* SCREEN (premul) */
r.rgb = src.rgb + dst.rgb - src.rgb * dst.rgb;
r.a = src.a + dst.a - src.a * dst.a;
}
else if (mode == 15) { /* DARKEN */
r.rgb = min (src.rgb * dst.a, dst.rgb * src.a)
+ src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
r.a = src.a + dst.a - src.a * dst.a;
}
else if (mode == 16) { /* LIGHTEN */
r.rgb = max (src.rgb * dst.a, dst.rgb * src.a)
+ src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a);
r.a = src.a + dst.a - src.a * dst.a;
}
else if (mode == 23) { /* MULTIPLY (premul) */
r.rgb = src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a)
+ src.rgb * dst.rgb;
r.a = src.a + dst.a - src.a * dst.a;
}
/* SRC_OVER (3) and DIFFERENCE / EXCLUSION / HSL_* (21, 22, 24-27)
* fall through to the SRC_OVER default. */
return r;
}
/* Wrap _hb_gpu_slug with a sub-glyph extents bail-out. Many
* paint layers cover a small region of the outer glyph quad; for
* fragments outside the layer's bbox (with an AA + MSAA-spread
* margin) the slug coverage is exactly 0, so we can skip the
* band/curve walk entirely. */
float _hb_gpu_slug_clipped (float2 renderCoord, float2 pixelsPerEm, uint glyphLoc_)
{
int4 header0 = hb_gpu_fetch ((int) glyphLoc_);
float4 ext = (float4) header0 * HB_GPU_INV_UNITS;
float2 margin = 2.0 / pixelsPerEm;
if (any (renderCoord < ext.xy - margin) ||
any (renderCoord > ext.zw + margin))
return 0.0;
return _hb_gpu_slug (renderCoord, pixelsPerEm, glyphLoc_);
}
/* Combine slug coverages from all clip outlines on the layer.
* Factored so the shader has one set of inlined slug walks
* instead of two (one per LAYER op type). flags bits: 0x100 =
* HAS_CLIP2; 0x200 = HAS_CLIP3 (HAS_CLIP3 implies HAS_CLIP2). */
float _hb_gpu_layer_coverage (float2 renderCoord, float2 pixelsPerEm,
int base, int flags,
int clip1_payload, int clip2_payload, int clip3_payload)
{
float cov = _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
(uint) (base + clip1_payload));
if ((flags & 0x100) != 0)
{
cov *= _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
(uint) (base + clip2_payload));
if ((flags & 0x200) != 0)
cov *= _hb_gpu_slug_clipped (renderCoord, pixelsPerEm,
(uint) (base + clip3_payload));
}
return cov;
}
#define HB_GPU_PAINT_GROUP_DEPTH 4
float4 hb_gpu_paint (float2 renderCoord, uint glyphLoc_, float4 foreground,
out float coverage)
{
/* fwidth once, at uniform control flow. */
float2 pixelsPerEm = 1.0 / fwidth (renderCoord);
int base = (int) glyphLoc_;
int4 h0 = hb_gpu_fetch (base); /* (num_ops, _, _, _) */
int4 h2 = hb_gpu_fetch (base + 2); /* (ops_offset, _, _, _) */
int num_ops = h0.r;
int cursor = base + h2.r;
float4 acc = float4 (0.0, 0.0, 0.0, 0.0);
float4 group_stack[HB_GPU_PAINT_GROUP_DEPTH];
coverage = 0.0;
int sp = 0;
for (int i = 0; i < num_ops; i++)
{
int4 op = hb_gpu_fetch (cursor);
int op_type = op.r;
int aux = op.g;
int payload = (op.b << 16) | (op.a & 0xffff);
if (op_type == 0) /* LAYER_SOLID */
{
/* texel 1: (clip2_hi, clip2_lo, clip3_hi, clip3_lo) -- valid
* per HAS_CLIP2 / HAS_CLIP3 flag bits.
* texel 2: RGBA as signed Q15. */
int4 op2 = hb_gpu_fetch (cursor + 1);
int clip2_payload = (op2.r << 16) | (op2.g & 0xffff);
int clip3_payload = (op2.b << 16) | (op2.a & 0xffff);
int4 ct = hb_gpu_fetch (cursor + 2);
float4 col = ((aux & 1) != 0)
? float4 (foreground.rgb, foreground.a * ((float) ct.a / 32767.0))
: (float4) ct / 32767.0;
float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,
base, aux,
payload, clip2_payload, clip3_payload);
coverage = max (coverage, cov);
float4 src = float4 (col.rgb * col.a, col.a) * cov;
acc = src + acc * (1.0 - src.a);
cursor += 3;
}
else if (op_type == 1) /* LAYER_GRADIENT */
{
int4 op2 = hb_gpu_fetch (cursor + 1);
int clip2_payload = (op2.r << 16) | (op2.g & 0xffff);
int clip3_payload = (op2.b << 16) | (op2.a & 0xffff);
int4 op3 = hb_gpu_fetch (cursor + 2);
int grad_payload = (op3.r << 16) | (op3.g & 0xffff);
int extend = op3.b;
int stop_count = op3.a;
int subtype = aux & 0xff;
float4 col = float4 (0.0, 0.0, 0.0, 0.0);
if (subtype == 0)
col = _hb_gpu_sample_linear (renderCoord,
base + grad_payload,
stop_count, extend, foreground);
else if (subtype == 1)
col = _hb_gpu_sample_radial (renderCoord,
base + grad_payload,
stop_count, extend, foreground);
else if (subtype == 2)
col = _hb_gpu_sample_sweep (renderCoord,
base + grad_payload,
stop_count, extend, foreground);
float cov = _hb_gpu_layer_coverage (renderCoord, pixelsPerEm,
base, aux,
payload, clip2_payload, clip3_payload);
coverage = max (coverage, cov);
float4 src = float4 (col.rgb * col.a, col.a) * cov;
acc = src + acc * (1.0 - src.a);
cursor += 3;
}
else if (op_type == 2)
{
if (sp < HB_GPU_PAINT_GROUP_DEPTH) {
group_stack[sp] = acc;
sp++;
}
acc = float4 (0.0, 0.0, 0.0, 0.0);
cursor += 1;
}
else if (op_type == 3)
{
if (sp > 0) {
sp--;
float4 src = acc;
float4 dst = group_stack[sp];
acc = _hb_gpu_composite (src, dst, aux);
}
cursor += 1;
}
else
{
break;
}
}
return acc;
}
ÿÿÿÿ€?€?ÿÿÿÿÿÿ€€€¿þÿFǃù¢>À@€F€ÿÿÿ̼Œ+€D…ÿt<1ÀƒÿuJH�ƒþt>wGH�ƒþt0ƒþºH�HEÂÀÿÎH�ºƒþHGÂÃf.„ƒþºH�HEÂÃATUH‰õS‹GH‰û‹�H9Ð}H‹{H‹UH�ljKH‰[]A\Ã…ÒxvA‰Ô9ÊsÛDD‰àÑèF�d A9ÌrñA�üÿÿÿwND‰æHÁæ…ÒuaHƒ{tZH‰÷ÿH‰ÇH…Àt[‹CH‹sHÁàH‰Ât ÿH‰Ç‹CH‰{D‰#�Hétÿÿÿ÷Ò‰H‹H‰H�édÿÿÿH‹{ÿH‰ÇH…Àu¾‹D9àr‹CH‹{�Hé.ÿÿÿ÷Љë¸AWAVAUATUSH�ì¸dH‹%(H‰„$¨1ÀHÇÿÿÿÿÇFÿÿÿÿ‹—@…Ò„-H�D$8H‰ûH‰õE1äH‰D$H�GhL�·€H‰D$DA‹F(…À‰,�{lÿ‡ˆH‹»pH…ÿ„�ÿóA.VI‹~z^u\fïÀA.FzQuOA.FzHuFA.Vz?u=A.F z6u4A.F$z-u+H‰þA‹H‹»pÿ…À„L�l$@éOfDL�l$@H‹t$L‰êÿH‹»p‹T$@‹t$8ÿóAoNI‹F fïÀD$hH‹»pD$x„$ˆ„$˜)L$PH‰D$`ÿfïÀfHnÈD$x‹p„$ˆL$h„$˜L‹=M…ÿ„—L‰úA‹6I‹~H�L$PÿH‹|$hH‹t$pA‰Ç‹D$x…ÀtuóŒ$€óD$|.„$„zu.Œ$ˆzH�T$xt6H‹O8H‹GH…ÉtH‹IH�T$xH‰t$ H‰T$(H‰|$ÿÐH‹T$(H‹t$ H‹|$H‹O8H‹G0H…ÉtH‹I ÿÐE…ÿ„¿L‰îH‹»pÿH‰D$8I‰ÅH…À„ŽH‹t$H‹|$èH‹H�5H‰H9Æ„e‹T$@‹t$H‹D$D‹L$LÖÁ‰÷9òNúLÖ9ȉÎNðLÁ‹‹`fnê9Ïfn÷NÏfnÁ‹‹d9ÎNÎA‰É‹‹hfAná9ÊfbÄMÊfnÉ‹‹l9ÈMÈfnÙ‹KlfbËflÁÿÉfn΃`fnÁfbÍfbÆA‰F8flÁAF(‰MˆÎºAÿÄIƒÆ@HƒÅ‹ƒ@9ÐGÂA9Ä‚ûüÿÿ¸H‹”$¨dH+%(…¯H�ĸ[]A\A]A^A_ÃDA‹N,‹“`9ÊOщ“`‹“hA‹N49ÊLщ“h‹“dA‹N09ÊOщ“d‹“lA‹N89ÊLщ“l‰EéMÿÿÿ@L‰ïÿƃ]ÇEÿÿÿÿ1ÀéTÿÿÿf�ÿH‰ƒpH‰ÇH…À…XüÿÿëÎDÿ1É1ÒH�5H‰ÇH‰D$ÿH‹|$1É1ÒH�5ÿH‹|$1É1ÒH�5ÿH‹|$1É1ÒH�5ÿH�51É1ÒH‹|$ÿH‹|$ÿH�=ÿH‹t$H…ötL‰øðH±5u2H‹T$é»üÿÿÿH‰D$H‰ÆL‰øðH±5tÚHƒ|$„€üÿÿÿH‹|$H9Ç„lüÿÿÿéaüÿÿÿUSHƒìxdH‹%(H‰D$h1À€¾\„H‰óƆ\H�t$H‹»pÿH‰D$H‰ÅH…À„H�t$H�{hèH‹H‰H�H9„܋Cl‹L$ÇD$ ÿÿÿÿ‹|$‹T$HÇD$(ÿÈ‹t$óo“DfnÀH‹ƒTÏÖA‰ø)T$0H‰D$@‹ƒ@A‰ñHƒÀHÁàHØ9ùDNÁ9òPDNÊ9ùLÏfAnà9òfAnÉfbÄLÖfnÙfbˉT$XflÁD$HfoD$ foD$@@ óoD$L@,ÿƒ@H‹D$hdH+%(u HƒÄx[]ÀH‰ïÿƃ]ëÐÿ…öˆ(AVAUATUH‰ýS‹…Àˆ A‰Õ‰ó‰Â„Éu(9ðrt‹M9Ùs E„í…Ô‰]¸[]A\A]A^ËO9ÎA‰ÌDCæD9àrOÁèA9ÄsÆD‰æE‰æHöE…ä„ÕH‹}ÿH‰ÇH…À„§‹MH‰}D‰uë‘f�‰ÑÑé�T
9ÚrôA‰Ô�E‰æE…äy÷ЉE[1À]A\A]A^ÄD‰æHö…Àu¡Hƒ}tšH‰÷ÿH‰ÇH…ÀtF‹UH‹uH‰ÑHÒt“ÿ‹MH‰Çë…�‰Ø1ö)È�H‹EH�<Hÿéÿÿÿ@1ÀÃD‹ED9à‚tÿÿÿéãþÿÿ€H‹E1öÿ‹M1ÿé-ÿÿÿAU1ÉI‰õATUH‰ýS‰ÓºHƒìD‹gA�4ÜèA‰Á„À„&H‹EJ� `…Û„HÁãL‰î¿�€€€ó%óó-L�€óº€ÿÿ/áw(Â(ÚóÂÁTØUÁVÃóYÅó,ÐD‹V1À·ÒÇAE…Ò•ÀHƒÁHƒÆ Áà ЉAð‹FüA‰Â¶ÜAÁê‰ÚE¶ÒÁâE‰Ó)ډöÀAÁãÁëƒÂE)ÓH¯×E�SA‰ÛAÁãL¯×A)Û‰ÃHÁê'ÁãAƒÃ·Ò)ÃL¯ßIÁê'�CE·ÒH¯ÇIÁë'E·ÛHÁè%ÿÿL ØHÁàL ÐHÁàH ÐH‰AøL9Á…ÿÿÿHƒÄD‰È[]A\A]ÃAT1ÉA‰ôUH‰ÕºS‹GTH‰ûHƒÇP�p è„À„Å‹CT‹u‹M�PôH‹CX‰÷ÁïH�P‹U…Éxu…ÒxIfA�ÌÁÁA¹‰ÓfD‰`fD‰Áëf‰xf‰p‰H‰Ù1ö…Ò·ÉHÖHƒÀÁâ ʉPü[]A\Ã@ÁÁA¸fA�Ìf‰x‰H1ÉfD‰fD‰`f‰pëº�…Òx fA�Ì1ÉëŠf�¹fD‰`f‰1Éf‰xf‰pÇ@ë‰Æƒ]1À[]A\ÃAWAVAUATUSH�ì(ó$óL$óT$ó\$ dH‹%(H‰„$1À€¾]…Ø‹Ž@H‰ó…É„Ç�~`þ‡šH‰Õ1É1Ò1öH‰ïÿA‰Ä…À„�ƒø‡ÄE1öL�|$PD‰d$L�d$D1öH‰ïH�T$L‰ùÿH‰ïÿL‰æH‰ß‰Åè„À„H¾1ÿÿI‰ÅH…À„(fïÀ‹t$H�L$$L‰ÿH�T$ ÿól$óD$ óL$ óDL$ó\Åó$$(õó{(óS óDC,(ÜóYÈóYD$$óYÚóX[0óXÍók$óXðA(ÁóYÇóYåóXØóC4óXÄA(áóAYàóXÄó%/ãƒá/ƒnó,Ã/àfA‰Eƒo/¸ÿsó,ÀóófA‰EóYÈ/Ùƒ¢/
¸ÿs óY
ó,ÁóYÆfA‰E/؃l/¸ÿs óYó,À(Ç(ÊfA‰EóóYÅWûóAYÈó\È(Ç(ñT5/5†
ó5ó^ñó
óDYÆóDYÁA/àƒD/ºÿsóA,ÐóYÆóYÁ/àƒ´/¹ÿsó,ÈWë(ÅóYÆóYÁ/àƒ�/¾ÿsó,ðóYÖóYÑ/₵¸€ÿÿé½f�ƃ]L‰÷ÿH‹„$dH+%(…gH�Ä([]A\A]A^A_À‰ÀH�<@HÁçÿI‰ÇH…À„”I‰Æé ýÿÿ@fïÉWÝó%óDYÁóYÁóYÙóYÊóDYÄóYÄóYÜ(ÑóYÔóA,Ðó,Èó,ó/¸ÿsó,·À·ö·É·ÒHÁàH�|$0L‰l$8H ðL‰þHÁàH ÈHÁàH ЋT$I‰EH¸H‰D$0è‹L$0D‹l$4L‹|$8ÿÁ‰ $„À„ L‰÷ÿC�t-E1À1É1ÒL‰ÿÿH‰D$(I‰ÅH…À„ÑH�t$(H�{hèH‹H‰H�H9„¥‹ClL‰â¾H‰ßD�hÿèH‰ÂH…À„’·D$·íD‰éE·íÁéHÁà·ÉH èHÁàL èHÁàH ÈH‰ÿC`ƒ<$†8þÿÿL‰ÿÿé*þÿÿf.„ƃ]éþÿÿ@/à¸ÿfA‰E‚‘üÿÿ¸€ÿÿé™üÿÿL‰ïÿƃ]ƒ<$w¢éÕýÿÿDƃ]L‰÷ÿëÞ¾€ÿÿé‡ýÿÿ¹€ÿÿéTýÿÿ¸€ÿÿé&üÿÿ¸€ÿÿé¤üÿÿ¸€ÿÿénüÿÿº€ÿÿé ýÿÿÿAWAVAUATUSHƒìhó$óL$ óT$ó\$ód$ól$dH‹%(H‰D$X1À€¾]uU‹Ž@I‰ö…ÉtH�~`þwhH‰ÖL‰÷H‰Õè„Àt-L�d$LH‰ïM‹®€A‹ž|ÿL‰æL‰÷‰Åè„Àu9H‹D$XdH+%(…ñHƒÄh[]A\A]A^A_ÃfDAƆ]ëÍfD¾(1ÿÿI‰ÇH…ÀtÛfïÀH�L$$H�T$ ‰ÞL‰ïHÇ@ÿól$ ó4$óD$óL$ód$ ó\$$ó\Åó\ÎóDl$óT$óEN,óA\Õ(øD(ÁóYüóDYÄóYËóYÃóYâóXýóYÓóA^(óDXÆóXÎóXÅ(÷óAn D(×óYóE(ØE(àóDYÝóAXåóAXÕóEX^0óEYÑóDXÞóAv$óDYæóEXf4óEXâóDE/ÓƒrD/ƒ6óA,ÃE/ÔfA‰ƒ6D/%¸ÿsóA,ÄóA\ÈfA‰GD/уQ/
¸ÿsó,Áó\ÇfA‰GD/Ѓ"/¸ÿsó,ÀD/ÔfA‰Gƒ÷/%¸ÿsó,ÄD/ÒfA‰GƒÌ/¸ÿsó,Â(Ë(ÅfA‰G
óYÎAÇG óAYÁó\Áó
WÙ(ÐT/‡pfïÀWÎóóDYÈóYØóYÈóYÅóDYÊóYÚóYÊ(èóYêóA,Ñó,Ëó,ñó,Å·À·ö·É·ÒHÁàH�|$0L‰|$8H ðL‰îHÁàH ÈHÁàH ЉÚI‰GH¸ H‰D$0è‹|$0‹t$4L‹|$8D�o„À„ƒ1ÒöE1À1ÉL‰ÿÿH‰D$(H‰ÂH…À„WH�t$(I�~hH‰$èH‹
H‹$H‰
H�
H9Á„#A‹FlL‰â¾L‰÷�Hÿ‰ $èH‰ÂH…À„·Ã‹ $·íHÁàH è‰Î·ÉHÁàÁîH È·ÎHÁàH ÈH‰AÿF`Aƒý†RüÿÿL‰ÿÿéDüÿÿDóó^ÐóóDYÊóDYÈE/уD/
ºÿsóA,ÑóYÚóYØD/Óƒ¦/¹ÿsó,ËWñóYòóYðD/Ös|/5¾ÿsó,öóYêóYèD/Õƒ¥/-¸ÿ‚*þÿÿé)þÿÿ@H‰×ÿAƆ]Aƒý‡"ÿÿÿéoûÿÿE/Ô¸ÿfA‰‚Êüÿÿ¸€ÿÿéÔüÿÿ¾€ÿÿë�¹€ÿÿébÿÿÿ¸€ÿÿé—üÿÿ¸€ÿÿé<ýÿÿ¸€ÿÿéýÿÿ¸€ÿÿéæüÿÿ¸€ÿÿé·üÿÿº€ÿÿéÿÿÿ¸€ÿÿé‘ýÿÿÿAWAVAUATUSHƒìxó$óL$ óT$ó\$ód$ól$dH‹%(H‰D$h1À€¾]uY‹Ž@H‰ó…ÉtL�~`þ‡\H‰ÖH‰ßI‰Ôè„Àt-L�l$\L‰çL‹³€‹«|ÿL‰îH‰ßA‰Äè„Àu-H‹D$hdH+%(…’HƒÄx[]A\A]A^A_Ãf.„¾1ÿÿI‰ÇH…À„×fïÀól$ód$H�L$,ó\$H�T$(ó$óT$óL$ H�t$$H�|$ ÿH�L$4‰îL‰÷H�T$0ÿóD$$óL$(óT$,ód$ (Ùól$4óDS(ó\Ðó\ÜóD[,ó5(ÊóT$0D(ËóYÝóDYÊóYÑóYÍók$óXÜóDXÌóc óXÐóXÈA(ùE(ÁóYü(ÂóAYÂóX{0óDYÅóDXC4óXø(ÂóAYÃ/÷óDXÀƒ/=ƒíó,ÇA/ðfA‰ƒîD/¸ÿsóA,ÀóA\ÙfA‰G/óƒù/¸ÿsó,Ãó\ÊfA‰G/ñƒË/
¸ÿsó,ÁA(Â(ÌfA‰GóóYÅóAYËó\ÈA(ÂWÂ(ÙT/†Ÿóó^Ùó
óDYÛóDYÙA/óƒfD/ºÿsóA,ÓóYÃóYÁ/ðƒ/¹ÿsó,ÈWêóYëóYé/õƒý/-¾ÿsó,õóYãóYá/ôrN¸€ÿÿëYfDfïÉWÕóóDYÙóYÁóYÑóYÌóDYÛóYÃóYÓ(áóYãóA,Óó,Èó,ò/%¸ÿsó,Ä·À·ö·É·ÒHÁàH�|$@L‰|$HH ðL‰öHÁàH ÈHÁàH ЉêI‰GH¸H‰D$@è‹|$@‹t$DL‹|$HD�w„À„Õ1ÒöE1À1ÉL‰ÿÿH‰D$8H‰ÂH…À„©H�t$8H�{hH‰$èH‹
H‹$H‰
H�
H9Áty‹ClL‰ê1öH‰ß�Hÿ‰ $èH‰ÂH…Àtk·Å‹ $E·äHÁàL à‰Î·ÉHÁàÁîH È·ÎHÁàH ÈH‰ÿC`Aƒþ†ôûÿÿL‰ÿÿéæûÿÿƃ]é×ûÿÿ@H‰×ÿƃ]AƒþwÉé¸ûÿÿDA/ð¸ÿfA‰‚ýÿÿ¸€ÿÿéýÿÿ¸€ÿÿéðüÿÿ¹€ÿÿéïýÿÿ¾€ÿÿé þÿÿ¸€ÿÿé=ýÿÿ¸€ÿÿéýÿÿº€ÿÿé¤ýÿÿÿATUSHƒì dH‹%(H‰D$1À€¾]u2D‹ž@H‰óE…Ût#�~`þ‡sH�t$ H‰ßA‰Ô‰Íè„ÀuH‹D$dH+%(…¤HƒÄ []A\ËCT1ÉH�{Pº�p è„À„"‹CT‹|$ 1öE…ä‹L$@•Æ�PôH‹CXA‰øAÁèH�P‹D$…Ɉ؅Àˆøf�ÎÁÁE1Òf‰z‰ÇfD‰Áïf‰r‰JfD‰BA‰ø1É…ÀE·ÀIȉè¶ô‰ð·ÉÁà)ð¾�€€€�x‰èÁèH¯þ¶ÀA‰ÁAÁáHÁï'A)Á·ÿA�AH¯ÆHÁè%ÿÿH øHÁàH ȉé@¶íHÁàÁéL ÀH‰B ‰ÈÁà)È�H‰èÁàH¯Î)èƒÀH¯ÆHÁé'·ÉHÁè'Áà ȉBÿC`é¿þÿÿ@…ÀxTf�Î1Éé(ÿÿÿ„ƃ]é—þÿÿ@E1Éf�ÎÁÁfD‰BfD‰
f‰rf‰z‰JE1Àéÿÿÿf„1Éf‰rf‰
fD‰Bf‰zÇBëÔÿU‰ÕSH‰óHƒì‹Fx…Àt�Pÿ‰Vxƒøv HƒÄ[]Ã@�{`þwO‹CT1ÉH�{Pº�pè„Àt5‹CT�PüH‹CXH�Pºf‰f‰hÇ@ÿC`HƒÄ[]Ãf.„ƃ]ë”S‹FxH‰óÿÀ‰Fxƒøw?�~`þw6‹FT1ÉH�{Pº�pè„Àt‹CT�PüH‹CXHÇPÿC`[ÃDƃ][ÃGCC: (conda-forge gcc 14.3.0-19) 14.3.0 GNUÀÀ zRx �
0Dˆ(X°A†LƒKP÷
AAG „AƒP8 SKŒD†C ƒe
ABF ACBJÃÆÌ0Ü�A†DƒG [
AAG jAA5AƒM
B _05AƒM
B _8PŽB�BŒA †A(ƒG0\
(A ABBA (ŒìA†MƒG@j
AAG (¸NA†LƒKP¬
AAJ LäàFŽF�B ŒA(†D0ƒ
(A BBBG X
(A BBBA 84?A†JƒXP¯
AAD 

AAA <pÇL†FƒN a
AAE hAAFÃÆA ƒ†8°¨B�IŒA †A(ƒD@‚
(A ABBG ì=Aƒ;ëDæ \4K�BŒA †D(ƒD0ë
(Dà AÆBÌBÍF D(A ABBFÃÆÌÍH0ƒ†Œ�|”‡U�BŽI �E(ŒA0†D8ƒG`Ç
8A0A(B BBBF D
8C0A(B BBBH @ÃÆÌÍÎÏC`ƒ†Œ�Ž�0�A†AƒD g
DAF ODA(HoA†AƒD S
DAB tˆœ$°ZAƒG W
AP fCØì8‹B�BŒA †D(ƒJ@k
(A ABBA 8<‹B�BŒA †D(ƒJ@k
(A ABBA ,xÇBŒA†D ƒ�
ABI L¨›B�BŽB �B(ŒA0†A8ƒG�â
8D0A(B BBBH ø/Aƒh$BA†DƒG sAA<t(PòBŒA†D ƒb
ABD L|>B�BŽB �B(ŒA0†A8ƒGð
8A0A(B BBBF ,ÌpA†AƒD�H
AAH dügJŽB�B ŒA(†D0ƒr
(A BBBD n
(C BBBI `ÃÆÌÍÎH0ƒ†Œ�Ž8deB�GŒA †D(ƒK0B(D ABB4 þBŒF†I ƒ
ABE bABLؤB�BŽB �B(ŒA0†A8ƒGà-
8A0A(B BBBH H(ªB�BŽB �B(ŒA0†A8ƒD ©
8A0A(B BBBG HtOB�BŽB �B(ŒA0†A8ƒD°­
8A0A(B BBBK 0ÀBŒA†A ƒD@c
 AABA 4ô‰A†CƒG V
AAE K
AAK , YAƒI
F Hñÿ 

e
 ˜ ˆ
Þ
°,Sn�±5×÷55ŽìÈNàa? Ç "ØJp\YJZ‰™XËVOTªoR¤$&')+-/0135679;=?ACDFH¾H>JLNîNeP/PþRTVXZ\a"ˆž¦±¼Îäú-C\s€�—ž©" ¨ã""=H^…«Ó + L o ‘ ¯ × ÿ &
S
q
�
–
$ë²
¼
&Ó
'è
÷
  . C Y )‡t ‡ +�¢ -o¹ /Ò 0ë 1 ! 3ZG R o } 5” 6« 7‹Æ Ø ô 9‹

;Ç.
=›B
U
f
u
‡
?/š