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 interop::RustStream, prelude::*, yuva_pixmap_info::SupportedDataTypes, AlphaType, Data,
13 EncodedImageFormat, EncodedOrigin, IRect, ISize, Image, ImageInfo, Pixmap, YUVAPixmapInfo,
14 YUVAPixmaps,
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 unsafeResult<&str, Utf8Error> { CStr::from_ptr(skia_bindings::SkCodec_ResultToString(arg1: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}
41
42pub const NO_FRAME: i32 = sb::SkCodec_kNoFrame;
43
44#[repr(C)]
45#[derive(Copy, Clone, Debug)]
46pub 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
57native_transmutable!(SkCodec_FrameInfo, FrameInfo, frameinfo_layout);
58
59impl 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
65pub use sb::SkCodec_SkScanlineOrder as ScanlineOrder;
66variant_name!(ScanlineOrder::BottomUp);
67
68pub use sb::SkCodec_IsAnimated as IsAnimated;
69variant_name!(IsAnimated::Yes);
70
71pub struct Codec<'a> {
72 inner: RefHandle<SkCodec>,
73 pd: PhantomData<&'a mut dyn io::Read>,
74}
75
76impl NativeDrop for SkCodec {
77 fn drop(&mut self) {
78 unsafe { sb::C_SkCodec_delete(self) }
79 }
80}
81
82impl 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
96impl 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
410pub 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