Leptonica  1.77.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 
148 #include "allheaders.h"
149 
150 static l_int32 blendComponents(l_int32 a, l_int32 b, l_float32 fract);
151 static l_int32 blendHardLightComponents(l_int32 a, l_int32 b, l_float32 fract);
152 
153 
154 /*-------------------------------------------------------------*
155  * Blending two images that are not colormapped *
156  *-------------------------------------------------------------*/
173 PIX *
174 pixBlend(PIX *pixs1,
175  PIX *pixs2,
176  l_int32 x,
177  l_int32 y,
178  l_float32 fract)
179 {
180 l_int32 w1, h1, d1, d2;
181 BOX *box;
182 PIX *pixc, *pixt, *pixd;
183 
184  PROCNAME("pixBlend");
185 
186  if (!pixs1)
187  return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
188  if (!pixs2)
189  return (PIX *)ERROR_PTR("pixs2 not defined", procName, 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  procName, 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", procName);
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 
261 PIX *
263  PIX *pixs1,
264  PIX *pixs2,
265  l_int32 x,
266  l_int32 y,
267  l_float32 fract,
268  l_int32 type)
269 {
270 l_int32 i, j, d, wc, hc, w, h, wplc;
271 l_int32 val, rval, gval, bval;
272 l_uint32 pixval;
273 l_uint32 *linec, *datac;
274 PIX *pixc, *pix1, *pix2;
275 
276  PROCNAME("pixBlendMask");
277 
278  if (!pixs1)
279  return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
280  if (!pixs2)
281  return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
282  if (pixGetDepth(pixs1) == 1)
283  return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, NULL);
284  if (pixGetDepth(pixs2) != 1)
285  return (PIX *)ERROR_PTR("pixs2 not 1 bpp", procName, NULL);
286  if (pixd == pixs1 && pixGetColormap(pixs1))
287  return (PIX *)ERROR_PTR("inplace; pixs1 has colormap", procName, NULL);
288  if (pixd && (pixd != pixs1))
289  return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, NULL);
290  if (fract < 0.0 || fract > 1.0) {
291  L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
292  fract = 0.5;
293  }
294  if (type != L_BLEND_WITH_INVERSE && type != L_BLEND_TO_WHITE &&
295  type != L_BLEND_TO_BLACK) {
296  L_WARNING("invalid blend type; setting to L_BLEND_WITH_INVERSE\n",
297  procName);
298  type = L_BLEND_WITH_INVERSE;
299  }
300 
301  /* If pixd != NULL, we know that it is equal to pixs1 and
302  * that pixs1 does not have a colormap, so that an in-place operation
303  * can be done. Otherwise, remove colormap from pixs1 if
304  * it exists and unpack to at least 8 bpp if necessary,
305  * to do the blending on a new pix. */
306  if (!pixd) {
308  if (pixGetDepth(pix1) < 8)
309  pix2 = pixConvertTo8(pix1, FALSE);
310  else
311  pix2 = pixClone(pix1);
312  pixd = pixCopy(NULL, pix2);
313  pixDestroy(&pix1);
314  pixDestroy(&pix2);
315  }
316 
317  pixGetDimensions(pixd, &w, &h, &d); /* d must be either 8 or 32 bpp */
318  pixc = pixClone(pixs2);
319  wc = pixGetWidth(pixc);
320  hc = pixGetHeight(pixc);
321  datac = pixGetData(pixc);
322  wplc = pixGetWpl(pixc);
323 
324  /* Check limits for src1, in case clipping was not done. */
325  switch (type)
326  {
328  /*
329  * The basic logic for this blending is:
330  * p --> (1 - f) * p + f * (1 - p)
331  * where p is a normalized value: p = pixval / 255.
332  * Thus,
333  * p --> p + f * (1 - 2 * p)
334  */
335  for (i = 0; i < hc; i++) {
336  if (i + y < 0 || i + y >= h) continue;
337  linec = datac + i * wplc;
338  for (j = 0; j < wc; j++) {
339  if (j + x < 0 || j + x >= w) continue;
340  bval = GET_DATA_BIT(linec, j);
341  if (bval) {
342  switch (d)
343  {
344  case 8:
345  pixGetPixel(pixd, x + j, y + i, &pixval);
346  val = (l_int32)(pixval + fract * (255 - 2 * pixval));
347  pixSetPixel(pixd, x + j, y + i, val);
348  break;
349  case 32:
350  pixGetPixel(pixd, x + j, y + i, &pixval);
351  extractRGBValues(pixval, &rval, &gval, &bval);
352  rval = (l_int32)(rval + fract * (255 - 2 * rval));
353  gval = (l_int32)(gval + fract * (255 - 2 * gval));
354  bval = (l_int32)(bval + fract * (255 - 2 * bval));
355  composeRGBPixel(rval, gval, bval, &pixval);
356  pixSetPixel(pixd, x + j, y + i, pixval);
357  break;
358  default:
359  L_WARNING("d neither 8 nor 32 bpp; no blend\n",
360  procName);
361  }
362  }
363  }
364  }
365  break;
366  case L_BLEND_TO_WHITE:
367  /*
368  * The basic logic for this blending is:
369  * p --> p + f * (1 - p) (p normalized to [0...1])
370  */
371  for (i = 0; i < hc; i++) {
372  if (i + y < 0 || i + y >= h) continue;
373  linec = datac + i * wplc;
374  for (j = 0; j < wc; j++) {
375  if (j + x < 0 || j + x >= w) continue;
376  bval = GET_DATA_BIT(linec, j);
377  if (bval) {
378  switch (d)
379  {
380  case 8:
381  pixGetPixel(pixd, x + j, y + i, &pixval);
382  val = (l_int32)(pixval + fract * (255 - pixval));
383  pixSetPixel(pixd, x + j, y + i, val);
384  break;
385  case 32:
386  pixGetPixel(pixd, x + j, y + i, &pixval);
387  extractRGBValues(pixval, &rval, &gval, &bval);
388  rval = (l_int32)(rval + fract * (255 - rval));
389  gval = (l_int32)(gval + fract * (255 - gval));
390  bval = (l_int32)(bval + fract * (255 - bval));
391  composeRGBPixel(rval, gval, bval, &pixval);
392  pixSetPixel(pixd, x + j, y + i, pixval);
393  break;
394  default:
395  L_WARNING("d neither 8 nor 32 bpp; no blend\n",
396  procName);
397  }
398  }
399  }
400  }
401  break;
402  case L_BLEND_TO_BLACK:
403  /*
404  * The basic logic for this blending is:
405  * p --> (1 - f) * p (p normalized to [0...1])
406  */
407  for (i = 0; i < hc; i++) {
408  if (i + y < 0 || i + y >= h) continue;
409  linec = datac + i * wplc;
410  for (j = 0; j < wc; j++) {
411  if (j + x < 0 || j + x >= w) continue;
412  bval = GET_DATA_BIT(linec, j);
413  if (bval) {
414  switch (d)
415  {
416  case 8:
417  pixGetPixel(pixd, x + j, y + i, &pixval);
418  val = (l_int32)((1. - fract) * pixval);
419  pixSetPixel(pixd, x + j, y + i, val);
420  break;
421  case 32:
422  pixGetPixel(pixd, x + j, y + i, &pixval);
423  extractRGBValues(pixval, &rval, &gval, &bval);
424  rval = (l_int32)((1. - fract) * rval);
425  gval = (l_int32)((1. - fract) * gval);
426  bval = (l_int32)((1. - fract) * bval);
427  composeRGBPixel(rval, gval, bval, &pixval);
428  pixSetPixel(pixd, x + j, y + i, pixval);
429  break;
430  default:
431  L_WARNING("d neither 8 nor 32 bpp; no blend\n",
432  procName);
433  }
434  }
435  }
436  }
437  break;
438  default:
439  L_WARNING("invalid binary mask blend type\n", procName);
440  break;
441  }
442 
443  pixDestroy(&pixc);
444  return pixd;
445 }
446 
447 
488 PIX *
490  PIX *pixs1,
491  PIX *pixs2,
492  l_int32 x,
493  l_int32 y,
494  l_float32 fract,
495  l_int32 type,
496  l_int32 transparent,
497  l_uint32 transpix)
498 {
499 l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta;
500 l_int32 ival, irval, igval, ibval, cval, dval;
501 l_uint32 val32;
502 l_uint32 *linec, *lined, *datac, *datad;
503 PIX *pixc, *pix1, *pix2;
504 
505  PROCNAME("pixBlendGray");
506 
507  if (!pixs1)
508  return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
509  if (!pixs2)
510  return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
511  if (pixGetDepth(pixs1) == 1)
512  return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd);
513  if (pixd == pixs1 && pixGetColormap(pixs1))
514  return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd);
515  if (pixd && (pixd != pixs1))
516  return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd);
517  if (fract < 0.0 || fract > 1.0) {
518  L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
519  fract = 0.5;
520  }
521  if (type != L_BLEND_GRAY && type != L_BLEND_GRAY_WITH_INVERSE) {
522  L_WARNING("invalid blend type; setting to L_BLEND_GRAY\n", procName);
523  type = L_BLEND_GRAY;
524  }
525 
526  /* If pixd != NULL, we know that it is equal to pixs1 and
527  * that pixs1 does not have a colormap, so that an in-place operation
528  * can be done. Otherwise, remove colormap from pixs1 if
529  * it exists and unpack to at least 8 bpp if necessary,
530  * to do the blending on a new pix. */
531  if (!pixd) {
533  if (pixGetDepth(pix1) < 8)
534  pix2 = pixConvertTo8(pix1, FALSE);
535  else
536  pix2 = pixClone(pix1);
537  pixd = pixCopy(NULL, pix2);
538  pixDestroy(&pix1);
539  pixDestroy(&pix2);
540  }
541 
542  pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */
543  wpld = pixGetWpl(pixd);
544  datad = pixGetData(pixd);
545  pixc = pixConvertTo8(pixs2, 0);
546  pixGetDimensions(pixc, &wc, &hc, NULL);
547  datac = pixGetData(pixc);
548  wplc = pixGetWpl(pixc);
549 
550  /* Check limits for src1, in case clipping was not done */
551  if (type == L_BLEND_GRAY) {
552  /*
553  * The basic logic for this blending is:
554  * p --> (1 - f) * p + f * c
555  * where c is the 8 bpp blender. All values are normalized to [0...1].
556  */
557  for (i = 0; i < hc; i++) {
558  if (i + y < 0 || i + y >= h) continue;
559  linec = datac + i * wplc;
560  lined = datad + (i + y) * wpld;
561  switch (d)
562  {
563  case 8:
564  for (j = 0; j < wc; j++) {
565  if (j + x < 0 || j + x >= w) continue;
566  cval = GET_DATA_BYTE(linec, j);
567  if (transparent == 0 || cval != transpix) {
568  dval = GET_DATA_BYTE(lined, j + x);
569  ival = (l_int32)((1. - fract) * dval + fract * cval);
570  SET_DATA_BYTE(lined, j + x, ival);
571  }
572  }
573  break;
574  case 32:
575  for (j = 0; j < wc; j++) {
576  if (j + x < 0 || j + x >= w) continue;
577  cval = GET_DATA_BYTE(linec, j);
578  if (transparent == 0 || cval != transpix) {
579  val32 = *(lined + j + x);
580  extractRGBValues(val32, &irval, &igval, &ibval);
581  irval = (l_int32)((1. - fract) * irval + fract * cval);
582  igval = (l_int32)((1. - fract) * igval + fract * cval);
583  ibval = (l_int32)((1. - fract) * ibval + fract * cval);
584  composeRGBPixel(irval, igval, ibval, &val32);
585  *(lined + j + x) = val32;
586  }
587  }
588  break;
589  default:
590  break; /* shouldn't happen */
591  }
592  }
593  } else { /* L_BLEND_GRAY_WITH_INVERSE */
594  for (i = 0; i < hc; i++) {
595  if (i + y < 0 || i + y >= h) continue;
596  linec = datac + i * wplc;
597  lined = datad + (i + y) * wpld;
598  switch (d)
599  {
600  case 8:
601  /*
602  * For 8 bpp, the dest pix is shifted by a signed amount
603  * proportional to the distance from 128 (the pivot value),
604  * and to the darkness of src2. If the dest is darker
605  * than 128, it becomes lighter, and v.v.
606  * The basic logic is:
607  * d --> d + f * (0.5 - d) * (1 - c)
608  * where d and c are normalized pixel values for src1 and
609  * src2, respectively, with 8 bit normalization to [0...1].
610  */
611  for (j = 0; j < wc; j++) {
612  if (j + x < 0 || j + x >= w) continue;
613  cval = GET_DATA_BYTE(linec, j);
614  if (transparent == 0 || cval != transpix) {
615  ival = GET_DATA_BYTE(lined, j + x);
616  delta = (128 - ival) * (255 - cval) / 256;
617  ival += (l_int32)(fract * delta + 0.5);
618  SET_DATA_BYTE(lined, j + x, ival);
619  }
620  }
621  break;
622  case 32:
623  /* Each component is shifted by the same formula for 8 bpp */
624  for (j = 0; j < wc; j++) {
625  if (j + x < 0 || j + x >= w) continue;
626  cval = GET_DATA_BYTE(linec, j);
627  if (transparent == 0 || cval != transpix) {
628  val32 = *(lined + j + x);
629  extractRGBValues(val32, &irval, &igval, &ibval);
630  delta = (128 - irval) * (255 - cval) / 256;
631  irval += (l_int32)(fract * delta + 0.5);
632  delta = (128 - igval) * (255 - cval) / 256;
633  igval += (l_int32)(fract * delta + 0.5);
634  delta = (128 - ibval) * (255 - cval) / 256;
635  ibval += (l_int32)(fract * delta + 0.5);
636  composeRGBPixel(irval, igval, ibval, &val32);
637  *(lined + j + x) = val32;
638  }
639  }
640  break;
641  default:
642  break; /* shouldn't happen */
643  }
644  }
645  }
646 
647  pixDestroy(&pixc);
648  return pixd;
649 }
650 
651 
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  PROCNAME("pixBlendGrayInverse");
703 
704  if (!pixs1)
705  return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
706  if (!pixs2)
707  return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
708  if (pixGetDepth(pixs1) == 1)
709  return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd);
710  if (pixd == pixs1 && pixGetColormap(pixs1))
711  return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd);
712  if (pixd && (pixd != pixs1))
713  return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd);
714  if (fract < 0.0 || fract > 1.0) {
715  L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
716  fract = 0.5;
717  }
718 
719  /* If pixd != NULL, we know that it is equal to pixs1 and
720  * that pixs1 does not have a colormap, so that an in-place operation
721  * can be done. Otherwise, remove colormap from pixs1 if
722  * it exists and unpack to at least 8 bpp if necessary,
723  * to do the blending on a new pix. */
724  if (!pixd) {
726  if (pixGetDepth(pix1) < 8)
727  pix2 = pixConvertTo8(pix1, FALSE);
728  else
729  pix2 = pixClone(pix1);
730  pixd = pixCopy(NULL, pix2);
731  pixDestroy(&pix1);
732  pixDestroy(&pix2);
733  }
734 
735  pixGetDimensions(pixd, &w, &h, &d); /* 8 or 32 bpp */
736  wpld = pixGetWpl(pixd);
737  datad = pixGetData(pixd);
738  pixc = pixConvertTo8(pixs2, 0);
739  pixGetDimensions(pixc, &wc, &hc, NULL);
740  datac = pixGetData(pixc);
741  wplc = pixGetWpl(pixc);
742 
743  /* Check limits for src1, in case clipping was not done */
744  for (i = 0; i < hc; i++) {
745  if (i + y < 0 || i + y >= h) continue;
746  linec = datac + i * wplc;
747  lined = datad + (i + y) * wpld;
748  switch (d)
749  {
750  case 8:
751  for (j = 0; j < wc; j++) {
752  if (j + x < 0 || j + x >= w) continue;
753  cval = GET_DATA_BYTE(linec, j);
754  dval = GET_DATA_BYTE(lined, j + x);
755  a = (1.0 - fract) * dval + fract * (255.0 - dval);
756  dval = (l_int32)(cval * dval / 255.0 +
757  a * (255.0 - cval) / 255.0);
758  SET_DATA_BYTE(lined, j + x, dval);
759  }
760  break;
761  case 32:
762  for (j = 0; j < wc; j++) {
763  if (j + x < 0 || j + x >= w) continue;
764  cval = GET_DATA_BYTE(linec, j);
765  val32 = *(lined + j + x);
766  extractRGBValues(val32, &irval, &igval, &ibval);
767  a = (1.0 - fract) * irval + fract * (255.0 - irval);
768  irval = (l_int32)(cval * irval / 255.0 +
769  a * (255.0 - cval) / 255.0);
770  a = (1.0 - fract) * igval + fract * (255.0 - igval);
771  igval = (l_int32)(cval * igval / 255.0 +
772  a * (255.0 - cval) / 255.0);
773  a = (1.0 - fract) * ibval + fract * (255.0 - ibval);
774  ibval = (l_int32)(cval * ibval / 255.0 +
775  a * (255.0 - cval) / 255.0);
776  composeRGBPixel(irval, igval, ibval, &val32);
777  *(lined + j + x) = val32;
778  }
779  break;
780  default:
781  break; /* shouldn't happen */
782  }
783  }
784 
785  pixDestroy(&pixc);
786  return pixd;
787 }
788 
789 
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  PROCNAME("pixBlendColor");
836 
837  if (!pixs1)
838  return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
839  if (!pixs2)
840  return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
841  if (pixGetDepth(pixs1) == 1)
842  return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, NULL);
843  if (pixd == pixs1 && pixGetDepth(pixs1) != 32)
844  return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", procName, NULL);
845  if (pixd && (pixd != pixs1))
846  return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, NULL);
847  if (fract < 0.0 || fract > 1.0) {
848  L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
849  fract = 0.5;
850  }
851 
852  /* If pixd != null, we know that it is equal to pixs1 and
853  * that pixs1 is 32 bpp rgb, so that an in-place operation
854  * can be done. Otherwise, pixConvertTo32() will remove a
855  * colormap from pixs1 if it exists and unpack to 32 bpp
856  * (if necessary) to do the blending on a new 32 bpp Pix. */
857  if (!pixd)
858  pixd = pixConvertTo32(pixs1);
859  pixGetDimensions(pixd, &w, &h, NULL);
860  wpld = pixGetWpl(pixd);
861  datad = pixGetData(pixd);
862  pixc = pixConvertTo32(pixs2); /* blend with 32 bpp rgb */
863  pixGetDimensions(pixc, &wc, &hc, NULL);
864  datac = pixGetData(pixc);
865  wplc = pixGetWpl(pixc);
866 
867  /* Check limits for src1, in case clipping was not done */
868  for (i = 0; i < hc; i++) {
869  /*
870  * The basic logic for this blending is:
871  * p --> (1 - f) * p + f * c
872  * for each color channel. c is a color component of the blender.
873  * All values are normalized to [0...1].
874  */
875  if (i + y < 0 || i + y >= h) continue;
876  linec = datac + i * wplc;
877  lined = datad + (i + y) * wpld;
878  for (j = 0; j < wc; j++) {
879  if (j + x < 0 || j + x >= w) continue;
880  cval32 = *(linec + j);
881  if (transparent == 0 ||
882  ((cval32 & 0xffffff00) != (transpix & 0xffffff00))) {
883  val32 = *(lined + j + x);
884  extractRGBValues(cval32, &rcval, &gcval, &bcval);
885  extractRGBValues(val32, &rval, &gval, &bval);
886  rval = (l_int32)((1. - fract) * rval + fract * rcval);
887  gval = (l_int32)((1. - fract) * gval + fract * gcval);
888  bval = (l_int32)((1. - fract) * bval + fract * bcval);
889  composeRGBPixel(rval, gval, bval, &val32);
890  *(lined + j + x) = val32;
891  }
892  }
893  }
894 
895  pixDestroy(&pixc);
896  return pixd;
897 }
898 
899 
900 /*
901  * pixBlendColorByChannel()
902  *
903  * Input: pixd (<optional>; either NULL or equal to pixs1 for in-place)
904  * pixs1 (blendee; depth > 1)
905  * pixs2 (blender, any depth; typ. smaller in size than pixs1)
906  * x,y (origin [UL corner] of pixs2 relative to
907  * the origin of pixs1)
908  * rfract, gfract, bfract (blending fractions by channel)
909  * transparent (1 to use transparency; 0 otherwise)
910  * transpix (pixel color in pixs2 that is to be transparent)
911  * Return: pixd if OK; pixs1 on error
912  *
913  * Notes:
914  * (1) This generalizes pixBlendColor() in two ways:
915  * (a) The mixing fraction is specified per channel.
916  * (b) The mixing fraction may be < 0 or > 1, in which case,
917  * the min or max of two images are taken, respectively.
918  * (2) Specifically,
919  * for p = pixs1[i], c = pixs2[i], f = fract[i], i = 1, 2, 3:
920  * f < 0.0: p --> min(p, c)
921  * 0.0 <= f <= 1.0: p --> (1 - f) * p + f * c
922  * f > 1.0: p --> max(a, c)
923  * Special cases:
924  * f = 0: p --> p
925  * f = 1: p --> c
926  * (3) See usage notes in pixBlendColor()
927  * (4) pixBlendColor() would be equivalent to
928  * pixBlendColorChannel(..., fract, fract, fract, ...);
929  * at a small cost of efficiency.
930  */
931 PIX *
932 pixBlendColorByChannel(PIX *pixd,
933  PIX *pixs1,
934  PIX *pixs2,
935  l_int32 x,
936  l_int32 y,
937  l_float32 rfract,
938  l_float32 gfract,
939  l_float32 bfract,
940  l_int32 transparent,
941  l_uint32 transpix)
942 {
943 l_int32 i, j, wc, hc, w, h, wplc, wpld;
944 l_int32 rval, gval, bval, rcval, gcval, bcval;
945 l_uint32 cval32, val32;
946 l_uint32 *linec, *lined, *datac, *datad;
947 PIX *pixc;
948 
949  PROCNAME("pixBlendColorByChannel");
950 
951  if (!pixs1)
952  return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
953  if (!pixs2)
954  return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
955  if (pixGetDepth(pixs1) == 1)
956  return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd);
957  if (pixd == pixs1 && pixGetDepth(pixs1) != 32)
958  return (PIX *)ERROR_PTR("inplace; pixs1 not 32 bpp", procName, pixd);
959  if (pixd && (pixd != pixs1))
960  return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd);
961 
962  /* If pixd != NULL, we know that it is equal to pixs1 and
963  * that pixs1 is 32 bpp rgb, so that an in-place operation
964  * can be done. Otherwise, pixConvertTo32() will remove a
965  * colormap from pixs1 if it exists and unpack to 32 bpp
966  * (if necessary) to do the blending on a new 32 bpp Pix. */
967  if (!pixd)
968  pixd = pixConvertTo32(pixs1);
969  pixGetDimensions(pixd, &w, &h, NULL);
970  wpld = pixGetWpl(pixd);
971  datad = pixGetData(pixd);
972  pixc = pixConvertTo32(pixs2);
973  pixGetDimensions(pixc, &wc, &hc, NULL);
974  datac = pixGetData(pixc);
975  wplc = pixGetWpl(pixc);
976 
977  /* Check limits for src1, in case clipping was not done */
978  for (i = 0; i < hc; i++) {
979  if (i + y < 0 || i + y >= h) continue;
980  linec = datac + i * wplc;
981  lined = datad + (i + y) * wpld;
982  for (j = 0; j < wc; j++) {
983  if (j + x < 0 || j + x >= w) continue;
984  cval32 = *(linec + j);
985  if (transparent == 0 ||
986  ((cval32 & 0xffffff00) != (transpix & 0xffffff00))) {
987  val32 = *(lined + j + x);
988  extractRGBValues(cval32, &rcval, &gcval, &bcval);
989  extractRGBValues(val32, &rval, &gval, &bval);
990  rval = blendComponents(rval, rcval, rfract);
991  gval = blendComponents(gval, gcval, gfract);
992  bval = blendComponents(bval, bcval, bfract);
993  composeRGBPixel(rval, gval, bval, &val32);
994  *(lined + j + x) = val32;
995  }
996  }
997  }
998 
999  pixDestroy(&pixc);
1000  return pixd;
1001 }
1002 
1003 
1004 static l_int32
1005 blendComponents(l_int32 a,
1006  l_int32 b,
1007  l_float32 fract)
1008 {
1009  if (fract < 0.)
1010  return ((a < b) ? a : b);
1011  if (fract > 1.)
1012  return ((a > b) ? a : b);
1013  return (l_int32)((1. - fract) * a + fract * b);
1014 }
1015 
1016 
1060 PIX *
1062  PIX *pixs1,
1063  PIX *pixs2,
1064  l_int32 x,
1065  l_int32 y,
1066  l_float32 fract,
1067  l_int32 shift)
1068 {
1069 l_int32 i, j, d, wc, hc, w, h, wplc, wpld, delta, overlap;
1070 l_int32 rval, gval, bval, cval, dval, mval, median, pivot;
1071 l_uint32 val32;
1072 l_uint32 *linec, *lined, *datac, *datad;
1073 l_float32 fmedian, factor;
1074 BOX *box, *boxt;
1075 PIX *pixc, *pix1, *pix2;
1076 
1077  PROCNAME("pixBlendGrayAdapt");
1078 
1079  if (!pixs1)
1080  return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1081  if (!pixs2)
1082  return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1083  if (pixGetDepth(pixs1) == 1)
1084  return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd);
1085  if (pixd == pixs1 && pixGetColormap(pixs1))
1086  return (PIX *)ERROR_PTR("can't do in-place with cmap", procName, pixd);
1087  if (pixd && (pixd != pixs1))
1088  return (PIX *)ERROR_PTR("pixd must be NULL or pixs1", procName, pixd);
1089  if (fract < 0.0 || fract > 1.0) {
1090  L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
1091  fract = 0.5;
1092  }
1093  if (shift == -1) shift = 64; /* default value */
1094  if (shift < 0 || shift > 127) {
1095  L_WARNING("invalid shift; setting to 64\n", procName);
1096  shift = 64;
1097  }
1098 
1099  /* Test for overlap */
1100  pixGetDimensions(pixs1, &w, &h, NULL);
1101  pixGetDimensions(pixs2, &wc, &hc, NULL);
1102  box = boxCreate(x, y, wc, hc);
1103  boxt = boxCreate(0, 0, w, h);
1104  boxIntersects(box, boxt, &overlap);
1105  boxDestroy(&boxt);
1106  if (!overlap) {
1107  boxDestroy(&box);
1108  return (PIX *)ERROR_PTR("no image overlap", procName, pixd);
1109  }
1110 
1111  /* If pixd != NULL, we know that it is equal to pixs1 and
1112  * that pixs1 does not have a colormap, so that an in-place operation
1113  * can be done. Otherwise, remove colormap from pixs1 if
1114  * it exists and unpack to at least 8 bpp if necessary,
1115  * to do the blending on a new pix. */
1116  if (!pixd) {
1118  if (pixGetDepth(pix1) < 8)
1119  pix2 = pixConvertTo8(pix1, FALSE);
1120  else
1121  pix2 = pixClone(pix1);
1122  pixd = pixCopy(NULL, pix2);
1123  pixDestroy(&pix1);
1124  pixDestroy(&pix2);
1125  }
1126 
1127  /* Get the median value in the region of blending */
1128  pix1 = pixClipRectangle(pixd, box, NULL);
1129  pix2 = pixConvertTo8(pix1, 0);
1130  pixGetRankValueMasked(pix2, NULL, 0, 0, 1, 0.5, &fmedian, NULL);
1131  median = (l_int32)(fmedian + 0.5);
1132  if (median < 128)
1133  pivot = median + shift;
1134  else
1135  pivot = median - shift;
1136  pixDestroy(&pix1);
1137  pixDestroy(&pix2);
1138  boxDestroy(&box);
1139 
1140  /* Process over src2; clip to src1. */
1141  d = pixGetDepth(pixd);
1142  wpld = pixGetWpl(pixd);
1143  datad = pixGetData(pixd);
1144  pixc = pixConvertTo8(pixs2, 0);
1145  datac = pixGetData(pixc);
1146  wplc = pixGetWpl(pixc);
1147  for (i = 0; i < hc; i++) {
1148  if (i + y < 0 || i + y >= h) continue;
1149  linec = datac + i * wplc;
1150  lined = datad + (i + y) * wpld;
1151  switch (d)
1152  {
1153  case 8:
1154  /*
1155  * For 8 bpp, the dest pix is shifted by an amount
1156  * proportional to the distance from the pivot value,
1157  * and to the darkness of src2. In no situation will it
1158  * pass the pivot value in intensity.
1159  * The basic logic is:
1160  * d --> d + f * (np - d) * (1 - c)
1161  * where np, d and c are normalized pixel values for
1162  * the pivot, src1 and src2, respectively, with normalization
1163  * to 255.
1164  */
1165  for (j = 0; j < wc; j++) {
1166  if (j + x < 0 || j + x >= w) continue;
1167  dval = GET_DATA_BYTE(lined, j + x);
1168  cval = GET_DATA_BYTE(linec, j);
1169  delta = (pivot - dval) * (255 - cval) / 256;
1170  dval += (l_int32)(fract * delta + 0.5);
1171  SET_DATA_BYTE(lined, j + x, dval);
1172  }
1173  break;
1174  case 32:
1175  /*
1176  * For 32 bpp, the dest pix is shifted by an amount
1177  * proportional to the max component distance from the
1178  * pivot value, and to the darkness of src2. Each component
1179  * is shifted by the same fraction, either up or down,
1180  * depending on the shift direction (which is toward the
1181  * pivot). The basic logic for the red component is:
1182  * r --> r + f * (np - m) * (1 - c) * (r / m)
1183  * where np, r, m and c are normalized pixel values for
1184  * the pivot, the r component of src1, the max component
1185  * of src1, and src2, respectively, again with normalization
1186  * to 255. Likewise for the green and blue components.
1187  */
1188  for (j = 0; j < wc; j++) {
1189  if (j + x < 0 || j + x >= w) continue;
1190  cval = GET_DATA_BYTE(linec, j);
1191  val32 = *(lined + j + x);
1192  extractRGBValues(val32, &rval, &gval, &bval);
1193  mval = L_MAX(rval, gval);
1194  mval = L_MAX(mval, bval);
1195  mval = L_MAX(mval, 1);
1196  delta = (pivot - mval) * (255 - cval) / 256;
1197  factor = fract * delta / mval;
1198  rval += (l_int32)(factor * rval + 0.5);
1199  gval += (l_int32)(factor * gval + 0.5);
1200  bval += (l_int32)(factor * bval + 0.5);
1201  composeRGBPixel(rval, gval, bval, &val32);
1202  *(lined + j + x) = val32;
1203  }
1204  break;
1205  default:
1206  break; /* shouldn't happen */
1207  }
1208  }
1209 
1210  pixDestroy(&pixc);
1211  return pixd;
1212 }
1213 
1214 
1234 PIX *
1236  PIX *pixb,
1237  l_float32 factor,
1238  l_int32 type)
1239 {
1240 l_int32 i, j, w, h, d, wb, hb, db, wd, hd, wplb, wpld;
1241 l_int32 valb, vald, nvald, rval, gval, bval, nrval, ngval, nbval;
1242 l_float32 nfactor, fract;
1243 l_uint32 val32, nval32;
1244 l_uint32 *lined, *datad, *lineb, *datab;
1245 PIX *pixd;
1246 
1247  PROCNAME("pixFadeWithGray");
1248 
1249  if (!pixs)
1250  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1251  if (!pixb)
1252  return (PIX *)ERROR_PTR("pixb not defined", procName, NULL);
1253  if (pixGetDepth(pixs) == 1)
1254  return (PIX *)ERROR_PTR("pixs is 1 bpp", procName, NULL);
1255  pixGetDimensions(pixb, &wb, &hb, &db);
1256  if (db != 8)
1257  return (PIX *)ERROR_PTR("pixb not 8 bpp", procName, NULL);
1258  if (factor < 0.0 || factor > 255.0)
1259  return (PIX *)ERROR_PTR("factor not in [0.0...255.0]", procName, NULL);
1260  if (type != L_BLEND_TO_WHITE && type != L_BLEND_TO_BLACK)
1261  return (PIX *)ERROR_PTR("invalid fade type", procName, NULL);
1262 
1263  /* Remove colormap if it exists; otherwise copy */
1265  pixGetDimensions(pixd, &wd, &hd, &d);
1266  w = L_MIN(wb, wd);
1267  h = L_MIN(hb, hd);
1268  datad = pixGetData(pixd);
1269  wpld = pixGetWpl(pixd);
1270  datab = pixGetData(pixb);
1271  wplb = pixGetWpl(pixb);
1272 
1273  /* The basic logic for this blending is, for each component p of pixs:
1274  * fade-to-white: p --> p + (f * c) * (1 - p)
1275  * fade-to-black: p --> p - (f * c) * p
1276  * with c being the 8 bpp blender pixel of pixb, and with both
1277  * p and c normalized to [0...1]. */
1278  nfactor = factor / 255.;
1279  for (i = 0; i < h; i++) {
1280  lineb = datab + i * wplb;
1281  lined = datad + i * wpld;
1282  for (j = 0; j < w; j++) {
1283  valb = GET_DATA_BYTE(lineb, j);
1284  fract = nfactor * (l_float32)valb;
1285  fract = L_MIN(fract, 1.0);
1286  if (d == 8) {
1287  vald = GET_DATA_BYTE(lined, j);
1288  if (type == L_BLEND_TO_WHITE)
1289  nvald = vald + (l_int32)(fract * (255. - (l_float32)vald));
1290  else /* L_BLEND_TO_BLACK */
1291  nvald = vald - (l_int32)(fract * (l_float32)vald);
1292  SET_DATA_BYTE(lined, j, nvald);
1293  } else { /* d == 32 */
1294  val32 = lined[j];
1295  extractRGBValues(val32, &rval, &gval, &bval);
1296  if (type == L_BLEND_TO_WHITE) {
1297  nrval = rval + (l_int32)(fract * (255. - (l_float32)rval));
1298  ngval = gval + (l_int32)(fract * (255. - (l_float32)gval));
1299  nbval = bval + (l_int32)(fract * (255. - (l_float32)bval));
1300  } else {
1301  nrval = rval - (l_int32)(fract * (l_float32)rval);
1302  ngval = gval - (l_int32)(fract * (l_float32)gval);
1303  nbval = bval - (l_int32)(fract * (l_float32)bval);
1304  }
1305  composeRGBPixel(nrval, ngval, nbval, &nval32);
1306  lined[j] = nval32;
1307  }
1308  }
1309  }
1310 
1311  return pixd;
1312 }
1313 
1314 
1315 /*
1316  * pixBlendHardLight()
1317  *
1318  * Input: pixd (<optional>; either NULL or equal to pixs1 for in-place)
1319  * pixs1 (blendee; depth > 1, may be cmapped)
1320  * pixs2 (blender, 8 or 32 bpp; may be colormapped;
1321  * typ. smaller in size than pixs1)
1322  * x,y (origin [UL corner] of pixs2 relative to
1323  * the origin of pixs1)
1324  * fract (blending fraction, or 'opacity factor')
1325  * Return: pixd if OK; pixs1 on error
1326  *
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  */
1348 PIX *
1349 pixBlendHardLight(PIX *pixd,
1350  PIX *pixs1,
1351  PIX *pixs2,
1352  l_int32 x,
1353  l_int32 y,
1354  l_float32 fract)
1355 {
1356 l_int32 i, j, w, h, d, wc, hc, dc, wplc, wpld;
1357 l_int32 cval, dval, rcval, gcval, bcval, rdval, gdval, bdval;
1358 l_uint32 cval32, dval32;
1359 l_uint32 *linec, *lined, *datac, *datad;
1360 PIX *pixc, *pixt;
1361 
1362  PROCNAME("pixBlendHardLight");
1363 
1364  if (!pixs1)
1365  return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1366  if (!pixs2)
1367  return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1368  pixGetDimensions(pixs1, &w, &h, &d);
1369  pixGetDimensions(pixs2, &wc, &hc, &dc);
1370  if (d == 1)
1371  return (PIX *)ERROR_PTR("pixs1 is 1 bpp", procName, pixd);
1372  if (dc != 8 && dc != 32)
1373  return (PIX *)ERROR_PTR("pixs2 not 8 or 32 bpp", procName, pixd);
1374  if (pixd && (pixd != pixs1))
1375  return (PIX *)ERROR_PTR("inplace and pixd != pixs1", procName, pixd);
1376  if (pixd == pixs1 && pixGetColormap(pixs1))
1377  return (PIX *)ERROR_PTR("inplace and pixs1 cmapped", procName, pixd);
1378  if (pixd && d != 8 && d != 32)
1379  return (PIX *)ERROR_PTR("inplace and not 8 or 32 bpp", procName, pixd);
1380 
1381  if (fract < 0.0 || fract > 1.0) {
1382  L_WARNING("fract must be in [0.0, 1.0]; setting to 0.5\n", procName);
1383  fract = 0.5;
1384  }
1385 
1386  /* If pixs2 has a colormap, remove it */
1387  pixc = pixRemoveColormap(pixs2, REMOVE_CMAP_BASED_ON_SRC); /* clone ok */
1388  dc = pixGetDepth(pixc);
1389 
1390  /* There are 4 cases:
1391  * * pixs1 has or doesn't have a colormap
1392  * * pixc is either 8 or 32 bpp
1393  * In all situations, if pixs has a colormap it must be removed,
1394  * and pixd must have a depth that is equal to or greater than pixc. */
1395  if (dc == 32) {
1396  if (pixGetColormap(pixs1)) { /* pixd == NULL */
1398  } else {
1399  if (!pixd) {
1400  pixd = pixConvertTo32(pixs1);
1401  } else {
1402  pixt = pixConvertTo32(pixs1);
1403  pixCopy(pixd, pixt);
1404  pixDestroy(&pixt);
1405  }
1406  }
1407  d = 32;
1408  } else { /* dc == 8 */
1409  if (pixGetColormap(pixs1)) /* pixd == NULL */
1411  else
1412  pixd = pixCopy(pixd, pixs1);
1413  d = pixGetDepth(pixd);
1414  }
1415 
1416  if (!(d == 8 && dc == 8) && /* 3 cases only */
1417  !(d == 32 && dc == 8) &&
1418  !(d == 32 && dc == 32)) {
1419  pixDestroy(&pixc);
1420  return (PIX *)ERROR_PTR("bad! -- invalid depth combo!", procName, pixd);
1421  }
1422 
1423  wpld = pixGetWpl(pixd);
1424  datad = pixGetData(pixd);
1425  datac = pixGetData(pixc);
1426  wplc = pixGetWpl(pixc);
1427  for (i = 0; i < hc; i++) {
1428  if (i + y < 0 || i + y >= h) continue;
1429  linec = datac + i * wplc;
1430  lined = datad + (i + y) * wpld;
1431  for (j = 0; j < wc; j++) {
1432  if (j + x < 0 || j + x >= w) continue;
1433  if (d == 8 && dc == 8) {
1434  dval = GET_DATA_BYTE(lined, x + j);
1435  cval = GET_DATA_BYTE(linec, j);
1436  dval = blendHardLightComponents(dval, cval, fract);
1437  SET_DATA_BYTE(lined, x + j, dval);
1438  } else if (d == 32 && dc == 8) {
1439  dval32 = *(lined + x + j);
1440  extractRGBValues(dval32, &rdval, &gdval, &bdval);
1441  cval = GET_DATA_BYTE(linec, j);
1442  rdval = blendHardLightComponents(rdval, cval, fract);
1443  gdval = blendHardLightComponents(gdval, cval, fract);
1444  bdval = blendHardLightComponents(bdval, cval, fract);
1445  composeRGBPixel(rdval, gdval, bdval, &dval32);
1446  *(lined + x + j) = dval32;
1447  } else if (d == 32 && dc == 32) {
1448  dval32 = *(lined + x + j);
1449  extractRGBValues(dval32, &rdval, &gdval, &bdval);
1450  cval32 = *(linec + j);
1451  extractRGBValues(cval32, &rcval, &gcval, &bcval);
1452  rdval = blendHardLightComponents(rdval, rcval, fract);
1453  gdval = blendHardLightComponents(gdval, gcval, fract);
1454  bdval = blendHardLightComponents(bdval, bcval, fract);
1455  composeRGBPixel(rdval, gdval, bdval, &dval32);
1456  *(lined + x + j) = dval32;
1457  }
1458  }
1459  }
1460 
1461  pixDestroy(&pixc);
1462  return pixd;
1463 }
1464 
1465 
1466 /*
1467  * blendHardLightComponents()
1468  * Input: a (8 bpp blendee component)
1469  * b (8 bpp blender component)
1470  * fract (fraction of blending; use 1.0 for usual definition)
1471  * Return: blended 8 bpp component
1472  *
1473  * Notes:
1474  *
1475  * The basic logic for this blending is:
1476  * b < 0.5:
1477  * a --> 2 * a * (0.5 - f * (0.5 - b))
1478  * b >= 0.5:
1479  * a --> 1 - 2 * (1 - a) * (1 - (0.5 - f * (0.5 - b)))
1480  *
1481  * In the limit that f == 1 (standard hardlight blending):
1482  * b < 0.5: a --> 2 * a * b
1483  * or
1484  * a --> a - a * (1 - 2 * b)
1485  * b >= 0.5: a --> 1 - 2 * (1 - a) * (1 - b)
1486  * or
1487  * a --> a + (1 - a) * (2 * b - 1)
1488  *
1489  * You can see that for standard hardlight blending:
1490  * b < 0.5: a is pushed linearly with b down to 0
1491  * b >= 0.5: a is pushed linearly with b up to 1
1492  * a is unchanged if b = 0.5
1493  *
1494  * Our opacity factor f reduces the deviation of b from 0.5:
1495  * f == 0: b --> 0.5, so no blending occurs
1496  * f == 1: b --> b, so we get full conventional blending
1497  *
1498  * There is a variant of hardlight blending called "softlight" blending:
1499  * (e.g., http://jswidget.com/blog/tag/hard-light/)
1500  * b < 0.5:
1501  * a --> a - a * (0.5 - b) * (1 - Abs(2 * a - 1))
1502  * b >= 0.5:
1503  * a --> a + (1 - a) * (b - 0.5) * (1 - Abs(2 * a - 1))
1504  * which limits the amount that 'a' can be moved to a maximum of
1505  * halfway toward 0 or 1, and further reduces it as 'a' moves
1506  * away from 0.5.
1507  * As you can see, there are a nearly infinite number of different
1508  * blending formulas that can be conjured up.
1509  */
1510 static l_int32 blendHardLightComponents(l_int32 a,
1511  l_int32 b,
1512  l_float32 fract)
1513 {
1514  if (b < 0x80) {
1515  b = 0x80 - (l_int32)(fract * (0x80 - b));
1516  return (a * b) >> 7;
1517  } else {
1518  b = 0x80 + (l_int32)(fract * (b - 0x80));
1519  return 0xff - (((0xff - b) * (0xff - a)) >> 7);
1520  }
1521 }
1522 
1523 
1524 /*-------------------------------------------------------------*
1525  * Blending two colormapped images *
1526  *-------------------------------------------------------------*/
1554 l_ok
1556  PIX *pixb,
1557  l_int32 x,
1558  l_int32 y,
1559  l_int32 sindex)
1560 {
1561 l_int32 rval, gval, bval;
1562 l_int32 i, j, w, h, d, ncb, wb, hb, wpls;
1563 l_int32 index, val, nadded;
1564 l_int32 lut[256];
1565 l_uint32 pval;
1566 l_uint32 *lines, *datas;
1567 PIXCMAP *cmaps, *cmapb, *cmapsc;
1568 
1569  PROCNAME("pixBlendCmap");
1570 
1571  if (!pixs)
1572  return ERROR_INT("pixs not defined", procName, 1);
1573  if (!pixb)
1574  return ERROR_INT("pixb not defined", procName, 1);
1575  if ((cmaps = pixGetColormap(pixs)) == NULL)
1576  return ERROR_INT("no colormap in pixs", procName, 1);
1577  if ((cmapb = pixGetColormap(pixb)) == NULL)
1578  return ERROR_INT("no colormap in pixb", procName, 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}", procName, 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", procName, 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}", procName, 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  PROCNAME("pixBlendWithGrayMask");
1707 
1708  if (!pixs1)
1709  return (PIX *)ERROR_PTR("pixs1 not defined", procName, NULL);
1710  if (!pixs2)
1711  return (PIX *)ERROR_PTR("pixs2 not defined", procName, NULL);
1712  pixGetDimensions(pixs1, &w1, &h1, &d1);
1713  pixGetDimensions(pixs2, &w2, &h2, &d2);
1714  if (d1 == 1 || d2 == 1)
1715  return (PIX *)ERROR_PTR("pixs1 or pixs2 is 1 bpp", procName, NULL);
1716  if (pixg) {
1717  if (pixGetDepth(pixg) != 8)
1718  return (PIX *)ERROR_PTR("pixg not 8 bpp", procName, NULL);
1719  pixGetDimensions(pixg, &wg, &hg, NULL);
1720  wmin = L_MIN(w2, wg);
1721  hmin = L_MIN(h2, hg);
1722  pixg2 = pixClone(pixg);
1723  } else { /* use the alpha component of pixs2 */
1724  spp = pixGetSpp(pixs2);
1725  if (d2 != 32 || spp != 4)
1726  return (PIX *)ERROR_PTR("no alpha; pixs2 not rgba", procName, NULL);
1727  wmin = w2;
1728  hmin = h2;
1729  pixg2 = pixGetRGBComponent(pixs2, L_ALPHA_CHANNEL);
1730  }
1731 
1732  /* Remove colormaps if they exist; clones are OK */
1735 
1736  /* Regularize to the same depth if necessary */
1737  d1 = pixGetDepth(pixr1);
1738  d2 = pixGetDepth(pixr2);
1739  if (d1 == 32) { /* convert d2 to rgb if necessary */
1740  pix1 = pixClone(pixr1);
1741  if (d2 != 32)
1742  pix2 = pixConvertTo32(pixr2);
1743  else
1744  pix2 = pixClone(pixr2);
1745  } else if (d2 == 32) { /* and d1 != 32; convert to 32 */
1746  pix2 = pixClone(pixr2);
1747  pix1 = pixConvertTo32(pixr1);
1748  } else { /* both are 8 bpp or less */
1749  pix1 = pixConvertTo8(pixr1, FALSE);
1750  pix2 = pixConvertTo8(pixr2, FALSE);
1751  }
1752  pixDestroy(&pixr1);
1753  pixDestroy(&pixr2);
1754 
1755  /* Sanity check: both either 8 or 32 bpp */
1756  d1 = pixGetDepth(pix1);
1757  d2 = pixGetDepth(pix2);
1758  if (d1 != d2 || (d1 != 8 && d1 != 32)) {
1759  pixDestroy(&pix1);
1760  pixDestroy(&pix2);
1761  pixDestroy(&pixg2);
1762  return (PIX *)ERROR_PTR("depths not regularized! bad!", procName, NULL);
1763  }
1764 
1765  /* Start with a copy of pix1 */
1766  pixd = pixCopy(NULL, pix1);
1767  pixDestroy(&pix1);
1768 
1769  /* Blend pix2 onto pixd, using pixg2.
1770  * Let the normalized pixel value of pixg2 be f = pixval / 255,
1771  * and the pixel values of pixd and pix2 be p1 and p2, rsp.
1772  * Then the blended value is:
1773  * p = (1.0 - f) * p1 + f * p2
1774  * Blending is done component-wise if rgb.
1775  * Scan over pix2 and pixg2, clipping to pixd where necessary. */
1776  datad = pixGetData(pixd);
1777  datas = pixGetData(pix2);
1778  datag = pixGetData(pixg2);
1779  wpld = pixGetWpl(pixd);
1780  wpls = pixGetWpl(pix2);
1781  wplg = pixGetWpl(pixg2);
1782  for (i = 0; i < hmin; i++) {
1783  if (i + y < 0 || i + y >= h1) continue;
1784  lined = datad + (i + y) * wpld;
1785  lines = datas + i * wpls;
1786  lineg = datag + i * wplg;
1787  for (j = 0; j < wmin; j++) {
1788  if (j + x < 0 || j + x >= w1) continue;
1789  val = GET_DATA_BYTE(lineg, j);
1790  if (val == 0) continue; /* pix2 is transparent */
1791  fract = (l_float32)val / 255.;
1792  if (d1 == 8) {
1793  dval = GET_DATA_BYTE(lined, j + x);
1794  sval = GET_DATA_BYTE(lines, j);
1795  dval = (l_int32)((1.0 - fract) * dval + fract * sval);
1796  SET_DATA_BYTE(lined, j + x, dval);
1797  } else { /* 32 */
1798  dval32 = *(lined + j + x);
1799  sval32 = *(lines + j);
1800  extractRGBValues(dval32, &drval, &dgval, &dbval);
1801  extractRGBValues(sval32, &srval, &sgval, &sbval);
1802  drval = (l_int32)((1.0 - fract) * drval + fract * srval);
1803  dgval = (l_int32)((1.0 - fract) * dgval + fract * sgval);
1804  dbval = (l_int32)((1.0 - fract) * dbval + fract * sbval);
1805  composeRGBPixel(drval, dgval, dbval, &dval32);
1806  *(lined + j + x) = dval32;
1807  }
1808  }
1809  }
1810 
1811  pixDestroy(&pixg2);
1812  pixDestroy(&pix2);
1813  return pixd;
1814 }
1815 
1816 
1817 /*---------------------------------------------------------------------*
1818  * Blending background to a specific color *
1819  *---------------------------------------------------------------------*/
1845 PIX *
1847  PIX *pixs,
1848  BOX *box,
1849  l_uint32 color,
1850  l_float32 gamma,
1851  l_int32 minval,
1852  l_int32 maxval)
1853 {
1854 l_int32 x, y, w, h;
1855 BOX *boxt;
1856 PIX *pixt, *pixc, *pixr, *pixg;
1857 
1858  PROCNAME("pixBlendBackgroundToColor");
1859 
1860  if (!pixs)
1861  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1862  if (pixGetDepth(pixs) != 32)
1863  return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, pixd);
1864  if (pixd && (pixd != pixs))
1865  return (PIX *)ERROR_PTR("pixd neither null nor pixs", procName, pixd);
1866 
1867  /* Extract the (optionally cropped) region, pixr, and generate
1868  * an identically sized pixc with the uniform color. */
1869  if (!pixd)
1870  pixd = pixCopy(NULL, pixs);
1871  if (box) {
1872  pixr = pixClipRectangle(pixd, box, &boxt);
1873  boxGetGeometry(boxt, &x, &y, &w, &h);
1874  pixc = pixCreate(w, h, 32);
1875  boxDestroy(&boxt);
1876  } else {
1877  pixc = pixCreateTemplate(pixs);
1878  pixr = pixClone(pixd);
1879  }
1880  pixSetAllArbitrary(pixc, color);
1881 
1882  /* Set up the alpha channel */
1883  pixg = pixConvertTo8(pixr, 0);
1884  pixGammaTRC(pixg, pixg, gamma, minval, maxval);
1885  pixSetRGBComponent(pixc, pixg, L_ALPHA_CHANNEL);
1886 
1887  /* Blend and replace in pixd */
1888  pixt = pixBlendWithGrayMask(pixr, pixc, NULL, 0, 0);
1889  if (box) {
1890  pixRasterop(pixd, x, y, w, h, PIX_SRC, pixt, 0, 0);
1891  pixDestroy(&pixt);
1892  } else {
1893  pixTransferAllData(pixd, &pixt, 0, 0);
1894  }
1895 
1896  pixDestroy(&pixc);
1897  pixDestroy(&pixr);
1898  pixDestroy(&pixg);
1899  return pixd;
1900 }
1901 
1902 
1903 /*---------------------------------------------------------------------*
1904  * Multiplying by a specific color *
1905  *---------------------------------------------------------------------*/
1925 PIX *
1927  PIX *pixs,
1928  BOX *box,
1929  l_uint32 color)
1930 {
1931 l_int32 i, j, bx, by, w, h, wpl;
1932 l_int32 red, green, blue, rval, gval, bval, nrval, ngval, nbval;
1933 l_float32 frval, fgval, fbval;
1934 l_uint32 *data, *line;
1935 PIX *pixt;
1936 
1937  PROCNAME("pixMultiplyByColor");
1938 
1939  if (!pixs)
1940  return (PIX *)ERROR_PTR("pixs not defined", procName, pixd);
1941  if (pixGetDepth(pixs) != 32)
1942  return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, pixd);
1943  if (pixd && (pixd != pixs))
1944  return (PIX *)ERROR_PTR("pixd neither null nor pixs", procName, pixd);
1945 
1946  if (!pixd)
1947  pixd = pixCopy(NULL, pixs);
1948  if (box) {
1949  boxGetGeometry(box, &bx, &by, NULL, NULL);
1950  pixt = pixClipRectangle(pixd, box, NULL);
1951  } else {
1952  pixt = pixClone(pixd);
1953  }
1954 
1955  /* Multiply each pixel in pixt by the color */
1956  extractRGBValues(color, &red, &green, &blue);
1957  frval = (1. / 255.) * red;
1958  fgval = (1. / 255.) * green;
1959  fbval = (1. / 255.) * blue;
1960  data = pixGetData(pixt);
1961  wpl = pixGetWpl(pixt);
1962  pixGetDimensions(pixt, &w, &h, NULL);
1963  for (i = 0; i < h; i++) {
1964  line = data + i * wpl;
1965  for (j = 0; j < w; j++) {
1966  extractRGBValues(line[j], &rval, &gval, &bval);
1967  nrval = (l_int32)(frval * rval + 0.5);
1968  ngval = (l_int32)(fgval * gval + 0.5);
1969  nbval = (l_int32)(fbval * bval + 0.5);
1970  composeRGBPixel(nrval, ngval, nbval, line + j);
1971  }
1972  }
1973 
1974  /* Replace */
1975  if (box)
1976  pixRasterop(pixd, bx, by, w, h, PIX_SRC, pixt, 0, 0);
1977  pixDestroy(&pixt);
1978  return pixd;
1979 }
1980 
1981 
1982 /*---------------------------------------------------------------------*
1983  * Rendering with alpha blending over a uniform background *
1984  *---------------------------------------------------------------------*/
2003 PIX *
2005  l_uint32 color)
2006 {
2007 PIX *pixt, *pixd;
2008 
2009  PROCNAME("pixAlphaBlendUniform");
2010 
2011  if (!pixs)
2012  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2013  if (pixGetDepth(pixs) != 32)
2014  return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
2015  if (pixGetSpp(pixs) != 4) {
2016  L_WARNING("no alpha channel; returning clone\n", procName);
2017  return pixClone(pixs);
2018  }
2019 
2020  pixt = pixCreateTemplate(pixs);
2021  pixSetAllArbitrary(pixt, color);
2022  pixSetSpp(pixt, 3); /* not required */
2023  pixd = pixBlendWithGrayMask(pixt, pixs, NULL, 0, 0);
2024 
2025  pixDestroy(&pixt);
2026  return pixd;
2027 }
2028 
2029 
2030 /*---------------------------------------------------------------------*
2031  * Adding an alpha layer for blending *
2032  *---------------------------------------------------------------------*/
2056 PIX *
2058  l_float32 fract,
2059  l_int32 invert)
2060 {
2061 PIX *pixd, *pix1, *pix2;
2062 
2063  PROCNAME("pixAddAlphaToBlend");
2064 
2065  if (!pixs)
2066  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2067  if (fract < 0.0 || fract > 1.0)
2068  return (PIX *)ERROR_PTR("invalid fract", procName, NULL);
2069 
2070  /* Convert to 32 bpp */
2071  if (pixGetColormap(pixs))
2073  else
2074  pix1 = pixClone(pixs);
2075  pixd = pixConvertTo32(pix1); /* new */
2076 
2077  /* Use an inverted image if this will be blended with a dark image */
2078  if (invert) pixInvert(pixd, pixd);
2079 
2080  /* Generate alpha layer */
2081  pix2 = pixConvertTo8(pix1, 0); /* new */
2082  pixInvert(pix2, pix2);
2083  pixMultConstantGray(pix2, fract);
2084  pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL);
2085 
2086  pixDestroy(&pix1);
2087  pixDestroy(&pix2);
2088  return pixd;
2089 }
2090 
2091 
2092 
2093 /*---------------------------------------------------------------------*
2094  * Setting a transparent alpha component over a white background *
2095  *---------------------------------------------------------------------*/
2115 PIX *
2117 {
2118 PIX *pixd, *pix1, *pix2, *pix3, *pix4;
2119 
2120  PROCNAME("pixSetAlphaOverWhite");
2121 
2122  if (!pixs)
2123  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
2124  if (!(pixGetDepth(pixs) == 32 || pixGetColormap(pixs)))
2125  return (PIX *)ERROR_PTR("pixs not 32 bpp or cmapped", procName, NULL);
2126 
2127  /* Remove colormap if it exists; otherwise copy */
2129 
2130  /* Generate a 1 bpp image where a white pixel in pixd is 0.
2131  * In the comments below, a "white" pixel refers to pixd.
2132  * pix1 is rgb, pix2 is 8 bpp gray, pix3 is 1 bpp. */
2133  pix1 = pixInvert(NULL, pixd); /* send white (255) to 0 for each sample */
2134  pix2 = pixConvertRGBToGrayMinMax(pix1, L_CHOOSE_MAX); /* 0 if white */
2135  pix3 = pixThresholdToBinary(pix2, 1); /* sets white pixels to 1 */
2136  pixInvert(pix3, pix3); /* sets white pixels to 0 */
2137 
2138  /* Generate the alpha component using the distance transform,
2139  * which measures the distance to the nearest bg (0) pixel in pix3.
2140  * After multiplying by 128, its value is 0 (transparent)
2141  * over white pixels, and goes to opaque (255) two pixels away
2142  * from the nearest white pixel. */
2143  pix4 = pixDistanceFunction(pix3, 8, 8, L_BOUNDARY_FG);
2144  pixMultConstantGray(pix4, 128.0);
2145  pixSetRGBComponent(pixd, pix4, L_ALPHA_CHANNEL);
2146 
2147  pixDestroy(&pix1);
2148  pixDestroy(&pix2);
2149  pixDestroy(&pix3);
2150  pixDestroy(&pix4);
2151  return pixd;
2152 }
2153 
2154 
2155 /*---------------------------------------------------------------------*
2156  * Fading from the edge *
2157  *---------------------------------------------------------------------*/
2177 l_ok
2179  l_int32 dir,
2180  l_int32 fadeto,
2181  l_float32 distfract,
2182  l_float32 maxfade)
2183 {
2184 l_int32 i, j, w, h, d, wpl, xmin, ymin, range, val, rval, gval, bval;
2185 l_float32 slope, limit, del;
2186 l_uint32 *data, *line;
2187 
2188  PROCNAME("pixLinearEdgeFade");
2189 
2190  if (!pixs)
2191  return ERROR_INT("pixs not defined", procName, 1);
2192  if (pixGetColormap(pixs) != NULL)
2193  return ERROR_INT("pixs has a colormap", procName, 1);
2194  pixGetDimensions(pixs, &w, &h, &d);
2195  if (d != 8 && d != 32)
2196  return ERROR_INT("pixs not 8 or 32 bpp", procName, 1);
2197  if (dir != L_FROM_LEFT && dir != L_FROM_RIGHT &&
2198  dir != L_FROM_TOP && dir != L_FROM_BOT)
2199  return ERROR_INT("invalid fade direction from edge", procName, 1);
2200  if (fadeto != L_BLEND_TO_WHITE && fadeto != L_BLEND_TO_BLACK)
2201  return ERROR_INT("invalid fadeto photometry", procName, 1);
2202  if (maxfade <= 0) return 0;
2203  if (maxfade > 1.0)
2204  return ERROR_INT("invalid maxfade", procName, 1);
2205  if (distfract <= 0 || distfract * L_MIN(w, h) < 1.0) {
2206  L_INFO("distfract is too small\n", procName);
2207  return 0;
2208  }
2209  if (distfract > 1.0)
2210  return ERROR_INT("invalid distfract", procName, 1);
2211 
2212  /* Set up parameters */
2213  if (dir == L_FROM_LEFT) {
2214  range = (l_int32)(distfract * w);
2215  xmin = 0;
2216  slope = maxfade / (l_float32)range;
2217  } else if (dir == L_FROM_RIGHT) {
2218  range = (l_int32)(distfract * w);
2219  xmin = w - range;
2220  slope = maxfade / (l_float32)range;
2221  } else if (dir == L_FROM_TOP) {
2222  range = (l_int32)(distfract * h);
2223  ymin = 0;
2224  slope = maxfade / (l_float32)range;
2225  } else if (dir == L_FROM_BOT) {
2226  range = (l_int32)(distfract * h);
2227  ymin = h - range;
2228  slope = maxfade / (l_float32)range;
2229  }
2230 
2231  limit = (fadeto == L_BLEND_TO_WHITE) ? 255.0 : 0.0;
2232  data = pixGetData(pixs);
2233  wpl = pixGetWpl(pixs);
2234  if (dir == L_FROM_LEFT || dir == L_FROM_RIGHT) {
2235  for (j = 0; j < range; j++) {
2236  del = (dir == L_FROM_LEFT) ? maxfade - slope * j
2237  : maxfade - slope * (range - j);
2238  for (i = 0; i < h; i++) {
2239  line = data + i * wpl;
2240  if (d == 8) {
2241  val = GET_DATA_BYTE(line, xmin + j);
2242  val += (limit - val) * del + 0.5;
2243  SET_DATA_BYTE(line, xmin + j, val);
2244  } else { /* rgb */
2245  extractRGBValues(*(line + xmin + j), &rval, &gval, &bval);
2246  rval += (limit - rval) * del + 0.5;
2247  gval += (limit - gval) * del + 0.5;
2248  bval += (limit - bval) * del + 0.5;
2249  composeRGBPixel(rval, gval, bval, line + xmin + j);
2250  }
2251  }
2252  }
2253  } else { /* dir == L_FROM_TOP || L_FROM_BOT */
2254  for (i = 0; i < range; i++) {
2255  del = (dir == L_FROM_TOP) ? maxfade - slope * i
2256  : maxfade - slope * (range - i);
2257  line = data + (ymin + i) * wpl;
2258  for (j = 0; j < w; j++) {
2259  if (d == 8) {
2260  val = GET_DATA_BYTE(line, j);
2261  val += (limit - val) * del + 0.5;
2262  SET_DATA_BYTE(line, j, val);
2263  } else { /* rgb */
2264  extractRGBValues(*(line + j), &rval, &gval, &bval);
2265  rval += (limit - rval) * del + 0.5;
2266  gval += (limit - gval) * del + 0.5;
2267  bval += (limit - bval) * del + 0.5;
2268  composeRGBPixel(rval, gval, bval, line + j);
2269  }
2270  }
2271  }
2272  }
2273 
2274  return 0;
2275 }
2276 
2277 
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:322
l_ok pixSetRGBComponent(PIX *pixd, PIX *pixs, l_int32 comp)
pixSetRGBComponent()
Definition: pix2.c:2463
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3233
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:489
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:193
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3041
PIX * pixBlendGrayAdapt(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 shift)
pixBlendGrayAdapt()
Definition: blend.c:1061
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:302
struct Pix * pixs
Definition: bilateral.h:117
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1395
#define SET_DATA_QBIT(pdata, n, val)
Definition: arrayaccess.h:168
PIX * pixSetAlphaOverWhite(PIX *pixs)
pixSetAlphaOverWhite()
Definition: blend.c:2116
void pixcmapDestroy(PIXCMAP **pcmap)
pixcmapDestroy()
Definition: colormap.c:265
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1624
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:443
PIX * pixRemoveColormapGeneral(PIX *pixs, l_int32 type, l_int32 ifnocmap)
pixRemoveColormapGeneral()
Definition: pixconv.c:272
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:820
PIX * pixFadeWithGray(PIX *pixs, PIX *pixb, l_float32 factor, l_int32 type)
pixFadeWithGray()
Definition: blend.c:1235
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
PIX * pixCreateTemplate(PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:367
PIX * pixClipRectangle(PIX *pixs, BOX *box, BOX **pboxc)
pixClipRectangle()
Definition: pix5.c:1020
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1573
PIX * pixGetRGBComponent(PIX *pixs, l_int32 comp)
pixGetRGBComponent()
Definition: pix2.c:2404
#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:1555
l_ok pixSetAllArbitrary(PIX *pix, l_uint32 val)
pixSetAllArbitrary()
Definition: pix2.c:876
l_int32 pixcmapGetIndex(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex)
pixcmapGetIndex()
Definition: colormap.c:963
PIX * pixAlphaBlendUniform(PIX *pixs, l_uint32 color)
pixAlphaBlendUniform()
Definition: blend.c:2004
PIX * pixMultiplyByColor(PIX *pixd, PIX *pixs, BOX *box, l_uint32 color)
pixMultiplyByColor()
Definition: blend.c:1926
PIX * pixBlend(PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract)
pixBlend()
Definition: blend.c:174
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:253
l_ok pixcmapGetColor(PIXCMAP *cmap, l_int32 index, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
pixcmapGetColor()
Definition: colormap.c:751
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:1692
#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:688
PIX * pixConvertRGBToGrayMinMax(PIX *pixs, l_int32 type)
pixConvertRGBToGrayMinMax()
Definition: pixconv.c:887
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:515
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:1107
PIX * pixBlendMask(PIX *pixd, PIX *pixs1, PIX *pixs2, l_int32 x, l_int32 y, l_float32 fract, l_int32 type)
pixBlendMask()
Definition: blend.c:262
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:180
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1065
PIX * pixAddAlphaToBlend(PIX *pixs, l_float32 fract, l_int32 invert)
pixAddAlphaToBlend()
Definition: blend.c:2057
PIX * pixDistanceFunction(PIX *pixs, l_int32 connectivity, l_int32 outdepth, l_int32 boundcond)
pixDistanceFunction()
Definition: seedfill.c:2533
#define GET_DATA_DIBIT(pdata, n)
Definition: arrayaccess.h:145
Definition: pix.h:718
l_int32 pixcmapGetCount(PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:635
Definition: pix.h:134
l_ok pixMultConstantGray(PIX *pixs, l_float32 val)
pixMultConstantGray()
Definition: pixarith.c:186
#define PIX_SRC
Definition: pix.h:327
PIX * pixCopy(PIX *pixd, PIX *pixs)
pixCopy()
Definition: pix1.c:628
PIX * pixGammaTRC(PIX *pixd, PIX *pixs, l_float32 gamma, l_int32 minval, l_int32 maxval)
pixGammaTRC()
Definition: enhance.c:174
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:278
l_ok boxIntersects(BOX *box1, BOX *box2, l_int32 *presult)
boxIntersects()
Definition: boxfunc1.c:131
PIXCMAP * pixcmapCopy(PIXCMAP *cmaps)
pixcmapCopy()
Definition: colormap.c:234
l_ok composeRGBPixel(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *ppixel)
composeRGBPixel()
Definition: pix2.c:2671
l_ok pixTransferAllData(PIX *pixd, PIX **ppixs, l_int32 copytext, l_int32 copyformat)
pixTransferAllData()
Definition: pix1.c:855
l_ok pixcmapAddColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapAddColor()
Definition: colormap.c:341
l_ok boxGetGeometry(BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:310
Definition: pix.h:480
void extractRGBValues(l_uint32 pixel, l_int32 *prval, l_int32 *pgval, l_int32 *pbval)
extractRGBValues()
Definition: pix2.c:2737
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:165
PIX * pixBlendBackgroundToColor(PIX *pixd, PIX *pixs, BOX *box, l_uint32 color, l_float32 gamma, l_int32 minval, l_int32 maxval)
pixBlendBackgroundToColor()
Definition: blend.c:1846
l_ok pixLinearEdgeFade(PIX *pixs, l_int32 dir, l_int32 fadeto, l_float32 distfract, l_float32 maxfade)
pixLinearEdgeFade()
Definition: blend.c:2178
l_int32 minval
Definition: bilateral.h:124