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)>,
fill_rule: FillRule,
)
pub fn fill_path( &mut self, path: &mut PathStorage, color: Color, transform: &TransAffine, clip: Option<(f64, f64, f64, f64)>, fill_rule: FillRule, )
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_mask_with_color<F>(
&mut self,
mask: &LcdMask,
dst_x: i32,
dst_y: i32,
clip: Option<(i32, i32, i32, i32)>,
color_at: F,
)
pub fn composite_mask_with_color<F>( &mut self, mask: &LcdMask, dst_x: i32, dst_y: i32, clip: Option<(i32, i32, i32, i32)>, color_at: F, )
Composite an LcdMask using a per-pixel source colour callback.
The callback receives destination pixel coordinates in this buffer’s Y-up pixel space. This keeps the LCD coverage pipeline shared for solid and gradient fills while allowing colour to vary across the mask.
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.
Auto Trait Implementations§
impl Freeze for LcdBuffer
impl RefUnwindSafe for LcdBuffer
impl Send for LcdBuffer
impl Sync for LcdBuffer
impl Unpin for LcdBuffer
impl UnsafeUnpin for LcdBuffer
impl UnwindSafe for LcdBuffer
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more