png/decoder/
interlace_info.rs

1use std::ops::Range;
2
3use crate::adam7::{Adam7Info, Adam7Iterator};
4
5/// Describes which interlacing algorithm applies to a decoded row.
6///
7/// PNG (2003) specifies two interlace modes, but reserves future extensions.
8///
9/// See also [Reader.next_interlaced_row](crate::Reader::next_interlaced_row).
10#[derive(Clone, Copy, Debug)]
11pub enum InterlaceInfo {
12    /// The `null` method means no interlacing.
13    Null(NullInfo),
14    /// [The `Adam7` algorithm](https://en.wikipedia.org/wiki/Adam7_algorithm) derives its name
15    /// from doing 7 passes over the image, only decoding a subset of all pixels in each pass.
16    /// The following table shows pictorially what parts of each 8x8 area of the image is found in
17    /// each pass:
18    ///
19    /// ```txt
20    /// 1 6 4 6 2 6 4 6
21    /// 7 7 7 7 7 7 7 7
22    /// 5 6 5 6 5 6 5 6
23    /// 7 7 7 7 7 7 7 7
24    /// 3 6 4 6 3 6 4 6
25    /// 7 7 7 7 7 7 7 7
26    /// 5 6 5 6 5 6 5 6
27    /// 7 7 7 7 7 7 7 7
28    /// ```
29    Adam7(Adam7Info),
30}
31
32#[derive(Clone, Copy, Debug)]
33pub struct NullInfo {
34    line: u32,
35}
36
37impl InterlaceInfo {
38    pub(crate) fn line_number(&self) -> u32 {
39        match self {
40            InterlaceInfo::Null(NullInfo { line }) => *line,
41            InterlaceInfo::Adam7(Adam7Info { line, .. }) => *line,
42        }
43    }
44
45    pub(crate) fn get_adam7_info(&self) -> Option<&Adam7Info> {
46        match self {
47            InterlaceInfo::Null(_) => None,
48            InterlaceInfo::Adam7(adam7info) => Some(adam7info),
49        }
50    }
51}
52
53pub(crate) struct InterlaceInfoIter(IterImpl);
54
55impl InterlaceInfoIter {
56    pub fn empty() -> Self {
57        Self(IterImpl::None(0..0))
58    }
59
60    pub fn new(width: u32, height: u32, interlaced: bool) -> Self {
61        if interlaced {
62            Self(IterImpl::Adam7(Adam7Iterator::new(width, height)))
63        } else {
64            Self(IterImpl::None(0..height))
65        }
66    }
67}
68
69impl Iterator for InterlaceInfoIter {
70    type Item = InterlaceInfo;
71
72    fn next(&mut self) -> Option<InterlaceInfo> {
73        match self.0 {
74            IterImpl::Adam7(ref mut adam7) => Some(InterlaceInfo::Adam7(adam7.next()?)),
75            IterImpl::None(ref mut height) => Some(InterlaceInfo::Null(NullInfo {
76                line: height.next()?,
77            })),
78        }
79    }
80}
81
82enum IterImpl {
83    None(Range<u32>),
84    Adam7(Adam7Iterator),
85}
86
87#[cfg(test)]
88mod test {
89    use super::*;
90
91    #[test]
92    fn null() {
93        assert_eq!(
94            InterlaceInfoIter::new(8, 8, false)
95                .map(|info| info.line_number())
96                .collect::<Vec<_>>(),
97            vec![0, 1, 2, 3, 4, 5, 6, 7],
98        );
99    }
100
101    #[test]
102    fn adam7() {
103        assert_eq!(
104            InterlaceInfoIter::new(8, 8, true)
105                .map(|info| info.line_number())
106                .collect::<Vec<_>>(),
107            vec![
108                0, // pass 1
109                0, // pass 2
110                0, // pass 3
111                0, 1, // pass 4
112                0, 1, // pass 5
113                0, 1, 2, 3, // pass 6
114                0, 1, 2, 3, // pass 7
115            ],
116        );
117    }
118
119    #[test]
120    fn empty() {
121        assert_eq!(
122            InterlaceInfoIter::empty()
123                .map(|info| info.line_number())
124                .collect::<Vec<_>>(),
125            vec![],
126        );
127    }
128}