c_bindings.rs (16110B)
1 #![allow(clippy::missing_safety_doc)] 2 3 use std::{ptr::null_mut, slice}; 4 5 use libc::{fclose, fopen, fread, free, malloc, memset, FILE}; 6 7 use crate::{ 8 double_to_s15Fixed16Number, 9 iccread::*, 10 s15Fixed16Number_to_float, 11 transform::get_rgb_colorants, 12 transform::DataType, 13 transform::{qcms_transform, transform_create}, 14 transform_util, 15 Intent, 16 }; 17 18 #[no_mangle] 19 pub extern "C" fn qcms_profile_sRGB() -> *mut Profile { 20 let profile = Profile::new_sRGB(); 21 Box::into_raw(profile) 22 } 23 24 #[no_mangle] 25 pub extern "C" fn qcms_profile_displayP3() -> *mut Profile { 26 let profile = Profile::new_displayP3(); 27 Box::into_raw(profile) 28 } 29 30 31 //XXX: it would be nice if we had a way of ensuring 32 // everything in a profile was initialized regardless of how it was created 33 //XXX: should this also be taking a black_point? 34 /* similar to CGColorSpaceCreateCalibratedRGB */ 35 #[no_mangle] 36 pub unsafe extern "C" fn qcms_profile_create_rgb_with_gamma_set( 37 white_point: qcms_CIE_xyY, 38 primaries: qcms_CIE_xyYTRIPLE, 39 redGamma: f32, 40 greenGamma: f32, 41 blueGamma: f32, 42 ) -> *mut Profile { 43 let profile = 44 Profile::new_rgb_with_gamma_set(white_point, primaries, redGamma, greenGamma, blueGamma); 45 profile.map_or_else(null_mut, Box::into_raw) 46 } 47 48 #[no_mangle] 49 pub unsafe extern "C" fn qcms_profile_create_gray_with_gamma(gamma: f32) -> *mut Profile { 50 let profile = Profile::new_gray_with_gamma(gamma); 51 Box::into_raw(profile) 52 } 53 54 #[no_mangle] 55 pub unsafe extern "C" fn qcms_profile_create_rgb_with_gamma( 56 white_point: qcms_CIE_xyY, 57 primaries: qcms_CIE_xyYTRIPLE, 58 gamma: f32, 59 ) -> *mut Profile { 60 qcms_profile_create_rgb_with_gamma_set(white_point, primaries, gamma, gamma, gamma) 61 } 62 63 #[no_mangle] 64 pub unsafe extern "C" fn qcms_profile_create_rgb_with_table( 65 white_point: qcms_CIE_xyY, 66 primaries: qcms_CIE_xyYTRIPLE, 67 table: *const u16, 68 num_entries: i32, 69 ) -> *mut Profile { 70 let table = slice::from_raw_parts(table, num_entries as usize); 71 let profile = Profile::new_rgb_with_table(white_point, primaries, table); 72 profile.map_or_else(null_mut, Box::into_raw) 73 } 74 75 #[no_mangle] 76 pub unsafe extern "C" fn qcms_profile_create_cicp( 77 colour_primaries: u8, 78 transfer_characteristics: u8, 79 ) -> *mut Profile { 80 Profile::new_cicp(colour_primaries.into(), transfer_characteristics.into()) 81 .map_or_else(null_mut, Box::into_raw) 82 } 83 84 /* qcms_profile_from_memory does not hold a reference to the memory passed in */ 85 #[no_mangle] 86 pub unsafe extern "C" fn qcms_profile_from_memory( 87 mem: *const libc::c_void, 88 size: usize, 89 ) -> *mut Profile { 90 let mem = slice::from_raw_parts(mem as *const libc::c_uchar, size); 91 let profile = Profile::new_from_slice(mem, false); 92 profile.map_or_else(null_mut, Box::into_raw) 93 } 94 95 #[no_mangle] 96 pub unsafe extern "C" fn qcms_profile_from_memory_curves_only( 97 mem: *const libc::c_void, 98 size: usize, 99 ) -> *mut Profile { 100 let mem = slice::from_raw_parts(mem as *const libc::c_uchar, size); 101 let profile = Profile::new_from_slice(mem, true); 102 profile.map_or_else(null_mut, Box::into_raw) 103 } 104 105 106 #[no_mangle] 107 pub extern "C" fn qcms_profile_get_rendering_intent(profile: &Profile) -> Intent { 108 profile.rendering_intent 109 } 110 #[no_mangle] 111 pub extern "C" fn qcms_profile_get_color_space(profile: &Profile) -> icColorSpaceSignature { 112 profile.color_space 113 } 114 #[no_mangle] 115 pub extern "C" fn qcms_profile_is_sRGB(profile: &Profile) -> bool { 116 profile.is_sRGB() 117 } 118 119 #[no_mangle] 120 pub unsafe extern "C" fn qcms_profile_release(profile: *mut Profile) { 121 drop(Box::from_raw(profile)); 122 } 123 unsafe extern "C" fn qcms_data_from_file( 124 file: *mut FILE, 125 mem: *mut *mut libc::c_void, 126 size: *mut usize, 127 ) { 128 let length: u32; 129 let remaining_length: u32; 130 let read_length: usize; 131 let mut length_be: u32 = 0; 132 let data: *mut libc::c_void; 133 *mem = std::ptr::null_mut::<libc::c_void>(); 134 *size = 0; 135 if fread( 136 &mut length_be as *mut u32 as *mut libc::c_void, 137 1, 138 ::std::mem::size_of::<u32>(), 139 file, 140 ) != ::std::mem::size_of::<u32>() 141 { 142 return; 143 } 144 length = u32::from_be(length_be); 145 if length > MAX_PROFILE_SIZE as libc::c_uint 146 || (length as libc::c_ulong) < ::std::mem::size_of::<u32>() as libc::c_ulong 147 { 148 return; 149 } 150 /* allocate room for the entire profile */ 151 data = malloc(length as usize); 152 if data.is_null() { 153 return; 154 } 155 /* copy in length to the front so that the buffer will contain the entire profile */ 156 *(data as *mut u32) = length_be; 157 remaining_length = 158 (length as libc::c_ulong - ::std::mem::size_of::<u32>() as libc::c_ulong) as u32; 159 /* read the rest profile */ 160 read_length = fread( 161 (data as *mut libc::c_uchar).add(::std::mem::size_of::<u32>()) as *mut libc::c_void, 162 1, 163 remaining_length as usize, 164 file, 165 ) as usize; 166 if read_length != remaining_length as usize { 167 free(data); 168 return; 169 } 170 /* successfully get the profile.*/ 171 *mem = data; 172 *size = length as usize; 173 } 174 175 #[no_mangle] 176 pub unsafe extern "C" fn qcms_profile_from_file(file: *mut FILE) -> *mut Profile { 177 let mut length: usize = 0; 178 let profile: *mut Profile; 179 let mut data: *mut libc::c_void = std::ptr::null_mut::<libc::c_void>(); 180 qcms_data_from_file(file, &mut data, &mut length); 181 if data.is_null() || length == 0 { 182 return std::ptr::null_mut::<Profile>(); 183 } 184 profile = qcms_profile_from_memory(data, length); 185 free(data); 186 profile 187 } 188 #[no_mangle] 189 pub unsafe extern "C" fn qcms_profile_from_path(path: *const libc::c_char) -> *mut Profile { 190 if let Ok(Some(boxed_profile)) = std::ffi::CStr::from_ptr(path) 191 .to_str() 192 .map(Profile::new_from_path) 193 { 194 Box::into_raw(boxed_profile) 195 } else { 196 std::ptr::null_mut() 197 } 198 } 199 200 #[no_mangle] 201 pub unsafe extern "C" fn qcms_data_from_path( 202 path: *const libc::c_char, 203 mem: *mut *mut libc::c_void, 204 size: *mut usize, 205 ) { 206 *mem = std::ptr::null_mut::<libc::c_void>(); 207 *size = 0; 208 let file = fopen(path, b"rb\x00" as *const u8 as *const libc::c_char); 209 if !file.is_null() { 210 qcms_data_from_file(file, mem, size); 211 fclose(file); 212 }; 213 } 214 215 #[cfg(windows)] 216 extern "C" { 217 pub fn _wfopen(filename: *const libc::wchar_t, mode: *const libc::wchar_t) -> *mut FILE; 218 } 219 220 #[cfg(windows)] 221 #[no_mangle] 222 pub unsafe extern "C" fn qcms_profile_from_unicode_path(path: *const libc::wchar_t) { 223 let file = _wfopen(path, ['r' as u16, 'b' as u16, '\0' as u16].as_ptr()); 224 if !file.is_null() { 225 qcms_profile_from_file(file); 226 fclose(file); 227 }; 228 } 229 230 #[cfg(windows)] 231 #[no_mangle] 232 pub unsafe extern "C" fn qcms_data_from_unicode_path( 233 path: *const libc::wchar_t, 234 mem: *mut *mut libc::c_void, 235 size: *mut usize, 236 ) { 237 *mem = 0 as *mut libc::c_void; 238 *size = 0; 239 let file = _wfopen(path, ['r' as u16, 'b' as u16, '\0' as u16].as_ptr()); 240 if !file.is_null() { 241 qcms_data_from_file(file, mem, size); 242 fclose(file); 243 }; 244 } 245 246 #[no_mangle] 247 pub extern "C" fn qcms_transform_create( 248 in_0: &Profile, 249 in_type: DataType, 250 out: &Profile, 251 out_type: DataType, 252 intent: Intent, 253 ) -> *mut qcms_transform { 254 let transform = transform_create(in_0, in_type, out, out_type, intent); 255 match transform { 256 Some(transform) => Box::into_raw(transform), 257 None => null_mut(), 258 } 259 } 260 261 #[no_mangle] 262 pub unsafe extern "C" fn qcms_data_create_rgb_with_gamma( 263 white_point: qcms_CIE_xyY, 264 primaries: qcms_CIE_xyYTRIPLE, 265 gamma: f32, 266 mem: *mut *mut libc::c_void, 267 size: *mut usize, 268 ) { 269 let length: u32; 270 let mut index: u32; 271 let xyz_count: u32; 272 let trc_count: u32; 273 let mut tag_table_offset: usize; 274 let mut tag_data_offset: usize; 275 let data: *mut libc::c_void; 276 277 let TAG_XYZ: [u32; 3] = [TAG_rXYZ, TAG_gXYZ, TAG_bXYZ]; 278 let TAG_TRC: [u32; 3] = [TAG_rTRC, TAG_gTRC, TAG_bTRC]; 279 if mem.is_null() || size.is_null() { 280 return; 281 } 282 *mem = std::ptr::null_mut::<libc::c_void>(); 283 *size = 0; 284 /* 285 * total length = icc profile header(128) + tag count(4) + 286 * (tag table item (12) * total tag (6 = 3 rTRC + 3 rXYZ)) + rTRC elements data (3 * 20) 287 * + rXYZ elements data (3*16), and all tag data elements must start at the 4-byte boundary. 288 */ 289 xyz_count = 3; // rXYZ, gXYZ, bXYZ 290 trc_count = 3; // rTRC, gTRC, bTRC 291 length = 292 (128 + 4) as libc::c_uint + 12 * (xyz_count + trc_count) + xyz_count * 20 + trc_count * 16; 293 // reserve the total memory. 294 data = malloc(length as usize); 295 if data.is_null() { 296 return; 297 } 298 memset(data, 0, length as usize); 299 // Part1 : write rXYZ, gXYZ and bXYZ 300 let colorants = match get_rgb_colorants(white_point, primaries) { 301 Some(colorants) => colorants, 302 None => { 303 free(data); 304 return; 305 } 306 }; 307 308 let data = std::slice::from_raw_parts_mut(data as *mut u8, length as usize); 309 // the position of first tag's signature in tag table 310 tag_table_offset = (128 + 4) as usize; // the start of tag data elements. 311 tag_data_offset = ((128 + 4) as libc::c_uint + 12 * (xyz_count + trc_count)) as usize; 312 index = 0; 313 while index < xyz_count { 314 // tag table 315 write_u32(data, tag_table_offset, TAG_XYZ[index as usize]); // 20 bytes per TAG_(r/g/b)XYZ tag element 316 write_u32(data, tag_table_offset + 4, tag_data_offset as u32); 317 write_u32(data, tag_table_offset + 8, 20); 318 // tag data element 319 write_u32(data, tag_data_offset, XYZ_TYPE); 320 // reserved 4 bytes. 321 write_u32( 322 data, 323 tag_data_offset + 8, 324 double_to_s15Fixed16Number(colorants.m[0][index as usize] as f64) as u32, 325 ); 326 write_u32( 327 data, 328 tag_data_offset + 12, 329 double_to_s15Fixed16Number(colorants.m[1][index as usize] as f64) as u32, 330 ); 331 write_u32( 332 data, 333 tag_data_offset + 16, 334 double_to_s15Fixed16Number(colorants.m[2][index as usize] as f64) as u32, 335 ); 336 tag_table_offset += 12; 337 tag_data_offset += 20; 338 index += 1 339 } 340 // Part2 : write rTRC, gTRC and bTRC 341 index = 0; 342 while index < trc_count { 343 // tag table 344 write_u32(data, tag_table_offset, TAG_TRC[index as usize]); // 14 bytes per TAG_(r/g/b)TRC element 345 write_u32(data, tag_table_offset + 4, tag_data_offset as u32); 346 write_u32(data, tag_table_offset + 8, 14); 347 // tag data element 348 write_u32(data, tag_data_offset, CURVE_TYPE); 349 // reserved 4 bytes. 350 write_u32(data, tag_data_offset + 8, 1); // count 351 write_u16(data, tag_data_offset + 12, float_to_u8Fixed8Number(gamma)); 352 tag_table_offset += 12; 353 tag_data_offset += 16; 354 index += 1 355 } 356 /* Part3 : write profile header 357 * 358 * Important header fields are left empty. This generates a profile for internal use only. 359 * We should be generating: Profile version (04300000h), Profile signature (acsp), 360 * PCS illumiant field. Likewise mandatory profile tags are omitted. 361 */ 362 write_u32(data, 0, length); // the total length of this memory 363 write_u32(data, 12, DISPLAY_DEVICE_PROFILE); // profile->class_type 364 write_u32(data, 16, RGB_SIGNATURE); // profile->color_space 365 write_u32(data, 20, XYZ_TYPE); // profile->pcs 366 write_u32(data, 64, Intent::Perceptual as u32); // profile->rendering_intent 367 write_u32(data, 128, 6); // total tag count 368 // prepare the result 369 *mem = data.as_mut_ptr() as *mut libc::c_void; 370 *size = length as usize; 371 } 372 373 #[no_mangle] 374 pub unsafe extern "C" fn qcms_transform_data( 375 transform: &qcms_transform, 376 src: *const libc::c_void, 377 dest: *mut libc::c_void, 378 length: usize, 379 ) { 380 transform.transform_fn.expect("non-null function pointer")( 381 transform, 382 src as *const u8, 383 dest as *mut u8, 384 length, 385 ); 386 } 387 /* 388 use crate::matrix; 389 #[repr(C)] 390 #[derive(Clone, Debug, Default)] 391 pub struct qcms_mat3r3 { 392 pub rows: [[f32; 3] ; 3], 393 } 394 impl qcms_mat3r3 { 395 fn from(m: matrix::Matrix) -> qcms_mat3r3 { 396 qcms_mat3r3{ 397 rows: [ 398 m.row(0), 399 m.row(1), 400 m.row(2), 401 ], 402 } 403 } 404 } 405 */ 406 #[repr(C)] 407 #[derive(Clone, Debug, Default)] 408 #[allow(clippy::upper_case_acronyms)] 409 pub struct qcms_profile_data { 410 pub class_type: u32, 411 pub color_space: u32, 412 pub pcs: u32, 413 pub rendering_intent: Intent, 414 pub red_colorant_xyzd50: [f32; 3], 415 pub blue_colorant_xyzd50: [f32; 3], 416 pub green_colorant_xyzd50: [f32; 3], 417 // Number of samples in the e.g. gamma->linear LUT. 418 pub linear_from_trc_red_samples: i32, 419 pub linear_from_trc_blue_samples: i32, 420 pub linear_from_trc_green_samples: i32, 421 } 422 423 pub use crate::iccread::Profile as qcms_profile; 424 425 #[no_mangle] 426 pub extern "C" fn qcms_profile_get_data( 427 profile: &qcms_profile, 428 out_data: &mut qcms_profile_data, 429 ) { 430 out_data.class_type = profile.class_type; 431 out_data.color_space = profile.color_space; 432 out_data.pcs = profile.pcs; 433 out_data.rendering_intent = profile.rendering_intent; 434 435 fn colorant(c: &XYZNumber) -> [f32;3] { 436 [c.X, c.Y, c.Z].map(s15Fixed16Number_to_float) 437 } 438 out_data.red_colorant_xyzd50 = colorant(&profile.redColorant); 439 out_data.blue_colorant_xyzd50 = colorant(&profile.blueColorant); 440 out_data.green_colorant_xyzd50 = colorant(&profile.greenColorant); 441 442 fn trc_to_samples(trc: &Option<Box<curveType>>) -> i32 { 443 if let Some(ref trc) = *trc { 444 match &**trc { 445 curveType::Curve(v) => { 446 let len = v.len(); 447 if len <= 1 { 448 -1 449 } else { 450 len as i32 451 } 452 }, 453 curveType::Parametric(_) => -1, 454 } 455 } else { 456 0 457 } 458 } 459 out_data.linear_from_trc_red_samples = trc_to_samples(&profile.redTRC); 460 out_data.linear_from_trc_blue_samples = trc_to_samples(&profile.blueTRC); 461 out_data.linear_from_trc_green_samples = trc_to_samples(&profile.greenTRC); 462 } 463 464 #[repr(u8)] 465 pub enum qcms_color_channel { 466 Red, 467 Green, 468 Blue, 469 } 470 471 #[no_mangle] 472 pub extern "C" fn qcms_profile_get_lut( 473 profile: &qcms_profile, 474 channel: qcms_color_channel, // FYI: UB if you give Rust something out of range! 475 out_begin: *mut f32, 476 out_end: *mut f32, 477 ) { 478 let out_slice = unsafe { 479 std::slice::from_raw_parts_mut(out_begin, out_end.offset_from(out_begin) as usize) 480 }; 481 482 let trc = match channel { 483 qcms_color_channel::Red => &profile.redTRC, 484 qcms_color_channel::Green => &profile.greenTRC, 485 qcms_color_channel::Blue => &profile.blueTRC, 486 }; 487 488 let samples_u16 = if let Some(trc) = trc { 489 let trc = &*trc; 490 // Yes, sub-optimal, but easier to implement, and these aren't big or hot: 491 // 1. Ask for a new vec<u16> lut based on the trc. 492 // * (eat the extra alloc) 493 // 2. Convert the u16s back out to f32s in our slice. 494 // * (eat the copy and quantization error from f32->u16->f32 roundtrip) 495 transform_util::build_lut_for_linear_from_tf(trc, Some(out_slice.len())) 496 } else { 497 Vec::new() 498 }; 499 500 assert_eq!(samples_u16.len(), out_slice.len()); 501 for (d, s) in out_slice.iter_mut().zip(samples_u16.into_iter()) { 502 *d = (s as f32) / (u16::MAX as f32); 503 } 504 } 505 506 pub type icColorSpaceSignature = u32; 507 pub const icSigGrayData: icColorSpaceSignature = 0x47524159; // 'GRAY' 508 pub const icSigRgbData: icColorSpaceSignature = 0x52474220; // 'RGB ' 509 pub const icSigCmykData: icColorSpaceSignature = 0x434d594b; // 'CMYK' 510 511 pub use crate::iccread::qcms_profile_is_bogus; 512 pub use crate::transform::{ 513 qcms_enable_iccv4, qcms_profile_precache_output_transform, qcms_transform_release, 514 };