msla_format 0.2.0

Library for encoding and decoding various MSLA file formats: Elegoo (.goo), Chitu Encrypted (.ctb), NanoDLP (.nanodlp).
Documentation
use std::{
    fmt::{self, Debug},
    iter::repeat_n,
};

use anyhow::Result;

use crate::serde::{Deserializer, Serializer, SliceDeserializer};
use image::RgbaImage;
use nalgebra::{Vector2, Vector3};

use crate::ctb::Section;

/// RGB bitmap image.
#[derive(Default)]
pub struct PreviewImage {
    width: u32,
    data: Vec<Vector3<u8>>,
}

impl PreviewImage {
    pub fn from_image(image: &RgbaImage) -> Self {
        let mut data = Vec::with_capacity((image.width() * image.height()) as usize);
        for color in image.chunks(4) {
            data.push(Vector3::new(color[0], color[1], color[2]));
        }

        Self {
            width: image.width(),
            data,
        }
    }

    pub fn inner_data(&self) -> &[Vector3<u8>] {
        &self.data
    }

    pub fn size(&self) -> Vector2<u32> {
        Vector2::new(self.width, (self.data.len() / self.width as usize) as u32)
    }

    pub fn get_pixel(&self, x: u32, y: u32) -> Vector3<u8> {
        let index = y * self.width + x;
        self.data[index as usize]
    }

    pub fn set_pixel(&mut self, x: u32, y: u32, rgb: Vector3<u8>) {
        let index = y * self.width + x;
        self.data[index as usize] = rgb;
    }
}

impl PreviewImage {
    pub(crate) fn deserialize(des: &mut SliceDeserializer) -> Result<Self> {
        let width = des.read_u32_le();
        let height = des.read_u32_le();
        let image = Section::deserialize(des)?;

        des.jump_to(image.offset as usize);
        PreviewImage::from_bytes(
            des.read_slice(image.size as usize),
            Vector2::new(width, height),
        )
    }

    pub(crate) fn serialize<T: Serializer>(&self, ser: &mut T) {
        let size = self.size();
        ser.write_u32_le(size.x);
        ser.write_u32_le(size.y);
        let section = ser.reserve(8);

        let data = self.to_bytes();
        let offset = ser.pos();
        ser.write_bytes(&data);
        ser.execute_at(section, |ser| {
            Section::new(offset, data.len()).serialize(ser)
        });
    }

    fn from_bytes(bytes: &[u8], size: Vector2<u32>) -> Result<Self> {
        let pixels = size.x as usize * size.y as usize;
        let mut data = Vec::with_capacity(pixels);

        let mut i = 0;
        while i < bytes.len() {
            let run = u16::from_le_bytes([bytes[i], bytes[i + 1]]);
            i += 2;

            let red = (((run >> 11) & 0x1F) << 3) as u8;
            let green = (((run >> 6) & 0x1F) << 3) as u8;
            let blue = ((run & 0x1F) << 3) as u8;

            let mut count = 1;
            if run & 0x20 != 0 {
                count += (bytes[i + 1] as u16 & 0x0F) << 8 | bytes[i] as u16;
                i += 2;
            }

            data.extend(repeat_n(Vector3::new(red, green, blue), count as usize));
        }

        if data.len() < pixels {
            data.resize(pixels, Vector3::zeros());
        }

        Ok(Self {
            width: size.x,
            data,
        })
    }

    fn to_bytes(&self) -> Vec<u8> {
        let mut out = Vec::new();
        let mut last_color = self.data[0];
        let mut length = 0;

        let mut add_run = |length: u32, color: Vector3<u8>| {
            let color = ((color.z >> 3) as u16)
                | ((color.y >> 2) as u16) << 5
                | ((color.x >> 3) as u16) << 11;

            match length {
                0 => {}
                x @ 1..=2 => {
                    let value = color & !0x20;
                    out.extend(repeat_n([value as u8, (value >> 8) as u8], x as usize).flatten());
                }
                x => {
                    let value = color | 0x20;
                    out.extend([value as u8, (value >> 8) as u8]);
                    let value = (x - 1) | 0x3000;
                    out.extend([value as u8, (value >> 8) as u8]);
                }
            }
        };

        for pixel in self.data.iter() {
            if *pixel == last_color {
                length += 1;
                if length == 0xFFF {
                    add_run(length, last_color);
                    length = 0;
                }
            } else {
                add_run(length, last_color);
                last_color = *pixel;
                length = 1;
            }
        }

        add_run(length, last_color);

        out
    }
}

impl Debug for PreviewImage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let size = self.size();
        f.debug_struct("PreviewImage")
            .field("width", &self.width)
            .field("height", &size.y)
            .finish()
    }
}