Coverage Report

Created: 2025-10-28 08:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/moxcms-0.7.9/src/writer.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 3/2025. All rights reserved.
3
 * //
4
 * // Redistribution and use in source and binary forms, with or without modification,
5
 * // are permitted provided that the following conditions are met:
6
 * //
7
 * // 1.  Redistributions of source code must retain the above copyright notice, this
8
 * // list of conditions and the following disclaimer.
9
 * //
10
 * // 2.  Redistributions in binary form must reproduce the above copyright notice,
11
 * // this list of conditions and the following disclaimer in the documentation
12
 * // and/or other materials provided with the distribution.
13
 * //
14
 * // 3.  Neither the name of the copyright holder nor the names of its
15
 * // contributors may be used to endorse or promote products derived from
16
 * // this software without specific prior written permission.
17
 * //
18
 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29
use crate::profile::{LutDataType, ProfileHeader};
30
use crate::tag::{TAG_SIZE, Tag, TagTypeDefinition};
31
use crate::trc::ToneReprCurve;
32
use crate::{
33
    CicpProfile, CmsError, ColorDateTime, ColorProfile, DataColorSpace, LocalizableString,
34
    LutMultidimensionalType, LutStore, LutType, LutWarehouse, Matrix3d, ProfileClass,
35
    ProfileSignature, ProfileText, ProfileVersion, Vector3d, Xyzd,
36
};
37
38
pub(crate) trait FloatToFixedS15Fixed16 {
39
    fn to_s15_fixed16(self) -> i32;
40
}
41
42
pub(crate) trait FloatToFixedU8Fixed8 {
43
    fn to_u8_fixed8(self) -> u16;
44
}
45
46
// pub(crate) trait FloatToFixedU16 {
47
//     fn to_fixed_u16(self) -> u16;
48
// }
49
50
// impl FloatToFixedU16 for f32 {
51
//     #[inline]
52
//     fn to_fixed_u16(self) -> u16 {
53
//         const SCALE: f64 = (1 << 16) as f64;
54
//         (self as f64 * SCALE + 0.5)
55
//             .floor()
56
//             .clamp(u16::MIN as f64, u16::MAX as f64) as u16
57
//     }
58
// }
59
60
impl FloatToFixedS15Fixed16 for f32 {
61
    #[inline]
62
0
    fn to_s15_fixed16(self) -> i32 {
63
        const SCALE: f64 = (1 << 16) as f64;
64
0
        (self as f64 * SCALE + 0.5)
65
0
            .floor()
66
0
            .clamp(i32::MIN as f64, i32::MAX as f64) as i32
67
0
    }
68
}
69
70
impl FloatToFixedS15Fixed16 for f64 {
71
    #[inline]
72
0
    fn to_s15_fixed16(self) -> i32 {
73
        const SCALE: f64 = (1 << 16) as f64;
74
0
        (self * SCALE + 0.5)
75
0
            .floor()
76
0
            .clamp(i32::MIN as f64, i32::MAX as f64) as i32
77
0
    }
78
}
79
80
#[inline]
81
0
fn write_u32_be(into: &mut Vec<u8>, value: u32) {
82
0
    let bytes = value.to_be_bytes();
83
0
    into.push(bytes[0]);
84
0
    into.push(bytes[1]);
85
0
    into.push(bytes[2]);
86
0
    into.push(bytes[3]);
87
0
}
88
89
#[inline]
90
0
pub(crate) fn write_u16_be(into: &mut Vec<u8>, value: u16) {
91
0
    let bytes = value.to_be_bytes();
92
0
    into.push(bytes[0]);
93
0
    into.push(bytes[1]);
94
0
}
95
96
#[inline]
97
0
fn write_i32_be(into: &mut Vec<u8>, value: i32) {
98
0
    let bytes = value.to_be_bytes();
99
0
    into.push(bytes[0]);
100
0
    into.push(bytes[1]);
101
0
    into.push(bytes[2]);
102
0
    into.push(bytes[3]);
103
0
}
104
105
0
fn first_two_ascii_bytes(s: &String) -> [u8; 2] {
106
0
    let bytes = s.as_bytes();
107
0
    if bytes.len() >= 2 {
108
0
        bytes[0..2].try_into().unwrap()
109
0
    } else if bytes.len() == 1 {
110
0
        let vec = vec![bytes[0], 0u8];
111
0
        vec.try_into().unwrap()
112
    } else {
113
0
        let vec = vec![0u8, 0u8];
114
0
        vec.try_into().unwrap()
115
    }
116
0
}
117
118
/// Writes Multi Localized Unicode
119
#[inline]
120
0
fn write_mluc(into: &mut Vec<u8>, strings: &[LocalizableString]) -> usize {
121
0
    assert!(!strings.is_empty());
122
0
    let start = into.len();
123
0
    let tag_def: u32 = TagTypeDefinition::MultiLocalizedUnicode.into();
124
0
    write_u32_be(into, tag_def);
125
0
    write_u32_be(into, 0);
126
0
    let number_of_records = strings.len();
127
0
    write_u32_be(into, number_of_records as u32);
128
0
    write_u32_be(into, 12); // Record size, must be 12
129
0
    let lang = first_two_ascii_bytes(&strings[0].language);
130
0
    into.extend_from_slice(&lang);
131
0
    let country = first_two_ascii_bytes(&strings[0].country);
132
0
    into.extend_from_slice(&country);
133
0
    let first_string_len = strings[0].value.len() * 2;
134
0
    write_u32_be(into, first_string_len as u32);
135
0
    let mut first_string_offset = 16 + 12 * strings.len();
136
0
    write_u32_be(into, first_string_offset as u32);
137
0
    first_string_offset += first_string_len;
138
0
    for record in strings.iter().skip(1) {
139
0
        let lang = first_two_ascii_bytes(&record.language);
140
0
        into.extend_from_slice(&lang);
141
0
        let country = first_two_ascii_bytes(&record.country);
142
0
        into.extend_from_slice(&country);
143
0
        let first_string_len = record.value.len() * 2;
144
0
        write_u32_be(into, first_string_len as u32);
145
0
        write_u32_be(into, first_string_offset as u32);
146
0
        first_string_offset += first_string_len;
147
0
    }
148
0
    for record in strings.iter() {
149
0
        for chunk in record.value.encode_utf16() {
150
0
            write_u16_be(into, chunk);
151
0
        }
152
    }
153
0
    let end = into.len();
154
0
    end - start
155
0
}
156
157
#[inline]
158
0
fn write_string_value(into: &mut Vec<u8>, text: &ProfileText) -> usize {
159
0
    match text {
160
0
        ProfileText::PlainString(text) => {
161
0
            let vec = vec![LocalizableString {
162
0
                language: "en".to_string(),
163
0
                country: "US".to_string(),
164
0
                value: text.clone(),
165
0
            }];
166
0
            write_mluc(into, &vec)
167
        }
168
0
        ProfileText::Localizable(localizable) => {
169
0
            if localizable.is_empty() {
170
0
                return 0;
171
0
            }
172
0
            write_mluc(into, localizable)
173
        }
174
0
        ProfileText::Description(description) => {
175
0
            let vec = vec![LocalizableString {
176
0
                language: "en".to_string(),
177
0
                country: "US".to_string(),
178
0
                value: description.unicode_string.clone(),
179
0
            }];
180
0
            write_mluc(into, &vec)
181
        }
182
    }
183
0
}
184
185
#[inline]
186
0
fn write_xyz_tag_value(into: &mut Vec<u8>, xyz: Xyzd) {
187
0
    let tag_definition: u32 = TagTypeDefinition::Xyz.into();
188
0
    write_u32_be(into, tag_definition);
189
0
    write_u32_be(into, 0);
190
0
    let x_fixed = xyz.x.to_s15_fixed16();
191
0
    write_i32_be(into, x_fixed);
192
0
    let y_fixed = xyz.y.to_s15_fixed16();
193
0
    write_i32_be(into, y_fixed);
194
0
    let z_fixed = xyz.z.to_s15_fixed16();
195
0
    write_i32_be(into, z_fixed);
196
0
}
197
198
#[inline]
199
0
fn write_tag_entry(into: &mut Vec<u8>, tag: Tag, tag_entry: usize, tag_size: usize) {
200
0
    let tag_value: u32 = tag.into();
201
0
    write_u32_be(into, tag_value);
202
0
    write_u32_be(into, tag_entry as u32);
203
0
    write_u32_be(into, tag_size as u32);
204
0
}
205
206
0
fn write_trc_entry(into: &mut Vec<u8>, trc: &ToneReprCurve) -> Result<usize, CmsError> {
207
0
    match trc {
208
0
        ToneReprCurve::Lut(lut) => {
209
0
            let curv: u32 = TagTypeDefinition::LutToneCurve.into();
210
0
            write_u32_be(into, curv);
211
0
            write_u32_be(into, 0);
212
0
            write_u32_be(into, lut.len() as u32);
213
0
            for item in lut.iter() {
214
0
                write_u16_be(into, *item);
215
0
            }
216
0
            Ok(12 + lut.len() * 2)
217
        }
218
0
        ToneReprCurve::Parametric(parametric_curve) => {
219
0
            if parametric_curve.len() > 7
220
0
                || parametric_curve.len() == 6
221
0
                || parametric_curve.len() == 2
222
            {
223
0
                return Err(CmsError::InvalidProfile);
224
0
            }
225
0
            let para: u32 = TagTypeDefinition::ParametricToneCurve.into();
226
0
            write_u32_be(into, para);
227
0
            write_u32_be(into, 0);
228
0
            if parametric_curve.len() == 1 {
229
0
                write_u16_be(into, 0);
230
0
            } else if parametric_curve.len() == 3 {
231
0
                write_u16_be(into, 1);
232
0
            } else if parametric_curve.len() == 4 {
233
0
                write_u16_be(into, 2);
234
0
            } else if parametric_curve.len() == 5 {
235
0
                write_u16_be(into, 3);
236
0
            } else if parametric_curve.len() == 7 {
237
0
                write_u16_be(into, 4);
238
0
            }
239
0
            write_u16_be(into, 0);
240
0
            for item in parametric_curve.iter() {
241
0
                write_i32_be(into, item.to_s15_fixed16());
242
0
            }
243
0
            Ok(12 + 4 * parametric_curve.len())
244
        }
245
    }
246
0
}
247
248
#[inline]
249
0
fn write_cicp_entry(into: &mut Vec<u8>, cicp: &CicpProfile) {
250
0
    let cicp_tag: u32 = TagTypeDefinition::Cicp.into();
251
0
    write_u32_be(into, cicp_tag);
252
0
    write_u32_be(into, 0);
253
0
    into.push(cicp.color_primaries as u8);
254
0
    into.push(cicp.transfer_characteristics as u8);
255
0
    into.push(cicp.matrix_coefficients as u8);
256
0
    into.push(if cicp.full_range { 1 } else { 0 });
257
0
}
258
259
0
fn write_chad(into: &mut Vec<u8>, matrix: Matrix3d) {
260
0
    let arr_type: u32 = TagTypeDefinition::S15Fixed16Array.into();
261
0
    write_u32_be(into, arr_type);
262
0
    write_u32_be(into, 0);
263
0
    write_matrix3d(into, matrix);
264
0
}
265
266
#[inline]
267
0
fn write_matrix3d(into: &mut Vec<u8>, v: Matrix3d) {
268
0
    write_i32_be(into, v.v[0][0].to_s15_fixed16());
269
0
    write_i32_be(into, v.v[0][1].to_s15_fixed16());
270
0
    write_i32_be(into, v.v[0][2].to_s15_fixed16());
271
272
0
    write_i32_be(into, v.v[1][0].to_s15_fixed16());
273
0
    write_i32_be(into, v.v[1][1].to_s15_fixed16());
274
0
    write_i32_be(into, v.v[1][2].to_s15_fixed16());
275
276
0
    write_i32_be(into, v.v[2][0].to_s15_fixed16());
277
0
    write_i32_be(into, v.v[2][1].to_s15_fixed16());
278
0
    write_i32_be(into, v.v[2][2].to_s15_fixed16());
279
0
}
280
281
#[inline]
282
0
fn write_vector3d(into: &mut Vec<u8>, v: Vector3d) {
283
0
    write_i32_be(into, v.v[0].to_s15_fixed16());
284
0
    write_i32_be(into, v.v[1].to_s15_fixed16());
285
0
    write_i32_be(into, v.v[2].to_s15_fixed16());
286
0
}
287
288
#[inline]
289
0
fn write_lut_entry(into: &mut Vec<u8>, lut: &LutDataType) -> Result<usize, CmsError> {
290
0
    if !lut.has_same_kind() {
291
0
        return Err(CmsError::InvalidProfile);
292
0
    }
293
0
    let start = into.len();
294
0
    let lut16_tag: u32 = match &lut.input_table {
295
0
        LutStore::Store8(_) => LutType::Lut8.into(),
296
0
        LutStore::Store16(_) => LutType::Lut16.into(),
297
    };
298
0
    write_u32_be(into, lut16_tag);
299
0
    write_u32_be(into, 0);
300
0
    into.push(lut.num_input_channels);
301
0
    into.push(lut.num_output_channels);
302
0
    into.push(lut.num_clut_grid_points);
303
0
    into.push(0);
304
0
    write_matrix3d(into, lut.matrix);
305
0
    write_u16_be(into, lut.num_input_table_entries);
306
0
    write_u16_be(into, lut.num_output_table_entries);
307
0
    match &lut.input_table {
308
0
        LutStore::Store8(input_table) => {
309
0
            for &item in input_table.iter() {
310
0
                into.push(item);
311
0
            }
312
        }
313
0
        LutStore::Store16(input_table) => {
314
0
            for &item in input_table.iter() {
315
0
                write_u16_be(into, item);
316
0
            }
317
        }
318
    }
319
0
    match &lut.clut_table {
320
0
        LutStore::Store8(input_table) => {
321
0
            for &item in input_table.iter() {
322
0
                into.push(item);
323
0
            }
324
        }
325
0
        LutStore::Store16(input_table) => {
326
0
            for &item in input_table.iter() {
327
0
                write_u16_be(into, item);
328
0
            }
329
        }
330
    }
331
0
    match &lut.output_table {
332
0
        LutStore::Store8(input_table) => {
333
0
            for &item in input_table.iter() {
334
0
                into.push(item);
335
0
            }
336
        }
337
0
        LutStore::Store16(input_table) => {
338
0
            for &item in input_table.iter() {
339
0
                write_u16_be(into, item);
340
0
            }
341
        }
342
    }
343
0
    let end = into.len();
344
0
    Ok(end - start)
345
0
}
346
347
#[inline]
348
0
fn write_mab_entry(
349
0
    into: &mut Vec<u8>,
350
0
    lut: &LutMultidimensionalType,
351
0
    is_a_to_b: bool,
352
0
) -> Result<usize, CmsError> {
353
0
    let start = into.len();
354
0
    let lut16_tag: u32 = if is_a_to_b {
355
0
        LutType::LutMab.into()
356
    } else {
357
0
        LutType::LutMba.into()
358
    };
359
0
    write_u32_be(into, lut16_tag);
360
0
    write_u32_be(into, 0);
361
0
    into.push(lut.num_input_channels);
362
0
    into.push(lut.num_output_channels);
363
0
    write_u16_be(into, 0);
364
0
    let mut working_offset = 32usize;
365
366
0
    let mut data = Vec::new();
367
368
    // Offset to "B curves"
369
0
    if !lut.b_curves.is_empty() {
370
0
        while working_offset % 4 != 0 {
371
0
            data.push(0);
372
0
            working_offset += 1;
373
0
        }
374
375
0
        write_u32_be(into, working_offset as u32);
376
377
0
        for trc in lut.b_curves.iter() {
378
0
            let curve_size = write_trc_entry(&mut data, trc)?;
379
0
            working_offset += curve_size;
380
0
            while working_offset % 4 != 0 {
381
0
                data.push(0);
382
0
                working_offset += 1;
383
0
            }
384
        }
385
0
    } else {
386
0
        write_u32_be(into, 0);
387
0
    }
388
389
    // Offset to matrix
390
0
    if !lut.m_curves.is_empty() {
391
0
        while working_offset % 4 != 0 {
392
0
            data.push(0);
393
0
            working_offset += 1;
394
0
        }
395
396
0
        write_u32_be(into, working_offset as u32);
397
0
        write_matrix3d(&mut data, lut.matrix);
398
0
        write_vector3d(&mut data, lut.bias);
399
0
        working_offset += 9 * 4 + 3 * 4;
400
        // Offset to "M curves"
401
0
        write_u32_be(into, working_offset as u32);
402
0
        for trc in lut.m_curves.iter() {
403
0
            let curve_size = write_trc_entry(&mut data, trc)?;
404
0
            working_offset += curve_size;
405
0
            while working_offset % 4 != 0 {
406
0
                data.push(0);
407
0
                working_offset += 1;
408
0
            }
409
        }
410
0
    } else {
411
0
        // Offset to matrix
412
0
        write_u32_be(into, 0);
413
0
        // Offset to "M curves"
414
0
        write_u32_be(into, 0);
415
0
    }
416
417
0
    let mut clut_start = data.len();
418
419
    // Offset to CLUT
420
0
    if let Some(clut) = &lut.clut {
421
0
        while working_offset % 4 != 0 {
422
0
            data.push(0);
423
0
            working_offset += 1;
424
0
        }
425
426
0
        clut_start = data.len();
427
428
0
        write_u32_be(into, working_offset as u32);
429
430
        // Writing CLUT
431
0
        for &pt in lut.grid_points.iter() {
432
0
            data.push(pt);
433
0
        }
434
0
        data.push(match clut {
435
0
            LutStore::Store8(_) => 1,
436
0
            LutStore::Store16(_) => 2,
437
        }); // Entry size
438
0
        data.push(0);
439
0
        data.push(0);
440
0
        data.push(0);
441
0
        match clut {
442
0
            LutStore::Store8(store) => {
443
0
                for &element in store.iter() {
444
0
                    data.push(element)
445
                }
446
            }
447
0
            LutStore::Store16(store) => {
448
0
                for &element in store.iter() {
449
0
                    write_u16_be(&mut data, element);
450
0
                }
451
            }
452
        }
453
0
    } else {
454
0
        write_u32_be(into, 0);
455
0
    }
456
457
0
    let clut_size = data.len() - clut_start;
458
0
    working_offset += clut_size;
459
460
    // Offset to "A curves"
461
0
    if !lut.a_curves.is_empty() {
462
0
        while working_offset % 4 != 0 {
463
0
            data.push(0);
464
0
            working_offset += 1;
465
0
        }
466
467
0
        write_u32_be(into, working_offset as u32);
468
469
0
        for trc in lut.a_curves.iter() {
470
0
            let curve_size = write_trc_entry(&mut data, trc)?;
471
0
            working_offset += curve_size;
472
0
            while working_offset % 4 != 0 {
473
0
                data.push(0);
474
0
                working_offset += 1;
475
0
            }
476
        }
477
0
    } else {
478
0
        write_u32_be(into, 0);
479
0
    }
480
481
0
    into.extend(data);
482
483
0
    let end = into.len();
484
0
    Ok(end - start)
485
0
}
486
487
0
fn write_lut(into: &mut Vec<u8>, lut: &LutWarehouse, is_a_to_b: bool) -> Result<usize, CmsError> {
488
0
    match lut {
489
0
        LutWarehouse::Lut(lut) => Ok(write_lut_entry(into, lut)?),
490
0
        LutWarehouse::Multidimensional(mab) => write_mab_entry(into, mab, is_a_to_b),
491
    }
492
0
}
493
494
impl ProfileHeader {
495
0
    fn encode(&self) -> Vec<u8> {
496
0
        let mut encoder: Vec<u8> = Vec::with_capacity(size_of::<ProfileHeader>());
497
0
        write_u32_be(&mut encoder, self.size); // Size
498
0
        write_u32_be(&mut encoder, 0); // CMM Type
499
0
        write_u32_be(&mut encoder, self.version.into()); // Version Number Type
500
0
        write_u32_be(&mut encoder, self.profile_class.into()); // Profile class
501
0
        write_u32_be(&mut encoder, self.data_color_space.into()); // Data color space
502
0
        write_u32_be(&mut encoder, self.pcs.into()); // PCS
503
0
        self.creation_date_time.encode(&mut encoder); // Date time
504
0
        write_u32_be(&mut encoder, self.signature.into()); // Profile signature
505
0
        write_u32_be(&mut encoder, self.platform);
506
0
        write_u32_be(&mut encoder, self.flags);
507
0
        write_u32_be(&mut encoder, self.device_manufacturer);
508
0
        write_u32_be(&mut encoder, self.device_model);
509
0
        for &i in self.device_attributes.iter() {
510
0
            encoder.push(i);
511
0
        }
512
0
        write_u32_be(&mut encoder, self.rendering_intent.into());
513
0
        write_i32_be(&mut encoder, self.illuminant.x.to_s15_fixed16());
514
0
        write_i32_be(&mut encoder, self.illuminant.y.to_s15_fixed16());
515
0
        write_i32_be(&mut encoder, self.illuminant.z.to_s15_fixed16());
516
0
        write_u32_be(&mut encoder, self.creator);
517
0
        for &i in self.profile_id.iter() {
518
0
            encoder.push(i);
519
0
        }
520
0
        for &i in self.reserved.iter() {
521
0
            encoder.push(i);
522
0
        }
523
0
        write_u32_be(&mut encoder, self.tag_count);
524
0
        encoder
525
0
    }
526
}
527
528
impl ColorProfile {
529
0
    fn writable_tags_count(&self) -> usize {
530
0
        let mut tags_count = 0usize;
531
0
        if self.red_colorant != Xyzd::default() {
532
0
            tags_count += 1;
533
0
        }
534
0
        if self.green_colorant != Xyzd::default() {
535
0
            tags_count += 1;
536
0
        }
537
0
        if self.blue_colorant != Xyzd::default() {
538
0
            tags_count += 1;
539
0
        }
540
0
        if self.red_trc.is_some() {
541
0
            tags_count += 1;
542
0
        }
543
0
        if self.green_trc.is_some() {
544
0
            tags_count += 1;
545
0
        }
546
0
        if self.blue_trc.is_some() {
547
0
            tags_count += 1;
548
0
        }
549
0
        if self.gray_trc.is_some() {
550
0
            tags_count += 1;
551
0
        }
552
0
        if self.cicp.is_some() {
553
0
            tags_count += 1;
554
0
        }
555
0
        if self.media_white_point.is_some() {
556
0
            tags_count += 1;
557
0
        }
558
0
        if self.gamut.is_some() {
559
0
            tags_count += 1;
560
0
        }
561
0
        if self.chromatic_adaptation.is_some() {
562
0
            tags_count += 1;
563
0
        }
564
0
        if self.lut_a_to_b_perceptual.is_some() {
565
0
            tags_count += 1;
566
0
        }
567
0
        if self.lut_a_to_b_colorimetric.is_some() {
568
0
            tags_count += 1;
569
0
        }
570
0
        if self.lut_a_to_b_saturation.is_some() {
571
0
            tags_count += 1;
572
0
        }
573
0
        if self.lut_b_to_a_perceptual.is_some() {
574
0
            tags_count += 1;
575
0
        }
576
0
        if self.lut_b_to_a_colorimetric.is_some() {
577
0
            tags_count += 1;
578
0
        }
579
0
        if self.lut_b_to_a_saturation.is_some() {
580
0
            tags_count += 1;
581
0
        }
582
0
        if self.luminance.is_some() {
583
0
            tags_count += 1;
584
0
        }
585
0
        if let Some(description) = &self.description {
586
0
            if description.has_values() {
587
0
                tags_count += 1;
588
0
            }
589
0
        }
590
0
        if let Some(copyright) = &self.copyright {
591
0
            if copyright.has_values() {
592
0
                tags_count += 1;
593
0
            }
594
0
        }
595
0
        if let Some(vd) = &self.viewing_conditions_description {
596
0
            if vd.has_values() {
597
0
                tags_count += 1;
598
0
            }
599
0
        }
600
0
        if let Some(vd) = &self.device_model {
601
0
            if vd.has_values() {
602
0
                tags_count += 1;
603
0
            }
604
0
        }
605
0
        if let Some(vd) = &self.device_manufacturer {
606
0
            if vd.has_values() {
607
0
                tags_count += 1;
608
0
            }
609
0
        }
610
0
        tags_count
611
0
    }
612
613
    /// Encodes profile
614
0
    pub fn encode(&self) -> Result<Vec<u8>, CmsError> {
615
0
        let mut entries = Vec::new();
616
0
        let tags_count = self.writable_tags_count();
617
0
        let mut tags = Vec::with_capacity(TAG_SIZE * tags_count);
618
0
        let mut base_offset = size_of::<ProfileHeader>() + TAG_SIZE * tags_count;
619
0
        if self.red_colorant != Xyzd::default() {
620
0
            write_tag_entry(&mut tags, Tag::RedXyz, base_offset, 20);
621
0
            write_xyz_tag_value(&mut entries, self.red_colorant);
622
0
            base_offset += 20;
623
0
        }
624
0
        if self.green_colorant != Xyzd::default() {
625
0
            write_tag_entry(&mut tags, Tag::GreenXyz, base_offset, 20);
626
0
            write_xyz_tag_value(&mut entries, self.green_colorant);
627
0
            base_offset += 20;
628
0
        }
629
0
        if self.blue_colorant != Xyzd::default() {
630
0
            write_tag_entry(&mut tags, Tag::BlueXyz, base_offset, 20);
631
0
            write_xyz_tag_value(&mut entries, self.blue_colorant);
632
0
            base_offset += 20;
633
0
        }
634
0
        if let Some(chad) = self.chromatic_adaptation {
635
0
            write_tag_entry(&mut tags, Tag::ChromaticAdaptation, base_offset, 8 + 9 * 4);
636
0
            write_chad(&mut entries, chad);
637
0
            base_offset += 8 + 9 * 4;
638
0
        }
639
0
        if let Some(trc) = &self.red_trc {
640
0
            let entry_size = write_trc_entry(&mut entries, trc)?;
641
0
            write_tag_entry(&mut tags, Tag::RedToneReproduction, base_offset, entry_size);
642
0
            base_offset += entry_size;
643
0
        }
644
0
        if let Some(trc) = &self.green_trc {
645
0
            let entry_size = write_trc_entry(&mut entries, trc)?;
646
0
            write_tag_entry(
647
0
                &mut tags,
648
0
                Tag::GreenToneReproduction,
649
0
                base_offset,
650
0
                entry_size,
651
            );
652
0
            base_offset += entry_size;
653
0
        }
654
0
        if let Some(trc) = &self.blue_trc {
655
0
            let entry_size = write_trc_entry(&mut entries, trc)?;
656
0
            write_tag_entry(
657
0
                &mut tags,
658
0
                Tag::BlueToneReproduction,
659
0
                base_offset,
660
0
                entry_size,
661
            );
662
0
            base_offset += entry_size;
663
0
        }
664
0
        if let Some(trc) = &self.gray_trc {
665
0
            let entry_size = write_trc_entry(&mut entries, trc)?;
666
0
            write_tag_entry(
667
0
                &mut tags,
668
0
                Tag::GreyToneReproduction,
669
0
                base_offset,
670
0
                entry_size,
671
            );
672
0
            base_offset += entry_size;
673
0
        }
674
0
        if self.white_point != Xyzd::default() {
675
0
            write_tag_entry(&mut tags, Tag::MediaWhitePoint, base_offset, 20);
676
0
            write_xyz_tag_value(&mut entries, self.white_point);
677
0
            base_offset += 20;
678
0
        }
679
680
0
        let has_cicp = self.cicp.is_some();
681
682
        // This tag may be present when the data colour space in the profile header is RGB, YCbCr, or XYZ, and the
683
        // profile class in the profile header is Input or Display. The tag shall not be present for other data colour spaces
684
        // or profile classes indicated in the profile header.
685
686
0
        if let Some(cicp) = &self.cicp {
687
0
            if (self.profile_class == ProfileClass::InputDevice
688
0
                || self.profile_class == ProfileClass::DisplayDevice)
689
0
                && (self.color_space == DataColorSpace::Rgb
690
0
                    || self.color_space == DataColorSpace::YCbr
691
0
                    || self.color_space == DataColorSpace::Xyz)
692
0
            {
693
0
                write_tag_entry(&mut tags, Tag::CodeIndependentPoints, base_offset, 12);
694
0
                write_cicp_entry(&mut entries, cicp);
695
0
                base_offset += 12;
696
0
            }
697
0
        }
698
699
0
        if let Some(lut) = &self.lut_a_to_b_perceptual {
700
0
            let entry_size = write_lut(&mut entries, lut, true)?;
701
0
            write_tag_entry(
702
0
                &mut tags,
703
0
                Tag::DeviceToPcsLutPerceptual,
704
0
                base_offset,
705
0
                entry_size,
706
            );
707
0
            base_offset += entry_size;
708
0
        }
709
710
0
        if let Some(lut) = &self.lut_a_to_b_colorimetric {
711
0
            let entry_size = write_lut(&mut entries, lut, true)?;
712
0
            write_tag_entry(
713
0
                &mut tags,
714
0
                Tag::DeviceToPcsLutColorimetric,
715
0
                base_offset,
716
0
                entry_size,
717
            );
718
0
            base_offset += entry_size;
719
0
        }
720
721
0
        if let Some(lut) = &self.lut_a_to_b_saturation {
722
0
            let entry_size = write_lut(&mut entries, lut, true)?;
723
0
            write_tag_entry(
724
0
                &mut tags,
725
0
                Tag::DeviceToPcsLutSaturation,
726
0
                base_offset,
727
0
                entry_size,
728
            );
729
0
            base_offset += entry_size;
730
0
        }
731
732
0
        if let Some(lut) = &self.lut_b_to_a_perceptual {
733
0
            let entry_size = write_lut(&mut entries, lut, false)?;
734
0
            write_tag_entry(
735
0
                &mut tags,
736
0
                Tag::PcsToDeviceLutPerceptual,
737
0
                base_offset,
738
0
                entry_size,
739
            );
740
0
            base_offset += entry_size;
741
0
        }
742
743
0
        if let Some(lut) = &self.lut_b_to_a_colorimetric {
744
0
            let entry_size = write_lut(&mut entries, lut, false)?;
745
0
            write_tag_entry(
746
0
                &mut tags,
747
0
                Tag::PcsToDeviceLutColorimetric,
748
0
                base_offset,
749
0
                entry_size,
750
            );
751
0
            base_offset += entry_size;
752
0
        }
753
754
0
        if let Some(lut) = &self.lut_b_to_a_saturation {
755
0
            let entry_size = write_lut(&mut entries, lut, false)?;
756
0
            write_tag_entry(
757
0
                &mut tags,
758
0
                Tag::PcsToDeviceLutSaturation,
759
0
                base_offset,
760
0
                entry_size,
761
            );
762
0
            base_offset += entry_size;
763
0
        }
764
765
0
        if let Some(lut) = &self.gamut {
766
0
            let entry_size = write_lut(&mut entries, lut, false)?;
767
0
            write_tag_entry(&mut tags, Tag::Gamut, base_offset, entry_size);
768
0
            base_offset += entry_size;
769
0
        }
770
771
0
        if let Some(luminance) = self.luminance {
772
0
            write_tag_entry(&mut tags, Tag::Luminance, base_offset, 20);
773
0
            write_xyz_tag_value(&mut entries, luminance);
774
0
            base_offset += 20;
775
0
        }
776
777
0
        if let Some(description) = &self.description {
778
0
            if description.has_values() {
779
0
                let entry_size = write_string_value(&mut entries, description);
780
0
                write_tag_entry(&mut tags, Tag::ProfileDescription, base_offset, entry_size);
781
0
                base_offset += entry_size;
782
0
            }
783
0
        }
784
785
0
        if let Some(copyright) = &self.copyright {
786
0
            if copyright.has_values() {
787
0
                let entry_size = write_string_value(&mut entries, copyright);
788
0
                write_tag_entry(&mut tags, Tag::Copyright, base_offset, entry_size);
789
0
                base_offset += entry_size;
790
0
            }
791
0
        }
792
793
0
        if let Some(vd) = &self.viewing_conditions_description {
794
0
            if vd.has_values() {
795
0
                let entry_size = write_string_value(&mut entries, vd);
796
0
                write_tag_entry(
797
0
                    &mut tags,
798
0
                    Tag::ViewingConditionsDescription,
799
0
                    base_offset,
800
0
                    entry_size,
801
0
                );
802
0
                base_offset += entry_size;
803
0
            }
804
0
        }
805
806
0
        if let Some(vd) = &self.device_model {
807
0
            if vd.has_values() {
808
0
                let entry_size = write_string_value(&mut entries, vd);
809
0
                write_tag_entry(&mut tags, Tag::DeviceModel, base_offset, entry_size);
810
0
                base_offset += entry_size;
811
0
            }
812
0
        }
813
814
0
        if let Some(vd) = &self.device_manufacturer {
815
0
            if vd.has_values() {
816
0
                let entry_size = write_string_value(&mut entries, vd);
817
0
                write_tag_entry(&mut tags, Tag::DeviceManufacturer, base_offset, entry_size);
818
0
                // base_offset += entry_size;
819
0
            }
820
0
        }
821
822
0
        tags.extend(entries);
823
824
0
        let profile_header = ProfileHeader {
825
0
            size: size_of::<ProfileHeader>() as u32 + tags.len() as u32,
826
0
            pcs: self.pcs,
827
0
            profile_class: self.profile_class,
828
0
            rendering_intent: self.rendering_intent,
829
            cmm_type: 0,
830
0
            version: if has_cicp {
831
0
                ProfileVersion::V4_3
832
            } else {
833
0
                ProfileVersion::V4_0
834
            },
835
0
            data_color_space: self.color_space,
836
0
            creation_date_time: ColorDateTime::now(),
837
0
            signature: ProfileSignature::Acsp,
838
            platform: 0u32,
839
            flags: 0u32,
840
            device_manufacturer: 0u32,
841
            device_model: 0u32,
842
0
            device_attributes: [0u8; 8],
843
0
            illuminant: self.white_point.to_xyz(),
844
            creator: 0u32,
845
0
            profile_id: [0u8; 16],
846
0
            reserved: [0u8; 28],
847
0
            tag_count: tags_count as u32,
848
        };
849
0
        let mut header = profile_header.encode();
850
0
        header.extend(tags);
851
0
        Ok(header)
852
0
    }
853
}
854
855
impl FloatToFixedU8Fixed8 for f32 {
856
    #[inline]
857
0
    fn to_u8_fixed8(self) -> u16 {
858
0
        if self > 255.0 + 255.0 / 256f32 {
859
0
            0xffffu16
860
0
        } else if self < 0.0 {
861
0
            0u16
862
        } else {
863
0
            (self * 256.0 + 0.5).floor() as u16
864
        }
865
0
    }
866
}
867
868
#[cfg(test)]
869
mod 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
}