mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 14:16:10 -06:00
825 lines
25 KiB
C
825 lines
25 KiB
C
/*====================================================================*
|
|
- Copyright (C) 2001 Leptonica. All rights reserved.
|
|
- This software is distributed in the hope that it will be
|
|
- useful, but with NO WARRANTY OF ANY KIND.
|
|
- No author or distributor accepts responsibility to anyone for the
|
|
- consequences of using this software, or for whether it serves any
|
|
- particular purpose or works at all, unless he or she says so in
|
|
- writing. Everyone is granted permission to copy, modify and
|
|
- redistribute this source code, for commercial or non-commercial
|
|
- purposes, with the following restrictions: (1) the origin of this
|
|
- source code must not be misrepresented; (2) modified versions must
|
|
- be plainly marked as such; and (3) this notice may not be removed
|
|
- or altered from any source or modified source distribution.
|
|
*====================================================================*/
|
|
|
|
|
|
/*
|
|
* writefile.c
|
|
*
|
|
* High-level procedures for writing images to file:
|
|
* l_int32 pixaWriteFiles()
|
|
* l_int32 pixWrite()
|
|
* l_int32 pixWriteStream()
|
|
* l_int32 pixWriteImpliedFormat()
|
|
*
|
|
* Selection of output format if default is requested
|
|
* l_int32 pixChooseOutputFormat()
|
|
* l_int32 getImpliedFileFormat()
|
|
* const char *getFormatExtension()
|
|
*
|
|
* Write to memory
|
|
* l_int32 pixWriteMem()
|
|
*
|
|
* Image display for debugging
|
|
* l_int32 pixDisplay()
|
|
* l_int32 pixDisplayWithTitle()
|
|
* l_int32 pixDisplayWrite()
|
|
* l_int32 pixDisplayWriteFormat()
|
|
* l_int32 pixSaveTiled()
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "allheaders.h"
|
|
|
|
/* MS VC++ can't handle array initialization with static consts ! */
|
|
#define L_BUF_SIZE 512
|
|
|
|
/* For display using xv */
|
|
static const l_int32 MAX_DISPLAY_WIDTH = 1000;
|
|
static const l_int32 MAX_DISPLAY_HEIGHT = 800;
|
|
static const l_int32 MAX_SIZE_FOR_PNG = 200;
|
|
|
|
/* PostScript output for printing */
|
|
static const l_float32 DEFAULT_SCALING = 1.0;
|
|
|
|
/* Global array of image file format extension names.
|
|
* This is in 1-1 corrspondence with format enum in imageio.h. */
|
|
static const l_int32 NUM_EXTENSIONS = 14;
|
|
const char *ImageFileFormatExtensions[] = {"unknown",
|
|
"bmp",
|
|
"jpg",
|
|
"png",
|
|
"tif",
|
|
"tif",
|
|
"tif",
|
|
"tif",
|
|
"tif",
|
|
"tif",
|
|
"tif",
|
|
"pnm",
|
|
"ps",
|
|
"gif"};
|
|
|
|
/* 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 },
|
|
{ ".ps", IFF_PS } };
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
* Top-level procedures for writing images to file *
|
|
*---------------------------------------------------------------------*/
|
|
/*!
|
|
* pixaWriteFiles()
|
|
*
|
|
* Input: rootname
|
|
* pixa
|
|
* format (defined in imageio.h)
|
|
* Return: 0 if OK; 1 on error
|
|
*/
|
|
l_int32
|
|
pixaWriteFiles(const char *rootname,
|
|
PIXA *pixa,
|
|
l_int32 format)
|
|
{
|
|
char bigbuf[L_BUF_SIZE];
|
|
l_int32 i, n;
|
|
PIX *pix;
|
|
|
|
PROCNAME("pixaWriteFiles");
|
|
|
|
if (!rootname)
|
|
return ERROR_INT("rootname not defined", procName, 1);
|
|
if (!pixa)
|
|
return ERROR_INT("pixa not defined", procName, 1);
|
|
|
|
n = pixaGetCount(pixa);
|
|
for (i = 0; i < n; i++) {
|
|
snprintf(bigbuf, L_BUF_SIZE, "%s%03d.%s", rootname, i,
|
|
ImageFileFormatExtensions[format]);
|
|
pix = pixaGetPix(pixa, i, L_CLONE);
|
|
pixWrite(bigbuf, pix, format);
|
|
pixDestroy(&pix);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixWrite()
|
|
*
|
|
* Input: filename
|
|
* pix
|
|
* format (defined in imageio.h)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* 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.
|
|
*/
|
|
l_int32
|
|
pixWrite(const char *filename,
|
|
PIX *pix,
|
|
l_int32 format)
|
|
{
|
|
FILE *fp;
|
|
|
|
PROCNAME("pixWrite");
|
|
|
|
if (!pix)
|
|
return ERROR_INT("pix not defined", procName, 1);
|
|
if (!filename)
|
|
return ERROR_INT("filename not defined", procName, 1);
|
|
|
|
if ((fp = fopen(filename, "wb+")) == NULL)
|
|
return ERROR_INT("stream not opened", procName, 1);
|
|
|
|
if (pixWriteStream(fp, pix, format)) {
|
|
fclose(fp);
|
|
return ERROR_INT("pix not written to stream", procName, 1);
|
|
}
|
|
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixWriteStream()
|
|
*
|
|
* Input: stream
|
|
* pix
|
|
* format
|
|
* Return: 0 if OK; 1 on error.
|
|
*/
|
|
l_int32
|
|
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, 75, 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 */
|
|
return pixWriteStreamTiff(fp, pix, format);
|
|
break;
|
|
|
|
case IFF_PNM:
|
|
return pixWriteStreamPnm(fp, pix);
|
|
break;
|
|
|
|
case IFF_GIF:
|
|
return pixWriteStreamGif(fp, pix);
|
|
break;
|
|
|
|
case IFF_PS:
|
|
return pixWriteStreamPS(fp, pix, NULL, 0, DEFAULT_SCALING);
|
|
break;
|
|
|
|
default:
|
|
return ERROR_INT("unknown format", procName, 1);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixWriteImpliedFormat()
|
|
*
|
|
* Input: filename
|
|
* pix
|
|
* quality (iff JPEG; 1 - 100, 0 for default)
|
|
* progressive (iff JPEG; 0 for baseline seq., 1 for progressive)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* 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.
|
|
*/
|
|
l_int32
|
|
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
|
|
format = IFF_TIFF_LZW;
|
|
}
|
|
|
|
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", 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 *
|
|
*---------------------------------------------------------------------*/
|
|
/*!
|
|
* pixChooseOutputFormat()
|
|
*
|
|
* Input: pix
|
|
* Return: output format, or 0 on error
|
|
*
|
|
* 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.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
|
|
/*!
|
|
* getImpliedFileFormat()
|
|
*
|
|
* Input: filename
|
|
* Return: output format, or IFF_UNKNOWN on error or invalid extension.
|
|
*
|
|
* Notes:
|
|
* (1) This determines the output file format from the extension
|
|
* of the input filename.
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
|
|
FREE(extension);
|
|
return format;
|
|
}
|
|
|
|
|
|
/*!
|
|
* getFormatExtension()
|
|
*
|
|
* Input: format (integer)
|
|
* Return: extension (string), or null if format is out of range
|
|
*
|
|
* Notes:
|
|
* (1) This string is NOT owned by the caller; it is just a pointer
|
|
* to a global string. Do not free it.
|
|
*/
|
|
const char *
|
|
getFormatExtension(l_int32 format)
|
|
{
|
|
PROCNAME("getFormatExtension");
|
|
|
|
if (format < 0 || format >= NUM_EXTENSIONS)
|
|
return (const char *)ERROR_PTR("format out of bounds", procName, NULL);
|
|
|
|
return ImageFileFormatExtensions[format];
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
* Write to memory *
|
|
*---------------------------------------------------------------------*/
|
|
/*!
|
|
* pixWriteMem()
|
|
*
|
|
* Input: &data (<return> data of tiff compressed image)
|
|
* &size (<return> size of returned data)
|
|
* pix
|
|
* format (defined in imageio.h)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* 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).
|
|
*/
|
|
l_int32
|
|
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, 75, 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, DEFAULT_SCALING);
|
|
break;
|
|
|
|
default:
|
|
return ERROR_INT("unknown format", procName, 1);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
* Image display for debugging *
|
|
*---------------------------------------------------------------------*/
|
|
/*!
|
|
* pixDisplay()
|
|
*
|
|
* Input: pix (1, 2, 4, 8, 16, 32 bpp)
|
|
* x, y (location of xv frame)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This uses xv to display. It must be on your $PATH variable.
|
|
* (2) Because xv reduces images to fit the screen, we do this
|
|
* reduction in advance, and write it out to a temporary file
|
|
* in the current directory with the name "junk_xv_display.*"
|
|
* (3) This function uses a static internal variable to number
|
|
* output files written by a single process. Behavior
|
|
* with a shared library may be unpredictable.
|
|
*/
|
|
l_int32
|
|
pixDisplay(PIX *pixs,
|
|
l_int32 x,
|
|
l_int32 y)
|
|
{
|
|
return pixDisplayWithTitle(pixs, x, y, NULL, 1);
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixDisplayWithTitle()
|
|
*
|
|
* Input: pix (1, 2, 4, 8, 16, 32 bpp)
|
|
* x, y (location of xv frame)
|
|
* title (<optional> on xv window; can be NULL);
|
|
* dispflag (0 to disable; 1 to write)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) See notes for pixDisplay().
|
|
* (2) This displays the image if dispflag == 1.
|
|
*/
|
|
l_int32
|
|
pixDisplayWithTitle(PIX *pixs,
|
|
l_int32 x,
|
|
l_int32 y,
|
|
const char *title,
|
|
l_int32 dispflag)
|
|
{
|
|
char *tempname;
|
|
char buffer[L_BUF_SIZE];
|
|
static l_int32 index = 0; /* caution: not .so or thread safe */
|
|
l_int32 w, h, d;
|
|
l_float32 ratw, rath, ratmin;
|
|
PIX *pixt;
|
|
|
|
PROCNAME("pixDisplayWithTitle");
|
|
|
|
if (dispflag == 0) return 0;
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
|
|
pixGetDimensions(pixs, &w, &h, &d);
|
|
if (w <= MAX_DISPLAY_WIDTH && h <= MAX_DISPLAY_HEIGHT) {
|
|
if (d == 16) /* take MSB */
|
|
pixt = pixConvert16To8(pixs, 1);
|
|
else
|
|
pixt = pixClone(pixs);
|
|
}
|
|
else {
|
|
ratw = (l_float32)MAX_DISPLAY_WIDTH / (l_float32)w;
|
|
rath = (l_float32)MAX_DISPLAY_HEIGHT / (l_float32)h;
|
|
ratmin = L_MIN(ratw, rath);
|
|
if (ratmin < 0.125 && d == 1)
|
|
pixt = pixScaleToGray8(pixs);
|
|
else if (ratmin < 0.25 && d == 1)
|
|
pixt = pixScaleToGray4(pixs);
|
|
else if (ratmin < 0.33 && d == 1)
|
|
pixt = pixScaleToGray3(pixs);
|
|
else if (ratmin < 0.5 && d == 1)
|
|
pixt = pixScaleToGray2(pixs);
|
|
else
|
|
pixt = pixScale(pixs, ratmin, ratmin);
|
|
if (!pixt)
|
|
return ERROR_INT("pixt not made", procName, 1);
|
|
}
|
|
|
|
if (index == 0) {
|
|
snprintf(buffer, L_BUF_SIZE, "rm -f junk_xv_display.*");
|
|
system(buffer);
|
|
}
|
|
|
|
index++;
|
|
if (pixGetDepth(pixt) < 8 ||
|
|
(w < MAX_SIZE_FOR_PNG && h < MAX_SIZE_FOR_PNG)) {
|
|
snprintf(buffer, L_BUF_SIZE, "junk_xv_display.%03d.png", index);
|
|
pixWrite(buffer, pixt, IFF_PNG);
|
|
}
|
|
else {
|
|
snprintf(buffer, L_BUF_SIZE, "junk_xv_display.%03d.jpg", index);
|
|
pixWrite(buffer, pixt, IFF_JFIF_JPEG);
|
|
}
|
|
tempname = stringNew(buffer);
|
|
|
|
if (title)
|
|
snprintf(buffer, L_BUF_SIZE,
|
|
"xv -quit -geometry +%d+%d -name \"%s\" %s &",
|
|
x, y, title, tempname);
|
|
else
|
|
snprintf(buffer, L_BUF_SIZE,
|
|
"xv -quit -geometry +%d+%d %s &", x, y, tempname);
|
|
system(buffer);
|
|
|
|
pixDestroy(&pixt);
|
|
FREE(tempname);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixDisplayWrite()
|
|
*
|
|
* Input: pix (1, 2, 4, 8, 16, 32 bpp)
|
|
* reduction (-1 to reset/erase; 0 to disable;
|
|
* otherwise this is a reduction factor)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This defaults to jpeg output for pix that are 32 bpp or
|
|
* 8 bpp without a colormap. If you want to write all images
|
|
* losslessly, use format == IFF_PNG in pixDisplayWriteFormat().
|
|
* (2) See pixDisplayWriteFormat() for usage details.
|
|
*/
|
|
l_int32
|
|
pixDisplayWrite(PIX *pixs,
|
|
l_int32 reduction)
|
|
{
|
|
return pixDisplayWriteFormat(pixs, reduction, IFF_JFIF_JPEG);
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixDisplayWriteFormat()
|
|
*
|
|
* Input: pix (1, 2, 4, 8, 16, 32 bpp)
|
|
* reduction (-1 to reset/erase; 0 to disable;
|
|
* otherwise this is a reduction factor)
|
|
* format (IFF_PNG or IFF_JFIF_JPEG)
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) This writes files if reduction > 0. These can be
|
|
* displayed, ordered in a tiled representation, with,
|
|
* for example, gthumb.
|
|
* (2) All previously written files can be erased by calling with
|
|
* reduction < 0; the value of pixs is ignored.
|
|
* (3) If reduction > 1 and depth == 1, this does a scale-to-gray
|
|
* reduction.
|
|
* (4) This function uses a static internal variable to number
|
|
* output files written by a single process. Behavior
|
|
* with a shared library may be unpredictable.
|
|
* (5) Output file format is as follows:
|
|
* format == IFF_JFIF_JPEG:
|
|
* png if d < 8 or d == 16 or if the output pix
|
|
* has a colormap. Otherwise, output is jpg.
|
|
* format == IFF_PNG:
|
|
* png (lossless) on all images.
|
|
* (6) For 16 bpp, the choice of full dynamic range with log scale
|
|
* is the best for displaying these images. Alternative outputs are
|
|
* pix8 = pixMaxDynamicRange(pixt, L_LINEAR_SCALE);
|
|
* pix8 = pixConvert16To8(pixt, 0); // low order byte
|
|
* pix8 = pixConvert16To8(pixt, 1); // high order byte
|
|
*/
|
|
l_int32
|
|
pixDisplayWriteFormat(PIX *pixs,
|
|
l_int32 reduction,
|
|
l_int32 format)
|
|
{
|
|
char buffer[L_BUF_SIZE];
|
|
l_float32 scale;
|
|
PIX *pixt, *pix8;
|
|
static l_int32 index = 0; /* caution: not .so or thread safe */
|
|
|
|
PROCNAME("pixDisplayWriteFormat");
|
|
|
|
if (reduction == 0) return 0;
|
|
|
|
if (reduction < 0) {
|
|
index = 0; /* reset; this will cause erasure at next call to write */
|
|
return 0;
|
|
}
|
|
|
|
if (format != IFF_JFIF_JPEG && format != IFF_PNG)
|
|
return ERROR_INT("invalid format", procName, 1);
|
|
if (!pixs)
|
|
return ERROR_INT("pixs not defined", procName, 1);
|
|
|
|
if (index == 0) {
|
|
snprintf(buffer, L_BUF_SIZE,
|
|
"rm -f junk_write_display.*.png junk_write_display.*.jpg");
|
|
system(buffer);
|
|
}
|
|
index++;
|
|
|
|
if (reduction == 1)
|
|
pixt = pixClone(pixs);
|
|
else {
|
|
scale = 1. / (l_float32)reduction;
|
|
if (pixGetDepth(pixs) == 1)
|
|
pixt = pixScaleToGray(pixs, scale);
|
|
else
|
|
pixt = pixScale(pixs, scale, scale);
|
|
}
|
|
|
|
if (pixGetDepth(pixt) == 16) {
|
|
pix8 = pixMaxDynamicRange(pixt, L_LOG_SCALE);
|
|
snprintf(buffer, L_BUF_SIZE, "junk_write_display.%03d.png", index);
|
|
pixWrite(buffer, pix8, IFF_PNG);
|
|
pixDestroy(&pix8);
|
|
}
|
|
else if (pixGetDepth(pixt) < 8 || pixGetColormap(pixt)) {
|
|
snprintf(buffer, L_BUF_SIZE, "junk_write_display.%03d.png", index);
|
|
pixWrite(buffer, pixt, IFF_PNG);
|
|
}
|
|
else {
|
|
snprintf(buffer, L_BUF_SIZE, "junk_write_display.%03d.jpg", index);
|
|
pixWrite(buffer, pixt, format);
|
|
}
|
|
pixDestroy(&pixt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* pixSaveTiled()
|
|
*
|
|
* Input: pixs (1, 2, 4, 8, 32 bpp)
|
|
* pixa (the pix are accumulated here)
|
|
* reduction (0 to disable; otherwise this is a reduction factor)
|
|
* newrow (0 if placed on the same row as previous; 1 otherwise)
|
|
* space (horizontal and vertical spacing, in pixels)
|
|
* dp (depth of pixa; 8 or 32 bpp; only used on first call)
|
|
* Return: 0 if OK, 1 on error.
|
|
*
|
|
* 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) @reduction is the integer reduction factor for the input
|
|
* image. After reduction 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 reduction == 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.
|
|
*/
|
|
l_int32
|
|
pixSaveTiled(PIX *pixs,
|
|
PIXA *pixa,
|
|
l_int32 reduction,
|
|
l_int32 newrow,
|
|
l_int32 space,
|
|
l_int32 dp)
|
|
{
|
|
l_int32 n, top, left, bx, by, bw, w, h, depth, bottom;
|
|
l_float32 scale;
|
|
BOX *box;
|
|
PIX *pix, *pixt1, *pixt2;
|
|
|
|
PROCNAME("pixSaveTiled");
|
|
|
|
if (reduction == 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", procName);
|
|
depth = 32;
|
|
} else
|
|
depth = dp;
|
|
}
|
|
else { /* extract the depth and bottom params from the first pix */
|
|
pix = pixaGetPix(pixa, 0, L_CLONE);
|
|
depth = pixGetDepth(pix);
|
|
bottom = pixGetInputFormat(pix); /* not typical usage! */
|
|
pixDestroy(&pix);
|
|
}
|
|
|
|
if (reduction == 1)
|
|
pixt1 = pixClone(pixs);
|
|
else {
|
|
scale = 1. / (l_float32)reduction;
|
|
if (pixGetDepth(pixs) == 1)
|
|
pixt1 = pixScaleToGray(pixs, scale);
|
|
else
|
|
pixt1 = pixScale(pixs, scale, scale);
|
|
}
|
|
if (depth == 8)
|
|
pixt2 = pixConvertTo8(pixt1, 0);
|
|
else
|
|
pixt2 = pixConvertTo32(pixt1);
|
|
pixDestroy(&pixt1);
|
|
|
|
/* 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 if (n > 0) {
|
|
pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL);
|
|
top = by;
|
|
left = bx + bw + space;
|
|
}
|
|
|
|
pixGetDimensions(pixt2, &w, &h, NULL);
|
|
bottom = L_MAX(bottom, top + h);
|
|
box = boxCreate(left, top, w, h);
|
|
pixaAddPix(pixa, pixt2, L_INSERT);
|
|
pixaAddBox(pixa, box, L_INSERT);
|
|
|
|
/* Save the new bottom value */
|
|
pix = pixaGetPix(pixa, 0, L_CLONE);
|
|
pixSetInputFormat(pix, bottom); /* not typical usage! */
|
|
pixDestroy(&pix);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|