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