1 /*====================================================================*
2  -  Copyright (C) 2001 Leptonica.  All rights reserved.
3  -
4  -  Redistribution and use in source and binary forms, with or without
5  -  modification, are permitted provided that the following conditions
6  -  are met:
7  -  1. Redistributions of source code must retain the above copyright
8  -     notice, this list of conditions and the following disclaimer.
9  -  2. Redistributions in binary form must reproduce the above
10  -     copyright notice, this list of conditions and the following
11  -     disclaimer in the documentation and/or other materials
12  -     provided with the distribution.
13  -
14  -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18  -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
27 /*!
28  * \file colormap.c
29  * <pre>
30  *
31  *      Colormap creation, copy, destruction, addition
32  *           PIXCMAP    *pixcmapCreate()
33  *           PIXCMAP    *pixcmapCreateRandom()
34  *           PIXCMAP    *pixcmapCreateLinear()
35  *           PIXCMAP    *pixcmapCopy()
36  *           void        pixcmapDestroy()
37  *           l_int32     pixcmapAddColor()
38  *           l_int32     pixcmapAddRGBA()
39  *           l_int32     pixcmapAddNewColor()
40  *           l_int32     pixcmapAddNearestColor()
41  *           l_int32     pixcmapUsableColor()
42  *           l_int32     pixcmapAddBlackOrWhite()
43  *           l_int32     pixcmapSetBlackAndWhite()
44  *           l_int32     pixcmapGetCount()
45  *           l_int32     pixcmapGetDepth()
46  *           l_int32     pixcmapGetMinDepth()
47  *           l_int32     pixcmapGetFreeCount()
48  *           l_int32     pixcmapClear()
49  *
50  *      Colormap random access and test
51  *           l_int32     pixcmapGetColor()
52  *           l_int32     pixcmapGetColor32()
53  *           l_int32     pixcmapGetRGBA()
54  *           l_int32     pixcmapGetRGBA32()
55  *           l_int32     pixcmapResetColor()
56  *           l_int32     pixcmapSetAlpha()
57  *           l_int32     pixcmapGetIndex()
58  *           l_int32     pixcmapHasColor()
59  *           l_int32     pixcmapIsOpaque()
60  *           l_int32     pixcmapIsBlackAndWhite()
61  *           l_int32     pixcmapCountGrayColors()
62  *           l_int32     pixcmapGetRankIntensity()
63  *           l_int32     pixcmapGetNearestIndex()
64  *           l_int32     pixcmapGetNearestGrayIndex()
65  *           l_int32     pixcmapGetDistanceToColor()
66  *           l_int32     pixcmapGetRangeValues()
67  *
68  *      Colormap conversion
69  *           PIXCMAP    *pixcmapGrayToColor()
70  *           PIXCMAP    *pixcmapColorToGray()
71  *           PIXCMAP    *pixcmapConvertTo4()
72  *           PIXCMAP    *pixcmapConvertTo8()
73  *
74  *      Colormap I/O
75  *           l_int32     pixcmapRead()
76  *           l_int32     pixcmapReadStream()
77  *           l_int32     pixcmapReadMem()
78  *           l_int32     pixcmapWrite()
79  *           l_int32     pixcmapWriteStream()
80  *           l_int32     pixcmapWriteMem()
81  *
82  *      Extract colormap arrays and serialization
83  *           l_int32     pixcmapToArrays()
84  *           l_int32     pixcmapToRGBTable()
85  *           l_int32     pixcmapSerializeToMemory()
86  *           PIXCMAP    *pixcmapDeserializeFromMemory()
87  *           char       *pixcmapConvertToHex()
88  *
89  *      Colormap transforms
90  *           l_int32     pixcmapGammaTRC()
91  *           l_int32     pixcmapContrastTRC()
92  *           l_int32     pixcmapShiftIntensity()
93  *           l_int32     pixcmapShiftByComponent()
94  * </pre>
95  */
96 
97 #include <string.h>
98 #include "allheaders.h"
99 
100 /*-------------------------------------------------------------*
101  *                Colormap creation and addition               *
102  *-------------------------------------------------------------*/
103 /*!
104  * \brief   pixcmapCreate()
105  *
106  * \param[in]    depth bpp, of pix
107  * \return  cmap, or NULL on error
108  */
109 PIXCMAP *
pixcmapCreate(l_int32 depth)110 pixcmapCreate(l_int32  depth)
111 {
112 RGBA_QUAD  *cta;
113 PIXCMAP    *cmap;
114 
115     PROCNAME("pixcmapCreate");
116 
117     if (depth != 1 && depth != 2 && depth !=4 && depth != 8)
118         return (PIXCMAP *)ERROR_PTR("depth not in {1,2,4,8}", procName, NULL);
119 
120     cmap = (PIXCMAP *)LEPT_CALLOC(1, sizeof(PIXCMAP));
121     cmap->depth = depth;
122     cmap->nalloc = 1 << depth;
123     cta = (RGBA_QUAD *)LEPT_CALLOC(cmap->nalloc, sizeof(RGBA_QUAD));
124     cmap->array = cta;
125     cmap->n = 0;
126     return cmap;
127 }
128 
129 
130 /*!
131  * \brief   pixcmapCreateRandom()
132  *
133  * \param[in]    depth bpp, of pix; 2, 4 or 8
134  * \param[in]    hasblack 1 if the first color is black; 0 if no black
135  * \param[in]    haswhite 1 if the last color is white; 0 if no white
136  * \return  cmap, or NULL on error
137  *
138  * <pre>
139  * Notes:
140  *      (1) This sets up a colormap with random colors,
141  *          where the first color is optionally black, the last color
142  *          is optionally white, and the remaining colors are
143  *          chosen randomly.
144  *      (2) The number of randomly chosen colors is:
145  *               2^(depth) - haswhite - hasblack
146  *      (3) Because rand() is seeded, it might disrupt otherwise
147  *          deterministic results if also used elsewhere in a program.
148  *      (4) rand() is not threadsafe, and will generate garbage if run
149  *          on multiple threads at once -- though garbage is generally
150  *          what you want from a random number generator!
151  *      (5) Modern rand()s have equal randomness in low and high order
152  *          bits, but older ones don't.  Here, we're just using rand()
153  *          to choose colors for output.
154  * </pre>
155  */
156 PIXCMAP *
pixcmapCreateRandom(l_int32 depth,l_int32 hasblack,l_int32 haswhite)157 pixcmapCreateRandom(l_int32  depth,
158                     l_int32  hasblack,
159                     l_int32  haswhite)
160 {
161 l_int32   ncolors, i;
162 l_int32   red[256], green[256], blue[256];
163 PIXCMAP  *cmap;
164 
165     PROCNAME("pixcmapCreateRandom");
166 
167     if (depth != 2 && depth != 4 && depth != 8)
168         return (PIXCMAP *)ERROR_PTR("depth not in {2, 4, 8}", procName, NULL);
169     if (hasblack != 0) hasblack = 1;
170     if (haswhite != 0) haswhite = 1;
171 
172     cmap = pixcmapCreate(depth);
173     ncolors = 1 << depth;
174     if (hasblack)  /* first color is optionally black */
175         pixcmapAddColor(cmap, 0, 0, 0);
176     for (i = hasblack; i < ncolors - haswhite; i++) {
177         red[i] = (l_uint32)rand() & 0xff;
178         green[i] = (l_uint32)rand() & 0xff;
179         blue[i] = (l_uint32)rand() & 0xff;
180         pixcmapAddColor(cmap, red[i], green[i], blue[i]);
181     }
182     if (haswhite)  /* last color is optionally white */
183         pixcmapAddColor(cmap, 255, 255, 255);
184 
185     return cmap;
186 }
187 
188 
189 /*!
190  * \brief   pixcmapCreateLinear()
191  *
192  * \param[in]    d depth of pix for this colormap; 1, 2, 4 or 8
193  * \param[in]    nlevels valid in range [2, 2^d]
194  * \return  cmap, or NULL on error
195  *
196  * <pre>
197  * Notes:
198  *      (1) Colormap has equally spaced gray color values
199  *          from black (0, 0, 0) to white (255, 255, 255).
200  * </pre>
201  */
202 PIXCMAP *
pixcmapCreateLinear(l_int32 d,l_int32 nlevels)203 pixcmapCreateLinear(l_int32  d,
204                     l_int32  nlevels)
205 {
206 l_int32   maxlevels, i, val;
207 PIXCMAP  *cmap;
208 
209     PROCNAME("pixcmapCreateLinear");
210 
211     if (d != 1 && d != 2 && d !=4 && d != 8)
212         return (PIXCMAP *)ERROR_PTR("d not in {1, 2, 4, 8}", procName, NULL);
213     maxlevels = 1 << d;
214     if (nlevels < 2 || nlevels > maxlevels)
215         return (PIXCMAP *)ERROR_PTR("invalid nlevels", procName, NULL);
216 
217     cmap = pixcmapCreate(d);
218     for (i = 0; i < nlevels; i++) {
219         val = (255 * i) / (nlevels - 1);
220         pixcmapAddColor(cmap, val, val, val);
221     }
222     return cmap;
223 }
224 
225 
226 /*!
227  * \brief   pixcmapCopy()
228  *
229  * \param[in]    cmaps
230  * \return  cmapd, or NULL on error
231  */
232 PIXCMAP *
pixcmapCopy(PIXCMAP * cmaps)233 pixcmapCopy(PIXCMAP  *cmaps)
234 {
235 l_int32   nbytes;
236 PIXCMAP  *cmapd;
237 
238     PROCNAME("pixcmapCopy");
239 
240     if (!cmaps)
241         return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL);
242     if (cmaps->nalloc > 256)
243         return (PIXCMAP *)ERROR_PTR("nalloc > 256", procName, NULL);
244 
245     cmapd = (PIXCMAP *)LEPT_CALLOC(1, sizeof(PIXCMAP));
246     nbytes = cmaps->nalloc * sizeof(RGBA_QUAD);
247     cmapd->array = (void *)LEPT_CALLOC(1, nbytes);
248     memcpy(cmapd->array, cmaps->array, nbytes);
249     cmapd->n = cmaps->n;
250     cmapd->nalloc = cmaps->nalloc;
251     cmapd->depth = cmaps->depth;
252     return cmapd;
253 }
254 
255 
256 /*!
257  * \brief   pixcmapDestroy()
258  *
259  * \param[in,out]   pcmap set to null
260  * \return  void
261  */
262 void
pixcmapDestroy(PIXCMAP ** pcmap)263 pixcmapDestroy(PIXCMAP  **pcmap)
264 {
265 PIXCMAP  *cmap;
266 
267     PROCNAME("pixcmapDestroy");
268 
269     if (pcmap == NULL) {
270         L_WARNING("ptr address is null!\n", procName);
271         return;
272     }
273 
274     if ((cmap = *pcmap) == NULL)
275         return;
276 
277     LEPT_FREE(cmap->array);
278     LEPT_FREE(cmap);
279     *pcmap = NULL;
280     return;
281 }
282 
283 
284 /*!
285  * \brief   pixcmapAddColor()
286  *
287  * \param[in]    cmap
288  * \param[in]    rval, gval, bval colormap entry to be added; each number
289  *                                is in range [0, ... 255]
290  * \return  0 if OK, 1 on error
291  *
292  * <pre>
293  * Notes:
294  *      (1) This always adds the color if there is room.
295  *      (2) The alpha component is 255 (opaque)
296  * </pre>
297  */
298 l_int32
pixcmapAddColor(PIXCMAP * cmap,l_int32 rval,l_int32 gval,l_int32 bval)299 pixcmapAddColor(PIXCMAP  *cmap,
300                 l_int32   rval,
301                 l_int32   gval,
302                 l_int32   bval)
303 {
304 RGBA_QUAD  *cta;
305 
306     PROCNAME("pixcmapAddColor");
307 
308     if (!cmap)
309         return ERROR_INT("cmap not defined", procName, 1);
310     if (cmap->n >= cmap->nalloc)
311         return ERROR_INT("no free color entries", procName, 1);
312 
313     cta = (RGBA_QUAD *)cmap->array;
314     cta[cmap->n].red = rval;
315     cta[cmap->n].green = gval;
316     cta[cmap->n].blue = bval;
317     cta[cmap->n].alpha = 255;
318     cmap->n++;
319     return 0;
320 }
321 
322 
323 /*!
324  * \brief   pixcmapAddRGBA()
325  *
326  * \param[in]    cmap
327  * \param[in]    rval, gval, bval, aval colormap entry to be added;
328  *                                      each number is in range [0, ... 255]
329  * \return  0 if OK, 1 on error
330  *
331  * <pre>
332  * Notes:
333  *      (1) This always adds the color if there is room.
334  * </pre>
335  */
336 l_int32
pixcmapAddRGBA(PIXCMAP * cmap,l_int32 rval,l_int32 gval,l_int32 bval,l_int32 aval)337 pixcmapAddRGBA(PIXCMAP  *cmap,
338                l_int32   rval,
339                l_int32   gval,
340                l_int32   bval,
341                l_int32   aval)
342 {
343 RGBA_QUAD  *cta;
344 
345     PROCNAME("pixcmapAddRGBA");
346 
347     if (!cmap)
348         return ERROR_INT("cmap not defined", procName, 1);
349     if (cmap->n >= cmap->nalloc)
350         return ERROR_INT("no free color entries", procName, 1);
351 
352     cta = (RGBA_QUAD *)cmap->array;
353     cta[cmap->n].red = rval;
354     cta[cmap->n].green = gval;
355     cta[cmap->n].blue = bval;
356     cta[cmap->n].alpha = aval;
357     cmap->n++;
358     return 0;
359 }
360 
361 
362 /*!
363  * \brief   pixcmapAddNewColor()
364  *
365  * \param[in]    cmap
366  * \param[in]    rval, gval, bval colormap entry to be added; each number
367  *                                is in range [0, ... 255]
368  * \param[out]   pindex index of color
369  * \return  0 if OK, 1 on error; 2 if unable to add color
370  *
371  * <pre>
372  * Notes:
373  *      (1) This only adds color if not already there.
374  *      (2) The alpha component is 255 (opaque)
375  *      (3) This returns the index of the new (or existing) color.
376  *      (4) Returns 2 with a warning if unable to add this color;
377  *          the caller should check the return value.
378  * </pre>
379  */
380 l_int32
pixcmapAddNewColor(PIXCMAP * cmap,l_int32 rval,l_int32 gval,l_int32 bval,l_int32 * pindex)381 pixcmapAddNewColor(PIXCMAP  *cmap,
382                    l_int32   rval,
383                    l_int32   gval,
384                    l_int32   bval,
385                    l_int32  *pindex)
386 {
387     PROCNAME("pixcmapAddNewColor");
388 
389     if (!pindex)
390         return ERROR_INT("&index not defined", procName, 1);
391     *pindex = 0;
392     if (!cmap)
393         return ERROR_INT("cmap not defined", procName, 1);
394 
395         /* Check if the color is already present. */
396     if (!pixcmapGetIndex(cmap, rval, gval, bval, pindex))  /* found */
397         return 0;
398 
399         /* We need to add the color.  Is there room? */
400     if (cmap->n >= cmap->nalloc) {
401         L_WARNING("no free color entries\n", procName);
402         return 2;
403     }
404 
405         /* There's room.  Add it. */
406     pixcmapAddColor(cmap, rval, gval, bval);
407     *pindex = pixcmapGetCount(cmap) - 1;
408     return 0;
409 }
410 
411 
412 /*!
413  * \brief   pixcmapAddNearestColor()
414  *
415  * \param[in]    cmap
416  * \param[in]    rval, gval, bval colormap entry to be added; each number
417  *                                is in range [0, ... 255]
418  * \param[out]   pindex index of color
419  * \return  0 if OK, 1 on error
420  *
421  * <pre>
422  * Notes:
423  *      (1) This only adds color if not already there.
424  *      (2) The alpha component is 255 (opaque)
425  *      (3) If it's not in the colormap and there is no room to add
426  *          another color, this returns the index of the nearest color.
427  * </pre>
428  */
429 l_int32
pixcmapAddNearestColor(PIXCMAP * cmap,l_int32 rval,l_int32 gval,l_int32 bval,l_int32 * pindex)430 pixcmapAddNearestColor(PIXCMAP  *cmap,
431                        l_int32   rval,
432                        l_int32   gval,
433                        l_int32   bval,
434                        l_int32  *pindex)
435 {
436     PROCNAME("pixcmapAddNearestColor");
437 
438     if (!pindex)
439         return ERROR_INT("&index not defined", procName, 1);
440     *pindex = 0;
441     if (!cmap)
442         return ERROR_INT("cmap not defined", procName, 1);
443 
444         /* Check if the color is already present. */
445     if (!pixcmapGetIndex(cmap, rval, gval, bval, pindex))  /* found */
446         return 0;
447 
448         /* We need to add the color.  Is there room? */
449     if (cmap->n < cmap->nalloc) {
450         pixcmapAddColor(cmap, rval, gval, bval);
451         *pindex = pixcmapGetCount(cmap) - 1;
452         return 0;
453     }
454 
455         /* There's no room.  Return the index of the nearest color */
456     pixcmapGetNearestIndex(cmap, rval, gval, bval, pindex);
457     return 0;
458 }
459 
460 
461 /*!
462  * \brief   pixcmapUsableColor()
463  *
464  * \param[in]    cmap
465  * \param[in]    rval, gval, bval colormap entry to be added; each number
466  *                                is in range [0, ... 255]
467  * \param[out]   pusable 1 if usable; 0 if not
468  * \return  0 if OK, 1 on error
469  *
470  * <pre>
471  * Notes:
472  *      (1) This checks if the color already exists or if there is
473  *          room to add it.  It makes no change in the colormap.
474  * </pre>
475  */
476 l_int32
pixcmapUsableColor(PIXCMAP * cmap,l_int32 rval,l_int32 gval,l_int32 bval,l_int32 * pusable)477 pixcmapUsableColor(PIXCMAP  *cmap,
478                    l_int32   rval,
479                    l_int32   gval,
480                    l_int32   bval,
481                    l_int32  *pusable)
482 {
483 l_int32  index;
484 
485     PROCNAME("pixcmapUsableColor");
486 
487     if (!pusable)
488         return ERROR_INT("&usable not defined", procName, 1);
489     *pusable = 0;
490     if (!cmap)
491         return ERROR_INT("cmap not defined", procName, 1);
492 
493         /* Is there room to add it? */
494     if (cmap->n < cmap->nalloc) {
495         *pusable = 1;
496         return 0;
497     }
498 
499         /* No room; check if the color is already present. */
500     if (!pixcmapGetIndex(cmap, rval, gval, bval, &index))   /* found */
501         *pusable = 1;
502     return 0;
503 }
504 
505 
506 /*!
507  * \brief   pixcmapAddBlackOrWhite()
508  *
509  * \param[in]    cmap
510  * \param[in]    color 0 for black, 1 for white
511  * \param[out]   pindex [optional] index of color; can be null
512  * \return  0 if OK, 1 on error
513  *
514  * <pre>
515  * Notes:
516  *      (1) This only adds color if not already there.
517  *      (2) The alpha component is 255 (opaque)
518  *      (3) This sets index to the requested color.
519  *      (4) If there is no room in the colormap, returns the index
520  *          of the closest color.
521  * </pre>
522  */
523 l_int32
pixcmapAddBlackOrWhite(PIXCMAP * cmap,l_int32 color,l_int32 * pindex)524 pixcmapAddBlackOrWhite(PIXCMAP  *cmap,
525                        l_int32   color,
526                        l_int32  *pindex)
527 {
528 l_int32  index;
529 
530     PROCNAME("pixcmapAddBlackOrWhite");
531 
532     if (pindex) *pindex = 0;
533     if (!cmap)
534         return ERROR_INT("cmap not defined", procName, 1);
535 
536     if (color == 0) {  /* black */
537         if (pixcmapGetFreeCount(cmap) > 0)
538             pixcmapAddNewColor(cmap, 0, 0, 0, &index);
539         else
540             pixcmapGetRankIntensity(cmap, 0.0, &index);
541     } else {  /* white */
542         if (pixcmapGetFreeCount(cmap) > 0)
543             pixcmapAddNewColor(cmap, 255, 255, 255, &index);
544         else
545             pixcmapGetRankIntensity(cmap, 1.0, &index);
546     }
547 
548     if (pindex)
549         *pindex = index;
550     return 0;
551 }
552 
553 
554 /*!
555  * \brief   pixcmapSetBlackAndWhite()
556  *
557  * \param[in]    cmap
558  * \param[in]    setblack 0 for no operation; 1 to set darkest color to black
559  * \param[in]    setwhite 0 for no operation; 1 to set lightest color to white
560  * \return  0 if OK, 1 on error
561  */
562 l_int32
pixcmapSetBlackAndWhite(PIXCMAP * cmap,l_int32 setblack,l_int32 setwhite)563 pixcmapSetBlackAndWhite(PIXCMAP  *cmap,
564                         l_int32   setblack,
565                         l_int32   setwhite)
566 {
567 l_int32  index;
568 
569     PROCNAME("pixcmapSetBlackAndWhite");
570 
571     if (!cmap)
572         return ERROR_INT("cmap not defined", procName, 1);
573 
574     if (setblack) {
575         pixcmapGetRankIntensity(cmap, 0.0, &index);
576         pixcmapResetColor(cmap, index, 0, 0, 0);
577     }
578     if (setwhite) {
579         pixcmapGetRankIntensity(cmap, 1.0, &index);
580         pixcmapResetColor(cmap, index, 255, 255, 255);
581     }
582     return 0;
583 }
584 
585 
586 /*!
587  * \brief   pixcmapGetCount()
588  *
589  * \param[in]    cmap
590  * \return  count, or 0 on error
591  */
592 l_int32
pixcmapGetCount(PIXCMAP * cmap)593 pixcmapGetCount(PIXCMAP  *cmap)
594 {
595     PROCNAME("pixcmapGetCount");
596 
597     if (!cmap)
598         return ERROR_INT("cmap not defined", procName, 0);
599     return cmap->n;
600 }
601 
602 
603 /*!
604  * \brief   pixcmapGetFreeCount()
605  *
606  * \param[in]    cmap
607  * \return  free entries, or 0 on error
608  */
609 l_int32
pixcmapGetFreeCount(PIXCMAP * cmap)610 pixcmapGetFreeCount(PIXCMAP  *cmap)
611 {
612     PROCNAME("pixcmapGetFreeCount");
613 
614     if (!cmap)
615         return ERROR_INT("cmap not defined", procName, 0);
616     return (cmap->nalloc - cmap->n);
617 }
618 
619 
620 /*!
621  * \brief   pixcmapGetDepth()
622  *
623  * \param[in]    cmap
624  * \return  depth, or 0 on error
625  */
626 l_int32
pixcmapGetDepth(PIXCMAP * cmap)627 pixcmapGetDepth(PIXCMAP  *cmap)
628 {
629     PROCNAME("pixcmapGetDepth");
630 
631     if (!cmap)
632         return ERROR_INT("cmap not defined", procName, 0);
633     return cmap->depth;
634 }
635 
636 
637 /*!
638  * \brief   pixcmapGetMinDepth()
639  *
640  * \param[in]    cmap
641  * \param[out]   pmindepth minimum depth to support the colormap
642  * \return  0 if OK, 1 on error
643  *
644  * <pre>
645  * Notes:
646  *      (1) On error, &mindepth is returned as 0.
647  * </pre>
648  */
649 l_int32
pixcmapGetMinDepth(PIXCMAP * cmap,l_int32 * pmindepth)650 pixcmapGetMinDepth(PIXCMAP  *cmap,
651                    l_int32  *pmindepth)
652 {
653 l_int32  ncolors;
654 
655     PROCNAME("pixcmapGetMinDepth");
656 
657     if (!pmindepth)
658         return ERROR_INT("&mindepth not defined", procName, 1);
659     *pmindepth = 0;
660     if (!cmap)
661         return ERROR_INT("cmap not defined", procName, 1);
662 
663     ncolors = pixcmapGetCount(cmap);
664     if (ncolors <= 4)
665         *pmindepth = 2;
666     else if (ncolors <= 16)
667         *pmindepth = 4;
668     else  /* ncolors > 16 */
669         *pmindepth = 8;
670     return 0;
671 }
672 
673 
674 /*!
675  * \brief   pixcmapClear()
676  *
677  * \param[in]    cmap
678  * \return  0 if OK, 1 on error
679  *
680  * <pre>
681  * Notes:
682  *      (1) This removes the colors by setting the count to 0.
683  * </pre>
684  */
685 l_int32
pixcmapClear(PIXCMAP * cmap)686 pixcmapClear(PIXCMAP  *cmap)
687 {
688     PROCNAME("pixcmapClear");
689 
690     if (!cmap)
691         return ERROR_INT("cmap not defined", procName, 1);
692     cmap->n = 0;
693     return 0;
694 }
695 
696 
697 /*-------------------------------------------------------------*
698  *                      Colormap random access                 *
699  *-------------------------------------------------------------*/
700 /*!
701  * \brief   pixcmapGetColor()
702  *
703  * \param[in]    cmap
704  * \param[in]    index
705  * \param[out]   prval, pgval, pbval each color value
706  * \return  0 if OK, 1 if not accessible caller should check
707  */
708 l_int32
pixcmapGetColor(PIXCMAP * cmap,l_int32 index,l_int32 * prval,l_int32 * pgval,l_int32 * pbval)709 pixcmapGetColor(PIXCMAP  *cmap,
710                 l_int32   index,
711                 l_int32  *prval,
712                 l_int32  *pgval,
713                 l_int32  *pbval)
714 {
715 RGBA_QUAD  *cta;
716 
717     PROCNAME("pixcmapGetColor");
718 
719     if (!prval || !pgval || !pbval)
720         return ERROR_INT("&rval, &gval, &bval not all defined", procName, 1);
721     *prval = *pgval = *pbval = 0;
722     if (!cmap)
723         return ERROR_INT("cmap not defined", procName, 1);
724     if (index < 0 || index >= cmap->n)
725         return ERROR_INT("index out of bounds", procName, 1);
726 
727     cta = (RGBA_QUAD *)cmap->array;
728     *prval = cta[index].red;
729     *pgval = cta[index].green;
730     *pbval = cta[index].blue;
731     return 0;
732 }
733 
734 
735 /*!
736  * \brief   pixcmapGetColor32()
737  *
738  * \param[in]    cmap
739  * \param[in]    index
740  * \param[out]   pval32 32-bit rgb color value
741  * \return  0 if OK, 1 if not accessible caller should check
742  *
743  * <pre>
744  * Notes:
745  *      (1) The returned alpha channel value is 255.
746  * </pre>
747  */
748 l_int32
pixcmapGetColor32(PIXCMAP * cmap,l_int32 index,l_uint32 * pval32)749 pixcmapGetColor32(PIXCMAP   *cmap,
750                   l_int32    index,
751                   l_uint32  *pval32)
752 {
753 l_int32  rval, gval, bval;
754 
755     PROCNAME("pixcmapGetColor32");
756 
757     if (!pval32)
758         return ERROR_INT("&val32 not defined", procName, 1);
759     *pval32 = 0;
760 
761     if (pixcmapGetColor(cmap, index, &rval, &gval, &bval) != 0)
762         return ERROR_INT("rgb values not found", procName, 1);
763     composeRGBAPixel(rval, gval, bval, 255, pval32);
764     return 0;
765 }
766 
767 
768 /*!
769  * \brief   pixcmapGetRGBA()
770  *
771  * \param[in]    cmap
772  * \param[in]    index
773  * \param[out]   prval, pgval, pbval, paval each color value
774  * \return  0 if OK, 1 if not accessible caller should check
775  */
776 l_int32
pixcmapGetRGBA(PIXCMAP * cmap,l_int32 index,l_int32 * prval,l_int32 * pgval,l_int32 * pbval,l_int32 * paval)777 pixcmapGetRGBA(PIXCMAP  *cmap,
778                l_int32   index,
779                l_int32  *prval,
780                l_int32  *pgval,
781                l_int32  *pbval,
782                l_int32  *paval)
783 {
784 RGBA_QUAD  *cta;
785 
786     PROCNAME("pixcmapGetRGBA");
787 
788     if (!prval || !pgval || !pbval || !paval)
789         return ERROR_INT("&rval, &gval, &bval, &aval not all defined",
790                 procName, 1);
791     *prval = *pgval = *pbval = *paval = 0;
792     if (!cmap)
793         return ERROR_INT("cmap not defined", procName, 1);
794     if (index < 0 || index >= cmap->n)
795         return ERROR_INT("index out of bounds", procName, 1);
796 
797     cta = (RGBA_QUAD *)cmap->array;
798     *prval = cta[index].red;
799     *pgval = cta[index].green;
800     *pbval = cta[index].blue;
801     *paval = cta[index].alpha;
802     return 0;
803 }
804 
805 
806 /*!
807  * \brief   pixcmapGetRGBA32()
808  *
809  * \param[in]    cmap
810  * \param[in]    index
811  * \param[out]   pval32 32-bit rgba color value
812  * \return  0 if OK, 1 if not accessible caller should check
813  */
814 l_int32
pixcmapGetRGBA32(PIXCMAP * cmap,l_int32 index,l_uint32 * pval32)815 pixcmapGetRGBA32(PIXCMAP   *cmap,
816                  l_int32    index,
817                  l_uint32  *pval32)
818 {
819 l_int32  rval, gval, bval, aval;
820 
821     PROCNAME("pixcmapGetRGBA32");
822 
823     if (!pval32)
824         return ERROR_INT("&val32 not defined", procName, 1);
825     *pval32 = 0;
826 
827     if (pixcmapGetRGBA(cmap, index, &rval, &gval, &bval, &aval) != 0)
828         return ERROR_INT("rgba values not found", procName, 1);
829     composeRGBAPixel(rval, gval, bval, aval, pval32);
830     return 0;
831 }
832 
833 
834 /*!
835  * \brief   pixcmapResetColor()
836  *
837  * \param[in]    cmap
838  * \param[in]    index
839  * \param[in]    rval, gval, bval colormap entry to be reset; each number
840  *                                is in range [0, ... 255]
841  * \return  0 if OK, 1 if not accessible caller should check
842  *
843  * <pre>
844  * Notes:
845  *      (1) This resets sets the color of an entry that has already
846  *          been set and included in the count of colors.
847  *      (2) The alpha component is 255 (opaque)
848  * </pre>
849  */
850 l_int32
pixcmapResetColor(PIXCMAP * cmap,l_int32 index,l_int32 rval,l_int32 gval,l_int32 bval)851 pixcmapResetColor(PIXCMAP  *cmap,
852                   l_int32   index,
853                   l_int32   rval,
854                   l_int32   gval,
855                   l_int32   bval)
856 {
857 RGBA_QUAD  *cta;
858 
859     PROCNAME("pixcmapResetColor");
860 
861     if (!cmap)
862         return ERROR_INT("cmap not defined", procName, 1);
863     if (index < 0 || index >= cmap->n)
864         return ERROR_INT("index out of bounds", procName, 1);
865 
866     cta = (RGBA_QUAD *)cmap->array;
867     cta[index].red = rval;
868     cta[index].green = gval;
869     cta[index].blue = bval;
870     cta[index].alpha = 255;
871     return 0;
872 }
873 
874 
875 /*!
876  * \brief   pixcmapSetAlpha()
877  *
878  * \param[in]    cmap
879  * \param[in]    index
880  * \param[in]    aval in range [0, ... 255]
881  * \return  0 if OK, 1 on error
882  *
883  * <pre>
884  * Notes:
885  *      (1) This modifies the transparency of one entry in a colormap.
886  *          The alpha component by default is 255 (opaque).
887  *          This is used when extracting the colormap from a PNG file
888  *          without decoding the image.
889  * </pre>
890  */
891 l_int32
pixcmapSetAlpha(PIXCMAP * cmap,l_int32 index,l_int32 aval)892 pixcmapSetAlpha(PIXCMAP  *cmap,
893                 l_int32   index,
894                 l_int32   aval)
895 {
896 RGBA_QUAD  *cta;
897 
898     PROCNAME("pixcmapSetAlpha");
899 
900     if (!cmap)
901         return ERROR_INT("cmap not defined", procName, 1);
902     if (index < 0 || index >= cmap->n)
903         return ERROR_INT("index out of bounds", procName, 1);
904 
905     cta = (RGBA_QUAD *)cmap->array;
906     cta[index].alpha = aval;
907     return 0;
908 }
909 
910 
911 /*!
912  * \brief   pixcmapGetIndex()
913  *
914  * \param[in]    cmap
915  * \param[in]    rval, gval, bval colormap colors to search for; each number
916  *                                is in range [0, ... 255]
917  * \param[out]   pindex found index
918  * \return  0 if found, 1 if not found caller must check
919  */
920 l_int32
pixcmapGetIndex(PIXCMAP * cmap,l_int32 rval,l_int32 gval,l_int32 bval,l_int32 * pindex)921 pixcmapGetIndex(PIXCMAP  *cmap,
922                 l_int32   rval,
923                 l_int32   gval,
924                 l_int32   bval,
925                 l_int32  *pindex)
926 {
927 l_int32     n, i;
928 RGBA_QUAD  *cta;
929 
930     PROCNAME("pixcmapGetIndex");
931 
932     if (!pindex)
933         return ERROR_INT("&index not defined", procName, 1);
934     *pindex = 0;
935     if (!cmap)
936         return ERROR_INT("cmap not defined", procName, 1);
937     n = pixcmapGetCount(cmap);
938 
939     cta = (RGBA_QUAD *)cmap->array;
940     for (i = 0; i < n; i++) {
941         if (rval == cta[i].red &&
942             gval == cta[i].green &&
943             bval == cta[i].blue) {
944             *pindex = i;
945             return 0;
946         }
947     }
948     return 1;
949 }
950 
951 
952 /*!
953  * \brief   pixcmapHasColor()
954  *
955  * \param[in]    cmap
956  * \param[out]   pcolor TRUE if cmap has color; FALSE otherwise
957  * \return  0 if OK, 1 on error
958  */
959 l_int32
pixcmapHasColor(PIXCMAP * cmap,l_int32 * pcolor)960 pixcmapHasColor(PIXCMAP  *cmap,
961                 l_int32  *pcolor)
962 {
963 l_int32   n, i;
964 l_int32  *rmap, *gmap, *bmap;
965 
966     PROCNAME("pixcmapHasColor");
967 
968     if (!pcolor)
969         return ERROR_INT("&color not defined", procName, 1);
970     *pcolor = FALSE;
971     if (!cmap)
972         return ERROR_INT("cmap not defined", procName, 1);
973 
974     if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, NULL))
975         return ERROR_INT("colormap arrays not made", procName, 1);
976     n = pixcmapGetCount(cmap);
977     for (i = 0; i < n; i++) {
978         if ((rmap[i] != gmap[i]) || (rmap[i] != bmap[i])) {
979             *pcolor = TRUE;
980             break;
981         }
982     }
983 
984     LEPT_FREE(rmap);
985     LEPT_FREE(gmap);
986     LEPT_FREE(bmap);
987     return 0;
988 }
989 
990 
991 /*!
992  * \brief   pixcmapIsOpaque()
993  *
994  * \param[in]    cmap
995  * \param[out]   popaque TRUE if fully opaque: all entries are 255
996  * \return  0 if OK, 1 on error
997  */
998 l_int32
pixcmapIsOpaque(PIXCMAP * cmap,l_int32 * popaque)999 pixcmapIsOpaque(PIXCMAP  *cmap,
1000                 l_int32  *popaque)
1001 {
1002 l_int32     i, n;
1003 RGBA_QUAD  *cta;
1004 
1005     PROCNAME("pixcmapIsOpaque");
1006 
1007     if (!popaque)
1008         return ERROR_INT("&opaque not defined", procName, 1);
1009     *popaque = TRUE;
1010     if (!cmap)
1011         return ERROR_INT("cmap not defined", procName, 1);
1012 
1013     n = pixcmapGetCount(cmap);
1014     cta = (RGBA_QUAD *)cmap->array;
1015     for (i = 0; i < n; i++) {
1016         if (cta[i].alpha != 255) {
1017             *popaque = FALSE;
1018             break;
1019         }
1020     }
1021     return 0;
1022 }
1023 
1024 
1025 /*!
1026  * \brief   pixcmapIsBlackAndWhite()
1027  *
1028  * \param[in]    cmap
1029  * \param[out]   pblackwhite TRUE if the cmap has only two colors:
1030  *                           black (0,0,0) and white (255,255,255)
1031  * \return  0 if OK, 1 on error
1032  */
1033 l_int32
pixcmapIsBlackAndWhite(PIXCMAP * cmap,l_int32 * pblackwhite)1034 pixcmapIsBlackAndWhite(PIXCMAP  *cmap,
1035                        l_int32  *pblackwhite)
1036 {
1037 l_int32     val0, val1, hascolor;
1038 RGBA_QUAD  *cta;
1039 
1040     PROCNAME("pixcmapIsBlackAndWhite");
1041 
1042     if (!pblackwhite)
1043         return ERROR_INT("&blackwhite not defined", procName, 1);
1044     *pblackwhite = FALSE;
1045     if (!cmap)
1046         return ERROR_INT("cmap not defined", procName, 1);
1047     if (pixcmapGetCount(cmap) != 2)
1048         return 0;
1049 
1050     pixcmapHasColor(cmap, &hascolor);
1051     if (hascolor) return 0;
1052 
1053     cta = (RGBA_QUAD *)cmap->array;
1054     val0 = cta[0].red;
1055     val1 = cta[1].red;
1056     if ((val0 == 0 && val1 == 255) || (val0 == 255 && val1 == 0))
1057         *pblackwhite = TRUE;
1058     return 0;
1059 }
1060 
1061 
1062 /*!
1063  * \brief   pixcmapCountGrayColors()
1064  *
1065  * \param[in]    cmap
1066  * \param[out]   pngray number of gray colors
1067  * \return  0 if OK, 1 on error
1068  *
1069  * <pre>
1070  * Notes:
1071  *      (1) This counts the unique gray colors, including black and white.
1072  * </pre>
1073  */
1074 l_int32
pixcmapCountGrayColors(PIXCMAP * cmap,l_int32 * pngray)1075 pixcmapCountGrayColors(PIXCMAP  *cmap,
1076                        l_int32  *pngray)
1077 {
1078 l_int32   n, i, rval, gval, bval, count;
1079 l_int32  *array;
1080 
1081     PROCNAME("pixcmapCountGrayColors");
1082 
1083     if (!pngray)
1084         return ERROR_INT("&ngray not defined", procName, 1);
1085     *pngray = 0;
1086     if (!cmap)
1087         return ERROR_INT("cmap not defined", procName, 1);
1088 
1089     array = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
1090     n = pixcmapGetCount(cmap);
1091     count = 0;
1092     for (i = 0; i < n; i++) {
1093         pixcmapGetColor(cmap, i, &rval, &gval, &bval);
1094         if ((rval == gval) && (rval == bval) && (array[rval] == 0)) {
1095             array[rval] = 1;
1096             count++;
1097         }
1098     }
1099 
1100     LEPT_FREE(array);
1101     *pngray = count;
1102     return 0;
1103 }
1104 
1105 
1106 /*!
1107  * \brief   pixcmapGetRankIntensity()
1108  *
1109  * \param[in]    cmap
1110  * \param[in]    rankval 0.0 for darkest, 1.0 for lightest color
1111  * \param[out]   pindex the index into the colormap that
1112  *                      corresponds to the rank intensity color
1113  * \return  0 if OK, 1 on error
1114  */
1115 l_int32
pixcmapGetRankIntensity(PIXCMAP * cmap,l_float32 rankval,l_int32 * pindex)1116 pixcmapGetRankIntensity(PIXCMAP    *cmap,
1117                         l_float32   rankval,
1118                         l_int32    *pindex)
1119 {
1120 l_int32  n, i, rval, gval, bval, rankindex;
1121 NUMA    *na, *nasort;
1122 
1123     PROCNAME("pixcmapGetRankIntensity");
1124 
1125     if (!pindex)
1126         return ERROR_INT("&index not defined", procName, 1);
1127     *pindex = 0;
1128     if (!cmap)
1129         return ERROR_INT("cmap not defined", procName, 1);
1130     if (rankval < 0.0 || rankval > 1.0)
1131         return ERROR_INT("rankval not in [0.0 ... 1.0]", procName, 1);
1132 
1133     n = pixcmapGetCount(cmap);
1134     na = numaCreate(n);
1135     for (i = 0; i < n; i++) {
1136         pixcmapGetColor(cmap, i, &rval, &gval, &bval);
1137         numaAddNumber(na, rval + gval + bval);
1138     }
1139     nasort = numaGetSortIndex(na, L_SORT_INCREASING);
1140     rankindex = (l_int32)(rankval * (n - 1) + 0.5);
1141     numaGetIValue(nasort, rankindex, pindex);
1142 
1143     numaDestroy(&na);
1144     numaDestroy(&nasort);
1145     return 0;
1146 }
1147 
1148 
1149 /*!
1150  * \brief   pixcmapGetNearestIndex()
1151  *
1152  * \param[in]    cmap
1153  * \param[in]    rval, gval, bval colormap colors to search for; each number
1154  *                                is in range [0, ... 255]
1155  * \param[out]   pindex the index of the nearest color
1156  * \return  0 if OK, 1 on error caller must check
1157  *
1158  * <pre>
1159  * Notes:
1160  *      (1) Returns the index of the exact color if possible, otherwise the
1161  *          index of the color closest to the target color.
1162  *      (2) Nearest color is that which is the least sum-of-squares distance
1163  *          from the target color.
1164  * </pre>
1165  */
1166 l_int32
pixcmapGetNearestIndex(PIXCMAP * cmap,l_int32 rval,l_int32 gval,l_int32 bval,l_int32 * pindex)1167 pixcmapGetNearestIndex(PIXCMAP  *cmap,
1168                        l_int32   rval,
1169                        l_int32   gval,
1170                        l_int32   bval,
1171                        l_int32  *pindex)
1172 {
1173 l_int32     i, n, delta, dist, mindist;
1174 RGBA_QUAD  *cta;
1175 
1176     PROCNAME("pixcmapGetNearestIndex");
1177 
1178     if (!pindex)
1179         return ERROR_INT("&index not defined", procName, 1);
1180     *pindex = UNDEF;
1181     if (!cmap)
1182         return ERROR_INT("cmap not defined", procName, 1);
1183 
1184     if ((cta = (RGBA_QUAD *)cmap->array) == NULL)
1185         return ERROR_INT("cta not defined(!)", procName, 1);
1186     n = pixcmapGetCount(cmap);
1187 
1188     mindist = 3 * 255 * 255 + 1;
1189     for (i = 0; i < n; i++) {
1190         delta = cta[i].red - rval;
1191         dist = delta * delta;
1192         delta = cta[i].green - gval;
1193         dist += delta * delta;
1194         delta = cta[i].blue - bval;
1195         dist += delta * delta;
1196         if (dist < mindist) {
1197             *pindex = i;
1198             if (dist == 0)
1199                 break;
1200             mindist = dist;
1201         }
1202     }
1203 
1204     return 0;
1205 }
1206 
1207 
1208 /*!
1209  * \brief   pixcmapGetNearestGrayIndex()
1210  *
1211  * \param[in]    cmap
1212  * \param[in]    val gray value to search for; in range [0, ... 255]
1213  * \param[out]   pindex the index of the nearest color
1214  * \return  0 if OK, 1 on error caller must check
1215  *
1216  * <pre>
1217  * Notes:
1218  *      (1) This should be used on gray colormaps.  It uses only the
1219  *          green value of the colormap.
1220  *      (2) Returns the index of the exact color if possible, otherwise the
1221  *          index of the color closest to the target color.
1222  * </pre>
1223  */
1224 l_int32
pixcmapGetNearestGrayIndex(PIXCMAP * cmap,l_int32 val,l_int32 * pindex)1225 pixcmapGetNearestGrayIndex(PIXCMAP  *cmap,
1226                            l_int32   val,
1227                            l_int32  *pindex)
1228 {
1229 l_int32     i, n, dist, mindist;
1230 RGBA_QUAD  *cta;
1231 
1232     PROCNAME("pixcmapGetNearestGrayIndex");
1233 
1234     if (!pindex)
1235         return ERROR_INT("&index not defined", procName, 1);
1236     *pindex = 0;
1237     if (!cmap)
1238         return ERROR_INT("cmap not defined", procName, 1);
1239     if (val < 0 || val > 255)
1240         return ERROR_INT("val not in [0 ... 255]", procName, 1);
1241 
1242     if ((cta = (RGBA_QUAD *)cmap->array) == NULL)
1243         return ERROR_INT("cta not defined(!)", procName, 1);
1244     n = pixcmapGetCount(cmap);
1245 
1246     mindist = 256;
1247     for (i = 0; i < n; i++) {
1248         dist = cta[i].green - val;
1249         dist = L_ABS(dist);
1250         if (dist < mindist) {
1251             *pindex = i;
1252             if (dist == 0)
1253                 break;
1254             mindist = dist;
1255         }
1256     }
1257 
1258     return 0;
1259 }
1260 
1261 
1262 /*!
1263  * \brief   pixcmapGetDistanceToColor()
1264  *
1265  * \param[in]    cmap
1266  * \param[in]    index
1267  * \param[in]    rval, gval, bval target color
1268  * \param[out]   pdist the distance from the cmap entry to target
1269  * \return  0 if OK, 1 on error
1270  *
1271  * <pre>
1272  * Notes:
1273  *      (1) Returns the L2 distance (squared) between the color at index i
1274  *          and the target color.
1275  * </pre>
1276  */
1277 l_int32
pixcmapGetDistanceToColor(PIXCMAP * cmap,l_int32 index,l_int32 rval,l_int32 gval,l_int32 bval,l_int32 * pdist)1278 pixcmapGetDistanceToColor(PIXCMAP  *cmap,
1279                           l_int32   index,
1280                           l_int32   rval,
1281                           l_int32   gval,
1282                           l_int32   bval,
1283                           l_int32  *pdist)
1284 {
1285 l_int32     n, delta, dist;
1286 RGBA_QUAD  *cta;
1287 
1288     PROCNAME("pixcmapGetDistanceToColor");
1289 
1290     if (!pdist)
1291         return ERROR_INT("&dist not defined", procName, 1);
1292     *pdist = UNDEF;
1293     if (!cmap)
1294         return ERROR_INT("cmap not defined", procName, 1);
1295     n = pixcmapGetCount(cmap);
1296     if (index >= n)
1297         return ERROR_INT("invalid index", procName, 1);
1298 
1299     if ((cta = (RGBA_QUAD *)cmap->array) == NULL)
1300         return ERROR_INT("cta not defined(!)", procName, 1);
1301 
1302     delta = cta[index].red - rval;
1303     dist = delta * delta;
1304     delta = cta[index].green - gval;
1305     dist += delta * delta;
1306     delta = cta[index].blue - bval;
1307     dist += delta * delta;
1308     *pdist = dist;
1309 
1310     return 0;
1311 }
1312 
1313 
1314 /*!
1315  * \brief   pixcmapGetRangeValues()
1316  *
1317  * \param[in]    cmap
1318  * \param[in]    select L_SELECT_RED, L_SELECT_GREEN, L_SELECT_BLUE or
1319  *                      L_SELECT_AVERAGE
1320  * \param[out]   pminval [optional] minimum value of component
1321  * \param[out]   pmaxval [optional] maximum value of component
1322  * \param[out]   pminindex [optional] index of minimum value
1323  * \param[out]   pmaxindex [optional] index of maximum value
1324  * \return  0 if OK, 1 on error
1325  *
1326  * <pre>
1327  * Notes:
1328  *      (1) Returns, for selected components (or the average), the
1329  *          the extreme values (min and/or max) and their indices
1330  *          that are found in the cmap.
1331  * </pre>
1332  */
1333 l_int32
pixcmapGetRangeValues(PIXCMAP * cmap,l_int32 select,l_int32 * pminval,l_int32 * pmaxval,l_int32 * pminindex,l_int32 * pmaxindex)1334 pixcmapGetRangeValues(PIXCMAP  *cmap,
1335                       l_int32   select,
1336                       l_int32  *pminval,
1337                       l_int32  *pmaxval,
1338                       l_int32  *pminindex,
1339                       l_int32  *pmaxindex)
1340 {
1341 l_int32  i, n, imin, imax, minval, maxval, rval, gval, bval, aveval;
1342 
1343     PROCNAME("pixcmapGetRangeValues");
1344 
1345     if (pminval) *pminval = UNDEF;
1346     if (pmaxval) *pmaxval = UNDEF;
1347     if (pminindex) *pminindex = UNDEF;
1348     if (pmaxindex) *pmaxindex = UNDEF;
1349     if (!pminval && !pmaxval && !pminindex && !pmaxindex)
1350         return ERROR_INT("no result requested", procName, 1);
1351     if (!cmap)
1352         return ERROR_INT("cmap not defined", procName, 1);
1353 
1354     imin = UNDEF;
1355     imax = UNDEF;
1356     minval = 100000;
1357     maxval = -1;
1358     n = pixcmapGetCount(cmap);
1359     for (i = 0; i < n; i++) {
1360         pixcmapGetColor(cmap, i, &rval, &gval, &bval);
1361         if (select == L_SELECT_RED) {
1362             if (rval < minval) {
1363                 minval = rval;
1364                 imin = i;
1365             }
1366             if (rval > maxval) {
1367                 maxval = rval;
1368                 imax = i;
1369             }
1370         } else if (select == L_SELECT_GREEN) {
1371             if (gval < minval) {
1372                 minval = gval;
1373                 imin = i;
1374             }
1375             if (gval > maxval) {
1376                 maxval = gval;
1377                 imax = i;
1378             }
1379         } else if (select == L_SELECT_BLUE) {
1380             if (bval < minval) {
1381                 minval = bval;
1382                 imin = i;
1383             }
1384             if (bval > maxval) {
1385                 maxval = bval;
1386                 imax = i;
1387             }
1388         } else if (select == L_SELECT_AVERAGE) {
1389             aveval = (rval + gval + bval) / 3;
1390             if (aveval < minval) {
1391                 minval = aveval;
1392                 imin = i;
1393             }
1394             if (aveval > maxval) {
1395                 maxval = aveval;
1396                 imax = i;
1397             }
1398         } else {
1399             return ERROR_INT("invalid selection", procName, 1);
1400         }
1401     }
1402 
1403     if (pminval) *pminval = minval;
1404     if (pmaxval) *pmaxval = maxval;
1405     if (pminindex) *pminindex = imin;
1406     if (pmaxindex) *pmaxindex = imax;
1407     return 0;
1408 }
1409 
1410 
1411 /*-------------------------------------------------------------*
1412  *                       Colormap conversion                   *
1413  *-------------------------------------------------------------*/
1414 /*!
1415  * \brief   pixcmapGrayToColor()
1416  *
1417  * \param[in]    color
1418  * \return  cmap, or NULL on error
1419  *
1420  * <pre>
1421  * Notes:
1422  *      (1) This creates a colormap that maps from gray to
1423  *          a specific color.  In the mapping, each component
1424  *          is faded to white, depending on the gray value.
1425  *      (2) In use, this is simply attached to a grayscale pix
1426  *          to give it the input color.
1427  * </pre>
1428  */
1429 PIXCMAP *
pixcmapGrayToColor(l_uint32 color)1430 pixcmapGrayToColor(l_uint32  color)
1431 {
1432 l_int32   i, rval, gval, bval;
1433 PIXCMAP  *cmap;
1434 
1435     extractRGBValues(color, &rval, &gval, &bval);
1436     cmap = pixcmapCreate(8);
1437     for (i = 0; i < 256; i++) {
1438         pixcmapAddColor(cmap, rval + (i * (255 - rval)) / 255,
1439                         gval + (i * (255 - gval)) / 255,
1440                         bval + (i * (255 - bval)) / 255);
1441     }
1442 
1443     return cmap;
1444 }
1445 
1446 
1447 /*!
1448  * \brief   pixcmapColorToGray()
1449  *
1450  * \param[in]    cmaps
1451  * \param[in]    rwt, gwt, bwt  non-negative; these should add to 1.0
1452  * \return  cmap gray, or NULL on error
1453  *
1454  * <pre>
1455  * Notes:
1456  *      (1) This creates a gray colormap from an arbitrary colormap.
1457  *      (2) In use, attach the output gray colormap to the pix
1458  *          (or a copy of it) that provided the input colormap.
1459  * </pre>
1460  */
1461 PIXCMAP *
pixcmapColorToGray(PIXCMAP * cmaps,l_float32 rwt,l_float32 gwt,l_float32 bwt)1462 pixcmapColorToGray(PIXCMAP   *cmaps,
1463                    l_float32  rwt,
1464                    l_float32  gwt,
1465                    l_float32  bwt)
1466 {
1467 l_int32    i, n, rval, gval, bval, val;
1468 l_float32  sum;
1469 PIXCMAP   *cmapd;
1470 
1471     PROCNAME("pixcmapColorToGray");
1472 
1473     if (!cmaps)
1474         return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL);
1475     if (rwt < 0.0 || gwt < 0.0 || bwt < 0.0)
1476         return (PIXCMAP *)ERROR_PTR("weights not all >= 0.0", procName, NULL);
1477 
1478         /* Make sure the sum of weights is 1.0; otherwise, you can get
1479          * overflow in the gray value. */
1480     sum = rwt + gwt + bwt;
1481     if (sum == 0.0) {
1482         L_WARNING("all weights zero; setting equal to 1/3\n", procName);
1483         rwt = gwt = bwt = 0.33333;
1484         sum = 1.0;
1485     }
1486     if (L_ABS(sum - 1.0) > 0.0001) {  /* maintain ratios with sum == 1.0 */
1487         L_WARNING("weights don't sum to 1; maintaining ratios\n", procName);
1488         rwt = rwt / sum;
1489         gwt = gwt / sum;
1490         bwt = bwt / sum;
1491     }
1492 
1493     cmapd = pixcmapCopy(cmaps);
1494     n = pixcmapGetCount(cmapd);
1495     for (i = 0; i < n; i++) {
1496         pixcmapGetColor(cmapd, i, &rval, &gval, &bval);
1497         val = (l_int32)(rwt * rval + gwt * gval + bwt * bval + 0.5);
1498         pixcmapResetColor(cmapd, i, val, val, val);
1499     }
1500 
1501     return cmapd;
1502 }
1503 
1504 
1505 /*!
1506  * \brief   pixcmapConvertTo4()
1507  *
1508  * \param[in]    cmaps   colormap for 2 bpp pix
1509  * \return  cmapd   (4 bpp)
1510  *
1511  * <pre>
1512  * Notes:
1513  *      (1) This converts a 2 bpp colormap to 4 bpp.  The colors
1514  *          are the same; the output colormap entry array has size 16.
1515  * </pre>
1516  */
1517 PIXCMAP *
pixcmapConvertTo4(PIXCMAP * cmaps)1518 pixcmapConvertTo4(PIXCMAP  *cmaps)
1519 {
1520 l_int32   i, n, rval, gval, bval;
1521 PIXCMAP  *cmapd;
1522 
1523     PROCNAME("pixcmapConvertTo4");
1524 
1525     if (!cmaps)
1526         return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL);
1527     if (pixcmapGetDepth(cmaps) != 2)
1528         return (PIXCMAP *)ERROR_PTR("cmaps not for 2 bpp pix", procName, NULL);
1529 
1530     cmapd = pixcmapCreate(4);
1531     n = pixcmapGetCount(cmaps);
1532     for (i = 0; i < n; i++) {
1533         pixcmapGetColor(cmaps, i, &rval, &gval, &bval);
1534         pixcmapAddColor(cmapd, rval, gval, bval);
1535     }
1536     return cmapd;
1537 }
1538 
1539 
1540 /*!
1541  * \brief   pixcmapConvertTo8()
1542  *
1543  * \param[in]    cmaps   colormap for 2 bpp or 4 bpp pix
1544  * \return  cmapd   (8 bpp)
1545  *
1546  * <pre>
1547  * Notes:
1548  *      (1) This converts a 2 bpp or 4 bpp colormap to 8 bpp.  The colors
1549  *          are the same; the output colormap entry array has size 256.
1550  * </pre>
1551  */
1552 PIXCMAP *
pixcmapConvertTo8(PIXCMAP * cmaps)1553 pixcmapConvertTo8(PIXCMAP  *cmaps)
1554 {
1555 l_int32   i, n, depth, rval, gval, bval;
1556 PIXCMAP  *cmapd;
1557 
1558     PROCNAME("pixcmapConvertTo8");
1559 
1560     if (!cmaps)
1561         return (PIXCMAP *)ERROR_PTR("cmaps not defined", procName, NULL);
1562     depth = pixcmapGetDepth(cmaps);
1563     if (depth == 8) return pixcmapCopy(cmaps);
1564     if (depth != 2 && depth != 4)
1565         return (PIXCMAP *)ERROR_PTR("cmaps not 2 or 4 bpp", procName, NULL);
1566 
1567     cmapd = pixcmapCreate(8);
1568     n = pixcmapGetCount(cmaps);
1569     for (i = 0; i < n; i++) {
1570         pixcmapGetColor(cmaps, i, &rval, &gval, &bval);
1571         pixcmapAddColor(cmapd, rval, gval, bval);
1572     }
1573     return cmapd;
1574 }
1575 
1576 
1577 /*-------------------------------------------------------------*
1578  *                         Colormap I/O                        *
1579  *-------------------------------------------------------------*/
1580 /*!
1581  * \brief   pixcmapRead()
1582  *
1583  * \param[in]    filename
1584  * \return  cmap, or NULL on error
1585  */
1586 PIXCMAP *
pixcmapRead(const char * filename)1587 pixcmapRead(const char  *filename)
1588 {
1589 FILE     *fp;
1590 PIXCMAP  *cmap;
1591 
1592     PROCNAME("pixcmapRead");
1593 
1594     if (!filename)
1595         return (PIXCMAP *)ERROR_PTR("filename not defined", procName, NULL);
1596 
1597     if ((fp = fopenReadStream(filename)) == NULL)
1598         return (PIXCMAP *)ERROR_PTR("stream not opened", procName, NULL);
1599     cmap = pixcmapReadStream(fp);
1600     fclose(fp);
1601     if (!cmap)
1602         return (PIXCMAP *)ERROR_PTR("cmap not read", procName, NULL);
1603     return cmap;
1604 }
1605 
1606 
1607 /*!
1608  * \brief   pixcmapReadStream()
1609  *
1610  * \param[in]    fp file stream
1611  * \return  cmap, or NULL on error
1612  */
1613 PIXCMAP *
pixcmapReadStream(FILE * fp)1614 pixcmapReadStream(FILE  *fp)
1615 {
1616 l_int32   rval, gval, bval, aval, ignore;
1617 l_int32   i, index, ret, depth, ncolors;
1618 PIXCMAP  *cmap;
1619 
1620     PROCNAME("pixcmapReadStream");
1621 
1622     if (!fp)
1623         return (PIXCMAP *)ERROR_PTR("stream not defined", procName, NULL);
1624 
1625     ret = fscanf(fp, "\nPixcmap: depth = %d bpp; %d colors\n",
1626                  &depth, &ncolors);
1627     if (ret != 2 ||
1628         (depth != 1 && depth != 2 && depth != 4 && depth != 8) ||
1629         (ncolors < 2 || ncolors > 256))
1630         return (PIXCMAP *)ERROR_PTR("invalid cmap size", procName, NULL);
1631     ignore = fscanf(fp, "Color    R-val    G-val    B-val   Alpha\n");
1632     ignore = fscanf(fp, "----------------------------------------\n");
1633 
1634     cmap = pixcmapCreate(depth);
1635     for (i = 0; i < ncolors; i++) {
1636         if (fscanf(fp, "%3d       %3d      %3d      %3d      %3d\n",
1637                         &index, &rval, &gval, &bval, &aval) != 5) {
1638             pixcmapDestroy(&cmap);
1639             return (PIXCMAP *)ERROR_PTR("invalid entry", procName, NULL);
1640         }
1641         pixcmapAddRGBA(cmap, rval, gval, bval, aval);
1642     }
1643     return cmap;
1644 }
1645 
1646 
1647 /*!
1648  * \brief   pixcmapReadMem()
1649  *
1650  * \param[in]    data  serialization of pixcmap; in ascii
1651  * \param[in]    size  of data in bytes; can use strlen to get it
1652  * \return  cmap, or NULL on error
1653  */
1654 PIXCMAP *
pixcmapReadMem(const l_uint8 * data,size_t size)1655 pixcmapReadMem(const l_uint8  *data,
1656                size_t          size)
1657 {
1658 FILE     *fp;
1659 PIXCMAP  *cmap;
1660 
1661     PROCNAME("pixcmapReadMem");
1662 
1663     if (!data)
1664         return (PIXCMAP *)ERROR_PTR("data not defined", procName, NULL);
1665     if ((fp = fopenReadFromMemory(data, size)) == NULL)
1666         return (PIXCMAP *)ERROR_PTR("stream not opened", procName, NULL);
1667 
1668     cmap = pixcmapReadStream(fp);
1669     fclose(fp);
1670     if (!cmap) L_ERROR("cmap not read\n", procName);
1671     return cmap;
1672 }
1673 
1674 
1675 /*!
1676  * \brief   pixcmapWrite()
1677  *
1678  * \param[in]    filename
1679  * \param[in]    cmap
1680  * \return  0 if OK, 1 on error
1681  */
1682 l_int32
pixcmapWrite(const char * filename,PIXCMAP * cmap)1683 pixcmapWrite(const char  *filename,
1684              PIXCMAP     *cmap)
1685 {
1686 l_int32  ret;
1687 FILE    *fp;
1688 
1689     PROCNAME("pixcmapWrite");
1690 
1691     if (!filename)
1692         return ERROR_INT("filename not defined", procName, 1);
1693     if (!cmap)
1694         return ERROR_INT("cmap not defined", procName, 1);
1695 
1696     if ((fp = fopenWriteStream(filename, "w")) == NULL)
1697         return ERROR_INT("stream not opened", procName, 1);
1698     ret = pixcmapWriteStream(fp, cmap);
1699     fclose(fp);
1700     if (ret)
1701         return ERROR_INT("cmap not written to stream", procName, 1);
1702     return 0;
1703 }
1704 
1705 
1706 
1707 /*!
1708  * \brief   pixcmapWriteStream()
1709  *
1710  * \param[in]    fp file stream
1711    \param[in]    cmap
1712  * \return  0 if OK, 1 on error
1713  */
1714 l_int32
pixcmapWriteStream(FILE * fp,PIXCMAP * cmap)1715 pixcmapWriteStream(FILE     *fp,
1716                    PIXCMAP  *cmap)
1717 {
1718 l_int32  *rmap, *gmap, *bmap, *amap;
1719 l_int32   i;
1720 
1721     PROCNAME("pixcmapWriteStream");
1722 
1723     if (!fp)
1724         return ERROR_INT("stream not defined", procName, 1);
1725     if (!cmap)
1726         return ERROR_INT("cmap not defined", procName, 1);
1727 
1728     if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap))
1729         return ERROR_INT("colormap arrays not made", procName, 1);
1730 
1731     fprintf(fp, "\nPixcmap: depth = %d bpp; %d colors\n", cmap->depth, cmap->n);
1732     fprintf(fp, "Color    R-val    G-val    B-val   Alpha\n");
1733     fprintf(fp, "----------------------------------------\n");
1734     for (i = 0; i < cmap->n; i++)
1735         fprintf(fp, "%3d       %3d      %3d      %3d      %3d\n",
1736                 i, rmap[i], gmap[i], bmap[i], amap[i]);
1737     fprintf(fp, "\n");
1738 
1739     LEPT_FREE(rmap);
1740     LEPT_FREE(gmap);
1741     LEPT_FREE(bmap);
1742     LEPT_FREE(amap);
1743     return 0;
1744 }
1745 
1746 
1747 /*!
1748  * \brief   pixcmapWriteMem()
1749  *
1750  * \param[out]   pdata data of serialized pixcmap; ascii
1751  * \param[out]   psize size of returned data
1752  * \param[in]    cmap
1753  * \return  0 if OK, 1 on error
1754  *
1755  * <pre>
1756  * Notes:
1757  *      (1) Serializes a pixcmap in memory and puts the result in a buffer.
1758  * </pre>
1759  */
1760 l_int32
pixcmapWriteMem(l_uint8 ** pdata,size_t * psize,PIXCMAP * cmap)1761 pixcmapWriteMem(l_uint8  **pdata,
1762                 size_t    *psize,
1763                 PIXCMAP   *cmap)
1764 {
1765 l_int32  ret;
1766 FILE    *fp;
1767 
1768     PROCNAME("pixcmapWriteMem");
1769 
1770     if (pdata) *pdata = NULL;
1771     if (psize) *psize = 0;
1772     if (!pdata)
1773         return ERROR_INT("&data not defined", procName, 1);
1774     if (!psize)
1775         return ERROR_INT("&size not defined", procName, 1);
1776     if (!cmap)
1777         return ERROR_INT("cmap not defined", procName, 1);
1778 
1779 #if HAVE_FMEMOPEN
1780     if ((fp = open_memstream((char **)pdata, psize)) == NULL)
1781         return ERROR_INT("stream not opened", procName, 1);
1782     ret = pixcmapWriteStream(fp, cmap);
1783 #else
1784     L_INFO("work-around: writing to a temp file\n", procName);
1785   #ifdef _WIN32
1786     if ((fp = fopenWriteWinTempfile()) == NULL)
1787         return ERROR_INT("tmpfile stream not opened", procName, 1);
1788   #else
1789     if ((fp = tmpfile()) == NULL)
1790         return ERROR_INT("tmpfile stream not opened", procName, 1);
1791   #endif  /* _WIN32 */
1792     ret = pixcmapWriteStream(fp, cmap);
1793     rewind(fp);
1794     *pdata = l_binaryReadStream(fp, psize);
1795 #endif  /* HAVE_FMEMOPEN */
1796     fclose(fp);
1797     return ret;
1798 }
1799 
1800 
1801 /*----------------------------------------------------------------------*
1802  *               Extract colormap arrays and serialization              *
1803  *----------------------------------------------------------------------*/
1804 /*!
1805  * \brief   pixcmapToArrays()
1806  *
1807  * \param[in]    cmap colormap
1808  * \param[out]   prmap, pgmap, pbmap  colormap arrays
1809  * \param[out]   pamap [optional] alpha array
1810  * \return  0 if OK; 1 on error
1811  */
1812 l_int32
pixcmapToArrays(PIXCMAP * cmap,l_int32 ** prmap,l_int32 ** pgmap,l_int32 ** pbmap,l_int32 ** pamap)1813 pixcmapToArrays(PIXCMAP   *cmap,
1814                 l_int32  **prmap,
1815                 l_int32  **pgmap,
1816                 l_int32  **pbmap,
1817                 l_int32  **pamap)
1818 {
1819 l_int32    *rmap, *gmap, *bmap, *amap;
1820 l_int32     i, ncolors;
1821 RGBA_QUAD  *cta;
1822 
1823     PROCNAME("pixcmapToArrays");
1824 
1825     if (!prmap || !pgmap || !pbmap)
1826         return ERROR_INT("&rmap, &gmap, &bmap not all defined", procName, 1);
1827     *prmap = *pgmap = *pbmap = NULL;
1828     if (pamap) *pamap = NULL;
1829     if (!cmap)
1830         return ERROR_INT("cmap not defined", procName, 1);
1831 
1832     ncolors = pixcmapGetCount(cmap);
1833     if (((rmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32))) == NULL) ||
1834         ((gmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32))) == NULL) ||
1835         ((bmap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32))) == NULL))
1836             return ERROR_INT("calloc fail for *map", procName, 1);
1837     *prmap = rmap;
1838     *pgmap = gmap;
1839     *pbmap = bmap;
1840     if (pamap) {
1841         amap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32));
1842         *pamap = amap;
1843     }
1844 
1845     cta = (RGBA_QUAD *)cmap->array;
1846     for (i = 0; i < ncolors; i++) {
1847         rmap[i] = cta[i].red;
1848         gmap[i] = cta[i].green;
1849         bmap[i] = cta[i].blue;
1850         if (pamap)
1851             amap[i] = cta[i].alpha;
1852     }
1853 
1854     return 0;
1855 }
1856 
1857 
1858 /*!
1859  * \brief   pixcmapToRGBTable()
1860  *
1861  * \param[in]    cmap colormap
1862  * \param[out]   ptab table of rgba values for the colormap
1863  * \param[out]   pncolors [optional] size of table
1864  * \return  0 if OK; 1 on error
1865  */
1866 l_int32
pixcmapToRGBTable(PIXCMAP * cmap,l_uint32 ** ptab,l_int32 * pncolors)1867 pixcmapToRGBTable(PIXCMAP    *cmap,
1868                   l_uint32  **ptab,
1869                   l_int32    *pncolors)
1870 {
1871 l_int32    i, ncolors, rval, gval, bval, aval;
1872 l_uint32  *tab;
1873 
1874     PROCNAME("pixcmapToRGBTable");
1875 
1876     if (!ptab)
1877         return ERROR_INT("&tab not defined", procName, 1);
1878     *ptab = NULL;
1879     if (!cmap)
1880         return ERROR_INT("cmap not defined", procName, 1);
1881 
1882     ncolors = pixcmapGetCount(cmap);
1883     if (pncolors)
1884         *pncolors = ncolors;
1885     if ((tab = (l_uint32 *)LEPT_CALLOC(ncolors, sizeof(l_uint32))) == NULL)
1886         return ERROR_INT("tab not made", procName, 1);
1887     *ptab = tab;
1888 
1889     for (i = 0; i < ncolors; i++) {
1890         pixcmapGetRGBA(cmap, i, &rval, &gval, &bval, &aval);
1891         composeRGBAPixel(rval, gval, bval, aval, &tab[i]);
1892     }
1893     return 0;
1894 }
1895 
1896 
1897 /*!
1898  * \brief   pixcmapSerializeToMemory()
1899  *
1900  * \param[in]    cmap colormap
1901  * \param[in]    cpc components/color: 3 for rgb, 4 for rgba
1902  * \param[out]   pncolors number of colors in table
1903  * \param[out]   pdata binary string, cpc bytes per color
1904  * \return  0 if OK; 1 on error
1905  *
1906  * <pre>
1907  * Notes:
1908  *      (1) When serializing to store in a pdf, use %cpc = 3.
1909  * </pre>
1910  */
1911 l_int32
pixcmapSerializeToMemory(PIXCMAP * cmap,l_int32 cpc,l_int32 * pncolors,l_uint8 ** pdata)1912 pixcmapSerializeToMemory(PIXCMAP   *cmap,
1913                          l_int32    cpc,
1914                          l_int32   *pncolors,
1915                          l_uint8  **pdata)
1916 {
1917 l_int32   i, ncolors, rval, gval, bval, aval;
1918 l_uint8  *data;
1919 
1920     PROCNAME("pixcmapSerializeToMemory");
1921 
1922     if (!pdata)
1923         return ERROR_INT("&data not defined", procName, 1);
1924     *pdata = NULL;
1925     if (!pncolors)
1926         return ERROR_INT("&ncolors not defined", procName, 1);
1927     *pncolors = 0;
1928     if (!cmap)
1929         return ERROR_INT("cmap not defined", procName, 1);
1930     if (cpc != 3 && cpc != 4)
1931         return ERROR_INT("cpc not 3 or 4", procName, 1);
1932 
1933     ncolors = pixcmapGetCount(cmap);
1934     *pncolors = ncolors;
1935     if ((data = (l_uint8 *)LEPT_CALLOC(cpc * ncolors, sizeof(l_uint8))) == NULL)
1936         return ERROR_INT("data not made", procName, 1);
1937     *pdata = data;
1938 
1939     for (i = 0; i < ncolors; i++) {
1940         pixcmapGetRGBA(cmap, i, &rval, &gval, &bval, &aval);
1941         data[cpc * i] = rval;
1942         data[cpc * i + 1] = gval;
1943         data[cpc * i + 2] = bval;
1944         if (cpc == 4)
1945             data[cpc * i + 3] = aval;
1946     }
1947     return 0;
1948 }
1949 
1950 
1951 /*!
1952  * \brief   pixcmapDeserializeFromMemory()
1953  *
1954  * \param[in]    data binary string, 3 or 4 bytes per color
1955  * \param[in]    cpc components/color: 3 for rgb, 4 for rgba
1956  * \param[in]    ncolors
1957  * \return  cmap, or NULL on error
1958  */
1959 PIXCMAP *
pixcmapDeserializeFromMemory(l_uint8 * data,l_int32 cpc,l_int32 ncolors)1960 pixcmapDeserializeFromMemory(l_uint8  *data,
1961                              l_int32   cpc,
1962                              l_int32   ncolors)
1963 {
1964 l_int32   i, d, rval, gval, bval, aval;
1965 PIXCMAP  *cmap;
1966 
1967     PROCNAME("pixcmapDeserializeFromMemory");
1968 
1969     if (!data)
1970         return (PIXCMAP *)ERROR_PTR("data not defined", procName, NULL);
1971     if (cpc != 3 && cpc != 4)
1972         return (PIXCMAP *)ERROR_PTR("cpc not 3 or 4", procName, NULL);
1973     if (ncolors == 0)
1974         return (PIXCMAP *)ERROR_PTR("no entries", procName, NULL);
1975     if (ncolors > 256)
1976         return (PIXCMAP *)ERROR_PTR("ncolors > 256", procName, NULL);
1977 
1978     if (ncolors > 16)
1979         d = 8;
1980     else if (ncolors > 4)
1981         d = 4;
1982     else if (ncolors > 2)
1983         d = 2;
1984     else
1985         d = 1;
1986     cmap = pixcmapCreate(d);
1987     for (i = 0; i < ncolors; i++) {
1988         rval = data[cpc * i];
1989         gval = data[cpc * i + 1];
1990         bval = data[cpc * i + 2];
1991         if (cpc == 4)
1992             aval = data[cpc * i + 3];
1993         else
1994             aval = 255;  /* opaque */
1995         pixcmapAddRGBA(cmap, rval, gval, bval, aval);
1996     }
1997 
1998     return cmap;
1999 }
2000 
2001 
2002 /*!
2003  * \brief   pixcmapConvertToHex()
2004  *
2005  * \param[in]    data  binary serialized data
2006  * \param[in]    ncolors in colormap
2007  * \return  hexdata bracketed, space-separated ascii hex string,
2008  *                       or NULL on error.
2009  *
2010  * <pre>
2011  * Notes:
2012  *      (1) The number of bytes in %data is 3 * ncolors.
2013  *      (2) Output is in form:
2014  *             < r0g0b0 r1g1b1 ... rngnbn >
2015  *          where r0, g0, b0 ... are each 2 bytes of hex ascii
2016  *      (3) This is used in pdf files to express the colormap as an
2017  *          array in ascii (human-readable) format.
2018  * </pre>
2019  */
2020 char *
pixcmapConvertToHex(l_uint8 * data,l_int32 ncolors)2021 pixcmapConvertToHex(l_uint8 *data,
2022                     l_int32  ncolors)
2023 {
2024 l_int32  i, j, hexbytes;
2025 char    *hexdata = NULL;
2026 char     buf[4];
2027 
2028     PROCNAME("pixcmapConvertToHex");
2029 
2030     if (!data)
2031         return (char *)ERROR_PTR("data not defined", procName, NULL);
2032     if (ncolors < 1)
2033         return (char *)ERROR_PTR("no colors", procName, NULL);
2034 
2035     hexbytes = 2 + (2 * 3 + 1) * ncolors + 2;
2036     hexdata = (char *)LEPT_CALLOC(hexbytes, sizeof(char));
2037     hexdata[0] = '<';
2038     hexdata[1] = ' ';
2039 
2040     for (i = 0; i < ncolors; i++) {
2041         j = 2 + (2 * 3 + 1) * i;
2042         snprintf(buf, sizeof(buf), "%02x", data[3 * i]);
2043         hexdata[j] = buf[0];
2044         hexdata[j + 1] = buf[1];
2045         snprintf(buf, sizeof(buf), "%02x", data[3 * i + 1]);
2046         hexdata[j + 2] = buf[0];
2047         hexdata[j + 3] = buf[1];
2048         snprintf(buf, sizeof(buf), "%02x", data[3 * i + 2]);
2049         hexdata[j + 4] = buf[0];
2050         hexdata[j + 5] = buf[1];
2051         hexdata[j + 6] = ' ';
2052     }
2053     hexdata[j + 7] = '>';
2054     hexdata[j + 8] = '\0';
2055     return hexdata;
2056 }
2057 
2058 
2059 /*-------------------------------------------------------------*
2060  *                     Colormap transforms                     *
2061  *-------------------------------------------------------------*/
2062 /*!
2063  * \brief   pixcmapGammaTRC()
2064  *
2065  * \param[in]    cmap colormap
2066  * \param[in]    gamma gamma correction; must be > 0.0
2067  * \param[in]    minval  input value that gives 0 for output; can be < 0
2068  * \param[in]    maxval  input value that gives 255 for output; can be > 255
2069  * \return  0 if OK; 1 on error
2070  *
2071  * <pre>
2072  * Notes:
2073  *      (1) This is an in-place transform
2074  *      (2) See pixGammaTRC() and numaGammaTRC() in enhance.c
2075  *          for description and use of transform
2076  * </pre>
2077  */
2078 l_int32
pixcmapGammaTRC(PIXCMAP * cmap,l_float32 gamma,l_int32 minval,l_int32 maxval)2079 pixcmapGammaTRC(PIXCMAP   *cmap,
2080                 l_float32  gamma,
2081                 l_int32    minval,
2082                 l_int32    maxval)
2083 {
2084 l_int32  rval, gval, bval, trval, tgval, tbval, i, ncolors;
2085 NUMA    *nag;
2086 
2087     PROCNAME("pixcmapGammaTRC");
2088 
2089     if (!cmap)
2090         return ERROR_INT("cmap not defined", procName, 1);
2091     if (gamma <= 0.0) {
2092         L_WARNING("gamma must be > 0.0; setting to 1.0\n", procName);
2093         gamma = 1.0;
2094     }
2095     if (minval >= maxval)
2096         return ERROR_INT("minval not < maxval", procName, 1);
2097 
2098     if (gamma == 1.0 && minval == 0 && maxval == 255)  /* no-op */
2099         return 0;
2100 
2101     if ((nag = numaGammaTRC(gamma, minval, maxval)) == NULL)
2102         return ERROR_INT("nag not made", procName, 1);
2103 
2104     ncolors = pixcmapGetCount(cmap);
2105     for (i = 0; i < ncolors; i++) {
2106         pixcmapGetColor(cmap, i, &rval, &gval, &bval);
2107         numaGetIValue(nag, rval, &trval);
2108         numaGetIValue(nag, gval, &tgval);
2109         numaGetIValue(nag, bval, &tbval);
2110         pixcmapResetColor(cmap, i, trval, tgval, tbval);
2111     }
2112 
2113     numaDestroy(&nag);
2114     return 0;
2115 }
2116 
2117 
2118 /*!
2119  * \brief   pixcmapContrastTRC()
2120  *
2121  * \param[in]    cmap colormap
2122  * \param[in]    factor generally between 0.0 [no enhancement]
2123  *                      and 1.0, but can be larger than 1.0
2124  * \return  0 if OK; 1 on error
2125  *
2126  * <pre>
2127  * Notes:
2128  *      (1) This is an in-place transform
2129  *      (2) See pixContrastTRC() and numaContrastTRC() in enhance.c
2130  *          for description and use of transform
2131  * </pre>
2132  */
2133 l_int32
pixcmapContrastTRC(PIXCMAP * cmap,l_float32 factor)2134 pixcmapContrastTRC(PIXCMAP   *cmap,
2135                    l_float32  factor)
2136 {
2137 l_int32  i, ncolors, rval, gval, bval, trval, tgval, tbval;
2138 NUMA    *nac;
2139 
2140     PROCNAME("pixcmapContrastTRC");
2141 
2142     if (!cmap)
2143         return ERROR_INT("cmap not defined", procName, 1);
2144     if (factor < 0.0) {
2145         L_WARNING("factor must be >= 0.0; setting to 0.0\n", procName);
2146         factor = 0.0;
2147     }
2148 
2149     if ((nac = numaContrastTRC(factor)) == NULL)
2150         return ERROR_INT("nac not made", procName, 1);
2151 
2152     ncolors = pixcmapGetCount(cmap);
2153     for (i = 0; i < ncolors; i++) {
2154         pixcmapGetColor(cmap, i, &rval, &gval, &bval);
2155         numaGetIValue(nac, rval, &trval);
2156         numaGetIValue(nac, gval, &tgval);
2157         numaGetIValue(nac, bval, &tbval);
2158         pixcmapResetColor(cmap, i, trval, tgval, tbval);
2159     }
2160 
2161     numaDestroy(&nac);
2162     return 0;
2163 }
2164 
2165 
2166 /*!
2167  * \brief   pixcmapShiftIntensity()
2168  *
2169  * \param[in]    cmap colormap
2170  * \param[in]    fraction between -1.0 and +1.0
2171  * \return  0 if OK; 1 on error
2172  *
2173  * <pre>
2174  * Notes:
2175  *      (1) This is an in-place transform
2176  *      (2) It does a proportional shift of the intensity for each color.
2177  *      (3) If fraction < 0.0, it moves all colors towards (0,0,0).
2178  *          This darkens the image.
2179  *          If fraction > 0.0, it moves all colors towards (255,255,255)
2180  *          This fades the image.
2181  *      (4) The equivalent transform can be accomplished with pixcmapGammaTRC(),
2182  *          but it is considerably more difficult (see numaGammaTRC()).
2183  * </pre>
2184  */
2185 l_int32
pixcmapShiftIntensity(PIXCMAP * cmap,l_float32 fraction)2186 pixcmapShiftIntensity(PIXCMAP   *cmap,
2187                       l_float32  fraction)
2188 {
2189 l_int32  i, ncolors, rval, gval, bval;
2190 
2191     PROCNAME("pixcmapShiftIntensity");
2192 
2193     if (!cmap)
2194         return ERROR_INT("cmap not defined", procName, 1);
2195     if (fraction < -1.0 || fraction > 1.0)
2196         return ERROR_INT("fraction not in [-1.0, 1.0]", procName, 1);
2197 
2198     ncolors = pixcmapGetCount(cmap);
2199     for (i = 0; i < ncolors; i++) {
2200         pixcmapGetColor(cmap, i, &rval, &gval, &bval);
2201         if (fraction < 0.0)
2202             pixcmapResetColor(cmap, i,
2203                               (l_int32)((1.0 + fraction) * rval),
2204                               (l_int32)((1.0 + fraction) * gval),
2205                               (l_int32)((1.0 + fraction) * bval));
2206         else
2207             pixcmapResetColor(cmap, i,
2208                               rval + (l_int32)(fraction * (255 - rval)),
2209                               gval + (l_int32)(fraction * (255 - gval)),
2210                               bval + (l_int32)(fraction * (255 - bval)));
2211     }
2212 
2213     return 0;
2214 }
2215 
2216 
2217 /*!
2218  * \brief   pixcmapShiftByComponent()
2219  *
2220  * \param[in]    cmap colormap
2221  * \param[in]    srcval source color: 0xrrggbb00
2222  * \param[in]    dstval target color: 0xrrggbb00
2223  * \return  0 if OK; 1 on error
2224  *
2225  * <pre>
2226  * Notes:
2227  *      (1) This is an in-place transform
2228  *      (2) It implements pixelShiftByComponent() for each color.
2229  *          The mapping is specified by srcval and dstval.
2230  *      (3) If a component decreases, the component in the colormap
2231  *          decreases by the same ratio.  Likewise for increasing, except
2232  *          all ratios are taken with respect to the distance from 255.
2233  * </pre>
2234  */
2235 l_int32
pixcmapShiftByComponent(PIXCMAP * cmap,l_uint32 srcval,l_uint32 dstval)2236 pixcmapShiftByComponent(PIXCMAP  *cmap,
2237                         l_uint32  srcval,
2238                         l_uint32  dstval)
2239 {
2240 l_int32   i, ncolors, rval, gval, bval;
2241 l_uint32  newval;
2242 
2243     PROCNAME("pixcmapShiftByComponent");
2244 
2245     if (!cmap)
2246         return ERROR_INT("cmap not defined", procName, 1);
2247 
2248     ncolors = pixcmapGetCount(cmap);
2249     for (i = 0; i < ncolors; i++) {
2250         pixcmapGetColor(cmap, i, &rval, &gval, &bval);
2251         pixelShiftByComponent(rval, gval, bval, srcval, dstval, &newval);
2252         extractRGBValues(newval, &rval, &gval, &bval);
2253         pixcmapResetColor(cmap, i, rval, gval, bval);
2254     }
2255 
2256     return 0;
2257 }
2258