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 pixconv.c
29  * <pre>
30  *
31  *      These functions convert between images of different types
32  *      without scaling.
33  *
34  *      Conversion from 8 bpp grayscale to 1, 2, 4 and 8 bpp
35  *           PIX        *pixThreshold8()
36  *
37  *      Conversion from colormap to full color or grayscale
38  *           PIX        *pixRemoveColormapGeneral()
39  *           PIX        *pixRemoveColormap()
40  *
41  *      Add colormap losslessly (8 to 8)
42  *           l_int32     pixAddGrayColormap8()
43  *           PIX        *pixAddMinimalGrayColormap8()
44  *
45  *      Conversion from RGB color to grayscale
46  *           PIX        *pixConvertRGBToLuminance()
47  *           PIX        *pixConvertRGBToGray()
48  *           PIX        *pixConvertRGBToGrayFast()
49  *           PIX        *pixConvertRGBToGrayMinMax()
50  *           PIX        *pixConvertRGBToGraySatBoost()
51  *           PIX        *pixConvertRGBToGrayArb()
52  *           PIX        *pixConvertRGBToBinaryArb()
53  *
54  *      Conversion from grayscale to colormap
55  *           PIX        *pixConvertGrayToColormap()  -- 2, 4, 8 bpp
56  *           PIX        *pixConvertGrayToColormap8()  -- 8 bpp only
57  *
58  *      Colorizing conversion from grayscale to color
59  *           PIX        *pixColorizeGray()  -- 8 bpp or cmapped
60  *
61  *      Conversion from RGB color to colormap
62  *           PIX        *pixConvertRGBToColormap()
63  *
64  *      Conversion from colormap to 1 bpp
65  *           PIX        *pixConvertCmapTo1()
66  *
67  *      Quantization for relatively small number of colors in source
68  *           l_int32     pixQuantizeIfFewColors()
69  *
70  *      Conversion from 16 bpp to 8 bpp
71  *           PIX        *pixConvert16To8()
72  *
73  *      Conversion from grayscale to false color
74  *           PIX        *pixConvertGrayToFalseColor()
75  *
76  *      Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp
77  *           PIX        *pixUnpackBinary()
78  *           PIX        *pixConvert1To16()
79  *           PIX        *pixConvert1To32()
80  *
81  *      Unpacking conversion from 1 bpp to 2 bpp
82  *           PIX        *pixConvert1To2Cmap()
83  *           PIX        *pixConvert1To2()
84  *
85  *      Unpacking conversion from 1 bpp to 4 bpp
86  *           PIX        *pixConvert1To4Cmap()
87  *           PIX        *pixConvert1To4()
88  *
89  *      Unpacking conversion from 1, 2 and 4 bpp to 8 bpp
90  *           PIX        *pixConvert1To8()
91  *           PIX        *pixConvert2To8()
92  *           PIX        *pixConvert4To8()
93  *
94  *      Unpacking conversion from 8 bpp to 16 bpp
95  *           PIX        *pixConvert8To16()
96  *
97  *      Top-level conversion to 1 bpp
98  *           PIX        *pixConvertTo1()
99  *           PIX        *pixConvertTo1BySampling()
100  *
101  *      Top-level conversion to 2 bpp
102  *           PIX        *pixConvertTo2()
103  *           PIX        *pixConvert8To2()
104  *
105  *      Top-level conversion to 4 bpp
106  *           PIX        *pixConvertTo4()
107  *           PIX        *pixConvert8To4()
108  *
109  *      Top-level conversion to 8 bpp
110  *           PIX        *pixConvertTo8()
111  *           PIX        *pixConvertTo8BySampling()
112  *           PIX        *pixConvertTo8Colormap()
113  *
114  *      Top-level conversion to 16 bpp
115  *           PIX        *pixConvertTo16()
116  *
117  *      Top-level conversion to 32 bpp (RGB)
118  *           PIX        *pixConvertTo32()   ***
119  *           PIX        *pixConvertTo32BySampling()   ***
120  *           PIX        *pixConvert8To32()  ***
121  *
122  *      Top-level conversion to 8 or 32 bpp, without colormap
123  *           PIX        *pixConvertTo8Or32
124  *
125  *      Conversion between 24 bpp and 32 bpp rgb
126  *           PIX        *pixConvert24To32()
127  *           PIX        *pixConvert32To24()
128  *
129  *      Conversion between 32 bpp (1 spp) and 16 or 8 bpp
130  *           PIX        *pixConvert32To16()
131  *           PIX        *pixConvert32To8()
132  *
133  *      Removal of alpha component by blending with white background
134  *           PIX        *pixRemoveAlpha()
135  *
136  *      Addition of alpha component to 1 bpp
137  *           PIX        *pixAddAlphaTo1bpp()
138  *
139  *      Lossless depth conversion (unpacking)
140  *           PIX        *pixConvertLossless()
141  *
142  *      Conversion for printing in PostScript
143  *           PIX        *pixConvertForPSWrap()
144  *
145  *      Scaling conversion to subpixel RGB
146  *           PIX        *pixConvertToSubpixelRGB()
147  *           PIX        *pixConvertGrayToSubpixelRGB()
148  *           PIX        *pixConvertColorToSubpixelRGB()
149  *
150  *      Setting neutral point for min/max boost conversion to gray
151  *          void         l_setNeutralBoostVal()
152  * </pre>
153  */
154 
155 #include <string.h>
156 #include <math.h>
157 #include "allheaders.h"
158 
159 /* ------- Set neutral point for min/max boost conversion to gray ------ */
160    /* Call l_setNeutralBoostVal() to change this */
161 static l_int32  var_NEUTRAL_BOOST_VAL = 180;
162 
163 
164 #ifndef  NO_CONSOLE_IO
165 #define DEBUG_CONVERT_TO_COLORMAP  0
166 #define DEBUG_UNROLLING 0
167 #endif   /* ~NO_CONSOLE_IO */
168 
169 
170 /*-------------------------------------------------------------*
171  *     Conversion from 8 bpp grayscale to 1, 2 4 and 8 bpp     *
172  *-------------------------------------------------------------*/
173 /*!
174  * \brief   pixThreshold8()
175  *
176  * \param[in]    pixs       8 bpp grayscale
177  * \param[in]    d          destination depth: 1, 2, 4 or 8
178  * \param[in]    nlevels    number of levels to be used for colormap
179  * \param[in]    cmapflag   1 if makes colormap; 0 otherwise
180  * \return  pixd thresholded with standard dest thresholds,
181  *              or NULL on error
182  *
183  * <pre>
184  * Notes:
185  *      (1) This uses, by default, equally spaced "target" values
186  *          that depend on the number of levels, with thresholds
187  *          halfway between.  For N levels, with separation (N-1)/255,
188  *          there are N-1 fixed thresholds.
189  *      (2) For 1 bpp destination, the number of levels can only be 2
190  *          and if a cmap is made, black is (0,0,0) and white
191  *          is (255,255,255), which is opposite to the convention
192  *          without a colormap.
193  *      (3) For 1, 2 and 4 bpp, the nlevels arg is used if a colormap
194  *          is made; otherwise, we take the most significant bits
195  *          from the src that will fit in the dest.
196  *      (4) For 8 bpp, the input pixs is quantized to nlevels.  The
197  *          dest quantized with that mapping, either through a colormap
198  *          table or directly with 8 bit values.
199  *      (5) Typically you should not use make a colormap for 1 bpp dest.
200  *      (6) This is not dithering.  Each pixel is treated independently.
201  * </pre>
202  */
203 PIX *
pixThreshold8(PIX * pixs,l_int32 d,l_int32 nlevels,l_int32 cmapflag)204 pixThreshold8(PIX     *pixs,
205               l_int32  d,
206               l_int32  nlevels,
207               l_int32  cmapflag)
208 {
209 PIX       *pixd;
210 PIXCMAP   *cmap;
211 
212     PROCNAME("pixThreshold8");
213 
214     if (!pixs)
215         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
216     if (pixGetDepth(pixs) != 8)
217         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
218     if (cmapflag && nlevels < 2)
219         return (PIX *)ERROR_PTR("nlevels must be at least 2", procName, NULL);
220 
221     switch (d) {
222     case 1:
223         pixd = pixThresholdToBinary(pixs, 128);
224         if (cmapflag) {
225             cmap = pixcmapCreateLinear(1, 2);
226             pixSetColormap(pixd, cmap);
227         }
228         break;
229     case 2:
230         pixd = pixThresholdTo2bpp(pixs, nlevels, cmapflag);
231         break;
232     case 4:
233         pixd = pixThresholdTo4bpp(pixs, nlevels, cmapflag);
234         break;
235     case 8:
236         pixd = pixThresholdOn8bpp(pixs, nlevels, cmapflag);
237         break;
238     default:
239         return (PIX *)ERROR_PTR("d must be in {1,2,4,8}", procName, NULL);
240     }
241 
242     if (!pixd)
243         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
244     pixCopyInputFormat(pixd, pixs);
245     return pixd;
246 }
247 
248 
249 /*-------------------------------------------------------------*
250  *               Conversion from colormapped pix               *
251  *-------------------------------------------------------------*/
252 /*!
253  * \brief   pixRemoveColormapGeneral()
254  *
255  * \param[in]    pixs      any depth, with or without colormap
256  * \param[in]    type      REMOVE_CMAP_TO_BINARY,
257  *                         REMOVE_CMAP_TO_GRAYSCALE,
258  *                         REMOVE_CMAP_TO_FULL_COLOR,
259  *                         REMOVE_CMAP_WITH_ALPHA,
260  *                         REMOVE_CMAP_BASED_ON_SRC
261  * \param[in]    ifnocmap  L_CLONE, L_COPY
262  * \return  pixd always a new pix; without colormap, or NULL on error
263  *
264  * <pre>
265  * Notes:
266  *      (1) Convenience function that allows choice between returning
267  *          a clone or a copy if pixs does not have a colormap.
268  *      (2) See pixRemoveColormap().
269  * </pre>
270  */
271 PIX *
pixRemoveColormapGeneral(PIX * pixs,l_int32 type,l_int32 ifnocmap)272 pixRemoveColormapGeneral(PIX     *pixs,
273                          l_int32  type,
274                          l_int32  ifnocmap)
275 {
276     PROCNAME("pixRemoveColormapGeneral");
277 
278     if (!pixs)
279         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
280     if (ifnocmap != L_CLONE && ifnocmap != L_COPY)
281         return (PIX *)ERROR_PTR("invalid value for ifnocmap", procName, NULL);
282 
283     if (pixGetColormap(pixs))
284         return pixRemoveColormap(pixs, type);
285 
286     if (ifnocmap == L_CLONE)
287         return pixClone(pixs);
288     else
289         return pixCopy(NULL, pixs);
290 }
291 
292 
293 /*!
294  * \brief   pixRemoveColormap()
295  *
296  * \param[in]    pixs   see restrictions below
297  * \param[in]    type   REMOVE_CMAP_TO_BINARY,
298  *                      REMOVE_CMAP_TO_GRAYSCALE,
299  *                      REMOVE_CMAP_TO_FULL_COLOR,
300  *                      REMOVE_CMAP_WITH_ALPHA,
301  *                      REMOVE_CMAP_BASED_ON_SRC
302  * \return  pixd without colormap, or NULL on error
303  *
304  * <pre>
305  * Notes:
306  *      (1) If pixs does not have a colormap, a clone is returned.
307  *      (2) Otherwise, the input pixs is restricted to 1, 2, 4 or 8 bpp.
308  *      (3) Use REMOVE_CMAP_TO_BINARY only on 1 bpp pix.
309  *      (4) For grayscale conversion from RGB, use a weighted average
310  *          of RGB values, and always return an 8 bpp pix, regardless
311  *          of whether the input pixs depth is 2, 4 or 8 bpp.
312  *      (5) REMOVE_CMAP_TO_FULL_COLOR ignores the alpha component and
313  *          returns a 32 bpp pix with spp == 3 and the alpha bytes are 0.
314  *      (6) For REMOVE_CMAP_BASED_ON_SRC, if there is no color, this
315  *          returns either a 1 bpp or 8 bpp grayscale pix.
316  *          If there is color, this returns a 32 bpp pix, with either:
317  *           * 3 spp, if the alpha values are all 255 (opaque), or
318  *           * 4 spp (preserving the alpha), if any alpha values are not 255.
319  * </pre>
320  */
321 PIX *
pixRemoveColormap(PIX * pixs,l_int32 type)322 pixRemoveColormap(PIX     *pixs,
323                   l_int32  type)
324 {
325 l_int32    sval, rval, gval, bval, val0, val1;
326 l_int32    i, j, k, w, h, d, wpls, wpld, ncolors, count;
327 l_int32    opaque, colorfound, blackwhite;
328 l_int32   *rmap, *gmap, *bmap, *amap, *graymap;
329 l_uint32  *datas, *lines, *datad, *lined, *lut;
330 l_uint32   sword, dword;
331 PIXCMAP   *cmap;
332 PIX       *pixd;
333 
334     PROCNAME("pixRemoveColormap");
335 
336     if (!pixs)
337         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
338     if ((cmap = pixGetColormap(pixs)) == NULL)
339         return pixClone(pixs);
340 
341     if (type != REMOVE_CMAP_TO_BINARY &&
342         type != REMOVE_CMAP_TO_GRAYSCALE &&
343         type != REMOVE_CMAP_TO_FULL_COLOR &&
344         type != REMOVE_CMAP_WITH_ALPHA &&
345         type != REMOVE_CMAP_BASED_ON_SRC) {
346         L_WARNING("Invalid type; converting based on src\n", procName);
347         type = REMOVE_CMAP_BASED_ON_SRC;
348     }
349 
350     pixGetDimensions(pixs, &w, &h, &d);
351     if (d != 1 && d != 2 && d != 4 && d != 8)
352         return (PIX *)ERROR_PTR("pixs must be {1,2,4,8} bpp", procName, NULL);
353 
354     if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap))
355         return (PIX *)ERROR_PTR("colormap arrays not made", procName, NULL);
356 
357     if (d != 1 && type == REMOVE_CMAP_TO_BINARY) {
358         L_WARNING("not 1 bpp; can't remove cmap to binary\n", procName);
359         type = REMOVE_CMAP_BASED_ON_SRC;
360     }
361 
362         /* Select output type depending on colormap content */
363     if (type == REMOVE_CMAP_BASED_ON_SRC) {
364         pixcmapIsOpaque(cmap, &opaque);
365         pixcmapHasColor(cmap, &colorfound);
366         pixcmapIsBlackAndWhite(cmap, &blackwhite);
367         if (!opaque) {  /* save the alpha */
368             type = REMOVE_CMAP_WITH_ALPHA;
369         } else if (colorfound) {
370             type = REMOVE_CMAP_TO_FULL_COLOR;
371         } else {  /* opaque and no color */
372             if (d == 1 && blackwhite)  /* can binarize without loss */
373                 type = REMOVE_CMAP_TO_BINARY;
374             else
375                 type = REMOVE_CMAP_TO_GRAYSCALE;
376         }
377     }
378 
379     ncolors = pixcmapGetCount(cmap);
380     datas = pixGetData(pixs);
381     wpls = pixGetWpl(pixs);
382     if (type == REMOVE_CMAP_TO_BINARY) {
383         if ((pixd = pixCopy(NULL, pixs)) == NULL) {
384             L_ERROR("pixd not made\n", procName);
385             goto cleanup_arrays;
386         }
387         pixcmapGetColor(cmap, 0, &rval, &gval, &bval);
388         val0 = rval + gval + bval;
389         pixcmapGetColor(cmap, 1, &rval, &gval, &bval);
390         val1 = rval + gval + bval;
391         if (val0 < val1)  /* photometrically inverted from standard */
392             pixInvert(pixd, pixd);
393         pixDestroyColormap(pixd);
394     } else if (type == REMOVE_CMAP_TO_GRAYSCALE) {
395         if ((pixd = pixCreate(w, h, 8)) == NULL) {
396             L_ERROR("pixd not made\n", procName);
397             goto cleanup_arrays;
398         }
399         pixCopyResolution(pixd, pixs);
400         pixCopyInputFormat(pixd, pixs);
401         datad = pixGetData(pixd);
402         wpld = pixGetWpl(pixd);
403         graymap = (l_int32 *)LEPT_CALLOC(ncolors, sizeof(l_int32));
404         for (i = 0; i < pixcmapGetCount(cmap); i++) {
405             graymap[i] = (l_int32)(L_RED_WEIGHT * rmap[i] +
406                                    L_GREEN_WEIGHT * gmap[i] +
407                                    L_BLUE_WEIGHT * bmap[i] + 0.5);
408         }
409         for (i = 0; i < h; i++) {
410             lines = datas + i * wpls;
411             lined = datad + i * wpld;
412             switch (d)   /* depth test above; no default permitted */
413             {
414                 case 8:
415                         /* Unrolled 4x */
416                     for (j = 0, count = 0; j + 3 < w; j += 4, count++) {
417                         sword = lines[count];
418                         dword = (graymap[(sword >> 24) & 0xff] << 24) |
419                             (graymap[(sword >> 16) & 0xff] << 16) |
420                             (graymap[(sword >> 8) & 0xff] << 8) |
421                             graymap[sword & 0xff];
422                         lined[count] = dword;
423                     }
424                         /* Cleanup partial word */
425                     for (; j < w; j++) {
426                         sval = GET_DATA_BYTE(lines, j);
427                         gval = graymap[sval];
428                         SET_DATA_BYTE(lined, j, gval);
429                     }
430 #if DEBUG_UNROLLING
431 #define CHECK_VALUE(a, b, c) if (GET_DATA_BYTE(a, b) != c) { \
432     fprintf(stderr, "Error: mismatch at %d, %d vs %d\n", \
433             j, GET_DATA_BYTE(a, b), c); }
434                     for (j = 0; j < w; j++) {
435                         sval = GET_DATA_BYTE(lines, j);
436                         gval = graymap[sval];
437                         CHECK_VALUE(lined, j, gval);
438                     }
439 #endif
440                     break;
441                 case 4:
442                         /* Unrolled 8x */
443                     for (j = 0, count = 0; j + 7 < w; j += 8, count++) {
444                         sword = lines[count];
445                         dword = (graymap[(sword >> 28) & 0xf] << 24) |
446                             (graymap[(sword >> 24) & 0xf] << 16) |
447                             (graymap[(sword >> 20) & 0xf] << 8) |
448                             graymap[(sword >> 16) & 0xf];
449                         lined[2 * count] = dword;
450                         dword = (graymap[(sword >> 12) & 0xf] << 24) |
451                             (graymap[(sword >> 8) & 0xf] << 16) |
452                             (graymap[(sword >> 4) & 0xf] << 8) |
453                             graymap[sword & 0xf];
454                         lined[2 * count + 1] = dword;
455                     }
456                         /* Cleanup partial word */
457                     for (; j < w; j++) {
458                         sval = GET_DATA_QBIT(lines, j);
459                         gval = graymap[sval];
460                         SET_DATA_BYTE(lined, j, gval);
461                     }
462 #if DEBUG_UNROLLING
463                     for (j = 0; j < w; j++) {
464                         sval = GET_DATA_QBIT(lines, j);
465                         gval = graymap[sval];
466                         CHECK_VALUE(lined, j, gval);
467                     }
468 #endif
469                     break;
470                 case 2:
471                         /* Unrolled 16x */
472                     for (j = 0, count = 0; j + 15 < w; j += 16, count++) {
473                         sword = lines[count];
474                         dword = (graymap[(sword >> 30) & 0x3] << 24) |
475                             (graymap[(sword >> 28) & 0x3] << 16) |
476                             (graymap[(sword >> 26) & 0x3] << 8) |
477                             graymap[(sword >> 24) & 0x3];
478                         lined[4 * count] = dword;
479                         dword = (graymap[(sword >> 22) & 0x3] << 24) |
480                             (graymap[(sword >> 20) & 0x3] << 16) |
481                             (graymap[(sword >> 18) & 0x3] << 8) |
482                             graymap[(sword >> 16) & 0x3];
483                         lined[4 * count + 1] = dword;
484                         dword = (graymap[(sword >> 14) & 0x3] << 24) |
485                             (graymap[(sword >> 12) & 0x3] << 16) |
486                             (graymap[(sword >> 10) & 0x3] << 8) |
487                             graymap[(sword >> 8) & 0x3];
488                         lined[4 * count + 2] = dword;
489                         dword = (graymap[(sword >> 6) & 0x3] << 24) |
490                             (graymap[(sword >> 4) & 0x3] << 16) |
491                             (graymap[(sword >> 2) & 0x3] << 8) |
492                             graymap[sword & 0x3];
493                         lined[4 * count + 3] = dword;
494                     }
495                         /* Cleanup partial word */
496                     for (; j < w; j++) {
497                         sval = GET_DATA_DIBIT(lines, j);
498                         gval = graymap[sval];
499                         SET_DATA_BYTE(lined, j, gval);
500                     }
501 #if DEBUG_UNROLLING
502                     for (j = 0; j < w; j++) {
503                         sval = GET_DATA_DIBIT(lines, j);
504                         gval = graymap[sval];
505                         CHECK_VALUE(lined, j, gval);
506                     }
507 #endif
508                     break;
509                 case 1:
510                         /* Unrolled 8x */
511                     for (j = 0, count = 0; j + 31 < w; j += 32, count++) {
512                         sword = lines[count];
513                         for (k = 0; k < 4; k++) {
514                                 /* The top byte is always the relevant one */
515                             dword = (graymap[(sword >> 31) & 0x1] << 24) |
516                                 (graymap[(sword >> 30) & 0x1] << 16) |
517                                 (graymap[(sword >> 29) & 0x1] << 8) |
518                                 graymap[(sword >> 28) & 0x1];
519                             lined[8 * count + 2 * k] = dword;
520                             dword = (graymap[(sword >> 27) & 0x1] << 24) |
521                                 (graymap[(sword >> 26) & 0x1] << 16) |
522                                 (graymap[(sword >> 25) & 0x1] << 8) |
523                                 graymap[(sword >> 24) & 0x1];
524                             lined[8 * count + 2 * k + 1] = dword;
525                             sword <<= 8;  /* Move up the next byte */
526                         }
527                     }
528                         /* Cleanup partial word */
529                     for (; j < w; j++) {
530                         sval = GET_DATA_BIT(lines, j);
531                         gval = graymap[sval];
532                         SET_DATA_BYTE(lined, j, gval);
533                     }
534 #if DEBUG_UNROLLING
535                     for (j = 0; j < w; j++) {
536                         sval = GET_DATA_BIT(lines, j);
537                         gval = graymap[sval];
538                         CHECK_VALUE(lined, j, gval);
539                     }
540 #undef CHECK_VALUE
541 #endif
542                     break;
543                 default:
544                     return NULL;
545             }
546         }
547         if (graymap)
548             LEPT_FREE(graymap);
549     } else {  /* type == REMOVE_CMAP_TO_FULL_COLOR or REMOVE_CMAP_WITH_ALPHA */
550         if ((pixd = pixCreate(w, h, 32)) == NULL) {
551             L_ERROR("pixd not made\n", procName);
552             goto cleanup_arrays;
553         }
554         pixCopyInputFormat(pixd, pixs);
555         pixCopyResolution(pixd, pixs);
556         if (type == REMOVE_CMAP_WITH_ALPHA)
557             pixSetSpp(pixd, 4);
558         datad = pixGetData(pixd);
559         wpld = pixGetWpl(pixd);
560         lut = (l_uint32 *)LEPT_CALLOC(ncolors, sizeof(l_uint32));
561         for (i = 0; i < ncolors; i++) {
562             if (type == REMOVE_CMAP_TO_FULL_COLOR)
563                 composeRGBPixel(rmap[i], gmap[i], bmap[i], lut + i);
564             else  /* full color plus alpha */
565                 composeRGBAPixel(rmap[i], gmap[i], bmap[i], amap[i], lut + i);
566         }
567 
568         for (i = 0; i < h; i++) {
569             lines = datas + i * wpls;
570             lined = datad + i * wpld;
571             for (j = 0; j < w; j++) {
572                 if (d == 8)
573                     sval = GET_DATA_BYTE(lines, j);
574                 else if (d == 4)
575                     sval = GET_DATA_QBIT(lines, j);
576                 else if (d == 2)
577                     sval = GET_DATA_DIBIT(lines, j);
578                 else  /* (d == 1) */
579                     sval = GET_DATA_BIT(lines, j);
580                 if (sval >= ncolors)
581                     L_WARNING("pixel value out of bounds\n", procName);
582                 else
583                     lined[j] = lut[sval];
584             }
585         }
586         LEPT_FREE(lut);
587     }
588 
589 cleanup_arrays:
590     LEPT_FREE(rmap);
591     LEPT_FREE(gmap);
592     LEPT_FREE(bmap);
593     LEPT_FREE(amap);
594     return pixd;
595 }
596 
597 
598 /*-------------------------------------------------------------*
599  *              Add colormap losslessly (8 to 8)               *
600  *-------------------------------------------------------------*/
601 /*!
602  * \brief   pixAddGrayColormap8()
603  *
604  * \param[in]    pixs   8 bpp
605  * \return  0 if OK, 1 on error
606  *
607  * <pre>
608  * Notes:
609  *      (1) If pixs has a colormap, this is a no-op.
610  * </pre>
611  */
612 l_int32
pixAddGrayColormap8(PIX * pixs)613 pixAddGrayColormap8(PIX  *pixs)
614 {
615 PIXCMAP  *cmap;
616 
617     PROCNAME("pixAddGrayColormap8");
618 
619     if (!pixs || pixGetDepth(pixs) != 8)
620         return ERROR_INT("pixs not defined or not 8 bpp", procName, 1);
621     if (pixGetColormap(pixs))
622         return 0;
623 
624     cmap = pixcmapCreateLinear(8, 256);
625     pixSetColormap(pixs, cmap);
626     return 0;
627 }
628 
629 
630 /*!
631  * \brief   pixAddMinimalGrayColormap8()
632  *
633  * \param[in]    pixs   8 bpp
634  * \return  0 if OK, 1 on error
635  *
636  * <pre>
637  * Notes:
638  *      (1) This generates a colormapped version of the input image
639  *          that has the same number of colormap entries as the
640  *          input image has unique gray levels.
641  * </pre>
642  */
643 PIX *
pixAddMinimalGrayColormap8(PIX * pixs)644 pixAddMinimalGrayColormap8(PIX  *pixs)
645 {
646 l_int32    ncolors, w, h, i, j, wpl1, wpld, index, val;
647 l_int32   *inta, *revmap;
648 l_uint32  *data1, *datad, *line1, *lined;
649 PIX       *pix1, *pixd;
650 PIXCMAP   *cmap;
651 
652     PROCNAME("pixAddMinimalGrayColormap8");
653 
654     if (!pixs || pixGetDepth(pixs) != 8)
655         return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", procName, NULL);
656 
657         /* Eliminate the easy cases */
658     pixNumColors(pixs, 1, &ncolors);
659     cmap = pixGetColormap(pixs);
660     if (cmap) {
661         if (pixcmapGetCount(cmap) == ncolors)  /* irreducible */
662             return pixCopy(NULL, pixs);
663         else
664             pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
665     } else {
666         if (ncolors == 256) {
667             pix1 = pixCopy(NULL, pixs);
668             pixAddGrayColormap8(pix1);
669             return pix1;
670         }
671         pix1 = pixClone(pixs);
672     }
673 
674         /* Find the gray levels and make a reverse map */
675     pixGetDimensions(pix1, &w, &h, NULL);
676     data1 = pixGetData(pix1);
677     wpl1 = pixGetWpl(pix1);
678     inta = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
679     for (i = 0; i < h; i++) {
680         line1 = data1 + i * wpl1;
681         for (j = 0; j < w; j++) {
682             val = GET_DATA_BYTE(line1, j);
683             inta[val] = 1;
684         }
685     }
686     cmap = pixcmapCreate(8);
687     revmap = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
688     for (i = 0, index = 0; i < 256; i++) {
689         if (inta[i]) {
690             pixcmapAddColor(cmap, i, i, i);
691             revmap[i] = index++;
692         }
693     }
694 
695         /* Set all pixels in pixd to the colormap index */
696     pixd = pixCreateTemplate(pix1);
697     pixSetColormap(pixd, cmap);
698     pixCopyInputFormat(pixd, pixs);
699     pixCopyResolution(pixd, pixs);
700     datad = pixGetData(pixd);
701     wpld = pixGetWpl(pixd);
702     for (i = 0; i < h; i++) {
703         line1 = data1 + i * wpl1;
704         lined = datad + i * wpld;
705         for (j = 0; j < w; j++) {
706             val = GET_DATA_BYTE(line1, j);
707             SET_DATA_BYTE(lined, j, revmap[val]);
708         }
709     }
710 
711     pixDestroy(&pix1);
712     LEPT_FREE(inta);
713     LEPT_FREE(revmap);
714     return pixd;
715 }
716 
717 
718 /*-------------------------------------------------------------*
719  *            Conversion from RGB color to grayscale           *
720  *-------------------------------------------------------------*/
721 /*!
722  * \brief   pixConvertRGBToLuminance()
723  *
724  * \param[in]    pixs   32 bpp RGB
725  * \return  8 bpp pix, or NULL on error
726  *
727  * <pre>
728  * Notes:
729  *      (1) Use a standard luminance conversion.
730  * </pre>
731  */
732 PIX *
pixConvertRGBToLuminance(PIX * pixs)733 pixConvertRGBToLuminance(PIX *pixs)
734 {
735   return pixConvertRGBToGray(pixs, 0.0, 0.0, 0.0);
736 }
737 
738 
739 /*!
740  * \brief   pixConvertRGBToGray()
741  *
742  * \param[in]    pixs           32 bpp RGB
743  * \param[in]    rwt, gwt, bwt  non-negative; these should add to 1.0,
744  *                              or use 0.0 for default
745  * \return  8 bpp pix, or NULL on error
746  *
747  * <pre>
748  * Notes:
749  *      (1) Use a weighted average of the RGB values.
750  * </pre>
751  */
752 PIX *
pixConvertRGBToGray(PIX * pixs,l_float32 rwt,l_float32 gwt,l_float32 bwt)753 pixConvertRGBToGray(PIX       *pixs,
754                     l_float32  rwt,
755                     l_float32  gwt,
756                     l_float32  bwt)
757 {
758 l_int32    i, j, w, h, wpls, wpld, val;
759 l_uint32   word;
760 l_uint32  *datas, *lines, *datad, *lined;
761 l_float32  sum;
762 PIX       *pixd;
763 
764     PROCNAME("pixConvertRGBToGray");
765 
766     if (!pixs)
767         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
768     if (pixGetDepth(pixs) != 32)
769         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
770     if (rwt < 0.0 || gwt < 0.0 || bwt < 0.0)
771         return (PIX *)ERROR_PTR("weights not all >= 0.0", procName, NULL);
772 
773         /* Make sure the sum of weights is 1.0; otherwise, you can get
774          * overflow in the gray value. */
775     if (rwt == 0.0 && gwt == 0.0 && bwt == 0.0) {
776         rwt = L_RED_WEIGHT;
777         gwt = L_GREEN_WEIGHT;
778         bwt = L_BLUE_WEIGHT;
779     }
780     sum = rwt + gwt + bwt;
781     if (L_ABS(sum - 1.0) > 0.0001) {  /* maintain ratios with sum == 1.0 */
782         L_WARNING("weights don't sum to 1; maintaining ratios\n", procName);
783         rwt = rwt / sum;
784         gwt = gwt / sum;
785         bwt = bwt / sum;
786     }
787 
788     pixGetDimensions(pixs, &w, &h, NULL);
789     datas = pixGetData(pixs);
790     wpls = pixGetWpl(pixs);
791     if ((pixd = pixCreate(w, h, 8)) == NULL)
792         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
793     pixCopyResolution(pixd, pixs);
794     pixCopyInputFormat(pixd, pixs);
795     datad = pixGetData(pixd);
796     wpld = pixGetWpl(pixd);
797 
798     for (i = 0; i < h; i++) {
799         lines = datas + i * wpls;
800         lined = datad + i * wpld;
801         for (j = 0; j < w; j++) {
802             word = *(lines + j);
803             val = (l_int32)(rwt * ((word >> L_RED_SHIFT) & 0xff) +
804                             gwt * ((word >> L_GREEN_SHIFT) & 0xff) +
805                             bwt * ((word >> L_BLUE_SHIFT) & 0xff) + 0.5);
806             SET_DATA_BYTE(lined, j, val);
807         }
808     }
809 
810     return pixd;
811 }
812 
813 
814 /*!
815  * \brief   pixConvertRGBToGrayFast()
816  *
817  * \param[in]    pixs    32 bpp RGB
818  * \return  8 bpp pix, or NULL on error
819  *
820  * <pre>
821  * Notes:
822  *      (1) This function should be used if speed of conversion
823  *          is paramount, and the green channel can be used as
824  *          a fair representative of the RGB intensity.  It is
825  *          several times faster than pixConvertRGBToGray().
826  *      (2) To combine RGB to gray conversion with subsampling,
827  *          use pixScaleRGBToGrayFast() instead.
828  * </pre>
829  */
830 PIX *
pixConvertRGBToGrayFast(PIX * pixs)831 pixConvertRGBToGrayFast(PIX  *pixs)
832 {
833 l_int32    i, j, w, h, wpls, wpld, val;
834 l_uint32  *datas, *lines, *datad, *lined;
835 PIX       *pixd;
836 
837     PROCNAME("pixConvertRGBToGrayFast");
838 
839     if (!pixs)
840         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
841     if (pixGetDepth(pixs) != 32)
842         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
843 
844     pixGetDimensions(pixs, &w, &h, NULL);
845     datas = pixGetData(pixs);
846     wpls = pixGetWpl(pixs);
847     if ((pixd = pixCreate(w, h, 8)) == NULL)
848         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
849     pixCopyResolution(pixd, pixs);
850     pixCopyInputFormat(pixd, pixs);
851     datad = pixGetData(pixd);
852     wpld = pixGetWpl(pixd);
853 
854     for (i = 0; i < h; i++) {
855         lines = datas + i * wpls;
856         lined = datad + i * wpld;
857         for (j = 0; j < w; j++, lines++) {
858             val = ((*lines) >> L_GREEN_SHIFT) & 0xff;
859             SET_DATA_BYTE(lined, j, val);
860         }
861     }
862 
863     return pixd;
864 }
865 
866 
867 /*!
868  * \brief   pixConvertRGBToGrayMinMax()
869  *
870  * \param[in]    pixs   32 bpp RGB
871  * \param[in]    type   L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAXDIFF,
872  *                      L_CHOOSE_MIN_BOOST, L_CHOOSE_MAX_BOOST
873  * \return  8 bpp pix, or NULL on error
874  *
875  * <pre>
876  * Notes:
877  *      (1) This chooses various components or combinations of them,
878  *          from the three RGB sample values.  In addition to choosing
879  *          the min, max, and maxdiff (difference between max and min),
880  *          this also allows boosting the min and max about a reference
881  *          value.
882  *      (2) The default reference value for boosting the min and max
883  *          is 200.  This can be changed with l_setNeutralBoostVal()
884  * </pre>
885  */
886 PIX *
pixConvertRGBToGrayMinMax(PIX * pixs,l_int32 type)887 pixConvertRGBToGrayMinMax(PIX     *pixs,
888                           l_int32  type)
889 {
890 l_int32    i, j, w, h, wpls, wpld, rval, gval, bval, val, minval, maxval;
891 l_uint32  *datas, *lines, *datad, *lined;
892 PIX       *pixd;
893 
894     PROCNAME("pixConvertRGBToGrayMinMax");
895 
896     if (!pixs)
897         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
898     if (pixGetDepth(pixs) != 32)
899         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
900     if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX &&
901         type != L_CHOOSE_MAXDIFF && type != L_CHOOSE_MIN_BOOST &&
902         type != L_CHOOSE_MAX_BOOST)
903         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
904 
905     pixGetDimensions(pixs, &w, &h, NULL);
906     datas = pixGetData(pixs);
907     wpls = pixGetWpl(pixs);
908     if ((pixd = pixCreate(w, h, 8)) == NULL)
909         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
910     pixCopyResolution(pixd, pixs);
911     pixCopyInputFormat(pixd, pixs);
912     datad = pixGetData(pixd);
913     wpld = pixGetWpl(pixd);
914 
915     for (i = 0; i < h; i++) {
916         lines = datas + i * wpls;
917         lined = datad + i * wpld;
918         for (j = 0; j < w; j++) {
919             extractRGBValues(lines[j], &rval, &gval, &bval);
920             if (type == L_CHOOSE_MIN || type == L_CHOOSE_MIN_BOOST) {
921                 val = L_MIN(rval, gval);
922                 val = L_MIN(val, bval);
923                 if (type == L_CHOOSE_MIN_BOOST)
924                     val = L_MIN(255, (val * val) / var_NEUTRAL_BOOST_VAL);
925             } else if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAX_BOOST) {
926                 val = L_MAX(rval, gval);
927                 val = L_MAX(val, bval);
928                 if (type == L_CHOOSE_MAX_BOOST)
929                     val = L_MIN(255, (val * val) / var_NEUTRAL_BOOST_VAL);
930             } else {  /* L_CHOOSE_MAXDIFF */
931                 minval = L_MIN(rval, gval);
932                 minval = L_MIN(minval, bval);
933                 maxval = L_MAX(rval, gval);
934                 maxval = L_MAX(maxval, bval);
935                 val = maxval - minval;
936             }
937             SET_DATA_BYTE(lined, j, val);
938         }
939     }
940 
941     return pixd;
942 }
943 
944 
945 /*!
946  * \brief   pixConvertRGBToGraySatBoost()
947  *
948  * \param[in]    pixs    32 bpp rgb
949  * \param[in]    refval  between 1 and 255; typ. less than 128
950  * \return  pixd 8 bpp, or NULL on error
951  *
952  * <pre>
953  * Notes:
954  *      (1) This returns the max component value, boosted by
955  *          the saturation. The maximum boost occurs where
956  *          the maximum component value is equal to some reference value.
957  *          This particular weighting is due to Dany Qumsiyeh.
958  *      (2) For gray pixels (zero saturation), this returns
959  *          the intensity of any component.
960  *      (3) For fully saturated pixels ('fullsat'), this rises linearly
961  *          with the max value and has a slope equal to 255 divided
962  *          by the reference value; for a max value greater than
963  *          the reference value, it is clipped to 255.
964  *      (4) For saturation values in between, the output is a linear
965  *          combination of (2) and (3), weighted by saturation.
966  *          It falls between these two curves, and does not exceed 255.
967  *      (5) This can be useful for distinguishing an object that has nonzero
968  *          saturation from a gray background.  For this, the refval
969  *          should be chosen near the expected value of the background,
970  *          to achieve maximum saturation boost there.
971  * </pre>
972  */
973 PIX  *
pixConvertRGBToGraySatBoost(PIX * pixs,l_int32 refval)974 pixConvertRGBToGraySatBoost(PIX     *pixs,
975                             l_int32  refval)
976 {
977 l_int32     w, h, d, i, j, wplt, wpld;
978 l_int32     rval, gval, bval, sval, minrg, maxrg, min, max, delta;
979 l_int32     fullsat, newval;
980 l_float32  *invmax, *ratio;
981 l_uint32   *linet, *lined, *datat, *datad;
982 PIX        *pixt, *pixd;
983 
984     PROCNAME("pixConvertRGBToGraySatBoost");
985 
986     if (!pixs)
987         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
988     pixGetDimensions(pixs, &w, &h, &d);
989     if (d != 32 && !pixGetColormap(pixs))
990         return (PIX *)ERROR_PTR("pixs not cmapped or rgb", procName, NULL);
991     if (refval < 1 || refval > 255)
992         return (PIX *)ERROR_PTR("refval not in [1 ... 255]", procName, NULL);
993 
994     pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
995     pixd = pixCreate(w, h, 8);
996     pixCopyResolution(pixd, pixs);
997     pixCopyInputFormat(pixd, pixs);
998     wplt = pixGetWpl(pixt);
999     datat = pixGetData(pixt);
1000     wpld = pixGetWpl(pixd);
1001     datad = pixGetData(pixd);
1002     invmax = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32));
1003     ratio = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32));
1004     for (i = 1; i < 256; i++) {  /* i == 0  --> delta = sval = newval = 0 */
1005         invmax[i] = 1.0 / (l_float32)i;
1006         ratio[i] = (l_float32)i / (l_float32)refval;
1007     }
1008     for (i = 0; i < h; i++) {
1009         linet = datat + i * wplt;
1010         lined = datad + i * wpld;
1011         for (j = 0; j < w; j++) {
1012             extractRGBValues(linet[j], &rval, &gval, &bval);
1013             minrg = L_MIN(rval, gval);
1014             min = L_MIN(minrg, bval);
1015             maxrg = L_MAX(rval, gval);
1016             max = L_MAX(maxrg, bval);
1017             delta = max - min;
1018             if (delta == 0)  /* gray; no chroma */
1019                 sval = 0;
1020             else
1021                 sval = (l_int32)(255. * (l_float32)delta * invmax[max] + 0.5);
1022 
1023             fullsat = L_MIN(255, 255 * ratio[max]);
1024             newval = (sval * fullsat + (255 - sval) * max) / 255;
1025             SET_DATA_BYTE(lined, j, newval);
1026         }
1027     }
1028 
1029     pixDestroy(&pixt);
1030     LEPT_FREE(invmax);
1031     LEPT_FREE(ratio);
1032     return pixd;
1033 }
1034 
1035 
1036 /*!
1037  * \brief   pixConvertRGBToGrayArb()
1038  *
1039  * \param[in]    pixs        32 bpp RGB
1040  * \param[in]    rc, gc, bc  arithmetic factors; can be negative
1041  * \return  8 bpp pix, or NULL on error
1042  *
1043  * <pre>
1044  * Notes:
1045  *      (1) This converts to gray using an arbitrary linear combination
1046  *          of the rgb color components.  It differs from pixConvertToGray(),
1047  *          which uses only positive coefficients that sum to 1.
1048  *      (2) The gray output values are clipped to 0 and 255.
1049  * </pre>
1050  */
1051 PIX *
pixConvertRGBToGrayArb(PIX * pixs,l_float32 rc,l_float32 gc,l_float32 bc)1052 pixConvertRGBToGrayArb(PIX       *pixs,
1053                        l_float32  rc,
1054                        l_float32  gc,
1055                        l_float32  bc)
1056 {
1057 l_int32    i, j, w, h, wpls, wpld, rval, gval, bval, val;
1058 l_uint32  *datas, *lines, *datad, *lined;
1059 PIX       *pixd;
1060 
1061     PROCNAME("pixConvertRGBToGrayArb");
1062 
1063     if (!pixs)
1064         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1065     if (pixGetDepth(pixs) != 32)
1066         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
1067     if (rc <= 0 && gc <= 0 && bc <= 0)
1068         return (PIX *)ERROR_PTR("all coefficients <= 0", procName, NULL);
1069 
1070     pixGetDimensions(pixs, &w, &h, NULL);
1071     datas = pixGetData(pixs);
1072     wpls = pixGetWpl(pixs);
1073     if ((pixd = pixCreate(w, h, 8)) == NULL)
1074         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1075     pixCopyResolution(pixd, pixs);
1076     pixCopyInputFormat(pixd, pixs);
1077     datad = pixGetData(pixd);
1078     wpld = pixGetWpl(pixd);
1079 
1080     for (i = 0; i < h; i++) {
1081         lines = datas + i * wpls;
1082         lined = datad + i * wpld;
1083         for (j = 0; j < w; j++) {
1084             extractRGBValues(lines[j], &rval, &gval, &bval);
1085             val = (l_int32)(rc * rval + gc * gval + bc * bval);
1086             val = L_MIN(255, L_MAX(0, val));
1087             SET_DATA_BYTE(lined, j, val);
1088         }
1089     }
1090 
1091     return pixd;
1092 }
1093 
1094 
1095 /*!
1096  * \brief   pixConvertRGBToBinaryArb()
1097  *
1098  * \param[in]    pixs        32 bpp RGB
1099  * \param[in]    rc, gc, bc  arithmetic factors; can be negative
1100  * \param[in]    thresh      binarization threshold
1101  * \param[in]    relation    L_SELECT_IF_LT, L_SELECT_IF_GT
1102  *                           L_SELECT_IF_LTE, L_SELECT_IF_GTE
1103  * \return  1 bpp pix, or NULL on error
1104  *
1105  * <pre>
1106  * Notes:
1107  *      (1) This makes a 1 bpp mask from an RGB image, using an arbitrary
1108  *          linear combination of the rgb color components, along with
1109  *          a threshold and a selection choice of the gray value relative
1110  *          to %thresh.
1111  * </pre>
1112  */
1113 PIX *
pixConvertRGBToBinaryArb(PIX * pixs,l_float32 rc,l_float32 gc,l_float32 bc,l_int32 thresh,l_int32 relation)1114 pixConvertRGBToBinaryArb(PIX       *pixs,
1115                          l_float32  rc,
1116                          l_float32  gc,
1117                          l_float32  bc,
1118                          l_int32    thresh,
1119                          l_int32    relation)
1120 {
1121 l_int32  threshold;
1122 PIX     *pix1, *pix2;
1123 
1124     PROCNAME("pixConvertRGBToBinaryArb");
1125 
1126     if (!pixs || pixGetDepth(pixs) != 32)
1127         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
1128     if (rc <= 0 && gc <= 0 && bc <= 0)
1129         return (PIX *)ERROR_PTR("all coefficients <= 0", procName, NULL);
1130     if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT &&
1131         relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE)
1132         return (PIX *)ERROR_PTR("invalid relation", procName, NULL);
1133 
1134     pix1 = pixConvertRGBToGrayArb(pixs, rc, gc, bc);
1135     threshold = (relation == L_SELECT_IF_LTE || relation == L_SELECT_IF_GT) ?
1136                              thresh : thresh + 1;
1137     pix2 = pixThresholdToBinary(pix1, threshold);
1138     if (relation == L_SELECT_IF_GT || relation == L_SELECT_IF_GTE)
1139         pixInvert(pix2, pix2);
1140     pixDestroy(&pix1);
1141     return pix2;
1142 }
1143 
1144 
1145 /*---------------------------------------------------------------------------*
1146  *                  Conversion from grayscale to colormap                    *
1147  *---------------------------------------------------------------------------*/
1148 /*!
1149  * \brief   pixConvertGrayToColormap()
1150  *
1151  * \param[in]    pixs    2, 4 or 8 bpp grayscale
1152  * \return  pixd 2, 4 or 8 bpp with colormap, or NULL on error
1153  *
1154  * <pre>
1155  * Notes:
1156  *      (1) This is a simple interface for adding a colormap to a
1157  *          2, 4 or 8 bpp grayscale image without causing any
1158  *          quantization.  There is some similarity to operations
1159  *          in grayquant.c, such as pixThresholdOn8bpp(), where
1160  *          the emphasis is on quantization with an arbitrary number
1161  *          of levels, and a colormap is an option.
1162  *      (2) Returns a copy if pixs already has a colormap.
1163  *      (3) For 8 bpp src, this is a lossless transformation.
1164  *      (4) For 2 and 4 bpp src, this generates a colormap that
1165  *          assumes full coverage of the gray space, with equally spaced
1166  *          levels: 4 levels for d = 2 and 16 levels for d = 4.
1167  *      (5) In all cases, the depth of the dest is the same as the src.
1168  * </pre>
1169  */
1170 PIX *
pixConvertGrayToColormap(PIX * pixs)1171 pixConvertGrayToColormap(PIX  *pixs)
1172 {
1173 l_int32    d;
1174 PIX       *pixd;
1175 PIXCMAP   *cmap;
1176 
1177     PROCNAME("pixConvertGrayToColormap");
1178 
1179     if (!pixs)
1180         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1181     d = pixGetDepth(pixs);
1182     if (d != 2 && d != 4 && d != 8)
1183         return (PIX *)ERROR_PTR("pixs not 2, 4 or 8 bpp", procName, NULL);
1184 
1185     if (pixGetColormap(pixs)) {
1186         L_INFO("pixs already has a colormap\n", procName);
1187         return pixCopy(NULL, pixs);
1188     }
1189 
1190     if (d == 8)  /* lossless conversion */
1191         return pixConvertGrayToColormap8(pixs, 2);
1192 
1193         /* Build a cmap with equally spaced target values over the
1194          * full 8 bpp range. */
1195     pixd = pixCopy(NULL, pixs);
1196     cmap = pixcmapCreateLinear(d, 1 << d);
1197     pixSetColormap(pixd, cmap);
1198     pixCopyInputFormat(pixd, pixs);
1199     return pixd;
1200 }
1201 
1202 
1203 /*!
1204  * \brief   pixConvertGrayToColormap8()
1205  *
1206  * \param[in]    pixs       8 bpp grayscale
1207  * \param[in]    mindepth   of pixd; valid values are 2, 4 and 8
1208  * \return  pixd 2, 4 or 8 bpp with colormap, or NULL on error
1209  *
1210  * <pre>
1211  * Notes:
1212  *      (1) Returns a copy if pixs already has a colormap.
1213  *      (2) This is a lossless transformation; there is no quantization.
1214  *          We compute the number of different gray values in pixs,
1215  *          and construct a colormap that has exactly these values.
1216  *      (3) 'mindepth' is the minimum depth of pixd.  If mindepth == 8,
1217  *          pixd will always be 8 bpp.  Let the number of different
1218  *          gray values in pixs be ngray.  If mindepth == 4, we attempt
1219  *          to save pixd as a 4 bpp image, but if ngray > 16,
1220  *          pixd must be 8 bpp.  Likewise, if mindepth == 2,
1221  *          the depth of pixd will be 2 if ngray <= 4 and 4 if ngray > 4
1222  *          but <= 16.
1223  * </pre>
1224  */
1225 PIX *
pixConvertGrayToColormap8(PIX * pixs,l_int32 mindepth)1226 pixConvertGrayToColormap8(PIX     *pixs,
1227                           l_int32  mindepth)
1228 {
1229 l_int32    ncolors, w, h, depth, i, j, wpls, wpld;
1230 l_int32    index, num, val, newval;
1231 l_int32    array[256];
1232 l_uint32  *lines, *lined, *datas, *datad;
1233 NUMA      *na;
1234 PIX       *pixd;
1235 PIXCMAP   *cmap;
1236 
1237     PROCNAME("pixConvertGrayToColormap8");
1238 
1239     if (!pixs)
1240         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1241     if (pixGetDepth(pixs) != 8)
1242         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
1243     if (mindepth != 2 && mindepth != 4 && mindepth != 8) {
1244         L_WARNING("invalid value of mindepth; setting to 8\n", procName);
1245         mindepth = 8;
1246     }
1247 
1248     if (pixGetColormap(pixs)) {
1249         L_INFO("pixs already has a colormap\n", procName);
1250         return pixCopy(NULL, pixs);
1251     }
1252 
1253     na = pixGetGrayHistogram(pixs, 1);
1254     numaGetCountRelativeToZero(na, L_GREATER_THAN_ZERO, &ncolors);
1255     if (mindepth == 8 || ncolors > 16)
1256         depth = 8;
1257     else if (mindepth == 4 || ncolors > 4)
1258         depth = 4;
1259     else
1260         depth = 2;
1261 
1262     pixGetDimensions(pixs, &w, &h, NULL);
1263     pixd = pixCreate(w, h, depth);
1264     cmap = pixcmapCreate(depth);
1265     pixSetColormap(pixd, cmap);
1266     pixCopyInputFormat(pixd, pixs);
1267     pixCopyResolution(pixd, pixs);
1268 
1269     index = 0;
1270     for (i = 0; i < 256; i++) {
1271         array[i] = 0;  /* only to quiet the static checker */
1272         numaGetIValue(na, i, &num);
1273         if (num > 0) {
1274             pixcmapAddColor(cmap, i, i, i);
1275             array[i] = index;
1276             index++;
1277         }
1278     }
1279 
1280     datas = pixGetData(pixs);
1281     wpls = pixGetWpl(pixs);
1282     datad = pixGetData(pixd);
1283     wpld = pixGetWpl(pixd);
1284     for (i = 0; i < h; i++) {
1285         lines = datas + i * wpls;
1286         lined = datad + i * wpld;
1287         for (j = 0; j < w; j++) {
1288             val = GET_DATA_BYTE(lines, j);
1289             newval = array[val];
1290             if (depth == 2)
1291                 SET_DATA_DIBIT(lined, j, newval);
1292             else if (depth == 4)
1293                 SET_DATA_QBIT(lined, j, newval);
1294             else  /* depth == 8 */
1295                 SET_DATA_BYTE(lined, j, newval);
1296         }
1297     }
1298 
1299     numaDestroy(&na);
1300     return pixd;
1301 }
1302 
1303 
1304 /*---------------------------------------------------------------------------*
1305  *                Colorizing conversion from grayscale to color              *
1306  *---------------------------------------------------------------------------*/
1307 /*!
1308  * \brief   pixColorizeGray()
1309  *
1310  * \param[in]    pixs      8 bpp gray; 2, 4 or 8 bpp colormapped
1311  * \param[in]    color     32 bit rgba pixel
1312  * \param[in]    cmapflag  1 for result to have colormap; 0 for RGB
1313  * \return  pixd 8 bpp colormapped or 32 bpp rgb, or NULL on error
1314  *
1315  * <pre>
1316  * Notes:
1317  *      (1) This applies the specific color to the grayscale image.
1318  *      (2) If pixs already has a colormap, it is removed to gray
1319  *          before colorizing.
1320  * </pre>
1321  */
1322 PIX *
pixColorizeGray(PIX * pixs,l_uint32 color,l_int32 cmapflag)1323 pixColorizeGray(PIX      *pixs,
1324                 l_uint32  color,
1325                 l_int32   cmapflag)
1326 {
1327 l_int32    i, j, w, h, wplt, wpld, val8;
1328 l_uint32  *datad, *datat, *lined, *linet, *tab;
1329 PIX       *pixt, *pixd;
1330 PIXCMAP   *cmap;
1331 
1332     PROCNAME("pixColorizeGray");
1333 
1334     if (!pixs)
1335         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1336     if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs))
1337         return (PIX *)ERROR_PTR("pixs not 8 bpp or cmapped", procName, NULL);
1338 
1339     if (pixGetColormap(pixs))
1340         pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1341     else
1342         pixt = pixClone(pixs);
1343 
1344     cmap = pixcmapGrayToColor(color);
1345     if (cmapflag) {
1346         pixd = pixCopy(NULL, pixt);
1347         pixSetColormap(pixd, cmap);
1348         pixDestroy(&pixt);
1349         return pixd;
1350     }
1351 
1352         /* Make an RGB pix */
1353     pixcmapToRGBTable(cmap, &tab, NULL);
1354     pixGetDimensions(pixt, &w, &h, NULL);
1355     pixd = pixCreate(w, h, 32);
1356     pixCopyResolution(pixd, pixs);
1357     pixCopyInputFormat(pixd, pixs);
1358     datad = pixGetData(pixd);
1359     wpld = pixGetWpl(pixd);
1360     datat = pixGetData(pixt);
1361     wplt = pixGetWpl(pixt);
1362     for (i = 0; i < h; i++) {
1363         lined = datad + i * wpld;
1364         linet = datat + i * wplt;
1365         for (j = 0; j < w; j++) {
1366             val8 = GET_DATA_BYTE(linet, j);
1367             lined[j] = tab[val8];
1368         }
1369     }
1370 
1371     pixDestroy(&pixt);
1372     pixcmapDestroy(&cmap);
1373     LEPT_FREE(tab);
1374     return pixd;
1375 }
1376 
1377 
1378 /*---------------------------------------------------------------------------*
1379  *                    Conversion from RGB color to colormap                  *
1380  *---------------------------------------------------------------------------*/
1381 /*!
1382  * \brief   pixConvertRGBToColormap()
1383  *
1384  * \param[in]    pixs       32 bpp rgb
1385  * \param[in]    ditherflag  1 to dither, 0 otherwise
1386  * \return  pixd 2, 4 or 8 bpp with colormap, or NULL on error
1387  *
1388  * <pre>
1389  * Notes:
1390  *      (1) This function has two relatively simple modes of color
1391  *          quantization:
1392  *            (a) If the image is made orthographically and has not more
1393  *                than 256 'colors' at the level 4 octcube leaves,
1394  *                it is quantized nearly exactly.  The ditherflag
1395  *                is ignored.
1396  *            (b) Most natural images have more than 256 different colors;
1397  *                in that case we use adaptive octree quantization,
1398  *                with dithering if requested.
1399  *      (2) If there are not more than 256 occupied level 4 octcubes,
1400  *          the color in the colormap that represents all pixels in
1401  *          one of those octcubes is given by the first pixel that
1402  *          falls into that octcube.
1403  *      (3) If there are more than 256 colors, we use adaptive octree
1404  *          color quantization.
1405  *      (4) Dithering gives better visual results on images where
1406  *          there is a color wash (a slow variation of color), but it
1407  *          is about twice as slow and results in significantly larger
1408  *          files when losslessly compressed (e.g., into png).
1409  * </pre>
1410  */
1411 PIX *
pixConvertRGBToColormap(PIX * pixs,l_int32 ditherflag)1412 pixConvertRGBToColormap(PIX     *pixs,
1413                         l_int32  ditherflag)
1414 {
1415 l_int32  ncolors;
1416 NUMA    *na;
1417 PIX     *pixd;
1418 
1419     PROCNAME("pixConvertRGBToColormap");
1420 
1421     if (!pixs)
1422         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1423     if (pixGetDepth(pixs) != 32)
1424         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
1425     if (pixGetSpp(pixs) == 4)
1426         L_WARNING("pixs has alpha; removing\n", procName);
1427 
1428         /* Get the histogram and count the number of occupied level 4
1429          * leaf octcubes.  We don't yet know if this is the number of
1430          * actual colors, but if it's not, all pixels falling into
1431          * the same leaf octcube will be assigned to the color of the
1432          * first pixel that lands there. */
1433     na = pixOctcubeHistogram(pixs, 4, &ncolors);
1434 
1435         /* If there are too many occupied leaf octcubes to be
1436          * represented directly in a colormap, fall back to octree
1437          * quantization, optionally with dithering. */
1438     if (ncolors > 256) {
1439         numaDestroy(&na);
1440         if (ditherflag)
1441             L_INFO("More than 256 colors; using octree quant with dithering\n",
1442                    procName);
1443         else
1444             L_INFO("More than 256 colors; using octree quant; no dithering\n",
1445                    procName);
1446         return pixOctreeColorQuant(pixs, 240, ditherflag);
1447     }
1448 
1449         /* There are not more than 256 occupied leaf octcubes.
1450          * Quantize to those octcubes. */
1451     pixd = pixFewColorsOctcubeQuant2(pixs, 4, na, ncolors, NULL);
1452     pixCopyInputFormat(pixd, pixs);
1453     numaDestroy(&na);
1454     return pixd;
1455 }
1456 
1457 
1458 /*---------------------------------------------------------------------------*
1459  *                     Conversion from colormap to 1 bpp                     *
1460  *---------------------------------------------------------------------------*/
1461 /*!
1462  * \brief   pixConvertCmapTo1()
1463  *
1464  * \param[in]    pixs   cmapped
1465  * \return  pixd 1 bpp, or NULL on error
1466  *
1467  * <pre>
1468  * Notes:
1469  *      (1) This is an extreme color quantizer.  It decides which
1470  *          colors map to FG (black) and which to BG (white).
1471  *      (2) This uses two heuristics to make the decision:
1472  *          (a) colors similar to each other are likely to be in the same class
1473  *          (b) there is usually much less FG than BG.
1474  * </pre>
1475  */
1476 PIX *
pixConvertCmapTo1(PIX * pixs)1477 pixConvertCmapTo1(PIX  *pixs)
1478 {
1479 l_int32    i, j, nc, w, h, imin, imax, factor, wpl1, wpld;
1480 l_int32    index, rmin, gmin, bmin, rmax, gmax, bmax, dmin, dmax;
1481 l_float32  minfract, ifract;
1482 l_int32   *lut;
1483 l_uint32  *line1, *lined, *data1, *datad;
1484 NUMA      *na1, *na2;  /* histograms */
1485 PIX       *pix1, *pixd;
1486 PIXCMAP   *cmap;
1487 
1488     PROCNAME("pixConvertCmapTo1");
1489 
1490     if (!pixs)
1491         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1492     if ((cmap = pixGetColormap(pixs)) == NULL)
1493         return (PIX *)ERROR_PTR("no colormap", procName, NULL);
1494 
1495         /* Select target colors for the two classes.  Find the
1496          * colors with smallest and largest average component values.
1497          * The smallest is class 0 and the largest is class 1. */
1498     pixcmapGetRangeValues(cmap, L_SELECT_AVERAGE, NULL, NULL, &imin, &imax);
1499     pixcmapGetColor(cmap, imin, &rmin, &gmin, &bmin);
1500     pixcmapGetColor(cmap, imax, &rmax, &gmax, &bmax);
1501     nc = pixcmapGetCount(cmap);
1502 
1503         /* Assign colors to the two classes.  The histogram is
1504          * initialized to 0, so any colors not found when computing
1505          * the sampled histogram will get zero weight in minfract. */
1506     if ((lut = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32))) == NULL)
1507         return (PIX *)ERROR_PTR("calloc fail for lut", procName, NULL);
1508     pixGetDimensions(pixs, &w, &h, NULL);
1509     factor = L_MAX(1, (l_int32)sqrt((l_float64)(w * h) / 50000. + 0.5));
1510     na1 = pixGetCmapHistogram(pixs, factor);
1511     na2 = numaNormalizeHistogram(na1, 1.0);
1512     minfract = 0.0;
1513     for (i = 0; i < nc; i++) {
1514         numaGetFValue(na2, i, &ifract);
1515         pixcmapGetDistanceToColor(cmap, i, rmin, gmin, bmin, &dmin);
1516         pixcmapGetDistanceToColor(cmap, i, rmax, gmax, bmax, &dmax);
1517         if (dmin < dmax) {  /* closer to dark extreme value */
1518             lut[i] = 1;  /* black pixel in 1 bpp image */
1519             minfract += ifract;
1520         }
1521     }
1522     numaDestroy(&na1);
1523     numaDestroy(&na2);
1524 
1525         /* Generate the output binarized image */
1526     pix1 = pixConvertTo8(pixs, 1);
1527     pixd = pixCreate(w, h, 1);
1528     data1 = pixGetData(pix1);
1529     datad = pixGetData(pixd);
1530     wpl1 = pixGetWpl(pix1);
1531     wpld = pixGetWpl(pixd);
1532     for (i = 0; i < h; i++) {
1533         line1 = data1 + i * wpl1;
1534         lined = datad + i * wpld;
1535         for (j = 0; j < w; j++) {
1536             index = GET_DATA_BYTE(line1, j);
1537             if (lut[index] == 1) SET_DATA_BIT(lined, j);
1538         }
1539     }
1540     pixDestroy(&pix1);
1541     LEPT_FREE(lut);
1542 
1543         /* We expect minfract (the dark colors) to be less than 0.5.
1544          * If that is not the case, invert pixd. */
1545     if (minfract > 0.5) {
1546         L_INFO("minfract = %5.3f; inverting\n", procName, minfract);
1547         pixInvert(pixd, pixd);
1548     }
1549 
1550     return pixd;
1551 }
1552 
1553 
1554 /*---------------------------------------------------------------------------*
1555  *        Quantization for relatively small number of colors in source       *
1556  *---------------------------------------------------------------------------*/
1557 /*!
1558  * \brief   pixQuantizeIfFewColors()
1559  *
1560  * \param[in]    pixs           8 bpp gray or 32 bpp rgb
1561  * \param[in]    maxcolors      max number of colors allowed to be returned
1562  *                              from pixColorsForQuantization();
1563  *                              use 0 for default
1564  * \param[in]    mingraycolors  min number of gray levels that a grayscale
1565  *                              image is quantized to; use 0 for default
1566  * \param[in]    octlevel       for octcube quantization: 3 or 4
1567  * \param[out]   ppixd          2,4 or 8 bpp quantized; null if too many colors
1568  * \return  0 if OK, 1 on error or if pixs can't be quantized into
1569  *              a small number of colors.
1570  *
1571  * <pre>
1572  * Notes:
1573  *      (1) This is a wrapper that tests if the pix can be quantized
1574  *          with good quality using a small number of colors.  If so,
1575  *          it does the quantization, defining a colormap and using
1576  *          pixels whose value is an index into the colormap.
1577  *      (2) If the image has color, it is quantized with 8 bpp pixels.
1578  *          If the image is essentially grayscale, the pixels are
1579  *          either 4 or 8 bpp, depending on the size of the required
1580  *          colormap.
1581  *      (3) %octlevel = 4 generates a larger colormap and larger
1582  *          compressed image than %octlevel = 3.  If image quality is
1583  *          important, you should use %octlevel = 4.
1584  *      (4) If the image already has a colormap, it returns a clone.
1585  * </pre>
1586  */
1587 l_int32
pixQuantizeIfFewColors(PIX * pixs,l_int32 maxcolors,l_int32 mingraycolors,l_int32 octlevel,PIX ** ppixd)1588 pixQuantizeIfFewColors(PIX     *pixs,
1589                        l_int32  maxcolors,
1590                        l_int32  mingraycolors,
1591                        l_int32  octlevel,
1592                        PIX    **ppixd)
1593 {
1594 l_int32  d, ncolors, iscolor, graycolors;
1595 PIX     *pixg, *pixd;
1596 
1597     PROCNAME("pixQuantizeIfFewColors");
1598 
1599     if (!ppixd)
1600         return ERROR_INT("&pixd not defined", procName, 1);
1601     *ppixd = NULL;
1602     if (!pixs)
1603         return ERROR_INT("pixs not defined", procName, 1);
1604     d = pixGetDepth(pixs);
1605     if (d != 8 && d != 32)
1606         return ERROR_INT("pixs not defined", procName, 1);
1607     if (pixGetColormap(pixs) != NULL) {
1608         *ppixd = pixClone(pixs);
1609         return 0;
1610     }
1611     if (maxcolors <= 0)
1612         maxcolors = 15;  /* default */
1613     if (maxcolors > 50)
1614         L_WARNING("maxcolors > 50; very large!\n", procName);
1615     if (mingraycolors <= 0)
1616         mingraycolors = 10;  /* default */
1617     if (mingraycolors > 30)
1618         L_WARNING("mingraycolors > 30; very large!\n", procName);
1619     if (octlevel != 3 && octlevel != 4) {
1620         L_WARNING("invalid octlevel; setting to 3\n", procName);
1621         octlevel = 3;
1622     }
1623 
1624         /* Test the number of colors.  For color, the octcube leaves
1625          * are at level 4. */
1626     pixColorsForQuantization(pixs, 0, &ncolors, &iscolor, 0);
1627     if (ncolors > maxcolors)
1628         return ERROR_INT("too many colors", procName, 1);
1629 
1630         /* Quantize!
1631          *  (1) For color:
1632          *      If octlevel == 4, try to quantize to an octree where
1633          *      the octcube leaves are at level 4. If that fails,
1634          *      back off to level 3.
1635          *      If octlevel == 3, quantize to level 3 directly.
1636          *      For level 3, the quality is usually good enough and there
1637          *      is negligible chance of getting more than 256 colors.
1638          *  (2) For grayscale, multiply ncolors by 1.5 for extra quality,
1639          *      but use at least mingraycolors and not more than 256. */
1640     if (iscolor) {
1641         pixd = pixFewColorsOctcubeQuant1(pixs, octlevel);
1642         if (!pixd) {  /* backoff */
1643             pixd = pixFewColorsOctcubeQuant1(pixs, octlevel - 1);
1644             if (octlevel == 3)  /* shouldn't happen */
1645                 L_WARNING("quantized at level 2; low quality\n", procName);
1646         }
1647     } else { /* image is really grayscale */
1648         if (d == 32)
1649             pixg = pixConvertRGBToLuminance(pixs);
1650         else
1651             pixg = pixClone(pixs);
1652         graycolors = L_MAX(mingraycolors, (l_int32)(1.5 * ncolors));
1653         graycolors = L_MIN(graycolors, 256);
1654         if (graycolors < 16)
1655             pixd = pixThresholdTo4bpp(pixg, graycolors, 1);
1656         else
1657             pixd = pixThresholdOn8bpp(pixg, graycolors, 1);
1658         pixDestroy(&pixg);
1659     }
1660     *ppixd = pixd;
1661 
1662     if (!pixd)
1663         return ERROR_INT("pixd not made", procName, 1);
1664     pixCopyInputFormat(pixd, pixs);
1665     return 0;
1666 }
1667 
1668 
1669 
1670 /*---------------------------------------------------------------------------*
1671  *                    Conversion from 16 bpp to 8 bpp                        *
1672  *---------------------------------------------------------------------------*/
1673 /*!
1674  * \brief   pixConvert16To8()
1675  *
1676  * \param[in]    pixs     16 bpp
1677  * \param[in]    type     L_LS_BYTE, L_MS_BYTE, L_AUTO_BYTE, L_CLIP_TO_FF
1678  * \return  pixd 8 bpp, or NULL on error
1679  *
1680  * <pre>
1681  * Notes:
1682  *      (1) With L_AUTO_BYTE, if the max pixel value is greater than 255,
1683  *          use the MSB; otherwise, use the LSB.
1684  *      (2) With L_CLIP_TO_FF, use min(pixel-value, 0xff) for each
1685  *          16-bit src pixel.
1686  * </pre>
1687  */
1688 PIX *
pixConvert16To8(PIX * pixs,l_int32 type)1689 pixConvert16To8(PIX     *pixs,
1690                 l_int32  type)
1691 {
1692 l_uint16   dword;
1693 l_int32    w, h, wpls, wpld, i, j, val, use_lsb;
1694 l_uint32   sword, first, second;
1695 l_uint32  *datas, *datad, *lines, *lined;
1696 PIX       *pixd;
1697 
1698     PROCNAME("pixConvert16To8");
1699 
1700     if (!pixs)
1701         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1702     if (pixGetDepth(pixs) != 16)
1703         return (PIX *)ERROR_PTR("pixs not 16 bpp", procName, NULL);
1704     if (type != L_LS_BYTE && type != L_MS_BYTE &&
1705         type != L_AUTO_BYTE && type != L_CLIP_TO_FF)
1706         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
1707 
1708     pixGetDimensions(pixs, &w, &h, NULL);
1709     if ((pixd = pixCreate(w, h, 8)) == NULL)
1710         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1711     pixCopyInputFormat(pixd, pixs);
1712     pixCopyResolution(pixd, pixs);
1713     wpls = pixGetWpl(pixs);
1714     datas = pixGetData(pixs);
1715     wpld = pixGetWpl(pixd);
1716     datad = pixGetData(pixd);
1717 
1718     if (type == L_AUTO_BYTE) {
1719         use_lsb = TRUE;
1720         for (i = 0; i < h; i++) {
1721             lines = datas + i * wpls;
1722             for (j = 0; j < wpls; j++) {
1723                  val = GET_DATA_TWO_BYTES(lines, j);
1724                  if (val > 255) {
1725                      use_lsb = FALSE;
1726                      break;
1727                  }
1728             }
1729             if (!use_lsb) break;
1730         }
1731         type = (use_lsb) ? L_LS_BYTE : L_MS_BYTE;
1732     }
1733 
1734         /* Convert 2 pixels at a time */
1735     for (i = 0; i < h; i++) {
1736         lines = datas + i * wpls;
1737         lined = datad + i * wpld;
1738         if (type == L_LS_BYTE) {
1739             for (j = 0; j < wpls; j++) {
1740                 sword = *(lines + j);
1741                 dword = ((sword >> 8) & 0xff00) | (sword & 0xff);
1742                 SET_DATA_TWO_BYTES(lined, j, dword);
1743             }
1744         } else if (type == L_MS_BYTE) {
1745             for (j = 0; j < wpls; j++) {
1746                 sword = *(lines + j);
1747                 dword = ((sword >> 16) & 0xff00) | ((sword >> 8) & 0xff);
1748                 SET_DATA_TWO_BYTES(lined, j, dword);
1749             }
1750         } else {  /* type == L_CLIP_TO_FF */
1751             for (j = 0; j < wpls; j++) {
1752                 sword = *(lines + j);
1753                 first = (sword >> 24) ? 255 : ((sword >> 16) & 0xff);
1754                 second = ((sword >> 8) & 0xff) ? 255 : (sword & 0xff);
1755                 dword = (first << 8) | second;
1756                 SET_DATA_TWO_BYTES(lined, j, dword);
1757             }
1758         }
1759     }
1760 
1761     return pixd;
1762 }
1763 
1764 
1765 
1766 /*---------------------------------------------------------------------------*
1767  *                Conversion from grayscale to false color
1768  *---------------------------------------------------------------------------*/
1769 /*!
1770  * \brief   pixConvertGrayToFalseColor()
1771  *
1772  * \param[in]    pixs    8 or 16 bpp grayscale
1773  * \param[in]    gamma   (factor) 0.0 or 1.0 for default; > 1.0 for brighter;
1774  *                       2.0 is quite nice
1775  * \return  pixd 8 bpp with colormap, or NULL on error
1776  *
1777  * <pre>
1778  * Notes:
1779  *      (1) For 8 bpp input, this simply adds a colormap to the input image.
1780  *      (2) For 16 bpp input, it first converts to 8 bpp, using the MSB,
1781  *          and then adds the colormap.
1782  *      (3) The colormap is modeled after the Matlab "jet" configuration.
1783  * </pre>
1784  */
1785 PIX *
pixConvertGrayToFalseColor(PIX * pixs,l_float32 gamma)1786 pixConvertGrayToFalseColor(PIX       *pixs,
1787                            l_float32  gamma)
1788 {
1789 l_int32    d, i, rval, bval, gval;
1790 l_int32   *curve;
1791 l_float32  invgamma, x;
1792 PIX       *pixd;
1793 PIXCMAP   *cmap;
1794 
1795     PROCNAME("pixConvertGrayToFalseColor");
1796 
1797     if (!pixs)
1798         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1799     d = pixGetDepth(pixs);
1800     if (d != 8 && d != 16)
1801         return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", procName, NULL);
1802 
1803     if (d == 16) {
1804         pixd = pixConvert16To8(pixs, L_MS_BYTE);
1805     } else {  /* d == 8 */
1806         if (pixGetColormap(pixs))
1807             pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
1808         else
1809             pixd = pixCopy(NULL, pixs);
1810     }
1811     if (!pixd)
1812         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1813     cmap = pixcmapCreate(8);
1814     pixSetColormap(pixd, cmap);
1815     pixCopyResolution(pixd, pixs);
1816     pixCopyInputFormat(pixd, pixs);
1817 
1818         /* Generate curve for transition part of color map */
1819     curve = (l_int32 *)LEPT_CALLOC(64, sizeof(l_int32));
1820     if (gamma == 0.0) gamma = 1.0;
1821     invgamma = 1. / gamma;
1822     for (i = 0; i < 64; i++) {
1823         x = (l_float32)i / 64.;
1824         curve[i] = (l_int32)(255. * powf(x, invgamma) + 0.5);
1825     }
1826 
1827     for (i = 0; i < 256; i++) {
1828         if (i < 32) {
1829             rval = 0;
1830             gval = 0;
1831             bval = curve[i + 32];
1832         } else if (i < 96) {   /* 32 - 95 */
1833             rval = 0;
1834             gval = curve[i - 32];
1835             bval = 255;
1836         } else if (i < 160) {  /* 96 - 159 */
1837             rval = curve[i - 96];
1838             gval = 255;
1839             bval = curve[159 - i];
1840         } else if (i < 224) {  /* 160 - 223 */
1841             rval = 255;
1842             gval = curve[223 - i];
1843             bval = 0;
1844         } else {  /* 224 - 255 */
1845             rval = curve[287 - i];
1846             gval = 0;
1847             bval = 0;
1848         }
1849         pixcmapAddColor(cmap, rval, gval, bval);
1850     }
1851 
1852     LEPT_FREE(curve);
1853     return pixd;
1854 }
1855 
1856 
1857 /*---------------------------------------------------------------------------*
1858  *         Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp         *
1859  *---------------------------------------------------------------------------*/
1860 /*!
1861  * \brief   pixUnpackBinary()
1862  *
1863  * \param[in]    pixs     1 bpp
1864  * \param[in]    depth    of destination: 2, 4, 8, 16 or 32 bpp
1865  * \param[in]    invert   0:  binary 0 --> grayscale 0
1866  *                            binary 1 --> grayscale 0xff...
1867  *                        1:  binary 0 --> grayscale 0xff...
1868  *                            binary 1 --> grayscale 0
1869  * \return  pixd 2, 4, 8, 16 or 32 bpp, or NULL on error
1870  *
1871  * <pre>
1872  * Notes:
1873  *      (1) This function calls special cases of pixConvert1To*(),
1874  *          for 2, 4, 8, 16 and 32 bpp destinations.
1875  * </pre>
1876  */
1877 PIX *
pixUnpackBinary(PIX * pixs,l_int32 depth,l_int32 invert)1878 pixUnpackBinary(PIX     *pixs,
1879                 l_int32  depth,
1880                 l_int32  invert)
1881 {
1882 PIX  *pixd;
1883 
1884     PROCNAME("pixUnpackBinary");
1885 
1886     if (!pixs)
1887         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1888     if (pixGetDepth(pixs) != 1)
1889         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
1890     if (depth != 2 && depth != 4 && depth != 8 && depth != 16 && depth != 32)
1891         return (PIX *)ERROR_PTR("depth not 2, 4, 8, 16 or 32 bpp",
1892                                 procName, NULL);
1893 
1894     if (depth == 2) {
1895         if (invert == 0)
1896             pixd = pixConvert1To2(NULL, pixs, 0, 3);
1897         else  /* invert bits */
1898             pixd = pixConvert1To2(NULL, pixs, 3, 0);
1899     } else if (depth == 4) {
1900         if (invert == 0)
1901             pixd = pixConvert1To4(NULL, pixs, 0, 15);
1902         else  /* invert bits */
1903             pixd = pixConvert1To4(NULL, pixs, 15, 0);
1904     } else if (depth == 8) {
1905         if (invert == 0)
1906             pixd = pixConvert1To8(NULL, pixs, 0, 255);
1907         else  /* invert bits */
1908             pixd = pixConvert1To8(NULL, pixs, 255, 0);
1909     } else if (depth == 16) {
1910         if (invert == 0)
1911             pixd = pixConvert1To16(NULL, pixs, 0, 0xffff);
1912         else  /* invert bits */
1913             pixd = pixConvert1To16(NULL, pixs, 0xffff, 0);
1914     } else {
1915         if (invert == 0)
1916             pixd = pixConvert1To32(NULL, pixs, 0, 0xffffffff);
1917         else  /* invert bits */
1918             pixd = pixConvert1To32(NULL, pixs, 0xffffffff, 0);
1919     }
1920 
1921     pixCopyInputFormat(pixd, pixs);
1922     return pixd;
1923 }
1924 
1925 
1926 /*!
1927  * \brief   pixConvert1To16()
1928  *
1929  * \param[in]    pixd    [optional] 16 bpp, can be null
1930  * \param[in]    pixs    1 bpp
1931  * \param[in]    val0    16 bit value to be used for 0s in pixs
1932  * \param[in]    val1    16 bit value to be used for 1s in pixs
1933  * \return  pixd 16 bpp
1934  *
1935  * <pre>
1936  * Notes:
1937  *      (1) If pixd is null, a new pix is made.
1938  *      (2) If pixd is not null, it must be of equal width and height
1939  *          as pixs.  It is always returned.
1940  * </pre>
1941  */
1942 PIX *
pixConvert1To16(PIX * pixd,PIX * pixs,l_uint16 val0,l_uint16 val1)1943 pixConvert1To16(PIX      *pixd,
1944                 PIX      *pixs,
1945                 l_uint16  val0,
1946                 l_uint16  val1)
1947 {
1948 l_int32    w, h, i, j, dibit, ndibits, wpls, wpld;
1949 l_uint16   val[2];
1950 l_uint32   index;
1951 l_uint32  *tab, *datas, *datad, *lines, *lined;
1952 
1953     PROCNAME("pixConvert1To16");
1954 
1955     if (!pixs)
1956         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1957     if (pixGetDepth(pixs) != 1)
1958         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
1959 
1960     pixGetDimensions(pixs, &w, &h, NULL);
1961     if (pixd) {
1962         if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
1963             return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
1964         if (pixGetDepth(pixd) != 16)
1965             return (PIX *)ERROR_PTR("pixd not 16 bpp", procName, pixd);
1966     } else {
1967         if ((pixd = pixCreate(w, h, 16)) == NULL)
1968             return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1969     }
1970     pixCopyResolution(pixd, pixs);
1971     pixCopyInputFormat(pixd, pixs);
1972 
1973         /* Use a table to convert 2 src bits at a time */
1974     tab = (l_uint32 *)LEPT_CALLOC(4, sizeof(l_uint32));
1975     val[0] = val0;
1976     val[1] = val1;
1977     for (index = 0; index < 4; index++) {
1978         tab[index] = (val[(index >> 1) & 1] << 16) | val[index & 1];
1979     }
1980 
1981     datas = pixGetData(pixs);
1982     wpls = pixGetWpl(pixs);
1983     datad = pixGetData(pixd);
1984     wpld = pixGetWpl(pixd);
1985     ndibits = (w + 1) / 2;
1986     for (i = 0; i < h; i++) {
1987         lines = datas + i * wpls;
1988         lined = datad + i * wpld;
1989         for (j = 0; j < ndibits; j++) {
1990             dibit = GET_DATA_DIBIT(lines, j);
1991             lined[j] = tab[dibit];
1992         }
1993     }
1994 
1995     LEPT_FREE(tab);
1996     return pixd;
1997 }
1998 
1999 
2000 /*!
2001  * \brief   pixConvert1To32()
2002  *
2003  * \param[in]    pixd    [optional] 32 bpp, can be null
2004  * \param[in]    pixs    1 bpp
2005  * \param[in]    val0    32 bit value to be used for 0s in pixs
2006  * \param[in]    val1    32 bit value to be used for 1s in pixs
2007  * \return  pixd 32 bpp
2008  *
2009  * <pre>
2010  * Notes:
2011  *      (1) If pixd is null, a new pix is made.
2012  *      (2) If pixd is not null, it must be of equal width and height
2013  *          as pixs.  It is always returned.
2014  * </pre>
2015  */
2016 PIX *
pixConvert1To32(PIX * pixd,PIX * pixs,l_uint32 val0,l_uint32 val1)2017 pixConvert1To32(PIX      *pixd,
2018                 PIX      *pixs,
2019                 l_uint32  val0,
2020                 l_uint32  val1)
2021 {
2022 l_int32    w, h, i, j, wpls, wpld, bit;
2023 l_uint32   val[2];
2024 l_uint32  *datas, *datad, *lines, *lined;
2025 
2026     PROCNAME("pixConvert1To32");
2027 
2028     if (!pixs)
2029         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2030     if (pixGetDepth(pixs) != 1)
2031         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
2032 
2033     pixGetDimensions(pixs, &w, &h, NULL);
2034     if (pixd) {
2035         if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
2036             return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
2037         if (pixGetDepth(pixd) != 32)
2038             return (PIX *)ERROR_PTR("pixd not 32 bpp", procName, pixd);
2039     } else {
2040         if ((pixd = pixCreate(w, h, 32)) == NULL)
2041             return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2042     }
2043     pixCopyResolution(pixd, pixs);
2044     pixCopyInputFormat(pixd, pixs);
2045 
2046     val[0] = val0;
2047     val[1] = val1;
2048     datas = pixGetData(pixs);
2049     wpls = pixGetWpl(pixs);
2050     datad = pixGetData(pixd);
2051     wpld = pixGetWpl(pixd);
2052     for (i = 0; i < h; i++) {
2053         lines = datas + i * wpls;
2054         lined = datad + i * wpld;
2055         for (j = 0; j <w; j++) {
2056             bit = GET_DATA_BIT(lines, j);
2057             lined[j] = val[bit];
2058         }
2059     }
2060 
2061     return pixd;
2062 }
2063 
2064 
2065 /*---------------------------------------------------------------------------*
2066  *                    Conversion from 1 bpp to 2 bpp                         *
2067  *---------------------------------------------------------------------------*/
2068 /*!
2069  * \brief   pixConvert1To2Cmap()
2070  *
2071  * \param[in]    pixs    1 bpp
2072  * \return  pixd 2 bpp, cmapped
2073  *
2074  * <pre>
2075  * Notes:
2076  *      (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0)
2077  * </pre>
2078  */
2079 PIX *
pixConvert1To2Cmap(PIX * pixs)2080 pixConvert1To2Cmap(PIX  *pixs)
2081 {
2082 PIX      *pixd;
2083 PIXCMAP  *cmap;
2084 
2085     PROCNAME("pixConvert1To2Cmap");
2086 
2087     if (!pixs)
2088         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2089     if (pixGetDepth(pixs) != 1)
2090         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
2091 
2092     if ((pixd = pixConvert1To2(NULL, pixs, 0, 1)) == NULL)
2093         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2094     cmap = pixcmapCreate(2);
2095     pixcmapAddColor(cmap, 255, 255, 255);
2096     pixcmapAddColor(cmap, 0, 0, 0);
2097     pixSetColormap(pixd, cmap);
2098     pixCopyInputFormat(pixd, pixs);
2099 
2100     return pixd;
2101 }
2102 
2103 
2104 /*!
2105  * \brief   pixConvert1To2()
2106  *
2107  * \param[in]    pixd    [optional] 2 bpp, can be null
2108  * \param[in]    pixs    1 bpp
2109  * \param[in]    val0    2 bit value to be used for 0s in pixs
2110  * \param[in]    val1    2 bit value to be used for 1s in pixs
2111  * \return  pixd 2 bpp
2112  *
2113  * <pre>
2114  * Notes:
2115  *      (1) If pixd is null, a new pix is made.
2116  *      (2) If pixd is not null, it must be of equal width and height
2117  *          as pixs.  It is always returned.
2118  *      (3) A simple unpacking might use val0 = 0 and val1 = 3.
2119  *      (4) If you want a colormapped pixd, use pixConvert1To2Cmap().
2120  * </pre>
2121  */
2122 PIX *
pixConvert1To2(PIX * pixd,PIX * pixs,l_int32 val0,l_int32 val1)2123 pixConvert1To2(PIX     *pixd,
2124                PIX     *pixs,
2125                l_int32  val0,
2126                l_int32  val1)
2127 {
2128 l_int32    w, h, i, j, byteval, nbytes, wpls, wpld;
2129 l_uint8    val[2];
2130 l_uint32   index;
2131 l_uint16  *tab;
2132 l_uint32  *datas, *datad, *lines, *lined;
2133 
2134     PROCNAME("pixConvert1To2");
2135 
2136     if (!pixs)
2137         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
2138     if (pixGetDepth(pixs) != 1)
2139         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
2140 
2141     pixGetDimensions(pixs, &w, &h, NULL);
2142     if (pixd) {
2143         if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
2144             return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
2145         if (pixGetDepth(pixd) != 2)
2146             return (PIX *)ERROR_PTR("pixd not 2 bpp", procName, pixd);
2147     } else {
2148         if ((pixd = pixCreate(w, h, 2)) == NULL)
2149             return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2150     }
2151     pixCopyResolution(pixd, pixs);
2152     pixCopyInputFormat(pixd, pixs);
2153 
2154         /* Use a table to convert 8 src bits to 16 dest bits */
2155     tab = (l_uint16 *)LEPT_CALLOC(256, sizeof(l_uint16));
2156     val[0] = val0;
2157     val[1] = val1;
2158     for (index = 0; index < 256; index++) {
2159         tab[index] = (val[(index >> 7) & 1] << 14) |
2160                      (val[(index >> 6) & 1] << 12) |
2161                      (val[(index >> 5) & 1] << 10) |
2162                      (val[(index >> 4) & 1] << 8) |
2163                      (val[(index >> 3) & 1] << 6) |
2164                      (val[(index >> 2) & 1] << 4) |
2165                      (val[(index >> 1) & 1] << 2) | val[index & 1];
2166     }
2167 
2168     datas = pixGetData(pixs);
2169     wpls = pixGetWpl(pixs);
2170     datad = pixGetData(pixd);
2171     wpld = pixGetWpl(pixd);
2172     nbytes = (w + 7) / 8;
2173     for (i = 0; i < h; i++) {
2174         lines = datas + i * wpls;
2175         lined = datad + i * wpld;
2176         for (j = 0; j < nbytes; j++) {
2177             byteval = GET_DATA_BYTE(lines, j);
2178             SET_DATA_TWO_BYTES(lined, j, tab[byteval]);
2179         }
2180     }
2181 
2182     LEPT_FREE(tab);
2183     return pixd;
2184 }
2185 
2186 
2187 /*---------------------------------------------------------------------------*
2188  *                    Conversion from 1 bpp to 4 bpp                         *
2189  *---------------------------------------------------------------------------*/
2190 /*!
2191  * \brief   pixConvert1To4Cmap()
2192  *
2193  * \param[in]    pixs    1 bpp
2194  * \return  pixd 4 bpp, cmapped
2195  *
2196  * <pre>
2197  * Notes:
2198  *      (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0)
2199  * </pre>
2200  */
2201 PIX *
pixConvert1To4Cmap(PIX * pixs)2202 pixConvert1To4Cmap(PIX  *pixs)
2203 {
2204 PIX      *pixd;
2205 PIXCMAP  *cmap;
2206 
2207     PROCNAME("pixConvert1To4Cmap");
2208 
2209     if (!pixs)
2210         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2211     if (pixGetDepth(pixs) != 1)
2212         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
2213 
2214     if ((pixd = pixConvert1To4(NULL, pixs, 0, 1)) == NULL)
2215         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2216     cmap = pixcmapCreate(4);
2217     pixcmapAddColor(cmap, 255, 255, 255);
2218     pixcmapAddColor(cmap, 0, 0, 0);
2219     pixSetColormap(pixd, cmap);
2220     pixCopyInputFormat(pixd, pixs);
2221 
2222     return pixd;
2223 }
2224 
2225 
2226 /*!
2227  * \brief   pixConvert1To4()
2228  *
2229  * \param[in]    pixd    [optional] 4 bpp, can be null
2230  * \param[in]    pixs    1 bpp
2231  * \param[in]    val0    4 bit value to be used for 0s in pixs
2232  * \param[in]    val1    4 bit value to be used for 1s in pixs
2233  * \return  pixd 4 bpp
2234  *
2235  * <pre>
2236  * Notes:
2237  *      (1) If pixd is null, a new pix is made.
2238  *      (2) If pixd is not null, it must be of equal width and height
2239  *          as pixs.  It is always returned.
2240  *      (3) A simple unpacking might use val0 = 0 and val1 = 15, or v.v.
2241  *      (4) If you want a colormapped pixd, use pixConvert1To4Cmap().
2242  * </pre>
2243  */
2244 PIX *
pixConvert1To4(PIX * pixd,PIX * pixs,l_int32 val0,l_int32 val1)2245 pixConvert1To4(PIX     *pixd,
2246                PIX     *pixs,
2247                l_int32  val0,
2248                l_int32  val1)
2249 {
2250 l_int32    w, h, i, j, byteval, nbytes, wpls, wpld;
2251 l_uint8    val[2];
2252 l_uint32   index;
2253 l_uint32  *tab, *datas, *datad, *lines, *lined;
2254 
2255     PROCNAME("pixConvert1To4");
2256 
2257     if (!pixs)
2258         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
2259     if (pixGetDepth(pixs) != 1)
2260         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
2261 
2262     pixGetDimensions(pixs, &w, &h, NULL);
2263     if (pixd) {
2264         if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
2265             return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
2266         if (pixGetDepth(pixd) != 4)
2267             return (PIX *)ERROR_PTR("pixd not 4 bpp", procName, pixd);
2268     } else {
2269         if ((pixd = pixCreate(w, h, 4)) == NULL)
2270             return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2271     }
2272     pixCopyResolution(pixd, pixs);
2273     pixCopyInputFormat(pixd, pixs);
2274 
2275         /* Use a table to convert 8 src bits to 32 bit dest word */
2276     tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
2277     val[0] = val0;
2278     val[1] = val1;
2279     for (index = 0; index < 256; index++) {
2280         tab[index] = (val[(index >> 7) & 1] << 28) |
2281                      (val[(index >> 6) & 1] << 24) |
2282                      (val[(index >> 5) & 1] << 20) |
2283                      (val[(index >> 4) & 1] << 16) |
2284                      (val[(index >> 3) & 1] << 12) |
2285                      (val[(index >> 2) & 1] << 8) |
2286                      (val[(index >> 1) & 1] << 4) | val[index & 1];
2287     }
2288 
2289     datas = pixGetData(pixs);
2290     wpls = pixGetWpl(pixs);
2291     datad = pixGetData(pixd);
2292     wpld = pixGetWpl(pixd);
2293     nbytes = (w + 7) / 8;
2294     for (i = 0; i < h; i++) {
2295         lines = datas + i * wpls;
2296         lined = datad + i * wpld;
2297         for (j = 0; j < nbytes; j++) {
2298             byteval = GET_DATA_BYTE(lines, j);
2299             lined[j] = tab[byteval];
2300         }
2301     }
2302 
2303     LEPT_FREE(tab);
2304     return pixd;
2305 }
2306 
2307 
2308 /*---------------------------------------------------------------------------*
2309  *               Conversion from 1, 2 and 4 bpp to 8 bpp                     *
2310  *---------------------------------------------------------------------------*/
2311 /*!
2312  * \brief   pixConvert1To8Cmap()
2313  *
2314  * \param[in]    pixs    1 bpp
2315  * \return  pixd 8 bpp, cmapped
2316  *
2317  * <pre>
2318  * Notes:
2319  *      (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0)
2320  * </pre>
2321  */
2322 PIX *
pixConvert1To8Cmap(PIX * pixs)2323 pixConvert1To8Cmap(PIX  *pixs)
2324 {
2325 PIX      *pixd;
2326 PIXCMAP  *cmap;
2327 
2328     PROCNAME("pixConvert1To8Cmap");
2329 
2330     if (!pixs)
2331         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2332     if (pixGetDepth(pixs) != 1)
2333         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, NULL);
2334 
2335     if ((pixd = pixConvert1To8(NULL, pixs, 0, 1)) == NULL)
2336         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2337     cmap = pixcmapCreate(8);
2338     pixcmapAddColor(cmap, 255, 255, 255);
2339     pixcmapAddColor(cmap, 0, 0, 0);
2340     pixSetColormap(pixd, cmap);
2341     pixCopyInputFormat(pixd, pixs);
2342     return pixd;
2343 }
2344 
2345 
2346 /*!
2347  * \brief   pixConvert1To8()
2348  *
2349  * \param[in]    pixd    [optional] 8 bpp, can be null
2350  * \param[in]    pixs    1 bpp
2351  * \param[in]    val0    8 bit value to be used for 0s in pixs
2352  * \param[in]    val1    8 bit value to be used for 1s in pixs
2353  * \return  pixd 8 bpp
2354  *
2355  * <pre>
2356  * Notes:
2357  *      (1) If pixd is null, a new pix is made.
2358  *      (2) If pixd is not null, it must be of equal width and height
2359  *          as pixs.  It is always returned.
2360  *      (3) A simple unpacking might use val0 = 0 and val1 = 255, or v.v.
2361  *      (4) To have a colormap associated with the 8 bpp pixd,
2362  *          use pixConvert1To8Cmap().
2363  * </pre>
2364  */
2365 PIX *
pixConvert1To8(PIX * pixd,PIX * pixs,l_uint8 val0,l_uint8 val1)2366 pixConvert1To8(PIX     *pixd,
2367                PIX     *pixs,
2368                l_uint8  val0,
2369                l_uint8  val1)
2370 {
2371 l_int32    w, h, i, j, qbit, nqbits, wpls, wpld;
2372 l_uint8    val[2];
2373 l_uint32   index;
2374 l_uint32  *tab, *datas, *datad, *lines, *lined;
2375 
2376     PROCNAME("pixConvert1To8");
2377 
2378     if (!pixs)
2379         return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
2380     if (pixGetDepth(pixs) != 1)
2381         return (PIX *)ERROR_PTR("pixs not 1 bpp", procName, pixd);
2382 
2383     pixGetDimensions(pixs, &w, &h, NULL);
2384     if (pixd) {
2385         if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd))
2386             return (PIX *)ERROR_PTR("pix sizes unequal", procName, pixd);
2387         if (pixGetDepth(pixd) != 8)
2388             return (PIX *)ERROR_PTR("pixd not 8 bpp", procName, pixd);
2389     } else {
2390         if ((pixd = pixCreate(w, h, 8)) == NULL)
2391             return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2392     }
2393     pixCopyResolution(pixd, pixs);
2394     pixCopyInputFormat(pixd, pixs);
2395 
2396         /* Use a table to convert 4 src bits at a time */
2397     tab = (l_uint32 *)LEPT_CALLOC(16, sizeof(l_uint32));
2398     val[0] = val0;
2399     val[1] = val1;
2400     for (index = 0; index < 16; index++) {
2401         tab[index] = (val[(index >> 3) & 1] << 24) |
2402                      (val[(index >> 2) & 1] << 16) |
2403                      (val[(index >> 1) & 1] << 8) | val[index & 1];
2404     }
2405 
2406     datas = pixGetData(pixs);
2407     wpls = pixGetWpl(pixs);
2408     datad = pixGetData(pixd);
2409     wpld = pixGetWpl(pixd);
2410     nqbits = (w + 3) / 4;
2411     for (i = 0; i < h; i++) {
2412         lines = datas + i * wpls;
2413         lined = datad + i * wpld;
2414         for (j = 0; j < nqbits; j++) {
2415             qbit = GET_DATA_QBIT(lines, j);
2416             lined[j] = tab[qbit];
2417         }
2418     }
2419 
2420     LEPT_FREE(tab);
2421     return pixd;
2422 }
2423 
2424 
2425 /*!
2426  * \brief   pixConvert2To8()
2427  *
2428  * \param[in]    pixs      2 bpp
2429  * \param[in]    val0      8 bit value to be used for 00 in pixs
2430  * \param[in]    val1      8 bit value to be used for 01 in pixs
2431  * \param[in]    val2      8 bit value to be used for 10 in pixs
2432  * \param[in]    val3      8 bit value to be used for 11 in pixs
2433  * \param[in]    cmapflag  TRUE if pixd is to have a colormap; FALSE otherwise
2434  * \return  pixd 8 bpp, or NULL on error
2435  *
2436  * <pre>
2437  * Notes:
2438  *      ~ A simple unpacking might use val0 = 0,
2439  *        val1 = 85 (0x55), val2 = 170 (0xaa), val3 = 255.
2440  *      ~ If cmapflag is TRUE:
2441  *          ~ The 8 bpp image is made with a colormap.
2442  *          ~ If pixs has a colormap, the input values are ignored and
2443  *            the 8 bpp image is made using the colormap
2444  *          ~ If pixs does not have a colormap, the input values are
2445  *            used to build the colormap.
2446  *      ~ If cmapflag is FALSE:
2447  *          ~ The 8 bpp image is made without a colormap.
2448  *          ~ If pixs has a colormap, the input values are ignored,
2449  *            the colormap is removed, and the values stored in the 8 bpp
2450  *            image are from the colormap.
2451  *          ~ If pixs does not have a colormap, the input values are
2452  *            used to populate the 8 bpp image.
2453  * </pre>
2454  */
2455 PIX *
pixConvert2To8(PIX * pixs,l_uint8 val0,l_uint8 val1,l_uint8 val2,l_uint8 val3,l_int32 cmapflag)2456 pixConvert2To8(PIX     *pixs,
2457                l_uint8  val0,
2458                l_uint8  val1,
2459                l_uint8  val2,
2460                l_uint8  val3,
2461                l_int32  cmapflag)
2462 {
2463 l_int32    w, h, i, j, nbytes, wpls, wpld, dibit, byte;
2464 l_uint8    val[4];
2465 l_uint32   index;
2466 l_uint32  *tab, *datas, *datad, *lines, *lined;
2467 PIX       *pixd;
2468 PIXCMAP   *cmaps, *cmapd;
2469 
2470     PROCNAME("pixConvert2To8");
2471 
2472     if (!pixs)
2473         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2474     if (pixGetDepth(pixs) != 2)
2475         return (PIX *)ERROR_PTR("pixs not 2 bpp", procName, NULL);
2476 
2477     cmaps = pixGetColormap(pixs);
2478     if (cmaps && cmapflag == FALSE)
2479         return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2480 
2481     pixGetDimensions(pixs, &w, &h, NULL);
2482     if ((pixd = pixCreate(w, h, 8)) == NULL)
2483         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2484     pixCopyResolution(pixd, pixs);
2485     pixCopyInputFormat(pixd, pixs);
2486     datas = pixGetData(pixs);
2487     wpls = pixGetWpl(pixs);
2488     datad = pixGetData(pixd);
2489     wpld = pixGetWpl(pixd);
2490 
2491     if (cmapflag == TRUE) {  /* pixd will have a colormap */
2492         if (cmaps) {  /* use the existing colormap from pixs */
2493             cmapd = pixcmapConvertTo8(cmaps);
2494         } else {  /* make a colormap from the input values */
2495             cmapd = pixcmapCreate(8);
2496             pixcmapAddColor(cmapd, val0, val0, val0);
2497             pixcmapAddColor(cmapd, val1, val1, val1);
2498             pixcmapAddColor(cmapd, val2, val2, val2);
2499             pixcmapAddColor(cmapd, val3, val3, val3);
2500         }
2501         pixSetColormap(pixd, cmapd);
2502         for (i = 0; i < h; i++) {
2503             lines = datas + i * wpls;
2504             lined = datad + i * wpld;
2505             for (j = 0; j < w; j++) {
2506                 dibit = GET_DATA_DIBIT(lines, j);
2507                 SET_DATA_BYTE(lined, j, dibit);
2508             }
2509         }
2510         return pixd;
2511     }
2512 
2513         /* Last case: no colormap in either pixs or pixd.
2514          * Use input values and build a table to convert 1 src byte
2515          * (4 src pixels) at a time */
2516     tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
2517     val[0] = val0;
2518     val[1] = val1;
2519     val[2] = val2;
2520     val[3] = val3;
2521     for (index = 0; index < 256; index++) {
2522         tab[index] = (val[(index >> 6) & 3] << 24) |
2523                      (val[(index >> 4) & 3] << 16) |
2524                      (val[(index >> 2) & 3] << 8) | val[index & 3];
2525     }
2526 
2527     nbytes = (w + 3) / 4;
2528     for (i = 0; i < h; i++) {
2529         lines = datas + i * wpls;
2530         lined = datad + i * wpld;
2531         for (j = 0; j < nbytes; j++) {
2532             byte = GET_DATA_BYTE(lines, j);
2533             lined[j] = tab[byte];
2534         }
2535     }
2536 
2537     LEPT_FREE(tab);
2538     return pixd;
2539 }
2540 
2541 
2542 /*!
2543  * \brief   pixConvert4To8()
2544  *
2545  * \param[in]    pixs      4 bpp
2546  * \param[in]    cmapflag  TRUE if pixd is to have a colormap; FALSE otherwise
2547  * \return  pixd 8 bpp, or NULL on error
2548  *
2549  * <pre>
2550  * Notes:
2551  *      ~ If cmapflag is TRUE:
2552  *          ~ pixd is made with a colormap.
2553  *          ~ If pixs has a colormap, it is copied and the colormap
2554  *            index values are placed in pixd.
2555  *          ~ If pixs does not have a colormap, a colormap with linear
2556  *            trc is built and the pixel values in pixs are placed in
2557  *            pixd as colormap index values.
2558  *      ~ If cmapflag is FALSE:
2559  *          ~ pixd is made without a colormap.
2560  *          ~ If pixs has a colormap, it is removed and the values stored
2561  *            in pixd are from the colormap (converted to gray).
2562  *          ~ If pixs does not have a colormap, the pixel values in pixs
2563  *            are used, with shift replication, to populate pixd.
2564  * </pre>
2565  */
2566 PIX *
pixConvert4To8(PIX * pixs,l_int32 cmapflag)2567 pixConvert4To8(PIX     *pixs,
2568                l_int32  cmapflag)
2569 {
2570 l_int32    w, h, i, j, wpls, wpld, byte, qbit;
2571 l_uint32  *datas, *datad, *lines, *lined;
2572 PIX       *pixd;
2573 PIXCMAP   *cmaps, *cmapd;
2574 
2575     PROCNAME("pixConvert4To8");
2576 
2577     if (!pixs)
2578         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2579     if (pixGetDepth(pixs) != 4)
2580         return (PIX *)ERROR_PTR("pixs not 4 bpp", procName, NULL);
2581 
2582     cmaps = pixGetColormap(pixs);
2583     if (cmaps && cmapflag == FALSE)
2584         return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2585 
2586     pixGetDimensions(pixs, &w, &h, NULL);
2587     if ((pixd = pixCreate(w, h, 8)) == NULL)
2588         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
2589     pixCopyResolution(pixd, pixs);
2590     pixCopyInputFormat(pixd, pixs);
2591     datas = pixGetData(pixs);
2592     wpls = pixGetWpl(pixs);
2593     datad = pixGetData(pixd);
2594     wpld = pixGetWpl(pixd);
2595 
2596     if (cmapflag == TRUE) {  /* pixd will have a colormap */
2597         if (cmaps) {  /* use the existing colormap from pixs */
2598             cmapd = pixcmapConvertTo8(cmaps);
2599         } else {  /* make a colormap with a linear trc */
2600             cmapd = pixcmapCreate(8);
2601             for (i = 0; i < 16; i++)
2602                 pixcmapAddColor(cmapd, 17 * i, 17 * i, 17 * i);
2603         }
2604         pixSetColormap(pixd, cmapd);
2605         for (i = 0; i < h; i++) {
2606             lines = datas + i * wpls;
2607             lined = datad + i * wpld;
2608             for (j = 0; j < w; j++) {
2609                 qbit = GET_DATA_QBIT(lines, j);
2610                 SET_DATA_BYTE(lined, j, qbit);
2611             }
2612         }
2613         return pixd;
2614     }
2615 
2616         /* Last case: no colormap in either pixs or pixd.
2617          * Replicate the qbit value into 8 bits. */
2618     for (i = 0; i < h; i++) {
2619         lines = datas + i * wpls;
2620         lined = datad + i * wpld;
2621         for (j = 0; j < w; j++) {
2622             qbit = GET_DATA_QBIT(lines, j);
2623             byte = (qbit << 4) | qbit;
2624             SET_DATA_BYTE(lined, j, byte);
2625         }
2626     }
2627     return pixd;
2628 }
2629 
2630 
2631 
2632 /*---------------------------------------------------------------------------*
2633  *               Unpacking conversion from 8 bpp to 16 bpp                   *
2634  *---------------------------------------------------------------------------*/
2635 /*!
2636  * \brief   pixConvert8To16()
2637  *
2638  * \param[in]    pixs       8 bpp; colormap removed to gray
2639  * \param[in]    leftshift  number of bits: 0 is no shift;
2640  *                          8 replicates in MSB and LSB of dest
2641  * \return  pixd 16 bpp, or NULL on error
2642  *
2643  * <pre>
2644  * Notes:
2645  *      (1) For left shift of 8, the 8 bit value is replicated in both
2646  *          the MSB and the LSB of the pixels in pixd.  That way, we get
2647  *          proportional mapping, with a correct map from 8 bpp white
2648  *          (0xff) to 16 bpp white (0xffff).
2649  * </pre>
2650  */
2651 PIX *
pixConvert8To16(PIX * pixs,l_int32 leftshift)2652 pixConvert8To16(PIX     *pixs,
2653                 l_int32  leftshift)
2654 {
2655 l_int32    i, j, w, h, d, wplt, wpld, val;
2656 l_uint32  *datat, *datad, *linet, *lined;
2657 PIX       *pixt, *pixd;
2658 
2659     PROCNAME("pixConvert8To16");
2660 
2661     if (!pixs)
2662         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2663     pixGetDimensions(pixs, &w, &h, &d);
2664     if (d != 8)
2665         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
2666     if (leftshift < 0 || leftshift > 8)
2667         return (PIX *)ERROR_PTR("leftshift not in [0 ... 8]", procName, NULL);
2668 
2669     if (pixGetColormap(pixs) != NULL)
2670         pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2671     else
2672         pixt = pixClone(pixs);
2673 
2674     pixd = pixCreate(w, h, 16);
2675     pixCopyResolution(pixd, pixs);
2676     pixCopyInputFormat(pixd, pixs);
2677     datat = pixGetData(pixt);
2678     datad = pixGetData(pixd);
2679     wplt = pixGetWpl(pixt);
2680     wpld = pixGetWpl(pixd);
2681     for (i = 0; i < h; i++) {
2682         linet = datat + i * wplt;
2683         lined = datad + i * wpld;
2684         for (j = 0; j < w; j++) {
2685             val = GET_DATA_BYTE(linet, j);
2686             if (leftshift == 8)
2687                 val = val | (val << leftshift);
2688             else
2689                 val <<= leftshift;
2690             SET_DATA_TWO_BYTES(lined, j, val);
2691         }
2692     }
2693 
2694     pixDestroy(&pixt);
2695     return pixd;
2696 }
2697 
2698 
2699 /*---------------------------------------------------------------------------*
2700  *                     Top-level conversion to 2 bpp                         *
2701  *---------------------------------------------------------------------------*/
2702 /*!
2703  * \brief   pixConvertTo2()
2704  *
2705  * \param[in]    pixs   1, 2, 4, 8, 32 bpp; colormap OK but will be removed
2706  * \return  pixd   2 bpp, or NULL on error
2707  *
2708  * <pre>
2709  * Notes:
2710  *      (1) This is a top-level function, with simple default values
2711  *          used in pixConvertTo8() if unpacking is necessary.
2712  *      (2) Any existing colormap is removed; the result is always gray.
2713  *      (3) If the input image has 2 bpp and no colormap, the operation is
2714  *          lossless and a copy is returned.
2715  * </pre>
2716  */
2717 PIX *
pixConvertTo2(PIX * pixs)2718 pixConvertTo2(PIX  *pixs)
2719 {
2720 l_int32  d;
2721 PIX     *pix1, *pix2, *pix3, *pixd;
2722 
2723     PROCNAME("pixConvertTo2");
2724 
2725     if (!pixs)
2726         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2727     d = pixGetDepth(pixs);
2728     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32)
2729         return (PIX *)ERROR_PTR("depth not {1,2,4,8,32}", procName, NULL);
2730 
2731     if (pixGetColormap(pixs) != NULL) {
2732         pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2733         d = pixGetDepth(pix1);
2734     } else {
2735         pix1 = pixCopy(NULL, pixs);
2736     }
2737     if (d == 32)
2738         pix2 = pixConvertTo8(pix1, FALSE);
2739     else
2740         pix2 = pixClone(pix1);
2741     pixDestroy(&pix1);
2742     if (d == 1) {
2743         pixd = pixConvert1To2(NULL, pix2, 3, 0);
2744     } else if (d == 2) {
2745         pixd = pixClone(pix2);
2746     } else if (d == 4) {
2747         pix3 = pixConvert4To8(pix2, FALSE);  /* unpack to 8 */
2748         pixd = pixConvert8To2(pix3);
2749         pixDestroy(&pix3);
2750     } else {  /* d == 8 */
2751         pixd = pixConvert8To2(pix2);
2752     }
2753     pixDestroy(&pix2);
2754     return pixd;
2755 }
2756 
2757 
2758 /*!
2759  * \brief   pixConvert8To2()
2760  *
2761  * \param[in]     pix     8 bpp; colormap OK
2762  * \return  pixd  2 bpp, or NULL on error
2763  *
2764  * <pre>
2765  * Notes:
2766  *      (1) Any existing colormap is removed to gray.
2767  * </pre>
2768  */
2769 PIX *
pixConvert8To2(PIX * pix)2770 pixConvert8To2(PIX  *pix)
2771 {
2772 l_int32    i, j, w, h, wpls, wpld;
2773 l_uint32   word;
2774 l_uint32  *datas, *lines, *datad, *lined;
2775 PIX       *pixs, *pixd;
2776 
2777     PROCNAME("pixConvert8To2");
2778 
2779     if (!pix || pixGetDepth(pix) != 8)
2780         return (PIX *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL);
2781 
2782     if (pixGetColormap(pix) != NULL)
2783         pixs = pixRemoveColormap(pix, REMOVE_CMAP_TO_GRAYSCALE);
2784     else
2785         pixs = pixClone(pix);
2786     pixGetDimensions(pixs, &w, &h, NULL);
2787     datas = pixGetData(pixs);
2788     wpls = pixGetWpl(pixs);
2789     pixd = pixCreate(w, h, 2);
2790     datad = pixGetData(pixd);
2791     wpld = pixGetWpl(pixd);
2792     for (i = 0; i < h; i++) {
2793         lines = datas + i * wpls;
2794         lined = datad + i * wpld;
2795         for (j = 0; j < wpls; j++) {  /* march through 4 pixels at a time */
2796             word = lines[j] & 0xc0c0c0c0;  /* top 2 bits of each byte */
2797             word = (word >> 24) | ((word & 0xff0000) >> 18) |
2798                    ((word & 0xff00) >> 12) | ((word & 0xff) >> 6);
2799             SET_DATA_BYTE(lined, j, word);  /* only LS byte is filled */
2800         }
2801     }
2802     pixDestroy(&pixs);
2803     return pixd;
2804 }
2805 
2806 
2807 /*---------------------------------------------------------------------------*
2808  *                     Top-level conversion to 4 bpp                         *
2809  *---------------------------------------------------------------------------*/
2810 /*!
2811  * \brief   pixConvertTo4()
2812  *
2813  * \param[in]    pixs   1, 2, 4, 8, 32 bpp; colormap OK but will be removed
2814  * \return  pixd   4 bpp, or NULL on error
2815  *
2816  * <pre>
2817  * Notes:
2818  *      (1) This is a top-level function, with simple default values
2819  *          used in pixConvertTo8() if unpacking is necessary.
2820  *      (2) Any existing colormap is removed; the result is always gray.
2821  *      (3) If the input image has 4 bpp and no colormap, the operation is
2822  *          lossless and a copy is returned.
2823  * </pre>
2824  */
2825 PIX *
pixConvertTo4(PIX * pixs)2826 pixConvertTo4(PIX  *pixs)
2827 {
2828 l_int32  d;
2829 PIX     *pix1, *pix2, *pix3, *pixd;
2830 
2831     PROCNAME("pixConvertTo4");
2832 
2833     if (!pixs)
2834         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2835     d = pixGetDepth(pixs);
2836     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32)
2837         return (PIX *)ERROR_PTR("depth not {1,2,4,8,32}", procName, NULL);
2838 
2839     if (pixGetColormap(pixs) != NULL) {
2840         pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
2841         d = pixGetDepth(pix1);
2842     } else {
2843         pix1 = pixCopy(NULL, pixs);
2844     }
2845     if (d == 32)
2846         pix2 = pixConvertTo8(pix1, FALSE);
2847     else
2848         pix2 = pixClone(pix1);
2849     pixDestroy(&pix1);
2850     if (d == 1) {
2851         pixd = pixConvert1To4(NULL, pix2, 15, 0);
2852     } else if (d == 2) {
2853         pix3 = pixConvert2To8(pix2, 0, 0x55, 0xaa, 0xff, FALSE);
2854         pixd = pixConvert8To4(pix3);
2855         pixDestroy(&pix3);
2856     } else if (d == 4) {
2857         pixd = pixClone(pix2);
2858     } else {  /* d == 8 */
2859         pixd = pixConvert8To4(pix2);
2860     }
2861     pixDestroy(&pix2);
2862     return pixd;
2863 }
2864 
2865 
2866 /*!
2867  * \brief   pixConvert8To4()
2868  *
2869  * \param[in]     pix     8 bpp; colormap OK
2870  * \return  pixd  4 bpp, or NULL on error
2871  *
2872  * <pre>
2873  * Notes:
2874  *      (1) Any existing colormap is removed to gray.
2875  * </pre>
2876  */
2877 PIX *
pixConvert8To4(PIX * pix)2878 pixConvert8To4(PIX  *pix)
2879 {
2880 l_int32    i, j, w, h, wpls, wpld, val;
2881 l_uint32  *datas, *lines, *datad, *lined;
2882 PIX       *pixs, *pixd;
2883 
2884     PROCNAME("pixConvert8To4");
2885 
2886     if (!pix || pixGetDepth(pix) != 8)
2887         return (PIX *)ERROR_PTR("pix undefined or not 8 bpp", procName, NULL);
2888 
2889     if (pixGetColormap(pix) != NULL)
2890         pixs = pixRemoveColormap(pix, REMOVE_CMAP_TO_GRAYSCALE);
2891     else
2892         pixs = pixClone(pix);
2893     pixGetDimensions(pixs, &w, &h, NULL);
2894     datas = pixGetData(pixs);
2895     wpls = pixGetWpl(pixs);
2896     pixd = pixCreate(w, h, 4);
2897     datad = pixGetData(pixd);
2898     wpld = pixGetWpl(pixd);
2899     for (i = 0; i < h; i++) {
2900         lines = datas + i * wpls;
2901         lined = datad + i * wpld;
2902         for (j = 0; j < w; j++) {
2903             val = GET_DATA_BYTE(lines, j);
2904             val = val >> 4;  /* take top 4 bits */
2905             SET_DATA_QBIT(lined, j, val);
2906         }
2907     }
2908     pixDestroy(&pixs);
2909     return pixd;
2910 }
2911 
2912 
2913 /*---------------------------------------------------------------------------*
2914  *                     Top-level conversion to 1 bpp                         *
2915  *---------------------------------------------------------------------------*/
2916 /*!
2917  * \brief   pixConvertTo1()
2918  *
2919  * \param[in]    pixs       1, 2, 4, 8, 16 or 32 bpp
2920  * \param[in]    threshold  for final binarization, relative to 8 bpp
2921  * \return  pixd 1 bpp, or NULL on error
2922  *
2923  * <pre>
2924  * Notes:
2925  *      (1) This is a top-level function, with simple default values
2926  *          used in pixConvertTo8() if unpacking is necessary.
2927  *      (2) Any existing colormap is removed.
2928  *      (3) If the input image has 1 bpp and no colormap, the operation is
2929  *          lossless and a copy is returned.
2930  * </pre>
2931  */
2932 PIX *
pixConvertTo1(PIX * pixs,l_int32 threshold)2933 pixConvertTo1(PIX     *pixs,
2934               l_int32  threshold)
2935 {
2936 l_int32   d, color0, color1, rval, gval, bval;
2937 PIX      *pixg, *pixd;
2938 PIXCMAP  *cmap;
2939 
2940     PROCNAME("pixConvertTo1");
2941 
2942     if (!pixs)
2943         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2944     d = pixGetDepth(pixs);
2945     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
2946         return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL);
2947 
2948     cmap = pixGetColormap(pixs);
2949     if (d == 1) {
2950         if (!cmap) {
2951             return pixCopy(NULL, pixs);
2952         } else {  /* strip the colormap off, and invert if reasonable
2953                    for standard binary photometry.  */
2954             pixcmapGetColor(cmap, 0, &rval, &gval, &bval);
2955             color0 = rval + gval + bval;
2956             pixcmapGetColor(cmap, 1, &rval, &gval, &bval);
2957             color1 = rval + gval + bval;
2958             pixd = pixCopy(NULL, pixs);
2959             pixDestroyColormap(pixd);
2960             if (color1 > color0)
2961                 pixInvert(pixd, pixd);
2962             return pixd;
2963         }
2964     }
2965 
2966         /* For all other depths, use 8 bpp as an intermediary */
2967     pixg = pixConvertTo8(pixs, FALSE);
2968     pixd = pixThresholdToBinary(pixg, threshold);
2969     pixDestroy(&pixg);
2970     return pixd;
2971 }
2972 
2973 
2974 /*!
2975  * \brief   pixConvertTo1BySampling()
2976  *
2977  * \param[in]    pixs       1, 2, 4, 8, 16 or 32 bpp
2978  * \param[in]    factor     submsampling factor; integer >= 1
2979  * \param[in]    threshold  for final binarization, relative to 8 bpp
2980  * \return  pixd 1 bpp, or NULL on error
2981  *
2982  * <pre>
2983  * Notes:
2984  *      (1) This is a quick and dirty, top-level converter.
2985  *      (2) See pixConvertTo1() for default values.
2986  * </pre>
2987  */
2988 PIX *
pixConvertTo1BySampling(PIX * pixs,l_int32 factor,l_int32 threshold)2989 pixConvertTo1BySampling(PIX     *pixs,
2990                         l_int32  factor,
2991                         l_int32  threshold)
2992 {
2993 l_float32  scalefactor;
2994 PIX       *pixt, *pixd;
2995 
2996     PROCNAME("pixConvertTo1BySampling");
2997 
2998     if (!pixs)
2999         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3000     if (factor < 1)
3001         return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL);
3002 
3003     scalefactor = 1. / (l_float32)factor;
3004     pixt = pixScaleBySampling(pixs, scalefactor, scalefactor);
3005     pixd = pixConvertTo1(pixt, threshold);
3006 
3007     pixDestroy(&pixt);
3008     return pixd;
3009 }
3010 
3011 
3012 /*---------------------------------------------------------------------------*
3013  *                     Top-level conversion to 8 bpp                         *
3014  *---------------------------------------------------------------------------*/
3015 /*!
3016  * \brief   pixConvertTo8()
3017  *
3018  * \param[in]    pixs      1, 2, 4, 8, 16 or 32 bpp
3019  * \param[in]    cmapflag  TRUE if pixd is to have a colormap; FALSE otherwise
3020  * \return  pixd 8 bpp, or NULL on error
3021  *
3022  * <pre>
3023  * Notes:
3024  *      (1) This is a top-level function, with simple default values
3025  *          for unpacking.
3026  *      (2) The result, pixd, is made with a colormap if specified.
3027  *          It is always a new image -- never a clone.  For example,
3028  *          if d == 8, and cmapflag matches the existence of a cmap
3029  *          in pixs, the operation is lossless and it returns a copy.
3030  *      (3) The default values used are:
3031  *          ~ 1 bpp: val0 = 255, val1 = 0
3032  *          ~ 2 bpp: 4 bpp:  even increments over dynamic range
3033  *          ~ 8 bpp: lossless if cmap matches cmapflag
3034  *          ~ 16 bpp: use most significant byte
3035  *      (4) If 32 bpp RGB, this is converted to gray.  If you want
3036  *          to do color quantization, you must specify the type
3037  *          explicitly, using the color quantization code.
3038  * </pre>
3039  */
3040 PIX *
pixConvertTo8(PIX * pixs,l_int32 cmapflag)3041 pixConvertTo8(PIX     *pixs,
3042               l_int32  cmapflag)
3043 {
3044 l_int32   d;
3045 PIX      *pixd;
3046 PIXCMAP  *cmap;
3047 
3048     PROCNAME("pixConvertTo8");
3049 
3050     if (!pixs)
3051         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3052     d = pixGetDepth(pixs);
3053     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
3054         return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL);
3055 
3056     if (d == 1) {
3057         if (cmapflag)
3058             return pixConvert1To8Cmap(pixs);
3059         else
3060             return pixConvert1To8(NULL, pixs, 255, 0);
3061     } else if (d == 2) {
3062         return pixConvert2To8(pixs, 0, 85, 170, 255, cmapflag);
3063     } else if (d == 4) {
3064         return pixConvert4To8(pixs, cmapflag);
3065     } else if (d == 8) {
3066         cmap = pixGetColormap(pixs);
3067         if ((cmap && cmapflag) || (!cmap && !cmapflag)) {
3068             return pixCopy(NULL, pixs);
3069         } else if (cmap) {  /* !cmapflag */
3070             return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
3071         } else {  /* !cmap && cmapflag; add colormap to pixd */
3072             pixd = pixCopy(NULL, pixs);
3073             pixAddGrayColormap8(pixd);
3074             return pixd;
3075         }
3076     } else if (d == 16) {
3077         pixd = pixConvert16To8(pixs, L_MS_BYTE);
3078         if (cmapflag)
3079             pixAddGrayColormap8(pixd);
3080         return pixd;
3081     } else { /* d == 32 */
3082         pixd = pixConvertRGBToLuminance(pixs);
3083         if (cmapflag)
3084             pixAddGrayColormap8(pixd);
3085         return pixd;
3086     }
3087 }
3088 
3089 
3090 /*!
3091  * \brief   pixConvertTo8BySampling()
3092  *
3093  * \param[in]    pixs      1, 2, 4, 8, 16 or 32 bpp
3094  * \param[in]    factor    submsampling factor; integer >= 1
3095  * \param[in]    cmapflag  TRUE if pixd is to have a colormap; FALSE otherwise
3096  * \return  pixd 8 bpp, or NULL on error
3097  *
3098  * <pre>
3099  * Notes:
3100  *      (1) This is a fast, quick/dirty, top-level converter.
3101  *      (2) See pixConvertTo8() for default values.
3102  * </pre>
3103  */
3104 PIX *
pixConvertTo8BySampling(PIX * pixs,l_int32 factor,l_int32 cmapflag)3105 pixConvertTo8BySampling(PIX     *pixs,
3106                         l_int32  factor,
3107                         l_int32  cmapflag)
3108 {
3109 l_float32  scalefactor;
3110 PIX       *pixt, *pixd;
3111 
3112     PROCNAME("pixConvertTo8BySampling");
3113 
3114     if (!pixs)
3115         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3116     if (factor < 1)
3117         return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL);
3118 
3119     scalefactor = 1. / (l_float32)factor;
3120     pixt = pixScaleBySampling(pixs, scalefactor, scalefactor);
3121     pixd = pixConvertTo8(pixt, cmapflag);
3122 
3123     pixDestroy(&pixt);
3124     return pixd;
3125 }
3126 
3127 
3128 /*!
3129  * \brief   pixConvertTo8Colormap()
3130  *
3131  * \param[in]    pixs    1, 2, 4, 8, 16 or 32 bpp
3132  * \param[in]    dither  1 to dither if necessary; 0 otherwise
3133  * \return  pixd 8 bpp, cmapped, or NULL on error
3134  *
3135  * <pre>
3136  * Notes:
3137  *      (1) This is a top-level function, with simple default values
3138  *          for unpacking.
3139  *      (2) The result, pixd, is always made with a colormap.
3140  *      (3) If d == 8, the operation is lossless and it returns a copy.
3141  *      (4) The default values used for increasing depth are:
3142  *          ~ 1 bpp: val0 = 255, val1 = 0
3143  *          ~ 2 bpp: 4 bpp:  even increments over dynamic range
3144  *      (5) For 16 bpp, use the most significant byte.
3145  *      (6) For 32 bpp RGB, use octcube quantization with optional dithering.
3146  * </pre>
3147  */
3148 PIX *
pixConvertTo8Colormap(PIX * pixs,l_int32 dither)3149 pixConvertTo8Colormap(PIX     *pixs,
3150                       l_int32  dither)
3151 {
3152 l_int32  d;
3153 
3154     PROCNAME("pixConvertTo8Colormap");
3155 
3156     if (!pixs)
3157         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3158     d = pixGetDepth(pixs);
3159     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
3160         return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", procName, NULL);
3161 
3162     if (d != 32)
3163         return pixConvertTo8(pixs, 1);
3164 
3165     return pixConvertRGBToColormap(pixs, dither);
3166 }
3167 
3168 
3169 /*---------------------------------------------------------------------------*
3170  *                    Top-level conversion to 16 bpp                         *
3171  *---------------------------------------------------------------------------*/
3172 /*!
3173  * \brief   pixConvertTo16()
3174  *
3175  * \param[in]    pixs    1, 8 bpp
3176  * \return  pixd 16 bpp, or NULL on error
3177  *
3178  *  Usage: Top-level function, with simple default values for unpacking.
3179  *      1 bpp:  val0 = 0xffff, val1 = 0
3180  *      8 bpp:  replicates the 8 bit value in both the MSB and LSB
3181  *              of the 16 bit pixel.
3182  */
3183 PIX *
pixConvertTo16(PIX * pixs)3184 pixConvertTo16(PIX  *pixs)
3185 {
3186 l_int32  d;
3187 
3188     PROCNAME("pixConvertTo16");
3189 
3190     if (!pixs)
3191         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3192 
3193     d = pixGetDepth(pixs);
3194     if (d == 1)
3195         return pixConvert1To16(NULL, pixs, 0xffff, 0);
3196     else if (d == 8)
3197         return pixConvert8To16(pixs, 8);
3198     else
3199         return (PIX *)ERROR_PTR("src depth not 1 or 8 bpp", procName, NULL);
3200 }
3201 
3202 
3203 
3204 /*---------------------------------------------------------------------------*
3205  *                    Top-level conversion to 32 bpp                         *
3206  *---------------------------------------------------------------------------*/
3207 /*!
3208  * \brief   pixConvertTo32()
3209  *
3210  * \param[in]    pixs    1, 2, 4, 8, 16 or 32 bpp
3211  * \return  pixd 32 bpp, or NULL on error
3212  *
3213  *  Usage: Top-level function, with simple default values for unpacking.
3214  *      1 bpp:  val0 = 255, val1 = 0
3215  *              and then replication into R, G and B components
3216  *      2 bpp:  if colormapped, use the colormap values; otherwise,
3217  *              use val0 = 0, val1 = 0x55, val2 = 0xaa, val3 = 255
3218  *              and replicate gray into R, G and B components
3219  *      4 bpp:  if colormapped, use the colormap values; otherwise,
3220  *              replicate 2 nybs into a byte, and then into R,G,B components
3221  *      8 bpp:  if colormapped, use the colormap values; otherwise,
3222  *              replicate gray values into R, G and B components
3223  *      16 bpp: replicate MSB into R, G and B components
3224  *      24 bpp: unpack the pixels, maintaining word alignment on each scanline
3225  *      32 bpp: makes a copy
3226  *
3227  * <pre>
3228  * Notes:
3229  *      (1) Never returns a clone of pixs.
3230  * </pre>
3231  */
3232 PIX *
pixConvertTo32(PIX * pixs)3233 pixConvertTo32(PIX  *pixs)
3234 {
3235 l_int32  d;
3236 PIX     *pix1, *pixd;
3237 
3238     PROCNAME("pixConvertTo32");
3239 
3240     if (!pixs)
3241         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3242 
3243     d = pixGetDepth(pixs);
3244     if (d == 1) {
3245         return pixConvert1To32(NULL, pixs, 0xffffffff, 0);
3246     } else if (d == 2) {
3247         pix1 = pixConvert2To8(pixs, 0, 85, 170, 255, TRUE);
3248         pixd = pixConvert8To32(pix1);
3249         pixDestroy(&pix1);
3250         return pixd;
3251     } else if (d == 4) {
3252         pix1 = pixConvert4To8(pixs, TRUE);
3253         pixd = pixConvert8To32(pix1);
3254         pixDestroy(&pix1);
3255         return pixd;
3256     } else if (d == 8) {
3257         return pixConvert8To32(pixs);
3258     } else if (d == 16) {
3259         pix1 = pixConvert16To8(pixs, L_MS_BYTE);
3260         pixd = pixConvert8To32(pix1);
3261         pixDestroy(&pix1);
3262         return pixd;
3263     } else if (d == 24) {
3264         return pixConvert24To32(pixs);
3265     } else if (d == 32) {
3266         return pixCopy(NULL, pixs);
3267     } else {
3268         return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8, 16, 32 bpp",
3269                                 procName, NULL);
3270     }
3271 }
3272 
3273 
3274 /*!
3275  * \brief   pixConvertTo32BySampling()
3276  *
3277  * \param[in]    pixs    1, 2, 4, 8, 16 or 32 bpp
3278  * \param[in]    factor  submsampling factor; integer >= 1
3279  * \return  pixd 32 bpp, or NULL on error
3280  *
3281  * <pre>
3282  * Notes:
3283  *      (1) This is a fast, quick/dirty, top-level converter.
3284  *      (2) See pixConvertTo32() for default values.
3285  * </pre>
3286  */
3287 PIX *
pixConvertTo32BySampling(PIX * pixs,l_int32 factor)3288 pixConvertTo32BySampling(PIX     *pixs,
3289                          l_int32  factor)
3290 {
3291 l_float32  scalefactor;
3292 PIX       *pix1, *pixd;
3293 
3294     PROCNAME("pixConvertTo32BySampling");
3295 
3296     if (!pixs)
3297         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3298     if (factor < 1)
3299         return (PIX *)ERROR_PTR("factor must be >= 1", procName, NULL);
3300 
3301     scalefactor = 1. / (l_float32)factor;
3302     pix1 = pixScaleBySampling(pixs, scalefactor, scalefactor);
3303     pixd = pixConvertTo32(pix1);
3304 
3305     pixDestroy(&pix1);
3306     return pixd;
3307 }
3308 
3309 
3310 /*!
3311  * \brief   pixConvert8To32()
3312  *
3313  * \param[in]    pixs    8 bpp
3314  * \return  32 bpp rgb pix, or NULL on error
3315  *
3316  * <pre>
3317  * Notes:
3318  *      (1) If there is no colormap, replicates the gray value
3319  *          into the 3 MSB of the dest pixel.
3320  * </pre>
3321  */
3322 PIX *
pixConvert8To32(PIX * pixs)3323 pixConvert8To32(PIX  *pixs)
3324 {
3325 l_int32    i, j, w, h, wpls, wpld, val;
3326 l_uint32  *datas, *datad, *lines, *lined;
3327 l_uint32  *tab;
3328 PIX       *pixd;
3329 
3330     PROCNAME("pixConvert8To32");
3331 
3332     if (!pixs)
3333         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3334     if (pixGetDepth(pixs) != 8)
3335         return (PIX *)ERROR_PTR("pixs not 8 bpp", procName, NULL);
3336 
3337     if (pixGetColormap(pixs))
3338         return pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
3339 
3340     pixGetDimensions(pixs, &w, &h, NULL);
3341     datas = pixGetData(pixs);
3342     wpls = pixGetWpl(pixs);
3343     if ((pixd = pixCreate(w, h, 32)) == NULL)
3344         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
3345     pixCopyResolution(pixd, pixs);
3346     pixCopyInputFormat(pixd, pixs);
3347     datad = pixGetData(pixd);
3348     wpld = pixGetWpl(pixd);
3349 
3350         /* Replication table gray --> rgb */
3351     tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32));
3352     for (i = 0; i < 256; i++)
3353       tab[i] = (i << 24) | (i << 16) | (i << 8);
3354 
3355         /* Replicate 1 --> 4 bytes (alpha byte not set) */
3356     for (i = 0; i < h; i++) {
3357         lines = datas + i * wpls;
3358         lined = datad + i * wpld;
3359         for (j = 0; j < w; j++) {
3360             val = GET_DATA_BYTE(lines, j);
3361             lined[j] = tab[val];
3362         }
3363     }
3364 
3365     LEPT_FREE(tab);
3366     return pixd;
3367 }
3368 
3369 
3370 /*---------------------------------------------------------------------------*
3371  *           Top-level conversion to 8 or 32 bpp, without colormap           *
3372  *---------------------------------------------------------------------------*/
3373 /*!
3374  * \brief   pixConvertTo8Or32()
3375  *
3376  * \param[in]    pixs      1, 2, 4, 8, 16, with or without colormap;
3377  *                         or 32 bpp rgb
3378  * \param[in]    copyflag  L_CLONE or L_COPY
3379  * \param[in]    warnflag  1 to issue warning if colormap is removed; else 0
3380  * \return  pixd 8 bpp grayscale or 32 bpp rgb, or NULL on error
3381  *
3382  * <pre>
3383  * Notes:
3384  *      (1) If there is a colormap, the colormap is removed to 8 or 32 bpp,
3385  *          depending on whether the colors in the colormap are all gray.
3386  *      (2) If the input is either rgb or 8 bpp without a colormap,
3387  *          this returns either a clone or a copy, depending on %copyflag.
3388  *      (3) Otherwise, the pix is converted to 8 bpp grayscale.
3389  *          In all cases, pixd does not have a colormap.
3390  * </pre>
3391  */
3392 PIX *
pixConvertTo8Or32(PIX * pixs,l_int32 copyflag,l_int32 warnflag)3393 pixConvertTo8Or32(PIX     *pixs,
3394                   l_int32  copyflag,
3395                   l_int32  warnflag)
3396 {
3397 l_int32  d;
3398 PIX     *pixd;
3399 
3400     PROCNAME("pixConvertTo8Or32");
3401 
3402     if (!pixs)
3403         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3404     if (copyflag != L_CLONE && copyflag != L_COPY)
3405         return (PIX *)ERROR_PTR("invalid copyflag", procName, NULL);
3406 
3407     d = pixGetDepth(pixs);
3408     if (pixGetColormap(pixs)) {
3409         if (warnflag) L_WARNING("pix has colormap; removing\n", procName);
3410         pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
3411     } else if (d == 8 || d == 32) {
3412         if (copyflag == L_CLONE)
3413             pixd = pixClone(pixs);
3414         else  /* copyflag == L_COPY */
3415             pixd = pixCopy(NULL, pixs);
3416     } else {
3417         pixd = pixConvertTo8(pixs, 0);
3418     }
3419 
3420         /* Sanity check on result */
3421     d = pixGetDepth(pixd);
3422     if (d != 8 && d != 32) {
3423         pixDestroy(&pixd);
3424         return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", procName, NULL);
3425     }
3426 
3427     return pixd;
3428 }
3429 
3430 
3431 /*---------------------------------------------------------------------------*
3432  *                 Conversion between 24 bpp and 32 bpp rgb                  *
3433  *---------------------------------------------------------------------------*/
3434 /*!
3435  * \brief   pixConvert24To32()
3436  *
3437  * \param[in]    pixs    24 bpp rgb
3438  * \return  pixd 32 bpp rgb, or NULL on error
3439  *
3440  * <pre>
3441  * Notes:
3442  *      (1) 24 bpp rgb pix are not supported in leptonica, except for a small
3443  *          number of formatted write operations.  The data is a byte array,
3444  *          with pixels in order r,g,b, and padded to 32 bit boundaries
3445  *          in each line.
3446  *      (2) Because 24 bpp rgb pix are conveniently generated by programs
3447  *          such as xpdf (which has SplashBitmaps that store the raster
3448  *          data in consecutive 24-bit rgb pixels), it is useful to provide
3449  *          24 bpp pix that simply incorporate that data.  The only things
3450  *          we can do with these are:
3451  *            (a) write them to file in png, jpeg, tiff and pnm
3452  *            (b) interconvert between 24 and 32 bpp in memory (for testing).
3453  * </pre>
3454  */
3455 PIX *
pixConvert24To32(PIX * pixs)3456 pixConvert24To32(PIX  *pixs)
3457 {
3458 l_uint8   *lines;
3459 l_int32    w, h, d, i, j, wpls, wpld, rval, gval, bval;
3460 l_uint32   pixel;
3461 l_uint32  *datas, *datad, *lined;
3462 PIX       *pixd;
3463 
3464     PROCNAME("pixConvert24to32");
3465 
3466     if (!pixs)
3467         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3468     pixGetDimensions(pixs, &w, &h, &d);
3469     if (d != 24)
3470         return (PIX *)ERROR_PTR("pixs not 24 bpp", procName, NULL);
3471 
3472     pixd = pixCreateNoInit(w, h, 32);
3473     datas = pixGetData(pixs);
3474     datad = pixGetData(pixd);
3475     wpls = pixGetWpl(pixs);
3476     wpld = pixGetWpl(pixd);
3477     for (i = 0; i < h; i++) {
3478         lines = (l_uint8 *)(datas + i * wpls);
3479         lined = datad + i * wpld;
3480         for (j = 0; j < w; j++) {
3481             rval = *lines++;
3482             gval = *lines++;
3483             bval = *lines++;
3484             composeRGBPixel(rval, gval, bval, &pixel);
3485             lined[j] = pixel;
3486         }
3487     }
3488     pixCopyResolution(pixd, pixs);
3489     pixCopyInputFormat(pixd, pixs);
3490     return pixd;
3491 }
3492 
3493 
3494 /*!
3495  * \brief   pixConvert32To24()
3496  *
3497  * \param[in]    pixs    32 bpp rgb
3498  * \return  pixd 24 bpp rgb, or NULL on error
3499  *
3500  * <pre>
3501  * Notes:
3502  *      (1) See pixconvert24To32().
3503  * </pre>
3504  */
3505 PIX *
pixConvert32To24(PIX * pixs)3506 pixConvert32To24(PIX  *pixs)
3507 {
3508 l_uint8   *rgbdata8;
3509 l_int32    w, h, d, i, j, wpls, wpld, rval, gval, bval;
3510 l_uint32  *datas, *lines, *rgbdata;
3511 PIX       *pixd;
3512 
3513     PROCNAME("pixConvert32to24");
3514 
3515     if (!pixs)
3516         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3517     pixGetDimensions(pixs, &w, &h, &d);
3518     if (d != 32)
3519         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
3520 
3521     datas = pixGetData(pixs);
3522     wpls = pixGetWpl(pixs);
3523     pixd = pixCreateNoInit(w, h, 24);
3524     rgbdata = pixGetData(pixd);
3525     wpld = pixGetWpl(pixd);
3526     for (i = 0; i < h; i++) {
3527         lines = datas + i * wpls;
3528         rgbdata8 = (l_uint8 *)(rgbdata + i * wpld);
3529         for (j = 0; j < w; j++) {
3530             extractRGBValues(lines[j], &rval, &gval, &bval);
3531             *rgbdata8++ = rval;
3532             *rgbdata8++ = gval;
3533             *rgbdata8++ = bval;
3534         }
3535     }
3536     pixCopyResolution(pixd, pixs);
3537     pixCopyInputFormat(pixd, pixs);
3538     return pixd;
3539 }
3540 
3541 
3542 /*---------------------------------------------------------------------------*
3543  *            Conversion between 32 bpp (1 spp) and 16 or 8 bpp              *
3544  *---------------------------------------------------------------------------*/
3545 /*!
3546  * \brief   pixConvert32To16()
3547  *
3548  * \param[in]    pixs   32 bpp, single component
3549  * \param[in]    type   L_LS_TWO_BYTES, L_MS_TWO_BYTES, L_CLIP_TO_FFFF
3550  * \return  pixd 16 bpp , or NULL on error
3551  *
3552  * <pre>
3553  * Notes:
3554  *      (1) The data in pixs is typically used for labelling.
3555  *          It is an array of l_uint32 values, not rgb or rgba.
3556  * </pre>
3557  */
3558 PIX *
pixConvert32To16(PIX * pixs,l_int32 type)3559 pixConvert32To16(PIX     *pixs,
3560                  l_int32  type)
3561 {
3562 l_uint16   dword;
3563 l_int32    w, h, i, j, wpls, wpld;
3564 l_uint32   sword;
3565 l_uint32  *datas, *lines, *datad, *lined;
3566 PIX       *pixd;
3567 
3568     PROCNAME("pixConvert32to16");
3569 
3570     if (!pixs || pixGetDepth(pixs) != 32)
3571         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
3572     if (type != L_LS_TWO_BYTES && type != L_MS_TWO_BYTES &&
3573         type != L_CLIP_TO_FFFF)
3574         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
3575 
3576     pixGetDimensions(pixs, &w, &h, NULL);
3577     if ((pixd = pixCreate(w, h, 16)) == NULL)
3578         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
3579     pixCopyResolution(pixd, pixs);
3580     pixCopyInputFormat(pixd, pixs);
3581     wpls = pixGetWpl(pixs);
3582     datas = pixGetData(pixs);
3583     wpld = pixGetWpl(pixd);
3584     datad = pixGetData(pixd);
3585 
3586     for (i = 0; i < h; i++) {
3587         lines = datas + i * wpls;
3588         lined = datad + i * wpld;
3589         if (type == L_LS_TWO_BYTES) {
3590             for (j = 0; j < wpls; j++) {
3591                 sword = *(lines + j);
3592                 dword = sword & 0xffff;
3593                 SET_DATA_TWO_BYTES(lined, j, dword);
3594             }
3595         } else if (type == L_MS_TWO_BYTES) {
3596             for (j = 0; j < wpls; j++) {
3597                 sword = *(lines + j);
3598                 dword = sword >> 16;
3599                 SET_DATA_TWO_BYTES(lined, j, dword);
3600             }
3601         } else {  /* type == L_CLIP_TO_FFFF */
3602             for (j = 0; j < wpls; j++) {
3603                 sword = *(lines + j);
3604                 dword = (sword >> 16) ? 0xffff : (sword & 0xffff);
3605                 SET_DATA_TWO_BYTES(lined, j, dword);
3606             }
3607         }
3608     }
3609 
3610     return pixd;
3611 }
3612 
3613 
3614 /*!
3615  * \brief   pixConvert32To8()
3616  *
3617  * \param[in]    pixs    32 bpp, single component
3618  * \param[in]    type16  L_LS_TWO_BYTES, L_MS_TWO_BYTES, L_CLIP_TO_FFFF
3619  * \param[in]    type8   L_LS_BYTE, L_MS_BYTE, L_CLIP_TO_FF
3620  * \return  pixd 8 bpp, or NULL on error
3621  */
3622 PIX *
pixConvert32To8(PIX * pixs,l_int32 type16,l_int32 type8)3623 pixConvert32To8(PIX     *pixs,
3624                 l_int32  type16,
3625                 l_int32  type8)
3626 {
3627 PIX  *pix1, *pixd;
3628 
3629     PROCNAME("pixConvert32to8");
3630 
3631     if (!pixs || pixGetDepth(pixs) != 32)
3632         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
3633     if (type16 != L_LS_TWO_BYTES && type16 != L_MS_TWO_BYTES &&
3634         type16 != L_CLIP_TO_FFFF)
3635         return (PIX *)ERROR_PTR("invalid type16", procName, NULL);
3636     if (type8 != L_LS_BYTE && type8 != L_MS_BYTE && type8 != L_CLIP_TO_FF)
3637         return (PIX *)ERROR_PTR("invalid type8", procName, NULL);
3638 
3639     pix1 = pixConvert32To16(pixs, type16);
3640     pixd = pixConvert16To8(pix1, type8);
3641     pixDestroy(&pix1);
3642     return pixd;
3643 }
3644 
3645 
3646 /*---------------------------------------------------------------------------*
3647  *        Removal of alpha component by blending with white background       *
3648  *---------------------------------------------------------------------------*/
3649 /*!
3650  * \brief   pixRemoveAlpha()
3651  *
3652  * \param[in]    pixs   any depth
3653  * \return  pixd        if 32 bpp rgba, pixs blended over a white background;
3654  *                      a clone of pixs otherwise, and NULL on error
3655  *
3656  * <pre>
3657  * Notes:
3658  *      (1) This is a wrapper on pixAlphaBlendUniform()
3659  * </pre>
3660  */
3661 PIX *
pixRemoveAlpha(PIX * pixs)3662 pixRemoveAlpha(PIX *pixs)
3663 {
3664     PROCNAME("pixRemoveAlpha");
3665 
3666     if (!pixs)
3667         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3668 
3669     if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4)
3670         return pixAlphaBlendUniform(pixs, 0xffffff00);
3671     else
3672         return pixClone(pixs);
3673 }
3674 
3675 
3676 /*---------------------------------------------------------------------------*
3677  *                  Addition of alpha component to 1 bpp                     *
3678  *---------------------------------------------------------------------------*/
3679 /*!
3680  * \brief   pixAddAlphaTo1bpp()
3681  *
3682  * \param[in]    pixd    [optional] 1 bpp, can be null or equal to pixs
3683  *               pixs    1 bpp
3684  * \return  pixd 1 bpp with colormap and non-opaque alpha,
3685  *                    or NULL on error
3686  *
3687  * <pre>
3688  * Notes:
3689  *      (1) We don't use 1 bpp colormapped images with alpha in leptonica,
3690  *          but we support generating them (here), writing to png, and reading
3691  *          the png.  On reading, they are converted to 32 bpp RGBA.
3692  *      (2) The background (0) pixels in pixs become fully transparent, and the
3693  *          foreground (1) pixels are fully opaque.  Thus, pixd is a 1 bpp
3694  *          representation of a stencil, that can be used to paint over pixels
3695  *          of a backing image that are masked by the foreground in pixs.
3696  * </pre>
3697  */
3698 PIX *
pixAddAlphaTo1bpp(PIX * pixd,PIX * pixs)3699 pixAddAlphaTo1bpp(PIX  *pixd,
3700                   PIX  *pixs)
3701 {
3702 PIXCMAP  *cmap;
3703 
3704     PROCNAME("pixAddAlphaTo1bpp");
3705 
3706     if (!pixs || (pixGetDepth(pixs) != 1))
3707         return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
3708     if (pixd && (pixd != pixs))
3709         return (PIX *)ERROR_PTR("pixd defined but != pixs", procName, NULL);
3710 
3711     pixd = pixCopy(pixd, pixs);
3712     cmap = pixcmapCreate(1);
3713     pixSetColormap(pixd, cmap);
3714     pixcmapAddRGBA(cmap, 255, 255, 255, 0);  /* 0 ==> white + transparent */
3715     pixcmapAddRGBA(cmap, 0, 0, 0, 255);  /* 1 ==> black + opaque */
3716     return pixd;
3717 }
3718 
3719 
3720 /*---------------------------------------------------------------------------*
3721  *                  Lossless depth conversion (unpacking)                    *
3722  *---------------------------------------------------------------------------*/
3723 /*!
3724  * \brief   pixConvertLossless()
3725  *
3726  * \param[in]    pixs    1, 2, 4, 8 bpp, not cmapped
3727  * \param[in]    d       destination depth: 2, 4 or 8
3728  * \return  pixd 2, 4 or 8 bpp, or NULL on error
3729  *
3730  * <pre>
3731  * Notes:
3732  *      (1) This is a lossless unpacking (depth-increasing)
3733  *          conversion.  If ds is the depth of pixs, then
3734  *           ~ if d < ds, returns NULL
3735  *           ~ if d == ds, returns a copy
3736  *           ~ if d > ds, does the unpacking conversion
3737  *      (2) If pixs has a colormap, this is an error.
3738  * </pre>
3739  */
3740 PIX *
pixConvertLossless(PIX * pixs,l_int32 d)3741 pixConvertLossless(PIX     *pixs,
3742                    l_int32  d)
3743 {
3744 l_int32    w, h, ds, wpls, wpld, i, j, val;
3745 l_uint32  *datas, *datad, *lines, *lined;
3746 PIX       *pixd;
3747 
3748     PROCNAME("pixConvertLossless");
3749 
3750     if (!pixs)
3751         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3752     if (pixGetColormap(pixs))
3753         return (PIX *)ERROR_PTR("pixs has colormap", procName, NULL);
3754     if (d != 2 && d != 4 && d != 8)
3755         return (PIX *)ERROR_PTR("invalid dest depth", procName, NULL);
3756 
3757     pixGetDimensions(pixs, &w, &h, &ds);
3758     if (d < ds)
3759         return (PIX *)ERROR_PTR("depth > d", procName, NULL);
3760     else if (d == ds)
3761         return pixCopy(NULL, pixs);
3762 
3763     if ((pixd = pixCreate(w, h, d)) == NULL)
3764         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
3765     pixCopyResolution(pixd, pixs);
3766     pixCopyInputFormat(pixd, pixs);
3767 
3768         /* Unpack the bits */
3769     datas = pixGetData(pixs);
3770     wpls = pixGetWpl(pixs);
3771     datad = pixGetData(pixd);
3772     wpld = pixGetWpl(pixd);
3773     for (i = 0; i < h; i++) {
3774         lines = datas + i * wpls;
3775         lined = datad + i * wpld;
3776         switch (ds)
3777         {
3778         case 1:
3779             for (j = 0; j < w; j++) {
3780                 val = GET_DATA_BIT(lines, j);
3781                 if (d == 8)
3782                     SET_DATA_BYTE(lined, j, val);
3783                 else if (d == 4)
3784                     SET_DATA_QBIT(lined, j, val);
3785                 else  /* d == 2 */
3786                     SET_DATA_DIBIT(lined, j, val);
3787             }
3788             break;
3789         case 2:
3790             for (j = 0; j < w; j++) {
3791                 val = GET_DATA_DIBIT(lines, j);
3792                 if (d == 8)
3793                     SET_DATA_BYTE(lined, j, val);
3794                 else  /* d == 4 */
3795                     SET_DATA_QBIT(lined, j, val);
3796             }
3797             break;
3798         case 4:
3799             for (j = 0; j < w; j++) {
3800                 val = GET_DATA_DIBIT(lines, j);
3801                 SET_DATA_BYTE(lined, j, val);
3802             }
3803             break;
3804         }
3805     }
3806 
3807     return pixd;
3808 }
3809 
3810 
3811 /*---------------------------------------------------------------------------*
3812  *                     Conversion for printing in PostScript                 *
3813  *---------------------------------------------------------------------------*/
3814 /*!
3815  * \brief   pixConvertForPSWrap()
3816  *
3817  * \param[in]    pixs    1, 2, 4, 8, 16, 32 bpp
3818  * \return  pixd    1, 8, or 32 bpp, or NULL on error
3819  *
3820  * <pre>
3821  * Notes:
3822  *      (1) For wrapping in PostScript, we convert pixs to
3823  *          1 bpp, 8 bpp (gray) and 32 bpp (RGB color).
3824  *      (2) Colormaps are removed.  For pixs with colormaps, the
3825  *          images are converted to either 8 bpp gray or 32 bpp
3826  *          RGB, depending on whether the colormap has color content.
3827  *      (3) Images without colormaps, that are not 1 bpp or 32 bpp,
3828  *          are converted to 8 bpp gray.
3829  * </pre>
3830  */
3831 PIX *
pixConvertForPSWrap(PIX * pixs)3832 pixConvertForPSWrap(PIX  *pixs)
3833 {
3834 l_int32   d;
3835 PIX      *pixd;
3836 PIXCMAP  *cmap;
3837 
3838     PROCNAME("pixConvertForPSWrap");
3839 
3840     if (!pixs)
3841         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3842 
3843     cmap = pixGetColormap(pixs);
3844     d = pixGetDepth(pixs);
3845     switch (d)
3846     {
3847     case 1:
3848     case 32:
3849         pixd = pixClone(pixs);
3850         break;
3851     case 2:
3852         if (cmap)
3853             pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
3854         else
3855             pixd = pixConvert2To8(pixs, 0, 0x55, 0xaa, 0xff, FALSE);
3856         break;
3857     case 4:
3858         if (cmap)
3859             pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
3860         else
3861             pixd = pixConvert4To8(pixs, FALSE);
3862         break;
3863     case 8:
3864         pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
3865         break;
3866     case 16:
3867         pixd = pixConvert16To8(pixs, L_MS_BYTE);
3868         break;
3869     default:
3870         fprintf(stderr, "depth not in {1, 2, 4, 8, 16, 32}");
3871         return NULL;
3872    }
3873 
3874    return pixd;
3875 }
3876 
3877 
3878 /*---------------------------------------------------------------------------*
3879  *                      Scaling conversion to subpixel RGB                   *
3880  *---------------------------------------------------------------------------*/
3881 /*!
3882  * \brief   pixConvertToSubpixelRGB()
3883  *
3884  * \param[in]    pixs            8 bpp grayscale, 32 bpp rgb, or colormapped
3885  * \param[in]    scalex, scaley  anisotropic scaling permitted between
3886  *                               source and destination
3887  * \param[in]    order           of subpixel rgb color components in
3888  *                               composition of pixd:
3889  *                               L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR,
3890  *                               L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR
3891  * \return  pixd 32 bpp, or NULL on error
3892  *
3893  * <pre>
3894  * Notes:
3895  *      (1) If pixs has a colormap, it is removed based on its contents
3896  *          to either 8 bpp gray or rgb.
3897  *      (2) For horizontal subpixel splitting, the input image
3898  *          is rescaled by %scaley vertically and by 3.0 times
3899  *          %scalex horizontally.  Then each horizontal triplet
3900  *          of pixels is mapped back to a single rgb pixel, with the
3901  *          r, g and b values being assigned based on the pixel triplet.
3902  *          For gray triplets, the r, g, and b values are set equal to
3903  *          the three gray values.  For color triplets, the r, g and b
3904  *          values are set equal to the components from the appropriate
3905  *          subpixel.  Vertical subpixel splitting is handled similarly.
3906  *      (3) See pixConvertGrayToSubpixelRGB() and
3907  *          pixConvertColorToSubpixelRGB() for further details.
3908  * </pre>
3909  */
3910 PIX *
pixConvertToSubpixelRGB(PIX * pixs,l_float32 scalex,l_float32 scaley,l_int32 order)3911 pixConvertToSubpixelRGB(PIX       *pixs,
3912                         l_float32  scalex,
3913                         l_float32  scaley,
3914                         l_int32    order)
3915 {
3916 l_int32    d;
3917 PIX       *pix1, *pixd;
3918 PIXCMAP   *cmap;
3919 
3920     PROCNAME("pixConvertToSubpixelRGB");
3921 
3922     if (!pixs)
3923         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3924     d = pixGetDepth(pixs);
3925     cmap = pixGetColormap(pixs);
3926     if (d != 8 && d != 32 && !cmap)
3927         return (PIX *)ERROR_PTR("pix not 8 or 32 bpp and not cmapped",
3928                                 procName, NULL);
3929     if (scalex <= 0.0 || scaley <= 0.0)
3930         return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL);
3931     if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR &&
3932         order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR)
3933         return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL);
3934     if ((pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC)) == NULL)
3935         return (PIX *)ERROR_PTR("pix1 not made", procName, NULL);
3936 
3937     d = pixGetDepth(pix1);
3938     pixd = NULL;
3939     if (d == 8)
3940         pixd = pixConvertGrayToSubpixelRGB(pix1, scalex, scaley, order);
3941     else if (d == 32)
3942         pixd = pixConvertColorToSubpixelRGB(pix1, scalex, scaley, order);
3943     else
3944         L_ERROR("invalid depth %d\n", procName, d);
3945 
3946     pixDestroy(&pix1);
3947     return pixd;
3948 }
3949 
3950 
3951 /*!
3952  * \brief   pixConvertGrayToSubpixelRGB()
3953  *
3954  * \param[in]    pixs            8 bpp or colormapped
3955  * \param[in]    scalex, scaley
3956  * \param[in]    order           of subpixel rgb color components in
3957  *                               composition of pixd:
3958  *                               L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR,
3959  *                               L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR
3960  * \return  pixd 32 bpp, or NULL on error
3961  *
3962  * <pre>
3963  * Notes:
3964  *      (1) If pixs has a colormap, it is removed to 8 bpp.
3965  *      (2) For horizontal subpixel splitting, the input gray image
3966  *          is rescaled by %scaley vertically and by 3.0 times
3967  *          %scalex horizontally.  Then each horizontal triplet
3968  *          of pixels is mapped back to a single rgb pixel, with the
3969  *          r, g and b values being assigned from the triplet of gray values.
3970  *          Similar operations are used for vertical subpixel splitting.
3971  *      (3) This is a form of subpixel rendering that tends to give the
3972  *          resulting text a sharper and somewhat chromatic display.
3973  *          For horizontal subpixel splitting, the observable difference
3974  *          between %order=L_SUBPIXEL_ORDER_RGB and
3975  *          %order=L_SUBPIXEL_ORDER_BGR is reduced by optical diffusers
3976  *          in the display that make the pixel color appear to emerge
3977  *          from the entire pixel.
3978  * </pre>
3979  */
3980 PIX *
pixConvertGrayToSubpixelRGB(PIX * pixs,l_float32 scalex,l_float32 scaley,l_int32 order)3981 pixConvertGrayToSubpixelRGB(PIX       *pixs,
3982                             l_float32  scalex,
3983                             l_float32  scaley,
3984                             l_int32    order)
3985 {
3986 l_int32    w, h, d, wd, hd, wplt, wpld, i, j, rval, gval, bval, direction;
3987 l_uint32  *datat, *datad, *linet, *lined;
3988 PIX       *pix1, *pix2, *pixd;
3989 PIXCMAP   *cmap;
3990 
3991     PROCNAME("pixConvertGrayToSubpixelRGB");
3992 
3993     if (!pixs)
3994         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
3995     d = pixGetDepth(pixs);
3996     cmap = pixGetColormap(pixs);
3997     if (d != 8 && !cmap)
3998         return (PIX *)ERROR_PTR("pix not 8 bpp & not cmapped", procName, NULL);
3999     if (scalex <= 0.0 || scaley <= 0.0)
4000         return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL);
4001     if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR &&
4002         order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR)
4003         return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL);
4004 
4005     direction =
4006         (order == L_SUBPIXEL_ORDER_RGB || order == L_SUBPIXEL_ORDER_BGR)
4007         ? L_HORIZ : L_VERT;
4008     pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
4009     if (direction == L_HORIZ)
4010         pix2 = pixScale(pix1, 3.0 * scalex, scaley);
4011     else  /* L_VERT */
4012         pix2 = pixScale(pix1, scalex, 3.0 * scaley);
4013 
4014     pixGetDimensions(pix2, &w, &h, NULL);
4015     wd = (direction == L_HORIZ) ? w / 3 : w;
4016     hd = (direction == L_VERT) ? h / 3 : h;
4017     pixd = pixCreate(wd, hd, 32);
4018     datad = pixGetData(pixd);
4019     wpld = pixGetWpl(pixd);
4020     datat = pixGetData(pix2);
4021     wplt = pixGetWpl(pix2);
4022     if (direction == L_HORIZ) {
4023         for (i = 0; i < hd; i++) {
4024             linet = datat + i * wplt;
4025             lined = datad + i * wpld;
4026             for (j = 0; j < wd; j++) {
4027                 rval = GET_DATA_BYTE(linet, 3 * j);
4028                 gval = GET_DATA_BYTE(linet, 3 * j + 1);
4029                 bval = GET_DATA_BYTE(linet, 3 * j + 2);
4030                 if (order == L_SUBPIXEL_ORDER_RGB)
4031                     composeRGBPixel(rval, gval, bval, &lined[j]);
4032                 else  /* order BGR */
4033                     composeRGBPixel(bval, gval, rval, &lined[j]);
4034             }
4035         }
4036     } else {  /* L_VERT */
4037         for (i = 0; i < hd; i++) {
4038             linet = datat + 3 * i * wplt;
4039             lined = datad + i * wpld;
4040             for (j = 0; j < wd; j++) {
4041                 rval = GET_DATA_BYTE(linet, j);
4042                 gval = GET_DATA_BYTE(linet + wplt, j);
4043                 bval = GET_DATA_BYTE(linet + 2 * wplt, j);
4044                 if (order == L_SUBPIXEL_ORDER_VRGB)
4045                     composeRGBPixel(rval, gval, bval, &lined[j]);
4046                 else  /* order VBGR */
4047                     composeRGBPixel(bval, gval, rval, &lined[j]);
4048             }
4049         }
4050     }
4051 
4052     pixDestroy(&pix1);
4053     pixDestroy(&pix2);
4054     return pixd;
4055 }
4056 
4057 
4058 /*!
4059  * \brief   pixConvertColorToSubpixelRGB()
4060  *
4061  * \param[in]    pixs            32 bpp or colormapped
4062  * \param[in]    scalex, scaley
4063  * \param[in]    order           of subpixel rgb color components in
4064  *                               composition of pixd:
4065  *                               L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR,
4066  *                               L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR
4067  * \return  pixd 32 bpp, or NULL on error
4068  *
4069  * <pre>
4070  * Notes:
4071  *      (1) If pixs has a colormap, it is removed to 32 bpp rgb.
4072  *          If the colormap has no color, pixConvertGrayToSubpixelRGB()
4073  *          should be called instead, because it will give the same result
4074  *          more efficiently.  The function pixConvertToSubpixelRGB()
4075  *          will do the best thing for all cases.
4076  *      (2) For horizontal subpixel splitting, the input rgb image
4077  *          is rescaled by %scaley vertically and by 3.0 times
4078  *          %scalex horizontally.  Then for each horizontal triplet
4079  *          of pixels, the r component of the final pixel is selected
4080  *          from the r component of the appropriate pixel in the triplet,
4081  *          and likewise for g and b.  Vertical subpixel splitting is
4082  *          handled similarly.
4083  * </pre>
4084  */
4085 PIX *
pixConvertColorToSubpixelRGB(PIX * pixs,l_float32 scalex,l_float32 scaley,l_int32 order)4086 pixConvertColorToSubpixelRGB(PIX       *pixs,
4087                              l_float32  scalex,
4088                              l_float32  scaley,
4089                              l_int32    order)
4090 {
4091 l_int32    w, h, d, wd, hd, wplt, wpld, i, j, rval, gval, bval, direction;
4092 l_uint32  *datat, *datad, *linet, *lined;
4093 PIX       *pix1, *pix2, *pixd;
4094 PIXCMAP   *cmap;
4095 
4096     PROCNAME("pixConvertColorToSubpixelRGB");
4097 
4098     if (!pixs)
4099         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
4100     d = pixGetDepth(pixs);
4101     cmap = pixGetColormap(pixs);
4102     if (d != 32 && !cmap)
4103         return (PIX *)ERROR_PTR("pix not 32 bpp & not cmapped", procName, NULL);
4104     if (scalex <= 0.0 || scaley <= 0.0)
4105         return (PIX *)ERROR_PTR("scale factors must be > 0", procName, NULL);
4106     if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR &&
4107         order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR)
4108         return (PIX *)ERROR_PTR("invalid subpixel order", procName, NULL);
4109 
4110     direction =
4111         (order == L_SUBPIXEL_ORDER_RGB || order == L_SUBPIXEL_ORDER_BGR)
4112         ? L_HORIZ : L_VERT;
4113     pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR);
4114     if (direction == L_HORIZ)
4115         pix2 = pixScale(pix1, 3.0 * scalex, scaley);
4116     else  /* L_VERT */
4117         pix2 = pixScale(pix1, scalex, 3.0 * scaley);
4118 
4119     pixGetDimensions(pix2, &w, &h, NULL);
4120     wd = (direction == L_HORIZ) ? w / 3 : w;
4121     hd = (direction == L_VERT) ? h / 3 : h;
4122     pixd = pixCreate(wd, hd, 32);
4123     pixCopyInputFormat(pixd, pixs);
4124     datad = pixGetData(pixd);
4125     wpld = pixGetWpl(pixd);
4126     datat = pixGetData(pix2);
4127     wplt = pixGetWpl(pix2);
4128     if (direction == L_HORIZ) {
4129         for (i = 0; i < hd; i++) {
4130             linet = datat + i * wplt;
4131             lined = datad + i * wpld;
4132             for (j = 0; j < wd; j++) {
4133                 if (order == L_SUBPIXEL_ORDER_RGB) {
4134                     extractRGBValues(linet[3 * j], &rval, NULL, NULL);
4135                     extractRGBValues(linet[3 * j + 1], NULL, &gval, NULL);
4136                     extractRGBValues(linet[3 * j + 2], NULL, NULL, &bval);
4137                 } else {  /* order BGR */
4138                     extractRGBValues(linet[3 * j], NULL, NULL, &bval);
4139                     extractRGBValues(linet[3 * j + 1], NULL, &gval, NULL);
4140                     extractRGBValues(linet[3 * j + 2], &rval, NULL, NULL);
4141                 }
4142                 composeRGBPixel(rval, gval, bval, &lined[j]);
4143             }
4144         }
4145     } else {  /* L_VERT */
4146         for (i = 0; i < hd; i++) {
4147             linet = datat + 3 * i * wplt;
4148             lined = datad + i * wpld;
4149             for (j = 0; j < wd; j++) {
4150                 if (order == L_SUBPIXEL_ORDER_VRGB) {
4151                     extractRGBValues(linet[j], &rval, NULL, NULL);
4152                     extractRGBValues((linet + wplt)[j], NULL, &gval, NULL);
4153                     extractRGBValues((linet + 2 * wplt)[j], NULL, NULL, &bval);
4154                 } else {  /* order VBGR */
4155                     extractRGBValues(linet[j], NULL, NULL, &bval);
4156                     extractRGBValues((linet + wplt)[j], NULL, &gval, NULL);
4157                     extractRGBValues((linet + 2 * wplt)[j], &rval, NULL, NULL);
4158                 }
4159                 composeRGBPixel(rval, gval, bval, &lined[j]);
4160             }
4161         }
4162     }
4163 
4164     if (pixGetSpp(pixs) == 4)
4165         pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley);
4166 
4167     pixDestroy(&pix1);
4168     pixDestroy(&pix2);
4169     return pixd;
4170 }
4171 
4172 
4173 /*---------------------------------------------------------------------*
4174  *       Setting neutral point for min/max boost conversion to gray    *
4175  *---------------------------------------------------------------------*/
4176 /*!
4177  * \brief   l_setNeutralBoostVal()
4178  *
4179  * \param[in]    val    between 1 and 255; typical value is 180
4180  * \return  void
4181  *
4182  * <pre>
4183  * Notes:
4184  *      (1) This raises or lowers the selected min or max RGB component value,
4185  *          depending on if that component is above or below this value.
4186  * </pre>
4187  */
4188 void
l_setNeutralBoostVal(l_int32 val)4189 l_setNeutralBoostVal(l_int32  val)
4190 {
4191     PROCNAME("l_setNeutralBoostVal");
4192 
4193     if (val <= 0) {
4194         L_ERROR("invalid reference value for neutral boost\n", procName);
4195         return;
4196     }
4197     var_NEUTRAL_BOOST_VAL = val;
4198 }
4199