Mercurial > hgrepos > Python2 > PyMuPDF
view mupdf-source/thirdparty/leptonica/src/writefile.c @ 38:8934ac156ef5
Allow to build with the PyPI package "clang" instead of "libclang".
1. It seems to be maintained.
2. In the FreeBSD base system there is no pre-built libclang.so. If you
need this library you have to install llvm from ports additionally.
2. On FreeBSD there is no pre-built wheel "libclang" with a packaged
libclang.so.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Tue, 23 Sep 2025 10:27:15 +0200 |
| parents | b50eed0cc0ef |
| children |
line wrap: on
line source
/*====================================================================* - 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 getFormatFromExtension() * 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() * PIX *pixMakeColorSquare() * void l_chooseDisplayProg() * * Change format for missing library * void changeFormatForMissingLib() * * Nonfunctional stub of pix output for debugging * 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) */ #ifdef HAVE_CONFIG_H #include <config_auto.h> #endif /* HAVE_CONFIG_H */ #include <string.h> #include "allheaders.h" /* Set defaults for the display program (xv, xli, xzgv, open, irfanview) * that is 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 */ #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 correspondence 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. * Note that the extension string always includes a '.' */ struct ExtensionMap { char extension[16]; l_int32 format; }; static const struct ExtensionMap extension_map[] = { { ".bmp", IFF_BMP }, { ".jpg", IFF_JFIF_JPEG }, { ".jpeg", IFF_JFIF_JPEG }, { ".JPG", IFF_JFIF_JPEG }, { ".png", IFF_PNG }, { ".tif", IFF_TIFF }, { ".tiff", IFF_TIFF }, { ".tiffg4", IFF_TIFF_G4 }, { ".pbm", IFF_PNM }, { ".pgm", IFF_PNM }, { ".pnm", IFF_PNM }, { ".gif", IFF_GIF }, { ".jp2", IFF_JP2 }, { ".j2k", 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; prevq = var_JPEG_QUALITY; newq = (new_quality == 0) ? 75 : new_quality; if (newq < 1 || newq > 100) L_ERROR("invalid jpeg quality; unchanged\n", __func__); else var_JPEG_QUALITY = newq; return prevq; } /*----------------------------------------------------------------------* * Set global variable LeptDebugOK for writing to named temp files * *----------------------------------------------------------------------*/ LEPT_DLL 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; if (!rootname) return ERROR_INT("rootname not defined", __func__, 1); if (!pixa) return ERROR_INT("pixa not defined", __func__, 1); if (format < 0 || format == IFF_UNKNOWN || format >= NumImageFileFormatExtensions) return ERROR_INT("invalid format", __func__, 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) { if (LeptDebugOK) { return pixWrite(fname, pix, format); } else { L_INFO("write to named temp file %s is disabled\n", __func__, 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; if (!pix) return ERROR_INT("pix not defined", __func__, 1); if (!fname) return ERROR_INT("fname not defined", __func__, 1); if ((fp = fopenWriteStream(fname, "wb+")) == NULL) return ERROR_INT_1("stream not opened", fname, __func__, 1); ret = pixWriteStream(fp, pix, format); fclose(fp); if (ret) return ERROR_INT_1("pix not written to stream", fname, __func__, 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; if (!pix) return ERROR_INT("pix not defined", __func__, 1); if (!filename) return ERROR_INT("filename not defined", __func__, 1); if (pixGetAutoFormat(pix, &format)) return ERROR_INT("auto format not returned", __func__, 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) { if (!fp) return ERROR_INT("stream not defined", __func__, 1); if (!pix) return ERROR_INT("pix not defined", __func__, 1); if (format == IFF_DEFAULT) format = pixChooseOutputFormat(pix); /* Use bmp format for testing if library for requested * format for jpeg, png or tiff is not available */ changeFormatForMissingLib(&format); 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); case IFF_PNG: /* no gamma value stored */ return pixWriteStreamPng(fp, pix, 0.0); 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); case IFF_PNM: return pixWriteStreamPnm(fp, pix); case IFF_PS: return pixWriteStreamPS(fp, pix, NULL, 0, DefaultScaling); case IFF_GIF: return pixWriteStreamGif(fp, pix); case IFF_JP2: return pixWriteStreamJp2k(fp, pix, 34, 0, L_JP2_CODEC, 0, 0); case IFF_WEBP: return pixWriteStreamWebP(fp, pix, 80, 0); case IFF_LPDF: return pixWriteStreamPdf(fp, pix, 0, NULL); case IFF_SPIX: return pixWriteStreamSpix(fp, pix); default: return ERROR_INT("unknown format", __func__, 1); } 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; if (!filename) return ERROR_INT("filename not defined", __func__, 1); if (!pix) return ERROR_INT("pix not defined", __func__, 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", __func__); } 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; if (!pix) return ERROR_INT("pix not defined", __func__, 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; l_int32 format = IFF_UNKNOWN; if (!filename) return ERROR_INT("extension not defined", __func__, IFF_UNKNOWN); if (splitPathAtExtension (filename, NULL, &extension)) return IFF_UNKNOWN; format = getFormatFromExtension(extension); LEPT_FREE(extension); return format; } /*! * \brief getFormatFromExtension() * * \param[in] extension * \return output format, or IFF_UNKNOWN on error or invalid extension. * * <pre> * Notes: * (1) This determines the integer for writing in a format that * corresponds to the image file type extension. For example, * the integer code corresponding to the extension "jpg" is 2; * it is used to write with jpeg encoding. * </pre> */ l_int32 getFormatFromExtension(const char *extension) { int i, numext; l_int32 format = IFF_UNKNOWN; if (!extension) return ERROR_INT("extension not defined", __func__, 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; } } 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; if (!pformat) return ERROR_INT("&format not defined", __func__, 0); *pformat = IFF_UNKNOWN; if (!pix) return ERROR_INT("pix not defined", __func__, 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) { if (format < 0 || format >= NumImageFileFormatExtensions) return (const char *)ERROR_PTR("invalid format", __func__, 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; if (!pdata) return ERROR_INT("&data not defined", __func__, 1 ); if (!psize) return ERROR_INT("&size not defined", __func__, 1 ); if (!pix) return ERROR_INT("&pix not defined", __func__, 1 ); if (format == IFF_DEFAULT) format = pixChooseOutputFormat(pix); /* Use bmp format for testing if library for requested * format for jpeg, png or tiff is not available */ changeFormatForMissingLib(&format); 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 */ case IFF_TIFF_JPEG: /* compressed, 8 bpp gray or 32 bpp rgb */ 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", __func__, 1); } 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; if (!LeptDebugOK) { L_INFO("displaying files is disabled; " "use setLeptDebugOK(1) to enable\n", __func__); return 0; } if (scale == 0.0) return 0; if (scale < 0.0) return ERROR_INT("invalid scale factor", __func__, 1); if ((pixs = pixRead(fname)) == NULL) return ERROR_INT("pixs not read", __func__, 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, xv or (for apple) open * On Windows: i_view or the application currently registered * as the default viewer (the browser 'open' action * based on the extension of the image file). * 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. * (7) pixDisplay() can be inactivated at runtime by calling: * l_chooseDisplayProg(L_DISPLAY_WITH_NONE); * </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 and the global * var_DISPLAY_PROG != L_DISPLAY_WITH_NONE; 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_atomic index = 0; /* caution: not .so 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 */ if (!LeptDebugOK) { L_INFO("displaying images is disabled;\n " "use setLeptDebugOK(1) to enable\n", __func__); return 0; } #ifdef OS_IOS /* iOS 11 does not support system() */ return ERROR_INT("iOS 11 does not support system()", __func__, 1); #endif /* OS_IOS */ if (dispflag != 1 || var_DISPLAY_PROG == L_DISPLAY_WITH_NONE) return 0; if (!pixs) return ERROR_INT("pixs not defined", __func__, 1); #ifndef _WIN32 /* unix */ 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_OPEN) return ERROR_INT("invalid unix program chosen for display", __func__, 1); #else /* _WIN32 */ if (var_DISPLAY_PROG != L_DISPLAY_WITH_IV && var_DISPLAY_PROG != L_DISPLAY_WITH_OPEN) return ERROR_INT("invalid windows program chosen for display", __func__, 1); #endif /* _WIN32 */ /* 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", __func__, 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 { /* L_DISPLAY_WITH_OPEN */ snprintf(buffer, Bufsize, "open %s &", tempname); } callSystemDebug(buffer); #else /* _WIN32 */ /* Windows: L_DISPLAY_WITH_IV || L_DISPLAY_WITH_OPEN */ pathname = genPathname(tempname, NULL); _fullpath(fullpath, pathname, sizeof(fullpath)); if (var_DISPLAY_PROG == L_DISPLAY_WITH_IV) { 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); } } else { /* L_DISPLAY_WITH_OPEN */ snprintf(buffer, Bufsize, "explorer.exe /open,\"%s\"", fullpath); } callSystemDebug(buffer); LEPT_FREE(pathname); #endif /* _WIN32 */ pixDestroy(&pix1); pixDestroy(&pix2); LEPT_FREE(tempname); return 0; } /*! * \brief pixMakeColorSquare() * * \param[in] color in 0xrrggbb00 format * \param[in] size in pixels; >= 100; use 0 for default (min size) * \param[in] addlabel use 1 to display the color component values * \param[in] location of text: L_ADD_ABOVE, etc; ignored if %addlabel == 0 * \param[in] textcolor of text label; in 0xrrggbb00 format * \return 32 bpp rgb pixd if OK; NULL on error * * <pre> * Notes: * (1) If %addlabel == 0, %location and %textcolor are ignored. * (2) To make an array of color squares, use pixDisplayColorArray(). * </pre> */ PIX * pixMakeColorSquare(l_uint32 color, l_int32 size, l_int32 addlabel, l_int32 location, l_uint32 textcolor) { char buf[32]; l_int32 w, rval, gval, bval; L_BMF *bmf; PIX *pix1, *pix2; w = (size <= 0) ? 100 : size; if (addlabel && w < 100) { L_WARNING("size too small for label; omitting label\n", __func__); addlabel = 0; } if ((pix1 = pixCreate(w, w, 32)) == NULL) return (PIX *)ERROR_PTR("pix1 not madel", __func__, NULL); pixSetAllArbitrary(pix1, color); if (!addlabel) return pix1; /* Adding text of color component values */ if (location != L_ADD_ABOVE && location != L_ADD_AT_TOP && location != L_ADD_AT_BOT && location != L_ADD_BELOW) { L_ERROR("invalid location: adding below\n", __func__); location = L_ADD_BELOW; } bmf = bmfCreate(NULL, 4); extractRGBValues(color, &rval, &gval, &bval); snprintf(buf, sizeof(buf), "%d,%d,%d", rval, gval, bval); pix2 = pixAddSingleTextblock(pix1, bmf, buf, textcolor, location, NULL); pixDestroy(&pix1); bmfDestroy(&bmf); return pix2; } 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"); } } /*---------------------------------------------------------------------* * Change format for missing lib * *---------------------------------------------------------------------*/ /*! * \brief changeFormatForMissingLib() * * \param[in,out] pformat addr of requested output image format * \return void * * <pre> * Notes: * (1) This is useful for testing functionality when the library for * the requested output format (jpeg, png or tiff) is not linked. * In that case, the output format is changed to bmp. * </pre> */ void changeFormatForMissingLib(l_int32 *pformat) { #if !defined(HAVE_LIBJPEG) if (*pformat == IFF_JFIF_JPEG) { L_WARNING("jpeg library missing; output bmp format\n", __func__); *pformat = IFF_BMP; } #endif /* !defined(HAVE_LIBJPEG) */ #if !defined(HAVE_LIBPNG) if (*pformat == IFF_PNG) { L_WARNING("png library missing; output bmp format\n", __func__); *pformat = IFF_BMP; } #endif /* !defined(HAVE_LIBPNG) */ #if !defined(HAVE_LIBTIFF) if (L_FORMAT_IS_TIFF(*pformat)) { L_WARNING("tiff library missing; output bmp format\n", __func__); *pformat = IFF_BMP; } #endif /* !defined(HAVE_LIBTIFF) */ } /*---------------------------------------------------------------------* * Deprecated pix output for debugging * *---------------------------------------------------------------------*/ /*! * \brief pixDisplayWrite() * * \param[in] pix * \param[in] reduction * \return 1 (error) * * <pre> * Notes: * As of 1.80, this is a non-functional stub. * </pre> */ l_ok pixDisplayWrite(PIX *pixs, l_int32 reduction) { lept_stderr("\n########################################################\n" " pixDisplayWrite() was last used in tesseract 3.04," " in Feb 2016. As of 1.80, it is a non-functional stub\n" "########################################################" "\n\n\n"); return 1; }
