currently only used by gif.c, but maybe we'll support producing
8-bit (or bigger indexed) png files at some point
*/
-#include "image.h"
+#include "imager.h"
+#include "imageri.h"
+static void makemap_webmap(i_quantize *);
static void makemap_addi(i_quantize *, i_img **imgs, int count);
static void makemap_mediancut(i_quantize *, i_img **imgs, int count);
+static void makemap_mono(i_quantize *);
+static void makemap_gray(i_quantize *, int step);
+
+static int makemap_palette(i_quantize *, i_img **imgs, int count);
static
void
handle multiple colour maps.
*/
+/*
+=item i_quant_makemap(C<quant>, C<imgs>, C<count>)
+
+=category Image quantization
+
+Analyzes the C<count> images in C<imgs> according to the rules in
+C<quant> to build a color map (optimal or not depending on
+C<< quant->make_colors >>).
+
+=cut
+*/
+
void
-quant_makemap(i_quantize *quant, i_img **imgs, int count) {
+i_quant_makemap(i_quantize *quant, i_img **imgs, int count) {
if (quant->translate == pt_giflib) {
/* giflib does it's own color table generation */
/* use user's specified map */
break;
case mc_web_map:
- {
- int r, g, b;
- int i = 0;
- for (r = 0; r < 256; r+=0x33)
- for (g = 0; g < 256; g+=0x33)
- for (b = 0; b < 256; b += 0x33)
- setcol(quant->mc_colors+i++, r, g, b, 0);
- quant->mc_count = i;
- }
+ makemap_webmap(quant);
break;
case mc_median_cut:
makemap_mediancut(quant, imgs, count);
break;
+ case mc_mono:
+ makemap_mono(quant);
+ break;
+
+ case mc_gray:
+ makemap_gray(quant, 1);
+ break;
+
+ case mc_gray4:
+ makemap_gray(quant, 85);
+ break;
+
+ case mc_gray16:
+ makemap_gray(quant, 17);
+ break;
+
case mc_addi:
default:
makemap_addi(quant, imgs, count);
}
}
-#ifdef HAVE_LIBGIF
-static void translate_giflib(i_quantize *, i_img *, i_palidx *);
-#endif
static void translate_closest(i_quantize *, i_img *, i_palidx *);
static void translate_errdiff(i_quantize *, i_img *, i_palidx *);
static void translate_addi(i_quantize *, i_img *, i_palidx *);
-/* Quantize the image given the palette in quant.
+/*
+=item i_quant_translate(C<quant>, C<img>)
+
+=category Image quantization
+
+Quantize the image given the palette in C<quant>.
+
+On success returns a pointer to a memory block of C<< img->xsize *
+img->ysize >> C<i_palidx> entries.
- The giflib quantizer ignores the palette.
+On failure returns NULL.
+
+You should call myfree() on the returned block when you're done with
+it.
+
+This function will fail if the supplied palette contains no colors.
+
+=cut
*/
-i_palidx *quant_translate(i_quantize *quant, i_img *img) {
+i_palidx *
+i_quant_translate(i_quantize *quant, i_img *img) {
i_palidx *result;
+ size_t bytes;
+
mm_log((1, "quant_translate(quant %p, img %p)\n", quant, img));
- result = mymalloc(img->xsize * img->ysize);
+ /* there must be at least one color in the paletted (though even that
+ isn't very useful */
+ if (quant->mc_count == 0) {
+ i_push_error(0, "no colors available for translation");
+ return NULL;
+ }
+
+ bytes = img->xsize * img->ysize;
+ if (bytes / img->ysize != img->xsize) {
+ i_push_error(0, "integer overflow calculating memory allocation");
+ return NULL;
+ }
+ result = mymalloc(bytes);
switch (quant->translate) {
case pt_closest:
return result;
}
-#ifdef HAVE_LIBGIF_THIS_NOT_USED
-
-#include "gif_lib.h"
-
-#define GET_RGB(im, x, y, ri, gi, bi, col) \
- i_gpix((im),(x),(y),&(col)); (ri)=(col).rgb.r; \
- if((im)->channels==3) { (bi)=(col).rgb.b; (gi)=(col).rgb.g; }
-
-static int
-quant_replicate(i_img *im, i_palidx *output, i_quantize *quant);
-
-
-/* Use the gif_lib quantization functions to quantize the image */
-static void translate_giflib(i_quantize *quant, i_img *img, i_palidx *out) {
- int x,y,ColorMapSize,colours_in;
- unsigned long Size;
- int i;
-
- GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL;
- GifByteType *RedP, *GreenP, *BlueP;
- ColorMapObject *OutputColorMap = NULL;
-
- i_color col;
-
- mm_log((1,"translate_giflib(quant %p, img %p, out %p)\n", quant, img, out));
-
- /*if (!(im->channels==1 || im->channels==3)) { fprintf(stderr,"Unable to write gif, improper colorspace.\n"); exit(3); }*/
-
- ColorMapSize = quant->mc_size;
-
- Size = ((long) img->xsize) * img->ysize * sizeof(GifByteType);
-
-
- if ((OutputColorMap = MakeMapObject(ColorMapSize, NULL)) == NULL)
- m_fatal(0,"Failed to allocate memory for Output colormap.");
- /* if ((OutputBuffer = (GifByteType *) mymalloc(im->xsize * im->ysize * sizeof(GifByteType))) == NULL)
- m_fatal(0,"Failed to allocate memory for output buffer.");*/
-
- /* ******************************************************* */
- /* count the number of colours in the image */
- colours_in=i_count_colors(img, OutputColorMap->ColorCount);
-
- if(colours_in != -1) { /* less then the number wanted */
- /* so we copy them over as-is */
- mm_log((2,"image has %d colours, which fits in %d. Copying\n",
- colours_in,ColorMapSize));
- quant_replicate(img, out, quant);
- /* saves the colors, so don't fall through */
- return;
- } else {
-
- mm_log((2,"image has %d colours, more then %d. Quantizing\n",colours_in,ColorMapSize));
-
- if (img->channels >= 3) {
- if ((RedBuffer = (GifByteType *) mymalloc((unsigned int) Size)) == NULL) {
- m_fatal(0,"Failed to allocate memory required, aborted.");
- return;
- }
- if ((GreenBuffer = (GifByteType *) mymalloc((unsigned int) Size)) == NULL) {
- m_fatal(0,"Failed to allocate memory required, aborted.");
- myfree(RedBuffer);
- return;
- }
-
- if ((BlueBuffer = (GifByteType *) mymalloc((unsigned int) Size)) == NULL) {
- m_fatal(0,"Failed to allocate memory required, aborted.");
- myfree(RedBuffer);
- myfree(GreenBuffer);
- return;
- }
-
- RedP = RedBuffer;
- GreenP = GreenBuffer;
- BlueP = BlueBuffer;
-
- for (y=0; y< img->ysize; y++) for (x=0; x < img->xsize; x++) {
- i_gpix(img,x,y,&col);
- *RedP++ = col.rgb.r;
- *GreenP++ = col.rgb.g;
- *BlueP++ = col.rgb.b;
- }
-
- } else {
-
- if ((RedBuffer = (GifByteType *) mymalloc((unsigned int) Size))==NULL) {
- m_fatal(0,"Failed to allocate memory required, aborted.");
- return;
- }
-
- GreenBuffer=BlueBuffer=RedBuffer;
- RedP = RedBuffer;
- for (y=0; y< img->ysize; y++) for (x=0; x < img->xsize; x++) {
- i_gpix(img,x,y,&col);
- *RedP++ = col.rgb.r;
- }
- }
-
- if (QuantizeBuffer(img->xsize, img->ysize, &ColorMapSize, RedBuffer, GreenBuffer, BlueBuffer,
- out, OutputColorMap->Colors) == GIF_ERROR) {
- mm_log((1,"Error in QuantizeBuffer, unable to write image.\n"));
- }
- }
-
- myfree(RedBuffer);
- if (img->channels == 3) { myfree(GreenBuffer); myfree(BlueBuffer); }
-
- /* copy over the color map */
- for (i = 0; i < ColorMapSize; ++i) {
- quant->mc_colors[i].rgb.r = OutputColorMap->Colors[i].Red;
- quant->mc_colors[i].rgb.g = OutputColorMap->Colors[i].Green;
- quant->mc_colors[i].rgb.b = OutputColorMap->Colors[i].Blue;
- }
- quant->mc_count = ColorMapSize;
-}
-
-static
-int
-quant_replicate(i_img *im, GifByteType *output, i_quantize *quant) {
- int x, y, alloced, r, g=0, b=0, idx ;
- i_color col;
-
- alloced=0;
- for(y=0; y<im->ysize; y++) {
- for(x=0; x<im->xsize; x++) {
-
- GET_RGB(im, x,y, r,g,b, col);
-
- for(idx=0; idx<alloced; idx++) { /* linear search for an index */
- if(quant->mc_colors[idx].rgb.r==r &&
- quant->mc_colors[idx].rgb.g==g &&
- quant->mc_colors[idx].rgb.b==b) {
- break;
- }
- }
-
- if(idx >= alloced) { /* if we haven't already, we */
- idx=alloced++; /* add the colour to the map */
- if(quant->mc_size < alloced) {
- mm_log((1,"Tried to allocate more then %d colours.\n",
- quant->mc_size));
- return 0;
- }
- quant->mc_colors[idx].rgb.r=r;
- quant->mc_colors[idx].rgb.g=g;
- quant->mc_colors[idx].rgb.b=b;
- }
- *output=idx; /* fill output buffer */
- output++; /* with colour indexes */
- }
- }
- quant->mc_count = alloced;
- return 1;
-}
-
-#endif
-
static void translate_closest(i_quantize *quant, i_img *img, i_palidx *out) {
quant->perturb = 0;
translate_addi(quant, img, out);
return rand()/(RAND_MAX+1.0);
}
+#ifdef NOTEF
static
int
eucl_d(cvec* cv,i_color *cl) { return PWR2(cv->r-cl->channel[0])+PWR2(cv->g-cl->channel[1])+PWR2(cv->b-cl->channel[2]); }
+#endif
static
int
+ PWR2(cv->b - chans[2]);
}
-static
-int
-ceucl_d(i_color *c1, i_color *c2) { return PWR2(c1->channel[0]-c2->channel[0])+PWR2(c1->channel[1]-c2->channel[1])+PWR2(c1->channel[2]-c2->channel[2]); }
+static int
+ceucl_d(i_color *c1, i_color *c2) {
+return PWR2(c1->channel[0]-c2->channel[0])
+ +PWR2(c1->channel[1]-c2->channel[1])
+ +PWR2(c1->channel[2]-c2->channel[2]);
+}
static const int
gray_samples[] = { 0, 0, 0 };
static void
makemap_addi(i_quantize *quant, i_img **imgs, int count) {
cvec *clr;
- int cnum, i, x, y, bst_idx=0, ld, cd, iter, currhb, img_num;
+ int cnum, i, bst_idx=0, ld, cd, iter, currhb, img_num;
+ i_img_dim x, y;
i_sample_t *val;
float dlt, accerr;
hashbox *hb;
i_mempool mp;
- int maxwidth = 0;
+ i_img_dim maxwidth = 0;
i_sample_t *line;
const int *sample_indices;
+ mm_log((1, "makemap_addi(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n",
+ quant, quant->mc_count, quant->mc_colors, imgs, count));
+
+ if (makemap_palette(quant, imgs, count))
+ return;
+
i_mempool_init(&mp);
clr = i_mempool_alloc(&mp, sizeof(cvec) * quant->mc_size);
}
/* mymalloc doesn't clear memory, so I think we need this */
for (; i < quant->mc_size; ++i) {
+ /*clr[i].r = clr[i].g = clr[i].b = 0;*/
+ clr[i].dr = 0;
+ clr[i].dg = 0;
+ clr[i].db = 0;
clr[i].fixed = 0;
clr[i].mcount = 0;
}
quant->mc_count = cnum;
#endif
+#if 0
+ mm_log((1, "makemap_addi returns - quant.mc_count = %d\n", quant->mc_count));
+ for (i = 0; i < quant->mc_count; ++i)
+ mm_log((5, " map entry %d: (%d, %d, %d)\n", i, clr[i].r, clr[i].g, clr[i].b));
+#endif
+
i_mempool_destroy(&mp);
+
+ mm_log((1, "makemap_addi() - %d colors\n", quant->mc_count));
}
typedef struct {
i_sample_t max[3]; /* maximum for each channel */
i_sample_t width[3]; /* width for each channel */
int start, size; /* beginning and size of the partition */
- int pixels; /* number of pixels represented by this partition */
+ i_img_dim pixels; /* number of pixels represented by this partition */
} medcut_partition;
/*
makemap_mediancut(i_quantize *quant, i_img **imgs, int count) {
quant_color_entry *colors;
i_mempool mp;
- int imgn, x, y, i, ch;
- int max_width;
+ int imgn, i, ch;
+ i_img_dim x, y, max_width;
i_color *line;
int color_count;
- int total_pixels;
+ i_img_dim total_pixels;
medcut_partition *parts;
int part_num;
int in, out;
this isn't terribly efficient, but it should work */
int chan_count;
- /*printf("images %d pal size %d\n", count, quant->mc_size);*/
+ mm_log((1, "makemap_mediancut(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n",
+ quant, quant->mc_count, quant->mc_colors, imgs, count));
+
+ if (makemap_palette(quant, imgs, count))
+ return;
i_mempool_init(&mp);
color_count = 1;
while (color_count < quant->mc_size) {
- int max_index, max_ch; /* index/channel with biggest spread */
+ /* initialized to avoid compiler warnings */
+ int max_index = 0, max_ch = 0; /* index/channel with biggest spread */
int max_size;
medcut_partition *workpart;
int cum_total;
}
/*printf("out %d colors\n", quant->mc_count);*/
i_mempool_destroy(&mp);
+
+ mm_log((1, "makemap_mediancut() - %d colors\n", quant->mc_count));
+}
+
+static void
+makemap_mono(i_quantize *quant) {
+ quant->mc_colors[0].rgba.r = 0;
+ quant->mc_colors[0].rgba.g = 0;
+ quant->mc_colors[0].rgba.b = 0;
+ quant->mc_colors[0].rgba.a = 255;
+ quant->mc_colors[1].rgba.r = 255;
+ quant->mc_colors[1].rgba.g = 255;
+ quant->mc_colors[1].rgba.b = 255;
+ quant->mc_colors[1].rgba.a = 255;
+ quant->mc_count = 2;
+}
+
+static void
+makemap_gray(i_quantize *quant, int step) {
+ int gray = 0;
+ int i = 0;
+
+ while (gray < 256) {
+ setcol(quant->mc_colors+i, gray, gray, gray, 255);
+ ++i;
+ gray += step;
+ }
+ quant->mc_count = i;
+}
+
+static void
+makemap_webmap(i_quantize *quant) {
+ int r, g, b;
+
+ int i = 0;
+ for (r = 0; r < 256; r+=0x33)
+ for (g = 0; g < 256; g+=0x33)
+ for (b = 0; b < 256; b += 0x33)
+ setcol(quant->mc_colors+i++, r, g, b, 255);
+ quant->mc_count = i;
+}
+
+static int
+in_palette(i_color *c, i_quantize *quant, int size) {
+ int i;
+
+ for (i = 0; i < size; ++i) {
+ if (c->channel[0] == quant->mc_colors[i].channel[0]
+ && c->channel[1] == quant->mc_colors[i].channel[1]
+ && c->channel[2] == quant->mc_colors[i].channel[2]) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*
+=item makemap_palette(quant, imgs, count)
+
+Tests if all the given images are paletted and have a common palette,
+if they do it builds that palette.
+
+A possible improvement might be to eliminate unused colors in the
+images palettes.
+
+=cut
+*/
+
+static int
+makemap_palette(i_quantize *quant, i_img **imgs, int count) {
+ int size = quant->mc_count;
+ int i;
+ int imgn;
+ char used[256];
+ int col_count;
+
+ mm_log((1, "makemap_palette(quant %p { mc_count=%d, mc_colors=%p }, imgs %p, count %d)\n",
+ quant, quant->mc_count, quant->mc_colors, imgs, count));
+ /* we try to build a common palette here, if we can manage that, then
+ that's the palette we use */
+ for (imgn = 0; imgn < count; ++imgn) {
+ int eliminate_unused;
+ if (imgs[imgn]->type != i_palette_type) {
+ mm_log((1, "makemap_palette() -> 0 (non-palette image)\n"));
+ return 0;
+ }
+
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
+ &eliminate_unused)) {
+ eliminate_unused = 1;
+ }
+
+ if (eliminate_unused) {
+ i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
+ i_img_dim x, y;
+ memset(used, 0, sizeof(used));
+
+ for (y = 0; y < imgs[imgn]->ysize; ++y) {
+ i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
+ for (x = 0; x < imgs[imgn]->xsize; ++x)
+ used[line[x]] = 1;
+ }
+
+ myfree(line);
+ }
+ else {
+ /* assume all are in use */
+ memset(used, 1, sizeof(used));
+ }
+
+ col_count = i_colorcount(imgs[imgn]);
+ for (i = 0; i < col_count; ++i) {
+ i_color c;
+
+ i_getcolors(imgs[imgn], i, &c, 1);
+ if (used[i]) {
+ if (in_palette(&c, quant, size) < 0) {
+ if (size < quant->mc_size) {
+ quant->mc_colors[size++] = c;
+ }
+ else {
+ mm_log((1, "makemap_palette() -> 0 (too many colors)\n"));
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ mm_log((1, "makemap_palette() -> 1 (%d total colors)\n", size));
+ quant->mc_count = size;
+
+ return 1;
}
#define pboxjump 32
/*#define IM_CFRAND2DIST*/
#endif
+/* return true if the color map contains only grays */
+static int
+is_gray_map(const i_quantize *quant) {
+ int i;
+
+ for (i = 0; i < quant->mc_count; ++i) {
+ if (quant->mc_colors[i].rgb.r != quant->mc_colors[i].rgb.g
+ || quant->mc_colors[i].rgb.r != quant->mc_colors[i].rgb.b) {
+ mm_log((1, " not a gray map\n"));
+ return 0;
+ }
+ }
+
+ mm_log((1, " is a gray map\n"));
+ return 1;
+}
+
#ifdef IM_CFHASHBOX
/* The original version I wrote for this used the sort.
welcome.
*/
static void hbsetup(i_quantize *quant, hashbox *hb) {
- long *dists, mind, maxd, cd;
+ long *dists, mind, maxd;
int cr, cb, cg, hbnum, i;
i_color cenc;
#ifdef HB_SORT
#endif
static void translate_addi(i_quantize *quant, i_img *img, i_palidx *out) {
- int x, y, i, k, bst_idx;
+ i_img_dim x, y, k;
+ int i, bst_idx = 0;
i_color val;
int pixdev = quant->perturb;
CF_VARS;
int mapw, maph, mapo;
int i;
errdiff_t *err;
- int errw;
+ i_img_dim errw;
int difftotal;
- int x, y, dx, dy;
- int minr, maxr, ming, maxg, minb, maxb, cr, cg, cb;
- i_color find;
- int bst_idx;
+ i_img_dim x, y, dx, dy;
+ int bst_idx = 0;
+ int is_gray = is_gray_map(quant);
CF_VARS;
if ((quant->errdiff & ed_mask) == ed_custom) {
for (y = 0; y < img->ysize; ++y) {
for (x = 0; x < img->xsize; ++x) {
i_color val;
- long ld, cd;
errdiff_t perr;
i_gpix(img, x, y, &val);
if (img->channels < 3) {
val.channel[1] = val.channel[2] = val.channel[0];
}
+ else if (is_gray) {
+ int gray = 0.5 + color_to_grey(&val);
+ val.channel[0] = val.channel[1] = val.channel[2] = gray;
+ }
perr = err[x+mapo];
perr.r = perr.r < 0 ? -((-perr.r)/difftotal) : perr.r/difftotal;
perr.g = perr.g < 0 ? -((-perr.g)/difftotal) : perr.g/difftotal;
static void prescan(i_img **imgs,int count, int cnum, cvec *clr, i_sample_t *line) {
- int i,k,j,x,y;
+ int i,k,j;
+ i_img_dim x,y;
i_sample_t *val;
const int *chans;
void
cr_hashindex(cvec clr[256],int cnum,hashbox hb[512]) {
- int bx,mind,cd,cumcnt,bst_idx,i;
+ int bx,mind,cd,cumcnt,i;
/* printf("indexing... \n");*/
cumcnt=0;
mind=196608;
for(i=0; i<cnum; i++) {
cd = maxdist(bx,&clr[i]);
- if (cd < mind) { mind=cd; bst_idx=i; }
+ if (cd < mind) { mind=cd; }
}
hb[bx].cnt=0;
bbox(boxnum,&r0,&r1,&g0,&g1,&b0,&b1);
- mr=max(abs(b-b0),abs(b-b1));
- mg=max(abs(g-g0),abs(g-g1));
- mb=max(abs(r-r0),abs(r-r1));
+ mr=i_max(abs(b-b0),abs(b-b1));
+ mg=i_max(abs(g-g0),abs(g-g1));
+ mb=i_max(abs(r-r0),abs(r-r1));
return PWR2(mr)+PWR2(mg)+PWR2(mb);
}
if (r0<=r && r<=r1 && g0<=g && g<=g1 && b0<=b && b<=b1) return 0;
- mr=min(abs(b-b0),abs(b-b1));
- mg=min(abs(g-g0),abs(g-g1));
- mb=min(abs(r-r0),abs(r-r1));
+ mr=i_min(abs(b-b0),abs(b-b1));
+ mg=i_min(abs(g-g0),abs(g-g1));
+ mb=i_min(abs(r-r0),abs(r-r1));
mr=PWR2(mr);
mg=PWR2(mg);
static void transparent_errdiff(i_quantize *, i_palidx *, i_img *, i_palidx);
static void transparent_ordered(i_quantize *, i_palidx *, i_img *, i_palidx);
-void quant_transparent(i_quantize *quant, i_palidx *data, i_img *img,
+/*
+=item i_quant_transparent(C<quant>, C<data>, C<img>, C<trans_index>)
+
+=category Image quantization
+
+Dither the alpha channel on C<img> into the palette indexes in
+C<data>. Pixels to be transparent are replaced with C<trans_pixel>.
+
+The method used depends on the tr_* members of C<quant>.
+
+=cut
+*/
+
+void
+i_quant_transparent(i_quantize *quant, i_palidx *data, i_img *img,
i_palidx trans_index)
{
switch (quant->transp) {
transparent_threshold(i_quantize *quant, i_palidx *data, i_img *img,
i_palidx trans_index)
{
- int x, y;
+ i_img_dim x, y;
i_sample_t *line = mymalloc(img->xsize * sizeof(i_sample_t));
int trans_chan = img->channels > 2 ? 3 : 1;
int mapw, maph, mapo;
int errw, *err, *errp;
int difftotal, out, error;
- int x, y, dx, dy, i;
+ i_img_dim x, y, dx, dy;
+ int i;
i_sample_t *line;
int trans_chan = img->channels > 2 ? 3 : 1;
}
/* builtin ordered dither maps */
-unsigned char orddith_maps[][64] =
+static unsigned char
+orddith_maps[][64] =
{
{ /* random
this is purely random - it's pretty awful
i_palidx trans_index)
{
unsigned char *spot;
- int x, y;
+ i_img_dim x, y;
i_sample_t *line;
int trans_chan = img->channels > 2 ? 3 : 1;
if (quant->tr_orddith == od_custom)