Leptonica  1.83.1
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 
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  *-------------------------------------------------------------*/
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 */
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,
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 
262 PIX *
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) {
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  {
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 
489 PIX *
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) {
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 
687 PIX *
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) {
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 
819 PIX *
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 
1063 PIX *
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) {
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 
1235 PIX *
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 */
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 */
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 */
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  *-------------------------------------------------------------*/
1556 l_ok
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  *---------------------------------------------------------------------*/
1691 PIX *
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 */
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  *---------------------------------------------------------------------*/
1843 PIX *
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  *---------------------------------------------------------------------*/
1921 PIX *
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  *---------------------------------------------------------------------*/
1997 PIX *
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  *---------------------------------------------------------------------*/
2048 PIX *
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))
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  *---------------------------------------------------------------------*/
2105 PIX *
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 */
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  *---------------------------------------------------------------------*/
2165 l_ok
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 if (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 }
#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 * pixAlphaBlendUniform(PIX *pixs, l_uint32 color)
pixAlphaBlendUniform()
Definition: blend.c:1998
PIX * pixBlendGrayAdapt(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 shift)
pixBlendGrayAdapt()
Definition: blend.c:1064
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:820
PIX * pixMultiplyByColor(PIX *pixd, PIX *pixs, BOX *box, l_uint32 color)
pixMultiplyByColor()
Definition: blend.c:1922
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:490
PIX * pixBlendWithGrayMask(PIX *pixs1, PIX *pixs2, PIX *pixg, l_int32 x, l_int32 y)
pixBlendWithGrayMask()
Definition: blend.c:1692
l_ok pixLinearEdgeFade(PIX *pixs, l_int32 dir, l_int32 fadeto, l_float32 distfract, l_float32 maxfade)
pixLinearEdgeFade()
Definition: blend.c:2166
PIX * pixAddAlphaToBlend(PIX *pixs, l_float32 fract, l_int32 invert)
pixAddAlphaToBlend()
Definition: blend.c:2049
PIX * pixFadeWithGray(PIX *pixs, PIX *pixb, l_float32 factor, l_int32 type)
pixFadeWithGray()
Definition: blend.c:1236
PIX * pixSetAlphaOverWhite(PIX *pixs)
pixSetAlphaOverWhite()
Definition: blend.c:2106
PIX * pixBlendMask(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 type)
pixBlendMask()
Definition: blend.c:263
PIX * pixBlend(PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract)
pixBlend()
Definition: blend.c:176
PIX * pixBlendGrayInverse(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract)
pixBlendGrayInverse()
Definition: blend.c:688
PIX * pixBlendBackgroundToColor(PIX *pixd, PIX *pixs, BOX *box, l_uint32 color, l_float32 gamma, l_int32 minval, l_int32 maxval)
pixBlendBackgroundToColor()
Definition: blend.c:1844
l_ok pixBlendCmap(PIX *pixs, PIX *pixb, l_int32 x, l_int32 y, l_int32 sindex)
pixBlendCmap()
Definition: blend.c:1557
l_ok boxGetGeometry(const BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:301
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:273
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:171
l_ok boxIntersects(BOX *box1, BOX *box2, l_int32 *presult)
boxIntersects()
Definition: boxfunc1.c:140
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:272
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:683
l_int32 pixcmapGetIndex(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex)
pixcmapGetIndex()
Definition: colormap.c:989
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:789
PIXCMAP * pixcmapCopy(const PIXCMAP *cmaps)
pixcmapCopy()
Definition: colormap.c:243
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:403
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:443
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1642
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1582
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:608
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1074
PIX * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:689
l_ok pixTransferAllData(PIX *pixd, PIX **ppixs, l_int32 copytext, l_int32 copyformat)
pixTransferAllData()
Definition: pix1.c:879
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:380
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:582
PIX * pixGetRGBComponent(PIX *pixs, l_int32 comp)
pixGetRGBComponent()
Definition: pix2.c:2464
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:192
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2728
void extractRGBValues(l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
extractRGBValues()
Definition: pix2.c:2793
l_ok pixSetRGBComponent(PIX *pixd, PIX *pixs, l_int32 comp)
pixSetRGBComponent()
Definition: pix2.c:2521
l_ok pixSetAllArbitrary(PIX *pix, l_uint32 val)
pixSetAllArbitrary()
Definition: pix2.c:929
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1481
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:1138
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:994
@ L_ALPHA_CHANNEL
Definition: pix.h:331
@ L_BLEND_GRAY
Definition: pix.h:549
@ L_BLEND_TO_WHITE
Definition: pix.h:547
@ L_BLEND_WITH_INVERSE
Definition: pix.h:546
@ L_BLEND_TO_BLACK
Definition: pix.h:548
@ L_BLEND_GRAY_WITH_INVERSE
Definition: pix.h:550
@ REMOVE_CMAP_TO_FULL_COLOR
Definition: pix.h:382
@ REMOVE_CMAP_BASED_ON_SRC
Definition: pix.h:384
@ L_COPY
Definition: pix.h:505
@ L_FROM_BOT
Definition: pix.h:830
@ L_FROM_LEFT
Definition: pix.h:827
@ L_FROM_RIGHT
Definition: pix.h:828
@ L_FROM_TOP
Definition: pix.h:829
#define PIX_SRC
Definition: pix.h:444
l_ok pixMultConstantGray(PIX *pixs, l_float32 val)
pixMultConstantGray()
Definition: pixarith.c:188
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:947
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:324
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3055
PIX * pixRemoveColormapGeneral(PIX *pixs, l_int32 type, l_int32 ifnocmap)
pixRemoveColormapGeneral()
Definition: pixconv.c:276
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3246
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:2499