1#![no_std]
4
5use core::fmt::{self, Debug};
6use core::marker::PhantomData;
7
8use embedded_graphics::prelude::{DrawTarget, ImageDrawable, OriginDimensions, Point, Size};
9use embedded_graphics::Pixel;
10use embedded_graphics::{pixelcolor::Rgb888, prelude::PixelColor};
11use parser::eat_len_prefixed_subblocks;
12
13use crate::parser::{le_u16, take, take1, take_slice};
14
15mod bitstream;
16pub mod lzw;
17mod parser;
18
19struct LenPrefixRawDataView<'a> {
21 remains: &'a [u8],
22 current_block: &'a [u8],
23 cursor: u8,
24}
25
26impl<'a> LenPrefixRawDataView<'a> {
27 pub fn new(data: &'a [u8]) -> Self {
28 let len = data[0] as usize;
29 Self {
30 remains: &data[1 + len..],
31 current_block: &data[1..1 + len],
32 cursor: 0,
33 }
34 }
35
36 #[inline]
37 fn shift_cursor(&mut self) {
38 if self.current_block.is_empty() {
39 } else if self.cursor < self.current_block.len() as u8 - 1 {
41 self.cursor += 1;
42 } else {
43 self.cursor = 0;
44 self.shift_next_block();
45 }
46 }
47
48 #[inline]
50 fn shift_next_block(&mut self) {
51 if self.current_block.is_empty() {
52 return;
54 }
55 let len = self.remains[0] as usize;
56 if len == 0 {
57 self.remains = &[];
58 self.current_block = &[];
59 } else {
60 self.current_block = &self.remains[1..1 + len];
61 self.remains = &self.remains[1 + len..];
62 }
63 }
64}
65
66impl Iterator for LenPrefixRawDataView<'_> {
67 type Item = u8;
68
69 #[inline(always)]
70 fn next(&mut self) -> Option<Self::Item> {
71 if self.current_block.is_empty() {
72 return None;
73 }
74
75 let current = self.current_block[self.cursor as usize];
76 self.shift_cursor();
77 Some(current)
78 }
79}
80
81#[non_exhaustive]
82#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
83pub enum Version {
84 V87a,
85 V89a,
86}
87
88#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
89pub struct Header {
90 version: Version,
91 pub width: u16,
92 pub height: u16,
93 has_global_color_table: bool,
94 color_resolution: u8, pub bg_color_index: u8,
97 }
99
100impl Header {
101 pub fn parse(input: &[u8]) -> Result<(&[u8], (Header, Option<ColorTable<'_>>)), ParseError> {
102 let (input, magic) = take::<3>(input)?;
103
104 if &magic != b"GIF" {
105 return Err(ParseError::InvalidFileSignature(magic));
106 }
107
108 let (input, ver) = take::<3>(input)?;
109 let version = if &ver == b"87a" {
110 Version::V87a
111 } else if &ver == b"89a" {
112 Version::V89a
113 } else {
114 return Err(ParseError::InvalidFileSignature(magic));
115 };
116
117 let (intput, screen_width) = le_u16(input)?;
118 let (intput, screen_height) = le_u16(intput)?;
119
120 let (input, flags) = take1(intput)?;
121 let has_global_color_table = flags & 0b1000_0000 != 0;
122 let global_color_table_size = if has_global_color_table {
123 2_usize.pow(((flags & 0b0000_0111) + 1) as u32)
124 } else {
125 0
126 };
127 let color_resolution = (flags & 0b0111_0000) >> 4;
128 let _is_sorted = flags & 0b0000_1000 != 0;
129
130 let (input, bg_color_index) = take1(input)?;
131 let (input, _pixel_aspect_ratio) = take1(input)?;
132
133 let (input, color_table) = if global_color_table_size > 0 {
134 let (input, table) = take_slice(input, global_color_table_size * 3)?;
136 (input, Some(ColorTable::new(table)))
137 } else {
138 (input, None)
139 };
140
141 Ok((
142 input,
143 (
144 Header {
145 version,
146 width: screen_width,
147 height: screen_height,
148 has_global_color_table,
149 color_resolution,
150 bg_color_index,
151 },
152 color_table,
153 ),
154 ))
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
159pub struct ColorTable<'a> {
160 data: &'a [u8],
161}
162
163impl<'a> ColorTable<'a> {
164 pub(crate) const fn new(data: &'a [u8]) -> Self {
165 Self { data }
166 }
167
168 pub const fn len(&self) -> usize {
170 self.data.len() / 3
171 }
172
173 pub fn get(&self, index: u8) -> Option<Rgb888> {
177 let base = 3 * (index as usize);
178 if base >= self.data.len() {
179 return None;
180 }
181
182 Some(Rgb888::new(
183 self.data[base],
184 self.data[base + 1],
185 self.data[base + 2],
186 ))
187 }
188}
189
190#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
191pub struct RawGif<'a> {
192 header: Header,
194
195 global_color_table: Option<ColorTable<'a>>,
196
197 raw_block_data: &'a [u8],
199}
200
201impl<'a> RawGif<'a> {
202 fn from_slice(bytes: &'a [u8]) -> Result<Self, ParseError> {
203 let (remaining, (header, color_table)) = Header::parse(bytes)?;
204
205 Ok(Self {
206 header,
207 global_color_table: color_table,
208 raw_block_data: remaining,
209 })
210 }
211}
212
213#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
214pub struct GraphicControl {
215 pub is_transparent: bool,
216 pub transparent_color_index: u8,
217 pub delay_centis: u16,
219}
220
221#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
222
223pub struct ImageBlock<'a> {
224 pub left: u16,
225 pub top: u16,
226 pub width: u16,
227 pub height: u16,
228 pub is_interlaced: bool,
229 pub lzw_min_code_size: u8,
230 local_color_table: Option<ColorTable<'a>>,
231 image_data: &'a [u8],
232}
233
234impl<'a> ImageBlock<'a> {
235 pub fn parse(input: &'a [u8]) -> Result<(&[u8], Self), ParseError> {
237 let (input, left) = le_u16(input)?;
238 let (input, top) = le_u16(input)?;
239 let (input, width) = le_u16(input)?;
240 let (input, height) = le_u16(input)?;
241 let (input, flags) = take1(input)?;
242 let is_interlaced = flags & 0b0100_0000 != 0;
243 let has_local_color_table = flags & 0b1000_0000 != 0;
244 let local_color_table_size = if has_local_color_table {
245 2_usize.pow(((flags & 0b0000_0111) + 1) as u32)
246 } else {
247 0
248 };
249
250 let (input, local_color_table) = if local_color_table_size > 0 {
251 let (input, table) = take_slice(input, local_color_table_size * 3)?;
253 (input, Some(ColorTable::new(table)))
254 } else {
255 (input, None)
256 };
257
258 let (input, lzw_min_code_size) = take1(input)?;
259
260 let mut input0 = input;
261 let mut n = 1;
262 loop {
263 let (input, block_size) = take1(input0)?;
264 if block_size == 0 {
265 input0 = input;
266 break;
267 }
268 let (input, _) = take_slice(input, block_size as usize)?;
269 n += block_size as usize + 1;
270 input0 = input;
271 }
272
273 Ok((
274 input0,
275 Self {
276 left,
277 top,
278 width,
279 height,
280 is_interlaced,
281 lzw_min_code_size,
282 local_color_table,
283 image_data: &input[..n],
284 },
285 ))
286 }
287}
288
289#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
290pub enum ExtensionBlock<'a> {
291 GraphicControl(GraphicControl),
292 Comment(&'a [u8]),
293 PlainText,
295 NetscapeApplication { repetitions: u16 },
296 Application, Unknown(u8, &'a [u8]),
298}
299
300impl<'a> ExtensionBlock<'a> {
301 pub fn parse(input: &'a [u8]) -> Result<(&'a [u8], Self), ParseError> {
302 let (input, ext_label) = take1(input)?;
303 match ext_label {
304 0xff => {
305 let (input, block_size_1) = take1(input)?;
306 let (input, app_id) = take::<8>(input)?;
307 let (input, app_auth_code) = take::<3>(input)?;
308 if block_size_1 == 11 && &app_id == b"NETSCAPE" && &app_auth_code == b"2.0" {
309 let (input, block_size_2) = take1(input)?;
310 if block_size_2 == 3 {
311 let (input, always_one) = take1(input)?;
312 if always_one == 1 {
313 let (input, repetitions) = le_u16(input)?;
314 let (input, eob) = take1(input)?;
315 if eob == 0 {
316 return Ok((
317 input,
318 ExtensionBlock::NetscapeApplication { repetitions },
319 ));
320 }
321 }
322 }
323 }
324 let mut input0 = input;
325 loop {
326 let (input, block_size_2) = take1(input0)?;
327 if block_size_2 == 0 {
328 input0 = input;
329 break;
330 }
331 let (input, _data) = take_slice(input, block_size_2 as usize)?;
332 input0 = input;
333 }
334 Ok((input0, ExtensionBlock::Application))
335 }
336 0xfe => {
337 let mut input0 = input;
339 let mut size = 0;
340 loop {
341 let (input, block_size) = take1(input0)?;
342 size += 1;
343 if block_size == 0 {
344 input0 = input;
345 break;
346 }
347 let (input, _data) = take_slice(input, block_size as usize)?;
348 input0 = input;
349 size += block_size as usize;
350 }
351 Ok((input0, ExtensionBlock::Comment(&input[..size])))
352 }
353 0xf9 => {
354 let (input, block_size) = take1(input)?; if block_size != 4 {
357 return Err(ParseError::InvalidConstSizeBytes); }
359 let (input, flags) = take1(input)?;
360 let is_transparent = flags & 0b0000_0001 != 0;
361 let (input, delay_centis) = le_u16(input)?;
362 let (input, transparent_color_index) = take1(input)?;
363 let (input, block_terminator) = take1(input)?;
364 if block_terminator != 0 {
365 return Err(ParseError::InvalidByte);
366 }
367
368 Ok((
369 input,
370 ExtensionBlock::GraphicControl(GraphicControl {
371 is_transparent,
372 transparent_color_index,
373 delay_centis,
374 }),
375 ))
376 }
377 0x01 => {
378 let (input, _) = take::<13>(input)?;
379 let input = eat_len_prefixed_subblocks(input)?;
380 Ok((input, ExtensionBlock::PlainText))
381 }
382 _ => unimplemented!(),
383 }
384 }
385}
386
387#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
388pub enum Segment<'a> {
389 Image(ImageBlock<'a>),
390 Extension(ExtensionBlock<'a>),
391 Trailer,
393}
394
395impl<'a> Segment<'a> {
396 pub fn parse(input: &'a [u8]) -> Result<(&'a [u8], Self), ParseError> {
397 let (input, ext_magic) = take1(input)?;
398
399 if ext_magic == 0x21 {
400 let (input, ext) = ExtensionBlock::parse(input)?;
401 Ok((input, Segment::Extension(ext)))
402 } else if ext_magic == 0x2c {
403 let (input, image_block) = ImageBlock::parse(input)?;
405 Ok((input, Segment::Image(image_block)))
406 } else if ext_magic == 0x3b {
407 if input.is_empty() {
408 Ok((input, Segment::Trailer))
409 } else {
410 Err(ParseError::JunkAfterTrailerByte)
411 }
412 } else {
413 return Err(ParseError::InvalidByte);
414 }
415 }
416
417 fn skip_to_next_graphic_control(mut input: &[u8]) -> Result<&[u8], ParseError> {
418 loop {
419 let (input0, ext_magic) = take1(input)?;
420 if ext_magic == 0x21 {
421 let (input1, label) = take1(input0)?;
422 if label == 0xF9 {
423 return Ok(input);
424 } else if label == 0xFF {
425 input = eat_len_prefixed_subblocks(input1)?;
427 } else if label == 0x01 {
428 let (input2, _) = take::<13>(input1)?;
431 input = eat_len_prefixed_subblocks(input2)?;
432 } else if label == 0xFE {
433 input = eat_len_prefixed_subblocks(input1)?;
435 } else {
436 return Err(ParseError::InvalidExtensionLabel);
437 }
438 } else if ext_magic == 0x2c {
439 let (input1, _) = ImageBlock::parse(input0)?;
442 input = input1;
443 } else if ext_magic == 0x3b {
444 return Ok(&[]);
446 } else if ext_magic == 0x00 {
447 return Ok(input0);
449 } else {
450 return Err(ParseError::InvalidByte);
451 }
452 }
453 }
454
455 pub const fn type_name(&self) -> &'static str {
456 match self {
457 Segment::Image(_) => "Image",
458 Segment::Extension(_) => "Extension",
459 Segment::Trailer => "Trailer",
460 }
461 }
462}
463
464#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
465pub struct Gif<'a, C = Rgb888> {
466 raw_gif: RawGif<'a>,
467 color_type: PhantomData<C>,
468}
469
470impl<'a, C> Gif<'a, C> {
471 pub fn from_slice(input: &'a [u8]) -> Result<Self, ParseError> {
472 let raw_gif = RawGif::from_slice(input)?;
473 Ok(Self {
474 raw_gif,
475 color_type: PhantomData,
476 })
477 }
478
479 pub fn frames(&'a self) -> FrameIterator<'a, C> {
480 FrameIterator::new(self)
481 }
482
483 pub fn width(&self) -> u16 {
484 self.raw_gif.header.width
485 }
486
487 pub fn height(&self) -> u16 {
488 self.raw_gif.header.height
489 }
490}
491
492pub struct FrameIterator<'a, C> {
493 gif: &'a Gif<'a, C>,
494 frame_index: usize,
495 remain_raw_data: &'a [u8],
496}
497
498impl<'a, C> FrameIterator<'a, C> {
499 fn new(gif: &'a Gif<'a, C>) -> Self {
500 Self {
501 gif,
502 frame_index: 0,
503 remain_raw_data: gif.raw_gif.raw_block_data,
504 }
505 }
506}
507
508impl<'a, C: PixelColor> Iterator for FrameIterator<'a, C> {
509 type Item = Frame<'a, C>;
510
511 fn next(&mut self) -> Option<Self::Item> {
512 if self.remain_raw_data.is_empty() {
513 return None;
514 }
515
516 let input = self.remain_raw_data;
517 let input0 = Segment::skip_to_next_graphic_control(input).ok()?;
518
519 let (input00, seg) = Segment::parse(input0).ok()?;
520 self.remain_raw_data = input00;
521
522 if let Segment::Extension(ExtensionBlock::GraphicControl(ctrl)) = seg {
523 let frame = Frame {
524 delay_centis: ctrl.delay_centis,
525 is_transparent: ctrl.is_transparent,
526 transparent_color_index: ctrl.transparent_color_index,
527 global_color_table: self.gif.raw_gif.global_color_table.clone(),
528 header: &self.gif.raw_gif.header,
529 raw_data: input00,
530 frame_index: self.frame_index,
531 _marker: PhantomData,
532 };
533 self.frame_index += 1;
534 return Some(frame);
535 } else {
536 None
537 }
538 }
539}
540
541#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
542pub struct Frame<'a, C> {
543 pub delay_centis: u16,
544 pub is_transparent: bool,
545 pub transparent_color_index: u8,
546 global_color_table: Option<ColorTable<'a>>,
547 header: &'a Header,
548 raw_data: &'a [u8],
549 frame_index: usize,
550 _marker: PhantomData<C>,
551}
552
553impl<'a, C> OriginDimensions for Frame<'a, C> {
554 fn size(&self) -> Size {
555 Size::new(self.header.width as _, self.header.height as _)
556 }
557}
558
559impl<'a, C> ImageDrawable for Frame<'a, C>
560where
561 C: PixelColor + From<Rgb888>,
562{
563 type Color = C;
564
565 fn draw<D>(&self, target: &mut D) -> Result<(), D::Error>
566 where
567 D: DrawTarget<Color = Self::Color>,
568 {
569 let mut input = self.raw_data;
570 while let Ok((input0, seg)) = Segment::parse(input) {
571 input = input0;
572 match seg {
573 Segment::Extension(ExtensionBlock::GraphicControl(_)) | Segment::Trailer => {
574 break;
576 }
577 Segment::Image(ImageBlock {
578 left,
579 top,
580 width,
581 lzw_min_code_size,
582 local_color_table,
583 image_data,
584 ..
585 }) => {
586 let transparent_color_index = if self.is_transparent {
587 Some(self.transparent_color_index)
588 } else {
589 None
590 };
591 let color_table = local_color_table
592 .or_else(|| self.global_color_table.clone())
593 .unwrap();
594 let raw_image_data = LenPrefixRawDataView::new(image_data);
595 let mut decoder = lzw::Decoder::new(raw_image_data, lzw_min_code_size);
596
597 let mut idx: u32 = 0;
598
599 while let Ok(Some(decoded)) = decoder.decode_next() {
600 target.draw_iter(decoded.iter().filter_map(|&color_index| {
601 if transparent_color_index == Some(color_index) {
602 idx += 1;
604 return None;
605 }
606 let x = left + (idx % u32::from(width)) as u16;
607 let y = top + (idx / u32::from(width)) as u16;
608 idx += 1;
609
610 let color = color_table.get(color_index).unwrap();
611 Some(Pixel(Point::new(x as i32, y as i32), color.into()))
612 }))?;
613 }
614 }
615 _ => (),
616 }
617 }
618
619 Ok(())
620 }
621
622 fn draw_sub_image<D>(
623 &self,
624 target: &mut D,
625 area: &embedded_graphics::primitives::Rectangle,
626 ) -> Result<(), D::Error>
627 where
628 D: DrawTarget<Color = Self::Color>,
629 {
630 let mut input = self.raw_data;
631 while let Ok((input0, seg)) = Segment::parse(input) {
632 input = input0;
633 match seg {
634 Segment::Extension(ExtensionBlock::GraphicControl(_)) | Segment::Trailer => {
635 break;
637 }
638 Segment::Image(ImageBlock {
639 left,
640 top,
641 width,
642 lzw_min_code_size,
643 local_color_table,
644 image_data,
645 ..
646 }) => {
647 let transparent_color_index = if self.is_transparent {
648 Some(self.transparent_color_index)
649 } else {
650 None
651 };
652 let color_table = local_color_table
653 .or_else(|| self.global_color_table.clone())
654 .unwrap();
655 let raw_image_data = LenPrefixRawDataView::new(image_data);
656 let mut decoder = lzw::Decoder::new(raw_image_data, lzw_min_code_size);
657
658 let mut idx: u32 = 0;
659
660 while let Ok(Some(decoded)) = decoder.decode_next() {
661 target.draw_iter(decoded.iter().filter_map(|&color_index| {
662 if transparent_color_index == Some(color_index) {
663 idx += 1;
664 return None;
665 }
666
667 let x = left + (idx % u32::from(width)) as u16;
668 let y = top + (idx / u32::from(width)) as u16;
669 idx += 1;
670
671 let pt = Point::new(x as i32, y as i32);
672 if area.contains(pt) {
673 let color = color_table.get(color_index).unwrap();
674 Some(Pixel(pt, color.into()))
675 } else {
676 None
677 }
678 }))?;
679 }
680 }
681 _ => (),
682 }
683 }
684
685 Ok(())
686 }
687}
688
689impl<C> fmt::Debug for Frame<'_, C> {
690 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
691 f.debug_struct("Frame")
692 .field("frame_index", &self.frame_index)
693 .field("delay_centis", &self.delay_centis)
694 .field("is_transparent", &self.is_transparent)
695 .field("transparent_color_index", &self.transparent_color_index)
696 .field("len(remain_data)", &self.raw_data.len())
697 .finish()
698 }
699}
700
701#[cfg(feature = "defmt")]
702impl<C> defmt::Format for Frame<'_, C> {
703 fn format(&self, f: defmt::Formatter) {
704 defmt::write!(
705 f,
706 "Frame {{ frame_index: {}, delay_centis: {} remain: {}}}",
707 self.frame_index,
708 self.delay_centis,
709 self.raw_data.len()
710 );
711 }
712}
713
714#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
716pub enum ParseError {
717 UnsupportedBpp(u16),
719
720 UnexpectedEndOfFile,
722
723 InvalidFileSignature([u8; 3]),
725
726 UnsupportedCompressionMethod(u32),
728
729 UnsupportedHeaderLength(u32),
731
732 UnsupportedChannelMasks,
734
735 InvalidImageDimensions,
737
738 InvalidByte,
739
740 JunkAfterTrailerByte,
741
742 InvalidConstSizeBytes,
744
745 InvalidExtensionLabel,
746}