about summary refs log tree commit diff
path: root/lib/libppmcolor.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libppmcolor.c')
-rw-r--r--lib/libppmcolor.c553
1 files changed, 279 insertions, 274 deletions
diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c
index c0a88dc2..04858f6a 100644
--- a/lib/libppmcolor.c
+++ b/lib/libppmcolor.c
@@ -9,6 +9,7 @@
 ** implied warranty.
 */
 
+#include <stdbool.h>
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
@@ -22,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);
+#define MAXCOLORNAMES 1000u
+    /* The maximum size of a colornames array, in the old interface */
 
-    pixel retval;
+static colorhash_table
+allocColorHash(void) {
 
-    PPM_PUTR(retval, color[PAM_RED_PLANE]);
-    PPM_PUTG(retval, color[PAM_GRN_PLANE]);
-    PPM_PUTB(retval, color[PAM_BLU_PLANE]);
+    colorhash_table cht;
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
 
-    free(color);
+    if (setjmp(jmpbuf) != 0)
+        cht = NULL;
+    else {
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+        cht = ppm_alloccolorhash();
+    }
+    pm_setjmpbuf(origJmpbufP);
 
-    return retval;
+    return cht;
 }
 
 
 
-pixel
-ppm_parsecolor(const char * const colorname,
-               pixval       const maxval) {
+static ppm_ColorDict *
+colorDict_newEmpty() {
 
-    return ppm_parsecolor2(colorname, maxval, TRUE);
-}
+    ppm_ColorDict * colorDictP;
 
+    MALLOCVAR_NOFAIL(colorDictP);
 
+    colorDictP->version = 0;
+    colorDictP->size    = 0;
+    colorDictP->name    = NULL;
+    colorDictP->color   = NULL;
+    colorDictP->cht     = allocColorHash();
+    colorDictP->count   = 0;
 
-char *
-ppm_colorname(const pixel * const colorP,
-              pixval        const maxval,
-              int           const hexok)   {
+    if (!colorDictP->cht)
+        pm_error("Unable to allocate space for color hash");
 
-    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);
-        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;
-    }
-
-    f = pm_openColornameFile(NULL, !hexok);
-
-    if (!f)
-        STRSCPY(colorname, "");
-    else {
-        int bestDiff;
-        bool eof;
-
-        for (bestDiff = 32767, eof = FALSE;
-             !eof && bestDiff > 0; ) {
-            struct colorfile_entry const ce = pm_colorget(f);
-            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(f);
-
-        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;
 
-static colorhash_table
-allocColorHash(void) {
+    for (i = 0; i < colorDictP->size; ++i)
+        pm_strfree(colorDictP->name[i]);
 
-    colorhash_table cht;
-    jmp_buf jmpbuf;
-    jmp_buf * origJmpbufP;
+    if (colorDictP->name)
+        free(colorDictP->name);
+    if (colorDictP->color)
+        free(colorDictP->color);
 
-    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;
         }
     }
 }
@@ -228,44 +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;
 
-    nColorsDone = 0;
-    done = FALSE;
-    *errorP = NULL;
+    PPM_ASSIGN(color, ce.r, ce.g, ce.b);
 
-    while (!done && !*errorP) {
-        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;
+        }
     }
 }
 
@@ -274,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. not file by that name exists), abort the
+   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);
         }
@@ -314,55 +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) {
+ppm_ColorDict *
+ppm_colorDict_new(const char * const fileName,
+                  int          const mustOpen) {
 
-    const char ** colornames;
+    ppm_ColorDict * colorDictP;
+    const char * error;
 
-    colornames = allocColorNames();
+    colorDictP = colorDict_newEmpty();
 
-    if (colornames == NULL)
-        pm_asprintf(errorP, "Unable to allocate space for colorname table.");
-    else {
-        pixel * colors;
+    readColorFile(fileName, mustOpen, colorDictP, &error);
+
+    if (error) {
+        pm_errormsg("%s", error);
+        pm_strfree(error);
+        pm_longjmp();
+    }
+    return colorDictP;
+}
 
-        MALLOCARRAY(colors, MAXCOLORNAMES);
 
-        if (colors == NULL)
-            pm_asprintf(errorP, "Unable to allocate space for color table.");
-        else {
-            colorhash_table cht;
 
-            cht = allocColorHash();
+pixel
+ppm_parsecolor2(const char * const colorname,
+                pixval       const maxval,
+                int          const closeOk) {
 
-            if (cht == NULL)
-                pm_asprintf(errorP, "Unable to allocate space for color hash");
-            else {
-                readColorFile(fileName, mustOpen,
-                              nColorsP, colornames, colors, cht,
-                              errorP);
+    tuple const color = pnm_parsecolor2(colorname, maxval, closeOk);
 
-                if (*errorP)
-                    ppm_freecolorhash(cht);
-                else
-                    *chtP = cht;
-            }
-            if (*errorP)
-                free(colors);
-            else
-                *colorsP = colors;
+    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;
 }
 
 
@@ -371,60 +356,80 @@ void
 ppm_readcolordict(const char *      const fileName,
                   int               const mustOpen,
                   unsigned int *    const nColorsP,
-                  const char ***    const colornamesP,
+                  const char ***    const colorNamesP,
                   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.
 
-   Return as *colornamesP the names of those colors.  *colornamesP is a
-   malloced array that Caller must free with ppm_freecolornames().
+   Return as *colorNamesP the names of those colors.  *colorNamesP is a
+   malloced array that Caller must free with ppm_freecolorNames().
    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.
+   to the index into *colorNamesP for the name of the color.
 
-   Each of 'nColorsP, 'colornamesP', and 'colorsP' may be null, in which case
+   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;
 }
 
 
@@ -433,23 +438,23 @@ void
 ppm_readcolornamefile(const char *      const fileName,
                       int               const mustOpen,
                       colorhash_table * const chtP,
-                      const char ***    const colornamesP) {
+                      const char ***    const colorNamesP) {
 
-    ppm_readcolordict(fileName, mustOpen, NULL, colornamesP, NULL, chtP);
+    ppm_readcolordict(fileName, mustOpen, NULL, colorNamesP, NULL, chtP);
 }
 
 
 
 void
-ppm_freecolornames(const char ** const colornames) {
+ppm_freecolornames(const char ** const colorNames) {
 
     unsigned int i;
 
     for (i = 0; i < MAXCOLORNAMES; ++i)
-        if (colornames[i])
-            free((char *)colornames[i]);
+        if (colorNames[i])
+            free((char *)colorNames[i]);
 
-    free(colornames);
+    free(colorNames);
 }