use borrow::Cow;
use io::{Read, Write};
use ops::{Deref, DerefMut};
use std::{borrow, error, fmt, io, mem, ops, result};
use crc32fast::Hasher as Crc32;
use deflate::write::ZlibEncoder;
use crate::chunk::{self, ChunkType};
use crate::common::{
AnimationControl, BitDepth, BlendOp, BytesPerPixel, ColorType, Compression, DisposeOp,
FrameControl, Info, ParameterError, ParameterErrorKind, ScaledFloat,
};
use crate::filter::{filter, AdaptiveFilterType, FilterType};
use crate::text_metadata::{
EncodableTextChunk, ITXtChunk, TEXtChunk, TextEncodingError, ZTXtChunk,
};
use crate::traits::WriteBytesExt;
pub type Result<T> = result::Result<T, EncodingError>;
#[derive(Debug)]
pub enum EncodingError {
IoError(io::Error),
Format(FormatError),
Parameter(ParameterError),
LimitsExceeded,
}
#[derive(Debug)]
pub struct FormatError {
inner: FormatErrorKind,
}
#[derive(Debug)]
enum FormatErrorKind {
ZeroWidth,
ZeroHeight,
InvalidColorCombination(BitDepth, ColorType),
NoPalette,
WrittenTooMuch(usize),
NotAnimated,
OutOfBounds,
EndReached,
ZeroFrames,
MissingFrames,
MissingData(usize),
Unrecoverable,
BadTextEncoding(TextEncodingError),
}
impl error::Error for EncodingError {
fn cause(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
EncodingError::IoError(err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for EncodingError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
use self::EncodingError::*;
match self {
IoError(err) => write!(fmt, "{}", err),
Format(desc) => write!(fmt, "{}", desc),
Parameter(desc) => write!(fmt, "{}", desc),
LimitsExceeded => write!(fmt, "Limits are exceeded."),
}
}
}
impl fmt::Display for FormatError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
use FormatErrorKind::*;
match self.inner {
ZeroWidth => write!(fmt, "Zero width not allowed"),
ZeroHeight => write!(fmt, "Zero height not allowed"),
ZeroFrames => write!(fmt, "Zero frames not allowed"),
InvalidColorCombination(depth, color) => write!(
fmt,
"Invalid combination of bit-depth '{:?}' and color-type '{:?}'",
depth, color
),
NoPalette => write!(fmt, "can't write indexed image without palette"),
WrittenTooMuch(index) => write!(fmt, "wrong data size, got {} bytes too many", index),
NotAnimated => write!(fmt, "not an animation"),
OutOfBounds => write!(
fmt,
"the dimension and position go over the frame boundaries"
),
EndReached => write!(fmt, "all the frames have been already written"),
MissingFrames => write!(fmt, "there are still frames to be written"),
MissingData(n) => write!(fmt, "there are still {} bytes to be written", n),
Unrecoverable => write!(
fmt,
"a previous error put the writer into an unrecoverable state"
),
BadTextEncoding(tee) => match tee {
TextEncodingError::Unrepresentable => write!(
fmt,
"The text metadata cannot be encoded into valid ISO 8859-1"
),
TextEncodingError::InvalidKeywordSize => write!(fmt, "Invalid keyword size"),
TextEncodingError::CompressionError => {
write!(fmt, "Unable to compress text metadata")
}
},
}
}
}
impl From<io::Error> for EncodingError {
fn from(err: io::Error) -> EncodingError {
EncodingError::IoError(err)
}
}
impl From<EncodingError> for io::Error {
fn from(err: EncodingError) -> io::Error {
io::Error::new(io::ErrorKind::Other, err.to_string())
}
}
impl From<FormatErrorKind> for FormatError {
fn from(kind: FormatErrorKind) -> Self {
FormatError { inner: kind }
}
}
impl From<TextEncodingError> for EncodingError {
fn from(tee: TextEncodingError) -> Self {
EncodingError::Format(FormatError {
inner: FormatErrorKind::BadTextEncoding(tee),
})
}
}
pub struct Encoder<'a, W: Write> {
w: W,
info: Info<'a>,
options: Options,
}
#[derive(Default)]
struct Options {
filter: FilterType,
adaptive_filter: AdaptiveFilterType,
sep_def_img: bool,
validate_sequence: bool,
}
impl<'a, W: Write> Encoder<'a, W> {
pub fn new(w: W, width: u32, height: u32) -> Encoder<'static, W> {
Encoder {
w,
info: Info::with_size(width, height),
options: Options::default(),
}
}
pub fn set_animated(&mut self, num_frames: u32, num_plays: u32) -> Result<()> {
if num_frames == 0 {
return Err(EncodingError::Format(FormatErrorKind::ZeroFrames.into()));
}
let actl = AnimationControl {
num_frames,
num_plays,
};
let fctl = FrameControl {
sequence_number: 0,
width: self.info.width,
height: self.info.height,
..Default::default()
};
self.info.animation_control = Some(actl);
self.info.frame_control = Some(fctl);
Ok(())
}
pub fn set_sep_def_img(&mut self, sep_def_img: bool) -> Result<()> {
if self.info.animation_control.is_some() {
self.options.sep_def_img = sep_def_img;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_palette<T: Into<Cow<'a, [u8]>>>(&mut self, palette: T) {
self.info.palette = Some(palette.into());
}
pub fn set_trns<T: Into<Cow<'a, [u8]>>>(&mut self, trns: T) {
self.info.trns = Some(trns.into());
}
pub fn set_source_gamma(&mut self, source_gamma: ScaledFloat) {
self.info.source_gamma = Some(source_gamma);
}
pub fn set_source_chromaticities(
&mut self,
source_chromaticities: super::SourceChromaticities,
) {
self.info.source_chromaticities = Some(source_chromaticities);
}
pub fn set_srgb(&mut self, rendering_intent: super::SrgbRenderingIntent) {
self.info.srgb = Some(rendering_intent);
}
pub fn write_header(self) -> Result<Writer<W>> {
Writer::new(self.w, PartialInfo::new(&self.info), self.options).init(&self.info)
}
pub fn set_color(&mut self, color: ColorType) {
self.info.color_type = color;
}
pub fn set_depth(&mut self, depth: BitDepth) {
self.info.bit_depth = depth;
}
pub fn set_compression(&mut self, compression: Compression) {
self.info.compression = compression;
}
pub fn set_filter(&mut self, filter: FilterType) {
self.options.filter = filter;
}
pub fn set_adaptive_filter(&mut self, adaptive_filter: AdaptiveFilterType) {
self.options.adaptive_filter = adaptive_filter;
}
pub fn set_frame_delay(&mut self, numerator: u16, denominator: u16) -> Result<()> {
if let Some(ref mut fctl) = self.info.frame_control {
fctl.delay_den = denominator;
fctl.delay_num = numerator;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_blend_op(&mut self, op: BlendOp) -> Result<()> {
if let Some(ref mut fctl) = self.info.frame_control {
fctl.blend_op = op;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_dispose_op(&mut self, op: DisposeOp) -> Result<()> {
if let Some(ref mut fctl) = self.info.frame_control {
fctl.dispose_op = op;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn add_text_chunk(&mut self, keyword: String, text: String) -> Result<()> {
let text_chunk = TEXtChunk::new(keyword, text);
self.info.uncompressed_latin1_text.push(text_chunk);
Ok(())
}
pub fn add_ztxt_chunk(&mut self, keyword: String, text: String) -> Result<()> {
let text_chunk = ZTXtChunk::new(keyword, text);
self.info.compressed_latin1_text.push(text_chunk);
Ok(())
}
pub fn add_itxt_chunk(&mut self, keyword: String, text: String) -> Result<()> {
let text_chunk = ITXtChunk::new(keyword, text);
self.info.utf8_text.push(text_chunk);
Ok(())
}
pub fn validate_sequence(&mut self, validate: bool) {
self.options.validate_sequence = validate;
}
}
pub struct Writer<W: Write> {
w: W,
info: PartialInfo,
options: Options,
images_written: u64,
animation_written: u32,
iend_written: bool,
}
struct PartialInfo {
width: u32,
height: u32,
bit_depth: BitDepth,
color_type: ColorType,
frame_control: Option<FrameControl>,
animation_control: Option<AnimationControl>,
compression: Compression,
has_palette: bool,
}
impl PartialInfo {
fn new(info: &Info) -> Self {
PartialInfo {
width: info.width,
height: info.height,
bit_depth: info.bit_depth,
color_type: info.color_type,
frame_control: info.frame_control,
animation_control: info.animation_control,
compression: info.compression,
has_palette: info.palette.is_some(),
}
}
fn bpp_in_prediction(&self) -> BytesPerPixel {
self.to_info().bpp_in_prediction()
}
fn raw_row_length(&self) -> usize {
self.to_info().raw_row_length()
}
fn raw_row_length_from_width(&self, width: u32) -> usize {
self.to_info().raw_row_length_from_width(width)
}
fn to_info(&self) -> Info<'static> {
Info {
width: self.width,
height: self.height,
bit_depth: self.bit_depth,
color_type: self.color_type,
frame_control: self.frame_control,
animation_control: self.animation_control,
compression: self.compression,
..Default::default()
}
}
}
const DEFAULT_BUFFER_LENGTH: usize = 4 * 1024;
pub(crate) fn write_chunk<W: Write>(mut w: W, name: chunk::ChunkType, data: &[u8]) -> Result<()> {
w.write_be(data.len() as u32)?;
w.write_all(&name.0)?;
w.write_all(data)?;
let mut crc = Crc32::new();
crc.update(&name.0);
crc.update(data);
w.write_be(crc.finalize())?;
Ok(())
}
impl<W: Write> Writer<W> {
fn new(w: W, info: PartialInfo, options: Options) -> Writer<W> {
Writer {
w,
info,
options,
images_written: 0,
animation_written: 0,
iend_written: false,
}
}
fn init(mut self, info: &Info<'_>) -> Result<Self> {
if self.info.width == 0 {
return Err(EncodingError::Format(FormatErrorKind::ZeroWidth.into()));
}
if self.info.height == 0 {
return Err(EncodingError::Format(FormatErrorKind::ZeroHeight.into()));
}
if self
.info
.color_type
.is_combination_invalid(self.info.bit_depth)
{
return Err(EncodingError::Format(
FormatErrorKind::InvalidColorCombination(self.info.bit_depth, self.info.color_type)
.into(),
));
}
self.w.write_all(&[137, 80, 78, 71, 13, 10, 26, 10])?; info.encode(&mut self.w)?;
Ok(self)
}
pub fn write_chunk(&mut self, name: ChunkType, data: &[u8]) -> Result<()> {
use std::convert::TryFrom;
if u32::try_from(data.len()).map_or(true, |length| length > i32::MAX as u32) {
let kind = FormatErrorKind::WrittenTooMuch(data.len() - i32::MAX as usize);
return Err(EncodingError::Format(kind.into()));
}
write_chunk(&mut self.w, name, data)
}
pub fn write_text_chunk<T: EncodableTextChunk>(&mut self, text_chunk: &T) -> Result<()> {
text_chunk.encode(&mut self.w)
}
fn validate_new_image(&self) -> Result<()> {
if !self.options.validate_sequence {
return Ok(());
}
match self.info.animation_control {
None => {
if self.images_written == 0 {
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::EndReached.into()))
}
}
Some(_) => {
if self.info.frame_control.is_some() {
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::EndReached.into()))
}
}
}
}
fn validate_sequence_done(&self) -> Result<()> {
if !self.options.validate_sequence {
return Ok(());
}
if (self.info.animation_control.is_some() && self.info.frame_control.is_some())
|| self.images_written == 0
{
Err(EncodingError::Format(FormatErrorKind::MissingFrames.into()))
} else {
Ok(())
}
}
const MAX_IDAT_CHUNK_LEN: u32 = std::u32::MAX >> 1;
#[allow(non_upper_case_globals)]
const MAX_fdAT_CHUNK_LEN: u32 = (std::u32::MAX >> 1) - 4;
pub fn write_image_data(&mut self, data: &[u8]) -> Result<()> {
if self.info.color_type == ColorType::Indexed && !self.info.has_palette {
return Err(EncodingError::Format(FormatErrorKind::NoPalette.into()));
}
self.validate_new_image()?;
let width: usize;
let height: usize;
if let Some(ref mut fctl) = self.info.frame_control {
width = fctl.width as usize;
height = fctl.height as usize;
} else {
width = self.info.width as usize;
height = self.info.height as usize;
}
let in_len = self.info.raw_row_length_from_width(width as u32) - 1;
let data_size = in_len * height;
if data_size != data.len() {
return Err(EncodingError::Parameter(
ParameterErrorKind::ImageBufferSize {
expected: data_size,
actual: data.len(),
}
.into(),
));
}
let prev = vec![0; in_len];
let mut prev = prev.as_slice();
let mut current = vec![0; in_len];
let mut zlib =
deflate::write::ZlibEncoder::new(Vec::new(), self.info.compression.to_options());
let bpp = self.info.bpp_in_prediction();
let filter_method = self.options.filter;
let adaptive_method = self.options.adaptive_filter;
for line in data.chunks(in_len) {
current.copy_from_slice(line);
let filter_type = filter(filter_method, adaptive_method, bpp, prev, &mut current);
zlib.write_all(&[filter_type as u8])?;
zlib.write_all(¤t)?;
prev = line;
}
let zlib_encoded = zlib.finish()?;
match self.info.frame_control {
None => {
self.write_zlib_encoded_idat(&zlib_encoded)?;
}
Some(_) if self.should_skip_frame_control_on_default_image() => {
self.write_zlib_encoded_idat(&zlib_encoded)?;
}
Some(ref mut fctl) => {
fctl.encode(&mut self.w)?;
fctl.sequence_number = fctl.sequence_number.wrapping_add(1);
self.animation_written += 1;
if self.images_written == 0 {
self.write_zlib_encoded_idat(&zlib_encoded)?;
} else {
let buff_size = zlib_encoded.len().min(Self::MAX_fdAT_CHUNK_LEN as usize);
let mut alldata = vec![0u8; 4 + buff_size];
for chunk in zlib_encoded.chunks(Self::MAX_fdAT_CHUNK_LEN as usize) {
alldata[..4].copy_from_slice(&fctl.sequence_number.to_be_bytes());
alldata[4..][..chunk.len()].copy_from_slice(chunk);
write_chunk(&mut self.w, chunk::fdAT, &alldata[..4 + chunk.len()])?;
fctl.sequence_number = fctl.sequence_number.wrapping_add(1);
}
}
}
}
self.increment_images_written();
Ok(())
}
fn increment_images_written(&mut self) {
self.images_written = self.images_written.saturating_add(1);
if let Some(actl) = self.info.animation_control {
if actl.num_frames <= self.animation_written {
self.info.frame_control = None;
}
}
}
fn write_iend(&mut self) -> Result<()> {
self.iend_written = true;
self.write_chunk(chunk::IEND, &[])
}
fn should_skip_frame_control_on_default_image(&self) -> bool {
self.options.sep_def_img && self.images_written == 0
}
fn write_zlib_encoded_idat(&mut self, zlib_encoded: &[u8]) -> Result<()> {
for chunk in zlib_encoded.chunks(Self::MAX_IDAT_CHUNK_LEN as usize) {
self.write_chunk(chunk::IDAT, chunk)?;
}
Ok(())
}
pub fn set_filter(&mut self, filter: FilterType) {
self.options.filter = filter;
}
pub fn set_adaptive_filter(&mut self, adaptive_filter: AdaptiveFilterType) {
self.options.adaptive_filter = adaptive_filter;
}
pub fn set_frame_delay(&mut self, numerator: u16, denominator: u16) -> Result<()> {
if let Some(ref mut fctl) = self.info.frame_control {
fctl.delay_den = denominator;
fctl.delay_num = numerator;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_frame_dimension(&mut self, width: u32, height: u32) -> Result<()> {
if let Some(ref mut fctl) = self.info.frame_control {
if Some(width) > self.info.width.checked_sub(fctl.x_offset)
|| Some(height) > self.info.height.checked_sub(fctl.y_offset)
{
return Err(EncodingError::Format(FormatErrorKind::OutOfBounds.into()));
} else if width == 0 {
return Err(EncodingError::Format(FormatErrorKind::ZeroWidth.into()));
} else if height == 0 {
return Err(EncodingError::Format(FormatErrorKind::ZeroHeight.into()));
}
fctl.width = width;
fctl.height = height;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_frame_position(&mut self, x: u32, y: u32) -> Result<()> {
if let Some(ref mut fctl) = self.info.frame_control {
if Some(x) > self.info.width.checked_sub(fctl.width)
|| Some(y) > self.info.height.checked_sub(fctl.height)
{
return Err(EncodingError::Format(FormatErrorKind::OutOfBounds.into()));
}
fctl.x_offset = x;
fctl.y_offset = y;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn reset_frame_dimension(&mut self) -> Result<()> {
if let Some(ref mut fctl) = self.info.frame_control {
fctl.width = self.info.width - fctl.x_offset;
fctl.height = self.info.height - fctl.y_offset;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn reset_frame_position(&mut self) -> Result<()> {
if let Some(ref mut fctl) = self.info.frame_control {
fctl.x_offset = 0;
fctl.y_offset = 0;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_blend_op(&mut self, op: BlendOp) -> Result<()> {
if let Some(ref mut fctl) = self.info.frame_control {
fctl.blend_op = op;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_dispose_op(&mut self, op: DisposeOp) -> Result<()> {
if let Some(ref mut fctl) = self.info.frame_control {
fctl.dispose_op = op;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn stream_writer(&mut self) -> Result<StreamWriter<W>> {
self.stream_writer_with_size(DEFAULT_BUFFER_LENGTH)
}
pub fn stream_writer_with_size(&mut self, size: usize) -> Result<StreamWriter<W>> {
StreamWriter::new(ChunkOutput::Borrowed(self), size)
}
pub fn into_stream_writer(self) -> Result<StreamWriter<'static, W>> {
self.into_stream_writer_with_size(DEFAULT_BUFFER_LENGTH)
}
pub fn into_stream_writer_with_size(self, size: usize) -> Result<StreamWriter<'static, W>> {
StreamWriter::new(ChunkOutput::Owned(self), size)
}
pub fn finish(mut self) -> Result<()> {
self.validate_sequence_done()?;
self.write_iend()?;
self.w.flush()?;
drop(self);
Ok(())
}
}
impl<W: Write> Drop for Writer<W> {
fn drop(&mut self) {
if !self.iend_written {
let _ = self.write_iend();
}
}
}
enum ChunkOutput<'a, W: Write> {
Borrowed(&'a mut Writer<W>),
Owned(Writer<W>),
}
impl<'a, W: Write> Deref for ChunkOutput<'a, W> {
type Target = Writer<W>;
fn deref(&self) -> &Self::Target {
match self {
ChunkOutput::Borrowed(writer) => writer,
ChunkOutput::Owned(writer) => writer,
}
}
}
impl<'a, W: Write> DerefMut for ChunkOutput<'a, W> {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
ChunkOutput::Borrowed(writer) => writer,
ChunkOutput::Owned(writer) => writer,
}
}
}
struct ChunkWriter<'a, W: Write> {
writer: ChunkOutput<'a, W>,
buffer: Vec<u8>,
index: usize,
curr_chunk: ChunkType,
}
impl<'a, W: Write> ChunkWriter<'a, W> {
fn new(writer: ChunkOutput<'a, W>, buf_len: usize) -> ChunkWriter<'a, W> {
const CAP: usize = std::u32::MAX as usize >> 1;
let curr_chunk;
if writer.images_written == 0 {
curr_chunk = chunk::IDAT;
} else {
curr_chunk = chunk::fdAT;
}
ChunkWriter {
writer,
buffer: vec![0; CAP.min(buf_len)],
index: 0,
curr_chunk,
}
}
fn next_frame_info(&self) -> (usize, usize) {
let wrt = self.writer.deref();
let width: usize;
let height: usize;
if let Some(fctl) = wrt.info.frame_control {
width = fctl.width as usize;
height = fctl.height as usize;
} else {
width = wrt.info.width as usize;
height = wrt.info.height as usize;
}
let in_len = wrt.info.raw_row_length_from_width(width as u32) - 1;
let data_size = in_len * height;
(in_len, data_size)
}
fn write_header(&mut self) -> Result<()> {
assert_eq!(self.index, 0, "Called when not flushed");
let wrt = self.writer.deref_mut();
self.curr_chunk = if wrt.images_written == 0 {
chunk::IDAT
} else {
chunk::fdAT
};
match wrt.info.frame_control {
Some(_) if wrt.should_skip_frame_control_on_default_image() => {}
Some(ref mut fctl) => {
fctl.encode(&mut wrt.w)?;
fctl.sequence_number += 1;
}
_ => {}
}
Ok(())
}
fn set_fctl(&mut self, f: FrameControl) {
if let Some(ref mut fctl) = self.writer.info.frame_control {
*fctl = FrameControl {
sequence_number: fctl.sequence_number,
..f
};
} else {
panic!("This function must be called on an animated PNG")
}
}
fn flush_inner(&mut self) -> io::Result<()> {
if self.index > 0 {
write_chunk(
&mut self.writer.w,
self.curr_chunk,
&self.buffer[..self.index],
)?;
self.index = 0;
}
Ok(())
}
}
impl<'a, W: Write> Write for ChunkWriter<'a, W> {
fn write(&mut self, mut data: &[u8]) -> io::Result<usize> {
if data.is_empty() {
return Ok(0);
}
if self.index == 0 {
let wrt = self.writer.deref_mut();
let no_fctl = wrt.should_skip_frame_control_on_default_image();
if wrt.info.frame_control.is_some() && !no_fctl {
let fctl = wrt.info.frame_control.as_mut().unwrap();
self.buffer[0..4].copy_from_slice(&fctl.sequence_number.to_be_bytes());
fctl.sequence_number += 1;
self.index = 4;
}
}
let written = data.len().min(self.buffer.len() - self.index);
data = &data[..written];
self.buffer[self.index..][..written].copy_from_slice(data);
self.index += written;
if self.index == self.buffer.len() {
self.flush_inner()?;
}
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
self.flush_inner()
}
}
impl<W: Write> Drop for ChunkWriter<'_, W> {
fn drop(&mut self) {
let _ = self.flush();
}
}
enum Wrapper<'a, W: Write> {
Chunk(ChunkWriter<'a, W>),
Zlib(ZlibEncoder<ChunkWriter<'a, W>>),
Unrecoverable,
None,
}
impl<'a, W: Write> Wrapper<'a, W> {
fn take(&mut self) -> Wrapper<'a, W> {
let mut swap = Wrapper::None;
mem::swap(self, &mut swap);
swap
}
}
pub struct StreamWriter<'a, W: Write> {
writer: Wrapper<'a, W>,
prev_buf: Vec<u8>,
curr_buf: Vec<u8>,
index: usize,
line_len: usize,
to_write: usize,
width: u32,
height: u32,
bpp: BytesPerPixel,
filter: FilterType,
adaptive_filter: AdaptiveFilterType,
fctl: Option<FrameControl>,
compression: Compression,
}
impl<'a, W: Write> StreamWriter<'a, W> {
fn new(writer: ChunkOutput<'a, W>, buf_len: usize) -> Result<StreamWriter<'a, W>> {
let PartialInfo {
width,
height,
frame_control: fctl,
compression,
..
} = writer.info;
let bpp = writer.info.bpp_in_prediction();
let in_len = writer.info.raw_row_length() - 1;
let filter = writer.options.filter;
let adaptive_filter = writer.options.adaptive_filter;
let prev_buf = vec![0; in_len];
let curr_buf = vec![0; in_len];
let mut chunk_writer = ChunkWriter::new(writer, buf_len);
let (line_len, to_write) = chunk_writer.next_frame_info();
chunk_writer.write_header()?;
let zlib = ZlibEncoder::new(chunk_writer, compression.to_options());
Ok(StreamWriter {
writer: Wrapper::Zlib(zlib),
index: 0,
prev_buf,
curr_buf,
bpp,
filter,
width,
height,
adaptive_filter,
line_len,
to_write,
fctl,
compression,
})
}
pub fn set_filter(&mut self, filter: FilterType) {
self.filter = filter;
}
pub fn set_adaptive_filter(&mut self, adaptive_filter: AdaptiveFilterType) {
self.adaptive_filter = adaptive_filter;
}
pub fn set_frame_delay(&mut self, numerator: u16, denominator: u16) -> Result<()> {
if let Some(ref mut fctl) = self.fctl {
fctl.delay_den = denominator;
fctl.delay_num = numerator;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_frame_dimension(&mut self, width: u32, height: u32) -> Result<()> {
if let Some(ref mut fctl) = self.fctl {
if Some(width) > self.width.checked_sub(fctl.x_offset)
|| Some(height) > self.height.checked_sub(fctl.y_offset)
{
return Err(EncodingError::Format(FormatErrorKind::OutOfBounds.into()));
} else if width == 0 {
return Err(EncodingError::Format(FormatErrorKind::ZeroWidth.into()));
} else if height == 0 {
return Err(EncodingError::Format(FormatErrorKind::ZeroHeight.into()));
}
fctl.width = width;
fctl.height = height;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_frame_position(&mut self, x: u32, y: u32) -> Result<()> {
if let Some(ref mut fctl) = self.fctl {
if Some(x) > self.width.checked_sub(fctl.width)
|| Some(y) > self.height.checked_sub(fctl.height)
{
return Err(EncodingError::Format(FormatErrorKind::OutOfBounds.into()));
}
fctl.x_offset = x;
fctl.y_offset = y;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn reset_frame_dimension(&mut self) -> Result<()> {
if let Some(ref mut fctl) = self.fctl {
fctl.width = self.width - fctl.x_offset;
fctl.height = self.height - fctl.y_offset;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn reset_frame_position(&mut self) -> Result<()> {
if let Some(ref mut fctl) = self.fctl {
fctl.x_offset = 0;
fctl.y_offset = 0;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_blend_op(&mut self, op: BlendOp) -> Result<()> {
if let Some(ref mut fctl) = self.fctl {
fctl.blend_op = op;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn set_dispose_op(&mut self, op: DisposeOp) -> Result<()> {
if let Some(ref mut fctl) = self.fctl {
fctl.dispose_op = op;
Ok(())
} else {
Err(EncodingError::Format(FormatErrorKind::NotAnimated.into()))
}
}
pub fn finish(mut self) -> Result<()> {
if self.to_write > 0 {
let err = FormatErrorKind::MissingData(self.to_write).into();
return Err(EncodingError::Format(err));
}
self.flush()?;
if let Wrapper::Chunk(wrt) = self.writer.take() {
wrt.writer.validate_sequence_done()?;
}
Ok(())
}
fn new_frame(&mut self) -> Result<()> {
let wrt = match &mut self.writer {
Wrapper::Chunk(wrt) => wrt,
Wrapper::Unrecoverable => {
let err = FormatErrorKind::Unrecoverable.into();
return Err(EncodingError::Format(err));
}
Wrapper::Zlib(_) => unreachable!("never called on a half-finished frame"),
Wrapper::None => unreachable!(),
};
wrt.flush()?;
wrt.writer.validate_new_image()?;
if let Some(fctl) = self.fctl {
wrt.set_fctl(fctl);
}
let (scansize, size) = wrt.next_frame_info();
self.line_len = scansize;
self.to_write = size;
wrt.write_header()?;
wrt.writer.increment_images_written();
match self.writer.take() {
Wrapper::Chunk(wrt) => {
let encoder = ZlibEncoder::new(wrt, self.compression.to_options());
self.writer = Wrapper::Zlib(encoder);
}
_ => unreachable!(),
};
Ok(())
}
}
impl<'a, W: Write> Write for StreamWriter<'a, W> {
fn write(&mut self, mut data: &[u8]) -> io::Result<usize> {
if let Wrapper::Unrecoverable = self.writer {
let err = FormatErrorKind::Unrecoverable.into();
return Err(EncodingError::Format(err).into());
}
if data.is_empty() {
return Ok(0);
}
if self.to_write == 0 {
match self.writer.take() {
Wrapper::Zlib(wrt) => match wrt.finish() {
Ok(chunk) => self.writer = Wrapper::Chunk(chunk),
Err(err) => {
self.writer = Wrapper::Unrecoverable;
return Err(err);
}
},
chunk @ Wrapper::Chunk(_) => self.writer = chunk,
Wrapper::Unrecoverable => unreachable!(),
Wrapper::None => unreachable!(),
};
self.new_frame()?;
}
let written = data.read(&mut self.curr_buf[..self.line_len][self.index..])?;
self.index += written;
self.to_write -= written;
if self.index == self.line_len {
let filter_type = filter(
self.filter,
self.adaptive_filter,
self.bpp,
&self.prev_buf,
&mut self.curr_buf,
);
let wrt = match &mut self.writer {
Wrapper::Zlib(wrt) => wrt,
_ => unreachable!(),
};
wrt.write_all(&[filter_type as u8])?;
wrt.write_all(&self.curr_buf)?;
mem::swap(&mut self.prev_buf, &mut self.curr_buf);
self.index = 0;
}
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
match &mut self.writer {
Wrapper::Zlib(wrt) => wrt.flush()?,
Wrapper::Chunk(wrt) => wrt.flush()?,
Wrapper::Unrecoverable | Wrapper::None => {
let err = FormatErrorKind::Unrecoverable.into();
return Err(EncodingError::Format(err).into());
}
}
if self.index > 0 {
let err = FormatErrorKind::WrittenTooMuch(self.index).into();
return Err(EncodingError::Format(err).into());
}
Ok(())
}
}
impl<W: Write> Drop for StreamWriter<'_, W> {
fn drop(&mut self) {
let _ = self.flush();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Decoder;
use rand::{thread_rng, Rng};
use std::fs::File;
use std::io::{Cursor, Write};
use std::{cmp, io};
#[test]
fn roundtrip() {
for _ in 0..10 {
for path in glob::glob("tests/pngsuite/*.png")
.unwrap()
.map(|r| r.unwrap())
{
if path.file_name().unwrap().to_str().unwrap().starts_with('x') {
continue;
}
eprintln!("{}", path.display());
let decoder = Decoder::new(File::open(path).unwrap());
let mut reader = decoder.read_info().unwrap();
let mut buf = vec![0; reader.output_buffer_size()];
let info = reader.next_frame(&mut buf).unwrap();
let mut out = Vec::new();
{
let mut wrapper = RandomChunkWriter {
rng: thread_rng(),
w: &mut out,
};
let mut encoder = Encoder::new(&mut wrapper, info.width, info.height);
encoder.set_color(info.color_type);
encoder.set_depth(info.bit_depth);
if let Some(palette) = &reader.info().palette {
encoder.set_palette(palette.clone());
}
let mut encoder = encoder.write_header().unwrap();
encoder.write_image_data(&buf).unwrap();
}
let decoder = Decoder::new(&*out);
let mut reader = decoder.read_info().unwrap();
let mut buf2 = vec![0; reader.output_buffer_size()];
reader.next_frame(&mut buf2).unwrap();
assert_eq!(buf, buf2);
}
}
}
#[test]
fn roundtrip_stream() {
for _ in 0..10 {
for path in glob::glob("tests/pngsuite/*.png")
.unwrap()
.map(|r| r.unwrap())
{
if path.file_name().unwrap().to_str().unwrap().starts_with('x') {
continue;
}
let decoder = Decoder::new(File::open(path).unwrap());
let mut reader = decoder.read_info().unwrap();
let mut buf = vec![0; reader.output_buffer_size()];
let info = reader.next_frame(&mut buf).unwrap();
let mut out = Vec::new();
{
let mut wrapper = RandomChunkWriter {
rng: thread_rng(),
w: &mut out,
};
let mut encoder = Encoder::new(&mut wrapper, info.width, info.height);
encoder.set_color(info.color_type);
encoder.set_depth(info.bit_depth);
if let Some(palette) = &reader.info().palette {
encoder.set_palette(palette.clone());
}
let mut encoder = encoder.write_header().unwrap();
let mut stream_writer = encoder.stream_writer().unwrap();
let mut outer_wrapper = RandomChunkWriter {
rng: thread_rng(),
w: &mut stream_writer,
};
outer_wrapper.write_all(&buf).unwrap();
}
let decoder = Decoder::new(&*out);
let mut reader = decoder.read_info().unwrap();
let mut buf2 = vec![0; reader.output_buffer_size()];
reader.next_frame(&mut buf2).unwrap();
assert_eq!(buf, buf2);
}
}
}
#[test]
fn image_palette() -> Result<()> {
for &bit_depth in &[1u8, 2, 4, 8] {
let path = format!("tests/pngsuite/basn3p0{}.png", bit_depth);
let decoder = Decoder::new(File::open(&path).unwrap());
let mut reader = decoder.read_info().unwrap();
let mut decoded_pixels = vec![0; reader.output_buffer_size()];
let info = reader.info();
assert_eq!(
info.width as usize * info.height as usize * usize::from(bit_depth),
decoded_pixels.len() * 8
);
let info = reader.next_frame(&mut decoded_pixels).unwrap();
let indexed_data = decoded_pixels;
let palette = reader.info().palette.as_ref().unwrap();
let mut out = Vec::new();
{
let mut encoder = Encoder::new(&mut out, info.width, info.height);
encoder.set_depth(BitDepth::from_u8(bit_depth).unwrap());
encoder.set_color(ColorType::Indexed);
encoder.set_palette(palette.as_ref());
let mut writer = encoder.write_header().unwrap();
writer.write_image_data(&indexed_data).unwrap();
}
let decoder = Decoder::new(&*out);
let mut reader = decoder.read_info().unwrap();
let mut redecoded = vec![0; reader.output_buffer_size()];
reader.next_frame(&mut redecoded).unwrap();
assert_eq!(indexed_data, redecoded);
}
Ok(())
}
#[test]
fn expect_error_on_wrong_image_len() -> Result<()> {
let width = 10;
let height = 10;
let output = vec![0u8; 1024];
let writer = Cursor::new(output);
let mut encoder = Encoder::new(writer, width as u32, height as u32);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Rgb);
let mut png_writer = encoder.write_header()?;
let correct_image_size = width * height * 3;
let image = vec![0u8; correct_image_size + 1];
let result = png_writer.write_image_data(image.as_ref());
assert!(result.is_err());
Ok(())
}
#[test]
fn expect_error_on_empty_image() -> Result<()> {
let output = vec![0u8; 1024];
let mut writer = Cursor::new(output);
let encoder = Encoder::new(&mut writer, 0, 0);
assert!(encoder.write_header().is_err());
let encoder = Encoder::new(&mut writer, 100, 0);
assert!(encoder.write_header().is_err());
let encoder = Encoder::new(&mut writer, 0, 100);
assert!(encoder.write_header().is_err());
Ok(())
}
#[test]
fn expect_error_on_invalid_bit_depth_color_type_combination() -> Result<()> {
let output = vec![0u8; 1024];
let mut writer = Cursor::new(output);
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::One);
encoder.set_color(ColorType::Rgb);
assert!(encoder.write_header().is_err());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::One);
encoder.set_color(ColorType::GrayscaleAlpha);
assert!(encoder.write_header().is_err());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::One);
encoder.set_color(ColorType::Rgba);
assert!(encoder.write_header().is_err());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Two);
encoder.set_color(ColorType::Rgb);
assert!(encoder.write_header().is_err());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Two);
encoder.set_color(ColorType::GrayscaleAlpha);
assert!(encoder.write_header().is_err());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Two);
encoder.set_color(ColorType::Rgba);
assert!(encoder.write_header().is_err());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Four);
encoder.set_color(ColorType::Rgb);
assert!(encoder.write_header().is_err());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Four);
encoder.set_color(ColorType::GrayscaleAlpha);
assert!(encoder.write_header().is_err());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Four);
encoder.set_color(ColorType::Rgba);
assert!(encoder.write_header().is_err());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Sixteen);
encoder.set_color(ColorType::Indexed);
assert!(encoder.write_header().is_err());
Ok(())
}
#[test]
fn can_write_header_with_valid_bit_depth_color_type_combination() -> Result<()> {
let output = vec![0u8; 1024];
let mut writer = Cursor::new(output);
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::One);
encoder.set_color(ColorType::Grayscale);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::One);
encoder.set_color(ColorType::Indexed);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Two);
encoder.set_color(ColorType::Grayscale);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Two);
encoder.set_color(ColorType::Indexed);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Four);
encoder.set_color(ColorType::Grayscale);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Four);
encoder.set_color(ColorType::Indexed);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Rgb);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Indexed);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::GrayscaleAlpha);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Rgba);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Sixteen);
encoder.set_color(ColorType::Grayscale);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Sixteen);
encoder.set_color(ColorType::Rgb);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Sixteen);
encoder.set_color(ColorType::GrayscaleAlpha);
assert!(encoder.write_header().is_ok());
let mut encoder = Encoder::new(&mut writer, 1, 1);
encoder.set_depth(BitDepth::Sixteen);
encoder.set_color(ColorType::Rgba);
assert!(encoder.write_header().is_ok());
Ok(())
}
#[test]
fn all_filters_roundtrip() -> io::Result<()> {
let pixel: Vec<_> = (0..48).collect();
let roundtrip = |filter: FilterType| -> io::Result<()> {
let mut buffer = vec![];
let mut encoder = Encoder::new(&mut buffer, 4, 4);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Rgb);
encoder.set_filter(filter);
encoder.write_header()?.write_image_data(&pixel)?;
let decoder = crate::Decoder::new(Cursor::new(buffer));
let mut reader = decoder.read_info()?;
let info = reader.info();
assert_eq!(info.width, 4);
assert_eq!(info.height, 4);
let mut dest = vec![0; pixel.len()];
reader.next_frame(&mut dest)?;
assert_eq!(dest, pixel, "Deviation with filter type {:?}", filter);
Ok(())
};
roundtrip(FilterType::NoFilter)?;
roundtrip(FilterType::Sub)?;
roundtrip(FilterType::Up)?;
roundtrip(FilterType::Avg)?;
roundtrip(FilterType::Paeth)?;
Ok(())
}
#[test]
fn some_gamma_roundtrip() -> io::Result<()> {
let pixel: Vec<_> = (0..48).collect();
let roundtrip = |gamma: Option<ScaledFloat>| -> io::Result<()> {
let mut buffer = vec![];
let mut encoder = Encoder::new(&mut buffer, 4, 4);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Rgb);
encoder.set_filter(FilterType::Avg);
if let Some(gamma) = gamma {
encoder.set_source_gamma(gamma);
}
encoder.write_header()?.write_image_data(&pixel)?;
let decoder = crate::Decoder::new(Cursor::new(buffer));
let mut reader = decoder.read_info()?;
assert_eq!(
reader.info().source_gamma,
gamma,
"Deviation with gamma {:?}",
gamma
);
let mut dest = vec![0; pixel.len()];
let info = reader.next_frame(&mut dest)?;
assert_eq!(info.width, 4);
assert_eq!(info.height, 4);
Ok(())
};
roundtrip(None)?;
roundtrip(Some(ScaledFloat::new(0.35)))?;
roundtrip(Some(ScaledFloat::new(0.45)))?;
roundtrip(Some(ScaledFloat::new(0.55)))?;
roundtrip(Some(ScaledFloat::new(0.7)))?;
roundtrip(Some(ScaledFloat::new(1.0)))?;
roundtrip(Some(ScaledFloat::new(2.5)))?;
Ok(())
}
#[test]
fn write_image_chunks_beyond_first() -> Result<()> {
let width = 10;
let height = 10;
let output = vec![0u8; 1024];
let writer = Cursor::new(output);
let mut encoder = Encoder::new(writer, width, height);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
let mut png_writer = encoder.write_header()?;
for _ in 0..3 {
let correct_image_size = (width * height) as usize;
let image = vec![0u8; correct_image_size];
png_writer.write_image_data(image.as_ref())?;
}
Ok(())
}
#[test]
fn image_validate_sequence_without_animation() -> Result<()> {
let width = 10;
let height = 10;
let output = vec![0u8; 1024];
let writer = Cursor::new(output);
let mut encoder = Encoder::new(writer, width, height);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
encoder.validate_sequence(true);
let mut png_writer = encoder.write_header()?;
let correct_image_size = (width * height) as usize;
let image = vec![0u8; correct_image_size];
png_writer.write_image_data(image.as_ref())?;
assert!(png_writer.write_image_data(image.as_ref()).is_err());
Ok(())
}
#[test]
fn image_validate_animation() -> Result<()> {
let width = 10;
let height = 10;
let output = vec![0u8; 1024];
let writer = Cursor::new(output);
let correct_image_size = (width * height) as usize;
let image = vec![0u8; correct_image_size];
let mut encoder = Encoder::new(writer, width, height);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
encoder.set_animated(1, 0)?;
encoder.validate_sequence(true);
let mut png_writer = encoder.write_header()?;
png_writer.write_image_data(image.as_ref())?;
Ok(())
}
#[test]
fn image_validate_animation2() -> Result<()> {
let width = 10;
let height = 10;
let output = vec![0u8; 1024];
let writer = Cursor::new(output);
let correct_image_size = (width * height) as usize;
let image = vec![0u8; correct_image_size];
let mut encoder = Encoder::new(writer, width, height);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
encoder.set_animated(2, 0)?;
encoder.validate_sequence(true);
let mut png_writer = encoder.write_header()?;
png_writer.write_image_data(image.as_ref())?;
png_writer.write_image_data(image.as_ref())?;
png_writer.finish()?;
Ok(())
}
#[test]
fn image_validate_animation_sep_def_image() -> Result<()> {
let width = 10;
let height = 10;
let output = vec![0u8; 1024];
let writer = Cursor::new(output);
let correct_image_size = (width * height) as usize;
let image = vec![0u8; correct_image_size];
let mut encoder = Encoder::new(writer, width, height);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
encoder.set_animated(1, 0)?;
encoder.set_sep_def_img(true)?;
encoder.validate_sequence(true);
let mut png_writer = encoder.write_header()?;
png_writer.write_image_data(image.as_ref())?;
png_writer.write_image_data(image.as_ref())?;
png_writer.finish()?;
Ok(())
}
#[test]
fn image_validate_missing_image() -> Result<()> {
let width = 10;
let height = 10;
let output = vec![0u8; 1024];
let writer = Cursor::new(output);
let mut encoder = Encoder::new(writer, width, height);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
encoder.validate_sequence(true);
let png_writer = encoder.write_header()?;
assert!(png_writer.finish().is_err());
Ok(())
}
#[test]
fn image_validate_missing_animated_frame() -> Result<()> {
let width = 10;
let height = 10;
let output = vec![0u8; 1024];
let writer = Cursor::new(output);
let correct_image_size = (width * height) as usize;
let image = vec![0u8; correct_image_size];
let mut encoder = Encoder::new(writer, width, height);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
encoder.set_animated(2, 0)?;
encoder.validate_sequence(true);
let mut png_writer = encoder.write_header()?;
png_writer.write_image_data(image.as_ref())?;
assert!(png_writer.finish().is_err());
Ok(())
}
#[test]
fn issue_307_stream_validation() -> Result<()> {
let output = vec![0u8; 1024];
let mut cursor = Cursor::new(output);
let encoder = Encoder::new(&mut cursor, 1, 1); let mut writer = encoder.write_header()?;
let mut stream = writer.stream_writer()?;
let written = stream.write(&[1, 2, 3, 4])?;
assert_eq!(written, 1);
stream.finish()?;
drop(writer);
{
cursor.set_position(0);
let mut decoder = Decoder::new(cursor).read_info().expect("A valid image");
let mut buffer = [0u8; 1];
decoder.next_frame(&mut buffer[..]).expect("Valid read");
assert_eq!(buffer, [1]);
}
Ok(())
}
#[test]
#[cfg(all(unix, not(target_pointer_width = "32")))]
fn exper_error_on_huge_chunk() -> Result<()> {
let empty = vec![0; 1usize << 31];
let writer = Cursor::new(vec![0u8; 1024]);
let mut encoder = Encoder::new(writer, 10, 10);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
let mut png_writer = encoder.write_header()?;
assert!(png_writer.write_chunk(chunk::fdAT, &empty).is_err());
Ok(())
}
#[test]
#[cfg(all(unix, not(target_pointer_width = "32")))]
fn exper_error_on_non_u32_chunk() -> Result<()> {
let empty = vec![0; 1usize << 32];
let writer = Cursor::new(vec![0u8; 1024]);
let mut encoder = Encoder::new(writer, 10, 10);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
let mut png_writer = encoder.write_header()?;
assert!(png_writer.write_chunk(chunk::fdAT, &empty).is_err());
Ok(())
}
#[test]
fn finish_drops_inner_writer() -> Result<()> {
struct NoWriter<'flag>(&'flag mut bool);
impl Write for NoWriter<'_> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Drop for NoWriter<'_> {
fn drop(&mut self) {
*self.0 = true;
}
}
let mut flag = false;
{
let mut encoder = Encoder::new(NoWriter(&mut flag), 10, 10);
encoder.set_depth(BitDepth::Eight);
encoder.set_color(ColorType::Grayscale);
let mut writer = encoder.write_header()?;
writer.write_image_data(&vec![0; 100])?;
writer.finish()?;
}
assert!(flag, "PNG finished but writer was not dropped");
Ok(())
}
struct RandomChunkWriter<R: Rng, W: Write> {
rng: R,
w: W,
}
impl<R: Rng, W: Write> Write for RandomChunkWriter<R, W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let len = cmp::min(self.rng.gen_range(1..50), buf.len());
self.w.write(&buf[0..len])
}
fn flush(&mut self) -> io::Result<()> {
self.w.flush()
}
}
}
impl Compression {
fn to_options(self) -> deflate::CompressionOptions {
match self {
Compression::Default => deflate::CompressionOptions::default(),
Compression::Fast => deflate::CompressionOptions::fast(),
Compression::Best => deflate::CompressionOptions::high(),
Compression::Huffman => deflate::CompressionOptions::huffman_only(),
Compression::Rle => deflate::CompressionOptions::rle(),
}
}
}