1use crate::{prelude::*, ColorType, Data, ImageInfo, Pixmap, YUVAInfo, YUVColorSpace};
2use skia_bindings::{self as sb, SkYUVAPixmapInfo, SkYUVAPixmaps};
3use std::{ffi::c_void, fmt, ptr};
4use yuva_pixmap_info::SupportedDataTypes;
5
6/// Data type for Y, U, V, and possibly A channels independent of how values are packed into planes.
7pub use yuva_pixmap_info::DataType;
8variant_name!(DataType::Float16);
9
10/// [YUVAInfo] combined with per-plane [ColorType]s and row bytes. Fully specifies the [Pixmap]`s
11/// for a YUVA image without the actual pixel memory and data.
12pub type YUVAPixmapInfo = Handle<SkYUVAPixmapInfo>;
13unsafe_send_sync!(YUVAPixmapInfo);
14
15impl NativeDrop for SkYUVAPixmapInfo {
16 fn drop(&mut self) {
17 unsafe { sb::C_SkYUVAPixmapInfo_destruct(self) }
18 }
19}
20
21impl NativePartialEq for SkYUVAPixmapInfo {
22 fn eq(&self, rhs: &Self) -> bool {
23 unsafe { sb::C_SkYUVAPixmapInfo_equals(self, b:rhs) }
24 }
25}
26
27impl fmt::Debug for YUVAPixmapInfo {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 let plane_infos: Vec<_> = self.plane_infos().collect();
30 let row_bytes: Vec<_> = self.row_bytes_iter().collect();
31 f&mut DebugStruct<'_, '_>.debug_struct("YUVAPixmapInfo")
32 .field("yuva_info", &self.yuva_info())
33 .field("plane_infos", &plane_infos)
34 .field("row_bytes", &row_bytes)
35 .field(name:"data_type", &self.data_type())
36 .finish()
37 }
38}
39
40impl YUVAPixmapInfo {
41 pub const MAX_PLANES: usize = sb::SkYUVAInfo_kMaxPlanes as _;
42 pub const DATA_TYPE_CNT: usize = DataType::Last as _;
43
44 /// Initializes the [YUVAPixmapInfo] from a [YUVAInfo] with per-plane color types and row bytes.
45 /// This will return [None] if the colorTypes aren't compatible with the [YUVAInfo] or if a
46 /// rowBytes entry is not valid for the plane dimensions and color type. Color type and
47 /// row byte values beyond the number of planes in [YUVAInfo] are ignored. All [ColorType]s
48 /// must have the same [DataType] or this will return [None].
49 ///
50 /// If `rowBytes` is [None] then bpp*width is assumed for each plane.
51 pub fn new(
52 info: &YUVAInfo,
53 color_types: &[ColorType],
54 row_bytes: Option<&[usize]>,
55 ) -> Option<Self> {
56 if color_types.len() != info.num_planes() {
57 return None;
58 }
59 if let Some(rb) = row_bytes {
60 if rb.len() != color_types.len() {
61 return None;
62 }
63 }
64 let mut color_types_array = [ColorType::Unknown; Self::MAX_PLANES];
65 color_types_array[..color_types.len()].copy_from_slice(color_types);
66
67 let mut row_bytes_array = [0; Self::MAX_PLANES];
68 let row_bytes_ptr = {
69 if let Some(row_bytes) = row_bytes {
70 row_bytes_array[..row_bytes.len()].copy_from_slice(row_bytes);
71 row_bytes_array.as_ptr()
72 } else {
73 ptr::null()
74 }
75 };
76
77 let info = unsafe {
78 SkYUVAPixmapInfo::new(
79 info.native(),
80 color_types_array.native().as_ptr(),
81 row_bytes_ptr,
82 )
83 };
84 Self::native_is_valid(&info).if_true_then_some(|| Self::from_native_c(info))
85 }
86
87 /// Like above but uses [yuva_pixmap_info::default_color_type_for_data_type] to determine each plane's [ColorType]. If
88 /// `rowBytes` is [None] then bpp*width is assumed for each plane.
89 pub fn from_data_type(
90 info: &YUVAInfo,
91 data_type: DataType,
92 row_bytes: Option<&[usize]>,
93 ) -> Option<Self> {
94 let mut row_bytes_array = [0; Self::MAX_PLANES];
95 let row_bytes_ptr = {
96 if let Some(row_bytes) = row_bytes {
97 row_bytes_array[..row_bytes.len()].copy_from_slice(row_bytes);
98 row_bytes_array.as_ptr()
99 } else {
100 ptr::null()
101 }
102 };
103
104 let info = unsafe { SkYUVAPixmapInfo::new1(info.native(), data_type, row_bytes_ptr) };
105
106 Self::native_is_valid(&info).if_true_then_some(|| Self::from_native_c(info))
107 }
108
109 pub fn yuva_info(&self) -> &YUVAInfo {
110 YUVAInfo::from_native_ref(&self.native().fYUVAInfo)
111 }
112
113 pub fn yuv_color_space(&self) -> YUVColorSpace {
114 self.yuva_info().yuv_color_space()
115 }
116
117 /// The number of [Pixmap] planes.
118 pub fn num_planes(&self) -> usize {
119 self.yuva_info().num_planes()
120 }
121
122 /// The per-YUV`[A]` channel data type.
123 pub fn data_type(&self) -> DataType {
124 self.native().fDataType
125 }
126
127 /// Row bytes for the ith plane. Returns `None` if `i` >= [`Self::num_planes()`] or this
128 /// [YUVAPixmapInfo] is invalid.
129 pub fn row_bytes(&self, i: usize) -> Option<usize> {
130 (i < self.num_planes()).if_true_then_some(|| unsafe {
131 sb::C_SkYUVAPixmapInfo_rowBytes(self.native(), i.try_into().unwrap())
132 })
133 }
134
135 /// Row bytes for all planes.
136 pub fn row_bytes_iter(&self) -> impl Iterator<Item = usize> + Captures<&Self> {
137 (0..self.num_planes()).map(move |i| self.row_bytes(i).unwrap())
138 }
139
140 /// Image info for the ith plane, or `None` if `i` >= [`Self::num_planes()`]
141 pub fn plane_info(&self, i: usize) -> Option<&ImageInfo> {
142 (i < self.num_planes()).if_true_then_some(|| {
143 ImageInfo::from_native_ref(unsafe {
144 &*sb::C_SkYUVAPixmapInfo_planeInfo(self.native(), i.try_into().unwrap())
145 })
146 })
147 }
148
149 /// An iterator of all planes' image infos.
150 pub fn plane_infos(&self) -> impl Iterator<Item = &ImageInfo> {
151 (0..self.num_planes()).map(move |i| self.plane_info(i).unwrap())
152 }
153
154 /// Determine size to allocate for all planes. Optionally retrieves the per-plane sizes in
155 /// planeSizes if not [None]. If total size overflows will return SIZE_MAX and set all
156 /// `plane_sizes` to SIZE_MAX.
157 pub fn compute_total_bytes(
158 &self,
159 plane_sizes: Option<&mut [usize; Self::MAX_PLANES]>,
160 ) -> usize {
161 unsafe {
162 self.native().computeTotalBytes(
163 plane_sizes
164 .map(|ps| ps.as_mut_ptr())
165 .unwrap_or(ptr::null_mut()),
166 )
167 }
168 }
169
170 /// Takes an allocation that is assumed to be at least [compute_total_bytes(&self)] in size and
171 /// configures the first [numPlanes(&self)] entries in pixmaps array to point into that memory.
172 /// The remaining entries of pixmaps are default initialized. Returns [None] if this
173 /// [YUVAPixmapInfo] not valid.
174 #[allow(clippy::missing_safety_doc)]
175 pub unsafe fn init_pixmaps_from_single_allocation(
176 &self,
177 memory: *mut c_void,
178 ) -> Option<[Pixmap; Self::MAX_PLANES]> {
179 // Can't return a Vec<Pixmap> because Pixmaps can't be cloned.
180 let mut pixmaps: [Pixmap; Self::MAX_PLANES] = Default::default();
181 self.native()
182 .initPixmapsFromSingleAllocation(memory, pixmaps[0].native_mut())
183 .if_true_some(pixmaps)
184 }
185
186 /// Is this valid and does it use color types allowed by the passed [SupportedDataTypes]?
187 pub fn is_supported(&self, data_types: &SupportedDataTypes) -> bool {
188 unsafe { self.native().isSupported(data_types.native()) }
189 }
190
191 pub(crate) fn new_if_valid(
192 set_pixmap_info: impl Fn(&mut SkYUVAPixmapInfo) -> bool,
193 ) -> Option<Self> {
194 let mut pixmap_info = Self::new_invalid();
195 let r = set_pixmap_info(&mut pixmap_info);
196 (r && Self::native_is_valid(&pixmap_info))
197 .if_true_then_some(|| YUVAPixmapInfo::from_native_c(pixmap_info))
198 }
199
200 /// Returns `true` if this has been configured with a non-empty dimensioned [YUVAInfo] with
201 /// compatible color types and row bytes.
202 fn native_is_valid(info: *const SkYUVAPixmapInfo) -> bool {
203 unsafe { sb::C_SkYUVAPixmapInfo_isValid(info) }
204 }
205
206 /// Creates a native default instance that is invalid.
207 fn new_invalid() -> SkYUVAPixmapInfo {
208 construct(|pi| unsafe { sb::C_SkYUVAPixmapInfo_Construct(pi) })
209 }
210}
211
212/// Helper to store [Pixmap] planes as described by a [YUVAPixmapInfo]. Can be responsible for
213/// allocating/freeing memory for pixmaps or use external memory.
214pub type YUVAPixmaps = Handle<SkYUVAPixmaps>;
215unsafe_send_sync!(YUVAPixmaps);
216
217impl NativeDrop for SkYUVAPixmaps {
218 fn drop(&mut self) {
219 unsafe { sb::C_SkYUVAPixmaps_destruct(self) }
220 }
221}
222
223impl NativeClone for SkYUVAPixmaps {
224 fn clone(&self) -> Self {
225 construct(|pixmaps: *mut SkYUVAPixmaps| unsafe { sb::C_SkYUVAPixmaps_MakeCopy(self, uninitialized:pixmaps) })
226 }
227}
228
229impl fmt::Debug for YUVAPixmaps {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 f&mut DebugStruct<'_, '_>.debug_struct("YUVAPixmaps")
232 .field("planes", &self.planes())
233 .field("yuva_info", &self.yuva_info())
234 .field(name:"data_type", &self.data_type())
235 .finish()
236 }
237}
238
239impl YUVAPixmaps {
240 pub const MAX_PLANES: usize = YUVAPixmapInfo::MAX_PLANES;
241
242 pub fn recommended_rgba_color_type(dt: DataType) -> ColorType {
243 ColorType::from_native_c(unsafe { sb::SkYUVAPixmaps::RecommendedRGBAColorType(dt) })
244 }
245
246 /// Allocate space for pixmaps' pixels in the [YUVAPixmaps].
247 pub fn allocate(info: &YUVAPixmapInfo) -> Option<Self> {
248 Self::try_construct(|pixmaps| unsafe {
249 sb::C_SkYUVAPixmaps_Allocate(pixmaps, info.native());
250 Self::native_is_valid(pixmaps)
251 })
252 }
253
254 /// Use storage in [Data] as backing store for pixmaps' pixels. [Data] is retained by the
255 /// [YUVAPixmaps].
256 pub fn from_data(info: &YUVAPixmapInfo, data: impl Into<Data>) -> Option<Self> {
257 Self::try_construct(|pixmaps| unsafe {
258 sb::C_SkYUVAPixmaps_FromData(pixmaps, info.native(), data.into().into_ptr());
259 Self::native_is_valid(pixmaps)
260 })
261 }
262
263 /// Use passed in memory as backing store for pixmaps' pixels. Caller must ensure memory remains
264 /// allocated while pixmaps are in use. There must be at least
265 /// [YUVAPixmapInfo::computeTotalBytes(&self)] allocated starting at memory.
266 #[allow(clippy::missing_safety_doc)]
267 pub unsafe fn from_external_memory(info: &YUVAPixmapInfo, memory: *mut c_void) -> Option<Self> {
268 Self::try_construct(|pixmaps| {
269 sb::C_SkYUVAPixmaps_FromExternalMemory(pixmaps, info.native(), memory);
270 Self::native_is_valid(pixmaps)
271 })
272 }
273
274 /// Wraps existing `Pixmap`s. The [YUVAPixmaps] will have no ownership of the [Pixmap]s' pixel
275 /// memory so the caller must ensure it remains valid. Will return [None] if
276 /// the [YUVAInfo] isn't compatible with the [Pixmap] array (number of planes, plane dimensions,
277 /// sufficient color channels in planes, ...).
278 #[allow(clippy::missing_safety_doc)]
279 pub unsafe fn from_external_pixmaps(
280 info: &YUVAInfo,
281 pixmaps: &[Pixmap; Self::MAX_PLANES],
282 ) -> Option<Self> {
283 Self::try_construct(|pms| {
284 sb::C_SkYUVAPixmaps_FromExternalPixmaps(pms, info.native(), pixmaps[0].native());
285 Self::native_is_valid(pms)
286 })
287 }
288
289 pub fn yuva_info(&self) -> &YUVAInfo {
290 YUVAInfo::from_native_ref(&self.native().fYUVAInfo)
291 }
292
293 pub fn data_type(&self) -> DataType {
294 self.native().fDataType
295 }
296
297 pub fn pixmaps_info(&self) -> YUVAPixmapInfo {
298 YUVAPixmapInfo::construct(|info| unsafe {
299 sb::C_SkYUVAPixmaps_pixmapsInfo(self.native(), info)
300 })
301 }
302
303 /// Number of pixmap planes.
304 pub fn num_planes(&self) -> usize {
305 self.yuva_info().num_planes()
306 }
307
308 /// Access the [Pixmap] planes.
309 pub fn planes(&self) -> &[Pixmap] {
310 unsafe {
311 let planes = Pixmap::from_native_ptr(sb::C_SkYUVAPixmaps_planes(self.native()));
312 safer::from_raw_parts(planes, self.num_planes())
313 }
314 }
315
316 /// Get the ith [Pixmap] plane. `Pixmap` will be default initialized if i >= numPlanes.
317 pub fn plane(&self, i: usize) -> &Pixmap {
318 &self.planes()[i]
319 }
320
321 pub(crate) fn native_is_valid(pixmaps: *const SkYUVAPixmaps) -> bool {
322 unsafe { sb::C_SkYUVAPixmaps_isValid(pixmaps) }
323 }
324}
325
326pub mod yuva_pixmap_info {
327 use crate::{prelude::*, ColorType};
328 use skia_bindings::{self as sb, SkYUVAPixmapInfo_SupportedDataTypes};
329 use std::fmt;
330
331 pub use crate::yuva_info::PlaneConfig;
332 pub use crate::yuva_info::Subsampling;
333
334 /// Data type for Y, U, V, and possibly A channels independent of how values are packed into
335 /// planes.
336 pub use skia_bindings::SkYUVAPixmapInfo_DataType as DataType;
337
338 pub type SupportedDataTypes = Handle<SkYUVAPixmapInfo_SupportedDataTypes>;
339 unsafe_send_sync!(SupportedDataTypes);
340
341 impl NativeDrop for SkYUVAPixmapInfo_SupportedDataTypes {
342 fn drop(&mut self) {
343 unsafe { sb::C_SkYUVAPixmapInfo_SupportedDataTypes_destruct(self) }
344 }
345 }
346
347 impl Default for SupportedDataTypes {
348 /// Defaults to nothing supported.
349 fn default() -> Self {
350 Self::construct(|sdt| unsafe {
351 sb::C_SkYUVAPixmapInfo_SupportedDataTypes_Construct(sdt)
352 })
353 }
354 }
355
356 impl fmt::Debug for SupportedDataTypes {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 f.debug_struct("SupportedDataType")
359 .field("data_type_support", &self.native().fDataTypeSupport)
360 .finish()
361 }
362 }
363
364 impl SupportedDataTypes {
365 /// All legal combinations of [PlaneConfig] and [DataType] are supported.
366 pub fn all() -> Self {
367 Self::construct(|sdt| unsafe { sb::C_SkYUVAPixmapInfo_SupportedDataTypes_All(sdt) })
368 }
369
370 /// Checks whether there is a supported combination of color types for planes structured
371 /// as indicated by [PlaneConfig] with channel data types as indicated by [DataType].
372 pub fn supported(&self, pc: PlaneConfig, dt: DataType) -> bool {
373 unsafe { sb::C_SkYUVAPixmapInfo_SupportedDataTypes_supported(self.native(), pc, dt) }
374 }
375
376 /// Update to add support for pixmaps with `num_channels` channels where each channel is
377 /// represented as [DataType].
378 pub fn enable_data_type(&mut self, dt: DataType, num_channels: usize) {
379 unsafe {
380 self.native_mut()
381 .enableDataType(dt, num_channels.try_into().unwrap())
382 }
383 }
384 }
385
386 /// Gets the default [ColorType] to use with `num_channels` channels, each represented as [DataType].
387 /// Returns [ColorType::Unknown] if no such color type.
388 pub fn default_color_type_for_data_type(dt: DataType, num_channels: usize) -> ColorType {
389 ColorType::from_native_c(unsafe {
390 sb::C_SkYUVAPixmapInfo_DefaultColorTypeForDataType(dt, num_channels.try_into().unwrap())
391 })
392 }
393
394 /// If the [ColorType] is supported for YUVA pixmaps this will return the number of YUVA channels
395 /// that can be stored in a plane of this color type and what the [DataType] is of those channels.
396 /// If the [ColorType] is not supported as a YUVA plane the number of channels is reported as 0
397 /// and the [DataType] returned should be ignored.
398 pub fn num_channels_and_data_type(color_type: ColorType) -> (usize, DataType) {
399 let mut data_type = DataType::Float16;
400 let channels = unsafe {
401 sb::C_SkYUVAPixmapInfo_NumChannelsAndDataType(color_type.into_native(), &mut data_type)
402 };
403 (channels.try_into().unwrap(), data_type)
404 }
405}
406
407#[cfg(test)]
408mod tests {
409 use crate::{ColorType, YUVAPixmaps};
410
411 #[test]
412 fn recommended_color_type() {
413 assert_eq!(
414 YUVAPixmaps::recommended_rgba_color_type(super::DataType::Float16),
415 ColorType::RGBAF16
416 );
417 }
418}
419