Leptonica  1.83.1
Image processing and image analysis suite
ccbord.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 
27 
250 #ifdef HAVE_CONFIG_H
251 #include <config_auto.h>
252 #endif /* HAVE_CONFIG_H */
253 
254 #include <string.h>
255 #include "allheaders.h"
256 #include "pix_internal.h"
257 #include "ccbord_internal.h"
258 
259 static const l_int32 INITIAL_PTR_ARRAYSIZE = 20; /* n'import quoi */
260 
261  /* In ccbaGenerateSinglePath(): don't save holes
262  * in c.c. with ridiculously many small holes */
263 static const l_int32 NMAX_HOLES = 150;
264 
265  /* Tables used to trace the border.
266  * - The 8 pixel positions of neighbors Q are labeled clockwise
267  * starting from the west:
268  * 1 2 3
269  * 0 P 4
270  * 7 6 5
271  * where the labels are the index offset [0, ... 7] of Q relative to P.
272  * - xpostab[] and ypostab[] give the actual x and y pixel offsets
273  * of Q relative to P, indexed by the index offset.
274  * - qpostab[pos] gives the new index offset of Q relative to P, at
275  * the time that a new P has been chosen to be in index offset
276  * position 'pos' relative to the previous P. The relation
277  * between P and Q is always 4-connected. */
278 static const l_int32 xpostab[] = {-1, -1, 0, 1, 1, 1, 0, -1};
279 static const l_int32 ypostab[] = {0, -1, -1, -1, 0, 1, 1, 1};
280 static const l_int32 qpostab[] = {6, 6, 0, 0, 2, 2, 4, 4};
281 
282  /* Static functions */
283 static CCBORDA *ccbaCreate(PIX *pixs, l_int32 n);
284 static CCBORD *ccbCreate(PIX *pixs);
285 static void ccbDestroy(CCBORD **pccb);
286 static l_ok ccbaAddCcb(CCBORDA *ccba, CCBORD *ccb);
287 static l_int32 ccbaExtendArray(CCBORDA *ccba);
288 static l_int32 ccbaGetCount(CCBORDA *ccba);
289 static CCBORD *ccbaGetCcb(CCBORDA *ccba, l_int32 index);
290 static CCBORD *pixGetCCBorders(PIX *pixs, BOX *box);
291 static PTA *pixGetOuterBorderPta(PIX *pixs, BOX *box);
292 static l_ok pixGetHoleBorder(CCBORD *ccb, PIX *pixs, BOX *box,
293  l_int32 xs, l_int32 ys);
294 static l_int32 findNextBorderPixel(l_int32 w, l_int32 h, l_uint32 *data,
295  l_int32 wpl, l_int32 px, l_int32 py,
296  l_int32 *pqpos, l_int32 *pnpx,
297  l_int32 *pnpy);
298 static void locateOutsideSeedPixel(l_int32 fpx, l_int32 fpy, l_int32 spx,
299  l_int32 spy, l_int32 *pxs, l_int32 *pys);
300 static PTA *getCutPathForHole(PIX *pix, PTA *pta, BOX *boxinner, l_int32 *pdir,
301  l_int32 *plen);
302 
303 #ifndef NO_CONSOLE_IO
304 #define DEBUG_PRINT 0
305 #endif /* NO CONSOLE_IO */
306 
307 
308 /*---------------------------------------------------------------------*
309  * ccba and ccb creation and destruction *
310  *---------------------------------------------------------------------*/
318 static CCBORDA *
320  l_int32 n)
321 {
322 CCBORDA *ccba;
323 
324  if (n <= 0)
325  n = INITIAL_PTR_ARRAYSIZE;
326 
327  ccba = (CCBORDA *)LEPT_CALLOC(1, sizeof(CCBORDA));
328  if (pixs) {
329  ccba->pix = pixClone(pixs);
330  ccba->w = pixGetWidth(pixs);
331  ccba->h = pixGetHeight(pixs);
332  }
333  ccba->n = 0;
334  ccba->nalloc = n;
335  if ((ccba->ccb = (CCBORD **)LEPT_CALLOC(n, sizeof(CCBORD *))) == NULL) {
336  ccbaDestroy(&ccba);
337  return (CCBORDA *)ERROR_PTR("ccba ptrs not made", __func__, NULL);
338  }
339  return ccba;
340 }
341 
342 
349 void
351 {
352 l_int32 i;
353 CCBORDA *ccba;
354 
355  if (pccba == NULL) {
356  L_WARNING("ptr address is NULL!\n", __func__);
357  return;
358  }
359 
360  if ((ccba = *pccba) == NULL)
361  return;
362 
363  pixDestroy(&ccba->pix);
364  for (i = 0; i < ccba->n; i++)
365  ccbDestroy(&ccba->ccb[i]);
366  LEPT_FREE(ccba->ccb);
367  LEPT_FREE(ccba);
368  *pccba = NULL;
369 }
370 
371 
378 static CCBORD *
380 {
381 BOXA *boxa;
382 CCBORD *ccb;
383 PTA *start;
384 PTAA *local;
385 
386  if (pixs && pixGetDepth(pixs) != 1) /* pixs can be null */
387  return (CCBORD *)ERROR_PTR("pixs defined and not 1bpp", __func__, NULL);
388 
389  ccb = (CCBORD *)LEPT_CALLOC(1, sizeof(CCBORD));
390  ccb->refcount = 1;
391  if (pixs)
392  ccb->pix = pixClone(pixs);
393  boxa = boxaCreate(1);
394  ccb->boxa = boxa;
395  start = ptaCreate(1);
396  ccb->start = start;
397  local = ptaaCreate(1);
398  ccb->local = local;
399  return ccb;
400 }
401 
402 
409 static void
411 {
412 CCBORD *ccb;
413 
414  if (pccb == NULL) {
415  L_WARNING("ptr address is NULL!\n", __func__);
416  return;
417  }
418 
419  if ((ccb = *pccb) == NULL)
420  return;
421 
422  if (--ccb->refcount == 0) {
423  if (ccb->pix)
424  pixDestroy(&ccb->pix);
425  if (ccb->boxa)
426  boxaDestroy(&ccb->boxa);
427  if (ccb->start)
428  ptaDestroy(&ccb->start);
429  if (ccb->local)
430  ptaaDestroy(&ccb->local);
431  if (ccb->global)
432  ptaaDestroy(&ccb->global);
433  if (ccb->step)
434  numaaDestroy(&ccb->step);
435  if (ccb->splocal)
436  ptaDestroy(&ccb->splocal);
437  if (ccb->spglobal)
438  ptaDestroy(&ccb->spglobal);
439  LEPT_FREE(ccb);
440  *pccb = NULL;
441  }
442 }
443 
444 
445 /*---------------------------------------------------------------------*
446  * ccba addition *
447  *---------------------------------------------------------------------*/
455 static l_ok
457  CCBORD *ccb)
458 {
459 l_int32 n;
460 
461  if (!ccba)
462  return ERROR_INT("ccba not defined", __func__, 1);
463  if (!ccb)
464  return ERROR_INT("ccb not defined", __func__, 1);
465 
466  n = ccbaGetCount(ccba);
467  if (n >= ccba->nalloc) {
468  if (ccbaExtendArray(ccba))
469  return ERROR_INT("extension failed", __func__, 1);
470  }
471  ccba->ccb[n] = ccb;
472  ccba->n++;
473  return 0;
474 }
475 
476 
483 static l_int32
485 {
486  if (!ccba)
487  return ERROR_INT("ccba not defined", __func__, 1);
488 
489  if ((ccba->ccb = (CCBORD **)reallocNew((void **)&ccba->ccb,
490  sizeof(CCBORD *) * ccba->nalloc,
491  2 * sizeof(CCBORD *) * ccba->nalloc)) == NULL)
492  return ERROR_INT("new ptr array not returned", __func__, 1);
493 
494  ccba->nalloc = 2 * ccba->nalloc;
495  return 0;
496 }
497 
498 
499 
500 /*---------------------------------------------------------------------*
501  * ccba accessors *
502  *---------------------------------------------------------------------*/
509 static l_int32
511 {
512 
513  if (!ccba)
514  return ERROR_INT("ccba not defined", __func__, 0);
515 
516  return ccba->n;
517 }
518 
519 
532 static CCBORD *
534  l_int32 index)
535 {
536 CCBORD *ccb;
537 
538  if (!ccba)
539  return (CCBORD *)ERROR_PTR("ccba not defined", __func__, NULL);
540  if (index < 0 || index >= ccba->n)
541  return (CCBORD *)ERROR_PTR("index out of bounds", __func__, NULL);
542 
543  ccb = ccba->ccb[index];
544  ccb->refcount++;
545  return ccb;
546 }
547 
548 
549 
550 /*---------------------------------------------------------------------*
551  * Top-level border-finding routines *
552  *---------------------------------------------------------------------*/
559 CCBORDA *
561 {
562 l_int32 n, i;
563 BOX *box;
564 BOXA *boxa;
565 CCBORDA *ccba;
566 CCBORD *ccb;
567 PIX *pix;
568 PIXA *pixa;
569 
570  if (!pixs)
571  return (CCBORDA *)ERROR_PTR("pixs not defined", __func__, NULL);
572  if (pixGetDepth(pixs) != 1)
573  return (CCBORDA *)ERROR_PTR("pixs not binary", __func__, NULL);
574 
575  if ((boxa = pixConnComp(pixs, &pixa, 8)) == NULL)
576  return (CCBORDA *)ERROR_PTR("boxa not made", __func__, NULL);
577  n = boxaGetCount(boxa);
578 
579  if ((ccba = ccbaCreate(pixs, n)) == NULL) {
580  boxaDestroy(&boxa);
581  pixaDestroy(&pixa);
582  return (CCBORDA *)ERROR_PTR("ccba not made", __func__, NULL);
583  }
584  for (i = 0; i < n; i++) {
585  if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) {
586  ccbaDestroy(&ccba);
587  pixaDestroy(&pixa);
588  boxaDestroy(&boxa);
589  return (CCBORDA *)ERROR_PTR("pix not found", __func__, NULL);
590  }
591  if ((box = pixaGetBox(pixa, i, L_CLONE)) == NULL) {
592  ccbaDestroy(&ccba);
593  pixaDestroy(&pixa);
594  boxaDestroy(&boxa);
595  pixDestroy(&pix);
596  return (CCBORDA *)ERROR_PTR("box not found", __func__, NULL);
597  }
598  ccb = pixGetCCBorders(pix, box);
599  pixDestroy(&pix);
600  boxDestroy(&box);
601  if (!ccb) {
602  ccbaDestroy(&ccba);
603  pixaDestroy(&pixa);
604  boxaDestroy(&boxa);
605  return (CCBORDA *)ERROR_PTR("ccb not made", __func__, NULL);
606  }
607 /* ptaWriteStream(stderr, ccb->local, 1); */
608  ccbaAddCcb(ccba, ccb);
609  }
610 
611  boxaDestroy(&boxa);
612  pixaDestroy(&pixa);
613  return ccba;
614 }
615 
616 
643 static CCBORD *
645  BOX *box)
646 {
647 l_int32 allzero, i, x, xh, w, nh;
648 l_int32 xs, ys; /* starting hole border pixel, relative in pixs */
649 l_uint32 val;
650 BOX *boxt, *boxe;
651 BOXA *boxa;
652 CCBORD *ccb;
653 PIX *pixh; /* for hole components */
654 PIX *pixt;
655 PIXA *pixa;
656 
657  if (!pixs)
658  return (CCBORD *)ERROR_PTR("pixs not defined", __func__, NULL);
659  if (!box)
660  return (CCBORD *)ERROR_PTR("box not defined", __func__, NULL);
661  if (pixGetDepth(pixs) != 1)
662  return (CCBORD *)ERROR_PTR("pixs not binary", __func__, NULL);
663 
664  pixZero(pixs, &allzero);
665  if (allzero)
666  return (CCBORD *)ERROR_PTR("pixs all 0", __func__, NULL);
667 
668  if ((ccb = ccbCreate(pixs)) == NULL)
669  return (CCBORD *)ERROR_PTR("ccb not made", __func__, NULL);
670 
671  /* Get the exterior border */
672  pixGetOuterBorder(ccb, pixs, box);
673 
674  /* Find the holes, if any */
675  if ((pixh = pixHolesByFilling(pixs, 4)) == NULL) {
676  ccbDestroy(&ccb);
677  return (CCBORD *)ERROR_PTR("pixh not made", __func__, NULL);
678  }
679  pixZero(pixh, &allzero);
680  if (allzero) { /* no holes */
681  pixDestroy(&pixh);
682  return ccb;
683  }
684 
685  /* Get c.c. and locations of the holes */
686  if ((boxa = pixConnComp(pixh, &pixa, 4)) == NULL) {
687  ccbDestroy(&ccb);
688  pixDestroy(&pixh);
689  return (CCBORD *)ERROR_PTR("boxa not made", __func__, NULL);
690  }
691  nh = boxaGetCount(boxa);
692 /* lept_stderr("%d holes\n", nh); */
693 
694  /* For each hole, find an interior pixel within the hole,
695  * then march to the right and stop at the first border
696  * pixel. Save the bounding box of the border, which
697  * is 1 pixel bigger on each side than the bounding box
698  * of the hole itself. Note that we use a pix of the
699  * c.c. of the hole itself to be sure that we start
700  * with a pixel in the hole of the proper component.
701  * If we did everything from the parent component, it is
702  * possible to start in a different hole that is within
703  * the b.b. of a larger hole. */
704  w = pixGetWidth(pixs);
705  for (i = 0; i < nh; i++) {
706  boxt = boxaGetBox(boxa, i, L_CLONE);
707  pixt = pixaGetPix(pixa, i, L_CLONE);
708  ys = boxt->y; /* there must be a hole pixel on this raster line */
709  for (x = 0; x < boxt->w; x++) { /* look for (fg) hole pixel */
710  pixGetPixel(pixt, x, 0, &val);
711  if (val == 1) {
712  xh = x;
713  break;
714  }
715  }
716  if (x == boxt->w) {
717  L_WARNING("no hole pixel found!\n", __func__);
718  continue;
719  }
720  for (x = xh + boxt->x; x < w; x++) { /* look for (fg) border pixel */
721  pixGetPixel(pixs, x, ys, &val);
722  if (val == 1) {
723  xs = x;
724  break;
725  }
726  }
727  boxe = boxCreate(boxt->x - 1, boxt->y - 1, boxt->w + 2, boxt->h + 2);
728 #if DEBUG_PRINT
729  boxPrintStreamInfo(stderr, box);
730  boxPrintStreamInfo(stderr, boxe);
731  lept_stderr("xs = %d, ys = %d\n", xs, ys);
732 #endif /* DEBUG_PRINT */
733  pixGetHoleBorder(ccb, pixs, boxe, xs, ys);
734  boxDestroy(&boxt);
735  boxDestroy(&boxe);
736  pixDestroy(&pixt);
737  }
738 
739  boxaDestroy(&boxa);
740  pixaDestroy(&pixa);
741  pixDestroy(&pixh);
742  return ccb;
743 }
744 
745 
752 PTAA *
754 {
755 l_int32 i, n;
756 BOX *box;
757 BOXA *boxa;
758 PIX *pix;
759 PIXA *pixa;
760 PTA *pta;
761 PTAA *ptaa;
762 
763  if (!pixs)
764  return (PTAA *)ERROR_PTR("pixs not defined", __func__, NULL);
765  if (pixGetDepth(pixs) != 1)
766  return (PTAA *)ERROR_PTR("pixs not binary", __func__, NULL);
767 
768  boxa = pixConnComp(pixs, &pixa, 8);
769  n = boxaGetCount(boxa);
770  if (n == 0) {
771  boxaDestroy(&boxa);
772  pixaDestroy(&pixa);
773  return (PTAA *)ERROR_PTR("pixs empty", __func__, NULL);
774  }
775 
776  ptaa = ptaaCreate(n);
777  for (i = 0; i < n; i++) {
778  box = boxaGetBox(boxa, i, L_CLONE);
779  pix = pixaGetPix(pixa, i, L_CLONE);
780  pta = pixGetOuterBorderPta(pix, box);
781  if (pta)
782  ptaaAddPta(ptaa, pta, L_INSERT);
783  boxDestroy(&box);
784  pixDestroy(&pix);
785  }
786 
787  pixaDestroy(&pixa);
788  boxaDestroy(&boxa);
789  return ptaa;
790 }
791 
792 
810 static PTA *
812  BOX *box)
813 {
814 l_int32 allzero, x, y;
815 BOX *boxt;
816 CCBORD *ccb;
817 PTA *ptaloc, *ptad;
818 
819  if (!pixs)
820  return (PTA *)ERROR_PTR("pixs not defined", __func__, NULL);
821  if (pixGetDepth(pixs) != 1)
822  return (PTA *)ERROR_PTR("pixs not binary", __func__, NULL);
823 
824  pixZero(pixs, &allzero);
825  if (allzero)
826  return (PTA *)ERROR_PTR("pixs all 0", __func__, NULL);
827 
828  if ((ccb = ccbCreate(pixs)) == NULL)
829  return (PTA *)ERROR_PTR("ccb not made", __func__, NULL);
830  if (!box)
831  boxt = boxCreate(0, 0, pixGetWidth(pixs), pixGetHeight(pixs));
832  else
833  boxt = boxClone(box);
834 
835  /* Get the exterior border in local coords */
836  pixGetOuterBorder(ccb, pixs, boxt);
837  if ((ptaloc = ptaaGetPta(ccb->local, 0, L_CLONE)) == NULL) {
838  ccbDestroy(&ccb);
839  boxDestroy(&boxt);
840  return (PTA *)ERROR_PTR("ptaloc not made", __func__, NULL);
841  }
842 
843  /* Transform to global coordinates, if they are given */
844  if (box) {
845  boxGetGeometry(box, &x, &y, NULL, NULL);
846  ptad = ptaTransform(ptaloc, x, y, 1.0, 1.0);
847  } else {
848  ptad = ptaClone(ptaloc);
849  }
850 
851  ptaDestroy(&ptaloc);
852  boxDestroy(&boxt);
853  ccbDestroy(&ccb);
854  return ptad;
855 }
856 
857 
858 /*---------------------------------------------------------------------*
859  * Lower-level border-finding routines *
860  *---------------------------------------------------------------------*/
881 l_ok
883  PIX *pixs,
884  BOX *box)
885 {
886 l_int32 fpx, fpy, spx, spy, qpos;
887 l_int32 px, py, npx, npy;
888 l_int32 w, h, wpl;
889 l_uint32 *data;
890 PTA *pta;
891 PIX *pixb; /* with 1 pixel border */
892 
893  if (!ccb)
894  return ERROR_INT("ccb not defined", __func__, 1);
895  if (!pixs)
896  return ERROR_INT("pixs not defined", __func__, 1);
897  if (!box)
898  return ERROR_INT("box not defined", __func__, 1);
899 
900  /* Add 1-pixel border all around, and find start pixel */
901  if ((pixb = pixAddBorder(pixs, 1, 0)) == NULL)
902  return ERROR_INT("pixs not made", __func__, 1);
903  if (!nextOnPixelInRaster(pixb, 1, 1, &px, &py)) {
904  pixDestroy(&pixb);
905  return ERROR_INT("no start pixel found", __func__, 1);
906  }
907  qpos = 0; /* relative to p */
908  fpx = px; /* save location of first pixel on border */
909  fpy = py;
910 
911  /* Save box and start pixel in relative coords */
912  boxaAddBox(ccb->boxa, box, L_COPY);
913  ptaAddPt(ccb->start, px - 1, py - 1);
914 
915  pta = ptaCreate(0);
916  ptaaAddPta(ccb->local, pta, L_INSERT);
917  ptaAddPt(pta, px - 1, py - 1); /* initial point */
918  pixGetDimensions(pixb, &w, &h, NULL);
919  data = pixGetData(pixb);
920  wpl = pixGetWpl(pixb);
921 
922  /* Get the second point; if there is none, return */
923  if (findNextBorderPixel(w, h, data, wpl, px, py, &qpos, &npx, &npy)) {
924  pixDestroy(&pixb);
925  return 0;
926  }
927 
928  spx = npx; /* save location of second pixel on border */
929  spy = npy;
930  ptaAddPt(pta, npx - 1, npy - 1); /* second point */
931  px = npx;
932  py = npy;
933 
934  while (1) {
935  findNextBorderPixel(w, h, data, wpl, px, py, &qpos, &npx, &npy);
936  if (px == fpx && py == fpy && npx == spx && npy == spy)
937  break;
938  ptaAddPt(pta, npx - 1, npy - 1);
939  px = npx;
940  py = npy;
941  }
942 
943  pixDestroy(&pixb);
944  return 0;
945 }
946 
947 
967 static l_ok
969  PIX *pixs,
970  BOX *box,
971  l_int32 xs,
972  l_int32 ys)
973 {
974 l_int32 fpx, fpy, spx, spy, qpos;
975 l_int32 px, py, npx, npy;
976 l_int32 w, h, wpl;
977 l_uint32 *data;
978 PTA *pta;
979 
980  if (!ccb)
981  return ERROR_INT("ccb not defined", __func__, 1);
982  if (!pixs)
983  return ERROR_INT("pixs not defined", __func__, 1);
984  if (!box)
985  return ERROR_INT("box not defined", __func__, 1);
986 
987  /* Add border and find start pixel */
988  qpos = 0; /* orientation of Q relative to P */
989  fpx = xs; /* save location of first pixel on border */
990  fpy = ys;
991 
992  /* Save box and start pixel */
993  boxaAddBox(ccb->boxa, box, L_COPY);
994  ptaAddPt(ccb->start, xs, ys);
995 
996  pta = ptaCreate(0);
997  ptaaAddPta(ccb->local, pta, L_INSERT);
998  ptaAddPt(pta, xs, ys); /* initial pixel */
999 
1000  w = pixGetWidth(pixs);
1001  h = pixGetHeight(pixs);
1002  data = pixGetData(pixs);
1003  wpl = pixGetWpl(pixs);
1004 
1005  /* Get the second point; there should always be at least 4 pts
1006  * in a minimal hole border! */
1007  if (findNextBorderPixel(w, h, data, wpl, xs, ys, &qpos, &npx, &npy))
1008  return ERROR_INT("isolated hole border point!", __func__, 1);
1009 
1010  spx = npx; /* save location of second pixel on border */
1011  spy = npy;
1012  ptaAddPt(pta, npx, npy); /* second pixel */
1013  px = npx;
1014  py = npy;
1015 
1016  while (1) {
1017  findNextBorderPixel(w, h, data, wpl, px, py, &qpos, &npx, &npy);
1018  if (px == fpx && py == fpy && npx == spx && npy == spy)
1019  break;
1020  ptaAddPt(pta, npx, npy);
1021  px = npx;
1022  py = npy;
1023  }
1024 
1025  return 0;
1026 }
1027 
1028 
1047 static l_int32
1049  l_int32 h,
1050  l_uint32 *data,
1051  l_int32 wpl,
1052  l_int32 px,
1053  l_int32 py,
1054  l_int32 *pqpos,
1055  l_int32 *pnpx,
1056  l_int32 *pnpy)
1057 {
1058 l_int32 qpos, i, pos, npx, npy, val;
1059 l_uint32 *line;
1060 
1061  qpos = *pqpos;
1062  for (i = 1; i < 8; i++) {
1063  pos = (qpos + i) % 8;
1064  npx = px + xpostab[pos];
1065  npy = py + ypostab[pos];
1066  if (npx < 0 || npx >= w || npy < 0 || npy >= h)
1067  continue;
1068  line = data + npy * wpl;
1069  val = GET_DATA_BIT(line, npx);
1070  if (val) {
1071  *pnpx = npx;
1072  *pnpy = npy;
1073  *pqpos = qpostab[pos];
1074  return 0;
1075  }
1076  }
1077 
1078  return 1;
1079 }
1080 
1081 
1100 static void
1102  l_int32 fpy,
1103  l_int32 spx,
1104  l_int32 spy,
1105  l_int32 *pxs,
1106  l_int32 *pys)
1107 {
1108 l_int32 dx, dy;
1109 
1110  dx = spx - fpx;
1111  dy = spy - fpy;
1112 
1113  if (dx * dy == 1) {
1114  *pxs = fpx + dx;
1115  *pys = fpy;
1116  } else if (dx * dy == -1) {
1117  *pxs = fpx;
1118  *pys = fpy + dy;
1119  } else if (dx == 0) {
1120  *pxs = fpx + dy;
1121  *pys = fpy + dy;
1122  } else /* dy == 0 */ {
1123  *pxs = fpx + dx;
1124  *pys = fpy - dx;
1125  }
1126 
1127  return;
1128 }
1129 
1130 
1131 
1132 /*---------------------------------------------------------------------*
1133  * Border conversions *
1134  *---------------------------------------------------------------------*/
1148 l_ok
1150 {
1151 l_int32 ncc, nb, n, i, j, k, xul, yul, x, y;
1152 CCBORD *ccb;
1153 PTAA *ptaal, *ptaag;
1154 PTA *ptal, *ptag;
1155 
1156  if (!ccba)
1157  return ERROR_INT("ccba not defined", __func__, 1);
1158 
1159  ncc = ccbaGetCount(ccba); /* number of c.c. */
1160  for (i = 0; i < ncc; i++) {
1161  ccb = ccbaGetCcb(ccba, i);
1162 
1163  /* Get the UL corner in global coords, (xul, yul), of the c.c. */
1164  boxaGetBoxGeometry(ccb->boxa, 0, &xul, &yul, NULL, NULL);
1165 
1166  /* Make a new global ptaa, removing any old one */
1167  ptaal = ccb->local;
1168  nb = ptaaGetCount(ptaal); /* number of borders */
1169  if (ccb->global) /* remove old one */
1170  ptaaDestroy(&ccb->global);
1171  if ((ptaag = ptaaCreate(nb)) == NULL) {
1172  ccbDestroy(&ccb);
1173  return ERROR_INT("ptaag not made", __func__, 1);
1174  }
1175  ccb->global = ptaag; /* save new one */
1176 
1177  /* Iterate through the borders for this c.c. */
1178  for (j = 0; j < nb; j++) {
1179  ptal = ptaaGetPta(ptaal, j, L_CLONE);
1180  n = ptaGetCount(ptal); /* number of pixels in border */
1181  ptag = ptaCreate(n);
1182  ptaaAddPta(ptaag, ptag, L_INSERT);
1183  for (k = 0; k < n; k++) {
1184  ptaGetIPt(ptal, k, &x, &y);
1185  ptaAddPt(ptag, x + xul, y + yul);
1186  }
1187  ptaDestroy(&ptal);
1188  }
1189  ccbDestroy(&ccb);
1190  }
1191 
1192  return 0;
1193 }
1194 
1195 
1218 l_ok
1220 {
1221 l_int32 ncc, nb, n, i, j, k;
1222 l_int32 px, py, cx, cy, stepdir;
1223 l_int32 dirtab[][3] = {{1, 2, 3}, {0, -1, 4}, {7, 6, 5}};
1224 CCBORD *ccb;
1225 NUMA *na;
1226 NUMAA *naa; /* step chain code; to be made */
1227 PTA *ptal;
1228 PTAA *ptaal; /* local chain code */
1229 
1230  if (!ccba)
1231  return ERROR_INT("ccba not defined", __func__, 1);
1232 
1233  ncc = ccbaGetCount(ccba); /* number of c.c. */
1234  for (i = 0; i < ncc; i++) {
1235  ccb = ccbaGetCcb(ccba, i);
1236 
1237  /* Make a new step numaa, removing any old one */
1238  ptaal = ccb->local;
1239  nb = ptaaGetCount(ptaal); /* number of borders */
1240  if (ccb->step) /* remove old one */
1241  numaaDestroy(&ccb->step);
1242  if ((naa = numaaCreate(nb)) == NULL) {
1243  ccbDestroy(&ccb);
1244  return ERROR_INT("naa not made", __func__, 1);
1245  }
1246  ccb->step = naa; /* save new one */
1247 
1248  /* Iterate through the borders for this c.c. */
1249  for (j = 0; j < nb; j++) {
1250  ptal = ptaaGetPta(ptaal, j, L_CLONE);
1251  n = ptaGetCount(ptal); /* number of pixels in border */
1252  if (n == 1) { /* isolated pixel */
1253  na = numaCreate(1); /* but leave it empty */
1254  } else { /* trace out the boundary */
1255  na = numaCreate(n);
1256  ptaGetIPt(ptal, 0, &px, &py);
1257  for (k = 1; k < n; k++) {
1258  ptaGetIPt(ptal, k, &cx, &cy);
1259  stepdir = dirtab[1 + cy - py][1 + cx - px];
1260  numaAddNumber(na, stepdir);
1261  px = cx;
1262  py = cy;
1263  }
1264  }
1265  numaaAddNuma(naa, na, L_INSERT);
1266  ptaDestroy(&ptal);
1267  }
1268  ccbDestroy(&ccb); /* just decrement refcount */
1269  }
1270 
1271  return 0;
1272 }
1273 
1274 
1291 l_ok
1293  l_int32 coordtype)
1294 {
1295 l_int32 ncc, nb, n, i, j, k;
1296 l_int32 xul, yul, xstart, ystart, x, y, stepdir;
1297 BOXA *boxa;
1298 CCBORD *ccb;
1299 NUMA *na;
1300 NUMAA *naa;
1301 PTAA *ptaan; /* new pix coord ptaa */
1302 PTA *ptas, *ptan;
1303 
1304  if (!ccba)
1305  return ERROR_INT("ccba not defined", __func__, 1);
1306  if (coordtype != CCB_GLOBAL_COORDS && coordtype != CCB_LOCAL_COORDS)
1307  return ERROR_INT("coordtype not valid", __func__, 1);
1308 
1309  ncc = ccbaGetCount(ccba); /* number of c.c. */
1310  for (i = 0; i < ncc; i++) {
1311  ccb = ccbaGetCcb(ccba, i);
1312  if ((naa = ccb->step) == NULL) {
1313  ccbDestroy(&ccb);
1314  return ERROR_INT("step numaa not found", __func__, 1);
1315  } if ((boxa = ccb->boxa) == NULL) {
1316  ccbDestroy(&ccb);
1317  return ERROR_INT("boxa not found", __func__, 1);
1318  } if ((ptas = ccb->start) == NULL) {
1319  ccbDestroy(&ccb);
1320  return ERROR_INT("start pta not found", __func__, 1);
1321  }
1322 
1323  /* For global coords, get the (xul, yul) of the c.c.;
1324  * otherwise, use relative coords. */
1325  if (coordtype == CCB_LOCAL_COORDS) {
1326  xul = 0;
1327  yul = 0;
1328  } else { /* coordtype == CCB_GLOBAL_COORDS */
1329  /* Get UL corner in global coords */
1330  if (boxaGetBoxGeometry(boxa, 0, &xul, &yul, NULL, NULL)) {
1331  ccbDestroy(&ccb);
1332  return ERROR_INT("bounding rectangle not found", __func__, 1);
1333  }
1334  }
1335 
1336  /* Make a new ptaa, removing any old one */
1337  nb = numaaGetCount(naa); /* number of borders */
1338  if ((ptaan = ptaaCreate(nb)) == NULL) {
1339  ccbDestroy(&ccb);
1340  return ERROR_INT("ptaan not made", __func__, 1);
1341  }
1342  if (coordtype == CCB_LOCAL_COORDS) {
1343  if (ccb->local) /* remove old one */
1344  ptaaDestroy(&ccb->local);
1345  ccb->local = ptaan; /* save new local chain */
1346  } else { /* coordtype == CCB_GLOBAL_COORDS */
1347  if (ccb->global) /* remove old one */
1348  ptaaDestroy(&ccb->global);
1349  ccb->global = ptaan; /* save new global chain */
1350  }
1351 
1352  /* Iterate through the borders for this c.c. */
1353  for (j = 0; j < nb; j++) {
1354  na = numaaGetNuma(naa, j, L_CLONE);
1355  n = numaGetCount(na); /* number of steps in border */
1356  if ((ptan = ptaCreate(n + 1)) == NULL) {
1357  ccbDestroy(&ccb);
1358  numaDestroy(&na);
1359  return ERROR_INT("ptan not made", __func__, 1);
1360  }
1361  ptaaAddPta(ptaan, ptan, L_INSERT);
1362  ptaGetIPt(ptas, j, &xstart, &ystart);
1363  x = xul + xstart;
1364  y = yul + ystart;
1365  ptaAddPt(ptan, x, y);
1366  for (k = 0; k < n; k++) {
1367  numaGetIValue(na, k, &stepdir);
1368  x += xpostab[stepdir];
1369  y += ypostab[stepdir];
1370  ptaAddPt(ptan, x, y);
1371  }
1372  numaDestroy(&na);
1373  }
1374  ccbDestroy(&ccb);
1375  }
1376 
1377  return 0;
1378 }
1379 
1380 
1400 l_ok
1402  l_int32 ptsflag)
1403 {
1404 l_int32 ncc, npt, i, j, xul, yul, x, y, delx, dely;
1405 l_int32 xp, yp, delxp, delyp; /* prev point and increments */
1406 CCBORD *ccb;
1407 PTA *ptal, *ptag;
1408 
1409  if (!ccba)
1410  return ERROR_INT("ccba not defined", __func__, 1);
1411 
1412  /* Make sure we have a local single path representation */
1413  if ((ccb = ccbaGetCcb(ccba, 0)) == NULL)
1414  return ERROR_INT("no ccb", __func__, 1);
1415  if (!ccb->splocal)
1416  ccbaGenerateSinglePath(ccba);
1417  ccbDestroy(&ccb); /* clone ref */
1418 
1419  ncc = ccbaGetCount(ccba); /* number of c.c. */
1420  for (i = 0; i < ncc; i++) {
1421  ccb = ccbaGetCcb(ccba, i);
1422 
1423  /* Get the UL corner in global coords, (xul, yul), of the c.c. */
1424  if (boxaGetBoxGeometry(ccb->boxa, 0, &xul, &yul, NULL, NULL)) {
1425  ccbDestroy(&ccb);
1426  return ERROR_INT("bounding rectangle not found", __func__, 1);
1427  }
1428 
1429  /* Make a new spglobal pta, removing any old one */
1430  ptal = ccb->splocal;
1431  npt = ptaGetCount(ptal); /* number of points */
1432  if (ccb->spglobal) /* remove old one */
1433  ptaDestroy(&ccb->spglobal);
1434  if ((ptag = ptaCreate(npt)) == NULL) {
1435  ccbDestroy(&ccb);
1436  return ERROR_INT("ptag not made", __func__, 1);
1437  }
1438  ccb->spglobal = ptag; /* save new one */
1439 
1440  /* Convert local to global */
1441  if (ptsflag == CCB_SAVE_ALL_PTS) {
1442  for (j = 0; j < npt; j++) {
1443  ptaGetIPt(ptal, j, &x, &y);
1444  ptaAddPt(ptag, x + xul, y + yul);
1445  }
1446  } else { /* ptsflag = CCB_SAVE_TURNING_PTS */
1447  ptaGetIPt(ptal, 0, &xp, &yp); /* get the 1st pt */
1448  ptaAddPt(ptag, xp + xul, yp + yul); /* save the 1st pt */
1449  if (npt == 2) { /* get and save the 2nd pt */
1450  ptaGetIPt(ptal, 1, &x, &y);
1451  ptaAddPt(ptag, x + xul, y + yul);
1452  } else if (npt > 2) {
1453  ptaGetIPt(ptal, 1, &x, &y);
1454  delxp = x - xp;
1455  delyp = y - yp;
1456  xp = x;
1457  yp = y;
1458  for (j = 2; j < npt; j++) {
1459  ptaGetIPt(ptal, j, &x, &y);
1460  delx = x - xp;
1461  dely = y - yp;
1462  if (delx != delxp || dely != delyp)
1463  ptaAddPt(ptag, xp + xul, yp + yul);
1464  xp = x;
1465  yp = y;
1466  delxp = delx;
1467  delyp = dely;
1468  }
1469  ptaAddPt(ptag, xp + xul, yp + yul);
1470  }
1471  }
1472 
1473  ccbDestroy(&ccb); /* clone ref */
1474  }
1475 
1476  return 0;
1477 }
1478 
1479 
1480 
1481 /*---------------------------------------------------------------------*
1482  * Conversion to single path *
1483  *---------------------------------------------------------------------*/
1519 l_ok
1521 {
1522 l_int32 i, j, k, ncc, nb, ncut, npt, dir, len, state, lostholes;
1523 l_int32 x, y, xl, yl, xf, yf;
1524 BOX *boxinner;
1525 BOXA *boxa;
1526 CCBORD *ccb;
1527 PTA *pta, *ptac, *ptah;
1528 PTA *ptahc; /* cyclic permutation of hole border, with end pts at cut */
1529 PTA *ptas; /* output result: new single path for c.c. */
1530 PTA *ptaf; /* points on the hole borders that intersect with cuts */
1531 PTA *ptal; /* points on outer border that intersect with cuts */
1532 PTA *ptap, *ptarp; /* path and reverse path between borders */
1533 PTAA *ptaa;
1534 PTAA *ptaap; /* ptaa for all paths between borders */
1535 
1536  if (!ccba)
1537  return ERROR_INT("ccba not defined", __func__, 1);
1538 
1539  ncc = ccbaGetCount(ccba); /* number of c.c. */
1540  lostholes = 0;
1541  for (i = 0; i < ncc; i++) {
1542  ccb = ccbaGetCcb(ccba, i);
1543  if ((ptaa = ccb->local) == NULL) {
1544  L_WARNING("local pixel loc array not found\n", __func__);
1545  continue;
1546  }
1547  nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */
1548 
1549  /* Prepare the output pta */
1550  if (ccb->splocal)
1551  ptaDestroy(&ccb->splocal);
1552  ptas = ptaCreate(0);
1553  ccb->splocal = ptas;
1554 
1555  /* If no holes, just concat the outer border */
1556  pta = ptaaGetPta(ptaa, 0, L_CLONE);
1557  if (nb == 1 || nb > NMAX_HOLES + 1) {
1558  ptaJoin(ptas, pta, 0, -1);
1559  ptaDestroy(&pta); /* remove clone */
1560  ccbDestroy(&ccb); /* remove clone */
1561  continue;
1562  }
1563 
1564  /* Find the (nb - 1) cut paths that connect holes
1565  * with outer border */
1566  boxa = ccb->boxa;
1567  ptaap = ptaaCreate(nb - 1);
1568  ptaf = ptaCreate(nb - 1);
1569  ptal = ptaCreate(nb - 1);
1570  for (j = 1; j < nb; j++) {
1571  boxinner = boxaGetBox(boxa, j, L_CLONE);
1572 
1573  /* Find a short path and store it */
1574  ptac = getCutPathForHole(ccb->pix, pta, boxinner, &dir, &len);
1575  if (len == 0) { /* lost the hole */
1576  lostholes++;
1577 /* boxPrintStreamInfo(stderr, boxa->box[0]); */
1578  }
1579  ptaaAddPta(ptaap, ptac, L_INSERT);
1580 /* lept_stderr("dir = %d, length = %d\n", dir, len); */
1581 /* ptaWriteStream(stderr, ptac, 1); */
1582 
1583  /* Store the first and last points in the cut path,
1584  * which must be on a hole border and the outer
1585  * border, respectively */
1586  ncut = ptaGetCount(ptac);
1587  if (ncut == 0) { /* missed hole; neg coords won't match */
1588  ptaAddPt(ptaf, -1, -1);
1589  ptaAddPt(ptal, -1, -1);
1590  } else {
1591  ptaGetIPt(ptac, 0, &x, &y);
1592  ptaAddPt(ptaf, x, y);
1593  ptaGetIPt(ptac, ncut - 1, &x, &y);
1594  ptaAddPt(ptal, x, y);
1595  }
1596  boxDestroy(&boxinner);
1597  }
1598 
1599  /* Make a single path for the c.c. using these connections */
1600  npt = ptaGetCount(pta); /* outer border pts */
1601  for (k = 0; k < npt; k++) {
1602  ptaGetIPt(pta, k, &x, &y);
1603  if (k == 0) { /* if there is a cut at the first point,
1604  * we can wait until the end to take it */
1605  ptaAddPt(ptas, x, y);
1606  continue;
1607  }
1608  state = L_NOT_FOUND;
1609  for (j = 0; j < nb - 1; j++) { /* iterate over cut end pts */
1610  ptaGetIPt(ptal, j, &xl, &yl); /* cut point on outer border */
1611  if (x == xl && y == yl) { /* take this cut to the hole */
1612  state = L_FOUND;
1613  ptap = ptaaGetPta(ptaap, j, L_CLONE);
1614  ptarp = ptaReverse(ptap, 1);
1615  /* Cut point on hole border: */
1616  ptaGetIPt(ptaf, j, &xf, &yf);
1617  /* Hole border: */
1618  ptah = ptaaGetPta(ptaa, j + 1, L_CLONE);
1619  ptahc = ptaCyclicPerm(ptah, xf, yf);
1620 /* ptaWriteStream(stderr, ptahc, 1); */
1621  ptaJoin(ptas, ptarp, 0, -1);
1622  ptaJoin(ptas, ptahc, 0, -1);
1623  ptaJoin(ptas, ptap, 0, -1);
1624  ptaDestroy(&ptap);
1625  ptaDestroy(&ptarp);
1626  ptaDestroy(&ptah);
1627  ptaDestroy(&ptahc);
1628  break;
1629  }
1630  }
1631  if (state == L_NOT_FOUND)
1632  ptaAddPt(ptas, x, y);
1633  }
1634 
1635 /* ptaWriteStream(stderr, ptas, 1); */
1636  ptaaDestroy(&ptaap);
1637  ptaDestroy(&ptaf);
1638  ptaDestroy(&ptal);
1639  ptaDestroy(&pta); /* remove clone */
1640  ccbDestroy(&ccb); /* remove clone */
1641  }
1642 
1643  if (lostholes > 0)
1644  L_INFO("***** %d lost holes *****\n", __func__, lostholes);
1645  return 0;
1646 }
1647 
1648 
1675 static PTA *
1677  PTA *pta,
1678  BOX *boxinner,
1679  l_int32 *pdir,
1680  l_int32 *plen)
1681 {
1682 l_int32 w, h, nc, x, y, xl, yl, xmid, ymid;
1683 l_uint32 val;
1684 PTA *ptac;
1685 
1686  if (!pix)
1687  return (PTA *)ERROR_PTR("pix not defined", __func__, NULL);
1688  if (!pta)
1689  return (PTA *)ERROR_PTR("pta not defined", __func__, NULL);
1690  if (!boxinner)
1691  return (PTA *)ERROR_PTR("boxinner not defined", __func__, NULL);
1692 
1693  pixGetDimensions(pix, &w, &h, NULL);
1694  ptac = ptaCreate(4);
1695  xmid = boxinner->x + boxinner->w / 2;
1696  ymid = boxinner->y + boxinner->h / 2;
1697 
1698  /* try top first */
1699  for (y = ymid; y >= 0; y--) {
1700  pixGetPixel(pix, xmid, y, &val);
1701  if (val == 1) {
1702  ptaAddPt(ptac, xmid, y);
1703  break;
1704  }
1705  }
1706  for (y = y - 1; y >= 0; y--) {
1707  pixGetPixel(pix, xmid, y, &val);
1708  if (val == 1)
1709  ptaAddPt(ptac, xmid, y);
1710  else
1711  break;
1712  }
1713  nc = ptaGetCount(ptac);
1714  ptaGetIPt(ptac, nc - 1, &xl, &yl);
1715  if (ptaContainsPt(pta, xl, yl)) {
1716  *pdir = 1;
1717  *plen = nc;
1718  return ptac;
1719  }
1720 
1721  /* Next try bottom */
1722  ptaEmpty(ptac);
1723  for (y = ymid; y < h; y++) {
1724  pixGetPixel(pix, xmid, y, &val);
1725  if (val == 1) {
1726  ptaAddPt(ptac, xmid, y);
1727  break;
1728  }
1729  }
1730  for (y = y + 1; y < h; y++) {
1731  pixGetPixel(pix, xmid, y, &val);
1732  if (val == 1)
1733  ptaAddPt(ptac, xmid, y);
1734  else
1735  break;
1736  }
1737  nc = ptaGetCount(ptac);
1738  ptaGetIPt(ptac, nc - 1, &xl, &yl);
1739  if (ptaContainsPt(pta, xl, yl)) {
1740  *pdir = 3;
1741  *plen = nc;
1742  return ptac;
1743  }
1744 
1745  /* Next try left */
1746  ptaEmpty(ptac);
1747  for (x = xmid; x >= 0; x--) {
1748  pixGetPixel(pix, x, ymid, &val);
1749  if (val == 1) {
1750  ptaAddPt(ptac, x, ymid);
1751  break;
1752  }
1753  }
1754  for (x = x - 1; x >= 0; x--) {
1755  pixGetPixel(pix, x, ymid, &val);
1756  if (val == 1)
1757  ptaAddPt(ptac, x, ymid);
1758  else
1759  break;
1760  }
1761  nc = ptaGetCount(ptac);
1762  ptaGetIPt(ptac, nc - 1, &xl, &yl);
1763  if (ptaContainsPt(pta, xl, yl)) {
1764  *pdir = 0;
1765  *plen = nc;
1766  return ptac;
1767  }
1768 
1769  /* Finally try right */
1770  ptaEmpty(ptac);
1771  for (x = xmid; x < w; x++) {
1772  pixGetPixel(pix, x, ymid, &val);
1773  if (val == 1) {
1774  ptaAddPt(ptac, x, ymid);
1775  break;
1776  }
1777  }
1778  for (x = x + 1; x < w; x++) {
1779  pixGetPixel(pix, x, ymid, &val);
1780  if (val == 1)
1781  ptaAddPt(ptac, x, ymid);
1782  else
1783  break;
1784  }
1785  nc = ptaGetCount(ptac);
1786  ptaGetIPt(ptac, nc - 1, &xl, &yl);
1787  if (ptaContainsPt(pta, xl, yl)) {
1788  *pdir = 2;
1789  *plen = nc;
1790  return ptac;
1791  }
1792 
1793  /* Sometimes, there is nothing. */
1794  ptaEmpty(ptac);
1795  *plen = 0;
1796  return ptac;
1797 }
1798 
1799 
1800 
1801 /*---------------------------------------------------------------------*
1802  * Border rendering *
1803  *---------------------------------------------------------------------*/
1817 PIX *
1819 {
1820 l_int32 ncc, nb, n, i, j, k, x, y;
1821 CCBORD *ccb;
1822 PIX *pixd;
1823 PTAA *ptaa;
1824 PTA *pta;
1825 
1826  if (!ccba)
1827  return (PIX *)ERROR_PTR("ccba not defined", __func__, NULL);
1828 
1829  if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL)
1830  return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1831  ncc = ccbaGetCount(ccba); /* number of c.c. */
1832  for (i = 0; i < ncc; i++) {
1833  ccb = ccbaGetCcb(ccba, i);
1834  if ((ptaa = ccb->global) == NULL) {
1835  L_WARNING("global pixel loc array not found", __func__);
1836  ccbDestroy(&ccb);
1837  continue;
1838  }
1839  nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */
1840  for (j = 0; j < nb; j++) {
1841  pta = ptaaGetPta(ptaa, j, L_CLONE);
1842  n = ptaGetCount(pta); /* number of pixels in the border */
1843  for (k = 0; k < n; k++) {
1844  ptaGetIPt(pta, k, &x, &y);
1845  pixSetPixel(pixd, x, y, 1);
1846  }
1847  ptaDestroy(&pta);
1848  }
1849  ccbDestroy(&ccb);
1850  }
1851 
1852  return pixd;
1853 }
1854 
1855 
1869 PIX *
1871 {
1872 l_int32 ncc, npt, i, j, x, y;
1873 CCBORD *ccb;
1874 PIX *pixd;
1875 PTA *ptag;
1876 
1877  if (!ccba)
1878  return (PIX *)ERROR_PTR("ccba not defined", __func__, NULL);
1879 
1880  if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL)
1881  return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1882  ncc = ccbaGetCount(ccba); /* number of c.c. */
1883  for (i = 0; i < ncc; i++) {
1884  ccb = ccbaGetCcb(ccba, i);
1885  if ((ptag = ccb->spglobal) == NULL) {
1886  L_WARNING("spglobal pixel loc array not found\n", __func__);
1887  ccbDestroy(&ccb);
1888  continue;
1889  }
1890  npt = ptaGetCount(ptag); /* number of pixels on path */
1891  for (j = 0; j < npt; j++) {
1892  ptaGetIPt(ptag, j, &x, &y);
1893  pixSetPixel(pixd, x, y, 1);
1894  }
1895  ccbDestroy(&ccb); /* clone ref */
1896  }
1897 
1898  return pixd;
1899 }
1900 
1901 
1958 PIX *
1960 {
1961 l_int32 ncc, i, nb, n, j, k, x, y, xul, yul, xoff, yoff, w, h;
1962 l_int32 fpx, fpy, spx, spy, xs, ys;
1963 BOX *box;
1964 BOXA *boxa;
1965 CCBORD *ccb;
1966 PIX *pixd, *pixt, *pixh;
1967 PTAA *ptaa;
1968 PTA *pta;
1969 
1970  if (!ccba)
1971  return (PIX *)ERROR_PTR("ccba not defined", __func__, NULL);
1972 
1973  if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL)
1974  return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1975  ncc = ccbaGetCount(ccba);
1976  for (i = 0; i < ncc; i++) {
1977  ccb = ccbaGetCcb(ccba, i);
1978  if ((boxa = ccb->boxa) == NULL) {
1979  pixDestroy(&pixd);
1980  ccbDestroy(&ccb);
1981  return (PIX *)ERROR_PTR("boxa not found", __func__, NULL);
1982  }
1983 
1984  /* Render border in pixt */
1985  if ((ptaa = ccb->local) == NULL) {
1986  L_WARNING("local chain array not found\n", __func__);
1987  ccbDestroy(&ccb);
1988  continue;
1989  }
1990 
1991  nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */
1992  for (j = 0; j < nb; j++) {
1993  if ((box = boxaGetBox(boxa, j, L_CLONE)) == NULL) {
1994  pixDestroy(&pixd);
1995  ccbDestroy(&ccb);
1996  return (PIX *)ERROR_PTR("b. box not found", __func__, NULL);
1997  }
1998  if (j == 0) {
1999  boxGetGeometry(box, &xul, &yul, &w, &h);
2000  xoff = yoff = 0;
2001  } else {
2002  boxGetGeometry(box, &xoff, &yoff, &w, &h);
2003  }
2004  boxDestroy(&box);
2005 
2006  /* Render the border in a minimum-sized pix;
2007  * subtract xoff and yoff because the pixel
2008  * location is stored relative to the c.c., but
2009  * we need it relative to just the hole border. */
2010  if ((pixt = pixCreate(w, h, 1)) == NULL) {
2011  pixDestroy(&pixd);
2012  ccbDestroy(&ccb);
2013  return (PIX *)ERROR_PTR("pixt not made", __func__, NULL);
2014  }
2015  pta = ptaaGetPta(ptaa, j, L_CLONE);
2016  n = ptaGetCount(pta); /* number of pixels in the border */
2017  for (k = 0; k < n; k++) {
2018  ptaGetIPt(pta, k, &x, &y);
2019  pixSetPixel(pixt, x - xoff, y - yoff, 1);
2020  if (j > 0) { /* need this for finding hole border pixel */
2021  if (k == 0) {
2022  fpx = x - xoff;
2023  fpy = y - yoff;
2024  }
2025  if (k == 1) {
2026  spx = x - xoff;
2027  spy = y - yoff;
2028  }
2029  }
2030  }
2031  ptaDestroy(&pta);
2032 
2033  /* Get the filled component */
2034  if (j == 0) { /* if outer border, fill from outer boundary */
2035  if ((pixh = pixFillClosedBorders(pixt, 4)) == NULL) {
2036  pixDestroy(&pixd);
2037  pixDestroy(&pixt);
2038  ccbDestroy(&ccb);
2039  return (PIX *)ERROR_PTR("pixh not made", __func__, NULL);
2040  }
2041  } else { /* fill the hole from inside */
2042  /* get the location of a seed pixel in the hole */
2043  locateOutsideSeedPixel(fpx, fpy, spx, spy, &xs, &ys);
2044 
2045  /* Put seed in hole and fill interior of hole,
2046  * using pixt as clipping mask */
2047  pixh = pixCreateTemplate(pixt);
2048  pixSetPixel(pixh, xs, ys, 1); /* put seed pixel in hole */
2049  pixInvert(pixt, pixt); /* to make filling mask */
2050  pixSeedfillBinary(pixh, pixh, pixt, 4); /* 4-fill hole */
2051  }
2052 
2053  /* XOR into the dest */
2054  pixRasterop(pixd, xul + xoff, yul + yoff, w, h, PIX_XOR,
2055  pixh, 0, 0);
2056  pixDestroy(&pixt);
2057  pixDestroy(&pixh);
2058  }
2059  ccbDestroy(&ccb);
2060  }
2061  return pixd;
2062 }
2063 
2064 
2065 
2087 PIX *
2089 {
2090 l_int32 ncc, nb, n, i, j, k, x, y, xul, yul, w, h;
2091 l_int32 fpx, fpy, spx, spy, xs, ys;
2092 BOXA *boxa;
2093 CCBORD *ccb;
2094 PIX *pixd, *pixc, *pixs;
2095 PTAA *ptaa;
2096 PTA *pta;
2097 
2098  if (!ccba)
2099  return (PIX *)ERROR_PTR("ccba not defined", __func__, NULL);
2100 
2101  if ((pixd = pixCreate(ccba->w, ccba->h, 1)) == NULL)
2102  return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
2103  ncc = ccbaGetCount(ccba);
2104  for (i = 0; i < ncc; i++) {
2105  /* Generate clipping mask from border pixels and seed image
2106  * from one seed for each closed border. */
2107  ccb = ccbaGetCcb(ccba, i);
2108  if ((boxa = ccb->boxa) == NULL) {
2109  pixDestroy(&pixd);
2110  ccbDestroy(&ccb);
2111  return (PIX *)ERROR_PTR("boxa not found", __func__, NULL);
2112  }
2113  if (boxaGetBoxGeometry(boxa, 0, &xul, &yul, &w, &h)) {
2114  pixDestroy(&pixd);
2115  ccbDestroy(&ccb);
2116  return (PIX *)ERROR_PTR("b. box not found", __func__, NULL);
2117  }
2118  pixc = pixCreate(w + 2, h + 2, 1);
2119  pixs = pixCreateTemplate(pixc);
2120 
2121  if ((ptaa = ccb->local) == NULL) {
2122  pixDestroy(&pixc);
2123  pixDestroy(&pixs);
2124  ccbDestroy(&ccb);
2125  L_WARNING("local chain array not found\n", __func__);
2126  continue;
2127  }
2128  nb = ptaaGetCount(ptaa); /* number of borders in the c.c. */
2129  for (j = 0; j < nb; j++) {
2130  pta = ptaaGetPta(ptaa, j, L_CLONE);
2131  n = ptaGetCount(pta); /* number of pixels in the border */
2132 
2133  /* Render border pixels in pixc */
2134  for (k = 0; k < n; k++) {
2135  ptaGetIPt(pta, k, &x, &y);
2136  pixSetPixel(pixc, x + 1, y + 1, 1);
2137  if (k == 0) {
2138  fpx = x + 1;
2139  fpy = y + 1;
2140  } else if (k == 1) {
2141  spx = x + 1;
2142  spy = y + 1;
2143  }
2144  }
2145 
2146  /* Get and set seed pixel for this border in pixs */
2147  if (n > 1)
2148  locateOutsideSeedPixel(fpx, fpy, spx, spy, &xs, &ys);
2149  else /* isolated c.c. */
2150  xs = ys = 0;
2151  pixSetPixel(pixs, xs, ys, 1);
2152  ptaDestroy(&pta);
2153  }
2154 
2155  /* Fill from seeds in pixs, using pixc as the clipping mask,
2156  * to reconstruct the c.c. */
2157  pixInvert(pixc, pixc); /* to convert clipping -> filling mask */
2158  pixSeedfillBinary(pixs, pixs, pixc, 4); /* 4-fill */
2159  pixInvert(pixs, pixs); /* to make the c.c. */
2160 
2161  /* XOR into the dest */
2162  pixRasterop(pixd, xul, yul, w, h, PIX_XOR, pixs, 1, 1);
2163 
2164  pixDestroy(&pixc);
2165  pixDestroy(&pixs);
2166  ccbDestroy(&ccb); /* ref-counted */
2167  }
2168  return pixd;
2169 }
2170 
2171 
2172 /*---------------------------------------------------------------------*
2173  * Serialize for I/O *
2174  *---------------------------------------------------------------------*/
2182 l_ok
2183 ccbaWrite(const char *filename,
2184  CCBORDA *ccba)
2185 {
2186 FILE *fp;
2187 
2188  if (!filename)
2189  return ERROR_INT("filename not defined", __func__, 1);
2190  if (!ccba)
2191  return ERROR_INT("ccba not defined", __func__, 1);
2192 
2193  if ((fp = fopenWriteStream(filename, "wb+")) == NULL)
2194  return ERROR_INT("stream not opened", __func__, 1);
2195  if (ccbaWriteStream(fp, ccba)) {
2196  fclose(fp);
2197  return ERROR_INT("ccba not written to stream", __func__, 1);
2198  }
2199 
2200  fclose(fp);
2201  return 0;
2202 }
2203 
2204 
2205 
2232 l_ok
2234  CCBORDA *ccba)
2235 {
2236 char strbuf[256];
2237 l_uint8 bval;
2238 l_uint8 *datain, *dataout;
2239 l_int32 i, j, k, bx, by, bw, bh, val, startx, starty;
2240 l_int32 ncc, nb, n;
2241 l_uint32 w, h;
2242 size_t inbytes, outbytes;
2243 L_BBUFFER *bbuf;
2244 CCBORD *ccb;
2245 NUMA *na;
2246 NUMAA *naa;
2247 PTA *pta;
2248 
2249 #if !HAVE_LIBZ /* defined in environ.h */
2250  return ERROR_INT("no libz: can't write data", __func__, 1);
2251 #else
2252 
2253  if (!fp)
2254  return ERROR_INT("stream not open", __func__, 1);
2255  if (!ccba)
2256  return ERROR_INT("ccba not defined", __func__, 1);
2257 
2258  if ((bbuf = bbufferCreate(NULL, 1000)) == NULL)
2259  return ERROR_INT("bbuf not made", __func__, 1);
2260 
2261  ncc = ccbaGetCount(ccba);
2262  snprintf(strbuf, sizeof(strbuf), "ccba: %7d cc\n", ncc);
2263  bbufferRead(bbuf, (l_uint8 *)strbuf, 18);
2264  w = pixGetWidth(ccba->pix);
2265  h = pixGetHeight(ccba->pix);
2266  bbufferRead(bbuf, (l_uint8 *)&w, 4); /* width */
2267  bbufferRead(bbuf, (l_uint8 *)&h, 4); /* height */
2268  for (i = 0; i < ncc; i++) {
2269  ccb = ccbaGetCcb(ccba, i);
2270  if (boxaGetBoxGeometry(ccb->boxa, 0, &bx, &by, &bw, &bh)) {
2271  bbufferDestroy(&bbuf);
2272  ccbDestroy(&ccb);
2273  return ERROR_INT("bounding box not found", __func__, 1);
2274  }
2275  bbufferRead(bbuf, (l_uint8 *)&bx, 4); /* ulx of c.c. */
2276  bbufferRead(bbuf, (l_uint8 *)&by, 4); /* uly of c.c. */
2277  bbufferRead(bbuf, (l_uint8 *)&bw, 4); /* w of c.c. */
2278  bbufferRead(bbuf, (l_uint8 *)&bh, 4); /* h of c.c. */
2279  if ((naa = ccb->step) == NULL) {
2280  ccbaGenerateStepChains(ccba);
2281  naa = ccb->step;
2282  }
2283  nb = numaaGetCount(naa);
2284  bbufferRead(bbuf, (l_uint8 *)&nb, 4); /* number of borders in c.c. */
2285  pta = ccb->start;
2286  for (j = 0; j < nb; j++) {
2287  ptaGetIPt(pta, j, &startx, &starty);
2288  bbufferRead(bbuf, (l_uint8 *)&startx, 4); /* starting x in border */
2289  bbufferRead(bbuf, (l_uint8 *)&starty, 4); /* starting y in border */
2290  na = numaaGetNuma(naa, j, L_CLONE);
2291  n = numaGetCount(na);
2292  for (k = 0; k < n; k++) {
2293  numaGetIValue(na, k, &val);
2294  if (k % 2 == 0)
2295  bval = (l_uint8)val << 4;
2296  else
2297  bval |= (l_uint8)val;
2298  if (k % 2 == 1)
2299  bbufferRead(bbuf, (l_uint8 *)&bval, 1); /* 2 border steps */
2300  }
2301  if (n % 2 == 1) {
2302  bval |= 0x8;
2303  bbufferRead(bbuf, (l_uint8 *)&bval, 1); /* end with 0xz8, */
2304  /* where z = {0..7} */
2305  } else { /* n % 2 == 0 */
2306  bval = 0x88;
2307  bbufferRead(bbuf, (l_uint8 *)&bval, 1); /* end with 0x88 */
2308  }
2309  numaDestroy(&na);
2310  }
2311  ccbDestroy(&ccb);
2312  }
2313 
2314  datain = bbufferDestroyAndSaveData(&bbuf, &inbytes);
2315  dataout = zlibCompress(datain, inbytes, &outbytes);
2316  fwrite(dataout, 1, outbytes, fp);
2317 
2318  LEPT_FREE(datain);
2319  LEPT_FREE(dataout);
2320  return 0;
2321 
2322 #endif /* !HAVE_LIBZ */
2323 }
2324 
2325 
2332 CCBORDA *
2333 ccbaRead(const char *filename)
2334 {
2335 FILE *fp;
2336 CCBORDA *ccba;
2337 
2338  if (!filename)
2339  return (CCBORDA *)ERROR_PTR("filename not defined", __func__, NULL);
2340 
2341  if ((fp = fopenReadStream(filename)) == NULL)
2342  return (CCBORDA *)ERROR_PTR("stream not opened", __func__, NULL);
2343  ccba = ccbaReadStream(fp);
2344  fclose(fp);
2345 
2346  if (!ccba)
2347  return (CCBORDA *)ERROR_PTR("ccba not returned", __func__, NULL);
2348  return ccba;
2349 }
2350 
2351 
2376 CCBORDA *
2378 {
2379 char strbuf[256];
2380 l_uint8 bval;
2381 l_uint8 *datain, *dataout;
2382 l_int32 i, j, startx, starty;
2383 l_int32 offset, nib1, nib2;
2384 l_int32 ncc, nb;
2385 l_uint32 width, height, w, h, xoff, yoff;
2386 size_t inbytes, outbytes;
2387 BOX *box;
2388 CCBORD *ccb;
2389 CCBORDA *ccba;
2390 NUMA *na;
2391 NUMAA *step;
2392 
2393 #if !HAVE_LIBZ /* defined in environ.h */
2394  return (CCBORDA *)ERROR_PTR("no libz: can't read data", __func__, NULL);
2395 #else
2396 
2397  if (!fp)
2398  return (CCBORDA *)ERROR_PTR("stream not open", __func__, NULL);
2399 
2400  if ((datain = l_binaryReadStream(fp, &inbytes)) == NULL)
2401  return (CCBORDA *)ERROR_PTR("data not read from file", __func__, NULL);
2402  dataout = zlibUncompress(datain, inbytes, &outbytes);
2403  LEPT_FREE(datain);
2404  if (!dataout)
2405  return (CCBORDA *)ERROR_PTR("dataout not made", __func__, NULL);
2406 
2407  offset = 18;
2408  memcpy(strbuf, dataout, offset);
2409  strbuf[17] = '\0';
2410  if (memcmp(strbuf, "ccba:", 5) != 0) {
2411  LEPT_FREE(dataout);
2412  return (CCBORDA *)ERROR_PTR("file not type ccba", __func__, NULL);
2413  }
2414  sscanf(strbuf, "ccba: %7d cc\n", &ncc);
2415 /* lept_stderr("ncc = %d\n", ncc); */
2416  if ((ccba = ccbaCreate(NULL, ncc)) == NULL) {
2417  LEPT_FREE(dataout);
2418  return (CCBORDA *)ERROR_PTR("ccba not made", __func__, NULL);
2419  }
2420 
2421  memcpy(&width, dataout + offset, 4);
2422  offset += 4;
2423  memcpy(&height, dataout + offset, 4);
2424  offset += 4;
2425  ccba->w = width;
2426  ccba->h = height;
2427 /* lept_stderr("width = %d, height = %d\n", width, height); */
2428 
2429  for (i = 0; i < ncc; i++) { /* should be ncc */
2430  ccb = ccbCreate(NULL);
2431  ccbaAddCcb(ccba, ccb);
2432 
2433  memcpy(&xoff, dataout + offset, 4);
2434  offset += 4;
2435  memcpy(&yoff, dataout + offset, 4);
2436  offset += 4;
2437  memcpy(&w, dataout + offset, 4);
2438  offset += 4;
2439  memcpy(&h, dataout + offset, 4);
2440  offset += 4;
2441  box = boxCreate(xoff, yoff, w, h);
2442  boxaAddBox(ccb->boxa, box, L_INSERT);
2443 /* lept_stderr("xoff = %d, yoff = %d, w = %d, h = %d\n",
2444  xoff, yoff, w, h); */
2445 
2446  memcpy(&nb, dataout + offset, 4);
2447  offset += 4;
2448 /* lept_stderr("num borders = %d\n", nb); */
2449  step = numaaCreate(nb);
2450  ccb->step = step;
2451 
2452  for (j = 0; j < nb; j++) { /* should be nb */
2453  memcpy(&startx, dataout + offset, 4);
2454  offset += 4;
2455  memcpy(&starty, dataout + offset, 4);
2456  offset += 4;
2457  ptaAddPt(ccb->start, startx, starty);
2458 /* lept_stderr("startx = %d, starty = %d\n", startx, starty); */
2459  na = numaCreate(0);
2460  numaaAddNuma(step, na, L_INSERT);
2461 
2462  while(1) {
2463  bval = *(dataout + offset);
2464  offset++;
2465  nib1 = (bval >> 4);
2466  nib2 = bval & 0xf;
2467  if (nib1 != 8)
2468  numaAddNumber(na, nib1);
2469  else
2470  break;
2471  if (nib2 != 8)
2472  numaAddNumber(na, nib2);
2473  else
2474  break;
2475  }
2476  }
2477  }
2478  LEPT_FREE(dataout);
2479  return ccba;
2480 
2481 #endif /* !HAVE_LIBZ */
2482 }
2483 
2484 
2485 /*---------------------------------------------------------------------*
2486  * SVG Output *
2487  *---------------------------------------------------------------------*/
2495 l_ok
2496 ccbaWriteSVG(const char *filename,
2497  CCBORDA *ccba)
2498 {
2499 char *svgstr;
2500 
2501  if (!filename)
2502  return ERROR_INT("filename not defined", __func__, 1);
2503  if (!ccba)
2504  return ERROR_INT("ccba not defined", __func__, 1);
2505 
2506  if ((svgstr = ccbaWriteSVGString(ccba)) == NULL)
2507  return ERROR_INT("svgstr not made", __func__, 1);
2508 
2509  l_binaryWrite(filename, "w", svgstr, strlen(svgstr));
2510  LEPT_FREE(svgstr);
2511 
2512  return 0;
2513 }
2514 
2515 
2523 char *
2525 {
2526 char *svgstr;
2527 char smallbuf[256];
2528 char line0[] = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>";
2529 char line1[] = "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20000303 Stylable//EN\" \"http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd\">";
2530 char line2[] = "<svg>";
2531 char line3[] = "<polygon style=\"stroke-width:1;stroke:black;\" points=\"";
2532 char line4[] = "\" />";
2533 char line5[] = "</svg>";
2534 char space[] = " ";
2535 l_int32 i, j, ncc, npt, x, y;
2536 CCBORD *ccb;
2537 PTA *pta;
2538 SARRAY *sa;
2539 
2540  if (!ccba)
2541  return (char *)ERROR_PTR("ccba not defined", __func__, NULL);
2542 
2543  sa = sarrayCreate(0);
2544  sarrayAddString(sa, line0, L_COPY);
2545  sarrayAddString(sa, line1, L_COPY);
2546  sarrayAddString(sa, line2, L_COPY);
2547  ncc = ccbaGetCount(ccba);
2548  for (i = 0; i < ncc; i++) {
2549  if ((ccb = ccbaGetCcb(ccba, i)) == NULL) {
2550  sarrayDestroy(&sa);
2551  return (char *)ERROR_PTR("ccb not found", __func__, NULL);
2552  }
2553  if ((pta = ccb->spglobal) == NULL) {
2554  sarrayDestroy(&sa);
2555  ccbDestroy(&ccb);
2556  return (char *)ERROR_PTR("spglobal not made", __func__, NULL);
2557  }
2558  sarrayAddString(sa, line3, L_COPY);
2559  npt = ptaGetCount(pta);
2560  for (j = 0; j < npt; j++) {
2561  ptaGetIPt(pta, j, &x, &y);
2562  snprintf(smallbuf, sizeof(smallbuf), "%0d,%0d", x, y);
2563  sarrayAddString(sa, smallbuf, L_COPY);
2564  }
2565  sarrayAddString(sa, line4, L_COPY);
2566  ccbDestroy(&ccb);
2567  }
2568  sarrayAddString(sa, line5, L_COPY);
2569  sarrayAddString(sa, space, L_COPY);
2570 
2571  svgstr = sarrayToString(sa, 1);
2572 /* lept_stderr("%s", svgstr); */
2573 
2574  sarrayDestroy(&sa);
2575  return svgstr;
2576 }
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
l_ok bbufferRead(L_BBUFFER *bb, l_uint8 *src, l_int32 nbytes)
bbufferRead()
Definition: bbuffer.c:259
L_BBUFFER * bbufferCreate(const l_uint8 *indata, l_int32 nalloc)
bbufferCreate()
Definition: bbuffer.c:130
l_uint8 * bbufferDestroyAndSaveData(L_BBUFFER **pbb, size_t *pnbytes)
bbufferDestroyAndSaveData()
Definition: bbuffer.c:202
void bbufferDestroy(L_BBUFFER **pbb)
bbufferDestroy()
Definition: bbuffer.c:170
l_ok boxGetGeometry(const BOX *box, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxGetGeometry()
Definition: boxbasic.c:301
void boxDestroy(BOX **pbox)
boxDestroy()
Definition: boxbasic.c:273
BOX * boxClone(BOX *box)
boxClone()
Definition: boxbasic.c:249
l_ok boxaGetBoxGeometry(BOXA *boxa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph)
boxaGetBoxGeometry()
Definition: boxbasic.c:796
l_ok boxaAddBox(BOXA *boxa, BOX *box, l_int32 copyflag)
boxaAddBox()
Definition: boxbasic.c:553
void boxaDestroy(BOXA **pboxa)
boxaDestroy()
Definition: boxbasic.c:519
l_int32 boxaGetCount(const BOXA *boxa)
boxaGetCount()
Definition: boxbasic.c:661
BOX * boxaGetBox(BOXA *boxa, l_int32 index, l_int32 accessflag)
boxaGetBox()
Definition: boxbasic.c:702
BOX * boxCreate(l_int32 x, l_int32 y, l_int32 w, l_int32 h)
boxCreate()
Definition: boxbasic.c:171
BOXA * boxaCreate(l_int32 n)
boxaCreate()
Definition: boxbasic.c:442
l_ok boxPrintStreamInfo(FILE *fp, BOX *box)
boxPrintStreamInfo()
Definition: boxbasic.c:2270
l_ok ccbaWriteSVG(const char *filename, CCBORDA *ccba)
ccbaWriteSVG()
Definition: ccbord.c:2496
CCBORDA * ccbaRead(const char *filename)
ccbaRead()
Definition: ccbord.c:2333
void ccbaDestroy(CCBORDA **pccba)
ccbaDestroy()
Definition: ccbord.c:350
PIX * ccbaDisplayBorder(CCBORDA *ccba)
ccbaDisplayBorder()
Definition: ccbord.c:1818
char * ccbaWriteSVGString(CCBORDA *ccba)
ccbaWriteSVGString()
Definition: ccbord.c:2524
CCBORDA * ccbaReadStream(FILE *fp)
ccbaReadStream()
Definition: ccbord.c:2377
l_ok pixGetOuterBorder(CCBORD *ccb, PIX *pixs, BOX *box)
pixGetOuterBorder()
Definition: ccbord.c:882
l_ok ccbaGenerateSinglePath(CCBORDA *ccba)
ccbaGenerateSinglePath()
Definition: ccbord.c:1520
PIX * ccbaDisplayImage2(CCBORDA *ccba)
ccbaDisplayImage2()
Definition: ccbord.c:2088
PIX * ccbaDisplayImage1(CCBORDA *ccba)
ccbaDisplayImage1()
Definition: ccbord.c:1959
static l_int32 findNextBorderPixel(l_int32 w, l_int32 h, l_uint32 *data, l_int32 wpl, l_int32 px, l_int32 py, l_int32 *pqpos, l_int32 *pnpx, l_int32 *pnpy)
findNextBorderPixel()
Definition: ccbord.c:1048
PIX * ccbaDisplaySPBorder(CCBORDA *ccba)
ccbaDisplaySPBorder()
Definition: ccbord.c:1870
static CCBORD * ccbaGetCcb(CCBORDA *ccba, l_int32 index)
ccbaGetCcb()
Definition: ccbord.c:533
l_ok ccbaWrite(const char *filename, CCBORDA *ccba)
ccbaWrite()
Definition: ccbord.c:2183
static l_int32 ccbaGetCount(CCBORDA *ccba)
ccbaGetCount()
Definition: ccbord.c:510
l_ok ccbaStepChainsToPixCoords(CCBORDA *ccba, l_int32 coordtype)
ccbaStepChainsToPixCoords()
Definition: ccbord.c:1292
l_ok ccbaGenerateGlobalLocs(CCBORDA *ccba)
ccbaGenerateGlobalLocs()
Definition: ccbord.c:1149
static CCBORDA * ccbaCreate(PIX *pixs, l_int32 n)
ccbaCreate()
Definition: ccbord.c:319
CCBORDA * pixGetAllCCBorders(PIX *pixs)
pixGetAllCCBorders()
Definition: ccbord.c:560
static PTA * pixGetOuterBorderPta(PIX *pixs, BOX *box)
pixGetOuterBorderPta()
Definition: ccbord.c:811
static CCBORD * pixGetCCBorders(PIX *pixs, BOX *box)
pixGetCCBorders()
Definition: ccbord.c:644
l_ok ccbaGenerateStepChains(CCBORDA *ccba)
ccbaGenerateStepChains()
Definition: ccbord.c:1219
static l_ok pixGetHoleBorder(CCBORD *ccb, PIX *pixs, BOX *box, l_int32 xs, l_int32 ys)
pixGetHoleBorder()
Definition: ccbord.c:968
static void ccbDestroy(CCBORD **pccb)
ccbDestroy()
Definition: ccbord.c:410
static l_int32 ccbaExtendArray(CCBORDA *ccba)
ccbaExtendArray()
Definition: ccbord.c:484
PTAA * pixGetOuterBordersPtaa(PIX *pixs)
pixGetOuterBordersPtaa()
Definition: ccbord.c:753
l_ok ccbaGenerateSPGlobalLocs(CCBORDA *ccba, l_int32 ptsflag)
ccbaGenerateSPGlobalLocs()
Definition: ccbord.c:1401
static l_ok ccbaAddCcb(CCBORDA *ccba, CCBORD *ccb)
ccbaAddCcb()
Definition: ccbord.c:456
l_ok ccbaWriteStream(FILE *fp, CCBORDA *ccba)
ccbaWriteStream()
Definition: ccbord.c:2233
static CCBORD * ccbCreate(PIX *pixs)
ccbCreate()
Definition: ccbord.c:379
static PTA * getCutPathForHole(PIX *pix, PTA *pta, BOX *boxinner, l_int32 *pdir, l_int32 *plen)
getCutPathForHole()
Definition: ccbord.c:1676
static void locateOutsideSeedPixel(l_int32 fpx, l_int32 fpy, l_int32 spx, l_int32 spy, l_int32 *pxs, l_int32 *pys)
locateOutsideSeedPixel()
Definition: ccbord.c:1101
BOXA * pixConnComp(PIX *pixs, PIXA **ppixa, l_int32 connectivity)
pixConnComp()
Definition: conncomp.c:152
l_int32 nextOnPixelInRaster(PIX *pixs, l_int32 xstart, l_int32 ystart, l_int32 *px, l_int32 *py)
nextOnPixelInRaster()
Definition: conncomp.c:450
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:460
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:193
l_int32 numaaGetCount(NUMAA *naa)
numaaGetCount()
Definition: numabasic.c:1516
NUMA * numaaGetNuma(NUMAA *naa, l_int32 index, l_int32 accessflag)
numaaGetNuma()
Definition: numabasic.c:1617
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:357
NUMAA * numaaCreate(l_int32 n)
numaaCreate()
Definition: numabasic.c:1302
l_int32 numaGetCount(NUMA *na)
numaGetCount()
Definition: numabasic.c:630
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:720
l_ok numaaAddNuma(NUMAA *naa, NUMA *na, l_int32 copyflag)
numaaAddNuma()
Definition: numabasic.c:1435
void numaaDestroy(NUMAA **pnaa)
numaaDestroy()
Definition: numabasic.c:1401
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1642
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:608
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1074
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:380
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:582
l_ok pixSetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 val)
pixSetPixel()
Definition: pix2.c:263
l_ok pixGetPixel(PIX *pix, l_int32 x, l_int32 y, l_uint32 *pval)
pixGetPixel()
Definition: pix2.c:192
PIX * pixAddBorder(PIX *pixs, l_int32 npix, l_uint32 val)
pixAddBorder()
Definition: pix2.c:1773
l_ok pixZero(PIX *pix, l_int32 *pempty)
pixZero()
Definition: pix3.c:1777
PIX * pixInvert(PIX *pixd, PIX *pixs)
pixInvert()
Definition: pix3.c:1481
@ L_COPY
Definition: pix.h:505
@ L_CLONE
Definition: pix.h:506
@ L_INSERT
Definition: pix.h:504
#define PIX_XOR
Definition: pix.h:454
void pixaDestroy(PIXA **ppixa)
pixaDestroy()
Definition: pixabasic.c:404
BOX * pixaGetBox(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetBox()
Definition: pixabasic.c:764
PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype)
pixaGetPix()
Definition: pixabasic.c:647
PTAA * ptaaCreate(l_int32 n)
ptaaCreate()
Definition: ptabasic.c:905
PTA * ptaaGetPta(PTAA *ptaa, l_int32 index, l_int32 accessflag)
ptaaGetPta()
Definition: ptabasic.c:1064
l_ok ptaEmpty(PTA *pta)
ptaEmpty()
Definition: ptabasic.c:308
l_ok ptaGetIPt(PTA *pta, l_int32 index, l_int32 *px, l_int32 *py)
ptaGetIPt()
Definition: ptabasic.c:527
l_ok ptaaAddPta(PTAA *ptaa, PTA *pta, l_int32 copyflag)
ptaaAddPta()
Definition: ptabasic.c:963
PTA * ptaClone(PTA *pta)
ptaClone()
Definition: ptabasic.c:286
l_int32 ptaaGetCount(PTAA *ptaa)
ptaaGetCount()
Definition: ptabasic.c:1046
l_ok ptaAddPt(PTA *pta, l_float32 x, l_float32 y)
ptaAddPt()
Definition: ptabasic.c:328
l_int32 ptaGetCount(PTA *pta)
ptaGetCount()
Definition: ptabasic.c:480
void ptaaDestroy(PTAA **pptaa)
ptaaDestroy()
Definition: ptabasic.c:930
PTA * ptaCreate(l_int32 n)
ptaCreate()
Definition: ptabasic.c:120
void ptaDestroy(PTA **ppta)
ptaDestroy()
Definition: ptabasic.c:191
l_int32 ptaContainsPt(PTA *pta, l_int32 x, l_int32 y)
ptaContainsPt()
Definition: ptafunc1.c:649
PTA * ptaCyclicPerm(PTA *ptas, l_int32 xs, l_int32 ys)
ptaCyclicPerm()
Definition: ptafunc1.c:324
l_ok ptaJoin(PTA *ptad, PTA *ptas, l_int32 istart, l_int32 iend)
ptaJoin()
Definition: ptafunc1.c:166
PTA * ptaReverse(PTA *ptas, l_int32 type)
ptaReverse()
Definition: ptafunc1.c:252
PTA * ptaTransform(PTA *ptas, l_int32 shiftx, l_int32 shifty, l_float32 scalex, l_float32 scaley)
ptaTransform()
Definition: ptafunc1.c:715
l_ok pixRasterop(PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy)
pixRasterop()
Definition: rop.c:204
SARRAY * sarrayCreate(l_int32 n)
sarrayCreate()
Definition: sarray1.c:169
void sarrayDestroy(SARRAY **psa)
sarrayDestroy()
Definition: sarray1.c:353
l_ok sarrayAddString(SARRAY *sa, const char *string, l_int32 copyflag)
sarrayAddString()
Definition: sarray1.c:435
char * sarrayToString(SARRAY *sa, l_int32 addnlflag)
sarrayToString()
Definition: sarray1.c:716
PIX * pixSeedfillBinary(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 connectivity)
pixSeedfillBinary()
Definition: seedfill.c:247
PIX * pixFillClosedBorders(PIX *pixs, l_int32 connectivity)
pixFillClosedBorders()
Definition: seedfill.c:652
PIX * pixHolesByFilling(PIX *pixs, l_int32 connectivity)
pixHolesByFilling()
Definition: seedfill.c:603
l_int32 y
Definition: pix_internal.h:258
l_int32 x
Definition: pix_internal.h:257
l_int32 w
Definition: pix_internal.h:259
l_int32 h
Definition: pix_internal.h:260
struct Ptaa * global
struct Pta * spglobal
struct Numaa * step
struct Pta * start
struct Boxa * boxa
struct Pix * pix
l_atomic refcount
struct Ptaa * local
struct Pta * splocal
l_int32 w
l_int32 n
l_int32 nalloc
struct CCBord ** ccb
l_int32 h
struct Pix * pix
void lept_stderr(const char *fmt,...)
lept_stderr()
Definition: utils1.c:306
l_uint8 * l_binaryReadStream(FILE *fp, size_t *pnbytes)
l_binaryReadStream()
Definition: utils2.c:1358
FILE * fopenWriteStream(const char *filename, const char *modestring)
fopenWriteStream()
Definition: utils2.c:1905
l_ok l_binaryWrite(const char *filename, const char *operation, const void *data, size_t nbytes)
l_binaryWrite()
Definition: utils2.c:1519
void * reallocNew(void **pindata, size_t oldsize, size_t newsize)
reallocNew()
Definition: utils2.c:1262
FILE * fopenReadStream(const char *filename)
fopenReadStream()
Definition: utils2.c:1864
l_uint8 * zlibUncompress(const l_uint8 *datain, size_t nin, size_t *pnout)
zlibUncompress()
Definition: zlibmem.c:193
l_uint8 * zlibCompress(const l_uint8 *datain, size_t nin, size_t *pnout)
zlibCompress()
Definition: zlibmem.c:92