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