Skip to content

Commit

Permalink
Change kcolor signatures, add helpers.
Browse files Browse the repository at this point in the history
  • Loading branch information
kohler committed Feb 19, 2024
1 parent 7d0a236 commit 233036b
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 130 deletions.
58 changes: 20 additions & 38 deletions src/kcolor.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ pthread_mutex_t kd3_sort_lock;

const char* kc_debug_str(kcolor x) {
static int whichbuf = 0;
static char buf[4][64];
whichbuf = (whichbuf + 1) % 4;
static char buf[8][64];
whichbuf = (whichbuf + 1) % 8;
if (x.a[0] >= 0 && x.a[1] >= 0 && x.a[2] >= 0) {
kc_revgamma_transform(&x);
x = kc_revgamma_transform(x);
snprintf(buf[whichbuf], sizeof(buf[whichbuf]), "#%02X%02X%02X",
x.a[0] >> 7, x.a[1] >> 7, x.a[2] >> 7);
} else
Expand Down Expand Up @@ -154,14 +154,15 @@ void kc_set_gamma(int type, double gamma) {
#endif
}

void kc_revgamma_transform(kcolor* x) {
kcolor kc_revgamma_transform(kcolor x) {
int d;
for (d = 0; d != 3; ++d) {
int c = gamma_tables[1][x->a[d] >> 7];
while (c < 0x7F80 && x->a[d] >= gamma_tables[0][(c + 0x80) >> 7])
int c = gamma_tables[1][x.a[d] >> 7];
while (c < 0x7F80 && x.a[d] >= gamma_tables[0][(c + 0x80) >> 7])
c += 0x80;
x->a[d] = c;
x.a[d] = c;
}
return x;
}

#if 0
Expand All @@ -170,13 +171,11 @@ static void kc_test_gamma() {
for (x = 0; x != 256; ++x)
for (y = 0; y != 256; ++y)
for (z = 0; z != 256; ++z) {
kcolor k;
kc_set8g(&k, x, y, z);
kcolor k = kc_make8g(x, y, z);
kc_revgamma_transform(&k);
if ((k.a[0] >> 7) != x || (k.a[1] >> 7) != y
|| (k.a[2] >> 7) != z) {
kcolor kg;
kc_set8g(&kg, x, y, z);
kcolor kg = kc_make8g(x, y, z);
fprintf(stderr, "#%02X%02X%02X ->g #%04X%04X%04X ->revg #%02X%02X%02X!\n",
x, y, z, kg.a[0], kg.a[1], kg.a[2],
k.a[0] >> 7, k.a[1] >> 7, k.a[2] >> 7);
Expand Down Expand Up @@ -359,12 +358,12 @@ struct kd3_treepos {
int offset;
};

void kd3_init(kd3_tree* kd3, void (*transform)(kcolor*)) {
void kd3_init(kd3_tree* kd3, kcolor (*transform)(int, int, int)) {
kd3->tree = NULL;
kd3->ks = Gif_NewArray(kcolor, 256);
kd3->nitems = 0;
kd3->items_cap = 256;
kd3->transform = transform;
kd3->transform = transform ? transform : kc_make8g;
kd3->xradius = NULL;
kd3->disabled = -1;
}
Expand All @@ -375,12 +374,12 @@ void kd3_cleanup(kd3_tree* kd3) {
Gif_DeleteArray(kd3->xradius);
}

void kd3_add_transformed(kd3_tree* kd3, const kcolor* k) {
void kd3_add_transformed(kd3_tree* kd3, kcolor k) {
if (kd3->nitems == kd3->items_cap) {
kd3->items_cap *= 2;
Gif_ReArray(kd3->ks, kcolor, kd3->items_cap);
}
kd3->ks[kd3->nitems] = *k;
kd3->ks[kd3->nitems] = k;
++kd3->nitems;
if (kd3->tree) {
Gif_DeleteArray(kd3->tree);
Expand All @@ -390,14 +389,6 @@ void kd3_add_transformed(kd3_tree* kd3, const kcolor* k) {
}
}

void kd3_add8g(kd3_tree* kd3, int a0, int a1, int a2) {
kcolor k;
kc_set8g(&k, a0, a1, a2);
if (kd3->transform)
kd3->transform(&k);
kd3_add_transformed(kd3, &k);
}

static kd3_tree* kd3_sorter;

static int kd3_item_compare_0(const void* a, const void* b) {
Expand Down Expand Up @@ -525,7 +516,7 @@ void kd3_build_xradius(kd3_tree* kd3) {
kd3->xradius[i] = (unsigned) -1;
for (i = 0; i != kd3->nitems; ++i)
for (j = i + 1; j != kd3->nitems; ++j) {
unsigned dist = kc_distance(&kd3->ks[i], &kd3->ks[j]);
unsigned dist = kc_distance(kd3->ks[i], kd3->ks[j]);
unsigned radius = dist / 4;
if (radius < kd3->xradius[i])
kd3->xradius[i] = radius;
Expand Down Expand Up @@ -574,7 +565,7 @@ void kd3_build(kd3_tree* kd3) {
Gif_DeleteArray(perm);
}

void kd3_init_build(kd3_tree* kd3, void (*transform)(kcolor*),
void kd3_init_build(kd3_tree* kd3, kcolor (*transform)(int, int, int),
const Gif_Colormap* gfcm) {
int i;
kd3_init(kd3, transform);
Expand All @@ -584,8 +575,7 @@ void kd3_init_build(kd3_tree* kd3, void (*transform)(kcolor*),
kd3_build(kd3);
}

int kd3_closest_transformed(kd3_tree* kd3, const kcolor* k,
unsigned* dist_store) {
int kd3_closest_transformed(kd3_tree* kd3, kcolor k, unsigned* dist_store) {
const kd3_treepos* stack[32];
uint8_t state[32];
int stackpos = 0;
Expand All @@ -605,7 +595,7 @@ int kd3_closest_transformed(kd3_tree* kd3, const kcolor* k,

if (p->offset < 0) {
if (p->pivot >= 0 && kd3->disabled != p->pivot) {
unsigned dist = kc_distance(&kd3->ks[p->pivot], k);
unsigned dist = kc_distance(kd3->ks[p->pivot], k);
if (dist < mindist) {
mindist = dist;
result = p->pivot;
Expand All @@ -614,14 +604,14 @@ int kd3_closest_transformed(kd3_tree* kd3, const kcolor* k,
if (--stackpos >= 0)
++state[stackpos];
} else if (state[stackpos] == 0) {
if (k->a[stackpos % 3] < p->pivot)
if (k.a[stackpos % 3] < p->pivot)
stack[stackpos + 1] = p + 1;
else
stack[stackpos + 1] = p + p->offset;
++stackpos;
state[stackpos] = 0;
} else {
int delta = k->a[stackpos % 3] - p->pivot;
int delta = k.a[stackpos % 3] - p->pivot;
if (state[stackpos] == 1
&& (unsigned) delta * (unsigned) delta < mindist) {
if (delta < 0)
Expand All @@ -639,11 +629,3 @@ int kd3_closest_transformed(kd3_tree* kd3, const kcolor* k,
*dist_store = mindist;
return result;
}

int kd3_closest8g(kd3_tree* kd3, int a0, int a1, int a2) {
kcolor k;
kc_set8g(&k, a0, a1, a2);
if (kd3->transform)
kd3->transform(&k);
return kd3_closest_transformed(kd3, &k, NULL);
}
91 changes: 50 additions & 41 deletions src/kcolor.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,12 @@ typedef union kacolor {
extern uint16_t* gamma_tables[2];


/* set `*kc` to the gamma transformation of `a0/a1/a2` [RGB] */
static inline void kc_set8g(kcolor* kc, int a0, int a1, int a2) {
kc->a[0] = gamma_tables[0][a0];
kc->a[1] = gamma_tables[0][a1];
kc->a[2] = gamma_tables[0][a2];
}

/* return the gamma transformation of `a0/a1/a2` [RGB] */
static inline kcolor kc_make8g(int a0, int a1, int a2) {
kcolor kc;
kc_set8g(&kc, a0, a1, a2);
kc.a[0] = gamma_tables[0][a0];
kc.a[1] = gamma_tables[0][a1];
kc.a[2] = gamma_tables[0][a2];
return kc;
}

Expand Down Expand Up @@ -90,47 +85,48 @@ static inline kacolor kac_transparent() {
const char* kc_debug_str(kcolor x);

/* set `*x` to the reverse gamma transformation of `*x` */
void kc_revgamma_transform(kcolor* x);
kcolor kc_revgamma_transform(kcolor x);

/* return the reverse gramma transformation of `*x` as a Gif_Color */
static inline Gif_Color kc_togfcg(const kcolor* x) {
kcolor xx = *x;
static inline Gif_Color kc_togfcg(kcolor x) {
Gif_Color gfc;
kc_revgamma_transform(&xx);
gfc.gfc_red = (uint8_t) (xx.a[0] >> 7);
gfc.gfc_green = (uint8_t) (xx.a[1] >> 7);
gfc.gfc_blue = (uint8_t) (xx.a[2] >> 7);
x = kc_revgamma_transform(x);
gfc.gfc_red = (uint8_t) (x.a[0] >> 7);
gfc.gfc_green = (uint8_t) (x.a[1] >> 7);
gfc.gfc_blue = (uint8_t) (x.a[2] >> 7);
gfc.haspixel = 0;
return gfc;
}


/* return the squared Euclidean distance between `*x` and `*y` */
static inline uint32_t kc_distance(const kcolor* x, const kcolor* y) {
static inline uint32_t kc_distance(kcolor x, kcolor y) {
/* It’s OK to use unsigned multiplication for this: the low 32 bits
are the same either way. Unsigned avoids undefined behavior. */
uint32_t d0 = x->a[0] - y->a[0];
uint32_t d1 = x->a[1] - y->a[1];
uint32_t d2 = x->a[2] - y->a[2];
uint32_t d0 = x.a[0] - y.a[0];
uint32_t d1 = x.a[1] - y.a[1];
uint32_t d2 = x.a[2] - y.a[2];
return d0 * d0 + d1 * d1 + d2 * d2;
}

/* return the luminance value for `*x`; result is between 0 and KC_MAX */
static inline int kc_luminance(const kcolor* x) {
return (55 * x->a[0] + 183 * x->a[1] + 19 * x->a[2]) >> 8;
static inline int kc_luminance(kcolor kc) {
return (55 * kc.a[0] + 183 * kc.a[1] + 19 * kc.a[2]) >> 8;
}

/* set `*x` to the grayscale version of `*x`, transformed by luminance */
static inline void kc_luminance_transform(kcolor* x) {
static inline kcolor kc_luminance_transform(int a0, int a1, int a2) {
/* For grayscale colormaps, use distance in luminance space instead of
distance in RGB space. The weights for the R,G,B components in
luminance space are 0.2126,0.7152,0.0722. (That's ITU primaries, which
are compatible with sRGB; NTSC recommended our previous values,
0.299,0.587,0.114.) Using the proportional factors 55,183,19 we get a
scaled gray value between 0 and 255 * 257; dividing by 256 gives us
what we want. Thanks to Christian Kumpf, <[email protected]>, for
providing a patch.*/
x->a[0] = x->a[1] = x->a[2] = kc_luminance(x);
providing a patch. */
kcolor kc = kc_make8g(a0, a1, a2);
kc.a[0] = kc.a[1] = kc.a[2] = kc_luminance(kc);
return kc;
}


Expand Down Expand Up @@ -158,22 +154,34 @@ struct kd3_tree {
int nitems;
int items_cap;
int maxdepth;
void (*transform)(kcolor*);
kcolor (*transform)(int, int, int);
unsigned* xradius;
};

/* initialize `kd3` with the given color `transform` (may be NULL) */
void kd3_init(kd3_tree* kd3, void (*transform)(kcolor*));
void kd3_init(kd3_tree* kd3, kcolor (*transform)(int, int, int));

/* free `kd3` */
void kd3_cleanup(kd3_tree* kd3);

/* return the transformed color for 8-bit color `a0/a1/a2` (RGB) */
static inline kcolor kd3_make8g(kd3_tree* kd3, int a0, int a1, int a2) {
return kd3->transform(a0, a1, a2);
}

/* return the transformed color for `*gfc` */
static inline kcolor kd3_makegfcg(kd3_tree* kd3, const Gif_Color* gfc) {
return kd3_make8g(kd3, gfc->gfc_red, gfc->gfc_green, gfc->gfc_blue);
}

/* add the transformed color `k` to `*kd3` (do not apply `kd3->transform`). */
void kd3_add_transformed(kd3_tree* kd3, const kcolor* k);
void kd3_add_transformed(kd3_tree* kd3, kcolor k);

/* given 8-bit color `a0/a1/a2` (RGB), gamma-transform it, transform it
by `kd3->transform` if necessary, and add it to `*kd3` */
void kd3_add8g(kd3_tree* kd3, int a0, int a1, int a2);
/* given 8-bit color `a0/a1/a2` (RGB), transform it by `kd3->transform`
(e.g., apply gamma), and add it to `*kd3` */
static inline void kd3_add8g(kd3_tree* kd3, int a0, int a1, int a2) {
kd3_add_transformed(kd3, kd3_make8g(kd3, a0, a1, a2));
}

/* set `kd3->xradius`. given color `i`, `kd3->xradius[i]` is the square of the
color's uniquely owned neighborhood.
Expand All @@ -185,18 +193,19 @@ void kd3_build_xradius(kd3_tree* kd3);
void kd3_build(kd3_tree* kd3);

/* kd3_init + kd3_add8g for all colors in `gfcm` + kd3_build */
void kd3_init_build(kd3_tree* kd3, void (*transform)(kcolor*),
void kd3_init_build(kd3_tree* kd3, kcolor (*transform)(int, int, int),
const Gif_Colormap* gfcm);

/* return the index of the color in `*kd3` closest to `k`.
if `dist!=NULL`, store the distance from `k` to that index in `*dist`. */
int kd3_closest_transformed(kd3_tree* kd3, const kcolor* k,
unsigned* dist);
int kd3_closest_transformed(kd3_tree* kd3, kcolor k, unsigned* dist);

/* given 8-bit color `a0/a1/a2` (RGB), gamma-transform it, transform it by
`kd3->transform` if necessary, and return the index of the color in
`*kd3` closest to it. */
int kd3_closest8g(kd3_tree* kd3, int a0, int a1, int a2);
/* given 8-bit color `a0/a1/a2` (RGB), transform it by `kd3->transform`
(e.g., apply gamma), and return the index of the color in `*kd3`
closest to the result. */
static inline int kd3_closest8g(kd3_tree* kd3, int a0, int a1, int a2) {
return kd3_closest_transformed(kd3, kd3_make8g(kd3, a0, a1, a2), NULL);
}

/* disable color index `i` in `*kd3`: it will never be returned by
`kd3_closest*` */
Expand Down Expand Up @@ -293,11 +302,11 @@ static inline void sc_clear(scale_color* x) {
x->a[0] = x->a[1] = x->a[2] = x->a[3] = 0;
}

static inline scale_color sc_makekc(const kcolor* k) {
static inline scale_color sc_makekc(kcolor k) {
scale_color sc;
sc.a[0] = k->a[0];
sc.a[1] = k->a[1];
sc.a[2] = k->a[2];
sc.a[0] = k.a[0];
sc.a[1] = k.a[1];
sc.a[2] = k.a[2];
sc.a[3] = KC_MAX;
return sc;
}
Expand Down
Loading

0 comments on commit 233036b

Please sign in to comment.