Expand description
LCD subpixel text as a per-channel coverage mask that composites onto arbitrary backgrounds — no bg pre-fill, no destination-color knowledge required at rasterization time.
§Why this replaces the pre-fill approach
The older PixfmtRgba32Lcd path baked the caller’s background colour
into the rasterised output via a per-channel src-over against the
pre-filled framebuffer. That coupled the LCD glyphs to one specific
destination and forced us to know that destination everywhere text is
drawn — driving the walk / sample / push / pop complexity.
Instead, we keep the three subpixel coverage values independent:
the output of the rasteriser is three 8-bit channels per pixel
(cov_r, cov_g, cov_b) describing how much of each subpixel the glyph
covered. At composite time a per-channel Porter-Duff over blend
mixes the TEXT COLOUR into the live destination:
dst.r = src.r * cov.r + dst.r * (1 - cov.r)
dst.g = src.g * cov.g + dst.g * (1 - cov.g)
dst.b = src.b * cov.b + dst.b * (1 - cov.b)The coverage mask is the same regardless of where it lands; the blend naturally produces the correct LCD chroma against any background.
See lcd-subpixel-compositing.md at the repository root for the full
derivation.
§Pipeline
shape_text (rustybuzz kerning + fallback chain — unchanged)
│
per-glyph PathStorage → ConvTransform(scale_x_3) → PixfmtGray8
(8-bit grayscale coverage at 3× horizontal resolution)
│
5-tap low-pass filter per output channel
│
packed (cov_r, cov_g, cov_b) 3-byte maskStructs§
- Cached
LcdText - Result of
rasterize_text_lcd_cached. Callers composite the mask at(x - baseline_x_in_mask, y - baseline_y_in_mask)where(x, y)is the target baseline position in local / screen coordinates. - LcdBuffer
- LCD coverage buffer, row 0 = bottom (matches
Framebufferconvention). - LcdMask
- 3-byte-per-pixel LCD coverage mask. Callers composite via
composite_lcd_mask. The distinction from a normal RGBA image is crucial: the three channels are independent coverage values, not an RGB colour — they drive a per-channel blend where each subpixel mixes the source colour with the destination colour by its own amount. - LcdMask
Builder - Accumulator for an
LcdMask. Build the gray buffer with one or morewith_pathscalls (each opens an AGG rasterizer scope), thenfinalizeto apply the 5-tap filter and produce the packed mask.
Functions§
- composite_
lcd_ mask - Composite an
LcdMaskontodst_rgbausing per-channel Porter-Duff “over”: each subpixel mixessrc_colorinto the live destination by its own coverage. The destination colour is whatever pixels are currently at the target rect — so this works over any background. - identity_
xform - Identity transform — exposed so call sites that don’t otherwise
depend on
agg_rust::trans_affine::TransAffinecan pass one. - rasterize_
lcd_ mask - Rasterize
textat baseline(x, y)into a 3-channel coverage mask of sizemask_w × mask_h.transformis applied before the 3× X scale that puts the path into the high-resolution grayscale buffer. - rasterize_
lcd_ mask_ multi - Multi-span variant: raster several
(text, x, y)tuples into a single mask. Used by wrapped-textLabelso every line shares one 3×-wide gray buffer and one filter pass. The gray buffer is written cumulatively by AGG (glyphs in different pixels don’t interact, so non-overlapping lines just occupy disjoint rows). - rasterize_
text_ lcd_ cached - Rasterise
textinfontatsizeinto a 3-channel LCD coverage mask, caching the result so subsequent calls with the same(text, font, size)return the sharedArcwithout re-running AGG. - rect_
to_ pixel_ clip - Convert a screen-space float clip rect
(x, y, w, h)to the integer pixel clip box(x1, y1, x2, y2)(half-open) used by [LcdBuffer::composite_mask]. Floor on the left/bottom and ceil on the right/top so any pixel touched by the clip rect (even partially) is included — matches the AGG raster-clip convention.