1 /* libppm3.c - ppm utility library part 3
2 **
3 ** Colormap routines.
4 **
5 ** Copyright (C) 1989, 1991 by Jef Poskanzer.
6 **
7 ** Permission to use, copy, modify, and distribute this software and its
8 ** documentation for any purpose and without fee is hereby granted, provided
9 ** that the above copyright notice appear in all copies and that both that
10 ** copyright notice and this permission notice appear in supporting
11 ** documentation.  This software is provided "as is" without express or
12 ** implied warranty.
13 */
14 
15 #include "netpbm/pm_config.h"
16 #include "netpbm/pm_c_util.h"
17 #include "netpbm/nstring.h"
18 #include "netpbm/mallocvar.h"
19 #include "ppm.h"
20 #include "ppmcmap.h"
21 
22 #define HASH_SIZE 20023
23 
24 
25 
26 static __inline__ unsigned int
ppm_hashpixel(pixel const p)27 ppm_hashpixel(pixel const p) {
28 
29     return (unsigned int) (PPM_GETR(p) * 33 * 33
30                            + PPM_GETG(p) * 33
31                            + PPM_GETB(p)) % HASH_SIZE;
32 }
33 
34 
35 
36 colorhist_vector
ppm_computecolorhist(pixel ** const pixels,const int cols,const int rows,const int maxcolors,int * const colorsP)37 ppm_computecolorhist( pixel ** const pixels,
38                       const int cols, const int rows, const int maxcolors,
39                       int * const colorsP ) {
40 /*----------------------------------------------------------------------------
41    Compute a color histogram for the image described by 'pixels',
42    'cols', and 'rows'.  I.e. a colorhist_vector containing an entry
43    for each color in the image and for each one the number of pixels
44    of that color (i.e. a color histogram).
45 
46    If 'maxcolors' is zero, make the output have 5 spare slots at the end
47    for expansion.
48 
49    If 'maxcolors' is nonzero, make the output have 'maxcolors' slots in
50    it, and if there are more colors than that in the image, don't return
51    anything except a NULL pointer.
52 -----------------------------------------------------------------------------*/
53     colorhash_table cht;
54     colorhist_vector chv;
55 
56     cht = ppm_computecolorhash(pixels, cols, rows, maxcolors, colorsP);
57     if (cht == NULL)
58         chv = NULL;
59     else {
60         chv = ppm_colorhashtocolorhist(cht, maxcolors);
61         ppm_freecolorhash(cht);
62     }
63     return chv;
64 }
65 
66 
67 
68 colorhist_vector
ppm_computecolorhist2(FILE * const ifP,int const cols,int const rows,pixval const maxval,int const format,int const maxcolorCt,int * const colorCtP)69 ppm_computecolorhist2(FILE * const ifP,
70                       int    const cols,
71                       int    const rows,
72                       pixval const maxval,
73                       int    const format,
74                       int    const maxcolorCt,
75                       int *  const colorCtP ) {
76 
77     colorhist_vector retval;
78     colorhash_table  cht;
79 
80     cht = ppm_computecolorhash2(ifP, cols, rows, maxval, format,
81                                 maxcolorCt, colorCtP);
82     if (cht ==NULL)
83         retval = NULL;
84     else {
85         retval = ppm_colorhashtocolorhist(cht, maxcolorCt);
86 
87         ppm_freecolorhash(cht);
88     }
89 
90     return retval;
91 }
92 
93 
94 
95 void
ppm_addtocolorhist(colorhist_vector chv,int * const colorsP,const int maxcolors,const pixel * const colorP,const int value,const int position)96 ppm_addtocolorhist( colorhist_vector chv,
97                     int * const colorsP, const int maxcolors,
98                     const pixel * const colorP,
99                     const int value, const int position ) {
100     int i, j;
101 
102     /* Search colorhist for the color. */
103     for ( i = 0; i < *colorsP; ++i )
104         if ( PPM_EQUAL( chv[i].color, *colorP ) ) {
105             /* Found it - move to new slot. */
106             if ( position > i ) {
107                 for ( j = i; j < position; ++j )
108                     chv[j] = chv[j + 1];
109             } else if ( position < i ) {
110                 for ( j = i; j > position; --j )
111                     chv[j] = chv[j - 1];
112             }
113             chv[position].color = *colorP;
114             chv[position].value = value;
115             return;
116         }
117     if ( *colorsP < maxcolors ) {
118         /* Didn't find it, but there's room to add it; so do so. */
119         for ( i = *colorsP; i > position; --i )
120             chv[i] = chv[i - 1];
121         chv[position].color = *colorP;
122         chv[position].value = value;
123         ++(*colorsP);
124     }
125 }
126 
127 
128 
129 static colorhash_table
alloccolorhash(void)130 alloccolorhash(void)  {
131     colorhash_table cht;
132     int i;
133 
134     MALLOCARRAY(cht, HASH_SIZE);
135     if (cht) {
136         for (i = 0; i < HASH_SIZE; ++i)
137             cht[i] = NULL;
138     }
139     return cht;
140 }
141 
142 
143 
144 colorhash_table
ppm_alloccolorhash(void)145 ppm_alloccolorhash(void)  {
146     colorhash_table cht;
147 
148     cht = alloccolorhash();
149 
150     if (cht == NULL)
151         pm_error( "out of memory allocating hash table" );
152 
153     return cht;
154 }
155 
156 
157 
158 static void
readppmrow(FILE * const fileP,pixel * const pixelrow,int const cols,pixval const maxval,int const format,const char ** const errorP)159 readppmrow(FILE *        const fileP,
160            pixel *       const pixelrow,
161            int           const cols,
162            pixval        const maxval,
163            int           const format,
164            const char ** const errorP) {
165 
166     jmp_buf jmpbuf;
167     jmp_buf * origJmpbufP;
168 
169     if (setjmp(jmpbuf) != 0) {
170         pm_setjmpbuf(origJmpbufP);
171         pm_asprintf(errorP, "Failed to read row of image.");
172     } else {
173         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
174 
175         ppm_readppmrow(fileP, pixelrow, cols, maxval, format);
176 
177         *errorP = NULL; /* Would have longjmped if anything went wrong */
178 
179         pm_setjmpbuf(origJmpbufP);
180     }
181 }
182 
183 
184 
185 static void
buildHashTable(FILE * const ifP,pixel ** const pixels,unsigned int const cols,unsigned int const rows,pixval const maxval,int const format,unsigned int const maxcolors,colorhash_table const cht,pixel * const rowbuffer,int * const nColorsP,bool * const tooManyColorsP,const char ** const errorP)186 buildHashTable(FILE *          const ifP,
187                pixel **        const pixels,
188                unsigned int    const cols,
189                unsigned int    const rows,
190                pixval          const maxval,
191                int             const format,
192                unsigned int    const maxcolors,
193                colorhash_table const cht,
194                pixel *         const rowbuffer,
195                int *           const nColorsP,
196                bool *          const tooManyColorsP,
197                const char **   const errorP) {
198 /*----------------------------------------------------------------------------
199   Look at all the colors in the file *ifP or array pixels[][] and add
200   them to the hash table 'cht'.
201 
202   Even if we fail, we may add some colors to 'cht'.
203 
204   As soon as we've seen more that 'maxcolors' colors, we quit.  In that
205   case, only, we return *tooManyColorsP == true.  That is not a failure.
206   'maxcolors' == 0 means infinity.
207 -----------------------------------------------------------------------------*/
208     unsigned int row;
209     unsigned int nColors;
210 
211     nColors = 0;   /* initial value */
212     *tooManyColorsP = FALSE; /* initial value */
213     *errorP = NULL;  /* initial value */
214 
215     /* Go through the entire image, building a hash table of colors. */
216     for (row = 0; row < rows && !*tooManyColorsP && !*errorP; ++row) {
217         unsigned int col;
218         pixel * pixelrow;  /* The row of pixels we are processing */
219 
220         if (ifP) {
221             readppmrow(ifP, rowbuffer, cols, maxval, format, errorP);
222             pixelrow = rowbuffer;
223         } else
224             pixelrow = pixels[row];
225 
226         for (col = 0; col < cols && !*tooManyColorsP && !*errorP; ++col) {
227             const pixel apixel = pixelrow[col];
228             const int hash = ppm_hashpixel(apixel);
229             colorhist_list chl;
230 
231             for (chl = cht[hash];
232                  chl && !PPM_EQUAL(chl->ch.color, apixel);
233                  chl = chl->next);
234 
235             if (chl)
236                 ++chl->ch.value;
237             else {
238                 /* It's not in the hash yet, so add it (if allowed) */
239                 ++nColors;
240                 if (maxcolors > 0 && nColors > maxcolors)
241                     *tooManyColorsP = TRUE;
242                 else {
243                     MALLOCVAR(chl);
244                     if (chl == NULL)
245                         pm_asprintf(errorP,
246                                     "out of memory computing hash table");
247                     chl->ch.color = apixel;
248                     chl->ch.value = 1;
249                     chl->next = cht[hash];
250                     cht[hash] = chl;
251                 }
252             }
253         }
254     }
255     *nColorsP = nColors;
256 }
257 
258 
259 
260 static void
computecolorhash(pixel ** const pixels,unsigned int const cols,unsigned int const rows,unsigned int const maxcolors,int * const nColorsP,FILE * const ifP,pixval const maxval,int const format,colorhash_table * const chtP,const char ** const errorP)261 computecolorhash(pixel **          const pixels,
262                  unsigned int      const cols,
263                  unsigned int      const rows,
264                  unsigned int      const maxcolors,
265                  int *             const nColorsP,
266                  FILE *            const ifP,
267                  pixval            const maxval,
268                  int               const format,
269                  colorhash_table * const chtP,
270                  const char **     const errorP) {
271 /*----------------------------------------------------------------------------
272    Compute a color histogram from an image.  The input is one of two types:
273 
274    1) a two-dimensional array of pixels 'pixels';  In this case, 'pixels'
275       is non-NULL and 'ifP' is NULL.
276 
277    2) an open file, positioned to the image data.  In this case,
278       'pixels' is NULL and 'ifP' is non-NULL.  ifP is the stream
279       descriptor for the input file, and 'maxval' and 'format' are
280       parameters of the image data in it.
281 
282       We return with the file still open and its position undefined.
283 
284    In either case, the image is 'cols' by 'rows'.
285 
286    Return the number of colors found as *colorsP.
287 
288    However, if 'maxcolors' is nonzero and the number of colors is
289    greater than 'maxcolors', return a null return value and *colorsP
290    undefined.
291 -----------------------------------------------------------------------------*/
292     pixel * rowbuffer;  /* malloc'ed */
293         /* Buffer for a row read from the input file; undefined (but still
294            allocated) if input is not from a file.
295         */
296 
297     MALLOCARRAY(rowbuffer, cols);
298 
299     if (rowbuffer == NULL)
300         pm_asprintf(errorP, "Unable to allocate %u-column row buffer.", cols);
301     else {
302         colorhash_table cht;
303         bool tooManyColors;
304 
305         cht = alloccolorhash();
306 
307         if (cht == NULL)
308             pm_asprintf(errorP, "Unable to allocate color hash.");
309         else {
310             buildHashTable(ifP, pixels, cols, rows, maxval, format, maxcolors,
311                            cht, rowbuffer,
312                            nColorsP, &tooManyColors, errorP);
313 
314             if (tooManyColors) {
315                 ppm_freecolorhash(cht);
316                 *chtP = NULL;
317             } else
318                 *chtP = cht;
319 
320             if (*errorP)
321                 ppm_freecolorhash(cht);
322         }
323         free(rowbuffer);
324     }
325 }
326 
327 
328 
329 colorhash_table
ppm_computecolorhash(pixel ** const pixels,int const cols,int const rows,int const maxcolors,int * const colorsP)330 ppm_computecolorhash(pixel ** const pixels,
331                      int      const cols,
332                      int      const rows,
333                      int      const maxcolors,
334                      int *    const colorsP) {
335 
336     colorhash_table cht;
337     const char * error;
338 
339     computecolorhash(pixels, cols, rows, maxcolors, colorsP,
340                      NULL, 0, 0, &cht, &error);
341 
342     if (error) {
343         pm_errormsg("%s", error);
344         pm_strfree(error);
345         pm_longjmp();
346     }
347     return cht;
348 }
349 
350 
351 
352 colorhash_table
ppm_computecolorhash2(FILE * const ifP,int const cols,int const rows,pixval const maxval,int const format,int const maxcolors,int * const colorsP)353 ppm_computecolorhash2(FILE * const ifP,
354                       int    const cols,
355                       int    const rows,
356                       pixval const maxval,
357                       int    const format,
358                       int    const maxcolors,
359                       int *  const colorsP ) {
360 
361     colorhash_table cht;
362     const char * error;
363 
364     computecolorhash(NULL, cols, rows, maxcolors, colorsP,
365                      ifP, maxval, format, &cht, &error);
366 
367     if (error) {
368         pm_errormsg("%s", error);
369         pm_strfree(error);
370         pm_longjmp();
371     }
372     return cht;
373 }
374 
375 
376 
377 int
ppm_addtocolorhash(colorhash_table const cht,const pixel * const colorP,int const value)378 ppm_addtocolorhash(colorhash_table const cht,
379                    const pixel *   const colorP,
380                    int             const value) {
381 /*----------------------------------------------------------------------------
382    Add color *colorP to the color hash 'cht' with associated value 'value'.
383 
384    Assume the color is not already in the hash.
385 -----------------------------------------------------------------------------*/
386     int retval;
387     colorhist_list chl;
388 
389     MALLOCVAR(chl);
390     if (chl == NULL)
391         retval = -1;
392     else {
393         int const hash = ppm_hashpixel(*colorP);
394 
395         chl->ch.color = *colorP;
396         chl->ch.value = value;
397         chl->next = cht[hash];
398         cht[hash] = chl;
399         retval = 0;
400     }
401     return retval;
402 }
403 
404 
405 
406 void
ppm_delfromcolorhash(colorhash_table const cht,const pixel * const colorP)407 ppm_delfromcolorhash(colorhash_table const cht,
408                      const pixel *   const colorP) {
409 /*----------------------------------------------------------------------------
410    Delete the color *colorP from the colorhahs 'cht', if it's there.
411 -----------------------------------------------------------------------------*/
412     int hash;
413     colorhist_list * chlP;
414 
415     hash = ppm_hashpixel(*colorP);
416     for (chlP = &cht[hash]; *chlP; chlP = &(*chlP)->next) {
417         if (PPM_EQUAL((*chlP)->ch.color, *colorP)) {
418             /* chlP points to a pointer to the hash chain element we want
419                to remove.
420             */
421             colorhist_list const chl = *chlP;
422             *chlP = chl->next;
423             free(chl);
424             return;
425         }
426     }
427 }
428 
429 
430 
431 static unsigned int
colorHashSize(colorhash_table const cht)432 colorHashSize(colorhash_table const cht) {
433 /*----------------------------------------------------------------------------
434    Return the number of colors in the colorhash table 'cht'
435 -----------------------------------------------------------------------------*/
436     unsigned int nColors;
437         /* Number of colors found so far */
438     int i;
439     /* Loop through the hash table. */
440     nColors = 0;
441     for (i = 0; i < HASH_SIZE; ++i) {
442         colorhist_list chl;
443         for (chl = cht[i]; chl; chl = chl->next)
444             ++nColors;
445     }
446     return nColors;
447 }
448 
449 
450 
451 colorhist_vector
ppm_colorhashtocolorhist(colorhash_table const cht,int const maxcolors)452 ppm_colorhashtocolorhist(colorhash_table const cht, int const maxcolors) {
453 
454     colorhist_vector chv;
455     colorhist_list   chl;
456     unsigned int     chvSize;
457 
458     if (maxcolors == 0)
459         /* We leave space for 5 more colors so caller can add in special
460            colors like background color and transparent color.
461         */
462         chvSize = colorHashSize(cht) + 5;
463     else
464         /* Caller is responsible for making sure there are no more
465            than 'maxcolors' colors in the colorhash table.  NOTE:
466            Before March 2002, the maxcolors == 0 invocation didn't
467            exist.
468         */
469         chvSize = maxcolors;
470 
471     /* Collate the hash table into a simple colorhist array. */
472     MALLOCARRAY(chv, chvSize);
473     if (chv == NULL)
474         pm_error("out of memory generating histogram");
475 
476     {
477         int i, j;
478         /* Loop through the hash table. */
479         j = 0;
480         for (i = 0; i < HASH_SIZE; ++i)
481             for (chl = cht[i]; chl; chl = chl->next) {
482                 /* Add the new entry. */
483                 chv[j] = chl->ch;
484                 ++j;
485             }
486     }
487     return chv;
488 }
489 
490 
491 
492 colorhash_table
ppm_colorhisttocolorhash(colorhist_vector const chv,int const colors)493 ppm_colorhisttocolorhash(colorhist_vector const chv,
494                          int              const colors) {
495 
496     colorhash_table retval;
497     colorhash_table cht;
498     const char * error;
499 
500     cht = alloccolorhash( );  /* Initializes to NULLs */
501     if (cht == NULL)
502         pm_asprintf(&error, "Unable to allocate color hash");
503     else {
504         unsigned int i;
505 
506         for (i = 0, error = NULL; i < colors && !error; ++i) {
507             pixel const color = chv[i].color;
508             int const hash = ppm_hashpixel(color);
509 
510             colorhist_list chl;
511 
512             for (chl = cht[hash]; chl && !error; chl = chl->next)
513                 if (PPM_EQUAL(chl->ch.color, color))
514                     pm_asprintf(&error, "same color found twice: (%u %u %u)",
515                                 PPM_GETR(color),
516                                 PPM_GETG(color),
517                                 PPM_GETB(color));
518             MALLOCVAR(chl);
519             if (chl == NULL)
520                 pm_asprintf(&error, "out of memory");
521             else {
522                 chl->ch.color = color;
523                 chl->ch.value = i;
524                 chl->next = cht[hash];
525                 cht[hash] = chl;
526             }
527         }
528         if (error)
529             ppm_freecolorhash(cht);
530     }
531     if (error) {
532         pm_errormsg("%s", error);
533         pm_strfree(error);
534         pm_longjmp();
535     } else
536         retval = cht;
537 
538     return retval;
539 }
540 
541 
542 
543 int
ppm_lookupcolor(colorhash_table const cht,const pixel * const colorP)544 ppm_lookupcolor(colorhash_table const cht,
545                 const pixel *   const colorP) {
546     int hash;
547     colorhist_list chl;
548 
549     hash = ppm_hashpixel(*colorP);
550     for (chl = cht[hash]; chl; chl = chl->next)
551         if (PPM_EQUAL(chl->ch.color, *colorP))
552             return chl->ch.value;
553 
554     return -1;
555 }
556 
557 
558 
559 void
ppm_freecolorhist(colorhist_vector const chv)560 ppm_freecolorhist(colorhist_vector const chv) {
561     free(chv);
562 }
563 
564 
565 
566 void
ppm_freecolorhash(colorhash_table const cht)567 ppm_freecolorhash(colorhash_table const cht) {
568 
569     int i;
570     colorhist_list chl, chlnext;
571 
572     for (i = 0; i < HASH_SIZE; ++i)
573         for (chl = cht[i]; chl != (colorhist_list) 0; chl = chlnext) {
574             chlnext = chl->next;
575             free(chl);
576         }
577     free(cht);
578 }
579 
580 
581 /*****************************************************************************
582   The following "color row" routines are taken from Ingo Wilken's ilbm
583   package, dated December 1994.  Since they're only used by ppmtoilbm
584   and ilbmtoppm today, they aren't documented or well maintained, but
585   they seem pretty useful and ought to be used in other programs.
586 
587   -Bryan 2001.03.10
588 ****************************************************************************/
589 
590 /* libcmap2.c - support routines for color rows
591 **
592 ** Copyright (C) 1994 Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
593 **
594 ** Permission to use, copy, modify, and distribute this software and its
595 ** documentation for any purpose and without fee is hereby granted, provided
596 ** that the above copyright notice appear in all copies and that both that
597 ** copyright notice and this permission notice appear in supporting
598 ** documentation.  This software is provided "as is" without express or
599 ** implied warranty.
600 */
601 
602 colorhash_table
ppm_colorrowtocolorhash(colorrow,ncolors)603 ppm_colorrowtocolorhash(colorrow, ncolors)
604     pixel *colorrow;
605     int ncolors;
606 {
607     colorhash_table cht;
608     int i;
609 
610     cht = ppm_alloccolorhash();
611     for( i = 0; i < ncolors; i++ ) {
612         if( ppm_lookupcolor(cht, &colorrow[i]) < 0 ) {
613             if( ppm_addtocolorhash(cht, &colorrow[i], i) < 0 )
614                 pm_error("out of memory adding to hash table");
615         }
616     }
617     return cht;
618 }
619 
620 
621 pixel *
ppm_computecolorrow(pixels,cols,rows,maxcolors,ncolorsP)622 ppm_computecolorrow(pixels, cols, rows, maxcolors, ncolorsP)
623     pixel **pixels;
624     int cols, rows, maxcolors;
625     int *ncolorsP;
626 {
627     int ncolors, row, col;
628     colorhash_table cht;
629     pixel *pixrow;
630 
631     pixrow = ppm_allocrow(maxcolors);
632     cht = ppm_alloccolorhash(); ncolors = 0;
633     for( row = 0; row < rows; row++ ) {
634         for( col = 0; col < cols; col++ ) {
635             if( ppm_lookupcolor(cht, &pixels[row][col]) < 0 ) {
636                 if( ncolors >= maxcolors ) {
637                     ppm_freerow(pixrow);
638                     pixrow = (pixel *)0;
639                     ncolors = -1;
640                     goto fail;
641                 }
642                 if( ppm_addtocolorhash(cht, &pixels[row][col], ncolors) < 0 )
643                     pm_error("out of memory adding to hash table");
644                 pixrow[ncolors] = pixels[row][col];
645                 ++ncolors;
646             }
647         }
648     }
649 fail:
650     ppm_freecolorhash(cht);
651 
652     *ncolorsP = ncolors;
653     return pixrow;
654 }
655 
656 
657 pixel *
ppm_mapfiletocolorrow(file,maxcolors,ncolorsP,maxvalP)658 ppm_mapfiletocolorrow(file, maxcolors, ncolorsP, maxvalP)
659     FILE *file;
660     int maxcolors;
661     int *ncolorsP;
662     pixval *maxvalP;
663 {
664     int cols, rows, format, row, col, ncolors;
665     pixel *pixrow, *temprow;
666     colorhash_table cht;
667 
668     pixrow = ppm_allocrow(maxcolors);
669 
670     ppm_readppminit(file, &cols, &rows, maxvalP, &format);
671     temprow = ppm_allocrow(cols);
672     cht = ppm_alloccolorhash(); ncolors = 0;
673     for( row = 0; row < rows; row++ ) {
674         ppm_readppmrow(file, temprow, cols, *maxvalP, format);
675         for( col = 0; col < cols; col++ ) {
676             if( ppm_lookupcolor(cht, &temprow[col]) < 0 ) {
677                 if( ncolors >= maxcolors ) {
678                     ppm_freerow(pixrow);
679                     pixrow = (pixel *)0;
680                     ncolors = -1;
681                     goto fail;
682                 }
683                 if( ppm_addtocolorhash(cht, &temprow[col], ncolors) < 0 )
684                     pm_error("out of memory adding to hash table");
685                 pixrow[ncolors] = temprow[col];
686                 ++ncolors;
687             }
688         }
689     }
690 fail:
691     ppm_freecolorhash(cht);
692     ppm_freerow(temprow);
693 
694     *ncolorsP = ncolors;
695     return pixrow;
696 }
697 
698 
699 
700 static int (*customCmp)(pixel *, pixel *);
701 
702 #ifndef LITERAL_FN_DEF_MATCH
703 static qsort_comparison_fn customStub;
704 #endif
705 
706 static int
customStub(const void * const a,const void * const b)707 customStub(const void * const a,
708            const void * const b) {
709 
710     return (*customCmp)((pixel *)a, (pixel *)b);
711 }
712 
713 
714 
715 #ifndef LITERAL_FN_DEF_MATCH
716 static qsort_comparison_fn pixelCmp;
717 #endif
718 
719 static int
pixelCmp(const void * const a,const void * const b)720 pixelCmp(const void * const a,
721          const void * const b) {
722 
723     const pixel * const p1 = (const pixel *)a;
724     const pixel * const p2 = (const pixel *)b;
725 
726     int diff;
727 
728     diff = PPM_GETR(*p1) - PPM_GETR(*p2);
729     if( diff == 0 ) {
730         diff = PPM_GETG(*p1) - PPM_GETG(*p2);
731         if( diff == 0 )
732             diff = PPM_GETB(*p1) - PPM_GETB(*p2);
733     }
734     return diff;
735 }
736 
737 
738 
739 void
ppm_sortcolorrow(pixel * const colorrow,int const ncolors,int (* cmpfunc)(pixel *,pixel *))740 ppm_sortcolorrow(pixel * const colorrow,
741                  int     const ncolors,
742                  int (*cmpfunc)(pixel *, pixel *)) {
743 
744     if (cmpfunc) {
745         customCmp = cmpfunc;
746         qsort((void *)colorrow, ncolors, sizeof(pixel), customStub);
747     } else
748         qsort((void *)colorrow, ncolors, sizeof(pixel), pixelCmp);
749 }
750 
751 
752 
753 int
ppm_addtocolorrow(colorrow,ncolorsP,maxcolors,pixelP)754 ppm_addtocolorrow(colorrow, ncolorsP, maxcolors, pixelP)
755     pixel *colorrow;
756     int *ncolorsP;
757     int maxcolors;
758     pixel *pixelP;
759 {
760     int i;
761     pixval r, g, b;
762 
763     r = PPM_GETR(*pixelP);
764     g = PPM_GETG(*pixelP);
765     b = PPM_GETB(*pixelP);
766 
767     for( i = 0; i < *ncolorsP; i++ ) {
768         if( PPM_GETR(colorrow[i]) == r &&
769             PPM_GETG(colorrow[i]) == g &&
770             PPM_GETB(colorrow[i]) == b )
771                 return i;
772     }
773 
774     i = *ncolorsP;
775     if( i >= maxcolors )
776         return -1;
777     colorrow[i] = *pixelP;
778     ++*ncolorsP;
779     return i;
780 }
781 
782 
783 int
ppm_findclosestcolor(const pixel * const colormap,int const ncolors,const pixel * const pP)784 ppm_findclosestcolor(const pixel * const colormap,
785                      int           const ncolors,
786                      const pixel * const pP) {
787 
788     /* Search colormap for closest match.       */
789 
790     int i;
791     int ind;
792     unsigned int bestDist;
793 
794     bestDist = UINT_MAX;
795     ind = -1;
796 
797     for(i = 0; i < ncolors && bestDist > 0; ++i) {
798         unsigned int const dist = PPM_DISTANCE(*pP, colormap[i]);
799 
800         if (dist < bestDist ) {
801             ind = i;
802             bestDist = dist;
803         }
804     }
805     return ind;
806 }
807 
808 
809 void
ppm_colorrowtomapfile(FILE * ofp,pixel * colormap,int ncolors,pixval maxval)810 ppm_colorrowtomapfile(FILE *ofp, pixel *colormap, int ncolors, pixval maxval)
811 {
812     int i;
813 
814     ppm_writeppminit(ofp, ncolors, 1, maxval, 1);
815     for( i = 0; i < ncolors; i++ )
816         ppm_writeppmrow(ofp, &colormap[i], 1, maxval, 1);
817 }
818 
819 
820 
821