1extern crate deflate;
2
3use std::borrow::Cow;
4use std::error;
5use std::fmt;
6use std::io::{self, Write};
7use std::mem;
8use std::result;
9
10use chunk;
11use crc::Crc32;
12use common::{Info, ColorType, BitDepth, Compression};
13use filter::{FilterType, filter};
14use traits::{WriteBytesExt, HasParameters, Parameter};
15
16pub type Result<T> = result::Result<T, EncodingError>;
17
18#[derive(Debug)]
19pub enum EncodingError {
20 IoError(io::Error),
21 Format(Cow<'static, str>),
22}
23
24impl error::Error for EncodingError {
25 fn description(&self) -> &str {
26 use self::EncodingError::*;
27 match *self {
28 IoError(ref err) => err.description(),
29 Format(ref desc) => &desc,
30 }
31 }
32}
33
34impl fmt::Display for EncodingError {
35 fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
36 write!(fmt, "{}", (self as &error::Error).description())
37 }
38}
39
40impl From<io::Error> for EncodingError {
41 fn from(err: io::Error) -> EncodingError {
42 EncodingError::IoError(err)
43 }
44}
45impl From<EncodingError> for io::Error {
46 fn from(err: EncodingError) -> io::Error {
47 io::Error::new(io::ErrorKind::Other, (&err as &error::Error).description())
48 }
49}
50
51pub struct Encoder<W: Write> {
53 w: W,
54 info: Info,
55}
56
57impl<W: Write> Encoder<W> {
58 pub fn new(w: W, width: u32, height: u32) -> Encoder<W> {
59 let mut info = Info::default();
60 info.width = width;
61 info.height = height;
62 Encoder { w: w, info: info }
63 }
64
65 pub fn write_header(self) -> Result<Writer<W>> {
66 Writer::new(self.w, self.info).init()
67 }
68}
69
70impl<W: Write> HasParameters for Encoder<W> {}
71
72impl<W: Write> Parameter<Encoder<W>> for ColorType {
73 fn set_param(self, this: &mut Encoder<W>) {
74 this.info.color_type = self
75 }
76}
77
78impl<W: Write> Parameter<Encoder<W>> for BitDepth {
79 fn set_param(self, this: &mut Encoder<W>) {
80 this.info.bit_depth = self
81 }
82}
83
84impl<W: Write, C: Into<Compression>> Parameter<Encoder<W>> for C {
88 fn set_param(self, this: &mut Encoder<W>) {
89 this.info.compression = self.into()
90 }
91}
92
93impl <W: Write> Parameter<Encoder<W>> for FilterType {
94 fn set_param(self, this: &mut Encoder<W>) {
95 this.info.filter = self
96 }
97}
98
99pub struct Writer<W: Write> {
101 w: W,
102 info: Info,
103}
104
105impl<W: Write> Writer<W> {
106 fn new(w: W, info: Info) -> Writer<W> {
107 let w = Writer { w: w, info: info };
108 w
109 }
110
111 fn init(mut self) -> Result<Self> {
112 try!(self.w.write_all(&[137, 80, 78, 71, 13, 10, 26, 10]));
113 let mut data = [0; 13];
114 try!((&mut data[..]).write_be(self.info.width));
115 try!((&mut data[4..]).write_be(self.info.height));
116 data[8] = self.info.bit_depth as u8;
117 data[9] = self.info.color_type as u8;
118 data[12] = if self.info.interlaced { 1 } else { 0 };
119 try!(self.write_chunk(chunk::IHDR, &data));
120 Ok(self)
121 }
122
123 pub fn write_chunk(&mut self, name: [u8; 4], data: &[u8]) -> Result<()> {
124 try!(self.w.write_be(data.len() as u32));
125 try!(self.w.write_all(&name));
126 try!(self.w.write_all(data));
127 let mut crc = Crc32::new();
128 crc.update(&name);
129 crc.update(data);
130 try!(self.w.write_be(crc.checksum()));
131 Ok(())
132 }
133
134 pub fn write_image_data(&mut self, data: &[u8]) -> Result<()> {
136 let bpp = self.info.bytes_per_pixel();
137 let in_len = self.info.raw_row_length() - 1;
138 let mut prev = vec![0; in_len];
139 let mut current = vec![0; in_len];
140 let data_size = in_len * self.info.height as usize;
141 if data_size != data.len() {
142 let message = format!("wrong data size, expected {} got {}", data_size, data.len());
143 return Err(EncodingError::Format(message.into()));
144 }
145 let mut zlib = deflate::write::ZlibEncoder::new(Vec::new(), self.info.compression.clone());
146 let filter_method = self.info.filter;
147 for line in data.chunks(in_len) {
148 current.copy_from_slice(&line);
149 try!(zlib.write_all(&[filter_method as u8]));
150 filter(filter_method, bpp, &prev, &mut current);
151 try!(zlib.write_all(¤t));
152 mem::swap(&mut prev, &mut current);
153 }
154 self.write_chunk(chunk::IDAT, &try!(zlib.finish()))
155 }
156}
157
158impl<W: Write> Drop for Writer<W> {
159 fn drop(&mut self) {
160 let _ = self.write_chunk(chunk::IEND, &[]);
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 extern crate rand;
169 extern crate glob;
170
171 use self::rand::Rng;
172 use std::{io, cmp};
173 use std::io::Write;
174 use std::fs::File;
175
176 #[test]
177 fn roundtrip() {
178 for _ in 0..10 {
180 for path in glob::glob("tests/pngsuite/*.png").unwrap().map(|r| r.unwrap()) {
181 if path.file_name().unwrap().to_str().unwrap().starts_with("x") {
182 continue;
184 }
185 let decoder = ::Decoder::new(File::open(path).unwrap());
187 let (info, mut reader) = decoder.read_info().unwrap();
188 if info.line_size != 32 {
189 continue;
191 }
192 let mut buf = vec![0; info.buffer_size()];
193 reader.next_frame(&mut buf).unwrap();
194 let mut out = Vec::new();
196 {
197 let mut wrapper = RandomChunkWriter {
198 rng: self::rand::thread_rng(),
199 w: &mut out
200 };
201
202 let mut encoder = Encoder::new(&mut wrapper, info.width, info.height).write_header().unwrap();
203 encoder.write_image_data(&buf).unwrap();
204 }
205 let decoder = ::Decoder::new(&*out);
207 let (info, mut reader) = decoder.read_info().unwrap();
208 let mut buf2 = vec![0; info.buffer_size()];
209 reader.next_frame(&mut buf2).unwrap();
210 assert_eq!(buf, buf2);
212 }
213 }
214 }
215
216 #[test]
217 fn expect_error_on_wrong_image_len() -> Result<()> {
218 use std::io::Cursor;
219
220 let width = 10;
221 let height = 10;
222
223 let output = vec![0u8; 1024];
224 let writer = Cursor::new(output);
225 let mut encoder = Encoder::new(writer, width as u32, height as u32);
226 encoder.set(BitDepth::Eight);
227 encoder.set(ColorType::RGB);
228 let mut png_writer = encoder.write_header()?;
229
230 let correct_image_size = width * height * 3;
231 let image = vec![0u8; correct_image_size + 1];
232 let result = png_writer.write_image_data(image.as_ref());
233 assert!(result.is_err());
234
235 Ok(())
236 }
237
238 struct RandomChunkWriter<'a, R: Rng, W: Write + 'a> {
240 rng: R,
241 w: &'a mut W
242 }
243
244 impl<'a, R: Rng, W: Write + 'a> Write for RandomChunkWriter<'a, R, W> {
245 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
246 let len = cmp::min(self.rng.gen_range(1, 50), buf.len());
248
249 self.w.write(&buf[0..len])
250 }
251
252 fn flush(&mut self) -> io::Result<()> {
253 self.w.flush()
254 }
255 }
256
257}