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
15pub 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 self.writer.write_u32::<BigEndian>(chunk_data.len() as u32)?;
235 self.writer.write_all(&chunk_type)?;
237 self.writer.write_all(chunk_data)?;
239 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 let color_type = match self.meta.color {
275 Grayscale(_) => 0b000,
276 GrayscaleA(_) => 0b100,
277 RGB(_) => 0b010,
278 RGBA(_) => 0b110,
279 };
280 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}