1 //! Common common used both by decoder and encoder
2 extern crate color_quant;
3 
4 use std::borrow::Cow;
5 
6 /// Disposal method
7 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
8 #[repr(u8)]
9 pub enum DisposalMethod {
10     /// StreamingDecoder is not required to take any action.
11     Any = 0,
12     /// Do not dispose.
13     Keep = 1,
14     /// Restore to background color.
15     Background = 2,
16     /// Restore to previous.
17     Previous = 3,
18 }
19 
20 impl DisposalMethod {
21     /// Converts `u8` to `Option<Self>`
from_u8(n: u8) -> Option<DisposalMethod>22     pub fn from_u8(n: u8) -> Option<DisposalMethod> {
23         match n {
24             0 => Some(DisposalMethod::Any),
25             1 => Some(DisposalMethod::Keep),
26             2 => Some(DisposalMethod::Background),
27             3 => Some(DisposalMethod::Previous),
28             _ => None
29         }
30     }
31 }
32 
33 /// Known GIF block labels.
34 ///
35 /// Note that the block uniquely specifies the layout of bytes that follow and how they are
36 /// framed. For example, the header always has a fixed length but is followed by a variable amount
37 /// of additional data. An image descriptor may be followed by a local color table depending on
38 /// information read in it. Therefore, it doesn't make sense to continue parsing after encountering
39 /// an unknown block as the semantics of following bytes are unclear.
40 ///
41 /// The extension block provides a common framing for an arbitrary amount of application specific
42 /// data which may be ignored.
43 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
44 #[repr(u8)]
45 pub enum Block {
46     /// Image block.
47     Image = 0x2C,
48     /// Extension block.
49     Extension = 0x21,
50     /// Image trailer.
51     Trailer = 0x3B,
52 }
53 
54 impl Block {
55     /// Converts `u8` to `Option<Self>`
from_u8(n: u8) -> Option<Block>56     pub fn from_u8(n: u8) -> Option<Block> {
57         match n {
58             0x2C => Some(Block::Image),
59             0x21 => Some(Block::Extension),
60             0x3B => Some(Block::Trailer),
61             _ => None
62         }
63     }
64 }
65 
66 /// A newtype wrapper around an arbitrary extension ID.
67 ///
68 /// An extension is some amount of byte data organized in sub-blocks so that one can skip over it
69 /// without knowing the semantics. Though technically you likely want to use a `Application`
70 /// extension, the library tries to stay flexible here.
71 ///
72 /// This allows us to customize the set of impls compared to a raw `u8`. It also clarifies the
73 /// intent and gives some inherent methods for interoperability with known extension types.
74 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
75 pub struct AnyExtension(pub u8);
76 
77 /// Known GIF extension labels.
78 ///
79 /// These are extensions which may be interpreted by the library and to which a specification with
80 /// the internal data layout is known.
81 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
82 #[repr(u8)]
83 pub enum Extension {
84     /// Plain Text extension.
85     ///
86     /// This instructs the decoder to render a text as characters in a grid of cells, in a
87     /// mono-spaced font of its choosing. This is seldom actually implemented and ignored by
88     /// ImageMagick. The color is always taken from the global table which further complicates any
89     /// use. No real information on the frame sequencing of this block is available in the
90     /// standard.
91     Text = 0x01,
92     /// Control extension.
93     Control = 0xF9,
94     /// Comment extension.
95     Comment = 0xFE,
96     /// Application extension.
97     ///
98     /// See [ImageMagick] for an idea of commonly recognized extensions.
99     ///
100     /// [ImageMagick]: https://github.com/ImageMagick/ImageMagick/blob/b0b58c6303195928060f55f9c3ca8233ab7f7733/coders/gif.c#L1128
101     Application = 0xFF,
102 }
103 
104 impl AnyExtension {
105     /// Decode the label as a known extension.
into_known(self) -> Option<Extension>106     pub fn into_known(self) -> Option<Extension> {
107         Extension::from_u8(self.0)
108     }
109 }
110 
111 impl From<Extension> for AnyExtension {
from(ext: Extension) -> Self112     fn from(ext: Extension) -> Self {
113         AnyExtension(ext as u8)
114     }
115 }
116 
117 impl Extension {
118     /// Converts `u8` to a `Extension` if it is known.
from_u8(n: u8) -> Option<Extension>119     pub fn from_u8(n: u8) -> Option<Extension> {
120         match n {
121             0x01 => Some(Extension::Text),
122             0xF9 => Some(Extension::Control),
123             0xFE => Some(Extension::Comment),
124             0xFF => Some(Extension::Application),
125             _ => None
126         }
127     }
128 }
129 
130 /// A GIF frame
131 #[derive(Debug, Clone)]
132 pub struct Frame<'a> {
133     /// Frame delay in units of 10 ms.
134     pub delay: u16,
135     /// Disposal method.
136     pub dispose: DisposalMethod,
137     /// Transparent index (if available).
138     pub transparent: Option<u8>,
139     /// True if the frame needs user input to be displayed.
140     pub needs_user_input: bool,
141     /// Offset from the top border of the canvas.
142     pub top: u16,
143     /// Offset from the left border of the canvas.
144     pub left: u16,
145     /// Width of the frame.
146     pub width: u16,
147     /// Height of the frame.
148     pub height: u16,
149     /// True if the image is interlaced.
150     pub interlaced: bool,
151     /// Frame local color palette if available.
152     pub palette: Option<Vec<u8>>,
153     /// Buffer containing the image data.
154     /// Only indices unless configured differently.
155     pub buffer: Cow<'a, [u8]>
156 }
157 
158 impl<'a> Default for Frame<'a> {
default() -> Frame<'a>159     fn default() -> Frame<'a> {
160         Frame {
161             delay: 0,
162             dispose: DisposalMethod::Keep,
163             transparent: None,
164             needs_user_input: false,
165             top: 0,
166             left: 0,
167             width: 0,
168             height: 0,
169             interlaced: false,
170             palette: None,
171             buffer: Cow::Borrowed(&[])
172         }
173     }
174 }
175 
176 impl Frame<'static> {
177     /// Creates a frame from pixels in RGBA format.
178     ///
179     /// This is a lossy method. The `gif` format does not support arbitrary alpha but only a 1-bit
180     /// transparency mask per pixel. Any non-zero alpha value will be interpreted as a fully opaque
181     /// pixel. Additionally, only 256 colors can appear in a single frame. The palette will be
182     /// reduced by the NeuQuant algorithm if necessary. Different frames have independent palettes.
183     ///
184     /// *Note: This method is not optimized for speed.*
185     ///
186     /// # Panics:
187     /// *   If the length of pixels does not equal `width * height * 4`.
from_rgba(width: u16, height: u16, pixels: &mut [u8]) -> Frame<'static>188     pub fn from_rgba(width: u16, height: u16, pixels: &mut [u8]) -> Frame<'static> {
189         Frame::from_rgba_speed(width, height, pixels, 1)
190     }
191 
192     /// Creates a frame from pixels in RGBA format.
193     ///
194     /// `speed` is a value in the range [1, 30].
195     /// The higher the value the faster it runs at the cost of image quality.
196     /// A `speed` of 10 is a good compromise between speed and quality.
197     ///
198     /// This is a lossy method. The `gif` format does not support arbitrary alpha but only a 1-bit
199     /// transparency mask per pixel. Any non-zero alpha value will be interpreted as a fully opaque
200     /// pixel. Additionally, only 256 colors can appear in a single frame. The palette will be
201     /// reduced by the NeuQuant algorithm if necessary. Different frames have independent palettes.
202     ///
203     /// # Panics:
204     /// *   If the length of pixels does not equal `width * height * 4`.
205     /// *   If `speed < 1` or `speed > 30`
from_rgba_speed(width: u16, height: u16, pixels: &mut [u8], speed: i32) -> Frame<'static>206     pub fn from_rgba_speed(width: u16, height: u16, pixels: &mut [u8], speed: i32) -> Frame<'static> {
207         assert_eq!(width as usize * height as usize * 4, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame");
208         assert!(speed >= 1 && speed <= 30, "speed needs to be in the range [1, 30]");
209         let mut frame = Frame::default();
210         let mut transparent = None;
211         for pix in pixels.chunks_mut(4) {
212             if pix[3] != 0 {
213                 pix[3] = 0xFF;
214             } else {
215                 transparent = Some([pix[0], pix[1], pix[2], pix[3]])
216             }
217         }
218         frame.width = width;
219         frame.height = height;
220         let nq = color_quant::NeuQuant::new(speed, 256, pixels);
221         frame.buffer = Cow::Owned(pixels.chunks(4).map(|pix| nq.index_of(pix) as u8).collect());
222         frame.palette = Some(nq.color_map_rgb());
223         frame.transparent = if let Some(t) = transparent {
224             Some(nq.index_of(&t) as u8)
225         } else {
226             None
227         };
228 
229         frame
230     }
231 
232     /// Creates a frame from a palette and indexed pixels.
233     ///
234     /// # Panics:
235     /// *   If the length of pixels does not equal `width * height`.
236     /// *   If the length of palette > `256 * 3`.
from_palette_pixels(width: u16, height: u16, pixels: &[u8], palette: &[u8], transparent: Option<u8>) -> Frame<'static>237     pub fn from_palette_pixels(width: u16, height: u16, pixels: &[u8], palette: &[u8], transparent: Option<u8>) -> Frame<'static> {
238         assert_eq!(width as usize * height as usize, pixels.len(), "Too many or too little pixels for the given width and height to create a GIF Frame");
239         assert!(palette.len() <= 256*3, "Too many palette values to create a GIF Frame");
240         let mut frame = Frame::default();
241 
242         frame.width = width;
243         frame.height = height;
244 
245         frame.buffer = Cow::Owned(pixels.to_vec());
246         frame.palette = Some(palette.to_vec());
247 
248         frame.transparent = transparent;
249 
250         frame
251     }
252 
253     /// Creates a frame from indexed pixels in the global palette.
254     ///
255     /// # Panics:
256     /// *   If the length of pixels does not equal `width * height`.
from_indexed_pixels(width: u16, height: u16, pixels: &[u8], transparent: Option<u8>) -> Frame<'static>257     pub fn from_indexed_pixels(width: u16, height: u16, pixels: &[u8], transparent: Option<u8>) -> Frame<'static> {
258         assert_eq!(width as usize * height as usize, pixels.len(), "Too many or too little pixels for the given width and height to create a GIF Frame");
259         let mut frame = Frame::default();
260 
261         frame.width = width;
262         frame.height = height;
263 
264         frame.buffer = Cow::Owned(pixels.to_vec());
265         frame.palette = None;
266 
267         frame.transparent = transparent;
268 
269         frame
270     }
271 
272     /// Creates a frame from pixels in RGB format.
273     ///
274     /// This is a lossy method. In the `gif` format only 256 colors can appear in a single frame.
275     /// The palette will be reduced by the NeuQuant algorithm if necessary. Different frames have
276     /// independent palettes.
277     ///
278     /// *Note: This method is not optimized for speed.*
279     ///
280     /// # Panics:
281     /// *   If the length of pixels does not equal `width * height * 3`.
from_rgb(width: u16, height: u16, pixels: &[u8]) -> Frame<'static>282     pub fn from_rgb(width: u16, height: u16, pixels: &[u8]) -> Frame<'static> {
283         Frame::from_rgb_speed(width, height, pixels, 1)
284     }
285 
286     /// Creates a frame from pixels in RGB format.
287     ///
288     /// `speed` is a value in the range [1, 30].
289     ///
290     /// This is a lossy method. In the `gif` format only 256 colors can appear in a single frame.
291     /// The palette will be reduced by the NeuQuant algorithm if necessary. Different frames have
292     /// independent palettes.
293     ///
294     /// The higher the value the faster it runs at the cost of image quality.
295     /// A `speed` of 10 is a good compromise between speed and quality.
296     ///
297     /// # Panics:
298     /// *   If the length of pixels does not equal `width * height * 3`.
299     /// *   If `speed < 1` or `speed > 30`
from_rgb_speed(width: u16, height: u16, pixels: &[u8], speed: i32) -> Frame<'static>300     pub fn from_rgb_speed(width: u16, height: u16, pixels: &[u8], speed: i32) -> Frame<'static> {
301         assert_eq!(width as usize * height as usize * 3, pixels.len(), "Too much or too little pixel data for the given width and height to create a GIF Frame");
302         let mut vec: Vec<u8> = Vec::with_capacity(pixels.len() + width as usize * height as usize);
303         for v in pixels.chunks(3) {
304             vec.extend([v[0], v[1], v[2], 0xFF].iter().cloned())
305         }
306         Frame::from_rgba_speed(width, height, &mut vec, speed)
307     }
308 
required_bytes(&self) -> usize309     pub(crate) fn required_bytes(&self) -> usize {
310         usize::from(self.width) * usize::from(self.height)
311     }
312 }
313