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}