hb-gpu: HarfBuzz Manual

## hb-gpu hb-gpu — GPU shape encoding |

## Functions

const char * | hb_gpu_shader_fragment_source ()
const char * | hb_gpu_shader_vertex_source ()
hb_gpu_draw_t * | hb_gpu_draw_create_or_fail ()
hb_gpu_draw_t * | hb_gpu_draw_reference ()
void | hb_gpu_draw_destroy ()
hb_bool_t | hb_gpu_draw_set_user_data ()
void * | hb_gpu_draw_get_user_data ()
hb_draw_funcs_t * | hb_gpu_draw_get_funcs ()
void | hb_gpu_draw_set_scale ()
void | hb_gpu_draw_glyph ()
hb_blob_t * | hb_gpu_draw_encode ()
void | hb_gpu_draw_get_extents ()
void | hb_gpu_draw_reset ()
void | hb_gpu_draw_recycle_blob ()

## Types and Values

enum | hb_gpu_shader_lang_t
typedef | hb_gpu_draw_t

## Includes

    #include <hb-gpu.h>

## Description

This module implements the Slug GPU text rasterization algorithm by Eric Lengyel. Glyph outlines are encoded on the CPU into compact blobs that the GPU decodes and rasterizes directly in the fragment shader, with no intermediate bitmap atlas.

See the live web demo.

### Encoding

Each glyph is encoded independently into an opaque blob of RGBA16I texels (8 bytes each). Typical encoding flow:

    hb_gpu_draw_t *draw = hb_gpu_draw_create_or_fail ();

    hb_gpu_draw_glyph (draw, font, gid);
    hb_blob_t *blob = hb_gpu_draw_encode (draw);

    // Upload hb_blob_get_data(blob) to the atlas at some offset.
    unsigned atlas_offset = upload_to_atlas (blob);

    hb_gpu_draw_recycle_blob (draw, blob);
    hb_gpu_draw_reset (draw);
    // repeat for more glyphs...

    hb_gpu_draw_destroy (draw);

The encoder object and its internal buffers are reused across glyphs. Call `hb_gpu_draw_recycle_blob()` after each encode to allow buffer reuse; call `hb_gpu_draw_reset()` before each new glyph.

### Atlas setup

All encoded blobs are uploaded into a single GL_TEXTURE_BUFFER backed by a GL_RGBA16I format buffer. Each glyph occupies a contiguous range of texels at a known offset:

    GLuint buf, tex;
    glGenBuffers (1, &buf);
    glGenTextures (1, &tex);

    glBindBuffer (GL_TEXTURE_BUFFER, buf);
    glBufferData (GL_TEXTURE_BUFFER, capacity * 8, NULL, GL_STATIC_DRAW);

    glBindTexture (GL_TEXTURE_BUFFER, tex);
    glTexBuffer (GL_TEXTURE_BUFFER, GL_RGBA16I, buf);

    // Upload a glyph blob at 'offset' texels:
    glBufferSubData (GL_TEXTURE_BUFFER,
                     offset * 8,
                     hb_blob_get_length (blob),
                     hb_blob_get_data (blob, NULL));

### Shader compilation

The library provides GLSL source strings for a vertex helper function and a fragment rendering function. Prepend a version directive and append your own `main()`:

    unsigned vert_count, frag_count;
    const char * const *vert_lib = hb_gpu_shader_vertex_sources (
        HB_GPU_SHADER_LANG_GLSL, &vert_count);
    const char * const *frag_lib = hb_gpu_shader_fragment_sources (
        HB_GPU_SHADER_LANG_GLSL, &frag_count);

    const char *vert_sources[] = {
        "#version 330\n",
        hb_gpu_shader_vertex_source (HB_GPU_SHADER_LANG_GLSL),
        your_vertex_main
    };
    glShaderSource (vert_shader, 3, vert_sources, NULL);
    // Same pattern for the fragment shader.

### Vertex shader

The vertex library provides one function:

    void hb_gpu_dilate (inout vec2 position, inout vec2 texcoord,
                        vec2 normal, vec4 jac,
                        mat4 m, vec2 viewport);

This expands each glyph quad by approximately half a pixel on screen to ensure proper antialiasing coverage at the edges. It must be called in the vertex shader before computing gl_Position.

Per-vertex attributes for each glyph quad (2 triangles, 6 vertices, 4 unique corners):

- position (vec2): object-space vertex position, computed from the glyph's bounding box. For corner (cx, cy) where cx,cy are 0 or 1:

      pos.x = pen_x + scale * lerp(extent_min_x, extent_max_x, cx)
      pos.y = pen_y - scale * lerp(extent_min_y, extent_max_y, cy)

  where scale = font_size / upem.

- texcoord (vec2): em-space texture coordinates, equal to the raw extent values: `(ex, ey)` where ex/ey are the glyph's bounding box corners in font design units. The fragment shader samples the encoded blob in this coordinate space.

- normal (vec2): outward normal at each corner. `(-1, +1)` for (0,0), `(+1, +1)` for (1,0), `(-1, -1)` for (0,1), `(+1, -1)` for (1,1). Note: y is negated relative to cx,cy because the common em-to-object transform flips y.

- jac (vec4): maps object-space displacements back to em-space after dilation, stored row-major as (j00, j01, j10, j11). For the common case of uniform scaling with y-flip:

      jac = vec4(emPerPos, 0.0, 0.0, -emPerPos)

  where emPerPos = upem / font_size. For non-trivial transforms, see the hb_gpu_dilate source.

- m (mat4): the model-view-projection matrix (uniform).

- viewport (vec2): the viewport size in pixels (uniform).

A typical vertex main:

    uniform mat4 u_matViewProjection;
    uniform vec2 u_viewport;

    in vec2 a_position;
    in vec2 a_texcoord;
    in vec2 a_normal;
    in float a_emPerPos;
    in uint a_glyphLoc;

    out vec2 v_texcoord;
    flat out uint v_glyphLoc;

    void main () {
      vec2 pos = a_position;
      vec2 tex = a_texcoord;
      vec4 jac = vec4 (a_emPerPos, 0.0, 0.0, -a_emPerPos);
      hb_gpu_dilate (pos, tex, a_normal, jac,
                     u_matViewProjection, u_viewport);
      gl_Position = u_matViewProjection * vec4 (pos, 0.0, 1.0);
      v_texcoord = tex;
      v_glyphLoc = a_glyphLoc;
    }

### Font scale

The encoder works in font design units (upem). For best results, do not set a scale on the hb_font_t passed to `hb_gpu_draw_glyph()` — leave it at the default (upem). Scaling is applied later when computing vertex positions:

    float scale = font_size / upem;
    pos.x = pen_x + scale * extent_x;
    pos.y = pen_y - scale * extent_y;

If you do set a font scale (e.g. for hinting), the encoded blob and extents will be in the scaled coordinate space, and you must adjust emPerPos accordingly.

### Dilation

Each glyph is rendered on a screen-aligned quad whose corners match the glyph's bounding box. Without dilation, the antialiased edges at the quad boundary get clipped — the fragment shader needs at least half a pixel of padding around the glyph to produce smooth coverage gradients.

hb_gpu_dilate computes a perspective-correct half-pixel expansion. It adjusts both the vertex position (expanding the quad) and the texcoord (so the fragment shader samples the right location in em-space after expansion).

The `jac` parameter maps object-space displacements back to em-space. For the common case of uniform scaling with y-flip (`pos.y = pen_y - scale * ey`):

    float emPerPos = upem / font_size;
    jac = vec4(emPerPos, 0.0, 0.0, -emPerPos);

For non-uniform scaling or shearing transforms, jac is the 2x2 inverse of the em-to-object linear transform, stored row-major.

#### Static dilation alternative

Applications that do not need perspective correctness (e.g. strictly 2D rendering at known sizes) can skip hb_gpu_dilate and instead expand each quad vertex by a fixed amount along its normal, based on the minimum expected font size:

    float min_ppem = 10.0;  // smallest expected size in pixels
    float pad = 0.5 * upem / min_ppem;  // half-pixel in em units
    pos += normal * pad * scale;
    texcoord += normal * pad;

This over-dilates at larger sizes (wasting fill rate on transparent pixels) but is simpler to implement and avoids the need for the MVP matrix and viewport size in the vertex shader.

### Fragment shader

The fragment library provides two functions:

    float hb_gpu_render (vec2 renderCoord, uint glyphLoc);

It requires the `hb_gpu_atlas` uniform to be bound to the texture containing the encoded glyph data. By default this is an `isamplerBuffer` (GL_TEXTURE_BUFFER). For platforms without texture buffers (e.g. WebGL2), define `HB_GPU_ATLAS_2D` before including the shader source; the atlas then becomes an `isampler2D` and a `uniform int hb_gpu_atlas_width` must also be set to the texture width.

Parameters:

- renderCoord: the interpolated em-space coordinate from the vertex shader (v_texcoord).

- glyphLoc: the texel offset of this glyph's encoded blob in the atlas buffer. Passed as a flat varying from the vertex shader.

Returns the coverage in [0, 1]: 1.0 inside the glyph, 0.0 outside, with antialiased edges.

A typical fragment main:

    in vec2 v_texcoord;
    flat in uint v_glyphLoc;
    out vec4 fragColor;

    void main () {
      float coverage = hb_gpu_render (v_texcoord, v_glyphLoc);
      fragColor = vec4 (0.0, 0.0, 0.0, coverage);
    }

The coverage value can be used with alpha blending (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) to composite over a background, or multiplied with any color.

### Stem darkening

    float hb_gpu_darken (float coverage, float brightness, float ppem);

Optional stem darkening adjusts coverage at small sizes so thin stems are not washed out by gamma correction.

Parameters:

- coverage: the output of hb_gpu_render.

- brightness: foreground brightness in [0, 1], computed as `dot (foreground.rgb, vec3 (1.0 / 3.0))`.

- ppem: pixels per em at this fragment, computed as `1.0 / max (fwidth (v_texcoord).x, fwidth (v_texcoord).y)`.

Example:

    float coverage = hb_gpu_render (v_texcoord, v_glyphLoc);
    float brightness = dot (u_foreground.rgb, vec3 (1.0 / 3.0));
    float ppem = 1.0 / max (fwidth (v_texcoord).x,
                             fwidth (v_texcoord).y);
    coverage = hb_gpu_darken (coverage, brightness, ppem);

## Functions

### hb_gpu_shader_fragment_source ()

    const char *
    hb_gpu_shader_fragment_source (hb_gpu_shader_lang_t lang);

Returns the fragment shader source for the specified shader language. The returned string is static and must not be freed.

The caller should prepend a `#version` directive and append their own ``main()`` function, then pass the combined sources to ``glShaderSource()``.

#### Parameters

lang | shader language variant |

#### Returns

A shader source string, or `NULL` if `lang` is unsupported.

[transfer none]

Since: 14.0.0

### hb_gpu_shader_vertex_source ()

    const char *
    hb_gpu_shader_vertex_source (hb_gpu_shader_lang_t lang);

Returns the vertex shader source for the specified shader language. The returned string is static and must not be freed.

#### Parameters

lang | shader language variant |

#### Returns

A shader source string, or `NULL` if `lang` is unsupported.

[transfer none]

Since: 14.0.0

### hb_gpu_draw_create_or_fail ()

    hb_gpu_draw_t *
    hb_gpu_draw_create_or_fail (void);

Creates a new GPU shape encoder.

#### Returns

A newly allocated hb_gpu_draw_t, or `NULL` on allocation failure.

[transfer full]

Since: 14.0.0

### hb_gpu_draw_reference ()

    hb_gpu_draw_t *
    hb_gpu_draw_reference (hb_gpu_draw_t *draw);

Increases the reference count on `draw` by one.

[skip]

#### Parameters

draw | a GPU shape encoder |

#### Returns

The referenced hb_gpu_draw_t.

[transfer full]

Since: 14.0.0

### hb_gpu_draw_destroy ()

    void
    hb_gpu_draw_destroy (hb_gpu_draw_t *draw);

Decreases the reference count on `draw` by one. When the reference count reaches zero, the encoder is freed.

[skip]

#### Parameters

draw | a GPU shape encoder |

Since: 14.0.0

### hb_gpu_draw_set_user_data ()

    hb_bool_t
    hb_gpu_draw_set_user_data (hb_gpu_draw_t *draw,
                               hb_user_data_key_t *key,
                               void *data,
                               hb_destroy_func_t destroy,
                               hb_bool_t replace);

Attaches user data to `draw` .

[skip]

#### Parameters

draw | a GPU shape encoder |
key | the user-data key |
data | a pointer to the user data |
destroy | a callback to call when `data` is not needed anymore. | [nullable]
replace | whether to replace an existing data with the same key |

#### Returns

`true` if success, `false` otherwise

Since: 14.0.0

### hb_gpu_draw_get_user_data ()

    void *
    hb_gpu_draw_get_user_data (hb_gpu_draw_t *draw,
                               hb_user_data_key_t *key);

Fetches the user-data associated with the specified key.

[skip]

#### Parameters

draw | a GPU shape encoder |
key | the user-data key |

#### Returns

A pointer to the user data.

[transfer none]

Since: 14.0.0

### hb_gpu_draw_get_funcs ()

    hb_draw_funcs_t *
    hb_gpu_draw_get_funcs (void);

Fetches the singleton hb_draw_funcs_t that feeds outline data into an hb_gpu_draw_t. Pass the hb_gpu_draw_t as the `draw_data` argument when calling the draw functions.

#### Returns

The GPU draw functions.

[transfer none]

Since: 14.0.0

### hb_gpu_draw_set_scale ()

    void
    hb_gpu_draw_set_scale (hb_gpu_draw_t *draw,
                           int x_scale,
                           int y_scale);

Sets the font scale so the encoded blob can embed it for shader use (e.g. computing pixels-per-em). Called automatically by `hb_gpu_draw_glyph()`.

#### Parameters

draw | a GPU shape encoder |
x_scale | horizontal scale (typically from `hb_font_get_scale()`) |
y_scale | vertical scale |

Since: 14.1.0

### hb_gpu_draw_glyph ()

    void
    hb_gpu_draw_glyph (hb_gpu_draw_t *draw,
                       hb_font_t *font,
                       hb_codepoint_t codepoint);

Convenience wrapper that draws a single glyph outline into the encoder using `hb_font_draw_glyph()`. Also sets the font scale on the encoder.

#### Parameters

draw | a GPU shape encoder |
font | font to draw from |
codepoint | glyph ID to draw |

Since: 14.0.0

### hb_gpu_draw_encode ()

    hb_blob_t *
    hb_gpu_draw_encode (hb_gpu_draw_t *draw);

Encodes the accumulated outlines into a compact blob suitable for GPU rendering. The blob data is an array of RGBA16I texels (8 bytes each) to be uploaded to a texture buffer object.

The returned blob owns its own copy of the data.

#### Parameters

draw | a GPU shape encoder |

#### Returns

An hb_blob_t containing the encoded data, or `NULL` if encoding fails.

[transfer full]

Since: 14.0.0

### hb_gpu_draw_get_extents ()

    void
    hb_gpu_draw_get_extents (hb_gpu_draw_t *draw,
                             hb_glyph_extents_t *extents);

Fetches the extents of the accumulated outlines.

#### Parameters

draw | a GPU shape encoder |
extents | glyph extents. | [out]

Since: 14.0.0

### hb_gpu_draw_reset ()

    void
    hb_gpu_draw_reset (hb_gpu_draw_t *draw);

Resets the encoder, discarding all accumulated outlines. The internal encode buffer is kept for reuse.

#### Parameters

draw | a GPU shape encoder |

Since: 14.0.0

### hb_gpu_draw_recycle_blob ()

    void
    hb_gpu_draw_recycle_blob (hb_gpu_draw_t *draw,
                              hb_blob_t *blob);

Returns a blob to the encoder for potential reuse. The caller transfers ownership of `blob` .

Currently this simply destroys the blob. A future version may reclaim the underlying buffer to avoid allocation on the next `hb_gpu_draw_encode()` call.

#### Parameters

draw | a GPU shape encoder |
blob | a blob previously returned by `hb_gpu_draw_encode()`. | [transfer full]

Since: 14.0.0

## Types and Values

### enum hb_gpu_shader_lang_t

Shader language variant.

#### Members

HB_GPU_SHADER_LANG_GLSL | GLSL (OpenGL 3.3 / OpenGL ES 3.0 / WebGL 2.0) |
HB_GPU_SHADER_LANG_WGSL | WGSL (WebGPU) |
HB_GPU_SHADER_LANG_MSL | MSL (Metal) |
HB_GPU_SHADER_LANG_HLSL | HLSL (Direct3D) |

Since: 14.0.0

### hb_gpu_draw_t

    typedef struct hb_gpu_draw_t hb_gpu_draw_t;

An opaque GPU shape encoder. Accumulates outlines via draw callbacks, then encodes them into a compact blob for GPU rendering.

Since: 14.0.0