use super::HandleStore;
use std::ffi::{CStr, CString, c_char};
use std::sync::LazyLock;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorspaceType {
None = 0,
Gray = 1,
Rgb = 2,
Bgr = 3,
Cmyk = 4,
Lab = 5,
Indexed = 6,
Separation = 7,
Icc = 8,
}
#[derive(Debug, Clone)]
pub struct Colorspace {
pub cs_type: ColorspaceType,
pub n: i32,
pub name: String,
pub base_cs: ColorspaceHandle,
pub lookup: Vec<u8>, pub high: i32, pub colorants: Vec<CString>, pub icc_data: Vec<u8>, }
impl Default for Colorspace {
fn default() -> Self {
Self {
cs_type: ColorspaceType::None,
n: 0,
name: String::new(),
base_cs: 0,
lookup: Vec::new(),
high: 0,
colorants: Vec::new(),
icc_data: Vec::new(),
}
}
}
pub static COLORSPACES: LazyLock<HandleStore<Colorspace>> = LazyLock::new(HandleStore::default);
const CUSTOM_CS_OFFSET: ColorspaceHandle = 100;
pub type ColorspaceHandle = u64;
pub const FZ_COLORSPACE_GRAY: ColorspaceHandle = 1;
pub const FZ_COLORSPACE_RGB: ColorspaceHandle = 2;
pub const FZ_COLORSPACE_BGR: ColorspaceHandle = 3;
pub const FZ_COLORSPACE_CMYK: ColorspaceHandle = 4;
pub const FZ_COLORSPACE_LAB: ColorspaceHandle = 5;
fn colorspace_n(handle: ColorspaceHandle) -> i32 {
match handle {
FZ_COLORSPACE_GRAY => 1,
FZ_COLORSPACE_RGB | FZ_COLORSPACE_BGR => 3,
FZ_COLORSPACE_CMYK => 4,
FZ_COLORSPACE_LAB => 3,
h if h >= CUSTOM_CS_OFFSET => {
let real_handle = h - CUSTOM_CS_OFFSET;
if let Some(cs) = COLORSPACES.get(real_handle) {
if let Ok(guard) = cs.lock() {
return guard.n;
}
}
0
}
_ => 0,
}
}
fn colorspace_type(handle: ColorspaceHandle) -> ColorspaceType {
match handle {
FZ_COLORSPACE_GRAY => ColorspaceType::Gray,
FZ_COLORSPACE_RGB => ColorspaceType::Rgb,
FZ_COLORSPACE_BGR => ColorspaceType::Bgr,
FZ_COLORSPACE_CMYK => ColorspaceType::Cmyk,
FZ_COLORSPACE_LAB => ColorspaceType::Lab,
h if h >= CUSTOM_CS_OFFSET => {
let real_handle = h - CUSTOM_CS_OFFSET;
if let Some(cs) = COLORSPACES.get(real_handle) {
if let Ok(guard) = cs.lock() {
return guard.cs_type;
}
}
ColorspaceType::None
}
_ => ColorspaceType::None,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_device_gray(_ctx: super::Handle) -> ColorspaceHandle {
FZ_COLORSPACE_GRAY
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_device_rgb(_ctx: super::Handle) -> ColorspaceHandle {
FZ_COLORSPACE_RGB
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_device_bgr(_ctx: super::Handle) -> ColorspaceHandle {
FZ_COLORSPACE_BGR
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_device_cmyk(_ctx: super::Handle) -> ColorspaceHandle {
FZ_COLORSPACE_CMYK
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_device_lab(_ctx: super::Handle) -> ColorspaceHandle {
FZ_COLORSPACE_LAB
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_keep_colorspace(
_ctx: super::Handle,
cs: ColorspaceHandle,
) -> ColorspaceHandle {
cs }
#[unsafe(no_mangle)]
pub extern "C" fn fz_drop_colorspace(_ctx: super::Handle, cs: ColorspaceHandle) {
if cs >= CUSTOM_CS_OFFSET {
let real_handle = cs - CUSTOM_CS_OFFSET;
let _ = COLORSPACES.remove(real_handle);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_n(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
colorspace_n(cs)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_is_gray(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
i32::from(colorspace_type(cs) == ColorspaceType::Gray)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_is_rgb(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
i32::from(colorspace_type(cs) == ColorspaceType::Rgb)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_is_cmyk(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
i32::from(colorspace_type(cs) == ColorspaceType::Cmyk)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_is_lab(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
i32::from(colorspace_type(cs) == ColorspaceType::Lab)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_is_device(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
i32::from((FZ_COLORSPACE_GRAY..=FZ_COLORSPACE_LAB).contains(&cs))
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_is_indexed(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
i32::from(colorspace_type(cs) == ColorspaceType::Indexed)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_is_device_n(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
i32::from(colorspace_type(cs) == ColorspaceType::Separation)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_is_subtractive(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
match colorspace_type(cs) {
ColorspaceType::Cmyk | ColorspaceType::Separation => 1,
_ => 0,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_device_n_has_cmyk(
_ctx: super::Handle,
cs: ColorspaceHandle,
) -> i32 {
if cs == FZ_COLORSPACE_CMYK {
return 1;
}
if cs < CUSTOM_CS_OFFSET {
return 0;
}
let real_handle = cs - CUSTOM_CS_OFFSET;
if let Some(entry) = COLORSPACES.get(real_handle) {
if let Ok(guard) = entry.lock() {
if guard.cs_type != ColorspaceType::Separation {
return 0;
}
if guard.base_cs == FZ_COLORSPACE_CMYK {
return 1;
}
let name_lower = guard.name.to_ascii_lowercase();
if name_lower.contains("cyan")
|| name_lower.contains("magenta")
|| name_lower.contains("yellow")
|| name_lower.contains("black")
{
return 1;
}
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_device_n_has_only_cmyk(
_ctx: super::Handle,
cs: ColorspaceHandle,
) -> i32 {
if cs == FZ_COLORSPACE_CMYK {
return 1;
}
if cs < CUSTOM_CS_OFFSET {
return 0;
}
let real_handle = cs - CUSTOM_CS_OFFSET;
if let Some(entry) = COLORSPACES.get(real_handle) {
if let Ok(guard) = entry.lock() {
if guard.cs_type != ColorspaceType::Separation {
return 0;
}
let cmyk = ["cyan", "magenta", "yellow", "black"];
let colorants: Vec<&str> = guard.name.split(',').map(|s| s.trim()).collect();
if colorants.is_empty() {
return 0;
}
for colorant in &colorants {
if !cmyk.contains(&colorant.to_ascii_lowercase().as_str()) {
return 0;
}
}
return 1;
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_name(_ctx: super::Handle, cs: ColorspaceHandle) -> *const c_char {
match cs {
FZ_COLORSPACE_GRAY => c"DeviceGray".as_ptr(),
FZ_COLORSPACE_RGB => c"DeviceRGB".as_ptr(),
FZ_COLORSPACE_BGR => c"DeviceBGR".as_ptr(),
FZ_COLORSPACE_CMYK => c"DeviceCMYK".as_ptr(),
FZ_COLORSPACE_LAB => c"Lab".as_ptr(),
_ => c"Unknown".as_ptr(),
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_indexed_colorspace(
_ctx: super::Handle,
base: ColorspaceHandle,
high: i32,
lookup: *const u8,
) -> ColorspaceHandle {
let base_n = colorspace_n(base);
if base_n == 0 || high < 0 {
return 0;
}
let lookup_size = ((high + 1) * base_n) as usize;
let lookup_data = if lookup.is_null() || lookup_size == 0 {
vec![0u8; lookup_size]
} else {
unsafe { std::slice::from_raw_parts(lookup, lookup_size) }.to_vec()
};
let cs = Colorspace {
cs_type: ColorspaceType::Indexed,
n: 1, name: format!("Indexed({})", high),
base_cs: base,
lookup: lookup_data,
high,
colorants: Vec::new(),
icc_data: Vec::new(),
};
COLORSPACES.insert(cs) + CUSTOM_CS_OFFSET
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_device_n_colorspace(
_ctx: super::Handle,
base: ColorspaceHandle,
n: i32,
colorants: *const *const c_char,
) -> ColorspaceHandle {
if n <= 0 || n > 32 {
return 0;
}
let mut colorant_cstrings = Vec::with_capacity(n as usize);
let mut name_parts = Vec::with_capacity(n as usize);
if !colorants.is_null() {
for i in 0..n {
let ptr = unsafe { *colorants.add(i as usize) };
if ptr.is_null() {
colorant_cstrings.push(CString::new("").unwrap());
} else {
let c_str = unsafe { CStr::from_ptr(ptr) };
let s = c_str.to_string_lossy().into_owned();
name_parts.push(s.clone());
colorant_cstrings
.push(CString::new(s).unwrap_or_else(|_| CString::new("").unwrap()));
}
}
}
let name = if name_parts.is_empty() {
format!("DeviceN({})", n)
} else {
name_parts.join(",")
};
let cs = Colorspace {
cs_type: ColorspaceType::Separation,
n,
name,
base_cs: base,
lookup: Vec::new(),
high: 0,
colorants: colorant_cstrings,
icc_data: Vec::new(),
};
COLORSPACES.insert(cs) + CUSTOM_CS_OFFSET
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_icc_colorspace(
_ctx: super::Handle,
type_hint: i32, _flags: i32,
name: *const c_char,
data: *const u8,
size: usize,
) -> ColorspaceHandle {
let n = match type_hint {
1 => 1, 2 | 3 => 3, 4 => 4, _ => 3,
};
let cs_name = if name.is_null() {
"ICCBased".to_string()
} else {
let c_str = unsafe { CStr::from_ptr(name) };
c_str.to_str().unwrap_or("ICCBased").to_string()
};
let icc_data = if data.is_null() || size == 0 {
Vec::new()
} else {
unsafe { std::slice::from_raw_parts(data, size) }.to_vec()
};
let cs = Colorspace {
cs_type: ColorspaceType::Icc,
n,
name: cs_name,
base_cs: FZ_COLORSPACE_RGB,
lookup: Vec::new(),
high: 0,
colorants: Vec::new(),
icc_data,
};
COLORSPACES.insert(cs) + CUSTOM_CS_OFFSET
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_base(
_ctx: super::Handle,
cs: ColorspaceHandle,
) -> ColorspaceHandle {
if cs < CUSTOM_CS_OFFSET {
return 0; }
let real_handle = cs - CUSTOM_CS_OFFSET;
if let Some(colorspace) = COLORSPACES.get(real_handle) {
if let Ok(guard) = colorspace.lock() {
return guard.base_cs;
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_high(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
if cs < CUSTOM_CS_OFFSET {
return -1;
}
let real_handle = cs - CUSTOM_CS_OFFSET;
if let Some(colorspace) = COLORSPACES.get(real_handle) {
if let Ok(guard) = colorspace.lock() {
if guard.cs_type == ColorspaceType::Indexed {
return guard.high;
}
}
}
-1
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_lookup(_ctx: super::Handle, cs: ColorspaceHandle) -> *const u8 {
if cs < CUSTOM_CS_OFFSET {
return std::ptr::null();
}
let real_handle = cs - CUSTOM_CS_OFFSET;
if let Some(entry) = COLORSPACES.get(real_handle) {
if let Ok(guard) = entry.lock() {
if guard.cs_type == ColorspaceType::Indexed && !guard.lookup.is_empty() {
return guard.lookup.as_ptr();
}
}
}
std::ptr::null()
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_convert_color(
_ctx: super::Handle,
src_cs: ColorspaceHandle,
src: *const f32,
dst_cs: ColorspaceHandle,
dst: *mut f32,
_proof_cs: ColorspaceHandle,
) {
if src.is_null() || dst.is_null() {
return;
}
let src_n = colorspace_n(src_cs) as usize;
let dst_n = colorspace_n(dst_cs) as usize;
if src_n == 0 || dst_n == 0 {
return;
}
let (src_slice, dst_slice) = unsafe {
(
std::slice::from_raw_parts(src, src_n),
std::slice::from_raw_parts_mut(dst, dst_n),
)
};
match (colorspace_type(src_cs), colorspace_type(dst_cs)) {
(ColorspaceType::Gray, ColorspaceType::Rgb) => {
let g = src_slice[0];
dst_slice[0] = g;
dst_slice[1] = g;
dst_slice[2] = g;
}
(ColorspaceType::Rgb, ColorspaceType::Gray) => {
dst_slice[0] = src_slice[0] * 0.299 + src_slice[1] * 0.587 + src_slice[2] * 0.114;
}
(ColorspaceType::Rgb, ColorspaceType::Cmyk) => {
let r = src_slice[0];
let g = src_slice[1];
let b = src_slice[2];
let k = 1.0 - r.max(g).max(b);
if k < 1.0 {
let inv_k = 1.0 / (1.0 - k);
dst_slice[0] = (1.0 - r - k) * inv_k;
dst_slice[1] = (1.0 - g - k) * inv_k;
dst_slice[2] = (1.0 - b - k) * inv_k;
} else {
dst_slice[0] = 0.0;
dst_slice[1] = 0.0;
dst_slice[2] = 0.0;
}
dst_slice[3] = k;
}
(ColorspaceType::Cmyk, ColorspaceType::Rgb) => {
let c = src_slice[0];
let m = src_slice[1];
let y = src_slice[2];
let k = src_slice[3];
dst_slice[0] = (1.0 - c) * (1.0 - k);
dst_slice[1] = (1.0 - m) * (1.0 - k);
dst_slice[2] = (1.0 - y) * (1.0 - k);
}
_ if src_cs == dst_cs => {
dst_slice[..src_n.min(dst_n)].copy_from_slice(&src_slice[..src_n.min(dst_n)]);
}
_ => {
dst_slice.fill(0.0);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_clone_colorspace(
_ctx: super::Handle,
cs: ColorspaceHandle,
) -> ColorspaceHandle {
fz_keep_colorspace(_ctx, cs)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_eq(
_ctx: super::Handle,
a: ColorspaceHandle,
b: ColorspaceHandle,
) -> i32 {
if a == b { 1 } else { 0 }
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_type(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
colorspace_type(cs) as i32
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_is_icc(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
let cs_type = colorspace_type(cs);
if cs_type == ColorspaceType::Icc { 1 } else { 0 }
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_clamp_color(
_ctx: super::Handle,
cs: ColorspaceHandle,
color_in: *const f32,
color_out: *mut f32,
) {
if color_in.is_null() || color_out.is_null() {
return;
}
let n = colorspace_n(cs) as usize;
if n == 0 {
return;
}
unsafe {
let input = std::slice::from_raw_parts(color_in, n);
let output = std::slice::from_raw_parts_mut(color_out, n);
for i in 0..n {
output[i] = input[i].clamp(0.0, 1.0);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_colorant(
_ctx: super::Handle,
cs: ColorspaceHandle,
idx: i32,
) -> *const c_char {
if idx < 0 {
return std::ptr::null();
}
let idx = idx as usize;
match cs {
FZ_COLORSPACE_GRAY => match idx {
0 => c"Gray".as_ptr(),
_ => std::ptr::null(),
},
FZ_COLORSPACE_RGB => match idx {
0 => c"Red".as_ptr(),
1 => c"Green".as_ptr(),
2 => c"Blue".as_ptr(),
_ => std::ptr::null(),
},
FZ_COLORSPACE_BGR => match idx {
0 => c"Blue".as_ptr(),
1 => c"Green".as_ptr(),
2 => c"Red".as_ptr(),
_ => std::ptr::null(),
},
FZ_COLORSPACE_CMYK => match idx {
0 => c"Cyan".as_ptr(),
1 => c"Magenta".as_ptr(),
2 => c"Yellow".as_ptr(),
3 => c"Black".as_ptr(),
_ => std::ptr::null(),
},
FZ_COLORSPACE_LAB => match idx {
0 => c"L".as_ptr(),
1 => c"a".as_ptr(),
2 => c"b".as_ptr(),
_ => std::ptr::null(),
},
h if h >= CUSTOM_CS_OFFSET => {
let real_handle = h - CUSTOM_CS_OFFSET;
if let Some(entry) = COLORSPACES.get(real_handle) {
if let Ok(guard) = entry.lock() {
if idx < guard.colorants.len() {
return guard.colorants[idx].as_ptr();
}
}
}
std::ptr::null()
}
_ => std::ptr::null(),
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_num_colorants(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
if colorspace_type(cs) == ColorspaceType::Separation {
colorspace_n(cs)
} else {
0
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_base_n(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
let base = fz_colorspace_base(_ctx, cs);
colorspace_n(base)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_convert_pixel(
_ctx: super::Handle,
src_cs: ColorspaceHandle,
src: *const f32,
dst_cs: ColorspaceHandle,
dst: *mut f32,
) {
fz_convert_color(_ctx, src_cs, src, dst_cs, dst, 0)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_device_srgb(_ctx: super::Handle) -> ColorspaceHandle {
FZ_COLORSPACE_RGB
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_device_grayscale(_ctx: super::Handle) -> ColorspaceHandle {
FZ_COLORSPACE_GRAY
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_has_spots(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
let cs_type = colorspace_type(cs);
if cs_type == ColorspaceType::Separation {
1
} else {
0
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_n_spots(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
if fz_colorspace_has_spots(_ctx, cs) != 0 {
1
} else {
0
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_name_string(
_ctx: super::Handle,
cs: ColorspaceHandle,
) -> *const c_char {
fz_colorspace_name(_ctx, cs)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_is_valid(_ctx: super::Handle, cs: ColorspaceHandle) -> i32 {
if cs == 0 {
0
} else if cs <= 5 || cs >= CUSTOM_CS_OFFSET {
1
} else {
0
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_colorspace_max(_ctx: super::Handle, cs: ColorspaceHandle) -> f32 {
if colorspace_type(cs) == ColorspaceType::Indexed {
fz_colorspace_high(_ctx, cs) as f32
} else {
1.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_device_colorspaces() {
assert_eq!(fz_colorspace_n(0, FZ_COLORSPACE_GRAY), 1);
assert_eq!(fz_colorspace_n(0, FZ_COLORSPACE_RGB), 3);
assert_eq!(fz_colorspace_n(0, FZ_COLORSPACE_CMYK), 4);
}
#[test]
fn test_colorspace_checks() {
assert_eq!(fz_colorspace_is_gray(0, FZ_COLORSPACE_GRAY), 1);
assert_eq!(fz_colorspace_is_rgb(0, FZ_COLORSPACE_RGB), 1);
assert_eq!(fz_colorspace_is_cmyk(0, FZ_COLORSPACE_CMYK), 1);
}
#[test]
fn test_colorspace_is_gray_negative() {
assert_eq!(fz_colorspace_is_gray(0, FZ_COLORSPACE_RGB), 0);
assert_eq!(fz_colorspace_is_gray(0, FZ_COLORSPACE_CMYK), 0);
}
#[test]
fn test_colorspace_is_rgb_negative() {
assert_eq!(fz_colorspace_is_rgb(0, FZ_COLORSPACE_GRAY), 0);
assert_eq!(fz_colorspace_is_rgb(0, FZ_COLORSPACE_CMYK), 0);
}
#[test]
fn test_colorspace_is_cmyk_negative() {
assert_eq!(fz_colorspace_is_cmyk(0, FZ_COLORSPACE_GRAY), 0);
assert_eq!(fz_colorspace_is_cmyk(0, FZ_COLORSPACE_RGB), 0);
}
#[test]
fn test_colorspace_type() {
assert!(matches!(
colorspace_type(FZ_COLORSPACE_GRAY),
ColorspaceType::Gray
));
assert!(matches!(
colorspace_type(FZ_COLORSPACE_RGB),
ColorspaceType::Rgb
));
assert!(matches!(
colorspace_type(FZ_COLORSPACE_CMYK),
ColorspaceType::Cmyk
));
assert!(matches!(colorspace_type(99), ColorspaceType::None));
}
#[test]
fn test_colorspace_n() {
assert_eq!(colorspace_n(FZ_COLORSPACE_GRAY), 1);
assert_eq!(colorspace_n(FZ_COLORSPACE_RGB), 3);
assert_eq!(colorspace_n(FZ_COLORSPACE_CMYK), 4);
assert_eq!(colorspace_n(99), 0);
}
#[test]
fn test_device_gray_handle() {
let handle = fz_device_gray(0);
assert_eq!(handle, FZ_COLORSPACE_GRAY);
}
#[test]
fn test_device_rgb_handle() {
let handle = fz_device_rgb(0);
assert_eq!(handle, FZ_COLORSPACE_RGB);
}
#[test]
fn test_device_cmyk_handle() {
let handle = fz_device_cmyk(0);
assert_eq!(handle, FZ_COLORSPACE_CMYK);
}
#[test]
fn test_keep_drop_colorspace() {
let handle = fz_keep_colorspace(0, FZ_COLORSPACE_RGB);
assert_eq!(handle, FZ_COLORSPACE_RGB);
fz_drop_colorspace(0, handle);
}
#[test]
fn test_convert_color_gray_to_rgb() {
let src = [0.5f32];
let mut dst = [0.0f32; 3];
fz_convert_color(
0,
FZ_COLORSPACE_GRAY,
src.as_ptr(),
FZ_COLORSPACE_RGB,
dst.as_mut_ptr(),
0,
);
assert!((dst[0] - 0.5).abs() < 0.01);
assert!((dst[1] - 0.5).abs() < 0.01);
assert!((dst[2] - 0.5).abs() < 0.01);
}
#[test]
fn test_convert_color_rgb_to_gray() {
let src = [1.0f32, 1.0, 1.0]; let mut dst = [0.0f32];
fz_convert_color(
0,
FZ_COLORSPACE_RGB,
src.as_ptr(),
FZ_COLORSPACE_GRAY,
dst.as_mut_ptr(),
0,
);
assert!((dst[0] - 1.0).abs() < 0.01);
}
#[test]
fn test_convert_color_rgb_to_cmyk() {
let src = [1.0f32, 0.0, 0.0]; let mut dst = [0.0f32; 4];
fz_convert_color(
0,
FZ_COLORSPACE_RGB,
src.as_ptr(),
FZ_COLORSPACE_CMYK,
dst.as_mut_ptr(),
0,
);
assert!(dst[0] < 0.1); assert_eq!(dst[3], 0.0); }
#[test]
fn test_convert_color_cmyk_to_rgb() {
let src = [0.0f32, 0.0, 0.0, 0.0]; let mut dst = [0.0f32; 3];
fz_convert_color(
0,
FZ_COLORSPACE_CMYK,
src.as_ptr(),
FZ_COLORSPACE_RGB,
dst.as_mut_ptr(),
0,
);
assert!((dst[0] - 1.0).abs() < 0.01);
assert!((dst[1] - 1.0).abs() < 0.01);
assert!((dst[2] - 1.0).abs() < 0.01);
}
#[test]
fn test_convert_color_same_colorspace() {
let src = [0.25f32, 0.5, 0.75];
let mut dst = [0.0f32; 3];
fz_convert_color(
0,
FZ_COLORSPACE_RGB,
src.as_ptr(),
FZ_COLORSPACE_RGB,
dst.as_mut_ptr(),
0,
);
assert_eq!(dst, src);
}
#[test]
fn test_convert_color_null_pointers() {
fz_convert_color(
0,
FZ_COLORSPACE_RGB,
std::ptr::null(),
FZ_COLORSPACE_GRAY,
std::ptr::null_mut(),
0,
);
}
#[test]
fn test_convert_color_invalid_colorspace() {
let src = [0.5f32];
let mut dst = [1.0f32; 3];
fz_convert_color(0, 99, src.as_ptr(), FZ_COLORSPACE_RGB, dst.as_mut_ptr(), 0);
}
#[test]
fn test_colorspace_is_device() {
assert_eq!(fz_colorspace_is_device(0, FZ_COLORSPACE_GRAY), 1);
assert_eq!(fz_colorspace_is_device(0, FZ_COLORSPACE_RGB), 1);
assert_eq!(fz_colorspace_is_device(0, FZ_COLORSPACE_CMYK), 1);
}
#[test]
fn test_colorspace_is_indexed() {
assert_eq!(fz_colorspace_is_indexed(0, FZ_COLORSPACE_RGB), 0);
assert_eq!(fz_colorspace_is_indexed(0, FZ_COLORSPACE_GRAY), 0);
}
#[test]
fn test_colorspace_is_device_n() {
assert_eq!(fz_colorspace_is_device_n(0, FZ_COLORSPACE_RGB), 0);
assert_eq!(fz_colorspace_is_device_n(0, FZ_COLORSPACE_CMYK), 0);
}
#[test]
fn test_colorspace_is_subtractive() {
assert_eq!(fz_colorspace_is_subtractive(0, FZ_COLORSPACE_CMYK), 1);
assert_eq!(fz_colorspace_is_subtractive(0, FZ_COLORSPACE_RGB), 0);
assert_eq!(fz_colorspace_is_subtractive(0, FZ_COLORSPACE_GRAY), 0);
}
#[test]
fn test_new_indexed_colorspace() {
let lookup = [
255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 0, ];
let cs = fz_new_indexed_colorspace(0, FZ_COLORSPACE_RGB, 3, lookup.as_ptr());
assert!(cs >= CUSTOM_CS_OFFSET);
assert_eq!(fz_colorspace_is_indexed(0, cs), 1);
assert_eq!(fz_colorspace_n(0, cs), 1);
assert_eq!(fz_colorspace_base(0, cs), FZ_COLORSPACE_RGB);
assert_eq!(fz_colorspace_high(0, cs), 3);
}
#[test]
fn test_new_indexed_colorspace_null_lookup() {
let cs = fz_new_indexed_colorspace(0, FZ_COLORSPACE_RGB, 3, std::ptr::null());
assert!(cs >= CUSTOM_CS_OFFSET);
}
#[test]
fn test_new_indexed_colorspace_invalid() {
let cs1 = fz_new_indexed_colorspace(0, 99, 3, std::ptr::null());
assert_eq!(cs1, 0);
let cs2 = fz_new_indexed_colorspace(0, FZ_COLORSPACE_RGB, -1, std::ptr::null());
assert_eq!(cs2, 0);
}
#[test]
fn test_new_device_n_colorspace() {
let cs = fz_new_device_n_colorspace(0, FZ_COLORSPACE_CMYK, 2, std::ptr::null());
assert!(cs >= CUSTOM_CS_OFFSET);
assert_eq!(fz_colorspace_is_device_n(0, cs), 1);
assert_eq!(fz_colorspace_n(0, cs), 2);
}
#[test]
fn test_new_device_n_colorspace_invalid() {
let cs1 = fz_new_device_n_colorspace(0, FZ_COLORSPACE_CMYK, 0, std::ptr::null());
assert_eq!(cs1, 0);
let cs2 = fz_new_device_n_colorspace(0, FZ_COLORSPACE_CMYK, 100, std::ptr::null());
assert_eq!(cs2, 0);
}
#[test]
fn test_new_icc_colorspace() {
let cs = fz_new_icc_colorspace(0, 0, 0, std::ptr::null(), std::ptr::null(), 0);
assert!(cs >= CUSTOM_CS_OFFSET);
assert_eq!(fz_colorspace_n(0, cs), 3);
}
#[test]
fn test_new_icc_colorspace_with_name() {
let name = c"sRGB IEC61966-2.1";
let cs = fz_new_icc_colorspace(0, 0, 0, name.as_ptr(), std::ptr::null(), 0);
assert!(cs >= CUSTOM_CS_OFFSET);
}
#[test]
fn test_colorspace_base_device() {
assert_eq!(fz_colorspace_base(0, FZ_COLORSPACE_RGB), 0);
assert_eq!(fz_colorspace_base(0, FZ_COLORSPACE_GRAY), 0);
}
#[test]
fn test_colorspace_high_device() {
assert_eq!(fz_colorspace_high(0, FZ_COLORSPACE_RGB), -1);
assert_eq!(fz_colorspace_high(0, FZ_COLORSPACE_GRAY), -1);
}
#[test]
fn test_colorspace_lookup_returns_null_for_non_indexed() {
assert!(fz_colorspace_lookup(0, FZ_COLORSPACE_RGB).is_null());
}
#[test]
fn test_colorspace_lookup_returns_pointer_for_indexed() {
let lookup = [255u8, 0, 0, 0, 255, 0, 0, 0, 255];
let cs = fz_new_indexed_colorspace(0, FZ_COLORSPACE_RGB, 2, lookup.as_ptr());
assert!(cs >= CUSTOM_CS_OFFSET);
let ptr = fz_colorspace_lookup(0, cs);
assert!(!ptr.is_null());
assert_eq!(unsafe { *ptr }, 255);
fz_drop_colorspace(0, cs);
}
}