Skip to content

Commit

Permalink
Cut time to optimize an image with many colors in half.
Browse files Browse the repository at this point in the history
O(n^2) -> O(n).
  • Loading branch information
kohler committed Jun 30, 2014
1 parent 4f030c1 commit cdd7f23
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 37 deletions.
11 changes: 10 additions & 1 deletion src/kcolor.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ static inline kcolor kc_make8g(int a0, int a1, int a2) {
return x;
}

/* return the kcolor representation of `*gfc` (no gamma transformation) */
static inline kcolor kc_makegfc(const Gif_Color* gfc) {
kcolor kc;
kc.a[0] = (gfc->gfc_red << 8) + gfc->gfc_red;
kc.a[1] = (gfc->gfc_green << 8) + gfc->gfc_green;
kc.a[2] = (gfc->gfc_blue << 8) + gfc->gfc_blue;
return kc;
}

/* return the gamma transformation of `*gfc` */
static inline kcolor kc_makegfcg(const Gif_Color* gfc) {
return kc_make8g(gfc->gfc_red, gfc->gfc_green, gfc->gfc_blue);
Expand Down Expand Up @@ -208,7 +217,7 @@ typedef struct kchist {
void kchist_init(kchist* kch);
void kchist_cleanup(kchist* kch);
void kchist_make(kchist* kch, Gif_Stream* gfs, uint32_t* ntransp);
void kchist_add(kchist* kch, kcolor color, kchist_count_t count);
kchistitem* kchist_add(kchist* kch, kcolor color, kchist_count_t count);
void kchist_compress(kchist* kch);


Expand Down
58 changes: 30 additions & 28 deletions src/optimize.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <config.h>
#include "gifsicle.h"
#include "kcolor.h"
#include <assert.h>
#include <string.h>

Expand Down Expand Up @@ -43,6 +44,8 @@ static int screen_height;

/* Colormap containing all colors in the image. May have >256 colors */
static Gif_Colormap *all_colormap;
/* Histogram so we can find colors quickly */
static kchist all_colormap_hist;

/* The old global colormap, or a fake one we created if necessary */
static Gif_Colormap *in_global_map;
Expand Down Expand Up @@ -86,36 +89,32 @@ delete_opt_data(Gif_OptData *od)
}


/* colormap_combine: Ensure that each color in 'src' is represented in 'dst'.
For each color 'i' in 'src', src->col[i].pixel == some j so that
GIF_COLOREQ(&src->col[i], &dst->col[j]). dst->col[0] is reserved for
transparency; no source color will be mapped to it. */
/* all_colormap_add: Ensure that each color in 'src' is represented in
'all_colormap'. For each color 'i' in 'src', src->col[i].pixel == some j
so that GIF_COLOREQ(&src->col[i], &all_colormap->col[j]).
all_colormap->col[0] is reserved for transparency; no source color will
be mapped to it. */

void
colormap_combine(Gif_Colormap *dst, Gif_Colormap *src)
{
Gif_Color *src_col, *dst_col;
int i, j;
static void all_colormap_add(const Gif_Colormap* src) {
int i;

/* expand dst->col if necessary. This might change dst->col */
if (dst->ncol + src->ncol >= dst->capacity) {
dst->capacity *= 2;
Gif_ReArray(dst->col, Gif_Color, dst->capacity);
}
/* expand dst->col if necessary. This might change dst->col */
if (all_colormap->ncol + src->ncol >= all_colormap->capacity) {
all_colormap->capacity *= 2;
Gif_ReArray(all_colormap->col, Gif_Color, all_colormap->capacity);
}

src_col = src->col;
dst_col = dst->col;
for (i = 0; i < src->ncol; i++, src_col++) {
for (j = 1; j < dst->ncol; j++) {
if (GIF_COLOREQ(src_col, &dst_col[j]))
goto found;
for (i = 1; i < src->ncol; ++i) {
kchistitem* khi = kchist_add(&all_colormap_hist,
kc_makegfc(&src->col[i]), 0);
if (!khi->count) {
all_colormap->col[all_colormap->ncol] = src->col[i];
all_colormap->col[all_colormap->ncol].pixel = 0;
khi->count = all_colormap->ncol;
++all_colormap->ncol;
}
src->col[i].pixel = khi->count;
}
dst_col[j] = *src_col;
dst_col[j].pixel = 0;
dst->ncol++;
found:
src_col->pixel = j;
}
}


Expand Down Expand Up @@ -367,17 +366,20 @@ initialize_optimizer(Gif_Stream *gfs)
{
int any_globals = 0;
int first_transparent = -1;

kchist_init(&all_colormap_hist);
for (i = 0; i < gfs->nimages; i++) {
Gif_Image *gfi = gfs->images[i];
if (gfi->local)
colormap_combine(all_colormap, gfi->local);
all_colormap_add(gfi->local);
else
any_globals = 1;
if (gfi->transparent >= 0 && first_transparent < 0)
first_transparent = i;
}
if (any_globals)
colormap_combine(all_colormap, in_global_map);
all_colormap_add(in_global_map);
kchist_cleanup(&all_colormap_hist);

/* try and maintain transparency's pixel value */
if (first_transparent >= 0) {
Expand Down
21 changes: 13 additions & 8 deletions src/quantize.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,10 @@ void kchist_cleanup(kchist* kch) {
kch->h = NULL;
}

void kchist_add(kchist* kch, kcolor k, kchist_count_t count) {
kchistitem* kchist_add(kchist* kch, kcolor k, kchist_count_t count) {
unsigned hash1, hash2 = 0;
kacolor ka;
kchistitem *khi;
ka.k = k;
ka.a[3] = 0;

Expand All @@ -206,8 +207,11 @@ void kchist_add(kchist* kch, kcolor k, kchist_count_t count) {
| ((ka.a[1] & 0x7FE0) << 5)
| ((ka.a[2] & 0x7FE0) >> 5)) % kch->capacity;

while (kch->h[hash1].count
&& memcmp(&kch->h[hash1].ka, &ka, sizeof(ka)) != 0) {
while (1) {
khi = &kch->h[hash1];
if (!khi->count
|| memcmp(&khi->ka, &ka, sizeof(ka)) == 0)
break;
if (!hash2) {
hash2 = (((ka.a[0] & 0x03FF) << 20)
| ((ka.a[1] & 0x03FF) << 10)
Expand All @@ -219,13 +223,14 @@ void kchist_add(kchist* kch, kcolor k, kchist_count_t count) {
hash1 -= kch->capacity;
}

if (!kch->h[hash1].count) {
kch->h[hash1].ka = ka;
if (!khi->count) {
khi->ka = ka;
++kch->n;
}
kch->h[hash1].count += count;
if (kch->h[hash1].count < count)
kch->h[hash1].count = (kchist_count_t) -1;
khi->count += count;
if (khi->count < count)
khi->count = (kchist_count_t) -1;
return khi;
}

static void kchist_grow(kchist* kch) {
Expand Down

0 comments on commit cdd7f23

Please sign in to comment.