Skip to main content

lodepng/
ffi.rs

1#![allow(clippy::needless_borrow)]
2#![allow(clippy::missing_safety_doc)]
3#![allow(non_upper_case_globals)]
4
5use crate::rustimpl::RGBA;
6use std::fmt;
7use std::io;
8use std::num::NonZeroU8;
9use std::os::raw::{c_uint, c_void};
10
11#[cfg(not(unix))]
12use std::path::PathBuf;
13
14#[cfg(any(feature = "c_ffi", feature = "_deprecated_c_ffi_default_"))]
15mod functions;
16
17#[repr(C)]
18#[derive(Copy, Clone, Eq, PartialEq)]
19pub struct ErrorCode(pub c_uint);
20
21/// Type for `decode`, `encode`, etc. Same as standard PNG color types.
22#[repr(C)]
23#[derive(Copy, Clone, Debug, Eq, PartialEq)]
24pub enum ColorType {
25    /// greyscale: 1, 2, 4, 8, 16 bit
26    GREY = 0,
27    /// RGB: 8, 16 bit
28    RGB = 2,
29    /// palette: 1, 2, 4, 8 bit
30    PALETTE = 3,
31    /// greyscale with alpha: 8, 16 bit
32    GREY_ALPHA = 4,
33    /// RGB with alpha: 8, 16 bit
34    RGBA = 6,
35
36    /// Not PNG standard, for internal use only. BGRA with alpha, 8 bit
37    BGRA = 6 | 64,
38    /// Not PNG standard, for internal use only. BGR no alpha, 8 bit
39    BGR = 2 | 64,
40    /// Not PNG standard, for internal use only. BGR no alpha, padded, 8 bit
41    BGRX = 3 | 64,
42}
43
44impl ColorType {
45    /// Bits per pixel
46    #[must_use]
47    pub fn bpp(&self, bitdepth: u32) -> u32 {
48        self.bpp_(bitdepth).get().into()
49    }
50
51    #[inline]
52    pub(crate) fn bpp_(self, bitdepth: u32) -> NonZeroU8 {
53        let bitdepth = bitdepth as u8;
54        /*bits per pixel is amount of channels * bits per channel*/
55        let ch = self.channels();
56        NonZeroU8::new(if ch > 1 {
57            ch * if bitdepth == 8 {
58                8
59            } else {
60                16
61            }
62        } else {
63            debug_assert!((bitdepth > 0 && bitdepth <= 8) || bitdepth == 16, "{bitdepth}x{ch}");
64            bitdepth.max(1)
65        }).unwrap()
66    }
67}
68
69/// Color mode of an image. Contains all information required to decode the pixel
70/// bits to RGBA colors. This information is the same as used in the PNG file
71/// format, and is used both for PNG and raw image data in LodePNG.
72#[repr(C)]
73#[derive(Clone, Debug)]
74pub struct ColorMode {
75    /// color type, see PNG standard
76    pub colortype: ColorType,
77    /// bits per sample, see PNG standard
78    pub(crate) bitdepth: c_uint,
79
80    /// palette (`PLTE` and `tRNS`)
81    /// Dynamically allocated with the colors of the palette, including alpha.
82    /// When encoding a PNG, to store your colors in the palette of the ColorMode, first use
83    /// lodepng_palette_clear, then for each color use lodepng_palette_add.
84    /// If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette.
85    ///
86    /// When decoding, by default you can ignore this palette, since LodePNG already
87    /// fills the palette colors in the pixels of the raw RGBA output.
88    ///
89    /// The palette is only supported for color type 3.
90    pub(crate) palette: Option<Box<[RGBA; 256]>>,
91    /// palette size in number of colors (amount of bytes is 4 * `palettesize`)
92    pub(crate) palettesize: usize,
93
94    /// transparent color key (`tRNS`)
95    ///
96    /// This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
97    /// For greyscale PNGs, r, g and b will all 3 be set to the same.
98    ///
99    /// When decoding, by default you can ignore this information, since LodePNG sets
100    /// pixels with this key to transparent already in the raw RGBA output.
101    ///
102    /// The color key is only supported for color types 0 and 2.
103    pub(crate) key_defined: c_uint,
104    pub(crate) key_r: c_uint,
105    pub(crate) key_g: c_uint,
106    pub(crate) key_b: c_uint,
107}
108
109pub type custom_compress_callback = Option<fn(input: &[u8], output: &mut dyn io::Write, context: &CompressSettings) -> Result<(), crate::Error>>;
110pub type custom_decompress_callback = Option<fn(input: &[u8], output: &mut dyn io::Write, context: &DecompressSettings) -> Result<(), crate::Error>>;
111
112#[repr(C)]
113#[derive(Clone)]
114pub struct DecompressSettings {
115    pub(crate) custom_zlib: custom_decompress_callback,
116    pub(crate) custom_inflate: custom_decompress_callback,
117    pub(crate) custom_context: *const c_void,
118}
119
120impl fmt::Debug for DecompressSettings {
121    #[cold]
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        let mut s = f.debug_struct("DecompressSettings");
124        s.field("custom_zlib", &self.custom_zlib.is_some());
125        s.field("custom_inflate", &self.custom_inflate.is_some());
126        s.field("custom_context", &self.custom_context);
127        s.finish()
128    }
129}
130
131/// Settings for zlib compression. Tweaking these settings tweaks the balance between speed and compression ratio.
132#[repr(C)]
133#[derive(Clone)]
134pub struct CompressSettings {
135    /// Obsolete. No-op.
136    #[deprecated]
137    pub windowsize: u32,
138    /// Compression level 1 (fast) to 9 (best). Use `set_level()` instead.
139    #[deprecated]
140    pub minmatch: u16,
141    /// Obsolete. No-op.
142    #[deprecated]
143    pub nicematch: u16,
144    /// Obsolete. No-op.
145    #[deprecated]
146    pub btype: u8,
147    /// If false, it won't compress at all. Use `set_level(0)`
148    #[deprecated]
149    pub use_lz77: bool,
150    /// Obsolete. No-op.
151    #[deprecated]
152    pub lazymatching: bool,
153    /// use custom zlib encoder instead of built in one (default: None).
154    ///
155    /// You should configure the `flate2` crate to use another zlib instead.
156    /// This option will cause unnecessary buffering.
157    #[deprecated(note = "use features of the flate2 crate instead")]
158    pub custom_zlib: custom_compress_callback,
159    /// use custom deflate encoder instead of built in one (default: null)
160    /// if custom_zlib is used, custom_deflate is ignored since only the built in
161    /// zlib function will call custom_deflate
162    ///
163    /// You should configure the `flate2` crate to use another zlib instead.
164    /// This option will cause unnecessary buffering.
165    #[deprecated(note = "use features of the flate2 crate instead")]
166    pub custom_deflate: custom_compress_callback,
167    /// optional custom settings for custom functions
168    pub custom_context: *const c_void,
169}
170
171impl CompressSettings {
172    /// 0 (none), 1 (fast) to 9 (best)
173    #[allow(deprecated)]
174    pub fn set_level(&mut self, level: u8) {
175        self.use_lz77 = level != 0;
176        self.minmatch = level.into();
177    }
178
179    /// zlib compression level
180    #[allow(deprecated)]
181    #[must_use]
182    pub fn level(&self) -> u8 {
183        if self.use_lz77 {
184            if self.minmatch > 0 && self.minmatch <= 9 {
185                self.minmatch as _
186            } else {
187                7
188            }
189        } else {
190            0
191        }
192    }
193}
194
195/// The information of a `Time` chunk in PNG
196#[repr(C)]
197#[derive(Copy, Clone, Debug, Default)]
198pub struct Time {
199    pub year: u16,
200    pub month: u8,
201    pub day: u8,
202    pub hour: u8,
203    pub minute: u8,
204    pub second: u8,
205}
206
207/// Information about the PNG image, except pixels, width and height
208#[repr(C)]
209#[derive(Debug, Clone)]
210pub struct Info {
211    /// interlace method of the original file
212    pub interlace_method: u8,
213    /// color type and bits, palette and transparency of the PNG file
214    pub color: ColorMode,
215
216    ///  suggested background color chunk (bKGD)
217    ///  This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit.
218    ///
219    ///  For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding
220    ///  the encoder writes the red one. For palette PNGs: When decoding, the RGB value
221    ///  will be stored, not a palette index. But when encoding, specify the index of
222    ///  the palette in background_r, the other two are then ignored.
223    ///
224    ///  The decoder does not use this background color to edit the color of pixels.
225    pub background_defined: bool,
226    /// red component of suggested background color
227    pub background_r: u16,
228    /// green component of suggested background color
229    pub background_g: u16,
230    /// blue component of suggested background color
231    pub background_b: u16,
232
233    /// set to 1 to make the encoder generate a tIME chunk
234    pub time_defined: bool,
235    /// time chunk (tIME)
236    pub time: Time,
237
238    /// if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one
239    pub phys_defined: bool,
240    /// pixels per unit in x direction
241    pub phys_x: c_uint,
242    /// pixels per unit in y direction
243    pub phys_y: c_uint,
244    /// may be 0 (unknown unit) or 1 (metre)
245    pub phys_unit: u8,
246
247    /// There are 3 buffers, one for each position in the PNG where unknown chunks can appear
248    /// each buffer contains all unknown chunks for that position consecutively
249    /// The 3 buffers are the unknown chunks between certain critical chunks:
250    /// 0: IHDR-`PLTE`, 1: `PLTE`-IDAT, 2: IDAT-IEND
251    /// Must be boxed for FFI hack.
252    #[allow(clippy::box_collection)]
253    pub(crate) unknown_chunks: [Box<Vec<u8>>; 3],
254    /// Ugly hack for back-compat with C FFI
255    pub(crate) always_zero_for_ffi_hack: [usize; 3],
256
257    ///  non-international text chunks (tEXt and zTXt)
258    ///
259    ///  The `char**` arrays each contain num strings. The actual messages are in
260    ///  text_strings, while text_keys are keywords that give a short description what
261    ///  the actual text represents, e.g. Title, Author, Description, or anything else.
262    ///
263    ///  A keyword is minimum 1 character and maximum 79 characters long. It's
264    ///  discouraged to use a single line length longer than 79 characters for texts.
265    pub(crate) texts: Vec<LatinText>,
266
267    ///  international text chunks (iTXt)
268    ///  Similar to the non-international text chunks, but with additional strings
269    ///  "langtags" and "transkeys".
270    pub(crate) itexts: Vec<IntlText>,
271}
272
273#[derive(Debug, Clone)]
274pub(crate) struct LatinText {
275    pub(crate) key: Box<[u8]>,
276    pub(crate) value: Box<[u8]>,
277}
278
279#[derive(Debug, Clone)]
280pub(crate) struct IntlText {
281    pub(crate) key: Box<str>,
282    pub(crate) langtag: Box<str>,
283    pub(crate) transkey: Box<str>,
284    pub(crate) value: Box<str>,
285}
286
287/// Settings for the decoder. This contains settings for the PNG and the Zlib decoder, but not the `Info` settings from the `Info` structs.
288#[repr(C)]
289#[derive(Clone, Debug)]
290pub struct DecoderSettings {
291    /// in here is the setting to ignore Adler32 checksums
292    pub zlibsettings: DecompressSettings,
293    /// ignore CRC checksums
294    pub ignore_crc: bool,
295    pub color_convert: bool,
296    pub read_text_chunks: bool,
297    pub remember_unknown_chunks: bool,
298}
299
300/// automatically use color type with less bits per pixel if losslessly possible. Default: `AUTO`
301#[repr(C)]
302#[derive(Copy, Clone, Debug, PartialEq, Eq)]
303pub enum FilterStrategy {
304    /// every filter at zero
305    ZERO = 0,
306    /// Use filter that gives minumum sum, as described in the official PNG filter heuristic.
307    MINSUM,
308    /// Use the filter type that gives smallest Shannon entropy for this scanline. Depending
309    /// on the image, this is better or worse than minsum.
310    ENTROPY,
311    /// Brute-force-search PNG filters by compressing each filter for each scanline.
312    /// Experimental, very slow, and only rarely gives better compression than MINSUM.
313    BRUTE_FORCE,
314    /// use predefined_filters buffer: you specify the filter type for each scanline.
315    /// See `Encoder::set_predefined_filters`.
316    PREDEFINED,
317}
318
319#[repr(C)]
320#[derive(Clone, Debug)]
321pub struct EncoderSettings {
322    /// settings for the zlib encoder, such as window size, ...
323    pub zlibsettings: CompressSettings,
324    /// how to automatically choose output PNG color type, if at all
325    pub auto_convert: bool,
326    /// If true, follows the official PNG heuristic: if the PNG uses a palette or lower than
327    /// 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to
328    /// completely follow the official PNG heuristic, filter_palette_zero must be true and
329    /// filter_strategy must be FilterStrategy::MINSUM
330    pub filter_palette_zero: bool,
331    /// Which filter strategy to use when not using zeroes due to filter_palette_zero.
332    /// Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: FilterStrategy::MINSUM
333    pub filter_strategy: FilterStrategy,
334
335    /// used if filter_strategy is FilterStrategy::PREDEFINED. In that case, this must point to a buffer with
336    /// the same length as the amount of scanlines in the image, and each value must <= 5. You
337    /// have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero
338    /// must be set to 0 to ensure this is also used on palette or low bitdepth images
339    pub(crate) predefined_filters: *const u8,
340
341    /// force creating a `PLTE` chunk if colortype is 2 or 6 (= a suggested palette).
342    /// If colortype is 3, `PLTE` is _always_ created
343    pub force_palette: bool,
344    /// add LodePNG identifier and version as a text chunk, for debugging
345    pub add_id: bool,
346    /// encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks
347    pub text_compression: bool,
348}
349
350unsafe impl Send for EncoderSettings {}
351unsafe impl Sync for EncoderSettings {}
352
353/// The settings, state and information for extended encoding and decoding
354#[repr(C)]
355#[derive(Clone, Debug)]
356pub struct State {
357    pub decoder: DecoderSettings,
358    pub encoder: EncoderSettings,
359
360    /// specifies the format in which you would like to get the raw pixel buffer
361    pub info_raw: ColorMode,
362    /// info of the PNG image obtained after decoding
363    pub info_png: Info,
364    pub error: ErrorCode,
365}
366
367/// Gives characteristics about the colors of the image, which helps decide which color model to use for encoding.
368///
369/// Used internally by default if `auto_convert` is enabled. Public because it's useful for custom algorithms.
370#[repr(C)]
371#[derive(Debug)]
372pub struct ColorProfile {
373    /// not greyscale
374    pub colored: bool,
375    /// image is not opaque and color key is possible instead of full alpha
376    pub key: bool,
377    /// key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255
378    pub key_r: u16,
379    pub key_g: u16,
380    pub key_b: u16,
381    /// image is not opaque and alpha channel or alpha palette required
382    pub alpha: bool,
383    /// amount of colors, up to 257. Not valid if bits == 16.
384    /// bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.
385    pub bits: u8,
386    pub numcolors: u16,
387    /// Remembers up to the first 256 RGBA colors, in no particular order
388    pub palette: [crate::RGBA; 256],
389}
390
391impl fmt::Debug for CompressSettings {
392    #[allow(deprecated)]
393    #[cold]
394    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395        let mut s = f.debug_struct("CompressSettings");
396        s.field("minmatch", &self.minmatch);
397        s.field("use_lz77", &self.use_lz77);
398        s.field("custom_zlib", &self.custom_zlib.is_some());
399        s.field("custom_deflate", &self.custom_deflate.is_some());
400        s.field("custom_context", &self.custom_context);
401        s.finish()
402    }
403}