1use binrw::{BinRead, BinResult, BinWrite, VecArgs, binrw};
2use getset::{Getters, Setters};
3use image::{DynamicImage, GenericImageView, ImageBuffer};
4
5use crate::{ColorId, PixelEndian};
6
7#[binrw]
9#[br(map(i32::into))]
10#[bw(map(i32::from))]
11#[derive(Debug, Clone)]
12pub enum PixelDepth {
13 U8(i32),
14 U16(i32),
15}
16
17#[derive(Clone)]
23pub enum Pixels {
24 Rgb8(Vec<u8>),
25 Rgb16(Vec<u16>),
26 Luma8(Vec<u8>),
27 Luma16(Vec<u16>),
28}
29
30#[derive(Clone)]
32pub struct Frame((u32, u32), Pixels);
33
34#[derive(Clone, Getters, Setters)]
43#[getset(get = "pub", set = "pub")]
44pub struct FrameFormat {
45 color: ColorId,
46 depth: PixelDepth,
47 endian: PixelEndian,
48 width: u32,
49 height: u32,
50}
51
52#[derive(Debug, Clone)]
53enum PixelChannels {
54 Rgb,
55 Luma,
56}
57
58impl FrameFormat {
59 pub fn new(
60 color: ColorId,
61 depth: PixelDepth,
62 endian: PixelEndian,
63 width: u32,
64 height: u32,
65 ) -> Self {
66 Self {
67 color,
68 depth,
69 endian,
70 width,
71 height,
72 }
73 }
74
75 pub fn raw_len(&self) -> usize {
78 let channels: PixelChannels = (&self.color).into();
79 channels.len() * self.width as usize * self.height as usize
80 }
81
82 pub fn try_into_frame(&self, img: DynamicImage) -> Result<Frame, &'static str> {
85 if img.width() != self.width || img.height() != self.height {
86 return Err(
87 "Incompatible image dimensions. All frames must have the same width and height.",
88 );
89 }
90
91 let pixels = match ((&self.color).into(), &self.depth) {
92 (PixelChannels::Luma, PixelDepth::U8(_)) => Pixels::Luma8(img.to_luma8().into_raw()),
93 (PixelChannels::Luma, PixelDepth::U16(_)) => Pixels::Luma16(img.to_luma16().into_raw()),
94 (PixelChannels::Rgb, PixelDepth::U8(_)) => Pixels::Rgb8(img.to_rgb8().into_raw()),
95 (PixelChannels::Rgb, PixelDepth::U16(_)) => Pixels::Rgb16(img.to_rgb16().into_raw()),
96 };
97 Ok(Frame((self.width, self.height), pixels))
98 }
99}
100
101impl PixelChannels {
102 fn len(&self) -> usize {
103 match self {
104 PixelChannels::Rgb => 3,
105 PixelChannels::Luma => 1,
106 }
107 }
108}
109
110impl Pixels {
111 fn len(&self) -> usize {
112 match self {
113 Pixels::Rgb8(items) => items.len(),
114 Pixels::Rgb16(items) => items.len(),
115 Pixels::Luma8(items) => items.len(),
116 Pixels::Luma16(items) => items.len(),
117 }
118 }
119}
120
121impl TryFrom<&DynamicImage> for FrameFormat {
122 type Error = &'static str;
123
124 fn try_from(value: &DynamicImage) -> Result<FrameFormat, Self::Error> {
125 let (width, height) = value.dimensions();
126 match value {
127 DynamicImage::ImageRgb8(_) => Ok(FrameFormat::new(
128 ColorId::RGB,
129 PixelDepth::U8(8),
130 PixelEndian::default(),
131 width,
132 height,
133 )),
134 DynamicImage::ImageRgb16(_) => Ok(FrameFormat::new(
135 ColorId::RGB,
136 PixelDepth::U16(16),
137 PixelEndian::default(),
138 width,
139 height,
140 )),
141 DynamicImage::ImageLuma8(_) => Ok(FrameFormat::new(
142 ColorId::MONO,
143 PixelDepth::U8(8),
144 PixelEndian::default(),
145 width,
146 height,
147 )),
148 DynamicImage::ImageLuma16(_) => Ok(FrameFormat::new(
149 ColorId::MONO,
150 PixelDepth::U16(16),
151 PixelEndian::default(),
152 width,
153 height,
154 )),
155 _ => Err("Unsupported image type."),
156 }
157 }
158}
159
160impl TryFrom<Frame> for DynamicImage {
161 type Error = &'static str;
162
163 fn try_from(value: Frame) -> Result<Self, Self::Error> {
164 let Frame((width, height), pixels) = value;
165 match match pixels {
166 Pixels::Rgb8(p) => ImageBuffer::<image::Rgb<u8>, Vec<u8>>::from_raw(width, height, p)
167 .and_then(|buf| Some(buf.into())),
168 Pixels::Rgb16(p) => {
169 ImageBuffer::<image::Rgb<u16>, Vec<u16>>::from_raw(width, height, p)
170 .and_then(|buf| Some(buf.into()))
171 }
172 Pixels::Luma8(p) => ImageBuffer::<image::Luma<u8>, Vec<u8>>::from_raw(width, height, p)
173 .and_then(|buf| Some(buf.into())),
174 Pixels::Luma16(p) => {
175 ImageBuffer::<image::Luma<u16>, Vec<u16>>::from_raw(width, height, p)
176 .and_then(|buf| Some(buf.into()))
177 }
178 } {
179 Some(img) => Ok(img),
180 None => Err("Unable to convert frame"),
181 }
182 }
183}
184
185impl BinRead for Frame {
186 type Args<'a> = FrameFormat;
187
188 fn read_options<R: std::io::Read + std::io::Seek>(
189 reader: &mut R,
190 _endian: binrw::Endian,
191 args: Self::Args<'_>,
192 ) -> BinResult<Self> {
193 let channels: PixelChannels = args.color().into();
194 let vec_args = VecArgs::builder()
195 .count(channels.len() * args.width as usize * args.height as usize)
196 .finalize();
197 let endian = args.endian().into();
198 let pixels = match (channels, args.depth()) {
199 (PixelChannels::Luma, PixelDepth::U8(_)) => {
200 Pixels::Luma8(<Vec<u8>>::read_options(reader, endian, vec_args)?)
201 }
202 (PixelChannels::Luma, PixelDepth::U16(_)) => {
203 Pixels::Luma16(<Vec<u16>>::read_options(reader, endian, vec_args)?)
204 }
205 (PixelChannels::Rgb, PixelDepth::U8(_)) => {
206 Pixels::Rgb8(<Vec<u8>>::read_options(reader, endian, vec_args)?)
207 }
208 (PixelChannels::Rgb, PixelDepth::U16(_)) => {
209 Pixels::Rgb16(<Vec<u16>>::read_options(reader, endian, vec_args)?)
210 }
211 };
212
213 Ok(Frame((args.width, args.height), pixels))
214 }
215}
216
217impl BinWrite for Frame {
218 type Args<'a> = FrameFormat;
219
220 fn write_options<W: std::io::Write + std::io::Seek>(
221 &self,
222 writer: &mut W,
223 _endian: binrw::Endian,
224 args: Self::Args<'_>,
225 ) -> BinResult<()> {
226 let Frame(_, pixels) = self;
227 let endian = args.endian().into();
228 match pixels {
229 Pixels::Rgb8(p) => p.write_options(writer, endian, ()),
230 Pixels::Rgb16(p) => p.write_options(writer, endian, ()),
231 Pixels::Luma8(p) => p.write_options(writer, endian, ()),
232 Pixels::Luma16(p) => p.write_options(writer, endian, ()),
233 }
234 }
235}
236
237impl From<&ColorId> for PixelChannels {
238 fn from(value: &ColorId) -> Self {
239 match value {
240 ColorId::RGB => PixelChannels::Rgb,
241 ColorId::BGR => PixelChannels::Rgb,
242 _ => PixelChannels::Luma,
243 }
244 }
245}
246
247impl From<i32> for PixelDepth {
248 fn from(value: i32) -> Self {
249 match value {
250 v if v > 8 => PixelDepth::U16(v),
251 v => PixelDepth::U8(v),
252 }
253 }
254}
255
256impl From<&PixelDepth> for i32 {
257 fn from(value: &PixelDepth) -> Self {
258 match value {
259 PixelDepth::U8(v) => *v,
260 PixelDepth::U16(v) => *v,
261 }
262 }
263}
264
265impl PartialEq<Frame> for FrameFormat {
266 fn eq(&self, frame: &Frame) -> bool {
267 let Frame((w, h), p) = frame;
268 &self.width == w && &self.height == h && self.raw_len() == p.len() && p == self.depth
269 }
270}
271
272impl PartialEq<PixelDepth> for &Pixels {
273 fn eq(&self, depth: &PixelDepth) -> bool {
274 match self {
275 Pixels::Rgb8(_) => matches!(depth, PixelDepth::U8(_)),
276 Pixels::Rgb16(_) => matches!(depth, PixelDepth::U16(_)),
277 Pixels::Luma8(_) => matches!(depth, PixelDepth::U8(_)),
278 Pixels::Luma16(_) => matches!(depth, PixelDepth::U16(_)),
279 }
280 }
281}