mirror of http://192.168.1.51:8099/lmh188/twain3.0
1410 lines
44 KiB
C
1410 lines
44 KiB
C
|
/*====================================================================*
|
||
|
- Copyright (C) 2001-2016 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.
|
||
|
*====================================================================*/
|
||
|
|
||
|
/*
|
||
|
* writefile.c
|
||
|
*
|
||
|
* Set jpeg quality for pixWrite() and pixWriteMem()
|
||
|
* l_int32 l_jpegSetQuality()
|
||
|
*
|
||
|
* Set global variable LeptDebugOK for writing to named temp files
|
||
|
* l_int32 setLeptDebugOK()
|
||
|
*
|
||
|
* High-level procedures for writing images to file:
|
||
|
* l_int32 pixaWriteFiles()
|
||
|
* l_int32 pixWriteDebug()
|
||
|
* l_int32 pixWrite()
|
||
|
* l_int32 pixWriteAutoFormat()
|
||
|
* l_int32 pixWriteStream()
|
||
|
* l_int32 pixWriteImpliedFormat()
|
||
|
*
|
||
|
* Selection of output format if default is requested
|
||
|
* l_int32 pixChooseOutputFormat()
|
||
|
* l_int32 getImpliedFileFormat()
|
||
|
* l_int32 pixGetAutoFormat()
|
||
|
* const char *getFormatExtension()
|
||
|
*
|
||
|
* Write to memory
|
||
|
* l_int32 pixWriteMem()
|
||
|
*
|
||
|
* Image display for debugging
|
||
|
* l_int32 l_fileDisplay()
|
||
|
* l_int32 pixDisplay()
|
||
|
* l_int32 pixDisplayWithTitle()
|
||
|
* l_int32 pixSaveTiled()
|
||
|
* l_int32 pixSaveTiledOutline()
|
||
|
* l_int32 pixSaveTiledWithText()
|
||
|
* void l_chooseDisplayProg()
|
||
|
*
|
||
|
* Deprecated pix output for debugging (still used in tesseract 3.05)
|
||
|
* l_int32 pixDisplayWrite()
|
||
|
*
|
||
|
* Supported file formats:
|
||
|
* (1) Writing is supported without any external libraries:
|
||
|
* bmp
|
||
|
* pnm (including pbm, pgm, etc)
|
||
|
* spix (raw serialized)
|
||
|
* (2) Writing is supported with installation of external libraries:
|
||
|
* png
|
||
|
* jpg (standard jfif version)
|
||
|
* tiff (including most varieties of compression)
|
||
|
* gif
|
||
|
* webp
|
||
|
* jp2 (jpeg2000)
|
||
|
* (3) Writing is supported through special interfaces:
|
||
|
* ps (PostScript, in psio1.c, psio2.c):
|
||
|
* level 1 (uncompressed)
|
||
|
* level 2 (g4 and dct encoding: requires tiff, jpg)
|
||
|
* level 3 (g4, dct and flate encoding: requires tiff, jpg, zlib)
|
||
|
* pdf (PDF, in pdfio.c):
|
||
|
* level 1 (g4 and dct encoding: requires tiff, jpg)
|
||
|
* level 2 (g4, dct and flate encoding: requires tiff, jpg, zlib)
|
||
|
*/
|
||
|
|
||
|
#include <string.h>
|
||
|
#include "allheaders.h"
|
||
|
|
||
|
/* Display program (xv, xli, xzgv, open) to be invoked by pixDisplay() */
|
||
|
#ifdef _WIN32
|
||
|
static l_int32 var_DISPLAY_PROG = L_DISPLAY_WITH_IV; /* default */
|
||
|
#elif defined(__APPLE__)
|
||
|
static l_int32 var_DISPLAY_PROG = L_DISPLAY_WITH_OPEN; /* default */
|
||
|
#else
|
||
|
static l_int32 var_DISPLAY_PROG = L_DISPLAY_WITH_XZGV; /* default */
|
||
|
#endif /* _WIN32 */
|
||
|
|
||
|
//static const l_int32 Bufsize = 512;
|
||
|
#define Bufsize 512
|
||
|
static const l_int32 MaxDisplayWidth = 1000;
|
||
|
static const l_int32 MaxDisplayHeight = 800;
|
||
|
static const l_int32 MaxSizeForPng = 200;
|
||
|
|
||
|
/* PostScript output for printing */
|
||
|
static const l_float32 DefaultScaling = 1.0;
|
||
|
|
||
|
/* Global array of image file format extension names. */
|
||
|
/* This is in 1-1 corrspondence with format enum in imageio.h. */
|
||
|
/* The empty string at the end represents the serialized format, */
|
||
|
/* which has no recognizable extension name, but the array must */
|
||
|
/* be padded to agree with the format enum. */
|
||
|
/* (Note on 'const': The size of the array can't be defined 'const' */
|
||
|
/* because that makes it static. The 'const' in the definition of */
|
||
|
/* the array refers to the strings in the array; the ptr to the */
|
||
|
/* array is not const and can be used 'extern' in other files.) */
|
||
|
LEPT_DLL l_int32 NumImageFileFormatExtensions = 20; /* array size */
|
||
|
LEPT_DLL const char *ImageFileFormatExtensions[] =
|
||
|
{"unknown",
|
||
|
"bmp",
|
||
|
"jpg",
|
||
|
"png",
|
||
|
"tif",
|
||
|
"tif",
|
||
|
"tif",
|
||
|
"tif",
|
||
|
"tif",
|
||
|
"tif",
|
||
|
"tif",
|
||
|
"pnm",
|
||
|
"ps",
|
||
|
"gif",
|
||
|
"jp2",
|
||
|
"webp",
|
||
|
"pdf",
|
||
|
"tif",
|
||
|
"default",
|
||
|
""};
|
||
|
|
||
|
/* Local map of image file name extension to output format */
|
||
|
struct ExtensionMap
|
||
|
{
|
||
|
char extension[8];
|
||
|
l_int32 format;
|
||
|
};
|
||
|
static const struct ExtensionMap extension_map[] =
|
||
|
{ { ".bmp", IFF_BMP },
|
||
|
{ ".jpg", IFF_JFIF_JPEG },
|
||
|
{ ".jpeg", IFF_JFIF_JPEG },
|
||
|
{ ".png", IFF_PNG },
|
||
|
{ ".tif", IFF_TIFF },
|
||
|
{ ".tiff", IFF_TIFF },
|
||
|
{ ".pnm", IFF_PNM },
|
||
|
{ ".gif", IFF_GIF },
|
||
|
{ ".jp2", IFF_JP2 },
|
||
|
{ ".ps", IFF_PS },
|
||
|
{ ".pdf", IFF_LPDF },
|
||
|
{ ".webp", IFF_WEBP } };
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Set jpeg quality for pixWrite() and pixWriteMem() *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/* Parameter that controls jpeg quality for high-level calls. */
|
||
|
static l_int32 var_JPEG_QUALITY = 75; /* default */
|
||
|
|
||
|
/*!
|
||
|
* \brief l_jpegSetQuality()
|
||
|
*
|
||
|
* \param[in] new_quality 1 - 100; 75 is default; 0 defaults to 75
|
||
|
* \return prev previous quality
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This variable is used in pixWriteStream() and pixWriteMem(),
|
||
|
* to control the jpeg quality. The default is 75.
|
||
|
* (2) It returns the previous quality, so for example:
|
||
|
* l_int32 prev = l_jpegSetQuality(85); //sets to 85
|
||
|
* pixWriteStream(...);
|
||
|
* l_jpegSetQuality(prev); // resets to previous value
|
||
|
* (3) On error, logs a message and does not change the variable.
|
||
|
*/
|
||
|
l_int32
|
||
|
l_jpegSetQuality(l_int32 new_quality)
|
||
|
{
|
||
|
l_int32 prevq, newq;
|
||
|
|
||
|
PROCNAME("l_jpeqSetQuality");
|
||
|
|
||
|
prevq = var_JPEG_QUALITY;
|
||
|
newq = (new_quality == 0) ? 75 : new_quality;
|
||
|
if (newq < 1 || newq > 100)
|
||
|
L_ERROR("invalid jpeg quality; unchanged\n", procName);
|
||
|
else
|
||
|
var_JPEG_QUALITY = newq;
|
||
|
return prevq;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------------------*
|
||
|
* Set global variable LeptDebugOK for writing to named temp files *
|
||
|
*----------------------------------------------------------------------*/
|
||
|
l_int32 LeptDebugOK = 0; /* default value */
|
||
|
/*!
|
||
|
* \brief setLeptDebugOK()
|
||
|
*
|
||
|
* \param[in] allow TRUE (1) or FALSE (0)
|
||
|
* \return void
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This sets or clears the global variable LeptDebugOK, to
|
||
|
* control writing files in a temp directory with names that
|
||
|
* are compiled in.
|
||
|
* (2) The default in the library distribution is 0. Call with
|
||
|
* %allow = 1 for development and debugging.
|
||
|
*/
|
||
|
void
|
||
|
setLeptDebugOK(l_int32 allow)
|
||
|
{
|
||
|
if (allow != 0) allow = 1;
|
||
|
LeptDebugOK = allow;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Top-level procedures for writing images to file *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixaWriteFiles()
|
||
|
*
|
||
|
* \param[in] rootname
|
||
|
* \param[in] pixa
|
||
|
* \param[in] format defined in imageio.h; see notes for default
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Use %format = IFF_DEFAULT to decide the output format
|
||
|
* individually for each pix.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixaWriteFiles(const char *rootname,
|
||
|
PIXA *pixa,
|
||
|
l_int32 format)
|
||
|
{
|
||
|
char bigbuf[Bufsize];
|
||
|
l_int32 i, n, pixformat;
|
||
|
PIX *pix;
|
||
|
|
||
|
PROCNAME("pixaWriteFiles");
|
||
|
|
||
|
if (!rootname)
|
||
|
return ERROR_INT("rootname not defined", procName, 1);
|
||
|
if (!pixa)
|
||
|
return ERROR_INT("pixa not defined", procName, 1);
|
||
|
if (format < 0 || format == IFF_UNKNOWN ||
|
||
|
format >= NumImageFileFormatExtensions)
|
||
|
return ERROR_INT("invalid format", procName, 1);
|
||
|
|
||
|
n = pixaGetCount(pixa);
|
||
|
for (i = 0; i < n; i++) {
|
||
|
pix = pixaGetPix(pixa, i, L_CLONE);
|
||
|
if (format == IFF_DEFAULT)
|
||
|
pixformat = pixChooseOutputFormat(pix);
|
||
|
else
|
||
|
pixformat = format;
|
||
|
snprintf(bigbuf, Bufsize, "%s%03d.%s", rootname, i,
|
||
|
ImageFileFormatExtensions[pixformat]);
|
||
|
pixWrite(bigbuf, pix, pixformat);
|
||
|
pixDestroy(&pix);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixWriteDebug()
|
||
|
*
|
||
|
* \param[in] fname
|
||
|
* \param[in] pix
|
||
|
* \param[in] format defined in imageio.h
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Debug version, intended for use in the library when writing
|
||
|
* to files in a temp directory with names that are compiled in.
|
||
|
* This is used instead of pixWrite() for all such library calls.
|
||
|
* (2) The global variable LeptDebugOK defaults to 0, and can be set
|
||
|
* or cleared by the function setLeptDebugOK().
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixWriteDebug(const char *fname,
|
||
|
PIX *pix,
|
||
|
l_int32 format)
|
||
|
{
|
||
|
PROCNAME("pixWriteDebug");
|
||
|
|
||
|
if (LeptDebugOK) {
|
||
|
return pixWrite(fname, pix, format);
|
||
|
} else {
|
||
|
L_INFO("write to named temp file %s is disabled\n", procName, fname);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixWrite()
|
||
|
*
|
||
|
* \param[in] fname
|
||
|
* \param[in] pix
|
||
|
* \param[in] format defined in imageio.h
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Open for write using binary mode (with the "b" flag)
|
||
|
* to avoid having Windows automatically translate the NL
|
||
|
* into CRLF, which corrupts image files. On non-windows
|
||
|
* systems this flag should be ignored, per ISO C90.
|
||
|
* Thanks to Dave Bryan for pointing this out.
|
||
|
* (2) If the default image format IFF_DEFAULT is requested:
|
||
|
* use the input format if known; otherwise, use a lossless format.
|
||
|
* (3) The default jpeg quality is 75. For some other value,
|
||
|
* Use l_jpegSetQuality().
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixWrite(const char *fname,
|
||
|
PIX *pix,
|
||
|
l_int32 format)
|
||
|
{
|
||
|
l_int32 ret;
|
||
|
FILE *fp;
|
||
|
|
||
|
PROCNAME("pixWrite");
|
||
|
|
||
|
if (!pix)
|
||
|
return ERROR_INT("pix not defined", procName, 1);
|
||
|
if (!fname)
|
||
|
return ERROR_INT("fname not defined", procName, 1);
|
||
|
|
||
|
if ((fp = fopenWriteStream(fname, "wb+")) == NULL)
|
||
|
return ERROR_INT("stream not opened", procName, 1);
|
||
|
|
||
|
ret = pixWriteStream(fp, pix, format);
|
||
|
fclose(fp);
|
||
|
if (ret)
|
||
|
return ERROR_INT("pix not written to stream", procName, 1);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixWriteAutoFormat()
|
||
|
*
|
||
|
* \param[in] filename
|
||
|
* \param[in] pix
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*/
|
||
|
l_ok
|
||
|
pixWriteAutoFormat(const char *filename,
|
||
|
PIX *pix)
|
||
|
{
|
||
|
l_int32 format;
|
||
|
|
||
|
PROCNAME("pixWriteAutoFormat");
|
||
|
|
||
|
if (!pix)
|
||
|
return ERROR_INT("pix not defined", procName, 1);
|
||
|
if (!filename)
|
||
|
return ERROR_INT("filename not defined", procName, 1);
|
||
|
|
||
|
if (pixGetAutoFormat(pix, &format))
|
||
|
return ERROR_INT("auto format not returned", procName, 1);
|
||
|
return pixWrite(filename, pix, format);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixWriteStream()
|
||
|
*
|
||
|
* \param[in] fp file stream
|
||
|
* \param[in] pix
|
||
|
* \param[in] format
|
||
|
* \return 0 if OK; 1 on error.
|
||
|
*/
|
||
|
l_ok
|
||
|
pixWriteStream(FILE *fp,
|
||
|
PIX *pix,
|
||
|
l_int32 format)
|
||
|
{
|
||
|
PROCNAME("pixWriteStream");
|
||
|
|
||
|
if (!fp)
|
||
|
return ERROR_INT("stream not defined", procName, 1);
|
||
|
if (!pix)
|
||
|
return ERROR_INT("pix not defined", procName, 1);
|
||
|
|
||
|
if (format == IFF_DEFAULT)
|
||
|
format = pixChooseOutputFormat(pix);
|
||
|
|
||
|
switch(format)
|
||
|
{
|
||
|
case IFF_BMP:
|
||
|
pixWriteStreamBmp(fp, pix);
|
||
|
break;
|
||
|
|
||
|
case IFF_JFIF_JPEG: /* default quality; baseline sequential */
|
||
|
return pixWriteStreamJpeg(fp, pix, var_JPEG_QUALITY, 0);
|
||
|
break;
|
||
|
|
||
|
case IFF_PNG: /* no gamma value stored */
|
||
|
return pixWriteStreamPng(fp, pix, 0.0);
|
||
|
break;
|
||
|
|
||
|
case IFF_TIFF: /* uncompressed */
|
||
|
case IFF_TIFF_PACKBITS: /* compressed, binary only */
|
||
|
case IFF_TIFF_RLE: /* compressed, binary only */
|
||
|
case IFF_TIFF_G3: /* compressed, binary only */
|
||
|
case IFF_TIFF_G4: /* compressed, binary only */
|
||
|
case IFF_TIFF_LZW: /* compressed, all depths */
|
||
|
case IFF_TIFF_ZIP: /* compressed, all depths */
|
||
|
case IFF_TIFF_JPEG: /* compressed, 8 bpp gray and 32 bpp rgb */
|
||
|
return pixWriteStreamTiff(fp, pix, format);
|
||
|
break;
|
||
|
|
||
|
case IFF_PNM:
|
||
|
return pixWriteStreamPnm(fp, pix);
|
||
|
break;
|
||
|
|
||
|
case IFF_PS:
|
||
|
return pixWriteStreamPS(fp, pix, NULL, 0, DefaultScaling);
|
||
|
break;
|
||
|
|
||
|
case IFF_GIF:
|
||
|
return pixWriteStreamGif(fp, pix);
|
||
|
break;
|
||
|
|
||
|
case IFF_JP2:
|
||
|
return pixWriteStreamJp2k(fp, pix, 34, 4, 0, 0);
|
||
|
break;
|
||
|
|
||
|
case IFF_WEBP:
|
||
|
return pixWriteStreamWebP(fp, pix, 80, 0);
|
||
|
break;
|
||
|
|
||
|
case IFF_LPDF:
|
||
|
return pixWriteStreamPdf(fp, pix, 0, NULL);
|
||
|
break;
|
||
|
|
||
|
case IFF_SPIX:
|
||
|
return pixWriteStreamSpix(fp, pix);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return ERROR_INT("unknown format", procName, 1);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixWriteImpliedFormat()
|
||
|
*
|
||
|
* \param[in] filename
|
||
|
* \param[in] pix
|
||
|
* \param[in] quality iff JPEG; 1 - 100, 0 for default
|
||
|
* \param[in] progressive iff JPEG; 0 for baseline seq., 1 for progressive
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This determines the output format from the filename extension.
|
||
|
* (2) The last two args are ignored except for requests for jpeg files.
|
||
|
* (3) The jpeg default quality is 75.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixWriteImpliedFormat(const char *filename,
|
||
|
PIX *pix,
|
||
|
l_int32 quality,
|
||
|
l_int32 progressive)
|
||
|
{
|
||
|
l_int32 format;
|
||
|
|
||
|
PROCNAME("pixWriteImpliedFormat");
|
||
|
|
||
|
if (!filename)
|
||
|
return ERROR_INT("filename not defined", procName, 1);
|
||
|
if (!pix)
|
||
|
return ERROR_INT("pix not defined", procName, 1);
|
||
|
|
||
|
/* Determine output format */
|
||
|
format = getImpliedFileFormat(filename);
|
||
|
if (format == IFF_UNKNOWN) {
|
||
|
format = IFF_PNG;
|
||
|
} else if (format == IFF_TIFF) {
|
||
|
if (pixGetDepth(pix) == 1)
|
||
|
format = IFF_TIFF_G4;
|
||
|
else
|
||
|
#ifdef _WIN32
|
||
|
format = IFF_TIFF_LZW; /* poor compression */
|
||
|
#else
|
||
|
format = IFF_TIFF_ZIP; /* native windows tools can't handle this */
|
||
|
#endif /* _WIN32 */
|
||
|
}
|
||
|
|
||
|
if (format == IFF_JFIF_JPEG) {
|
||
|
quality = L_MIN(quality, 100);
|
||
|
quality = L_MAX(quality, 0);
|
||
|
if (progressive != 0 && progressive != 1) {
|
||
|
progressive = 0;
|
||
|
L_WARNING("invalid progressive; setting to baseline\n", procName);
|
||
|
}
|
||
|
if (quality == 0)
|
||
|
quality = 75;
|
||
|
pixWriteJpeg (filename, pix, quality, progressive);
|
||
|
} else {
|
||
|
pixWrite(filename, pix, format);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Selection of output format if default is requested *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixChooseOutputFormat()
|
||
|
*
|
||
|
* \param[in] pix
|
||
|
* \return output format, or 0 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This should only be called if the requested format is IFF_DEFAULT.
|
||
|
* (2) If the pix wasn't read from a file, its input format value
|
||
|
* will be IFF_UNKNOWN, and in that case it is written out
|
||
|
* in a compressed but lossless format.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_int32
|
||
|
pixChooseOutputFormat(PIX *pix)
|
||
|
{
|
||
|
l_int32 d, format;
|
||
|
|
||
|
PROCNAME("pixChooseOutputFormat");
|
||
|
|
||
|
if (!pix)
|
||
|
return ERROR_INT("pix not defined", procName, 0);
|
||
|
|
||
|
d = pixGetDepth(pix);
|
||
|
format = pixGetInputFormat(pix);
|
||
|
if (format == IFF_UNKNOWN) { /* output lossless */
|
||
|
if (d == 1)
|
||
|
format = IFF_TIFF_G4;
|
||
|
else
|
||
|
format = IFF_PNG;
|
||
|
}
|
||
|
|
||
|
return format;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief getImpliedFileFormat()
|
||
|
*
|
||
|
* \param[in] filename
|
||
|
* \return output format, or IFF_UNKNOWN on error or invalid extension.
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This determines the output file format from the extension
|
||
|
* of the input filename.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_int32
|
||
|
getImpliedFileFormat(const char *filename)
|
||
|
{
|
||
|
char *extension;
|
||
|
int i, numext;
|
||
|
l_int32 format = IFF_UNKNOWN;
|
||
|
|
||
|
if (splitPathAtExtension (filename, NULL, &extension))
|
||
|
return IFF_UNKNOWN;
|
||
|
|
||
|
numext = sizeof(extension_map) / sizeof(extension_map[0]);
|
||
|
for (i = 0; i < numext; i++) {
|
||
|
if (!strcmp(extension, extension_map[i].extension)) {
|
||
|
format = extension_map[i].format;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LEPT_FREE(extension);
|
||
|
return format;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixGetAutoFormat()
|
||
|
*
|
||
|
* \param[in] pix
|
||
|
* \param[in] &format
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) The output formats are restricted to tiff, jpeg and png
|
||
|
* because these are the most commonly used image formats and
|
||
|
* the ones that are typically installed with leptonica.
|
||
|
* (2) This decides what compression to use based on the pix.
|
||
|
* It chooses tiff-g4 if 1 bpp without a colormap, jpeg with
|
||
|
* quality 75 if grayscale, rgb or rgba (where it loses
|
||
|
* the alpha layer), and lossless png for all other situations.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixGetAutoFormat(PIX *pix,
|
||
|
l_int32 *pformat)
|
||
|
{
|
||
|
l_int32 d;
|
||
|
PIXCMAP *cmap;
|
||
|
|
||
|
PROCNAME("pixGetAutoFormat");
|
||
|
|
||
|
if (!pformat)
|
||
|
return ERROR_INT("&format not defined", procName, 0);
|
||
|
*pformat = IFF_UNKNOWN;
|
||
|
if (!pix)
|
||
|
return ERROR_INT("pix not defined", procName, 0);
|
||
|
|
||
|
d = pixGetDepth(pix);
|
||
|
cmap = pixGetColormap(pix);
|
||
|
if (d == 1 && !cmap) {
|
||
|
*pformat = IFF_TIFF_G4;
|
||
|
} else if ((d == 8 && !cmap) || d == 24 || d == 32) {
|
||
|
*pformat = IFF_JFIF_JPEG;
|
||
|
} else {
|
||
|
*pformat = IFF_PNG;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief getFormatExtension()
|
||
|
*
|
||
|
* \param[in] format integer
|
||
|
* \return extension string, or NULL if format is out of range
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This string is NOT owned by the caller; it is just a pointer
|
||
|
* to a global string. Do not free it.
|
||
|
* </pre>
|
||
|
*/
|
||
|
const char *
|
||
|
getFormatExtension(l_int32 format)
|
||
|
{
|
||
|
PROCNAME("getFormatExtension");
|
||
|
|
||
|
if (format < 0 || format >= NumImageFileFormatExtensions)
|
||
|
return (const char *)ERROR_PTR("invalid format", procName, NULL);
|
||
|
|
||
|
return ImageFileFormatExtensions[format];
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Write to memory *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixWriteMem()
|
||
|
*
|
||
|
* \param[out] pdata data of tiff compressed image
|
||
|
* \param[out] psize size of returned data
|
||
|
* \param[in] pix
|
||
|
* \param[in] format defined in imageio.h
|
||
|
* \return 0 if OK, 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) On windows, this will only write tiff and PostScript to memory.
|
||
|
* For other formats, it requires open_memstream(3).
|
||
|
* (2) PostScript output is uncompressed, in hex ascii.
|
||
|
* Most printers support level 2 compression (tiff_g4 for 1 bpp,
|
||
|
* jpeg for 8 and 32 bpp).
|
||
|
* (3) The default jpeg quality is 75. For some other value,
|
||
|
* Use l_jpegSetQuality().
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixWriteMem(l_uint8 **pdata,
|
||
|
size_t *psize,
|
||
|
PIX *pix,
|
||
|
l_int32 format)
|
||
|
{
|
||
|
l_int32 ret;
|
||
|
|
||
|
PROCNAME("pixWriteMem");
|
||
|
|
||
|
if (!pdata)
|
||
|
return ERROR_INT("&data not defined", procName, 1 );
|
||
|
if (!psize)
|
||
|
return ERROR_INT("&size not defined", procName, 1 );
|
||
|
if (!pix)
|
||
|
return ERROR_INT("&pix not defined", procName, 1 );
|
||
|
|
||
|
if (format == IFF_DEFAULT)
|
||
|
format = pixChooseOutputFormat(pix);
|
||
|
|
||
|
switch(format)
|
||
|
{
|
||
|
case IFF_BMP:
|
||
|
ret = pixWriteMemBmp(pdata, psize, pix);
|
||
|
break;
|
||
|
|
||
|
case IFF_JFIF_JPEG: /* default quality; baseline sequential */
|
||
|
ret = pixWriteMemJpeg(pdata, psize, pix, var_JPEG_QUALITY, 0);
|
||
|
break;
|
||
|
|
||
|
case IFF_PNG: /* no gamma value stored */
|
||
|
ret = pixWriteMemPng(pdata, psize, pix, 0.0);
|
||
|
break;
|
||
|
|
||
|
case IFF_TIFF: /* uncompressed */
|
||
|
case IFF_TIFF_PACKBITS: /* compressed, binary only */
|
||
|
case IFF_TIFF_RLE: /* compressed, binary only */
|
||
|
case IFF_TIFF_G3: /* compressed, binary only */
|
||
|
case IFF_TIFF_G4: /* compressed, binary only */
|
||
|
case IFF_TIFF_LZW: /* compressed, all depths */
|
||
|
case IFF_TIFF_ZIP: /* compressed, all depths */
|
||
|
ret = pixWriteMemTiff(pdata, psize, pix, format);
|
||
|
break;
|
||
|
|
||
|
case IFF_PNM:
|
||
|
ret = pixWriteMemPnm(pdata, psize, pix);
|
||
|
break;
|
||
|
|
||
|
case IFF_PS:
|
||
|
ret = pixWriteMemPS(pdata, psize, pix, NULL, 0, DefaultScaling);
|
||
|
break;
|
||
|
|
||
|
case IFF_GIF:
|
||
|
ret = pixWriteMemGif(pdata, psize, pix);
|
||
|
break;
|
||
|
|
||
|
case IFF_JP2:
|
||
|
ret = pixWriteMemJp2k(pdata, psize, pix, 34, 0, 0, 0);
|
||
|
break;
|
||
|
|
||
|
case IFF_WEBP:
|
||
|
ret = pixWriteMemWebP(pdata, psize, pix, 80, 0);
|
||
|
break;
|
||
|
|
||
|
case IFF_LPDF:
|
||
|
ret = pixWriteMemPdf(pdata, psize, pix, 0, NULL);
|
||
|
break;
|
||
|
|
||
|
case IFF_SPIX:
|
||
|
ret = pixWriteMemSpix(pdata, psize, pix);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return ERROR_INT("unknown format", procName, 1);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Image display for debugging *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief l_fileDisplay()
|
||
|
*
|
||
|
* \param[in] fname
|
||
|
* \param[in] x, y location of display frame on the screen
|
||
|
* \param[in] scale scale factor (use 0 to skip display)
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This is a convenient wrapper for displaying image files.
|
||
|
* (2) It does nothing unless LeptDebugOK == TRUE.
|
||
|
* (2) Set %scale = 0 to disable display.
|
||
|
* (3) This downscales 1 bpp to gray.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
l_fileDisplay(const char *fname,
|
||
|
l_int32 x,
|
||
|
l_int32 y,
|
||
|
l_float32 scale)
|
||
|
{
|
||
|
PIX *pixs, *pixd;
|
||
|
|
||
|
PROCNAME("l_fileDisplay");
|
||
|
|
||
|
if (!LeptDebugOK) {
|
||
|
L_INFO("displaying files is disabled; "
|
||
|
"use setLeptDebugOK(1) to enable\n", procName);
|
||
|
return 0;
|
||
|
}
|
||
|
if (scale == 0.0)
|
||
|
return 0;
|
||
|
if (scale < 0.0)
|
||
|
return ERROR_INT("invalid scale factor", procName, 1);
|
||
|
if ((pixs = pixRead(fname)) == NULL)
|
||
|
return ERROR_INT("pixs not read", procName, 1);
|
||
|
|
||
|
if (scale == 1.0) {
|
||
|
pixd = pixClone(pixs);
|
||
|
} else {
|
||
|
if (scale < 1.0 && pixGetDepth(pixs) == 1)
|
||
|
pixd = pixScaleToGray(pixs, scale);
|
||
|
else
|
||
|
pixd = pixScale(pixs, scale, scale);
|
||
|
}
|
||
|
pixDisplay(pixd, x, y);
|
||
|
pixDestroy(&pixs);
|
||
|
pixDestroy(&pixd);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixDisplay()
|
||
|
*
|
||
|
* \param[in] pix 1, 2, 4, 8, 16, 32 bpp
|
||
|
* \param[in] x, y location of display frame on the screen
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) This is debugging code that displays an image on the screen.
|
||
|
* It uses a static internal variable to number the output files
|
||
|
* written by a single process. Behavior with a shared library
|
||
|
* may be unpredictable.
|
||
|
* (2) It does nothing unless LeptDebugOK == TRUE.
|
||
|
* (3) It uses these programs to display the image:
|
||
|
* On Unix: xzgv, xli or xv
|
||
|
* On Windows: i_view
|
||
|
* The display program must be on your $PATH variable. It is
|
||
|
* chosen by setting the global var_DISPLAY_PROG, using
|
||
|
* l_chooseDisplayProg(). Default on Unix is xzgv.
|
||
|
* (4) Images with dimensions larger than MaxDisplayWidth or
|
||
|
* MaxDisplayHeight are downscaled to fit those constraints.
|
||
|
* This is particularly important for displaying 1 bpp images
|
||
|
* with xv, because xv automatically downscales large images
|
||
|
* by subsampling, which looks poor. For 1 bpp, we use
|
||
|
* scale-to-gray to get decent-looking anti-aliased images.
|
||
|
* In all cases, we write a temporary file to /tmp/lept/disp,
|
||
|
* that is read by the display program.
|
||
|
* (5) The temporary file is written as png if, after initial
|
||
|
* processing for special cases, any of these obtain:
|
||
|
* * pix dimensions are smaller than some thresholds
|
||
|
* * pix depth is less than 8 bpp
|
||
|
* * pix is colormapped
|
||
|
* (6) For spp == 4, we call pixDisplayLayersRGBA() to show 3
|
||
|
* versions of the image: the image with a fully opaque
|
||
|
* alpha, the alpha, and the image as it would appear with
|
||
|
* a white background.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixDisplay(PIX *pixs,
|
||
|
l_int32 x,
|
||
|
l_int32 y)
|
||
|
{
|
||
|
return pixDisplayWithTitle(pixs, x, y, NULL, 1);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixDisplayWithTitle()
|
||
|
*
|
||
|
* \param[in] pix 1, 2, 4, 8, 16, 32 bpp
|
||
|
* \param[in] x, y location of display frame
|
||
|
* \param[in] title [optional] on frame; can be NULL;
|
||
|
* \param[in] dispflag 1 to write, else disabled
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) See notes for pixDisplay().
|
||
|
* (2) This displays the image if dispflag == 1; otherwise it punts.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixDisplayWithTitle(PIX *pixs,
|
||
|
l_int32 x,
|
||
|
l_int32 y,
|
||
|
const char *title,
|
||
|
l_int32 dispflag)
|
||
|
{
|
||
|
char *tempname;
|
||
|
char buffer[Bufsize];
|
||
|
static l_int32 index = 0; /* caution: not .so or thread safe */
|
||
|
l_int32 w, h, d, spp, maxheight, opaque, threeviews;
|
||
|
l_float32 ratw, rath, ratmin;
|
||
|
PIX *pix0, *pix1, *pix2;
|
||
|
PIXCMAP *cmap;
|
||
|
#ifndef _WIN32
|
||
|
l_int32 wt, ht;
|
||
|
#else
|
||
|
char *pathname;
|
||
|
char fullpath[_MAX_PATH];
|
||
|
#endif /* _WIN32 */
|
||
|
|
||
|
PROCNAME("pixDisplayWithTitle");
|
||
|
|
||
|
if (!LeptDebugOK) {
|
||
|
L_INFO("displaying images is disabled;\n "
|
||
|
"use setLeptDebugOK(1) to enable\n", procName);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef OS_IOS /* iOS 11 does not support system() */
|
||
|
return ERROR_INT("iOS 11 does not support system()", procName, 1);
|
||
|
#endif /* OS_IOS */
|
||
|
|
||
|
if (dispflag != 1) return 0;
|
||
|
if (!pixs)
|
||
|
return ERROR_INT("pixs not defined", procName, 1);
|
||
|
if (var_DISPLAY_PROG != L_DISPLAY_WITH_XZGV &&
|
||
|
var_DISPLAY_PROG != L_DISPLAY_WITH_XLI &&
|
||
|
var_DISPLAY_PROG != L_DISPLAY_WITH_XV &&
|
||
|
var_DISPLAY_PROG != L_DISPLAY_WITH_IV &&
|
||
|
var_DISPLAY_PROG != L_DISPLAY_WITH_OPEN) {
|
||
|
return ERROR_INT("no program chosen for display", procName, 1);
|
||
|
}
|
||
|
|
||
|
/* Display with three views if either spp = 4 or if colormapped
|
||
|
* and the alpha component is not fully opaque */
|
||
|
opaque = TRUE;
|
||
|
if ((cmap = pixGetColormap(pixs)) != NULL)
|
||
|
pixcmapIsOpaque(cmap, &opaque);
|
||
|
spp = pixGetSpp(pixs);
|
||
|
threeviews = (spp == 4 || !opaque) ? TRUE : FALSE;
|
||
|
|
||
|
/* If colormapped and not opaque, remove the colormap to RGBA */
|
||
|
if (!opaque)
|
||
|
pix0 = pixRemoveColormap(pixs, REMOVE_CMAP_WITH_ALPHA);
|
||
|
else
|
||
|
pix0 = pixClone(pixs);
|
||
|
|
||
|
/* Scale if necessary; this will also remove a colormap */
|
||
|
pixGetDimensions(pix0, &w, &h, &d);
|
||
|
maxheight = (threeviews) ? MaxDisplayHeight / 3 : MaxDisplayHeight;
|
||
|
if (w <= MaxDisplayWidth && h <= maxheight) {
|
||
|
if (d == 16) /* take MSB */
|
||
|
pix1 = pixConvert16To8(pix0, L_MS_BYTE);
|
||
|
else
|
||
|
pix1 = pixClone(pix0);
|
||
|
} else {
|
||
|
ratw = (l_float32)MaxDisplayWidth / (l_float32)w;
|
||
|
rath = (l_float32)maxheight / (l_float32)h;
|
||
|
ratmin = L_MIN(ratw, rath);
|
||
|
if (ratmin < 0.125 && d == 1)
|
||
|
pix1 = pixScaleToGray8(pix0);
|
||
|
else if (ratmin < 0.25 && d == 1)
|
||
|
pix1 = pixScaleToGray4(pix0);
|
||
|
else if (ratmin < 0.33 && d == 1)
|
||
|
pix1 = pixScaleToGray3(pix0);
|
||
|
else if (ratmin < 0.5 && d == 1)
|
||
|
pix1 = pixScaleToGray2(pix0);
|
||
|
else
|
||
|
pix1 = pixScale(pix0, ratmin, ratmin);
|
||
|
}
|
||
|
pixDestroy(&pix0);
|
||
|
if (!pix1)
|
||
|
return ERROR_INT("pix1 not made", procName, 1);
|
||
|
|
||
|
/* Generate the three views if required */
|
||
|
if (threeviews)
|
||
|
pix2 = pixDisplayLayersRGBA(pix1, 0xffffff00, 0);
|
||
|
else
|
||
|
pix2 = pixClone(pix1);
|
||
|
|
||
|
if (index == 0) { /* erase any existing images */
|
||
|
lept_rmdir("lept/disp");
|
||
|
lept_mkdir("lept/disp");
|
||
|
}
|
||
|
|
||
|
index++;
|
||
|
if (pixGetDepth(pix2) < 8 || pixGetColormap(pix2) ||
|
||
|
(w < MaxSizeForPng && h < MaxSizeForPng)) {
|
||
|
snprintf(buffer, Bufsize, "/tmp/lept/disp/write.%03d.png", index);
|
||
|
pixWrite(buffer, pix2, IFF_PNG);
|
||
|
} else {
|
||
|
snprintf(buffer, Bufsize, "/tmp/lept/disp/write.%03d.jpg", index);
|
||
|
pixWrite(buffer, pix2, IFF_JFIF_JPEG);
|
||
|
}
|
||
|
tempname = genPathname(buffer, NULL);
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
|
||
|
/* Unix */
|
||
|
if (var_DISPLAY_PROG == L_DISPLAY_WITH_XZGV) {
|
||
|
/* no way to display title */
|
||
|
pixGetDimensions(pix2, &wt, &ht, NULL);
|
||
|
snprintf(buffer, Bufsize,
|
||
|
"xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10,
|
||
|
x, y, tempname);
|
||
|
} else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XLI) {
|
||
|
if (title) {
|
||
|
snprintf(buffer, Bufsize,
|
||
|
"xli -dispgamma 1.0 -quiet -geometry +%d+%d -title \"%s\" %s &",
|
||
|
x, y, title, tempname);
|
||
|
} else {
|
||
|
snprintf(buffer, Bufsize,
|
||
|
"xli -dispgamma 1.0 -quiet -geometry +%d+%d %s &",
|
||
|
x, y, tempname);
|
||
|
}
|
||
|
} else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XV) {
|
||
|
if (title) {
|
||
|
snprintf(buffer, Bufsize,
|
||
|
"xv -quit -geometry +%d+%d -name \"%s\" %s &",
|
||
|
x, y, title, tempname);
|
||
|
} else {
|
||
|
snprintf(buffer, Bufsize,
|
||
|
"xv -quit -geometry +%d+%d %s &", x, y, tempname);
|
||
|
}
|
||
|
} else if (var_DISPLAY_PROG == L_DISPLAY_WITH_OPEN) {
|
||
|
snprintf(buffer, Bufsize, "open %s &", tempname);
|
||
|
}
|
||
|
callSystemDebug(buffer);
|
||
|
|
||
|
#else /* _WIN32 */
|
||
|
|
||
|
/* Windows: L_DISPLAY_WITH_IV */
|
||
|
pathname = genPathname(tempname, NULL);
|
||
|
_fullpath(fullpath, pathname, sizeof(fullpath));
|
||
|
if (title) {
|
||
|
snprintf(buffer, Bufsize,
|
||
|
"i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"",
|
||
|
fullpath, x, y, title);
|
||
|
} else {
|
||
|
snprintf(buffer, Bufsize, "i_view32.exe \"%s\" /pos=(%d,%d)",
|
||
|
fullpath, x, y);
|
||
|
}
|
||
|
callSystemDebug(buffer);
|
||
|
LEPT_FREE(pathname);
|
||
|
|
||
|
#endif /* _WIN32 */
|
||
|
|
||
|
pixDestroy(&pix1);
|
||
|
pixDestroy(&pix2);
|
||
|
LEPT_FREE(tempname);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixSaveTiled()
|
||
|
*
|
||
|
* \param[in] pixs 1, 2, 4, 8, 32 bpp
|
||
|
* \param[in] pixa the pix are accumulated here
|
||
|
* \param[in] scalefactor 0.0 to disable; otherwise this is a scale factor
|
||
|
* \param[in] newrow 0 if placed on the same row as previous; 1 otherwise
|
||
|
* \param[in] space horizontal and vertical spacing, in pixels
|
||
|
* \param[in] dp depth of pixa; 8 or 32 bpp; only used on first call
|
||
|
* \return 0 if OK, 1 on error.
|
||
|
*/
|
||
|
l_ok
|
||
|
pixSaveTiled(PIX *pixs,
|
||
|
PIXA *pixa,
|
||
|
l_float32 scalefactor,
|
||
|
l_int32 newrow,
|
||
|
l_int32 space,
|
||
|
l_int32 dp)
|
||
|
{
|
||
|
/* Save without an outline */
|
||
|
return pixSaveTiledOutline(pixs, pixa, scalefactor, newrow, space, 0, dp);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixSaveTiledOutline()
|
||
|
*
|
||
|
* \param[in] pixs 1, 2, 4, 8, 32 bpp
|
||
|
* \param[in] pixa the pix are accumulated here
|
||
|
* \param[in] scalefactor 0.0 to disable; otherwise this is a scale factor
|
||
|
* \param[in] newrow 0 if placed on the same row as previous; 1 otherwise
|
||
|
* \param[in] space horizontal and vertical spacing, in pixels
|
||
|
* \param[in] linewidth width of added outline for image; 0 for no outline
|
||
|
* \param[in] dp depth of pixa; 8 or 32 bpp; only used on first call
|
||
|
* \return 0 if OK, 1 on error.
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Before calling this function for the first time, use
|
||
|
* pixaCreate() to make the %pixa that will accumulate the pix.
|
||
|
* This is passed in each time pixSaveTiled() is called.
|
||
|
* (2) %scalefactor scales the input image. After scaling and
|
||
|
* possible depth conversion, the image is saved in the input
|
||
|
* pixa, along with a box that specifies the location to
|
||
|
* place it when tiled later. Disable saving the pix by
|
||
|
* setting %scalefactor == 0.0.
|
||
|
* (3) %newrow and %space specify the location of the new pix
|
||
|
* with respect to the last one(s) that were entered.
|
||
|
* (4) %dp specifies the depth at which all pix are saved. It can
|
||
|
* be only 8 or 32 bpp. Any colormap is removed. This is only
|
||
|
* used at the first invocation.
|
||
|
* (5) This function uses two variables from call to call.
|
||
|
* If they were static, the function would not be .so or thread
|
||
|
* safe, and furthermore, there would be interference with two or
|
||
|
* more pixa accumulating images at a time. Consequently,
|
||
|
* we use the first pix in the pixa to store and obtain both
|
||
|
* the depth and the current position of the bottom (one pixel
|
||
|
* below the lowest image raster line when laid out using
|
||
|
* the boxa). The bottom variable is stored in the input format
|
||
|
* field, which is the only field available for storing an int.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixSaveTiledOutline(PIX *pixs,
|
||
|
PIXA *pixa,
|
||
|
l_float32 scalefactor,
|
||
|
l_int32 newrow,
|
||
|
l_int32 space,
|
||
|
l_int32 linewidth,
|
||
|
l_int32 dp)
|
||
|
{
|
||
|
l_int32 n, top, left, bx, by, bw, w, h, depth, bottom;
|
||
|
BOX *box;
|
||
|
PIX *pix1, *pix2, *pix3, *pix4;
|
||
|
|
||
|
PROCNAME("pixSaveTiledOutline");
|
||
|
|
||
|
if (scalefactor == 0.0) return 0;
|
||
|
|
||
|
if (!pixs)
|
||
|
return ERROR_INT("pixs not defined", procName, 1);
|
||
|
if (!pixa)
|
||
|
return ERROR_INT("pixa not defined", procName, 1);
|
||
|
|
||
|
n = pixaGetCount(pixa);
|
||
|
if (n == 0) {
|
||
|
bottom = 0;
|
||
|
if (dp != 8 && dp != 32) {
|
||
|
L_WARNING("dp not 8 or 32 bpp; using 32\n", procName);
|
||
|
depth = 32;
|
||
|
} else {
|
||
|
depth = dp;
|
||
|
}
|
||
|
} else { /* extract the depth and bottom params from the first pix */
|
||
|
pix1 = pixaGetPix(pixa, 0, L_CLONE);
|
||
|
depth = pixGetDepth(pix1);
|
||
|
bottom = pixGetInputFormat(pix1); /* not typical usage! */
|
||
|
pixDestroy(&pix1);
|
||
|
}
|
||
|
|
||
|
/* Remove colormap if it exists; otherwise a copy. This
|
||
|
* guarantees that pix4 is not a clone of pixs. */
|
||
|
pix1 = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY);
|
||
|
|
||
|
/* Scale and convert to output depth */
|
||
|
if (scalefactor == 1.0) {
|
||
|
pix2 = pixClone(pix1);
|
||
|
} else if (scalefactor > 1.0) {
|
||
|
pix2 = pixScale(pix1, scalefactor, scalefactor);
|
||
|
} else { /* scalefactor < 1.0) */
|
||
|
if (pixGetDepth(pix1) == 1)
|
||
|
pix2 = pixScaleToGray(pix1, scalefactor);
|
||
|
else
|
||
|
pix2 = pixScale(pix1, scalefactor, scalefactor);
|
||
|
}
|
||
|
pixDestroy(&pix1);
|
||
|
if (depth == 8)
|
||
|
pix3 = pixConvertTo8(pix2, 0);
|
||
|
else
|
||
|
pix3 = pixConvertTo32(pix2);
|
||
|
pixDestroy(&pix2);
|
||
|
|
||
|
/* Add black outline */
|
||
|
if (linewidth > 0)
|
||
|
pix4 = pixAddBorder(pix3, linewidth, 0);
|
||
|
else
|
||
|
pix4 = pixClone(pix3);
|
||
|
pixDestroy(&pix3);
|
||
|
|
||
|
/* Find position of current pix (UL corner plus size) */
|
||
|
if (n == 0) {
|
||
|
top = 0;
|
||
|
left = 0;
|
||
|
} else if (newrow == 1) {
|
||
|
top = bottom + space;
|
||
|
left = 0;
|
||
|
} else { /* n > 0 */
|
||
|
pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL);
|
||
|
top = by;
|
||
|
left = bx + bw + space;
|
||
|
}
|
||
|
|
||
|
pixGetDimensions(pix4, &w, &h, NULL);
|
||
|
bottom = L_MAX(bottom, top + h);
|
||
|
box = boxCreate(left, top, w, h);
|
||
|
pixaAddPix(pixa, pix4, L_INSERT);
|
||
|
pixaAddBox(pixa, box, L_INSERT);
|
||
|
|
||
|
/* Save the new bottom value */
|
||
|
pix1 = pixaGetPix(pixa, 0, L_CLONE);
|
||
|
pixSetInputFormat(pix1, bottom); /* not typical usage! */
|
||
|
pixDestroy(&pix1);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*!
|
||
|
* \brief pixSaveTiledWithText()
|
||
|
*
|
||
|
* \param[in] pixs 1, 2, 4, 8, 32 bpp
|
||
|
* \param[in] pixa the pix are accumulated here; as 32 bpp
|
||
|
* \param[in] outwidth in pixels; use 0 to disable entirely
|
||
|
* \param[in] newrow 1 to start a new row; 0 to go on same row as previous
|
||
|
* \param[in] space horizontal and vertical spacing, in pixels
|
||
|
* \param[in] linewidth width of added outline for image; 0 for no outline
|
||
|
* \param[in] bmf [optional] font struct
|
||
|
* \param[in] textstr [optional] text string to be added
|
||
|
* \param[in] val color to set the text
|
||
|
* \param[in] location L_ADD_ABOVE, L_ADD_AT_TOP, L_ADD_AT_BOT, L_ADD_BELOW
|
||
|
* \return 0 if OK, 1 on error.
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (1) Before calling this function for the first time, use
|
||
|
* pixaCreate() to make the %pixa that will accumulate the pix.
|
||
|
* This is passed in each time pixSaveTiled() is called.
|
||
|
* (2) %outwidth is the scaled width. After scaling, the image is
|
||
|
* saved in the input pixa, along with a box that specifies
|
||
|
* the location to place it when tiled later. Disable saving
|
||
|
* the pix by setting %outwidth == 0.
|
||
|
* (3) %newrow and %space specify the location of the new pix
|
||
|
* with respect to the last one(s) that were entered.
|
||
|
* (4) All pix are saved as 32 bpp RGB.
|
||
|
* (5) If both %bmf and %textstr are defined, this generates a pix
|
||
|
* with the additional text; otherwise, no text is written.
|
||
|
* (6) The text is written before scaling, so it is properly
|
||
|
* antialiased in the scaled pix. However, if the pix on
|
||
|
* different calls have different widths, the size of the
|
||
|
* text will vary.
|
||
|
* (7) See pixSaveTiledOutline() for other implementation details.
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixSaveTiledWithText(PIX *pixs,
|
||
|
PIXA *pixa,
|
||
|
l_int32 outwidth,
|
||
|
l_int32 newrow,
|
||
|
l_int32 space,
|
||
|
l_int32 linewidth,
|
||
|
L_BMF *bmf,
|
||
|
const char *textstr,
|
||
|
l_uint32 val,
|
||
|
l_int32 location)
|
||
|
{
|
||
|
PIX *pix1, *pix2, *pix3, *pix4;
|
||
|
|
||
|
PROCNAME("pixSaveTiledWithText");
|
||
|
|
||
|
if (outwidth == 0) return 0;
|
||
|
|
||
|
if (!pixs)
|
||
|
return ERROR_INT("pixs not defined", procName, 1);
|
||
|
if (!pixa)
|
||
|
return ERROR_INT("pixa not defined", procName, 1);
|
||
|
|
||
|
pix1 = pixConvertTo32(pixs);
|
||
|
if (linewidth > 0)
|
||
|
pix2 = pixAddBorder(pix1, linewidth, 0);
|
||
|
else
|
||
|
pix2 = pixClone(pix1);
|
||
|
if (bmf && textstr)
|
||
|
pix3 = pixAddSingleTextblock(pix2, bmf, textstr, val, location, NULL);
|
||
|
else
|
||
|
pix3 = pixClone(pix2);
|
||
|
pix4 = pixScaleToSize(pix3, outwidth, 0);
|
||
|
pixSaveTiled(pix4, pixa, 1.0, newrow, space, 32);
|
||
|
pixDestroy(&pix1);
|
||
|
pixDestroy(&pix2);
|
||
|
pixDestroy(&pix3);
|
||
|
pixDestroy(&pix4);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
l_chooseDisplayProg(l_int32 selection)
|
||
|
{
|
||
|
if (selection == L_DISPLAY_WITH_XLI ||
|
||
|
selection == L_DISPLAY_WITH_XZGV ||
|
||
|
selection == L_DISPLAY_WITH_XV ||
|
||
|
selection == L_DISPLAY_WITH_IV ||
|
||
|
selection == L_DISPLAY_WITH_OPEN) {
|
||
|
var_DISPLAY_PROG = selection;
|
||
|
} else {
|
||
|
L_ERROR("invalid display program\n", "l_chooseDisplayProg");
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*---------------------------------------------------------------------*
|
||
|
* Deprecated pix output for debugging *
|
||
|
*---------------------------------------------------------------------*/
|
||
|
/*!
|
||
|
* \brief pixDisplayWrite()
|
||
|
*
|
||
|
* \param[in] pix 1, 2, 4, 8, 16, 32 bpp
|
||
|
* \param[in] reduction -1 to reset/erase; 0 to disable;
|
||
|
* otherwise this is a reduction factor
|
||
|
* \return 0 if OK; 1 on error
|
||
|
*
|
||
|
* <pre>
|
||
|
* Notes:
|
||
|
* (0) Deprecated.
|
||
|
* (1) This is a simple interface for writing a set of files.
|
||
|
* (2) This uses jpeg output for pix that are 32 bpp or 8 bpp
|
||
|
* without a colormap; otherwise, it uses png.
|
||
|
* (3) To erase any previously written files in the output directory:
|
||
|
* pixDisplayWrite(NULL, -1);
|
||
|
* (4) If reduction > 1 and depth == 1, this does a scale-to-gray
|
||
|
* reduction.
|
||
|
* (5) This function uses a static internal variable to number
|
||
|
* output files written by a single process. Behavior
|
||
|
* with a shared library may be unpredictable.
|
||
|
* (6) For 16 bpp, this displays the full dynamic range with log scale.
|
||
|
* Alternative image transforms to generate 8 bpp pix are:
|
||
|
* pix8 = pixMaxDynamicRange(pixt, L_LINEAR_SCALE);
|
||
|
* pix8 = pixConvert16To8(pixt, L_LS_BYTE); // low order byte
|
||
|
* pix8 = pixConvert16To8(pixt, L_MS_BYTE); // high order byte
|
||
|
* </pre>
|
||
|
*/
|
||
|
l_ok
|
||
|
pixDisplayWrite(PIX *pixs,
|
||
|
l_int32 reduction)
|
||
|
{
|
||
|
char buf[Bufsize];
|
||
|
char *fname;
|
||
|
l_float32 scale;
|
||
|
PIX *pix1, *pix2;
|
||
|
static l_int32 index = 0; /* caution: not .so or thread safe */
|
||
|
|
||
|
PROCNAME("pixDisplayWrite");
|
||
|
|
||
|
fprintf(stderr, "\n######################################################"
|
||
|
"\n Notice:\n"
|
||
|
" pixDisplayWrite() has been deprecated in leptonica \n"
|
||
|
" since version 1.74. It will become a non-functioning\n"
|
||
|
" stub in 1.80.\n"
|
||
|
"######################################################"
|
||
|
"\n\n\n");
|
||
|
|
||
|
if (reduction == 0) return 0;
|
||
|
if (reduction < 0) { /* initialize */
|
||
|
lept_rmdir("lept/display");
|
||
|
index = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
if (!pixs)
|
||
|
return ERROR_INT("pixs not defined", procName, 1);
|
||
|
if (index == 0)
|
||
|
lept_mkdir("lept/display");
|
||
|
index++;
|
||
|
|
||
|
if (reduction == 1) {
|
||
|
pix1 = pixClone(pixs);
|
||
|
} else {
|
||
|
scale = 1. / (l_float32)reduction;
|
||
|
if (pixGetDepth(pixs) == 1)
|
||
|
pix1 = pixScaleToGray(pixs, scale);
|
||
|
else
|
||
|
pix1 = pixScale(pixs, scale, scale);
|
||
|
}
|
||
|
|
||
|
if (pixGetDepth(pix1) == 16) {
|
||
|
pix2 = pixMaxDynamicRange(pix1, L_LOG_SCALE);
|
||
|
snprintf(buf, Bufsize, "file.%03d.png", index);
|
||
|
fname = pathJoin("/tmp/lept/display", buf);
|
||
|
pixWrite(fname, pix2, IFF_PNG);
|
||
|
pixDestroy(&pix2);
|
||
|
} else if (pixGetDepth(pix1) < 8 || pixGetColormap(pix1)) {
|
||
|
snprintf(buf, Bufsize, "file.%03d.png", index);
|
||
|
fname = pathJoin("/tmp/lept/display", buf);
|
||
|
pixWrite(fname, pix1, IFF_PNG);
|
||
|
} else {
|
||
|
snprintf(buf, Bufsize, "file.%03d.jpg", index);
|
||
|
fname = pathJoin("/tmp/lept/display", buf);
|
||
|
pixWrite(fname, pix1, IFF_JFIF_JPEG);
|
||
|
}
|
||
|
LEPT_FREE(fname);
|
||
|
pixDestroy(&pix1);
|
||
|
return 0;
|
||
|
}
|