1use core::fmt;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[non_exhaustive]
18#[repr(u8)]
19pub enum ChannelType {
20 U8 = 1,
22 U16 = 2,
24 F32 = 4,
26 F16 = 5,
28}
29
30impl ChannelType {
31 #[inline]
33 #[allow(unreachable_patterns)]
34 pub const fn byte_size(self) -> usize {
35 match self {
36 Self::U8 => 1,
37 Self::U16 | Self::F16 => 2,
38 Self::F32 => 4,
39 _ => 0,
40 }
41 }
42
43 #[inline]
45 pub const fn is_u8(self) -> bool {
46 matches!(self, Self::U8)
47 }
48
49 #[inline]
51 pub const fn is_u16(self) -> bool {
52 matches!(self, Self::U16)
53 }
54
55 #[inline]
57 pub const fn is_f32(self) -> bool {
58 matches!(self, Self::F32)
59 }
60
61 #[inline]
63 pub const fn is_f16(self) -> bool {
64 matches!(self, Self::F16)
65 }
66
67 #[inline]
69 #[allow(unreachable_patterns)]
70 pub const fn is_integer(self) -> bool {
71 matches!(self, Self::U8 | Self::U16)
72 }
73
74 #[inline]
76 #[allow(unreachable_patterns)]
77 pub const fn is_float(self) -> bool {
78 matches!(self, Self::F32 | Self::F16)
79 }
80}
81
82impl fmt::Display for ChannelType {
83 #[allow(unreachable_patterns)]
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 match self {
86 Self::U8 => f.write_str("U8"),
87 Self::U16 => f.write_str("U16"),
88 Self::F32 => f.write_str("F32"),
89 Self::F16 => f.write_str("F16"),
90 _ => write!(f, "ChannelType({})", *self as u8),
91 }
92 }
93}
94
95#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
101#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
102#[non_exhaustive]
103#[repr(u8)]
104pub enum ChannelLayout {
105 Gray = 1,
107 GrayAlpha = 2,
109 Rgb = 3,
111 Rgba = 4,
113 Bgra = 5,
115 Oklab = 6,
117 OklabA = 7,
119}
120
121impl ChannelLayout {
122 #[inline]
124 #[allow(unreachable_patterns)]
125 pub const fn channels(self) -> usize {
126 match self {
127 Self::Gray => 1,
128 Self::GrayAlpha => 2,
129 Self::Rgb | Self::Oklab => 3,
130 Self::Rgba | Self::Bgra | Self::OklabA => 4,
131 _ => 0,
132 }
133 }
134
135 #[inline]
137 #[allow(unreachable_patterns)]
138 pub const fn has_alpha(self) -> bool {
139 matches!(
140 self,
141 Self::GrayAlpha | Self::Rgba | Self::Bgra | Self::OklabA
142 )
143 }
144}
145
146impl fmt::Display for ChannelLayout {
147 #[allow(unreachable_patterns)]
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 match self {
150 Self::Gray => f.write_str("Gray"),
151 Self::GrayAlpha => f.write_str("GrayAlpha"),
152 Self::Rgb => f.write_str("RGB"),
153 Self::Rgba => f.write_str("RGBA"),
154 Self::Bgra => f.write_str("BGRA"),
155 Self::Oklab => f.write_str("Oklab"),
156 Self::OklabA => f.write_str("OklabA"),
157 _ => write!(f, "ChannelLayout({})", *self as u8),
158 }
159 }
160}
161
162#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
172#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
173#[non_exhaustive]
174#[repr(u8)]
175pub enum AlphaMode {
176 Undefined = 1,
178 Straight = 2,
180 Premultiplied = 3,
182 Opaque = 4,
184}
185
186impl AlphaMode {
187 #[inline]
189 pub const fn has_alpha(self) -> bool {
190 matches!(self, Self::Straight | Self::Premultiplied | Self::Opaque)
191 }
192}
193
194impl fmt::Display for AlphaMode {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 match self {
197 Self::Undefined => f.write_str("undefined"),
198 Self::Straight => f.write_str("straight"),
199 Self::Premultiplied => f.write_str("premultiplied"),
200 Self::Opaque => f.write_str("opaque"),
201 }
202 }
203}
204
205#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
211#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
212#[non_exhaustive]
213#[repr(u8)]
214pub enum TransferFunction {
215 Linear = 0,
217 Srgb = 1,
219 Bt709 = 2,
221 Pq = 3,
223 Hlg = 4,
225 Unknown = 255,
227}
228
229impl TransferFunction {
230 #[inline]
232 pub const fn from_cicp(tc: u8) -> Option<Self> {
233 match tc {
234 1 => Some(Self::Bt709),
235 8 => Some(Self::Linear),
236 13 => Some(Self::Srgb),
237 16 => Some(Self::Pq),
238 18 => Some(Self::Hlg),
239 _ => None,
240 }
241 }
242
243 #[allow(unreachable_patterns)]
245 #[inline]
246 pub const fn to_cicp(self) -> Option<u8> {
247 match self {
248 Self::Bt709 => Some(1),
249 Self::Linear => Some(8),
250 Self::Srgb => Some(13),
251 Self::Pq => Some(16),
252 Self::Hlg => Some(18),
253 Self::Unknown => None,
254 _ => None,
255 }
256 }
257
258 #[allow(unreachable_patterns)]
264 pub fn reference_white_nits(&self) -> f32 {
265 match self {
266 Self::Pq => 203.0,
267 _ => 1.0,
268 }
269 }
270}
271
272impl fmt::Display for TransferFunction {
273 #[allow(unreachable_patterns)]
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 match self {
276 Self::Linear => f.write_str("linear"),
277 Self::Srgb => f.write_str("sRGB"),
278 Self::Bt709 => f.write_str("BT.709"),
279 Self::Pq => f.write_str("PQ"),
280 Self::Hlg => f.write_str("HLG"),
281 Self::Unknown => f.write_str("unknown"),
282 _ => write!(f, "TransferFunction({})", *self as u8),
283 }
284 }
285}
286
287#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
295#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
296#[non_exhaustive]
297#[repr(u8)]
298pub enum ColorPrimaries {
299 #[default]
301 Bt709 = 1,
302 Bt2020 = 9,
304 DisplayP3 = 12,
306 Unknown = 255,
308}
309
310impl ColorPrimaries {
311 #[inline]
313 pub const fn from_cicp(code: u8) -> Option<Self> {
314 match code {
315 1 => Some(Self::Bt709),
316 9 => Some(Self::Bt2020),
317 12 => Some(Self::DisplayP3),
318 _ => None,
319 }
320 }
321
322 #[allow(unreachable_patterns)]
324 #[inline]
325 pub const fn to_cicp(self) -> Option<u8> {
326 match self {
327 Self::Bt709 => Some(1),
328 Self::Bt2020 => Some(9),
329 Self::DisplayP3 => Some(12),
330 Self::Unknown => None,
331 _ => None,
332 }
333 }
334
335 #[inline]
339 pub const fn contains(self, other: Self) -> bool {
340 self.gamut_width() >= other.gamut_width()
341 && !matches!(self, Self::Unknown)
342 && !matches!(other, Self::Unknown)
343 }
344
345 #[allow(unreachable_patterns)]
346 const fn gamut_width(self) -> u8 {
347 match self {
348 Self::Bt709 => 1,
349 Self::DisplayP3 => 2,
350 Self::Bt2020 => 3,
351 Self::Unknown => 0,
352 _ => 0,
353 }
354 }
355}
356
357impl fmt::Display for ColorPrimaries {
358 #[allow(unreachable_patterns)]
359 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360 match self {
361 Self::Bt709 => f.write_str("BT.709"),
362 Self::Bt2020 => f.write_str("BT.2020"),
363 Self::DisplayP3 => f.write_str("Display P3"),
364 Self::Unknown => f.write_str("unknown"),
365 _ => write!(f, "ColorPrimaries({})", *self as u8),
366 }
367 }
368}
369
370#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
376#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
377#[non_exhaustive]
378#[repr(u8)]
379pub enum SignalRange {
380 #[default]
382 Full = 0,
383 Narrow = 1,
385}
386
387impl fmt::Display for SignalRange {
388 #[allow(unreachable_patterns)]
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 match self {
391 Self::Full => f.write_str("full"),
392 Self::Narrow => f.write_str("narrow"),
393 _ => write!(f, "SignalRange({})", *self as u8),
394 }
395 }
396}
397
398#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
407#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
408#[non_exhaustive]
409pub struct PixelDescriptor {
410 pub format: PixelFormat,
412 pub transfer: TransferFunction,
414 pub alpha: Option<AlphaMode>,
416 pub primaries: ColorPrimaries,
418 pub signal_range: SignalRange,
420}
421
422impl PixelDescriptor {
423 #[inline]
427 pub const fn pixel_format(&self) -> PixelFormat {
428 self.format
429 }
430
431 #[inline]
433 pub const fn channel_type(&self) -> ChannelType {
434 self.format.channel_type()
435 }
436
437 #[inline]
439 pub const fn alpha(&self) -> Option<AlphaMode> {
440 self.alpha
441 }
442
443 #[inline]
445 pub const fn transfer(&self) -> TransferFunction {
446 self.transfer
447 }
448
449 #[inline]
451 pub const fn byte_order(&self) -> ByteOrder {
452 self.format.byte_order()
453 }
454
455 #[inline]
457 pub const fn color_model(&self) -> ColorModel {
458 self.format.color_model()
459 }
460
461 #[inline]
463 pub const fn layout(&self) -> ChannelLayout {
464 self.format.layout()
465 }
466
467 #[inline]
476 pub const fn new(
477 channel_type: ChannelType,
478 layout: ChannelLayout,
479 alpha: Option<AlphaMode>,
480 transfer: TransferFunction,
481 ) -> Self {
482 let format = match PixelFormat::from_parts(channel_type, layout, alpha) {
483 Some(f) => f,
484 None => panic!("unsupported PixelFormat combination"),
485 };
486 Self {
487 format,
488 transfer,
489 alpha,
490 primaries: ColorPrimaries::Bt709,
491 signal_range: SignalRange::Full,
492 }
493 }
494
495 #[inline]
502 pub const fn new_full(
503 channel_type: ChannelType,
504 layout: ChannelLayout,
505 alpha: Option<AlphaMode>,
506 transfer: TransferFunction,
507 primaries: ColorPrimaries,
508 ) -> Self {
509 let format = match PixelFormat::from_parts(channel_type, layout, alpha) {
510 Some(f) => f,
511 None => panic!("unsupported PixelFormat combination"),
512 };
513 Self {
514 format,
515 transfer,
516 alpha,
517 primaries,
518 signal_range: SignalRange::Full,
519 }
520 }
521
522 #[inline]
525 pub const fn from_pixel_format(format: PixelFormat) -> Self {
526 Self {
527 format,
528 transfer: TransferFunction::Unknown,
529 alpha: format.default_alpha(),
530 primaries: ColorPrimaries::Bt709,
531 signal_range: SignalRange::Full,
532 }
533 }
534
535 pub const RGB8_SRGB: Self = Self::new(
539 ChannelType::U8,
540 ChannelLayout::Rgb,
541 None,
542 TransferFunction::Srgb,
543 );
544 pub const RGBA8_SRGB: Self = Self::new(
546 ChannelType::U8,
547 ChannelLayout::Rgba,
548 Some(AlphaMode::Straight),
549 TransferFunction::Srgb,
550 );
551 pub const RGB16_SRGB: Self = Self::new(
553 ChannelType::U16,
554 ChannelLayout::Rgb,
555 None,
556 TransferFunction::Srgb,
557 );
558 pub const RGBA16_SRGB: Self = Self::new(
560 ChannelType::U16,
561 ChannelLayout::Rgba,
562 Some(AlphaMode::Straight),
563 TransferFunction::Srgb,
564 );
565 pub const RGBF32_LINEAR: Self = Self::new(
567 ChannelType::F32,
568 ChannelLayout::Rgb,
569 None,
570 TransferFunction::Linear,
571 );
572 pub const RGBAF32_LINEAR: Self = Self::new(
574 ChannelType::F32,
575 ChannelLayout::Rgba,
576 Some(AlphaMode::Straight),
577 TransferFunction::Linear,
578 );
579 pub const GRAY8_SRGB: Self = Self::new(
581 ChannelType::U8,
582 ChannelLayout::Gray,
583 None,
584 TransferFunction::Srgb,
585 );
586 pub const GRAY16_SRGB: Self = Self::new(
588 ChannelType::U16,
589 ChannelLayout::Gray,
590 None,
591 TransferFunction::Srgb,
592 );
593 pub const GRAYF32_LINEAR: Self = Self::new(
595 ChannelType::F32,
596 ChannelLayout::Gray,
597 None,
598 TransferFunction::Linear,
599 );
600 pub const GRAYA8_SRGB: Self = Self::new(
602 ChannelType::U8,
603 ChannelLayout::GrayAlpha,
604 Some(AlphaMode::Straight),
605 TransferFunction::Srgb,
606 );
607 pub const GRAYA16_SRGB: Self = Self::new(
609 ChannelType::U16,
610 ChannelLayout::GrayAlpha,
611 Some(AlphaMode::Straight),
612 TransferFunction::Srgb,
613 );
614 pub const GRAYAF32_LINEAR: Self = Self::new(
616 ChannelType::F32,
617 ChannelLayout::GrayAlpha,
618 Some(AlphaMode::Straight),
619 TransferFunction::Linear,
620 );
621 pub const BGRA8_SRGB: Self = Self::new(
623 ChannelType::U8,
624 ChannelLayout::Bgra,
625 Some(AlphaMode::Straight),
626 TransferFunction::Srgb,
627 );
628 pub const RGBX8_SRGB: Self = Self::new(
630 ChannelType::U8,
631 ChannelLayout::Rgba,
632 Some(AlphaMode::Undefined),
633 TransferFunction::Srgb,
634 );
635 pub const BGRX8_SRGB: Self = Self::new(
637 ChannelType::U8,
638 ChannelLayout::Bgra,
639 Some(AlphaMode::Undefined),
640 TransferFunction::Srgb,
641 );
642
643 pub const RGB8: Self = Self::new(
647 ChannelType::U8,
648 ChannelLayout::Rgb,
649 None,
650 TransferFunction::Unknown,
651 );
652 pub const RGBA8: Self = Self::new(
654 ChannelType::U8,
655 ChannelLayout::Rgba,
656 Some(AlphaMode::Straight),
657 TransferFunction::Unknown,
658 );
659 pub const RGB16: Self = Self::new(
661 ChannelType::U16,
662 ChannelLayout::Rgb,
663 None,
664 TransferFunction::Unknown,
665 );
666 pub const RGBA16: Self = Self::new(
668 ChannelType::U16,
669 ChannelLayout::Rgba,
670 Some(AlphaMode::Straight),
671 TransferFunction::Unknown,
672 );
673 pub const RGBF32: Self = Self::new(
675 ChannelType::F32,
676 ChannelLayout::Rgb,
677 None,
678 TransferFunction::Unknown,
679 );
680 pub const RGBAF32: Self = Self::new(
682 ChannelType::F32,
683 ChannelLayout::Rgba,
684 Some(AlphaMode::Straight),
685 TransferFunction::Unknown,
686 );
687 pub const GRAY8: Self = Self::new(
689 ChannelType::U8,
690 ChannelLayout::Gray,
691 None,
692 TransferFunction::Unknown,
693 );
694 pub const GRAY16: Self = Self::new(
696 ChannelType::U16,
697 ChannelLayout::Gray,
698 None,
699 TransferFunction::Unknown,
700 );
701 pub const GRAYF32: Self = Self::new(
703 ChannelType::F32,
704 ChannelLayout::Gray,
705 None,
706 TransferFunction::Unknown,
707 );
708 pub const GRAYA8: Self = Self::new(
710 ChannelType::U8,
711 ChannelLayout::GrayAlpha,
712 Some(AlphaMode::Straight),
713 TransferFunction::Unknown,
714 );
715 pub const GRAYA16: Self = Self::new(
717 ChannelType::U16,
718 ChannelLayout::GrayAlpha,
719 Some(AlphaMode::Straight),
720 TransferFunction::Unknown,
721 );
722 pub const GRAYAF32: Self = Self::new(
724 ChannelType::F32,
725 ChannelLayout::GrayAlpha,
726 Some(AlphaMode::Straight),
727 TransferFunction::Unknown,
728 );
729 pub const BGRA8: Self = Self::new(
731 ChannelType::U8,
732 ChannelLayout::Bgra,
733 Some(AlphaMode::Straight),
734 TransferFunction::Unknown,
735 );
736 pub const RGBX8: Self = Self::new(
738 ChannelType::U8,
739 ChannelLayout::Rgba,
740 Some(AlphaMode::Undefined),
741 TransferFunction::Unknown,
742 );
743 pub const BGRX8: Self = Self::new(
745 ChannelType::U8,
746 ChannelLayout::Bgra,
747 Some(AlphaMode::Undefined),
748 TransferFunction::Unknown,
749 );
750
751 pub const OKLABF32: Self = Self {
755 format: PixelFormat::OklabF32,
756 transfer: TransferFunction::Unknown,
757 alpha: None,
758 primaries: ColorPrimaries::Bt709,
759 signal_range: SignalRange::Full,
760 };
761 pub const OKLABAF32: Self = Self {
763 format: PixelFormat::OklabaF32,
764 transfer: TransferFunction::Unknown,
765 alpha: Some(AlphaMode::Straight),
766 primaries: ColorPrimaries::Bt709,
767 signal_range: SignalRange::Full,
768 };
769
770 #[inline]
774 pub const fn channels(self) -> usize {
775 self.format.channels()
776 }
777
778 #[inline]
780 pub const fn bytes_per_pixel(self) -> usize {
781 self.format.bytes_per_pixel()
782 }
783
784 #[inline]
786 pub const fn has_alpha(self) -> bool {
787 matches!(
788 self.alpha,
789 Some(AlphaMode::Straight) | Some(AlphaMode::Premultiplied) | Some(AlphaMode::Opaque)
790 )
791 }
792
793 #[inline]
795 pub const fn is_grayscale(self) -> bool {
796 self.format.is_grayscale()
797 }
798
799 #[inline]
801 pub const fn is_bgr(self) -> bool {
802 matches!(self.format.byte_order(), ByteOrder::Bgr)
803 }
804
805 #[inline]
807 #[must_use]
808 pub const fn with_transfer(self, transfer: TransferFunction) -> Self {
809 Self { transfer, ..self }
810 }
811
812 #[inline]
814 #[must_use]
815 pub const fn with_primaries(self, primaries: ColorPrimaries) -> Self {
816 Self { primaries, ..self }
817 }
818
819 #[inline]
821 #[must_use]
822 pub const fn with_alpha(self, alpha: Option<AlphaMode>) -> Self {
823 Self { alpha, ..self }
824 }
825
826 #[inline]
828 #[must_use]
829 pub const fn with_alpha_mode(self, alpha: Option<AlphaMode>) -> Self {
830 self.with_alpha(alpha)
831 }
832
833 #[inline]
835 #[must_use]
836 pub const fn with_signal_range(self, signal_range: SignalRange) -> Self {
837 Self {
838 signal_range,
839 ..self
840 }
841 }
842
843 #[inline]
848 pub const fn is_opaque(self) -> bool {
849 matches!(
850 self.alpha,
851 None | Some(AlphaMode::Undefined | AlphaMode::Opaque)
852 )
853 }
854
855 #[inline]
860 #[allow(unreachable_patterns)]
861 pub const fn may_have_transparency(self) -> bool {
862 matches!(
863 self.alpha,
864 Some(AlphaMode::Straight | AlphaMode::Premultiplied)
865 )
866 }
867
868 #[inline]
870 pub const fn is_linear(self) -> bool {
871 matches!(self.transfer, TransferFunction::Linear)
872 }
873
874 #[inline]
876 pub const fn is_unknown_transfer(self) -> bool {
877 matches!(self.transfer, TransferFunction::Unknown)
878 }
879
880 #[inline]
882 pub const fn min_alignment(self) -> usize {
883 self.format.channel_type().byte_size()
884 }
885
886 #[inline]
888 pub const fn aligned_stride(self, width: u32) -> usize {
889 width as usize * self.bytes_per_pixel()
890 }
891
892 #[inline]
898 pub const fn simd_aligned_stride(self, width: u32, simd_align: usize) -> usize {
899 let bpp = self.bytes_per_pixel();
900 let raw = width as usize * bpp;
901 let align = lcm(bpp, simd_align);
902 align_up_general(raw, align)
903 }
904
905 #[inline]
910 pub const fn layout_compatible(self, other: Self) -> bool {
911 self.format.channel_type() as u8 == other.format.channel_type() as u8
912 && self.layout() as u8 == other.layout() as u8
913 }
914}
915
916const fn gcd(mut a: usize, mut b: usize) -> usize {
919 while b != 0 {
920 let t = b;
921 b = a % b;
922 a = t;
923 }
924 a
925}
926
927const fn lcm(a: usize, b: usize) -> usize {
928 if a == 0 || b == 0 {
929 0
930 } else {
931 a / gcd(a, b) * b
932 }
933}
934
935const fn align_up_general(value: usize, align: usize) -> usize {
936 if align == 0 {
937 return value;
938 }
939 let rem = value % align;
940 if rem == 0 { value } else { value + align - rem }
941}
942
943impl fmt::Display for PixelDescriptor {
944 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945 write!(
946 f,
947 "{} {} {}",
948 self.format,
949 self.format.channel_type(),
950 self.transfer
951 )?;
952 if let Some(alpha) = self.alpha
953 && alpha.has_alpha()
954 {
955 write!(f, " alpha={alpha}")?;
956 }
957 Ok(())
958 }
959}
960
961#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
967#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
968#[non_exhaustive]
969#[repr(u8)]
970pub enum ColorModel {
971 Gray = 0,
973 Rgb = 1,
975 YCbCr = 2,
977 Oklab = 3,
979}
980
981impl ColorModel {
982 #[inline]
984 #[allow(unreachable_patterns)]
985 pub const fn color_channels(self) -> u8 {
986 match self {
987 Self::Gray => 1,
988 _ => 3,
989 }
990 }
991}
992
993impl fmt::Display for ColorModel {
994 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
995 match self {
996 Self::Gray => f.write_str("Gray"),
997 Self::Rgb => f.write_str("RGB"),
998 Self::YCbCr => f.write_str("YCbCr"),
999 Self::Oklab => f.write_str("Oklab"),
1000 }
1001 }
1002}
1003
1004#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
1010#[non_exhaustive]
1011#[repr(u8)]
1012pub enum ByteOrder {
1013 #[default]
1015 Native = 0,
1016 Bgr = 1,
1018}
1019
1020impl fmt::Display for ByteOrder {
1021 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1022 match self {
1023 Self::Native => f.write_str("native"),
1024 Self::Bgr => f.write_str("BGR"),
1025 }
1026 }
1027}
1028
1029#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1042#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1043#[non_exhaustive]
1044#[repr(u8)]
1045pub enum PixelFormat {
1046 Rgb8 = 1,
1047 Rgba8 = 2,
1048 Rgb16 = 3,
1049 Rgba16 = 4,
1050 RgbF32 = 5,
1051 RgbaF32 = 6,
1052 Gray8 = 7,
1053 Gray16 = 8,
1054 GrayF32 = 9,
1055 GrayA8 = 10,
1056 GrayA16 = 11,
1057 GrayAF32 = 12,
1058 Bgra8 = 13,
1059 Rgbx8 = 14,
1060 Bgrx8 = 15,
1061 OklabF32 = 16,
1062 OklabaF32 = 17,
1063}
1064
1065impl PixelFormat {
1066 #[inline]
1068 #[allow(unreachable_patterns)]
1069 pub const fn channel_type(self) -> ChannelType {
1070 match self {
1071 Self::Rgb8
1072 | Self::Rgba8
1073 | Self::Gray8
1074 | Self::GrayA8
1075 | Self::Bgra8
1076 | Self::Rgbx8
1077 | Self::Bgrx8 => ChannelType::U8,
1078 Self::Rgb16 | Self::Rgba16 | Self::Gray16 | Self::GrayA16 => ChannelType::U16,
1079 Self::RgbF32
1080 | Self::RgbaF32
1081 | Self::GrayF32
1082 | Self::GrayAF32
1083 | Self::OklabF32
1084 | Self::OklabaF32 => ChannelType::F32,
1085 _ => ChannelType::U8,
1086 }
1087 }
1088
1089 #[inline]
1091 #[allow(unreachable_patterns)]
1092 pub const fn layout(self) -> ChannelLayout {
1093 match self {
1094 Self::Rgb8 | Self::Rgb16 | Self::RgbF32 => ChannelLayout::Rgb,
1095 Self::Rgba8 | Self::Rgba16 | Self::RgbaF32 | Self::Rgbx8 => ChannelLayout::Rgba,
1096 Self::Gray8 | Self::Gray16 | Self::GrayF32 => ChannelLayout::Gray,
1097 Self::GrayA8 | Self::GrayA16 | Self::GrayAF32 => ChannelLayout::GrayAlpha,
1098 Self::Bgra8 | Self::Bgrx8 => ChannelLayout::Bgra,
1099 Self::OklabF32 => ChannelLayout::Oklab,
1100 Self::OklabaF32 => ChannelLayout::OklabA,
1101 _ => ChannelLayout::Rgb,
1102 }
1103 }
1104
1105 #[inline]
1107 #[allow(unreachable_patterns)]
1108 pub const fn color_model(self) -> ColorModel {
1109 match self {
1110 Self::Gray8
1111 | Self::Gray16
1112 | Self::GrayF32
1113 | Self::GrayA8
1114 | Self::GrayA16
1115 | Self::GrayAF32 => ColorModel::Gray,
1116 Self::OklabF32 | Self::OklabaF32 => ColorModel::Oklab,
1117 _ => ColorModel::Rgb,
1118 }
1119 }
1120
1121 #[inline]
1123 #[allow(unreachable_patterns)]
1124 pub const fn byte_order(self) -> ByteOrder {
1125 match self {
1126 Self::Bgra8 | Self::Bgrx8 => ByteOrder::Bgr,
1127 _ => ByteOrder::Native,
1128 }
1129 }
1130
1131 #[inline]
1133 pub const fn channels(self) -> usize {
1134 self.layout().channels()
1135 }
1136
1137 #[inline]
1139 pub const fn bytes_per_pixel(self) -> usize {
1140 self.channels() * self.channel_type().byte_size()
1141 }
1142
1143 #[inline]
1145 pub const fn has_alpha_bytes(self) -> bool {
1146 self.layout().has_alpha()
1147 }
1148
1149 #[inline]
1151 pub const fn is_grayscale(self) -> bool {
1152 matches!(self.color_model(), ColorModel::Gray)
1153 }
1154
1155 #[allow(unreachable_patterns)]
1161 #[inline]
1162 pub const fn default_alpha(self) -> Option<AlphaMode> {
1163 match self {
1164 Self::Rgb8
1165 | Self::Rgb16
1166 | Self::RgbF32
1167 | Self::Gray8
1168 | Self::Gray16
1169 | Self::GrayF32
1170 | Self::OklabF32 => None,
1171 Self::Rgbx8 | Self::Bgrx8 => Some(AlphaMode::Undefined),
1172 _ => Some(AlphaMode::Straight),
1173 }
1174 }
1175
1176 #[allow(unreachable_patterns)]
1178 #[inline]
1179 pub const fn name(self) -> &'static str {
1180 match self {
1181 Self::Rgb8 => "RGB8",
1182 Self::Rgba8 => "RGBA8",
1183 Self::Rgb16 => "RGB16",
1184 Self::Rgba16 => "RGBA16",
1185 Self::RgbF32 => "RgbF32",
1186 Self::RgbaF32 => "RgbaF32",
1187 Self::Gray8 => "Gray8",
1188 Self::Gray16 => "Gray16",
1189 Self::GrayF32 => "GrayF32",
1190 Self::GrayA8 => "GrayA8",
1191 Self::GrayA16 => "GrayA16",
1192 Self::GrayAF32 => "GrayAF32",
1193 Self::Bgra8 => "BGRA8",
1194 Self::Rgbx8 => "RGBX8",
1195 Self::Bgrx8 => "BGRX8",
1196 Self::OklabF32 => "OklabF32",
1197 Self::OklabaF32 => "OklabaF32",
1198 _ => "Unknown",
1199 }
1200 }
1201
1202 #[inline]
1207 pub const fn from_parts(
1208 channel_type: ChannelType,
1209 layout: ChannelLayout,
1210 alpha: Option<AlphaMode>,
1211 ) -> Option<Self> {
1212 let is_padding = matches!(alpha, Some(AlphaMode::Undefined));
1213 match (channel_type, layout, is_padding) {
1214 (ChannelType::U8, ChannelLayout::Rgb, _) => Some(Self::Rgb8),
1215 (ChannelType::U16, ChannelLayout::Rgb, _) => Some(Self::Rgb16),
1216 (ChannelType::F32, ChannelLayout::Rgb, _) => Some(Self::RgbF32),
1217
1218 (ChannelType::U8, ChannelLayout::Rgba, true) => Some(Self::Rgbx8),
1219 (ChannelType::U8, ChannelLayout::Rgba, false) => Some(Self::Rgba8),
1220 (ChannelType::U16, ChannelLayout::Rgba, _) => Some(Self::Rgba16),
1221 (ChannelType::F32, ChannelLayout::Rgba, _) => Some(Self::RgbaF32),
1222
1223 (ChannelType::U8, ChannelLayout::Gray, _) => Some(Self::Gray8),
1224 (ChannelType::U16, ChannelLayout::Gray, _) => Some(Self::Gray16),
1225 (ChannelType::F32, ChannelLayout::Gray, _) => Some(Self::GrayF32),
1226
1227 (ChannelType::U8, ChannelLayout::GrayAlpha, _) => Some(Self::GrayA8),
1228 (ChannelType::U16, ChannelLayout::GrayAlpha, _) => Some(Self::GrayA16),
1229 (ChannelType::F32, ChannelLayout::GrayAlpha, _) => Some(Self::GrayAF32),
1230
1231 (ChannelType::U8, ChannelLayout::Bgra, true) => Some(Self::Bgrx8),
1232 (ChannelType::U8, ChannelLayout::Bgra, false) => Some(Self::Bgra8),
1233
1234 (ChannelType::F32, ChannelLayout::Oklab, _) => Some(Self::OklabF32),
1235 (ChannelType::F32, ChannelLayout::OklabA, _) => Some(Self::OklabaF32),
1236
1237 _ => None,
1238 }
1239 }
1240
1241 #[allow(unreachable_patterns)]
1243 #[inline]
1244 pub const fn descriptor(self) -> PixelDescriptor {
1245 match self {
1246 Self::Rgb8 => PixelDescriptor::RGB8,
1247 Self::Rgba8 => PixelDescriptor::RGBA8,
1248 Self::Rgb16 => PixelDescriptor::RGB16,
1249 Self::Rgba16 => PixelDescriptor::RGBA16,
1250 Self::RgbF32 => PixelDescriptor::RGBF32,
1251 Self::RgbaF32 => PixelDescriptor::RGBAF32,
1252 Self::Gray8 => PixelDescriptor::GRAY8,
1253 Self::Gray16 => PixelDescriptor::GRAY16,
1254 Self::GrayF32 => PixelDescriptor::GRAYF32,
1255 Self::GrayA8 => PixelDescriptor::GRAYA8,
1256 Self::GrayA16 => PixelDescriptor::GRAYA16,
1257 Self::GrayAF32 => PixelDescriptor::GRAYAF32,
1258 Self::Bgra8 => PixelDescriptor::BGRA8,
1259 Self::Rgbx8 => PixelDescriptor::RGBX8,
1260 Self::Bgrx8 => PixelDescriptor::BGRX8,
1261 Self::OklabF32 => PixelDescriptor::OKLABF32,
1262 Self::OklabaF32 => PixelDescriptor::OKLABAF32,
1263 _ => PixelDescriptor::RGB8,
1264 }
1265 }
1266}
1267
1268impl fmt::Display for PixelFormat {
1269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1270 f.write_str(self.name())
1271 }
1272}
1273
1274#[cfg(test)]
1279mod tests {
1280 use alloc::format;
1281 use core::mem::size_of;
1282
1283 use super::*;
1284
1285 #[test]
1286 fn channel_type_byte_size() {
1287 assert_eq!(ChannelType::U8.byte_size(), 1);
1288 assert_eq!(ChannelType::U16.byte_size(), 2);
1289 assert_eq!(ChannelType::F16.byte_size(), 2);
1290 assert_eq!(ChannelType::F32.byte_size(), 4);
1291 }
1292
1293 #[test]
1294 fn descriptor_bytes_per_pixel() {
1295 assert_eq!(PixelDescriptor::RGB8.bytes_per_pixel(), 3);
1296 assert_eq!(PixelDescriptor::RGBA8.bytes_per_pixel(), 4);
1297 assert_eq!(PixelDescriptor::GRAY8.bytes_per_pixel(), 1);
1298 assert_eq!(PixelDescriptor::RGBAF32.bytes_per_pixel(), 16);
1299 assert_eq!(PixelDescriptor::GRAYA8.bytes_per_pixel(), 2);
1300 }
1301
1302 #[test]
1303 fn descriptor_has_alpha() {
1304 assert!(!PixelDescriptor::RGB8.has_alpha());
1305 assert!(PixelDescriptor::RGBA8.has_alpha());
1306 assert!(!PixelDescriptor::RGBX8.has_alpha());
1307 assert!(PixelDescriptor::GRAYA8.has_alpha());
1308 }
1309
1310 #[test]
1311 fn descriptor_is_grayscale() {
1312 assert!(PixelDescriptor::GRAY8.is_grayscale());
1313 assert!(PixelDescriptor::GRAYA8.is_grayscale());
1314 assert!(!PixelDescriptor::RGB8.is_grayscale());
1315 }
1316
1317 #[test]
1318 fn layout_compatible() {
1319 assert!(PixelDescriptor::RGB8_SRGB.layout_compatible(PixelDescriptor::RGB8));
1320 assert!(!PixelDescriptor::RGB8.layout_compatible(PixelDescriptor::RGBA8));
1321 }
1322
1323 #[test]
1324 fn pixel_format_descriptor_roundtrip() {
1325 let desc = PixelFormat::Rgba8.descriptor();
1326 assert_eq!(desc.layout(), ChannelLayout::Rgba);
1327 assert_eq!(desc.channel_type(), ChannelType::U8);
1328 }
1329
1330 #[test]
1331 fn pixel_format_enum_basics() {
1332 assert_eq!(PixelFormat::Rgb8.channels(), 3);
1333 assert_eq!(PixelFormat::Rgba8.channels(), 4);
1334 assert!(PixelFormat::Rgba8.has_alpha_bytes());
1335 assert!(!PixelFormat::Rgb8.has_alpha_bytes());
1336 assert_eq!(PixelFormat::RgbF32.bytes_per_pixel(), 12);
1337 assert_eq!(PixelFormat::RgbaF32.bytes_per_pixel(), 16);
1338 assert_eq!(PixelFormat::Gray8.channels(), 1);
1339 assert!(PixelFormat::Gray8.is_grayscale());
1340 assert!(!PixelFormat::Rgb8.is_grayscale());
1341 assert_eq!(PixelFormat::Bgra8.byte_order(), ByteOrder::Bgr);
1342 assert_eq!(PixelFormat::Rgb8.byte_order(), ByteOrder::Native);
1343 }
1344
1345 #[test]
1346 fn pixel_format_enum_size() {
1347 assert!(size_of::<PixelFormat>() <= 2);
1349 }
1350
1351 #[test]
1352 fn pixel_format_from_parts_roundtrip() {
1353 let fmt = PixelFormat::Rgba8;
1354 let rebuilt =
1355 PixelFormat::from_parts(fmt.channel_type(), fmt.layout(), fmt.default_alpha());
1356 assert_eq!(rebuilt, Some(fmt));
1357
1358 let fmt2 = PixelFormat::Bgra8;
1359 let rebuilt2 =
1360 PixelFormat::from_parts(fmt2.channel_type(), fmt2.layout(), fmt2.default_alpha());
1361 assert_eq!(rebuilt2, Some(fmt2));
1362
1363 let fmt3 = PixelFormat::Gray8;
1364 let rebuilt3 =
1365 PixelFormat::from_parts(fmt3.channel_type(), fmt3.layout(), fmt3.default_alpha());
1366 assert_eq!(rebuilt3, Some(fmt3));
1367 }
1368
1369 #[test]
1370 fn alpha_mode_semantics() {
1371 assert!(!PixelDescriptor::RGB8.has_alpha());
1373 assert!(!AlphaMode::Undefined.has_alpha());
1375 assert!(AlphaMode::Straight.has_alpha());
1377 assert!(AlphaMode::Premultiplied.has_alpha());
1378 assert!(AlphaMode::Opaque.has_alpha());
1379 }
1380
1381 #[test]
1382 fn color_primaries_containment() {
1383 assert!(ColorPrimaries::Bt2020.contains(ColorPrimaries::DisplayP3));
1384 assert!(ColorPrimaries::Bt2020.contains(ColorPrimaries::Bt709));
1385 assert!(ColorPrimaries::DisplayP3.contains(ColorPrimaries::Bt709));
1386 assert!(!ColorPrimaries::Bt709.contains(ColorPrimaries::DisplayP3));
1387 assert!(!ColorPrimaries::Unknown.contains(ColorPrimaries::Bt709));
1388 }
1389
1390 #[test]
1391 fn descriptor_size() {
1392 assert!(size_of::<PixelDescriptor>() <= 8);
1394 }
1395
1396 #[test]
1397 fn color_model_channels() {
1398 assert_eq!(ColorModel::Gray.color_channels(), 1);
1399 assert_eq!(ColorModel::Rgb.color_channels(), 3);
1400 assert_eq!(ColorModel::YCbCr.color_channels(), 3);
1401 assert_eq!(ColorModel::Oklab.color_channels(), 3);
1402 }
1403
1404 #[test]
1417 fn reference_white_nits_values() {
1418 assert_eq!(TransferFunction::Pq.reference_white_nits(), 203.0);
1419 assert_eq!(TransferFunction::Srgb.reference_white_nits(), 1.0);
1420 assert_eq!(TransferFunction::Hlg.reference_white_nits(), 1.0);
1421 assert_eq!(TransferFunction::Linear.reference_white_nits(), 1.0);
1422 assert_eq!(TransferFunction::Unknown.reference_white_nits(), 1.0);
1423 }
1424
1425 #[test]
1430 fn channel_type_display() {
1431 assert_eq!(format!("{}", ChannelType::U8), "U8");
1432 assert_eq!(format!("{}", ChannelType::U16), "U16");
1433 assert_eq!(format!("{}", ChannelType::F32), "F32");
1434 assert_eq!(format!("{}", ChannelType::F16), "F16");
1435 }
1436
1437 #[test]
1438 fn channel_layout_display() {
1439 assert_eq!(format!("{}", ChannelLayout::Gray), "Gray");
1440 assert_eq!(format!("{}", ChannelLayout::GrayAlpha), "GrayAlpha");
1441 assert_eq!(format!("{}", ChannelLayout::Rgb), "RGB");
1442 assert_eq!(format!("{}", ChannelLayout::Rgba), "RGBA");
1443 assert_eq!(format!("{}", ChannelLayout::Bgra), "BGRA");
1444 assert_eq!(format!("{}", ChannelLayout::Oklab), "Oklab");
1445 assert_eq!(format!("{}", ChannelLayout::OklabA), "OklabA");
1446 }
1447
1448 #[test]
1449 fn alpha_mode_display() {
1450 assert_eq!(format!("{}", AlphaMode::Undefined), "undefined");
1451 assert_eq!(format!("{}", AlphaMode::Straight), "straight");
1452 assert_eq!(format!("{}", AlphaMode::Premultiplied), "premultiplied");
1453 assert_eq!(format!("{}", AlphaMode::Opaque), "opaque");
1454 }
1455
1456 #[test]
1457 fn transfer_function_display() {
1458 assert_eq!(format!("{}", TransferFunction::Linear), "linear");
1459 assert_eq!(format!("{}", TransferFunction::Srgb), "sRGB");
1460 assert_eq!(format!("{}", TransferFunction::Bt709), "BT.709");
1461 assert_eq!(format!("{}", TransferFunction::Pq), "PQ");
1462 assert_eq!(format!("{}", TransferFunction::Hlg), "HLG");
1463 assert_eq!(format!("{}", TransferFunction::Unknown), "unknown");
1464 }
1465
1466 #[test]
1467 fn color_primaries_display() {
1468 assert_eq!(format!("{}", ColorPrimaries::Bt709), "BT.709");
1469 assert_eq!(format!("{}", ColorPrimaries::Bt2020), "BT.2020");
1470 assert_eq!(format!("{}", ColorPrimaries::DisplayP3), "Display P3");
1471 assert_eq!(format!("{}", ColorPrimaries::Unknown), "unknown");
1472 }
1473
1474 #[test]
1475 fn signal_range_display() {
1476 assert_eq!(format!("{}", SignalRange::Full), "full");
1477 assert_eq!(format!("{}", SignalRange::Narrow), "narrow");
1478 }
1479
1480 #[test]
1481 fn pixel_descriptor_display() {
1482 let s = format!("{}", PixelDescriptor::RGB8_SRGB);
1483 assert!(s.contains("U8"), "expected U8 in: {s}");
1484 assert!(s.contains("sRGB"), "expected sRGB in: {s}");
1485
1486 let s = format!("{}", PixelDescriptor::RGBA8_SRGB);
1487 assert!(s.contains("alpha=straight"), "expected alpha in: {s}");
1488 }
1489
1490 #[test]
1491 fn pixel_format_display() {
1492 let s = format!("{}", PixelFormat::Rgb8);
1493 assert!(s.contains("RGB8"));
1494 let s = format!("{}", PixelFormat::Bgra8);
1495 assert!(s.contains("BGRA8"));
1496 }
1497
1498 #[test]
1501 fn transfer_function_from_cicp() {
1502 assert_eq!(
1503 TransferFunction::from_cicp(1),
1504 Some(TransferFunction::Bt709)
1505 );
1506 assert_eq!(
1507 TransferFunction::from_cicp(8),
1508 Some(TransferFunction::Linear)
1509 );
1510 assert_eq!(
1511 TransferFunction::from_cicp(13),
1512 Some(TransferFunction::Srgb)
1513 );
1514 assert_eq!(TransferFunction::from_cicp(16), Some(TransferFunction::Pq));
1515 assert_eq!(TransferFunction::from_cicp(18), Some(TransferFunction::Hlg));
1516 assert_eq!(TransferFunction::from_cicp(99), None);
1517 }
1518
1519 #[test]
1520 fn transfer_function_to_cicp() {
1521 assert_eq!(TransferFunction::Bt709.to_cicp(), Some(1));
1522 assert_eq!(TransferFunction::Linear.to_cicp(), Some(8));
1523 assert_eq!(TransferFunction::Srgb.to_cicp(), Some(13));
1524 assert_eq!(TransferFunction::Pq.to_cicp(), Some(16));
1525 assert_eq!(TransferFunction::Hlg.to_cicp(), Some(18));
1526 assert_eq!(TransferFunction::Unknown.to_cicp(), None);
1527 }
1528
1529 #[test]
1530 fn transfer_function_cicp_roundtrip() {
1531 for tf in [
1532 TransferFunction::Bt709,
1533 TransferFunction::Linear,
1534 TransferFunction::Srgb,
1535 TransferFunction::Pq,
1536 TransferFunction::Hlg,
1537 ] {
1538 let code = tf.to_cicp().unwrap();
1539 assert_eq!(TransferFunction::from_cicp(code), Some(tf));
1540 }
1541 }
1542
1543 #[test]
1544 fn color_primaries_from_cicp() {
1545 assert_eq!(ColorPrimaries::from_cicp(1), Some(ColorPrimaries::Bt709));
1546 assert_eq!(ColorPrimaries::from_cicp(9), Some(ColorPrimaries::Bt2020));
1547 assert_eq!(
1548 ColorPrimaries::from_cicp(12),
1549 Some(ColorPrimaries::DisplayP3)
1550 );
1551 assert_eq!(ColorPrimaries::from_cicp(99), None);
1552 }
1553
1554 #[test]
1555 fn color_primaries_to_cicp() {
1556 assert_eq!(ColorPrimaries::Bt709.to_cicp(), Some(1));
1557 assert_eq!(ColorPrimaries::Bt2020.to_cicp(), Some(9));
1558 assert_eq!(ColorPrimaries::DisplayP3.to_cicp(), Some(12));
1559 assert_eq!(ColorPrimaries::Unknown.to_cicp(), None);
1560 }
1561
1562 #[test]
1565 fn channel_type_helpers() {
1566 assert!(ChannelType::U8.is_u8());
1567 assert!(!ChannelType::U8.is_u16());
1568 assert!(ChannelType::U16.is_u16());
1569 assert!(ChannelType::F32.is_f32());
1570 assert!(ChannelType::F16.is_f16());
1571 assert!(ChannelType::U8.is_integer());
1572 assert!(ChannelType::U16.is_integer());
1573 assert!(!ChannelType::F32.is_integer());
1574 assert!(ChannelType::F32.is_float());
1575 assert!(ChannelType::F16.is_float());
1576 assert!(!ChannelType::U8.is_float());
1577 }
1578
1579 #[test]
1582 fn channel_layout_channels() {
1583 assert_eq!(ChannelLayout::Gray.channels(), 1);
1584 assert_eq!(ChannelLayout::GrayAlpha.channels(), 2);
1585 assert_eq!(ChannelLayout::Rgb.channels(), 3);
1586 assert_eq!(ChannelLayout::Rgba.channels(), 4);
1587 assert_eq!(ChannelLayout::Bgra.channels(), 4);
1588 assert_eq!(ChannelLayout::Oklab.channels(), 3);
1589 assert_eq!(ChannelLayout::OklabA.channels(), 4);
1590 }
1591
1592 #[test]
1593 fn channel_layout_has_alpha() {
1594 assert!(!ChannelLayout::Gray.has_alpha());
1595 assert!(ChannelLayout::GrayAlpha.has_alpha());
1596 assert!(!ChannelLayout::Rgb.has_alpha());
1597 assert!(ChannelLayout::Rgba.has_alpha());
1598 assert!(ChannelLayout::Bgra.has_alpha());
1599 assert!(!ChannelLayout::Oklab.has_alpha());
1600 assert!(ChannelLayout::OklabA.has_alpha());
1601 }
1602
1603 #[test]
1606 fn with_transfer() {
1607 let desc = PixelDescriptor::RGB8_SRGB.with_transfer(TransferFunction::Linear);
1608 assert_eq!(desc.transfer(), TransferFunction::Linear);
1609 assert_eq!(desc.layout(), ChannelLayout::Rgb);
1610 }
1611
1612 #[test]
1613 fn with_primaries() {
1614 let desc = PixelDescriptor::RGB8_SRGB.with_primaries(ColorPrimaries::DisplayP3);
1615 assert_eq!(desc.primaries, ColorPrimaries::DisplayP3);
1616 }
1617
1618 #[test]
1619 fn with_signal_range() {
1620 let desc = PixelDescriptor::RGB8_SRGB.with_signal_range(SignalRange::Narrow);
1621 assert_eq!(desc.signal_range, SignalRange::Narrow);
1622 }
1623
1624 #[test]
1625 fn with_alpha_mode() {
1626 let desc = PixelDescriptor::RGBA8_SRGB.with_alpha(Some(AlphaMode::Premultiplied));
1627 assert_eq!(desc.alpha(), Some(AlphaMode::Premultiplied));
1628 }
1629
1630 #[test]
1633 fn is_opaque_and_may_have_transparency() {
1634 assert!(PixelDescriptor::RGB8_SRGB.is_opaque());
1635 assert!(!PixelDescriptor::RGB8_SRGB.may_have_transparency());
1636 assert!(!PixelDescriptor::RGBA8_SRGB.is_opaque());
1637 assert!(PixelDescriptor::RGBA8_SRGB.may_have_transparency());
1638
1639 let rgbx = PixelDescriptor::new(
1640 ChannelType::U8,
1641 ChannelLayout::Rgba,
1642 Some(AlphaMode::Undefined),
1643 TransferFunction::Srgb,
1644 );
1645 assert!(rgbx.is_opaque());
1646 assert!(!rgbx.may_have_transparency());
1647 }
1648
1649 #[test]
1650 fn is_linear_and_is_unknown_transfer() {
1651 assert!(!PixelDescriptor::RGB8_SRGB.is_linear());
1652 assert!(PixelDescriptor::RGBF32_LINEAR.is_linear());
1653 assert!(!PixelDescriptor::RGB8_SRGB.is_unknown_transfer());
1654 let desc = PixelDescriptor::RGB8_SRGB.with_transfer(TransferFunction::Unknown);
1655 assert!(desc.is_unknown_transfer());
1656 }
1657
1658 #[test]
1659 fn min_alignment() {
1660 assert_eq!(PixelDescriptor::RGB8_SRGB.min_alignment(), 1);
1661 assert_eq!(PixelDescriptor::RGBF32_LINEAR.min_alignment(), 4);
1662 }
1663
1664 #[test]
1665 fn aligned_stride() {
1666 assert_eq!(PixelDescriptor::RGB8_SRGB.aligned_stride(100), 300);
1667 assert_eq!(PixelDescriptor::RGBA8_SRGB.aligned_stride(100), 400);
1668 assert_eq!(PixelDescriptor::RGBF32_LINEAR.aligned_stride(10), 120);
1669 }
1670
1671 #[test]
1672 fn simd_aligned_stride() {
1673 let stride = PixelDescriptor::RGB8_SRGB.simd_aligned_stride(100, 16);
1674 assert!(stride >= 300);
1675 assert_eq!(stride % 16, 0);
1676 assert_eq!(stride % 3, 0); }
1678
1679 #[test]
1682 fn new_full_constructor() {
1683 let desc = PixelDescriptor::new_full(
1684 ChannelType::U8,
1685 ChannelLayout::Rgb,
1686 None,
1687 TransferFunction::Srgb,
1688 ColorPrimaries::DisplayP3,
1689 );
1690 assert_eq!(desc.primaries, ColorPrimaries::DisplayP3);
1691 assert_eq!(desc.transfer(), TransferFunction::Srgb);
1692 }
1693
1694 #[test]
1695 fn from_pixel_format_constructor() {
1696 let desc = PixelDescriptor::from_pixel_format(PixelFormat::Rgba8);
1697 assert_eq!(desc.layout(), ChannelLayout::Rgba);
1698 assert_eq!(desc.transfer(), TransferFunction::Unknown);
1699 assert_eq!(desc.primaries, ColorPrimaries::Bt709);
1700 assert_eq!(desc.signal_range, SignalRange::Full);
1701 }
1702
1703 #[test]
1706 fn pixel_format_name() {
1707 assert_eq!(PixelFormat::Rgb8.name(), "RGB8");
1708 assert_eq!(PixelFormat::Bgra8.name(), "BGRA8");
1709 assert_eq!(PixelFormat::Gray8.name(), "Gray8");
1710 }
1711
1712 #[test]
1715 fn color_model_display() {
1716 assert_eq!(format!("{}", ColorModel::Gray), "Gray");
1717 assert_eq!(format!("{}", ColorModel::Rgb), "RGB");
1718 assert_eq!(format!("{}", ColorModel::YCbCr), "YCbCr");
1719 assert_eq!(format!("{}", ColorModel::Oklab), "Oklab");
1720 }
1721
1722 #[test]
1725 fn signal_range_default() {
1726 assert_eq!(SignalRange::default(), SignalRange::Full);
1727 }
1728
1729 #[test]
1732 fn color_primaries_default() {
1733 assert_eq!(ColorPrimaries::default(), ColorPrimaries::Bt709);
1734 }
1735}