Coverage Report

Created: 2024-07-27 06:27

/src/leptonica/src/paintcmap.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 paintcmap.c
29
 * <pre>
30
 *
31
 *      These in-place functions paint onto colormap images.
32
 *
33
 *      Repaint selected pixels in region
34
 *           l_int32     pixSetSelectCmap()
35
 *
36
 *      Repaint non-white pixels in region
37
 *           l_int32     pixColorGrayRegionsCmap()
38
 *           l_int32     pixColorGrayCmap()
39
 *           l_int32     pixColorGrayMaskedCmap()
40
 *           l_int32     addColorizedGrayToCmap()
41
 *
42
 *      Repaint selected pixels through mask
43
 *           l_int32     pixSetSelectMaskedCmap()
44
 *
45
 *      Repaint all pixels through mask
46
 *           l_int32     pixSetMaskedCmap()
47
 *
48
 *
49
 *  The 'set select' functions condition the setting on a specific
50
 *  pixel value (i.e., index into the colormap) of the underlying
51
 *  Pix that is being modified.  The same conditioning is used in
52
 *  pixBlendCmap().
53
 *
54
 *  The pixColorGrayCmap() function sets all truly gray (r = g = b) pixels,
55
 *  with the exception of either black or white pixels, to a new color.
56
 *
57
 *  The pixSetSelectMaskedCmap() function conditions pixel painting
58
 *  on both a specific pixel value and location within the fg mask.
59
 *  By contrast, pixSetMaskedCmap() sets all pixels under the
60
 *  mask foreground, without considering the initial pixel values.
61
 * </pre>
62
 */
63
64
#ifdef HAVE_CONFIG_H
65
#include <config_auto.h>
66
#endif  /* HAVE_CONFIG_H */
67
68
#include <string.h>
69
#include "allheaders.h"
70
71
/*-------------------------------------------------------------*
72
 *               Repaint selected pixels in region             *
73
 *-------------------------------------------------------------*/
74
/*!
75
 * \brief   pixSetSelectCmap()
76
 *
77
 * \param[in]    pixs              1, 2, 4 or 8 bpp, with colormap
78
 * \param[in]    box               [optional] region to set color; can be NULL
79
 * \param[in]    sindex            colormap index of pixels to be changed
80
 * \param[in]    rval, gval, bval  new color to paint
81
 * \return  0 if OK, 1 on error
82
 *
83
 * <pre>
84
 * Notes:
85
 *      (1) This is an in-place operation.
86
 *      (2) It sets all pixels in region that have the color specified
87
 *          by the colormap index %sindex to the new color.
88
 *      (3) %sindex must be in the existing colormap; otherwise an
89
 *          error is returned.
90
 *      (4) If the new color exists in the colormap, it is used;
91
 *          otherwise, it is added to the colormap.  If it cannot be
92
 *          added because the colormap is full, an error is returned.
93
 *      (5) If %box is NULL, applies function to the entire image; otherwise,
94
 *          clips the operation to the intersection of the box and pix.
95
 *      (6) An example of use would be to set to a specific color all
96
 *          the light (background) pixels within a certain region of
97
 *          a 3-level 2 bpp image, while leaving light pixels outside
98
 *          this region unchanged.
99
 * </pre>
100
 */
101
l_ok
102
pixSetSelectCmap(PIX     *pixs,
103
                 BOX     *box,
104
                 l_int32  sindex,
105
                 l_int32  rval,
106
                 l_int32  gval,
107
                 l_int32  bval)
108
0
{
109
0
l_int32    i, j, w, h, d, n, x1, y1, x2, y2, bw, bh, val, wpls;
110
0
l_int32    index;  /* of new color to be set */
111
0
l_uint32  *lines, *datas;
112
0
PIXCMAP   *cmap;
113
114
0
    if (!pixs)
115
0
        return ERROR_INT("pixs not defined", __func__, 1);
116
0
    if ((cmap = pixGetColormap(pixs)) == NULL)
117
0
        return ERROR_INT("no colormap", __func__, 1);
118
0
    d = pixGetDepth(pixs);
119
0
    if (d != 1 && d != 2 && d != 4 && d != 8)
120
0
        return ERROR_INT("depth not in {1,2,4,8}", __func__, 1);
121
122
        /* Add new color if necessary; get index of this color in cmap */
123
0
    n = pixcmapGetCount(cmap);
124
0
    if (sindex >= n)
125
0
        return ERROR_INT("sindex too large; no cmap entry", __func__, 1);
126
0
    if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */
127
0
        if (pixcmapAddColor(cmap, rval, gval, bval))
128
0
            return ERROR_INT("error adding cmap entry", __func__, 1);
129
0
        else
130
0
            index = n;  /* we've added one color */
131
0
    }
132
133
        /* Determine the region of substitution */
134
0
    pixGetDimensions(pixs, &w, &h, NULL);
135
0
    if (!box) {
136
0
        x1 = y1 = 0;
137
0
        x2 = w;
138
0
        y2 = h;
139
0
    } else {
140
0
        boxGetGeometry(box, &x1, &y1, &bw, &bh);
141
0
        x2 = x1 + bw - 1;
142
0
        y2 = y1 + bh - 1;
143
0
    }
144
145
        /* Replace pixel value sindex by index in the region */
146
0
    datas = pixGetData(pixs);
147
0
    wpls = pixGetWpl(pixs);
148
0
    for (i = y1; i <= y2; i++) {
149
0
        if (i < 0 || i >= h)  /* clip */
150
0
            continue;
151
0
        lines = datas + i * wpls;
152
0
        for (j = x1; j <= x2; j++) {
153
0
            if (j < 0 || j >= w)  /* clip */
154
0
                continue;
155
0
            switch (d) {
156
0
            case 1:
157
0
                val = GET_DATA_BIT(lines, j);
158
0
                if (val == sindex) {
159
0
                    if (index == 0)
160
0
                        CLEAR_DATA_BIT(lines, j);
161
0
                    else
162
0
                        SET_DATA_BIT(lines, j);
163
0
                }
164
0
                break;
165
0
            case 2:
166
0
                val = GET_DATA_DIBIT(lines, j);
167
0
                if (val == sindex)
168
0
                    SET_DATA_DIBIT(lines, j, index);
169
0
                break;
170
0
            case 4:
171
0
                val = GET_DATA_QBIT(lines, j);
172
0
                if (val == sindex)
173
0
                    SET_DATA_QBIT(lines, j, index);
174
0
                break;
175
0
            case 8:
176
0
                val = GET_DATA_BYTE(lines, j);
177
0
                if (val == sindex)
178
0
                    SET_DATA_BYTE(lines, j, index);
179
0
                break;
180
0
            default:
181
0
                return ERROR_INT("depth not in {1,2,4,8}", __func__, 1);
182
0
            }
183
0
        }
184
0
    }
185
186
0
    return 0;
187
0
}
188
189
190
/*-------------------------------------------------------------*
191
 *                  Repaint gray pixels in region              *
192
 *-------------------------------------------------------------*/
193
/*!
194
 * \brief   pixColorGrayRegionsCmap()
195
 *
196
 * \param[in]    pixs               8 bpp, with colormap
197
 * \param[in]    boxa               of regions in which to apply color
198
 * \param[in]    type               L_PAINT_LIGHT, L_PAINT_DARK
199
 * \param[in]    rval, gval, bval   target color
200
 * \return  0 if OK, 1 on error
201
 *
202
 * <pre>
203
 * Notes:
204
 *      (1) This is an in-place operation.
205
 *      (2) If %type == L_PAINT_LIGHT, it colorizes non-black pixels,
206
 *          preserving antialiasing.
207
 *          If %type == L_PAINT_DARK, it colorizes non-white pixels,
208
 *          preserving antialiasing.  See pixColorGrayCmap() for details.
209
 *      (3) This can also be called through pixColorGrayRegions().
210
 *      (4) This increases the colormap size by the number of
211
 *          different gray (non-black or non-white) colors in the
212
 *          selected regions of pixs.  If there is not enough room in
213
 *          the colormap for this expansion, it returns 1 (error),
214
 *          and the caller should check the return value.
215
 *      (5) Because two boxes in %boxa can overlap, pixels that
216
 *          are colorized in the first box must be excluded in the
217
 *          second because their value exceeds the size of the map.
218
 * </pre>
219
 */
220
l_ok
221
pixColorGrayRegionsCmap(PIX     *pixs,
222
                        BOXA    *boxa,
223
                        l_int32  type,
224
                        l_int32  rval,
225
                        l_int32  gval,
226
                        l_int32  bval)
227
0
{
228
0
l_int32    i, j, k, w, h, n, nc, x1, y1, x2, y2, bw, bh, wpl;
229
0
l_int32    val, nval;
230
0
l_int32   *map;
231
0
l_uint32  *line, *data;
232
0
BOX       *box;
233
0
NUMA      *na;
234
0
PIXCMAP   *cmap;
235
236
0
    if (!pixs)
237
0
        return ERROR_INT("pixs not defined", __func__, 1);
238
0
    if (!boxa)
239
0
        return ERROR_INT("boxa not defined", __func__, 1);
240
0
    if ((cmap = pixGetColormap(pixs)) == NULL)
241
0
        return ERROR_INT("no colormap", __func__, 1);
242
0
    if (pixGetDepth(pixs) != 8)
243
0
        return ERROR_INT("depth not 8 bpp", __func__, 1);
244
0
    if (type != L_PAINT_DARK && type != L_PAINT_LIGHT)
245
0
        return ERROR_INT("invalid type", __func__, 1);
246
247
0
    nc = pixcmapGetCount(cmap);
248
0
    if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na))
249
0
        return ERROR_INT("no room; cmap full", __func__, 1);
250
0
    map = numaGetIArray(na);
251
0
    numaDestroy(&na);
252
0
    if (!map)
253
0
        return ERROR_INT("map not made", __func__, 1);
254
255
0
    pixGetDimensions(pixs, &w, &h, NULL);
256
0
    data = pixGetData(pixs);
257
0
    wpl = pixGetWpl(pixs);
258
0
    n = boxaGetCount(boxa);
259
0
    for (k = 0; k < n; k++) {
260
0
        box = boxaGetBox(boxa, k, L_CLONE);
261
0
        boxGetGeometry(box, &x1, &y1, &bw, &bh);
262
0
        x2 = x1 + bw - 1;
263
0
        y2 = y1 + bh - 1;
264
265
            /* Remap gray pixels in the region */
266
0
        for (i = y1; i <= y2; i++) {
267
0
            if (i < 0 || i >= h)  /* clip */
268
0
                continue;
269
0
            line = data + i * wpl;
270
0
            for (j = x1; j <= x2; j++) {
271
0
                if (j < 0 || j >= w)  /* clip */
272
0
                    continue;
273
0
                val = GET_DATA_BYTE(line, j);
274
0
                if (val >= nc) continue;  /* from overlapping b.b. */
275
0
                nval = map[val];
276
0
                if (nval != 256)
277
0
                    SET_DATA_BYTE(line, j, nval);
278
0
            }
279
0
        }
280
0
        boxDestroy(&box);
281
0
    }
282
283
0
    LEPT_FREE(map);
284
0
    return 0;
285
0
}
286
287
288
/*!
289
 * \brief   pixColorGrayCmap()
290
 *
291
 * \param[in]    pixs               2, 4 or 8 bpp, with colormap
292
 * \param[in]    box                [optional] region to set color; can be NULL
293
 * \param[in]    type               L_PAINT_LIGHT, L_PAINT_DARK
294
 * \param[in]    rval, gval, bval   target color
295
 * \return  0 if OK, 1 on error
296
 *
297
 * <pre>
298
 * Notes:
299
 *      (1) This is an in-place operation.
300
 *      (2) If %type == L_PAINT_LIGHT, it colorizes non-black pixels,
301
 *          preserving antialiasing.
302
 *          If %type == L_PAINT_DARK, it colorizes non-white pixels,
303
 *          preserving antialiasing.
304
 *      (3) %box gives the region to apply color; if NULL, this
305
 *          colorizes the entire image.
306
 *      (4) If the cmap is only 2 or 4 bpp, pixs is converted in-place
307
 *          to an 8 bpp cmap.  A 1 bpp cmap is not a valid input pix.
308
 *      (5) This can also be called through pixColorGray().
309
 *      (6) This operation increases the colormap size by the number of
310
 *          different gray (non-black or non-white) colors in the
311
 *          input colormap.  If there is not enough room in the colormap
312
 *          for this expansion, it returns 1 (error), and the caller
313
 *          should check the return value.
314
 *      (7) Using the darkness of each original pixel in the rect,
315
 *          it generates a new color (based on the input rgb values).
316
 *          If %type == L_PAINT_LIGHT, the new color is a (generally)
317
 *          darken-to-black version of the input rgb color, where the
318
 *          amount of darkening increases with the darkness of the
319
 *          original pixel color.
320
 *          If %type == L_PAINT_DARK, the new color is a (generally)
321
 *          faded-to-white version of the input rgb color, where the
322
 *          amount of fading increases with the brightness of the
323
 *          original pixel color.
324
 * </pre>
325
 */
326
l_ok
327
pixColorGrayCmap(PIX     *pixs,
328
                 BOX     *box,
329
                 l_int32  type,
330
                 l_int32  rval,
331
                 l_int32  gval,
332
                 l_int32  bval)
333
0
{
334
0
l_int32   w, h, d, ret;
335
0
PIX      *pixt;
336
0
BOXA     *boxa;
337
0
PIXCMAP  *cmap;
338
339
0
    if (!pixs)
340
0
        return ERROR_INT("pixs not defined", __func__, 1);
341
0
    if ((cmap = pixGetColormap(pixs)) == NULL)
342
0
        return ERROR_INT("no colormap", __func__, 1);
343
0
    pixGetDimensions(pixs, &w, &h, &d);
344
0
    if (d != 2 && d != 4 && d != 8)
345
0
        return ERROR_INT("depth not in {2, 4, 8}", __func__, 1);
346
0
    if (type != L_PAINT_DARK && type != L_PAINT_LIGHT)
347
0
        return ERROR_INT("invalid type", __func__, 1);
348
349
        /* If 2 bpp or 4 bpp, convert in-place to 8 bpp. */
350
0
    if (d == 2 || d == 4) {
351
0
        pixt = pixConvertTo8(pixs, 1);
352
0
        pixTransferAllData(pixs, &pixt, 0, 0);
353
0
    }
354
355
        /* If box == NULL, color the entire image */
356
0
    boxa = boxaCreate(1);
357
0
    if (box) {
358
0
        boxaAddBox(boxa, box, L_COPY);
359
0
    } else {
360
0
        box = boxCreate(0, 0, w, h);
361
0
        boxaAddBox(boxa, box, L_INSERT);
362
0
    }
363
0
    ret = pixColorGrayRegionsCmap(pixs, boxa, type, rval, gval, bval);
364
365
0
    boxaDestroy(&boxa);
366
0
    return ret;
367
0
}
368
369
370
/*!
371
 * \brief   pixColorGrayMaskedCmap()
372
 *
373
 * \param[in]    pixs               8 bpp, with colormap
374
 * \param[in]    pixm               1 bpp mask, through which to apply color
375
 * \param[in]    type               L_PAINT_LIGHT, L_PAINT_DARK
376
 * \param[in]    rval, gval, bval   target color
377
 * \return  0 if OK, 1 on error
378
 *
379
 * <pre>
380
 * Notes:
381
 *      (1) This is an in-place operation.
382
 *      (2) If %type == L_PAINT_LIGHT, it colorizes non-black pixels,
383
 *          preserving antialiasing.
384
 *          If %type == L_PAINT_DARK, it colorizes non-white pixels,
385
 *          preserving antialiasing.  See pixColorGrayCmap() for details.
386
 *      (3) This increases the colormap size by the number of
387
 *          different gray (non-black or non-white) colors in the
388
 *          input colormap.  If there is not enough room in the colormap
389
 *          for this expansion, it returns 1 (error).
390
 * </pre>
391
 */
392
l_ok
393
pixColorGrayMaskedCmap(PIX     *pixs,
394
                       PIX     *pixm,
395
                       l_int32  type,
396
                       l_int32  rval,
397
                       l_int32  gval,
398
                       l_int32  bval)
399
0
{
400
0
l_int32    i, j, w, h, wm, hm, wmin, hmin, wpl, wplm;
401
0
l_int32    val, nval;
402
0
l_int32   *map;
403
0
l_uint32  *line, *data, *linem, *datam;
404
0
NUMA      *na;
405
0
PIXCMAP   *cmap;
406
407
0
    if (!pixs)
408
0
        return ERROR_INT("pixs not defined", __func__, 1);
409
0
    if (!pixm || pixGetDepth(pixm) != 1)
410
0
        return ERROR_INT("pixm undefined or not 1 bpp", __func__, 1);
411
0
    if ((cmap = pixGetColormap(pixs)) == NULL)
412
0
        return ERROR_INT("no colormap", __func__, 1);
413
0
    if (pixGetDepth(pixs) != 8)
414
0
        return ERROR_INT("depth not 8 bpp", __func__, 1);
415
0
    if (type != L_PAINT_DARK && type != L_PAINT_LIGHT)
416
0
        return ERROR_INT("invalid type", __func__, 1);
417
418
0
    if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na))
419
0
        return ERROR_INT("no room; cmap full", __func__, 1);
420
0
    map = numaGetIArray(na);
421
0
    numaDestroy(&na);
422
0
    if (!map)
423
0
        return ERROR_INT("map not made", __func__, 1);
424
425
0
    pixGetDimensions(pixs, &w, &h, NULL);
426
0
    pixGetDimensions(pixm, &wm, &hm, NULL);
427
0
    if (wm != w)
428
0
        L_WARNING("wm = %d differs from w = %d\n", __func__, wm, w);
429
0
    if (hm != h)
430
0
        L_WARNING("hm = %d differs from h = %d\n", __func__, hm, h);
431
0
    wmin = L_MIN(w, wm);
432
0
    hmin = L_MIN(h, hm);
433
434
0
    data = pixGetData(pixs);
435
0
    wpl = pixGetWpl(pixs);
436
0
    datam = pixGetData(pixm);
437
0
    wplm = pixGetWpl(pixm);
438
439
        /* Remap gray pixels in the region */
440
0
    for (i = 0; i < hmin; i++) {
441
0
        line = data + i * wpl;
442
0
        linem = datam + i * wplm;
443
0
        for (j = 0; j < wmin; j++) {
444
0
            if (GET_DATA_BIT(linem, j) == 0)
445
0
                continue;
446
0
            val = GET_DATA_BYTE(line, j);
447
0
            nval = map[val];
448
0
            if (nval != 256)
449
0
                SET_DATA_BYTE(line, j, nval);
450
0
        }
451
0
    }
452
453
0
    LEPT_FREE(map);
454
0
    return 0;
455
0
}
456
457
458
/*!
459
 * \brief   addColorizedGrayToCmap()
460
 *
461
 * \param[in]    cmap              from 2 or 4 bpp pix
462
 * \param[in]    type              L_PAINT_LIGHT, L_PAINT_DARK
463
 * \param[in]    rval, gval, bval  target color
464
 * \param[out]   pna               [optional] table for mapping new cmap entries
465
 * \return  0 if OK; 1 on error; 2 if new colors will not fit in cmap.
466
 *
467
 * <pre>
468
 * Notes:
469
 *      (1) If %type == L_PAINT_LIGHT, it colorizes non-black pixels,
470
 *          preserving antialiasing.
471
 *          If %type == L_PAINT_DARK, it colorizes non-white pixels,
472
 *          preserving antialiasing.
473
 *      (2) This increases the colormap size by the number of
474
 *          different gray (non-black or non-white) colors in the
475
 *          input colormap.  If there is not enough room in the colormap
476
 *          for this expansion, it returns 1 (treated as a warning);
477
 *          the caller should check the return value.
478
 *      (3) This can be used to determine if the new colors will fit in
479
 *          the cmap, using null for &na.  Returns 0 if they fit; 2 if
480
 *          they don't fit.
481
 *      (4) The mapping table contains, for each gray color found, the
482
 *          index of the corresponding colorized pixel.  Non-gray
483
 *          pixels are assigned the invalid index 256.
484
 *      (5) See pixColorGrayCmap() for usage.
485
 * </pre>
486
 */
487
l_ok
488
addColorizedGrayToCmap(PIXCMAP  *cmap,
489
                       l_int32   type,
490
                       l_int32   rval,
491
                       l_int32   gval,
492
                       l_int32   bval,
493
                       NUMA    **pna)
494
0
{
495
0
l_int32  i, n, erval, egval, ebval, nrval, ngval, nbval, newindex;
496
0
NUMA    *na;
497
498
0
    if (pna) *pna = NULL;
499
0
    if (!cmap)
500
0
        return ERROR_INT("cmap not defined", __func__, 1);
501
0
    if (type != L_PAINT_DARK && type != L_PAINT_LIGHT)
502
0
        return ERROR_INT("invalid type", __func__, 1);
503
504
0
    n = pixcmapGetCount(cmap);
505
0
    na = numaCreate(n);
506
0
    for (i = 0; i < n; i++) {
507
0
        pixcmapGetColor(cmap, i, &erval, &egval, &ebval);
508
0
        if (type == L_PAINT_LIGHT) {
509
0
            if (erval == egval && erval == ebval && erval != 0) {
510
0
                nrval = (l_int32)(rval * (l_float32)erval / 255.);
511
0
                ngval = (l_int32)(gval * (l_float32)egval / 255.);
512
0
                nbval = (l_int32)(bval * (l_float32)ebval / 255.);
513
0
                if (pixcmapAddNewColor(cmap, nrval, ngval, nbval, &newindex)) {
514
0
                    numaDestroy(&na);
515
0
                    L_WARNING("no room; colormap full\n", __func__);
516
0
                    return 2;
517
0
                }
518
0
                numaAddNumber(na, newindex);
519
0
            } else {
520
0
                numaAddNumber(na, 256);  /* invalid number; not gray */
521
0
            }
522
0
        } else {  /* L_PAINT_DARK */
523
0
            if (erval == egval && erval == ebval && erval != 255) {
524
0
                nrval = rval +
525
0
                        (l_int32)((255. - rval) * (l_float32)erval / 255.);
526
0
                ngval = gval +
527
0
                        (l_int32)((255. - gval) * (l_float32)egval / 255.);
528
0
                nbval = bval +
529
0
                        (l_int32)((255. - bval) * (l_float32)ebval / 255.);
530
0
                if (pixcmapAddNewColor(cmap, nrval, ngval, nbval, &newindex)) {
531
0
                    numaDestroy(&na);
532
0
                    L_WARNING("no room; colormap full\n", __func__);
533
0
                    return 2;
534
0
                }
535
0
                numaAddNumber(na, newindex);
536
0
            } else {
537
0
                numaAddNumber(na, 256);  /* invalid number; not gray */
538
0
            }
539
0
        }
540
0
    }
541
542
0
    if (pna)
543
0
        *pna = na;
544
0
    else
545
0
        numaDestroy(&na);
546
0
    return 0;
547
0
}
548
549
550
/*-------------------------------------------------------------*
551
 *             Repaint selected pixels through mask            *
552
 *-------------------------------------------------------------*/
553
/*!
554
 * \brief   pixSetSelectMaskedCmap()
555
 *
556
 * \param[in]    pixs               2, 4 or 8 bpp, with colormap
557
 * \param[in]    pixm               [optional] 1 bpp mask; no-op if NULL
558
 * \param[in]    x, y               UL corner of mask relative to pixs
559
 * \param[in]    sindex             cmap index of pixels in pixs to be changed
560
 * \param[in]    rval, gval, bval   new color to substitute
561
 * \return  0 if OK, 1 on error
562
 *
563
 * <pre>
564
 * Notes:
565
 *      (1) This is an in-place operation.
566
 *      (2) This paints through the fg of pixm and replaces all pixels
567
 *          in pixs that have the value %sindex with the new color.
568
 *      (3) If pixm == NULL, a warning is given.
569
 *      (4) %sindex must be in the existing colormap; otherwise an
570
 *          error is returned.
571
 *      (5) If the new color exists in the colormap, it is used;
572
 *          otherwise, it is added to the colormap.  If the colormap
573
 *          is full, an error is returned.
574
 * </pre>
575
 */
576
l_ok
577
pixSetSelectMaskedCmap(PIX     *pixs,
578
                       PIX     *pixm,
579
                       l_int32  x,
580
                       l_int32  y,
581
                       l_int32  sindex,
582
                       l_int32  rval,
583
                       l_int32  gval,
584
                       l_int32  bval)
585
0
{
586
0
l_int32    i, j, w, h, d, n, wm, hm, wpls, wplm, val;
587
0
l_int32    index;  /* of new color to be set */
588
0
l_uint32  *lines, *linem, *datas, *datam;
589
0
PIXCMAP   *cmap;
590
591
0
    if (!pixs)
592
0
        return ERROR_INT("pixs not defined", __func__, 1);
593
0
    if ((cmap = pixGetColormap(pixs)) == NULL)
594
0
        return ERROR_INT("no colormap", __func__, 1);
595
0
    if (!pixm) {
596
0
        L_WARNING("no mask; nothing to do\n", __func__);
597
0
        return 0;
598
0
    }
599
600
0
    d = pixGetDepth(pixs);
601
0
    if (d != 2 && d != 4 && d != 8)
602
0
        return ERROR_INT("depth not in {2, 4, 8}", __func__, 1);
603
604
        /* add new color if necessary; get index of this color in cmap */
605
0
    n = pixcmapGetCount(cmap);
606
0
    if (sindex >= n)
607
0
        return ERROR_INT("sindex too large; no cmap entry", __func__, 1);
608
0
    if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */
609
0
        if (pixcmapAddColor(cmap, rval, gval, bval))
610
0
            return ERROR_INT("error adding cmap entry", __func__, 1);
611
0
        else
612
0
            index = n;  /* we've added one color */
613
0
    }
614
615
        /* replace pixel value sindex by index when fg pixel in pixmc
616
         * overlays it */
617
0
    pixGetDimensions(pixs, &w, &h, NULL);
618
0
    datas = pixGetData(pixs);
619
0
    wpls = pixGetWpl(pixs);
620
0
    wm = pixGetWidth(pixm);
621
0
    hm = pixGetHeight(pixm);
622
0
    datam = pixGetData(pixm);
623
0
    wplm = pixGetWpl(pixm);
624
0
    for (i = 0; i < hm; i++) {
625
0
        if (i + y < 0 || i + y >= h) continue;
626
0
        lines = datas + (y + i) * wpls;
627
0
        linem = datam + i * wplm;
628
0
        for (j = 0; j < wm; j++) {
629
0
            if (j + x < 0  || j + x >= w) continue;
630
0
            if (GET_DATA_BIT(linem, j)) {
631
0
                switch (d) {
632
0
                case 2:
633
0
                    val = GET_DATA_DIBIT(lines, x + j);
634
0
                    if (val == sindex)
635
0
                        SET_DATA_DIBIT(lines, x + j, index);
636
0
                    break;
637
0
                case 4:
638
0
                    val = GET_DATA_QBIT(lines, x + j);
639
0
                    if (val == sindex)
640
0
                        SET_DATA_QBIT(lines, x + j, index);
641
0
                    break;
642
0
                case 8:
643
0
                    val = GET_DATA_BYTE(lines, x + j);
644
0
                    if (val == sindex)
645
0
                        SET_DATA_BYTE(lines, x + j, index);
646
0
                    break;
647
0
                default:
648
0
                    return ERROR_INT("depth not in {1,2,4,8}", __func__, 1);
649
0
                }
650
0
            }
651
0
        }
652
0
    }
653
654
0
    return 0;
655
0
}
656
657
658
/*-------------------------------------------------------------*
659
 *               Repaint all pixels through mask               *
660
 *-------------------------------------------------------------*/
661
/*!
662
 * \brief   pixSetMaskedCmap()
663
 *
664
 * \param[in]    pixs               2, 4 or 8 bpp, colormapped
665
 * \param[in]    pixm               [optional] 1 bpp mask; no-op if NULL
666
 * \param[in]    x, y               origin of pixm relative to pixs;
667
 *                                  can be negative
668
 * \param[in]    rval, gval, bval   new color to set at each masked pixel
669
 * \return  0 if OK; 1 on error
670
 *
671
 * <pre>
672
 * Notes:
673
 *      (1) This is an in-place operation.
674
 *      (2) It paints a single color through the mask (as a stencil).
675
 *      (3) The mask origin is placed at (%x,%y) on %pixs, and the
676
 *          operation is clipped to the intersection of the mask and pixs.
677
 *      (4) If %pixm == NULL, a warning is given.
678
 *      (5) Typically, %pixm is a small binary mask located somewhere
679
 *          on the larger %pixs.
680
 *      (6) If the color is in the colormap, it is used.  Otherwise,
681
 *          it is added if possible; an error is returned if the
682
 *          colormap is already full.
683
 * </pre>
684
 */
685
l_ok
686
pixSetMaskedCmap(PIX      *pixs,
687
                 PIX      *pixm,
688
                 l_int32   x,
689
                 l_int32   y,
690
                 l_int32   rval,
691
                 l_int32   gval,
692
                 l_int32   bval)
693
0
{
694
0
l_int32    w, h, d, wpl, wm, hm, wplm;
695
0
l_int32    i, j, index;
696
0
l_uint32  *data, *datam, *line, *linem;
697
0
PIXCMAP   *cmap;
698
699
0
    if (!pixs)
700
0
        return ERROR_INT("pixs not defined", __func__, 1);
701
0
    if ((cmap = pixGetColormap(pixs)) == NULL)
702
0
        return ERROR_INT("no colormap in pixs", __func__, 1);
703
0
    if (!pixm) {
704
0
        L_WARNING("no mask; nothing to do\n", __func__);
705
0
        return 0;
706
0
    }
707
0
    d = pixGetDepth(pixs);
708
0
    if (d != 2 && d != 4 && d != 8)
709
0
        return ERROR_INT("depth not in {2,4,8}", __func__, 1);
710
0
    if (pixGetDepth(pixm) != 1)
711
0
        return ERROR_INT("pixm not 1 bpp", __func__, 1);
712
713
        /* Add new color if necessary; store in 'index' */
714
0
    if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) {  /* not found */
715
0
        if (pixcmapAddColor(cmap, rval, gval, bval))
716
0
            return ERROR_INT("no room in cmap", __func__, 1);
717
0
        index = pixcmapGetCount(cmap) - 1;
718
0
    }
719
720
0
    pixGetDimensions(pixs, &w, &h, NULL);
721
0
    wpl = pixGetWpl(pixs);
722
0
    data = pixGetData(pixs);
723
0
    pixGetDimensions(pixm, &wm, &hm, NULL);
724
0
    wplm = pixGetWpl(pixm);
725
0
    datam = pixGetData(pixm);
726
0
    for (i = 0; i < hm; i++) {
727
0
        if (i + y < 0 || i + y >= h) continue;
728
0
        line = data + (i + y) * wpl;
729
0
        linem = datam + i * wplm;
730
0
        for (j = 0; j < wm; j++) {
731
0
            if (j + x < 0  || j + x >= w) continue;
732
0
            if (GET_DATA_BIT(linem, j)) {  /* paint color */
733
0
                switch (d) {
734
0
                case 2:
735
0
                    SET_DATA_DIBIT(line, j + x, index);
736
0
                    break;
737
0
                case 4:
738
0
                    SET_DATA_QBIT(line, j + x, index);
739
0
                    break;
740
0
                case 8:
741
0
                    SET_DATA_BYTE(line, j + x, index);
742
0
                    break;
743
0
                default:
744
0
                    return ERROR_INT("depth not in {2,4,8}", __func__, 1);
745
0
                }
746
0
            }
747
0
        }
748
0
    }
749
750
0
    return 0;
751
0
}