Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/pixconv.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 pixconv.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * These functions convert between images of different types | |
| 32 * without scaling. | |
| 33 * | |
| 34 * Conversion from 8 bpp grayscale to 1, 2, 4 and 8 bpp | |
| 35 * PIX *pixThreshold8() | |
| 36 * | |
| 37 * Conversion from colormap to full color or grayscale | |
| 38 * PIX *pixRemoveColormapGeneral() | |
| 39 * PIX *pixRemoveColormap() | |
| 40 * | |
| 41 * Add colormap losslessly (8 to 8) | |
| 42 * l_int32 pixAddGrayColormap8() | |
| 43 * PIX *pixAddMinimalGrayColormap8() | |
| 44 * | |
| 45 * Conversion from RGB color to 8 bit gray | |
| 46 * PIX *pixConvertRGBToLuminance() | |
| 47 * PIX *pixConvertRGBToGrayGeneral() | |
| 48 * PIX *pixConvertRGBToGray() | |
| 49 * PIX *pixConvertRGBToGrayFast() | |
| 50 * PIX *pixConvertRGBToGrayMinMax() | |
| 51 * PIX *pixConvertRGBToGraySatBoost() | |
| 52 * PIX *pixConvertRGBToGrayArb() | |
| 53 * PIX *pixConvertRGBToBinaryArb() | |
| 54 * | |
| 55 * Conversion from grayscale to colormap | |
| 56 * PIX *pixConvertGrayToColormap() -- 2, 4, 8 bpp | |
| 57 * PIX *pixConvertGrayToColormap8() -- 8 bpp only | |
| 58 * | |
| 59 * Colorizing conversion from grayscale to color | |
| 60 * PIX *pixColorizeGray() -- 8 bpp or cmapped | |
| 61 * | |
| 62 * Conversion from RGB color to colormap | |
| 63 * PIX *pixConvertRGBToColormap() | |
| 64 * | |
| 65 * Conversion from colormap to 1 bpp | |
| 66 * PIX *pixConvertCmapTo1() | |
| 67 * | |
| 68 * Quantization for relatively small number of colors in source | |
| 69 * l_int32 pixQuantizeIfFewColors() | |
| 70 * | |
| 71 * Conversion from 16 bpp to 8 bpp | |
| 72 * PIX *pixConvert16To8() | |
| 73 * | |
| 74 * Conversion from grayscale to false color | |
| 75 * PIX *pixConvertGrayToFalseColor() | |
| 76 * | |
| 77 * Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp | |
| 78 * PIX *pixUnpackBinary() | |
| 79 * PIX *pixConvert1To16() | |
| 80 * PIX *pixConvert1To32() | |
| 81 * | |
| 82 * Unpacking conversion from 1 bpp to 2 bpp | |
| 83 * PIX *pixConvert1To2Cmap() | |
| 84 * PIX *pixConvert1To2() | |
| 85 * | |
| 86 * Unpacking conversion from 1 bpp to 4 bpp | |
| 87 * PIX *pixConvert1To4Cmap() | |
| 88 * PIX *pixConvert1To4() | |
| 89 * | |
| 90 * Unpacking conversion from 1, 2 and 4 bpp to 8 bpp | |
| 91 * PIX *pixConvert1To8() | |
| 92 * PIX *pixConvert2To8() | |
| 93 * PIX *pixConvert4To8() | |
| 94 * | |
| 95 * Unpacking conversion from 8 bpp to 16 bpp | |
| 96 * PIX *pixConvert8To16() | |
| 97 * | |
| 98 * Top-level conversion to 1 bpp | |
| 99 * PIX *pixConvertTo1Adaptive() | |
| 100 * PIX *pixConvertTo1() | |
| 101 * PIX *pixConvertTo1BySampling() | |
| 102 * | |
| 103 * Top-level conversion to 2 bpp | |
| 104 * PIX *pixConvertTo2() | |
| 105 * PIX *pixConvert8To2() | |
| 106 * | |
| 107 * Top-level conversion to 4 bpp | |
| 108 * PIX *pixConvertTo4() | |
| 109 * PIX *pixConvert8To4() | |
| 110 * | |
| 111 * Top-level conversion to 8 bpp | |
| 112 * PIX *pixConvertTo8() | |
| 113 * PIX *pixConvertTo8BySampling() | |
| 114 * PIX *pixConvertTo8Colormap() | |
| 115 * | |
| 116 * Top-level conversion to 16 bpp | |
| 117 * PIX *pixConvertTo16() | |
| 118 * | |
| 119 * Top-level conversion to 32 bpp (RGB) | |
| 120 * PIX *pixConvertTo32() *** | |
| 121 * PIX *pixConvertTo32BySampling() *** | |
| 122 * PIX *pixConvert8To32() *** | |
| 123 * | |
| 124 * Top-level conversion to 8 or 32 bpp, without colormap | |
| 125 * PIX *pixConvertTo8Or32 | |
| 126 * | |
| 127 * Conversion between 24 bpp and 32 bpp rgb | |
| 128 * PIX *pixConvert24To32() | |
| 129 * PIX *pixConvert32To24() | |
| 130 * | |
| 131 * Conversion between 32 bpp (1 spp) and 16 or 8 bpp | |
| 132 * PIX *pixConvert32To16() | |
| 133 * PIX *pixConvert32To8() | |
| 134 * | |
| 135 * Removal of alpha component by blending with white background | |
| 136 * PIX *pixRemoveAlpha() | |
| 137 * | |
| 138 * Addition of alpha component to 1 bpp | |
| 139 * PIX *pixAddAlphaTo1bpp() | |
| 140 * | |
| 141 * Lossless depth conversion (unpacking) | |
| 142 * PIX *pixConvertLossless() | |
| 143 * | |
| 144 * Conversion for printing in PostScript | |
| 145 * PIX *pixConvertForPSWrap() | |
| 146 * | |
| 147 * Scaling conversion to subpixel RGB | |
| 148 * PIX *pixConvertToSubpixelRGB() | |
| 149 * PIX *pixConvertGrayToSubpixelRGB() | |
| 150 * PIX *pixConvertColorToSubpixelRGB() | |
| 151 * | |
| 152 * Setting neutral point for min/max boost conversion to gray | |
| 153 * void l_setNeutralBoostVal() | |
| 154 * </pre> | |
| 155 */ | |
| 156 | |
| 157 #ifdef HAVE_CONFIG_H | |
| 158 #include <config_auto.h> | |
| 159 #endif /* HAVE_CONFIG_H */ | |
| 160 | |
| 161 #include <string.h> | |
| 162 #include <math.h> | |
| 163 #include "allheaders.h" | |
| 164 | |
| 165 /* ------- Set neutral point for min/max boost conversion to gray ------ */ | |
| 166 /* Call l_setNeutralBoostVal() to change this */ | |
| 167 static l_int32 var_NEUTRAL_BOOST_VAL = 180; | |
| 168 | |
| 169 | |
| 170 #ifndef NO_CONSOLE_IO | |
| 171 #define DEBUG_CONVERT_TO_COLORMAP 0 | |
| 172 #define DEBUG_UNROLLING 0 | |
| 173 #endif /* ~NO_CONSOLE_IO */ | |
| 174 | |
| 175 | |
| 176 /*-------------------------------------------------------------* | |
| 177 * Conversion from 8 bpp grayscale to 1, 2 4 and 8 bpp * | |
| 178 *-------------------------------------------------------------*/ | |
| 179 /*! | |
| 180 * \brief pixThreshold8() | |
| 181 * | |
| 182 * \param[in] pixs 8 bpp grayscale | |
| 183 * \param[in] d destination depth: 1, 2, 4 or 8 | |
| 184 * \param[in] nlevels number of levels to be used for colormap | |
| 185 * \param[in] cmapflag 1 if makes colormap; 0 otherwise | |
| 186 * \return pixd thresholded with standard dest thresholds, | |
| 187 * or NULL on error | |
| 188 * | |
| 189 * <pre> | |
| 190 * Notes: | |
| 191 * (1) This uses, by default, equally spaced "target" values | |
| 192 * that depend on the number of levels, with thresholds | |
| 193 * halfway between. For N levels, with separation (N-1)/255, | |
| 194 * there are N-1 fixed thresholds. | |
| 195 * (2) For 1 bpp destination, the number of levels can only be 2 | |
| 196 * and if a cmap is made, black is (0,0,0) and white | |
| 197 * is (255,255,255), which is opposite to the convention | |
| 198 * without a colormap. | |
| 199 * (3) For 1, 2 and 4 bpp, the nlevels arg is used if a colormap | |
| 200 * is made; otherwise, we take the most significant bits | |
| 201 * from the src that will fit in the dest. | |
| 202 * (4) For 8 bpp, the input pixs is quantized to nlevels. The | |
| 203 * dest quantized with that mapping, either through a colormap | |
| 204 * table or directly with 8 bit values. | |
| 205 * (5) Typically you should not use make a colormap for 1 bpp dest. | |
| 206 * (6) This is not dithering. Each pixel is treated independently. | |
| 207 * </pre> | |
| 208 */ | |
| 209 PIX * | |
| 210 pixThreshold8(PIX *pixs, | |
| 211 l_int32 d, | |
| 212 l_int32 nlevels, | |
| 213 l_int32 cmapflag) | |
| 214 { | |
| 215 PIX *pixd; | |
| 216 PIXCMAP *cmap; | |
| 217 | |
| 218 if (!pixs) | |
| 219 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 220 if (pixGetDepth(pixs) != 8) | |
| 221 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 222 if (cmapflag && nlevels < 2) | |
| 223 return (PIX *)ERROR_PTR("nlevels must be at least 2", __func__, NULL); | |
| 224 | |
| 225 switch (d) { | |
| 226 case 1: | |
| 227 pixd = pixThresholdToBinary(pixs, 128); | |
| 228 if (cmapflag) { | |
| 229 cmap = pixcmapCreateLinear(1, 2); | |
| 230 pixSetColormap(pixd, cmap); | |
| 231 } | |
| 232 break; | |
| 233 case 2: | |
| 234 pixd = pixThresholdTo2bpp(pixs, nlevels, cmapflag); | |
| 235 break; | |
| 236 case 4: | |
| 237 pixd = pixThresholdTo4bpp(pixs, nlevels, cmapflag); | |
| 238 break; | |
| 239 case 8: | |
| 240 pixd = pixThresholdOn8bpp(pixs, nlevels, cmapflag); | |
| 241 break; | |
| 242 default: | |
| 243 return (PIX *)ERROR_PTR("d must be in {1,2,4,8}", __func__, NULL); | |
| 244 } | |
| 245 | |
| 246 if (!pixd) | |
| 247 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 248 pixCopyInputFormat(pixd, pixs); | |
| 249 return pixd; | |
| 250 } | |
| 251 | |
| 252 | |
| 253 /*-------------------------------------------------------------* | |
| 254 * Conversion from colormapped pix * | |
| 255 *-------------------------------------------------------------*/ | |
| 256 /*! | |
| 257 * \brief pixRemoveColormapGeneral() | |
| 258 * | |
| 259 * \param[in] pixs any depth, with or without colormap | |
| 260 * \param[in] type REMOVE_CMAP_TO_BINARY, | |
| 261 * REMOVE_CMAP_TO_GRAYSCALE, | |
| 262 * REMOVE_CMAP_TO_FULL_COLOR, | |
| 263 * REMOVE_CMAP_WITH_ALPHA, | |
| 264 * REMOVE_CMAP_BASED_ON_SRC | |
| 265 * \param[in] ifnocmap L_CLONE, L_COPY | |
| 266 * \return pixd always a new pix; without colormap, or NULL on error | |
| 267 * | |
| 268 * <pre> | |
| 269 * Notes: | |
| 270 * (1) Convenience function that allows choice between returning | |
| 271 * a clone or a copy if pixs does not have a colormap. | |
| 272 * (2) See pixRemoveColormap(). | |
| 273 * </pre> | |
| 274 */ | |
| 275 PIX * | |
| 276 pixRemoveColormapGeneral(PIX *pixs, | |
| 277 l_int32 type, | |
| 278 l_int32 ifnocmap) | |
| 279 { | |
| 280 if (!pixs) | |
| 281 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 282 if (ifnocmap != L_CLONE && ifnocmap != L_COPY) | |
| 283 return (PIX *)ERROR_PTR("invalid value for ifnocmap", __func__, NULL); | |
| 284 | |
| 285 if (pixGetColormap(pixs)) | |
| 286 return pixRemoveColormap(pixs, type); | |
| 287 | |
| 288 if (ifnocmap == L_CLONE) | |
| 289 return pixClone(pixs); | |
| 290 else | |
| 291 return pixCopy(NULL, pixs); | |
| 292 } | |
| 293 | |
| 294 | |
| 295 /*! | |
| 296 * \brief pixRemoveColormap() | |
| 297 * | |
| 298 * \param[in] pixs see restrictions below | |
| 299 * \param[in] type REMOVE_CMAP_TO_BINARY, | |
| 300 * REMOVE_CMAP_TO_GRAYSCALE, | |
| 301 * REMOVE_CMAP_TO_FULL_COLOR, | |
| 302 * REMOVE_CMAP_WITH_ALPHA, | |
| 303 * REMOVE_CMAP_BASED_ON_SRC | |
| 304 * \return pixd without colormap, or NULL on error | |
| 305 * | |
| 306 * <pre> | |
| 307 * Notes: | |
| 308 * (1) If pixs does not have a colormap, a clone is returned. | |
| 309 * (2) Otherwise, the input pixs is restricted to 1, 2, 4 or 8 bpp. | |
| 310 * (3) Use REMOVE_CMAP_TO_BINARY only on 1 bpp pix. | |
| 311 * (4) For grayscale conversion from RGB, use a weighted average | |
| 312 * of RGB values, and always return an 8 bpp pix, regardless | |
| 313 * of whether the input pixs depth is 2, 4 or 8 bpp. | |
| 314 * (5) REMOVE_CMAP_TO_FULL_COLOR ignores the alpha component and | |
| 315 * returns a 32 bpp pix with spp == 3 and the alpha bytes are 0. | |
| 316 * (6) For REMOVE_CMAP_BASED_ON_SRC, if there is no color, this | |
| 317 * returns either a 1 bpp or 8 bpp grayscale pix. | |
| 318 * If there is color, this returns a 32 bpp pix, with either: | |
| 319 * * 3 spp, if the alpha values are all 255 (opaque), or | |
| 320 * * 4 spp (preserving the alpha), if any alpha values are not 255. | |
| 321 * </pre> | |
| 322 */ | |
| 323 PIX * | |
| 324 pixRemoveColormap(PIX *pixs, | |
| 325 l_int32 type) | |
| 326 { | |
| 327 l_int32 sval, rval, gval, bval, val0, val1; | |
| 328 l_int32 i, j, k, w, h, d, wpls, wpld, ncolors, nalloc, count; | |
| 329 l_int32 opaque, colorfound, blackwhite; | |
| 330 l_int32 *rmap, *gmap, *bmap, *amap; | |
| 331 l_uint32 *datas, *lines, *datad, *lined, *lut, *graymap; | |
| 332 l_uint32 sword, dword; | |
| 333 PIXCMAP *cmap; | |
| 334 PIX *pixd; | |
| 335 | |
| 336 if (!pixs) | |
| 337 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 338 if ((cmap = pixGetColormap(pixs)) == NULL) | |
| 339 return pixClone(pixs); | |
| 340 if (type != REMOVE_CMAP_TO_BINARY && | |
| 341 type != REMOVE_CMAP_TO_GRAYSCALE && | |
| 342 type != REMOVE_CMAP_TO_FULL_COLOR && | |
| 343 type != REMOVE_CMAP_WITH_ALPHA && | |
| 344 type != REMOVE_CMAP_BASED_ON_SRC) { | |
| 345 L_WARNING("Invalid type; converting based on src\n", __func__); | |
| 346 type = REMOVE_CMAP_BASED_ON_SRC; | |
| 347 } | |
| 348 pixGetDimensions(pixs, &w, &h, &d); | |
| 349 if (d != 1 && d != 2 && d != 4 && d != 8) | |
| 350 return (PIX *)ERROR_PTR("pixs must be {1,2,4,8} bpp", __func__, NULL); | |
| 351 | |
| 352 ncolors = pixcmapGetCount(cmap); | |
| 353 nalloc = 1 << d; /* allocate for max size in case of pixel corruption */ | |
| 354 if (ncolors > nalloc) | |
| 355 return (PIX *)ERROR_PTR("too many colors for pixel depth", | |
| 356 __func__, NULL); | |
| 357 | |
| 358 if (pixcmapToArrays(cmap, &rmap, &gmap, &bmap, &amap)) | |
| 359 return (PIX *)ERROR_PTR("colormap arrays not made", __func__, NULL); | |
| 360 | |
| 361 if (d != 1 && type == REMOVE_CMAP_TO_BINARY) { | |
| 362 L_WARNING("not 1 bpp; can't remove cmap to binary\n", __func__); | |
| 363 type = REMOVE_CMAP_BASED_ON_SRC; | |
| 364 } | |
| 365 | |
| 366 /* Select output type depending on colormap content */ | |
| 367 if (type == REMOVE_CMAP_BASED_ON_SRC) { | |
| 368 pixcmapIsOpaque(cmap, &opaque); | |
| 369 pixcmapHasColor(cmap, &colorfound); | |
| 370 pixcmapIsBlackAndWhite(cmap, &blackwhite); | |
| 371 if (!opaque) { /* save the alpha */ | |
| 372 type = REMOVE_CMAP_WITH_ALPHA; | |
| 373 } else if (colorfound) { | |
| 374 type = REMOVE_CMAP_TO_FULL_COLOR; | |
| 375 } else { /* opaque and no color */ | |
| 376 if (d == 1 && blackwhite) /* can binarize without loss */ | |
| 377 type = REMOVE_CMAP_TO_BINARY; | |
| 378 else | |
| 379 type = REMOVE_CMAP_TO_GRAYSCALE; | |
| 380 } | |
| 381 } | |
| 382 | |
| 383 datas = pixGetData(pixs); | |
| 384 wpls = pixGetWpl(pixs); | |
| 385 if (type == REMOVE_CMAP_TO_BINARY) { | |
| 386 if ((pixd = pixCopy(NULL, pixs)) == NULL) { | |
| 387 L_ERROR("pixd not made\n", __func__); | |
| 388 goto cleanup_arrays; | |
| 389 } | |
| 390 pixcmapGetColor(cmap, 0, &rval, &gval, &bval); | |
| 391 val0 = rval + gval + bval; | |
| 392 pixcmapGetColor(cmap, 1, &rval, &gval, &bval); | |
| 393 val1 = rval + gval + bval; | |
| 394 if (val0 < val1) /* photometrically inverted from standard */ | |
| 395 pixInvert(pixd, pixd); | |
| 396 pixDestroyColormap(pixd); | |
| 397 } else if (type == REMOVE_CMAP_TO_GRAYSCALE) { | |
| 398 if ((pixd = pixCreate(w, h, 8)) == NULL) { | |
| 399 L_ERROR("pixd not made\n", __func__); | |
| 400 goto cleanup_arrays; | |
| 401 } | |
| 402 pixCopyResolution(pixd, pixs); | |
| 403 pixCopyInputFormat(pixd, pixs); | |
| 404 datad = pixGetData(pixd); | |
| 405 wpld = pixGetWpl(pixd); | |
| 406 graymap = (l_uint32 *)LEPT_CALLOC(nalloc, sizeof(l_uint32)); | |
| 407 for (i = 0; i < ncolors; i++) { | |
| 408 graymap[i] = (l_uint32)(L_RED_WEIGHT * rmap[i] + | |
| 409 L_GREEN_WEIGHT * gmap[i] + | |
| 410 L_BLUE_WEIGHT * bmap[i] + 0.5); | |
| 411 } | |
| 412 for (i = 0; i < h; i++) { | |
| 413 lines = datas + i * wpls; | |
| 414 lined = datad + i * wpld; | |
| 415 switch (d) /* depth test above; no default permitted */ | |
| 416 { | |
| 417 case 8: | |
| 418 /* Unrolled 4x */ | |
| 419 for (j = 0, count = 0; j + 3 < w; j += 4, count++) { | |
| 420 sword = lines[count]; | |
| 421 dword = (graymap[(sword >> 24) & 0xff] << 24) | | |
| 422 (graymap[(sword >> 16) & 0xff] << 16) | | |
| 423 (graymap[(sword >> 8) & 0xff] << 8) | | |
| 424 graymap[sword & 0xff]; | |
| 425 lined[count] = dword; | |
| 426 } | |
| 427 /* Cleanup partial word */ | |
| 428 for (; j < w; j++) { | |
| 429 sval = GET_DATA_BYTE(lines, j); | |
| 430 gval = graymap[sval]; | |
| 431 SET_DATA_BYTE(lined, j, gval); | |
| 432 } | |
| 433 #if DEBUG_UNROLLING | |
| 434 #define CHECK_VALUE(a, b, c) if (GET_DATA_BYTE(a, b) != c) { \ | |
| 435 lept_stderr("Error: mismatch at %d, %d vs %d\n", \ | |
| 436 j, GET_DATA_BYTE(a, b), c); } | |
| 437 for (j = 0; j < w; j++) { | |
| 438 sval = GET_DATA_BYTE(lines, j); | |
| 439 gval = graymap[sval]; | |
| 440 CHECK_VALUE(lined, j, gval); | |
| 441 } | |
| 442 #endif | |
| 443 break; | |
| 444 case 4: | |
| 445 /* Unrolled 8x */ | |
| 446 for (j = 0, count = 0; j + 7 < w; j += 8, count++) { | |
| 447 sword = lines[count]; | |
| 448 dword = (graymap[(sword >> 28) & 0xf] << 24) | | |
| 449 (graymap[(sword >> 24) & 0xf] << 16) | | |
| 450 (graymap[(sword >> 20) & 0xf] << 8) | | |
| 451 graymap[(sword >> 16) & 0xf]; | |
| 452 lined[2 * count] = dword; | |
| 453 dword = (graymap[(sword >> 12) & 0xf] << 24) | | |
| 454 (graymap[(sword >> 8) & 0xf] << 16) | | |
| 455 (graymap[(sword >> 4) & 0xf] << 8) | | |
| 456 graymap[sword & 0xf]; | |
| 457 lined[2 * count + 1] = dword; | |
| 458 } | |
| 459 /* Cleanup partial word */ | |
| 460 for (; j < w; j++) { | |
| 461 sval = GET_DATA_QBIT(lines, j); | |
| 462 gval = graymap[sval]; | |
| 463 SET_DATA_BYTE(lined, j, gval); | |
| 464 } | |
| 465 #if DEBUG_UNROLLING | |
| 466 for (j = 0; j < w; j++) { | |
| 467 sval = GET_DATA_QBIT(lines, j); | |
| 468 gval = graymap[sval]; | |
| 469 CHECK_VALUE(lined, j, gval); | |
| 470 } | |
| 471 #endif | |
| 472 break; | |
| 473 case 2: | |
| 474 /* Unrolled 16x */ | |
| 475 for (j = 0, count = 0; j + 15 < w; j += 16, count++) { | |
| 476 sword = lines[count]; | |
| 477 dword = (graymap[(sword >> 30) & 0x3] << 24) | | |
| 478 (graymap[(sword >> 28) & 0x3] << 16) | | |
| 479 (graymap[(sword >> 26) & 0x3] << 8) | | |
| 480 graymap[(sword >> 24) & 0x3]; | |
| 481 lined[4 * count] = dword; | |
| 482 dword = (graymap[(sword >> 22) & 0x3] << 24) | | |
| 483 (graymap[(sword >> 20) & 0x3] << 16) | | |
| 484 (graymap[(sword >> 18) & 0x3] << 8) | | |
| 485 graymap[(sword >> 16) & 0x3]; | |
| 486 lined[4 * count + 1] = dword; | |
| 487 dword = (graymap[(sword >> 14) & 0x3] << 24) | | |
| 488 (graymap[(sword >> 12) & 0x3] << 16) | | |
| 489 (graymap[(sword >> 10) & 0x3] << 8) | | |
| 490 graymap[(sword >> 8) & 0x3]; | |
| 491 lined[4 * count + 2] = dword; | |
| 492 dword = (graymap[(sword >> 6) & 0x3] << 24) | | |
| 493 (graymap[(sword >> 4) & 0x3] << 16) | | |
| 494 (graymap[(sword >> 2) & 0x3] << 8) | | |
| 495 graymap[sword & 0x3]; | |
| 496 lined[4 * count + 3] = dword; | |
| 497 } | |
| 498 /* Cleanup partial word */ | |
| 499 for (; j < w; j++) { | |
| 500 sval = GET_DATA_DIBIT(lines, j); | |
| 501 gval = graymap[sval]; | |
| 502 SET_DATA_BYTE(lined, j, gval); | |
| 503 } | |
| 504 #if DEBUG_UNROLLING | |
| 505 for (j = 0; j < w; j++) { | |
| 506 sval = GET_DATA_DIBIT(lines, j); | |
| 507 gval = graymap[sval]; | |
| 508 CHECK_VALUE(lined, j, gval); | |
| 509 } | |
| 510 #endif | |
| 511 break; | |
| 512 case 1: | |
| 513 /* Unrolled 8x */ | |
| 514 for (j = 0, count = 0; j + 31 < w; j += 32, count++) { | |
| 515 sword = lines[count]; | |
| 516 for (k = 0; k < 4; k++) { | |
| 517 /* The top byte is always the relevant one */ | |
| 518 dword = (graymap[(sword >> 31) & 0x1] << 24) | | |
| 519 (graymap[(sword >> 30) & 0x1] << 16) | | |
| 520 (graymap[(sword >> 29) & 0x1] << 8) | | |
| 521 graymap[(sword >> 28) & 0x1]; | |
| 522 lined[8 * count + 2 * k] = dword; | |
| 523 dword = (graymap[(sword >> 27) & 0x1] << 24) | | |
| 524 (graymap[(sword >> 26) & 0x1] << 16) | | |
| 525 (graymap[(sword >> 25) & 0x1] << 8) | | |
| 526 graymap[(sword >> 24) & 0x1]; | |
| 527 lined[8 * count + 2 * k + 1] = dword; | |
| 528 sword <<= 8; /* Move up the next byte */ | |
| 529 } | |
| 530 } | |
| 531 /* Cleanup partial word */ | |
| 532 for (; j < w; j++) { | |
| 533 sval = GET_DATA_BIT(lines, j); | |
| 534 gval = graymap[sval]; | |
| 535 SET_DATA_BYTE(lined, j, gval); | |
| 536 } | |
| 537 #if DEBUG_UNROLLING | |
| 538 for (j = 0; j < w; j++) { | |
| 539 sval = GET_DATA_BIT(lines, j); | |
| 540 gval = graymap[sval]; | |
| 541 CHECK_VALUE(lined, j, gval); | |
| 542 } | |
| 543 #undef CHECK_VALUE | |
| 544 #endif | |
| 545 break; | |
| 546 default: | |
| 547 return NULL; | |
| 548 } | |
| 549 } | |
| 550 if (graymap) | |
| 551 LEPT_FREE(graymap); | |
| 552 } else { /* type == REMOVE_CMAP_TO_FULL_COLOR or REMOVE_CMAP_WITH_ALPHA */ | |
| 553 if ((pixd = pixCreate(w, h, 32)) == NULL) { | |
| 554 L_ERROR("pixd not made\n", __func__); | |
| 555 goto cleanup_arrays; | |
| 556 } | |
| 557 pixCopyInputFormat(pixd, pixs); | |
| 558 pixCopyResolution(pixd, pixs); | |
| 559 if (type == REMOVE_CMAP_WITH_ALPHA) | |
| 560 pixSetSpp(pixd, 4); | |
| 561 datad = pixGetData(pixd); | |
| 562 wpld = pixGetWpl(pixd); | |
| 563 lut = (l_uint32 *)LEPT_CALLOC(nalloc, sizeof(l_uint32)); | |
| 564 for (i = 0; i < ncolors; i++) { | |
| 565 if (type == REMOVE_CMAP_TO_FULL_COLOR) | |
| 566 composeRGBPixel(rmap[i], gmap[i], bmap[i], lut + i); | |
| 567 else /* full color plus alpha */ | |
| 568 composeRGBAPixel(rmap[i], gmap[i], bmap[i], amap[i], lut + i); | |
| 569 } | |
| 570 | |
| 571 for (i = 0; i < h; i++) { | |
| 572 lines = datas + i * wpls; | |
| 573 lined = datad + i * wpld; | |
| 574 for (j = 0; j < w; j++) { | |
| 575 if (d == 8) | |
| 576 sval = GET_DATA_BYTE(lines, j); | |
| 577 else if (d == 4) | |
| 578 sval = GET_DATA_QBIT(lines, j); | |
| 579 else if (d == 2) | |
| 580 sval = GET_DATA_DIBIT(lines, j); | |
| 581 else /* (d == 1) */ | |
| 582 sval = GET_DATA_BIT(lines, j); | |
| 583 if (sval >= ncolors) | |
| 584 L_WARNING("pixel value out of bounds\n", __func__); | |
| 585 else | |
| 586 lined[j] = lut[sval]; | |
| 587 } | |
| 588 } | |
| 589 LEPT_FREE(lut); | |
| 590 } | |
| 591 | |
| 592 cleanup_arrays: | |
| 593 LEPT_FREE(rmap); | |
| 594 LEPT_FREE(gmap); | |
| 595 LEPT_FREE(bmap); | |
| 596 LEPT_FREE(amap); | |
| 597 return pixd; | |
| 598 } | |
| 599 | |
| 600 | |
| 601 /*-------------------------------------------------------------* | |
| 602 * Add colormap losslessly (8 to 8) * | |
| 603 *-------------------------------------------------------------*/ | |
| 604 /*! | |
| 605 * \brief pixAddGrayColormap8() | |
| 606 * | |
| 607 * \param[in] pixs 8 bpp | |
| 608 * \return 0 if OK, 1 on error | |
| 609 * | |
| 610 * <pre> | |
| 611 * Notes: | |
| 612 * (1) If pixs has a colormap, this is a no-op. | |
| 613 * </pre> | |
| 614 */ | |
| 615 l_ok | |
| 616 pixAddGrayColormap8(PIX *pixs) | |
| 617 { | |
| 618 PIXCMAP *cmap; | |
| 619 | |
| 620 if (!pixs || pixGetDepth(pixs) != 8) | |
| 621 return ERROR_INT("pixs not defined or not 8 bpp", __func__, 1); | |
| 622 if (pixGetColormap(pixs)) | |
| 623 return 0; | |
| 624 | |
| 625 cmap = pixcmapCreateLinear(8, 256); | |
| 626 pixSetColormap(pixs, cmap); | |
| 627 return 0; | |
| 628 } | |
| 629 | |
| 630 | |
| 631 /*! | |
| 632 * \brief pixAddMinimalGrayColormap8() | |
| 633 * | |
| 634 * \param[in] pixs 8 bpp | |
| 635 * \return 0 if OK, 1 on error | |
| 636 * | |
| 637 * <pre> | |
| 638 * Notes: | |
| 639 * (1) This generates a colormapped version of the input image | |
| 640 * that has the same number of colormap entries as the | |
| 641 * input image has unique gray levels. | |
| 642 * </pre> | |
| 643 */ | |
| 644 PIX * | |
| 645 pixAddMinimalGrayColormap8(PIX *pixs) | |
| 646 { | |
| 647 l_int32 ncolors, w, h, i, j, wpl1, wpld, index, val; | |
| 648 l_int32 *inta, *revmap; | |
| 649 l_uint32 *data1, *datad, *line1, *lined; | |
| 650 PIX *pix1, *pixd; | |
| 651 PIXCMAP *cmap; | |
| 652 | |
| 653 if (!pixs || pixGetDepth(pixs) != 8) | |
| 654 return (PIX *)ERROR_PTR("pixs undefined or not 8 bpp", __func__, NULL); | |
| 655 | |
| 656 /* Eliminate the easy cases */ | |
| 657 pixNumColors(pixs, 1, &ncolors); | |
| 658 cmap = pixGetColormap(pixs); | |
| 659 if (cmap) { | |
| 660 if (pixcmapGetCount(cmap) == ncolors) /* irreducible */ | |
| 661 return pixCopy(NULL, pixs); | |
| 662 else | |
| 663 pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 664 } else { | |
| 665 if (ncolors == 256) { | |
| 666 pix1 = pixCopy(NULL, pixs); | |
| 667 pixAddGrayColormap8(pix1); | |
| 668 return pix1; | |
| 669 } | |
| 670 pix1 = pixClone(pixs); | |
| 671 } | |
| 672 | |
| 673 /* Find the gray levels and make a reverse map */ | |
| 674 pixGetDimensions(pix1, &w, &h, NULL); | |
| 675 data1 = pixGetData(pix1); | |
| 676 wpl1 = pixGetWpl(pix1); | |
| 677 inta = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 678 for (i = 0; i < h; i++) { | |
| 679 line1 = data1 + i * wpl1; | |
| 680 for (j = 0; j < w; j++) { | |
| 681 val = GET_DATA_BYTE(line1, j); | |
| 682 inta[val] = 1; | |
| 683 } | |
| 684 } | |
| 685 cmap = pixcmapCreate(8); | |
| 686 revmap = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32)); | |
| 687 for (i = 0, index = 0; i < 256; i++) { | |
| 688 if (inta[i]) { | |
| 689 pixcmapAddColor(cmap, i, i, i); | |
| 690 revmap[i] = index++; | |
| 691 } | |
| 692 } | |
| 693 | |
| 694 /* Set all pixels in pixd to the colormap index */ | |
| 695 pixd = pixCreateTemplate(pix1); | |
| 696 pixSetColormap(pixd, cmap); | |
| 697 pixCopyInputFormat(pixd, pixs); | |
| 698 pixCopyResolution(pixd, pixs); | |
| 699 datad = pixGetData(pixd); | |
| 700 wpld = pixGetWpl(pixd); | |
| 701 for (i = 0; i < h; i++) { | |
| 702 line1 = data1 + i * wpl1; | |
| 703 lined = datad + i * wpld; | |
| 704 for (j = 0; j < w; j++) { | |
| 705 val = GET_DATA_BYTE(line1, j); | |
| 706 SET_DATA_BYTE(lined, j, revmap[val]); | |
| 707 } | |
| 708 } | |
| 709 | |
| 710 pixDestroy(&pix1); | |
| 711 LEPT_FREE(inta); | |
| 712 LEPT_FREE(revmap); | |
| 713 return pixd; | |
| 714 } | |
| 715 | |
| 716 | |
| 717 /*-------------------------------------------------------------* | |
| 718 * Conversion from RGB color to grayscale * | |
| 719 *-------------------------------------------------------------*/ | |
| 720 /*! | |
| 721 * \brief pixConvertRGBToLuminance() | |
| 722 * | |
| 723 * \param[in] pixs 32 bpp RGB | |
| 724 * \return 8 bpp pix, or NULL on error | |
| 725 * | |
| 726 * <pre> | |
| 727 * Notes: | |
| 728 * (1) Use a standard luminance conversion. | |
| 729 * </pre> | |
| 730 */ | |
| 731 PIX * | |
| 732 pixConvertRGBToLuminance(PIX *pixs) | |
| 733 { | |
| 734 return pixConvertRGBToGray(pixs, 0.0, 0.0, 0.0); | |
| 735 } | |
| 736 | |
| 737 | |
| 738 /*! | |
| 739 * \brief pixConvertRGBToGrayGeneral() | |
| 740 * | |
| 741 * \param[in] pixs 32 bpp RGB | |
| 742 * \param[in] type color selection flag | |
| 743 * \param[in] rwt, gwt, bwt ignored if type != L_SELECT_WEIGHTED; | |
| 744 * if used, must sum to 1.0. | |
| 745 * \return 8 bpp pix, or NULL on error | |
| 746 * | |
| 747 * <pre> | |
| 748 * Notes: | |
| 749 * (1) The color selection flag is one of: L_SELECT_RED, L_SELECT_GREEN, | |
| 750 * L_SELECT_BLUE, L_SELECT_MIN, L_SELECT_MAX, L_SELECT_AVERAGE, | |
| 751 * L_SELECT_HUE, L_SELECT_SATURATION, L_SELECT_WEIGHTED. | |
| 752 * (2) The weights, if used, must all be non-negative and must sum to 1.0. | |
| 753 * </pre> | |
| 754 */ | |
| 755 PIX * | |
| 756 pixConvertRGBToGrayGeneral(PIX *pixs, | |
| 757 l_int32 type, | |
| 758 l_float32 rwt, | |
| 759 l_float32 gwt, | |
| 760 l_float32 bwt) | |
| 761 { | |
| 762 PIX *pix1; | |
| 763 | |
| 764 if (!pixs || pixGetDepth(pixs) != 32) | |
| 765 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL); | |
| 766 if (type != L_SELECT_RED && type != L_SELECT_GREEN && | |
| 767 type != L_SELECT_BLUE && type != L_SELECT_MIN && | |
| 768 type != L_SELECT_MAX && type != L_SELECT_AVERAGE && | |
| 769 type != L_SELECT_HUE && type != L_SELECT_SATURATION && | |
| 770 type != L_SELECT_WEIGHTED) | |
| 771 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 772 | |
| 773 if (type == L_SELECT_RED) { | |
| 774 pix1 = pixGetRGBComponent(pixs, COLOR_RED); | |
| 775 } else if (type == L_SELECT_GREEN) { | |
| 776 pix1 = pixGetRGBComponent(pixs, COLOR_GREEN); | |
| 777 } else if (type == L_SELECT_BLUE) { | |
| 778 pix1 = pixGetRGBComponent(pixs, COLOR_BLUE); | |
| 779 } else if (type == L_SELECT_MIN) { | |
| 780 pix1 = pixConvertRGBToGrayMinMax(pixs, L_CHOOSE_MIN); | |
| 781 } else if (type == L_SELECT_MAX) { | |
| 782 pix1 = pixConvertRGBToGrayMinMax(pixs, L_CHOOSE_MAX); | |
| 783 } else if (type == L_SELECT_AVERAGE) { | |
| 784 pix1 = pixConvertRGBToGray(pixs, 0.34f, 0.33f, 0.33f); | |
| 785 } else if (type == L_SELECT_HUE) { | |
| 786 pix1 = pixConvertRGBToHue(pixs); | |
| 787 } else if (type == L_SELECT_SATURATION) { | |
| 788 pix1 = pixConvertRGBToSaturation(pixs); | |
| 789 } else { /* L_SELECT_WEIGHTED */ | |
| 790 if (rwt < 0.0 || gwt < 0.0 || bwt < 0.0) | |
| 791 return (PIX *)ERROR_PTR("weights not all >= 0.0", __func__, NULL); | |
| 792 if (rwt + gwt + bwt != 1.0) | |
| 793 return (PIX *)ERROR_PTR("weights don't sum to 1.0", __func__, NULL); | |
| 794 pix1 = pixConvertRGBToGray(pixs, rwt, gwt, bwt); | |
| 795 } | |
| 796 | |
| 797 return pix1; | |
| 798 } | |
| 799 | |
| 800 | |
| 801 /*! | |
| 802 * \brief pixConvertRGBToGray() | |
| 803 * | |
| 804 * \param[in] pixs 32 bpp RGB | |
| 805 * \param[in] rwt, gwt, bwt non-negative; these should add to 1.0, | |
| 806 * or use 0.0 for default | |
| 807 * \return 8 bpp pix, or NULL on error | |
| 808 * | |
| 809 * <pre> | |
| 810 * Notes: | |
| 811 * (1) Use a weighted average of the RGB values. | |
| 812 * </pre> | |
| 813 */ | |
| 814 PIX * | |
| 815 pixConvertRGBToGray(PIX *pixs, | |
| 816 l_float32 rwt, | |
| 817 l_float32 gwt, | |
| 818 l_float32 bwt) | |
| 819 { | |
| 820 l_int32 i, j, w, h, wpls, wpld, val; | |
| 821 l_uint32 word; | |
| 822 l_uint32 *datas, *lines, *datad, *lined; | |
| 823 l_float32 sum; | |
| 824 PIX *pixd; | |
| 825 | |
| 826 if (!pixs) | |
| 827 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 828 if (pixGetDepth(pixs) != 32) | |
| 829 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); | |
| 830 if (rwt < 0.0 || gwt < 0.0 || bwt < 0.0) | |
| 831 return (PIX *)ERROR_PTR("weights not all >= 0.0", __func__, NULL); | |
| 832 | |
| 833 /* Make sure the sum of weights is 1.0; otherwise, you can get | |
| 834 * overflow in the gray value. */ | |
| 835 if (rwt == 0.0 && gwt == 0.0 && bwt == 0.0) { | |
| 836 rwt = L_RED_WEIGHT; | |
| 837 gwt = L_GREEN_WEIGHT; | |
| 838 bwt = L_BLUE_WEIGHT; | |
| 839 } | |
| 840 sum = rwt + gwt + bwt; | |
| 841 if (L_ABS(sum - 1.0) > 0.0001) { /* maintain ratios with sum == 1.0 */ | |
| 842 L_WARNING("weights don't sum to 1; maintaining ratios\n", __func__); | |
| 843 rwt = rwt / sum; | |
| 844 gwt = gwt / sum; | |
| 845 bwt = bwt / sum; | |
| 846 } | |
| 847 | |
| 848 pixGetDimensions(pixs, &w, &h, NULL); | |
| 849 datas = pixGetData(pixs); | |
| 850 wpls = pixGetWpl(pixs); | |
| 851 if ((pixd = pixCreate(w, h, 8)) == NULL) | |
| 852 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 853 pixCopyResolution(pixd, pixs); | |
| 854 pixCopyInputFormat(pixd, pixs); | |
| 855 datad = pixGetData(pixd); | |
| 856 wpld = pixGetWpl(pixd); | |
| 857 | |
| 858 for (i = 0; i < h; i++) { | |
| 859 lines = datas + i * wpls; | |
| 860 lined = datad + i * wpld; | |
| 861 for (j = 0; j < w; j++) { | |
| 862 word = *(lines + j); | |
| 863 val = (l_int32)(rwt * ((word >> L_RED_SHIFT) & 0xff) + | |
| 864 gwt * ((word >> L_GREEN_SHIFT) & 0xff) + | |
| 865 bwt * ((word >> L_BLUE_SHIFT) & 0xff) + 0.5); | |
| 866 SET_DATA_BYTE(lined, j, val); | |
| 867 } | |
| 868 } | |
| 869 | |
| 870 return pixd; | |
| 871 } | |
| 872 | |
| 873 | |
| 874 /*! | |
| 875 * \brief pixConvertRGBToGrayFast() | |
| 876 * | |
| 877 * \param[in] pixs 32 bpp RGB | |
| 878 * \return 8 bpp pix, or NULL on error | |
| 879 * | |
| 880 * <pre> | |
| 881 * Notes: | |
| 882 * (1) This function should be used if speed of conversion | |
| 883 * is paramount, and the green channel can be used as | |
| 884 * a fair representative of the RGB intensity. It is | |
| 885 * several times faster than pixConvertRGBToGray(). | |
| 886 * (2) To combine RGB to gray conversion with subsampling, | |
| 887 * use pixScaleRGBToGrayFast() instead. | |
| 888 * </pre> | |
| 889 */ | |
| 890 PIX * | |
| 891 pixConvertRGBToGrayFast(PIX *pixs) | |
| 892 { | |
| 893 l_int32 i, j, w, h, wpls, wpld, val; | |
| 894 l_uint32 *datas, *lines, *datad, *lined; | |
| 895 PIX *pixd; | |
| 896 | |
| 897 if (!pixs) | |
| 898 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 899 if (pixGetDepth(pixs) != 32) | |
| 900 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); | |
| 901 | |
| 902 pixGetDimensions(pixs, &w, &h, NULL); | |
| 903 datas = pixGetData(pixs); | |
| 904 wpls = pixGetWpl(pixs); | |
| 905 if ((pixd = pixCreate(w, h, 8)) == NULL) | |
| 906 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 907 pixCopyResolution(pixd, pixs); | |
| 908 pixCopyInputFormat(pixd, pixs); | |
| 909 datad = pixGetData(pixd); | |
| 910 wpld = pixGetWpl(pixd); | |
| 911 | |
| 912 for (i = 0; i < h; i++) { | |
| 913 lines = datas + i * wpls; | |
| 914 lined = datad + i * wpld; | |
| 915 for (j = 0; j < w; j++, lines++) { | |
| 916 val = ((*lines) >> L_GREEN_SHIFT) & 0xff; | |
| 917 SET_DATA_BYTE(lined, j, val); | |
| 918 } | |
| 919 } | |
| 920 | |
| 921 return pixd; | |
| 922 } | |
| 923 | |
| 924 | |
| 925 /*! | |
| 926 * \brief pixConvertRGBToGrayMinMax() | |
| 927 * | |
| 928 * \param[in] pixs 32 bpp RGB | |
| 929 * \param[in] type L_CHOOSE_MIN, L_CHOOSE_MAX, L_CHOOSE_MAXDIFF, | |
| 930 * L_CHOOSE_MIN_BOOST, L_CHOOSE_MAX_BOOST | |
| 931 * \return 8 bpp pix, or NULL on error | |
| 932 * | |
| 933 * <pre> | |
| 934 * Notes: | |
| 935 * (1) This chooses various components or combinations of them, | |
| 936 * from the three RGB sample values. In addition to choosing | |
| 937 * the min, max, and maxdiff (difference between max and min), | |
| 938 * this also allows boosting the min and max about a reference | |
| 939 * value. | |
| 940 * (2) The default reference value for boosting the min and max | |
| 941 * is 200. This can be changed with l_setNeutralBoostVal() | |
| 942 * (3) The result with L_CHOOSE_MAXDIFF is surprisingly sensitive | |
| 943 * to a jpeg compression/decompression cycle with quality = 75. | |
| 944 * </pre> | |
| 945 */ | |
| 946 PIX * | |
| 947 pixConvertRGBToGrayMinMax(PIX *pixs, | |
| 948 l_int32 type) | |
| 949 { | |
| 950 l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, val, minval, maxval; | |
| 951 l_uint32 *datas, *lines, *datad, *lined; | |
| 952 PIX *pixd; | |
| 953 | |
| 954 if (!pixs) | |
| 955 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 956 if (pixGetDepth(pixs) != 32) | |
| 957 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); | |
| 958 if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX && | |
| 959 type != L_CHOOSE_MAXDIFF && type != L_CHOOSE_MIN_BOOST && | |
| 960 type != L_CHOOSE_MAX_BOOST) | |
| 961 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 962 | |
| 963 pixGetDimensions(pixs, &w, &h, NULL); | |
| 964 datas = pixGetData(pixs); | |
| 965 wpls = pixGetWpl(pixs); | |
| 966 if ((pixd = pixCreate(w, h, 8)) == NULL) | |
| 967 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 968 pixCopyResolution(pixd, pixs); | |
| 969 pixCopyInputFormat(pixd, pixs); | |
| 970 datad = pixGetData(pixd); | |
| 971 wpld = pixGetWpl(pixd); | |
| 972 | |
| 973 for (i = 0; i < h; i++) { | |
| 974 lines = datas + i * wpls; | |
| 975 lined = datad + i * wpld; | |
| 976 for (j = 0; j < w; j++) { | |
| 977 extractRGBValues(lines[j], &rval, &gval, &bval); | |
| 978 if (type == L_CHOOSE_MIN || type == L_CHOOSE_MIN_BOOST) { | |
| 979 val = L_MIN(rval, gval); | |
| 980 val = L_MIN(val, bval); | |
| 981 if (type == L_CHOOSE_MIN_BOOST) | |
| 982 val = L_MIN(255, (val * val) / var_NEUTRAL_BOOST_VAL); | |
| 983 } else if (type == L_CHOOSE_MAX || type == L_CHOOSE_MAX_BOOST) { | |
| 984 val = L_MAX(rval, gval); | |
| 985 val = L_MAX(val, bval); | |
| 986 if (type == L_CHOOSE_MAX_BOOST) | |
| 987 val = L_MIN(255, (val * val) / var_NEUTRAL_BOOST_VAL); | |
| 988 } else { /* L_CHOOSE_MAXDIFF */ | |
| 989 minval = L_MIN(rval, gval); | |
| 990 minval = L_MIN(minval, bval); | |
| 991 maxval = L_MAX(rval, gval); | |
| 992 maxval = L_MAX(maxval, bval); | |
| 993 val = maxval - minval; | |
| 994 } | |
| 995 SET_DATA_BYTE(lined, j, val); | |
| 996 } | |
| 997 } | |
| 998 | |
| 999 return pixd; | |
| 1000 } | |
| 1001 | |
| 1002 | |
| 1003 /*! | |
| 1004 * \brief pixConvertRGBToGraySatBoost() | |
| 1005 * | |
| 1006 * \param[in] pixs 32 bpp rgb | |
| 1007 * \param[in] refval between 1 and 255; typ. less than 128 | |
| 1008 * \return pixd 8 bpp, or NULL on error | |
| 1009 * | |
| 1010 * <pre> | |
| 1011 * Notes: | |
| 1012 * (1) This returns the max component value, boosted by | |
| 1013 * the saturation. The maximum boost occurs where | |
| 1014 * the maximum component value is equal to some reference value. | |
| 1015 * This particular weighting is due to Dany Qumsiyeh. | |
| 1016 * (2) For gray pixels (zero saturation), this returns | |
| 1017 * the intensity of any component. | |
| 1018 * (3) For fully saturated pixels ('fullsat'), this rises linearly | |
| 1019 * with the max value and has a slope equal to 255 divided | |
| 1020 * by the reference value; for a max value greater than | |
| 1021 * the reference value, it is clipped to 255. | |
| 1022 * (4) For saturation values in between, the output is a linear | |
| 1023 * combination of (2) and (3), weighted by saturation. | |
| 1024 * It falls between these two curves, and does not exceed 255. | |
| 1025 * (5) This can be useful for distinguishing an object that has nonzero | |
| 1026 * saturation from a gray background. For this, the refval | |
| 1027 * should be chosen near the expected value of the background, | |
| 1028 * to achieve maximum saturation boost there. | |
| 1029 * </pre> | |
| 1030 */ | |
| 1031 PIX * | |
| 1032 pixConvertRGBToGraySatBoost(PIX *pixs, | |
| 1033 l_int32 refval) | |
| 1034 { | |
| 1035 l_int32 w, h, d, i, j, wplt, wpld; | |
| 1036 l_int32 rval, gval, bval, sval, minrg, maxrg, min, max, delta; | |
| 1037 l_int32 fullsat, newval; | |
| 1038 l_float32 *invmax, *ratio; | |
| 1039 l_uint32 *linet, *lined, *datat, *datad; | |
| 1040 PIX *pixt, *pixd; | |
| 1041 | |
| 1042 if (!pixs) | |
| 1043 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1044 pixGetDimensions(pixs, &w, &h, &d); | |
| 1045 if (d != 32 && !pixGetColormap(pixs)) | |
| 1046 return (PIX *)ERROR_PTR("pixs not cmapped or rgb", __func__, NULL); | |
| 1047 if (refval < 1 || refval > 255) | |
| 1048 return (PIX *)ERROR_PTR("refval not in [1 ... 255]", __func__, NULL); | |
| 1049 | |
| 1050 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); | |
| 1051 pixd = pixCreate(w, h, 8); | |
| 1052 pixCopyResolution(pixd, pixs); | |
| 1053 pixCopyInputFormat(pixd, pixs); | |
| 1054 wplt = pixGetWpl(pixt); | |
| 1055 datat = pixGetData(pixt); | |
| 1056 wpld = pixGetWpl(pixd); | |
| 1057 datad = pixGetData(pixd); | |
| 1058 invmax = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); | |
| 1059 ratio = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32)); | |
| 1060 for (i = 1; i < 256; i++) { /* i == 0 --> delta = sval = newval = 0 */ | |
| 1061 invmax[i] = 1.0f / (l_float32)i; | |
| 1062 ratio[i] = (l_float32)i / (l_float32)refval; | |
| 1063 } | |
| 1064 for (i = 0; i < h; i++) { | |
| 1065 linet = datat + i * wplt; | |
| 1066 lined = datad + i * wpld; | |
| 1067 for (j = 0; j < w; j++) { | |
| 1068 extractRGBValues(linet[j], &rval, &gval, &bval); | |
| 1069 minrg = L_MIN(rval, gval); | |
| 1070 min = L_MIN(minrg, bval); | |
| 1071 maxrg = L_MAX(rval, gval); | |
| 1072 max = L_MAX(maxrg, bval); | |
| 1073 delta = max - min; | |
| 1074 if (delta == 0) /* gray; no chroma */ | |
| 1075 sval = 0; | |
| 1076 else | |
| 1077 sval = (l_int32)(255. * (l_float32)delta * invmax[max] + 0.5); | |
| 1078 | |
| 1079 fullsat = L_MIN(255, 255 * ratio[max]); | |
| 1080 newval = (sval * fullsat + (255 - sval) * max) / 255; | |
| 1081 SET_DATA_BYTE(lined, j, newval); | |
| 1082 } | |
| 1083 } | |
| 1084 | |
| 1085 pixDestroy(&pixt); | |
| 1086 LEPT_FREE(invmax); | |
| 1087 LEPT_FREE(ratio); | |
| 1088 return pixd; | |
| 1089 } | |
| 1090 | |
| 1091 | |
| 1092 /*! | |
| 1093 * \brief pixConvertRGBToGrayArb() | |
| 1094 * | |
| 1095 * \param[in] pixs 32 bpp RGB | |
| 1096 * \param[in] rc, gc, bc arithmetic factors; can be negative | |
| 1097 * \return 8 bpp pix, or NULL on error | |
| 1098 * | |
| 1099 * <pre> | |
| 1100 * Notes: | |
| 1101 * (1) This converts to gray using an arbitrary linear combination | |
| 1102 * of the rgb color components. It differs from pixConvertToGray(), | |
| 1103 * which uses only positive coefficients that sum to 1. | |
| 1104 * (2) The gray output values are clipped to 0 and 255. | |
| 1105 * </pre> | |
| 1106 */ | |
| 1107 PIX * | |
| 1108 pixConvertRGBToGrayArb(PIX *pixs, | |
| 1109 l_float32 rc, | |
| 1110 l_float32 gc, | |
| 1111 l_float32 bc) | |
| 1112 { | |
| 1113 l_int32 i, j, w, h, wpls, wpld, rval, gval, bval, val; | |
| 1114 l_uint32 *datas, *lines, *datad, *lined; | |
| 1115 PIX *pixd; | |
| 1116 | |
| 1117 if (!pixs) | |
| 1118 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1119 if (pixGetDepth(pixs) != 32) | |
| 1120 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); | |
| 1121 if (rc <= 0 && gc <= 0 && bc <= 0) | |
| 1122 return (PIX *)ERROR_PTR("all coefficients <= 0", __func__, NULL); | |
| 1123 | |
| 1124 pixGetDimensions(pixs, &w, &h, NULL); | |
| 1125 datas = pixGetData(pixs); | |
| 1126 wpls = pixGetWpl(pixs); | |
| 1127 if ((pixd = pixCreate(w, h, 8)) == NULL) | |
| 1128 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1129 pixCopyResolution(pixd, pixs); | |
| 1130 pixCopyInputFormat(pixd, pixs); | |
| 1131 datad = pixGetData(pixd); | |
| 1132 wpld = pixGetWpl(pixd); | |
| 1133 | |
| 1134 for (i = 0; i < h; i++) { | |
| 1135 lines = datas + i * wpls; | |
| 1136 lined = datad + i * wpld; | |
| 1137 for (j = 0; j < w; j++) { | |
| 1138 extractRGBValues(lines[j], &rval, &gval, &bval); | |
| 1139 val = (l_int32)(rc * rval + gc * gval + bc * bval); | |
| 1140 val = L_MIN(255, L_MAX(0, val)); | |
| 1141 SET_DATA_BYTE(lined, j, val); | |
| 1142 } | |
| 1143 } | |
| 1144 | |
| 1145 return pixd; | |
| 1146 } | |
| 1147 | |
| 1148 | |
| 1149 /*! | |
| 1150 * \brief pixConvertRGBToBinaryArb() | |
| 1151 * | |
| 1152 * \param[in] pixs 32 bpp RGB | |
| 1153 * \param[in] rc, gc, bc arithmetic factors; can be negative | |
| 1154 * \param[in] thresh binarization threshold | |
| 1155 * \param[in] relation L_SELECT_IF_LT, L_SELECT_IF_GT | |
| 1156 * L_SELECT_IF_LTE, L_SELECT_IF_GTE | |
| 1157 * \return 1 bpp pix, or NULL on error | |
| 1158 * | |
| 1159 * <pre> | |
| 1160 * Notes: | |
| 1161 * (1) This makes a 1 bpp mask from an RGB image, using an arbitrary | |
| 1162 * linear combination of the rgb color components, along with | |
| 1163 * a threshold and a selection choice of the gray value relative | |
| 1164 * to %thresh. | |
| 1165 * </pre> | |
| 1166 */ | |
| 1167 PIX * | |
| 1168 pixConvertRGBToBinaryArb(PIX *pixs, | |
| 1169 l_float32 rc, | |
| 1170 l_float32 gc, | |
| 1171 l_float32 bc, | |
| 1172 l_int32 thresh, | |
| 1173 l_int32 relation) | |
| 1174 { | |
| 1175 l_int32 threshold; | |
| 1176 PIX *pix1, *pix2; | |
| 1177 | |
| 1178 if (!pixs || pixGetDepth(pixs) != 32) | |
| 1179 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL); | |
| 1180 if (rc <= 0 && gc <= 0 && bc <= 0) | |
| 1181 return (PIX *)ERROR_PTR("all coefficients <= 0", __func__, NULL); | |
| 1182 if (relation != L_SELECT_IF_LT && relation != L_SELECT_IF_GT && | |
| 1183 relation != L_SELECT_IF_LTE && relation != L_SELECT_IF_GTE) | |
| 1184 return (PIX *)ERROR_PTR("invalid relation", __func__, NULL); | |
| 1185 | |
| 1186 pix1 = pixConvertRGBToGrayArb(pixs, rc, gc, bc); | |
| 1187 threshold = (relation == L_SELECT_IF_LTE || relation == L_SELECT_IF_GT) ? | |
| 1188 thresh : thresh + 1; | |
| 1189 pix2 = pixThresholdToBinary(pix1, threshold); | |
| 1190 if (relation == L_SELECT_IF_GT || relation == L_SELECT_IF_GTE) | |
| 1191 pixInvert(pix2, pix2); | |
| 1192 pixDestroy(&pix1); | |
| 1193 return pix2; | |
| 1194 } | |
| 1195 | |
| 1196 | |
| 1197 /*---------------------------------------------------------------------------* | |
| 1198 * Conversion from grayscale to colormap * | |
| 1199 *---------------------------------------------------------------------------*/ | |
| 1200 /*! | |
| 1201 * \brief pixConvertGrayToColormap() | |
| 1202 * | |
| 1203 * \param[in] pixs 2, 4 or 8 bpp grayscale | |
| 1204 * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error | |
| 1205 * | |
| 1206 * <pre> | |
| 1207 * Notes: | |
| 1208 * (1) This is a simple interface for adding a colormap to a | |
| 1209 * 2, 4 or 8 bpp grayscale image without causing any | |
| 1210 * quantization. There is some similarity to operations | |
| 1211 * in grayquant.c, such as pixThresholdOn8bpp(), where | |
| 1212 * the emphasis is on quantization with an arbitrary number | |
| 1213 * of levels, and a colormap is an option. | |
| 1214 * (2) Returns a copy if pixs already has a colormap. | |
| 1215 * (3) For 8 bpp src, this is a lossless transformation. | |
| 1216 * (4) For 2 and 4 bpp src, this generates a colormap that | |
| 1217 * assumes full coverage of the gray space, with equally spaced | |
| 1218 * levels: 4 levels for d = 2 and 16 levels for d = 4. | |
| 1219 * (5) In all cases, the depth of the dest is the same as the src. | |
| 1220 * </pre> | |
| 1221 */ | |
| 1222 PIX * | |
| 1223 pixConvertGrayToColormap(PIX *pixs) | |
| 1224 { | |
| 1225 l_int32 d; | |
| 1226 PIX *pixd; | |
| 1227 PIXCMAP *cmap; | |
| 1228 | |
| 1229 if (!pixs) | |
| 1230 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1231 d = pixGetDepth(pixs); | |
| 1232 if (d != 2 && d != 4 && d != 8) | |
| 1233 return (PIX *)ERROR_PTR("pixs not 2, 4 or 8 bpp", __func__, NULL); | |
| 1234 | |
| 1235 if (pixGetColormap(pixs)) { | |
| 1236 L_INFO("pixs already has a colormap\n", __func__); | |
| 1237 return pixCopy(NULL, pixs); | |
| 1238 } | |
| 1239 | |
| 1240 if (d == 8) /* lossless conversion */ | |
| 1241 return pixConvertGrayToColormap8(pixs, 2); | |
| 1242 | |
| 1243 /* Build a cmap with equally spaced target values over the | |
| 1244 * full 8 bpp range. */ | |
| 1245 pixd = pixCopy(NULL, pixs); | |
| 1246 cmap = pixcmapCreateLinear(d, 1 << d); | |
| 1247 pixSetColormap(pixd, cmap); | |
| 1248 pixCopyInputFormat(pixd, pixs); | |
| 1249 return pixd; | |
| 1250 } | |
| 1251 | |
| 1252 | |
| 1253 /*! | |
| 1254 * \brief pixConvertGrayToColormap8() | |
| 1255 * | |
| 1256 * \param[in] pixs 8 bpp grayscale | |
| 1257 * \param[in] mindepth of pixd; valid values are 2, 4 and 8 | |
| 1258 * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error | |
| 1259 * | |
| 1260 * <pre> | |
| 1261 * Notes: | |
| 1262 * (1) Returns a copy if pixs already has a colormap. | |
| 1263 * (2) This is a lossless transformation; there is no quantization. | |
| 1264 * We compute the number of different gray values in pixs, | |
| 1265 * and construct a colormap that has exactly these values. | |
| 1266 * (3) 'mindepth' is the minimum depth of pixd. If mindepth == 8, | |
| 1267 * pixd will always be 8 bpp. Let the number of different | |
| 1268 * gray values in pixs be ngray. If mindepth == 4, we attempt | |
| 1269 * to save pixd as a 4 bpp image, but if ngray > 16, | |
| 1270 * pixd must be 8 bpp. Likewise, if mindepth == 2, | |
| 1271 * the depth of pixd will be 2 if ngray <= 4 and 4 if ngray > 4 | |
| 1272 * but <= 16. | |
| 1273 * </pre> | |
| 1274 */ | |
| 1275 PIX * | |
| 1276 pixConvertGrayToColormap8(PIX *pixs, | |
| 1277 l_int32 mindepth) | |
| 1278 { | |
| 1279 l_int32 ncolors, w, h, depth, i, j, wpls, wpld; | |
| 1280 l_int32 index, num, val, newval; | |
| 1281 l_int32 array[256]; | |
| 1282 l_uint32 *lines, *lined, *datas, *datad; | |
| 1283 NUMA *na; | |
| 1284 PIX *pixd; | |
| 1285 PIXCMAP *cmap; | |
| 1286 | |
| 1287 if (!pixs) | |
| 1288 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1289 if (pixGetDepth(pixs) != 8) | |
| 1290 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 1291 if (mindepth != 2 && mindepth != 4 && mindepth != 8) { | |
| 1292 L_WARNING("invalid value of mindepth; setting to 8\n", __func__); | |
| 1293 mindepth = 8; | |
| 1294 } | |
| 1295 | |
| 1296 if (pixGetColormap(pixs)) { | |
| 1297 L_INFO("pixs already has a colormap\n", __func__); | |
| 1298 return pixCopy(NULL, pixs); | |
| 1299 } | |
| 1300 | |
| 1301 na = pixGetGrayHistogram(pixs, 1); | |
| 1302 numaGetCountRelativeToZero(na, L_GREATER_THAN_ZERO, &ncolors); | |
| 1303 if (mindepth == 8 || ncolors > 16) | |
| 1304 depth = 8; | |
| 1305 else if (mindepth == 4 || ncolors > 4) | |
| 1306 depth = 4; | |
| 1307 else | |
| 1308 depth = 2; | |
| 1309 | |
| 1310 pixGetDimensions(pixs, &w, &h, NULL); | |
| 1311 pixd = pixCreate(w, h, depth); | |
| 1312 cmap = pixcmapCreate(depth); | |
| 1313 pixSetColormap(pixd, cmap); | |
| 1314 pixCopyInputFormat(pixd, pixs); | |
| 1315 pixCopyResolution(pixd, pixs); | |
| 1316 | |
| 1317 index = 0; | |
| 1318 for (i = 0; i < 256; i++) { | |
| 1319 array[i] = 0; /* only to quiet the static checker */ | |
| 1320 numaGetIValue(na, i, &num); | |
| 1321 if (num > 0) { | |
| 1322 pixcmapAddColor(cmap, i, i, i); | |
| 1323 array[i] = index; | |
| 1324 index++; | |
| 1325 } | |
| 1326 } | |
| 1327 | |
| 1328 datas = pixGetData(pixs); | |
| 1329 wpls = pixGetWpl(pixs); | |
| 1330 datad = pixGetData(pixd); | |
| 1331 wpld = pixGetWpl(pixd); | |
| 1332 for (i = 0; i < h; i++) { | |
| 1333 lines = datas + i * wpls; | |
| 1334 lined = datad + i * wpld; | |
| 1335 for (j = 0; j < w; j++) { | |
| 1336 val = GET_DATA_BYTE(lines, j); | |
| 1337 newval = array[val]; | |
| 1338 if (depth == 2) | |
| 1339 SET_DATA_DIBIT(lined, j, newval); | |
| 1340 else if (depth == 4) | |
| 1341 SET_DATA_QBIT(lined, j, newval); | |
| 1342 else /* depth == 8 */ | |
| 1343 SET_DATA_BYTE(lined, j, newval); | |
| 1344 } | |
| 1345 } | |
| 1346 | |
| 1347 numaDestroy(&na); | |
| 1348 return pixd; | |
| 1349 } | |
| 1350 | |
| 1351 | |
| 1352 /*---------------------------------------------------------------------------* | |
| 1353 * Colorizing conversion from grayscale to color * | |
| 1354 *---------------------------------------------------------------------------*/ | |
| 1355 /*! | |
| 1356 * \brief pixColorizeGray() | |
| 1357 * | |
| 1358 * \param[in] pixs 8 bpp gray; 2, 4 or 8 bpp colormapped | |
| 1359 * \param[in] color 32 bit rgba pixel | |
| 1360 * \param[in] cmapflag 1 for result to have colormap; 0 for RGB | |
| 1361 * \return pixd 8 bpp colormapped or 32 bpp rgb, or NULL on error | |
| 1362 * | |
| 1363 * <pre> | |
| 1364 * Notes: | |
| 1365 * (1) This applies the specific color to the grayscale image. | |
| 1366 * (2) If pixs already has a colormap, it is removed to gray | |
| 1367 * before colorizing. | |
| 1368 * </pre> | |
| 1369 */ | |
| 1370 PIX * | |
| 1371 pixColorizeGray(PIX *pixs, | |
| 1372 l_uint32 color, | |
| 1373 l_int32 cmapflag) | |
| 1374 { | |
| 1375 l_int32 i, j, w, h, wplt, wpld, val8; | |
| 1376 l_uint32 *datad, *datat, *lined, *linet, *tab; | |
| 1377 PIX *pixt, *pixd; | |
| 1378 PIXCMAP *cmap; | |
| 1379 | |
| 1380 if (!pixs) | |
| 1381 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1382 if (pixGetDepth(pixs) != 8 && !pixGetColormap(pixs)) | |
| 1383 return (PIX *)ERROR_PTR("pixs not 8 bpp or cmapped", __func__, NULL); | |
| 1384 | |
| 1385 if (pixGetColormap(pixs)) | |
| 1386 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 1387 else | |
| 1388 pixt = pixClone(pixs); | |
| 1389 | |
| 1390 cmap = pixcmapGrayToColor(color); | |
| 1391 if (cmapflag) { | |
| 1392 pixd = pixCopy(NULL, pixt); | |
| 1393 pixSetColormap(pixd, cmap); | |
| 1394 pixDestroy(&pixt); | |
| 1395 return pixd; | |
| 1396 } | |
| 1397 | |
| 1398 /* Make an RGB pix */ | |
| 1399 pixcmapToRGBTable(cmap, &tab, NULL); | |
| 1400 pixGetDimensions(pixt, &w, &h, NULL); | |
| 1401 pixd = pixCreate(w, h, 32); | |
| 1402 pixCopyResolution(pixd, pixs); | |
| 1403 pixCopyInputFormat(pixd, pixs); | |
| 1404 datad = pixGetData(pixd); | |
| 1405 wpld = pixGetWpl(pixd); | |
| 1406 datat = pixGetData(pixt); | |
| 1407 wplt = pixGetWpl(pixt); | |
| 1408 for (i = 0; i < h; i++) { | |
| 1409 lined = datad + i * wpld; | |
| 1410 linet = datat + i * wplt; | |
| 1411 for (j = 0; j < w; j++) { | |
| 1412 val8 = GET_DATA_BYTE(linet, j); | |
| 1413 lined[j] = tab[val8]; | |
| 1414 } | |
| 1415 } | |
| 1416 | |
| 1417 pixDestroy(&pixt); | |
| 1418 pixcmapDestroy(&cmap); | |
| 1419 LEPT_FREE(tab); | |
| 1420 return pixd; | |
| 1421 } | |
| 1422 | |
| 1423 | |
| 1424 /*---------------------------------------------------------------------------* | |
| 1425 * Conversion from RGB color to colormap * | |
| 1426 *---------------------------------------------------------------------------*/ | |
| 1427 /*! | |
| 1428 * \brief pixConvertRGBToColormap() | |
| 1429 * | |
| 1430 * \param[in] pixs 32 bpp rgb | |
| 1431 * \param[in] ditherflag 1 to dither, 0 otherwise | |
| 1432 * \return pixd 2, 4 or 8 bpp with colormap, or NULL on error | |
| 1433 * | |
| 1434 * <pre> | |
| 1435 * Notes: | |
| 1436 * (1) This function has two relatively simple modes of color | |
| 1437 * quantization: | |
| 1438 * (a) If the image is made orthographically and has not more | |
| 1439 * than 256 'colors' at the level 4 octcube leaves, | |
| 1440 * it is quantized nearly exactly. The ditherflag | |
| 1441 * is ignored. | |
| 1442 * (b) Most natural images have more than 256 different colors; | |
| 1443 * in that case we use adaptive octree quantization, | |
| 1444 * with dithering if requested. | |
| 1445 * (2) If there are not more than 256 occupied level 4 octcubes, | |
| 1446 * the color in the colormap that represents all pixels in | |
| 1447 * one of those octcubes is given by the first pixel that | |
| 1448 * falls into that octcube. | |
| 1449 * (3) Dithering gives better visual results on images where | |
| 1450 * there is a color wash (a slow variation of color), but it | |
| 1451 * is about twice as slow and results in significantly larger | |
| 1452 * files when losslessly compressed (e.g., into png). | |
| 1453 * </pre> | |
| 1454 */ | |
| 1455 PIX * | |
| 1456 pixConvertRGBToColormap(PIX *pixs, | |
| 1457 l_int32 ditherflag) | |
| 1458 { | |
| 1459 l_int32 ncolors; | |
| 1460 NUMA *na; | |
| 1461 PIX *pixd; | |
| 1462 | |
| 1463 if (!pixs) | |
| 1464 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1465 if (pixGetDepth(pixs) != 32) | |
| 1466 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); | |
| 1467 if (pixGetSpp(pixs) == 4) | |
| 1468 L_WARNING("pixs has alpha; removing\n", __func__); | |
| 1469 | |
| 1470 /* Get the histogram and count the number of occupied level 4 | |
| 1471 * leaf octcubes. We don't yet know if this is the number of | |
| 1472 * actual colors, but if it's not, all pixels falling into | |
| 1473 * the same leaf octcube will be assigned to the color of the | |
| 1474 * first pixel that lands there. */ | |
| 1475 na = pixOctcubeHistogram(pixs, 4, &ncolors); | |
| 1476 | |
| 1477 /* If 256 or fewer occupied leaf octcubes, quantize to those octcubes */ | |
| 1478 if (ncolors <= 256) { | |
| 1479 pixd = pixFewColorsOctcubeQuant2(pixs, 4, na, ncolors, NULL); | |
| 1480 pixCopyInputFormat(pixd, pixs); | |
| 1481 numaDestroy(&na); | |
| 1482 return pixd; | |
| 1483 } | |
| 1484 | |
| 1485 /* There are too many occupied leaf octcubes to be represented | |
| 1486 * directly in a colormap. Fall back to octree quantization, | |
| 1487 * optionally with dithering. */ | |
| 1488 numaDestroy(&na); | |
| 1489 if (ditherflag) | |
| 1490 L_INFO("More than 256 colors; using octree quant with dithering\n", | |
| 1491 __func__); | |
| 1492 else | |
| 1493 L_INFO("More than 256 colors; using octree quant; no dithering\n", | |
| 1494 __func__); | |
| 1495 return pixOctreeColorQuant(pixs, 240, ditherflag); | |
| 1496 } | |
| 1497 | |
| 1498 | |
| 1499 /*---------------------------------------------------------------------------* | |
| 1500 * Conversion from colormap to 1 bpp * | |
| 1501 *---------------------------------------------------------------------------*/ | |
| 1502 /*! | |
| 1503 * \brief pixConvertCmapTo1() | |
| 1504 * | |
| 1505 * \param[in] pixs cmapped | |
| 1506 * \return pixd 1 bpp, or NULL on error | |
| 1507 * | |
| 1508 * <pre> | |
| 1509 * Notes: | |
| 1510 * (1) This is an extreme color quantizer. It decides which | |
| 1511 * colors map to FG (black) and which to BG (white). | |
| 1512 * (2) This uses two heuristics to make the decision: | |
| 1513 * (a) colors similar to each other are likely to be in the same class | |
| 1514 * (b) there is usually much less FG than BG. | |
| 1515 * </pre> | |
| 1516 */ | |
| 1517 PIX * | |
| 1518 pixConvertCmapTo1(PIX *pixs) | |
| 1519 { | |
| 1520 l_int32 i, j, nc, w, h, imin, imax, factor, wpl1, wpld; | |
| 1521 l_int32 index, rmin, gmin, bmin, rmax, gmax, bmax, dmin, dmax; | |
| 1522 l_float32 minfract, ifract; | |
| 1523 l_int32 *lut; | |
| 1524 l_uint32 *line1, *lined, *data1, *datad; | |
| 1525 NUMA *na1, *na2; /* histograms */ | |
| 1526 PIX *pix1, *pixd; | |
| 1527 PIXCMAP *cmap; | |
| 1528 | |
| 1529 if (!pixs) | |
| 1530 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1531 if ((cmap = pixGetColormap(pixs)) == NULL) | |
| 1532 return (PIX *)ERROR_PTR("no colormap", __func__, NULL); | |
| 1533 | |
| 1534 /* Select target colors for the two classes. Find the | |
| 1535 * colors with smallest and largest average component values. | |
| 1536 * The smallest is class 0 and the largest is class 1. */ | |
| 1537 pixcmapGetRangeValues(cmap, L_SELECT_AVERAGE, NULL, NULL, &imin, &imax); | |
| 1538 pixcmapGetColor(cmap, imin, &rmin, &gmin, &bmin); | |
| 1539 pixcmapGetColor(cmap, imax, &rmax, &gmax, &bmax); | |
| 1540 nc = pixcmapGetCount(cmap); | |
| 1541 | |
| 1542 /* Assign colors to the two classes. The histogram is | |
| 1543 * initialized to 0, so any colors not found when computing | |
| 1544 * the sampled histogram will get zero weight in minfract. */ | |
| 1545 if ((lut = (l_int32 *)LEPT_CALLOC(nc, sizeof(l_int32))) == NULL) | |
| 1546 return (PIX *)ERROR_PTR("calloc fail for lut", __func__, NULL); | |
| 1547 pixGetDimensions(pixs, &w, &h, NULL); | |
| 1548 factor = L_MAX(1, (l_int32)sqrt((l_float64)(w * h) / 50000. + 0.5)); | |
| 1549 na1 = pixGetCmapHistogram(pixs, factor); | |
| 1550 na2 = numaNormalizeHistogram(na1, 1.0); | |
| 1551 minfract = 0.0; | |
| 1552 for (i = 0; i < nc; i++) { | |
| 1553 numaGetFValue(na2, i, &ifract); | |
| 1554 pixcmapGetDistanceToColor(cmap, i, rmin, gmin, bmin, &dmin); | |
| 1555 pixcmapGetDistanceToColor(cmap, i, rmax, gmax, bmax, &dmax); | |
| 1556 if (dmin < dmax) { /* closer to dark extreme value */ | |
| 1557 lut[i] = 1; /* black pixel in 1 bpp image */ | |
| 1558 minfract += ifract; | |
| 1559 } | |
| 1560 } | |
| 1561 numaDestroy(&na1); | |
| 1562 numaDestroy(&na2); | |
| 1563 | |
| 1564 /* Generate the output binarized image */ | |
| 1565 pix1 = pixConvertTo8(pixs, 1); | |
| 1566 pixd = pixCreate(w, h, 1); | |
| 1567 data1 = pixGetData(pix1); | |
| 1568 datad = pixGetData(pixd); | |
| 1569 wpl1 = pixGetWpl(pix1); | |
| 1570 wpld = pixGetWpl(pixd); | |
| 1571 for (i = 0; i < h; i++) { | |
| 1572 line1 = data1 + i * wpl1; | |
| 1573 lined = datad + i * wpld; | |
| 1574 for (j = 0; j < w; j++) { | |
| 1575 index = GET_DATA_BYTE(line1, j); | |
| 1576 if (lut[index] == 1) SET_DATA_BIT(lined, j); | |
| 1577 } | |
| 1578 } | |
| 1579 pixDestroy(&pix1); | |
| 1580 LEPT_FREE(lut); | |
| 1581 | |
| 1582 /* We expect minfract (the dark colors) to be less than 0.5. | |
| 1583 * If that is not the case, invert pixd. */ | |
| 1584 if (minfract > 0.5) { | |
| 1585 L_INFO("minfract = %5.3f; inverting\n", __func__, minfract); | |
| 1586 pixInvert(pixd, pixd); | |
| 1587 } | |
| 1588 | |
| 1589 return pixd; | |
| 1590 } | |
| 1591 | |
| 1592 | |
| 1593 /*---------------------------------------------------------------------------* | |
| 1594 * Quantization for relatively small number of colors in source * | |
| 1595 *---------------------------------------------------------------------------*/ | |
| 1596 /*! | |
| 1597 * \brief pixQuantizeIfFewColors() | |
| 1598 * | |
| 1599 * \param[in] pixs 8 bpp gray or 32 bpp rgb | |
| 1600 * \param[in] maxcolors max number of colors allowed to be returned | |
| 1601 * from pixColorsForQuantization(); | |
| 1602 * use 0 for default | |
| 1603 * \param[in] mingraycolors min number of gray levels that a grayscale | |
| 1604 * image is quantized to; use 0 for default | |
| 1605 * \param[in] octlevel for octcube quantization: 3 or 4 | |
| 1606 * \param[out] ppixd 2,4 or 8 bpp quantized; null if too many colors | |
| 1607 * \return 0 if OK, 1 on error or if pixs can't be quantized into | |
| 1608 * a small number of colors. | |
| 1609 * | |
| 1610 * <pre> | |
| 1611 * Notes: | |
| 1612 * (1) This is a wrapper that tests if the pix can be quantized | |
| 1613 * with good quality using a small number of colors. If so, | |
| 1614 * it does the quantization, defining a colormap and using | |
| 1615 * pixels whose value is an index into the colormap. | |
| 1616 * (2) If the image has color, it is quantized with 8 bpp pixels. | |
| 1617 * If the image is essentially grayscale, the pixels are | |
| 1618 * either 4 or 8 bpp, depending on the size of the required | |
| 1619 * colormap. | |
| 1620 * (3) %octlevel = 4 generates a larger colormap and larger | |
| 1621 * compressed image than %octlevel = 3. If image quality is | |
| 1622 * important, you should use %octlevel = 4. | |
| 1623 * (4) If the image already has a colormap, it returns a clone. | |
| 1624 * </pre> | |
| 1625 */ | |
| 1626 l_ok | |
| 1627 pixQuantizeIfFewColors(PIX *pixs, | |
| 1628 l_int32 maxcolors, | |
| 1629 l_int32 mingraycolors, | |
| 1630 l_int32 octlevel, | |
| 1631 PIX **ppixd) | |
| 1632 { | |
| 1633 l_int32 d, ncolors, iscolor, graycolors; | |
| 1634 PIX *pixg, *pixd; | |
| 1635 | |
| 1636 if (!ppixd) | |
| 1637 return ERROR_INT("&pixd not defined", __func__, 1); | |
| 1638 *ppixd = NULL; | |
| 1639 if (!pixs) | |
| 1640 return ERROR_INT("pixs not defined", __func__, 1); | |
| 1641 d = pixGetDepth(pixs); | |
| 1642 if (d != 8 && d != 32) | |
| 1643 return ERROR_INT("pixs not defined", __func__, 1); | |
| 1644 if (pixGetColormap(pixs) != NULL) { | |
| 1645 *ppixd = pixClone(pixs); | |
| 1646 return 0; | |
| 1647 } | |
| 1648 if (maxcolors <= 0) | |
| 1649 maxcolors = 15; /* default */ | |
| 1650 if (maxcolors > 50) | |
| 1651 L_WARNING("maxcolors > 50; very large!\n", __func__); | |
| 1652 if (mingraycolors <= 0) | |
| 1653 mingraycolors = 10; /* default */ | |
| 1654 if (mingraycolors > 30) | |
| 1655 L_WARNING("mingraycolors > 30; very large!\n", __func__); | |
| 1656 if (octlevel != 3 && octlevel != 4) { | |
| 1657 L_WARNING("invalid octlevel; setting to 3\n", __func__); | |
| 1658 octlevel = 3; | |
| 1659 } | |
| 1660 | |
| 1661 /* Test the number of colors. For color, the octcube leaves | |
| 1662 * are at level 4. */ | |
| 1663 pixColorsForQuantization(pixs, 0, &ncolors, &iscolor, 0); | |
| 1664 if (ncolors > maxcolors) | |
| 1665 return ERROR_INT("too many colors", __func__, 1); | |
| 1666 | |
| 1667 /* Quantize! | |
| 1668 * (1) For color: | |
| 1669 * If octlevel == 4, try to quantize to an octree where | |
| 1670 * the octcube leaves are at level 4. If that fails, | |
| 1671 * back off to level 3. | |
| 1672 * If octlevel == 3, quantize to level 3 directly. | |
| 1673 * For level 3, the quality is usually good enough and there | |
| 1674 * is negligible chance of getting more than 256 colors. | |
| 1675 * (2) For grayscale, multiply ncolors by 1.5 for extra quality, | |
| 1676 * but use at least mingraycolors and not more than 256. */ | |
| 1677 if (iscolor) { | |
| 1678 pixd = pixFewColorsOctcubeQuant1(pixs, octlevel); | |
| 1679 if (!pixd) { /* backoff */ | |
| 1680 pixd = pixFewColorsOctcubeQuant1(pixs, octlevel - 1); | |
| 1681 if (octlevel == 3) /* shouldn't happen */ | |
| 1682 L_WARNING("quantized at level 2; low quality\n", __func__); | |
| 1683 } | |
| 1684 } else { /* image is really grayscale */ | |
| 1685 if (d == 32) | |
| 1686 pixg = pixConvertRGBToLuminance(pixs); | |
| 1687 else | |
| 1688 pixg = pixClone(pixs); | |
| 1689 graycolors = L_MAX(mingraycolors, (l_int32)(1.5 * ncolors)); | |
| 1690 graycolors = L_MIN(graycolors, 256); | |
| 1691 if (graycolors < 16) | |
| 1692 pixd = pixThresholdTo4bpp(pixg, graycolors, 1); | |
| 1693 else | |
| 1694 pixd = pixThresholdOn8bpp(pixg, graycolors, 1); | |
| 1695 pixDestroy(&pixg); | |
| 1696 } | |
| 1697 *ppixd = pixd; | |
| 1698 | |
| 1699 if (!pixd) | |
| 1700 return ERROR_INT("pixd not made", __func__, 1); | |
| 1701 pixCopyInputFormat(pixd, pixs); | |
| 1702 return 0; | |
| 1703 } | |
| 1704 | |
| 1705 | |
| 1706 | |
| 1707 /*---------------------------------------------------------------------------* | |
| 1708 * Conversion from 16 bpp to 8 bpp * | |
| 1709 *---------------------------------------------------------------------------*/ | |
| 1710 /*! | |
| 1711 * \brief pixConvert16To8() | |
| 1712 * | |
| 1713 * \param[in] pixs 16 bpp | |
| 1714 * \param[in] type L_LS_BYTE, L_MS_BYTE, L_AUTO_BYTE, L_CLIP_TO_FF | |
| 1715 * \return pixd 8 bpp, or NULL on error | |
| 1716 * | |
| 1717 * <pre> | |
| 1718 * Notes: | |
| 1719 * (1) With L_AUTO_BYTE, if the max pixel value is greater than 255, | |
| 1720 * use the MSB; otherwise, use the LSB. | |
| 1721 * (2) With L_CLIP_TO_FF, use min(pixel-value, 0xff) for each | |
| 1722 * 16-bit src pixel. | |
| 1723 * </pre> | |
| 1724 */ | |
| 1725 PIX * | |
| 1726 pixConvert16To8(PIX *pixs, | |
| 1727 l_int32 type) | |
| 1728 { | |
| 1729 l_uint16 dword; | |
| 1730 l_int32 w, h, wpls, wpld, i, j, val, use_lsb; | |
| 1731 l_uint32 sword, first, second; | |
| 1732 l_uint32 *datas, *datad, *lines, *lined; | |
| 1733 PIX *pixd; | |
| 1734 | |
| 1735 if (!pixs) | |
| 1736 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1737 if (pixGetDepth(pixs) != 16) | |
| 1738 return (PIX *)ERROR_PTR("pixs not 16 bpp", __func__, NULL); | |
| 1739 if (type != L_LS_BYTE && type != L_MS_BYTE && | |
| 1740 type != L_AUTO_BYTE && type != L_CLIP_TO_FF) | |
| 1741 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 1742 | |
| 1743 pixGetDimensions(pixs, &w, &h, NULL); | |
| 1744 if ((pixd = pixCreate(w, h, 8)) == NULL) | |
| 1745 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1746 pixCopyInputFormat(pixd, pixs); | |
| 1747 pixCopyResolution(pixd, pixs); | |
| 1748 wpls = pixGetWpl(pixs); | |
| 1749 datas = pixGetData(pixs); | |
| 1750 wpld = pixGetWpl(pixd); | |
| 1751 datad = pixGetData(pixd); | |
| 1752 | |
| 1753 if (type == L_AUTO_BYTE) { | |
| 1754 use_lsb = TRUE; | |
| 1755 for (i = 0; i < h; i++) { | |
| 1756 lines = datas + i * wpls; | |
| 1757 for (j = 0; j < wpls; j++) { | |
| 1758 val = GET_DATA_TWO_BYTES(lines, j); | |
| 1759 if (val > 255) { | |
| 1760 use_lsb = FALSE; | |
| 1761 break; | |
| 1762 } | |
| 1763 } | |
| 1764 if (!use_lsb) break; | |
| 1765 } | |
| 1766 type = (use_lsb) ? L_LS_BYTE : L_MS_BYTE; | |
| 1767 } | |
| 1768 | |
| 1769 /* Convert 2 pixels at a time */ | |
| 1770 for (i = 0; i < h; i++) { | |
| 1771 lines = datas + i * wpls; | |
| 1772 lined = datad + i * wpld; | |
| 1773 if (type == L_LS_BYTE) { | |
| 1774 for (j = 0; j < wpls; j++) { | |
| 1775 sword = *(lines + j); | |
| 1776 dword = ((sword >> 8) & 0xff00) | (sword & 0xff); | |
| 1777 SET_DATA_TWO_BYTES(lined, j, dword); | |
| 1778 } | |
| 1779 } else if (type == L_MS_BYTE) { | |
| 1780 for (j = 0; j < wpls; j++) { | |
| 1781 sword = *(lines + j); | |
| 1782 dword = ((sword >> 16) & 0xff00) | ((sword >> 8) & 0xff); | |
| 1783 SET_DATA_TWO_BYTES(lined, j, dword); | |
| 1784 } | |
| 1785 } else { /* type == L_CLIP_TO_FF */ | |
| 1786 for (j = 0; j < wpls; j++) { | |
| 1787 sword = *(lines + j); | |
| 1788 first = (sword >> 24) ? 255 : ((sword >> 16) & 0xff); | |
| 1789 second = ((sword >> 8) & 0xff) ? 255 : (sword & 0xff); | |
| 1790 dword = (first << 8) | second; | |
| 1791 SET_DATA_TWO_BYTES(lined, j, dword); | |
| 1792 } | |
| 1793 } | |
| 1794 } | |
| 1795 | |
| 1796 return pixd; | |
| 1797 } | |
| 1798 | |
| 1799 | |
| 1800 /*---------------------------------------------------------------------------* | |
| 1801 * Conversion from grayscale to false color | |
| 1802 *---------------------------------------------------------------------------*/ | |
| 1803 /*! | |
| 1804 * \brief pixConvertGrayToFalseColor() | |
| 1805 * | |
| 1806 * \param[in] pixs 8 or 16 bpp grayscale | |
| 1807 * \param[in] gamma (factor) 0.0 or 1.0 for default; > 1.0 for brighter; | |
| 1808 * 2.0 is quite nice | |
| 1809 * \return pixd 8 bpp with colormap, or NULL on error | |
| 1810 * | |
| 1811 * <pre> | |
| 1812 * Notes: | |
| 1813 * (1) For 8 bpp input, this simply adds a colormap to the input image. | |
| 1814 * (2) For 16 bpp input, it first converts to 8 bpp, using the MSB, | |
| 1815 * and then adds the colormap. | |
| 1816 * (3) The colormap is modeled after the Matlab "jet" configuration. | |
| 1817 * </pre> | |
| 1818 */ | |
| 1819 PIX * | |
| 1820 pixConvertGrayToFalseColor(PIX *pixs, | |
| 1821 l_float32 gamma) | |
| 1822 { | |
| 1823 l_int32 d; | |
| 1824 PIX *pixd; | |
| 1825 PIXCMAP *cmap; | |
| 1826 | |
| 1827 if (!pixs) | |
| 1828 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1829 d = pixGetDepth(pixs); | |
| 1830 if (d != 8 && d != 16) | |
| 1831 return (PIX *)ERROR_PTR("pixs not 8 or 16 bpp", __func__, NULL); | |
| 1832 | |
| 1833 if (d == 16) { | |
| 1834 pixd = pixConvert16To8(pixs, L_MS_BYTE); | |
| 1835 } else { /* d == 8 */ | |
| 1836 if (pixGetColormap(pixs)) | |
| 1837 pixd = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 1838 else | |
| 1839 pixd = pixCopy(NULL, pixs); | |
| 1840 } | |
| 1841 if (!pixd) | |
| 1842 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1843 | |
| 1844 cmap = pixcmapGrayToFalseColor(gamma); | |
| 1845 pixSetColormap(pixd, cmap); | |
| 1846 pixCopyResolution(pixd, pixs); | |
| 1847 pixCopyInputFormat(pixd, pixs); | |
| 1848 return pixd; | |
| 1849 } | |
| 1850 | |
| 1851 | |
| 1852 /*---------------------------------------------------------------------------* | |
| 1853 * Unpacking conversion from 1 bpp to 2, 4, 8, 16 and 32 bpp * | |
| 1854 *---------------------------------------------------------------------------*/ | |
| 1855 /*! | |
| 1856 * \brief pixUnpackBinary() | |
| 1857 * | |
| 1858 * \param[in] pixs 1 bpp | |
| 1859 * \param[in] depth of destination: 2, 4, 8, 16 or 32 bpp | |
| 1860 * \param[in] invert 0: binary 0 --> grayscale 0 | |
| 1861 * binary 1 --> grayscale 0xff... | |
| 1862 * 1: binary 0 --> grayscale 0xff... | |
| 1863 * binary 1 --> grayscale 0 | |
| 1864 * \return pixd 2, 4, 8, 16 or 32 bpp, or NULL on error | |
| 1865 * | |
| 1866 * <pre> | |
| 1867 * Notes: | |
| 1868 * (1) This function calls special cases of pixConvert1To*(), | |
| 1869 * for 2, 4, 8, 16 and 32 bpp destinations. | |
| 1870 * </pre> | |
| 1871 */ | |
| 1872 PIX * | |
| 1873 pixUnpackBinary(PIX *pixs, | |
| 1874 l_int32 depth, | |
| 1875 l_int32 invert) | |
| 1876 { | |
| 1877 PIX *pixd; | |
| 1878 | |
| 1879 if (!pixs) | |
| 1880 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1881 if (pixGetDepth(pixs) != 1) | |
| 1882 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); | |
| 1883 if (depth != 2 && depth != 4 && depth != 8 && depth != 16 && depth != 32) | |
| 1884 return (PIX *)ERROR_PTR("depth not 2, 4, 8, 16 or 32 bpp", | |
| 1885 __func__, NULL); | |
| 1886 | |
| 1887 if (depth == 2) { | |
| 1888 if (invert == 0) | |
| 1889 pixd = pixConvert1To2(NULL, pixs, 0, 3); | |
| 1890 else /* invert bits */ | |
| 1891 pixd = pixConvert1To2(NULL, pixs, 3, 0); | |
| 1892 } else if (depth == 4) { | |
| 1893 if (invert == 0) | |
| 1894 pixd = pixConvert1To4(NULL, pixs, 0, 15); | |
| 1895 else /* invert bits */ | |
| 1896 pixd = pixConvert1To4(NULL, pixs, 15, 0); | |
| 1897 } else if (depth == 8) { | |
| 1898 if (invert == 0) | |
| 1899 pixd = pixConvert1To8(NULL, pixs, 0, 255); | |
| 1900 else /* invert bits */ | |
| 1901 pixd = pixConvert1To8(NULL, pixs, 255, 0); | |
| 1902 } else if (depth == 16) { | |
| 1903 if (invert == 0) | |
| 1904 pixd = pixConvert1To16(NULL, pixs, 0, 0xffff); | |
| 1905 else /* invert bits */ | |
| 1906 pixd = pixConvert1To16(NULL, pixs, 0xffff, 0); | |
| 1907 } else { | |
| 1908 if (invert == 0) | |
| 1909 pixd = pixConvert1To32(NULL, pixs, 0, 0xffffffff); | |
| 1910 else /* invert bits */ | |
| 1911 pixd = pixConvert1To32(NULL, pixs, 0xffffffff, 0); | |
| 1912 } | |
| 1913 | |
| 1914 pixCopyInputFormat(pixd, pixs); | |
| 1915 return pixd; | |
| 1916 } | |
| 1917 | |
| 1918 | |
| 1919 /*! | |
| 1920 * \brief pixConvert1To16() | |
| 1921 * | |
| 1922 * \param[in] pixd [optional] 16 bpp, can be null | |
| 1923 * \param[in] pixs 1 bpp | |
| 1924 * \param[in] val0 16 bit value to be used for 0s in pixs | |
| 1925 * \param[in] val1 16 bit value to be used for 1s in pixs | |
| 1926 * \return pixd 16 bpp | |
| 1927 * | |
| 1928 * <pre> | |
| 1929 * Notes: | |
| 1930 * (1) If pixd is null, a new pix is made. | |
| 1931 * (2) If pixd is not null, it must be of equal width and height | |
| 1932 * as pixs. It is always returned. | |
| 1933 * </pre> | |
| 1934 */ | |
| 1935 PIX * | |
| 1936 pixConvert1To16(PIX *pixd, | |
| 1937 PIX *pixs, | |
| 1938 l_uint16 val0, | |
| 1939 l_uint16 val1) | |
| 1940 { | |
| 1941 l_int32 w, h, i, j, dibit, ndibits, wpls, wpld; | |
| 1942 l_uint16 val[2]; | |
| 1943 l_uint32 index; | |
| 1944 l_uint32 *tab, *datas, *datad, *lines, *lined; | |
| 1945 | |
| 1946 if (!pixs) | |
| 1947 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1948 if (pixGetDepth(pixs) != 1) | |
| 1949 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); | |
| 1950 | |
| 1951 pixGetDimensions(pixs, &w, &h, NULL); | |
| 1952 if (pixd) { | |
| 1953 if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) | |
| 1954 return (PIX *)ERROR_PTR("pix sizes unequal", __func__, pixd); | |
| 1955 if (pixGetDepth(pixd) != 16) | |
| 1956 return (PIX *)ERROR_PTR("pixd not 16 bpp", __func__, pixd); | |
| 1957 } else { | |
| 1958 if ((pixd = pixCreate(w, h, 16)) == NULL) | |
| 1959 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 1960 } | |
| 1961 pixCopyResolution(pixd, pixs); | |
| 1962 pixCopyInputFormat(pixd, pixs); | |
| 1963 | |
| 1964 /* Use a table to convert 2 src bits at a time */ | |
| 1965 tab = (l_uint32 *)LEPT_CALLOC(4, sizeof(l_uint32)); | |
| 1966 val[0] = val0; | |
| 1967 val[1] = val1; | |
| 1968 for (index = 0; index < 4; index++) { | |
| 1969 tab[index] = (val[(index >> 1) & 1] << 16) | val[index & 1]; | |
| 1970 } | |
| 1971 | |
| 1972 datas = pixGetData(pixs); | |
| 1973 wpls = pixGetWpl(pixs); | |
| 1974 datad = pixGetData(pixd); | |
| 1975 wpld = pixGetWpl(pixd); | |
| 1976 ndibits = (w + 1) / 2; | |
| 1977 for (i = 0; i < h; i++) { | |
| 1978 lines = datas + i * wpls; | |
| 1979 lined = datad + i * wpld; | |
| 1980 for (j = 0; j < ndibits; j++) { | |
| 1981 dibit = GET_DATA_DIBIT(lines, j); | |
| 1982 lined[j] = tab[dibit]; | |
| 1983 } | |
| 1984 } | |
| 1985 | |
| 1986 LEPT_FREE(tab); | |
| 1987 return pixd; | |
| 1988 } | |
| 1989 | |
| 1990 | |
| 1991 /*! | |
| 1992 * \brief pixConvert1To32() | |
| 1993 * | |
| 1994 * \param[in] pixd [optional] 32 bpp, can be null | |
| 1995 * \param[in] pixs 1 bpp | |
| 1996 * \param[in] val0 32 bit value to be used for 0s in pixs | |
| 1997 * \param[in] val1 32 bit value to be used for 1s in pixs | |
| 1998 * \return pixd 32 bpp | |
| 1999 * | |
| 2000 * <pre> | |
| 2001 * Notes: | |
| 2002 * (1) If pixd is null, a new pix is made. | |
| 2003 * (2) If pixd is not null, it must be of equal width and height | |
| 2004 * as pixs. It is always returned. | |
| 2005 * </pre> | |
| 2006 */ | |
| 2007 PIX * | |
| 2008 pixConvert1To32(PIX *pixd, | |
| 2009 PIX *pixs, | |
| 2010 l_uint32 val0, | |
| 2011 l_uint32 val1) | |
| 2012 { | |
| 2013 l_int32 w, h, i, j, wpls, wpld, bit; | |
| 2014 l_uint32 val[2]; | |
| 2015 l_uint32 *datas, *datad, *lines, *lined; | |
| 2016 | |
| 2017 if (!pixs) | |
| 2018 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2019 if (pixGetDepth(pixs) != 1) | |
| 2020 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); | |
| 2021 | |
| 2022 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2023 if (pixd) { | |
| 2024 if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) | |
| 2025 return (PIX *)ERROR_PTR("pix sizes unequal", __func__, pixd); | |
| 2026 if (pixGetDepth(pixd) != 32) | |
| 2027 return (PIX *)ERROR_PTR("pixd not 32 bpp", __func__, pixd); | |
| 2028 } else { | |
| 2029 if ((pixd = pixCreate(w, h, 32)) == NULL) | |
| 2030 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2031 } | |
| 2032 pixCopyResolution(pixd, pixs); | |
| 2033 pixCopyInputFormat(pixd, pixs); | |
| 2034 | |
| 2035 val[0] = val0; | |
| 2036 val[1] = val1; | |
| 2037 datas = pixGetData(pixs); | |
| 2038 wpls = pixGetWpl(pixs); | |
| 2039 datad = pixGetData(pixd); | |
| 2040 wpld = pixGetWpl(pixd); | |
| 2041 for (i = 0; i < h; i++) { | |
| 2042 lines = datas + i * wpls; | |
| 2043 lined = datad + i * wpld; | |
| 2044 for (j = 0; j <w; j++) { | |
| 2045 bit = GET_DATA_BIT(lines, j); | |
| 2046 lined[j] = val[bit]; | |
| 2047 } | |
| 2048 } | |
| 2049 | |
| 2050 return pixd; | |
| 2051 } | |
| 2052 | |
| 2053 | |
| 2054 /*---------------------------------------------------------------------------* | |
| 2055 * Conversion from 1 bpp to 2 bpp * | |
| 2056 *---------------------------------------------------------------------------*/ | |
| 2057 /*! | |
| 2058 * \brief pixConvert1To2Cmap() | |
| 2059 * | |
| 2060 * \param[in] pixs 1 bpp | |
| 2061 * \return pixd 2 bpp, cmapped | |
| 2062 * | |
| 2063 * <pre> | |
| 2064 * Notes: | |
| 2065 * (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0) | |
| 2066 * </pre> | |
| 2067 */ | |
| 2068 PIX * | |
| 2069 pixConvert1To2Cmap(PIX *pixs) | |
| 2070 { | |
| 2071 PIX *pixd; | |
| 2072 PIXCMAP *cmap; | |
| 2073 | |
| 2074 if (!pixs) | |
| 2075 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2076 if (pixGetDepth(pixs) != 1) | |
| 2077 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); | |
| 2078 | |
| 2079 if ((pixd = pixConvert1To2(NULL, pixs, 0, 1)) == NULL) | |
| 2080 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2081 cmap = pixcmapCreate(2); | |
| 2082 pixcmapAddColor(cmap, 255, 255, 255); | |
| 2083 pixcmapAddColor(cmap, 0, 0, 0); | |
| 2084 pixSetColormap(pixd, cmap); | |
| 2085 pixCopyInputFormat(pixd, pixs); | |
| 2086 | |
| 2087 return pixd; | |
| 2088 } | |
| 2089 | |
| 2090 | |
| 2091 /*! | |
| 2092 * \brief pixConvert1To2() | |
| 2093 * | |
| 2094 * \param[in] pixd [optional] 2 bpp, can be null | |
| 2095 * \param[in] pixs 1 bpp | |
| 2096 * \param[in] val0 2 bit value to be used for 0s in pixs | |
| 2097 * \param[in] val1 2 bit value to be used for 1s in pixs | |
| 2098 * \return pixd 2 bpp | |
| 2099 * | |
| 2100 * <pre> | |
| 2101 * Notes: | |
| 2102 * (1) If pixd is null, a new pix is made. | |
| 2103 * (2) If pixd is not null, it must be of equal width and height | |
| 2104 * as pixs. It is always returned. | |
| 2105 * (3) A simple unpacking might use val0 = 0 and val1 = 3. | |
| 2106 * (4) If you want a colormapped pixd, use pixConvert1To2Cmap(). | |
| 2107 * </pre> | |
| 2108 */ | |
| 2109 PIX * | |
| 2110 pixConvert1To2(PIX *pixd, | |
| 2111 PIX *pixs, | |
| 2112 l_int32 val0, | |
| 2113 l_int32 val1) | |
| 2114 { | |
| 2115 l_int32 w, h, i, j, byteval, nbytes, wpls, wpld; | |
| 2116 l_uint8 val[2]; | |
| 2117 l_uint32 index; | |
| 2118 l_uint16 *tab; | |
| 2119 l_uint32 *datas, *datad, *lines, *lined; | |
| 2120 | |
| 2121 if (!pixs) | |
| 2122 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 2123 if (pixGetDepth(pixs) != 1) | |
| 2124 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 2125 | |
| 2126 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2127 if (pixd) { | |
| 2128 if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) | |
| 2129 return (PIX *)ERROR_PTR("pix sizes unequal", __func__, pixd); | |
| 2130 if (pixGetDepth(pixd) != 2) | |
| 2131 return (PIX *)ERROR_PTR("pixd not 2 bpp", __func__, pixd); | |
| 2132 } else { | |
| 2133 if ((pixd = pixCreate(w, h, 2)) == NULL) | |
| 2134 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2135 } | |
| 2136 pixCopyResolution(pixd, pixs); | |
| 2137 pixCopyInputFormat(pixd, pixs); | |
| 2138 | |
| 2139 /* Use a table to convert 8 src bits to 16 dest bits */ | |
| 2140 tab = (l_uint16 *)LEPT_CALLOC(256, sizeof(l_uint16)); | |
| 2141 val[0] = val0; | |
| 2142 val[1] = val1; | |
| 2143 for (index = 0; index < 256; index++) { | |
| 2144 tab[index] = (val[(index >> 7) & 1] << 14) | | |
| 2145 (val[(index >> 6) & 1] << 12) | | |
| 2146 (val[(index >> 5) & 1] << 10) | | |
| 2147 (val[(index >> 4) & 1] << 8) | | |
| 2148 (val[(index >> 3) & 1] << 6) | | |
| 2149 (val[(index >> 2) & 1] << 4) | | |
| 2150 (val[(index >> 1) & 1] << 2) | val[index & 1]; | |
| 2151 } | |
| 2152 | |
| 2153 datas = pixGetData(pixs); | |
| 2154 wpls = pixGetWpl(pixs); | |
| 2155 datad = pixGetData(pixd); | |
| 2156 wpld = pixGetWpl(pixd); | |
| 2157 nbytes = (w + 7) / 8; | |
| 2158 for (i = 0; i < h; i++) { | |
| 2159 lines = datas + i * wpls; | |
| 2160 lined = datad + i * wpld; | |
| 2161 for (j = 0; j < nbytes; j++) { | |
| 2162 byteval = GET_DATA_BYTE(lines, j); | |
| 2163 SET_DATA_TWO_BYTES(lined, j, tab[byteval]); | |
| 2164 } | |
| 2165 } | |
| 2166 | |
| 2167 LEPT_FREE(tab); | |
| 2168 return pixd; | |
| 2169 } | |
| 2170 | |
| 2171 | |
| 2172 /*---------------------------------------------------------------------------* | |
| 2173 * Conversion from 1 bpp to 4 bpp * | |
| 2174 *---------------------------------------------------------------------------*/ | |
| 2175 /*! | |
| 2176 * \brief pixConvert1To4Cmap() | |
| 2177 * | |
| 2178 * \param[in] pixs 1 bpp | |
| 2179 * \return pixd 4 bpp, cmapped | |
| 2180 * | |
| 2181 * <pre> | |
| 2182 * Notes: | |
| 2183 * (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0) | |
| 2184 * </pre> | |
| 2185 */ | |
| 2186 PIX * | |
| 2187 pixConvert1To4Cmap(PIX *pixs) | |
| 2188 { | |
| 2189 PIX *pixd; | |
| 2190 PIXCMAP *cmap; | |
| 2191 | |
| 2192 if (!pixs) | |
| 2193 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2194 if (pixGetDepth(pixs) != 1) | |
| 2195 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); | |
| 2196 | |
| 2197 if ((pixd = pixConvert1To4(NULL, pixs, 0, 1)) == NULL) | |
| 2198 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2199 cmap = pixcmapCreate(4); | |
| 2200 pixcmapAddColor(cmap, 255, 255, 255); | |
| 2201 pixcmapAddColor(cmap, 0, 0, 0); | |
| 2202 pixSetColormap(pixd, cmap); | |
| 2203 pixCopyInputFormat(pixd, pixs); | |
| 2204 | |
| 2205 return pixd; | |
| 2206 } | |
| 2207 | |
| 2208 | |
| 2209 /*! | |
| 2210 * \brief pixConvert1To4() | |
| 2211 * | |
| 2212 * \param[in] pixd [optional] 4 bpp, can be null | |
| 2213 * \param[in] pixs 1 bpp | |
| 2214 * \param[in] val0 4 bit value to be used for 0s in pixs | |
| 2215 * \param[in] val1 4 bit value to be used for 1s in pixs | |
| 2216 * \return pixd 4 bpp | |
| 2217 * | |
| 2218 * <pre> | |
| 2219 * Notes: | |
| 2220 * (1) If pixd is null, a new pix is made. | |
| 2221 * (2) If pixd is not null, it must be of equal width and height | |
| 2222 * as pixs. It is always returned. | |
| 2223 * (3) A simple unpacking might use val0 = 0 and val1 = 15, or v.v. | |
| 2224 * (4) If you want a colormapped pixd, use pixConvert1To4Cmap(). | |
| 2225 * </pre> | |
| 2226 */ | |
| 2227 PIX * | |
| 2228 pixConvert1To4(PIX *pixd, | |
| 2229 PIX *pixs, | |
| 2230 l_int32 val0, | |
| 2231 l_int32 val1) | |
| 2232 { | |
| 2233 l_int32 w, h, i, j, byteval, nbytes, wpls, wpld; | |
| 2234 l_uint8 val[2]; | |
| 2235 l_uint32 index; | |
| 2236 l_uint32 *tab, *datas, *datad, *lines, *lined; | |
| 2237 | |
| 2238 if (!pixs) | |
| 2239 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 2240 if (pixGetDepth(pixs) != 1) | |
| 2241 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 2242 | |
| 2243 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2244 if (pixd) { | |
| 2245 if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) | |
| 2246 return (PIX *)ERROR_PTR("pix sizes unequal", __func__, pixd); | |
| 2247 if (pixGetDepth(pixd) != 4) | |
| 2248 return (PIX *)ERROR_PTR("pixd not 4 bpp", __func__, pixd); | |
| 2249 } else { | |
| 2250 if ((pixd = pixCreate(w, h, 4)) == NULL) | |
| 2251 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2252 } | |
| 2253 pixCopyResolution(pixd, pixs); | |
| 2254 pixCopyInputFormat(pixd, pixs); | |
| 2255 | |
| 2256 /* Use a table to convert 8 src bits to 32 bit dest word */ | |
| 2257 tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); | |
| 2258 val[0] = val0; | |
| 2259 val[1] = val1; | |
| 2260 for (index = 0; index < 256; index++) { | |
| 2261 tab[index] = (val[(index >> 7) & 1] << 28) | | |
| 2262 (val[(index >> 6) & 1] << 24) | | |
| 2263 (val[(index >> 5) & 1] << 20) | | |
| 2264 (val[(index >> 4) & 1] << 16) | | |
| 2265 (val[(index >> 3) & 1] << 12) | | |
| 2266 (val[(index >> 2) & 1] << 8) | | |
| 2267 (val[(index >> 1) & 1] << 4) | val[index & 1]; | |
| 2268 } | |
| 2269 | |
| 2270 datas = pixGetData(pixs); | |
| 2271 wpls = pixGetWpl(pixs); | |
| 2272 datad = pixGetData(pixd); | |
| 2273 wpld = pixGetWpl(pixd); | |
| 2274 nbytes = (w + 7) / 8; | |
| 2275 for (i = 0; i < h; i++) { | |
| 2276 lines = datas + i * wpls; | |
| 2277 lined = datad + i * wpld; | |
| 2278 for (j = 0; j < nbytes; j++) { | |
| 2279 byteval = GET_DATA_BYTE(lines, j); | |
| 2280 lined[j] = tab[byteval]; | |
| 2281 } | |
| 2282 } | |
| 2283 | |
| 2284 LEPT_FREE(tab); | |
| 2285 return pixd; | |
| 2286 } | |
| 2287 | |
| 2288 | |
| 2289 /*---------------------------------------------------------------------------* | |
| 2290 * Conversion from 1, 2 and 4 bpp to 8 bpp * | |
| 2291 *---------------------------------------------------------------------------*/ | |
| 2292 /*! | |
| 2293 * \brief pixConvert1To8Cmap() | |
| 2294 * | |
| 2295 * \param[in] pixs 1 bpp | |
| 2296 * \return pixd 8 bpp, cmapped | |
| 2297 * | |
| 2298 * <pre> | |
| 2299 * Notes: | |
| 2300 * (1) Input 0 is mapped to (255, 255, 255); 1 is mapped to (0, 0, 0) | |
| 2301 * </pre> | |
| 2302 */ | |
| 2303 PIX * | |
| 2304 pixConvert1To8Cmap(PIX *pixs) | |
| 2305 { | |
| 2306 PIX *pixd; | |
| 2307 PIXCMAP *cmap; | |
| 2308 | |
| 2309 if (!pixs) | |
| 2310 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2311 if (pixGetDepth(pixs) != 1) | |
| 2312 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, NULL); | |
| 2313 | |
| 2314 if ((pixd = pixConvert1To8(NULL, pixs, 0, 1)) == NULL) | |
| 2315 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2316 cmap = pixcmapCreate(8); | |
| 2317 pixcmapAddColor(cmap, 255, 255, 255); | |
| 2318 pixcmapAddColor(cmap, 0, 0, 0); | |
| 2319 pixSetColormap(pixd, cmap); | |
| 2320 pixCopyInputFormat(pixd, pixs); | |
| 2321 return pixd; | |
| 2322 } | |
| 2323 | |
| 2324 | |
| 2325 /*! | |
| 2326 * \brief pixConvert1To8() | |
| 2327 * | |
| 2328 * \param[in] pixd [optional] 8 bpp, can be null | |
| 2329 * \param[in] pixs 1 bpp | |
| 2330 * \param[in] val0 8 bit value to be used for 0s in pixs | |
| 2331 * \param[in] val1 8 bit value to be used for 1s in pixs | |
| 2332 * \return pixd 8 bpp | |
| 2333 * | |
| 2334 * <pre> | |
| 2335 * Notes: | |
| 2336 * (1) If pixd is null, a new pix is made. | |
| 2337 * (2) If pixd is not null, it must be of equal width and height | |
| 2338 * as pixs. It is always returned. | |
| 2339 * (3) A simple unpacking might use val0 = 0 and val1 = 255, or v.v. | |
| 2340 * (4) To have a colormap associated with the 8 bpp pixd, | |
| 2341 * use pixConvert1To8Cmap(). | |
| 2342 * </pre> | |
| 2343 */ | |
| 2344 PIX * | |
| 2345 pixConvert1To8(PIX *pixd, | |
| 2346 PIX *pixs, | |
| 2347 l_uint8 val0, | |
| 2348 l_uint8 val1) | |
| 2349 { | |
| 2350 l_int32 w, h, i, j, qbit, nqbits, wpls, wpld; | |
| 2351 l_uint8 val[2]; | |
| 2352 l_uint32 index; | |
| 2353 l_uint32 *tab, *datas, *datad, *lines, *lined; | |
| 2354 | |
| 2355 if (!pixs) | |
| 2356 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 2357 if (pixGetDepth(pixs) != 1) | |
| 2358 return (PIX *)ERROR_PTR("pixs not 1 bpp", __func__, pixd); | |
| 2359 | |
| 2360 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2361 if (pixd) { | |
| 2362 if (w != pixGetWidth(pixd) || h != pixGetHeight(pixd)) | |
| 2363 return (PIX *)ERROR_PTR("pix sizes unequal", __func__, pixd); | |
| 2364 if (pixGetDepth(pixd) != 8) | |
| 2365 return (PIX *)ERROR_PTR("pixd not 8 bpp", __func__, pixd); | |
| 2366 } else { | |
| 2367 if ((pixd = pixCreate(w, h, 8)) == NULL) | |
| 2368 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2369 } | |
| 2370 pixCopyResolution(pixd, pixs); | |
| 2371 pixCopyInputFormat(pixd, pixs); | |
| 2372 pixSetPadBits(pixs, 0); | |
| 2373 | |
| 2374 /* Use a table to convert 4 src bits at a time */ | |
| 2375 tab = (l_uint32 *)LEPT_CALLOC(16, sizeof(l_uint32)); | |
| 2376 val[0] = val0; | |
| 2377 val[1] = val1; | |
| 2378 for (index = 0; index < 16; index++) { | |
| 2379 tab[index] = ((l_uint32)val[(index >> 3) & 1] << 24) | | |
| 2380 (val[(index >> 2) & 1] << 16) | | |
| 2381 (val[(index >> 1) & 1] << 8) | val[index & 1]; | |
| 2382 } | |
| 2383 | |
| 2384 datas = pixGetData(pixs); | |
| 2385 wpls = pixGetWpl(pixs); | |
| 2386 datad = pixGetData(pixd); | |
| 2387 wpld = pixGetWpl(pixd); | |
| 2388 nqbits = (w + 3) / 4; | |
| 2389 for (i = 0; i < h; i++) { | |
| 2390 lines = datas + i * wpls; | |
| 2391 lined = datad + i * wpld; | |
| 2392 for (j = 0; j < nqbits; j++) { | |
| 2393 qbit = GET_DATA_QBIT(lines, j); | |
| 2394 lined[j] = tab[qbit]; | |
| 2395 } | |
| 2396 } | |
| 2397 | |
| 2398 LEPT_FREE(tab); | |
| 2399 return pixd; | |
| 2400 } | |
| 2401 | |
| 2402 | |
| 2403 /*! | |
| 2404 * \brief pixConvert2To8() | |
| 2405 * | |
| 2406 * \param[in] pixs 2 bpp | |
| 2407 * \param[in] val0 8 bit value to be used for 00 in pixs | |
| 2408 * \param[in] val1 8 bit value to be used for 01 in pixs | |
| 2409 * \param[in] val2 8 bit value to be used for 10 in pixs | |
| 2410 * \param[in] val3 8 bit value to be used for 11 in pixs | |
| 2411 * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise | |
| 2412 * \return pixd 8 bpp, or NULL on error | |
| 2413 * | |
| 2414 * <pre> | |
| 2415 * Notes: | |
| 2416 * ~ A simple unpacking might use val0 = 0, | |
| 2417 * val1 = 85 (0x55), val2 = 170 (0xaa), val3 = 255. | |
| 2418 * ~ If cmapflag is TRUE: | |
| 2419 * ~ The 8 bpp image is made with a colormap. | |
| 2420 * ~ If pixs has a colormap, the input values are ignored and | |
| 2421 * the 8 bpp image is made using the colormap | |
| 2422 * ~ If pixs does not have a colormap, the input values are | |
| 2423 * used to build the colormap. | |
| 2424 * ~ If cmapflag is FALSE: | |
| 2425 * ~ The 8 bpp image is made without a colormap. | |
| 2426 * ~ If pixs has a colormap, the input values are ignored, | |
| 2427 * the colormap is removed, and the values stored in the 8 bpp | |
| 2428 * image are from the colormap. | |
| 2429 * ~ If pixs does not have a colormap, the input values are | |
| 2430 * used to populate the 8 bpp image. | |
| 2431 * </pre> | |
| 2432 */ | |
| 2433 PIX * | |
| 2434 pixConvert2To8(PIX *pixs, | |
| 2435 l_uint8 val0, | |
| 2436 l_uint8 val1, | |
| 2437 l_uint8 val2, | |
| 2438 l_uint8 val3, | |
| 2439 l_int32 cmapflag) | |
| 2440 { | |
| 2441 l_int32 w, h, i, j, nbytes, wpls, wpld, dibit, byte; | |
| 2442 l_uint32 val[4]; | |
| 2443 l_uint32 index; | |
| 2444 l_uint32 *tab, *datas, *datad, *lines, *lined; | |
| 2445 PIX *pixd; | |
| 2446 PIXCMAP *cmaps, *cmapd; | |
| 2447 | |
| 2448 if (!pixs) | |
| 2449 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2450 if (pixGetDepth(pixs) != 2) | |
| 2451 return (PIX *)ERROR_PTR("pixs not 2 bpp", __func__, NULL); | |
| 2452 | |
| 2453 cmaps = pixGetColormap(pixs); | |
| 2454 if (cmaps && cmapflag == FALSE) | |
| 2455 return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 2456 | |
| 2457 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2458 if ((pixd = pixCreate(w, h, 8)) == NULL) | |
| 2459 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2460 pixSetPadBits(pixs, 0); | |
| 2461 pixCopyResolution(pixd, pixs); | |
| 2462 pixCopyInputFormat(pixd, pixs); | |
| 2463 datas = pixGetData(pixs); | |
| 2464 wpls = pixGetWpl(pixs); | |
| 2465 datad = pixGetData(pixd); | |
| 2466 wpld = pixGetWpl(pixd); | |
| 2467 | |
| 2468 if (cmapflag == TRUE) { /* pixd will have a colormap */ | |
| 2469 if (cmaps) { /* use the existing colormap from pixs */ | |
| 2470 cmapd = pixcmapConvertTo8(cmaps); | |
| 2471 } else { /* make a colormap from the input values */ | |
| 2472 cmapd = pixcmapCreate(8); | |
| 2473 pixcmapAddColor(cmapd, val0, val0, val0); | |
| 2474 pixcmapAddColor(cmapd, val1, val1, val1); | |
| 2475 pixcmapAddColor(cmapd, val2, val2, val2); | |
| 2476 pixcmapAddColor(cmapd, val3, val3, val3); | |
| 2477 } | |
| 2478 pixSetColormap(pixd, cmapd); | |
| 2479 for (i = 0; i < h; i++) { | |
| 2480 lines = datas + i * wpls; | |
| 2481 lined = datad + i * wpld; | |
| 2482 for (j = 0; j < w; j++) { | |
| 2483 dibit = GET_DATA_DIBIT(lines, j); | |
| 2484 SET_DATA_BYTE(lined, j, dibit); | |
| 2485 } | |
| 2486 } | |
| 2487 return pixd; | |
| 2488 } | |
| 2489 | |
| 2490 /* Last case: no colormap in either pixs or pixd. | |
| 2491 * Use input values and build a table to convert 1 src byte | |
| 2492 * (4 src pixels) at a time */ | |
| 2493 tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); | |
| 2494 val[0] = val0; | |
| 2495 val[1] = val1; | |
| 2496 val[2] = val2; | |
| 2497 val[3] = val3; | |
| 2498 for (index = 0; index < 256; index++) { | |
| 2499 tab[index] = (val[(index >> 6) & 3] << 24) | | |
| 2500 (val[(index >> 4) & 3] << 16) | | |
| 2501 (val[(index >> 2) & 3] << 8) | val[index & 3]; | |
| 2502 } | |
| 2503 | |
| 2504 nbytes = (w + 3) / 4; | |
| 2505 for (i = 0; i < h; i++) { | |
| 2506 lines = datas + i * wpls; | |
| 2507 lined = datad + i * wpld; | |
| 2508 for (j = 0; j < nbytes; j++) { | |
| 2509 byte = GET_DATA_BYTE(lines, j); | |
| 2510 lined[j] = tab[byte]; | |
| 2511 } | |
| 2512 } | |
| 2513 | |
| 2514 LEPT_FREE(tab); | |
| 2515 return pixd; | |
| 2516 } | |
| 2517 | |
| 2518 | |
| 2519 /*! | |
| 2520 * \brief pixConvert4To8() | |
| 2521 * | |
| 2522 * \param[in] pixs 4 bpp | |
| 2523 * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise | |
| 2524 * \return pixd 8 bpp, or NULL on error | |
| 2525 * | |
| 2526 * <pre> | |
| 2527 * Notes: | |
| 2528 * ~ If cmapflag is TRUE: | |
| 2529 * ~ pixd is made with a colormap. | |
| 2530 * ~ If pixs has a colormap, it is copied and the colormap | |
| 2531 * index values are placed in pixd. | |
| 2532 * ~ If pixs does not have a colormap, a colormap with linear | |
| 2533 * trc is built and the pixel values in pixs are placed in | |
| 2534 * pixd as colormap index values. | |
| 2535 * ~ If cmapflag is FALSE: | |
| 2536 * ~ pixd is made without a colormap. | |
| 2537 * ~ If pixs has a colormap, it is removed and the values stored | |
| 2538 * in pixd are from the colormap (converted to gray). | |
| 2539 * ~ If pixs does not have a colormap, the pixel values in pixs | |
| 2540 * are used, with shift replication, to populate pixd. | |
| 2541 * </pre> | |
| 2542 */ | |
| 2543 PIX * | |
| 2544 pixConvert4To8(PIX *pixs, | |
| 2545 l_int32 cmapflag) | |
| 2546 { | |
| 2547 l_int32 w, h, i, j, wpls, wpld, byte, qbit; | |
| 2548 l_uint32 *datas, *datad, *lines, *lined; | |
| 2549 PIX *pixd; | |
| 2550 PIXCMAP *cmaps, *cmapd; | |
| 2551 | |
| 2552 if (!pixs) | |
| 2553 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2554 if (pixGetDepth(pixs) != 4) | |
| 2555 return (PIX *)ERROR_PTR("pixs not 4 bpp", __func__, NULL); | |
| 2556 | |
| 2557 cmaps = pixGetColormap(pixs); | |
| 2558 if (cmaps && cmapflag == FALSE) | |
| 2559 return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 2560 | |
| 2561 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2562 if ((pixd = pixCreate(w, h, 8)) == NULL) | |
| 2563 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 2564 pixCopyResolution(pixd, pixs); | |
| 2565 pixCopyInputFormat(pixd, pixs); | |
| 2566 datas = pixGetData(pixs); | |
| 2567 wpls = pixGetWpl(pixs); | |
| 2568 datad = pixGetData(pixd); | |
| 2569 wpld = pixGetWpl(pixd); | |
| 2570 | |
| 2571 if (cmapflag == TRUE) { /* pixd will have a colormap */ | |
| 2572 if (cmaps) { /* use the existing colormap from pixs */ | |
| 2573 cmapd = pixcmapConvertTo8(cmaps); | |
| 2574 } else { /* make a colormap with a linear trc */ | |
| 2575 cmapd = pixcmapCreate(8); | |
| 2576 for (i = 0; i < 16; i++) | |
| 2577 pixcmapAddColor(cmapd, 17 * i, 17 * i, 17 * i); | |
| 2578 } | |
| 2579 pixSetColormap(pixd, cmapd); | |
| 2580 for (i = 0; i < h; i++) { | |
| 2581 lines = datas + i * wpls; | |
| 2582 lined = datad + i * wpld; | |
| 2583 for (j = 0; j < w; j++) { | |
| 2584 qbit = GET_DATA_QBIT(lines, j); | |
| 2585 SET_DATA_BYTE(lined, j, qbit); | |
| 2586 } | |
| 2587 } | |
| 2588 return pixd; | |
| 2589 } | |
| 2590 | |
| 2591 /* Last case: no colormap in either pixs or pixd. | |
| 2592 * Replicate the qbit value into 8 bits. */ | |
| 2593 for (i = 0; i < h; i++) { | |
| 2594 lines = datas + i * wpls; | |
| 2595 lined = datad + i * wpld; | |
| 2596 for (j = 0; j < w; j++) { | |
| 2597 qbit = GET_DATA_QBIT(lines, j); | |
| 2598 byte = (qbit << 4) | qbit; | |
| 2599 SET_DATA_BYTE(lined, j, byte); | |
| 2600 } | |
| 2601 } | |
| 2602 return pixd; | |
| 2603 } | |
| 2604 | |
| 2605 | |
| 2606 | |
| 2607 /*---------------------------------------------------------------------------* | |
| 2608 * Unpacking conversion from 8 bpp to 16 bpp * | |
| 2609 *---------------------------------------------------------------------------*/ | |
| 2610 /*! | |
| 2611 * \brief pixConvert8To16() | |
| 2612 * | |
| 2613 * \param[in] pixs 8 bpp; colormap removed to gray | |
| 2614 * \param[in] leftshift number of bits: 0 is no shift; | |
| 2615 * 8 replicates in MSB and LSB of dest | |
| 2616 * \return pixd 16 bpp, or NULL on error | |
| 2617 * | |
| 2618 * <pre> | |
| 2619 * Notes: | |
| 2620 * (1) For left shift of 8, the 8 bit value is replicated in both | |
| 2621 * the MSB and the LSB of the pixels in pixd. That way, we get | |
| 2622 * proportional mapping, with a correct map from 8 bpp white | |
| 2623 * (0xff) to 16 bpp white (0xffff). | |
| 2624 * </pre> | |
| 2625 */ | |
| 2626 PIX * | |
| 2627 pixConvert8To16(PIX *pixs, | |
| 2628 l_int32 leftshift) | |
| 2629 { | |
| 2630 l_int32 i, j, w, h, d, wplt, wpld, val; | |
| 2631 l_uint32 *datat, *datad, *linet, *lined; | |
| 2632 PIX *pixt, *pixd; | |
| 2633 | |
| 2634 if (!pixs) | |
| 2635 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2636 pixGetDimensions(pixs, &w, &h, &d); | |
| 2637 if (d != 8) | |
| 2638 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 2639 if (leftshift < 0 || leftshift > 8) | |
| 2640 return (PIX *)ERROR_PTR("leftshift not in [0 ... 8]", __func__, NULL); | |
| 2641 | |
| 2642 if (pixGetColormap(pixs) != NULL) | |
| 2643 pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 2644 else | |
| 2645 pixt = pixClone(pixs); | |
| 2646 | |
| 2647 pixd = pixCreate(w, h, 16); | |
| 2648 pixCopyResolution(pixd, pixs); | |
| 2649 pixCopyInputFormat(pixd, pixs); | |
| 2650 datat = pixGetData(pixt); | |
| 2651 datad = pixGetData(pixd); | |
| 2652 wplt = pixGetWpl(pixt); | |
| 2653 wpld = pixGetWpl(pixd); | |
| 2654 for (i = 0; i < h; i++) { | |
| 2655 linet = datat + i * wplt; | |
| 2656 lined = datad + i * wpld; | |
| 2657 for (j = 0; j < w; j++) { | |
| 2658 val = GET_DATA_BYTE(linet, j); | |
| 2659 if (leftshift == 8) | |
| 2660 val = val | (val << leftshift); | |
| 2661 else | |
| 2662 val <<= leftshift; | |
| 2663 SET_DATA_TWO_BYTES(lined, j, val); | |
| 2664 } | |
| 2665 } | |
| 2666 | |
| 2667 pixDestroy(&pixt); | |
| 2668 return pixd; | |
| 2669 } | |
| 2670 | |
| 2671 | |
| 2672 /*---------------------------------------------------------------------------* | |
| 2673 * Top-level conversion to 2 bpp * | |
| 2674 *---------------------------------------------------------------------------*/ | |
| 2675 /*! | |
| 2676 * \brief pixConvertTo2() | |
| 2677 * | |
| 2678 * \param[in] pixs 1, 2, 4, 8, 24, 32 bpp; colormap OK but will be removed | |
| 2679 * \return pixd 2 bpp, or NULL on error | |
| 2680 * | |
| 2681 * <pre> | |
| 2682 * Notes: | |
| 2683 * (1) This is a top-level function, with simple default values | |
| 2684 * used in pixConvertTo8() if unpacking is necessary. | |
| 2685 * (2) Any existing colormap is removed; the result is always gray. | |
| 2686 * (3) If the input image has 2 bpp and no colormap, the operation is | |
| 2687 * lossless and a copy is returned. | |
| 2688 * </pre> | |
| 2689 */ | |
| 2690 PIX * | |
| 2691 pixConvertTo2(PIX *pixs) | |
| 2692 { | |
| 2693 l_int32 d; | |
| 2694 PIX *pix1, *pix2, *pix3, *pixd; | |
| 2695 | |
| 2696 if (!pixs) | |
| 2697 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2698 d = pixGetDepth(pixs); | |
| 2699 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 24 && d != 32) | |
| 2700 return (PIX *)ERROR_PTR("depth not {1,2,4,8,24,32}", __func__, NULL); | |
| 2701 | |
| 2702 if (pixGetColormap(pixs) != NULL) { | |
| 2703 pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 2704 d = pixGetDepth(pix1); | |
| 2705 } else { | |
| 2706 pix1 = pixCopy(NULL, pixs); | |
| 2707 } | |
| 2708 if (d == 24 || d == 32) | |
| 2709 pix2 = pixConvertTo8(pix1, FALSE); | |
| 2710 else | |
| 2711 pix2 = pixClone(pix1); | |
| 2712 pixDestroy(&pix1); | |
| 2713 if (d == 1) { | |
| 2714 pixd = pixConvert1To2(NULL, pix2, 3, 0); | |
| 2715 } else if (d == 2) { | |
| 2716 pixd = pixClone(pix2); | |
| 2717 } else if (d == 4) { | |
| 2718 pix3 = pixConvert4To8(pix2, FALSE); /* unpack to 8 */ | |
| 2719 pixd = pixConvert8To2(pix3); | |
| 2720 pixDestroy(&pix3); | |
| 2721 } else { /* d == 8 */ | |
| 2722 pixd = pixConvert8To2(pix2); | |
| 2723 } | |
| 2724 pixDestroy(&pix2); | |
| 2725 return pixd; | |
| 2726 } | |
| 2727 | |
| 2728 | |
| 2729 /*! | |
| 2730 * \brief pixConvert8To2() | |
| 2731 * | |
| 2732 * \param[in] pix 8 bpp; colormap OK | |
| 2733 * \return pixd 2 bpp, or NULL on error | |
| 2734 * | |
| 2735 * <pre> | |
| 2736 * Notes: | |
| 2737 * (1) Any existing colormap is removed to gray. | |
| 2738 * </pre> | |
| 2739 */ | |
| 2740 PIX * | |
| 2741 pixConvert8To2(PIX *pix) | |
| 2742 { | |
| 2743 l_int32 i, j, w, h, wpls, wpld; | |
| 2744 l_uint32 word; | |
| 2745 l_uint32 *datas, *lines, *datad, *lined; | |
| 2746 PIX *pixs, *pixd; | |
| 2747 | |
| 2748 if (!pix || pixGetDepth(pix) != 8) | |
| 2749 return (PIX *)ERROR_PTR("pix undefined or not 8 bpp", __func__, NULL); | |
| 2750 | |
| 2751 if (pixGetColormap(pix) != NULL) | |
| 2752 pixs = pixRemoveColormap(pix, REMOVE_CMAP_TO_GRAYSCALE); | |
| 2753 else | |
| 2754 pixs = pixClone(pix); | |
| 2755 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2756 datas = pixGetData(pixs); | |
| 2757 wpls = pixGetWpl(pixs); | |
| 2758 pixd = pixCreate(w, h, 2); | |
| 2759 datad = pixGetData(pixd); | |
| 2760 wpld = pixGetWpl(pixd); | |
| 2761 for (i = 0; i < h; i++) { | |
| 2762 lines = datas + i * wpls; | |
| 2763 lined = datad + i * wpld; | |
| 2764 for (j = 0; j < wpls; j++) { /* march through 4 pixels at a time */ | |
| 2765 word = lines[j] & 0xc0c0c0c0; /* top 2 bits of each byte */ | |
| 2766 word = (word >> 24) | ((word & 0xff0000) >> 18) | | |
| 2767 ((word & 0xff00) >> 12) | ((word & 0xff) >> 6); | |
| 2768 SET_DATA_BYTE(lined, j, word); /* only LS byte is filled */ | |
| 2769 } | |
| 2770 } | |
| 2771 pixDestroy(&pixs); | |
| 2772 return pixd; | |
| 2773 } | |
| 2774 | |
| 2775 | |
| 2776 /*---------------------------------------------------------------------------* | |
| 2777 * Top-level conversion to 4 bpp * | |
| 2778 *---------------------------------------------------------------------------*/ | |
| 2779 /*! | |
| 2780 * \brief pixConvertTo4() | |
| 2781 * | |
| 2782 * \param[in] pixs 1, 2, 4, 8, 24, 32 bpp; colormap OK but will be removed | |
| 2783 * \return pixd 4 bpp, or NULL on error | |
| 2784 * | |
| 2785 * <pre> | |
| 2786 * Notes: | |
| 2787 * (1) This is a top-level function, with simple default values | |
| 2788 * used in pixConvertTo8() if unpacking is necessary. | |
| 2789 * (2) Any existing colormap is removed; the result is always gray. | |
| 2790 * (3) If the input image has 4 bpp and no colormap, the operation is | |
| 2791 * lossless and a copy is returned. | |
| 2792 * </pre> | |
| 2793 */ | |
| 2794 PIX * | |
| 2795 pixConvertTo4(PIX *pixs) | |
| 2796 { | |
| 2797 l_int32 d; | |
| 2798 PIX *pix1, *pix2, *pix3, *pixd; | |
| 2799 | |
| 2800 if (!pixs) | |
| 2801 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2802 d = pixGetDepth(pixs); | |
| 2803 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 24 && d != 32) | |
| 2804 return (PIX *)ERROR_PTR("depth not {1,2,4,8,24,32}", __func__, NULL); | |
| 2805 | |
| 2806 if (pixGetColormap(pixs) != NULL) { | |
| 2807 pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 2808 d = pixGetDepth(pix1); | |
| 2809 } else { | |
| 2810 pix1 = pixCopy(NULL, pixs); | |
| 2811 } | |
| 2812 if (d == 24 || d == 32) | |
| 2813 pix2 = pixConvertTo8(pix1, FALSE); | |
| 2814 else | |
| 2815 pix2 = pixClone(pix1); | |
| 2816 pixDestroy(&pix1); | |
| 2817 if (d == 1) { | |
| 2818 pixd = pixConvert1To4(NULL, pix2, 15, 0); | |
| 2819 } else if (d == 2) { | |
| 2820 pix3 = pixConvert2To8(pix2, 0, 0x55, 0xaa, 0xff, FALSE); | |
| 2821 pixd = pixConvert8To4(pix3); | |
| 2822 pixDestroy(&pix3); | |
| 2823 } else if (d == 4) { | |
| 2824 pixd = pixClone(pix2); | |
| 2825 } else { /* d == 8 */ | |
| 2826 pixd = pixConvert8To4(pix2); | |
| 2827 } | |
| 2828 pixDestroy(&pix2); | |
| 2829 return pixd; | |
| 2830 } | |
| 2831 | |
| 2832 | |
| 2833 /*! | |
| 2834 * \brief pixConvert8To4() | |
| 2835 * | |
| 2836 * \param[in] pix 8 bpp; colormap OK | |
| 2837 * \return pixd 4 bpp, or NULL on error | |
| 2838 * | |
| 2839 * <pre> | |
| 2840 * Notes: | |
| 2841 * (1) Any existing colormap is removed to gray. | |
| 2842 * </pre> | |
| 2843 */ | |
| 2844 PIX * | |
| 2845 pixConvert8To4(PIX *pix) | |
| 2846 { | |
| 2847 l_int32 i, j, w, h, wpls, wpld, val; | |
| 2848 l_uint32 *datas, *lines, *datad, *lined; | |
| 2849 PIX *pixs, *pixd; | |
| 2850 | |
| 2851 if (!pix || pixGetDepth(pix) != 8) | |
| 2852 return (PIX *)ERROR_PTR("pix undefined or not 8 bpp", __func__, NULL); | |
| 2853 | |
| 2854 if (pixGetColormap(pix) != NULL) | |
| 2855 pixs = pixRemoveColormap(pix, REMOVE_CMAP_TO_GRAYSCALE); | |
| 2856 else | |
| 2857 pixs = pixClone(pix); | |
| 2858 pixGetDimensions(pixs, &w, &h, NULL); | |
| 2859 datas = pixGetData(pixs); | |
| 2860 wpls = pixGetWpl(pixs); | |
| 2861 pixd = pixCreate(w, h, 4); | |
| 2862 datad = pixGetData(pixd); | |
| 2863 wpld = pixGetWpl(pixd); | |
| 2864 for (i = 0; i < h; i++) { | |
| 2865 lines = datas + i * wpls; | |
| 2866 lined = datad + i * wpld; | |
| 2867 for (j = 0; j < w; j++) { | |
| 2868 val = GET_DATA_BYTE(lines, j); | |
| 2869 val = val >> 4; /* take top 4 bits */ | |
| 2870 SET_DATA_QBIT(lined, j, val); | |
| 2871 } | |
| 2872 } | |
| 2873 pixDestroy(&pixs); | |
| 2874 return pixd; | |
| 2875 } | |
| 2876 | |
| 2877 | |
| 2878 /*---------------------------------------------------------------------------* | |
| 2879 * Top-level conversion to 1 bpp * | |
| 2880 *---------------------------------------------------------------------------*/ | |
| 2881 /*! | |
| 2882 * \brief pixConvertTo1Adaptive() | |
| 2883 * | |
| 2884 * \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp | |
| 2885 * \return pixd 1 bpp, or NULL on error | |
| 2886 * | |
| 2887 * <pre> | |
| 2888 * Notes: | |
| 2889 * (1) This is a top-level function, that uses default values for | |
| 2890 * adaptive thresholding, if necessary. Otherwise, it is the same as | |
| 2891 * pixConvertTo1(), which uses a global threshold for binarization. | |
| 2892 * (2) Other high-level adaptive thresholding functions are | |
| 2893 * pixAdaptThresholdToBinary() and pixCleanImage(). | |
| 2894 * </pre> | |
| 2895 */ | |
| 2896 PIX * | |
| 2897 pixConvertTo1Adaptive(PIX *pixs) | |
| 2898 { | |
| 2899 l_int32 d, color0, color1, rval, gval, bval; | |
| 2900 PIX *pix1, *pix2, *pixd; | |
| 2901 PIXCMAP *cmap; | |
| 2902 | |
| 2903 if (!pixs) | |
| 2904 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2905 d = pixGetDepth(pixs); | |
| 2906 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32) | |
| 2907 return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,24,32}", __func__, NULL); | |
| 2908 | |
| 2909 cmap = pixGetColormap(pixs); | |
| 2910 if (d == 1) { | |
| 2911 if (!cmap) { | |
| 2912 return pixCopy(NULL, pixs); | |
| 2913 } else { /* strip the colormap off, and invert if reasonable | |
| 2914 for standard binary photometry. */ | |
| 2915 pixcmapGetColor(cmap, 0, &rval, &gval, &bval); | |
| 2916 color0 = rval + gval + bval; | |
| 2917 pixcmapGetColor(cmap, 1, &rval, &gval, &bval); | |
| 2918 color1 = rval + gval + bval; | |
| 2919 pixd = pixCopy(NULL, pixs); | |
| 2920 pixDestroyColormap(pixd); | |
| 2921 if (color1 > color0) | |
| 2922 pixInvert(pixd, pixd); | |
| 2923 return pixd; | |
| 2924 } | |
| 2925 } | |
| 2926 | |
| 2927 /* For all other depths, use 8 bpp as an intermediary */ | |
| 2928 pix1 = pixConvertTo8(pixs, FALSE); | |
| 2929 pix2 = pixBackgroundNormSimple(pix1, NULL, NULL); | |
| 2930 pixd = pixThresholdToBinary(pix2, 180); | |
| 2931 pixDestroy(&pix1); | |
| 2932 pixDestroy(&pix2); | |
| 2933 return pixd; | |
| 2934 } | |
| 2935 | |
| 2936 | |
| 2937 /*! | |
| 2938 * \brief pixConvertTo1() | |
| 2939 * | |
| 2940 * \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp | |
| 2941 * \param[in] threshold for final binarization, relative to 8 bpp | |
| 2942 * \return pixd 1 bpp, or NULL on error | |
| 2943 * | |
| 2944 * <pre> | |
| 2945 * Notes: | |
| 2946 * (1) This is a top-level function, with simple default values | |
| 2947 * used in pixConvertTo8() if unpacking is necessary. | |
| 2948 * (2) Any existing colormap is removed. | |
| 2949 * (3) If the input image has 1 bpp and no colormap, the operation is | |
| 2950 * lossless and a copy is returned. | |
| 2951 * </pre> | |
| 2952 */ | |
| 2953 PIX * | |
| 2954 pixConvertTo1(PIX *pixs, | |
| 2955 l_int32 threshold) | |
| 2956 { | |
| 2957 l_int32 d, color0, color1, rval, gval, bval; | |
| 2958 PIX *pixg, *pixd; | |
| 2959 PIXCMAP *cmap; | |
| 2960 | |
| 2961 if (!pixs) | |
| 2962 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2963 d = pixGetDepth(pixs); | |
| 2964 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32) | |
| 2965 return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,24,32}", __func__, NULL); | |
| 2966 | |
| 2967 cmap = pixGetColormap(pixs); | |
| 2968 if (d == 1) { | |
| 2969 if (!cmap) { | |
| 2970 return pixCopy(NULL, pixs); | |
| 2971 } else { /* strip the colormap off, and invert if reasonable | |
| 2972 for standard binary photometry. */ | |
| 2973 pixcmapGetColor(cmap, 0, &rval, &gval, &bval); | |
| 2974 color0 = rval + gval + bval; | |
| 2975 pixcmapGetColor(cmap, 1, &rval, &gval, &bval); | |
| 2976 color1 = rval + gval + bval; | |
| 2977 pixd = pixCopy(NULL, pixs); | |
| 2978 pixDestroyColormap(pixd); | |
| 2979 if (color1 > color0) | |
| 2980 pixInvert(pixd, pixd); | |
| 2981 return pixd; | |
| 2982 } | |
| 2983 } | |
| 2984 | |
| 2985 /* For all other depths, use 8 bpp as an intermediary */ | |
| 2986 pixg = pixConvertTo8(pixs, FALSE); | |
| 2987 pixd = pixThresholdToBinary(pixg, threshold); | |
| 2988 pixDestroy(&pixg); | |
| 2989 return pixd; | |
| 2990 } | |
| 2991 | |
| 2992 | |
| 2993 /*! | |
| 2994 * \brief pixConvertTo1BySampling() | |
| 2995 * | |
| 2996 * \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp | |
| 2997 * \param[in] factor subsampling factor; integer >= 1 | |
| 2998 * \param[in] threshold for final binarization, relative to 8 bpp | |
| 2999 * \return pixd 1 bpp, or NULL on error | |
| 3000 * | |
| 3001 * <pre> | |
| 3002 * Notes: | |
| 3003 * (1) This is a quick and dirty, top-level converter. | |
| 3004 * (2) See pixConvertTo1() for default values. | |
| 3005 * </pre> | |
| 3006 */ | |
| 3007 PIX * | |
| 3008 pixConvertTo1BySampling(PIX *pixs, | |
| 3009 l_int32 factor, | |
| 3010 l_int32 threshold) | |
| 3011 { | |
| 3012 l_float32 scalefactor; | |
| 3013 PIX *pixt, *pixd; | |
| 3014 | |
| 3015 if (!pixs) | |
| 3016 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3017 if (factor < 1) | |
| 3018 return (PIX *)ERROR_PTR("factor must be >= 1", __func__, NULL); | |
| 3019 | |
| 3020 scalefactor = 1.f / (l_float32)factor; | |
| 3021 pixt = pixScaleBySampling(pixs, scalefactor, scalefactor); | |
| 3022 pixd = pixConvertTo1(pixt, threshold); | |
| 3023 pixDestroy(&pixt); | |
| 3024 return pixd; | |
| 3025 } | |
| 3026 | |
| 3027 | |
| 3028 /*---------------------------------------------------------------------------* | |
| 3029 * Top-level conversion to 8 bpp * | |
| 3030 *---------------------------------------------------------------------------*/ | |
| 3031 /*! | |
| 3032 * \brief pixConvertTo8() | |
| 3033 * | |
| 3034 * \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp | |
| 3035 * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise | |
| 3036 * \return pixd 8 bpp, or NULL on error | |
| 3037 * | |
| 3038 * <pre> | |
| 3039 * Notes: | |
| 3040 * (1) This is a top-level function, with simple default values | |
| 3041 * for unpacking. | |
| 3042 * (2) The result, pixd, is made with a colormap if specified. | |
| 3043 * It is always a new image -- never a clone. For example, | |
| 3044 * if d == 8, and cmapflag matches the existence of a cmap | |
| 3045 * in pixs, the operation is lossless and it returns a copy. | |
| 3046 * (3) The default values used are: | |
| 3047 * ~ 1 bpp: val0 = 255, val1 = 0 | |
| 3048 * ~ 2 bpp: 4 bpp: even increments over dynamic range | |
| 3049 * ~ 8 bpp: lossless if cmap matches cmapflag | |
| 3050 * ~ 16 bpp: use most significant byte | |
| 3051 * (4) If 24 bpp or 32 bpp RGB, this is converted to gray. | |
| 3052 * For color quantization, you must specify the type explicitly, | |
| 3053 * using the color quantization code. | |
| 3054 * </pre> | |
| 3055 */ | |
| 3056 PIX * | |
| 3057 pixConvertTo8(PIX *pixs, | |
| 3058 l_int32 cmapflag) | |
| 3059 { | |
| 3060 l_int32 d; | |
| 3061 PIX *pix1, *pixd; | |
| 3062 PIXCMAP *cmap; | |
| 3063 | |
| 3064 if (!pixs) | |
| 3065 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3066 d = pixGetDepth(pixs); | |
| 3067 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32) | |
| 3068 return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,24,32}", __func__, NULL); | |
| 3069 | |
| 3070 if (d == 1) { | |
| 3071 if (cmapflag) | |
| 3072 return pixConvert1To8Cmap(pixs); | |
| 3073 else | |
| 3074 return pixConvert1To8(NULL, pixs, 255, 0); | |
| 3075 } else if (d == 2) { | |
| 3076 return pixConvert2To8(pixs, 0, 85, 170, 255, cmapflag); | |
| 3077 } else if (d == 4) { | |
| 3078 return pixConvert4To8(pixs, cmapflag); | |
| 3079 } else if (d == 8) { | |
| 3080 cmap = pixGetColormap(pixs); | |
| 3081 if ((cmap && cmapflag) || (!cmap && !cmapflag)) { | |
| 3082 return pixCopy(NULL, pixs); | |
| 3083 } else if (cmap) { /* !cmapflag */ | |
| 3084 return pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 3085 } else { /* !cmap && cmapflag; add colormap to pixd */ | |
| 3086 pixd = pixCopy(NULL, pixs); | |
| 3087 pixAddGrayColormap8(pixd); | |
| 3088 return pixd; | |
| 3089 } | |
| 3090 } else if (d == 16) { | |
| 3091 pixd = pixConvert16To8(pixs, L_MS_BYTE); | |
| 3092 if (cmapflag) | |
| 3093 pixAddGrayColormap8(pixd); | |
| 3094 return pixd; | |
| 3095 } else if (d == 24) { | |
| 3096 pix1 = pixConvert24To32(pixs); | |
| 3097 pixd = pixConvertRGBToLuminance(pix1); | |
| 3098 if (cmapflag) | |
| 3099 pixAddGrayColormap8(pixd); | |
| 3100 pixDestroy(&pix1); | |
| 3101 return pixd; | |
| 3102 } else { /* d == 32 */ | |
| 3103 pixd = pixConvertRGBToLuminance(pixs); | |
| 3104 if (cmapflag) | |
| 3105 pixAddGrayColormap8(pixd); | |
| 3106 return pixd; | |
| 3107 } | |
| 3108 } | |
| 3109 | |
| 3110 | |
| 3111 /*! | |
| 3112 * \brief pixConvertTo8BySampling() | |
| 3113 * | |
| 3114 * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp | |
| 3115 * \param[in] factor submsampling factor; integer >= 1 | |
| 3116 * \param[in] cmapflag TRUE if pixd is to have a colormap; FALSE otherwise | |
| 3117 * \return pixd 8 bpp, or NULL on error | |
| 3118 * | |
| 3119 * <pre> | |
| 3120 * Notes: | |
| 3121 * (1) This is a fast, quick/dirty, top-level converter. | |
| 3122 * (2) See pixConvertTo8() for default values. | |
| 3123 * </pre> | |
| 3124 */ | |
| 3125 PIX * | |
| 3126 pixConvertTo8BySampling(PIX *pixs, | |
| 3127 l_int32 factor, | |
| 3128 l_int32 cmapflag) | |
| 3129 { | |
| 3130 l_float32 scalefactor; | |
| 3131 PIX *pixt, *pixd; | |
| 3132 | |
| 3133 if (!pixs) | |
| 3134 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3135 if (factor < 1) | |
| 3136 return (PIX *)ERROR_PTR("factor must be >= 1", __func__, NULL); | |
| 3137 | |
| 3138 scalefactor = 1.f / (l_float32)factor; | |
| 3139 pixt = pixScaleBySampling(pixs, scalefactor, scalefactor); | |
| 3140 pixd = pixConvertTo8(pixt, cmapflag); | |
| 3141 | |
| 3142 pixDestroy(&pixt); | |
| 3143 return pixd; | |
| 3144 } | |
| 3145 | |
| 3146 | |
| 3147 /*! | |
| 3148 * \brief pixConvertTo8Colormap() | |
| 3149 * | |
| 3150 * \param[in] pixs 1, 2, 4, 8, 16 or 32 bpp | |
| 3151 * \param[in] dither 1 to dither if necessary; 0 otherwise | |
| 3152 * \return pixd 8 bpp, cmapped, or NULL on error | |
| 3153 * | |
| 3154 * <pre> | |
| 3155 * Notes: | |
| 3156 * (1) This is a top-level function, with simple default values | |
| 3157 * for unpacking. | |
| 3158 * (2) The result, pixd, is always made with a colormap. | |
| 3159 * (3) If d == 8, the operation is lossless and it returns a copy. | |
| 3160 * (4) The default values used for increasing depth are: | |
| 3161 * ~ 1 bpp: val0 = 255, val1 = 0 | |
| 3162 * ~ 2 bpp: 4 bpp: even increments over dynamic range | |
| 3163 * (5) For 16 bpp, use the most significant byte. | |
| 3164 * (6) For 32 bpp RGB, use octcube quantization with optional dithering. | |
| 3165 * </pre> | |
| 3166 */ | |
| 3167 PIX * | |
| 3168 pixConvertTo8Colormap(PIX *pixs, | |
| 3169 l_int32 dither) | |
| 3170 { | |
| 3171 l_int32 d; | |
| 3172 | |
| 3173 if (!pixs) | |
| 3174 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3175 d = pixGetDepth(pixs); | |
| 3176 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) | |
| 3177 return (PIX *)ERROR_PTR("depth not {1,2,4,8,16,32}", __func__, NULL); | |
| 3178 | |
| 3179 if (d != 32) | |
| 3180 return pixConvertTo8(pixs, 1); | |
| 3181 | |
| 3182 return pixConvertRGBToColormap(pixs, dither); | |
| 3183 } | |
| 3184 | |
| 3185 | |
| 3186 /*---------------------------------------------------------------------------* | |
| 3187 * Top-level conversion to 16 bpp * | |
| 3188 *---------------------------------------------------------------------------*/ | |
| 3189 /*! | |
| 3190 * \brief pixConvertTo16() | |
| 3191 * | |
| 3192 * \param[in] pixs 1, 8 bpp | |
| 3193 * \return pixd 16 bpp, or NULL on error | |
| 3194 * | |
| 3195 * Usage: Top-level function, with simple default values for unpacking. | |
| 3196 * 1 bpp: val0 = 0xffff, val1 = 0 | |
| 3197 * 8 bpp: replicates the 8 bit value in both the MSB and LSB | |
| 3198 * of the 16 bit pixel. | |
| 3199 */ | |
| 3200 PIX * | |
| 3201 pixConvertTo16(PIX *pixs) | |
| 3202 { | |
| 3203 l_int32 d; | |
| 3204 | |
| 3205 if (!pixs) | |
| 3206 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3207 | |
| 3208 d = pixGetDepth(pixs); | |
| 3209 if (d == 1) | |
| 3210 return pixConvert1To16(NULL, pixs, 0xffff, 0); | |
| 3211 else if (d == 8) | |
| 3212 return pixConvert8To16(pixs, 8); | |
| 3213 else | |
| 3214 return (PIX *)ERROR_PTR("src depth not 1 or 8 bpp", __func__, NULL); | |
| 3215 } | |
| 3216 | |
| 3217 | |
| 3218 | |
| 3219 /*---------------------------------------------------------------------------* | |
| 3220 * Top-level conversion to 32 bpp * | |
| 3221 *---------------------------------------------------------------------------*/ | |
| 3222 /*! | |
| 3223 * \brief pixConvertTo32() | |
| 3224 * | |
| 3225 * \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp | |
| 3226 * \return pixd 32 bpp, or NULL on error | |
| 3227 * | |
| 3228 * Usage: Top-level function, with simple default values for unpacking. | |
| 3229 * 1 bpp: val0 = 255, val1 = 0 | |
| 3230 * and then replication into R, G and B components | |
| 3231 * 2 bpp: if colormapped, use the colormap values; otherwise, | |
| 3232 * use val0 = 0, val1 = 0x55, val2 = 0xaa, val3 = 255 | |
| 3233 * and replicate gray into R, G and B components | |
| 3234 * 4 bpp: if colormapped, use the colormap values; otherwise, | |
| 3235 * replicate 2 nybs into a byte, and then into R,G,B components | |
| 3236 * 8 bpp: if colormapped, use the colormap values; otherwise, | |
| 3237 * replicate gray values into R, G and B components | |
| 3238 * 16 bpp: replicate MSB into R, G and B components | |
| 3239 * 24 bpp: unpack the pixels, maintaining word alignment on each scanline | |
| 3240 * 32 bpp: makes a copy | |
| 3241 * | |
| 3242 * <pre> | |
| 3243 * Notes: | |
| 3244 * (1) Never returns a clone of pixs. | |
| 3245 * </pre> | |
| 3246 */ | |
| 3247 PIX * | |
| 3248 pixConvertTo32(PIX *pixs) | |
| 3249 { | |
| 3250 l_int32 d; | |
| 3251 PIX *pix1, *pixd; | |
| 3252 | |
| 3253 if (!pixs) | |
| 3254 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3255 | |
| 3256 d = pixGetDepth(pixs); | |
| 3257 if (d == 1) { | |
| 3258 return pixConvert1To32(NULL, pixs, 0xffffffff, 0); | |
| 3259 } else if (d == 2) { | |
| 3260 pix1 = pixConvert2To8(pixs, 0, 85, 170, 255, TRUE); | |
| 3261 pixd = pixConvert8To32(pix1); | |
| 3262 pixDestroy(&pix1); | |
| 3263 return pixd; | |
| 3264 } else if (d == 4) { | |
| 3265 pix1 = pixConvert4To8(pixs, TRUE); | |
| 3266 pixd = pixConvert8To32(pix1); | |
| 3267 pixDestroy(&pix1); | |
| 3268 return pixd; | |
| 3269 } else if (d == 8) { | |
| 3270 return pixConvert8To32(pixs); | |
| 3271 } else if (d == 16) { | |
| 3272 pix1 = pixConvert16To8(pixs, L_MS_BYTE); | |
| 3273 pixd = pixConvert8To32(pix1); | |
| 3274 pixDestroy(&pix1); | |
| 3275 return pixd; | |
| 3276 } else if (d == 24) { | |
| 3277 return pixConvert24To32(pixs); | |
| 3278 } else if (d == 32) { | |
| 3279 return pixCopy(NULL, pixs); | |
| 3280 } else { | |
| 3281 return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8, 16, 32 bpp", | |
| 3282 __func__, NULL); | |
| 3283 } | |
| 3284 } | |
| 3285 | |
| 3286 | |
| 3287 /*! | |
| 3288 * \brief pixConvertTo32BySampling() | |
| 3289 * | |
| 3290 * \param[in] pixs 1, 2, 4, 8, 16, 24 or 32 bpp | |
| 3291 * \param[in] factor submsampling factor; integer >= 1 | |
| 3292 * \return pixd 32 bpp, or NULL on error | |
| 3293 * | |
| 3294 * <pre> | |
| 3295 * Notes: | |
| 3296 * (1) This is a fast, quick/dirty, top-level converter. | |
| 3297 * (2) See pixConvertTo32() for default values. | |
| 3298 * </pre> | |
| 3299 */ | |
| 3300 PIX * | |
| 3301 pixConvertTo32BySampling(PIX *pixs, | |
| 3302 l_int32 factor) | |
| 3303 { | |
| 3304 l_float32 scalefactor; | |
| 3305 PIX *pix1, *pixd; | |
| 3306 | |
| 3307 if (!pixs) | |
| 3308 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3309 if (factor < 1) | |
| 3310 return (PIX *)ERROR_PTR("factor must be >= 1", __func__, NULL); | |
| 3311 | |
| 3312 scalefactor = 1.f / (l_float32)factor; | |
| 3313 pix1 = pixScaleBySampling(pixs, scalefactor, scalefactor); | |
| 3314 pixd = pixConvertTo32(pix1); | |
| 3315 | |
| 3316 pixDestroy(&pix1); | |
| 3317 return pixd; | |
| 3318 } | |
| 3319 | |
| 3320 | |
| 3321 /*! | |
| 3322 * \brief pixConvert8To32() | |
| 3323 * | |
| 3324 * \param[in] pixs 8 bpp | |
| 3325 * \return 32 bpp rgb pix, or NULL on error | |
| 3326 * | |
| 3327 * <pre> | |
| 3328 * Notes: | |
| 3329 * (1) If there is no colormap, replicates the gray value | |
| 3330 * into the 3 MSB of the dest pixel. | |
| 3331 * </pre> | |
| 3332 */ | |
| 3333 PIX * | |
| 3334 pixConvert8To32(PIX *pixs) | |
| 3335 { | |
| 3336 l_int32 i, j, w, h, wpls, wpld, val; | |
| 3337 l_uint32 *datas, *datad, *lines, *lined; | |
| 3338 l_uint32 *tab; | |
| 3339 PIX *pixd; | |
| 3340 | |
| 3341 if (!pixs) | |
| 3342 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3343 if (pixGetDepth(pixs) != 8) | |
| 3344 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 3345 | |
| 3346 if (pixGetColormap(pixs)) | |
| 3347 return pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); | |
| 3348 | |
| 3349 pixGetDimensions(pixs, &w, &h, NULL); | |
| 3350 datas = pixGetData(pixs); | |
| 3351 wpls = pixGetWpl(pixs); | |
| 3352 if ((pixd = pixCreate(w, h, 32)) == NULL) | |
| 3353 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 3354 pixCopyResolution(pixd, pixs); | |
| 3355 pixCopyInputFormat(pixd, pixs); | |
| 3356 datad = pixGetData(pixd); | |
| 3357 wpld = pixGetWpl(pixd); | |
| 3358 | |
| 3359 /* Replication table gray --> rgb */ | |
| 3360 tab = (l_uint32 *)LEPT_CALLOC(256, sizeof(l_uint32)); | |
| 3361 for (i = 0; i < 256; i++) | |
| 3362 tab[i] = ((l_uint32)i << 24) | (i << 16) | (i << 8); | |
| 3363 | |
| 3364 /* Replicate 1 --> 4 bytes (alpha byte not set) */ | |
| 3365 for (i = 0; i < h; i++) { | |
| 3366 lines = datas + i * wpls; | |
| 3367 lined = datad + i * wpld; | |
| 3368 for (j = 0; j < w; j++) { | |
| 3369 val = GET_DATA_BYTE(lines, j); | |
| 3370 lined[j] = tab[val]; | |
| 3371 } | |
| 3372 } | |
| 3373 | |
| 3374 LEPT_FREE(tab); | |
| 3375 return pixd; | |
| 3376 } | |
| 3377 | |
| 3378 | |
| 3379 /*---------------------------------------------------------------------------* | |
| 3380 * Top-level conversion to 8 or 32 bpp, without colormap * | |
| 3381 *---------------------------------------------------------------------------*/ | |
| 3382 /*! | |
| 3383 * \brief pixConvertTo8Or32() | |
| 3384 * | |
| 3385 * \param[in] pixs 1, 2, 4, 8, 16, with or without colormap; | |
| 3386 * or 32 bpp rgb | |
| 3387 * \param[in] copyflag L_CLONE or L_COPY | |
| 3388 * \param[in] warnflag 1 to issue warning if colormap is removed; else 0 | |
| 3389 * \return pixd 8 bpp grayscale or 32 bpp rgb, or NULL on error | |
| 3390 * | |
| 3391 * <pre> | |
| 3392 * Notes: | |
| 3393 * (1) If there is a colormap, the colormap is removed to 8 or 32 bpp, | |
| 3394 * depending on whether the colors in the colormap are all gray. | |
| 3395 * (2) If the input is either rgb or 8 bpp without a colormap, | |
| 3396 * this returns either a clone or a copy, depending on %copyflag. | |
| 3397 * (3) Otherwise, the pix is converted to 8 bpp grayscale. | |
| 3398 * In all cases, pixd does not have a colormap. | |
| 3399 * </pre> | |
| 3400 */ | |
| 3401 PIX * | |
| 3402 pixConvertTo8Or32(PIX *pixs, | |
| 3403 l_int32 copyflag, | |
| 3404 l_int32 warnflag) | |
| 3405 { | |
| 3406 l_int32 d; | |
| 3407 PIX *pixd; | |
| 3408 | |
| 3409 if (!pixs) | |
| 3410 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3411 if (copyflag != L_CLONE && copyflag != L_COPY) | |
| 3412 return (PIX *)ERROR_PTR("invalid copyflag", __func__, NULL); | |
| 3413 | |
| 3414 d = pixGetDepth(pixs); | |
| 3415 if (pixGetColormap(pixs)) { | |
| 3416 if (warnflag) L_WARNING("pix has colormap; removing\n", __func__); | |
| 3417 pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); | |
| 3418 } else if (d == 8 || d == 32) { | |
| 3419 if (copyflag == L_CLONE) | |
| 3420 pixd = pixClone(pixs); | |
| 3421 else /* copyflag == L_COPY */ | |
| 3422 pixd = pixCopy(NULL, pixs); | |
| 3423 } else { | |
| 3424 pixd = pixConvertTo8(pixs, 0); | |
| 3425 } | |
| 3426 | |
| 3427 /* Sanity check on result */ | |
| 3428 d = pixGetDepth(pixd); | |
| 3429 if (d != 8 && d != 32) { | |
| 3430 pixDestroy(&pixd); | |
| 3431 return (PIX *)ERROR_PTR("depth not 8 or 32 bpp", __func__, NULL); | |
| 3432 } | |
| 3433 | |
| 3434 return pixd; | |
| 3435 } | |
| 3436 | |
| 3437 | |
| 3438 /*---------------------------------------------------------------------------* | |
| 3439 * Conversion between 24 bpp and 32 bpp rgb * | |
| 3440 *---------------------------------------------------------------------------*/ | |
| 3441 /*! | |
| 3442 * \brief pixConvert24To32() | |
| 3443 * | |
| 3444 * \param[in] pixs 24 bpp rgb | |
| 3445 * \return pixd 32 bpp rgb, or NULL on error | |
| 3446 * | |
| 3447 * <pre> | |
| 3448 * Notes: | |
| 3449 * (1) 24 bpp rgb pix are not supported in leptonica, except for a small | |
| 3450 * number of formatted write operations. The data is a byte array, | |
| 3451 * with pixels in order r,g,b, and padded to 32 bit boundaries | |
| 3452 * in each line. | |
| 3453 * (2) Because 24 bpp rgb pix are conveniently generated by programs | |
| 3454 * such as xpdf (which has SplashBitmaps that store the raster | |
| 3455 * data in consecutive 24-bit rgb pixels), it is useful to provide | |
| 3456 * 24 bpp pix that simply incorporate that data. The only things | |
| 3457 * we can do with these are: | |
| 3458 * (a) write them to file in png, jpeg, tiff and pnm | |
| 3459 * (b) interconvert between 24 and 32 bpp in memory (for testing). | |
| 3460 * </pre> | |
| 3461 */ | |
| 3462 PIX * | |
| 3463 pixConvert24To32(PIX *pixs) | |
| 3464 { | |
| 3465 l_uint8 *lines; | |
| 3466 l_int32 w, h, d, i, j, wpls, wpld, rval, gval, bval; | |
| 3467 l_uint32 pixel; | |
| 3468 l_uint32 *datas, *datad, *lined; | |
| 3469 PIX *pixd; | |
| 3470 | |
| 3471 if (!pixs) | |
| 3472 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3473 pixGetDimensions(pixs, &w, &h, &d); | |
| 3474 if (d != 24) | |
| 3475 return (PIX *)ERROR_PTR("pixs not 24 bpp", __func__, NULL); | |
| 3476 | |
| 3477 pixd = pixCreate(w, h, 32); | |
| 3478 datas = pixGetData(pixs); | |
| 3479 datad = pixGetData(pixd); | |
| 3480 wpls = pixGetWpl(pixs); | |
| 3481 wpld = pixGetWpl(pixd); | |
| 3482 for (i = 0; i < h; i++) { | |
| 3483 lines = (l_uint8 *)(datas + i * wpls); | |
| 3484 lined = datad + i * wpld; | |
| 3485 for (j = 0; j < w; j++) { | |
| 3486 rval = *lines++; | |
| 3487 gval = *lines++; | |
| 3488 bval = *lines++; | |
| 3489 composeRGBPixel(rval, gval, bval, &pixel); | |
| 3490 lined[j] = pixel; | |
| 3491 } | |
| 3492 } | |
| 3493 pixCopyResolution(pixd, pixs); | |
| 3494 pixCopyInputFormat(pixd, pixs); | |
| 3495 return pixd; | |
| 3496 } | |
| 3497 | |
| 3498 | |
| 3499 /*! | |
| 3500 * \brief pixConvert32To24() | |
| 3501 * | |
| 3502 * \param[in] pixs 32 bpp rgb | |
| 3503 * \return pixd 24 bpp rgb, or NULL on error | |
| 3504 * | |
| 3505 * <pre> | |
| 3506 * Notes: | |
| 3507 * (1) See pixconvert24To32(). | |
| 3508 * </pre> | |
| 3509 */ | |
| 3510 PIX * | |
| 3511 pixConvert32To24(PIX *pixs) | |
| 3512 { | |
| 3513 l_uint8 *rgbdata8; | |
| 3514 l_int32 w, h, d, i, j, wpls, wpld, rval, gval, bval; | |
| 3515 l_uint32 *datas, *lines, *rgbdata; | |
| 3516 PIX *pixd; | |
| 3517 | |
| 3518 if (!pixs) | |
| 3519 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3520 pixGetDimensions(pixs, &w, &h, &d); | |
| 3521 if (d != 32) | |
| 3522 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); | |
| 3523 | |
| 3524 datas = pixGetData(pixs); | |
| 3525 wpls = pixGetWpl(pixs); | |
| 3526 pixd = pixCreate(w, h, 24); | |
| 3527 rgbdata = pixGetData(pixd); | |
| 3528 wpld = pixGetWpl(pixd); | |
| 3529 for (i = 0; i < h; i++) { | |
| 3530 lines = datas + i * wpls; | |
| 3531 rgbdata8 = (l_uint8 *)(rgbdata + i * wpld); | |
| 3532 for (j = 0; j < w; j++) { | |
| 3533 extractRGBValues(lines[j], &rval, &gval, &bval); | |
| 3534 *rgbdata8++ = rval; | |
| 3535 *rgbdata8++ = gval; | |
| 3536 *rgbdata8++ = bval; | |
| 3537 } | |
| 3538 } | |
| 3539 pixCopyResolution(pixd, pixs); | |
| 3540 pixCopyInputFormat(pixd, pixs); | |
| 3541 return pixd; | |
| 3542 } | |
| 3543 | |
| 3544 | |
| 3545 /*---------------------------------------------------------------------------* | |
| 3546 * Conversion between 32 bpp (1 spp) and 16 or 8 bpp * | |
| 3547 *---------------------------------------------------------------------------*/ | |
| 3548 /*! | |
| 3549 * \brief pixConvert32To16() | |
| 3550 * | |
| 3551 * \param[in] pixs 32 bpp, single component | |
| 3552 * \param[in] type L_LS_TWO_BYTES, L_MS_TWO_BYTES, L_CLIP_TO_FFFF | |
| 3553 * \return pixd 16 bpp , or NULL on error | |
| 3554 * | |
| 3555 * <pre> | |
| 3556 * Notes: | |
| 3557 * (1) The data in pixs is typically used for labelling. | |
| 3558 * It is an array of l_uint32 values, not rgb or rgba. | |
| 3559 * </pre> | |
| 3560 */ | |
| 3561 PIX * | |
| 3562 pixConvert32To16(PIX *pixs, | |
| 3563 l_int32 type) | |
| 3564 { | |
| 3565 l_uint16 dword; | |
| 3566 l_int32 w, h, i, j, wpls, wpld; | |
| 3567 l_uint32 sword; | |
| 3568 l_uint32 *datas, *lines, *datad, *lined; | |
| 3569 PIX *pixd; | |
| 3570 | |
| 3571 if (!pixs || pixGetDepth(pixs) != 32) | |
| 3572 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL); | |
| 3573 if (type != L_LS_TWO_BYTES && type != L_MS_TWO_BYTES && | |
| 3574 type != L_CLIP_TO_FFFF) | |
| 3575 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 3576 | |
| 3577 pixGetDimensions(pixs, &w, &h, NULL); | |
| 3578 if ((pixd = pixCreate(w, h, 16)) == NULL) | |
| 3579 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 3580 pixCopyResolution(pixd, pixs); | |
| 3581 pixCopyInputFormat(pixd, pixs); | |
| 3582 wpls = pixGetWpl(pixs); | |
| 3583 datas = pixGetData(pixs); | |
| 3584 wpld = pixGetWpl(pixd); | |
| 3585 datad = pixGetData(pixd); | |
| 3586 | |
| 3587 for (i = 0; i < h; i++) { | |
| 3588 lines = datas + i * wpls; | |
| 3589 lined = datad + i * wpld; | |
| 3590 if (type == L_LS_TWO_BYTES) { | |
| 3591 for (j = 0; j < wpls; j++) { | |
| 3592 sword = *(lines + j); | |
| 3593 dword = sword & 0xffff; | |
| 3594 SET_DATA_TWO_BYTES(lined, j, dword); | |
| 3595 } | |
| 3596 } else if (type == L_MS_TWO_BYTES) { | |
| 3597 for (j = 0; j < wpls; j++) { | |
| 3598 sword = *(lines + j); | |
| 3599 dword = sword >> 16; | |
| 3600 SET_DATA_TWO_BYTES(lined, j, dword); | |
| 3601 } | |
| 3602 } else { /* type == L_CLIP_TO_FFFF */ | |
| 3603 for (j = 0; j < wpls; j++) { | |
| 3604 sword = *(lines + j); | |
| 3605 dword = (sword >> 16) ? 0xffff : (sword & 0xffff); | |
| 3606 SET_DATA_TWO_BYTES(lined, j, dword); | |
| 3607 } | |
| 3608 } | |
| 3609 } | |
| 3610 | |
| 3611 return pixd; | |
| 3612 } | |
| 3613 | |
| 3614 | |
| 3615 /*! | |
| 3616 * \brief pixConvert32To8() | |
| 3617 * | |
| 3618 * \param[in] pixs 32 bpp, single component | |
| 3619 * \param[in] type16 L_LS_TWO_BYTES, L_MS_TWO_BYTES, L_CLIP_TO_FFFF | |
| 3620 * \param[in] type8 L_LS_BYTE, L_MS_BYTE, L_CLIP_TO_FF | |
| 3621 * \return pixd 8 bpp, or NULL on error | |
| 3622 */ | |
| 3623 PIX * | |
| 3624 pixConvert32To8(PIX *pixs, | |
| 3625 l_int32 type16, | |
| 3626 l_int32 type8) | |
| 3627 { | |
| 3628 PIX *pix1, *pixd; | |
| 3629 | |
| 3630 if (!pixs || pixGetDepth(pixs) != 32) | |
| 3631 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL); | |
| 3632 if (type16 != L_LS_TWO_BYTES && type16 != L_MS_TWO_BYTES && | |
| 3633 type16 != L_CLIP_TO_FFFF) | |
| 3634 return (PIX *)ERROR_PTR("invalid type16", __func__, NULL); | |
| 3635 if (type8 != L_LS_BYTE && type8 != L_MS_BYTE && type8 != L_CLIP_TO_FF) | |
| 3636 return (PIX *)ERROR_PTR("invalid type8", __func__, NULL); | |
| 3637 | |
| 3638 pix1 = pixConvert32To16(pixs, type16); | |
| 3639 pixd = pixConvert16To8(pix1, type8); | |
| 3640 pixDestroy(&pix1); | |
| 3641 return pixd; | |
| 3642 } | |
| 3643 | |
| 3644 | |
| 3645 /*---------------------------------------------------------------------------* | |
| 3646 * Removal of alpha component by blending with white background * | |
| 3647 *---------------------------------------------------------------------------*/ | |
| 3648 /*! | |
| 3649 * \brief pixRemoveAlpha() | |
| 3650 * | |
| 3651 * \param[in] pixs any depth | |
| 3652 * \return pixd if 32 bpp rgba, pixs blended over a white background; | |
| 3653 * a clone of pixs otherwise, and NULL on error | |
| 3654 * | |
| 3655 * <pre> | |
| 3656 * Notes: | |
| 3657 * (1) This is a wrapper on pixAlphaBlendUniform() | |
| 3658 * </pre> | |
| 3659 */ | |
| 3660 PIX * | |
| 3661 pixRemoveAlpha(PIX *pixs) | |
| 3662 { | |
| 3663 if (!pixs) | |
| 3664 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3665 | |
| 3666 if (pixGetDepth(pixs) == 32 && pixGetSpp(pixs) == 4) | |
| 3667 return pixAlphaBlendUniform(pixs, 0xffffff00); | |
| 3668 else | |
| 3669 return pixClone(pixs); | |
| 3670 } | |
| 3671 | |
| 3672 | |
| 3673 /*---------------------------------------------------------------------------* | |
| 3674 * Addition of alpha component to 1 bpp * | |
| 3675 *---------------------------------------------------------------------------*/ | |
| 3676 /*! | |
| 3677 * \brief pixAddAlphaTo1bpp() | |
| 3678 * | |
| 3679 * \param[in] pixd [optional] 1 bpp, can be null or equal to pixs | |
| 3680 * \param[in] pixs 1 bpp | |
| 3681 * \return pixd 1 bpp with colormap and non-opaque alpha, | |
| 3682 * or NULL on error | |
| 3683 * | |
| 3684 * <pre> | |
| 3685 * Notes: | |
| 3686 * (1) We don't use 1 bpp colormapped images with alpha in leptonica, | |
| 3687 * but we support generating them (here), writing to png, and reading | |
| 3688 * the png. On reading, they are converted to 32 bpp RGBA. | |
| 3689 * (2) The background (0) pixels in pixs become fully transparent, and the | |
| 3690 * foreground (1) pixels are fully opaque. Thus, pixd is a 1 bpp | |
| 3691 * representation of a stencil, that can be used to paint over pixels | |
| 3692 * of a backing image that are masked by the foreground in pixs. | |
| 3693 * </pre> | |
| 3694 */ | |
| 3695 PIX * | |
| 3696 pixAddAlphaTo1bpp(PIX *pixd, | |
| 3697 PIX *pixs) | |
| 3698 { | |
| 3699 PIXCMAP *cmap; | |
| 3700 | |
| 3701 if (!pixs || (pixGetDepth(pixs) != 1)) | |
| 3702 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL); | |
| 3703 if (pixd && (pixd != pixs)) | |
| 3704 return (PIX *)ERROR_PTR("pixd defined but != pixs", __func__, NULL); | |
| 3705 | |
| 3706 pixd = pixCopy(pixd, pixs); | |
| 3707 cmap = pixcmapCreate(1); | |
| 3708 pixSetColormap(pixd, cmap); | |
| 3709 pixcmapAddRGBA(cmap, 255, 255, 255, 0); /* 0 ==> white + transparent */ | |
| 3710 pixcmapAddRGBA(cmap, 0, 0, 0, 255); /* 1 ==> black + opaque */ | |
| 3711 return pixd; | |
| 3712 } | |
| 3713 | |
| 3714 | |
| 3715 /*---------------------------------------------------------------------------* | |
| 3716 * Lossless depth conversion (unpacking) * | |
| 3717 *---------------------------------------------------------------------------*/ | |
| 3718 /*! | |
| 3719 * \brief pixConvertLossless() | |
| 3720 * | |
| 3721 * \param[in] pixs 1, 2, 4, 8 bpp, not cmapped | |
| 3722 * \param[in] d destination depth: 2, 4 or 8 | |
| 3723 * \return pixd 2, 4 or 8 bpp, or NULL on error | |
| 3724 * | |
| 3725 * <pre> | |
| 3726 * Notes: | |
| 3727 * (1) This is a lossless unpacking (depth-increasing) | |
| 3728 * conversion. If ds is the depth of pixs, then | |
| 3729 * ~ if d < ds, returns NULL | |
| 3730 * ~ if d == ds, returns a copy | |
| 3731 * ~ if d > ds, does the unpacking conversion | |
| 3732 * (2) If pixs has a colormap, this is an error. | |
| 3733 * </pre> | |
| 3734 */ | |
| 3735 PIX * | |
| 3736 pixConvertLossless(PIX *pixs, | |
| 3737 l_int32 d) | |
| 3738 { | |
| 3739 l_int32 w, h, ds, wpls, wpld, i, j, val; | |
| 3740 l_uint32 *datas, *datad, *lines, *lined; | |
| 3741 PIX *pixd; | |
| 3742 | |
| 3743 if (!pixs) | |
| 3744 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3745 if (pixGetColormap(pixs)) | |
| 3746 return (PIX *)ERROR_PTR("pixs has colormap", __func__, NULL); | |
| 3747 if (d != 2 && d != 4 && d != 8) | |
| 3748 return (PIX *)ERROR_PTR("invalid dest depth", __func__, NULL); | |
| 3749 | |
| 3750 pixGetDimensions(pixs, &w, &h, &ds); | |
| 3751 if (d < ds) | |
| 3752 return (PIX *)ERROR_PTR("depth > d", __func__, NULL); | |
| 3753 else if (d == ds) | |
| 3754 return pixCopy(NULL, pixs); | |
| 3755 | |
| 3756 if ((pixd = pixCreate(w, h, d)) == NULL) | |
| 3757 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); | |
| 3758 pixCopyResolution(pixd, pixs); | |
| 3759 pixCopyInputFormat(pixd, pixs); | |
| 3760 | |
| 3761 /* Unpack the bits */ | |
| 3762 datas = pixGetData(pixs); | |
| 3763 wpls = pixGetWpl(pixs); | |
| 3764 datad = pixGetData(pixd); | |
| 3765 wpld = pixGetWpl(pixd); | |
| 3766 for (i = 0; i < h; i++) { | |
| 3767 lines = datas + i * wpls; | |
| 3768 lined = datad + i * wpld; | |
| 3769 switch (ds) | |
| 3770 { | |
| 3771 case 1: | |
| 3772 for (j = 0; j < w; j++) { | |
| 3773 val = GET_DATA_BIT(lines, j); | |
| 3774 if (d == 8) | |
| 3775 SET_DATA_BYTE(lined, j, val); | |
| 3776 else if (d == 4) | |
| 3777 SET_DATA_QBIT(lined, j, val); | |
| 3778 else /* d == 2 */ | |
| 3779 SET_DATA_DIBIT(lined, j, val); | |
| 3780 } | |
| 3781 break; | |
| 3782 case 2: | |
| 3783 for (j = 0; j < w; j++) { | |
| 3784 val = GET_DATA_DIBIT(lines, j); | |
| 3785 if (d == 8) | |
| 3786 SET_DATA_BYTE(lined, j, val); | |
| 3787 else /* d == 4 */ | |
| 3788 SET_DATA_QBIT(lined, j, val); | |
| 3789 } | |
| 3790 break; | |
| 3791 case 4: | |
| 3792 for (j = 0; j < w; j++) { | |
| 3793 val = GET_DATA_DIBIT(lines, j); | |
| 3794 SET_DATA_BYTE(lined, j, val); | |
| 3795 } | |
| 3796 break; | |
| 3797 } | |
| 3798 } | |
| 3799 | |
| 3800 return pixd; | |
| 3801 } | |
| 3802 | |
| 3803 | |
| 3804 /*---------------------------------------------------------------------------* | |
| 3805 * Conversion for printing in PostScript * | |
| 3806 *---------------------------------------------------------------------------*/ | |
| 3807 /*! | |
| 3808 * \brief pixConvertForPSWrap() | |
| 3809 * | |
| 3810 * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp | |
| 3811 * \return pixd 1, 8, or 32 bpp, or NULL on error | |
| 3812 * | |
| 3813 * <pre> | |
| 3814 * Notes: | |
| 3815 * (1) For wrapping in PostScript, we convert pixs to | |
| 3816 * 1 bpp, 8 bpp (gray) and 32 bpp (RGB color). | |
| 3817 * (2) Colormaps are removed. For pixs with colormaps, the | |
| 3818 * images are converted to either 8 bpp gray or 32 bpp | |
| 3819 * RGB, depending on whether the colormap has color content. | |
| 3820 * (3) Images without colormaps, that are not 1 bpp or 32 bpp, | |
| 3821 * are converted to 8 bpp gray. | |
| 3822 * </pre> | |
| 3823 */ | |
| 3824 PIX * | |
| 3825 pixConvertForPSWrap(PIX *pixs) | |
| 3826 { | |
| 3827 l_int32 d; | |
| 3828 PIX *pixd; | |
| 3829 PIXCMAP *cmap; | |
| 3830 | |
| 3831 if (!pixs) | |
| 3832 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3833 | |
| 3834 cmap = pixGetColormap(pixs); | |
| 3835 d = pixGetDepth(pixs); | |
| 3836 switch (d) | |
| 3837 { | |
| 3838 case 1: | |
| 3839 case 32: | |
| 3840 pixd = pixClone(pixs); | |
| 3841 break; | |
| 3842 case 2: | |
| 3843 if (cmap) | |
| 3844 pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); | |
| 3845 else | |
| 3846 pixd = pixConvert2To8(pixs, 0, 0x55, 0xaa, 0xff, FALSE); | |
| 3847 break; | |
| 3848 case 4: | |
| 3849 if (cmap) | |
| 3850 pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); | |
| 3851 else | |
| 3852 pixd = pixConvert4To8(pixs, FALSE); | |
| 3853 break; | |
| 3854 case 8: | |
| 3855 pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); | |
| 3856 break; | |
| 3857 case 16: | |
| 3858 pixd = pixConvert16To8(pixs, L_MS_BYTE); | |
| 3859 break; | |
| 3860 default: | |
| 3861 lept_stderr("depth not in {1, 2, 4, 8, 16, 32}"); | |
| 3862 return NULL; | |
| 3863 } | |
| 3864 | |
| 3865 return pixd; | |
| 3866 } | |
| 3867 | |
| 3868 | |
| 3869 /*---------------------------------------------------------------------------* | |
| 3870 * Scaling conversion to subpixel RGB * | |
| 3871 *---------------------------------------------------------------------------*/ | |
| 3872 /*! | |
| 3873 * \brief pixConvertToSubpixelRGB() | |
| 3874 * | |
| 3875 * \param[in] pixs 8 bpp grayscale, 32 bpp rgb, or colormapped | |
| 3876 * \param[in] scalex, scaley anisotropic scaling permitted between | |
| 3877 * source and destination | |
| 3878 * \param[in] order of subpixel rgb color components in | |
| 3879 * composition of pixd: | |
| 3880 * L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR, | |
| 3881 * L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR | |
| 3882 * \return pixd 32 bpp, or NULL on error | |
| 3883 * | |
| 3884 * <pre> | |
| 3885 * Notes: | |
| 3886 * (1) If pixs has a colormap, it is removed based on its contents | |
| 3887 * to either 8 bpp gray or rgb. | |
| 3888 * (2) For horizontal subpixel splitting, the input image | |
| 3889 * is rescaled by %scaley vertically and by 3.0 times | |
| 3890 * %scalex horizontally. Then each horizontal triplet | |
| 3891 * of pixels is mapped back to a single rgb pixel, with the | |
| 3892 * r, g and b values being assigned based on the pixel triplet. | |
| 3893 * For gray triplets, the r, g, and b values are set equal to | |
| 3894 * the three gray values. For color triplets, the r, g and b | |
| 3895 * values are set equal to the components from the appropriate | |
| 3896 * subpixel. Vertical subpixel splitting is handled similarly. | |
| 3897 * (3) See pixConvertGrayToSubpixelRGB() and | |
| 3898 * pixConvertColorToSubpixelRGB() for further details. | |
| 3899 * </pre> | |
| 3900 */ | |
| 3901 PIX * | |
| 3902 pixConvertToSubpixelRGB(PIX *pixs, | |
| 3903 l_float32 scalex, | |
| 3904 l_float32 scaley, | |
| 3905 l_int32 order) | |
| 3906 { | |
| 3907 l_int32 d; | |
| 3908 PIX *pix1, *pixd; | |
| 3909 PIXCMAP *cmap; | |
| 3910 | |
| 3911 if (!pixs) | |
| 3912 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3913 d = pixGetDepth(pixs); | |
| 3914 cmap = pixGetColormap(pixs); | |
| 3915 if (d != 8 && d != 32 && !cmap) | |
| 3916 return (PIX *)ERROR_PTR("pix not 8 or 32 bpp and not cmapped", | |
| 3917 __func__, NULL); | |
| 3918 if (scalex <= 0.0 || scaley <= 0.0) | |
| 3919 return (PIX *)ERROR_PTR("scale factors must be > 0", __func__, NULL); | |
| 3920 if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR && | |
| 3921 order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR) | |
| 3922 return (PIX *)ERROR_PTR("invalid subpixel order", __func__, NULL); | |
| 3923 if ((pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC)) == NULL) | |
| 3924 return (PIX *)ERROR_PTR("pix1 not made", __func__, NULL); | |
| 3925 | |
| 3926 d = pixGetDepth(pix1); | |
| 3927 pixd = NULL; | |
| 3928 if (d == 8) | |
| 3929 pixd = pixConvertGrayToSubpixelRGB(pix1, scalex, scaley, order); | |
| 3930 else if (d == 32) | |
| 3931 pixd = pixConvertColorToSubpixelRGB(pix1, scalex, scaley, order); | |
| 3932 else | |
| 3933 L_ERROR("invalid depth %d\n", __func__, d); | |
| 3934 | |
| 3935 pixDestroy(&pix1); | |
| 3936 return pixd; | |
| 3937 } | |
| 3938 | |
| 3939 | |
| 3940 /*! | |
| 3941 * \brief pixConvertGrayToSubpixelRGB() | |
| 3942 * | |
| 3943 * \param[in] pixs 8 bpp or colormapped | |
| 3944 * \param[in] scalex, scaley | |
| 3945 * \param[in] order of subpixel rgb color components in | |
| 3946 * composition of pixd: | |
| 3947 * L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR, | |
| 3948 * L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR | |
| 3949 * \return pixd 32 bpp, or NULL on error | |
| 3950 * | |
| 3951 * <pre> | |
| 3952 * Notes: | |
| 3953 * (1) If pixs has a colormap, it is removed to 8 bpp. | |
| 3954 * (2) For horizontal subpixel splitting, the input gray image | |
| 3955 * is rescaled by %scaley vertically and by 3.0 times | |
| 3956 * %scalex horizontally. Then each horizontal triplet | |
| 3957 * of pixels is mapped back to a single rgb pixel, with the | |
| 3958 * r, g and b values being assigned from the triplet of gray values. | |
| 3959 * Similar operations are used for vertical subpixel splitting. | |
| 3960 * (3) This is a form of subpixel rendering that tends to give the | |
| 3961 * resulting text a sharper and somewhat chromatic display. | |
| 3962 * For horizontal subpixel splitting, the observable difference | |
| 3963 * between %order=L_SUBPIXEL_ORDER_RGB and | |
| 3964 * %order=L_SUBPIXEL_ORDER_BGR is reduced by optical diffusers | |
| 3965 * in the display that make the pixel color appear to emerge | |
| 3966 * from the entire pixel. | |
| 3967 * </pre> | |
| 3968 */ | |
| 3969 PIX * | |
| 3970 pixConvertGrayToSubpixelRGB(PIX *pixs, | |
| 3971 l_float32 scalex, | |
| 3972 l_float32 scaley, | |
| 3973 l_int32 order) | |
| 3974 { | |
| 3975 l_int32 w, h, d, wd, hd, wplt, wpld, i, j, rval, gval, bval, direction; | |
| 3976 l_uint32 *datat, *datad, *linet, *lined; | |
| 3977 PIX *pix1, *pix2, *pixd; | |
| 3978 PIXCMAP *cmap; | |
| 3979 | |
| 3980 if (!pixs) | |
| 3981 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 3982 d = pixGetDepth(pixs); | |
| 3983 cmap = pixGetColormap(pixs); | |
| 3984 if (d != 8 && !cmap) | |
| 3985 return (PIX *)ERROR_PTR("pix not 8 bpp & not cmapped", __func__, NULL); | |
| 3986 if (scalex <= 0.0 || scaley <= 0.0) | |
| 3987 return (PIX *)ERROR_PTR("scale factors must be > 0", __func__, NULL); | |
| 3988 if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR && | |
| 3989 order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR) | |
| 3990 return (PIX *)ERROR_PTR("invalid subpixel order", __func__, NULL); | |
| 3991 | |
| 3992 direction = | |
| 3993 (order == L_SUBPIXEL_ORDER_RGB || order == L_SUBPIXEL_ORDER_BGR) | |
| 3994 ? L_HORIZ : L_VERT; | |
| 3995 pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE); | |
| 3996 if (direction == L_HORIZ) | |
| 3997 pix2 = pixScale(pix1, 3.0f * scalex, scaley); | |
| 3998 else /* L_VERT */ | |
| 3999 pix2 = pixScale(pix1, scalex, 3.0f * scaley); | |
| 4000 | |
| 4001 pixGetDimensions(pix2, &w, &h, NULL); | |
| 4002 wd = (direction == L_HORIZ) ? w / 3 : w; | |
| 4003 hd = (direction == L_VERT) ? h / 3 : h; | |
| 4004 pixd = pixCreate(wd, hd, 32); | |
| 4005 datad = pixGetData(pixd); | |
| 4006 wpld = pixGetWpl(pixd); | |
| 4007 datat = pixGetData(pix2); | |
| 4008 wplt = pixGetWpl(pix2); | |
| 4009 if (direction == L_HORIZ) { | |
| 4010 for (i = 0; i < hd; i++) { | |
| 4011 linet = datat + i * wplt; | |
| 4012 lined = datad + i * wpld; | |
| 4013 for (j = 0; j < wd; j++) { | |
| 4014 rval = GET_DATA_BYTE(linet, 3 * j); | |
| 4015 gval = GET_DATA_BYTE(linet, 3 * j + 1); | |
| 4016 bval = GET_DATA_BYTE(linet, 3 * j + 2); | |
| 4017 if (order == L_SUBPIXEL_ORDER_RGB) | |
| 4018 composeRGBPixel(rval, gval, bval, &lined[j]); | |
| 4019 else /* order BGR */ | |
| 4020 composeRGBPixel(bval, gval, rval, &lined[j]); | |
| 4021 } | |
| 4022 } | |
| 4023 } else { /* L_VERT */ | |
| 4024 for (i = 0; i < hd; i++) { | |
| 4025 linet = datat + 3 * i * wplt; | |
| 4026 lined = datad + i * wpld; | |
| 4027 for (j = 0; j < wd; j++) { | |
| 4028 rval = GET_DATA_BYTE(linet, j); | |
| 4029 gval = GET_DATA_BYTE(linet + wplt, j); | |
| 4030 bval = GET_DATA_BYTE(linet + 2 * wplt, j); | |
| 4031 if (order == L_SUBPIXEL_ORDER_VRGB) | |
| 4032 composeRGBPixel(rval, gval, bval, &lined[j]); | |
| 4033 else /* order VBGR */ | |
| 4034 composeRGBPixel(bval, gval, rval, &lined[j]); | |
| 4035 } | |
| 4036 } | |
| 4037 } | |
| 4038 | |
| 4039 pixDestroy(&pix1); | |
| 4040 pixDestroy(&pix2); | |
| 4041 return pixd; | |
| 4042 } | |
| 4043 | |
| 4044 | |
| 4045 /*! | |
| 4046 * \brief pixConvertColorToSubpixelRGB() | |
| 4047 * | |
| 4048 * \param[in] pixs 32 bpp or colormapped | |
| 4049 * \param[in] scalex, scaley | |
| 4050 * \param[in] order of subpixel rgb color components in | |
| 4051 * composition of pixd: | |
| 4052 * L_SUBPIXEL_ORDER_RGB, L_SUBPIXEL_ORDER_BGR, | |
| 4053 * L_SUBPIXEL_ORDER_VRGB, L_SUBPIXEL_ORDER_VBGR | |
| 4054 * \return pixd 32 bpp, or NULL on error | |
| 4055 * | |
| 4056 * <pre> | |
| 4057 * Notes: | |
| 4058 * (1) If pixs has a colormap, it is removed to 32 bpp rgb. | |
| 4059 * If the colormap has no color, pixConvertGrayToSubpixelRGB() | |
| 4060 * should be called instead, because it will give the same result | |
| 4061 * more efficiently. The function pixConvertToSubpixelRGB() | |
| 4062 * will do the best thing for all cases. | |
| 4063 * (2) For horizontal subpixel splitting, the input rgb image | |
| 4064 * is rescaled by %scaley vertically and by 3.0 times | |
| 4065 * %scalex horizontally. Then for each horizontal triplet | |
| 4066 * of pixels, the r component of the final pixel is selected | |
| 4067 * from the r component of the appropriate pixel in the triplet, | |
| 4068 * and likewise for g and b. Vertical subpixel splitting is | |
| 4069 * handled similarly. | |
| 4070 * </pre> | |
| 4071 */ | |
| 4072 PIX * | |
| 4073 pixConvertColorToSubpixelRGB(PIX *pixs, | |
| 4074 l_float32 scalex, | |
| 4075 l_float32 scaley, | |
| 4076 l_int32 order) | |
| 4077 { | |
| 4078 l_int32 w, h, d, wd, hd, wplt, wpld, i, j, rval, gval, bval, direction; | |
| 4079 l_uint32 *datat, *datad, *linet, *lined; | |
| 4080 PIX *pix1, *pix2, *pixd; | |
| 4081 PIXCMAP *cmap; | |
| 4082 | |
| 4083 if (!pixs) | |
| 4084 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 4085 d = pixGetDepth(pixs); | |
| 4086 cmap = pixGetColormap(pixs); | |
| 4087 if (d != 32 && !cmap) | |
| 4088 return (PIX *)ERROR_PTR("pix not 32 bpp & not cmapped", __func__, NULL); | |
| 4089 if (scalex <= 0.0 || scaley <= 0.0) | |
| 4090 return (PIX *)ERROR_PTR("scale factors must be > 0", __func__, NULL); | |
| 4091 if (order != L_SUBPIXEL_ORDER_RGB && order != L_SUBPIXEL_ORDER_BGR && | |
| 4092 order != L_SUBPIXEL_ORDER_VRGB && order != L_SUBPIXEL_ORDER_VBGR) | |
| 4093 return (PIX *)ERROR_PTR("invalid subpixel order", __func__, NULL); | |
| 4094 | |
| 4095 direction = | |
| 4096 (order == L_SUBPIXEL_ORDER_RGB || order == L_SUBPIXEL_ORDER_BGR) | |
| 4097 ? L_HORIZ : L_VERT; | |
| 4098 pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); | |
| 4099 if (direction == L_HORIZ) | |
| 4100 pix2 = pixScale(pix1, 3.0f * scalex, scaley); | |
| 4101 else /* L_VERT */ | |
| 4102 pix2 = pixScale(pix1, scalex, 3.0f * scaley); | |
| 4103 | |
| 4104 pixGetDimensions(pix2, &w, &h, NULL); | |
| 4105 wd = (direction == L_HORIZ) ? w / 3 : w; | |
| 4106 hd = (direction == L_VERT) ? h / 3 : h; | |
| 4107 pixd = pixCreate(wd, hd, 32); | |
| 4108 pixCopyInputFormat(pixd, pixs); | |
| 4109 datad = pixGetData(pixd); | |
| 4110 wpld = pixGetWpl(pixd); | |
| 4111 datat = pixGetData(pix2); | |
| 4112 wplt = pixGetWpl(pix2); | |
| 4113 if (direction == L_HORIZ) { | |
| 4114 for (i = 0; i < hd; i++) { | |
| 4115 linet = datat + i * wplt; | |
| 4116 lined = datad + i * wpld; | |
| 4117 for (j = 0; j < wd; j++) { | |
| 4118 if (order == L_SUBPIXEL_ORDER_RGB) { | |
| 4119 extractRGBValues(linet[3 * j], &rval, NULL, NULL); | |
| 4120 extractRGBValues(linet[3 * j + 1], NULL, &gval, NULL); | |
| 4121 extractRGBValues(linet[3 * j + 2], NULL, NULL, &bval); | |
| 4122 } else { /* order BGR */ | |
| 4123 extractRGBValues(linet[3 * j], NULL, NULL, &bval); | |
| 4124 extractRGBValues(linet[3 * j + 1], NULL, &gval, NULL); | |
| 4125 extractRGBValues(linet[3 * j + 2], &rval, NULL, NULL); | |
| 4126 } | |
| 4127 composeRGBPixel(rval, gval, bval, &lined[j]); | |
| 4128 } | |
| 4129 } | |
| 4130 } else { /* L_VERT */ | |
| 4131 for (i = 0; i < hd; i++) { | |
| 4132 linet = datat + 3 * i * wplt; | |
| 4133 lined = datad + i * wpld; | |
| 4134 for (j = 0; j < wd; j++) { | |
| 4135 if (order == L_SUBPIXEL_ORDER_VRGB) { | |
| 4136 extractRGBValues(linet[j], &rval, NULL, NULL); | |
| 4137 extractRGBValues((linet + wplt)[j], NULL, &gval, NULL); | |
| 4138 extractRGBValues((linet + 2 * wplt)[j], NULL, NULL, &bval); | |
| 4139 } else { /* order VBGR */ | |
| 4140 extractRGBValues(linet[j], NULL, NULL, &bval); | |
| 4141 extractRGBValues((linet + wplt)[j], NULL, &gval, NULL); | |
| 4142 extractRGBValues((linet + 2 * wplt)[j], &rval, NULL, NULL); | |
| 4143 } | |
| 4144 composeRGBPixel(rval, gval, bval, &lined[j]); | |
| 4145 } | |
| 4146 } | |
| 4147 } | |
| 4148 | |
| 4149 if (pixGetSpp(pixs) == 4) | |
| 4150 pixScaleAndTransferAlpha(pixd, pixs, scalex, scaley); | |
| 4151 | |
| 4152 pixDestroy(&pix1); | |
| 4153 pixDestroy(&pix2); | |
| 4154 return pixd; | |
| 4155 } | |
| 4156 | |
| 4157 | |
| 4158 /*---------------------------------------------------------------------* | |
| 4159 * Setting neutral point for min/max boost conversion to gray * | |
| 4160 *---------------------------------------------------------------------*/ | |
| 4161 /*! | |
| 4162 * \brief l_setNeutralBoostVal() | |
| 4163 * | |
| 4164 * \param[in] val between 1 and 255; typical value is 180 | |
| 4165 * \return void | |
| 4166 * | |
| 4167 * <pre> | |
| 4168 * Notes: | |
| 4169 * (1) This raises or lowers the selected min or max RGB component value, | |
| 4170 * depending on if that component is above or below this value. | |
| 4171 * </pre> | |
| 4172 */ | |
| 4173 void | |
| 4174 l_setNeutralBoostVal(l_int32 val) | |
| 4175 { | |
| 4176 if (val <= 0) { | |
| 4177 L_ERROR("invalid reference value for neutral boost\n", __func__); | |
| 4178 return; | |
| 4179 } | |
| 4180 var_NEUTRAL_BOOST_VAL = val; | |
| 4181 } |
