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     transform::get_rgb_colorants,
11     transform::DataType,
12     transform::{qcms_transform, transform_create},
13     Intent,
14 };
15 
16 #[no_mangle]
qcms_profile_sRGB() -> *mut Profile17 pub extern "C" fn qcms_profile_sRGB() -> *mut Profile {
18     let profile = Profile::new_sRGB();
19     Box::into_raw(profile)
20 }
21 
22 //XXX: it would be nice if we had a way of ensuring
23 // everything in a profile was initialized regardless of how it was created
24 //XXX: should this also be taking a black_point?
25 /* similar to CGColorSpaceCreateCalibratedRGB */
26 #[no_mangle]
qcms_profile_create_rgb_with_gamma_set( white_point: qcms_CIE_xyY, primaries: qcms_CIE_xyYTRIPLE, redGamma: f32, greenGamma: f32, blueGamma: f32, ) -> *mut Profile27 pub unsafe extern "C" fn qcms_profile_create_rgb_with_gamma_set(
28     white_point: qcms_CIE_xyY,
29     primaries: qcms_CIE_xyYTRIPLE,
30     redGamma: f32,
31     greenGamma: f32,
32     blueGamma: f32,
33 ) -> *mut Profile {
34     let profile =
35         Profile::new_rgb_with_gamma_set(white_point, primaries, redGamma, greenGamma, blueGamma);
36     profile.map_or_else(null_mut, Box::into_raw)
37 }
38 
39 #[no_mangle]
qcms_profile_create_gray_with_gamma(gamma: f32) -> *mut Profile40 pub unsafe extern "C" fn qcms_profile_create_gray_with_gamma(gamma: f32) -> *mut Profile {
41     let profile = Profile::new_gray_with_gamma(gamma);
42     Box::into_raw(profile)
43 }
44 
45 #[no_mangle]
qcms_profile_create_rgb_with_gamma( white_point: qcms_CIE_xyY, primaries: qcms_CIE_xyYTRIPLE, gamma: f32, ) -> *mut Profile46 pub unsafe extern "C" fn qcms_profile_create_rgb_with_gamma(
47     white_point: qcms_CIE_xyY,
48     primaries: qcms_CIE_xyYTRIPLE,
49     gamma: f32,
50 ) -> *mut Profile {
51     qcms_profile_create_rgb_with_gamma_set(white_point, primaries, gamma, gamma, gamma)
52 }
53 
54 #[no_mangle]
qcms_profile_create_rgb_with_table( white_point: qcms_CIE_xyY, primaries: qcms_CIE_xyYTRIPLE, table: *const u16, num_entries: i32, ) -> *mut Profile55 pub unsafe extern "C" fn qcms_profile_create_rgb_with_table(
56     white_point: qcms_CIE_xyY,
57     primaries: qcms_CIE_xyYTRIPLE,
58     table: *const u16,
59     num_entries: i32,
60 ) -> *mut Profile {
61     let table = slice::from_raw_parts(table, num_entries as usize);
62     let profile = Profile::new_rgb_with_table(white_point, primaries, table);
63     profile.map_or_else(null_mut, Box::into_raw)
64 }
65 
66 #[no_mangle]
qcms_profile_create_cicp( colour_primaries: u8, transfer_characteristics: u8, ) -> *mut Profile67 pub unsafe extern "C" fn qcms_profile_create_cicp(
68     colour_primaries: u8,
69     transfer_characteristics: u8,
70 ) -> *mut Profile {
71     Profile::new_cicp(colour_primaries.into(), transfer_characteristics.into())
72         .map_or_else(null_mut, Box::into_raw)
73 }
74 
75 /* qcms_profile_from_memory does not hold a reference to the memory passed in */
76 #[no_mangle]
qcms_profile_from_memory( mem: *const libc::c_void, size: usize, ) -> *mut Profile77 pub unsafe extern "C" fn qcms_profile_from_memory(
78     mem: *const libc::c_void,
79     size: usize,
80 ) -> *mut Profile {
81     let mem = slice::from_raw_parts(mem as *const libc::c_uchar, size);
82     let profile = Profile::new_from_slice(mem);
83     profile.map_or_else(null_mut, Box::into_raw)
84 }
85 
86 #[no_mangle]
qcms_profile_get_rendering_intent(profile: &Profile) -> Intent87 pub extern "C" fn qcms_profile_get_rendering_intent(profile: &Profile) -> Intent {
88     profile.rendering_intent
89 }
90 #[no_mangle]
qcms_profile_get_color_space(profile: &Profile) -> icColorSpaceSignature91 pub extern "C" fn qcms_profile_get_color_space(profile: &Profile) -> icColorSpaceSignature {
92     profile.color_space
93 }
94 #[no_mangle]
qcms_profile_is_sRGB(profile: &Profile) -> bool95 pub extern "C" fn qcms_profile_is_sRGB(profile: &Profile) -> bool {
96     profile.is_sRGB()
97 }
98 
99 #[no_mangle]
qcms_profile_release(profile: *mut Profile)100 pub unsafe extern "C" fn qcms_profile_release(profile: *mut Profile) {
101     drop(Box::from_raw(profile));
102 }
qcms_data_from_file( file: *mut FILE, mem: *mut *mut libc::c_void, size: *mut usize, )103 unsafe extern "C" fn qcms_data_from_file(
104     file: *mut FILE,
105     mem: *mut *mut libc::c_void,
106     size: *mut usize,
107 ) {
108     let length: u32;
109     let remaining_length: u32;
110     let read_length: usize;
111     let mut length_be: u32 = 0;
112     let data: *mut libc::c_void;
113     *mem = std::ptr::null_mut::<libc::c_void>();
114     *size = 0;
115     if fread(
116         &mut length_be as *mut u32 as *mut libc::c_void,
117         1,
118         ::std::mem::size_of::<u32>(),
119         file,
120     ) != ::std::mem::size_of::<u32>()
121     {
122         return;
123     }
124     length = u32::from_be(length_be);
125     if length > MAX_PROFILE_SIZE as libc::c_uint
126         || (length as libc::c_ulong) < ::std::mem::size_of::<u32>() as libc::c_ulong
127     {
128         return;
129     }
130     /* allocate room for the entire profile */
131     data = malloc(length as usize);
132     if data.is_null() {
133         return;
134     }
135     /* copy in length to the front so that the buffer will contain the entire profile */
136     *(data as *mut u32) = length_be;
137     remaining_length =
138         (length as libc::c_ulong - ::std::mem::size_of::<u32>() as libc::c_ulong) as u32;
139     /* read the rest profile */
140     read_length = fread(
141         (data as *mut libc::c_uchar).add(::std::mem::size_of::<u32>()) as *mut libc::c_void,
142         1,
143         remaining_length as usize,
144         file,
145     ) as usize;
146     if read_length != remaining_length as usize {
147         free(data);
148         return;
149     }
150     /* successfully get the profile.*/
151     *mem = data;
152     *size = length as usize;
153 }
154 
155 #[no_mangle]
qcms_profile_from_file(file: *mut FILE) -> *mut Profile156 pub unsafe extern "C" fn qcms_profile_from_file(file: *mut FILE) -> *mut Profile {
157     let mut length: usize = 0;
158     let profile: *mut Profile;
159     let mut data: *mut libc::c_void = std::ptr::null_mut::<libc::c_void>();
160     qcms_data_from_file(file, &mut data, &mut length);
161     if data.is_null() || length == 0 {
162         return std::ptr::null_mut::<Profile>();
163     }
164     profile = qcms_profile_from_memory(data, length);
165     free(data);
166     profile
167 }
168 #[no_mangle]
qcms_profile_from_path(path: *const libc::c_char) -> *mut Profile169 pub unsafe extern "C" fn qcms_profile_from_path(path: *const libc::c_char) -> *mut Profile {
170     if let Ok(Some(boxed_profile)) = std::ffi::CStr::from_ptr(path)
171         .to_str()
172         .map(Profile::new_from_path)
173     {
174         Box::into_raw(boxed_profile)
175     } else {
176         std::ptr::null_mut()
177     }
178 }
179 
180 #[no_mangle]
qcms_data_from_path( path: *const libc::c_char, mem: *mut *mut libc::c_void, size: *mut usize, )181 pub unsafe extern "C" fn qcms_data_from_path(
182     path: *const libc::c_char,
183     mem: *mut *mut libc::c_void,
184     size: *mut usize,
185 ) {
186     *mem = std::ptr::null_mut::<libc::c_void>();
187     *size = 0;
188     let file = fopen(path, b"rb\x00" as *const u8 as *const libc::c_char);
189     if !file.is_null() {
190         qcms_data_from_file(file, mem, size);
191         fclose(file);
192     };
193 }
194 
195 #[cfg(windows)]
196 extern "C" {
_wfopen(filename: *const libc::wchar_t, mode: *const libc::wchar_t) -> *mut FILE197     pub fn _wfopen(filename: *const libc::wchar_t, mode: *const libc::wchar_t) -> *mut FILE;
198 }
199 
200 #[cfg(windows)]
201 #[no_mangle]
qcms_profile_from_unicode_path(path: *const libc::wchar_t)202 pub unsafe extern "C" fn qcms_profile_from_unicode_path(path: *const libc::wchar_t) {
203     let file = _wfopen(path, ['r' as u16, 'b' as u16, '\0' as u16].as_ptr());
204     if !file.is_null() {
205         qcms_profile_from_file(file);
206         fclose(file);
207     };
208 }
209 
210 #[cfg(windows)]
211 #[no_mangle]
qcms_data_from_unicode_path( path: *const libc::wchar_t, mem: *mut *mut libc::c_void, size: *mut usize, )212 pub unsafe extern "C" fn qcms_data_from_unicode_path(
213     path: *const libc::wchar_t,
214     mem: *mut *mut libc::c_void,
215     size: *mut usize,
216 ) {
217     *mem = 0 as *mut libc::c_void;
218     *size = 0;
219     let file = _wfopen(path, ['r' as u16, 'b' as u16, '\0' as u16].as_ptr());
220     if !file.is_null() {
221         qcms_data_from_file(file, mem, size);
222         fclose(file);
223     };
224 }
225 
226 #[no_mangle]
qcms_transform_create( in_0: &Profile, in_type: DataType, out: &Profile, out_type: DataType, intent: Intent, ) -> *mut qcms_transform227 pub extern "C" fn qcms_transform_create(
228     in_0: &Profile,
229     in_type: DataType,
230     out: &Profile,
231     out_type: DataType,
232     intent: Intent,
233 ) -> *mut qcms_transform {
234     let transform = transform_create(in_0, in_type, out, out_type, intent);
235     match transform {
236         Some(transform) => Box::into_raw(transform),
237         None => null_mut(),
238     }
239 }
240 
241 #[no_mangle]
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, )242 pub unsafe extern "C" fn qcms_data_create_rgb_with_gamma(
243     white_point: qcms_CIE_xyY,
244     primaries: qcms_CIE_xyYTRIPLE,
245     gamma: f32,
246     mem: *mut *mut libc::c_void,
247     size: *mut usize,
248 ) {
249     let length: u32;
250     let mut index: u32;
251     let xyz_count: u32;
252     let trc_count: u32;
253     let mut tag_table_offset: usize;
254     let mut tag_data_offset: usize;
255     let data: *mut libc::c_void;
256 
257     let TAG_XYZ: [u32; 3] = [TAG_rXYZ, TAG_gXYZ, TAG_bXYZ];
258     let TAG_TRC: [u32; 3] = [TAG_rTRC, TAG_gTRC, TAG_bTRC];
259     if mem.is_null() || size.is_null() {
260         return;
261     }
262     *mem = std::ptr::null_mut::<libc::c_void>();
263     *size = 0;
264     /*
265     	* total length = icc profile header(128) + tag count(4) +
266     	* (tag table item (12) * total tag (6 = 3 rTRC + 3 rXYZ)) + rTRC elements data (3 * 20)
267     	* + rXYZ elements data (3*16), and all tag data elements must start at the 4-byte boundary.
268     	*/
269     xyz_count = 3; // rXYZ, gXYZ, bXYZ
270     trc_count = 3; // rTRC, gTRC, bTRC
271     length =
272         (128 + 4) as libc::c_uint + 12 * (xyz_count + trc_count) + xyz_count * 20 + trc_count * 16;
273     // reserve the total memory.
274     data = malloc(length as usize);
275     if data.is_null() {
276         return;
277     }
278     memset(data, 0, length as usize);
279     // Part1 : write rXYZ, gXYZ and bXYZ
280     let colorants = match get_rgb_colorants(white_point, primaries) {
281         Some(colorants) => colorants,
282         None => {
283             free(data);
284             return;
285         }
286     };
287 
288     let data = std::slice::from_raw_parts_mut(data as *mut u8, length as usize);
289     // the position of first tag's signature in tag table
290     tag_table_offset = (128 + 4) as usize; // the start of tag data elements.
291     tag_data_offset = ((128 + 4) as libc::c_uint + 12 * (xyz_count + trc_count)) as usize;
292     index = 0;
293     while index < xyz_count {
294         // tag table
295         write_u32(data, tag_table_offset, TAG_XYZ[index as usize]); // 20 bytes per TAG_(r/g/b)XYZ tag element
296         write_u32(data, tag_table_offset + 4, tag_data_offset as u32);
297         write_u32(data, tag_table_offset + 8, 20);
298         // tag data element
299         write_u32(data, tag_data_offset, XYZ_TYPE);
300         // reserved 4 bytes.
301         write_u32(
302             data,
303             tag_data_offset + 8,
304             double_to_s15Fixed16Number(colorants.m[0][index as usize] as f64) as u32,
305         );
306         write_u32(
307             data,
308             tag_data_offset + 12,
309             double_to_s15Fixed16Number(colorants.m[1][index as usize] as f64) as u32,
310         );
311         write_u32(
312             data,
313             tag_data_offset + 16,
314             double_to_s15Fixed16Number(colorants.m[2][index as usize] as f64) as u32,
315         );
316         tag_table_offset += 12;
317         tag_data_offset += 20;
318         index += 1
319     }
320     // Part2 : write rTRC, gTRC and bTRC
321     index = 0;
322     while index < trc_count {
323         // tag table
324         write_u32(data, tag_table_offset, TAG_TRC[index as usize]); // 14 bytes per TAG_(r/g/b)TRC element
325         write_u32(data, tag_table_offset + 4, tag_data_offset as u32);
326         write_u32(data, tag_table_offset + 8, 14);
327         // tag data element
328         write_u32(data, tag_data_offset, CURVE_TYPE);
329         // reserved 4 bytes.
330         write_u32(data, tag_data_offset + 8, 1); // count
331         write_u16(data, tag_data_offset + 12, float_to_u8Fixed8Number(gamma));
332         tag_table_offset += 12;
333         tag_data_offset += 16;
334         index += 1
335     }
336     /* Part3 : write profile header
337      *
338      * Important header fields are left empty. This generates a profile for internal use only.
339      * We should be generating: Profile version (04300000h), Profile signature (acsp),
340      * PCS illumiant field. Likewise mandatory profile tags are omitted.
341      */
342     write_u32(data, 0, length); // the total length of this memory
343     write_u32(data, 12, DISPLAY_DEVICE_PROFILE); // profile->class_type
344     write_u32(data, 16, RGB_SIGNATURE); // profile->color_space
345     write_u32(data, 20, XYZ_TYPE); // profile->pcs
346     write_u32(data, 64, Intent::Perceptual as u32); // profile->rendering_intent
347     write_u32(data, 128, 6); // total tag count
348                              // prepare the result
349     *mem = data.as_mut_ptr() as *mut libc::c_void;
350     *size = length as usize;
351 }
352 
353 #[no_mangle]
qcms_transform_data( transform: &qcms_transform, src: *const libc::c_void, dest: *mut libc::c_void, length: usize, )354 pub unsafe extern "C" fn qcms_transform_data(
355     transform: &qcms_transform,
356     src: *const libc::c_void,
357     dest: *mut libc::c_void,
358     length: usize,
359 ) {
360     transform.transform_fn.expect("non-null function pointer")(
361         transform,
362         src as *const u8,
363         dest as *mut u8,
364         length,
365     );
366 }
367 
368 pub type icColorSpaceSignature = u32;
369 pub const icSigGrayData: icColorSpaceSignature = 0x47524159; // 'GRAY'
370 pub const icSigRgbData: icColorSpaceSignature = 0x52474220; // 'RGB '
371 pub const icSigCmykData: icColorSpaceSignature = 0x434d594b; // 'CMYK'
372 
373 pub use crate::iccread::qcms_profile_is_bogus;
374 pub use crate::iccread::Profile as qcms_profile;
375 pub use crate::transform::{
376     qcms_enable_iccv4, qcms_profile_precache_output_transform, qcms_transform_release,
377 };
378