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

1128 lines
36 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() [behavior depends on WRITE_AS_NAMED]
* 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 pixDisplayMultiple()
* l_int32 pixDisplayWrite()
* l_int32 pixDisplayWriteFormat()
* l_int32 pixSaveTiled()
* l_int32 pixSaveTiledOutline()
* l_int32 pixSaveTiledWithText()
* void chooseDisplayProg()
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "allheaders.h"
/* ----------------------------------------------------------- */
/* Special flag for pixWrite(). The default value is 1. */
/* For the effect of this parameter, see comments there. */
#define WRITE_AS_NAMED 1
/* ----------------------------------------------------------- */
#ifdef COMPILER_MSVC
#define MAX_PATH 260 /* actually in <WinDef.h>; brings in other files */
#endif /* COMPILER_MSVC */
/* MS VC++ can't handle array initialization with static consts ! */
#define L_BUF_SIZE 512
/* Display program (xv, xli or xzgv) to be invoked by pixDisplay() */
#if COMPILER_MSVC
static l_int32 ChosenDisplayProg = L_DISPLAY_WITH_IV; /* default */
#else
static l_int32 ChosenDisplayProg = L_DISPLAY_WITH_XV; /* default */
#endif /* COMPILER_MSVC */
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.
* (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 = 16; /* array size */
LEPT_DLL const char *ImageFileFormatExtensions[] =
{"unknown",
"bmp",
"jpg",
"png",
"tif",
"tif",
"tif",
"tif",
"tif",
"tif",
"tif",
"pnm",
"ps",
"gif",
"jp2"
"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 } };
/*---------------------------------------------------------------------*
* 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);
if (format < 0 || format >= NumImageFileFormatExtensions)
return ERROR_INT("invalid format", 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.
* (2) There are two modes with respect to file naming.
* (a) The default code writes to @filename.
* (b) If WRITE_AS_NAMED is defined to 0, it's a bit fancier.
* Then, if @filename does not have a file extension, one is
* automatically appended, depending on the requested format.
* We provide option (b) because Windows programs need the
* file type as an extension to the file name.
* (3) If the default format is requested, we use the input format;
* if the input format is unknown, a lossless format is assigned.
*/
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 (format == IFF_JP2)
return ERROR_INT("jp2 not supported", procName, 1);
#if WRITE_AS_NAMED /* Default */
if ((fp = fopen(filename, "wb+")) == NULL)
return ERROR_INT("stream not opened", procName, 1);
#else /* Add an extension to the output name if none exists */
{l_int32 extlen;
char *extension, *filebuf;
splitPathAtExtension(filename, NULL, &extension);
extlen = strlen(extension);
FREE(extension);
if (extlen == 0) {
if (format == IFF_DEFAULT || format == IFF_UNKNOWN)
format = pixChooseOutputFormat(pix);
filebuf = (char *)CALLOC(strlen(filename) + 10, sizeof(char));
if (!filebuf)
return ERROR_INT("filebuf not made", procName, 1);
strncpy(filebuf, filename, strlen(filename));
strcat(filebuf, ".");
strcat(filebuf, ImageFileFormatExtensions[format]);
}
else
filebuf = (char *)filename;
fp = fopen(filebuf, "wb+");
if (filebuf != filename)
FREE(filebuf);
if (fp == NULL)
return ERROR_INT("stream not opened", procName, 1);
}
#endif /* WRITE_AS_NAMED */
if (pixWriteStream(fp, pix, format)) {
fclose(fp);
return ERROR_INT("pix not written to stream", procName, 1);
}
if (format != IFF_GIF) /* EGifCloseFile() closes file stream! */
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;
case IFF_JP2:
return ERROR_INT("jp2 format not supported", procName, 1);
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_ZIP;
}
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 >= NumImageFileFormatExtensions)
return (const char *)ERROR_PTR("invalid format", 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;
case IFF_JP2:
return ERROR_INT("jp2 not supported", procName, 1);
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 display frame on the screen)
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This displays the image using xv, xli or xzgv on Unix,
* or i_view on Windows. The display program must be on
* your $PATH variable. It is chosen by setting the global
* ChosenDisplayProg, using chooseDisplayProg().
* Default on Unix is xv.
* (2) Images with dimensions larger than MAX_DISPLAY_WIDTH or
* MAX_DISPLAY_HEIGHT are downscaled to fit those constraints.
* This is particulary important for displaying 1 bpp images
* with xv, because xv automatically downscales large images
* by subsampling, which looks lousy. 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, that is
* read by the display program.
* (3) Note: 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 display frame)
* title (<optional> on frame; can be NULL);
* dispflag (1 to write, else disabled)
* 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;
#ifndef COMPILER_MSVC
l_int32 wt, ht;
#else
char pathname[MAX_PATH];
#endif /* COMPILER_MSVC */
PROCNAME("pixDisplayWithTitle");
if (dispflag != 1) return 0;
if (!pixs)
return ERROR_INT("pixs not defined", procName, 1);
if (ChosenDisplayProg != L_DISPLAY_WITH_XV &&
ChosenDisplayProg != L_DISPLAY_WITH_XLI &&
ChosenDisplayProg != L_DISPLAY_WITH_XZGV &&
ChosenDisplayProg != L_DISPLAY_WITH_IV)
return ERROR_INT("no program chosen for display", 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 /tmp/junk_display.*");
system(buffer);
}
index++;
if (pixGetDepth(pixt) < 8 ||
(w < MAX_SIZE_FOR_PNG && h < MAX_SIZE_FOR_PNG)) {
snprintf(buffer, L_BUF_SIZE, "/tmp/junk_display.%03d.png", index);
pixWrite(buffer, pixt, IFF_PNG);
}
else {
snprintf(buffer, L_BUF_SIZE, "/tmp/junk_display.%03d.jpg", index);
pixWrite(buffer, pixt, IFF_JFIF_JPEG);
}
tempname = stringNew(buffer);
#ifndef COMPILER_MSVC
/* Unix */
if (ChosenDisplayProg == L_DISPLAY_WITH_XV) {
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);
}
else if (ChosenDisplayProg == L_DISPLAY_WITH_XLI) {
if (title)
snprintf(buffer, L_BUF_SIZE,
"xli -quiet -geometry +%d+%d -title \"%s\" %s &",
x, y, title, tempname);
else
snprintf(buffer, L_BUF_SIZE,
"xli -quiet -geometry +%d+%d %s &", x, y, tempname);
}
else if (ChosenDisplayProg == L_DISPLAY_WITH_XZGV) {
/* no way to display title */
pixGetDimensions(pixt, &wt, &ht, NULL);
snprintf(buffer, L_BUF_SIZE,
"xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10,
x, y, tempname);
}
system(buffer);
#else /* COMPILER_MSVC */
/* Windows: L_DISPLAY_WITH_IV */
_fullpath(pathname, tempname, sizeof(pathname));
if (title)
snprintf(buffer, L_BUF_SIZE,
"i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"",
pathname, x, y, title);
else
snprintf(buffer, L_BUF_SIZE, "i_view32.exe \"%s\" /pos=(%d,%d)",
pathname, x, y);
system(buffer);
#endif /* COMPILER_MSVC */
pixDestroy(&pixt);
FREE(tempname);
return 0;
}
/*!
* pixDisplayMultiple()
*
* Input: filepattern
* Return: 0 if OK; 1 on error
*
* Notes:
* (1) This allows display of multiple images using gthumb on unix
* and i_view32 on windows. The @filepattern is a regular
* expression that is expanded by the shell.
* (2) _fullpath automatically changes '/' to '\' if necessary.
*/
l_int32
pixDisplayMultiple(const char *filepattern)
{
char buffer[L_BUF_SIZE];
#ifdef COMPILER_MSVC
char pathname[MAX_PATH];
char *dir, *tail;
#endif /* COMPILER_MSVC */
PROCNAME("pixDisplayMultiple");
if (!filepattern || strlen(filepattern) == 0)
return ERROR_INT("filepattern not defined", procName, 1);
#ifndef COMPILER_MSVC
snprintf(buffer, L_BUF_SIZE, "gthumb %s &", filepattern);
#else
/* irFanView wants absolute path for directory */
_fullpath(pathname, filepattern, sizeof(pathname));
splitPathAtDirectory(pathname, &dir, &tail);
snprintf(buffer, L_BUF_SIZE,
"i_view32.exe \"%s\" /filepattern=\"%s\" /thumbs", dir, tail);
FREE(dir);
FREE(tail);
#endif
system(buffer);
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 using
* pixDisplayMultiple("/tmp/junk_write_display*");
* (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 /tmp/junk_write_display.*.png /tmp/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, "/tmp/junk_write_display.%03d.png", index);
pixWrite(buffer, pix8, IFF_PNG);
pixDestroy(&pix8);
}
else if (pixGetDepth(pixt) < 8 || pixGetColormap(pixt) ||
format == IFF_PNG) {
snprintf(buffer, L_BUF_SIZE, "/tmp/junk_write_display.%03d.png", index);
pixWrite(buffer, pixt, IFF_PNG);
}
else {
snprintf(buffer, L_BUF_SIZE, "/tmp/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.
*/
l_int32
pixSaveTiled(PIX *pixs,
PIXA *pixa,
l_int32 reduction,
l_int32 newrow,
l_int32 space,
l_int32 dp)
{
/* Save without an outline */
return pixSaveTiledOutline(pixs, pixa, reduction, newrow, space, 0, dp);
}
/*!
* pixSaveTiledOutline()
*
* 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)
* linewidth (width of added outline for image; 0 for no outline)
* 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
pixSaveTiledOutline(PIX *pixs,
PIXA *pixa,
l_int32 reduction,
l_int32 newrow,
l_int32 space,
l_int32 linewidth,
l_int32 dp)
{
l_int32 n, top, left, bx, by, bw, w, h, depth, bottom;
l_float32 scale;
BOX *box;
PIX *pix, *pixt1, *pixt2, *pixt3;
PROCNAME("pixSaveTiledOutline");
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);
}
/* Scale and convert to output depth */
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);
/* Add black outline */
if (linewidth > 0)
pixt3 = pixAddBorder(pixt2, linewidth, 0);
else
pixt3 = pixClone(pixt2);
pixDestroy(&pixt2);
/* 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(pixt3, &w, &h, NULL);
bottom = L_MAX(bottom, top + h);
box = boxCreate(left, top, w, h);
pixaAddPix(pixa, pixt3, 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;
}
/*!
* pixSaveTiledWithText()
*
* Input: pixs (1, 2, 4, 8, 32 bpp)
* pixa (the pix are accumulated here; as 32 bpp)
* outwidth (in pixels; use 0 to disable entirely)
* newrow (1 to start a new row; 0 to go on same row as previous)
* space (horizontal and vertical spacing, in pixels)
* linewidth (width of added outline for image; 0 for no outline)
* bmf (<optional> font struct)
* textstr (<optional> text string to be added)
* val (color to set the text)
* location (L_ADD_ABOVE, L_ADD_AT_TOP, L_ADD_AT_BOTTOM,
* L_ADD_BELOW)
* 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) @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.
*/
l_int32
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 *pixt1, *pixt2, *pixt3, *pixt4;
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);
pixt1 = pixConvertTo32(pixs);
if (linewidth > 0)
pixt2 = pixAddBorder(pixt1, linewidth, 0);
else
pixt2 = pixClone(pixt1);
if (bmf && textstr)
pixt3 = pixAddSingleTextblock(pixt2, bmf, textstr, val, location, NULL);
else
pixt3 = pixClone(pixt2);
pixt4 = pixScaleToSize(pixt3, outwidth, 0);
pixSaveTiled(pixt4, pixa, 1, newrow, space, 32);
pixDestroy(&pixt1);
pixDestroy(&pixt2);
pixDestroy(&pixt3);
pixDestroy(&pixt4);
return 0;
}
void
chooseDisplayProg(l_int32 selection)
{
if (selection == L_DISPLAY_WITH_XLI ||
selection == L_DISPLAY_WITH_XZGV ||
selection == L_DISPLAY_WITH_XV)
ChosenDisplayProg = selection;
else
L_ERROR("invalid unix display program", "chooseDisplayProg");
return;
}