From 04f0382f7f28e6456f23b150b60ce2b9fe7f0d98 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Wed, 28 Jun 2017 22:04:00 +0000 Subject: Add pnm_parsecolorn git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@3007 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- lib/colorname.c | 81 ++++++++++------ lib/colorname.h | 10 ++ lib/libpamcolor.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- lib/libppmd.c | 2 +- lib/pam.h | 3 + 5 files changed, 338 insertions(+), 39 deletions(-) diff --git a/lib/colorname.c b/lib/colorname.c index fd265870..b2bc4a83 100644 --- a/lib/colorname.c +++ b/lib/colorname.c @@ -21,8 +21,10 @@ #include #include #include +#include #include "netpbm/nstring.h" +#include "netpbm/mallocvar.h" #include "colorname.h" @@ -187,60 +189,77 @@ pm_colorget(FILE * const f) { void -pm_parse_dictionary_name(char const colorname[], - pixval const maxval, - int const closeOk, - pixel * const colorP) { +pm_parse_dictionary_namen(char const colorname[], + tuplen const color) { - FILE* f; + FILE * fP; bool gotit; bool colorfileExhausted; - struct colorfile_entry colorfile_entry; + struct colorfile_entry colorfileEntry; char * canoncolor; - pixval r,g,b; - f = pm_openColornameFile(NULL, TRUE); /* exits if error */ + fP = pm_openColornameFile(NULL, TRUE); /* exits if error */ canoncolor = strdup(colorname); pm_canonstr(canoncolor); gotit = FALSE; colorfileExhausted = FALSE; while (!gotit && !colorfileExhausted) { - colorfile_entry = pm_colorget(f); - if (colorfile_entry.colorname) { - pm_canonstr(colorfile_entry.colorname); - if (strcmp( canoncolor, colorfile_entry.colorname) == 0) + colorfileEntry = pm_colorget(fP); + if (colorfileEntry.colorname) { + pm_canonstr(colorfileEntry.colorname); + if (streq(canoncolor, colorfileEntry.colorname)) gotit = TRUE; } else colorfileExhausted = TRUE; } - fclose(f); + fclose(fP); if (!gotit) pm_error("unknown color '%s'", colorname); + + color[PAM_RED_PLANE] = (samplen)colorfileEntry.r / PAM_COLORFILE_MAXVAL; + color[PAM_GRN_PLANE] = (samplen)colorfileEntry.g / PAM_COLORFILE_MAXVAL; + color[PAM_BLU_PLANE] = (samplen)colorfileEntry.b / PAM_COLORFILE_MAXVAL; + + free(canoncolor); +} + + + +void +pm_parse_dictionary_name(char const colorname[], + pixval const maxval, + int const closeOk, + pixel * const colorP) { + + double const epsilon = 1.0/65536.0; + + tuplen color; + pixval r, g, b; + + MALLOCARRAY_NOFAIL(color, 3); + + pm_parse_dictionary_namen(colorname, color); + + r = ROUNDU(color[PAM_RED_PLANE] * maxval); + g = ROUNDU(color[PAM_GRN_PLANE] * maxval); + b = ROUNDU(color[PAM_BLU_PLANE] * maxval); - /* Rescale from [0..255] if necessary. */ - if (maxval != 255) { - r = colorfile_entry.r * maxval / 255; - g = colorfile_entry.g * maxval / 255; - b = colorfile_entry.b * maxval / 255; - - if (!closeOk) { - if (r * 255 / maxval != colorfile_entry.r || - g * 255 / maxval != colorfile_entry.g || - b * 255 / maxval != colorfile_entry.b) + if (!closeOk) { + if (maxval != PAM_COLORFILE_MAXVAL) { + if (fabs((double)r / maxval - color[PAM_RED_PLANE]) > epsilon || + fabs((double)g / maxval - color[PAM_GRN_PLANE]) > epsilon || + fabs((double)b / maxval - color[PAM_BLU_PLANE]) > epsilon) { pm_message("WARNING: color '%s' cannot be represented " "exactly with a maxval of %u. " "Approximating as (%u,%u,%u). " - "The color dictionary uses maxval 255, so that " - "maxval will always work.", - colorname, maxval, r, g, b); + "(The color dictionary uses maxval %u, so that " + "maxval will always work).", + colorname, maxval, r, g, b, + PAM_COLORFILE_MAXVAL); + } } - } else { - r = colorfile_entry.r; - g = colorfile_entry.g; - b = colorfile_entry.b; } - free(canoncolor); PPM_ASSIGN(*colorP, r, g, b); } diff --git a/lib/colorname.h b/lib/colorname.h index 74583144..86c3d585 100644 --- a/lib/colorname.h +++ b/lib/colorname.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -15,8 +16,13 @@ extern "C" { enum colornameFormat {PAM_COLORNAME_ENGLISH = 0, PAM_COLORNAME_HEXOK = 1}; +#define PAM_COLORFILE_MAXVAL 255 + struct colorfile_entry { long r, g, b; + /* Red, green, and blue components of color based on maxval + PAM_COLORFILE_MAXVAL + */ char * colorname; }; @@ -31,6 +37,10 @@ pm_openColornameFile(const char * const fileName, const int must_open); struct colorfile_entry pm_colorget(FILE * const f); +void +pm_parse_dictionary_namen(char const colorname[], + tuplen const color); + void pm_parse_dictionary_name(const char colorname[], pixval const maxval, diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c index 06dd2493..120a82cf 100644 --- a/lib/libpamcolor.c +++ b/lib/libpamcolor.c @@ -22,9 +22,274 @@ #include #include "netpbm/pm_c_util.h" +#include "netpbm/mallocvar.h" +#include "netpbm/nstring.h" +#include "netpbm/colorname.h" + +#include "netpbm/pam.h" +#include "netpbm/ppm.h" + + + +static void +computeHexTable(int * const 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 void +parseHexDigits(const char * const string, + char const delim, + const int * const hexit, + samplen * const nP, + unsigned int * const digitCtP) { + + unsigned int digitCt; + unsigned long n; + unsigned long maxval; + + for (digitCt = 0, n = 0, maxval = 1; string[digitCt] != delim; ) { + char const digit = string[digitCt]; + 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; + maxval *= 16; + ++digitCt; + } + } + *nP = (samplen) n / maxval; + *digitCtP = digitCt; +} + + + +static void +parseNewHexX11(char const colorname[], + tuplen const color) { +/*---------------------------------------------------------------------------- + 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; + unsigned int digitCt; + + computeHexTable(hexit); + + cp = &colorname[4]; + + parseHexDigits(cp, '/', hexit, &color[PAM_RED_PLANE], &digitCt); + + cp += digitCt; + ++cp; /* Skip the slash */ + + parseHexDigits(cp, '/', hexit, &color[PAM_GRN_PLANE], &digitCt); + + cp += digitCt; + ++cp; /* Skip the slash */ + + parseHexDigits(cp, '\0', hexit, &color[PAM_BLU_PLANE], &digitCt); +} + + + +static bool +isNormal(samplen const arg) { + + return arg >= 0.0 && arg <= 1.0; +} + + + +static void +parseNewDecX11(const char * const colorname, + tuplen const color) { -#include "pam.h" -#include "ppm.h" + int rc; + + rc = sscanf(colorname, "rgbi:%f/%f/%f", + &color[PAM_RED_PLANE], + &color[PAM_GRN_PLANE], + &color[PAM_BLU_PLANE]); + + if (rc != 3) + pm_error("invalid color specifier '%s'", colorname); + + if (!(isNormal(color[PAM_RED_PLANE]) && + isNormal(color[PAM_GRN_PLANE]) && + isNormal(color[PAM_BLU_PLANE]))) { + pm_error("invalid color specifier '%s' - " + "values must be between 0.0 and 1.0", colorname); + } +} + + + +static void +parseOldX11(const char * const colorname, + tuplen const color) { +/*---------------------------------------------------------------------------- + Return as *colorP the color specified by the old X11 style color + specififier colorname[] (e.g. #554055). +-----------------------------------------------------------------------------*/ + int hexit[256]; + + 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: + color[PAM_RED_PLANE] = (samplen)hexit[(unsigned int)colorname[1]]/15; + color[PAM_GRN_PLANE] = (samplen)hexit[(unsigned int)colorname[2]]/15; + color[PAM_BLU_PLANE] = (samplen)hexit[(unsigned int)colorname[3]]/15; + break; + + case 6: + color[PAM_RED_PLANE] = + ((samplen)(hexit[(unsigned int)colorname[1]] << 4) + + (samplen)(hexit[(unsigned int)colorname[2]] << 0)) + / 255; + color[PAM_GRN_PLANE] = + ((samplen)(hexit[(unsigned int)colorname[3]] << 4) + + (samplen)(hexit[(unsigned int)colorname[4]] << 0)) + / 255; + color[PAM_BLU_PLANE] = + ((samplen)(hexit[(unsigned int)colorname[5]] << 4) + + (samplen)(hexit[(unsigned int)colorname[6]] << 0)) + / 255; + break; + + case 9: + color[PAM_RED_PLANE] = + ((samplen)(hexit[(unsigned int)colorname[1]] << 8) + + (samplen)(hexit[(unsigned int)colorname[2]] << 4) + + (samplen)(hexit[(unsigned int)colorname[3]] << 0)) + / 4095; + color[PAM_GRN_PLANE] = + ((samplen)(hexit[(unsigned int)colorname[4]] << 8) + + (samplen)(hexit[(unsigned int)colorname[5]] << 4) + + (samplen)(hexit[(unsigned int)colorname[6]] << 0)) + / 4095; + color[PAM_BLU_PLANE] = + ((samplen)(hexit[(unsigned int)colorname[7]] << 8) + + (samplen)(hexit[(unsigned int)colorname[8]] << 4) + + (samplen)(hexit[(unsigned int)colorname[9]] << 0)) + / 4095; + break; + + case 12: + color[PAM_RED_PLANE] = + ((samplen)(hexit[(unsigned int)colorname[1]] << 12) + + (samplen)(hexit[(unsigned int)colorname[2]] << 8) + + (samplen)(hexit[(unsigned int)colorname[3]] << 4) + + (samplen)(hexit[(unsigned int)colorname[4]] << 0)) + / 65535; + color[PAM_GRN_PLANE] = + ((samplen)(hexit[(unsigned int)colorname[5]] << 12) + + (samplen)(hexit[(unsigned int)colorname[6]] << 8) + + (samplen)(hexit[(unsigned int)colorname[7]] << 4) + + (samplen)(hexit[(unsigned int)colorname[8]] << 0)) + / 65535; + color[PAM_BLU_PLANE] = + ((samplen)(hexit[(unsigned int)colorname[9]] << 12) + + (samplen)(hexit[(unsigned int)colorname[10]] << 8) + + (samplen)(hexit[(unsigned int)colorname[11]] << 4) + + (samplen)(hexit[(unsigned int)colorname[12]] << 0)) + / 65535; + break; + + default: + pm_error("invalid color specifier '%s'", colorname); + } +} + + + +static void +parseOldX11Dec(const char* const colorname, + tuplen const color) { + + int rc; + + rc = sscanf(colorname, "%f,%f,%f", + &color[PAM_RED_PLANE], + &color[PAM_GRN_PLANE], + &color[PAM_BLU_PLANE]); + + if (rc != 3) + pm_error("invalid color specifier '%s'", colorname); + + if (!(isNormal(color[PAM_RED_PLANE]) && + isNormal(color[PAM_GRN_PLANE]) && + isNormal(color[PAM_BLU_PLANE]))) { + pm_error("invalid color specifier '%s' - " + "values must be between 0.0 and 1.0", colorname); + } +} + + + +tuplen +pnm_parsecolorn(const char * const colorname) { + + tuplen retval; + + MALLOCARRAY_NOFAIL(retval, 3); + + if (strncmp(colorname, "rgb:", 4) == 0) + /* It's a new-X11-style hexadecimal rgb specifier. */ + parseNewHexX11(colorname, retval); + else if (strncmp(colorname, "rgbi:", 5) == 0) + /* It's a new-X11-style decimal/float rgb specifier. */ + parseNewDecX11(colorname, retval); + else if (colorname[0] == '#') + /* It's an old-X11-style hexadecimal rgb specifier. */ + parseOldX11(colorname, retval); + else if ((colorname[0] >= '0' && colorname[0] <= '9') || + colorname[0] == '.') + /* It's an old-style decimal/float rgb specifier. */ + parseOldX11Dec(colorname, retval); + else + /* Must be a name from the X-style rgb file. */ + pm_parse_dictionary_namen(colorname, retval); + + return retval; +} @@ -33,7 +298,7 @@ pnm_parsecolor(const char * const colorname, sample const maxval) { tuple retval; - pixel color; + tuplen color; struct pam pam; pam.len = PAM_STRUCT_SIZE(bytes_per_sample); @@ -43,11 +308,13 @@ pnm_parsecolor(const char * const colorname, retval = pnm_allocpamtuple(&pam); - color = ppm_parsecolor(colorname, maxval); + color = pnm_parsecolorn(colorname); + + retval[PAM_RED_PLANE] = ROUNDU(color[PAM_RED_PLANE] * maxval); + retval[PAM_GRN_PLANE] = ROUNDU(color[PAM_GRN_PLANE] * maxval); + retval[PAM_BLU_PLANE] = ROUNDU(color[PAM_BLU_PLANE] * maxval); - retval[PAM_RED_PLANE] = PPM_GETR(color); - retval[PAM_GRN_PLANE] = PPM_GETG(color); - retval[PAM_BLU_PLANE] = PPM_GETB(color); + free(color); return retval; } diff --git a/lib/libppmd.c b/lib/libppmd.c index 02c34015..a94ff107 100644 --- a/lib/libppmd.c +++ b/lib/libppmd.c @@ -850,7 +850,7 @@ ppmd_spline4p(pixel ** const pixels, /*---------------------------------------------------------------------------- Draw a cubic spline from 'endPt0' to 'endPt1', using 'ctlPt0' and 'ctlPt1' as control points in the classic way: a line through - 'endPt0' and 'ctlPt0' is tangent to the curve at 'entPt0' and the + 'endPt0' and 'ctlPt0' is tangent to the curve at 'endPt0' and the length of that line controls "enthusiasm," whatever that is. Same for 'endPt1' and 'ctlPt1'. -----------------------------------------------------------------------------*/ diff --git a/lib/pam.h b/lib/pam.h index c58e36a2..1cf7b855 100644 --- a/lib/pam.h +++ b/lib/pam.h @@ -493,6 +493,9 @@ tuple pnm_parsecolor(const char * const colorname, sample const maxval); +tuplen +pnm_parsecolorn(const char * const colorname); + const char * pnm_colorname(struct pam * const pamP, tuple const color, -- cgit 1.4.1