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