/rust/registry/src/index.crates.io-1949cf8c6b5b557f/gif-0.14.2/src/common.rs
Line | Count | Source |
1 | | use alloc::borrow::Cow; |
2 | | use alloc::vec::Vec; |
3 | | |
4 | | #[cfg(feature = "color_quant")] |
5 | | use alloc::collections::{BTreeMap, BTreeSet}; |
6 | | |
7 | | /// Disposal method |
8 | | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
9 | | #[repr(u8)] |
10 | | pub enum DisposalMethod { |
11 | | /// `StreamingDecoder` is not required to take any action. |
12 | | Any = 0, |
13 | | /// Do not dispose. |
14 | | Keep = 1, |
15 | | /// Restore to background color. |
16 | | Background = 2, |
17 | | /// Restore to previous. |
18 | | Previous = 3, |
19 | | } |
20 | | |
21 | | impl DisposalMethod { |
22 | | /// Converts `u8` to `Option<Self>` |
23 | | #[must_use] |
24 | 1.65k | pub const fn from_u8(n: u8) -> Option<Self> { |
25 | 1.65k | match n { |
26 | 727 | 0 => Some(Self::Any), |
27 | 107 | 1 => Some(Self::Keep), |
28 | 376 | 2 => Some(Self::Background), |
29 | 263 | 3 => Some(Self::Previous), |
30 | 178 | _ => None, |
31 | | } |
32 | 1.65k | } |
33 | | } |
34 | | |
35 | | /// Known GIF block labels. |
36 | | /// |
37 | | /// Note that the block uniquely specifies the layout of bytes that follow and how they are |
38 | | /// framed. For example, the header always has a fixed length but is followed by a variable amount |
39 | | /// of additional data. An image descriptor may be followed by a local color table depending on |
40 | | /// information read in it. Therefore, it doesn't make sense to continue parsing after encountering |
41 | | /// an unknown block as the semantics of following bytes are unclear. |
42 | | /// |
43 | | /// The extension block provides a common framing for an arbitrary amount of application specific |
44 | | /// data which may be ignored. |
45 | | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
46 | | #[repr(u8)] |
47 | | pub enum Block { |
48 | | /// Image block. |
49 | | Image = 0x2C, |
50 | | /// Extension block. |
51 | | Extension = 0x21, |
52 | | /// Image trailer. |
53 | | Trailer = 0x3B, |
54 | | } |
55 | | |
56 | | impl Block { |
57 | | /// Converts `u8` to `Option<Self>` |
58 | | #[must_use] |
59 | 128k | pub const fn from_u8(n: u8) -> Option<Self> { |
60 | 128k | match n { |
61 | 5.51k | 0x2C => Some(Self::Image), |
62 | 122k | 0x21 => Some(Self::Extension), |
63 | 5 | 0x3B => Some(Self::Trailer), |
64 | 263 | _ => None, |
65 | | } |
66 | 128k | } |
67 | | } |
68 | | |
69 | | /// A newtype wrapper around an arbitrary extension ID. |
70 | | /// |
71 | | /// An extension is some amount of byte data organized in sub-blocks so that one can skip over it |
72 | | /// without knowing the semantics. Though technically you likely want to use a `Application` |
73 | | /// extension, the library tries to stay flexible here. |
74 | | /// |
75 | | /// This allows us to customize the set of impls compared to a raw `u8`. It also clarifies the |
76 | | /// intent and gives some inherent methods for interoperability with known extension types. |
77 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
78 | | pub struct AnyExtension(pub u8); |
79 | | |
80 | | /// Known GIF extension labels. |
81 | | /// |
82 | | /// These are extensions which may be interpreted by the library and to which a specification with |
83 | | /// the internal data layout is known. |
84 | | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
85 | | #[repr(u8)] |
86 | | pub enum Extension { |
87 | | /// Plain Text extension. |
88 | | /// |
89 | | /// This instructs the decoder to render a text as characters in a grid of cells, in a |
90 | | /// mono-spaced font of its choosing. This is seldom actually implemented and ignored by |
91 | | /// ImageMagick. The color is always taken from the global table which further complicates any |
92 | | /// use. No real information on the frame sequencing of this block is available in the |
93 | | /// standard. |
94 | | Text = 0x01, |
95 | | /// Control extension. |
96 | | Control = 0xF9, |
97 | | /// Comment extension. |
98 | | Comment = 0xFE, |
99 | | /// Application extension. |
100 | | /// |
101 | | /// See [ImageMagick] for an idea of commonly recognized extensions. |
102 | | /// |
103 | | /// [ImageMagick]: https://github.com/ImageMagick/ImageMagick/blob/b0b58c6303195928060f55f9c3ca8233ab7f7733/coders/gif.c#L1128 |
104 | | Application = 0xFF, |
105 | | } |
106 | | |
107 | | impl AnyExtension { |
108 | | /// Decode the label as a known extension. |
109 | | #[must_use] |
110 | 244k | pub const fn into_known(self) -> Option<Extension> { |
111 | 244k | Extension::from_u8(self.0) |
112 | 244k | } |
113 | | } |
114 | | |
115 | | impl From<Extension> for AnyExtension { |
116 | 0 | fn from(ext: Extension) -> Self { |
117 | 0 | Self(ext as u8) |
118 | 0 | } |
119 | | } |
120 | | |
121 | | impl Extension { |
122 | | /// Converts `u8` to a `Extension` if it is known. |
123 | | #[must_use] |
124 | 244k | pub const fn from_u8(n: u8) -> Option<Self> { |
125 | 244k | match n { |
126 | 1.92k | 0x01 => Some(Self::Text), |
127 | 3.35k | 0xF9 => Some(Self::Control), |
128 | 658 | 0xFE => Some(Self::Comment), |
129 | 238k | 0xFF => Some(Self::Application), |
130 | 15 | _ => None, |
131 | | } |
132 | 244k | } |
133 | | } |
134 | | |
135 | | /// A GIF frame |
136 | | #[derive(Debug, Clone)] |
137 | | pub struct Frame<'a> { |
138 | | /// Frame delay in units of 10 ms. |
139 | | pub delay: u16, |
140 | | /// Disposal method. |
141 | | pub dispose: DisposalMethod, |
142 | | /// Transparent index (if available). |
143 | | pub transparent: Option<u8>, |
144 | | /// True if the frame needs user input to be displayed. |
145 | | pub needs_user_input: bool, |
146 | | /// Offset from the top border of the canvas. |
147 | | pub top: u16, |
148 | | /// Offset from the left border of the canvas. |
149 | | pub left: u16, |
150 | | /// Width of the frame. |
151 | | pub width: u16, |
152 | | /// Height of the frame. |
153 | | pub height: u16, |
154 | | /// True if the image is interlaced. |
155 | | pub interlaced: bool, |
156 | | /// Frame local color palette if available. |
157 | | pub palette: Option<Vec<u8>>, |
158 | | /// Buffer containing the image data. |
159 | | /// Only indices unless configured differently. |
160 | | pub buffer: Cow<'a, [u8]>, |
161 | | } |
162 | | |
163 | | impl Default for Frame<'_> { |
164 | 12.6k | fn default() -> Self { |
165 | 12.6k | Frame { |
166 | 12.6k | delay: 0, |
167 | 12.6k | dispose: DisposalMethod::Keep, |
168 | 12.6k | transparent: None, |
169 | 12.6k | needs_user_input: false, |
170 | 12.6k | top: 0, |
171 | 12.6k | left: 0, |
172 | 12.6k | width: 0, |
173 | 12.6k | height: 0, |
174 | 12.6k | interlaced: false, |
175 | 12.6k | palette: None, |
176 | 12.6k | buffer: Cow::Borrowed(&[]), |
177 | 12.6k | } |
178 | 12.6k | } |
179 | | } |
180 | | |
181 | | impl Frame<'static> { |
182 | | /// Creates a frame from pixels in RGBA format. |
183 | | /// |
184 | | /// This is a lossy method. The `gif` format does not support arbitrary alpha but only a 1-bit |
185 | | /// transparency mask per pixel. Any non-zero alpha value will be interpreted as a fully opaque |
186 | | /// pixel. Additionally, only 256 colors can appear in a single frame. The palette will be |
187 | | /// reduced by the NeuQuant algorithm if necessary. Different frames have independent palettes. |
188 | | /// |
189 | | /// *Note: This method is not optimized for speed.* |
190 | | /// |
191 | | /// # Panics: |
192 | | /// * If the length of pixels does not equal `width * height * 4`. |
193 | | #[cfg(feature = "color_quant")] |
194 | | #[track_caller] |
195 | 0 | pub fn from_rgba(width: u16, height: u16, pixels: &mut [u8]) -> Self { |
196 | 0 | Frame::from_rgba_speed(width, height, pixels, 1) |
197 | 0 | } |
198 | | |
199 | | /// Creates a frame from pixels in RGBA format. |
200 | | /// |
201 | | /// `speed` is a value in the range [1, 30]. |
202 | | /// The higher the value the faster it runs at the cost of image quality. |
203 | | /// A `speed` of 10 is a good compromise between speed and quality. |
204 | | /// |
205 | | /// This is a lossy method. The `gif` format does not support arbitrary alpha but only a 1-bit |
206 | | /// transparency mask per pixel. Any non-zero alpha value will be interpreted as a fully opaque |
207 | | /// pixel. Additionally, only 256 colors can appear in a single frame. The palette will be |
208 | | /// reduced by the NeuQuant algorithm if necessary. Different frames have independent palettes. |
209 | | /// |
210 | | /// # Panics: |
211 | | /// * If the length of pixels does not equal `width * height * 4`. |
212 | | /// * If `speed < 1` or `speed > 30` |
213 | | #[cfg(feature = "color_quant")] |
214 | | #[track_caller] |
215 | 0 | pub fn from_rgba_speed(width: u16, height: u16, pixels: &mut [u8], speed: i32) -> Self { |
216 | 0 | assert_eq!(width as usize * height as usize * 4, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame"); |
217 | 0 | assert!( |
218 | 0 | speed >= 1 && speed <= 30, |
219 | 0 | "speed needs to be in the range [1, 30]" |
220 | | ); |
221 | 0 | let mut transparent: Option<[u8; 4]> = None; |
222 | 0 | for pix in pixels.chunks_exact_mut(4) { |
223 | 0 | if pix[3] != 0 { |
224 | 0 | pix[3] = 0xFF; |
225 | 0 | continue; |
226 | 0 | } |
227 | | |
228 | 0 | if let Some([r, g, b, a]) = transparent { |
229 | 0 | pix[0] = r; |
230 | 0 | pix[1] = g; |
231 | 0 | pix[2] = b; |
232 | 0 | pix[3] = a; |
233 | 0 | } else { |
234 | 0 | transparent = Some([pix[0], pix[1], pix[2], pix[3]]); |
235 | 0 | } |
236 | | } |
237 | | |
238 | | // Attempt to build a palette of all colors. If we go over 256 colors, |
239 | | // switch to the NeuQuant algorithm. |
240 | 0 | let mut colors: BTreeSet<(u8, u8, u8, u8)> = BTreeSet::new(); |
241 | 0 | for pixel in pixels.chunks_exact(4) { |
242 | 0 | if colors.insert((pixel[0], pixel[1], pixel[2], pixel[3])) && colors.len() > 256 { |
243 | | // > 256 colours, let's use NeuQuant. |
244 | 0 | let nq = color_quant::NeuQuant::new(speed, 256, pixels); |
245 | | |
246 | | return Frame { |
247 | 0 | width, |
248 | 0 | height, |
249 | | buffer: Cow::Owned( |
250 | 0 | pixels |
251 | 0 | .chunks_exact(4) |
252 | 0 | .map(|pix| nq.index_of(pix) as u8) |
253 | 0 | .collect(), |
254 | | ), |
255 | 0 | palette: Some(nq.color_map_rgb()), |
256 | 0 | transparent: transparent.map(|t| nq.index_of(&t) as u8), |
257 | 0 | ..Frame::default() |
258 | | }; |
259 | 0 | } |
260 | | } |
261 | | |
262 | | // Palette size <= 256 elements, we can build an exact palette. |
263 | 0 | let mut colors_vec: Vec<(u8, u8, u8, u8)> = colors.into_iter().collect(); |
264 | 0 | colors_vec.sort_unstable(); |
265 | 0 | let palette = colors_vec |
266 | 0 | .iter() |
267 | 0 | .flat_map(|&(r, g, b, _a)| [r, g, b]) |
268 | 0 | .collect(); |
269 | 0 | let colors_lookup: BTreeMap<(u8, u8, u8, u8), u8> = |
270 | 0 | colors_vec.into_iter().zip(0..=255).collect(); |
271 | | |
272 | 0 | let index_of = |pixel: &[u8]| { |
273 | 0 | colors_lookup |
274 | 0 | .get(&(pixel[0], pixel[1], pixel[2], pixel[3])) |
275 | 0 | .copied() |
276 | 0 | .unwrap_or(0) |
277 | 0 | }; |
278 | | |
279 | | Frame { |
280 | 0 | width, |
281 | 0 | height, |
282 | 0 | buffer: Cow::Owned(pixels.chunks_exact(4).map(index_of).collect()), |
283 | 0 | palette: Some(palette), |
284 | 0 | transparent: transparent.map(|t| index_of(&t)), |
285 | 0 | ..Frame::default() |
286 | | } |
287 | 0 | } |
288 | | |
289 | | /// Creates a frame from pixels in LumaAlpha format (grayscale pixels with transparency). |
290 | | /// |
291 | | /// This is a lossy method. The `gif` format does not support arbitrary alpha but only a 1-bit |
292 | | /// transparency mask per pixel. Any non-zero alpha value will be interpreted as a fully opaque |
293 | | /// pixel. The least used color will be used to indicate alpha and replace with the closest other color |
294 | | /// in the image. Different frames have independent palettes. |
295 | | /// |
296 | | /// # Panics: |
297 | | /// * If the length of pixels does not equal `width * height * 2`. |
298 | 0 | pub fn from_grayscale_with_alpha(width: u16, height: u16, pixels: &[u8]) -> Self { |
299 | 0 | assert_eq!(width as usize * height as usize * 2, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame"); |
300 | | |
301 | | // Input is in LumaA format. |
302 | | // Count the occurrences of all the colors, then pick the least common color as alpha. |
303 | 0 | let mut num_transparent_pixels: u32 = 0; |
304 | 0 | let mut color_frequencies: [u32; 256] = [0; 256]; |
305 | 0 | for pixel in pixels.chunks_exact(2) { |
306 | 0 | let color = pixel[0]; |
307 | 0 | let alpha = pixel[1]; |
308 | | // do not count colors in fully transparent pixels |
309 | 0 | if alpha == 0 { |
310 | 0 | num_transparent_pixels += 1; |
311 | 0 | } else { |
312 | 0 | color_frequencies[color as usize] += 1; |
313 | 0 | } |
314 | | } |
315 | | |
316 | 0 | let grayscale_palette: Vec<u8> = (0..=255).flat_map(|i| [i, i, i]).collect(); |
317 | | |
318 | | // If there were no fully transparent pixels, do not allocate a color to transparency in the GIF |
319 | | // and return immediately with the generic grayscale palette |
320 | 0 | if num_transparent_pixels == 0 { |
321 | 0 | let stripped_alpha: Vec<u8> = pixels.chunks_exact(2).map(|pixel| pixel[0]).collect(); |
322 | 0 | return Frame { |
323 | 0 | width, |
324 | 0 | height, |
325 | 0 | buffer: Cow::Owned(stripped_alpha), |
326 | 0 | palette: Some(grayscale_palette), |
327 | 0 | transparent: None, |
328 | 0 | ..Frame::default() |
329 | 0 | }; |
330 | 0 | } |
331 | | |
332 | | // Choose the color that will be our alpha color |
333 | 0 | let least_used_color = color_frequencies |
334 | 0 | .iter() |
335 | 0 | .enumerate() |
336 | 0 | .min_by_key(|(_, &value)| value) |
337 | 0 | .map(|(index, _)| index as u8) |
338 | 0 | .expect("input slice is empty"); |
339 | | |
340 | | // pick the less used color out of the neighbours as the replacement color |
341 | 0 | let replacement_color = if least_used_color == 255 { |
342 | 0 | 254 |
343 | 0 | } else if least_used_color == 0 { |
344 | 0 | 1 |
345 | 0 | } else if color_frequencies[(least_used_color - 1) as usize] |
346 | 0 | < color_frequencies[(least_used_color + 1) as usize] |
347 | | { |
348 | 0 | least_used_color - 1 |
349 | | } else { |
350 | 0 | least_used_color + 1 |
351 | | }; |
352 | | |
353 | | // Strip alpha and replace fully transparent pixels with the chosen color |
354 | 0 | let paletted: Vec<u8> = pixels |
355 | 0 | .chunks_exact(2) |
356 | 0 | .map(|pixel| { |
357 | 0 | let color = pixel[0]; |
358 | 0 | let alpha = pixel[1]; |
359 | 0 | if alpha == 0 { |
360 | 0 | least_used_color |
361 | 0 | } else if color == least_used_color { |
362 | 0 | replacement_color |
363 | | } else { |
364 | 0 | color |
365 | | } |
366 | 0 | }) |
367 | 0 | .collect(); |
368 | | |
369 | 0 | Frame { |
370 | 0 | width, |
371 | 0 | height, |
372 | 0 | buffer: Cow::Owned(paletted), |
373 | 0 | palette: Some(grayscale_palette), |
374 | 0 | transparent: Some(least_used_color), |
375 | 0 | ..Frame::default() |
376 | 0 | } |
377 | 0 | } |
378 | | |
379 | | /// Creates a frame from a palette and indexed pixels. |
380 | | /// |
381 | | /// # Panics: |
382 | | /// * If the length of pixels does not equal `width * height`. |
383 | | /// * If the length of palette > `256 * 3`. |
384 | | #[track_caller] |
385 | 0 | pub fn from_palette_pixels( |
386 | 0 | width: u16, |
387 | 0 | height: u16, |
388 | 0 | pixels: impl Into<Vec<u8>>, |
389 | 0 | palette: impl Into<Vec<u8>>, |
390 | 0 | transparent: Option<u8>, |
391 | 0 | ) -> Self { |
392 | 0 | let pixels = pixels.into(); |
393 | 0 | let palette = palette.into(); |
394 | 0 | assert_eq!( |
395 | 0 | width as usize * height as usize, |
396 | 0 | pixels.len(), |
397 | 0 | "Too many or too little pixels for the given width and height to create a GIF Frame" |
398 | | ); |
399 | 0 | assert!( |
400 | 0 | palette.len() <= 256 * 3, |
401 | 0 | "Too many palette values to create a GIF Frame" |
402 | | ); |
403 | | |
404 | 0 | Frame { |
405 | 0 | width, |
406 | 0 | height, |
407 | 0 | buffer: Cow::Owned(pixels), |
408 | 0 | palette: Some(palette), |
409 | 0 | transparent, |
410 | 0 | ..Frame::default() |
411 | 0 | } |
412 | 0 | } Unexecuted instantiation: <gif::common::Frame>::from_palette_pixels::<_, _> Unexecuted instantiation: <gif::common::Frame>::from_palette_pixels::<&[u8], alloc::vec::Vec<u8>> |
413 | | |
414 | | /// Creates a frame from indexed pixels in the global palette. |
415 | | /// |
416 | | /// # Panics: |
417 | | /// * If the length of pixels does not equal `width * height`. |
418 | | #[track_caller] |
419 | 0 | pub fn from_indexed_pixels( |
420 | 0 | width: u16, |
421 | 0 | height: u16, |
422 | 0 | pixels: impl Into<Vec<u8>>, |
423 | 0 | transparent: Option<u8>, |
424 | 0 | ) -> Self { |
425 | 0 | let pixels = pixels.into(); |
426 | 0 | assert_eq!( |
427 | 0 | width as usize * height as usize, |
428 | 0 | pixels.len(), |
429 | 0 | "Too many or too little pixels for the given width and height to create a GIF Frame" |
430 | | ); |
431 | | |
432 | 0 | Frame { |
433 | 0 | width, |
434 | 0 | height, |
435 | 0 | buffer: Cow::Owned(pixels), |
436 | 0 | palette: None, |
437 | 0 | transparent, |
438 | 0 | ..Frame::default() |
439 | 0 | } |
440 | 0 | } |
441 | | |
442 | | /// Creates a frame from pixels in RGB format. |
443 | | /// |
444 | | /// This is a lossy method. In the `gif` format only 256 colors can appear in a single frame. |
445 | | /// The palette will be reduced by the NeuQuant algorithm if necessary. Different frames have |
446 | | /// independent palettes. |
447 | | /// |
448 | | /// *Note: This method is not optimized for speed.* |
449 | | /// |
450 | | /// # Panics: |
451 | | /// * If the length of pixels does not equal `width * height * 3`. |
452 | | #[cfg(feature = "color_quant")] |
453 | | #[must_use] |
454 | | #[track_caller] |
455 | 0 | pub fn from_rgb(width: u16, height: u16, pixels: &[u8]) -> Self { |
456 | 0 | Frame::from_rgb_speed(width, height, pixels, 1) |
457 | 0 | } |
458 | | |
459 | | /// Creates a frame from pixels in RGB format. |
460 | | /// |
461 | | /// `speed` is a value in the range [1, 30]. |
462 | | /// |
463 | | /// This is a lossy method. In the `gif` format only 256 colors can appear in a single frame. |
464 | | /// The palette will be reduced by the NeuQuant algorithm if necessary. Different frames have |
465 | | /// independent palettes. |
466 | | /// |
467 | | /// The higher the value the faster it runs at the cost of image quality. |
468 | | /// A `speed` of 10 is a good compromise between speed and quality. |
469 | | /// |
470 | | /// # Panics: |
471 | | /// * If the length of pixels does not equal `width * height * 3`. |
472 | | /// * If `speed < 1` or `speed > 30` |
473 | | #[cfg(feature = "color_quant")] |
474 | | #[must_use] |
475 | | #[track_caller] |
476 | 0 | pub fn from_rgb_speed(width: u16, height: u16, pixels: &[u8], speed: i32) -> Self { |
477 | 0 | assert_eq!(width as usize * height as usize * 3, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame"); |
478 | 0 | let mut vec: Vec<u8> = Vec::new(); |
479 | 0 | vec.try_reserve_exact(pixels.len() + width as usize * height as usize) |
480 | 0 | .expect("OOM"); |
481 | 0 | for v in pixels.chunks_exact(3) { |
482 | 0 | vec.extend_from_slice(&[v[0], v[1], v[2], 0xFF]); |
483 | 0 | } |
484 | 0 | Frame::from_rgba_speed(width, height, &mut vec, speed) |
485 | 0 | } |
486 | | |
487 | | /// Leaves empty buffer and empty palette behind |
488 | | #[inline] |
489 | 5.43k | pub(crate) fn take(&mut self) -> Self { |
490 | 5.43k | Frame { |
491 | 5.43k | delay: self.delay, |
492 | 5.43k | dispose: self.dispose, |
493 | 5.43k | transparent: self.transparent, |
494 | 5.43k | needs_user_input: self.needs_user_input, |
495 | 5.43k | top: self.top, |
496 | 5.43k | left: self.left, |
497 | 5.43k | width: self.width, |
498 | 5.43k | height: self.height, |
499 | 5.43k | interlaced: self.interlaced, |
500 | 5.43k | palette: core::mem::take(&mut self.palette), |
501 | 5.43k | buffer: core::mem::replace(&mut self.buffer, Cow::Borrowed(&[])), |
502 | 5.43k | } |
503 | 5.43k | } <gif::common::Frame>::take Line | Count | Source | 489 | 5.43k | pub(crate) fn take(&mut self) -> Self { | 490 | 5.43k | Frame { | 491 | 5.43k | delay: self.delay, | 492 | 5.43k | dispose: self.dispose, | 493 | 5.43k | transparent: self.transparent, | 494 | 5.43k | needs_user_input: self.needs_user_input, | 495 | 5.43k | top: self.top, | 496 | 5.43k | left: self.left, | 497 | 5.43k | width: self.width, | 498 | 5.43k | height: self.height, | 499 | 5.43k | interlaced: self.interlaced, | 500 | 5.43k | palette: core::mem::take(&mut self.palette), | 501 | 5.43k | buffer: core::mem::replace(&mut self.buffer, Cow::Borrowed(&[])), | 502 | 5.43k | } | 503 | 5.43k | } |
Unexecuted instantiation: <gif::common::Frame>::take |
504 | | } |
505 | | |
506 | | #[test] |
507 | | #[cfg(feature = "color_quant")] |
508 | | // Creating the `colors_lookup` hashmap in Frame::from_rgba_speed panics due to |
509 | | // overflow while bypassing NeuQuant and zipping a RangeFrom with 256 colors. |
510 | | // Changing .zip(0_u8..) to .zip(0_u8..=255) fixes this issue. |
511 | | fn rgba_speed_avoid_panic_256_colors() { |
512 | | let side = 16; |
513 | | let pixel_data: Vec<u8> = (0..=255).flat_map(|a| [a, a, a]).collect(); |
514 | | let _ = Frame::from_rgb(side, side, &pixel_data); |
515 | | } |