Coverage Report

Created: 2024-02-28 06:46

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