diff --git a/viter.c b/kmeans.c
similarity index 68%
rename from viter.c
rename to kmeans.c
index f526326..f596009 100644
--- a/viter.c
+++ b/kmeans.c
@@ -19,7 +19,7 @@ along with libimagequant. If not, see .
#include "libimagequant.h"
#include "pam.h"
-#include "viter.h"
+#include "kmeans.h"
#include "nearest.h"
#include
#include
@@ -32,16 +32,16 @@ along with libimagequant. If not, see .
#endif
/*
- * Voronoi iteration: new palette color is computed from weighted average of colors that map to that palette entry.
+ * K-Means iteration: new palette color is computed from weighted average of colors that map to that palette entry.
*/
-LIQ_PRIVATE void viter_init(const colormap *map, const unsigned int max_threads, viter_state average_color[])
+LIQ_PRIVATE void kmeans_init(const colormap *map, const unsigned int max_threads, kmeans_state average_color[])
{
- memset(average_color, 0, sizeof(average_color[0])*(VITER_CACHE_LINE_GAP+map->colors)*max_threads);
+ memset(average_color, 0, sizeof(average_color[0])*(KMEANS_CACHE_LINE_GAP+map->colors)*max_threads);
}
-LIQ_PRIVATE void viter_update_color(const f_pixel acolor, const float value, const colormap *map, unsigned int match, const unsigned int thread, viter_state average_color[])
+LIQ_PRIVATE void kmeans_update_color(const f_pixel acolor, const float value, const colormap *map, unsigned int match, const unsigned int thread, kmeans_state average_color[])
{
- match += thread * (VITER_CACHE_LINE_GAP+map->colors);
+ match += thread * (KMEANS_CACHE_LINE_GAP+map->colors);
average_color[match].a += acolor.a * value;
average_color[match].r += acolor.r * value;
average_color[match].g += acolor.g * value;
@@ -49,14 +49,14 @@ LIQ_PRIVATE void viter_update_color(const f_pixel acolor, const float value, con
average_color[match].total += value;
}
-LIQ_PRIVATE void viter_finalize(colormap *map, const unsigned int max_threads, const viter_state average_color[])
+LIQ_PRIVATE void kmeans_finalize(colormap *map, const unsigned int max_threads, const kmeans_state average_color[])
{
for (unsigned int i=0; i < map->colors; i++) {
double a=0, r=0, g=0, b=0, total=0;
// Aggregate results from all threads
for(unsigned int t=0; t < max_threads; t++) {
- const unsigned int offset = (VITER_CACHE_LINE_GAP+map->colors) * t + i;
+ const unsigned int offset = (KMEANS_CACHE_LINE_GAP+map->colors) * t + i;
a += average_color[offset].a;
r += average_color[offset].r;
@@ -77,12 +77,12 @@ LIQ_PRIVATE void viter_finalize(colormap *map, const unsigned int max_threads, c
}
}
-LIQ_PRIVATE double viter_do_iteration(histogram *hist, colormap *const map, viter_callback callback, const bool fast_palette)
+LIQ_PRIVATE double kmeans_do_iteration(histogram *hist, colormap *const map, kmeans_callback callback)
{
const unsigned int max_threads = omp_get_max_threads();
- viter_state average_color[(VITER_CACHE_LINE_GAP+map->colors) * max_threads];
- viter_init(map, max_threads, average_color);
- struct nearest_map *const n = nearest_init(map, fast_palette);
+ kmeans_state average_color[(KMEANS_CACHE_LINE_GAP+map->colors) * max_threads];
+ kmeans_init(map, max_threads, average_color);
+ struct nearest_map *const n = nearest_init(map);
hist_item *const achv = hist->achv;
const int hist_size = hist->size;
@@ -95,13 +95,13 @@ LIQ_PRIVATE double viter_do_iteration(histogram *hist, colormap *const map, vite
achv[j].tmp.likely_colormap_index = match;
total_diff += diff * achv[j].perceptual_weight;
- viter_update_color(achv[j].acolor, achv[j].perceptual_weight, map, match, omp_get_thread_num(), average_color);
+ kmeans_update_color(achv[j].acolor, achv[j].perceptual_weight, map, match, omp_get_thread_num(), average_color);
if (callback) callback(&achv[j], diff);
}
nearest_free(n);
- viter_finalize(map, max_threads, average_color);
+ kmeans_finalize(map, max_threads, average_color);
return total_diff / hist->total_perceptual_weight;
}
diff --git a/kmeans.h b/kmeans.h
new file mode 100644
index 0000000..c51d7bb
--- /dev/null
+++ b/kmeans.h
@@ -0,0 +1,19 @@
+
+#ifndef KMEANS_H
+#define KMEANS_H
+
+// Spread memory touched by different threads at least 64B apart which I assume is the cache line size. This should avoid memory write contention.
+#define KMEANS_CACHE_LINE_GAP ((64+sizeof(kmeans_state)-1)/sizeof(kmeans_state))
+
+typedef struct {
+ double a, r, g, b, total;
+} kmeans_state;
+
+typedef void (*kmeans_callback)(hist_item *item, float diff);
+
+LIQ_PRIVATE void kmeans_init(const colormap *map, const unsigned int max_threads, kmeans_state state[]);
+LIQ_PRIVATE void kmeans_update_color(const f_pixel acolor, const float value, const colormap *map, unsigned int match, const unsigned int thread, kmeans_state average_color[]);
+LIQ_PRIVATE void kmeans_finalize(colormap *map, const unsigned int max_threads, const kmeans_state state[]);
+LIQ_PRIVATE double kmeans_do_iteration(histogram *hist, colormap *const map, kmeans_callback callback);
+
+#endif
diff --git a/libimagequant.c b/libimagequant.c
index c17b93d..08f6962 100644
--- a/libimagequant.c
+++ b/libimagequant.c
@@ -54,7 +54,7 @@
#include "mediancut.h"
#include "nearest.h"
#include "blur.h"
-#include "viter.h"
+#include "kmeans.h"
#define LIQ_HIGH_MEMORY_LIMIT (1<<26) /* avoid allocating buffers larger than 64MB */
@@ -73,12 +73,12 @@ struct liq_attr {
void* (*malloc)(size_t);
void (*free)(void*);
- double target_mse, max_mse, voronoi_iteration_limit;
+ double target_mse, max_mse, kmeans_iteration_limit;
float min_opaque_val;
unsigned int max_colors, max_histogram_entries;
unsigned int min_posterization_output /* user setting */, min_posterization_input /* speed setting */;
- unsigned int voronoi_iterations, feedback_loop_trials;
- bool last_index_transparent, use_contrast_maps, use_dither_map, fast_palette;
+ unsigned int kmeans_iterations, feedback_loop_trials;
+ bool last_index_transparent, use_contrast_maps, use_dither_map;
unsigned char speed;
unsigned char progress_stage1, progress_stage2, progress_stage3;
@@ -141,7 +141,7 @@ struct liq_result {
float dither_level;
double gamma, palette_error;
int min_posterization_output;
- bool use_dither_map, fast_palette;
+ bool use_dither_map;
};
struct liq_histogram {
@@ -349,13 +349,12 @@ LIQ_EXPORT LIQ_NONNULL liq_error liq_set_speed(liq_attr* attr, int speed)
if (speed < 1 || speed > 10) return LIQ_VALUE_OUT_OF_RANGE;
unsigned int iterations = MAX(8-speed, 0); iterations += iterations * iterations/2;
- attr->voronoi_iterations = iterations;
- attr->voronoi_iteration_limit = 1.0/(double)(1<<(23-speed));
+ attr->kmeans_iterations = iterations;
+ attr->kmeans_iteration_limit = 1.0/(double)(1<<(23-speed));
attr->feedback_loop_trials = MAX(56-9*speed, 0);
attr->max_histogram_entries = (1<<17) + (1<<18)*(10-speed);
attr->min_posterization_input = (speed >= 8) ? 1 : 0;
- attr->fast_palette = (speed >= 7);
attr->use_dither_map = (speed <= (omp_get_max_threads() > 1 ? 7 : 5)); // parallelized dither map might speed up floyd remapping
attr->use_contrast_maps = (speed <= 7) || attr->use_dither_map;
attr->speed = speed;
@@ -461,7 +460,7 @@ LIQ_EXPORT LIQ_NONNULL void liq_attr_destroy(liq_attr *attr)
attr->free(attr);
}
-LIQ_EXPORT LIQ_NONNULL liq_attr* liq_attr_copy(liq_attr *orig)
+LIQ_EXPORT LIQ_NONNULL liq_attr* liq_attr_copy(const liq_attr *orig)
{
if (!CHECK_STRUCT_TYPE(orig, liq_attr)) {
return NULL;
@@ -528,11 +527,11 @@ LIQ_EXPORT liq_attr* liq_attr_create_with_allocator(void* (*custom_malloc)(size_
LIQ_EXPORT LIQ_NONNULL liq_error liq_image_add_fixed_color(liq_image *img, liq_color color)
{
if (!CHECK_STRUCT_TYPE(img, liq_image)) return LIQ_INVALID_POINTER;
- if (img->fixed_colors_count > 255) return LIQ_BUFFER_TOO_SMALL;
+ if (img->fixed_colors_count > 255) return LIQ_UNSUPPORTED;
float gamma_lut[256];
to_f_set_gamma(gamma_lut, img->gamma);
- img->fixed_colors[img->fixed_colors_count++] = to_f(gamma_lut, (rgba_pixel){
+ img->fixed_colors[img->fixed_colors_count++] = rgba_to_f(gamma_lut, (rgba_pixel){
.r = color.r,
.g = color.g,
.b = color.b,
@@ -543,7 +542,7 @@ LIQ_EXPORT LIQ_NONNULL liq_error liq_image_add_fixed_color(liq_image *img, liq_c
LIQ_NONNULL static liq_error liq_histogram_add_fixed_color_internal(liq_histogram *hist, f_pixel color)
{
- if (hist->fixed_colors_count > 255) return LIQ_BUFFER_TOO_SMALL;
+ if (hist->fixed_colors_count > 255) return LIQ_UNSUPPORTED;
hist->fixed_colors[hist->fixed_colors_count++] = color;
return LIQ_OK;
@@ -750,7 +749,7 @@ LIQ_NONNULL static void convert_row_to_f(liq_image *img, f_pixel *row_f_pixels,
const rgba_pixel *const row_pixels = liq_image_get_row_rgba(img, row);
for(unsigned int col=0; col < img->width; col++) {
- row_f_pixels[col] = to_f(gamma_lut, row_pixels[col]);
+ row_f_pixels[col] = rgba_to_f(gamma_lut, row_pixels[col]);
}
}
@@ -853,7 +852,7 @@ LIQ_EXPORT LIQ_NONNULL void liq_image_destroy(liq_image *input_image)
input_image->free(input_image);
}
-LIQ_EXPORT liq_histogram* liq_histogram_create(liq_attr* attr)
+LIQ_EXPORT liq_histogram* liq_histogram_create(const liq_attr* attr)
{
if (!CHECK_STRUCT_TYPE(attr, liq_attr)) {
return NULL;
@@ -893,7 +892,7 @@ LIQ_EXPORT LIQ_NONNULL liq_error liq_image_quantize(liq_image *const img, liq_at
{
if (!CHECK_STRUCT_TYPE(attr, liq_attr)) return LIQ_INVALID_POINTER;
if (!liq_image_has_rgba_pixels(img)) {
- return LIQ_INVALID_POINTER;
+ return LIQ_UNSUPPORTED;
}
liq_histogram *hist = liq_histogram_create(attr);
@@ -1011,7 +1010,7 @@ LIQ_EXPORT LIQ_NONNULL void liq_result_destroy(liq_result *res)
}
-LIQ_EXPORT LIQ_NONNULL double liq_get_quantization_error(liq_result *result) {
+LIQ_EXPORT LIQ_NONNULL double liq_get_quantization_error(const liq_result *result) {
if (!CHECK_STRUCT_TYPE(result, liq_result)) return -1;
if (result->palette_error >= 0) {
@@ -1021,7 +1020,7 @@ LIQ_EXPORT LIQ_NONNULL double liq_get_quantization_error(liq_result *result) {
return -1;
}
-LIQ_EXPORT LIQ_NONNULL double liq_get_remapping_error(liq_result *result) {
+LIQ_EXPORT LIQ_NONNULL double liq_get_remapping_error(const liq_result *result) {
if (!CHECK_STRUCT_TYPE(result, liq_result)) return -1;
if (result->remapping && result->remapping->palette_error >= 0) {
@@ -1031,7 +1030,7 @@ LIQ_EXPORT LIQ_NONNULL double liq_get_remapping_error(liq_result *result) {
return -1;
}
-LIQ_EXPORT LIQ_NONNULL int liq_get_quantization_quality(liq_result *result) {
+LIQ_EXPORT LIQ_NONNULL int liq_get_quantization_quality(const liq_result *result) {
if (!CHECK_STRUCT_TYPE(result, liq_result)) return -1;
if (result->palette_error >= 0) {
@@ -1041,7 +1040,7 @@ LIQ_EXPORT LIQ_NONNULL int liq_get_quantization_quality(liq_result *result) {
return -1;
}
-LIQ_EXPORT LIQ_NONNULL int liq_get_remapping_quality(liq_result *result) {
+LIQ_EXPORT LIQ_NONNULL int liq_get_remapping_quality(const liq_result *result) {
if (!CHECK_STRUCT_TYPE(result, liq_result)) return -1;
if (result->remapping && result->remapping->palette_error >= 0) {
@@ -1138,14 +1137,14 @@ LIQ_NONNULL static void set_rounded_palette(liq_palette *const dest, colormap *c
dest->count = map->colors;
for(unsigned int x = 0; x < map->colors; ++x) {
- rgba_pixel px = to_rgb(gamma, map->palette[x].acolor);
+ rgba_pixel px = f_to_rgb(gamma, map->palette[x].acolor);
px.r = posterize_channel(px.r, posterize);
px.g = posterize_channel(px.g, posterize);
px.b = posterize_channel(px.b, posterize);
px.a = posterize_channel(px.a, posterize);
- map->palette[x].acolor = to_f(gamma_lut, px); /* saves rounding error introduced by to_rgb, which makes remapping & dithering more accurate */
+ map->palette[x].acolor = rgba_to_f(gamma_lut, px); /* saves rounding error introduced by to_rgb, which makes remapping & dithering more accurate */
if (!px.a && !map->palette[x].fixed) {
px.r = 71; px.g = 112; px.b = 76;
@@ -1169,7 +1168,7 @@ LIQ_EXPORT LIQ_NONNULL const liq_palette *liq_get_palette(liq_result *result)
return &result->int_palette;
}
-LIQ_NONNULL static float remap_to_palette(liq_image *const input_image, unsigned char *const *const output_pixels, colormap *const map, const bool fast)
+LIQ_NONNULL static float remap_to_palette(liq_image *const input_image, unsigned char *const *const output_pixels, colormap *const map)
{
const int rows = input_image->height;
const unsigned int cols = input_image->width;
@@ -1179,11 +1178,11 @@ LIQ_NONNULL static float remap_to_palette(liq_image *const input_image, unsigned
return -1;
}
- struct nearest_map *const n = nearest_init(map, fast);
+ struct nearest_map *const n = nearest_init(map);
const unsigned int max_threads = omp_get_max_threads();
- viter_state average_color[(VITER_CACHE_LINE_GAP+map->colors) * max_threads];
- viter_init(map, max_threads, average_color);
+ kmeans_state average_color[(KMEANS_CACHE_LINE_GAP+map->colors) * max_threads];
+ kmeans_init(map, max_threads, average_color);
#pragma omp parallel for if (rows*cols > 3000) \
schedule(static) default(none) shared(average_color) reduction(+:remapping_error)
@@ -1195,11 +1194,11 @@ LIQ_NONNULL static float remap_to_palette(liq_image *const input_image, unsigned
output_pixels[row][col] = last_match = nearest_search(n, &row_pixels[col], last_match, &diff);
remapping_error += diff;
- viter_update_color(row_pixels[col], 1.0, map, last_match, omp_get_thread_num(), average_color);
+ kmeans_update_color(row_pixels[col], 1.0, map, last_match, omp_get_thread_num(), average_color);
}
}
- viter_finalize(map, max_threads, average_color);
+ kmeans_finalize(map, max_threads, average_color);
nearest_free(n);
@@ -1219,12 +1218,12 @@ inline static f_pixel get_dithered_pixel(const float dither_level, const float m
const float max_underflow = -0.1f;
// allowing some overflow prevents undithered bands caused by clamping of all channels
- if (px.r + sr > max_overflow) ratio = MIN(ratio, (max_overflow -px.r)/sr);
- else if (px.r + sr < max_underflow) ratio = MIN(ratio, (max_underflow-px.r)/sr);
- if (px.g + sg > max_overflow) ratio = MIN(ratio, (max_overflow -px.g)/sg);
- else if (px.g + sg < max_underflow) ratio = MIN(ratio, (max_underflow-px.g)/sg);
- if (px.b + sb > max_overflow) ratio = MIN(ratio, (max_overflow -px.b)/sb);
- else if (px.b + sb < max_underflow) ratio = MIN(ratio, (max_underflow-px.b)/sb);
+ if (px.r + sr > max_overflow) ratio = MIN(ratio, (max_overflow -px.r)/sr);
+ else { if (px.r + sr < max_underflow) ratio = MIN(ratio, (max_underflow-px.r)/sr); }
+ if (px.g + sg > max_overflow) ratio = MIN(ratio, (max_overflow -px.g)/sg);
+ else { if (px.g + sg < max_underflow) ratio = MIN(ratio, (max_underflow-px.g)/sg); }
+ if (px.b + sb > max_overflow) ratio = MIN(ratio, (max_overflow -px.b)/sb);
+ else { if (px.b + sb < max_underflow) ratio = MIN(ratio, (max_underflow-px.b)/sb); }
float a = px.a + sa;
if (a > 1.0) { a = 1.0; }
@@ -1255,7 +1254,7 @@ inline static f_pixel get_dithered_pixel(const float dither_level, const float m
*/
LIQ_NONNULL static bool remap_to_palette_floyd(liq_image *input_image, unsigned char *const output_pixels[], liq_remapping_result *quant, const float max_dither_error, const bool output_image_is_remapped)
{
- const unsigned int rows = input_image->height, cols = input_image->width;
+ const int rows = input_image->height, cols = input_image->width;
const unsigned char *dither_map = quant->use_dither_map ? (input_image->dither_map ? input_image->dither_map : input_image->edges) : NULL;
const colormap *map = quant->palette;
@@ -1270,7 +1269,7 @@ LIQ_NONNULL static bool remap_to_palette_floyd(liq_image *input_image, unsigned
nexterr = thiserr + (cols + 2);
bool ok = true;
- struct nearest_map *const n = nearest_init(map, false);
+ struct nearest_map *const n = nearest_init(map);
// response to this value is non-linear and without it any value < 0.8 would give almost no dithering
float base_dithering_level = quant->dither_level;
@@ -1281,9 +1280,9 @@ LIQ_NONNULL static bool remap_to_palette_floyd(liq_image *input_image, unsigned
}
base_dithering_level *= 15.0/16.0; // prevent small errors from accumulating
- bool fs_direction = true;
+ int fs_direction = 1;
unsigned int last_match=0;
- for (unsigned int row = 0; row < rows; ++row) {
+ for (int row = 0; row < rows; ++row) {
if (liq_remap_progress(quant, quant->progress_stage1 + row * (100.f - quant->progress_stage1) / rows)) {
ok = false;
break;
@@ -1291,7 +1290,7 @@ LIQ_NONNULL static bool remap_to_palette_floyd(liq_image *input_image, unsigned
memset(nexterr, 0, (cols + 2) * sizeof(*nexterr));
- unsigned int col = (fs_direction) ? 0 : (cols - 1);
+ int col = (fs_direction > 0) ? 0 : (cols - 1);
const f_pixel *const row_pixels = liq_image_get_row_f(input_image, row);
do {
@@ -1323,7 +1322,7 @@ LIQ_NONNULL static bool remap_to_palette_floyd(liq_image *input_image, unsigned
}
/* Propagate Floyd-Steinberg error terms. */
- if (fs_direction) {
+ if (fs_direction > 0) {
thiserr[col + 2].a += err.a * (7.f/16.f);
thiserr[col + 2].r += err.r * (7.f/16.f);
thiserr[col + 2].g += err.g * (7.f/16.f);
@@ -1367,19 +1366,18 @@ LIQ_NONNULL static bool remap_to_palette_floyd(liq_image *input_image, unsigned
}
// remapping is done in zig-zag
- if (fs_direction) {
- ++col;
+ col += fs_direction;
+ if (fs_direction > 0) {
if (col >= cols) break;
} else {
if (col <= 0) break;
- --col;
}
} while(1);
f_pixel *const temperr = thiserr;
thiserr = nexterr;
nexterr = temperr;
- fs_direction = !fs_direction;
+ fs_direction = -fs_direction;
}
input_image->free(MIN(thiserr, nexterr)); // MIN because pointers were swapped
@@ -1404,8 +1402,63 @@ LIQ_NONNULL static void remove_fixed_colors_from_histogram(histogram *hist, cons
}
}
-LIQ_EXPORT LIQ_NONNULL liq_error liq_histogram_add_image(liq_histogram *input_hist, liq_attr *options, liq_image *input_image)
+LIQ_EXPORT LIQ_NONNULL liq_error liq_histogram_add_colors(liq_histogram *input_hist, const liq_attr *options, const liq_histogram_entry entries[], int num_entries, double gamma)
{
+ if (!CHECK_STRUCT_TYPE(options, liq_attr)) return LIQ_INVALID_POINTER;
+ if (!CHECK_STRUCT_TYPE(input_hist, liq_histogram)) return LIQ_INVALID_POINTER;
+ if (!CHECK_USER_POINTER(entries)) return LIQ_INVALID_POINTER;
+ if (gamma < 0 || gamma >= 1.0) return LIQ_VALUE_OUT_OF_RANGE;
+ if (num_entries <= 0 || num_entries > 1<<30) return LIQ_VALUE_OUT_OF_RANGE;
+
+ if (input_hist->ignorebits > 0 && input_hist->had_image_added) {
+ return LIQ_UNSUPPORTED;
+ }
+ input_hist->ignorebits = 0;
+
+ input_hist->had_image_added = true;
+ input_hist->gamma = gamma ? gamma : 0.45455;
+
+ if (!input_hist->acht) {
+ input_hist->acht = pam_allocacolorhash(~0, num_entries*num_entries, 0, options->malloc, options->free);
+ if (!input_hist->acht) {
+ return LIQ_OUT_OF_MEMORY;
+ }
+ }
+ // Fake image size. It's only for hash size estimates.
+ if (!input_hist->acht->cols) {
+ input_hist->acht->cols = num_entries;
+ }
+ input_hist->acht->rows += num_entries;
+
+ const unsigned int hash_size = input_hist->acht->hash_size;
+ for(int i=0; i < num_entries; i++) {
+ const rgba_pixel rgba = {
+ .r = entries[i].color.r,
+ .g = entries[i].color.g,
+ .b = entries[i].color.b,
+ .a = entries[i].color.a,
+ };
+ union rgba_as_int px = {rgba};
+ unsigned int hash;
+ if (px.rgba.a) {
+ hash = px.l % hash_size;
+ } else {
+ hash=0; px.l=0;
+ }
+ if (!pam_add_to_hash(input_hist->acht, hash, entries[i].count, px, i, num_entries)) {
+ return LIQ_OUT_OF_MEMORY;
+ }
+ }
+
+ return LIQ_OK;
+}
+
+LIQ_EXPORT LIQ_NONNULL liq_error liq_histogram_add_image(liq_histogram *input_hist, const liq_attr *options, liq_image *input_image)
+{
+ if (!CHECK_STRUCT_TYPE(options, liq_attr)) return LIQ_INVALID_POINTER;
+ if (!CHECK_STRUCT_TYPE(input_hist, liq_histogram)) return LIQ_INVALID_POINTER;
+ if (!CHECK_STRUCT_TYPE(input_image, liq_image)) return LIQ_INVALID_POINTER;
+
const unsigned int cols = input_image->width, rows = input_image->height;
if (!input_image->noise && options->use_contrast_maps) {
@@ -1588,7 +1641,7 @@ LIQ_NONNULL static void contrast_maps(liq_image *image)
z *= 256.f;
noise[j*cols+i] = z < 256 ? z : 255;
z = (1.f-edge)*256.f;
- edges[j*cols+i] = z < 256 ? z : 255;
+ edges[j*cols+i] = z > 0 ? (z < 256 ? z : 255) : 0;
}
}
@@ -1729,11 +1782,11 @@ static colormap *find_best_palette(histogram *hist, const liq_attr *options, con
}
// after palette has been created, total error (MSE) is calculated to keep the best palette
- // at the same time Voronoi iteration is done to improve the palette
+ // at the same time K-Means iteration is done to improve the palette
// and histogram weights are adjusted based on remapping error to give more weight to poorly matched colors
const bool first_run_of_target_mse = !acolormap && target_mse > 0;
- double total_error = viter_do_iteration(hist, newmap, first_run_of_target_mse ? NULL : adjust_histogram_callback, !acolormap || options->fast_palette);
+ double total_error = kmeans_do_iteration(hist, newmap, first_run_of_target_mse ? NULL : adjust_histogram_callback);
// goal is to increase quality or to reduce number of colors used if quality is good enough
if (!acolormap || total_error < least_error || (total_error <= target_mse && newmap->colors < max_colors)) {
@@ -1741,7 +1794,7 @@ static colormap *find_best_palette(histogram *hist, const liq_attr *options, con
acolormap = newmap;
if (total_error < target_mse && total_error > 0) {
- // voronoi iteration improves quality above what mediancut aims for
+ // K-Means iteration improves quality above what mediancut aims for
// this compensates for it, making mediancut aim for worse
target_mse_overshoot = MIN(target_mse_overshoot*1.25, target_mse/total_error);
}
@@ -1794,8 +1847,6 @@ LIQ_NONNULL static liq_error pngquant_quantize(histogram *hist, const liq_attr *
assert((verbose_print(options, "SLOW debug checks enabled. Recompile with NDEBUG for normal operation."),1));
- // no point having perfect match with imperfect colors (ignorebits > 0)
- const bool fast_palette = options->fast_palette || hist->ignorebits > 0;
const bool few_input_colors = hist->size+fixed_colors_count <= options->max_colors;
if (liq_progress(options, options->progress_stage1)) return LIQ_ABORTED;
@@ -1812,14 +1863,14 @@ LIQ_NONNULL static liq_error pngquant_quantize(histogram *hist, const liq_attr *
return LIQ_VALUE_OUT_OF_RANGE;
}
- // Voronoi iteration approaches local minimum for the palette
- const double iteration_limit = options->voronoi_iteration_limit;
- unsigned int iterations = options->voronoi_iterations;
+ // K-Means iteration approaches local minimum for the palette
+ const double iteration_limit = options->kmeans_iteration_limit;
+ unsigned int iterations = options->kmeans_iterations;
if (!iterations && palette_error < 0 && max_mse < MAX_DIFF) iterations = 1; // otherwise total error is never calculated and MSE limit won't work
if (iterations) {
- // likely_colormap_index (used and set in viter_do_iteration) can't point to index outside colormap
+ // likely_colormap_index (used and set in kmeans_do_iteration) can't point to index outside colormap
if (acolormap->colors < 256) for(unsigned int j=0; j < hist->size; j++) {
if (hist->achv[j].tmp.likely_colormap_index >= acolormap->colors) {
hist->achv[j].tmp.likely_colormap_index = 0; // actual value doesn't matter, as the guess is out of date anyway
@@ -1831,7 +1882,7 @@ LIQ_NONNULL static liq_error pngquant_quantize(histogram *hist, const liq_attr *
double previous_palette_error = MAX_DIFF;
for(unsigned int i=0; i < iterations; i++) {
- palette_error = viter_do_iteration(hist, acolormap, NULL, i==0 || options->fast_palette);
+ palette_error = kmeans_do_iteration(hist, acolormap, NULL);
if (liq_progress(options, options->progress_stage1 + options->progress_stage2 + (i * options->progress_stage3 * 0.9f) / iterations)) {
break;
@@ -1882,7 +1933,6 @@ LIQ_NONNULL static liq_error pngquant_quantize(histogram *hist, const liq_attr *
.free = options->free,
.palette = acolormap,
.palette_error = palette_error,
- .fast_palette = fast_palette,
.use_dither_map = options->use_dither_map,
.gamma = gamma,
.min_posterization_output = options->min_posterization_output,
@@ -1946,12 +1996,12 @@ LIQ_EXPORT LIQ_NONNULL liq_error liq_write_remapped_image_rows(liq_result *quant
float remapping_error = result->palette_error;
if (result->dither_level == 0) {
set_rounded_palette(&result->int_palette, result->palette, result->gamma, quant->min_posterization_output);
- remapping_error = remap_to_palette(input_image, row_pointers, result->palette, quant->fast_palette);
+ remapping_error = remap_to_palette(input_image, row_pointers, result->palette);
} else {
const bool generate_dither_map = result->use_dither_map && (input_image->edges && !input_image->dither_map);
if (generate_dither_map) {
// If dithering (with dither map) is required, this image is used to find areas that require dithering
- remapping_error = remap_to_palette(input_image, row_pointers, result->palette, quant->fast_palette);
+ remapping_error = remap_to_palette(input_image, row_pointers, result->palette);
update_dither_map(row_pointers, input_image);
}
@@ -1959,7 +2009,7 @@ LIQ_EXPORT LIQ_NONNULL liq_error liq_write_remapped_image_rows(liq_result *quant
return LIQ_ABORTED;
}
- // remapping above was the last chance to do voronoi iteration, hence the final palette is set after remapping
+ // remapping above was the last chance to do K-Means iteration, hence the final palette is set after remapping
set_rounded_palette(&result->int_palette, result->palette, result->gamma, quant->min_posterization_output);
if (!remap_to_palette_floyd(input_image, row_pointers, result, MAX(remapping_error*2.4, 16.f/256.f), generate_dither_map)) {
diff --git a/libimagequant.h b/libimagequant.h
index ae503b1..c86f5a5 100644
--- a/libimagequant.h
+++ b/libimagequant.h
@@ -13,8 +13,8 @@
#define LIQ_EXPORT extern
#endif
-#define LIQ_VERSION 20800
-#define LIQ_VERSION_STRING "2.8.0"
+#define LIQ_VERSION 20900
+#define LIQ_VERSION_STRING "2.9.0"
#ifndef LIQ_PRIVATE
#if defined(__GNUC__) || defined (__llvm__)
@@ -57,17 +57,24 @@ typedef enum liq_error {
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};
+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* (*malloc)(size_t), void (*free)(void*));
-LIQ_EXPORT LIQ_USERESULT liq_attr* liq_attr_copy(liq_attr *orig) LIQ_NONNULL;
+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(liq_attr* attr);
-LIQ_EXPORT LIQ_USERESULT liq_error liq_histogram_add_image(liq_histogram *hist, liq_attr *attr, liq_image* image);
+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 void liq_histogram_destroy(liq_histogram *hist) LIQ_NONNULL;
LIQ_EXPORT liq_error liq_set_max_colors(liq_attr* attr, int colors) LIQ_NONNULL;
@@ -117,10 +124,10 @@ LIQ_EXPORT LIQ_USERESULT const liq_palette *liq_get_palette(liq_result *result)
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(liq_result *result) LIQ_NONNULL;
-LIQ_EXPORT int liq_get_quantization_quality(liq_result *result) LIQ_NONNULL;
-LIQ_EXPORT double liq_get_remapping_error(liq_result *result) LIQ_NONNULL;
-LIQ_EXPORT int liq_get_remapping_quality(liq_result *result) 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;
diff --git a/mediancut.c b/mediancut.c
index 9d93d49..6e3e590 100644
--- a/mediancut.c
+++ b/mediancut.c
@@ -38,7 +38,7 @@
#define index_of_channel(ch) (offsetof(f_pixel,ch)/sizeof(float))
-static f_pixel averagepixels(unsigned int clrs, const hist_item achv[], const f_pixel center);
+static f_pixel averagepixels(unsigned int clrs, const hist_item achv[]);
struct box {
f_pixel color;
@@ -63,7 +63,7 @@ static f_pixel box_variance(const hist_item achv[], const struct box *box)
double variancea=0, variancer=0, varianceg=0, varianceb=0;
for(unsigned int i = 0; i < box->colors; ++i) {
- f_pixel px = achv[box->ind + i].acolor;
+ const f_pixel px = achv[box->ind + i].acolor;
double weight = achv[box->ind + i].adjusted_weight;
variancea += variance_diff(mean.a - px.a, 2.0/256.0)*weight;
variancer += variance_diff(mean.r - px.r, 1.0/256.0)*weight;
@@ -208,10 +208,10 @@ static double prepare_sort(struct box *b, hist_item achv[])
** Sort dimensions by their variance, and then sort colors first by dimension with highest variance
*/
channelvariance channels[4] = {
+ {index_of_channel(a), b->variance.a},
{index_of_channel(r), b->variance.r},
{index_of_channel(g), b->variance.g},
{index_of_channel(b), b->variance.b},
- {index_of_channel(a), b->variance.a},
};
qsort(channels, 4, sizeof(channels[0]), comparevariance);
@@ -245,7 +245,7 @@ static f_pixel get_median(const struct box *b, hist_item achv[])
// technically the second color is not guaranteed to be sorted correctly
// but most of the time it is good enough to be useful
- return averagepixels(2, &achv[b->ind + median_start], (f_pixel){0.5,0.5,0.5,0.5});
+ return averagepixels(2, &achv[b->ind + median_start]);
}
/*
@@ -278,13 +278,11 @@ static int best_splittable_box(struct box* bv, unsigned int boxes, const double
inline static double color_weight(f_pixel median, hist_item h)
{
float diff = colordifference(median, h.acolor);
- // if color is "good enough", don't split further
- if (diff < 1.f/256.f/256.f) diff /= 2.f;
return sqrt(diff) * (sqrt(1.0+h.adjusted_weight)-1.0);
}
static void set_colormap_from_boxes(colormap *map, struct box* bv, unsigned int boxes, hist_item *achv);
-static void adjust_histogram(hist_item *achv, const colormap *map, const struct box* bv, unsigned int boxes);
+static void adjust_histogram(hist_item *achv, const struct box* bv, unsigned int boxes);
static double box_error(const struct box *box, const hist_item achv[])
{
@@ -323,6 +321,16 @@ static bool total_box_error_below_target(double target_mse, struct box bv[], uns
return true;
}
+static void box_init(struct box *box, const hist_item *achv, const unsigned int ind, const unsigned int colors, const double sum) {
+ box->ind = ind;
+ box->colors = colors;
+ box->sum = sum;
+ box->total_error = -1;
+ box->color = averagepixels(colors, &achv[ind]);
+ box->variance = box_variance(achv, box);
+ box->max_error = box_max_error(achv, box);
+}
+
/*
** Here is the fun part, the median-cut colormap generator. This is based
** on Paul Heckbert's paper, "Color Image Quantization for Frame Buffer
@@ -336,14 +344,11 @@ LIQ_PRIVATE colormap *mediancut(histogram *hist, unsigned int newcolors, const d
/*
** Set up the initial box.
*/
- bv[0].ind = 0;
- bv[0].colors = hist->size;
- bv[0].color = averagepixels(bv[0].colors, &achv[bv[0].ind], (f_pixel){0.5,0.5,0.5,0.5});
- bv[0].variance = box_variance(achv, &bv[0]);
- bv[0].max_error = box_max_error(achv, &bv[0]);
- bv[0].sum = 0;
- bv[0].total_error = -1;
- for(unsigned int i=0; i < bv[0].colors; i++) bv[0].sum += achv[i].adjusted_weight;
+ double sum = 0;
+ for(unsigned int i=0; i < hist->size; i++) {
+ sum += achv[i].adjusted_weight;
+ }
+ box_init(&bv[0], achv, 0, hist->size, sum);
unsigned int boxes = 1;
@@ -388,20 +393,8 @@ LIQ_PRIVATE colormap *mediancut(histogram *hist, unsigned int newcolors, const d
double lowersum = 0;
for(unsigned int i=0; i < break_at; i++) lowersum += achv[indx + i].adjusted_weight;
- const f_pixel previous_center = bv[bi].color;
- bv[bi].colors = break_at;
- bv[bi].sum = lowersum;
- bv[bi].color = averagepixels(bv[bi].colors, &achv[bv[bi].ind], previous_center);
- bv[bi].total_error = -1;
- bv[bi].variance = box_variance(achv, &bv[bi]);
- bv[bi].max_error = box_max_error(achv, &bv[bi]);
- bv[boxes].ind = indx + break_at;
- bv[boxes].colors = clrs - break_at;
- bv[boxes].sum = sm - lowersum;
- bv[boxes].color = averagepixels(bv[boxes].colors, &achv[bv[boxes].ind], previous_center);
- bv[boxes].total_error = -1;
- bv[boxes].variance = box_variance(achv, &bv[boxes]);
- bv[boxes].max_error = box_max_error(achv, &bv[boxes]);
+ box_init(&bv[bi], achv, bv[bi].ind, break_at, lowersum);
+ box_init(&bv[boxes], achv, indx + break_at, clrs - break_at, sm - lowersum);
++boxes;
@@ -413,7 +406,7 @@ LIQ_PRIVATE colormap *mediancut(histogram *hist, unsigned int newcolors, const d
colormap *map = pam_colormap(boxes, malloc, free);
set_colormap_from_boxes(map, bv, boxes, achv);
- adjust_histogram(achv, map, bv, boxes);
+ adjust_histogram(achv, bv, boxes);
return map;
}
@@ -440,17 +433,16 @@ static void set_colormap_from_boxes(colormap *map, struct box* bv, unsigned int
}
/* increase histogram popularity by difference from the final color (this is used as part of feedback loop) */
-static void adjust_histogram(hist_item *achv, const colormap *map, const struct box* bv, unsigned int boxes)
+static void adjust_histogram(hist_item *achv, const struct box* bv, unsigned int boxes)
{
for(unsigned int bi = 0; bi < boxes; ++bi) {
for(unsigned int i=bv[bi].ind; i < bv[bi].ind+bv[bi].colors; i++) {
- achv[i].adjusted_weight *= sqrt(1.0 +colordifference(map->palette[bi].acolor, achv[i].acolor)/2.0);
achv[i].tmp.likely_colormap_index = bi;
}
}
}
-static f_pixel averagepixels(unsigned int clrs, const hist_item achv[], f_pixel center)
+static f_pixel averagepixels(unsigned int clrs, const hist_item achv[])
{
double r = 0, g = 0, b = 0, a = 0, sum = 0;
@@ -459,10 +451,10 @@ static f_pixel averagepixels(unsigned int clrs, const hist_item achv[], f_pixel
const double weight = achv[i].adjusted_weight;
sum += weight;
+ a += px.a * weight;
r += px.r * weight;
g += px.g * weight;
b += px.b * weight;
- a += px.a * weight;
}
if (sum) {
diff --git a/nearest.c b/nearest.c
index 448ab3a..2f1e926 100644
--- a/nearest.c
+++ b/nearest.c
@@ -117,7 +117,7 @@ static vp_node *vp_create_node(mempool *m, vp_sort_tmp *indexes, int num_indexes
return node;
}
-LIQ_PRIVATE struct nearest_map *nearest_init(const colormap *map, const bool fast) {
+LIQ_PRIVATE struct nearest_map *nearest_init(const colormap *map) {
mempool m = NULL;
struct nearest_map *handle = mempool_create(&m, sizeof(handle[0]), sizeof(handle[0]) + sizeof(vp_node)*map->colors+16, map->malloc, map->free);
diff --git a/nearest.h b/nearest.h
index 0a98ca6..e20233b 100644
--- a/nearest.h
+++ b/nearest.h
@@ -3,6 +3,6 @@
// pngquant
//
struct nearest_map;
-LIQ_PRIVATE struct nearest_map *nearest_init(const colormap *palette, const bool fast);
+LIQ_PRIVATE struct nearest_map *nearest_init(const colormap *palette);
LIQ_PRIVATE unsigned int nearest_search(const struct nearest_map *map, const f_pixel *px, const int palette_index_guess, float *diff);
LIQ_PRIVATE void nearest_free(struct nearest_map *map);
diff --git a/pam.c b/pam.c
index aae2927..74165e1 100644
--- a/pam.c
+++ b/pam.c
@@ -22,20 +22,14 @@
LIQ_PRIVATE bool pam_computeacolorhash(struct acolorhash_table *acht, const rgba_pixel *const pixels[], unsigned int cols, unsigned int rows, const unsigned char *importance_map)
{
- const unsigned int maxacolors = acht->maxcolors, ignorebits = acht->ignorebits;
+ const unsigned int ignorebits = acht->ignorebits;
const unsigned int channel_mask = 255U>>ignorebits<>ignorebits) ^ 0xFFU;
const unsigned int posterize_mask = channel_mask << 24 | channel_mask << 16 | channel_mask << 8 | channel_mask;
const unsigned int posterize_high_mask = channel_hmask << 24 | channel_hmask << 16 | channel_hmask << 8 | channel_hmask;
- struct acolorhist_arr_head *const buckets = acht->buckets;
- unsigned int colors = acht->colors;
const unsigned int hash_size = acht->hash_size;
- const unsigned int stacksize = sizeof(acht->freestack)/sizeof(acht->freestack[0]);
- struct acolorhist_arr_item **freestack = acht->freestack;
- unsigned int freestackp=acht->freestackp;
-
/* Go through the entire image, building a hash table of colors. */
for(unsigned int row = 0; row < rows; ++row) {
@@ -59,19 +53,32 @@ LIQ_PRIVATE bool pam_computeacolorhash(struct acolorhash_table *acht, const rgba
hash = px.l % hash_size;
}
+ if (!pam_add_to_hash(acht, hash, boost, px, row, rows)) {
+ return false;
+ }
+ }
+
+ }
+ acht->cols = cols;
+ acht->rows += rows;
+ return true;
+}
+
+LIQ_PRIVATE bool pam_add_to_hash(struct acolorhash_table *acht, unsigned int hash, float boost, union rgba_as_int px, unsigned int row, unsigned int rows)
+{
/* head of the hash function stores first 2 colors inline (achl->used = 1..2),
to reduce number of allocations of achl->other_items.
*/
- struct acolorhist_arr_head *achl = &buckets[hash];
+ struct acolorhist_arr_head *achl = &acht->buckets[hash];
if (achl->inline1.color.l == px.l && achl->used) {
achl->inline1.perceptual_weight += boost;
- continue;
+ return true;
}
if (achl->used) {
if (achl->used > 1) {
if (achl->inline2.color.l == px.l) {
achl->inline2.perceptual_weight += boost;
- continue;
+ return true;
}
// other items are stored as an array (which gets reallocated if needed)
struct acolorhist_arr_item *other_items = achl->other_items;
@@ -79,7 +86,7 @@ LIQ_PRIVATE bool pam_computeacolorhash(struct acolorhash_table *acht, const rgba
for (; i < achl->used-2; i++) {
if (other_items[i].color.l == px.l) {
other_items[i].perceptual_weight += boost;
- goto continue_outer_loop;
+ return true;
}
}
@@ -90,13 +97,11 @@ LIQ_PRIVATE bool pam_computeacolorhash(struct acolorhash_table *acht, const rgba
.perceptual_weight = boost,
};
achl->used++;
- ++colors;
- continue;
+ ++acht->colors;
+ return true;
}
- if (++colors > maxacolors) {
- acht->colors = colors;
- acht->freestackp = freestackp;
+ if (++acht->colors > acht->maxcolors) {
return false;
}
@@ -104,22 +109,24 @@ LIQ_PRIVATE bool pam_computeacolorhash(struct acolorhash_table *acht, const rgba
unsigned int capacity;
if (!other_items) { // there was no array previously, alloc "small" array
capacity = 8;
- if (freestackp <= 0) {
+ if (acht->freestackp <= 0) {
// estimate how many colors are going to be + headroom
- const size_t mempool_size = ((acht->rows + rows-row) * 2 * colors / (acht->rows + row + 1) + 1024) * sizeof(struct acolorhist_arr_item);
+ const size_t mempool_size = ((acht->rows + rows-row) * 2 * acht->colors / (acht->rows + row + 1) + 1024) * sizeof(struct acolorhist_arr_item);
new_items = mempool_alloc(&acht->mempool, sizeof(struct acolorhist_arr_item)*capacity, mempool_size);
} else {
// freestack stores previously freed (reallocated) arrays that can be reused
// (all pesimistically assumed to be capacity = 8)
- new_items = freestack[--freestackp];
+ new_items = acht->freestack[--acht->freestackp];
}
} else {
+ const unsigned int stacksize = sizeof(acht->freestack)/sizeof(acht->freestack[0]);
+
// simply reallocs and copies array to larger capacity
capacity = achl->capacity*2 + 16;
- if (freestackp < stacksize-1) {
- freestack[freestackp++] = other_items;
+ if (acht->freestackp < stacksize-1) {
+ acht->freestack[acht->freestackp++] = other_items;
}
- const size_t mempool_size = ((acht->rows + rows-row) * 2 * colors / (acht->rows + row + 1) + 32*capacity) * sizeof(struct acolorhist_arr_item);
+ const size_t mempool_size = ((acht->rows + rows-row) * 2 * acht->colors / (acht->rows + row + 1) + 32*capacity) * sizeof(struct acolorhist_arr_item);
new_items = mempool_alloc(&acht->mempool, sizeof(struct acolorhist_arr_item)*capacity, mempool_size);
if (!new_items) return false;
memcpy(new_items, other_items, sizeof(other_items[0])*achl->capacity);
@@ -137,23 +144,14 @@ LIQ_PRIVATE bool pam_computeacolorhash(struct acolorhash_table *acht, const rgba
achl->inline2.color.l = px.l;
achl->inline2.perceptual_weight = boost;
achl->used = 2;
- ++colors;
+ ++acht->colors;
}
} else {
achl->inline1.color.l = px.l;
achl->inline1.perceptual_weight = boost;
achl->used = 1;
- ++colors;
+ ++acht->colors;
}
-
- continue_outer_loop:;
- }
-
- }
- acht->colors = colors;
- acht->cols = cols;
- acht->rows += rows;
- acht->freestackp = freestackp;
return true;
}
@@ -177,10 +175,12 @@ LIQ_PRIVATE struct acolorhash_table *pam_allocacolorhash(unsigned int maxcolors,
return t;
}
-#define PAM_ADD_TO_HIST(entry) { \
- hist->achv[j].acolor = to_f(gamma_lut, entry.color.rgba); \
- total_weight += hist->achv[j].adjusted_weight = hist->achv[j].perceptual_weight = MIN(entry.perceptual_weight, max_perceptual_weight); \
- ++j; \
+ALWAYS_INLINE static float pam_add_to_hist(const float *gamma_lut, hist_item *achv, unsigned int j, const struct acolorhist_arr_item *entry, const float max_perceptual_weight)
+{
+ achv[j].acolor = rgba_to_f(gamma_lut, entry->color.rgba);
+ const float w = MIN(entry->perceptual_weight, max_perceptual_weight);
+ achv[j].adjusted_weight = achv[j].perceptual_weight = w;
+ return w;
}
LIQ_PRIVATE histogram *pam_acolorhashtoacolorhist(const struct acolorhash_table *acht, const double gamma, void* (*malloc)(size_t), void (*free)(void*))
@@ -206,13 +206,13 @@ LIQ_PRIVATE histogram *pam_acolorhashtoacolorhist(const struct acolorhash_table
for(unsigned int j=0, i=0; i < acht->hash_size; ++i) {
const struct acolorhist_arr_head *const achl = &acht->buckets[i];
if (achl->used) {
- PAM_ADD_TO_HIST(achl->inline1);
+ total_weight += pam_add_to_hist(gamma_lut, hist->achv, j++, &achl->inline1, max_perceptual_weight);
if (achl->used > 1) {
- PAM_ADD_TO_HIST(achl->inline2);
+ total_weight += pam_add_to_hist(gamma_lut, hist->achv, j++, &achl->inline2, max_perceptual_weight);
for(unsigned int k=0; k < achl->used-2; k++) {
- PAM_ADD_TO_HIST(achl->other_items[k]);
+ total_weight += pam_add_to_hist(gamma_lut, hist->achv, j++, &achl->other_items[k], max_perceptual_weight);
}
}
}
diff --git a/pam.h b/pam.h
index 3af703e..6657a14 100644
--- a/pam.h
+++ b/pam.h
@@ -93,8 +93,8 @@ LIQ_PRIVATE void to_f_set_gamma(float gamma_lut[], const double gamma);
Converts 8-bit color to internal gamma and premultiplied alpha.
(premultiplied color space is much better for blending of semitransparent colors)
*/
-ALWAYS_INLINE static f_pixel to_f(const float gamma_lut[], const rgba_pixel px);
-inline static f_pixel to_f(const float gamma_lut[], const rgba_pixel px)
+ALWAYS_INLINE static f_pixel rgba_to_f(const float gamma_lut[], const rgba_pixel px);
+inline static f_pixel rgba_to_f(const float gamma_lut[], const rgba_pixel px)
{
float a = px.a/255.f;
@@ -106,7 +106,7 @@ inline static f_pixel to_f(const float gamma_lut[], const rgba_pixel px)
};
}
-inline static rgba_pixel to_rgb(const float gamma, const f_pixel px)
+inline static rgba_pixel f_to_rgb(const float gamma, const f_pixel px)
{
if (px.a < 1.f/256.f) {
return (rgba_pixel){0,0,0,0};
@@ -226,7 +226,7 @@ typedef struct {
typedef struct {
f_pixel acolor;
float popularity;
- bool fixed; // if true it's user-supplied and must not be changed (e.g in voronoi iteration)
+ bool fixed; // if true it's user-supplied and must not be changed (e.g in K-Means iteration)
} colormap_item;
typedef struct colormap {
@@ -260,6 +260,7 @@ LIQ_PRIVATE void pam_freeacolorhash(struct acolorhash_table *acht);
LIQ_PRIVATE struct acolorhash_table *pam_allocacolorhash(unsigned int maxcolors, unsigned int surface, unsigned int ignorebits, void* (*malloc)(size_t), void (*free)(void*));
LIQ_PRIVATE histogram *pam_acolorhashtoacolorhist(const struct acolorhash_table *acht, const double gamma, void* (*malloc)(size_t), void (*free)(void*));
LIQ_PRIVATE bool pam_computeacolorhash(struct acolorhash_table *acht, const rgba_pixel *const pixels[], unsigned int cols, unsigned int rows, const unsigned char *importance_map);
+LIQ_PRIVATE bool pam_add_to_hash(struct acolorhash_table *acht, unsigned int hash, float boost, union rgba_as_int px, unsigned int row, unsigned int rows);
LIQ_PRIVATE void pam_freeacolorhist(histogram *h);
diff --git a/viter.h b/viter.h
deleted file mode 100644
index bbbaaa1..0000000
--- a/viter.h
+++ /dev/null
@@ -1,19 +0,0 @@
-
-#ifndef VITER_H
-#define VITER_H
-
-// Spread memory touched by different threads at least 64B apart which I assume is the cache line size. This should avoid memory write contention.
-#define VITER_CACHE_LINE_GAP ((64+sizeof(viter_state)-1)/sizeof(viter_state))
-
-typedef struct {
- double a, r, g, b, total;
-} viter_state;
-
-typedef void (*viter_callback)(hist_item *item, float diff);
-
-LIQ_PRIVATE void viter_init(const colormap *map, const unsigned int max_threads, viter_state state[]);
-LIQ_PRIVATE void viter_update_color(const f_pixel acolor, const float value, const colormap *map, unsigned int match, const unsigned int thread, viter_state average_color[]);
-LIQ_PRIVATE void viter_finalize(colormap *map, const unsigned int max_threads, const viter_state state[]);
-LIQ_PRIVATE double viter_do_iteration(histogram *hist, colormap *const map, viter_callback callback, const bool fast_palette);
-
-#endif