1 /*=============================================================================
2                                   libpammap.c
3 ===============================================================================
4 
5   These are functions that deal with tuple hashes and tuple tables.
6 
7   Both tuple hashes and tuple tables let you associate an arbitrary
8   integer with a tuple value.  A tuple hash lets you look up the one
9   integer value (if any) associated with a given tuple value, having
10   the low memory and execution time characteristics of a hash table.
11   A tuple table lets you scan all the values, being a table of elements
12   that consist of an ordered pair of a tuple value and integer.
13 
14   This file was originally written by Bryan Henderson and is contributed
15   to the public domain by him and subsequent authors.
16 =============================================================================*/
17 
18 #include <assert.h>
19 
20 #include "netpbm/pm_c_util.h"
21 #include "netpbm/mallocvar.h"
22 #include "netpbm/nstring.h"
23 
24 #include "pam.h"
25 #include "pammap.h"
26 
27 
28 #define HASH_SIZE 20023
29 
30 unsigned int
pnm_hashtuple(struct pam * const pamP,tuple const tuple)31 pnm_hashtuple(struct pam * const pamP,
32               tuple        const tuple) {
33 /*----------------------------------------------------------------------------
34    Return the hash value of the tuple 'tuple' -- i.e. an index into a hash
35    table.
36 -----------------------------------------------------------------------------*/
37     unsigned int const hash_factor[] = {1, 33, 33*33};
38 
39     unsigned int i;
40     unsigned int hash;
41 
42     hash = 0;  /* initial value */
43     for (i = 0; i < MIN(pamP->depth, 3); ++i) {
44         hash += tuple[i] * hash_factor[i];
45     }
46     hash %= HASH_SIZE;
47     return hash;
48 }
49 
50 
51 
52 
53 tuplehash
pnm_createtuplehash(void)54 pnm_createtuplehash(void) {
55 /*----------------------------------------------------------------------------
56    Create an empty tuple hash -- i.e. a hash table of zero length hash chains.
57 -----------------------------------------------------------------------------*/
58     tuplehash retval;
59     unsigned int i;
60 
61     MALLOCARRAY(retval, HASH_SIZE);
62 
63     if (retval == NULL)
64         pm_error("Out of memory allocating tuple hash of size %u",
65                  HASH_SIZE);
66 
67     for (i = 0; i < HASH_SIZE; ++i)
68         retval[i] = NULL;
69 
70     return retval;
71 }
72 
73 
74 
75 void
pnm_destroytuplehash(tuplehash const tuplehash)76 pnm_destroytuplehash(tuplehash const tuplehash) {
77 
78     unsigned int i;
79 
80     /* Free the chains */
81 
82     for (i = 0; i < HASH_SIZE; ++i) {
83         struct tupleint_list_item * p;
84         struct tupleint_list_item * next;
85 
86         /* Walk this chain, freeing each element */
87         for (p = tuplehash[i]; p; p = next) {
88             next = p->next;
89 
90             free(p);
91         }
92     }
93 
94     /* Free the table of chains */
95     free(tuplehash);
96 }
97 
98 
99 
100 
101 static struct tupleint_list_item *
allocTupleIntListItem(struct pam * const pamP)102 allocTupleIntListItem(struct pam * const pamP) {
103 
104 
105     /* This is complicated by the fact that the last element of a
106        tupleint_list_item is of variable length, because the last element
107        of _it_ is of variable length
108     */
109     struct tupleint_list_item * retval;
110 
111     unsigned int const size =
112         sizeof(*retval) - sizeof(retval->tupleint.tuple)
113         + pamP->depth * sizeof(sample);
114 
115     retval = (struct tupleint_list_item *) malloc(size);
116 
117     return retval;
118 }
119 
120 
121 
122 void
pnm_addtotuplehash(struct pam * const pamP,tuplehash const tuplehash,tuple const tupletoadd,int const value,int * const fitsP)123 pnm_addtotuplehash(struct pam *   const pamP,
124                    tuplehash      const tuplehash,
125                    tuple          const tupletoadd,
126                    int            const value,
127                    int *          const fitsP) {
128 /*----------------------------------------------------------------------------
129    Add a tuple value to the hash -- assume it isn't already there.
130 
131    Allocate new space for the tuple value and the hash chain element.
132 
133    If we can't allocate space for the new hash chain element, don't
134    change anything and return *fitsP = FALSE;
135 -----------------------------------------------------------------------------*/
136     struct tupleint_list_item * const listItemP = allocTupleIntListItem(pamP);
137     if (listItemP == NULL)
138         *fitsP = FALSE;
139     else {
140         unsigned int const hashvalue = pnm_hashtuple(pamP, tupletoadd);
141 
142         *fitsP = TRUE;
143 
144         pnm_assigntuple(pamP, listItemP->tupleint.tuple, tupletoadd);
145         listItemP->tupleint.value = value;
146         listItemP->next = tuplehash[hashvalue];
147         tuplehash[hashvalue] = listItemP;
148     }
149 }
150 
151 
152 
153 void
pnm_lookuptuple(struct pam * const pamP,const tuplehash tuplehash,const tuple searchval,int * const foundP,int * const retvalP)154 pnm_lookuptuple(struct pam *    const pamP,
155                 const tuplehash       tuplehash,
156                 const tuple           searchval,
157                 int *           const foundP,
158                 int *           const retvalP) {
159 /*----------------------------------------------------------------------------
160    Return as *revtvalP the index of the tuple value 'searchval' in the
161    tuple hash 'tuplehash'.
162 
163    But iff the tuple value isn't in the hash, return *foundP == false
164    and nothing as *retvalP.
165 -----------------------------------------------------------------------------*/
166     unsigned int const hashvalue = pnm_hashtuple(pamP, searchval);
167     struct tupleint_list_item * p;
168     struct tupleint_list_item * found;
169 
170     found = NULL;  /* None found yet */
171     for (p = tuplehash[hashvalue]; p && !found; p = p->next)
172         if (pnm_tupleequal(pamP, p->tupleint.tuple, searchval)) {
173             found = p;
174         }
175 
176     if (found) {
177         *foundP = TRUE;
178         *retvalP = found->tupleint.value;
179     } else
180         *foundP = FALSE;
181 }
182 
183 
184 
185 static void
addColorOccurrenceToHash(tuple const color,tuplehash const tuplefreqhash,struct pam * const pamP,unsigned int const maxsize,unsigned int * const sizeP,bool * const fullP)186 addColorOccurrenceToHash(tuple          const color,
187                          tuplehash      const tuplefreqhash,
188                          struct pam *   const pamP,
189                          unsigned int   const maxsize,
190                          unsigned int * const sizeP,
191                          bool *         const fullP) {
192 
193     unsigned int const hashvalue = pnm_hashtuple(pamP, color);
194 
195     struct tupleint_list_item *p;
196 
197     for (p = tuplefreqhash[hashvalue];
198          p && !pnm_tupleequal(pamP, p->tupleint.tuple, color);
199          p = p->next);
200 
201     if (p) {
202         /* It's in the hash; just tally one more occurrence */
203         ++p->tupleint.value;
204         *fullP = FALSE;
205     } else {
206         /* It's not in the hash yet, so add it (if allowed) */
207         ++(*sizeP);
208         if (maxsize > 0 && *sizeP > maxsize)
209             *fullP = TRUE;
210         else {
211             *fullP = FALSE;
212             p = allocTupleIntListItem(pamP);
213             if (p == NULL)
214                 pm_error("out of memory computing hash table");
215             pnm_assigntuple(pamP, p->tupleint.tuple, color);
216             p->tupleint.value = 1;
217             p->next = tuplefreqhash[hashvalue];
218             tuplefreqhash[hashvalue] = p;
219         }
220     }
221 }
222 
223 
224 
225 void
pnm_addtuplefreqoccurrence(struct pam * const pamP,tuple const value,tuplehash const tuplefreqhash,int * const firstOccurrenceP)226 pnm_addtuplefreqoccurrence(struct pam *   const pamP,
227                            tuple          const value,
228                            tuplehash      const tuplefreqhash,
229                            int *          const firstOccurrenceP) {
230 /*----------------------------------------------------------------------------
231   Tally one more occurrence of the tuple value 'value' to the tuple frequency
232   hash 'tuplefreqhash', adding the tuple to the hash if it isn't there
233   already.
234 
235   Allocate new space for the tuple value and the hash chain element.
236 
237   If we can't allocate space for the new hash chain element, abort the
238   program.
239 -----------------------------------------------------------------------------*/
240     unsigned int const hashvalue = pnm_hashtuple(pamP, value);
241 
242     struct tupleint_list_item * p;
243 
244     for (p = tuplefreqhash[hashvalue];
245          p && !pnm_tupleequal(pamP, p->tupleint.tuple, value);
246          p = p->next);
247 
248     if (p) {
249         /* It's in the hash; just tally one more occurrence */
250         ++p->tupleint.value;
251         *firstOccurrenceP = FALSE;
252     } else {
253         struct tupleint_list_item * p;
254 
255         /* It's not in the hash yet, so add it */
256         *firstOccurrenceP = TRUE;
257 
258         p = allocTupleIntListItem(pamP);
259         if (p == NULL)
260             pm_error("out of memory computing hash table");
261 
262         pnm_assigntuple(pamP, p->tupleint.tuple, value);
263         p->tupleint.value = 1;
264         p->next = tuplefreqhash[hashvalue];
265         tuplefreqhash[hashvalue] = p;
266     }
267 }
268 
269 
270 
271 static void
computehashrecoverable(struct pam * const pamP,tuple ** const tupleArray,unsigned int const maxsize,unsigned int const newDepth,sample const newMaxval,unsigned int * const sizeP,tuplehash * const tuplefreqhashP,tuple ** const rowbufferP,tuple * const colorP)272 computehashrecoverable(struct pam *   const pamP,
273                        tuple **       const tupleArray,
274                        unsigned int   const maxsize,
275                        unsigned int   const newDepth,
276                        sample         const newMaxval,
277                        unsigned int * const sizeP,
278                        tuplehash *    const tuplefreqhashP,
279                        tuple **       const rowbufferP,
280                        tuple *        const colorP) {
281 /*----------------------------------------------------------------------------
282    This is computetuplefreqhash(), only it leaves a trail so that if it
283    happens to longjmp out because of a failed memory allocation, the
284    setjmp'er can cleanup whatever it had done so far.
285 -----------------------------------------------------------------------------*/
286     unsigned int row;
287     struct pam freqPam;
288     bool full;
289 
290     freqPam = *pamP;
291     freqPam.maxval = newMaxval;
292     freqPam.depth = newDepth;
293 
294     assert(freqPam.depth <= pamP->depth);
295 
296     *tuplefreqhashP = pnm_createtuplehash();
297     *sizeP = 0;   /* initial value */
298 
299     *rowbufferP = pnm_allocpamrow(pamP);
300 
301     *colorP = pnm_allocpamtuple(pamP);
302 
303     full = FALSE;  /* initial value */
304 
305     /* Go through the entire raster, building a hash table of
306        tuple values.
307     */
308     for (row = 0; row < pamP->height && !full; ++row) {
309         unsigned int col;
310         const tuple * tuplerow;  /* The row of tuples we are processing */
311 
312         if (tupleArray)
313             tuplerow = tupleArray[row];
314         else {
315             pnm_readpamrow(pamP, *rowbufferP);
316             tuplerow = *rowbufferP;
317         }
318         for (col = 0; col < pamP->width && !full; ++col) {
319             pnm_scaletuple(pamP, *colorP, tuplerow[col], freqPam.maxval);
320             addColorOccurrenceToHash(
321                 *colorP, *tuplefreqhashP, &freqPam, maxsize, sizeP, &full);
322         }
323     }
324 
325     pnm_freepamtuple(*colorP); *colorP = NULL;
326     pnm_freepamrow(*rowbufferP); *rowbufferP = NULL;
327 
328     if (full) {
329         pnm_destroytuplehash(*tuplefreqhashP);
330         *tuplefreqhashP = NULL;
331     }
332 }
333 
334 
335 
336 static tuplehash
computetuplefreqhash(struct pam * const pamP,tuple ** const tupleArray,unsigned int const maxsize,unsigned int const newDepth,sample const newMaxval,unsigned int * const sizeP)337 computetuplefreqhash(struct pam *   const pamP,
338                      tuple **       const tupleArray,
339                      unsigned int   const maxsize,
340                      unsigned int   const newDepth,
341                      sample         const newMaxval,
342                      unsigned int * const sizeP) {
343 /*----------------------------------------------------------------------------
344   Compute a tuple frequency hash from a PAM.  This is a hash that gives
345   you the number of times a given tuple value occurs in the PAM.  You can
346   supply the input PAM in one of two ways:
347 
348   1) a two-dimensional array of tuples tupleArray[][];  In this case,
349      'tupleArray' is non-NULL.
350 
351   2) an open PAM file, positioned to the raster.  In this case,
352      'tupleArray' is NULL.  *pamP contains the file descriptor.
353 
354      We return with the file still open and its position undefined.
355 
356   In either case, *pamP contains parameters of the tuple array.
357 
358   Return the number of unique tuple values found as *sizeP.
359 
360   However, if the number of unique tuple values is greater than 'maxsize',
361   return a null return value and *sizeP undefined.
362 
363   The tuple values that index the hash have depth 'newDepth'.  We look at
364   only the first 'newDepth' planes of the input.  Caller must ensure that
365   the input has at least that many planes.
366 
367   The tuple values that index the hash are scaled to a new maxval of
368   'newMaxval'.  E.g.  if the input has maxval 100 and 'newMaxval' is
369   50, and a particular tuple has sample value 50, it would be counted
370   as sample value 25 in the hash.
371 -----------------------------------------------------------------------------*/
372     tuplehash tuplefreqhash;
373     tuple * rowbuffer;  /* malloc'ed */
374         /* Buffer for a row read from the input file; undefined (but still
375            allocated) if input is not from a file.
376         */
377     tuple color;
378         /* The color currently being added, scaled to the new maxval */
379     jmp_buf jmpbuf;
380     jmp_buf * origJmpbufP;
381 
382     /* Initialize to "none" for purposes of error recovery */
383     tuplefreqhash = NULL;
384     rowbuffer = NULL;
385     color = NULL;
386 
387     if (setjmp(jmpbuf) != 0) {
388         if (color)
389             pnm_freepamtuple(color);
390         if (rowbuffer)
391             pnm_freepamrow(rowbuffer);
392         if (tuplefreqhash)
393             pnm_destroytuplehash(tuplefreqhash);
394         pm_setjmpbuf(origJmpbufP);
395         pm_longjmp();
396     } else {
397         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
398         computehashrecoverable(pamP, tupleArray, maxsize, newDepth, newMaxval,
399                                sizeP, &tuplefreqhash, &rowbuffer, &color);
400         pm_setjmpbuf(origJmpbufP);
401     }
402     return tuplefreqhash;
403 }
404 
405 
406 
407 tuplehash
pnm_computetuplefreqhash(struct pam * const pamP,tuple ** const tupleArray,unsigned int const maxsize,unsigned int * const sizeP)408 pnm_computetuplefreqhash(struct pam *   const pamP,
409                          tuple **       const tupleArray,
410                          unsigned int   const maxsize,
411                          unsigned int * const sizeP) {
412 /*----------------------------------------------------------------------------
413    Compute the tuple frequency hash for the tuple array tupleArray[][].
414 -----------------------------------------------------------------------------*/
415     return computetuplefreqhash(pamP, tupleArray, maxsize,
416                                 pamP->depth, pamP->maxval,
417                                 sizeP);
418 }
419 
420 
421 
422 static void
alloctupletable(const struct pam * const pamP,unsigned int const size,tupletable * const tupletableP,const char ** const errorP)423 alloctupletable(const struct pam * const pamP,
424                 unsigned int       const size,
425                 tupletable *       const tupletableP,
426                 const char **      const errorP) {
427 
428     if (UINT_MAX / sizeof(struct tupleint) < size)
429         pm_asprintf(errorP, "size %u is too big for arithmetic", size);
430     else {
431         unsigned int const mainTableSize = size * sizeof(struct tupleint *);
432         unsigned int const tupleIntSize =
433             sizeof(struct tupleint) - sizeof(sample)
434             + pamP->depth * sizeof(sample);
435 
436         /* To save the enormous amount of time it could take to allocate
437            each individual tuple, we do a trick here and allocate everything
438            as a single malloc block and suballocate internally.
439         */
440         if ((UINT_MAX - mainTableSize) / tupleIntSize < size)
441             pm_asprintf(errorP, "size %u is too big for arithmetic", size);
442         else {
443             unsigned int const allocSize = mainTableSize + size * tupleIntSize;
444             void * pool;
445 
446             pool = malloc(allocSize);
447 
448             if (!pool)
449                 pm_asprintf(errorP,
450                             "Unable to allocate %u bytes for a %u-entry "
451                             "tuple table", allocSize, size);
452             else {
453                 tupletable const tbl = (tupletable) pool;
454 
455                 unsigned int i;
456 
457                 *errorP = NULL;
458 
459                 for (i = 0; i < size; ++i)
460                     tbl[i] = (struct tupleint *)
461                         ((char*)pool + mainTableSize + i * tupleIntSize);
462 
463                 *tupletableP = tbl;
464             }
465         }
466     }
467 }
468 
469 
470 
471 tupletable
pnm_alloctupletable(const struct pam * const pamP,unsigned int const size)472 pnm_alloctupletable(const struct pam * const pamP,
473                     unsigned int       const size) {
474 
475     tupletable retval;
476     const char * error;
477 
478     alloctupletable(pamP, size, &retval, &error);
479 
480     if (error) {
481         pm_errormsg("%s", error);
482         pm_strfree(error);
483         pm_longjmp();
484     }
485     return retval;
486 }
487 
488 
489 
490 void
pnm_freetupletable(const struct pam * const pamP,tupletable const tupletable)491 pnm_freetupletable(const struct pam * const pamP,
492                    tupletable         const tupletable) {
493 
494     /* Note that the address 'tupletable' is, to the operating system,
495        the address of a larger block of memory that contains not only
496        tupletable, but all the samples to which it points (e.g.
497        tupletable[0].tuple[0])
498     */
499 
500     free(tupletable);
501 }
502 
503 
504 
505 void
pnm_freetupletable2(const struct pam * const pamP,tupletable2 const tupletable)506 pnm_freetupletable2(const struct pam * const pamP,
507                     tupletable2        const tupletable) {
508 
509     pnm_freetupletable(pamP, tupletable.table);
510 }
511 
512 
513 
514 static tupletable
tuplehashtotable(const struct pam * const pamP,tuplehash const tuplehash,unsigned int const allocsize)515 tuplehashtotable(const struct pam * const pamP,
516                  tuplehash          const tuplehash,
517                  unsigned int       const allocsize) {
518 /*----------------------------------------------------------------------------
519    Create a tuple table containing the info from a tuple hash.  Allocate
520    space in the table for 'allocsize' elements even if there aren't that
521    many tuple values in the input hash.  That's so the caller has room
522    for expansion.
523 
524    Caller must ensure that 'allocsize' is at least as many tuple values
525    as there are in the input hash.
526 
527    We allocate new space for all the table contents; there are no pointers
528    in the table to tuples or anything else in existing space.
529 -----------------------------------------------------------------------------*/
530     tupletable tupletable;
531     const char * error;
532 
533     alloctupletable(pamP, allocsize, &tupletable, &error);
534 
535     if (error) {
536         pm_errormsg("%s", error);
537         pm_strfree(error);
538         pm_longjmp();
539     } else {
540         unsigned int i, j;
541         /* Loop through the hash table. */
542         j = 0;
543         for (i = 0; i < HASH_SIZE; ++i) {
544             /* Walk this hash chain */
545             struct tupleint_list_item * p;
546             for (p = tuplehash[i]; p; p = p->next) {
547                 assert(j < allocsize);
548                 tupletable[j]->value = p->tupleint.value;
549                 pnm_assigntuple(pamP, tupletable[j]->tuple, p->tupleint.tuple);
550                 ++j;
551             }
552         }
553     }
554     return tupletable;
555 }
556 
557 
558 
559 tupletable
pnm_tuplehashtotable(const struct pam * const pamP,tuplehash const tuplehash,unsigned int const allocsize)560 pnm_tuplehashtotable(const struct pam * const pamP,
561                      tuplehash          const tuplehash,
562                      unsigned int       const allocsize) {
563 
564     tupletable tupletable;
565 
566     tupletable = tuplehashtotable(pamP, tuplehash, allocsize);
567 
568     if (tupletable == NULL)
569         pm_error("out of memory generating tuple table");
570 
571     return tupletable;
572 }
573 
574 
575 
576 tuplehash
pnm_computetupletablehash(struct pam * const pamP,tupletable const tupletable,unsigned int const tupletableSize)577 pnm_computetupletablehash(struct pam * const pamP,
578                           tupletable   const tupletable,
579                           unsigned int const tupletableSize) {
580 /*----------------------------------------------------------------------------
581    Create a tuple hash containing indices into the tuple table
582    'tupletable'.  The hash index for the hash is the value of a tuple;
583    the hash value is the tuple table index for the element in the
584    tuple table that contains that tuple value.
585 
586    Assume there are no duplicate tuple values in the tuple table.
587 
588    We allocate space for the main hash table and all the elements of the
589    hash chains.
590 -----------------------------------------------------------------------------*/
591     tuplehash tupletablehash;
592     unsigned int i;
593     int fits;
594 
595     tupletablehash = pnm_createtuplehash();
596 
597     fits = TRUE;  /* initial assumption */
598     for (i = 0; i < tupletableSize && fits; ++i) {
599         pnm_addtotuplehash(pamP, tupletablehash,
600                            tupletable[i]->tuple, i, &fits);
601     }
602     if (!fits) {
603         pnm_destroytuplehash(tupletablehash);
604         pm_error("Out of memory computing tuple hash from tuple table");
605     }
606     return tupletablehash;
607 }
608 
609 
610 
611 tupletable
pnm_computetuplefreqtable3(struct pam * const pamP,tuple ** const tupleArray,unsigned int const maxsize,unsigned int const newDepth,sample const newMaxval,unsigned int * const countP)612 pnm_computetuplefreqtable3(struct pam *   const pamP,
613                            tuple **       const tupleArray,
614                            unsigned int   const maxsize,
615                            unsigned int   const newDepth,
616                            sample         const newMaxval,
617                            unsigned int * const countP) {
618 /*----------------------------------------------------------------------------
619    Compute a tuple frequency table from a PAM image.  This is an
620    array that tells how many times each tuple value occurs in the
621    image.
622 
623    Except for the format of the output, this function is the same as
624    computetuplefreqhash().
625 
626    If there are more than 'maxsize' unique tuple values in tupleArray[][],
627    give up.
628 
629    Return the array in newly malloc'ed storage.  Allocate space for
630    'maxsize' entries even if there aren't that many distinct tuple
631    values in tupleArray[].  That's so the caller has room for
632    expansion.
633 
634    If 'maxsize' is zero, allocate exactly as much space as there are
635    distinct tuple values in tupleArray[], and don't give up no matter
636    how many tuple values we find (except, of course, we abort if we
637    can't get enough memory).
638 
639    Return the number of unique tuple values in tupleArray[][] as
640    *countP.
641 
642    The tuples in the table have depth 'newDepth'.  We look at
643    only the first 'newDepth' planes of the input.  If the input doesn't
644    have that many planes, we throw an error.
645 
646    Scale the tuple values to a new maxval of 'newMaxval' before
647    processing them.  E.g. if the input has maxval 100 and 'newMaxval'
648    is 50, and a particular tuple has sample value 50, it would be
649    listed as sample value 25 in the output table.  This makes the
650    output table smaller and the processing time less.
651 -----------------------------------------------------------------------------*/
652     tuplehash tuplefreqhash;
653     tupletable tuplefreqtable;
654     unsigned int uniqueCount;
655 
656     if (newDepth > pamP->depth)
657         pm_error("pnm_computetuplefreqtable3 called with 'newDepth' "
658                  "argument (%u) greater than input depth (%u)",
659                  newDepth, pamP->depth);
660 
661     tuplefreqhash = computetuplefreqhash(pamP, tupleArray, maxsize,
662                                          newDepth, newMaxval, &uniqueCount);
663     if (tuplefreqhash == NULL)
664         tuplefreqtable = NULL;
665     else {
666         unsigned int tableSize = (maxsize == 0 ? uniqueCount : maxsize);
667         assert(tableSize >= uniqueCount);
668         tuplefreqtable = tuplehashtotable(pamP, tuplefreqhash, tableSize);
669         pnm_destroytuplehash(tuplefreqhash);
670         if (tuplefreqtable == NULL)
671             pm_error("Out of memory generating tuple table");
672     }
673     *countP = uniqueCount;
674 
675     return tuplefreqtable;
676 }
677 
678 
679 
680 tupletable
pnm_computetuplefreqtable2(struct pam * const pamP,tuple ** const tupleArray,unsigned int const maxsize,sample const newMaxval,unsigned int * const countP)681 pnm_computetuplefreqtable2(struct pam *   const pamP,
682                            tuple **       const tupleArray,
683                            unsigned int   const maxsize,
684                            sample         const newMaxval,
685                            unsigned int * const countP) {
686 
687     return
688         pnm_computetuplefreqtable3(pamP, tupleArray, maxsize,
689                                    pamP->depth, newMaxval, countP);
690 }
691 
692 
693 
694 tupletable
pnm_computetuplefreqtable(struct pam * const pamP,tuple ** const tupleArray,unsigned int const maxsize,unsigned int * const sizeP)695 pnm_computetuplefreqtable(struct pam *   const pamP,
696                           tuple **       const tupleArray,
697                           unsigned int   const maxsize,
698                           unsigned int * const sizeP) {
699 
700     return pnm_computetuplefreqtable2(pamP, tupleArray, maxsize, pamP->maxval,
701                                       sizeP);
702 }
703 
704 
705 
706 char*
pam_colorname(struct pam * const pamP,tuple const color,enum colornameFormat const format)707 pam_colorname(struct pam *         const pamP,
708               tuple                const color,
709               enum colornameFormat const format) {
710 
711     unsigned int r, g, b;
712     FILE* f;
713     static char colorname[200];
714 
715     r = pnm_scalesample(color[PAM_RED_PLANE], pamP->maxval, 255);
716     g = pnm_scalesample(color[PAM_GRN_PLANE], pamP->maxval, 255);
717     b = pnm_scalesample(color[PAM_BLU_PLANE], pamP->maxval, 255);
718 
719     f = pm_openColornameFile(NULL, format == PAM_COLORNAME_ENGLISH);
720     if (f != NULL) {
721         unsigned int best_diff;
722         bool done;
723 
724         best_diff = 32767;
725         done = FALSE;
726         while (!done) {
727             struct colorfile_entry const ce = pm_colorget(f);
728             if (ce.colorname) {
729                 unsigned int const this_diff =
730                     abs((int)r - (int)ce.r) +
731                     abs((int)g - (int)ce.g) +
732                     abs((int)b - (int)ce.b);
733 
734                 if (this_diff < best_diff) {
735                     best_diff = this_diff;
736                     strcpy(colorname, ce.colorname);
737                 }
738             } else
739                 done = TRUE;
740         }
741         fclose(f);
742         if (best_diff != 32767 &&
743             (best_diff == 0 || format == PAM_COLORNAME_ENGLISH))
744             return colorname;
745     }
746 
747     /* Color lookup failed, but caller is willing to take an X11-style
748        hex specifier, so return that.
749     */
750     sprintf(colorname, "#%02x%02x%02x", r, g, b);
751     return colorname;
752 }
753 
754 
755