about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--lib/colorname.c81
-rw-r--r--lib/colorname.h10
-rw-r--r--lib/libpamcolor.c281
-rw-r--r--lib/libppmd.c2
-rw-r--r--lib/pam.h3
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 <string.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <math.h>
 
 #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 <string.h>
 #include <stdio.h>
 #include <netpbm/ppm.h>
+#include <netpbm/pam.h>
 
 #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;
 };
 
@@ -32,6 +38,10 @@ 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,
                          int        const closeOk,
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 <limits.h>
 
 #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,