1 //! # Minimal gif encoder
2 use std::io;
3 use std::io::prelude::*;
4 use std::fmt;
5 use std::error;
6
7 use weezl::{BitOrder, encode::Encoder as LzwEncoder};
8
9 use crate::traits::{WriteBytesExt};
10 use crate::common::{AnyExtension, Block, DisposalMethod, Extension, Frame};
11
12 #[derive(Debug)]
13 enum FormatErrorKind {
14 /// The image has too many colors.
15 TooManyColors,
16 /// The image has no color palette which is required.
17 MissingColorPalette,
18 }
19
20 /// The image has incorrect properties, making it impossible to encode as a gif.
21 #[derive(Debug)]
22 pub struct EncodingFormatError {
23 kind: FormatErrorKind
24 }
25
26 impl error::Error for EncodingFormatError {}
27 impl fmt::Display for EncodingFormatError {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result28 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self.kind {
30 FormatErrorKind::TooManyColors => write!(fmt, "the image has too many colors"),
31 FormatErrorKind::MissingColorPalette => write!(fmt, "the GIF format requires a color palette but none was given")
32 }
33 }
34 }
35
36 impl From<FormatErrorKind> for EncodingFormatError {
from(kind: FormatErrorKind) -> Self37 fn from(kind: FormatErrorKind) -> Self {
38 EncodingFormatError { kind }
39 }
40 }
41
42 #[derive(Debug)]
43 /// Encoding error.
44 pub enum EncodingError {
45 /// Returned if the to image is not encodable as a gif.
46 Format(EncodingFormatError),
47 /// Wraps `std::io::Error`.
48 Io(io::Error),
49 }
50
51 impl fmt::Display for EncodingError {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result52 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
53 match self {
54 EncodingError::Io(err) => err.fmt(fmt),
55 EncodingError::Format(err) => err.fmt(fmt),
56 }
57 }
58 }
59
60 impl error::Error for EncodingError {
source(&self) -> Option<&(dyn error::Error + 'static)>61 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
62 match self {
63 EncodingError::Io(err) => Some(err),
64 EncodingError::Format(err) => Some(err),
65 }
66 }
67 }
68
69 impl From<io::Error> for EncodingError {
from(err: io::Error) -> Self70 fn from(err: io::Error) -> Self {
71 EncodingError::Io(err)
72 }
73 }
74
75 impl From<EncodingFormatError> for EncodingError {
from(err: EncodingFormatError) -> Self76 fn from(err: EncodingFormatError) -> Self {
77 EncodingError::Format(err)
78 }
79 }
80
81 impl From<FormatErrorKind> for EncodingError {
from(kind: FormatErrorKind) -> Self82 fn from(kind: FormatErrorKind) -> Self {
83 EncodingError::Format(kind.into())
84 }
85 }
86
87
88 /// Number of repetitions
89 #[derive(Copy, Clone, Debug)]
90 pub enum Repeat {
91 /// Finite number of repetitions
92 Finite(u16),
93 /// Infinite number of repetitions
94 Infinite
95 }
96
97 /// Extension data.
98 pub enum ExtensionData {
99 /// Control extension. Use `ExtensionData::new_control_ext` to construct.
100 Control {
101 /// Flags.
102 flags: u8,
103 /// Frame delay.
104 delay: u16,
105 /// Transparent index.
106 trns: u8
107 },
108 /// Sets the number of repetitions
109 Repetitions(Repeat)
110 }
111
112 impl ExtensionData {
113 /// Constructor for control extension data.
114 ///
115 /// `delay` is given in units of 10 ms.
new_control_ext(delay: u16, dispose: DisposalMethod, needs_user_input: bool, trns: Option<u8>) -> ExtensionData116 pub fn new_control_ext(delay: u16, dispose: DisposalMethod,
117 needs_user_input: bool, trns: Option<u8>) -> ExtensionData {
118 let mut flags = 0;
119 let trns = match trns {
120 Some(trns) => {
121 flags |= 1;
122 trns as u8
123 },
124 None => 0
125 };
126 flags |= (needs_user_input as u8) << 1;
127 flags |= (dispose as u8) << 2;
128 ExtensionData::Control {
129 flags: flags,
130 delay: delay,
131 trns: trns
132 }
133 }
134 }
135
136 /// GIF encoder.
137 pub struct Encoder<W: Write> {
138 w: W,
139 global_palette: bool,
140 width: u16,
141 height: u16,
142 buffer: Vec<u8>
143 }
144
145 impl<W: Write> Encoder<W> {
146 /// Creates a new encoder.
147 ///
148 /// `global_palette` gives the global color palette in the format `[r, g, b, ...]`,
149 /// if no global palette shall be used an empty slice may be supplied.
new(w: W, width: u16, height: u16, global_palette: &[u8]) -> Result<Self, EncodingError>150 pub fn new(w: W, width: u16, height: u16, global_palette: &[u8]) -> Result<Self, EncodingError> {
151 let buffer_size = (width as usize) * (height as usize);
152 Encoder {
153 w: w,
154 global_palette: false,
155 width: width,
156 height: height,
157 buffer: Vec::with_capacity(buffer_size)
158 }.write_global_palette(global_palette)
159 }
160
161 /// Write an extension block that signals a repeat behaviour.
set_repeat(&mut self, repeat: Repeat) -> Result<(), EncodingError>162 pub fn set_repeat(&mut self, repeat: Repeat) -> Result<(), EncodingError> {
163 self.write_extension(ExtensionData::Repetitions(repeat))
164 }
165
166 /// Writes the global color palette.
write_global_palette(mut self, palette: &[u8]) -> Result<Self, EncodingError>167 pub fn write_global_palette(mut self, palette: &[u8]) -> Result<Self, EncodingError> {
168 self.global_palette = true;
169 let mut flags = 0;
170 flags |= 0b1000_0000;
171 let num_colors = palette.len() / 3;
172 if num_colors > 256 {
173 return Err(EncodingError::from(FormatErrorKind::TooManyColors));
174 }
175 // Size of global color table.
176 flags |= flag_size(num_colors);
177 // Color resolution .. FIXME. This is mostly ignored (by ImageMagick at least) but hey, we
178 // should use some sensible value here or even allow configuring it?
179 flags |= flag_size(num_colors) << 4; // wtf flag
180 self.write_screen_desc(flags)?;
181 self.write_color_table(palette)?;
182 Ok(self)
183 }
184
185 /// Writes a frame to the image.
186 ///
187 /// Note: This function also writes a control extension if necessary.
write_frame(&mut self, frame: &Frame) -> Result<(), EncodingError>188 pub fn write_frame(&mut self, frame: &Frame) -> Result<(), EncodingError> {
189 // TODO commented off to pass test in lib.rs
190 //if frame.delay > 0 || frame.transparent.is_some() {
191 self.write_extension(ExtensionData::new_control_ext(
192 frame.delay,
193 frame.dispose,
194 frame.needs_user_input,
195 frame.transparent
196
197 ))?;
198 //}
199 self.w.write_le(Block::Image as u8)?;
200 self.w.write_le(frame.left)?;
201 self.w.write_le(frame.top)?;
202 self.w.write_le(frame.width)?;
203 self.w.write_le(frame.height)?;
204 let mut flags = 0;
205 if frame.interlaced {
206 flags |= 0b0100_0000;
207 }
208 match frame.palette {
209 Some(ref palette) => {
210 flags |= 0b1000_0000;
211 let num_colors = palette.len() / 3;
212 if num_colors > 256 {
213 return Err(EncodingError::from(FormatErrorKind::TooManyColors));
214 }
215 flags |= flag_size(num_colors);
216 self.w.write_le(flags)?;
217 self.write_color_table(palette)
218 },
219 None => if !self.global_palette {
220 Err(EncodingError::from(FormatErrorKind::MissingColorPalette))
221 } else {
222 self.w.write_le(flags).map_err(Into::into)
223 }
224 }?;
225 self.write_image_block(&frame.buffer)
226 }
227
write_image_block(&mut self, data: &[u8]) -> Result<(), EncodingError>228 fn write_image_block(&mut self, data: &[u8]) -> Result<(), EncodingError> {
229 {
230 let min_code_size: u8 = match flag_size(*data.iter().max().unwrap_or(&0) as usize + 1) + 1 {
231 1 => 2, // As per gif spec: The minimal code size has to be >= 2
232 n => n
233 };
234 self.w.write_le(min_code_size)?;
235 self.buffer.clear();
236 let mut enc = LzwEncoder::new(BitOrder::Lsb, min_code_size);
237 let len = enc.into_vec(&mut self.buffer).encode_all(data).consumed_out;
238
239 // Write blocks. `chunks_exact` seems to be slightly faster
240 // than `chunks` according to both Rust docs and benchmark results.
241 let mut iter = self.buffer[..len].chunks_exact(0xFF);
242 while let Some(full_block) = iter.next() {
243 self.w.write_le(0xFFu8)?;
244 self.w.write_all(full_block)?;
245 }
246 let last_block = iter.remainder();
247 if !last_block.is_empty() {
248 self.w.write_le(last_block.len() as u8)?;
249 self.w.write_all(last_block)?;
250 }
251 }
252 self.w.write_le(0u8).map_err(Into::into)
253 }
254
write_color_table(&mut self, table: &[u8]) -> Result<(), EncodingError>255 fn write_color_table(&mut self, table: &[u8]) -> Result<(), EncodingError> {
256 let num_colors = table.len() / 3;
257 if num_colors > 256 {
258 return Err(EncodingError::from(FormatErrorKind::TooManyColors));
259 }
260 let size = flag_size(num_colors);
261 self.w.write_all(&table[..num_colors * 3])?;
262 // Waste some space as of gif spec
263 for _ in 0..((2 << size) - num_colors) {
264 self.w.write_all(&[0, 0, 0])?
265 }
266 Ok(())
267 }
268
269 /// Writes an extension to the image.
270 ///
271 /// It is normally not necessary to call this method manually.
write_extension(&mut self, extension: ExtensionData) -> Result<(), EncodingError>272 pub fn write_extension(&mut self, extension: ExtensionData) -> Result<(), EncodingError> {
273 use self::ExtensionData::*;
274 // 0 finite repetitions can only be achieved
275 // if the corresponting extension is not written
276 if let Repetitions(Repeat::Finite(0)) = extension {
277 return Ok(())
278 }
279 self.w.write_le(Block::Extension as u8)?;
280 match extension {
281 Control { flags, delay, trns } => {
282 self.w.write_le(Extension::Control as u8)?;
283 self.w.write_le(4u8)?;
284 self.w.write_le(flags)?;
285 self.w.write_le(delay)?;
286 self.w.write_le(trns)?;
287 }
288 Repetitions(repeat) => {
289 self.w.write_le(Extension::Application as u8)?;
290 self.w.write_le(11u8)?;
291 self.w.write_all(b"NETSCAPE2.0")?;
292 self.w.write_le(3u8)?;
293 self.w.write_le(1u8)?;
294 match repeat {
295 Repeat::Finite(no) => self.w.write_le(no)?,
296 Repeat::Infinite => self.w.write_le(0u16)?,
297 }
298 }
299 }
300 self.w.write_le(0u8).map_err(Into::into)
301 }
302
303 /// Writes a raw extension to the image.
304 ///
305 /// This method can be used to write an unsupported extension to the file. `func` is the extension
306 /// identifier (e.g. `Extension::Application as u8`). `data` are the extension payload blocks. If any
307 /// contained slice has a lenght > 255 it is automatically divided into sub-blocks.
write_raw_extension(&mut self, func: AnyExtension, data: &[&[u8]]) -> io::Result<()>308 pub fn write_raw_extension(&mut self, func: AnyExtension, data: &[&[u8]]) -> io::Result<()> {
309 self.w.write_le(Block::Extension as u8)?;
310 self.w.write_le(func.0)?;
311 for block in data {
312 for chunk in block.chunks(0xFF) {
313 self.w.write_le(chunk.len() as u8)?;
314 self.w.write_all(chunk)?;
315 }
316 }
317 self.w.write_le(0u8)
318 }
319
320 /// Writes the logical screen desriptor
write_screen_desc(&mut self, flags: u8) -> io::Result<()>321 fn write_screen_desc(&mut self, flags: u8) -> io::Result<()> {
322 self.w.write_all(b"GIF89a")?;
323 self.w.write_le(self.width)?;
324 self.w.write_le(self.height)?;
325 self.w.write_le(flags)?; // packed field
326 self.w.write_le(0u8)?; // bg index
327 self.w.write_le(0u8) // aspect ratio
328 }
329 }
330
331 impl<W: Write> Drop for Encoder<W> {
332
333 #[cfg(feature = "raii_no_panic")]
drop(&mut self)334 fn drop(&mut self) {
335 let _ = self.w.write_le(Block::Trailer as u8);
336 }
337
338 #[cfg(not(feature = "raii_no_panic"))]
drop(&mut self)339 fn drop(&mut self) {
340 self.w.write_le(Block::Trailer as u8).unwrap()
341 }
342 }
343
344 // Color table size converted to flag bits
flag_size(size: usize) -> u8345 fn flag_size(size: usize) -> u8 {
346 match size {
347 0 ..=2 => 0,
348 3 ..=4 => 1,
349 5 ..=8 => 2,
350 9 ..=16 => 3,
351 17 ..=32 => 4,
352 33 ..=64 => 5,
353 65 ..=128 => 6,
354 129..=256 => 7,
355 _ => 7
356 }
357 }
358
359 #[test]
error_cast()360 fn error_cast() {
361 let _ : Box<dyn error::Error> = EncodingError::from(FormatErrorKind::MissingColorPalette).into();
362 }
363