gioser

tawasuyu/llimphi

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

Rust 124Markdown 82TOML 78Shell 12149 Definiciones

⎇ 168cd187519c0ad575d3ce63baee2f164c38d9d1 llimphi-cpu/src/lienzo.rs Rust · 87 líneas · 3.6 KB Historia Quién tocó qué crudo

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

1// =============================================================================
2//  lienzo :: del `Pixmap` al framebuffer de verdad
3// -----------------------------------------------------------------------------
4//  El último tramo, y el más tonto de todos — pero el que decide si se ve algo.
5//  `vello_cpu` entrega un `Pixmap` de `PremulRgba8` COMPACTO (ancho*alto, sin
6//  relleno). Un framebuffer real casi nunca es así: tiene un `stride` mayor que
7//  el ancho visible (la GPU alinea cada fila) y el orden de canales lo dicta el
8//  firmware, no nosotros — el GOP de UEFI reporta `Bgr` tan seguido como `Rgb`.
9//
10//  Por eso el volcado no es un `copy_from_slice`: es fila por fila, con
11//  conversión de canal. Se hace en el orden de memoria del destino para que el
12//  acceso sea secuencial; en un framebuffer sin caché de escritura combinada
13//  (el WC que el kernel programa en el PAT) saltar de acá para allá se paga muy
14//  caro.
15// =============================================================================
16
17use vello_cpu::Pixmap;
18
19/// Cómo espera los canales el framebuffer de destino.
20///
21/// Sólo los dos ordenamientos de 32 bits que aparecen en la práctica: es lo que
22/// reporta el GOP de UEFI (`PixelFormat::{Rgb, Bgr}`) y lo que ofrece virtio-gpu.
23/// Los modos de 24 bits y los paletizados no se contemplan — ningún camino de
24/// wawa los produce.
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum FormatoPixel {
27    /// Byte 0 = azul, 1 = verde, 2 = rojo, 3 = ignorado. Lo más común en x86.
28    Bgra8,
29    /// Byte 0 = rojo, 1 = verde, 2 = azul, 3 = ignorado.
30    Rgba8,
31}
32
33/// Vuelca un `Pixmap` sobre un framebuffer crudo.
34///
35/// - `destino`: los bytes del framebuffer, tal como los mapeó el kernel.
36/// - `stride_bytes`: cuántos bytes hay de una fila a la siguiente. **No** es
37///   `ancho * 4`: el firmware suele pedir más. Pasarlo mal produce la imagen
38///   inclinada en diagonal, que es el síntoma clásico de confundir stride con
39///   ancho.
40/// - `formato`: el orden de canales del destino.
41///
42/// Recorta a lo que entre: si el pixmap es más grande que el framebuffer, se
43/// copia la intersección en vez de entrar en pánico. Un kernel no debe morir
44/// porque una ventana quedó más ancha que la pantalla.
45///
46/// Devuelve cuántas filas se copiaron de verdad.
47pub fn volcar(
48    pixmap: &Pixmap,
49    destino: &mut [u8],
50    stride_bytes: usize,
51    formato: FormatoPixel,
52) -> usize {
53    let ancho = pixmap.width() as usize;
54    let alto = pixmap.height() as usize;
55    if ancho == 0 || alto == 0 || stride_bytes < 4 {
56        return 0;
57    }
58
59    let origen = pixmap.data();
60    // Cuántos píxeles de la fila caben en el destino, y cuántas filas hay.
61    let px_por_fila = ancho.min(stride_bytes / 4);
62    let filas = alto.min(destino.len() / stride_bytes);
63
64    for y in 0..filas {
65        let fila_orig = &origen[y * ancho..y * ancho + px_por_fila];
66        let base = y * stride_bytes;
67        let fila_dest = &mut destino[base..base + px_por_fila * 4];
68        for (px, dest) in fila_orig.iter().zip(fila_dest.chunks_exact_mut(4)) {
69            // El pixmap ya viene premultiplicado y el framebuffer es opaco, así
70            // que el alfa se descarta: no hay nada debajo con qué componer.
71            match formato {
72                FormatoPixel::Bgra8 => {
73                    dest[0] = px.b;
74                    dest[1] = px.g;
75                    dest[2] = px.r;
76                }
77                FormatoPixel::Rgba8 => {
78                    dest[0] = px.r;
79                    dest[1] = px.g;
80                    dest[2] = px.b;
81                }
82            }
83            dest[3] = 255;
84        }
85    }
86    filas
87}