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