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 fn split_run(written: u32, count: u32) -> (u32, u32, u32) {
134 let to_boundary = (8 - (written % 8)) % 8;
135 let prefix = count.min(to_boundary);
136 let rest = count - prefix;
137 (prefix, rest / 8, rest % 8)
138}
139
140pub trait Decoder {
142 fn push_pixels(&mut self, white: bool, count: u32);
144 fn next_line(&mut self);
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150pub(crate) enum Color {
151 White,
153 Black,
155}
156
157impl Color {
158 #[inline(always)]
160 fn opposite(self) -> Self {
161 match self {
162 Self::White => Self::Black,
163 Self::Black => Self::White,
164 }
165 }
166
167 #[inline(always)]
169 fn is_white(self) -> bool {
170 matches!(self, Self::White)
171 }
172}
173
174#[derive(Clone, Copy)]
176struct ColorChange {
177 idx: u32,
178 color: Color,
179}
180
181pub fn decode(data: &[u8], decoder: &mut impl Decoder, ctx: &mut DecoderContext) -> Result<usize> {
191 ctx.reset();
192 let mut reader = BitReader::new(data);
193
194 match ctx.settings.encoding {
195 EncodingMode::Group4 => decode_group4(ctx, &mut reader, decoder)?,
196 EncodingMode::Group3_1D => decode_group3_1d(ctx, &mut reader, decoder)?,
197 EncodingMode::Group3_2D { .. } => decode_group3_2d(ctx, &mut reader, decoder)?,
198 }
199
200 reader.align();
201 Ok(reader.byte_pos())
202}
203
204fn decode_group3_1d(
206 ctx: &mut DecoderContext,
207 reader: &mut BitReader<'_>,
208 decoder: &mut impl Decoder,
209) -> Result<()> {
210 let _ = reader.read_eol_if_available();
213
214 loop {
215 decode_1d_line(ctx, reader, decoder)?;
216 ctx.next_line(reader, decoder)?;
217
218 if group3_check_eob(ctx, reader) {
219 break;
220 }
221 }
222
223 Ok(())
224}
225
226fn decode_group3_2d(
228 ctx: &mut DecoderContext,
229 reader: &mut BitReader<'_>,
230 decoder: &mut impl Decoder,
231) -> Result<()> {
232 let _ = reader.read_eol_if_available();
235
236 loop {
237 let tag_bit = reader.read_bit()?;
238
239 if tag_bit == 1 {
240 decode_1d_line(ctx, reader, decoder)?;
241 } else {
242 decode_2d_line(ctx, reader, decoder)?;
243 }
244
245 ctx.next_line(reader, decoder)?;
246
247 if group3_check_eob(ctx, reader) {
248 break;
249 }
250 }
251
252 Ok(())
253}
254
255fn group3_check_eob(ctx: &mut DecoderContext, reader: &mut BitReader<'_>) -> bool {
257 let eol_count = reader.read_eol_if_available();
258
259 if ctx.settings.end_of_block && eol_count >= 6 {
264 return true;
265 }
266
267 if ctx.decoded_rows == ctx.settings.rows || reader.at_end() {
268 return true;
269 }
270
271 false
272}
273
274fn decode_group4(
275 ctx: &mut DecoderContext,
276 reader: &mut BitReader<'_>,
277 decoder: &mut impl Decoder,
278) -> Result<()> {
279 loop {
280 if ctx.settings.end_of_block && reader.peak_bits(24) == Ok(EOFB) {
281 reader.read_bits(24)?;
282 break;
283 }
284
285 if ctx.decoded_rows == ctx.settings.rows || reader.at_end() {
286 break;
287 }
288
289 decode_2d_line(ctx, reader, decoder)?;
290 ctx.next_line(reader, decoder)?;
291 }
292
293 Ok(())
294}
295
296#[inline(always)]
298fn decode_1d_line(
299 ctx: &mut DecoderContext,
300 reader: &mut BitReader<'_>,
301 decoder: &mut impl Decoder,
302) -> Result<()> {
303 while !ctx.at_eol() {
304 let run_length = reader.decode_run(ctx.color)?;
305 ctx.push_pixels(decoder, run_length);
306 ctx.color = ctx.color.opposite();
307 }
308
309 Ok(())
310}
311
312#[inline(always)]
314fn decode_2d_line(
315 ctx: &mut DecoderContext,
316 reader: &mut BitReader<'_>,
317 decoder: &mut impl Decoder,
318) -> Result<()> {
319 while !ctx.at_eol() {
320 let mode = reader.decode_mode()?;
321
322 match mode {
323 Mode::Pass => {
325 ctx.push_pixels(decoder, ctx.b2() - ctx.a0().unwrap_or(0));
326 ctx.update_b();
327 }
329 Mode::Vertical(i) => {
331 let b1 = ctx.b1();
332 let a1 = if i >= 0 {
333 b1.checked_add(i as u32).ok_or(DecodeError::Overflow)?
334 } else {
335 b1.checked_sub((-i) as u32).ok_or(DecodeError::Overflow)?
336 };
337
338 let a0 = ctx.a0().unwrap_or(0);
339
340 ctx.push_pixels(decoder, a1.checked_sub(a0).ok_or(DecodeError::Overflow)?);
341 ctx.color = ctx.color.opposite();
342
343 ctx.update_b();
344 }
345 Mode::Horizontal => {
347 let a0a1 = reader.decode_run(ctx.color)?;
348 ctx.push_pixels(decoder, a0a1);
349 ctx.color = ctx.color.opposite();
350
351 let a1a2 = reader.decode_run(ctx.color)?;
352 ctx.push_pixels(decoder, a1a2);
353 ctx.color = ctx.color.opposite();
354
355 ctx.update_b();
356 }
357 }
358 }
359
360 Ok(())
361}
362
363pub struct DecoderContext {
365 ref_changes: Vec<ColorChange>,
367 ref_pos: u32,
369 b1_idx: u32,
371 coding_changes: Vec<ColorChange>,
373 pixels_decoded: u32,
375 line_width: u32,
377 color: Color,
379 decoded_rows: u32,
381 settings: DecodeSettings,
383 invert_black: bool,
385}
386
387impl DecoderContext {
388 pub fn new(settings: DecodeSettings) -> Self {
390 Self {
391 ref_changes: vec![],
392 ref_pos: 0,
393 b1_idx: 0,
394 coding_changes: Vec::new(),
395 pixels_decoded: 0,
396 line_width: settings.columns,
397 color: Color::White,
399 decoded_rows: 0,
400 settings,
401 invert_black: settings.invert_black,
402 }
403 }
404
405 fn reset(&mut self) {
406 self.ref_changes.clear();
407 self.ref_pos = 0;
408 self.b1_idx = 0;
409 self.coding_changes.clear();
410 self.pixels_decoded = 0;
411 self.line_width = self.settings.columns;
412 self.color = Color::White;
413 self.decoded_rows = 0;
414 self.invert_black = self.settings.invert_black;
415 }
416
417 #[inline(always)]
419 fn a0(&self) -> Option<u32> {
420 if self.pixels_decoded == 0 {
421 None
425 } else {
426 Some(self.pixels_decoded)
428 }
429 }
430
431 #[inline(always)]
434 fn b1(&self) -> u32 {
435 self.ref_changes
436 .get(self.b1_idx as usize)
437 .map_or(self.line_width, |c| c.idx)
438 }
439
440 #[inline(always)]
442 fn b2(&self) -> u32 {
443 self.ref_changes
444 .get(self.b1_idx as usize + 1)
445 .map_or(self.line_width, |c| c.idx)
446 }
447
448 #[inline(always)]
450 fn update_b(&mut self) {
451 let target_color = self.color.opposite();
453 let min_idx = self.a0().map_or(0, |a| a + 1);
455
456 self.b1_idx = self.line_width;
457
458 for i in self.ref_pos..self.ref_changes.len() as u32 {
459 let change = &self.ref_changes[i as usize];
460
461 if change.idx < min_idx {
462 self.ref_pos = i + 1;
463 continue;
464 }
465
466 if change.color == target_color {
467 self.b1_idx = i;
468 break;
469 }
470 }
471 }
472
473 #[inline(always)]
474 fn push_pixels(&mut self, decoder: &mut impl Decoder, count: u32) {
475 let count = count.min(self.line_width - self.pixels_decoded);
477 let white = self.color.is_white() ^ self.invert_black;
478 decoder.push_pixels(white, count);
479
480 if count > 0 {
485 let is_change = self
486 .coding_changes
487 .last()
488 .map_or(!self.color.is_white(), |last| last.color != self.color);
489 if is_change {
490 self.coding_changes.push(ColorChange {
491 idx: self.pixels_decoded,
492 color: self.color,
493 });
494 }
495 self.pixels_decoded += count;
496 }
497 }
498
499 #[inline(always)]
500 fn at_eol(&self) -> bool {
501 self.a0().unwrap_or(0) == self.line_width
502 }
503
504 #[inline(always)]
505 fn next_line(&mut self, reader: &mut BitReader<'_>, decoder: &mut impl Decoder) -> Result<()> {
506 if self.pixels_decoded != self.settings.columns {
507 return Err(DecodeError::LineLengthMismatch);
508 }
509
510 core::mem::swap(&mut self.ref_changes, &mut self.coding_changes);
511 self.coding_changes.clear();
512 self.pixels_decoded = 0;
513 self.ref_pos = 0;
514 self.b1_idx = 0;
515 self.color = Color::White;
516 self.decoded_rows += 1;
517 decoder.next_line();
518
519 if self.settings.rows_are_byte_aligned {
520 reader.align();
521 }
522
523 self.update_b();
524
525 Ok(())
526 }
527}
528
529#[cfg(test)]
530mod tests {
531 use super::*;
532 use alloc::vec::Vec;
533
534 struct PixelCollector {
536 rows: Vec<Vec<bool>>,
537 current: Vec<bool>,
538 }
539
540 impl PixelCollector {
541 fn new() -> Self {
542 Self {
543 rows: Vec::new(),
544 current: Vec::new(),
545 }
546 }
547 }
548
549 impl Decoder for PixelCollector {
550 fn push_pixels(&mut self, white: bool, count: u32) {
551 for _ in 0..count {
552 self.current.push(white);
553 }
554 }
555
556 fn next_line(&mut self) {
557 self.rows.push(core::mem::take(&mut self.current));
558 }
559 }
560
561 fn g3_1d(columns: u32, rows: u32) -> DecodeSettings {
562 DecodeSettings {
563 columns,
564 rows,
565 end_of_block: false,
566 end_of_line: false,
567 rows_are_byte_aligned: false,
568 encoding: EncodingMode::Group3_1D,
569 invert_black: false,
570 }
571 }
572
573 fn g4(columns: u32, rows: u32) -> DecodeSettings {
574 DecodeSettings {
575 columns,
576 rows,
577 end_of_block: false,
578 end_of_line: false,
579 rows_are_byte_aligned: false,
580 encoding: EncodingMode::Group4,
581 invert_black: false,
582 }
583 }
584
585 const W: bool = true;
586 const B: bool = false;
587
588 #[test]
591 fn g3_1d_all_white() {
592 let mut sink = PixelCollector::new();
593 decode(&[0xB0], &mut sink, &mut DecoderContext::new(g3_1d(4, 1))).unwrap();
594 assert_eq!(sink.rows, [[W, W, W, W]]);
595 }
596
597 #[test]
600 fn g3_1d_white_then_black() {
601 let mut sink = PixelCollector::new();
602 decode(&[0x7C], &mut sink, &mut DecoderContext::new(g3_1d(4, 1))).unwrap();
603 assert_eq!(sink.rows, [[W, W, B, B]]);
604 }
605
606 #[test]
609 fn g3_1d_all_black() {
610 let mut sink = PixelCollector::new();
611 decode(
612 &[0x35, 0x30],
613 &mut sink,
614 &mut DecoderContext::new(g3_1d(4, 1)),
615 )
616 .unwrap();
617 assert_eq!(sink.rows, [[B, B, B, B]]);
618 }
619
620 #[test]
623 fn g3_1d_two_rows() {
624 let mut sink = PixelCollector::new();
625 decode(&[0xBB], &mut sink, &mut DecoderContext::new(g3_1d(4, 2))).unwrap();
626 assert_eq!(sink.rows, [[W, W, W, W], [W, W, W, W]]);
627 }
628
629 #[test]
632 fn group4_all_white() {
633 let mut sink = PixelCollector::new();
634 decode(&[0x80], &mut sink, &mut DecoderContext::new(g4(4, 1))).unwrap();
635 assert_eq!(sink.rows, [[W, W, W, W]]);
636 }
637
638 #[test]
641 fn invert_black_flag() {
642 let mut sink = PixelCollector::new();
643 let settings = DecodeSettings {
644 invert_black: true,
645 ..g3_1d(1, 1)
646 };
647 decode(&[0x1C], &mut sink, &mut DecoderContext::new(settings)).unwrap();
648 assert_eq!(sink.rows, [[B]]);
649 }
650
651 #[test]
653 fn empty_data_returns_unexpected_eof() {
654 let mut sink = PixelCollector::new();
655 let err = decode(&[], &mut sink, &mut DecoderContext::new(g3_1d(4, 1))).unwrap_err();
656 assert_eq!(err, DecodeError::UnexpectedEof);
657 }
658}
659
660#[cfg(test)]
661mod split_run_tests {
662 use super::split_run;
663
664 #[test]
670 fn the_parts_always_reconstruct_the_run() {
671 for written in 0..32u32 {
672 for count in 0..200u32 {
673 let (prefix, bytes, tail) = split_run(written, count);
674 assert_eq!(
675 prefix + bytes * 8 + tail,
676 count,
677 "written={written} count={count} split into {prefix}+{bytes}*8+{tail}"
678 );
679 assert!(tail < 8, "tail {tail} should have been a whole byte");
680 assert!(
681 prefix == count || (written + prefix) % 8 == 0,
682 "written={written} count={count}: prefix {prefix} left the writer unaligned"
683 );
684 }
685 }
686 }
687
688 #[test]
691 fn an_aligned_writer_gets_no_prefix() {
692 for written in [0u32, 8, 16, 800] {
693 assert_eq!(split_run(written, 24), (0, 3, 0), "written={written}");
694 }
695 }
696}