1use crate::prelude::{Color, FilterType};
2use image::{DynamicImage, ImageBuffer, Rgba, RgbaImage};
3
4#[derive(Clone, Default, Debug)]
9pub struct Image {
10 pub width: u32,
11 pub height: u32,
12 pub data: Vec<u8>,
13 pub filter_type: FilterType,
14}
15
16impl Image {
17 pub fn new(width: u32, height: u32, filter_type: FilterType) -> Self {
19 Self {
20 width,
21 height,
22 data: vec![0; 4 * width as usize * height as usize],
23 filter_type,
24 }
25 }
26
27 pub fn from_data(width: u32, height: u32, filter_type: FilterType, data: Vec<u8>) -> Self {
29 debug_assert_eq!(
30 data.len(),
31 4 * width as usize * height as usize,
32 "Data buffer size does not match width * height * 4"
33 );
34
35 Self {
36 width,
37 height,
38 data,
39 filter_type,
40 }
41 }
42
43 pub fn from_pixels(
48 width: u32,
49 height: u32,
50 filter_type: FilterType,
51 pixels: Vec<Color>,
52 ) -> Self {
53 debug_assert_eq!(
54 pixels.len(),
55 width as usize * height as usize,
56 "Pixel buffer size does not match width * height"
57 );
58
59 let mut data = Vec::with_capacity(pixels.len() * 4);
60
61 for color in pixels {
62 let [r, g, b, a] = color.to_array();
63
64 data.extend_from_slice(&[
65 (r.clamp(0.0, 1.0) * 255.0) as u8,
66 (g.clamp(0.0, 1.0) * 255.0) as u8,
67 (b.clamp(0.0, 1.0) * 255.0) as u8,
68 (a.clamp(0.0, 1.0) * 255.0) as u8,
69 ]);
70 }
71
72 Self {
73 width,
74 height,
75 data,
76 filter_type,
77 }
78 }
79
80 pub fn with_filter_type(mut self, filter_type: FilterType) -> Self {
82 self.filter_type = filter_type;
83 self
84 }
85
86 #[inline]
88 fn pixel_index(&self, x: u32, y: u32) -> usize {
89 ((y * self.width + x) * 4) as usize
90 }
91
92 pub fn set_pixel(&mut self, x: u32, y: u32, color: Color) {
96 if x < self.width && y < self.height {
97 let idx = self.pixel_index(x, y);
98 let [r, g, b, a] = color.to_array();
99
100 self.data[idx] = (r.clamp(0.0, 1.0) * 255.0) as u8;
101 self.data[idx + 1] = (g.clamp(0.0, 1.0) * 255.0) as u8;
102 self.data[idx + 2] = (b.clamp(0.0, 1.0) * 255.0) as u8;
103 self.data[idx + 3] = (a.clamp(0.0, 1.0) * 255.0) as u8;
104 }
105 }
106
107 pub fn get_pixel(&self, x: u32, y: u32) -> Option<Color> {
112 if x < self.width && y < self.height {
113 let idx = self.pixel_index(x, y);
114
115 Some(Color::new(
116 self.data[idx] as f32 / 255.0,
117 self.data[idx + 1] as f32 / 255.0,
118 self.data[idx + 2] as f32 / 255.0,
119 self.data[idx + 3] as f32 / 255.0,
120 ))
121 } else {
122 None
123 }
124 }
125
126 pub fn clear(&mut self, color: [f32; 4]) {
130 let color = [
131 (color[0].clamp(0.0, 1.0) * 255.0) as u8,
132 (color[1].clamp(0.0, 1.0) * 255.0) as u8,
133 (color[2].clamp(0.0, 1.0) * 255.0) as u8,
134 (color[3].clamp(0.0, 1.0) * 255.0) as u8,
135 ];
136
137 for chunk in self.data.chunks_exact_mut(4) {
138 chunk.copy_from_slice(&color);
139 }
140 }
141
142 pub fn to_dynamic(&self) -> DynamicImage {
144 let buffer = ImageBuffer::<Rgba<u8>, Vec<u8>>::from_raw(
145 self.width,
146 self.height,
147 self.data.clone(),
148 )
149 .expect("Failed to create ImageBuffer from Image data");
150
151 DynamicImage::ImageRgba8(buffer)
152 }
153
154 pub fn from_dynamic(dynamic: &DynamicImage, filter_type: FilterType) -> Self {
158 let rgba = dynamic.to_rgba8();
159 let width = rgba.width();
160 let height = rgba.height();
161 let data = rgba.into_raw();
162
163 Self {
164 width,
165 height,
166 data,
167 filter_type,
168 }
169 }
170
171 pub fn to_rgba8_buffer(&self) -> RgbaImage {
175 ImageBuffer::from_raw(self.width, self.height, self.data.clone())
176 .expect("Failed to create RGBA8 ImageBuffer")
177 }
178}