aura-gui 0.3.5

UI nativa acelerada por GPU para escritorio, Android e iOS, en un solo crate: estilo tipo Tailwind, componentes, sombras, animaciones y agentes de IA. Reposo a cero. Licencia AGPL-3.0 o comercial.
//! Texto nítido en pantallas de densidad normal (Windows y Linux a 100 %, monitores externos de macOS).
//!
//! Con 2× o más cada glifo tiene píxeles de sobra. Con 1× los trazos finos caen en medio píxel: se ven grises y
//! rotos. Dos ajustes baratos, aplicados una sola vez por glifo al rasterizarlo (no por fotograma):
//! - Posición entera en X: el trazo vertical cae sobre una columna de píxeles en vez de repartirse en dos.
//! - Refuerzo de cobertura (gamma): los bordes parciales pesan más, como el oscurecimiento de trazos de CoreText.

use cosmic_text::SubpixelBin;
use std::sync::OnceLock;

/// Por debajo de esta escala se aplican los ajustes.
const CRISP_BELOW: f32 = 2.0;
/// Exponente de la curva de cobertura: `cobertura^(1/GAMMA)`; 1 = sin cambio.
const GAMMA: f32 = 1.45;

pub(crate) fn is_crisp(scale: f32) -> bool {
    scale < CRISP_BELOW
}

/// Redondea la posición X al píxel entero más cercano; devuelve el desplazamiento a sumar a `x`.
pub(crate) fn snap_x(bin: &mut SubpixelBin) -> i32 {
    let carry = i32::from(matches!(bin, SubpixelBin::Two | SubpixelBin::Three));
    *bin = SubpixelBin::Zero;
    carry
}

/// Refuerza la cobertura de una máscara de 8 bits en su sitio.
pub(crate) fn boost(mask: &mut [u8]) {
    static LUT: OnceLock<[u8; 256]> = OnceLock::new();
    let lut = LUT.get_or_init(|| {
        std::array::from_fn(|i| ((i as f32 / 255.0).powf(1.0 / GAMMA) * 255.0).round().clamp(0.0, 255.0) as u8)
    });
    for value in mask {
        *value = lut[*value as usize];
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn solo_las_pantallas_de_densidad_normal_se_ajustan() {
        assert!(is_crisp(1.0) && is_crisp(1.5));
        assert!(!is_crisp(2.0) && !is_crisp(3.0));
    }

    #[test]
    fn la_posicion_se_redondea_al_pixel_mas_cercano() {
        let casos = [(SubpixelBin::Zero, 0), (SubpixelBin::One, 0), (SubpixelBin::Two, 1), (SubpixelBin::Three, 1)];
        for (mut bin, carry) in casos {
            assert_eq!(snap_x(&mut bin), carry);
            assert_eq!(bin, SubpixelBin::Zero);
        }
    }

    #[test]
    fn el_refuerzo_conserva_los_extremos_y_sube_los_medios_tonos() {
        let mut mask = [0u8, 64, 128, 255];
        boost(&mut mask);
        assert_eq!((mask[0], mask[3]), (0, 255));
        assert!(mask[1] > 64 && mask[2] > 128);
        assert!(mask.windows(2).all(|pair| pair[0] < pair[1]), "sigue siendo monótona");
    }
}