Blame lib/libpammap.c

Packit 78deda
/*=============================================================================
Packit 78deda
                                  libpammap.c
Packit 78deda
===============================================================================
Packit 78deda
Packit 78deda
  These are functions that deal with tuple hashes and tuple tables.
Packit 78deda
Packit 78deda
  Both tuple hashes and tuple tables let you associate an arbitrary
Packit 78deda
  integer with a tuple value.  A tuple hash lets you look up the one
Packit 78deda
  integer value (if any) associated with a given tuple value, having
Packit 78deda
  the low memory and execution time characteristics of a hash table.
Packit 78deda
  A tuple table lets you scan all the values, being a table of elements
Packit 78deda
  that consist of an ordered pair of a tuple value and integer.
Packit 78deda
Packit 78deda
  This file was originally written by Bryan Henderson and is contributed
Packit 78deda
  to the public domain by him and subsequent authors.
Packit 78deda
=============================================================================*/
Packit 78deda
Packit 78deda
#include <assert.h>
Packit 78deda
Packit 78deda
#include "netpbm/pm_c_util.h"
Packit 78deda
#include "netpbm/mallocvar.h"
Packit 78deda
#include "netpbm/nstring.h"
Packit 78deda
Packit 78deda
#include "pam.h"
Packit 78deda
#include "pammap.h"
Packit 78deda
Packit 78deda
Packit 78deda
#define HASH_SIZE 20023
Packit 78deda
Packit 78deda
unsigned int
Packit 78deda
pnm_hashtuple(struct pam * const pamP,
Packit 78deda
              tuple        const tuple) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Return the hash value of the tuple 'tuple' -- i.e. an index into a hash
Packit 78deda
   table.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int const hash_factor[] = {1, 33, 33*33};
Packit 78deda
Packit 78deda
    unsigned int i;
Packit 78deda
    unsigned int hash;
Packit 78deda
Packit 78deda
    hash = 0;  /* initial value */
Packit 78deda
    for (i = 0; i < MIN(pamP->depth, 3); ++i) {
Packit 78deda
        hash += tuple[i] * hash_factor[i];
Packit 78deda
    }
Packit 78deda
    hash %= HASH_SIZE;
Packit 78deda
    return hash;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
tuplehash
Packit 78deda
pnm_createtuplehash(void) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Create an empty tuple hash -- i.e. a hash table of zero length hash chains.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    tuplehash retval;
Packit 78deda
    unsigned int i;
Packit 78deda
Packit 78deda
    MALLOCARRAY(retval, HASH_SIZE);
Packit 78deda
Packit 78deda
    if (retval == NULL)
Packit 78deda
        pm_error("Out of memory allocating tuple hash of size %u",
Packit 78deda
                 HASH_SIZE);
Packit 78deda
Packit 78deda
    for (i = 0; i < HASH_SIZE; ++i) 
Packit 78deda
        retval[i] = NULL;
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pnm_destroytuplehash(tuplehash const tuplehash) {
Packit 78deda
Packit 78deda
    unsigned int i;
Packit 78deda
Packit 78deda
    /* Free the chains */
Packit 78deda
Packit 78deda
    for (i = 0; i < HASH_SIZE; ++i) {
Packit 78deda
        struct tupleint_list_item * p;
Packit 78deda
        struct tupleint_list_item * next;
Packit 78deda
        
Packit 78deda
        /* Walk this chain, freeing each element */
Packit 78deda
        for (p = tuplehash[i]; p; p = next) {
Packit 78deda
            next = p->next;
Packit 78deda
Packit 78deda
            free(p);
Packit 78deda
        }            
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* Free the table of chains */
Packit 78deda
    free(tuplehash);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static struct tupleint_list_item * 
Packit 78deda
allocTupleIntListItem(struct pam * const pamP) {
Packit 78deda
Packit 78deda
Packit 78deda
    /* This is complicated by the fact that the last element of a 
Packit 78deda
       tupleint_list_item is of variable length, because the last element
Packit 78deda
       of _it_ is of variable length 
Packit 78deda
    */
Packit 78deda
    struct tupleint_list_item * retval;
Packit 78deda
Packit Service 2370ca
    unsigned int const size = 
Packit 78deda
        sizeof(*retval) - sizeof(retval->tupleint.tuple) 
Packit 78deda
        + pamP->depth * sizeof(sample);
Packit 78deda
Packit 78deda
    retval = (struct tupleint_list_item *) malloc(size);
Packit 78deda
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pnm_addtotuplehash(struct pam *   const pamP,
Packit 78deda
                   tuplehash      const tuplehash, 
Packit 78deda
                   tuple          const tupletoadd,
Packit 78deda
                   int            const value,
Packit 78deda
                   int *          const fitsP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Add a tuple value to the hash -- assume it isn't already there.
Packit 78deda
Packit 78deda
   Allocate new space for the tuple value and the hash chain element.
Packit 78deda
Packit 78deda
   If we can't allocate space for the new hash chain element, don't
Packit 78deda
   change anything and return *fitsP = FALSE;
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    struct tupleint_list_item * const listItemP = allocTupleIntListItem(pamP);
Packit 78deda
    if (listItemP == NULL)
Packit 78deda
        *fitsP = FALSE;
Packit 78deda
    else {
Packit 78deda
        unsigned int const hashvalue = pnm_hashtuple(pamP, tupletoadd);
Packit 78deda
    
Packit 78deda
        *fitsP = TRUE;
Packit 78deda
Packit 78deda
        pnm_assigntuple(pamP, listItemP->tupleint.tuple, tupletoadd);
Packit 78deda
        listItemP->tupleint.value = value;
Packit 78deda
        listItemP->next = tuplehash[hashvalue];
Packit 78deda
        tuplehash[hashvalue] = listItemP;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pnm_lookuptuple(struct pam *    const pamP, 
Packit 78deda
                const tuplehash       tuplehash, 
Packit 78deda
                const tuple           searchval, 
Packit 78deda
                int *           const foundP, 
Packit 78deda
                int *           const retvalP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Return as *revtvalP the index of the tuple value 'searchval' in the
Packit 78deda
   tuple hash 'tuplehash'.
Packit 78deda
Packit 78deda
   But iff the tuple value isn't in the hash, return *foundP == false
Packit 78deda
   and nothing as *retvalP.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int const hashvalue = pnm_hashtuple(pamP, searchval);
Packit 78deda
    struct tupleint_list_item * p;
Packit 78deda
    struct tupleint_list_item * found;
Packit 78deda
Packit 78deda
    found = NULL;  /* None found yet */
Packit 78deda
    for (p = tuplehash[hashvalue]; p && !found; p = p->next)
Packit 78deda
        if (pnm_tupleequal(pamP, p->tupleint.tuple, searchval)) {
Packit 78deda
            found = p;
Packit 78deda
        }
Packit 78deda
Packit 78deda
    if (found) {
Packit 78deda
        *foundP = TRUE;
Packit 78deda
        *retvalP = found->tupleint.value;
Packit 78deda
    } else
Packit 78deda
        *foundP = FALSE;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
addColorOccurrenceToHash(tuple          const color, 
Packit 78deda
                         tuplehash      const tuplefreqhash,
Packit 78deda
                         struct pam *   const pamP,
Packit 78deda
                         unsigned int   const maxsize,
Packit 78deda
                         unsigned int * const sizeP,
Packit 78deda
                         bool *         const fullP) {
Packit 78deda
               
Packit 78deda
    unsigned int const hashvalue = pnm_hashtuple(pamP, color);
Packit 78deda
            
Packit 78deda
    struct tupleint_list_item *p;
Packit 78deda
Packit 78deda
    for (p = tuplefreqhash[hashvalue]; 
Packit 78deda
         p && !pnm_tupleequal(pamP, p->tupleint.tuple, color);
Packit 78deda
         p = p->next);
Packit 78deda
Packit 78deda
    if (p) {
Packit 78deda
        /* It's in the hash; just tally one more occurrence */
Packit 78deda
        ++p->tupleint.value;
Packit 78deda
        *fullP = FALSE;
Packit 78deda
    } else {
Packit 78deda
        /* It's not in the hash yet, so add it (if allowed) */
Packit 78deda
        ++(*sizeP);
Packit 78deda
        if (maxsize > 0 && *sizeP > maxsize) 
Packit 78deda
            *fullP = TRUE;
Packit 78deda
        else {
Packit 78deda
            *fullP = FALSE;
Packit 78deda
            p = allocTupleIntListItem(pamP);
Packit 78deda
            if (p == NULL)
Packit 78deda
                pm_error("out of memory computing hash table");
Packit 78deda
            pnm_assigntuple(pamP, p->tupleint.tuple, color);
Packit 78deda
            p->tupleint.value = 1;
Packit 78deda
            p->next = tuplefreqhash[hashvalue];
Packit 78deda
            tuplefreqhash[hashvalue] = p;
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pnm_addtuplefreqoccurrence(struct pam *   const pamP,
Packit 78deda
                           tuple          const value,
Packit 78deda
                           tuplehash      const tuplefreqhash,
Packit 78deda
                           int *          const firstOccurrenceP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Tally one more occurence of the tuple value 'value' to the tuple frequencey
Packit 78deda
  hash 'tuplefreqhash', adding the tuple to the hash if it isn't there
Packit 78deda
  already.
Packit 78deda
Packit 78deda
  Allocate new space for the tuple value and the hash chain element.
Packit 78deda
Packit 78deda
  If we can't allocate space for the new hash chain element, abort the
Packit 78deda
  program.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int const hashvalue = pnm_hashtuple(pamP, value);
Packit 78deda
            
Packit 78deda
    struct tupleint_list_item * p;
Packit 78deda
Packit 78deda
    for (p = tuplefreqhash[hashvalue]; 
Packit 78deda
         p && !pnm_tupleequal(pamP, p->tupleint.tuple, value);
Packit 78deda
         p = p->next);
Packit 78deda
Packit 78deda
    if (p) {
Packit 78deda
        /* It's in the hash; just tally one more occurrence */
Packit 78deda
        ++p->tupleint.value;
Packit 78deda
        *firstOccurrenceP = FALSE;
Packit 78deda
    } else {
Packit 78deda
        struct tupleint_list_item * p;
Packit 78deda
Packit 78deda
        /* It's not in the hash yet, so add it */
Packit 78deda
        *firstOccurrenceP = TRUE;
Packit 78deda
Packit 78deda
        p = allocTupleIntListItem(pamP);
Packit 78deda
        if (p == NULL)
Packit 78deda
            pm_error("out of memory computing hash table");
Packit 78deda
Packit 78deda
        pnm_assigntuple(pamP, p->tupleint.tuple, value);
Packit 78deda
        p->tupleint.value = 1;
Packit 78deda
        p->next = tuplefreqhash[hashvalue];
Packit 78deda
        tuplefreqhash[hashvalue] = p;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
computehashrecoverable(struct pam *   const pamP,
Packit 78deda
                       tuple **       const tupleArray, 
Packit 78deda
                       unsigned int   const maxsize, 
Packit 78deda
                       unsigned int   const newDepth,
Packit 78deda
                       sample         const newMaxval,
Packit 78deda
                       unsigned int * const sizeP,
Packit 78deda
                       tuplehash *    const tuplefreqhashP,
Packit 78deda
                       tuple **       const rowbufferP,
Packit 78deda
                       tuple *        const colorP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   This is computetuplefreqhash(), only it leaves a trail so that if it
Packit 78deda
   happens to longjmp out because of a failed memory allocation, the
Packit 78deda
   setjmp'er can cleanup whatever it had done so far.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    unsigned int row;
Packit 78deda
    struct pam freqPam;
Packit 78deda
    bool full;
Packit 78deda
Packit 78deda
    freqPam = *pamP;
Packit 78deda
    freqPam.maxval = newMaxval;
Packit 78deda
    freqPam.depth = newDepth;
Packit 78deda
Packit 78deda
    assert(freqPam.depth <= pamP->depth);
Packit 78deda
Packit 78deda
    *tuplefreqhashP = pnm_createtuplehash();
Packit 78deda
    *sizeP = 0;   /* initial value */
Packit 78deda
    
Packit 78deda
    *rowbufferP = pnm_allocpamrow(pamP);
Packit 78deda
    
Packit 78deda
    *colorP = pnm_allocpamtuple(pamP);
Packit 78deda
    
Packit 78deda
    full = FALSE;  /* initial value */
Packit 78deda
    
Packit 78deda
    /* Go through the entire raster, building a hash table of
Packit 78deda
       tuple values. 
Packit 78deda
    */
Packit 78deda
    for (row = 0; row < pamP->height && !full; ++row) {
Packit 78deda
        unsigned int col;
Packit 78deda
        const tuple * tuplerow;  /* The row of tuples we are processing */
Packit 78deda
        
Packit 78deda
        if (tupleArray)
Packit 78deda
            tuplerow = tupleArray[row];
Packit 78deda
        else {
Packit 78deda
            pnm_readpamrow(pamP, *rowbufferP);
Packit 78deda
            tuplerow = *rowbufferP;
Packit 78deda
        }
Packit 78deda
        for (col = 0; col < pamP->width && !full; ++col) {
Packit 78deda
            pnm_scaletuple(pamP, *colorP, tuplerow[col], freqPam.maxval);
Packit 78deda
            addColorOccurrenceToHash(
Packit 78deda
                *colorP, *tuplefreqhashP, &freqPam, maxsize, sizeP, &full);
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
Packit 78deda
    pnm_freepamtuple(*colorP); *colorP = NULL;
Packit 78deda
    pnm_freepamrow(*rowbufferP); *rowbufferP = NULL;
Packit 78deda
Packit 78deda
    if (full) {
Packit 78deda
        pnm_destroytuplehash(*tuplefreqhashP);
Packit 78deda
        *tuplefreqhashP = NULL;
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static tuplehash
Packit 78deda
computetuplefreqhash(struct pam *   const pamP,
Packit 78deda
                     tuple **       const tupleArray, 
Packit 78deda
                     unsigned int   const maxsize, 
Packit 78deda
                     unsigned int   const newDepth,
Packit 78deda
                     sample         const newMaxval,
Packit 78deda
                     unsigned int * const sizeP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
  Compute a tuple frequency hash from a PAM.  This is a hash that gives
Packit 78deda
  you the number of times a given tuple value occurs in the PAM.  You can
Packit 78deda
  supply the input PAM in one of two ways:
Packit 78deda
Packit 78deda
  1) a two-dimensional array of tuples tupleArray[][];  In this case,
Packit 78deda
     'tupleArray' is non-NULL.
Packit 78deda
Packit 78deda
  2) an open PAM file, positioned to the raster.  In this case,
Packit 78deda
     'tupleArray' is NULL.  *pamP contains the file descriptor.
Packit 78deda
  
Packit 78deda
     We return with the file still open and its position undefined.  
Packit 78deda
Packit 78deda
  In either case, *pamP contains parameters of the tuple array.
Packit 78deda
Packit 78deda
  Return the number of unique tuple values found as *sizeP.
Packit 78deda
Packit 78deda
  However, if the number of unique tuple values is greater than 'maxsize', 
Packit 78deda
  return a null return value and *sizeP undefined.
Packit 78deda
Packit 78deda
  The tuple values that index the hash have depth 'newDepth'.  We look at
Packit 78deda
  only the first 'newDepth' planes of the input.  Caler must ensure that
Packit 78deda
  the input has at least that many planes.
Packit 78deda
Packit 78deda
  The tuple values that index the hash are scaled to a new maxval of
Packit 78deda
  'newMaxval'.  E.g.  if the input has maxval 100 and 'newMaxval' is
Packit 78deda
  50, and a particular tuple has sample value 50, it would be counted
Packit 78deda
  as sample value 25 in the hash.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    tuplehash tuplefreqhash;
Packit 78deda
    tuple * rowbuffer;  /* malloc'ed */
Packit 78deda
        /* Buffer for a row read from the input file; undefined (but still
Packit 78deda
           allocated) if input is not from a file.
Packit 78deda
        */
Packit 78deda
    tuple color;  
Packit 78deda
        /* The color currently being added, scaled to the new maxval */
Packit 78deda
    jmp_buf jmpbuf;
Packit 78deda
    jmp_buf * origJmpbufP;
Packit 78deda
    
Packit 78deda
    /* Initialize to "none" for purposes of error recovery */
Packit 78deda
    tuplefreqhash = NULL;
Packit 78deda
    rowbuffer = NULL;
Packit 78deda
    color = NULL;
Packit 78deda
Packit 78deda
    if (setjmp(jmpbuf) != 0) {
Packit 78deda
        if (color) 
Packit 78deda
            pnm_freepamtuple(color);
Packit 78deda
        if (rowbuffer)
Packit 78deda
            pnm_freepamrow(rowbuffer);
Packit 78deda
        if (tuplefreqhash)
Packit 78deda
            pnm_destroytuplehash(tuplefreqhash);
Packit 78deda
        pm_setjmpbuf(origJmpbufP);
Packit 78deda
        pm_longjmp();
Packit 78deda
    } else {
Packit 78deda
        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
Packit 78deda
        computehashrecoverable(pamP, tupleArray, maxsize, newDepth, newMaxval,
Packit 78deda
                               sizeP, &tuplefreqhash, &rowbuffer, &color;;
Packit 78deda
        pm_setjmpbuf(origJmpbufP);
Packit 78deda
    }
Packit 78deda
    return tuplefreqhash;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
tuplehash
Packit 78deda
pnm_computetuplefreqhash(struct pam *   const pamP,
Packit 78deda
                         tuple **       const tupleArray,
Packit 78deda
                         unsigned int   const maxsize,
Packit 78deda
                         unsigned int * const sizeP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Compute the tuple frequency hash for the tuple array tupleArray[][].
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    return computetuplefreqhash(pamP, tupleArray, maxsize,
Packit 78deda
                                pamP->depth, pamP->maxval, 
Packit 78deda
                                sizeP);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static void
Packit 78deda
alloctupletable(const struct pam * const pamP, 
Packit 78deda
                unsigned int       const size,
Packit 78deda
                tupletable *       const tupletableP,
Packit 78deda
                const char **      const errorP) {
Packit 78deda
    
Packit 78deda
    if (UINT_MAX / sizeof(struct tupleint) < size)
Packit 78deda
        pm_asprintf(errorP, "size %u is too big for arithmetic", size);
Packit 78deda
    else {
Packit 78deda
        unsigned int const mainTableSize = size * sizeof(struct tupleint *);
Packit 78deda
        unsigned int const tupleIntSize = 
Packit 78deda
            sizeof(struct tupleint) - sizeof(sample) 
Packit 78deda
            + pamP->depth * sizeof(sample);
Packit 78deda
Packit 78deda
        /* To save the enormous amount of time it could take to allocate
Packit 78deda
           each individual tuple, we do a trick here and allocate everything
Packit 78deda
           as a single malloc block and suballocate internally.
Packit 78deda
        */
Packit 78deda
        if ((UINT_MAX - mainTableSize) / tupleIntSize < size)
Packit 78deda
            pm_asprintf(errorP, "size %u is too big for arithmetic", size);
Packit 78deda
        else {
Packit 78deda
            unsigned int const allocSize = mainTableSize + size * tupleIntSize;
Packit 78deda
            void * pool;
Packit 78deda
    
Packit 78deda
            pool = malloc(allocSize);
Packit 78deda
Packit 78deda
            if (!pool)
Packit 78deda
                pm_asprintf(errorP,
Packit 78deda
                            "Unable to allocate %u bytes for a %u-entry "
Packit 78deda
                            "tuple table", allocSize, size);
Packit 78deda
            else {
Packit 78deda
                tupletable const tbl = (tupletable) pool;
Packit 78deda
Packit 78deda
                unsigned int i;
Packit 78deda
Packit 78deda
                *errorP = NULL;
Packit 78deda
Packit 78deda
                for (i = 0; i < size; ++i)
Packit 78deda
                    tbl[i] = (struct tupleint *)
Packit 78deda
                        ((char*)pool + mainTableSize + i * tupleIntSize);
Packit 78deda
Packit 78deda
                *tupletableP = tbl;
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
tupletable
Packit 78deda
pnm_alloctupletable(const struct pam * const pamP, 
Packit 78deda
                    unsigned int       const size) {
Packit 78deda
Packit 78deda
    tupletable retval;
Packit 78deda
    const char * error;
Packit 78deda
Packit 78deda
    alloctupletable(pamP, size, &retval, &error);
Packit 78deda
Packit 78deda
    if (error) {
Packit 78deda
        pm_errormsg("%s", error);
Packit 78deda
        pm_strfree(error);
Packit 78deda
        pm_longjmp();
Packit 78deda
    }
Packit 78deda
    return retval;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pnm_freetupletable(const struct pam * const pamP,
Packit 78deda
                   tupletable         const tupletable) {
Packit 78deda
Packit 78deda
    /* Note that the address 'tupletable' is, to the operating system, 
Packit 78deda
       the address of a larger block of memory that contains not only 
Packit 78deda
       tupletable, but all the samples to which it points (e.g.
Packit 78deda
       tupletable[0].tuple[0])
Packit 78deda
    */
Packit 78deda
Packit 78deda
    free(tupletable);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
void
Packit 78deda
pnm_freetupletable2(const struct pam * const pamP,
Packit 78deda
                    tupletable2        const tupletable) {
Packit 78deda
Packit 78deda
    pnm_freetupletable(pamP, tupletable.table);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
static tupletable
Packit 78deda
tuplehashtotable(const struct pam * const pamP,
Packit 78deda
                 tuplehash          const tuplehash,
Packit 78deda
                 unsigned int       const allocsize) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Create a tuple table containing the info from a tuple hash.  Allocate
Packit 78deda
   space in the table for 'allocsize' elements even if there aren't that
Packit 78deda
   many tuple values in the input hash.  That's so the caller has room
Packit 78deda
   for expansion.
Packit 78deda
Packit 78deda
   Caller must ensure that 'allocsize' is at least as many tuple values
Packit 78deda
   as there are in the input hash.
Packit 78deda
Packit 78deda
   We allocate new space for all the table contents; there are no pointers
Packit 78deda
   in the table to tuples or anything else in existing space.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    tupletable tupletable;
Packit 78deda
    const char * error;
Packit 78deda
Packit 78deda
    alloctupletable(pamP, allocsize, &tupletable, &error);
Packit 78deda
Packit 78deda
    if (error) {
Packit 78deda
        pm_errormsg("%s", error);
Packit 78deda
        pm_strfree(error);
Packit 78deda
        pm_longjmp();
Packit 78deda
    } else {
Packit 78deda
        unsigned int i, j;
Packit 78deda
        /* Loop through the hash table. */
Packit 78deda
        j = 0;
Packit 78deda
        for (i = 0; i < HASH_SIZE; ++i) {
Packit 78deda
            /* Walk this hash chain */
Packit 78deda
            struct tupleint_list_item * p;
Packit 78deda
            for (p = tuplehash[i]; p; p = p->next) {
Packit 78deda
                assert(j < allocsize);
Packit 78deda
                tupletable[j]->value = p->tupleint.value;
Packit 78deda
                pnm_assigntuple(pamP, tupletable[j]->tuple, p->tupleint.tuple);
Packit 78deda
                ++j;
Packit 78deda
            }
Packit 78deda
        }
Packit 78deda
    }
Packit 78deda
    return tupletable;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
tupletable
Packit 78deda
pnm_tuplehashtotable(const struct pam * const pamP,
Packit 78deda
                     tuplehash          const tuplehash,
Packit 78deda
                     unsigned int       const allocsize) {
Packit 78deda
Packit 78deda
    tupletable tupletable;
Packit 78deda
Packit 78deda
    tupletable = tuplehashtotable(pamP, tuplehash, allocsize);
Packit 78deda
Packit 78deda
    if (tupletable == NULL)
Packit 78deda
        pm_error("out of memory generating tuple table");
Packit 78deda
Packit 78deda
    return tupletable;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
tuplehash
Packit 78deda
pnm_computetupletablehash(struct pam * const pamP, 
Packit 78deda
                          tupletable   const tupletable,
Packit 78deda
                          unsigned int const tupletableSize) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Create a tuple hash containing indices into the tuple table
Packit 78deda
   'tupletable'.  The hash index for the hash is the value of a tuple;
Packit 78deda
   the hash value is the tuple table index for the element in the
Packit 78deda
   tuple table that contains that tuple value.
Packit 78deda
Packit 78deda
   Assume there are no duplicate tuple values in the tuple table.
Packit 78deda
Packit 78deda
   We allocate space for the main hash table and all the elements of the
Packit 78deda
   hash chains.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    tuplehash tupletablehash;
Packit 78deda
    unsigned int i;
Packit 78deda
    int fits;
Packit 78deda
    
Packit 78deda
    tupletablehash = pnm_createtuplehash();
Packit 78deda
Packit 78deda
    fits = TRUE;  /* initial assumption */
Packit 78deda
    for (i = 0; i < tupletableSize && fits; ++i) {
Packit 78deda
        pnm_addtotuplehash(pamP, tupletablehash, 
Packit 78deda
                           tupletable[i]->tuple, i, &fits);
Packit 78deda
    }
Packit 78deda
    if (!fits) {
Packit 78deda
        pnm_destroytuplehash(tupletablehash);
Packit 78deda
        pm_error("Out of memory computing tuple hash from tuple table");
Packit 78deda
    }
Packit 78deda
    return tupletablehash;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
tupletable
Packit 78deda
pnm_computetuplefreqtable3(struct pam *   const pamP,
Packit 78deda
                           tuple **       const tupleArray,
Packit 78deda
                           unsigned int   const maxsize,
Packit 78deda
                           unsigned int   const newDepth,
Packit 78deda
                           sample         const newMaxval,
Packit 78deda
                           unsigned int * const countP) {
Packit 78deda
/*----------------------------------------------------------------------------
Packit 78deda
   Compute a tuple frequency table from a PAM image.  This is an
Packit 78deda
   array that tells how many times each tuple value occurs in the
Packit 78deda
   image.
Packit 78deda
Packit 78deda
   Except for the format of the output, this function is the same as
Packit 78deda
   computetuplefreqhash().
Packit 78deda
Packit 78deda
   If there are more than 'maxsize' unique tuple values in tupleArray[][],
Packit 78deda
   give up.
Packit 78deda
Packit 78deda
   Return the array in newly malloc'ed storage.  Allocate space for
Packit 78deda
   'maxsize' entries even if there aren't that many distinct tuple
Packit 78deda
   values in tupleArray[].  That's so the caller has room for
Packit 78deda
   expansion.
Packit 78deda
Packit 78deda
   If 'maxsize' is zero, allocate exactly as much space as there are
Packit 78deda
   distinct tuple values in tupleArray[], and don't give up no matter
Packit 78deda
   how many tuple values we find (except, of course, we abort if we
Packit 78deda
   can't get enough memory).
Packit 78deda
Packit 78deda
   Return the number of unique tuple values in tupleArray[][] as
Packit 78deda
   *countP.
Packit 78deda
Packit 78deda
   The tuples in the table have depth 'newDepth'.  We look at
Packit 78deda
   only the first 'newDepth' planes of the input.  If the input doesn't
Packit 78deda
   have that many planes, we throw an error.
Packit 78deda
Packit 78deda
   Scale the tuple values to a new maxval of 'newMaxval' before
Packit 78deda
   processing them.  E.g. if the input has maxval 100 and 'newMaxval'
Packit 78deda
   is 50, and a particular tuple has sample value 50, it would be
Packit 78deda
   listed as sample value 25 in the output table.  This makes the
Packit 78deda
   output table smaller and the processing time less.
Packit 78deda
-----------------------------------------------------------------------------*/
Packit 78deda
    tuplehash tuplefreqhash;
Packit 78deda
    tupletable tuplefreqtable;
Packit 78deda
    unsigned int uniqueCount;
Packit 78deda
Packit 78deda
    if (newDepth > pamP->depth)
Packit 78deda
        pm_error("pnm_computetuplefreqtable3 called with 'newDepth' "
Packit 78deda
                 "argument (%u) greater than input depth (%u)",
Packit 78deda
                 newDepth, pamP->depth);
Packit 78deda
Packit 78deda
    tuplefreqhash = computetuplefreqhash(pamP, tupleArray, maxsize, 
Packit 78deda
                                         newDepth, newMaxval, &uniqueCount);
Packit 78deda
    if (tuplefreqhash == NULL)
Packit 78deda
        tuplefreqtable = NULL;
Packit 78deda
    else {
Packit 78deda
        unsigned int tableSize = (maxsize == 0 ? uniqueCount : maxsize);
Packit 78deda
        assert(tableSize >= uniqueCount);
Packit 78deda
        tuplefreqtable = tuplehashtotable(pamP, tuplefreqhash, tableSize);
Packit 78deda
        pnm_destroytuplehash(tuplefreqhash);
Packit 78deda
        if (tuplefreqtable == NULL)
Packit 78deda
            pm_error("Out of memory generating tuple table");
Packit 78deda
    }
Packit 78deda
    *countP = uniqueCount;
Packit 78deda
Packit 78deda
    return tuplefreqtable;
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
tupletable
Packit 78deda
pnm_computetuplefreqtable2(struct pam *   const pamP,
Packit 78deda
                           tuple **       const tupleArray,
Packit 78deda
                           unsigned int   const maxsize,
Packit 78deda
                           sample         const newMaxval,
Packit 78deda
                           unsigned int * const countP) {
Packit 78deda
Packit 78deda
    return
Packit 78deda
        pnm_computetuplefreqtable3(pamP, tupleArray, maxsize,
Packit 78deda
                                   pamP->depth, newMaxval, countP);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
tupletable
Packit 78deda
pnm_computetuplefreqtable(struct pam *   const pamP,
Packit 78deda
                          tuple **       const tupleArray,
Packit 78deda
                          unsigned int   const maxsize,
Packit 78deda
                          unsigned int * const sizeP) {
Packit 78deda
Packit 78deda
    return pnm_computetuplefreqtable2(pamP, tupleArray, maxsize, pamP->maxval,
Packit 78deda
                                      sizeP);
Packit 78deda
}
Packit 78deda
Packit 78deda
Packit 78deda
Packit 78deda
char*
Packit 78deda
pam_colorname(struct pam *         const pamP, 
Packit 78deda
              tuple                const color, 
Packit 78deda
              enum colornameFormat const format) {
Packit 78deda
Packit 78deda
    unsigned int r, g, b;
Packit 78deda
    FILE* f;
Packit 78deda
    static char colorname[200];
Packit 78deda
Packit 78deda
    r = pnm_scalesample(color[PAM_RED_PLANE], pamP->maxval, 255);
Packit 78deda
    g = pnm_scalesample(color[PAM_GRN_PLANE], pamP->maxval, 255);
Packit 78deda
    b = pnm_scalesample(color[PAM_BLU_PLANE], pamP->maxval, 255);
Packit 78deda
Packit 78deda
    f = pm_openColornameFile(NULL, format == PAM_COLORNAME_ENGLISH);
Packit 78deda
    if (f != NULL) {
Packit 78deda
        unsigned int best_diff;
Packit 78deda
        bool done;
Packit 78deda
Packit 78deda
        best_diff = 32767;
Packit 78deda
        done = FALSE;
Packit 78deda
        while (!done) {
Packit 78deda
            struct colorfile_entry const ce = pm_colorget(f);
Packit 78deda
            if (ce.colorname) {
Packit 78deda
                unsigned int const this_diff = 
Packit 78deda
                    abs((int)r - (int)ce.r) + 
Packit 78deda
                    abs((int)g - (int)ce.g) + 
Packit 78deda
                    abs((int)b - (int)ce.b);
Packit 78deda
Packit 78deda
                if (this_diff < best_diff) {
Packit 78deda
                    best_diff = this_diff;
Packit 78deda
                    strcpy(colorname, ce.colorname);
Packit 78deda
                }
Packit 78deda
            } else
Packit 78deda
                done = TRUE;
Packit 78deda
        }
Packit 78deda
        fclose(f);
Packit 78deda
        if (best_diff != 32767 && 
Packit 78deda
            (best_diff == 0 || format == PAM_COLORNAME_ENGLISH))
Packit 78deda
            return colorname;
Packit 78deda
    }
Packit 78deda
Packit 78deda
    /* Color lookup failed, but caller is willing to take an X11-style
Packit 78deda
       hex specifier, so return that.
Packit 78deda
    */
Packit 78deda
    sprintf(colorname, "#%02x%02x%02x", r, g, b);
Packit 78deda
    return colorname;
Packit 78deda
}
Packit 78deda
Packit 78deda