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