/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 | } |