use super::{BUFFERS, Handle, HandleStore, PIXMAPS};
use crate::fitz::colorspace::Colorspace;
use crate::fitz::image::Image;
use std::sync::LazyLock;
pub static IMAGES: LazyLock<HandleStore<Image>> = LazyLock::new(HandleStore::default);
fn colorspace_to_handle(cs: &crate::fitz::colorspace::Colorspace) -> u64 {
match cs.name() {
"DeviceGray" => super::colorspace::FZ_COLORSPACE_GRAY,
"DeviceRGB" => super::colorspace::FZ_COLORSPACE_RGB,
"DeviceCMYK" => super::colorspace::FZ_COLORSPACE_CMYK,
"DeviceBGR" => super::colorspace::FZ_COLORSPACE_BGR,
_ => 0, }
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_image_from_pixmap(_ctx: Handle, pixmap: Handle, _mask: Handle) -> Handle {
if let Some(pm) = PIXMAPS.get(pixmap) {
if let Ok(guard) = pm.lock() {
let w = guard.w();
let h = guard.h();
let image = Image::new(w, h, None);
return IMAGES.insert(image);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_image_from_data(
_ctx: Handle,
w: i32,
h: i32,
bpc: i32,
_colorspace: Handle,
xres: i32,
yres: i32,
interpolate: i32,
imagemask: i32,
_decode: *const f32,
_mask: *const u8,
data: *const u8,
len: i32,
) -> Handle {
if data.is_null() || len <= 0 {
return 0;
}
let len = len as usize;
let data_slice = unsafe { std::slice::from_raw_parts(data, len) };
if let Ok(image) = Image::from_data(data_slice) {
let mut img = image;
if xres > 0 || yres > 0 {
img.set_resolution(
if xres > 0 { xres } else { 96 },
if yres > 0 { yres } else { 96 },
);
}
return IMAGES.insert(img);
}
if w <= 0 || h <= 0 {
return 0;
}
let bpc = bpc.clamp(1, 16);
let n = 3i32; let expected_size = ((w * h * n * bpc) / 8) as usize;
let data_copy: Vec<u8> = if data_slice.len() >= expected_size {
data_slice[..expected_size].to_vec()
} else {
let mut v = data_slice.to_vec();
v.resize(expected_size, 0);
v
};
let colorspace = Colorspace::device_rgb();
match Image::from_raw(w, h, bpc as u8, colorspace, data_copy) {
Ok(mut img) => {
if xres > 0 || yres > 0 {
img.set_resolution(
if xres > 0 { xres } else { 96 },
if yres > 0 { yres } else { 96 },
);
}
if imagemask != 0 {
img.set_has_alpha(true);
}
IMAGES.insert(img)
}
Err(_) => 0,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_keep_image(_ctx: Handle, image: Handle) -> Handle {
IMAGES.keep(image)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_drop_image(_ctx: Handle, image: Handle) {
let _ = IMAGES.remove(image);
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_w(_ctx: Handle, image: Handle) -> i32 {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
return guard.width();
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_h(_ctx: Handle, image: Handle) -> i32 {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
return guard.height();
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_xres(_ctx: Handle, image: Handle) -> i32 {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
return guard.xres();
}
}
96 }
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_yres(_ctx: Handle, image: Handle) -> i32 {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
return guard.yres();
}
}
96 }
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_colorspace(_ctx: Handle, image: Handle) -> Handle {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
if let Some(cs) = guard.colorspace() {
return colorspace_to_handle(cs);
}
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_is_mask(_ctx: Handle, image: Handle) -> i32 {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
return i32::from(guard.is_mask());
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_get_pixmap_from_image(
_ctx: Handle,
image: Handle,
_subarea: *const super::geometry::fz_irect,
_ctm: *mut super::geometry::fz_matrix,
w: *mut i32,
h: *mut i32,
) -> Handle {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
let img_w = guard.width();
let img_h = guard.height();
if !w.is_null() {
unsafe {
*w = img_w;
}
}
if !h.is_null() {
unsafe {
*h = img_h;
}
}
let cs_handle = match guard.colorspace() {
Some(cs) => colorspace_to_handle(cs),
None => 0,
};
let pixmap = super::pixmap::Pixmap::new(cs_handle, img_w, img_h, true);
return PIXMAPS.insert(pixmap);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_decode_image(
_ctx: Handle,
image: Handle,
_l2factor: i32,
_subarea: *const super::geometry::fz_irect,
) -> Handle {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
let img_w = guard.width();
let img_h = guard.height();
let cs_handle = match guard.colorspace() {
Some(cs) => colorspace_to_handle(cs),
None => 0,
};
let pixmap = super::pixmap::Pixmap::new(cs_handle, img_w, img_h, true);
return PIXMAPS.insert(pixmap);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_decode_image_scaled(
_ctx: Handle,
image: Handle,
w: i32,
h: i32,
_l2factor: i32,
_subarea: *const super::geometry::fz_irect,
) -> Handle {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
let cs_handle = match guard.colorspace() {
Some(cs) => colorspace_to_handle(cs),
None => 0,
};
let pixmap = super::pixmap::Pixmap::new(cs_handle, w, h, true);
return PIXMAPS.insert(pixmap);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_image_from_file(
_ctx: Handle,
filename: *const std::ffi::c_char,
) -> Handle {
if filename.is_null() {
return 0;
}
let c_str = unsafe { std::ffi::CStr::from_ptr(filename) };
let path = match c_str.to_str() {
Ok(s) => s,
Err(_) => return 0,
};
match std::fs::read(path) {
Ok(data) => {
match Image::from_data(&data) {
Ok(image) => IMAGES.insert(image),
Err(_) => 0,
}
}
Err(_) => 0,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_image_from_buffer(_ctx: Handle, buffer: Handle) -> Handle {
if let Some(buf) = BUFFERS.get(buffer) {
if let Ok(guard) = buf.lock() {
let data = guard.as_slice();
match Image::from_data(data) {
Ok(image) => IMAGES.insert(image),
Err(_) => 0,
}
} else {
0
}
} else {
0
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_is_valid(_ctx: Handle, image: Handle) -> i32 {
if IMAGES.get(image).is_some() { 1 } else { 0 }
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_clone_image(_ctx: Handle, image: Handle) -> Handle {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
let cloned = guard.clone();
return IMAGES.insert(cloned);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_bpp(_ctx: Handle, image: Handle) -> i32 {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
return guard.bits_per_pixel();
}
}
8 }
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_has_alpha(_ctx: Handle, image: Handle) -> i32 {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
return i32::from(guard.has_alpha());
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_orientation(_ctx: Handle, image: Handle) -> i32 {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
return guard.orientation() as i32;
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_width(_ctx: Handle, image: Handle) -> i32 {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
return guard.width();
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_image_height(_ctx: Handle, image: Handle) -> i32 {
if let Some(img) = IMAGES.get(image) {
if let Ok(guard) = img.lock() {
return guard.height();
}
}
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fz_new_image_from_buffer_data(
_ctx: Handle,
data: *const u8,
len: usize,
) -> Handle {
if data.is_null() || len == 0 {
return 0;
}
let slice = unsafe { std::slice::from_raw_parts(data, len) };
match Image::from_data(slice) {
Ok(image) => IMAGES.insert(image),
Err(_) => 0,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_image_from_pixmap() {
use crate::ffi::colorspace::FZ_COLORSPACE_RGB;
use crate::ffi::pixmap::Pixmap;
let pixmap = Pixmap::new(FZ_COLORSPACE_RGB, 10, 10, true);
let pixmap_handle = PIXMAPS.insert(pixmap);
let image_handle = fz_new_image_from_pixmap(0, pixmap_handle, 0);
assert_ne!(image_handle, 0);
fz_drop_image(0, image_handle);
PIXMAPS.remove(pixmap_handle);
}
#[test]
fn test_keep_image() {
let image = Image::new(10, 10, None);
let image_handle = IMAGES.insert(image);
let kept = fz_keep_image(0, image_handle);
assert_eq!(kept, image_handle);
fz_drop_image(0, image_handle);
}
#[test]
fn test_image_dimensions() {
let image = Image::new(100, 200, None);
let image_handle = IMAGES.insert(image);
assert_eq!(fz_image_w(0, image_handle), 100);
assert_eq!(fz_image_h(0, image_handle), 200);
fz_drop_image(0, image_handle);
}
#[test]
fn test_image_resolution() {
let image = Image::new(100, 100, None);
let image_handle = IMAGES.insert(image);
let xres = fz_image_xres(0, image_handle);
let yres = fz_image_yres(0, image_handle);
assert!(xres > 0);
assert!(yres > 0);
fz_drop_image(0, image_handle);
}
#[test]
fn test_image_colorspace() {
let image = Image::new(10, 10, None);
let image_handle = IMAGES.insert(image);
let cs_handle = fz_image_colorspace(0, image_handle);
assert_ne!(cs_handle, 0);
super::super::colorspace::COLORSPACES.remove(cs_handle);
fz_drop_image(0, image_handle);
}
#[test]
fn test_image_is_mask() {
let image = Image::new(10, 10, None);
let image_handle = IMAGES.insert(image);
let _is_mask = fz_image_is_mask(0, image_handle);
fz_drop_image(0, image_handle);
}
#[test]
fn test_get_pixmap_from_image() {
let image = Image::new(50, 50, None);
let image_handle = IMAGES.insert(image);
let mut w = 0i32;
let mut h = 0i32;
let pixmap_handle = fz_get_pixmap_from_image(
0,
image_handle,
std::ptr::null(),
std::ptr::null_mut(),
&mut w as *mut i32,
&mut h as *mut i32,
);
assert_ne!(pixmap_handle, 0);
assert_eq!(w, 50);
assert_eq!(h, 50);
PIXMAPS.remove(pixmap_handle);
fz_drop_image(0, image_handle);
}
#[test]
fn test_decode_image() {
let image = Image::new(20, 20, None);
let image_handle = IMAGES.insert(image);
let pixmap_handle = fz_decode_image(0, image_handle, 0, std::ptr::null());
assert_ne!(pixmap_handle, 0);
PIXMAPS.remove(pixmap_handle);
fz_drop_image(0, image_handle);
}
#[test]
fn test_decode_image_scaled() {
let image = Image::new(100, 100, None);
let image_handle = IMAGES.insert(image);
let pixmap_handle = fz_decode_image_scaled(0, image_handle, 50, 50, 0, std::ptr::null());
assert_ne!(pixmap_handle, 0);
PIXMAPS.remove(pixmap_handle);
fz_drop_image(0, image_handle);
}
#[test]
fn test_new_image_from_data_null() {
assert_eq!(
fz_new_image_from_data(
0,
10,
10,
8,
0,
96,
96,
0,
0,
std::ptr::null(),
std::ptr::null(),
std::ptr::null(),
0
),
0
);
}
#[test]
fn test_new_image_from_data_raw() {
let w = 10i32;
let h = 10i32;
let bpc = 8;
let size = ((w * h * 3 * bpc) / 8) as usize;
let data = vec![128u8; size];
let h_img = fz_new_image_from_data(
0,
w,
h,
bpc,
0,
96,
96,
0,
0,
std::ptr::null(),
std::ptr::null(),
data.as_ptr(),
size as i32,
);
if h_img != 0 {
assert_eq!(fz_image_w(0, h_img), 10);
fz_drop_image(0, h_img);
}
}
#[test]
fn test_new_image_from_data_invalid_dims() {
let data = [0u8; 100];
assert_eq!(
fz_new_image_from_data(
0,
0,
0,
8,
0,
96,
96,
0,
0,
std::ptr::null(),
std::ptr::null(),
data.as_ptr(),
100
),
0
);
}
#[test]
fn test_image_invalid_handle() {
assert_eq!(fz_image_w(0, 0), 0);
assert_eq!(fz_image_h(0, 0), 0);
assert_eq!(fz_image_xres(0, 0), 96);
assert_eq!(fz_image_yres(0, 0), 96);
assert_eq!(fz_image_colorspace(0, 0), 0);
assert_eq!(fz_image_is_mask(0, 0), 0);
assert_eq!(fz_image_bpp(0, 0), 8);
assert_eq!(fz_image_has_alpha(0, 0), 0);
assert_eq!(fz_image_orientation(0, 0), 0);
assert_eq!(fz_image_width(0, 0), 0);
assert_eq!(fz_image_height(0, 0), 0);
assert_eq!(fz_image_is_valid(0, 0), 0);
assert_eq!(fz_clone_image(0, 0), 0);
}
#[test]
fn test_get_pixmap_from_image_null_dims() {
let image = Image::new(20, 20, None);
let h = IMAGES.insert(image);
let pm = fz_get_pixmap_from_image(
0,
h,
std::ptr::null(),
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
);
assert_ne!(pm, 0);
PIXMAPS.remove(pm);
fz_drop_image(0, h);
}
#[test]
fn test_new_image_from_file_null() {
assert_eq!(fz_new_image_from_file(0, std::ptr::null()), 0);
}
#[test]
fn test_new_image_from_buffer_invalid() {
assert_eq!(fz_new_image_from_buffer(0, 99999), 0);
}
#[test]
fn test_new_image_from_buffer_data_null() {
assert_eq!(
unsafe { fz_new_image_from_buffer_data(0, std::ptr::null(), 100) },
0
);
assert_eq!(
unsafe { fz_new_image_from_buffer_data(0, [1u8].as_ptr(), 0) },
0
);
}
#[test]
fn test_fz_new_image_from_buffer_data_valid() {
let png_header = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
let h = unsafe { fz_new_image_from_buffer_data(0, png_header.as_ptr(), png_header.len()) };
if h != 0 {
fz_drop_image(0, h);
}
}
}