mirror of http://192.168.1.51:8099/lmh188/twain3.0
2453 lines
74 KiB
C
2453 lines
74 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 pixcomp.c
|
||
|
* <pre>
|
||
|
*
|
||
|
* Pixcomp creation and destruction
|
||
|
* PIXC *pixcompCreateFromPix()
|
||
|
* PIXC *pixcompCreateFromString()
|
||
|
* PIXC *pixcompCreateFromFile()
|
||
|
* void pixcompDestroy()
|
||
|
* PIXC *pixcompCopy()
|
||
|
|
||
|
* Pixcomp accessors
|
||
|
* l_int32 pixcompGetDimensions()
|
||
|
* l_int32 pixcompGetParameters()
|
||
|
*
|
||
|
* Pixcomp compression selection
|
||
|
* l_int32 pixcompDetermineFormat()
|
||
|
*
|
||
|
* Pixcomp conversion to Pix
|
||
|
* PIX *pixCreateFromPixcomp()
|
||
|
*
|
||
|
* Pixacomp creation and destruction
|
||
|
* PIXAC *pixacompCreate()
|
||
|
* PIXAC *pixacompCreateWithInit()
|
||
|
* PIXAC *pixacompCreateFromPixa()
|
||
|
* PIXAC *pixacompCreateFromFiles()
|
||
|
* PIXAC *pixacompCreateFromSA()
|
||
|
* void pixacompDestroy()
|
||
|
*
|
||
|
* Pixacomp addition/replacement
|
||
|
* l_int32 pixacompAddPix()
|
||
|
* l_int32 pixacompAddPixcomp()
|
||
|
* static 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()
|
||
|
* l_int32 pixacompGetOffset()
|
||
|
* l_int32 pixacompSetOffset()
|
||
|
*
|
||
|
* Pixacomp conversion to Pixa
|
||
|
* PIXA *pixaCreateFromPixacomp()
|
||
|
*
|
||
|
* Combining pixacomp
|
||
|
* l_int32 pixacompJoin()
|
||
|
* PIXAC *pixacompInterleave()
|
||
|
*
|
||
|
* Pixacomp serialized I/O
|
||
|
* PIXAC *pixacompRead()
|
||
|
* PIXAC *pixacompReadStream()
|
||
|
* PIXAC *pixacompReadMem()
|
||
|
* l_int32 pixacompWrite()
|
||
|
* l_int32 pixacompWriteStream()
|
||
|
* l_int32 pixacompWriteMem()
|
||
|
*
|
||
|
* Conversion to pdf
|
||
|
* l_int32 pixacompConvertToPdf()
|
||
|
* l_int32 pixacompConvertToPdfData()
|
||
|
* l_int32 pixacompFastConvertToPdfData()
|
||
|
*
|
||
|
* Output for debugging
|
||
|
* l_int32 pixacompWriteStreamInfo()
|
||
|
* l_int32 pixcompWriteStreamInfo()
|
||
|
* PIX *pixacompDisplayTiledAndScaled()
|
||
|
* l_int32 pixacompWriteFiles()
|
||
|
* l_int32 pixcompWriteFile()
|
||
|
*
|
||
|
* 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 jpeg compression quality
|
||
|
* can be specified using l_setJpegQuality(); otherwise the default is 75.
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* There are two modes of use in accumulating images:
|
||
|
* (1) addition to the end of the array
|
||
|
* (2) random insertion (replacement) into the array
|
||
|
*
|
||
|
* In use, we assume that the array is fully populated up to the
|
||
|
* index value (n - 1), where n is the value of the pixcomp field n.
|
||
|
* Addition can only be made to the end of the fully populated array,
|
||
|
* at the index value n. Insertion can be made randomly, but again
|
||
|
* only within the array of pixcomps; i.e., within the set of
|
||
|
* indices {0 .... n-1}. The functions are pixacompReplacePix()
|
||
|
* and pixacompReplacePixcomp(), and they destroy the existing pixcomp.
|
||
|
*
|
||
|
* For addition to the end of the array, initialize the pixacomp with
|
||
|
* pixacompCreate(), which generates an empty array of pixcomps ptrs.
|
||
|
* For random insertion and replacement of pixcomp into a pixacomp,
|
||
|
* initialize a fully populated array using pixacompCreateWithInit().
|
||
|
*
|
||
|
* The offset field allows you to use an offset-based index to
|
||
|
* access the 0-based ptr array in the pixacomp. This would typically
|
||
|
* be used to map the pixacomp array index to a page number, or v.v.
|
||
|
* By default, the offset is 0. For example, suppose you have 50 images,
|
||
|
* corresponding to page numbers 10 - 59. Then you could use
|
||
|
* pixac = pixacompCreateWithInit(50, 10, ...);
|
||
|
* This would allocate an array of 50 pixcomps, but if you asked for
|
||
|
* the pix at index 10, using pixacompGetPix(pixac, 10), it would
|
||
|
* apply the offset internally, returning the pix at index 0 in the array.
|
||
|
* </pre>
|
||
|
*/
|
||
|
|
||
|
#include <string.h>
|
||
|
#include "allheaders.h"
|
||
|
|
||
|
/* Bounds on initial array size */
|
||
|
static const l_uint32 MaxPtrArraySize = 1000000;
|
||
|
static const l_int32 InitialPtrArraySize = 20; /*!< n'importe quoi */
|
||
|
|
||
|
/* Bound on data size */
|
||
|
static const size_t MaxDataSize = 1000000000;
|
||
|
|
||
|
/* These two globals are defined in writefile.c */
|
||
|
extern l_int32 NumImageFileFormatExtensions;
|
||
|
extern const char *ImageFileFormatExtensions[];
|
||
|
|
||
|
/* Static functions */
|
||
|
static l_int32 pixacompExtendArray(PIXAC *pixac);
|
||
|
static l_int32 pixcompFastConvertToPdfData(PIXC *pixc, const char *title,
|
||
|
l_uint8 **pdata, size_t *pnbytes);
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Pixcomp creation and destruction *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixcompCreateFromPix()
|
||
|
*
|
||
|
* \param[in] pix
|
||
|
* \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
|
||
|
* \return pixc, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Use %comptype == IFF_DEFAULT to have the compression
|
||
|
* type automatically determined.
|
||
|
* (2) To compress jpeg with a quality other than the default (75), use
|
||
|
* l_jpegSetQuality()
|
||
|
* </pre>
|
||
|
*/
|
||
|
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);
|
||
|
|
||
|
pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC));
|
||
|
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\n", procName);
|
||
|
pixcompDestroy(&pixc);
|
||
|
return NULL;
|
||
|
}
|
||
|
pixc->data = data;
|
||
|
pixc->size = size;
|
||
|
|
||
|
return pixc;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixcompCreateFromString()
|
||
|
*
|
||
|
* \param[in] data compressed string
|
||
|
* \param[in] size number of bytes
|
||
|
* \param[in] copyflag L_INSERT or L_COPY
|
||
|
* \return pixc, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* 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.
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIXC *
|
||
|
pixcompCreateFromString(l_uint8 *data,
|
||
|
size_t 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, &format, &w, &h, &bps, &spp, &iscmap) == 1)
|
||
|
return (PIXC *)ERROR_PTR("header data not read", procName, NULL);
|
||
|
pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC));
|
||
|
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 = l_binaryCopy(data, size);
|
||
|
pixc->size = size;
|
||
|
return pixc;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixcompCreateFromFile()
|
||
|
*
|
||
|
* \param[in] filename
|
||
|
* \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
|
||
|
* \return pixc, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* 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.
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIXC *
|
||
|
pixcompCreateFromFile(const char *filename,
|
||
|
l_int32 comptype)
|
||
|
{
|
||
|
l_int32 format;
|
||
|
size_t nbytes;
|
||
|
l_uint8 *data;
|
||
|
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);
|
||
|
|
||
|
findFileFormat(filename, &format);
|
||
|
if (format == IFF_UNKNOWN) {
|
||
|
L_ERROR("unreadable file: %s\n", procName, filename);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* 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 = l_binaryRead(filename, &nbytes);
|
||
|
if ((pixc = pixcompCreateFromString(data, nbytes, L_INSERT)) == NULL) {
|
||
|
LEPT_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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixcompDestroy()
|
||
|
*
|
||
|
* \param[in,out] ppixc use ptr address so it will be nulled
|
||
|
* \return void
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Always nulls the input ptr.
|
||
|
* </pre>
|
||
|
*/
|
||
|
void
|
||
|
pixcompDestroy(PIXC **ppixc)
|
||
|
{
|
||
|
PIXC *pixc;
|
||
|
|
||
|
PROCNAME("pixcompDestroy");
|
||
|
|
||
|
if (!ppixc) {
|
||
|
L_WARNING("ptr address is null!\n", procName);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((pixc = *ppixc) == NULL)
|
||
|
return;
|
||
|
|
||
|
LEPT_FREE(pixc->data);
|
||
|
if (pixc->text)
|
||
|
LEPT_FREE(pixc->text);
|
||
|
LEPT_FREE(pixc);
|
||
|
*ppixc = NULL;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixcompCopy()
|
||
|
*
|
||
|
* \param[in] pixcs
|
||
|
* \return pixcd, or NULL on error
|
||
|
*/
|
||
|
PIXC *
|
||
|
pixcompCopy(PIXC *pixcs)
|
||
|
{
|
||
|
size_t size;
|
||
|
l_uint8 *datas, *datad;
|
||
|
PIXC *pixcd;
|
||
|
|
||
|
PROCNAME("pixcompCopy");
|
||
|
|
||
|
if (!pixcs)
|
||
|
return (PIXC *)ERROR_PTR("pixcs not defined", procName, NULL);
|
||
|
|
||
|
pixcd = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC));
|
||
|
pixcd->w = pixcs->w;
|
||
|
pixcd->h = pixcs->h;
|
||
|
pixcd->d = pixcs->d;
|
||
|
pixcd->xres = pixcs->xres;
|
||
|
pixcd->yres = pixcs->yres;
|
||
|
pixcd->comptype = pixcs->comptype;
|
||
|
if (pixcs->text != NULL)
|
||
|
pixcd->text = stringNew(pixcs->text);
|
||
|
pixcd->cmapflag = pixcs->cmapflag;
|
||
|
|
||
|
/* Copy image data */
|
||
|
size = pixcs->size;
|
||
|
datas = pixcs->data;
|
||
|
if ((datad = (l_uint8 *)LEPT_CALLOC(size, sizeof(l_int8))) == NULL) {
|
||
|
pixcompDestroy(&pixcd);
|
||
|
return (PIXC *)ERROR_PTR("pixcd not made", procName, NULL);
|
||
|
}
|
||
|
memcpy(datad, datas, size);
|
||
|
pixcd->data = datad;
|
||
|
pixcd->size = size;
|
||
|
return pixcd;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Pixcomp accessors *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixcompGetDimensions()
|
||
|
*
|
||
|
* \param[in] pixc
|
||
|
* \param[out] pw, ph, pd [optional]
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*/
|
||
|
l_ok
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixcompGetParameters()
|
||
|
*
|
||
|
* \param[in] pixc
|
||
|
* \param[out] pxres, pyres, pcomptype, pcmapflag [optional]
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*/
|
||
|
l_ok
|
||
|
pixcompGetParameters(PIXC *pixc,
|
||
|
l_int32 *pxres,
|
||
|
l_int32 *pyres,
|
||
|
l_int32 *pcomptype,
|
||
|
l_int32 *pcmapflag)
|
||
|
{
|
||
|
PROCNAME("pixcompGetParameters");
|
||
|
|
||
|
if (!pixc)
|
||
|
return ERROR_INT("pixc not defined", procName, 1);
|
||
|
if (pxres) *pxres = pixc->xres;
|
||
|
if (pyres) *pyres = pixc->yres;
|
||
|
if (pcomptype) *pcomptype = pixc->comptype;
|
||
|
if (pcmapflag) *pcmapflag = pixc->cmapflag;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Pixcomp compression selection *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixcompDetermineFormat()
|
||
|
*
|
||
|
* \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
|
||
|
* \param[in] d pix depth
|
||
|
* \param[in] cmapflag 1 if pix to be compressed as a colormap; 0 otherwise
|
||
|
* \param[out] pformat IFF_TIFF, IFF_PNG or IFF_JFIF_JPEG
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* 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 %d == 8 with no colormap and:
|
||
|
* * you wish to compress with png, use %comptype == IFF_PNG
|
||
|
* * you wish to compress with jpeg, use either
|
||
|
* %comptype == IFF_JFIF_JPEG or %comptype == IFF_DEFAULT.
|
||
|
* (4) If the pix cannot be compressed by the input value of
|
||
|
* %comptype, this selects IFF_PNG, which can compress all pix.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
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 *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixCreateFromPixcomp()
|
||
|
*
|
||
|
* \param[in] 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("pix width %d != pixc width %d\n", procName, w, pixc->w);
|
||
|
L_ERROR("pix width %d != pixc width\n", procName, w);
|
||
|
}
|
||
|
if (pixc->h != h)
|
||
|
L_ERROR("pix height %d != pixc height\n", procName, h);
|
||
|
if (pixc->d != d) {
|
||
|
if (pixc->d == 16) /* we strip 16 --> 8 bpp by default */
|
||
|
L_WARNING("pix depth %d != pixc depth 16\n", procName, d);
|
||
|
else
|
||
|
L_ERROR("pix depth %d != pixc depth\n", procName, d);
|
||
|
}
|
||
|
cmapinpix = (pixGetColormap(pix) != NULL);
|
||
|
if ((cmapinpix && !pixc->cmapflag) || (!cmapinpix && pixc->cmapflag))
|
||
|
L_ERROR("pix cmap flag inconsistent\n", procName);
|
||
|
format = pixGetInputFormat(pix);
|
||
|
if (format != pixc->comptype) {
|
||
|
L_ERROR("pix comptype %d not equal to pixc comptype\n",
|
||
|
procName, format);
|
||
|
}
|
||
|
|
||
|
return pix;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Pixacomp creation and destruction *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixacompCreate()
|
||
|
*
|
||
|
* \param[in] n initial number of ptrs
|
||
|
* \return pixac, or NULL on error
|
||
|
*/
|
||
|
PIXAC *
|
||
|
pixacompCreate(l_int32 n)
|
||
|
{
|
||
|
PIXAC *pixac;
|
||
|
|
||
|
PROCNAME("pixacompCreate");
|
||
|
|
||
|
if (n <= 0 || n > MaxPtrArraySize)
|
||
|
n = InitialPtrArraySize;
|
||
|
|
||
|
pixac = (PIXAC *)LEPT_CALLOC(1, sizeof(PIXAC));
|
||
|
pixac->n = 0;
|
||
|
pixac->nalloc = n;
|
||
|
pixac->offset = 0;
|
||
|
if ((pixac->pixc = (PIXC **)LEPT_CALLOC(n, sizeof(PIXC *))) == NULL) {
|
||
|
pixacompDestroy(&pixac);
|
||
|
return (PIXAC *)ERROR_PTR("pixc ptrs not made", procName, NULL);
|
||
|
}
|
||
|
if ((pixac->boxa = boxaCreate(n)) == NULL) {
|
||
|
pixacompDestroy(&pixac);
|
||
|
return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL);
|
||
|
}
|
||
|
|
||
|
return pixac;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompCreateWithInit()
|
||
|
*
|
||
|
* \param[in] n initial number of ptrs
|
||
|
* \param[in] offset difference: accessor index - pixacomp array index
|
||
|
* \param[in] pix [optional] initialize each ptr in pixacomp
|
||
|
* to this pix; can be NULL
|
||
|
* \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
|
||
|
* \return pixac, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Initializes a pixacomp to be fully populated with %pix,
|
||
|
* compressed using %comptype. If %pix == NULL, %comptype
|
||
|
* is ignored.
|
||
|
* (2) Typically, the array is initialized with a tiny pix.
|
||
|
* This is most easily done by setting %pix == NULL, causing
|
||
|
* initialization of each array element with a tiny placeholder
|
||
|
* pix (w = h = d = 1), using comptype = IFF_TIFF_G4 .
|
||
|
* (3) Example usage:
|
||
|
* // Generate pixacomp for pages 30 - 49. This has an array
|
||
|
* // size of 20 and the page number offset is 30.
|
||
|
* PixaComp *pixac = pixacompCreateWithInit(20, 30, NULL,
|
||
|
* IFF_TIFF_G4);
|
||
|
* // Now insert png-compressed images into the initialized array
|
||
|
* for (pageno = 30; pageno < 50; pageno++) {
|
||
|
* Pix *pixt = ... // derived from image[pageno]
|
||
|
* if (pixt)
|
||
|
* pixacompReplacePix(pixac, pageno, pixt, IFF_PNG);
|
||
|
* pixDestroy(&pixt);
|
||
|
* }
|
||
|
* The result is a pixac with 20 compressed strings, and with
|
||
|
* selected pixt replacing the placeholders.
|
||
|
* To extract the image for page 38, which is decompressed
|
||
|
* from element 8 in the array, use:
|
||
|
* pixt = pixacompGetPix(pixac, 38);
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIXAC *
|
||
|
pixacompCreateWithInit(l_int32 n,
|
||
|
l_int32 offset,
|
||
|
PIX *pix,
|
||
|
l_int32 comptype)
|
||
|
{
|
||
|
l_int32 i;
|
||
|
PIX *pixt;
|
||
|
PIXC *pixc;
|
||
|
PIXAC *pixac;
|
||
|
|
||
|
PROCNAME("pixacompCreateWithInit");
|
||
|
|
||
|
if (n <= 0 || n > MaxPtrArraySize)
|
||
|
return (PIXAC *)ERROR_PTR("n out of valid bounds", procName, NULL);
|
||
|
if (pix) {
|
||
|
if (comptype != IFF_DEFAULT && comptype != IFF_TIFF_G4 &&
|
||
|
comptype != IFF_PNG && comptype != IFF_JFIF_JPEG)
|
||
|
return (PIXAC *)ERROR_PTR("invalid comptype", procName, NULL);
|
||
|
} else {
|
||
|
comptype = IFF_TIFF_G4;
|
||
|
}
|
||
|
if (offset < 0) {
|
||
|
L_WARNING("offset < 0; setting to 0\n", procName);
|
||
|
offset = 0;
|
||
|
}
|
||
|
|
||
|
if ((pixac = pixacompCreate(n)) == NULL)
|
||
|
return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
|
||
|
pixacompSetOffset(pixac, offset);
|
||
|
if (pix)
|
||
|
pixt = pixClone(pix);
|
||
|
else
|
||
|
pixt = pixCreate(1, 1, 1);
|
||
|
for (i = 0; i < n; i++) {
|
||
|
pixc = pixcompCreateFromPix(pixt, comptype);
|
||
|
pixacompAddPixcomp(pixac, pixc, L_INSERT);
|
||
|
}
|
||
|
pixDestroy(&pixt);
|
||
|
|
||
|
return pixac;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompCreateFromPixa()
|
||
|
*
|
||
|
* \param[in] pixa
|
||
|
* \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
|
||
|
* \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* 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.
|
||
|
* (2) %accesstype is used to extract a boxa from %pixa.
|
||
|
* (3) To compress jpeg with a quality other than the default (75), use
|
||
|
* l_jpegSetQuality()
|
||
|
* </pre>
|
||
|
*/
|
||
|
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) {
|
||
|
boxaDestroy(&pixac->boxa);
|
||
|
pixac->boxa = boxa;
|
||
|
}
|
||
|
|
||
|
return pixac;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompCreateFromFiles()
|
||
|
*
|
||
|
* \param[in] dirname
|
||
|
* \param[in] substr [optional] substring filter on filenames; can be null
|
||
|
* \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
|
||
|
* \return pixac, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* 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.
|
||
|
* </pre>
|
||
|
*/
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompCreateFromSA()
|
||
|
*
|
||
|
* \param[in] sa full pathnames for all files
|
||
|
* \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
|
||
|
* \return pixac, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* 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.
|
||
|
* </pre>
|
||
|
*/
|
||
|
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_ERROR("pixc not read from file: %s\n", procName, str);
|
||
|
continue;
|
||
|
}
|
||
|
pixacompAddPixcomp(pixac, pixc, L_INSERT);
|
||
|
}
|
||
|
return pixac;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompDestroy()
|
||
|
*
|
||
|
* \param[in,out] ppixac use ptr address so it will be nulled
|
||
|
* \return void
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Always nulls the input ptr.
|
||
|
* </pre>
|
||
|
*/
|
||
|
void
|
||
|
pixacompDestroy(PIXAC **ppixac)
|
||
|
{
|
||
|
l_int32 i;
|
||
|
PIXAC *pixac;
|
||
|
|
||
|
PROCNAME("pixacompDestroy");
|
||
|
|
||
|
if (ppixac == NULL) {
|
||
|
L_WARNING("ptr address is NULL!\n", procName);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((pixac = *ppixac) == NULL)
|
||
|
return;
|
||
|
|
||
|
for (i = 0; i < pixac->n; i++)
|
||
|
pixcompDestroy(&pixac->pixc[i]);
|
||
|
LEPT_FREE(pixac->pixc);
|
||
|
boxaDestroy(&pixac->boxa);
|
||
|
LEPT_FREE(pixac);
|
||
|
|
||
|
*ppixac = NULL;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Pixacomp addition *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixacompAddPix()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] pix to be added
|
||
|
* \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The array is filled up to the (n-1)-th element, and this
|
||
|
* converts the input pix to a pixc and adds it at
|
||
|
* the n-th position.
|
||
|
* (2) The pixc produced from the pix is owned by the pixac.
|
||
|
* The input pix is not affected.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
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, L_INSERT);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompAddPixcomp()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] pixc to be added by insertion
|
||
|
* \param[in] copyflag L_INSERT, L_COPY
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Anything added to a pixac is owned by the pixac.
|
||
|
* So do not L_INSERT a pixc that is owned by another pixac,
|
||
|
* or destroy a pixc that has been L_INSERTed.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompAddPixcomp(PIXAC *pixac,
|
||
|
PIXC *pixc,
|
||
|
l_int32 copyflag)
|
||
|
{
|
||
|
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);
|
||
|
if (copyflag != L_INSERT && copyflag != L_COPY)
|
||
|
return ERROR_INT("invalid copyflag", procName, 1);
|
||
|
|
||
|
n = pixac->n;
|
||
|
if (n >= pixac->nalloc)
|
||
|
pixacompExtendArray(pixac);
|
||
|
if (copyflag == L_INSERT)
|
||
|
pixac->pixc[n] = pixc;
|
||
|
else /* L_COPY */
|
||
|
pixac->pixc[n] = pixcompCopy(pixc);
|
||
|
pixac->n++;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompExtendArray()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* 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.
|
||
|
* </pre>
|
||
|
*/
|
||
|
static 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompReplacePix()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] index caller's view of index within pixac; includes offset
|
||
|
* \param[in] pix owned by the caller
|
||
|
* \param[in] comptype IFF_DEFAULT, IFF_TIFF_G4, IFF_PNG, IFF_JFIF_JPEG
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The %index includes the offset, which must be subtracted
|
||
|
* to get the actual index into the ptr array.
|
||
|
* (2) The input %pix is converted to a pixc, which is then inserted
|
||
|
* into the pixac.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompReplacePix(PIXAC *pixac,
|
||
|
l_int32 index,
|
||
|
PIX *pix,
|
||
|
l_int32 comptype)
|
||
|
{
|
||
|
l_int32 n, aindex;
|
||
|
PIXC *pixc;
|
||
|
|
||
|
PROCNAME("pixacompReplacePix");
|
||
|
|
||
|
if (!pixac)
|
||
|
return ERROR_INT("pixac not defined", procName, 1);
|
||
|
n = pixacompGetCount(pixac);
|
||
|
aindex = index - pixac->offset;
|
||
|
if (aindex < 0 || aindex >= 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompReplacePixcomp()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] index caller's view of index within pixac; includes offset
|
||
|
* \param[in] pixc to replace existing one, which is destroyed
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The %index includes the offset, which must be subtracted
|
||
|
* to get the actual index into the ptr array.
|
||
|
* (2) The inserted %pixc is now owned by the pixac. The caller
|
||
|
* must not destroy it.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompReplacePixcomp(PIXAC *pixac,
|
||
|
l_int32 index,
|
||
|
PIXC *pixc)
|
||
|
{
|
||
|
l_int32 n, aindex;
|
||
|
PIXC *pixct;
|
||
|
|
||
|
PROCNAME("pixacompReplacePixcomp");
|
||
|
|
||
|
if (!pixac)
|
||
|
return ERROR_INT("pixac not defined", procName, 1);
|
||
|
n = pixacompGetCount(pixac);
|
||
|
aindex = index - pixac->offset;
|
||
|
if (aindex < 0 || aindex >= 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, L_NOCOPY); /* use %index */
|
||
|
pixcompDestroy(&pixct);
|
||
|
pixac->pixc[aindex] = pixc; /* replace; use array index */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompAddBox()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] box
|
||
|
* \param[in] copyflag L_INSERT, L_COPY
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*/
|
||
|
l_ok
|
||
|
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 *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixacompGetCount()
|
||
|
*
|
||
|
* \param[in] 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompGetPixcomp()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] index caller's view of index within pixac; includes offset
|
||
|
* \param[in] copyflag L_NOCOPY, L_COPY
|
||
|
* \return pixc, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The %index includes the offset, which must be subtracted
|
||
|
* to get the actual index into the ptr array.
|
||
|
* (2) If copyflag == L_NOCOPY, the pixc is owned by %pixac; do
|
||
|
* not destroy.
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIXC *
|
||
|
pixacompGetPixcomp(PIXAC *pixac,
|
||
|
l_int32 index,
|
||
|
l_int32 copyflag)
|
||
|
{
|
||
|
l_int32 aindex;
|
||
|
|
||
|
PROCNAME("pixacompGetPixcomp");
|
||
|
|
||
|
if (!pixac)
|
||
|
return (PIXC *)ERROR_PTR("pixac not defined", procName, NULL);
|
||
|
if (copyflag != L_NOCOPY && copyflag != L_COPY)
|
||
|
return (PIXC *)ERROR_PTR("invalid copyflag", procName, NULL);
|
||
|
aindex = index - pixac->offset;
|
||
|
if (aindex < 0 || aindex >= pixac->n)
|
||
|
return (PIXC *)ERROR_PTR("array index not valid", procName, NULL);
|
||
|
|
||
|
if (copyflag == L_NOCOPY)
|
||
|
return pixac->pixc[aindex];
|
||
|
else /* L_COPY */
|
||
|
return pixcompCopy(pixac->pixc[aindex]);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompGetPix()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] index caller's view of index within pixac; includes offset
|
||
|
* \return pix, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The %index includes the offset, which must be subtracted
|
||
|
* to get the actual index into the ptr array.
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIX *
|
||
|
pixacompGetPix(PIXAC *pixac,
|
||
|
l_int32 index)
|
||
|
{
|
||
|
l_int32 aindex;
|
||
|
PIXC *pixc;
|
||
|
|
||
|
PROCNAME("pixacompGetPix");
|
||
|
|
||
|
if (!pixac)
|
||
|
return (PIX *)ERROR_PTR("pixac not defined", procName, NULL);
|
||
|
aindex = index - pixac->offset;
|
||
|
if (aindex < 0 || aindex >= pixac->n)
|
||
|
return (PIX *)ERROR_PTR("array index not valid", procName, NULL);
|
||
|
|
||
|
pixc = pixacompGetPixcomp(pixac, index, L_NOCOPY);
|
||
|
return pixCreateFromPixcomp(pixc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompGetPixDimensions()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] index caller's view of index within pixac;
|
||
|
* includes offset
|
||
|
* \param[out] pw, ph, pd [optional] each can be null
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The %index includes the offset, which must be subtracted
|
||
|
* to get the actual index into the ptr array.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompGetPixDimensions(PIXAC *pixac,
|
||
|
l_int32 index,
|
||
|
l_int32 *pw,
|
||
|
l_int32 *ph,
|
||
|
l_int32 *pd)
|
||
|
{
|
||
|
l_int32 aindex;
|
||
|
PIXC *pixc;
|
||
|
|
||
|
PROCNAME("pixacompGetPixDimensions");
|
||
|
|
||
|
if (!pixac)
|
||
|
return ERROR_INT("pixac not defined", procName, 1);
|
||
|
aindex = index - pixac->offset;
|
||
|
if (aindex < 0 || aindex >= pixac->n)
|
||
|
return ERROR_INT("array index not valid", procName, 1);
|
||
|
|
||
|
if ((pixc = pixac->pixc[aindex]) == NULL)
|
||
|
return ERROR_INT("pixc not found!", procName, 1);
|
||
|
pixcompGetDimensions(pixc, pw, ph, pd);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompGetBoxa()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] 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);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompGetBoxaCount()
|
||
|
*
|
||
|
* \param[in] 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);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompGetBox()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] index caller's view of index within pixac;
|
||
|
* includes offset
|
||
|
* \param[in] accesstype L_COPY or L_CLONE
|
||
|
* \return box if null, not automatically an error, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The %index includes the offset, which must be subtracted
|
||
|
* to get the actual index into the ptr array.
|
||
|
* (2) There is always a boxa with a pixac, and it is initialized so
|
||
|
* that each box ptr is NULL.
|
||
|
* (3) In general, we expect that there is either a box associated
|
||
|
* with each pixc, or no boxes at all in the boxa.
|
||
|
* (4) 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().
|
||
|
* </pre>
|
||
|
*/
|
||
|
BOX *
|
||
|
pixacompGetBox(PIXAC *pixac,
|
||
|
l_int32 index,
|
||
|
l_int32 accesstype)
|
||
|
{
|
||
|
l_int32 aindex;
|
||
|
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);
|
||
|
aindex = index - pixac->offset;
|
||
|
if (aindex < 0 || aindex >= pixac->boxa->n)
|
||
|
return (BOX *)ERROR_PTR("array index not valid", procName, NULL);
|
||
|
if (accesstype != L_COPY && accesstype != L_CLONE)
|
||
|
return (BOX *)ERROR_PTR("invalid accesstype", procName, NULL);
|
||
|
|
||
|
box = pixac->boxa->box[aindex];
|
||
|
if (box) {
|
||
|
if (accesstype == L_COPY)
|
||
|
return boxCopy(box);
|
||
|
else /* accesstype == L_CLONE */
|
||
|
return boxClone(box);
|
||
|
} else {
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompGetBoxGeometry()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] index caller's view of index within pixac;
|
||
|
* includes offset
|
||
|
* \param[out] px, py, pw, ph [optional] each can be null
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The %index includes the offset, which must be subtracted
|
||
|
* to get the actual index into the ptr array.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompGetBoxGeometry(PIXAC *pixac,
|
||
|
l_int32 index,
|
||
|
l_int32 *px,
|
||
|
l_int32 *py,
|
||
|
l_int32 *pw,
|
||
|
l_int32 *ph)
|
||
|
{
|
||
|
l_int32 aindex;
|
||
|
BOX *box;
|
||
|
|
||
|
PROCNAME("pixacompGetBoxGeometry");
|
||
|
|
||
|
if (!pixac)
|
||
|
return ERROR_INT("pixac not defined", procName, 1);
|
||
|
aindex = index - pixac->offset;
|
||
|
if (aindex < 0 || aindex >= pixac->n)
|
||
|
return ERROR_INT("array index not valid", procName, 1);
|
||
|
|
||
|
if ((box = pixacompGetBox(pixac, aindex, L_CLONE)) == NULL)
|
||
|
return ERROR_INT("box not found!", procName, 1);
|
||
|
boxGetGeometry(box, px, py, pw, ph);
|
||
|
boxDestroy(&box);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompGetOffset()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \return offset, or 0 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The offset is the difference between the caller's view of
|
||
|
* the index into the array and the actual array index.
|
||
|
* By default it is 0.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_int32
|
||
|
pixacompGetOffset(PIXAC *pixac)
|
||
|
{
|
||
|
PROCNAME("pixacompGetOffset");
|
||
|
|
||
|
if (!pixac)
|
||
|
return ERROR_INT("pixac not defined", procName, 0);
|
||
|
return pixac->offset;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompSetOffset()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] offset non-negative
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The offset is the difference between the caller's view of
|
||
|
* the index into the array and the actual array index.
|
||
|
* By default it is 0.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompSetOffset(PIXAC *pixac,
|
||
|
l_int32 offset)
|
||
|
{
|
||
|
PROCNAME("pixacompSetOffset");
|
||
|
|
||
|
if (!pixac)
|
||
|
return ERROR_INT("pixac not defined", procName, 1);
|
||
|
pixac->offset = L_MAX(0, offset);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Pixacomp conversion to Pixa *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixaCreateFromPixacomp()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] accesstype L_COPY, L_CLONE, L_COPY_CLONE; for boxa
|
||
|
* \return pixa if OK, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Because the pixa has no notion of offset, the offset must
|
||
|
* be set to 0 before the conversion, so that pixacompGetPix()
|
||
|
* fetches all the pixcomps. It is reset at the end.
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIXA *
|
||
|
pixaCreateFromPixacomp(PIXAC *pixac,
|
||
|
l_int32 accesstype)
|
||
|
{
|
||
|
l_int32 i, n, offset;
|
||
|
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);
|
||
|
offset = pixacompGetOffset(pixac);
|
||
|
pixacompSetOffset(pixac, 0);
|
||
|
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("pix %d not made\n", procName, i);
|
||
|
continue;
|
||
|
}
|
||
|
pixaAddPix(pixa, pix, L_INSERT);
|
||
|
}
|
||
|
if (pixa->boxa) {
|
||
|
boxaDestroy(&pixa->boxa);
|
||
|
pixa->boxa = pixacompGetBoxa(pixac, accesstype);
|
||
|
}
|
||
|
pixacompSetOffset(pixac, offset);
|
||
|
|
||
|
return pixa;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Combining pixacomp
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixacompJoin()
|
||
|
*
|
||
|
* \param[in] pixacd dest pixac; add to this one
|
||
|
* \param[in] pixacs [optional] source pixac; add from this one
|
||
|
* \param[in] istart starting index in pixacs
|
||
|
* \param[in] iend ending index in pixacs; use -1 to cat all
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This appends a clone of each indicated pixc in pixcas to pixcad
|
||
|
* (2) istart < 0 is taken to mean 'read from the start' (istart = 0)
|
||
|
* (3) iend < 0 means 'read to the end'
|
||
|
* (4) If pixacs is NULL or contains no pixc, this is a no-op.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompJoin(PIXAC *pixacd,
|
||
|
PIXAC *pixacs,
|
||
|
l_int32 istart,
|
||
|
l_int32 iend)
|
||
|
{
|
||
|
l_int32 i, n, nb;
|
||
|
BOXA *boxas, *boxad;
|
||
|
PIXC *pixc;
|
||
|
|
||
|
PROCNAME("pixacompJoin");
|
||
|
|
||
|
if (!pixacd)
|
||
|
return ERROR_INT("pixacd not defined", procName, 1);
|
||
|
if (!pixacs || ((n = pixacompGetCount(pixacs)) == 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++) {
|
||
|
pixc = pixacompGetPixcomp(pixacs, i, L_NOCOPY);
|
||
|
pixacompAddPixcomp(pixacd, pixc, L_COPY);
|
||
|
}
|
||
|
|
||
|
boxas = pixacompGetBoxa(pixacs, L_CLONE);
|
||
|
boxad = pixacompGetBoxa(pixacd, L_CLONE);
|
||
|
nb = pixacompGetBoxaCount(pixacs);
|
||
|
iend = L_MIN(iend, nb - 1);
|
||
|
boxaJoin(boxad, boxas, istart, iend);
|
||
|
boxaDestroy(&boxas); /* just the clones */
|
||
|
boxaDestroy(&boxad); /* ditto */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompInterleave()
|
||
|
*
|
||
|
* \param[in] pixac1 first src pixac
|
||
|
* \param[in] pixac2 second src pixac
|
||
|
* \return pixacd interleaved from sources, or NULL on error.
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) If the two pixac have different sizes, a warning is issued,
|
||
|
* and the number of pairs returned is the minimum size.
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIXAC *
|
||
|
pixacompInterleave(PIXAC *pixac1,
|
||
|
PIXAC *pixac2)
|
||
|
{
|
||
|
l_int32 i, n1, n2, n, nb1, nb2;
|
||
|
BOX *box;
|
||
|
PIXC *pixc1, *pixc2;
|
||
|
PIXAC *pixacd;
|
||
|
|
||
|
PROCNAME("pixacompInterleave");
|
||
|
|
||
|
if (!pixac1)
|
||
|
return (PIXAC *)ERROR_PTR("pixac1 not defined", procName, NULL);
|
||
|
if (!pixac2)
|
||
|
return (PIXAC *)ERROR_PTR("pixac2 not defined", procName, NULL);
|
||
|
n1 = pixacompGetCount(pixac1);
|
||
|
n2 = pixacompGetCount(pixac2);
|
||
|
n = L_MIN(n1, n2);
|
||
|
if (n == 0)
|
||
|
return (PIXAC *)ERROR_PTR("at least one input pixac is empty",
|
||
|
procName, NULL);
|
||
|
if (n1 != n2)
|
||
|
L_WARNING("counts differ: %d != %d\n", procName, n1, n2);
|
||
|
|
||
|
pixacd = pixacompCreate(2 * n);
|
||
|
nb1 = pixacompGetBoxaCount(pixac1);
|
||
|
nb2 = pixacompGetBoxaCount(pixac2);
|
||
|
for (i = 0; i < n; i++) {
|
||
|
pixc1 = pixacompGetPixcomp(pixac1, i, L_COPY);
|
||
|
pixacompAddPixcomp(pixacd, pixc1, L_INSERT);
|
||
|
if (i < nb1) {
|
||
|
box = pixacompGetBox(pixac1, i, L_COPY);
|
||
|
pixacompAddBox(pixacd, box, L_INSERT);
|
||
|
}
|
||
|
pixc2 = pixacompGetPixcomp(pixac2, i, L_COPY);
|
||
|
pixacompAddPixcomp(pixacd, pixc2, L_INSERT);
|
||
|
if (i < nb2) {
|
||
|
box = pixacompGetBox(pixac2, i, L_COPY);
|
||
|
pixacompAddBox(pixacd, box, L_INSERT);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pixacd;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Pixacomp serialized I/O *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixacompRead()
|
||
|
*
|
||
|
* \param[in] filename
|
||
|
* \return pixac, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* 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.
|
||
|
* </pre>
|
||
|
*/
|
||
|
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);
|
||
|
pixac = pixacompReadStream(fp);
|
||
|
fclose(fp);
|
||
|
if (!pixac)
|
||
|
return (PIXAC *)ERROR_PTR("pixac not read", procName, NULL);
|
||
|
return pixac;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompReadStream()
|
||
|
*
|
||
|
* \param[in] fp file stream
|
||
|
* \return pixac, or NULL on error
|
||
|
*/
|
||
|
PIXAC *
|
||
|
pixacompReadStream(FILE *fp)
|
||
|
{
|
||
|
char buf[256];
|
||
|
l_uint8 *data;
|
||
|
l_int32 n, offset, i, w, h, d, ignore;
|
||
|
l_int32 comptype, cmapflag, version, xres, yres;
|
||
|
size_t size;
|
||
|
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", &n) != 1)
|
||
|
return (PIXAC *)ERROR_PTR("not a pixacomp file", procName, NULL);
|
||
|
if (fscanf(fp, "Offset of index into array = %d", &offset) != 1)
|
||
|
return (PIXAC *)ERROR_PTR("offset not read", procName, NULL);
|
||
|
|
||
|
if ((pixac = pixacompCreate(n)) == NULL)
|
||
|
return (PIXAC *)ERROR_PTR("pixac not made", procName, NULL);
|
||
|
if ((boxa = boxaReadStream(fp)) == NULL) {
|
||
|
pixacompDestroy(&pixac);
|
||
|
return (PIXAC *)ERROR_PTR("boxa not made", procName, NULL);
|
||
|
}
|
||
|
boxaDestroy(&pixac->boxa); /* empty */
|
||
|
pixac->boxa = boxa;
|
||
|
pixacompSetOffset(pixac, offset);
|
||
|
|
||
|
for (i = 0; i < n; i++) {
|
||
|
if (fscanf(fp, "\nPixcomp[%d]: w = %d, h = %d, d = %d\n",
|
||
|
&ignore, &w, &h, &d) != 4) {
|
||
|
pixacompDestroy(&pixac);
|
||
|
return (PIXAC *)ERROR_PTR("dimension reading", procName, NULL);
|
||
|
}
|
||
|
if (fscanf(fp, " comptype = %d, size = %zu, cmapflag = %d\n",
|
||
|
&comptype, &size, &cmapflag) != 3) {
|
||
|
pixacompDestroy(&pixac);
|
||
|
return (PIXAC *)ERROR_PTR("comptype/size reading", procName, NULL);
|
||
|
}
|
||
|
if (size > MaxDataSize) {
|
||
|
pixacompDestroy(&pixac);
|
||
|
L_ERROR("data size = %zu is too big", procName, size);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Use fgets() and sscanf(); not fscanf(), for the last
|
||
|
* bit of header data before the binary data. The reason is
|
||
|
* that fscanf throws away white space, and if the binary data
|
||
|
* happens to begin with ascii character(s) that are white
|
||
|
* space, it will swallow them and all will be lost! */
|
||
|
if (fgets(buf, sizeof(buf), fp) == NULL) {
|
||
|
pixacompDestroy(&pixac);
|
||
|
return (PIXAC *)ERROR_PTR("fgets read fail", procName, NULL);
|
||
|
}
|
||
|
if (sscanf(buf, " xres = %d, yres = %d\n", &xres, &yres) != 2) {
|
||
|
pixacompDestroy(&pixac);
|
||
|
return (PIXAC *)ERROR_PTR("read fail for res", procName, NULL);
|
||
|
}
|
||
|
if ((data = (l_uint8 *)LEPT_CALLOC(1, size)) == NULL) {
|
||
|
pixacompDestroy(&pixac);
|
||
|
return (PIXAC *)ERROR_PTR("calloc fail for data", procName, NULL);
|
||
|
}
|
||
|
if (fread(data, 1, size, fp) != size) {
|
||
|
pixacompDestroy(&pixac);
|
||
|
LEPT_FREE(data);
|
||
|
return (PIXAC *)ERROR_PTR("error reading data", procName, NULL);
|
||
|
}
|
||
|
fgetc(fp); /* swallow the ending nl */
|
||
|
pixc = (PIXC *)LEPT_CALLOC(1, sizeof(PIXC));
|
||
|
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, L_INSERT);
|
||
|
}
|
||
|
return pixac;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompReadMem()
|
||
|
*
|
||
|
* \param[in] data in pixacomp format
|
||
|
* \param[in] size of data
|
||
|
* \return pixac, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Deseralizes a buffer of pixacomp data into a pixac in memory.
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIXAC *
|
||
|
pixacompReadMem(const l_uint8 *data,
|
||
|
size_t size)
|
||
|
{
|
||
|
FILE *fp;
|
||
|
PIXAC *pixac;
|
||
|
|
||
|
PROCNAME("pixacompReadMem");
|
||
|
|
||
|
if (!data)
|
||
|
return (PIXAC *)ERROR_PTR("data not defined", procName, NULL);
|
||
|
if ((fp = fopenReadFromMemory(data, size)) == NULL)
|
||
|
return (PIXAC *)ERROR_PTR("stream not opened", procName, NULL);
|
||
|
|
||
|
pixac = pixacompReadStream(fp);
|
||
|
fclose(fp);
|
||
|
if (!pixac) L_ERROR("pixac not read\n", procName);
|
||
|
return pixac;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompWrite()
|
||
|
*
|
||
|
* \param[in] filename
|
||
|
* \param[in] pixac
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* 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.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompWrite(const char *filename,
|
||
|
PIXAC *pixac)
|
||
|
{
|
||
|
l_int32 ret;
|
||
|
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 = fopenWriteStream(filename, "wb")) == NULL)
|
||
|
return ERROR_INT("stream not opened", procName, 1);
|
||
|
ret = pixacompWriteStream(fp, pixac);
|
||
|
fclose(fp);
|
||
|
if (ret)
|
||
|
return ERROR_INT("pixacomp not written to stream", procName, 1);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompWriteStream()
|
||
|
*
|
||
|
* \param[in] fp file stream
|
||
|
* \param[in] pixac
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*/
|
||
|
l_ok
|
||
|
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", n);
|
||
|
fprintf(fp, "Offset of index into array = %d", pixac->offset);
|
||
|
boxaWriteStream(fp, pixac->boxa);
|
||
|
for (i = 0; i < n; i++) {
|
||
|
if ((pixc = pixacompGetPixcomp(pixac, pixac->offset + i, L_NOCOPY))
|
||
|
== NULL)
|
||
|
return ERROR_INT("pixc not found", procName, 1);
|
||
|
fprintf(fp, "\nPixcomp[%d]: w = %d, h = %d, d = %d\n",
|
||
|
i, pixc->w, pixc->h, pixc->d);
|
||
|
fprintf(fp, " comptype = %d, size = %zu, 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);
|
||
|
fprintf(fp, "\n");
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompWriteMem()
|
||
|
*
|
||
|
* \param[out] pdata serialized data of pixac
|
||
|
* \param[out] psize size of serialized data
|
||
|
* \param[in] pixac
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Serializes a pixac in memory and puts the result in a buffer.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompWriteMem(l_uint8 **pdata,
|
||
|
size_t *psize,
|
||
|
PIXAC *pixac)
|
||
|
{
|
||
|
l_int32 ret;
|
||
|
FILE *fp;
|
||
|
|
||
|
PROCNAME("pixacompWriteMem");
|
||
|
|
||
|
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 (!pixac)
|
||
|
return ERROR_INT("&pixac not defined", procName, 1);
|
||
|
|
||
|
#if HAVE_FMEMOPEN
|
||
|
if ((fp = open_memstream((char **)pdata, psize)) == NULL)
|
||
|
return ERROR_INT("stream not opened", procName, 1);
|
||
|
ret = pixacompWriteStream(fp, pixac);
|
||
|
#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 = pixacompWriteStream(fp, pixac);
|
||
|
rewind(fp);
|
||
|
*pdata = l_binaryReadStream(fp, psize);
|
||
|
#endif /* HAVE_FMEMOPEN */
|
||
|
fclose(fp);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------*
|
||
|
* Conversion to pdf *
|
||
|
*--------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixacompConvertToPdf()
|
||
|
*
|
||
|
* \param[in] pixac containing images all at the same resolution
|
||
|
* \param[in] res override the resolution of each input image,
|
||
|
* in ppi; 0 to respect the resolution embedded
|
||
|
* in the input
|
||
|
* \param[in] scalefactor scaling factor applied to each image; > 0.0
|
||
|
* \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE,
|
||
|
* L_FLATE_ENCODE, L_JP2K_ENCODE, or
|
||
|
* L_DEFAULT_ENCODE for default)
|
||
|
* \param[in] quality used for JPEG only; 0 for default (75)
|
||
|
* \param[in] title [optional] pdf title
|
||
|
* \param[in] fileout pdf file of all images
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This follows closely the function pixaConvertToPdf() in pdfio.c.
|
||
|
* (2) The images are encoded with G4 if 1 bpp; JPEG if 8 bpp without
|
||
|
* colormap and many colors, or 32 bpp; FLATE for anything else.
|
||
|
* (3) The scalefactor must be > 0.0; otherwise it is set to 1.0.
|
||
|
* (4) Specifying one of the three encoding types for %type forces
|
||
|
* all images to be compressed with that type. Use 0 to have
|
||
|
* the type determined for each image based on depth and whether
|
||
|
* or not it has a colormap.
|
||
|
* (5) If all images are jpeg compressed, don't require scaling
|
||
|
* and have the same resolution, it is much faster to skip
|
||
|
* transcoding with pixacompFastConvertToPdfData(), and then
|
||
|
* write the data out to file.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompConvertToPdf(PIXAC *pixac,
|
||
|
l_int32 res,
|
||
|
l_float32 scalefactor,
|
||
|
l_int32 type,
|
||
|
l_int32 quality,
|
||
|
const char *title,
|
||
|
const char *fileout)
|
||
|
{
|
||
|
l_uint8 *data;
|
||
|
l_int32 ret;
|
||
|
size_t nbytes;
|
||
|
|
||
|
PROCNAME("pixacompConvertToPdf");
|
||
|
|
||
|
if (!pixac)
|
||
|
return ERROR_INT("pixac not defined", procName, 1);
|
||
|
|
||
|
ret = pixacompConvertToPdfData(pixac, res, scalefactor, type, quality,
|
||
|
title, &data, &nbytes);
|
||
|
if (ret) {
|
||
|
LEPT_FREE(data);
|
||
|
return ERROR_INT("conversion to pdf failed", procName, 1);
|
||
|
}
|
||
|
|
||
|
ret = l_binaryWrite(fileout, "w", data, nbytes);
|
||
|
LEPT_FREE(data);
|
||
|
if (ret)
|
||
|
L_ERROR("pdf data not written to file\n", procName);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompConvertToPdfData()
|
||
|
*
|
||
|
* \param[in] pixac containing images all at the same resolution
|
||
|
* \param[in] res input resolution of all images
|
||
|
* \param[in] scalefactor scaling factor applied to each image; > 0.0
|
||
|
* \param[in] type encoding type (L_JPEG_ENCODE, L_G4_ENCODE,
|
||
|
* L_FLATE_ENCODE, L_JP2K_ENCODE, or
|
||
|
* L_DEFAULT_ENCODE for default)
|
||
|
* \param[in] quality used for JPEG only; 0 for default (75)
|
||
|
* \param[in] title [optional] pdf title
|
||
|
* \param[out] pdata output pdf data (of all images
|
||
|
* \param[out] pnbytes size of output pdf data
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) See pixacompConvertToPdf().
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompConvertToPdfData(PIXAC *pixac,
|
||
|
l_int32 res,
|
||
|
l_float32 scalefactor,
|
||
|
l_int32 type,
|
||
|
l_int32 quality,
|
||
|
const char *title,
|
||
|
l_uint8 **pdata,
|
||
|
size_t *pnbytes)
|
||
|
{
|
||
|
l_uint8 *imdata;
|
||
|
l_int32 i, n, ret, scaledres, pagetype;
|
||
|
size_t imbytes;
|
||
|
L_BYTEA *ba;
|
||
|
PIX *pixs, *pix;
|
||
|
L_PTRA *pa_data;
|
||
|
|
||
|
PROCNAME("pixacompConvertToPdfData");
|
||
|
|
||
|
if (!pdata)
|
||
|
return ERROR_INT("&data not defined", procName, 1);
|
||
|
*pdata = NULL;
|
||
|
if (!pnbytes)
|
||
|
return ERROR_INT("&nbytes not defined", procName, 1);
|
||
|
*pnbytes = 0;
|
||
|
if (!pixac)
|
||
|
return ERROR_INT("pixac not defined", procName, 1);
|
||
|
if (scalefactor <= 0.0) scalefactor = 1.0;
|
||
|
if (type != L_DEFAULT_ENCODE && type != L_JPEG_ENCODE &&
|
||
|
type != L_G4_ENCODE && type != L_FLATE_ENCODE &&
|
||
|
type != L_JP2K_ENCODE) {
|
||
|
L_WARNING("invalid compression type; using per-page default\n",
|
||
|
procName);
|
||
|
type = L_DEFAULT_ENCODE;
|
||
|
}
|
||
|
|
||
|
/* Generate all the encoded pdf strings */
|
||
|
n = pixacompGetCount(pixac);
|
||
|
pa_data = ptraCreate(n);
|
||
|
for (i = 0; i < n; i++) {
|
||
|
if ((pixs =
|
||
|
pixacompGetPix(pixac, pixacompGetOffset(pixac) + i)) == NULL) {
|
||
|
L_ERROR("pix[%d] not retrieved\n", procName, i);
|
||
|
continue;
|
||
|
}
|
||
|
if (pixGetWidth(pixs) == 1) { /* used sometimes as placeholders */
|
||
|
L_INFO("placeholder image[%d] has w = 1\n", procName, i);
|
||
|
pixDestroy(&pixs);
|
||
|
continue;
|
||
|
}
|
||
|
if (scalefactor != 1.0)
|
||
|
pix = pixScale(pixs, scalefactor, scalefactor);
|
||
|
else
|
||
|
pix = pixClone(pixs);
|
||
|
pixDestroy(&pixs);
|
||
|
scaledres = (l_int32)(res * scalefactor);
|
||
|
|
||
|
/* Select the encoding type */
|
||
|
if (type != L_DEFAULT_ENCODE) {
|
||
|
pagetype = type;
|
||
|
} else if (selectDefaultPdfEncoding(pix, &pagetype) != 0) {
|
||
|
L_ERROR("encoding type selection failed for pix[%d]\n",
|
||
|
procName, i);
|
||
|
pixDestroy(&pix);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ret = pixConvertToPdfData(pix, pagetype, quality, &imdata, &imbytes,
|
||
|
0, 0, scaledres, title, NULL, 0);
|
||
|
pixDestroy(&pix);
|
||
|
if (ret) {
|
||
|
L_ERROR("pdf encoding failed for pix[%d]\n", procName, i);
|
||
|
continue;
|
||
|
}
|
||
|
ba = l_byteaInitFromMem(imdata, imbytes);
|
||
|
LEPT_FREE(imdata);
|
||
|
ptraAdd(pa_data, ba);
|
||
|
}
|
||
|
ptraGetActualCount(pa_data, &n);
|
||
|
if (n == 0) {
|
||
|
L_ERROR("no pdf files made\n", procName);
|
||
|
ptraDestroy(&pa_data, FALSE, FALSE);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Concatenate them */
|
||
|
ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes);
|
||
|
|
||
|
ptraGetActualCount(pa_data, &n); /* recalculate in case it changes */
|
||
|
for (i = 0; i < n; i++) {
|
||
|
ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION);
|
||
|
l_byteaDestroy(&ba);
|
||
|
}
|
||
|
ptraDestroy(&pa_data, FALSE, FALSE);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompFastConvertToPdfData()
|
||
|
*
|
||
|
* \param[in] pixac containing images all at the same resolution
|
||
|
* \param[in] title [optional] pdf title
|
||
|
* \param[out] pdata output pdf data (of all images
|
||
|
* \param[out] pnbytes size of output pdf data
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This generates the pdf without transcoding if all the
|
||
|
* images in %pixac are compressed with jpeg.
|
||
|
* Images not jpeg compressed are skipped.
|
||
|
* (2) It assumes all images have the same resolution, and that
|
||
|
* the resolution embedded in each jpeg file is correct.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompFastConvertToPdfData(PIXAC *pixac,
|
||
|
const char *title,
|
||
|
l_uint8 **pdata,
|
||
|
size_t *pnbytes)
|
||
|
{
|
||
|
l_uint8 *imdata;
|
||
|
l_int32 i, n, ret, comptype;
|
||
|
size_t imbytes;
|
||
|
L_BYTEA *ba;
|
||
|
PIXC *pixc;
|
||
|
L_PTRA *pa_data;
|
||
|
|
||
|
PROCNAME("pixacompFastConvertToPdfData");
|
||
|
|
||
|
if (!pdata)
|
||
|
return ERROR_INT("&data not defined", procName, 1);
|
||
|
*pdata = NULL;
|
||
|
if (!pnbytes)
|
||
|
return ERROR_INT("&nbytes not defined", procName, 1);
|
||
|
*pnbytes = 0;
|
||
|
if (!pixac)
|
||
|
return ERROR_INT("pixac not defined", procName, 1);
|
||
|
|
||
|
/* Generate all the encoded pdf strings */
|
||
|
n = pixacompGetCount(pixac);
|
||
|
pa_data = ptraCreate(n);
|
||
|
for (i = 0; i < n; i++) {
|
||
|
if ((pixc = pixacompGetPixcomp(pixac, i, L_NOCOPY)) == NULL) {
|
||
|
L_ERROR("pixc[%d] not retrieved\n", procName, i);
|
||
|
continue;
|
||
|
}
|
||
|
pixcompGetParameters(pixc, NULL, NULL, &comptype, NULL);
|
||
|
if (comptype != IFF_JFIF_JPEG) {
|
||
|
L_ERROR("pixc[%d] not jpeg compressed\n", procName, i);
|
||
|
continue;
|
||
|
}
|
||
|
ret = pixcompFastConvertToPdfData(pixc, title, &imdata, &imbytes);
|
||
|
if (ret) {
|
||
|
L_ERROR("pdf encoding failed for pixc[%d]\n", procName, i);
|
||
|
continue;
|
||
|
}
|
||
|
ba = l_byteaInitFromMem(imdata, imbytes);
|
||
|
LEPT_FREE(imdata);
|
||
|
ptraAdd(pa_data, ba);
|
||
|
}
|
||
|
ptraGetActualCount(pa_data, &n);
|
||
|
if (n == 0) {
|
||
|
L_ERROR("no pdf files made\n", procName);
|
||
|
ptraDestroy(&pa_data, FALSE, FALSE);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* Concatenate them */
|
||
|
ret = ptraConcatenatePdfToData(pa_data, NULL, pdata, pnbytes);
|
||
|
|
||
|
/* Clean up */
|
||
|
ptraGetActualCount(pa_data, &n); /* recalculate in case it changes */
|
||
|
for (i = 0; i < n; i++) {
|
||
|
ba = (L_BYTEA *)ptraRemove(pa_data, i, L_NO_COMPACTION);
|
||
|
l_byteaDestroy(&ba);
|
||
|
}
|
||
|
ptraDestroy(&pa_data, FALSE, FALSE);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixcompFastConvertToPdfData()
|
||
|
*
|
||
|
* \param[in] pixc containing images all at the same resolution
|
||
|
* \param[in] title [optional] pdf title
|
||
|
* \param[out] pdata output pdf data (of all images
|
||
|
* \param[out] pnbytes size of output pdf data
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This generates the pdf without transcoding.
|
||
|
* (2) It assumes all images are jpeg encoded, have the same
|
||
|
* resolution, and that the resolution embedded in each
|
||
|
* jpeg file is correct. (It is transferred to the pdf
|
||
|
* via the cid.)
|
||
|
* </pre>
|
||
|
*/
|
||
|
static l_int32
|
||
|
pixcompFastConvertToPdfData(PIXC *pixc,
|
||
|
const char *title,
|
||
|
l_uint8 **pdata,
|
||
|
size_t *pnbytes)
|
||
|
{
|
||
|
l_uint8 *data;
|
||
|
L_COMP_DATA *cid;
|
||
|
|
||
|
PROCNAME("pixacompFastConvertToPdfData");
|
||
|
|
||
|
if (!pdata)
|
||
|
return ERROR_INT("&data not defined", procName, 1);
|
||
|
*pdata = NULL;
|
||
|
if (!pnbytes)
|
||
|
return ERROR_INT("&nbytes not defined", procName, 1);
|
||
|
*pnbytes = 0;
|
||
|
if (!pixc)
|
||
|
return ERROR_INT("pixc not defined", procName, 1);
|
||
|
|
||
|
/* Make a copy of the data */
|
||
|
data = l_binaryCopy(pixc->data, pixc->size);
|
||
|
cid = l_generateJpegDataMem(data, pixc->size, 0);
|
||
|
|
||
|
/* Note: cid is destroyed, along with data, by this function */
|
||
|
return cidConvertToPdfData(cid, title, pdata, pnbytes);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------*
|
||
|
* Output for debugging *
|
||
|
*--------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixacompWriteStreamInfo()
|
||
|
*
|
||
|
* \param[in] fp file stream
|
||
|
* \param[in] pixac
|
||
|
* \param[in] text [optional] identifying string; can be null
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*/
|
||
|
l_ok
|
||
|
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);
|
||
|
fprintf(fp, "Offset of index into array: %d\n", pixac->offset);
|
||
|
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, pixac->offset + i, L_NOCOPY);
|
||
|
pixcompWriteStreamInfo(fp, pixc, NULL);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixcompWriteStreamInfo()
|
||
|
*
|
||
|
* \param[in] fp file stream
|
||
|
* \param[in] pixc
|
||
|
* \param[in] text [optional] identifying string; can be null
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*/
|
||
|
l_ok
|
||
|
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 = %zu\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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompDisplayTiledAndScaled()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] outdepth output depth: 1, 8 or 32 bpp
|
||
|
* \param[in] tilewidth each pix is scaled to this width
|
||
|
* \param[in] ncols number of tiles in each row
|
||
|
* \param[in] background 0 for white, 1 for black; this is the color
|
||
|
* of the spacing between the images
|
||
|
* \param[in] spacing between images, and on outside
|
||
|
* \param[in] border width of additional black border on each image;
|
||
|
* use 0 for no border
|
||
|
* \return pix of tiled images, or NULL on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* 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) See pixaDisplayTiledAndScaled() for details.
|
||
|
* </pre>
|
||
|
*/
|
||
|
PIX *
|
||
|
pixacompDisplayTiledAndScaled(PIXAC *pixac,
|
||
|
l_int32 outdepth,
|
||
|
l_int32 tilewidth,
|
||
|
l_int32 ncols,
|
||
|
l_int32 background,
|
||
|
l_int32 spacing,
|
||
|
l_int32 border)
|
||
|
{
|
||
|
PIX *pixd;
|
||
|
PIXA *pixa;
|
||
|
|
||
|
PROCNAME("pixacompDisplayTiledAndScaled");
|
||
|
|
||
|
if (!pixac)
|
||
|
return (PIX *)ERROR_PTR("pixac not defined", procName, NULL);
|
||
|
|
||
|
if ((pixa = pixaCreateFromPixacomp(pixac, L_COPY)) == NULL)
|
||
|
return (PIX *)ERROR_PTR("pixa not made", procName, NULL);
|
||
|
|
||
|
pixd = pixaDisplayTiledAndScaled(pixa, outdepth, tilewidth, ncols,
|
||
|
background, spacing, border);
|
||
|
pixaDestroy(&pixa);
|
||
|
return pixd;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixacompWriteFiles()
|
||
|
*
|
||
|
* \param[in] pixac
|
||
|
* \param[in] subdir subdirectory of /tmp
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*/
|
||
|
l_ok
|
||
|
pixacompWriteFiles(PIXAC *pixac,
|
||
|
const char *subdir)
|
||
|
{
|
||
|
char buf[128];
|
||
|
l_int32 i, n;
|
||
|
PIXC *pixc;
|
||
|
|
||
|
PROCNAME("pixacompWriteFiles");
|
||
|
|
||
|
if (!pixac)
|
||
|
return ERROR_INT("pixac not defined", procName, 1);
|
||
|
|
||
|
if (lept_mkdir(subdir) > 0)
|
||
|
return ERROR_INT("invalid subdir", procName, 1);
|
||
|
|
||
|
n = pixacompGetCount(pixac);
|
||
|
for (i = 0; i < n; i++) {
|
||
|
pixc = pixacompGetPixcomp(pixac, i, L_NOCOPY);
|
||
|
snprintf(buf, sizeof(buf), "/tmp/%s/%03d", subdir, i);
|
||
|
pixcompWriteFile(buf, pixc);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
extern const char *ImageFileFormatExtensions[];
|
||
|
|
||
|
/*!
|
||
|
* \brief pixcompWriteFile()
|
||
|
*
|
||
|
* \param[in] rootname
|
||
|
* \param[in] pixc
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The compressed data is written to file, and the filename is
|
||
|
* generated by appending the format extension to %rootname.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixcompWriteFile(const char *rootname,
|
||
|
PIXC *pixc)
|
||
|
{
|
||
|
char buf[128];
|
||
|
|
||
|
PROCNAME("pixcompWriteFile");
|
||
|
|
||
|
if (!pixc)
|
||
|
return ERROR_INT("pixc not defined", procName, 1);
|
||
|
|
||
|
snprintf(buf, sizeof(buf), "%s.%s", rootname,
|
||
|
ImageFileFormatExtensions[pixc->comptype]);
|
||
|
l_binaryWrite(buf, "w", pixc->data, pixc->size);
|
||
|
return 0;
|
||
|
}
|