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