1use otf_pixels_compress::LzwDecoder;
25use otf_pixels_core::{
26 Animation, Background, Codec, Comment, DecodeCapability, Decoder, Format,
27 ImageDescriptor as CoreDescriptor, Limits, PixelFormat, PixelsError, Result, Source,
28 SourceInfo,
29};
30
31use crate::format::{
32 Disposal, GraphicControl, ImageDescriptor, SIGNATURE_87A, SIGNATURE_89A, Screen,
33 interlaced_pass_rows, interlaced_row, label, read_sub_blocks, skip_sub_blocks,
34};
35
36const MAX_COMPRESSED: usize = 64 * 1024 * 1024;
41
42const MAX_STREAM: usize = 512 * 1024 * 1024;
46
47const MAX_EXTENSION: usize = 4096;
50
51#[derive(Debug, Clone)]
53#[non_exhaustive]
54pub struct Frame {
55 pub pixels: Vec<u8>,
57 pub width: u32,
59 pub height: u32,
61 pub delay_centiseconds: u16,
63 pub disposal: Disposal,
65}
66
67#[derive(Debug)]
72pub struct GifDecoder<S: Source> {
73 descriptor: CoreDescriptor,
74 screen: Screen,
75 source: Option<Bytes>,
78 animation: Option<Animation>,
80 _source: std::marker::PhantomData<fn() -> S>,
82 global: Vec<[u8; 3]>,
84 canvas: Vec<u8>,
86 started: bool,
88 row: u32,
90 finished: bool,
92 pending: Option<Pending>,
94 background: [u8; 4],
96 info: SourceInfo,
98}
99
100#[derive(Debug)]
102struct Pending {
103 disposal: Disposal,
104 area: ImageDescriptor,
105 saved: Vec<u8>,
107 had_transparency: bool,
110}
111
112impl<S: Source> GifDecoder<S> {
113 pub fn new(mut source: S, limits: Limits) -> Result<Self> {
121 let mut header = [0_u8; 13];
122 source.read_exact(&mut header)?;
123
124 let signature = header.get(..6).unwrap_or(&[]);
125 if signature != SIGNATURE_87A && signature != SIGNATURE_89A {
126 return Err(PixelsError::malformed(
127 "gif",
128 "signature is neither GIF87a nor GIF89a",
129 ));
130 }
131 let mut screen_bytes = [0_u8; 7];
132 screen_bytes.copy_from_slice(header.get(6..13).unwrap_or(&[0; 7]));
133 let screen = Screen::parse(&screen_bytes)?;
134
135 let width = u32::from(screen.width);
136 let height = u32::from(screen.height);
137 let descriptor = CoreDescriptor::with_limits(width, height, PixelFormat::Rgba8, &limits)?;
139
140 let mut global = Vec::new();
141 if screen.global_table_size > 0 {
142 global = read_table(&mut source, screen.global_table_size)?;
143 }
144
145 let mut rest = Vec::new();
149 let mut chunk = [0_u8; 64 * 1024];
150 loop {
151 match source.read(&mut chunk)? {
152 0 => break,
153 n => rest.extend_from_slice(chunk.get(..n).unwrap_or_default()),
154 }
155 if rest.len() > MAX_STREAM {
156 return Err(PixelsError::unsupported(format!(
157 "gif: streams over {} MiB are not read",
158 MAX_STREAM >> 20
159 )));
160 }
161 }
162 let (animation, comments, interlaced) = scan_animation(&rest);
163
164 let canvas_len = descriptor
165 .byte_len()
166 .ok_or_else(|| PixelsError::malformed("gif", "canvas size overflows"))?;
167
168 let background = global
173 .get(screen.background as usize)
174 .map_or([0, 0, 0, 0], |c| [c[0], c[1], c[2], 255]);
175 let mut info = SourceInfo::default();
176 info.palette = true;
177 info.progressive = interlaced;
178 info.bits_per_sample = Some(8);
179 info.comments = comments;
180 info.background = global
181 .get(screen.background as usize)
182 .map(|c| Background::Rgb(c[0].into(), c[1].into(), c[2].into()));
183
184 Ok(Self {
185 descriptor,
186 screen,
187 source: Some(std::io::Cursor::new(rest)),
188 animation,
189 _source: std::marker::PhantomData,
190 global,
191 canvas: vec![0_u8; canvas_len],
194 started: false,
195 row: 0,
196 finished: false,
197 pending: None,
198 background,
199 info,
200 })
201 }
202
203 #[must_use]
205 pub const fn screen(&self) -> Screen {
206 self.screen
207 }
208
209 pub fn next_frame(&mut self) -> Result<Option<Frame>> {
220 if self.finished {
221 return Ok(None);
222 }
223 let Some(mut source) = self.source.take() else {
224 return Ok(None);
225 };
226 let result = self.decode_next(&mut source);
227 self.source = Some(source);
228 result
229 }
230
231 fn decode_next(&mut self, source: &mut Bytes) -> Result<Option<Frame>> {
233 self.apply_disposal();
238
239 let mut control = GraphicControl::default();
240 loop {
241 let mut marker = [0_u8; 1];
242 source.read_exact(&mut marker)?;
243 match marker[0] {
244 label::TRAILER => {
245 self.finished = true;
246 return Ok(None);
247 }
248 label::EXTENSION => {
249 let mut kind = [0_u8; 1];
250 source.read_exact(&mut kind)?;
251 match kind[0] {
252 label::GRAPHIC_CONTROL => {
253 let payload = read_sub_blocks(source, MAX_EXTENSION)?;
254 control = GraphicControl::parse(&payload);
255 }
256 _ => skip_sub_blocks(source)?,
260 }
261 }
262 label::IMAGE => {
263 let frame = self.decode_image(source, control)?;
264 return Ok(Some(frame));
265 }
266 other => {
267 return Err(PixelsError::malformed(
268 "gif",
269 format!("unknown block label {other:#04x}"),
270 ));
271 }
272 }
273 }
274 }
275
276 fn apply_disposal(&mut self) {
278 let Some(pending) = self.pending.take() else {
279 return;
280 };
281 match pending.disposal {
282 Disposal::None | Disposal::Keep => {}
286 Disposal::Background => {
287 let fill = if pending.had_transparency {
294 [0, 0, 0, 0]
295 } else {
296 self.background
297 };
298 self.fill_area(pending.area, &fill);
299 }
300 Disposal::Previous => {
301 self.restore_area(pending.area, &pending.saved);
302 }
303 }
304 }
305
306 fn fill_area(&mut self, area: ImageDescriptor, value: &[u8; 4]) {
308 let width = self.descriptor.width;
309 let height = self.descriptor.height;
310 for y in u32::from(area.top)..u32::from(area.top) + u32::from(area.height) {
311 if y >= height {
312 break;
313 }
314 for x in u32::from(area.left)..u32::from(area.left) + u32::from(area.width) {
315 if x >= width {
316 break;
317 }
318 let at = ((y * width + x) * 4) as usize;
319 if let Some(slot) = self.canvas.get_mut(at..at + 4) {
320 slot.copy_from_slice(value);
321 }
322 }
323 }
324 }
325
326 fn restore_area(&mut self, area: ImageDescriptor, saved: &[u8]) {
328 let width = self.descriptor.width;
329 let height = self.descriptor.height;
330 let area_width = u32::from(area.width) as usize;
331 for row in 0..u32::from(area.height) {
332 let y = u32::from(area.top) + row;
333 if y >= height {
334 break;
335 }
336 for column in 0..u32::from(area.width) {
337 let x = u32::from(area.left) + column;
338 if x >= width {
339 break;
340 }
341 let from = ((row as usize * area_width) + column as usize) * 4;
342 let to = ((y * width + x) * 4) as usize;
343 let (Some(source), Some(target)) =
344 (saved.get(from..from + 4), self.canvas.get_mut(to..to + 4))
345 else {
346 continue;
347 };
348 target.copy_from_slice(source);
349 }
350 }
351 }
352
353 fn decode_image(&mut self, source: &mut Bytes, control: GraphicControl) -> Result<Frame> {
355 let mut bytes = [0_u8; 9];
356 source.read_exact(&mut bytes)?;
357 let image = ImageDescriptor::parse(&bytes)?;
358
359 let local = if image.local_table_size > 0 {
363 read_table(source, image.local_table_size)?
364 } else {
365 Vec::new()
366 };
367 let palette: Vec<[u8; 3]> = if local.is_empty() {
370 self.global.clone()
371 } else {
372 local
373 };
374 if palette.is_empty() {
375 return Err(PixelsError::malformed(
376 "gif",
377 "frame has neither a local nor a global colour table",
378 ));
379 }
380
381 let mut minimum_width = [0_u8; 1];
382 source.read_exact(&mut minimum_width)?;
383 let compressed = read_sub_blocks(source, MAX_COMPRESSED)?;
384
385 let pixels = u32::from(image.width) as usize * u32::from(image.height) as usize;
386 let decoder =
387 LzwDecoder::gif(u32::from(minimum_width[0])).map_err(crate::compress_error)?;
388 let indices = decoder
391 .decode(&compressed, pixels)
392 .map_err(crate::compress_error)?;
393
394 let saved = if control.disposal == Disposal::Previous {
396 self.save_area(image)
397 } else {
398 Vec::new()
399 };
400
401 self.composite(image, &indices, &palette, control.transparent);
402 self.pending = Some(Pending {
403 disposal: control.disposal,
404 area: image,
405 saved,
406 had_transparency: control.transparent.is_some(),
407 });
408 self.started = true;
409
410 Ok(Frame {
411 pixels: self.canvas.clone(),
412 width: self.descriptor.width,
413 height: self.descriptor.height,
414 delay_centiseconds: control.delay_centiseconds,
415 disposal: control.disposal,
416 })
417 }
418
419 fn save_area(&self, area: ImageDescriptor) -> Vec<u8> {
421 let width = self.descriptor.width;
422 let height = self.descriptor.height;
423 let mut out =
424 vec![0_u8; u32::from(area.width) as usize * u32::from(area.height) as usize * 4];
425 let area_width = u32::from(area.width) as usize;
426 for row in 0..u32::from(area.height) {
427 let y = u32::from(area.top) + row;
428 if y >= height {
429 break;
430 }
431 for column in 0..u32::from(area.width) {
432 let x = u32::from(area.left) + column;
433 if x >= width {
434 break;
435 }
436 let from = ((y * width + x) * 4) as usize;
437 let to = ((row as usize * area_width) + column as usize) * 4;
438 let (Some(source), Some(target)) =
439 (self.canvas.get(from..from + 4), out.get_mut(to..to + 4))
440 else {
441 continue;
442 };
443 target.copy_from_slice(source);
444 }
445 }
446 out
447 }
448
449 fn composite(
451 &mut self,
452 image: ImageDescriptor,
453 indices: &[u8],
454 palette: &[[u8; 3]],
455 transparent: Option<u8>,
456 ) {
457 let canvas_width = self.descriptor.width;
458 let canvas_height = self.descriptor.height;
459 let frame_width = u32::from(image.width);
460 let frame_height = u32::from(image.height);
461
462 for source_row in 0..frame_height {
463 let target_row = if image.interlaced {
466 match deinterlace(source_row, frame_height) {
467 Some(row) => row,
468 None => continue,
469 }
470 } else {
471 source_row
472 };
473 let y = u32::from(image.top) + target_row;
474 if y >= canvas_height {
475 continue;
476 }
477
478 for column in 0..frame_width {
479 let x = u32::from(image.left) + column;
480 if x >= canvas_width {
481 continue;
482 }
483 let index = (source_row * frame_width + column) as usize;
484 let Some(&entry) = indices.get(index) else {
485 continue;
489 };
490 if Some(entry) == transparent {
491 continue;
494 }
495 let colour = palette.get(entry as usize).copied().unwrap_or([0, 0, 0]);
496 let at = ((y * canvas_width + x) * 4) as usize;
497 if let Some(slot) = self.canvas.get_mut(at..at + 4) {
498 slot.copy_from_slice(&[colour[0], colour[1], colour[2], 255]);
499 }
500 }
501 }
502 }
503
504 fn ensure_started(&mut self) -> Result<()> {
506 if self.started {
507 return Ok(());
508 }
509 match self.next_frame()? {
510 Some(_) => Ok(()),
511 None => Err(PixelsError::malformed("gif", "stream contains no frames")),
512 }
513 }
514}
515
516type Bytes = std::io::Cursor<Vec<u8>>;
518
519fn scan_animation(stream: &[u8]) -> (Option<Animation>, Vec<Comment>, bool) {
526 let mut comments = Vec::new();
527 let mut interlaced = None;
528 let mut source = stream;
529 let mut delays = Vec::new();
530 let mut delay = 0_u32;
531 let mut loop_count = 1;
533 let mut byte = [0_u8; 1];
534 while source.read_exact(&mut byte).is_ok() {
535 match byte[0] {
536 label::EXTENSION => {
537 if source.read_exact(&mut byte).is_err() {
538 break;
539 }
540 let Ok(payload) = read_sub_blocks(&mut source, MAX_EXTENSION) else {
541 break;
542 };
543 match byte[0] {
544 label::GRAPHIC_CONTROL => {
545 delay = u32::from(GraphicControl::parse(&payload).delay_centiseconds) * 10;
546 }
547 0xFE => comments.push(Comment {
548 keyword: String::new(),
549 text: String::from_utf8(payload.clone())
550 .unwrap_or_else(|_| payload.iter().map(|&c| char::from(c)).collect()),
551 }),
552 0xFF if payload.starts_with(b"NETSCAPE2.0")
555 || payload.starts_with(b"ANIMEXTS1.0") =>
556 {
557 if let [1, lo, hi, ..] = payload.get(11..).unwrap_or_default() {
558 loop_count = u32::from(u16::from_le_bytes([*lo, *hi]));
559 }
560 }
561 _ => {}
562 }
563 }
564 label::IMAGE => {
565 let mut bytes = [0_u8; 9];
566 if source.read_exact(&mut bytes).is_err() {
567 break;
568 }
569 let Ok(image) = ImageDescriptor::parse(&bytes) else {
570 break;
571 };
572 interlaced = interlaced.or(Some(image.interlaced));
573 let table = image.local_table_size * 3;
574 let mut code_size = [0_u8; 1];
575 if source.len() < table + 1 {
576 break;
577 }
578 source = source.get(table..).unwrap_or_default();
579 if source.read_exact(&mut code_size).is_err()
580 || skip_sub_blocks(&mut source).is_err()
581 {
582 break;
583 }
584 delays.push(delay);
585 delay = 0;
586 }
587 _ => break,
588 }
589 }
590 (
591 Animation::new(delays, loop_count),
592 comments,
593 interlaced.unwrap_or(false),
594 )
595}
596
597fn deinterlace(stored: u32, height: u32) -> Option<u32> {
599 let mut seen = 0;
600 for pass in 0..4 {
601 let rows = interlaced_pass_rows(pass, height);
602 if stored < seen + rows {
603 return interlaced_row(pass, stored - seen, height);
604 }
605 seen += rows;
606 }
607 None
608}
609
610fn read_table<S: Source>(source: &mut S, entries: usize) -> Result<Vec<[u8; 3]>> {
612 let mut bytes = vec![0_u8; entries * 3];
613 source.read_exact(&mut bytes)?;
614 Ok(bytes
615 .chunks_exact(3)
616 .map(|rgb| {
617 [
618 rgb.first().copied().unwrap_or(0),
619 rgb.get(1).copied().unwrap_or(0),
620 rgb.get(2).copied().unwrap_or(0),
621 ]
622 })
623 .collect())
624}
625
626impl<S: Source + std::fmt::Debug> Decoder for GifDecoder<S> {
627 fn descriptor(&self) -> CoreDescriptor {
628 self.descriptor
629 }
630
631 fn capability(&self) -> DecodeCapability {
632 DecodeCapability::Sequential
633 }
634
635 fn animation(&self) -> Option<Animation> {
636 self.animation.clone()
637 }
638
639 fn source_info(&self) -> SourceInfo {
642 self.info.clone()
643 }
644
645 fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
646 self.ensure_started()?;
647 if self.row >= self.descriptor.height {
648 return Err(PixelsError::invalid_argument(
649 "out",
650 format!("all {} rows have already been read", self.descriptor.height),
651 ));
652 }
653 let row_bytes = self.descriptor.row_bytes();
654 if out.len() != row_bytes {
655 return Err(PixelsError::invalid_argument(
656 "out",
657 format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
658 ));
659 }
660 let start = self.row as usize * row_bytes;
661 let row = self
662 .canvas
663 .get(start..start + row_bytes)
664 .ok_or_else(|| PixelsError::malformed("gif", "canvas is short"))?;
665 out.copy_from_slice(row);
666 self.row += 1;
667 Ok(())
668 }
669}
670
671#[must_use]
675pub fn probe(prefix: &[u8]) -> bool {
676 let head = prefix.get(..6);
677 head == Some(&SIGNATURE_87A[..]) || head == Some(&SIGNATURE_89A[..])
678}
679
680#[derive(Debug, Clone, Copy, Default)]
682pub struct GifCodec;
683
684impl Codec for GifCodec {
685 fn format(&self) -> Format {
686 Format::Gif
687 }
688
689 fn magic_len(&self) -> usize {
690 6
691 }
692
693 fn probe(&self, prefix: &[u8]) -> bool {
694 probe(prefix)
695 }
696}