diff options
-rw-r--r-- | lib/libpamcolor.c | 91 | ||||
-rw-r--r-- | lib/libppmcolor.c | 405 | ||||
-rw-r--r-- | lib/pam.h | 5 |
3 files changed, 133 insertions, 368 deletions
diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c index 95c9e18f..3cef5e2d 100644 --- a/lib/libpamcolor.c +++ b/lib/libpamcolor.c @@ -20,6 +20,7 @@ #include <string.h> #include <limits.h> +#include <math.h> #include "netpbm/pm_c_util.h" #include "netpbm/mallocvar.h" @@ -155,6 +156,50 @@ parseNewDecX11(const char * const colorname, static void +parseInteger(const char * const colorname, + tuplen const color) { + + unsigned int maxval; + unsigned int r, g, b; + int rc; + + rc = sscanf(colorname, "rgb-%u:%u/%u/%u", &maxval, &r, &g, &b); + + if (rc != 4) + pm_error("invalid color specifier '%s'. " + "If it starts with \"rgb-\", then it must have the format " + "rgb-<MAXVAL>:<RED>:<GRN>:<BLU>, " + "where <MAXVAL>, <RED>, <GRN>, and <BLU> are " + "unsigned integers", + colorname); + + if (maxval < 1 || maxval > PNM_OVERALLMAXVAL) + pm_error("Maxval in color specification '%s' is %u, " + "which is invalid because it is not between " + "1 and %u, inclusive", + colorname, maxval, PNM_OVERALLMAXVAL); + + if (r > maxval) + pm_error("Red value in color specification '%s' is %u, " + "whcih is invalid because the specified maxval is %u", + colorname, r, maxval); + if (g > maxval) + pm_error("Green value in color specification '%s' is %u, " + "whcih is invalid because the specified maxval is %u", + colorname, g, maxval); + if (b > maxval) + pm_error("Blue value in color specification '%s' is %u, " + "whcih is invalid because the specified maxval is %u", + colorname, b, maxval); + + color[PAM_RED_PLANE] = (float)r/maxval; + color[PAM_GRN_PLANE] = (float)g/maxval; + color[PAM_BLU_PLANE] = (float)b/maxval; +} + + + +static void parseOldX11(const char * const colorname, tuplen const color) { /*---------------------------------------------------------------------------- @@ -263,12 +308,15 @@ pnm_parsecolorn(const char * const colorname) { MALLOCARRAY_NOFAIL(retval, 3); - if (strncmp(colorname, "rgb:", 4) == 0) + if (strneq(colorname, "rgb:", 4)) /* It's a new-X11-style hexadecimal rgb specifier. */ parseNewHexX11(colorname, retval); - else if (strncmp(colorname, "rgbi:", 5) == 0) + else if (strneq(colorname, "rgbi:", 5)) /* It's a new-X11-style decimal/float rgb specifier. */ parseNewDecX11(colorname, retval); + else if (strneq(colorname, "rgb-", 4)) + /* It's a Netpbm-native decimal integer rgb specifier */ + parseInteger(colorname, retval); else if (colorname[0] == '#') /* It's an old-X11-style hexadecimal rgb specifier. */ parseOldX11(colorname, retval); @@ -285,9 +333,29 @@ pnm_parsecolorn(const char * const colorname) { +static void +warnIfNotExact(const char * const colorname, + tuple const rounded, + tuplen const exact, + sample const maxval, + unsigned int const plane) { + + float const epsilon = 1.0/65536.0; + + if (fabs((float)(rounded[plane] / maxval) - exact[plane]) > epsilon) { + pm_message("WARNING: Component %u of color '%s' is %f, " + "which cannot be represented precisely with maxval %lu. " + "Approximating as %lu.", + plane, colorname, exact[plane], maxval, rounded[plane]); + } +} + + + tuple -pnm_parsecolor(const char * const colorname, - sample const maxval) { +pnm_parsecolor2(const char * const colorname, + sample const maxval, + int const closeOk) { tuple retval; tuplen color; @@ -306,6 +374,12 @@ pnm_parsecolor(const char * const colorname, retval[PAM_GRN_PLANE] = ROUNDU(color[PAM_GRN_PLANE] * maxval); retval[PAM_BLU_PLANE] = ROUNDU(color[PAM_BLU_PLANE] * maxval); + if (!closeOk) { + warnIfNotExact(colorname, retval, color, maxval, PAM_RED_PLANE); + warnIfNotExact(colorname, retval, color, maxval, PAM_GRN_PLANE); + warnIfNotExact(colorname, retval, color, maxval, PAM_BLU_PLANE); + } + free(color); return retval; @@ -313,6 +387,15 @@ pnm_parsecolor(const char * const colorname, +tuple +pnm_parsecolor(const char * const colorname, + sample const maxval) { + + return pnm_parsecolor2(colorname, maxval, true); +} + + + const char * pnm_colorname(struct pam * const pamP, tuple const color, diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c index f37ec564..df9e5295 100644 --- a/lib/libppmcolor.c +++ b/lib/libppmcolor.c @@ -23,343 +23,7 @@ #include "netpbm/nstring.h" #include "ppm.h" #include "colorname.h" - - -static void -computeHexTable(int hexit[]) { - - unsigned int i; - - for ( i = 0; i < 256; ++i ) - hexit[i] = -1; - - hexit['0'] = 0; - hexit['1'] = 1; - hexit['2'] = 2; - hexit['3'] = 3; - hexit['4'] = 4; - hexit['5'] = 5; - hexit['6'] = 6; - hexit['7'] = 7; - hexit['8'] = 8; - hexit['9'] = 9; - hexit['a'] = hexit['A'] = 10; - hexit['b'] = hexit['B'] = 11; - hexit['c'] = hexit['C'] = 12; - hexit['d'] = hexit['D'] = 13; - hexit['e'] = hexit['E'] = 14; - hexit['f'] = hexit['F'] = 15; -} - - - -static long -invRgbnorm(pixval const rgb, - pixval const maxval, - unsigned int const hexDigits) { -/*---------------------------------------------------------------------------- - This is the inverse of 'rgbnorm', below. ------------------------------------------------------------------------------*/ - long retval; - - switch (hexDigits) { - case 1: - retval = (long)((double) rgb * 15 / maxval + 0.5); - break; - case 2: - retval = (long) ((double) rgb * 255 / maxval + 0.5); - break; - case 3: - retval = (long) ((double) rgb * 4095 / maxval + 0.5); - break; - case 4: - retval = (long) ((double) rgb * 65535UL / maxval + 0.5); - break; - default: - pm_message("Internal error in invRgbnorm()"); - abort(); - } - return retval; -} - - - -static pixval -rgbnorm(long const rgb, - pixval const maxval, - unsigned int const hexDigitCount, - bool const closeOk, - const char * const colorname) { -/*---------------------------------------------------------------------------- - Normalize the color (r, g, or b) value 'rgb', which was specified - with 'hexDigitCount' digits, to a maxval of 'maxval'. If the - number of digits isn't valid, issue an error message and identify - the complete color color specification in error as 'colorname'. - - For example, if the user says "0ff" and the maxval is 100, - then rgb is 0xff, n is 3, and our result is - 0xff / (16**3-1) * 100 = 6. ------------------------------------------------------------------------------*/ - pixval retval; - - switch (hexDigitCount) { - case 0: - pm_error("A hexadecimal color specifier in color '%s' is " - "an empty string", colorname); - break; - case 1: - retval = (pixval)((double) rgb * maxval / 15 + 0.5); - break; - case 2: - retval = (pixval) ((double) rgb * maxval / 255 + 0.5); - break; - case 3: - retval = (pixval) ((double) rgb * maxval / 4095 + 0.5); - break; - case 4: - retval = (pixval) ((double) rgb * maxval / 65535L + 0.5); - break; - default: - pm_error("color specifier '%s' has too many digits", colorname); - } - - if (!closeOk) { - long const newrgb = invRgbnorm(retval, maxval, hexDigitCount); - if (newrgb != rgb) - pm_message("WARNING: Component 0x%lx of color '%s' " - "cannot be represented precisely with maxval %u. " - "Approximating as %u.", - rgb, colorname, maxval, retval); - } - return retval; -} - - - -static void -parseHexDigits(const char * const string, - char const delim, - int const hexit[], - pixval * const nP, - unsigned int * const digitCountP) { - - unsigned int digitCount; - pixval n; - - digitCount = 0; /* initial value */ - n = 0; /* initial value */ - while (string[digitCount] != delim) { - char const digit = string[digitCount]; - if (digit == '\0') - pm_error("rgb: color spec ends prematurely"); - else { - int const hexval = hexit[(unsigned int)digit]; - if (hexval == -1) - pm_error("Invalid hex digit in rgb: color spec: 0x%02x", - digit); - n = n * 16 + hexval; - ++digitCount; - } - } - *nP = n; - *digitCountP = digitCount; -} - - - -static void -parseNewHexX11(char const colorname[], - pixval const maxval, - bool const closeOk, - pixel * const colorP) { -/*---------------------------------------------------------------------------- - Determine what color colorname[] specifies in the new style hex - color specification format (e.g. rgb:55/40/55). - - Return that color as *colorP. - - Assume colorname[] starts with "rgb:", but otherwise it might be - gibberish. ------------------------------------------------------------------------------*/ - int hexit[256]; - - const char * cp; - pixval n; - unsigned int digitCount; - pixval rNorm, gNorm, bNorm; - - computeHexTable(hexit); - - cp = &colorname[4]; - - parseHexDigits(cp, '/', hexit, &n, &digitCount); - - rNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname); - - cp += digitCount; - ++cp; /* Skip the slash */ - - parseHexDigits(cp, '/', hexit, &n, &digitCount); - - gNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname); - - cp += digitCount; - ++cp; /* Skip the slash */ - - parseHexDigits(cp, '\0', hexit, &n, &digitCount); - - bNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname); - - PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm); -} - - - -static void -parseNewDecX11(char const colorname[], - pixval const maxval, - bool const closeOk, - pixel * const colorP) { - - float const epsilon = 1.0/65536.0; - float fr, fg, fb; - pixval rNorm, gNorm, bNorm; - - if (sscanf( colorname, "rgbi:%f/%f/%f", &fr, &fg, &fb) != 3) - pm_error("invalid color specifier '%s'", colorname); - if (fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 - || fb < 0.0 || fb > 1.0) - pm_error("invalid color specifier '%s' - " - "values must be between 0.0 and 1.0", colorname ); - - rNorm = fr * maxval + 0.5; - gNorm = fg * maxval + 0.5; - bNorm = fb * maxval + 0.5; - - if (!closeOk) { - if (fabs((double)rNorm/maxval - fr) > epsilon || - fabs((double)gNorm/maxval - fg) > epsilon || - fabs((double)bNorm/maxval - fb) > epsilon) - pm_message("WARNING: Color '%s' cannot be represented " - "precisely with maxval %u. " - "Approximating as (%u,%u,%u).", - colorname, maxval, rNorm, gNorm, bNorm); - } - PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm); -} - - - -static void -parseOldX11(char const colorname[], - pixval const maxval, - bool const closeOk, - pixel * const colorP) { -/*---------------------------------------------------------------------------- - Return as *colorP the color specified by the old X11 style color - specififier colorname[] (e.g. #554055). ------------------------------------------------------------------------------*/ - int hexit[256]; - long r,g,b; - pixval rNorm, gNorm, bNorm; - - computeHexTable(hexit); - - if (!pm_strishex(&colorname[1])) - pm_error("Non-hexadecimal characters in #-type color specification"); - - switch (strlen(colorname) - 1 /* (Number of hex digits) */) { - case 3: - r = hexit[(int)colorname[1]]; - g = hexit[(int)colorname[2]]; - b = hexit[(int)colorname[3]]; - rNorm = rgbnorm(r, maxval, 1, closeOk, colorname); - gNorm = rgbnorm(g, maxval, 1, closeOk, colorname); - bNorm = rgbnorm(b, maxval, 1, closeOk, colorname); - break; - - case 6: - r = (hexit[(int)colorname[1]] << 4 ) + hexit[(int)colorname[2]]; - g = (hexit[(int)colorname[3]] << 4 ) + hexit[(int)colorname[4]]; - b = (hexit[(int)colorname[5]] << 4 ) + hexit[(int)colorname[6]]; - rNorm = rgbnorm(r, maxval, 2, closeOk, colorname); - gNorm = rgbnorm(g, maxval, 2, closeOk, colorname); - bNorm = rgbnorm(b, maxval, 2, closeOk, colorname); - break; - - case 9: - r = (hexit[(int)colorname[1]] << 8) + - (hexit[(int)colorname[2]] << 4) + - (hexit[(int)colorname[3]] << 0); - g = (hexit[(int)colorname[4]] << 8) + - (hexit[(int)colorname[5]] << 4) + - (hexit[(int)colorname[6]] << 0); - b = (hexit[(int)colorname[7]] << 8) + - (hexit[(int)colorname[8]] << 4) + - (hexit[(int)colorname[9]] << 0); - rNorm = rgbnorm(r, maxval, 3, closeOk, colorname); - gNorm = rgbnorm(g, maxval, 3, closeOk, colorname); - bNorm = rgbnorm(b, maxval, 3, closeOk, colorname); - break; - - case 12: - r = (hexit[(int)colorname[1]] << 12) + - (hexit[(int)colorname[2]] << 8) + - (hexit[(int)colorname[3]] << 4) + hexit[(int)colorname[4]]; - g = (hexit[(int)colorname[5]] << 12) + - (hexit[(int)colorname[6]] << 8) + - (hexit[(int)colorname[7]] << 4) + hexit[(int)colorname[8]]; - b = (hexit[(int)colorname[9]] << 12) + - (hexit[(int)colorname[10]] << 8) + - (hexit[(int)colorname[11]] << 4) + hexit[(int)colorname[12]]; - rNorm = rgbnorm(r, maxval, 4, closeOk, colorname); - gNorm = rgbnorm(g, maxval, 4, closeOk, colorname); - bNorm = rgbnorm(b, maxval, 4, closeOk, colorname); - break; - - default: - pm_error("invalid color specifier '%s'", colorname); - } - PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm); -} - - - - -static void -parseOldX11Dec(const char colorname[], - pixval const maxval, - bool const closeOk, - pixel * const colorP) { - - float const epsilon = 1.0/65536.0; - - float fr, fg, fb; - pixval rNorm, gNorm, bNorm; - - if (sscanf(colorname, "%f,%f,%f", &fr, &fg, &fb) != 3) - pm_error("invalid color specifier '%s'", colorname); - if (fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 - || fb < 0.0 || fb > 1.0) - pm_error("invalid color specifier '%s' - " - "values must be between 0.0 and 1.0", colorname ); - - rNorm = fr * maxval + 0.5; - gNorm = fg * maxval + 0.5; - bNorm = fb * maxval + 0.5; - - if (!closeOk) { - if (fabs((float)rNorm/maxval - fr) > epsilon || - fabs((float)gNorm/maxval - fg) > epsilon || - fabs((float)bNorm/maxval - fb) > epsilon) - pm_message("WARNING: Color '%s' cannot be represented " - "precisely with maxval %u. " - "Approximating as (%u,%u,%u).", - colorname, maxval, rNorm, gNorm, bNorm); - } - PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm); -} - +#include "pam.h" pixel @@ -367,26 +31,17 @@ ppm_parsecolor2(const char * const colorname, pixval const maxval, int const closeOk) { - pixel color; - - if (strncmp(colorname, "rgb:", 4) == 0) - /* It's a new-X11-style hexadecimal rgb specifier. */ - parseNewHexX11(colorname, maxval, closeOk, &color); - else if (strncmp(colorname, "rgbi:", 5) == 0) - /* It's a new-X11-style decimal/float rgb specifier. */ - parseNewDecX11(colorname, maxval, closeOk, &color); - else if (colorname[0] == '#') - /* It's an old-X11-style hexadecimal rgb specifier. */ - parseOldX11(colorname, maxval, closeOk, &color); - else if ((colorname[0] >= '0' && colorname[0] <= '9') || - colorname[0] == '.') - /* It's an old-style decimal/float rgb specifier. */ - parseOldX11Dec(colorname, maxval, closeOk, &color); - else - /* Must be a name from the X-style rgb file. */ - pm_parse_dictionary_name(colorname, maxval, closeOk, &color); + tuple const color = pnm_parsecolor2(colorname, maxval, closeOk); - return color; + 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; } @@ -408,6 +63,7 @@ ppm_colorname(const pixel * const colorP, int r, g, b; FILE * f; static char colorname[200]; + /* Null string means no suitable name so far */ if (maxval == 255) { r = PPM_GETR(*colorP); @@ -420,7 +76,10 @@ ppm_colorname(const pixel * const colorP, } f = pm_openColornameFile(NULL, !hexok); - if (f != NULL) { + + if (!f) + strcpy(colorname, ""); + else { int bestDiff; bool eof; @@ -441,15 +100,33 @@ ppm_colorname(const pixel * const colorP, eof = TRUE; } fclose(f); - if (bestDiff != 32767 && (bestDiff == 0 || ! hexok)) - return colorname; + + if (bestDiff == 32767) { + /* Color file contain no entries, so we can't even pick a close + one + */ + strcpy(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. + */ + strcpy(colorname, ""); + } } - /* 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); - return 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; +} diff --git a/lib/pam.h b/lib/pam.h index d8d0e5c7..9f999e93 100644 --- a/lib/pam.h +++ b/lib/pam.h @@ -490,6 +490,11 @@ pnm_createungammatransform(const struct pam * const pamP); #define pnm_freeungammatransform pnm_freegammatransform; tuple +pnm_parsecolor2(const char * const colorname, + sample const maxval, + int const closeOk); + +tuple pnm_parsecolor(const char * const colorname, sample const maxval); |