rustedbytes-bytepusher 0.2.1

A complete BytePusher virtual machine implementation in Rust
Documentation
use std::{cell::RefCell, rc::Rc};

use crate::{
    memory::Memory,
    vm::{SCREEN_BUFFER_SIZE, SCREEN_HEIGHT, SCREEN_WIDTH},
};
use image::{ImageBuffer, Rgb};

pub struct ScreenHandler {
    pub palette: [u32; 256],
    pub memory_register_addr: usize,
    pub memory: Rc<RefCell<Memory>>,
    pub window: Rc<RefCell<minifb::Window>>,
}

impl ScreenHandler {
    pub fn new(
        memory_register_addr: usize,
        memory: Rc<RefCell<Memory>>,
        window: Rc<RefCell<minifb::Window>>,
    ) -> Self {
        let palette = Self::init_palette();

        Self {
            palette,
            memory_register_addr,
            memory,
            window,
        }
    }

    pub fn render_frame(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        let new_frame = self.get_screen_buffer();

        let mut screen = [0u32; SCREEN_BUFFER_SIZE];

        screen
            .iter_mut()
            .zip(new_frame.iter())
            .for_each(|(screen_pixel, &frame_pixel)| {
                *screen_pixel = self.palette[frame_pixel as usize];
            });

        self.window
            .borrow_mut()
            .update_with_buffer(&screen, SCREEN_WIDTH, SCREEN_HEIGHT)?;

        Ok(())
    }

    pub fn save_screen_png(&self, file_index: u32) -> Result<(), Box<dyn std::error::Error>> {
        let buffer = self.get_screen_buffer();
        let mut img_buf = ImageBuffer::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32);
        for (i, pixel) in buffer.iter().enumerate() {
            let color = self.palette[*pixel as usize];
            let r = ((color >> 16) & 0xFF) as u8;
            let g = ((color >> 8) & 0xFF) as u8;
            let b = (color & 0xFF) as u8;
            let x = (i % SCREEN_WIDTH) as u32;
            let y = (i / SCREEN_WIDTH) as u32;
            img_buf.put_pixel(x, y, Rgb([r, g, b]));
        }
        let filename = format!("screenshot_{:04}.png", file_index);
        img_buf.save(&filename)?;
        Ok(())
    }

    fn init_palette() -> [u32; 256] {
        let mut palette: [u32; 256] = [0; 256];
        for (idx, val) in palette.iter_mut().enumerate() {
            if idx >= 216 {
                break;
            }
            *val = ((idx as u32 / 36 * 0x33) << 16)
                | ((idx as u32 / 6 % 6 * 0x33) << 8)
                | (idx as u32 % 6 * 0x33);
        }
        palette
    }

    fn get_screen_buffer(&self) -> [u8; SCREEN_BUFFER_SIZE] {
        let mem = self.memory.borrow();
        let graphics_addr = (mem[self.memory_register_addr] as usize) << 16;
        let new_frame = &mem[graphics_addr..graphics_addr + SCREEN_BUFFER_SIZE];
        let mut arr = [0u8; SCREEN_BUFFER_SIZE];
        arr.copy_from_slice(new_frame);
        arr
    }
}