From 182cb1b68e40b2e1d900b3ccd7a5ee7de818a9f4 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Mon, 1 Apr 2024 17:20:01 +0000 Subject: Allow color dictionary with more than 1000 colors; Add Resene paint colors to distributed color dictionary git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@4901 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- lib/libppmcolor.c | 534 +++++++++++++++++++++++++++--------------------------- 1 file changed, 267 insertions(+), 267 deletions(-) (limited to 'lib/libppmcolor.c') diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c index baa1d568..04858f6a 100644 --- a/lib/libppmcolor.c +++ b/lib/libppmcolor.c @@ -23,182 +23,98 @@ #include "pam.h" -pixel -ppm_parsecolor2(const char * const colorname, - pixval const maxval, - int const closeOk) { - - tuple const color = pnm_parsecolor2(colorname, maxval, closeOk); - - pixel retval; - - PPM_PUTR(retval, color[PAM_RED_PLANE]); - PPM_PUTG(retval, color[PAM_GRN_PLANE]); - PPM_PUTB(retval, color[PAM_BLU_PLANE]); - - free(color); - return retval; -} +#define MAXCOLORNAMES 1000u + /* The maximum size of a colornames array, in the old interface */ +static colorhash_table +allocColorHash(void) { + colorhash_table cht; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; -pixel -ppm_parsecolor(const char * const colorname, - pixval const maxval) { + if (setjmp(jmpbuf) != 0) + cht = NULL; + else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + cht = ppm_alloccolorhash(); + } + pm_setjmpbuf(origJmpbufP); - return ppm_parsecolor2(colorname, maxval, true); + return cht; } -char * -ppm_colorname(const pixel * const colorP, - pixval const maxval, - int const hexok) { - - int r, g, b; - FILE * fileP; - static char colorname[200]; - /* Null string means no suitable name so far */ +static ppm_ColorDict * +colorDict_newEmpty() { - if (maxval == 255) { - r = PPM_GETR(*colorP); - g = PPM_GETG(*colorP); - b = PPM_GETB(*colorP); - } else { - r = (int) PPM_GETR(*colorP) * 255 / (int) maxval; - g = (int) PPM_GETG(*colorP) * 255 / (int) maxval; - b = (int) PPM_GETB(*colorP) * 255 / (int) maxval; - } + ppm_ColorDict * colorDictP; - fileP = pm_openColornameFile(NULL, !hexok); + MALLOCVAR_NOFAIL(colorDictP); - if (!fileP) - STRSCPY(colorname, ""); - else { - int bestDiff; - bool eof; + colorDictP->version = 0; + colorDictP->size = 0; + colorDictP->name = NULL; + colorDictP->color = NULL; + colorDictP->cht = allocColorHash(); + colorDictP->count = 0; - for (bestDiff = 32767, eof = false; - !eof && bestDiff > 0; ) { - struct colorfile_entry const ce = pm_colorget(fileP); - if (ce.colorname) { - int const thisDiff = - abs(r - (int)ce.r) + - abs(g - (int)ce.g) + - abs(b - (int)ce.b); + if (!colorDictP->cht) + pm_error("Unable to allocate space for color hash"); - if (thisDiff < bestDiff) { - bestDiff = thisDiff; - STRSCPY(colorname, ce.colorname); - } - } else - eof = true; - } - fclose(fileP); - - if (bestDiff == 32767) { - /* Color file contain no entries, so we can't even pick a close - one - */ - STRSCPY(colorname, ""); - } else if (bestDiff > 0 && hexok) { - /* We didn't find an exact match and user is willing to accept - hex, so we don't have to use an approximate match. - */ - STRSCPY(colorname, ""); - } - } - - if (streq(colorname, "")) { - if (hexok) { - /* Color lookup failed, but caller is willing to take an X11-style - hex specifier, so return that. - */ - sprintf(colorname, "#%02x%02x%02x", r, g, b); - } else { - pm_error("Couldn't find any name colors at all"); - } - } - - return colorname; + return colorDictP; } -#define MAXCOLORNAMES 1000u - -static const char ** -allocColorNames() { - - const char ** colornames; - - MALLOCARRAY(colornames, MAXCOLORNAMES); - - if (colornames) { - unsigned int i; - for (i = 0; i < MAXCOLORNAMES; ++i) - colornames[i] = NULL; - } - return colornames; -} +void +ppm_colorDict_destroy(ppm_ColorDict * const colorDictP) { + unsigned int i; + for (i = 0; i < colorDictP->size; ++i) + pm_strfree(colorDictP->name[i]); -static colorhash_table -allocColorHash(void) { + if (colorDictP->name) + free(colorDictP->name); + if (colorDictP->color) + free(colorDictP->color); - colorhash_table cht; - jmp_buf jmpbuf; - jmp_buf * origJmpbufP; - - if (setjmp(jmpbuf) != 0) - cht = NULL; - else { - pm_setjmpbufsave(&jmpbuf, &origJmpbufP); - cht = ppm_alloccolorhash(); - } - pm_setjmpbuf(origJmpbufP); + ppm_freecolorhash(colorDictP->cht); - return cht; + free(colorDictP); } static void -processColorfileEntry(struct colorfile_entry const ce, - colorhash_table const cht, - const char ** const colornames, - pixel * const colors, - unsigned int * const colornameIndexP, - const char ** const errorP) { +colorDict_resize(ppm_ColorDict * const colorDictP, + unsigned int const newSz, + const char ** const errorP) { + + const char ** newName; - if (*colornameIndexP >= MAXCOLORNAMES) - pm_asprintf(errorP, "Too many colors in colorname dictionary. " - "Max allowed is %u", MAXCOLORNAMES); + newName = realloc(colorDictP->name, newSz * sizeof(colorDictP->name[0])); + if (!newName) + pm_asprintf(errorP, "Failed to extend allocation for color " + "dictionary to %u entries", newSz); else { - pixel color; + pixel * newColor; - PPM_ASSIGN(color, ce.r, ce.g, ce.b); + colorDictP->name = newName; - if (ppm_lookupcolor(cht, &color) >= 0) { - /* The color is already in the hash, which means we saw it - earlier in the file. We prefer the first name that the - file gives for each color, so we just ignore the - current entry. - */ + newColor = + realloc(colorDictP->color, newSz * sizeof(colorDictP->color[0])); + + if (!newColor) + pm_asprintf(errorP, "Failed to extend allocation for color " + "dictionary to %u entries", newSz); + else { *errorP = NULL; - } else { - ppm_addtocolorhash(cht, &color, *colornameIndexP); - colornames[*colornameIndexP] = pm_strdup(ce.colorname); - colors[*colornameIndexP] = color; - if (colornames[*colornameIndexP] == pm_strsol) - pm_asprintf(errorP, "Unable to allocate space for color name"); - else { - *errorP = NULL; - ++(*colornameIndexP); - } + colorDictP->color = newColor; + colorDictP->size = newSz; } } } @@ -229,41 +145,41 @@ openColornameFile(const char * const fileName, static void -readOpenColorFile(FILE * const colorFileP, - unsigned int * const nColorsP, - const char ** const colornames, - pixel * const colors, - colorhash_table const cht, - const char ** const errorP) { +processColorfileEntry(struct colorfile_entry const ce, + ppm_ColorDict * const colorDictP, + const char ** const errorP) { /*---------------------------------------------------------------------------- - Read the color dictionary file *colorFileP and add the colors in it - to colornames[], colors[], and 'cht'. - - colornames[] and colors[] must be allocated with MAXCOLORNAMES entries - at entry. - - We may add colors to 'cht' even if we fail. + Add the color file entry 'ce' to *colorDictP as required. -----------------------------------------------------------------------------*/ - unsigned int nColorsDone; - bool done; + pixel color; - for (nColorsDone = 0, done = false, *errorP = NULL; !done && !*errorP; ) { + PPM_ASSIGN(color, ce.r, ce.g, ce.b); - struct colorfile_entry const ce = pm_colorget(colorFileP); - - if (!ce.colorname) - done = true; - else - processColorfileEntry(ce, cht, colornames, colors, - &nColorsDone, errorP); - } - *nColorsP = nColorsDone; + if (ppm_lookupcolor(colorDictP->cht, &color) >= 0) { + /* The color is already in the hash, which means we saw it earlier in + the file. We prefer the first name that the file gives for each + color, so we just ignore the current entry. + */ + *errorP = NULL; + } else { + ppm_addtocolorhash(colorDictP->cht, &color, colorDictP->count); + if (colorDictP->count >= colorDictP->size) { + colorDict_resize(colorDictP, + MAX(1024, colorDictP->size * 2), + errorP); + } else + *errorP = NULL; - if (*errorP) { - unsigned int colorIndex; + assert(colorDictP->size >= colorDictP->count); - for (colorIndex = 0; colorIndex < nColorsDone; ++colorIndex) - pm_strfree(colornames[colorIndex]); + if (!*errorP) { + colorDictP->name[colorDictP->count] = pm_strdup(ce.colorname); + colorDictP->color[colorDictP->count] = color; + if (colorDictP->name[colorDictP->count] == pm_strsol) + pm_asprintf(errorP, "Unable to allocate space for color name"); + else + ++colorDictP->count; + } } } @@ -272,38 +188,37 @@ readOpenColorFile(FILE * const colorFileP, static void readColorFile(const char * const fileName, bool const mustOpen, - unsigned int * const nColorsP, - const char ** const colornames, - pixel * const colors, - colorhash_table const cht, + ppm_ColorDict * const colorDictP, const char ** const errorP) { /*---------------------------------------------------------------------------- Read the color dictionary file named 'fileName' and add the colors in it - to colornames[], colors[], and 'cht'. Return as *nColorsP the number - of colors in it. + to *colorDictP. If the file is not openable (e.g. no file by that name exists), abort the program if 'mustOpen' is true; otherwise, return values indicating a dictionary with no colors. - colornames[] and colors[] must be allocated with MAXCOLORNAMES entries - at entry. - - We may add colors to 'cht' even if we fail. + We may add colors to *colorDictP even if we fail. -----------------------------------------------------------------------------*/ FILE * colorFileP; openColornameFile(fileName, mustOpen, &colorFileP, errorP); if (!*errorP) { - if (colorFileP == NULL) { - /* Couldn't open it, but Caller says treat same as - empty file - */ - *nColorsP = 0; + if (!colorFileP) { + /* Couldn't open it, but Caller says treat same as empty file */ *errorP = NULL; } else { - readOpenColorFile(colorFileP, nColorsP, colornames, colors, cht, - errorP); + bool done; + + for (done = false, *errorP = NULL; !done && !*errorP; ) { + + struct colorfile_entry const ce = pm_colorget(colorFileP); + + if (!ce.colorname) + done = true; + else + processColorfileEntry(ce, colorDictP, errorP); + } fclose(colorFileP); } @@ -312,62 +227,127 @@ readColorFile(const char * const fileName, -static void -readcolordict(const char * const fileName, - bool const mustOpen, - unsigned int * const nColorsP, - const char *** const colorNamesP, - pixel ** const colorsP, - colorhash_table * const chtP, - const char ** const errorP) { -/*---------------------------------------------------------------------------- - Same as 'ppm_readcolordict' except that a) if we fail, we return a message - as *errorP instead of issuing a message; and b) we always return all three - return values. ------------------------------------------------------------------------------*/ - const char ** colorNames; - /* List of all the color names in the dictionary */ +ppm_ColorDict * +ppm_colorDict_new(const char * const fileName, + int const mustOpen) { - colorNames = allocColorNames(); + ppm_ColorDict * colorDictP; + const char * error; - if (colorNames == NULL) - pm_asprintf(errorP, "Unable to allocate space for colorname table."); - else { - pixel * colors; - /* colors[i] is the color that goes with name colorNames[i] */ + colorDictP = colorDict_newEmpty(); - MALLOCARRAY(colors, MAXCOLORNAMES); + readColorFile(fileName, mustOpen, colorDictP, &error); - if (colors == NULL) - pm_asprintf(errorP, "Unable to allocate space for color table."); - else { - colorhash_table cht; - /* Hash table mapping colorNames[] to colors[] */ + if (error) { + pm_errormsg("%s", error); + pm_strfree(error); + pm_longjmp(); + } + return colorDictP; +} - cht = allocColorHash(); - if (cht == NULL) - pm_asprintf(errorP, "Unable to allocate space for color hash"); - else { - readColorFile(fileName, mustOpen, - nColorsP, colorNames, colors, cht, - errorP); - if (*errorP) - ppm_freecolorhash(cht); - else - *chtP = cht; - } - if (*errorP) - free(colors); - else - *colorsP = colors; +pixel +ppm_parsecolor2(const char * const colorname, + pixval const maxval, + int const closeOk) { + + tuple const color = pnm_parsecolor2(colorname, maxval, closeOk); + + pixel retval; + + PPM_PUTR(retval, color[PAM_RED_PLANE]); + PPM_PUTG(retval, color[PAM_GRN_PLANE]); + PPM_PUTB(retval, color[PAM_BLU_PLANE]); + + free(color); + + return retval; +} + + + +pixel +ppm_parsecolor(const char * const colorname, + pixval const maxval) { + + return ppm_parsecolor2(colorname, maxval, true); +} + + + +char * +ppm_colorname(const pixel * const colorP, + pixval const maxval, + int const hexok) { + + int r, g, b; + FILE * fileP; + static char colorname[200]; + /* Null string means no suitable name so far */ + + if (maxval == 255) { + r = PPM_GETR(*colorP); + g = PPM_GETG(*colorP); + b = PPM_GETB(*colorP); + } else { + r = (int) PPM_GETR(*colorP) * 255 / (int) maxval; + g = (int) PPM_GETG(*colorP) * 255 / (int) maxval; + b = (int) PPM_GETB(*colorP) * 255 / (int) maxval; + } + + fileP = pm_openColornameFile(NULL, !hexok); + + if (!fileP) + STRSCPY(colorname, ""); + else { + int bestDiff; + bool eof; + + for (bestDiff = 32767, eof = false; + !eof && bestDiff > 0; ) { + struct colorfile_entry const ce = pm_colorget(fileP); + if (ce.colorname) { + int const thisDiff = + abs(r - (int)ce.r) + + abs(g - (int)ce.g) + + abs(b - (int)ce.b); + + if (thisDiff < bestDiff) { + bestDiff = thisDiff; + STRSCPY(colorname, ce.colorname); + } + } else + eof = true; + } + fclose(fileP); + + if (bestDiff == 32767) { + /* Color file contain no entries, so we can't even pick a close + one + */ + STRSCPY(colorname, ""); + } else if (bestDiff > 0 && hexok) { + /* We didn't find an exact match and user is willing to accept + hex, so we don't have to use an approximate match. + */ + STRSCPY(colorname, ""); + } + } + + if (streq(colorname, "")) { + if (hexok) { + /* Color lookup failed, but caller is willing to take an X11-style + hex specifier, so return that. + */ + sprintf(colorname, "#%02x%02x%02x", r, g, b); + } else { + pm_error("Couldn't find any name colors at all"); } - if (*errorP) - free(colorNames); - else - *colorNamesP = colorNames; } + + return colorname; } @@ -380,10 +360,11 @@ ppm_readcolordict(const char * const fileName, pixel ** const colorsP, colorhash_table * const chtP) { /*---------------------------------------------------------------------------- - Read the color dictionary from the file named 'fileName'. If we can't open - the file (e.g. because it does not exist), and 'mustOpen' is false, return - an empty dictionary (it contains no colors). But if 'mustOpen' is true, - abort the program instead of returning an empty dictionary. + Read the color dictionary from the file named 'fileName' (NULL means + default). If we can't open the file (e.g. because it does not exist), and + 'mustOpen' is false, return an empty dictionary (it contains no colors). + But if 'mustOpen' is true, abort the program instead of returning an empty + dictionary. Return as *nColorsP the number of colors in the dictionary. @@ -392,8 +373,8 @@ ppm_readcolordict(const char * const fileName, The first *nColorsP entries are valid; *chtP contains indices into this array. - Return as *colorsP the colors. *colorsP is a malloced array of size - MAXCOLORS with the first elements filled in and the rest undefined. + Return as *colorsP a malloced array of the colors. (*colorsP)[i] is + the color that goes with (*colorNamesP)[i]. Return as *chtP a color hash table mapping each color in the dictionary to the index into *colorNamesP for the name of the color. @@ -401,35 +382,54 @@ ppm_readcolordict(const char * const fileName, Each of 'nColorsP, 'colorNamesP', and 'colorsP' may be null, in which case we do not return the corresponding information (or allocate memory for it). -----------------------------------------------------------------------------*/ - colorhash_table cht; - const char ** colorNames; - pixel * colors; - unsigned int nColors; - const char * error; + ppm_ColorDict * colorDictP; - readcolordict(fileName, mustOpen, &nColors, &colorNames, &colors, &cht, - &error); + colorDictP = ppm_colorDict_new(fileName, mustOpen); - if (error) { - pm_errormsg("%s", error); - pm_strfree(error); - pm_longjmp(); + if (chtP) + *chtP = colorDictP->cht; + else + ppm_freecolorhash(colorDictP->cht); + + if (colorNamesP) { + /* We have a simplistic, primitive interface where the array must + be exactly MAXCOLORNAMES entries in size with unused entries + set to NULL so that caller can free it with a call to + 'ppm_freecolornames' (which has no size argument). + + So we fail now (as the old interface would) if there are more + than MAXCOLORNAMES colors and expand the array if there are + fewer. + */ + if (colorDictP->count > MAXCOLORNAMES) + pm_error("Too many color names (%u) in color dictionary. " + "Max allowed is %u", + colorDictP->count, MAXCOLORNAMES); + else { + unsigned int i; + + REALLOCARRAY(colorDictP->name, MAXCOLORNAMES); + if (!colorDictP->name) + pm_error("Failed to allocate color name array for " + "maximum colors %u", MAXCOLORNAMES); + for (i = colorDictP->count; i < MAXCOLORNAMES; ++i) + colorDictP->name[i] = NULL; + } + *colorNamesP = colorDictP->name; } else { - if (chtP) - *chtP = cht; - else - ppm_freecolorhash(cht); - if (colorNamesP) - *colorNamesP = colorNames; - else - ppm_freecolornames(colorNames); - if (colorsP) - *colorsP = colors; - else - ppm_freerow(colors); - if (nColorsP) - *nColorsP = nColors; + unsigned int i; + for (i = 0; i < colorDictP->count; ++i) + pm_strfree(colorDictP->name[i]); + free(colorDictP->name); } + + if (colorsP) + *colorsP = colorDictP->color; + else + free(colorDictP->color); + + if (nColorsP) + *nColorsP = colorDictP->count; } -- cgit 1.4.1