Skip to main content

skia_safe/core/
yuva_pixmaps.rs

1use crate::{ColorType, Data, ImageInfo, Pixmap, YUVAInfo, YUVColorSpace, prelude::*};
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, 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.debug_struct("YUVAPixmapInfo")
32            .field("yuva_info", &self.yuva_info())
33            .field("plane_infos", &plane_infos)
34            .field("row_bytes", &row_bytes)
35            .field("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).then(|| 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).then(|| 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.
128    ///
129    /// Returns [None] if `i` is out of range.
130    pub fn row_bytes(&self, i: usize) -> Option<usize> {
131        (i < self.num_planes()).then(|| unsafe {
132            sb::C_SkYUVAPixmapInfo_rowBytes(self.native(), i.try_into().unwrap())
133        })
134    }
135
136    /// Row bytes for all planes.
137    pub fn row_bytes_iter(&self) -> impl Iterator<Item = usize> + use<'_> {
138        (0..self.num_planes()).map(move |i| self.row_bytes(i).unwrap())
139    }
140
141    /// Image info for the ith plane.
142    ///
143    /// Returns [None] if `i` is out of range.
144    pub fn plane_info(&self, i: usize) -> Option<&ImageInfo> {
145        (i < self.num_planes()).then(|| {
146            ImageInfo::from_native_ref(unsafe {
147                &*sb::C_SkYUVAPixmapInfo_planeInfo(self.native(), i.try_into().unwrap())
148            })
149        })
150    }
151
152    /// An iterator of all planes' image infos.
153    pub fn plane_infos(&self) -> impl Iterator<Item = &ImageInfo> {
154        (0..self.num_planes()).map(move |i| self.plane_info(i).unwrap())
155    }
156
157    /// Determine size to allocate for all planes. Optionally retrieves the per-plane sizes in
158    /// planeSizes if not [None]. If total size overflows will return SIZE_MAX and set all
159    /// `plane_sizes` to SIZE_MAX.
160    pub fn compute_total_bytes(
161        &self,
162        plane_sizes: Option<&mut [usize; Self::MAX_PLANES]>,
163    ) -> usize {
164        unsafe {
165            self.native().computeTotalBytes(
166                plane_sizes
167                    .map(|ps| ps.as_mut_ptr())
168                    .unwrap_or(ptr::null_mut()),
169            )
170        }
171    }
172
173    /// Takes an allocation that is assumed to be at least [compute_total_bytes(&self)] in size and
174    /// configures the first [numPlanes(&self)] entries in pixmaps array to point into that memory.
175    /// The remaining entries of pixmaps are default initialized.
176    #[allow(clippy::missing_safety_doc)]
177    pub unsafe fn init_pixmaps_from_single_allocation(
178        &self,
179        memory: *mut c_void,
180    ) -> Option<[Pixmap; Self::MAX_PLANES]> {
181        unsafe {
182            // Can't return a Vec<Pixmap> because Pixmaps can't be cloned.
183            let mut pixmaps: [Pixmap; Self::MAX_PLANES] = Default::default();
184            self.native()
185                .initPixmapsFromSingleAllocation(memory, pixmaps[0].native_mut())
186                .then_some(pixmaps)
187        }
188    }
189
190    /// Is this valid and does it use color types allowed by the passed [SupportedDataTypes]?
191    pub fn is_supported(&self, data_types: &SupportedDataTypes) -> bool {
192        unsafe { self.native().isSupported(data_types.native()) }
193    }
194
195    pub(crate) fn new_if_valid(
196        set_pixmap_info: impl Fn(&mut SkYUVAPixmapInfo) -> bool,
197    ) -> Option<Self> {
198        let mut pixmap_info = Self::new_invalid();
199        let r = set_pixmap_info(&mut pixmap_info);
200        (r && Self::native_is_valid(&pixmap_info))
201            .then(|| YUVAPixmapInfo::from_native_c(pixmap_info))
202    }
203
204    /// Returns `true` if this has been configured with a non-empty dimensioned [YUVAInfo] with
205    /// compatible color types and row bytes.
206    fn native_is_valid(info: *const SkYUVAPixmapInfo) -> bool {
207        unsafe { sb::C_SkYUVAPixmapInfo_isValid(info) }
208    }
209
210    /// Creates a native default instance that is invalid.
211    fn new_invalid() -> SkYUVAPixmapInfo {
212        construct(|pi| unsafe { sb::C_SkYUVAPixmapInfo_Construct(pi) })
213    }
214}
215
216/// Helper to store [Pixmap] planes as described by a [YUVAPixmapInfo]. Can be responsible for
217/// allocating/freeing memory for pixmaps or use external memory.
218pub type YUVAPixmaps = Handle<SkYUVAPixmaps>;
219unsafe_send_sync!(YUVAPixmaps);
220
221impl NativeDrop for SkYUVAPixmaps {
222    fn drop(&mut self) {
223        unsafe { sb::C_SkYUVAPixmaps_destruct(self) }
224    }
225}
226
227impl NativeClone for SkYUVAPixmaps {
228    fn clone(&self) -> Self {
229        construct(|pixmaps| unsafe { sb::C_SkYUVAPixmaps_MakeCopy(self, pixmaps) })
230    }
231}
232
233impl fmt::Debug for YUVAPixmaps {
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        f.debug_struct("YUVAPixmaps")
236            .field("planes", &self.planes())
237            .field("yuva_info", &self.yuva_info())
238            .field("data_type", &self.data_type())
239            .finish()
240    }
241}
242
243impl YUVAPixmaps {
244    pub const MAX_PLANES: usize = YUVAPixmapInfo::MAX_PLANES;
245
246    pub fn recommended_rgba_color_type(dt: DataType) -> ColorType {
247        ColorType::from_native_c(unsafe { sb::SkYUVAPixmaps::RecommendedRGBAColorType(dt) })
248    }
249
250    /// Allocate space for pixmaps' pixels in the [YUVAPixmaps].
251    pub fn allocate(info: &YUVAPixmapInfo) -> Option<Self> {
252        Self::try_construct(|pixmaps| unsafe {
253            sb::C_SkYUVAPixmaps_Allocate(pixmaps, info.native());
254            Self::native_is_valid(pixmaps)
255        })
256    }
257
258    /// Use storage in [Data] as backing store for pixmaps' pixels. [Data] is retained by the
259    /// [YUVAPixmaps].
260    pub fn from_data(info: &YUVAPixmapInfo, data: impl Into<Data>) -> Option<Self> {
261        Self::try_construct(|pixmaps| unsafe {
262            sb::C_SkYUVAPixmaps_FromData(pixmaps, info.native(), data.into().into_ptr());
263            Self::native_is_valid(pixmaps)
264        })
265    }
266
267    /// Use passed in memory as backing store for pixmaps' pixels. Caller must ensure memory remains
268    /// allocated while pixmaps are in use. There must be at least
269    /// [YUVAPixmapInfo::computeTotalBytes(&self)] allocated starting at memory.
270    #[allow(clippy::missing_safety_doc)]
271    pub unsafe fn from_external_memory(info: &YUVAPixmapInfo, memory: *mut c_void) -> Option<Self> {
272        unsafe {
273            Self::try_construct(|pixmaps| {
274                sb::C_SkYUVAPixmaps_FromExternalMemory(pixmaps, info.native(), memory);
275                Self::native_is_valid(pixmaps)
276            })
277        }
278    }
279
280    /// Wraps existing `Pixmap`s. The [YUVAPixmaps] will have no ownership of the [Pixmap]s' pixel
281    /// memory so the caller must ensure it remains valid. Will return [None] if
282    /// the [YUVAInfo] isn't compatible with the [Pixmap] array (number of planes, plane dimensions,
283    /// sufficient color channels in planes, ...).
284    #[allow(clippy::missing_safety_doc)]
285    pub unsafe fn from_external_pixmaps(
286        info: &YUVAInfo,
287        pixmaps: &[Pixmap; Self::MAX_PLANES],
288    ) -> Option<Self> {
289        unsafe {
290            Self::try_construct(|pms| {
291                sb::C_SkYUVAPixmaps_FromExternalPixmaps(pms, info.native(), pixmaps[0].native());
292                Self::native_is_valid(pms)
293            })
294        }
295    }
296
297    pub fn yuva_info(&self) -> &YUVAInfo {
298        YUVAInfo::from_native_ref(&self.native().fYUVAInfo)
299    }
300
301    pub fn data_type(&self) -> DataType {
302        self.native().fDataType
303    }
304
305    pub fn pixmaps_info(&self) -> YUVAPixmapInfo {
306        YUVAPixmapInfo::construct(|info| unsafe {
307            sb::C_SkYUVAPixmaps_pixmapsInfo(self.native(), info)
308        })
309    }
310
311    /// Number of pixmap planes.
312    pub fn num_planes(&self) -> usize {
313        self.yuva_info().num_planes()
314    }
315
316    /// Access the [Pixmap] planes.
317    pub fn planes(&self) -> &[Pixmap] {
318        unsafe {
319            let planes = Pixmap::from_native_ptr(sb::C_SkYUVAPixmaps_planes(self.native()));
320            safer::from_raw_parts(planes, self.num_planes())
321        }
322    }
323
324    /// Get the ith [Pixmap] plane.
325    ///
326    /// Panics if `i` is out of range.
327    pub fn plane(&self, i: usize) -> &Pixmap {
328        &self.planes()[i]
329    }
330
331    pub(crate) fn native_is_valid(pixmaps: *const SkYUVAPixmaps) -> bool {
332        unsafe { sb::C_SkYUVAPixmaps_isValid(pixmaps) }
333    }
334}
335
336pub mod yuva_pixmap_info {
337    use crate::{ColorType, prelude::*};
338    use skia_bindings::{self as sb, SkYUVAPixmapInfo_SupportedDataTypes};
339    use std::fmt;
340
341    pub use crate::yuva_info::PlaneConfig;
342    pub use crate::yuva_info::Subsampling;
343
344    /// Data type for Y, U, V, and possibly A channels independent of how values are packed into
345    /// planes.
346    pub use skia_bindings::SkYUVAPixmapInfo_DataType as DataType;
347
348    pub type SupportedDataTypes = Handle<SkYUVAPixmapInfo_SupportedDataTypes>;
349    unsafe_send_sync!(SupportedDataTypes);
350
351    impl NativeDrop for SkYUVAPixmapInfo_SupportedDataTypes {
352        fn drop(&mut self) {
353            unsafe { sb::C_SkYUVAPixmapInfo_SupportedDataTypes_destruct(self) }
354        }
355    }
356
357    impl Default for SupportedDataTypes {
358        /// Defaults to nothing supported.
359        fn default() -> Self {
360            Self::construct(|sdt| unsafe {
361                sb::C_SkYUVAPixmapInfo_SupportedDataTypes_Construct(sdt)
362            })
363        }
364    }
365
366    impl fmt::Debug for SupportedDataTypes {
367        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
368            f.debug_struct("SupportedDataType")
369                .field("data_type_support", &self.native().fDataTypeSupport)
370                .finish()
371        }
372    }
373
374    impl SupportedDataTypes {
375        /// All legal combinations of [PlaneConfig] and [DataType] are supported.
376        pub fn all() -> Self {
377            Self::construct(|sdt| unsafe { sb::C_SkYUVAPixmapInfo_SupportedDataTypes_All(sdt) })
378        }
379
380        /// Checks whether there is a supported combination of color types for planes structured
381        /// as indicated by [PlaneConfig] with channel data types as indicated by [DataType].
382        pub fn supported(&self, pc: PlaneConfig, dt: DataType) -> bool {
383            unsafe { sb::C_SkYUVAPixmapInfo_SupportedDataTypes_supported(self.native(), pc, dt) }
384        }
385
386        /// Update to add support for pixmaps with `num_channels` channels where each channel is
387        /// represented as [DataType].
388        pub fn enable_data_type(&mut self, dt: DataType, num_channels: usize) {
389            unsafe {
390                self.native_mut()
391                    .enableDataType(dt, num_channels.try_into().unwrap())
392            }
393        }
394    }
395
396    /// Gets the default [ColorType] to use with `num_channels` channels, each represented as [DataType].
397    /// Returns [ColorType::Unknown] if no such color type.
398    pub fn default_color_type_for_data_type(dt: DataType, num_channels: usize) -> ColorType {
399        ColorType::from_native_c(unsafe {
400            sb::C_SkYUVAPixmapInfo_DefaultColorTypeForDataType(dt, num_channels.try_into().unwrap())
401        })
402    }
403
404    /// If the [ColorType] is supported for YUVA pixmaps this will return the number of YUVA channels
405    /// that can be stored in a plane of this color type and what the [DataType] is of those channels.
406    /// If the [ColorType] is not supported as a YUVA plane the number of channels is reported as 0
407    /// and the [DataType] returned should be ignored.
408    pub fn num_channels_and_data_type(color_type: ColorType) -> (usize, DataType) {
409        let mut data_type = DataType::Float16;
410        let channels = unsafe {
411            sb::C_SkYUVAPixmapInfo_NumChannelsAndDataType(color_type.into_native(), &mut data_type)
412        };
413        (channels.try_into().unwrap(), data_type)
414    }
415}
416
417#[cfg(test)]
418mod tests {
419    use crate::{ColorType, YUVAPixmaps};
420
421    #[test]
422    fn recommended_color_type() {
423        assert_eq!(
424            YUVAPixmaps::recommended_rgba_color_type(super::DataType::Float16),
425            ColorType::RGBAF16
426        );
427    }
428}