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