use crate::{
error::{check_err, Error},
ContextFlags, CrcAction, DecodeFlags, Format,
};
use spng_sys as sys;
use std::{ffi::CStr, io, marker::PhantomData, mem, mem::MaybeUninit, slice};
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
}
pub trait IfPresent<T> {
fn if_present(self) -> Result<Option<T>, Error>;
}
impl<T> IfPresent<T> for Result<T, Error> {
fn if_present(self) -> Result<Option<T>, Error> {
match self {
Ok(value) => Ok(Some(value)),
Err(Error::Chunkavail) => Ok(None),
Err(error) => Err(error),
}
}
}
#[derive(Debug)]
pub struct RawContext<R> {
raw: *mut sys::spng_ctx,
reader: Option<Box<R>>,
}
impl<R> Drop for RawContext<R> {
fn drop(&mut self) {
if !self.raw.is_null() {
unsafe {
sys::spng_ctx_free(self.raw);
}
}
}
}
impl<R> RawContext<R> {
pub fn new() -> Result<RawContext<R>, Error> {
RawContext::with_flags(ContextFlags::empty())
}
pub fn with_flags(flags: ContextFlags) -> Result<RawContext<R>, Error> {
unsafe {
let raw = sys::spng_ctx_new(flags.bits());
if raw.is_null() {
Err(Error::Mem)
} else {
Ok(RawContext { raw, reader: None })
}
}
}
pub fn set_crc_action(
&mut self,
critical: CrcAction,
ancillary: CrcAction,
) -> Result<(), Error> {
unsafe {
check_err(sys::spng_set_crc_action(
self.raw,
critical as i32,
ancillary as i32,
))
}
}
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 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)) }
}
pub fn get_chunk_limits(&self) -> Result<(usize, usize), Error> {
let mut chunk_size = 0;
let mut cache_size = 0;
unsafe {
check_err(sys::spng_get_chunk_limits(
self.raw,
&mut chunk_size,
&mut cache_size,
))?;
Ok((chunk_size, cache_size))
}
}
pub fn set_chunk_limits(&mut self, chunk_size: usize, cache_size: usize) -> Result<(), Error> {
unsafe { check_err(sys::spng_set_chunk_limits(self.raw, chunk_size, cache_size)) }
}
pub fn get_ihdr(&self) -> Result<sys::spng_ihdr, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_ihdr(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub fn get_plte(&self) -> Result<Plte, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_plte(self.raw, chunk.as_mut_ptr()))?;
Ok(Plte(chunk.assume_init()))
}
}
pub fn get_trns(&self) -> Result<sys::spng_trns, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_trns(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub fn get_chrm(&self) -> Result<sys::spng_chrm, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_chrm(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub fn get_chrm_int(&self) -> Result<sys::spng_chrm_int, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_chrm_int(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub fn get_gama(&self) -> Result<f64, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_gama(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub fn get_iccp(&self) -> Result<Ref<Iccp>, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_iccp(self.raw, chunk.as_mut_ptr()))?;
let chunk: Iccp = mem::transmute(chunk.assume_init());
Ok(Ref::from(chunk))
}
}
pub fn get_sbit(&self) -> Result<sys::spng_sbit, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_sbit(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub fn get_srgb(&self) -> Result<u8, Error> {
unsafe {
let mut rendering_intent = 0;
check_err(sys::spng_get_srgb(self.raw, &mut rendering_intent))?;
Ok(rendering_intent)
}
}
pub fn get_text(&self) -> Result<Ref<Vec<Text>>, Error> {
unsafe {
use std::ptr;
let mut len = 0;
check_err(sys::spng_get_text(self.raw, ptr::null_mut(), &mut len))?;
let mut vec = Vec::<Text>::new();
vec.reserve_exact(len as usize);
vec.set_len(len as usize);
let text_ptr = vec.as_mut_ptr() as *mut sys::spng_text;
check_err(sys::spng_get_text(self.raw, text_ptr, &mut len))?;
Ok(Ref::from(vec))
}
}
pub fn get_bkgd(&self) -> Result<sys::spng_bkgd, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_bkgd(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub fn get_phys(&self) -> Result<sys::spng_phys, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_phys(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub fn get_splt(&self) -> Result<Ref<Vec<Splt>>, Error> {
unsafe {
use std::ptr;
let mut len = 0;
check_err(sys::spng_get_splt(self.raw, ptr::null_mut(), &mut len))?;
let mut vec = Vec::<Splt>::new();
vec.reserve_exact(len as usize);
vec.set_len(len as usize);
let splt_ptr = vec.as_mut_ptr() as *mut sys::spng_splt;
check_err(sys::spng_get_splt(self.raw, splt_ptr, &mut len))?;
Ok(Ref::from(vec))
}
}
pub fn get_time(&self) -> Result<sys::spng_time, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_time(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub fn get_offs(&self) -> Result<sys::spng_offs, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_offs(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub fn get_exif(&self) -> Result<Ref<Exif>, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_exif(self.raw, chunk.as_mut_ptr()))?;
let chunk: Exif = mem::transmute(chunk.assume_init());
Ok(Ref::from(chunk))
}
}
pub fn get_row_info(&self) -> Result<sys::spng_row_info, Error> {
unsafe {
let mut chunk = MaybeUninit::uninit();
check_err(sys::spng_get_row_info(self.raw, chunk.as_mut_ptr()))?;
Ok(chunk.assume_init())
}
}
pub 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)
}
pub fn decode_image(
&mut self,
out: &mut [u8],
out_format: Format,
flags: DecodeFlags,
) -> Result<(), Error> {
unsafe {
check_err(sys::spng_decode_image(
self.raw,
out.as_mut_ptr() as _,
out.len(),
out_format as _,
flags.bits,
))
}
}
pub fn decode_row(&mut self, out: &mut [u8]) -> Result<(), Error> {
unsafe {
check_err(sys::spng_decode_row(
self.raw,
out.as_mut_ptr() as _,
out.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<R: io::Read> RawContext<R> {
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::<R>);
unsafe { check_err(sys::spng_set_png_stream(self.raw, read_fn, user)) }
}
}
impl<'a> RawContext<&'a [u8]> {
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(),
))
}
}
}
pub struct Ref<'a, T: 'a> {
data: T,
_p: PhantomData<&'a ()>,
}
impl<'a, T: 'a> std::ops::Deref for Ref<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<'a, T: 'a> From<T> for Ref<'a, T> {
fn from(t: T) -> Ref<'a, T> {
Ref {
data: t,
_p: PhantomData,
}
}
}
#[repr(C)]
pub struct Splt(sys::spng_splt);
impl Splt {
pub fn name(&self) -> Result<&str, std::str::Utf8Error> {
unsafe { CStr::from_ptr(self.0.name.as_ptr() as _).to_str() }
}
pub fn sample_depth(&self) -> u8 {
self.0.sample_depth
}
pub fn entries(&self) -> &[sys::spng_splt_entry] {
unsafe { slice::from_raw_parts(self.0.entries, self.0.n_entries as usize) }
}
}
#[repr(C)]
pub struct Plte(sys::spng_plte);
impl Plte {
pub fn entries(&self) -> &[sys::spng_plte_entry] {
unsafe { slice::from_raw_parts(self.0.entries.as_ptr(), self.0.n_entries as usize) }
}
}
#[repr(C)]
pub struct Exif(sys::spng_exif);
impl Exif {
pub fn data(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.0.data as _, self.0.length as usize) }
}
}
#[repr(C)]
pub struct Text(sys::spng_text);
impl Text {
pub fn keyword(&self) -> Result<&str, std::str::Utf8Error> {
unsafe { CStr::from_ptr(self.0.keyword.as_ptr() as _).to_str() }
}
pub fn type_(&self) -> i32 {
self.0.type_
}
pub fn length(&self) -> usize {
self.0.length
}
pub fn text(&self) -> Result<&str, std::str::Utf8Error> {
unsafe { CStr::from_ptr(self.0.text).to_str() }
}
pub fn compression_flag(&self) -> u8 {
self.0.compression_flag
}
pub fn compression_method(&self) -> u8 {
self.0.compression_method
}
pub fn language_tag(&self) -> Result<&str, std::str::Utf8Error> {
unsafe { CStr::from_ptr(self.0.language_tag).to_str() }
}
pub fn translated_keyword(&self) -> Result<&str, std::str::Utf8Error> {
unsafe { CStr::from_ptr(self.0.translated_keyword).to_str() }
}
}
#[repr(C)]
pub struct Iccp(sys::spng_iccp);
impl Iccp {
pub fn profile_name(&self) -> Result<&str, std::str::Utf8Error> {
unsafe { CStr::from_ptr(self.0.profile_name.as_ptr()).to_str() }
}
pub fn profile(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.0.profile as _, self.0.profile_len as usize) }
}
}