Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/thirdparty/leptonica/src/readfile.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/thirdparty/leptonica/src/readfile.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,1610 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. All rights reserved. + - + - Redistribution and use in source and binary forms, with or without + - modification, are permitted provided that the following conditions + - are met: + - 1. Redistributions of source code must retain the above copyright + - notice, this list of conditions and the following disclaimer. + - 2. Redistributions in binary form must reproduce the above + - copyright notice, this list of conditions and the following + - disclaimer in the documentation and/or other materials + - provided with the distribution. + - + - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY + - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *====================================================================*/ + +/*! + * \file readfile.c: reads image on file into memory + * <pre> + * + * Top-level functions for reading images from file + * PIXA *pixaReadFiles() + * PIXA *pixaReadFilesSA() + * PIX *pixRead() + * PIX *pixReadWithHint() + * PIX *pixReadIndexed() + * PIX *pixReadStream() + * + * Read header information from file + * l_int32 pixReadHeader() + * + * Format finders + * l_int32 findFileFormat() + * l_int32 findFileFormatStream() + * l_int32 findFileFormatBuffer() + * l_int32 fileFormatIsTiff() + * + * Read from memory + * PIX *pixReadMem() + * l_int32 pixReadHeaderMem() + * + * Output image file information + * void writeImageFileInfo() + * + * Test function for I/O with different formats + * l_int32 ioFormatTest() + * + * Supported file formats: + * (1) Reading is supported without any external libraries: + * bmp + * pnm (including pbm, pgm, etc) + * spix (raw serialized) + * (2) Reading is supported with installation of external libraries: + * png + * jpg (standard jfif version) + * tiff (including most varieties of compression) + * gif + * webp + * jp2 (jpeg 2000) + * (3) Other file types will get an "unknown format" error. + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include "allheaders.h" + + /* Output files for ioFormatTest(). */ +static const char *FILE_BMP = "/tmp/lept/format/file.bmp"; +static const char *FILE_PNG = "/tmp/lept/format/file.png"; +static const char *FILE_PNM = "/tmp/lept/format/file.pnm"; +static const char *FILE_G3 = "/tmp/lept/format/file_g3.tif"; +static const char *FILE_G4 = "/tmp/lept/format/file_g4.tif"; +static const char *FILE_RLE = "/tmp/lept/format/file_rle.tif"; +static const char *FILE_PB = "/tmp/lept/format/file_packbits.tif"; +static const char *FILE_LZW = "/tmp/lept/format/file_lzw.tif"; +static const char *FILE_ZIP = "/tmp/lept/format/file_zip.tif"; +static const char *FILE_TIFF_JPEG = "/tmp/lept/format/file_jpeg.tif"; +static const char *FILE_TIFF = "/tmp/lept/format/file.tif"; +static const char *FILE_JPG = "/tmp/lept/format/file.jpg"; +static const char *FILE_GIF = "/tmp/lept/format/file.gif"; +static const char *FILE_WEBP = "/tmp/lept/format/file.webp"; +static const char *FILE_JP2K = "/tmp/lept/format/file.jp2"; + + /* There are two jp2 formats, and two codecs associated with them: + * OPJ_CODEC_J2K (L_J2K_CODEC) is associated with JP2K_CODESTREAM + * OPJ_CODEC_JP2 (L_JP2_CODEC) is associated with JP2K_IMAGE_DATA */ +static const unsigned char JP2K_CODESTREAM[4] = { 0xff, 0x4f, 0xff, 0x51 }; +static const unsigned char JP2K_IMAGE_DATA[12] = { 0x00, 0x00, 0x00, 0x0c, + 0x6a, 0x50, 0x20, 0x20, + 0x0d, 0x0a, 0x87, 0x0a }; + + +/*---------------------------------------------------------------------* + * Top-level functions for reading images from file * + *---------------------------------------------------------------------*/ +/*! + * \brief pixaReadFiles() + * + * \param[in] dirname + * \param[in] substr [optional] substring filter on filenames; can be null + * \return pixa, or NULL on error + * + * <pre> + * Notes: + * (1) %dirname is the full path for the directory. + * (2) %substr is the part of the file name (excluding + * the directory) that is to be matched. All matching + * filenames are read into the Pixa. If substr is NULL, + * all filenames are read into the Pixa. + * </pre> + */ +PIXA * +pixaReadFiles(const char *dirname, + const char *substr) +{ +PIXA *pixa; +SARRAY *sa; + + if (!dirname) + return (PIXA *)ERROR_PTR("dirname not defined", __func__, NULL); + + if ((sa = getSortedPathnamesInDirectory(dirname, substr, 0, 0)) == NULL) + return (PIXA *)ERROR_PTR("sa not made", __func__, NULL); + + pixa = pixaReadFilesSA(sa); + sarrayDestroy(&sa); + return pixa; +} + + +/*! + * \brief pixaReadFilesSA() + * + * \param[in] sa full pathnames for all files + * \return pixa, or NULL on error + */ +PIXA * +pixaReadFilesSA(SARRAY *sa) +{ +char *str; +l_int32 i, n; +PIX *pix; +PIXA *pixa; + + if (!sa) + return (PIXA *)ERROR_PTR("sa not defined", __func__, NULL); + + n = sarrayGetCount(sa); + pixa = pixaCreate(n); + for (i = 0; i < n; i++) { + str = sarrayGetString(sa, i, L_NOCOPY); + if ((pix = pixRead(str)) == NULL) { + L_WARNING("pix not read from file %s\n", __func__, str); + continue; + } + pixaAddPix(pixa, pix, L_INSERT); + } + + return pixa; +} + + +/*! + * \brief pixRead() + * + * \param[in] filename with full pathname or in local directory + * \return pix if OK; NULL on error + * + * <pre> + * Notes: + * (1) See at top of file for supported formats. + * </pre> + */ +PIX * +pixRead(const char *filename) +{ +FILE *fp; +PIX *pix; + + if (!filename) + return (PIX *)ERROR_PTR("filename not defined", __func__, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIX*)ERROR_PTR_1("image file not found", + filename, __func__, NULL); + pix = pixReadStream(fp, 0); + fclose(fp); + if (!pix) + return (PIX *)ERROR_PTR_1("pix not read", filename, __func__, NULL); + return pix; +} + + +/*! + * \brief pixReadWithHint() + * + * \param[in] filename with full pathname or in local directory + * \param[in] hint bitwise OR of L_HINT_* values for jpeg; + * use 0 for no hint + * \return pix if OK; NULL on error + * + * <pre> + * Notes: + * (1) The hint is not binding, but may be used to optimize jpeg decoding. + * Use 0 for no hinting. + * </pre> + */ +PIX * +pixReadWithHint(const char *filename, + l_int32 hint) +{ +FILE *fp; +PIX *pix; + + if (!filename) + return (PIX *)ERROR_PTR("filename not defined", __func__, NULL); + + if ((fp = fopenReadStream(filename)) == NULL) + return (PIX *)ERROR_PTR_1("image file not found", + filename, __func__, NULL); + pix = pixReadStream(fp, hint); + fclose(fp); + + if (!pix) + return (PIX *)ERROR_PTR_1("image not returned", + filename, __func__, NULL); + return pix; +} + + +/*! + * \brief pixReadIndexed() + * + * \param[in] sa string array of full pathnames + * \param[in] index into pathname array + * \return pix if OK; null if not found + * + * <pre> + * Notes: + * (1) This function is useful for selecting image files from a + * directory, where the integer %index is embedded into + * the file name. + * (2) This is typically done by generating the sarray using + * getNumberedPathnamesInDirectory(), so that the %index + * pathname would have the number %index in it. The size + * of the sarray should be the largest number (plus 1) appearing + * in the file names, respecting the constraints in the + * call to getNumberedPathnamesInDirectory(). + * (3) Consequently, for some indices into the sarray, there may + * be no pathnames in the directory containing that number. + * By convention, we place empty C strings ("") in those + * locations in the sarray, and it is not an error if such + * a string is encountered and no pix is returned. + * Therefore, the caller must verify that a pix is returned. + * (4) See convertSegmentedPagesToPS() in src/psio1.c for an + * example of usage. + * </pre> + */ +PIX * +pixReadIndexed(SARRAY *sa, + l_int32 index) +{ +char *fname; +l_int32 n; +PIX *pix; + + if (!sa) + return (PIX *)ERROR_PTR("sa not defined", __func__, NULL); + n = sarrayGetCount(sa); + if (index < 0 || index >= n) + return (PIX *)ERROR_PTR("index out of bounds", __func__, NULL); + + fname = sarrayGetString(sa, index, L_NOCOPY); + if (fname[0] == '\0') + return NULL; + + if ((pix = pixRead(fname)) == NULL) { + L_ERROR("pix not read from file %s\n", __func__, fname); + return NULL; + } + + return pix; +} + + +/*! + * \brief pixReadStream() + * + * \param[in] fp file stream + * \param[in] hint bitwise OR of L_HINT_* values for jpeg; 0 for no hint + * \return pix if OK; NULL on error + * + * <pre> + * Notes: + * (1) The hint only applies to jpeg. + * </pre> + */ +PIX * +pixReadStream(FILE *fp, + l_int32 hint) +{ +l_int32 format, ret, valid; +l_uint8 *comment; +PIX *pix; +PIXCMAP *cmap; + + if (!fp) + return (PIX *)ERROR_PTR("stream not defined", __func__, NULL); + pix = NULL; + + findFileFormatStream(fp, &format); + switch (format) + { + case IFF_BMP: + if ((pix = pixReadStreamBmp(fp)) == NULL ) + return (PIX *)ERROR_PTR( "bmp: no pix returned", __func__, NULL); + break; + + case IFF_JFIF_JPEG: + if ((pix = pixReadStreamJpeg(fp, 0, 1, NULL, hint)) == NULL) + return (PIX *)ERROR_PTR( "jpeg: no pix returned", __func__, NULL); + ret = fgetJpegComment(fp, &comment); + if (!ret && comment) + pixSetText(pix, (char *)comment); + LEPT_FREE(comment); + break; + + case IFF_PNG: + if ((pix = pixReadStreamPng(fp)) == NULL) + return (PIX *)ERROR_PTR("png: no pix returned", __func__, NULL); + break; + + case IFF_TIFF: + case IFF_TIFF_PACKBITS: + case IFF_TIFF_RLE: + case IFF_TIFF_G3: + case IFF_TIFF_G4: + case IFF_TIFF_LZW: + case IFF_TIFF_ZIP: + case IFF_TIFF_JPEG: + if ((pix = pixReadStreamTiff(fp, 0)) == NULL) /* page 0 by default */ + return (PIX *)ERROR_PTR("tiff: no pix returned", __func__, NULL); + break; + + case IFF_PNM: + if ((pix = pixReadStreamPnm(fp)) == NULL) + return (PIX *)ERROR_PTR("pnm: no pix returned", __func__, NULL); + break; + + case IFF_GIF: + if ((pix = pixReadStreamGif(fp)) == NULL) + return (PIX *)ERROR_PTR("gif: no pix returned", __func__, NULL); + break; + + case IFF_JP2: + if ((pix = pixReadStreamJp2k(fp, 1, NULL, 0, 0)) == NULL) + return (PIX *)ERROR_PTR("jp2: no pix returned", __func__, NULL); + break; + + case IFF_WEBP: + if ((pix = pixReadStreamWebP(fp)) == NULL) + return (PIX *)ERROR_PTR("webp: no pix returned", __func__, NULL); + break; + + case IFF_PS: + L_ERROR("PostScript reading is not supported\n", __func__); + return NULL; + + case IFF_LPDF: + L_ERROR("Pdf reading is not supported\n", __func__); + return NULL; + + case IFF_SPIX: + if ((pix = pixReadStreamSpix(fp)) == NULL) + return (PIX *)ERROR_PTR("spix: no pix returned", __func__, NULL); + break; + + case IFF_UNKNOWN: + return (PIX *)ERROR_PTR( "Unknown format: no pix returned", + __func__, NULL); + break; + } + + if (pix) { + pixSetInputFormat(pix, format); + if ((cmap = pixGetColormap(pix))) { + pixcmapIsValid(cmap, pix, &valid); + if (!valid) { + pixDestroy(&pix); + return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL); + } + } + } + return pix; +} + + + +/*---------------------------------------------------------------------* + * Read header information from file * + *---------------------------------------------------------------------*/ +/*! + * \brief pixReadHeader() + * + * \param[in] filename with full pathname or in local directory + * \param[out] pformat [optional] file format + * \param[out] pw, ph [optional] width and height + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel 1, 3 or 4 + * \param[out] piscmap [optional] 1 if cmap exists; 0 otherwise + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This reads the actual headers for jpeg, png, tiff and pnm. + * For bmp and gif, we cheat and read the entire file into a pix, + * from which we extract the "header" information. + * </pre> + */ +l_ok +pixReadHeader(const char *filename, + l_int32 *pformat, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *piscmap) +{ +l_int32 format, ret, w, h, d, bps, spp, iscmap; +l_int32 type; /* ignored */ +FILE *fp; +PIX *pix; + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (piscmap) *piscmap = 0; + if (pformat) *pformat = 0; + iscmap = 0; /* init to false */ + if (!filename) + return ERROR_INT("filename not defined", __func__, 1); + + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT_1("image file not found", filename, __func__, 1); + findFileFormatStream(fp, &format); + fclose(fp); + + switch (format) + { + case IFF_BMP: /* cheating: reading the entire file */ + if ((pix = pixRead(filename)) == NULL) + return ERROR_INT_1( "bmp: pix not read", filename, __func__, 1); + pixGetDimensions(pix, &w, &h, &d); + bps = (d == 32) ? 8 : d; + spp = pixGetSpp(pix); + iscmap = (pixGetColormap(pix)) ? 1 : 0; + pixDestroy(&pix); + break; + + case IFF_JFIF_JPEG: + ret = readHeaderJpeg(filename, &w, &h, &spp, NULL, NULL); + bps = 8; + if (ret) + return ERROR_INT_1("jpeg: no header info returned", + filename, __func__, 1); + break; + + case IFF_PNG: + ret = readHeaderPng(filename, &w, &h, &bps, &spp, &iscmap); + if (ret) + return ERROR_INT_1("png: no header info returned", + filename, __func__, 1); + break; + + case IFF_TIFF: + case IFF_TIFF_PACKBITS: + case IFF_TIFF_RLE: + case IFF_TIFF_G3: + case IFF_TIFF_G4: + case IFF_TIFF_LZW: + case IFF_TIFF_ZIP: + case IFF_TIFF_JPEG: + /* Reading page 0 by default; possibly redefine format */ + ret = readHeaderTiff(filename, 0, &w, &h, &bps, &spp, NULL, &iscmap, + &format); + if (ret) + return ERROR_INT_1("tiff: no header info returned", + filename, __func__, 1); + break; + + case IFF_PNM: + ret = readHeaderPnm(filename, &w, &h, &d, &type, &bps, &spp); + if (ret) + return ERROR_INT_1("pnm: no header info returned", + filename, __func__, 1); + break; + + case IFF_GIF: /* cheating: reading the entire file */ + if ((pix = pixRead(filename)) == NULL) + return ERROR_INT_1( "gif: pix not read", filename, __func__, 1); + pixGetDimensions(pix, &w, &h, &d); + pixDestroy(&pix); + iscmap = 1; /* always colormapped; max 256 colors */ + spp = 1; + bps = d; + break; + + case IFF_JP2: + ret = readHeaderJp2k(filename, &w, &h, &bps, &spp, NULL); + break; + + case IFF_WEBP: + if (readHeaderWebP(filename, &w, &h, &spp)) + return ERROR_INT_1("webp: no header info returned", + filename, __func__, 1); + bps = 8; + break; + + case IFF_PS: + if (pformat) *pformat = format; + return ERROR_INT("PostScript reading is not supported\n", __func__, 1); + + case IFF_LPDF: + if (pformat) *pformat = format; + return ERROR_INT("Pdf reading is not supported\n", __func__, 1); + + case IFF_SPIX: + ret = readHeaderSpix(filename, &w, &h, &bps, &spp, &iscmap); + if (ret) + return ERROR_INT_1("spix: no header info returned", + filename, __func__, 1); + break; + + case IFF_UNKNOWN: + return ERROR_INT_1("unknown format in file", filename, __func__, 1); + } + + if (pw) *pw = w; + if (ph) *ph = h; + if (pbps) *pbps = bps; + if (pspp) *pspp = spp; + if (piscmap) *piscmap = iscmap; + if (pformat) *pformat = format; + return 0; +} + + +/*---------------------------------------------------------------------* + * Format finders * + *---------------------------------------------------------------------*/ +/*! + * \brief findFileFormat() + * + * \param[in] filename + * \param[out] pformat found format + * \return 0 if OK, 1 on error or if format is not recognized + */ +l_ok +findFileFormat(const char *filename, + l_int32 *pformat) +{ +l_int32 ret; +FILE *fp; + + if (!pformat) + return ERROR_INT("&format not defined", __func__, 1); + *pformat = IFF_UNKNOWN; + if (!filename) + return ERROR_INT("filename not defined", __func__, 1); + + if ((fp = fopenReadStream(filename)) == NULL) + return ERROR_INT_1("image file not found", filename, __func__, 1); + ret = findFileFormatStream(fp, pformat); + fclose(fp); + return ret; +} + + +/*! + * \brief findFileFormatStream() + * + * \param[in] fp file stream + * \param[out] pformat found format + * \return 0 if OK, 1 on error or if format is not recognized + * + * <pre> + * Notes: + * (1) Important: Side effect -- this resets fp to BOF. + * </pre> + */ +l_ok +findFileFormatStream(FILE *fp, + l_int32 *pformat) +{ +l_uint8 firstbytes[13]; +l_int32 format; + + if (!pformat) + return ERROR_INT("&format not defined", __func__, 1); + *pformat = IFF_UNKNOWN; + if (!fp) + return ERROR_INT("stream not defined", __func__, 1); + + rewind(fp); + if (fnbytesInFile(fp) < 12) + return ERROR_INT("truncated file", __func__, 1); + + if (fread(&firstbytes, 1, 12, fp) != 12) + return ERROR_INT("failed to read first 12 bytes of file", __func__, 1); + firstbytes[12] = 0; + rewind(fp); + + findFileFormatBuffer(firstbytes, &format); + if (format == IFF_TIFF) { + findTiffCompression(fp, &format); + rewind(fp); + } + *pformat = format; + if (format == IFF_UNKNOWN) + return 1; + else + return 0; +} + + +/*! + * \brief findFileFormatBuffer() + * + * \param[in] buf byte buffer at least 12 bytes in size; we can't check + * \param[out] pformat found format + * \return 0 if OK, 1 on error or if format is not recognized + * + * <pre> + * Notes: + * (1) This determines the file format from the first 12 bytes in + * the compressed data stream, which are stored in memory. + * (2) For tiff files, this returns IFF_TIFF. The specific tiff + * compression is then determined using findTiffCompression(). + * </pre> + */ +l_ok +findFileFormatBuffer(const l_uint8 *buf, + l_int32 *pformat) +{ +l_uint16 twobytepw; + + if (!pformat) + return ERROR_INT("&format not defined", __func__, 1); + *pformat = IFF_UNKNOWN; + if (!buf) + return ERROR_INT("byte buffer not defined", __func__, 0); + + /* Check the bmp and tiff 2-byte header ids */ + ((char *)(&twobytepw))[0] = buf[0]; + ((char *)(&twobytepw))[1] = buf[1]; + + if (convertOnBigEnd16(twobytepw) == BMP_ID) { + *pformat = IFF_BMP; + return 0; + } + + if (twobytepw == TIFF_BIGEND_ID || twobytepw == TIFF_LITTLEEND_ID) { + *pformat = IFF_TIFF; + return 0; + } + + /* Check for the p*m 2-byte header ids */ + if ((buf[0] == 'P' && buf[1] == '4') || /* newer packed */ + (buf[0] == 'P' && buf[1] == '1')) { /* old ASCII format */ + *pformat = IFF_PNM; + return 0; + } + + if ((buf[0] == 'P' && buf[1] == '5') || /* newer */ + (buf[0] == 'P' && buf[1] == '2')) { /* old */ + *pformat = IFF_PNM; + return 0; + } + + if ((buf[0] == 'P' && buf[1] == '6') || /* newer */ + (buf[0] == 'P' && buf[1] == '3')) { /* old */ + *pformat = IFF_PNM; + return 0; + } + + if (buf[0] == 'P' && buf[1] == '7') { /* new arbitrary (PAM) */ + *pformat = IFF_PNM; + return 0; + } + + /* Consider the first 11 bytes of the standard JFIF JPEG header: + * - The first two bytes are the most important: 0xffd8. + * - The next two bytes are the jfif marker: 0xffe0. + * Not all jpeg files have this marker. + * - The next two bytes are the header length. + * - The next 5 bytes are a null-terminated string. + * For JFIF, the string is "JFIF", naturally. For others it + * can be "Exif" or just about anything else. + * - Because of all this variability, we only check the first + * two byte marker. All jpeg files are identified as + * IFF_JFIF_JPEG. */ + if (buf[0] == 0xff && buf[1] == 0xd8) { + *pformat = IFF_JFIF_JPEG; + return 0; + } + + /* Check for the 8 byte PNG signature (png_signature in png.c): + * {137, 80, 78, 71, 13, 10, 26, 10} */ + if (buf[0] == 137 && buf[1] == 80 && buf[2] == 78 && buf[3] == 71 && + buf[4] == 13 && buf[5] == 10 && buf[6] == 26 && buf[7] == 10) { + *pformat = IFF_PNG; + return 0; + } + + /* Look for "GIF87a" or "GIF89a" */ + if (buf[0] == 'G' && buf[1] == 'I' && buf[2] == 'F' && buf[3] == '8' && + (buf[4] == '7' || buf[4] == '9') && buf[5] == 'a') { + *pformat = IFF_GIF; + return 0; + } + + /* Check for both types of jp2k file */ + if (memcmp(buf, JP2K_CODESTREAM, 4) == 0 || + memcmp(buf, JP2K_IMAGE_DATA, 12) == 0) { + *pformat = IFF_JP2; + return 0; + } + + /* Check for webp */ + if (buf[0] == 'R' && buf[1] == 'I' && buf[2] == 'F' && buf[3] == 'F' && + buf[8] == 'W' && buf[9] == 'E' && buf[10] == 'B' && buf[11] == 'P') { + *pformat = IFF_WEBP; + return 0; + } + + /* Check for ps */ + if (buf[0] == '%' && buf[1] == '!' && buf[2] == 'P' && buf[3] == 'S' && + buf[4] == '-' && buf[5] == 'A' && buf[6] == 'd' && buf[7] == 'o' && + buf[8] == 'b' && buf[9] == 'e') { + *pformat = IFF_PS; + return 0; + } + + /* Check for pdf */ + if (buf[0] == '%' && buf[1] == 'P' && buf[2] == 'D' && buf[3] == 'F' && + buf[4] == '-' && buf[5] == '1') { + *pformat = IFF_LPDF; + return 0; + } + + /* Check for "spix" serialized pix */ + if (buf[0] == 's' && buf[1] == 'p' && buf[2] == 'i' && buf[3] == 'x') { + *pformat = IFF_SPIX; + return 0; + } + + /* File format identifier not found; unknown */ + return 1; +} + + +/*! + * \brief fileFormatIsTiff() + * + * \param[in] fp file stream + * \return 1 if file is tiff; 0 otherwise or on error + */ +l_int32 +fileFormatIsTiff(FILE *fp) +{ +l_int32 format; + + if (!fp) + return ERROR_INT("stream not defined", __func__, 0); + + findFileFormatStream(fp, &format); + if (format == IFF_TIFF || format == IFF_TIFF_PACKBITS || + format == IFF_TIFF_RLE || format == IFF_TIFF_G3 || + format == IFF_TIFF_G4 || format == IFF_TIFF_LZW || + format == IFF_TIFF_ZIP || format == IFF_TIFF_JPEG) + return 1; + else + return 0; +} + + +/*---------------------------------------------------------------------* + * Read from memory * + *---------------------------------------------------------------------*/ +/*! + * \brief pixReadMem() + * + * \param[in] data const; encoded + * \param[in] size size of data + * \return pix, or NULL on error + * + * <pre> + * Notes: + * (1) This is a variation of pixReadStream(), where the data is read + * from a memory buffer rather than a file. + * (2) On Windows, this only reads tiff formatted files directly from + * memory. For other formats, it writes to a temp file and + * decompresses from file. + * (3) findFileFormatBuffer() requires up to 12 bytes to decide on + * the format. That determines the constraint here. But in + * fact the data must contain the entire compressed string for + * the image. + * </pre> + */ +PIX * +pixReadMem(const l_uint8 *data, + size_t size) +{ +l_int32 format, valid; +PIX *pix; +PIXCMAP *cmap; + + if (!data) + return (PIX *)ERROR_PTR("data not defined", __func__, NULL); + if (size < 12) + return (PIX *)ERROR_PTR("size < 12", __func__, NULL); + pix = NULL; + + findFileFormatBuffer(data, &format); + switch (format) + { + case IFF_BMP: + if ((pix = pixReadMemBmp(data, size)) == NULL ) + return (PIX *)ERROR_PTR( "bmp: no pix returned", __func__, NULL); + break; + + case IFF_JFIF_JPEG: + if ((pix = pixReadMemJpeg(data, size, 0, 1, NULL, 0)) == NULL) + return (PIX *)ERROR_PTR( "jpeg: no pix returned", __func__, NULL); + break; + + case IFF_PNG: + if ((pix = pixReadMemPng(data, size)) == NULL) + return (PIX *)ERROR_PTR("png: no pix returned", __func__, NULL); + break; + + case IFF_TIFF: + case IFF_TIFF_PACKBITS: + case IFF_TIFF_RLE: + case IFF_TIFF_G3: + case IFF_TIFF_G4: + case IFF_TIFF_LZW: + case IFF_TIFF_ZIP: + /* Reading page 0 by default */ + if ((pix = pixReadMemTiff(data, size, 0)) == NULL) + return (PIX *)ERROR_PTR("tiff: no pix returned", __func__, NULL); + break; + + case IFF_PNM: + if ((pix = pixReadMemPnm(data, size)) == NULL) + return (PIX *)ERROR_PTR("pnm: no pix returned", __func__, NULL); + break; + + case IFF_GIF: + if ((pix = pixReadMemGif(data, size)) == NULL) + return (PIX *)ERROR_PTR("gif: no pix returned", __func__, NULL); + break; + + case IFF_JP2: + if ((pix = pixReadMemJp2k(data, size, 1, NULL, 0, 0)) == NULL) + return (PIX *)ERROR_PTR("jp2k: no pix returned", __func__, NULL); + break; + + case IFF_WEBP: + if ((pix = pixReadMemWebP(data, size)) == NULL) + return (PIX *)ERROR_PTR("webp: no pix returned", __func__, NULL); + break; + + case IFF_PS: + L_ERROR("PostScript reading is not supported\n", __func__); + return NULL; + + case IFF_LPDF: + L_ERROR("Pdf reading is not supported\n", __func__); + return NULL; + + case IFF_SPIX: + if ((pix = pixReadMemSpix(data, size)) == NULL) + return (PIX *)ERROR_PTR("spix: no pix returned", __func__, NULL); + break; + + case IFF_UNKNOWN: + return (PIX *)ERROR_PTR("Unknown format: no pix returned", + __func__, NULL); + break; + } + + /* Set the input format. For tiff reading from memory we lose + * the actual input format; for 1 bpp, default to G4. Also + * verify that the colormap is valid. */ + if (pix) { + if (format == IFF_TIFF && pixGetDepth(pix) == 1) + format = IFF_TIFF_G4; + pixSetInputFormat(pix, format); + if ((cmap = pixGetColormap(pix))) { + pixcmapIsValid(cmap, pix, &valid); + if (!valid) { + pixDestroy(&pix); + return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL); + } + } + pixSetPadBits(pix, 0); + } + return pix; +} + + +/*! + * \brief pixReadHeaderMem() + * + * \param[in] data const; encoded + * \param[in] size size of data + * \param[out] pformat [optional] image format + * \param[out] pw, ph [optional] width and height + * \param[out] pbps [optional] bits/sample + * \param[out] pspp [optional] samples/pixel 1, 3 or 4 + * \param[out] piscmap [optional] 1 if cmap exists; 0 otherwise + * \return 0 if OK, 1 on error + * + * <pre> + * Notes: + * (1) This reads the actual headers for jpeg, png, tiff, jp2k and pnm. + * For bmp and gif, we cheat and read all the data into a pix, + * from which we extract the "header" information. + * (2) The amount of data required depends on the format. For + * png, it requires less than 30 bytes, but for jpeg it can + * require most of the compressed file. In practice, the data + * is typically the entire compressed file in memory. + * (3) findFileFormatBuffer() requires up to 12 bytes to decide on + * the format, which we require. + * </pre> + */ +l_ok +pixReadHeaderMem(const l_uint8 *data, + size_t size, + l_int32 *pformat, + l_int32 *pw, + l_int32 *ph, + l_int32 *pbps, + l_int32 *pspp, + l_int32 *piscmap) +{ +l_int32 format, ret, w, h, d, bps, spp, iscmap; +l_int32 type; /* not used */ +PIX *pix; + + if (pw) *pw = 0; + if (ph) *ph = 0; + if (pbps) *pbps = 0; + if (pspp) *pspp = 0; + if (piscmap) *piscmap = 0; + if (pformat) *pformat = 0; + iscmap = 0; /* init to false */ + if (!data) + return ERROR_INT("data not defined", __func__, 1); + if (size < 12) + return ERROR_INT("size < 12", __func__, 1); + + findFileFormatBuffer(data, &format); + + switch (format) + { + case IFF_BMP: /* cheating: read the pix */ + if ((pix = pixReadMemBmp(data, size)) == NULL) + return ERROR_INT( "bmp: pix not read", __func__, 1); + pixGetDimensions(pix, &w, &h, &d); + pixDestroy(&pix); + bps = (d == 32) ? 8 : d; + spp = (d == 32) ? 3 : 1; + break; + + case IFF_JFIF_JPEG: + ret = readHeaderMemJpeg(data, size, &w, &h, &spp, NULL, NULL); + bps = 8; + if (ret) + return ERROR_INT( "jpeg: no header info returned", __func__, 1); + break; + + case IFF_PNG: + ret = readHeaderMemPng(data, size, &w, &h, &bps, &spp, &iscmap); + if (ret) + return ERROR_INT( "png: no header info returned", __func__, 1); + break; + + case IFF_TIFF: + case IFF_TIFF_PACKBITS: + case IFF_TIFF_RLE: + case IFF_TIFF_G3: + case IFF_TIFF_G4: + case IFF_TIFF_LZW: + case IFF_TIFF_ZIP: + case IFF_TIFF_JPEG: + /* Reading page 0 by default; possibly redefine format */ + ret = readHeaderMemTiff(data, size, 0, &w, &h, &bps, &spp, + NULL, &iscmap, &format); + if (ret) + return ERROR_INT( "tiff: no header info returned", __func__, 1); + break; + + case IFF_PNM: + ret = readHeaderMemPnm(data, size, &w, &h, &d, &type, &bps, &spp); + if (ret) + return ERROR_INT( "pnm: no header info returned", __func__, 1); + break; + + case IFF_GIF: /* cheating: read the pix */ + if ((pix = pixReadMemGif(data, size)) == NULL) + return ERROR_INT( "gif: pix not read", __func__, 1); + pixGetDimensions(pix, &w, &h, &d); + pixDestroy(&pix); + iscmap = 1; /* always colormapped; max 256 colors */ + spp = 1; + bps = d; + break; + + case IFF_JP2: + ret = readHeaderMemJp2k(data, size, &w, &h, &bps, &spp, NULL); + break; + + case IFF_WEBP: + bps = 8; + ret = readHeaderMemWebP(data, size, &w, &h, &spp); + break; + + case IFF_PS: + if (pformat) *pformat = format; + return ERROR_INT("PostScript reading is not supported\n", __func__, 1); + + case IFF_LPDF: + if (pformat) *pformat = format; + return ERROR_INT("Pdf reading is not supported\n", __func__, 1); + + case IFF_SPIX: + ret = sreadHeaderSpix((l_uint32 *)data, size, &w, &h, &bps, + &spp, &iscmap); + if (ret) + return ERROR_INT( "pnm: no header info returned", __func__, 1); + break; + + case IFF_UNKNOWN: + return ERROR_INT("unknown format; no data returned", __func__, 1); + break; + } + + if (pw) *pw = w; + if (ph) *ph = h; + if (pbps) *pbps = bps; + if (pspp) *pspp = spp; + if (piscmap) *piscmap = iscmap; + if (pformat) *pformat = format; + return 0; +} + + +/*---------------------------------------------------------------------* + * Output image file information * + *---------------------------------------------------------------------*/ +extern const char *ImageFileFormatExtensions[]; + +/*! + * \brief writeImageFileInfo() + * + * \param[in] filename input file + * \param[in] fpout output file stream + * \param[in] headeronly 1 to read only the header; 0 to read both + * the header and the input file + * \return 0 if OK; 1 on error + * + * <pre> + * Notes: + * (1) If headeronly == 0 and the image has spp == 4,this will + * also call pixDisplayLayersRGBA() to display the image + * in three views. + * (2) This is a debug function that changes the value of + * var_PNG_STRIP_16_TO_8 to 1 (the default). + * </pre> + */ +l_ok +writeImageFileInfo(const char *filename, + FILE *fpout, + l_int32 headeronly) +{ +char *text; +l_int32 w, h, d, wpl, count, npages, color; +l_int32 format, bps, spp, iscmap, xres, yres, transparency; +FILE *fpin; +PIX *pix, *pixt; +PIXCMAP *cmap; + + if (!filename) + return ERROR_INT("filename not defined", __func__, 1); + if (!fpout) + return ERROR_INT("stream not defined", __func__, 1); + + /* Read the header */ + if (pixReadHeader(filename, &format, &w, &h, &bps, &spp, &iscmap)) { + L_ERROR("failure to read header of %s\n", __func__, filename); + return 1; + } + fprintf(fpout, "===============================================\n" + "Reading the header:\n"); + fprintf(fpout, " input image format type: %s\n", + ImageFileFormatExtensions[format]); + fprintf(fpout, " w = %d, h = %d, bps = %d, spp = %d, iscmap = %d\n", + w, h, bps, spp, iscmap); + + findFileFormat(filename, &format); + if (format == IFF_JP2) { + fpin = fopenReadStream(filename); + fgetJp2kResolution(fpin, &xres, &yres); + if (fpin) fclose(fpin); + fprintf(fpout, " xres = %d, yres = %d\n", xres, yres); + } else if (format == IFF_PNG) { + fpin = fopenReadStream(filename); + fgetPngResolution(fpin, &xres, &yres); + if (fpin) fclose(fpin); + fprintf(fpout, " xres = %d, yres = %d\n", xres, yres); + if (iscmap) { + fpin = fopenReadStream(filename); + fgetPngColormapInfo(fpin, &cmap, &transparency); + if (fpin) fclose(fpin); + if (transparency) + fprintf(fpout, " colormap has transparency\n"); + else + fprintf(fpout, " colormap does not have transparency\n"); + pixcmapWriteStream(fpout, cmap); + pixcmapDestroy(&cmap); + } + } else if (format == IFF_JFIF_JPEG) { + fpin = fopenReadStream(filename); + fgetJpegResolution(fpin, &xres, &yres); + if (fpin) fclose(fpin); + fprintf(fpout, " xres = %d, yres = %d\n", xres, yres); + } + + if (headeronly) + return 0; + + /* Read the full image. Note that when we read an image that + * has transparency in a colormap, we convert it to RGBA. */ + fprintf(fpout, "===============================================\n" + "Reading the full image:\n"); + + /* Preserve 16 bpp if the format is png */ + if (format == IFF_PNG && bps == 16) + l_pngSetReadStrip16To8(0); + + if ((pix = pixRead(filename)) == NULL) { + L_ERROR("failure to read full image of %s\n", __func__, filename); + return 1; + } + + format = pixGetInputFormat(pix); + pixGetDimensions(pix, &w, &h, &d); + wpl = pixGetWpl(pix); + spp = pixGetSpp(pix); + fprintf(fpout, " input image format type: %s\n", + ImageFileFormatExtensions[format]); + fprintf(fpout, " w = %d, h = %d, d = %d, spp = %d, wpl = %d\n", + w, h, d, spp, wpl); + fprintf(fpout, " xres = %d, yres = %d\n", + pixGetXRes(pix), pixGetYRes(pix)); + + text = pixGetText(pix); + if (text) /* not null */ + fprintf(fpout, " text: %s\n", text); + + cmap = pixGetColormap(pix); + if (cmap) { + pixcmapHasColor(cmap, &color); + if (color) + fprintf(fpout, " colormap exists and has color values:"); + else + fprintf(fpout, " colormap exists and has only gray values:"); + pixcmapWriteStream(fpout, pixGetColormap(pix)); + } + else + fprintf(fpout, " colormap does not exist\n"); + + if (format == IFF_TIFF || format == IFF_TIFF_G4 || + format == IFF_TIFF_G3 || format == IFF_TIFF_PACKBITS) { + fprintf(fpout, " Tiff header information:\n"); + fpin = fopenReadStream(filename); + tiffGetCount(fpin, &npages); + if (fpin) fclose(fpin); + if (npages == 1) + fprintf(fpout, " One page in file\n"); + else + fprintf(fpout, " %d pages in file\n", npages); + fprintTiffInfo(fpout, filename); + } + + if (d == 1) { + pixCountPixels(pix, &count, NULL); + pixGetDimensions(pix, &w, &h, NULL); + fprintf(fpout, " 1 bpp: foreground pixel fraction ON/Total = %g\n", + (l_float32)count / (l_float32)(w * h)); + } + fprintf(fpout, "===============================================\n"); + + /* If there is an alpha component, visualize it. Note that when + * alpha == 0, the rgb layer is transparent. We visualize the + * result when a white background is visible through the + * transparency layer. */ + if (pixGetSpp(pix) == 4) { + pixt = pixDisplayLayersRGBA(pix, 0xffffff00, 600); + pixDisplay(pixt, 100, 100); + pixDestroy(&pixt); + } + + if (format == IFF_PNG && bps == 16) + l_pngSetReadStrip16To8(1); /* return to default if format is png */ + + pixDestroy(&pix); + return 0; +} + + +/*---------------------------------------------------------------------* + * Test function for I/O with different formats * + *---------------------------------------------------------------------*/ +/*! + * \brief ioFormatTest() + * + * \param[in] filename input image file + * \return 0 if OK; 1 on error or if the test fails + * + * <pre> + * Notes: + * (1) This writes and reads a set of output files losslessly + * in different formats to /tmp/format/, and tests that the + * result before and after is unchanged. + * (2) This should work properly on input images of any depth, + * with and without colormaps. + * (3) All supported formats are tested for bmp, png, tiff and + * non-ascii pnm. Ascii pnm also works (but who'd ever want + * to use it?) We allow 2 bpp bmp, although it's not + * supported elsewhere. And we don't support reading + * 16 bpp png, although this can be turned on in pngio.c. + * (4) This silently skips png or tiff testing if HAVE_LIBPNG + * or HAVE_LIBTIFF are 0, respectively. + * </pre> + */ +l_ok +ioFormatTest(const char *filename) +{ +l_int32 w, h, d, equal, problems; +#if HAVE_LIBJPEG || HAVE_LIBWEBP || HAVE_LIBJP2K +l_int32 depth; +#endif +#if HAVE_LIBJPEG || HAVE_LIBTIFF || HAVE_LIBWEBP || HAVE_LIBJP2K +l_float32 diff; +#endif +BOX *box; +PIX *pixs, *pixc, *pix1, *pix2; +PIXCMAP *cmap; + + if (!filename) + return ERROR_INT("filename not defined", __func__, 1); + + /* Read the input file and limit the size */ + if ((pix1 = pixRead(filename)) == NULL) + return ERROR_INT("pix1 not made", __func__, 1); + pixGetDimensions(pix1, &w, &h, NULL); + if (w > 250 && h > 250) { /* take the central 250 x 250 region */ + box = boxCreate(w / 2 - 125, h / 2 - 125, 250, 250); + pixs = pixClipRectangle(pix1, box, NULL); + boxDestroy(&box); + } else { + pixs = pixClone(pix1); + } + pixDestroy(&pix1); + + lept_mkdir("lept/format"); + + /* Note that the reader automatically removes colormaps + * from 1 bpp BMP images, but not from 8 bpp BMP images. + * Therefore, if our 8 bpp image initially doesn't have a + * colormap, we are going to need to remove it from any + * pix read from a BMP file. */ + pixc = pixClone(pixs); /* laziness */ + + /* This does not test the alpha layer pixels, because most + * formats don't support it. Remove any alpha. */ + if (pixGetSpp(pixc) == 4) + pixSetSpp(pixc, 3); + cmap = pixGetColormap(pixc); /* colormap; can be NULL */ + d = pixGetDepth(pixc); + + problems = FALSE; + + /* ----------------------- BMP -------------------------- */ + + /* BMP works for 1, 2, 4, 8 and 32 bpp images. + * It always writes colormaps for 1 and 8 bpp, so we must + * remove it after readback if the input image doesn't have + * a colormap. Although we can write/read 2 bpp BMP, nobody + * else can read them! */ + if (d == 1 || d == 8) { + L_INFO("write/read bmp\n", __func__); + pixWrite(FILE_BMP, pixc, IFF_BMP); + pix1 = pixRead(FILE_BMP); + if (!cmap) + pix2 = pixRemoveColormap(pix1, REMOVE_CMAP_BASED_ON_SRC); + else + pix2 = pixClone(pix1); + pixEqual(pixc, pix2, &equal); + if (!equal) { + L_INFO(" **** bad bmp image: d = %d ****\n", __func__, d); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); + } + + if (d == 2 || d == 4 || d == 32) { + L_INFO("write/read bmp\n", __func__); + pixWrite(FILE_BMP, pixc, IFF_BMP); + pix1 = pixRead(FILE_BMP); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad bmp image: d = %d ****\n", __func__, d); + problems = TRUE; + } + pixDestroy(&pix1); + } + + /* ----------------------- PNG -------------------------- */ +#if HAVE_LIBPNG + /* PNG works for all depths, but here, because we strip + * 16 --> 8 bpp on reading, we don't test png for 16 bpp. */ + if (d != 16) { + L_INFO("write/read png\n", __func__); + pixWrite(FILE_PNG, pixc, IFF_PNG); + pix1 = pixRead(FILE_PNG); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad png image: d = %d ****\n", __func__, d); + problems = TRUE; + } + pixDestroy(&pix1); + } +#endif /* HAVE_LIBPNG */ + + /* ----------------------- TIFF -------------------------- */ +#if HAVE_LIBTIFF && HAVE_LIBJPEG + /* TIFF works for 1, 2, 4, 8, 16 and 32 bpp images. + * Because 8 bpp tiff always writes 256 entry colormaps, the + * colormap sizes may be different for 8 bpp images with + * colormap; we are testing if the image content is the same. + * Likewise, the 2 and 4 bpp tiff images with colormaps + * have colormap sizes 4 and 16, rsp. This test should + * work properly on the content, regardless of the number + * of color entries in pixc. */ + + /* tiff uncompressed works for all pixel depths */ + L_INFO("write/read uncompressed tiff\n", __func__); + pixWrite(FILE_TIFF, pixc, IFF_TIFF); + pix1 = pixRead(FILE_TIFF); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff uncompressed image: d = %d ****\n", + __func__, d); + problems = TRUE; + } + pixDestroy(&pix1); + + /* tiff lzw works for all pixel depths */ + L_INFO("write/read lzw compressed tiff\n", __func__); + pixWrite(FILE_LZW, pixc, IFF_TIFF_LZW); + pix1 = pixRead(FILE_LZW); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff lzw compressed image: d = %d ****\n", + __func__, d); + problems = TRUE; + } + pixDestroy(&pix1); + + /* tiff adobe deflate (zip) works for all pixel depths */ + L_INFO("write/read zip compressed tiff\n", __func__); + pixWrite(FILE_ZIP, pixc, IFF_TIFF_ZIP); + pix1 = pixRead(FILE_ZIP); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff zip compressed image: d = %d ****\n", + __func__, d); + problems = TRUE; + } + pixDestroy(&pix1); + + /* tiff jpeg encoding works for grayscale and rgb */ + if (d == 8 || d == 32) { + PIX *pixc1; + L_INFO("write/read jpeg compressed tiff\n", __func__); + if (d == 8 && pixGetColormap(pixc)) { + pixc1 = pixRemoveColormap(pixc, REMOVE_CMAP_BASED_ON_SRC); + pixWrite(FILE_TIFF_JPEG, pixc1, IFF_TIFF_JPEG); + if ((pix1 = pixRead(FILE_TIFF_JPEG)) == NULL) { + L_INFO(" did not read FILE_TIFF_JPEG\n", __func__); + problems = TRUE; + } + pixDestroy(&pixc1); + } else { + pixWrite(FILE_TIFF_JPEG, pixc, IFF_TIFF_JPEG); + pix1 = pixRead(FILE_TIFF_JPEG); + if (d == 8) { + pixCompareGray(pix1, pixc, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } else { + pixCompareRGB(pix1, pixc, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } + if (diff > 8.0) { + L_INFO(" **** bad tiff jpeg compressed image: " + "d = %d, diff = %5.2f ****\n", __func__, d, diff); + problems = TRUE; + } + } + pixDestroy(&pix1); + } + + /* tiff g4, g3, rle and packbits work for 1 bpp */ + if (d == 1) { + L_INFO("write/read g4 compressed tiff\n", __func__); + pixWrite(FILE_G4, pixc, IFF_TIFF_G4); + pix1 = pixRead(FILE_G4); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff g4 image ****\n", __func__); + problems = TRUE; + } + pixDestroy(&pix1); + + L_INFO("write/read g3 compressed tiff\n", __func__); + pixWrite(FILE_G3, pixc, IFF_TIFF_G3); + pix1 = pixRead(FILE_G3); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff g3 image ****\n", __func__); + problems = TRUE; + } + pixDestroy(&pix1); + + L_INFO("write/read rle compressed tiff\n", __func__); + pixWrite(FILE_RLE, pixc, IFF_TIFF_RLE); + pix1 = pixRead(FILE_RLE); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff rle image: d = %d ****\n", __func__, d); + problems = TRUE; + } + pixDestroy(&pix1); + + L_INFO("write/read packbits compressed tiff\n", __func__); + pixWrite(FILE_PB, pixc, IFF_TIFF_PACKBITS); + pix1 = pixRead(FILE_PB); + pixEqual(pixc, pix1, &equal); + if (!equal) { + L_INFO(" **** bad tiff packbits image: d = %d ****\n", + __func__, d); + problems = TRUE; + } + pixDestroy(&pix1); + } +#endif /* HAVE_LIBTIFF && HAVE_LIBJPEG */ + + /* ----------------------- PNM -------------------------- */ + + /* pnm works for 1, 2, 4, 8, 16 and 32 bpp. + * pnm doesn't have colormaps, so when we write colormapped + * pix out as pnm, the colormap is removed. Thus for the test, + * we must remove the colormap from pixc before testing. */ + L_INFO("write/read pnm\n", __func__); + pixWrite(FILE_PNM, pixc, IFF_PNM); + pix1 = pixRead(FILE_PNM); + if (cmap) + pix2 = pixRemoveColormap(pixc, REMOVE_CMAP_BASED_ON_SRC); + else + pix2 = pixClone(pixc); + pixEqual(pix1, pix2, &equal); + if (!equal) { + L_INFO(" **** bad pnm image: d = %d ****\n", __func__, d); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); + + /* ----------------------- GIF -------------------------- */ +#if HAVE_LIBGIF + /* GIF works for only 1 and 8 bpp, colormapped */ + if (d != 8 || !cmap) + pix1 = pixConvertTo8(pixc, 1); + else + pix1 = pixClone(pixc); + L_INFO("write/read gif\n", __func__); + pixWrite(FILE_GIF, pix1, IFF_GIF); + pix2 = pixRead(FILE_GIF); + pixEqual(pix1, pix2, &equal); + if (!equal) { + L_INFO(" **** bad gif image: d = %d ****\n", __func__, d); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); +#endif /* HAVE_LIBGIF */ + + /* ----------------------- JPEG ------------------------- */ +#if HAVE_LIBJPEG + /* JPEG works for only 8 bpp gray and rgb */ + if (cmap || d > 8) + pix1 = pixConvertTo32(pixc); + else + pix1 = pixConvertTo8(pixc, 0); + depth = pixGetDepth(pix1); + L_INFO("write/read jpeg\n", __func__); + pixWrite(FILE_JPG, pix1, IFF_JFIF_JPEG); + pix2 = pixRead(FILE_JPG); + if (depth == 8) { + pixCompareGray(pix1, pix2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } else { + pixCompareRGB(pix1, pix2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } + if (diff > 8.0) { + L_INFO(" **** bad jpeg image: d = %d, diff = %5.2f ****\n", + __func__, depth, diff); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); +#endif /* HAVE_LIBJPEG */ + + /* ----------------------- WEBP ------------------------- */ +#if HAVE_LIBWEBP + /* WEBP works for rgb and rgba */ + if (cmap || d <= 16) + pix1 = pixConvertTo32(pixc); + else + pix1 = pixClone(pixc); + depth = pixGetDepth(pix1); + L_INFO("write/read webp\n", __func__); + pixWrite(FILE_WEBP, pix1, IFF_WEBP); + pix2 = pixRead(FILE_WEBP); + pixCompareRGB(pix1, pix2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, NULL, NULL); + if (diff > 5.0) { + L_INFO(" **** bad webp image: d = %d, diff = %5.2f ****\n", + __func__, depth, diff); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); +#endif /* HAVE_LIBWEBP */ + + /* ----------------------- JP2K ------------------------- */ +#if HAVE_LIBJP2K + /* JP2K works for only 8 bpp gray, rgb and rgba */ + if (cmap || d > 8) + pix1 = pixConvertTo32(pixc); + else + pix1 = pixConvertTo8(pixc, 0); + depth = pixGetDepth(pix1); + L_INFO("write/read jp2k\n", __func__); + pixWrite(FILE_JP2K, pix1, IFF_JP2); + pix2 = pixRead(FILE_JP2K); + if (depth == 8) { + pixCompareGray(pix1, pix2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } else { + pixCompareRGB(pix1, pix2, L_COMPARE_ABS_DIFF, 0, NULL, &diff, + NULL, NULL); + } + lept_stderr("diff = %7.3f\n", diff); + if (diff > 7.0) { + L_INFO(" **** bad jp2k image: d = %d, diff = %5.2f ****\n", + __func__, depth, diff); + problems = TRUE; + } + pixDestroy(&pix1); + pixDestroy(&pix2); +#endif /* HAVE_LIBJP2K */ + + if (problems == FALSE) + L_INFO("All formats read and written OK!\n", __func__); + + pixDestroy(&pixc); + pixDestroy(&pixs); + return problems; +}
