Coverage Report

Created: 2025-06-13 06:48

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