busybar_render/
encoding.rs1use std::fmt;
2use std::io::Cursor;
3use std::path::Path;
4
5use base64::Engine as _;
6use base64::engine::general_purpose::STANDARD;
7
8use crate::error::RenderError;
9use crate::raw::RawImage;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ImageFormat {
13 Bmp,
14 Jpeg,
15 Png,
16}
17
18impl ImageFormat {
19 pub fn from_path(path: &Path) -> Option<Self> {
20 let extension = path.extension()?.to_str()?.to_ascii_lowercase();
21
22 match extension.as_str() {
23 "bmp" => Some(Self::Bmp),
24 "jpg" | "jpeg" => Some(Self::Jpeg),
25 "png" => Some(Self::Png),
26 _ => None,
27 }
28 }
29
30 fn encoding(self) -> image::ImageFormat {
31 match self {
32 Self::Bmp => image::ImageFormat::Bmp,
33 Self::Jpeg => image::ImageFormat::Jpeg,
34 Self::Png => image::ImageFormat::Png,
35 }
36 }
37}
38
39impl fmt::Display for ImageFormat {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 Self::Bmp => f.write_str("bmp"),
43 Self::Jpeg => f.write_str("jpeg"),
44 Self::Png => f.write_str("png"),
45 }
46 }
47}
48
49impl RawImage {
50 pub fn encode(&self, format: ImageFormat) -> Result<Vec<u8>, RenderError> {
51 let mut buffer = Cursor::new(Vec::new());
52
53 self.buffer()
54 .write_to(&mut buffer, format.encoding())
55 .map_err(|source| RenderError::Encode {
56 width: self.width(),
57 height: self.height(),
58 format,
59 source,
60 })?;
61
62 Ok(buffer.into_inner())
63 }
64
65 pub fn encode_base64(&self, format: ImageFormat) -> Result<String, RenderError> {
66 self.encode(format).map(|bytes| STANDARD.encode(bytes))
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::raw::PixelLayout;
74
75 #[test]
76 fn picks_a_format_from_the_file_extension() {
77 assert_eq!(
78 ImageFormat::from_path(Path::new("frame.bmp")),
79 Some(ImageFormat::Bmp)
80 );
81 assert_eq!(
82 ImageFormat::from_path(Path::new("frame.JPEG")),
83 Some(ImageFormat::Jpeg)
84 );
85 assert_eq!(
86 ImageFormat::from_path(Path::new("./out/frame.png")),
87 Some(ImageFormat::Png)
88 );
89 assert_eq!(ImageFormat::from_path(Path::new("frame.raw")), None);
90 assert_eq!(ImageFormat::from_path(Path::new("frame")), None);
91 }
92
93 #[test]
94 fn encodes_every_format_it_supports() {
95 let image = RawImage::new(2, 2, PixelLayout::Gray8, &[0, 64, 128, 255]).unwrap();
96
97 assert!(image.encode(ImageFormat::Bmp).unwrap().starts_with(b"BM"));
98 assert!(
99 image
100 .encode(ImageFormat::Jpeg)
101 .unwrap()
102 .starts_with(&[0xff, 0xd8])
103 );
104 assert!(
105 image
106 .encode(ImageFormat::Png)
107 .unwrap()
108 .starts_with(b"\x89PNG")
109 );
110 }
111
112 #[test]
113 fn encodes_to_base64() {
114 let image = RawImage::new(1, 1, PixelLayout::Gray8, &[0x7f]).unwrap();
115 let encoded = image.encode_base64(ImageFormat::Png).unwrap();
116
117 assert_eq!(
118 STANDARD.decode(&encoded).unwrap(),
119 image.encode(ImageFormat::Png).unwrap()
120 );
121 }
122}