image_extras/
wbmp.rs

1//! Encoding and Decoding of WBMP Images
2//!
3//! WBMP (Wireless BitMaP) Format is an image format used by the WAP protocol.
4//!
5//! # Related Links
6//! * <https://en.wikipedia.org/wiki/Wireless_Application_Protocol_Bitmap_Format> - The WBMP format on Wikipedia
7//! * <https://www.wapforum.org/what/technical/SPEC-WAESpec-19990524.pdf> - The WAP Specification
8
9use 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
16/// Encoder for Wbmp images.
17pub 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
62/// Decoder for Wbmp images.
63pub 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    /// Create a new `WbmpDecoder`.
73    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}