1 /* vim: set ts=8 sw=8 noexpandtab: */
2 //  qcms
3 //  Copyright (C) 2009 Mozilla Foundation
4 //  Copyright (C) 1998-2007 Marti Maria
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 
24 use std::{
25     convert::TryInto,
26     sync::atomic::{AtomicBool, Ordering},
27     sync::Arc,
28 };
29 
30 use crate::{
31     double_to_s15Fixed16Number,
32     transform::{set_rgb_colorants, PrecacheOuput},
33 };
34 use crate::{matrix::Matrix, s15Fixed16Number, s15Fixed16Number_to_float, Intent, Intent::*};
35 
36 pub static SUPPORTS_ICCV4: AtomicBool = AtomicBool::new(cfg!(feature = "iccv4-enabled"));
37 
38 pub const RGB_SIGNATURE: u32 = 0x52474220;
39 pub const GRAY_SIGNATURE: u32 = 0x47524159;
40 pub const XYZ_SIGNATURE: u32 = 0x58595A20;
41 pub const LAB_SIGNATURE: u32 = 0x4C616220;
42 pub const CMYK_SIGNATURE: u32 = 0x434D594B; // 'CMYK'
43 
44 /// A color profile
45 #[derive(Default)]
46 pub struct Profile {
47     pub(crate) class_type: u32,
48     pub(crate) color_space: u32,
49     pub(crate) pcs: u32,
50     pub(crate) rendering_intent: Intent,
51     pub(crate) redColorant: XYZNumber,
52     pub(crate) blueColorant: XYZNumber,
53     pub(crate) greenColorant: XYZNumber,
54     pub(crate) redTRC: Option<Box<curveType>>,
55     pub(crate) blueTRC: Option<Box<curveType>>,
56     pub(crate) greenTRC: Option<Box<curveType>>,
57     pub(crate) grayTRC: Option<Box<curveType>>,
58     pub(crate) A2B0: Option<Box<lutType>>,
59     pub(crate) B2A0: Option<Box<lutType>>,
60     pub(crate) mAB: Option<Box<lutmABType>>,
61     pub(crate) mBA: Option<Box<lutmABType>>,
62     pub(crate) chromaticAdaption: Option<Matrix>,
63     pub(crate) output_table_r: Option<Arc<PrecacheOuput>>,
64     pub(crate) output_table_g: Option<Arc<PrecacheOuput>>,
65     pub(crate) output_table_b: Option<Arc<PrecacheOuput>>,
66     is_srgb: Option<bool>,
67 }
68 
69 #[derive(Default)]
70 pub(crate) struct lutmABType {
71     pub num_in_channels: u8,
72     pub num_out_channels: u8,
73     // 16 is the upperbound, actual is 0..num_in_channels.
74     pub num_grid_points: [u8; 16],
75     pub e00: s15Fixed16Number,
76     pub e01: s15Fixed16Number,
77     pub e02: s15Fixed16Number,
78     pub e03: s15Fixed16Number,
79     pub e10: s15Fixed16Number,
80     pub e11: s15Fixed16Number,
81     pub e12: s15Fixed16Number,
82     pub e13: s15Fixed16Number,
83     pub e20: s15Fixed16Number,
84     pub e21: s15Fixed16Number,
85     pub e22: s15Fixed16Number,
86     pub e23: s15Fixed16Number,
87     // reversed elements (for mBA)
88     pub reversed: bool,
89     pub clut_table: Option<Vec<f32>>,
90     pub a_curves: [Option<Box<curveType>>; MAX_CHANNELS],
91     pub b_curves: [Option<Box<curveType>>; MAX_CHANNELS],
92     pub m_curves: [Option<Box<curveType>>; MAX_CHANNELS],
93 }
94 #[derive(Clone)]
95 pub(crate) enum curveType {
96     Curve(Vec<uInt16Number>),
97     Parametric(Vec<f32>),
98 }
99 type uInt16Number = u16;
100 
101 /* should lut8Type and lut16Type be different types? */
102 pub(crate) struct lutType {
103     // used by lut8Type/lut16Type (mft2) only
104     pub num_input_channels: u8,
105     pub num_output_channels: u8,
106     pub num_clut_grid_points: u8,
107     pub e00: s15Fixed16Number,
108     pub e01: s15Fixed16Number,
109     pub e02: s15Fixed16Number,
110     pub e10: s15Fixed16Number,
111     pub e11: s15Fixed16Number,
112     pub e12: s15Fixed16Number,
113     pub e20: s15Fixed16Number,
114     pub e21: s15Fixed16Number,
115     pub e22: s15Fixed16Number,
116     pub num_input_table_entries: u16,
117     pub num_output_table_entries: u16,
118     pub input_table: Vec<f32>,
119     pub clut_table: Vec<f32>,
120     pub output_table: Vec<f32>,
121 }
122 
123 #[repr(C)]
124 #[derive(Copy, Clone, Default)]
125 pub struct XYZNumber {
126     pub X: s15Fixed16Number,
127     pub Y: s15Fixed16Number,
128     pub Z: s15Fixed16Number,
129 }
130 
131 /// A color in the CIE xyY color space
132 /* the names for the following two types are sort of ugly */
133 #[repr(C)]
134 #[derive(Copy, Clone)]
135 pub struct qcms_CIE_xyY {
136     pub x: f64,
137     pub y: f64,
138     pub Y: f64,
139 }
140 
141 /// a set of CIE_xyY values that can use to describe the primaries of a color space
142 #[repr(C)]
143 #[derive(Copy, Clone)]
144 pub struct qcms_CIE_xyYTRIPLE {
145     pub red: qcms_CIE_xyY,
146     pub green: qcms_CIE_xyY,
147     pub blue: qcms_CIE_xyY,
148 }
149 
150 struct Tag {
151     signature: u32,
152     offset: u32,
153     size: u32,
154 }
155 
156 /* It might be worth having a unified limit on content controlled
157  * allocation per profile. This would remove the need for many
158  * of the arbitrary limits that we used */
159 
160 type TagIndex = [Tag];
161 
162 /* a wrapper around the memory that we are going to parse
163  * into a qcms_profile */
164 struct MemSource<'a> {
165     buf: &'a [u8],
166     valid: bool,
167     invalid_reason: Option<&'static str>,
168 }
169 pub type uInt8Number = u8;
170 #[inline]
uInt8Number_to_float(a: uInt8Number) -> f32171 fn uInt8Number_to_float(a: uInt8Number) -> f32 {
172     a as f32 / 255.0
173 }
174 
175 #[inline]
uInt16Number_to_float(a: uInt16Number) -> f32176 fn uInt16Number_to_float(a: uInt16Number) -> f32 {
177     a as f32 / 65535.0
178 }
179 
invalid_source(mut mem: &mut MemSource, reason: &'static str)180 fn invalid_source(mut mem: &mut MemSource, reason: &'static str) {
181     mem.valid = false;
182     mem.invalid_reason = Some(reason);
183 }
read_u32(mem: &mut MemSource, offset: usize) -> u32184 fn read_u32(mem: &mut MemSource, offset: usize) -> u32 {
185     let val = mem.buf.get(offset..offset + 4);
186     if let Some(val) = val {
187         let val = val.try_into().unwrap();
188         u32::from_be_bytes(val)
189     } else {
190         invalid_source(mem, "Invalid offset");
191         0
192     }
193 }
read_u16(mem: &mut MemSource, offset: usize) -> u16194 fn read_u16(mem: &mut MemSource, offset: usize) -> u16 {
195     let val = mem.buf.get(offset..offset + 2);
196     if let Some(val) = val {
197         let val = val.try_into().unwrap();
198         u16::from_be_bytes(val)
199     } else {
200         invalid_source(mem, "Invalid offset");
201         0
202     }
203 }
read_u8(mem: &mut MemSource, offset: usize) -> u8204 fn read_u8(mem: &mut MemSource, offset: usize) -> u8 {
205     let val = mem.buf.get(offset);
206     if let Some(val) = val {
207         *val
208     } else {
209         invalid_source(mem, "Invalid offset");
210         0
211     }
212 }
read_s15Fixed16Number(mem: &mut MemSource, offset: usize) -> s15Fixed16Number213 fn read_s15Fixed16Number(mem: &mut MemSource, offset: usize) -> s15Fixed16Number {
214     read_u32(mem, offset) as s15Fixed16Number
215 }
read_uInt8Number(mem: &mut MemSource, offset: usize) -> uInt8Number216 fn read_uInt8Number(mem: &mut MemSource, offset: usize) -> uInt8Number {
217     read_u8(mem, offset)
218 }
read_uInt16Number(mem: &mut MemSource, offset: usize) -> uInt16Number219 fn read_uInt16Number(mem: &mut MemSource, offset: usize) -> uInt16Number {
220     read_u16(mem, offset)
221 }
write_u32(mem: &mut [u8], offset: usize, value: u32)222 pub fn write_u32(mem: &mut [u8], offset: usize, value: u32) {
223     // we use get() and expect() instead of [..] so there's only one call to panic
224     // instead of two
225     mem.get_mut(offset..offset + std::mem::size_of_val(&value))
226         .expect("OOB")
227         .copy_from_slice(&value.to_be_bytes());
228 }
write_u16(mem: &mut [u8], offset: usize, value: u16)229 pub fn write_u16(mem: &mut [u8], offset: usize, value: u16) {
230     // we use get() and expect() instead of [..] so there's only one call to panic
231     // intead of two
232     mem.get_mut(offset..offset + std::mem::size_of_val(&value))
233         .expect("OOB")
234         .copy_from_slice(&value.to_be_bytes());
235 }
236 
237 /* An arbitrary 4MB limit on profile size */
238 pub(crate) const MAX_PROFILE_SIZE: usize = 1024 * 1024 * 4;
239 const MAX_TAG_COUNT: u32 = 1024;
240 
check_CMM_type_signature(_src: &mut MemSource)241 fn check_CMM_type_signature(_src: &mut MemSource) {
242     //uint32_t CMM_type_signature = read_u32(src, 4);
243     //TODO: do the check?
244 }
check_profile_version(src: &mut MemSource)245 fn check_profile_version(src: &mut MemSource) {
246     /*
247     uint8_t major_revision = read_u8(src, 8 + 0);
248     uint8_t minor_revision = read_u8(src, 8 + 1);
249     */
250     let reserved1: u8 = read_u8(src, (8 + 2) as usize);
251     let reserved2: u8 = read_u8(src, (8 + 3) as usize);
252     /* Checking the version doesn't buy us anything
253     if (major_revision != 0x4) {
254         if (major_revision > 0x2)
255             invalid_source(src, "Unsupported major revision");
256         if (minor_revision > 0x40)
257             invalid_source(src, "Unsupported minor revision");
258     }
259     */
260     if reserved1 != 0 || reserved2 != 0 {
261         invalid_source(src, "Invalid reserved bytes");
262     };
263 }
264 
265 const INPUT_DEVICE_PROFILE: u32 = 0x73636e72; // 'scnr'
266 pub const DISPLAY_DEVICE_PROFILE: u32 = 0x6d6e7472; // 'mntr'
267 const OUTPUT_DEVICE_PROFILE: u32 = 0x70727472; // 'prtr'
268 const DEVICE_LINK_PROFILE: u32 = 0x6c696e6b; // 'link'
269 const COLOR_SPACE_PROFILE: u32 = 0x73706163; // 'spac'
270 const ABSTRACT_PROFILE: u32 = 0x61627374; // 'abst'
271 const NAMED_COLOR_PROFILE: u32 = 0x6e6d636c; // 'nmcl'
272 
read_class_signature(mut profile: &mut Profile, mem: &mut MemSource)273 fn read_class_signature(mut profile: &mut Profile, mem: &mut MemSource) {
274     profile.class_type = read_u32(mem, 12);
275     match profile.class_type {
276         DISPLAY_DEVICE_PROFILE
277         | INPUT_DEVICE_PROFILE
278         | OUTPUT_DEVICE_PROFILE
279         | COLOR_SPACE_PROFILE => {}
280         _ => {
281             invalid_source(mem, "Invalid  Profile/Device Class signature");
282         }
283     };
284 }
read_color_space(mut profile: &mut Profile, mem: &mut MemSource)285 fn read_color_space(mut profile: &mut Profile, mem: &mut MemSource) {
286     profile.color_space = read_u32(mem, 16);
287     match profile.color_space {
288         RGB_SIGNATURE | GRAY_SIGNATURE => {}
289         #[cfg(feature = "cmyk")]
290         CMYK_SIGNATURE => {}
291         _ => {
292             invalid_source(mem, "Unsupported colorspace");
293         }
294     };
295 }
read_pcs(mut profile: &mut Profile, mem: &mut MemSource)296 fn read_pcs(mut profile: &mut Profile, mem: &mut MemSource) {
297     profile.pcs = read_u32(mem, 20);
298     match profile.pcs {
299         XYZ_SIGNATURE | LAB_SIGNATURE => {}
300         _ => {
301             invalid_source(mem, "Unsupported pcs");
302         }
303     };
304 }
read_tag_table(_profile: &mut Profile, mem: &mut MemSource) -> Vec<Tag>305 fn read_tag_table(_profile: &mut Profile, mem: &mut MemSource) -> Vec<Tag> {
306     let count = read_u32(mem, 128);
307     if count > MAX_TAG_COUNT {
308         invalid_source(mem, "max number of tags exceeded");
309         return Vec::new();
310     }
311     let mut index = Vec::with_capacity(count as usize);
312     for i in 0..count {
313         index.push(Tag {
314             signature: read_u32(mem, (128 + 4 + 4 * i * 3) as usize),
315             offset: read_u32(mem, (128 + 4 + 4 * i * 3 + 4) as usize),
316             size: read_u32(mem, (128 + 4 + 4 * i * 3 + 8) as usize),
317         });
318     }
319 
320     index
321 }
322 
323 /// Checks a profile for obvious inconsistencies and returns
324 /// true if the profile looks bogus and should probably be
325 /// ignored.
326 #[no_mangle]
qcms_profile_is_bogus(profile: &mut Profile) -> bool327 pub extern "C" fn qcms_profile_is_bogus(profile: &mut Profile) -> bool {
328     let mut sum: [f32; 3] = [0.; 3];
329     let mut target: [f32; 3] = [0.; 3];
330     let mut tolerance: [f32; 3] = [0.; 3];
331     let rX: f32;
332     let rY: f32;
333     let rZ: f32;
334     let gX: f32;
335     let gY: f32;
336     let gZ: f32;
337     let bX: f32;
338     let bY: f32;
339     let bZ: f32;
340     let negative: bool;
341     let mut i: u32;
342     // We currently only check the bogosity of RGB profiles
343     if profile.color_space != RGB_SIGNATURE {
344         return false;
345     }
346     if profile.A2B0.is_some()
347         || profile.B2A0.is_some()
348         || profile.mAB.is_some()
349         || profile.mBA.is_some()
350     {
351         return false;
352     }
353     rX = s15Fixed16Number_to_float(profile.redColorant.X);
354     rY = s15Fixed16Number_to_float(profile.redColorant.Y);
355     rZ = s15Fixed16Number_to_float(profile.redColorant.Z);
356     gX = s15Fixed16Number_to_float(profile.greenColorant.X);
357     gY = s15Fixed16Number_to_float(profile.greenColorant.Y);
358     gZ = s15Fixed16Number_to_float(profile.greenColorant.Z);
359     bX = s15Fixed16Number_to_float(profile.blueColorant.X);
360     bY = s15Fixed16Number_to_float(profile.blueColorant.Y);
361     bZ = s15Fixed16Number_to_float(profile.blueColorant.Z);
362     // Sum the values; they should add up to something close to white
363     sum[0] = rX + gX + bX;
364     sum[1] = rY + gY + bY;
365     sum[2] = rZ + gZ + bZ;
366     // Build our target vector (see mozilla bug 460629)
367     target[0] = 0.96420;
368     target[1] = 1.00000;
369     target[2] = 0.82491;
370     // Our tolerance vector - Recommended by Chris Murphy based on
371     // conversion from the LAB space criterion of no more than 3 in any one
372     // channel. This is similar to, but slightly more tolerant than Adobe's
373     // criterion.
374     tolerance[0] = 0.02;
375     tolerance[1] = 0.02;
376     tolerance[2] = 0.04;
377     // Compare with our tolerance
378     i = 0;
379     while i < 3 {
380         if !(sum[i as usize] - tolerance[i as usize] <= target[i as usize]
381             && sum[i as usize] + tolerance[i as usize] >= target[i as usize])
382         {
383             return true;
384         }
385         i += 1
386     }
387     if !cfg!(target_os = "macos") {
388         negative = (rX < 0.)
389             || (rY < 0.)
390             || (rZ < 0.)
391             || (gX < 0.)
392             || (gY < 0.)
393             || (gZ < 0.)
394             || (bX < 0.)
395             || (bY < 0.)
396             || (bZ < 0.);
397     } else {
398         // Chromatic adaption to D50 can result in negative XYZ, but the white
399         // point D50 tolerance test has passed. Accept negative values herein.
400         // See https://bugzilla.mozilla.org/show_bug.cgi?id=498245#c18 onwards
401         // for discussion about whether profile XYZ can or cannot be negative,
402         // per the spec. Also the https://bugzil.la/450923 user report.
403 
404         // FIXME: allow this relaxation on all ports?
405         negative = false; // bogus
406     }
407     if negative {
408         return true;
409     }
410     // All Good
411     false
412 }
413 
414 pub const TAG_bXYZ: u32 = 0x6258595a;
415 pub const TAG_gXYZ: u32 = 0x6758595a;
416 pub const TAG_rXYZ: u32 = 0x7258595a;
417 pub const TAG_rTRC: u32 = 0x72545243;
418 pub const TAG_bTRC: u32 = 0x62545243;
419 pub const TAG_gTRC: u32 = 0x67545243;
420 pub const TAG_kTRC: u32 = 0x6b545243;
421 pub const TAG_A2B0: u32 = 0x41324230;
422 pub const TAG_B2A0: u32 = 0x42324130;
423 pub const TAG_CHAD: u32 = 0x63686164;
424 
find_tag(index: &TagIndex, tag_id: u32) -> Option<&Tag>425 fn find_tag(index: &TagIndex, tag_id: u32) -> Option<&Tag> {
426     for t in index {
427         if t.signature == tag_id {
428             return Some(t);
429         }
430     }
431     None
432 }
433 
434 pub const XYZ_TYPE: u32 = 0x58595a20; // 'XYZ '
435 pub const CURVE_TYPE: u32 = 0x63757276; // 'curv'
436 pub const PARAMETRIC_CURVE_TYPE: u32 = 0x70617261; // 'para'
437 pub const LUT16_TYPE: u32 = 0x6d667432; // 'mft2'
438 pub const LUT8_TYPE: u32 = 0x6d667431; // 'mft1'
439 pub const LUT_MAB_TYPE: u32 = 0x6d414220; // 'mAB '
440 pub const LUT_MBA_TYPE: u32 = 0x6d424120; // 'mBA '
441 pub const CHROMATIC_TYPE: u32 = 0x73663332; // 'sf32'
442 
read_tag_s15Fixed16ArrayType(src: &mut MemSource, tag: &Tag) -> Matrix443 fn read_tag_s15Fixed16ArrayType(src: &mut MemSource, tag: &Tag) -> Matrix {
444     let mut matrix: Matrix = Matrix {
445         m: [[0.; 3]; 3],
446     };
447     let offset: u32 = tag.offset;
448     let type_0: u32 = read_u32(src, offset as usize);
449     // Check mandatory type signature for s16Fixed16ArrayType
450     if type_0 != CHROMATIC_TYPE {
451         invalid_source(src, "unexpected type, expected \'sf32\'");
452     }
453     for i in 0..=8 {
454         matrix.m[(i / 3) as usize][(i % 3) as usize] = s15Fixed16Number_to_float(
455             read_s15Fixed16Number(src, (offset + 8 + (i * 4) as u32) as usize),
456         );
457     }
458     matrix
459 }
read_tag_XYZType(src: &mut MemSource, index: &TagIndex, tag_id: u32) -> XYZNumber460 fn read_tag_XYZType(src: &mut MemSource, index: &TagIndex, tag_id: u32) -> XYZNumber {
461     let mut num: XYZNumber = {
462         let init = XYZNumber { X: 0, Y: 0, Z: 0 };
463         init
464     };
465     let tag = find_tag(&index, tag_id);
466     if let Some(tag) = tag {
467         let offset: u32 = tag.offset;
468         let type_0: u32 = read_u32(src, offset as usize);
469         if type_0 != XYZ_TYPE {
470             invalid_source(src, "unexpected type, expected XYZ");
471         }
472         num.X = read_s15Fixed16Number(src, (offset + 8) as usize);
473         num.Y = read_s15Fixed16Number(src, (offset + 12) as usize);
474         num.Z = read_s15Fixed16Number(src, (offset + 16) as usize)
475     } else {
476         invalid_source(src, "missing xyztag");
477     }
478     num
479 }
480 // Read the tag at a given offset rather then the tag_index.
481 // This method is used when reading mAB tags where nested curveType are
482 // present that are not part of the tag_index.
read_curveType(src: &mut MemSource, offset: u32, len: &mut u32) -> Option<Box<curveType>>483 fn read_curveType(src: &mut MemSource, offset: u32, len: &mut u32) -> Option<Box<curveType>> {
484     const COUNT_TO_LENGTH: [u32; 5] = [1, 3, 4, 5, 7]; //PARAMETRIC_CURVE_TYPE
485     let type_0: u32 = read_u32(src, offset as usize);
486     let count: u32;
487     if type_0 != CURVE_TYPE && type_0 != PARAMETRIC_CURVE_TYPE {
488         invalid_source(src, "unexpected type, expected CURV or PARA");
489         return None;
490     }
491     if type_0 == CURVE_TYPE {
492         count = read_u32(src, (offset + 8) as usize);
493         //arbitrary
494         if count > 40000 {
495             invalid_source(src, "curve size too large");
496             return None;
497         }
498         let mut table = Vec::with_capacity(count as usize);
499         for i in 0..count {
500             table.push(read_u16(src, (offset + 12 + i * 2) as usize));
501         }
502         *len = 12 + count * 2;
503         Some(Box::new(curveType::Curve(table)))
504     } else {
505         count = read_u16(src, (offset + 8) as usize) as u32;
506         if count > 4 {
507             invalid_source(src, "parametric function type not supported.");
508             return None;
509         }
510         let mut params = Vec::with_capacity(count as usize);
511         for i in 0..COUNT_TO_LENGTH[count as usize] {
512             params.push(s15Fixed16Number_to_float(read_s15Fixed16Number(
513                 src,
514                 (offset + 12 + i * 4) as usize,
515             )));
516         }
517         *len = 12 + COUNT_TO_LENGTH[count as usize] * 4;
518         if count == 1 || count == 2 {
519             /* we have a type 1 or type 2 function that has a division by 'a' */
520             let a: f32 = params[1];
521             if a == 0.0 {
522                 invalid_source(src, "parametricCurve definition causes division by zero");
523             }
524         }
525         Some(Box::new(curveType::Parametric(params)))
526     }
527 }
read_tag_curveType( src: &mut MemSource, index: &TagIndex, tag_id: u32, ) -> Option<Box<curveType>>528 fn read_tag_curveType(
529     src: &mut MemSource,
530     index: &TagIndex,
531     tag_id: u32,
532 ) -> Option<Box<curveType>> {
533     let tag = find_tag(index, tag_id);
534     if let Some(tag) = tag {
535         let mut len: u32 = 0;
536         return read_curveType(src, tag.offset, &mut len);
537     } else {
538         invalid_source(src, "missing curvetag");
539     }
540     None
541 }
542 
543 const MAX_LUT_SIZE: u32 = 500000; // arbitrary
544 const MAX_CHANNELS: usize = 10; // arbitrary
read_nested_curveType( src: &mut MemSource, curveArray: &mut [Option<Box<curveType>>; MAX_CHANNELS], num_channels: u8, curve_offset: u32, )545 fn read_nested_curveType(
546     src: &mut MemSource,
547     curveArray: &mut [Option<Box<curveType>>; MAX_CHANNELS],
548     num_channels: u8,
549     curve_offset: u32,
550 ) {
551     let mut channel_offset: u32 = 0;
552     for i in 0..usize::from(num_channels) {
553         let mut tag_len: u32 = 0;
554         curveArray[i] = read_curveType(src, curve_offset + channel_offset, &mut tag_len);
555         if curveArray[i].is_none() {
556             invalid_source(src, "invalid nested curveType curve");
557             break;
558         } else {
559             channel_offset += tag_len;
560             // 4 byte aligned
561             if tag_len % 4 != 0 {
562                 channel_offset += 4 - tag_len % 4
563             }
564         }
565     }
566 }
567 
568 /* See section 10.10 for specs */
read_tag_lutmABType(src: &mut MemSource, tag: &Tag) -> Option<Box<lutmABType>>569 fn read_tag_lutmABType(src: &mut MemSource, tag: &Tag) -> Option<Box<lutmABType>> {
570     let offset: u32 = tag.offset;
571     let mut clut_size: u32 = 1;
572     let type_0: u32 = read_u32(src, offset as usize);
573     if type_0 != LUT_MAB_TYPE && type_0 != LUT_MBA_TYPE {
574         return None;
575     }
576     let num_in_channels = read_u8(src, (offset + 8) as usize);
577     let num_out_channels = read_u8(src, (offset + 9) as usize);
578     if num_in_channels > 10 || num_out_channels > 10 {
579         return None;
580     }
581     // We require 3in/out channels since we only support RGB->XYZ (or RGB->LAB)
582     // XXX: If we remove this restriction make sure that the number of channels
583     //      is less or equal to the maximum number of mAB curves in qcmsint.h
584     //      also check for clut_size overflow. Also make sure it's != 0
585     if num_in_channels != 3 || num_out_channels != 3 {
586         return None;
587     }
588     // some of this data is optional and is denoted by a zero offset
589     // we also use this to track their existance
590     let mut a_curve_offset = read_u32(src, (offset + 28) as usize);
591     let mut clut_offset = read_u32(src, (offset + 24) as usize);
592     let mut m_curve_offset = read_u32(src, (offset + 20) as usize);
593     let mut matrix_offset = read_u32(src, (offset + 16) as usize);
594     let mut b_curve_offset = read_u32(src, (offset + 12) as usize);
595     // Convert offsets relative to the tag to relative to the profile
596     // preserve zero for optional fields
597     if a_curve_offset != 0 {
598         a_curve_offset += offset
599     }
600     if clut_offset != 0 {
601         clut_offset += offset
602     }
603     if m_curve_offset != 0 {
604         m_curve_offset += offset
605     }
606     if matrix_offset != 0 {
607         matrix_offset += offset
608     }
609     if b_curve_offset != 0 {
610         b_curve_offset += offset
611     }
612     if clut_offset != 0 {
613         debug_assert!(num_in_channels == 3);
614         // clut_size can not overflow since lg(256^num_in_channels) = 24 bits.
615         for i in 0..u32::from(num_in_channels) {
616             clut_size *= read_u8(src, (clut_offset + i) as usize) as u32;
617             if clut_size == 0 {
618                 invalid_source(src, "bad clut_size");
619             }
620         }
621     } else {
622         clut_size = 0
623     }
624     // 24bits * 3 won't overflow either
625     clut_size *= num_out_channels as u32;
626     if clut_size > MAX_LUT_SIZE {
627         return None;
628     }
629 
630     let mut lut = Box::new(lutmABType::default());
631 
632     if clut_offset != 0 {
633         for i in 0..usize::from(num_in_channels) {
634             lut.num_grid_points[i] = read_u8(src, clut_offset as usize + i);
635             if lut.num_grid_points[i] == 0 {
636                 invalid_source(src, "bad grid_points");
637             }
638         }
639     }
640     // Reverse the processing of transformation elements for mBA type.
641     lut.reversed = type_0 == LUT_MBA_TYPE;
642     lut.num_in_channels = num_in_channels;
643     lut.num_out_channels = num_out_channels;
644     if matrix_offset != 0 {
645         // read the matrix if we have it
646         lut.e00 = read_s15Fixed16Number(src, (matrix_offset + (4 * 0) as u32) as usize); // the caller checks that this doesn't happen
647         lut.e01 = read_s15Fixed16Number(src, (matrix_offset + (4 * 1) as u32) as usize);
648         lut.e02 = read_s15Fixed16Number(src, (matrix_offset + (4 * 2) as u32) as usize);
649         lut.e10 = read_s15Fixed16Number(src, (matrix_offset + (4 * 3) as u32) as usize);
650         lut.e11 = read_s15Fixed16Number(src, (matrix_offset + (4 * 4) as u32) as usize);
651         lut.e12 = read_s15Fixed16Number(src, (matrix_offset + (4 * 5) as u32) as usize);
652         lut.e20 = read_s15Fixed16Number(src, (matrix_offset + (4 * 6) as u32) as usize);
653         lut.e21 = read_s15Fixed16Number(src, (matrix_offset + (4 * 7) as u32) as usize);
654         lut.e22 = read_s15Fixed16Number(src, (matrix_offset + (4 * 8) as u32) as usize);
655         lut.e03 = read_s15Fixed16Number(src, (matrix_offset + (4 * 9) as u32) as usize);
656         lut.e13 = read_s15Fixed16Number(src, (matrix_offset + (4 * 10) as u32) as usize);
657         lut.e23 = read_s15Fixed16Number(src, (matrix_offset + (4 * 11) as u32) as usize)
658     }
659     if a_curve_offset != 0 {
660         read_nested_curveType(src, &mut lut.a_curves, num_in_channels, a_curve_offset);
661     }
662     if m_curve_offset != 0 {
663         read_nested_curveType(src, &mut lut.m_curves, num_out_channels, m_curve_offset);
664     }
665     if b_curve_offset != 0 {
666         read_nested_curveType(src, &mut lut.b_curves, num_out_channels, b_curve_offset);
667     } else {
668         invalid_source(src, "B curves required");
669     }
670     if clut_offset != 0 {
671         let clut_precision = read_u8(src, (clut_offset + 16) as usize);
672         let mut clut_table = Vec::with_capacity(clut_size as usize);
673         if clut_precision == 1 {
674             for i in 0..clut_size {
675                 clut_table.push(uInt8Number_to_float(read_uInt8Number(
676                     src,
677                     (clut_offset + 20 + i * 1) as usize,
678                 )));
679             }
680             lut.clut_table = Some(clut_table);
681         } else if clut_precision == 2 {
682             for i in 0..clut_size {
683                 clut_table.push(uInt16Number_to_float(read_uInt16Number(
684                     src,
685                     (clut_offset + 20 + i * 2) as usize,
686                 )));
687             }
688             lut.clut_table = Some(clut_table);
689         } else {
690             invalid_source(src, "Invalid clut precision");
691         }
692     }
693     if !src.valid {
694         return None;
695     }
696     Some(lut)
697 }
read_tag_lutType(src: &mut MemSource, tag: &Tag) -> Option<Box<lutType>>698 fn read_tag_lutType(src: &mut MemSource, tag: &Tag) -> Option<Box<lutType>> {
699     let offset: u32 = tag.offset;
700     let type_0: u32 = read_u32(src, offset as usize);
701     let num_input_table_entries: u16;
702     let num_output_table_entries: u16;
703     let input_offset: u32;
704     let entry_size: usize;
705     if type_0 == LUT8_TYPE {
706         num_input_table_entries = 256u16;
707         num_output_table_entries = 256u16;
708         entry_size = 1;
709         input_offset = 48
710     } else if type_0 == LUT16_TYPE {
711         num_input_table_entries = read_u16(src, (offset + 48) as usize);
712         num_output_table_entries = read_u16(src, (offset + 50) as usize);
713 
714         // these limits come from the spec
715         if num_input_table_entries < 2
716             || num_input_table_entries > 4096
717             || num_output_table_entries < 2
718             || num_output_table_entries > 4096
719         {
720             invalid_source(src, "Bad channel count");
721             return None;
722         }
723         entry_size = 2;
724         input_offset = 52
725     } else {
726         debug_assert!(false);
727         invalid_source(src, "Unexpected lut type");
728         return None;
729     }
730     let in_chan = read_u8(src, (offset + 8) as usize);
731     let out_chan = read_u8(src, (offset + 9) as usize);
732     if !(in_chan == 3 || in_chan == 4) || out_chan != 3 {
733         invalid_source(src, "CLUT only supports RGB and CMYK");
734         return None;
735     }
736 
737     let grid_points = read_u8(src, (offset + 10) as usize);
738     let clut_size = match (grid_points as u32).checked_pow(in_chan as u32) {
739         Some(clut_size) => clut_size,
740         _ => {
741             invalid_source(src, "CLUT size overflow");
742             return None;
743         }
744     };
745     if clut_size > MAX_LUT_SIZE {
746         invalid_source(src, "CLUT too large");
747         return None;
748     }
749     if clut_size <= 0 {
750         invalid_source(src, "CLUT must not be empty.");
751         return None;
752     }
753 
754     let e00 = read_s15Fixed16Number(src, (offset + 12) as usize);
755     let e01 = read_s15Fixed16Number(src, (offset + 16) as usize);
756     let e02 = read_s15Fixed16Number(src, (offset + 20) as usize);
757     let e10 = read_s15Fixed16Number(src, (offset + 24) as usize);
758     let e11 = read_s15Fixed16Number(src, (offset + 28) as usize);
759     let e12 = read_s15Fixed16Number(src, (offset + 32) as usize);
760     let e20 = read_s15Fixed16Number(src, (offset + 36) as usize);
761     let e21 = read_s15Fixed16Number(src, (offset + 40) as usize);
762     let e22 = read_s15Fixed16Number(src, (offset + 44) as usize);
763 
764     let mut input_table = Vec::with_capacity((num_input_table_entries * in_chan as u16) as usize);
765     for i in 0..(num_input_table_entries * in_chan as u16) {
766         if type_0 == LUT8_TYPE {
767             input_table.push(uInt8Number_to_float(read_uInt8Number(
768                 src,
769                 (offset + input_offset) as usize + i as usize * entry_size,
770             )))
771         } else {
772             input_table.push(uInt16Number_to_float(read_uInt16Number(
773                 src,
774                 (offset + input_offset) as usize + i as usize * entry_size,
775             )))
776         }
777     }
778     let clut_offset = ((offset + input_offset) as usize
779         + (num_input_table_entries as i32 * in_chan as i32) as usize * entry_size)
780         as u32;
781 
782     let mut clut_table = Vec::with_capacity((clut_size * out_chan as u32) as usize);
783     for i in 0..clut_size * out_chan as u32 {
784         if type_0 == LUT8_TYPE {
785             clut_table.push(uInt8Number_to_float(read_uInt8Number(
786                 src,
787                 clut_offset as usize + i as usize * entry_size,
788             )));
789         } else if type_0 == LUT16_TYPE {
790             clut_table.push(uInt16Number_to_float(read_uInt16Number(
791                 src,
792                 clut_offset as usize + i as usize * entry_size,
793             )));
794         }
795     }
796 
797     let output_offset =
798         (clut_offset as usize + (clut_size * out_chan as u32) as usize * entry_size) as u32;
799 
800     let mut output_table =
801         Vec::with_capacity((num_output_table_entries * out_chan as u16) as usize);
802     for i in 0..num_output_table_entries as i32 * out_chan as i32 {
803         if type_0 == LUT8_TYPE {
804             output_table.push(uInt8Number_to_float(read_uInt8Number(
805                 src,
806                 output_offset as usize + i as usize * entry_size,
807             )))
808         } else {
809             output_table.push(uInt16Number_to_float(read_uInt16Number(
810                 src,
811                 output_offset as usize + i as usize * entry_size,
812             )))
813         }
814     }
815     Some(Box::new(lutType {
816         num_input_table_entries,
817         num_output_table_entries,
818         num_input_channels: in_chan,
819         num_output_channels: out_chan,
820         num_clut_grid_points: grid_points,
821         e00,
822         e01,
823         e02,
824         e10,
825         e11,
826         e12,
827         e20,
828         e21,
829         e22,
830         input_table,
831         clut_table,
832         output_table,
833     }))
834 }
read_rendering_intent(mut profile: &mut Profile, src: &mut MemSource)835 fn read_rendering_intent(mut profile: &mut Profile, src: &mut MemSource) {
836     let intent = read_u32(src, 64);
837     profile.rendering_intent = match intent {
838         x if x == Perceptual as u32 => Perceptual,
839         x if x == RelativeColorimetric as u32 => RelativeColorimetric,
840         x if x == Saturation as u32 => Saturation,
841         x if x == AbsoluteColorimetric as u32 => AbsoluteColorimetric,
842         _ => {
843             invalid_source(src, "unknown rendering intent");
844             Intent::default()
845         }
846     };
847 }
profile_create() -> Box<Profile>848 fn profile_create() -> Box<Profile> {
849     Box::new(Profile::default())
850 }
851 /* build sRGB gamma table */
852 /* based on cmsBuildParametricGamma() */
build_sRGB_gamma_table(num_entries: i32) -> Vec<u16>853 fn build_sRGB_gamma_table(num_entries: i32) -> Vec<u16> {
854     /* taken from lcms: Build_sRGBGamma() */
855     let gamma: f64 = 2.4;
856     let a: f64 = 1.0 / 1.055;
857     let b: f64 = 0.055 / 1.055;
858     let c: f64 = 1.0 / 12.92;
859     let d: f64 = 0.04045;
860     let mut table = Vec::with_capacity(num_entries as usize);
861 
862     for i in 0..num_entries {
863         let x: f64 = i as f64 / (num_entries - 1) as f64;
864         let y: f64;
865         let mut output: f64;
866         // IEC 61966-2.1 (sRGB)
867         // Y = (aX + b)^Gamma | X >= d
868         // Y = cX             | X < d
869         if x >= d {
870             let e: f64 = a * x + b;
871             if e > 0. {
872                 y = e.powf(gamma)
873             } else {
874                 y = 0.
875             }
876         } else {
877             y = c * x
878         }
879         // Saturate -- this could likely move to a separate function
880         output = y * 65535.0 + 0.5;
881         if output > 65535.0 {
882             output = 65535.0
883         }
884         if output < 0.0 {
885             output = 0.0
886         }
887         table.push(output.floor() as u16);
888     }
889     table
890 }
curve_from_table(table: &[u16]) -> Box<curveType>891 fn curve_from_table(table: &[u16]) -> Box<curveType> {
892     Box::new(curveType::Curve(table.to_vec()))
893 }
float_to_u8Fixed8Number(a: f32) -> u16894 pub fn float_to_u8Fixed8Number(a: f32) -> u16 {
895     if a > 255.0 + 255.0 / 256f32 {
896         0xffffu16
897     } else if a < 0.0 {
898         0u16
899     } else {
900         (a * 256.0 + 0.5).floor() as u16
901     }
902 }
903 
curve_from_gamma(gamma: f32) -> Box<curveType>904 fn curve_from_gamma(gamma: f32) -> Box<curveType> {
905     Box::new(curveType::Curve(vec![float_to_u8Fixed8Number(gamma)]))
906 }
907 
identity_curve() -> Box<curveType>908 fn identity_curve() -> Box<curveType> {
909     Box::new(curveType::Curve(Vec::new()))
910 }
911 
912 /* from lcms: cmsWhitePointFromTemp */
913 /* tempK must be >= 4000. and <= 25000.
914  * Invalid values of tempK will return
915  * (x,y,Y) = (-1.0, -1.0, -1.0)
916  * similar to argyll: icx_DTEMP2XYZ() */
white_point_from_temp(temp_K: i32) -> qcms_CIE_xyY917 fn white_point_from_temp(temp_K: i32) -> qcms_CIE_xyY {
918     let mut white_point: qcms_CIE_xyY = qcms_CIE_xyY {
919         x: 0.,
920         y: 0.,
921         Y: 0.,
922     };
923     // No optimization provided.
924     let T = temp_K as f64; // Square
925     let T2 = T * T; // Cube
926     let T3 = T2 * T;
927     // For correlated color temperature (T) between 4000K and 7000K:
928     let x = if T >= 4000.0 && T <= 7000.0 {
929         -4.6070 * (1E9 / T3) + 2.9678 * (1E6 / T2) + 0.09911 * (1E3 / T) + 0.244063
930     } else if T > 7000.0 && T <= 25000.0 {
931         -2.0064 * (1E9 / T3) + 1.9018 * (1E6 / T2) + 0.24748 * (1E3 / T) + 0.237040
932     } else {
933         // or for correlated color temperature (T) between 7000K and 25000K:
934         // Invalid tempK
935         white_point.x = -1.0;
936         white_point.y = -1.0;
937         white_point.Y = -1.0;
938         debug_assert!(false, "invalid temp");
939         return white_point;
940     };
941     // Obtain y(x)
942     let y = -3.000 * (x * x) + 2.870 * x - 0.275;
943     // wave factors (not used, but here for futures extensions)
944     // let M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y);
945     // let M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y);
946     // Fill white_point struct
947     white_point.x = x;
948     white_point.y = y;
949     white_point.Y = 1.0;
950     white_point
951 }
952 #[no_mangle]
qcms_white_point_sRGB() -> qcms_CIE_xyY953 pub extern "C" fn qcms_white_point_sRGB() -> qcms_CIE_xyY {
954     white_point_from_temp(6504)
955 }
956 
957 impl Profile {
958     //XXX: it would be nice if we had a way of ensuring
959     // everything in a profile was initialized regardless of how it was created
960     //XXX: should this also be taking a black_point?
961     /* similar to CGColorSpaceCreateCalibratedRGB */
new_rgb_with_table( white_point: qcms_CIE_xyY, primaries: qcms_CIE_xyYTRIPLE, table: &[u16], ) -> Option<Box<Profile>>962     pub fn new_rgb_with_table(
963         white_point: qcms_CIE_xyY,
964         primaries: qcms_CIE_xyYTRIPLE,
965         table: &[u16],
966     ) -> Option<Box<Profile>> {
967         let mut profile = profile_create();
968         //XXX: should store the whitepoint
969         if !set_rgb_colorants(&mut profile, white_point, primaries) {
970             return None;
971         }
972         profile.redTRC = Some(curve_from_table(table));
973         profile.blueTRC = Some(curve_from_table(table));
974         profile.greenTRC = Some(curve_from_table(table));
975         profile.class_type = DISPLAY_DEVICE_PROFILE;
976         profile.rendering_intent = Perceptual;
977         profile.color_space = RGB_SIGNATURE;
978         profile.pcs = XYZ_TYPE;
979         Some(profile)
980     }
new_sRGB() -> Box<Profile>981     pub fn new_sRGB() -> Box<Profile> {
982         let Rec709Primaries = qcms_CIE_xyYTRIPLE {
983             red: {
984                 qcms_CIE_xyY {
985                     x: 0.6400,
986                     y: 0.3300,
987                     Y: 1.0,
988                 }
989             },
990             green: {
991                 qcms_CIE_xyY {
992                     x: 0.3000,
993                     y: 0.6000,
994                     Y: 1.0,
995                 }
996             },
997             blue: {
998                 qcms_CIE_xyY {
999                     x: 0.1500,
1000                     y: 0.0600,
1001                     Y: 1.0,
1002                 }
1003             },
1004         };
1005         let D65 = qcms_white_point_sRGB();
1006         let table = build_sRGB_gamma_table(1024);
1007 
1008         let mut srgb = Profile::new_rgb_with_table(D65, Rec709Primaries, &table).unwrap();
1009         srgb.is_srgb = Some(true);
1010         srgb
1011     }
1012 
1013     /// Returns true if this profile is sRGB
is_sRGB(&self) -> bool1014     pub fn is_sRGB(&self) -> bool {
1015         matches!(self.is_srgb, Some(true))
1016     }
1017 
new_sRGB_parametric() -> Box<Profile>1018     pub(crate) fn new_sRGB_parametric() -> Box<Profile> {
1019         let primaries = qcms_CIE_xyYTRIPLE {
1020             red: {
1021                 qcms_CIE_xyY {
1022                     x: 0.6400,
1023                     y: 0.3300,
1024                     Y: 1.0,
1025                 }
1026             },
1027             green: {
1028                 qcms_CIE_xyY {
1029                     x: 0.3000,
1030                     y: 0.6000,
1031                     Y: 1.0,
1032                 }
1033             },
1034             blue: {
1035                 qcms_CIE_xyY {
1036                     x: 0.1500,
1037                     y: 0.0600,
1038                     Y: 1.0,
1039                 }
1040             },
1041         };
1042         let white_point = qcms_white_point_sRGB();
1043         let mut profile = profile_create();
1044         set_rgb_colorants(&mut profile, white_point, primaries);
1045 
1046         let curve = Box::new(curveType::Parametric(vec![
1047             2.4,
1048             1. / 1.055,
1049             0.055 / 1.055,
1050             1. / 12.92,
1051             0.04045,
1052         ]));
1053         profile.redTRC = Some(curve.clone());
1054         profile.blueTRC = Some(curve.clone());
1055         profile.greenTRC = Some(curve);
1056         profile.class_type = DISPLAY_DEVICE_PROFILE;
1057         profile.rendering_intent = Perceptual;
1058         profile.color_space = RGB_SIGNATURE;
1059         profile.pcs = XYZ_TYPE;
1060         profile
1061     }
1062 
1063     /// Create a new profile with D50 adopted white and identity transform functions
new_XYZD50() -> Box<Profile>1064     pub fn new_XYZD50() -> Box<Profile> {
1065         let mut profile = profile_create();
1066         profile.redColorant.X = double_to_s15Fixed16Number(1.);
1067         profile.redColorant.Y = double_to_s15Fixed16Number(0.);
1068         profile.redColorant.Z = double_to_s15Fixed16Number(0.);
1069         profile.greenColorant.X = double_to_s15Fixed16Number(0.);
1070         profile.greenColorant.Y = double_to_s15Fixed16Number(1.);
1071         profile.greenColorant.Z = double_to_s15Fixed16Number(0.);
1072         profile.blueColorant.X = double_to_s15Fixed16Number(0.);
1073         profile.blueColorant.Y = double_to_s15Fixed16Number(0.);
1074         profile.blueColorant.Z = double_to_s15Fixed16Number(1.);
1075         profile.redTRC = Some(identity_curve());
1076         profile.blueTRC = Some(identity_curve());
1077         profile.greenTRC = Some(identity_curve());
1078 
1079         profile.class_type = DISPLAY_DEVICE_PROFILE;
1080         profile.rendering_intent = Perceptual;
1081         profile.color_space = RGB_SIGNATURE;
1082         profile.pcs = XYZ_TYPE;
1083         profile
1084     }
1085 
new_gray_with_gamma(gamma: f32) -> Box<Profile>1086     pub fn new_gray_with_gamma(gamma: f32) -> Box<Profile> {
1087         let mut profile = profile_create();
1088 
1089         profile.grayTRC = Some(curve_from_gamma(gamma));
1090         profile.class_type = DISPLAY_DEVICE_PROFILE;
1091         profile.rendering_intent = Perceptual;
1092         profile.color_space = GRAY_SIGNATURE;
1093         profile.pcs = XYZ_TYPE;
1094         profile
1095     }
1096 
new_rgb_with_gamma_set( white_point: qcms_CIE_xyY, primaries: qcms_CIE_xyYTRIPLE, redGamma: f32, greenGamma: f32, blueGamma: f32, ) -> Option<Box<Profile>>1097     pub fn new_rgb_with_gamma_set(
1098         white_point: qcms_CIE_xyY,
1099         primaries: qcms_CIE_xyYTRIPLE,
1100         redGamma: f32,
1101         greenGamma: f32,
1102         blueGamma: f32,
1103     ) -> Option<Box<Profile>> {
1104         let mut profile = profile_create();
1105 
1106         //XXX: should store the whitepoint
1107         if !set_rgb_colorants(&mut profile, white_point, primaries) {
1108             return None;
1109         }
1110         profile.redTRC = Some(curve_from_gamma(redGamma));
1111         profile.blueTRC = Some(curve_from_gamma(blueGamma));
1112         profile.greenTRC = Some(curve_from_gamma(greenGamma));
1113         profile.class_type = DISPLAY_DEVICE_PROFILE;
1114         profile.rendering_intent = Perceptual;
1115         profile.color_space = RGB_SIGNATURE;
1116         profile.pcs = XYZ_TYPE;
1117         Some(profile)
1118     }
1119 
new_from_slice(mem: &[u8]) -> Option<Box<Profile>>1120     pub fn new_from_slice(mem: &[u8]) -> Option<Box<Profile>> {
1121         let length: u32;
1122         let mut source: MemSource = MemSource {
1123             buf: mem,
1124             valid: false,
1125             invalid_reason: None,
1126         };
1127         let index;
1128         source.valid = true;
1129         let mut src: &mut MemSource = &mut source;
1130         if mem.len() < 4 {
1131             return None;
1132         }
1133         length = read_u32(src, 0);
1134         if length as usize <= mem.len() {
1135             // shrink the area that we can read if appropriate
1136             src.buf = &src.buf[0..length as usize];
1137         } else {
1138             return None;
1139         }
1140         /* ensure that the profile size is sane so it's easier to reason about */
1141         if src.buf.len() <= 64 || src.buf.len() >= MAX_PROFILE_SIZE {
1142             return None;
1143         }
1144         let mut profile = profile_create();
1145 
1146         check_CMM_type_signature(src);
1147         check_profile_version(src);
1148         read_class_signature(&mut profile, src);
1149         read_rendering_intent(&mut profile, src);
1150         read_color_space(&mut profile, src);
1151         read_pcs(&mut profile, src);
1152         //TODO read rest of profile stuff
1153         if !src.valid {
1154             return None;
1155         }
1156 
1157         index = read_tag_table(&mut profile, src);
1158         if !src.valid || index.is_empty() {
1159             return None;
1160         }
1161 
1162         if let Some(chad) = find_tag(&index, TAG_CHAD) {
1163             profile.chromaticAdaption = Some(read_tag_s15Fixed16ArrayType(src, chad))
1164         } else {
1165             profile.chromaticAdaption = None; //Signal the data is not present
1166         }
1167 
1168         if profile.class_type == DISPLAY_DEVICE_PROFILE
1169             || profile.class_type == INPUT_DEVICE_PROFILE
1170             || profile.class_type == OUTPUT_DEVICE_PROFILE
1171             || profile.class_type == COLOR_SPACE_PROFILE
1172         {
1173             if profile.color_space == RGB_SIGNATURE {
1174                 if let Some(A2B0) = find_tag(&index, TAG_A2B0) {
1175                     let lut_type = read_u32(src, A2B0.offset as usize);
1176                     if lut_type == LUT8_TYPE || lut_type == LUT16_TYPE {
1177                         profile.A2B0 = read_tag_lutType(src, A2B0)
1178                     } else if lut_type == LUT_MAB_TYPE {
1179                         profile.mAB = read_tag_lutmABType(src, A2B0)
1180                     }
1181                 }
1182                 if let Some(B2A0) = find_tag(&index, TAG_B2A0) {
1183                     let lut_type = read_u32(src, B2A0.offset as usize);
1184                     if lut_type == LUT8_TYPE || lut_type == LUT16_TYPE {
1185                         profile.B2A0 = read_tag_lutType(src, B2A0)
1186                     } else if lut_type == LUT_MBA_TYPE {
1187                         profile.mBA = read_tag_lutmABType(src, B2A0)
1188                     }
1189                 }
1190                 if find_tag(&index, TAG_rXYZ).is_some() || !SUPPORTS_ICCV4.load(Ordering::Relaxed) {
1191                     profile.redColorant = read_tag_XYZType(src, &index, TAG_rXYZ);
1192                     profile.greenColorant = read_tag_XYZType(src, &index, TAG_gXYZ);
1193                     profile.blueColorant = read_tag_XYZType(src, &index, TAG_bXYZ)
1194                 }
1195                 if !src.valid {
1196                     return None;
1197                 }
1198 
1199                 if find_tag(&index, TAG_rTRC).is_some() || !SUPPORTS_ICCV4.load(Ordering::Relaxed) {
1200                     profile.redTRC = read_tag_curveType(src, &index, TAG_rTRC);
1201                     profile.greenTRC = read_tag_curveType(src, &index, TAG_gTRC);
1202                     profile.blueTRC = read_tag_curveType(src, &index, TAG_bTRC);
1203                     if profile.redTRC.is_none()
1204                         || profile.blueTRC.is_none()
1205                         || profile.greenTRC.is_none()
1206                     {
1207                         return None;
1208                     }
1209                 }
1210             } else if profile.color_space == GRAY_SIGNATURE {
1211                 profile.grayTRC = read_tag_curveType(src, &index, TAG_kTRC);
1212                 profile.grayTRC.as_ref()?;
1213             } else if profile.color_space == CMYK_SIGNATURE {
1214                 if let Some(A2B0) = find_tag(&index, TAG_A2B0) {
1215                     let lut_type = read_u32(src, A2B0.offset as usize);
1216                     if lut_type == LUT8_TYPE || lut_type == LUT16_TYPE {
1217                         profile.A2B0 = read_tag_lutType(src, A2B0)
1218                     } else if lut_type == LUT_MBA_TYPE {
1219                         profile.mAB = read_tag_lutmABType(src, A2B0)
1220                     }
1221                 }
1222             } else {
1223                 debug_assert!(false, "read_color_space protects against entering here");
1224                 return None;
1225             }
1226         } else {
1227             return None;
1228         }
1229 
1230         if !src.valid {
1231             return None;
1232         }
1233         Some(profile)
1234     }
1235     /// Precomputes the information needed for this profile to be
1236     /// used as the output profile when constructing a `Transform`.
precache_output_transform(&mut self)1237     pub fn precache_output_transform(&mut self) {
1238         crate::transform::qcms_profile_precache_output_transform(self);
1239     }
1240 }
1241