From 2b43dddb90da0bc4aa033fa14d191e8f1bfceed2 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Wed, 27 May 2020 18:18:22 -0400 Subject: [PATCH] Update libspng to master and expose RawContext for now --- spng-sys/build.rs | 3 + spng-sys/libspng | 2 +- spng-sys/src/ffi.rs | 12 ++ spng/src/lib.rs | 465 ++++++++++++++++++++++++++++++++++++-------- spng/tests/tests.rs | 101 ++++++++-- 5 files changed, 490 insertions(+), 93 deletions(-) diff --git a/spng-sys/build.rs b/spng-sys/build.rs index 8f7162a..d7cc4b6 100644 --- a/spng-sys/build.rs +++ b/spng-sys/build.rs @@ -6,6 +6,9 @@ fn main() { if let Some(libz_include) = env::var_os("DEP_Z_INCLUDE") { build.include(libz_include); } + if cfg!(target_feature = "ssse3") { + build.define("SPNG_SSE", Some("3")); + } build.compile("spng"); // DEP_SPNG_INCLUDE diff --git a/spng-sys/libspng b/spng-sys/libspng index 2079ef6..1d7cd08 160000 --- a/spng-sys/libspng +++ b/spng-sys/libspng @@ -1 +1 @@ -Subproject commit 2079ef6f223feea2570b537c047c9140a5b72551 +Subproject commit 1d7cd0806b17de4a8f53f8117d24d48dfb04178f diff --git a/spng-sys/src/ffi.rs b/spng-sys/src/ffi.rs index 951ecc7..8e64764 100644 --- a/spng-sys/src/ffi.rs +++ b/spng-sys/src/ffi.rs @@ -122,8 +122,20 @@ pub const spng_color_type_SPNG_COLOR_TYPE_INDEXED: spng_color_type = 3; pub const spng_color_type_SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: spng_color_type = 4; pub const spng_color_type_SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: spng_color_type = 6; pub type spng_color_type = i32; +pub const spng_filter_SPNG_FILTER_NONE: spng_filter = 0; +pub const spng_filter_SPNG_FILTER_SUB: spng_filter = 1; +pub const spng_filter_SPNG_FILTER_UP: spng_filter = 2; +pub const spng_filter_SPNG_FILTER_AVERAGE: spng_filter = 3; +pub const spng_filter_SPNG_FILTER_PAETH: spng_filter = 4; +pub type spng_filter = i32; +pub const spng_interlace_method_SPNG_INTERLACE_NONE: spng_interlace_method = 0; +pub const spng_interlace_method_SPNG_INTERLACE_ADAM7: spng_interlace_method = 1; +pub type spng_interlace_method = i32; pub const spng_format_SPNG_FMT_RGBA8: spng_format = 1; pub const spng_format_SPNG_FMT_RGBA16: spng_format = 2; +pub const spng_format_SPNG_FMT_RGB8: spng_format = 4; +pub const spng_format_SPNG_FMT_PNG: spng_format = 16; +pub const spng_format_SPNG_FMT_RAW: spng_format = 32; pub type spng_format = i32; pub const spng_ctx_flags_SPNG_CTX_IGNORE_ADLER32: spng_ctx_flags = 1; pub type spng_ctx_flags = i32; diff --git a/spng/src/lib.rs b/spng/src/lib.rs index c29c2d7..443891c 100644 --- a/spng/src/lib.rs +++ b/spng/src/lib.rs @@ -12,7 +12,7 @@ //! let output_buffer_size = reader.output_buffer_size(); //! assert_eq!(300, out_info.width); //! assert_eq!(300, out_info.height); -//! assert_eq!(8, out_info.bit_depth); +//! assert_eq!(8, out_info.bit_depth as u8); //! assert_eq!(4, out_info.color_type.samples()); //! assert_eq!(out_info.buffer_size, output_buffer_size); //! let mut out = vec![0; output_buffer_size]; @@ -35,26 +35,30 @@ pub use error::Error; pub enum Format { Rgba8 = sys::spng_format_SPNG_FMT_RGBA8, Rgba16 = sys::spng_format_SPNG_FMT_RGBA16, + Rgb8 = sys::spng_format_SPNG_FMT_RGB8, + /// The PNG's format in host-endian + Png = sys::spng_format_SPNG_FMT_PNG, + /// The PNG's format in big-endian + Raw = sys::spng_format_SPNG_FMT_RAW, } -#[repr(i32)] +#[repr(u8)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ColorType { - Grayscale = sys::spng_color_type_SPNG_COLOR_TYPE_GRAYSCALE, + Grayscale = sys::spng_color_type_SPNG_COLOR_TYPE_GRAYSCALE as u8, /// RGB - Truecolor = sys::spng_color_type_SPNG_COLOR_TYPE_TRUECOLOR, - Indexed = sys::spng_color_type_SPNG_COLOR_TYPE_INDEXED, - GrayscaleAlpha = sys::spng_color_type_SPNG_COLOR_TYPE_GRAYSCALE_ALPHA, + Truecolor = sys::spng_color_type_SPNG_COLOR_TYPE_TRUECOLOR as u8, + Indexed = sys::spng_color_type_SPNG_COLOR_TYPE_INDEXED as u8, + GrayscaleAlpha = sys::spng_color_type_SPNG_COLOR_TYPE_GRAYSCALE_ALPHA as u8, /// RGBA - TruecolorAlpha = sys::spng_color_type_SPNG_COLOR_TYPE_TRUECOLOR_ALPHA, + TruecolorAlpha = sys::spng_color_type_SPNG_COLOR_TYPE_TRUECOLOR_ALPHA as u8, } impl TryFrom for ColorType { type Error = Error; - fn try_from(c: u8) -> Result { + fn try_from(value: u8) -> Result { use ColorType::*; - let c = c as i32; - match c { + match value as i32 { sys::spng_color_type_SPNG_COLOR_TYPE_GRAYSCALE => Ok(Grayscale), sys::spng_color_type_SPNG_COLOR_TYPE_TRUECOLOR => Ok(Truecolor), sys::spng_color_type_SPNG_COLOR_TYPE_INDEXED => Ok(Indexed), @@ -78,6 +82,31 @@ impl ColorType { } } +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum BitDepth { + One = 1, + Two = 2, + Four = 4, + Eight = 8, + Sixteen = 16, +} + +impl TryFrom for BitDepth { + type Error = Error; + fn try_from(value: u8) -> Result { + use BitDepth::*; + match value as i32 { + 1 => Ok(One), + 2 => Ok(Two), + 4 => Ok(Four), + 8 => Ok(Eight), + 16 => Ok(Sixteen), + _ => Err(Error::BitDepth), + } + } +} + bitflags::bitflags! { /// Decoding flags pub struct DecodeFlags: i32 { @@ -92,6 +121,13 @@ bitflags::bitflags! { } } +bitflags::bitflags! { + pub struct ContextFlags: i32 { + /// Ignore checksum in `DEFLATE` streams + const IGNORE_ADLER32 = sys::spng_ctx_flags_SPNG_CTX_IGNORE_ADLER32; + } +} + /// Decoding limits #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Limits { @@ -101,11 +137,13 @@ pub struct Limits { pub max_height: u32, } +const PNG_U32_MAX: u32 = std::u32::MAX / 2 - 1; + impl Default for Limits { fn default() -> Limits { Limits { - max_width: std::u32::MAX / 2 - 1, - max_height: std::u32::MAX / 2 - 1, + max_width: PNG_U32_MAX, + max_height: PNG_U32_MAX, } } } @@ -115,8 +153,9 @@ impl Default for Limits { pub struct Decoder { reader: R, limits: Limits, + context_flags: ContextFlags, decode_flags: DecodeFlags, - output_format: Option, + output_format: Format, } /// Decoded output image information @@ -128,12 +167,19 @@ pub struct OutputInfo { pub height: u32, /// The color channels pub color_type: ColorType, - /// The per component bit depth - pub bit_depth: u8, + /// The per-component bit depth + pub bit_depth: BitDepth, /// The minimum buffer size required for the decoded pixel output pub buffer_size: usize, } +impl OutputInfo { + /// The width of each row or scanline + pub fn line_size(&self) -> usize { + self.buffer_size / self.height as usize + } +} + /// PNG image information #[derive(Debug)] pub struct Info { @@ -143,14 +189,50 @@ pub struct Info { pub height: u32, /// The color channels pub color_type: ColorType, - /// The per component bit depth - pub bit_depth: u8, + /// The per-component bit depth + pub bit_depth: BitDepth, +} + +impl Info { + fn from_header(header: &sys::spng_ihdr) -> Result { + Ok(Info { + width: header.width, + height: header.height, + bit_depth: BitDepth::try_from(header.bit_depth)?, + color_type: ColorType::try_from(header.color_type)?, + }) + } + + fn output_info( + &self, + output_format: Format, + output_buffer_size: usize, + ) -> Result { + let bit_depth = match output_format { + Format::Png | Format::Raw => self.bit_depth, + Format::Rgb8 | Format::Rgba8 => BitDepth::Eight, + Format::Rgba16 => BitDepth::Sixteen, + }; + let color_type = match output_format { + Format::Png | Format::Raw => self.color_type, + Format::Rgb8 => ColorType::Truecolor, + Format::Rgba8 => ColorType::TruecolorAlpha, + Format::Rgba16 => ColorType::TruecolorAlpha, + }; + Ok(OutputInfo { + bit_depth, + color_type, + width: self.width, + height: self.height, + buffer_size: output_buffer_size, + }) + } } #[derive(Debug)] /// PNG reader pub struct Reader { - ctx: Context, + ctx: RawContext, out_format: Format, info: Info, decode_flags: DecodeFlags, @@ -178,13 +260,16 @@ unsafe extern "C" fn read_fn( sys::spng_errno_SPNG_OK } +/// The raw decoding context. +/// +/// #[derive(Debug)] -struct Context { +pub struct RawContext { raw: *mut sys::spng_ctx, reader: Option>, } -impl Drop for Context { +impl Drop for RawContext { fn drop(&mut self) { if !self.raw.is_null() { unsafe { @@ -194,19 +279,19 @@ impl Drop for Context { } } -impl Context { - fn new(flags: i32) -> Result, Error> { +impl RawContext { + pub fn new(flags: ContextFlags) -> Result, Error> { unsafe { - let raw = sys::spng_ctx_new(flags); + let raw = sys::spng_ctx_new(flags.bits()); if raw.is_null() { Err(Error::Mem) } else { - Ok(Context { raw, reader: None }) + Ok(RawContext { raw, reader: None }) } } } - fn decoded_image_size(&self, out_format: Format) -> Result { + pub fn decoded_image_size(&self, out_format: Format) -> Result { let mut len = 0; unsafe { check_err(sys::spng_decoded_image_size( @@ -218,30 +303,179 @@ impl Context { Ok(len) } - fn set_image_limits(&mut self, max_width: u32, max_height: u32) -> Result<(), Error> { + pub fn set_image_limits(&mut self, max_width: u32, max_height: u32) -> Result<(), Error> { unsafe { check_err(sys::spng_set_image_limits(self.raw, max_width, max_height)) } } - fn set_png_stream(&mut self, reader: R) -> Result<(), Error> - where - R: io::Read, - { - let mut boxed = Box::new(reader); - let user = boxed.as_mut() as *mut R as *mut _; - self.reader = Some(boxed); - let read_fn: sys::spng_read_fn = Some(read_fn::); - unsafe { check_err(sys::spng_set_png_stream(self.raw, read_fn, user)) } + /// Returns the image limits: `(width, height)` + pub fn get_image_limits(&self) -> Result<(u32, u32), Error> { + let mut width = 0; + let mut height = 0; + unsafe { + check_err(sys::spng_get_image_limits( + self.raw, + &mut width, + &mut height, + ))?; + Ok((width, height)) + } + } + + pub fn get_ihdr(&self) -> Result { + unsafe { + let mut ihdr = mem::zeroed(); + check_err(sys::spng_get_ihdr(self.raw, &mut ihdr))?; + Ok(ihdr) + } + } + + pub fn get_plte(&self) -> Result { + unsafe { + let mut plte = mem::zeroed(); + check_err(sys::spng_get_plte(self.raw, &mut plte))?; + Ok(plte) + } + } + + pub fn get_trns(&self) -> Result { + unsafe { + let mut trns = mem::zeroed(); + check_err(sys::spng_get_trns(self.raw, &mut trns))?; + Ok(trns) + } + } + + pub fn get_chrm(&self) -> Result { + unsafe { + let mut chrm = mem::zeroed(); + check_err(sys::spng_get_chrm(self.raw, &mut chrm))?; + Ok(chrm) + } + } + + pub fn get_chrm_int(&self) -> Result { + unsafe { + let mut spng_chrm_int = mem::zeroed(); + check_err(sys::spng_get_chrm_int(self.raw, &mut spng_chrm_int))?; + Ok(spng_chrm_int) + } + } + + pub fn get_gama(&self) -> Result { + unsafe { + let mut gama = mem::zeroed(); + check_err(sys::spng_get_gama(self.raw, &mut gama))?; + Ok(gama) + } } - fn get_ihdr(&self) -> Result { + pub fn get_iccp(&self) -> Result { unsafe { - let mut header = mem::zeroed(); - check_err(sys::spng_get_ihdr(self.raw, &mut header))?; - Ok(header) + let mut iccp = mem::zeroed(); + check_err(sys::spng_get_iccp(self.raw, &mut iccp))?; + Ok(iccp) } } - fn decode_image( + pub fn get_sbit(&self) -> Result { + unsafe { + let mut sbit = mem::zeroed(); + check_err(sys::spng_get_sbit(self.raw, &mut sbit))?; + Ok(sbit) + } + } + + /// Returns the `sRGB` rendering intent or `Err(Chunkavil)` for non-`sRGB` images. + pub fn get_srgb(&self) -> Result { + unsafe { + let mut rendering_intent = mem::zeroed(); + check_err(sys::spng_get_srgb(self.raw, &mut rendering_intent))?; + Ok(rendering_intent) + } + } + + /// Returns text information + /// + /// Note that the referenced text data pointers are freed when the context is dropped. + pub fn get_text(&self) -> Result, Error> { + unsafe { + use std::ptr; + let mut len = 0; + check_err(sys::spng_get_text(self.raw, ptr::null_mut(), &mut len))?; + let mut text = vec![mem::zeroed::(); len as usize]; + check_err(sys::spng_get_text(self.raw, text.as_mut_ptr(), &mut len))?; + Ok(text) + } + } + + pub fn get_bkgd(&self) -> Result { + unsafe { + let mut bkgd = mem::zeroed(); + check_err(sys::spng_get_bkgd(self.raw, &mut bkgd))?; + Ok(bkgd) + } + } + + /// Return the physical pixel dimensions + pub fn get_phys(&self) -> Result { + unsafe { + let mut phys = mem::zeroed(); + check_err(sys::spng_get_phys(self.raw, &mut phys))?; + Ok(phys) + } + } + + /// Returns suggested palettes + /// + /// Note that the referenced suggested palette pointers are freed when the context is dropped. + pub fn get_splt(&self) -> Result, Error> { + unsafe { + use std::ptr; + let mut len = 0; + check_err(sys::spng_get_splt(self.raw, ptr::null_mut(), &mut len))?; + let mut splt = vec![mem::zeroed::(); len as usize]; + check_err(sys::spng_get_splt(self.raw, splt.as_mut_ptr(), &mut len))?; + Ok(splt) + } + } + + pub fn get_time(&self) -> Result { + unsafe { + let mut time = mem::zeroed(); + check_err(sys::spng_get_time(self.raw, &mut time))?; + Ok(time) + } + } + + /// Return the image offset + pub fn get_offs(&self) -> Result { + unsafe { + let mut offs = mem::zeroed(); + check_err(sys::spng_get_offs(self.raw, &mut offs))?; + Ok(offs) + } + } + + /// Return `EXIF` data. + /// + /// Note that `exif.data` is freed when the context is destroyed. + pub fn get_exif(&self) -> Result { + unsafe { + let mut exif = mem::zeroed(); + check_err(sys::spng_get_exif(self.raw, &mut exif))?; + Ok(exif) + } + } + + pub fn get_row_info(&self) -> Result { + unsafe { + let mut row_info = mem::zeroed(); + check_err(sys::spng_get_row_info(self.raw, &mut row_info))?; + Ok(row_info) + } + } + + pub fn decode_image( &mut self, output: &mut [u8], out_format: Format, @@ -257,25 +491,88 @@ impl Context { )) } } + + pub fn decode_row(&mut self, output: &mut [u8]) -> Result<(), Error> { + unsafe { + check_err(sys::spng_decode_row( + self.raw, + output.as_mut_ptr() as _, + output.len(), + )) + } + } + + pub fn decode_scanline(&mut self, output: &mut [u8]) -> Result<(), Error> { + unsafe { + check_err(sys::spng_decode_scanline( + self.raw, + output.as_mut_ptr() as _, + output.len(), + )) + } + } } -impl Decoder { - /// Create a new `png` decoder with the default limits - pub fn new(r: R) -> Decoder { - Decoder::with_limits(r, Limits::default()) +impl RawContext { + /// Set the input `png` stream reader. The input buffer or stream may only be set once per context. + pub fn set_png_stream(&mut self, reader: R) -> Result<(), Error> { + let mut boxed = Box::new(reader); + let user = boxed.as_mut() as *mut R as *mut _; + self.reader = Some(boxed); + let read_fn: sys::spng_read_fn = Some(read_fn::); + unsafe { check_err(sys::spng_set_png_stream(self.raw, read_fn, user)) } } +} - /// Create a new `png` decoder with the given limits - pub fn with_limits(r: R, limits: Limits) -> Decoder { +impl<'a> RawContext<&'a [u8]> { + /// Set the input `png` buffer. The input buffer or stream may only be set once per context. + pub fn set_png_buffer(&mut self, buf: &'a [u8]) -> Result<(), Error> { + unsafe { + check_err(sys::spng_set_png_buffer( + self.raw, + buf.as_ptr() as *const _, + buf.len(), + )) + } + } +} + +impl Decoder { + /// Create a new `png` decoder with the default limits + pub fn new(reader: R) -> Decoder { let decode_flags = DecodeFlags::empty(); + let context_flags = ContextFlags::empty(); + let output_format = Format::Png; + let limits = Limits::default(); Decoder { - reader: r, + reader, limits, decode_flags, - output_format: None, + context_flags, + output_format, } } + pub fn with_limits(mut self, limits: Limits) -> Decoder { + self.limits = limits; + self + } + + pub fn with_context_flags(mut self, context_flags: ContextFlags) -> Decoder { + self.context_flags = context_flags; + self + } + + pub fn with_decode_flags(mut self, decode_flags: DecodeFlags) -> Decoder { + self.decode_flags = decode_flags; + self + } + + pub fn with_output_format(mut self, output_format: Format) -> Decoder { + self.output_format = output_format; + self + } + /// Set the limits pub fn set_limits(&mut self, limits: Limits) { self.limits = limits; @@ -287,48 +584,58 @@ impl Decoder { } /// Set the output image format - pub fn set_output_format(&mut self, format: Format) { - self.output_format = Some(format); + pub fn set_output_format(&mut self, output_format: Format) { + self.output_format = output_format; + } + + pub fn set_context_flags(&mut self, context_flags: ContextFlags) { + self.context_flags = context_flags; } /// Read the `png` header and initialize decoding. - pub fn read_info(self) -> Result<(OutputInfo, Reader), Error> { - let mut ctx = Context::new(0)?; + pub fn read_info(self) -> Result<(OutputInfo, Reader), Error> + where + R: io::Read, + { + let mut ctx = RawContext::new(self.context_flags)?; ctx.set_image_limits(self.limits.max_width, self.limits.max_height)?; ctx.set_png_stream(self.reader)?; let header = ctx.get_ihdr()?; - - let info = Info { - bit_depth: header.bit_depth, - color_type: ColorType::try_from(header.color_type)?, - width: header.width, - height: header.height, - }; - let out_format = - self.output_format - .unwrap_or_else(|| match (info.bit_depth, info.color_type) { - (16, _) => Format::Rgba16, - (_, _) => Format::Rgba8, - }); - let buffer_size = ctx.decoded_image_size(out_format)?; - - let (out_bit_depth, out_color_type) = match out_format { - Format::Rgba8 => (8, ColorType::TruecolorAlpha), - Format::Rgba16 => (16, ColorType::TruecolorAlpha), - }; - let out_info = OutputInfo { - width: info.width, - height: info.height, - bit_depth: out_bit_depth, - color_type: out_color_type, - buffer_size, + let output_buffer_size = ctx.decoded_image_size(self.output_format)?; + let info = Info::from_header(&header)?; + let out_info = info.output_info(self.output_format, output_buffer_size)?; + let reader = Reader { + ctx, + out_format: self.output_format, + info, + decode_flags: self.decode_flags, + output_buffer_size, }; + + Ok((out_info, reader)) + } +} + +impl<'a> Decoder<&'a [u8]> { + /// Read the `png` header and initialize decoding. + /// + /// Like [`read_info`] but prevents extra copies when the `png` data is already in memory. + /// + /// [`read_info`]: method@Decoder::read_info + pub fn read_info_from_slice(self) -> Result<(OutputInfo, Reader<&'a [u8]>), Error> { + let mut ctx = RawContext::new(self.context_flags)?; + ctx.set_image_limits(self.limits.max_width, self.limits.max_height)?; + ctx.set_png_buffer(self.reader)?; + let header = ctx.get_ihdr()?; + let output_buffer_size = ctx.decoded_image_size(self.output_format)?; + let info = Info::from_header(&header)?; + let out_info = info.output_info(self.output_format, output_buffer_size)?; let reader = Reader { ctx, - out_format, + out_format: self.output_format, info, decode_flags: self.decode_flags, - output_buffer_size: buffer_size, + output_buffer_size, }; Ok((out_info, reader)) diff --git a/spng/tests/tests.rs b/spng/tests/tests.rs index df2663b..852625f 100644 --- a/spng/tests/tests.rs +++ b/spng/tests/tests.rs @@ -1,17 +1,36 @@ use spng::Decoder; use std::io::Cursor; +static TEST_PNG_001: &[u8] = include_bytes!("test-001.png"); +static TEST_PNG_002: &[u8] = include_bytes!("test-002.png"); + +#[test] +fn decode_001_cursor() -> Result<(), Box> { + let cursor = Cursor::new(TEST_PNG_001); + let decoder = Decoder::new(cursor); + let (out_info, mut reader) = decoder.read_info()?; + let output_buffer_size = reader.output_buffer_size(); + assert_eq!(300, out_info.width); + assert_eq!(300, out_info.height); + assert_eq!(8, out_info.bit_depth as u8); + assert_eq!(4, out_info.color_type.samples()); + assert_eq!(out_info.buffer_size, output_buffer_size); + let mut out = vec![0; output_buffer_size]; + reader.next_frame(&mut out)?; + Ok(()) +} + #[test] -fn decode_001() -> Result<(), Box> { - static TEST_PNG: &[u8] = include_bytes!("test-001.png"); +fn decode_001_cursor_buffered() -> Result<(), Box> { + use std::io; - let cursor = Cursor::new(TEST_PNG); + let cursor = io::BufReader::new(Cursor::new(TEST_PNG_001)); let decoder = Decoder::new(cursor); let (out_info, mut reader) = decoder.read_info()?; let output_buffer_size = reader.output_buffer_size(); assert_eq!(300, out_info.width); assert_eq!(300, out_info.height); - assert_eq!(8, out_info.bit_depth); + assert_eq!(8, out_info.bit_depth as u8); assert_eq!(4, out_info.color_type.samples()); assert_eq!(out_info.buffer_size, output_buffer_size); let mut out = vec![0; output_buffer_size]; @@ -20,16 +39,44 @@ fn decode_001() -> Result<(), Box> { } #[test] -fn decode_002() -> Result<(), Box> { - static TEST_PNG: &[u8] = include_bytes!("test-002.png"); +fn decode_001_slice() -> Result<(), Box> { + let decoder = Decoder::new(TEST_PNG_001); + let (out_info, mut reader) = decoder.read_info()?; + let output_buffer_size = reader.output_buffer_size(); + assert_eq!(300, out_info.width); + assert_eq!(300, out_info.height); + assert_eq!(8, out_info.bit_depth as u8); + assert_eq!(4, out_info.color_type.samples()); + assert_eq!(out_info.buffer_size, output_buffer_size); + let mut out = vec![0; output_buffer_size]; + reader.next_frame(&mut out)?; + Ok(()) +} - let cursor = Cursor::new(TEST_PNG); +#[test] +fn decode_001_from_slice() -> Result<(), Box> { + let decoder = Decoder::new(TEST_PNG_001); + let (out_info, mut reader) = decoder.read_info_from_slice()?; + let output_buffer_size = reader.output_buffer_size(); + assert_eq!(300, out_info.width); + assert_eq!(300, out_info.height); + assert_eq!(8, out_info.bit_depth as u8); + assert_eq!(4, out_info.color_type.samples()); + assert_eq!(out_info.buffer_size, output_buffer_size); + let mut out = vec![0; output_buffer_size]; + reader.next_frame(&mut out)?; + Ok(()) +} + +#[test] +fn decode_002_cursor() -> Result<(), Box> { + let cursor = Cursor::new(TEST_PNG_002); let decoder = Decoder::new(cursor); let (out_info, mut reader) = decoder.read_info()?; let output_buffer_size = reader.output_buffer_size(); assert_eq!(380, out_info.width); assert_eq!(287, out_info.height); - assert_eq!(8, out_info.bit_depth); + assert_eq!(8, out_info.bit_depth as u8); assert_eq!(4, out_info.color_type.samples()); assert_eq!(out_info.buffer_size, output_buffer_size); let mut out = vec![0; output_buffer_size]; @@ -38,18 +85,46 @@ fn decode_002() -> Result<(), Box> { } #[test] -fn decode_002_buffered() -> Result<(), Box> { - static TEST_PNG: &[u8] = include_bytes!("test-002.png"); - +fn decode_002_cursor_buffered() -> Result<(), Box> { use std::io; - let cursor = io::BufReader::new(Cursor::new(TEST_PNG)); + let cursor = io::BufReader::new(Cursor::new(TEST_PNG_002)); let decoder = Decoder::new(cursor); let (out_info, mut reader) = decoder.read_info()?; let output_buffer_size = reader.output_buffer_size(); assert_eq!(380, out_info.width); assert_eq!(287, out_info.height); - assert_eq!(8, out_info.bit_depth); + assert_eq!(8, out_info.bit_depth as u8); + assert_eq!(4, out_info.color_type.samples()); + assert_eq!(out_info.buffer_size, output_buffer_size); + let mut out = vec![0; output_buffer_size]; + reader.next_frame(&mut out)?; + Ok(()) +} + +#[test] +fn decode_002_slice() -> Result<(), Box> { + let decoder = Decoder::new(TEST_PNG_002); + let (out_info, mut reader) = decoder.read_info()?; + let output_buffer_size = reader.output_buffer_size(); + assert_eq!(380, out_info.width); + assert_eq!(287, out_info.height); + assert_eq!(8, out_info.bit_depth as u8); + assert_eq!(4, out_info.color_type.samples()); + assert_eq!(out_info.buffer_size, output_buffer_size); + let mut out = vec![0; output_buffer_size]; + reader.next_frame(&mut out)?; + Ok(()) +} + +#[test] +fn decode_002_from_slice() -> Result<(), Box> { + let decoder = Decoder::new(TEST_PNG_002); + let (out_info, mut reader) = decoder.read_info_from_slice()?; + let output_buffer_size = reader.output_buffer_size(); + assert_eq!(380, out_info.width); + assert_eq!(287, out_info.height); + assert_eq!(8, out_info.bit_depth as u8); assert_eq!(4, out_info.color_type.samples()); assert_eq!(out_info.buffer_size, output_buffer_size); let mut out = vec![0; output_buffer_size];