png/decoder/interlace_info.rs
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![],
);
}
}