|
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 |
|