comparison mupdf-source/thirdparty/leptonica/src/blend.c @ 2:b50eed0cc0ef upstream

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