1pub mod iterators;
18pub mod pixel;
19
20use std::path::Path;
21
22use crate::{CoreError, Result, drawing::traits::Drawable};
23use image::{ImageBuffer, ImageReader, Rgba as ImageRgba};
24use minifb::{Key, Window, WindowOptions};
25use pixel::{Pixel, Rgba};
26
27pub struct Image<P: Pixel> {
30 width: usize,
31 height: usize,
32 data: Vec<P>,
33}
34
35impl<P> Image<P>
36where
37 P: Pixel,
38{
39 pub fn new(width: usize, height: usize) -> Self {
41 Image {
42 width,
43 height,
44 data: vec![P::from_rgba8([0, 0, 0, 0]).unwrap(); width * height],
45 }
46 }
47
48 pub fn open<Pth: AsRef<Path>>(path: Pth) -> Result<Self> {
50 let image = ImageReader::open(path)?.decode()?.to_rgba8();
51 let (width, height) = image.dimensions();
52 let width = width as usize;
53 let height = height as usize;
54
55 let data: Result<Vec<P>> = image.pixels().map(|p| P::from_rgba8(p.0)).collect();
56 let data = data?;
57
58 Ok(Image {
59 width,
60 height,
61 data,
62 })
63 }
64
65 pub fn save<Pth: AsRef<Path>>(&self, path: Pth) -> Result<()> {
68 let rgba8_data: Vec<[u8; 4]> = self.data.iter().map(|pixel| pixel.to_rgba8()).collect();
69 let rgba8_bytes: Vec<u8> = rgba8_data.iter().flatten().copied().collect();
70
71 let buffer = ImageBuffer::<ImageRgba<u8>, _>::from_raw(
72 self.width as u32,
73 self.height as u32,
74 rgba8_bytes,
75 )
76 .ok_or_else(|| std::io::Error::other("Invalid buffer"))?;
77 buffer.save(path)?;
78
79 Ok(())
80 }
81
82 pub fn display(&self, title: &str) -> Result<()> {
84 let (width, height) = self.dimensions();
85
86 let mut window = Window::new(
88 title,
89 width,
90 height,
91 WindowOptions {
92 resize: false,
93 ..Default::default()
94 },
95 )?;
96 window.set_target_fps(30);
97
98 let rgba8_data: Vec<[u8; 4]> = self.data.iter().map(|px| px.to_rgba8()).collect();
100
101 let mut buffer: Vec<u32> = Vec::with_capacity(rgba8_data.len());
102 for pixel in rgba8_data.iter() {
103 buffer.push(u32::from_be_bytes([pixel[3], pixel[0], pixel[1], pixel[2]]));
104 }
105
106 while window.is_open() && !window.is_key_down(Key::Escape) {
107 window.update_with_buffer(&buffer, width, height)?;
108 }
109
110 Ok(())
111 }
112
113 pub fn get_pixel(&self, position: (usize, usize)) -> Result<&P> {
116 let idx = position.1 * self.width + position.0;
117 self.data.get(idx).ok_or_else(|| {
118 CoreError::OutOfBounds(format!(
119 "{:#?} is out of bounds for image of size {:#?}",
120 position,
121 self.dimensions()
122 ))
123 })
124 }
125
126 pub fn set_pixel(&mut self, position: (usize, usize), color: P) -> Result<()> {
130 let idx = position.1 * self.width + position.0;
131 if let Some(px) = self.data.get_mut(idx) {
132 *px = color;
133 }
134 Ok(())
135 }
136
137 pub fn draw<D: Drawable<P>>(&mut self, shape: D) -> Result<()> {
139 shape.draw_on(self)?;
140 Ok(())
141 }
142
143 pub fn dimensions(&self) -> (usize, usize) {
145 (self.width, self.height)
146 }
147
148 pub fn is_empty(&self) -> bool {
150 self.data.is_empty()
151 }
152
153 pub fn to_rgba8(&self) -> Image<Rgba<u8>> {
155 let rgba_data: Vec<[u8; 4]> = self.data.iter().map(|px| px.to_rgba8()).collect();
156 let rgba_data: Vec<Rgba<u8>> = rgba_data
157 .into_iter()
158 .map(|rgba| Rgba {
159 r: rgba[0],
160 g: rgba[1],
161 b: rgba[2],
162 a: rgba[3],
163 })
164 .collect();
165
166 Image {
167 width: self.width,
168 height: self.height,
169 data: rgba_data,
170 }
171 }
172}