/* libpnmrw.c - PBM/PGM/PPM read/write library ** ** Copyright (C) 1988, 1989, 1991, 1992 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. */ /* $Id: libpnmrw.c,v 1.5 1996/06/03 05:35:25 torsten Exp $ */ #if defined(HAVE_PARAM_H) #include #endif #if defined(SVR2) || defined(SVR3) || defined(SVR4) #ifndef SYSV #define SYSV #endif #endif #if ! ( defined(BSD) || defined(SYSV) || defined(MSDOS) ) /* CONFIGURE: If your system is >= 4.2BSD, set the BSD option; if you're a ** System V site, set the SYSV option; and if you're IBM-compatible, set ** MSDOS. If your compiler is ANSI C, you're probably better off setting ** SYSV - all it affects is string handling. */ #define BSD /* #define SYSV */ /* #define MSDOS */ #endif #include #include "libpnmrw.h" #ifdef SYSV #include #ifndef _AIX #define rindex(s,c) strrchr(s,c) #endif #else /*SYSV */ #include #endif /*SYSV */ void *malloc(int); void free(void *); /* Definitions. */ #define pbm_allocarray( cols, rows ) \ ((bit**) pm_allocarray( cols, rows, sizeof(bit) )) #define pbm_allocrow( cols ) ((bit*) pm_allocrow( cols, sizeof(bit) )) #define pbm_freearray( bits, rows ) pm_freearray( (char**) bits, rows ) #define pbm_freerow( bitrow ) pm_freerow( (char*) bitrow ) #define pgm_allocarray( cols, rows ) \ ((gray**) pm_allocarray( cols, rows, sizeof(gray) )) #define pgm_allocrow( cols ) ((gray*) pm_allocrow( cols, sizeof(gray) )) #define pgm_freearray( grays, rows ) pm_freearray( (char**) grays, rows ) #define pgm_freerow( grayrow ) pm_freerow( (char*) grayrow ) #define ppm_allocarray( cols, rows ) \ ((pixel**) pm_allocarray( cols, rows, sizeof(pixel) )) #define ppm_allocrow( cols ) ((pixel*) pm_allocrow( cols, sizeof(pixel) )) #define ppm_freearray( pixels, rows ) pm_freearray( (char**) pixels, rows ) #define ppm_freerow( pixelrow ) pm_freerow( (char*) pixelrow ) /* Variables. */ static char *progname; /* Variable-sized arrays. */ char * pm_allocrow(int cols, int size) { register char *itrow; itrow = (char *) malloc(cols * size); if (itrow == (char *) 0) { fprintf(stderr, "%s: out of memory allocating a row\n", progname); return (char *) 0; } return itrow; } void pm_freerow(char *itrow) { free(itrow); } char ** pm_allocarray(int cols, int rows, int size) { char **its; int i; its = (char **) malloc(rows * sizeof(char *)); if (its == (char **) 0) { fprintf(stderr, "%s: out of memory allocating an array\n", progname); return (char **) 0; } its[0] = (char *) malloc(rows * cols * size); if (its[0] == (char *) 0) { fprintf(stderr, "%s: out of memory allocating an array\n", progname); free((char *) its); return (char **) 0; } for (i = 1; i < rows; ++i) its[i] = &(its[0][i * cols * size]); return its; } void pm_freearray(char **its, int rows) { free(its[0]); free(its); } /* File open/close that handles "-" as stdin and checks errors. */ static void pm_perror(char *reason) { #if defined(BSD4_4) __const extern char *__const sys_errlist[]; #else extern __const char *__const sys_errlist[]; /* extern char *sys_errlist[]; */ #endif extern int errno; char *e; e = sys_errlist[errno]; if (reason != 0 && reason[0] != '\0') fprintf(stderr, "%s: %s - %s\n", progname, reason, e); else fprintf(stderr, "%s: %s\n", progname, e); } FILE * pm_openr(char *name) { FILE *f; if (strcmp(name, "-") == 0) f = stdin; else { f = fopen(name, "r"); if (f == NULL) { pm_perror(name); return (FILE *) 0; } } return f; } FILE * pm_openw(char *name) { FILE *f; f = fopen(name, "w"); if (f == NULL) { pm_perror(name); return (FILE *) 0; } return f; } int pm_closer(FILE * f) { if (ferror(f)) { fprintf(stderr, "%s: a file read error occurred at some point\n", progname); return -1; } if (f != stdin) if (fclose(f) != 0) { pm_perror("fclose"); return -1; } return 0; } int pm_closew(FILE * f) { fflush(f); if (ferror(f)) { fprintf(stderr, "%s: a file write error occurred at some point\n", progname); return -1; } if (f != stdout) if (fclose(f) != 0) { pm_perror("fclose"); return -1; } return 0; } static int pbm_getc(FILE * file) { register int ich; ich = getc(file); if (ich == EOF) { fprintf(stderr, "%s: EOF / read error\n", progname); return EOF; } if (ich == '#') { do { ich = getc(file); if (ich == EOF) { fprintf(stderr, "%s: EOF / read error\n", progname); return EOF; } } while (ich != '\n' && ich != '\r'); } return ich; } static bit pbm_getbit(FILE * file) { register int ich; do { ich = pbm_getc(file); if (ich == EOF) return -1; } while (ich == ' ' || ich == '\t' || ich == '\n' || ich == '\r'); if (ich != '0' && ich != '1') { fprintf(stderr, "%s: junk in file where bits should be\n", progname); return -1; } return (ich == '1') ? 1 : 0; } static int pbm_readmagicnumber(FILE * file) { int ich1, ich2; ich1 = getc(file); if (ich1 == EOF) { fprintf(stderr, "%s: EOF / read error reading magic number\n", progname); return -1; } ich2 = getc(file); if (ich2 == EOF) { fprintf(stderr, "%s: EOF / read error reading magic number\n", progname); return -1; } return ich1 * 256 + ich2; } static int pbm_getint(FILE * file) { register char ich; register int i; do { ich = pbm_getc(file); if (ich == EOF) return -1; } while (ich == ' ' || ich == '\t' || ich == '\n' || ich == '\r'); if (ich < '0' || ich > '9') { fprintf(stderr, "%s: junk in file where an integer should be\n", progname); return -1; } i = 0; do { i = i * 10 + ich - '0'; ich = pbm_getc(file); if (ich == EOF) return -1; } while (ich >= '0' && ich <= '9'); return i; } static int pbm_readpbminitrest(FILE * file, int *colsP, int *rowsP) { /* Read size. */ *colsP = pbm_getint(file); *rowsP = pbm_getint(file); if (*colsP == -1 || *rowsP == -1) return -1; return 0; } static int pbm_getrawbyte(FILE * file) { register int iby; iby = getc(file); if (iby == EOF) { fprintf(stderr, "%s: EOF / read error\n", progname); return -1; } return iby; } static int pbm_readpbmrow(FILE * file, bit * bitrow, int cols, int format) { register int col, bitshift, b; register int item; register bit *bP; switch (format) { case PBM_FORMAT: for (col = 0, bP = bitrow; col < cols; ++col, ++bP) { b = pbm_getbit(file); if (b == -1) return -1; *bP = b; } break; case RPBM_FORMAT: bitshift = -1; for (col = 0, bP = bitrow; col < cols; ++col, ++bP) { if (bitshift == -1) { item = pbm_getrawbyte(file); if (item == -1) return -1; bitshift = 7; } *bP = (item >> bitshift) & 1; --bitshift; } break; default: fprintf(stderr, "%s: can't happen\n", progname); return -1; } return 0; } static void pbm_writepbminit(FILE * file, int cols, int rows, int forceplain) { if (!forceplain) fprintf(file, "%c%c\n%d %d\n", PBM_MAGIC1, RPBM_MAGIC2, cols, rows); else fprintf(file, "%c%c\n%d %d\n", PBM_MAGIC1, PBM_MAGIC2, cols, rows); } static void pbm_writepbmrowraw(FILE * file, bit * bitrow, int cols) { register int col, bitshift; register unsigned char item; register bit *bP; bitshift = 7; item = 0; for (col = 0, bP = bitrow; col < cols; ++col, ++bP) { if (*bP) item += 1 << bitshift; --bitshift; if (bitshift == -1) { putc(item, file); bitshift = 7; item = 0; } } if (bitshift != 7) putc(item, file); } static void pbm_writepbmrowplain(FILE * file, bit * bitrow, int cols) { register int col, charcount; register bit *bP; charcount = 0; for (col = 0, bP = bitrow; col < cols; ++col, ++bP) { if (charcount >= 70) { putc('\n', file); charcount = 0; } putc(*bP ? '1' : '0', file); ++charcount; } putc('\n', file); } static void pbm_writepbmrow(FILE * file, bit * bitrow, int cols, int forceplain) { if (!forceplain) pbm_writepbmrowraw(file, bitrow, cols); else pbm_writepbmrowplain(file, bitrow, cols); } static int pgm_readpgminitrest(FILE * file, int *colsP, int *rowsP, gray * maxvalP) { int maxval; /* Read size. */ *colsP = pbm_getint(file); *rowsP = pbm_getint(file); if (*colsP == -1 || *rowsP == -1) return -1; /* Read maxval. */ maxval = pbm_getint(file); if (maxval == -1) return -1; if (maxval > PGM_MAXMAXVAL) { fprintf(stderr, "%s: maxval is too large\n", progname); return -1; } *maxvalP = maxval; return 0; } static int pgm_readpgmrow(FILE * file, gray * grayrow, int cols, gray maxval, int format) { register int col, val; register gray *gP; switch (format) { case PGM_FORMAT: for (col = 0, gP = grayrow; col < cols; ++col, ++gP) { val = pbm_getint(file); if (val == -1) return -1; *gP = val; } break; case RPGM_FORMAT: if (fread(grayrow, 1, cols, file) != cols) { fprintf(stderr, "%s: EOF / read error\n", progname); return -1; } break; default: fprintf(stderr, "%s: can't happen\n", progname); return -1; } return 0; } static void pgm_writepgminit(FILE * file, int cols, int rows, gray maxval, int forceplain) { if (maxval <= 255 && !forceplain) fprintf( file, "%c%c\n%d %d\n%d\n", PGM_MAGIC1, RPGM_MAGIC2, cols, rows, maxval); else fprintf( file, "%c%c\n%d %d\n%d\n", PGM_MAGIC1, PGM_MAGIC2, cols, rows, maxval); } static void putus(short unsigned int n, FILE * file) { if (n >= 10) putus(n / 10, file); putc(n % 10 + '0', file); } static int pgm_writepgmrowraw(FILE * file, gray * grayrow, int cols, gray maxval) { if (fwrite(grayrow, 1, cols, file) != cols) { fprintf(stderr, "%s: write error\n", progname); return -1; } return 0; } static int pgm_writepgmrowplain(FILE * file, gray * grayrow, int cols, gray maxval) { register int col, charcount; register gray *gP; charcount = 0; for (col = 0, gP = grayrow; col < cols; ++col, ++gP) { if (charcount >= 65) { putc('\n', file); charcount = 0; } else if (charcount > 0) { putc(' ', file); ++charcount; } putus((unsigned short) *gP, file); charcount += 3; } if (charcount > 0) putc('\n', file); return 0; } static int pgm_writepgmrow(FILE * file, gray * grayrow, int cols, gray maxval, int forceplain) { if (maxval <= 255 && !forceplain) return pgm_writepgmrowraw(file, grayrow, cols, maxval); else return pgm_writepgmrowplain(file, grayrow, cols, maxval); } static int ppm_readppminitrest(FILE * file, int *colsP, int *rowsP, pixval * maxvalP) { int maxval; /* Read size. */ *colsP = pbm_getint(file); *rowsP = pbm_getint(file); if (*colsP == -1 || *rowsP == -1) return -1; /* Read maxval. */ maxval = pbm_getint(file); if (maxval == -1) return -1; if (maxval > PPM_MAXMAXVAL) { fprintf(stderr, "%s: maxval is too large\n", progname); return -1; } *maxvalP = maxval; return 0; } static int ppm_readppmrow(FILE * file, pixel * pixelrow, int cols, pixval maxval, int format) { register int col; register pixel *pP; register int r, g, b; gray *grayrow; register gray *gP; switch (format) { case PPM_FORMAT: for (col = 0, pP = pixelrow; col < cols; ++col, ++pP) { r = pbm_getint(file); g = pbm_getint(file); b = pbm_getint(file); if (r == -1 || g == -1 || b == -1) return -1; PPM_ASSIGN(*pP, r, g, b); } break; case RPPM_FORMAT: grayrow = pgm_allocrow(3 * cols); if (grayrow == (gray *) 0) return -1; if (fread(grayrow, 1, 3 * cols, file) != 3 * cols) { fprintf(stderr, "%s: EOF / read error\n", progname); return -1; } for (col = 0, gP = grayrow, pP = pixelrow; col < cols; ++col, ++pP) { r = *gP++; g = *gP++; b = *gP++; PPM_ASSIGN(*pP, r, g, b); } pgm_freerow(grayrow); break; default: fprintf(stderr, "%s: can't happen\n", progname); return -1; } return 0; } static void ppm_writeppminit(FILE * file, int cols, int rows, pixval maxval, int forceplain) { if (maxval <= 255 && !forceplain) fprintf( file, "%c%c\n%d %d\n%d\n", PPM_MAGIC1, RPPM_MAGIC2, cols, rows, maxval); else fprintf( file, "%c%c\n%d %d\n%d\n", PPM_MAGIC1, PPM_MAGIC2, cols, rows, maxval); } static int ppm_writeppmrowraw(FILE * file, pixel * pixelrow, int cols, pixval maxval) { register int col; register pixel *pP; gray *grayrow; register gray *gP; grayrow = pgm_allocrow(3 * cols); if (grayrow == (gray *) 0) return -1; for (col = 0, pP = pixelrow, gP = grayrow; col < cols; ++col, ++pP) { *gP++ = PPM_GETR(*pP); *gP++ = PPM_GETG(*pP); *gP++ = PPM_GETB(*pP); } if (fwrite(grayrow, 1, 3 * cols, file) != 3 * cols) { fprintf(stderr, "%s: write error\n", progname); return -1; } pgm_freerow(grayrow); return 0; } static int ppm_writeppmrowplain(FILE * file, pixel * pixelrow, int cols, pixval maxval) { register int col, charcount; register pixel *pP; register pixval val; charcount = 0; for (col = 0, pP = pixelrow; col < cols; ++col, ++pP) { if (charcount >= 65) { putc('\n', file); charcount = 0; } else if (charcount > 0) { putc(' ', file); putc(' ', file); charcount += 2; } val = PPM_GETR(*pP); putus(val, file); putc(' ', file); val = PPM_GETG(*pP); putus(val, file); putc(' ', file); val = PPM_GETB(*pP); putus(val, file); charcount += 11; } if (charcount > 0) putc('\n', file); return 0; } static int ppm_writeppmrow(FILE * file, pixel * pixelrow, int cols, pixval maxval, int forceplain) { if (maxval <= 255 && !forceplain) return ppm_writeppmrowraw(file, pixelrow, cols, maxval); else return ppm_writeppmrowplain(file, pixelrow, cols, maxval); } void pnm_init2(char *pn) { /* Save program name. */ progname = pn; } xelval pnm_pbmmaxval = 1; int pnm_readpnminit(FILE * file, int *colsP, int *rowsP, xelval * maxvalP, int *formatP) { gray gmaxval; /* Check magic number. */ *formatP = pbm_readmagicnumber(file); if (*formatP == -1) return -1; switch (PNM_FORMAT_TYPE(*formatP)) { case PPM_TYPE: if (ppm_readppminitrest(file, colsP, rowsP, (pixval *) maxvalP) < 0) return -1; break; case PGM_TYPE: if (pgm_readpgminitrest(file, colsP, rowsP, &gmaxval) < 0) return -1; *maxvalP = (xelval) gmaxval; break; case PBM_TYPE: if (pbm_readpbminitrest(file, colsP, rowsP) < 0) return -1; *maxvalP = pnm_pbmmaxval; break; default: fprintf( stderr, "%s: bad magic number - not a ppm, pgm, or pbm file\n", progname); return -1; } return 0; } int pnm_readpnmrow(FILE * file, xel * xelrow, int cols, xelval maxval, int format) { register int col; register xel *xP; gray *grayrow; register gray *gP; bit *bitrow; register bit *bP; switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: if (ppm_readppmrow(file, (pixel *) xelrow, cols, (pixval) maxval, format) < 0) return -1; break; case PGM_TYPE: grayrow = pgm_allocrow(cols); if (grayrow == (gray *) 0) return -1; if (pgm_readpgmrow(file, grayrow, cols, (gray) maxval, format) < 0) return -1; for (col = 0, xP = xelrow, gP = grayrow; col < cols; ++col, ++xP, ++gP) PNM_ASSIGN1(*xP, *gP); pgm_freerow(grayrow); break; case PBM_TYPE: bitrow = pbm_allocrow(cols); if (bitrow == (bit *) 0) return -1; if (pbm_readpbmrow(file, bitrow, cols, format) < 0) { pbm_freerow(bitrow); return -1; } for (col = 0, xP = xelrow, bP = bitrow; col < cols; ++col, ++xP, ++bP) PNM_ASSIGN1(*xP, *bP == PBM_BLACK ? 0 : pnm_pbmmaxval); pbm_freerow(bitrow); break; default: fprintf(stderr, "%s: can't happen\n", progname); return -1; } return 0; } xel ** pnm_readpnm(FILE * file, int *colsP, int *rowsP, xelval * maxvalP, int *formatP) { xel **xels; int row; if (pnm_readpnminit(file, colsP, rowsP, maxvalP, formatP) < 0) return (xel **) 0; xels = pnm_allocarray(*colsP, *rowsP); if (xels == (xel **) 0) return (xel **) 0; for (row = 0; row < *rowsP; ++row) if (pnm_readpnmrow(file, xels[row], *colsP, *maxvalP, *formatP) < 0) { pnm_freearray(xels, *rowsP); return (xel **) 0; } return xels; } int pnm_writepnminit(FILE * file, int cols, int rows, xelval maxval, int format, int forceplain) { switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: ppm_writeppminit(file, cols, rows, (pixval) maxval, forceplain); break; case PGM_TYPE: pgm_writepgminit(file, cols, rows, (gray) maxval, forceplain); break; case PBM_TYPE: pbm_writepbminit(file, cols, rows, forceplain); break; default: fprintf(stderr, "%s: can't happen\n", progname); return -1; } return 0; } int pnm_writepnmrow(FILE * file, xel * xelrow, int cols, xelval maxval, int format, int forceplain) { register int col; register xel *xP; gray *grayrow; register gray *gP; bit *bitrow; register bit *bP; switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: if (ppm_writeppmrow(file, (pixel *) xelrow, cols, (pixval) maxval, forceplain) < 0) return -1; break; case PGM_TYPE: grayrow = pgm_allocrow(cols); if (grayrow == (gray *) 0) return -1; for (col = 0, gP = grayrow, xP = xelrow; col < cols; ++col, ++gP, ++xP) *gP = PNM_GET1(*xP); if (pgm_writepgmrow(file, grayrow, cols, (gray) maxval, forceplain) < 0) { pgm_freerow(grayrow); return -1; } pgm_freerow(grayrow); break; case PBM_TYPE: bitrow = pbm_allocrow(cols); if (bitrow == (bit *) 0) return -1; for (col = 0, bP = bitrow, xP = xelrow; col < cols; ++col, ++bP, ++xP) *bP = PNM_GET1(*xP) == 0 ? PBM_BLACK : PBM_WHITE; pbm_writepbmrow(file, bitrow, cols, forceplain); pbm_freerow(bitrow); break; default: fprintf(stderr, "%s: can't happen\n", progname); return -1; } return 0; } int pnm_writepnm(FILE * file, xel ** xels, int cols, int rows, xelval maxval, int format, int forceplain) { int row; if (pnm_writepnminit(file, cols, rows, maxval, format, forceplain) < 0) return -1; for (row = 0; row < rows; ++row) if (pnm_writepnmrow(file, xels[row], cols, maxval, format, forceplain) < 0) return -1; return 0; } /* Colormap stuff. */ #define HASH_SIZE 20023 #define ppm_hashpixel(p) ((((long) PPM_GETR(p)*33023 + \ (long) PPM_GETG(p)*30013 + \ (long) PPM_GETB(p)*27011) & 0x7fffffff) % HASH_SIZE) colorhist_vector ppm_computecolorhist(pixel ** pixels, int cols, int rows, int maxcolors, int *colorsP) { colorhash_table cht; colorhist_vector chv; cht = ppm_computecolorhash(pixels, cols, rows, maxcolors, colorsP); if (cht == (colorhash_table) 0) return (colorhist_vector) 0; chv = ppm_colorhashtocolorhist(cht, maxcolors); ppm_freecolorhash(cht); return chv; } void ppm_addtocolorhist(colorhist_vector chv, int *colorsP, int maxcolors, pixel * colorP, int value, int position) { int i, j; /* Search colorhist for the color. */ for (i = 0; i < *colorsP; ++i) if (PPM_EQUAL(chv[i].color, *colorP)) { /* Found it - move to new slot. */ if (position > i) { for (j = i; j < position; ++j) chv[j] = chv[j + 1]; } else if (position < i) { for (j = i; j > position; --j) chv[j] = chv[j - 1]; } chv[position].color = *colorP; chv[position].value = value; return; } if (*colorsP < maxcolors) { /* Didn't find it, but there's room to add it; so do so. */ for (i = *colorsP; i > position; --i) chv[i] = chv[i - 1]; chv[position].color = *colorP; chv[position].value = value; ++(*colorsP); } } colorhash_table ppm_computecolorhash(pixel ** pixels, int cols, int rows, int maxcolors, int *colorsP) { colorhash_table cht; register pixel *pP; colorhist_list chl; int col, row, hash; cht = ppm_alloccolorhash(); if (cht == (colorhash_table) 0) return (colorhash_table) 0; *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 == 0) { fprintf(stderr, "%s: out of memory computing hash table\n", progname); ppm_freecolorhash(cht); return (colorhash_table) 0; } chl->ch.color = *pP; chl->ch.value = 1; chl->next = cht[hash]; cht[hash] = chl; } } return cht; } colorhash_table ppm_alloccolorhash(void) { colorhash_table cht; int i; cht = (colorhash_table) malloc(HASH_SIZE * sizeof(colorhist_list)); if (cht == 0) { fprintf(stderr, "%s: out of memory allocating hash table\n", progname); return (colorhash_table) 0; } for (i = 0; i < HASH_SIZE; ++i) cht[i] = (colorhist_list) 0; return cht; } int ppm_addtocolorhash(colorhash_table cht, pixel * colorP, int value) { register int hash; register colorhist_list chl; chl = (colorhist_list) malloc(sizeof(struct colorhist_list_item)); if (chl == 0) return -1; hash = ppm_hashpixel(*colorP); chl->ch.color = *colorP; chl->ch.value = value; chl->next = cht[hash]; cht[hash] = chl; return 0; } colorhist_vector ppm_colorhashtocolorhist(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 == (colorhist_vector) 0) { fprintf(stderr, "%s: out of memory generating histogram\n", progname); return (colorhist_vector) 0; } /* 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; } /* All done. */ return chv; } colorhash_table ppm_colorhisttocolorhash(colorhist_vector chv, int colors) { colorhash_table cht; int i, hash; pixel color; colorhist_list chl; cht = ppm_alloccolorhash(); if (cht == (colorhash_table) 0) return (colorhash_table) 0; for (i = 0; i < colors; ++i) { color = chv[i].color; hash = ppm_hashpixel(color); for (chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next) if (PPM_EQUAL(chl->ch.color, color)) { fprintf(stderr, "%s: same color found twice - %d %d %d\n", progname, PPM_GETR(color), PPM_GETG(color), PPM_GETB(color)); ppm_freecolorhash(cht); return (colorhash_table) 0; } chl = (colorhist_list) malloc(sizeof(struct colorhist_list_item)); if (chl == (colorhist_list) 0) { fprintf(stderr, "%s: out of memory\n", progname); ppm_freecolorhash(cht); return (colorhash_table) 0; } chl->ch.color = color; chl->ch.value = i; chl->next = cht[hash]; cht[hash] = chl; } return cht; } int ppm_lookupcolor(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; } void ppm_freecolorhist(colorhist_vector chv) { free((char *) chv); } void ppm_freecolorhash(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); }