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