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, 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 vec = vec![LocalizableString {
176 language: "en".to_string(),
177 country: "US".to_string(),
178 value: description.unicode_string.clone(),
179 }];
180 write_mluc(into, &vec)
181 }
182 }
183}
184
185#[inline]
186fn write_xyz_tag_value(into: &mut Vec<u8>, xyz: Xyzd) {
187 let tag_definition: u32 = TagTypeDefinition::Xyz.into();
188 write_u32_be(into, tag_definition);
189 write_u32_be(into, 0);
190 let x_fixed = xyz.x.to_s15_fixed16();
191 write_i32_be(into, x_fixed);
192 let y_fixed = xyz.y.to_s15_fixed16();
193 write_i32_be(into, y_fixed);
194 let z_fixed = xyz.z.to_s15_fixed16();
195 write_i32_be(into, z_fixed);
196}
197
198#[inline]
199fn write_tag_entry(into: &mut Vec<u8>, tag: Tag, tag_entry: usize, tag_size: usize) {
200 let tag_value: u32 = tag.into();
201 write_u32_be(into, tag_value);
202 write_u32_be(into, tag_entry as u32);
203 write_u32_be(into, tag_size as u32);
204}
205
206fn write_trc_entry(into: &mut Vec<u8>, trc: &ToneReprCurve) -> Result<usize, CmsError> {
207 match trc {
208 ToneReprCurve::Lut(lut) => {
209 let curv: u32 = TagTypeDefinition::LutToneCurve.into();
210 write_u32_be(into, curv);
211 write_u32_be(into, 0);
212 write_u32_be(into, lut.len() as u32);
213 for item in lut.iter() {
214 write_u16_be(into, *item);
215 }
216 Ok(12 + lut.len() * 2)
217 }
218 ToneReprCurve::Parametric(parametric_curve) => {
219 if parametric_curve.len() > 7
220 || parametric_curve.len() == 6
221 || parametric_curve.len() == 2
222 {
223 return Err(CmsError::InvalidProfile);
224 }
225 let para: u32 = TagTypeDefinition::ParametricToneCurve.into();
226 write_u32_be(into, para);
227 write_u32_be(into, 0);
228 if parametric_curve.len() == 1 {
229 write_u16_be(into, 0);
230 } else if parametric_curve.len() == 3 {
231 write_u16_be(into, 1);
232 } else if parametric_curve.len() == 4 {
233 write_u16_be(into, 2);
234 } else if parametric_curve.len() == 5 {
235 write_u16_be(into, 3);
236 } else if parametric_curve.len() == 7 {
237 write_u16_be(into, 4);
238 }
239 write_u16_be(into, 0);
240 for item in parametric_curve.iter() {
241 write_i32_be(into, item.to_s15_fixed16());
242 }
243 Ok(12 + 4 * parametric_curve.len())
244 }
245 }
246}
247
248#[inline]
249fn write_cicp_entry(into: &mut Vec<u8>, cicp: &CicpProfile) {
250 let cicp_tag: u32 = TagTypeDefinition::Cicp.into();
251 write_u32_be(into, cicp_tag);
252 write_u32_be(into, 0);
253 into.push(cicp.color_primaries as u8);
254 into.push(cicp.transfer_characteristics as u8);
255 into.push(cicp.matrix_coefficients as u8);
256 into.push(if cicp.full_range { 1 } else { 0 });
257}
258
259fn write_chad(into: &mut Vec<u8>, matrix: Matrix3d) {
260 let arr_type: u32 = TagTypeDefinition::S15Fixed16Array.into();
261 write_u32_be(into, arr_type);
262 write_u32_be(into, 0);
263 write_matrix3d(into, matrix);
264}
265
266#[inline]
267fn write_matrix3d(into: &mut Vec<u8>, v: Matrix3d) {
268 write_i32_be(into, v.v[0][0].to_s15_fixed16());
269 write_i32_be(into, v.v[0][1].to_s15_fixed16());
270 write_i32_be(into, v.v[0][2].to_s15_fixed16());
271
272 write_i32_be(into, v.v[1][0].to_s15_fixed16());
273 write_i32_be(into, v.v[1][1].to_s15_fixed16());
274 write_i32_be(into, v.v[1][2].to_s15_fixed16());
275
276 write_i32_be(into, v.v[2][0].to_s15_fixed16());
277 write_i32_be(into, v.v[2][1].to_s15_fixed16());
278 write_i32_be(into, v.v[2][2].to_s15_fixed16());
279}
280
281#[inline]
282fn write_vector3d(into: &mut Vec<u8>, v: Vector3d) {
283 write_i32_be(into, v.v[0].to_s15_fixed16());
284 write_i32_be(into, v.v[1].to_s15_fixed16());
285 write_i32_be(into, v.v[2].to_s15_fixed16());
286}
287
288#[inline]
289fn write_lut_entry(into: &mut Vec<u8>, lut: &LutDataType) -> Result<usize, CmsError> {
290 if !lut.has_same_kind() {
291 return Err(CmsError::InvalidProfile);
292 }
293 let start = into.len();
294 let lut16_tag: u32 = match &lut.input_table {
295 LutStore::Store8(_) => LutType::Lut8.into(),
296 LutStore::Store16(_) => LutType::Lut16.into(),
297 };
298 write_u32_be(into, lut16_tag);
299 write_u32_be(into, 0);
300 into.push(lut.num_input_channels);
301 into.push(lut.num_output_channels);
302 into.push(lut.num_clut_grid_points);
303 into.push(0);
304 write_matrix3d(into, lut.matrix);
305 write_u16_be(into, lut.num_input_table_entries);
306 write_u16_be(into, lut.num_output_table_entries);
307 match &lut.input_table {
308 LutStore::Store8(input_table) => {
309 for &item in input_table.iter() {
310 into.push(item);
311 }
312 }
313 LutStore::Store16(input_table) => {
314 for &item in input_table.iter() {
315 write_u16_be(into, item);
316 }
317 }
318 }
319 match &lut.clut_table {
320 LutStore::Store8(input_table) => {
321 for &item in input_table.iter() {
322 into.push(item);
323 }
324 }
325 LutStore::Store16(input_table) => {
326 for &item in input_table.iter() {
327 write_u16_be(into, item);
328 }
329 }
330 }
331 match &lut.output_table {
332 LutStore::Store8(input_table) => {
333 for &item in input_table.iter() {
334 into.push(item);
335 }
336 }
337 LutStore::Store16(input_table) => {
338 for &item in input_table.iter() {
339 write_u16_be(into, item);
340 }
341 }
342 }
343 let end = into.len();
344 Ok(end - start)
345}
346
347#[inline]
348fn write_mab_entry(
349 into: &mut Vec<u8>,
350 lut: &LutMultidimensionalType,
351 is_a_to_b: bool,
352) -> Result<usize, CmsError> {
353 let start = into.len();
354 let lut16_tag: u32 = if is_a_to_b {
355 LutType::LutMab.into()
356 } else {
357 LutType::LutMba.into()
358 };
359 write_u32_be(into, lut16_tag);
360 write_u32_be(into, 0);
361 into.push(lut.num_input_channels);
362 into.push(lut.num_output_channels);
363 write_u16_be(into, 0);
364 let mut working_offset = 32usize;
365
366 let mut data = Vec::new();
367
368 if !lut.b_curves.is_empty() {
370 while working_offset % 4 != 0 {
371 data.push(0);
372 working_offset += 1;
373 }
374
375 write_u32_be(into, working_offset as u32);
376
377 for trc in lut.b_curves.iter() {
378 let curve_size = write_trc_entry(&mut data, trc)?;
379 working_offset += curve_size;
380 while working_offset % 4 != 0 {
381 data.push(0);
382 working_offset += 1;
383 }
384 }
385 } else {
386 write_u32_be(into, 0);
387 }
388
389 if !lut.m_curves.is_empty() {
391 while working_offset % 4 != 0 {
392 data.push(0);
393 working_offset += 1;
394 }
395
396 write_u32_be(into, working_offset as u32);
397 write_matrix3d(&mut data, lut.matrix);
398 write_vector3d(&mut data, lut.bias);
399 working_offset += 9 * 4 + 3 * 4;
400 write_u32_be(into, working_offset as u32);
402 for trc in lut.m_curves.iter() {
403 let curve_size = write_trc_entry(&mut data, trc)?;
404 working_offset += curve_size;
405 while working_offset % 4 != 0 {
406 data.push(0);
407 working_offset += 1;
408 }
409 }
410 } else {
411 write_u32_be(into, 0);
413 write_u32_be(into, 0);
415 }
416
417 let mut clut_start = data.len();
418
419 if let Some(clut) = &lut.clut {
421 while working_offset % 4 != 0 {
422 data.push(0);
423 working_offset += 1;
424 }
425
426 clut_start = data.len();
427
428 write_u32_be(into, working_offset as u32);
429
430 for &pt in lut.grid_points.iter() {
432 data.push(pt);
433 }
434 data.push(match clut {
435 LutStore::Store8(_) => 1,
436 LutStore::Store16(_) => 2,
437 }); data.push(0);
439 data.push(0);
440 data.push(0);
441 match clut {
442 LutStore::Store8(store) => {
443 for &element in store.iter() {
444 data.push(element)
445 }
446 }
447 LutStore::Store16(store) => {
448 for &element in store.iter() {
449 write_u16_be(&mut data, element);
450 }
451 }
452 }
453 } else {
454 write_u32_be(into, 0);
455 }
456
457 let clut_size = data.len() - clut_start;
458 working_offset += clut_size;
459
460 if !lut.a_curves.is_empty() {
462 while working_offset % 4 != 0 {
463 data.push(0);
464 working_offset += 1;
465 }
466
467 write_u32_be(into, working_offset as u32);
468
469 for trc in lut.a_curves.iter() {
470 let curve_size = write_trc_entry(&mut data, trc)?;
471 working_offset += curve_size;
472 while working_offset % 4 != 0 {
473 data.push(0);
474 working_offset += 1;
475 }
476 }
477 } else {
478 write_u32_be(into, 0);
479 }
480
481 into.extend(data);
482
483 let end = into.len();
484 Ok(end - start)
485}
486
487fn write_lut(into: &mut Vec<u8>, lut: &LutWarehouse, is_a_to_b: bool) -> Result<usize, CmsError> {
488 match lut {
489 LutWarehouse::Lut(lut) => Ok(write_lut_entry(into, lut)?),
490 LutWarehouse::Multidimensional(mab) => write_mab_entry(into, mab, is_a_to_b),
491 }
492}
493
494impl ProfileHeader {
495 fn encode(&self) -> Vec<u8> {
496 let mut encoder: Vec<u8> = Vec::with_capacity(size_of::<ProfileHeader>());
497 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);
506 write_u32_be(&mut encoder, self.flags);
507 write_u32_be(&mut encoder, self.device_manufacturer);
508 write_u32_be(&mut encoder, self.device_model);
509 for &i in self.device_attributes.iter() {
510 encoder.push(i);
511 }
512 write_u32_be(&mut encoder, self.rendering_intent.into());
513 write_i32_be(&mut encoder, self.illuminant.x.to_s15_fixed16());
514 write_i32_be(&mut encoder, self.illuminant.y.to_s15_fixed16());
515 write_i32_be(&mut encoder, self.illuminant.z.to_s15_fixed16());
516 write_u32_be(&mut encoder, self.creator);
517 for &i in self.profile_id.iter() {
518 encoder.push(i);
519 }
520 for &i in self.reserved.iter() {
521 encoder.push(i);
522 }
523 write_u32_be(&mut encoder, self.tag_count);
524 encoder
525 }
526}
527
528impl ColorProfile {
529 fn writable_tags_count(&self) -> usize {
530 let mut tags_count = 0usize;
531 if self.red_colorant != Xyzd::default() {
532 tags_count += 1;
533 }
534 if self.green_colorant != Xyzd::default() {
535 tags_count += 1;
536 }
537 if self.blue_colorant != Xyzd::default() {
538 tags_count += 1;
539 }
540 if self.red_trc.is_some() {
541 tags_count += 1;
542 }
543 if self.green_trc.is_some() {
544 tags_count += 1;
545 }
546 if self.blue_trc.is_some() {
547 tags_count += 1;
548 }
549 if self.gray_trc.is_some() {
550 tags_count += 1;
551 }
552 if self.cicp.is_some() {
553 tags_count += 1;
554 }
555 if self.media_white_point.is_some() {
556 tags_count += 1;
557 }
558 if self.gamut.is_some() {
559 tags_count += 1;
560 }
561 if self.chromatic_adaptation.is_some() {
562 tags_count += 1;
563 }
564 if self.lut_a_to_b_perceptual.is_some() {
565 tags_count += 1;
566 }
567 if self.lut_a_to_b_colorimetric.is_some() {
568 tags_count += 1;
569 }
570 if self.lut_a_to_b_saturation.is_some() {
571 tags_count += 1;
572 }
573 if self.lut_b_to_a_perceptual.is_some() {
574 tags_count += 1;
575 }
576 if self.lut_b_to_a_colorimetric.is_some() {
577 tags_count += 1;
578 }
579 if self.lut_b_to_a_saturation.is_some() {
580 tags_count += 1;
581 }
582 if self.luminance.is_some() {
583 tags_count += 1;
584 }
585 if let Some(description) = &self.description {
586 if description.has_values() {
587 tags_count += 1;
588 }
589 }
590 if let Some(copyright) = &self.copyright {
591 if copyright.has_values() {
592 tags_count += 1;
593 }
594 }
595 if let Some(vd) = &self.viewing_conditions_description {
596 if vd.has_values() {
597 tags_count += 1;
598 }
599 }
600 if let Some(vd) = &self.device_model {
601 if vd.has_values() {
602 tags_count += 1;
603 }
604 }
605 if let Some(vd) = &self.device_manufacturer {
606 if vd.has_values() {
607 tags_count += 1;
608 }
609 }
610 tags_count
611 }
612
613 pub fn encode(&self) -> Result<Vec<u8>, CmsError> {
615 let mut entries = Vec::new();
616 let tags_count = self.writable_tags_count();
617 let mut tags = Vec::with_capacity(TAG_SIZE * tags_count);
618 let mut base_offset = size_of::<ProfileHeader>() + TAG_SIZE * tags_count;
619 if self.red_colorant != Xyzd::default() {
620 write_tag_entry(&mut tags, Tag::RedXyz, base_offset, 20);
621 write_xyz_tag_value(&mut entries, self.red_colorant);
622 base_offset += 20;
623 }
624 if self.green_colorant != Xyzd::default() {
625 write_tag_entry(&mut tags, Tag::GreenXyz, base_offset, 20);
626 write_xyz_tag_value(&mut entries, self.green_colorant);
627 base_offset += 20;
628 }
629 if self.blue_colorant != Xyzd::default() {
630 write_tag_entry(&mut tags, Tag::BlueXyz, base_offset, 20);
631 write_xyz_tag_value(&mut entries, self.blue_colorant);
632 base_offset += 20;
633 }
634 if let Some(chad) = self.chromatic_adaptation {
635 write_tag_entry(&mut tags, Tag::ChromaticAdaptation, base_offset, 8 + 9 * 4);
636 write_chad(&mut entries, chad);
637 base_offset += 8 + 9 * 4;
638 }
639 if let Some(trc) = &self.red_trc {
640 let entry_size = write_trc_entry(&mut entries, trc)?;
641 write_tag_entry(&mut tags, Tag::RedToneReproduction, base_offset, entry_size);
642 base_offset += entry_size;
643 }
644 if let Some(trc) = &self.green_trc {
645 let entry_size = write_trc_entry(&mut entries, trc)?;
646 write_tag_entry(
647 &mut tags,
648 Tag::GreenToneReproduction,
649 base_offset,
650 entry_size,
651 );
652 base_offset += entry_size;
653 }
654 if let Some(trc) = &self.blue_trc {
655 let entry_size = write_trc_entry(&mut entries, trc)?;
656 write_tag_entry(
657 &mut tags,
658 Tag::BlueToneReproduction,
659 base_offset,
660 entry_size,
661 );
662 base_offset += entry_size;
663 }
664 if let Some(trc) = &self.gray_trc {
665 let entry_size = write_trc_entry(&mut entries, trc)?;
666 write_tag_entry(
667 &mut tags,
668 Tag::GreyToneReproduction,
669 base_offset,
670 entry_size,
671 );
672 base_offset += entry_size;
673 }
674 if self.white_point != Xyzd::default() {
675 write_tag_entry(&mut tags, Tag::MediaWhitePoint, base_offset, 20);
676 write_xyz_tag_value(&mut entries, self.white_point);
677 base_offset += 20;
678 }
679
680 let has_cicp = self.cicp.is_some();
681
682 if let Some(cicp) = &self.cicp {
687 if (self.profile_class == ProfileClass::InputDevice
688 || self.profile_class == ProfileClass::DisplayDevice)
689 && (self.color_space == DataColorSpace::Rgb
690 || self.color_space == DataColorSpace::YCbr
691 || self.color_space == DataColorSpace::Xyz)
692 {
693 write_tag_entry(&mut tags, Tag::CodeIndependentPoints, base_offset, 12);
694 write_cicp_entry(&mut entries, cicp);
695 base_offset += 12;
696 }
697 }
698
699 if let Some(lut) = &self.lut_a_to_b_perceptual {
700 let entry_size = write_lut(&mut entries, lut, true)?;
701 write_tag_entry(
702 &mut tags,
703 Tag::DeviceToPcsLutPerceptual,
704 base_offset,
705 entry_size,
706 );
707 base_offset += entry_size;
708 }
709
710 if let Some(lut) = &self.lut_a_to_b_colorimetric {
711 let entry_size = write_lut(&mut entries, lut, true)?;
712 write_tag_entry(
713 &mut tags,
714 Tag::DeviceToPcsLutColorimetric,
715 base_offset,
716 entry_size,
717 );
718 base_offset += entry_size;
719 }
720
721 if let Some(lut) = &self.lut_a_to_b_saturation {
722 let entry_size = write_lut(&mut entries, lut, true)?;
723 write_tag_entry(
724 &mut tags,
725 Tag::DeviceToPcsLutSaturation,
726 base_offset,
727 entry_size,
728 );
729 base_offset += entry_size;
730 }
731
732 if let Some(lut) = &self.lut_b_to_a_perceptual {
733 let entry_size = write_lut(&mut entries, lut, false)?;
734 write_tag_entry(
735 &mut tags,
736 Tag::PcsToDeviceLutPerceptual,
737 base_offset,
738 entry_size,
739 );
740 base_offset += entry_size;
741 }
742
743 if let Some(lut) = &self.lut_b_to_a_colorimetric {
744 let entry_size = write_lut(&mut entries, lut, false)?;
745 write_tag_entry(
746 &mut tags,
747 Tag::PcsToDeviceLutColorimetric,
748 base_offset,
749 entry_size,
750 );
751 base_offset += entry_size;
752 }
753
754 if let Some(lut) = &self.lut_b_to_a_saturation {
755 let entry_size = write_lut(&mut entries, lut, false)?;
756 write_tag_entry(
757 &mut tags,
758 Tag::PcsToDeviceLutSaturation,
759 base_offset,
760 entry_size,
761 );
762 base_offset += entry_size;
763 }
764
765 if let Some(lut) = &self.gamut {
766 let entry_size = write_lut(&mut entries, lut, false)?;
767 write_tag_entry(&mut tags, Tag::Gamut, base_offset, entry_size);
768 base_offset += entry_size;
769 }
770
771 if let Some(luminance) = self.luminance {
772 write_tag_entry(&mut tags, Tag::Luminance, base_offset, 20);
773 write_xyz_tag_value(&mut entries, luminance);
774 base_offset += 20;
775 }
776
777 if let Some(description) = &self.description {
778 if description.has_values() {
779 let entry_size = write_string_value(&mut entries, description);
780 write_tag_entry(&mut tags, Tag::ProfileDescription, base_offset, entry_size);
781 base_offset += entry_size;
782 }
783 }
784
785 if let Some(copyright) = &self.copyright {
786 if copyright.has_values() {
787 let entry_size = write_string_value(&mut entries, copyright);
788 write_tag_entry(&mut tags, Tag::Copyright, base_offset, entry_size);
789 base_offset += entry_size;
790 }
791 }
792
793 if let Some(vd) = &self.viewing_conditions_description {
794 if vd.has_values() {
795 let entry_size = write_string_value(&mut entries, vd);
796 write_tag_entry(
797 &mut tags,
798 Tag::ViewingConditionsDescription,
799 base_offset,
800 entry_size,
801 );
802 base_offset += entry_size;
803 }
804 }
805
806 if let Some(vd) = &self.device_model {
807 if vd.has_values() {
808 let entry_size = write_string_value(&mut entries, vd);
809 write_tag_entry(&mut tags, Tag::DeviceModel, base_offset, entry_size);
810 base_offset += entry_size;
811 }
812 }
813
814 if let Some(vd) = &self.device_manufacturer {
815 if vd.has_values() {
816 let entry_size = write_string_value(&mut entries, vd);
817 write_tag_entry(&mut tags, Tag::DeviceManufacturer, base_offset, entry_size);
818 }
820 }
821
822 tags.extend(entries);
823
824 let profile_header = ProfileHeader {
825 size: size_of::<ProfileHeader>() as u32 + tags.len() as u32,
826 pcs: self.pcs,
827 profile_class: self.profile_class,
828 rendering_intent: self.rendering_intent,
829 cmm_type: 0,
830 version: if has_cicp {
831 ProfileVersion::V4_3
832 } else {
833 ProfileVersion::V4_0
834 },
835 data_color_space: self.color_space,
836 creation_date_time: ColorDateTime::now(),
837 signature: ProfileSignature::Acsp,
838 platform: 0u32,
839 flags: 0u32,
840 device_manufacturer: 0u32,
841 device_model: 0u32,
842 device_attributes: [0u8; 8],
843 illuminant: self.white_point.to_xyz(),
844 creator: 0u32,
845 profile_id: [0u8; 16],
846 reserved: [0u8; 28],
847 tag_count: tags_count as u32,
848 };
849 let mut header = profile_header.encode();
850 header.extend(tags);
851 Ok(header)
852 }
853}
854
855impl FloatToFixedU8Fixed8 for f32 {
856 #[inline]
857 fn to_u8_fixed8(self) -> u16 {
858 if self > 255.0 + 255.0 / 256f32 {
859 0xffffu16
860 } else if self < 0.0 {
861 0u16
862 } else {
863 (self * 256.0 + 0.5).floor() as u16
864 }
865 }
866}
867
868#[cfg(test)]
869mod tests {
870 use super::*;
871
872 #[test]
873 fn to_u8_fixed8() {
874 assert_eq!(0, 0f32.to_u8_fixed8());
875 assert_eq!(0x0100, 1f32.to_u8_fixed8());
876 assert_eq!(u16::MAX, (255f32 + (255f32 / 256f32)).to_u8_fixed8());
877 }
878
879 #[test]
880 fn to_s15_fixed16() {
881 assert_eq!(0x80000000u32 as i32, (-32768f32).to_s15_fixed16());
882 assert_eq!(0, 0f32.to_s15_fixed16());
883 assert_eq!(0x10000, 1.0f32.to_s15_fixed16());
884 assert_eq!(
885 i32::MAX,
886 (32767f32 + (65535f32 / 65536f32)).to_s15_fixed16()
887 );
888 }
889}