Returns `LIQ_VALUE_OUT_OF_RANGE` if the speed is outside the 1-10 range.
----
int liq_get_speed(liq_attr* attr);
Returns the value set by `liq_set_speed()`.
----
liq_error liq_set_min_opacity(liq_attr* attr, int min);
This was a workaround for Internet Explorer 6, but because this browser is not used any more, this option has been deprecated and removed.
----
int liq_get_min_opacity(liq_attr* attr);
This function has been deprecated.
----
liq_set_min_posterization(liq_attr* attr, int bits);
Ignores given number of least significant bits in all channels, posterizing image to `2^bits` levels. `0` gives full quality. Use `2` for VGA or 16-bit RGB565 displays, `4` if image is going to be output on a RGB444/RGBA4444 display (e.g. low-quality textures on Android).
Returns `LIQ_VALUE_OUT_OF_RANGE` if the value is outside the 0-4 range.
----
int liq_get_min_posterization(liq_attr* attr);
Returns the value set by `liq_set_min_posterization()`.
----
liq_set_last_index_transparent(liq_attr* attr, int is_last);
`0` (default) makes alpha colors sorted before opaque colors. Non-`0` mixes colors together except completely transparent color, which is moved to the end of the palette. This is a workaround for programs that blindly assume the last palette entry is transparent.
----
liq_image *liq_image_create_custom(liq_attr *attr, liq_image_get_rgba_row_callback *row_callback, void *user_info, int width, int height, double gamma);
void image_get_rgba_row_callback(liq_color row_out[], int row_index, int width, void *user_info) {
for(int column_index=0; column_index < width; column_index++) {
row_out[column_index] = /* generate pixel at (row_index, column_index) */;
}
}
Creates image object that will use callback to read image data. This allows on-the-fly conversion of images that are not in the RGBA color space.
`user_info` value will be passed to the callback. It may be useful for storing pointer to program's internal representation of the image.
The callback must read/generate `row_index`-th row and write its RGBA pixels to the `row_out` array. Row `width` is given for convenience and will always equal to image width.
The callback will be called multiple times for each row. Quantization and remapping require at least two full passes over image data, so caching of callback's work makes no sense — in such case it's better to convert entire image and use `liq_image_create_rgba()` instead.
To use RGB image:
void rgb_to_rgba_callback(liq_color row_out[], int row_index, int width, void *user_info) {
unsigned char *rgb_row = ((unsigned char *)user_info) + 3*width*row_index;
for(int i=0; i < width; i++) {
row_out[i].r = rgb_row[i*3];
row_out[i].g = rgb_row[i*3+1];
row_out[i].b = rgb_row[i*3+2];
row_out[i].a = 255;
}
}
liq_image *img = liq_image_create_custom(attr, rgb_to_rgba_callback, rgb_bitmap, width, height, 0);
The library doesn't support RGB bitmaps "natively", because supporting only single format allows compiler to inline more code, 4-byte pixel alignment is faster, and SSE instructions operate on 4 values at once, so alpha support is almost free.
----
liq_error liq_image_set_memory_ownership(liq_image *image, int ownership_flags);
Passes ownership of image pixel data and/or its rows array to the `liq_image` object, so you don't have to free it yourself. Memory owned by the object will be freed at its discretion with `free` function specified in `liq_attr_create_with_allocator()` (by default it's stdlib's `free()`).
* `LIQ_OWN_PIXELS` makes pixel array owned by the object. The pixels will be freed automatically at any point when it's no longer needed. If you set this flag you must **not** free the pixel array yourself. If the image has been created with `liq_image_create_rgba_rows()` then the starting address of the array of pixels is assumed to be the lowest address of any row.
* `LIQ_OWN_ROWS` makes array of row pointers (but not the pixels pointed by these rows) owned by the object. Rows will be freed when object is deallocated. If you set this flag you must **not** free the rows array yourself. This flag is valid only if the object has been created with `liq_image_create_rgba_rows()`.
These flags can be combined with binary *or*, i.e. `LIQ_OWN_PIXELS | LIQ_OWN_ROWS`.
This function must not be used if the image has been created with `liq_image_create_custom()`.
Returns `LIQ_VALUE_OUT_OF_RANGE` if invalid flags are specified or the image object only takes pixels from a callback.
----
liq_error liq_image_set_background(liq_image *image, liq_image *background_image);
Analyze and remap this image with assumption that it will be always presented exactly on top of this background.
When this image is remapped to a palette with a fully transparent color (use `liq_image_add_fixed_color()` to ensure this) pixels that are better represented by the background than the palette will be made transparent. This function can be used to improve quality of animated GIFs by setting previous animation frame as the background.
This function takes full ownership of the background image, so you should **not** free the background object. It will be freed automatically together with the foreground image.
Returns `LIQ_BUFFER_TOO_SMALL` if the background image has a different size than the foreground.
----
liq_error liq_image_set_importance_map(liq_image *image, unsigned char map[], size_t buffer_size, liq_ownership ownership);
Impotance map controls which areas of the image get more palette colors. Pixels corresponding to 0 values in the map are completely ignored. The higher the value the more weight is placed on the given pixel, giving it higher chance of influencing the final palette.
The map is one byte per pixel and must have the same size as the image (width×height bytes). `buffer_size` argument is used to double-check that.
If the `ownership` is `LIQ_COPY_PIXELS` then the `map` content be copied immediately (it's up to you to ensure the `map` memory is freed).
If the `ownership` is `LIQ_OWN_PIXELS` then the `map` memory will be owned by the image and will be freed automatically when the image is freed. If a custom allocator has been set using `liq_attr_create_with_allocator()`, the `map` must be allocated using the same allocator. This option is deprecated. Use the Rust API or `LIQ_COPY_PIXELS` instead.
Returns `LIQ_INVALID_POINTER` if any pointer is `NULL`, `LIQ_BUFFER_TOO_SMALL` if the `buffer_size` does not match the image size, and `LIQ_UNSUPPORTED` if `ownership` isn't a valid value.
----
liq_error liq_write_remapped_image_rows(liq_result *result, liq_image *input_image, unsigned char **row_pointers);
Similar to `liq_write_remapped_image()`. Writes remapped image, at 1 byte per pixel, to each row pointed by `row_pointers` array. The array must have at least as many elements as height of the image, and each row must have at least as many bytes as width of the image. Rows must not overlap.
For best performance call `liq_get_palette()` *after* this function, as remapping may change the palette (except when `liq_histogram_quantize()` is used).
Returns `LIQ_INVALID_POINTER` if `result` or `input_image` is `NULL`.
----
double liq_get_quantization_error(liq_result *result);
Returns mean square error of quantization (square of difference between pixel values in the source image and its remapped version). Alpha channel, gamma correction and approximate importance of pixels is taken into account, so the result isn't exactly the mean square error of all channels.
For most images MSE 1-5 is excellent. 7-10 is OK. 20-30 will have noticeable errors. 100 is awful.
This function may return `-1` if the value is not available (this happens when a high speed has been requested, the image hasn't been remapped yet, and quality limit hasn't been set, see `liq_set_speed()` and `liq_set_quality()`). The value is not updated when multiple images are remapped, it applies only to the image used in `liq_image_quantize()` or the first image that has been remapped. See `liq_get_remapping_error()`.
----
double liq_get_remapping_error(liq_result *result);
Returns mean square error of last remapping done (square of difference between pixel values in the remapped image and its remapped version). Alpha channel and gamma correction are taken into account, so the result isn't exactly the mean square error of all channels.
This function may return `-1` if the value is not available (this happens when a high speed has been requested or the image hasn't been remapped yet).
----
double liq_get_quantization_quality(liq_result *result);
Analoguous to `liq_get_quantization_error()`, but returns quantization error as quality value in the same 0-100 range that is used by `liq_set_quality()`.
It may return `-1` if the value is not available (see note in `liq_get_quantization_error()`).
This function can be used to add upper limit to quality options presented to the user, e.g.
liq_attr *attr = liq_attr_create();
liq_image *img = liq_image_create_rgba(…);
liq_result *res;
liq_image_quantize(img, attr, &res);
int max_attainable_quality = liq_get_quantization_quality(res);
printf("Please select quality between 0 and %d: ", max_attainable_quality);
int user_selected_quality = prompt();
if (user_selected_quality < max_attainable_quality) {
liq_set_quality(user_selected_quality, 0);
liq_result_destroy(res);
liq_image_quantize(img, attr, &res);
}
liq_write_remapped_image(…);
----
double liq_get_remapping_quality(liq_result *result);
Analoguous to `liq_get_remapping_error()`, but returns quantization error as quality value in the same 0-100 range that is used by `liq_set_quality()`.
----
void liq_set_log_callback(liq_attr*, liq_log_callback_function*, void *user_info);
void log_flush_callback_function(const liq_attr*, void *user_info) {}
Sets up callback function to be called when the library reports status or errors. The callback must not call any library functions.
`user_info` value will be passed through to the callback. It can be `NULL`.
`NULL` callback clears the current callback.
In the log callback the `message` is a zero-terminated string containing informative message to output. It is valid only until the callback returns, so you must copy it.
`liq_set_log_flush_callback()` sets up callback function that will be called after the last log callback, which can be used to flush buffers and free resources used by the log callback.
----
void liq_set_progress_callback(liq_attr*, liq_progress_callback_function*, void *user_info);
void liq_result_set_progress_callback(liq_result*, liq_progress_callback_function*, void *user_info);
int progress_callback_function(const liq_attr*, float progress_percent, void *user_info) {}
Sets up callback function to be called while the library is processing images. The callback may abort processing by returning `0`.
Setting callback to `NULL` clears the current callback. `liq_set_progress_callback` is for quantization progress, and `liq_result_set_progress_callback` is for remapping progress (currently only dithered remapping reports progress).
`user_info` value will be passed through to the callback. It can be `NULL`.
The callback must not call any library functions.
`progress_percent` is a value between 0 and 100 that estimates how much of the current task has been done.
The callback should return `1` to continue the operation, and `0` to abort current operation.
----
liq_attr* liq_attr_create_with_allocator(void* (*malloc)(size_t), void (*free)(void*));
This function is deprecated. Same as `liq_attr_create`, but specifies `free` to use for `liq_image_set_memory_ownership`. The `malloc` argument is not used.
The library will use Rust's [global allocator](https://doc.rust-lang.org/std/alloc/index.html).
----
liq_attr* liq_attr_copy(liq_attr *orig);
Creates an independent copy of `liq_attr`. The copy should also be freed using `liq_attr_destroy()`.
---
liq_error liq_set_output_gamma(liq_result* res, double gamma);
Sets gamma correction for generated palette and remapped image. Must be > 0 and < 1, e.g. `0.45455` for gamma 1/2.2 in PNG images. By default output gamma is same as gamma of the input image.
----
int liq_image_get_width(const liq_image *img);
int liq_image_get_height(const liq_image *img);
double liq_get_output_gamma(const liq_result *result);
Getters for `width`, `height` and `gamma` of the input image.
If the input is invalid, these all return -1.
---
liq_error liq_image_add_fixed_color(liq_image* img, liq_color color);
liq_error liq_histogram_add_fixed_color(liq_histogram* hist, liq_color color, double gamma);
Reserves a color in the output palette created from this image. It behaves as if the given color was used in the image and was very important.
RGB values of `liq_color` are assumed to have the same gamma as the image. For the histogram function, the `gamma` can be `0` (see `liq_image_create_rgba()`).
It must be called before the image is quantized.
Returns error if more than 256 colors are added. If image is quantized to fewer colors than the number of fixed colors added, then excess fixed colors will be ignored.
For histograms see also a more flexible `liq_histogram_add_colors()`.
---
int liq_version();
Returns version of the library as an integer. Same as `LIQ_VERSION`. Human-readable version is defined as `LIQ_VERSION_STRING`.
## Multiple images with the same palette
It's possible to efficiently generate a single palette that is optimal for multiple images, e.g. for an APNG animation. This is done by collecting statistics of images in a `liq_histogram` object.
liq_attr *attr = liq_attr_create();
liq_histogram *hist = liq_histogram_create(attr);
liq_image *image1 = liq_image_create_rgba(attr, example_bitmap_rgba1, width, height, 0);
liq_histogram_add_image(hist, attr, image1);
liq_image *image2 = liq_image_create_rgba(attr, example_bitmap_rgba2, width, height, 0);
liq_histogram_add_image(hist, attr, image2);
liq_result *result;
liq_error err = liq_histogram_quantize(attr, hist, &result);
if (LIQ_OK == err) {
// result will contain shared palette best for both image1 and image2
}
---
liq_histogram *liq_histogram_create(liq_attr *attr);
Creates histogram object that will be used to collect color statistics from multiple images. It must be freed using `liq_histogram_destroy()`.
All options should be set on `attr` before the histogram object is created. Options changed later may not have effect.
---
liq_error liq_histogram_add_image(liq_histogram *hist, liq_attr *attr, liq_image* image);
"Learns" colors from the image, which will be later used to generate the palette.
After the image is added to the histogram it may be freed to save memory (but it's more efficient to keep the image object if it's going to be used for remapping).
Fixed colors added to the image are also added to the histogram. If total number of fixed colors exceeds 256, this function will fail with `LIQ_BUFFER_TOO_SMALL`.
---
liq_error liq_histogram_add_colors(liq_histogram *hist, liq_attr *attr, liq_histogram_entry entries[], int num_entries, double gamma);
Alternative to `liq_histogram_add_image()`. Intead of counting colors in an image, it directly takes an array of colors and their counts (see `liq_histogram_entry` in `libimagequant.h`). This function is only useful if you already have a histogram of the image from another source.
For description of gamma, see `liq_image_create_rgba()`.
---
liq_error liq_histogram_quantize(liq_histogram *const hist, liq_attr *const attr, liq_result **out_result);
Generates palette from the histogram. On success returns `LIQ_OK` and writes `liq_result*` pointer to `out_result`. Use it as follows:
liq_result *result;
liq_error err = liq_histogram_quantize(attr, hist, &result);
if (LIQ_OK == err) {
// Use result here to remap and get palette
}
Returns `LIQ_QUALITY_TOO_LOW` if the palette is worse than limit set in `liq_set_quality()`. One histogram object can be quantized only once.
Palette generated using this function won't be improved during remapping. If you're generating palette for only one image, it's better to use `liq_image_quantize()`.
## Multithreading
* Different threads can perform unrelated quantizations/remappings at the same time (e.g. each thread working on a different image).
* The same `liq_attr`, `liq_result`, etc. can be accessed from different threads, but not at the same time (e.g. you can create `liq_attr` in one thread and free it in another).
* By default, this library uses threads internally. You can set `RAYON_NUM_THREADS` environmental variable to control the number of threads used. You can disable threads completely by compiling with `--no-default-features`.
## Working with GIF
The library can generate palettes for GIF images. To ensure correct transparency is used you need to preprocess the image yourself and replace alpha values other than 0 or 255 with one of these.
For animated GIFs see `liq_image_set_background()` which remaps images for GIF's "keep" frame disposal method. See [gif.ski](https://gif.ski).
## Cross-compilation
You can compile the library for other platforms via `cargo build --target=…`. See `rustup target list` for the list of platforms.
When compiling for WASM, you need to disable default features of this library (compile with `--no-default-features` flag). Otherwise it will use mult-threading, which requires [special handling in WASM](https://github.com/GoogleChromeLabs/wasm-bindgen-rayon).
If you're cross-compiling a dynamic library (so/dylib/DLL), you may need to [configure a linker](https://doc.rust-lang.org/cargo/reference/config.html#target) for Cargo. For building for Android see [this tutorial](https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-21-rust-on-android.html) and [cargo-ndk](https://lib.rs/crates/cargo-ndk).
libimagequant-4.4.0/imagequant-sys/build.rs 0000664 0000000 0000000 00000000136 15032747170 0020763 0 ustar 00root root 0000000 0000000 fn main() {
println!("cargo:include={}", std::env::var("CARGO_MANIFEST_DIR").unwrap());
}
libimagequant-4.4.0/imagequant-sys/c_test/ 0000775 0000000 0000000 00000000000 15032747170 0020577 5 ustar 00root root 0000000 0000000 libimagequant-4.4.0/imagequant-sys/c_test/Cargo.toml 0000664 0000000 0000000 00000000367 15032747170 0022535 0 ustar 00root root 0000000 0000000 [package]
name = "c_test"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
doctest = false
[package.metadata.release]
release = false
[dependencies]
imagequant-sys = { version = "4.0.3", path = ".." }
[build-dependencies]
cc = "1.1.7"
libimagequant-4.4.0/imagequant-sys/c_test/build.rs 0000664 0000000 0000000 00000000171 15032747170 0022243 0 ustar 00root root 0000000 0000000 fn main() {
cc::Build::new()
.include("..")
.file("test.c")
.compile("imagequanttestbin");
}
libimagequant-4.4.0/imagequant-sys/c_test/src/ 0000775 0000000 0000000 00000000000 15032747170 0021366 5 ustar 00root root 0000000 0000000 libimagequant-4.4.0/imagequant-sys/c_test/src/lib.rs 0000664 0000000 0000000 00000000244 15032747170 0022502 0 ustar 00root root 0000000 0000000 #[cfg(test)]
extern crate imagequant_sys;
#[cfg(test)]
extern "C" {
fn run_liq_tests();
}
#[test]
fn c_test() {
unsafe {
run_liq_tests();
}
}
libimagequant-4.4.0/imagequant-sys/c_test/test.c 0000664 0000000 0000000 00000011102 15032747170 0021715 0 ustar 00root root 0000000 0000000 #undef NDEBUG
#include
#include "libimagequant.h"
#include
#include
static char magic[] = "magic";
static void test_log_callback_function(const liq_attr *at, const char *message, void* user_info) {
assert(at);
assert(user_info == magic);
assert(message);
assert(strlen(message));
}
static int test_abort_callback(float progress_percent, void* user_info) {
assert(progress_percent >= 0.0 && progress_percent <= 100.0);
assert(user_info == magic);
return 0;
}
static int progress_called = 0;
static int test_continue_callback(float progress_percent, void* user_info) {
assert(progress_percent >= 0.0 && progress_percent <= 100.0);
assert(user_info == magic);
progress_called++;
return 1;
}
static void test_abort() {
liq_attr *attr = liq_attr_create();
unsigned char dummy[4] = {0};
liq_image *img = liq_image_create_rgba(attr, dummy, 1, 1, 0);
liq_attr_set_progress_callback(attr, test_abort_callback, magic);
liq_result *res = liq_quantize_image(attr, img);
assert(!res);
liq_attr_destroy(attr);
}
static void test_zero_histogram() {
liq_attr *attr = liq_attr_create();
liq_histogram *hist = liq_histogram_create(attr);
assert(hist);
liq_result *res;
liq_error err = liq_histogram_quantize(hist, attr, &res);
assert(!res);
assert(err);
liq_attr_destroy(attr);
liq_histogram_destroy(hist);
}
static void test_histogram() {
liq_attr *attr = liq_attr_create();
liq_histogram *hist = liq_histogram_create(attr);
assert(hist);
const unsigned char dummy1[4] = {255,0,255,255};
liq_image *const img1 = liq_image_create_rgba(attr, dummy1, 1, 1, 0);
assert(img1);
const liq_error err1 = liq_histogram_add_image(hist, attr, img1);
assert(LIQ_OK == err1);
const unsigned char dummy2[4] = {0,0,0,0};
liq_image *const img2 = liq_image_create_rgba(attr, dummy2, 1, 1, 0);
assert(img2);
liq_image_add_fixed_color(img2, (liq_color){255,255,255,255});
const liq_error err2 = liq_histogram_add_image(hist, attr, img2);
assert(LIQ_OK == err2);
liq_image_destroy(img1);
liq_image_destroy(img2);
liq_result *res;
liq_error err = liq_histogram_quantize(hist, attr, &res);
assert(LIQ_OK == err);
assert(res);
liq_attr_destroy(attr);
liq_histogram_destroy(hist);
const liq_palette *pal = liq_get_palette(res);
assert(pal);
assert(pal->count == 3);
liq_result_destroy(res);
}
static void test_fixed_colors() {
liq_attr *attr = liq_attr_create();
liq_attr_set_progress_callback(attr, test_continue_callback, magic);
liq_set_log_callback(attr, test_log_callback_function, magic);
unsigned char dummy[4] = {0};
liq_image *img = liq_image_create_rgba(attr, dummy, 1, 1, 0);
assert(img);
liq_image_add_fixed_color(img, (liq_color){0,0,0,0});
liq_result *res = liq_quantize_image(attr, img);
assert(res);
assert(progress_called);
const liq_palette *pal = liq_get_palette(res);
assert(pal);
assert(pal->count == 1);
liq_result_destroy(res);
liq_image_destroy(img);
liq_attr_destroy(attr);
}
static void test_fixed_colors_order() {
liq_attr *attr = liq_attr_create();
unsigned char dummy[4] = {0};
liq_image *img = liq_image_create_rgba(attr, dummy, 1, 1, 0);
assert(img);
liq_color colors[17] = {
{0,0,0,0}, {1,1,1,1}, {2,2,2,2}, {3,3,3,3}, {4,4,4,4}, {5,4,4,4},
{6,4,4,4}, {6,7,4,4}, {6,7,8,4}, {6,7,8,9}, {10,7,8,9}, {10,11,8,9},
{10,11,12,9}, {10,11,12,13}, {14,11,12,13}, {14,15,12,13}, {253,254,255,254},
};
for(int i=0; i < 17; i++) {
liq_image_add_fixed_color(img, colors[i]);
}
liq_result *res = liq_quantize_image(attr, img);
assert(res);
const liq_palette *pal = liq_get_palette(res);
assert(pal);
assert(pal->count == 17);
for(int i=0; i < 17; i++) {
assert(pal->entries[i].r == colors[i].r);
assert(pal->entries[i].g == colors[i].g);
assert(pal->entries[i].b == colors[i].b);
assert(pal->entries[i].a == colors[i].a);
}
liq_set_dithering_level(res, 1.0);
char buf[1];
assert(LIQ_OK == liq_write_remapped_image(res, img, buf, 1));
liq_result_set_progress_callback(res, test_abort_callback, magic);
assert(LIQ_ABORTED == liq_write_remapped_image(res, img, buf, 1));
liq_result_destroy(res);
liq_image_destroy(img);
liq_attr_destroy(attr);
}
void run_liq_tests() {
test_fixed_colors();
test_fixed_colors_order();
test_abort();
test_histogram();
test_zero_histogram();
}
libimagequant-4.4.0/imagequant-sys/example.c 0000664 0000000 0000000 00000007641 15032747170 0021125 0 ustar 00root root 0000000 0000000 /**
This is an example how to write your own simple pngquant using libimagequant.
libimagequant works with any PNG library. This example uses lodepng, because it's easier to use than libpng.
1. Get lodepng.c (download lodepng.cpp and rename it) and lodepng.h
from https://lodev.org/lodepng/ and put them in this directry
2. Compile libimagequant (see README.md)
3. Compile and run the example:
gcc -O3 example.c libimagequant.a -o example
./example truecolor_file.png
This example code can be freely copied under CC0 (public domain) license.
*/
#include "lodepng.h" // Get it from https://raw.githubusercontent.com/lvandeve/lodepng/master/lodepng.h
#include "lodepng.c" // Get it from https://raw.githubusercontent.com/lvandeve/lodepng/master/lodepng.cpp and rename
#include
#include
#include "libimagequant.h"
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Please specify a path to a PNG file\n");
return EXIT_FAILURE;
}
const char *input_png_file_path = argv[1];
// Load PNG file and decode it as raw RGBA pixels
// This uses lodepng library for PNG reading (not part of libimagequant)
unsigned int width, height;
unsigned char *raw_rgba_pixels;
unsigned int status = lodepng_decode32_file(&raw_rgba_pixels, &width, &height, input_png_file_path);
if (status) {
fprintf(stderr, "Can't load %s: %s\n", input_png_file_path, lodepng_error_text(status));
return EXIT_FAILURE;
}
// Use libimagequant to make a palette for the RGBA pixels
liq_attr *handle = liq_attr_create();
liq_image *input_image = liq_image_create_rgba(handle, raw_rgba_pixels, width, height, 0);
// You could set more options here, like liq_set_quality
liq_result *quantization_result;
if (liq_image_quantize(input_image, handle, &quantization_result) != LIQ_OK) {
fprintf(stderr, "Quantization failed\n");
return EXIT_FAILURE;
}
// Use libimagequant to make new image pixels from the palette
size_t pixels_size = width * height;
unsigned char *raw_8bit_pixels = malloc(pixels_size);
liq_set_dithering_level(quantization_result, 1.0);
liq_write_remapped_image(quantization_result, input_image, raw_8bit_pixels, pixels_size);
const liq_palette *palette = liq_get_palette(quantization_result);
// Save converted pixels as a PNG file
// This uses lodepng library for PNG writing (not part of libimagequant)
LodePNGState state;
lodepng_state_init(&state);
state.info_raw.colortype = LCT_PALETTE;
state.info_raw.bitdepth = 8;
state.info_png.color.colortype = LCT_PALETTE;
state.info_png.color.bitdepth = 8;
for(int i=0; i < palette->count; i++) {
lodepng_palette_add(&state.info_png.color, palette->entries[i].r, palette->entries[i].g, palette->entries[i].b, palette->entries[i].a);
lodepng_palette_add(&state.info_raw, palette->entries[i].r, palette->entries[i].g, palette->entries[i].b, palette->entries[i].a);
}
unsigned char *output_file_data;
size_t output_file_size;
unsigned int out_status = lodepng_encode(&output_file_data, &output_file_size, raw_8bit_pixels, width, height, &state);
if (out_status) {
fprintf(stderr, "Can't encode image: %s\n", lodepng_error_text(out_status));
return EXIT_FAILURE;
}
const char *output_png_file_path = "quantized_example.png";
FILE *fp = fopen(output_png_file_path, "wb");
if (!fp) {
fprintf(stderr, "Unable to write to %s\n", output_png_file_path);
return EXIT_FAILURE;
}
fwrite(output_file_data, 1, output_file_size, fp);
fclose(fp);
printf("Written %s\n", output_png_file_path);
// Done. Free memory.
liq_result_destroy(quantization_result); // Must be freed only after you're done using the palette
liq_image_destroy(input_image);
liq_attr_destroy(handle);
free(raw_8bit_pixels);
lodepng_state_cleanup(&state);
}
libimagequant-4.4.0/imagequant-sys/imagequant.pc.in 0000664 0000000 0000000 00000000470 15032747170 0022403 0 ustar 00root root 0000000 0000000 prefix=@PREFIX@
includedir=${prefix}/include
libdir=${prefix}/lib
Name: imagequant
Description: Small, portable C library for high-quality conversion of RGBA images to 8-bit indexed-color (palette) images.
URL: https://pngquant.org/lib/
Version: @VERSION@
Libs: -L${libdir} -limagequant
Cflags: -I${includedir}
libimagequant-4.4.0/imagequant-sys/imagequant.xcodeproj/ 0000775 0000000 0000000 00000000000 15032747170 0023445 5 ustar 00root root 0000000 0000000 libimagequant-4.4.0/imagequant-sys/imagequant.xcodeproj/project.pbxproj 0000664 0000000 0000000 00000032321 15032747170 0026522 0 ustar 00root root 0000000 0000000 // !$*UTF8*$!
{
/* generated with cargo-xcode 1.4.3 */
archiveVersion = 1;
classes = {
};
objectVersion = 53;
objects = {
/* Begin PBXBuildFile section */
CA60077B6400A6A410610A27 /* Cargo.toml in Sources */ = {
isa = PBXBuildFile;
fileRef = CA6091AFFEBB3EF4668187A5 /* Cargo.toml */;
settings = {
COMPILER_FLAGS = "--lib"; /* == OTHER_INPUT_FILE_FLAGS */
};
};
/* End PBXBuildFile section */
/* Begin PBXBuildRule section */
CA6091AFFEBBAC6C1400ACA8 /* PBXBuildRule */ = {
isa = PBXBuildRule;
compilerSpec = com.apple.compilers.proxy.script;
dependencyFile = "$(DERIVED_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME).d";
filePatterns = "*/Cargo.toml"; /* must contain asterisk */
fileType = pattern.proxy;
inputFiles = ();
isEditable = 0;
name = "Cargo project build";
outputFiles = (
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)",
);
script = "# generated with cargo-xcode 1.4.3\n\nset -eu; export PATH=$PATH:~/.cargo/bin:/usr/local/bin;\nif [ \"${IS_MACCATALYST-NO}\" = YES ]; then\n CARGO_XCODE_TARGET_TRIPLE=\"${CARGO_XCODE_TARGET_ARCH}-apple-ios-macabi\"\nelse\n CARGO_XCODE_TARGET_TRIPLE=\"${CARGO_XCODE_TARGET_ARCH}-apple-${CARGO_XCODE_TARGET_OS}\"\nfi\nif [ \"$CARGO_XCODE_TARGET_OS\" != \"darwin\" ]; then\n PATH=\"${PATH/\\/Contents\\/Developer\\/Toolchains\\/XcodeDefault.xctoolchain\\/usr\\/bin:/xcode-provided-ld-cant-link-lSystem-for-the-host-build-script:}\"\nfi\nPATH=\"$PATH:/opt/homebrew/bin\" # Rust projects often depend on extra tools like nasm, which Xcode lacks\nif [ \"$CARGO_XCODE_BUILD_MODE\" == release ]; then\n OTHER_INPUT_FILE_FLAGS=\"${OTHER_INPUT_FILE_FLAGS} --release\"\nfi\nif command -v rustup &> /dev/null; then\n if ! rustup target list --installed | egrep -q \"${CARGO_XCODE_TARGET_TRIPLE}\"; then\n echo \"warning: this build requires rustup toolchain for $CARGO_XCODE_TARGET_TRIPLE, but it isn\'t installed\"\n rustup target add \"${CARGO_XCODE_TARGET_TRIPLE}\" || echo >&2 \"warning: can\'t install $CARGO_XCODE_TARGET_TRIPLE\"\n fi\nfi\nif [ \"$ACTION\" = clean ]; then\n ( set -x; cargo clean --manifest-path=\"$SCRIPT_INPUT_FILE\" ${OTHER_INPUT_FILE_FLAGS} --target=\"${CARGO_XCODE_TARGET_TRIPLE}\"; );\nelse\n ( set -x; cargo build --manifest-path=\"$SCRIPT_INPUT_FILE\" --features=\"${CARGO_XCODE_FEATURES:-}\" ${OTHER_INPUT_FILE_FLAGS} --target=\"${CARGO_XCODE_TARGET_TRIPLE}\"; );\nfi\n# it\'s too hard to explain Cargo\'s actual exe path to Xcode build graph, so hardlink to a known-good path instead\nBUILT_SRC=\"${CARGO_TARGET_DIR}/${CARGO_XCODE_TARGET_TRIPLE}/${CARGO_XCODE_BUILD_MODE}/${CARGO_XCODE_CARGO_FILE_NAME}\"\nln -f -- \"$BUILT_SRC\" \"$SCRIPT_OUTPUT_FILE_0\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\nDEP_FILE_SRC=\"${CARGO_TARGET_DIR}/${CARGO_XCODE_TARGET_TRIPLE}/${CARGO_XCODE_BUILD_MODE}/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\nif [ -f \"$DEP_FILE_SRC\" ]; then\n DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\nfi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don\'t stay around after archs change\n# must match input for LipoScript\nFILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\ntouch \"$FILE_LIST\"\nif ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\nfi\n";
};
/* End PBXBuildRule section */
/* Begin PBXFileReference section */
CA60A7F2A4D647C0CE156F07 /* staticlib */ = {
isa = PBXFileReference;
explicitFileType = "archive.ar";
includeInIndex = 0;
name = "libimagequant_sys_static.a";
sourceTree = TARGET_BUILD_DIR;
};
CA6091AFFEBB3EF4668187A5 /* Cargo.toml */ = {
isa = PBXFileReference;
lastKnownFileType = text;
fileEncoding = 4;
name = "Cargo.toml";
path = "Cargo.toml";
sourceTree = "";
};
/* Rust needs libresolv */
ADDEDBA66A6E1 = {
isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition";
name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT;
};
/* End PBXFileReference section */
/* Begin PBXGroup section */
CA6091AFFEBB98AF0B5890DB /* Frameworks */ = {
isa = PBXGroup;
children = (
ADDEDBA66A6E2,
);
name = Frameworks;
sourceTree = "";
};
ADDEDBA66A6E2 /* Required for static linking */ = {
isa = PBXGroup;
children = (
ADDEDBA66A6E1
);
name = "Required for static linking";
sourceTree = "";
};
CA6091AFFEBB22869D176AE5 /* Products */ = {
isa = PBXGroup;
children = (
CA60A7F2A4D647C0CE156F07,
);
name = Products;
sourceTree = "";
};
CA6091AFFEBBD65BC3C892A8 /* Main */ = {
isa = PBXGroup;
children = (
CA6091AFFEBB3EF4668187A5,
CA6091AFFEBB22869D176AE5,
CA6091AFFEBB98AF0B5890DB,
);
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
CA60A7F2A4D6A6A410610A27 /* imagequant_sys-staticlib */ = {
isa = PBXNativeTarget;
buildConfigurationList = CA601449CA94A6A410610A27;
buildPhases = (
CA607796DFF0A6A410610A27 /* Sources */,
CA6091AFFEBBAF6EBB7F357C /* Universal Binary lipo */,
);
buildRules = (
CA6091AFFEBBAC6C1400ACA8 /* PBXBuildRule */,
);
dependencies = (
);
name = "imagequant_sys-staticlib";
productName = "libimagequant_sys_static.a";
productReference = CA60A7F2A4D647C0CE156F07;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
CA607796DFF0A6A410610A27 = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CA60077B6400A6A410610A27
);
runOnlyForDeploymentPostprocessing = 0;
};
CA601449CA94A6A410610A27 /* staticlib */ = {
isa = XCConfigurationList;
buildConfigurations = (
CA60994CA1EBA6A410610A27 /* Release */,
CA60FBBF8BCDA6A410610A27 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CA60994CA1EBA6A410610A27 /* staticlib */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "imagequant_sys_static";
"CARGO_XCODE_CARGO_FILE_NAME" = "libimagequant_sys.a";
"CARGO_XCODE_CARGO_DEP_FILE_NAME" = "libimagequant_sys.d";
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
SKIP_INSTALL = YES;
INSTALL_GROUP = "";
INSTALL_MODE_FLAG = "";
INSTALL_OWNER = "";
};
name = Release;
};
CA60FBBF8BCDA6A410610A27 /* staticlib */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = "imagequant_sys_static";
"CARGO_XCODE_CARGO_FILE_NAME" = "libimagequant_sys.a";
"CARGO_XCODE_CARGO_DEP_FILE_NAME" = "libimagequant_sys.d";
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
SKIP_INSTALL = YES;
INSTALL_GROUP = "";
INSTALL_MODE_FLAG = "";
INSTALL_OWNER = "";
};
name = Debug;
};
CA6091AFFEBBAF6EBB7F357C /* LipoScript */ = {
name = "Universal Binary lipo";
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = ();
inputFileListPaths = ();
inputPaths = (
"$(DERIVED_FILE_DIR)/$(ARCHS)-$(EXECUTABLE_NAME).xcfilelist",
);
outputFileListPaths = ();
outputPaths = (
"$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)"
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# generated with cargo-xcode 1.4.3\nset -eux; cat \"$DERIVED_FILE_DIR/$ARCHS-$EXECUTABLE_NAME.xcfilelist\" | tr '\\n' '\\0' | xargs -0 lipo -create -output \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"";
};
CA6091AFFEBB80E02D6C7F57 = {
isa = XCConfigurationList;
buildConfigurations = (
CA60B3CFE6713CC16B37690B /* Release */,
CA60B3CFE671228BE02872F8 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CA60B3CFE6713CC16B37690B = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
SUPPORTS_MACCATALYST = YES;
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target"; /* for cargo */
CARGO_XCODE_FEATURES = ""; /* configure yourself */
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = "aarch64";
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = "x86_64"; /* catalyst adds h suffix */
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = "i686";
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = "darwin";
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = "ios";
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = "ios";
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = "tvos";
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = "tvos";
PRODUCT_NAME = "imagequant-sys";
SDKROOT = macosx;
"CARGO_XCODE_BUILD_MODE" = "release"; /* for xcode scripts */
};
name = Release;
};
CA60B3CFE671228BE02872F8 = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
SUPPORTS_MACCATALYST = YES;
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target"; /* for cargo */
CARGO_XCODE_FEATURES = ""; /* configure yourself */
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = "aarch64";
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = "x86_64"; /* catalyst adds h suffix */
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = "i686";
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = "darwin";
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = "ios";
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = "ios";
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = "tvos";
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = "tvos";
PRODUCT_NAME = "imagequant-sys";
SDKROOT = macosx;
"CARGO_XCODE_BUILD_MODE" = "debug"; /* for xcode scripts */
ONLY_ACTIVE_ARCH = YES;
};
name = Debug;
};
CA6091AFFEBBE04653AD465F = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
TargetAttributes = {
CA60A7F2A4D6A6A410610A27 = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = CA6091AFFEBB80E02D6C7F57;
compatibilityVersion = "Xcode 11.4";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = CA6091AFFEBBD65BC3C892A8;
productRefGroup = CA6091AFFEBB22869D176AE5 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
CA60A7F2A4D6A6A410610A27,
);
};
};
rootObject = CA6091AFFEBBE04653AD465F;
}
libimagequant-4.4.0/imagequant-sys/libimagequant.cs 0000664 0000000 0000000 00000014717 15032747170 0022501 0 ustar 00root root 0000000 0000000 /*
This is an example demonstrating use of libimagequant from C#.
This example code can be freely copied under CC0 (public domain) license.
*/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
struct liq_color
{
public byte r, g, b, a;
};
[StructLayout(LayoutKind.Sequential)]
struct liq_palette
{
public int count;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public liq_color[] entries;
};
enum liq_error
{
LIQ_OK = 0,
LIQ_QUALITY_TOO_LOW = 99,
LIQ_VALUE_OUT_OF_RANGE = 100,
LIQ_OUT_OF_MEMORY,
LIQ_ABORTED,
LIQ_BITMAP_NOT_AVAILABLE,
LIQ_BUFFER_TOO_SMALL,
LIQ_INVALID_POINTER,
};
namespace liq
{
using liq_attr_ptr = IntPtr;
using liq_image_ptr = IntPtr;
using liq_result_ptr = IntPtr;
using size_t = UIntPtr;
class Liq
{
[DllImport(@"imagequant.dll")]
public static extern liq_attr_ptr liq_attr_create();
[DllImport(@"imagequant.dll")]
public static extern liq_attr_ptr liq_attr_copy(liq_attr_ptr attr);
[DllImport(@"imagequant.dll")]
public static extern void liq_attr_destroy(liq_attr_ptr attr);
[DllImport(@"imagequant.dll")]
public static extern liq_error liq_set_max_colors(liq_attr_ptr attr, int colors);
[DllImport(@"imagequant.dll")]
public static extern int liq_get_max_colors(liq_attr_ptr attr);
[DllImport(@"imagequant.dll")]
public static extern liq_error liq_set_speed(liq_attr_ptr attr, int speed);
[DllImport(@"imagequant.dll")]
public static extern int liq_get_speed(liq_attr_ptr attr);
[DllImport(@"imagequant.dll")]
public static extern liq_error liq_set_min_opacity(liq_attr_ptr attr, int min);
[DllImport(@"imagequant.dll")]
public static extern int liq_get_min_opacity(liq_attr_ptr attr);
[DllImport(@"imagequant.dll")]
public static extern liq_error liq_set_min_posterization(liq_attr_ptr attr, int bits);
[DllImport(@"imagequant.dll")]
public static extern int liq_get_min_posterization(liq_attr_ptr attr);
[DllImport(@"imagequant.dll")]
public static extern liq_error liq_set_quality(liq_attr_ptr attr, int minimum, int maximum);
[DllImport(@"imagequant.dll")]
public static extern int liq_get_min_quality(liq_attr_ptr attr);
[DllImport(@"imagequant.dll")]
public static extern int liq_get_max_quality(liq_attr_ptr attr);
[DllImport(@"imagequant.dll")]
public static extern void liq_set_last_index_transparent(liq_attr_ptr attr, int is_last);
[DllImport(@"imagequant.dll")]
public static extern liq_image_ptr liq_image_create_rgba(liq_attr_ptr attr, [In, MarshalAs(UnmanagedType.LPArray)] byte[] bitmap, int width, int height, double gamma);
[DllImport(@"imagequant.dll")]
public static extern liq_error liq_image_set_memory_ownership(liq_image_ptr image, int ownership_flags);
[DllImport(@"imagequant.dll")]
public static extern liq_error liq_image_add_fixed_color(liq_image_ptr img, liq_color color);
[DllImport(@"imagequant.dll")]
public static extern int liq_image_get_width(liq_image_ptr img);
[DllImport(@"imagequant.dll")]
public static extern int liq_image_get_height(liq_image_ptr img);
[DllImport(@"imagequant.dll")]
public static extern void liq_image_destroy(liq_image_ptr img);
[DllImport(@"imagequant.dll")]
public static extern liq_result_ptr liq_quantize_image(liq_attr_ptr attr, liq_image_ptr input_image);
[DllImport(@"imagequant.dll")]
public static extern liq_error liq_set_dithering_level(liq_result_ptr res, float dither_level);
[DllImport(@"imagequant.dll")]
public static extern liq_error liq_set_output_gamma(liq_result_ptr res, double gamma);
[DllImport(@"imagequant.dll")]
public static extern double liq_get_output_gamma(liq_result_ptr res);
[DllImport(@"imagequant.dll")]
public static extern IntPtr liq_get_palette(liq_result_ptr res);
[DllImport(@"imagequant.dll")]
public static extern liq_error liq_write_remapped_image(liq_result_ptr res, liq_image_ptr input_image, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] buffer, size_t buffer_size);
[DllImport(@"imagequant.dll")]
public static extern double liq_get_quantization_error(liq_result_ptr res);
[DllImport(@"imagequant.dll")]
public static extern int liq_get_quantization_quality(liq_result_ptr res);
[DllImport(@"imagequant.dll")]
public static extern double liq_get_remapping_error(liq_result_ptr res);
[DllImport(@"imagequant.dll")]
public static extern int liq_get_remapping_quality(liq_result_ptr res);
[DllImport(@"imagequant.dll")]
public static extern void liq_result_destroy(liq_result_ptr res);
[DllImport(@"imagequant.dll")]
public static extern int liq_version();
static void Main(string[] args)
{
Console.WriteLine("library version: {0}", liq_version());
int width = 3;
int height = 1;
var attr = liq_attr_create();
if (attr == IntPtr.Zero) throw new Exception("can't create attr");
byte[] bitmap = { // R, G, B, A, R, G, B, A, ...
111, 222, 33, 255,
255, 0, 255, 255,
255, 0, 255, 255,
};
var img = liq_image_create_rgba(attr, bitmap, width, height, 0);
if (img == IntPtr.Zero) throw new Exception("can't create image");
var res = liq_quantize_image(attr, img);
if (res == IntPtr.Zero) throw new Exception("can't quantize image");
var buffer_size = width * height;
var remapped = new byte[buffer_size];
var err = liq_write_remapped_image(res, img, remapped, (UIntPtr)buffer_size);
if (err != liq_error.LIQ_OK)
{
throw new Exception("remapping error");
}
Console.WriteLine("first pixel is {0}th palette entry", remapped[0]);
liq_palette pal = (liq_palette)Marshal.PtrToStructure(liq_get_palette(res), typeof(liq_palette));
Console.WriteLine("palette entries: {0}; red of first entry: {1}", pal.count, pal.entries[0].r);
liq_image_destroy(img);
liq_result_destroy(res);
liq_attr_destroy(attr);
}
}
}
libimagequant-4.4.0/imagequant-sys/libimagequant.h 0000664 0000000 0000000 00000015721 15032747170 0022317 0 ustar 00root root 0000000 0000000 /*
* https://pngquant.org
*/
#ifndef LIBIMAGEQUANT_H
#define LIBIMAGEQUANT_H
#ifdef IMAGEQUANT_EXPORTS
#define LIQ_EXPORT __declspec(dllexport)
#endif
#ifndef LIQ_EXPORT
#define LIQ_EXPORT extern
#endif
#define LIQ_VERSION 40003
#define LIQ_VERSION_STRING "4.0.3"
#ifndef LIQ_PRIVATE
#if defined(__GNUC__) || defined (__llvm__)
#define LIQ_PRIVATE __attribute__((visibility("hidden")))
#define LIQ_NONNULL __attribute__((nonnull))
#define LIQ_USERESULT __attribute__((warn_unused_result))
#else
#define LIQ_PRIVATE
#define LIQ_NONNULL
#define LIQ_USERESULT
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include
typedef struct liq_attr liq_attr;
typedef struct liq_image liq_image;
typedef struct liq_result liq_result;
typedef struct liq_histogram liq_histogram;
typedef struct liq_color {
unsigned char r, g, b, a;
} liq_color;
typedef struct liq_palette {
unsigned int count;
liq_color entries[256];
} liq_palette;
typedef enum liq_error {
LIQ_OK = 0,
LIQ_QUALITY_TOO_LOW = 99,
LIQ_VALUE_OUT_OF_RANGE = 100,
LIQ_OUT_OF_MEMORY,
LIQ_ABORTED,
LIQ_BITMAP_NOT_AVAILABLE,
LIQ_BUFFER_TOO_SMALL,
LIQ_INVALID_POINTER,
LIQ_UNSUPPORTED,
} liq_error;
enum liq_ownership {
LIQ_OWN_ROWS=4,
LIQ_OWN_PIXELS=8,
LIQ_COPY_PIXELS=16,
};
typedef struct liq_histogram_entry {
liq_color color;
unsigned int count;
} liq_histogram_entry;
LIQ_EXPORT LIQ_USERESULT liq_attr* liq_attr_create(void);
LIQ_EXPORT LIQ_USERESULT liq_attr* liq_attr_create_with_allocator(void* removed, void *unsupported);
LIQ_EXPORT LIQ_USERESULT liq_attr* liq_attr_copy(const liq_attr *orig) LIQ_NONNULL;
LIQ_EXPORT void liq_attr_destroy(liq_attr *attr) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT liq_histogram* liq_histogram_create(const liq_attr* attr);
LIQ_EXPORT liq_error liq_histogram_add_image(liq_histogram *hist, const liq_attr *attr, liq_image* image) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_histogram_add_colors(liq_histogram *hist, const liq_attr *attr, const liq_histogram_entry entries[], int num_entries, double gamma) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_histogram_add_fixed_color(liq_histogram *hist, liq_color color, double gamma) LIQ_NONNULL;
LIQ_EXPORT void liq_histogram_destroy(liq_histogram *hist) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_set_max_colors(liq_attr* attr, int colors) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT int liq_get_max_colors(const liq_attr* attr) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_set_speed(liq_attr* attr, int speed) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT int liq_get_speed(const liq_attr* attr) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_set_min_opacity(liq_attr* attr, int min) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT int liq_get_min_opacity(const liq_attr* attr) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_set_min_posterization(liq_attr* attr, int bits) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT int liq_get_min_posterization(const liq_attr* attr) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_set_quality(liq_attr* attr, int minimum, int maximum) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT int liq_get_min_quality(const liq_attr* attr) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT int liq_get_max_quality(const liq_attr* attr) LIQ_NONNULL;
LIQ_EXPORT void liq_set_last_index_transparent(liq_attr* attr, int is_last) LIQ_NONNULL;
typedef void liq_log_callback_function(const liq_attr*, const char *message, void* user_info);
typedef void liq_log_flush_callback_function(const liq_attr*, void* user_info);
LIQ_EXPORT void liq_set_log_callback(liq_attr*, liq_log_callback_function*, void* user_info);
LIQ_EXPORT void liq_set_log_flush_callback(liq_attr*, liq_log_flush_callback_function*, void* user_info);
typedef int liq_progress_callback_function(float progress_percent, void* user_info);
LIQ_EXPORT void liq_attr_set_progress_callback(liq_attr*, liq_progress_callback_function*, void* user_info);
LIQ_EXPORT void liq_result_set_progress_callback(liq_result*, liq_progress_callback_function*, void* user_info);
// The rows and their data are not modified. The type of `rows` is non-const only due to a bug in C's typesystem design.
LIQ_EXPORT LIQ_USERESULT liq_image *liq_image_create_rgba_rows(const liq_attr *attr, void *const rows[], int width, int height, double gamma) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT liq_image *liq_image_create_rgba(const liq_attr *attr, const void *bitmap, int width, int height, double gamma) LIQ_NONNULL;
typedef void liq_image_get_rgba_row_callback(liq_color row_out[], int row, int width, void* user_info);
LIQ_EXPORT LIQ_USERESULT liq_image *liq_image_create_custom(const liq_attr *attr, liq_image_get_rgba_row_callback *row_callback, void* user_info, int width, int height, double gamma);
LIQ_EXPORT liq_error liq_image_set_memory_ownership(liq_image *image, int ownership_flags) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_image_set_background(liq_image *img, liq_image *background_image) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_image_set_importance_map(liq_image *img, unsigned char buffer[], size_t buffer_size, enum liq_ownership memory_handling) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_image_add_fixed_color(liq_image *img, liq_color color) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT int liq_image_get_width(const liq_image *img) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT int liq_image_get_height(const liq_image *img) LIQ_NONNULL;
LIQ_EXPORT void liq_image_destroy(liq_image *img) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT liq_error liq_histogram_quantize(liq_histogram *const input_hist, liq_attr *const options, liq_result **result_output) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT liq_error liq_image_quantize(liq_image *const input_image, liq_attr *const options, liq_result **result_output) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT liq_error liq_result_from_palette(const liq_attr *options, const liq_color *palette, unsigned int palette_size, double gamma, liq_result **result_output) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_set_dithering_level(liq_result *res, float dither_level) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_set_output_gamma(liq_result* res, double gamma) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT double liq_get_output_gamma(const liq_result *result) LIQ_NONNULL;
LIQ_EXPORT LIQ_USERESULT const liq_palette *liq_get_palette(liq_result *result) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_write_remapped_image(liq_result *result, liq_image *input_image, void *buffer, size_t buffer_size) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_write_remapped_image_rows(liq_result *result, liq_image *input_image, unsigned char **row_pointers) LIQ_NONNULL;
LIQ_EXPORT double liq_get_quantization_error(const liq_result *result) LIQ_NONNULL;
LIQ_EXPORT int liq_get_quantization_quality(const liq_result *result) LIQ_NONNULL;
LIQ_EXPORT double liq_get_remapping_error(const liq_result *result) LIQ_NONNULL;
LIQ_EXPORT int liq_get_remapping_quality(const liq_result *result) LIQ_NONNULL;
LIQ_EXPORT void liq_result_destroy(liq_result *) LIQ_NONNULL;
LIQ_EXPORT int liq_version(void);
// Deprecated
LIQ_EXPORT LIQ_USERESULT liq_result *liq_quantize_image(liq_attr *options, liq_image *input_image) LIQ_NONNULL;
#ifdef __cplusplus
}
#endif
#endif
libimagequant-4.4.0/imagequant-sys/org/ 0000775 0000000 0000000 00000000000 15032747170 0020105 5 ustar 00root root 0000000 0000000 libimagequant-4.4.0/imagequant-sys/org/pngquant/ 0000775 0000000 0000000 00000000000 15032747170 0021742 5 ustar 00root root 0000000 0000000 libimagequant-4.4.0/imagequant-sys/org/pngquant/Image.java 0000664 0000000 0000000 00000005110 15032747170 0023624 0 ustar 00root root 0000000 0000000 package org.pngquant;
import org.pngquant.*;
import java.awt.image.*;
/**
* PngQuant's representation of an Image constructed from BufferedImage.
*/
public class Image extends LiqObject {
/**
* Converts BufferedImage to internal representation (pixel data is copied).
* It's best to use BufferedImage in RGB/RGBA format backed by DataBufferByte.
* Throws if conversion fails.
*/
public Image(BufferedImage image) throws PngQuantException {
this(new PngQuant(), image);
}
public Image(PngQuant attr, BufferedImage image) throws PngQuantException {
handle = handleFromImage(attr, image);
if (handle == 0) {
BufferedImage converted = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
converted.getGraphics().drawImage(image, 0, 0, null);
handle = handleFromImage(attr, converted);
if (handle == 0) {
throw new PngQuantException();
}
}
}
/**
* Guarantees presence of the given color in the palette (subject to setMaxColors())
* if this image is used for quantization.
*/
public native boolean addFixedColor(int r, int g, int b, int a);
public boolean addFixedColor(int r, int g, int b) {
return addFixedColor(r, g, b, 255);
}
public native int getWidth();
public native int getHeight();
public void close() {
if (handle != 0) {
liq_image_destroy(handle);
handle = 0;
}
}
private static long handleFromImage(PngQuant attr, BufferedImage image) {
// The JNI wrapper will accept non-premultiplied ABGR and BGR only.
int type = image.getType();
if (type != BufferedImage.TYPE_3BYTE_BGR &&
type != BufferedImage.TYPE_4BYTE_ABGR &&
type != BufferedImage.TYPE_4BYTE_ABGR_PRE) return 0;
WritableRaster raster = image.getRaster();
ColorModel color = image.getColorModel();
if (type == BufferedImage.TYPE_4BYTE_ABGR_PRE) color.coerceData(raster, false);
DataBuffer buffer = raster.getDataBuffer();
if (buffer instanceof DataBufferByte) {
byte[] imageData = ((DataBufferByte)buffer).getData();
return liq_image_create(attr.handle, imageData,
raster.getWidth(), raster.getHeight(), color.getNumComponents());
}
return 0;
}
private static native long liq_image_create(long attr, byte[] bitmap, int width, int height, int components);
private static native void liq_image_destroy(long handle);
}
libimagequant-4.4.0/imagequant-sys/org/pngquant/LiqObject.java 0000664 0000000 0000000 00000000645 15032747170 0024466 0 ustar 00root root 0000000 0000000 package org.pngquant;
abstract class LiqObject {
static {
// libimagequant.jnilib or libimagequant.so must be in java.library.path
System.loadLibrary("imagequant");
}
long handle;
/**
* Free memory used by the library. The object must not be used after this call.
*/
abstract public void close();
protected void finalize() throws Throwable {
close();
}
}
libimagequant-4.4.0/imagequant-sys/org/pngquant/PngQuant.c 0000664 0000000 0000000 00000014666 15032747170 0023660 0 ustar 00root root 0000000 0000000 #include "org/pngquant/PngQuant.h"
#include "org/pngquant/Image.h"
#include "org/pngquant/Result.h"
#include "libimagequant.h"
#include
typedef struct {
liq_image *image;
jbyte *data;
} liq_jni_image;
static void *handle(JNIEnv *env, jobject obj) {
jlong h = (*env)->GetLongField(env, obj, (*env)->GetFieldID(env, (*env)->GetObjectClass(env, obj), "handle", "J"));
return (void*)h;
}
JNIEXPORT jlong JNICALL Java_org_pngquant_PngQuant_liq_1attr_1create(JNIEnv *env, jclass class) {
return (jlong)liq_attr_create();
}
JNIEXPORT jlong JNICALL Java_org_pngquant_PngQuant_liq_1attr_1copy(JNIEnv *env, jclass class, jlong attr) {
return (jlong)liq_attr_copy((liq_attr*)attr);
}
JNIEXPORT void JNICALL Java_org_pngquant_PngQuant_liq_1attr_1destroy(JNIEnv *env, jclass class, jlong attr) {
return liq_attr_destroy((liq_attr*)attr);
}
JNIEXPORT jboolean JNICALL Java_org_pngquant_PngQuant_setMaxColors(JNIEnv *env, jobject obj, jint colors) {
return LIQ_OK == liq_set_max_colors(handle(env, obj), colors);
}
JNIEXPORT jboolean JNICALL Java_org_pngquant_PngQuant_setSpeed(JNIEnv *env, jobject obj, jint speed) {
return LIQ_OK == liq_set_speed(handle(env, obj), speed);
}
JNIEXPORT jboolean JNICALL Java_org_pngquant_PngQuant_setMinPosterization(JNIEnv *env, jobject obj, jint p) {
return LIQ_OK == liq_set_min_posterization(handle(env, obj), p);
}
JNIEXPORT jboolean JNICALL Java_org_pngquant_PngQuant_setQuality__I(JNIEnv *env, jobject obj, jint q) {
return LIQ_OK == liq_set_quality(handle(env, obj), q/2, q);
}
JNIEXPORT jboolean JNICALL Java_org_pngquant_PngQuant_setQuality__II(JNIEnv *env, jobject obj, jint qmin, jint qmax) {
return LIQ_OK == liq_set_quality(handle(env, obj), qmin, qmax);
}
static void convert_abgr(liq_color row_out[], int row_index, int width, void* user_info) {
liq_jni_image *jniimg = user_info;
int column_index;
for(column_index=0; column_index < width; column_index++) {
row_out[column_index].r = jniimg->data[4*(width*row_index + column_index) + 3];
row_out[column_index].g = jniimg->data[4*(width*row_index + column_index) + 2];
row_out[column_index].b = jniimg->data[4*(width*row_index + column_index) + 1];
row_out[column_index].a = jniimg->data[4*(width*row_index + column_index) + 0];
}
}
static void convert_bgr(liq_color row_out[], int row_index, int width, void* user_info) {
liq_jni_image *jniimg = user_info;
int column_index;
for(column_index=0; column_index < width; column_index++) {
row_out[column_index].r = jniimg->data[3*(width*row_index + column_index) + 2];
row_out[column_index].g = jniimg->data[3*(width*row_index + column_index) + 1];
row_out[column_index].b = jniimg->data[3*(width*row_index + column_index) + 0];
row_out[column_index].a = 255;
}
}
JNIEXPORT jlong JNICALL Java_org_pngquant_Image_liq_1image_1create(JNIEnv *env, jclass class, jlong attr, jbyteArray bytearray, jint w, jint h, jint components) {
/* liq_image needs to be wrapped to keep track of allocated buffer */
liq_jni_image *jniimg = malloc(sizeof(liq_jni_image));
/* copying buffer, since ReleaseByteArrayElements was crashing when called from finalize() */
jsize size = (*env)->GetArrayLength(env, bytearray);
jniimg->data = malloc(size);
(*env)->GetByteArrayRegion(env, bytearray, 0, size, jniimg->data);
jniimg->image = liq_image_create_custom((liq_attr*)attr, components == 4 ? convert_abgr : convert_bgr, jniimg, w, h, 0);
if (!jniimg->image) {
free(jniimg->data);
free(jniimg);
return 0;
}
return (jlong)jniimg;
}
JNIEXPORT jboolean JNICALL Java_org_pngquant_Image_addFixedColor(JNIEnv *env, jobject obj, jint r, jint g, jint b, jint a) {
liq_color c = {r,g,b,a};
return LIQ_OK == liq_image_add_fixed_color(((liq_jni_image*)handle(env,obj))->image, c);
}
JNIEXPORT jint JNICALL Java_org_pngquant_Image_getWidth(JNIEnv *env, jobject obj) {
return liq_image_get_width(((liq_jni_image*)handle(env,obj))->image);
}
JNIEXPORT jint JNICALL Java_org_pngquant_Image_getHeight(JNIEnv *env, jobject obj) {
return liq_image_get_height(((liq_jni_image*)handle(env,obj))->image);
}
JNIEXPORT void JNICALL Java_org_pngquant_Image_liq_1image_1destroy(JNIEnv *env, jclass class, jlong handle) {
liq_jni_image *jniimg = (liq_jni_image*)handle;
liq_image_destroy(jniimg->image);
free(jniimg->data);
free(jniimg);
}
JNIEXPORT jlong JNICALL Java_org_pngquant_Result_liq_1quantize_1image(JNIEnv *env, jclass class, jlong attr, jlong handle) {
return (jlong)liq_quantize_image((liq_attr*)attr, ((liq_jni_image*)handle)->image);
}
JNIEXPORT jboolean JNICALL Java_org_pngquant_Result_setDitheringLevel(JNIEnv *env, jobject obj, jfloat l) {
return LIQ_OK == liq_set_dithering_level(handle(env, obj), l);
}
JNIEXPORT jboolean JNICALL Java_org_pngquant_Result_setGamma(JNIEnv *env, jobject obj, jdouble gamma) {
return LIQ_OK == liq_set_output_gamma(handle(env, obj), gamma);
}
JNIEXPORT jdouble JNICALL Java_org_pngquant_Result_getGamma(JNIEnv *env, jobject obj) {
return liq_get_output_gamma(handle(env, obj));
}
JNIEXPORT jboolean JNICALL Java_org_pngquant_Result_liq_1write_1remapped_1image(JNIEnv *env, jclass class, jlong result, jlong image_handle, jbyteArray bytearray) {
jsize size = (*env)->GetArrayLength(env, bytearray);
jbyte *bitmap = (*env)->GetByteArrayElements(env, bytearray, 0);
liq_error err = liq_write_remapped_image((liq_result*)result, ((liq_jni_image*)image_handle)->image, bitmap, size);
(*env)->ReleaseByteArrayElements(env, bytearray, bitmap, 0);
return LIQ_OK == err;
}
JNIEXPORT jdouble JNICALL Java_org_pngquant_Result_getMeanSquareError(JNIEnv *env, jobject obj) {
return liq_get_quantization_error(handle(env, obj));
}
JNIEXPORT jint JNICALL Java_org_pngquant_Result_getQuality(JNIEnv *env, jobject obj) {
return liq_get_quantization_quality(handle(env, obj));
}
JNIEXPORT void JNICALL Java_org_pngquant_Result_liq_1result_1destroy(JNIEnv *env, jclass class, jlong result) {
return liq_result_destroy((liq_result*)result);
}
JNIEXPORT jbyteArray JNICALL Java_org_pngquant_Result_liq_1get_1palette(JNIEnv *env, jclass class, jlong result) {
const liq_palette *pal = liq_get_palette((liq_result*)result);
jbyteArray arr = (*env)->NewByteArray(env, pal->count * 4);
int i;
for(i=0; i < pal->count; i++) {
(*env)->SetByteArrayRegion(env, arr, i*4, 4, ((jbyte*)&pal->entries[i]));
}
return arr;
}
libimagequant-4.4.0/imagequant-sys/org/pngquant/PngQuant.java 0000664 0000000 0000000 00000006356 15032747170 0024354 0 ustar 00root root 0000000 0000000 package org.pngquant;
import org.pngquant.*;
import java.awt.image.*;
/**
* Starting point for the library. Holds configuration. Equivalent of liq_attr* in libimagequant.
*/
public class PngQuant extends LiqObject {
/**
* Single instance can be "recycled" for many remappings.
*/
public PngQuant() {
handle = liq_attr_create();
}
public PngQuant(PngQuant other) {
handle = liq_attr_copy(other.handle);
}
/**
* 1-shot quantization and remapping with current settings.
* @see quantize()
*
* @return 8-bit indexed image or null on failure
*/
public BufferedImage getRemapped(BufferedImage bufimg) {
try {
Image liqimg = new Image(this, bufimg);
BufferedImage remapped = getRemapped(liqimg);
liqimg.close();
return remapped;
} catch(PngQuantException e) {
return null;
}
}
/** @return remapped image or null on failure */
public BufferedImage getRemapped(Image liqimg) {
Result result = quantize(liqimg);
if (result == null) return null;
BufferedImage remapped = result.getRemapped(liqimg);
result.close();
return remapped;
}
/**
* Performs quantization (chooses optimal palette for the given Image).
* Returned object can be used to customize remapping and reused to remap other images to the same palette.
* @link http://pngquant.org/lib/#liq_quantize_image
*
* @return null on failure
*/
public Result quantize(Image img) {
try {
return new Result(this, img);
} catch(PngQuantException e) {
return null;
}
}
/**
* Remapped images won't use more than given number of colors (may use less if setQuality() is used)
*
* @link http://pngquant.org/lib/#liq_set_max_colors
*/
public native boolean setMaxColors(int colors);
/**
* Equivalent of setQuality(target/2, target)
*
* @link http://pngquant.org/lib/#liq_set_quality
*/
public native boolean setQuality(int target);
/**
* Quality in range 0-100. Quantization will fail if minimum quality cannot
* be achieved with given number of colors.
*
* @link http://pngquant.org/lib/#liq_set_quality
*/
public native boolean setQuality(int min, int max);
/**
* Speed in range 1 (slowest) and 11 (fastest). 3 is the optimum.
* Higher speeds quantize quicker, but at cost of quality and sometimes larger images.
*
* @link http://pngquant.org/lib/#liq_set_speed
*/
public native boolean setSpeed(int speed);
/**
* Reduces color precision by truncating number of least significant bits.
* Slightly improves speed and helps generating images for low-fidelity displays/textures.
*
* @link http://pngquant.org/lib/#liq_set_min_posterization
*/
public native boolean setMinPosterization(int bits);
public void close() {
if (handle != 0) {
liq_attr_destroy(handle);
handle = 0;
}
}
private static native long liq_attr_create();
private static native long liq_attr_copy(long orig);
private static native void liq_attr_destroy(long handle);
}
libimagequant-4.4.0/imagequant-sys/org/pngquant/PngQuantException.java 0000664 0000000 0000000 00000000114 15032747170 0026215 0 ustar 00root root 0000000 0000000 package org.pngquant;
public class PngQuantException extends Exception {
}
libimagequant-4.4.0/imagequant-sys/org/pngquant/Result.java 0000664 0000000 0000000 00000005733 15032747170 0024073 0 ustar 00root root 0000000 0000000 package org.pngquant;
import org.pngquant.*;
import java.awt.image.*;
/**
* Quantization result that holds palette and options for remapping.
*/
public class Result extends LiqObject {
/**
* Throws when quantization fails (e.g. due to failing to achieve minimum quality)
*/
public Result(PngQuant pngquant, Image image) throws PngQuantException {
handle = liq_quantize_image(pngquant.handle, image.handle);
if (handle == 0) {
throw new PngQuantException();
}
}
/**
* @return BufferedImage remapped to palette this Result has been created with or null on failure.
*/
public BufferedImage getRemapped(Image orig_image) {
byte[] pal = liq_get_palette(handle);
IndexColorModel color = new IndexColorModel(8, pal.length/4, pal, 0, true);
BufferedImage img = new BufferedImage(
orig_image.getWidth(), orig_image.getHeight(),
BufferedImage.TYPE_BYTE_INDEXED, color);
byte[] data = get8bitDataFromImage(img);
if (data == null) return null;
if (!liq_write_remapped_image(handle, orig_image.handle, data)) return null;
return img;
}
/**
* Dithering strength. Floyd-Steinberg is always used and in
* speed settings 1-5 high-quality adaptive dithering is used.
* @see PngQuant.setSpeed()
* @link http://pngquant.org/lib/#liq_set_dithering_level
*
* @param dither_level Dithering in range 0 (none) and 1 (full)
*/
public native boolean setDitheringLevel(float dither_level);
/**
* The default is 0.45455 (1/2.2) which is PNG's approximation of sRGB.
*/
public native boolean setGamma(double gamma);
public native double getGamma();
/**
* Mean Square Error of remapping of image used to create this result.
* @link http://pngquant.org/lib/#liq_get_quantization_error
*
* @return MSE or -1 if not available
*/
public native double getMeanSquareError();
/**
* @link http://pngquant.org/lib/#liq_get_quantization_quality
* @return Actually achieved quality in 0-100 range on scale compatible with PngQuant.setQuality()
*/
public native int getQuality();
public void close() {
if (handle != 0) {
liq_result_destroy(handle);
handle = 0;
}
}
private static byte[] get8bitDataFromImage(BufferedImage image) {
if (image.getType() == BufferedImage.TYPE_BYTE_INDEXED) {
DataBuffer buffer = image.getRaster().getDataBuffer();
if (buffer instanceof DataBufferByte) {
return ((DataBufferByte)buffer).getData();
}
}
return null;
}
private static native byte[] liq_get_palette(long handle);
private static native long liq_quantize_image(long attr, long image);
private static native boolean liq_write_remapped_image(long handle, long image, byte[] buffer);
private static native void liq_result_destroy(long handle);
}
libimagequant-4.4.0/imagequant-sys/pom.xml 0000664 0000000 0000000 00000004775 15032747170 0020650 0 ustar 00root root 0000000 0000000 4.0.0org.pngquantlibimagequantjar4.0.3pngquanthttps://pngquant.org.org.codehaus.mojoexec-maven-plugin1.1buildcompileexecmake-j8USE_SSE=1javacleancleanexecmakecleanmac-x64Macx64org.pngquantlibimagequant-jni1jnilibmac-x64linux-x64unixLinuxx64org.pngquantlibimagequant-jniso1linux-x64
libimagequant-4.4.0/imagequant-sys/src/ 0000775 0000000 0000000 00000000000 15032747170 0020105 5 ustar 00root root 0000000 0000000 libimagequant-4.4.0/imagequant-sys/src/ffi.rs 0000664 0000000 0000000 00000071003 15032747170 0021220 0 ustar 00root root 0000000 0000000 //! Exports API for C programs and C-FFI-compatible languages. See `libimagequant.h` or for C docs.
//!
//! This crate is not supposed to be used in Rust directly. For Rust, see the parent [imagequant](https://lib.rs/imagequant) crate.
#![allow(non_camel_case_types)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::wildcard_imports)]
#![allow(clippy::items_after_statements)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_possible_wrap)]
use imagequant::capi::*;
use imagequant::Error::LIQ_OK;
use imagequant::*;
use std::ffi::CString;
use std::mem::{ManuallyDrop, MaybeUninit};
use std::os::raw::{c_char, c_int, c_uint, c_void};
use std::ptr;
pub use imagequant::Error as liq_error;
#[repr(C)]
pub struct liq_attr {
magic_header: MagicTag,
inner: Attributes,
c_api_free: unsafe extern "C" fn(*mut c_void),
}
#[repr(C)]
pub struct liq_image<'pixels> {
magic_header: MagicTag,
inner: ManuallyDrop>,
c_api_free: unsafe extern "C" fn(*mut c_void),
}
#[repr(C)]
pub struct liq_result {
magic_header: MagicTag,
inner: QuantizationResult,
}
#[repr(C)]
pub struct liq_histogram {
magic_header: MagicTag,
inner: Histogram,
}
pub type liq_palette = Palette;
pub type liq_histogram_entry = HistogramEntry;
pub type liq_color = RGBA;
pub type liq_log_callback_function = unsafe extern "C" fn(liq: &liq_attr, message: *const c_char, user_info: AnySyncSendPtr);
pub type liq_log_flush_callback_function = unsafe extern "C" fn(liq: &liq_attr, user_info: AnySyncSendPtr);
pub type liq_progress_callback_function = unsafe extern "C" fn(progress_percent: f32, user_info: AnySyncSendPtr) -> c_int;
pub type liq_image_get_rgba_row_callback = unsafe extern "C" fn(row_out: *mut MaybeUninit, row: c_int, width: c_int, user_info: AnySyncSendPtr);
bitflags::bitflags! {
#[repr(C)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct liq_ownership: c_int {
/// Moves ownership of the rows array. It will free it using `free()` or custom allocator.
const LIQ_OWN_ROWS = 4;
/// Moves ownership of the pixel data. It will free it using `free()` or custom allocator.
const LIQ_OWN_PIXELS = 8;
/// Makes a copy of the pixels, so the `liq_image` is not tied to pixel's lifetime.
const LIQ_COPY_PIXELS = 16;
}
}
#[repr(transparent)]
#[derive(PartialEq, Debug, Copy, Clone)]
pub(crate) struct MagicTag(*const u8);
// Safety: Rust overreacts about C pointers. Data behind this ptr isn't used.
unsafe impl Sync for MagicTag {}
unsafe impl Send for MagicTag {}
pub(crate) static LIQ_ATTR_MAGIC: MagicTag = MagicTag(b"liq_attr_magic\0".as_ptr());
pub(crate) static LIQ_IMAGE_MAGIC: MagicTag = MagicTag(b"liq_image_magic\0".as_ptr());
pub(crate) static LIQ_RESULT_MAGIC: MagicTag = MagicTag(b"liq_result_magic\0".as_ptr());
pub(crate) static LIQ_HISTOGRAM_MAGIC: MagicTag = MagicTag(b"liq_histogram_magic\0".as_ptr());
pub(crate) static LIQ_FREED_MAGIC: MagicTag = MagicTag(b"liq_freed_magic\0".as_ptr());
#[no_mangle]
#[inline(never)]
unsafe extern "C" fn liq_received_invalid_pointer(ptr: *const u8) -> bool {
if ptr.is_null() {
return true;
}
let _ = ptr::read_volatile(ptr);
false
}
macro_rules! bad_object {
($obj:expr, $tag:expr) => {{
let obj = &*$obj;
#[allow(unused_unsafe)]
#[allow(clippy::ptr_as_ptr)]
let bork = if cfg!(miri) { false } else { unsafe { liq_received_invalid_pointer((obj as *const _ as *const u8)) } };
(bork || (($obj).magic_header != $tag))
}};
}
impl Drop for liq_attr {
fn drop(&mut self) {
if bad_object!(self, LIQ_ATTR_MAGIC) { return; }
self.magic_header = LIQ_FREED_MAGIC;
}
}
impl Drop for liq_image<'_> {
fn drop(&mut self) {
if bad_object!(self, LIQ_IMAGE_MAGIC) { return; }
unsafe { ManuallyDrop::drop(&mut self.inner); }
self.magic_header = LIQ_FREED_MAGIC;
}
}
impl Drop for liq_result {
fn drop(&mut self) {
if bad_object!(self, LIQ_RESULT_MAGIC) { return; }
self.magic_header = LIQ_FREED_MAGIC;
}
}
impl Drop for liq_histogram {
fn drop(&mut self) {
if bad_object!(self, LIQ_HISTOGRAM_MAGIC) { return; }
self.magic_header = LIQ_FREED_MAGIC;
}
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_version() -> c_uint {
LIQ_VERSION
}
#[no_mangle]
#[inline(never)]
#[deprecated]
pub extern "C" fn liq_set_min_opacity(_: &mut liq_attr, _: c_int) -> liq_error {
LIQ_OK
}
#[no_mangle]
#[inline(never)]
#[deprecated]
pub extern "C" fn liq_get_min_opacity(_: &liq_attr) -> c_int {
0
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_set_last_index_transparent(attr: &mut liq_attr, is_last: c_int) {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return; }
attr.inner.set_last_index_transparent(is_last != 0);
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_palette(result: &mut liq_result) -> Option<&liq_palette> {
if bad_object!(result, LIQ_RESULT_MAGIC) { return None; }
Some(liq_get_palette_impl(&mut result.inner))
}
/// A `void*` pointer to any data, as long as it's thread-safe
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct AnySyncSendPtr(pub *mut c_void);
impl Default for AnySyncSendPtr {
fn default() -> Self {
Self(ptr::null_mut())
}
}
/// C callback user is responsible for ensuring safety
unsafe impl Send for AnySyncSendPtr {}
unsafe impl Sync for AnySyncSendPtr {}
#[no_mangle]
#[inline(never)]
pub unsafe extern "C" fn liq_attr_set_progress_callback(attr: &mut liq_attr, callback: liq_progress_callback_function, user_info: AnySyncSendPtr) {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return; }
let cb = move |f| if callback(f, user_info) == 0 { ControlFlow::Break} else { ControlFlow::Continue};
attr.inner.set_progress_callback(cb);
}
#[no_mangle]
#[inline(never)]
pub unsafe extern "C" fn liq_result_set_progress_callback(result: &mut liq_result, callback: liq_progress_callback_function, user_info: AnySyncSendPtr) {
if bad_object!(result, LIQ_RESULT_MAGIC) { return; }
result.inner.set_progress_callback(move |f| if callback(f, user_info) == 0 { ControlFlow::Break} else { ControlFlow::Continue});
}
#[allow(clippy::cast_ptr_alignment)]
unsafe fn attr_to_liq_attr_ptr(ptr: &Attributes) -> &liq_attr {
let liq_attr = std::ptr::NonNull::::dangling();
let outer_addr = std::ptr::addr_of!(*liq_attr.as_ptr()) as isize;
let inner_addr = std::ptr::addr_of!((*liq_attr.as_ptr()).inner) as isize;
&*(ptr as *const Attributes).cast::().offset(outer_addr - inner_addr).cast::()
}
#[no_mangle]
#[inline(never)]
pub unsafe extern "C" fn liq_set_log_callback(attr: &mut liq_attr, callback: liq_log_callback_function, user_info: AnySyncSendPtr) {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return; }
attr.inner.set_log_callback(move |attr, msg| {
if let Ok(tmp) = CString::new(msg) {
callback(attr_to_liq_attr_ptr(attr), tmp.as_ptr(), user_info);
}
});
}
#[no_mangle]
#[inline(never)]
pub unsafe extern "C" fn liq_set_log_flush_callback(attr: &mut liq_attr, callback: liq_log_flush_callback_function, user_info: AnySyncSendPtr) {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return; }
attr.inner.set_log_flush_callback(move |attr| callback(attr_to_liq_attr_ptr(attr), user_info));
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_set_max_colors(attr: &mut liq_attr, colors: c_uint) -> liq_error {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return Error::InvalidPointer; }
attr.inner.set_max_colors(colors).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_max_colors(attr: &liq_attr) -> c_uint {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return !0; }
attr.inner.max_colors()
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_set_min_posterization(attr: &mut liq_attr, bits: c_int) -> liq_error {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return Error::InvalidPointer; }
attr.inner.set_min_posterization(bits as u8).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_min_posterization(attr: &liq_attr) -> c_uint {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return !0; }
attr.inner.min_posterization().into()
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_set_speed(attr: &mut liq_attr, speed: c_int) -> liq_error {
attr.inner.set_speed(speed).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_speed(attr: &liq_attr) -> c_uint {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return !0; }
attr.inner.speed()
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_set_quality(attr: &mut liq_attr, minimum: c_uint, target: c_uint) -> liq_error {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return Error::InvalidPointer; }
attr.inner.set_quality(minimum as u8, target as u8).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_min_quality(attr: &liq_attr) -> c_uint {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return !0; }
attr.inner.quality().0.into()
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_max_quality(attr: &liq_attr) -> c_uint {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return !0; }
attr.inner.quality().1.into()
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_quantize_image(attr: &mut liq_attr, img: &mut liq_image) -> Option> {
if bad_object!(attr, LIQ_ATTR_MAGIC) ||
bad_object!(img, LIQ_IMAGE_MAGIC) { return None; }
let img = &mut img.inner;
let attr = &mut attr.inner;
attr.quantize(img).ok().map(|inner| Box::new(liq_result {
magic_header: LIQ_RESULT_MAGIC,
inner,
}))
}
#[no_mangle]
#[inline(never)]
pub unsafe extern "C" fn liq_write_remapped_image(result: &mut liq_result, input_image: &mut liq_image, buffer_bytes: *mut MaybeUninit, buffer_size: usize) -> liq_error {
if bad_object!(result, LIQ_RESULT_MAGIC) ||
bad_object!(input_image, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
let input_image = &mut input_image.inner;
let result = &mut result.inner;
if liq_received_invalid_pointer(buffer_bytes.cast()) { return Error::InvalidPointer; }
let required_size = (input_image.width()) * (input_image.height());
if buffer_size < required_size { return Error::BufferTooSmall; }
let buffer_bytes = std::slice::from_raw_parts_mut(buffer_bytes, required_size);
liq_write_remapped_image_impl(result, input_image, buffer_bytes).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub unsafe extern "C" fn liq_write_remapped_image_rows(result: &mut liq_result, input_image: &mut liq_image, row_pointers: *mut *mut MaybeUninit) -> liq_error {
if bad_object!(result, LIQ_RESULT_MAGIC) ||
bad_object!(input_image, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
let input_image = &mut input_image.inner;
let result = &mut result.inner;
if liq_received_invalid_pointer(row_pointers.cast()) { return Error::InvalidPointer; }
let rows = std::slice::from_raw_parts_mut(row_pointers, input_image.height());
liq_write_remapped_image_rows_impl(result, input_image, rows).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_image_add_fixed_color(img: &mut liq_image, color: liq_color) -> liq_error {
if bad_object!(img, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
img.inner.add_fixed_color(color).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_histogram_add_fixed_color(hist: &mut liq_histogram, color: liq_color, gamma: f64) -> liq_error {
if bad_object!(hist, LIQ_HISTOGRAM_MAGIC) { return Error::InvalidPointer; }
let hist = &mut hist.inner;
hist.add_fixed_color(color, gamma).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_image_get_width(img: &liq_image) -> c_uint {
if bad_object!(img, LIQ_IMAGE_MAGIC) { return !0; }
img.inner.width() as _
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_image_get_height(img: &liq_image) -> c_uint {
if bad_object!(img, LIQ_IMAGE_MAGIC) { return !0; }
img.inner.height() as _
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_image_destroy(_: Option>) {}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_image_set_background<'pixels>(img: &mut liq_image<'pixels>, background: Box>) -> liq_error {
if bad_object!(img, LIQ_IMAGE_MAGIC) ||
bad_object!(background, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
let background = unsafe { ManuallyDrop::take(&mut ManuallyDrop::new(background).inner) };
img.inner.set_background(background).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub unsafe extern "C" fn liq_image_set_importance_map(img: &mut liq_image, importance_map: *mut u8, buffer_size: usize, ownership: liq_ownership) -> liq_error {
if bad_object!(img, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
let free_fn = img.c_api_free;
let img = &mut img.inner;
if buffer_size == 0 || liq_received_invalid_pointer(importance_map) { return Error::InvalidPointer; }
let required_size = img.width() * img.height();
if buffer_size < required_size {
return Error::BufferTooSmall;
}
let importance_map_slice = std::slice::from_raw_parts(importance_map, required_size);
if ownership == liq_ownership::LIQ_COPY_PIXELS {
img.set_importance_map(importance_map_slice).err().unwrap_or(LIQ_OK)
} else if ownership == liq_ownership::LIQ_OWN_PIXELS {
let copy: Box<[u8]> = importance_map_slice.into();
free_fn(importance_map.cast());
img.set_importance_map(copy).err().unwrap_or(LIQ_OK);
LIQ_OK
} else {
Error::Unsupported
}
}
#[no_mangle]
#[inline(never)]
pub unsafe extern "C" fn liq_image_set_memory_ownership(img: &mut liq_image, ownership_flags: liq_ownership) -> liq_error {
if bad_object!(img, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
let both = liq_ownership::LIQ_OWN_ROWS | liq_ownership::LIQ_OWN_PIXELS;
if ownership_flags.is_empty() || (ownership_flags | both) != both {
return Error::ValueOutOfRange;
}
let own_rows = ownership_flags.contains(liq_ownership::LIQ_OWN_ROWS);
let own_pixels = ownership_flags.contains(liq_ownership::LIQ_OWN_PIXELS);
liq_image_set_memory_ownership_impl(&mut img.inner, own_rows, own_pixels, img.c_api_free).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_histogram_create(attr: &liq_attr) -> Option> {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return None; }
Some(Box::new(liq_histogram {
magic_header: LIQ_HISTOGRAM_MAGIC,
inner: Histogram::new(&attr.inner),
}))
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_histogram_destroy(_hist: Option>) {}
#[no_mangle]
#[inline(never)]
#[deprecated(note = "custom allocators are no longer supported")]
pub extern "C" fn liq_attr_create_with_allocator(_unused: *mut c_void, free: unsafe extern "C" fn(*mut c_void)) -> Option> {
let attr = Box::new(liq_attr {
magic_header: LIQ_ATTR_MAGIC,
inner: Attributes::new(),
c_api_free: free,
});
debug_assert_eq!(std::ptr::addr_of!(*attr), unsafe { attr_to_liq_attr_ptr(&attr.inner) } as *const liq_attr);
Some(attr)
}
#[no_mangle]
#[inline(never)]
#[allow(deprecated)]
pub extern "C" fn liq_attr_create() -> Option> {
liq_attr_create_with_allocator(ptr::null_mut(), libc::free)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_attr_copy(attr: &liq_attr) -> Option> {
if bad_object!(attr, LIQ_ATTR_MAGIC) { return None; }
Some(Box::new(liq_attr {
magic_header: LIQ_ATTR_MAGIC,
inner: attr.inner.clone(),
c_api_free: attr.c_api_free,
}))
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_attr_destroy(_attr: Option>) {}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_result_destroy(_res: Option>) {}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_set_output_gamma(result: &mut liq_result, gamma: f64) -> liq_error {
if bad_object!(result, LIQ_RESULT_MAGIC) { return Error::InvalidPointer; }
result.inner.set_output_gamma(gamma).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_set_dithering_level(result: &mut liq_result, dither_level: f32) -> liq_error {
if bad_object!(result, LIQ_RESULT_MAGIC) { return Error::InvalidPointer; }
result.inner.set_dithering_level(dither_level).err().unwrap_or(LIQ_OK)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_output_gamma(result: &liq_result) -> f64 {
if bad_object!(result, LIQ_RESULT_MAGIC) { return -1.; }
result.inner.output_gamma()
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_quantization_error(result: &liq_result) -> f64 {
if bad_object!(result, LIQ_RESULT_MAGIC) { return -1.; }
result.inner.quantization_error().unwrap_or(-1.)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_remapping_error(result: &liq_result) -> f64 {
if bad_object!(result, LIQ_RESULT_MAGIC) { return -1.; }
result.inner.remapping_error().unwrap_or(-1.)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_quantization_quality(result: &liq_result) -> c_int {
if bad_object!(result, LIQ_RESULT_MAGIC) { return -1; }
result.inner.quantization_quality().map_or(-1, c_int::from)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_get_remapping_quality(result: &liq_result) -> c_int {
if bad_object!(result, LIQ_RESULT_MAGIC) { return -1; }
result.inner.remapping_quality().map_or(-1, c_int::from)
}
#[no_mangle]
#[inline(never)]
pub extern "C" fn liq_image_quantize(img: &mut liq_image, attr: &mut liq_attr, write_only_output: &mut MaybeUninit