Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
