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