Leptonica 1.68
C Image Processing Library

blend.c

Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines