use std::convert::TryFrom;
use std::{io, mem, slice};
use spng_sys as sys;
mod error;
use error::check_err;
pub use error::Error;
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Format {
Rgba8 = sys::spng_format_SPNG_FMT_RGBA8,
Rgba16 = sys::spng_format_SPNG_FMT_RGBA16,
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ColorType {
Grayscale = sys::spng_color_type_SPNG_COLOR_TYPE_GRAYSCALE,
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,
TruecolorAlpha = sys::spng_color_type_SPNG_COLOR_TYPE_TRUECOLOR_ALPHA,
}
impl TryFrom<u8> for ColorType {
type Error = Error;
fn try_from(c: u8) -> Result<ColorType, Error> {
use ColorType::*;
let c = c as i32;
match c {
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),
sys::spng_color_type_SPNG_COLOR_TYPE_GRAYSCALE_ALPHA => Ok(GrayscaleAlpha),
sys::spng_color_type_SPNG_COLOR_TYPE_TRUECOLOR_ALPHA => Ok(TruecolorAlpha),
_ => Err(Error::ColorType),
}
}
}
impl ColorType {
pub fn samples(self) -> usize {
use ColorType::*;
match self {
Grayscale | Indexed => 1,
GrayscaleAlpha => 2,
Truecolor => 3,
TruecolorAlpha => 4,
}
}
}
bitflags::bitflags! {
pub struct DecodeFlags: i32 {
const TRANSPARENCY = sys::spng_decode_flags_SPNG_DECODE_TRNS;
const GAMMA = sys::spng_decode_flags_SPNG_DECODE_GAMMA;
const PROGRESSIVE = sys::spng_decode_flags_SPNG_DECODE_PROGRESSIVE;
#[doc(hidden)]
const SIGNIFICANT_BIT = sys::spng_decode_flags_SPNG_DECODE_USE_SBIT;
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Limits {
pub max_width: u32,
pub max_height: u32,
}
impl Default for Limits {
fn default() -> Limits {
Limits {
max_width: std::u32::MAX / 2 - 1,
max_height: std::u32::MAX / 2 - 1,
}
}
}
#[derive(Debug)]
pub struct Decoder<R> {
reader: R,
limits: Limits,
decode_flags: DecodeFlags,
output_format: Option<Format>,
}
#[derive(Debug)]
pub struct OutputInfo {
pub width: u32,
pub height: u32,
pub color_type: ColorType,
pub bit_depth: u8,
pub buffer_size: usize,
}
#[derive(Debug)]
pub struct Info {
pub width: u32,
pub height: u32,
pub color_type: ColorType,
pub bit_depth: u8,
}
#[derive(Debug)]
pub struct Reader<R> {
ctx: Context,
out_format: Format,
info: Info,
#[allow(unused)]
inner: Box<R>,
decode_flags: DecodeFlags,
output_buffer_size: usize,
}
unsafe extern "C" fn read_fn<R: io::Read>(
_: *mut sys::spng_ctx,
user: *mut libc::c_void,
dest: *mut libc::c_void,
len: usize,
) -> libc::c_int {
let reader: &mut R = &mut *(user as *mut R as *mut _);
let dest = slice::from_raw_parts_mut(dest as *mut u8, len);
let mut offset = 0;
while offset < len {
let buf = &mut dest[offset..];
let ret = reader.read(buf);
match ret {
Ok(0) => return sys::spng_errno_SPNG_IO_EOF,
Ok(n) => offset += n,
Err(_) => return sys::spng_errno_SPNG_IO_ERROR,
}
}
sys::spng_errno_SPNG_OK
}
#[derive(Debug)]
struct Context {
raw: *mut sys::spng_ctx,
}
impl Drop for Context {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe {
sys::spng_ctx_free(self.raw);
}
}
}
}
impl Context {
fn new(flags: i32) -> Result<Context, Error> {
unsafe {
let raw = sys::spng_ctx_new(flags);
if raw.is_null() {
Err(Error::Mem)
} else {
Ok(Context { raw })
}
}
}
fn decoded_image_size(&self, out_format: Format) -> Result<usize, Error> {
let mut len = 0;
unsafe {
check_err(sys::spng_decoded_image_size(
self.raw,
out_format as _,
&mut len,
))?;
}
Ok(len)
}
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<R>(
&mut self,
read_fn: sys::spng_read_fn,
reader: *mut R,
) -> Result<(), Error> {
unsafe {
check_err(sys::spng_set_png_stream(
self.raw,
read_fn,
reader as *mut _,
))
}
}
fn get_ihdr(&self) -> Result<sys::spng_ihdr, Error> {
unsafe {
let mut header = mem::zeroed();
check_err(sys::spng_get_ihdr(self.raw, &mut header))?;
Ok(header)
}
}
fn decode_image(
&mut self,
output: &mut [u8],
out_format: Format,
flags: DecodeFlags,
) -> Result<(), Error> {
unsafe {
check_err(sys::spng_decode_image(
self.raw,
output.as_mut_ptr() as _,
output.len(),
out_format as _,
flags.bits,
))
}
}
}
impl<R: io::Read> Decoder<R> {
pub fn new(r: R) -> Decoder<R> {
Decoder::with_limits(r, Limits::default())
}
pub fn with_limits(r: R, limits: Limits) -> Decoder<R> {
let decode_flags = DecodeFlags::empty();
Decoder {
reader: r,
limits,
decode_flags,
output_format: None,
}
}
pub fn set_limits(&mut self, limits: Limits) {
self.limits = limits;
}
pub fn set_decode_flags(&mut self, decode_flags: DecodeFlags) {
self.decode_flags = decode_flags;
}
pub fn set_output_format(&mut self, format: Format) {
self.output_format = Some(format);
}
pub fn read_info(self) -> Result<(OutputInfo, Reader<R>), Error> {
let mut inner = Box::new(self.reader);
let mut ctx = Context::new(self.decode_flags.bits)?;
ctx.set_image_limits(self.limits.max_width, self.limits.max_height)?;
ctx.set_png_stream(Some(read_fn::<R>), inner.as_mut() as *mut R as *mut _)?;
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 reader = Reader {
ctx,
out_format,
info,
decode_flags: self.decode_flags,
inner,
output_buffer_size: buffer_size,
};
Ok((out_info, reader))
}
}
impl<R> Reader<R> {
pub fn info(&self) -> &Info {
&self.info
}
pub fn output_buffer_size(&self) -> usize {
self.output_buffer_size
}
pub fn next_frame(&mut self, output: &mut [u8]) -> Result<(), Error> {
self.ctx
.decode_image(output, self.out_format, self.decode_flags)
}
}