gift/
private.rs

1// private.rs
2//
3// Copyright (c) 2019-2025  Douglas Lau
4//
5//! Private module for top-level items
6use crate::{
7    Result,
8    block::{DisposalMethod, GraphicControl},
9    decode, encode,
10};
11use pix::{Palette, Raster, gray::Gray8, rgb::SRgba8};
12use std::io::{Read, Write};
13
14/// Raster for an animation step.
15pub(crate) enum StepRaster {
16    /// True color 24-bit raster
17    TrueColor(Raster<SRgba8>),
18    /// Indexed color 8-bit raster
19    Indexed(Raster<Gray8>, Palette),
20}
21
22/// One step of an animation.
23#[derive(Clone)]
24pub struct Step {
25    /// Raster of the animation step
26    pub(crate) raster: StepRaster,
27    /// Graphic control for the step
28    pub(crate) graphic_control_ext: Option<GraphicControl>,
29}
30
31/// GIF file decoder
32///
33/// Can be converted to one of three `Iterator`s:
34/// * [into_iter] / [into_steps] for high-level [Step]s
35/// * [into_frames] for mid-level [Frame]s
36/// * [into_blocks] for low-level [Block]s
37///
38/// ## Example: Get a `Raster` from a GIF
39/// ```
40/// use gift::Decoder;
41///
42/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
43/// # let gif = &[
44/// #   0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x02, 0x00,
45/// #   0x02, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00,
46/// #   0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00,
47/// #   0x02, 0x00, 0x02, 0x00, 0x00, 0x02, 0x03, 0x0c,
48/// #   0x10, 0x05, 0x00, 0x3b,
49/// # ][..];
50/// // ... open a `File` as "gif"
51/// if let Some(step) = Decoder::new(gif).into_steps().next() {
52///     // was there a decoding error?
53///     let step = step?;
54///     let raster = step.raster();
55///     // ... work with raster
56/// }
57/// # Ok(())
58/// # }
59/// ```
60///
61/// [Block]: block/enum.Block.html
62/// [Frame]: block/struct.Frame.html
63/// [into_blocks]: struct.Decoder.html#method.into_blocks
64/// [into_frames]: struct.Decoder.html#method.into_frames
65/// [into_iter]: struct.Decoder.html#method.into_iter
66/// [into_steps]: struct.Decoder.html#method.into_steps
67/// [Step]: struct.Step.html
68///
69pub struct Decoder<R: Read> {
70    /// Reader for input data
71    reader: R,
72    /// Maximum image size, in bytes
73    max_image_sz: Option<usize>,
74}
75
76impl Clone for StepRaster {
77    fn clone(&self) -> Self {
78        match self {
79            StepRaster::TrueColor(r) => {
80                StepRaster::TrueColor(Raster::with_raster(r))
81            }
82            StepRaster::Indexed(r, p) => {
83                StepRaster::Indexed(Raster::with_raster(r), p.clone())
84            }
85        }
86    }
87}
88
89impl Step {
90    /// Create an animation step with a true color raster.
91    pub fn with_true_color(raster: Raster<SRgba8>) -> Self {
92        let raster = StepRaster::TrueColor(raster);
93        Step {
94            raster,
95            graphic_control_ext: None,
96        }
97    }
98
99    /// Create an animation step with an indexed raster.
100    pub fn with_indexed(raster: Raster<Gray8>, palette: Palette) -> Self {
101        let raster = StepRaster::Indexed(raster, palette);
102        Step {
103            raster,
104            graphic_control_ext: None,
105        }
106    }
107
108    /// Adjust the disposal method.
109    pub fn with_disposal_method(mut self, method: DisposalMethod) -> Self {
110        let mut control = self.graphic_control_ext.unwrap_or_default();
111        control.set_disposal_method(method);
112        if control != GraphicControl::default() {
113            self.graphic_control_ext = Some(control);
114        } else {
115            self.graphic_control_ext = None;
116        }
117        self
118    }
119
120    /// Adjust the transparent color.
121    pub fn with_transparent_color(mut self, clr: Option<u8>) -> Self {
122        let mut control = self.graphic_control_ext.unwrap_or_default();
123        control.set_transparent_color(clr);
124        if control != GraphicControl::default() {
125            self.graphic_control_ext = Some(control);
126        } else {
127            self.graphic_control_ext = None;
128        }
129        self
130    }
131
132    /// Get the transparent color
133    pub fn transparent_color(&self) -> Option<u8> {
134        self.graphic_control_ext.and_then(|c| c.transparent_color())
135    }
136
137    /// Adjust the delay time.
138    pub fn with_delay_time_cs(mut self, delay: Option<u16>) -> Self {
139        let mut control = self.graphic_control_ext.unwrap_or_default();
140        control.set_delay_time_cs(delay.unwrap_or_default());
141        if control != GraphicControl::default() {
142            self.graphic_control_ext = Some(control);
143        } else {
144            self.graphic_control_ext = None;
145        }
146        self
147    }
148
149    /// Get the raster
150    pub fn raster(&self) -> &Raster<SRgba8> {
151        match &self.raster {
152            StepRaster::TrueColor(r) => r,
153            StepRaster::Indexed(_, _) => todo!("convert to true color"),
154        }
155    }
156
157    /// Get the delay time in centiseconds
158    pub fn delay_time_cs(&self) -> Option<u16> {
159        self.graphic_control_ext.map(|c| c.delay_time_cs())
160    }
161}
162
163impl<R: Read> Decoder<R> {
164    /// Create a new GIF decoder.
165    pub fn new(reader: R) -> Self {
166        Decoder {
167            reader,
168            max_image_sz: Some(1 << 25),
169        }
170    }
171
172    /// Set the maximum image size (in bytes) to allow for decoding.
173    pub fn max_image_sz(mut self, max_image_sz: Option<usize>) -> Self {
174        self.max_image_sz = max_image_sz;
175        self
176    }
177
178    /// Convert into a block `Iterator`.
179    pub fn into_blocks(self) -> decode::Blocks<R> {
180        decode::Blocks::new(self.reader, self.max_image_sz)
181    }
182
183    /// Convert into a frame `Iterator`.
184    pub fn into_frames(self) -> decode::Frames<R> {
185        decode::Frames::new(self.into_blocks())
186    }
187
188    /// Convert into a step `Iterator` without looping.
189    pub fn into_steps(self) -> decode::Steps<R> {
190        decode::Steps::new_once(self.into_frames())
191    }
192}
193
194impl<R: Read> IntoIterator for Decoder<R> {
195    type Item = Result<Step>;
196    type IntoIter = decode::Steps<R>;
197
198    /// Convert into a step `Iterator` with looping
199    fn into_iter(self) -> Self::IntoIter {
200        decode::Steps::new_looping(self.into_frames())
201    }
202}
203
204/// GIF file encoder
205///
206/// Can be converted to one of three encoders:
207/// * [into_step_enc] for high-level [Step]s
208/// * [into_frame_enc] for mid-level [Frame]s
209/// * [into_block_enc] for low-level [Block]s
210///
211/// ## Encoding Example
212/// ```
213/// use gift::{Encoder, Step};
214/// use pix::{gray::Gray8, Palette, Raster, rgb::SRgb8};
215/// use std::error::Error;
216/// use std::io::Write;
217///
218/// fn encode<W: Write>(mut w: W) -> Result<(), Box<dyn Error>> {
219///     let mut enc = Encoder::new(&mut w).into_step_enc();
220///     let mut raster = Raster::with_clear(4, 4);
221///     *raster.pixel_mut(0, 0) = Gray8::new(1);
222///     *raster.pixel_mut(1, 1) = Gray8::new(1);
223///     *raster.pixel_mut(2, 2) = Gray8::new(1);
224///     *raster.pixel_mut(3, 3) = Gray8::new(1);
225///     let mut palette = Palette::new(2);
226///     palette.set_entry(SRgb8::new(0xFF, 0, 0));
227///     palette.set_entry(SRgb8::new(0xFF, 0xFF, 0));
228///     let step = Step::with_indexed(raster, palette);
229///     enc.encode_step(&step)?;
230///     Ok(())
231/// }
232/// ```
233///
234/// [Block]: block/enum.Block.html
235/// [Frame]: block/struct.Frame.html
236/// [into_block_enc]: struct.Encoder.html#method.into_block_enc
237/// [into_frame_enc]: struct.Encoder.html#method.into_frame_enc
238/// [into_step_enc]: struct.Encoder.html#method.into_step_enc
239/// [Step]: struct.Step.html
240pub struct Encoder<W: Write> {
241    /// Writer for output data
242    writer: W,
243}
244
245impl<W: Write> Encoder<W> {
246    /// Create a new GIF encoder.
247    pub fn new(writer: W) -> Self {
248        Encoder { writer }
249    }
250
251    /// Convert into a block encoder.
252    pub fn into_block_enc(self) -> encode::BlockEnc<W> {
253        encode::BlockEnc::new(self.writer)
254    }
255
256    /// Convert into a frame encoder.
257    pub fn into_frame_enc(self) -> encode::FrameEnc<W> {
258        encode::FrameEnc::new(self.into_block_enc())
259    }
260
261    /// Convert into a step encoder.
262    pub fn into_step_enc(self) -> encode::StepEnc<W> {
263        encode::StepEnc::new(self.into_frame_enc())
264    }
265}