Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 /* 00017 * blend.c 00018 * 00019 * Blending two images that are not colormapped 00020 * PIX *pixBlend() 00021 * PIX *pixBlendMask() 00022 * PIX *pixBlendGray() 00023 * PIX *pixBlendColor() 00024 * PIX *pixBlendColorByChannel() 00025 * PIX *pixBlendGrayAdapt() 00026 * static l_int32 blendComponents() 00027 * PIX *pixFadeWithGray() 00028 * PIX *pixBlendHardLight() 00029 * static l_int32 blendHardLightComponents() 00030 * 00031 * Blending two colormapped images 00032 * l_int32 pixBlendCmap() 00033 * 00034 * Blending two images using a third (alpha mask) 00035 * PIX *pixBlendWithGrayMask() 00036 * 00037 * Coloring "gray" pixels 00038 * l_int32 pixColorGray() 00039 * 00040 * Adjusting one or more colors to a target color 00041 * PIX *pixSnapColor() 00042 * PIX *pixSnapColorCmap() 00043 * 00044 * Mapping colors based on a source/target pair 00045 * PIX *pixLinearMapToTargetColor() 00046 * l_uint32 pixelLinearMapToTargetColor() 00047 * 00048 * Fractional shift of RGB towards black or white 00049 * l_uint32 pixelFractionalShift() 00050 * 00051 * In blending operations a new pix is produced where typically 00052 * a subset of pixels in src1 are changed by the set of pixels 00053 * in src2, when src2 is located in a given position relative 00054 * to src1. This is similar to rasterop, except that the 00055 * blending operations we allow are more complex, and typically 00056 * result in dest pixels that are a linear combination of two 00057 * pixels, such as src1 and its inverse. I find it convenient 00058 * to think of src2 as the "blender" (the one that takes the action) 00059 * and src1 as the "blendee" (the one that changes). 00060 * 00061 * Blending works best when src1 is 8 or 32 bpp. We also allow 00062 * src1 to be colormapped, but the colormap is removed before blending, 00063 * so if src1 is colormapped, we can't allow in-place blending. 00064 * 00065 * Because src2 is typically smaller than src1, we can implement by 00066 * clipping src2 to src1 and then transforming some of the dest 00067 * pixels that are under the support of src2. In practice, we 00068 * do the clipping in the inner pixel loop. For grayscale and 00069 * color src2, we also allow a simple form of transparency, where 00070 * pixels of a particular value in src2 are transparent; for those pixels, 00071 * no blending is done. 00072 * 00073 * The blending functions are categorized by the depth of src2, 00074 * the blender, and not that of src1, the blendee. 00075 * 00076 * - If src2 is 1 bpp, we can do one of three things: 00077 * (1) L_BLEND_WITH_INVERSE: Blend a given fraction of src1 with its 00078 * inverse color for those pixels in src2 that are fg (ON), 00079 * and leave the dest pixels unchanged for pixels in src2 that 00080 * are bg (OFF). 00081 * (2) L_BLEND_TO_WHITE: Fade the src1 pixels toward white by a 00082 * given fraction for those pixels in src2 that are fg (ON), 00083 * and leave the dest pixels unchanged for pixels in src2 that 00084 * are bg (OFF). 00085 * (3) L_BLEND_TO_BLACK: Fade the src1 pixels toward black by a 00086 * given fraction for those pixels in src2 that are fg (ON), 00087 * and leave the dest pixels unchanged for pixels in src2 that 00088 * are bg (OFF). 00089 * The blending function is pixBlendMask(). 00090 * 00091 * - If src2 is 8 bpp grayscale, we can do one of two things 00092 * (but see pixFadeWithGray() below): 00093 * (1) L_BLEND_GRAY: If src1 is 8 bpp, mix the two values, using 00094 * a fraction of src2 and (1 - fraction) of src1. 00095 * If src1 is 32 bpp (rgb), mix the fraction of src2 with 00096 * each of the color components in src1. 00097 * (2) L_BLEND_GRAY_WITH_INVERSE: Use the grayscale value in src2 00098 * to determine how much of the inverse of a src1 pixel is 00099 * to be combined with the pixel value. The input fraction 00100 * further acts to scale the change in the src1 pixel. 00101 * The blending function is pixBlendGray(). 00102 * 00103 * - If src2 is color, we blend a given fraction of src2 with 00104 * src1. If src1 is 8 bpp, the resulting image is 32 bpp. 00105 * The blending function is pixBlendColor(). 00106 * 00107 * - For all three blending functions -- pixBlendMask(), pixBlendGray() 00108 * and pixBlendColor() -- you can apply the blender to the blendee 00109 * either in-place or generating a new pix. For the in-place 00110 * operation, this requires that the depth of the resulting pix 00111 * must equal that of the input pixs1. 00112 * 00113 * - We remove colormaps from src1 and src2 before blending. 00114 * Any quantization would have to be done after blending. 00115 * 00116 * We include another function, pixFadeWithGray(), that blends 00117 * a gray or color src1 with a gray src2. It does one of these things: 00118 * (1) L_BLEND_TO_WHITE: Fade the src1 pixels toward white by 00119 * a number times the value in src2. 00120 * (2) L_BLEND_TO_BLACK: Fade the src1 pixels toward black by 00121 * a number times the value in src2. 00122 * 00123 * Also included is a generalization of the so-called "hard light" 00124 * blending: pixBlendHardLight(). We generalize by allowing a fraction < 1.0 00125 * of the blender to be admixed with the blendee. The standard function 00126 * does full mixing. 00127 */ 00128 00129 00130 #include <stdio.h> 00131 #include <stdlib.h> 00132 #include "allheaders.h" 00133 00134 static l_int32 blendComponents(l_int32 a, l_int32 b, l_float32 fract); 00135 static l_int32 blendHardLightComponents(l_int32 a, l_int32 b, l_float32 fract); 00136 00137 00138 00139 /*-------------------------------------------------------------* 00140 * Pixel blending * 00141 *-------------------------------------------------------------*/ 00142 /*! 00143 * pixBlend() 00144 * 00145 * Input: pixs1 (blendee) 00146 * pixs2 (blender; typ. smaller) 00147 * x,y (origin (UL corner) of pixs2 relative to 00148 * the origin of pixs1; can be < 0) 00149 * fract (blending fraction) 00150 * Return: pixd (blended image), or null on error 00151 * 00152 * Notes: 00153 * (1) This is a simple top-level interface. For more flexibility, 00154 * call directly into pixBlendMask(), etc. 00155 */ 00156 PIX * 00157 pixBlend(PIX *pixs1, 00158 PIX *pixs2, 00159 l_int32 x, 00160 l_int32 y, 00161 l_float32 fract) 00162 { 00163 l_int32 w1, h1, d1, d2; 00164 BOX *box; 00165 PIX *pixc, *pixt, *pixd; 00166 00167 PROCNAME("pixBlend"); 00168 00169 if (!pixs1) 00170 return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); 00171 if (!pixs2) 00172 return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); 00173 00174 /* check relative depths */ 00175 d1 = pixGetDepth(pixs1); 00176 d2 = pixGetDepth(pixs2); 00177 if (d1 == 1 && d2 > 1) 00178 return (PIX *)ERROR_PTR("mixing gray or color with 1 bpp", 00179 procName, NULL); 00180 00181 /* remove colormap from pixs2 if necessary */ 00182 pixt = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); 00183 d2 = pixGetDepth(pixt); 00184 00185 /* Check if pixs2 is clipped by its position with respect 00186 * to pixs1; if so, clip it and redefine x and y if necessary. 00187 * This actually isn't necessary, as the specific blending 00188 * functions do the clipping directly in the pixel loop 00189 * over pixs2, but it's included here to show how it can 00190 * easily be done on pixs2 first. */ 00191 pixGetDimensions(pixs1, &w1, &h1, NULL); 00192 box = boxCreate(-x, -y, w1, h1); /* box of pixs1 relative to pixs2 */ 00193 pixc = pixClipRectangle(pixt, box, NULL); 00194 boxDestroy(&box); 00195 if (!pixc) { 00196 L_WARNING("box doesn't overlap pix", procName); 00197 return NULL; 00198 } 00199 x = L_MAX(0, x); 00200 y = L_MAX(0, y); 00201 00202 if (d2 == 1) 00203 pixd = pixBlendMask(NULL, pixs1, pixc, x, y, fract, 00204 L_BLEND_WITH_INVERSE); 00205 else if (d2 == 8) 00206 pixd = pixBlendGray(NULL, pixs1, pixc, x, y, fract, 00207 L_BLEND_GRAY, 0, 0); 00208 else /* d2 == 32 */ 00209 pixd = pixBlendColor(NULL, pixs1, pixc, x, y, fract, 0, 0); 00210 00211 pixDestroy(&pixc); 00212 pixDestroy(&pixt); 00213 return pixd; 00214 } 00215 00216 00217 /*! 00218 * pixBlendMask() 00219 * 00220 * Input: pixd (<optional>; either NULL or equal to pixs1 for in-place) 00221 * pixs1 (blendee; depth > 1) 00222 * pixs2 (blender; typ. smaller in size than pixs1) 00223 * x,y (origin (UL corner) of pixs2 relative to 00224 * the origin of pixs1; can be < 0) 00225 * fract (blending fraction) 00226 * type (L_BLEND_WITH_INVERSE, L_BLEND_TO_WHITE, L_BLEND_TO_BLACK) 00227 * Return: pixd if OK; pixs1 on error 00228 * 00229 * Notes: 00230 * (1) pixs2 must be 1 bpp 00231 * (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop. 00232 * (3) If pixs1 has a colormap, it is removed. 00233 * (4) For inplace operation, call it this way: 00234 * pixBlendMask(pixs1, pixs1, pixs2, ...) 00235 * (5) For generating a new pixd: 00236 * pixd = pixBlendMask(NULL, pixs1, pixs2, ...) 00237 * (6) Only call in-place if pixs1 does not have a colormap. 00238 */ 00239 PIX * 00240 pixBlendMask(PIX *pixd, 00241 PIX *pixs1, 00242 PIX *pixs2, 00243 l_int32 x, 00244 l_int32 y, 00245 l_float32 fract, 00246 l_int32 type) 00247 { 00248 l_int32 i, j, d, wc, hc, w, h, wplc; 00249 l_int32 val, rval, gval, bval; 00250 l_uint32 pixval; 00251 l_uint32 *linec, *datac; 00252 PIX *pixc, *pixt1, *pixt2; 00253 00254 PROCNAME("pixBlendMask"); 00255 00256 if (!pixs1) 00257 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); 00258 if (!pixs2) 00259 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); 00260 if (pixGetDepth(pixs1) == 1) 00261 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); 00262 if (pixGetDepth(pixs2) != 1) 00263 return (PIX *)ERROR_PTR("pixs2 not 1 bpp", procName, pixd); 00264 if (pixd == pixs1 && pixGetColormap(pixs1)) 00265 return (PIX *)ERROR_PTR("inplace; pixs1 has colormap", procName, pixd); 00266 if (pixd && (pixd != pixs1)) 00267 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); 00268 if (fract < 0.0 || fract > 1.0) { 00269 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5", procName); 00270 fract = 0.5; 00271 } 00272 if (type != L_BLEND_WITH_INVERSE && type != L_BLEND_TO_WHITE && 00273 type != L_BLEND_TO_BLACK) { 00274 L_WARNING("invalid blend type; setting to L_BLEND_WITH_INVERSE", 00275 procName); 00276 type = L_BLEND_WITH_INVERSE; 00277 } 00278 00279 00280 /* If pixd != NULL, we know that it is equal to pixs1 and 00281 * that pixs1 does not have a colormap, so that an in-place operation 00282 * can be done. Otherwise, remove colormap from pixs1 if 00283 * it exists and unpack to at least 8 bpp if necessary, 00284 * to do the blending on a new pix. */ 00285 if (!pixd) { 00286 pixt1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); 00287 if (pixGetDepth(pixt1) < 8) 00288 pixt2 = pixConvertTo8(pixt1, FALSE); 00289 else 00290 pixt2 = pixClone(pixt1); 00291 pixd = pixCopy(NULL, pixt2); 00292 pixDestroy(&pixt1); 00293 pixDestroy(&pixt2); 00294 } 00295 00296 pixGetDimensions(pixd, &w, &h, &d); /* d must be either 8 or 32 bpp */ 00297 pixc = pixClone(pixs2); 00298 wc = pixGetWidth(pixc); 00299 hc = pixGetHeight(pixc); 00300 datac = pixGetData(pixc); 00301 wplc = pixGetWpl(pixc); 00302 00303 /* Check limits for src1, in case clipping was not done. */ 00304 switch (type) 00305 { 00306 case L_BLEND_WITH_INVERSE: 00307 /* 00308 * The core logic for this blending is: 00309 * p --> (1 - f) * p + f * (1 - p) 00310 * where p is a normalized value: p = pixval / 255. 00311 * Thus, 00312 * p --> p + f * (1 - 2 * p) 00313 */ 00314 for (i = 0; i < hc; i++) { 00315 if (i + y < 0 || i + y >= h) continue; 00316 linec = datac + i * wplc; 00317 for (j = 0; j < wc; j++) { 00318 if (j + x < 0 || j + x >= w) continue; 00319 bval = GET_DATA_BIT(linec, j); 00320 if (bval) { 00321 switch (d) 00322 { 00323 case 8: 00324 pixGetPixel(pixd, x + j, y + i, &pixval); 00325 val = (l_int32)(pixval + fract * (255 - 2 * pixval)); 00326 pixSetPixel(pixd, x + j, y + i, val); 00327 break; 00328 case 32: 00329 pixGetPixel(pixd, x + j, y + i, &pixval); 00330 extractRGBValues(pixval, &rval, &gval, &bval); 00331 rval = (l_int32)(rval + fract * (255 - 2 * rval)); 00332 gval = (l_int32)(gval + fract * (255 - 2 * gval)); 00333 bval = (l_int32)(bval + fract * (255 - 2 * bval)); 00334 composeRGBPixel(rval, gval, bval, &pixval); 00335 pixSetPixel(pixd, x + j, y + i, pixval); 00336 break; 00337 default: 00338 L_WARNING("d neither 8 nor 32 bpp; no blend", procName); 00339 } 00340 } 00341 } 00342 } 00343 break; 00344 case L_BLEND_TO_WHITE: 00345 for (i = 0; i < hc; i++) { 00346 if (i + y < 0 || i + y >= h) continue; 00347 linec = datac + i * wplc; 00348 for (j = 0; j < wc; j++) { 00349 if (j + x < 0 || j + x >= w) continue; 00350 bval = GET_DATA_BIT(linec, j); 00351 if (bval) { 00352 switch (d) 00353 { 00354 case 8: 00355 pixGetPixel(pixd, x + j, y + i, &pixval); 00356 val = (l_int32)(pixval + fract * (255 - pixval)); 00357 pixSetPixel(pixd, x + j, y + i, val); 00358 break; 00359 case 32: 00360 pixGetPixel(pixd, x + j, y + i, &pixval); 00361 extractRGBValues(pixval, &rval, &gval, &bval); 00362 rval = (l_int32)(rval + fract * (255 - rval)); 00363 gval = (l_int32)(gval + fract * (255 - gval)); 00364 bval = (l_int32)(bval + fract * (255 - bval)); 00365 composeRGBPixel(rval, gval, bval, &pixval); 00366 pixSetPixel(pixd, x + j, y + i, pixval); 00367 break; 00368 default: 00369 L_WARNING("d neither 8 nor 32 bpp; no blend", procName); 00370 } 00371 } 00372 } 00373 } 00374 break; 00375 case L_BLEND_TO_BLACK: 00376 for (i = 0; i < hc; i++) { 00377 if (i + y < 0 || i + y >= h) continue; 00378 linec = datac + i * wplc; 00379 for (j = 0; j < wc; j++) { 00380 if (j + x < 0 || j + x >= w) continue; 00381 bval = GET_DATA_BIT(linec, j); 00382 if (bval) { 00383 switch (d) 00384 { 00385 case 8: 00386 pixGetPixel(pixd, x + j, y + i, &pixval); 00387 val = (l_int32)((1. - fract) * pixval); 00388 pixSetPixel(pixd, x + j, y + i, val); 00389 break; 00390 case 32: 00391 pixGetPixel(pixd, x + j, y + i, &pixval); 00392 extractRGBValues(pixval, &rval, &gval, &bval); 00393 rval = (l_int32)((1. - fract) * rval); 00394 gval = (l_int32)((1. - fract) * gval); 00395 bval = (l_int32)((1. - fract) * bval); 00396 composeRGBPixel(rval, gval, bval, &pixval); 00397 pixSetPixel(pixd, x + j, y + i, pixval); 00398 break; 00399 default: 00400 L_WARNING("d neither 8 nor 32 bpp; no blend", procName); 00401 } 00402 } 00403 } 00404 } 00405 break; 00406 default: 00407 L_WARNING("invalid binary mask blend type", procName); 00408 break; 00409 } 00410 00411 pixDestroy(&pixc); 00412 return pixd; 00413 } 00414 00415 00416 /*! 00417 * pixBlendGray() 00418 * 00419 * Input: pixd (<optional>; either NULL or equal to pixs1 for in-place) 00420 * pixs1 (blendee; depth > 1) 00421 * pixs2 (blender, 8 bpp; typ. smaller in size than pixs1) 00422 * x,y (origin (UL corner) of pixs2 relative to 00423 * the origin of pixs1; can be < 0) 00424 * fract (blending fraction) 00425 * type (L_BLEND_GRAY, L_BLEND_GRAY_WITH_INVERSE) 00426 * transparent (1 to use transparency; 0 otherwise) 00427 * transpix (pixel grayval in pixs2 that is to be transparent) 00428 * Return: pixd if OK; pixs1 on error 00429 * 00430 * Notes: 00431 * (1) pixs2 must be 8 bpp, and have no colormap. 00432 * (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop. 00433 * (3) If pixs1 has a colormap, it is removed. 00434 * (4) If pixs1 has depth < 8, it is unpacked to generate a 8 bpp pix. 00435 * (5) For inplace operation, call it this way: 00436 * pixBlendGray(pixs1, pixs1, pixs2, ...) 00437 * (6) For generating a new pixd: 00438 * pixd = pixBlendGray(NULL, pixs1, pixs2, ...) 00439 * (7) Only call in-place if pixs1 does not have a colormap; 00440 * otherwise it is an error. 00441 * (8) If transparent = 0, the blending fraction (fract) is 00442 * applied equally to all pixels. 00443 * (9) If transparent = 1, all pixels of value transpix (typically 00444 * either 0 or 0xff) in pixs2 are transparent in the blend. 00445 * (10) After processing pixs1, it is either 8 bpp or 32 bpp: 00446 * - if 8 bpp, the fraction of pixs2 is mixed with pixs1. 00447 * - if 32 bpp, each component of pixs1 is mixed with 00448 * the same fraction of pixs2. 00449 * (11) For L_BLEND_GRAY_WITH_INVERSE, the white values of the blendee 00450 * (cval == 255 in the code below) result in a delta of 0. 00451 * Thus, these pixels are intrinsically transparent! 00452 * The "pivot" value of the src, at which no blending occurs, is 00453 * 128. Compare with the adaptive pivot in pixBlendGrayAdapt(). 00454 */ 00455 PIX * 00456 pixBlendGray(PIX *pixd, 00457 PIX *pixs1, 00458 PIX *pixs2, 00459 l_int32 x, 00460 l_int32 y, 00461 l_float32 fract, 00462 l_int32 type, 00463 l_int32 transparent, 00464 l_uint32 transpix) 00465 { 00466 l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta; 00467 l_int32 ival, irval, igval, ibval, cval, dval; 00468 l_uint32 val32; 00469 l_uint32 *linec, *lined, *datac, *datad; 00470 PIX *pixc, *pixt1, *pixt2; 00471 00472 PROCNAME("pixBlendGray"); 00473 00474 if (!pixs1) 00475 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); 00476 if (!pixs2) 00477 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); 00478 if (pixGetDepth(pixs1) == 1) 00479 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); 00480 if (pixGetDepth(pixs2) != 8) 00481 return (PIX *)ERROR_PTR("pixs2 not 8 bpp", procName, pixd); 00482 if (pixGetColormap(pixs2)) 00483 return (PIX *)ERROR_PTR("pixs2 has a colormap", procName, pixd); 00484 if (pixd == pixs1 && pixGetColormap(pixs1)) 00485 return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd); 00486 if (pixd && (pixd != pixs1)) 00487 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); 00488 if (fract < 0.0 || fract > 1.0) { 00489 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5", procName); 00490 fract = 0.5; 00491 } 00492 if (type != L_BLEND_GRAY && type != L_BLEND_GRAY_WITH_INVERSE) { 00493 L_WARNING("invalid blend type; setting to L_BLEND_GRAY", procName); 00494 type = L_BLEND_GRAY; 00495 } 00496 00497 /* If pixd != NULL, we know that it is equal to pixs1 and 00498 * that pixs1 does not have a colormap, so that an in-place operation 00499 * can be done. Otherwise, remove colormap from pixs1 if 00500 * it exists and unpack to at least 8 bpp if necessary, 00501 * to do the blending on a new pix. */ 00502 if (!pixd) { 00503 pixt1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); 00504 if (pixGetDepth(pixt1) < 8) 00505 pixt2 = pixConvertTo8(pixt1, FALSE); 00506 else 00507 pixt2 = pixClone(pixt1); 00508 pixd = pixCopy(NULL, pixt2); 00509 pixDestroy(&pixt1); 00510 pixDestroy(&pixt2); 00511 } 00512 00513 pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */ 00514 wpld = pixGetWpl(pixd); 00515 datad = pixGetData(pixd); 00516 pixc = pixClone(pixs2); 00517 pixGetDimensions(pixc, &wc, &hc, NULL); 00518 datac = pixGetData(pixc); 00519 wplc = pixGetWpl(pixc); 00520 00521 /* check limits for src1, in case clipping was not done */ 00522 if (type == L_BLEND_GRAY) { 00523 for (i = 0; i < hc; i++) { 00524 if (i + y < 0 || i + y >= h) continue; 00525 linec = datac + i * wplc; 00526 lined = datad + (i + y) * wpld; 00527 switch (d) 00528 { 00529 case 8: 00530 for (j = 0; j < wc; j++) { 00531 if (j + x < 0 || j + x >= w) continue; 00532 cval = GET_DATA_BYTE(linec, j); 00533 if (transparent == 0 || 00534 (transparent != 0 && cval != transpix)) { 00535 dval = GET_DATA_BYTE(lined, j + x); 00536 ival = (l_int32)((1. - fract) * dval + fract * cval); 00537 SET_DATA_BYTE(lined, j + x, ival); 00538 } 00539 } 00540 break; 00541 case 32: 00542 for (j = 0; j < wc; j++) { 00543 if (j + x < 0 || j + x >= w) continue; 00544 cval = GET_DATA_BYTE(linec, j); 00545 if (transparent == 0 || 00546 (transparent != 0 && cval != transpix)) { 00547 val32 = *(lined + j + x); 00548 extractRGBValues(val32, &irval, &igval, &ibval); 00549 irval = (l_int32)((1. - fract) * irval + fract * cval); 00550 igval = (l_int32)((1. - fract) * igval + fract * cval); 00551 ibval = (l_int32)((1. - fract) * ibval + fract * cval); 00552 composeRGBPixel(irval, igval, ibval, &val32); 00553 *(lined + j + x) = val32; 00554 } 00555 } 00556 break; 00557 default: 00558 break; /* shouldn't happen */ 00559 } 00560 } 00561 } 00562 else { /* L_BLEND_GRAY_WITH_INVERSE */ 00563 for (i = 0; i < hc; i++) { 00564 if (i + y < 0 || i + y >= h) continue; 00565 linec = datac + i * wplc; 00566 lined = datad + (i + y) * wpld; 00567 switch (d) 00568 { 00569 case 8: 00570 /* 00571 * For 8 bpp, the dest pix is shifted by a signed amount 00572 * proportional to the distance from 128 (the pivot value), 00573 * and to the darkness of src2. If the dest is darker 00574 * than 128, it becomes lighter, and v.v. 00575 * The basic logic is: 00576 * d --> d + f * (0.5 - d) * (1 - c) 00577 * where d and c are normalized pixel values for src1 and 00578 * src2, respectively, with normalization to 255. 00579 */ 00580 for (j = 0; j < wc; j++) { 00581 if (j + x < 0 || j + x >= w) continue; 00582 cval = GET_DATA_BYTE(linec, j); 00583 if (transparent == 0 || 00584 (transparent != 0 && cval != transpix)) { 00585 ival = GET_DATA_BYTE(lined, j + x); 00586 delta = (128 - ival) * (255 - cval) / 256; 00587 ival += (l_int32)(fract * delta + 0.5); 00588 SET_DATA_BYTE(lined, j + x, ival); 00589 } 00590 } 00591 break; 00592 case 32: 00593 /* Each component is shifted by the same formula for 8 bpp */ 00594 for (j = 0; j < wc; j++) { 00595 if (j + x < 0 || j + x >= w) continue; 00596 cval = GET_DATA_BYTE(linec, j); 00597 if (transparent == 0 || 00598 (transparent != 0 && cval != transpix)) { 00599 val32 = *(lined + j + x); 00600 extractRGBValues(val32, &irval, &igval, &ibval); 00601 delta = (128 - irval) * (255 - cval) / 256; 00602 irval += (l_int32)(fract * delta + 0.5); 00603 delta = (128 - igval) * (255 - cval) / 256; 00604 igval += (l_int32)(fract * delta + 0.5); 00605 delta = (128 - ibval) * (255 - cval) / 256; 00606 ibval += (l_int32)(fract * delta + 0.5); 00607 composeRGBPixel(irval, igval, ibval, &val32); 00608 *(lined + j + x) = val32; 00609 } 00610 } 00611 break; 00612 default: 00613 break; /* shouldn't happen */ 00614 } 00615 } 00616 } 00617 00618 pixDestroy(&pixc); 00619 return pixd; 00620 } 00621 00622 00623 /*! 00624 * pixBlendColor() 00625 * 00626 * Input: pixd (<optional>; either NULL or equal to pixs1 for in-place) 00627 * pixs1 (blendee; depth > 1) 00628 * pixs2 (blender, 32 bpp; typ. smaller in size than pixs1) 00629 * x,y (origin (UL corner) of pixs2 relative to 00630 * the origin of pixs1) 00631 * fract (blending fraction) 00632 * transparent (1 to use transparency; 0 otherwise) 00633 * transpix (pixel color in pixs2 that is to be transparent) 00634 * Return: pixd if OK; pixs1 on error 00635 * 00636 * Notes: 00637 * (1) pixs2 must be 32 bpp, and have no colormap. 00638 * (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop. 00639 * (3) If pixs1 has a colormap, it is removed to generate a 32 bpp pix. 00640 * (4) If pixs1 has depth < 32, it is unpacked to generate a 32 bpp pix. 00641 * (5) For inplace operation, call it this way: 00642 * pixBlendColor(pixs1, pixs1, pixs2, ...) 00643 * (6) For generating a new pixd: 00644 * pixd = pixBlendColor(NULL, pixs1, pixs2, ...) 00645 * (7) Only call in-place if pixs1 is 32 bpp; otherwise it is an error. 00646 * (8) If transparent = 0, the blending fraction (fract) is 00647 * applied equally to all pixels. 00648 * (9) If transparent = 1, all pixels of value transpix (typically 00649 * either 0 or 0xffffff00) in pixs2 are transparent in the blend. 00650 */ 00651 PIX * 00652 pixBlendColor(PIX *pixd, 00653 PIX *pixs1, 00654 PIX *pixs2, 00655 l_int32 x, 00656 l_int32 y, 00657 l_float32 fract, 00658 l_int32 transparent, 00659 l_uint32 transpix) 00660 { 00661 l_int32 i, j, wc, hc, w, h, wplc, wpld; 00662 l_int32 rval, gval, bval, rcval, gcval, bcval; 00663 l_uint32 cval32, val32; 00664 l_uint32 *linec, *lined, *datac, *datad; 00665 PIX *pixc, *pixt1, *pixt2; 00666 00667 PROCNAME("pixBlendColor"); 00668 00669 if (!pixs1) 00670 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); 00671 if (!pixs2) 00672 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); 00673 if (pixGetDepth(pixs1) == 1) 00674 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); 00675 if (pixGetDepth(pixs2) != 32) 00676 return (PIX *)ERROR_PTR("pixs2 not 32 bpp", procName, pixd); 00677 if (pixd == pixs1 && pixGetDepth(pixs1) != 32) 00678 return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", procName, pixd); 00679 if (pixd && (pixd != pixs1)) 00680 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); 00681 if (fract < 0.0 || fract > 1.0) { 00682 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5", procName); 00683 fract = 0.5; 00684 } 00685 00686 /* If pixd != NULL, we know that it is equal to pixs1 and 00687 * that pixs1 is 32 bpp rgb, so that an in-place operation 00688 * can be done. Otherwise, remove colormap from pixs1 if 00689 * it exists and unpack to 32 bpp if necessary, to do the 00690 * blending on a new 32 bpp Pix. */ 00691 if (!pixd) { 00692 pixt1 = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR); 00693 if (pixGetDepth(pixt1) < 32) 00694 pixt2 = pixConvertTo32(pixt1); 00695 else 00696 pixt2 = pixClone(pixt1); 00697 pixd = pixCopy(NULL, pixt2); 00698 pixDestroy(&pixt1); 00699 pixDestroy(&pixt2); 00700 } 00701 00702 pixGetDimensions(pixd, &w, &h, NULL); 00703 wpld = pixGetWpl(pixd); 00704 datad = pixGetData(pixd); 00705 pixc = pixClone(pixs2); 00706 pixGetDimensions(pixc, &wc, &hc, NULL); 00707 datac = pixGetData(pixc); 00708 wplc = pixGetWpl(pixc); 00709 00710 /* check limits for src1, in case clipping was not done */ 00711 for (i = 0; i < hc; i++) { 00712 if (i + y < 0 || i + y >= h) continue; 00713 linec = datac + i * wplc; 00714 lined = datad + (i + y) * wpld; 00715 for (j = 0; j < wc; j++) { 00716 if (j + x < 0 || j + x >= w) continue; 00717 cval32 = *(linec + j); 00718 if (transparent == 0 || 00719 (transparent != 0 && 00720 ((cval32 & 0xffffff00) != (transpix & 0xffffff00)))) { 00721 val32 = *(lined + j + x); 00722 extractRGBValues(cval32, &rcval, &gcval, &bcval); 00723 extractRGBValues(val32, &rval, &gval, &bval); 00724 rval = (l_int32)((1. - fract) * rval + fract * rcval); 00725 gval = (l_int32)((1. - fract) * gval + fract * gcval); 00726 bval = (l_int32)((1. - fract) * bval + fract * bcval); 00727 composeRGBPixel(rval, gval, bval, &val32); 00728 *(lined + j + x) = val32; 00729 } 00730 } 00731 } 00732 00733 pixDestroy(&pixc); 00734 return pixd; 00735 } 00736 00737 00738 /* 00739 * pixBlendColorByChannel() 00740 * 00741 * This is an extended version of pixBlendColor. All parameters have the 00742 * same meaning except it takes one mixing fraction per channel, and the 00743 * mixing fraction may be < 0 or > 1, in which case, the min or max of two 00744 * images are taken. More specifically, 00745 * 00746 * for a = pixs1[i], b = pixs2[i]: 00747 * frac < 0.0 --> min(a, b) 00748 * frac > 1.0 --> max(a, b) 00749 * else --> (1-frac)*a + frac*b 00750 * frac == 0 --> a 00751 * frac == 1 --> b 00752 * 00753 * Notes: 00754 * (1) See usage notes in pixBlendColor() 00755 * (2) pixBlendColor() would be equivalent to 00756 * pixBlendColorChannel(..., fract, fract, fract, ...); 00757 * at a small cost of efficiency. 00758 */ 00759 PIX * 00760 pixBlendColorByChannel(PIX *pixd, 00761 PIX *pixs1, 00762 PIX *pixs2, 00763 l_int32 x, 00764 l_int32 y, 00765 l_float32 rfract, 00766 l_float32 gfract, 00767 l_float32 bfract, 00768 l_int32 transparent, 00769 l_uint32 transpix) 00770 { 00771 l_int32 i, j, wc, hc, w, h, wplc, wpld; 00772 l_int32 rval, gval, bval, rcval, gcval, bcval; 00773 l_uint32 cval32, val32; 00774 l_uint32 *linec, *lined, *datac, *datad; 00775 PIX *pixc, *pixt1, *pixt2; 00776 00777 PROCNAME("pixBlendColorByChannel"); 00778 00779 if (!pixs1) 00780 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); 00781 if (!pixs2) 00782 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); 00783 if (pixGetDepth(pixs1) == 1) 00784 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); 00785 if (pixGetDepth(pixs2) != 32) 00786 return (PIX *)ERROR_PTR("pixs2 not 32 bpp", procName, pixd); 00787 if (pixd == pixs1 && pixGetDepth(pixs1) != 32) 00788 return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", procName, pixd); 00789 if (pixd && (pixd != pixs1)) 00790 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); 00791 00792 /* If pixd != NULL, we know that it is equal to pixs1 and 00793 * that pixs1 is 32 bpp rgb, so that an in-place operation 00794 * can be done. Otherwise, remove colormap from pixs1 if 00795 * it exists and unpack to 32 bpp if necessary, to do the 00796 * blending on a new 32 bpp Pix. */ 00797 if (!pixd) { 00798 pixt1 = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR); 00799 if (pixGetDepth(pixt1) < 32) 00800 pixt2 = pixConvertTo32(pixt1); 00801 else 00802 pixt2 = pixClone(pixt1); 00803 pixd = pixCopy(NULL, pixt2); 00804 pixDestroy(&pixt1); 00805 pixDestroy(&pixt2); 00806 } 00807 00808 pixGetDimensions(pixd, &w, &h, NULL); 00809 wpld = pixGetWpl(pixd); 00810 datad = pixGetData(pixd); 00811 pixc = pixClone(pixs2); 00812 pixGetDimensions(pixc, &wc, &hc, NULL); 00813 datac = pixGetData(pixc); 00814 wplc = pixGetWpl(pixc); 00815 00816 /* Check limits for src1, in case clipping was not done */ 00817 for (i = 0; i < hc; i++) { 00818 if (i + y < 0 || i + y >= h) continue; 00819 linec = datac + i * wplc; 00820 lined = datad + (i + y) * wpld; 00821 for (j = 0; j < wc; j++) { 00822 if (j + x < 0 || j + x >= w) continue; 00823 cval32 = *(linec + j); 00824 if (transparent == 0 || 00825 (transparent != 0 && 00826 ((cval32 & 0xffffff00) != (transpix & 0xffffff00)))) { 00827 val32 = *(lined + j + x); 00828 extractRGBValues(cval32, &rcval, &gcval, &bcval); 00829 extractRGBValues(val32, &rval, &gval, &bval); 00830 rval = blendComponents(rval, rcval, rfract); 00831 gval = blendComponents(gval, gcval, gfract); 00832 bval = blendComponents(bval, bcval, bfract); 00833 composeRGBPixel(rval, gval, bval, &val32); 00834 *(lined + j + x) = val32; 00835 } 00836 } 00837 } 00838 00839 pixDestroy(&pixc); 00840 return pixd; 00841 } 00842 00843 00844 static l_int32 00845 blendComponents(l_int32 a, 00846 l_int32 b, 00847 l_float32 fract) 00848 { 00849 if (fract < 0.) 00850 return ((a < b) ? a : b); 00851 if (fract > 1.) 00852 return ((a > b) ? a : b); 00853 return (l_int32)((1. - fract) * a + fract * b); 00854 } 00855 00856 00857 /*! 00858 * pixBlendGrayAdapt() 00859 * 00860 * Input: pixd (<optional>; either NULL or equal to pixs1 for in-place) 00861 * pixs1 (blendee; depth > 1) 00862 * pixs2 (blender, 8 bpp; typ. smaller in size than pixs1) 00863 * x,y (origin (UL corner) of pixs2 relative to 00864 * the origin of pixs1; can be < 0) 00865 * fract (blending fraction) 00866 * shift (>= 0 but <= 128: shift of zero blend value from 00867 * median source; use -1 for default value; ) 00868 * Return: pixd if OK; pixs1 on error 00869 * 00870 * Notes: 00871 * (1) pixs2 must be 8 bpp, and have no colormap. 00872 * (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop. 00873 * (3) If pixs1 has a colormap, it is removed. 00874 * (4) If pixs1 has depth < 8, it is unpacked to generate a 8 bpp pix. 00875 * (5) For inplace operation, call it this way: 00876 * pixBlendGray(pixs1, pixs1, pixs2, ...) 00877 * For generating a new pixd: 00878 * pixd = pixBlendGray(NULL, pixs1, pixs2, ...) 00879 * Only call in-place if pixs1 does not have a colormap; 00880 * otherwise it is an error. 00881 * (6) This does a blend with inverse. Whereas in pixGlendGray(), the 00882 * zero blend point is where the blendee pixel is 128, here 00883 * the zero blend point is found adaptively, with respect to the 00884 * median of the blendee region. If the median is < 128, 00885 * the zero blend point is found from 00886 * median + shift. 00887 * Otherwise, if the median >= 128, the zero blend point is 00888 * median - shift. 00889 * The purpose of shifting the zero blend point away from the 00890 * median is to prevent a situation in pixBlendGray() where 00891 * the median is 128 and the blender is not visible. 00892 * The default value of shift is 64. 00893 * (7) After processing pixs1, it is either 8 bpp or 32 bpp: 00894 * - if 8 bpp, the fraction of pixs2 is mixed with pixs1. 00895 * - if 32 bpp, each component of pixs1 is mixed with 00896 * the same fraction of pixs2. 00897 * (8) The darker the blender, the more it mixes with the blendee. 00898 * A blender value of 0 has maximum mixing; a value of 255 00899 * has no mixing and hence is transparent. 00900 */ 00901 PIX * 00902 pixBlendGrayAdapt(PIX *pixd, 00903 PIX *pixs1, 00904 PIX *pixs2, 00905 l_int32 x, 00906 l_int32 y, 00907 l_float32 fract, 00908 l_int32 shift) 00909 { 00910 l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta, overlap; 00911 l_int32 rval, gval, bval, cval, dval, mval, median, pivot; 00912 l_uint32 val32; 00913 l_uint32 *linec, *lined, *datac, *datad; 00914 l_float32 fmedian, factor; 00915 BOX *box, *boxt; 00916 PIX *pixc, *pixt1, *pixt2; 00917 00918 PROCNAME("pixBlendGrayAdapt"); 00919 00920 if (!pixs1) 00921 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); 00922 if (!pixs2) 00923 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); 00924 if (pixGetDepth(pixs1) == 1) 00925 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); 00926 if (pixGetDepth(pixs2) != 8) 00927 return (PIX *)ERROR_PTR("pixs2 not 8 bpp", procName, pixd); 00928 if (pixGetColormap(pixs2)) 00929 return (PIX *)ERROR_PTR("pixs2 has a colormap", procName, pixd); 00930 if (pixd == pixs1 && pixGetColormap(pixs1)) 00931 return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd); 00932 if (pixd && (pixd != pixs1)) 00933 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd); 00934 if (fract < 0.0 || fract > 1.0) { 00935 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5", procName); 00936 fract = 0.5; 00937 } 00938 if (shift == -1) shift = 64; /* default value */ 00939 if (shift < 0 || shift > 127) { 00940 L_WARNING("invalid shift; setting to 64", procName); 00941 shift = 64; 00942 } 00943 00944 /* Test for overlap */ 00945 pixGetDimensions(pixs1, &w, &h, NULL); 00946 pixGetDimensions(pixs2, &wc, &hc, NULL); 00947 box = boxCreate(x, y, wc, hc); 00948 boxt = boxCreate(0, 0, w, h); 00949 boxIntersects(box, boxt, &overlap); 00950 boxDestroy(&boxt); 00951 if (!overlap) { 00952 boxDestroy(&box); 00953 return (PIX *)ERROR_PTR("no image overlap", procName, pixd); 00954 } 00955 00956 /* If pixd != NULL, we know that it is equal to pixs1 and 00957 * that pixs1 does not have a colormap, so that an in-place operation 00958 * can be done. Otherwise, remove colormap from pixs1 if 00959 * it exists and unpack to at least 8 bpp if necessary, 00960 * to do the blending on a new pix. */ 00961 if (!pixd) { 00962 pixt1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); 00963 if (pixGetDepth(pixt1) < 8) 00964 pixt2 = pixConvertTo8(pixt1, FALSE); 00965 else 00966 pixt2 = pixClone(pixt1); 00967 pixd = pixCopy(NULL, pixt2); 00968 pixDestroy(&pixt1); 00969 pixDestroy(&pixt2); 00970 } 00971 00972 /* Get the median value in the region of blending */ 00973 pixt1 = pixClipRectangle(pixd, box, NULL); 00974 pixt2 = pixConvertTo8(pixt1, 0); 00975 pixGetRankValueMasked(pixt2, NULL, 0, 0, 1, 0.5, &fmedian, NULL); 00976 median = (l_int32)(fmedian + 0.5); 00977 if (median < 128) 00978 pivot = median + shift; 00979 else 00980 pivot = median - shift; 00981 /* L_INFO_INT2("median = %d, pivot = %d", procName, median, pivot); */ 00982 pixDestroy(&pixt1); 00983 pixDestroy(&pixt2); 00984 boxDestroy(&box); 00985 00986 /* Process over src2; clip to src1. */ 00987 d = pixGetDepth(pixd); 00988 wpld = pixGetWpl(pixd); 00989 datad = pixGetData(pixd); 00990 pixc = pixClone(pixs2); 00991 datac = pixGetData(pixc); 00992 wplc = pixGetWpl(pixc); 00993 for (i = 0; i < hc; i++) { 00994 if (i + y < 0 || i + y >= h) continue; 00995 linec = datac + i * wplc; 00996 lined = datad + (i + y) * wpld; 00997 switch (d) 00998 { 00999 case 8: 01000 /* 01001 * For 8 bpp, the dest pix is shifted by an amount 01002 * proportional to the distance from the pivot value, 01003 * and to the darkness of src2. In no situation will it 01004 * pass the pivot value in intensity. 01005 * The basic logic is: 01006 * d --> d + f * (np - d) * (1 - c) 01007 * where np, d and c are normalized pixel values for 01008 * the pivot, src1 and src2, respectively, with normalization 01009 * to 255. 01010 */ 01011 for (j = 0; j < wc; j++) { 01012 if (j + x < 0 || j + x >= w) continue; 01013 dval = GET_DATA_BYTE(lined, j + x); 01014 cval = GET_DATA_BYTE(linec, j); 01015 delta = (pivot - dval) * (255 - cval) / 256; 01016 dval += (l_int32)(fract * delta + 0.5); 01017 SET_DATA_BYTE(lined, j + x, dval); 01018 } 01019 break; 01020 case 32: 01021 /* 01022 * For 32 bpp, the dest pix is shifted by an amount 01023 * proportional to the max component distance from the 01024 * pivot value, and to the darkness of src2. Each component 01025 * is shifted by the same fraction, either up or down, 01026 * depending on the shift direction (which is toward the 01027 * pivot). The basic logic for the red component is: 01028 * r --> r + f * (np - m) * (1 - c) * (r / m) 01029 * where np, r, m and c are normalized pixel values for 01030 * the pivot, the r component of src1, the max component 01031 * of src1, and src2, respectively, again with normalization 01032 * to 255. Likewise for the green and blue components. 01033 */ 01034 for (j = 0; j < wc; j++) { 01035 if (j + x < 0 || j + x >= w) continue; 01036 cval = GET_DATA_BYTE(linec, j); 01037 val32 = *(lined + j + x); 01038 extractRGBValues(val32, &rval, &gval, &bval); 01039 mval = L_MAX(rval, gval); 01040 mval = L_MAX(mval, bval); 01041 mval = L_MAX(mval, 1); 01042 delta = (pivot - mval) * (255 - cval) / 256; 01043 factor = fract * delta / mval; 01044 rval += (l_int32)(factor * rval + 0.5); 01045 gval += (l_int32)(factor * gval + 0.5); 01046 bval += (l_int32)(factor * bval + 0.5); 01047 composeRGBPixel(rval, gval, bval, &val32); 01048 *(lined + j + x) = val32; 01049 } 01050 break; 01051 default: 01052 break; /* shouldn't happen */ 01053 } 01054 } 01055 01056 pixDestroy(&pixc); 01057 return pixd; 01058 } 01059 01060 01061 /*! 01062 * pixFadeWithGray() 01063 * 01064 * Input: pixs (colormapped or 8 bpp or 32 bpp) 01065 * pixb (8 bpp blender) 01066 * factor (multiplicative factor to apply to blender value) 01067 * type (L_BLEND_TO_WHITE, L_BLEND_TO_BLACK) 01068 * Return: pixd, or null on error 01069 * 01070 * Notes: 01071 * (1) This function combines two pix aligned to the UL corner; they 01072 * need not be the same size. 01073 * (2) Each pixel in pixb is multiplied by 'factor' divided by 255, and 01074 * clipped to the range [0 ... 1]. This gives the fade fraction 01075 * to be appied to pixs. Fade either to white (L_BLEND_TO_WHITE) 01076 * or to black (L_BLEND_TO_BLACK). 01077 */ 01078 PIX * 01079 pixFadeWithGray(PIX *pixs, 01080 PIX *pixb, 01081 l_float32 factor, 01082 l_int32 type) 01083 { 01084 l_int32 i, j, w, h, d, wb, hb, db, wd, hd, wplb, wpld; 01085 l_int32 valb, vald, nvald, rval, gval, bval, nrval, ngval, nbval; 01086 l_float32 nfactor, fract; 01087 l_uint32 val32, nval32; 01088 l_uint32 *lined, *datad, *lineb, *datab; 01089 PIX *pixd; 01090 PIXCMAP *cmap; 01091 01092 PROCNAME("pixFadeWithGray"); 01093 01094 if (!pixs) 01095 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 01096 if (!pixb) 01097 return (PIX *)ERROR_PTR("pixb not defined", procName, NULL); 01098 cmap = pixGetColormap(pixs); 01099 d = pixGetDepth(pixs); 01100 if (d < 8 && !cmap) 01101 return (PIX *)ERROR_PTR("pixs not cmapped and < 8bpp", procName, NULL); 01102 pixGetDimensions(pixb, &wb, &hb, &db); 01103 if (db != 8) 01104 return (PIX *)ERROR_PTR("pixb not 8bpp", procName, NULL); 01105 if (type != L_BLEND_TO_WHITE && type != L_BLEND_TO_BLACK) 01106 return (PIX *)ERROR_PTR("invalid fade type", procName, NULL); 01107 01108 if (cmap) 01109 pixd = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); 01110 else 01111 pixd = pixCopy(NULL, pixs); 01112 pixGetDimensions(pixd, &wd, &hd, &d); 01113 w = L_MIN(wb, wd); 01114 h = L_MIN(hb, hd); 01115 datad = pixGetData(pixd); 01116 wpld = pixGetWpl(pixd); 01117 datab = pixGetData(pixb); 01118 wplb = pixGetWpl(pixb); 01119 01120 nfactor = factor / 255.; 01121 for (i = 0; i < h; i++) { 01122 lineb = datab + i * wplb; 01123 lined = datad + i * wpld; 01124 for (j = 0; j < w; j++) { 01125 valb = GET_DATA_BYTE(lineb, j); 01126 fract = nfactor * (l_float32)valb; 01127 fract = L_MIN(fract, 1.0); 01128 if (d == 8) { 01129 vald = GET_DATA_BYTE(lined, j); 01130 if (type == L_BLEND_TO_WHITE) 01131 nvald = vald + (l_int32)(fract * (255. - (l_float32)vald)); 01132 else /* L_BLEND_TO_BLACK */ 01133 nvald = vald - (l_int32)(fract * (l_float32)vald); 01134 SET_DATA_BYTE(lined, j, nvald); 01135 } 01136 else { /* d == 32 */ 01137 val32 = lined[j]; 01138 extractRGBValues(val32, &rval, &gval, &bval); 01139 if (type == L_BLEND_TO_WHITE) { 01140 nrval = rval + (l_int32)(fract * (255. - (l_float32)rval)); 01141 ngval = gval + (l_int32)(fract * (255. - (l_float32)gval)); 01142 nbval = bval + (l_int32)(fract * (255. - (l_float32)bval)); 01143 } 01144 else { 01145 nrval = rval - (l_int32)(fract * (l_float32)rval); 01146 ngval = gval - (l_int32)(fract * (l_float32)gval); 01147 nbval = bval - (l_int32)(fract * (l_float32)bval); 01148 } 01149 composeRGBPixel(nrval, ngval, nbval, &nval32); 01150 lined[j] = nval32; 01151 } 01152 } 01153 } 01154 01155 return pixd; 01156 } 01157 01158 01159 /* 01160 * pixBlendHardLight() 01161 * 01162 * Input: pixd (<optional>; either NULL or equal to pixs1 for in-place) 01163 * pixs1 (blendee; depth > 1, may be cmapped) 01164 * pixs2 (blender, 8 or 32 bpp; may be colormapped; 01165 * typ. smaller in size than pixs1) 01166 * x,y (origin (UL corner) of pixs2 relative to 01167 * the origin of pixs1) 01168 * fract (blending fraction, or 'opacity factor') 01169 * Return: pixd if OK; pixs1 on error 01170 * 01171 * Notes: 01172 * (1) pixs2 must be 8 or 32 bpp; either may have a colormap. 01173 * (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop. 01174 * (3) Only call in-place if pixs1 is not colormapped. 01175 * (4) If pixs1 has a colormap, it is removed to generate either an 01176 * 8 or 32 bpp pix, depending on the colormap. 01177 * (5) For inplace operation, call it this way: 01178 * pixBlendHardLight(pixs1, pixs1, pixs2, ...) 01179 * (6) For generating a new pixd: 01180 * pixd = pixBlendHardLight(NULL, pixs1, pixs2, ...) 01181 * (7) This is a generalization of the usual hard light blending, 01182 * where fract == 1.0. 01183 * (8) When the opacity factor fract = 1.0, this implements "overlay" 01184 * blending, by swapping pixs1 and pixs2. 01185 * (9) See, e.g.: 01186 * http://www.pegtop.net/delphi/articles/blendmodes/hardlight.htm 01187 * http://www.digitalartform.com/imageArithmetic.htm 01188 * (10) This function was built by Paco Galanes. 01189 */ 01190 PIX * 01191 pixBlendHardLight(PIX *pixd, 01192 PIX *pixs1, 01193 PIX *pixs2, 01194 l_int32 x, 01195 l_int32 y, 01196 l_float32 fract) 01197 { 01198 l_int32 i, j, w, h, d, wc, hc, dc, wplc, wpld; 01199 l_int32 cval, dval, rcval, gcval, bcval, rdval, gdval, bdval; 01200 l_uint32 cval32, dval32; 01201 l_uint32 *linec, *lined, *datac, *datad; 01202 PIX *pixc, *pixt; 01203 01204 PROCNAME("pixBlendHardLight"); 01205 01206 if (!pixs1) 01207 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd); 01208 if (!pixs2) 01209 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd); 01210 pixGetDimensions(pixs1, &w, &h, &d); 01211 pixGetDimensions(pixs2, &wc, &hc, &dc); 01212 if (d == 1) 01213 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd); 01214 if (dc != 8 && dc != 32) 01215 return (PIX *)ERROR_PTR("pixs2 not 8 or 32 bpp", procName, pixd); 01216 if (pixd && (pixd != pixs1)) 01217 return (PIX *)ERROR_PTR("inplace and pixd != pixs1", procName, pixd); 01218 if (pixd == pixs1 && pixGetColormap(pixs1)) 01219 return (PIX *)ERROR_PTR("inplace and pixs1 cmapped", procName, pixd); 01220 if (pixd && d != 8 && d != 32) 01221 return (PIX *)ERROR_PTR("inplace and not 8 or 32 bpp", procName, pixd); 01222 01223 if (fract < 0.0 || fract > 1.0) { 01224 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5", procName); 01225 fract = 0.5; 01226 } 01227 01228 /* If pixs2 has a colormap, remove it */ 01229 if (pixGetColormap(pixs2)) 01230 pixc = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); 01231 else 01232 pixc = pixClone(pixs2); 01233 dc = pixGetDepth(pixc); 01234 01235 /* There are 4 cases: 01236 * * pixs1 has or doesn't have a colormap 01237 * * pixc is either 8 or 32 bpp 01238 * In all situations, if pixs has a colormap it must be removed, 01239 * and pixd must have a depth that is equal to or greater than pixc. */ 01240 if (dc == 32) { 01241 if (pixGetColormap(pixs1)) /* pixd == NULL */ 01242 pixd = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR); 01243 else { 01244 if (!pixd) 01245 pixd = pixConvertTo32(pixs1); 01246 else { 01247 pixt = pixConvertTo32(pixs1); 01248 pixCopy(pixd, pixt); 01249 pixDestroy(&pixt); 01250 } 01251 } 01252 d = 32; 01253 } else { /* dc == 8 */ 01254 if (pixGetColormap(pixs1)) /* pixd == NULL */ 01255 pixd = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); 01256 else 01257 pixd = pixCopy(pixd, pixs1); 01258 d = pixGetDepth(pixd); 01259 } 01260 01261 if (!(d == 8 && dc == 8) && /* 3 cases only */ 01262 !(d == 32 && dc == 8) && 01263 !(d == 32 && dc == 32)) { 01264 pixDestroy(&pixc); 01265 return (PIX *)ERROR_PTR("bad! -- invalid depth combo!", procName, pixd); 01266 } 01267 01268 wpld = pixGetWpl(pixd); 01269 datad = pixGetData(pixd); 01270 datac = pixGetData(pixc); 01271 wplc = pixGetWpl(pixc); 01272 for (i = 0; i < hc; i++) { 01273 if (i + y < 0 || i + y >= h) continue; 01274 linec = datac + i * wplc; 01275 lined = datad + (i + y) * wpld; 01276 for (j = 0; j < wc; j++) { 01277 if (j + x < 0 || j + x >= w) continue; 01278 if (d == 8 && dc == 8) { 01279 dval = GET_DATA_BYTE(lined, x + j); 01280 cval = GET_DATA_BYTE(linec, j); 01281 dval = blendHardLightComponents(dval, cval, fract); 01282 SET_DATA_BYTE(lined, x + j, dval); 01283 } else if (d == 32 && dc == 8) { 01284 dval32 = *(lined + x + j); 01285 extractRGBValues(dval32, &rdval, &gdval, &bdval); 01286 cval = GET_DATA_BYTE(linec, j); 01287 rdval = blendHardLightComponents(rdval, cval, fract); 01288 gdval = blendHardLightComponents(gdval, cval, fract); 01289 bdval = blendHardLightComponents(bdval, cval, fract); 01290 composeRGBPixel(rdval, gdval, bdval, &dval32); 01291 *(lined + x + j) = dval32; 01292 } else if (d == 32 && dc == 32) { 01293 dval32 = *(lined + x + j); 01294 extractRGBValues(dval32, &rdval, &gdval, &bdval); 01295 cval32 = *(linec + j); 01296 extractRGBValues(cval32, &rcval, &gcval, &bcval); 01297 rdval = blendHardLightComponents(rdval, rcval, fract); 01298 gdval = blendHardLightComponents(gdval, gcval, fract); 01299 bdval = blendHardLightComponents(bdval, bcval, fract); 01300 composeRGBPixel(rdval, gdval, bdval, &dval32); 01301 *(lined + x + j) = dval32; 01302 } 01303 } 01304 } 01305 01306 pixDestroy(&pixc); 01307 return pixd; 01308 } 01309 01310 01311 /* 01312 * blendHardLightComponents() 01313 * Input: a (8 bpp blendee component) 01314 * b (8 bpp blender component) 01315 * fract (fraction of blending; use 1.0 for usual definition) 01316 * Return: blended 8 bpp component 01317 */ 01318 static l_int32 blendHardLightComponents(l_int32 a, 01319 l_int32 b, 01320 l_float32 fract) 01321 { 01322 if (b < 0x80) { 01323 b = 0x80 - (l_int32)(fract * (0x80 - b)); 01324 return (a * b) >> 7; 01325 } else { 01326 b = 0x80 + (l_int32)(fract * (b - 0x80)); 01327 return 0xff - (((0xff - b) * (0xff - a)) >> 7); 01328 } 01329 } 01330 01331 01332 /*-------------------------------------------------------------* 01333 * Blending two colormapped images * 01334 *-------------------------------------------------------------*/ 01335 /*! 01336 * pixBlendCmap() 01337 * 01338 * Input: pixs (2, 4 or 8 bpp, with colormap) 01339 * pixb (colormapped blender) 01340 * x, y (UL corner of blender relative to pixs) 01341 * sindex (colormap index of pixels in pixs to be changed) 01342 * Return: 0 if OK, 1 on error 01343 * 01344 * Note: 01345 * (1) This function combines two colormaps, and replaces the pixels 01346 * in pixs that have a specified color value with those in pixb. 01347 * (2) sindex must be in the existing colormap; otherwise an 01348 * error is returned. In use, sindex will typically be the index 01349 * for white (255, 255, 255). 01350 * (3) Blender colors that already exist in the colormap are used; 01351 * others are added. If any blender colors cannot be 01352 * stored in the colormap, an error is returned. 01353 * (4) In the implementation, a mapping is generated from each 01354 * original blender colormap index to the corresponding index 01355 * in the expanded colormap for pixs. Then for each pixel in 01356 * pixs with value sindex, and which is covered by a blender pixel, 01357 * the new index corresponding to the blender pixel is substituted 01358 * for sindex. 01359 */ 01360 l_int32 01361 pixBlendCmap(PIX *pixs, 01362 PIX *pixb, 01363 l_int32 x, 01364 l_int32 y, 01365 l_int32 sindex) 01366 { 01367 l_int32 rval, gval, bval; 01368 l_int32 i, j, w, h, d, ncb, wb, hb, wpls; 01369 l_int32 index, val, nadded; 01370 l_int32 lut[256]; 01371 l_uint32 pval; 01372 l_uint32 *lines, *datas; 01373 PIXCMAP *cmaps, *cmapb, *cmapsc; 01374 01375 PROCNAME("pixBlendCmap"); 01376 01377 if (!pixs) 01378 return ERROR_INT("pixs not defined", procName, 1); 01379 if (!pixb) 01380 return ERROR_INT("pixb not defined", procName, 1); 01381 if ((cmaps = pixGetColormap(pixs)) == NULL) 01382 return ERROR_INT("no colormap in pixs", procName, 1); 01383 if ((cmapb = pixGetColormap(pixb)) == NULL) 01384 return ERROR_INT("no colormap in pixb", procName, 1); 01385 ncb = pixcmapGetCount(cmapb); 01386 01387 /* Make a copy of cmaps; we'll add to this if necessary 01388 * and substitute at the end if we found there was enough room 01389 * to hold all the new colors. */ 01390 cmapsc = pixcmapCopy(cmaps); 01391 01392 pixGetDimensions(pixs, &w, &h, &d); 01393 if (d != 2 && d != 4 && d != 8) 01394 return ERROR_INT("depth not in {2,4,8}", procName, 1); 01395 01396 /* Add new colors if necessary; get mapping array between 01397 * cmaps and cmapb. */ 01398 for (i = 0, nadded = 0; i < ncb; i++) { 01399 pixcmapGetColor(cmapb, i, &rval, &gval, &bval); 01400 if (pixcmapGetIndex(cmapsc, rval, gval, bval, &index)) { /* not found */ 01401 if (pixcmapAddColor(cmapsc, rval, gval, bval)) { 01402 pixcmapDestroy(&cmapsc); 01403 return ERROR_INT("not enough room in cmaps", procName, 1); 01404 } 01405 lut[i] = pixcmapGetCount(cmapsc) - 1; 01406 nadded++; 01407 } 01408 else 01409 lut[i] = index; 01410 } 01411 01412 /* Replace cmaps if colors have been added. */ 01413 if (nadded == 0) 01414 pixcmapDestroy(&cmapsc); 01415 else 01416 pixSetColormap(pixs, cmapsc); 01417 01418 /* Replace each pixel value sindex by mapped colormap index when 01419 * a blender pixel in pixbc overlays it. */ 01420 datas = pixGetData(pixs); 01421 wpls = pixGetWpl(pixs); 01422 pixGetDimensions(pixb, &wb, &hb, NULL); 01423 for (i = 0; i < hb; i++) { 01424 if (i + y < 0 || i + y >= h) continue; 01425 lines = datas + (y + i) * wpls; 01426 for (j = 0; j < wb; j++) { 01427 if (j + x < 0 || j + x >= w) continue; 01428 switch (d) { 01429 case 2: 01430 val = GET_DATA_DIBIT(lines, x + j); 01431 if (val == sindex) { 01432 pixGetPixel(pixb, j, i, &pval); 01433 SET_DATA_DIBIT(lines, x + j, lut[pval]); 01434 } 01435 break; 01436 case 4: 01437 val = GET_DATA_QBIT(lines, x + j); 01438 if (val == sindex) { 01439 pixGetPixel(pixb, j, i, &pval); 01440 SET_DATA_QBIT(lines, x + j, lut[pval]); 01441 } 01442 break; 01443 case 8: 01444 val = GET_DATA_BYTE(lines, x + j); 01445 if (val == sindex) { 01446 pixGetPixel(pixb, j, i, &pval); 01447 SET_DATA_BYTE(lines, x + j, lut[pval]); 01448 } 01449 break; 01450 default: 01451 return ERROR_INT("depth not in {2,4,8}", procName, 1); 01452 } 01453 } 01454 } 01455 01456 return 0; 01457 } 01458 01459 01460 /*---------------------------------------------------------------------* 01461 * Blending two images using a third * 01462 *---------------------------------------------------------------------*/ 01463 /*! 01464 * pixBlendWithGrayMask() 01465 * 01466 * Input: pixs1 (8 bpp gray, rgb or colormapped) 01467 * pixs2 (8 bpp gray, rgb or colormapped) 01468 * pixg (8 bpp gray, for transparency of pixs2; can be null) 01469 * x, y (UL corner of pixg with respect to pixs1) 01470 * Return: pixd (blended image), or null on error 01471 * 01472 * Notes: 01473 * (1) The result is 8 bpp grayscale if both pixs1 and pixs2 are 01474 * 8 bpp gray. Otherwise, the result is 32 bpp rgb. 01475 * (2) pixg is an 8 bpp transparency image, where 0 is transparent 01476 * and 255 is opaque. It determines the transparency of pixs2 01477 * when applied over pixs1. It can be null if pixs2 is rgb, 01478 * in which case we use the alpha component of pixs2. 01479 * (3) If pixg exists, both it and pixs2 must be the same size, 01480 * and they are applied with both their UL corners at the 01481 * location (x, y) in pixs1. 01482 * (4) The pixels in pixd are a combination of those in pixs1 01483 * and pixs2, where the amount from pixs2 is proportional to 01484 * the value of the pixel (p) in pixg, and the amount from pixs1 01485 * is proportional to (255 - p). Thus pixg is a transparency 01486 * image (usually called an alpha blender) where each pixel 01487 * can be associated with a pixel in pixs2, and determines 01488 * the amount of the pixs2 pixel in the final result. 01489 * For example, if pixg is all 0, pixs2 is transparent and 01490 * the result in pixd is simply pixs1. 01491 * (5) A typical use is for the pixs2/pixg combination to be 01492 * a small watermark that is applied to pixs1. 01493 */ 01494 PIX * 01495 pixBlendWithGrayMask(PIX *pixs1, 01496 PIX *pixs2, 01497 PIX *pixg, 01498 l_int32 x, 01499 l_int32 y) 01500 { 01501 l_int32 w1, h1, d1, w2, h2, d2, wg, hg, wmin, hmin, wpld, wpls, wplg; 01502 l_int32 i, j, val, dval, sval; 01503 l_int32 drval, dgval, dbval, srval, sgval, sbval; 01504 l_uint32 dval32, sval32; 01505 l_uint32 *datad, *datas, *datag, *lined, *lines, *lineg; 01506 l_float32 fract; 01507 PIX *pixr1, *pixr2, *pix1, *pix2, *pixalpha, *pixd; 01508 01509 PROCNAME("pixBlendWithGrayMask"); 01510 01511 if (!pixs1) 01512 return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL); 01513 if (!pixs2) 01514 return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL); 01515 pixGetDimensions(pixs1, &w1, &h1, &d1); 01516 pixGetDimensions(pixs2, &w2, &h2, &d2); 01517 if (d1 == 1 || d2 == 1) 01518 return (PIX *)ERROR_PTR("pixs1 or pixs2 is 1 bpp", procName, NULL); 01519 if (pixg) { 01520 if (pixGetDepth(pixg) != 8) 01521 return (PIX *)ERROR_PTR("pixg not 8 bpp", procName, NULL); 01522 pixGetDimensions(pixg, &wg, &hg, NULL); 01523 wmin = L_MIN(w2, wg); 01524 hmin = L_MIN(h2, hg); 01525 pixalpha = pixClone(pixg); 01526 } 01527 else { /* use the alpha component of pixs2 */ 01528 if (d2 != 32) 01529 return (PIX *)ERROR_PTR("no alpha; pixs2 not rgba", procName, NULL); 01530 wmin = w2; 01531 hmin = h2; 01532 pixalpha = pixGetRGBComponent(pixs2, L_ALPHA_CHANNEL); 01533 } 01534 01535 /* Remove colormaps if they exist */ 01536 if (pixGetColormap(pixs1)) 01537 pixr1 = pixRemoveColormap(pixs1, REMOVE_CMAP_BASED_ON_SRC); 01538 else 01539 pixr1 = pixClone(pixs1); 01540 if (pixGetColormap(pixs2)) 01541 pixr2 = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); 01542 else 01543 pixr2 = pixClone(pixs2); 01544 01545 /* Regularize to the same depth if necessary */ 01546 d1 = pixGetDepth(pixr1); 01547 d2 = pixGetDepth(pixr2); 01548 if (d1 == 32) { /* convert d2 to rgb if necessary */ 01549 pix1 = pixClone(pixr1); 01550 if (d2 != 32) 01551 pix2 = pixConvertTo32(pixr2); 01552 else 01553 pix2 = pixClone(pixr2); 01554 } 01555 else if (d2 == 32) { /* and d1 != 32; convert to 32 */ 01556 pix2 = pixClone(pixr2); 01557 pix1 = pixConvertTo32(pixr1); 01558 } 01559 else { /* both are 8 bpp or less */ 01560 pix1 = pixConvertTo8(pixr1, FALSE); 01561 pix2 = pixConvertTo8(pixr2, FALSE); 01562 } 01563 pixDestroy(&pixr1); 01564 pixDestroy(&pixr2); 01565 01566 /* Sanity check */ 01567 d1 = pixGetDepth(pix1); 01568 d2 = pixGetDepth(pix2); 01569 if (d1 != d2) { 01570 pixDestroy(&pix1); 01571 pixDestroy(&pix2); 01572 return (PIX *)ERROR_PTR("depths not regularized! bad!", procName, NULL); 01573 } 01574 01575 /* Start off with a copy of pix1. Then only change 01576 * pixels that will be blended with pix2. */ 01577 pixd = pixCopy(NULL, pix1); 01578 pixDestroy(&pix1); 01579 01580 /* 01581 * Let the normalized pixel value of pixg be f = pixval / 255, 01582 * and the pixel values of pixs1 and pixs2 be p1 and p2, rsp. 01583 * Then the blended value is: 01584 * p = (1.0 - f) * p1 + f * p2 01585 * Blending is done component-wise if rgb. 01586 * 01587 * Scan over pixs2 and pixg, doing clipping on pixs1 where necessary. 01588 */ 01589 datad = pixGetData(pixd); 01590 datas = pixGetData(pix2); 01591 datag = pixGetData(pixalpha); 01592 wpld = pixGetWpl(pixd); 01593 wpls = pixGetWpl(pix2); 01594 wplg = pixGetWpl(pixalpha); 01595 for (i = 0; i < hmin; i++) { 01596 if (i + y < 0 || i + y >= h1) continue; 01597 lined = datad + (i + y) * wpld; 01598 lines = datas + i * wpls; 01599 lineg = datag + i * wplg; 01600 for (j = 0; j < wmin; j++) { 01601 if (j + x < 0 || j + x >= w1) continue; 01602 val = GET_DATA_BYTE(lineg, j); 01603 if (val == 0) continue; /* pix2 is transparent */ 01604 fract = (l_float32)val / 255.; 01605 switch (d1) 01606 { 01607 case 8: 01608 dval = GET_DATA_BYTE(lined, j + x); 01609 sval = GET_DATA_BYTE(lines, j); 01610 dval = (l_int32)((1.0 - fract) * dval + fract * sval); 01611 SET_DATA_BYTE(lined, j + x, dval); 01612 break; 01613 case 32: 01614 dval32 = *(lined + j + x); 01615 sval32 = *(lines + j); 01616 extractRGBValues(dval32, &drval, &dgval, &dbval); 01617 extractRGBValues(sval32, &srval, &sgval, &sbval); 01618 drval = (l_int32)((1.0 - fract) * drval + fract * srval); 01619 dgval = (l_int32)((1.0 - fract) * dgval + fract * sgval); 01620 dbval = (l_int32)((1.0 - fract) * dbval + fract * sbval); 01621 composeRGBPixel(drval, dgval, dbval, &dval32); 01622 *(lined + j + x) = dval32; 01623 break; 01624 default: 01625 return (PIX *)ERROR_PTR("impossible error", procName, NULL); 01626 } 01627 } 01628 } 01629 01630 pixDestroy(&pixalpha); 01631 pixDestroy(&pix2); 01632 return pixd; 01633 } 01634 01635 01636 /*---------------------------------------------------------------------* 01637 * Coloring "gray" pixels * 01638 *---------------------------------------------------------------------*/ 01639 /*! 01640 * pixColorGray() 01641 * 01642 * Input: pixs (8 bpp gray, rgb or colormapped image) 01643 * box (<optional> region in which to apply color; can be NULL) 01644 * type (L_PAINT_LIGHT, L_PAINT_DARK) 01645 * thresh (average value below/above which pixel is unchanged) 01646 * rval, gval, bval (new color to paint) 01647 * Return: 0 if OK; 1 on error 01648 * 01649 * Notes: 01650 * (1) This is an in-place operation; pixs is modified. 01651 * If pixs is colormapped, the operation will add colors to the 01652 * colormap. Otherwise, pixs will be converted to 32 bpp rgb if 01653 * it is initially 8 bpp gray. 01654 * (2) If type == L_PAINT_LIGHT, it colorizes non-black pixels, 01655 * preserving antialiasing. 01656 * If type == L_PAINT_DARK, it colorizes non-white pixels, 01657 * preserving antialiasing. 01658 * (3) If box is NULL, applies function to the entire image; otherwise, 01659 * clips the operation to the intersection of the box and pix. 01660 * (4) If colormapped, calls pixColorGrayCmap(), which applies the 01661 * coloring algorithm only to pixels that are strictly gray. 01662 * (5) For RGB, determines a "gray" value by averaging; then uses this 01663 * value, plus the input rgb target, to generate the output 01664 * pixel values. 01665 * (6) thresh is only used for rgb; it is ignored for colormapped pix. 01666 * If type == L_PAINT_LIGHT, use thresh = 0 if all pixels are to 01667 * be colored (black pixels will be unaltered). 01668 * In situations where there are a lot of black pixels, 01669 * setting thresh > 0 will make the function considerably 01670 * more efficient without affecting the final result. 01671 * If type == L_PAINT_DARK, use thresh = 255 if all pixels 01672 * are to be colored (white pixels will be unaltered). 01673 * In situations where there are a lot of white pixels, 01674 * setting thresh < 255 will make the function considerably 01675 * more efficient without affecting the final result. 01676 */ 01677 l_int32 01678 pixColorGray(PIX *pixs, 01679 BOX *box, 01680 l_int32 type, 01681 l_int32 thresh, 01682 l_int32 rval, 01683 l_int32 gval, 01684 l_int32 bval) 01685 { 01686 l_int32 i, j, w, h, d, wpl, x1, x2, y1, y2, bw, bh; 01687 l_int32 nrval, ngval, nbval, aveval; 01688 l_float32 factor; 01689 l_uint32 val32; 01690 l_uint32 *line, *data; 01691 PIX *pixt; 01692 PIXCMAP *cmap; 01693 01694 PROCNAME("pixColorGray"); 01695 01696 if (!pixs) 01697 return ERROR_INT("pixs not defined", procName, 1); 01698 if (type != L_PAINT_LIGHT && type != L_PAINT_DARK) 01699 return ERROR_INT("invalid type", procName, 1); 01700 01701 cmap = pixGetColormap(pixs); 01702 pixGetDimensions(pixs, &w, &h, &d); 01703 if (!cmap && d != 8 && d != 32) 01704 return ERROR_INT("pixs not cmapped, 8 bpp or rgb", procName, 1); 01705 if (cmap) 01706 return pixColorGrayCmap(pixs, box, type, rval, gval, bval); 01707 01708 /* rgb or 8 bpp gray image; check the thresh */ 01709 if (type == L_PAINT_LIGHT) { /* thresh should be low */ 01710 if (thresh >= 255) 01711 return ERROR_INT("thresh must be < 255; else this is a no-op", 01712 procName, 1); 01713 if (thresh > 127) 01714 L_WARNING("threshold set very high", procName); 01715 } 01716 else { /* type == L_PAINT_DARK; thresh should be high */ 01717 if (thresh <= 0) 01718 return ERROR_INT("thresh must be > 0; else this is a no-op", 01719 procName, 1); 01720 if (thresh < 128) 01721 L_WARNING("threshold set very low", procName); 01722 } 01723 01724 if (d == 8) { 01725 pixt = pixConvertTo32(pixs); 01726 pixTransferAllData(pixs, &pixt, 1, 0); 01727 } 01728 01729 if (!box) { 01730 x1 = y1 = 0; 01731 x2 = w; 01732 y2 = h; 01733 } 01734 else { 01735 boxGetGeometry(box, &x1, &y1, &bw, &bh); 01736 x2 = x1 + bw - 1; 01737 y2 = y1 + bh - 1; 01738 } 01739 01740 data = pixGetData(pixs); 01741 wpl = pixGetWpl(pixs); 01742 factor = 1. / 255.; 01743 for (i = y1; i <= y2; i++) { 01744 if (i < 0 || i >= h) 01745 continue; 01746 line = data + i * wpl; 01747 for (j = x1; j <= x2; j++) { 01748 if (j < 0 || j >= w) 01749 continue; 01750 val32 = *(line + j); 01751 aveval = ((val32 >> 24) + ((val32 >> 16) & 0xff) + 01752 ((val32 >> 8) & 0xff)) / 3; 01753 if (type == L_PAINT_LIGHT) { 01754 if (aveval < thresh) /* skip sufficiently dark pixels */ 01755 continue; 01756 nrval = (l_int32)(rval * aveval * factor); 01757 ngval = (l_int32)(gval * aveval * factor); 01758 nbval = (l_int32)(bval * aveval * factor); 01759 } 01760 else { /* type == L_PAINT_DARK */ 01761 if (aveval > thresh) /* skip sufficiently light pixels */ 01762 continue; 01763 nrval = rval + (l_int32)((255. - rval) * aveval * factor); 01764 ngval = gval + (l_int32)((255. - gval) * aveval * factor); 01765 nbval = bval + (l_int32)((255. - bval) * aveval * factor); 01766 } 01767 composeRGBPixel(nrval, ngval, nbval, &val32); 01768 *(line + j) = val32; 01769 } 01770 } 01771 01772 return 0; 01773 } 01774 01775 01776 01777 /*------------------------------------------------------------------* 01778 * Adjusting one or more colors to a target color * 01779 *------------------------------------------------------------------*/ 01780 /*! 01781 * pixSnapColor() 01782 * 01783 * Input: pixd (<optional>; either NULL or equal to pixs for in-place) 01784 * pixs (colormapped or 8 bpp gray or 32 bpp rgb) 01785 * srcval (color center to be selected for change: 0xrrggbb00) 01786 * dstval (target color for pixels: 0xrrggbb00) 01787 * diff (max absolute difference, applied to all components) 01788 * Return: pixd (with all pixels within diff of pixval set to pixval), 01789 * or pixd on error 01790 * 01791 * Notes: 01792 * (1) For inplace operation, call it this way: 01793 * pixSnapColor(pixs, pixs, ... ) 01794 * (2) For generating a new pixd: 01795 * pixd = pixSnapColor(NULL, pixs, ...) 01796 * (3) If pixs has a colormap, it is handled by pixSnapColorCmap(). 01797 * (4) All pixels within 'diff' of 'srcval', componentwise, 01798 * will be changed to 'dstval'. 01799 */ 01800 PIX * 01801 pixSnapColor(PIX *pixd, 01802 PIX *pixs, 01803 l_uint32 srcval, 01804 l_uint32 dstval, 01805 l_int32 diff) 01806 { 01807 l_int32 val, sval, dval; 01808 l_int32 rval, gval, bval, rsval, gsval, bsval; 01809 l_int32 i, j, w, h, d, wpl; 01810 l_uint32 pixel; 01811 l_uint32 *line, *data; 01812 01813 PROCNAME("pixSnapColor"); 01814 01815 if (!pixs) 01816 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01817 if (pixd && (pixd != pixs)) 01818 return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); 01819 01820 if (pixGetColormap(pixs)) 01821 return pixSnapColorCmap(pixd, pixs, srcval, dstval, diff); 01822 01823 /* pixs does not have a colormap; it must be 8 bpp gray or 01824 * 32 bpp rgb. */ 01825 if (pixGetDepth(pixs) < 8) 01826 return (PIX *)ERROR_PTR("pixs is < 8 bpp", procName, pixd); 01827 01828 /* Do the work on pixd */ 01829 if (!pixd) 01830 pixd = pixCopy(NULL, pixs); 01831 01832 pixGetDimensions(pixd, &w, &h, &d); 01833 data = pixGetData(pixd); 01834 wpl = pixGetWpl(pixd); 01835 if (d == 8) { 01836 sval = srcval & 0xff; 01837 dval = dstval & 0xff; 01838 for (i = 0; i < h; i++) { 01839 line = data + i * wpl; 01840 for (j = 0; j < w; j++) { 01841 val = GET_DATA_BYTE(line, j); 01842 if (L_ABS(val - sval) <= diff) 01843 SET_DATA_BYTE(line, j, dval); 01844 } 01845 } 01846 } 01847 else { /* d == 32 */ 01848 extractRGBValues(srcval, &rsval, &gsval, &bsval); 01849 for (i = 0; i < h; i++) { 01850 line = data + i * wpl; 01851 for (j = 0; j < w; j++) { 01852 pixel = *(line + j); 01853 extractRGBValues(pixel, &rval, &gval, &bval); 01854 if ((L_ABS(rval - rsval) <= diff) && 01855 (L_ABS(gval - gsval) <= diff) && 01856 (L_ABS(bval - bsval) <= diff)) 01857 *(line + j) = dstval; /* replace */ 01858 } 01859 } 01860 } 01861 01862 return pixd; 01863 } 01864 01865 01866 /*! 01867 * pixSnapColorCmap() 01868 * 01869 * Input: pixd (<optional>; either NULL or equal to pixs for in-place) 01870 * pixs (colormapped) 01871 * srcval (color center to be selected for change: 0xrrggbb00) 01872 * dstval (target color for pixels: 0xrrggbb00) 01873 * diff (max absolute difference, applied to all components) 01874 * Return: pixd (with all pixels within diff of srcval set to dstval), 01875 * or pixd on error 01876 * 01877 * Notes: 01878 * (1) For inplace operation, call it this way: 01879 * pixSnapCcmap(pixs, pixs, ... ) 01880 * (2) For generating a new pixd: 01881 * pixd = pixSnapCmap(NULL, pixs, ...) 01882 * (3) pixs must have a colormap. 01883 * (4) All colors within 'diff' of 'srcval', componentwise, 01884 * will be changed to 'dstval'. 01885 */ 01886 PIX * 01887 pixSnapColorCmap(PIX *pixd, 01888 PIX *pixs, 01889 l_uint32 srcval, 01890 l_uint32 dstval, 01891 l_int32 diff) 01892 { 01893 l_int32 i, ncolors, index, found; 01894 l_int32 rval, gval, bval, rsval, gsval, bsval, rdval, gdval, bdval; 01895 l_int32 *tab; 01896 PIX *pixm; 01897 PIXCMAP *cmap; 01898 01899 PROCNAME("pixSnapColorCmap"); 01900 01901 if (!pixs) 01902 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 01903 if (!pixGetColormap(pixs)) 01904 return (PIX *)ERROR_PTR("cmap not found", procName, pixd); 01905 if (pixd && (pixd != pixs)) 01906 return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); 01907 01908 if (!pixd) 01909 pixd = pixCopy(NULL, pixs); 01910 01911 /* If no free colors, look for one close to the target 01912 * that can be commandeered. */ 01913 cmap = pixGetColormap(pixd); 01914 ncolors = pixcmapGetCount(cmap); 01915 extractRGBValues(srcval, &rsval, &gsval, &bsval); 01916 extractRGBValues(dstval, &rdval, &gdval, &bdval); 01917 found = FALSE; 01918 if (pixcmapGetFreeCount(cmap) == 0) { 01919 for (i = 0; i < ncolors; i++) { 01920 pixcmapGetColor(cmap, i, &rval, &gval, &bval); 01921 if ((L_ABS(rval - rsval) <= diff) && 01922 (L_ABS(gval - gsval) <= diff) && 01923 (L_ABS(bval - bsval) <= diff)) { 01924 index = i; 01925 pixcmapResetColor(cmap, index, rdval, gdval, bdval); 01926 found = TRUE; 01927 break; 01928 } 01929 } 01930 } 01931 else { /* just add the new color */ 01932 pixcmapAddColor(cmap, rdval, gdval, bdval); 01933 ncolors = pixcmapGetCount(cmap); 01934 index = ncolors - 1; /* index of new destination color */ 01935 found = TRUE; 01936 } 01937 01938 if (!found) { 01939 L_INFO("nothing to do", procName); 01940 return pixd; 01941 } 01942 01943 /* For each color in cmap that is close enough to srcval, 01944 * set the tab value to 1. Then generate a 1 bpp mask with 01945 * fg pixels for every pixel in pixd that is close enough 01946 * to srcval (i.e., has value 1 in tab). */ 01947 if ((tab = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL) 01948 return (PIX *)ERROR_PTR("tab not made", procName, pixd); 01949 for (i = 0; i < ncolors; i++) { 01950 pixcmapGetColor(cmap, i, &rval, &gval, &bval); 01951 if ((L_ABS(rval - rsval) <= diff) && 01952 (L_ABS(gval - gsval) <= diff) && 01953 (L_ABS(bval - bsval) <= diff)) 01954 tab[i] = 1; 01955 } 01956 pixm = pixMakeMaskFromLUT(pixd, tab); 01957 FREE(tab); 01958 01959 /* Use the binary mask to set all selected pixels to 01960 * the dest color index. */ 01961 pixSetMasked(pixd, pixm, dstval); 01962 pixDestroy(&pixm); 01963 01964 /* Remove all unused colors from the colormap. */ 01965 pixRemoveUnusedColors(pixd); 01966 01967 return pixd; 01968 } 01969 01970 01971 /*------------------------------------------------------------------* 01972 * Mapping colors based on a source/target pair * 01973 *------------------------------------------------------------------*/ 01974 /*! 01975 * pixLinearMapToTargetColor() 01976 * 01977 * Input: pixd (<optional>; either NULL or equal to pixs for in-place) 01978 * pixs (32 bpp rgb) 01979 * srcval (source color: 0xrrggbb00) 01980 * dstval (target color: 0xrrggbb00) 01981 * Return: pixd (with all pixels mapped based on the srcval/destval 01982 * mapping), or pixd on error 01983 * 01984 * Notes: 01985 * (1) For each component (r, b, g) separately, this does a piecewise 01986 * linear mapping of the colors in pixs to colors in pixd. 01987 * If rs and rd are the red src and dest components in @srcval and 01988 * @dstval, then the range [0 ... rs] in pixs is mapped to 01989 * [0 ... rd] in pixd. Likewise, the range [rs ... 255] in pixs 01990 * is mapped to [rd ... 255] in pixd. And similarly for green 01991 * and blue. 01992 * (2) The mapping will in general change the hue of the pixels. 01993 * However, if the src and dst targets are related by 01994 * a transformation given by pixelFractionalShift(), the hue 01995 * is invariant. 01996 * (3) For inplace operation, call it this way: 01997 * pixLinearMapToTargetColor(pixs, pixs, ... ) 01998 * (4) For generating a new pixd: 01999 * pixd = pixLinearMapToTargetColor(NULL, pixs, ...) 02000 */ 02001 PIX * 02002 pixLinearMapToTargetColor(PIX *pixd, 02003 PIX *pixs, 02004 l_uint32 srcval, 02005 l_uint32 dstval) 02006 { 02007 l_int32 i, j, w, h, wpl; 02008 l_int32 rval, gval, bval, rsval, gsval, bsval, rdval, gdval, bdval; 02009 l_int32 *rtab, *gtab, *btab; 02010 l_uint32 pixel; 02011 l_uint32 *line, *data; 02012 02013 PROCNAME("pixLinearMapToTargetColor"); 02014 02015 if (!pixs) 02016 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd); 02017 if (pixd && (pixd != pixs)) 02018 return (PIX *)ERROR_PTR("pixd not null or == pixs", procName, pixd); 02019 if (pixGetDepth(pixs) != 32) 02020 return (PIX *)ERROR_PTR("pixs is not 32 bpp", procName, pixd); 02021 02022 /* Do the work on pixd */ 02023 if (!pixd) 02024 pixd = pixCopy(NULL, pixs); 02025 02026 extractRGBValues(srcval, &rsval, &gsval, &bsval); 02027 extractRGBValues(dstval, &rdval, &gdval, &bdval); 02028 rsval = L_MIN(254, L_MAX(1, rsval)); 02029 gsval = L_MIN(254, L_MAX(1, gsval)); 02030 bsval = L_MIN(254, L_MAX(1, bsval)); 02031 rtab = (l_int32 *)CALLOC(256, sizeof(l_int32)); 02032 gtab = (l_int32 *)CALLOC(256, sizeof(l_int32)); 02033 btab = (l_int32 *)CALLOC(256, sizeof(l_int32)); 02034 for (i = 0; i < 256; i++) { 02035 if (i <= rsval) 02036 rtab[i] = (i * rdval) / rsval; 02037 else 02038 rtab[i] = rdval + ((255 - rdval) * (i - rsval)) / (255 - rsval); 02039 if (i <= gsval) 02040 gtab[i] = (i * gdval) / gsval; 02041 else 02042 gtab[i] = gdval + ((255 - gdval) * (i - gsval)) / (255 - gsval); 02043 if (i <= bsval) 02044 btab[i] = (i * bdval) / bsval; 02045 else 02046 btab[i] = bdval + ((255 - bdval) * (i - bsval)) / (255 - bsval); 02047 } 02048 pixGetDimensions(pixd, &w, &h, NULL); 02049 data = pixGetData(pixd); 02050 wpl = pixGetWpl(pixd); 02051 for (i = 0; i < h; i++) { 02052 line = data + i * wpl; 02053 for (j = 0; j < w; j++) { 02054 pixel = line[j]; 02055 extractRGBValues(pixel, &rval, &gval, &bval); 02056 composeRGBPixel(rtab[rval], gtab[gval], btab[bval], &pixel); 02057 line[j] = pixel; 02058 } 02059 } 02060 02061 FREE(rtab); 02062 FREE(gtab); 02063 FREE(btab); 02064 return pixd; 02065 } 02066 02067 02068 /*! 02069 * pixelLinearMapToTargetColor() 02070 * 02071 * Input: scolor (rgb source color: 0xrrggbb00) 02072 * srcmap (source mapping color: 0xrrggbb00) 02073 * dstmap (target mapping color: 0xrrggbb00) 02074 * &pdcolor (<return> rgb dest color: 0xrrggbb00) 02075 * Return: 0 if OK, 1 on error 02076 * 02077 * Notes: 02078 * (1) This does this does a piecewise linear mapping of each 02079 * component of @scolor to @dcolor, based on the relation 02080 * between the components of @srcmap and @dstmap. It is the 02081 * same transformation, performed on a single color, as mapped 02082 * on every pixel in a pix by pixLinearMapToTargetColor(). 02083 * (2) For each component, if the sval is larger than the smap, 02084 * the dval will be pushed up from dmap towards white. 02085 * Otherwise, dval will be pushed down from dmap towards black. 02086 * This is because you can visualize the transformation as 02087 * a linear stretching where smap moves to dmap, and everything 02088 * else follows linearly with 0 and 255 fixed. 02089 * (3) The mapping will in general change the hue of @scolor. 02090 * However, if the @srcmap and @dstmap targets are related by 02091 * a transformation given by pixelFractionalShift(), the hue 02092 * will be invariant. 02093 */ 02094 l_int32 02095 pixelLinearMapToTargetColor(l_uint32 scolor, 02096 l_uint32 srcmap, 02097 l_uint32 dstmap, 02098 l_uint32 *pdcolor) 02099 { 02100 l_int32 srval, sgval, sbval, drval, dgval, dbval; 02101 l_int32 srmap, sgmap, sbmap, drmap, dgmap, dbmap; 02102 02103 PROCNAME("pixelLinearMapToTargetColor"); 02104 02105 if (!pdcolor) 02106 return ERROR_INT("&dcolor not defined", procName, 1); 02107 *pdcolor = 0; 02108 02109 extractRGBValues(scolor, &srval, &sgval, &sbval); 02110 extractRGBValues(srcmap, &srmap, &sgmap, &sbmap); 02111 extractRGBValues(dstmap, &drmap, &dgmap, &dbmap); 02112 srmap = L_MIN(254, L_MAX(1, srmap)); 02113 sgmap = L_MIN(254, L_MAX(1, sgmap)); 02114 sbmap = L_MIN(254, L_MAX(1, sbmap)); 02115 02116 if (srval < srmap) 02117 drval = (srval * drmap) / srmap; 02118 else 02119 drval = drmap + ((255 - drmap) * (srval - srmap)) / (255 - srmap); 02120 if (sgval < sgmap) 02121 dgval = (sgval * dgmap) / sgmap; 02122 else 02123 dgval = dgmap + ((255 - dgmap) * (sgval - sgmap)) / (255 - sgmap); 02124 if (sbval < sbmap) 02125 dbval = (sbval * dbmap) / sbmap; 02126 else 02127 dbval = dbmap + ((255 - dbmap) * (sbval - sbmap)) / (255 - sbmap); 02128 02129 composeRGBPixel(drval, dgval, dbval, pdcolor); 02130 return 0; 02131 } 02132 02133 02134 /*------------------------------------------------------------------* 02135 * Fractional shift of RGB towards black or white * 02136 *------------------------------------------------------------------*/ 02137 /*! 02138 * pixelFractionalShift() 02139 * 02140 * Input: rval, gval, bval 02141 * fraction (negative toward black; positive toward white) 02142 * &ppixel (<return> rgb value) 02143 * Return: 0 if OK, 1 on error 02144 * 02145 * Notes: 02146 * (1) This transformation leaves the hue invariant, while changing 02147 * the saturation and intensity. It can be used for that 02148 * purpose in pixLinearMapToTargetColor(). 02149 * (2) @fraction is in the range [-1 .... +1]. If @fraction < 0, 02150 * saturation is increased and brightness is reduced. The 02151 * opposite results if @fraction > 0. If @fraction == -1, 02152 * the resulting pixel is black; @fraction == 1 results in white. 02153 */ 02154 l_int32 02155 pixelFractionalShift(l_int32 rval, 02156 l_int32 gval, 02157 l_int32 bval, 02158 l_float32 fraction, 02159 l_uint32 *ppixel) 02160 { 02161 l_int32 nrval, ngval, nbval; 02162 02163 PROCNAME("pixelFractionalShift"); 02164 02165 if (!ppixel) 02166 return ERROR_INT("&pixel defined", procName, 1); 02167 if (fraction < -1.0 || fraction > 1.0) 02168 return ERROR_INT("fraction not in [-1 ... +1]", procName, 1); 02169 02170 nrval = (fraction < 0) ? (l_int32)((1.0 + fraction) * rval + 0.5) : 02171 rval + (l_int32)(fraction * (255 - rval) + 0.5); 02172 ngval = (fraction < 0) ? (l_int32)((1.0 + fraction) * gval + 0.5) : 02173 gval + (l_int32)(fraction * (255 - gval) + 0.5); 02174 nbval = (fraction < 0) ? (l_int32)((1.0 + fraction) * bval + 0.5) : 02175 bval + (l_int32)(fraction * (255 - bval) + 0.5); 02176 composeRGBPixel(nrval, ngval, nbval, ppixel); 02177 return 0; 02178 } 02179 02180 02181