/*====================================================================* - 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
* 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; }