twain3.0/3rdparty/hgOCR/leptonica/pixabasic.c

3244 lines
79 KiB
C

/*====================================================================*
- 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
* <pre>
*
* 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()
* 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.
* </pre>
*/
#ifdef HAVE_CONFIG_H
#include "config_auto.h"
#endif /* HAVE_CONFIG_H */
#include <string.h>
#include "allheaders.h"
/* Bounds on initial array size */
static const l_uint32 MaxPtrArraySize = 100000;
static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */
/* Static functions */
static l_int32 pixaExtendArray(PIXA *pixa);
/*---------------------------------------------------------------------*
* Pixa creation, destruction, copy *
*---------------------------------------------------------------------*/
/*!
* \brief pixaCreate()
*
* \param[in] n initial number of ptrs
* \return pixa, or NULL on error
*
* <pre>
* Notes:
* (1) This creates an empty boxa.
* </pre>
*/
PIXA *
pixaCreate(l_int32 n)
{
PIXA *pixa;
PROCNAME("pixaCreate");
if (n <= 0 || n > MaxPtrArraySize)
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
*
* <pre>
* Notes:
* (1) For bpp = 1, we truncate each retrieved pix to the ON
* pixels, which we assume for now start at (0,0)
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) Decrements the ref count and, if 0, destroys the pixa.
* (2) Always nulls the input ptr.
* </pre>
*/
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;
return;
}
/*!
* \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)
pixaExtendArray(pixa);
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
*
* <pre>
* Notes:
* (1) Doubles the size of the pixa and boxa ptr arrays.
* </pre>
*/
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
* \return 0 if OK; 1 on error
*
* <pre>
* 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.
* </pre>
*/
l_ok
pixaExtendArrayToSize(PIXA *pixa,
l_int32 size)
{
PROCNAME("pixaExtendArrayToSize");
if (!pixa)
return ERROR_INT("pixa not defined", procName, 1);
if (size > pixa->nalloc) {
if ((pixa->pix = (PIX **)reallocNew((void **)&pixa->pix,
sizeof(PIX *) * pixa->nalloc,
size * sizeof(PIX *))) == 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
*
* <pre>
* 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().
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) This destroys the existing boxa in the pixa.
* </pre>
*/
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
*
* <pre>
* 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!
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) It is considered to be an error if there are no pix.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) It is considered to be an error if there are no pix.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) A pixa is "full" if the array of pix is fully
* occupied from index 0 to index (pixa->n - 1).
* </pre>
*/
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.
*
* <pre>
* Notes:
* (1) All pix have non-empty text strings if the returned value %ntext
* equals the pixa count.
* </pre>
*/
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.
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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);
* </pre>
*/
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.
*
* <pre>
* Notes:
* (1) For each pix in the pixa, write out the pix dimensions, spp,
* text string (if it exists), and cmap info.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) In-place replacement of one pix.
* (2) The previous pix at that location is destroyed.
* </pre>
*/
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
*
* <pre>
* 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().
* </pre>
*/
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)
return ERROR_INT("index not in {0...n}", procName, 1);
if (!pixs)
return ERROR_INT("pixs not defined", procName, 1);
if (n >= pixa->nalloc) { /* extend both ptr arrays */
pixaExtendArray(pixa);
boxaExtendArray(pixa->boxa);
}
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
*
* <pre>
* 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.
* </pre>
*/
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)
return ERROR_INT("index not in {0...n - 1}", procName, 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
*
* <pre>
* 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.
* </pre>
*/
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)
return ERROR_INT("index not in {0...n - 1}", procName, 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
*
* <pre>
* Notes:
* (1) This gives error messages for invalid indices
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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.
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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'
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
PIXAA *
pixaaCreate(l_int32 n)
{
PIXAA *paa;
PROCNAME("pixaaCreate");
if (n <= 0 || n > MaxPtrArraySize)
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
*
* <pre>
* 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.
* </pre>
*/
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;
return;
}
/*---------------------------------------------------------------------*
* 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)
pixaaExtendArray(paa);
paa->pixa[n] = pixac;
paa->n++;
return 0;
}
/*!
* \brief pixaaExtendArray()
*
* \param[in] paa
* \return 0 if OK; 1 on error
*/
l_ok
pixaaExtendArray(PIXAA *paa)
{
PROCNAME("pixaaExtendArray");
if (!paa)
return ERROR_INT("paa not defined", procName, 1);
if ((paa->pixa = (PIXA **)reallocNew((void **)&paa->pixa,
sizeof(PIXA *) * paa->nalloc,
2 * sizeof(PIXA *) * paa->nalloc)) == NULL)
return ERROR_INT("new ptr array not returned", procName, 1);
paa->nalloc = 2 * paa->nalloc;
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
*
* <pre>
* Notes:
* (1) The box can be used, for example, to hold the support region
* of a pixa that is being added to the pixaa.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) If paa is empty, a returned na will also be empty.
* </pre>
*/
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
*
* <pre>
* 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
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) It is considered to be an error if any pixa have no pix.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) It is considered to be an error if any pixa have no pix.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) Does not require boxa associated with each pixa to be full.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) This destroys all pixa in the pixaa, and nulls the ptrs
* in the pixa ptr array.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) The pix are stored in the file as png.
* If the png library is not linked, this will fail.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) The pix are stored in the file as png.
* If the png library is not linked, this will fail.
* </pre>
*/
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 ((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
*
* <pre>
* 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().
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) The pix are stored in the file as png.
* If the png library is not linked, this will fail.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) The pix are stored in the file as png.
* If the png library is not linked, this will fail.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) Serializes a pixa in memory and puts the result in a buffer.
* </pre>
*/
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
*
* <pre>
* 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.
* </pre>
*/
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.
*
* <pre>
* 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.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) The pix are stored in the file as png.
* If the png library is not linked, this will fail.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) The pix are stored in the file as png.
* If the png library is not linked, this will fail.
* </pre>
*/
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 ((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
*
* <pre>
* Notes:
* (1) The pix are stored in the file as png.
* If the png library is not linked, this will fail.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) The pix are stored in the file as png.
* If the png library is not linked, this will fail.
* </pre>
*/
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
*
* <pre>
* Notes:
* (1) Serializes a pixaa in memory and puts the result in a buffer.
* </pre>
*/
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;
}