Mercurial > hg > xemacs-beta
diff src/imgproc.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | b39c14581166 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/imgproc.c Mon Aug 13 11:28:15 2007 +0200 @@ -0,0 +1,564 @@ +/* Image processing functions + Copyright (C) 1998 Jareth Hein + +This file is a part of XEmacs + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +/* Original author: Jareth Hein */ + +/* Parts of this file are based on code from Sam Leffler's tiff library, + with the original copyright displayed here: + + Copyright (c) 1988-1997 Sam Leffler + Copyright (c) 1991-1997 Silicon Graphics, Inc. + + Permission to use, copy, modify, distribute, and sell this software and + its documentation for any purpose is hereby granted without fee, provided + that (i) the above copyright notices and this permission notice appear in + all copies of the software and related documentation, and (ii) the names of + Sam Leffler and Silicon Graphics may not be used in any advertising or + publicity relating to the software without the specific, prior written + permission of Sam Leffler and Silicon Graphics. */ + +/* Quantizing code based off of the paper + Color Image Quantization for Frame Buffer Display, Paul Heckbert, + Siggraph '82 proceedings, pp. 297-307 */ + +#include <config.h> +#include "lisp.h" +#include "imgproc.h" + +static void +get_histogram(quant_table *qt, unsigned char *pic, + int width, int height, Colorbox* box) +{ + register unsigned char *inptr; + register int red, green, blue; + register unsigned int j, i; + + box->rmin = box->gmin = box->bmin = 999; + box->rmax = box->gmax = box->bmax = -1; + box->total = width * height; + + inptr = pic; + for (i = 0; i < height; i++) + { + for (j = width; j-- > 0;) + { + red = *inptr++ >> COLOR_SHIFT; + green = *inptr++ >> COLOR_SHIFT; + blue = *inptr++ >> COLOR_SHIFT; + if (red < box->rmin) + box->rmin = red; + if (red > box->rmax) + box->rmax = red; + if (green < box->gmin) + box->gmin = green; + if (green > box->gmax) + box->gmax = green; + if (blue < box->bmin) + box->bmin = blue; + if (blue > box->bmax) + box->bmax = blue; + qt->histogram[red][green][blue]++; + } + } +} + +static Colorbox * +largest_box(quant_table *qt) +{ + register Colorbox *p, *b; + register int size; + + b = NULL; + size = -1; + for (p = qt->usedboxes; p != NULL; p = p->next) + if ((p->rmax > p->rmin || p->gmax > p->gmin || + p->bmax > p->bmin) && p->total > size) + size = (b = p)->total; + return (b); +} + +static void +shrinkbox(quant_table *qt, Colorbox* box) +{ + register int *histp, ir, ig, ib; + + if (box->rmax > box->rmin) + { + for (ir = box->rmin; ir <= box->rmax; ++ir) + for (ig = box->gmin; ig <= box->gmax; ++ig) + { + histp = &(qt->histogram[ir][ig][box->bmin]); + for (ib = box->bmin; ib <= box->bmax; ++ib) + if (*histp++ != 0) + { + box->rmin = ir; + goto have_rmin; + } + } + have_rmin: + if (box->rmax > box->rmin) + for (ir = box->rmax; ir >= box->rmin; --ir) + for (ig = box->gmin; ig <= box->gmax; ++ig) + { + histp = &(qt->histogram[ir][ig][box->bmin]); + ib = box->bmin; + for (; ib <= box->bmax; ++ib) + if (*histp++ != 0) + { + box->rmax = ir; + goto have_rmax; + } + } + } + have_rmax: + if (box->gmax > box->gmin) + { + for (ig = box->gmin; ig <= box->gmax; ++ig) + for (ir = box->rmin; ir <= box->rmax; ++ir) + { + histp = &(qt->histogram[ir][ig][box->bmin]); + for (ib = box->bmin; ib <= box->bmax; ++ib) + if (*histp++ != 0) + { + box->gmin = ig; + goto have_gmin; + } + } + have_gmin: + if (box->gmax > box->gmin) + for (ig = box->gmax; ig >= box->gmin; --ig) + for (ir = box->rmin; ir <= box->rmax; ++ir) + { + histp = &(qt->histogram[ir][ig][box->bmin]); + ib = box->bmin; + for (; ib <= box->bmax; ++ib) + if (*histp++ != 0) + { + box->gmax = ig; + goto have_gmax; + } + } + } + have_gmax: + if (box->bmax > box->bmin) + { + for (ib = box->bmin; ib <= box->bmax; ++ib) + for (ir = box->rmin; ir <= box->rmax; ++ir) + { + histp = &(qt->histogram[ir][box->gmin][ib]); + for (ig = box->gmin; ig <= box->gmax; ++ig) + { + if (*histp != 0) + { + box->bmin = ib; + goto have_bmin; + } + histp += B_LEN; + } + } + have_bmin: + if (box->bmax > box->bmin) + for (ib = box->bmax; ib >= box->bmin; --ib) + for (ir = box->rmin; ir <= box->rmax; ++ir) + { + histp = &(qt->histogram[ir][box->gmin][ib]); + ig = box->gmin; + for (; ig <= box->gmax; ++ig) + { + if (*histp != 0) + { + box->bmax = ib; + goto have_bmax; + } + histp += B_LEN; + } + } + } + have_bmax: + ; +} + +static void +splitbox(quant_table *qt, Colorbox* ptr) +{ + int hist2[B_LEN]; + int first = 0, last = 0; + register Colorbox *new; + register int *iptr, *histp; + register int i, j; + register int ir,ig,ib; + register int sum, sum1, sum2; + enum { RED, GREEN, BLUE } axis; + + /* + * See which axis is the largest, do a histogram along that + * axis. Split at median point. Contract both new boxes to + * fit points and return + */ + i = ptr->rmax - ptr->rmin; + if (i >= ptr->gmax - ptr->gmin && i >= ptr->bmax - ptr->bmin) + axis = RED; + else if (ptr->gmax - ptr->gmin >= ptr->bmax - ptr->bmin) + axis = GREEN; + else + axis = BLUE; + /* get histogram along longest axis */ + switch (axis) + { + case RED: + histp = &hist2[ptr->rmin]; + for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) + { + *histp = 0; + for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) + { + iptr = &(qt->histogram[ir][ig][ptr->bmin]); + for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) + *histp += *iptr++; + } + histp++; + } + first = ptr->rmin; + last = ptr->rmax; + break; + case GREEN: + histp = &hist2[ptr->gmin]; + for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) + { + *histp = 0; + for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) + { + iptr = &(qt->histogram[ir][ig][ptr->bmin]); + for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) + *histp += *iptr++; + } + histp++; + } + first = ptr->gmin; + last = ptr->gmax; + break; + case BLUE: + histp = &hist2[ptr->bmin]; + for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) + { + *histp = 0; + for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) + { + iptr = &(qt->histogram[ir][ptr->gmin][ib]); + for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) + { + *histp += *iptr; + iptr += B_LEN; + } + } + histp++; + } + first = ptr->bmin; + last = ptr->bmax; + break; + } + /* find median point */ + sum2 = ptr->total / 2; + histp = &hist2[first]; + sum = 0; + for (i = first; i <= last && (sum += *histp++) < sum2; ++i) + ; + if (i == first) + i++; + + /* Create new box, re-allocate points */ + new = qt->freeboxes; + qt->freeboxes = new->next; + if (qt->freeboxes) + qt->freeboxes->prev = NULL; + if (qt->usedboxes) + qt->usedboxes->prev = new; + new->next = qt->usedboxes; + qt->usedboxes = new; + + histp = &hist2[first]; + for (sum1 = 0, j = first; j < i; j++) + sum1 += *histp++; + for (sum2 = 0, j = i; j <= last; j++) + sum2 += *histp++; + new->total = sum1; + ptr->total = sum2; + + new->rmin = ptr->rmin; + new->rmax = ptr->rmax; + new->gmin = ptr->gmin; + new->gmax = ptr->gmax; + new->bmin = ptr->bmin; + new->bmax = ptr->bmax; + switch (axis) + { + case RED: + new->rmax = i-1; + ptr->rmin = i; + break; + case GREEN: + new->gmax = i-1; + ptr->gmin = i; + break; + case BLUE: + new->bmax = i-1; + ptr->bmin = i; + break; + } + shrinkbox (qt, new); + shrinkbox (qt, ptr); +} + + +static C_cell * +create_colorcell(quant_table *qt, int num_colors, int red, int green, int blue) +{ + register int ir, ig, ib, i; + register C_cell *ptr; + int mindist, next_n; + register int tmp, dist, n; + + ir = red >> (COLOR_DEPTH-C_DEPTH); + ig = green >> (COLOR_DEPTH-C_DEPTH); + ib = blue >> (COLOR_DEPTH-C_DEPTH); + ptr = (C_cell *)xmalloc(sizeof (C_cell)); + *(qt->ColorCells + ir*C_LEN*C_LEN + ig*C_LEN + ib) = ptr; + ptr->num_ents = 0; + + /* + * Step 1: find all colors inside this cell, while we're at + * it, find distance of centermost point to furthest corner + */ + mindist = 99999999; + for (i = 0; i < num_colors; ++i) + { + if (qt->rm[i]>>(COLOR_DEPTH-C_DEPTH) != ir || + qt->gm[i]>>(COLOR_DEPTH-C_DEPTH) != ig || + qt->bm[i]>>(COLOR_DEPTH-C_DEPTH) != ib) + continue; + ptr->entries[ptr->num_ents][0] = i; + ptr->entries[ptr->num_ents][1] = 0; + ++ptr->num_ents; + tmp = qt->rm[i] - red; + if (tmp < (MAX_COLOR/C_LEN/2)) + tmp = MAX_COLOR/C_LEN-1 - tmp; + dist = tmp*tmp; + tmp = qt->gm[i] - green; + if (tmp < (MAX_COLOR/C_LEN/2)) + tmp = MAX_COLOR/C_LEN-1 - tmp; + dist += tmp*tmp; + tmp = qt->bm[i] - blue; + if (tmp < (MAX_COLOR/C_LEN/2)) + tmp = MAX_COLOR/C_LEN-1 - tmp; + dist += tmp*tmp; + if (dist < mindist) + mindist = dist; + } + + /* + * Step 3: find all points within that distance to cell. + */ + for (i = 0; i < num_colors; ++i) + { + if (qt->rm[i] >> (COLOR_DEPTH-C_DEPTH) == ir && + qt->gm[i] >> (COLOR_DEPTH-C_DEPTH) == ig && + qt->bm[i] >> (COLOR_DEPTH-C_DEPTH) == ib) + continue; + dist = 0; + if ((tmp = red - qt->rm[i]) > 0 || + (tmp = qt->rm[i] - (red + MAX_COLOR/C_LEN-1)) > 0 ) + dist += tmp*tmp; + if ((tmp = green - qt->gm[i]) > 0 || + (tmp = qt->gm[i] - (green + MAX_COLOR/C_LEN-1)) > 0 ) + dist += tmp*tmp; + if ((tmp = blue - qt->bm[i]) > 0 || + (tmp = qt->bm[i] - (blue + MAX_COLOR/C_LEN-1)) > 0 ) + dist += tmp*tmp; + if (dist < mindist) + { + ptr->entries[ptr->num_ents][0] = i; + ptr->entries[ptr->num_ents][1] = dist; + ++ptr->num_ents; + } + } + + /* + * Sort color cells by distance, use cheap exchange sort + */ + for (n = ptr->num_ents - 1; n > 0; n = next_n) + { + next_n = 0; + for (i = 0; i < n; ++i) + if (ptr->entries[i][1] > ptr->entries[i+1][1]) + { + tmp = ptr->entries[i][0]; + ptr->entries[i][0] = ptr->entries[i+1][0]; + ptr->entries[i+1][0] = tmp; + tmp = ptr->entries[i][1]; + ptr->entries[i][1] = ptr->entries[i+1][1]; + ptr->entries[i+1][1] = tmp; + next_n = i; + } + } + return (ptr); +} + +static int +map_colortable(quant_table *qt, int num_colors) +{ + register int *histp = &(qt->histogram[0][0][0]); + register C_cell *cell; + register int j, tmp, d2, dist; + int ir, ig, ib, i; + + for (ir = 0; ir < B_LEN; ++ir) + for (ig = 0; ig < B_LEN; ++ig) + for (ib = 0; ib < B_LEN; ++ib, histp++) + { + if (*histp == 0) + { + *histp = -1; + continue; + } + cell = *(qt->ColorCells + + (((ir>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2) + + ((ig>>(B_DEPTH-C_DEPTH)) << C_DEPTH) + + (ib>>(B_DEPTH-C_DEPTH)))); + if (cell == NULL ) + cell = create_colorcell (qt, num_colors, + ir << COLOR_SHIFT, + ig << COLOR_SHIFT, + ib << COLOR_SHIFT); + if (cell == NULL) /* memory exhausted! punt! */ + return -1; + dist = 9999999; + for (i = 0; i < cell->num_ents && + dist > cell->entries[i][1]; ++i) + { + j = cell->entries[i][0]; + d2 = qt->rm[j] - (ir << COLOR_SHIFT); + d2 *= d2; + tmp = qt->gm[j] - (ig << COLOR_SHIFT); + d2 += tmp*tmp; + tmp = qt->bm[j] - (ib << COLOR_SHIFT); + d2 += tmp*tmp; + if (d2 < dist) + { + dist = d2; + *histp = j; + } + } + } + return 0; +} + +quant_table * +build_EImage_quantable(unsigned char *eimage, int width, int height, int num_colors) +{ + quant_table *qt; + Colorbox *box_list, *ptr; + int i,res; + + qt = (quant_table*)xmalloc_and_zero (sizeof(quant_table)); + if (qt == NULL) return NULL; + + assert (num_colors < 257 && num_colors > 2); + /* + * STEP 1: create empty boxes + */ + qt->usedboxes = NULL; + box_list = qt->freeboxes = (Colorbox *)xmalloc (num_colors*sizeof (Colorbox)); + qt->freeboxes[0].next = &(qt->freeboxes[1]); + qt->freeboxes[0].prev = NULL; + for (i = 1; i < num_colors-1; ++i) + { + qt->freeboxes[i].next = &(qt->freeboxes[i+1]); + qt->freeboxes[i].prev = &(qt->freeboxes[i-1]); + } + qt->freeboxes[num_colors-1].next = NULL; + qt->freeboxes[num_colors-1].prev = &(qt->freeboxes[num_colors-2]); + + /* + * STEP 2: get histogram, initialize first box + */ + ptr = qt->freeboxes; + qt->freeboxes = ptr->next; + if (qt->freeboxes) + qt->freeboxes->prev = NULL; + ptr->next = qt->usedboxes; + qt->usedboxes = ptr; + if (ptr->next) + ptr->next->prev = ptr; + get_histogram (qt, eimage, width, height, ptr); + + /* + * STEP 3: continually subdivide boxes until no more free + * boxes remain or until all colors assigned. + */ + while (qt->freeboxes != NULL) + { + ptr = largest_box(qt); + if (ptr != NULL) + splitbox (qt, ptr); + else + qt->freeboxes = NULL; + } + + /* + * STEP 4: assign colors to all boxes + */ + for (i = 0, ptr = qt->usedboxes; ptr != NULL; ++i, ptr = ptr->next) + { + qt->rm[i] = ((ptr->rmin + ptr->rmax) << COLOR_SHIFT) / 2; + qt->gm[i] = ((ptr->gmin + ptr->gmax) << COLOR_SHIFT) / 2; + qt->bm[i] = ((ptr->bmin + ptr->bmax) << COLOR_SHIFT) / 2; + qt->um[i] = ptr->total; + } + qt->num_active_colors = i; + + /* We're done with the boxes now */ + xfree (box_list); + qt->freeboxes = qt->usedboxes = NULL; + + /* + * STEP 5: scan histogram and map all values to closest color + */ + /* 5a: create cell list as described in Heckbert */ + qt->ColorCells = (C_cell **)xmalloc_and_zero (C_LEN*C_LEN*C_LEN*sizeof (C_cell*)); + /* 5b: create mapping from truncated pixel space to color + table entries */ + res = map_colortable (qt, num_colors); + + /* 5c: done with ColorCells */ + for (i = 0; i < C_LEN*C_LEN*C_LEN; i++) + if (qt->ColorCells[i]) xfree (qt->ColorCells[i]); + xfree (qt->ColorCells); + + if (res) + { + /* we failed in memory allocation, so clean up an leave */ + xfree(qt); + return NULL; + } + + return qt; +}