1use std::{fs::{File, self}, io::Read, collections::HashMap};
80
81const FLAG_PALETTE: u8 = 1 << 0;
82const FLAG_TRANSPARENCY: u8 = 1 << 1;
83const HEADER_SIZE: usize = 11;
84
85#[derive(Debug, PartialEq, Clone, Copy)]
86pub enum PixelFormat {
87 RGB, RGBA,
88}
89
90#[derive(Debug, PartialEq)]
92pub struct DecodedPIE {
93 pub width: u16,
94 pub height: u16,
95 pub format: PixelFormat,
96 pub pixels: Vec<u8>,
97}
98
99#[derive(Debug, PartialEq)]
102pub struct EncodedPIE {
103 pub width: u16,
104 pub height: u16,
105 pub indices: Vec<u8>,
106 pub palette: Option<Palette>,
107}
108
109#[derive(Debug, PartialEq)]
110pub enum DecodeError {
111 MissingPalette,
112}
113
114#[derive(Debug, PartialEq)]
115pub enum EncodeError {
116 WrongPixelCount,
117 ColorNotInPalette,
118}
119
120#[derive(Debug, PartialEq, Clone)]
122pub struct Palette {
123 pub format: PixelFormat,
124 pub colors: Vec<u8>, }
126
127pub fn write(path: &str, width: u16, height: u16, embed_palette: bool, maybe_palette: Option<&Palette>, pixels: Vec<u8>) -> Result<bool, EncodeError> {
138 let encoded = encode(width, height, &pixels, embed_palette, maybe_palette).expect("Failed to encode data.");
139 let mut flags = 0;
140
141 if encoded.indices.len() / 2 > u16::MAX as usize {
142 return Err(EncodeError::WrongPixelCount);
143 }
144
145 let mut bytes: Vec<u8> = vec!['P' as u8, 'I' as u8, 'E' as u8, 1];
146 bytes.append(&mut width.to_be_bytes().to_vec());
147 bytes.append(&mut height.to_be_bytes().to_vec());
148 bytes.push(0); bytes.append(&mut ((encoded.indices.len() / 2) as u16).to_be_bytes().to_vec());
150 bytes.append(&mut encoded.indices.to_vec());
151
152 if embed_palette {
153 flags |= FLAG_PALETTE;
154 bytes.append(&mut encoded.palette.unwrap().colors.to_vec());
155 }
156
157 bytes[8] = flags;
158
159 fs::write(path, &bytes).expect("Failed to write file.");
160 Ok(true)
161}
162
163pub fn encode(width: u16, height: u16, pixel_bytes: &[u8], embed_palette: bool, maybe_palette: Option<&Palette>) -> Result<EncodedPIE, EncodeError> {
167 let mut encoded = EncodedPIE {
168 width, height,
169 indices: Vec::new(),
170 palette: None
171 };
172
173
174 let mut chunk_size = 4;
175 if pixel_bytes.len() == (width as usize * height as usize * 3) {
176 chunk_size = 3;
177 };
178
179 if maybe_palette.is_none() {
181 let mut indices = Vec::new();
182 let mut palette = Palette {
183 format: if chunk_size == 3 { PixelFormat::RGB } else { PixelFormat::RGBA },
184 colors: Vec::new()
185 };
186 let mut map = HashMap::new();
187 let mut index: u8 = 0;
188 for chunk in pixel_bytes.chunks(chunk_size) {
189 if !map.contains_key(chunk) {
190 map.insert(chunk, index);
191 index += 1;
192 palette.colors.append(&mut chunk.to_vec());
193 }
194
195 indices.push(*map.get(chunk).unwrap() as u8);
196 }
197
198 if embed_palette {
199 encoded.palette = Some(palette);
200 }
201 encoded.indices = rle(&indices, 255);
202 } else if let Some(palette) = maybe_palette {
203 let mut indices = Vec::new();
204 let map = palette.colors.chunks(chunk_size).into_iter().enumerate().fold(HashMap::new(), |mut acc, (idx, x)| {
205 acc.insert(x, idx);
206 acc
207 });
208 for chunk in pixel_bytes.chunks(chunk_size) {
209 if !map.contains_key(chunk) {
210 return Err(EncodeError::ColorNotInPalette);
211 }
212
213 indices.push(*map.get(chunk).unwrap() as u8);
214
215 if embed_palette {
216 encoded.palette = Some(palette.to_owned());
217 }
218 encoded.indices = rle(&indices, 255);
219 }
220 }
221
222 Ok(encoded)
223}
224
225pub fn rle(data: &[u8], limit: usize) -> Vec<u8> {
227 let mut encoded = Vec::new();
228 let mut i = 0;
229 while i < data.len() {
230 let mut count = 1;
231 while i + count < data.len() && data[i] == data[i + count] && count < limit {
232 count += 1;
233 }
234 encoded.push(count as u8);
235 encoded.push(data[i]);
236 i += count;
237 }
238 encoded
239}
240
241pub fn read(path: &str, palette: Option<&Palette>) -> Result<DecodedPIE, DecodeError> {
248 let mut file = File::open(path).expect("Could not open file");
249 let mut bytes = Vec::new();
250 file.read_to_end(&mut bytes).expect("Could not read file");
251
252 decode(&bytes, palette)
253}
254
255pub fn decode(bytes: &[u8], maybe_palette: Option<&Palette>) -> Result<DecodedPIE, DecodeError> {
259 let mut decoded = DecodedPIE {
260 width: 0, height: 0,
261 format: PixelFormat::RGB, pixels: vec![]
262 };
263
264 let mut palette = Palette {
265 format: PixelFormat::RGB,
266 colors: Vec::new(),
267 };
268
269 assert!(bytes[0] == 'P' as u8 && bytes[1] == 'I' as u8 && bytes[2] == 'E' as u8);
270 decoded.width = u16::from_be_bytes([bytes[4], bytes[5]]);
271 decoded.height = u16::from_be_bytes([bytes[6], bytes[7]]);
272 let flags = bytes[8];
273
274 palette.format = PixelFormat::RGB;
275 let mut step = 3;
276
277 if flags & FLAG_TRANSPARENCY > 0 {
278 palette.format = PixelFormat::RGBA;
279 step = 4;
280 }
281
282 let data_length = u16::from_be_bytes([bytes[9], bytes[10]]);
283
284 if flags & FLAG_PALETTE > 0 {
285 for (index, _) in bytes.iter().skip(HEADER_SIZE + (data_length * 2) as usize).enumerate().step_by(step) {
286 let absolute_index = HEADER_SIZE + (data_length * 2) as usize + index - 1;
287 for i in 0..step {
288 palette.colors.push(bytes[absolute_index + step - i]);
289 }
290 }
291 } else if let Some(p) = maybe_palette {
292 palette.format = p.format;
293 palette.colors = p.colors.to_owned();
294 } else {
295 return Err(DecodeError::MissingPalette);
296 }
297
298 for i in (HEADER_SIZE..(HEADER_SIZE + (data_length * 2) as usize)).step_by(2) {
299 let run_length = bytes[i];
300 let color_index = bytes[i + 1] as usize * step;
301
302 for _ in 0..run_length {
303 decoded.pixels.append(&mut vec![palette.colors[color_index + 2], palette.colors[color_index + 1], palette.colors[color_index]]);
304 }
305 }
306
307 decoded.format = palette.format;
308
309 Ok(decoded)
310}
311
312#[test]
313fn test_decode() {
314 let bytes = include_bytes!("../images/test_embedded_palette.pie");
315 let decoded = decode(bytes, None).unwrap();
316 let palette_bytes: [u8; 12] = [
317 0x6A, 0xBE, 0x30,
318 0xFF, 0xFF, 0xFF,
319 0x00, 0x00, 0x00,
320 0x5B, 0x6E, 0xE1,
321 ];
322 let start_pixel: [u8; 3] = [0x6A, 0xBE, 0x30];
323 let end_pixel: [u8; 3] = [0x5B, 0x6E, 0xE1];
324 let decoded_with_palette = decode(bytes, Some(&Palette {
325 format: PixelFormat::RGB,
326 colors: palette_bytes.to_vec(),
327 })).unwrap();
328
329 assert_eq!(start_pixel, decoded.pixels[0..3]);
330 assert_eq!(end_pixel, decoded.pixels[decoded.pixels.len() - 3..]);
331 assert_eq!(decoded.pixels, decoded_with_palette.pixels);
332}
333
334#[test]
335fn test_encode() {
336 let pixels: Vec<u8> = vec![
337 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00,
338 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
339 0xFF, 0x00, 0xCC, 0xFF, 0x00, 0xCC, 0xFF, 0x00, 0xCC, 0xFF, 0x00, 0xCC, 0xFF, 0x00, 0xCC,
340 0xBE, 0xEF, 0x00, 0xBE, 0xEF, 0x00, 0xBE, 0xEF, 0x00, 0xBE, 0xEF, 0x00, 0xFF, 0xFF, 0xFF,
341 ];
342
343 let palette = Palette {
344 format: PixelFormat::RGB,
345 colors: vec![
346 0xFF, 0xFF, 0xFF,
347 0xFF, 0x00, 0x00,
348 0xBE, 0xEF, 0x00,
349 0xFF, 0x00, 0xCC,
350 ],
351 };
352
353 let encoded = encode(5, 4, &pixels, true, Some(&palette)).unwrap();
354 assert_eq!([5, 1] as [u8; 2], encoded.indices[0..2]);
355 assert_eq!([5, 0] as [u8; 2], encoded.indices[2..4]);
356 assert_eq!([5, 3] as [u8; 2], encoded.indices[4..6]);
357 assert_eq!([4, 2] as [u8; 2], encoded.indices[6..8]);
358 assert_eq!([1, 0] as [u8; 2], encoded.indices[8..10]);
359 assert_eq!(palette.colors, encoded.palette.unwrap().colors);
360
361 let encoded = encode(5, 4, &pixels, false, Some(&palette)).unwrap();
362 assert_eq!([5, 1] as [u8; 2], encoded.indices[0..2]);
363 assert_eq!([5, 0] as [u8; 2], encoded.indices[2..4]);
364 assert_eq!([5, 3] as [u8; 2], encoded.indices[4..6]);
365 assert_eq!([4, 2] as [u8; 2], encoded.indices[6..8]);
366 assert_eq!([1, 0] as [u8; 2], encoded.indices[8..10]);
367 assert!(encoded.palette.is_none());
368
369 let encoded = encode(5, 4, &pixels, true, None).unwrap();
370 assert_eq!([5, 0] as [u8; 2], encoded.indices[0..2]);
371 assert_eq!([5, 1] as [u8; 2], encoded.indices[2..4]);
372 assert_eq!([5, 2] as [u8; 2], encoded.indices[4..6]);
373 assert_eq!([4, 3] as [u8; 2], encoded.indices[6..8]);
374 assert_eq!([1, 1] as [u8; 2], encoded.indices[8..10]);
375 assert_eq!([0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xCC, 0xBE, 0xEF, 0x00] as [u8; 12], encoded.palette.unwrap().colors.as_slice());
376
377 let encoded = encode(5, 4, &pixels, false, None).unwrap();
378 assert_eq!([5, 0] as [u8; 2], encoded.indices[0..2]);
379 assert_eq!([5, 1] as [u8; 2], encoded.indices[2..4]);
380 assert_eq!([5, 2] as [u8; 2], encoded.indices[4..6]);
381 assert_eq!([4, 3] as [u8; 2], encoded.indices[6..8]);
382 assert_eq!([1, 1] as [u8; 2], encoded.indices[8..10]);
383 assert!(encoded.palette.is_none());
384}
385
386#[test]
387fn test_read() {
388 let decoded = read("images/test_embedded_palette.pie", None).unwrap();
389 let palette_bytes: [u8; 12] = [
390 0x6A, 0xBE, 0x30,
391 0xFF, 0xFF, 0xFF,
392 0x00, 0x00, 0x00,
393 0x5B, 0x6E, 0xE1,
394 ];
395 let decoded_with_palette = read("images/test_embedded_palette.pie", Some(&Palette {
396 format: PixelFormat::RGB,
397 colors: palette_bytes.to_vec(),
398 })).unwrap();
399
400 let start_pixel: [u8; 3] = [0x6A, 0xBE, 0x30];
401 let end_pixel: [u8; 3] = [0x5B, 0x6E, 0xE1];
402
403 assert_eq!(start_pixel, decoded.pixels[0..3]);
404 assert_eq!(end_pixel, decoded.pixels[decoded.pixels.len() - 3..]);
405 assert_eq!(decoded.pixels, decoded_with_palette.pixels);
406}
407
408#[test]
409fn test_write() {
410 let pixels: Vec<u8> = vec![
411 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00,
412 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
413 0xFF, 0x00, 0xCC, 0xFF, 0x00, 0xCC, 0xFF, 0x00, 0xCC, 0xFF, 0x00, 0xCC, 0xFF, 0x00, 0xCC,
414 0xBE, 0xEF, 0x00, 0xBE, 0xEF, 0x00, 0xBE, 0xEF, 0x00, 0xBE, 0xEF, 0x00, 0xFF, 0xFF, 0xFF,
415 ];
416
417 let palette = Palette {
418 format: PixelFormat::RGB,
419 colors: vec![
420 0xFF, 0xFF, 0xFF,
421 0xFF, 0x00, 0x00,
422 0xBE, 0xEF, 0x00,
423 0xFF, 0x00, 0xCC,
424 ],
425 };
426
427 assert!(write("tmp.pie", 5, 4, true, Some(&palette), pixels.to_owned()).is_ok());
428
429 let decoded = read("tmp.pie", Some(&palette)).expect("Could not read");
430 assert_eq!(pixels, decoded.pixels);
431 assert!(fs::remove_file("tmp.pie").is_ok());
432}