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}