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: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 planeConfig:plane,
75 subsampling:subsampling.into_native(),
76 planeIdx: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; 4] = [ISize::default(); YUVAInfo::MAX_PLANES];
95 let size: usize = unsafeResult {
96 SkYUVAInfo::PlaneDimensions(
97 imageDimensions:image_dimensions.into().into_native(),
98 arg1:config,
99 arg2:subsampling.into_native(),
100 arg3:origin.into_native(),
101 planeDimensions: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 unsafeResult { sb::C_SkYUVAInfo_NumPlanes(planeConfig: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 unsafeResult { sb::C_SkYUVAInfo_NumChannelsInPlane(planarConfig:config, i: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(arg1:config) }
130}
131
132impl Default for YUVAInfo {
133 fn default() -> Self {
134 Self::construct(|yi: *mut SkYUVAInfo| unsafe { sb::C_SkYUVAInfo_Construct(uninitialized:yi) })
135 }
136}
137
138impl NativePartialEq for YUVAInfo {
139 fn eq(&self, rhs: &Self) -> bool {
140 unsafe { sb::C_SkYUVAInfo_equals(self.native(), b:rhs.native()) }
141 }
142}
143
144impl fmt::Debug for YUVAInfo {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 f&mut DebugStruct<'_, '_>.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(name:"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}
297