1extern crate png;
7
8#[derive(std::fmt::Debug)]
12pub enum Error {
13 Decoding(::png::DecodingError),
15 Encoding(::png::EncodingError),
17 InvalidBitDepth,
19 InvalidColorType,
21}
22
23impl std::convert::From<::png::DecodingError> for Error {
24 fn from(err: ::png::DecodingError) -> Self {
25 Error::Decoding(err)
26 }
27}
28
29impl std::convert::From<::png::EncodingError> for Error {
30 fn from(err: ::png::EncodingError) -> Self {
31 Error::Encoding(err)
32 }
33}
34
35fn into_raw_parts<T>(v: Vec<T>) -> (*mut T, usize, usize) {
36 let mut m = std::mem::ManuallyDrop::new(v);
37 (m.as_mut_ptr(), m.len(), m.capacity())
38}
39
40pub fn read_rgba8<Reader>(r: Reader) -> Result<crate::rgba::Image<u8>, Error>
48where
49 Reader: std::io::Read,
50{
51 let (output_info, mut png_reader) = ::png::Decoder::new(r).read_info()?;
52
53 let ::png::OutputInfo {
54 height,
55 width,
56 color_type,
57 bit_depth,
58 ..
59 } = output_info;
60
61 if color_type != ::png::ColorType::RGBA {
62 return Err(Error::InvalidColorType);
63 }
64
65 if bit_depth != ::png::BitDepth::Eight {
66 return Err(Error::InvalidBitDepth);
67 }
68
69 let height = height as usize;
70 let width = width as usize;
71 let size = height * width;
72
73 let mut r = minivec::MiniVec::<u8>::with_capacity(size);
74 let mut g = minivec::MiniVec::<u8>::with_capacity(size);
75 let mut b = minivec::MiniVec::<u8>::with_capacity(size);
76 let mut a = minivec::MiniVec::<u8>::with_capacity(size);
77
78 let mut row_idx = 0;
79
80 let num_channels = 4;
81 let num_cols = width;
82 while let Some(row) = png_reader.next_row()? {
83 let idx = row_idx * num_cols;
84 let end_idx = idx + num_cols;
85
86 row
87 .chunks_exact(num_channels)
88 .zip(r.spare_capacity_mut()[idx..end_idx].iter_mut())
89 .zip(g.spare_capacity_mut()[idx..end_idx].iter_mut())
90 .zip(b.spare_capacity_mut()[idx..end_idx].iter_mut())
91 .zip(a.spare_capacity_mut()[idx..end_idx].iter_mut())
92 .for_each(|((((chunk, r), g), b), a)| {
93 *r = std::mem::MaybeUninit::new(chunk[0]);
94 *g = std::mem::MaybeUninit::new(chunk[1]);
95 *b = std::mem::MaybeUninit::new(chunk[2]);
96 *a = std::mem::MaybeUninit::new(chunk[3]);
97 });
98
99 row_idx += 1;
100 }
101
102 unsafe {
103 r.set_len(size);
104 g.set_len(size);
105 b.set_len(size);
106 a.set_len(size);
107 }
108
109 Ok(crate::rgba::Image {
110 r,
111 g,
112 b,
113 a,
114 h: height,
115 w: width,
116 })
117}
118
119pub fn read_rgb8<Reader>(r: Reader) -> Result<crate::rgb::Image<u8>, Error>
127where
128 Reader: std::io::Read,
129{
130 let (output_info, mut png_reader) = ::png::Decoder::new(r).read_info()?;
131
132 let ::png::OutputInfo {
133 height,
134 width,
135 color_type,
136 bit_depth,
137 ..
138 } = output_info;
139
140 if color_type != ::png::ColorType::RGBA && color_type != ::png::ColorType::RGB {
141 return Err(Error::InvalidColorType);
142 }
143
144 if bit_depth != ::png::BitDepth::Eight {
145 return Err(Error::InvalidBitDepth);
146 }
147
148 let height = height as usize;
149 let width = width as usize;
150 let size = height * width;
151
152 let mut r = minivec::MiniVec::<u8>::with_capacity(size);
153 let mut g = minivec::MiniVec::<u8>::with_capacity(size);
154 let mut b = minivec::MiniVec::<u8>::with_capacity(size);
155
156 let mut row_idx = 0;
157
158 let num_channels = if color_type == ::png::ColorType::RGBA {
159 4
160 } else {
161 3
162 };
163
164 let num_cols = width;
165 while let Some(row) = png_reader.next_row()? {
166 let idx = row_idx * num_cols;
167 let end_idx = idx + num_cols;
168
169 row
170 .chunks_exact(num_channels)
171 .zip(r.spare_capacity_mut()[idx..end_idx].iter_mut())
172 .zip(g.spare_capacity_mut()[idx..end_idx].iter_mut())
173 .zip(b.spare_capacity_mut()[idx..end_idx].iter_mut())
174 .for_each(|(((chunk, r), g), b)| {
175 *r = std::mem::MaybeUninit::new(chunk[0]);
176 *g = std::mem::MaybeUninit::new(chunk[1]);
177 *b = std::mem::MaybeUninit::new(chunk[2]);
178 });
179
180 row_idx += 1;
181 }
182
183 unsafe {
184 r.set_len(size);
185 g.set_len(size);
186 b.set_len(size);
187 }
188
189 Ok(crate::rgb::Image {
190 r,
191 g,
192 b,
193 h: height,
194 w: width,
195 })
196}
197
198#[allow(clippy::cast_possible_truncation)]
206pub fn write_rgba8<Writer, Iter>(
207 writer: Writer,
208 img: Iter,
209 width: usize,
210 height: usize,
211) -> Result<(), Error>
212where
213 Writer: std::io::Write,
214 Iter: std::iter::Iterator<Item = [u8; 4]>,
215{
216 let mut png_encoder = ::png::Encoder::new(writer, width as u32, height as u32);
217 png_encoder.set_color(::png::ColorType::RGBA);
218 png_encoder.set_depth(::png::BitDepth::Eight);
219 let mut png_writer = png_encoder.write_header()?;
220
221 let num_channels = 4;
222 let count = num_channels * width * height;
223
224 let mut buf = vec![std::mem::MaybeUninit::<u8>::uninit(); count];
225 buf
226 .chunks_exact_mut(num_channels)
227 .zip(img)
228 .for_each(|(chunk, [r, g, b, a])| {
229 chunk[0] = std::mem::MaybeUninit::<u8>::new(r);
230 chunk[1] = std::mem::MaybeUninit::<u8>::new(g);
231 chunk[2] = std::mem::MaybeUninit::<u8>::new(b);
232 chunk[3] = std::mem::MaybeUninit::<u8>::new(a);
233 });
234
235 let (ptr, len, cap) = into_raw_parts(buf);
236 let buf = unsafe { Vec::<u8>::from_raw_parts(ptr.cast::<u8>(), len, cap) };
237
238 Ok(png_writer.write_image_data(&buf)?)
239}
240
241#[allow(clippy::cast_possible_truncation)]
249pub fn write_rgb8<Writer, Iter>(
250 writer: Writer,
251 img: Iter,
252 width: usize,
253 height: usize,
254) -> Result<(), Error>
255where
256 Writer: std::io::Write,
257 Iter: std::iter::Iterator<Item = [u8; 3]>,
258{
259 let mut png_encoder = ::png::Encoder::new(writer, width as u32, height as u32);
260 png_encoder.set_color(::png::ColorType::RGB);
261 png_encoder.set_depth(::png::BitDepth::Eight);
262 let mut png_writer = png_encoder.write_header()?;
263
264 let num_channels = 3;
265 let count = num_channels * width * height;
266
267 let mut buf = vec![std::mem::MaybeUninit::<u8>::uninit(); count];
268 buf
269 .chunks_exact_mut(num_channels)
270 .zip(img)
271 .for_each(|(chunk, [r, g, b])| {
272 chunk[0] = std::mem::MaybeUninit::<u8>::new(r);
273 chunk[1] = std::mem::MaybeUninit::<u8>::new(g);
274 chunk[2] = std::mem::MaybeUninit::<u8>::new(b);
275 });
276
277 let (ptr, len, cap) = into_raw_parts(buf);
278 let buf = unsafe { Vec::<u8>::from_raw_parts(ptr.cast::<u8>(), len, cap) };
279
280 Ok(png_writer.write_image_data(&buf)?)
281}
282
283#[allow(clippy::cast_possible_truncation)]
291pub fn write_grayalpha8<Writer, Iter>(
292 writer: Writer,
293 img: Iter,
294 width: usize,
295 height: usize,
296) -> Result<(), Error>
297where
298 Writer: std::io::Write,
299 Iter: std::iter::Iterator<Item = [u8; 2]>,
300{
301 let mut png_encoder = ::png::Encoder::new(writer, width as u32, height as u32);
302 png_encoder.set_color(::png::ColorType::GrayscaleAlpha);
303 png_encoder.set_depth(::png::BitDepth::Eight);
304 let mut png_writer = png_encoder.write_header()?;
305
306 let num_channels = 2;
307 let count = num_channels * width * height;
308
309 let mut buf = vec![std::mem::MaybeUninit::<u8>::uninit(); count];
310 buf
311 .chunks_exact_mut(num_channels)
312 .zip(img)
313 .for_each(|(chunk, [v, a])| {
314 chunk[0] = std::mem::MaybeUninit::<u8>::new(v);
315 chunk[1] = std::mem::MaybeUninit::<u8>::new(a);
316 });
317
318 let (ptr, len, cap) = into_raw_parts(buf);
319 let buf = unsafe { Vec::<u8>::from_raw_parts(ptr.cast::<u8>(), len, cap) };
320
321 Ok(png_writer.write_image_data(&buf)?)
322}