mirror_graphics/texture/
nv12.rs

1use super::Texture2DSample;
2
3use mirror_common::Size;
4use wgpu::{Device, Texture, TextureAspect, TextureFormat};
5
6/// YCbCr, Y′CbCr, or Y Pb/Cb Pr/Cr, also written as YCBCR or Y′CBCR, is a
7/// family of color spaces used as a part of the color image pipeline in video
8/// and digital photography systems. Y′ is the luma component and CB and CR are
9/// the blue-difference and red-difference chroma components. Y′ (with prime) is
10/// distinguished from Y, which is luminance, meaning that light intensity is
11/// nonlinearly encoded based on gamma corrected RGB primaries.
12///
13/// Y′CbCr color spaces are defined by a mathematical coordinate transformation
14/// from an associated RGB primaries and white point. If the underlying RGB
15/// color space is absolute, the Y′CbCr color space is an absolute color space
16/// as well; conversely, if the RGB space is ill-defined, so is Y′CbCr. The
17/// transformation is defined in equations 32, 33 in ITU-T H.273. Nevertheless
18/// that rule does not apply to P3-D65 primaries used by Netflix with
19/// BT.2020-NCL matrix, so that means matrix was not derived from primaries, but
20/// now Netflix allows BT.2020 primaries (since 2021).[1] The same happens with
21/// JPEG: it has BT.601 matrix derived from System M primaries, yet the
22/// primaries of most images are BT.709.
23///
24/// NV12 is possibly the most commonly-used 8-bit 4:2:0 format. It is the
25/// default for Android camera preview.[19] The entire image in Y is written
26/// out, followed by interleaved lines that go U0, V0, U1, V1, etc.
27pub struct Nv12(Texture, Texture);
28
29impl Nv12 {
30    pub(crate) fn new(device: &Device, size: Size) -> Self {
31        let mut textures = Self::create(device, size);
32        Self(textures.next().unwrap(), textures.next().unwrap())
33    }
34}
35
36impl Texture2DSample for Nv12 {
37    fn create_texture_descriptor(size: Size) -> impl IntoIterator<Item = (Size, TextureFormat)> {
38        [
39            (size, TextureFormat::R8Unorm),
40            (
41                Size {
42                    width: size.width / 2,
43                    height: size.height / 2,
44                },
45                TextureFormat::Rg8Unorm,
46            ),
47        ]
48    }
49
50    fn views_descriptors<'a>(
51        &'a self,
52        texture: Option<&'a Texture>,
53    ) -> impl IntoIterator<Item = (&'a Texture, TextureFormat, TextureAspect)> {
54        // When you create a view directly for a texture, the external texture is a
55        // single texture, and you need to create different planes of views on top of
56        // the single texture.
57        if let Some(texture) = texture {
58            [
59                (texture, TextureFormat::R8Unorm, TextureAspect::Plane0),
60                (texture, TextureFormat::Rg8Unorm, TextureAspect::Plane1),
61            ]
62        } else {
63            [
64                (&self.0, TextureFormat::R8Unorm, TextureAspect::All),
65                (&self.1, TextureFormat::Rg8Unorm, TextureAspect::All),
66            ]
67        }
68    }
69
70    fn copy_buffer_descriptors<'a>(
71        &self,
72        buffers: &'a [&'a [u8]],
73    ) -> impl IntoIterator<Item = (&'a [u8], &Texture, TextureAspect, Size)> {
74        let size = {
75            let size = self.0.size();
76            Size {
77                width: size.width,
78                height: size.height,
79            }
80        };
81
82        [
83            (buffers[0], &self.0, TextureAspect::All, size),
84            (buffers[1], &self.1, TextureAspect::All, size),
85        ]
86    }
87}