Skip to main content

pito_codecs/gif/
decode.rs

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}