1 //! Small, portable C library for high-quality conversion of RGBA images to 8-bit indexed-color (palette) images.
2 //! It's powering [pngquant2](https://pngquant.org).
3 //!
4 //! This is a low-level crate exposing a C API. If you're looking for a Rust library, see [imagequant](https://crates.io/crates/imagequant).
5 //!
6 //! ## License
7 //!
8 //! Libimagequant is dual-licensed:
9 //!
10 //! * For Free/Libre Open Source Software it's available under [GPL v3 or later](https://raw.github.com/ImageOptim/libimagequant/master/COPYRIGHT) with additional copyright notices for older parts of the code.
11 //!
12 //! * For use in non-GPL software (e.g. closed-source or App Store distribution) please ask kornel@pngquant.org for a commercial license.
13 //!
14 //! ## Overview
15 //!
16 //! The basic flow is:
17 //!
18 //! 1. Create attributes object and configure the library.
19 //! 2. Create image object from RGBA pixels or data source.
20 //! 3. Perform quantization (generate palette).
21 //! 4. Store remapped image and final palette.
22 //! 5. Free memory.
23 //!
24 //! Please note that libimagequant only handles raw uncompressed arrays of pixels in memory and is completely independent of any file format.
25 //!
26 //! There are 3 ways to create image object for quantization:
27 //!
28 //!   * `liq_image_create_rgba()` for simple, contiguous RGBA pixel arrays (width×height×4 bytes large bitmap).
29 //!   * `liq_image_create_rgba_rows()` for non-contiguous RGBA pixel arrays (that have padding between rows or reverse order, e.g. BMP).
30 //!   * `liq_image_create_custom()` for RGB, ABGR, YUV and all other formats that can be converted on-the-fly to RGBA (you have to supply the conversion function).
31 //!
32 //! Note that "image" here means raw uncompressed pixels. If you have a compressed image file, such as PNG, you must use another library (e.g. lodepng) to decode it first.
33 
34 #![allow(non_camel_case_types)]
35 
36 #[cfg(feature = "openmp")]
37 extern crate openmp_sys;
38 
39 use std::os::raw::{c_int, c_uint, c_char, c_void};
40 use std::error;
41 use std::fmt;
42 
43 pub enum liq_attr {}
44 pub enum liq_image {}
45 pub enum liq_result {}
46 pub enum liq_histogram {}
47 pub type liq_color = rgb::RGBA8;
48 
49 #[repr(C)]
50 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
51 pub enum liq_error {
52     LIQ_OK = 0,
53     LIQ_QUALITY_TOO_LOW = 99,
54     LIQ_VALUE_OUT_OF_RANGE = 100,
55     LIQ_OUT_OF_MEMORY,
56     LIQ_ABORTED,
57     LIQ_BITMAP_NOT_AVAILABLE,
58     LIQ_BUFFER_TOO_SMALL,
59     LIQ_INVALID_POINTER,
60     LIQ_UNSUPPORTED,
61 }
62 
63 bitflags::bitflags! {
64     #[repr(C)]
65     pub struct liq_ownership: c_int {
66         /// Moves ownership of the rows array. It will free it using `free()` or custom allocator.
67         const LIQ_OWN_ROWS = 4;
68         /// Moves ownership of the pixel data. It will free it using `free()` or custom allocator.
69         const LIQ_OWN_PIXELS = 8;
70         /// Makes a copy of the pixels, so the `liq_image` is not tied to pixel's lifetime.
71         const LIQ_COPY_PIXELS = 16;
72     }
73 }
74 
75 #[repr(C)]
76 pub struct liq_palette {
77     pub count: c_int,
78     pub entries: [liq_color; 256],
79 }
80 
81 #[repr(C)]
82 #[derive(Debug, Copy, Clone)]
83 pub struct liq_histogram_entry {
84     pub color: liq_color,
85     pub count: c_uint,
86 }
87 
88 impl error::Error for liq_error {
89 }
90 
91 impl fmt::Display for liq_error {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result92     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93         f.write_str(match *self {
94             liq_error::LIQ_OK => "OK",
95             liq_error::LIQ_QUALITY_TOO_LOW => "QUALITY_TOO_LOW",
96             liq_error::LIQ_VALUE_OUT_OF_RANGE => "VALUE_OUT_OF_RANGE",
97             liq_error::LIQ_OUT_OF_MEMORY => "OUT_OF_MEMORY",
98             liq_error::LIQ_ABORTED => "ABORTED",
99             liq_error::LIQ_BITMAP_NOT_AVAILABLE => "BITMAP_NOT_AVAILABLE",
100             liq_error::LIQ_BUFFER_TOO_SMALL => "BUFFER_TOO_SMALL",
101             liq_error::LIQ_INVALID_POINTER => "INVALID_POINTER",
102             liq_error::LIQ_UNSUPPORTED => "UNSUPPORTED",
103         })
104     }
105 }
106 
107 impl liq_error {
108     #[inline]
is_ok(&self) -> bool109     pub fn is_ok(&self) -> bool {
110         *self == liq_error::LIQ_OK
111     }
112 
is_err(&self) -> bool113     pub fn is_err(&self) -> bool {
114         !self.is_ok()
115     }
116 
117     #[inline]
ok(&self) -> Result<(), liq_error>118     pub fn ok(&self) -> Result<(), liq_error> {
119         match *self {
120             liq_error::LIQ_OK => Ok(()),
121             e => Err(e),
122         }
123     }
124 
125     #[inline]
ok_or<E>(self, err: E) -> Result<(), E>126     pub fn ok_or<E>(self, err: E) -> Result<(), E> {
127         if self.is_ok() {
128             Ok(())
129         } else {
130             Err(err)
131         }
132     }
133 
unwrap(&self)134     pub fn unwrap(&self) {
135         assert!(self.is_ok(), "{}", self);
136     }
137 
expect(&self, msg: &str)138     pub fn expect(&self, msg: &str) {
139         assert!(self.is_ok(), "{}", msg);
140     }
141 }
142 
143 pub type liq_log_callback_function = Option<unsafe extern "C" fn(arg1: &liq_attr, message: *const c_char, user_info: *mut c_void)>;
144 pub type liq_log_flush_callback_function = Option<unsafe extern "C" fn(arg1: &liq_attr, user_info: *mut c_void)>;
145 pub type liq_progress_callback_function = Option<unsafe extern "C" fn(progress_percent: f32, user_info: *mut c_void) -> c_int>;
146 pub type liq_image_get_rgba_row_callback = unsafe extern "C" fn(row_out: *mut liq_color, row: c_int, width: c_int, user_info: *mut c_void);
147 
148 #[link(name="imagequant", kind="static")]
149 extern "C" {
150 
151     /// Returns object that will hold initial settings (attributes) for the library.
152     ///
153     /// The object should be freed using `liq_attr_destroy()` after it's no longer needed.
154     /// Returns `NULL` in the unlikely case that the library cannot run on the current machine (e.g. the library has been compiled for SSE-capable x86 CPU and run on VIA C3 CPU).
liq_attr_create() -> *mut liq_attr155     pub fn liq_attr_create() -> *mut liq_attr;
liq_attr_create_with_allocator(malloc: unsafe extern "C" fn(usize) -> *mut c_void, free: unsafe extern "C" fn(*mut c_void)) -> *mut liq_attr156     pub fn liq_attr_create_with_allocator(malloc: unsafe extern "C" fn(usize) -> *mut c_void, free: unsafe extern "C" fn(*mut c_void)) -> *mut liq_attr;
liq_attr_copy(orig: &liq_attr) -> *mut liq_attr157     pub fn liq_attr_copy(orig: &liq_attr) -> *mut liq_attr;
liq_attr_destroy(attr: &mut liq_attr)158     pub fn liq_attr_destroy(attr: &mut liq_attr);
159 
160     /// Specifies maximum number of colors to use.
161     ///
162     /// The default is 256. Instead of setting a fixed limit it's better to use `liq_set_quality()`.
163     /// The first argument is attributes object from `liq_attr_create()`.
164     /// Returns `LIQ_VALUE_OUT_OF_RANGE` if number of colors is outside the range 2-256.
liq_set_max_colors(attr: &mut liq_attr, colors: c_int) -> liq_error165     pub fn liq_set_max_colors(attr: &mut liq_attr, colors: c_int) -> liq_error;
liq_get_max_colors(attr: &liq_attr) -> c_int166     pub fn liq_get_max_colors(attr: &liq_attr) -> c_int;
liq_set_speed(attr: &mut liq_attr, speed: c_int) -> liq_error167     pub fn liq_set_speed(attr: &mut liq_attr, speed: c_int) -> liq_error;
liq_get_speed(attr: &liq_attr) -> c_int168     pub fn liq_get_speed(attr: &liq_attr) -> c_int;
liq_set_min_posterization(attr: &mut liq_attr, bits: c_int) -> liq_error169     pub fn liq_set_min_posterization(attr: &mut liq_attr, bits: c_int) -> liq_error;
liq_get_min_posterization(attr: &liq_attr) -> c_int170     pub fn liq_get_min_posterization(attr: &liq_attr) -> c_int;
171     /// Quality is in range `0` (worst) to `100` (best) and values are analoguous to JPEG quality (i.e. `80` is usually good enough).
172     ///
173     /// Quantization will attempt to use the lowest number of colors needed to achieve `maximum` quality. `maximum` value of `100` is the default and means conversion as good as possible.
174     /// If it's not possible to convert the image with at least `minimum` quality (i.e. 256 colors is not enough to meet the minimum quality), then `liq_image_quantize()` will fail. The default minumum is `0` (proceeds regardless of quality).
175     /// Quality measures how well the generated palette fits image given to `liq_image_quantize()`. If a different image is remapped with `liq_write_remapped_image()` then actual quality may be different.
176     /// Regardless of the quality settings the number of colors won't exceed the maximum (see `liq_set_max_colors()`).
177     /// The first argument is attributes object from `liq_attr_create()`.
178     ///
179     /// Returns `LIQ_VALUE_OUT_OF_RANGE` if target is lower than minimum or any of them is outside the 0-100 range.
180     /// Returns `LIQ_INVALID_POINTER` if `attr` appears to be invalid.
liq_set_quality(attr: &mut liq_attr, minimum: c_int, maximum: c_int) -> liq_error181     pub fn liq_set_quality(attr: &mut liq_attr, minimum: c_int, maximum: c_int) -> liq_error;
liq_get_min_quality(attr: &liq_attr) -> c_int182     pub fn liq_get_min_quality(attr: &liq_attr) -> c_int;
liq_get_max_quality(attr: &liq_attr) -> c_int183     pub fn liq_get_max_quality(attr: &liq_attr) -> c_int;
liq_set_last_index_transparent(attr: &mut liq_attr, is_last: c_int)184     pub fn liq_set_last_index_transparent(attr: &mut liq_attr, is_last: c_int);
185 
liq_image_create_rgba_rows(attr: &liq_attr, rows: *const *const u8, width: c_int, height: c_int, gamma: f64) -> *mut liq_image186     pub fn liq_image_create_rgba_rows(attr: &liq_attr, rows: *const *const u8, width: c_int, height: c_int, gamma: f64) -> *mut liq_image;
187     /// Creates an object that represents the image pixels to be used for quantization and remapping.
188     ///
189     /// The pixel array must be contiguous run of RGBA pixels (alpha is the last component, 0 = transparent, 255 = opaque).
190     ///
191     /// The first argument is attributes object from `liq_attr_create()`. The same `attr` object should be used for the entire process, from creation of images to quantization.
192     ///
193     /// The `pixels` array must not be modified or freed until this object is freed with `liq_image_destroy()`. See also `liq_image_set_memory_ownership()`.
194     ///
195     /// `width` and `height` are dimensions in pixels. An image 10x10 pixel large will need a 400-byte array.
196     ///
197     /// `gamma` can be `0` for images with the typical 1/2.2 [gamma](https://en.wikipedia.org/wiki/Gamma_correction).
198     /// Otherwise `gamma` must be > 0 and < 1, e.g. `0.45455` (1/2.2) or `0.55555` (1/1.8). Generated palette will use the same gamma unless `liq_set_output_gamma()` is used. If `liq_set_output_gamma` is not used, then it only affects whether brighter or darker areas of the image will get more palette colors allocated.
199     ///
200     /// Returns `NULL` on failure, e.g. if `pixels` is `NULL` or `width`/`height` is <= 0.
liq_image_create_rgba(attr: &liq_attr, bitmap: *const u8, width: c_int, height: c_int, gamma: f64) -> *mut liq_image201     pub fn liq_image_create_rgba(attr: &liq_attr, bitmap: *const u8, width: c_int, height: c_int, gamma: f64) -> *mut liq_image;
202     /// unsafe: It will crash if the owned memory wasn't allocated using `libc::malloc()` (or whatever allocator C side is using)
liq_image_set_memory_ownership(image: &liq_image, own: liq_ownership) -> liq_error203     pub fn liq_image_set_memory_ownership(image: &liq_image, own: liq_ownership) -> liq_error;
204 
liq_set_log_callback(arg1: &mut liq_attr, arg2: liq_log_callback_function, user_info: *mut c_void)205     pub fn liq_set_log_callback(arg1: &mut liq_attr, arg2: liq_log_callback_function, user_info: *mut c_void);
liq_set_log_flush_callback(arg1: &mut liq_attr, arg2: liq_log_flush_callback_function, user_info: *mut c_void)206     pub fn liq_set_log_flush_callback(arg1: &mut liq_attr, arg2: liq_log_flush_callback_function, user_info: *mut c_void);
liq_attr_set_progress_callback(arg1: &mut liq_attr, arg2: liq_progress_callback_function, user_info: *mut c_void)207     pub fn liq_attr_set_progress_callback(arg1: &mut liq_attr, arg2: liq_progress_callback_function, user_info: *mut c_void);
liq_result_set_progress_callback(arg1: &mut liq_result, arg2: liq_progress_callback_function, user_info: *mut c_void)208     pub fn liq_result_set_progress_callback(arg1: &mut liq_result, arg2: liq_progress_callback_function, user_info: *mut c_void);
liq_image_create_custom(attr: &liq_attr, row_callback: liq_image_get_rgba_row_callback, user_info: *mut c_void, width: c_int, height: c_int, gamma: f64) -> *mut liq_image209     pub fn liq_image_create_custom(attr: &liq_attr, row_callback: liq_image_get_rgba_row_callback, user_info: *mut c_void, width: c_int, height: c_int, gamma: f64) -> *mut liq_image;
210     /// Remap assuming the image will be always presented exactly on top of this background.
211     ///
212     /// Takes ownership of the background image (i.e. do NOT use it afterwards, do NOT call liq_image_destroy on it).
213     ///
214     /// The background must have the same size. The foreground image must have a transparent color.
liq_image_set_background(img: &mut liq_image, background: *mut liq_image) -> liq_error215     pub fn liq_image_set_background(img: &mut liq_image, background: *mut liq_image) -> liq_error;
liq_image_set_importance_map(img: &mut liq_image, buffer: *mut u8, buffer_size: usize, own: liq_ownership) -> liq_error216     pub fn liq_image_set_importance_map(img: &mut liq_image, buffer: *mut u8, buffer_size: usize, own: liq_ownership) -> liq_error;
liq_image_add_fixed_color(img: &mut liq_image, color: liq_color) -> liq_error217     pub fn liq_image_add_fixed_color(img: &mut liq_image, color: liq_color) -> liq_error;
liq_image_get_width(img: &liq_image) -> c_int218     pub fn liq_image_get_width(img: &liq_image) -> c_int;
liq_image_get_height(img: &liq_image) -> c_int219     pub fn liq_image_get_height(img: &liq_image) -> c_int;
liq_image_destroy(img: &mut liq_image)220     pub fn liq_image_destroy(img: &mut liq_image);
221 
liq_histogram_create(attr: &liq_attr) -> *mut liq_histogram222     pub fn liq_histogram_create(attr: &liq_attr) -> *mut liq_histogram;
liq_histogram_add_image(hist: &mut liq_histogram, attr: &liq_attr, image: &mut liq_image) -> liq_error223     pub fn liq_histogram_add_image(hist: &mut liq_histogram, attr: &liq_attr, image: &mut liq_image) -> liq_error;
liq_histogram_add_colors(hist: &mut liq_histogram, attr: &liq_attr, entries: *const liq_histogram_entry, num_entries: c_int, gamma: f64) -> liq_error224     pub fn liq_histogram_add_colors(hist: &mut liq_histogram, attr: &liq_attr, entries: *const liq_histogram_entry, num_entries: c_int, gamma: f64) -> liq_error;
liq_histogram_add_fixed_color(hist: &mut liq_histogram, color: liq_color) -> liq_error225     pub fn liq_histogram_add_fixed_color(hist: &mut liq_histogram, color: liq_color) -> liq_error;
liq_histogram_destroy(hist: &mut liq_histogram)226     pub fn liq_histogram_destroy(hist: &mut liq_histogram);
227 
228     /// Performs quantization (palette generation) based on settings in `attr` (from `liq_attr_create()`) and pixels of the image.
229     ///
230     /// Returns `LIQ_OK` if quantization succeeds and sets `liq_result` pointer in `out_result`. The last argument is used for receiving the `result` object:
231     ///
232     ///     liq_result *result;
233     ///     if (LIQ_OK == liq_image_quantize(img, attr, &result)) { // Note &result
234     ///         // result pointer is valid here
235     ///     }
236     ///
237     /// Returns `LIQ_QUALITY_TOO_LOW` if quantization fails due to limit set in `liq_set_quality()`.
238     ///
239     /// See `liq_write_remapped_image()`.
240     ///
241     /// If you want to generate one palette for multiple images at once, see `liq_histogram_create()`.
liq_quantize_image(options: &liq_attr, input_image: &liq_image) -> *mut liq_result242     pub fn liq_quantize_image(options: &liq_attr, input_image: &liq_image) -> *mut liq_result;
liq_histogram_quantize(input_hist: &liq_histogram, options: &liq_attr, result_output: &mut *mut liq_result) -> liq_error243     pub fn liq_histogram_quantize(input_hist: &liq_histogram, options: &liq_attr, result_output: &mut *mut liq_result) -> liq_error;
liq_image_quantize(input_image: &liq_image, options: &liq_attr, result_output: &mut *mut liq_result) -> liq_error244     pub fn liq_image_quantize(input_image: &liq_image, options: &liq_attr, result_output: &mut *mut liq_result) -> liq_error;
245     /// Enables/disables dithering in `liq_write_remapped_image()`.
246     ///
247     /// Dithering level must be between `0` and `1` (inclusive). Dithering level `0` enables fast non-dithered remapping. Otherwise a variation of Floyd-Steinberg error diffusion is used.
248     ///
249     /// Precision of the dithering algorithm depends on the speed setting, see `liq_set_speed()`.
250     ///
251     /// Returns `LIQ_VALUE_OUT_OF_RANGE` if the dithering level is outside the 0-1 range.
liq_set_dithering_level(res: &mut liq_result, dither_level: f32) -> liq_error252     pub fn liq_set_dithering_level(res: &mut liq_result, dither_level: f32) -> liq_error;
liq_set_output_gamma(res: &mut liq_result, gamma: f64) -> liq_error253     pub fn liq_set_output_gamma(res: &mut liq_result, gamma: f64) -> liq_error;
liq_get_output_gamma(result: &liq_result) -> f64254     pub fn liq_get_output_gamma(result: &liq_result) -> f64;
255     /// Returns pointer to palette optimized for image that has been quantized or remapped
256     /// (final refinements are applied to the palette during remapping).
257     ///
258     /// It's valid to call this method before remapping, if you don't plan to remap any images or want to use same palette for multiple images.
259     ///
260     /// `liq_palette->count` contains number of colors (up to 256), `liq_palette->entries[n]` contains RGBA value for nth palette color.
liq_get_palette<'a>(result: &'a mut liq_result) -> &'a liq_palette261     pub fn liq_get_palette<'a>(result: &'a mut liq_result) -> &'a liq_palette;
262     /// Remaps the image to palette and writes its pixels to the given buffer, 1 pixel per byte.
263     ///
264     /// The buffer must be large enough to fit the entire image, i.e. width×height bytes large. For safety, pass the size of the buffer as `buffer_size`.
265     ///
266     /// For best performance call `liq_get_palette()` *after* this function, as palette is improved during remapping (except when `liq_histogram_quantize()` is used).
267     ///
268     /// Returns `LIQ_BUFFER_TOO_SMALL` if given size of the buffer is not enough to fit the entire image.
269     ///
270     ///     int buffer_size = width*height;
271     ///     char *buffer = malloc(buffer_size);
272     ///     if (LIQ_OK == liq_write_remapped_image(result, input_image, buffer, buffer_size)) {
273     ///         liq_palette *pal = liq_get_palette(result);
274     ///         // save image
275     ///     }
276     ///
277     /// See `liq_get_palette()`.
278     ///
279     /// The buffer is assumed to be contiguous, with rows ordered from top to bottom, and no gaps between rows. If you need to write rows with padding or upside-down order, then use `liq_write_remapped_image_rows()`.
280     ///
281     /// Please note that it only writes raw uncompressed pixels to memory. It does not perform any PNG compression. If you'd like to create a PNG file then you need to pass the raw pixel data to another library, e.g. libpng or lodepng. See `rwpng.c` in `pngquant` project for an example how to do that.
liq_write_remapped_image(result: &mut liq_result, input_image: &mut liq_image, buffer: *mut u8, buffer_size: usize) -> liq_error282     pub fn liq_write_remapped_image(result: &mut liq_result, input_image: &mut liq_image, buffer: *mut u8, buffer_size: usize) -> liq_error;
liq_write_remapped_image_rows(result: &mut liq_result, input_image: &mut liq_image, row_pointers: *const *mut u8) -> liq_error283     pub fn liq_write_remapped_image_rows(result: &mut liq_result, input_image: &mut liq_image, row_pointers: *const *mut u8) -> liq_error;
liq_get_quantization_error(result: &liq_result) -> f64284     pub fn liq_get_quantization_error(result: &liq_result) -> f64;
liq_get_quantization_quality(result: &liq_result) -> c_int285     pub fn liq_get_quantization_quality(result: &liq_result) -> c_int;
286 
liq_result_destroy(res: &mut liq_result)287     pub fn liq_result_destroy(res: &mut liq_result);
liq_get_remapping_error(result: &liq_result) -> f64288     pub fn liq_get_remapping_error(result: &liq_result) -> f64;
liq_get_remapping_quality(result: &liq_result) -> c_int289     pub fn liq_get_remapping_quality(result: &liq_result) -> c_int;
liq_version() -> c_int290     pub fn liq_version() -> c_int;
291 }
292 
293 #[test]
ownership_bitflags()294 fn ownership_bitflags() {
295     assert_eq!(4+16, (liq_ownership::LIQ_OWN_ROWS | liq_ownership::LIQ_COPY_PIXELS).bits());
296 }
297 
298 #[test]
links_and_runs()299 fn links_and_runs() {
300     use std::ptr;
301     unsafe {
302         assert!(liq_version() >= 20901);
303         let attr = liq_attr_create();
304         assert!(!attr.is_null());
305         let hist = liq_histogram_create(&*attr);
306         assert!(!hist.is_null());
307         let mut res = ptr::null_mut();
308         liq_histogram_quantize(&*hist, &*attr, &mut res);
309         assert!(res.is_null());
310         liq_histogram_destroy(&mut *hist);
311         liq_attr_destroy(&mut *attr);
312     }
313 }
314