use embedded_graphics::{geometry::Size, pixelcolor::Rgb565, primitives::Rectangle, prelude::Point}; use embedded_graphics_core::draw_target::DrawTarget; use crate::display::palette::Rgb; pub fn render_sprite_onto_ili9341(ili9341: &mut D, sprite: &[u8], palette: &[Rgb; 16]) where D: DrawTarget, { let pixel_count = sprite.len() * 2; if pixel_count == 0 { return; } // OPTIMIZATION 1: Pre-compute the 16-color palette to Rgb565 once. let mut palette565 = [Rgb565::new(0, 0, 0); 16]; for (i, Rgb(r, g, b)) in palette.iter().enumerate() { palette565[i] = Rgb565::new(*r >> 3, *g >> 2, *b >> 3); } let mut src_width = 1usize; while src_width * src_width < pixel_count { src_width += 1; } let target_width = 240usize; let scale = core::cmp::max(1, target_width / src_width); for y in 0..src_width { for x in 0..src_width { let pixel_index = y * src_width + x; if pixel_index >= pixel_count { break; } // Bitwise operations are slightly faster than division/modulo let byte_index = pixel_index >> 1; let pixel_byte = sprite[byte_index]; let color_index = if pixel_index.is_multiple_of(2) { (pixel_byte >> 4) & 0x0F } else { pixel_byte & 0x0F }; let color = palette565[color_index as usize]; let start_x = (x * scale) as i32; let start_y = (y * scale) as i32; let end_x = core::cmp::min(start_x + scale as i32, target_width as i32); let end_y = core::cmp::min(start_y + scale as i32, target_width as i32); let width = u32::try_from(end_x - start_x).unwrap(); let height = u32::try_from(end_y - start_y).unwrap(); let area = Rectangle::new( Point::new(start_x.try_into().unwrap(), start_y.try_into().unwrap()), Size::new(width, height), ); let _ = ili9341.fill_solid(&area, color); } } }