use std::convert::TryFrom;
use std::io;
use spng_sys as sys;
mod error;
pub mod raw;
pub use error::Error;
use raw::RawContext;
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CrcAction {
Error = sys::spng_crc_action_SPNG_CRC_ERROR,
Discard = sys::spng_crc_action_SPNG_CRC_DISCARD,
Use = sys::spng_crc_action_SPNG_CRC_USE,
}
#[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,
Rgb8 = sys::spng_format_SPNG_FMT_RGB8,
Png = sys::spng_format_SPNG_FMT_PNG,
Raw = sys::spng_format_SPNG_FMT_RAW,
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ColorType {
Grayscale = sys::spng_color_type_SPNG_COLOR_TYPE_GRAYSCALE as u8,
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,
TruecolorAlpha = sys::spng_color_type_SPNG_COLOR_TYPE_TRUECOLOR_ALPHA as u8,
}
impl ColorType {
pub const RGB: ColorType = ColorType::Truecolor;
pub const RGBA: ColorType = ColorType::TruecolorAlpha;
}
impl TryFrom<u8> for ColorType {
type Error = Error;
fn try_from(value: u8) -> Result<ColorType, Error> {
use ColorType::*;
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),
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,
}
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum BitDepth {
One = 1,
Two = 2,
Four = 4,
Eight = 8,
Sixteen = 16,
}
impl TryFrom<u8> for BitDepth {
type Error = Error;
fn try_from(value: u8) -> Result<BitDepth, Error> {
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! {
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;
}
}
bitflags::bitflags! {
pub struct ContextFlags: i32 {
const IGNORE_ADLER32 = sys::spng_ctx_flags_SPNG_CTX_IGNORE_ADLER32;
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Limits {
pub max_width: u32,
pub max_height: u32,
}
const PNG_U32_MAX: u32 = std::u32::MAX / 2 - 1;
impl Default for Limits {
fn default() -> Limits {
Limits {
max_width: PNG_U32_MAX,
max_height: PNG_U32_MAX,
}
}
}
#[derive(Debug)]
pub struct Decoder<R> {
reader: R,
limits: Limits,
context_flags: ContextFlags,
decode_flags: DecodeFlags,
output_format: Format,
}
#[derive(Debug)]
pub struct OutputInfo {
pub width: u32,
pub height: u32,
pub color_type: ColorType,
pub bit_depth: BitDepth,
pub buffer_size: usize,
}
impl OutputInfo {
pub fn line_size(&self) -> usize {
self.buffer_size / self.height as usize
}
}
impl OutputInfo {
fn from_ihdr_format_buffer_size(
ihdr: &sys::spng_ihdr,
output_format: Format,
output_buffer_size: usize,
) -> Result<OutputInfo, Error> {
let bit_depth = match output_format {
Format::Png | Format::Raw => BitDepth::try_from(ihdr.bit_depth)?,
Format::Rgb8 | Format::Rgba8 => BitDepth::Eight,
Format::Rgba16 => BitDepth::Sixteen,
};
let color_type = match output_format {
Format::Png | Format::Raw => ColorType::try_from(ihdr.color_type)?,
Format::Rgb8 => ColorType::Truecolor,
Format::Rgba8 => ColorType::TruecolorAlpha,
Format::Rgba16 => ColorType::TruecolorAlpha,
};
Ok(OutputInfo {
bit_depth,
color_type,
width: ihdr.width,
height: ihdr.height,
buffer_size: output_buffer_size,
})
}
}
#[derive(Debug)]
pub struct Info {
pub width: u32,
pub height: u32,
pub color_type: ColorType,
pub bit_depth: BitDepth,
}
impl Info {
fn from_ihdr(header: &sys::spng_ihdr) -> Result<Info, Error> {
Ok(Info {
width: header.width,
height: header.height,
bit_depth: BitDepth::try_from(header.bit_depth)?,
color_type: ColorType::try_from(header.color_type)?,
})
}
}
#[derive(Debug)]
pub struct Reader<R> {
ctx: RawContext<R>,
ihdr: sys::spng_ihdr,
out_format: Format,
decode_flags: DecodeFlags,
output_buffer_size: usize,
}
impl<R> Decoder<R> {
pub fn new(reader: R) -> Decoder<R> {
let decode_flags = DecodeFlags::empty();
let context_flags = ContextFlags::empty();
let output_format = Format::Png;
let limits = Limits::default();
Decoder {
reader,
limits,
decode_flags,
context_flags,
output_format,
}
}
pub fn with_limits(mut self, limits: Limits) -> Decoder<R> {
self.limits = limits;
self
}
pub fn with_context_flags(mut self, context_flags: ContextFlags) -> Decoder<R> {
self.context_flags = context_flags;
self
}
pub fn with_decode_flags(mut self, decode_flags: DecodeFlags) -> Decoder<R> {
self.decode_flags = decode_flags;
self
}
pub fn with_output_format(mut self, output_format: Format) -> Decoder<R> {
self.output_format = output_format;
self
}
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, output_format: Format) {
self.output_format = output_format;
}
pub fn set_context_flags(&mut self, context_flags: ContextFlags) {
self.context_flags = context_flags;
}
pub fn read_info(self) -> Result<(OutputInfo, Reader<R>), Error>
where
R: io::Read,
{
let mut ctx = RawContext::with_flags(self.context_flags)?;
ctx.set_image_limits(self.limits.max_width, self.limits.max_height)?;
ctx.set_png_stream(self.reader)?;
let ihdr = ctx.get_ihdr()?;
let output_buffer_size = ctx.decoded_image_size(self.output_format)?;
let output_info = OutputInfo::from_ihdr_format_buffer_size(
&ihdr,
self.output_format,
output_buffer_size,
)?;
let reader = Reader {
ctx,
ihdr,
out_format: self.output_format,
decode_flags: self.decode_flags,
output_buffer_size,
};
Ok((output_info, reader))
}
}
impl<R> Reader<R> {
pub fn info(&self) -> Info {
Info::from_ihdr(&self.ihdr).expect("invalid ihdr")
}
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)
}
}
pub fn decode<R>(reader: R, output_format: Format) -> Result<(OutputInfo, Vec<u8>), Error>
where
R: io::Read,
{
let decoder = Decoder::new(reader).with_output_format(output_format);
let (out_info, mut reader) = decoder.read_info()?;
let mut out = Vec::new();
out.reserve_exact(out_info.buffer_size);
unsafe {
out.set_len(out_info.buffer_size);
}
reader.next_frame(&mut out)?;
Ok((out_info, out))
}