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 | #[repr (C)] |
43 | #[derive (Copy, Clone, Debug)] |
44 | pub 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 | |
55 | native_transmutable!(SkCodec_FrameInfo, FrameInfo, frameinfo_layout); |
56 | |
57 | impl 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 | |
63 | pub use sb::SkCodec_SkScanlineOrder as ScanlineOrder; |
64 | variant_name!(ScanlineOrder::BottomUp); |
65 | |
66 | pub struct Codec<'a> { |
67 | inner: RefHandle<SkCodec>, |
68 | pd: PhantomData<&'a mut dyn io::Read>, |
69 | } |
70 | |
71 | impl NativeDrop for SkCodec { |
72 | fn drop(&mut self) { |
73 | unsafe { sb::C_SkCodec_delete(self) } |
74 | } |
75 | } |
76 | |
77 | impl 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 | |
91 | impl 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 | |
394 | pub 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 | |