pub struct LcdBuffer { /* private fields */ }Expand description
LCD coverage buffer, row 0 = bottom (matches Framebuffer convention).
Two planes, 3 bytes per pixel each:
color: per-channel premultiplied RGB colour accumulated from every paint so far.(R_color, G_color, B_color)where each byte ischannel_color * channel_alpha.alpha: per-channel alpha/coverage accumulated from every paint so far.(R_alpha, G_alpha, B_alpha)where each byte is the combined opacity of that subpixel column (0 = untouched, 255 = fully opaque).
Why per-channel alpha? LCD subpixel rendering produces a distinct
coverage value per R/G/B channel, so a single per-pixel alpha can’t
represent the output correctly at glyph edges and fractional image
boundaries. Splitting alpha per-channel gives each subpixel its own
Porter-Duff state: paints accumulate independently through the same
premultiplied src-over math you’d use for a normal RGBA surface, just
three streams instead of one. A cached LcdBuffer with partial
coverage can be composited onto any destination without the “black
rect where unpainted” failure mode that killed the first-cut design.
Implementations§
Source§impl LcdBuffer
impl LcdBuffer
Sourcepub fn new(width: u32, height: u32) -> Self
pub fn new(width: u32, height: u32) -> Self
Allocate a fully-transparent buffer (color zero, alpha zero everywhere). “Transparent” here means the per-channel alpha is 0, so composite-onto-destination leaves the destination unchanged wherever no paint has landed yet.
pub fn width(&self) -> u32
pub fn height(&self) -> u32
pub fn color_plane(&self) -> &[u8] ⓘ
pub fn alpha_plane(&self) -> &[u8] ⓘ
pub fn color_plane_mut(&mut self) -> &mut [u8] ⓘ
pub fn alpha_plane_mut(&mut self) -> &mut [u8] ⓘ
Sourcepub fn planes_mut(&mut self) -> (&mut [u8], &mut [u8])
pub fn planes_mut(&mut self) -> (&mut [u8], &mut [u8])
Both planes mutably in one borrow — for inner loops that update a pixel’s colour and alpha together (image blit, manual composite).
Sourcepub fn into_planes(self) -> (Vec<u8>, Vec<u8>)
pub fn into_planes(self) -> (Vec<u8>, Vec<u8>)
Consume the buffer, returning the owned (color, alpha) planes
as a pair — used when moving the painted pixels into Arcs for
a widget’s backbuffer cache or for GPU texture upload.
Sourcepub fn color_plane_flipped(&self) -> Vec<u8> ⓘ
pub fn color_plane_flipped(&self) -> Vec<u8> ⓘ
Top-row-first copy of the colour plane, suitable for a plain RGB8 upload or CPU blit. Row 0 of the output is the VISUAL top of the buffer (Y-up → Y-down flip).
Sourcepub fn alpha_plane_flipped(&self) -> Vec<u8> ⓘ
pub fn alpha_plane_flipped(&self) -> Vec<u8> ⓘ
Top-row-first copy of the alpha plane.
Sourcepub fn to_rgba8_top_down_collapsed(&self) -> Vec<u8> ⓘ
pub fn to_rgba8_top_down_collapsed(&self) -> Vec<u8> ⓘ
Collapse both planes into a single top-row-first straight-alpha
RGBA8 image suitable for the existing blit pipeline (one texture,
standard SRC_ALPHA, ONE_MINUS_SRC_ALPHA blend).
The per-channel alphas get collapsed to a single per-pixel alpha
via max(R_alpha, G_alpha, B_alpha); RGB is recovered by dividing
the premult colour by that max alpha (straight-alpha form). This
conversion is lossy when the three subpixel alphas diverge
(the whole point of the per-channel representation is lost under
collapse). It’s correct for typical monochrome-text cases where
all three alphas agree, and degrades gracefully otherwise —
Phase 5.2’s two-plane blit path preserves the full per-channel
information through upload and shader.
Sourcepub fn clear(&mut self, color: Color)
pub fn clear(&mut self, color: Color)
Fill the entire buffer with a solid colour. Every subpixel gets
the same premultiplied colour contribution and the same alpha —
a flat clear has no per-subpixel differentiation, so the three
alpha channels are all set to color.a and the three colour
channels to color.rgb * color.a.
Sourcepub fn fill_path(
&mut self,
path: &mut PathStorage,
color: Color,
transform: &TransAffine,
clip: Option<(f64, f64, f64, f64)>,
)
pub fn fill_path( &mut self, path: &mut PathStorage, color: Color, transform: &TransAffine, clip: Option<(f64, f64, f64, f64)>, )
Fill an AGG path through the LCD pipeline: rasterize at 3× X
resolution → 5-tap filter → per-channel src-over composite into
this buffer. transform is applied to path before the 3× X
scale (typically the caller’s CTM); the path’s coordinates are
in the buffer’s pixel space (Y-up, origin = bottom-left).
Optional clip is a screen-space rect (post-CTM, in mask pixel
coords) — pixels outside it are unaffected.
First non-text primitive on the buffer. Future fill / stroke /
image-blit entry points either call this directly (for solid
fills / outlines) or open their own LcdMaskBuilder scope when
they need to batch many paths into one mask.
First-cut implementation: rasterizes at the buffer’s full size. A later optimization can compute the path’s bbox and size the scratch tightly — measurable win for small paths in large buffers, but architecturally identical and not required for correctness.
Sourcepub fn composite_mask(
&mut self,
mask: &LcdMask,
src: Color,
dst_x: i32,
dst_y: i32,
clip: Option<(i32, i32, i32, i32)>,
)
pub fn composite_mask( &mut self, mask: &LcdMask, src: Color, dst_x: i32, dst_y: i32, clip: Option<(i32, i32, i32, i32)>, )
Composite an LcdMask into this buffer using per-channel
premultiplied Porter-Duff src-over. Each subpixel column’s
effective alpha is src.a × mask.channel_coverage, and colour +
alpha both accumulate under the standard premult src-over:
eff_a_c = src.a * mask.c
buf.color_c := src.c * eff_a_c + buf.color_c * (1 - eff_a_c)
buf.alpha_c := eff_a_c + buf.alpha_c * (1 - eff_a_c)(dst_x, dst_y) is the mask’s bottom-left in this buffer’s Y-up
pixel grid; mask row my writes to buffer row dst_y + my.
Optional clip (in this buffer’s integer pixel coords:
(x1, y1, x2, y2), half-open) suppresses writes outside its
bounds — used by widgets that paint inside a clipping parent.
Sourcepub fn composite_buffer(
&mut self,
src: &LcdBuffer,
dst_x: i32,
dst_y: i32,
clip: Option<(i32, i32, i32, i32)>,
)
pub fn composite_buffer( &mut self, src: &LcdBuffer, dst_x: i32, dst_y: i32, clip: Option<(i32, i32, i32, i32)>, )
Composite src onto this buffer at offset (dst_x, dst_y) via
per-channel premultiplied src-over — the buffer-level
analogue of Self::composite_mask. Each of the three
subpixel columns applies src.ch_alpha as its own
Porter-Duff weight:
buf.color_c := src.color_c + buf.color_c * (1 - src.alpha_c)
buf.alpha_c := src.alpha_c + buf.alpha_c * (1 - src.alpha_c)Untouched source pixels (alpha zero on every channel) don’t change the buffer at all — exactly the semantic that makes a popped layer leave unpainted areas alone, no seed trick needed.