gstreamer/subprojects/gst-plugins-bad/gst/dvbsubenc/libimagequant/pam.c

326 lines
10 KiB
C
Raw Normal View History

/* pam.c - pam (portable alpha map) utility library
**
** Copyright (C) 1989, 1991 by Jef Poskanzer.
** Copyright (C) 1997, 2000, 2002 by Greg Roelofs; based on an idea by
** Stefan Schneider.
** © 2009-2013 by Kornel Lesinski.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
*/
#include <stdlib.h>
#include <string.h>
#include "libimagequant.h"
#include "pam.h"
#include "mempool.h"
/* *INDENT-OFF* */
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)
/* *INDENT-ON* */
{
const unsigned int maxacolors = acht->maxcolors, ignorebits =
acht->ignorebits;
const unsigned int channel_mask = 255U >> ignorebits << ignorebits;
const unsigned int channel_hmask = (255U >> 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) {
float boost = 1.0;
for (unsigned int col = 0; col < cols; ++col) {
union rgba_as_int px = { pixels[row][col] };
unsigned int hash;
struct acolorhist_arr_head *achl;
if (importance_map) {
boost = 0.5f + (double) *importance_map++ / 255.f;
}
// RGBA color is casted to long for easier hasing/comparisons
if (!px.rgba.a) {
// "dirty alpha" has different RGBA values that end up being the same fully transparent color
px.l = 0;
hash = 0;
} else {
// mask posterizes all 4 channels in one go
px.l =
(px.l & posterize_mask) | ((px.l & posterize_high_mask) >> (8 -
ignorebits));
// fancier hashing algorithms didn't improve much
hash = px.l % hash_size;
}
/* head of the hash function stores first 2 colors inline (achl->used = 1..2),
to reduce number of allocations of achl->other_items.
*/
achl = &buckets[hash];
if (achl->inline1.color.l == px.l && achl->used) {
achl->inline1.perceptual_weight += boost;
continue;
}
if (achl->used) {
if (achl->used > 1) {
struct acolorhist_arr_item *other_items;
unsigned int i = 0;
struct acolorhist_arr_item *new_items;
unsigned int capacity;
if (achl->inline2.color.l == px.l) {
achl->inline2.perceptual_weight += boost;
continue;
}
// other items are stored as an array (which gets reallocated if needed)
other_items = achl->other_items;
for (i = 0; i < achl->used - 2; i++) {
if (other_items[i].color.l == px.l) {
other_items[i].perceptual_weight += boost;
goto continue_outer_loop;
}
}
// the array was allocated with spare items
if (i < achl->capacity) {
other_items[i] = (struct acolorhist_arr_item) {
.color = px,.perceptual_weight = boost,
};
achl->used++;
++colors;
continue;
}
if (++colors > maxacolors) {
acht->colors = colors;
acht->freestackp = freestackp;
return false;
}
if (!other_items) { // there was no array previously, alloc "small" array
capacity = 8;
if (freestackp <= 0) {
// estimate how many colors are going to be + headroom
const int mempool_size =
((acht->rows + rows - row) * 2 * 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];
}
} else {
// simply reallocs and copies array to larger capacity
capacity = achl->capacity * 2 + 16;
if (freestackp < stacksize - 1) {
freestack[freestackp++] = other_items;
}
{
const int mempool_size =
((acht->rows + rows - row) * 2 * 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);
}
achl->other_items = new_items;
achl->capacity = capacity;
new_items[i] = (struct acolorhist_arr_item) {
.color = px,.perceptual_weight = boost,
};
achl->used++;
} else {
// these are elses for first checks whether first and second inline-stored colors are used
achl->inline2.color.l = px.l;
achl->inline2.perceptual_weight = boost;
achl->used = 2;
++colors;
}
} else {
achl->inline1.color.l = px.l;
achl->inline1.perceptual_weight = boost;
achl->used = 1;
++colors;
}
continue_outer_loop:;
}
}
acht->colors = colors;
acht->cols = cols;
acht->rows += rows;
acht->freestackp = freestackp;
return true;
}
LIQ_PRIVATE struct acolorhash_table *
pam_allocacolorhash (unsigned int maxcolors, unsigned int surface,
unsigned int ignorebits, void *(*malloc) (size_t), void (*free) (void *))
{
const unsigned int estimated_colors =
MIN (maxcolors, surface / (ignorebits + (surface > 512 * 512 ? 5 : 4)));
const unsigned int hash_size =
estimated_colors < 66000 ? 6673 : (estimated_colors <
200000 ? 12011 : 24019);
mempool m = NULL;
const unsigned int buckets_size =
hash_size * sizeof (struct acolorhist_arr_head);
const unsigned int mempool_size =
sizeof (struct acolorhash_table) + buckets_size +
estimated_colors * sizeof (struct acolorhist_arr_item);
struct acolorhash_table *t =
mempool_create (&m, sizeof (*t) + buckets_size, mempool_size, malloc,
free);
if (!t)
return NULL;
*t = (struct acolorhash_table) {
.mempool = m,.hash_size = hash_size,.maxcolors = maxcolors,.ignorebits =
ignorebits,
};
memset (t->buckets, 0, hash_size * sizeof (struct acolorhist_arr_head));
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; \
}
LIQ_PRIVATE histogram *
pam_acolorhashtoacolorhist (const struct acolorhash_table *acht,
const double gamma, void *(*malloc) (size_t), void (*free) (void *))
{
histogram *hist = malloc (sizeof (hist[0]));
float gamma_lut[256];
float max_perceptual_weight;
double total_weight;
unsigned int i, j, k;
if (!hist || !acht)
return NULL;
*hist = (histogram) {
.achv = malloc (acht->colors * sizeof (hist->achv[0])),.size =
acht->colors,.free = free,.ignorebits = acht->ignorebits,};
if (!hist->achv)
return NULL;
to_f_set_gamma (gamma_lut, gamma);
/* Limit perceptual weight to 1/10th of the image surface area to prevent
a single color from dominating all others. */
max_perceptual_weight = 0.1f * acht->cols * acht->rows;
total_weight = 0;
for (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);
if (achl->used > 1) {
PAM_ADD_TO_HIST (achl->inline2);
for (k = 0; k < achl->used - 2; k++) {
PAM_ADD_TO_HIST (achl->other_items[k]);
}
}
}
}
hist->total_perceptual_weight = total_weight;
return hist;
}
LIQ_PRIVATE void
pam_freeacolorhash (struct acolorhash_table *acht)
{
mempool_destroy (acht->mempool);
}
LIQ_PRIVATE void
pam_freeacolorhist (histogram * hist)
{
hist->free (hist->achv);
hist->free (hist);
}
LIQ_PRIVATE colormap *
pam_colormap (unsigned int colors, void *(*malloc) (size_t),
void (*free) (void *))
{
const size_t colors_size = colors * sizeof (colormap_item);
colormap *map;
assert (colors > 0 && colors < 65536);
map = malloc (sizeof (colormap) + colors_size);
if (!map)
return NULL;
*map = (colormap) {
.malloc = malloc,.free = free,.subset_palette = NULL,.colors = colors,};
memset (map->palette, 0, colors_size);
return map;
}
LIQ_PRIVATE colormap *
pam_duplicate_colormap (colormap * map)
{
colormap *dupe = pam_colormap (map->colors, map->malloc, map->free);
for (unsigned int i = 0; i < map->colors; i++) {
dupe->palette[i] = map->palette[i];
}
if (map->subset_palette) {
dupe->subset_palette = pam_duplicate_colormap (map->subset_palette);
}
return dupe;
}
LIQ_PRIVATE void
pam_freecolormap (colormap * c)
{
if (c->subset_palette)
pam_freecolormap (c->subset_palette);
c->free (c);
}
LIQ_PRIVATE void
to_f_set_gamma (float gamma_lut[], const double gamma)
{
for (int i = 0; i < 256; i++) {
gamma_lut[i] = pow ((double) i / 255.0, internal_gamma / gamma);
}
}