diff options
Diffstat (limited to 'gfx/qcms/src/c_bindings.rs')
-rw-r--r-- | gfx/qcms/src/c_bindings.rs | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/gfx/qcms/src/c_bindings.rs b/gfx/qcms/src/c_bindings.rs new file mode 100644 index 0000000000..efe6674fe5 --- /dev/null +++ b/gfx/qcms/src/c_bindings.rs @@ -0,0 +1,507 @@ +#![allow(clippy::missing_safety_doc)] + +use std::{ptr::null_mut, slice}; + +use libc::{fclose, fopen, fread, free, malloc, memset, FILE}; + +use crate::{ + double_to_s15Fixed16Number, + iccread::*, + s15Fixed16Number_to_float, + transform::get_rgb_colorants, + transform::DataType, + transform::{qcms_transform, transform_create}, + transform_util, + Intent, +}; + +#[no_mangle] +pub extern "C" fn qcms_profile_sRGB() -> *mut Profile { + let profile = Profile::new_sRGB(); + Box::into_raw(profile) +} + +//XXX: it would be nice if we had a way of ensuring +// everything in a profile was initialized regardless of how it was created +//XXX: should this also be taking a black_point? +/* similar to CGColorSpaceCreateCalibratedRGB */ +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_create_rgb_with_gamma_set( + white_point: qcms_CIE_xyY, + primaries: qcms_CIE_xyYTRIPLE, + redGamma: f32, + greenGamma: f32, + blueGamma: f32, +) -> *mut Profile { + let profile = + Profile::new_rgb_with_gamma_set(white_point, primaries, redGamma, greenGamma, blueGamma); + profile.map_or_else(null_mut, Box::into_raw) +} + +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_create_gray_with_gamma(gamma: f32) -> *mut Profile { + let profile = Profile::new_gray_with_gamma(gamma); + Box::into_raw(profile) +} + +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_create_rgb_with_gamma( + white_point: qcms_CIE_xyY, + primaries: qcms_CIE_xyYTRIPLE, + gamma: f32, +) -> *mut Profile { + qcms_profile_create_rgb_with_gamma_set(white_point, primaries, gamma, gamma, gamma) +} + +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_create_rgb_with_table( + white_point: qcms_CIE_xyY, + primaries: qcms_CIE_xyYTRIPLE, + table: *const u16, + num_entries: i32, +) -> *mut Profile { + let table = slice::from_raw_parts(table, num_entries as usize); + let profile = Profile::new_rgb_with_table(white_point, primaries, table); + profile.map_or_else(null_mut, Box::into_raw) +} + +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_create_cicp( + colour_primaries: u8, + transfer_characteristics: u8, +) -> *mut Profile { + Profile::new_cicp(colour_primaries.into(), transfer_characteristics.into()) + .map_or_else(null_mut, Box::into_raw) +} + +/* qcms_profile_from_memory does not hold a reference to the memory passed in */ +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_from_memory( + mem: *const libc::c_void, + size: usize, +) -> *mut Profile { + let mem = slice::from_raw_parts(mem as *const libc::c_uchar, size); + let profile = Profile::new_from_slice(mem, false); + profile.map_or_else(null_mut, Box::into_raw) +} + +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_from_memory_curves_only( + mem: *const libc::c_void, + size: usize, +) -> *mut Profile { + let mem = slice::from_raw_parts(mem as *const libc::c_uchar, size); + let profile = Profile::new_from_slice(mem, true); + profile.map_or_else(null_mut, Box::into_raw) +} + + +#[no_mangle] +pub extern "C" fn qcms_profile_get_rendering_intent(profile: &Profile) -> Intent { + profile.rendering_intent +} +#[no_mangle] +pub extern "C" fn qcms_profile_get_color_space(profile: &Profile) -> icColorSpaceSignature { + profile.color_space +} +#[no_mangle] +pub extern "C" fn qcms_profile_is_sRGB(profile: &Profile) -> bool { + profile.is_sRGB() +} + +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_release(profile: *mut Profile) { + drop(Box::from_raw(profile)); +} +unsafe extern "C" fn qcms_data_from_file( + file: *mut FILE, + mem: *mut *mut libc::c_void, + size: *mut usize, +) { + let length: u32; + let remaining_length: u32; + let read_length: usize; + let mut length_be: u32 = 0; + let data: *mut libc::c_void; + *mem = std::ptr::null_mut::<libc::c_void>(); + *size = 0; + if fread( + &mut length_be as *mut u32 as *mut libc::c_void, + 1, + ::std::mem::size_of::<u32>(), + file, + ) != ::std::mem::size_of::<u32>() + { + return; + } + length = u32::from_be(length_be); + if length > MAX_PROFILE_SIZE as libc::c_uint + || (length as libc::c_ulong) < ::std::mem::size_of::<u32>() as libc::c_ulong + { + return; + } + /* allocate room for the entire profile */ + data = malloc(length as usize); + if data.is_null() { + return; + } + /* copy in length to the front so that the buffer will contain the entire profile */ + *(data as *mut u32) = length_be; + remaining_length = + (length as libc::c_ulong - ::std::mem::size_of::<u32>() as libc::c_ulong) as u32; + /* read the rest profile */ + read_length = fread( + (data as *mut libc::c_uchar).add(::std::mem::size_of::<u32>()) as *mut libc::c_void, + 1, + remaining_length as usize, + file, + ) as usize; + if read_length != remaining_length as usize { + free(data); + return; + } + /* successfully get the profile.*/ + *mem = data; + *size = length as usize; +} + +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_from_file(file: *mut FILE) -> *mut Profile { + let mut length: usize = 0; + let profile: *mut Profile; + let mut data: *mut libc::c_void = std::ptr::null_mut::<libc::c_void>(); + qcms_data_from_file(file, &mut data, &mut length); + if data.is_null() || length == 0 { + return std::ptr::null_mut::<Profile>(); + } + profile = qcms_profile_from_memory(data, length); + free(data); + profile +} +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_from_path(path: *const libc::c_char) -> *mut Profile { + if let Ok(Some(boxed_profile)) = std::ffi::CStr::from_ptr(path) + .to_str() + .map(Profile::new_from_path) + { + Box::into_raw(boxed_profile) + } else { + std::ptr::null_mut() + } +} + +#[no_mangle] +pub unsafe extern "C" fn qcms_data_from_path( + path: *const libc::c_char, + mem: *mut *mut libc::c_void, + size: *mut usize, +) { + *mem = std::ptr::null_mut::<libc::c_void>(); + *size = 0; + let file = fopen(path, b"rb\x00" as *const u8 as *const libc::c_char); + if !file.is_null() { + qcms_data_from_file(file, mem, size); + fclose(file); + }; +} + +#[cfg(windows)] +extern "C" { + pub fn _wfopen(filename: *const libc::wchar_t, mode: *const libc::wchar_t) -> *mut FILE; +} + +#[cfg(windows)] +#[no_mangle] +pub unsafe extern "C" fn qcms_profile_from_unicode_path(path: *const libc::wchar_t) { + let file = _wfopen(path, ['r' as u16, 'b' as u16, '\0' as u16].as_ptr()); + if !file.is_null() { + qcms_profile_from_file(file); + fclose(file); + }; +} + +#[cfg(windows)] +#[no_mangle] +pub unsafe extern "C" fn qcms_data_from_unicode_path( + path: *const libc::wchar_t, + mem: *mut *mut libc::c_void, + size: *mut usize, +) { + *mem = 0 as *mut libc::c_void; + *size = 0; + let file = _wfopen(path, ['r' as u16, 'b' as u16, '\0' as u16].as_ptr()); + if !file.is_null() { + qcms_data_from_file(file, mem, size); + fclose(file); + }; +} + +#[no_mangle] +pub extern "C" fn qcms_transform_create( + in_0: &Profile, + in_type: DataType, + out: &Profile, + out_type: DataType, + intent: Intent, +) -> *mut qcms_transform { + let transform = transform_create(in_0, in_type, out, out_type, intent); + match transform { + Some(transform) => Box::into_raw(transform), + None => null_mut(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn qcms_data_create_rgb_with_gamma( + white_point: qcms_CIE_xyY, + primaries: qcms_CIE_xyYTRIPLE, + gamma: f32, + mem: *mut *mut libc::c_void, + size: *mut usize, +) { + let length: u32; + let mut index: u32; + let xyz_count: u32; + let trc_count: u32; + let mut tag_table_offset: usize; + let mut tag_data_offset: usize; + let data: *mut libc::c_void; + + let TAG_XYZ: [u32; 3] = [TAG_rXYZ, TAG_gXYZ, TAG_bXYZ]; + let TAG_TRC: [u32; 3] = [TAG_rTRC, TAG_gTRC, TAG_bTRC]; + if mem.is_null() || size.is_null() { + return; + } + *mem = std::ptr::null_mut::<libc::c_void>(); + *size = 0; + /* + * total length = icc profile header(128) + tag count(4) + + * (tag table item (12) * total tag (6 = 3 rTRC + 3 rXYZ)) + rTRC elements data (3 * 20) + * + rXYZ elements data (3*16), and all tag data elements must start at the 4-byte boundary. + */ + xyz_count = 3; // rXYZ, gXYZ, bXYZ + trc_count = 3; // rTRC, gTRC, bTRC + length = + (128 + 4) as libc::c_uint + 12 * (xyz_count + trc_count) + xyz_count * 20 + trc_count * 16; + // reserve the total memory. + data = malloc(length as usize); + if data.is_null() { + return; + } + memset(data, 0, length as usize); + // Part1 : write rXYZ, gXYZ and bXYZ + let colorants = match get_rgb_colorants(white_point, primaries) { + Some(colorants) => colorants, + None => { + free(data); + return; + } + }; + + let data = std::slice::from_raw_parts_mut(data as *mut u8, length as usize); + // the position of first tag's signature in tag table + tag_table_offset = (128 + 4) as usize; // the start of tag data elements. + tag_data_offset = ((128 + 4) as libc::c_uint + 12 * (xyz_count + trc_count)) as usize; + index = 0; + while index < xyz_count { + // tag table + write_u32(data, tag_table_offset, TAG_XYZ[index as usize]); // 20 bytes per TAG_(r/g/b)XYZ tag element + write_u32(data, tag_table_offset + 4, tag_data_offset as u32); + write_u32(data, tag_table_offset + 8, 20); + // tag data element + write_u32(data, tag_data_offset, XYZ_TYPE); + // reserved 4 bytes. + write_u32( + data, + tag_data_offset + 8, + double_to_s15Fixed16Number(colorants.m[0][index as usize] as f64) as u32, + ); + write_u32( + data, + tag_data_offset + 12, + double_to_s15Fixed16Number(colorants.m[1][index as usize] as f64) as u32, + ); + write_u32( + data, + tag_data_offset + 16, + double_to_s15Fixed16Number(colorants.m[2][index as usize] as f64) as u32, + ); + tag_table_offset += 12; + tag_data_offset += 20; + index += 1 + } + // Part2 : write rTRC, gTRC and bTRC + index = 0; + while index < trc_count { + // tag table + write_u32(data, tag_table_offset, TAG_TRC[index as usize]); // 14 bytes per TAG_(r/g/b)TRC element + write_u32(data, tag_table_offset + 4, tag_data_offset as u32); + write_u32(data, tag_table_offset + 8, 14); + // tag data element + write_u32(data, tag_data_offset, CURVE_TYPE); + // reserved 4 bytes. + write_u32(data, tag_data_offset + 8, 1); // count + write_u16(data, tag_data_offset + 12, float_to_u8Fixed8Number(gamma)); + tag_table_offset += 12; + tag_data_offset += 16; + index += 1 + } + /* Part3 : write profile header + * + * Important header fields are left empty. This generates a profile for internal use only. + * We should be generating: Profile version (04300000h), Profile signature (acsp), + * PCS illumiant field. Likewise mandatory profile tags are omitted. + */ + write_u32(data, 0, length); // the total length of this memory + write_u32(data, 12, DISPLAY_DEVICE_PROFILE); // profile->class_type + write_u32(data, 16, RGB_SIGNATURE); // profile->color_space + write_u32(data, 20, XYZ_TYPE); // profile->pcs + write_u32(data, 64, Intent::Perceptual as u32); // profile->rendering_intent + write_u32(data, 128, 6); // total tag count + // prepare the result + *mem = data.as_mut_ptr() as *mut libc::c_void; + *size = length as usize; +} + +#[no_mangle] +pub unsafe extern "C" fn qcms_transform_data( + transform: &qcms_transform, + src: *const libc::c_void, + dest: *mut libc::c_void, + length: usize, +) { + transform.transform_fn.expect("non-null function pointer")( + transform, + src as *const u8, + dest as *mut u8, + length, + ); +} +/* +use crate::matrix; +#[repr(C)] +#[derive(Clone, Debug, Default)] +pub struct qcms_mat3r3 { + pub rows: [[f32; 3] ; 3], +} +impl qcms_mat3r3 { + fn from(m: matrix::Matrix) -> qcms_mat3r3 { + qcms_mat3r3{ + rows: [ + m.row(0), + m.row(1), + m.row(2), + ], + } + } +} +*/ +#[repr(C)] +#[derive(Clone, Debug, Default)] +#[allow(clippy::upper_case_acronyms)] +pub struct qcms_profile_data { + pub class_type: u32, + pub color_space: u32, + pub pcs: u32, + pub rendering_intent: Intent, + pub red_colorant_xyzd50: [f32; 3], + pub blue_colorant_xyzd50: [f32; 3], + pub green_colorant_xyzd50: [f32; 3], + // Number of samples in the e.g. gamma->linear LUT. + pub linear_from_trc_red_samples: i32, + pub linear_from_trc_blue_samples: i32, + pub linear_from_trc_green_samples: i32, +} + +pub use crate::iccread::Profile as qcms_profile; + +#[no_mangle] +pub extern "C" fn qcms_profile_get_data( + profile: &qcms_profile, + out_data: &mut qcms_profile_data, +) { + out_data.class_type = profile.class_type; + out_data.color_space = profile.color_space; + out_data.pcs = profile.pcs; + out_data.rendering_intent = profile.rendering_intent; + + fn colorant(c: &XYZNumber) -> [f32;3] { + [c.X, c.Y, c.Z].map(s15Fixed16Number_to_float) + } + out_data.red_colorant_xyzd50 = colorant(&profile.redColorant); + out_data.blue_colorant_xyzd50 = colorant(&profile.blueColorant); + out_data.green_colorant_xyzd50 = colorant(&profile.greenColorant); + + fn trc_to_samples(trc: &Option<Box<curveType>>) -> i32 { + if let Some(ref trc) = *trc { + match &**trc { + curveType::Curve(v) => { + let len = v.len(); + if len <= 1 { + -1 + } else { + len as i32 + } + }, + curveType::Parametric(_) => -1, + } + } else { + 0 + } + } + out_data.linear_from_trc_red_samples = trc_to_samples(&profile.redTRC); + out_data.linear_from_trc_blue_samples = trc_to_samples(&profile.blueTRC); + out_data.linear_from_trc_green_samples = trc_to_samples(&profile.greenTRC); +} + +#[repr(u8)] +pub enum qcms_color_channel { + Red, + Green, + Blue, +} + +#[no_mangle] +pub extern "C" fn qcms_profile_get_lut( + profile: &qcms_profile, + channel: qcms_color_channel, // FYI: UB if you give Rust something out of range! + out_begin: *mut f32, + out_end: *mut f32, +) { + let out_slice = unsafe { + std::slice::from_raw_parts_mut(out_begin, out_end.offset_from(out_begin) as usize) + }; + + let trc = match channel { + qcms_color_channel::Red => &profile.redTRC, + qcms_color_channel::Green => &profile.greenTRC, + qcms_color_channel::Blue => &profile.blueTRC, + }; + + let samples_u16 = if let Some(trc) = trc { + let trc = &*trc; + // Yes, sub-optimal, but easier to implement, and these aren't big or hot: + // 1. Ask for a new vec<u16> lut based on the trc. + // * (eat the extra alloc) + // 2. Convert the u16s back out to f32s in our slice. + // * (eat the copy and quantization error from f32->u16->f32 roundtrip) + transform_util::build_lut_for_linear_from_tf(trc, Some(out_slice.len())) + } else { + Vec::new() + }; + + assert_eq!(samples_u16.len(), out_slice.len()); + for (d, s) in out_slice.iter_mut().zip(samples_u16.into_iter()) { + *d = (s as f32) / (u16::MAX as f32); + } +} + +pub type icColorSpaceSignature = u32; +pub const icSigGrayData: icColorSpaceSignature = 0x47524159; // 'GRAY' +pub const icSigRgbData: icColorSpaceSignature = 0x52474220; // 'RGB ' +pub const icSigCmykData: icColorSpaceSignature = 0x434d594b; // 'CMYK' + +pub use crate::iccread::qcms_profile_is_bogus; +pub use crate::transform::{ + qcms_enable_iccv4, qcms_profile_precache_output_transform, qcms_transform_release, +}; |