webp_rust/decoder/
alpha.rs1use crate::decoder::lossless::decode_lossless_vp8l_to_argb;
4use crate::decoder::DecoderError;
5
6const ALPHA_HEADER_LEN: usize = 1;
7const ALPHA_NO_COMPRESSION: u8 = 0;
8const ALPHA_LOSSLESS_COMPRESSION: u8 = 1;
9const ALPHA_PREPROCESSED_LEVELS: u8 = 2;
10const ALPHA_FILTER_NONE: u8 = 0;
11const ALPHA_FILTER_HORIZONTAL: u8 = 1;
12const ALPHA_FILTER_VERTICAL: u8 = 2;
13const ALPHA_FILTER_GRADIENT: u8 = 3;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct AlphaHeader {
18 pub compression: u8,
20 pub filter: u8,
22 pub preprocessing: u8,
24}
25
26pub fn parse_alpha_header(data: &[u8]) -> Result<AlphaHeader, DecoderError> {
28 let Some(&header) = data.first() else {
29 return Err(DecoderError::NotEnoughData("ALPH header"));
30 };
31
32 let reserved = header >> 6;
33 if reserved != 0 {
34 return Err(DecoderError::Bitstream("ALPH reserved bits must be zero"));
35 }
36
37 let alpha = AlphaHeader {
38 compression: header & 0x03,
39 filter: (header >> 2) & 0x03,
40 preprocessing: (header >> 4) & 0x03,
41 };
42
43 if alpha.compression > ALPHA_LOSSLESS_COMPRESSION {
44 return Err(DecoderError::Bitstream(
45 "unsupported ALPH compression method",
46 ));
47 }
48 if alpha.preprocessing > ALPHA_PREPROCESSED_LEVELS {
49 return Err(DecoderError::Bitstream(
50 "unsupported ALPH preprocessing mode",
51 ));
52 }
53
54 Ok(alpha)
55}
56
57fn gradient_predictor(left: u8, top: u8, top_left: u8) -> u8 {
58 (left as i32 + top as i32 - top_left as i32).clamp(0, 255) as u8
59}
60
61fn unfilter_row(
62 filter: u8,
63 prev: Option<&[u8]>,
64 deltas: &[u8],
65 out: &mut [u8],
66) -> Result<(), DecoderError> {
67 match filter {
68 ALPHA_FILTER_NONE => {
69 out.copy_from_slice(deltas);
70 }
71 ALPHA_FILTER_HORIZONTAL => {
72 let mut pred = prev.map_or(0, |line| line[0]);
73 for (dst, &delta) in out.iter_mut().zip(deltas.iter()) {
74 *dst = pred.wrapping_add(delta);
75 pred = *dst;
76 }
77 }
78 ALPHA_FILTER_VERTICAL => {
79 if let Some(prev) = prev {
80 for ((dst, &delta), &top) in out.iter_mut().zip(deltas.iter()).zip(prev.iter()) {
81 *dst = top.wrapping_add(delta);
82 }
83 } else {
84 unfilter_row(ALPHA_FILTER_HORIZONTAL, None, deltas, out)?;
85 }
86 }
87 ALPHA_FILTER_GRADIENT => {
88 if let Some(prev) = prev {
89 let mut top_left = prev[0];
90 let mut left = prev[0];
91 for (x, (dst, &delta)) in out.iter_mut().zip(deltas.iter()).enumerate() {
92 let top = prev[x];
93 left = delta.wrapping_add(gradient_predictor(left, top, top_left));
94 top_left = top;
95 *dst = left;
96 }
97 } else {
98 unfilter_row(ALPHA_FILTER_HORIZONTAL, None, deltas, out)?;
99 }
100 }
101 _ => return Err(DecoderError::Bitstream("invalid ALPH filter")),
102 }
103 Ok(())
104}
105
106fn unfilter_alpha(
107 alpha: &[u8],
108 filter: u8,
109 width: usize,
110 height: usize,
111) -> Result<Vec<u8>, DecoderError> {
112 let expected_len = width
113 .checked_mul(height)
114 .ok_or(DecoderError::Bitstream("alpha plane size overflow"))?;
115 if alpha.len() < expected_len {
116 return Err(DecoderError::NotEnoughData("alpha plane payload"));
117 }
118
119 let mut decoded = vec![0u8; expected_len];
120 for y in 0..height {
121 let row_start = y * width;
122 let row_end = row_start + width;
123 let (head, tail) = decoded.split_at_mut(row_start);
124 let prev = if y == 0 {
125 None
126 } else {
127 Some(&head[row_start - width..row_start])
128 };
129 unfilter_row(filter, prev, &alpha[row_start..row_end], &mut tail[..width])?;
130 }
131 Ok(decoded)
132}
133
134pub fn decode_alpha_plane(
138 data: &[u8],
139 width: usize,
140 height: usize,
141) -> Result<Vec<u8>, DecoderError> {
142 let header = parse_alpha_header(data)?;
143 let payload = data
144 .get(ALPHA_HEADER_LEN..)
145 .ok_or(DecoderError::NotEnoughData("ALPH payload"))?;
146 let pixel_count = width
147 .checked_mul(height)
148 .ok_or(DecoderError::Bitstream("alpha plane size overflow"))?;
149
150 match header.compression {
151 ALPHA_NO_COMPRESSION => {
152 if payload.len() < pixel_count {
153 return Err(DecoderError::NotEnoughData("ALPH raw payload"));
154 }
155 unfilter_alpha(&payload[..pixel_count], header.filter, width, height)
156 }
157 ALPHA_LOSSLESS_COMPRESSION => {
158 let (decoded_width, decoded_height, argb) = decode_lossless_vp8l_to_argb(payload)?;
159 if decoded_width != width || decoded_height != height {
160 return Err(DecoderError::Bitstream(
161 "ALPH VP8L dimensions do not match image size",
162 ));
163 }
164 let mut filtered = vec![0u8; pixel_count];
165 for (dst, pixel) in filtered.iter_mut().zip(argb.iter()) {
166 *dst = ((pixel >> 8) & 0xff) as u8;
167 }
168 unfilter_alpha(&filtered, header.filter, width, height)
169 }
170 _ => Err(DecoderError::Bitstream(
171 "unsupported ALPH compression method",
172 )),
173 }
174}
175
176pub fn apply_alpha_plane(rgba: &mut [u8], alpha: &[u8]) -> Result<(), DecoderError> {
178 let expected_len = alpha
179 .len()
180 .checked_mul(4)
181 .ok_or(DecoderError::Bitstream("RGBA buffer size overflow"))?;
182 if rgba.len() != expected_len {
183 return Err(DecoderError::InvalidParam(
184 "RGBA buffer length does not match alpha plane",
185 ));
186 }
187
188 for (pixel, &value) in rgba.chunks_exact_mut(4).zip(alpha.iter()) {
189 pixel[3] = value;
190 }
191 Ok(())
192}
193
194#[cfg(test)]
195mod tests {
196 use super::{decode_alpha_plane, ALPHA_FILTER_HORIZONTAL};
197
198 #[test]
199 fn decode_alpha_plane_unfilters_horizontal_rows() {
200 let width = 4usize;
201 let height = 2usize;
202 let plane = [10u8, 20, 25, 40, 5, 7, 9, 11];
203 let mut filtered = Vec::with_capacity(1 + plane.len());
204 filtered.push(ALPHA_FILTER_HORIZONTAL << 2);
205
206 filtered.push(plane[0]);
207 for x in 1..width {
208 filtered.push(plane[x].wrapping_sub(plane[x - 1]));
209 }
210 filtered.push(plane[width].wrapping_sub(plane[0]));
211 for x in 1..width {
212 filtered.push(plane[width + x].wrapping_sub(plane[width + x - 1]));
213 }
214
215 let decoded = decode_alpha_plane(&filtered, width, height).unwrap();
216
217 assert_eq!(decoded, plane);
218 }
219}