1use std::io::{BufRead, Seek, Write};
10
11use image::error::{
12 DecodingError, EncodingError, ImageFormatHint, UnsupportedError, UnsupportedErrorKind,
13};
14use image::{ColorType, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageResult};
15
16pub struct WbmpEncoder<W> {
18 writer: W,
19 threshold: u8,
20}
21
22impl<W: Write> WbmpEncoder<W> {
23 pub fn new(writer: W) -> Self {
24 Self {
25 writer,
26 threshold: 127_u8,
27 }
28 }
29
30 pub fn with_threshold(mut self, threshold: u8) -> Self {
31 self.threshold = threshold;
32 self
33 }
34}
35
36impl<W: Write> ImageEncoder for WbmpEncoder<W> {
37 fn write_image(
38 mut self,
39 buf: &[u8],
40 width: u32,
41 height: u32,
42 color_type: ExtendedColorType,
43 ) -> std::result::Result<(), ImageError> {
44 let color = match color_type {
45 ExtendedColorType::L8 => wbmp::ColorType::Luma8,
46 ExtendedColorType::Rgba8 => wbmp::ColorType::Rgba8,
47 _ => {
48 return Err(ImageError::Encoding(EncodingError::new(
49 format_hint(),
50 "Unsupported ColorType".to_string(),
51 )));
52 }
53 };
54
55 wbmp::Encoder::new(&mut self.writer)
56 .with_threshold(self.threshold)
57 .encode(buf, width, height, color)
58 .map_err(convert_wbmp_error)
59 }
60}
61
62pub struct WbmpDecoder<R> {
64 dimensions: (u32, u32),
65 inner: wbmp::Decoder<R>,
66}
67
68impl<R> WbmpDecoder<R>
69where
70 R: BufRead + Seek,
71{
72 pub fn new(r: R) -> Result<WbmpDecoder<R>, ImageError> {
74 let inner = wbmp::Decoder::new(r).map_err(convert_wbmp_error)?;
75 let dimensions = inner.dimensions();
76
77 Ok(WbmpDecoder { dimensions, inner })
78 }
79}
80
81impl<R: BufRead + Seek> ImageDecoder for WbmpDecoder<R> {
82 fn dimensions(&self) -> (u32, u32) {
83 self.dimensions
84 }
85
86 fn color_type(&self) -> ColorType {
87 ColorType::L8
88 }
89
90 fn original_color_type(&self) -> ExtendedColorType {
91 ExtendedColorType::L1
92 }
93
94 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
95 let (width, height) = self.dimensions;
96 assert_eq!(buf.len(), (width * height) as usize, "Invalid buffer size");
97
98 self.inner.read_image_data(buf).map_err(convert_wbmp_error)
99 }
100
101 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
102 (*self).read_image(buf)
103 }
104}
105
106fn convert_wbmp_error(err: wbmp::error::WbmpError) -> ImageError {
107 use wbmp::error::WbmpError;
108 match err {
109 WbmpError::IoError(inner) => ImageError::IoError(inner),
110 WbmpError::UnsupportedType(inner) => {
111 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
112 format_hint(),
113 UnsupportedErrorKind::GenericFeature(format!(
114 "type {inner} is not supported for wbmp images"
115 )),
116 ))
117 }
118 WbmpError::UnsupportedHeaders => {
119 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
120 format_hint(),
121 UnsupportedErrorKind::GenericFeature(
122 "Extension headers are not supported for wbmp images".to_string(),
123 ),
124 ))
125 }
126 WbmpError::InvalidImageData => ImageError::Encoding(EncodingError::new(format_hint(), err)),
127 WbmpError::UsageError(_) => ImageError::Decoding(DecodingError::new(format_hint(), err)),
128 }
129}
130
131fn format_hint() -> ImageFormatHint {
132 ImageFormatHint::Name("WBMP".to_string())
133}