Skip to main content

LcdBuffer

Struct LcdBuffer 

Source
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 is channel_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

Source

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.

Source

pub fn width(&self) -> u32

Source

pub fn height(&self) -> u32

Source

pub fn color_plane(&self) -> &[u8]

Source

pub fn alpha_plane(&self) -> &[u8]

Source

pub fn color_plane_mut(&mut self) -> &mut [u8]

Source

pub fn alpha_plane_mut(&mut self) -> &mut [u8]

Source

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).

Source

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.

Source

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).

Source

pub fn alpha_plane_flipped(&self) -> Vec<u8>

Top-row-first copy of the alpha plane.

Source

pub fn copy_rows_flipped_into( &self, row_start: u32, row_end: u32, dst_color: &mut [u8], dst_alpha: &mut [u8], )

Copy a top-down row range of both planes into caller-provided top-down destination planes, in place — the strip-update companion to Self::color_plane_flipped / Self::alpha_plane_flipped.

Row space: row_start..row_end are TOP-DOWN row indices (row 0 = visual top), half-open. dst_color / dst_alpha are full-size top-down planes (len == width * height * 3). For each top-down row r in the range we copy width * 3 bytes from the Y-up source row height - 1 - r into the same top-down row r of the destination — the same Y-up→top-down flip [flip_plane] performs, but restricted to a band of rows so a dirty-strip edit never rewrites the untouched majority of the buffer.

Rows outside the range are left untouched (they already hold the previously-flipped content the caller is reusing via Arc::make_mut).

Source

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.

Source

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.

Source

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.

The coverage mask is sized to the transformed path’s bounding box, not the whole buffer, via build_bounded_mask. A single small fill (e.g. a 1400×22 strip-background rect on a 1400×1492 band buffer) used to allocate + rasterize + 5-tap filter a full-buffer mask, making per-fill cost O(buffer) — measured at ~40 ms for that strip. Bounding to the bbox makes it O(bbox); the output is byte-identical (see build_bounded_mask for the equivalence argument and fill_path_bbox_tests for the pixel checks).

Source

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.

Source

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, )
where F: FnMut(i32, i32) -> Color,

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.

Source

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§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts 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>

Converts 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)

Converts &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)

Converts &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
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<A> Is for A
where A: Any,

Source§

fn is<T>() -> bool
where T: Any,

Checks if the current type “is” another type, using a TypeId equality comparison. This is most useful in the context of generic logic. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.