tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
chore: refresco desde el monorepo — vello 0.9 / wgpu 29, 120 miembros
1//! **Costura entre celdas** (*conflation*) — la medición que respalda la
2//! cuantización de `shuma_module_shell::view::tui::cuantiza`.
3//!
4//! Dos rects que comparten borde y se antialiasan **por separado** no suman
5//! 1.0 en el píxel de la junta: cada uno lo cubre a medias y la composición da
6//! ~0,75. Un grid de terminal pinta un rect por celda (fondos, y los bloques
7//! `▀▄█` con los que un TUI rellena zonas), así que eso es una costura más
8//! clara en CADA frontera — a escala de pantalla, una cuadrícula sobre lo que
9//! debería ser una zona sólida.
10//!
11//! No se arregla haciendo coincidir los bordes: ya coincidían. Se arregla
12//! haciéndolos caer en **enteros**, así cada píxel queda cubierto por completo
13//! por uno solo de los dos rects.
14
15use llimphi_compositor::View;
16use llimphi_layout::taffy::prelude::{percent, Size, Style};
17use llimphi_raster::peniko::{Color, Fill};
18use llimphi_raster::vello::kurbo::{Affine, Rect as KRect};
19use llimphi_test::with_harness;
20
21const W: u32 = 120;
22const H: u32 = 40;
23/// Ancho de celda fraccionario, como el del grid real (la mono a 14 px no da
24/// un avance entero).
25const CELL_W: f64 = 8.4;
26const CELDAS: usize = 12;
27const X0: f64 = 5.0;
28/// Franja de píxeles estrictamente interior al área pintada.
29const INTERIOR: std::ops::RangeInclusive<usize> = 6..=104;
30
31#[derive(Clone, Copy)]
32enum Modo {
33 /// Un `fill` por celda con bordes fraccionarios — cómo estaba.
34 PorCelda,
35 /// Un `fill` por celda con los bordes cuantizados a entero — cómo quedó.
36 Cuantizado,
37 /// El área entera de un solo `fill` — el ideal contra el que comparamos.
38 UnRect,
39}
40
41fn escena(modo: Modo) -> View<()> {
42 View::<()>::new(Style {
43 size: Size { width: percent(1.0_f32), height: percent(1.0_f32) },
44 ..Default::default()
45 })
46 .paint_with(move |scene: &mut llimphi_raster::vello::Scene,
47 _ts: &mut llimphi_text::Typesetter,
48 _r| {
49 let blanco = Color::from_rgba8(255, 255, 255, 255);
50 let (y0, y1): (f64, f64) = (10.0, 30.0);
51 match modo {
52 Modo::PorCelda => {
53 for c in 0..CELDAS {
54 let x = X0 + c as f64 * CELL_W;
55 scene.fill(Fill::NonZero, Affine::IDENTITY, blanco, None,
56 &KRect::new(x, y0, x + CELL_W, y1));
57 }
58 }
59 Modo::Cuantizado => {
60 for c in 0..CELDAS {
61 let xa = (X0 + c as f64 * CELL_W).round();
62 let xb = (X0 + (c + 1) as f64 * CELL_W).round();
63 scene.fill(Fill::NonZero, Affine::IDENTITY, blanco, None,
64 &KRect::new(xa, y0.round(), xb, y1.round()));
65 }
66 }
67 Modo::UnRect => {
68 scene.fill(Fill::NonZero, Affine::IDENTITY, blanco, None,
69 &KRect::new(X0, y0, X0 + CELDAS as f64 * CELL_W, y1));
70 }
71 }
72 })
73}
74
75/// Los píxeles del canal rojo de una fila del frame.
76fn perfil(rgba: &[u8], w: u32, fila: u32) -> Vec<u8> {
77 (0..w).map(|x| rgba[((fila * w + x) * 4) as usize]).collect()
78}
79
80/// `(mínimo, cuántos píxeles hundidos)` en el interior del área pintada. En una
81/// zona sólida blanca, lo sano es `(255, 0)`.
82fn hundidos(rgba: &[u8]) -> (u8, usize) {
83 let p = perfil(rgba, W, 20);
84 let min = INTERIOR.map(|x| p[x]).min().unwrap();
85 let n = INTERIOR.filter(|x| p[*x] < 250).count();
86 (min, n)
87}
88
89#[test]
90fn cuantizar_los_bordes_de_celda_cierra_la_costura() {
91 with_harness(|h| {
92 let negro = Color::from_rgba8(0, 0, 0, 255);
93 let por_celda = hundidos(&h.render(escena(Modo::PorCelda), W, H, negro).rgba);
94 let cuantizado = hundidos(&h.render(escena(Modo::Cuantizado), W, H, negro).rgba);
95 let un_rect = hundidos(&h.render(escena(Modo::UnRect), W, H, negro).rgba);
96
97 // El artefacto existe y es visible: ~0,75 de cobertura en cada junta.
98 assert!(
99 por_celda.1 >= CELDAS - 3 && por_celda.0 < 230,
100 "el fill por celda con bordes fraccionarios debería dejar costura, \
101 y midió min={} hundidos={} — si esto falla, vello dejó de \
102 antialiasar por shape y la cuantización sobra",
103 por_celda.0, por_celda.1
104 );
105 // Cuantizado queda idéntico a pintar el área de un solo rect.
106 assert_eq!(cuantizado, (255, 0), "cuantizado tiene que quedar sólido");
107 assert_eq!(un_rect, (255, 0), "el rect único es el patrón de comparación");
108 });
109}