skia_safe/core/
yuva_info.rs

1use super::image_info;
2use crate::{prelude::*, EncodedOrigin, ISize, Matrix};
3use skia_bindings::{self as sb, SkYUVAInfo, SkYUVAInfo_Subsampling};
4use std::{fmt, ptr};
5
6/// Specifies the structure of planes for a YUV image with optional alpha. The actual planar data
7/// is not part of this structure and depending on usage is in external textures or pixmaps.
8pub type YUVAInfo = Handle<SkYUVAInfo>;
9unsafe_send_sync!(YUVAInfo);
10
11impl NativeDrop for SkYUVAInfo {
12    fn drop(&mut self) {
13        unsafe { sb::C_SkYUVAInfo_destruct(self) }
14    }
15}
16
17/// Specifies how YUV (and optionally A) are divided among planes. Planes are separated by
18/// underscores in the enum value names. Within each plane the pixmap/texture channels are
19/// mapped to the YUVA channels in the order specified, e.g. for kY_UV Y is in channel 0 of plane
20/// 0, U is in channel 0 of plane 1, and V is in channel 1 of plane 1. Channel ordering
21/// within a pixmap/texture given the channels it contains:
22/// A:                       0:A
23/// Luminance/Gray:          0:Gray
24/// Luminance/Gray + Alpha:  0:Gray, 1:A
25/// RG                       0:R,    1:G
26/// RGB                      0:R,    1:G, 2:B
27/// RGBA                     0:R,    1:G, 2:B, 3:A
28pub use sb::SkYUVAInfo_PlaneConfig as PlaneConfig;
29variant_name!(PlaneConfig::YUV);
30
31/// UV subsampling is also specified in the enum value names using J:a:b notation (e.g. 4:2:0 is
32/// 1/2 horizontal and 1/2 vertical resolution for U and V). If alpha is present it is not sub-
33/// sampled. Note that Subsampling values other than k444 are only valid with [PlaneConfig] values
34/// that have U and V in different planes than Y (and A, if present).
35#[repr(i32)]
36#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
37pub enum Subsampling {
38    Unknown = SkYUVAInfo_Subsampling::kUnknown as _,
39    S444 = SkYUVAInfo_Subsampling::k444 as _,
40    S422 = SkYUVAInfo_Subsampling::k422 as _,
41    S420 = SkYUVAInfo_Subsampling::k420 as _,
42    S440 = SkYUVAInfo_Subsampling::k440 as _,
43    S411 = SkYUVAInfo_Subsampling::k411 as _,
44    S410 = SkYUVAInfo_Subsampling::k410 as _,
45}
46
47native_transmutable!(SkYUVAInfo_Subsampling, Subsampling, subsampling_layout);
48
49/// Describes how subsampled chroma values are sited relative to luma values.
50///
51/// Currently only centered siting is supported but will expand to support additional sitings.
52pub use sb::SkYUVAInfo_Siting as Siting;
53variant_name!(Siting::Centered);
54
55/// Ratio of Y/A values to U/V values in x and y.
56pub fn subsampling_factors(subsampling: Subsampling) -> (i32, i32) {
57    let mut factors: [i32; 2] = Default::default();
58    unsafe { sb::C_SkYUVAInfo_SubsamplingFactors(subsampling.into_native(), &mut factors[0]) };
59    #[allow(clippy::tuple_array_conversions)]
60    (factors[0], factors[1])
61}
62
63/// `SubsamplingFactors(Subsampling)` if `plane_index` refers to a U/V plane and otherwise `(1, 1)`
64/// if inputs are valid. Invalid inputs consist of incompatible [PlaneConfig] [Subsampling]
65/// `plane_index` combinations. `(0, 0)` is returned for invalid inputs.
66pub fn plane_subsampling_factors(
67    plane: PlaneConfig,
68    subsampling: Subsampling,
69    plane_index: usize,
70) -> (i32, i32) {
71    let mut factors: [i32; 2] = Default::default();
72    unsafe {
73        sb::C_SkYUVAInfo_PlaneSubsamplingFactors(
74            plane,
75            subsampling.into_native(),
76            plane_index.try_into().unwrap(),
77            &mut factors[0],
78        )
79    };
80    #[allow(clippy::tuple_array_conversions)]
81    (factors[0], factors[1])
82}
83
84/// Given image dimensions, a planer configuration, subsampling, and origin, determine the expected
85/// size of each plane. Returns the expected planes. The input image dimensions are as displayed
86/// (after the planes have been transformed to the intended display orientation). The plane
87/// dimensions are output as the planes are stored in memory (may be rotated from image dimensions).
88pub fn plane_dimensions(
89    image_dimensions: impl Into<ISize>,
90    config: PlaneConfig,
91    subsampling: Subsampling,
92    origin: EncodedOrigin,
93) -> Vec<ISize> {
94    let mut plane_dimensions = [ISize::default(); YUVAInfo::MAX_PLANES];
95    let size: usize = unsafe {
96        SkYUVAInfo::PlaneDimensions(
97            image_dimensions.into().into_native(),
98            config,
99            subsampling.into_native(),
100            origin.into_native(),
101            plane_dimensions.native_mut().as_mut_ptr(),
102        )
103    }
104    .try_into()
105    .unwrap();
106
107    plane_dimensions[0..size].to_vec()
108}
109
110/// Number of planes for a given [PlaneConfig].
111pub fn num_planes(config: PlaneConfig) -> usize {
112    unsafe { sb::C_SkYUVAInfo_NumPlanes(config) }
113        .try_into()
114        .unwrap()
115}
116
117/// Number of Y, U, V, A channels in the ith plane for a given [PlaneConfig] (or [None] if i is
118/// invalid).
119pub fn num_channels_in_plane(config: PlaneConfig, i: usize) -> Option<usize> {
120    (i < num_planes(config)).if_true_then_some(|| {
121        unsafe { sb::C_SkYUVAInfo_NumChannelsInPlane(config, i.try_into().unwrap()) }
122            .try_into()
123            .unwrap()
124    })
125}
126
127/// Does the [PlaneConfig] have alpha values?
128pub fn has_alpha(config: PlaneConfig) -> bool {
129    unsafe { sb::SkYUVAInfo_HasAlpha(config) }
130}
131
132impl Default for YUVAInfo {
133    fn default() -> Self {
134        Self::construct(|yi| unsafe { sb::C_SkYUVAInfo_Construct(yi) })
135    }
136}
137
138impl NativePartialEq for YUVAInfo {
139    fn eq(&self, rhs: &Self) -> bool {
140        unsafe { sb::C_SkYUVAInfo_equals(self.native(), rhs.native()) }
141    }
142}
143
144impl fmt::Debug for YUVAInfo {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        f.debug_struct("YUVAInfo")
147            .field("dimensions", &self.dimensions())
148            .field("plane_config", &self.plane_config())
149            .field("subsampling", &self.subsampling())
150            .field("yuv_color_space", &self.yuv_color_space())
151            .field("origin", &self.origin())
152            .field("siting_xy", &self.siting_xy())
153            .finish()
154    }
155}
156
157impl YUVAInfo {
158    pub const MAX_PLANES: usize = sb::SkYUVAInfo_kMaxPlanes as _;
159
160    /// `dimensions` should specify the size of the full resolution image (after planes have been
161    /// oriented to how the image is displayed as indicated by `origin`).
162    pub fn new(
163        dimensions: impl Into<ISize>,
164        config: PlaneConfig,
165        subsampling: Subsampling,
166        color_space: image_info::YUVColorSpace,
167        origin: impl Into<Option<EncodedOrigin>>,
168        siting_xy: impl Into<Option<(Siting, Siting)>>,
169    ) -> Option<Self> {
170        let origin = origin.into().unwrap_or(EncodedOrigin::TopLeft);
171        let (siting_x, siting_y) = siting_xy
172            .into()
173            .unwrap_or((Siting::Centered, Siting::Centered));
174
175        let n = unsafe {
176            SkYUVAInfo::new(
177                dimensions.into().into_native(),
178                config,
179                subsampling.into_native(),
180                color_space,
181                origin.into_native(),
182                siting_x,
183                siting_y,
184            )
185        };
186        Self::native_is_valid(&n).if_true_then_some(|| Self::from_native_c(n))
187    }
188
189    pub fn plane_config(&self) -> PlaneConfig {
190        self.native().fPlaneConfig
191    }
192
193    pub fn subsampling(&self) -> Subsampling {
194        Subsampling::from_native_c(self.native().fSubsampling)
195    }
196
197    pub fn plane_subsampling_factors(&self, plane_index: usize) -> (i32, i32) {
198        plane_subsampling_factors(self.plane_config(), self.subsampling(), plane_index)
199    }
200
201    /// Dimensions of the full resolution image (after planes have been oriented to how the image
202    /// is displayed as indicated by fOrigin).
203    pub fn dimensions(&self) -> ISize {
204        ISize::from_native_c(self.native().fDimensions)
205    }
206
207    pub fn width(&self) -> i32 {
208        self.dimensions().width
209    }
210
211    pub fn height(&self) -> i32 {
212        self.dimensions().height
213    }
214
215    pub fn yuv_color_space(&self) -> image_info::YUVColorSpace {
216        self.native().fYUVColorSpace
217    }
218
219    pub fn siting_xy(&self) -> (Siting, Siting) {
220        let n = self.native();
221        (n.fSitingX, n.fSitingY)
222    }
223
224    pub fn origin(&self) -> EncodedOrigin {
225        EncodedOrigin::from_native_c(self.native().fOrigin)
226    }
227
228    pub fn origin_matrix(&self) -> Matrix {
229        self.origin().to_matrix((self.width(), self.height()))
230    }
231
232    pub fn has_alpha(&self) -> bool {
233        has_alpha(self.plane_config())
234    }
235
236    /// Returns the dimensions for each plane. Dimensions are as stored in memory, before
237    /// transformation to image display space as indicated by [origin(&self)].
238    pub fn plane_dimensions(&self) -> Vec<ISize> {
239        self::plane_dimensions(
240            self.dimensions(),
241            self.plane_config(),
242            self.subsampling(),
243            self.origin(),
244        )
245    }
246
247    /// Given a per-plane row bytes, determine size to allocate for all planes. Optionally retrieves
248    /// the per-plane byte sizes in planeSizes if not `None`. If total size overflows will return
249    /// `SIZE_MAX` and set all planeSizes to `SIZE_MAX`.
250    pub fn compute_total_bytes(
251        &self,
252        row_bytes: &[usize; Self::MAX_PLANES],
253        plane_sizes: Option<&mut [usize; Self::MAX_PLANES]>,
254    ) -> usize {
255        unsafe {
256            self.native().computeTotalBytes(
257                row_bytes.as_ptr(),
258                plane_sizes
259                    .map(|v| v.as_mut_ptr())
260                    .unwrap_or(ptr::null_mut()),
261            )
262        }
263    }
264
265    pub fn num_planes(&self) -> usize {
266        num_planes(self.plane_config())
267    }
268
269    pub fn num_channels_in_plane(&self, i: usize) -> Option<usize> {
270        num_channels_in_plane(self.plane_config(), i)
271    }
272
273    /// Returns a [YUVAInfo] that is identical to this one but with the passed [Subsampling]. If the
274    /// passed [Subsampling] is not [Subsampling::S444] and this info's [PlaneConfig] is not
275    /// compatible with chroma subsampling (because Y is in the same plane as UV) then the result
276    /// will be `None`.
277    pub fn with_subsampling(&self, subsampling: Subsampling) -> Option<Self> {
278        Self::try_construct(|info| unsafe {
279            sb::C_SkYUVAInfo_makeSubsampling(self.native(), subsampling.into_native(), info);
280            Self::native_is_valid(&*info)
281        })
282    }
283
284    /// Returns a [YUVAInfo] that is identical to this one but with the passed dimensions. If the
285    /// passed dimensions is empty then the result will be `None`.
286    pub fn with_dimensions(&self, dimensions: impl Into<ISize>) -> Option<Self> {
287        Self::try_construct(|info| unsafe {
288            sb::C_SkYUVAInfo_makeDimensions(self.native(), dimensions.into().native(), info);
289            Self::native_is_valid(&*info)
290        })
291    }
292
293    pub(crate) fn native_is_valid(info: &SkYUVAInfo) -> bool {
294        info.fPlaneConfig != PlaneConfig::Unknown
295    }
296}