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 
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  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 
264 PIX *
266  PIX *pixs1,
267  PIX *pixs2,
268  l_int32 x,
269  l_int32 y,
270  l_float32 fract,
271  l_int32 type)
272 {
273 l_int32 i, j, d, wc, hc, w, h, wplc;
274 l_int32 val, rval, gval, bval;
275 l_uint32 pixval;
276 l_uint32 *linec, *datac;
277 PIX *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);
301  type = L_BLEND_WITH_INVERSE;
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 
493 PIX *
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 {
504 l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta;
505 l_int32 ival, irval, igval, ibval, cval, dval;
506 l_uint32 val32;
507 l_uint32 *linec, *lined, *datac, *datad;
508 PIX *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 
693 PIX *
695  PIX *pixs1,
696  PIX *pixs2,
697  l_int32 x,
698  l_int32 y,
699  l_float32 fract)
700 {
701 l_int32 i, j, d, wc, hc, w, h, wplc, wpld;
702 l_int32 irval, igval, ibval, cval, dval;
703 l_float32 a;
704 l_uint32 val32;
705 l_uint32 *linec, *lined, *datac, *datad;
706 PIX *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 
827 PIX *
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 {
837 l_int32 i, j, wc, hc, w, h, wplc, wpld;
838 l_int32 rval, gval, bval, rcval, gcval, bcval;
839 l_uint32 cval32, val32;
840 l_uint32 *linec, *lined, *datac, *datad;
841 PIX *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  */
945 PIX *
946 pixBlendColorByChannel(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 {
957 l_int32 i, j, wc, hc, w, h, wplc, wpld;
958 l_int32 rval, gval, bval, rcval, gcval, bcval;
959 l_uint32 cval32, val32;
960 l_uint32 *linec, *lined, *datac, *datad;
961 PIX *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 
1018 static l_int32
1019 blendComponents(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 
1075 PIX *
1077  PIX *pixs1,
1078  PIX *pixs2,
1079  l_int32 x,
1080  l_int32 y,
1081  l_float32 fract,
1082  l_int32 shift)
1083 {
1084 l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta, overlap;
1085 l_int32 rval, gval, bval, cval, dval, mval, median, pivot;
1086 l_uint32 val32;
1087 l_uint32 *linec, *lined, *datac, *datad;
1088 l_float32 fmedian, factor;
1089 BOX *box, *boxt;
1090 PIX *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 
1249 PIX *
1251  PIX *pixb,
1252  l_float32 factor,
1253  l_int32 type)
1254 {
1255 l_int32 i, j, w, h, d, wb, hb, db, wd, hd, wplb, wpld;
1256 l_int32 valb, vald, nvald, rval, gval, bval, nrval, ngval, nbval;
1257 l_float32 nfactor, fract;
1258 l_uint32 val32, nval32;
1259 l_uint32 *lined, *datad, *lineb, *datab;
1260 PIX *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  */
1365 PIX *
1366 pixBlendHardLight(PIX *pixd,
1367  PIX *pixs1,
1368  PIX *pixs2,
1369  l_int32 x,
1370  l_int32 y,
1371  l_float32 fract)
1372 {
1373 l_int32 i, j, w, h, d, wc, hc, dc, wplc, wpld;
1374 l_int32 cval, dval, rcval, gcval, bcval, rdval, gdval, bdval;
1375 l_uint32 cval32, dval32;
1376 l_uint32 *linec, *lined, *datac, *datad;
1377 PIX *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  */
1530 static 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  *-------------------------------------------------------------*/
1574 l_ok
1576  PIX *pixb,
1577  l_int32 x,
1578  l_int32 y,
1579  l_int32 sindex)
1580 {
1581 l_int32 rval, gval, bval;
1582 l_int32 i, j, w, h, d, ncb, wb, hb, wpls;
1583 l_int32 index, val, nadded;
1584 l_int32 lut[256];
1585 l_uint32 pval;
1586 l_uint32 *lines, *datas;
1587 PIXCMAP *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  *---------------------------------------------------------------------*/
1711 PIX *
1713  PIX *pixs2,
1714  PIX *pixg,
1715  l_int32 x,
1716  l_int32 y)
1717 {
1718 l_int32 w1, h1, d1, w2, h2, d2, spp, wg, hg, wmin, hmin, wpld, wpls, wplg;
1719 l_int32 i, j, val, dval, sval;
1720 l_int32 drval, dgval, dbval, srval, sgval, sbval;
1721 l_uint32 dval32, sval32;
1722 l_uint32 *datad, *datas, *datag, *lined, *lines, *lineg;
1723 l_float32 fract;
1724 PIX *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  *---------------------------------------------------------------------*/
1865 PIX *
1867  PIX *pixs,
1868  BOX *box,
1869  l_uint32 color,
1870  l_float32 gamma,
1871  l_int32 minval,
1872  l_int32 maxval)
1873 {
1874 l_int32 x, y, w, h;
1875 BOX *boxt;
1876 PIX *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);
1905  pixSetRGBComponent(pixc, pixg, L_ALPHA_CHANNEL);
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  *---------------------------------------------------------------------*/
1945 PIX *
1947  PIX *pixs,
1948  BOX *box,
1949  l_uint32 color)
1950 {
1951 l_int32 i, j, bx, by, w, h, wpl;
1952 l_int32 red, green, blue, rval, gval, bval, nrval, ngval, nbval;
1953 l_float32 frval, fgval, fbval;
1954 l_uint32 *data, *line;
1955 PIX *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  *---------------------------------------------------------------------*/
2023 PIX *
2025  l_uint32 color)
2026 {
2027 PIX *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  *---------------------------------------------------------------------*/
2076 PIX *
2078  l_float32 fract,
2079  l_int32 invert)
2080 {
2081 PIX *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);
2104  pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL);
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  *---------------------------------------------------------------------*/
2135 PIX *
2137 {
2138 PIX *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);
2165  pixSetRGBComponent(pixd, pix4, L_ALPHA_CHANNEL);
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  *---------------------------------------------------------------------*/
2197 l_ok
2199  l_int32 dir,
2200  l_int32 fadeto,
2201  l_float32 distfract,
2202  l_float32 maxfade)
2203 {
2204 l_int32 i, j, w, h, d, wpl, xmin, ymin, range, val, rval, gval, bval;
2205 l_float32 slope, limit, del;
2206 l_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 }
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
l_ok pixSetRGBComponent(PIX *pixd, PIX *pixs, l_int32 comp)
pixSetRGBComponent()
Definition: pix2.c:2538
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3332
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
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
Definition: pix.h:712
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 * pixCopy(PIX *pixd, const PIX *pixs)
pixCopy()
Definition: pix1.c:705
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3133
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 * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
struct Pix * pixs
Definition: bilateral.h:117
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1509
#define SET_DATA_QBIT(pdata, n, val)
Definition: arrayaccess.h:168
PIX * pixSetAlphaOverWhite(PIX *pixs)
pixSetAlphaOverWhite()
Definition: blend.c:2136
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:279
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:447
PIX * pixRemoveColormapGeneral(PIX *pixs, l_int32 type, l_int32 ifnocmap)
pixRemoveColormapGeneral()
Definition: pixconv.c:278
l_float32 * range
Definition: bilateral.h:123
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 * pixFadeWithGray(PIX *pixs, PIX *pixb, l_float32 factor, l_int32 type)
pixFadeWithGray()
Definition: blend.c:1250
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:1026
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1699
PIX * pixGetRGBComponent(PIX *pixs, l_int32 comp)
pixGetRGBComponent()
Definition: pix2.c:2479
#define SET_DATA_DIBIT(pdata, n, val)
Definition: arrayaccess.h:149
l_ok pixBlendCmap(PIX *pixs, PIX *pixb, l_int32 x, l_int32 y, l_int32 sindex)
pixBlendCmap()
Definition: blend.c:1575
l_ok pixSetAllArbitrary(PIX *pix, l_uint32 val)
pixSetAllArbitrary()
Definition: pix2.c:951
l_int32 pixcmapGetIndex(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex)
pixcmapGetIndex()
Definition: colormap.c:1036
PIX * pixAlphaBlendUniform(PIX *pixs, l_uint32 color)
pixAlphaBlendUniform()
Definition: blend.c:2024
PIX * pixMultiplyByColor(PIX *pixd, PIX *pixs, BOX *box, l_uint32 color)
pixMultiplyByColor()
Definition: blend.c:1946
PIX * pixBlend(PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract)
pixBlend()
Definition: blend.c:176
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:263
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:824
l_int32 maxval
Definition: bilateral.h:125
PIX * pixBlendWithGrayMask(PIX *pixs1, PIX *pixs2, PIX *pixg, l_int32 x, l_int32 y)
pixBlendWithGrayMask()
Definition: blend.c:1712
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
#define GET_DATA_QBIT(pdata, n)
Definition: arrayaccess.h:164
PIX * pixBlendGrayInverse(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract)
pixBlendGrayInverse()
Definition: blend.c:694
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:963
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
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 * pixBlendMask(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 type)
pixBlendMask()
Definition: blend.c:265
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:190
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
PIX * pixAddAlphaToBlend(PIX *pixs, l_float32 fract, l_int32 invert)
pixAddAlphaToBlend()
Definition: blend.c:2077
PIX * pixDistanceFunction(PIX *pixs, l_int32 connectivity, l_int32 outdepth, l_int32 boundcond)
pixDistanceFunction()
Definition: seedfill.c:2535
#define GET_DATA_DIBIT(pdata, n)
Definition: arrayaccess.h:145
Definition: pix.h:138
l_ok pixMultConstantGray(PIX *pixs, l_float32 val)
pixMultConstantGray()
Definition: pixarith.c:190
#define PIX_SRC
Definition: pix.h:330
PIX * pixGammaTRC(PIX *pixd, PIX *pixs, l_float32 gamma, l_int32 minval, l_int32 maxval)
pixGammaTRC()
Definition: enhance.c:176
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:282
l_ok boxIntersects(BOX *box1, BOX *box2, l_int32 *presult)
boxIntersects()
Definition: boxfunc1.c:141
l_int32 pixcmapGetCount(const PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:708
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2751
PIXCMAP * pixcmapCopy(const PIXCMAP *cmaps)
pixcmapCopy()
Definition: colormap.c:248
l_ok pixTransferAllData(PIX *pixd, PIX **ppixs, l_int32 copytext, l_int32 copyformat)
pixTransferAllData()
Definition: pix1.c:902
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:414
l_ok boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:313
Definition: pix.h:480
void extractRGBValues(l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
extractRGBValues()
Definition: pix2.c:2820
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:172
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
l_ok pixLinearEdgeFade(PIX *pixs, l_int32 dir, l_int32 fadeto, l_float32 distfract, l_float32 maxfade)
pixLinearEdgeFade()
Definition: blend.c:2198
l_int32 minval
Definition: bilateral.h:124