Leptonica 1.82.0
Image processing and image analysis suite
blend.c
Go to the documentation of this file.
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
147#ifdef HAVE_CONFIG_H
148#include <config_auto.h>
149#endif /* HAVE_CONFIG_H */
150
151#include "allheaders.h"
152
153static l_int32 blendComponents(l_int32 a, l_int32 b, l_float32 fract);
154static l_int32 blendHardLightComponents(l_int32 a, l_int32 b, l_float32 fract);
155
156/*-------------------------------------------------------------*
157 * Blending two images that are not colormapped *
158 *-------------------------------------------------------------*/
175PIX *
177 PIX *pixs2,
178 l_int32 x,
179 l_int32 y,
180 l_float32 fract)
181{
182l_int32 w1, h1, d1, d2;
183BOX *box;
184PIX *pixc, *pixt, *pixd;
185
186 PROCNAME("pixBlend");
187
188 if (!pixs1)
189 return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
190 if (!pixs2)
191 return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
192
193 /* check relative depths */
194 d1 = pixGetDepth(pixs1);
195 d2 = pixGetDepth(pixs2);
196 if (d1 == 1 && d2 > 1)
197 return (PIX *)ERROR_PTR("mixing gray or color with 1 bpp",
198 procName, NULL);
199
200 /* remove colormap from pixs2 if necessary */
202 d2 = pixGetDepth(pixt);
203
204 /* Check if pixs2 is clipped by its position with respect
205 * to pixs1; if so, clip it and redefine x and y if necessary.
206 * This actually isn't necessary, as the specific blending
207 * functions do the clipping directly in the pixel loop
208 * over pixs2, but it's included here to show how it can
209 * easily be done on pixs2 first. */
210 pixGetDimensions(pixs1, &w1, &h1, NULL);
211 box = boxCreate(-x, -y, w1, h1); /* box of pixs1 relative to pixs2 */
212 pixc = pixClipRectangle(pixt, box, NULL);
213 boxDestroy(&box);
214 if (!pixc) {
215 L_WARNING("box doesn't overlap pix\n", procName);
216 pixDestroy(&pixt);
217 return NULL;
218 }
219 x = L_MAX(0, x);
220 y = L_MAX(0, y);
221
222 if (d2 == 1) {
223 pixd = pixBlendMask(NULL, pixs1, pixc, x, y, fract,
225 } else if (d2 == 8) {
226 pixd = pixBlendGray(NULL, pixs1, pixc, x, y, fract,
227 L_BLEND_GRAY, 0, 0);
228 } else { /* d2 == 32 */
229 pixd = pixBlendColor(NULL, pixs1, pixc, x, y, fract, 0, 0);
230 }
231
232 pixDestroy(&pixc);
233 pixDestroy(&pixt);
234 return pixd;
235}
236
237
264PIX *
266 PIX *pixs1,
267 PIX *pixs2,
268 l_int32 x,
269 l_int32 y,
270 l_float32 fract,
271 l_int32 type)
272{
273l_int32 i, j, d, wc, hc, w, h, wplc;
274l_int32 val, rval, gval, bval;
275l_uint32 pixval;
276l_uint32 *linec, *datac;
277PIX *pixc, *pix1, *pix2;
278
279 PROCNAME("pixBlendMask");
280
281 if (!pixs1)
282 return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
283 if (!pixs2)
284 return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
285 if (pixGetDepth(pixs1) == 1)
286 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, NULL);
287 if (pixGetDepth(pixs2) != 1)
288 return (PIX *)ERROR_PTR("pixs2 not 1 bpp", procName, NULL);
289 if (pixd == pixs1 && pixGetColormap(pixs1))
290 return (PIX *)ERROR_PTR("inplace; pixs1 has colormap", procName, NULL);
291 if (pixd && (pixd != pixs1))
292 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, NULL);
293 if (fract < 0.0 || fract > 1.0) {
294 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
295 fract = 0.5;
296 }
297 if (type != L_BLEND_WITH_INVERSE && type != L_BLEND_TO_WHITE &&
298 type != L_BLEND_TO_BLACK) {
299 L_WARNING("invalid blend type; setting to L_BLEND_WITH_INVERSE\n",
300 procName);
302 }
303
304 /* If pixd != NULL, we know that it is equal to pixs1 and
305 * that pixs1 does not have a colormap, so that an in-place operation
306 * can be done. Otherwise, remove colormap from pixs1 if
307 * it exists and unpack to at least 8 bpp if necessary,
308 * to do the blending on a new pix. */
309 if (!pixd) {
311 if (pixGetDepth(pix1) < 8)
312 pix2 = pixConvertTo8(pix1, FALSE);
313 else
314 pix2 = pixClone(pix1);
315 pixd = pixCopy(NULL, pix2);
316 pixDestroy(&pix1);
317 pixDestroy(&pix2);
318 }
319
320 pixGetDimensions(pixd, &w, &h, &d); /* d must be either 8 or 32 bpp */
321 pixc = pixClone(pixs2);
322 wc = pixGetWidth(pixc);
323 hc = pixGetHeight(pixc);
324 datac = pixGetData(pixc);
325 wplc = pixGetWpl(pixc);
326
327 /* Check limits for src1, in case clipping was not done. */
328 switch (type)
329 {
331 /*
332 * The basic logic for this blending is:
333 * p --> (1 - f) * p + f * (1 - p)
334 * where p is a normalized value: p = pixval / 255.
335 * Thus,
336 * p --> p + f * (1 - 2 * p)
337 */
338 for (i = 0; i < hc; i++) {
339 if (i + y < 0 || i + y >= h) continue;
340 linec = datac + i * wplc;
341 for (j = 0; j < wc; j++) {
342 if (j + x < 0 || j + x >= w) continue;
343 bval = GET_DATA_BIT(linec, j);
344 if (bval) {
345 switch (d)
346 {
347 case 8:
348 pixGetPixel(pixd, x + j, y + i, &pixval);
349 val = (l_int32)(pixval + fract * (255 - 2 * pixval));
350 pixSetPixel(pixd, x + j, y + i, val);
351 break;
352 case 32:
353 pixGetPixel(pixd, x + j, y + i, &pixval);
354 extractRGBValues(pixval, &rval, &gval, &bval);
355 rval = (l_int32)(rval + fract * (255 - 2 * rval));
356 gval = (l_int32)(gval + fract * (255 - 2 * gval));
357 bval = (l_int32)(bval + fract * (255 - 2 * bval));
358 composeRGBPixel(rval, gval, bval, &pixval);
359 pixSetPixel(pixd, x + j, y + i, pixval);
360 break;
361 default:
362 L_WARNING("d neither 8 nor 32 bpp; no blend\n",
363 procName);
364 }
365 }
366 }
367 }
368 break;
369 case L_BLEND_TO_WHITE:
370 /*
371 * The basic logic for this blending is:
372 * p --> p + f * (1 - p) (p normalized to [0...1])
373 */
374 for (i = 0; i < hc; i++) {
375 if (i + y < 0 || i + y >= h) continue;
376 linec = datac + i * wplc;
377 for (j = 0; j < wc; j++) {
378 if (j + x < 0 || j + x >= w) continue;
379 bval = GET_DATA_BIT(linec, j);
380 if (bval) {
381 switch (d)
382 {
383 case 8:
384 pixGetPixel(pixd, x + j, y + i, &pixval);
385 val = (l_int32)(pixval + fract * (255 - pixval));
386 pixSetPixel(pixd, x + j, y + i, val);
387 break;
388 case 32:
389 pixGetPixel(pixd, x + j, y + i, &pixval);
390 extractRGBValues(pixval, &rval, &gval, &bval);
391 rval = (l_int32)(rval + fract * (255 - rval));
392 gval = (l_int32)(gval + fract * (255 - gval));
393 bval = (l_int32)(bval + fract * (255 - bval));
394 composeRGBPixel(rval, gval, bval, &pixval);
395 pixSetPixel(pixd, x + j, y + i, pixval);
396 break;
397 default:
398 L_WARNING("d neither 8 nor 32 bpp; no blend\n",
399 procName);
400 }
401 }
402 }
403 }
404 break;
405 case L_BLEND_TO_BLACK:
406 /*
407 * The basic logic for this blending is:
408 * p --> (1 - f) * p (p normalized to [0...1])
409 */
410 for (i = 0; i < hc; i++) {
411 if (i + y < 0 || i + y >= h) continue;
412 linec = datac + i * wplc;
413 for (j = 0; j < wc; j++) {
414 if (j + x < 0 || j + x >= w) continue;
415 bval = GET_DATA_BIT(linec, j);
416 if (bval) {
417 switch (d)
418 {
419 case 8:
420 pixGetPixel(pixd, x + j, y + i, &pixval);
421 val = (l_int32)((1. - fract) * pixval);
422 pixSetPixel(pixd, x + j, y + i, val);
423 break;
424 case 32:
425 pixGetPixel(pixd, x + j, y + i, &pixval);
426 extractRGBValues(pixval, &rval, &gval, &bval);
427 rval = (l_int32)((1. - fract) * rval);
428 gval = (l_int32)((1. - fract) * gval);
429 bval = (l_int32)((1. - fract) * bval);
430 composeRGBPixel(rval, gval, bval, &pixval);
431 pixSetPixel(pixd, x + j, y + i, pixval);
432 break;
433 default:
434 L_WARNING("d neither 8 nor 32 bpp; no blend\n",
435 procName);
436 }
437 }
438 }
439 }
440 break;
441 default:
442 L_WARNING("invalid binary mask blend type\n", procName);
443 break;
444 }
445
446 pixDestroy(&pixc);
447 return pixd;
448}
449
450
493PIX *
495 PIX *pixs1,
496 PIX *pixs2,
497 l_int32 x,
498 l_int32 y,
499 l_float32 fract,
500 l_int32 type,
501 l_int32 transparent,
502 l_uint32 transpix)
503{
504l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta;
505l_int32 ival, irval, igval, ibval, cval, dval;
506l_uint32 val32;
507l_uint32 *linec, *lined, *datac, *datad;
508PIX *pixc, *pix1, *pix2;
509
510 PROCNAME("pixBlendGray");
511
512 if (!pixs1)
513 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
514 if (!pixs2)
515 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
516 if (pixGetDepth(pixs1) == 1)
517 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd);
518 if (pixd == pixs1 && pixGetColormap(pixs1))
519 return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd);
520 if (pixd && (pixd != pixs1))
521 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd);
522 if (fract < 0.0 || fract > 1.0) {
523 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
524 fract = 0.5;
525 }
526 if (type != L_BLEND_GRAY && type != L_BLEND_GRAY_WITH_INVERSE) {
527 L_WARNING("invalid blend type; setting to L_BLEND_GRAY\n", procName);
528 type = L_BLEND_GRAY;
529 }
530
531 /* If pixd != NULL, we know that it is equal to pixs1 and
532 * that pixs1 does not have a colormap, so that an in-place operation
533 * can be done. Otherwise, remove colormap from pixs1 if
534 * it exists and unpack to at least 8 bpp if necessary,
535 * to do the blending on a new pix. */
536 if (!pixd) {
538 if (pixGetDepth(pix1) < 8)
539 pix2 = pixConvertTo8(pix1, FALSE);
540 else
541 pix2 = pixClone(pix1);
542 pixd = pixCopy(NULL, pix2);
543 pixDestroy(&pix1);
544 pixDestroy(&pix2);
545 }
546
547 pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */
548 wpld = pixGetWpl(pixd);
549 datad = pixGetData(pixd);
550 pixc = pixConvertTo8(pixs2, 0);
551 pixGetDimensions(pixc, &wc, &hc, NULL);
552 datac = pixGetData(pixc);
553 wplc = pixGetWpl(pixc);
554
555 /* Check limits for src1, in case clipping was not done */
556 if (type == L_BLEND_GRAY) {
557 /*
558 * The basic logic for this blending is:
559 * p --> (1 - f) * p + f * c
560 * where c is the 8 bpp blender. All values are normalized to [0...1].
561 */
562 for (i = 0; i < hc; i++) {
563 if (i + y < 0 || i + y >= h) continue;
564 linec = datac + i * wplc;
565 lined = datad + (i + y) * wpld;
566 switch (d)
567 {
568 case 8:
569 for (j = 0; j < wc; j++) {
570 if (j + x < 0 || j + x >= w) continue;
571 cval = GET_DATA_BYTE(linec, j);
572 if (transparent == 0 || cval != transpix) {
573 dval = GET_DATA_BYTE(lined, j + x);
574 ival = (l_int32)((1. - fract) * dval + fract * cval);
575 SET_DATA_BYTE(lined, j + x, ival);
576 }
577 }
578 break;
579 case 32:
580 for (j = 0; j < wc; j++) {
581 if (j + x < 0 || j + x >= w) continue;
582 cval = GET_DATA_BYTE(linec, j);
583 if (transparent == 0 || cval != transpix) {
584 val32 = *(lined + j + x);
585 extractRGBValues(val32, &irval, &igval, &ibval);
586 irval = (l_int32)((1. - fract) * irval + fract * cval);
587 igval = (l_int32)((1. - fract) * igval + fract * cval);
588 ibval = (l_int32)((1. - fract) * ibval + fract * cval);
589 composeRGBPixel(irval, igval, ibval, &val32);
590 *(lined + j + x) = val32;
591 }
592 }
593 break;
594 default:
595 break; /* shouldn't happen */
596 }
597 }
598 } else { /* L_BLEND_GRAY_WITH_INVERSE */
599 for (i = 0; i < hc; i++) {
600 if (i + y < 0 || i + y >= h) continue;
601 linec = datac + i * wplc;
602 lined = datad + (i + y) * wpld;
603 switch (d)
604 {
605 case 8:
606 /*
607 * For 8 bpp, the dest pix is shifted by a signed amount
608 * proportional to the distance from 128 (the pivot value),
609 * and to the darkness of src2. If the dest is darker
610 * than 128, it becomes lighter, and v.v.
611 * The basic logic is:
612 * d --> d + f * (0.5 - d) * (1 - c)
613 * where d and c are normalized pixel values for src1 and
614 * src2, respectively, with 8 bit normalization to [0...1].
615 */
616 for (j = 0; j < wc; j++) {
617 if (j + x < 0 || j + x >= w) continue;
618 cval = GET_DATA_BYTE(linec, j);
619 if (transparent == 0 || cval != transpix) {
620 ival = GET_DATA_BYTE(lined, j + x);
621 delta = (128 - ival) * (255 - cval) / 256;
622 ival += (l_int32)(fract * delta + 0.5);
623 SET_DATA_BYTE(lined, j + x, ival);
624 }
625 }
626 break;
627 case 32:
628 /* Each component is shifted by the same formula for 8 bpp */
629 for (j = 0; j < wc; j++) {
630 if (j + x < 0 || j + x >= w) continue;
631 cval = GET_DATA_BYTE(linec, j);
632 if (transparent == 0 || cval != transpix) {
633 val32 = *(lined + j + x);
634 extractRGBValues(val32, &irval, &igval, &ibval);
635 delta = (128 - irval) * (255 - cval) / 256;
636 irval += (l_int32)(fract * delta + 0.5);
637 delta = (128 - igval) * (255 - cval) / 256;
638 igval += (l_int32)(fract * delta + 0.5);
639 delta = (128 - ibval) * (255 - cval) / 256;
640 ibval += (l_int32)(fract * delta + 0.5);
641 composeRGBPixel(irval, igval, ibval, &val32);
642 *(lined + j + x) = val32;
643 }
644 }
645 break;
646 default:
647 break; /* shouldn't happen */
648 }
649 }
650 }
651
652 pixDestroy(&pixc);
653 return pixd;
654}
655
656
693PIX *
695 PIX *pixs1,
696 PIX *pixs2,
697 l_int32 x,
698 l_int32 y,
699 l_float32 fract)
700{
701l_int32 i, j, d, wc, hc, w, h, wplc, wpld;
702l_int32 irval, igval, ibval, cval, dval;
703l_float32 a;
704l_uint32 val32;
705l_uint32 *linec, *lined, *datac, *datad;
706PIX *pixc, *pix1, *pix2;
707
708 PROCNAME("pixBlendGrayInverse");
709
710 if (!pixs1)
711 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
712 if (!pixs2)
713 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
714 if (pixGetDepth(pixs1) == 1)
715 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd);
716 if (pixd == pixs1 && pixGetColormap(pixs1))
717 return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd);
718 if (pixd && (pixd != pixs1))
719 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd);
720 if (fract < 0.0 || fract > 1.0) {
721 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
722 fract = 0.5;
723 }
724
725 /* If pixd != NULL, we know that it is equal to pixs1 and
726 * that pixs1 does not have a colormap, so that an in-place operation
727 * can be done. Otherwise, remove colormap from pixs1 if
728 * it exists and unpack to at least 8 bpp if necessary,
729 * to do the blending on a new pix. */
730 if (!pixd) {
732 if (pixGetDepth(pix1) < 8)
733 pix2 = pixConvertTo8(pix1, FALSE);
734 else
735 pix2 = pixClone(pix1);
736 pixd = pixCopy(NULL, pix2);
737 pixDestroy(&pix1);
738 pixDestroy(&pix2);
739 }
740
741 pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */
742 wpld = pixGetWpl(pixd);
743 datad = pixGetData(pixd);
744 pixc = pixConvertTo8(pixs2, 0);
745 pixGetDimensions(pixc, &wc, &hc, NULL);
746 datac = pixGetData(pixc);
747 wplc = pixGetWpl(pixc);
748
749 /* Check limits for src1, in case clipping was not done */
750 for (i = 0; i < hc; i++) {
751 if (i + y < 0 || i + y >= h) continue;
752 linec = datac + i * wplc;
753 lined = datad + (i + y) * wpld;
754 switch (d)
755 {
756 case 8:
757 for (j = 0; j < wc; j++) {
758 if (j + x < 0 || j + x >= w) continue;
759 cval = GET_DATA_BYTE(linec, j);
760 dval = GET_DATA_BYTE(lined, j + x);
761 a = (1.0 - fract) * dval + fract * (255.0 - dval);
762 dval = (l_int32)(cval * dval / 255.0 +
763 a * (255.0 - cval) / 255.0);
764 SET_DATA_BYTE(lined, j + x, dval);
765 }
766 break;
767 case 32:
768 for (j = 0; j < wc; j++) {
769 if (j + x < 0 || j + x >= w) continue;
770 cval = GET_DATA_BYTE(linec, j);
771 val32 = *(lined + j + x);
772 extractRGBValues(val32, &irval, &igval, &ibval);
773 a = (1.0 - fract) * irval + fract * (255.0 - irval);
774 irval = (l_int32)(cval * irval / 255.0 +
775 a * (255.0 - cval) / 255.0);
776 a = (1.0 - fract) * igval + fract * (255.0 - igval);
777 igval = (l_int32)(cval * igval / 255.0 +
778 a * (255.0 - cval) / 255.0);
779 a = (1.0 - fract) * ibval + fract * (255.0 - ibval);
780 ibval = (l_int32)(cval * ibval / 255.0 +
781 a * (255.0 - cval) / 255.0);
782 composeRGBPixel(irval, igval, ibval, &val32);
783 *(lined + j + x) = val32;
784 }
785 break;
786 default:
787 break; /* shouldn't happen */
788 }
789 }
790
791 pixDestroy(&pixc);
792 return pixd;
793}
794
795
827PIX *
829 PIX *pixs1,
830 PIX *pixs2,
831 l_int32 x,
832 l_int32 y,
833 l_float32 fract,
834 l_int32 transparent,
835 l_uint32 transpix)
836{
837l_int32 i, j, wc, hc, w, h, wplc, wpld;
838l_int32 rval, gval, bval, rcval, gcval, bcval;
839l_uint32 cval32, val32;
840l_uint32 *linec, *lined, *datac, *datad;
841PIX *pixc;
842
843 PROCNAME("pixBlendColor");
844
845 if (!pixs1)
846 return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
847 if (!pixs2)
848 return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
849 if (pixGetDepth(pixs1) == 1)
850 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, NULL);
851 if (pixd == pixs1 && pixGetDepth(pixs1) != 32)
852 return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", procName, NULL);
853 if (pixd && (pixd != pixs1))
854 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, NULL);
855 if (fract < 0.0 || fract > 1.0) {
856 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
857 fract = 0.5;
858 }
859
860 /* If pixd != null, we know that it is equal to pixs1 and
861 * that pixs1 is 32 bpp rgb, so that an in-place operation
862 * can be done. Otherwise, pixConvertTo32() will remove a
863 * colormap from pixs1 if it exists and unpack to 32 bpp
864 * (if necessary) to do the blending on a new 32 bpp Pix. */
865 if (!pixd)
866 pixd = pixConvertTo32(pixs1);
867 pixGetDimensions(pixd, &w, &h, NULL);
868 wpld = pixGetWpl(pixd);
869 datad = pixGetData(pixd);
870 pixc = pixConvertTo32(pixs2); /* blend with 32 bpp rgb */
871 pixGetDimensions(pixc, &wc, &hc, NULL);
872 datac = pixGetData(pixc);
873 wplc = pixGetWpl(pixc);
874
875 /* Check limits for src1, in case clipping was not done */
876 for (i = 0; i < hc; i++) {
877 /*
878 * The basic logic for this blending is:
879 * p --> (1 - f) * p + f * c
880 * for each color channel. c is a color component of the blender.
881 * All values are normalized to [0...1].
882 */
883 if (i + y < 0 || i + y >= h) continue;
884 linec = datac + i * wplc;
885 lined = datad + (i + y) * wpld;
886 for (j = 0; j < wc; j++) {
887 if (j + x < 0 || j + x >= w) continue;
888 cval32 = *(linec + j);
889 if (transparent == 0 ||
890 ((cval32 & 0xffffff00) != (transpix & 0xffffff00))) {
891 val32 = *(lined + j + x);
892 extractRGBValues(cval32, &rcval, &gcval, &bcval);
893 extractRGBValues(val32, &rval, &gval, &bval);
894 rval = (l_int32)((1. - fract) * rval + fract * rcval);
895 gval = (l_int32)((1. - fract) * gval + fract * gcval);
896 bval = (l_int32)((1. - fract) * bval + fract * bcval);
897 composeRGBPixel(rval, gval, bval, &val32);
898 *(lined + j + x) = val32;
899 }
900 }
901 }
902
903 pixDestroy(&pixc);
904 return pixd;
905}
906
907
908/*
909 * \brief pixBlendColorByChannel()
910 *
911 * \param[in] pixd [optional] either equal to pixs1 for in-place,
912 * or NULL
913 * \param[in] pixs1 blendee; depth > 1
914 * \param[in] pixs2 blender, any depth; typically, the area of
915 * pixs2 is smaller than pixs1
916 * \param[in] x,y origin [UL corner] of pixs2 relative to
917 * the origin of pixs1
918 * \param[in] rfract blending fraction in red channel
919 * \param[in] gfract blending fraction in green channel
920 * \param[in] bfract blending fraction in blue channel
921 * \param[in] transparent 1 to use transparency; 0 otherwise
922 * \param[in] transpix pixel color in pixs2 that is to be transparent
923 * \return pixd if OK; pixd on error
924 *
925 * <pre>
926 * Notes:
927 * (1) This generalizes pixBlendColor() in two ways:
928 * (a) The mixing fraction is specified per channel.
929 * (b) The mixing fraction may be < 0 or > 1, in which case,
930 * the min or max of two images are taken, respectively.
931 * (2) Specifically,
932 * for p = pixs1[i], c = pixs2[i], f = fract[i], i = 1, 2, 3:
933 * f < 0.0: p --> min(p, c)
934 * 0.0 <= f <= 1.0: p --> (1 - f) * p + f * c
935 * f > 1.0: p --> max(a, c)
936 * Special cases:
937 * f = 0: p --> p
938 * f = 1: p --> c
939 * (3) See usage notes in pixBlendColor()
940 * (4) pixBlendColor() would be equivalent to
941 * pixBlendColorChannel(..., fract, fract, fract, ...);
942 * at a small cost of efficiency.
943 * </pre>
944 */
945PIX *
946pixBlendColorByChannel(PIX *pixd,
947 PIX *pixs1,
948 PIX *pixs2,
949 l_int32 x,
950 l_int32 y,
951 l_float32 rfract,
952 l_float32 gfract,
953 l_float32 bfract,
954 l_int32 transparent,
955 l_uint32 transpix)
956{
957l_int32 i, j, wc, hc, w, h, wplc, wpld;
958l_int32 rval, gval, bval, rcval, gcval, bcval;
959l_uint32 cval32, val32;
960l_uint32 *linec, *lined, *datac, *datad;
961PIX *pixc;
962
963 PROCNAME("pixBlendColorByChannel");
964
965 if (!pixs1)
966 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
967 if (!pixs2)
968 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
969 if (pixGetDepth(pixs1) == 1)
970 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd);
971 if (pixd == pixs1 && pixGetDepth(pixs1) != 32)
972 return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", procName, pixd);
973 if (pixd && (pixd != pixs1))
974 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd);
975
976 /* If pixd != NULL, we know that it is equal to pixs1 and
977 * that pixs1 is 32 bpp rgb, so that an in-place operation
978 * can be done. Otherwise, pixConvertTo32() will remove a
979 * colormap from pixs1 if it exists and unpack to 32 bpp
980 * (if necessary) to do the blending on a new 32 bpp Pix. */
981 if (!pixd)
982 pixd = pixConvertTo32(pixs1);
983 pixGetDimensions(pixd, &w, &h, NULL);
984 wpld = pixGetWpl(pixd);
985 datad = pixGetData(pixd);
986 pixc = pixConvertTo32(pixs2);
987 pixGetDimensions(pixc, &wc, &hc, NULL);
988 datac = pixGetData(pixc);
989 wplc = pixGetWpl(pixc);
990
991 /* Check limits for src1, in case clipping was not done */
992 for (i = 0; i < hc; i++) {
993 if (i + y < 0 || i + y >= h) continue;
994 linec = datac + i * wplc;
995 lined = datad + (i + y) * wpld;
996 for (j = 0; j < wc; j++) {
997 if (j + x < 0 || j + x >= w) continue;
998 cval32 = *(linec + j);
999 if (transparent == 0 ||
1000 ((cval32 & 0xffffff00) != (transpix & 0xffffff00))) {
1001 val32 = *(lined + j + x);
1002 extractRGBValues(cval32, &rcval, &gcval, &bcval);
1003 extractRGBValues(val32, &rval, &gval, &bval);
1004 rval = blendComponents(rval, rcval, rfract);
1005 gval = blendComponents(gval, gcval, gfract);
1006 bval = blendComponents(bval, bcval, bfract);
1007 composeRGBPixel(rval, gval, bval, &val32);
1008 *(lined + j + x) = val32;
1009 }
1010 }
1011 }
1012
1013 pixDestroy(&pixc);
1014 return pixd;
1015}
1016
1017
1018static l_int32
1019blendComponents(l_int32 a,
1020 l_int32 b,
1021 l_float32 fract)
1022{
1023 if (fract < 0.)
1024 return ((a < b) ? a : b);
1025 if (fract > 1.)
1026 return ((a > b) ? a : b);
1027 return (l_int32)((1. - fract) * a + fract * b);
1028}
1029
1030
1075PIX *
1077 PIX *pixs1,
1078 PIX *pixs2,
1079 l_int32 x,
1080 l_int32 y,
1081 l_float32 fract,
1082 l_int32 shift)
1083{
1084l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta, overlap;
1085l_int32 rval, gval, bval, cval, dval, mval, median, pivot;
1086l_uint32 val32;
1087l_uint32 *linec, *lined, *datac, *datad;
1088l_float32 fmedian, factor;
1089BOX *box, *boxt;
1090PIX *pixc, *pix1, *pix2;
1091
1092 PROCNAME("pixBlendGrayAdapt");
1093
1094 if (!pixs1)
1095 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1096 if (!pixs2)
1097 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1098 if (pixGetDepth(pixs1) == 1)
1099 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd);
1100 if (pixd == pixs1 && pixGetColormap(pixs1))
1101 return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd);
1102 if (pixd && (pixd != pixs1))
1103 return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd);
1104 if (fract < 0.0 || fract > 1.0) {
1105 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
1106 fract = 0.5;
1107 }
1108 if (shift == -1) shift = 64; /* default value */
1109 if (shift < 0 || shift > 127) {
1110 L_WARNING("invalid shift; setting to 64\n", procName);
1111 shift = 64;
1112 }
1113
1114 /* Test for overlap */
1115 pixGetDimensions(pixs1, &w, &h, NULL);
1116 pixGetDimensions(pixs2, &wc, &hc, NULL);
1117 box = boxCreate(x, y, wc, hc);
1118 boxt = boxCreate(0, 0, w, h);
1119 boxIntersects(box, boxt, &overlap);
1120 boxDestroy(&boxt);
1121 if (!overlap) {
1122 boxDestroy(&box);
1123 return (PIX *)ERROR_PTR("no image overlap", procName, pixd);
1124 }
1125
1126 /* If pixd != NULL, we know that it is equal to pixs1 and
1127 * that pixs1 does not have a colormap, so that an in-place operation
1128 * can be done. Otherwise, remove colormap from pixs1 if
1129 * it exists and unpack to at least 8 bpp if necessary,
1130 * to do the blending on a new pix. */
1131 if (!pixd) {
1133 if (pixGetDepth(pix1) < 8)
1134 pix2 = pixConvertTo8(pix1, FALSE);
1135 else
1136 pix2 = pixClone(pix1);
1137 pixd = pixCopy(NULL, pix2);
1138 pixDestroy(&pix1);
1139 pixDestroy(&pix2);
1140 }
1141
1142 /* Get the median value in the region of blending */
1143 pix1 = pixClipRectangle(pixd, box, NULL);
1144 pix2 = pixConvertTo8(pix1, 0);
1145 pixGetRankValueMasked(pix2, NULL, 0, 0, 1, 0.5, &fmedian, NULL);
1146 median = (l_int32)(fmedian + 0.5);
1147 if (median < 128)
1148 pivot = median + shift;
1149 else
1150 pivot = median - shift;
1151 pixDestroy(&pix1);
1152 pixDestroy(&pix2);
1153 boxDestroy(&box);
1154
1155 /* Process over src2; clip to src1. */
1156 d = pixGetDepth(pixd);
1157 wpld = pixGetWpl(pixd);
1158 datad = pixGetData(pixd);
1159 pixc = pixConvertTo8(pixs2, 0);
1160 datac = pixGetData(pixc);
1161 wplc = pixGetWpl(pixc);
1162 for (i = 0; i < hc; i++) {
1163 if (i + y < 0 || i + y >= h) continue;
1164 linec = datac + i * wplc;
1165 lined = datad + (i + y) * wpld;
1166 switch (d)
1167 {
1168 case 8:
1169 /*
1170 * For 8 bpp, the dest pix is shifted by an amount
1171 * proportional to the distance from the pivot value,
1172 * and to the darkness of src2. In no situation will it
1173 * pass the pivot value in intensity.
1174 * The basic logic is:
1175 * d --> d + f * (np - d) * (1 - c)
1176 * where np, d and c are normalized pixel values for
1177 * the pivot, src1 and src2, respectively, with normalization
1178 * to 255.
1179 */
1180 for (j = 0; j < wc; j++) {
1181 if (j + x < 0 || j + x >= w) continue;
1182 dval = GET_DATA_BYTE(lined, j + x);
1183 cval = GET_DATA_BYTE(linec, j);
1184 delta = (pivot - dval) * (255 - cval) / 256;
1185 dval += (l_int32)(fract * delta + 0.5);
1186 SET_DATA_BYTE(lined, j + x, dval);
1187 }
1188 break;
1189 case 32:
1190 /*
1191 * For 32 bpp, the dest pix is shifted by an amount
1192 * proportional to the max component distance from the
1193 * pivot value, and to the darkness of src2. Each component
1194 * is shifted by the same fraction, either up or down,
1195 * depending on the shift direction (which is toward the
1196 * pivot). The basic logic for the red component is:
1197 * r --> r + f * (np - m) * (1 - c) * (r / m)
1198 * where np, r, m and c are normalized pixel values for
1199 * the pivot, the r component of src1, the max component
1200 * of src1, and src2, respectively, again with normalization
1201 * to 255. Likewise for the green and blue components.
1202 */
1203 for (j = 0; j < wc; j++) {
1204 if (j + x < 0 || j + x >= w) continue;
1205 cval = GET_DATA_BYTE(linec, j);
1206 val32 = *(lined + j + x);
1207 extractRGBValues(val32, &rval, &gval, &bval);
1208 mval = L_MAX(rval, gval);
1209 mval = L_MAX(mval, bval);
1210 mval = L_MAX(mval, 1);
1211 delta = (pivot - mval) * (255 - cval) / 256;
1212 factor = fract * delta / mval;
1213 rval += (l_int32)(factor * rval + 0.5);
1214 gval += (l_int32)(factor * gval + 0.5);
1215 bval += (l_int32)(factor * bval + 0.5);
1216 composeRGBPixel(rval, gval, bval, &val32);
1217 *(lined + j + x) = val32;
1218 }
1219 break;
1220 default:
1221 break; /* shouldn't happen */
1222 }
1223 }
1224
1225 pixDestroy(&pixc);
1226 return pixd;
1227}
1228
1229
1249PIX *
1251 PIX *pixb,
1252 l_float32 factor,
1253 l_int32 type)
1254{
1255l_int32 i, j, w, h, d, wb, hb, db, wd, hd, wplb, wpld;
1256l_int32 valb, vald, nvald, rval, gval, bval, nrval, ngval, nbval;
1257l_float32 nfactor, fract;
1258l_uint32 val32, nval32;
1259l_uint32 *lined, *datad, *lineb, *datab;
1260PIX *pixd;
1261
1262 PROCNAME("pixFadeWithGray");
1263
1264 if (!pixs)
1265 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1266 if (!pixb)
1267 return (PIX *)ERROR_PTR("pixb not defined", procName, NULL);
1268 if (pixGetDepth(pixs) == 1)
1269 return (PIX *)ERROR_PTR("pixs is 1 bpp", procName, NULL);
1270 pixGetDimensions(pixb, &wb, &hb, &db);
1271 if (db != 8)
1272 return (PIX *)ERROR_PTR("pixb not 8 bpp", procName, NULL);
1273 if (factor < 0.0 || factor > 255.0)
1274 return (PIX *)ERROR_PTR("factor not in [0.0...255.0]", procName, NULL);
1275 if (type != L_BLEND_TO_WHITE && type != L_BLEND_TO_BLACK)
1276 return (PIX *)ERROR_PTR("invalid fade type", procName, NULL);
1277
1278 /* Remove colormap if it exists; otherwise copy */
1280 pixGetDimensions(pixd, &wd, &hd, &d);
1281 w = L_MIN(wb, wd);
1282 h = L_MIN(hb, hd);
1283 datad = pixGetData(pixd);
1284 wpld = pixGetWpl(pixd);
1285 datab = pixGetData(pixb);
1286 wplb = pixGetWpl(pixb);
1287
1288 /* The basic logic for this blending is, for each component p of pixs:
1289 * fade-to-white: p --> p + (f * c) * (1 - p)
1290 * fade-to-black: p --> p - (f * c) * p
1291 * with c being the 8 bpp blender pixel of pixb, and with both
1292 * p and c normalized to [0...1]. */
1293 nfactor = factor / 255.;
1294 for (i = 0; i < h; i++) {
1295 lineb = datab + i * wplb;
1296 lined = datad + i * wpld;
1297 for (j = 0; j < w; j++) {
1298 valb = GET_DATA_BYTE(lineb, j);
1299 fract = nfactor * (l_float32)valb;
1300 fract = L_MIN(fract, 1.0);
1301 if (d == 8) {
1302 vald = GET_DATA_BYTE(lined, j);
1303 if (type == L_BLEND_TO_WHITE)
1304 nvald = vald + (l_int32)(fract * (255. - (l_float32)vald));
1305 else /* L_BLEND_TO_BLACK */
1306 nvald = vald - (l_int32)(fract * (l_float32)vald);
1307 SET_DATA_BYTE(lined, j, nvald);
1308 } else { /* d == 32 */
1309 val32 = lined[j];
1310 extractRGBValues(val32, &rval, &gval, &bval);
1311 if (type == L_BLEND_TO_WHITE) {
1312 nrval = rval + (l_int32)(fract * (255. - (l_float32)rval));
1313 ngval = gval + (l_int32)(fract * (255. - (l_float32)gval));
1314 nbval = bval + (l_int32)(fract * (255. - (l_float32)bval));
1315 } else {
1316 nrval = rval - (l_int32)(fract * (l_float32)rval);
1317 ngval = gval - (l_int32)(fract * (l_float32)gval);
1318 nbval = bval - (l_int32)(fract * (l_float32)bval);
1319 }
1320 composeRGBPixel(nrval, ngval, nbval, &nval32);
1321 lined[j] = nval32;
1322 }
1323 }
1324 }
1325
1326 return pixd;
1327}
1328
1329
1330/*
1331 * \brief pixBlendHardLight()
1332 *
1333 * \param[in] pixd either NULL or equal to pixs1 for in-place
1334 * \param[in] pixs1 blendee; depth > 1, may be cmapped
1335 * \param[in] pixs2 blender, 8 or 32 bpp; may be colormapped;
1336 * typ. smaller in size than pixs1
1337 * \param[in] x,y origin [UL corner] of pixs2 relative to
1338 * the origin of pixs1
1339 * \param[in] fract blending fraction, or 'opacity factor'
1340 * \return pixd if OK; pixs1 on error
1341 *
1342 * <pre>
1343 * Notes:
1344 * (1) pixs2 must be 8 or 32 bpp; either may have a colormap.
1345 * (2) Clipping of pixs2 to pixs1 is done in the inner pixel loop.
1346 * (3) Only call in-place if pixs1 is not colormapped.
1347 * (4) If pixs1 has a colormap, it is removed to generate either an
1348 * 8 or 32 bpp pix, depending on the colormap.
1349 * (5) For inplace operation, call it this way:
1350 * pixBlendHardLight(pixs1, pixs1, pixs2, ...)
1351 * (6) For generating a new pixd:
1352 * pixd = pixBlendHardLight(NULL, pixs1, pixs2, ...)
1353 * (7) This is a generalization of the usual hard light blending,
1354 * where fract == 1.0.
1355 * (8) "Overlay" blending is the same as hard light blending, with
1356 * fract == 1.0, except that the components are switched
1357 * in the test. (Note that the result is symmetric in the
1358 * two components.)
1359 * (9) See, e.g.:
1360 * http://www.pegtop.net/delphi/articles/blendmodes/hardlight.htm
1361 * http://www.digitalartform.com/imageArithmetic.htm
1362 * (10) This function was built by Paco Galanes.
1363 * </pre>
1364 */
1365PIX *
1366pixBlendHardLight(PIX *pixd,
1367 PIX *pixs1,
1368 PIX *pixs2,
1369 l_int32 x,
1370 l_int32 y,
1371 l_float32 fract)
1372{
1373l_int32 i, j, w, h, d, wc, hc, dc, wplc, wpld;
1374l_int32 cval, dval, rcval, gcval, bcval, rdval, gdval, bdval;
1375l_uint32 cval32, dval32;
1376l_uint32 *linec, *lined, *datac, *datad;
1377PIX *pixc, *pixt;
1378
1379 PROCNAME("pixBlendHardLight");
1380
1381 if (!pixs1)
1382 return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1383 if (!pixs2)
1384 return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1385 pixGetDimensions(pixs1, &w, &h, &d);
1386 pixGetDimensions(pixs2, &wc, &hc, &dc);
1387 if (d == 1)
1388 return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd);
1389 if (dc != 8 && dc != 32)
1390 return (PIX *)ERROR_PTR("pixs2 not 8 or 32 bpp", procName, pixd);
1391 if (pixd && (pixd != pixs1))
1392 return (PIX *)ERROR_PTR("inplace and pixd != pixs1", procName, pixd);
1393 if (pixd == pixs1 && pixGetColormap(pixs1))
1394 return (PIX *)ERROR_PTR("inplace and pixs1 cmapped", procName, pixd);
1395 if (pixd && d != 8 && d != 32)
1396 return (PIX *)ERROR_PTR("inplace and not 8 or 32 bpp", procName, pixd);
1397
1398 if (fract < 0.0 || fract > 1.0) {
1399 L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
1400 fract = 0.5;
1401 }
1402
1403 /* If pixs2 has a colormap, remove it */
1404 pixc = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); /* clone ok */
1405 dc = pixGetDepth(pixc);
1406
1407 /* There are 4 cases:
1408 * * pixs1 has or doesn't have a colormap
1409 * * pixc is either 8 or 32 bpp
1410 * In all situations, if pixs has a colormap it must be removed,
1411 * and pixd must have a depth that is equal to or greater than pixc. */
1412 if (dc == 32) {
1413 if (pixGetColormap(pixs1)) { /* pixd == NULL */
1415 } else {
1416 if (!pixd) {
1417 pixd = pixConvertTo32(pixs1);
1418 } else {
1419 pixt = pixConvertTo32(pixs1);
1420 pixCopy(pixd, pixt);
1421 pixDestroy(&pixt);
1422 }
1423 }
1424 d = 32;
1425 } else { /* dc == 8 */
1426 if (pixGetColormap(pixs1)) /* pixd == NULL */
1428 else
1429 pixd = pixCopy(pixd, pixs1);
1430 d = pixGetDepth(pixd);
1431 }
1432
1433 if (!(d == 8 && dc == 8) && /* 3 cases only */
1434 !(d == 32 && dc == 8) &&
1435 !(d == 32 && dc == 32)) {
1436 pixDestroy(&pixc);
1437 return (PIX *)ERROR_PTR("bad! -- invalid depth combo!", procName, pixd);
1438 }
1439
1440 wpld = pixGetWpl(pixd);
1441 datad = pixGetData(pixd);
1442 datac = pixGetData(pixc);
1443 wplc = pixGetWpl(pixc);
1444 for (i = 0; i < hc; i++) {
1445 if (i + y < 0 || i + y >= h) continue;
1446 linec = datac + i * wplc;
1447 lined = datad + (i + y) * wpld;
1448 for (j = 0; j < wc; j++) {
1449 if (j + x < 0 || j + x >= w) continue;
1450 if (d == 8 && dc == 8) {
1451 dval = GET_DATA_BYTE(lined, x + j);
1452 cval = GET_DATA_BYTE(linec, j);
1453 dval = blendHardLightComponents(dval, cval, fract);
1454 SET_DATA_BYTE(lined, x + j, dval);
1455 } else if (d == 32 && dc == 8) {
1456 dval32 = *(lined + x + j);
1457 extractRGBValues(dval32, &rdval, &gdval, &bdval);
1458 cval = GET_DATA_BYTE(linec, j);
1459 rdval = blendHardLightComponents(rdval, cval, fract);
1460 gdval = blendHardLightComponents(gdval, cval, fract);
1461 bdval = blendHardLightComponents(bdval, cval, fract);
1462 composeRGBPixel(rdval, gdval, bdval, &dval32);
1463 *(lined + x + j) = dval32;
1464 } else if (d == 32 && dc == 32) {
1465 dval32 = *(lined + x + j);
1466 extractRGBValues(dval32, &rdval, &gdval, &bdval);
1467 cval32 = *(linec + j);
1468 extractRGBValues(cval32, &rcval, &gcval, &bcval);
1469 rdval = blendHardLightComponents(rdval, rcval, fract);
1470 gdval = blendHardLightComponents(gdval, gcval, fract);
1471 bdval = blendHardLightComponents(bdval, bcval, fract);
1472 composeRGBPixel(rdval, gdval, bdval, &dval32);
1473 *(lined + x + j) = dval32;
1474 }
1475 }
1476 }
1477
1478 pixDestroy(&pixc);
1479 return pixd;
1480}
1481
1482
1483/*
1484 * \brief blendHardLightComponents()
1485 *
1486 * \param[in] a 8 bpp blendee component
1487 * \param[in] b 8 bpp blender component
1488 * \param[in] fract fraction of blending; use 1.0 for usual definition
1489 * \return blended 8 bpp component
1490 *
1491 * <pre>
1492 * Notes:
1493 *
1494 * The basic logic for this blending is:
1495 * b < 0.5:
1496 * a --> 2 * a * (0.5 - f * (0.5 - b))
1497 * b >= 0.5:
1498 * a --> 1 - 2 * (1 - a) * (1 - (0.5 - f * (0.5 - b)))
1499 *
1500 * In the limit that f == 1 (standard hardlight blending):
1501 * b < 0.5: a --> 2 * a * b
1502 * or
1503 * a --> a - a * (1 - 2 * b)
1504 * b >= 0.5: a --> 1 - 2 * (1 - a) * (1 - b)
1505 * or
1506 * a --> a + (1 - a) * (2 * b - 1)
1507 *
1508 * You can see that for standard hardlight blending:
1509 * b < 0.5: a is pushed linearly with b down to 0
1510 * b >= 0.5: a is pushed linearly with b up to 1
1511 * a is unchanged if b = 0.5
1512 *
1513 * Our opacity factor f reduces the deviation of b from 0.5:
1514 * f == 0: b --> 0.5, so no blending occurs
1515 * f == 1: b --> b, so we get full conventional blending
1516 *
1517 * There is a variant of hardlight blending called "softlight" blending:
1518 * (e.g., http://jswidget.com/blog/tag/hard-light/)
1519 * b < 0.5:
1520 * a --> a - a * (0.5 - b) * (1 - Abs(2 * a - 1))
1521 * b >= 0.5:
1522 * a --> a + (1 - a) * (b - 0.5) * (1 - Abs(2 * a - 1))
1523 * which limits the amount that 'a' can be moved to a maximum of
1524 * halfway toward 0 or 1, and further reduces it as 'a' moves
1525 * away from 0.5.
1526 * As you can see, there are a nearly infinite number of different
1527 * blending formulas that can be conjured up.
1528 * </pre>
1529 */
1530static l_int32 blendHardLightComponents(l_int32 a,
1531 l_int32 b,
1532 l_float32 fract)
1533{
1534 if (b < 0x80) {
1535 b = 0x80 - (l_int32)(fract * (0x80 - b));
1536 return (a * b) >> 7;
1537 } else {
1538 b = 0x80 + (l_int32)(fract * (b - 0x80));
1539 return 0xff - (((0xff - b) * (0xff - a)) >> 7);
1540 }
1541}
1542
1543
1544/*-------------------------------------------------------------*
1545 * Blending two colormapped images *
1546 *-------------------------------------------------------------*/
1574l_ok
1576 PIX *pixb,
1577 l_int32 x,
1578 l_int32 y,
1579 l_int32 sindex)
1580{
1581l_int32 rval, gval, bval;
1582l_int32 i, j, w, h, d, ncb, wb, hb, wpls;
1583l_int32 index, val, nadded;
1584l_int32 lut[256];
1585l_uint32 pval;
1586l_uint32 *lines, *datas;
1587PIXCMAP *cmaps, *cmapb, *cmapsc;
1588
1589 PROCNAME("pixBlendCmap");
1590
1591 if (!pixs)
1592 return ERROR_INT("pixs not defined", procName, 1);
1593 if (!pixb)
1594 return ERROR_INT("pixb not defined", procName, 1);
1595 if ((cmaps = pixGetColormap(pixs)) == NULL)
1596 return ERROR_INT("no colormap in pixs", procName, 1);
1597 if ((cmapb = pixGetColormap(pixb)) == NULL)
1598 return ERROR_INT("no colormap in pixb", procName, 1);
1599 ncb = pixcmapGetCount(cmapb);
1600
1601 pixGetDimensions(pixs, &w, &h, &d);
1602 if (d != 2 && d != 4 && d != 8)
1603 return ERROR_INT("depth not in {2,4,8}", procName, 1);
1604
1605 /* Make a copy of cmaps; we'll add to this if necessary
1606 * and substitute at the end if we found there was enough room
1607 * to hold all the new colors. */
1608 cmapsc = pixcmapCopy(cmaps);
1609
1610 /* Add new colors if necessary; get mapping array between
1611 * cmaps and cmapb. */
1612 for (i = 0, nadded = 0; i < ncb; i++) {
1613 pixcmapGetColor(cmapb, i, &rval, &gval, &bval);
1614 if (pixcmapGetIndex(cmapsc, rval, gval, bval, &index)) { /* not found */
1615 if (pixcmapAddColor(cmapsc, rval, gval, bval)) {
1616 pixcmapDestroy(&cmapsc);
1617 return ERROR_INT("not enough room in cmaps", procName, 1);
1618 }
1619 lut[i] = pixcmapGetCount(cmapsc) - 1;
1620 nadded++;
1621 } else {
1622 lut[i] = index;
1623 }
1624 }
1625
1626 /* Replace cmaps if colors have been added. */
1627 if (nadded == 0)
1628 pixcmapDestroy(&cmapsc);
1629 else
1630 pixSetColormap(pixs, cmapsc);
1631
1632 /* Replace each pixel value sindex by mapped colormap index when
1633 * a blender pixel in pixbc overlays it. */
1634 datas = pixGetData(pixs);
1635 wpls = pixGetWpl(pixs);
1636 pixGetDimensions(pixb, &wb, &hb, NULL);
1637 for (i = 0; i < hb; i++) {
1638 if (i + y < 0 || i + y >= h) continue;
1639 lines = datas + (y + i) * wpls;
1640 for (j = 0; j < wb; j++) {
1641 if (j + x < 0 || j + x >= w) continue;
1642 switch (d) {
1643 case 2:
1644 val = GET_DATA_DIBIT(lines, x + j);
1645 if (val == sindex) {
1646 pixGetPixel(pixb, j, i, &pval);
1647 SET_DATA_DIBIT(lines, x + j, lut[pval]);
1648 }
1649 break;
1650 case 4:
1651 val = GET_DATA_QBIT(lines, x + j);
1652 if (val == sindex) {
1653 pixGetPixel(pixb, j, i, &pval);
1654 SET_DATA_QBIT(lines, x + j, lut[pval]);
1655 }
1656 break;
1657 case 8:
1658 val = GET_DATA_BYTE(lines, x + j);
1659 if (val == sindex) {
1660 pixGetPixel(pixb, j, i, &pval);
1661 SET_DATA_BYTE(lines, x + j, lut[pval]);
1662 }
1663 break;
1664 default:
1665 return ERROR_INT("depth not in {2,4,8}", procName, 1);
1666 }
1667 }
1668 }
1669
1670 return 0;
1671}
1672
1673
1674/*---------------------------------------------------------------------*
1675 * Blending two images using a third *
1676 *---------------------------------------------------------------------*/
1711PIX *
1713 PIX *pixs2,
1714 PIX *pixg,
1715 l_int32 x,
1716 l_int32 y)
1717{
1718l_int32 w1, h1, d1, w2, h2, d2, spp, wg, hg, wmin, hmin, wpld, wpls, wplg;
1719l_int32 i, j, val, dval, sval;
1720l_int32 drval, dgval, dbval, srval, sgval, sbval;
1721l_uint32 dval32, sval32;
1722l_uint32 *datad, *datas, *datag, *lined, *lines, *lineg;
1723l_float32 fract;
1724PIX *pixr1, *pixr2, *pix1, *pix2, *pixg2, *pixd;
1725
1726 PROCNAME("pixBlendWithGrayMask");
1727
1728 if (!pixs1)
1729 return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
1730 if (!pixs2)
1731 return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
1732 pixGetDimensions(pixs1, &w1, &h1, &d1);
1733 pixGetDimensions(pixs2, &w2, &h2, &d2);
1734 if (d1 == 1 || d2 == 1)
1735 return (PIX *)ERROR_PTR("pixs1 or pixs2 is 1 bpp", procName, NULL);
1736 if (pixg) {
1737 if (pixGetDepth(pixg) != 8)
1738 return (PIX *)ERROR_PTR("pixg not 8 bpp", procName, NULL);
1739 pixGetDimensions(pixg, &wg, &hg, NULL);
1740 wmin = L_MIN(w2, wg);
1741 hmin = L_MIN(h2, hg);
1742 pixg2 = pixClone(pixg);
1743 } else { /* use the alpha component of pixs2 */
1744 spp = pixGetSpp(pixs2);
1745 if (d2 != 32 || spp != 4)
1746 return (PIX *)ERROR_PTR("no alpha; pixs2 not rgba", procName, NULL);
1747 wmin = w2;
1748 hmin = h2;
1749 pixg2 = pixGetRGBComponent(pixs2, L_ALPHA_CHANNEL);
1750 }
1751
1752 /* Remove colormaps if they exist; clones are OK */
1755
1756 /* Regularize to the same depth if necessary */
1757 d1 = pixGetDepth(pixr1);
1758 d2 = pixGetDepth(pixr2);
1759 if (d1 == 32) { /* convert d2 to rgb if necessary */
1760 pix1 = pixClone(pixr1);
1761 if (d2 != 32)
1762 pix2 = pixConvertTo32(pixr2);
1763 else
1764 pix2 = pixClone(pixr2);
1765 } else if (d2 == 32) { /* and d1 != 32; convert to 32 */
1766 pix2 = pixClone(pixr2);
1767 pix1 = pixConvertTo32(pixr1);
1768 } else { /* both are 8 bpp or less */
1769 pix1 = pixConvertTo8(pixr1, FALSE);
1770 pix2 = pixConvertTo8(pixr2, FALSE);
1771 }
1772 pixDestroy(&pixr1);
1773 pixDestroy(&pixr2);
1774
1775 /* Sanity check: both either 8 or 32 bpp */
1776 d1 = pixGetDepth(pix1);
1777 d2 = pixGetDepth(pix2);
1778 if (d1 != d2 || (d1 != 8 && d1 != 32)) {
1779 pixDestroy(&pix1);
1780 pixDestroy(&pix2);
1781 pixDestroy(&pixg2);
1782 return (PIX *)ERROR_PTR("depths not regularized! bad!", procName, NULL);
1783 }
1784
1785 /* Start with a copy of pix1 */
1786 pixd = pixCopy(NULL, pix1);
1787 pixDestroy(&pix1);
1788
1789 /* Blend pix2 onto pixd, using pixg2.
1790 * Let the normalized pixel value of pixg2 be f = pixval / 255,
1791 * and the pixel values of pixd and pix2 be p1 and p2, rsp.
1792 * Then the blended value is:
1793 * p = (1.0 - f) * p1 + f * p2
1794 * Blending is done component-wise if rgb.
1795 * Scan over pix2 and pixg2, clipping to pixd where necessary. */
1796 datad = pixGetData(pixd);
1797 datas = pixGetData(pix2);
1798 datag = pixGetData(pixg2);
1799 wpld = pixGetWpl(pixd);
1800 wpls = pixGetWpl(pix2);
1801 wplg = pixGetWpl(pixg2);
1802 for (i = 0; i < hmin; i++) {
1803 if (i + y < 0 || i + y >= h1) continue;
1804 lined = datad + (i + y) * wpld;
1805 lines = datas + i * wpls;
1806 lineg = datag + i * wplg;
1807 for (j = 0; j < wmin; j++) {
1808 if (j + x < 0 || j + x >= w1) continue;
1809 val = GET_DATA_BYTE(lineg, j);
1810 if (val == 0) continue; /* pix2 is transparent */
1811 fract = (l_float32)val / 255.;
1812 if (d1 == 8) {
1813 dval = GET_DATA_BYTE(lined, j + x);
1814 sval = GET_DATA_BYTE(lines, j);
1815 dval = (l_int32)((1.0 - fract) * dval + fract * sval);
1816 SET_DATA_BYTE(lined, j + x, dval);
1817 } else { /* 32 */
1818 dval32 = *(lined + j + x);
1819 sval32 = *(lines + j);
1820 extractRGBValues(dval32, &drval, &dgval, &dbval);
1821 extractRGBValues(sval32, &srval, &sgval, &sbval);
1822 drval = (l_int32)((1.0 - fract) * drval + fract * srval);
1823 dgval = (l_int32)((1.0 - fract) * dgval + fract * sgval);
1824 dbval = (l_int32)((1.0 - fract) * dbval + fract * sbval);
1825 composeRGBPixel(drval, dgval, dbval, &dval32);
1826 *(lined + j + x) = dval32;
1827 }
1828 }
1829 }
1830
1831 pixDestroy(&pixg2);
1832 pixDestroy(&pix2);
1833 return pixd;
1834}
1835
1836
1837/*---------------------------------------------------------------------*
1838 * Blending background to a specific color *
1839 *---------------------------------------------------------------------*/
1865PIX *
1867 PIX *pixs,
1868 BOX *box,
1869 l_uint32 color,
1870 l_float32 gamma,
1871 l_int32 minval,
1872 l_int32 maxval)
1873{
1874l_int32 x, y, w, h;
1875BOX *boxt;
1876PIX *pixt, *pixc, *pixr, *pixg;
1877
1878 PROCNAME("pixBlendBackgroundToColor");
1879
1880 if (!pixs)
1881 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1882 if (pixGetDepth(pixs) != 32)
1883 return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, pixd);
1884 if (pixd && (pixd != pixs))
1885 return (PIX *)ERROR_PTR("pixd neither null nor pixs", procName, pixd);
1886
1887 /* Extract the (optionally cropped) region, pixr, and generate
1888 * an identically sized pixc with the uniform color. */
1889 if (!pixd)
1890 pixd = pixCopy(NULL, pixs);
1891 if (box) {
1892 pixr = pixClipRectangle(pixd, box, &boxt);
1893 boxGetGeometry(boxt, &x, &y, &w, &h);
1894 pixc = pixCreate(w, h, 32);
1895 boxDestroy(&boxt);
1896 } else {
1897 pixc = pixCreateTemplate(pixs);
1898 pixr = pixClone(pixd);
1899 }
1900 pixSetAllArbitrary(pixc, color);
1901
1902 /* Set up the alpha channel */
1903 pixg = pixConvertTo8(pixr, 0);
1904 pixGammaTRC(pixg, pixg, gamma, minval, maxval);
1906
1907 /* Blend and replace in pixd */
1908 pixt = pixBlendWithGrayMask(pixr, pixc, NULL, 0, 0);
1909 if (box) {
1910 pixRasterop(pixd, x, y, w, h, PIX_SRC, pixt, 0, 0);
1911 pixDestroy(&pixt);
1912 } else {
1913 pixTransferAllData(pixd, &pixt, 0, 0);
1914 }
1915
1916 pixDestroy(&pixc);
1917 pixDestroy(&pixr);
1918 pixDestroy(&pixg);
1919 return pixd;
1920}
1921
1922
1923/*---------------------------------------------------------------------*
1924 * Multiplying by a specific color *
1925 *---------------------------------------------------------------------*/
1945PIX *
1947 PIX *pixs,
1948 BOX *box,
1949 l_uint32 color)
1950{
1951l_int32 i, j, bx, by, w, h, wpl;
1952l_int32 red, green, blue, rval, gval, bval, nrval, ngval, nbval;
1953l_float32 frval, fgval, fbval;
1954l_uint32 *data, *line;
1955PIX *pixt;
1956
1957 PROCNAME("pixMultiplyByColor");
1958
1959 if (!pixs)
1960 return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1961 if (pixGetDepth(pixs) != 32)
1962 return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, pixd);
1963 if (pixd && (pixd != pixs))
1964 return (PIX *)ERROR_PTR("pixd neither null nor pixs", procName, pixd);
1965
1966 if (!pixd)
1967 pixd = pixCopy(NULL, pixs);
1968 if (box) {
1969 boxGetGeometry(box, &bx, &by, NULL, NULL);
1970 pixt = pixClipRectangle(pixd, box, NULL);
1971 } else {
1972 pixt = pixClone(pixd);
1973 }
1974
1975 /* Multiply each pixel in pixt by the color */
1976 extractRGBValues(color, &red, &green, &blue);
1977 frval = (1. / 255.) * red;
1978 fgval = (1. / 255.) * green;
1979 fbval = (1. / 255.) * blue;
1980 data = pixGetData(pixt);
1981 wpl = pixGetWpl(pixt);
1982 pixGetDimensions(pixt, &w, &h, NULL);
1983 for (i = 0; i < h; i++) {
1984 line = data + i * wpl;
1985 for (j = 0; j < w; j++) {
1986 extractRGBValues(line[j], &rval, &gval, &bval);
1987 nrval = (l_int32)(frval * rval + 0.5);
1988 ngval = (l_int32)(fgval * gval + 0.5);
1989 nbval = (l_int32)(fbval * bval + 0.5);
1990 composeRGBPixel(nrval, ngval, nbval, line + j);
1991 }
1992 }
1993
1994 /* Replace */
1995 if (box)
1996 pixRasterop(pixd, bx, by, w, h, PIX_SRC, pixt, 0, 0);
1997 pixDestroy(&pixt);
1998 return pixd;
1999}
2000
2001
2002/*---------------------------------------------------------------------*
2003 * Rendering with alpha blending over a uniform background *
2004 *---------------------------------------------------------------------*/
2023PIX *
2025 l_uint32 color)
2026{
2027PIX *pixt, *pixd;
2028
2029 PROCNAME("pixAlphaBlendUniform");
2030
2031 if (!pixs)
2032 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2033 if (pixGetDepth(pixs) != 32)
2034 return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
2035 if (pixGetSpp(pixs) != 4) {
2036 L_WARNING("no alpha channel; returning clone\n", procName);
2037 return pixClone(pixs);
2038 }
2039
2040 pixt = pixCreateTemplate(pixs);
2041 pixSetAllArbitrary(pixt, color);
2042 pixSetSpp(pixt, 3); /* not required */
2043 pixd = pixBlendWithGrayMask(pixt, pixs, NULL, 0, 0);
2044
2045 pixDestroy(&pixt);
2046 return pixd;
2047}
2048
2049
2050/*---------------------------------------------------------------------*
2051 * Adding an alpha layer for blending *
2052 *---------------------------------------------------------------------*/
2076PIX *
2078 l_float32 fract,
2079 l_int32 invert)
2080{
2081PIX *pixd, *pix1, *pix2;
2082
2083 PROCNAME("pixAddAlphaToBlend");
2084
2085 if (!pixs)
2086 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2087 if (fract < 0.0 || fract > 1.0)
2088 return (PIX *)ERROR_PTR("invalid fract", procName, NULL);
2089
2090 /* Convert to 32 bpp */
2091 if (pixGetColormap(pixs))
2093 else
2094 pix1 = pixClone(pixs);
2095 pixd = pixConvertTo32(pix1); /* new */
2096
2097 /* Use an inverted image if this will be blended with a dark image */
2098 if (invert) pixInvert(pixd, pixd);
2099
2100 /* Generate alpha layer */
2101 pix2 = pixConvertTo8(pix1, 0); /* new */
2102 pixInvert(pix2, pix2);
2103 pixMultConstantGray(pix2, fract);
2105
2106 pixDestroy(&pix1);
2107 pixDestroy(&pix2);
2108 return pixd;
2109}
2110
2111
2112
2113/*---------------------------------------------------------------------*
2114 * Setting a transparent alpha component over a white background *
2115 *---------------------------------------------------------------------*/
2135PIX *
2137{
2138PIX *pixd, *pix1, *pix2, *pix3, *pix4;
2139
2140 PROCNAME("pixSetAlphaOverWhite");
2141
2142 if (!pixs)
2143 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2144 if (!(pixGetDepth(pixs) == 32 || pixGetColormap(pixs)))
2145 return (PIX *)ERROR_PTR("pixs not 32 bpp or cmapped", procName, NULL);
2146
2147 /* Remove colormap if it exists; otherwise copy */
2149
2150 /* Generate a 1 bpp image where a white pixel in pixd is 0.
2151 * In the comments below, a "white" pixel refers to pixd.
2152 * pix1 is rgb, pix2 is 8 bpp gray, pix3 is 1 bpp. */
2153 pix1 = pixInvert(NULL, pixd); /* send white (255) to 0 for each sample */
2154 pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX); /* 0 if white */
2155 pix3 = pixThresholdToBinary(pix2, 1); /* sets white pixels to 1 */
2156 pixInvert(pix3, pix3); /* sets white pixels to 0 */
2157
2158 /* Generate the alpha component using the distance transform,
2159 * which measures the distance to the nearest bg (0) pixel in pix3.
2160 * After multiplying by 128, its value is 0 (transparent)
2161 * over white pixels, and goes to opaque (255) two pixels away
2162 * from the nearest white pixel. */
2163 pix4 = pixDistanceFunction(pix3, 8, 8, L_BOUNDARY_FG);
2164 pixMultConstantGray(pix4, 128.0);
2166
2167 pixDestroy(&pix1);
2168 pixDestroy(&pix2);
2169 pixDestroy(&pix3);
2170 pixDestroy(&pix4);
2171 return pixd;
2172}
2173
2174
2175/*---------------------------------------------------------------------*
2176 * Fading from the edge *
2177 *---------------------------------------------------------------------*/
2197l_ok
2199 l_int32 dir,
2200 l_int32 fadeto,
2201 l_float32 distfract,
2202 l_float32 maxfade)
2203{
2204l_int32 i, j, w, h, d, wpl, xmin, ymin, range, val, rval, gval, bval;
2205l_float32 slope, limit, del;
2206l_uint32 *data, *line;
2207
2208 PROCNAME("pixLinearEdgeFade");
2209
2210 if (!pixs)
2211 return ERROR_INT("pixs not defined", procName, 1);
2212 if (pixGetColormap(pixs) != NULL)
2213 return ERROR_INT("pixs has a colormap", procName, 1);
2214 pixGetDimensions(pixs, &w, &h, &d);
2215 if (d != 8 && d != 32)
2216 return ERROR_INT("pixs not 8 or 32 bpp", procName, 1);
2217 if (dir != L_FROM_LEFT && dir != L_FROM_RIGHT &&
2218 dir != L_FROM_TOP && dir != L_FROM_BOT)
2219 return ERROR_INT("invalid fade direction from edge", procName, 1);
2220 if (fadeto != L_BLEND_TO_WHITE && fadeto != L_BLEND_TO_BLACK)
2221 return ERROR_INT("invalid fadeto photometry", procName, 1);
2222 if (maxfade <= 0) return 0;
2223 if (maxfade > 1.0)
2224 return ERROR_INT("invalid maxfade", procName, 1);
2225 if (distfract <= 0 || distfract * L_MIN(w, h) < 1.0) {
2226 L_INFO("distfract is too small\n", procName);
2227 return 0;
2228 }
2229 if (distfract > 1.0)
2230 return ERROR_INT("invalid distfract", procName, 1);
2231
2232 /* Set up parameters */
2233 if (dir == L_FROM_LEFT) {
2234 range = (l_int32)(distfract * w);
2235 xmin = 0;
2236 slope = maxfade / (l_float32)range;
2237 } else if (dir == L_FROM_RIGHT) {
2238 range = (l_int32)(distfract * w);
2239 xmin = w - range;
2240 slope = maxfade / (l_float32)range;
2241 } else if (dir == L_FROM_TOP) {
2242 range = (l_int32)(distfract * h);
2243 ymin = 0;
2244 slope = maxfade / (l_float32)range;
2245 } else if (dir == L_FROM_BOT) {
2246 range = (l_int32)(distfract * h);
2247 ymin = h - range;
2248 slope = maxfade / (l_float32)range;
2249 }
2250
2251 limit = (fadeto == L_BLEND_TO_WHITE) ? 255.0 : 0.0;
2252 data = pixGetData(pixs);
2253 wpl = pixGetWpl(pixs);
2254 if (dir == L_FROM_LEFT || dir == L_FROM_RIGHT) {
2255 for (j = 0; j < range; j++) {
2256 del = (dir == L_FROM_LEFT) ? maxfade - slope * j
2257 : maxfade - slope * (range - j);
2258 for (i = 0; i < h; i++) {
2259 line = data + i * wpl;
2260 if (d == 8) {
2261 val = GET_DATA_BYTE(line, xmin + j);
2262 val += (limit - val) * del + 0.5;
2263 SET_DATA_BYTE(line, xmin + j, val);
2264 } else { /* rgb */
2265 extractRGBValues(*(line + xmin + j), &rval, &gval, &bval);
2266 rval += (limit - rval) * del + 0.5;
2267 gval += (limit - gval) * del + 0.5;
2268 bval += (limit - bval) * del + 0.5;
2269 composeRGBPixel(rval, gval, bval, line + xmin + j);
2270 }
2271 }
2272 }
2273 } else { /* dir == L_FROM_TOP || L_FROM_BOT */
2274 for (i = 0; i < range; i++) {
2275 del = (dir == L_FROM_TOP) ? maxfade - slope * i
2276 : maxfade - slope * (range - i);
2277 line = data + (ymin + i) * wpl;
2278 for (j = 0; j < w; j++) {
2279 if (d == 8) {
2280 val = GET_DATA_BYTE(line, j);
2281 val += (limit - val) * del + 0.5;
2282 SET_DATA_BYTE(line, j, val);
2283 } else { /* rgb */
2284 extractRGBValues(*(line + j), &rval, &gval, &bval);
2285 rval += (limit - rval) * del + 0.5;
2286 gval += (limit - gval) * del + 0.5;
2287 bval += (limit - bval) * del + 0.5;
2288 composeRGBPixel(rval, gval, bval, line + j);
2289 }
2290 }
2291 }
2292 }
2293
2294 return 0;
2295}
#define GET_DATA_QBIT(pdata, n)
Definition: arrayaccess.h:164
#define SET_DATA_DIBIT(pdata, n, val)
Definition: arrayaccess.h:149
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
#define GET_DATA_DIBIT(pdata, n)
Definition: arrayaccess.h:145
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
#define SET_DATA_QBIT(pdata, n, val)
Definition: arrayaccess.h:168
PIX * pixBlend(PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract)
pixBlend()
Definition: blend.c:176
PIX * pixBlendWithGrayMask(PIX *pixs1, PIX *pixs2, PIX *pixg, l_int32 x, l_int32 y)
pixBlendWithGrayMask()
Definition: blend.c:1712
PIX * pixMultiplyByColor(PIX *pixd, PIX *pixs, BOX *box, l_uint32 color)
pixMultiplyByColor()
Definition: blend.c:1946
PIX * pixAddAlphaToBlend(PIX *pixs, l_float32 fract, l_int32 invert)
pixAddAlphaToBlend()
Definition: blend.c:2077
PIX * pixBlendGrayAdapt(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 shift)
pixBlendGrayAdapt()
Definition: blend.c:1076
PIX * pixAlphaBlendUniform(PIX *pixs, l_uint32 color)
pixAlphaBlendUniform()
Definition: blend.c:2024
PIX * pixBlendColor(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 transparent, l_uint32 transpix)
pixBlendColor()
Definition: blend.c:828
PIX * pixSetAlphaOverWhite(PIX *pixs)
pixSetAlphaOverWhite()
Definition: blend.c:2136
PIX * pixBlendGray(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 type, l_int32 transparent, l_uint32 transpix)
pixBlendGray()
Definition: blend.c:494
l_ok pixLinearEdgeFade(PIX *pixs, l_int32 dir, l_int32 fadeto, l_float32 distfract, l_float32 maxfade)
pixLinearEdgeFade()
Definition: blend.c:2198
PIX * pixBlendBackgroundToColor(PIX *pixd, PIX *pixs, BOX *box, l_uint32 color, l_float32 gamma, l_int32 minval, l_int32 maxval)
pixBlendBackgroundToColor()
Definition: blend.c:1866
PIX * pixBlendGrayInverse(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract)
pixBlendGrayInverse()
Definition: blend.c:694
PIX * pixBlendMask(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 type)
pixBlendMask()
Definition: blend.c:265
l_ok pixBlendCmap(PIX *pixs, PIX *pixb, l_int32 x, l_int32 y, l_int32 sindex)
pixBlendCmap()
Definition: blend.c:1575
PIX * pixFadeWithGray(PIX *pixs, PIX *pixb, l_float32 factor, l_int32 type)
pixFadeWithGray()
Definition: blend.c:1250
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:282
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:172
l_ok boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:313
l_ok boxIntersects(BOX *box1, BOX *box2, l_int32 *presult)
boxIntersects()
Definition: boxfunc1.c:141
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:279
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:708
PIXCMAP * pixcmapCopy(const PIXCMAP *cmaps)
pixcmapCopy()
Definition: colormap.c:248
l_int32 pixcmapGetIndex(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex)
pixcmapGetIndex()
Definition: colormap.c:1036
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:824
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
PIX * pixGammaTRC(PIX *pixd, PIX *pixs, l_float32 gamma, l_int32 minval, l_int32 maxval)
pixGammaTRC()
Definition: enhance.c:176
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:447
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1699
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
l_ok pixTransferAllData(PIX *pixd, PIX **ppixs, l_int32 copytext, l_int32 copyformat)
pixTransferAllData()
Definition: pix1.c:902
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:263
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:190
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2751
void extractRGBValues(l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
extractRGBValues()
Definition: pix2.c:2820
PIX * pixGetRGBComponent(PIX *pixs, l_int32 comp)
pixGetRGBComponent()
Definition: pix2.c:2479
l_ok pixSetRGBComponent(PIX *pixd, PIX *pixs, l_int32 comp)
pixSetRGBComponent()
Definition: pix2.c:2538
l_ok pixSetAllArbitrary(PIX *pix, l_uint32 val)
pixSetAllArbitrary()
Definition: pix2.c:951
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
l_ok pixGetRankValueMasked(PIX *pixs, PIX *pixm, l_int32 x, l_int32 y, l_int32 factor, l_float32 rank, l_float32 *pval, NUMA **pna)
pixGetRankValueMasked()
Definition: pix4.c:1168
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:1026
@ L_ALPHA_CHANNEL
Definition: pix.h:207
@ L_BLEND_GRAY
Definition: pix.h:756
@ L_BLEND_TO_WHITE
Definition: pix.h:754
@ L_BLEND_WITH_INVERSE
Definition: pix.h:753
@ L_BLEND_TO_BLACK
Definition: pix.h:755
@ L_BLEND_GRAY_WITH_INVERSE
Definition: pix.h:757
@ REMOVE_CMAP_TO_FULL_COLOR
Definition: pix.h:258
@ REMOVE_CMAP_BASED_ON_SRC
Definition: pix.h:260
@ L_COPY
Definition: pix.h:712
@ L_FROM_BOT
Definition: pix.h:1037
@ L_FROM_LEFT
Definition: pix.h:1034
@ L_FROM_RIGHT
Definition: pix.h:1035
@ L_FROM_TOP
Definition: pix.h:1036
#define PIX_SRC
Definition: pix.h:330
l_ok pixMultConstantGray(PIX *pixs, l_float32 val)
pixMultConstantGray()
Definition: pixarith.c:190
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3133
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:963
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3332
PIX * pixRemoveColormapGeneral(PIX *pixs, l_int32 type, l_int32 ifnocmap)
pixRemoveColormapGeneral()
Definition: pixconv.c:278
l_ok pixRasterop(PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy)
pixRasterop()
Definition: rop.c:204
PIX * pixDistanceFunction(PIX *pixs, l_int32 connectivity, l_int32 outdepth, l_int32 boundcond)
pixDistanceFunction()
Definition: seedfill.c:2535
Definition: pix.h:481
Definition: pix.h:139