mirror of http://192.168.1.51:8099/lmh188/twain3.0
3244 lines
79 KiB
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;
|
||
|
}
|
||
|
|