/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *====================================================================*/ /*! * \file pixabasic.c *
 *
 *      Pixa creation, destruction, copying
 *           PIXA     *pixaCreate()
 *           PIXA     *pixaCreateFromPix()
 *           PIXA     *pixaCreateFromBoxa()
 *           PIXA     *pixaSplitPix()
 *           void      pixaDestroy()
 *           PIXA     *pixaCopy()
 *
 *      Pixa addition
 *           l_int32   pixaAddPix()
 *           l_int32   pixaAddBox()
 *           static l_int32   pixaExtendArray()
 *           l_int32   pixaExtendArrayToSize()
 *
 *      Pixa accessors
 *           l_int32   pixaGetCount()
 *           l_int32   pixaChangeRefcount()
 *           PIX      *pixaGetPix()
 *           l_int32   pixaGetPixDimensions()
 *           BOXA     *pixaGetBoxa()
 *           l_int32   pixaGetBoxaCount()
 *           BOX      *pixaGetBox()
 *           l_int32   pixaGetBoxGeometry()
 *           l_int32   pixaSetBoxa()
 *           PIX     **pixaGetPixArray()
 *           l_int32   pixaVerifyDepth()
 *           l_int32   pixaVerifyDimensions()
 *           l_int32   pixaIsFull()
 *           l_int32   pixaCountText()
 *           l_int32   pixaSetText()
 *           void   ***pixaGetLinePtrs()
 *
 *      Pixa output info
 *           l_int32   pixaWriteStreamInfo()
 *
 *      Pixa array modifiers
 *           l_int32   pixaReplacePix()
 *           l_int32   pixaInsertPix()
 *           l_int32   pixaRemovePix()
 *           l_int32   pixaRemovePixAndSave()
 *           l_int32   pixaRemoveSelected()
 *           l_int32   pixaInitFull()
 *           l_int32   pixaClear()
 *
 *      Pixa and Pixaa combination
 *           l_int32   pixaJoin()
 *           PIXA     *pixaInterleave()
 *           l_int32   pixaaJoin()
 *
 *      Pixaa creation, destruction
 *           PIXAA    *pixaaCreate()
 *           PIXAA    *pixaaCreateFromPixa()
 *           void      pixaaDestroy()
 *
 *      Pixaa addition
 *           l_int32   pixaaAddPixa()
 *           static l_int32   pixaaExtendArray()
 *           l_int32   pixaaAddPix()
 *           l_int32   pixaaAddBox()
 *
 *      Pixaa accessors
 *           l_int32   pixaaGetCount()
 *           PIXA     *pixaaGetPixa()
 *           BOXA     *pixaaGetBoxa()
 *           PIX      *pixaaGetPix()
 *           l_int32   pixaaVerifyDepth()
 *           l_int32   pixaaVerifyDimensions()
 *           l_int32   pixaaIsFull()
 *
 *      Pixaa array modifiers
 *           l_int32   pixaaInitFull()
 *           l_int32   pixaaReplacePixa()
 *           l_int32   pixaaClear()
 *           l_int32   pixaaTruncate()
 *
 *      Pixa serialized I/O  (requires png support)
 *           PIXA     *pixaRead()
 *           PIXA     *pixaReadStream()
 *           PIXA     *pixaReadMem()
 *           l_int32   pixaWriteDebug()
 *           l_int32   pixaWrite()
 *           l_int32   pixaWriteStream()
 *           l_int32   pixaWriteMem()
 *           PIXA     *pixaReadBoth()
 *
 *      Pixaa serialized I/O  (requires png support)
 *           PIXAA    *pixaaReadFromFiles()
 *           PIXAA    *pixaaRead()
 *           PIXAA    *pixaaReadStream()
 *           PIXAA    *pixaaReadMem()
 *           l_int32   pixaaWrite()
 *           l_int32   pixaaWriteStream()
 *           l_int32   pixaaWriteMem()
 *
 *
 *   Important note on reference counting:
 *     Reference counting for the Pixa is analogous to that for the Boxa.
 *     See pix.h for details.   pixaCopy() provides three possible modes
 *     of copy.  The basic rule is that however a Pixa is obtained
 *     (e.g., from pixaCreate*(), pixaCopy(), or a Pixaa accessor),
 *     it is necessary to call pixaDestroy() on it.
 * 
*/ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include #include "allheaders.h" /* Bounds on array sizes */ static const size_t MaxInitPtrArraySize = 100000; static const size_t MaxPixaPtrArraySize = 5000000; static const size_t MaxPixaaPtrArraySize = 1000000; static const size_t InitialPtrArraySize = 20; /*!< n'importe quoi */ /* Static functions */ static l_int32 pixaExtendArray(PIXA *pixa); static l_int32 pixaaExtendArray(PIXAA *paa); /*---------------------------------------------------------------------* * Pixa creation, destruction, copy * *---------------------------------------------------------------------*/ /*! * \brief pixaCreate() * * \param[in] n initial number of ptrs * \return pixa, or NULL on error * *
 * Notes:
 *      (1) This creates an empty boxa.
 * 
*/ PIXA * pixaCreate(l_int32 n) { PIXA *pixa; PROCNAME("pixaCreate"); if (n <= 0 || n > MaxInitPtrArraySize) n = InitialPtrArraySize; pixa = (PIXA *)LEPT_CALLOC(1, sizeof(PIXA)); pixa->n = 0; pixa->nalloc = n; pixa->refcount = 1; pixa->pix = (PIX **)LEPT_CALLOC(n, sizeof(PIX *)); pixa->boxa = boxaCreate(n); if (!pixa->pix || !pixa->boxa) { pixaDestroy(&pixa); return (PIXA *)ERROR_PTR("pix or boxa not made", procName, NULL); } return pixa; } /*! * \brief pixaCreateFromPix() * * \param[in] pixs with individual components on a lattice * \param[in] n number of components * \param[in] cellw width of each cell * \param[in] cellh height of each cell * \return pixa, or NULL on error * *
 * Notes:
 *      (1) For bpp = 1, we truncate each retrieved pix to the ON
 *          pixels, which we assume for now start at (0,0)
 * 
*/ PIXA * pixaCreateFromPix(PIX *pixs, l_int32 n, l_int32 cellw, l_int32 cellh) { l_int32 w, h, d, nw, nh, i, j, index; PIX *pix1, *pix2; PIXA *pixa; PROCNAME("pixaCreateFromPix"); if (!pixs) return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); if (n <= 0) return (PIXA *)ERROR_PTR("n must be > 0", procName, NULL); if ((pixa = pixaCreate(n)) == NULL) return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if ((pix1 = pixCreate(cellw, cellh, d)) == NULL) { pixaDestroy(&pixa); return (PIXA *)ERROR_PTR("pix1 not made", procName, NULL); } nw = (w + cellw - 1) / cellw; nh = (h + cellh - 1) / cellh; for (i = 0, index = 0; i < nh; i++) { for (j = 0; j < nw && index < n; j++, index++) { pixRasterop(pix1, 0, 0, cellw, cellh, PIX_SRC, pixs, j * cellw, i * cellh); if (d == 1 && !pixClipToForeground(pix1, &pix2, NULL)) pixaAddPix(pixa, pix2, L_INSERT); else pixaAddPix(pixa, pix1, L_COPY); } } pixDestroy(&pix1); return pixa; } /*! * \brief pixaCreateFromBoxa() * * \param[in] pixs * \param[in] boxa * \param[in] start first box to use * \param[in] num number of boxes; use 0 to go to the end * \param[out] pcropwarn [optional] TRUE if the boxa extent * is larger than pixs. * \return pixad, or NULL on error * *
 * Notes:
 *      (1) This simply extracts from pixs the region corresponding to each
 *          box in the boxa.  To extract all the regions, set both %start
 *          and %num to 0.
 *      (2) The 5th arg is optional.  If the extent of the boxa exceeds the
 *          size of the pixa, so that some boxes are either clipped
 *          or entirely outside the pix, a warning is returned as TRUE.
 *      (3) pixad will have only the properly clipped elements, and
 *          the internal boxa will be correct.
 * 
*/ PIXA * pixaCreateFromBoxa(PIX *pixs, BOXA *boxa, l_int32 start, l_int32 num, l_int32 *pcropwarn) { l_int32 i, n, end, w, h, wbox, hbox, cropwarn; BOX *box, *boxc; PIX *pixd; PIXA *pixad; PROCNAME("pixaCreateFromBoxa"); if (!pixs) return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); if (!boxa) return (PIXA *)ERROR_PTR("boxa not defined", procName, NULL); if (num < 0) return (PIXA *)ERROR_PTR("num must be >= 0", procName, NULL); n = boxaGetCount(boxa); end = (num == 0) ? n - 1 : L_MIN(start + num - 1, n - 1); if ((pixad = pixaCreate(end - start + 1)) == NULL) return (PIXA *)ERROR_PTR("pixad not made", procName, NULL); boxaGetExtent(boxa, &wbox, &hbox, NULL); pixGetDimensions(pixs, &w, &h, NULL); cropwarn = FALSE; if (wbox > w || hbox > h) cropwarn = TRUE; if (pcropwarn) *pcropwarn = cropwarn; for (i = start; i <= end; i++) { box = boxaGetBox(boxa, i, L_COPY); if (cropwarn) { /* if box is outside pixs, pixd is NULL */ pixd = pixClipRectangle(pixs, box, &boxc); /* may be NULL */ if (pixd) { pixaAddPix(pixad, pixd, L_INSERT); pixaAddBox(pixad, boxc, L_INSERT); } boxDestroy(&box); } else { pixd = pixClipRectangle(pixs, box, NULL); pixaAddPix(pixad, pixd, L_INSERT); pixaAddBox(pixad, box, L_INSERT); } } return pixad; } /*! * \brief pixaSplitPix() * * \param[in] pixs with individual components on a lattice * \param[in] nx number of mosaic cells horizontally * \param[in] ny number of mosaic cells vertically * \param[in] borderwidth of added border on all sides * \param[in] bordercolor in our RGBA format: 0xrrggbbaa * \return pixa, or NULL on error * *
 * Notes:
 *      (1) This is a variant on pixaCreateFromPix(), where we
 *          simply divide the image up into (approximately) equal
 *          subunits.  If you want the subimages to have essentially
 *          the same aspect ratio as the input pix, use nx = ny.
 *      (2) If borderwidth is 0, we ignore the input bordercolor and
 *          redefine it to white.
 *      (3) The bordercolor is always used to initialize each tiled pix,
 *          so that if the src is clipped, the unblitted part will
 *          be this color.  This avoids 1 pixel wide black stripes at the
 *          left and lower edges.
 * 
*/ PIXA * pixaSplitPix(PIX *pixs, l_int32 nx, l_int32 ny, l_int32 borderwidth, l_uint32 bordercolor) { l_int32 w, h, d, cellw, cellh, i, j; PIX *pix1; PIXA *pixa; PROCNAME("pixaSplitPix"); if (!pixs) return (PIXA *)ERROR_PTR("pixs not defined", procName, NULL); if (nx <= 0 || ny <= 0) return (PIXA *)ERROR_PTR("nx and ny must be > 0", procName, NULL); borderwidth = L_MAX(0, borderwidth); if ((pixa = pixaCreate(nx * ny)) == NULL) return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); cellw = (w + nx - 1) / nx; /* round up */ cellh = (h + ny - 1) / ny; for (i = 0; i < ny; i++) { for (j = 0; j < nx; j++) { if ((pix1 = pixCreate(cellw + 2 * borderwidth, cellh + 2 * borderwidth, d)) == NULL) { pixaDestroy(&pixa); return (PIXA *)ERROR_PTR("pix1 not made", procName, NULL); } pixCopyColormap(pix1, pixs); if (borderwidth == 0) { /* initialize full image to white */ if (d == 1) pixClearAll(pix1); else pixSetAll(pix1); } else { pixSetAllArbitrary(pix1, bordercolor); } pixRasterop(pix1, borderwidth, borderwidth, cellw, cellh, PIX_SRC, pixs, j * cellw, i * cellh); pixaAddPix(pixa, pix1, L_INSERT); } } return pixa; } /*! * \brief pixaDestroy() * * \param[in,out] ppixa use ptr address so it will be nulled * *
 * Notes:
 *      (1) Decrements the ref count and, if 0, destroys the pixa.
 *      (2) Always nulls the input ptr.
 * 
*/ void pixaDestroy(PIXA **ppixa) { l_int32 i; PIXA *pixa; PROCNAME("pixaDestroy"); if (ppixa == NULL) { L_WARNING("ptr address is NULL!\n", procName); return; } if ((pixa = *ppixa) == NULL) return; /* Decrement the refcount. If it is 0, destroy the pixa. */ pixaChangeRefcount(pixa, -1); if (pixa->refcount <= 0) { for (i = 0; i < pixa->n; i++) pixDestroy(&pixa->pix[i]); LEPT_FREE(pixa->pix); boxaDestroy(&pixa->boxa); LEPT_FREE(pixa); } *ppixa = NULL; } /*! * \brief pixaCopy() * * \param[in] pixa * \param[in] copyflag see pix.h for details: * L_COPY makes a new pixa and copies each pix and each box; * L_CLONE gives a new ref-counted handle to the input pixa; * L_COPY_CLONE makes a new pixa and inserts clones of * all pix and boxes * \return new pixa, or NULL on error */ PIXA * pixaCopy(PIXA *pixa, l_int32 copyflag) { l_int32 i, nb; BOX *boxc; PIX *pixc; PIXA *pixac; PROCNAME("pixaCopy"); if (!pixa) return (PIXA *)ERROR_PTR("pixa not defined", procName, NULL); if (copyflag == L_CLONE) { pixaChangeRefcount(pixa, 1); return pixa; } if (copyflag != L_COPY && copyflag != L_COPY_CLONE) return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); if ((pixac = pixaCreate(pixa->n)) == NULL) return (PIXA *)ERROR_PTR("pixac not made", procName, NULL); nb = pixaGetBoxaCount(pixa); for (i = 0; i < pixa->n; i++) { if (copyflag == L_COPY) { pixc = pixaGetPix(pixa, i, L_COPY); if (i < nb) boxc = pixaGetBox(pixa, i, L_COPY); } else { /* copy-clone */ pixc = pixaGetPix(pixa, i, L_CLONE); if (i < nb) boxc = pixaGetBox(pixa, i, L_CLONE); } pixaAddPix(pixac, pixc, L_INSERT); if (i < nb) pixaAddBox(pixac, boxc, L_INSERT); } return pixac; } /*---------------------------------------------------------------------* * Pixa addition * *---------------------------------------------------------------------*/ /*! * \brief pixaAddPix() * * \param[in] pixa * \param[in] pix to be added * \param[in] copyflag L_INSERT, L_COPY, L_CLONE * \return 0 if OK; 1 on error */ l_ok pixaAddPix(PIXA *pixa, PIX *pix, l_int32 copyflag) { l_int32 n; PIX *pixc; PROCNAME("pixaAddPix"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); if (copyflag == L_INSERT) pixc = pix; else if (copyflag == L_COPY) pixc = pixCopy(NULL, pix); else if (copyflag == L_CLONE) pixc = pixClone(pix); else return ERROR_INT("invalid copyflag", procName, 1); if (!pixc) return ERROR_INT("pixc not made", procName, 1); n = pixaGetCount(pixa); if (n >= pixa->nalloc) { if (pixaExtendArray(pixa)) { if (copyflag != L_INSERT) pixDestroy(&pixc); return ERROR_INT("extension failed", procName, 1); } } pixa->pix[n] = pixc; pixa->n++; return 0; } /*! * \brief pixaAddBox() * * \param[in] pixa * \param[in] box * \param[in] copyflag L_INSERT, L_COPY, L_CLONE * \return 0 if OK, 1 on error */ l_ok pixaAddBox(PIXA *pixa, BOX *box, l_int32 copyflag) { PROCNAME("pixaAddBox"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (!box) return ERROR_INT("box not defined", procName, 1); if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE) return ERROR_INT("invalid copyflag", procName, 1); boxaAddBox(pixa->boxa, box, copyflag); return 0; } /*! * \brief pixaExtendArray() * * \param[in] pixa * \return 0 if OK; 1 on error * *
 * Notes:
 *      (1) Doubles the size of the pixa and boxa ptr arrays.
 *      (2) The max number of pix in the array is 5 million.
 * 
*/ static l_int32 pixaExtendArray(PIXA *pixa) { PROCNAME("pixaExtendArray"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); return pixaExtendArrayToSize(pixa, 2 * pixa->nalloc); } /*! * \brief pixaExtendArrayToSize() * * \param[in] pixa * \param[in] size number of pix ptrs in new array * \return 0 if OK; 1 on error * *
 * Notes:
 *      (1) If necessary, reallocs new pixa and boxa ptrs arrays to %size.
 *          The pixa and boxa ptr arrays must always be equal in size.
 *      (2) The max number of pix ptrs is 5M.
 * 
*/ l_ok pixaExtendArrayToSize(PIXA *pixa, size_t size) { size_t oldsize, newsize; PROCNAME("pixaExtendArrayToSize"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (pixa->nalloc > MaxPixaPtrArraySize) /* belt & suspenders */ return ERROR_INT("pixa has too many ptrs", procName, 1); if (size > MaxPixaPtrArraySize) return ERROR_INT("size > 5M ptrs; too large", procName, 1); if (size <= pixa->nalloc) { L_INFO("size too small; no extension\n", procName); return 0; } oldsize = pixa->nalloc * sizeof(PIX *); newsize = size * sizeof(PIX *); if ((pixa->pix = (PIX **)reallocNew((void **)&pixa->pix, oldsize, newsize)) == NULL) return ERROR_INT("new ptr array not returned", procName, 1); pixa->nalloc = size; return boxaExtendArrayToSize(pixa->boxa, size); } /*---------------------------------------------------------------------* * Pixa accessors * *---------------------------------------------------------------------*/ /*! * \brief pixaGetCount() * * \param[in] pixa * \return count, or 0 if no pixa */ l_int32 pixaGetCount(PIXA *pixa) { PROCNAME("pixaGetCount"); if (!pixa) return ERROR_INT("pixa not defined", procName, 0); return pixa->n; } /*! * \brief pixaChangeRefcount() * * \param[in] pixa * \param[in] delta * \return 0 if OK, 1 on error */ l_ok pixaChangeRefcount(PIXA *pixa, l_int32 delta) { PROCNAME("pixaChangeRefcount"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); pixa->refcount += delta; return 0; } /*! * \brief pixaGetPix() * * \param[in] pixa * \param[in] index to the index-th pix * \param[in] accesstype L_COPY or L_CLONE * \return pix, or NULL on error */ PIX * pixaGetPix(PIXA *pixa, l_int32 index, l_int32 accesstype) { PIX *pix; PROCNAME("pixaGetPix"); if (!pixa) return (PIX *)ERROR_PTR("pixa not defined", procName, NULL); if (index < 0 || index >= pixa->n) return (PIX *)ERROR_PTR("index not valid", procName, NULL); if ((pix = pixa->pix[index]) == NULL) { L_ERROR("no pix at pixa[%d]\n", procName, index); return (PIX *)ERROR_PTR("pix not found!", procName, NULL); } if (accesstype == L_COPY) return pixCopy(NULL, pix); else if (accesstype == L_CLONE) return pixClone(pix); else return (PIX *)ERROR_PTR("invalid accesstype", procName, NULL); } /*! * \brief pixaGetPixDimensions() * * \param[in] pixa * \param[in] index to the index-th box * \param[out] pw, ph, pd [optional] each can be null * \return 0 if OK, 1 on error */ l_ok pixaGetPixDimensions(PIXA *pixa, l_int32 index, l_int32 *pw, l_int32 *ph, l_int32 *pd) { PIX *pix; PROCNAME("pixaGetPixDimensions"); if (pw) *pw = 0; if (ph) *ph = 0; if (pd) *pd = 0; if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (index < 0 || index >= pixa->n) return ERROR_INT("index not valid", procName, 1); if ((pix = pixaGetPix(pixa, index, L_CLONE)) == NULL) return ERROR_INT("pix not found!", procName, 1); pixGetDimensions(pix, pw, ph, pd); pixDestroy(&pix); return 0; } /*! * \brief pixaGetBoxa() * * \param[in] pixa * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE * \return boxa, or NULL on error */ BOXA * pixaGetBoxa(PIXA *pixa, l_int32 accesstype) { PROCNAME("pixaGetBoxa"); if (!pixa) return (BOXA *)ERROR_PTR("pixa not defined", procName, NULL); if (!pixa->boxa) return (BOXA *)ERROR_PTR("boxa not defined", procName, NULL); if (accesstype != L_COPY && accesstype != L_CLONE && accesstype != L_COPY_CLONE) return (BOXA *)ERROR_PTR("invalid accesstype", procName, NULL); return boxaCopy(pixa->boxa, accesstype); } /*! * \brief pixaGetBoxaCount() * * \param[in] pixa * \return count, or 0 on error */ l_int32 pixaGetBoxaCount(PIXA *pixa) { PROCNAME("pixaGetBoxaCount"); if (!pixa) return ERROR_INT("pixa not defined", procName, 0); return boxaGetCount(pixa->boxa); } /*! * \brief pixaGetBox() * * \param[in] pixa * \param[in] index to the index-th pix * \param[in] accesstype L_COPY or L_CLONE * \return box if null, not automatically an error, or NULL on error * *
 * Notes:
 *      (1) There is always a boxa with a pixa, and it is initialized so
 *          that each box ptr is NULL.
 *      (2) In general, we expect that there is either a box associated
 *          with each pix, or no boxes at all in the boxa.
 *      (3) Having no boxes is thus not an automatic error.  Whether it
 *          is an actual error is determined by the calling program.
 *          If the caller expects to get a box, it is an error; see, e.g.,
 *          pixaGetBoxGeometry().
 * 
*/ BOX * pixaGetBox(PIXA *pixa, l_int32 index, l_int32 accesstype) { BOX *box; PROCNAME("pixaGetBox"); if (!pixa) return (BOX *)ERROR_PTR("pixa not defined", procName, NULL); if (!pixa->boxa) return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); if (index < 0 || index >= pixa->boxa->n) return (BOX *)ERROR_PTR("index not valid", procName, NULL); if (accesstype != L_COPY && accesstype != L_CLONE) return (BOX *)ERROR_PTR("invalid accesstype", procName, NULL); box = pixa->boxa->box[index]; if (box) { if (accesstype == L_COPY) return boxCopy(box); else /* accesstype == L_CLONE */ return boxClone(box); } else { return NULL; } } /*! * \brief pixaGetBoxGeometry() * * \param[in] pixa * \param[in] index to the index-th box * \param[out] px, py, pw, ph [optional] each can be null * \return 0 if OK, 1 on error */ l_ok pixaGetBoxGeometry(PIXA *pixa, l_int32 index, l_int32 *px, l_int32 *py, l_int32 *pw, l_int32 *ph) { BOX *box; PROCNAME("pixaGetBoxGeometry"); if (px) *px = 0; if (py) *py = 0; if (pw) *pw = 0; if (ph) *ph = 0; if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (index < 0 || index >= pixa->n) return ERROR_INT("index not valid", procName, 1); if ((box = pixaGetBox(pixa, index, L_CLONE)) == NULL) return ERROR_INT("box not found!", procName, 1); boxGetGeometry(box, px, py, pw, ph); boxDestroy(&box); return 0; } /*! * \brief pixaSetBoxa() * * \param[in] pixa * \param[in] boxa * \param[in] accesstype L_INSERT, L_COPY, L_CLONE * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This destroys the existing boxa in the pixa.
 * 
*/ l_ok pixaSetBoxa(PIXA *pixa, BOXA *boxa, l_int32 accesstype) { PROCNAME("pixaSetBoxa"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (!boxa) return ERROR_INT("boxa not defined", procName, 1); if (accesstype != L_INSERT && accesstype != L_COPY && accesstype != L_CLONE) return ERROR_INT("invalid access type", procName, 1); boxaDestroy(&pixa->boxa); if (accesstype == L_INSERT) pixa->boxa = boxa; else pixa->boxa = boxaCopy(boxa, accesstype); return 0; } /*! * \brief pixaGetPixArray() * * \param[in] pixa * \return pix array, or NULL on error * *
 * Notes:
 *      (1) This returns a ptr to the actual array.  The array is
 *          owned by the pixa, so it must not be destroyed.
 *      (2) The caller should always check if the return value is NULL
 *          before accessing any of the pix ptrs in this array!
 * 
*/ PIX ** pixaGetPixArray(PIXA *pixa) { PROCNAME("pixaGetPixArray"); if (!pixa) return (PIX **)ERROR_PTR("pixa not defined", procName, NULL); return pixa->pix; } /*! * \brief pixaVerifyDepth() * * \param[in] pixa * \param[out] psame 1 if depth is the same for all pix; 0 otherwise * \param[out] pmaxd [optional] max depth of all pix * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) It is considered to be an error if there are no pix.
 * 
*/ l_ok pixaVerifyDepth(PIXA *pixa, l_int32 *psame, l_int32 *pmaxd) { l_int32 i, n, d, maxd, same; PROCNAME("pixaVerifyDepth"); if (pmaxd) *pmaxd = 0; if (!psame) return ERROR_INT("psame not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if ((n = pixaGetCount(pixa)) == 0) return ERROR_INT("no pix in pixa", procName, 1); same = 1; pixaGetPixDimensions(pixa, 0, NULL, NULL, &maxd); for (i = 1; i < n; i++) { if (pixaGetPixDimensions(pixa, i, NULL, NULL, &d)) return ERROR_INT("pix depth not found", procName, 1); maxd = L_MAX(maxd, d); if (d != maxd) same = 0; } *psame = same; if (pmaxd) *pmaxd = maxd; return 0; } /*! * \brief pixaVerifyDimensions() * * \param[in] pixa * \param[out] psame 1 if dimensions are the same for all pix; 0 otherwise * \param[out] pmaxw [optional] max width of all pix * \param[out] pmaxh [optional] max height of all pix * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) It is considered to be an error if there are no pix.
 * 
*/ l_ok pixaVerifyDimensions(PIXA *pixa, l_int32 *psame, l_int32 *pmaxw, l_int32 *pmaxh) { l_int32 i, n, w, h, maxw, maxh, same; PROCNAME("pixaVerifyDimensions"); if (pmaxw) *pmaxw = 0; if (pmaxh) *pmaxh = 0; if (!psame) return ERROR_INT("psame not defined", procName, 1); *psame = 0; if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if ((n = pixaGetCount(pixa)) == 0) return ERROR_INT("no pix in pixa", procName, 1); same = 1; pixaGetPixDimensions(pixa, 0, &maxw, &maxh, NULL); for (i = 1; i < n; i++) { if (pixaGetPixDimensions(pixa, i, &w, &h, NULL)) return ERROR_INT("pix dimensions not found", procName, 1); maxw = L_MAX(maxw, w); maxh = L_MAX(maxh, h); if (w != maxw || h != maxh) same = 0; } *psame = same; if (pmaxw) *pmaxw = maxw; if (pmaxh) *pmaxh = maxh; return 0; } /*! * \brief pixaIsFull() * * \param[in] pixa * \param[out] pfullpa [optional] 1 if pixa is full * \param[out] pfullba [optional] 1 if boxa is full * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) A pixa is "full" if the array of pix is fully
 *          occupied from index 0 to index (pixa->n - 1).
 * 
*/ l_ok pixaIsFull(PIXA *pixa, l_int32 *pfullpa, l_int32 *pfullba) { l_int32 i, n, full; BOXA *boxa; PIX *pix; PROCNAME("pixaIsFull"); if (pfullpa) *pfullpa = 0; if (pfullba) *pfullba = 0; if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (pfullpa) { full = 1; for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) { full = 0; break; } pixDestroy(&pix); } *pfullpa = full; } if (pfullba) { boxa = pixaGetBoxa(pixa, L_CLONE); boxaIsFull(boxa, pfullba); boxaDestroy(&boxa); } return 0; } /*! * \brief pixaCountText() * * \param[in] pixa * \param[out] pntext number of pix with non-empty text strings * \return 0 if OK, 1 on error. * *
 * Notes:
 *      (1) All pix have non-empty text strings if the returned value %ntext
 *          equals the pixa count.
 * 
*/ l_ok pixaCountText(PIXA *pixa, l_int32 *pntext) { char *text; l_int32 i, n; PIX *pix; PROCNAME("pixaCountText"); if (!pntext) return ERROR_INT("&ntext not defined", procName, 1); *pntext = 0; if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) continue; text = pixGetText(pix); if (text && strlen(text) > 0) (*pntext)++; pixDestroy(&pix); } return 0; } /*! * \brief pixaSetText() * * \param[in] pixa * \param[in] text [optional] single text string, to insert in each pix * \param[in] sa [optional] array of text strings, to insert in each pix * \return 0 if OK, 1 on error. * *
 * Notes:
 *      (1) To clear all the text fields, use %sa == NULL and %text == NULL.
 *      (2) To set all the text fields to the same value %text, use %sa = NULL.
 *      (3) If %sa is defined, we ignore %text and use it; %sa must have
 *          the same count as %pixa.
 * 
*/ l_ok pixaSetText(PIXA *pixa, const char *text, SARRAY *sa) { char *str; l_int32 i, n; PIX *pix; PROCNAME("pixaSetText"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (sa && (sarrayGetCount(sa) != n)) return ERROR_INT("pixa and sa sizes differ", procName, 1); if (!sa) { for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) continue; pixSetText(pix, text); pixDestroy(&pix); } return 0; } for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) continue; str = sarrayGetString(sa, i, L_NOCOPY); pixSetText(pix, str); pixDestroy(&pix); } return 0; } /*! * \brief pixaGetLinePtrs() * * \param[in] pixa of pix that all have the same depth * \param[out] psize [optional] number of pix in the pixa * \return array of array of line ptrs, or NULL on error * *
 * Notes:
 *      (1) See pixGetLinePtrs() for details.
 *      (2) It is best if all pix in the pixa are the same size.
 *          The size of each line ptr array is equal to the height
 *          of the pix that it refers to.
 *      (3) This is an array of arrays.  To destroy it:
 *            for (i = 0; i < size; i++)
 *                LEPT_FREE(lineset[i]);
 *            LEPT_FREE(lineset);
 * 
*/ void *** pixaGetLinePtrs(PIXA *pixa, l_int32 *psize) { l_int32 i, n, same; void **lineptrs; void ***lineset; PIX *pix; PROCNAME("pixaGetLinePtrs"); if (psize) *psize = 0; if (!pixa) return (void ***)ERROR_PTR("pixa not defined", procName, NULL); pixaVerifyDepth(pixa, &same, NULL); if (!same) return (void ***)ERROR_PTR("pixa not all same depth", procName, NULL); n = pixaGetCount(pixa); if (psize) *psize = n; if ((lineset = (void ***)LEPT_CALLOC(n, sizeof(void **))) == NULL) return (void ***)ERROR_PTR("lineset not made", procName, NULL); for (i = 0; i < n; i++) { pix = pixaGetPix(pixa, i, L_CLONE); lineptrs = pixGetLinePtrs(pix, NULL); lineset[i] = lineptrs; pixDestroy(&pix); } return lineset; } /*---------------------------------------------------------------------* * Pixa output info * *---------------------------------------------------------------------*/ /*! * \brief pixaWriteStreamInfo() * * \param[in] fp file stream * \param[in] pixa * \return 0 if OK, 1 on error. * *
 * Notes:
 *      (1) For each pix in the pixa, write out the pix dimensions, spp,
 *          text string (if it exists), and cmap info.
 * 
*/ l_ok pixaWriteStreamInfo(FILE *fp, PIXA *pixa) { char *text; l_int32 i, n, w, h, d, spp, count, hastext; PIX *pix; PIXCMAP *cmap; PROCNAME("pixaWriteStreamInfo"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) { fprintf(fp, "%d: no pix at this index\n", i); continue; } pixGetDimensions(pix, &w, &h, &d); spp = pixGetSpp(pix); text = pixGetText(pix); hastext = (text && strlen(text) > 0); if ((cmap = pixGetColormap(pix)) != NULL) count = pixcmapGetCount(cmap); fprintf(fp, "Pix %d: w = %d, h = %d, d = %d, spp = %d", i, w, h, d, spp); if (cmap) fprintf(fp, ", cmap(%d colors)", count); if (hastext) fprintf(fp, ", text = %s", text); fprintf(fp, "\n"); pixDestroy(&pix); } return 0; } /*---------------------------------------------------------------------* * Pixa array modifiers * *---------------------------------------------------------------------*/ /*! * \brief pixaReplacePix() * * \param[in] pixa * \param[in] index to the index-th pix * \param[in] pix insert to replace existing one * \param[in] box [optional] insert to replace existing * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) In-place replacement of one pix.
 *      (2) The previous pix at that location is destroyed.
 * 
*/ l_ok pixaReplacePix(PIXA *pixa, l_int32 index, PIX *pix, BOX *box) { BOXA *boxa; PROCNAME("pixaReplacePix"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (index < 0 || index >= pixa->n) return ERROR_INT("index not valid", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); pixDestroy(&(pixa->pix[index])); pixa->pix[index] = pix; if (box) { boxa = pixa->boxa; if (index > boxa->n) return ERROR_INT("boxa index not valid", procName, 1); boxaReplaceBox(boxa, index, box); } return 0; } /*! * \brief pixaInsertPix() * * \param[in] pixa * \param[in] index at which pix is to be inserted * \param[in] pixs new pix to be inserted * \param[in] box [optional] new box to be inserted * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This shifts pixa[i] --> pixa[i + 1] for all i >= index,
 *          and then inserts at pixa[index].
 *      (2) To insert at the beginning of the array, set index = 0.
 *      (3) It should not be used repeatedly on large arrays,
 *          because the function is O(n).
 *      (4) To append a pix to a pixa, it's easier to use pixaAddPix().
 * 
*/ l_ok pixaInsertPix(PIXA *pixa, l_int32 index, PIX *pixs, BOX *box) { l_int32 i, n; PROCNAME("pixaInsertPix"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (index < 0 || index > n) { L_ERROR("index %d not in [0,...,%d]\n", procName, index, n); return 1; } if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (n >= pixa->nalloc) { /* extend both ptr arrays */ if (pixaExtendArray(pixa)) return ERROR_INT("extension failed", procName, 1); if (boxaExtendArray(pixa->boxa)) return ERROR_INT("extension failed", procName, 1); } pixa->n++; for (i = n; i > index; i--) pixa->pix[i] = pixa->pix[i - 1]; pixa->pix[index] = pixs; /* Optionally, insert the box */ if (box) boxaInsertBox(pixa->boxa, index, box); return 0; } /*! * \brief pixaRemovePix() * * \param[in] pixa * \param[in] index of pix to be removed * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This shifts pixa[i] --> pixa[i - 1] for all i > index.
 *      (2) It should not be used repeatedly on large arrays,
 *          because the function is O(n).
 *      (3) The corresponding box is removed as well, if it exists.
 * 
*/ l_ok pixaRemovePix(PIXA *pixa, l_int32 index) { l_int32 i, n, nbox; BOXA *boxa; PIX **array; PROCNAME("pixaRemovePix"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (index < 0 || index >= n) { L_ERROR("index %d not in [0,...,%d]\n", procName, index, n - 1); return 1; } /* Remove the pix */ array = pixa->pix; pixDestroy(&array[index]); for (i = index + 1; i < n; i++) array[i - 1] = array[i]; array[n - 1] = NULL; pixa->n--; /* Remove the box if it exists */ boxa = pixa->boxa; nbox = boxaGetCount(boxa); if (index < nbox) boxaRemoveBox(boxa, index); return 0; } /*! * \brief pixaRemovePixAndSave() * * \param[in] pixa * \param[in] index of pix to be removed * \param[out] ppix [optional] removed pix * \param[out] pbox [optional] removed box * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This shifts pixa[i] --> pixa[i - 1] for all i > index.
 *      (2) It should not be used repeatedly on large arrays,
 *          because the function is O(n).
 *      (3) The corresponding box is removed as well, if it exists.
 *      (4) The removed pix and box can either be retained or destroyed.
 * 
*/ l_ok pixaRemovePixAndSave(PIXA *pixa, l_int32 index, PIX **ppix, BOX **pbox) { l_int32 i, n, nbox; BOXA *boxa; PIX **array; PROCNAME("pixaRemovePixAndSave"); if (ppix) *ppix = NULL; if (pbox) *pbox = NULL; if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); if (index < 0 || index >= n) { L_ERROR("index %d not in [0,...,%d]\n", procName, index, n - 1); return 1; } /* Remove the pix */ array = pixa->pix; if (ppix) *ppix = pixaGetPix(pixa, index, L_CLONE); pixDestroy(&array[index]); for (i = index + 1; i < n; i++) array[i - 1] = array[i]; array[n - 1] = NULL; pixa->n--; /* Remove the box if it exists */ boxa = pixa->boxa; nbox = boxaGetCount(boxa); if (index < nbox) boxaRemoveBoxAndSave(boxa, index, pbox); return 0; } /*! * \brief pixaRemoveSelected() * * \param[in] pixa * \param[in] naindex numa of indices of pix to be removed * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This gives error messages for invalid indices
 * 
*/ l_ok pixaRemoveSelected(PIXA *pixa, NUMA *naindex) { l_int32 i, n, index; NUMA *na1; PROCNAME("pixaRemoveSelected"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (!naindex) return ERROR_INT("naindex not defined", procName, 1); if ((n = numaGetCount(naindex)) == 0) return ERROR_INT("naindex is empty", procName, 1); /* Remove from highest indices first */ na1 = numaSort(NULL, naindex, L_SORT_DECREASING); for (i = 0; i < n; i++) { numaGetIValue(na1, i, &index); pixaRemovePix(pixa, index); } numaDestroy(&na1); return 0; } /*! * \brief pixaInitFull() * * \param[in] pixa typically empty * \param[in] pix [optional] to be replicated to the entire pixa ptr array * \param[in] box [optional] to be replicated to the entire boxa ptr array * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This initializes a pixa by filling up the entire pix ptr array
 *          with copies of %pix.  If %pix == NULL, we use a tiny placeholder
 *          pix (w = h = d = 1).  Any existing pix are destroyed.
 *          It also optionally fills the boxa with copies of %box.
 *          After this operation, the numbers of pix and (optionally)
 *          boxes are equal to the number of allocated ptrs.
 *      (2) Note that we use pixaReplacePix() instead of pixaInsertPix().
 *          They both have the same effect when inserting into a NULL ptr
 *          in the pixa ptr array:
 *      (3) If the boxa is not initialized (i.e., filled with boxes),
 *          later insertion of boxes will cause an error, because the
 *          'n' field is 0.
 *      (4) Example usage.  This function is useful to prepare for a
 *          random insertion (or replacement) of pix into a pixa.
 *          To randomly insert pix into a pixa, without boxes, up to
 *          some index "max":
 *             Pixa *pixa = pixaCreate(max);
 *             pixaInitFull(pixa, NULL, NULL);
 *          An existing pixa with a smaller ptr array can also be reused:
 *             pixaExtendArrayToSize(pixa, max);
 *             pixaInitFull(pixa, NULL, NULL);
 *          The initialization allows the pixa to always be properly
 *          filled, even if all pix (and boxes) are not later replaced.
 * 
*/ l_ok pixaInitFull(PIXA *pixa, PIX *pix, BOX *box) { l_int32 i, n; PIX *pix1; PROCNAME("pixaInitFull"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixa->nalloc; pixa->n = n; for (i = 0; i < n; i++) { if (pix) pix1 = pixCopy(NULL, pix); else pix1 = pixCreate(1, 1, 1); pixaReplacePix(pixa, i, pix1, NULL); } if (box) boxaInitFull(pixa->boxa, box); return 0; } /*! * \brief pixaClear() * * \param[in] pixa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This destroys all pix in the pixa, as well as
 *          all boxes in the boxa.  The ptrs in the pix ptr array
 *          are all null'd.  The number of allocated pix, n, is set to 0.
 * 
*/ l_ok pixaClear(PIXA *pixa) { l_int32 i, n; PROCNAME("pixaClear"); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); for (i = 0; i < n; i++) pixDestroy(&pixa->pix[i]); pixa->n = 0; return boxaClear(pixa->boxa); } /*---------------------------------------------------------------------* * Pixa and Pixaa combination * *---------------------------------------------------------------------*/ /*! * \brief pixaJoin() * * \param[in] pixad dest pixa; add to this one * \param[in] pixas [optional] source pixa; add from this one * \param[in] istart starting index in pixas * \param[in] iend ending index in pixas; use -1 to cat all * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This appends a clone of each indicated pix in pixas to pixad
 *      (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
 *      (3) iend < 0 means 'read to the end'
 *      (4) If pixas is NULL or contains no pix, this is a no-op.
 * 
*/ l_ok pixaJoin(PIXA *pixad, PIXA *pixas, l_int32 istart, l_int32 iend) { l_int32 i, n, nb; BOXA *boxas, *boxad; PIX *pix; PROCNAME("pixaJoin"); if (!pixad) return ERROR_INT("pixad not defined", procName, 1); if (!pixas || ((n = pixaGetCount(pixas)) == 0)) return 0; if (istart < 0) istart = 0; if (iend < 0 || iend >= n) iend = n - 1; if (istart > iend) return ERROR_INT("istart > iend; nothing to add", procName, 1); for (i = istart; i <= iend; i++) { pix = pixaGetPix(pixas, i, L_CLONE); pixaAddPix(pixad, pix, L_INSERT); } boxas = pixaGetBoxa(pixas, L_CLONE); boxad = pixaGetBoxa(pixad, L_CLONE); nb = pixaGetBoxaCount(pixas); iend = L_MIN(iend, nb - 1); boxaJoin(boxad, boxas, istart, iend); boxaDestroy(&boxas); /* just the clones */ boxaDestroy(&boxad); return 0; } /*! * \brief pixaInterleave() * * \param[in] pixa1 first src pixa * \param[in] pixa2 second src pixa * \param[in] copyflag L_CLONE, L_COPY * \return pixa interleaved from sources, or NULL on error. * *
 * Notes:
 *      (1) %copyflag determines if the pix are copied or cloned.
 *          The boxes, if they exist, are copied.
 *      (2) If the two pixa have different sizes, a warning is issued,
 *          and the number of pairs returned is the minimum size.
 * 
*/ PIXA * pixaInterleave(PIXA *pixa1, PIXA *pixa2, l_int32 copyflag) { l_int32 i, n1, n2, n, nb1, nb2; BOX *box; PIX *pix; PIXA *pixad; PROCNAME("pixaInterleave"); if (!pixa1) return (PIXA *)ERROR_PTR("pixa1 not defined", procName, NULL); if (!pixa2) return (PIXA *)ERROR_PTR("pixa2 not defined", procName, NULL); if (copyflag != L_COPY && copyflag != L_CLONE) return (PIXA *)ERROR_PTR("invalid copyflag", procName, NULL); n1 = pixaGetCount(pixa1); n2 = pixaGetCount(pixa2); n = L_MIN(n1, n2); if (n == 0) return (PIXA *)ERROR_PTR("at least one input pixa is empty", procName, NULL); if (n1 != n2) L_WARNING("counts differ: %d != %d\n", procName, n1, n2); pixad = pixaCreate(2 * n); nb1 = pixaGetBoxaCount(pixa1); nb2 = pixaGetBoxaCount(pixa2); for (i = 0; i < n; i++) { pix = pixaGetPix(pixa1, i, copyflag); pixaAddPix(pixad, pix, L_INSERT); if (i < nb1) { box = pixaGetBox(pixa1, i, L_COPY); pixaAddBox(pixad, box, L_INSERT); } pix = pixaGetPix(pixa2, i, copyflag); pixaAddPix(pixad, pix, L_INSERT); if (i < nb2) { box = pixaGetBox(pixa2, i, L_COPY); pixaAddBox(pixad, box, L_INSERT); } } return pixad; } /*! * \brief pixaaJoin() * * \param[in] paad dest pixaa; add to this one * \param[in] paas [optional] source pixaa; add from this one * \param[in] istart starting index in pixaas * \param[in] iend ending index in pixaas; use -1 to cat all * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This appends a clone of each indicated pixa in paas to pixaad
 *      (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
 *      (3) iend < 0 means 'read to the end'
 * 
*/ l_ok pixaaJoin(PIXAA *paad, PIXAA *paas, l_int32 istart, l_int32 iend) { l_int32 i, n; PIXA *pixa; PROCNAME("pixaaJoin"); if (!paad) return ERROR_INT("pixaad not defined", procName, 1); if (!paas) return 0; if (istart < 0) istart = 0; n = pixaaGetCount(paas, NULL); if (iend < 0 || iend >= n) iend = n - 1; if (istart > iend) return ERROR_INT("istart > iend; nothing to add", procName, 1); for (i = istart; i <= iend; i++) { pixa = pixaaGetPixa(paas, i, L_CLONE); pixaaAddPixa(paad, pixa, L_INSERT); } return 0; } /*---------------------------------------------------------------------* * Pixaa creation and destruction * *---------------------------------------------------------------------*/ /*! * \brief pixaaCreate() * * \param[in] n initial number of pixa ptrs * \return paa, or NULL on error * *
 * Notes:
 *      (1) A pixaa provides a 2-level hierarchy of images.
 *          A common use is for segmentation masks, which are
 *          inexpensive to store in png format.
 *      (2) For example, suppose you want a mask for each textline
 *          in a two-column page.  The textline masks for each column
 *          can be represented by a pixa, of which there are 2 in the pixaa.
 *          The boxes for the textline mask components within a column
 *          can have their origin referred to the column rather than the page.
 *          Then the boxa field can be used to represent the two box (regions)
 *          for the columns, and the (x,y) components of each box can
 *          be used to get the absolute position of the textlines on
 *          the page.
 * 
*/ PIXAA * pixaaCreate(l_int32 n) { PIXAA *paa; PROCNAME("pixaaCreate"); if (n <= 0 || n > MaxInitPtrArraySize) n = InitialPtrArraySize; paa = (PIXAA *)LEPT_CALLOC(1, sizeof(PIXAA)); paa->n = 0; paa->nalloc = n; if ((paa->pixa = (PIXA **)LEPT_CALLOC(n, sizeof(PIXA *))) == NULL) { pixaaDestroy(&paa); return (PIXAA *)ERROR_PTR("pixa ptrs not made", procName, NULL); } paa->boxa = boxaCreate(n); return paa; } /*! * \brief pixaaCreateFromPixa() * * \param[in] pixa * \param[in] n number specifying subdivision of pixa * \param[in] type L_CHOOSE_CONSECUTIVE, L_CHOOSE_SKIP_BY * \param[in] copyflag L_CLONE, L_COPY * \return paa, or NULL on error * *
 * Notes:
 *      (1) This subdivides a pixa into a set of smaller pixa that
 *          are accumulated into a pixaa.
 *      (2) If type == L_CHOOSE_CONSECUTIVE, the first 'n' pix are
 *          put in a pixa and added to pixaa, then the next 'n', etc.
 *          If type == L_CHOOSE_SKIP_BY, the first pixa is made by
 *          aggregating pix[0], pix[n], pix[2*n], etc.
 *      (3) The copyflag specifies if each new pix is a copy or a clone.
 * 
*/ PIXAA * pixaaCreateFromPixa(PIXA *pixa, l_int32 n, l_int32 type, l_int32 copyflag) { l_int32 count, i, j, npixa; PIX *pix; PIXA *pixat; PIXAA *paa; PROCNAME("pixaaCreateFromPixa"); if (!pixa) return (PIXAA *)ERROR_PTR("pixa not defined", procName, NULL); count = pixaGetCount(pixa); if (count == 0) return (PIXAA *)ERROR_PTR("no pix in pixa", procName, NULL); if (n <= 0) return (PIXAA *)ERROR_PTR("n must be > 0", procName, NULL); if (type != L_CHOOSE_CONSECUTIVE && type != L_CHOOSE_SKIP_BY) return (PIXAA *)ERROR_PTR("invalid type", procName, NULL); if (copyflag != L_CLONE && copyflag != L_COPY) return (PIXAA *)ERROR_PTR("invalid copyflag", procName, NULL); if (type == L_CHOOSE_CONSECUTIVE) npixa = (count + n - 1) / n; else /* L_CHOOSE_SKIP_BY */ npixa = L_MIN(n, count); paa = pixaaCreate(npixa); if (type == L_CHOOSE_CONSECUTIVE) { for (i = 0; i < count; i++) { if (i % n == 0) pixat = pixaCreate(n); pix = pixaGetPix(pixa, i, copyflag); pixaAddPix(pixat, pix, L_INSERT); if (i % n == n - 1) pixaaAddPixa(paa, pixat, L_INSERT); } if (i % n != 0) pixaaAddPixa(paa, pixat, L_INSERT); } else { /* L_CHOOSE_SKIP_BY */ for (i = 0; i < npixa; i++) { pixat = pixaCreate(count / npixa + 1); for (j = i; j < count; j += n) { pix = pixaGetPix(pixa, j, copyflag); pixaAddPix(pixat, pix, L_INSERT); } pixaaAddPixa(paa, pixat, L_INSERT); } } return paa; } /*! * \brief pixaaDestroy() * * \param[in,out] ppaa use ptr address so it will be nulled * \return void */ void pixaaDestroy(PIXAA **ppaa) { l_int32 i; PIXAA *paa; PROCNAME("pixaaDestroy"); if (ppaa == NULL) { L_WARNING("ptr address is NULL!\n", procName); return; } if ((paa = *ppaa) == NULL) return; for (i = 0; i < paa->n; i++) pixaDestroy(&paa->pixa[i]); LEPT_FREE(paa->pixa); boxaDestroy(&paa->boxa); LEPT_FREE(paa); *ppaa = NULL; } /*---------------------------------------------------------------------* * Pixaa addition * *---------------------------------------------------------------------*/ /*! * \brief pixaaAddPixa() * * \param[in] paa * \param[in] pixa to be added * \param[in] copyflag: * L_INSERT inserts the pixa directly; * L_COPY makes a new pixa and copies each pix and each box; * L_CLONE gives a new handle to the input pixa; * L_COPY_CLONE makes a new pixa and inserts clones of * all pix and boxes * \return 0 if OK; 1 on error */ l_ok pixaaAddPixa(PIXAA *paa, PIXA *pixa, l_int32 copyflag) { l_int32 n; PIXA *pixac; PROCNAME("pixaaAddPixa"); if (!paa) return ERROR_INT("paa not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE && copyflag != L_COPY_CLONE) return ERROR_INT("invalid copyflag", procName, 1); if (copyflag == L_INSERT) { pixac = pixa; } else { if ((pixac = pixaCopy(pixa, copyflag)) == NULL) return ERROR_INT("pixac not made", procName, 1); } n = pixaaGetCount(paa, NULL); if (n >= paa->nalloc) { if (pixaaExtendArray(paa)) { if (copyflag != L_INSERT) pixaDestroy(&pixac); return ERROR_INT("extension failed", procName, 1); } } paa->pixa[n] = pixac; paa->n++; return 0; } /*! * \brief pixaaExtendArray() * * \param[in] paa * \return 0 if OK; 1 on error * *
 * Notes:
 *      (1) The max number of pixa ptrs is 1M.
 * 
*/ static l_int32 pixaaExtendArray(PIXAA *paa) { size_t oldsize, newsize; PROCNAME("pixaaExtendArray"); if (!paa) return ERROR_INT("paa not defined", procName, 1); if (paa->nalloc > MaxPixaaPtrArraySize) /* belt & suspenders */ return ERROR_INT("paa has too many ptrs", procName, 1); oldsize = paa->nalloc * sizeof(PIXA *); newsize = 2 * oldsize; if (newsize > 8 * MaxPixaaPtrArraySize) return ERROR_INT("newsize > 8 MB; too large", procName, 1); if ((paa->pixa = (PIXA **)reallocNew((void **)&paa->pixa, oldsize, newsize)) == NULL) return ERROR_INT("new ptr array not returned", procName, 1); paa->nalloc *= 2; return 0; } /*! * \brief pixaaAddPix() * * \param[in] paa input paa * \param[in] index index of pixa in paa * \param[in] pix to be added * \param[in] box [optional] to be added * \param[in] copyflag L_INSERT, L_COPY, L_CLONE * \return 0 if OK; 1 on error */ l_ok pixaaAddPix(PIXAA *paa, l_int32 index, PIX *pix, BOX *box, l_int32 copyflag) { PIXA *pixa; PROCNAME("pixaaAddPix"); if (!paa) return ERROR_INT("paa not defined", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); if ((pixa = pixaaGetPixa(paa, index, L_CLONE)) == NULL) return ERROR_INT("pixa not found", procName, 1); pixaAddPix(pixa, pix, copyflag); if (box) pixaAddBox(pixa, box, copyflag); pixaDestroy(&pixa); return 0; } /*! * \brief pixaaAddBox() * * \param[in] paa * \param[in] box * \param[in] copyflag L_INSERT, L_COPY, L_CLONE * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) The box can be used, for example, to hold the support region
 *          of a pixa that is being added to the pixaa.
 * 
*/ l_ok pixaaAddBox(PIXAA *paa, BOX *box, l_int32 copyflag) { PROCNAME("pixaaAddBox"); if (!paa) return ERROR_INT("paa not defined", procName, 1); if (!box) return ERROR_INT("box not defined", procName, 1); if (copyflag != L_INSERT && copyflag != L_COPY && copyflag != L_CLONE) return ERROR_INT("invalid copyflag", procName, 1); boxaAddBox(paa->boxa, box, copyflag); return 0; } /*---------------------------------------------------------------------* * Pixaa accessors * *---------------------------------------------------------------------*/ /*! * \brief pixaaGetCount() * * \param[in] paa * \param[out] pna [optional] number of pix in each pixa * \return count, or 0 if no pixaa * *
 * Notes:
 *      (1) If paa is empty, a returned na will also be empty.
 * 
*/ l_int32 pixaaGetCount(PIXAA *paa, NUMA **pna) { l_int32 i, n; NUMA *na; PIXA *pixa; PROCNAME("pixaaGetCount"); if (pna) *pna = NULL; if (!paa) return ERROR_INT("paa not defined", procName, 0); n = paa->n; if (pna) { if ((na = numaCreate(n)) == NULL) return ERROR_INT("na not made", procName, 0); *pna = na; for (i = 0; i < n; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); numaAddNumber(na, pixaGetCount(pixa)); pixaDestroy(&pixa); } } return n; } /*! * \brief pixaaGetPixa() * * \param[in] paa * \param[in] index to the index-th pixa * \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE * \return pixa, or NULL on error * *
 * Notes:
 *      (1) L_COPY makes a new pixa with a copy of every pix
 *      (2) L_CLONE just makes a new reference to the pixa,
 *          and bumps the counter.  You would use this, for example,
 *          when you need to extract some data from a pix within a
 *          pixa within a pixaa.
 *      (3) L_COPY_CLONE makes a new pixa with a clone of every pix
 *          and box
 *      (4) In all cases, you must invoke pixaDestroy() on the returned pixa
 * 
*/ PIXA * pixaaGetPixa(PIXAA *paa, l_int32 index, l_int32 accesstype) { PIXA *pixa; PROCNAME("pixaaGetPixa"); if (!paa) return (PIXA *)ERROR_PTR("paa not defined", procName, NULL); if (index < 0 || index >= paa->n) return (PIXA *)ERROR_PTR("index not valid", procName, NULL); if (accesstype != L_COPY && accesstype != L_CLONE && accesstype != L_COPY_CLONE) return (PIXA *)ERROR_PTR("invalid accesstype", procName, NULL); if ((pixa = paa->pixa[index]) == NULL) { /* shouldn't happen! */ L_ERROR("missing pixa[%d]\n", procName, index); return (PIXA *)ERROR_PTR("pixa not found at index", procName, NULL); } return pixaCopy(pixa, accesstype); } /*! * \brief pixaaGetBoxa() * * \param[in] paa * \param[in] accesstype L_COPY, L_CLONE * \return boxa, or NULL on error * *
 * Notes:
 *      (1) L_COPY returns a copy; L_CLONE returns a new reference to the boxa.
 *      (2) In both cases, invoke boxaDestroy() on the returned boxa.
 * 
*/ BOXA * pixaaGetBoxa(PIXAA *paa, l_int32 accesstype) { PROCNAME("pixaaGetBoxa"); if (!paa) return (BOXA *)ERROR_PTR("paa not defined", procName, NULL); if (accesstype != L_COPY && accesstype != L_CLONE) return (BOXA *)ERROR_PTR("invalid access type", procName, NULL); return boxaCopy(paa->boxa, accesstype); } /*! * \brief pixaaGetPix() * * \param[in] paa * \param[in] index index into the pixa array in the pixaa * \param[in] ipix index into the pix array in the pixa * \param[in] accessflag L_COPY or L_CLONE * \return pix, or NULL on error */ PIX * pixaaGetPix(PIXAA *paa, l_int32 index, l_int32 ipix, l_int32 accessflag) { PIX *pix; PIXA *pixa; PROCNAME("pixaaGetPix"); if ((pixa = pixaaGetPixa(paa, index, L_CLONE)) == NULL) return (PIX *)ERROR_PTR("pixa not retrieved", procName, NULL); if ((pix = pixaGetPix(pixa, ipix, accessflag)) == NULL) L_ERROR("pix not retrieved\n", procName); pixaDestroy(&pixa); return pix; } /*! * \brief pixaaVerifyDepth() * * \param[in] paa * \param[out] psame 1 if all pix have the same depth; 0 otherwise * \param[out] pmaxd [optional] max depth of all pix in pixaa * \return 0 if OK; 1 on error * *
 * Notes:
 *      (1) It is considered to be an error if any pixa have no pix.
 * 
*/ l_ok pixaaVerifyDepth(PIXAA *paa, l_int32 *psame, l_int32 *pmaxd) { l_int32 i, n, d, maxd, same, samed; PIXA *pixa; PROCNAME("pixaaVerifyDepth"); if (pmaxd) *pmaxd = 0; if (!psame) return ERROR_INT("psame not defined", procName, 1); *psame = 0; if (!paa) return ERROR_INT("paa not defined", procName, 1); if ((n = pixaaGetCount(paa, NULL)) == 0) return ERROR_INT("no pixa in paa", procName, 1); pixa = pixaaGetPixa(paa, 0, L_CLONE); pixaVerifyDepth(pixa, &same, &maxd); /* init same, maxd with first pixa */ pixaDestroy(&pixa); for (i = 1; i < n; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); pixaVerifyDepth(pixa, &samed, &d); pixaDestroy(&pixa); maxd = L_MAX(maxd, d); if (!samed || maxd != d) same = 0; } *psame = same; if (pmaxd) *pmaxd = maxd; return 0; } /*! * \brief pixaaVerifyDimensions() * * \param[in] paa * \param[out] psame 1 if all pix have the same depth; 0 otherwise * \param[out] pmaxw [optional] max width of all pix in pixaa * \param[out] pmaxh [optional] max height of all pix in pixaa * \return 0 if OK; 1 on error * *
 * Notes:
 *      (1) It is considered to be an error if any pixa have no pix.
 * 
*/ l_ok pixaaVerifyDimensions(PIXAA *paa, l_int32 *psame, l_int32 *pmaxw, l_int32 *pmaxh) { l_int32 i, n, w, h, maxw, maxh, same, same2; PIXA *pixa; PROCNAME("pixaaVerifyDimensions"); if (pmaxw) *pmaxw = 0; if (pmaxh) *pmaxh = 0; if (!psame) return ERROR_INT("psame not defined", procName, 1); *psame = 0; if (!paa) return ERROR_INT("paa not defined", procName, 1); if ((n = pixaaGetCount(paa, NULL)) == 0) return ERROR_INT("no pixa in paa", procName, 1); /* Init same; init maxw and maxh from first pixa */ pixa = pixaaGetPixa(paa, 0, L_CLONE); pixaVerifyDimensions(pixa, &same, &maxw, &maxh); pixaDestroy(&pixa); for (i = 1; i < n; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); pixaVerifyDimensions(pixa, &same2, &w, &h); pixaDestroy(&pixa); maxw = L_MAX(maxw, w); maxh = L_MAX(maxh, h); if (!same2 || maxw != w || maxh != h) same = 0; } *psame = same; if (pmaxw) *pmaxw = maxw; if (pmaxh) *pmaxh = maxh; return 0; } /*! * \brief pixaaIsFull() * * \param[in] paa * \param[out] pfull 1 if all pixa in the paa have full pix arrays * \return return 0 if OK, 1 on error * *
 * Notes:
 *      (1) Does not require boxa associated with each pixa to be full.
 * 
*/ l_int32 pixaaIsFull(PIXAA *paa, l_int32 *pfull) { l_int32 i, n, full; PIXA *pixa; PROCNAME("pixaaIsFull"); if (!pfull) return ERROR_INT("&full not defined", procName, 0); *pfull = 0; if (!paa) return ERROR_INT("paa not defined", procName, 0); n = pixaaGetCount(paa, NULL); full = 1; for (i = 0; i < n; i++) { pixa = pixaaGetPixa(paa, i, L_CLONE); pixaIsFull(pixa, &full, NULL); pixaDestroy(&pixa); if (!full) break; } *pfull = full; return 0; } /*---------------------------------------------------------------------* * Pixaa array modifiers * *---------------------------------------------------------------------*/ /*! * \brief pixaaInitFull() * * \param[in] paa typically empty * \param[in] pixa to be replicated into the entire pixa ptr array * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This initializes a pixaa by filling up the entire pixa ptr array
 *          with copies of %pixa.  Any existing pixa are destroyed.
 *      (2) Example usage.  This function is useful to prepare for a
 *          random insertion (or replacement) of pixa into a pixaa.
 *          To randomly insert pixa into a pixaa, up to some index "max":
 *             Pixaa *paa = pixaaCreate(max);
 *             Pixa *pixa = pixaCreate(1);  // if you want little memory
 *             pixaaInitFull(paa, pixa);  // copy it to entire array
 *             pixaDestroy(&pixa);  // no longer needed
 *          The initialization allows the pixaa to always be properly filled.
 * 
*/ l_ok pixaaInitFull(PIXAA *paa, PIXA *pixa) { l_int32 i, n; PIXA *pixat; PROCNAME("pixaaInitFull"); if (!paa) return ERROR_INT("paa not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = paa->nalloc; paa->n = n; for (i = 0; i < n; i++) { pixat = pixaCopy(pixa, L_COPY); pixaaReplacePixa(paa, i, pixat); } return 0; } /*! * \brief pixaaReplacePixa() * * \param[in] paa * \param[in] index to the index-th pixa * \param[in] pixa insert to replace existing one * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This allows random insertion of a pixa into a pixaa, with
 *          destruction of any existing pixa at that location.
 *          The input pixa is now owned by the pixaa.
 *      (2) No other pixa in the array are affected.
 *      (3) The index must be within the allowed set.
 * 
*/ l_ok pixaaReplacePixa(PIXAA *paa, l_int32 index, PIXA *pixa) { PROCNAME("pixaaReplacePixa"); if (!paa) return ERROR_INT("paa not defined", procName, 1); if (index < 0 || index >= paa->n) return ERROR_INT("index not valid", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); pixaDestroy(&(paa->pixa[index])); paa->pixa[index] = pixa; return 0; } /*! * \brief pixaaClear() * * \param[in] paa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This destroys all pixa in the pixaa, and nulls the ptrs
 *          in the pixa ptr array.
 * 
*/ l_ok pixaaClear(PIXAA *paa) { l_int32 i, n; PROCNAME("pixaClear"); if (!paa) return ERROR_INT("paa not defined", procName, 1); n = pixaaGetCount(paa, NULL); for (i = 0; i < n; i++) pixaDestroy(&paa->pixa[i]); paa->n = 0; return 0; } /*! * \brief pixaaTruncate() * * \param[in] paa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) This identifies the largest index containing a pixa that
 *          has any pix within it, destroys all pixa above that index,
 *          and resets the count.
 * 
*/ l_ok pixaaTruncate(PIXAA *paa) { l_int32 i, n, np; PIXA *pixa; PROCNAME("pixaaTruncate"); if (!paa) return ERROR_INT("paa not defined", procName, 1); n = pixaaGetCount(paa, NULL); for (i = n - 1; i >= 0; i--) { pixa = pixaaGetPixa(paa, i, L_CLONE); if (!pixa) { paa->n--; continue; } np = pixaGetCount(pixa); pixaDestroy(&pixa); if (np == 0) { pixaDestroy(&paa->pixa[i]); paa->n--; } else { break; } } return 0; } /*---------------------------------------------------------------------* * Pixa serialized I/O * *---------------------------------------------------------------------*/ /*! * \brief pixaRead() * * \param[in] filename * \return pixa, or NULL on error * *
 * Notes:
 *      (1) The pix are stored in the file as png.
 *          If the png library is not linked, this will fail.
 * 
*/ PIXA * pixaRead(const char *filename) { FILE *fp; PIXA *pixa; PROCNAME("pixaRead"); #if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ return (PIXA *)ERROR_PTR("no libpng: can't read data", procName, NULL); #endif /* !HAVE_LIBPNG */ if (!filename) return (PIXA *)ERROR_PTR("filename not defined", procName, NULL); if ((fp = fopenReadStream(filename)) == NULL) return (PIXA *)ERROR_PTR("stream not opened", procName, NULL); pixa = pixaReadStream(fp); fclose(fp); if (!pixa) return (PIXA *)ERROR_PTR("pixa not read", procName, NULL); return pixa; } /*! * \brief pixaReadStream() * * \param[in] fp file stream * \return pixa, or NULL on error * *
 * Notes:
 *      (1) The pix are stored in the file as png.
 *          If the png library is not linked, this will fail.
 *      (2) It is OK for the pixa to be empty.
 * 
*/ PIXA * pixaReadStream(FILE *fp) { l_int32 n, i, xres, yres, version; l_int32 ignore; BOXA *boxa; PIX *pix; PIXA *pixa; PROCNAME("pixaReadStream"); #if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ return (PIXA *)ERROR_PTR("no libpng: can't read data", procName, NULL); #endif /* !HAVE_LIBPNG */ if (!fp) return (PIXA *)ERROR_PTR("stream not defined", procName, NULL); if (fscanf(fp, "\nPixa Version %d\n", &version) != 1) return (PIXA *)ERROR_PTR("not a pixa file", procName, NULL); if (version != PIXA_VERSION_NUMBER) return (PIXA *)ERROR_PTR("invalid pixa version", procName, NULL); if (fscanf(fp, "Number of pix = %d\n", &n) != 1) return (PIXA *)ERROR_PTR("not a pixa file", procName, NULL); if (n < 0) return (PIXA *)ERROR_PTR("num pix ptrs < 0", procName, NULL); if (n > MaxPixaPtrArraySize) return (PIXA *)ERROR_PTR("too many pix ptrs", procName, NULL); if (n == 0) L_INFO("the pixa is empty\n", procName); if ((boxa = boxaReadStream(fp)) == NULL) return (PIXA *)ERROR_PTR("boxa not made", procName, NULL); if ((pixa = pixaCreate(n)) == NULL) { boxaDestroy(&boxa); return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); } boxaDestroy(&pixa->boxa); pixa->boxa = boxa; for (i = 0; i < n; i++) { if ((fscanf(fp, " pix[%d]: xres = %d, yres = %d\n", &ignore, &xres, &yres)) != 3) { pixaDestroy(&pixa); return (PIXA *)ERROR_PTR("res reading error", procName, NULL); } if ((pix = pixReadStreamPng(fp)) == NULL) { pixaDestroy(&pixa); return (PIXA *)ERROR_PTR("pix not read", procName, NULL); } pixSetXRes(pix, xres); pixSetYRes(pix, yres); pixaAddPix(pixa, pix, L_INSERT); } return pixa; } /*! * \brief pixaReadMem() * * \param[in] data of serialized pixa * \param[in] size of data in bytes * \return pixa, or NULL on error */ PIXA * pixaReadMem(const l_uint8 *data, size_t size) { FILE *fp; PIXA *pixa; PROCNAME("pixaReadMem"); if (!data) return (PIXA *)ERROR_PTR("data not defined", procName, NULL); if ((fp = fopenReadFromMemory(data, size)) == NULL) return (PIXA *)ERROR_PTR("stream not opened", procName, NULL); pixa = pixaReadStream(fp); fclose(fp); if (!pixa) L_ERROR("pixa not read\n", procName); return pixa; } /*! * \brief pixaWriteDebug() * * \param[in] fname * \param[in] pixa * \return 0 if OK; 1 on error * *
 * Notes:
 *      (1) Debug version, intended for use in the library when writing
 *          to files in a temp directory with names that are compiled in.
 *          This is used instead of pixaWrite() for all such library calls.
 *      (2) The global variable LeptDebugOK defaults to 0, and can be set
 *          or cleared by the function setLeptDebugOK().
 * 
*/ l_ok pixaWriteDebug(const char *fname, PIXA *pixa) { PROCNAME("pixaWriteDebug"); if (LeptDebugOK) { return pixaWrite(fname, pixa); } else { L_INFO("write to named temp file %s is disabled\n", procName, fname); return 0; } } /*! * \brief pixaWrite() * * \param[in] filename * \param[in] pixa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) The pix are stored in the file as png.
 *          If the png library is not linked, this will fail.
 * 
*/ l_ok pixaWrite(const char *filename, PIXA *pixa) { l_int32 ret; FILE *fp; PROCNAME("pixaWrite"); #if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ return ERROR_INT("no libpng: can't write data", procName, 1); #endif /* !HAVE_LIBPNG */ if (!filename) return ERROR_INT("filename not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); if ((fp = fopenWriteStream(filename, "wb")) == NULL) return ERROR_INT("stream not opened", procName, 1); ret = pixaWriteStream(fp, pixa); fclose(fp); if (ret) return ERROR_INT("pixa not written to stream", procName, 1); return 0; } /*! * \brief pixaWriteStream() * * \param[in] fp file stream opened for "wb" * \param[in] pixa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) The pix are stored in the file as png.
 *          If the png library is not linked, this will fail.
 * 
*/ l_ok pixaWriteStream(FILE *fp, PIXA *pixa) { l_int32 n, i; PIX *pix; PROCNAME("pixaWriteStream"); #if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ return ERROR_INT("no libpng: can't write data", procName, 1); #endif /* !HAVE_LIBPNG */ if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); n = pixaGetCount(pixa); fprintf(fp, "\nPixa Version %d\n", PIXA_VERSION_NUMBER); fprintf(fp, "Number of pix = %d\n", n); boxaWriteStream(fp, pixa->boxa); for (i = 0; i < n; i++) { if ((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL) return ERROR_INT("pix not found", procName, 1); fprintf(fp, " pix[%d]: xres = %d, yres = %d\n", i, pix->xres, pix->yres); pixWriteStreamPng(fp, pix, 0.0); pixDestroy(&pix); } return 0; } /*! * \brief pixaWriteMem() * * \param[out] pdata data of serialized pixa * \param[out] psize size of returned data * \param[in] pixa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) Serializes a pixa in memory and puts the result in a buffer.
 * 
*/ l_ok pixaWriteMem(l_uint8 **pdata, size_t *psize, PIXA *pixa) { l_int32 ret; FILE *fp; PROCNAME("pixaWriteMem"); if (pdata) *pdata = NULL; if (psize) *psize = 0; if (!pdata) return ERROR_INT("&data not defined", procName, 1); if (!psize) return ERROR_INT("&size not defined", procName, 1); if (!pixa) return ERROR_INT("pixa not defined", procName, 1); #if HAVE_FMEMOPEN if ((fp = open_memstream((char **)pdata, psize)) == NULL) return ERROR_INT("stream not opened", procName, 1); ret = pixaWriteStream(fp, pixa); #else L_INFO("work-around: writing to a temp file\n", procName); #ifdef _WIN32 if ((fp = fopenWriteWinTempfile()) == NULL) return ERROR_INT("tmpfile stream not opened", procName, 1); #else if ((fp = tmpfile()) == NULL) return ERROR_INT("tmpfile stream not opened", procName, 1); #endif /* _WIN32 */ ret = pixaWriteStream(fp, pixa); rewind(fp); *pdata = l_binaryReadStream(fp, psize); #endif /* HAVE_FMEMOPEN */ fclose(fp); return ret; } /*! * \brief pixaReadBoth() * * \param[in] filename * \return pixa, or NULL on error * *
 * Notes:
 *      (1) This reads serialized files of either a pixa or a pixacomp,
 *          and returns a pixa in memory.  It requires png and jpeg libraries.
 * 
*/ PIXA * pixaReadBoth(const char *filename) { char buf[32]; char *sname; PIXA *pixa; PIXAC *pac; PROCNAME("pixaReadBoth"); if (!filename) return (PIXA *)ERROR_PTR("filename not defined", procName, NULL); l_getStructStrFromFile(filename, L_STR_NAME, &sname); if (!sname) return (PIXA *)ERROR_PTR("struct name not found", procName, NULL); snprintf(buf, sizeof(buf), "%s", sname); LEPT_FREE(sname); if (strcmp(buf, "Pixacomp") == 0) { if ((pac = pixacompRead(filename)) == NULL) return (PIXA *)ERROR_PTR("pac not made", procName, NULL); pixa = pixaCreateFromPixacomp(pac, L_COPY); pixacompDestroy(&pac); } else if (strcmp(buf, "Pixa") == 0) { if ((pixa = pixaRead(filename)) == NULL) return (PIXA *)ERROR_PTR("pixa not made", procName, NULL); } else { return (PIXA *)ERROR_PTR("invalid file type", procName, NULL); } return pixa; } /*---------------------------------------------------------------------* * Pixaa serialized I/O * *---------------------------------------------------------------------*/ /*! * \brief pixaaReadFromFiles() * * \param[in] dirname directory * \param[in] substr [optional] substring filter on filenames; can be NULL * \param[in] first 0-based * \param[in] nfiles use 0 for everything from %first to the end * \return paa, or NULL on error or if no pixa files are found. * *
 * Notes:
 *      (1) The files must be serialized pixa files (e.g., *.pa)
 *          If some files cannot be read, warnings are issued.
 *      (2) Use %substr to filter filenames in the directory.  If
 *          %substr == NULL, this takes all files.
 *      (3) After filtering, use %first and %nfiles to select
 *          a contiguous set of files, that have been lexically
 *          sorted in increasing order.
 * 
*/ PIXAA * pixaaReadFromFiles(const char *dirname, const char *substr, l_int32 first, l_int32 nfiles) { char *fname; l_int32 i, n; PIXA *pixa; PIXAA *paa; SARRAY *sa; PROCNAME("pixaaReadFromFiles"); if (!dirname) return (PIXAA *)ERROR_PTR("dirname not defined", procName, NULL); sa = getSortedPathnamesInDirectory(dirname, substr, first, nfiles); if (!sa || ((n = sarrayGetCount(sa)) == 0)) { sarrayDestroy(&sa); return (PIXAA *)ERROR_PTR("no pixa files found", procName, NULL); } paa = pixaaCreate(n); for (i = 0; i < n; i++) { fname = sarrayGetString(sa, i, L_NOCOPY); if ((pixa = pixaRead(fname)) == NULL) { L_ERROR("pixa not read for %d-th file", procName, i); continue; } pixaaAddPixa(paa, pixa, L_INSERT); } sarrayDestroy(&sa); return paa; } /*! * \brief pixaaRead() * * \param[in] filename * \return paa, or NULL on error * *
 * Notes:
 *      (1) The pix are stored in the file as png.
 *          If the png library is not linked, this will fail.
 * 
*/ PIXAA * pixaaRead(const char *filename) { FILE *fp; PIXAA *paa; PROCNAME("pixaaRead"); #if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ return (PIXAA *)ERROR_PTR("no libpng: can't read data", procName, NULL); #endif /* !HAVE_LIBPNG */ if (!filename) return (PIXAA *)ERROR_PTR("filename not defined", procName, NULL); if ((fp = fopenReadStream(filename)) == NULL) return (PIXAA *)ERROR_PTR("stream not opened", procName, NULL); paa = pixaaReadStream(fp); fclose(fp); if (!paa) return (PIXAA *)ERROR_PTR("paa not read", procName, NULL); return paa; } /*! * \brief pixaaReadStream() * * \param[in] fp file stream * \return paa, or NULL on error * *
 * Notes:
 *      (1) The pix are stored in the file as png.
 *          If the png library is not linked, this will fail.
 *      (2) It is OK for the pixaa to be empty.
 * 
*/ PIXAA * pixaaReadStream(FILE *fp) { l_int32 n, i, version; l_int32 ignore; BOXA *boxa; PIXA *pixa; PIXAA *paa; PROCNAME("pixaaReadStream"); #if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ return (PIXAA *)ERROR_PTR("no libpng: can't read data", procName, NULL); #endif /* !HAVE_LIBPNG */ if (!fp) return (PIXAA *)ERROR_PTR("stream not defined", procName, NULL); if (fscanf(fp, "\nPixaa Version %d\n", &version) != 1) return (PIXAA *)ERROR_PTR("not a pixaa file", procName, NULL); if (version != PIXAA_VERSION_NUMBER) return (PIXAA *)ERROR_PTR("invalid pixaa version", procName, NULL); if (fscanf(fp, "Number of pixa = %d\n", &n) != 1) return (PIXAA *)ERROR_PTR("not a pixaa file", procName, NULL); if (n < 0) return (PIXAA *)ERROR_PTR("num pixa ptrs < 0", procName, NULL); if (n > MaxPixaaPtrArraySize) return (PIXAA *)ERROR_PTR("too many pixa ptrs", procName, NULL); if (n == 0) L_INFO("the pixaa is empty\n", procName); if ((paa = pixaaCreate(n)) == NULL) return (PIXAA *)ERROR_PTR("paa not made", procName, NULL); if ((boxa = boxaReadStream(fp)) == NULL) { pixaaDestroy(&paa); return (PIXAA *)ERROR_PTR("boxa not made", procName, NULL); } boxaDestroy(&paa->boxa); paa->boxa = boxa; for (i = 0; i < n; i++) { if ((fscanf(fp, "\n\n --------------- pixa[%d] ---------------\n", &ignore)) != 1) { pixaaDestroy(&paa); return (PIXAA *)ERROR_PTR("text reading", procName, NULL); } if ((pixa = pixaReadStream(fp)) == NULL) { pixaaDestroy(&paa); return (PIXAA *)ERROR_PTR("pixa not read", procName, NULL); } pixaaAddPixa(paa, pixa, L_INSERT); } return paa; } /*! * \brief pixaaReadMem() * * \param[in] data of serialized pixaa * \param[in] size of data in bytes * \return paa, or NULL on error */ PIXAA * pixaaReadMem(const l_uint8 *data, size_t size) { FILE *fp; PIXAA *paa; PROCNAME("paaReadMem"); if (!data) return (PIXAA *)ERROR_PTR("data not defined", procName, NULL); if ((fp = fopenReadFromMemory(data, size)) == NULL) return (PIXAA *)ERROR_PTR("stream not opened", procName, NULL); paa = pixaaReadStream(fp); fclose(fp); if (!paa) L_ERROR("paa not read\n", procName); return paa; } /*! * \brief pixaaWrite() * * \param[in] filename * \param[in] paa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) The pix are stored in the file as png.
 *          If the png library is not linked, this will fail.
 * 
*/ l_ok pixaaWrite(const char *filename, PIXAA *paa) { l_int32 ret; FILE *fp; PROCNAME("pixaaWrite"); #if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ return ERROR_INT("no libpng: can't read data", procName, 1); #endif /* !HAVE_LIBPNG */ if (!filename) return ERROR_INT("filename not defined", procName, 1); if (!paa) return ERROR_INT("paa not defined", procName, 1); if ((fp = fopenWriteStream(filename, "wb")) == NULL) return ERROR_INT("stream not opened", procName, 1); ret = pixaaWriteStream(fp, paa); fclose(fp); if (ret) return ERROR_INT("paa not written to stream", procName, 1); return 0; } /*! * \brief pixaaWriteStream() * * \param[in] fp file stream opened for "wb" * \param[in] paa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) The pix are stored in the file as png.
 *          If the png library is not linked, this will fail.
 * 
*/ l_ok pixaaWriteStream(FILE *fp, PIXAA *paa) { l_int32 n, i; PIXA *pixa; PROCNAME("pixaaWriteStream"); #if !HAVE_LIBPNG /* defined in environ.h and config_auto.h */ return ERROR_INT("no libpng: can't read data", procName, 1); #endif /* !HAVE_LIBPNG */ if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!paa) return ERROR_INT("paa not defined", procName, 1); n = pixaaGetCount(paa, NULL); fprintf(fp, "\nPixaa Version %d\n", PIXAA_VERSION_NUMBER); fprintf(fp, "Number of pixa = %d\n", n); boxaWriteStream(fp, paa->boxa); for (i = 0; i < n; i++) { if ((pixa = pixaaGetPixa(paa, i, L_CLONE)) == NULL) return ERROR_INT("pixa not found", procName, 1); fprintf(fp, "\n\n --------------- pixa[%d] ---------------\n", i); pixaWriteStream(fp, pixa); pixaDestroy(&pixa); } return 0; } /*! * \brief pixaaWriteMem() * * \param[out] pdata data of serialized pixaa * \param[out] psize size of returned data * \param[in] paa * \return 0 if OK, 1 on error * *
 * Notes:
 *      (1) Serializes a pixaa in memory and puts the result in a buffer.
 * 
*/ l_ok pixaaWriteMem(l_uint8 **pdata, size_t *psize, PIXAA *paa) { l_int32 ret; FILE *fp; PROCNAME("pixaaWriteMem"); if (pdata) *pdata = NULL; if (psize) *psize = 0; if (!pdata) return ERROR_INT("&data not defined", procName, 1); if (!psize) return ERROR_INT("&size not defined", procName, 1); if (!paa) return ERROR_INT("paa not defined", procName, 1); #if HAVE_FMEMOPEN if ((fp = open_memstream((char **)pdata, psize)) == NULL) return ERROR_INT("stream not opened", procName, 1); ret = pixaaWriteStream(fp, paa); #else L_INFO("work-around: writing to a temp file\n", procName); #ifdef _WIN32 if ((fp = fopenWriteWinTempfile()) == NULL) return ERROR_INT("tmpfile stream not opened", procName, 1); #else if ((fp = tmpfile()) == NULL) return ERROR_INT("tmpfile stream not opened", procName, 1); #endif /* _WIN32 */ ret = pixaaWriteStream(fp, paa); rewind(fp); *pdata = l_binaryReadStream(fp, psize); #endif /* HAVE_FMEMOPEN */ fclose(fp); return ret; }