launchkey_sdk/launchkey/
bitmap.rs

1use image::DynamicImage;
2
3/// Width of the Launchkey MK4 display in pixels.
4const DISPLAY_WIDTH: u32 = 128;
5
6/// Height of the Launchkey MK4 display in pixels.
7const DISPLAY_HEIGHT: u32 = 64;
8
9/// Number of bytes per bitmap for the Launchkey MK4.
10const BITMAP_SIZE: usize = 1216;
11
12/// Number of bytes per row (128px / 7 bits per byte = ~18.285 ≈ 19).
13const BYTES_PER_ROW: u32 = 19;
14
15/// Represents a 128x64 monochrome bitmap for Launchkey MK4 displays
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct LaunchkeyBitmap([u8; BITMAP_SIZE]);
18
19impl LaunchkeyBitmap {
20    /// Creates a new bitmap from a 1216-byte array, where each byte encodes pixels in a specific format.
21    pub fn new(data: [u8; BITMAP_SIZE]) -> Self {
22        Self(data)
23    }
24
25    /// Converts a full image to a Launchkey-compatible 128x64 monochrome bitmap using a brightness threshold.
26    pub fn from_image(img: DynamicImage, threshold: u8) -> Self {
27        let img = img.resize_exact(DISPLAY_WIDTH, DISPLAY_HEIGHT, image::imageops::Nearest);
28        let img = img.to_luma8();
29
30        let mut data = [0u8; BITMAP_SIZE];
31
32        for y in 0..DISPLAY_HEIGHT {
33            for x in 0..DISPLAY_WIDTH {
34                let pixel = img.get_pixel(x, y)[0];
35                if pixel > threshold {
36                    let byte_index = ((y * BYTES_PER_ROW) + (x / 7)) as usize;
37                    let bit_offset = 6 - (x % 7); // Leftmost pixel = highest bit (bit 7)
38                    data[byte_index] |= 1 << bit_offset;
39                }
40            }
41        }
42
43        Self(data)
44    }
45}
46
47impl TryFrom<&[u8]> for LaunchkeyBitmap {
48    type Error = String;
49
50    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
51        if value.len() != BITMAP_SIZE {
52            Err(format!("Invalid length: {} (must be {})", value.len(), BITMAP_SIZE))
53        } else {
54            let mut buffer = [0u8; BITMAP_SIZE];
55            buffer.copy_from_slice(value);
56            Ok(Self(buffer))
57        }
58    }
59}
60
61impl From<LaunchkeyBitmap> for [u8; BITMAP_SIZE] {
62    fn from(val: LaunchkeyBitmap) -> Self {
63        val.0
64    }
65}
66
67impl AsRef<[u8; BITMAP_SIZE]> for LaunchkeyBitmap {
68    fn as_ref(&self) -> &[u8; BITMAP_SIZE] {
69        &self.0
70    }
71}