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}