| 1 | use std::{ |
| 2 | ffi::{self, CStr}, |
| 3 | fmt, io, |
| 4 | marker::PhantomData, |
| 5 | mem, ptr, result, |
| 6 | }; |
| 7 | |
| 8 | use skia_bindings::{self as sb, SkCodec, SkCodec_FrameInfo, SkCodec_Options}; |
| 9 | |
| 10 | use super::codec_animation; |
| 11 | use crate::{ |
| 12 | interop::RustStream, prelude::*, yuva_pixmap_info::SupportedDataTypes, AlphaType, Data, |
| 13 | EncodedImageFormat, EncodedOrigin, IRect, ISize, Image, ImageInfo, Pixmap, YUVAPixmapInfo, |
| 14 | YUVAPixmaps, |
| 15 | }; |
| 16 | |
| 17 | pub use sb::SkCodec_Result as Result; |
| 18 | variant_name!(Result::IncompleteInput); |
| 19 | |
| 20 | // TODO: implement Display |
| 21 | |
| 22 | pub fn result_to_string(result: Result) -> &'static str { |
| 23 | unsafeResult<&str, Utf8Error> { CStr::from_ptr(skia_bindings::SkCodec_ResultToString(arg1:result)) } |
| 24 | .to_str() |
| 25 | .unwrap() |
| 26 | } |
| 27 | |
| 28 | pub use sb::SkCodec_SelectionPolicy as SelectionPolicy; |
| 29 | variant_name!(SelectionPolicy::PreferStillImage); |
| 30 | |
| 31 | pub use sb::SkCodec_ZeroInitialized as ZeroInitialized; |
| 32 | variant_name!(ZeroInitialized::Yes); |
| 33 | |
| 34 | #[derive (Copy, Clone, PartialEq, Eq, Debug)] |
| 35 | pub struct Options { |
| 36 | pub zero_initialized: ZeroInitialized, |
| 37 | pub subset: Option<IRect>, |
| 38 | pub frame_index: usize, |
| 39 | pub prior_frame: Option<usize>, |
| 40 | } |
| 41 | |
| 42 | pub const NO_FRAME: i32 = sb::SkCodec_kNoFrame; |
| 43 | |
| 44 | #[repr (C)] |
| 45 | #[derive (Copy, Clone, Debug)] |
| 46 | pub struct FrameInfo { |
| 47 | pub required_frame: i32, |
| 48 | pub duration: i32, |
| 49 | pub fully_received: bool, |
| 50 | pub alpha_type: AlphaType, |
| 51 | pub has_alpha_within_bounds: bool, |
| 52 | pub disposal_method: codec_animation::DisposalMethod, |
| 53 | pub blend: codec_animation::Blend, |
| 54 | pub rect: IRect, |
| 55 | } |
| 56 | |
| 57 | native_transmutable!(SkCodec_FrameInfo, FrameInfo, frameinfo_layout); |
| 58 | |
| 59 | impl Default for FrameInfo { |
| 60 | fn default() -> Self { |
| 61 | Self::construct(|frame_info: *mut SkCodec_FrameInfo| unsafe { sb::C_SkFrameInfo_Construct(uninitialized:frame_info) }) |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | pub use sb::SkCodec_SkScanlineOrder as ScanlineOrder; |
| 66 | variant_name!(ScanlineOrder::BottomUp); |
| 67 | |
| 68 | pub use sb::SkCodec_IsAnimated as IsAnimated; |
| 69 | variant_name!(IsAnimated::Yes); |
| 70 | |
| 71 | pub struct Codec<'a> { |
| 72 | inner: RefHandle<SkCodec>, |
| 73 | pd: PhantomData<&'a mut dyn io::Read>, |
| 74 | } |
| 75 | |
| 76 | impl NativeDrop for SkCodec { |
| 77 | fn drop(&mut self) { |
| 78 | unsafe { sb::C_SkCodec_delete(self) } |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | impl fmt::Debug for Codec<'_> { |
| 83 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 84 | f&mut DebugStruct<'_, '_>.debug_struct("Codec" ) |
| 85 | .field("info" , &self.info()) |
| 86 | .field("dimensions" , &self.dimensions()) |
| 87 | .field("bounds" , &self.bounds()) |
| 88 | .field("origin" , &self.origin()) |
| 89 | .field("encoded_format" , &self.encoded_format()) |
| 90 | .field("scanline_order" , &self.scanline_order()) |
| 91 | .field(name:"next_scanline" , &self.next_scanline()) |
| 92 | .finish() |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | impl Codec<'_> { |
| 97 | pub fn from_stream<'a, T: io::Read + io::Seek>( |
| 98 | stream: &'a mut T, |
| 99 | decoders: &[codecs::Decoder], |
| 100 | selection_policy: impl Into<Option<SelectionPolicy>>, |
| 101 | ) -> result::Result<Codec<'a>, Result> { |
| 102 | let stream = RustStream::new_seekable(stream); |
| 103 | let mut result = Result::Unimplemented; |
| 104 | let codec = unsafe { |
| 105 | sb::C_SkCodec_MakeFromStream( |
| 106 | // Transfer ownership of the SkStream to the Codec. |
| 107 | stream.into_native(), |
| 108 | decoders.as_ptr() as _, |
| 109 | decoders.len(), |
| 110 | &mut result, |
| 111 | selection_policy |
| 112 | .into() |
| 113 | .unwrap_or(SelectionPolicy::PreferStillImage), |
| 114 | ) |
| 115 | }; |
| 116 | if result != Result::Success { |
| 117 | return Err(result); |
| 118 | } |
| 119 | Ok(Codec::from_ptr(codec).expect("Codec is null" )) |
| 120 | } |
| 121 | |
| 122 | // TODO: wrap from_data with SkPngChunkReader |
| 123 | |
| 124 | // TODO: Deprecated in Skia |
| 125 | pub fn from_data(data: impl Into<Data>) -> Option<Codec<'static>> { |
| 126 | Self::from_ptr(unsafe { sb::C_SkCodec_MakeFromData(data.into().into_ptr()) }) |
| 127 | } |
| 128 | |
| 129 | pub fn from_data_with_decoders( |
| 130 | data: impl Into<Data>, |
| 131 | decoders: &[codecs::Decoder], |
| 132 | ) -> Option<Codec<'static>> { |
| 133 | Self::from_ptr(unsafe { |
| 134 | sb::C_SkCodec_MakeFromData2( |
| 135 | data.into().into_ptr(), |
| 136 | decoders.as_ptr() as _, |
| 137 | decoders.len(), |
| 138 | ) |
| 139 | }) |
| 140 | } |
| 141 | |
| 142 | pub fn info(&self) -> ImageInfo { |
| 143 | let mut info = ImageInfo::default(); |
| 144 | unsafe { sb::C_SkCodec_getInfo(self.native(), info.native_mut()) }; |
| 145 | info |
| 146 | } |
| 147 | |
| 148 | pub fn dimensions(&self) -> ISize { |
| 149 | ISize::from_native_c(unsafe { sb::C_SkCodec_dimensions(self.native()) }) |
| 150 | } |
| 151 | |
| 152 | pub fn bounds(&self) -> IRect { |
| 153 | IRect::construct(|r| unsafe { sb::C_SkCodec_bounds(self.native(), r) }) |
| 154 | } |
| 155 | |
| 156 | // TODO: getICCProfile |
| 157 | |
| 158 | pub fn has_high_bit_depth_encoded_data(&self) -> bool { |
| 159 | unsafe { sb::C_SkCodec_hasHighBitDepthEncodedData(self.native()) } |
| 160 | } |
| 161 | |
| 162 | pub fn origin(&self) -> EncodedOrigin { |
| 163 | EncodedOrigin::from_native_c(unsafe { sb::C_SkCodec_getOrigin(self.native()) }) |
| 164 | } |
| 165 | |
| 166 | pub fn get_scaled_dimensions(&self, desired_scale: f32) -> ISize { |
| 167 | ISize::from_native_c(unsafe { |
| 168 | sb::C_SkCodec_getScaledDimensions(self.native(), desired_scale) |
| 169 | }) |
| 170 | } |
| 171 | |
| 172 | pub fn valid_subset(&self, desired_subset: impl AsRef<IRect>) -> Option<IRect> { |
| 173 | let mut desired_subset = *desired_subset.as_ref(); |
| 174 | unsafe { sb::C_SkCodec_getValidSubset(self.native(), desired_subset.native_mut()) } |
| 175 | .if_true_some(desired_subset) |
| 176 | } |
| 177 | |
| 178 | pub fn encoded_format(&self) -> EncodedImageFormat { |
| 179 | unsafe { sb::C_SkCodec_getEncodedFormat(self.native()) } |
| 180 | } |
| 181 | |
| 182 | // TODO: May wrap `getEncodedData()`. But how? It would return the stream, which is already |
| 183 | // mutably borrowed. |
| 184 | |
| 185 | pub fn get_pixels_with_options( |
| 186 | &mut self, |
| 187 | info: &ImageInfo, |
| 188 | pixels: &mut [u8], |
| 189 | row_bytes: usize, |
| 190 | options: Option<&Options>, |
| 191 | ) -> Result { |
| 192 | assert_eq!(pixels.len(), info.compute_byte_size(row_bytes)); |
| 193 | unsafe { |
| 194 | let native_options = options.map(|options| Self::native_options(options)); |
| 195 | self.native_mut().getPixels( |
| 196 | info.native(), |
| 197 | pixels.as_mut_ptr() as *mut _, |
| 198 | row_bytes, |
| 199 | native_options.as_ptr_or_null(), |
| 200 | ) |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | #[deprecated ( |
| 205 | since = "0.33.1" , |
| 206 | note = "Use the safe variant get_pixels_with_options()." |
| 207 | )] |
| 208 | #[allow (clippy::missing_safety_doc)] |
| 209 | pub unsafe fn get_pixels( |
| 210 | &mut self, |
| 211 | info: &ImageInfo, |
| 212 | pixels: *mut ffi::c_void, |
| 213 | row_bytes: usize, |
| 214 | ) -> Result { |
| 215 | self.native_mut() |
| 216 | .getPixels(info.native(), pixels, row_bytes, ptr::null()) |
| 217 | } |
| 218 | |
| 219 | #[allow (clippy::missing_safety_doc)] |
| 220 | pub unsafe fn get_pixels_to_pixmap( |
| 221 | &mut self, |
| 222 | pixmap: &Pixmap, |
| 223 | options: Option<&Options>, |
| 224 | ) -> Result { |
| 225 | let native_options = options.map(|options| Self::native_options(options)); |
| 226 | self.native_mut().getPixels( |
| 227 | pixmap.info().native(), |
| 228 | pixmap.writable_addr(), |
| 229 | pixmap.row_bytes(), |
| 230 | native_options.as_ptr_or_null(), |
| 231 | ) |
| 232 | } |
| 233 | |
| 234 | unsafe fn native_options(options: &Options) -> SkCodec_Options { |
| 235 | SkCodec_Options { |
| 236 | fZeroInitialized: options.zero_initialized, |
| 237 | fSubset: options.subset.native().as_ptr_or_null(), |
| 238 | fFrameIndex: options.frame_index.try_into().unwrap(), |
| 239 | fPriorFrame: match options.prior_frame { |
| 240 | None => sb::SkCodec_kNoFrame, |
| 241 | Some(frame) => frame.try_into().expect("invalid prior frame" ), |
| 242 | }, |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | pub fn get_image<'a>( |
| 247 | &mut self, |
| 248 | info: impl Into<Option<ImageInfo>>, |
| 249 | options: impl Into<Option<&'a Options>>, |
| 250 | ) -> std::result::Result<Image, Result> { |
| 251 | let info = info.into().unwrap_or_else(|| self.info()); |
| 252 | let options = options |
| 253 | .into() |
| 254 | .map(|options| unsafe { Self::native_options(options) }); |
| 255 | let mut result = Result::InternalError; |
| 256 | Image::from_ptr(unsafe { |
| 257 | sb::C_SkCodec_getImage( |
| 258 | self.native_mut(), |
| 259 | info.native(), |
| 260 | options.as_ptr_or_null(), |
| 261 | &mut result, |
| 262 | ) |
| 263 | }) |
| 264 | .ok_or(result) |
| 265 | } |
| 266 | |
| 267 | pub fn query_yuva_info( |
| 268 | &self, |
| 269 | supported_data_types: &SupportedDataTypes, |
| 270 | ) -> Option<YUVAPixmapInfo> { |
| 271 | YUVAPixmapInfo::new_if_valid(|pixmap_info| unsafe { |
| 272 | self.native() |
| 273 | .queryYUVAInfo(supported_data_types.native(), pixmap_info) |
| 274 | }) |
| 275 | } |
| 276 | |
| 277 | pub fn get_yuva_planes(&mut self, pixmaps: &YUVAPixmaps) -> Result { |
| 278 | unsafe { self.native_mut().getYUVAPlanes(pixmaps.native()) } |
| 279 | } |
| 280 | |
| 281 | pub fn start_incremental_decode<'a>( |
| 282 | &mut self, |
| 283 | dst_info: &ImageInfo, |
| 284 | dst: &mut [u8], |
| 285 | row_bytes: usize, |
| 286 | options: impl Into<Option<&'a Options>>, |
| 287 | ) -> Result { |
| 288 | if !dst_info.valid_pixels(row_bytes, dst) { |
| 289 | return Result::InvalidParameters; |
| 290 | } |
| 291 | let options = options |
| 292 | .into() |
| 293 | .map(|options| unsafe { Self::native_options(options) }); |
| 294 | unsafe { |
| 295 | self.native_mut().startIncrementalDecode( |
| 296 | dst_info.native(), |
| 297 | dst.as_mut_ptr() as _, |
| 298 | row_bytes, |
| 299 | options.as_ptr_or_null(), |
| 300 | ) |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | pub fn incremental_decode(&mut self) -> (Result, Option<usize>) { |
| 305 | let mut rows_decoded = Default::default(); |
| 306 | let r = unsafe { sb::C_SkCodec_incrementalDecode(self.native_mut(), &mut rows_decoded) }; |
| 307 | if r == Result::IncompleteInput { |
| 308 | (r, Some(rows_decoded.try_into().unwrap())) |
| 309 | } else { |
| 310 | (r, None) |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | pub fn start_scanline_decode<'a>( |
| 315 | &mut self, |
| 316 | dst_info: &ImageInfo, |
| 317 | options: impl Into<Option<&'a Options>>, |
| 318 | ) -> Result { |
| 319 | let options = options |
| 320 | .into() |
| 321 | .map(|options| unsafe { Self::native_options(options) }); |
| 322 | unsafe { |
| 323 | self.native_mut() |
| 324 | .startScanlineDecode(dst_info.native(), options.as_ptr_or_null()) |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | pub fn get_scanlines(&mut self, dst: &mut [u8], count_lines: usize, row_bytes: usize) -> usize { |
| 329 | assert!(mem::size_of_val(dst) >= count_lines * row_bytes); |
| 330 | unsafe { |
| 331 | self.native_mut().getScanlines( |
| 332 | dst.as_mut_ptr() as _, |
| 333 | count_lines.try_into().unwrap(), |
| 334 | row_bytes, |
| 335 | ) |
| 336 | } |
| 337 | .try_into() |
| 338 | .unwrap() |
| 339 | } |
| 340 | |
| 341 | pub fn skip_scanlines(&mut self, count_lines: usize) -> bool { |
| 342 | unsafe { |
| 343 | self.native_mut() |
| 344 | .skipScanlines(count_lines.try_into().unwrap()) |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | pub fn scanline_order(&self) -> ScanlineOrder { |
| 349 | unsafe { sb::C_SkCodec_getScanlineOrder(self.native()) } |
| 350 | } |
| 351 | |
| 352 | pub fn next_scanline(&self) -> i32 { |
| 353 | unsafe { sb::C_SkCodec_nextScanline(self.native()) } |
| 354 | } |
| 355 | |
| 356 | pub fn outbound_scanline(&self, input_scanline: i32) -> i32 { |
| 357 | unsafe { self.native().outputScanline(input_scanline) } |
| 358 | } |
| 359 | |
| 360 | pub fn get_frame_count(&mut self) -> usize { |
| 361 | unsafe { sb::C_SkCodec_getFrameCount(self.native_mut()) } |
| 362 | .try_into() |
| 363 | .unwrap() |
| 364 | } |
| 365 | |
| 366 | pub fn get_frame_info(&mut self, index: usize) -> Option<FrameInfo> { |
| 367 | let mut info = FrameInfo::default(); |
| 368 | unsafe { |
| 369 | sb::C_SkCodec_getFrameInfo( |
| 370 | self.native_mut(), |
| 371 | index.try_into().unwrap(), |
| 372 | info.native_mut(), |
| 373 | ) |
| 374 | } |
| 375 | .then_some(info) |
| 376 | } |
| 377 | |
| 378 | pub fn get_repetition_count(&mut self) -> Option<usize> { |
| 379 | const REPETITION_COUNT_INFINITE: i32 = -1; |
| 380 | let count = unsafe { sb::C_SkCodec_getRepetitionCount(self.native_mut()) }; |
| 381 | if count != REPETITION_COUNT_INFINITE { |
| 382 | Some(count.try_into().unwrap()) |
| 383 | } else { |
| 384 | None |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | pub fn is_animated(&mut self) -> IsAnimated { |
| 389 | unsafe { sb::C_SkCodec_isAnimated(self.native_mut()) } |
| 390 | } |
| 391 | |
| 392 | // TODO: Register |
| 393 | |
| 394 | fn native(&self) -> &SkCodec { |
| 395 | self.inner.native() |
| 396 | } |
| 397 | |
| 398 | fn native_mut(&mut self) -> &mut SkCodec { |
| 399 | self.inner.native_mut() |
| 400 | } |
| 401 | |
| 402 | pub(crate) fn from_ptr<'a>(codec: *mut SkCodec) -> Option<Codec<'a>> { |
| 403 | RefHandle::from_ptr(codec).map(|inner| Codec { |
| 404 | inner, |
| 405 | pd: PhantomData, |
| 406 | }) |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | pub mod codecs { |
| 411 | use std::{fmt, io, ptr, result, str}; |
| 412 | |
| 413 | use skia_bindings::{self as sb, SkCodecs_Decoder}; |
| 414 | |
| 415 | use super::Result; |
| 416 | use crate::{interop::RustStream, prelude::*, AlphaType, Codec, Image}; |
| 417 | |
| 418 | pub type Decoder = Handle<SkCodecs_Decoder>; |
| 419 | unsafe_send_sync!(Decoder); |
| 420 | |
| 421 | impl fmt::Debug for Decoder { |
| 422 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 423 | f.debug_struct("Decoder" ).field("id" , &self.id()).finish() |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | impl NativeDrop for SkCodecs_Decoder { |
| 428 | fn drop(&mut self) { |
| 429 | unsafe { sb::C_SkCodecs_Decoder_destruct(self) } |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | impl NativeClone for SkCodecs_Decoder { |
| 434 | fn clone(&self) -> Self { |
| 435 | construct(|d| unsafe { sb::C_SkCodecs_Decoder_CopyConstruct(d, self) }) |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | impl Decoder { |
| 440 | pub fn id(&self) -> &'static str { |
| 441 | let mut len: usize = 0; |
| 442 | let ptr = unsafe { sb::C_SkCodecs_Decoder_getId(self.native(), &mut len) }; |
| 443 | let chars = unsafe { safer::from_raw_parts(ptr as _, len) }; |
| 444 | str::from_utf8(chars).expect("Invalid UTF-8 decoder id" ) |
| 445 | } |
| 446 | |
| 447 | pub fn is_format(&self, data: &[u8]) -> bool { |
| 448 | unsafe { |
| 449 | (self.native().isFormat.expect("Decoder::isFormat is null" ))( |
| 450 | data.as_ptr() as _, |
| 451 | data.len(), |
| 452 | ) |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | pub fn from_stream<'a>( |
| 457 | &self, |
| 458 | stream: &'a mut impl io::Read, |
| 459 | ) -> result::Result<Codec<'a>, Result> { |
| 460 | let stream = RustStream::new(stream); |
| 461 | let mut result = Result::Unimplemented; |
| 462 | let codec = unsafe { |
| 463 | sb::C_SkCodecs_Decoder_MakeFromStream( |
| 464 | self.native(), |
| 465 | // Transfer ownership of the SkStream to the Codec. |
| 466 | stream.into_native(), |
| 467 | &mut result, |
| 468 | ptr::null_mut(), |
| 469 | ) |
| 470 | }; |
| 471 | if result != Result::Success { |
| 472 | return Err(result); |
| 473 | } |
| 474 | Ok(Codec::from_ptr(codec).expect("Codec is null" )) |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | // TODO: wrap Register() |
| 479 | |
| 480 | pub fn deferred_image( |
| 481 | codec: Codec<'_>, |
| 482 | alpha_type: impl Into<Option<AlphaType>>, |
| 483 | ) -> Option<Borrows<'_, Image>> { |
| 484 | let alpha_type: Option<AlphaType> = alpha_type.into(); |
| 485 | // Even though codec is getting consumed / moved here, we need to preserve the borrow of the |
| 486 | // input stream. |
| 487 | Image::from_ptr(unsafe { |
| 488 | sb::C_SkCodecs_DeferredImage(codec.inner.into_ptr(), alpha_type.as_ptr_or_null()) |
| 489 | }) |
| 490 | .map(|h| unsafe { Borrows::unchecked_new(h) }) |
| 491 | } |
| 492 | } |
| 493 | |