home *** CD-ROM | disk | FTP | other *** search
- /*
- * xv24to8.c - contains the 24-to-8-bit Conv24to8 procedure
- *
- * The Conv24to8 procedure takes a pointer to a 24-bit image (loaded
- * previously). The image will be a w * h * 3 byte array of
- * bytes. The image will be arranged with 3 bytes per pixel (in order
- * R, G, and B), pixel 0 at the top left corner. (As normal.)
- * The procedure also takes a maximum number of colors to use (numcols)
- *
- * The Conv24to8 procedure will set up the following: it will allocate and
- * create 'pic', a pWIDE*pHIGH 8-bit picture. it will set up pWIDE and pHIGH.
- * it will load up the r[], g[], and b[] colormap arrays. it will NOT
- * calculate numcols, since the cmap sort procedure has to be called anyway
- *
- * Conv24to8 returns '0' if successful, '1' if it failed (presumably on a
- * malloc())
- *
- * The 'slow' code is based on Heckbert's Median Cut algorithm.
- *
- * contains:
- * Cont24to8()
- * Init24to8()
- */
-
- /*
- * Copyright 1989, 1990, 1991, 1992 by John Bradley and
- * The University of Pennsylvania
- *
- * Permission to use, copy, and distribute for non-commercial purposes,
- * is hereby granted without fee, providing that the above copyright
- * notice appear in all copies and that both the copyright notice and this
- * permission notice appear in supporting documentation.
- *
- * The software may be modified for your own purposes, but modified versions
- * may not be distributed.
- *
- * This software is provided "as is" without any expressed or implied warranty.
- *
- * The author may be contacted via:
- * US Mail: John Bradley
- * GRASP Lab, Room 301C
- * 3401 Walnut St.
- * Philadelphia, PA 19104
- *
- * Phone: (215) 898-8813
- * EMail: bradley@cis.upenn.edu
- */
-
-
- /*
- * Portions Copyright (C) 1989, 1991 by Jef Poskanzer. See copyright notice
- * at beginning of relevant code.
- */
-
-
- #include "xvimage.h"
-
- #define MAX_CMAP_SIZE 256
- #define COLOR_DEPTH 8
- #define MAX_COLOR 256
- #define B_DEPTH 5 /* # bits/pixel to use */
- #define B_LEN (1<<B_DEPTH)
- #define C_DEPTH 3
- #define C_LEN (1<<C_DEPTH) /* # cells/color to use */
-
- #define R2FACT 20 /* .300 * .300 * 256 = 23 */
- #define G2FACT 39 /* .586 * .586 * 256 = 88 */
- #define B2FACT 8 /* .114 * .114 * 256 = 3 */
-
-
- typedef struct colorbox {
- struct colorbox *next, *prev;
- int rmin,rmax, gmin,gmax, bmin,bmax;
- int total;
- } CBOX;
-
- typedef struct {
- int num_ents;
- int entries[MAX_CMAP_SIZE][2];
- } CCELL;
-
- static byte *pic24;
- static int num_colors, WIDE, HIGH;
- static int histogram[B_LEN][B_LEN][B_LEN];
-
- CBOX *freeboxes, *usedboxes;
- CCELL **ColorCells;
-
- static void get_histogram();
- static CBOX *largest_box();
- static void splitbox();
- static void shrinkbox();
- static void assign_color();
- static CCELL *create_colorcell();
- static void map_colortable();
- static int quant_fsdither();
- static int Quick24to8();
- static int QuickCheck();
- static int ppmquant();
-
- static int tbl1[512], /* tables used in F-S Dithering */
- tbl3[512], /* contain i/16, 3i/16, 5i/16, 7i/16, */
- tbl5[512], /* (i=-256..255) respectively */
- tbl7[512];
-
-
- /****************************/
- void Init24to8()
- /****************************/
- {
- /* initialize Floyd-Steinberg division tables */
- /* make sure rounding is done correctly for negative values! */
-
- int i;
-
- for (i = -256; i < 0; i++) {
- tbl1[i+256] = -((8 -i )/16);
- tbl3[i+256] = -((8 -3*i)/16);
- tbl5[i+256] = -((8 -5*i)/16);
- tbl7[i+256] = -((8 -7*i)/16);
- }
-
- for (i = 0; i < 256; i++) {
- tbl1[i+256] = (i +8)/16;
- tbl3[i+256] = (3*i+8)/16;
- tbl5[i+256] = (5*i+8)/16;
- tbl7[i+256] = (7*i+8)/16;
- }
-
- #ifdef FOO
- for (i=0; i<512; i++) {
- printf("%3d: tbl1=%3d, tbl3=%3d, tbl5=%3d, tbl7=%3d\n",
- i-256, tbl1[i], tbl3[i], tbl5[i], tbl7[i]);
- }
- #endif
- }
-
-
-
- /****************************/
- int Conv24to8(p,w,h,nc)
- /****************************/
- byte *p;
- int w,h,nc;
- {
- int i, j;
- CBOX *box_list, *ptr;
-
-
- if (nc<=0) nc = 255; /* 'nc == 0' breaks code */
-
- /* copy arguments to local-global variables */
- pic24 = p; pWIDE = WIDE = w; pHIGH = HIGH = h; num_colors = nc;
-
- /* allocate pic immediately, so that if we can't allocate it, we don't
- waste time running this algorithm */
-
- pic = (byte *) malloc(WIDE * HIGH);
- if (pic == NULL) {
- fprintf(stderr,"%s: Conv24to8() - failed to allocate 'pic'\n",cmd);
- return(1);
- }
-
- if (!noqcheck && QuickCheck(pic24,w,h,nc)) {
- /* see if it's a <256 color RGB pic */
- printf("No color compression was necessary.\n");
- return 0;
- }
-
- else if (conv24 == CONV24_FAST) {
- printf("Doing 'quick' 24-bit to 8-bit conversion.\n");
- return(Quick24to8(pic24,w,h));
- }
-
- else if (conv24 == CONV24_BEST) {
- printf("Doing 'best' 24-bit to 8-bit conversion.\n");
- return(ppmquant(pic24,w,h,nc));
- }
-
- else
- printf("Doing 'slow' 24-bit to 8-bit conversion.\n");
-
-
-
- /**** STEP 1: create empty boxes ****/
-
- usedboxes = NULL;
- box_list = freeboxes = (CBOX *) malloc(num_colors * sizeof(CBOX));
-
- if (box_list == NULL) {
- fprintf(stderr,"%s: Conv24to8() - failed to allocate 'freeboxes'\n",cmd);
- return(1);
- }
-
- for (i=0; i<num_colors; i++) {
- freeboxes[i].next = &freeboxes[i+1];
- freeboxes[i].prev = &freeboxes[i-1];
- }
- freeboxes[0].prev = NULL;
- freeboxes[num_colors-1].next = NULL;
-
-
-
-
- /**** STEP 2: get histogram, initialize first box ****/
-
- ptr = freeboxes;
- freeboxes = ptr->next;
- if (freeboxes) freeboxes->prev = NULL;
-
- ptr->next = usedboxes;
- usedboxes = ptr;
- if (ptr->next) ptr->next->prev = ptr;
-
- get_histogram(ptr);
-
-
-
-
- /**** STEP 3: continually subdivide boxes until no more free boxes remain */
-
- while (freeboxes) {
- ptr = largest_box();
- if (ptr) splitbox(ptr);
- else break;
- }
-
-
-
-
- /**** STEP 4: assign colors to all boxes ****/
-
- for (i=0, ptr=usedboxes; i<num_colors && ptr; i++, ptr=ptr->next) {
- assign_color(ptr, &r[i], &g[i], &b[i]);
- }
-
- /* We're done with the boxes now */
- num_colors = i;
- free(box_list);
- box_list = freeboxes = usedboxes = NULL;
-
-
-
-
- /**** STEP 5: scan histogram and map all values to closest color */
-
- /* 5a: create cell list as described in Heckbert[2] */
-
- ColorCells = (CCELL **) calloc(C_LEN*C_LEN*C_LEN, sizeof(CCELL *));
-
-
- /* 5b: create mapping from truncated pixel space to color table entries */
-
- map_colortable();
-
-
-
-
- /**** STEP 6: scan image, match input values to table entries */
-
- i = quant_fsdither();
-
- /* free everything that can be freed */
- for (j=0 ; j < C_LEN*C_LEN*C_LEN ; j++) {
- if (ColorCells[j] != NULL) free (ColorCells[j]);
- }
- free(ColorCells);
-
- return i;
- }
-
-
- /****************************/
- static void get_histogram(box)
- CBOX *box;
- /****************************/
- {
- int i,j,r,g,b,*ptr;
- byte *p;
-
- box->rmin = box->gmin = box->bmin = 999;
- box->rmax = box->gmax = box->bmax = -1;
- box->total = WIDE * HIGH;
-
- /* zero out histogram */
- ptr = &histogram[0][0][0];
- for (i=B_LEN*B_LEN*B_LEN; i>0; i-- ) *ptr++ = 0;
-
- /* calculate histogram */
- p = pic24;
- for (i=0; i<HIGH; i++)
- for (j=0; j<WIDE; j++) {
- r = (*p++) >> (COLOR_DEPTH-B_DEPTH);
- g = (*p++) >> (COLOR_DEPTH-B_DEPTH);
- b = (*p++) >> (COLOR_DEPTH-B_DEPTH);
-
- if (r < box->rmin) box->rmin = r;
- if (r > box->rmax) box->rmax = r;
-
- if (g < box->gmin) box->gmin = g;
- if (g > box->gmax) box->gmax = g;
-
- if (b < box->bmin) box->bmin = b;
- if (b > box->bmax) box->bmax = b;
- histogram[r][g][b]++;
- }
- }
-
-
-
- /******************************/
- static CBOX *largest_box()
- /******************************/
- {
- CBOX *tmp, *ptr;
- int size = -1;
-
- tmp = usedboxes;
- ptr = NULL;
-
- while (tmp) {
- if ( (tmp->rmax > tmp->rmin ||
- tmp->gmax > tmp->gmin ||
- tmp->bmax > tmp->bmin) && tmp->total > size ) {
- ptr = tmp;
- size = tmp->total;
- }
- tmp = tmp->next;
- }
- return(ptr);
- }
-
-
-
- /******************************/
- static void splitbox(ptr)
- CBOX *ptr;
- /******************************/
- {
- int hist2[B_LEN], first, last, i, rdel, gdel, bdel;
- CBOX *new;
- int *iptr, *histp, ir, ig, ib;
- int rmin,rmax,gmin,gmax,bmin,bmax;
- enum {RED,GREEN,BLUE} which;
-
- /*
- * 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
- */
-
- first = last = 0; /* shut RT hcc compiler up */
-
- rmin = ptr->rmin; rmax = ptr->rmax;
- gmin = ptr->gmin; gmax = ptr->gmax;
- bmin = ptr->bmin; bmax = ptr->bmax;
-
- rdel = rmax - rmin;
- gdel = gmax - gmin;
- bdel = bmax - bmin;
-
- if (rdel>=gdel && rdel>=bdel) which = RED;
- else if (gdel>=bdel) which = GREEN;
- else which = BLUE;
-
- /* get histogram along longest axis */
- switch (which) {
-
- case RED:
- histp = &hist2[rmin];
- for (ir=rmin; ir<=rmax; ir++) {
- *histp = 0;
- for (ig=gmin; ig<=gmax; ig++) {
- iptr = &histogram[ir][ig][bmin];
- for (ib=bmin; ib<=bmax; ib++) {
- *histp += *iptr;
- ++iptr;
- }
- }
- ++histp;
- }
- first = rmin; last = rmax;
- break;
-
- case GREEN:
- histp = &hist2[gmin];
- for (ig=gmin; ig<=gmax; ig++) {
- *histp = 0;
- for (ir=rmin; ir<=rmax; ir++) {
- iptr = &histogram[ir][ig][bmin];
- for (ib=bmin; ib<=bmax; ib++) {
- *histp += *iptr;
- ++iptr;
- }
- }
- ++histp;
- }
- first = gmin; last = gmax;
- break;
-
- case BLUE:
- histp = &hist2[bmin];
- for (ib=bmin; ib<=bmax; ib++) {
- *histp = 0;
- for (ir=rmin; ir<=rmax; ir++) {
- iptr = &histogram[ir][gmin][ib];
- for (ig=gmin; ig<=gmax; ig++) {
- *histp += *iptr;
- iptr += B_LEN;
- }
- }
- ++histp;
- }
- first = bmin; last = bmax;
- break;
- }
-
-
- /* find median point */
- {
- int sum, sum2;
-
- histp = &hist2[first];
-
- 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 = freeboxes;
- freeboxes = new->next;
- if (freeboxes) freeboxes->prev = NULL;
-
- if (usedboxes) usedboxes->prev = new;
- new->next = usedboxes;
- usedboxes = new;
-
- {
- int sum1,sum2,j;
-
- histp = &hist2[first];
- sum1 = 0;
- for (j = first; j < i; ++j) sum1 += *histp++;
- sum2 = 0;
- for (j = i; j <= last; ++j) sum2 += *histp++;
- new->total = sum1;
- ptr->total = sum2;
- }
-
-
- new->rmin = rmin; new->rmax = rmax;
- new->gmin = gmin; new->gmax = gmax;
- new->bmin = bmin; new->bmax = bmax;
-
- switch (which) {
- 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(new);
- shrinkbox(ptr);
- }
-
-
- /****************************/
- static void shrinkbox(box)
- CBOX *box;
- /****************************/
- {
- int *histp,ir,ig,ib;
- int rmin,rmax,gmin,gmax,bmin,bmax;
-
- rmin = box->rmin; rmax = box->rmax;
- gmin = box->gmin; gmax = box->gmax;
- bmin = box->bmin; bmax = box->bmax;
-
- if (rmax>rmin) {
- for (ir=rmin; ir<=rmax; ir++)
- for (ig=gmin; ig<=gmax; ig++) {
- histp = &histogram[ir][ig][bmin];
- for (ib=bmin; ib<=bmax; ib++)
- if (*histp++ != 0) {
- box->rmin = rmin = ir;
- goto have_rmin;
- }
- }
-
- have_rmin:
- if (rmax>rmin)
- for (ir=rmax; ir>=rmin; --ir)
- for (ig=gmin; ig<=gmax; ig++) {
- histp = &histogram[ir][ig][bmin];
- for (ib=bmin; ib<=bmax; ib++)
- if (*histp++ != 0) {
- box->rmax = rmax = ir;
- goto have_rmax;
- }
- }
- }
-
-
- have_rmax:
-
- if (gmax>gmin) {
- for (ig=gmin; ig<=gmax; ig++)
- for (ir=rmin; ir<=rmax; ir++) {
- histp = &histogram[ir][ig][bmin];
- for (ib=bmin; ib<=bmax; ib++)
- if (*histp++ != 0) {
- box->gmin = gmin = ig;
- goto have_gmin;
- }
- }
- have_gmin:
- if (gmax>gmin)
- for (ig=gmax; ig>=gmin; --ig)
- for (ir=rmin; ir<=rmax; ir++) {
- histp = &histogram[ir][ig][bmin];
- for (ib=bmin; ib<=bmax; ib++)
- if (*histp++ != 0) {
- box->gmax = gmax = ig;
- goto have_gmax;
- }
- }
- }
-
-
- have_gmax:
-
- if (bmax>bmin) {
- for (ib=bmin; ib<=bmax; ib++)
- for (ir=rmin; ir<=rmax; ir++) {
- histp = &histogram[ir][gmin][ib];
- for (ig=gmin; ig<=gmax; ig++) {
- if (*histp != 0) {
- box->bmin = bmin = ib;
- goto have_bmin;
- }
- histp += B_LEN;
- }
- }
- have_bmin:
- if (bmax>bmin)
- for (ib=bmax; ib>=bmin; --ib)
- for (ir=rmin; ir<=rmax; ir++) {
- histp = &histogram[ir][gmin][ib];
- for (ig=gmin; ig<=gmax; ig++) {
- if (*histp != 0) {
- bmax = ib;
- goto have_bmax;
- }
- histp += B_LEN;
- }
- }
- }
-
- have_bmax: return;
- }
-
-
-
- /*******************************/
- static void assign_color(ptr,rp,gp,bp)
- CBOX *ptr;
- byte *rp,*gp,*bp;
- /*******************************/
- {
- /* +1 ensures that color represents the middle of the box */
- int r,g,b;
-
- r = ((ptr->rmin + ptr->rmax) << (COLOR_DEPTH - B_DEPTH)) / 2;
- g = ((ptr->gmin + ptr->gmax) << (COLOR_DEPTH - B_DEPTH)) / 2;
- b = ((ptr->bmin + ptr->bmax) << (COLOR_DEPTH - B_DEPTH)) / 2;
-
- *rp = (byte) r; *gp = (byte) g; *bp = (byte) b;
- }
-
-
-
- /*******************************/
- static CCELL *create_colorcell(r1,g1,b1)
- int r1,g1,b1;
- /*******************************/
- {
- register int i;
- register CCELL *ptr;
- register byte *rp,*gp,*bp;
- int ir,ig,ib;
- long dist, mindist, tmp;
-
- ir = r1 >> (COLOR_DEPTH-C_DEPTH);
- ig = g1 >> (COLOR_DEPTH-C_DEPTH);
- ib = b1 >> (COLOR_DEPTH-C_DEPTH);
-
- r1 &= ~1 << (COLOR_DEPTH-C_DEPTH);
- g1 &= ~1 << (COLOR_DEPTH-C_DEPTH);
- b1 &= ~1 << (COLOR_DEPTH-C_DEPTH);
-
- ptr = (CCELL *) malloc(sizeof(CCELL));
- *(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 = 2000000000;
-
- rp=r; gp=g; bp=b;
- for (i=0; i<num_colors; i++,rp++,gp++,bp++)
- if( *rp>>(COLOR_DEPTH-C_DEPTH) == ir &&
- *gp>>(COLOR_DEPTH-C_DEPTH) == ig &&
- *bp>>(COLOR_DEPTH-C_DEPTH) == ib) {
-
- ptr->entries[ptr->num_ents][0] = i;
- ptr->entries[ptr->num_ents][1] = 0;
- ++ptr->num_ents;
-
- tmp = *rp - r1;
- if (tmp < (MAX_COLOR/C_LEN/2)) tmp = MAX_COLOR/C_LEN-1 - tmp;
- dist = (tmp*tmp * R2FACT);
-
- tmp = *gp - g1;
- if (tmp < (MAX_COLOR/C_LEN/2)) tmp = MAX_COLOR/C_LEN-1 - tmp;
- dist += (tmp*tmp * G2FACT);
-
- tmp = *bp - b1;
- if (tmp < (MAX_COLOR/C_LEN/2)) tmp = MAX_COLOR/C_LEN-1 - tmp;
- dist += (tmp*tmp * B2FACT);
-
- if (dist < mindist) mindist = dist;
- }
-
-
- /* step 3: find all points within that distance to box */
-
- rp=r; gp=g; bp=b;
- for (i=0; i<num_colors; i++,rp++,gp++,bp++)
- if (*rp >> (COLOR_DEPTH-C_DEPTH) != ir ||
- *gp >> (COLOR_DEPTH-C_DEPTH) != ig ||
- *bp >> (COLOR_DEPTH-C_DEPTH) != ib) {
-
- dist = 0;
-
- if ((tmp = r1 - *rp)>0 || (tmp = *rp - (r1 + MAX_COLOR/C_LEN-1)) > 0 )
- dist += (tmp*tmp * R2FACT);
-
- if( (tmp = g1 - *gp)>0 || (tmp = *gp - (g1 + MAX_COLOR/C_LEN-1)) > 0 )
- dist += (tmp*tmp * G2FACT);
-
- if( (tmp = b1 - *bp)>0 || (tmp = *bp - (b1 + MAX_COLOR/C_LEN-1)) > 0 )
- dist += (tmp*tmp * B2FACT);
-
- 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 */
- {
- int n, next_n;
-
- n = ptr->num_ents - 1;
- while (n>0) {
- 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;
- }
- }
- n = next_n;
- }
- }
- return (ptr);
- }
-
-
-
-
- /***************************/
- static void map_colortable()
- /***************************/
- {
- int ir,ig,ib, *histp;
- CCELL *cell;
-
- histp = &histogram[0][0][0];
- for (ir=0; ir<B_LEN; ir++)
- for (ig=0; ig<B_LEN; ig++)
- for (ib=0; ib<B_LEN; ib++) {
- if (*histp==0) *histp = -1;
- else {
- int i, j;
- long dist, d2, tmp;
-
- cell = *(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(ir<<(COLOR_DEPTH-B_DEPTH),
- ig<<(COLOR_DEPTH-B_DEPTH),
- ib<<(COLOR_DEPTH-B_DEPTH));
-
- dist = 2000000000;
- for (i=0; i<cell->num_ents && dist>cell->entries[i][1]; i++) {
- j = cell->entries[i][0];
- d2 = r[j] - (ir << (COLOR_DEPTH-B_DEPTH));
- d2 = (d2 * d2 * R2FACT);
- tmp = g[j] - (ig << (COLOR_DEPTH-B_DEPTH));
- d2 += (tmp*tmp * G2FACT);
- tmp = b[j] - (ib << (COLOR_DEPTH-B_DEPTH));
- d2 += (tmp*tmp * B2FACT);
- if( d2 < dist ) { dist = d2; *histp = j; }
- }
- }
- histp++;
- }
- }
-
-
-
- /*****************************/
- static int quant_fsdither()
- /*****************************/
- {
- register int *thisptr, *nextptr;
- int *thisline, *nextline, *tmpptr;
- int r1, g1, b1, r2, g2, b2, r3, g3, b3;
- int i, j, k, d, imax, jmax, oval;
- byte *inptr, *outptr;
- int lastline, lastpixel;
-
- imax = HIGH - 1;
- jmax = WIDE - 1;
-
- thisline = (int *) malloc(WIDE * 3 * sizeof(int));
- nextline = (int *) malloc(WIDE * 3 * sizeof(int));
-
- if (thisline == NULL || nextline == NULL) {
- fprintf(stderr,"%s: unable to allocate stuff for the 'dither' routine\n",
- cmd);
- return 1;
- }
-
-
- inptr = (byte *) pic24;
- outptr = (byte *) pic;
-
- /* get first line of picture */
- for (j=WIDE * 3, tmpptr=nextline; j; j--)
- *tmpptr++ = (int) *inptr++;
-
- for (i=0; i<HIGH; i++) {
- /* swap thisline and nextline */
- tmpptr = thisline; thisline = nextline; nextline = tmpptr;
- lastline = (i==imax);
-
- /* read in next line */
- if (!lastline)
- for (j=WIDE * 3, tmpptr=nextline; j; j--)
- *tmpptr++ = (int) *inptr++;
-
- /* dither this line and put it into the output picture */
- thisptr = thisline; nextptr = nextline;
-
- for (j=0; j<WIDE; j++) {
- lastpixel = (j==jmax);
-
- r2 = *thisptr++; g2 = *thisptr++; b2 = *thisptr++;
-
- RANGE(r2, 0, MAX_COLOR-1);
- RANGE(g2, 0, MAX_COLOR-1);
- RANGE(b2, 0, MAX_COLOR-1);
-
- r1 = r2; g1 = g2; b1 = b2;
-
- r2 >>= (COLOR_DEPTH-B_DEPTH);
- g2 >>= (COLOR_DEPTH-B_DEPTH);
- b2 >>= (COLOR_DEPTH-B_DEPTH);
-
- if ( (oval=histogram[r2][g2][b2]) == -1) {
- int ci, cj;
- long dist, d2, tmp;
- CCELL *cell;
-
- cell = *( ColorCells +
- ( ((r2>>(B_DEPTH-C_DEPTH)) << C_DEPTH*2)
- + ((g2>>(B_DEPTH-C_DEPTH)) << C_DEPTH )
- + (b2>>(B_DEPTH-C_DEPTH)) ) );
-
- if (cell==NULL) cell = create_colorcell(r1,g1,b1);
-
- dist = 2000000000;
- for (ci=0; ci<cell->num_ents && dist>cell->entries[ci][1]; ci++) {
- cj = cell->entries[ci][0];
- d2 = (r[cj] >> (COLOR_DEPTH-B_DEPTH)) - r2;
- d2 = (d2*d2 * R2FACT);
- tmp = (g[cj] >> (COLOR_DEPTH-B_DEPTH)) - g2;
- d2 += (tmp*tmp * G2FACT);
- tmp = (b[cj] >> (COLOR_DEPTH-B_DEPTH)) - b2;
- d2 += (tmp*tmp * B2FACT);
- if (d2<dist) { dist = d2; oval = cj; }
- }
- histogram[r2][g2][b2] = oval;
- }
-
- *outptr++ = oval;
-
- r1 -= r[oval]; g1 -= g[oval]; b1 -= b[oval];
-
- /* don't use tables, because r1,g1,b1 could go negative */
- if (!lastpixel) {
- thisptr[0] += (r1<0) ? (r1*7-8)/16 : (r1*7+8)/16;
- thisptr[1] += (g1<0) ? (g1*7-8)/16 : (g1*7+8)/16;
- thisptr[2] += (b1<0) ? (b1*7-8)/16 : (b1*7+8)/16;
- }
-
- if (!lastline) {
- if (j) {
- nextptr[-3] += (r1<0) ? (r1*3-8)/16 : (r1*3+8)/16;
- nextptr[-2] += (g1<0) ? (g1*3-8)/16 : (g1*3+8)/16;
- nextptr[-1] += (b1<0) ? (b1*3-8)/16 : (b1*3+8)/16;
- }
-
- nextptr[0] += (r1<0) ? (r1*5-8)/16 : (r1*5+8)/16;
- nextptr[1] += (g1<0) ? (g1*5-8)/16 : (g1*5+8)/16;
- nextptr[2] += (b1<0) ? (b1*5-8)/16 : (b1*5+8)/16;
-
- if (!lastpixel) {
- nextptr[3] += (r1<0) ? (r1-8)/16 : (r1+8)/16;
- nextptr[4] += (g1<0) ? (g1-8)/16 : (g1+8)/16;
- nextptr[5] += (b1<0) ? (b1-8)/16 : (b1+8)/16;
- }
- nextptr += 3;
- }
- }
- }
-
- free(thisline); free(nextline);
- return 0;
- }
-
-
-
-
-
-
-
-
-
-
- /************************************/
- static int Quick24to8(p24,w,h)
- byte *p24;
- int w,h;
- {
-
- /* floyd-steinberg dithering.
- *
- * ---- x 7/16
- * 3/16 5/16 1/16
- *
- */
-
- /* called after 'pic' has been alloced, pWIDE,pHIGH set up, mono/1-bit
- checked already */
-
- byte *pp;
- int r1, g1, b1;
- int *thisline, *nextline, *thisptr, *nextptr, *tmpptr;
- int i, j, val, pwide3;
- int imax, jmax;
-
- pp = pic; pwide3 = w * 3; imax = h-1; jmax = w-1;
-
- /* up to 256 colors: 3 bits R, 3 bits G, 2 bits B (RRRGGGBB) */
- #define RMASK 0xe0
- #define R_SHIFT 0
- #define GMASK 0xe0
- #define G_SHIFT 3
- #define BMASK 0xc0
- #define B_SHIFT 6
-
- /* load up colormap */
- /* note that 0 and 255 of each color are always in the map; */
- /* intermediate values are evenly spaced. */
- for (i=0; i<256; i++) {
- r[i] = (((i<<R_SHIFT) & RMASK) * 255 + RMASK/2) / RMASK;
- g[i] = (((i<<G_SHIFT) & GMASK) * 255 + GMASK/2) / GMASK;
- b[i] = (((i<<B_SHIFT) & BMASK) * 255 + BMASK/2) / BMASK;
- }
-
-
- thisline = (int *) malloc(pwide3 * sizeof(int));
- nextline = (int *) malloc(pwide3 * sizeof(int));
- if (!thisline || !nextline) {
- fprintf(stderr,"%s: unable to allocate memory in Quick24to8()\n", cmd);
- return(1);
- }
-
- /* get first line of picture */
- for (j=pwide3, tmpptr=nextline; j; j--) *tmpptr++ = (int) *p24++;
-
- for (i=0; i<h; i++) {
- tmpptr = thisline; thisline = nextline; nextline = tmpptr; /* swap */
-
- if (i!=imax) /* get next line */
- for (j=pwide3, tmpptr=nextline; j; j--)
- *tmpptr++ = (int) *p24++;
-
- for (j=0, thisptr=thisline, nextptr=nextline; j<w; j++,pp++) {
- r1 = *thisptr++; g1 = *thisptr++; b1 = *thisptr++;
- RANGE(r1,0,255); RANGE(g1,0,255); RANGE(b1,0,255);
-
- /* choose actual pixel value */
- val = (((r1&RMASK)>>R_SHIFT) | ((g1&GMASK)>>G_SHIFT) |
- ((b1&BMASK)>>B_SHIFT));
- *pp = val;
-
- /* compute color errors */
- r1 -= r[val];
- g1 -= g[val];
- b1 -= b[val];
-
- /* Add fractions of errors to adjacent pixels */
- r1 += 256; /* make positive for table indexing */
- g1 += 256;
- b1 += 256;
-
- if (j!=jmax) { /* adjust RIGHT pixel */
- thisptr[0] += tbl7[r1];
- thisptr[1] += tbl7[g1];
- thisptr[2] += tbl7[b1];
- }
-
- if (i!=imax) { /* do BOTTOM pixel */
- nextptr[0] += tbl5[r1];
- nextptr[1] += tbl5[g1];
- nextptr[2] += tbl5[b1];
-
- if (j>0) { /* do BOTTOM LEFT pixel */
- nextptr[-3] += tbl3[r1];
- nextptr[-2] += tbl3[g1];
- nextptr[-1] += tbl3[b1];
- }
-
- if (j!=jmax) { /* do BOTTOM RIGHT pixel */
- nextptr[3] += tbl1[r1];
- nextptr[4] += tbl1[g1];
- nextptr[5] += tbl1[b1];
- }
- nextptr += 3;
- }
- }
- }
-
- return 0;
- }
-
-
-
- /****************************/
- static int QuickCheck(pic24,w,h,maxcol)
- byte *pic24;
- int w,h,maxcol;
- {
- /* scans picture until it finds more than 'maxcol' different colors. If it
- finds more than 'maxcol' colors, it returns '0'. If it DOESN'T, it does
- the 24-to-8 conversion by simply sticking the colors it found into
- a colormap, and changing instances of a color in pic24 into colormap
- indicies (in pic) */
-
- unsigned long colors[256],col;
- int i, nc, low, high, mid;
- byte *p, *pix;
-
- if (maxcol>256) maxcol = 256;
-
- /* put the first color in the table by hand */
- nc = 0; mid = 0;
-
- for (i=w*h,p=pic24; i; i--) {
- col = (((u_long) *p++) << 16);
- col += (((u_long) *p++) << 8);
- col += *p++;
-
- /* binary search the 'colors' array to see if it's in there */
- low = 0; high = nc-1;
- while (low <= high) {
- mid = (low+high)/2;
- if (col < colors[mid]) high = mid - 1;
- else if (col > colors[mid]) low = mid + 1;
- else break;
- }
-
- if (high < low) { /* didn't find color in list, add it. */
- /* WARNING: this is an overlapped memory copy. memcpy doesn't do
- it correctly, hence 'bcopy', which claims to */
- if (nc>=maxcol) return 0;
- bcopy(&colors[low], &colors[low+1], (nc - low) * sizeof(unsigned long));
- colors[low] = col;
- nc++;
- }
- }
-
-
- /* run through the data a second time, this time mapping pixel values in
- pic24 into colormap offsets into 'colors' */
-
- for (i=w*h,p=pic24, pix=pic; i; i--,pix++) {
- col = (((u_long) *p++) << 16);
- col += (((u_long) *p++) << 8);
- col += *p++;
-
- /* binary search the 'colors' array. It *IS* in there */
- low = 0; high = nc-1;
- while (low <= high) {
- mid = (low+high)/2;
- if (col < colors[mid]) high = mid - 1;
- else if (col > colors[mid]) low = mid + 1;
- else break;
- }
-
- if (high < low) {
- fprintf(stderr,"QuickCheck: impossible situation!\n");
- exit(1);
- }
- *pix = mid;
- }
-
- /* and load up the 'desired colormap' */
- for (i=0; i<nc; i++) {
- r[i] = colors[i]>>16;
- g[i] = (colors[i]>>8) & 0xff;
- b[i] = colors[i] & 0xff;
- }
-
- return 1;
- }
-
-
-
-
-
-
-
-
-
-
-
-
- /***************************************************************/
- /* The following code based on code from the 'pbmplus' package */
- /* written by Jef Poskanzer */
- /***************************************************************/
-
-
- /* ppmquant.c - quantize the colors in a pixmap down to a specified number
- **
- ** Copyright (C) 1989, 1991 by Jef Poskanzer.
- **
- ** 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.
- */
-
-
- #undef max
- #define max(a,b) ((a) > (b) ? (a) : (b))
- #undef min
- #define min(a,b) ((a) < (b) ? (a) : (b))
- #undef abs
- #define abs(a) ((a) >= 0 ? (a) : -(a))
- #undef odd
- #define odd(n) ((n) & 1)
-
- typedef unsigned char pixval;
-
- #define PPM_MAXMAXVAL 255
- typedef struct { pixval r, g, b; } pixel;
-
- #define PPM_GETR(p) ((p).r)
- #define PPM_GETG(p) ((p).g)
- #define PPM_GETB(p) ((p).b)
-
- #define PPM_ASSIGN(p,red,grn,blu) \
- { (p).r = (red); (p).g = (grn); (p).b = (blu); }
-
- #define PPM_EQUAL(p,q) ( (p).r == (q).r && (p).g == (q).g && (p).b == (q).b )
-
-
- /* Color scaling macro -- to make writing ppmtowhatever easier. */
-
- #define PPM_DEPTH(newp,p,oldmaxval,newmaxval) \
- PPM_ASSIGN( (newp), \
- (int) PPM_GETR(p) * (newmaxval) / (oldmaxval), \
- (int) PPM_GETG(p) * (newmaxval) / (oldmaxval), \
- (int) PPM_GETB(p) * (newmaxval) / (oldmaxval) )
-
-
- /* Luminance macro. */
-
- /*
- * #define PPM_LUMIN(p) \
- * ( 0.299 * PPM_GETR(p) + 0.587 * PPM_GETG(p) + 0.114 * PPM_GETB(p) )
- */
-
- /* Luminance macro, using only integer ops. Returns an int (*256) JHB */
- #define PPM_LUMIN(p) \
- ( 77 * PPM_GETR(p) + 150 * PPM_GETG(p) + 29 * PPM_GETB(p) )
-
- /* Color histogram stuff. */
-
- typedef struct colorhist_item* colorhist_vector;
- struct colorhist_item { pixel color;
- int value;
- };
-
- typedef struct colorhist_list_item* colorhist_list;
- struct colorhist_list_item { struct colorhist_item ch;
- colorhist_list next;
- };
-
- typedef colorhist_list* colorhash_table;
-
-
- #define MAXCOLORS 32767
- #define CLUSTER_MAXVAL 63
-
- #define LARGE_LUM
- #define REP_AVERAGE_PIXELS
-
- #define FS_SCALE 1024
-
- #define HASH_SIZE 6553
-
- #define ppm_hashpixel(p) ((((int) PPM_GETR(p) * 33023 + \
- (int) PPM_GETG(p) * 30013 + \
- (int) PPM_GETB(p) * 27011) & 0x7fffffff) \
- % HASH_SIZE)
-
-
-
- /*** function defs ***/
- static colorhist_vector mediancut();
- static int redcompare(), greencompare(), bluecompare();
- static int sumcompare();
- static colorhist_vector ppm_computecolorhist(), ppm_colorhashtocolorhist();
- static colorhash_table ppm_computecolorhash(), ppm_alloccolorhash();
- static void ppm_addtocolorhash();
- static void ppm_freecolorhist(), ppm_freecolorhash();
- static int ppm_lookupcolor();
-
-
- /****************************************************************************/
- static int ppmquant(pic24, cols, rows, newcolors)
- byte *pic24;
- int cols, rows, newcolors;
- {
- pixel** pixels;
- register pixel* pP;
- int argn, row;
- register int col, limitcol;
- pixval maxval, newmaxval;
- int colors;
- register int index;
- colorhist_vector chv, colormap;
- colorhash_table cht;
- int verbose, floyd;
- long *thisrerr, *nextrerr, *thisgerr, *nextgerr;
- long *thisberr, *nextberr, *temperr;
- register long sr, sg, sb, err;
- int fs_direction, i;
- unsigned char *picptr;
- static char *fn = "ppmquant()";
-
- maxval = 255;
-
- /*
- * reformat 24-bit pic24 image (3 bytes per pixel) into 2-dimensional
- * array of pixel structures
- */
-
- if (DEBUG) fprintf(stderr,"%s: remapping to ppm-style internal fmt\n", fn);
-
- pixels = (pixel **) malloc(rows * sizeof(pixel *));
- if (!pixels) FatalError("couldn't allocate 'pixels' array");
- for (row=0; row<rows; row++) {
- pixels[row] = (pixel *) malloc(cols * sizeof(pixel));
- if (!pixels[row]) FatalError("couldn't allocate a row of pixels array");
-
- for (col=0, pP=pixels[row]; col<cols; col++, pP++) {
- pP->r = *pic24++;
- pP->g = *pic24++;
- pP->b = *pic24++;
- }
- }
- if (DEBUG) fprintf(stderr,"%s: done format remapping\n", fn);
-
-
-
-
- /*
- * attempt to make a histogram of the colors, unclustered.
- * If at first we don't succeed, lower maxval to increase color
- * coherence and try again. This will eventually terminate, with
- * maxval at worst 15, since 32^3 is approximately MAXCOLORS.
- */
-
- for ( ; ; ) {
- if (DEBUG) fprintf(stderr, "%s: making histogram\n", fn);
-
- chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
- if (chv != (colorhist_vector) 0) break;
-
- if (DEBUG) fprintf(stderr, "%s: too many colors!\n", fn);
- newmaxval = maxval / 2;
- if (DEBUG) fprintf(stderr, "%s: rescaling colors (maxval=%d) %s\n",
- fn, newmaxval, "to improve clustering");
-
- for (row=0; row<rows; ++row)
- for (col=0, pP=pixels[row]; col<cols; ++col, ++pP)
- PPM_DEPTH( *pP, *pP, maxval, newmaxval );
- maxval = newmaxval;
- }
-
- if (DEBUG) fprintf(stderr,"%s: %d colors found\n", fn, colors);
-
-
-
- /*
- * Step 3: apply median-cut to histogram, making the new colormap.
- */
-
- if (DEBUG) fprintf(stderr, "%s: choosing %d colors\n", fn, newcolors);
- colormap = mediancut(chv, colors, rows * cols, maxval, newcolors);
- ppm_freecolorhist(chv);
-
-
-
- /*
- * Step 4: map the colors in the image to their closest match in the
- * new colormap, and write 'em out.
- */
-
- if (DEBUG) fprintf(stderr,"%s: mapping image to new colors\n", fn);
- cht = ppm_alloccolorhash();
-
- picptr = pic;
- for (row = 0; row < rows; ++row) {
- col = 0; limitcol = cols; pP = pixels[row];
-
- do {
- int hash;
- colorhist_list chl;
-
- /* Check hash table to see if we have already matched this color. */
- /* index = ppm_lookupcolor( cht, pP ); */
-
- hash = ppm_hashpixel(*pP);
- for (chl = cht[hash]; chl; chl = chl->next)
- if (PPM_EQUAL(chl->ch.color, *pP)) {index = chl->ch.value; break;}
-
- if (!chl /*index = -1*/) {/* No; search colormap for closest match. */
- register int i, r1, g1, b1, r2, g2, b2;
- register long dist, newdist;
-
- r1 = PPM_GETR( *pP );
- g1 = PPM_GETG( *pP );
- b1 = PPM_GETB( *pP );
- dist = 2000000000;
-
- for (i=0; i<newcolors; i++) {
- r2 = PPM_GETR( colormap[i].color );
- g2 = PPM_GETG( colormap[i].color );
- b2 = PPM_GETB( colormap[i].color );
-
- newdist = ( r1 - r2 ) * ( r1 - r2 ) +
- ( g1 - g2 ) * ( g1 - g2 ) +
- ( b1 - b2 ) * ( b1 - b2 );
-
- if (newdist<dist) { index = i; dist = newdist; }
- }
-
- /* ppm_addtocolorhash(cht, pP, index); */
- hash = ppm_hashpixel(*pP);
- chl = (colorhist_list) malloc(sizeof(struct colorhist_list_item));
- if (!chl) FatalError("ran out of memory adding to hash table");
-
- chl->ch.color = *pP;
- chl->ch.value = index;
- chl->next = cht[hash];
- cht[hash] = chl;
- }
-
- *picptr++ = index;
-
- ++col;
- ++pP;
- }
- while (col != limitcol);
- }
-
- /* rescale the colormap and load the XV colormap */
- for (i=0; i<newcolors; i++) {
- PPM_DEPTH(colormap[i].color, colormap[i].color, maxval, 255);
- r[i] = PPM_GETR( colormap[i].color );
- g[i] = PPM_GETG( colormap[i].color );
- b[i] = PPM_GETB( colormap[i].color );
- }
-
- /* free the pixels array */
- for (i=0; i<rows; i++) free(pixels[i]);
- free(pixels);
-
- /* free cht and colormap */
- ppm_freecolorhist(colormap);
- ppm_freecolorhash(cht);
-
- return 0;
- }
-
-
-
- /*
- ** Here is the fun part, the median-cut colormap generator. This is based
- ** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
- ** Display", SIGGRAPH '82 Proceedings, page 297.
- */
-
- typedef struct box* box_vector;
- struct box {
- int index;
- int colors;
- int sum;
- };
-
-
- /****************************************************************************/
- static colorhist_vector mediancut( chv, colors, sum, maxval, newcolors )
- colorhist_vector chv;
- int colors, sum, newcolors;
- pixval maxval;
- {
- colorhist_vector colormap;
- box_vector bv;
- register int bi, i;
- int boxes;
-
- bv = (box_vector) malloc(sizeof(struct box) * newcolors);
- colormap = (colorhist_vector)
- malloc(sizeof(struct colorhist_item) * newcolors );
-
- if (!bv || !colormap) FatalError("unable to malloc in mediancut()");
-
- for (i=0; i<newcolors; i++)
- PPM_ASSIGN(colormap[i].color, 0, 0, 0);
-
- /*
- * Set up the initial box.
- */
- bv[0].index = 0;
- bv[0].colors = colors;
- bv[0].sum = sum;
- boxes = 1;
-
-
- /*
- ** Main loop: split boxes until we have enough.
- */
-
- while ( boxes < newcolors ) {
- register int indx, clrs;
- int sm;
- register int minr, maxr, ming, maxg, minb, maxb, v;
- int halfsum, lowersum;
-
- /*
- ** Find the first splittable box.
- */
- for (bi=0; bv[bi].colors<2 && bi<boxes; bi++) ;
- if (bi == boxes) break; /* ran out of colors! */
-
- indx = bv[bi].index;
- clrs = bv[bi].colors;
- sm = bv[bi].sum;
-
- /*
- ** Go through the box finding the minimum and maximum of each
- ** component - the boundaries of the box.
- */
- minr = maxr = PPM_GETR( chv[indx].color );
- ming = maxg = PPM_GETG( chv[indx].color );
- minb = maxb = PPM_GETB( chv[indx].color );
-
- for (i=1; i<clrs; i++) {
- v = PPM_GETR( chv[indx + i].color );
- if (v < minr) minr = v;
- if (v > maxr) maxr = v;
-
- v = PPM_GETG( chv[indx + i].color );
- if (v < ming) ming = v;
- if (v > maxg) maxg = v;
-
- v = PPM_GETB( chv[indx + i].color );
- if (v < minb) minb = v;
- if (v > maxb) maxb = v;
- }
-
- /*
- ** Find the largest dimension, and sort by that component. I have
- ** included two methods for determining the "largest" dimension;
- ** first by simply comparing the range in RGB space, and second
- ** by transforming into luminosities before the comparison. You
- ** can switch which method is used by switching the commenting on
- ** the LARGE_ defines at the beginning of this source file.
- */
- {
- /* LARGE_LUM version */
-
- pixel p;
- int rl, gl, bl;
-
- PPM_ASSIGN(p, maxr - minr, 0, 0);
- rl = PPM_LUMIN(p);
-
- PPM_ASSIGN(p, 0, maxg - ming, 0);
- gl = PPM_LUMIN(p);
-
- PPM_ASSIGN(p, 0, 0, maxb - minb);
- bl = PPM_LUMIN(p);
-
- if (rl >= gl && rl >= bl)
- qsort( (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
- redcompare );
- else if (gl >= bl)
- qsort( (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
- greencompare );
- else qsort( (char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
- bluecompare );
- }
-
- /*
- ** Now find the median based on the counts, so that about half the
- ** pixels (not colors, pixels) are in each subdivision.
- */
- lowersum = chv[indx].value;
- halfsum = sm / 2;
- for (i=1; i<clrs-1; i++) {
- if (lowersum >= halfsum) break;
- lowersum += chv[indx + i].value;
- }
-
- /*
- ** Split the box, and sort to bring the biggest boxes to the top.
- */
- bv[bi].colors = i;
- bv[bi].sum = lowersum;
- bv[boxes].index = indx + i;
- bv[boxes].colors = clrs - i;
- bv[boxes].sum = sm - lowersum;
- ++boxes;
- qsort( (char*) bv, boxes, sizeof(struct box), sumcompare );
- } /* while (boxes ... */
-
- /*
- ** Ok, we've got enough boxes. Now choose a representative color for
- ** each box. There are a number of possible ways to make this choice.
- ** One would be to choose the center of the box; this ignores any structure
- ** within the boxes. Another method would be to average all the colors in
- ** the box - this is the method specified in Heckbert's paper. A third
- ** method is to average all the pixels in the box. You can switch which
- ** method is used by switching the commenting on the REP_ defines at
- ** the beginning of this source file.
- */
-
- for (bi=0; bi<boxes; bi++) {
- /* REP_AVERAGE_PIXELS version */
- register int indx = bv[bi].index;
- register int clrs = bv[bi].colors;
- register long r = 0, g = 0, b = 0, sum = 0;
-
- for (i=0; i<clrs; i++) {
- r += PPM_GETR( chv[indx + i].color ) * chv[indx + i].value;
- g += PPM_GETG( chv[indx + i].color ) * chv[indx + i].value;
- b += PPM_GETB( chv[indx + i].color ) * chv[indx + i].value;
- sum += chv[indx + i].value;
- }
-
- r = r / sum; if (r>maxval) r = maxval; /* avoid math errors */
- g = g / sum; if (g>maxval) g = maxval;
- b = b / sum; if (b>maxval) b = maxval;
-
- PPM_ASSIGN( colormap[bi].color, r, g, b );
- }
-
- free(bv);
- return colormap;
- }
-
-
- /**********************************/
- static int redcompare(ch1, ch2)
- colorhist_vector ch1, ch2;
- {
- return (int) PPM_GETR( ch1->color ) - (int) PPM_GETR( ch2->color );
- }
-
- /**********************************/
- static int greencompare(ch1, ch2)
- colorhist_vector ch1, ch2;
- {
- return (int) PPM_GETG( ch1->color ) - (int) PPM_GETG( ch2->color );
- }
-
- /**********************************/
- static int bluecompare( ch1, ch2 )
- colorhist_vector ch1, ch2;
- {
- return (int) PPM_GETB( ch1->color ) - (int) PPM_GETB( ch2->color );
- }
-
- /**********************************/
- static int sumcompare( b1, b2 )
- box_vector b1, b2;
- {
- return b2->sum - b1->sum;
- }
-
-
-
- /****************************************************************************/
- static colorhist_vector ppm_computecolorhist(pixels, cols, rows,
- maxcolors, colorsP)
- pixel** pixels;
- int cols, rows;
- int* colorsP;
- {
- colorhash_table cht;
- colorhist_vector chv;
-
- cht = ppm_computecolorhash(pixels, cols, rows, maxcolors, colorsP);
- if (!cht) return (colorhist_vector) 0;
-
- chv = ppm_colorhashtocolorhist(cht, maxcolors);
- ppm_freecolorhash(cht);
- return chv;
- }
-
-
- /****************************************************************************/
- static colorhash_table ppm_computecolorhash(pixels, cols, rows,
- maxcolors, colorsP )
- pixel** pixels;
- int cols, rows;
- int* colorsP;
- {
- colorhash_table cht;
- register pixel* pP;
- colorhist_list chl;
- int col, row, hash;
-
- cht = ppm_alloccolorhash( );
- *colorsP = 0;
-
- /* Go through the entire image, building a hash table of colors. */
- for (row=0; row<rows; row++)
- for (col=0, pP=pixels[row]; col<cols; col++, pP++) {
- hash = ppm_hashpixel(*pP);
-
- for (chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next)
- if (PPM_EQUAL(chl->ch.color, *pP)) break;
-
- if (chl != (colorhist_list) 0) ++(chl->ch.value);
- else {
- if ((*colorsP)++ > maxcolors) {
- ppm_freecolorhash(cht);
- return (colorhash_table) 0;
- }
-
- chl = (colorhist_list) malloc(sizeof(struct colorhist_list_item));
- if (!chl) FatalError("ran out of memory computing hash table");
-
- chl->ch.color = *pP;
- chl->ch.value = 1;
- chl->next = cht[hash];
- cht[hash] = chl;
- }
- }
-
- return cht;
- }
-
-
- /****************************************************************************/
- static colorhash_table ppm_alloccolorhash()
- {
- colorhash_table cht;
- int i;
-
- cht = (colorhash_table) malloc( HASH_SIZE * sizeof(colorhist_list) );
- if (!cht) FatalError("ran out of memory allocating hash table");
-
- for (i=0; i<HASH_SIZE; i++ )
- cht[i] = (colorhist_list) 0;
-
- return cht;
- }
-
-
- /****************************************************************************/
- static void ppm_addtocolorhash( cht, colorP, value )
- colorhash_table cht;
- pixel* colorP;
- int value;
- {
- int hash;
- colorhist_list chl;
-
- hash = ppm_hashpixel( *colorP );
- chl = (colorhist_list) malloc( sizeof(struct colorhist_list_item) );
- if (!chl) FatalError("ran out of memory adding to hash table");
-
- chl->ch.color = *colorP;
- chl->ch.value = value;
- chl->next = cht[hash];
- cht[hash] = chl;
- }
-
-
- /****************************************************************************/
- static colorhist_vector ppm_colorhashtocolorhist( cht, maxcolors )
- colorhash_table cht;
- int maxcolors;
- {
- colorhist_vector chv;
- colorhist_list chl;
- int i, j;
-
- /* Now collate the hash table into a simple colorhist array. */
- chv = (colorhist_vector) malloc( maxcolors * sizeof(struct colorhist_item) );
-
- /* (Leave room for expansion by caller.) */
- if (!chv) FatalError("ran out of memory generating histogram");
-
- /* Loop through the hash table. */
- j = 0;
- for (i=0; i<HASH_SIZE; i++)
- for (chl = cht[i]; chl != (colorhist_list) 0; chl = chl->next) {
- /* Add the new entry. */
- chv[j] = chl->ch;
- ++j;
- }
-
- return chv;
- }
-
-
- /****************************************************************************/
- static int ppm_lookupcolor( cht, colorP )
- colorhash_table cht;
- pixel* colorP;
- {
- int hash;
- colorhist_list chl;
-
- hash = ppm_hashpixel( *colorP );
- for (chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next)
- if (PPM_EQUAL(chl->ch.color, *colorP)) return chl->ch.value;
-
- return -1;
- }
-
-
- /****************************************************************************/
- static void ppm_freecolorhist( chv )
- colorhist_vector chv;
- {
- free( (char*) chv );
- }
-
-
- /****************************************************************************/
- static void ppm_freecolorhash( cht )
- colorhash_table cht;
- {
- int i;
- colorhist_list chl, chlnext;
-
- for (i=0; i<HASH_SIZE; i++)
- for (chl = cht[i]; chl != (colorhist_list) 0; chl = chlnext) {
- chlnext = chl->next;
- free( (char*) chl );
- }
-
- free( (char*) cht );
- }
-