comparison mupdf-source/thirdparty/leptonica/src/paintcmap.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 {
109 l_int32 i, j, w, h, d, n, x1, y1, x2, y2, bw, bh, val, wpls;
110 l_int32 index; /* of new color to be set */
111 l_uint32 *lines, *datas;
112 PIXCMAP *cmap;
113
114 if (!pixs)
115 return ERROR_INT("pixs not defined", __func__, 1);
116 if ((cmap = pixGetColormap(pixs)) == NULL)
117 return ERROR_INT("no colormap", __func__, 1);
118 d = pixGetDepth(pixs);
119 if (d != 1 && d != 2 && d != 4 && d != 8)
120 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 n = pixcmapGetCount(cmap);
124 if (sindex >= n)
125 return ERROR_INT("sindex too large; no cmap entry", __func__, 1);
126 if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */
127 if (pixcmapAddColor(cmap, rval, gval, bval))
128 return ERROR_INT("error adding cmap entry", __func__, 1);
129 else
130 index = n; /* we've added one color */
131 }
132
133 /* Determine the region of substitution */
134 pixGetDimensions(pixs, &w, &h, NULL);
135 if (!box) {
136 x1 = y1 = 0;
137 x2 = w;
138 y2 = h;
139 } else {
140 boxGetGeometry(box, &x1, &y1, &bw, &bh);
141 x2 = x1 + bw - 1;
142 y2 = y1 + bh - 1;
143 }
144
145 /* Replace pixel value sindex by index in the region */
146 datas = pixGetData(pixs);
147 wpls = pixGetWpl(pixs);
148 for (i = y1; i <= y2; i++) {
149 if (i < 0 || i >= h) /* clip */
150 continue;
151 lines = datas + i * wpls;
152 for (j = x1; j <= x2; j++) {
153 if (j < 0 || j >= w) /* clip */
154 continue;
155 switch (d) {
156 case 1:
157 val = GET_DATA_BIT(lines, j);
158 if (val == sindex) {
159 if (index == 0)
160 CLEAR_DATA_BIT(lines, j);
161 else
162 SET_DATA_BIT(lines, j);
163 }
164 break;
165 case 2:
166 val = GET_DATA_DIBIT(lines, j);
167 if (val == sindex)
168 SET_DATA_DIBIT(lines, j, index);
169 break;
170 case 4:
171 val = GET_DATA_QBIT(lines, j);
172 if (val == sindex)
173 SET_DATA_QBIT(lines, j, index);
174 break;
175 case 8:
176 val = GET_DATA_BYTE(lines, j);
177 if (val == sindex)
178 SET_DATA_BYTE(lines, j, index);
179 break;
180 default:
181 return ERROR_INT("depth not in {1,2,4,8}", __func__, 1);
182 }
183 }
184 }
185
186 return 0;
187 }
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 {
228 l_int32 i, j, k, w, h, n, nc, x1, y1, x2, y2, bw, bh, wpl;
229 l_int32 val, nval;
230 l_int32 *map;
231 l_uint32 *line, *data;
232 BOX *box;
233 NUMA *na;
234 PIXCMAP *cmap;
235
236 if (!pixs)
237 return ERROR_INT("pixs not defined", __func__, 1);
238 if (!boxa)
239 return ERROR_INT("boxa not defined", __func__, 1);
240 if ((cmap = pixGetColormap(pixs)) == NULL)
241 return ERROR_INT("no colormap", __func__, 1);
242 if (pixGetDepth(pixs) != 8)
243 return ERROR_INT("depth not 8 bpp", __func__, 1);
244 if (type != L_PAINT_DARK && type != L_PAINT_LIGHT)
245 return ERROR_INT("invalid type", __func__, 1);
246
247 nc = pixcmapGetCount(cmap);
248 if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na))
249 return ERROR_INT("no room; cmap full", __func__, 1);
250 map = numaGetIArray(na);
251 numaDestroy(&na);
252 if (!map)
253 return ERROR_INT("map not made", __func__, 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_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 {
334 l_int32 w, h, d, ret;
335 PIX *pixt;
336 BOXA *boxa;
337 PIXCMAP *cmap;
338
339 if (!pixs)
340 return ERROR_INT("pixs not defined", __func__, 1);
341 if ((cmap = pixGetColormap(pixs)) == NULL)
342 return ERROR_INT("no colormap", __func__, 1);
343 pixGetDimensions(pixs, &w, &h, &d);
344 if (d != 2 && d != 4 && d != 8)
345 return ERROR_INT("depth not in {2, 4, 8}", __func__, 1);
346 if (type != L_PAINT_DARK && type != L_PAINT_LIGHT)
347 return ERROR_INT("invalid type", __func__, 1);
348
349 /* If 2 bpp or 4 bpp, convert in-place to 8 bpp. */
350 if (d == 2 || d == 4) {
351 pixt = pixConvertTo8(pixs, 1);
352 pixTransferAllData(pixs, &pixt, 0, 0);
353 }
354
355 /* If box == NULL, color the entire image */
356 boxa = boxaCreate(1);
357 if (box) {
358 boxaAddBox(boxa, box, L_COPY);
359 } else {
360 box = boxCreate(0, 0, w, h);
361 boxaAddBox(boxa, box, L_INSERT);
362 }
363 ret = pixColorGrayRegionsCmap(pixs, boxa, type, rval, gval, bval);
364
365 boxaDestroy(&boxa);
366 return ret;
367 }
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 {
400 l_int32 i, j, w, h, wm, hm, wmin, hmin, wpl, wplm;
401 l_int32 val, nval;
402 l_int32 *map;
403 l_uint32 *line, *data, *linem, *datam;
404 NUMA *na;
405 PIXCMAP *cmap;
406
407 if (!pixs)
408 return ERROR_INT("pixs not defined", __func__, 1);
409 if (!pixm || pixGetDepth(pixm) != 1)
410 return ERROR_INT("pixm undefined or not 1 bpp", __func__, 1);
411 if ((cmap = pixGetColormap(pixs)) == NULL)
412 return ERROR_INT("no colormap", __func__, 1);
413 if (pixGetDepth(pixs) != 8)
414 return ERROR_INT("depth not 8 bpp", __func__, 1);
415 if (type != L_PAINT_DARK && type != L_PAINT_LIGHT)
416 return ERROR_INT("invalid type", __func__, 1);
417
418 if (addColorizedGrayToCmap(cmap, type, rval, gval, bval, &na))
419 return ERROR_INT("no room; cmap full", __func__, 1);
420 map = numaGetIArray(na);
421 numaDestroy(&na);
422 if (!map)
423 return ERROR_INT("map not made", __func__, 1);
424
425 pixGetDimensions(pixs, &w, &h, NULL);
426 pixGetDimensions(pixm, &wm, &hm, NULL);
427 if (wm != w)
428 L_WARNING("wm = %d differs from w = %d\n", __func__, wm, w);
429 if (hm != h)
430 L_WARNING("hm = %d differs from h = %d\n", __func__, hm, h);
431 wmin = L_MIN(w, wm);
432 hmin = L_MIN(h, hm);
433
434 data = pixGetData(pixs);
435 wpl = pixGetWpl(pixs);
436 datam = pixGetData(pixm);
437 wplm = pixGetWpl(pixm);
438
439 /* Remap gray pixels in the region */
440 for (i = 0; i < hmin; i++) {
441 line = data + i * wpl;
442 linem = datam + i * wplm;
443 for (j = 0; j < wmin; j++) {
444 if (GET_DATA_BIT(linem, j) == 0)
445 continue;
446 val = GET_DATA_BYTE(line, j);
447 nval = map[val];
448 if (nval != 256)
449 SET_DATA_BYTE(line, j, nval);
450 }
451 }
452
453 LEPT_FREE(map);
454 return 0;
455 }
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 {
495 l_int32 i, n, erval, egval, ebval, nrval, ngval, nbval, newindex;
496 NUMA *na;
497
498 if (pna) *pna = NULL;
499 if (!cmap)
500 return ERROR_INT("cmap not defined", __func__, 1);
501 if (type != L_PAINT_DARK && type != L_PAINT_LIGHT)
502 return ERROR_INT("invalid type", __func__, 1);
503
504 n = pixcmapGetCount(cmap);
505 na = numaCreate(n);
506 for (i = 0; i < n; i++) {
507 pixcmapGetColor(cmap, i, &erval, &egval, &ebval);
508 if (type == L_PAINT_LIGHT) {
509 if (erval == egval && erval == ebval && erval != 0) {
510 nrval = (l_int32)(rval * (l_float32)erval / 255.);
511 ngval = (l_int32)(gval * (l_float32)egval / 255.);
512 nbval = (l_int32)(bval * (l_float32)ebval / 255.);
513 if (pixcmapAddNewColor(cmap, nrval, ngval, nbval, &newindex)) {
514 numaDestroy(&na);
515 L_WARNING("no room; colormap full\n", __func__);
516 return 2;
517 }
518 numaAddNumber(na, newindex);
519 } else {
520 numaAddNumber(na, 256); /* invalid number; not gray */
521 }
522 } else { /* L_PAINT_DARK */
523 if (erval == egval && erval == ebval && erval != 255) {
524 nrval = rval +
525 (l_int32)((255. - rval) * (l_float32)erval / 255.);
526 ngval = gval +
527 (l_int32)((255. - gval) * (l_float32)egval / 255.);
528 nbval = bval +
529 (l_int32)((255. - bval) * (l_float32)ebval / 255.);
530 if (pixcmapAddNewColor(cmap, nrval, ngval, nbval, &newindex)) {
531 numaDestroy(&na);
532 L_WARNING("no room; colormap full\n", __func__);
533 return 2;
534 }
535 numaAddNumber(na, newindex);
536 } else {
537 numaAddNumber(na, 256); /* invalid number; not gray */
538 }
539 }
540 }
541
542 if (pna)
543 *pna = na;
544 else
545 numaDestroy(&na);
546 return 0;
547 }
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 {
586 l_int32 i, j, w, h, d, n, wm, hm, wpls, wplm, val;
587 l_int32 index; /* of new color to be set */
588 l_uint32 *lines, *linem, *datas, *datam;
589 PIXCMAP *cmap;
590
591 if (!pixs)
592 return ERROR_INT("pixs not defined", __func__, 1);
593 if ((cmap = pixGetColormap(pixs)) == NULL)
594 return ERROR_INT("no colormap", __func__, 1);
595 if (!pixm) {
596 L_WARNING("no mask; nothing to do\n", __func__);
597 return 0;
598 }
599
600 d = pixGetDepth(pixs);
601 if (d != 2 && d != 4 && d != 8)
602 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 n = pixcmapGetCount(cmap);
606 if (sindex >= n)
607 return ERROR_INT("sindex too large; no cmap entry", __func__, 1);
608 if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */
609 if (pixcmapAddColor(cmap, rval, gval, bval))
610 return ERROR_INT("error adding cmap entry", __func__, 1);
611 else
612 index = n; /* we've added one color */
613 }
614
615 /* replace pixel value sindex by index when fg pixel in pixmc
616 * overlays it */
617 pixGetDimensions(pixs, &w, &h, NULL);
618 datas = pixGetData(pixs);
619 wpls = pixGetWpl(pixs);
620 wm = pixGetWidth(pixm);
621 hm = pixGetHeight(pixm);
622 datam = pixGetData(pixm);
623 wplm = pixGetWpl(pixm);
624 for (i = 0; i < hm; i++) {
625 if (i + y < 0 || i + y >= h) continue;
626 lines = datas + (y + i) * wpls;
627 linem = datam + i * wplm;
628 for (j = 0; j < wm; j++) {
629 if (j + x < 0 || j + x >= w) continue;
630 if (GET_DATA_BIT(linem, j)) {
631 switch (d) {
632 case 2:
633 val = GET_DATA_DIBIT(lines, x + j);
634 if (val == sindex)
635 SET_DATA_DIBIT(lines, x + j, index);
636 break;
637 case 4:
638 val = GET_DATA_QBIT(lines, x + j);
639 if (val == sindex)
640 SET_DATA_QBIT(lines, x + j, index);
641 break;
642 case 8:
643 val = GET_DATA_BYTE(lines, x + j);
644 if (val == sindex)
645 SET_DATA_BYTE(lines, x + j, index);
646 break;
647 default:
648 return ERROR_INT("depth not in {1,2,4,8}", __func__, 1);
649 }
650 }
651 }
652 }
653
654 return 0;
655 }
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 {
694 l_int32 w, h, d, wpl, wm, hm, wplm;
695 l_int32 i, j, index;
696 l_uint32 *data, *datam, *line, *linem;
697 PIXCMAP *cmap;
698
699 if (!pixs)
700 return ERROR_INT("pixs not defined", __func__, 1);
701 if ((cmap = pixGetColormap(pixs)) == NULL)
702 return ERROR_INT("no colormap in pixs", __func__, 1);
703 if (!pixm) {
704 L_WARNING("no mask; nothing to do\n", __func__);
705 return 0;
706 }
707 d = pixGetDepth(pixs);
708 if (d != 2 && d != 4 && d != 8)
709 return ERROR_INT("depth not in {2,4,8}", __func__, 1);
710 if (pixGetDepth(pixm) != 1)
711 return ERROR_INT("pixm not 1 bpp", __func__, 1);
712
713 /* Add new color if necessary; store in 'index' */
714 if (pixcmapGetIndex(cmap, rval, gval, bval, &index)) { /* not found */
715 if (pixcmapAddColor(cmap, rval, gval, bval))
716 return ERROR_INT("no room in cmap", __func__, 1);
717 index = pixcmapGetCount(cmap) - 1;
718 }
719
720 pixGetDimensions(pixs, &w, &h, NULL);
721 wpl = pixGetWpl(pixs);
722 data = pixGetData(pixs);
723 pixGetDimensions(pixm, &wm, &hm, NULL);
724 wplm = pixGetWpl(pixm);
725 datam = pixGetData(pixm);
726 for (i = 0; i < hm; i++) {
727 if (i + y < 0 || i + y >= h) continue;
728 line = data + (i + y) * wpl;
729 linem = datam + i * wplm;
730 for (j = 0; j < wm; j++) {
731 if (j + x < 0 || j + x >= w) continue;
732 if (GET_DATA_BIT(linem, j)) { /* paint color */
733 switch (d) {
734 case 2:
735 SET_DATA_DIBIT(line, j + x, index);
736 break;
737 case 4:
738 SET_DATA_QBIT(line, j + x, index);
739 break;
740 case 8:
741 SET_DATA_BYTE(line, j + x, index);
742 break;
743 default:
744 return ERROR_INT("depth not in {2,4,8}", __func__, 1);
745 }
746 }
747 }
748 }
749
750 return 0;
751 }