1use super::lossless::LosslessDecoder;
2use crate::os_lib::webp::decoder::DecodingError;
3use crate::os_lib::webp::io::ReadBytesExt;
4use crate::os_lib::webp::io::{BufRead, Read};
5use alloc::vec;
6use alloc::vec::Vec;
7
8use crate::os_lib::webp::alpha_blending::do_alpha_blending;
9
10#[derive(Debug, Clone)]
11pub(crate) struct WebPExtendedInfo {
12 pub(crate) alpha: bool,
13
14 pub(crate) canvas_width: u32,
15 pub(crate) canvas_height: u32,
16
17 #[allow(unused)]
18 pub(crate) icc_profile: bool,
19 pub(crate) exif_metadata: bool,
20 pub(crate) xmp_metadata: bool,
21 pub(crate) animation: bool,
22
23 pub(crate) background_color: Option<[u8; 4]>,
24 pub(crate) background_color_hint: [u8; 4],
25}
26
27#[allow(clippy::too_many_arguments)]
32pub(crate) fn composite_frame(
33 canvas: &mut [u8],
34 canvas_width: u32,
35 canvas_height: u32,
36 clear_color: Option<[u8; 4]>,
37 frame: &[u8],
38 frame_offset_x: u32,
39 frame_offset_y: u32,
40 frame_width: u32,
41 frame_height: u32,
42 frame_has_alpha: bool,
43 frame_use_alpha_blending: bool,
44 previous_frame_width: u32,
45 previous_frame_height: u32,
46 previous_frame_offset_x: u32,
47 previous_frame_offset_y: u32,
48) {
49 let frame_is_full_size = frame_offset_x == 0
50 && frame_offset_y == 0
51 && frame_width == canvas_width
52 && frame_height == canvas_height;
53
54 if frame_is_full_size && !frame_use_alpha_blending {
55 if frame_has_alpha {
56 canvas.copy_from_slice(frame);
57 } else {
58 for (input, output) in frame.chunks_exact(3).zip(canvas.chunks_exact_mut(4)) {
59 output[..3].copy_from_slice(input);
60 output[3] = 255;
61 }
62 }
63 return;
64 }
65
66 if let Some(clear_color) = clear_color {
68 match (frame_is_full_size, frame_has_alpha) {
69 (true, true) => {
70 for pixel in canvas.chunks_exact_mut(4) {
71 pixel.copy_from_slice(&clear_color);
72 }
73 }
74 (true, false) => {
75 for pixel in canvas.chunks_exact_mut(3) {
76 pixel.copy_from_slice(&clear_color[..3]);
77 }
78 }
79 (false, true) => {
80 for y in 0..previous_frame_height as usize {
81 for x in 0..previous_frame_width as usize {
82 let canvas_index = ((x + previous_frame_offset_x as usize)
83 + (y + previous_frame_offset_y as usize) * canvas_width as usize)
84 * 4;
85
86 let output = &mut canvas[canvas_index..][..4];
87 output.copy_from_slice(&clear_color);
88 }
89 }
90 }
91 (false, false) => {
92 for y in 0..previous_frame_height as usize {
93 for x in 0..previous_frame_width as usize {
94 let canvas_index = ((x + previous_frame_offset_x as usize)
96 + (y + previous_frame_offset_y as usize) * canvas_width as usize)
97 * 3;
98
99 let output = &mut canvas[canvas_index..][..3];
100 output.copy_from_slice(&clear_color[..3]);
101 }
102 }
103 }
104 }
105 }
106
107 let width = frame_width.min(canvas_width.saturating_sub(frame_offset_x)) as usize;
108 let height = frame_height.min(canvas_height.saturating_sub(frame_offset_y)) as usize;
109
110 if frame_has_alpha && frame_use_alpha_blending {
111 for y in 0..height {
112 for x in 0..width {
113 let frame_index = (x + y * frame_width as usize) * 4;
114 let canvas_index = ((x + frame_offset_x as usize)
115 + (y + frame_offset_y as usize) * canvas_width as usize)
116 * 4;
117
118 let input = &frame[frame_index..][..4];
119 let output = &mut canvas[canvas_index..][..4];
120
121 let blended =
122 do_alpha_blending(input.try_into().unwrap(), output.try_into().unwrap());
123 output.copy_from_slice(&blended);
124 }
125 }
126 } else if frame_has_alpha {
127 for y in 0..height {
128 let frame_index = (y * frame_width as usize) * 4;
129 let canvas_index = (frame_offset_x as usize
130 + (y + frame_offset_y as usize) * canvas_width as usize)
131 * 4;
132
133 canvas[canvas_index..][..width * 4].copy_from_slice(&frame[frame_index..][..width * 4]);
134 }
135 } else {
136 for y in 0..height {
137 let index = (y * frame_width as usize) * 3;
138 let canvas_index = (frame_offset_x as usize
139 + (y + frame_offset_y as usize) * canvas_width as usize)
140 * 4;
141 let input = &frame[index..][..width * 3];
142 let output = &mut canvas[canvas_index..][..width * 4];
143
144 for (input, output) in input.chunks_exact(3).zip(output.chunks_exact_mut(4)) {
145 output[..3].copy_from_slice(input);
146 output[3] = 255;
147 }
148 }
149 }
150}
151
152pub(crate) fn get_alpha_predictor(
153 x: usize,
154 y: usize,
155 width: usize,
156 filtering_method: FilteringMethod,
157 image_slice: &[u8],
158) -> u8 {
159 match filtering_method {
160 FilteringMethod::None => 0,
161 FilteringMethod::Horizontal => {
162 if x == 0 && y == 0 {
163 0
164 } else if x == 0 {
165 let index = (y - 1) * width + x;
166 image_slice[index * 4 + 3]
167 } else {
168 let index = y * width + x - 1;
169 image_slice[index * 4 + 3]
170 }
171 }
172 FilteringMethod::Vertical => {
173 if x == 0 && y == 0 {
174 0
175 } else if y == 0 {
176 let index = y * width + x - 1;
177 image_slice[index * 4 + 3]
178 } else {
179 let index = (y - 1) * width + x;
180 image_slice[index * 4 + 3]
181 }
182 }
183 FilteringMethod::Gradient => {
184 let (left, top, top_left) = match (x, y) {
185 (0, 0) => (0, 0, 0),
186 (0, y) => {
187 let above_index = (y - 1) * width + x;
188 let val = image_slice[above_index * 4 + 3];
189 (val, val, val)
190 }
191 (x, 0) => {
192 let before_index = y * width + x - 1;
193 let val = image_slice[before_index * 4 + 3];
194 (val, val, val)
195 }
196 (x, y) => {
197 let left_index = y * width + x - 1;
198 let left = image_slice[left_index * 4 + 3];
199 let top_index = (y - 1) * width + x;
200 let top = image_slice[top_index * 4 + 3];
201 let top_left_index = (y - 1) * width + x - 1;
202 let top_left = image_slice[top_left_index * 4 + 3];
203
204 (left, top, top_left)
205 }
206 };
207
208 let combination = i16::from(left) + i16::from(top) - i16::from(top_left);
209 i16::clamp(combination, 0, 255).try_into().unwrap()
210 }
211 }
212}
213
214pub(crate) fn read_extended_header<R: Read>(
215 reader: &mut R,
216) -> Result<WebPExtendedInfo, DecodingError> {
217 let chunk_flags = reader.read_u8()?;
218
219 let icc_profile = chunk_flags & 0b00100000 != 0;
220 let alpha = chunk_flags & 0b00010000 != 0;
221 let exif_metadata = chunk_flags & 0b00001000 != 0;
222 let xmp_metadata = chunk_flags & 0b00000100 != 0;
223 let animation = chunk_flags & 0b00000010 != 0;
224
225 let _reserved_bytes = read_3_bytes(reader)?;
227
228 let canvas_width = read_3_bytes(reader)? + 1;
229 let canvas_height = read_3_bytes(reader)? + 1;
230
231 if u32::checked_mul(canvas_width, canvas_height).is_none() {
233 return Err(DecodingError::ImageTooLarge);
234 }
235
236 let info = WebPExtendedInfo {
237 icc_profile,
238 alpha,
239 exif_metadata,
240 xmp_metadata,
241 animation,
242 canvas_width,
243 canvas_height,
244 background_color_hint: [0; 4],
245 background_color: None,
246 };
247
248 Ok(info)
249}
250
251pub(crate) fn read_3_bytes<R: Read>(reader: &mut R) -> Result<u32, DecodingError> {
252 let mut buffer: [u8; 3] = [0; 3];
253 reader.read_exact(&mut buffer)?;
254 let value: u32 =
255 (u32::from(buffer[2]) << 16) | (u32::from(buffer[1]) << 8) | u32::from(buffer[0]);
256 Ok(value)
257}
258
259#[derive(Debug)]
260pub(crate) struct AlphaChunk {
261 _preprocessing: bool,
262 pub(crate) filtering_method: FilteringMethod,
263 pub(crate) data: Vec<u8>,
264}
265
266#[derive(Debug, Copy, Clone)]
267pub(crate) enum FilteringMethod {
268 None,
269 Horizontal,
270 Vertical,
271 Gradient,
272}
273
274pub(crate) fn read_alpha_chunk<R: BufRead>(
275 reader: &mut R,
276 width: u16,
277 height: u16,
278) -> Result<AlphaChunk, DecodingError> {
279 let info_byte = reader.read_u8()?;
280
281 let preprocessing = (info_byte & 0b00110000) >> 4;
282 let filtering = (info_byte & 0b00001100) >> 2;
283 let compression = info_byte & 0b00000011;
284
285 let preprocessing = match preprocessing {
286 0 => false,
287 1 => true,
288 _ => return Err(DecodingError::InvalidAlphaPreprocessing),
289 };
290
291 let filtering_method = match filtering {
292 0 => FilteringMethod::None,
293 1 => FilteringMethod::Horizontal,
294 2 => FilteringMethod::Vertical,
295 3 => FilteringMethod::Gradient,
296 _ => unreachable!(),
297 };
298
299 let lossless_compression = match compression {
300 0 => false,
301 1 => true,
302 _ => return Err(DecodingError::InvalidCompressionMethod),
303 };
304
305 let data = if lossless_compression {
306 let mut decoder = LosslessDecoder::new(reader);
307
308 let mut data = vec![0; usize::from(width) * usize::from(height) * 4];
309 decoder.decode_frame(u32::from(width), u32::from(height), true, &mut data)?;
310
311 let mut green = vec![0; usize::from(width) * usize::from(height)];
312 for (rgba_val, green_val) in data.chunks_exact(4).zip(green.iter_mut()) {
313 *green_val = rgba_val[1];
314 }
315 green
316 } else {
317 let mut framedata = vec![0; width as usize * height as usize];
318 reader.read_exact(&mut framedata)?;
319 framedata
320 };
321
322 let chunk = AlphaChunk {
323 _preprocessing: preprocessing,
324 filtering_method,
325 data,
326 };
327
328 Ok(chunk)
329}