1use otf_pixels_compress::LzwDecoder;
25use otf_pixels_core::{
26 Animation, Codec, DecodeCapability, Decoder, Format, ImageDescriptor as CoreDescriptor, Limits,
27 PixelFormat, PixelsError, Result, Source,
28};
29
30use crate::format::{
31 Disposal, GraphicControl, ImageDescriptor, SIGNATURE_87A, SIGNATURE_89A, Screen,
32 interlaced_pass_rows, interlaced_row, label, read_sub_blocks, skip_sub_blocks,
33};
34
35const MAX_COMPRESSED: usize = 64 * 1024 * 1024;
40
41const MAX_STREAM: usize = 512 * 1024 * 1024;
45
46const MAX_EXTENSION: usize = 4096;
49
50#[derive(Debug, Clone)]
52#[non_exhaustive]
53pub struct Frame {
54 pub pixels: Vec<u8>,
56 pub width: u32,
58 pub height: u32,
60 pub delay_centiseconds: u16,
62 pub disposal: Disposal,
64}
65
66#[derive(Debug)]
71pub struct GifDecoder<S: Source> {
72 descriptor: CoreDescriptor,
73 screen: Screen,
74 source: Option<Bytes>,
77 animation: Option<Animation>,
79 _source: std::marker::PhantomData<fn() -> S>,
81 global: Vec<[u8; 3]>,
83 canvas: Vec<u8>,
85 started: bool,
87 row: u32,
89 finished: bool,
91 pending: Option<Pending>,
93 background: [u8; 4],
95}
96
97#[derive(Debug)]
99struct Pending {
100 disposal: Disposal,
101 area: ImageDescriptor,
102 saved: Vec<u8>,
104 had_transparency: bool,
107}
108
109impl<S: Source> GifDecoder<S> {
110 pub fn new(mut source: S, limits: Limits) -> Result<Self> {
118 let mut header = [0_u8; 13];
119 source.read_exact(&mut header)?;
120
121 let signature = header.get(..6).unwrap_or(&[]);
122 if signature != SIGNATURE_87A && signature != SIGNATURE_89A {
123 return Err(PixelsError::malformed(
124 "gif",
125 "signature is neither GIF87a nor GIF89a",
126 ));
127 }
128 let mut screen_bytes = [0_u8; 7];
129 screen_bytes.copy_from_slice(header.get(6..13).unwrap_or(&[0; 7]));
130 let screen = Screen::parse(&screen_bytes)?;
131
132 let width = u32::from(screen.width);
133 let height = u32::from(screen.height);
134 let descriptor = CoreDescriptor::with_limits(width, height, PixelFormat::Rgba8, &limits)?;
136
137 let mut global = Vec::new();
138 if screen.global_table_size > 0 {
139 global = read_table(&mut source, screen.global_table_size)?;
140 }
141
142 let mut rest = Vec::new();
146 let mut chunk = [0_u8; 64 * 1024];
147 loop {
148 match source.read(&mut chunk)? {
149 0 => break,
150 n => rest.extend_from_slice(chunk.get(..n).unwrap_or_default()),
151 }
152 if rest.len() > MAX_STREAM {
153 return Err(PixelsError::unsupported(format!(
154 "gif: streams over {} MiB are not read",
155 MAX_STREAM >> 20
156 )));
157 }
158 }
159 let animation = scan_animation(&rest);
160
161 let canvas_len = descriptor
162 .byte_len()
163 .ok_or_else(|| PixelsError::malformed("gif", "canvas size overflows"))?;
164
165 let background = global
170 .get(screen.background as usize)
171 .map_or([0, 0, 0, 0], |c| [c[0], c[1], c[2], 255]);
172
173 Ok(Self {
174 descriptor,
175 screen,
176 source: Some(std::io::Cursor::new(rest)),
177 animation,
178 _source: std::marker::PhantomData,
179 global,
180 canvas: vec![0_u8; canvas_len],
183 started: false,
184 row: 0,
185 finished: false,
186 pending: None,
187 background,
188 })
189 }
190
191 #[must_use]
193 pub const fn screen(&self) -> Screen {
194 self.screen
195 }
196
197 pub fn next_frame(&mut self) -> Result<Option<Frame>> {
208 if self.finished {
209 return Ok(None);
210 }
211 let Some(mut source) = self.source.take() else {
212 return Ok(None);
213 };
214 let result = self.decode_next(&mut source);
215 self.source = Some(source);
216 result
217 }
218
219 fn decode_next(&mut self, source: &mut Bytes) -> Result<Option<Frame>> {
221 self.apply_disposal();
226
227 let mut control = GraphicControl::default();
228 loop {
229 let mut marker = [0_u8; 1];
230 source.read_exact(&mut marker)?;
231 match marker[0] {
232 label::TRAILER => {
233 self.finished = true;
234 return Ok(None);
235 }
236 label::EXTENSION => {
237 let mut kind = [0_u8; 1];
238 source.read_exact(&mut kind)?;
239 match kind[0] {
240 label::GRAPHIC_CONTROL => {
241 let payload = read_sub_blocks(source, MAX_EXTENSION)?;
242 control = GraphicControl::parse(&payload);
243 }
244 _ => skip_sub_blocks(source)?,
248 }
249 }
250 label::IMAGE => {
251 let frame = self.decode_image(source, control)?;
252 return Ok(Some(frame));
253 }
254 other => {
255 return Err(PixelsError::malformed(
256 "gif",
257 format!("unknown block label {other:#04x}"),
258 ));
259 }
260 }
261 }
262 }
263
264 fn apply_disposal(&mut self) {
266 let Some(pending) = self.pending.take() else {
267 return;
268 };
269 match pending.disposal {
270 Disposal::None | Disposal::Keep => {}
274 Disposal::Background => {
275 let fill = if pending.had_transparency {
282 [0, 0, 0, 0]
283 } else {
284 self.background
285 };
286 self.fill_area(pending.area, &fill);
287 }
288 Disposal::Previous => {
289 self.restore_area(pending.area, &pending.saved);
290 }
291 }
292 }
293
294 fn fill_area(&mut self, area: ImageDescriptor, value: &[u8; 4]) {
296 let width = self.descriptor.width;
297 let height = self.descriptor.height;
298 for y in u32::from(area.top)..u32::from(area.top) + u32::from(area.height) {
299 if y >= height {
300 break;
301 }
302 for x in u32::from(area.left)..u32::from(area.left) + u32::from(area.width) {
303 if x >= width {
304 break;
305 }
306 let at = ((y * width + x) * 4) as usize;
307 if let Some(slot) = self.canvas.get_mut(at..at + 4) {
308 slot.copy_from_slice(value);
309 }
310 }
311 }
312 }
313
314 fn restore_area(&mut self, area: ImageDescriptor, saved: &[u8]) {
316 let width = self.descriptor.width;
317 let height = self.descriptor.height;
318 let area_width = u32::from(area.width) as usize;
319 for row in 0..u32::from(area.height) {
320 let y = u32::from(area.top) + row;
321 if y >= height {
322 break;
323 }
324 for column in 0..u32::from(area.width) {
325 let x = u32::from(area.left) + column;
326 if x >= width {
327 break;
328 }
329 let from = ((row as usize * area_width) + column as usize) * 4;
330 let to = ((y * width + x) * 4) as usize;
331 let (Some(source), Some(target)) =
332 (saved.get(from..from + 4), self.canvas.get_mut(to..to + 4))
333 else {
334 continue;
335 };
336 target.copy_from_slice(source);
337 }
338 }
339 }
340
341 fn decode_image(&mut self, source: &mut Bytes, control: GraphicControl) -> Result<Frame> {
343 let mut bytes = [0_u8; 9];
344 source.read_exact(&mut bytes)?;
345 let image = ImageDescriptor::parse(&bytes)?;
346
347 let local = if image.local_table_size > 0 {
351 read_table(source, image.local_table_size)?
352 } else {
353 Vec::new()
354 };
355 let palette: Vec<[u8; 3]> = if local.is_empty() {
358 self.global.clone()
359 } else {
360 local
361 };
362 if palette.is_empty() {
363 return Err(PixelsError::malformed(
364 "gif",
365 "frame has neither a local nor a global colour table",
366 ));
367 }
368
369 let mut minimum_width = [0_u8; 1];
370 source.read_exact(&mut minimum_width)?;
371 let compressed = read_sub_blocks(source, MAX_COMPRESSED)?;
372
373 let pixels = u32::from(image.width) as usize * u32::from(image.height) as usize;
374 let decoder =
375 LzwDecoder::gif(u32::from(minimum_width[0])).map_err(crate::compress_error)?;
376 let indices = decoder
379 .decode(&compressed, pixels)
380 .map_err(crate::compress_error)?;
381
382 let saved = if control.disposal == Disposal::Previous {
384 self.save_area(image)
385 } else {
386 Vec::new()
387 };
388
389 self.composite(image, &indices, &palette, control.transparent);
390 self.pending = Some(Pending {
391 disposal: control.disposal,
392 area: image,
393 saved,
394 had_transparency: control.transparent.is_some(),
395 });
396 self.started = true;
397
398 Ok(Frame {
399 pixels: self.canvas.clone(),
400 width: self.descriptor.width,
401 height: self.descriptor.height,
402 delay_centiseconds: control.delay_centiseconds,
403 disposal: control.disposal,
404 })
405 }
406
407 fn save_area(&self, area: ImageDescriptor) -> Vec<u8> {
409 let width = self.descriptor.width;
410 let height = self.descriptor.height;
411 let mut out =
412 vec![0_u8; u32::from(area.width) as usize * u32::from(area.height) as usize * 4];
413 let area_width = u32::from(area.width) as usize;
414 for row in 0..u32::from(area.height) {
415 let y = u32::from(area.top) + row;
416 if y >= height {
417 break;
418 }
419 for column in 0..u32::from(area.width) {
420 let x = u32::from(area.left) + column;
421 if x >= width {
422 break;
423 }
424 let from = ((y * width + x) * 4) as usize;
425 let to = ((row as usize * area_width) + column as usize) * 4;
426 let (Some(source), Some(target)) =
427 (self.canvas.get(from..from + 4), out.get_mut(to..to + 4))
428 else {
429 continue;
430 };
431 target.copy_from_slice(source);
432 }
433 }
434 out
435 }
436
437 fn composite(
439 &mut self,
440 image: ImageDescriptor,
441 indices: &[u8],
442 palette: &[[u8; 3]],
443 transparent: Option<u8>,
444 ) {
445 let canvas_width = self.descriptor.width;
446 let canvas_height = self.descriptor.height;
447 let frame_width = u32::from(image.width);
448 let frame_height = u32::from(image.height);
449
450 for source_row in 0..frame_height {
451 let target_row = if image.interlaced {
454 match deinterlace(source_row, frame_height) {
455 Some(row) => row,
456 None => continue,
457 }
458 } else {
459 source_row
460 };
461 let y = u32::from(image.top) + target_row;
462 if y >= canvas_height {
463 continue;
464 }
465
466 for column in 0..frame_width {
467 let x = u32::from(image.left) + column;
468 if x >= canvas_width {
469 continue;
470 }
471 let index = (source_row * frame_width + column) as usize;
472 let Some(&entry) = indices.get(index) else {
473 continue;
477 };
478 if Some(entry) == transparent {
479 continue;
482 }
483 let colour = palette.get(entry as usize).copied().unwrap_or([0, 0, 0]);
484 let at = ((y * canvas_width + x) * 4) as usize;
485 if let Some(slot) = self.canvas.get_mut(at..at + 4) {
486 slot.copy_from_slice(&[colour[0], colour[1], colour[2], 255]);
487 }
488 }
489 }
490 }
491
492 fn ensure_started(&mut self) -> Result<()> {
494 if self.started {
495 return Ok(());
496 }
497 match self.next_frame()? {
498 Some(_) => Ok(()),
499 None => Err(PixelsError::malformed("gif", "stream contains no frames")),
500 }
501 }
502}
503
504type Bytes = std::io::Cursor<Vec<u8>>;
506
507fn scan_animation(stream: &[u8]) -> Option<Animation> {
513 let mut source = stream;
514 let mut delays = Vec::new();
515 let mut delay = 0_u32;
516 let mut loop_count = 1;
518 let mut byte = [0_u8; 1];
519 while source.read_exact(&mut byte).is_ok() {
520 match byte[0] {
521 label::EXTENSION => {
522 if source.read_exact(&mut byte).is_err() {
523 break;
524 }
525 let Ok(payload) = read_sub_blocks(&mut source, MAX_EXTENSION) else {
526 break;
527 };
528 match byte[0] {
529 label::GRAPHIC_CONTROL => {
530 delay = u32::from(GraphicControl::parse(&payload).delay_centiseconds) * 10;
531 }
532 0xFF if payload.starts_with(b"NETSCAPE2.0")
535 || payload.starts_with(b"ANIMEXTS1.0") =>
536 {
537 if let [1, lo, hi, ..] = payload.get(11..).unwrap_or_default() {
538 loop_count = u32::from(u16::from_le_bytes([*lo, *hi]));
539 }
540 }
541 _ => {}
542 }
543 }
544 label::IMAGE => {
545 let mut bytes = [0_u8; 9];
546 if source.read_exact(&mut bytes).is_err() {
547 break;
548 }
549 let Ok(image) = ImageDescriptor::parse(&bytes) else {
550 break;
551 };
552 let table = image.local_table_size * 3;
553 let mut code_size = [0_u8; 1];
554 if source.len() < table + 1 {
555 break;
556 }
557 source = source.get(table..).unwrap_or_default();
558 if source.read_exact(&mut code_size).is_err()
559 || skip_sub_blocks(&mut source).is_err()
560 {
561 break;
562 }
563 delays.push(delay);
564 delay = 0;
565 }
566 _ => break,
567 }
568 }
569 Animation::new(delays, loop_count)
570}
571
572fn deinterlace(stored: u32, height: u32) -> Option<u32> {
574 let mut seen = 0;
575 for pass in 0..4 {
576 let rows = interlaced_pass_rows(pass, height);
577 if stored < seen + rows {
578 return interlaced_row(pass, stored - seen, height);
579 }
580 seen += rows;
581 }
582 None
583}
584
585fn read_table<S: Source>(source: &mut S, entries: usize) -> Result<Vec<[u8; 3]>> {
587 let mut bytes = vec![0_u8; entries * 3];
588 source.read_exact(&mut bytes)?;
589 Ok(bytes
590 .chunks_exact(3)
591 .map(|rgb| {
592 [
593 rgb.first().copied().unwrap_or(0),
594 rgb.get(1).copied().unwrap_or(0),
595 rgb.get(2).copied().unwrap_or(0),
596 ]
597 })
598 .collect())
599}
600
601impl<S: Source + std::fmt::Debug> Decoder for GifDecoder<S> {
602 fn descriptor(&self) -> CoreDescriptor {
603 self.descriptor
604 }
605
606 fn capability(&self) -> DecodeCapability {
607 DecodeCapability::Sequential
608 }
609
610 fn animation(&self) -> Option<Animation> {
611 self.animation.clone()
612 }
613
614 fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
615 self.ensure_started()?;
616 if self.row >= self.descriptor.height {
617 return Err(PixelsError::invalid_argument(
618 "out",
619 format!("all {} rows have already been read", self.descriptor.height),
620 ));
621 }
622 let row_bytes = self.descriptor.row_bytes();
623 if out.len() != row_bytes {
624 return Err(PixelsError::invalid_argument(
625 "out",
626 format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
627 ));
628 }
629 let start = self.row as usize * row_bytes;
630 let row = self
631 .canvas
632 .get(start..start + row_bytes)
633 .ok_or_else(|| PixelsError::malformed("gif", "canvas is short"))?;
634 out.copy_from_slice(row);
635 self.row += 1;
636 Ok(())
637 }
638}
639
640#[must_use]
644pub fn probe(prefix: &[u8]) -> bool {
645 let head = prefix.get(..6);
646 head == Some(&SIGNATURE_87A[..]) || head == Some(&SIGNATURE_89A[..])
647}
648
649#[derive(Debug, Clone, Copy, Default)]
651pub struct GifCodec;
652
653impl Codec for GifCodec {
654 fn format(&self) -> Format {
655 Format::Gif
656 }
657
658 fn magic_len(&self) -> usize {
659 6
660 }
661
662 fn probe(&self, prefix: &[u8]) -> bool {
663 probe(prefix)
664 }
665}