use leptonica_sys::{
l_int32, pixClone, pixDestroy, pixGetHeight, pixGetWidth, pixRead, pixReadMem,
};
use crate::memory::{LeptonicaClone, LeptonicaDestroy, RefCountedExclusive};
use std::convert::{AsRef, Infallible, TryInto};
use std::{ffi::CStr, num::TryFromIntError};
use thiserror::Error;
#[derive(Debug)]
pub struct Pix(*mut leptonica_sys::Pix);
#[derive(Debug, Error, PartialEq)]
pub enum PixReadMemError {
#[error("Pix::read_mem returned null")]
NullPtr,
#[error("Failed to convert image size")]
ImageSizeConversion(#[from] TryFromIntError),
}
impl From<Infallible> for PixReadMemError {
fn from(_: Infallible) -> Self {
unreachable!()
}
}
#[derive(Debug, Error)]
#[error("Pix::read returned null")]
pub struct PixReadError();
impl AsRef<*mut leptonica_sys::Pix> for Pix {
fn as_ref(&self) -> &*mut leptonica_sys::Pix {
&self.0
}
}
impl AsRef<leptonica_sys::Pix> for Pix {
fn as_ref(&self) -> &leptonica_sys::Pix {
unsafe { &*self.0 }
}
}
impl Pix {
pub unsafe fn new_from_pointer(ptr: *mut leptonica_sys::Pix) -> Self {
Self(ptr)
}
pub fn read(filename: &CStr) -> Result<RefCountedExclusive<Self>, PixReadError> {
let ptr = unsafe { pixRead(filename.as_ptr()) };
if ptr.is_null() {
Err(PixReadError())
} else {
Ok(unsafe { RefCountedExclusive::new(Self(ptr)) })
}
}
pub fn read_mem(img: &[u8]) -> Result<RefCountedExclusive<Self>, PixReadMemError> {
let ptr = unsafe { pixReadMem(img.as_ptr(), img.len().try_into()?) };
if ptr.is_null() {
Err(PixReadMemError::NullPtr)
} else {
Ok(unsafe { RefCountedExclusive::new(Self(ptr)) })
}
}
pub fn get_height(&self) -> l_int32 {
unsafe { pixGetHeight(self.0) }
}
pub fn get_width(&self) -> l_int32 {
unsafe { pixGetWidth(self.0) }
}
}
impl LeptonicaDestroy for Pix {
unsafe fn destroy(&mut self) {
pixDestroy(&mut self.0);
}
}
impl LeptonicaClone for Pix {
unsafe fn clone(&mut self) -> Self {
Self::new_from_pointer(pixClone(self.0))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn read_error_test() {
let path = std::ffi::CString::new("fail").unwrap();
assert!(Pix::read(&path).is_err());
}
#[test]
fn read_mem_error_test() {
assert_eq!(Pix::read_mem(&[]).err(), Some(PixReadMemError::NullPtr));
}
#[test]
fn read_test() {
let path = std::ffi::CString::new("image.png").unwrap();
let pix = Pix::read(&path).unwrap();
assert_eq!(pix.get_width(), 200);
}
#[test]
fn read_memory_test() {
let pix = Pix::read_mem(include_bytes!("../image.png")).unwrap();
assert_eq!(pix.get_height(), 23);
}
#[test]
fn clone_test() {
let pix = Pix::read_mem(include_bytes!("../image.png")).unwrap();
let pix = pix.to_ref_counted();
assert_eq!(pix.get_height(), 23);
}
}