1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::ops::Range;

use crate::adam7::{Adam7Info, Adam7Iterator};

/// Describes which interlacing algorithm applies to a decoded row.
///
/// PNG (2003) specifies two interlace modes, but reserves future extensions.
///
/// See also [Reader.next_interlaced_row](crate::Reader::next_interlaced_row).
#[derive(Clone, Copy, Debug)]
pub enum InterlaceInfo {
    /// The `null` method means no interlacing.
    Null(NullInfo),
    /// [The `Adam7` algorithm](https://en.wikipedia.org/wiki/Adam7_algorithm) derives its name
    /// from doing 7 passes over the image, only decoding a subset of all pixels in each pass.
    /// The following table shows pictorially what parts of each 8x8 area of the image is found in
    /// each pass:
    ///
    /// ```txt
    /// 1 6 4 6 2 6 4 6
    /// 7 7 7 7 7 7 7 7
    /// 5 6 5 6 5 6 5 6
    /// 7 7 7 7 7 7 7 7
    /// 3 6 4 6 3 6 4 6
    /// 7 7 7 7 7 7 7 7
    /// 5 6 5 6 5 6 5 6
    /// 7 7 7 7 7 7 7 7
    /// ```
    Adam7(Adam7Info),
}

#[derive(Clone, Copy, Debug)]
pub struct NullInfo {
    line: u32,
}

impl InterlaceInfo {
    pub(crate) fn line_number(&self) -> u32 {
        match self {
            InterlaceInfo::Null(NullInfo { line }) => *line,
            InterlaceInfo::Adam7(Adam7Info { line, .. }) => *line,
        }
    }

    pub(crate) fn get_adam7_info(&self) -> Option<&Adam7Info> {
        match self {
            InterlaceInfo::Null(_) => None,
            InterlaceInfo::Adam7(adam7info) => Some(adam7info),
        }
    }
}

pub(crate) struct InterlaceInfoIter(IterImpl);

impl InterlaceInfoIter {
    pub fn empty() -> Self {
        Self(IterImpl::None(0..0))
    }

    pub fn new(width: u32, height: u32, interlaced: bool) -> Self {
        if interlaced {
            Self(IterImpl::Adam7(Adam7Iterator::new(width, height)))
        } else {
            Self(IterImpl::None(0..height))
        }
    }
}

impl Iterator for InterlaceInfoIter {
    type Item = InterlaceInfo;

    fn next(&mut self) -> Option<InterlaceInfo> {
        match self.0 {
            IterImpl::Adam7(ref mut adam7) => Some(InterlaceInfo::Adam7(adam7.next()?)),
            IterImpl::None(ref mut height) => Some(InterlaceInfo::Null(NullInfo {
                line: height.next()?,
            })),
        }
    }
}

enum IterImpl {
    None(Range<u32>),
    Adam7(Adam7Iterator),
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn null() {
        assert_eq!(
            InterlaceInfoIter::new(8, 8, false)
                .map(|info| info.line_number())
                .collect::<Vec<_>>(),
            vec![0, 1, 2, 3, 4, 5, 6, 7],
        );
    }

    #[test]
    fn adam7() {
        assert_eq!(
            InterlaceInfoIter::new(8, 8, true)
                .map(|info| info.line_number())
                .collect::<Vec<_>>(),
            vec![
                0, // pass 1
                0, // pass 2
                0, // pass 3
                0, 1, // pass 4
                0, 1, // pass 5
                0, 1, 2, 3, // pass 6
                0, 1, 2, 3, // pass 7
            ],
        );
    }

    #[test]
    fn empty() {
        assert_eq!(
            InterlaceInfoIter::empty()
                .map(|info| info.line_number())
                .collect::<Vec<_>>(),
            vec![],
        );
    }
}