xref: /aosp_15_r20/external/skia/experimental/rust_png/ffi/FFI.rs (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker // Copyright 2024 Google LLC
2*c8dee2aaSAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*c8dee2aaSAndroid Build Coastguard Worker // found in the LICENSE file.
4*c8dee2aaSAndroid Build Coastguard Worker 
5*c8dee2aaSAndroid Build Coastguard Worker //! This crate provides C++ bindings for the `png` Rust crate.
6*c8dee2aaSAndroid Build Coastguard Worker //!
7*c8dee2aaSAndroid Build Coastguard Worker //! The public API of this crate is the C++ API declared by the `#[cxx::bridge]`
8*c8dee2aaSAndroid Build Coastguard Worker //! macro below and exposed through the auto-generated `FFI.rs.h` header.
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker use std::io::{ErrorKind, Read, Write};
11*c8dee2aaSAndroid Build Coastguard Worker use std::pin::Pin;
12*c8dee2aaSAndroid Build Coastguard Worker 
13*c8dee2aaSAndroid Build Coastguard Worker // No `use png::...` nor `use ffi::...` because we want the code to explicitly
14*c8dee2aaSAndroid Build Coastguard Worker // spell out if it means `ffi::ColorType` vs `png::ColorType` (or `Reader`
15*c8dee2aaSAndroid Build Coastguard Worker // vs `png::Reader`).
16*c8dee2aaSAndroid Build Coastguard Worker 
17*c8dee2aaSAndroid Build Coastguard Worker #[cxx::bridge(namespace = "rust_png")]
18*c8dee2aaSAndroid Build Coastguard Worker mod ffi {
19*c8dee2aaSAndroid Build Coastguard Worker     /// FFI-friendly equivalent of `png::ColorType`.
20*c8dee2aaSAndroid Build Coastguard Worker     enum ColorType {
21*c8dee2aaSAndroid Build Coastguard Worker         Grayscale = 0,
22*c8dee2aaSAndroid Build Coastguard Worker         Rgb = 2,
23*c8dee2aaSAndroid Build Coastguard Worker         Indexed = 3,
24*c8dee2aaSAndroid Build Coastguard Worker         GrayscaleAlpha = 4,
25*c8dee2aaSAndroid Build Coastguard Worker         Rgba = 6,
26*c8dee2aaSAndroid Build Coastguard Worker     }
27*c8dee2aaSAndroid Build Coastguard Worker 
28*c8dee2aaSAndroid Build Coastguard Worker     /// FFI-friendly simplification of `Option<png::DecodingError>`.
29*c8dee2aaSAndroid Build Coastguard Worker     enum DecodingResult {
30*c8dee2aaSAndroid Build Coastguard Worker         Success,
31*c8dee2aaSAndroid Build Coastguard Worker         FormatError,
32*c8dee2aaSAndroid Build Coastguard Worker         ParameterError,
33*c8dee2aaSAndroid Build Coastguard Worker         LimitsExceededError,
34*c8dee2aaSAndroid Build Coastguard Worker         /// `IncompleteInput` is equivalent to `png::DecodingError::IoError(
35*c8dee2aaSAndroid Build Coastguard Worker         /// std::io::ErrorKind::UnexpectedEof.into())`.  It is named after
36*c8dee2aaSAndroid Build Coastguard Worker         /// `SkCodec::Result::kIncompleteInput`.
37*c8dee2aaSAndroid Build Coastguard Worker         ///
38*c8dee2aaSAndroid Build Coastguard Worker         /// `ReadTrait` is infallible and therefore we provide no generic
39*c8dee2aaSAndroid Build Coastguard Worker         /// equivalent of the `png::DecodingError::IoError` variant
40*c8dee2aaSAndroid Build Coastguard Worker         /// (other than the special case of `IncompleteInput`).
41*c8dee2aaSAndroid Build Coastguard Worker         IncompleteInput,
42*c8dee2aaSAndroid Build Coastguard Worker     }
43*c8dee2aaSAndroid Build Coastguard Worker 
44*c8dee2aaSAndroid Build Coastguard Worker     /// FFI-friendly equivalent of `png::DisposeOp`.
45*c8dee2aaSAndroid Build Coastguard Worker     enum DisposeOp {
46*c8dee2aaSAndroid Build Coastguard Worker         None,
47*c8dee2aaSAndroid Build Coastguard Worker         Background,
48*c8dee2aaSAndroid Build Coastguard Worker         Previous,
49*c8dee2aaSAndroid Build Coastguard Worker     }
50*c8dee2aaSAndroid Build Coastguard Worker 
51*c8dee2aaSAndroid Build Coastguard Worker     /// FFI-friendly equivalent of `png::BlendOp`.
52*c8dee2aaSAndroid Build Coastguard Worker     enum BlendOp {
53*c8dee2aaSAndroid Build Coastguard Worker         Source,
54*c8dee2aaSAndroid Build Coastguard Worker         Over,
55*c8dee2aaSAndroid Build Coastguard Worker     }
56*c8dee2aaSAndroid Build Coastguard Worker 
57*c8dee2aaSAndroid Build Coastguard Worker     /// FFI-friendly simplification of `Option<png::EncodingError>`.
58*c8dee2aaSAndroid Build Coastguard Worker     enum EncodingResult {
59*c8dee2aaSAndroid Build Coastguard Worker         Success,
60*c8dee2aaSAndroid Build Coastguard Worker         IoError,
61*c8dee2aaSAndroid Build Coastguard Worker         FormatError,
62*c8dee2aaSAndroid Build Coastguard Worker         ParameterError,
63*c8dee2aaSAndroid Build Coastguard Worker         LimitsExceededError,
64*c8dee2aaSAndroid Build Coastguard Worker     }
65*c8dee2aaSAndroid Build Coastguard Worker 
66*c8dee2aaSAndroid Build Coastguard Worker     unsafe extern "C++" {
67*c8dee2aaSAndroid Build Coastguard Worker         include!("experimental/rust_png/ffi/FFI.h");
68*c8dee2aaSAndroid Build Coastguard Worker 
69*c8dee2aaSAndroid Build Coastguard Worker         type ReadTrait;
read(self: Pin<&mut ReadTrait>, buffer: &mut [u8]) -> usize70*c8dee2aaSAndroid Build Coastguard Worker         fn read(self: Pin<&mut ReadTrait>, buffer: &mut [u8]) -> usize;
71*c8dee2aaSAndroid Build Coastguard Worker 
72*c8dee2aaSAndroid Build Coastguard Worker         type WriteTrait;
write(self: Pin<&mut WriteTrait>, buffer: &[u8]) -> bool73*c8dee2aaSAndroid Build Coastguard Worker         fn write(self: Pin<&mut WriteTrait>, buffer: &[u8]) -> bool;
flush(self: Pin<&mut WriteTrait>)74*c8dee2aaSAndroid Build Coastguard Worker         fn flush(self: Pin<&mut WriteTrait>);
75*c8dee2aaSAndroid Build Coastguard Worker     }
76*c8dee2aaSAndroid Build Coastguard Worker 
77*c8dee2aaSAndroid Build Coastguard Worker     // Rust functions, types, and methods that are exposed through FFI.
78*c8dee2aaSAndroid Build Coastguard Worker     //
79*c8dee2aaSAndroid Build Coastguard Worker     // To avoid duplication, there are no doc comments inside the `extern "Rust"`
80*c8dee2aaSAndroid Build Coastguard Worker     // section. The doc comments of these items can instead be found in the
81*c8dee2aaSAndroid Build Coastguard Worker     // actual Rust code, outside of the `#[cxx::bridge]` manifest.
82*c8dee2aaSAndroid Build Coastguard Worker     extern "Rust" {
new_reader(input: UniquePtr<ReadTrait>) -> Box<ResultOfReader>83*c8dee2aaSAndroid Build Coastguard Worker         fn new_reader(input: UniquePtr<ReadTrait>) -> Box<ResultOfReader>;
84*c8dee2aaSAndroid Build Coastguard Worker 
85*c8dee2aaSAndroid Build Coastguard Worker         type ResultOfReader;
err(self: &ResultOfReader) -> DecodingResult86*c8dee2aaSAndroid Build Coastguard Worker         fn err(self: &ResultOfReader) -> DecodingResult;
unwrap(self: &mut ResultOfReader) -> Box<Reader>87*c8dee2aaSAndroid Build Coastguard Worker         fn unwrap(self: &mut ResultOfReader) -> Box<Reader>;
88*c8dee2aaSAndroid Build Coastguard Worker 
89*c8dee2aaSAndroid Build Coastguard Worker         type Reader;
height(self: &Reader) -> u3290*c8dee2aaSAndroid Build Coastguard Worker         fn height(self: &Reader) -> u32;
width(self: &Reader) -> u3291*c8dee2aaSAndroid Build Coastguard Worker         fn width(self: &Reader) -> u32;
interlaced(self: &Reader) -> bool92*c8dee2aaSAndroid Build Coastguard Worker         fn interlaced(self: &Reader) -> bool;
is_srgb(self: &Reader) -> bool93*c8dee2aaSAndroid Build Coastguard Worker         fn is_srgb(self: &Reader) -> bool;
try_get_chrm( self: &Reader, wx: &mut f32, wy: &mut f32, rx: &mut f32, ry: &mut f32, gx: &mut f32, gy: &mut f32, bx: &mut f32, by: &mut f32, ) -> bool94*c8dee2aaSAndroid Build Coastguard Worker         fn try_get_chrm(
95*c8dee2aaSAndroid Build Coastguard Worker             self: &Reader,
96*c8dee2aaSAndroid Build Coastguard Worker             wx: &mut f32,
97*c8dee2aaSAndroid Build Coastguard Worker             wy: &mut f32,
98*c8dee2aaSAndroid Build Coastguard Worker             rx: &mut f32,
99*c8dee2aaSAndroid Build Coastguard Worker             ry: &mut f32,
100*c8dee2aaSAndroid Build Coastguard Worker             gx: &mut f32,
101*c8dee2aaSAndroid Build Coastguard Worker             gy: &mut f32,
102*c8dee2aaSAndroid Build Coastguard Worker             bx: &mut f32,
103*c8dee2aaSAndroid Build Coastguard Worker             by: &mut f32,
104*c8dee2aaSAndroid Build Coastguard Worker         ) -> bool;
try_get_cicp_chunk( self: &Reader, primaries_id: &mut u8, transfer_id: &mut u8, matrix_id: &mut u8, is_full_range: &mut bool, ) -> bool105*c8dee2aaSAndroid Build Coastguard Worker         fn try_get_cicp_chunk(
106*c8dee2aaSAndroid Build Coastguard Worker             self: &Reader,
107*c8dee2aaSAndroid Build Coastguard Worker             primaries_id: &mut u8,
108*c8dee2aaSAndroid Build Coastguard Worker             transfer_id: &mut u8,
109*c8dee2aaSAndroid Build Coastguard Worker             matrix_id: &mut u8,
110*c8dee2aaSAndroid Build Coastguard Worker             is_full_range: &mut bool,
111*c8dee2aaSAndroid Build Coastguard Worker         ) -> bool;
try_get_gama(self: &Reader, gamma: &mut f32) -> bool112*c8dee2aaSAndroid Build Coastguard Worker         fn try_get_gama(self: &Reader, gamma: &mut f32) -> bool;
has_iccp_chunk(self: &Reader) -> bool113*c8dee2aaSAndroid Build Coastguard Worker         fn has_iccp_chunk(self: &Reader) -> bool;
get_iccp_chunk(self: &Reader) -> &[u8]114*c8dee2aaSAndroid Build Coastguard Worker         fn get_iccp_chunk(self: &Reader) -> &[u8];
has_trns_chunk(self: &Reader) -> bool115*c8dee2aaSAndroid Build Coastguard Worker         fn has_trns_chunk(self: &Reader) -> bool;
get_trns_chunk(self: &Reader) -> &[u8]116*c8dee2aaSAndroid Build Coastguard Worker         fn get_trns_chunk(self: &Reader) -> &[u8];
has_plte_chunk(self: &Reader) -> bool117*c8dee2aaSAndroid Build Coastguard Worker         fn has_plte_chunk(self: &Reader) -> bool;
get_plte_chunk(self: &Reader) -> &[u8]118*c8dee2aaSAndroid Build Coastguard Worker         fn get_plte_chunk(self: &Reader) -> &[u8];
has_actl_chunk(self: &Reader) -> bool119*c8dee2aaSAndroid Build Coastguard Worker         fn has_actl_chunk(self: &Reader) -> bool;
get_actl_num_frames(self: &Reader) -> u32120*c8dee2aaSAndroid Build Coastguard Worker         fn get_actl_num_frames(self: &Reader) -> u32;
get_actl_num_plays(self: &Reader) -> u32121*c8dee2aaSAndroid Build Coastguard Worker         fn get_actl_num_plays(self: &Reader) -> u32;
has_fctl_chunk(self: &Reader) -> bool122*c8dee2aaSAndroid Build Coastguard Worker         fn has_fctl_chunk(self: &Reader) -> bool;
get_fctl_info( self: &Reader, width: &mut u32, height: &mut u32, x_offset: &mut u32, y_offset: &mut u32, dispose_op: &mut DisposeOp, blend_op: &mut BlendOp, duration_ms: &mut u32, )123*c8dee2aaSAndroid Build Coastguard Worker         fn get_fctl_info(
124*c8dee2aaSAndroid Build Coastguard Worker             self: &Reader,
125*c8dee2aaSAndroid Build Coastguard Worker             width: &mut u32,
126*c8dee2aaSAndroid Build Coastguard Worker             height: &mut u32,
127*c8dee2aaSAndroid Build Coastguard Worker             x_offset: &mut u32,
128*c8dee2aaSAndroid Build Coastguard Worker             y_offset: &mut u32,
129*c8dee2aaSAndroid Build Coastguard Worker             dispose_op: &mut DisposeOp,
130*c8dee2aaSAndroid Build Coastguard Worker             blend_op: &mut BlendOp,
131*c8dee2aaSAndroid Build Coastguard Worker             duration_ms: &mut u32,
132*c8dee2aaSAndroid Build Coastguard Worker         );
output_buffer_size(self: &Reader) -> usize133*c8dee2aaSAndroid Build Coastguard Worker         fn output_buffer_size(self: &Reader) -> usize;
output_color_type(self: &Reader) -> ColorType134*c8dee2aaSAndroid Build Coastguard Worker         fn output_color_type(self: &Reader) -> ColorType;
output_bits_per_component(self: &Reader) -> u8135*c8dee2aaSAndroid Build Coastguard Worker         fn output_bits_per_component(self: &Reader) -> u8;
next_frame_info(self: &mut Reader) -> DecodingResult136*c8dee2aaSAndroid Build Coastguard Worker         fn next_frame_info(self: &mut Reader) -> DecodingResult;
next_interlaced_row<'a>( self: &'a mut Reader, row: &mut &'a [u8], ) -> DecodingResult137*c8dee2aaSAndroid Build Coastguard Worker         unsafe fn next_interlaced_row<'a>(
138*c8dee2aaSAndroid Build Coastguard Worker             self: &'a mut Reader,
139*c8dee2aaSAndroid Build Coastguard Worker             row: &mut &'a [u8],
140*c8dee2aaSAndroid Build Coastguard Worker         ) -> DecodingResult;
expand_last_interlaced_row( self: &Reader, img: &mut [u8], img_row_stride: usize, row: &[u8], bits_per_pixel: u8, )141*c8dee2aaSAndroid Build Coastguard Worker         fn expand_last_interlaced_row(
142*c8dee2aaSAndroid Build Coastguard Worker             self: &Reader,
143*c8dee2aaSAndroid Build Coastguard Worker             img: &mut [u8],
144*c8dee2aaSAndroid Build Coastguard Worker             img_row_stride: usize,
145*c8dee2aaSAndroid Build Coastguard Worker             row: &[u8],
146*c8dee2aaSAndroid Build Coastguard Worker             bits_per_pixel: u8,
147*c8dee2aaSAndroid Build Coastguard Worker         );
148*c8dee2aaSAndroid Build Coastguard Worker 
new_stream_writer( output: UniquePtr<WriteTrait>, width: u32, height: u32, color: ColorType, bits_per_component: u8, ) -> Box<ResultOfStreamWriter>149*c8dee2aaSAndroid Build Coastguard Worker         fn new_stream_writer(
150*c8dee2aaSAndroid Build Coastguard Worker             output: UniquePtr<WriteTrait>,
151*c8dee2aaSAndroid Build Coastguard Worker             width: u32,
152*c8dee2aaSAndroid Build Coastguard Worker             height: u32,
153*c8dee2aaSAndroid Build Coastguard Worker             color: ColorType,
154*c8dee2aaSAndroid Build Coastguard Worker             bits_per_component: u8,
155*c8dee2aaSAndroid Build Coastguard Worker         ) -> Box<ResultOfStreamWriter>;
156*c8dee2aaSAndroid Build Coastguard Worker 
157*c8dee2aaSAndroid Build Coastguard Worker         type ResultOfStreamWriter;
err(self: &ResultOfStreamWriter) -> EncodingResult158*c8dee2aaSAndroid Build Coastguard Worker         fn err(self: &ResultOfStreamWriter) -> EncodingResult;
unwrap(self: &mut ResultOfStreamWriter) -> Box<StreamWriter>159*c8dee2aaSAndroid Build Coastguard Worker         fn unwrap(self: &mut ResultOfStreamWriter) -> Box<StreamWriter>;
160*c8dee2aaSAndroid Build Coastguard Worker 
161*c8dee2aaSAndroid Build Coastguard Worker         type StreamWriter;
write(self: &mut StreamWriter, data: &[u8]) -> EncodingResult162*c8dee2aaSAndroid Build Coastguard Worker         fn write(self: &mut StreamWriter, data: &[u8]) -> EncodingResult;
finish_encoding(stream_writer: Box<StreamWriter>) -> EncodingResult163*c8dee2aaSAndroid Build Coastguard Worker         fn finish_encoding(stream_writer: Box<StreamWriter>) -> EncodingResult;
164*c8dee2aaSAndroid Build Coastguard Worker     }
165*c8dee2aaSAndroid Build Coastguard Worker }
166*c8dee2aaSAndroid Build Coastguard Worker 
167*c8dee2aaSAndroid Build Coastguard Worker impl From<png::ColorType> for ffi::ColorType {
from(value: png::ColorType) -> Self168*c8dee2aaSAndroid Build Coastguard Worker     fn from(value: png::ColorType) -> Self {
169*c8dee2aaSAndroid Build Coastguard Worker         match value {
170*c8dee2aaSAndroid Build Coastguard Worker             png::ColorType::Grayscale => Self::Grayscale,
171*c8dee2aaSAndroid Build Coastguard Worker             png::ColorType::Rgb => Self::Rgb,
172*c8dee2aaSAndroid Build Coastguard Worker             png::ColorType::Indexed => Self::Indexed,
173*c8dee2aaSAndroid Build Coastguard Worker             png::ColorType::GrayscaleAlpha => Self::GrayscaleAlpha,
174*c8dee2aaSAndroid Build Coastguard Worker             png::ColorType::Rgba => Self::Rgba,
175*c8dee2aaSAndroid Build Coastguard Worker         }
176*c8dee2aaSAndroid Build Coastguard Worker     }
177*c8dee2aaSAndroid Build Coastguard Worker }
178*c8dee2aaSAndroid Build Coastguard Worker 
179*c8dee2aaSAndroid Build Coastguard Worker impl Into<png::ColorType> for ffi::ColorType {
into(self) -> png::ColorType180*c8dee2aaSAndroid Build Coastguard Worker     fn into(self) -> png::ColorType {
181*c8dee2aaSAndroid Build Coastguard Worker         match self {
182*c8dee2aaSAndroid Build Coastguard Worker             Self::Grayscale => png::ColorType::Grayscale,
183*c8dee2aaSAndroid Build Coastguard Worker             Self::Rgb => png::ColorType::Rgb,
184*c8dee2aaSAndroid Build Coastguard Worker             Self::GrayscaleAlpha => png::ColorType::GrayscaleAlpha,
185*c8dee2aaSAndroid Build Coastguard Worker             Self::Rgba => png::ColorType::Rgba,
186*c8dee2aaSAndroid Build Coastguard Worker 
187*c8dee2aaSAndroid Build Coastguard Worker             // `SkPngRustEncoderImpl` only uses the color types above.
188*c8dee2aaSAndroid Build Coastguard Worker             _ => unreachable!(),
189*c8dee2aaSAndroid Build Coastguard Worker         }
190*c8dee2aaSAndroid Build Coastguard Worker     }
191*c8dee2aaSAndroid Build Coastguard Worker }
192*c8dee2aaSAndroid Build Coastguard Worker 
193*c8dee2aaSAndroid Build Coastguard Worker impl From<png::DisposeOp> for ffi::DisposeOp {
from(value: png::DisposeOp) -> Self194*c8dee2aaSAndroid Build Coastguard Worker     fn from(value: png::DisposeOp) -> Self {
195*c8dee2aaSAndroid Build Coastguard Worker         match value {
196*c8dee2aaSAndroid Build Coastguard Worker             png::DisposeOp::None => Self::None,
197*c8dee2aaSAndroid Build Coastguard Worker             png::DisposeOp::Background => Self::Background,
198*c8dee2aaSAndroid Build Coastguard Worker             png::DisposeOp::Previous => Self::Previous,
199*c8dee2aaSAndroid Build Coastguard Worker         }
200*c8dee2aaSAndroid Build Coastguard Worker     }
201*c8dee2aaSAndroid Build Coastguard Worker }
202*c8dee2aaSAndroid Build Coastguard Worker 
203*c8dee2aaSAndroid Build Coastguard Worker impl From<png::BlendOp> for ffi::BlendOp {
from(value: png::BlendOp) -> Self204*c8dee2aaSAndroid Build Coastguard Worker     fn from(value: png::BlendOp) -> Self {
205*c8dee2aaSAndroid Build Coastguard Worker         match value {
206*c8dee2aaSAndroid Build Coastguard Worker             png::BlendOp::Source => Self::Source,
207*c8dee2aaSAndroid Build Coastguard Worker             png::BlendOp::Over => Self::Over,
208*c8dee2aaSAndroid Build Coastguard Worker         }
209*c8dee2aaSAndroid Build Coastguard Worker     }
210*c8dee2aaSAndroid Build Coastguard Worker }
211*c8dee2aaSAndroid Build Coastguard Worker 
212*c8dee2aaSAndroid Build Coastguard Worker impl From<Option<&png::DecodingError>> for ffi::DecodingResult {
from(option: Option<&png::DecodingError>) -> Self213*c8dee2aaSAndroid Build Coastguard Worker     fn from(option: Option<&png::DecodingError>) -> Self {
214*c8dee2aaSAndroid Build Coastguard Worker         match option {
215*c8dee2aaSAndroid Build Coastguard Worker             None => Self::Success,
216*c8dee2aaSAndroid Build Coastguard Worker             Some(decoding_error) => match decoding_error {
217*c8dee2aaSAndroid Build Coastguard Worker                 png::DecodingError::IoError(e) => {
218*c8dee2aaSAndroid Build Coastguard Worker                     if e.kind() == ErrorKind::UnexpectedEof {
219*c8dee2aaSAndroid Build Coastguard Worker                         Self::IncompleteInput
220*c8dee2aaSAndroid Build Coastguard Worker                     } else {
221*c8dee2aaSAndroid Build Coastguard Worker                         // `ReadTrait` is infallible => we expect no other kind of
222*c8dee2aaSAndroid Build Coastguard Worker                         // `png::DecodingError::IoError`.
223*c8dee2aaSAndroid Build Coastguard Worker                         unreachable!()
224*c8dee2aaSAndroid Build Coastguard Worker                     }
225*c8dee2aaSAndroid Build Coastguard Worker                 }
226*c8dee2aaSAndroid Build Coastguard Worker                 png::DecodingError::Format(_) => Self::FormatError,
227*c8dee2aaSAndroid Build Coastguard Worker                 png::DecodingError::Parameter(_) => Self::ParameterError,
228*c8dee2aaSAndroid Build Coastguard Worker                 png::DecodingError::LimitsExceeded => Self::LimitsExceededError,
229*c8dee2aaSAndroid Build Coastguard Worker             },
230*c8dee2aaSAndroid Build Coastguard Worker         }
231*c8dee2aaSAndroid Build Coastguard Worker     }
232*c8dee2aaSAndroid Build Coastguard Worker }
233*c8dee2aaSAndroid Build Coastguard Worker 
234*c8dee2aaSAndroid Build Coastguard Worker impl From<Option<&png::EncodingError>> for ffi::EncodingResult {
from(option: Option<&png::EncodingError>) -> Self235*c8dee2aaSAndroid Build Coastguard Worker     fn from(option: Option<&png::EncodingError>) -> Self {
236*c8dee2aaSAndroid Build Coastguard Worker         match option {
237*c8dee2aaSAndroid Build Coastguard Worker             None => Self::Success,
238*c8dee2aaSAndroid Build Coastguard Worker             Some(encoding_error) => match encoding_error {
239*c8dee2aaSAndroid Build Coastguard Worker                 png::EncodingError::IoError(_) => Self::IoError,
240*c8dee2aaSAndroid Build Coastguard Worker                 png::EncodingError::Format(_) => Self::FormatError,
241*c8dee2aaSAndroid Build Coastguard Worker                 png::EncodingError::Parameter(_) => Self::ParameterError,
242*c8dee2aaSAndroid Build Coastguard Worker                 png::EncodingError::LimitsExceeded => Self::LimitsExceededError,
243*c8dee2aaSAndroid Build Coastguard Worker             },
244*c8dee2aaSAndroid Build Coastguard Worker         }
245*c8dee2aaSAndroid Build Coastguard Worker     }
246*c8dee2aaSAndroid Build Coastguard Worker }
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker impl<'a> Read for Pin<&'a mut ffi::ReadTrait> {
read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>249*c8dee2aaSAndroid Build Coastguard Worker     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
250*c8dee2aaSAndroid Build Coastguard Worker         Ok(self.as_mut().read(buf))
251*c8dee2aaSAndroid Build Coastguard Worker     }
252*c8dee2aaSAndroid Build Coastguard Worker }
253*c8dee2aaSAndroid Build Coastguard Worker 
254*c8dee2aaSAndroid Build Coastguard Worker impl<'a> Write for Pin<&'a mut ffi::WriteTrait> {
write(&mut self, buf: &[u8]) -> std::io::Result<usize>255*c8dee2aaSAndroid Build Coastguard Worker     fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
256*c8dee2aaSAndroid Build Coastguard Worker         if self.as_mut().write(buf) {
257*c8dee2aaSAndroid Build Coastguard Worker             Ok(buf.len())
258*c8dee2aaSAndroid Build Coastguard Worker         } else {
259*c8dee2aaSAndroid Build Coastguard Worker             Err(ErrorKind::Other.into())
260*c8dee2aaSAndroid Build Coastguard Worker         }
261*c8dee2aaSAndroid Build Coastguard Worker     }
262*c8dee2aaSAndroid Build Coastguard Worker 
flush(&mut self) -> std::io::Result<()>263*c8dee2aaSAndroid Build Coastguard Worker     fn flush(&mut self) -> std::io::Result<()> {
264*c8dee2aaSAndroid Build Coastguard Worker         self.as_mut().flush();
265*c8dee2aaSAndroid Build Coastguard Worker         Ok(())
266*c8dee2aaSAndroid Build Coastguard Worker     }
267*c8dee2aaSAndroid Build Coastguard Worker }
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker /// FFI-friendly wrapper around `Result<T, E>` (`cxx` can't handle arbitrary
270*c8dee2aaSAndroid Build Coastguard Worker /// generics, so we manually monomorphize here, but still expose a minimal,
271*c8dee2aaSAndroid Build Coastguard Worker /// somewhat tweaked API of the original type).
272*c8dee2aaSAndroid Build Coastguard Worker struct ResultOfReader(Result<Reader, png::DecodingError>);
273*c8dee2aaSAndroid Build Coastguard Worker 
274*c8dee2aaSAndroid Build Coastguard Worker impl ResultOfReader {
err(&self) -> ffi::DecodingResult275*c8dee2aaSAndroid Build Coastguard Worker     fn err(&self) -> ffi::DecodingResult {
276*c8dee2aaSAndroid Build Coastguard Worker         self.0.as_ref().err().into()
277*c8dee2aaSAndroid Build Coastguard Worker     }
278*c8dee2aaSAndroid Build Coastguard Worker 
unwrap(&mut self) -> Box<Reader>279*c8dee2aaSAndroid Build Coastguard Worker     fn unwrap(&mut self) -> Box<Reader> {
280*c8dee2aaSAndroid Build Coastguard Worker         // Leaving `self` in a C++-friendly "moved-away" state.
281*c8dee2aaSAndroid Build Coastguard Worker         let mut result = Err(png::DecodingError::LimitsExceeded);
282*c8dee2aaSAndroid Build Coastguard Worker         std::mem::swap(&mut self.0, &mut result);
283*c8dee2aaSAndroid Build Coastguard Worker 
284*c8dee2aaSAndroid Build Coastguard Worker         Box::new(result.unwrap())
285*c8dee2aaSAndroid Build Coastguard Worker     }
286*c8dee2aaSAndroid Build Coastguard Worker }
287*c8dee2aaSAndroid Build Coastguard Worker 
compute_transformations(info: &png::Info) -> png::Transformations288*c8dee2aaSAndroid Build Coastguard Worker fn compute_transformations(info: &png::Info) -> png::Transformations {
289*c8dee2aaSAndroid Build Coastguard Worker     // There are 2 scenarios where `EXPAND` transformation may be needed:
290*c8dee2aaSAndroid Build Coastguard Worker     //
291*c8dee2aaSAndroid Build Coastguard Worker     // * `SkSwizzler` can handle low-bit-depth `ColorType::Indexed`, but it may not
292*c8dee2aaSAndroid Build Coastguard Worker     //   support other inputs with low bit depth (e.g. `kGray_Color` with bpp=4). We
293*c8dee2aaSAndroid Build Coastguard Worker     //   use `EXPAND` to ask the `png` crate to expand such low-bpp images to at
294*c8dee2aaSAndroid Build Coastguard Worker     //   least 8 bits.
295*c8dee2aaSAndroid Build Coastguard Worker     // * We may need to inject an alpha channel from the `tRNS` chunk if present.
296*c8dee2aaSAndroid Build Coastguard Worker     //   Note that we can't check `info.trns.is_some()` because at this point we
297*c8dee2aaSAndroid Build Coastguard Worker     //   have not yet read beyond the `IHDR` chunk.
298*c8dee2aaSAndroid Build Coastguard Worker     //
299*c8dee2aaSAndroid Build Coastguard Worker     // We avoid using `EXPAND` for `ColorType::Indexed` because this results in some
300*c8dee2aaSAndroid Build Coastguard Worker     // performance gains - see https://crbug.com/356882657 for more details.
301*c8dee2aaSAndroid Build Coastguard Worker     let mut result = match info.color_type {
302*c8dee2aaSAndroid Build Coastguard Worker         // Work around bpp<8 limitations of `SkSwizzler`
303*c8dee2aaSAndroid Build Coastguard Worker         png::ColorType::Rgba | png::ColorType::GrayscaleAlpha if (info.bit_depth as u8) < 8 => {
304*c8dee2aaSAndroid Build Coastguard Worker             png::Transformations::EXPAND
305*c8dee2aaSAndroid Build Coastguard Worker         }
306*c8dee2aaSAndroid Build Coastguard Worker 
307*c8dee2aaSAndroid Build Coastguard Worker         // Handle `tRNS` expansion + work around bpp<8 limitations of `SkSwizzler`
308*c8dee2aaSAndroid Build Coastguard Worker         png::ColorType::Rgb | png::ColorType::Grayscale => png::Transformations::EXPAND,
309*c8dee2aaSAndroid Build Coastguard Worker 
310*c8dee2aaSAndroid Build Coastguard Worker         // Otherwise there is no need to `EXPAND`.
311*c8dee2aaSAndroid Build Coastguard Worker         png::ColorType::Indexed | png::ColorType::Rgba | png::ColorType::GrayscaleAlpha => {
312*c8dee2aaSAndroid Build Coastguard Worker             png::Transformations::IDENTITY
313*c8dee2aaSAndroid Build Coastguard Worker         }
314*c8dee2aaSAndroid Build Coastguard Worker     };
315*c8dee2aaSAndroid Build Coastguard Worker 
316*c8dee2aaSAndroid Build Coastguard Worker     // We mimic how the `libpng`-based `SkPngCodec` handles G16 and GA16.
317*c8dee2aaSAndroid Build Coastguard Worker     //
318*c8dee2aaSAndroid Build Coastguard Worker     // TODO(https://crbug.com/359245096): Avoid stripping least signinficant 8 bits in G16 and
319*c8dee2aaSAndroid Build Coastguard Worker     // GA16 images.
320*c8dee2aaSAndroid Build Coastguard Worker     if info.bit_depth == png::BitDepth::Sixteen {
321*c8dee2aaSAndroid Build Coastguard Worker         if matches!(info.color_type, png::ColorType::Grayscale | png::ColorType::GrayscaleAlpha) {
322*c8dee2aaSAndroid Build Coastguard Worker             result = result | png::Transformations::STRIP_16;
323*c8dee2aaSAndroid Build Coastguard Worker         }
324*c8dee2aaSAndroid Build Coastguard Worker     }
325*c8dee2aaSAndroid Build Coastguard Worker 
326*c8dee2aaSAndroid Build Coastguard Worker     result
327*c8dee2aaSAndroid Build Coastguard Worker }
328*c8dee2aaSAndroid Build Coastguard Worker 
329*c8dee2aaSAndroid Build Coastguard Worker /// FFI-friendly wrapper around `png::Reader<R>` (`cxx` can't handle arbitrary
330*c8dee2aaSAndroid Build Coastguard Worker /// generics, so we manually monomorphize here, but still expose a minimal,
331*c8dee2aaSAndroid Build Coastguard Worker /// somewhat tweaked API of the original type).
332*c8dee2aaSAndroid Build Coastguard Worker struct Reader {
333*c8dee2aaSAndroid Build Coastguard Worker     reader: png::Reader<cxx::UniquePtr<ffi::ReadTrait>>,
334*c8dee2aaSAndroid Build Coastguard Worker     last_interlace_info: Option<png::InterlaceInfo>,
335*c8dee2aaSAndroid Build Coastguard Worker }
336*c8dee2aaSAndroid Build Coastguard Worker 
337*c8dee2aaSAndroid Build Coastguard Worker impl Reader {
new(input: cxx::UniquePtr<ffi::ReadTrait>) -> Result<Self, png::DecodingError>338*c8dee2aaSAndroid Build Coastguard Worker     fn new(input: cxx::UniquePtr<ffi::ReadTrait>) -> Result<Self, png::DecodingError> {
339*c8dee2aaSAndroid Build Coastguard Worker         // By default, the decoder is limited to using 64 Mib. If we ever need to change
340*c8dee2aaSAndroid Build Coastguard Worker         // that, we can use `png::Decoder::new_with_limits`.
341*c8dee2aaSAndroid Build Coastguard Worker         let mut decoder = png::Decoder::new(input);
342*c8dee2aaSAndroid Build Coastguard Worker 
343*c8dee2aaSAndroid Build Coastguard Worker         let info = decoder.read_header_info()?;
344*c8dee2aaSAndroid Build Coastguard Worker         let transformations = compute_transformations(info);
345*c8dee2aaSAndroid Build Coastguard Worker         decoder.set_transformations(transformations);
346*c8dee2aaSAndroid Build Coastguard Worker 
347*c8dee2aaSAndroid Build Coastguard Worker         Ok(Self { reader: decoder.read_info()?, last_interlace_info: None })
348*c8dee2aaSAndroid Build Coastguard Worker     }
349*c8dee2aaSAndroid Build Coastguard Worker 
height(&self) -> u32350*c8dee2aaSAndroid Build Coastguard Worker     fn height(&self) -> u32 {
351*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().height
352*c8dee2aaSAndroid Build Coastguard Worker     }
353*c8dee2aaSAndroid Build Coastguard Worker 
width(&self) -> u32354*c8dee2aaSAndroid Build Coastguard Worker     fn width(&self) -> u32 {
355*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().width
356*c8dee2aaSAndroid Build Coastguard Worker     }
357*c8dee2aaSAndroid Build Coastguard Worker 
358*c8dee2aaSAndroid Build Coastguard Worker     /// Returns whether the PNG image is interlaced.
interlaced(&self) -> bool359*c8dee2aaSAndroid Build Coastguard Worker     fn interlaced(&self) -> bool {
360*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().interlaced
361*c8dee2aaSAndroid Build Coastguard Worker     }
362*c8dee2aaSAndroid Build Coastguard Worker 
363*c8dee2aaSAndroid Build Coastguard Worker     /// Returns whether the decoded PNG image contained a `sRGB` chunk.
is_srgb(&self) -> bool364*c8dee2aaSAndroid Build Coastguard Worker     fn is_srgb(&self) -> bool {
365*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().srgb.is_some()
366*c8dee2aaSAndroid Build Coastguard Worker     }
367*c8dee2aaSAndroid Build Coastguard Worker 
368*c8dee2aaSAndroid Build Coastguard Worker     /// If the decoded PNG image contained a `cHRM` chunk then `try_get_chrm`
369*c8dee2aaSAndroid Build Coastguard Worker     /// returns `true` and populates the out parameters (`wx`, `wy`, `rx`,
370*c8dee2aaSAndroid Build Coastguard Worker     /// etc.).  Otherwise, returns `false`.
try_get_chrm( &self, wx: &mut f32, wy: &mut f32, rx: &mut f32, ry: &mut f32, gx: &mut f32, gy: &mut f32, bx: &mut f32, by: &mut f32, ) -> bool371*c8dee2aaSAndroid Build Coastguard Worker     fn try_get_chrm(
372*c8dee2aaSAndroid Build Coastguard Worker         &self,
373*c8dee2aaSAndroid Build Coastguard Worker         wx: &mut f32,
374*c8dee2aaSAndroid Build Coastguard Worker         wy: &mut f32,
375*c8dee2aaSAndroid Build Coastguard Worker         rx: &mut f32,
376*c8dee2aaSAndroid Build Coastguard Worker         ry: &mut f32,
377*c8dee2aaSAndroid Build Coastguard Worker         gx: &mut f32,
378*c8dee2aaSAndroid Build Coastguard Worker         gy: &mut f32,
379*c8dee2aaSAndroid Build Coastguard Worker         bx: &mut f32,
380*c8dee2aaSAndroid Build Coastguard Worker         by: &mut f32,
381*c8dee2aaSAndroid Build Coastguard Worker     ) -> bool {
382*c8dee2aaSAndroid Build Coastguard Worker         fn copy_channel(channel: &(png::ScaledFloat, png::ScaledFloat), x: &mut f32, y: &mut f32) {
383*c8dee2aaSAndroid Build Coastguard Worker             *x = channel.0.into_value();
384*c8dee2aaSAndroid Build Coastguard Worker             *y = channel.1.into_value();
385*c8dee2aaSAndroid Build Coastguard Worker         }
386*c8dee2aaSAndroid Build Coastguard Worker 
387*c8dee2aaSAndroid Build Coastguard Worker         match self.reader.info().chrm_chunk.as_ref() {
388*c8dee2aaSAndroid Build Coastguard Worker             None => false,
389*c8dee2aaSAndroid Build Coastguard Worker             Some(chrm) => {
390*c8dee2aaSAndroid Build Coastguard Worker                 copy_channel(&chrm.white, wx, wy);
391*c8dee2aaSAndroid Build Coastguard Worker                 copy_channel(&chrm.red, rx, ry);
392*c8dee2aaSAndroid Build Coastguard Worker                 copy_channel(&chrm.green, gx, gy);
393*c8dee2aaSAndroid Build Coastguard Worker                 copy_channel(&chrm.blue, bx, by);
394*c8dee2aaSAndroid Build Coastguard Worker                 true
395*c8dee2aaSAndroid Build Coastguard Worker             }
396*c8dee2aaSAndroid Build Coastguard Worker         }
397*c8dee2aaSAndroid Build Coastguard Worker     }
398*c8dee2aaSAndroid Build Coastguard Worker 
399*c8dee2aaSAndroid Build Coastguard Worker     /// If the decoded PNG image contained a `cICP` chunk then
400*c8dee2aaSAndroid Build Coastguard Worker     /// `try_get_cicp_chunk` returns `true` and populates the out
401*c8dee2aaSAndroid Build Coastguard Worker     /// parameters.  Otherwise, returns `false`.
try_get_cicp_chunk( &self, primaries_id: &mut u8, transfer_id: &mut u8, matrix_id: &mut u8, is_full_range: &mut bool, ) -> bool402*c8dee2aaSAndroid Build Coastguard Worker     fn try_get_cicp_chunk(
403*c8dee2aaSAndroid Build Coastguard Worker         &self,
404*c8dee2aaSAndroid Build Coastguard Worker         primaries_id: &mut u8,
405*c8dee2aaSAndroid Build Coastguard Worker         transfer_id: &mut u8,
406*c8dee2aaSAndroid Build Coastguard Worker         matrix_id: &mut u8,
407*c8dee2aaSAndroid Build Coastguard Worker         is_full_range: &mut bool,
408*c8dee2aaSAndroid Build Coastguard Worker     ) -> bool {
409*c8dee2aaSAndroid Build Coastguard Worker         match self.reader.info().coding_independent_code_points.as_ref() {
410*c8dee2aaSAndroid Build Coastguard Worker             None => false,
411*c8dee2aaSAndroid Build Coastguard Worker             Some(cicp) => {
412*c8dee2aaSAndroid Build Coastguard Worker                 *primaries_id = cicp.color_primaries;
413*c8dee2aaSAndroid Build Coastguard Worker                 *transfer_id = cicp.transfer_function;
414*c8dee2aaSAndroid Build Coastguard Worker                 *matrix_id = cicp.matrix_coefficients;
415*c8dee2aaSAndroid Build Coastguard Worker                 *is_full_range = cicp.is_video_full_range_image;
416*c8dee2aaSAndroid Build Coastguard Worker                 true
417*c8dee2aaSAndroid Build Coastguard Worker             }
418*c8dee2aaSAndroid Build Coastguard Worker         }
419*c8dee2aaSAndroid Build Coastguard Worker     }
420*c8dee2aaSAndroid Build Coastguard Worker 
421*c8dee2aaSAndroid Build Coastguard Worker     /// If the decoded PNG image contained a `gAMA` chunk then `try_get_gama`
422*c8dee2aaSAndroid Build Coastguard Worker     /// returns `true` and populates the `gamma` out parameter.  Otherwise,
423*c8dee2aaSAndroid Build Coastguard Worker     /// returns `false`.
try_get_gama(&self, gamma: &mut f32) -> bool424*c8dee2aaSAndroid Build Coastguard Worker     fn try_get_gama(&self, gamma: &mut f32) -> bool {
425*c8dee2aaSAndroid Build Coastguard Worker         match self.reader.info().gama_chunk.as_ref() {
426*c8dee2aaSAndroid Build Coastguard Worker             None => false,
427*c8dee2aaSAndroid Build Coastguard Worker             Some(scaled_float) => {
428*c8dee2aaSAndroid Build Coastguard Worker                 *gamma = scaled_float.into_value();
429*c8dee2aaSAndroid Build Coastguard Worker                 true
430*c8dee2aaSAndroid Build Coastguard Worker             }
431*c8dee2aaSAndroid Build Coastguard Worker         }
432*c8dee2aaSAndroid Build Coastguard Worker     }
433*c8dee2aaSAndroid Build Coastguard Worker 
434*c8dee2aaSAndroid Build Coastguard Worker     /// Returns whether the `iCCP` chunk exists.
has_iccp_chunk(&self) -> bool435*c8dee2aaSAndroid Build Coastguard Worker     fn has_iccp_chunk(&self) -> bool {
436*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().icc_profile.is_some()
437*c8dee2aaSAndroid Build Coastguard Worker     }
438*c8dee2aaSAndroid Build Coastguard Worker 
439*c8dee2aaSAndroid Build Coastguard Worker     /// Returns contents of the `iCCP` chunk.  Panics if there is no `iCCP`
440*c8dee2aaSAndroid Build Coastguard Worker     /// chunk.
get_iccp_chunk(&self) -> &[u8]441*c8dee2aaSAndroid Build Coastguard Worker     fn get_iccp_chunk(&self) -> &[u8] {
442*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().icc_profile.as_ref().unwrap().as_ref()
443*c8dee2aaSAndroid Build Coastguard Worker     }
444*c8dee2aaSAndroid Build Coastguard Worker 
445*c8dee2aaSAndroid Build Coastguard Worker     /// Returns whether the `tRNS` chunk exists.
has_trns_chunk(&self) -> bool446*c8dee2aaSAndroid Build Coastguard Worker     fn has_trns_chunk(&self) -> bool {
447*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().trns.is_some()
448*c8dee2aaSAndroid Build Coastguard Worker     }
449*c8dee2aaSAndroid Build Coastguard Worker 
450*c8dee2aaSAndroid Build Coastguard Worker     /// Returns contents of the `tRNS` chunk.  Panics if there is no `tRNS`
451*c8dee2aaSAndroid Build Coastguard Worker     /// chunk.
get_trns_chunk(&self) -> &[u8]452*c8dee2aaSAndroid Build Coastguard Worker     fn get_trns_chunk(&self) -> &[u8] {
453*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().trns.as_ref().unwrap().as_ref()
454*c8dee2aaSAndroid Build Coastguard Worker     }
455*c8dee2aaSAndroid Build Coastguard Worker 
456*c8dee2aaSAndroid Build Coastguard Worker     /// Returns whether the `PLTE` chunk exists.
has_plte_chunk(&self) -> bool457*c8dee2aaSAndroid Build Coastguard Worker     fn has_plte_chunk(&self) -> bool {
458*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().palette.is_some()
459*c8dee2aaSAndroid Build Coastguard Worker     }
460*c8dee2aaSAndroid Build Coastguard Worker 
461*c8dee2aaSAndroid Build Coastguard Worker     /// Returns contents of the `PLTE` chunk.  Panics if there is no `PLTE`
462*c8dee2aaSAndroid Build Coastguard Worker     /// chunk.
get_plte_chunk(&self) -> &[u8]463*c8dee2aaSAndroid Build Coastguard Worker     fn get_plte_chunk(&self) -> &[u8] {
464*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().palette.as_ref().unwrap().as_ref()
465*c8dee2aaSAndroid Build Coastguard Worker     }
466*c8dee2aaSAndroid Build Coastguard Worker 
467*c8dee2aaSAndroid Build Coastguard Worker     /// Returns whether the `acTL` chunk exists.
has_actl_chunk(&self) -> bool468*c8dee2aaSAndroid Build Coastguard Worker     fn has_actl_chunk(&self) -> bool {
469*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().animation_control.is_some()
470*c8dee2aaSAndroid Build Coastguard Worker     }
471*c8dee2aaSAndroid Build Coastguard Worker 
472*c8dee2aaSAndroid Build Coastguard Worker     /// Returns `num_frames` from the `acTL` chunk.  Panics if there is no
473*c8dee2aaSAndroid Build Coastguard Worker     /// `acTL` chunk.
474*c8dee2aaSAndroid Build Coastguard Worker     ///
475*c8dee2aaSAndroid Build Coastguard Worker     /// The returned value is equal the number of `fcTL` chunks.  (Note that it
476*c8dee2aaSAndroid Build Coastguard Worker     /// doesn't count `IDAT` nor `fdAT` chunks.  In particular, if an `fcTL`
477*c8dee2aaSAndroid Build Coastguard Worker     /// chunk doesn't appear before an `IDAT` chunk then `IDAT` is not part
478*c8dee2aaSAndroid Build Coastguard Worker     /// of the animation.)
479*c8dee2aaSAndroid Build Coastguard Worker     ///
480*c8dee2aaSAndroid Build Coastguard Worker     /// See also
481*c8dee2aaSAndroid Build Coastguard Worker     /// <https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk>.
get_actl_num_frames(&self) -> u32482*c8dee2aaSAndroid Build Coastguard Worker     fn get_actl_num_frames(&self) -> u32 {
483*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().animation_control.as_ref().unwrap().num_frames
484*c8dee2aaSAndroid Build Coastguard Worker     }
485*c8dee2aaSAndroid Build Coastguard Worker 
486*c8dee2aaSAndroid Build Coastguard Worker     /// Returns `num_plays` from the `acTL` chunk.  Panics if there is no `acTL`
487*c8dee2aaSAndroid Build Coastguard Worker     /// chunk.
488*c8dee2aaSAndroid Build Coastguard Worker     ///
489*c8dee2aaSAndroid Build Coastguard Worker     /// `0` indicates that the animation should play indefinitely. See
490*c8dee2aaSAndroid Build Coastguard Worker     /// <https://wiki.mozilla.org/APNG_Specification#.60acTL.60:_The_Animation_Control_Chunk>.
get_actl_num_plays(&self) -> u32491*c8dee2aaSAndroid Build Coastguard Worker     fn get_actl_num_plays(&self) -> u32 {
492*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().animation_control.as_ref().unwrap().num_plays
493*c8dee2aaSAndroid Build Coastguard Worker     }
494*c8dee2aaSAndroid Build Coastguard Worker 
495*c8dee2aaSAndroid Build Coastguard Worker     /// Returns whether a `fcTL` chunk has been parsed (and can be read using
496*c8dee2aaSAndroid Build Coastguard Worker     /// `get_fctl_info`).
has_fctl_chunk(&self) -> bool497*c8dee2aaSAndroid Build Coastguard Worker     fn has_fctl_chunk(&self) -> bool {
498*c8dee2aaSAndroid Build Coastguard Worker         self.reader.info().frame_control.is_some()
499*c8dee2aaSAndroid Build Coastguard Worker     }
500*c8dee2aaSAndroid Build Coastguard Worker 
501*c8dee2aaSAndroid Build Coastguard Worker     /// Returns `png::FrameControl` information.
502*c8dee2aaSAndroid Build Coastguard Worker     ///
503*c8dee2aaSAndroid Build Coastguard Worker     /// Panics if no `fcTL` chunk hasn't been parsed yet.
get_fctl_info( &self, width: &mut u32, height: &mut u32, x_offset: &mut u32, y_offset: &mut u32, dispose_op: &mut ffi::DisposeOp, blend_op: &mut ffi::BlendOp, duration_ms: &mut u32, )504*c8dee2aaSAndroid Build Coastguard Worker     fn get_fctl_info(
505*c8dee2aaSAndroid Build Coastguard Worker         &self,
506*c8dee2aaSAndroid Build Coastguard Worker         width: &mut u32,
507*c8dee2aaSAndroid Build Coastguard Worker         height: &mut u32,
508*c8dee2aaSAndroid Build Coastguard Worker         x_offset: &mut u32,
509*c8dee2aaSAndroid Build Coastguard Worker         y_offset: &mut u32,
510*c8dee2aaSAndroid Build Coastguard Worker         dispose_op: &mut ffi::DisposeOp,
511*c8dee2aaSAndroid Build Coastguard Worker         blend_op: &mut ffi::BlendOp,
512*c8dee2aaSAndroid Build Coastguard Worker         duration_ms: &mut u32,
513*c8dee2aaSAndroid Build Coastguard Worker     ) {
514*c8dee2aaSAndroid Build Coastguard Worker         let frame_control = self.reader.info().frame_control.as_ref().unwrap();
515*c8dee2aaSAndroid Build Coastguard Worker         *width = frame_control.width;
516*c8dee2aaSAndroid Build Coastguard Worker         *height = frame_control.height;
517*c8dee2aaSAndroid Build Coastguard Worker         *x_offset = frame_control.x_offset;
518*c8dee2aaSAndroid Build Coastguard Worker         *y_offset = frame_control.y_offset;
519*c8dee2aaSAndroid Build Coastguard Worker         *dispose_op = frame_control.dispose_op.into();
520*c8dee2aaSAndroid Build Coastguard Worker         *blend_op = frame_control.blend_op.into();
521*c8dee2aaSAndroid Build Coastguard Worker 
522*c8dee2aaSAndroid Build Coastguard Worker         // https://wiki.mozilla.org/APNG_Specification#.60fcTL.60:_The_Frame_Control_Chunk
523*c8dee2aaSAndroid Build Coastguard Worker         // says:
524*c8dee2aaSAndroid Build Coastguard Worker         //
525*c8dee2aaSAndroid Build Coastguard Worker         // > "The `delay_num` and `delay_den` parameters together specify a fraction
526*c8dee2aaSAndroid Build Coastguard Worker         // > indicating the time to display the current frame, in seconds. If the
527*c8dee2aaSAndroid Build Coastguard Worker         // > denominator is 0, it is to be treated as if it were 100 (that is,
528*c8dee2aaSAndroid Build Coastguard Worker         // > `delay_num` then specifies 1/100ths of a second).
529*c8dee2aaSAndroid Build Coastguard Worker         *duration_ms = if frame_control.delay_den == 0 {
530*c8dee2aaSAndroid Build Coastguard Worker             10 * frame_control.delay_num as u32
531*c8dee2aaSAndroid Build Coastguard Worker         } else {
532*c8dee2aaSAndroid Build Coastguard Worker             1000 * frame_control.delay_num as u32 / frame_control.delay_den as u32
533*c8dee2aaSAndroid Build Coastguard Worker         };
534*c8dee2aaSAndroid Build Coastguard Worker     }
535*c8dee2aaSAndroid Build Coastguard Worker 
output_buffer_size(&self) -> usize536*c8dee2aaSAndroid Build Coastguard Worker     fn output_buffer_size(&self) -> usize {
537*c8dee2aaSAndroid Build Coastguard Worker         self.reader.output_buffer_size()
538*c8dee2aaSAndroid Build Coastguard Worker     }
539*c8dee2aaSAndroid Build Coastguard Worker 
output_color_type(&self) -> ffi::ColorType540*c8dee2aaSAndroid Build Coastguard Worker     fn output_color_type(&self) -> ffi::ColorType {
541*c8dee2aaSAndroid Build Coastguard Worker         self.reader.output_color_type().0.into()
542*c8dee2aaSAndroid Build Coastguard Worker     }
543*c8dee2aaSAndroid Build Coastguard Worker 
output_bits_per_component(&self) -> u8544*c8dee2aaSAndroid Build Coastguard Worker     fn output_bits_per_component(&self) -> u8 {
545*c8dee2aaSAndroid Build Coastguard Worker         self.reader.output_color_type().1 as u8
546*c8dee2aaSAndroid Build Coastguard Worker     }
547*c8dee2aaSAndroid Build Coastguard Worker 
next_frame_info(&mut self) -> ffi::DecodingResult548*c8dee2aaSAndroid Build Coastguard Worker     fn next_frame_info(&mut self) -> ffi::DecodingResult {
549*c8dee2aaSAndroid Build Coastguard Worker         self.reader.next_frame_info().as_ref().err().into()
550*c8dee2aaSAndroid Build Coastguard Worker     }
551*c8dee2aaSAndroid Build Coastguard Worker 
552*c8dee2aaSAndroid Build Coastguard Worker     /// Decodes the next row - see
553*c8dee2aaSAndroid Build Coastguard Worker     /// https://docs.rs/png/latest/png/struct.Reader.html#method.next_interlaced_row
554*c8dee2aaSAndroid Build Coastguard Worker     ///
555*c8dee2aaSAndroid Build Coastguard Worker     /// TODO(https://crbug.com/357876243): Consider using `read_row` to avoid an extra copy.
556*c8dee2aaSAndroid Build Coastguard Worker     /// See also https://github.com/image-rs/image-png/pull/493
next_interlaced_row<'a>(&'a mut self, row: &mut &'a [u8]) -> ffi::DecodingResult557*c8dee2aaSAndroid Build Coastguard Worker     fn next_interlaced_row<'a>(&'a mut self, row: &mut &'a [u8]) -> ffi::DecodingResult {
558*c8dee2aaSAndroid Build Coastguard Worker         let result = self.reader.next_interlaced_row();
559*c8dee2aaSAndroid Build Coastguard Worker         if let Ok(maybe_row) = result.as_ref() {
560*c8dee2aaSAndroid Build Coastguard Worker             self.last_interlace_info = maybe_row.as_ref().map(|r| r.interlace()).copied();
561*c8dee2aaSAndroid Build Coastguard Worker             *row = maybe_row.map(|r| r.data()).unwrap_or(&[]);
562*c8dee2aaSAndroid Build Coastguard Worker         }
563*c8dee2aaSAndroid Build Coastguard Worker         result.as_ref().err().into()
564*c8dee2aaSAndroid Build Coastguard Worker     }
565*c8dee2aaSAndroid Build Coastguard Worker 
566*c8dee2aaSAndroid Build Coastguard Worker     /// Expands the last decoded interlaced row - see
567*c8dee2aaSAndroid Build Coastguard Worker     /// https://docs.rs/png/latest/png/fn.expand_interlaced_row
expand_last_interlaced_row( &self, img: &mut [u8], img_row_stride: usize, row: &[u8], bits_per_pixel: u8, )568*c8dee2aaSAndroid Build Coastguard Worker     fn expand_last_interlaced_row(
569*c8dee2aaSAndroid Build Coastguard Worker         &self,
570*c8dee2aaSAndroid Build Coastguard Worker         img: &mut [u8],
571*c8dee2aaSAndroid Build Coastguard Worker         img_row_stride: usize,
572*c8dee2aaSAndroid Build Coastguard Worker         row: &[u8],
573*c8dee2aaSAndroid Build Coastguard Worker         bits_per_pixel: u8,
574*c8dee2aaSAndroid Build Coastguard Worker     ) {
575*c8dee2aaSAndroid Build Coastguard Worker         let Some(png::InterlaceInfo::Adam7(ref adam7info)) = self.last_interlace_info.as_ref()
576*c8dee2aaSAndroid Build Coastguard Worker         else {
577*c8dee2aaSAndroid Build Coastguard Worker             panic!("This function should only be called after decoding an interlaced row");
578*c8dee2aaSAndroid Build Coastguard Worker         };
579*c8dee2aaSAndroid Build Coastguard Worker         png::expand_interlaced_row(img, img_row_stride, row, adam7info, bits_per_pixel);
580*c8dee2aaSAndroid Build Coastguard Worker     }
581*c8dee2aaSAndroid Build Coastguard Worker }
582*c8dee2aaSAndroid Build Coastguard Worker 
583*c8dee2aaSAndroid Build Coastguard Worker /// This provides a public C++ API for decoding a PNG image.
new_reader(input: cxx::UniquePtr<ffi::ReadTrait>) -> Box<ResultOfReader>584*c8dee2aaSAndroid Build Coastguard Worker fn new_reader(input: cxx::UniquePtr<ffi::ReadTrait>) -> Box<ResultOfReader> {
585*c8dee2aaSAndroid Build Coastguard Worker     Box::new(ResultOfReader(Reader::new(input)))
586*c8dee2aaSAndroid Build Coastguard Worker }
587*c8dee2aaSAndroid Build Coastguard Worker 
588*c8dee2aaSAndroid Build Coastguard Worker /// FFI-friendly wrapper around `Result<T, E>` (`cxx` can't handle arbitrary
589*c8dee2aaSAndroid Build Coastguard Worker /// generics, so we manually monomorphize here, but still expose a minimal,
590*c8dee2aaSAndroid Build Coastguard Worker /// somewhat tweaked API of the original type).
591*c8dee2aaSAndroid Build Coastguard Worker struct ResultOfStreamWriter(Result<StreamWriter, png::EncodingError>);
592*c8dee2aaSAndroid Build Coastguard Worker 
593*c8dee2aaSAndroid Build Coastguard Worker impl ResultOfStreamWriter {
err(&self) -> ffi::EncodingResult594*c8dee2aaSAndroid Build Coastguard Worker     fn err(&self) -> ffi::EncodingResult {
595*c8dee2aaSAndroid Build Coastguard Worker         self.0.as_ref().err().into()
596*c8dee2aaSAndroid Build Coastguard Worker     }
597*c8dee2aaSAndroid Build Coastguard Worker 
unwrap(&mut self) -> Box<StreamWriter>598*c8dee2aaSAndroid Build Coastguard Worker     fn unwrap(&mut self) -> Box<StreamWriter> {
599*c8dee2aaSAndroid Build Coastguard Worker         // Leaving `self` in a C++-friendly "moved-away" state.
600*c8dee2aaSAndroid Build Coastguard Worker         let mut result = Err(png::EncodingError::LimitsExceeded);
601*c8dee2aaSAndroid Build Coastguard Worker         std::mem::swap(&mut self.0, &mut result);
602*c8dee2aaSAndroid Build Coastguard Worker 
603*c8dee2aaSAndroid Build Coastguard Worker         Box::new(result.unwrap())
604*c8dee2aaSAndroid Build Coastguard Worker     }
605*c8dee2aaSAndroid Build Coastguard Worker }
606*c8dee2aaSAndroid Build Coastguard Worker 
607*c8dee2aaSAndroid Build Coastguard Worker /// FFI-friendly wrapper around `png::StreamWriter` (`cxx` can't handle
608*c8dee2aaSAndroid Build Coastguard Worker /// arbitrary generics, so we manually monomorphize here, but still expose a
609*c8dee2aaSAndroid Build Coastguard Worker /// minimal, somewhat tweaked API of the original type).
610*c8dee2aaSAndroid Build Coastguard Worker struct StreamWriter(png::StreamWriter<'static, cxx::UniquePtr<ffi::WriteTrait>>);
611*c8dee2aaSAndroid Build Coastguard Worker 
612*c8dee2aaSAndroid Build Coastguard Worker impl StreamWriter {
new( output: cxx::UniquePtr<ffi::WriteTrait>, width: u32, height: u32, color: ffi::ColorType, bits_per_component: u8, ) -> Result<Self, png::EncodingError>613*c8dee2aaSAndroid Build Coastguard Worker     fn new(
614*c8dee2aaSAndroid Build Coastguard Worker         output: cxx::UniquePtr<ffi::WriteTrait>,
615*c8dee2aaSAndroid Build Coastguard Worker         width: u32,
616*c8dee2aaSAndroid Build Coastguard Worker         height: u32,
617*c8dee2aaSAndroid Build Coastguard Worker         color: ffi::ColorType,
618*c8dee2aaSAndroid Build Coastguard Worker         bits_per_component: u8,
619*c8dee2aaSAndroid Build Coastguard Worker     ) -> Result<Self, png::EncodingError> {
620*c8dee2aaSAndroid Build Coastguard Worker         let mut encoder = png::Encoder::new(output, width, height);
621*c8dee2aaSAndroid Build Coastguard Worker         encoder.set_color(color.into());
622*c8dee2aaSAndroid Build Coastguard Worker         encoder.set_depth(match bits_per_component {
623*c8dee2aaSAndroid Build Coastguard Worker             8 => png::BitDepth::Eight,
624*c8dee2aaSAndroid Build Coastguard Worker             16 => png::BitDepth::Sixteen,
625*c8dee2aaSAndroid Build Coastguard Worker 
626*c8dee2aaSAndroid Build Coastguard Worker             // `SkPngRustEncoderImpl` only encodes 8-bit or 16-bit images.
627*c8dee2aaSAndroid Build Coastguard Worker             _ => unreachable!(),
628*c8dee2aaSAndroid Build Coastguard Worker         });
629*c8dee2aaSAndroid Build Coastguard Worker 
630*c8dee2aaSAndroid Build Coastguard Worker         let writer = encoder.write_header()?;
631*c8dee2aaSAndroid Build Coastguard Worker         let stream_writer = writer.into_stream_writer()?;
632*c8dee2aaSAndroid Build Coastguard Worker         Ok(Self(stream_writer))
633*c8dee2aaSAndroid Build Coastguard Worker     }
634*c8dee2aaSAndroid Build Coastguard Worker 
635*c8dee2aaSAndroid Build Coastguard Worker     /// FFI-friendly wrapper around `Write::write` implementation of
636*c8dee2aaSAndroid Build Coastguard Worker     /// `png::StreamWriter`.
637*c8dee2aaSAndroid Build Coastguard Worker     ///
638*c8dee2aaSAndroid Build Coastguard Worker     /// See also https://docs.rs/png/latest/png/struct.StreamWriter.html#method.write
write(&mut self, data: &[u8]) -> ffi::EncodingResult639*c8dee2aaSAndroid Build Coastguard Worker     pub fn write(&mut self, data: &[u8]) -> ffi::EncodingResult {
640*c8dee2aaSAndroid Build Coastguard Worker         let io_result = self.0.write(data);
641*c8dee2aaSAndroid Build Coastguard Worker         let encoding_result = io_result.map_err(|err| png::EncodingError::IoError(err));
642*c8dee2aaSAndroid Build Coastguard Worker         encoding_result.as_ref().err().into()
643*c8dee2aaSAndroid Build Coastguard Worker     }
644*c8dee2aaSAndroid Build Coastguard Worker }
645*c8dee2aaSAndroid Build Coastguard Worker 
646*c8dee2aaSAndroid Build Coastguard Worker /// This provides a public C++ API for encoding a PNG image.
new_stream_writer( output: cxx::UniquePtr<ffi::WriteTrait>, width: u32, height: u32, color: ffi::ColorType, bits_per_component: u8, ) -> Box<ResultOfStreamWriter>647*c8dee2aaSAndroid Build Coastguard Worker fn new_stream_writer(
648*c8dee2aaSAndroid Build Coastguard Worker     output: cxx::UniquePtr<ffi::WriteTrait>,
649*c8dee2aaSAndroid Build Coastguard Worker     width: u32,
650*c8dee2aaSAndroid Build Coastguard Worker     height: u32,
651*c8dee2aaSAndroid Build Coastguard Worker     color: ffi::ColorType,
652*c8dee2aaSAndroid Build Coastguard Worker     bits_per_component: u8,
653*c8dee2aaSAndroid Build Coastguard Worker ) -> Box<ResultOfStreamWriter> {
654*c8dee2aaSAndroid Build Coastguard Worker     Box::new(ResultOfStreamWriter(StreamWriter::new(
655*c8dee2aaSAndroid Build Coastguard Worker         output,
656*c8dee2aaSAndroid Build Coastguard Worker         width,
657*c8dee2aaSAndroid Build Coastguard Worker         height,
658*c8dee2aaSAndroid Build Coastguard Worker         color,
659*c8dee2aaSAndroid Build Coastguard Worker         bits_per_component,
660*c8dee2aaSAndroid Build Coastguard Worker     )))
661*c8dee2aaSAndroid Build Coastguard Worker }
662*c8dee2aaSAndroid Build Coastguard Worker 
663*c8dee2aaSAndroid Build Coastguard Worker /// FFI-friendly wrapper around `png::StreamWriter::finish`.
664*c8dee2aaSAndroid Build Coastguard Worker ///
665*c8dee2aaSAndroid Build Coastguard Worker /// See also https://docs.rs/png/latest/png/struct.StreamWriter.html#method.finish
finish_encoding(stream_writer: Box<StreamWriter>) -> ffi::EncodingResult666*c8dee2aaSAndroid Build Coastguard Worker fn finish_encoding(stream_writer: Box<StreamWriter>) -> ffi::EncodingResult {
667*c8dee2aaSAndroid Build Coastguard Worker     stream_writer.0.finish().as_ref().err().into()
668*c8dee2aaSAndroid Build Coastguard Worker }
669