1#![no_std]
28#![forbid(unsafe_code)]
29#![forbid(missing_docs)]
30
31extern crate alloc;
32
33use crate::bit_reader::BitReader;
34
35use crate::decode::{EOFB, Mode};
36use alloc::vec;
37use alloc::vec::Vec;
38
39mod bit_reader;
40mod decode;
41mod state_machine;
42
43pub type Result<T> = core::result::Result<T, DecodeError>;
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum DecodeError {
49 UnexpectedEof,
51 InvalidCode,
53 LineLengthMismatch,
55 Overflow,
57}
58
59impl core::fmt::Display for DecodeError {
60 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
61 match self {
62 Self::UnexpectedEof => write!(f, "unexpected end of input"),
63 Self::InvalidCode => write!(f, "invalid CCITT code sequence"),
64 Self::LineLengthMismatch => write!(f, "scanline length mismatch"),
65 Self::Overflow => write!(f, "arithmetic overflow in position calculation"),
66 }
67 }
68}
69
70impl core::error::Error for DecodeError {}
71
72#[derive(Copy, Clone, Debug, PartialEq, Eq)]
74pub enum EncodingMode {
75 Group4,
77 Group3_1D,
79 Group3_2D {
81 k: u32,
83 },
84}
85
86#[derive(Copy, Clone, Debug)]
88pub struct DecodeSettings {
89 pub columns: u32,
91 pub rows: u32,
97 pub end_of_block: bool,
102 pub end_of_line: bool,
104 pub rows_are_byte_aligned: bool,
107 pub encoding: EncodingMode,
109 pub invert_black: bool,
111}
112
113pub trait Decoder {
115 fn push_pixel(&mut self, white: bool);
117 fn push_pixel_chunk(&mut self, white: bool, chunk_count: u32);
126 fn next_line(&mut self);
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub(crate) enum Color {
133 White,
135 Black,
137}
138
139impl Color {
140 #[inline(always)]
142 fn opposite(self) -> Self {
143 match self {
144 Self::White => Self::Black,
145 Self::Black => Self::White,
146 }
147 }
148
149 #[inline(always)]
151 fn is_white(self) -> bool {
152 matches!(self, Self::White)
153 }
154}
155
156#[derive(Clone, Copy)]
158struct ColorChange {
159 idx: u32,
160 color: Color,
161}
162
163pub fn decode(data: &[u8], decoder: &mut impl Decoder, settings: &DecodeSettings) -> Result<usize> {
173 let mut ctx = DecoderContext::new(decoder, settings);
174 let mut reader = BitReader::new(data);
175
176 match settings.encoding {
177 EncodingMode::Group4 => decode_group4(&mut ctx, &mut reader)?,
178 EncodingMode::Group3_1D => decode_group3_1d(&mut ctx, &mut reader)?,
179 EncodingMode::Group3_2D { .. } => decode_group3_2d(&mut ctx, &mut reader)?,
180 }
181
182 reader.align();
183 Ok(reader.byte_pos())
184}
185
186fn decode_group3_1d<T: Decoder>(
188 ctx: &mut DecoderContext<'_, T>,
189 reader: &mut BitReader<'_>,
190) -> Result<()> {
191 let _ = reader.read_eol_if_available();
194
195 loop {
196 decode_1d_line(ctx, reader)?;
197 ctx.next_line(reader)?;
198
199 if group3_check_eob(ctx, reader) {
200 break;
201 }
202 }
203
204 Ok(())
205}
206
207fn decode_group3_2d<T: Decoder>(
209 ctx: &mut DecoderContext<'_, T>,
210 reader: &mut BitReader<'_>,
211) -> Result<()> {
212 let _ = reader.read_eol_if_available();
215
216 loop {
217 let tag_bit = reader.read_bit()?;
218
219 if tag_bit == 1 {
220 decode_1d_line(ctx, reader)?;
221 } else {
222 decode_2d_line(ctx, reader)?;
223 }
224
225 ctx.next_line(reader)?;
226
227 if group3_check_eob(ctx, reader) {
228 break;
229 }
230 }
231
232 Ok(())
233}
234
235fn group3_check_eob<T: Decoder>(
237 ctx: &mut DecoderContext<'_, T>,
238 reader: &mut BitReader<'_>,
239) -> bool {
240 let eol_count = reader.read_eol_if_available();
241
242 if ctx.settings.end_of_block && eol_count >= 6 {
247 return true;
248 }
249
250 if ctx.decoded_rows == ctx.settings.rows || reader.at_end() {
251 return true;
252 }
253
254 false
255}
256
257fn decode_group4<T: Decoder>(
258 ctx: &mut DecoderContext<'_, T>,
259 reader: &mut BitReader<'_>,
260) -> Result<()> {
261 loop {
262 if ctx.settings.end_of_block && reader.peak_bits(24) == Ok(EOFB) {
263 reader.read_bits(24)?;
264 break;
265 }
266
267 if ctx.decoded_rows == ctx.settings.rows || reader.at_end() {
268 break;
269 }
270
271 decode_2d_line(ctx, reader)?;
272 ctx.next_line(reader)?;
273 }
274
275 Ok(())
276}
277
278#[inline(always)]
280fn decode_1d_line<T: Decoder>(
281 ctx: &mut DecoderContext<'_, T>,
282 reader: &mut BitReader<'_>,
283) -> Result<()> {
284 while !ctx.at_eol() {
285 let run_length = reader.decode_run(ctx.color)?;
286 ctx.push_pixels(run_length);
287 ctx.color = ctx.color.opposite();
288 }
289
290 Ok(())
291}
292
293#[inline(always)]
295fn decode_2d_line<T: Decoder>(
296 ctx: &mut DecoderContext<'_, T>,
297 reader: &mut BitReader<'_>,
298) -> Result<()> {
299 while !ctx.at_eol() {
300 let mode = reader.decode_mode()?;
301
302 match mode {
303 Mode::Pass => {
305 ctx.push_pixels(ctx.b2() - ctx.a0().unwrap_or(0));
306 ctx.update_b();
307 }
309 Mode::Vertical(i) => {
311 let b1 = ctx.b1();
312 let a1 = if i >= 0 {
313 b1.checked_add(i as u32).ok_or(DecodeError::Overflow)?
314 } else {
315 b1.checked_sub((-i) as u32).ok_or(DecodeError::Overflow)?
316 };
317
318 let a0 = ctx.a0().unwrap_or(0);
319
320 ctx.push_pixels(a1.checked_sub(a0).ok_or(DecodeError::Overflow)?);
321 ctx.color = ctx.color.opposite();
322
323 ctx.update_b();
324 }
325 Mode::Horizontal => {
327 let a0a1 = reader.decode_run(ctx.color)?;
328 ctx.push_pixels(a0a1);
329 ctx.color = ctx.color.opposite();
330
331 let a1a2 = reader.decode_run(ctx.color)?;
332 ctx.push_pixels(a1a2);
333 ctx.color = ctx.color.opposite();
334
335 ctx.update_b();
336 }
337 }
338 }
339
340 Ok(())
341}
342
343struct DecoderContext<'a, T: Decoder> {
344 ref_changes: Vec<ColorChange>,
346 ref_pos: u32,
348 b1_idx: u32,
350 coding_changes: Vec<ColorChange>,
352 pixels_decoded: u32,
354 decoder: &'a mut T,
356 line_width: u32,
358 color: Color,
360 decoded_rows: u32,
362 settings: &'a DecodeSettings,
364 invert_black: bool,
366}
367
368impl<'a, T: Decoder> DecoderContext<'a, T> {
369 fn new(decoder: &'a mut T, settings: &'a DecodeSettings) -> Self {
370 Self {
371 ref_changes: vec![],
372 ref_pos: 0,
373 b1_idx: 0,
374 coding_changes: Vec::new(),
375 pixels_decoded: 0,
376 decoder,
377 line_width: settings.columns,
378 color: Color::White,
380 decoded_rows: 0,
381 settings,
382 invert_black: settings.invert_black,
383 }
384 }
385
386 fn a0(&self) -> Option<u32> {
388 if self.pixels_decoded == 0 {
389 None
393 } else {
394 Some(self.pixels_decoded)
396 }
397 }
398
399 fn b1(&self) -> u32 {
402 self.ref_changes
403 .get(self.b1_idx as usize)
404 .map_or(self.line_width, |c| c.idx)
405 }
406
407 fn b2(&self) -> u32 {
409 self.ref_changes
410 .get(self.b1_idx as usize + 1)
411 .map_or(self.line_width, |c| c.idx)
412 }
413
414 #[inline(always)]
416 fn update_b(&mut self) {
417 let target_color = self.color.opposite();
419 let min_idx = self.a0().map_or(0, |a| a + 1);
421
422 self.b1_idx = self.line_width;
423
424 for i in self.ref_pos..self.ref_changes.len() as u32 {
425 let change = &self.ref_changes[i as usize];
426
427 if change.idx < min_idx {
428 self.ref_pos = i + 1;
429 continue;
430 }
431
432 if change.color == target_color {
433 self.b1_idx = i;
434 break;
435 }
436 }
437 }
438
439 #[inline(always)]
440 fn push_pixels(&mut self, count: u32) {
441 let count = count.min(self.line_width - self.pixels_decoded);
443 let white = self.color.is_white() ^ self.invert_black;
444 let mut remaining = count;
445
446 let pixels_to_boundary = (8 - (self.pixels_decoded % 8)) % 8;
448 let unaligned_pixels = remaining.min(pixels_to_boundary);
449 for _ in 0..unaligned_pixels {
450 self.decoder.push_pixel(white);
451 remaining -= 1;
452 }
453
454 let full_chunks = remaining / 8;
456 if full_chunks > 0 {
457 self.decoder.push_pixel_chunk(white, full_chunks);
458 remaining %= 8;
459 }
460
461 for _ in 0..remaining {
463 self.decoder.push_pixel(white);
464 }
465
466 if count > 0 {
471 let is_change = self
472 .coding_changes
473 .last()
474 .map_or(!self.color.is_white(), |last| last.color != self.color);
475 if is_change {
476 self.coding_changes.push(ColorChange {
477 idx: self.pixels_decoded,
478 color: self.color,
479 });
480 }
481 self.pixels_decoded += count;
482 }
483 }
484
485 fn at_eol(&self) -> bool {
486 self.a0().unwrap_or(0) == self.line_width
487 }
488
489 #[inline(always)]
490 fn next_line(&mut self, reader: &mut BitReader<'_>) -> Result<()> {
491 if self.pixels_decoded != self.settings.columns {
492 return Err(DecodeError::LineLengthMismatch);
493 }
494
495 core::mem::swap(&mut self.ref_changes, &mut self.coding_changes);
496 self.coding_changes.clear();
497 self.pixels_decoded = 0;
498 self.ref_pos = 0;
499 self.b1_idx = 0;
500 self.color = Color::White;
501 self.decoded_rows += 1;
502 self.decoder.next_line();
503
504 if self.settings.rows_are_byte_aligned {
505 reader.align();
506 }
507
508 self.update_b();
509
510 Ok(())
511 }
512}
513
514#[cfg(test)]
515mod tests {
516 use super::*;
517 use alloc::vec::Vec;
518
519 struct PixelCollector {
521 rows: Vec<Vec<bool>>,
522 current: Vec<bool>,
523 }
524
525 impl PixelCollector {
526 fn new() -> Self {
527 Self {
528 rows: Vec::new(),
529 current: Vec::new(),
530 }
531 }
532 }
533
534 impl Decoder for PixelCollector {
535 fn push_pixel(&mut self, white: bool) {
536 self.current.push(white);
537 }
538
539 fn push_pixel_chunk(&mut self, white: bool, chunk_count: u32) {
540 for _ in 0..chunk_count * 8 {
541 self.current.push(white);
542 }
543 }
544
545 fn next_line(&mut self) {
546 self.rows.push(core::mem::take(&mut self.current));
547 }
548 }
549
550 fn g3_1d(columns: u32, rows: u32) -> DecodeSettings {
551 DecodeSettings {
552 columns,
553 rows,
554 end_of_block: false,
555 end_of_line: false,
556 rows_are_byte_aligned: false,
557 encoding: EncodingMode::Group3_1D,
558 invert_black: false,
559 }
560 }
561
562 fn g4(columns: u32, rows: u32) -> DecodeSettings {
563 DecodeSettings {
564 columns,
565 rows,
566 end_of_block: false,
567 end_of_line: false,
568 rows_are_byte_aligned: false,
569 encoding: EncodingMode::Group4,
570 invert_black: false,
571 }
572 }
573
574 const W: bool = true;
575 const B: bool = false;
576
577 #[test]
580 fn g3_1d_all_white() {
581 let mut sink = PixelCollector::new();
582 decode(&[0xB0], &mut sink, &g3_1d(4, 1)).unwrap();
583 assert_eq!(sink.rows, [[W, W, W, W]]);
584 }
585
586 #[test]
589 fn g3_1d_white_then_black() {
590 let mut sink = PixelCollector::new();
591 decode(&[0x7C], &mut sink, &g3_1d(4, 1)).unwrap();
592 assert_eq!(sink.rows, [[W, W, B, B]]);
593 }
594
595 #[test]
598 fn g3_1d_all_black() {
599 let mut sink = PixelCollector::new();
600 decode(&[0x35, 0x30], &mut sink, &g3_1d(4, 1)).unwrap();
601 assert_eq!(sink.rows, [[B, B, B, B]]);
602 }
603
604 #[test]
607 fn g3_1d_two_rows() {
608 let mut sink = PixelCollector::new();
609 decode(&[0xBB], &mut sink, &g3_1d(4, 2)).unwrap();
610 assert_eq!(sink.rows, [[W, W, W, W], [W, W, W, W]]);
611 }
612
613 #[test]
616 fn group4_all_white() {
617 let mut sink = PixelCollector::new();
618 decode(&[0x80], &mut sink, &g4(4, 1)).unwrap();
619 assert_eq!(sink.rows, [[W, W, W, W]]);
620 }
621
622 #[test]
625 fn invert_black_flag() {
626 let mut sink = PixelCollector::new();
627 let settings = DecodeSettings {
628 invert_black: true,
629 ..g3_1d(1, 1)
630 };
631 decode(&[0x1C], &mut sink, &settings).unwrap();
632 assert_eq!(sink.rows, [[B]]);
633 }
634
635 #[test]
637 fn empty_data_returns_unexpected_eof() {
638 let mut sink = PixelCollector::new();
639 let err = decode(&[], &mut sink, &g3_1d(4, 1)).unwrap_err();
640 assert_eq!(err, DecodeError::UnexpectedEof);
641 }
642}