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 | |