1use std::fmt::{self, Display};
10use std::io::{BufRead, Seek, Write};
11
12use image::error::{DecodingError, EncodingError, ImageFormatHint, LimitError, LimitErrorKind};
13use image::{ColorType, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageResult};
14
15#[derive(Debug, Clone)]
17enum EncoderError {
18 ImageTooLarge,
20
21 UnsupportedColorType,
23}
24
25impl Display for EncoderError {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 match self {
28 EncoderError::ImageTooLarge => f.write_fmt(format_args!(
29 "Specified image is too large for the OTB format (Max 255x255)"
30 )),
31 EncoderError::UnsupportedColorType => f.write_fmt(format_args!(
32 "Color type of specified image is not supported"
33 )),
34 }
35 }
36}
37impl std::error::Error for EncoderError {}
38
39impl From<EncoderError> for ImageError {
40 fn from(e: EncoderError) -> ImageError {
41 match e {
42 EncoderError::ImageTooLarge => {
43 ImageError::Limits(LimitError::from_kind(LimitErrorKind::DimensionError))
44 }
45 _ => ImageError::Encoding(EncodingError::new(ImageFormatHint::Name("otb".into()), e)),
46 }
47 }
48}
49
50pub struct OtbEncoder<W> {
52 writer: W,
53 threshold: u8,
54}
55
56impl<W: Write> OtbEncoder<W> {
57 pub fn new(writer: W) -> Self {
58 Self {
59 writer,
60 threshold: 127_u8,
61 }
62 }
63
64 pub fn with_threshold(mut self, threshold: u8) -> Self {
65 self.threshold = threshold;
66 self
67 }
68}
69
70impl<W: Write> ImageEncoder for OtbEncoder<W> {
71 fn write_image(
72 mut self,
73 buf: &[u8],
74 width: u32,
75 height: u32,
76 color_type: ExtendedColorType,
77 ) -> std::result::Result<(), ImageError> {
78 if width > 0xFF || height > 0xFF {
79 return Err(EncoderError::ImageTooLarge.into());
80 }
81 if color_type != ExtendedColorType::L8 {
82 return Err(EncoderError::UnsupportedColorType.into());
83 }
84
85 let _ = self
87 .writer
88 .write(&[0x00, width as u8, height as u8, 0x01])?;
89
90 let mut current_byte = 0_u8;
92 let mut bit = 0;
93 for buf_idx in 0..(width * height) {
94 if buf[buf_idx as usize] < self.threshold {
95 current_byte |= 1 << (7 - bit);
96 }
97 bit += 1;
98 if bit == 8 {
99 self.writer.write_all(&[current_byte])?;
100 current_byte = 0_u8;
101 bit = 0;
102 };
103 }
104 if bit != 0 {
105 self.writer.write_all(&[current_byte])?;
106 }
107
108 Ok(())
109 }
110}
111
112#[derive(Debug, Clone)]
114enum DecoderError {
115 UnsupportedInfoField(u8),
117 WidthZero,
119 HeightZero,
121 UnsupportedColorDepth(u8),
123}
124
125impl Display for DecoderError {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 match self {
128 Self::UnsupportedInfoField(info) => {
129 f.write_fmt(format_args!("Unsupported value in info field {info:08b}"))
130 }
131 Self::WidthZero => f.write_fmt(format_args!("Width cannot be zero")),
132 Self::HeightZero => f.write_fmt(format_args!("Height cannot be zero")),
133 Self::UnsupportedColorDepth(depth) => f.write_fmt(format_args!(
134 "Unsupported color depth value in headers {depth}"
135 )),
136 }
137 }
138}
139impl std::error::Error for DecoderError {}
140
141impl From<DecoderError> for ImageError {
142 fn from(e: DecoderError) -> ImageError {
143 ImageError::Decoding(DecodingError::new(ImageFormatHint::Name("otb".into()), e))
144 }
145}
146
147pub struct OtbDecoder<R> {
149 reader: R,
150 dimensions: (u32, u32),
151}
152
153impl<R> OtbDecoder<R>
154where
155 R: BufRead + Seek,
156{
157 pub fn new(reader: R) -> Result<OtbDecoder<R>, ImageError> {
159 let mut decoder = Self::new_decoder(reader);
160 decoder.read_metadata()?;
161 Ok(decoder)
162 }
163
164 fn new_decoder(reader: R) -> OtbDecoder<R> {
165 Self {
166 reader,
167 dimensions: (0, 0),
168 }
169 }
170
171 fn read_metadata(&mut self) -> Result<(), ImageError> {
172 let mut header_buf = [0_u8; 4];
173 self.reader.read_exact(&mut header_buf)?;
174
175 let [info_field, width, height, depth] = header_buf;
176
177 if info_field != 0 {
179 return Err(DecoderError::UnsupportedInfoField(info_field).into());
180 }
181
182 if width == 0 {
184 return Err(DecoderError::WidthZero.into());
185 }
186
187 if height == 0 {
189 return Err(DecoderError::HeightZero.into());
190 }
191
192 if depth != 1 {
194 return Err(DecoderError::UnsupportedColorDepth(depth).into());
195 }
196
197 self.dimensions = (width as u32, height as u32);
198
199 Ok(())
200 }
201}
202
203impl<R: BufRead + Seek> ImageDecoder for OtbDecoder<R> {
204 fn dimensions(&self) -> (u32, u32) {
205 self.dimensions
206 }
207
208 fn color_type(&self) -> ColorType {
209 ColorType::L8
210 }
211
212 fn original_color_type(&self) -> ExtendedColorType {
213 ExtendedColorType::L1
214 }
215
216 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
217 let (width, height) = (self.dimensions.0 as usize, self.dimensions.1 as usize);
218
219 assert_eq!(buf.len(), width * height, "Invalid buffer length");
220
221 let mut byte_buf = vec![0_u8; (width * height).div_ceil(8)].into_boxed_slice();
223 self.reader.read_exact(&mut byte_buf)?;
224
225 for (i, &byte) in byte_buf.iter().enumerate() {
227 for bit in 0..8 {
228 let buf_idx = 8 * i + bit;
229 if buf_idx >= buf.len() {
230 break;
231 }
232
233 buf[buf_idx] = if (byte >> (7 - bit)) & 1 == 0 {
234 0xFF
235 } else {
236 0x00
237 };
238 }
239 }
240 Ok(())
241 }
242
243 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
244 (*self).read_image(buf)
245 }
246}
247
248mod test {
249 #[test]
250 fn test_decode_image() {
251 use image::ImageDecoder;
252 use std::io::Cursor;
253 let otb_data = vec![
254 0x00, 0x48, 0x1C, 0x01, 0x7F, 0xFF, 0xEF, 0xFF, 0xEF, 0xFF, 0xFB, 0xFF, 0xFE, 0x40, 0x3F, 0xE8, 0x38, 0x2F,
258 0xFF, 0xFB, 0xFF, 0xFE, 0x48, 0x3F, 0xA8, 0x38, 0x2F, 0x9F, 0xFB, 0xFF, 0xFE, 0x4C,
259 0xFF, 0xA9, 0xFF, 0x2F, 0x8F, 0xFA, 0xDA, 0xDA, 0x4E, 0xFF, 0x29, 0x01, 0x2F, 0x80,
260 0xFA, 0x52, 0x52, 0x5E, 0x7F, 0x69, 0x31, 0x2F, 0xBF, 0x7B, 0x07, 0x06, 0x4F, 0xFF,
261 0x69, 0x79, 0x2F, 0xBE, 0xFB, 0x77, 0x76, 0x47, 0xFF, 0x69, 0x79, 0x2F, 0xBE, 0x7B,
262 0x07, 0x06, 0x47, 0xFE, 0xEF, 0x7D, 0xEF, 0xBE, 0x7B, 0xFF, 0xFE, 0x47, 0xFC, 0xEF,
263 0x7D, 0xE7, 0xBC, 0xF1, 0xFF, 0xFC, 0x40, 0xF0, 0xEF, 0x7D, 0xE7, 0x7C, 0xF1, 0xED,
264 0xBC, 0x21, 0xE7, 0xC9, 0x79, 0x27, 0x98, 0xF1, 0xE5, 0x3C, 0x21, 0xE7, 0xC9, 0x39,
265 0x27, 0xC8, 0xF1, 0xF0, 0x7C, 0x16, 0x6F, 0x89, 0x39, 0x23, 0xE6, 0xE0, 0xF7, 0x78,
266 0x15, 0x2F, 0x88, 0x82, 0x23, 0xF3, 0xE0, 0xF0, 0x78, 0x08, 0x3F, 0x04, 0x44, 0x43,
267 0xD7, 0xE0, 0xFF, 0xF8, 0x04, 0x3E, 0x02, 0x28, 0x81, 0xEF, 0xC0, 0x7F, 0xF0, 0x02,
268 0x3C, 0x01, 0x39, 0x00, 0xFF, 0x80, 0x3F, 0xE0, 0x01, 0x38, 0x00, 0xBA, 0x00, 0x7F,
269 0x00, 0x1F, 0xC0, 0x00, 0xF0, 0x00, 0x7C, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0xFF, 0xC0,
270 0x00, 0x38, 0x00, 0x1C, 0x00, 0x07, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
271 0xFF, 0xAA, 0x2A, 0xF3, 0x87, 0x87, 0x3F, 0x1E, 0x67, 0x0F, 0x54, 0x15, 0xF3, 0x93,
272 0x9F, 0x3E, 0x4E, 0x27, 0x27, 0xA8, 0x2A, 0xF3, 0x87, 0x8F, 0x3E, 0x4E, 0x07, 0x27,
273 0x54, 0x55, 0xF3, 0x93, 0x9F, 0x3E, 0x0E, 0x47, 0x27, 0xAA, 0xFF, 0xF3, 0x9B, 0x87,
274 0x0E, 0x4E, 0x67, 0x0F, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
275 0x00, ];
277 let decoder = crate::otb::OtbDecoder::new(Cursor::new(otb_data)).unwrap();
278 let (width, height) = decoder.dimensions();
279 assert!(width == 0x48);
280 assert!(height == 0x1C);
281 let mut img_bytes = vec![0; 2016];
282 decoder.read_image(&mut img_bytes).unwrap();
283 }
284
285 #[test]
286 fn test_decoder_irregular_width() {
287 use image::ImageDecoder;
288 use std::io::Cursor;
289 let expected_data = [
290 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ];
301 #[allow(clippy::unusual_byte_groupings)]
302 let image_data: [u8; 17] = [
303 0,
304 10,
305 10,
306 1, 0b_00000000,
308 0b00_000011,
309 0b0000_0001,
310 0b001000_00,
311 0b10000100_,
312 0b_01000000,
313 0b10_010000,
314 0b0010_0010,
315 0b000100_00,
316 0b01001000_,
317 0b_00001100,
318 0b00_000000,
319 0b0000_0000,
320 ];
321 let decoder = crate::otb::OtbDecoder::new(Cursor::new(image_data)).unwrap();
322 let (width, height) = decoder.dimensions();
323 assert!(width == 10);
324 assert!(height == 10);
325 let mut img_bytes = vec![0; 100];
326 decoder.read_image(&mut img_bytes).unwrap();
327 img_bytes.iter().enumerate().for_each(|(i, byte)| {
328 assert_eq!(*byte, expected_data[i]);
329 });
330 }
331
332 #[test]
333 fn test_decoder() {
334 use image::ImageDecoder;
335 use std::io::Cursor;
336 let expected_data = [
337 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, ];
346 let image_data: [u8; 12] = [
347 0, 8, 8, 1, 0b00011000, 0b00100100, 0b01000010, 0b10000001, 0b10000001, 0b01000010, 0b00100100, 0b00011000, ];
357 let decoder = crate::otb::OtbDecoder::new(Cursor::new(image_data)).unwrap();
358 let (width, height) = decoder.dimensions();
359 assert!(width == 8);
360 assert!(height == 8);
361 let mut img_bytes = vec![0; 64];
362 decoder.read_image(&mut img_bytes).unwrap();
363 img_bytes.iter().enumerate().for_each(|(i, byte)| {
364 assert_eq!(*byte, expected_data[i]);
365 });
366 }
367
368 #[test]
369 fn test_encoder() {
370 use image::ImageEncoder;
371 let img_data = [
372 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, ];
381 let expected_data: [u8; 12] = [
382 0, 8, 8, 1, 0b00011000, 0b00100100, 0b01000010, 0b10000001, 0b10000001, 0b01000010, 0b00100100, 0b00011000, ];
392 let mut encoded_data = Vec::<u8>::with_capacity(expected_data.len());
393 let encoder = crate::otb::OtbEncoder::new(&mut encoded_data);
394 encoder
395 .write_image(&img_data, 8, 8, image::ExtendedColorType::L8)
396 .unwrap();
397 encoded_data.iter().enumerate().for_each(|(i, byte)| {
398 assert_eq!(*byte, expected_data[i]);
399 });
400 }
401
402 #[test]
403 fn test_encoder_irregular_width() {
404 use image::ImageEncoder;
405 let img_data = [
406 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ];
417 #[allow(clippy::unusual_byte_groupings)]
418 let expected_data: [u8; 17] = [
419 0,
420 10,
421 10,
422 1, 0b_00000000,
424 0b00_000011,
425 0b0000_0001,
426 0b001000_00,
427 0b10000100_,
428 0b_01000000,
429 0b10_010000,
430 0b0010_0010,
431 0b000100_00,
432 0b01001000_,
433 0b_00001100,
434 0b00_000000,
435 0b0000_0000,
436 ];
437 let mut encoded_data = Vec::<u8>::with_capacity(expected_data.len());
438 let encoder = crate::otb::OtbEncoder::new(&mut encoded_data);
439 encoder
440 .write_image(&img_data, 10, 10, image::ExtendedColorType::L8)
441 .unwrap();
442 encoded_data.iter().enumerate().for_each(|(i, byte)| {
443 assert_eq!(*byte, expected_data[i]);
444 });
445 }
446}