mirui 0.32.1

A lightweight, no_std ECS-driven UI framework for embedded, desktop, and WebAssembly
Documentation
use super::SwRenderer;
use crate::render::font::{Font, GlyphKind};
use crate::types::{Color, Fixed, Point, Rect};

impl SwRenderer<'_> {
    pub(super) fn draw_label_inner(
        &mut self,
        pos: &Point,
        text: &[u8],
        font: &Font,
        clip: &Rect,
        color: &Color,
        opa: u8,
    ) {
        let phys_pos = self.viewport.point_to_physical(*pos);
        let phys_clip = self.viewport.rect_to_physical(*clip);
        let scale = self.viewport.scale().to_int().max(1);
        let phys_bounds = phys_clip.pixel_bounds();
        let (mut cx, cy) = phys_pos.floor();
        let metrics = font.metrics();
        let char_h = metrics.line_height as i32;
        // Target glyph height in physical pixels — lets a multi-table
        // provider pick the closest fixed-size representation.
        let requested_size = (font.size as i32 * scale).clamp(1, u16::MAX as i32) as u16;
        // Decode the byte stream as UTF-8 so multi-byte CJK glyphs
        // map to one codepoint each. Invalid bytes fall back to U+FFFD,
        // which the font is free to skip.
        let chars = core::str::from_utf8(text)
            .map(|s| s.chars().collect::<alloc::vec::Vec<_>>())
            .unwrap_or_else(|_| text.iter().map(|&b| b as char).collect());
        for ch in chars {
            let Some(g) = font.glyph(ch, requested_size) else {
                continue;
            };
            let advance;
            match &g.kind {
                GlyphKind::Mono(bitmap) => {
                    advance = g.advance as i32 * scale;
                    self.blit_mono_glyph(bitmap, cx, cy, scale, char_h, phys_bounds, color, opa);
                }
                GlyphKind::Sdf {
                    atlas,
                    source_size,
                    bit_depth,
                    spread,
                    ..
                } => {
                    // Render at the Font's requested size, not the atlas
                    // size: one SDF atlas resamples to whatever font.size
                    // asks for, so a larger Font grows the glyph smoothly.
                    // Advance scales by the same resample ratio so the pen
                    // keeps pace; with font.size == source_size this is
                    // g.advance × scale, leaving the common path
                    // byte-identical.
                    advance =
                        g.advance as i32 * requested_size as i32 / (*source_size as i32).max(1);
                    self.blit_sdf_glyph(
                        atlas,
                        *source_size,
                        *bit_depth,
                        *spread,
                        cx,
                        cy,
                        requested_size,
                        phys_bounds,
                        color,
                        opa,
                    );
                }
            }
            cx += advance;
        }
    }

    #[allow(clippy::too_many_arguments)]
    fn blit_mono_glyph(
        &mut self,
        bitmap: &[u8],
        cx: i32,
        cy: i32,
        scale: i32,
        char_h: i32,
        phys_bounds: (i32, i32, i32, i32),
        color: &Color,
        opa: u8,
    ) {
        let (clip_x, clip_y, clip_x2, clip_y2) = phys_bounds;
        for row in 0..char_h.min(bitmap.len() as i32) {
            let byte = bitmap[row as usize];
            for col in 0..8 {
                if byte & (0x80 >> col) == 0 {
                    continue;
                }
                for sy in 0..scale {
                    for sx in 0..scale {
                        let px = cx + col * scale + sx;
                        let py = cy + row * scale + sy;
                        if px >= clip_x && px < clip_x2 && py >= clip_y && py < clip_y2 {
                            self.target.blend_pixel(
                                Fixed::from_int(px),
                                Fixed::from_int(py),
                                color,
                                opa,
                            );
                        }
                    }
                }
            }
        }
    }
}