1extern crate alloc;
3use crate::graphics::color;
4use alloc::vec::Vec;
5
6#[derive(Debug)]
7pub enum BmpError {
8 InvalidSignature,
9 UnsupportedFormat,
10 InvalidData,
11}
12
13pub struct BmpImage {
14 width: u32,
15 height: u32,
16 pixels: Vec<color::Color>,
17}
18
19impl BmpImage {
20 pub fn from_bytes(data: &[u8]) -> Result<Self, BmpError> {
21 if data.len() < 54 || data[0] != b'B' || data[1] != b'M' {
23 return Err(BmpError::InvalidSignature);
24 }
25
26 let width = u32::from_le_bytes([data[18], data[19], data[20], data[21]]);
28 let height = u32::from_le_bytes([data[22], data[23], data[24], data[25]]);
29 let bpp = u16::from_le_bytes([data[28], data[29]]);
30
31 if bpp != 24 && bpp != 32 {
33 return Err(BmpError::UnsupportedFormat);
34 }
35
36 let data_offset = u32::from_le_bytes([data[10], data[11], data[12], data[13]]) as usize;
37
38 let mut pixels = Vec::with_capacity((width * height) as usize);
39 let bytes_per_pixel = (bpp / 8) as usize;
40 let row_padding = (4 - (width * bytes_per_pixel as u32) % 4) % 4;
41
42 for y in 0..height {
44 let row_start = data_offset
45 + ((height - y - 1) * (width * bytes_per_pixel as u32 + row_padding)) as usize;
46
47 for x in 0..width {
48 let pixel_start = row_start + (x * bytes_per_pixel as u32) as usize;
49 if pixel_start + bytes_per_pixel > data.len() {
50 return Err(BmpError::InvalidData);
51 }
52
53 let b = data[pixel_start];
54 let g = data[pixel_start + 1];
55 let r = data[pixel_start + 2];
56 let a = if bytes_per_pixel == 4 {
57 data[pixel_start + 3]
58 } else {
59 255
60 };
61
62 pixels.push(color::Color::with_alpha(r, g, b, a));
63 }
64 }
65
66 Ok(BmpImage {
67 width,
68 height,
69 pixels,
70 })
71 }
72
73 pub fn width(&self) -> u32 {
74 self.width
75 }
76
77 pub fn height(&self) -> u32 {
78 self.height
79 }
80
81 pub fn pixel(&self, x: u32, y: u32) -> Option<color::Color> {
82 if x < self.width && y < self.height {
83 let index = (y * self.width + x) as usize;
84 Some(self.pixels[index])
85 } else {
86 None
87 }
88 }
89}