apng_encoder/apng/
encoder.rs

1
2use std::cmp;
3use std::io::{self, Write};
4
5use byteorder::{BigEndian, WriteBytesExt};
6use enum_iterator::IntoEnumIterator;
7use flate2::Compression;
8use flate2::Crc;
9use flate2::write::ZlibEncoder;
10
11use super::{Color, Frame, Meta};
12use super::errors::{ApngResult, ApngError};
13
14
15/// APNG Encoder
16///
17/// # Example
18///
19/// ```
20/// use apng_encoder::{Color, Delay, Frame, Meta};
21/// use apng_encoder::Encoder;
22/// use std::fs::File;
23///
24/// // Generate 2x2 Animated PNG (4 frames)
25/// let meta = Meta {
26///     width: 2,
27///     height: 2,
28///     color: Color::RGB(8),
29///     frames: 4,
30///     plays: None, // Infinite loop
31/// };
32///
33/// // Delay = 1/2 (0.5) seconds
34/// let frame = Frame {
35///     delay: Some(Delay::new(1, 2)),
36///     ..Default::default()
37/// };
38///
39/// let mut file = File::create("test-output/2x2.png").unwrap();
40/// let mut encoder = Encoder::create(&mut file, meta).unwrap();
41///
42/// // RED   GREEN
43/// // BLACK BLUE
44/// encoder.write_frame(
45///     &[
46///  // (x=0,y=0)            (x=1,y=0)
47///     0xFF, 0x00, 0x00,    0x00, 0xFF, 0x00,
48///  // (x=0,y=1)            (x=1,y=1)
49///     0x00, 0x00, 0x00,    0x00, 0x00, 0xFF,
50///     ],
51///     Some(&frame),
52///     None,
53///     None).unwrap();
54/// // BLACK RED
55/// // BLUE  GREEN
56/// encoder.write_frame(
57///     &[
58///     0x00, 0x00, 0x00,   0xFF, 0x00, 0x00,
59///     0x00, 0x00, 0xFF,   0x00, 0xFF, 0x00,
60///     ],
61///     Some(&frame),
62///     None,
63///     None).unwrap();
64/// // BLUE  BLACK
65/// // GREEN RED
66/// encoder.write_frame(
67///     &[
68///     0x00, 0x00, 0xFF,   0x00, 0x00, 0x00,
69///     0x00, 0xFF, 0x00,   0xFF, 0x00, 0x00,
70///     ],
71///     Some(&frame),
72///     None,
73///     None).unwrap();
74/// // GREEN BLUE
75/// // RED   BLACK
76/// encoder.write_frame(
77///     &[
78///     0x00, 0xFF, 0x00,   0x00, 0x00, 0xFF,
79///     0xFF, 0x00, 0x00,   0x00, 0x00, 0x00,
80///     ],
81///     Some(&frame),
82///     None,
83///     None).unwrap();
84/// // !!IMPORTANT DONT FORGET!!
85/// encoder.finish().unwrap();
86/// ```
87
88
89
90pub struct Encoder<'a, F: io::Write> {
91    default_image: bool,
92    meta: Meta,
93    sequence: u32,
94    writer: &'a mut F,
95    written_frames: usize,
96}
97
98#[derive(Clone, Copy, IntoEnumIterator)]
99pub enum Filter {
100    None = 0,
101    Sub = 1,
102    Up = 2,
103    Average = 3,
104    Paeth = 4,
105}
106
107#[derive(Clone, Copy, Debug)]
108pub struct Rectangle {
109    height: u32,
110    modified: bool,
111    width: u32,
112    x: u32,
113    y: u32,
114}
115
116
117impl<'a, F: io::Write> Encoder<'a, F> {
118    pub fn create(writer: &'a mut F, meta: Meta) -> ApngResult<Self> {
119        validate_color(meta.color)?;
120        let mut instance = Encoder {
121            default_image: false,
122            meta,
123            sequence: 0,
124            writer,
125            written_frames: 0,
126        };
127        Self::write_signature(&mut instance)?;
128        Self::write_image_header(&mut instance)?;
129        Self::write_animation_control(&mut instance)?;
130        Ok(instance)
131    }
132
133    pub fn finish(mut self) -> ApngResult<()> {
134        if self.written_frames < self.meta.frames as usize {
135            return Err(ApngError::NotEnoughFrames(self.meta.frames as usize, self.written_frames));
136        }
137        let zero: [u8;0] = [];
138        self.write_chunk(*b"IEND", &zero)
139    }
140
141    pub fn write_default_image(&mut self, image_data: &[u8], filter: Option<Filter>, row_stride: Option<usize>) -> ApngResult<()> {
142        if self.default_image {
143            return Err(ApngError::MulitiDefaultImage);
144        }
145        if 0 < self.sequence {
146            return Err(ApngError::DefaultImageNotAtFirst);
147        }
148        self.default_image = true;
149        let rect = self.compute_rect(None);
150        let mut buffer = vec![];
151        self.make_image_data(image_data, row_stride, &mut buffer, rect, filter)?;
152        self.write_chunk(*b"IDAT", &buffer)?;
153        Ok(())
154    }
155
156    pub fn write_frame(&mut self, image_data: &[u8], frame: Option<&Frame>, filter: Option<Filter>, row_stride: Option<usize>) -> ApngResult<()> {
157        self.written_frames += 1;
158        if (self.meta.frames as usize) < self.written_frames {
159            return Err(ApngError::TooManyFrames(self.meta.frames as usize, self.written_frames));
160        }
161        if !self.default_image && self.sequence == 0 {
162            self.write_animation_frame_with_default(image_data, row_stride, frame, filter)
163        } else {
164            self.write_animation_frame(image_data, row_stride, frame, filter)
165        }
166    }
167
168    fn compute_rect(&self, frame: Option<&Frame>) -> Rectangle {
169        let width = frame.and_then(|it| it.width).unwrap_or(self.meta.width);
170        let height = frame.and_then(|it| it.height).unwrap_or(self.meta.height);
171        let x = frame.and_then(|it| it.x).unwrap_or(0);
172        let y = frame.and_then(|it| it.y).unwrap_or(0);
173        let modified = x != 0 || y != 0 || width != self.meta.width || height != self.meta.height;
174        Rectangle { width, height, x, y, modified }
175    }
176
177    fn next_sequence(&mut self) -> u32 {
178        let result = self.sequence;
179        self.sequence += 1;
180        result
181    }
182
183    fn make_image_data(&mut self, image_data: &[u8], row_stride: Option<usize>, buffer: &mut Vec<u8>, rect: Rectangle, filter: Option<Filter>) -> ApngResult<()> {
184        let row_stride = self.compute_row_stride(&image_data, row_stride, rect)?;
185        let mut e = ZlibEncoder::new(buffer, Compression::best());
186        let pixel_bytes = self.meta.color.pixel_bytes();
187        let filter = filter.map(Ok).unwrap_or_else(|| infer_best_filter(image_data, row_stride, pixel_bytes))?;
188        filter.apply(image_data, row_stride, pixel_bytes, &mut e)?;
189        e.finish()?;
190        Ok(())
191    }
192
193    fn compute_row_stride(&self, image_data: &[u8], row_stride: Option<usize>, rect: Rectangle) -> ApngResult<usize> {
194        let row_stride = row_stride.unwrap_or_else(|| rect.width as usize * self.meta.color.pixel_bytes());
195        let data_height = (image_data.len() / row_stride) as u32;
196        if self.meta.width < rect.right() || self.meta.height < rect.bottom() || rect.bottom() < data_height{
197            return Err(ApngError::TooLargeImage);
198        }
199        if data_height < rect.height {
200            return Err(ApngError::TooSmallImage);
201        }
202        Ok(row_stride)
203    }
204
205    fn write_animation_frame(&mut self, image_data: &[u8], row_stride: Option<usize>, frame: Option<&Frame>, filter: Option<Filter>) -> ApngResult<()> {
206        let rect = self.write_frame_control(frame)?;
207        let mut buffer = vec![];
208        buffer.write_u32::<BigEndian>(self.next_sequence())?;
209        self.make_image_data(image_data, row_stride, &mut buffer, rect, filter)?;
210        self.write_chunk(*b"fdAT", &buffer)?;
211        Ok(())
212    }
213
214    fn write_animation_frame_with_default(&mut self, image_data: &[u8], row_stride: Option<usize>, frame: Option<&Frame>, filter: Option<Filter>) -> ApngResult<()> {
215        let rect = self.write_frame_control(frame)?;
216        if rect.modified {
217            return Err(ApngError::InvalidDefaultImageRectangle);
218        }
219        let mut buffer = vec![];
220        self.make_image_data(image_data, row_stride, &mut buffer, rect, filter)?;
221        self.write_chunk(*b"IDAT", &buffer)?;
222        Ok(())
223    }
224
225    fn write_animation_control(&mut self) -> ApngResult<()> {
226        let mut buffer = vec![];
227        buffer.write_u32::<BigEndian>(self.meta.frames)?;
228        buffer.write_u32::<BigEndian>(self.meta.plays.unwrap_or(0))?;
229        self.write_chunk(*b"acTL", &buffer)
230    }
231
232    fn write_chunk(&mut self, chunk_type: [u8;4], chunk_data: &[u8]) -> ApngResult<()> {
233        // Length
234        self.writer.write_u32::<BigEndian>(chunk_data.len() as u32)?;
235        // Type
236        self.writer.write_all(&chunk_type)?;
237        // Data
238        self.writer.write_all(chunk_data)?;
239        // CRC
240        let mut crc = Crc::new();
241        crc.update(&chunk_type);
242        crc.update(chunk_data);
243        self.writer.write_u32::<BigEndian>(crc.sum() as u32)?;
244        Ok(())
245    }
246
247    fn write_frame_control(&mut self, frame: Option<&Frame>) -> ApngResult<Rectangle> {
248        let rect = self.compute_rect(frame);
249        let delay = frame.and_then(|it| it.delay).unwrap_or_default();
250        let dispose = frame.and_then(|it| it.dispose_operator).unwrap_or_default() as u8;
251        let blend = frame.and_then(|it| it.blend_operator).unwrap_or_default() as u8;
252
253        let mut buffer = vec![];
254        buffer.write_u32::<BigEndian>(self.next_sequence())?;
255        buffer.write_u32::<BigEndian>(rect.width)?;
256        buffer.write_u32::<BigEndian>(rect.height)?;
257        buffer.write_u32::<BigEndian>(rect.x)?;
258        buffer.write_u32::<BigEndian>(rect.y)?;
259        buffer.write_u16::<BigEndian>(delay.numerator)?;
260        buffer.write_u16::<BigEndian>(delay.denominator)?;
261        buffer.write_all(&[dispose, blend])?;
262        self.write_chunk(*b"fcTL", &buffer)?;
263
264        Ok(rect)
265    }
266
267    fn write_image_header(&mut self) -> ApngResult<()> {
268        use super::Color::*;
269
270        let mut buffer = vec![];
271        buffer.write_u32::<BigEndian>(self.meta.width)?;
272        buffer.write_u32::<BigEndian>(self.meta.height)?;
273        // Alpha - Color - Palette
274        let color_type = match self.meta.color {
275            Grayscale(_) => 0b000,
276            GrayscaleA(_) => 0b100,
277            RGB(_) => 0b010,
278            RGBA(_) => 0b110,
279        };
280        // ... compression_method, filter_method, interlace_method
281        buffer.write_all(&[self.meta.color.bit_depth(), color_type, 0, 0, 0])?;
282        self.write_chunk(*b"IHDR", &buffer)
283    }
284
285    fn write_signature(&mut self) -> ApngResult<()> {
286        self.writer.write_all(&[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])?;
287        Ok(())
288    }
289}
290
291
292impl Filter {
293    fn apply<E: Write>(self, image_data: &[u8], row_stride: usize, pixel_bytes: usize, e: &mut E) -> ApngResult<()> {
294        let f = match self {
295            Filter::Average => filter_average,
296            Filter::None => filter_none,
297            Filter::Paeth => filter_paeth,
298            Filter::Sub => filter_sub,
299            Filter::Up => filter_up,
300        };
301        f(image_data, row_stride, pixel_bytes, e)
302    }
303}
304
305
306impl Rectangle {
307    fn right(&self) -> u32 {
308        self.x + self.width
309    }
310
311    fn bottom(&self) -> u32 {
312        self.y + self.height
313    }
314}
315
316
317fn filter_none<E: Write>(image_data: &[u8], row_stride: usize, _pixel_bytes: usize, e: &mut E) -> ApngResult<()> {
318    for line in image_data.chunks(row_stride) {
319        e.write_all(&[0x00])?;
320        e.write_all(line)?;
321    }
322    Ok(())
323}
324
325fn filter_sub<E: Write>(image_data: &[u8], row_stride: usize, pixel_bytes: usize, e: &mut E) -> ApngResult<()> {
326    let mut buffer = vec![0; row_stride];
327
328    for line in image_data.chunks(row_stride) {
329        e.write_all(&[0x01])?;
330        buffer[..pixel_bytes].clone_from_slice(&line[..pixel_bytes]);
331        for (i, it) in buffer.iter_mut().enumerate().take(row_stride).skip(pixel_bytes) {
332            *it = line[i].wrapping_sub(line[i - pixel_bytes]);
333        }
334        e.write_all(&buffer)?;
335    }
336
337    Ok(())
338}
339
340fn filter_up<E: Write>(image_data: &[u8], row_stride: usize, _pixel_bytes: usize, e: &mut E) -> ApngResult<()> {
341    let lines: Vec<&[u8]> = image_data.chunks(row_stride).collect();
342    let mut buffer = vec![0; row_stride];
343
344    e.write_all(&[0x02])?;
345    e.write_all(&lines[0])?;
346
347    for line in lines.windows(2) {
348        e.write_all(&[0x02])?;
349        for (i, it) in buffer.iter_mut().enumerate().take(row_stride) {
350            *it = line[1][i].wrapping_sub(line[0][i]);
351        }
352        e.write_all(&buffer)?;
353    }
354
355    Ok(())
356}
357
358fn filter_average<E: Write>(image_data: &[u8], row_stride: usize, pixel_bytes: usize, e: &mut E) -> ApngResult<()> {
359    let lines: Vec<&[u8]> = image_data.chunks(row_stride).collect();
360    let mut buffer = vec![0; row_stride];
361
362    e.write_all(&[0x03])?;
363    buffer[..pixel_bytes].clone_from_slice(&lines[0][..pixel_bytes]);
364    for (i, it) in buffer.iter_mut().enumerate().take(row_stride).skip(pixel_bytes) {
365        *it = lines[0][i].wrapping_sub(lines[0][i - pixel_bytes] / 2);
366    }
367    e.write_all(&buffer)?;
368
369    for line in lines.windows(2) {
370        e.write_all(&[0x03])?;
371        for (i, it) in buffer.iter_mut().enumerate().take(pixel_bytes) {
372            *it = line[1][i].wrapping_sub(line[0][i] / 2);
373        }
374        for (i, it) in buffer.iter_mut().enumerate().take(row_stride).skip(pixel_bytes) {
375            let sum = (i16::from(line[1][i - pixel_bytes]) + i16::from(line[0][i])) / 2;
376            *it = line[1][i].wrapping_sub(sum as u8);
377        }
378        e.write_all(&buffer)?;
379    }
380
381    Ok(())
382}
383
384fn filter_paeth<E: Write>(image_data: &[u8], row_stride: usize, pixel_bytes: usize, e: &mut E) -> ApngResult<()> {
385    fn paeth(left: u8, up_left: u8, up: u8) -> u8 {
386        let w_left = i16::from(left);
387        let w_up = i16::from(up);
388        let w_up_left = i16::from(up_left);
389
390        let base = w_left + w_up - w_up_left;
391        let d_left = (base - w_left).abs();
392        let d_up = (base - w_up).abs();
393        let d_up_left = (base - w_up_left).abs();
394
395        if d_left <= d_up && d_left <= d_up_left {
396            return left;
397        }
398
399        if d_up <= d_up_left {
400            return up;
401        }
402
403        up_left
404    }
405
406    let lines: Vec<&[u8]> = image_data.chunks(row_stride).collect();
407    let mut buffer = vec![0; row_stride];
408
409    e.write_all(&[0x04])?;
410    buffer[..pixel_bytes].clone_from_slice(&lines[0][..pixel_bytes]);
411    for (i, it) in buffer.iter_mut().enumerate().take(row_stride).skip(pixel_bytes) {
412        *it = lines[0][i].wrapping_sub(paeth(lines[0][i - pixel_bytes], 0, 0));
413    }
414    e.write_all(&buffer)?;
415
416    for line in lines.windows(2) {
417        e.write_all(&[0x04])?;
418        for (i, it) in buffer.iter_mut().enumerate().take(pixel_bytes) {
419            *it = line[1][i].wrapping_sub(paeth(0, 0, line[0][i]));
420        }
421        for (i, it) in buffer.iter_mut().enumerate().take(row_stride).skip(pixel_bytes) {
422            *it = line[1][i].wrapping_sub(paeth(line[1][i - pixel_bytes], line[0][i - pixel_bytes], line[0][i]));
423        }
424        e.write_all(&buffer)?;
425    }
426
427    Ok(())
428}
429
430fn get_compressed_size(filter: Filter, image_data: &[u8], row_stride: usize, pixel_bytes: usize) -> ApngResult<usize> {
431    let mut out = vec![];
432    filter.apply(image_data, row_stride, pixel_bytes, &mut out)?;
433    Ok(out.len())
434}
435
436fn infer_best_filter(image_data: &[u8], row_stride: usize, pixel_bytes: usize) -> ApngResult<Filter> {
437    let mut tiny_image_data = vec![];
438    let len = image_data.len();
439    let lines = len / row_stride;
440
441    if 50 < lines {
442        let top_end = row_stride * 10;
443        let middle_start = cmp::max(top_end, lines / 2 * row_stride);
444        let middle_end = cmp::min(middle_start + 10 * row_stride, len);
445        let bottom_start = cmp::max(middle_end, (cmp::max(lines, 10) - 10) * row_stride);
446
447        tiny_image_data.extend_from_slice(&image_data[0 .. top_end]);
448        tiny_image_data.extend_from_slice(&image_data[middle_start .. middle_end]);
449        tiny_image_data.extend_from_slice(&image_data[bottom_start .. image_data.len()]);
450    } else {
451        tiny_image_data.extend_from_slice(&image_data[0 .. cmp::min(10, lines) * row_stride]);
452    }
453
454
455    let mut results = vec![];
456    for filter in Filter::into_enum_iter() {
457        let size = get_compressed_size(filter, &tiny_image_data, row_stride, pixel_bytes)?;
458        results.push((filter, size));
459    }
460
461    Ok(results.iter().max_by_key(|it| it.1).unwrap().0)
462}
463
464
465fn validate_color(color: Color) -> ApngResult<()> {
466    use self::Color::*;
467
468    match color {
469        Grayscale(b) if [1, 2, 4, 8, 16].contains(&b) => (),
470        GrayscaleA(b) | RGB(b) | RGBA(b) if [8, 16].contains(&b) => (),
471        _ => return Err(ApngError::InvalidColor),
472    };
473
474    Ok(())
475}