1use super::lzw::LzwDecoder;
2use super::{Error, Result};
3use crate::media::Rgba;
4use std::collections::VecDeque;
5use std::sync::mpsc::{Receiver, TryRecvError, sync_channel};
6
7const AHEAD: usize = 16;
8const MAX_PIXELS: usize = 1 << 28;
9const MIN_BUDGET: usize = 1 << 20;
10const PIXELS_PER_BYTE: usize = 1 << 12;
11const LZW_PIXELS_PER_BYTE: usize = 1 << 14;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum Disposal {
15 Keep,
16 Background,
17 Previous,
18}
19
20pub struct Frame<'a> {
21 pub image: &'a Rgba,
22 pub delay_cs: u16,
23 pub left: u16,
24 pub top: u16,
25 pub width: u16,
26 pub height: u16,
27 pub disposal: Disposal,
28}
29
30pub struct Animation {
31 pub width: u32,
32 pub height: u32,
33 pub loop_count: Option<u16>,
34 pub frames: Vec<(Rgba, u16)>,
35}
36
37struct Raw {
38 left: u16,
39 top: u16,
40 width: u16,
41 height: u16,
42 interlaced: bool,
43 palette: Option<(usize, usize)>,
44 transparent: Option<u8>,
45 delay_cs: u16,
46 disposal: Disposal,
47 min_code: u8,
48 data: Vec<u8>,
49 indices: Vec<u8>,
50 decoded: usize,
51}
52
53pub struct Decoder<'a> {
54 data: &'a [u8],
55 pos: usize,
56 width: u16,
57 height: u16,
58 global: Option<(usize, usize)>,
59 loop_count: Option<u16>,
60 canvas: Rgba,
61 saved: Vec<u8>,
62 last: Option<(Disposal, u16, u16, u16, u16)>,
63 queue: VecDeque<Receiver<Raw>>,
64 spare: Vec<Raw>,
65 current: Option<Raw>,
66 ended: bool,
67 gce: Option<(Disposal, u16, Option<u8>)>,
68}
69
70fn table(packed: u8) -> usize {
71 3 << ((packed & 7) + 1)
72}
73
74impl<'a> Decoder<'a> {
75 pub fn new(data: &'a [u8]) -> Result<Self> {
76 if data.len() < 13 || !(data.starts_with(b"GIF87a") || data.starts_with(b"GIF89a")) {
77 return Err(Error::NotGif);
78 }
79 let width = u16::from_le_bytes([data[6], data[7]]);
80 let height = u16::from_le_bytes([data[8], data[9]]);
81 let packed = data[10];
82 let mut pos = 13;
83 let mut global = None;
84 if packed & 0x80 != 0 {
85 let n = table(packed);
86 if pos + n > data.len() {
87 return Err(Error::Truncated);
88 }
89 global = Some((pos, n / 3));
90 pos += n;
91 }
92 if width == 0 || height == 0 {
93 return Err(Error::Invalid("zero canvas size"));
94 }
95 let pixels = width as usize * height as usize;
96 if pixels > MAX_PIXELS {
97 return Err(Error::Invalid("canvas too large"));
98 }
99 if pixels > data.len().saturating_mul(PIXELS_PER_BYTE).max(MIN_BUDGET) {
100 return Err(Error::Invalid("canvas far larger than the file"));
101 }
102 let mut d = Decoder {
103 data,
104 pos,
105 width,
106 height,
107 global,
108 loop_count: None,
109 canvas: Rgba::new(width as u32, height as u32),
110 saved: Vec::new(),
111 last: None,
112 queue: VecDeque::new(),
113 spare: Vec::new(),
114 current: None,
115 ended: false,
116 gce: None,
117 };
118 d.fill()?;
119 Ok(d)
120 }
121
122 pub fn width(&self) -> u32 {
123 self.width as u32
124 }
125
126 pub fn height(&self) -> u32 {
127 self.height as u32
128 }
129
130 pub fn loop_count(&self) -> Option<u16> {
131 self.loop_count
132 }
133
134 fn sub_blocks(&mut self, mut sink: Option<&mut Vec<u8>>) -> Result<()> {
135 loop {
136 let Some(&n) = self.data.get(self.pos) else {
137 return Err(Error::Truncated);
138 };
139 self.pos += 1;
140 if n == 0 {
141 return Ok(());
142 }
143 let end = self.pos + n as usize;
144 if end > self.data.len() {
145 if let Some(s) = sink.as_deref_mut() {
146 s.extend_from_slice(&self.data[self.pos..]);
147 }
148 self.pos = self.data.len();
149 return Err(Error::Truncated);
150 }
151 if let Some(s) = sink.as_deref_mut() {
152 s.extend_from_slice(&self.data[self.pos..end]);
153 }
154 self.pos = end;
155 }
156 }
157
158 fn extension(&mut self) -> Result<()> {
159 let Some(&label) = self.data.get(self.pos) else {
160 return Err(Error::Truncated);
161 };
162 self.pos += 1;
163 match label {
164 0xF9 => {
165 let d = self.data;
166 if self.pos + 6 <= d.len() && d[self.pos] >= 4 {
167 let p = self.pos + 1;
168 let flags = d[p];
169 let delay = u16::from_le_bytes([d[p + 1], d[p + 2]]);
170 let disposal = match (flags >> 2) & 7 {
171 2 => Disposal::Background,
172 3 => Disposal::Previous,
173 _ => Disposal::Keep,
174 };
175 let transparent = (flags & 1 != 0).then_some(d[p + 3]);
176 self.gce = Some((disposal, delay, transparent));
177 }
178 self.sub_blocks(None)
179 }
180 0xFF => {
181 let d = self.data;
182 let p = self.pos;
183 if p + 1 + 11 <= d.len() && d[p] == 11 {
184 let id = &d[p + 1..p + 12];
185 if id == b"NETSCAPE2.0" || id == b"ANIMEXTS1.0" {
186 let q = p + 12;
187 if q + 4 <= d.len() && d[q] >= 3 && d[q + 1] == 1 {
188 self.loop_count = Some(u16::from_le_bytes([d[q + 2], d[q + 3]]));
189 }
190 }
191 }
192 self.sub_blocks(None)
193 }
194 _ => self.sub_blocks(None),
195 }
196 }
197
198 fn scan(&mut self) -> Result<Option<Raw>> {
199 loop {
200 let Some(&b) = self.data.get(self.pos) else {
201 return Ok(None);
202 };
203 self.pos += 1;
204 match b {
205 0x21 => self.extension()?,
206 0x2C => return self.image().map(Some),
207 0x3B => return Ok(None),
208 _ => return Ok(None),
209 }
210 }
211 }
212
213 fn image(&mut self) -> Result<Raw> {
214 let d = self.data;
215 let p = self.pos;
216 if p + 9 > d.len() {
217 return Err(Error::Truncated);
218 }
219 let word = |i: usize| u16::from_le_bytes([d[p + i], d[p + i + 1]]);
220 let (left, top, width, height) = (word(0), word(2), word(4), word(6));
221 let packed = d[p + 8];
222 self.pos += 9;
223 let canvas = self.width as usize * self.height as usize;
224 if width as usize * height as usize > canvas.max(1 << 20) * 4 {
225 return Err(Error::Invalid("frame far larger than the canvas"));
226 }
227 let mut palette = None;
228 if packed & 0x80 != 0 {
229 let n = table(packed);
230 if self.pos + n > d.len() {
231 return Err(Error::Truncated);
232 }
233 palette = Some((self.pos, n / 3));
234 self.pos += n;
235 }
236 let Some(&min_code) = d.get(self.pos) else {
237 return Err(Error::Truncated);
238 };
239 self.pos += 1;
240 let (disposal, delay_cs, transparent) =
241 self.gce.take().unwrap_or((Disposal::Keep, 0, None));
242 let mut raw = self.spare.pop().unwrap_or_else(|| Raw {
243 left: 0,
244 top: 0,
245 width: 0,
246 height: 0,
247 interlaced: false,
248 palette: None,
249 transparent: None,
250 delay_cs: 0,
251 disposal: Disposal::Keep,
252 min_code: 0,
253 data: Vec::new(),
254 indices: Vec::new(),
255 decoded: 0,
256 });
257 raw.left = left;
258 raw.top = top;
259 raw.width = width;
260 raw.height = height;
261 raw.interlaced = packed & 0x40 != 0;
262 raw.palette = palette;
263 raw.transparent = transparent;
264 raw.delay_cs = delay_cs;
265 raw.disposal = disposal;
266 raw.min_code = min_code;
267 raw.data.clear();
268 raw.decoded = 0;
269 let mut data = std::mem::take(&mut raw.data);
270 let r = self.sub_blocks(Some(&mut data));
271 raw.data = data;
272 match r {
273 Ok(()) | Err(Error::Truncated) => Ok(raw),
274 Err(e) => Err(e),
275 }
276 }
277
278 fn fill(&mut self) -> Result<()> {
279 if self.ended {
280 return Ok(());
281 }
282 while self.queue.len() < AHEAD {
283 match self.scan() {
284 Ok(Some(mut raw)) => {
285 let (tx, rx) = sync_channel(1);
286 rayon::spawn(move || {
287 decode_raw(&mut raw);
288 let _ = tx.send(raw);
289 });
290 self.queue.push_back(rx);
291 }
292 Ok(None) | Err(Error::Truncated) => {
293 self.ended = true;
294 break;
295 }
296 Err(e) => return Err(e),
297 }
298 }
299 Ok(())
300 }
301
302 pub fn next_frame(&mut self) -> Result<Option<Frame<'_>>> {
303 if let Some(r) = self.current.take() {
304 self.spare.push(r);
305 }
306 self.fill()?;
307 let Some(rx) = self.queue.pop_front() else {
308 return Ok(None);
309 };
310 let raw = wait(rx).ok_or(Error::Invalid("frame decode task failed"))?;
311 self.dispose();
312 self.draw(&raw);
313 let frame = (
314 raw.delay_cs,
315 raw.left,
316 raw.top,
317 raw.width,
318 raw.height,
319 raw.disposal,
320 );
321 self.last = Some((raw.disposal, raw.left, raw.top, raw.width, raw.height));
322 self.current = Some(raw);
323 Ok(Some(Frame {
324 image: &self.canvas,
325 delay_cs: frame.0,
326 left: frame.1,
327 top: frame.2,
328 width: frame.3,
329 height: frame.4,
330 disposal: frame.5,
331 }))
332 }
333
334 fn clip(&self, left: u16, top: u16, w: u16, h: u16) -> (usize, usize, usize, usize) {
335 let cw = self.width as usize;
336 let ch = self.height as usize;
337 let x0 = (left as usize).min(cw);
338 let y0 = (top as usize).min(ch);
339 let x1 = (left as usize + w as usize).min(cw);
340 let y1 = (top as usize + h as usize).min(ch);
341 (x0, y0, x1, y1)
342 }
343
344 fn dispose(&mut self) {
345 let Some((disposal, left, top, w, h)) = self.last.take() else {
346 return;
347 };
348 let (x0, y0, x1, y1) = self.clip(left, top, w, h);
349 let stride = self.canvas.stride();
350 match disposal {
351 Disposal::Keep => {}
352 Disposal::Background => {
353 for y in y0..y1 {
354 self.canvas.data[y * stride + x0 * 4..y * stride + x1 * 4].fill(0);
355 }
356 }
357 Disposal::Previous => {
358 let n = (x1 - x0) * 4;
359 for (i, y) in (y0..y1).enumerate() {
360 if self.saved.len() >= (i + 1) * n {
361 self.canvas.data[y * stride + x0 * 4..y * stride + x1 * 4]
362 .copy_from_slice(&self.saved[i * n..(i + 1) * n]);
363 }
364 }
365 }
366 }
367 }
368
369 fn draw(&mut self, raw: &Raw) {
370 let (x0, y0, x1, y1) = self.clip(raw.left, raw.top, raw.width, raw.height);
371 if x0 >= x1 || y0 >= y1 {
372 return;
373 }
374 let stride = self.canvas.stride();
375 if raw.disposal == Disposal::Previous {
376 self.saved.clear();
377 for y in y0..y1 {
378 self.saved
379 .extend_from_slice(&self.canvas.data[y * stride + x0 * 4..y * stride + x1 * 4]);
380 }
381 }
382 let mut colors = [[0u8; 4]; 256];
383 let pal = raw.palette.or(self.global);
384 if let Some((at, n)) = pal {
385 for (i, c) in colors.iter_mut().enumerate().take(n.min(256)) {
386 let p = at + i * 3;
387 *c = [self.data[p], self.data[p + 1], self.data[p + 2], 255];
388 }
389 for c in colors.iter_mut().skip(n.min(256)) {
390 *c = [0, 0, 0, 255];
391 }
392 } else {
393 colors.fill([0, 0, 0, 255]);
394 }
395 let opaque = raw.transparent.is_none();
396 let t = raw.transparent.unwrap_or(0) as usize;
397 let w = raw.width as usize;
398 let full_rows = raw.decoded / w.max(1);
399 let tail = raw.decoded % w.max(1);
400 let rows = raw.height as usize;
401 for k in 0..rows.min(full_rows + 1) {
402 let y = if raw.interlaced {
403 interlaced_row(k, rows)
404 } else {
405 k
406 };
407 let cy = raw.top as usize + y;
408 if cy < y0 || cy >= y1 {
409 continue;
410 }
411 let n = if k < full_rows { w } else { tail };
412 let src = &raw.indices[k * w..k * w + n];
413 let lim = (x1 - x0).min(n);
414 let src = &src[..lim];
415 let dst = &mut self.canvas.data[cy * stride + x0 * 4..cy * stride + (x0 + lim) * 4];
416 blit(src, dst, &colors, opaque, t);
417 }
418 }
419
420 pub fn canvas(&self) -> &Rgba {
421 &self.canvas
422 }
423}
424
425crate::media::isa::dispatch! {
426 fn blit(src: &[u8], dst: &mut [u8], colors: &[[u8; 4]; 256], opaque: bool, t: usize) {
427 if opaque {
428 for (d, &i) in dst.chunks_exact_mut(4).zip(src) {
429 d.copy_from_slice(&colors[i as usize]);
430 }
431 } else {
432 for (d, &i) in dst.chunks_exact_mut(4).zip(src) {
433 if i as usize != t {
434 d.copy_from_slice(&colors[i as usize]);
435 }
436 }
437 }
438 }
439}
440
441fn interlaced_row(k: usize, h: usize) -> usize {
442 let p1 = h.div_ceil(8);
443 let p2 = (h + 3) / 8;
444 let p3 = (h + 1) / 4;
445 if k < p1 {
446 k * 8
447 } else if k < p1 + p2 {
448 (k - p1) * 8 + 4
449 } else if k < p1 + p2 + p3 {
450 (k - p1 - p2) * 4 + 2
451 } else {
452 (k - p1 - p2 - p3) * 2 + 1
453 }
454}
455
456fn wait(rx: Receiver<Raw>) -> Option<Raw> {
457 if rayon::current_thread_index().is_none() {
458 return rx.recv().ok();
459 }
460 loop {
461 match rx.try_recv() {
462 Ok(raw) => return Some(raw),
463 Err(TryRecvError::Empty) => {
464 if rayon::yield_now() != Some(rayon::Yield::Executed) {
465 std::thread::yield_now();
466 }
467 }
468 Err(TryRecvError::Disconnected) => return None,
469 }
470 }
471}
472
473fn decode_raw(raw: &mut Raw) {
474 thread_local! {
475 static LZW: std::cell::RefCell<LzwDecoder> = std::cell::RefCell::new(LzwDecoder::default());
476 }
477 let n = (raw.width as usize * raw.height as usize)
478 .min(raw.data.len().saturating_mul(LZW_PIXELS_PER_BYTE));
479 raw.indices.clear();
480 raw.indices.resize(n, 0);
481 raw.decoded = LZW.with_borrow_mut(|l| l.decode(raw.min_code, &raw.data, &mut raw.indices));
482}
483
484pub fn decode(data: &[u8]) -> Result<Animation> {
485 let mut d = Decoder::new(data)?;
486 let mut frames = Vec::new();
487 while let Some(f) = d.next_frame()? {
488 let delay = f.delay_cs;
489 frames.push((f.image.clone(), delay));
490 }
491 Ok(Animation {
492 width: d.width(),
493 height: d.height(),
494 loop_count: d.loop_count(),
495 frames,
496 })
497}