tawasuyu

tawasuyu/llimphi

Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.

Rust 229TOML 101Markdown 82Shell 24052 Sutichaykuna

⎇ main llimphi-test/tests/costura_celdas.rs Rust · 109 siq'ikuna · 4.5 KB Ñawpa willay Pi imata llamkarqan ch'uya

chore: refresco desde el monorepo — vello 0.9 / wgpu 29, 120 miembros d251eb32b · Sergio ·

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}