Skip to main content

skia_safe/codec/
_codec.rs

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
20// TODO: implement Display
21
22pub 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                // Transfer ownership of the SkStream to the Codec.
120                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    // TODO: wrap from_data with SkPngChunkReader
136
137    // TODO: Deprecated in Skia
138    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    // TODO: getICCProfile()
170    // TODO: getHdrMetadata()
171
172    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    // TODO: Register
409
410    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                    // Transfer ownership of the SkStream to the Codec.
482                    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    // TODO: wrap Register()
495
496    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        // Even though codec is getting consumed / moved here, we need to preserve the borrow of the
502        // input stream.
503        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}