use std::ffi::CString;
use std::io::{self, Write};
use std::slice;
use mupdf_sys::*;
use crate::{context, Buffer, Colorspace, Error, IRect};
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum ImageFormat {
PNG = 0,
PNM = 1,
PAM = 2,
PSD = 3,
PS = 4,
}
#[derive(Debug)]
pub struct Pixmap {
pub(crate) inner: *mut fz_pixmap,
}
impl Pixmap {
pub(crate) unsafe fn from_raw(pixmap: *mut fz_pixmap) -> Self {
Self { inner: pixmap }
}
pub fn new(
cs: &Colorspace,
x: i32,
y: i32,
w: i32,
h: i32,
alpha: bool,
) -> Result<Self, Error> {
unsafe { ffi_try!(mupdf_new_pixmap(context(), cs.inner, x, y, w, h, alpha)) }
.map(|inner| Self { inner })
}
pub fn new_with_rect(cs: &Colorspace, rect: IRect, alpha: bool) -> Result<Self, Error> {
let x = rect.x0;
let y = rect.y0;
let w = rect.x1 - rect.x0;
let h = rect.y1 - rect.y0;
Self::new(cs, x, y, w, h, alpha)
}
pub fn new_with_w_h(cs: &Colorspace, w: i32, h: i32, alpha: bool) -> Result<Self, Error> {
Self::new(cs, 0, 0, w, h, alpha)
}
pub fn x(&self) -> i32 {
unsafe { (*self.inner).x }
}
pub fn y(&self) -> i32 {
unsafe { (*self.inner).y }
}
pub fn origin(&self) -> (i32, i32) {
unsafe { ((*self.inner).x, (*self.inner).y) }
}
pub fn width(&self) -> u32 {
unsafe { (*self.inner).w as u32 }
}
pub fn height(&self) -> u32 {
unsafe { (*self.inner).h as u32 }
}
pub fn stride(&self) -> isize {
unsafe { (*self.inner).stride }
}
pub fn n(&self) -> u8 {
unsafe { (*self.inner).n }
}
pub fn alpha(&self) -> bool {
unsafe { (*self.inner).alpha > 0 }
}
pub fn color_space(&self) -> Option<Colorspace> {
unsafe {
let ptr = (*self.inner).colorspace;
if ptr.is_null() {
return None;
}
Some(Colorspace::from_raw(ptr))
}
}
pub fn resolution(&self) -> (i32, i32) {
unsafe {
let x_res = (*self.inner).xres;
let y_res = (*self.inner).yres;
(x_res, y_res)
}
}
pub fn set_resolution(&mut self, x_res: i32, y_res: i32) {
unsafe {
fz_set_pixmap_resolution(context(), self.inner, x_res, y_res);
}
}
pub fn rect(&self) -> IRect {
unsafe { fz_pixmap_bbox(context(), self.inner).into() }
}
pub fn samples(&self) -> &[u8] {
let len = (self.width() * self.height() * self.n() as u32) as usize;
let ptr = unsafe { (*self.inner).samples };
if ptr.is_null() {
&[]
} else {
unsafe { slice::from_raw_parts(ptr, len) }
}
}
pub fn samples_mut(&mut self) -> &mut [u8] {
let len = (self.width() * self.height() * self.n() as u32) as usize;
let ptr = unsafe { (*self.inner).samples };
if ptr.is_null() {
&mut []
} else {
unsafe { slice::from_raw_parts_mut(ptr, len) }
}
}
pub fn pixels(&self) -> Option<&[u32]> {
if self.n() != 4 || !self.alpha() {
return None;
}
let size = (self.width() * self.height()) as usize;
if size * 4 != (self.height() as usize * self.stride() as usize) {
return None;
}
let ptr = unsafe { (*self.inner).samples };
if ptr.is_null() {
Some(&[])
} else {
Some(unsafe { slice::from_raw_parts(ptr.cast(), size) })
}
}
pub fn clear(&mut self) -> Result<(), Error> {
unsafe { ffi_try!(mupdf_clear_pixmap(context(), self.inner)) }
}
pub fn clear_with(&mut self, value: i32) -> Result<(), Error> {
unsafe { ffi_try!(mupdf_clear_pixmap_with_value(context(), self.inner, value)) }
}
pub fn save_as(&self, filename: &str, format: ImageFormat) -> Result<(), Error> {
let c_filename = CString::new(filename)?;
unsafe {
ffi_try!(mupdf_save_pixmap_as(
context(),
self.inner,
c_filename.as_ptr(),
format as i32
))
}
}
pub fn invert(&mut self) -> Result<(), Error> {
unsafe { ffi_try!(mupdf_invert_pixmap(context(), self.inner)) }
}
pub fn gamma(&mut self, gamma: f32) -> Result<(), Error> {
unsafe { ffi_try!(mupdf_gamma_pixmap(context(), self.inner, gamma)) }
}
pub fn tint(&mut self, black: i32, white: i32) -> Result<(), Error> {
unsafe { ffi_try!(mupdf_tint_pixmap(context(), self.inner, black, white)) }
}
fn get_image_data(&self, format: ImageFormat) -> Result<Buffer, Error> {
unsafe {
ffi_try!(mupdf_pixmap_get_image_data(
context(),
self.inner,
format as i32
))
}
.map(|inner| unsafe { Buffer::from_raw(inner) })
}
pub fn write_to<W: Write>(&self, w: &mut W, format: ImageFormat) -> Result<u64, Error> {
let mut buf = self.get_image_data(format)?;
Ok(io::copy(&mut buf, w)?)
}
pub fn try_clone(&self) -> Result<Self, Error> {
unsafe { ffi_try!(mupdf_clone_pixmap(context(), self.inner)) }.map(|inner| Self { inner })
}
}
impl Drop for Pixmap {
fn drop(&mut self) {
if !self.inner.is_null() {
unsafe { fz_drop_pixmap(context(), self.inner) };
}
}
}
impl Clone for Pixmap {
fn clone(&self) -> Pixmap {
self.try_clone().unwrap()
}
}
#[cfg(test)]
mod test {
use super::{Colorspace, IRect, Pixmap};
#[test]
fn test_pixmap_properties() {
let cs = Colorspace::device_rgb();
let mut pixmap = Pixmap::new_with_w_h(&cs, 100, 100, false).expect("Pixmap::new_with_w_h");
pixmap.clear_with(0).unwrap();
let pixmap_cs = pixmap.color_space().unwrap();
assert_eq!(cs, pixmap_cs);
let resolution = pixmap.resolution();
assert_eq!(resolution, (96, 96));
let rect = pixmap.rect();
assert_eq!(rect, IRect::new(0, 0, 100, 100));
assert_eq!(pixmap.origin(), (0, 0));
let samples = pixmap.samples();
assert!(samples.iter().all(|x| *x == 0));
assert_eq!(samples.len(), 100 * 100 * pixmap_cs.n() as usize);
}
#[test]
fn test_pixmap_clear() {
let cs = Colorspace::device_rgb();
let mut pixmap = Pixmap::new_with_w_h(&cs, 100, 100, false).expect("Pixmap::new_with_w_h");
pixmap.clear().unwrap();
pixmap.clear_with(1).unwrap();
}
#[test]
fn test_pixmap_invert() {
let cs = Colorspace::device_rgb();
let mut pixmap = Pixmap::new_with_w_h(&cs, 100, 100, false).expect("Pixmap::new_with_w_h");
pixmap.clear().unwrap();
pixmap.invert().unwrap();
}
#[test]
fn test_pixmap_gamma() {
let cs = Colorspace::device_rgb();
let mut pixmap = Pixmap::new_with_w_h(&cs, 100, 100, false).expect("Pixmap::new_with_w_h");
pixmap.clear().unwrap();
pixmap.gamma(2.0).unwrap();
}
#[test]
fn test_pixmap_tint() {
let cs = Colorspace::device_rgb();
let mut pixmap = Pixmap::new_with_w_h(&cs, 100, 100, false).expect("Pixmap::new_with_w_h");
pixmap.clear().unwrap();
pixmap.tint(0, 255).unwrap();
}
#[test]
fn test_pixmap_pixels() {
let cs = Colorspace::device_rgb();
let mut pixmap = Pixmap::new_with_w_h(&cs, 100, 100, false).expect("Pixmap::new_with_w_h");
pixmap.clear().unwrap();
let pixels = pixmap.pixels();
assert!(pixels.is_none());
let mut pixmap = Pixmap::new_with_w_h(&cs, 100, 100, true).expect("Pixmap::new_with_w_h");
pixmap.clear().unwrap();
let pixels = pixmap.pixels();
assert!(pixels.is_some());
}
#[test]
fn test_pixmap_empty() {
let cs = Colorspace::device_rgb();
let mut pixmap = Pixmap::new_with_w_h(&cs, 0, 0, false).expect("Pixmap::new_with_w_h");
assert!(pixmap.samples().is_empty());
assert!(pixmap.samples_mut().is_empty());
}
}