ultimatepp/bazaar/PixRaster/lib/pixcomp.c
micio a74e1468c9 PixRaster : updated Leptonica library to version 1.65
git-svn-id: svn://ultimatepp.org/upp/trunk@2598 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2010-08-07 23:38:03 +00:00

1586 lines
47 KiB
C

/*====================================================================*
- 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.
*====================================================================*/
/*
* pixcomp.c
*
* Pixcomp creation and destruction
* PIXC *pixcompCreateFromPix()
* PIXC *pixcompCreateFromString()
* PIXC *pixcompCreateFromFile()
* void pixcompDestroy()
* Pixcomp accessors
* l_int32 pixcompGetDimensions()
* l_int32 pixcompDetermineFormat()
*
* Pixcomp conversion to Pix
* PIX *pixCreateFromPixcomp()
*
* Pixacomp creation and destruction
* PIXAC *pixacompCreate()
* PIXAC *pixacompCreateInitialized()
* PIXAC *pixacompCreateFromPixa()
* PIXAC *pixacompCreateFromFiles()
* PIXAC *pixacompCreateFromSA()
* void pixacompDestroy()
*
* Pixacomp addition/replacement
* l_int32 pixacompAddPix()
* l_int32 pixacompAddPixcomp()
* l_int32 pixacompExtendArray()
* l_int32 pixacompReplacePix()
* l_int32 pixacompReplacePixcomp()
* l_int32 pixacompAddBox()
*
* Pixacomp accessors
* l_int32 pixacompGetCount()
* PIXC *pixacompGetPixcomp()
* PIX *pixacompGetPix()
* l_int32 pixacompGetPixDimensions()
* BOXA *pixacompGetBoxa()
* l_int32 pixacompGetBoxaCount()
* BOX *pixacompGetBox()
* l_int32 pixacompGetBoxGeometry()
*
* Pixacomp conversion to Pixa
* PIXA *pixaCreateFromPixacomp()
*
* Pixacomp serialized I/O
* PIXAC *pixacompRead()
* PIXAC *pixacompReadStream()
* l_int32 pixacompWrite()
* l_int32 pixacompWriteStream()
*
* Output for debugging
* l_int32 pixacompWriteStreamInfo()
* l_int32 pixcompWriteStreamInfo()
* PIX *pixacompDisplayTiledAndScaled()
*
* The Pixacomp is an array of Pixcomp, where each Pixcomp is a compressed
* string of the image. We don't use reference counting here.
* The basic application is to allow a large array of highly
* compressible images to reside in memory. We purposely don't
* reuse the Pixa for this, to avoid confusion and programming errors.
*
* Three compression formats are used: g4, png and jpeg.
* The compression type can be either specified or defaulted.
* If specified and it is not possible to compress (for example,
* you specify a jpeg on a 1 bpp image or one with a colormap),
* the compression type defaults to png.
*
* The serialized version of the Pixacomp is similar to that for
* a Pixa, except that each Pixcomp can be compressed by one of
* tiffg4, png, or jpeg. Unlike serialization of the Pixa,
* serialization of the Pixacomp does not require any imaging
* libraries because it simply reads and writes the compressed data.
*
* For random insertion and replacement of pixcomp into a pixcomp,
* initialize a fully populated array using pixacompCreateInitialized().
* Then use pixacompReplacePix() or pixacompReplacePixcomp() for
* the random insertion.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allheaders.h"
static const l_int32 INITIAL_PTR_ARRAYSIZE = 20; /* n'import quoi */
/* These two globals are defined in writefile.c */
extern l_int32 NumImageFileFormatExtensions;
extern const char *ImageFileFormatExtensions[];
/*---------------------------------------------------------------------*
* Pixcomp creation and destruction *
*---------------------------------------------------------------------*/
/*!
* pixcompCreateFromPix()
*
* Input: pix
* comptype (IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG)
* Return: pixc, or null on error
*
* Notes:
* (1) Use @comptype == IFF_DEFAULT to have the compression
* type automatically determined.
*/
PIXC *
pixcompCreateFromPix(PIX *pix,
l_int32 comptype)
{
size_t size;
char *text;
l_int32 ret, format;
l_uint8 *data;
PIXC *pixc;
PROCNAME("pixcompCreateFromPix");
if (!pix)
return (PIXC *)ERROR_PTR("pix not defined", procName, NULL);
if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
return (PIXC *)ERROR_PTR("invalid comptype", procName, NULL);
if ((pixc = (PIXC *)CALLOC(1, sizeof(PIXC))) == NULL)
return (PIXC *)ERROR_PTR("pixc not made", procName, NULL);
pixGetDimensions(pix, &pixc->w, &pixc->h, &pixc->d);
pixGetResolution(pix, &pixc->xres, &pixc->yres);
if (pixGetColormap(pix))
pixc->cmapflag = 1;
if ((text = pixGetText(pix)) != NULL)
pixc->text = stringNew(text);
pixcompDetermineFormat(comptype, pixc->d, pixc->cmapflag, &format);
pixc->comptype = format;
ret = pixWriteMem(&data, &size, pix, format);
if (ret) {
L_ERROR("write to memory failed", procName);
pixcompDestroy(&pixc);
return NULL;
}
pixc->data = data;
pixc->size = (l_int32)size;
return pixc;
}
/*!
* pixcompCreateFromString()
*
* Input: data (compressed string)
* size (number of bytes)
* copyflag (L_INSERT or L_COPY)
* Return: pixc, or null on error
*
* Notes:
* (1) This works when the compressed string is png, jpeg or tiffg4.
* (2) The copyflag determines if the data in the new Pixcomp is
* a copy of the input data.
*/
PIXC *
pixcompCreateFromString(l_uint8 *data,
l_int32 size,
l_int32 copyflag)
{
l_int32 format, w, h, d, bps, spp, iscmap;
PIXC *pixc;
PROCNAME("pixcompCreateFromString");
if (!data)
return (PIXC *)ERROR_PTR("data not defined", procName, NULL);
if (copyflag != L_INSERT && copyflag != L_COPY)
return (PIXC *)ERROR_PTR("invalid copyflag", procName, NULL);
if (pixReadHeaderMem(data, (size_t)size, &format, &w, &h,
&bps, &spp, &iscmap) == 1)
return (PIXC *)ERROR_PTR("header data not read", procName, NULL);
if ((pixc = (PIXC *)CALLOC(1, sizeof(PIXC))) == NULL)
return (PIXC *)ERROR_PTR("pixc not made", procName, NULL);
d = (spp == 3) ? 32 : bps * spp;
pixc->w = w;
pixc->h = h;
pixc->d = d;
pixc->comptype = format;
pixc->cmapflag = iscmap;
if (copyflag == L_INSERT)
pixc->data = data;
else
pixc->data = arrayCopy(data, size);
pixc->size = size;
return pixc;
}
/*!
* pixcompCreateFromFile()
*
* Input: filename
* comptype (IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG)
* Return: pixc, or null on error
*
* Notes:
* (1) Use @comptype == IFF_DEFAULT to have the compression
* type automatically determined.
* (2) If the comptype is invalid for this file, the default will
* be substituted.
*/
PIXC *
pixcompCreateFromFile(const char *filename,
l_int32 comptype)
{
l_uint8 *data;
l_int32 nbytes, format;
FILE *fp;
PIX *pix;
PIXC *pixc;
PROCNAME("pixcompCreateFromFile");
if (!filename)
return (PIXC *)ERROR_PTR("filename not defined", procName, NULL);
if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
return (PIXC *)ERROR_PTR("invalid comptype", procName, NULL);
if ((fp = fopenReadStream(filename)) == NULL)
return (PIXC *)ERROR_PTR("image file not found", procName, NULL);
findFileFormat(fp, &format);
fclose(fp);
/* Can we accept the encoded file directly? Remember that
* png is the "universal" compression type, so if requested
* it takes precedence. Otherwise, if the file is already
* compressed in g4 or jpeg, just accept the string. */
if ((format == IFF_TIFF_G4 && comptype != IFF_PNG) ||
(format == IFF_JFIF_JPEG && comptype != IFF_PNG))
comptype = format;
if (comptype != IFF_DEFAULT && comptype == format) {
data = arrayRead(filename, &nbytes);
if ((pixc = pixcompCreateFromString(data, nbytes, L_INSERT)) == NULL) {
FREE(data);
return (PIXC *)ERROR_PTR("pixc not made (string)", procName, NULL);
}
return pixc;
}
/* Need to recompress in the default format */
if ((pix = pixRead(filename)) == NULL)
return (PIXC *)ERROR_PTR("pix not read", procName, NULL);
if ((pixc = pixcompCreateFromPix(pix, comptype)) == NULL) {
pixDestroy(&pix);
return (PIXC *)ERROR_PTR("pixc not made", procName, NULL);
}
pixDestroy(&pix);
return pixc;
}
/*!
* pixcompDestroy()
*
* Input: &pixc <will be nulled>
* Return: void
*
* Notes:
* (1) Always nulls the input ptr.
*/
void
pixcompDestroy(PIXC **ppixc)
{
PIXC *pixc;
PROCNAME("pixcompDestroy");
if (!ppixc) {
L_WARNING("ptr address is null!", procName);
return;
}
if ((pixc = *ppixc) == NULL)
return;
FREE(pixc->data);
if (pixc->text)
FREE(pixc->text);
FREE(pixc);
*ppixc = NULL;
return;
}
/*---------------------------------------------------------------------*
* Pixcomp accessors *
*---------------------------------------------------------------------*/
/*!
* pixcompGetDimensions()
*
* Input: pixc
* &w, &h, &d (<optional return>)
* Return: 0 if OK, 1 on error
*/
l_int32
pixcompGetDimensions(PIXC *pixc,
l_int32 *pw,
l_int32 *ph,
l_int32 *pd)
{
PROCNAME("pixcompGetDimensions");
if (!pixc)
return ERROR_INT("pixc not defined", procName, 1);
if (pw) *pw = pixc->w;
if (ph) *ph = pixc->h;
if (pd) *pd = pixc->d;
return 0;
}
/*!
* pixcompDetermineFormat()
*
* Input: comptype (IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG)
* d (pix depth)
* cmapflag (1 if pix to be compressed as a colormap; 0 otherwise)
* &format (return IFF_TIFF, IFF_PNG or IFF_JFIF_JPEG)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This determines the best format for a pix, given both
* the request (@comptype) and the image characteristics.
* (2) If @comptype == IFF_DEFAULT, this does not necessarily result
* in png encoding. Instead, it returns one of the three formats
* that is both valid and most likely to give best compression.
* (3) If the pix cannot be compressed by the input value of
* @comptype, this selects IFF_PNG, which can compress all pix.
*/
l_int32
pixcompDetermineFormat(l_int32 comptype,
l_int32 d,
l_int32 cmapflag,
l_int32 *pformat)
{
PROCNAME("pixcompDetermineFormat");
if (!pformat)
return ERROR_INT("&format not defined", procName, 1);
*pformat = IFF_PNG; /* init value and default */
if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
return ERROR_INT("invalid comptype", procName, 1);
if (comptype == IFF_DEFAULT) {
if (d == 1)
*pformat = IFF_TIFF_G4;
else if (d == 16)
*pformat = IFF_PNG;
else if (d >= 8 && !cmapflag)
*pformat = IFF_JFIF_JPEG;
}
else if (comptype == IFF_TIFF_G4 && d == 1)
*pformat = IFF_TIFF_G4;
else if (comptype == IFF_JFIF_JPEG && d >= 8 && !cmapflag)
*pformat = IFF_JFIF_JPEG;
return 0;
}
/*---------------------------------------------------------------------*
* Pixcomp conversion to Pix *
*---------------------------------------------------------------------*/
/*!
* pixCreateFromPixcomp()
*
* Input: pixc
* Return: pix, or null on error
*/
PIX *
pixCreateFromPixcomp(PIXC *pixc)
{
l_int32 w, h, d, cmapinpix, format;
PIX *pix;
PROCNAME("pixCreateFromPixcomp");
if (!pixc)
return (PIX *)ERROR_PTR("pixc not defined", procName, NULL);
if ((pix = pixReadMem(pixc->data, pixc->size)) == NULL)
return (PIX *)ERROR_PTR("pix not read", procName, NULL);
pixSetResolution(pix, pixc->xres, pixc->yres);
if (pixc->text)
pixSetText(pix, pixc->text);
/* Check fields for consistency */
pixGetDimensions(pix, &w, &h, &d);
if (pixc->w != w) {
L_INFO_INT2("pix width %d != pixc width %d", procName, w, pixc->w);
L_ERROR_INT("pix width %d != pixc width", procName, w);
}
if (pixc->h != h)
L_ERROR_INT("pix height %d != pixc height", procName, h);
if (pixc->d != d) {
if (pixc->d == 16) /* we strip 16 --> 8 bpp by default */
L_WARNING_INT("pix depth %d != pixc depth 16", procName, d);
else
L_ERROR_INT("pix depth %d != pixc depth", procName, d);
}
cmapinpix = (pixGetColormap(pix) != NULL);
if ((cmapinpix && !pixc->cmapflag) || (!cmapinpix && pixc->cmapflag))
L_ERROR("pix cmap flag inconsistent", procName);
format = pixGetInputFormat(pix);
if (format != pixc->comptype) {
L_ERROR_INT("pix comptype %d not equal to pixc comptype",
procName, format);
}
return pix;
}
/*---------------------------------------------------------------------*
* Pixacomp creation and destruction *
*---------------------------------------------------------------------*/
/*!
* pixacompCreate()
*
* Input: n (initial number of ptrs)
* Return: pixac, or null on error
*/
PIXAC *
pixacompCreate(l_int32 n)
{
PIXAC *pixac;
PROCNAME("pixacompCreate");
if (n <= 0)
n = INITIAL_PTR_ARRAYSIZE;
if ((pixac = (PIXAC *)CALLOC(1, sizeof(PIXAC))) == NULL)
return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
pixac->n = 0;
pixac->nalloc = n;
if ((pixac->pixc = (PIXC **)CALLOC(n, sizeof(PIXC *))) == NULL)
return (PIXAC *)ERROR_PTR("pixc ptrs not made", procName, NULL);
if ((pixac->boxa = boxaCreate(n)) == NULL)
return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL);
return pixac;
}
/*!
* pixacompCreateInitialized()
*
* Input: n (initial number of ptrs)
* pix (initialize each ptr in pixacomp to this pix)
* comptype (IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG)
* Return: pixac, or null on error
*
* Notes:
* (1) Initializes a pixacomp to be fully populated with @pix.
* (2) Typically use a very small @pix (w = h = 1) with
* @comptype == IFF_TIFF_G4 for the initialization.
* (3) Example usage:
* Pix *pix = pixCreate(1, 1, 1);
* Pixacomp *pixac = pixacompCreateInitialized(50, pix, IFF_TIFF_G4);
* for (i = 0; i < 50; i++) {
* Pix *pixt = ...
* if (pixt)
* pixacompReplacePix(pixac, i, pixt, IFF_TIFF_G4);
* pixDestroy(&pixt);
* }
* The result is a fully populated pixac with selected pixt
* replacing the placeholders.
*/
PIXAC *
pixacompCreateInitialized(l_int32 n,
PIX *pix,
l_int32 comptype)
{
l_int32 i;
PIXC *pixc;
PIXAC *pixac;
PROCNAME("pixacompCreateInitialized");
if (n <= 0)
return (PIXAC *)ERROR_PTR("n must be > 0", procName, NULL);
if (!pix)
return (PIXAC *)ERROR_PTR("pix not defined", procName, NULL);
if ((pixac = pixacompCreate(n)) == NULL)
return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
for (i = 0; i < n; i++) {
pixc = pixcompCreateFromPix(pix, comptype);
pixacompAddPixcomp(pixac, pixc);
}
return pixac;
}
/*!
* pixacompCreateFromPixa()
*
* Input: pixa
* comptype (IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG)
* accesstype (L_COPY, L_CLONE, L_COPY_CLONE; for boxa)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) If @format == IFF_DEFAULT, the conversion format for each
* image is chosen automatically. Otherwise, we use the
* specified format unless it can't be done (e.g., jpeg
* for a 1, 2 or 4 bpp pix, or a pix with a colormap),
* in which case we use the default (assumed best) compression.
*/
PIXAC *
pixacompCreateFromPixa(PIXA *pixa,
l_int32 comptype,
l_int32 accesstype)
{
l_int32 i, n;
BOXA *boxa;
PIX *pix;
PIXAC *pixac;
PROCNAME("pixacompCreateFromPixa");
if (!pixa)
return (PIXAC *)ERROR_PTR("pixa not defined", procName, NULL);
if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
if (accesstype != L_COPY && accesstype != L_CLONE &&
accesstype != L_COPY_CLONE)
return (PIXAC *)ERROR_PTR("invalid accesstype", procName, NULL);
n = pixaGetCount(pixa);
if ((pixac = pixacompCreate(n)) == NULL)
return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
for (i = 0; i < n; i++) {
pix = pixaGetPix(pixa, i, L_CLONE);
pixacompAddPix(pixac, pix, comptype);
pixDestroy(&pix);
}
if ((boxa = pixaGetBoxa(pixa, accesstype)) != NULL) {
if (pixac->boxa) {
boxaDestroy(&pixac->boxa);
pixac->boxa = boxa;
}
}
return pixac;
}
/*!
* pixacompCreateFromFiles()
*
* Input: dirname
* substr (<optional> substring filter on filenames; can be null)
* comptype (IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG)
* Return: pixac, or null on error
*
* Notes:
* (1) @dirname is the full path for the directory.
* (2) @substr is the part of the file name (excluding
* the directory) that is to be matched. All matching
* filenames are read into the Pixa. If substr is NULL,
* all filenames are read into the Pixa.
* (3) Use @comptype == IFF_DEFAULT to have the compression
* type automatically determined for each file.
* (4) If the comptype is invalid for a file, the default will
* be substituted.
*/
PIXAC *
pixacompCreateFromFiles(const char *dirname,
const char *substr,
l_int32 comptype)
{
PIXAC *pixac;
SARRAY *sa;
PROCNAME("pixacompCreateFromFiles");
if (!dirname)
return (PIXAC *)ERROR_PTR("dirname not defined", procName, NULL);
if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL)
return (PIXAC *)ERROR_PTR("sa not made", procName, NULL);
pixac = pixacompCreateFromSA(sa, comptype);
sarrayDestroy(&sa);
return pixac;
}
/*!
* pixacompCreateFromSA()
*
* Input: sarray (full pathnames for all files)
* comptype (IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG)
* Return: pixac, or null on error
*
* Notes:
* (1) Use @comptype == IFF_DEFAULT to have the compression
* type automatically determined for each file.
* (2) If the comptype is invalid for a file, the default will
* be substituted.
*/
PIXAC *
pixacompCreateFromSA(SARRAY *sa,
l_int32 comptype)
{
char *str;
l_int32 i, n;
PIXC *pixc;
PIXAC *pixac;
PROCNAME("pixacompCreateFromSA");
if (!sa)
return (PIXAC *)ERROR_PTR("sarray not defined", procName, NULL);
if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
n = sarrayGetCount(sa);
pixac = pixacompCreate(n);
for (i = 0; i < n; i++) {
str = sarrayGetString(sa, i, L_NOCOPY);
if ((pixc = pixcompCreateFromFile(str, comptype)) == NULL) {
L_WARNING_STRING("pixc not read from file %s", procName, str);
continue;
}
pixacompAddPixcomp(pixac, pixc);
}
return pixac;
}
/*!
* pixacompDestroy()
*
* Input: &pixac (<to be nulled>)
* Return: void
*
* Notes:
* (1) Always nulls the input ptr.
*/
void
pixacompDestroy(PIXAC **ppixac)
{
l_int32 i;
PIXAC *pixac;
PROCNAME("pixacompDestroy");
if (ppixac == NULL) {
L_WARNING("ptr address is NULL!", procName);
return;
}
if ((pixac = *ppixac) == NULL)
return;
for (i = 0; i < pixac->n; i++)
pixcompDestroy(&pixac->pixc[i]);
FREE(pixac->pixc);
boxaDestroy(&pixac->boxa);
FREE(pixac);
*ppixac = NULL;
return;
}
/*---------------------------------------------------------------------*
* Pixacomp addition *
*---------------------------------------------------------------------*/
/*!
* pixacompAddPix()
*
* Input: pixac
* pix (to be added)
* comptype (IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG)
* Return: 0 if OK; 1 on error
*/
l_int32
pixacompAddPix(PIXAC *pixac,
PIX *pix,
l_int32 comptype)
{
l_int32 cmapflag, format;
PIXC *pixc;
PROCNAME("pixacompAddPix");
if (!pixac)
return ERROR_INT("pixac not defined", procName, 1);
if (!pix)
return ERROR_INT("pix not defined", procName, 1);
if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
return ERROR_INT("invalid format", procName, 1);
cmapflag = pixGetColormap(pix) ? 1 : 0;
pixcompDetermineFormat(comptype, pixGetDepth(pix), cmapflag, &format);
if ((pixc = pixcompCreateFromPix(pix, format)) == NULL)
return ERROR_INT("pixc not made", procName, 1);
pixacompAddPixcomp(pixac, pixc);
return 0;
}
/*!
* pixacompAddPixcomp()
*
* Input: pixac
* pixc (to be added by insertion)
* Return: 0 if OK; 1 on error
*/
l_int32
pixacompAddPixcomp(PIXAC *pixac,
PIXC *pixc)
{
l_int32 n;
PROCNAME("pixacompAddPixcomp");
if (!pixac)
return ERROR_INT("pixac not defined", procName, 1);
if (!pixc)
return ERROR_INT("pixc not defined", procName, 1);
n = pixac->n;
if (n >= pixac->nalloc)
pixacompExtendArray(pixac);
pixac->pixc[n] = pixc;
pixac->n++;
return 0;
}
/*!
* pixacompExtendArray()
*
* Input: pixac
* 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 pixc. We always want the sizes of the
* pixac and boxa ptr arrays to be equal.
*/
l_int32
pixacompExtendArray(PIXAC *pixac)
{
PROCNAME("pixacompExtendArray");
if (!pixac)
return ERROR_INT("pixac not defined", procName, 1);
if ((pixac->pixc = (PIXC **)reallocNew((void **)&pixac->pixc,
sizeof(PIXC *) * pixac->nalloc,
2 * sizeof(PIXC *) * pixac->nalloc)) == NULL)
return ERROR_INT("new ptr array not returned", procName, 1);
pixac->nalloc = 2 * pixac->nalloc;
boxaExtendArray(pixac->boxa);
return 0;
}
/*!
* pixacompReplacePix()
*
* Input: pixac
* index (of pixc within pixac to be replaced)
* pix (owned by the caller)
* comptype (IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) The input @pix is converted to a pixc, which is then inserted
* into the pixac.
*/
l_int32
pixacompReplacePix(PIXAC *pixac,
l_int32 index,
PIX *pix,
l_int32 comptype)
{
l_int32 n;
PIXC *pixc;
PROCNAME("pixacompReplacePix");
if (!pixac)
return ERROR_INT("pixac not defined", procName, 1);
n = pixacompGetCount(pixac);
if (index < 0 || index >= n)
return ERROR_INT("array index out of bounds", procName, 1);
if (!pix)
return ERROR_INT("pix not defined", procName, 1);
if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
return ERROR_INT("invalid format", procName, 1);
pixc = pixcompCreateFromPix(pix, comptype);
pixacompReplacePixcomp(pixac, index, pixc);
return 0;
}
/*!
* pixacompReplacePixcomp()
*
* Input: pixac
* index (of pixc within pixac to be replaced)
* pixc (to replace existing one, which is destroyed)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) The inserted @pixc is now owned by the pixac. The caller
* must not destroy it.
*/
l_int32
pixacompReplacePixcomp(PIXAC *pixac,
l_int32 index,
PIXC *pixc)
{
l_int32 n;
PIXC *pixct;
PROCNAME("pixacompReplacePixcomp");
if (!pixac)
return ERROR_INT("pixac not defined", procName, 1);
n = pixacompGetCount(pixac);
if (index < 0 || index >= n)
return ERROR_INT("array index out of bounds", procName, 1);
if (!pixc)
return ERROR_INT("pixc not defined", procName, 1);
pixct = pixacompGetPixcomp(pixac, index);
pixcompDestroy(&pixct);
pixac->pixc[index] = pixc; /* replace */
return 0;
}
/*!
* pixacompAddBox()
*
* Input: pixac
* box
* copyflag (L_INSERT, L_COPY)
* Return: 0 if OK, 1 on error
*/
l_int32
pixacompAddBox(PIXAC *pixac,
BOX *box,
l_int32 copyflag)
{
PROCNAME("pixacompAddBox");
if (!pixac)
return ERROR_INT("pixac not defined", procName, 1);
if (!box)
return ERROR_INT("box not defined", procName, 1);
if (copyflag != L_INSERT && copyflag != L_COPY)
return ERROR_INT("invalid copyflag", procName, 1);
boxaAddBox(pixac->boxa, box, copyflag);
return 0;
}
/*---------------------------------------------------------------------*
* Pixacomp accessors *
*---------------------------------------------------------------------*/
/*!
* pixacompGetCount()
*
* Input: pixac
* Return: count, or 0 if no pixa
*/
l_int32
pixacompGetCount(PIXAC *pixac)
{
PROCNAME("pixacompGetCount");
if (!pixac)
return ERROR_INT("pixac not defined", procName, 0);
return pixac->n;
}
/*!
* pixacompGetPixcomp()
*
* Input: pixac
* index (to the index-th pix)
* Return: pixc, or null on error
*
* Notes:
* (1) Important: this is just a ptr to the pixc owned by the pixac.
* Do not destroy unless you are replacing the pixc.
*/
PIXC *
pixacompGetPixcomp(PIXAC *pixac,
l_int32 index)
{
PROCNAME("pixacompGetPixcomp");
if (!pixac)
return (PIXC *)ERROR_PTR("pixac not defined", procName, NULL);
if (index < 0 || index >= pixac->n)
return (PIXC *)ERROR_PTR("index not valid", procName, NULL);
return pixac->pixc[index];
}
/*!
* pixacompGetPix()
*
* Input: pixac
* index (to the index-th pix)
* Return: pix, or null on error
*/
PIX *
pixacompGetPix(PIXAC *pixac,
l_int32 index)
{
PIXC *pixc;
PROCNAME("pixacompGetPix");
if (!pixac)
return (PIX *)ERROR_PTR("pixac not defined", procName, NULL);
if (index < 0 || index >= pixac->n)
return (PIX *)ERROR_PTR("index not valid", procName, NULL);
pixc = pixacompGetPixcomp(pixac, index);
return pixCreateFromPixcomp(pixc);
}
/*!
* pixacompGetPixDimensions()
*
* 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
pixacompGetPixDimensions(PIXAC *pixac,
l_int32 index,
l_int32 *pw,
l_int32 *ph,
l_int32 *pd)
{
PIXC *pixc;
PROCNAME("pixacompGetPixDimensions");
if (!pixac)
return ERROR_INT("pixac not defined", procName, 1);
if (index < 0 || index >= pixac->n)
return ERROR_INT("index not valid", procName, 1);
if ((pixc = pixac->pixc[index]) == NULL)
return ERROR_INT("pixc not found!", procName, 1);
pixcompGetDimensions(pixc, pw, ph, pd);
return 0;
}
/*!
* pixacompGetBoxa()
*
* Input: pixac
* accesstype (L_COPY, L_CLONE, L_COPY_CLONE)
* Return: boxa, or null on error
*/
BOXA *
pixacompGetBoxa(PIXAC *pixac,
l_int32 accesstype)
{
PROCNAME("pixacompGetBoxa");
if (!pixac)
return (BOXA *)ERROR_PTR("pixac not defined", procName, NULL);
if (!pixac->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(pixac->boxa, accesstype);
}
/*!
* pixacompGetBoxaCount()
*
* Input: pixac
* Return: count, or 0 on error
*/
l_int32
pixacompGetBoxaCount(PIXAC *pixac)
{
PROCNAME("pixacompGetBoxaCount");
if (!pixac)
return ERROR_INT("pixac not defined", procName, 0);
return boxaGetCount(pixac->boxa);
}
/*!
* pixacompGetBox()
*
* Input: pixac
* 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 pixac, 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 pixc, 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.,
* pixacGetBoxGeometry().
*/
BOX *
pixacompGetBox(PIXAC *pixac,
l_int32 index,
l_int32 accesstype)
{
BOX *box;
PROCNAME("pixacompGetBox");
if (!pixac)
return (BOX *)ERROR_PTR("pixac not defined", procName, NULL);
if (!pixac->boxa)
return (BOX *)ERROR_PTR("boxa not defined", procName, NULL);
if (index < 0 || index >= pixac->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 = pixac->boxa->box[index];
if (box) {
if (accesstype == L_COPY)
return boxCopy(box);
else /* accesstype == L_CLONE */
return boxClone(box);
}
else
return NULL;
}
/*!
* pixacompGetBoxGeometry()
*
* Input: pixac
* 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
pixacompGetBoxGeometry(PIXAC *pixac,
l_int32 index,
l_int32 *px,
l_int32 *py,
l_int32 *pw,
l_int32 *ph)
{
BOX *box;
PROCNAME("pixacompGetBoxGeometry");
if (!pixac)
return ERROR_INT("pixac not defined", procName, 1);
if (index < 0 || index >= pixac->n)
return ERROR_INT("index not valid", procName, 1);
if ((box = pixacompGetBox(pixac, index, L_CLONE)) == NULL)
return ERROR_INT("box not found!", procName, 1);
boxGetGeometry(box, px, py, pw, ph);
boxDestroy(&box);
return 0;
}
/*---------------------------------------------------------------------*
* Pixacomp conversion to Pixa *
*---------------------------------------------------------------------*/
/*!
* pixaCreateFromPixacomp()
*
* Input: pixac
* accesstype (L_COPY, L_CLONE, L_COPY_CLONE; for boxa)
* Return: pixa if OK, or null on error
*/
PIXA *
pixaCreateFromPixacomp(PIXAC *pixac,
l_int32 accesstype)
{
l_int32 i, n;
PIX *pix;
PIXA *pixa;
PROCNAME("pixaCreateFromPixacomp");
if (!pixac)
return (PIXA *)ERROR_PTR("pixac not defined", procName, NULL);
if (accesstype != L_COPY && accesstype != L_CLONE &&
accesstype != L_COPY_CLONE)
return (PIXA *)ERROR_PTR("invalid accesstype", procName, NULL);
n = pixacompGetCount(pixac);
if ((pixa = pixaCreate(n)) == NULL)
return (PIXA *)ERROR_PTR("pixa not made", procName, NULL);
for (i = 0; i < n; i++) {
if ((pix = pixacompGetPix(pixac, i)) == NULL) {
L_WARNING_INT("pix %d not made", procName, i);
continue;
}
pixaAddPix(pixa, pix, L_INSERT);
}
if (pixa->boxa) {
boxaDestroy(&pixa->boxa);
pixa->boxa = pixacompGetBoxa(pixac, accesstype);
}
return pixa;
}
/*---------------------------------------------------------------------*
* Pixacomp serialized I/O *
*---------------------------------------------------------------------*/
/*!
* pixacompRead()
*
* Input: filename
* Return: pixac, or null on error
*
* Notes:
* (1) Unlike the situation with serialized Pixa, where the image
* data is stored in png format, the Pixacomp image data
* can be stored in tiffg4, png and jpg formats.
*/
PIXAC *
pixacompRead(const char *filename)
{
FILE *fp;
PIXAC *pixac;
PROCNAME("pixacompRead");
if (!filename)
return (PIXAC *)ERROR_PTR("filename not defined", procName, NULL);
if ((fp = fopenReadStream(filename)) == NULL)
return (PIXAC *)ERROR_PTR("stream not opened", procName, NULL);
if ((pixac = pixacompReadStream(fp)) == NULL) {
fclose(fp);
return (PIXAC *)ERROR_PTR("pixac not read", procName, NULL);
}
fclose(fp);
return pixac;
}
/*!
* pixacompReadStream()
*
* Input: stream
* Return: pixac, or null on error
*/
PIXAC *
pixacompReadStream(FILE *fp)
{
l_uint8 *data;
l_int32 n, i, w, h, d, ignore;
l_int32 comptype, size, cmapflag, version, xres, yres;
BOXA *boxa;
PIXC *pixc;
PIXAC *pixac;
PROCNAME("pixacompReadStream");
if (!fp)
return (PIXAC *)ERROR_PTR("stream not defined", procName, NULL);
if (fscanf(fp, "\nPixacomp Version %d\n", &version) != 1)
return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL);
if (version != PIXACOMP_VERSION_NUMBER)
return (PIXAC *)ERROR_PTR("invalid pixacomp version", procName, NULL);
if (fscanf(fp, "Number of pixcomp = %d", &n) != 1)
return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL);
if ((pixac = pixacompCreate(n)) == NULL)
return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
if ((boxa = boxaReadStream(fp)) == NULL)
return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL);
boxaDestroy(&pixac->boxa); /* empty */
pixac->boxa = boxa;
for (i = 0; i < n; i++) {
if ((pixc = (PIXC *)CALLOC(1, sizeof(PIXC))) == NULL)
return (PIXAC *)ERROR_PTR("pixc not made", procName, NULL);
if (fscanf(fp, " Pixcomp[%d]: w = %d, h = %d, d = %d\n",
&ignore, &w, &h, &d) != 4)
return (PIXAC *)ERROR_PTR("size reading", procName, NULL);
if (fscanf(fp, " comptype = %d, size = %d, cmapflag = %d\n",
&comptype, &size, &cmapflag) != 3)
return (PIXAC *)ERROR_PTR("comptype/size reading", procName, NULL);
if (fscanf(fp, " xres = %d, yres = %d\n", &xres, &yres) != 2)
return (PIXAC *)ERROR_PTR("res reading", procName, NULL);
if ((data = (l_uint8 *)CALLOC(1, size)) == NULL)
return (PIXAC *)ERROR_PTR("calloc fail for data", procName, NULL);
fread(data, size, 1, fp);
pixc->w = w;
pixc->h = h;
pixc->d = d;
pixc->xres = xres;
pixc->yres = yres;
pixc->comptype = comptype;
pixc->cmapflag = cmapflag;
pixc->data = data;
pixc->size = size;
pixacompAddPixcomp(pixac, pixc);
}
return pixac;
}
/*!
* pixacompWrite()
*
* Input: filename
* pixac
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) Unlike the situation with serialized Pixa, where the image
* data is stored in png format, the Pixacomp image data
* can be stored in tiffg4, png and jpg formats.
*/
l_int32
pixacompWrite(const char *filename,
PIXAC *pixac)
{
FILE *fp;
PROCNAME("pixacompWrite");
if (!filename)
return ERROR_INT("filename not defined", procName, 1);
if (!pixac)
return ERROR_INT("pixacomp not defined", procName, 1);
if ((fp = fopen(filename, "w")) == NULL)
return ERROR_INT("stream not opened", procName, 1);
if (pixacompWriteStream(fp, pixac))
return ERROR_INT("pixacomp not written to stream", procName, 1);
fclose(fp);
return 0;
}
/*!
* pixacompWriteStream()
*
* Input: stream
* pixac
* Return: 0 if OK, 1 on error
*/
l_int32
pixacompWriteStream(FILE *fp,
PIXAC *pixac)
{
l_int32 n, i;
PIXC *pixc;
PROCNAME("pixacompWriteStream");
if (!fp)
return ERROR_INT("stream not defined", procName, 1);
if (!pixac)
return ERROR_INT("pixac not defined", procName, 1);
n = pixacompGetCount(pixac);
fprintf(fp, "\nPixacomp Version %d\n", PIXACOMP_VERSION_NUMBER);
fprintf(fp, "Number of pixcomp = %d", n);
boxaWriteStream(fp, pixac->boxa);
for (i = 0; i < n; i++) {
if ((pixc = pixacompGetPixcomp(pixac, i)) == NULL)
return ERROR_INT("pixc not found", procName, 1);
fprintf(fp, " Pixcomp[%d]: w = %d, h = %d, d = %d\n",
i, pixc->w, pixc->h, pixc->d);
fprintf(fp, " comptype = %d, size = %d, cmapflag = %d\n",
pixc->comptype, pixc->size, pixc->cmapflag);
fprintf(fp, " xres = %d, yres = %d\n", pixc->xres, pixc->yres);
fwrite(pixc->data, 1, pixc->size, fp);
}
return 0;
}
/*--------------------------------------------------------------------*
* Output for debugging *
*--------------------------------------------------------------------*/
/*!
* pixacompWriteStreamInfo()
*
* Input: fp (file stream)
* pixac
* text (<optional> identifying string; can be null)
* Return: 0 if OK, 1 on error
*/
l_int32
pixacompWriteStreamInfo(FILE *fp,
PIXAC *pixac,
const char *text)
{
l_int32 i, n, nboxes;
PIXC *pixc;
PROCNAME("pixacompWriteStreamInfo");
if (!fp)
return ERROR_INT("fp not defined", procName, 1);
if (!pixac)
return ERROR_INT("pixac not defined", procName, 1);
if (text)
fprintf(fp, "Pixacomp Info for %s:\n", text);
else
fprintf(fp, "Pixacomp Info:\n");
n = pixacompGetCount(pixac);
nboxes = pixacompGetBoxaCount(pixac);
fprintf(fp, "Number of pixcomp: %d\n", n);
fprintf(fp, "Size of pixcomp array alloc: %d\n", pixac->nalloc);
if (nboxes > 0)
fprintf(fp, "Boxa has %d boxes\n", nboxes);
else
fprintf(fp, "Boxa is empty\n");
for (i = 0; i < n; i++) {
pixc = pixacompGetPixcomp(pixac, i);
pixcompWriteStreamInfo(fp, pixc, NULL);
}
return 0;
}
/*!
* pixcompWriteStreamInfo()
*
* Input: fp (file stream)
* pixc
* text (<optional> identifying string; can be null)
* Return: 0 if OK, 1 on error
*/
l_int32
pixcompWriteStreamInfo(FILE *fp,
PIXC *pixc,
const char *text)
{
PROCNAME("pixcompWriteStreamInfo");
if (!fp)
return ERROR_INT("fp not defined", procName, 1);
if (!pixc)
return ERROR_INT("pixc not defined", procName, 1);
if (text)
fprintf(fp, " Pixcomp Info for %s:", text);
else
fprintf(fp, " Pixcomp Info:");
fprintf(fp, " width = %d, height = %d, depth = %d\n",
pixc->w, pixc->h, pixc->d);
fprintf(fp, " xres = %d, yres = %d, size in bytes = %d\n",
pixc->xres, pixc->yres, pixc->size);
if (pixc->cmapflag)
fprintf(fp, " has colormap\n");
else
fprintf(fp, " no colormap\n");
if (pixc->comptype < NumImageFileFormatExtensions)
fprintf(fp, " comptype = %s (%d)\n",
ImageFileFormatExtensions[pixc->comptype], pixc->comptype);
else
fprintf(fp, " Error!! Invalid comptype index: %d\n", pixc->comptype);
return 0;
}
/*!
* pixacompDisplayTiledAndScaled()
*
* Input: pixac
* outdepth (output depth: 1, 8 or 32 bpp)
* tilewidth (each pix is scaled to this width)
* ncols (number of tiles in each row)
* background (0 for white, 1 for black; this is the color
* of the spacing between the images)
* spacing (between images, and on outside)
* border (width of additional black border on each image;
* use 0 for no border)
* Return: pix of tiled images, or null on error
*
* Notes:
* (1) This is the same function as pixaDisplayTiledAndScaled(),
* except it works on a Pixacomp instead of a Pix. It is particularly
* useful for showing the images in a Pixacomp at reduced resolution.
* (2) This can be used to tile a number of renderings of
* an image that are at different scales and depths.
* (3) Each image, after scaling and optionally adding the
* black border, has width 'tilewidth'. Thus, the border does
* not affect the spacing between the image tiles. The
* maximum allowed border width is tilewidth / 5.
*/
PIX *
pixacompDisplayTiledAndScaled(PIXAC *pixac,
l_int32 outdepth,
l_int32 tilewidth,
l_int32 ncols,
l_int32 background,
l_int32 spacing,
l_int32 border)
{
l_int32 x, y, w, h, wd, hd, d;
l_int32 i, n, nrows, maxht, ninrow, irow, bordval;
l_int32 *rowht;
l_float32 scalefact;
PIX *pix, *pixn, *pixt, *pixb, *pixd;
PIXA *pixan;
PROCNAME("pixacompDisplayTiledAndScaled");
if (!pixac)
return (PIX *)ERROR_PTR("pixac not defined", procName, NULL);
if (outdepth != 1 && outdepth != 8 && outdepth != 32)
return (PIX *)ERROR_PTR("outdepth not in {1, 8, 32}", procName, NULL);
if (border < 0 || border > tilewidth / 5)
border = 0;
if ((n = pixacompGetCount(pixac)) == 0)
return (PIX *)ERROR_PTR("no components", procName, NULL);
/* Normalize scale and depth for each pix; optionally add border */
pixan = pixaCreate(n);
bordval = (outdepth == 1) ? 1 : 0;
for (i = 0; i < n; i++) {
if ((pix = pixacompGetPix(pixac, i)) == NULL) {
L_WARNING_INT("pix %d not made", procName, i);
continue;
}
pixGetDimensions(pix, &w, &h, &d);
scalefact = (l_float32)(tilewidth - 2 * border) / (l_float32)w;
if (d == 1 && outdepth > 1 && scalefact < 1.0)
pixt = pixScaleToGray(pix, scalefact);
else
pixt = pixScale(pix, scalefact, scalefact);
if (outdepth == 1)
pixn = pixConvertTo1(pixt, 128);
else if (outdepth == 8)
pixn = pixConvertTo8(pixt, FALSE);
else /* outdepth == 32 */
pixn = pixConvertTo32(pixt);
pixDestroy(&pixt);
if (border)
pixb = pixAddBorder(pixn, border, bordval);
else
pixb = pixClone(pixn);
pixaAddPix(pixan, pixb, L_INSERT);
pixDestroy(&pix);
pixDestroy(&pixn);
}
if ((n = pixaGetCount(pixan)) == 0) { /* should not have changed! */
pixaDestroy(&pixan);
return (PIX *)ERROR_PTR("no components", procName, NULL);
}
/* Determine the size of each row and of pixd */
wd = tilewidth * ncols + spacing * (ncols + 1);
nrows = (n + ncols - 1) / ncols;
if ((rowht = (l_int32 *)CALLOC(nrows, sizeof(l_int32))) == NULL)
return (PIX *)ERROR_PTR("rowht array not made", procName, NULL);
maxht = 0;
ninrow = 0;
irow = 0;
for (i = 0; i < n; i++) {
pix = pixaGetPix(pixan, i, L_CLONE);
ninrow++;
pixGetDimensions(pix, &w, &h, NULL);
maxht = L_MAX(h, maxht);
if (ninrow == ncols) {
rowht[irow] = maxht;
maxht = ninrow = 0; /* reset */
irow++;
}
pixDestroy(&pix);
}
if (ninrow > 0) { /* last fencepost */
rowht[irow] = maxht;
irow++; /* total number of rows */
}
nrows = irow;
hd = spacing * (nrows + 1);
for (i = 0; i < nrows; i++)
hd += rowht[i];
pixd = pixCreate(wd, hd, outdepth);
if ((background == 1 && outdepth == 1) ||
(background == 0 && outdepth != 1))
pixSetAll(pixd);
/* Now blit images to pixd */
x = y = spacing;
irow = 0;
for (i = 0; i < n; i++) {
pix = pixaGetPix(pixan, i, L_CLONE);
pixGetDimensions(pix, &w, &h, NULL);
if (i && ((i % ncols) == 0)) { /* start new row */
x = spacing;
y += spacing + rowht[irow];
irow++;
}
pixRasterop(pixd, x, y, w, h, PIX_SRC, pix, 0, 0);
x += tilewidth + spacing;
pixDestroy(&pix);
}
pixaDestroy(&pixan);
FREE(rowht);
return pixd;
}