// ============================================================================= // escena_patron.rs — el árbol de prueba que se pinta por LOS DOS caminos. // ----------------------------------------------------------------------------- // Llimphi tiene dos rasterizadores: `vello` (GPU, lo que corre en Linux) y // `vello_cpu` vía `llimphi-cpu` (lo que corre en el kernel bare-metal de wawa). // El compositor es el MISMO —`paint` emite las mismas órdenes— pero **nadie // había comparado nunca la salida**. O sea que la UI de wawa podía verse // distinta de la de Linux y no había forma de enterarse sin bootear y mirar. // // Este archivo se **incluye con `include!`** desde los dos lados, y ésa no es // una maña sino la única forma: los dos backends de `llimphi-text` son // excluyentes (`cpu` vs `vello`), así que un solo binario no puede linkear los // dos y `llimphi-wawa` vive en un workspace aparte justamente por eso. Un // archivo compartido es lo que garantiza que las dos corridas pinten el mismo // árbol y no dos árboles parecidos. // // Sólo se usa la API de `llimphi-compositor`, que compila igual en los dos // perfiles. Los dos sitios que lo incluyen corren en el anfitrión (el volcador // de wawa con `--features std`), así que `Vec` de `std` está bien: lo que se // comparte es el ÁRBOL, no el perfil sin sistema operativo. // ============================================================================= use llimphi_compositor::llimphi_layout::taffy::prelude::{ length, percent, FlexDirection, Size, Style, }; use llimphi_compositor::llimphi_layout::taffy::Rect as Insets; use llimphi_compositor::peniko::Color; use llimphi_compositor::{Shadow, View}; /// Tamaño de todos los patrones. Chico a propósito: lo que se compara es el /// **rasterizado**, y un lienzo grande sólo multiplica los bytes del volcado. pub const ANCHO: u32 = 200; pub const ALTO: u32 = 150; fn caja(w: f32, h: f32) -> View { View::::new(Style { size: Size { width: length(w), height: length(h) }, ..Default::default() }) } fn raiz(hijos: Vec>) -> View { View::::new(Style { flex_direction: FlexDirection::Column, size: Size { width: percent(1.0_f32), height: percent(1.0_f32) }, gap: Size { width: length(0.0_f32), height: length(6.0_f32) }, padding: Insets { left: length(8.0_f32), right: length(8.0_f32), top: length(8.0_f32), bottom: length(8.0_f32), }, ..Default::default() }) .fill(Color::from_rgba8(18, 18, 24, 255)) .children(hijos) } /// Los patrones, **separados por rasgo**. Que vayan sueltos y no todos en un /// cuadro es deliberado: si los dos caminos divergen, el número por patrón dice /// **en qué** divergen. Un solo número global diría «difieren» y nada más. pub fn patrones() -> Vec<(&'static str, View)> { let mut v = Vec::new(); // 1. Rectángulos alineados a la grilla: sin AA de por medio. Si esto ya // difiere, el problema no es el antialiasing sino el color o el volcado. v.push(( "rects", raiz(vec![ caja(80.0, 30.0).fill(Color::from_rgba8(220, 60, 60, 255)), caja(120.0, 25.0).fill(Color::from_rgba8(60, 200, 120, 255)), caja(40.0, 40.0).fill(Color::from_rgba8(80, 120, 240, 255)), ]), )); // 2. Esquinas redondeadas y borde: acá sí entra el antialiasing, que es // donde dos rasterizadores distintos tienen todo el derecho a diferir. v.push(( "radios", raiz(vec![ caja(90.0, 40.0) .fill(Color::from_rgba8(200, 200, 220, 255)) .radius(12.0), caja(90.0, 40.0) .fill(Color::from_rgba8(40, 40, 60, 255)) .radius(6.0) .border(2.0, Color::from_rgba8(240, 180, 60, 255)), ]), )); // 3. Alpha y capas: `alpha` abre una capa en los dos backends, y componer // capas es donde una diferencia de orden se nota entera. v.push(( "alpha", raiz(vec![ caja(150.0, 50.0) .fill(Color::from_rgba8(240, 80, 40, 255)) .alpha(0.5) .children(vec![caja(60.0, 30.0) .fill(Color::from_rgba8(40, 240, 200, 255))]), caja(150.0, 40.0).fill(Color::from_rgba8(90, 90, 240, 128)), ]), )); // 4. Sombra: un blur, o sea el rasgo con más aritmética de los cuatro. v.push(( "sombra", raiz(vec![caja(110.0, 60.0) .fill(Color::from_rgba8(230, 230, 240, 255)) .radius(10.0) .shadow(Shadow { color: Color::from_rgba8(0, 0, 0, 160), blur: 10.0, dx: 3.0, dy: 4.0, spread: 0.0, })]), )); // 5. Texto. La fuente va embebida (`include_bytes!` en `llimphi-text`), así // que los dos caminos shapean con el MISMO archivo y lo que quede es // diferencia de rasterizado de glifos, no de tipografía. v.push(( "texto", raiz(vec![ View::::new(Style::default()).text( "Hola wawa — jgpqy", 18.0, Color::from_rgba8(240, 240, 250, 255), ), View::::new(Style::default()).text( "renglón chico 0123456789", 11.0, Color::from_rgba8(180, 200, 240, 255), ), ]), )); // 6. **La escalera de tamaños de texto.** Existe para decidir una pregunta // concreta: la divergencia del texto entre los dos rasterizadores, ¿es // de la ORLA o es SISTEMÁTICA? // // La tinta de un glifo crece con el área (≈ s²) y su borde con el // perímetro (≈ s). Si la diferencia viene sólo del antialiasing del // contorno, su peso relativo tiene que caer como 1/s al agrandar la // letra. Si en cambio se mantiene, no es el borde: es que un lado pinta // la cobertura distinto en todo el glifo, y eso sí sería un problema. // // Una sola letra por patrón, y con curvas: menos glifos, menos ruido. for tam in [11.0f32, 18.0, 48.0, 96.0] { v.push(( match tam as u32 { 11 => "texto_11", 18 => "texto_18", 48 => "texto_48", _ => "texto_96", }, raiz(vec![View::::new(Style::default()).text( "Ooo", tam, Color::from_rgba8(255, 255, 255, 255), )]), )); } // 7. Texto separado por REPERTORIO, no por tamaño: ASCII puro contra // caracteres que pueden no estar en las fuentes embebidas. for (nombre, cadena) in [ ("texto_ascii", "Hamburgefonstiv 0123"), ("texto_acentos", "renglon ñáéíóú"), ("texto_raya", "guion — largo"), ] { v.push(( nombre, raiz(vec![View::::new(Style::default()).text( cadena, 18.0, Color::from_rgba8(255, 255, 255, 255), )]), )); } v }