1 use borrow::Cow;
2 use io::{Read, Write};
3 use ops::{Deref, DerefMut};
4 use std::{borrow, error, fmt, io, mem, ops, result};
5
6 use crc32fast::Hasher as Crc32;
7 use deflate::write::ZlibEncoder;
8
9 use crate::chunk::{self, ChunkType};
10 use crate::common::{
11 AnimationControl, BitDepth, BlendOp, BytesPerPixel, ColorType, Compression, DisposeOp,
12 FrameControl, Info, ParameterError, ParameterErrorKind, ScaledFloat,
13 };
14 use crate::filter::{filter, AdaptiveFilterType, FilterType};
15 use crate::traits::WriteBytesExt;
16
17 pub type Result<T> = result::Result<T, EncodingError>;
18
19 #[derive(Debug)]
20 pub enum EncodingError {
21 IoError(io::Error),
22 Format(FormatError),
23 Parameter(ParameterError),
24 LimitsExceeded,
25 }
26
27 #[derive(Debug)]
28 pub struct FormatError {
29 inner: FormatErrorKind,
30 }
31
32 #[derive(Debug)]
33 enum FormatErrorKind {
34 ZeroWidth,
35 ZeroHeight,
36 InvalidColorCombination(BitDepth, ColorType),
37 NoPalette,
38 // TODO: wait, what?
39 WrittenTooMuch(usize),
40 NotAnimated,
41 OutOfBounds,
42 EndReached,
43 ZeroFrames,
44 MissingFrames,
45 MissingData(usize),
46 Unrecoverable,
47 }
48
49 impl error::Error for EncodingError {
cause(&self) -> Option<&(dyn error::Error + 'static)>50 fn cause(&self) -> Option<&(dyn error::Error + 'static)> {
51 match self {
52 EncodingError::IoError(err) => Some(err),
53 _ => None,
54 }
55 }
56 }
57
58 impl fmt::Display for EncodingError {
fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error>59 fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
60 use self::EncodingError::*;
61 match self {
62 IoError(err) => write!(fmt, "{}", err),
63 Format(desc) => write!(fmt, "{}", desc),
64 Parameter(desc) => write!(fmt, "{}", desc),
65 LimitsExceeded => write!(fmt, "Limits are exceeded."),
66 }
67 }
68 }
69
70 impl fmt::Display for FormatError {
fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error>71 fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
72 use FormatErrorKind::*;
73 match self.inner {
74 ZeroWidth => write!(fmt, "Zero width not allowed"),
75 ZeroHeight => write!(fmt, "Zero height not allowed"),
76 ZeroFrames => write!(fmt, "Zero frames not allowed"),
77 InvalidColorCombination(depth, color) => write!(
78 fmt,
79 "Invalid combination of bit-depth '{:?}' and color-type '{:?}'",
80 depth, color
81 ),
82 NoPalette => write!(fmt, "can't write indexed image without palette"),
83 WrittenTooMuch(index) => write!(fmt, "wrong data size, got {} bytes too many", index),
84 NotAnimated => write!(fmt, "not an animation"),
85 OutOfBounds => write!(
86 fmt,
87 "the dimension and position go over the frame boundaries"
88 ),
89 EndReached => write!(fmt, "all the frames have been already written"),
90 MissingFrames => write!(fmt, "there are still frames to be written"),
91 MissingData(n) => write!(fmt, "there are still {} bytes to be written", n),
92 Unrecoverable => write!(
93 fmt,
94 "a previous error put the writer into an unrecoverable state"
95 ),
96 }
97 }
98 }
99
100 impl From<io::Error> for EncodingError {
from(err: io::Error) -> EncodingError101 fn from(err: io::Error) -> EncodingError {
102 EncodingError::IoError(err)
103 }
104 }
105
106 impl From<EncodingError> for io::Error {
from(err: EncodingError) -> io::Error107 fn from(err: EncodingError) -> io::Error {
108 io::Error::new(io::ErrorKind::Other, err.to_string())
109 }
110 }
111
112 // Private impl.
113 impl From<FormatErrorKind> for FormatError {
from(kind: FormatErrorKind) -> Self114 fn from(kind: FormatErrorKind) -> Self {
115 FormatError { inner: kind }
116 }
117 }
118
119 /// PNG Encoder
120 pub struct Encoder<'a, W: Write> {
121 w: W,
122 info: Info<'a>,
123 filter: FilterType,
124 adaptive_filter: AdaptiveFilterType,
125 sep_def_img: bool,
126 }
127
128 impl<'a, W: Write> Encoder<'a, W> {
new(w: W, width: u32, height: u32) -> Encoder<'static, W>129 pub fn new(w: W, width: u32, height: u32) -> Encoder<'static, W> {
130 Encoder {
131 w,
132 info: Info::with_size(width, height),
133 filter: FilterType::default(),
134 adaptive_filter: AdaptiveFilterType::default(),
135 sep_def_img: false,
136 }
137 }
138
139 /// Specify that the image is animated.
140 ///
141 /// `num_frames` controls how many frames the animation has, while
142 /// `num_plays` controls how many times the animation should be
143 /// repeaded until it stops, if it's zero then it will repeat
144 /// inifinitely
145 ///
146 /// This method returns an error if `num_frames` is 0.
set_animated(&mut self, num_frames: u32, num_plays: u32) -> Result<()>147 pub fn set_animated(&mut self, num_frames: u32, num_plays: u32) -> Result<()> {
148 if num_frames == 0 {
149 return Err(EncodingError::Format(FormatErrorKind::ZeroFrames.into()));
150 }
151 let actl = AnimationControl {
152 num_frames,
153 num_plays,
154 };
155 let fctl = FrameControl {
156 sequence_number: 0,
157 width: self.info.width,
158 height: self.info.height,
159 ..Default::default()
160 };
161 self.info.animation_control = Some(actl);
162 self.info.frame_control = Some(fctl);
163 Ok(())
164 }
165
set_sep_def_img(&mut self, sep_def_img: bool) -> Result<()>166 pub fn set_sep_def_img(&mut self, sep_def_img: bool) -> Result<()> {
167 if self.info.animation_control.is_none() {
168 self.sep_def_img = sep_def_img;
169 Ok(())
170 } else {
171 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
172 }
173 }
174
175 /// Sets the raw byte contents of the PLTE chunk. This method accepts
176 /// both borrowed and owned byte data.
set_palette<T: Into<Cow<'a, [u8]>>>(&mut self, palette: T)177 pub fn set_palette<T: Into<Cow<'a, [u8]>>>(&mut self, palette: T) {
178 self.info.palette = Some(palette.into());
179 }
180
181 /// Sets the raw byte contents of the tRNS chunk. This method accepts
182 /// both borrowed and owned byte data.
set_trns<T: Into<Cow<'a, [u8]>>>(&mut self, trns: T)183 pub fn set_trns<T: Into<Cow<'a, [u8]>>>(&mut self, trns: T) {
184 self.info.trns = Some(trns.into());
185 }
186
187 /// Set the display gamma of the source system on which the image was generated or last edited.
set_source_gamma(&mut self, source_gamma: ScaledFloat)188 pub fn set_source_gamma(&mut self, source_gamma: ScaledFloat) {
189 self.info.source_gamma = Some(source_gamma);
190 }
191
192 /// Set the chromaticities for the source system's display channels (red, green, blue) and the whitepoint
193 /// of the source system on which the image was generated or last edited.
set_source_chromaticities( &mut self, source_chromaticities: super::SourceChromaticities, )194 pub fn set_source_chromaticities(
195 &mut self,
196 source_chromaticities: super::SourceChromaticities,
197 ) {
198 self.info.source_chromaticities = Some(source_chromaticities);
199 }
200
201 /// Mark the image data as conforming to the SRGB color space with the specified rendering intent.
202 ///
203 /// Matching source gamma and chromaticities chunks are added automatically.
204 /// Any manually specified source gamma or chromaticities will be ignored.
set_srgb(&mut self, rendering_intent: super::SrgbRenderingIntent)205 pub fn set_srgb(&mut self, rendering_intent: super::SrgbRenderingIntent) {
206 self.info.srgb = Some(rendering_intent);
207 }
208
write_header(self) -> Result<Writer<W>>209 pub fn write_header(self) -> Result<Writer<W>> {
210 Writer::new(
211 self.w,
212 PartialInfo::new(&self.info),
213 self.filter,
214 self.adaptive_filter,
215 self.sep_def_img,
216 )
217 .init(&self.info)
218 }
219
220 /// Set the color of the encoded image.
221 ///
222 /// These correspond to the color types in the png IHDR data that will be written. The length
223 /// of the image data that is later supplied must match the color type, otherwise an error will
224 /// be emitted.
set_color(&mut self, color: ColorType)225 pub fn set_color(&mut self, color: ColorType) {
226 self.info.color_type = color;
227 }
228
229 /// Set the indicated depth of the image data.
set_depth(&mut self, depth: BitDepth)230 pub fn set_depth(&mut self, depth: BitDepth) {
231 self.info.bit_depth = depth;
232 }
233
234 /// Set compression parameters.
235 ///
236 /// Accepts a `Compression` or any type that can transform into a `Compression`. Notably `deflate::Compression` and
237 /// `deflate::CompressionOptions` which "just work".
set_compression(&mut self, compression: Compression)238 pub fn set_compression(&mut self, compression: Compression) {
239 self.info.compression = compression;
240 }
241
242 /// Set the used filter type.
243 ///
244 /// The default filter is [`FilterType::Sub`] which provides a basic prediction algorithm for
245 /// sample values based on the previous. For a potentially better compression ratio, at the
246 /// cost of more complex processing, try out [`FilterType::Paeth`].
247 ///
248 /// [`FilterType::Sub`]: enum.FilterType.html#variant.Sub
249 /// [`FilterType::Paeth`]: enum.FilterType.html#variant.Paeth
set_filter(&mut self, filter: FilterType)250 pub fn set_filter(&mut self, filter: FilterType) {
251 self.filter = filter;
252 }
253
254 /// Set the adaptive filter type.
255 ///
256 /// Adaptive filtering attempts to select the best filter for each line
257 /// based on heuristics which minimize the file size for compression rather
258 /// than use a single filter for the entire image. The default method is
259 /// [`AdaptiveFilterType::NonAdaptive`].
260 ///
261 /// [`AdaptiveFilterType::NonAdaptive`]: enum.AdaptiveFilterType.html
set_adaptive_filter(&mut self, adaptive_filter: AdaptiveFilterType)262 pub fn set_adaptive_filter(&mut self, adaptive_filter: AdaptiveFilterType) {
263 self.adaptive_filter = adaptive_filter;
264 }
265
266 /// Set the fraction of time every frame is going to be displayed, in seconds.
267 ///
268 /// *Note that this parameter can be set for each individual frame after
269 /// [`write_header`] is called. (see [`Writer::set_frame_delay`])*
270 ///
271 /// If the denominator is 0, it is to be treated as if it were 100
272 /// (that is, the numerator then specifies 1/100ths of a second).
273 /// If the the value of the numerator is 0 the decoder should render the next frame
274 /// as quickly as possible, though viewers may impose a reasonable lower bound.
275 ///
276 /// The default value is 0 for both the numerator and denominator.
277 ///
278 /// This method will return an error if the image is not animated.
279 /// (see [`set_animated`])
280 ///
281 /// [`write_header`]: struct.Encoder.html#method.write_header
282 /// [`set_animated`]: struct.Encoder.html#method.set_animated
283 /// [`Writer::set_frame_delay`]: struct.Writer#method.set_frame_delay
set_frame_delay(&mut self, numerator: u16, denominator: u16) -> Result<()>284 pub fn set_frame_delay(&mut self, numerator: u16, denominator: u16) -> Result<()> {
285 if let Some(ref mut fctl) = self.info.frame_control {
286 fctl.delay_den = denominator;
287 fctl.delay_num = numerator;
288 Ok(())
289 } else {
290 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
291 }
292 }
293
294 /// Set the blend operation for every frame.
295 ///
296 /// The blend operation specifies whether the frame is to be alpha blended
297 /// into the current output buffer content, or whether it should completely
298 /// replace its region in the output buffer.
299 ///
300 /// *Note that this parameter can be set for each individual frame after
301 /// [`writer_header`] is called. (see [`Writer::set_blend_op`])*
302 ///
303 /// See the [`BlendOp`] documentaion for the possible values and their effects.
304 ///
305 /// *Note that for the first frame the two blend modes are functionally
306 /// equivalent due to the clearing of the output buffer at the beginning
307 /// of each play.*
308 ///
309 /// The default value is [`BlendOp::Source`].
310 ///
311 /// This method will return an error if the image is not animated.
312 /// (see [`set_animated`])
313 ///
314 /// [`BlendOP`]: enum.BlendOp.html
315 /// [`BlendOP::Source`]: enum.BlendOp.html#variant.Source
316 /// [`write_header`]: struct.Encoder.html#method.write_header
317 /// [`set_animated`]: struct.Encoder.html#method.set_animated
318 /// [`Writer::set_blend_op`]: struct.Writer#method.set_blend_op
set_blend_op(&mut self, op: BlendOp) -> Result<()>319 pub fn set_blend_op(&mut self, op: BlendOp) -> Result<()> {
320 if let Some(ref mut fctl) = self.info.frame_control {
321 fctl.blend_op = op;
322 Ok(())
323 } else {
324 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
325 }
326 }
327
328 /// Set the dispose operation for every frame.
329 ///
330 /// The dispose operation specifies how the output buffer should be changed
331 /// at the end of the delay (before rendering the next frame)
332 ///
333 /// *Note that this parameter can be set for each individual frame after
334 /// [`writer_header`] is called (see [`Writer::set_dispose_op`])*
335 ///
336 /// See the [`DisposeOp`] documentaion for the possible values and their effects.
337 ///
338 /// *Note that if the first frame uses [`DisposeOp::Previous`]
339 /// it will be treated as [`DisposeOp::Background`].*
340 ///
341 /// The default value is [`DisposeOp::None`].
342 ///
343 /// This method will return an error if the image is not animated.
344 /// (see [`set_animated`])
345 ///
346 /// [`DisposeOp`]: ../common/enum.BlendOp.html
347 /// [`DisposeOp::Previous`]: ../common/enum.BlendOp.html#variant.Previous
348 /// [`DisposeOp::Background`]: ../common/enum.BlendOp.html#variant.Background
349 /// [`DisposeOp::None`]: ../common/enum.BlendOp.html#variant.None
350 /// [`write_header`]: struct.Encoder.html#method.write_header
351 /// [`set_animated`]: struct.Encoder.html#method.set_animated
352 /// [`Writer::set_dispose_op`]: struct.Writer#method.set_dispose_op
set_dispose_op(&mut self, op: DisposeOp) -> Result<()>353 pub fn set_dispose_op(&mut self, op: DisposeOp) -> Result<()> {
354 if let Some(ref mut fctl) = self.info.frame_control {
355 fctl.dispose_op = op;
356 Ok(())
357 } else {
358 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
359 }
360 }
361 }
362
363 /// PNG writer
364 pub struct Writer<W: Write> {
365 w: W,
366 info: PartialInfo,
367 filter: FilterType,
368 adaptive_filter: AdaptiveFilterType,
369 sep_def_img: bool,
370 written: u64,
371 }
372
373 /// Contains the subset of attributes of [Info] needed for [Writer] to function
374 struct PartialInfo {
375 width: u32,
376 height: u32,
377 bit_depth: BitDepth,
378 color_type: ColorType,
379 frame_control: Option<FrameControl>,
380 animation_control: Option<AnimationControl>,
381 compression: Compression,
382 has_palette: bool,
383 }
384
385 impl PartialInfo {
new(info: &Info) -> Self386 fn new(info: &Info) -> Self {
387 PartialInfo {
388 width: info.width,
389 height: info.height,
390 bit_depth: info.bit_depth,
391 color_type: info.color_type,
392 frame_control: info.frame_control,
393 animation_control: info.animation_control,
394 compression: info.compression,
395 has_palette: info.palette.is_some(),
396 }
397 }
398
bpp_in_prediction(&self) -> BytesPerPixel399 fn bpp_in_prediction(&self) -> BytesPerPixel {
400 // Passthrough
401 self.to_info().bpp_in_prediction()
402 }
403
raw_row_length(&self) -> usize404 fn raw_row_length(&self) -> usize {
405 // Passthrough
406 self.to_info().raw_row_length()
407 }
408
raw_row_length_from_width(&self, width: u32) -> usize409 fn raw_row_length_from_width(&self, width: u32) -> usize {
410 // Passthrough
411 self.to_info().raw_row_length_from_width(width)
412 }
413
414 /// Converts this partial info to an owned Info struct,
415 /// setting missing values to their defaults
to_info(&self) -> Info<'static>416 fn to_info(&self) -> Info<'static> {
417 let mut info = Info::default();
418 info.width = self.width;
419 info.height = self.height;
420 info.bit_depth = self.bit_depth;
421 info.color_type = self.color_type;
422 info.frame_control = self.frame_control;
423 info.animation_control = self.animation_control;
424 info.compression = self.compression;
425 info
426 }
427 }
428
429 const DEFAULT_BUFFER_LENGTH: usize = 4 * 1024;
430
write_chunk<W: Write>(mut w: W, name: chunk::ChunkType, data: &[u8]) -> Result<()>431 pub(crate) fn write_chunk<W: Write>(mut w: W, name: chunk::ChunkType, data: &[u8]) -> Result<()> {
432 w.write_be(data.len() as u32)?;
433 w.write_all(&name.0)?;
434 w.write_all(data)?;
435 let mut crc = Crc32::new();
436 crc.update(&name.0);
437 crc.update(data);
438 w.write_be(crc.finalize())?;
439 Ok(())
440 }
441
442 impl<W: Write> Writer<W> {
new( w: W, info: PartialInfo, filter: FilterType, adaptive_filter: AdaptiveFilterType, sep_def_img: bool, ) -> Writer<W>443 fn new(
444 w: W,
445 info: PartialInfo,
446 filter: FilterType,
447 adaptive_filter: AdaptiveFilterType,
448 sep_def_img: bool,
449 ) -> Writer<W> {
450 Writer {
451 w,
452 info,
453 filter,
454 adaptive_filter,
455 sep_def_img,
456 written: 0,
457 }
458 }
459
init(mut self, info: &Info<'_>) -> Result<Self>460 fn init(mut self, info: &Info<'_>) -> Result<Self> {
461 if self.info.width == 0 {
462 return Err(EncodingError::Format(FormatErrorKind::ZeroWidth.into()));
463 }
464
465 if self.info.height == 0 {
466 return Err(EncodingError::Format(FormatErrorKind::ZeroHeight.into()));
467 }
468
469 if self
470 .info
471 .color_type
472 .is_combination_invalid(self.info.bit_depth)
473 {
474 return Err(EncodingError::Format(
475 FormatErrorKind::InvalidColorCombination(self.info.bit_depth, self.info.color_type)
476 .into(),
477 ));
478 }
479
480 self.w.write_all(&[137, 80, 78, 71, 13, 10, 26, 10])?; // PNG signature
481 info.encode(&mut self.w)?;
482
483 Ok(self)
484 }
485
write_chunk(&mut self, name: ChunkType, data: &[u8]) -> Result<()>486 pub fn write_chunk(&mut self, name: ChunkType, data: &[u8]) -> Result<()> {
487 write_chunk(&mut self.w, name, data)
488 }
489
max_frames(&self) -> u64490 fn max_frames(&self) -> u64 {
491 match self.info.animation_control {
492 Some(a) if self.sep_def_img => a.num_frames as u64 + 1,
493 Some(a) => a.num_frames as u64,
494 None => 1,
495 }
496 }
497
498 /// Writes the image data.
write_image_data(&mut self, data: &[u8]) -> Result<()>499 pub fn write_image_data(&mut self, data: &[u8]) -> Result<()> {
500 const MAX_IDAT_CHUNK_LEN: u32 = std::u32::MAX >> 1;
501 #[allow(non_upper_case_globals)]
502 const MAX_fdAT_CHUNK_LEN: u32 = (std::u32::MAX >> 1) - 4;
503
504 if self.info.color_type == ColorType::Indexed && !self.info.has_palette {
505 return Err(EncodingError::Format(FormatErrorKind::NoPalette.into()));
506 }
507
508 if self.written > self.max_frames() {
509 return Err(EncodingError::Format(FormatErrorKind::EndReached.into()));
510 }
511
512 let width: usize;
513 let height: usize;
514 if let Some(ref mut fctl) = self.info.frame_control {
515 width = fctl.width as usize;
516 height = fctl.height as usize;
517 } else {
518 width = self.info.width as usize;
519 height = self.info.height as usize;
520 }
521
522 let in_len = self.info.raw_row_length_from_width(width as u32) - 1;
523 let data_size = in_len * height;
524 if data_size != data.len() {
525 return Err(EncodingError::Parameter(
526 ParameterErrorKind::ImageBufferSize {
527 expected: data_size,
528 actual: data.len(),
529 }
530 .into(),
531 ));
532 }
533
534 let prev = vec![0; in_len];
535 let mut prev = prev.as_slice();
536 let mut current = vec![0; in_len];
537
538 let mut zlib = deflate::write::ZlibEncoder::new(
539 Vec::new(),
540 self.info.compression.clone().to_options(),
541 );
542 let bpp = self.info.bpp_in_prediction();
543 let filter_method = self.filter;
544 let adaptive_method = self.adaptive_filter;
545 for line in data.chunks(in_len) {
546 current.copy_from_slice(&line);
547 let filter_type = filter(filter_method, adaptive_method, bpp, &prev, &mut current);
548 zlib.write_all(&[filter_type as u8])?;
549 zlib.write_all(¤t)?;
550 prev = line;
551 }
552 let zlib_encoded = zlib.finish()?;
553 if self.sep_def_img || self.info.frame_control.is_none() {
554 self.sep_def_img = false;
555 for chunk in zlib_encoded.chunks(MAX_IDAT_CHUNK_LEN as usize) {
556 self.write_chunk(chunk::IDAT, &chunk)?;
557 }
558 } else if let Some(ref mut fctl) = self.info.frame_control {
559 fctl.encode(&mut self.w)?;
560 fctl.sequence_number = fctl.sequence_number.wrapping_add(1);
561
562 if self.written == 0 {
563 for chunk in zlib_encoded.chunks(MAX_IDAT_CHUNK_LEN as usize) {
564 self.write_chunk(chunk::IDAT, &chunk)?;
565 }
566 } else {
567 let buff_size = zlib_encoded.len().min(MAX_fdAT_CHUNK_LEN as usize);
568 let mut alldata = vec![0u8; 4 + buff_size];
569 for chunk in zlib_encoded.chunks(MAX_fdAT_CHUNK_LEN as usize) {
570 alldata[..4].copy_from_slice(&fctl.sequence_number.to_be_bytes());
571 alldata[4..][..chunk.len()].copy_from_slice(chunk);
572 write_chunk(&mut self.w, chunk::fdAT, &alldata[..4 + chunk.len()])?;
573 fctl.sequence_number = fctl.sequence_number.wrapping_add(1);
574 }
575 }
576 } else {
577 unreachable!(); // this should be unreachable
578 }
579 self.written += 1;
580 Ok(())
581 }
582
583 /// Set the used filter type for the following frames.
584 ///
585 /// The default filter is [`FilterType::Sub`] which provides a basic prediction algorithm for
586 /// sample values based on the previous. For a potentially better compression ratio, at the
587 /// cost of more complex processing, try out [`FilterType::Paeth`].
588 ///
589 /// [`FilterType::Sub`]: enum.FilterType.html#variant.Sub
590 /// [`FilterType::Paeth`]: enum.FilterType.html#variant.Paeth
set_filter(&mut self, filter: FilterType)591 pub fn set_filter(&mut self, filter: FilterType) {
592 self.filter = filter;
593 }
594
595 /// Set the adaptive filter type for the following frames.
596 ///
597 /// Adaptive filtering attempts to select the best filter for each line
598 /// based on heuristics which minimize the file size for compression rather
599 /// than use a single filter for the entire image. The default method is
600 /// [`AdaptiveFilterType::NonAdaptive`].
601 ///
602 /// [`AdaptiveFilterType::NonAdaptive`]: enum.AdaptiveFilterType.html
set_adaptive_filter(&mut self, adaptive_filter: AdaptiveFilterType)603 pub fn set_adaptive_filter(&mut self, adaptive_filter: AdaptiveFilterType) {
604 self.adaptive_filter = adaptive_filter;
605 }
606
607 /// Set the fraction of time the following frames are going to be displayed,
608 /// in seconds
609 ///
610 /// If the denominator is 0, it is to be treated as if it were 100
611 /// (that is, the numerator then specifies 1/100ths of a second).
612 /// If the the value of the numerator is 0 the decoder should render the next frame
613 /// as quickly as possible, though viewers may impose a reasonable lower bound.
614 ///
615 /// This method will return an error if the image is not animated.
set_frame_delay(&mut self, numerator: u16, denominator: u16) -> Result<()>616 pub fn set_frame_delay(&mut self, numerator: u16, denominator: u16) -> Result<()> {
617 if let Some(ref mut fctl) = self.info.frame_control {
618 fctl.delay_den = denominator;
619 fctl.delay_num = numerator;
620 Ok(())
621 } else {
622 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
623 }
624 }
625
626 /// Set the dimension of the following frames.
627 ///
628 /// This function will return an error when:
629 /// - The image is not an animated;
630 ///
631 /// - The selected dimension, considering also the current frame position,
632 /// goes outside the image boudries;
633 ///
634 /// - One or both the width and height are 0;
635 ///
636 // ??? TODO ???
637 // - The next frame is the default image
set_frame_dimension(&mut self, width: u32, height: u32) -> Result<()>638 pub fn set_frame_dimension(&mut self, width: u32, height: u32) -> Result<()> {
639 if let Some(ref mut fctl) = self.info.frame_control {
640 if Some(width) > self.info.width.checked_sub(fctl.x_offset)
641 || Some(height) > self.info.height.checked_sub(fctl.y_offset)
642 {
643 return Err(EncodingError::Format(FormatErrorKind::OutOfBounds.into()));
644 } else if width == 0 {
645 return Err(EncodingError::Format(FormatErrorKind::ZeroWidth.into()));
646 } else if height == 0 {
647 return Err(EncodingError::Format(FormatErrorKind::ZeroHeight.into()));
648 }
649 fctl.width = width;
650 fctl.height = height;
651 Ok(())
652 } else {
653 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
654 }
655 }
656
657 /// Set the position of the following frames.
658 ///
659 /// An error will be returned if:
660 /// - The image is not animated;
661 ///
662 /// - The selected position, considering also the current frame dimension,
663 /// goes outside the image boudries;
664 ///
665 // ??? TODO ???
666 // - The next frame is the default image
set_frame_position(&mut self, x: u32, y: u32) -> Result<()>667 pub fn set_frame_position(&mut self, x: u32, y: u32) -> Result<()> {
668 if let Some(ref mut fctl) = self.info.frame_control {
669 if Some(x) > self.info.width.checked_sub(fctl.width)
670 || Some(y) > self.info.height.checked_sub(fctl.height)
671 {
672 return Err(EncodingError::Format(FormatErrorKind::OutOfBounds.into()));
673 }
674 fctl.x_offset = x;
675 fctl.y_offset = y;
676 Ok(())
677 } else {
678 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
679 }
680 }
681
682 /// Set the frame dimension to occupy all the image, starting from
683 /// the current position.
684 ///
685 /// To reset the frame to the full image size [`reset_frame_position`]
686 /// should be called first.
687 ///
688 /// This method will return an error if the image is not animated.
689 ///
690 /// [`reset_frame_position`]: struct.Writer.html#method.reset_frame_position
reset_frame_dimension(&mut self) -> Result<()>691 pub fn reset_frame_dimension(&mut self) -> Result<()> {
692 if let Some(ref mut fctl) = self.info.frame_control {
693 fctl.width = self.info.width - fctl.x_offset;
694 fctl.height = self.info.height - fctl.y_offset;
695 Ok(())
696 } else {
697 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
698 }
699 }
700
701 /// Set the frame position to (0, 0).
702 ///
703 /// Equivalent to calling [`set_frame_position(0, 0)`].
704 ///
705 /// This method will return an error if the image is not animated.
706 ///
707 /// [`set_frame_position(0, 0)`]: struct.Writer.html#method.set_frame_position
reset_frame_position(&mut self) -> Result<()>708 pub fn reset_frame_position(&mut self) -> Result<()> {
709 if let Some(ref mut fctl) = self.info.frame_control {
710 fctl.x_offset = 0;
711 fctl.y_offset = 0;
712 Ok(())
713 } else {
714 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
715 }
716 }
717
718 /// Set the blend operation for the following frames.
719 ///
720 /// The blend operation specifies whether the frame is to be alpha blended
721 /// into the current output buffer content, or whether it should completely
722 /// replace its region in the output buffer.
723 ///
724 /// See the [`BlendOp`] documentaion for the possible values and their effects.
725 ///
726 /// *Note that for the first frame the two blend modes are functionally
727 /// equivalent due to the clearing of the output buffer at the beginning
728 /// of each play.*
729 ///
730 /// This method will return an error if the image is not animated.
731 ///
732 /// [`BlendOP`]: enum.BlendOp.html
set_blend_op(&mut self, op: BlendOp) -> Result<()>733 pub fn set_blend_op(&mut self, op: BlendOp) -> Result<()> {
734 if let Some(ref mut fctl) = self.info.frame_control {
735 fctl.blend_op = op;
736 Ok(())
737 } else {
738 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
739 }
740 }
741
742 /// Set the dispose operation for the following frames.
743 ///
744 /// The dispose operation specifies how the output buffer should be changed
745 /// at the end of the delay (before rendering the next frame)
746 ///
747 /// See the [`DisposeOp`] documentaion for the possible values and their effects.
748 ///
749 /// *Note that if the first frame uses [`DisposeOp::Previous`]
750 /// it will be treated as [`DisposeOp::Background`].*
751 ///
752 /// This method will return an error if the image is not animated.
753 ///
754 /// [`DisposeOp`]: ../common/enum.BlendOp.html
755 /// [`DisposeOp::Previous`]: ../common/enum.BlendOp.html#variant.Previous
756 /// [`DisposeOp::Background`]: ../common/enum.BlendOp.html#variant.Background
set_dispose_op(&mut self, op: DisposeOp) -> Result<()>757 pub fn set_dispose_op(&mut self, op: DisposeOp) -> Result<()> {
758 if let Some(ref mut fctl) = self.info.frame_control {
759 fctl.dispose_op = op;
760 Ok(())
761 } else {
762 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
763 }
764 }
765
766 /// Create a stream writer.
767 ///
768 /// This allows you to create images that do not fit in memory. The default
769 /// chunk size is 4K, use `stream_writer_with_size` to set another chunk
770 /// size.
771 ///
772 /// This borrows the writer which allows for manually appending additional
773 /// chunks after the image data has been written.
stream_writer(&mut self) -> Result<StreamWriter<W>>774 pub fn stream_writer(&mut self) -> Result<StreamWriter<W>> {
775 self.stream_writer_with_size(DEFAULT_BUFFER_LENGTH)
776 }
777
778 /// Create a stream writer with custom buffer size.
779 ///
780 /// See [`stream_writer`].
781 ///
782 /// [`stream_writer`]: #fn.stream_writer
stream_writer_with_size(&mut self, size: usize) -> Result<StreamWriter<W>>783 pub fn stream_writer_with_size(&mut self, size: usize) -> Result<StreamWriter<W>> {
784 StreamWriter::new(ChunkOutput::Borrowed(self), size)
785 }
786
787 /// Turn this into a stream writer for image data.
788 ///
789 /// This allows you to create images that do not fit in memory. The default
790 /// chunk size is 4K, use `stream_writer_with_size` to set another chunk
791 /// size.
into_stream_writer(self) -> Result<StreamWriter<'static, W>>792 pub fn into_stream_writer(self) -> Result<StreamWriter<'static, W>> {
793 self.into_stream_writer_with_size(DEFAULT_BUFFER_LENGTH)
794 }
795
796 /// Turn this into a stream writer with custom buffer size.
797 ///
798 /// See [`into_stream_writer`].
799 ///
800 /// [`into_stream_writer`]: #fn.into_stream_writer
into_stream_writer_with_size(self, size: usize) -> Result<StreamWriter<'static, W>>801 pub fn into_stream_writer_with_size(self, size: usize) -> Result<StreamWriter<'static, W>> {
802 StreamWriter::new(ChunkOutput::Owned(self), size)
803 }
804 }
805
806 impl<W: Write> Drop for Writer<W> {
drop(&mut self)807 fn drop(&mut self) {
808 let _ = self.write_chunk(chunk::IEND, &[]);
809 }
810 }
811
812 enum ChunkOutput<'a, W: Write> {
813 Borrowed(&'a mut Writer<W>),
814 Owned(Writer<W>),
815 }
816
817 // opted for deref for practical reasons
818 impl<'a, W: Write> Deref for ChunkOutput<'a, W> {
819 type Target = Writer<W>;
820
deref(&self) -> &Self::Target821 fn deref(&self) -> &Self::Target {
822 match self {
823 ChunkOutput::Borrowed(writer) => writer,
824 ChunkOutput::Owned(writer) => writer,
825 }
826 }
827 }
828
829 impl<'a, W: Write> DerefMut for ChunkOutput<'a, W> {
deref_mut(&mut self) -> &mut Self::Target830 fn deref_mut(&mut self) -> &mut Self::Target {
831 match self {
832 ChunkOutput::Borrowed(writer) => writer,
833 ChunkOutput::Owned(writer) => writer,
834 }
835 }
836 }
837
838 /// This writer is used between the actual writer and the
839 /// ZlibEncoder and has the job of packaging the compressed
840 /// data into a PNG chunk, based on the image metadata
841 ///
842 /// Currently the way it works is that the specified buffer
843 /// will hold one chunk at the time and bufferize the incoming
844 /// data until `flush` is called or the maximum chunk size
845 /// is reached.
846 ///
847 /// The maximum chunk is the smallest between the selected buffer size
848 /// and `u32::MAX >> 1` (`0x7fffffff` or `2147483647` dec)
849 ///
850 /// When a chunk has to be flushed the length (that is now known)
851 /// and the CRC will be written at the correct locations in the chunk.
852 struct ChunkWriter<'a, W: Write> {
853 writer: ChunkOutput<'a, W>,
854 buffer: Vec<u8>,
855 /// keeps track of where the last byte was written
856 index: usize,
857 curr_chunk: ChunkType,
858 }
859
860 impl<'a, W: Write> ChunkWriter<'a, W> {
new(writer: ChunkOutput<'a, W>, buf_len: usize) -> ChunkWriter<'a, W>861 fn new(writer: ChunkOutput<'a, W>, buf_len: usize) -> ChunkWriter<'a, W> {
862 // currently buf_len will determine the size of each chunk
863 // the len is capped to the maximum size every chunk can hold
864 // (this wont ever overflow an u32)
865 //
866 // TODO (maybe): find a way to hold two chunks at a time if `usize`
867 // is 64 bits.
868 const CAP: usize = std::u32::MAX as usize >> 1;
869 let curr_chunk;
870 if writer.sep_def_img || writer.info.frame_control.is_none() || writer.written == 0 {
871 curr_chunk = chunk::IDAT;
872 } else {
873 curr_chunk = chunk::fdAT;
874 }
875 ChunkWriter {
876 writer,
877 buffer: vec![0; CAP.min(buf_len)],
878 index: 0,
879 curr_chunk,
880 }
881 }
882
883 /// Returns the size of each scanline for the next frame
884 /// paired with the size of the whole frame
885 ///
886 /// This is used by the `StreamWriter` to know when the scanline ends
887 /// so it can filter compress it and also to know when to start
888 /// the next one
next_frame_info(&self) -> (usize, usize)889 fn next_frame_info(&self) -> (usize, usize) {
890 let wrt = self.writer.deref();
891
892 let width: usize;
893 let height: usize;
894 if let Some(fctl) = wrt.info.frame_control {
895 width = fctl.width as usize;
896 height = fctl.height as usize;
897 } else {
898 width = wrt.info.width as usize;
899 height = wrt.info.height as usize;
900 }
901
902 let in_len = wrt.info.raw_row_length_from_width(width as u32) - 1;
903 let data_size = in_len * height;
904
905 (in_len, data_size)
906 }
907
908 /// NOTE: this bypasses the internal buffer so the flush method should be called before this
909 /// in the case there is some data left in the buffer when this is called, it will panic
write_header(&mut self) -> Result<()>910 fn write_header(&mut self) -> Result<()> {
911 assert_eq!(self.index, 0, "Called when not flushed");
912 let wrt = self.writer.deref_mut();
913
914 self.curr_chunk = match wrt.info.frame_control {
915 _ if wrt.sep_def_img => chunk::IDAT,
916 None => chunk::IDAT,
917 Some(ref mut fctl) => {
918 fctl.encode(&mut wrt.w)?;
919 fctl.sequence_number += 1;
920 match wrt.written {
921 0 => chunk::IDAT,
922 _ => chunk::fdAT,
923 }
924 }
925 };
926 Ok(())
927 }
928
929 /// Set the `FrameControl` for the following frame
930 ///
931 /// It will ignore the `sequence_number` of the parameter
932 /// as it is updated internally.
set_fctl(&mut self, f: FrameControl)933 fn set_fctl(&mut self, f: FrameControl) {
934 if let Some(ref mut fctl) = self.writer.info.frame_control {
935 // ingnore the sequence number
936 *fctl = FrameControl {
937 sequence_number: fctl.sequence_number,
938 ..f
939 };
940 } else {
941 panic!("This function must be called on an animated PNG")
942 }
943 }
944
945 /// Flushes the current chunk
flush_inner(&mut self) -> io::Result<()>946 fn flush_inner(&mut self) -> io::Result<()> {
947 if self.index > 0 {
948 // flush the chunk and reset everything
949 write_chunk(
950 &mut self.writer.w,
951 self.curr_chunk,
952 &self.buffer[..self.index],
953 )?;
954 self.index = 0;
955 }
956 Ok(())
957 }
958 }
959
960 impl<'a, W: Write> Write for ChunkWriter<'a, W> {
write(&mut self, mut data: &[u8]) -> io::Result<usize>961 fn write(&mut self, mut data: &[u8]) -> io::Result<usize> {
962 if data.is_empty() {
963 return Ok(0);
964 }
965
966 // index == 0 means a chunk as been flushed out
967 if self.index == 0 {
968 let wrt = self.writer.deref_mut();
969 // ??? maybe use self.curr_chunk == chunk::fdAT ???
970 if !wrt.sep_def_img && wrt.info.frame_control.is_some() && wrt.written > 0 {
971 let fctl = wrt.info.frame_control.as_mut().unwrap();
972 self.buffer[0..4].copy_from_slice(&fctl.sequence_number.to_be_bytes());
973 fctl.sequence_number += 1;
974 self.index = 4;
975 }
976 }
977
978 // cap the buffer length to the maximum nuber of bytes that can't still
979 // be added to the current chunk
980 let written = data.len().min(self.buffer.len() - self.index);
981 data = &data[..written];
982
983 self.buffer[self.index..][..written].copy_from_slice(data);
984 self.index += written;
985
986 // if the maximum data for this chunk as been reached it needs to be flushed
987 if self.index == self.buffer.len() {
988 self.flush_inner()?;
989 }
990 Ok(written)
991 }
992
flush(&mut self) -> io::Result<()>993 fn flush(&mut self) -> io::Result<()> {
994 self.flush_inner()
995 }
996 }
997
998 impl<W: Write> Drop for ChunkWriter<'_, W> {
drop(&mut self)999 fn drop(&mut self) {
1000 let _ = self.flush();
1001 }
1002 }
1003
1004 // TODO: find a better name
1005 //
1006 /// This enum is used to be allow the `StreamWriter` to keep
1007 /// its inner `ChunkWriter` without wrapping it inside a
1008 /// `ZlibEncoder`. This is used in the case that between the
1009 /// change of state that happens when the last write of a frame
1010 /// is performed an error occurs, which obviously has to be returned.
1011 /// This creates the problem of where to store the writer before
1012 /// exiting the function, and this is where `Wrapper` comes in.
1013 ///
1014 /// Unfortunately the `ZlibWriter` can't be used because on the
1015 /// write following the error, `finish` wuold be called and that
1016 /// would write some data even if 0 bytes where compressed.
1017 ///
1018 /// If the `finish` function fails then there is nothing much to
1019 /// do as the `ChunkWriter` would get lost so the `Unrecoverable`
1020 /// variant is used to signal that.
1021 enum Wrapper<'a, W: Write> {
1022 Chunk(ChunkWriter<'a, W>),
1023 Zlib(ZlibEncoder<ChunkWriter<'a, W>>),
1024 Unrecoverable,
1025 /// This is used in-between, should never be matched
1026 None,
1027 }
1028
1029 impl<'a, W: Write> Wrapper<'a, W> {
1030 /// Like `Option::take` this returns the `Wrapper` contained
1031 /// in `self` and replaces it with `Wrapper::None`
take(&mut self) -> Wrapper<'a, W>1032 fn take(&mut self) -> Wrapper<'a, W> {
1033 let mut swap = Wrapper::None;
1034 mem::swap(self, &mut swap);
1035 swap
1036 }
1037 }
1038
1039 /// Streaming PNG writer
1040 ///
1041 /// This may silently fail in the destructor, so it is a good idea to call
1042 /// [`finish`](#method.finish) or [`flush`] before dropping.
1043 ///
1044 /// [`flush`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html#tymethod.flush
1045 pub struct StreamWriter<'a, W: Write> {
1046 /// The option here is needed in order to access the inner `ChunkWriter` in-between
1047 /// each frame, which is needed for writing the fcTL chunks between each frame
1048 writer: Wrapper<'a, W>,
1049 prev_buf: Vec<u8>,
1050 curr_buf: Vec<u8>,
1051 /// Amount of data already written
1052 index: usize,
1053 /// length of the current scanline
1054 line_len: usize,
1055 /// size of the frame (width * height * sample_size)
1056 to_write: usize,
1057 /// Flag used to signal the end of the image
1058 end: bool,
1059
1060 width: u32,
1061 height: u32,
1062
1063 bpp: BytesPerPixel,
1064 filter: FilterType,
1065 adaptive_filter: AdaptiveFilterType,
1066 fctl: Option<FrameControl>,
1067 compression: Compression,
1068 }
1069
1070 impl<'a, W: Write> StreamWriter<'a, W> {
new(writer: ChunkOutput<'a, W>, buf_len: usize) -> Result<StreamWriter<'a, W>>1071 fn new(writer: ChunkOutput<'a, W>, buf_len: usize) -> Result<StreamWriter<'a, W>> {
1072 if writer.max_frames() < writer.written {
1073 return Err(EncodingError::Format(FormatErrorKind::EndReached.into()));
1074 }
1075
1076 let PartialInfo {
1077 width,
1078 height,
1079 frame_control: fctl,
1080 compression,
1081 ..
1082 } = writer.info;
1083
1084 let bpp = writer.info.bpp_in_prediction();
1085 let in_len = writer.info.raw_row_length() - 1;
1086 let filter = writer.filter;
1087 let adaptive_filter = writer.adaptive_filter;
1088 let prev_buf = vec![0; in_len];
1089 let curr_buf = vec![0; in_len];
1090
1091 let mut chunk_writer = ChunkWriter::new(writer, buf_len);
1092 let (line_len, to_write) = chunk_writer.next_frame_info();
1093 chunk_writer.write_header()?;
1094 let zlib = ZlibEncoder::new(chunk_writer, compression.to_options());
1095
1096 Ok(StreamWriter {
1097 writer: Wrapper::Zlib(zlib),
1098 index: 0,
1099 prev_buf,
1100 curr_buf,
1101 end: false,
1102 bpp,
1103 filter,
1104 width,
1105 height,
1106 adaptive_filter,
1107 line_len,
1108 to_write,
1109 fctl,
1110 compression,
1111 })
1112 }
1113
1114 /// Set the used filter type for the next frame.
1115 ///
1116 /// The default filter is [`FilterType::Sub`] which provides a basic prediction algorithm for
1117 /// sample values based on the previous. For a potentially better compression ratio, at the
1118 /// cost of more complex processing, try out [`FilterType::Paeth`].
1119 ///
1120 /// [`FilterType::Sub`]: enum.FilterType.html#variant.Sub
1121 /// [`FilterType::Paeth`]: enum.FilterType.html#variant.Paeth
set_filter(&mut self, filter: FilterType)1122 pub fn set_filter(&mut self, filter: FilterType) {
1123 self.filter = filter;
1124 }
1125
1126 /// Set the adaptive filter type for the next frame.
1127 ///
1128 /// Adaptive filtering attempts to select the best filter for each line
1129 /// based on heuristics which minimize the file size for compression rather
1130 /// than use a single filter for the entire image. The default method is
1131 /// [`AdaptiveFilterType::NonAdaptive`].
1132 ///
1133 /// [`AdaptiveFilterType::NonAdaptive`]: enum.AdaptiveFilterType.html
set_adaptive_filter(&mut self, adaptive_filter: AdaptiveFilterType)1134 pub fn set_adaptive_filter(&mut self, adaptive_filter: AdaptiveFilterType) {
1135 self.adaptive_filter = adaptive_filter;
1136 }
1137
1138 /// Set the fraction of time the following frames are going to be displayed,
1139 /// in seconds
1140 ///
1141 /// If the denominator is 0, it is to be treated as if it were 100
1142 /// (that is, the numerator then specifies 1/100ths of a second).
1143 /// If the the value of the numerator is 0 the decoder should render the next frame
1144 /// as quickly as possible, though viewers may impose a reasonable lower bound.
1145 ///
1146 /// This method will return an error if the image is not animated.
set_frame_delay(&mut self, numerator: u16, denominator: u16) -> Result<()>1147 pub fn set_frame_delay(&mut self, numerator: u16, denominator: u16) -> Result<()> {
1148 if let Some(ref mut fctl) = self.fctl {
1149 fctl.delay_den = denominator;
1150 fctl.delay_num = numerator;
1151 Ok(())
1152 } else {
1153 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
1154 }
1155 }
1156
1157 /// Set the dimension of the following frames.
1158 ///
1159 /// This function will return an error when:
1160 /// - The image is not an animated;
1161 ///
1162 /// - The selected dimension, considering also the current frame position,
1163 /// goes outside the image boudries;
1164 ///
1165 /// - One or both the width and height are 0;
1166 ///
set_frame_dimension(&mut self, width: u32, height: u32) -> Result<()>1167 pub fn set_frame_dimension(&mut self, width: u32, height: u32) -> Result<()> {
1168 if let Some(ref mut fctl) = self.fctl {
1169 if Some(width) > self.width.checked_sub(fctl.x_offset)
1170 || Some(height) > self.height.checked_sub(fctl.y_offset)
1171 {
1172 return Err(EncodingError::Format(FormatErrorKind::OutOfBounds.into()));
1173 } else if width == 0 {
1174 return Err(EncodingError::Format(FormatErrorKind::ZeroWidth.into()));
1175 } else if height == 0 {
1176 return Err(EncodingError::Format(FormatErrorKind::ZeroHeight.into()));
1177 }
1178 fctl.width = width;
1179 fctl.height = height;
1180 Ok(())
1181 } else {
1182 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
1183 }
1184 }
1185
1186 /// Set the position of the following frames.
1187 ///
1188 /// An error will be returned if:
1189 /// - The image is not animated;
1190 ///
1191 /// - The selected position, considering also the current frame dimension,
1192 /// goes outside the image boudries;
1193 ///
set_frame_position(&mut self, x: u32, y: u32) -> Result<()>1194 pub fn set_frame_position(&mut self, x: u32, y: u32) -> Result<()> {
1195 if let Some(ref mut fctl) = self.fctl {
1196 if Some(x) > self.width.checked_sub(fctl.width)
1197 || Some(y) > self.height.checked_sub(fctl.height)
1198 {
1199 return Err(EncodingError::Format(FormatErrorKind::OutOfBounds.into()));
1200 }
1201 fctl.x_offset = x;
1202 fctl.y_offset = y;
1203 Ok(())
1204 } else {
1205 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
1206 }
1207 }
1208
1209 /// Set the frame dimension to occupy all the image, starting from
1210 /// the current position.
1211 ///
1212 /// To reset the frame to the full image size [`reset_frame_position`]
1213 /// should be called first.
1214 ///
1215 /// This method will return an error if the image is not animated.
1216 ///
1217 /// [`reset_frame_position`]: struct.Writer.html#method.reset_frame_position
reset_frame_dimension(&mut self) -> Result<()>1218 pub fn reset_frame_dimension(&mut self) -> Result<()> {
1219 if let Some(ref mut fctl) = self.fctl {
1220 fctl.width = self.width - fctl.x_offset;
1221 fctl.height = self.height - fctl.y_offset;
1222 Ok(())
1223 } else {
1224 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
1225 }
1226 }
1227
1228 /// Set the frame position to (0, 0).
1229 ///
1230 /// Equivalent to calling [`set_frame_position(0, 0)`].
1231 ///
1232 /// This method will return an error if the image is not animated.
1233 ///
1234 /// [`set_frame_position(0, 0)`]: struct.Writer.html#method.set_frame_position
reset_frame_position(&mut self) -> Result<()>1235 pub fn reset_frame_position(&mut self) -> Result<()> {
1236 if let Some(ref mut fctl) = self.fctl {
1237 fctl.x_offset = 0;
1238 fctl.y_offset = 0;
1239 Ok(())
1240 } else {
1241 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
1242 }
1243 }
1244
1245 /// Set the blend operation for the following frames.
1246 ///
1247 /// The blend operation specifies whether the frame is to be alpha blended
1248 /// into the current output buffer content, or whether it should completely
1249 /// replace its region in the output buffer.
1250 ///
1251 /// See the [`BlendOp`] documentaion for the possible values and their effects.
1252 ///
1253 /// *Note that for the first frame the two blend modes are functionally
1254 /// equivalent due to the clearing of the output buffer at the beginning
1255 /// of each play.*
1256 ///
1257 /// This method will return an error if the image is not animated.
1258 ///
1259 /// [`BlendOP`]: enum.BlendOp.html
set_blend_op(&mut self, op: BlendOp) -> Result<()>1260 pub fn set_blend_op(&mut self, op: BlendOp) -> Result<()> {
1261 if let Some(ref mut fctl) = self.fctl {
1262 fctl.blend_op = op;
1263 Ok(())
1264 } else {
1265 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
1266 }
1267 }
1268
1269 /// Set the dispose operation for the following frames.
1270 ///
1271 /// The dispose operation specifies how the output buffer should be changed
1272 /// at the end of the delay (before rendering the next frame)
1273 ///
1274 /// See the [`DisposeOp`] documentaion for the possible values and their effects.
1275 ///
1276 /// *Note that if the first frame uses [`DisposeOp::Previous`]
1277 /// it will be treated as [`DisposeOp::Background`].*
1278 ///
1279 /// This method will return an error if the image is not animated.
1280 ///
1281 /// [`DisposeOp`]: ../common/enum.BlendOp.html
1282 /// [`DisposeOp::Previous`]: ../common/enum.BlendOp.html#variant.Previous
1283 /// [`DisposeOp::Background`]: ../common/enum.BlendOp.html#variant.Background
set_dispose_op(&mut self, op: DisposeOp) -> Result<()>1284 pub fn set_dispose_op(&mut self, op: DisposeOp) -> Result<()> {
1285 if let Some(ref mut fctl) = self.fctl {
1286 fctl.dispose_op = op;
1287 Ok(())
1288 } else {
1289 Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
1290 }
1291 }
1292
finish(mut self) -> Result<()>1293 pub fn finish(mut self) -> Result<()> {
1294 if !self.end {
1295 let err = FormatErrorKind::MissingFrames.into();
1296 return Err(EncodingError::Format(err));
1297 } else if self.to_write > 0 {
1298 let err = FormatErrorKind::MissingData(self.to_write).into();
1299 return Err(EncodingError::Format(err));
1300 }
1301 // TODO: call `writer.finish` somehow?
1302 self.flush()?;
1303 Ok(())
1304 }
1305
1306 /// Flushes the buffered chunk, checks if it was the last frame,
1307 /// writes the next frame header and gets the next frame scanline size
1308 /// and image size.
new_frame(&mut self) -> Result<()>1309 fn new_frame(&mut self) -> Result<()> {
1310 let wrt = match &mut self.writer {
1311 Wrapper::Chunk(wrt) => wrt,
1312 _ => unreachable!(),
1313 };
1314 wrt.flush()?;
1315 if let Some(fctl) = self.fctl {
1316 wrt.set_fctl(fctl);
1317 }
1318 let (scansize, size) = wrt.next_frame_info();
1319 self.line_len = scansize;
1320 self.to_write = size;
1321 wrt.writer.written += 1;
1322 wrt.write_header()?;
1323 self.end = wrt.writer.written + 1 == wrt.writer.max_frames();
1324
1325 // now it can be taken because the next statements cannot cause any errors
1326 let wrt = match self.writer.take() {
1327 Wrapper::Chunk(wrt) => wrt,
1328 _ => unreachable!(),
1329 };
1330 self.writer = Wrapper::Zlib(ZlibEncoder::new(wrt, self.compression.to_options()));
1331 Ok(())
1332 }
1333 }
1334
1335 impl<'a, W: Write> Write for StreamWriter<'a, W> {
write(&mut self, mut data: &[u8]) -> io::Result<usize>1336 fn write(&mut self, mut data: &[u8]) -> io::Result<usize> {
1337 if let Wrapper::Unrecoverable = self.writer {
1338 let err = FormatErrorKind::Unrecoverable.into();
1339 return Err(EncodingError::Format(err).into());
1340 }
1341
1342 if data.is_empty() {
1343 return Ok(0);
1344 }
1345
1346 if self.to_write == 0 {
1347 if self.end {
1348 let err = FormatErrorKind::EndReached.into();
1349 return Err(EncodingError::Format(err).into());
1350 }
1351 match self.writer.take() {
1352 Wrapper::Zlib(wrt) => match wrt.finish() {
1353 Ok(chunk) => self.writer = Wrapper::Chunk(chunk),
1354 Err(err) => {
1355 self.writer = Wrapper::Unrecoverable;
1356 return Err(err);
1357 }
1358 },
1359 chunk @ Wrapper::Chunk(_) => self.writer = chunk,
1360 Wrapper::None | Wrapper::Unrecoverable => unreachable!(),
1361 };
1362 self.new_frame()?;
1363 }
1364 let written = data.read(&mut self.curr_buf[..self.line_len][self.index..])?;
1365 self.index += written;
1366 self.to_write -= written;
1367
1368 if self.index == self.line_len {
1369 let filter_type = filter(
1370 self.filter,
1371 self.adaptive_filter,
1372 self.bpp,
1373 &self.prev_buf,
1374 &mut self.curr_buf,
1375 );
1376 // This can't fail as the other variant is used only to allow the zlib encoder to finish
1377 let wrt = match &mut self.writer {
1378 Wrapper::Zlib(wrt) => wrt,
1379 _ => unreachable!(),
1380 };
1381 wrt.write_all(&[filter_type as u8])?;
1382 wrt.write_all(&self.curr_buf)?;
1383 mem::swap(&mut self.prev_buf, &mut self.curr_buf);
1384 self.index = 0;
1385 }
1386 Ok(written)
1387 }
1388
flush(&mut self) -> io::Result<()>1389 fn flush(&mut self) -> io::Result<()> {
1390 match &mut self.writer {
1391 Wrapper::Zlib(wrt) => wrt.flush()?,
1392 _ => unreachable!(),
1393 }
1394 if self.index > 0 {
1395 let err = FormatErrorKind::WrittenTooMuch(self.index).into();
1396 return Err(EncodingError::Format(err).into());
1397 }
1398 Ok(())
1399 }
1400 }
1401
1402 impl<W: Write> Drop for StreamWriter<'_, W> {
drop(&mut self)1403 fn drop(&mut self) {
1404 let _ = self.flush();
1405 }
1406 }
1407
1408 #[cfg(test)]
1409 mod tests {
1410 use super::*;
1411 use crate::Decoder;
1412
1413 use rand::{thread_rng, Rng};
1414 use std::fs::File;
1415 use std::io::Write;
1416 use std::{cmp, io};
1417
1418 #[test]
roundtrip()1419 fn roundtrip() {
1420 // More loops = more random testing, but also more test wait time
1421 for _ in 0..10 {
1422 for path in glob::glob("tests/pngsuite/*.png")
1423 .unwrap()
1424 .map(|r| r.unwrap())
1425 {
1426 if path.file_name().unwrap().to_str().unwrap().starts_with('x') {
1427 // x* files are expected to fail to decode
1428 continue;
1429 }
1430 eprintln!("{}", path.display());
1431 // Decode image
1432 let decoder = Decoder::new(File::open(path).unwrap());
1433 let mut reader = decoder.read_info().unwrap();
1434 let mut buf = vec![0; reader.output_buffer_size()];
1435 let info = reader.next_frame(&mut buf).unwrap();
1436 // Encode decoded image
1437 let mut out = Vec::new();
1438 {
1439 let mut wrapper = RandomChunkWriter {
1440 rng: thread_rng(),
1441 w: &mut out,
1442 };
1443
1444 let mut encoder = Encoder::new(&mut wrapper, info.width, info.height);
1445 encoder.set_color(info.color_type);
1446 encoder.set_depth(info.bit_depth);
1447 if let Some(palette) = &reader.info().palette {
1448 encoder.set_palette(palette.clone());
1449 }
1450 let mut encoder = encoder.write_header().unwrap();
1451 encoder.write_image_data(&buf).unwrap();
1452 }
1453 // Decode encoded decoded image
1454 let decoder = Decoder::new(&*out);
1455 let mut reader = decoder.read_info().unwrap();
1456 let mut buf2 = vec![0; reader.output_buffer_size()];
1457 reader.next_frame(&mut buf2).unwrap();
1458 // check if the encoded image is ok:
1459 assert_eq!(buf, buf2);
1460 }
1461 }
1462 }
1463
1464 #[test]
roundtrip_stream()1465 fn roundtrip_stream() {
1466 // More loops = more random testing, but also more test wait time
1467 for _ in 0..10 {
1468 for path in glob::glob("tests/pngsuite/*.png")
1469 .unwrap()
1470 .map(|r| r.unwrap())
1471 {
1472 if path.file_name().unwrap().to_str().unwrap().starts_with('x') {
1473 // x* files are expected to fail to decode
1474 continue;
1475 }
1476 // Decode image
1477 let decoder = Decoder::new(File::open(path).unwrap());
1478 let mut reader = decoder.read_info().unwrap();
1479 let mut buf = vec![0; reader.output_buffer_size()];
1480 let info = reader.next_frame(&mut buf).unwrap();
1481 // Encode decoded image
1482 let mut out = Vec::new();
1483 {
1484 let mut wrapper = RandomChunkWriter {
1485 rng: thread_rng(),
1486 w: &mut out,
1487 };
1488
1489 let mut encoder = Encoder::new(&mut wrapper, info.width, info.height);
1490 encoder.set_color(info.color_type);
1491 encoder.set_depth(info.bit_depth);
1492 if let Some(palette) = &reader.info().palette {
1493 encoder.set_palette(palette.clone());
1494 }
1495 let mut encoder = encoder.write_header().unwrap();
1496 let mut stream_writer = encoder.stream_writer().unwrap();
1497
1498 let mut outer_wrapper = RandomChunkWriter {
1499 rng: thread_rng(),
1500 w: &mut stream_writer,
1501 };
1502
1503 outer_wrapper.write_all(&buf).unwrap();
1504 }
1505 // Decode encoded decoded image
1506 let decoder = Decoder::new(&*out);
1507 let mut reader = decoder.read_info().unwrap();
1508 let mut buf2 = vec![0; reader.output_buffer_size()];
1509 reader.next_frame(&mut buf2).unwrap();
1510 // check if the encoded image is ok:
1511 assert_eq!(buf, buf2);
1512 }
1513 }
1514 }
1515
1516 #[test]
image_palette() -> Result<()>1517 fn image_palette() -> Result<()> {
1518 for &bit_depth in &[1u8, 2, 4, 8] {
1519 // Do a reference decoding, choose a fitting palette image from pngsuite
1520 let path = format!("tests/pngsuite/basn3p0{}.png", bit_depth);
1521 let decoder = Decoder::new(File::open(&path).unwrap());
1522 let mut reader = decoder.read_info().unwrap();
1523
1524 let mut decoded_pixels = vec![0; reader.output_buffer_size()];
1525 let info = reader.info();
1526 assert_eq!(
1527 info.width as usize * info.height as usize * usize::from(bit_depth),
1528 decoded_pixels.len() * 8
1529 );
1530 let info = reader.next_frame(&mut decoded_pixels).unwrap();
1531 let indexed_data = decoded_pixels;
1532
1533 let palette = reader.info().palette.as_ref().unwrap();
1534 let mut out = Vec::new();
1535 {
1536 let mut encoder = Encoder::new(&mut out, info.width, info.height);
1537 encoder.set_depth(BitDepth::from_u8(bit_depth).unwrap());
1538 encoder.set_color(ColorType::Indexed);
1539 encoder.set_palette(palette.as_ref());
1540
1541 let mut writer = encoder.write_header().unwrap();
1542 writer.write_image_data(&indexed_data).unwrap();
1543 }
1544
1545 // Decode re-encoded image
1546 let decoder = Decoder::new(&*out);
1547 let mut reader = decoder.read_info().unwrap();
1548 let mut redecoded = vec![0; reader.output_buffer_size()];
1549 reader.next_frame(&mut redecoded).unwrap();
1550 // check if the encoded image is ok:
1551 assert_eq!(indexed_data, redecoded);
1552 }
1553 Ok(())
1554 }
1555
1556 #[test]
expect_error_on_wrong_image_len() -> Result<()>1557 fn expect_error_on_wrong_image_len() -> Result<()> {
1558 use std::io::Cursor;
1559
1560 let width = 10;
1561 let height = 10;
1562
1563 let output = vec![0u8; 1024];
1564 let writer = Cursor::new(output);
1565 let mut encoder = Encoder::new(writer, width as u32, height as u32);
1566 encoder.set_depth(BitDepth::Eight);
1567 encoder.set_color(ColorType::Rgb);
1568 let mut png_writer = encoder.write_header()?;
1569
1570 let correct_image_size = width * height * 3;
1571 let image = vec![0u8; correct_image_size + 1];
1572 let result = png_writer.write_image_data(image.as_ref());
1573 assert!(result.is_err());
1574
1575 Ok(())
1576 }
1577
1578 #[test]
expect_error_on_empty_image() -> Result<()>1579 fn expect_error_on_empty_image() -> Result<()> {
1580 use std::io::Cursor;
1581
1582 let output = vec![0u8; 1024];
1583 let mut writer = Cursor::new(output);
1584
1585 let encoder = Encoder::new(&mut writer, 0, 0);
1586 assert!(encoder.write_header().is_err());
1587
1588 let encoder = Encoder::new(&mut writer, 100, 0);
1589 assert!(encoder.write_header().is_err());
1590
1591 let encoder = Encoder::new(&mut writer, 0, 100);
1592 assert!(encoder.write_header().is_err());
1593
1594 Ok(())
1595 }
1596
1597 #[test]
expect_error_on_invalid_bit_depth_color_type_combination() -> Result<()>1598 fn expect_error_on_invalid_bit_depth_color_type_combination() -> Result<()> {
1599 use std::io::Cursor;
1600
1601 let output = vec![0u8; 1024];
1602 let mut writer = Cursor::new(output);
1603
1604 let mut encoder = Encoder::new(&mut writer, 1, 1);
1605 encoder.set_depth(BitDepth::One);
1606 encoder.set_color(ColorType::Rgb);
1607 assert!(encoder.write_header().is_err());
1608
1609 let mut encoder = Encoder::new(&mut writer, 1, 1);
1610 encoder.set_depth(BitDepth::One);
1611 encoder.set_color(ColorType::GrayscaleAlpha);
1612 assert!(encoder.write_header().is_err());
1613
1614 let mut encoder = Encoder::new(&mut writer, 1, 1);
1615 encoder.set_depth(BitDepth::One);
1616 encoder.set_color(ColorType::Rgba);
1617 assert!(encoder.write_header().is_err());
1618
1619 let mut encoder = Encoder::new(&mut writer, 1, 1);
1620 encoder.set_depth(BitDepth::Two);
1621 encoder.set_color(ColorType::Rgb);
1622 assert!(encoder.write_header().is_err());
1623
1624 let mut encoder = Encoder::new(&mut writer, 1, 1);
1625 encoder.set_depth(BitDepth::Two);
1626 encoder.set_color(ColorType::GrayscaleAlpha);
1627 assert!(encoder.write_header().is_err());
1628
1629 let mut encoder = Encoder::new(&mut writer, 1, 1);
1630 encoder.set_depth(BitDepth::Two);
1631 encoder.set_color(ColorType::Rgba);
1632 assert!(encoder.write_header().is_err());
1633
1634 let mut encoder = Encoder::new(&mut writer, 1, 1);
1635 encoder.set_depth(BitDepth::Four);
1636 encoder.set_color(ColorType::Rgb);
1637 assert!(encoder.write_header().is_err());
1638
1639 let mut encoder = Encoder::new(&mut writer, 1, 1);
1640 encoder.set_depth(BitDepth::Four);
1641 encoder.set_color(ColorType::GrayscaleAlpha);
1642 assert!(encoder.write_header().is_err());
1643
1644 let mut encoder = Encoder::new(&mut writer, 1, 1);
1645 encoder.set_depth(BitDepth::Four);
1646 encoder.set_color(ColorType::Rgba);
1647 assert!(encoder.write_header().is_err());
1648
1649 let mut encoder = Encoder::new(&mut writer, 1, 1);
1650 encoder.set_depth(BitDepth::Sixteen);
1651 encoder.set_color(ColorType::Indexed);
1652 assert!(encoder.write_header().is_err());
1653
1654 Ok(())
1655 }
1656
1657 #[test]
can_write_header_with_valid_bit_depth_color_type_combination() -> Result<()>1658 fn can_write_header_with_valid_bit_depth_color_type_combination() -> Result<()> {
1659 use std::io::Cursor;
1660
1661 let output = vec![0u8; 1024];
1662 let mut writer = Cursor::new(output);
1663
1664 let mut encoder = Encoder::new(&mut writer, 1, 1);
1665 encoder.set_depth(BitDepth::One);
1666 encoder.set_color(ColorType::Grayscale);
1667 assert!(encoder.write_header().is_ok());
1668
1669 let mut encoder = Encoder::new(&mut writer, 1, 1);
1670 encoder.set_depth(BitDepth::One);
1671 encoder.set_color(ColorType::Indexed);
1672 assert!(encoder.write_header().is_ok());
1673
1674 let mut encoder = Encoder::new(&mut writer, 1, 1);
1675 encoder.set_depth(BitDepth::Two);
1676 encoder.set_color(ColorType::Grayscale);
1677 assert!(encoder.write_header().is_ok());
1678
1679 let mut encoder = Encoder::new(&mut writer, 1, 1);
1680 encoder.set_depth(BitDepth::Two);
1681 encoder.set_color(ColorType::Indexed);
1682 assert!(encoder.write_header().is_ok());
1683
1684 let mut encoder = Encoder::new(&mut writer, 1, 1);
1685 encoder.set_depth(BitDepth::Four);
1686 encoder.set_color(ColorType::Grayscale);
1687 assert!(encoder.write_header().is_ok());
1688
1689 let mut encoder = Encoder::new(&mut writer, 1, 1);
1690 encoder.set_depth(BitDepth::Four);
1691 encoder.set_color(ColorType::Indexed);
1692 assert!(encoder.write_header().is_ok());
1693
1694 let mut encoder = Encoder::new(&mut writer, 1, 1);
1695 encoder.set_depth(BitDepth::Eight);
1696 encoder.set_color(ColorType::Grayscale);
1697 assert!(encoder.write_header().is_ok());
1698
1699 let mut encoder = Encoder::new(&mut writer, 1, 1);
1700 encoder.set_depth(BitDepth::Eight);
1701 encoder.set_color(ColorType::Rgb);
1702 assert!(encoder.write_header().is_ok());
1703
1704 let mut encoder = Encoder::new(&mut writer, 1, 1);
1705 encoder.set_depth(BitDepth::Eight);
1706 encoder.set_color(ColorType::Indexed);
1707 assert!(encoder.write_header().is_ok());
1708
1709 let mut encoder = Encoder::new(&mut writer, 1, 1);
1710 encoder.set_depth(BitDepth::Eight);
1711 encoder.set_color(ColorType::GrayscaleAlpha);
1712 assert!(encoder.write_header().is_ok());
1713
1714 let mut encoder = Encoder::new(&mut writer, 1, 1);
1715 encoder.set_depth(BitDepth::Eight);
1716 encoder.set_color(ColorType::Rgba);
1717 assert!(encoder.write_header().is_ok());
1718
1719 let mut encoder = Encoder::new(&mut writer, 1, 1);
1720 encoder.set_depth(BitDepth::Sixteen);
1721 encoder.set_color(ColorType::Grayscale);
1722 assert!(encoder.write_header().is_ok());
1723
1724 let mut encoder = Encoder::new(&mut writer, 1, 1);
1725 encoder.set_depth(BitDepth::Sixteen);
1726 encoder.set_color(ColorType::Rgb);
1727 assert!(encoder.write_header().is_ok());
1728
1729 let mut encoder = Encoder::new(&mut writer, 1, 1);
1730 encoder.set_depth(BitDepth::Sixteen);
1731 encoder.set_color(ColorType::GrayscaleAlpha);
1732 assert!(encoder.write_header().is_ok());
1733
1734 let mut encoder = Encoder::new(&mut writer, 1, 1);
1735 encoder.set_depth(BitDepth::Sixteen);
1736 encoder.set_color(ColorType::Rgba);
1737 assert!(encoder.write_header().is_ok());
1738
1739 Ok(())
1740 }
1741
1742 #[test]
all_filters_roundtrip() -> io::Result<()>1743 fn all_filters_roundtrip() -> io::Result<()> {
1744 let pixel: Vec<_> = (0..48).collect();
1745
1746 let roundtrip = |filter: FilterType| -> io::Result<()> {
1747 let mut buffer = vec![];
1748 let mut encoder = Encoder::new(&mut buffer, 4, 4);
1749 encoder.set_depth(BitDepth::Eight);
1750 encoder.set_color(ColorType::Rgb);
1751 encoder.set_filter(filter);
1752 encoder.write_header()?.write_image_data(&pixel)?;
1753
1754 let decoder = crate::Decoder::new(io::Cursor::new(buffer));
1755 let mut reader = decoder.read_info()?;
1756 let info = reader.info();
1757 assert_eq!(info.width, 4);
1758 assert_eq!(info.height, 4);
1759 let mut dest = vec![0; pixel.len()];
1760 reader.next_frame(&mut dest)?;
1761 assert_eq!(dest, pixel, "Deviation with filter type {:?}", filter);
1762
1763 Ok(())
1764 };
1765
1766 roundtrip(FilterType::NoFilter)?;
1767 roundtrip(FilterType::Sub)?;
1768 roundtrip(FilterType::Up)?;
1769 roundtrip(FilterType::Avg)?;
1770 roundtrip(FilterType::Paeth)?;
1771
1772 Ok(())
1773 }
1774
1775 #[test]
some_gamma_roundtrip() -> io::Result<()>1776 fn some_gamma_roundtrip() -> io::Result<()> {
1777 let pixel: Vec<_> = (0..48).collect();
1778
1779 let roundtrip = |gamma: Option<ScaledFloat>| -> io::Result<()> {
1780 let mut buffer = vec![];
1781 let mut encoder = Encoder::new(&mut buffer, 4, 4);
1782 encoder.set_depth(BitDepth::Eight);
1783 encoder.set_color(ColorType::Rgb);
1784 encoder.set_filter(FilterType::Avg);
1785 if let Some(gamma) = gamma {
1786 encoder.set_source_gamma(gamma);
1787 }
1788 encoder.write_header()?.write_image_data(&pixel)?;
1789
1790 let decoder = crate::Decoder::new(io::Cursor::new(buffer));
1791 let mut reader = decoder.read_info()?;
1792 assert_eq!(
1793 reader.info().source_gamma,
1794 gamma,
1795 "Deviation with gamma {:?}",
1796 gamma
1797 );
1798 let mut dest = vec![0; pixel.len()];
1799 let info = reader.next_frame(&mut dest)?;
1800 assert_eq!(info.width, 4);
1801 assert_eq!(info.height, 4);
1802
1803 Ok(())
1804 };
1805
1806 roundtrip(None)?;
1807 roundtrip(Some(ScaledFloat::new(0.35)))?;
1808 roundtrip(Some(ScaledFloat::new(0.45)))?;
1809 roundtrip(Some(ScaledFloat::new(0.55)))?;
1810 roundtrip(Some(ScaledFloat::new(0.7)))?;
1811 roundtrip(Some(ScaledFloat::new(1.0)))?;
1812 roundtrip(Some(ScaledFloat::new(2.5)))?;
1813
1814 Ok(())
1815 }
1816
1817 /// A Writer that only writes a few bytes at a time
1818 struct RandomChunkWriter<R: Rng, W: Write> {
1819 rng: R,
1820 w: W,
1821 }
1822
1823 impl<R: Rng, W: Write> Write for RandomChunkWriter<R, W> {
write(&mut self, buf: &[u8]) -> io::Result<usize>1824 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1825 // choose a random length to write
1826 let len = cmp::min(self.rng.gen_range(1, 50), buf.len());
1827
1828 self.w.write(&buf[0..len])
1829 }
1830
flush(&mut self) -> io::Result<()>1831 fn flush(&mut self) -> io::Result<()> {
1832 self.w.flush()
1833 }
1834 }
1835 }
1836
1837 /// Mod to encapsulate the converters depending on the `deflate` crate.
1838 ///
1839 /// Since this only contains trait impls, there is no need to make this public, they are simply
1840 /// available when the mod is compiled as well.
1841 impl crate::common::Compression {
to_options(self) -> deflate::CompressionOptions1842 fn to_options(self) -> deflate::CompressionOptions {
1843 match self {
1844 Compression::Default => deflate::CompressionOptions::default(),
1845 Compression::Fast => deflate::CompressionOptions::fast(),
1846 Compression::Best => deflate::CompressionOptions::high(),
1847 Compression::Huffman => deflate::CompressionOptions::huffman_only(),
1848 Compression::Rle => deflate::CompressionOptions::rle(),
1849 }
1850 }
1851 }
1852