1 | use crate::{prelude::*, ColorType, Data, ImageInfo, Pixmap, YUVAInfo, YUVColorSpace}; |
2 | use skia_bindings::{self as sb, SkYUVAPixmapInfo, SkYUVAPixmaps}; |
3 | use std::{ffi::c_void, fmt, ptr}; |
4 | use yuva_pixmap_info::SupportedDataTypes; |
5 | |
6 | /// Data type for Y, U, V, and possibly A channels independent of how values are packed into planes. |
7 | pub use yuva_pixmap_info::DataType; |
8 | variant_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. |
12 | pub type YUVAPixmapInfo = Handle<SkYUVAPixmapInfo>; |
13 | unsafe_send_sync!(YUVAPixmapInfo); |
14 | |
15 | impl NativeDrop for SkYUVAPixmapInfo { |
16 | fn drop(&mut self) { |
17 | unsafe { sb::C_SkYUVAPixmapInfo_destruct(self) } |
18 | } |
19 | } |
20 | |
21 | impl NativePartialEq for SkYUVAPixmapInfo { |
22 | fn eq(&self, rhs: &Self) -> bool { |
23 | unsafe { sb::C_SkYUVAPixmapInfo_equals(self, b:rhs) } |
24 | } |
25 | } |
26 | |
27 | impl 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 | |
40 | impl 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. |
214 | pub type YUVAPixmaps = Handle<SkYUVAPixmaps>; |
215 | unsafe_send_sync!(YUVAPixmaps); |
216 | |
217 | impl NativeDrop for SkYUVAPixmaps { |
218 | fn drop(&mut self) { |
219 | unsafe { sb::C_SkYUVAPixmaps_destruct(self) } |
220 | } |
221 | } |
222 | |
223 | impl NativeClone for SkYUVAPixmaps { |
224 | fn clone(&self) -> Self { |
225 | construct(|pixmaps: *mut SkYUVAPixmaps| unsafe { sb::C_SkYUVAPixmaps_MakeCopy(self, uninitialized:pixmaps) }) |
226 | } |
227 | } |
228 | |
229 | impl 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 | |
239 | impl 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 | |
326 | pub 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)] |
408 | mod 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 | |