use super::*;
use std::sync::Arc;
use crate::text::Font;
const FONT_BYTES: &[u8] = include_bytes!("../../../demo/assets/CascadiaCode.ttf");
fn font() -> Arc<Font> {
Arc::new(Font::from_slice(FONT_BYTES).expect("font"))
}
#[test]
fn test_lcd_mask_has_coverage() {
let mask = rasterize_lcd_mask(
&font(),
"Hello",
16.0,
4.0,
12.0,
200,
40,
&TransAffine::new(),
);
let total: u64 = mask.data.iter().map(|&b| b as u64).sum();
assert!(total > 0, "rasterize_lcd_mask produced all-zero coverage");
}
#[test]
fn test_lcd_mask_has_channel_variation() {
let mask = rasterize_lcd_mask(
&font(),
"Wing",
24.0,
4.0,
16.0,
400,
40,
&TransAffine::new(),
);
let mut saw = false;
for px in mask.data.chunks_exact(3) {
let r = px[0];
let g = px[1];
let b = px[2];
let mx = r.max(g).max(b);
let mn = r.min(g).min(b);
if mx > 20 && (mx - mn) > 10 {
saw = true;
break;
}
}
assert!(saw, "no per-channel variation at edges");
}
#[test]
fn test_composite_dark_on_light_and_light_on_dark() {
let mask = rasterize_lcd_mask(&font(), "Hi", 20.0, 2.0, 14.0, 80, 24, &TransAffine::new());
let mut fb_white = vec![255u8; 80 * 24 * 4];
composite_lcd_mask(&mut fb_white, 80, 24, &mask, Color::black(), 0, 0);
let sum_white: u64 = fb_white
.chunks_exact(4)
.map(|p| p[0] as u64 + p[1] as u64 + p[2] as u64)
.sum();
assert!(
sum_white < 80 * 24 * 3 * 255,
"dark-on-white composite left every pixel white"
);
let mut fb_black = vec![0u8; 80 * 24 * 4];
for chunk in fb_black.chunks_exact_mut(4) {
chunk[3] = 255;
}
composite_lcd_mask(&mut fb_black, 80, 24, &mask, Color::white(), 0, 0);
let sum_black: u64 = fb_black
.chunks_exact(4)
.map(|p| p[0] as u64 + p[1] as u64 + p[2] as u64)
.sum();
assert!(
sum_black > 0,
"light-on-black composite left every pixel black"
);
}
#[test]
fn test_composite_lcd_mask_honours_src_alpha() {
let mask = LcdMask {
data: vec![255, 255, 255],
width: 1,
height: 1,
};
let mut fb_full = vec![255u8, 255, 255, 255];
composite_lcd_mask(
&mut fb_full,
1,
1,
&mask,
Color::rgba(0.0, 0.0, 0.0, 1.0),
0,
0,
);
assert_eq!(
fb_full[0], 0,
"alpha=1 black-on-white should fully cover → R=0"
);
let mut fb_half = vec![255u8, 255, 255, 255];
composite_lcd_mask(
&mut fb_half,
1,
1,
&mask,
Color::rgba(0.0, 0.0, 0.0, 0.5),
0,
0,
);
assert!(
fb_half[0] >= 120 && fb_half[0] <= 135,
"alpha=0.5 black-on-white should land near R=128, got {}",
fb_half[0]
);
let mut fb_zero = vec![255u8, 255, 255, 255];
composite_lcd_mask(
&mut fb_zero,
1,
1,
&mask,
Color::rgba(0.0, 0.0, 0.0, 0.0),
0,
0,
);
assert_eq!(fb_zero[0], 255, "alpha=0 must leave destination untouched");
}
#[test]
fn test_lcd_buffer_clear_writes_solid_color() {
let mut buf = LcdBuffer::new(4, 3);
buf.clear(Color::rgba(1.0, 0.5, 0.25, 1.0));
for px in buf.color_plane().chunks_exact(3) {
assert_eq!(px[0], 255);
assert_eq!(px[1], 128);
assert_eq!(px[2], 64);
}
let mut buf2 = LcdBuffer::new(2, 2);
buf2.clear(Color::rgba(1.0, 1.0, 1.0, 0.5));
for px in buf2.color_plane().chunks_exact(3) {
assert_eq!(px[0], 128);
assert_eq!(px[1], 128);
assert_eq!(px[2], 128);
}
}
#[test]
fn test_lcd_buffer_fresh_is_fully_transparent() {
let buf = LcdBuffer::new(8, 4);
assert!(
buf.color_plane().iter().all(|&b| b == 0),
"fresh buffer's color plane must be zero"
);
assert!(
buf.alpha_plane().iter().all(|&b| b == 0),
"fresh buffer's alpha plane must be zero (= fully transparent)"
);
}
#[test]
fn test_lcd_buffer_transparent_plus_black_text_leaves_alpha_only() {
let f = font();
let mask = rasterize_lcd_mask(&f, "Hi", 20.0, 2.0, 14.0, 80, 24, &TransAffine::new());
let mut buf = LcdBuffer::new(80, 24);
buf.composite_mask(&mask, Color::black(), 0, 0, None);
assert!(
buf.color_plane().iter().all(|&b| b == 0),
"black-text-on-transparent: premult colour is 0, so color_plane stays zero"
);
let alpha_nonzero = buf.alpha_plane().iter().filter(|&&b| b > 0).count();
assert!(
alpha_nonzero > 0,
"alpha_plane must show coverage where text was rasterized"
);
let bottom_left_i = 0;
let bottom_right_i = (80 - 1) * 3;
let top_left_i = (23 * 80) * 3;
let top_right_i = (23 * 80 + 79) * 3;
for i in [bottom_left_i, bottom_right_i, top_left_i, top_right_i] {
assert_eq!(
&buf.alpha_plane()[i..i + 3],
&[0u8, 0, 0],
"corner at byte offset {i} should be transparent"
);
}
}
#[test]
fn test_lcd_buffer_red_text_writes_premultiplied_color() {
let f = font();
let w = 80u32;
let h = 24u32;
let mask = rasterize_lcd_mask(&f, "I", 24.0, 4.0, 18.0, w, h, &TransAffine::new());
let mut buf = LcdBuffer::new(w, h);
buf.composite_mask(&mask, Color::rgba(1.0, 0.0, 0.0, 1.0), 0, 0, None);
let mut saw_full_red = false;
for i in (0..(w * h) as usize).map(|p| p * 3) {
if buf.alpha_plane()[i] == 255
&& buf.color_plane()[i] == 255
&& buf.color_plane()[i + 1] == 0
&& buf.color_plane()[i + 2] == 0
{
saw_full_red = true;
break;
}
}
assert!(
saw_full_red,
"expected at least one fully-covered pure-red pixel"
);
}
#[test]
fn test_lcd_buffer_composite_buffer_leaves_dst_untouched_where_src_is_transparent() {
let src = LcdBuffer::new(4, 4);
let mut dst = LcdBuffer::new(4, 4);
dst.clear(Color::white());
for px in dst.color_plane().chunks_exact(3) {
assert_eq!(px, [255, 255, 255]);
}
for px in dst.alpha_plane().chunks_exact(3) {
assert_eq!(px, [255, 255, 255]);
}
dst.composite_buffer(&src, 0, 0, None);
for px in dst.color_plane().chunks_exact(3) {
assert_eq!(
px,
[255, 255, 255],
"dst colour must survive transparent src composite"
);
}
for px in dst.alpha_plane().chunks_exact(3) {
assert_eq!(
px,
[255, 255, 255],
"dst alpha must survive transparent src composite"
);
}
}
#[test]
fn test_lcd_buffer_composite_buffer_opaque_pixel_replaces_dst() {
let mut src = LcdBuffer::new(3, 3);
let i = (1 * 3 + 1) * 3;
src.color_plane_mut()[i] = 255; src.color_plane_mut()[i + 1] = 0;
src.color_plane_mut()[i + 2] = 0;
src.alpha_plane_mut()[i] = 255;
src.alpha_plane_mut()[i + 1] = 255;
src.alpha_plane_mut()[i + 2] = 255;
let mut dst = LcdBuffer::new(3, 3);
dst.clear(Color::white());
dst.composite_buffer(&src, 0, 0, None);
assert_eq!(
&dst.color_plane()[i..i + 3],
&[255, 0, 0],
"opaque src pixel must fully replace dst pixel's colour"
);
assert_eq!(
&dst.alpha_plane()[i..i + 3],
&[255, 255, 255],
"alpha stays full opacity after opaque-src overwrite"
);
assert_eq!(
&dst.color_plane()[0..3],
&[255, 255, 255],
"corner should retain dst white (src was transparent there)"
);
}
#[test]
fn test_lcd_buffer_composite_mask_deposits_coverage() {
let mask = rasterize_lcd_mask(&font(), "Hi", 20.0, 2.0, 14.0, 80, 24, &TransAffine::new());
let mut buf = LcdBuffer::new(80, 24);
buf.clear(Color::white()); let before: u64 = buf.color_plane().iter().map(|&b| b as u64).sum();
buf.composite_mask(&mask, Color::black(), 0, 0, None); let after: u64 = buf.color_plane().iter().map(|&b| b as u64).sum();
assert!(
after < before,
"compositing dark text onto white bg should reduce summed brightness"
);
}
#[test]
fn test_lcd_mask_builder_matches_legacy_text_path() {
let f = font();
let w: u32 = 120;
let h: u32 = 30;
let xform = TransAffine::new();
let legacy = rasterize_lcd_mask_multi(&f, &[("Equiv", 4.0, 18.0)], 22.0, w, h, &xform);
let mut builder = LcdMaskBuilder::new(w, h);
builder.with_paths(&xform, |add| {
let (mut paths, _) = crate::text::shape_text(&f, "Equiv", 22.0, 4.0, 18.0);
for p in paths.iter_mut() {
add(p);
}
});
let built = builder.finalize();
assert_eq!(legacy.width, built.width);
assert_eq!(legacy.height, built.height);
assert_eq!(
legacy.data, built.data,
"LcdMaskBuilder must reproduce rasterize_lcd_mask_multi byte-for-byte"
);
}
#[test]
fn test_lcd_buffer_fill_path_solid_rect() {
use agg_rust::basics::PATH_FLAGS_NONE;
let mut buf = LcdBuffer::new(20, 10);
buf.clear(Color::white());
let mut path = PathStorage::new();
path.move_to(5.0, 3.0);
path.line_to(15.0, 3.0);
path.line_to(15.0, 7.0);
path.line_to(5.0, 7.0);
path.close_polygon(PATH_FLAGS_NONE);
buf.fill_path(
&mut path,
Color::black(),
&TransAffine::new(),
None,
FillRule::NonZero,
);
let pixel = |x: usize, y: usize| -> (u8, u8, u8) {
let i = (y * 20 + x) * 3;
(
buf.color_plane()[i],
buf.color_plane()[i + 1],
buf.color_plane()[i + 2],
)
};
assert_eq!(
pixel(10, 5),
(0, 0, 0),
"interior pixel of solid rect should be fully covered black"
);
assert_eq!(
pixel(1, 1),
(255, 255, 255),
"pixel outside rect should be untouched"
);
assert_eq!(
pixel(18, 8),
(255, 255, 255),
"pixel outside rect should be untouched"
);
}
#[test]
fn test_lcd_buffer_fill_path_matches_text_pipeline_for_glyphs() {
let f = font();
let w: u32 = 80;
let h: u32 = 24;
let size = 18.0;
let baseline = (4.0_f64, 14.0_f64);
let legacy_mask = rasterize_lcd_mask_multi(
&f,
&[("ag", baseline.0, baseline.1)],
size,
w,
h,
&TransAffine::new(),
);
let mut buf_a = LcdBuffer::new(w, h);
buf_a.clear(Color::white());
buf_a.composite_mask(&legacy_mask, Color::black(), 0, 0, None);
let (mut paths, _) = crate::text::shape_text(&f, "ag", size, baseline.0, baseline.1);
let mut buf_b = LcdBuffer::new(w, h);
buf_b.clear(Color::white());
let mut builder = LcdMaskBuilder::new(w, h);
builder.with_paths(&TransAffine::new(), |add| {
for p in paths.iter_mut() {
add(p);
}
});
let mask_b = builder.finalize();
buf_b.composite_mask(&mask_b, Color::black(), 0, 0, None);
assert_eq!(
buf_a.color_plane(),
buf_b.color_plane(),
"fill_path-via-builder must match legacy text mask pipeline byte-for-byte"
);
}
#[test]
fn test_lcd_buffer_composite_matches_composite_lcd_mask() {
let w: u32 = 100;
let h: u32 = 28;
let mask = rasterize_lcd_mask(&font(), "Equiv", 22.0, 4.0, 18.0, w, h, &TransAffine::new());
let mut rgba = vec![255u8; (w * h * 4) as usize];
composite_lcd_mask(&mut rgba, w, h, &mask, Color::black(), 0, 0);
let mut buf = LcdBuffer::new(w, h);
buf.clear(Color::white());
buf.composite_mask(&mask, Color::black(), 0, 0, None);
for y in 0..h as usize {
for x in 0..w as usize {
let ai = (y * w as usize + x) * 4;
let bi = (y * w as usize + x) * 3;
let a_rgb = (rgba[ai], rgba[ai + 1], rgba[ai + 2]);
let b_rgb = (
buf.color_plane()[bi],
buf.color_plane()[bi + 1],
buf.color_plane()[bi + 2],
);
assert_eq!(
a_rgb, b_rgb,
"RGB mismatch at ({x},{y}): RGBA-path={a_rgb:?} LcdBuffer-path={b_rgb:?}"
);
}
}
}