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