1use crate::bit_writer::BitWriter;
8use crate::error::Result;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12#[repr(u8)]
13pub enum ColorSpace {
14 #[default]
16 Rgb = 0,
17 Gray = 1,
19 Xyb = 2,
21 Unknown = 3,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
27#[repr(u8)]
28pub enum WhitePoint {
29 #[default]
31 D65 = 1,
32 Custom = 2,
34 E = 10,
36 Dci = 11,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
42#[repr(u8)]
43pub enum Primaries {
44 #[default]
46 Srgb = 1,
47 Custom = 2,
49 Bt2100 = 9,
51 P3 = 11,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
57#[repr(u8)]
58pub enum TransferFunction {
59 Bt709 = 1,
61 Unknown = 2,
63 Linear = 8,
65 #[default]
67 Srgb = 13,
68 Pq = 16,
70 Dci = 17,
72 Hlg = 18,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
78#[repr(u8)]
79pub enum RenderingIntent {
80 #[default]
82 Perceptual = 0,
83 Relative = 1,
85 Saturation = 2,
87 Absolute = 3,
89}
90
91#[derive(Debug, Clone, Default)]
93pub struct ColorEncoding {
94 pub color_space: ColorSpace,
96 pub white_point: WhitePoint,
98 pub primaries: Primaries,
100 pub transfer_function: TransferFunction,
102 pub rendering_intent: RenderingIntent,
104 pub want_icc: bool,
106 pub gamma: Option<f32>,
109}
110
111impl ColorEncoding {
112 pub fn srgb() -> Self {
114 Self {
115 color_space: ColorSpace::Rgb,
116 white_point: WhitePoint::D65,
117 primaries: Primaries::Srgb,
118 transfer_function: TransferFunction::Srgb,
119 rendering_intent: RenderingIntent::Perceptual,
120 want_icc: false,
121 gamma: None,
122 }
123 }
124
125 pub fn linear_srgb() -> Self {
127 Self {
128 color_space: ColorSpace::Rgb,
129 white_point: WhitePoint::D65,
130 primaries: Primaries::Srgb,
131 transfer_function: TransferFunction::Linear,
132 rendering_intent: RenderingIntent::Perceptual,
133 want_icc: false,
134 gamma: None,
135 }
136 }
137
138 pub fn gray() -> Self {
140 Self {
141 color_space: ColorSpace::Gray,
142 white_point: WhitePoint::D65,
143 primaries: Primaries::Srgb,
144 transfer_function: TransferFunction::Srgb,
145 rendering_intent: RenderingIntent::Perceptual,
146 want_icc: false,
147 gamma: None,
148 }
149 }
150
151 pub fn display_p3() -> Self {
153 Self {
154 color_space: ColorSpace::Rgb,
155 white_point: WhitePoint::D65,
156 primaries: Primaries::P3,
157 transfer_function: TransferFunction::Srgb,
158 rendering_intent: RenderingIntent::Perceptual,
159 want_icc: false,
160 gamma: None,
161 }
162 }
163
164 pub fn with_gamma(gamma: f32) -> Self {
169 Self {
170 gamma: Some(gamma),
171 ..Self::srgb()
172 }
173 }
174
175 pub fn gray_with_gamma(gamma: f32) -> Self {
177 Self {
178 gamma: Some(gamma),
179 ..Self::gray()
180 }
181 }
182
183 pub fn bt2100_pq() -> Self {
185 Self {
186 color_space: ColorSpace::Rgb,
187 white_point: WhitePoint::D65,
188 primaries: Primaries::Bt2100,
189 transfer_function: TransferFunction::Pq,
190 rendering_intent: RenderingIntent::Perceptual,
191 want_icc: false,
192 gamma: None,
193 }
194 }
195
196 pub fn grayscale() -> Self {
198 Self {
199 color_space: ColorSpace::Gray,
200 white_point: WhitePoint::D65,
201 primaries: Primaries::Srgb,
202 transfer_function: TransferFunction::Srgb,
203 rendering_intent: RenderingIntent::Perceptual,
204 want_icc: false,
205 gamma: None,
206 }
207 }
208
209 pub fn is_srgb(&self) -> bool {
215 self.color_space == ColorSpace::Rgb
216 && self.white_point == WhitePoint::D65
217 && self.primaries == Primaries::Srgb
218 && self.transfer_function == TransferFunction::Srgb
219 && self.rendering_intent == RenderingIntent::Perceptual
220 && !self.want_icc
221 && self.gamma.is_none()
222 }
223
224 pub fn is_gray(&self) -> bool {
226 self.color_space == ColorSpace::Gray
227 }
228
229 pub fn write(&self, writer: &mut BitWriter) -> Result<()> {
231 let all_default = self.is_srgb();
233 crate::trace::debug_eprintln!(
234 "CENC [bit {}]: all_default = {}",
235 writer.bits_written(),
236 all_default
237 );
238 writer.write_bit(all_default)?;
239
240 if all_default {
241 return Ok(());
242 }
243
244 crate::trace::debug_eprintln!(
246 "CENC [bit {}]: want_icc = {}",
247 writer.bits_written(),
248 self.want_icc
249 );
250 writer.write_bit(self.want_icc)?;
251
252 crate::trace::debug_eprintln!(
254 "CENC [bit {}]: color_space = {:?} ({})",
255 writer.bits_written(),
256 self.color_space,
257 self.color_space as u8
258 );
259 writer.write(2, self.color_space as u64)?;
260
261 if self.want_icc {
262 return Ok(());
264 }
265
266 let wp = match self.white_point {
268 WhitePoint::D65 => 1,
269 WhitePoint::Custom => 2,
270 WhitePoint::E => 10,
271 WhitePoint::Dci => 11,
272 };
273 crate::trace::debug_eprintln!(
274 "CENC [bit {}]: white_point = {:?} ({})",
275 writer.bits_written(),
276 self.white_point,
277 wp
278 );
279 writer.write_enum_default(wp)?;
280 if self.white_point == WhitePoint::Custom {
281 todo!("Custom white point not implemented");
283 }
284
285 if self.color_space == ColorSpace::Rgb {
287 let prim = match self.primaries {
288 Primaries::Srgb => 1,
289 Primaries::Custom => 2,
290 Primaries::Bt2100 => 9,
291 Primaries::P3 => 11,
292 };
293 crate::trace::debug_eprintln!(
294 "CENC [bit {}]: primaries = {:?} ({})",
295 writer.bits_written(),
296 self.primaries,
297 prim
298 );
299 writer.write_enum_default(prim)?;
300 if self.primaries == Primaries::Custom {
301 todo!("Custom primaries not implemented");
303 }
304 } else {
305 crate::trace::debug_eprintln!(
306 "CENC [bit {}]: primaries skipped (not RGB)",
307 writer.bits_written()
308 );
309 }
310
311 let have_gamma = self.gamma.is_some();
313 crate::trace::debug_eprintln!(
314 "CENC [bit {}]: have_gamma = {}",
315 writer.bits_written(),
316 have_gamma
317 );
318 writer.write_bit(have_gamma)?;
319
320 if have_gamma {
321 let g = self.gamma.expect("gamma must be set when have_gamma=true");
322 let encoded = (g * 10_000_000.0).round() as u32;
324 crate::trace::debug_eprintln!(
325 "CENC [bit {}]: gamma = {} (encoded {})",
326 writer.bits_written(),
327 g,
328 encoded
329 );
330 writer.write(24, encoded as u64)?;
331 } else {
332 let tf = match self.transfer_function {
334 TransferFunction::Bt709 => 1,
335 TransferFunction::Unknown => 2,
336 TransferFunction::Linear => 8,
337 TransferFunction::Srgb => 13,
338 TransferFunction::Pq => 16,
339 TransferFunction::Dci => 17,
340 TransferFunction::Hlg => 18,
341 };
342 crate::trace::debug_eprintln!(
343 "CENC [bit {}]: transfer_function = {:?} ({})",
344 writer.bits_written(),
345 self.transfer_function,
346 tf
347 );
348 writer.write_enum_default(tf)?;
349 }
350
351 crate::trace::debug_eprintln!(
353 "CENC [bit {}]: rendering_intent = {:?} ({})",
354 writer.bits_written(),
355 self.rendering_intent,
356 self.rendering_intent as u8
357 );
358 writer.write(2, self.rendering_intent as u64)?;
359 crate::trace::debug_eprintln!("CENC [bit {}]: color_encoding done", writer.bits_written());
360
361 Ok(())
362 }
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368
369 #[test]
370 fn test_srgb_is_default() {
371 let enc = ColorEncoding::srgb();
372 assert!(enc.is_srgb());
375 }
376
377 #[test]
378 fn test_write_srgb() {
379 let enc = ColorEncoding::srgb();
380 let mut writer = BitWriter::new();
381 enc.write(&mut writer).unwrap();
382 writer.zero_pad_to_byte();
383
384 assert_eq!(writer.bits_written(), 8);
387 }
388
389 #[test]
390 fn test_write_non_default_srgb() {
391 let enc = ColorEncoding {
393 color_space: ColorSpace::Rgb,
394 white_point: WhitePoint::D65,
395 primaries: Primaries::Srgb,
396 transfer_function: TransferFunction::Srgb,
397 rendering_intent: RenderingIntent::Relative, want_icc: false,
399 gamma: None,
400 };
401 let mut writer = BitWriter::new();
402 enc.write(&mut writer).unwrap();
403 writer.zero_pad_to_byte();
404
405 assert_eq!(writer.bits_written(), 24);
412 }
413
414 #[test]
415 fn test_color_space_values() {
416 assert_eq!(ColorSpace::Rgb as u8, 0);
417 assert_eq!(ColorSpace::Gray as u8, 1);
418 assert_eq!(ColorSpace::Xyb as u8, 2);
419 assert_eq!(ColorSpace::Unknown as u8, 3);
420 }
421
422 #[test]
423 fn test_white_point_values() {
424 assert_eq!(WhitePoint::D65 as u8, 1);
425 assert_eq!(WhitePoint::Custom as u8, 2);
426 assert_eq!(WhitePoint::E as u8, 10);
427 assert_eq!(WhitePoint::Dci as u8, 11);
428 }
429
430 #[test]
431 fn test_primaries_values() {
432 assert_eq!(Primaries::Srgb as u8, 1);
433 assert_eq!(Primaries::Custom as u8, 2);
434 assert_eq!(Primaries::Bt2100 as u8, 9);
435 assert_eq!(Primaries::P3 as u8, 11);
436 }
437
438 #[test]
439 fn test_transfer_function_values() {
440 assert_eq!(TransferFunction::Bt709 as u8, 1);
441 assert_eq!(TransferFunction::Unknown as u8, 2);
442 assert_eq!(TransferFunction::Linear as u8, 8);
443 assert_eq!(TransferFunction::Srgb as u8, 13);
444 assert_eq!(TransferFunction::Pq as u8, 16);
445 assert_eq!(TransferFunction::Dci as u8, 17);
446 assert_eq!(TransferFunction::Hlg as u8, 18);
447 }
448
449 #[test]
450 fn test_rendering_intent_values() {
451 assert_eq!(RenderingIntent::Perceptual as u8, 0);
452 assert_eq!(RenderingIntent::Relative as u8, 1);
453 assert_eq!(RenderingIntent::Saturation as u8, 2);
454 assert_eq!(RenderingIntent::Absolute as u8, 3);
455 }
456
457 #[test]
458 fn test_write_linear_srgb() {
459 let enc = ColorEncoding::linear_srgb();
460 assert_eq!(enc.transfer_function, TransferFunction::Linear);
461
462 let mut writer = BitWriter::new();
463 enc.write(&mut writer).unwrap();
464 assert!(writer.bits_written() > 0);
465 }
466
467 #[test]
468 fn test_write_grayscale() {
469 let enc = ColorEncoding::grayscale();
470 assert!(enc.is_gray());
471 assert_eq!(enc.color_space, ColorSpace::Gray);
472
473 let mut writer = BitWriter::new();
474 enc.write(&mut writer).unwrap();
475 assert!(writer.bits_written() > 0);
477 }
478
479 #[test]
480 fn test_write_gray() {
481 let enc = ColorEncoding::gray();
482 assert!(enc.is_gray());
483
484 let mut writer = BitWriter::new();
485 enc.write(&mut writer).unwrap();
486 assert!(writer.bits_written() > 0);
487 }
488
489 #[test]
490 fn test_write_display_p3() {
491 let enc = ColorEncoding::display_p3();
492 assert_eq!(enc.primaries, Primaries::P3);
493
494 let mut writer = BitWriter::new();
495 enc.write(&mut writer).unwrap();
496 assert!(writer.bits_written() > 0);
497 }
498
499 #[test]
500 fn test_write_bt2100_pq() {
501 let enc = ColorEncoding::bt2100_pq();
502 assert_eq!(enc.primaries, Primaries::Bt2100);
503 assert_eq!(enc.transfer_function, TransferFunction::Pq);
504
505 let mut writer = BitWriter::new();
506 enc.write(&mut writer).unwrap();
507 assert!(writer.bits_written() > 0);
508 }
509
510 #[test]
511 fn test_write_with_want_icc() {
512 let mut enc = ColorEncoding::srgb();
513 enc.want_icc = true;
514
515 let mut writer = BitWriter::new();
516 enc.write(&mut writer).unwrap();
517 assert_eq!(writer.bits_written(), 4);
519 }
520
521 #[test]
522 fn test_write_bt709_transfer() {
523 let mut enc = ColorEncoding::srgb();
524 enc.transfer_function = TransferFunction::Bt709;
525
526 let mut writer = BitWriter::new();
527 enc.write(&mut writer).unwrap();
528 assert!(writer.bits_written() > 0);
529 }
530
531 #[test]
532 fn test_write_dci_transfer() {
533 let mut enc = ColorEncoding::srgb();
534 enc.transfer_function = TransferFunction::Dci;
535
536 let mut writer = BitWriter::new();
537 enc.write(&mut writer).unwrap();
538 assert!(writer.bits_written() > 0);
539 }
540
541 #[test]
542 fn test_write_hlg_transfer() {
543 let mut enc = ColorEncoding::srgb();
544 enc.transfer_function = TransferFunction::Hlg;
545
546 let mut writer = BitWriter::new();
547 enc.write(&mut writer).unwrap();
548 assert!(writer.bits_written() > 0);
549 }
550
551 #[test]
552 fn test_write_e_white_point() {
553 let mut enc = ColorEncoding::srgb();
554 enc.white_point = WhitePoint::E;
555
556 let mut writer = BitWriter::new();
557 enc.write(&mut writer).unwrap();
558 assert!(writer.bits_written() > 0);
559 }
560
561 #[test]
562 fn test_write_dci_white_point() {
563 let mut enc = ColorEncoding::srgb();
564 enc.white_point = WhitePoint::Dci;
565
566 let mut writer = BitWriter::new();
567 enc.write(&mut writer).unwrap();
568 assert!(writer.bits_written() > 0);
569 }
570
571 #[test]
572 fn test_rendering_intent_saturation() {
573 let mut enc = ColorEncoding::srgb();
574 enc.rendering_intent = RenderingIntent::Saturation;
575
576 let mut writer = BitWriter::new();
577 enc.write(&mut writer).unwrap();
578 assert!(writer.bits_written() > 0);
579 }
580
581 #[test]
582 fn test_rendering_intent_absolute() {
583 let mut enc = ColorEncoding::srgb();
584 enc.rendering_intent = RenderingIntent::Absolute;
585
586 let mut writer = BitWriter::new();
587 enc.write(&mut writer).unwrap();
588 assert!(writer.bits_written() > 0);
589 }
590
591 #[test]
592 fn test_xyb_color_space() {
593 let mut enc = ColorEncoding::srgb();
594 enc.color_space = ColorSpace::Xyb;
595
596 let mut writer = BitWriter::new();
597 enc.write(&mut writer).unwrap();
598 assert!(writer.bits_written() > 0);
600 }
601
602 #[test]
603 fn test_unknown_color_space() {
604 let mut enc = ColorEncoding::srgb();
605 enc.color_space = ColorSpace::Unknown;
606
607 let mut writer = BitWriter::new();
608 enc.write(&mut writer).unwrap();
609 assert!(writer.bits_written() > 0);
611 }
612
613 #[test]
614 fn test_default_encoding() {
615 let enc = ColorEncoding::default();
616 assert_eq!(enc.color_space, ColorSpace::Rgb);
617 assert_eq!(enc.white_point, WhitePoint::D65);
618 assert_eq!(enc.primaries, Primaries::Srgb);
619 assert_eq!(enc.transfer_function, TransferFunction::Srgb);
620 assert_eq!(enc.rendering_intent, RenderingIntent::Perceptual);
621 assert!(!enc.want_icc);
622 assert!(enc.gamma.is_none());
623 }
624
625 #[test]
626 fn test_gamma_encoding() {
627 let enc = ColorEncoding::with_gamma(0.45455);
629 assert!(!enc.is_srgb()); assert_eq!(enc.gamma, Some(0.45455));
631
632 let mut writer = BitWriter::new();
633 enc.write(&mut writer).unwrap();
634 writer.zero_pad_to_byte();
635
636 let encoded = (0.45455_f32 * 10_000_000.0).round() as u32;
638 assert_eq!(encoded, 4_545_500);
639
640 assert_eq!(writer.bits_written(), 40); }
645
646 #[test]
647 fn test_gray_with_gamma() {
648 let enc = ColorEncoding::gray_with_gamma(0.45455);
649 assert!(enc.is_gray());
650 assert_eq!(enc.gamma, Some(0.45455));
651 assert!(!enc.is_srgb());
652
653 let mut writer = BitWriter::new();
654 enc.write(&mut writer).unwrap();
655 assert!(writer.bits_written() > 0);
657 }
658}