| 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> + use<'_> { |
| 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 | |