• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*====================================================================*
2  -  Copyright (C) 2001 Leptonica.  All rights reserved.
3  -  This software is distributed in the hope that it will be
4  -  useful, but with NO WARRANTY OF ANY KIND.
5  -  No author or distributor accepts responsibility to anyone for the
6  -  consequences of using this software, or for whether it serves any
7  -  particular purpose or works at all, unless he or she says so in
8  -  writing.  Everyone is granted permission to copy, modify and
9  -  redistribute this source code, for commercial or non-commercial
10  -  purposes, with the following restrictions: (1) the origin of this
11  -  source code must not be misrepresented; (2) modified versions must
12  -  be plainly marked as such; and (3) this notice may not be removed
13  -  or altered from any source or modified source distribution.
14  *====================================================================*/
15 
16 /*
17  *  pix3.c
18  *
19  *    This file has these operations:
20  *
21  *      (1) Mask-directed operations
22  *      (2) Full-image bit-logical operations
23  *      (3) Foreground pixel counting operations on 1 bpp images
24  *      (4) Sum of pixel values
25  *      (5) Mirrored tiling of a smaller image
26  *
27  *
28  *    Masked operations
29  *           l_int32     pixSetMasked()
30  *           l_int32     pixSetMaskedGeneral()
31  *           l_int32     pixCombineMasked()
32  *           l_int32     pixCombineMaskedGeneral()
33  *           l_int32     pixPaintThroughMask()
34  *           PIX        *pixPaintSelfThroughMask()
35  *
36  *    One and two-image boolean operations on arbitrary depth images
37  *           PIX        *pixInvert()
38  *           PIX        *pixOr()
39  *           PIX        *pixAnd()
40  *           PIX        *pixXor()
41  *           PIX        *pixSubtract()
42  *
43  *    Foreground pixel counting in 1 bpp images
44  *           l_int32     pixZero()
45  *           l_int32     pixCountPixels()
46  *           NUMA       *pixaCountPixels()
47  *           l_int32     pixCountPixelsInRow()
48  *           NUMA       *pixCountPixelsByRow()
49  *           l_int32     pixThresholdPixels()
50  *           l_int32    *makePixelSumTab8()
51  *           l_int32    *makePixelCentroidTab8()
52  *
53  *    Sum of pixel values
54  *           l_int32     pixSumPixelValues()
55  *
56  *    Mirrored tiling
57  *           PIX        *pixMirroredTiling()
58  *
59  *    Static helper function
60  *           static l_int32  findTilePatchCenter()
61  */
62 
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include "allheaders.h"
67 
68 static l_int32 findTilePatchCenter(PIX *pixs, BOX *box, l_int32 dir,
69                                    l_uint32 targdist, l_uint32 *pdist,
70                                    l_int32 *pxc, l_int32 *pyc);
71 
72 #ifndef  NO_CONSOLE_IO
73 #define   EQUAL_SIZE_WARNING      0
74 #endif  /* ~NO_CONSOLE_IO */
75 
76 
77 /*-------------------------------------------------------------*
78  *                        Masked operations                    *
79  *-------------------------------------------------------------*/
80 /*!
81  *  pixSetMasked()
82  *
83  *      Input:  pixd (1, 2, 4, 8, 16 or 32 bpp; or colormapped)
84  *              pixm (<optional> 1 bpp mask; no operation if NULL)
85  *              val (value to set at each masked pixel)
86  *      Return: 0 if OK; 1 on error
87  *
88  *  Notes:
89  *      (1) In-place operation.  Calls pixSetMaskedCmap() for colormapped
90  *          images.
91  *      (2) If pixm == NULL, a warning is given.
92  *      (3) It is an implicitly aligned operation, where the UL
93  *          corners of pixd and pixm coincide.  A warning is
94  *          issued if the two image sizes differ significantly,
95  *          but the operation proceeds.
96  *      (4) Each pixel in pixd that co-locates with an ON pixel
97  *          in pixm is set to the specified input value.
98  *          Other pixels in pixd are not changed.
99  *      (5) You can visualize this as painting the color through
100  *          the mask, as a stencil.
101  *      (6) If you do not want to have the UL corners aligned,
102  *          use the function pixSetMaskedGeneral(), which requires
103  *          you to input the UL corner of pixm relative to pixd.
104  *      (7) Implementation details: see comments in pixPaintThroughMask()
105  *          for when we use rasterop to do the painting.
106  */
107 l_int32
pixSetMasked(PIX * pixd,PIX * pixm,l_uint32 val)108 pixSetMasked(PIX      *pixd,
109              PIX      *pixm,
110              l_uint32  val)
111 {
112 l_int32    wd, hd, wm, hm, w, h, d, wpld, wplm;
113 l_int32    i, j, rval, gval, bval;
114 l_uint32  *datad, *datam, *lined, *linem;
115 
116     PROCNAME("pixSetMasked");
117 
118     if (!pixd)
119         return ERROR_INT("pixd not defined", procName, 1);
120     if (!pixm) {
121         L_WARNING("no mask; nothing to do", procName);
122         return 0;
123     }
124     if (pixGetColormap(pixd)) {
125         extractRGBValues(val, &rval, &gval, &bval);
126         return pixSetMaskedCmap(pixd, pixm, 0, 0, rval, gval, bval);
127     }
128 
129     if (pixGetDepth(pixm) != 1)
130         return ERROR_INT("pixm not 1 bpp", procName, 1);
131     d = pixGetDepth(pixd);
132     if (d == 1)
133         val &= 1;
134     else if (d == 2)
135         val &= 3;
136     else if (d == 4)
137         val &= 0x0f;
138     else if (d == 8)
139         val &= 0xff;
140     else if (d == 16)
141         val &= 0xffff;
142     else if (d != 32)
143         return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
144     pixGetDimensions(pixm, &wm, &hm, NULL);
145 
146         /* If d == 1, use rasterop; it's about 25x faster */
147     if (d == 1) {
148         if (val == 0) {
149             PIX *pixmi = pixInvert(NULL, pixm);
150             pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmi, 0, 0);
151             pixDestroy(&pixmi);
152         }
153         else  /* val == 1 */
154             pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixm, 0, 0);
155         return 0;
156     }
157 
158         /* For d < 32, use rasterop for val == 0 (black); ~3x faster. */
159     if (d < 32 && val == 0) {
160         PIX *pixmd = pixUnpackBinary(pixm, d, 1);
161         pixRasterop(pixd, 0, 0, wm, hm, PIX_MASK, pixmd, 0, 0);
162         pixDestroy(&pixmd);
163         return 0;
164     }
165 
166         /* For d < 32, use rasterop for val == maxval (white); ~3x faster. */
167     if (d < 32 && val == ((1 << d) - 1)) {
168         PIX *pixmd = pixUnpackBinary(pixm, d, 0);
169         pixRasterop(pixd, 0, 0, wm, hm, PIX_PAINT, pixmd, 0, 0);
170         pixDestroy(&pixmd);
171         return 0;
172     }
173 
174     pixGetDimensions(pixd, &wd, &hd, &d);
175     w = L_MIN(wd, wm);
176     h = L_MIN(hd, hm);
177     if (L_ABS(wd - wm) > 7 || L_ABS(hd - hm) > 7)  /* allow a small tolerance */
178         L_WARNING("pixd and pixm sizes differ", procName);
179 
180     datad = pixGetData(pixd);
181     datam = pixGetData(pixm);
182     wpld = pixGetWpl(pixd);
183     wplm = pixGetWpl(pixm);
184     for (i = 0; i < h; i++) {
185         lined = datad + i * wpld;
186         linem = datam + i * wplm;
187         for (j = 0; j < w; j++) {
188             if (GET_DATA_BIT(linem, j)) {
189                 switch(d)
190                 {
191                 case 2:
192                     SET_DATA_DIBIT(lined, j, val);
193                     break;
194                 case 4:
195                     SET_DATA_QBIT(lined, j, val);
196                     break;
197                 case 8:
198                     SET_DATA_BYTE(lined, j, val);
199                     break;
200                 case 16:
201                     SET_DATA_TWO_BYTES(lined, j, val);
202                     break;
203                 case 32:
204                     *(lined + j) = val;
205                     break;
206                 default:
207                     return ERROR_INT("shouldn't get here", procName, 1);
208                 }
209             }
210         }
211     }
212 
213     return 0;
214 }
215 
216 
217 /*!
218  *  pixSetMaskedGeneral()
219  *
220  *      Input:  pixd (8, 16 or 32 bpp)
221  *              pixm (<optional> 1 bpp mask; no operation if null)
222  *              val (value to set at each masked pixel)
223  *              x, y (location of UL corner of pixm relative to pixd;
224  *                    can be negative)
225  *      Return: 0 if OK; 1 on error
226  *
227  *  Notes:
228  *      (1) This is an in-place operation.
229  *      (2) Alignment is explicit.  If you want the UL corners of
230  *          the two images to be aligned, use pixSetMasked().
231  *      (3) A typical use would be painting through the foreground
232  *          of a small binary mask pixm, located somewhere on a
233  *          larger pixd.  Other pixels in pixd are not changed.
234  *      (4) You can visualize this as painting the color through
235  *          the mask, as a stencil.
236  *      (5) This uses rasterop to handle clipping and different depths of pixd.
237  *      (6) If pixd has a colormap, you should call pixPaintThroughMask().
238  *      (7) Why is this function here, if pixPaintThroughMask() does the
239  *          same thing, and does it more generally?  I've retained it here
240  *          to show how one can paint through a mask using only full
241  *          image rasterops, rather than pixel peeking in pixm and poking
242  *          in pixd.  It's somewhat baroque, but I found it amusing.
243  */
244 l_int32
pixSetMaskedGeneral(PIX * pixd,PIX * pixm,l_uint32 val,l_int32 x,l_int32 y)245 pixSetMaskedGeneral(PIX      *pixd,
246                     PIX      *pixm,
247                     l_uint32  val,
248                     l_int32   x,
249                     l_int32   y)
250 {
251 l_int32    wm, hm, d;
252 PIX       *pixmu, *pixc;
253 
254     PROCNAME("pixSetMaskedGeneral");
255 
256     if (!pixd)
257         return ERROR_INT("pixd not defined", procName, 1);
258     if (!pixm)  /* nothing to do */
259         return 0;
260 
261     d = pixGetDepth(pixd);
262     if (d != 8 && d != 16 && d != 32)
263         return ERROR_INT("pixd not 8, 16 or 32 bpp", procName, 1);
264     if (pixGetDepth(pixm) != 1)
265         return ERROR_INT("pixm not 1 bpp", procName, 1);
266 
267         /* Unpack binary to depth d, with inversion:  1 --> 0, 0 --> 0xff... */
268     if ((pixmu = pixUnpackBinary(pixm, d, 1)) == NULL)
269         return ERROR_INT("pixmu not made", procName, 1);
270 
271         /* Clear stenciled pixels in pixd */
272     pixGetDimensions(pixm, &wm, &hm, NULL);
273     pixRasterop(pixd, x, y, wm, hm, PIX_SRC & PIX_DST, pixmu, 0, 0);
274 
275         /* Generate image with requisite color */
276     if ((pixc = pixCreateTemplate(pixmu)) == NULL)
277         return ERROR_INT("pixc not made", procName, 1);
278     pixSetAllArbitrary(pixc, val);
279 
280         /* Invert stencil mask, and paint color color into stencil */
281     pixInvert(pixmu, pixmu);
282     pixAnd(pixmu, pixmu, pixc);
283 
284         /* Finally, repaint stenciled pixels, with val, in pixd */
285     pixRasterop(pixd, x, y, wm, hm, PIX_SRC | PIX_DST, pixmu, 0, 0);
286 
287     pixDestroy(&pixmu);
288     pixDestroy(&pixc);
289     return 0;
290 }
291 
292 
293 /*!
294  *  pixCombineMasked()
295  *
296  *      Input:  pixd (1 bpp, 8 bpp gray or 32 bpp rgb; no cmap)
297  *              pixs (1 bpp, 8 bpp gray or 32 bpp rgb; no cmap)
298  *              pixm (<optional> 1 bpp mask; no operation if NULL)
299  *      Return: 0 if OK; 1 on error
300  *
301  *  Notes:
302  *      (1) In-place operation; pixd is changed.
303  *      (2) This sets each pixel in pixd that co-locates with an ON
304  *          pixel in pixm to the corresponding value of pixs.
305  *      (3) pixs and pixd must be the same depth and not colormapped.
306  *      (4) All three input pix are aligned at the UL corner, and the
307  *          operation is clipped to the intersection of all three images.
308  *      (5) If pixm == NULL, it's a no-op.
309  *      (6) Implementation: see notes in pixCombineMaskedGeneral().
310  *          For 8 bpp selective masking, you might guess that it
311  *          would be faster to generate an 8 bpp version of pixm,
312  *          using pixConvert1To8(pixm, 0, 255), and then use a
313  *          general combine operation
314  *               d = (d & ~m) | (s & m)
315  *          on a word-by-word basis.  Not always.  The word-by-word
316  *          combine takes a time that is independent of the mask data.
317  *          If the mask is relatively sparse, the byte-check method
318  *          is actually faster!
319  */
320 l_int32
pixCombineMasked(PIX * pixd,PIX * pixs,PIX * pixm)321 pixCombineMasked(PIX  *pixd,
322                  PIX  *pixs,
323                  PIX  *pixm)
324 {
325 l_int32    w, h, d, ws, hs, ds, wm, hm, dm, wmin, hmin;
326 l_int32    wpl, wpls, wplm, i, j, val;
327 l_uint32  *data, *datas, *datam, *line, *lines, *linem;
328 PIX       *pixt;
329 
330     PROCNAME("pixCombineMasked");
331 
332     if (!pixm)  /* nothing to do */
333         return 0;
334     if (!pixd)
335         return ERROR_INT("pixd not defined", procName, 1);
336     if (!pixs)
337         return ERROR_INT("pixs not defined", procName, 1);
338     pixGetDimensions(pixd, &w, &h, &d);
339     pixGetDimensions(pixs, &ws, &hs, &ds);
340     pixGetDimensions(pixm, &wm, &hm, &dm);
341     if (d != ds)
342         return ERROR_INT("pixs and pixd depths differ", procName, 1);
343     if (dm != 1)
344         return ERROR_INT("pixm not 1 bpp", procName, 1);
345     if (d != 1 && d != 8 && d != 32)
346         return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1);
347     if (pixGetColormap(pixd) || pixGetColormap(pixs))
348         return ERROR_INT("pixs and/or pixd is cmapped", procName, 1);
349 
350         /* For d = 1, use rasterop.  pixt is the part from pixs, under
351          * the fg of pixm, that is to be combined with pixd.  We also
352          * use pixt to remove all fg of pixd that is under the fg of pixm.
353          * Then pixt and pixd are combined by ORing. */
354     wmin = L_MIN(w, L_MIN(ws, wm));
355     hmin = L_MIN(h, L_MIN(hs, hm));
356     if (d == 1) {
357         pixt = pixAnd(NULL, pixs, pixm);
358         pixRasterop(pixd, 0, 0, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
359                     pixm, 0, 0);
360         pixRasterop(pixd, 0, 0, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0);
361         pixDestroy(&pixt);
362         return 0;
363     }
364 
365     data = pixGetData(pixd);
366     datas = pixGetData(pixs);
367     datam = pixGetData(pixm);
368     wpl = pixGetWpl(pixd);
369     wpls = pixGetWpl(pixs);
370     wplm = pixGetWpl(pixm);
371     if (d == 8) {
372         for (i = 0; i < hmin; i++) {
373             line = data + i * wpl;
374             lines = datas + i * wpls;
375             linem = datam + i * wplm;
376             for (j = 0; j < wmin; j++) {
377                 if (GET_DATA_BIT(linem, j)) {
378                    val = GET_DATA_BYTE(lines, j);
379                    SET_DATA_BYTE(line, j, val);
380                 }
381             }
382         }
383     }
384     else {  /* d == 32 */
385         for (i = 0; i < hmin; i++) {
386             line = data + i * wpl;
387             lines = datas + i * wpls;
388             linem = datam + i * wplm;
389             for (j = 0; j < wmin; j++) {
390                 if (GET_DATA_BIT(linem, j))
391                    line[j] = lines[j];
392             }
393         }
394     }
395 
396     return 0;
397 }
398 
399 
400 /*!
401  *  pixCombineMaskedGeneral()
402  *
403  *      Input:  pixd (1 bpp, 8 bpp gray or 32 bpp rgb)
404  *              pixs (1 bpp, 8 bpp gray or 32 bpp rgb)
405  *              pixm (<optional> 1 bpp mask)
406  *              x, y (origin of pixs and pixm relative to pixd; can be negative)
407  *      Return: 0 if OK; 1 on error
408  *
409  *  Notes:
410  *      (1) In-place operation; pixd is changed.
411  *      (2) This is a generalized version of pixCombinedMasked(), where
412  *          the source and mask can be placed at the same (arbitrary)
413  *          location relative to pixd.
414  *      (3) pixs and pixd must be the same depth and not colormapped.
415  *      (4) The UL corners of both pixs and pixm are aligned with
416  *          the point (x, y) of pixd, and the operation is clipped to
417  *          the intersection of all three images.
418  *      (5) If pixm == NULL, it's a no-op.
419  *      (6) Implementation.  There are two ways to do these.  In the first,
420  *          we use rasterop, ORing the part of pixs under the mask
421  *          with pixd (which has been appropriately cleared there first).
422  *          In the second, the mask is used one pixel at a time to
423  *          selectively replace pixels of pixd with those of pixs.
424  *          Here, we use rasterop for 1 bpp and pixel-wise replacement
425  *          for 8 and 32 bpp.  To use rasterop for 8 bpp, for example,
426  *          we must first generate an 8 bpp version of the mask.
427  *          The code is simple:
428  *
429  *             Pix *pixm8 = pixConvert1To8(NULL, pixm, 0, 255);
430  *             Pix *pixt = pixAnd(NULL, pixs, pixm8);
431  *             pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
432  *                         pixm8, 0, 0);
433  *             pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST,
434  *                         pixt, 0, 0);
435  *             pixDestroy(&pixt);
436  *             pixDestroy(&pixm8);
437  */
438 l_int32
pixCombineMaskedGeneral(PIX * pixd,PIX * pixs,PIX * pixm,l_int32 x,l_int32 y)439 pixCombineMaskedGeneral(PIX      *pixd,
440                         PIX      *pixs,
441                         PIX      *pixm,
442                         l_int32   x,
443                         l_int32   y)
444 {
445 l_int32    d, w, h, ws, hs, ds, wm, hm, dm, wmin, hmin;
446 l_int32    wpl, wpls, wplm, i, j, val;
447 l_uint32  *data, *datas, *datam, *line, *lines, *linem;
448 PIX       *pixt;
449 
450     PROCNAME("pixCombineMaskedGeneral");
451 
452     if (!pixm)  /* nothing to do */
453         return 0;
454     if (!pixd)
455         return ERROR_INT("pixd not defined", procName, 1);
456     if (!pixs)
457         return ERROR_INT("pixs not defined", procName, 1);
458     pixGetDimensions(pixd, &w, &h, &d);
459     pixGetDimensions(pixs, &ws, &hs, &ds);
460     pixGetDimensions(pixm, &wm, &hm, &dm);
461     if (d != ds)
462         return ERROR_INT("pixs and pixd depths differ", procName, 1);
463     if (dm != 1)
464         return ERROR_INT("pixm not 1 bpp", procName, 1);
465     if (d != 1 && d != 8 && d != 32)
466         return ERROR_INT("pixd not 1, 8 or 32 bpp", procName, 1);
467     if (pixGetColormap(pixd) || pixGetColormap(pixs))
468         return ERROR_INT("pixs and/or pixd is cmapped", procName, 1);
469 
470         /* For d = 1, use rasterop.  pixt is the part from pixs, under
471          * the fg of pixm, that is to be combined with pixd.  We also
472          * use pixt to remove all fg of pixd that is under the fg of pixm.
473          * Then pixt and pixd are combined by ORing. */
474     wmin = L_MIN(ws, wm);
475     hmin = L_MIN(hs, hm);
476     if (d == 1) {
477         pixt = pixAnd(NULL, pixs, pixm);
478         pixRasterop(pixd, x, y, wmin, hmin, PIX_DST & PIX_NOT(PIX_SRC),
479                     pixm, 0, 0);
480         pixRasterop(pixd, x, y, wmin, hmin, PIX_SRC | PIX_DST, pixt, 0, 0);
481         pixDestroy(&pixt);
482         return 0;
483     }
484 
485     wpl = pixGetWpl(pixd);
486     data = pixGetData(pixd);
487     wpls = pixGetWpl(pixs);
488     datas = pixGetData(pixs);
489     wplm = pixGetWpl(pixm);
490     datam = pixGetData(pixm);
491 
492     for (i = 0; i < hmin; i++) {
493         if (y + i < 0 || y + i >= h) continue;
494         line = data + (y + i) * wpl;
495         lines = datas + i * wpls;
496         linem = datam + i * wplm;
497         for (j = 0; j < wmin; j++) {
498             if (x + j < 0 || x + j >= w) continue;
499             if (GET_DATA_BIT(linem, j)) {
500                 switch (d)
501                 {
502                 case 8:
503                     val = GET_DATA_BYTE(lines, j);
504                     SET_DATA_BYTE(line, x + j, val);
505                     break;
506                 case 32:
507                     *(line + x + j) = *(lines + j);
508                     break;
509                 default:
510                     return ERROR_INT("shouldn't get here", procName, 1);
511                 }
512             }
513         }
514     }
515 
516     return 0;
517 }
518 
519 
520 /*!
521  *  pixPaintThroughMask()
522  *
523  *      Input:  pixd (1, 2, 4, 8, 16 or 32 bpp; or colormapped)
524  *              pixm (<optional> 1 bpp mask)
525  *              x, y (origin of pixm relative to pixd; can be negative)
526  *              val (pixel value to set at each masked pixel)
527  *      Return: 0 if OK; 1 on error
528  *
529  *  Notes:
530  *      (1) In-place operation.  Calls pixSetMaskedCmap() for colormapped
531  *          images.
532  *      (2) For 1, 2, 4, 8 and 16 bpp gray, we take the appropriate
533  *          number of least significant bits of val.
534  *      (3) If pixm == NULL, it's a no-op.
535  *      (4) The mask origin is placed at (x,y) on pixd, and the
536  *          operation is clipped to the intersection of rectangles.
537  *      (5) For rgb, the components in val are in the canonical locations,
538  *          with red in location COLOR_RED, etc.
539  *      (6) Implementation detail 1:
540  *          For painting with val == 0 or val == maxval, you can use rasterop.
541  *          If val == 0, invert the mask so that it's 0 over the region
542  *          into which you want to write, and use PIX_SRC & PIX_DST to
543  *          clear those pixels.  To write with val = maxval (all 1's),
544  *          use PIX_SRC | PIX_DST to set all bits under the mask.
545  *      (7) Implementation detail 2:
546  *          The rasterop trick can be used for depth > 1 as well.
547  *          For val == 0, generate the mask for depth d from the binary
548  *          mask using
549  *              pixmd = pixUnpackBinary(pixm, d, 1);
550  *          and use pixRasterop() with PIX_MASK.  For val == maxval,
551  *              pixmd = pixUnpackBinary(pixm, d, 0);
552  *          and use pixRasterop() with PIX_PAINT.
553  *          But note that if d == 32 bpp, it is about 3x faster to use
554  *          the general implementation (not pixRasterop()).
555  *      (8) Implementation detail 3:
556  *          It might be expected that the switch in the inner loop will
557  *          cause large branching delays and should be avoided.
558  *          This is not the case, because the entrance is always the
559  *          same and the compiler can correctly predict the jump.
560  */
561 l_int32
pixPaintThroughMask(PIX * pixd,PIX * pixm,l_int32 x,l_int32 y,l_uint32 val)562 pixPaintThroughMask(PIX      *pixd,
563                     PIX      *pixm,
564                     l_int32   x,
565                     l_int32   y,
566                     l_uint32  val)
567 {
568 l_int32    d, w, h, wm, hm, wpl, wplm, i, j, rval, gval, bval;
569 l_uint32  *data, *datam, *line, *linem;
570 
571     PROCNAME("pixPaintThroughMask");
572 
573     if (!pixd)
574         return ERROR_INT("pixd not defined", procName, 1);
575     if (!pixm)  /* nothing to do */
576         return 0;
577     if (pixGetColormap(pixd)) {
578         extractRGBValues(val, &rval, &gval, &bval);
579         return pixSetMaskedCmap(pixd, pixm, x, y, rval, gval, bval);
580     }
581 
582     if (pixGetDepth(pixm) != 1)
583         return ERROR_INT("pixm not 1 bpp", procName, 1);
584     d = pixGetDepth(pixd);
585     if (d == 1)
586         val &= 1;
587     else if (d == 2)
588         val &= 3;
589     else if (d == 4)
590         val &= 0x0f;
591     else if (d == 8)
592         val &= 0xff;
593     else if (d == 16)
594         val &= 0xffff;
595     else if (d != 32)
596         return ERROR_INT("pixd not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
597     pixGetDimensions(pixm, &wm, &hm, NULL);
598 
599         /* If d == 1, use rasterop; it's about 25x faster. */
600     if (d == 1) {
601         if (val == 0) {
602             PIX *pixmi = pixInvert(NULL, pixm);
603             pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmi, 0, 0);
604             pixDestroy(&pixmi);
605         }
606         else  /* val == 1 */
607             pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixm, 0, 0);
608         return 0;
609     }
610 
611         /* For d < 32, use rasterop if val == 0 (black); ~3x faster. */
612     if (d < 32 && val == 0) {
613         PIX *pixmd = pixUnpackBinary(pixm, d, 1);
614         pixRasterop(pixd, x, y, wm, hm, PIX_MASK, pixmd, 0, 0);
615         pixDestroy(&pixmd);
616         return 0;
617     }
618 
619         /* For d < 32, use rasterop if val == maxval (white); ~3x faster. */
620     if (d < 32 && val == ((1 << d) - 1)) {
621         PIX *pixmd = pixUnpackBinary(pixm, d, 0);
622         pixRasterop(pixd, x, y, wm, hm, PIX_PAINT, pixmd, 0, 0);
623         pixDestroy(&pixmd);
624         return 0;
625     }
626 
627         /* All other cases */
628     pixGetDimensions(pixd, &w, &h, NULL);
629     wpl = pixGetWpl(pixd);
630     data = pixGetData(pixd);
631     wplm = pixGetWpl(pixm);
632     datam = pixGetData(pixm);
633     for (i = 0; i < hm; i++) {
634         if (y + i < 0 || y + i >= h) continue;
635         line = data + (y + i) * wpl;
636         linem = datam + i * wplm;
637         for (j = 0; j < wm; j++) {
638             if (x + j < 0 || x + j >= w) continue;
639             if (GET_DATA_BIT(linem, j)) {
640                 switch (d)
641                 {
642                 case 2:
643                     SET_DATA_DIBIT(line, x + j, val);
644                     break;
645                 case 4:
646                     SET_DATA_QBIT(line, x + j, val);
647                     break;
648                 case 8:
649                     SET_DATA_BYTE(line, x + j, val);
650                     break;
651                 case 16:
652                     SET_DATA_TWO_BYTES(line, x + j, val);
653                     break;
654                 case 32:
655                     *(line + x + j) = val;
656                     break;
657                 default:
658                     return ERROR_INT("shouldn't get here", procName, 1);
659                 }
660             }
661         }
662     }
663 
664     return 0;
665 }
666 
667 
668 /*!
669  *  pixPaintSelfThroughMask()
670  *
671  *      Input:  pixd (8 bpp gray or 32 bpp rgb; not colormapped)
672  *              pixm (1 bpp mask)
673  *              x, y (origin of pixm relative to pixd; must not be negative)
674  *              tilesize (requested size for tiling)
675  *              searchdir (L_HORIZ, L_VERT)
676  *      Return: 0 if OK; 1 on error
677  *
678  *  Notes:
679  *      (1) In-place operation; pixd is changed.
680  *      (2) If pixm == NULL, it's a no-op.
681  *      (3) The mask origin is placed at (x,y) on pixd, and the
682  *          operation is clipped to the intersection of pixd and the
683  *          fg of the mask.
684  *      (4) The tilesize is the the requested size for tiling.  The
685  *          actual size for each c.c. will be bounded by the minimum
686  *          dimension of the c.c. and the distance at which the tile
687  *          center is located.
688  *      (5) searchdir is the direction with respect to the b.b. of each
689  *          mask component, from which the square patch is chosen and
690  *          tiled onto the image, clipped by the mask component.
691  *      (6) Specifically, a mirrored tiling, generated from pixd,
692  *          is used to construct the pixels that are painted onto
693  *          pixd through pixm.
694  */
695 l_int32
pixPaintSelfThroughMask(PIX * pixd,PIX * pixm,l_int32 x,l_int32 y,l_int32 tilesize,l_int32 searchdir)696 pixPaintSelfThroughMask(PIX      *pixd,
697                         PIX      *pixm,
698                         l_int32   x,
699                         l_int32   y,
700                         l_int32   tilesize,
701                         l_int32   searchdir)
702 {
703 l_int32   w, h, d, wm, hm, dm, i, n, xc, yc, bx, by, bw, bh;
704 l_int32   depth, cctilesize;
705 l_uint32  dist, minside, retval;
706 BOX      *box, *boxt;
707 BOXA     *boxa;
708 PIX      *pix, *pixf, *pixdf, *pixt, *pixc;
709 PIXA     *pixa;
710 
711     PROCNAME("pixPaintSelfThroughMask");
712 
713     if (!pixm)  /* nothing to do */
714         return 0;
715     if (!pixd)
716         return ERROR_INT("pixd not defined", procName, 1);
717     if (pixGetColormap(pixd) != NULL)
718         return ERROR_INT("pixd has colormap", procName, 1);
719     pixGetDimensions(pixd, &w, &h, &d);
720     if (d != 8 && d != 32)
721         return ERROR_INT("pixd not 8 or 32 bpp", procName, 1);
722     pixGetDimensions(pixm, &wm, &hm, &dm);
723     if (dm != 1)
724         return ERROR_INT("pixm not 1 bpp", procName, 1);
725     if (x < 0 || y < 0)
726         return ERROR_INT("x and y must be non-negative", procName, 1);
727     if (tilesize < 1)
728         return ERROR_INT("tilesize must be >= 1", procName, 1);
729     if (searchdir != L_HORIZ && searchdir != L_VERT)
730         return ERROR_INT("searchdir not in {L_HORIZ, L_VERT}", procName, 1);
731 
732         /* Embed mask in full sized mask */
733     if (wm < w || hm < h) {
734         pixf = pixCreate(w, h, 1);
735         pixRasterop(pixf, x, y, wm, hm, PIX_SRC, pixm, 0, 0);
736     }
737     else
738         pixf = pixClone(pixm);
739 
740         /* Get connected components of mask */
741     boxa = pixConnComp(pixf, &pixa, 8);
742     if ((n = pixaGetCount(pixa)) == 0) {
743         L_WARNING("no fg in mask", procName);
744         pixDestroy(&pixf);
745         pixaDestroy(&pixa);
746         boxaDestroy(&boxa);
747         return 1;
748     }
749 
750         /* Get distance function for the mask */
751     pixInvert(pixf, pixf);
752     depth = (tilesize < 256) ? 8 : 16;
753     pixdf = pixDistanceFunction(pixf, 4, depth, L_BOUNDARY_BG);
754     pixDestroy(&pixf);
755 
756         /* For each c.c., generate a representative tile for texturizing
757          * and apply it through the mask.  The input 'tilesize' is the
758          * requested value.  findTilePatchCenter() returns the distance
759          * at which this patch can safely be found. */
760     retval = 0;
761     for (i = 0; i < n; i++) {
762         pix = pixaGetPix(pixa, i, L_CLONE);
763         box = pixaGetBox(pixa, i, L_CLONE);
764         boxGetGeometry(box, &bx, &by, &bw, &bh);
765 	minside = L_MIN(bw, bh);
766 
767         findTilePatchCenter(pixdf, box, searchdir, L_MIN(minside, tilesize),
768                             &dist, &xc, &yc);
769         cctilesize = L_MIN(tilesize, dist);  /* for this c.c. */
770         if (cctilesize < 1) {
771             L_WARNING("region not found!", procName);
772             pixDestroy(&pix);
773             boxDestroy(&box);
774             retval = 1;
775             continue;
776         }
777 
778             /* Extract the selected square from pixd, and generate
779              * an image the size of the b.b. of the c.c., which
780              * is then painted through the c.c. mask.  */
781         boxt = boxCreate(L_MAX(0, xc - dist / 2), L_MAX(0, yc - dist / 2),
782                          cctilesize, cctilesize);
783         pixt = pixClipRectangle(pixd, boxt, NULL);
784         pixc = pixMirroredTiling(pixt, bw, bh);
785         pixCombineMaskedGeneral(pixd, pixc, pix, bx, by);
786         pixDestroy(&pix);
787         pixDestroy(&pixt);
788         pixDestroy(&pixc);
789         boxDestroy(&box);
790         boxDestroy(&boxt);
791     }
792 
793     pixDestroy(&pixdf);
794     pixaDestroy(&pixa);
795     boxaDestroy(&boxa);
796     return retval;
797 }
798 
799 
800 /*-------------------------------------------------------------*
801  *    One and two-image boolean ops on arbitrary depth images  *
802  *-------------------------------------------------------------*/
803 /*!
804  *  pixInvert()
805  *
806  *      Input:  pixd  (<optional>; this can be null, equal to pixs,
807  *                     or different from pixs)
808  *              pixs
809  *      Return: pixd, or null on error
810  *
811  *  Notes:
812  *      (1) This inverts pixs, for all pixel depths.
813  *      (2) There are 3 cases:
814  *           (a) pixd == null,   ~src --> new pixd
815  *           (b) pixd == pixs,   ~src --> src  (in-place)
816  *           (c) pixd != pixs,   ~src --> input pixd
817  *      (3) For clarity, if the case is known, use these patterns:
818  *           (a) pixd = pixInvert(NULL, pixs);
819  *           (b) pixInvert(pixs, pixs);
820  *           (c) pixInvert(pixd, pixs);
821  */
822 PIX *
pixInvert(PIX * pixd,PIX * pixs)823 pixInvert(PIX  *pixd,
824           PIX  *pixs)
825 {
826     PROCNAME("pixInvert");
827 
828     if (!pixs)
829         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
830 
831         /* Prepare pixd for in-place operation */
832     if ((pixd = pixCopy(pixd, pixs)) == NULL)
833         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
834 
835     pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
836                 PIX_NOT(PIX_DST), NULL, 0, 0);   /* invert pixd */
837 
838     return pixd;
839 }
840 
841 
842 /*!
843  *  pixOr()
844  *
845  *      Input:  pixd  (<optional>; this can be null, equal to pixs1,
846  *                     different from pixs1)
847  *              pixs1 (can be == pixd)
848  *              pixs2 (must be != pixd)
849  *      Return: pixd always
850  *
851  *  Notes:
852  *      (1) This gives the union of two images with equal depth,
853  *          aligning them to the the UL corner.  pixs1 and pixs2
854  *          need not have the same width and height.
855  *      (2) There are 3 cases:
856  *            (a) pixd == null,   (src1 | src2) --> new pixd
857  *            (b) pixd == pixs1,  (src1 | src2) --> src1  (in-place)
858  *            (c) pixd != pixs1,  (src1 | src2) --> input pixd
859  *      (3) For clarity, if the case is known, use these patterns:
860  *            (a) pixd = pixOr(NULL, pixs1, pixs2);
861  *            (b) pixOr(pixs1, pixs1, pixs2);
862  *            (c) pixOr(pixd, pixs1, pixs2);
863  *      (4) The size of the result is determined by pixs1.
864  *      (5) The depths of pixs1 and pixs2 must be equal.
865  *      (6) Note carefully that the order of pixs1 and pixs2 only matters
866  *          for the in-place case.  For in-place, you must have
867  *          pixd == pixs1.  Setting pixd == pixs2 gives an incorrect
868  *          result: the copy puts pixs1 image data in pixs2, and
869  *          the rasterop is then between pixs2 and pixs2 (a no-op).
870  */
871 PIX *
pixOr(PIX * pixd,PIX * pixs1,PIX * pixs2)872 pixOr(PIX  *pixd,
873       PIX  *pixs1,
874       PIX  *pixs2)
875 {
876     PROCNAME("pixOr");
877 
878     if (!pixs1)
879         return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
880     if (!pixs2)
881         return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
882     if (pixd == pixs2)
883         return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
884     if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
885         return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
886 
887 #if  EQUAL_SIZE_WARNING
888     if (!pixSizesEqual(pixs1, pixs2))
889         L_WARNING("pixs1 and pixs2 not equal sizes", procName);
890 #endif  /* EQUAL_SIZE_WARNING */
891 
892         /* Prepare pixd to be a copy of pixs1 */
893     if ((pixd = pixCopy(pixd, pixs1)) == NULL)
894         return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
895 
896         /* src1 | src2 --> dest */
897     pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
898                 PIX_SRC | PIX_DST, pixs2, 0, 0);
899 
900     return pixd;
901 }
902 
903 
904 /*!
905  *  pixAnd()
906  *
907  *      Input:  pixd  (<optional>; this can be null, equal to pixs1,
908  *                     different from pixs1)
909  *              pixs1 (can be == pixd)
910  *              pixs2 (must be != pixd)
911  *      Return: pixd always
912  *
913  *  Notes:
914  *      (1) This gives the intersection of two images with equal depth,
915  *          aligning them to the the UL corner.  pixs1 and pixs2
916  *          need not have the same width and height.
917  *      (2) There are 3 cases:
918  *            (a) pixd == null,   (src1 & src2) --> new pixd
919  *            (b) pixd == pixs1,  (src1 & src2) --> src1  (in-place)
920  *            (c) pixd != pixs1,  (src1 & src2) --> input pixd
921  *      (3) For clarity, if the case is known, use these patterns:
922  *            (a) pixd = pixAnd(NULL, pixs1, pixs2);
923  *            (b) pixAnd(pixs1, pixs1, pixs2);
924  *            (c) pixAnd(pixd, pixs1, pixs2);
925  *      (4) The size of the result is determined by pixs1.
926  *      (5) The depths of pixs1 and pixs2 must be equal.
927  *      (6) Note carefully that the order of pixs1 and pixs2 only matters
928  *          for the in-place case.  For in-place, you must have
929  *          pixd == pixs1.  Setting pixd == pixs2 gives an incorrect
930  *          result: the copy puts pixs1 image data in pixs2, and
931  *          the rasterop is then between pixs2 and pixs2 (a no-op).
932  */
933 PIX *
pixAnd(PIX * pixd,PIX * pixs1,PIX * pixs2)934 pixAnd(PIX  *pixd,
935        PIX  *pixs1,
936        PIX  *pixs2)
937 {
938     PROCNAME("pixAnd");
939 
940     if (!pixs1)
941         return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
942     if (!pixs2)
943         return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
944     if (pixd == pixs2)
945         return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
946     if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
947         return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
948 
949 #if  EQUAL_SIZE_WARNING
950     if (!pixSizesEqual(pixs1, pixs2))
951         L_WARNING("pixs1 and pixs2 not equal sizes", procName);
952 #endif  /* EQUAL_SIZE_WARNING */
953 
954         /* Prepare pixd to be a copy of pixs1 */
955     if ((pixd = pixCopy(pixd, pixs1)) == NULL)
956         return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
957 
958         /* src1 & src2 --> dest */
959     pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
960                 PIX_SRC & PIX_DST, pixs2, 0, 0);
961 
962     return pixd;
963 }
964 
965 
966 /*!
967  *  pixXor()
968  *
969  *      Input:  pixd  (<optional>; this can be null, equal to pixs1,
970  *                     different from pixs1)
971  *              pixs1 (can be == pixd)
972  *              pixs2 (must be != pixd)
973  *      Return: pixd always
974  *
975  *  Notes:
976  *      (1) This gives the XOR of two images with equal depth,
977  *          aligning them to the the UL corner.  pixs1 and pixs2
978  *          need not have the same width and height.
979  *      (2) There are 3 cases:
980  *            (a) pixd == null,   (src1 ^ src2) --> new pixd
981  *            (b) pixd == pixs1,  (src1 ^ src2) --> src1  (in-place)
982  *            (c) pixd != pixs1,  (src1 ^ src2) --> input pixd
983  *      (3) For clarity, if the case is known, use these patterns:
984  *            (a) pixd = pixXor(NULL, pixs1, pixs2);
985  *            (b) pixXor(pixs1, pixs1, pixs2);
986  *            (c) pixXor(pixd, pixs1, pixs2);
987  *      (4) The size of the result is determined by pixs1.
988  *      (5) The depths of pixs1 and pixs2 must be equal.
989  *      (6) Note carefully that the order of pixs1 and pixs2 only matters
990  *          for the in-place case.  For in-place, you must have
991  *          pixd == pixs1.  Setting pixd == pixs2 gives an incorrect
992  *          result: the copy puts pixs1 image data in pixs2, and
993  *          the rasterop is then between pixs2 and pixs2 (a no-op).
994  */
995 PIX *
pixXor(PIX * pixd,PIX * pixs1,PIX * pixs2)996 pixXor(PIX  *pixd,
997        PIX  *pixs1,
998        PIX  *pixs2)
999 {
1000     PROCNAME("pixXor");
1001 
1002     if (!pixs1)
1003         return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1004     if (!pixs2)
1005         return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1006     if (pixd == pixs2)
1007         return (PIX *)ERROR_PTR("cannot have pixs2 == pixd", procName, pixd);
1008     if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1009         return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
1010 
1011 #if  EQUAL_SIZE_WARNING
1012     if (!pixSizesEqual(pixs1, pixs2))
1013         L_WARNING("pixs1 and pixs2 not equal sizes", procName);
1014 #endif  /* EQUAL_SIZE_WARNING */
1015 
1016         /* Prepare pixd to be a copy of pixs1 */
1017     if ((pixd = pixCopy(pixd, pixs1)) == NULL)
1018         return (PIX *)ERROR_PTR("pixd not made", procName, pixd);
1019 
1020         /* src1 ^ src2 --> dest */
1021     pixRasterop(pixd, 0, 0, pixGetWidth(pixd), pixGetHeight(pixd),
1022                 PIX_SRC ^ PIX_DST, pixs2, 0, 0);
1023 
1024     return pixd;
1025 }
1026 
1027 
1028 /*!
1029  *  pixSubtract()
1030  *
1031  *      Input:  pixd  (<optional>; this can be null, equal to pixs1,
1032  *                     equal to pixs2, or different from both pixs1 and pixs2)
1033  *              pixs1 (can be == pixd)
1034  *              pixs2 (can be == pixd)
1035  *      Return: pixd always
1036  *
1037  *  Notes:
1038  *      (1) This gives the set subtraction of two images with equal depth,
1039  *          aligning them to the the UL corner.  pixs1 and pixs2
1040  *          need not have the same width and height.
1041  *      (2) Source pixs2 is always subtracted from source pixs1.
1042  *          The result is
1043  *                  pixs1 \ pixs2 = pixs1 & (~pixs2)
1044  *      (3) There are 4 cases:
1045  *            (a) pixd == null,   (src1 - src2) --> new pixd
1046  *            (b) pixd == pixs1,  (src1 - src2) --> src1  (in-place)
1047  *            (c) pixd == pixs2,  (src1 - src2) --> src2  (in-place)
1048  *            (d) pixd != pixs1 && pixd != pixs2),
1049  *                                 (src1 - src2) --> input pixd
1050  *      (4) For clarity, if the case is known, use these patterns:
1051  *            (a) pixd = pixSubtract(NULL, pixs1, pixs2);
1052  *            (b) pixSubtract(pixs1, pixs1, pixs2);
1053  *            (c) pixSubtract(pixs2, pixs1, pixs2);
1054  *            (d) pixSubtract(pixd, pixs1, pixs2);
1055  *      (5) The size of the result is determined by pixs1.
1056  *      (6) The depths of pixs1 and pixs2 must be equal.
1057  */
1058 PIX *
pixSubtract(PIX * pixd,PIX * pixs1,PIX * pixs2)1059 pixSubtract(PIX  *pixd,
1060             PIX  *pixs1,
1061             PIX  *pixs2)
1062 {
1063 l_int32  w, h;
1064 
1065     PROCNAME("pixSubtract");
1066 
1067     if (!pixs1)
1068         return (PIX *)ERROR_PTR("pixs1 not defined", procName, pixd);
1069     if (!pixs2)
1070         return (PIX *)ERROR_PTR("pixs2 not defined", procName, pixd);
1071     if (pixGetDepth(pixs1) != pixGetDepth(pixs2))
1072         return (PIX *)ERROR_PTR("depths of pixs* unequal", procName, pixd);
1073 
1074 #if  EQUAL_SIZE_WARNING
1075     if (!pixSizesEqual(pixs1, pixs2))
1076         L_WARNING("pixs1 and pixs2 not equal sizes", procName);
1077 #endif  /* EQUAL_SIZE_WARNING */
1078 
1079     pixGetDimensions(pixs1, &w, &h, NULL);
1080     if (!pixd) {
1081         pixd = pixCopy(NULL, pixs1);
1082         pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1083             pixs2, 0, 0);   /* src1 & (~src2)  */
1084     }
1085     else if (pixd == pixs1) {
1086         pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1087             pixs2, 0, 0);   /* src1 & (~src2)  */
1088     }
1089     else if (pixd == pixs2) {
1090         pixRasterop(pixd, 0, 0, w, h, PIX_NOT(PIX_DST) & PIX_SRC,
1091             pixs1, 0, 0);   /* src1 & (~src2)  */
1092     }
1093     else  { /* pixd != pixs1 && pixd != pixs2 */
1094         pixCopy(pixd, pixs1);  /* sizes pixd to pixs1 if unequal */
1095         pixRasterop(pixd, 0, 0, w, h, PIX_DST & PIX_NOT(PIX_SRC),
1096             pixs2, 0, 0);   /* src1 & (~src2)  */
1097     }
1098 
1099     return pixd;
1100 }
1101 
1102 
1103 /*-------------------------------------------------------------*
1104  *                         Pixel counting                      *
1105  *-------------------------------------------------------------*/
1106 /*!
1107  *  pixZero()
1108  *
1109  *      Input:  pix
1110  *              &empty  (<return> 1 if all bits in image are 0; 0 otherwise)
1111  *      Return: 0 if OK; 1 on error
1112  *
1113  *  Notes:
1114  *      (1) For a binary image, if there are no fg (black) pixels, empty = 1.
1115  *      (2) For a grayscale image, if all pixels are black (0), empty = 1.
1116  *      (3) For an RGB image, if all 4 components in every pixel is 0,
1117  *          empty = 1.
1118  */
1119 l_int32
pixZero(PIX * pix,l_int32 * pempty)1120 pixZero(PIX      *pix,
1121         l_int32  *pempty)
1122 {
1123 l_int32    w, h, wpl, i, j, fullwords, endbits;
1124 l_uint32   endmask;
1125 l_uint32  *data, *line;
1126 
1127     PROCNAME("pixZero");
1128 
1129     if (!pempty)
1130         return ERROR_INT("pempty not defined", procName, 1);
1131     *pempty = 1;
1132     if (!pix)
1133         return ERROR_INT("pix not defined", procName, 1);
1134 
1135     w = pixGetWidth(pix) * pixGetDepth(pix);
1136     h = pixGetHeight(pix);
1137     wpl = pixGetWpl(pix);
1138     data = pixGetData(pix);
1139     fullwords = w / 32;
1140     endbits = w & 31;
1141     endmask = 0xffffffff << (32 - endbits);
1142 
1143     for (i = 0; i < h; i++) {
1144         line = data + wpl * i;
1145         for (j = 0; j < fullwords; j++)
1146             if (*line++) {
1147                 *pempty = 0;
1148                 return 0;
1149             }
1150         if (endbits) {
1151             if (*line & endmask) {
1152                 *pempty = 0;
1153                 return 0;
1154             }
1155         }
1156     }
1157 
1158     return 0;
1159 }
1160 
1161 
1162 /*!
1163  *  pixCountPixels()
1164  *
1165  *      Input:  binary pix
1166  *              &count (<return> count of ON pixels)
1167  *              tab8  (<optional> 8-bit pixel lookup table)
1168  *      Return: 0 if OK; 1 on error
1169  */
1170 l_int32
pixCountPixels(PIX * pix,l_int32 * pcount,l_int32 * tab8)1171 pixCountPixels(PIX      *pix,
1172                l_int32  *pcount,
1173                l_int32  *tab8)
1174 {
1175 l_uint32   endmask;
1176 l_int32    w, h, wpl, i, j;
1177 l_int32    fullwords, endbits, sum;
1178 l_int32   *tab;
1179 l_uint32  *data;
1180 
1181     PROCNAME("pixCountPixels");
1182 
1183     if (!pcount)
1184         return ERROR_INT("pcount not defined", procName, 1);
1185     *pcount = 0;
1186     if (!pix || pixGetDepth(pix) != 1)
1187         return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
1188 
1189     if (!tab8)
1190         tab = makePixelSumTab8();
1191     else
1192         tab = tab8;
1193 
1194     pixGetDimensions(pix, &w, &h, NULL);
1195     wpl = pixGetWpl(pix);
1196     data = pixGetData(pix);
1197     fullwords = w >> 5;
1198     endbits = w & 31;
1199     endmask = 0xffffffff << (32 - endbits);
1200 
1201     sum = 0;
1202     for (i = 0; i < h; i++, data += wpl) {
1203         for (j = 0; j < fullwords; j++) {
1204             l_uint32 word = data[j];
1205             if (word) {
1206                 sum += tab[word & 0xff] +
1207                        tab[(word >> 8) & 0xff] +
1208                        tab[(word >> 16) & 0xff] +
1209                        tab[(word >> 24) & 0xff];
1210             }
1211         }
1212         if (endbits) {
1213             l_uint32 word = data[j] & endmask;
1214             if (word) {
1215                 sum += tab[word & 0xff] +
1216                        tab[(word >> 8) & 0xff] +
1217                        tab[(word >> 16) & 0xff] +
1218                        tab[(word >> 24) & 0xff];
1219             }
1220         }
1221     }
1222     *pcount = sum;
1223 
1224     if (!tab8)
1225         FREE(tab);
1226     return 0;
1227 }
1228 
1229 
1230 /*!
1231  *  pixaCountPixels()
1232  *
1233  *      Input:  pixa (array of binary pix)
1234  *      Return: na of ON pixels in each pix, or null on error
1235  */
1236 NUMA *
pixaCountPixels(PIXA * pixa)1237 pixaCountPixels(PIXA  *pixa)
1238 {
1239 l_int32   d, i, n, count;
1240 l_int32  *tab;
1241 NUMA     *na;
1242 PIX      *pix;
1243 
1244     PROCNAME("pixaCountPixels");
1245 
1246     if (!pixa)
1247         return (NUMA *)ERROR_PTR("pix not defined", procName, NULL);
1248 
1249     if ((n = pixaGetCount(pixa)) == 0)
1250         return numaCreate(1);
1251 
1252     pix = pixaGetPix(pixa, 0, L_CLONE);
1253     d = pixGetDepth(pix);
1254     pixDestroy(&pix);
1255     if (d != 1)
1256         return (NUMA *)ERROR_PTR("pixa not 1 bpp", procName, NULL);
1257 
1258     tab = makePixelSumTab8();
1259     if ((na = numaCreate(n)) == NULL)
1260         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
1261     for (i = 0; i < n; i++) {
1262         pix = pixaGetPix(pixa, i, L_CLONE);
1263         pixCountPixels(pix, &count, tab);
1264         numaAddNumber(na, count);
1265         pixDestroy(&pix);
1266     }
1267 
1268     FREE(tab);
1269     return na;
1270 }
1271 
1272 
1273 /*!
1274  *  pixCountPixelsInRow()
1275  *
1276  *      Input:  binary pix
1277  *              row number
1278  *              &count (<return> sum of ON pixels in raster line)
1279  *              tab8  (<optional> 8-bit pixel lookup table)
1280  *      Return: 0 if OK; 1 on error
1281  */
1282 l_int32
pixCountPixelsInRow(PIX * pix,l_int32 row,l_int32 * pcount,l_int32 * tab8)1283 pixCountPixelsInRow(PIX      *pix,
1284                     l_int32   row,
1285                     l_int32  *pcount,
1286                     l_int32  *tab8)
1287 {
1288 l_uint32   word, endmask;
1289 l_int32    j, w, h, wpl;
1290 l_int32    fullwords, endbits, sum;
1291 l_int32   *tab;
1292 l_uint32  *line;
1293 
1294     PROCNAME("pixCountPixelsInRow");
1295 
1296     if (!pcount)
1297         return ERROR_INT("pcount not defined", procName, 1);
1298     *pcount = 0;
1299     if (!pix || pixGetDepth(pix) != 1)
1300         return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
1301 
1302     pixGetDimensions(pix, &w, &h, NULL);
1303     if (row < 0 || row >= h)
1304         return ERROR_INT("row out of bounds", procName, 1);
1305     wpl = pixGetWpl(pix);
1306     line = pixGetData(pix) + row * wpl;
1307     fullwords = w >> 5;
1308     endbits = w & 31;
1309     endmask = 0xffffffff << (32 - endbits);
1310 
1311     if (!tab8)
1312         tab = makePixelSumTab8();
1313     else
1314         tab = tab8;
1315 
1316     sum = 0;
1317     for (j = 0; j < fullwords; j++) {
1318         word = line[j];
1319         if (word) {
1320             sum += tab[word & 0xff] +
1321                    tab[(word >> 8) & 0xff] +
1322                    tab[(word >> 16) & 0xff] +
1323                    tab[(word >> 24) & 0xff];
1324         }
1325     }
1326     if (endbits) {
1327         word = line[j] & endmask;
1328         if (word) {
1329             sum += tab[word & 0xff] +
1330                    tab[(word >> 8) & 0xff] +
1331                    tab[(word >> 16) & 0xff] +
1332                    tab[(word >> 24) & 0xff];
1333         }
1334     }
1335     *pcount = sum;
1336 
1337     if (!tab8)
1338         FREE(tab);
1339     return 0;
1340 }
1341 
1342 
1343 /*!
1344  *  pixCountPixelsByRow()
1345  *
1346  *      Input:  binary pix
1347  *              tab8  (<optional> 8-bit pixel lookup table)
1348  *      Return: na of counts, or null on error
1349  */
1350 NUMA *
pixCountPixelsByRow(PIX * pix,l_int32 * tab8)1351 pixCountPixelsByRow(PIX      *pix,
1352                     l_int32  *tab8)
1353 {
1354 l_int32   h, i, count;
1355 l_int32  *tab;
1356 NUMA     *na;
1357 
1358     PROCNAME("pixCountPixelsByRow");
1359 
1360     if (!pix || pixGetDepth(pix) != 1)
1361         return (NUMA *)ERROR_PTR("pix undefined or not 1 bpp", procName, NULL);
1362 
1363     if (!tab8)
1364         tab = makePixelSumTab8();
1365     else
1366         tab = tab8;
1367 
1368     if ((na = numaCreate(h)) == NULL)
1369         return (NUMA *)ERROR_PTR("na not made", procName, NULL);
1370 
1371     h = pixGetHeight(pix);
1372     for (i = 0; i < h; i++) {
1373         pixCountPixelsInRow(pix, i, &count, tab);
1374         numaAddNumber(na, count);
1375     }
1376 
1377     if (!tab8)
1378         FREE(tab);
1379 
1380     return na;
1381 }
1382 
1383 
1384 /*!
1385  *  pixThresholdPixels()
1386  *
1387  *      Input:  binary pix
1388  *              threshold
1389  *              &above (<return> 1 if above threshold;
1390  *                               0 if equal to or less than threshold)
1391  *              tab8  (<optional> 8-bit pixel lookup table)
1392  *      Return: 0 if OK; 1 on error
1393  *
1394  *  Notes:
1395  *      (1) This sums the ON pixels and returns immediately if the count
1396  *          goes above threshold.  It is therefore more efficient
1397  *          for matching images (by running this function on the xor of
1398  *          the 2 images) than using pixCountPixels(), which counts all
1399  *          pixels before returning.
1400  */
1401 l_int32
pixThresholdPixels(PIX * pix,l_int32 thresh,l_int32 * pabove,l_int32 * tab8)1402 pixThresholdPixels(PIX      *pix,
1403                    l_int32   thresh,
1404                    l_int32  *pabove,
1405                    l_int32  *tab8)
1406 {
1407 l_uint32   word, endmask;
1408 l_int32   *tab;
1409 l_int32    w, h, wpl, i, j;
1410 l_int32    fullwords, endbits, sum;
1411 l_uint32  *line, *data;
1412 
1413     PROCNAME("pixThresholdPixels");
1414 
1415     if (!pabove)
1416         return ERROR_INT("pabove not defined", procName, 1);
1417     *pabove = 0;
1418     if (!pix || pixGetDepth(pix) != 1)
1419         return ERROR_INT("pix not defined or not 1 bpp", procName, 1);
1420 
1421     if (!tab8)
1422         tab = makePixelSumTab8();
1423     else
1424         tab = tab8;
1425 
1426     pixGetDimensions(pix, &w, &h, NULL);
1427     wpl = pixGetWpl(pix);
1428     data = pixGetData(pix);
1429     fullwords = w >> 5;
1430     endbits = w & 31;
1431     endmask = 0xffffffff << (32 - endbits);
1432 
1433     sum = 0;
1434     for (i = 0; i < h; i++) {
1435         line = data + wpl * i;
1436         for (j = 0; j < fullwords; j++) {
1437             word = line[j];
1438             if (word) {
1439                 sum += tab[word & 0xff] +
1440                        tab[(word >> 8) & 0xff] +
1441                        tab[(word >> 16) & 0xff] +
1442                        tab[(word >> 24) & 0xff];
1443             }
1444         }
1445         if (endbits) {
1446             word = line[j] & endmask;
1447             if (word) {
1448                 sum += tab[word & 0xff] +
1449                        tab[(word >> 8) & 0xff] +
1450                        tab[(word >> 16) & 0xff] +
1451                        tab[(word >> 24) & 0xff];
1452             }
1453         }
1454         if (sum > thresh) {
1455             *pabove = 1;
1456             if (!tab8)
1457                 FREE(tab);
1458             return 0;
1459         }
1460     }
1461 
1462     if (!tab8)
1463         FREE(tab);
1464     return 0;
1465 }
1466 
1467 
1468 /*!
1469  *  makePixelSumTab8()
1470  *
1471  *      Input:  void
1472  *      Return: table of 256 l_int32, or null on error
1473  *
1474  *  Notes:
1475  *      (1) This table of integers gives the number of 1 bits
1476  *          in the 8 bit index.
1477  */
1478 l_int32 *
makePixelSumTab8(void)1479 makePixelSumTab8(void)
1480 {
1481 l_uint8   byte;
1482 l_int32   i;
1483 l_int32  *tab;
1484 
1485     PROCNAME("makePixelSumTab8");
1486 
1487     if ((tab = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
1488         return (l_int32 *)ERROR_PTR("tab not made", procName, NULL);
1489 
1490     for (i = 0; i < 256; i++) {
1491         byte = (l_uint8)i;
1492         tab[i] = (byte & 0x1) +
1493                  ((byte >> 1) & 0x1) +
1494                  ((byte >> 2) & 0x1) +
1495                  ((byte >> 3) & 0x1) +
1496                  ((byte >> 4) & 0x1) +
1497                  ((byte >> 5) & 0x1) +
1498                  ((byte >> 6) & 0x1) +
1499                  ((byte >> 7) & 0x1);
1500     }
1501 
1502     return tab;
1503 }
1504 
1505 
1506 /*!
1507  *  makePixelCentroidTab8()
1508  *
1509  *      Input:  void
1510  *      Return: table of 256 l_int32, or null on error
1511  *
1512  *  Notes:
1513  *      (1) This table of integers gives the centroid weight of the 1 bits
1514  *          in the 8 bit index.  In other words, if sumtab is obtained by
1515  *          makePixelSumTab8, and centroidtab is obtained by
1516  *          makePixelCentroidTab8, then, for 1 <= i <= 255,
1517  *          centroidtab[i] / (float)sumtab[i]
1518  *          is the centroid of the 1 bits in the 8-bit index i, where the
1519  *          MSB is considered to have position 0 and the LSB is considered
1520  *          to have position 7.
1521  */
1522 l_int32 *
makePixelCentroidTab8(void)1523 makePixelCentroidTab8(void)
1524 {
1525 l_int32   i;
1526 l_int32  *tab;
1527 
1528     PROCNAME("makePixelCentroidTab8");
1529 
1530     if ((tab = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL)
1531         return (l_int32 *)ERROR_PTR("tab not made", procName, NULL);
1532 
1533     tab[0] = 0;
1534     tab[1] = 7;
1535     for (i = 2; i < 4; i++) {
1536         tab[i] = tab[i - 2] + 6;
1537     }
1538     for (i = 4; i < 8; i++) {
1539         tab[i] = tab[i - 4] + 5;
1540     }
1541     for (i = 8; i < 16; i++) {
1542         tab[i] = tab[i - 8] + 4;
1543     }
1544     for (i = 16; i < 32; i++) {
1545         tab[i] = tab[i - 16] + 3;
1546     }
1547     for (i = 32; i < 64; i++) {
1548         tab[i] = tab[i - 32] + 2;
1549     }
1550     for (i = 64; i < 128; i++) {
1551         tab[i] = tab[i - 64] + 1;
1552     }
1553     for (i = 128; i < 256; i++) {
1554         tab[i] = tab[i - 128];
1555     }
1556 
1557     return tab;
1558 }
1559 
1560 
1561 /*-------------------------------------------------------------*
1562  *                       Sum of pixel values                   *
1563  *-------------------------------------------------------------*/
1564 /*!
1565  *  pixSumPixelValues()
1566  *
1567  *      Input:  pix (1, 2, 4, 8, 16, 32 bpp; not cmapped)
1568  *              box (<optional> if null, use entire image)
1569  *              &sum (<return> sum of pixel values in region)
1570  *      Return: 0 if OK; 1 on error
1571  */
1572 l_int32
pixSumPixelValues(PIX * pix,BOX * box,l_float64 * psum)1573 pixSumPixelValues(PIX        *pix,
1574                   BOX        *box,
1575                   l_float64  *psum)
1576 {
1577 l_int32    w, h, d, wpl, i, j, xstart, xend, ystart, yend, bw, bh;
1578 l_uint32  *data, *line;
1579 l_float64  sum;
1580 BOX       *boxc;
1581 
1582     PROCNAME("pixSumPixelValues");
1583 
1584     if (!psum)
1585         return ERROR_INT("psum not defined", procName, 1);
1586     *psum = 0;
1587     if (!pix)
1588         return ERROR_INT("pix not defined", procName, 1);
1589     if (pixGetColormap(pix) != NULL)
1590         return ERROR_INT("pix is colormapped", procName, 1);
1591     pixGetDimensions(pix, &w, &h, &d);
1592     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
1593         return ERROR_INT("pix not 1, 2, 4, 8, 16 or 32 bpp", procName, 1);
1594 
1595     wpl = pixGetWpl(pix);
1596     data = pixGetData(pix);
1597     boxc = NULL;
1598     if (box) {
1599         boxc = boxClipToRectangle(box, w, h);
1600         if (!boxc)
1601             return ERROR_INT("box outside image", procName, 1);
1602     }
1603     xstart = ystart = 0;
1604     xend = w;
1605     yend = h;
1606     if (boxc) {
1607         boxGetGeometry(boxc, &xstart, &ystart, &bw, &bh);
1608         xend = xstart + bw;  /* 1 past the end */
1609         yend = ystart + bh;  /* 1 past the end */
1610         boxDestroy(&boxc);
1611     }
1612 
1613     sum = 0;
1614     for (i = ystart; i < yend; i++) {
1615         line = data + i * wpl;
1616         for (j = xstart; j < xend; j++) {
1617             if (d == 1)
1618                 sum += GET_DATA_BIT(line, j);
1619             else if (d == 2)
1620                 sum += GET_DATA_DIBIT(line, j);
1621             else if (d == 4)
1622                 sum += GET_DATA_QBIT(line, j);
1623             else if (d == 8)
1624                 sum += GET_DATA_BYTE(line, j);
1625             else if (d == 16)
1626                 sum += GET_DATA_TWO_BYTES(line, j);
1627             else if (d == 32)
1628                 sum += line[j];
1629         }
1630     }
1631     *psum = sum;
1632 
1633     return 0;
1634 }
1635 
1636 
1637 
1638 /*-------------------------------------------------------------*
1639  *              Mirrored tiling of a smaller image             *
1640  *-------------------------------------------------------------*/
1641 /*!
1642  *  pixMirroredTiling()
1643  *
1644  *      Input:  pixs (8 or 32 bpp, small tile; to be replicated)
1645  *              w, h (dimensions of output pix)
1646  *      Return: pixd (usually larger pix, mirror-tiled with pixs),
1647  *              or null on error
1648  *
1649  *  Notes:
1650  *      (1) This uses mirrored tiling, where each row alternates
1651  *          with LR flips and every column alternates with TB
1652  *          flips, such that the result is a tiling with identical
1653  *          2 x 2 tiles, each of which is composed of these transforms:
1654  *                  -----------------
1655  *                  | 1    |  LR    |
1656  *                  -----------------
1657  *                  | TB   |  LR/TB |
1658  *                  -----------------
1659  */
1660 PIX *
pixMirroredTiling(PIX * pixs,l_int32 w,l_int32 h)1661 pixMirroredTiling(PIX     *pixs,
1662                   l_int32  w,
1663                   l_int32  h)
1664 {
1665 l_int32   wt, ht, d, i, j, nx, ny;
1666 PIX      *pixd, *pixsfx, *pixsfy, *pixsfxy, *pix;
1667 
1668     PROCNAME("pixMirroredTiling");
1669 
1670     if (!pixs)
1671         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
1672     pixGetDimensions(pixs, &wt, &ht, &d);
1673     if (wt <= 0 || ht <= 0)
1674         return (PIX *)ERROR_PTR("pixs size illegal", procName, NULL);
1675     if (d != 8 && d != 32)
1676         return (PIX *)ERROR_PTR("depth not 32 bpp", procName, NULL);
1677     if ((pixd = pixCreate(w, h, d)) == NULL)
1678         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
1679 
1680     nx = (w + wt - 1) / wt;
1681     ny = (h + ht - 1) / ht;
1682     pixsfx = pixFlipLR(NULL, pixs);
1683     pixsfy = pixFlipTB(NULL, pixs);
1684     pixsfxy = pixFlipTB(NULL, pixsfx);
1685     for (i = 0; i < ny; i++) {
1686         for (j = 0; j < nx; j++) {
1687             pix = pixs;
1688             if ((i & 1) && !(j & 1))
1689                 pix = pixsfy;
1690             else if (!(i & 1) && (j & 1))
1691                 pix = pixsfx;
1692             else if ((i & 1) && (j & 1))
1693                 pix = pixsfxy;
1694             pixRasterop(pixd, j * wt, i * ht, wt, ht, PIX_SRC, pix, 0, 0);
1695         }
1696     }
1697 
1698     pixDestroy(&pixsfx);
1699     pixDestroy(&pixsfy);
1700     pixDestroy(&pixsfxy);
1701     return pixd;
1702 }
1703 
1704 
1705 /*!
1706  *  findTilePatchCenter()
1707  *
1708  *      Input:  pixs (8 or 16 bpp; distance function of a binary mask)
1709  *              box (region of pixs to search around)
1710  *              searchdir (L_HORIZ or L_VERT; direction to search)
1711  *              targdist (desired distance of selected patch center from fg)
1712  *              &dist (<return> actual distance of selected location)
1713  *              &xc, &yc (<return> location of selected patch center)
1714  *      Return: 0 if OK, 1 on error
1715  *
1716  *  Notes:
1717  *      (1) This looks for a patch of non-masked image, that is outside
1718  *          but near the input box.  The input pixs is a distance
1719  *          function giving the distance from the fg in a binary mask.
1720  *      (2) The target distance implicitly specifies a desired size
1721  *          for the patch.  The location of the center of the patch,
1722  *          and the actual distance from fg are returned.
1723  *      (3) If the target distance is larger than 255, a 16-bit distance
1724  *          transform is input.
1725  *      (4) It is assured that a square centered at (xc, yc) and of
1726  *          size 'dist' will not intersect with the fg of the binary
1727  *          mask that was used to generate pixs.
1728  */
1729 static l_int32
findTilePatchCenter(PIX * pixs,BOX * box,l_int32 searchdir,l_uint32 targdist,l_uint32 * pdist,l_int32 * pxc,l_int32 * pyc)1730 findTilePatchCenter(PIX       *pixs,
1731                     BOX       *box,
1732                     l_int32    searchdir,
1733                     l_uint32   targdist,
1734                     l_uint32  *pdist,
1735                     l_int32   *pxc,
1736                     l_int32   *pyc)
1737 {
1738 l_int32   w, h, bx, by, bw, bh, left, right, top, bot, i, j;
1739 l_uint32  val, maxval;
1740 
1741     PROCNAME("findTilePatchCenter");
1742 
1743     if (!pdist || !pxc || !pyc)
1744         return ERROR_INT("&pdist, &pxc, &pyc not all defined", procName, 1);
1745     *pdist = *pxc = *pyc = 0;
1746     if (!pixs)
1747         return ERROR_INT("pixs not defined", procName, 1);
1748     if (!box)
1749         return ERROR_INT("box not defined", procName, 1);
1750 
1751     pixGetDimensions(pixs, &w, &h, NULL);
1752     boxGetGeometry(box, &bx, &by, &bw, &bh);
1753 
1754     if (searchdir == L_HORIZ) {
1755         left = bx;   /* distance to left of box */
1756         right = w - bx - bw + 1;   /* distance to right of box */
1757         maxval = 0;
1758         if (left > right) {  /* search to left */
1759             for (j = bx - 1; j >= 0; j--) {
1760                 for (i = by; i < by + bh; i++) {
1761                     pixGetPixel(pixs, j, i, &val);
1762                     if (val > maxval) {
1763                         maxval = val;
1764                         *pxc = j;
1765                         *pyc = i;
1766                         *pdist = val;
1767                         if (val >= targdist)
1768                             return 0;
1769                     }
1770                 }
1771             }
1772         }
1773         else {  /* search to right */
1774             for (j = bx + bw; j < w; j++) {
1775                 for (i = by; i < by + bh; i++) {
1776                     pixGetPixel(pixs, j, i, &val);
1777                     if (val > maxval) {
1778                         maxval = val;
1779                         *pxc = j;
1780                         *pyc = i;
1781                         *pdist = val;
1782                         if (val >= targdist)
1783                             return 0;
1784                     }
1785                 }
1786             }
1787         }
1788     }
1789     else {  /* searchdir == L_VERT */
1790         top = by;    /* distance above box */
1791         bot = h - by - bh + 1;   /* distance below box */
1792         maxval = 0;
1793         if (top > bot) {  /* search above */
1794             for (i = by - 1; i >= 0; i--) {
1795                 for (j = bx; j < bx + bw; j++) {
1796                     pixGetPixel(pixs, j, i, &val);
1797                     if (val > maxval) {
1798                         maxval = val;
1799                         *pxc = j;
1800                         *pyc = i;
1801                         *pdist = val;
1802                         if (val >= targdist)
1803                             return 0;
1804                     }
1805                 }
1806             }
1807         }
1808         else {  /* search below */
1809             for (i = by + bh; i < h; i++) {
1810                 for (j = bx; j < bx + bw; j++) {
1811                     pixGetPixel(pixs, j, i, &val);
1812                     if (val > maxval) {
1813                         maxval = val;
1814                         *pxc = j;
1815                         *pyc = i;
1816                         *pdist = val;
1817                         if (val >= targdist)
1818                             return 0;
1819                     }
1820                 }
1821             }
1822         }
1823     }
1824 
1825 
1826     pixGetPixel(pixs, *pxc, *pyc, pdist);
1827     return 0;
1828 }
1829 
1830