blob: e073d4cf29e8fd8f28ec73472274ad087fee0111 [file] [log] [blame]
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
- This software is distributed in the hope that it will be
- useful, but with NO WARRANTY OF ANY KIND.
- No author or distributor accepts responsibility to anyone for the
- consequences of using this software, or for whether it serves any
- particular purpose or works at all, unless he or she says so in
- writing. Everyone is granted permission to copy, modify and
- redistribute this source code, for commercial or non-commercial
- purposes, with the following restrictions: (1) the origin of this
- source code must not be misrepresented; (2) modified versions must
- be plainly marked as such; and (3) this notice may not be removed
- or altered from any source or modified source distribution.
*====================================================================*/
/*
* pixabasic.c
*
* Pixa creation, destruction, copying
* PIXA *pixaCreate()
* PIXA *pixaCreateFromPix()
* PIXA *pixaCreateFromBoxa()
* PIXA *pixaSplitPix()
* void pixaDestroy()
* PIXA *pixaCopy()
*
* Pixa addition
* l_int32 pixaAddPix()
* l_int32 pixaExtendArray()
* l_int32 pixaAddBox()
*
* Pixa accessors
* l_int32 pixaGetCount()
* l_int32 pixaChangeRefcount()
* PIX *pixaGetPix()
* l_int32 pixaGetPixDimensions()
* PIX *pixaGetBoxa()
* l_int32 pixaGetBoxaCount()
* BOX *pixaGetBox()
* l_int32 pixaGetBoxGeometry()
* PIX **pixaGetPixArray()
*
* Pixa array modifiers
* l_int32 pixaReplacePix()
* l_int32 pixaInsertPix()
* l_int32 pixaRemovePix()
*
* Pixa combination
* PIXA *pixaJoin()
*
* Pixaa creation, destruction
* PIXAA *pixaaCreate()
* PIXAA *pixaaCreateFromPixa()
* void pixaaDestroy()
*
* Pixaa addition
* l_int32 pixaaAddPixa()
* l_int32 pixaaExtendArray()
* l_int32 pixaaAddBox()
*
* Pixaa accessors
* l_int32 pixaaGetCount()
* PIXA *pixaaGetPixa()
* BOXA *pixaaGetBoxa()
*
* Pixa serialized I/O
* PIXA *pixaRead()
* PIXA *pixaReadStream()
* l_int32 pixaWrite()
* l_int32 pixaWriteStream()
*
* Pixaa serialized I/O
* PIXAA *pixaaRead()
* PIXAA *pixaaReadStream()
* l_int32 pixaaWrite()
* l_int32 pixaaWriteStream()
*
*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allheaders.h"
static const l_int32 INITIAL_PTR_ARRAYSIZE = 20; /* n'import quoi */
/*---------------------------------------------------------------------*
* Pixa creation, destruction, copy *
*---------------------------------------------------------------------*/
/*!
* pixaCreate()
*
* Input: n (initial number of ptrs)
* Return: pixa, or null on error
*/
PIXA *
pixaCreate(l_int32 n)
{
PIXA *pixa;
PROCNAME("pixaCreate");
if (n <= 0)
n = INITIAL_PTR_ARRAYSIZE;
if ((pixa = (PIXA *)CALLOC(1, sizeof(PIXA))) == NULL)
return (PIXA *)ERROR_PTR("pixa not made", procName, NULL);
pixa->n = 0;
pixa->nalloc = n;
pixa->refcount = 1;
if ((pixa->pix = (PIX **)CALLOC(n, sizeof(PIX *))) == NULL)
return (PIXA *)ERROR_PTR("pix ptrs not made", procName, NULL);
if ((pixa->boxa = boxaCreate(n)) == NULL)
return (PIXA *)ERROR_PTR("boxa not made", procName, NULL);
return pixa;
}
/*!
* pixaCreateFromPix()
*
* Input: pixs (with individual components on a lattice)
* n (number of components)
* cellw (width of each cell)
* cellh (height of each cell)
* Return: pixa, or null on error
*
* Note: for bpp = 1, we truncate each retrieved pix to
* the ON pixels, which we assume for now start at (0,0)
*/
PIXA *
pixaCreateFromPix(PIX *pixs,
l_int32 n,
l_int32 cellw,
l_int32 cellh)
{
l_int32 w, h, d, nw, nh, i, j, index;
PIX *pix, *pixt;
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 ((pixt = pixCreate(cellw, cellh, d)) == NULL)
return (PIXA *)ERROR_PTR("pixt 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(pixt, 0, 0, cellw, cellh, PIX_SRC, pixs,
j * cellw, i * cellh);
if (d == 1 && !pixClipToForeground(pixt, &pix, NULL))
pixaAddPix(pixa, pix, L_INSERT);
else
pixaAddPix(pixa, pixt, L_COPY);
}
}
pixDestroy(&pixt);
return pixa;
}
/*!
* pixaCreateFromBoxa()
*
* Input: pixs
* boxa
* &cropwarn (<optional return> TRUE if the boxa extent
* is larger than pixs.
* Return: pixad, or null on error
*
* Notes:
* (1) This simply extracts from pixs the region corresponding to each
* box in the boxa.
* (2) The 3rd arg is optional. If the extent of the boxa exceeds the
* size of the pixa, so that some boxes are either clipped
* or entirely outside the pix, a warning is returned as TRUE.
* (3) pixad will have only the properly clipped elements, and
* the internal boxa will be correct.
*/
PIXA *
pixaCreateFromBoxa(PIX *pixs,
BOXA *boxa,
l_int32 *pcropwarn)
{
l_int32 i, n, 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);
n = boxaGetCount(boxa);
if ((pixad = pixaCreate(n)) == 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 = 0; i < n; 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;
}
/*!
* pixaSplitPix()
*
* Input: pixs (with individual components on a lattice)
* nx (number of mosaic cells horizontally)
* ny (number of mosaic cells vertically)
* borderwidth (of added border on all sides)
* bordercolor (in our RGBA format: 0xrrggbbaa)
* Return: pixa, or null on error
*
* Notes:
* (1) This is a variant on pixaCreateFromPix(), where we
* simply divide the image up into (approximately) equal
* subunits. If you want the subimages to have essentially
* the same aspect ratio as the input pix, use nx = ny.
* (2) If borderwidth is 0, we ignore the input bordercolor and
* redefine it to white.
* (3) The bordercolor is always used to initialize each tiled pix,
* so that if the src is clipped, the unblitted part will
* be this color. This avoids 1 pixel wide black stripes at the
* left and lower edges.
*/
PIXA *
pixaSplitPix(PIX *pixs,
l_int32 nx,
l_int32 ny,
l_int32 borderwidth,
l_uint32 bordercolor)
{
l_int32 w, h, d, cellw, cellh, i, j;
PIX *pixt;
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 ((pixt = pixCreate(cellw + 2 * borderwidth,
cellh + 2 * borderwidth, d)) == NULL)
return (PIXA *)ERROR_PTR("pixt not made", procName, NULL);
pixCopyColormap(pixt, pixs);
if (borderwidth == 0) { /* initialize full image to white */
if (d == 1)
pixClearAll(pixt);
else
pixSetAll(pixt);
}
else
pixSetAllArbitrary(pixt, bordercolor);
pixRasterop(pixt, borderwidth, borderwidth, cellw, cellh,
PIX_SRC, pixs, j * cellw, i * cellh);
pixaAddPix(pixa, pixt, L_INSERT);
}
}
return pixa;
}
/*!
* pixaDestroy()
*
* Input: &pixa (<can be nulled>)
* Return: void
*
* Notes:
* (1) Decrements the ref count and, if 0, destroys the pixa.
* (2) Always nulls the input ptr.
*/
void
pixaDestroy(PIXA **ppixa)
{
l_int32 i;
PIXA *pixa;
PROCNAME("pixaDestroy");
if (ppixa == NULL) {
L_WARNING("ptr address is NULL!", 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]);
FREE(pixa->pix);
boxaDestroy(&pixa->boxa);
FREE(pixa);
}
*ppixa = NULL;
return;
}
/*!
* pixaCopy()
*
* Input: pixas
* copyflag:
* 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
*
* Note: see pix.h for description of the copy types.
*/
PIXA *
pixaCopy(PIXA *pixa,
l_int32 copyflag)
{
l_int32 i;
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);
for (i = 0; i < pixa->n; i++) {
if (copyflag == L_COPY) {
pixc = pixaGetPix(pixa, i, L_COPY);
boxc = pixaGetBox(pixa, i, L_COPY);
}
else { /* copy-clone */
pixc = pixaGetPix(pixa, i, L_CLONE);
boxc = pixaGetBox(pixa, i, L_CLONE);
}
pixaAddPix(pixac, pixc, L_INSERT);
pixaAddBox(pixac, boxc, L_INSERT);
}
return pixac;
}
/*---------------------------------------------------------------------*
* Pixa addition *
*---------------------------------------------------------------------*/
/*!
* pixaAddPix()
*
* Input: pixa
* pix (to be added)
* copyflag (L_INSERT, L_COPY, L_CLONE)
* Return: 0 if OK; 1 on error
*/
l_int32
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;
}
/*!
* pixaExtendArray()
*
* Input: pixa
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) We extend the boxa array simultaneously. This is
* necessary in case we are NOT adding boxes simultaneously
* with adding pix. We always want the sizes of the
* pixa and boxa ptr arrays to be equal.
*/
l_int32
pixaExtendArray(PIXA *pixa)
{
PROCNAME("pixaExtendArray");
if (!pixa)
return ERROR_INT("pixa not defined", procName, 1);
if ((pixa->pix = (PIX **)reallocNew((void **)&pixa->pix,
sizeof(PIX *) * pixa->nalloc,
2 * sizeof(PIX *) * pixa->nalloc)) == NULL)
return ERROR_INT("new ptr array not returned", procName, 1);
pixa->nalloc = 2 * pixa->nalloc;
boxaExtendArray(pixa->boxa);
return 0;
}
/*!
* pixaAddBox()
*
* Input: pixa
* box
* copyflag (L_INSERT, L_COPY, L_CLONE)
* Return: 0 if OK, 1 on error
*/
l_int32
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;
}
/*---------------------------------------------------------------------*
* Pixa accessors *
*---------------------------------------------------------------------*/
/*!
* pixaGetCount()
*
* Input: 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;
}
/*!
* pixaChangeRefcount()
*
* Input: pixa
* Return: 0 if OK, 1 on error
*/
l_int32
pixaChangeRefcount(PIXA *pixa,
l_int32 delta)
{
PROCNAME("pixaChangeRefcount");
if (!pixa)
return ERROR_INT("pixa not defined", procName, 1);
pixa->refcount += delta;
return 0;
}
/*!
* pixaGetPix()
*
* Input: pixa
* index (to the index-th pix)
* accesstype (L_COPY or L_CLONE)
* Return: pix, or null on error
*/
PIX *
pixaGetPix(PIXA *pixa,
l_int32 index,
l_int32 accesstype)
{
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 (accesstype == L_COPY)
return pixCopy(NULL, pixa->pix[index]);
else if (accesstype == L_CLONE)
return pixClone(pixa->pix[index]);
else
return (PIX *)ERROR_PTR("invalid accesstype", procName, NULL);
}
/*!
* pixaGetPixDimensions()
*
* Input: pixa
* index (to the index-th box)
* &w, &h, &d (<optional return>; each can be null)
* Return: 0 if OK, 1 on error
*/
l_int32
pixaGetPixDimensions(PIXA *pixa,
l_int32 index,
l_int32 *pw,
l_int32 *ph,
l_int32 *pd)
{
PIX *pix;
PROCNAME("pixaGetPixDimensions");
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;
}
/*!
* pixaGetBoxa()
*
* Input: pixa
* 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);
}
/*!
* pixaGetBoxaCount()
*
* Input: 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);
}
/*!
* pixaGetBox()
*
* Input: pixa
* index (to the index-th pix)
* accesstype (L_COPY or L_CLONE)
* Return: box (if null, not automatically an error), or null on error
*
* Notes:
* (1) There is always a boxa with a pixa, and it is initialized so
* that each box ptr is NULL.
* (2) In general, we expect that there is either a box associated
* with each pix, or no boxes at all in the boxa.
* (3) Having no boxes is thus not an automatic error. Whether it
* is an actual error is determined by the calling program.
* If the caller expects to get a box, it is an error; see, e.g.,
* pixaGetBoxGeometry().
*/
BOX *
pixaGetBox(PIXA *pixa,
l_int32 index,
l_int32 accesstype)
{
BOX *box;
PROCNAME("pixaGetBox");
if (!pixa)
return (BOX *)ERROR_PTR("pixa not defined", procName, NULL);
if (!pixa->boxa)
return (BOX *)ERROR_PTR("boxa not defined", procName, NULL);
if (index < 0 || index >= pixa->boxa->n)
return (BOX *)ERROR_PTR("index not valid", procName, NULL);
if (accesstype != L_COPY && accesstype != L_CLONE)
return (BOX *)ERROR_PTR("invalid accesstype", procName, NULL);
box = pixa->boxa->box[index];
if (box) {
if (accesstype == L_COPY)
return boxCopy(box);
else /* accesstype == L_CLONE */
return boxClone(box);
}
else
return NULL;
}
/*!
* pixaGetBoxGeometry()
*
* Input: pixa
* index (to the index-th box)
* &x, &y, &w, &h (<optional return>; each can be null)
* Return: 0 if OK, 1 on error
*/
l_int32
pixaGetBoxGeometry(PIXA *pixa,
l_int32 index,
l_int32 *px,
l_int32 *py,
l_int32 *pw,
l_int32 *ph)
{
BOX *box;
PROCNAME("pixaGetBoxGeometry");
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;
}
/*!
* pixaGetPixArray()
*
* Input: pixa
* Return: pix array, or null on error
*
* Notes:
* (1) This returns a ptr to the actual array. The array is
* owned by the pixa, so it must not be destroyed.
* (2) The caller should always check if the return value is NULL
* before accessing any of the pix ptrs in this array!
*/
PIX **
pixaGetPixArray(PIXA *pixa)
{
PROCNAME("pixaGetPixArray");
if (!pixa)
return (PIX **)ERROR_PTR("pixa not defined", procName, NULL);
return pixa->pix;
}
/*---------------------------------------------------------------------*
* Pixa array modifiers *
*---------------------------------------------------------------------*/
/*!
* pixaReplacePix()
*
* Input: pixa
* index (to the index-th pix)
* pix (insert to replace existing one)
* box (<optional> insert to replace existing)
* Return: 0 if OK, 1 on error
*
* Notes:
* - In-place replacement of one pix
* - The previous pix at that location is destroyed
*/
l_int32
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;
}
/*!
* pixaInsertPix()
*
* Input: pixa
* index (at which pix is to be inserted)
* pixs (new pix to be inserted)
* box (<optional> new box to be inserted)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This shifts pixa[i] --> pixa[i + 1] for all i >= index,
* and then inserts at pixa[index].
* (2) To insert at the beginning of the array, set index = 0.
* (3) It should not be used repeatedly on large arrays,
* because the function is O(n).
* (4) To append a pix to a pixa, it's easier to use pixaAddPix().
*/
l_int32
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;
}
/*!
* pixaRemovePix()
*
* Input: pixa
* index (of pix to be removed)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This shifts pixa[i] --> pixa[i - 1] for all i > index.
* (2) It should not be used repeatedly on large arrays,
* because the function is O(n).
* (3) The corresponding box is removed as well, if it exists.
*/
l_int32
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;
}
/*---------------------------------------------------------------------*
* Pixa combination *
*---------------------------------------------------------------------*/
/*!
* pixaJoin()
*
* Input: pixad (dest pixa; add to this one)
* pixas (source pixa; add from this one)
* istart (starting index in nas)
* iend (ending index in nas; use 0 to cat all)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This appends a clone of each indicated pix in pixas to pixad
* (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
* (3) iend <= 0 means 'read to the end'
*/
l_int32
pixaJoin(PIXA *pixad,
PIXA *pixas,
l_int32 istart,
l_int32 iend)
{
l_int32 ns, i;
BOXA *boxas, *boxad;
PIX *pix;
PROCNAME("pixaJoin");
if (!pixad)
return ERROR_INT("pixad not defined", procName, 1);
if (!pixas)
return ERROR_INT("pixas not defined", procName, 1);
ns = pixaGetCount(pixas);
if (istart < 0)
istart = 0;
if (istart >= ns)
return ERROR_INT("istart out of bounds", procName, 1);
if (iend <= 0)
iend = ns - 1;
if (iend >= ns)
return ERROR_INT("iend out of bounds", procName, 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);
boxaJoin(boxad, boxas, 0, 0);
boxaDestroy(&boxas); /* just the clones */
boxaDestroy(&boxad);
return 0;
}
/*---------------------------------------------------------------------*
* Pixaa creation and destruction *
*---------------------------------------------------------------------*/
/*!
* pixaaCreate()
*
* Input: n (initial number of pixa ptrs)
* Return: pixaa, or null on error
*
* Notes:
* (1) A pixaa provides a 2-level hierarchy of images.
* A common use is for segmentation masks, which are
* inexpensive to store in png format.
* (2) For example, suppose you want a mask for each textline
* in a two-column page. The textline masks for each column
* can be represented by a pixa, of which there are 2 in the pixaa.
* The boxes for the textline mask components within a column
* can have their origin referred to the column rather than the page.
* Then the boxa field can be used to represent the two box (regions)
* for the columns, and the (x,y) components of each box can
* be used to get the absolute position of the textlines on
* the page.
*/
PIXAA *
pixaaCreate(l_int32 n)
{
PIXAA *pixaa;
PROCNAME("pixaaCreate");
if (n <= 0)
n = INITIAL_PTR_ARRAYSIZE;
if ((pixaa = (PIXAA *)CALLOC(1, sizeof(PIXAA))) == NULL)
return (PIXAA *)ERROR_PTR("pixaa not made", procName, NULL);
pixaa->n = 0;
pixaa->nalloc = n;
if ((pixaa->pixa = (PIXA **)CALLOC(n, sizeof(PIXA *))) == NULL)
return (PIXAA *)ERROR_PTR("pixa ptrs not made", procName, NULL);
pixaa->boxa = boxaCreate(n);
return pixaa;
}
/*!
* pixaaCreateFromPixa()
*
* Input: pixa
* n (number specifying subdivision of pixa)
* type (L_CHOOSE_CONSECUTIVE, L_CHOOSE_SKIP_BY)
* copyflag (L_CLONE, L_COPY)
* Return: pixaa, or null on error
*
* Notes:
* (1) This subdivides a pixa into a set of smaller pixa that
* are accumulated into a pixaa.
* (2) If type == L_CHOOSE_CONSECUTIVE, the first 'n' pix are
* put in a pixa and added to pixaa, then the next 'n', etc.
* If type == L_CHOOSE_SKIP_BY, the first pixa is made by
* aggregating pix[0], pix[n], pix[2*n], etc.
* (3) The copyflag specifies if each new pix is a copy or a clone.
*/
PIXAA *
pixaaCreateFromPixa(PIXA *pixa,
l_int32 n,
l_int32 type,
l_int32 copyflag)
{
l_int32 count, i, j, npixa;
PIX *pix;
PIXA *pixat;
PIXAA *pixaa;
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 (L_CHOOSE_CONSECUTIVE)
npixa = (count + n - 1) / n;
else /* L_CHOOSE_SKIP_BY */
npixa = L_MIN(n, count);
pixaa = 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(pixaa, pixat, L_INSERT);
}
if (i % n != 0)
pixaaAddPixa(pixaa, 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(pixaa, pixat, L_INSERT);
}
}
return pixaa;
}
/*!
* pixaaDestroy()
*
* Input: &pixaa <to be nulled>
* Return: void
*/
void
pixaaDestroy(PIXAA **ppixaa)
{
l_int32 i;
PIXAA *pixaa;
PROCNAME("pixaaDestroy");
if (ppixaa == NULL) {
L_WARNING("ptr address is NULL!", procName);
return;
}
if ((pixaa = *ppixaa) == NULL)
return;
for (i = 0; i < pixaa->n; i++)
pixaDestroy(&pixaa->pixa[i]);
FREE(pixaa->pixa);
boxaDestroy(&pixaa->boxa);
FREE(pixaa);
*ppixaa = NULL;
return;
}
/*---------------------------------------------------------------------*
* Pixaa addition *
*---------------------------------------------------------------------*/
/*!
* pixaaAddPixa()
*
* Input: pixaa
* pixa (to be added)
* 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_int32
pixaaAddPixa(PIXAA *pixaa,
PIXA *pixa,
l_int32 copyflag)
{
l_int32 n;
PIXA *pixac;
PROCNAME("pixaaAddPixa");
if (!pixaa)
return ERROR_INT("pixaa 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(pixaa);
if (n >= pixaa->nalloc)
pixaaExtendArray(pixaa);
pixaa->pixa[n] = pixac;
pixaa->n++;
return 0;
}
/*!
* pixaaExtendArray()
*
* Input: pixaa
* Return: 0 if OK; 1 on error
*/
l_int32
pixaaExtendArray(PIXAA *pixaa)
{
PROCNAME("pixaaExtendArray");
if (!pixaa)
return ERROR_INT("pixaa not defined", procName, 1);
if ((pixaa->pixa = (PIXA **)reallocNew((void **)&pixaa->pixa,
sizeof(PIXA *) * pixaa->nalloc,
2 * sizeof(PIXA *) * pixaa->nalloc)) == NULL)
return ERROR_INT("new ptr array not returned", procName, 1);
pixaa->nalloc = 2 * pixaa->nalloc;
return 0;
}
/*!
* pixaaAddBox()
*
* Input: pixaa
* box
* copyflag (L_INSERT, L_COPY, L_CLONE)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) The box can be used, for example, to hold the support region
* of a pixa that is being added to the pixaa.
*/
l_int32
pixaaAddBox(PIXAA *pixaa,
BOX *box,
l_int32 copyflag)
{
PROCNAME("pixaaAddBox");
if (!pixaa)
return ERROR_INT("pixaa 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(pixaa->boxa, box, copyflag);
return 0;
}
/*---------------------------------------------------------------------*
* Pixaa accessors *
*---------------------------------------------------------------------*/
/*!
* pixaaGetCount()
*
* Input: pixaa
* Return: count, or 0 if no pixaa
*/
l_int32
pixaaGetCount(PIXAA *pixaa)
{
PROCNAME("pixaaGetCount");
if (!pixaa)
return ERROR_INT("pixaa not defined", procName, 0);
return pixaa->n;
}
/*!
* pixaaGetPixa()
*
* Input: pixaa
* index (to the index-th pixa)
* accesstype (L_COPY, L_CLONE, L_COPY_CLONE)
* Return: pixa, or null on error
*
* Notes:
* (1) L_COPY makes a new pixa with a copy of every pix
* (2) L_CLONE just makes a new reference to the pixa,
* and bumps the counter. You would use this, for example,
* when you need to extract some data from a pix within a
* pixa within a pixaa.
* (3) L_COPY_CLONE makes a new pixa with a clone of every pix
* and box
* (4) In all cases, you must invoke pixaDestroy() on the returned pixa
*/
PIXA *
pixaaGetPixa(PIXAA *pixaa,
l_int32 index,
l_int32 accesstype)
{
PIXA *pixa;
PROCNAME("pixaaGetPixa");
if (!pixaa)
return (PIXA *)ERROR_PTR("pixaa not defined", procName, NULL);
if (index < 0 || index >= pixaa->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 = pixaa->pixa[index]) == NULL) /* shouldn't happen! */
return (PIXA *)ERROR_PTR("no pixa[index]", procName, NULL);
return pixaCopy(pixa, accesstype);
}
/*!
* pixaaGetBoxa()
*
* Input: pixaa
* accesstype (L_COPY, L_CLONE)
* Return: boxa, or null on error
*
* Notes:
* (1) L_COPY returns a copy; L_CLONE returns a new reference to the boxa.
* (2) In both cases, invoke boxaDestroy() on the returned boxa.
*/
BOXA *
pixaaGetBoxa(PIXAA *pixaa,
l_int32 accesstype)
{
PROCNAME("pixaaGetBoxa");
if (!pixaa)
return (BOXA *)ERROR_PTR("pixaa not defined", procName, NULL);
if (accesstype != L_COPY && accesstype != L_CLONE)
return (BOXA *)ERROR_PTR("invalid access type", procName, NULL);
return boxaCopy(pixaa->boxa, accesstype);
}
/*---------------------------------------------------------------------*
* Pixa serialized I/O *
*---------------------------------------------------------------------*/
/*!
* pixaRead()
*
* Input: filename
* Return: pixa, or null on error
*
* Notes:
* (1) The pix are stored in the file as png.
*/
PIXA *
pixaRead(const char *filename)
{
FILE *fp;
PIXA *pixa;
PROCNAME("pixaRead");
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);
if ((pixa = pixaReadStream(fp)) == NULL) {
fclose(fp);
return (PIXA *)ERROR_PTR("pixa not read", procName, NULL);
}
fclose(fp);
return pixa;
}
/*!
* pixaReadStream()
*
* Input: stream
* Return: pixa, or null on error
*/
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 */
return (PIXA *)ERROR_PTR("no libpng: can't read data", procName, NULL);
#else
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 ((pixa = pixaCreate(n)) == NULL)
return (PIXA *)ERROR_PTR("pixa not made", procName, NULL);
if ((boxa = boxaReadStream(fp)) == NULL)
return (PIXA *)ERROR_PTR("boxa 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)
return (PIXA *)ERROR_PTR("res reading", procName, NULL);
if ((pix = pixReadStreamPng(fp)) == NULL)
return (PIXA *)ERROR_PTR("pix not read", procName, NULL);
pixSetXRes(pix, xres);
pixSetYRes(pix, yres);
pixaAddPix(pixa, pix, L_INSERT);
}
return pixa;
#endif /* !HAVE_LIBPNG */
}
/*!
* pixaWrite()
*
* Input: filename
* pixa
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) The pix are written to file in png format.
*/
l_int32
pixaWrite(const char *filename,
PIXA *pixa)
{
FILE *fp;
PROCNAME("pixaWrite");
if (!filename)
return ERROR_INT("filename not defined", procName, 1);
if (!pixa)
return ERROR_INT("pixa not defined", procName, 1);
if ((fp = fopen(filename, "w")) == NULL)
return ERROR_INT("stream not opened", procName, 1);
if (pixaWriteStream(fp, pixa))
return ERROR_INT("pixa not written to stream", procName, 1);
fclose(fp);
return 0;
}
/*!
* pixaWriteStream()
*
* Input: stream
* pixa
* Return: 0 if OK, 1 on error
*/
l_int32
pixaWriteStream(FILE *fp,
PIXA *pixa)
{
l_int32 n, i;
PIX *pix;
PROCNAME("pixaWriteStream");
#if !HAVE_LIBPNG /* defined in environ.h */
return ERROR_INT("no libpng: can't write data", procName, 1);
#else
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;
#endif /* !HAVE_LIBPNG */
}
/*---------------------------------------------------------------------*
* Pixaa serialized I/O *
*---------------------------------------------------------------------*/
/*!
* pixaaRead()
*
* Input: filename
* Return: pixaa, or null on error
*
* Notes:
* (1) The pix are stored in the file as png.
*/
PIXAA *
pixaaRead(const char *filename)
{
FILE *fp;
PIXAA *pixaa;
PROCNAME("pixaaRead");
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);
if ((pixaa = pixaaReadStream(fp)) == NULL) {
fclose(fp);
return (PIXAA *)ERROR_PTR("pixaa not read", procName, NULL);
}
fclose(fp);
return pixaa;
}
/*!
* pixaaReadStream()
*
* Input: stream
* Return: pixaa, or null on error
*/
PIXAA *
pixaaReadStream(FILE *fp)
{
l_int32 n, i, version;
l_int32 ignore;
BOXA *boxa;
PIXA *pixa;
PIXAA *pixaa;
PROCNAME("pixaaReadStream");
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 ((pixaa = pixaaCreate(n)) == NULL)
return (PIXAA *)ERROR_PTR("pixaa not made", procName, NULL);
if ((boxa = boxaReadStream(fp)) == NULL)
return (PIXAA *)ERROR_PTR("boxa not made", procName, NULL);
boxaDestroy(&pixaa->boxa);
pixaa->boxa = boxa;
for (i = 0; i < n; i++) {
if ((fscanf(fp, "\n\n --------------- pixa[%d] ---------------\n",
&ignore)) != 1) {
return (PIXAA *)ERROR_PTR("text reading", procName, NULL);
}
if ((pixa = pixaReadStream(fp)) == NULL)
return (PIXAA *)ERROR_PTR("pixa not read", procName, NULL);
pixaaAddPixa(pixaa, pixa, L_INSERT);
}
return pixaa;
}
/*!
* pixaaWrite()
*
* Input: filename
* pixaa
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) Serialization of pixaa; the pix are written in png
*/
l_int32
pixaaWrite(const char *filename,
PIXAA *pixaa)
{
FILE *fp;
PROCNAME("pixaaWrite");
if (!filename)
return ERROR_INT("filename not defined", procName, 1);
if (!pixaa)
return ERROR_INT("pixaa not defined", procName, 1);
if ((fp = fopen(filename, "w")) == NULL)
return ERROR_INT("stream not opened", procName, 1);
if (pixaaWriteStream(fp, pixaa))
return ERROR_INT("pixaa not written to stream", procName, 1);
fclose(fp);
return 0;
}
/*!
* pixaaWriteStream()
*
* Input: stream
* pixaa
* Return: 0 if OK, 1 on error
*/
l_int32
pixaaWriteStream(FILE *fp,
PIXAA *pixaa)
{
l_int32 n, i;
PIXA *pixa;
PROCNAME("pixaaWriteStream");
if (!fp)
return ERROR_INT("stream not defined", procName, 1);
if (!pixaa)
return ERROR_INT("pixaa not defined", procName, 1);
n = pixaaGetCount(pixaa);
fprintf(fp, "\nPixaa Version %d\n", PIXAA_VERSION_NUMBER);
fprintf(fp, "Number of pixa = %d\n", n);
boxaWriteStream(fp, pixaa->boxa);
for (i = 0; i < n; i++) {
if ((pixa = pixaaGetPixa(pixaa, 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;
}