1use std::{
2 cmp,
3 fmt::{Display, Formatter},
4 iter, mem,
5 num::NonZeroU32,
6};
7
8use bitflags::bitflags;
9use num_enum::TryFromPrimitive;
10
11use crate::{
12 align_to, ceil_rshift,
13 error::Error,
14 frame::{PlaneInformation, PlaneInformationVec},
15 invalid_param_error,
16 media::FrameDescriptor,
17 Result,
18};
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21pub struct Resolution {
22 pub width: u32,
23 pub height: u32,
24}
25
26impl Resolution {
27 pub const fn new(width: u32, height: u32) -> Self {
28 Self {
29 width,
30 height,
31 }
32 }
33
34 pub const SQCIF: Self = Self::new(128, 96);
35 pub const QCIF: Self = Self::new(176, 144);
36 pub const CIF: Self = Self::new(352, 288);
37 pub const QQVGA: Self = Self::new(160, 120);
38 pub const QVGA: Self = Self::new(320, 240);
39 pub const VGA: Self = Self::new(640, 480);
40 pub const SVGA: Self = Self::new(800, 600);
41 pub const XGA: Self = Self::new(1024, 768);
42 pub const SXGA: Self = Self::new(1280, 1024);
43 pub const UXGA: Self = Self::new(1600, 1200);
44 pub const QXGA: Self = Self::new(2048, 1536);
45 pub const SD: Self = Self::new(720, 480);
46 pub const HD: Self = Self::new(1280, 720);
47 pub const FHD: Self = Self::new(1920, 1080);
48 pub const QHD: Self = Self::new(2560, 1440);
49 pub const UHD_4K: Self = Self::new(3840, 2160);
50 pub const UHD_8K: Self = Self::new(7680, 4320);
51}
52
53#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
54#[repr(u8)]
55pub enum ColorRange {
56 #[default]
57 Unspecified,
58 Video,
59 Full,
60 MAX,
61}
62
63#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
64#[repr(u8)]
65pub enum ColorMatrix {
66 #[default]
67 Identity = 0, BT709, Unspecified, Reserved, FCC, BT470BG, SMPTE170M, SMPTE240M, YCgCo, BT2020NCL, BT2020CL, SMPTE2085, ChromaDerivedNCL, ChromaDerivedCL, ICtCp, SMPTE2128, }
84
85#[allow(non_camel_case_types)]
86#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
87#[repr(u8)]
88pub enum ColorPrimaries {
89 #[default]
90 Reserved = 0, BT709, Unspecified, BT470M = 4, BT470BG, SMPTE170M, SMPTE240M, Film, BT2020, SMPTE428, SMPTE431, SMPTE432, JEDEC_P22 = 22, }
104
105#[allow(non_camel_case_types)]
106#[repr(u8)]
107#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
108pub enum ColorTransferCharacteristics {
109 #[default]
110 Reserved = 0, BT709, Unspecified, BT470M = 4, BT470BG, SMPTE170M, SMPTE240M, Linear, Log, LogSqrt, IEC61966_2_4, BT1361E, IEC61966_2_1, BT2020_10, BT2020_12, SMPTE2084, SMPTE428, ARIB_STD_B67, }
129
130#[repr(u8)]
131#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, TryFromPrimitive)]
132pub enum PixelFormat {
133 #[default]
134 ARGB32 = 0, BGRA32, ABGR32, RGBA32, RGB24, BGR24, I420, I422, I444, I440, NV12, NV21, NV16, NV61, NV24, NV42, YV12, YV16, YV24, YUYV, YVYU, UYVY, VYUY, AYUV, Y8, YA8, RGB30, BGR30, ARGB64, BGRA64, ABGR64, RGBA64, I010, I210, I410, I44010, P010, P210, P410, I012, I212, I412, I44012, P012, P212, P412, I016, I216, I416, I44016, P016, P216, P416, MAX,
188}
189
190#[repr(u8)]
191#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, TryFromPrimitive)]
192pub enum CompressionFormat {
193 #[default]
194 MJPEG,
195}
196
197#[derive(Clone, Copy, Debug, Eq, PartialEq)]
198pub enum VideoFormat {
199 Pixel(PixelFormat),
200 Compression(CompressionFormat),
201}
202
203#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
204pub enum ChromaLocation {
205 #[default]
206 Unspecified,
207 Left,
208 Center,
209 TopLeft,
210 Top,
211 BottomLeft,
212 Bottom,
213}
214
215#[derive(Clone, Copy, Debug, Eq, PartialEq)]
216pub enum ChromaSubsampling {
217 YUV420,
218 YUV422,
219 YUV444,
220}
221
222#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
223pub enum Rotation {
224 #[default]
225 None,
226 Rotation90,
227 Rotation180,
228 Rotation270,
229}
230
231#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
232pub enum Origin {
233 #[default]
234 TopDown,
235 BottomUp,
236}
237
238#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
239pub enum ScaleFilter {
240 Nearest,
241 #[default]
242 Bilinear,
243 Bicubic,
244}
245
246#[derive(Clone, Debug, Eq, PartialEq)]
247pub struct VideoFrameDescriptor {
248 pub format: PixelFormat,
249 pub width: NonZeroU32,
250 pub height: NonZeroU32,
251 pub color_range: ColorRange,
252 pub color_matrix: ColorMatrix,
253 pub color_primaries: ColorPrimaries,
254 pub color_transfer_characteristics: ColorTransferCharacteristics,
255 pub chroma_location: ChromaLocation,
256 pub rotation: Rotation,
257 pub origin: Origin,
258 pub transparent: bool,
259 pub extra_alpha: bool,
260 pub crop_left: u32,
261 pub crop_top: u32,
262 pub crop_right: u32,
263 pub crop_bottom: u32,
264}
265
266impl VideoFrameDescriptor {
267 pub fn new(format: PixelFormat, width: NonZeroU32, height: NonZeroU32) -> Self {
268 Self {
269 format,
270 width,
271 height,
272 color_range: ColorRange::default(),
273 color_matrix: ColorMatrix::default(),
274 color_primaries: ColorPrimaries::default(),
275 color_transfer_characteristics: ColorTransferCharacteristics::default(),
276 chroma_location: ChromaLocation::default(),
277 rotation: Rotation::default(),
278 origin: Origin::default(),
279 transparent: false,
280 extra_alpha: false,
281 crop_left: 0,
282 crop_top: 0,
283 crop_right: 0,
284 crop_bottom: 0,
285 }
286 }
287
288 pub fn try_new(format: PixelFormat, width: u32, height: u32) -> Result<Self> {
289 let width = NonZeroU32::new(width).ok_or(invalid_param_error!(width))?;
290 let height = NonZeroU32::new(height).ok_or(invalid_param_error!(height))?;
291
292 Ok(Self::new(format, width, height))
293 }
294}
295
296impl From<VideoFrameDescriptor> for FrameDescriptor {
297 fn from(desc: VideoFrameDescriptor) -> Self {
298 FrameDescriptor::Video(desc)
299 }
300}
301
302bitflags! {
303 #[repr(transparent)]
304 struct PixelFormatFlags: u32 {
305 const Alpha = 1 << 0;
306 const RGB = 1 << 1;
307 const YUV = 1 << 2;
308 const Planar = 1 << 3;
309 const Packed = 1 << 4;
310 const BiPlanar = 1 << 5;
311 }
312}
313
314struct PixelFormatDescriptor {
315 components: u8,
316 chroma_shift_x: u8,
317 chroma_shift_y: u8,
318 depth: u8,
319 flags: PixelFormatFlags,
320 component_bytes: [u8; 4],
321}
322
323macro_rules! pix_fmt_flags {
324 ($($flag:ident)|+) => {
325 PixelFormatFlags::from_bits_truncate(0 $(| PixelFormatFlags::$flag.bits())+)
326 };
327 ($flag:ident) => {
328 PixelFormatFlags::from_bits_truncate(PixelFormatFlags::$flag.bits())
329 };
330}
331
332static PIXEL_FORMAT_DESC: [PixelFormatDescriptor; PixelFormat::MAX as usize] = [
333 PixelFormatDescriptor {
335 components: 1,
336 chroma_shift_x: 0,
337 chroma_shift_y: 0,
338 depth: 8,
339 flags: pix_fmt_flags!(Alpha | RGB | Packed),
340 component_bytes: [4, 0, 0, 0],
341 },
342 PixelFormatDescriptor {
344 components: 1,
345 chroma_shift_x: 0,
346 chroma_shift_y: 0,
347 depth: 8,
348 flags: pix_fmt_flags!(Alpha | RGB | Packed),
349 component_bytes: [4, 0, 0, 0],
350 },
351 PixelFormatDescriptor {
353 components: 1,
354 chroma_shift_x: 0,
355 chroma_shift_y: 0,
356 depth: 8,
357 flags: pix_fmt_flags!(Alpha | RGB | Packed),
358 component_bytes: [4, 0, 0, 0],
359 },
360 PixelFormatDescriptor {
362 components: 1,
363 chroma_shift_x: 0,
364 chroma_shift_y: 0,
365 depth: 8,
366 flags: pix_fmt_flags!(Alpha | RGB | Packed),
367 component_bytes: [4, 0, 0, 0],
368 },
369 PixelFormatDescriptor {
371 components: 1,
372 chroma_shift_x: 0,
373 chroma_shift_y: 0,
374 depth: 8,
375 flags: pix_fmt_flags!(RGB | Packed),
376 component_bytes: [3, 0, 0, 0],
377 },
378 PixelFormatDescriptor {
380 components: 1,
381 chroma_shift_x: 0,
382 chroma_shift_y: 0,
383 depth: 8,
384 flags: pix_fmt_flags!(RGB | Packed),
385 component_bytes: [3, 0, 0, 0],
386 },
387 PixelFormatDescriptor {
389 components: 3,
390 chroma_shift_x: 1,
391 chroma_shift_y: 1,
392 depth: 8,
393 flags: pix_fmt_flags!(YUV | Planar),
394 component_bytes: [1, 1, 1, 0],
395 },
396 PixelFormatDescriptor {
398 components: 3,
399 chroma_shift_x: 1,
400 chroma_shift_y: 0,
401 depth: 8,
402 flags: pix_fmt_flags!(YUV | Planar),
403 component_bytes: [1, 1, 1, 0],
404 },
405 PixelFormatDescriptor {
407 components: 3,
408 chroma_shift_x: 0,
409 chroma_shift_y: 0,
410 depth: 8,
411 flags: pix_fmt_flags!(YUV | Planar),
412 component_bytes: [1, 1, 1, 0],
413 },
414 PixelFormatDescriptor {
416 components: 3,
417 chroma_shift_x: 0,
418 chroma_shift_y: 1,
419 depth: 8,
420 flags: pix_fmt_flags!(YUV | Planar),
421 component_bytes: [1, 1, 1, 0],
422 },
423 PixelFormatDescriptor {
425 components: 2,
426 chroma_shift_x: 1,
427 chroma_shift_y: 1,
428 depth: 8,
429 flags: pix_fmt_flags!(YUV | BiPlanar),
430 component_bytes: [1, 2, 0, 0],
431 },
432 PixelFormatDescriptor {
434 components: 2,
435 chroma_shift_x: 1,
436 chroma_shift_y: 1,
437 depth: 8,
438 flags: pix_fmt_flags!(YUV | BiPlanar),
439 component_bytes: [1, 2, 0, 0],
440 },
441 PixelFormatDescriptor {
443 components: 2,
444 chroma_shift_x: 1,
445 chroma_shift_y: 0,
446 depth: 8,
447 flags: pix_fmt_flags!(YUV | BiPlanar),
448 component_bytes: [1, 2, 0, 0],
449 },
450 PixelFormatDescriptor {
452 components: 2,
453 chroma_shift_x: 1,
454 chroma_shift_y: 0,
455 depth: 8,
456 flags: pix_fmt_flags!(YUV | BiPlanar),
457 component_bytes: [1, 2, 0, 0],
458 },
459 PixelFormatDescriptor {
461 components: 2,
462 chroma_shift_x: 0,
463 chroma_shift_y: 0,
464 depth: 8,
465 flags: pix_fmt_flags!(YUV | BiPlanar),
466 component_bytes: [1, 2, 0, 0],
467 },
468 PixelFormatDescriptor {
470 components: 2,
471 chroma_shift_x: 0,
472 chroma_shift_y: 0,
473 depth: 8,
474 flags: pix_fmt_flags!(YUV | BiPlanar),
475 component_bytes: [1, 2, 0, 0],
476 },
477 PixelFormatDescriptor {
479 components: 3,
480 chroma_shift_x: 1,
481 chroma_shift_y: 1,
482 depth: 8,
483 flags: pix_fmt_flags!(YUV | Planar),
484 component_bytes: [1, 1, 1, 0],
485 },
486 PixelFormatDescriptor {
488 components: 3,
489 chroma_shift_x: 1,
490 chroma_shift_y: 0,
491 depth: 8,
492 flags: pix_fmt_flags!(YUV | Planar),
493 component_bytes: [1, 1, 1, 0],
494 },
495 PixelFormatDescriptor {
497 components: 3,
498 chroma_shift_x: 0,
499 chroma_shift_y: 0,
500 depth: 8,
501 flags: pix_fmt_flags!(YUV | Planar),
502 component_bytes: [1, 1, 1, 0],
503 },
504 PixelFormatDescriptor {
506 components: 1,
507 chroma_shift_x: 1,
508 chroma_shift_y: 0,
509 depth: 8,
510 flags: pix_fmt_flags!(YUV | Packed),
511 component_bytes: [4, 0, 0, 0],
512 },
513 PixelFormatDescriptor {
515 components: 1,
516 chroma_shift_x: 1,
517 chroma_shift_y: 0,
518 depth: 8,
519 flags: pix_fmt_flags!(YUV | Packed),
520 component_bytes: [4, 0, 0, 0],
521 },
522 PixelFormatDescriptor {
524 components: 1,
525 chroma_shift_x: 1,
526 chroma_shift_y: 0,
527 depth: 8,
528 flags: pix_fmt_flags!(YUV | Packed),
529 component_bytes: [4, 0, 0, 0],
530 },
531 PixelFormatDescriptor {
533 components: 1,
534 chroma_shift_x: 1,
535 chroma_shift_y: 0,
536 depth: 8,
537 flags: pix_fmt_flags!(YUV | Packed),
538 component_bytes: [4, 0, 0, 0],
539 },
540 PixelFormatDescriptor {
542 components: 1,
543 chroma_shift_x: 0,
544 chroma_shift_y: 0,
545 depth: 8,
546 flags: pix_fmt_flags!(Alpha | YUV | Packed),
547 component_bytes: [4, 0, 0, 0],
548 },
549 PixelFormatDescriptor {
551 components: 1,
552 chroma_shift_x: 0,
553 chroma_shift_y: 0,
554 depth: 8,
555 flags: PixelFormatFlags::Planar,
556 component_bytes: [1, 0, 0, 0],
557 },
558 PixelFormatDescriptor {
560 components: 2,
561 chroma_shift_x: 0,
562 chroma_shift_y: 0,
563 depth: 8,
564 flags: pix_fmt_flags!(Alpha | Planar),
565 component_bytes: [1, 1, 0, 0],
566 },
567 PixelFormatDescriptor {
569 components: 1,
570 chroma_shift_x: 0,
571 chroma_shift_y: 0,
572 depth: 10,
573 flags: pix_fmt_flags!(RGB | Packed),
574 component_bytes: [4, 0, 0, 0],
575 },
576 PixelFormatDescriptor {
578 components: 1,
579 chroma_shift_x: 0,
580 chroma_shift_y: 0,
581 depth: 10,
582 flags: pix_fmt_flags!(RGB | Packed),
583 component_bytes: [4, 0, 0, 0],
584 },
585 PixelFormatDescriptor {
587 components: 1,
588 chroma_shift_x: 0,
589 chroma_shift_y: 0,
590 depth: 16,
591 flags: pix_fmt_flags!(Alpha | RGB | Packed),
592 component_bytes: [8, 0, 0, 0],
593 },
594 PixelFormatDescriptor {
596 components: 1,
597 chroma_shift_x: 0,
598 chroma_shift_y: 0,
599 depth: 16,
600 flags: pix_fmt_flags!(Alpha | RGB | Packed),
601 component_bytes: [8, 0, 0, 0],
602 },
603 PixelFormatDescriptor {
605 components: 1,
606 chroma_shift_x: 0,
607 chroma_shift_y: 0,
608 depth: 16,
609 flags: pix_fmt_flags!(Alpha | RGB | Packed),
610 component_bytes: [8, 0, 0, 0],
611 },
612 PixelFormatDescriptor {
614 components: 1,
615 chroma_shift_x: 0,
616 chroma_shift_y: 0,
617 depth: 16,
618 flags: pix_fmt_flags!(Alpha | RGB | Packed),
619 component_bytes: [8, 0, 0, 0],
620 },
621 PixelFormatDescriptor {
623 components: 3,
624 chroma_shift_x: 1,
625 chroma_shift_y: 1,
626 depth: 10,
627 flags: pix_fmt_flags!(YUV | Planar),
628 component_bytes: [2, 2, 2, 0],
629 },
630 PixelFormatDescriptor {
632 components: 3,
633 chroma_shift_x: 1,
634 chroma_shift_y: 0,
635 depth: 10,
636 flags: pix_fmt_flags!(YUV | Planar),
637 component_bytes: [2, 2, 2, 0],
638 },
639 PixelFormatDescriptor {
641 components: 3,
642 chroma_shift_x: 0,
643 chroma_shift_y: 0,
644 depth: 10,
645 flags: pix_fmt_flags!(YUV | Planar),
646 component_bytes: [2, 2, 2, 0],
647 },
648 PixelFormatDescriptor {
650 components: 3,
651 chroma_shift_x: 0,
652 chroma_shift_y: 1,
653 depth: 10,
654 flags: pix_fmt_flags!(YUV | Planar),
655 component_bytes: [2, 2, 2, 0],
656 },
657 PixelFormatDescriptor {
659 components: 2,
660 chroma_shift_x: 1,
661 chroma_shift_y: 1,
662 depth: 10,
663 flags: pix_fmt_flags!(YUV | BiPlanar),
664 component_bytes: [2, 4, 0, 0],
665 },
666 PixelFormatDescriptor {
668 components: 2,
669 chroma_shift_x: 1,
670 chroma_shift_y: 0,
671 depth: 10,
672 flags: pix_fmt_flags!(YUV | BiPlanar),
673 component_bytes: [2, 4, 0, 0],
674 },
675 PixelFormatDescriptor {
677 components: 2,
678 chroma_shift_x: 0,
679 chroma_shift_y: 0,
680 depth: 10,
681 flags: pix_fmt_flags!(YUV | BiPlanar),
682 component_bytes: [2, 4, 0, 0],
683 },
684 PixelFormatDescriptor {
686 components: 3,
687 chroma_shift_x: 1,
688 chroma_shift_y: 1,
689 depth: 12,
690 flags: pix_fmt_flags!(YUV | Planar),
691 component_bytes: [2, 2, 2, 0],
692 },
693 PixelFormatDescriptor {
695 components: 3,
696 chroma_shift_x: 1,
697 chroma_shift_y: 0,
698 depth: 12,
699 flags: pix_fmt_flags!(YUV | Planar),
700 component_bytes: [2, 2, 2, 0],
701 },
702 PixelFormatDescriptor {
704 components: 3,
705 chroma_shift_x: 0,
706 chroma_shift_y: 0,
707 depth: 12,
708 flags: pix_fmt_flags!(YUV | Planar),
709 component_bytes: [2, 2, 2, 0],
710 },
711 PixelFormatDescriptor {
713 components: 3,
714 chroma_shift_x: 0,
715 chroma_shift_y: 1,
716 depth: 12,
717 flags: pix_fmt_flags!(YUV | Planar),
718 component_bytes: [2, 2, 2, 0],
719 },
720 PixelFormatDescriptor {
722 components: 2,
723 chroma_shift_x: 1,
724 chroma_shift_y: 1,
725 depth: 12,
726 flags: pix_fmt_flags!(YUV | BiPlanar),
727 component_bytes: [2, 4, 0, 0],
728 },
729 PixelFormatDescriptor {
731 components: 2,
732 chroma_shift_x: 1,
733 chroma_shift_y: 0,
734 depth: 12,
735 flags: pix_fmt_flags!(YUV | BiPlanar),
736 component_bytes: [2, 4, 0, 0],
737 },
738 PixelFormatDescriptor {
740 components: 2,
741 chroma_shift_x: 0,
742 chroma_shift_y: 0,
743 depth: 12,
744 flags: pix_fmt_flags!(YUV | BiPlanar),
745 component_bytes: [2, 4, 0, 0],
746 },
747 PixelFormatDescriptor {
749 components: 3,
750 chroma_shift_x: 1,
751 chroma_shift_y: 1,
752 depth: 16,
753 flags: pix_fmt_flags!(YUV | Planar),
754 component_bytes: [2, 2, 2, 0],
755 },
756 PixelFormatDescriptor {
758 components: 3,
759 chroma_shift_x: 1,
760 chroma_shift_y: 0,
761 depth: 16,
762 flags: pix_fmt_flags!(YUV | Planar),
763 component_bytes: [2, 2, 2, 0],
764 },
765 PixelFormatDescriptor {
767 components: 3,
768 chroma_shift_x: 0,
769 chroma_shift_y: 0,
770 depth: 16,
771 flags: pix_fmt_flags!(YUV | Planar),
772 component_bytes: [2, 2, 2, 0],
773 },
774 PixelFormatDescriptor {
776 components: 3,
777 chroma_shift_x: 0,
778 chroma_shift_y: 1,
779 depth: 16,
780 flags: pix_fmt_flags!(YUV | Planar),
781 component_bytes: [2, 2, 2, 0],
782 },
783 PixelFormatDescriptor {
785 components: 2,
786 chroma_shift_x: 1,
787 chroma_shift_y: 1,
788 depth: 16,
789 flags: pix_fmt_flags!(YUV | BiPlanar),
790 component_bytes: [2, 4, 0, 0],
791 },
792 PixelFormatDescriptor {
794 components: 2,
795 chroma_shift_x: 1,
796 chroma_shift_y: 0,
797 depth: 16,
798 flags: pix_fmt_flags!(YUV | BiPlanar),
799 component_bytes: [2, 4, 0, 0],
800 },
801 PixelFormatDescriptor {
803 components: 2,
804 chroma_shift_x: 0,
805 chroma_shift_y: 0,
806 depth: 16,
807 flags: pix_fmt_flags!(YUV | BiPlanar),
808 component_bytes: [2, 4, 0, 0],
809 },
810];
811
812impl PixelFormat {
813 pub fn components(&self) -> u8 {
814 PIXEL_FORMAT_DESC[*self as usize].components
815 }
816
817 pub fn component_bytes(&self, component: u8) -> u8 {
818 PIXEL_FORMAT_DESC[*self as usize].component_bytes[component as usize]
819 }
820
821 pub fn chroma_subsampling(&self) -> Option<ChromaSubsampling> {
822 if !self.is_yuv() {
823 return None;
824 }
825
826 let desc = &PIXEL_FORMAT_DESC[*self as usize];
827
828 match (desc.chroma_shift_x, desc.chroma_shift_y) {
829 (1, 1) => Some(ChromaSubsampling::YUV420),
830 (1, 0) => Some(ChromaSubsampling::YUV422),
831 (0, 0) => Some(ChromaSubsampling::YUV444),
832 _ => None,
833 }
834 }
835
836 pub fn depth(&self) -> u8 {
837 PIXEL_FORMAT_DESC[*self as usize].depth
838 }
839
840 pub fn is_rgb(&self) -> bool {
841 PIXEL_FORMAT_DESC[*self as usize].flags.contains(PixelFormatFlags::RGB)
842 }
843
844 pub fn is_yuv(&self) -> bool {
845 PIXEL_FORMAT_DESC[*self as usize].flags.contains(PixelFormatFlags::YUV)
846 }
847
848 pub fn is_planar(&self) -> bool {
849 PIXEL_FORMAT_DESC[*self as usize].flags.contains(PixelFormatFlags::Planar)
850 }
851
852 pub fn is_packed(&self) -> bool {
853 PIXEL_FORMAT_DESC[*self as usize].flags.contains(PixelFormatFlags::Packed)
854 }
855
856 pub fn is_biplanar(&self) -> bool {
857 PIXEL_FORMAT_DESC[*self as usize].flags.contains(PixelFormatFlags::BiPlanar)
858 }
859
860 pub fn calc_plane_row_bytes(&self, plane_index: usize, width: u32) -> u32 {
861 let desc = &PIXEL_FORMAT_DESC[*self as usize];
862 let component_bytes = desc.component_bytes[plane_index];
863
864 if plane_index > 0 && (self.is_planar() || self.is_biplanar()) {
865 ceil_rshift(width, desc.chroma_shift_x as u32) * component_bytes as u32
866 } else {
867 width * component_bytes as u32
868 }
869 }
870
871 pub fn calc_plane_height(&self, plane_index: usize, height: u32) -> u32 {
872 if plane_index > 0 && (self.is_planar() || self.is_biplanar()) {
873 let desc = &PIXEL_FORMAT_DESC[*self as usize];
874 ceil_rshift(height, desc.chroma_shift_y as u32)
875 } else {
876 height
877 }
878 }
879
880 pub(crate) fn calc_data(&self, width: u32, height: u32, alignment: u32) -> (usize, PlaneInformationVec) {
881 let desc = &PIXEL_FORMAT_DESC[*self as usize];
882 let mut size;
883 let mut planes = PlaneInformationVec::with_capacity(desc.components as usize);
884
885 match self {
886 PixelFormat::RGB24 | PixelFormat::BGR24 | PixelFormat::Y8 => {
887 let stride = align_to(width * desc.component_bytes[0] as u32, cmp::max(alignment, 4)) as usize;
888 planes.push(PlaneInformation::Video(stride, height));
889 size = stride * height as usize;
890 }
891 PixelFormat::YA8 => {
892 let stride = align_to(width * desc.component_bytes[0] as u32, cmp::max(alignment, 4)) as usize;
893 planes.extend(iter::repeat_n(PlaneInformation::Video(stride, height), 2));
894 size = stride * height as usize * 2;
895 }
896 PixelFormat::YUYV | PixelFormat::YVYU | PixelFormat::UYVY | PixelFormat::VYUY | PixelFormat::AYUV => {
897 let stride = align_to(ceil_rshift(width, desc.chroma_shift_x as u32) * 4, alignment) as usize;
898 planes.push(PlaneInformation::Video(stride, height));
899 size = stride * height as usize;
900 }
901 _ => {
902 let stride = align_to(width * desc.component_bytes[0] as u32, alignment) as usize;
903 planes.push(PlaneInformation::Video(stride, height));
904 size = stride * height as usize;
905 for i in 1..desc.components as usize {
906 let stride = align_to(ceil_rshift(width, desc.chroma_shift_x as u32) * desc.component_bytes[i] as u32, alignment) as usize;
907 let height = ceil_rshift(height, desc.chroma_shift_y as u32);
908 planes.push(PlaneInformation::Video(stride, height));
909 size += stride * height as usize;
910 }
911 }
912 }
913
914 (size, planes)
915 }
916
917 pub(crate) fn calc_data_with_stride(&self, height: u32, stride: usize) -> (usize, PlaneInformationVec) {
918 let desc = &PIXEL_FORMAT_DESC[*self as usize];
919 let mut size;
920 let mut planes = PlaneInformationVec::with_capacity(desc.components as usize);
921
922 planes.push(PlaneInformation::Video(stride, height));
923 size = stride * height as usize;
924 for i in 1..desc.components as usize {
925 let plane_stride = ceil_rshift(stride, desc.chroma_shift_x as usize) * desc.component_bytes[i] as usize;
926 let plane_height = ceil_rshift(height, desc.chroma_shift_y as u32);
927 planes.push(PlaneInformation::Video(plane_stride, plane_height));
928 size += plane_stride * plane_height as usize;
929 }
930
931 (size, planes)
932 }
933
934 pub(crate) fn calc_chroma_dimensions(&self, width: u32, height: u32) -> (u32, u32) {
935 let desc = &PIXEL_FORMAT_DESC[*self as usize];
936 let chroma_width = ceil_rshift(width, desc.chroma_shift_x as u32);
937 let chroma_height = ceil_rshift(height, desc.chroma_shift_y as u32);
938 (chroma_width, chroma_height)
939 }
940}
941
942impl From<PixelFormat> for usize {
943 fn from(value: PixelFormat) -> Self {
944 value as usize
945 }
946}
947
948impl TryFrom<usize> for PixelFormat {
949 type Error = Error;
950
951 fn try_from(value: usize) -> Result<Self> {
952 if value <= PixelFormat::MAX as usize {
953 Ok(unsafe { mem::transmute::<u8, PixelFormat>(value as u8) })
954 } else {
955 Err(invalid_param_error!(value))
956 }
957 }
958}
959
960impl From<ColorRange> for usize {
961 fn from(value: ColorRange) -> Self {
962 value as usize
963 }
964}
965
966impl From<usize> for ColorRange {
967 fn from(value: usize) -> Self {
968 match value {
969 0 => ColorRange::Unspecified,
970 1 => ColorRange::Video,
971 2 => ColorRange::Full,
972 _ => ColorRange::Unspecified,
973 }
974 }
975}
976
977impl From<ColorMatrix> for usize {
978 fn from(value: ColorMatrix) -> Self {
979 value as usize
980 }
981}
982
983impl TryFrom<usize> for ColorMatrix {
984 type Error = Error;
985
986 fn try_from(value: usize) -> Result<Self> {
987 match value {
988 0 => Ok(ColorMatrix::Identity),
989 1 => Ok(ColorMatrix::BT709),
990 2 => Ok(ColorMatrix::Unspecified),
991 4 => Ok(ColorMatrix::FCC),
992 5 => Ok(ColorMatrix::BT470BG),
993 6 => Ok(ColorMatrix::SMPTE170M),
994 7 => Ok(ColorMatrix::SMPTE240M),
995 8 => Ok(ColorMatrix::YCgCo),
996 9 => Ok(ColorMatrix::BT2020NCL),
997 10 => Ok(ColorMatrix::BT2020CL),
998 11 => Ok(ColorMatrix::SMPTE2085),
999 12 => Ok(ColorMatrix::ChromaDerivedNCL),
1000 13 => Ok(ColorMatrix::ChromaDerivedCL),
1001 14 => Ok(ColorMatrix::ICtCp),
1002 _ => Err(invalid_param_error!(value)),
1003 }
1004 }
1005}
1006
1007impl From<ColorPrimaries> for usize {
1008 fn from(value: ColorPrimaries) -> Self {
1009 value as usize
1010 }
1011}
1012
1013impl TryFrom<usize> for ColorPrimaries {
1014 type Error = Error;
1015
1016 fn try_from(value: usize) -> Result<Self> {
1017 match value {
1018 0 => Ok(ColorPrimaries::Reserved),
1019 1 => Ok(ColorPrimaries::BT709),
1020 2 => Ok(ColorPrimaries::Unspecified),
1021 4 => Ok(ColorPrimaries::BT470M),
1022 5 => Ok(ColorPrimaries::BT470BG),
1023 6 => Ok(ColorPrimaries::SMPTE170M),
1024 7 => Ok(ColorPrimaries::SMPTE240M),
1025 8 => Ok(ColorPrimaries::Film),
1026 9 => Ok(ColorPrimaries::BT2020),
1027 10 => Ok(ColorPrimaries::SMPTE428),
1028 11 => Ok(ColorPrimaries::SMPTE431),
1029 12 => Ok(ColorPrimaries::SMPTE432),
1030 _ => Err(invalid_param_error!(value)),
1031 }
1032 }
1033}
1034
1035impl From<ColorTransferCharacteristics> for usize {
1036 fn from(value: ColorTransferCharacteristics) -> Self {
1037 value as usize
1038 }
1039}
1040
1041impl TryFrom<usize> for ColorTransferCharacteristics {
1042 type Error = Error;
1043
1044 fn try_from(value: usize) -> Result<Self> {
1045 match value {
1046 0 => Ok(ColorTransferCharacteristics::Reserved),
1047 1 => Ok(ColorTransferCharacteristics::BT709),
1048 2 => Ok(ColorTransferCharacteristics::Unspecified),
1049 4 => Ok(ColorTransferCharacteristics::BT470M),
1050 5 => Ok(ColorTransferCharacteristics::BT470BG),
1051 6 => Ok(ColorTransferCharacteristics::SMPTE170M),
1052 7 => Ok(ColorTransferCharacteristics::SMPTE240M),
1053 8 => Ok(ColorTransferCharacteristics::Linear),
1054 9 => Ok(ColorTransferCharacteristics::Log),
1055 10 => Ok(ColorTransferCharacteristics::LogSqrt),
1056 11 => Ok(ColorTransferCharacteristics::IEC61966_2_4),
1057 12 => Ok(ColorTransferCharacteristics::BT1361E),
1058 13 => Ok(ColorTransferCharacteristics::IEC61966_2_1),
1059 14 => Ok(ColorTransferCharacteristics::BT2020_10),
1060 15 => Ok(ColorTransferCharacteristics::BT2020_12),
1061 16 => Ok(ColorTransferCharacteristics::SMPTE2084),
1062 17 => Ok(ColorTransferCharacteristics::SMPTE428),
1063 18 => Ok(ColorTransferCharacteristics::ARIB_STD_B67),
1064 _ => Err(invalid_param_error!(value)),
1065 }
1066 }
1067}
1068
1069impl Display for VideoFormat {
1070 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1071 match self {
1072 VideoFormat::Pixel(format) => write!(f, "{:?}", format),
1073 VideoFormat::Compression(format) => write!(f, "{:?}", format),
1074 }
1075 }
1076}
1077
1078impl VideoFormat {
1079 pub fn is_compressed(&self) -> bool {
1080 matches!(self, VideoFormat::Compression(_))
1081 }
1082
1083 pub fn is_yuv(&self) -> bool {
1084 match self {
1085 VideoFormat::Pixel(format) => format.is_yuv(),
1086 VideoFormat::Compression(CompressionFormat::MJPEG) => true,
1087 }
1088 }
1089}
1090
1091const COMPRESSION_MASK: u32 = 0x8000;
1092
1093impl From<VideoFormat> for u32 {
1094 fn from(val: VideoFormat) -> Self {
1095 match val {
1096 VideoFormat::Pixel(format) => format as u32,
1097 VideoFormat::Compression(format) => format as u32 | COMPRESSION_MASK,
1098 }
1099 }
1100}
1101
1102impl TryFrom<u32> for VideoFormat {
1103 type Error = Error;
1104
1105 fn try_from(value: u32) -> Result<Self> {
1106 if value & COMPRESSION_MASK != 0 {
1107 let format_value = value & !COMPRESSION_MASK;
1108 CompressionFormat::try_from(format_value as u8).map(VideoFormat::Compression).map_err(|e| Error::Invalid(e.to_string()))
1109 } else {
1110 PixelFormat::try_from(value as u8).map(VideoFormat::Pixel).map_err(|e| Error::Invalid(e.to_string()))
1111 }
1112 }
1113}