1use crate::profile::{LutDataType, ProfileHeader};
30use crate::tag::{TAG_SIZE, Tag, TagTypeDefinition};
31use crate::trc::ToneReprCurve;
32use crate::{
33 CicpProfile, CmsError, ColorDateTime, ColorProfile, DataColorSpace, LocalizableString,
34 LutMultidimensionalType, LutStore, LutType, LutWarehouse, Matrix3d, ProfileClass,
35 ProfileSignature, ProfileText, ProfileVersion, Vector3d, ViewingConditions, Xyz, Xyzd,
36};
37
38pub(crate) trait FloatToFixedS15Fixed16 {
39 fn to_s15_fixed16(self) -> i32;
40}
41
42pub(crate) trait FloatToFixedU8Fixed8 {
43 fn to_u8_fixed8(self) -> u16;
44}
45
46impl FloatToFixedS15Fixed16 for f32 {
61 #[inline]
62 fn to_s15_fixed16(self) -> i32 {
63 const SCALE: f64 = (1 << 16) as f64;
64 (self as f64 * SCALE + 0.5)
65 .floor()
66 .clamp(i32::MIN as f64, i32::MAX as f64) as i32
67 }
68}
69
70impl FloatToFixedS15Fixed16 for f64 {
71 #[inline]
72 fn to_s15_fixed16(self) -> i32 {
73 const SCALE: f64 = (1 << 16) as f64;
74 (self * SCALE + 0.5)
75 .floor()
76 .clamp(i32::MIN as f64, i32::MAX as f64) as i32
77 }
78}
79
80#[inline]
81fn write_u32_be(into: &mut Vec<u8>, value: u32) {
82 let bytes = value.to_be_bytes();
83 into.push(bytes[0]);
84 into.push(bytes[1]);
85 into.push(bytes[2]);
86 into.push(bytes[3]);
87}
88
89#[inline]
90pub(crate) fn write_u16_be(into: &mut Vec<u8>, value: u16) {
91 let bytes = value.to_be_bytes();
92 into.push(bytes[0]);
93 into.push(bytes[1]);
94}
95
96#[inline]
97fn write_i32_be(into: &mut Vec<u8>, value: i32) {
98 let bytes = value.to_be_bytes();
99 into.push(bytes[0]);
100 into.push(bytes[1]);
101 into.push(bytes[2]);
102 into.push(bytes[3]);
103}
104
105fn first_two_ascii_bytes(s: &String) -> [u8; 2] {
106 let bytes = s.as_bytes();
107 if bytes.len() >= 2 {
108 bytes[0..2].try_into().unwrap()
109 } else if bytes.len() == 1 {
110 let vec = vec![bytes[0], 0u8];
111 vec.try_into().unwrap()
112 } else {
113 let vec = vec![0u8, 0u8];
114 vec.try_into().unwrap()
115 }
116}
117
118#[inline]
120fn write_mluc(into: &mut Vec<u8>, strings: &[LocalizableString]) -> usize {
121 assert!(!strings.is_empty());
122 let start = into.len();
123 let tag_def: u32 = TagTypeDefinition::MultiLocalizedUnicode.into();
124 write_u32_be(into, tag_def);
125 write_u32_be(into, 0);
126 let number_of_records = strings.len();
127 write_u32_be(into, number_of_records as u32);
128 write_u32_be(into, 12); let lang = first_two_ascii_bytes(&strings[0].language);
130 into.extend_from_slice(&lang);
131 let country = first_two_ascii_bytes(&strings[0].country);
132 into.extend_from_slice(&country);
133 let first_string_len = strings[0].value.len() * 2;
134 write_u32_be(into, first_string_len as u32);
135 let mut first_string_offset = 16 + 12 * strings.len();
136 write_u32_be(into, first_string_offset as u32);
137 first_string_offset += first_string_len;
138 for record in strings.iter().skip(1) {
139 let lang = first_two_ascii_bytes(&record.language);
140 into.extend_from_slice(&lang);
141 let country = first_two_ascii_bytes(&record.country);
142 into.extend_from_slice(&country);
143 let first_string_len = record.value.len() * 2;
144 write_u32_be(into, first_string_len as u32);
145 write_u32_be(into, first_string_offset as u32);
146 first_string_offset += first_string_len;
147 }
148 for record in strings.iter() {
149 for chunk in record.value.encode_utf16() {
150 write_u16_be(into, chunk);
151 }
152 }
153 let end = into.len();
154 end - start
155}
156
157#[inline]
158fn write_string_value(into: &mut Vec<u8>, text: &ProfileText) -> usize {
159 match text {
160 ProfileText::PlainString(text) => {
161 let vec = vec![LocalizableString {
162 language: "en".to_string(),
163 country: "US".to_string(),
164 value: text.clone(),
165 }];
166 write_mluc(into, &vec)
167 }
168 ProfileText::Localizable(localizable) => {
169 if localizable.is_empty() {
170 return 0;
171 }
172 write_mluc(into, localizable)
173 }
174 ProfileText::Description(description) => {
175 let value = if description.unicode_string.is_empty() {
176 description.ascii_string.clone()
177 } else {
178 description.unicode_string.clone()
179 };
180 let vec = vec![LocalizableString {
181 language: "en".to_string(),
182 country: "US".to_string(),
183 value,
184 }];
185 write_mluc(into, &vec)
186 }
187 }
188}
189
190#[inline]
191fn write_xyz_tag_value(into: &mut Vec<u8>, xyz: Xyzd) {
192 let tag_definition: u32 = TagTypeDefinition::Xyz.into();
193 write_u32_be(into, tag_definition);
194 write_u32_be(into, 0);
195 let x_fixed = xyz.x.to_s15_fixed16();
196 write_i32_be(into, x_fixed);
197 let y_fixed = xyz.y.to_s15_fixed16();
198 write_i32_be(into, y_fixed);
199 let z_fixed = xyz.z.to_s15_fixed16();
200 write_i32_be(into, z_fixed);
201}
202
203#[inline]
204fn write_tag_entry(into: &mut Vec<u8>, tag: Tag, tag_entry: usize, tag_size: usize) {
205 let tag_value: u32 = tag.into();
206 write_u32_be(into, tag_value);
207 write_u32_be(into, tag_entry as u32);
208 write_u32_be(into, tag_size as u32);
209}
210
211#[inline]
212fn write_xyz(into: &mut Vec<u8>, xyz: Xyz) {
213 let x_fixed = xyz.x.to_s15_fixed16();
214 write_i32_be(into, x_fixed);
215 let y_fixed = xyz.y.to_s15_fixed16();
216 write_i32_be(into, y_fixed);
217 let z_fixed = xyz.z.to_s15_fixed16();
218 write_i32_be(into, z_fixed);
219}
220
221#[inline]
222fn write_viewing_conditions_value(
223 into: &mut Vec<u8>,
224 viewing_conditions: &ViewingConditions,
225) -> usize {
226 let tag_definition: u32 = TagTypeDefinition::DefViewingConditions.into();
227 write_u32_be(into, tag_definition);
228 write_u32_be(into, 0);
229 write_xyz(into, viewing_conditions.illuminant);
230 write_xyz(into, viewing_conditions.surround);
231 write_u32_be(into, viewing_conditions.observer.into());
232 36
233}
234
235fn write_trc_entry(into: &mut Vec<u8>, trc: &ToneReprCurve) -> Result<usize, CmsError> {
236 match trc {
237 ToneReprCurve::Lut(lut) => {
238 let curv: u32 = TagTypeDefinition::LutToneCurve.into();
239 write_u32_be(into, curv);
240 write_u32_be(into, 0);
241 write_u32_be(into, lut.len() as u32);
242 for item in lut.iter() {
243 write_u16_be(into, *item);
244 }
245 Ok(12 + lut.len() * 2)
246 }
247 ToneReprCurve::Parametric(parametric_curve) => {
248 if parametric_curve.len() > 7
249 || parametric_curve.len() == 6
250 || parametric_curve.len() == 2
251 {
252 return Err(CmsError::InvalidProfile);
253 }
254 let para: u32 = TagTypeDefinition::ParametricToneCurve.into();
255 write_u32_be(into, para);
256 write_u32_be(into, 0);
257 if parametric_curve.len() == 1 {
258 write_u16_be(into, 0);
259 } else if parametric_curve.len() == 3 {
260 write_u16_be(into, 1);
261 } else if parametric_curve.len() == 4 {
262 write_u16_be(into, 2);
263 } else if parametric_curve.len() == 5 {
264 write_u16_be(into, 3);
265 } else if parametric_curve.len() == 7 {
266 write_u16_be(into, 4);
267 }
268 write_u16_be(into, 0);
269 for item in parametric_curve.iter() {
270 write_i32_be(into, item.to_s15_fixed16());
271 }
272 Ok(12 + 4 * parametric_curve.len())
273 }
274 }
275}
276
277#[inline]
278fn write_cicp_entry(into: &mut Vec<u8>, cicp: &CicpProfile) {
279 let cicp_tag: u32 = TagTypeDefinition::Cicp.into();
280 write_u32_be(into, cicp_tag);
281 write_u32_be(into, 0);
282 into.push(cicp.color_primaries as u8);
283 into.push(cicp.transfer_characteristics as u8);
284 into.push(cicp.matrix_coefficients as u8);
285 into.push(if cicp.full_range { 1 } else { 0 });
286}
287
288fn write_chad(into: &mut Vec<u8>, matrix: Matrix3d) {
289 let arr_type: u32 = TagTypeDefinition::S15Fixed16Array.into();
290 write_u32_be(into, arr_type);
291 write_u32_be(into, 0);
292 write_matrix3d(into, matrix);
293}
294
295#[inline]
296fn write_matrix3d(into: &mut Vec<u8>, v: Matrix3d) {
297 write_i32_be(into, v.v[0][0].to_s15_fixed16());
298 write_i32_be(into, v.v[0][1].to_s15_fixed16());
299 write_i32_be(into, v.v[0][2].to_s15_fixed16());
300
301 write_i32_be(into, v.v[1][0].to_s15_fixed16());
302 write_i32_be(into, v.v[1][1].to_s15_fixed16());
303 write_i32_be(into, v.v[1][2].to_s15_fixed16());
304
305 write_i32_be(into, v.v[2][0].to_s15_fixed16());
306 write_i32_be(into, v.v[2][1].to_s15_fixed16());
307 write_i32_be(into, v.v[2][2].to_s15_fixed16());
308}
309
310#[inline]
311fn write_vector3d(into: &mut Vec<u8>, v: Vector3d) {
312 write_i32_be(into, v.v[0].to_s15_fixed16());
313 write_i32_be(into, v.v[1].to_s15_fixed16());
314 write_i32_be(into, v.v[2].to_s15_fixed16());
315}
316
317#[inline]
318fn write_lut_entry(into: &mut Vec<u8>, lut: &LutDataType) -> Result<usize, CmsError> {
319 if !lut.has_same_kind() {
320 return Err(CmsError::InvalidProfile);
321 }
322 let start = into.len();
323 let lut16_tag: u32 = match &lut.input_table {
324 LutStore::Store8(_) => LutType::Lut8.into(),
325 LutStore::Store16(_) => LutType::Lut16.into(),
326 };
327 write_u32_be(into, lut16_tag);
328 write_u32_be(into, 0);
329 into.push(lut.num_input_channels);
330 into.push(lut.num_output_channels);
331 into.push(lut.num_clut_grid_points);
332 into.push(0);
333 write_matrix3d(into, lut.matrix);
334 write_u16_be(into, lut.num_input_table_entries);
335 write_u16_be(into, lut.num_output_table_entries);
336 match &lut.input_table {
337 LutStore::Store8(input_table) => {
338 for &item in input_table.iter() {
339 into.push(item);
340 }
341 }
342 LutStore::Store16(input_table) => {
343 for &item in input_table.iter() {
344 write_u16_be(into, item);
345 }
346 }
347 }
348 match &lut.clut_table {
349 LutStore::Store8(input_table) => {
350 for &item in input_table.iter() {
351 into.push(item);
352 }
353 }
354 LutStore::Store16(input_table) => {
355 for &item in input_table.iter() {
356 write_u16_be(into, item);
357 }
358 }
359 }
360 match &lut.output_table {
361 LutStore::Store8(input_table) => {
362 for &item in input_table.iter() {
363 into.push(item);
364 }
365 }
366 LutStore::Store16(input_table) => {
367 for &item in input_table.iter() {
368 write_u16_be(into, item);
369 }
370 }
371 }
372 let end = into.len();
373 Ok(end - start)
374}
375
376#[inline]
377fn write_mab_entry(
378 into: &mut Vec<u8>,
379 lut: &LutMultidimensionalType,
380 is_a_to_b: bool,
381) -> Result<usize, CmsError> {
382 let start = into.len();
383 let lut16_tag: u32 = if is_a_to_b {
384 LutType::LutMab.into()
385 } else {
386 LutType::LutMba.into()
387 };
388 write_u32_be(into, lut16_tag);
389 write_u32_be(into, 0);
390 into.push(lut.num_input_channels);
391 into.push(lut.num_output_channels);
392 write_u16_be(into, 0);
393 let mut working_offset = 32usize;
394
395 let mut data = Vec::new();
396
397 if !lut.b_curves.is_empty() {
399 while working_offset % 4 != 0 {
400 data.push(0);
401 working_offset += 1;
402 }
403
404 write_u32_be(into, working_offset as u32);
405
406 for trc in lut.b_curves.iter() {
407 let curve_size = write_trc_entry(&mut data, trc)?;
408 working_offset += curve_size;
409 while working_offset % 4 != 0 {
410 data.push(0);
411 working_offset += 1;
412 }
413 }
414 } else {
415 write_u32_be(into, 0);
416 }
417
418 if !lut.m_curves.is_empty() {
420 while working_offset % 4 != 0 {
421 data.push(0);
422 working_offset += 1;
423 }
424
425 write_u32_be(into, working_offset as u32);
426 write_matrix3d(&mut data, lut.matrix);
427 write_vector3d(&mut data, lut.bias);
428 working_offset += 9 * 4 + 3 * 4;
429 write_u32_be(into, working_offset as u32);
431 for trc in lut.m_curves.iter() {
432 let curve_size = write_trc_entry(&mut data, trc)?;
433 working_offset += curve_size;
434 while working_offset % 4 != 0 {
435 data.push(0);
436 working_offset += 1;
437 }
438 }
439 } else {
440 write_u32_be(into, 0);
442 write_u32_be(into, 0);
444 }
445
446 let mut clut_start = data.len();
447
448 if let Some(clut) = &lut.clut {
450 while working_offset % 4 != 0 {
451 data.push(0);
452 working_offset += 1;
453 }
454
455 clut_start = data.len();
456
457 write_u32_be(into, working_offset as u32);
458
459 for &pt in lut.grid_points.iter() {
461 data.push(pt);
462 }
463 data.push(match clut {
464 LutStore::Store8(_) => 1,
465 LutStore::Store16(_) => 2,
466 }); data.push(0);
468 data.push(0);
469 data.push(0);
470 match clut {
471 LutStore::Store8(store) => {
472 for &element in store.iter() {
473 data.push(element)
474 }
475 }
476 LutStore::Store16(store) => {
477 for &element in store.iter() {
478 write_u16_be(&mut data, element);
479 }
480 }
481 }
482 } else {
483 write_u32_be(into, 0);
484 }
485
486 let clut_size = data.len() - clut_start;
487 working_offset += clut_size;
488
489 if !lut.a_curves.is_empty() {
491 while working_offset % 4 != 0 {
492 data.push(0);
493 working_offset += 1;
494 }
495
496 write_u32_be(into, working_offset as u32);
497
498 for trc in lut.a_curves.iter() {
499 let curve_size = write_trc_entry(&mut data, trc)?;
500 working_offset += curve_size;
501 while working_offset % 4 != 0 {
502 data.push(0);
503 working_offset += 1;
504 }
505 }
506 } else {
507 write_u32_be(into, 0);
508 }
509
510 into.extend(data);
511
512 let end = into.len();
513 Ok(end - start)
514}
515
516fn write_lut(into: &mut Vec<u8>, lut: &LutWarehouse, is_a_to_b: bool) -> Result<usize, CmsError> {
517 match lut {
518 LutWarehouse::Lut(lut) => Ok(write_lut_entry(into, lut)?),
519 LutWarehouse::Multidimensional(mab) => write_mab_entry(into, mab, is_a_to_b),
520 }
521}
522
523impl ProfileHeader {
524 fn encode(&self) -> Vec<u8> {
525 let mut encoder: Vec<u8> = Vec::with_capacity(size_of::<ProfileHeader>());
526 write_u32_be(&mut encoder, self.size); write_u32_be(&mut encoder, 0); write_u32_be(&mut encoder, self.version.into()); write_u32_be(&mut encoder, self.profile_class.into()); write_u32_be(&mut encoder, self.data_color_space.into()); write_u32_be(&mut encoder, self.pcs.into()); self.creation_date_time.encode(&mut encoder); write_u32_be(&mut encoder, self.signature.into()); write_u32_be(&mut encoder, self.platform);
535 write_u32_be(&mut encoder, self.flags);
536 write_u32_be(&mut encoder, self.device_manufacturer);
537 write_u32_be(&mut encoder, self.device_model);
538 for &i in self.device_attributes.iter() {
539 encoder.push(i);
540 }
541 write_u32_be(&mut encoder, self.rendering_intent.into());
542 write_i32_be(&mut encoder, self.illuminant.x.to_s15_fixed16());
543 write_i32_be(&mut encoder, self.illuminant.y.to_s15_fixed16());
544 write_i32_be(&mut encoder, self.illuminant.z.to_s15_fixed16());
545 write_u32_be(&mut encoder, self.creator);
546 for &i in self.profile_id.iter() {
547 encoder.push(i);
548 }
549 for &i in self.reserved.iter() {
550 encoder.push(i);
551 }
552 write_u32_be(&mut encoder, self.tag_count);
553 encoder
554 }
555}
556
557impl ColorProfile {
558 fn writable_tags_count(&self) -> usize {
559 let mut tags_count = 0usize;
560 if self.red_colorant != Xyzd::default() {
561 tags_count += 1;
562 }
563 if self.green_colorant != Xyzd::default() {
564 tags_count += 1;
565 }
566 if self.blue_colorant != Xyzd::default() {
567 tags_count += 1;
568 }
569 if self.red_trc.is_some() {
570 tags_count += 1;
571 }
572 if self.green_trc.is_some() {
573 tags_count += 1;
574 }
575 if self.blue_trc.is_some() {
576 tags_count += 1;
577 }
578 if self.gray_trc.is_some() {
579 tags_count += 1;
580 }
581 if self.cicp.is_some() {
582 tags_count += 1;
583 }
584 if self.media_white_point.is_some() {
585 tags_count += 1;
586 }
587 if self.gamut.is_some() {
588 tags_count += 1;
589 }
590 if self.chromatic_adaptation.is_some() {
591 tags_count += 1;
592 }
593 if self.lut_a_to_b_perceptual.is_some() {
594 tags_count += 1;
595 }
596 if self.lut_a_to_b_colorimetric.is_some() {
597 tags_count += 1;
598 }
599 if self.lut_a_to_b_saturation.is_some() {
600 tags_count += 1;
601 }
602 if self.lut_b_to_a_perceptual.is_some() {
603 tags_count += 1;
604 }
605 if self.lut_b_to_a_colorimetric.is_some() {
606 tags_count += 1;
607 }
608 if self.lut_b_to_a_saturation.is_some() {
609 tags_count += 1;
610 }
611 if self.luminance.is_some() {
612 tags_count += 1;
613 }
614 if let Some(description) = &self.description {
615 if description.has_values() {
616 tags_count += 1;
617 }
618 }
619 if let Some(copyright) = &self.copyright {
620 if copyright.has_values() {
621 tags_count += 1;
622 }
623 }
624 if self.viewing_conditions.is_some() {
625 tags_count += 1;
626 }
627 if let Some(vd) = &self.viewing_conditions_description {
628 if vd.has_values() {
629 tags_count += 1;
630 }
631 }
632 if let Some(vd) = &self.device_model {
633 if vd.has_values() {
634 tags_count += 1;
635 }
636 }
637 if let Some(vd) = &self.device_manufacturer {
638 if vd.has_values() {
639 tags_count += 1;
640 }
641 }
642 tags_count
643 }
644
645 pub fn encode(&self) -> Result<Vec<u8>, CmsError> {
647 let mut entries = Vec::new();
648 let tags_count = self.writable_tags_count();
649 let mut tags = Vec::with_capacity(TAG_SIZE * tags_count);
650 let mut base_offset = size_of::<ProfileHeader>() + TAG_SIZE * tags_count;
651 if self.red_colorant != Xyzd::default() {
652 write_tag_entry(&mut tags, Tag::RedXyz, base_offset, 20);
653 write_xyz_tag_value(&mut entries, self.red_colorant);
654 base_offset += 20;
655 }
656 if self.green_colorant != Xyzd::default() {
657 write_tag_entry(&mut tags, Tag::GreenXyz, base_offset, 20);
658 write_xyz_tag_value(&mut entries, self.green_colorant);
659 base_offset += 20;
660 }
661 if self.blue_colorant != Xyzd::default() {
662 write_tag_entry(&mut tags, Tag::BlueXyz, base_offset, 20);
663 write_xyz_tag_value(&mut entries, self.blue_colorant);
664 base_offset += 20;
665 }
666 if let Some(chad) = self.chromatic_adaptation {
667 write_tag_entry(&mut tags, Tag::ChromaticAdaptation, base_offset, 8 + 9 * 4);
668 write_chad(&mut entries, chad);
669 base_offset += 8 + 9 * 4;
670 }
671 if let Some(trc) = &self.red_trc {
672 let entry_size = write_trc_entry(&mut entries, trc)?;
673 write_tag_entry(&mut tags, Tag::RedToneReproduction, base_offset, entry_size);
674 base_offset += entry_size;
675 }
676 if let Some(trc) = &self.green_trc {
677 let entry_size = write_trc_entry(&mut entries, trc)?;
678 write_tag_entry(
679 &mut tags,
680 Tag::GreenToneReproduction,
681 base_offset,
682 entry_size,
683 );
684 base_offset += entry_size;
685 }
686 if let Some(trc) = &self.blue_trc {
687 let entry_size = write_trc_entry(&mut entries, trc)?;
688 write_tag_entry(
689 &mut tags,
690 Tag::BlueToneReproduction,
691 base_offset,
692 entry_size,
693 );
694 base_offset += entry_size;
695 }
696 if let Some(trc) = &self.gray_trc {
697 let entry_size = write_trc_entry(&mut entries, trc)?;
698 write_tag_entry(
699 &mut tags,
700 Tag::GreyToneReproduction,
701 base_offset,
702 entry_size,
703 );
704 base_offset += entry_size;
705 }
706
707 if let Some(media_wp) = self.media_white_point {
708 write_tag_entry(&mut tags, Tag::MediaWhitePoint, base_offset, 20);
709 write_xyz_tag_value(&mut entries, media_wp);
710 base_offset += 20;
711 }
712
713 let has_cicp = self.cicp.is_some();
714
715 if let Some(cicp) = &self.cicp {
720 if (self.profile_class == ProfileClass::InputDevice
721 || self.profile_class == ProfileClass::DisplayDevice)
722 && (self.color_space == DataColorSpace::Rgb
723 || self.color_space == DataColorSpace::YCbr
724 || self.color_space == DataColorSpace::Xyz)
725 {
726 write_tag_entry(&mut tags, Tag::CodeIndependentPoints, base_offset, 12);
727 write_cicp_entry(&mut entries, cicp);
728 base_offset += 12;
729 }
730 }
731
732 if let Some(lut) = &self.lut_a_to_b_perceptual {
733 let entry_size = write_lut(&mut entries, lut, true)?;
734 write_tag_entry(
735 &mut tags,
736 Tag::DeviceToPcsLutPerceptual,
737 base_offset,
738 entry_size,
739 );
740 base_offset += entry_size;
741 }
742
743 if let Some(lut) = &self.lut_a_to_b_colorimetric {
744 let entry_size = write_lut(&mut entries, lut, true)?;
745 write_tag_entry(
746 &mut tags,
747 Tag::DeviceToPcsLutColorimetric,
748 base_offset,
749 entry_size,
750 );
751 base_offset += entry_size;
752 }
753
754 if let Some(lut) = &self.lut_a_to_b_saturation {
755 let entry_size = write_lut(&mut entries, lut, true)?;
756 write_tag_entry(
757 &mut tags,
758 Tag::DeviceToPcsLutSaturation,
759 base_offset,
760 entry_size,
761 );
762 base_offset += entry_size;
763 }
764
765 if let Some(lut) = &self.lut_b_to_a_perceptual {
766 let entry_size = write_lut(&mut entries, lut, false)?;
767 write_tag_entry(
768 &mut tags,
769 Tag::PcsToDeviceLutPerceptual,
770 base_offset,
771 entry_size,
772 );
773 base_offset += entry_size;
774 }
775
776 if let Some(lut) = &self.lut_b_to_a_colorimetric {
777 let entry_size = write_lut(&mut entries, lut, false)?;
778 write_tag_entry(
779 &mut tags,
780 Tag::PcsToDeviceLutColorimetric,
781 base_offset,
782 entry_size,
783 );
784 base_offset += entry_size;
785 }
786
787 if let Some(lut) = &self.lut_b_to_a_saturation {
788 let entry_size = write_lut(&mut entries, lut, false)?;
789 write_tag_entry(
790 &mut tags,
791 Tag::PcsToDeviceLutSaturation,
792 base_offset,
793 entry_size,
794 );
795 base_offset += entry_size;
796 }
797
798 if let Some(lut) = &self.gamut {
799 let entry_size = write_lut(&mut entries, lut, false)?;
800 write_tag_entry(&mut tags, Tag::Gamut, base_offset, entry_size);
801 base_offset += entry_size;
802 }
803
804 if let Some(luminance) = self.luminance {
805 write_tag_entry(&mut tags, Tag::Luminance, base_offset, 20);
806 write_xyz_tag_value(&mut entries, luminance);
807 base_offset += 20;
808 }
809
810 if let Some(description) = &self.description {
811 if description.has_values() {
812 let entry_size = write_string_value(&mut entries, description);
813 write_tag_entry(&mut tags, Tag::ProfileDescription, base_offset, entry_size);
814 base_offset += entry_size;
815 }
816 }
817
818 if let Some(copyright) = &self.copyright {
819 if copyright.has_values() {
820 let entry_size = write_string_value(&mut entries, copyright);
821 write_tag_entry(&mut tags, Tag::Copyright, base_offset, entry_size);
822 base_offset += entry_size;
823 }
824 }
825
826 if let Some(vc) = &self.viewing_conditions {
827 let entry_size = write_viewing_conditions_value(&mut entries, vc);
828 write_tag_entry(&mut tags, Tag::ObserverConditions, base_offset, entry_size);
829 base_offset += entry_size;
830 }
831
832 if let Some(vd) = &self.viewing_conditions_description {
833 if vd.has_values() {
834 let entry_size = write_string_value(&mut entries, vd);
835 write_tag_entry(
836 &mut tags,
837 Tag::ViewingConditionsDescription,
838 base_offset,
839 entry_size,
840 );
841 base_offset += entry_size;
842 }
843 }
844
845 if let Some(vd) = &self.device_model {
846 if vd.has_values() {
847 let entry_size = write_string_value(&mut entries, vd);
848 write_tag_entry(&mut tags, Tag::DeviceModel, base_offset, entry_size);
849 base_offset += entry_size;
850 }
851 }
852
853 if let Some(vd) = &self.device_manufacturer {
854 if vd.has_values() {
855 let entry_size = write_string_value(&mut entries, vd);
856 write_tag_entry(&mut tags, Tag::DeviceManufacturer, base_offset, entry_size);
857 }
859 }
860
861 tags.extend(entries);
862
863 let profile_header = ProfileHeader {
864 size: size_of::<ProfileHeader>() as u32 + tags.len() as u32,
865 pcs: self.pcs,
866 profile_class: self.profile_class,
867 rendering_intent: self.rendering_intent,
868 cmm_type: 0,
869 version: if has_cicp {
870 ProfileVersion::V4_3
871 } else if self.version_internal < ProfileVersion::V4_0 {
872 ProfileVersion::V4_0
873 } else {
874 self.version_internal
875 },
876 data_color_space: self.color_space,
877 creation_date_time: ColorDateTime::now(),
878 signature: ProfileSignature::Acsp,
879 platform: 0u32,
880 flags: 0u32,
881 device_manufacturer: 0u32,
882 device_model: 0u32,
883 device_attributes: [0u8; 8],
884 illuminant: self.white_point.to_xyz(),
885 creator: 0u32,
886 profile_id: [0u8; 16],
887 reserved: [0u8; 28],
888 tag_count: tags_count as u32,
889 };
890 let mut header = profile_header.encode();
891 header.extend(tags);
892 Ok(header)
893 }
894}
895
896impl FloatToFixedU8Fixed8 for f32 {
897 #[inline]
898 fn to_u8_fixed8(self) -> u16 {
899 if self > 255.0 + 255.0 / 256f32 {
900 0xffffu16
901 } else if self < 0.0 {
902 0u16
903 } else {
904 (self * 256.0 + 0.5).floor() as u16
905 }
906 }
907}
908
909#[cfg(test)]
910mod tests {
911 use super::*;
912
913 #[test]
914 fn to_u8_fixed8() {
915 assert_eq!(0, 0f32.to_u8_fixed8());
916 assert_eq!(0x0100, 1f32.to_u8_fixed8());
917 assert_eq!(u16::MAX, (255f32 + (255f32 / 256f32)).to_u8_fixed8());
918 }
919
920 #[test]
921 fn to_s15_fixed16() {
922 assert_eq!(0x80000000u32 as i32, (-32768f32).to_s15_fixed16());
923 assert_eq!(0, 0f32.to_s15_fixed16());
924 assert_eq!(0x10000, 1.0f32.to_s15_fixed16());
925 assert_eq!(
926 i32::MAX,
927 (32767f32 + (65535f32 / 65536f32)).to_s15_fixed16()
928 );
929 }
930}