Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/blend.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 blend.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Blending two images that are not colormapped | |
| 32 * PIX *pixBlend() | |
| 33 * PIX *pixBlendMask() | |
| 34 * PIX *pixBlendGray() | |
| 35 * PIX *pixBlendGrayInverse() | |
| 36 * PIX *pixBlendColor() | |
| 37 * PIX *pixBlendColorByChannel() | |
| 38 * PIX *pixBlendGrayAdapt() | |
| 39 * static l_int32 blendComponents() | |
| 40 * PIX *pixFadeWithGray() | |
| 41 * PIX *pixBlendHardLight() | |
| 42 * static l_int32 blendHardLightComponents() | |
| 43 * | |
| 44 * Blending two colormapped images | |
| 45 * l_int32 pixBlendCmap() | |
| 46 * | |
| 47 * Blending two images using a third (alpha mask) | |
| 48 * PIX *pixBlendWithGrayMask() | |
| 49 * | |
| 50 * Blending background to a specific color | |
| 51 * PIX *pixBlendBackgroundToColor() | |
| 52 * | |
| 53 * Multiplying by a specific color | |
| 54 * PIX *pixMultiplyByColor() | |
| 55 * | |
| 56 * Rendering with alpha blending over a uniform background | |
| 57 * PIX *pixAlphaBlendUniform() | |
| 58 * | |
| 59 * Adding an alpha layer for blending | |
| 60 * PIX *pixAddAlphaToBlend() | |
| 61 * | |
| 62 * Setting a transparent alpha component over a white background | |
| 63 * PIX *pixSetAlphaOverWhite() | |
| 64 * | |
| 65 * Fading from the edge | |
| 66 * l_int32 pixLinearEdgeFade() | |
| 67 * | |
| 68 * In blending operations a new pix is produced where typically | |
| 69 * a subset of pixels in src1 are changed by the set of pixels | |
| 70 * in src2, when src2 is located in a given position relative | |
| 71 * to src1. This is similar to rasterop, except that the | |
| 72 * blending operations we allow are more complex, and typically | |
| 73 * result in dest pixels that are a linear combination of two | |
| 74 * pixels, such as src1 and its inverse. I find it convenient | |
| 75 * to think of src2 as the "blender" (the one that takes the action) | |
| 76 * and src1 as the "blendee" (the one that changes). | |
| 77 * | |
| 78 * Blending works best when src1 is 8 or 32 bpp. We also allow | |
| 79 * src1 to be colormapped, but the colormap is removed before blending, | |
| 80 * so if src1 is colormapped, we can't allow in-place blending. | |
| 81 * | |
| 82 * Because src2 is typically smaller than src1, we can implement by | |
| 83 * clipping src2 to src1 and then transforming some of the dest | |
| 84 * pixels that are under the support of src2. In practice, we | |
| 85 * do the clipping in the inner pixel loop. For grayscale and | |
| 86 * color src2, we also allow a simple form of transparency, where | |
| 87 * pixels of a particular value in src2 are transparent; for those pixels, | |
| 88 * no blending is done. | |
| 89 * | |
| 90 * The blending functions are categorized by the depth of src2, | |
| 91 * the blender, and not that of src1, the blendee. | |
| 92 * | |
| 93 * ~ If src2 is 1 bpp, we can do one of three things: | |
| 94 * (1) L_BLEND_WITH_INVERSE: Blend a given fraction of src1 with its | |
| 95 * inverse color for those pixels in src2 that are fg (ON), | |
| 96 * and leave the dest pixels unchanged for pixels in src2 that | |
| 97 * are bg (OFF). | |
| 98 * (2) L_BLEND_TO_WHITE: Fade the src1 pixels toward white by a | |
| 99 * given fraction for those pixels in src2 that are fg (ON), | |
| 100 * and leave the dest pixels unchanged for pixels in src2 that | |
| 101 * are bg (OFF). | |
| 102 * (3) L_BLEND_TO_BLACK: Fade the src1 pixels toward black by a | |
| 103 * given fraction for those pixels in src2 that are fg (ON), | |
| 104 * and leave the dest pixels unchanged for pixels in src2 that | |
| 105 * are bg (OFF). | |
| 106 * The blending function is pixBlendMask(). | |
| 107 * | |
| 108 * ~ If src2 is 8 bpp grayscale, we can do one of two things | |
| 109 * (but see pixFadeWithGray() below): | |
| 110 * (1) L_BLEND_GRAY: If src1 is 8 bpp, mix the two values, using | |
| 111 * a fraction of src2 and (1 - fraction) of src1. | |
| 112 * If src1 is 32 bpp (rgb), mix the fraction of src2 with | |
| 113 * each of the color components in src1. | |
| 114 * (2) L_BLEND_GRAY_WITH_INVERSE: Use the grayscale value in src2 | |
| 115 * to determine how much of the inverse of a src1 pixel is | |
| 116 * to be combined with the pixel value. The input fraction | |
| 117 * further acts to scale the change in the src1 pixel. | |
| 118 * The blending function is pixBlendGray(). | |
| 119 * | |
| 120 * ~ If src2 is color, we blend a given fraction of src2 with | |
| 121 * src1. If src1 is 8 bpp, the resulting image is 32 bpp. | |
| 122 * The blending function is pixBlendColor(). | |
| 123 * | |
| 124 * ~ For all three blending functions -- pixBlendMask(), pixBlendGray() | |
| 125 * and pixBlendColor() -- you can apply the blender to the blendee | |
| 126 * either in-place or generating a new pix. For the in-place | |
| 127 * operation, this requires that the depth of the resulting pix | |
| 128 * must equal that of the input pixs1. | |
| 129 * | |
| 130 * ~ We remove colormaps from src1 and src2 before blending. | |
| 131 * Any quantization would have to be done after blending. | |
| 132 * | |
| 133 * We include another function, pixFadeWithGray(), that blends | |
| 134 * a gray or color src1 with a gray src2. It does one of these things: | |
| 135 * (1) L_BLEND_TO_WHITE: Fade the src1 pixels toward white by | |
| 136 * a number times the value in src2. | |
| 137 * (2) L_BLEND_TO_BLACK: Fade the src1 pixels toward black by | |
| 138 * a number times the value in src2. | |
| 139 * | |
| 140 * Also included is a generalization of the so-called "hard light" | |
| 141 * blending: pixBlendHardLight(). We generalize by allowing a fraction < 1.0 | |
| 142 * of the blender to be admixed with the blendee. The standard function | |
| 143 * does full mixing. | |
| 144 * </pre> | |
| 145 */ | |
| 146 | |
| 147 #ifdef HAVE_CONFIG_H | |
| 148 #include <config_auto.h> | |
| 149 #endif /* HAVE_CONFIG_H */ | |
| 150 | |
| 151 #include "allheaders.h" | |
| 152 | |
| 153 static l_int32 blendComponents(l_int32 a, l_int32 b, l_float32 fract); | |
| 154 static l_int32 blendHardLightComponents(l_int32 a, l_int32 b, l_float32 fract); | |
| 155 | |
| 156 /*-------------------------------------------------------------* | |
| 157 * Blending two images that are not colormapped * | |
| 158 *-------------------------------------------------------------*/ | |
| 159 /*! | |
| 160 * \brief pixBlend() | |
| 161 * | |
| 162 * \param[in] pixs1 blendee | |
| 163 * \param[in] pixs2 blender; typ. smaller | |
| 164 * \param[in] x,y origin [UL corner] of pixs2 relative to | |
| 165 * the origin of pixs1; can be < 0 | |
| 166 * \param[in] fract blending fraction | |
| 167 * \return pixd blended image, or null on error | |
| 168 * | |
| 169 * <pre> | |
| 170 * Notes: | |
| 171 * (1) This is a simple top-level interface. For more flexibility, | |
| 172 * call directly into pixBlendMask(), etc. | |
| 173 * </pre> | |
| 174 */ | |
| 175 PIX * | |
| 176 pixBlend(PIX *pixs1, | |
| 177 PIX *pixs2, | |
| 178 l_int32 x, | |
| 179 l_int32 y, | |
| 180 l_float32 fract) | |
| 181 { | |
| 182 l_int32 w1, h1, d1, d2; | |
| 183 BOX *box; | |
| 184 PIX *pixc, *pixt, *pixd; | |
| 185 | |
| 186 if (!pixs1) | |
| 187 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, NULL); | |
| 188 if (!pixs2) | |
| 189 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, NULL); | |
| 190 | |
| 191 /* check relative depths */ | |
| 192 d1 = pixGetDepth(pixs1); | |
| 193 d2 = pixGetDepth(pixs2); | |
| 194 if (d1 == 1 && d2 > 1) | |
| 195 return (PIX *)ERROR_PTR("mixing gray or color with 1 bpp", | |
| 196 __func__, NULL); | |
| 197 | |
| 198 /* remove colormap from pixs2 if necessary */ | |
| 199 pixt = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); | |
| 200 d2 = pixGetDepth(pixt); | |
| 201 | |
| 202 /* Check if pixs2 is clipped by its position with respect | |
| 203 * to pixs1; if so, clip it and redefine x and y if necessary. | |
| 204 * This actually isn't necessary, as the specific blending | |
| 205 * functions do the clipping directly in the pixel loop | |
| 206 * over pixs2, but it's included here to show how it can | |
| 207 * easily be done on pixs2 first. */ | |
| 208 pixGetDimensions(pixs1, &w1, &h1, NULL); | |
| 209 box = boxCreate(-x, -y, w1, h1); /* box of pixs1 relative to pixs2 */ | |
| 210 pixc = pixClipRectangle(pixt, box, NULL); | |
| 211 boxDestroy(&box); | |
| 212 if (!pixc) { | |
| 213 L_WARNING("box doesn't overlap pix\n", __func__); | |
| 214 pixDestroy(&pixt); | |
| 215 return NULL; | |
| 216 } | |
| 217 x = L_MAX(0, x); | |
| 218 y = L_MAX(0, y); | |
| 219 | |
| 220 if (d2 == 1) { | |
| 221 pixd = pixBlendMask(NULL, pixs1, pixc, x, y, fract, | |
| 222 L_BLEND_WITH_INVERSE); | |
| 223 } else if (d2 == 8) { | |
| 224 pixd = pixBlendGray(NULL, pixs1, pixc, x, y, fract, | |
| 225 L_BLEND_GRAY, 0, 0); | |
| 226 } else { /* d2 == 32 */ | |
| 227 pixd = pixBlendColor(NULL, pixs1, pixc, x, y, fract, 0, 0); | |
| 228 } | |
| 229 | |
| 230 pixDestroy(&pixc); | |
| 231 pixDestroy(&pixt); | |
| 232 return pixd; | |
| 233 } | |
| 234 | |
| 235 | |
| 236 /*! | |
| 237 * \brief pixBlendMask() | |
| 238 * | |
| 239 * \param[in] pixd [optional]; either NULL or equal to pixs1 for in-place | |
| 240 * \param[in] pixs1 blendee, depth > 1 | |
| 241 * \param[in] pixs2 blender, 1 bpp; typ. smaller in size than pixs1 | |
| 242 * \param[in] x,y origin [UL corner] of pixs2 relative to | |
| 243 * the origin of pixs1; can be < 0 | |
| 244 * \param[in] fract blending fraction | |
| 245 * \param[in] type L_BLEND_WITH_INVERSE, L_BLEND_TO_WHITE, | |
| 246 * L_BLEND_TO_BLACK | |
| 247 * \return pixd if OK; null on error | |
| 248 * | |
| 249 * <pre> | |
| 250 * Notes: | |
| 251 * (1) Clipping of pixs2 to pixs1 is done in the inner pixel loop. | |
| 252 * (2) If pixs1 has a colormap, it is removed. | |
| 253 * (3) For inplace operation (pixs1 not cmapped), call it this way: | |
| 254 * pixBlendMask(pixs1, pixs1, pixs2, ...) | |
| 255 * (4) For generating a new pixd: | |
| 256 * pixd = pixBlendMask(NULL, pixs1, pixs2, ...) | |
| 257 * (5) Only call in-place if pixs1 does not have a colormap. | |
| 258 * (6) Invalid %fract defaults to 0.5 with a warning. | |
| 259 * Invalid %type defaults to L_BLEND_WITH_INVERSE with a warning. | |
| 260 * </pre> | |
| 261 */ | |
| 262 PIX * | |
| 263 pixBlendMask(PIX *pixd, | |
| 264 PIX *pixs1, | |
| 265 PIX *pixs2, | |
| 266 l_int32 x, | |
| 267 l_int32 y, | |
| 268 l_float32 fract, | |
| 269 l_int32 type) | |
| 270 { | |
| 271 l_int32 i, j, d, wc, hc, w, h, wplc; | |
| 272 l_int32 val, rval, gval, bval; | |
| 273 l_uint32 pixval; | |
| 274 l_uint32 *linec, *datac; | |
| 275 PIX *pixc, *pix1, *pix2; | |
| 276 | |
| 277 if (!pixs1) | |
| 278 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, NULL); | |
| 279 if (!pixs2) | |
| 280 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, NULL); | |
| 281 if (pixGetDepth(pixs1) == 1) | |
| 282 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", __func__, NULL); | |
| 283 if (pixGetDepth(pixs2) != 1) | |
| 284 return (PIX *)ERROR_PTR("pixs2 not 1 bpp", __func__, NULL); | |
| 285 if (pixd == pixs1 && pixGetColormap(pixs1)) | |
| 286 return (PIX *)ERROR_PTR("inplace; pixs1 has colormap", __func__, NULL); | |
| 287 if (pixd && (pixd != pixs1)) | |
| 288 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", __func__, NULL); | |
| 289 if (fract < 0.0 || fract > 1.0) { | |
| 290 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", __func__); | |
| 291 fract = 0.5; | |
| 292 } | |
| 293 if (type != L_BLEND_WITH_INVERSE && type != L_BLEND_TO_WHITE && | |
| 294 type != L_BLEND_TO_BLACK) { | |
| 295 L_WARNING("invalid blend type; setting to L_BLEND_WITH_INVERSE\n", | |
| 296 __func__); | |
| 297 type = L_BLEND_WITH_INVERSE; | |
| 298 } | |
| 299 | |
| 300 /* If pixd != NULL, we know that it is equal to pixs1 and | |
| 301 * that pixs1 does not have a colormap, so that an in-place operation | |
| 302 * can be done. Otherwise, remove colormap from pixs1 if | |
| 303 * it exists and unpack to at least 8 bpp if necessary, | |
| 304 * to do the blending on a new pix. */ | |
| 305 if (!pixd) { | |
| 306 pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); | |
| 307 if (pixGetDepth(pix1) < 8) | |
| 308 pix2 = pixConvertTo8(pix1, FALSE); | |
| 309 else | |
| 310 pix2 = pixClone(pix1); | |
| 311 pixd = pixCopy(NULL, pix2); | |
| 312 pixDestroy(&pix1); | |
| 313 pixDestroy(&pix2); | |
| 314 } | |
| 315 | |
| 316 pixGetDimensions(pixd, &w, &h, &d); /* d must be either 8 or 32 bpp */ | |
| 317 pixc = pixClone(pixs2); | |
| 318 wc = pixGetWidth(pixc); | |
| 319 hc = pixGetHeight(pixc); | |
| 320 datac = pixGetData(pixc); | |
| 321 wplc = pixGetWpl(pixc); | |
| 322 | |
| 323 /* Check limits for src1, in case clipping was not done. */ | |
| 324 switch (type) | |
| 325 { | |
| 326 case L_BLEND_WITH_INVERSE: | |
| 327 /* | |
| 328 * The basic logic for this blending is: | |
| 329 * p --> (1 - f) * p + f * (1 - p) | |
| 330 * where p is a normalized value: p = pixval / 255. | |
| 331 * Thus, | |
| 332 * p --> p + f * (1 - 2 * p) | |
| 333 */ | |
| 334 for (i = 0; i < hc; i++) { | |
| 335 if (i + y < 0 || i + y >= h) continue; | |
| 336 linec = datac + i * wplc; | |
| 337 for (j = 0; j < wc; j++) { | |
| 338 if (j + x < 0 || j + x >= w) continue; | |
| 339 bval = GET_DATA_BIT(linec, j); | |
| 340 if (bval) { | |
| 341 switch (d) | |
| 342 { | |
| 343 case 8: | |
| 344 pixGetPixel(pixd, x + j, y + i, &pixval); | |
| 345 val = (l_int32)(pixval + fract * (255 - 2 * pixval)); | |
| 346 pixSetPixel(pixd, x + j, y + i, val); | |
| 347 break; | |
| 348 case 32: | |
| 349 pixGetPixel(pixd, x + j, y + i, &pixval); | |
| 350 extractRGBValues(pixval, &rval, &gval, &bval); | |
| 351 rval = (l_int32)(rval + fract * (255 - 2 * rval)); | |
| 352 gval = (l_int32)(gval + fract * (255 - 2 * gval)); | |
| 353 bval = (l_int32)(bval + fract * (255 - 2 * bval)); | |
| 354 composeRGBPixel(rval, gval, bval, &pixval); | |
| 355 pixSetPixel(pixd, x + j, y + i, pixval); | |
| 356 break; | |
| 357 default: | |
| 358 L_WARNING("d neither 8 nor 32 bpp; no blend\n", | |
| 359 __func__); | |
| 360 } | |
| 361 } | |
| 362 } | |
| 363 } | |
| 364 break; | |
| 365 case L_BLEND_TO_WHITE: | |
| 366 /* | |
| 367 * The basic logic for this blending is: | |
| 368 * p --> p + f * (1 - p) (p normalized to [0...1]) | |
| 369 */ | |
| 370 for (i = 0; i < hc; i++) { | |
| 371 if (i + y < 0 || i + y >= h) continue; | |
| 372 linec = datac + i * wplc; | |
| 373 for (j = 0; j < wc; j++) { | |
| 374 if (j + x < 0 || j + x >= w) continue; | |
| 375 bval = GET_DATA_BIT(linec, j); | |
| 376 if (bval) { | |
| 377 switch (d) | |
| 378 { | |
| 379 case 8: | |
| 380 pixGetPixel(pixd, x + j, y + i, &pixval); | |
| 381 val = (l_int32)(pixval + fract * (255 - pixval)); | |
| 382 pixSetPixel(pixd, x + j, y + i, val); | |
| 383 break; | |
| 384 case 32: | |
| 385 pixGetPixel(pixd, x + j, y + i, &pixval); | |
| 386 extractRGBValues(pixval, &rval, &gval, &bval); | |
| 387 rval = (l_int32)(rval + fract * (255 - rval)); | |
| 388 gval = (l_int32)(gval + fract * (255 - gval)); | |
| 389 bval = (l_int32)(bval + fract * (255 - bval)); | |
| 390 composeRGBPixel(rval, gval, bval, &pixval); | |
| 391 pixSetPixel(pixd, x + j, y + i, pixval); | |
| 392 break; | |
| 393 default: | |
| 394 L_WARNING("d neither 8 nor 32 bpp; no blend\n", | |
| 395 __func__); | |
| 396 } | |
| 397 } | |
| 398 } | |
| 399 } | |
| 400 break; | |
| 401 case L_BLEND_TO_BLACK: | |
| 402 /* | |
| 403 * The basic logic for this blending is: | |
| 404 * p --> (1 - f) * p (p normalized to [0...1]) | |
| 405 */ | |
| 406 for (i = 0; i < hc; i++) { | |
| 407 if (i + y < 0 || i + y >= h) continue; | |
| 408 linec = datac + i * wplc; | |
| 409 for (j = 0; j < wc; j++) { | |
| 410 if (j + x < 0 || j + x >= w) continue; | |
| 411 bval = GET_DATA_BIT(linec, j); | |
| 412 if (bval) { | |
| 413 switch (d) | |
| 414 { | |
| 415 case 8: | |
| 416 pixGetPixel(pixd, x + j, y + i, &pixval); | |
| 417 val = (l_int32)((1. - fract) * pixval); | |
| 418 pixSetPixel(pixd, x + j, y + i, val); | |
| 419 break; | |
| 420 case 32: | |
| 421 pixGetPixel(pixd, x + j, y + i, &pixval); | |
| 422 extractRGBValues(pixval, &rval, &gval, &bval); | |
| 423 rval = (l_int32)((1. - fract) * rval); | |
| 424 gval = (l_int32)((1. - fract) * gval); | |
| 425 bval = (l_int32)((1. - fract) * bval); | |
| 426 composeRGBPixel(rval, gval, bval, &pixval); | |
| 427 pixSetPixel(pixd, x + j, y + i, pixval); | |
| 428 break; | |
| 429 default: | |
| 430 L_WARNING("d neither 8 nor 32 bpp; no blend\n", | |
| 431 __func__); | |
| 432 } | |
| 433 } | |
| 434 } | |
| 435 } | |
| 436 break; | |
| 437 default: | |
| 438 L_WARNING("invalid binary mask blend type\n", __func__); | |
| 439 break; | |
| 440 } | |
| 441 | |
| 442 pixDestroy(&pixc); | |
| 443 return pixd; | |
| 444 } | |
| 445 | |
| 446 | |
| 447 /*! | |
| 448 * \brief pixBlendGray() | |
| 449 * | |
| 450 * \param[in] pixd [optional] either equal to pixs1 for in-place, | |
| 451 * or NULL | |
| 452 * \param[in] pixs1 blendee, depth > 1 | |
| 453 * \param[in] pixs2 blender, any depth; typically, the area of | |
| 454 * pixs2 is smaller than pixs1 | |
| 455 * \param[in] x,y origin [UL corner] of pixs2 relative to | |
| 456 * the origin of pixs1; can be < 0 | |
| 457 * \param[in] fract blending fraction | |
| 458 * \param[in] type L_BLEND_GRAY, L_BLEND_GRAY_WITH_INVERSE | |
| 459 * \param[in] transparent 1 to use transparency; 0 otherwise | |
| 460 * \param[in] transpix pixel grayval in pixs2 that is to be transparent | |
| 461 * \return pixd if OK; pixs1 on error | |
| 462 * | |
| 463 * <pre> | |
| 464 * Notes: | |
| 465 * (1) For inplace operation (pixs1 not cmapped), call it this way: | |
| 466 * pixBlendGray(pixs1, pixs1, pixs2, ...) | |
| 467 * (2) For generating a new pixd: | |
| 468 * pixd = pixBlendGray(NULL, pixs1, pixs2, ...) | |
| 469 * (3) Clipping of pixs2 to pixs1 is done in the inner pixel loop. | |
| 470 * (4) If pixs1 has a colormap, it is removed; otherwise, if pixs1 | |
| 471 * has depth < 8, it is unpacked to generate a 8 bpp pix. | |
| 472 * (5) If transparent = 0, the blending fraction (fract) is | |
| 473 * applied equally to all pixels. | |
| 474 * (6) If transparent = 1, all pixels of value transpix (typically | |
| 475 * either 0 or 0xff) in pixs2 are transparent in the blend. | |
| 476 * (7) After processing pixs1, it is either 8 bpp or 32 bpp: | |
| 477 * ~ if 8 bpp, the fraction of pixs2 is mixed with pixs1. | |
| 478 * ~ if 32 bpp, each component of pixs1 is mixed with | |
| 479 * the same fraction of pixs2. | |
| 480 * (8) For L_BLEND_GRAY_WITH_INVERSE, the white values of the blendee | |
| 481 * (cval == 255 in the code below) result in a delta of 0. | |
| 482 * Thus, these pixels are intrinsically transparent! | |
| 483 * The "pivot" value of the src, at which no blending occurs, is | |
| 484 * 128. Compare with the adaptive pivot in pixBlendGrayAdapt(). | |
| 485 * (9) Invalid %fract defaults to 0.5 with a warning. | |
| 486 * Invalid %type defaults to L_BLEND_GRAY with a warning. | |
| 487 * </pre> | |
| 488 */ | |
| 489 PIX * | |
| 490 pixBlendGray(PIX *pixd, | |
| 491 PIX *pixs1, | |
| 492 PIX *pixs2, | |
| 493 l_int32 x, | |
| 494 l_int32 y, | |
| 495 l_float32 fract, | |
| 496 l_int32 type, | |
| 497 l_int32 transparent, | |
| 498 l_uint32 transpix) | |
| 499 { | |
| 500 l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta; | |
| 501 l_int32 ival, irval, igval, ibval, cval, dval; | |
| 502 l_uint32 val32; | |
| 503 l_uint32 *linec, *lined, *datac, *datad; | |
| 504 PIX *pixc, *pix1, *pix2; | |
| 505 | |
| 506 if (!pixs1) | |
| 507 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); | |
| 508 if (!pixs2) | |
| 509 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); | |
| 510 if (pixGetDepth(pixs1) == 1) | |
| 511 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", __func__, pixd); | |
| 512 if (pixd == pixs1 && pixGetColormap(pixs1)) | |
| 513 return (PIX *)ERROR_PTR("can't do in-place with cmap", __func__, pixd); | |
| 514 if (pixd && (pixd != pixs1)) | |
| 515 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", __func__, pixd); | |
| 516 if (fract < 0.0 || fract > 1.0) { | |
| 517 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", __func__); | |
| 518 fract = 0.5; | |
| 519 } | |
| 520 if (type != L_BLEND_GRAY && type != L_BLEND_GRAY_WITH_INVERSE) { | |
| 521 L_WARNING("invalid blend type; setting to L_BLEND_GRAY\n", __func__); | |
| 522 type = L_BLEND_GRAY; | |
| 523 } | |
| 524 | |
| 525 /* If pixd != NULL, we know that it is equal to pixs1 and | |
| 526 * that pixs1 does not have a colormap, so that an in-place operation | |
| 527 * can be done. Otherwise, remove colormap from pixs1 if | |
| 528 * it exists and unpack to at least 8 bpp if necessary, | |
| 529 * to do the blending on a new pix. */ | |
| 530 if (!pixd) { | |
| 531 pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); | |
| 532 if (pixGetDepth(pix1) < 8) | |
| 533 pix2 = pixConvertTo8(pix1, FALSE); | |
| 534 else | |
| 535 pix2 = pixClone(pix1); | |
| 536 pixd = pixCopy(NULL, pix2); | |
| 537 pixDestroy(&pix1); | |
| 538 pixDestroy(&pix2); | |
| 539 } | |
| 540 | |
| 541 pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */ | |
| 542 wpld = pixGetWpl(pixd); | |
| 543 datad = pixGetData(pixd); | |
| 544 pixc = pixConvertTo8(pixs2, 0); | |
| 545 pixGetDimensions(pixc, &wc, &hc, NULL); | |
| 546 datac = pixGetData(pixc); | |
| 547 wplc = pixGetWpl(pixc); | |
| 548 | |
| 549 /* Check limits for src1, in case clipping was not done */ | |
| 550 if (type == L_BLEND_GRAY) { | |
| 551 /* | |
| 552 * The basic logic for this blending is: | |
| 553 * p --> (1 - f) * p + f * c | |
| 554 * where c is the 8 bpp blender. All values are normalized to [0...1]. | |
| 555 */ | |
| 556 for (i = 0; i < hc; i++) { | |
| 557 if (i + y < 0 || i + y >= h) continue; | |
| 558 linec = datac + i * wplc; | |
| 559 lined = datad + (i + y) * wpld; | |
| 560 switch (d) | |
| 561 { | |
| 562 case 8: | |
| 563 for (j = 0; j < wc; j++) { | |
| 564 if (j + x < 0 || j + x >= w) continue; | |
| 565 cval = GET_DATA_BYTE(linec, j); | |
| 566 if (transparent == 0 || cval != transpix) { | |
| 567 dval = GET_DATA_BYTE(lined, j + x); | |
| 568 ival = (l_int32)((1. - fract) * dval + fract * cval); | |
| 569 SET_DATA_BYTE(lined, j + x, ival); | |
| 570 } | |
| 571 } | |
| 572 break; | |
| 573 case 32: | |
| 574 for (j = 0; j < wc; j++) { | |
| 575 if (j + x < 0 || j + x >= w) continue; | |
| 576 cval = GET_DATA_BYTE(linec, j); | |
| 577 if (transparent == 0 || cval != transpix) { | |
| 578 val32 = *(lined + j + x); | |
| 579 extractRGBValues(val32, &irval, &igval, &ibval); | |
| 580 irval = (l_int32)((1. - fract) * irval + fract * cval); | |
| 581 igval = (l_int32)((1. - fract) * igval + fract * cval); | |
| 582 ibval = (l_int32)((1. - fract) * ibval + fract * cval); | |
| 583 composeRGBPixel(irval, igval, ibval, &val32); | |
| 584 *(lined + j + x) = val32; | |
| 585 } | |
| 586 } | |
| 587 break; | |
| 588 default: | |
| 589 break; /* shouldn't happen */ | |
| 590 } | |
| 591 } | |
| 592 } else { /* L_BLEND_GRAY_WITH_INVERSE */ | |
| 593 for (i = 0; i < hc; i++) { | |
| 594 if (i + y < 0 || i + y >= h) continue; | |
| 595 linec = datac + i * wplc; | |
| 596 lined = datad + (i + y) * wpld; | |
| 597 switch (d) | |
| 598 { | |
| 599 case 8: | |
| 600 /* | |
| 601 * For 8 bpp, the dest pix is shifted by a signed amount | |
| 602 * proportional to the distance from 128 (the pivot value), | |
| 603 * and to the darkness of src2. If the dest is darker | |
| 604 * than 128, it becomes lighter, and v.v. | |
| 605 * The basic logic is: | |
| 606 * d --> d + f * (0.5 - d) * (1 - c) | |
| 607 * where d and c are normalized pixel values for src1 and | |
| 608 * src2, respectively, with 8 bit normalization to [0...1]. | |
| 609 */ | |
| 610 for (j = 0; j < wc; j++) { | |
| 611 if (j + x < 0 || j + x >= w) continue; | |
| 612 cval = GET_DATA_BYTE(linec, j); | |
| 613 if (transparent == 0 || cval != transpix) { | |
| 614 ival = GET_DATA_BYTE(lined, j + x); | |
| 615 delta = (128 - ival) * (255 - cval) / 256; | |
| 616 ival += (l_int32)(fract * delta + 0.5); | |
| 617 SET_DATA_BYTE(lined, j + x, ival); | |
| 618 } | |
| 619 } | |
| 620 break; | |
| 621 case 32: | |
| 622 /* Each component is shifted by the same formula for 8 bpp */ | |
| 623 for (j = 0; j < wc; j++) { | |
| 624 if (j + x < 0 || j + x >= w) continue; | |
| 625 cval = GET_DATA_BYTE(linec, j); | |
| 626 if (transparent == 0 || cval != transpix) { | |
| 627 val32 = *(lined + j + x); | |
| 628 extractRGBValues(val32, &irval, &igval, &ibval); | |
| 629 delta = (128 - irval) * (255 - cval) / 256; | |
| 630 irval += (l_int32)(fract * delta + 0.5); | |
| 631 delta = (128 - igval) * (255 - cval) / 256; | |
| 632 igval += (l_int32)(fract * delta + 0.5); | |
| 633 delta = (128 - ibval) * (255 - cval) / 256; | |
| 634 ibval += (l_int32)(fract * delta + 0.5); | |
| 635 composeRGBPixel(irval, igval, ibval, &val32); | |
| 636 *(lined + j + x) = val32; | |
| 637 } | |
| 638 } | |
| 639 break; | |
| 640 default: | |
| 641 break; /* shouldn't happen */ | |
| 642 } | |
| 643 } | |
| 644 } | |
| 645 | |
| 646 pixDestroy(&pixc); | |
| 647 return pixd; | |
| 648 } | |
| 649 | |
| 650 | |
| 651 /*! | |
| 652 * \brief pixBlendGrayInverse() | |
| 653 * | |
| 654 * \param[in] pixd [optional] either equal to pixs1 for in-place, or NULL | |
| 655 * \param[in] pixd [optional] either NULL or equal to pixs1 for in-place | |
| 656 * \param[in] pixs1 blendee, depth > 1 | |
| 657 * \param[in] pixs2 blender, any depth; typ. smaller in size than pixs1 | |
| 658 * \param[in] x,y origin [UL corner] of pixs2 relative to | |
| 659 * the origin of pixs1; can be < 0 | |
| 660 * \param[in] fract blending fraction | |
| 661 * \return pixd if OK; pixs1 on error | |
| 662 * | |
| 663 * <pre> | |
| 664 * Notes: | |
| 665 * (1) For inplace operation (pixs1 not cmapped), call it this way: | |
| 666 * pixBlendGrayInverse(pixs1, pixs1, pixs2, ...) | |
| 667 * (2) For generating a new pixd: | |
| 668 * pixd = pixBlendGrayInverse(NULL, pixs1, pixs2, ...) | |
| 669 * (3) Clipping of pixs2 to pixs1 is done in the inner pixel loop. | |
| 670 * (4) If pixs1 has a colormap, it is removed; otherwise if pixs1 | |
| 671 * has depth < 8, it is unpacked to generate a 8 bpp pix. | |
| 672 * (5) This is a no-nonsense blender. It changes the src1 pixel except | |
| 673 * when the src1 pixel is midlevel gray. Use fract == 1 for the most | |
| 674 * aggressive blending, where, if the gray pixel in pixs2 is 0, | |
| 675 * we get a complete inversion of the color of the src pixel in pixs1. | |
| 676 * (6) The basic logic is that each component transforms by: | |
| 677 d --> c * d + (1 - c ) * (f * (1 - d) + d * (1 - f)) | |
| 678 * where c is the blender pixel from pixs2, | |
| 679 * f is %fract, | |
| 680 * c and d are normalized to [0...1] | |
| 681 * This has the property that for f == 0 (no blend) or c == 1 (white): | |
| 682 * d --> d | |
| 683 * For c == 0 (black) we get maximum inversion: | |
| 684 * d --> f * (1 - d) + d * (1 - f) [inversion by fraction f] | |
| 685 * </pre> | |
| 686 */ | |
| 687 PIX * | |
| 688 pixBlendGrayInverse(PIX *pixd, | |
| 689 PIX *pixs1, | |
| 690 PIX *pixs2, | |
| 691 l_int32 x, | |
| 692 l_int32 y, | |
| 693 l_float32 fract) | |
| 694 { | |
| 695 l_int32 i, j, d, wc, hc, w, h, wplc, wpld; | |
| 696 l_int32 irval, igval, ibval, cval, dval; | |
| 697 l_float32 a; | |
| 698 l_uint32 val32; | |
| 699 l_uint32 *linec, *lined, *datac, *datad; | |
| 700 PIX *pixc, *pix1, *pix2; | |
| 701 | |
| 702 if (!pixs1) | |
| 703 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); | |
| 704 if (!pixs2) | |
| 705 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); | |
| 706 if (pixGetDepth(pixs1) == 1) | |
| 707 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", __func__, pixd); | |
| 708 if (pixd == pixs1 && pixGetColormap(pixs1)) | |
| 709 return (PIX *)ERROR_PTR("can't do in-place with cmap", __func__, pixd); | |
| 710 if (pixd && (pixd != pixs1)) | |
| 711 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", __func__, pixd); | |
| 712 if (fract < 0.0 || fract > 1.0) { | |
| 713 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", __func__); | |
| 714 fract = 0.5; | |
| 715 } | |
| 716 | |
| 717 /* If pixd != NULL, we know that it is equal to pixs1 and | |
| 718 * that pixs1 does not have a colormap, so that an in-place operation | |
| 719 * can be done. Otherwise, remove colormap from pixs1 if | |
| 720 * it exists and unpack to at least 8 bpp if necessary, | |
| 721 * to do the blending on a new pix. */ | |
| 722 if (!pixd) { | |
| 723 pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); | |
| 724 if (pixGetDepth(pix1) < 8) | |
| 725 pix2 = pixConvertTo8(pix1, FALSE); | |
| 726 else | |
| 727 pix2 = pixClone(pix1); | |
| 728 pixd = pixCopy(NULL, pix2); | |
| 729 pixDestroy(&pix1); | |
| 730 pixDestroy(&pix2); | |
| 731 } | |
| 732 | |
| 733 pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */ | |
| 734 wpld = pixGetWpl(pixd); | |
| 735 datad = pixGetData(pixd); | |
| 736 pixc = pixConvertTo8(pixs2, 0); | |
| 737 pixGetDimensions(pixc, &wc, &hc, NULL); | |
| 738 datac = pixGetData(pixc); | |
| 739 wplc = pixGetWpl(pixc); | |
| 740 | |
| 741 /* Check limits for src1, in case clipping was not done */ | |
| 742 for (i = 0; i < hc; i++) { | |
| 743 if (i + y < 0 || i + y >= h) continue; | |
| 744 linec = datac + i * wplc; | |
| 745 lined = datad + (i + y) * wpld; | |
| 746 switch (d) | |
| 747 { | |
| 748 case 8: | |
| 749 for (j = 0; j < wc; j++) { | |
| 750 if (j + x < 0 || j + x >= w) continue; | |
| 751 cval = GET_DATA_BYTE(linec, j); | |
| 752 dval = GET_DATA_BYTE(lined, j + x); | |
| 753 a = (1.0 - fract) * dval + fract * (255.0 - dval); | |
| 754 dval = (l_int32)(cval * dval / 255.0 + | |
| 755 a * (255.0 - cval) / 255.0); | |
| 756 SET_DATA_BYTE(lined, j + x, dval); | |
| 757 } | |
| 758 break; | |
| 759 case 32: | |
| 760 for (j = 0; j < wc; j++) { | |
| 761 if (j + x < 0 || j + x >= w) continue; | |
| 762 cval = GET_DATA_BYTE(linec, j); | |
| 763 val32 = *(lined + j + x); | |
| 764 extractRGBValues(val32, &irval, &igval, &ibval); | |
| 765 a = (1.0 - fract) * irval + fract * (255.0 - irval); | |
| 766 irval = (l_int32)(cval * irval / 255.0 + | |
| 767 a * (255.0 - cval) / 255.0); | |
| 768 a = (1.0 - fract) * igval + fract * (255.0 - igval); | |
| 769 igval = (l_int32)(cval * igval / 255.0 + | |
| 770 a * (255.0 - cval) / 255.0); | |
| 771 a = (1.0 - fract) * ibval + fract * (255.0 - ibval); | |
| 772 ibval = (l_int32)(cval * ibval / 255.0 + | |
| 773 a * (255.0 - cval) / 255.0); | |
| 774 composeRGBPixel(irval, igval, ibval, &val32); | |
| 775 *(lined + j + x) = val32; | |
| 776 } | |
| 777 break; | |
| 778 default: | |
| 779 break; /* shouldn't happen */ | |
| 780 } | |
| 781 } | |
| 782 | |
| 783 pixDestroy(&pixc); | |
| 784 return pixd; | |
| 785 } | |
| 786 | |
| 787 | |
| 788 /*! | |
| 789 * \brief pixBlendColor() | |
| 790 * | |
| 791 * \param[in] pixd [optional] either equal to pixs1 for in-place, | |
| 792 * or NULL | |
| 793 * \param[in] pixs1 blendee; depth > 1 | |
| 794 * \param[in] pixs2 blender, any depth; typically, the area of | |
| 795 * pixs2 is smaller than pixs1 | |
| 796 * \param[in] x,y origin [UL corner] of pixs2 relative to | |
| 797 * the origin of pixs1 | |
| 798 * \param[in] fract blending fraction | |
| 799 * \param[in] transparent 1 to use transparency; 0 otherwise | |
| 800 * \param[in] transpix pixel color in pixs2 that is to be transparent | |
| 801 * \return pixd, or null on error | |
| 802 * | |
| 803 * <pre> | |
| 804 * Notes: | |
| 805 * (1) For inplace operation (pixs1 must be 32 bpp), call it this way: | |
| 806 * pixBlendColor(pixs1, pixs1, pixs2, ...) | |
| 807 * (2) For generating a new pixd: | |
| 808 * pixd = pixBlendColor(NULL, pixs1, pixs2, ...) | |
| 809 * (3) If pixs2 is not 32 bpp rgb, it is converted. | |
| 810 * (4) Clipping of pixs2 to pixs1 is done in the inner pixel loop. | |
| 811 * (5) If pixs1 has a colormap, it is removed to generate a 32 bpp pix. | |
| 812 * (6) If pixs1 has depth < 32, it is unpacked to generate a 32 bpp pix. | |
| 813 * (7) If transparent = 0, the blending fraction (fract) is | |
| 814 * applied equally to all pixels. | |
| 815 * (8) If transparent = 1, all pixels of value transpix (typically | |
| 816 * either 0 or 0xffffff00) in pixs2 are transparent in the blend. | |
| 817 * </pre> | |
| 818 */ | |
| 819 PIX * | |
| 820 pixBlendColor(PIX *pixd, | |
| 821 PIX *pixs1, | |
| 822 PIX *pixs2, | |
| 823 l_int32 x, | |
| 824 l_int32 y, | |
| 825 l_float32 fract, | |
| 826 l_int32 transparent, | |
| 827 l_uint32 transpix) | |
| 828 { | |
| 829 l_int32 i, j, wc, hc, w, h, wplc, wpld; | |
| 830 l_int32 rval, gval, bval, rcval, gcval, bcval; | |
| 831 l_uint32 cval32, val32; | |
| 832 l_uint32 *linec, *lined, *datac, *datad; | |
| 833 PIX *pixc; | |
| 834 | |
| 835 if (!pixs1) | |
| 836 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, NULL); | |
| 837 if (!pixs2) | |
| 838 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, NULL); | |
| 839 if (pixGetDepth(pixs1) == 1) | |
| 840 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", __func__, NULL); | |
| 841 if (pixd == pixs1 && pixGetDepth(pixs1) != 32) | |
| 842 return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", __func__, NULL); | |
| 843 if (pixd && (pixd != pixs1)) | |
| 844 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", __func__, NULL); | |
| 845 if (fract < 0.0 || fract > 1.0) { | |
| 846 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", __func__); | |
| 847 fract = 0.5; | |
| 848 } | |
| 849 | |
| 850 /* If pixd != null, we know that it is equal to pixs1 and | |
| 851 * that pixs1 is 32 bpp rgb, so that an in-place operation | |
| 852 * can be done. Otherwise, pixConvertTo32() will remove a | |
| 853 * colormap from pixs1 if it exists and unpack to 32 bpp | |
| 854 * (if necessary) to do the blending on a new 32 bpp Pix. */ | |
| 855 if (!pixd) | |
| 856 pixd = pixConvertTo32(pixs1); | |
| 857 pixGetDimensions(pixd, &w, &h, NULL); | |
| 858 wpld = pixGetWpl(pixd); | |
| 859 datad = pixGetData(pixd); | |
| 860 pixc = pixConvertTo32(pixs2); /* blend with 32 bpp rgb */ | |
| 861 pixGetDimensions(pixc, &wc, &hc, NULL); | |
| 862 datac = pixGetData(pixc); | |
| 863 wplc = pixGetWpl(pixc); | |
| 864 | |
| 865 /* Check limits for src1, in case clipping was not done */ | |
| 866 for (i = 0; i < hc; i++) { | |
| 867 /* | |
| 868 * The basic logic for this blending is: | |
| 869 * p --> (1 - f) * p + f * c | |
| 870 * for each color channel. c is a color component of the blender. | |
| 871 * All values are normalized to [0...1]. | |
| 872 */ | |
| 873 if (i + y < 0 || i + y >= h) continue; | |
| 874 linec = datac + i * wplc; | |
| 875 lined = datad + (i + y) * wpld; | |
| 876 for (j = 0; j < wc; j++) { | |
| 877 if (j + x < 0 || j + x >= w) continue; | |
| 878 cval32 = *(linec + j); | |
| 879 if (transparent == 0 || | |
| 880 ((cval32 & 0xffffff00) != (transpix & 0xffffff00))) { | |
| 881 val32 = *(lined + j + x); | |
| 882 extractRGBValues(cval32, &rcval, &gcval, &bcval); | |
| 883 extractRGBValues(val32, &rval, &gval, &bval); | |
| 884 rval = (l_int32)((1. - fract) * rval + fract * rcval); | |
| 885 gval = (l_int32)((1. - fract) * gval + fract * gcval); | |
| 886 bval = (l_int32)((1. - fract) * bval + fract * bcval); | |
| 887 composeRGBPixel(rval, gval, bval, &val32); | |
| 888 *(lined + j + x) = val32; | |
| 889 } | |
| 890 } | |
| 891 } | |
| 892 | |
| 893 pixDestroy(&pixc); | |
| 894 return pixd; | |
| 895 } | |
| 896 | |
| 897 | |
| 898 /* | |
| 899 * \brief pixBlendColorByChannel() | |
| 900 * | |
| 901 * \param[in] pixd [optional] either equal to pixs1 for in-place, | |
| 902 * or NULL | |
| 903 * \param[in] pixs1 blendee; depth > 1 | |
| 904 * \param[in] pixs2 blender, any depth; typically, the area of | |
| 905 * pixs2 is smaller than pixs1 | |
| 906 * \param[in] x,y origin [UL corner] of pixs2 relative to | |
| 907 * the origin of pixs1 | |
| 908 * \param[in] rfract blending fraction in red channel | |
| 909 * \param[in] gfract blending fraction in green channel | |
| 910 * \param[in] bfract blending fraction in blue channel | |
| 911 * \param[in] transparent 1 to use transparency; 0 otherwise | |
| 912 * \param[in] transpix pixel color in pixs2 that is to be transparent | |
| 913 * \return pixd if OK; pixd on error | |
| 914 * | |
| 915 * <pre> | |
| 916 * Notes: | |
| 917 * (1) This generalizes pixBlendColor() in two ways: | |
| 918 * (a) The mixing fraction is specified per channel. | |
| 919 * (b) The mixing fraction may be < 0 or > 1, in which case, | |
| 920 * the min or max of two images are taken, respectively. | |
| 921 * (2) Specifically, | |
| 922 * for p = pixs1[i], c = pixs2[i], f = fract[i], i = 1, 2, 3: | |
| 923 * f < 0.0: p --> min(p, c) | |
| 924 * 0.0 <= f <= 1.0: p --> (1 - f) * p + f * c | |
| 925 * f > 1.0: p --> max(a, c) | |
| 926 * Special cases: | |
| 927 * f = 0: p --> p | |
| 928 * f = 1: p --> c | |
| 929 * (3) See usage notes in pixBlendColor() | |
| 930 * (4) pixBlendColor() would be equivalent to | |
| 931 * pixBlendColorChannel(..., fract, fract, fract, ...); | |
| 932 * at a small cost of efficiency. | |
| 933 * </pre> | |
| 934 */ | |
| 935 PIX * | |
| 936 pixBlendColorByChannel(PIX *pixd, | |
| 937 PIX *pixs1, | |
| 938 PIX *pixs2, | |
| 939 l_int32 x, | |
| 940 l_int32 y, | |
| 941 l_float32 rfract, | |
| 942 l_float32 gfract, | |
| 943 l_float32 bfract, | |
| 944 l_int32 transparent, | |
| 945 l_uint32 transpix) | |
| 946 { | |
| 947 l_int32 i, j, wc, hc, w, h, wplc, wpld; | |
| 948 l_int32 rval, gval, bval, rcval, gcval, bcval; | |
| 949 l_uint32 cval32, val32; | |
| 950 l_uint32 *linec, *lined, *datac, *datad; | |
| 951 PIX *pixc; | |
| 952 | |
| 953 if (!pixs1) | |
| 954 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); | |
| 955 if (!pixs2) | |
| 956 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); | |
| 957 if (pixGetDepth(pixs1) == 1) | |
| 958 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", __func__, pixd); | |
| 959 if (pixd == pixs1 && pixGetDepth(pixs1) != 32) | |
| 960 return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", __func__, pixd); | |
| 961 if (pixd && (pixd != pixs1)) | |
| 962 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", __func__, pixd); | |
| 963 | |
| 964 /* If pixd != NULL, we know that it is equal to pixs1 and | |
| 965 * that pixs1 is 32 bpp rgb, so that an in-place operation | |
| 966 * can be done. Otherwise, pixConvertTo32() will remove a | |
| 967 * colormap from pixs1 if it exists and unpack to 32 bpp | |
| 968 * (if necessary) to do the blending on a new 32 bpp Pix. */ | |
| 969 if (!pixd) | |
| 970 pixd = pixConvertTo32(pixs1); | |
| 971 pixGetDimensions(pixd, &w, &h, NULL); | |
| 972 wpld = pixGetWpl(pixd); | |
| 973 datad = pixGetData(pixd); | |
| 974 pixc = pixConvertTo32(pixs2); | |
| 975 pixGetDimensions(pixc, &wc, &hc, NULL); | |
| 976 datac = pixGetData(pixc); | |
| 977 wplc = pixGetWpl(pixc); | |
| 978 | |
| 979 /* Check limits for src1, in case clipping was not done */ | |
| 980 for (i = 0; i < hc; i++) { | |
| 981 if (i + y < 0 || i + y >= h) continue; | |
| 982 linec = datac + i * wplc; | |
| 983 lined = datad + (i + y) * wpld; | |
| 984 for (j = 0; j < wc; j++) { | |
| 985 if (j + x < 0 || j + x >= w) continue; | |
| 986 cval32 = *(linec + j); | |
| 987 if (transparent == 0 || | |
| 988 ((cval32 & 0xffffff00) != (transpix & 0xffffff00))) { | |
| 989 val32 = *(lined + j + x); | |
| 990 extractRGBValues(cval32, &rcval, &gcval, &bcval); | |
| 991 extractRGBValues(val32, &rval, &gval, &bval); | |
| 992 rval = blendComponents(rval, rcval, rfract); | |
| 993 gval = blendComponents(gval, gcval, gfract); | |
| 994 bval = blendComponents(bval, bcval, bfract); | |
| 995 composeRGBPixel(rval, gval, bval, &val32); | |
| 996 *(lined + j + x) = val32; | |
| 997 } | |
| 998 } | |
| 999 } | |
| 1000 | |
| 1001 pixDestroy(&pixc); | |
| 1002 return pixd; | |
| 1003 } | |
| 1004 | |
| 1005 | |
| 1006 static l_int32 | |
| 1007 blendComponents(l_int32 a, | |
| 1008 l_int32 b, | |
| 1009 l_float32 fract) | |
| 1010 { | |
| 1011 if (fract < 0.) | |
| 1012 return ((a < b) ? a : b); | |
| 1013 if (fract > 1.) | |
| 1014 return ((a > b) ? a : b); | |
| 1015 return (l_int32)((1. - fract) * a + fract * b); | |
| 1016 } | |
| 1017 | |
| 1018 | |
| 1019 /*! | |
| 1020 * \brief pixBlendGrayAdapt() | |
| 1021 * | |
| 1022 * \param[in] pixd [optional] either equal to pixs1 for in-place, or NULL | |
| 1023 * \param[in] pixs1 blendee; depth > 1 | |
| 1024 * \param[in] pixs2 blender, any depth; typically, the area of | |
| 1025 * pixs2 is smaller than pixs1 | |
| 1026 * \param[in] x,y origin [UL corner] of pixs2 relative to | |
| 1027 * the origin of pixs1; can be < 0 | |
| 1028 * \param[in] fract blending fraction | |
| 1029 * \param[in] shift >= 0 but <= 128: shift of zero blend value from | |
| 1030 * median source; use -1 for default value; | |
| 1031 * \return pixd if OK; pixs1 on error | |
| 1032 * | |
| 1033 * <pre> | |
| 1034 * Notes: | |
| 1035 * (1) For inplace operation (pixs1 not cmapped), call it this way: | |
| 1036 * pixBlendGrayAdapt(pixs1, pixs1, pixs2, ...) | |
| 1037 * For generating a new pixd: | |
| 1038 * pixd = pixBlendGrayAdapt(NULL, pixs1, pixs2, ...) | |
| 1039 * (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop. | |
| 1040 * (3) If pixs1 has a colormap, it is removed. | |
| 1041 * (4) If pixs1 has depth < 8, it is unpacked to generate a 8 bpp pix. | |
| 1042 * (5) This does a blend with inverse. Whereas in pixGlendGray(), the | |
| 1043 * zero blend point is where the blendee pixel is 128, here | |
| 1044 * the zero blend point is found adaptively, with respect to the | |
| 1045 * median of the blendee region. If the median is < 128, | |
| 1046 * the zero blend point is found from | |
| 1047 * median + shift. | |
| 1048 * Otherwise, if the median >= 128, the zero blend point is | |
| 1049 * median - shift. | |
| 1050 * The purpose of shifting the zero blend point away from the | |
| 1051 * median is to prevent a situation in pixBlendGray() where | |
| 1052 * the median is 128 and the blender is not visible. | |
| 1053 * The default value of shift is 64. | |
| 1054 * (6) After processing pixs1, it is either 8 bpp or 32 bpp: | |
| 1055 * ~ if 8 bpp, the fraction of pixs2 is mixed with pixs1. | |
| 1056 * ~ if 32 bpp, each component of pixs1 is mixed with | |
| 1057 * the same fraction of pixs2. | |
| 1058 * (7) The darker the blender, the more it mixes with the blendee. | |
| 1059 * A blender value of 0 has maximum mixing; a value of 255 | |
| 1060 * has no mixing and hence is transparent. | |
| 1061 * </pre> | |
| 1062 */ | |
| 1063 PIX * | |
| 1064 pixBlendGrayAdapt(PIX *pixd, | |
| 1065 PIX *pixs1, | |
| 1066 PIX *pixs2, | |
| 1067 l_int32 x, | |
| 1068 l_int32 y, | |
| 1069 l_float32 fract, | |
| 1070 l_int32 shift) | |
| 1071 { | |
| 1072 l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta, overlap; | |
| 1073 l_int32 rval, gval, bval, cval, dval, mval, median, pivot; | |
| 1074 l_uint32 val32; | |
| 1075 l_uint32 *linec, *lined, *datac, *datad; | |
| 1076 l_float32 fmedian, factor; | |
| 1077 BOX *box, *boxt; | |
| 1078 PIX *pixc, *pix1, *pix2; | |
| 1079 | |
| 1080 if (!pixs1) | |
| 1081 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); | |
| 1082 if (!pixs2) | |
| 1083 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); | |
| 1084 if (pixGetDepth(pixs1) == 1) | |
| 1085 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", __func__, pixd); | |
| 1086 if (pixd == pixs1 && pixGetColormap(pixs1)) | |
| 1087 return (PIX *)ERROR_PTR("can't do in-place with cmap", __func__, pixd); | |
| 1088 if (pixd && (pixd != pixs1)) | |
| 1089 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", __func__, pixd); | |
| 1090 if (fract < 0.0 || fract > 1.0) { | |
| 1091 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", __func__); | |
| 1092 fract = 0.5; | |
| 1093 } | |
| 1094 if (shift == -1) shift = 64; /* default value */ | |
| 1095 if (shift < 0 || shift > 127) { | |
| 1096 L_WARNING("invalid shift; setting to 64\n", __func__); | |
| 1097 shift = 64; | |
| 1098 } | |
| 1099 | |
| 1100 /* Test for overlap */ | |
| 1101 pixGetDimensions(pixs1, &w, &h, NULL); | |
| 1102 pixGetDimensions(pixs2, &wc, &hc, NULL); | |
| 1103 box = boxCreate(x, y, wc, hc); | |
| 1104 boxt = boxCreate(0, 0, w, h); | |
| 1105 boxIntersects(box, boxt, &overlap); | |
| 1106 boxDestroy(&boxt); | |
| 1107 if (!overlap) { | |
| 1108 boxDestroy(&box); | |
| 1109 return (PIX *)ERROR_PTR("no image overlap", __func__, pixd); | |
| 1110 } | |
| 1111 | |
| 1112 /* If pixd != NULL, we know that it is equal to pixs1 and | |
| 1113 * that pixs1 does not have a colormap, so that an in-place operation | |
| 1114 * can be done. Otherwise, remove colormap from pixs1 if | |
| 1115 * it exists and unpack to at least 8 bpp if necessary, | |
| 1116 * to do the blending on a new pix. */ | |
| 1117 if (!pixd) { | |
| 1118 pix1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); | |
| 1119 if (pixGetDepth(pix1) < 8) | |
| 1120 pix2 = pixConvertTo8(pix1, FALSE); | |
| 1121 else | |
| 1122 pix2 = pixClone(pix1); | |
| 1123 pixd = pixCopy(NULL, pix2); | |
| 1124 pixDestroy(&pix1); | |
| 1125 pixDestroy(&pix2); | |
| 1126 } | |
| 1127 | |
| 1128 /* Get the median value in the region of blending */ | |
| 1129 pix1 = pixClipRectangle(pixd, box, NULL); | |
| 1130 pix2 = pixConvertTo8(pix1, 0); | |
| 1131 pixGetRankValueMasked(pix2, NULL, 0, 0, 1, 0.5, &fmedian, NULL); | |
| 1132 median = (l_int32)(fmedian + 0.5); | |
| 1133 if (median < 128) | |
| 1134 pivot = median + shift; | |
| 1135 else | |
| 1136 pivot = median - shift; | |
| 1137 pixDestroy(&pix1); | |
| 1138 pixDestroy(&pix2); | |
| 1139 boxDestroy(&box); | |
| 1140 | |
| 1141 /* Process over src2; clip to src1. */ | |
| 1142 d = pixGetDepth(pixd); | |
| 1143 wpld = pixGetWpl(pixd); | |
| 1144 datad = pixGetData(pixd); | |
| 1145 pixc = pixConvertTo8(pixs2, 0); | |
| 1146 datac = pixGetData(pixc); | |
| 1147 wplc = pixGetWpl(pixc); | |
| 1148 for (i = 0; i < hc; i++) { | |
| 1149 if (i + y < 0 || i + y >= h) continue; | |
| 1150 linec = datac + i * wplc; | |
| 1151 lined = datad + (i + y) * wpld; | |
| 1152 switch (d) | |
| 1153 { | |
| 1154 case 8: | |
| 1155 /* | |
| 1156 * For 8 bpp, the dest pix is shifted by an amount | |
| 1157 * proportional to the distance from the pivot value, | |
| 1158 * and to the darkness of src2. In no situation will it | |
| 1159 * pass the pivot value in intensity. | |
| 1160 * The basic logic is: | |
| 1161 * d --> d + f * (np - d) * (1 - c) | |
| 1162 * where np, d and c are normalized pixel values for | |
| 1163 * the pivot, src1 and src2, respectively, with normalization | |
| 1164 * to 255. | |
| 1165 */ | |
| 1166 for (j = 0; j < wc; j++) { | |
| 1167 if (j + x < 0 || j + x >= w) continue; | |
| 1168 dval = GET_DATA_BYTE(lined, j + x); | |
| 1169 cval = GET_DATA_BYTE(linec, j); | |
| 1170 delta = (pivot - dval) * (255 - cval) / 256; | |
| 1171 dval += (l_int32)(fract * delta + 0.5); | |
| 1172 SET_DATA_BYTE(lined, j + x, dval); | |
| 1173 } | |
| 1174 break; | |
| 1175 case 32: | |
| 1176 /* | |
| 1177 * For 32 bpp, the dest pix is shifted by an amount | |
| 1178 * proportional to the max component distance from the | |
| 1179 * pivot value, and to the darkness of src2. Each component | |
| 1180 * is shifted by the same fraction, either up or down, | |
| 1181 * depending on the shift direction (which is toward the | |
| 1182 * pivot). The basic logic for the red component is: | |
| 1183 * r --> r + f * (np - m) * (1 - c) * (r / m) | |
| 1184 * where np, r, m and c are normalized pixel values for | |
| 1185 * the pivot, the r component of src1, the max component | |
| 1186 * of src1, and src2, respectively, again with normalization | |
| 1187 * to 255. Likewise for the green and blue components. | |
| 1188 */ | |
| 1189 for (j = 0; j < wc; j++) { | |
| 1190 if (j + x < 0 || j + x >= w) continue; | |
| 1191 cval = GET_DATA_BYTE(linec, j); | |
| 1192 val32 = *(lined + j + x); | |
| 1193 extractRGBValues(val32, &rval, &gval, &bval); | |
| 1194 mval = L_MAX(rval, gval); | |
| 1195 mval = L_MAX(mval, bval); | |
| 1196 mval = L_MAX(mval, 1); | |
| 1197 delta = (pivot - mval) * (255 - cval) / 256; | |
| 1198 factor = fract * delta / mval; | |
| 1199 rval += (l_int32)(factor * rval + 0.5); | |
| 1200 gval += (l_int32)(factor * gval + 0.5); | |
| 1201 bval += (l_int32)(factor * bval + 0.5); | |
| 1202 composeRGBPixel(rval, gval, bval, &val32); | |
| 1203 *(lined + j + x) = val32; | |
| 1204 } | |
| 1205 break; | |
| 1206 default: | |
| 1207 break; /* shouldn't happen */ | |
| 1208 } | |
| 1209 } | |
| 1210 | |
| 1211 pixDestroy(&pixc); | |
| 1212 return pixd; | |
| 1213 } | |
| 1214 | |
| 1215 | |
| 1216 /*! | |
| 1217 * \brief pixFadeWithGray() | |
| 1218 * | |
| 1219 * \param[in] pixs colormapped or 8 bpp or 32 bpp | |
| 1220 * \param[in] pixb 8 bpp blender | |
| 1221 * \param[in] factor multiplicative factor to apply to blender value | |
| 1222 * \param[in] type L_BLEND_TO_WHITE, L_BLEND_TO_BLACK | |
| 1223 * \return pixd, or null on error | |
| 1224 * | |
| 1225 * <pre> | |
| 1226 * Notes: | |
| 1227 * (1) This function combines two pix aligned to the UL corner; they | |
| 1228 * need not be the same size. | |
| 1229 * (2) Each pixel in pixb is multiplied by 'factor' divided by 255, and | |
| 1230 * clipped to the range [0 ... 1]. This gives the fade fraction | |
| 1231 * to be applied to pixs. Fade either to white (L_BLEND_TO_WHITE) | |
| 1232 * or to black (L_BLEND_TO_BLACK). | |
| 1233 * </pre> | |
| 1234 */ | |
| 1235 PIX * | |
| 1236 pixFadeWithGray(PIX *pixs, | |
| 1237 PIX *pixb, | |
| 1238 l_float32 factor, | |
| 1239 l_int32 type) | |
| 1240 { | |
| 1241 l_int32 i, j, w, h, d, wb, hb, db, wd, hd, wplb, wpld; | |
| 1242 l_int32 valb, vald, nvald, rval, gval, bval, nrval, ngval, nbval; | |
| 1243 l_float32 nfactor, fract; | |
| 1244 l_uint32 val32, nval32; | |
| 1245 l_uint32 *lined, *datad, *lineb, *datab; | |
| 1246 PIX *pixd; | |
| 1247 | |
| 1248 if (!pixs) | |
| 1249 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1250 if (!pixb) | |
| 1251 return (PIX *)ERROR_PTR("pixb not defined", __func__, NULL); | |
| 1252 if (pixGetDepth(pixs) == 1) | |
| 1253 return (PIX *)ERROR_PTR("pixs is 1 bpp", __func__, NULL); | |
| 1254 pixGetDimensions(pixb, &wb, &hb, &db); | |
| 1255 if (db != 8) | |
| 1256 return (PIX *)ERROR_PTR("pixb not 8 bpp", __func__, NULL); | |
| 1257 if (factor < 0.0 || factor > 255.0) | |
| 1258 return (PIX *)ERROR_PTR("factor not in [0.0...255.0]", __func__, NULL); | |
| 1259 if (type != L_BLEND_TO_WHITE && type != L_BLEND_TO_BLACK) | |
| 1260 return (PIX *)ERROR_PTR("invalid fade type", __func__, NULL); | |
| 1261 | |
| 1262 /* Remove colormap if it exists; otherwise copy */ | |
| 1263 pixd = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY); | |
| 1264 pixGetDimensions(pixd, &wd, &hd, &d); | |
| 1265 w = L_MIN(wb, wd); | |
| 1266 h = L_MIN(hb, hd); | |
| 1267 datad = pixGetData(pixd); | |
| 1268 wpld = pixGetWpl(pixd); | |
| 1269 datab = pixGetData(pixb); | |
| 1270 wplb = pixGetWpl(pixb); | |
| 1271 | |
| 1272 /* The basic logic for this blending is, for each component p of pixs: | |
| 1273 * fade-to-white: p --> p + (f * c) * (1 - p) | |
| 1274 * fade-to-black: p --> p - (f * c) * p | |
| 1275 * with c being the 8 bpp blender pixel of pixb, and with both | |
| 1276 * p and c normalized to [0...1]. */ | |
| 1277 nfactor = factor / 255.; | |
| 1278 for (i = 0; i < h; i++) { | |
| 1279 lineb = datab + i * wplb; | |
| 1280 lined = datad + i * wpld; | |
| 1281 for (j = 0; j < w; j++) { | |
| 1282 valb = GET_DATA_BYTE(lineb, j); | |
| 1283 fract = nfactor * (l_float32)valb; | |
| 1284 fract = L_MIN(fract, 1.0); | |
| 1285 if (d == 8) { | |
| 1286 vald = GET_DATA_BYTE(lined, j); | |
| 1287 if (type == L_BLEND_TO_WHITE) | |
| 1288 nvald = vald + (l_int32)(fract * (255. - (l_float32)vald)); | |
| 1289 else /* L_BLEND_TO_BLACK */ | |
| 1290 nvald = vald - (l_int32)(fract * (l_float32)vald); | |
| 1291 SET_DATA_BYTE(lined, j, nvald); | |
| 1292 } else { /* d == 32 */ | |
| 1293 val32 = lined[j]; | |
| 1294 extractRGBValues(val32, &rval, &gval, &bval); | |
| 1295 if (type == L_BLEND_TO_WHITE) { | |
| 1296 nrval = rval + (l_int32)(fract * (255. - (l_float32)rval)); | |
| 1297 ngval = gval + (l_int32)(fract * (255. - (l_float32)gval)); | |
| 1298 nbval = bval + (l_int32)(fract * (255. - (l_float32)bval)); | |
| 1299 } else { | |
| 1300 nrval = rval - (l_int32)(fract * (l_float32)rval); | |
| 1301 ngval = gval - (l_int32)(fract * (l_float32)gval); | |
| 1302 nbval = bval - (l_int32)(fract * (l_float32)bval); | |
| 1303 } | |
| 1304 composeRGBPixel(nrval, ngval, nbval, &nval32); | |
| 1305 lined[j] = nval32; | |
| 1306 } | |
| 1307 } | |
| 1308 } | |
| 1309 | |
| 1310 return pixd; | |
| 1311 } | |
| 1312 | |
| 1313 | |
| 1314 /* | |
| 1315 * \brief pixBlendHardLight() | |
| 1316 * | |
| 1317 * \param[in] pixd either NULL or equal to pixs1 for in-place | |
| 1318 * \param[in] pixs1 blendee; depth > 1, may be cmapped | |
| 1319 * \param[in] pixs2 blender, 8 or 32 bpp; may be colormapped; | |
| 1320 * typ. smaller in size than pixs1 | |
| 1321 * \param[in] x,y origin [UL corner] of pixs2 relative to | |
| 1322 * the origin of pixs1 | |
| 1323 * \param[in] fract blending fraction, or 'opacity factor' | |
| 1324 * \return pixd if OK; pixs1 on error | |
| 1325 * | |
| 1326 * <pre> | |
| 1327 * Notes: | |
| 1328 * (1) pixs2 must be 8 or 32 bpp; either may have a colormap. | |
| 1329 * (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop. | |
| 1330 * (3) Only call in-place if pixs1 is not colormapped. | |
| 1331 * (4) If pixs1 has a colormap, it is removed to generate either an | |
| 1332 * 8 or 32 bpp pix, depending on the colormap. | |
| 1333 * (5) For inplace operation, call it this way: | |
| 1334 * pixBlendHardLight(pixs1, pixs1, pixs2, ...) | |
| 1335 * (6) For generating a new pixd: | |
| 1336 * pixd = pixBlendHardLight(NULL, pixs1, pixs2, ...) | |
| 1337 * (7) This is a generalization of the usual hard light blending, | |
| 1338 * where fract == 1.0. | |
| 1339 * (8) "Overlay" blending is the same as hard light blending, with | |
| 1340 * fract == 1.0, except that the components are switched | |
| 1341 * in the test. (Note that the result is symmetric in the | |
| 1342 * two components.) | |
| 1343 * (9) See, e.g.: | |
| 1344 * http://www.pegtop.net/delphi/articles/blendmodes/hardlight.htm | |
| 1345 * http://www.digitalartform.com/imageArithmetic.htm | |
| 1346 * (10) This function was built by Paco Galanes. | |
| 1347 * </pre> | |
| 1348 */ | |
| 1349 PIX * | |
| 1350 pixBlendHardLight(PIX *pixd, | |
| 1351 PIX *pixs1, | |
| 1352 PIX *pixs2, | |
| 1353 l_int32 x, | |
| 1354 l_int32 y, | |
| 1355 l_float32 fract) | |
| 1356 { | |
| 1357 l_int32 i, j, w, h, d, wc, hc, dc, wplc, wpld; | |
| 1358 l_int32 cval, dval, rcval, gcval, bcval, rdval, gdval, bdval; | |
| 1359 l_uint32 cval32, dval32; | |
| 1360 l_uint32 *linec, *lined, *datac, *datad; | |
| 1361 PIX *pixc, *pixt; | |
| 1362 | |
| 1363 if (!pixs1) | |
| 1364 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd); | |
| 1365 if (!pixs2) | |
| 1366 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd); | |
| 1367 pixGetDimensions(pixs1, &w, &h, &d); | |
| 1368 pixGetDimensions(pixs2, &wc, &hc, &dc); | |
| 1369 if (d == 1) | |
| 1370 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", __func__, pixd); | |
| 1371 if (dc != 8 && dc != 32) | |
| 1372 return (PIX *)ERROR_PTR("pixs2 not 8 or 32 bpp", __func__, pixd); | |
| 1373 if (pixd && (pixd != pixs1)) | |
| 1374 return (PIX *)ERROR_PTR("inplace and pixd != pixs1", __func__, pixd); | |
| 1375 if (pixd == pixs1 && pixGetColormap(pixs1)) | |
| 1376 return (PIX *)ERROR_PTR("inplace and pixs1 cmapped", __func__, pixd); | |
| 1377 if (pixd && d != 8 && d != 32) | |
| 1378 return (PIX *)ERROR_PTR("inplace and not 8 or 32 bpp", __func__, pixd); | |
| 1379 | |
| 1380 if (fract < 0.0 || fract > 1.0) { | |
| 1381 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", __func__); | |
| 1382 fract = 0.5; | |
| 1383 } | |
| 1384 | |
| 1385 /* If pixs2 has a colormap, remove it */ | |
| 1386 pixc = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); /* clone ok */ | |
| 1387 dc = pixGetDepth(pixc); | |
| 1388 | |
| 1389 /* There are 4 cases: | |
| 1390 * * pixs1 has or doesn't have a colormap | |
| 1391 * * pixc is either 8 or 32 bpp | |
| 1392 * In all situations, if pixs has a colormap it must be removed, | |
| 1393 * and pixd must have a depth that is equal to or greater than pixc. */ | |
| 1394 if (dc == 32) { | |
| 1395 if (pixGetColormap(pixs1)) { /* pixd == NULL */ | |
| 1396 pixd = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR); | |
| 1397 } else { | |
| 1398 if (!pixd) { | |
| 1399 pixd = pixConvertTo32(pixs1); | |
| 1400 } else { | |
| 1401 pixt = pixConvertTo32(pixs1); | |
| 1402 pixCopy(pixd, pixt); | |
| 1403 pixDestroy(&pixt); | |
| 1404 } | |
| 1405 } | |
| 1406 d = 32; | |
| 1407 } else { /* dc == 8 */ | |
| 1408 if (pixGetColormap(pixs1)) /* pixd == NULL */ | |
| 1409 pixd = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); | |
| 1410 else | |
| 1411 pixd = pixCopy(pixd, pixs1); | |
| 1412 d = pixGetDepth(pixd); | |
| 1413 } | |
| 1414 | |
| 1415 if (!(d == 8 && dc == 8) && /* 3 cases only */ | |
| 1416 !(d == 32 && dc == 8) && | |
| 1417 !(d == 32 && dc == 32)) { | |
| 1418 pixDestroy(&pixc); | |
| 1419 return (PIX *)ERROR_PTR("bad! -- invalid depth combo!", __func__, pixd); | |
| 1420 } | |
| 1421 | |
| 1422 wpld = pixGetWpl(pixd); | |
| 1423 datad = pixGetData(pixd); | |
| 1424 datac = pixGetData(pixc); | |
| 1425 wplc = pixGetWpl(pixc); | |
| 1426 for (i = 0; i < hc; i++) { | |
| 1427 if (i + y < 0 || i + y >= h) continue; | |
| 1428 linec = datac + i * wplc; | |
| 1429 lined = datad + (i + y) * wpld; | |
| 1430 for (j = 0; j < wc; j++) { | |
| 1431 if (j + x < 0 || j + x >= w) continue; | |
| 1432 if (d == 8 && dc == 8) { | |
| 1433 dval = GET_DATA_BYTE(lined, x + j); | |
| 1434 cval = GET_DATA_BYTE(linec, j); | |
| 1435 dval = blendHardLightComponents(dval, cval, fract); | |
| 1436 SET_DATA_BYTE(lined, x + j, dval); | |
| 1437 } else if (d == 32 && dc == 8) { | |
| 1438 dval32 = *(lined + x + j); | |
| 1439 extractRGBValues(dval32, &rdval, &gdval, &bdval); | |
| 1440 cval = GET_DATA_BYTE(linec, j); | |
| 1441 rdval = blendHardLightComponents(rdval, cval, fract); | |
| 1442 gdval = blendHardLightComponents(gdval, cval, fract); | |
| 1443 bdval = blendHardLightComponents(bdval, cval, fract); | |
| 1444 composeRGBPixel(rdval, gdval, bdval, &dval32); | |
| 1445 *(lined + x + j) = dval32; | |
| 1446 } else if (d == 32 && dc == 32) { | |
| 1447 dval32 = *(lined + x + j); | |
| 1448 extractRGBValues(dval32, &rdval, &gdval, &bdval); | |
| 1449 cval32 = *(linec + j); | |
| 1450 extractRGBValues(cval32, &rcval, &gcval, &bcval); | |
| 1451 rdval = blendHardLightComponents(rdval, rcval, fract); | |
| 1452 gdval = blendHardLightComponents(gdval, gcval, fract); | |
| 1453 bdval = blendHardLightComponents(bdval, bcval, fract); | |
| 1454 composeRGBPixel(rdval, gdval, bdval, &dval32); | |
| 1455 *(lined + x + j) = dval32; | |
| 1456 } | |
| 1457 } | |
| 1458 } | |
| 1459 | |
| 1460 pixDestroy(&pixc); | |
| 1461 return pixd; | |
| 1462 } | |
| 1463 | |
| 1464 | |
| 1465 /* | |
| 1466 * \brief blendHardLightComponents() | |
| 1467 * | |
| 1468 * \param[in] a 8 bpp blendee component | |
| 1469 * \param[in] b 8 bpp blender component | |
| 1470 * \param[in] fract fraction of blending; use 1.0 for usual definition | |
| 1471 * \return blended 8 bpp component | |
| 1472 * | |
| 1473 * <pre> | |
| 1474 * Notes: | |
| 1475 * | |
| 1476 * The basic logic for this blending is: | |
| 1477 * b < 0.5: | |
| 1478 * a --> 2 * a * (0.5 - f * (0.5 - b)) | |
| 1479 * b >= 0.5: | |
| 1480 * a --> 1 - 2 * (1 - a) * (1 - (0.5 - f * (0.5 - b))) | |
| 1481 * | |
| 1482 * In the limit that f == 1 (standard hardlight blending): | |
| 1483 * b < 0.5: a --> 2 * a * b | |
| 1484 * or | |
| 1485 * a --> a - a * (1 - 2 * b) | |
| 1486 * b >= 0.5: a --> 1 - 2 * (1 - a) * (1 - b) | |
| 1487 * or | |
| 1488 * a --> a + (1 - a) * (2 * b - 1) | |
| 1489 * | |
| 1490 * You can see that for standard hardlight blending: | |
| 1491 * b < 0.5: a is pushed linearly with b down to 0 | |
| 1492 * b >= 0.5: a is pushed linearly with b up to 1 | |
| 1493 * a is unchanged if b = 0.5 | |
| 1494 * | |
| 1495 * Our opacity factor f reduces the deviation of b from 0.5: | |
| 1496 * f == 0: b --> 0.5, so no blending occurs | |
| 1497 * f == 1: b --> b, so we get full conventional blending | |
| 1498 * | |
| 1499 * There is a variant of hardlight blending called "softlight" blending: | |
| 1500 * (e.g., http://jswidget.com/blog/tag/hard-light/) | |
| 1501 * b < 0.5: | |
| 1502 * a --> a - a * (0.5 - b) * (1 - Abs(2 * a - 1)) | |
| 1503 * b >= 0.5: | |
| 1504 * a --> a + (1 - a) * (b - 0.5) * (1 - Abs(2 * a - 1)) | |
| 1505 * which limits the amount that 'a' can be moved to a maximum of | |
| 1506 * halfway toward 0 or 1, and further reduces it as 'a' moves | |
| 1507 * away from 0.5. | |
| 1508 * As you can see, there are a nearly infinite number of different | |
| 1509 * blending formulas that can be conjured up. | |
| 1510 * </pre> | |
| 1511 */ | |
| 1512 static l_int32 blendHardLightComponents(l_int32 a, | |
| 1513 l_int32 b, | |
| 1514 l_float32 fract) | |
| 1515 { | |
| 1516 if (b < 0x80) { | |
| 1517 b = 0x80 - (l_int32)(fract * (0x80 - b)); | |
| 1518 return (a * b) >> 7; | |
| 1519 } else { | |
| 1520 b = 0x80 + (l_int32)(fract * (b - 0x80)); | |
| 1521 return 0xff - (((0xff - b) * (0xff - a)) >> 7); | |
| 1522 } | |
| 1523 } | |
| 1524 | |
| 1525 | |
| 1526 /*-------------------------------------------------------------* | |
| 1527 * Blending two colormapped images * | |
| 1528 *-------------------------------------------------------------*/ | |
| 1529 /*! | |
| 1530 * \brief pixBlendCmap() | |
| 1531 * | |
| 1532 * \param[in] pixs 2, 4 or 8 bpp, with colormap | |
| 1533 * \param[in] pixb colormapped blender | |
| 1534 * \param[in] x, y UL corner of blender relative to pixs | |
| 1535 * \param[in] sindex colormap index of pixels in pixs to be changed | |
| 1536 * \return 0 if OK, 1 on error | |
| 1537 * | |
| 1538 * <pre> | |
| 1539 * Notes: | |
| 1540 * (1) This function combines two colormaps, and replaces the pixels | |
| 1541 * in pixs that have a specified color value with those in pixb. | |
| 1542 * (2) sindex must be in the existing colormap; otherwise an | |
| 1543 * error is returned. In use, sindex will typically be the index | |
| 1544 * for white (255, 255, 255). | |
| 1545 * (3) Blender colors that already exist in the colormap are used; | |
| 1546 * others are added. If any blender colors cannot be | |
| 1547 * stored in the colormap, an error is returned. | |
| 1548 * (4) In the implementation, a mapping is generated from each | |
| 1549 * original blender colormap index to the corresponding index | |
| 1550 * in the expanded colormap for pixs. Then for each pixel in | |
| 1551 * pixs with value sindex, and which is covered by a blender pixel, | |
| 1552 * the new index corresponding to the blender pixel is substituted | |
| 1553 * for sindex. | |
| 1554 * </pre> | |
| 1555 */ | |
| 1556 l_ok | |
| 1557 pixBlendCmap(PIX *pixs, | |
| 1558 PIX *pixb, | |
| 1559 l_int32 x, | |
| 1560 l_int32 y, | |
| 1561 l_int32 sindex) | |
| 1562 { | |
| 1563 l_int32 rval, gval, bval; | |
| 1564 l_int32 i, j, w, h, d, ncb, wb, hb, wpls; | |
| 1565 l_int32 index, val, nadded; | |
| 1566 l_int32 lut[256]; | |
| 1567 l_uint32 pval; | |
| 1568 l_uint32 *lines, *datas; | |
| 1569 PIXCMAP *cmaps, *cmapb, *cmapsc; | |
| 1570 | |
| 1571 if (!pixs) | |
| 1572 return ERROR_INT("pixs not defined", __func__, 1); | |
| 1573 if (!pixb) | |
| 1574 return ERROR_INT("pixb not defined", __func__, 1); | |
| 1575 if ((cmaps = pixGetColormap(pixs)) == NULL) | |
| 1576 return ERROR_INT("no colormap in pixs", __func__, 1); | |
| 1577 if ((cmapb = pixGetColormap(pixb)) == NULL) | |
| 1578 return ERROR_INT("no colormap in pixb", __func__, 1); | |
| 1579 ncb = pixcmapGetCount(cmapb); | |
| 1580 | |
| 1581 pixGetDimensions(pixs, &w, &h, &d); | |
| 1582 if (d != 2 && d != 4 && d != 8) | |
| 1583 return ERROR_INT("depth not in {2,4,8}", __func__, 1); | |
| 1584 | |
| 1585 /* Make a copy of cmaps; we'll add to this if necessary | |
| 1586 * and substitute at the end if we found there was enough room | |
| 1587 * to hold all the new colors. */ | |
| 1588 cmapsc = pixcmapCopy(cmaps); | |
| 1589 | |
| 1590 /* Add new colors if necessary; get mapping array between | |
| 1591 * cmaps and cmapb. */ | |
| 1592 for (i = 0, nadded = 0; i < ncb; i++) { | |
| 1593 pixcmapGetColor(cmapb, i, &rval, &gval, &bval); | |
| 1594 if (pixcmapGetIndex(cmapsc, rval, gval, bval, &index)) { /* not found */ | |
| 1595 if (pixcmapAddColor(cmapsc, rval, gval, bval)) { | |
| 1596 pixcmapDestroy(&cmapsc); | |
| 1597 return ERROR_INT("not enough room in cmaps", __func__, 1); | |
| 1598 } | |
| 1599 lut[i] = pixcmapGetCount(cmapsc) - 1; | |
| 1600 nadded++; | |
| 1601 } else { | |
| 1602 lut[i] = index; | |
| 1603 } | |
| 1604 } | |
| 1605 | |
| 1606 /* Replace cmaps if colors have been added. */ | |
| 1607 if (nadded == 0) | |
| 1608 pixcmapDestroy(&cmapsc); | |
| 1609 else | |
| 1610 pixSetColormap(pixs, cmapsc); | |
| 1611 | |
| 1612 /* Replace each pixel value sindex by mapped colormap index when | |
| 1613 * a blender pixel in pixbc overlays it. */ | |
| 1614 datas = pixGetData(pixs); | |
| 1615 wpls = pixGetWpl(pixs); | |
| 1616 pixGetDimensions(pixb, &wb, &hb, NULL); | |
| 1617 for (i = 0; i < hb; i++) { | |
| 1618 if (i + y < 0 || i + y >= h) continue; | |
| 1619 lines = datas + (y + i) * wpls; | |
| 1620 for (j = 0; j < wb; j++) { | |
| 1621 if (j + x < 0 || j + x >= w) continue; | |
| 1622 switch (d) { | |
| 1623 case 2: | |
| 1624 val = GET_DATA_DIBIT(lines, x + j); | |
| 1625 if (val == sindex) { | |
| 1626 pixGetPixel(pixb, j, i, &pval); | |
| 1627 SET_DATA_DIBIT(lines, x + j, lut[pval]); | |
| 1628 } | |
| 1629 break; | |
| 1630 case 4: | |
| 1631 val = GET_DATA_QBIT(lines, x + j); | |
| 1632 if (val == sindex) { | |
| 1633 pixGetPixel(pixb, j, i, &pval); | |
| 1634 SET_DATA_QBIT(lines, x + j, lut[pval]); | |
| 1635 } | |
| 1636 break; | |
| 1637 case 8: | |
| 1638 val = GET_DATA_BYTE(lines, x + j); | |
| 1639 if (val == sindex) { | |
| 1640 pixGetPixel(pixb, j, i, &pval); | |
| 1641 SET_DATA_BYTE(lines, x + j, lut[pval]); | |
| 1642 } | |
| 1643 break; | |
| 1644 default: | |
| 1645 return ERROR_INT("depth not in {2,4,8}", __func__, 1); | |
| 1646 } | |
| 1647 } | |
| 1648 } | |
| 1649 | |
| 1650 return 0; | |
| 1651 } | |
| 1652 | |
| 1653 | |
| 1654 /*---------------------------------------------------------------------* | |
| 1655 * Blending two images using a third * | |
| 1656 *---------------------------------------------------------------------*/ | |
| 1657 /*! | |
| 1658 * \brief pixBlendWithGrayMask() | |
| 1659 * | |
| 1660 * \param[in] pixs1 8 bpp gray, rgb, rgba or colormapped | |
| 1661 * \param[in] pixs2 8 bpp gray, rgb, rgba or colormapped | |
| 1662 * \param[in] pixg [optional] 8 bpp gray, for transparency of pixs2; | |
| 1663 * can be null | |
| 1664 * \param[in] x, y UL corner of pixs2 and pixg with respect to pixs1 | |
| 1665 * \return pixd blended image, or null on error | |
| 1666 * | |
| 1667 * <pre> | |
| 1668 * Notes: | |
| 1669 * (1) The result is 8 bpp grayscale if both pixs1 and pixs2 are | |
| 1670 * 8 bpp gray. Otherwise, the result is 32 bpp rgb. | |
| 1671 * (2) pixg is an 8 bpp transparency image, where 0 is transparent | |
| 1672 * and 255 is opaque. It determines the transparency of pixs2 | |
| 1673 * when applied over pixs1. It can be null if pixs2 is rgba, | |
| 1674 * in which case we use the alpha component of pixs2. | |
| 1675 * (3) If pixg exists, it need not be the same size as pixs2. | |
| 1676 * However, we assume their UL corners are aligned with each other, | |
| 1677 * and placed at the location (x, y) in pixs1. | |
| 1678 * (4) The pixels in pixd are a combination of those in pixs1 | |
| 1679 * and pixs2, where the amount from pixs2 is proportional to | |
| 1680 * the value of the pixel (p) in pixg, and the amount from pixs1 | |
| 1681 * is proportional to (255 - p). Thus pixg is a transparency | |
| 1682 * image (usually called an alpha blender) where each pixel | |
| 1683 * can be associated with a pixel in pixs2, and determines | |
| 1684 * the amount of the pixs2 pixel in the final result. | |
| 1685 * For example, if pixg is all 0, pixs2 is transparent and | |
| 1686 * the result in pixd is simply pixs1. | |
| 1687 * (5) A typical use is for the pixs2/pixg combination to be | |
| 1688 * a small watermark that is applied to pixs1. | |
| 1689 * </pre> | |
| 1690 */ | |
| 1691 PIX * | |
| 1692 pixBlendWithGrayMask(PIX *pixs1, | |
| 1693 PIX *pixs2, | |
| 1694 PIX *pixg, | |
| 1695 l_int32 x, | |
| 1696 l_int32 y) | |
| 1697 { | |
| 1698 l_int32 w1, h1, d1, w2, h2, d2, spp, wg, hg, wmin, hmin, wpld, wpls, wplg; | |
| 1699 l_int32 i, j, val, dval, sval; | |
| 1700 l_int32 drval, dgval, dbval, srval, sgval, sbval; | |
| 1701 l_uint32 dval32, sval32; | |
| 1702 l_uint32 *datad, *datas, *datag, *lined, *lines, *lineg; | |
| 1703 l_float32 fract; | |
| 1704 PIX *pixr1, *pixr2, *pix1, *pix2, *pixg2, *pixd; | |
| 1705 | |
| 1706 if (!pixs1) | |
| 1707 return (PIX *)ERROR_PTR("pixs1 not defined", __func__, NULL); | |
| 1708 if (!pixs2) | |
| 1709 return (PIX *)ERROR_PTR("pixs2 not defined", __func__, NULL); | |
| 1710 pixGetDimensions(pixs1, &w1, &h1, &d1); | |
| 1711 pixGetDimensions(pixs2, &w2, &h2, &d2); | |
| 1712 if (d1 == 1 || d2 == 1) | |
| 1713 return (PIX *)ERROR_PTR("pixs1 or pixs2 is 1 bpp", __func__, NULL); | |
| 1714 if (pixg) { | |
| 1715 if (pixGetDepth(pixg) != 8) | |
| 1716 return (PIX *)ERROR_PTR("pixg not 8 bpp", __func__, NULL); | |
| 1717 pixGetDimensions(pixg, &wg, &hg, NULL); | |
| 1718 wmin = L_MIN(w2, wg); | |
| 1719 hmin = L_MIN(h2, hg); | |
| 1720 pixg2 = pixClone(pixg); | |
| 1721 } else { /* use the alpha component of pixs2 */ | |
| 1722 spp = pixGetSpp(pixs2); | |
| 1723 if (d2 != 32 || spp != 4) | |
| 1724 return (PIX *)ERROR_PTR("no alpha; pixs2 not rgba", __func__, NULL); | |
| 1725 wmin = w2; | |
| 1726 hmin = h2; | |
| 1727 pixg2 = pixGetRGBComponent(pixs2, L_ALPHA_CHANNEL); | |
| 1728 } | |
| 1729 | |
| 1730 /* Remove colormaps if they exist; clones are OK */ | |
| 1731 pixr1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); | |
| 1732 pixr2 = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); | |
| 1733 | |
| 1734 /* Regularize to the same depth if necessary */ | |
| 1735 d1 = pixGetDepth(pixr1); | |
| 1736 d2 = pixGetDepth(pixr2); | |
| 1737 if (d1 == 32) { /* convert d2 to rgb if necessary */ | |
| 1738 pix1 = pixClone(pixr1); | |
| 1739 if (d2 != 32) | |
| 1740 pix2 = pixConvertTo32(pixr2); | |
| 1741 else | |
| 1742 pix2 = pixClone(pixr2); | |
| 1743 } else if (d2 == 32) { /* and d1 != 32; convert to 32 */ | |
| 1744 pix2 = pixClone(pixr2); | |
| 1745 pix1 = pixConvertTo32(pixr1); | |
| 1746 } else { /* both are 8 bpp or less */ | |
| 1747 pix1 = pixConvertTo8(pixr1, FALSE); | |
| 1748 pix2 = pixConvertTo8(pixr2, FALSE); | |
| 1749 } | |
| 1750 pixDestroy(&pixr1); | |
| 1751 pixDestroy(&pixr2); | |
| 1752 | |
| 1753 /* Output a copy of pix1 to avoid side-effecting input pixs1 */ | |
| 1754 pixd = pixCopy(NULL, pix1); | |
| 1755 pixDestroy(&pix1); | |
| 1756 | |
| 1757 /* Sanity check: both either 8 or 32 bpp */ | |
| 1758 d1 = pixGetDepth(pixd); | |
| 1759 d2 = pixGetDepth(pix2); | |
| 1760 if (!pixd || d1 != d2 || (d1 != 8 && d1 != 32)) { | |
| 1761 pixDestroy(&pixd); | |
| 1762 pixDestroy(&pix2); | |
| 1763 pixDestroy(&pixg2); | |
| 1764 return (PIX *)ERROR_PTR("depths not regularized! bad!", __func__, NULL); | |
| 1765 } | |
| 1766 | |
| 1767 /* Blend pix2 onto pixd, using pixg2. | |
| 1768 * Let the normalized pixel value of pixg2 be f = pixval / 255, | |
| 1769 * and the pixel values of pixd and pix2 be p1 and p2, rsp. | |
| 1770 * Then the blended value is: | |
| 1771 * p = (1.0 - f) * p1 + f * p2 | |
| 1772 * Blending is done component-wise if rgb. | |
| 1773 * Scan over pix2 and pixg2, clipping to pixd where necessary. */ | |
| 1774 datad = pixGetData(pixd); | |
| 1775 datas = pixGetData(pix2); | |
| 1776 datag = pixGetData(pixg2); | |
| 1777 wpld = pixGetWpl(pixd); | |
| 1778 wpls = pixGetWpl(pix2); | |
| 1779 wplg = pixGetWpl(pixg2); | |
| 1780 for (i = 0; i < hmin; i++) { | |
| 1781 if (i + y < 0 || i + y >= h1) continue; | |
| 1782 lined = datad + (i + y) * wpld; | |
| 1783 lines = datas + i * wpls; | |
| 1784 lineg = datag + i * wplg; | |
| 1785 for (j = 0; j < wmin; j++) { | |
| 1786 if (j + x < 0 || j + x >= w1) continue; | |
| 1787 val = GET_DATA_BYTE(lineg, j); | |
| 1788 if (val == 0) continue; /* pix2 is transparent */ | |
| 1789 fract = (l_float32)val / 255.; | |
| 1790 if (d1 == 8) { | |
| 1791 dval = GET_DATA_BYTE(lined, j + x); | |
| 1792 sval = GET_DATA_BYTE(lines, j); | |
| 1793 dval = (l_int32)((1.0 - fract) * dval + fract * sval); | |
| 1794 SET_DATA_BYTE(lined, j + x, dval); | |
| 1795 } else { /* 32 */ | |
| 1796 dval32 = *(lined + j + x); | |
| 1797 sval32 = *(lines + j); | |
| 1798 extractRGBValues(dval32, &drval, &dgval, &dbval); | |
| 1799 extractRGBValues(sval32, &srval, &sgval, &sbval); | |
| 1800 drval = (l_int32)((1.0 - fract) * drval + fract * srval); | |
| 1801 dgval = (l_int32)((1.0 - fract) * dgval + fract * sgval); | |
| 1802 dbval = (l_int32)((1.0 - fract) * dbval + fract * sbval); | |
| 1803 composeRGBPixel(drval, dgval, dbval, &dval32); | |
| 1804 *(lined + j + x) = dval32; | |
| 1805 } | |
| 1806 } | |
| 1807 } | |
| 1808 | |
| 1809 pixDestroy(&pixg2); | |
| 1810 pixDestroy(&pix2); | |
| 1811 return pixd; | |
| 1812 } | |
| 1813 | |
| 1814 | |
| 1815 /*---------------------------------------------------------------------* | |
| 1816 * Blending background to a specific color * | |
| 1817 *---------------------------------------------------------------------*/ | |
| 1818 /*! | |
| 1819 * \brief pixBlendBackgroundToColor() | |
| 1820 * | |
| 1821 * \param[in] pixd can be NULL or pixs | |
| 1822 * \param[in] pixs 32 bpp rgb | |
| 1823 * \param[in] box region for blending; can be NULL) | |
| 1824 * \param[in] color 32 bit color in 0xrrggbb00 format | |
| 1825 * \param[in] gamma, minval, maxval args for grayscale TRC mapping | |
| 1826 * \return pixd always | |
| 1827 * | |
| 1828 * <pre> | |
| 1829 * Notes: | |
| 1830 * (1) This in effect replaces light background pixels in pixs | |
| 1831 * by the input color. It does it by alpha blending so that | |
| 1832 * there are no visible artifacts from hard cutoffs. | |
| 1833 * (2) If pixd == pixs, this is done in-place. | |
| 1834 * (3) If box == NULL, this is performed on all of pixs. | |
| 1835 * (4) The alpha component for blending is derived from pixs, | |
| 1836 * by converting to grayscale and enhancing with a TRC. | |
| 1837 * (5) The last three arguments specify the TRC operation. | |
| 1838 * Suggested values are: %gamma = 0.3, %minval = 50, %maxval = 200. | |
| 1839 * To skip the TRC, use %gamma == 1, %minval = 0, %maxval = 255. | |
| 1840 * See pixGammaTRC() for details. | |
| 1841 * </pre> | |
| 1842 */ | |
| 1843 PIX * | |
| 1844 pixBlendBackgroundToColor(PIX *pixd, | |
| 1845 PIX *pixs, | |
| 1846 BOX *box, | |
| 1847 l_uint32 color, | |
| 1848 l_float32 gamma, | |
| 1849 l_int32 minval, | |
| 1850 l_int32 maxval) | |
| 1851 { | |
| 1852 l_int32 x, y, w, h; | |
| 1853 BOX *boxt; | |
| 1854 PIX *pixt, *pixc, *pixr, *pixg; | |
| 1855 | |
| 1856 if (!pixs) | |
| 1857 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1858 if (pixGetDepth(pixs) != 32) | |
| 1859 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, pixd); | |
| 1860 if (pixd && (pixd != pixs)) | |
| 1861 return (PIX *)ERROR_PTR("pixd neither null nor pixs", __func__, pixd); | |
| 1862 | |
| 1863 /* Extract the (optionally cropped) region, pixr, and generate | |
| 1864 * an identically sized pixc with the uniform color. */ | |
| 1865 if (!pixd) | |
| 1866 pixd = pixCopy(NULL, pixs); | |
| 1867 if (box) { | |
| 1868 pixr = pixClipRectangle(pixd, box, &boxt); | |
| 1869 boxGetGeometry(boxt, &x, &y, &w, &h); | |
| 1870 pixc = pixCreate(w, h, 32); | |
| 1871 boxDestroy(&boxt); | |
| 1872 } else { | |
| 1873 pixc = pixCreateTemplate(pixs); | |
| 1874 pixr = pixClone(pixd); | |
| 1875 } | |
| 1876 pixSetAllArbitrary(pixc, color); | |
| 1877 | |
| 1878 /* Set up the alpha channel */ | |
| 1879 pixg = pixConvertTo8(pixr, 0); | |
| 1880 pixGammaTRC(pixg, pixg, gamma, minval, maxval); | |
| 1881 pixSetRGBComponent(pixc, pixg, L_ALPHA_CHANNEL); | |
| 1882 | |
| 1883 /* Blend and replace in pixd */ | |
| 1884 pixt = pixBlendWithGrayMask(pixr, pixc, NULL, 0, 0); | |
| 1885 if (box) { | |
| 1886 pixRasterop(pixd, x, y, w, h, PIX_SRC, pixt, 0, 0); | |
| 1887 pixDestroy(&pixt); | |
| 1888 } else { | |
| 1889 pixTransferAllData(pixd, &pixt, 0, 0); | |
| 1890 } | |
| 1891 | |
| 1892 pixDestroy(&pixc); | |
| 1893 pixDestroy(&pixr); | |
| 1894 pixDestroy(&pixg); | |
| 1895 return pixd; | |
| 1896 } | |
| 1897 | |
| 1898 | |
| 1899 /*---------------------------------------------------------------------* | |
| 1900 * Multiplying by a specific color * | |
| 1901 *---------------------------------------------------------------------*/ | |
| 1902 /*! | |
| 1903 * \brief pixMultiplyByColor() | |
| 1904 * | |
| 1905 * \param[in] pixd can be NULL or pixs | |
| 1906 * \param[in] pixs 32 bpp rgb | |
| 1907 * \param[in] box region for filtering; can be NULL) | |
| 1908 * \param[in] color 32 bit color in 0xrrggbb00 format | |
| 1909 * \return pixd always | |
| 1910 * | |
| 1911 * <pre> | |
| 1912 * Notes: | |
| 1913 * (1) This filters all pixels in the specified region by | |
| 1914 * multiplying each component by the input color. | |
| 1915 * This leaves black invariant and transforms white to the | |
| 1916 * input color. | |
| 1917 * (2) If pixd == pixs, this is done in-place. | |
| 1918 * (3) If box == NULL, this is performed on all of pixs. | |
| 1919 * </pre> | |
| 1920 */ | |
| 1921 PIX * | |
| 1922 pixMultiplyByColor(PIX *pixd, | |
| 1923 PIX *pixs, | |
| 1924 BOX *box, | |
| 1925 l_uint32 color) | |
| 1926 { | |
| 1927 l_int32 i, j, bx, by, w, h, wpl; | |
| 1928 l_int32 red, green, blue, rval, gval, bval, nrval, ngval, nbval; | |
| 1929 l_float32 frval, fgval, fbval; | |
| 1930 l_uint32 *data, *line; | |
| 1931 PIX *pixt; | |
| 1932 | |
| 1933 if (!pixs) | |
| 1934 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); | |
| 1935 if (pixGetDepth(pixs) != 32) | |
| 1936 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, pixd); | |
| 1937 if (pixd && (pixd != pixs)) | |
| 1938 return (PIX *)ERROR_PTR("pixd neither null nor pixs", __func__, pixd); | |
| 1939 | |
| 1940 if (!pixd) | |
| 1941 pixd = pixCopy(NULL, pixs); | |
| 1942 if (box) { | |
| 1943 boxGetGeometry(box, &bx, &by, NULL, NULL); | |
| 1944 pixt = pixClipRectangle(pixd, box, NULL); | |
| 1945 } else { | |
| 1946 pixt = pixClone(pixd); | |
| 1947 } | |
| 1948 | |
| 1949 /* Multiply each pixel in pixt by the color */ | |
| 1950 extractRGBValues(color, &red, &green, &blue); | |
| 1951 frval = (1. / 255.) * red; | |
| 1952 fgval = (1. / 255.) * green; | |
| 1953 fbval = (1. / 255.) * blue; | |
| 1954 data = pixGetData(pixt); | |
| 1955 wpl = pixGetWpl(pixt); | |
| 1956 pixGetDimensions(pixt, &w, &h, NULL); | |
| 1957 for (i = 0; i < h; i++) { | |
| 1958 line = data + i * wpl; | |
| 1959 for (j = 0; j < w; j++) { | |
| 1960 extractRGBValues(line[j], &rval, &gval, &bval); | |
| 1961 nrval = (l_int32)(frval * rval + 0.5); | |
| 1962 ngval = (l_int32)(fgval * gval + 0.5); | |
| 1963 nbval = (l_int32)(fbval * bval + 0.5); | |
| 1964 composeRGBPixel(nrval, ngval, nbval, line + j); | |
| 1965 } | |
| 1966 } | |
| 1967 | |
| 1968 /* Replace */ | |
| 1969 if (box) | |
| 1970 pixRasterop(pixd, bx, by, w, h, PIX_SRC, pixt, 0, 0); | |
| 1971 pixDestroy(&pixt); | |
| 1972 return pixd; | |
| 1973 } | |
| 1974 | |
| 1975 | |
| 1976 /*---------------------------------------------------------------------* | |
| 1977 * Rendering with alpha blending over a uniform background * | |
| 1978 *---------------------------------------------------------------------*/ | |
| 1979 /*! | |
| 1980 * \brief pixAlphaBlendUniform() | |
| 1981 * | |
| 1982 * \param[in] pixs 32 bpp rgba, with alpha | |
| 1983 * \param[in] color 32 bit color in 0xrrggbb00 format | |
| 1984 * \return pixd 32 bpp rgb: pixs blended over uniform color %color, | |
| 1985 * a clone of pixs if no alpha, and null on error | |
| 1986 * | |
| 1987 * <pre> | |
| 1988 * Notes: | |
| 1989 * (1) This is a convenience function that renders 32 bpp RGBA images | |
| 1990 * (with an alpha channel) over a uniform background of | |
| 1991 * value %color. To render over a white background, | |
| 1992 * use %color = 0xffffff00. The result is an RGB image. | |
| 1993 * (2) If pixs does not have an alpha channel, it returns a clone | |
| 1994 * of pixs. | |
| 1995 * </pre> | |
| 1996 */ | |
| 1997 PIX * | |
| 1998 pixAlphaBlendUniform(PIX *pixs, | |
| 1999 l_uint32 color) | |
| 2000 { | |
| 2001 PIX *pixt, *pixd; | |
| 2002 | |
| 2003 if (!pixs) | |
| 2004 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2005 if (pixGetDepth(pixs) != 32) | |
| 2006 return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL); | |
| 2007 if (pixGetSpp(pixs) != 4) { | |
| 2008 L_WARNING("no alpha channel; returning clone\n", __func__); | |
| 2009 return pixClone(pixs); | |
| 2010 } | |
| 2011 | |
| 2012 pixt = pixCreateTemplate(pixs); | |
| 2013 pixSetAllArbitrary(pixt, color); | |
| 2014 pixSetSpp(pixt, 3); /* not required */ | |
| 2015 pixd = pixBlendWithGrayMask(pixt, pixs, NULL, 0, 0); | |
| 2016 | |
| 2017 pixDestroy(&pixt); | |
| 2018 return pixd; | |
| 2019 } | |
| 2020 | |
| 2021 | |
| 2022 /*---------------------------------------------------------------------* | |
| 2023 * Adding an alpha layer for blending * | |
| 2024 *---------------------------------------------------------------------*/ | |
| 2025 /*! | |
| 2026 * \brief pixAddAlphaToBlend() | |
| 2027 * | |
| 2028 * \param[in] pixs any depth | |
| 2029 * \param[in] fract fade fraction in the alpha component | |
| 2030 * \param[in] invert 1 to photometrically invert pixs | |
| 2031 * \return pixd 32 bpp with alpha, or null on error | |
| 2032 * | |
| 2033 * <pre> | |
| 2034 * Notes: | |
| 2035 * (1) This is a simple alpha layer generator, where typically white has | |
| 2036 * maximum transparency and black has minimum. | |
| 2037 * (2) If %invert == 1, generate the same alpha layer but invert | |
| 2038 * the input image photometrically. This is useful for blending | |
| 2039 * over dark images, where you want dark regions in pixs, such | |
| 2040 * as text, to be lighter in the blended image. | |
| 2041 * (3) The fade %fract gives the minimum transparency (i.e., | |
| 2042 * maximum opacity). A small fraction is useful for adding | |
| 2043 * a watermark to an image. | |
| 2044 * (4) If pixs has a colormap, it is removed to rgb. | |
| 2045 * (5) If pixs already has an alpha layer, it is overwritten. | |
| 2046 * </pre> | |
| 2047 */ | |
| 2048 PIX * | |
| 2049 pixAddAlphaToBlend(PIX *pixs, | |
| 2050 l_float32 fract, | |
| 2051 l_int32 invert) | |
| 2052 { | |
| 2053 PIX *pixd, *pix1, *pix2; | |
| 2054 | |
| 2055 if (!pixs) | |
| 2056 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2057 if (fract < 0.0 || fract > 1.0) | |
| 2058 return (PIX *)ERROR_PTR("invalid fract", __func__, NULL); | |
| 2059 | |
| 2060 /* Convert to 32 bpp */ | |
| 2061 if (pixGetColormap(pixs)) | |
| 2062 pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR); | |
| 2063 else | |
| 2064 pix1 = pixClone(pixs); | |
| 2065 pixd = pixConvertTo32(pix1); /* new */ | |
| 2066 | |
| 2067 /* Use an inverted image if this will be blended with a dark image */ | |
| 2068 if (invert) pixInvert(pixd, pixd); | |
| 2069 | |
| 2070 /* Generate alpha layer */ | |
| 2071 pix2 = pixConvertTo8(pix1, 0); /* new */ | |
| 2072 pixInvert(pix2, pix2); | |
| 2073 pixMultConstantGray(pix2, fract); | |
| 2074 pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); | |
| 2075 | |
| 2076 pixDestroy(&pix1); | |
| 2077 pixDestroy(&pix2); | |
| 2078 return pixd; | |
| 2079 } | |
| 2080 | |
| 2081 | |
| 2082 | |
| 2083 /*---------------------------------------------------------------------* | |
| 2084 * Setting a transparent alpha component over a white background * | |
| 2085 *---------------------------------------------------------------------*/ | |
| 2086 /*! | |
| 2087 * \brief pixSetAlphaOverWhite() | |
| 2088 * | |
| 2089 * \param[in] pixs colormapped or 32 bpp rgb; no alpha | |
| 2090 * \return pixd new pix with meaningful alpha component, | |
| 2091 * or null on error | |
| 2092 * | |
| 2093 * <pre> | |
| 2094 * Notes: | |
| 2095 * (1) The generated alpha component is transparent over white | |
| 2096 * (background) pixels in pixs, and quickly grades to opaque | |
| 2097 * away from the transparent parts. This is a cheap and | |
| 2098 * dirty alpha generator. The 2 pixel gradation is useful | |
| 2099 * to blur the boundary between the transparent region | |
| 2100 * (that will render entirely from a backing image) and | |
| 2101 * the remainder which renders from pixs. | |
| 2102 * (2) All alpha component bits in pixs are overwritten. | |
| 2103 * </pre> | |
| 2104 */ | |
| 2105 PIX * | |
| 2106 pixSetAlphaOverWhite(PIX *pixs) | |
| 2107 { | |
| 2108 PIX *pixd, *pix1, *pix2, *pix3, *pix4; | |
| 2109 | |
| 2110 if (!pixs) | |
| 2111 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 2112 if (!(pixGetDepth(pixs) == 32 || pixGetColormap(pixs))) | |
| 2113 return (PIX *)ERROR_PTR("pixs not 32 bpp or cmapped", __func__, NULL); | |
| 2114 | |
| 2115 /* Remove colormap if it exists; otherwise copy */ | |
| 2116 pixd = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_TO_FULL_COLOR, L_COPY); | |
| 2117 | |
| 2118 /* Generate a 1 bpp image where a white pixel in pixd is 0. | |
| 2119 * In the comments below, a "white" pixel refers to pixd. | |
| 2120 * pix1 is rgb, pix2 is 8 bpp gray, pix3 is 1 bpp. */ | |
| 2121 pix1 = pixInvert(NULL, pixd); /* send white (255) to 0 for each sample */ | |
| 2122 pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX); /* 0 if white */ | |
| 2123 pix3 = pixThresholdToBinary(pix2, 1); /* sets white pixels to 1 */ | |
| 2124 pixInvert(pix3, pix3); /* sets white pixels to 0 */ | |
| 2125 | |
| 2126 /* Generate the alpha component using the distance transform, | |
| 2127 * which measures the distance to the nearest bg (0) pixel in pix3. | |
| 2128 * After multiplying by 128, its value is 0 (transparent) | |
| 2129 * over white pixels, and goes to opaque (255) two pixels away | |
| 2130 * from the nearest white pixel. */ | |
| 2131 pix4 = pixDistanceFunction(pix3, 8, 8, L_BOUNDARY_FG); | |
| 2132 pixMultConstantGray(pix4, 128.0); | |
| 2133 pixSetRGBComponent(pixd, pix4, L_ALPHA_CHANNEL); | |
| 2134 | |
| 2135 pixDestroy(&pix1); | |
| 2136 pixDestroy(&pix2); | |
| 2137 pixDestroy(&pix3); | |
| 2138 pixDestroy(&pix4); | |
| 2139 return pixd; | |
| 2140 } | |
| 2141 | |
| 2142 | |
| 2143 /*---------------------------------------------------------------------* | |
| 2144 * Fading from the edge * | |
| 2145 *---------------------------------------------------------------------*/ | |
| 2146 /*! | |
| 2147 * \brief pixLinearEdgeFade() | |
| 2148 * | |
| 2149 * \param[in] pixs 8 or 32 bpp; no colormap | |
| 2150 * \param[in] dir L_FROM_LEFT, L_FROM_RIGHT, L_FROM_TOP, L_FROM_BOT | |
| 2151 * \param[in] fadeto L_BLEND_TO_WHITE, L_BLEND_TO_BLACK | |
| 2152 * \param[in] distfract fraction of width or height over which fading occurs | |
| 2153 * \param[in] maxfade fraction of fading at the edge, <= 1.0 | |
| 2154 * \return 0 if OK, 1 on error | |
| 2155 * | |
| 2156 * <pre> | |
| 2157 * Notes: | |
| 2158 * (1) In-place operation. | |
| 2159 * (2) Maximum fading fraction %maxfade occurs at the edge of the image, | |
| 2160 * and the fraction goes to 0 at the fractional distance %distfract | |
| 2161 * from the edge. %maxfade must be in [0, 1]. | |
| 2162 * (3) %distrfact must be in [0, 1], and typically it would be <= 0.5. | |
| 2163 * </pre> | |
| 2164 */ | |
| 2165 l_ok | |
| 2166 pixLinearEdgeFade(PIX *pixs, | |
| 2167 l_int32 dir, | |
| 2168 l_int32 fadeto, | |
| 2169 l_float32 distfract, | |
| 2170 l_float32 maxfade) | |
| 2171 { | |
| 2172 l_int32 i, j, w, h, d, wpl, xmin, ymin, range, val, rval, gval, bval; | |
| 2173 l_float32 slope, limit, del; | |
| 2174 l_uint32 *data, *line; | |
| 2175 | |
| 2176 if (!pixs) | |
| 2177 return ERROR_INT("pixs not defined", __func__, 1); | |
| 2178 if (pixGetColormap(pixs) != NULL) | |
| 2179 return ERROR_INT("pixs has a colormap", __func__, 1); | |
| 2180 pixGetDimensions(pixs, &w, &h, &d); | |
| 2181 if (d != 8 && d != 32) | |
| 2182 return ERROR_INT("pixs not 8 or 32 bpp", __func__, 1); | |
| 2183 if (dir != L_FROM_LEFT && dir != L_FROM_RIGHT && | |
| 2184 dir != L_FROM_TOP && dir != L_FROM_BOT) | |
| 2185 return ERROR_INT("invalid fade direction from edge", __func__, 1); | |
| 2186 if (fadeto != L_BLEND_TO_WHITE && fadeto != L_BLEND_TO_BLACK) | |
| 2187 return ERROR_INT("invalid fadeto photometry", __func__, 1); | |
| 2188 if (maxfade <= 0) return 0; | |
| 2189 if (maxfade > 1.0) | |
| 2190 return ERROR_INT("invalid maxfade", __func__, 1); | |
| 2191 if (distfract <= 0 || distfract * L_MIN(w, h) < 1.0) { | |
| 2192 L_INFO("distfract is too small\n", __func__); | |
| 2193 return 0; | |
| 2194 } | |
| 2195 if (distfract > 1.0) | |
| 2196 return ERROR_INT("invalid distfract", __func__, 1); | |
| 2197 | |
| 2198 /* Set up parameters */ | |
| 2199 if (dir == L_FROM_LEFT) { | |
| 2200 range = (l_int32)(distfract * w); | |
| 2201 xmin = 0; | |
| 2202 slope = maxfade / (l_float32)range; | |
| 2203 } else if (dir == L_FROM_RIGHT) { | |
| 2204 range = (l_int32)(distfract * w); | |
| 2205 xmin = w - range; | |
| 2206 slope = maxfade / (l_float32)range; | |
| 2207 } else if (dir == L_FROM_TOP) { | |
| 2208 range = (l_int32)(distfract * h); | |
| 2209 ymin = 0; | |
| 2210 slope = maxfade / (l_float32)range; | |
| 2211 } else { /* dir == L_FROM_BOT */ | |
| 2212 range = (l_int32)(distfract * h); | |
| 2213 ymin = h - range; | |
| 2214 slope = maxfade / (l_float32)range; | |
| 2215 } | |
| 2216 | |
| 2217 limit = (fadeto == L_BLEND_TO_WHITE) ? 255.0 : 0.0; | |
| 2218 data = pixGetData(pixs); | |
| 2219 wpl = pixGetWpl(pixs); | |
| 2220 if (dir == L_FROM_LEFT || dir == L_FROM_RIGHT) { | |
| 2221 for (j = 0; j < range; j++) { | |
| 2222 del = (dir == L_FROM_LEFT) ? maxfade - slope * j | |
| 2223 : maxfade - slope * (range - j); | |
| 2224 for (i = 0; i < h; i++) { | |
| 2225 line = data + i * wpl; | |
| 2226 if (d == 8) { | |
| 2227 val = GET_DATA_BYTE(line, xmin + j); | |
| 2228 val += (limit - val) * del + 0.5; | |
| 2229 SET_DATA_BYTE(line, xmin + j, val); | |
| 2230 } else { /* rgb */ | |
| 2231 extractRGBValues(*(line + xmin + j), &rval, &gval, &bval); | |
| 2232 rval += (limit - rval) * del + 0.5; | |
| 2233 gval += (limit - gval) * del + 0.5; | |
| 2234 bval += (limit - bval) * del + 0.5; | |
| 2235 composeRGBPixel(rval, gval, bval, line + xmin + j); | |
| 2236 } | |
| 2237 } | |
| 2238 } | |
| 2239 } else { /* dir == L_FROM_TOP || L_FROM_BOT */ | |
| 2240 for (i = 0; i < range; i++) { | |
| 2241 del = (dir == L_FROM_TOP) ? maxfade - slope * i | |
| 2242 : maxfade - slope * (range - i); | |
| 2243 line = data + (ymin + i) * wpl; | |
| 2244 for (j = 0; j < w; j++) { | |
| 2245 if (d == 8) { | |
| 2246 val = GET_DATA_BYTE(line, j); | |
| 2247 val += (limit - val) * del + 0.5; | |
| 2248 SET_DATA_BYTE(line, j, val); | |
| 2249 } else { /* rgb */ | |
| 2250 extractRGBValues(*(line + j), &rval, &gval, &bval); | |
| 2251 rval += (limit - rval) * del + 0.5; | |
| 2252 gval += (limit - gval) * del + 0.5; | |
| 2253 bval += (limit - bval) * del + 0.5; | |
| 2254 composeRGBPixel(rval, gval, bval, line + j); | |
| 2255 } | |
| 2256 } | |
| 2257 } | |
| 2258 } | |
| 2259 | |
| 2260 return 0; | |
| 2261 } |
