png/
filter.rs

1use std;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4#[repr(u8)]
5pub enum FilterType {
6    NoFilter = 0,
7    Sub = 1,
8    Up = 2,
9    Avg = 3,
10    Paeth = 4
11}
12
13 impl FilterType {  
14    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
15    pub fn from_u8(n: u8) -> Option<FilterType> {
16        match n {
17            0 => Some(FilterType::NoFilter),
18            1 => Some(FilterType::Sub),
19            2 => Some(FilterType::Up),
20            3 => Some(FilterType::Avg),
21            4 => Some(FilterType::Paeth),
22            _ => None
23        }
24    }
25}
26
27fn filter_paeth(a: u8, b: u8, c: u8) -> u8 {
28    let ia = a as i16;
29    let ib = b as i16;
30    let ic = c as i16;
31
32    let p = ia + ib - ic;
33
34    let pa = (p - ia).abs();
35    let pb = (p - ib).abs();
36    let pc = (p - ic).abs();
37
38    if pa <= pb && pa <= pc {
39        a
40    } else if pb <= pc {
41        b
42    } else {
43        c
44    }
45}
46
47pub fn unfilter(filter: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) -> std::result::Result<(), &'static str> {
48    use self::FilterType::*;
49    assert!(bpp > 0);
50    let len = current.len();
51
52    match filter {
53        NoFilter => Ok(()),
54        Sub => {
55            for i in bpp..len {
56                current[i] = current[i].wrapping_add(
57                    current[i - bpp]
58                );
59            }
60            Ok(())
61        }
62        Up => {
63            if previous.len() < len {
64                Err("Filtering failed: not enough data in previous row")
65            } else {
66                for i in 0..len {
67                    current[i] = current[i].wrapping_add(
68                        previous[i]
69                    );
70                }
71                Ok(())
72            }
73        }
74        Avg => {
75            if previous.len() < len {
76                Err("Filtering failed:  not enough data in previous row")
77            } else if bpp > len {
78                Err("Filtering failed: bytes per pixel is greater than length of row")
79            } else {
80                for i in 0..bpp {
81                    current[i] = current[i].wrapping_add(
82                        previous[i] / 2
83                    );
84                }
85
86                for i in bpp..len {
87                    current[i] = current[i].wrapping_add(
88                        ((current[i - bpp] as i16 + previous[i] as i16) / 2) as u8
89                    );
90                }
91                Ok(())
92            }
93        }
94        Paeth => {
95            if previous.len() < len {
96                Err("Filtering failed: not enough data in previous row")
97            } else if bpp > len {
98                Err("Filtering failed: bytes per pixel is greater than length of row")
99            } else {
100                for i in 0..bpp {
101                    current[i] = current[i].wrapping_add(
102                        filter_paeth(0, previous[i], 0)
103                    );
104                }
105
106                for i in bpp..len {
107                    current[i] = current[i].wrapping_add(
108                        filter_paeth(current[i - bpp], previous[i], previous[i - bpp])
109                    );
110                }
111                Ok(())
112            }
113        }
114    }
115}
116
117pub fn filter(method: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) {
118    use self::FilterType::*;
119    assert!(bpp > 0);
120    let len  = current.len();
121
122    match method {
123        NoFilter => (),
124        Sub      => {
125            for i in (bpp..len).rev() {
126                current[i] = current[i].wrapping_sub(current[i - bpp]);
127            }
128        }
129        Up       => {
130            for i in 0..len {
131                current[i] = current[i].wrapping_sub(previous[i]);
132            }
133        }
134        Avg  => {
135            for i in (bpp..len).rev() {
136                current[i] = current[i].wrapping_sub(current[i - bpp].wrapping_add(previous[i]) / 2);
137            }
138
139            for i in 0..bpp {
140                current[i] = current[i].wrapping_sub(previous[i] / 2);
141            }
142        }
143        Paeth    => {
144            for i in (bpp..len).rev() {
145                current[i] = current[i].wrapping_sub(filter_paeth(current[i - bpp], previous[i], previous[i - bpp]));
146            }
147
148            for i in 0..bpp {
149                current[i] = current[i].wrapping_sub(filter_paeth(0, previous[i], 0));
150            }
151        }
152    }
153}