1 /*====================================================================*
2  -  Copyright (C) 2001-2016 Leptonica.  All rights reserved.
3  -
4  -  Redistribution and use in source and binary forms, with or without
5  -  modification, are permitted provided that the following conditions
6  -  are met:
7  -  1. Redistributions of source code must retain the above copyright
8  -     notice, this list of conditions and the following disclaimer.
9  -  2. Redistributions in binary form must reproduce the above
10  -     copyright notice, this list of conditions and the following
11  -     disclaimer in the documentation and/or other materials
12  -     provided with the distribution.
13  -
14  -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18  -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
27 /*
28  * writefile.c
29  *
30  *     Set jpeg quality for pixWrite() and pixWriteMem()
31  *        l_int32     l_jpegSetQuality()
32  *
33  *     Set global variable LeptDebugOK for writing to named temp files
34  *        l_int32     setLeptDebugOK()
35  *
36  *     High-level procedures for writing images to file:
37  *        l_int32     pixaWriteFiles()
38  *        l_int32     pixWriteDebug()
39  *        l_int32     pixWrite()
40  *        l_int32     pixWriteAutoFormat()
41  *        l_int32     pixWriteStream()
42  *        l_int32     pixWriteImpliedFormat()
43  *
44  *     Selection of output format if default is requested
45  *        l_int32     pixChooseOutputFormat()
46  *        l_int32     getImpliedFileFormat()
47  *        l_int32     pixGetAutoFormat()
48  *        const char *getFormatExtension()
49  *
50  *     Write to memory
51  *        l_int32     pixWriteMem()
52  *
53  *     Image display for debugging
54  *        l_int32     l_fileDisplay()
55  *        l_int32     pixDisplay()
56  *        l_int32     pixDisplayWithTitle()
57  *        l_int32     pixSaveTiled()
58  *        l_int32     pixSaveTiledOutline()
59  *        l_int32     pixSaveTiledWithText()
60  *        void        l_chooseDisplayProg()
61  *
62  *     Deprecated pix output for debugging (still used in tesseract 3.05)
63  *        l_int32     pixDisplayWrite()
64  *
65  *  Supported file formats:
66  *  (1) Writing is supported without any external libraries:
67  *          bmp
68  *          pnm   (including pbm, pgm, etc)
69  *          spix  (raw serialized)
70  *  (2) Writing is supported with installation of external libraries:
71  *          png
72  *          jpg   (standard jfif version)
73  *          tiff  (including most varieties of compression)
74  *          gif
75  *          webp
76  *          jp2 (jpeg2000)
77  *  (3) Writing is supported through special interfaces:
78  *          ps (PostScript, in psio1.c, psio2.c):
79  *              level 1 (uncompressed)
80  *              level 2 (g4 and dct encoding: requires tiff, jpg)
81  *              level 3 (g4, dct and flate encoding: requires tiff, jpg, zlib)
82  *          pdf (PDF, in pdfio.c):
83  *              level 1 (g4 and dct encoding: requires tiff, jpg)
84  *              level 2 (g4, dct and flate encoding: requires tiff, jpg, zlib)
85  */
86 
87 #include <string.h>
88 #include "allheaders.h"
89 
90     /* Display program (xv, xli, xzgv, open) to be invoked by pixDisplay()  */
91 #ifdef _WIN32
92 static l_int32  var_DISPLAY_PROG = L_DISPLAY_WITH_IV;  /* default */
93 #elif  defined(__APPLE__)
94 static l_int32  var_DISPLAY_PROG = L_DISPLAY_WITH_OPEN;  /* default */
95 #else
96 static l_int32  var_DISPLAY_PROG = L_DISPLAY_WITH_XZGV;  /* default */
97 #endif  /* _WIN32 */
98 
99 static const l_int32  L_BUFSIZE = 512;
100 static const l_int32  MAX_DISPLAY_WIDTH = 1000;
101 static const l_int32  MAX_DISPLAY_HEIGHT = 800;
102 static const l_int32  MAX_SIZE_FOR_PNG = 200;
103 
104     /* PostScript output for printing */
105 static const l_float32  DEFAULT_SCALING = 1.0;
106 
107     /* Global array of image file format extension names.                */
108     /* This is in 1-1 corrspondence with format enum in imageio.h.       */
109     /* The empty string at the end represents the serialized format,     */
110     /* which has no recognizable extension name, but the array must      */
111     /* be padded to agree with the format enum.                          */
112     /* (Note on 'const': The size of the array can't be defined 'const'  */
113     /* because that makes it static.  The 'const' in the definition of   */
114     /* the array refers to the strings in the array; the ptr to the      */
115     /* array is not const and can be used 'extern' in other files.)      */
116 LEPT_DLL l_int32  NumImageFileFormatExtensions = 19;  /* array size */
117 LEPT_DLL const char *ImageFileFormatExtensions[] =
118          {"unknown",
119           "bmp",
120           "jpg",
121           "png",
122           "tif",
123           "tif",
124           "tif",
125           "tif",
126           "tif",
127           "tif",
128           "tif",
129           "pnm",
130           "ps",
131           "gif",
132           "jp2",
133           "webp",
134           "pdf",
135           "default",
136           ""};
137 
138     /* Local map of image file name extension to output format */
139 struct ExtensionMap
140 {
141     char     extension[8];
142     l_int32  format;
143 };
144 static const struct ExtensionMap extension_map[] =
145                             { { ".bmp",  IFF_BMP       },
146                               { ".jpg",  IFF_JFIF_JPEG },
147                               { ".jpeg", IFF_JFIF_JPEG },
148                               { ".png",  IFF_PNG       },
149                               { ".tif",  IFF_TIFF      },
150                               { ".tiff", IFF_TIFF      },
151                               { ".pnm",  IFF_PNM       },
152                               { ".gif",  IFF_GIF       },
153                               { ".jp2",  IFF_JP2       },
154                               { ".ps",   IFF_PS        },
155                               { ".pdf",  IFF_LPDF      },
156                               { ".webp", IFF_WEBP      } };
157 
158 
159 /*---------------------------------------------------------------------*
160  *           Set jpeg quality for pixWrite() and pixWriteMem()         *
161  *---------------------------------------------------------------------*/
162     /* Parameter that controls jpeg quality for high-level calls. */
163 static l_int32  var_JPEG_QUALITY = 75;   /* default */
164 
165 /*!
166  * \brief   l_jpegSetQuality()
167  *
168  * \param[in]    new_quality    1 - 100; 75 is default; 0 defaults to 75
169  * \return       prev           previous quality
170  *
171  * <pre>
172  * Notes:
173  *      (1) This variable is used in pixWriteStream() and pixWriteMem(),
174  *          to control the jpeg quality.  The default is 75.
175  *      (2) It returns the previous quality, so for example:
176  *           l_int32  prev = l_jpegSetQuality(85);  //sets to 85
177  *           pixWriteStream(...);
178  *           l_jpegSetQuality(prev);   // resets to previous value
179  *      (3) On error, logs a message and does not change the variable.
180  */
181 l_int32
l_jpegSetQuality(l_int32 new_quality)182 l_jpegSetQuality(l_int32  new_quality)
183 {
184 l_int32  prevq, newq;
185 
186     PROCNAME("l_jpeqSetQuality");
187 
188     prevq = var_JPEG_QUALITY;
189     newq = (new_quality == 0) ? 75 : new_quality;
190     if (newq < 1 || newq > 100)
191         L_ERROR("invalid jpeg quality; unchanged\n", procName);
192     else
193         var_JPEG_QUALITY = newq;
194     return prevq;
195 }
196 
197 
198 /*----------------------------------------------------------------------*
199  *    Set global variable LeptDebugOK for writing to named temp files   *
200  *----------------------------------------------------------------------*/
201 l_int32 LeptDebugOK = 0;  /* default value */
202 /*!
203  * \brief   setLeptDebugOK()
204  *
205  * \param[in]    allow     TRUE (1) or FALSE (0)
206  * \return       void
207  *
208  * <pre>
209  * Notes:
210  *      (1) This sets or clears the global variable LeptDebugOK, to
211  *          control writing files in a temp directory with names that
212  *          are compiled in.
213  *      (2) The default in the library distribution is 0.  Call with
214  *          %allow = 1 for development and debugging.
215  */
216 void
setLeptDebugOK(l_int32 allow)217 setLeptDebugOK(l_int32  allow)
218 {
219     if (allow != 0) allow = 1;
220     LeptDebugOK = allow;
221 }
222 
223 
224 /*---------------------------------------------------------------------*
225  *           Top-level procedures for writing images to file           *
226  *---------------------------------------------------------------------*/
227 /*!
228  * \brief   pixaWriteFiles()
229  *
230  * \param[in]    rootname
231  * \param[in]    pixa
232  * \param[in]    format  defined in imageio.h; see notes for default
233  * \return  0 if OK; 1 on error
234  *
235  * <pre>
236  * Notes:
237  *      (1) Use %format = IFF_DEFAULT to decide the output format
238  *          individually for each pix.
239  * </pre>
240  */
241 l_int32
pixaWriteFiles(const char * rootname,PIXA * pixa,l_int32 format)242 pixaWriteFiles(const char  *rootname,
243                PIXA        *pixa,
244                l_int32      format)
245 {
246 char     bigbuf[L_BUFSIZE];
247 l_int32  i, n, pixformat;
248 PIX     *pix;
249 
250     PROCNAME("pixaWriteFiles");
251 
252     if (!rootname)
253         return ERROR_INT("rootname not defined", procName, 1);
254     if (!pixa)
255         return ERROR_INT("pixa not defined", procName, 1);
256     if (format < 0 || format == IFF_UNKNOWN ||
257         format >= NumImageFileFormatExtensions)
258         return ERROR_INT("invalid format", procName, 1);
259 
260     n = pixaGetCount(pixa);
261     for (i = 0; i < n; i++) {
262         pix = pixaGetPix(pixa, i, L_CLONE);
263         if (format == IFF_DEFAULT)
264             pixformat = pixChooseOutputFormat(pix);
265         else
266             pixformat = format;
267         snprintf(bigbuf, L_BUFSIZE, "%s%03d.%s", rootname, i,
268                  ImageFileFormatExtensions[pixformat]);
269         pixWrite(bigbuf, pix, pixformat);
270         pixDestroy(&pix);
271     }
272 
273     return 0;
274 }
275 
276 
277 /*!
278  * \brief   pixWriteDebug()
279  *
280  * \param[in]    fname
281  * \param[in]    pix
282  * \param[in]    format  defined in imageio.h
283  * \return  0 if OK; 1 on error
284  *
285  * <pre>
286  * Notes:
287  *      (1) Debug version, intended for use in the library when writing
288  *          to files in a temp directory with names that are compiled in.
289  *          This is used instead of pixWrite() for all such library calls.
290  *      (2) The global variable LeptDebugOK defaults to 0, and can be set
291  *          or cleared by the function setLeptDebugOK().
292  * </pre>
293  */
294 l_int32
pixWriteDebug(const char * fname,PIX * pix,l_int32 format)295 pixWriteDebug(const char  *fname,
296               PIX         *pix,
297               l_int32      format)
298 {
299     PROCNAME("pixWriteDebug");
300 
301     if (LeptDebugOK) {
302         return pixWrite(fname, pix, format);
303     } else {
304         L_INFO("write to named temp file %s is disabled\n", procName, fname);
305         return 0;
306     }
307 }
308 
309 
310 /*!
311  * \brief   pixWrite()
312  *
313  * \param[in]    fname
314  * \param[in]    pix
315  * \param[in]    format  defined in imageio.h
316  * \return  0 if OK; 1 on error
317  *
318  * <pre>
319  * Notes:
320  *      (1) Open for write using binary mode (with the "b" flag)
321  *          to avoid having Windows automatically translate the NL
322  *          into CRLF, which corrupts image files.  On non-windows
323  *          systems this flag should be ignored, per ISO C90.
324  *          Thanks to Dave Bryan for pointing this out.
325  *      (2) If the default image format IFF_DEFAULT is requested:
326  *          use the input format if known; otherwise, use a lossless format.
327  *      (3) The default jpeg quality is 75.  For some other value,
328  *          Use l_jpegSetQuality().
329  * </pre>
330  */
331 l_int32
pixWrite(const char * fname,PIX * pix,l_int32 format)332 pixWrite(const char  *fname,
333          PIX         *pix,
334          l_int32      format)
335 {
336 l_int32  ret;
337 FILE    *fp;
338 
339     PROCNAME("pixWrite");
340 
341     if (!pix)
342         return ERROR_INT("pix not defined", procName, 1);
343     if (!fname)
344         return ERROR_INT("fname not defined", procName, 1);
345 
346     if ((fp = fopenWriteStream(fname, "wb+")) == NULL)
347         return ERROR_INT("stream not opened", procName, 1);
348 
349     ret = pixWriteStream(fp, pix, format);
350     fclose(fp);
351     if (ret)
352         return ERROR_INT("pix not written to stream", procName, 1);
353     return 0;
354 }
355 
356 
357 /*!
358  * \brief   pixWriteAutoFormat()
359  *
360  * \param[in]    filename
361  * \param[in]    pix
362  * \return  0 if OK; 1 on error
363  */
364 l_int32
pixWriteAutoFormat(const char * filename,PIX * pix)365 pixWriteAutoFormat(const char  *filename,
366                    PIX         *pix)
367 {
368 l_int32  format;
369 
370     PROCNAME("pixWriteAutoFormat");
371 
372     if (!pix)
373         return ERROR_INT("pix not defined", procName, 1);
374     if (!filename)
375         return ERROR_INT("filename not defined", procName, 1);
376 
377     if (pixGetAutoFormat(pix, &format))
378         return ERROR_INT("auto format not returned", procName, 1);
379     return pixWrite(filename, pix, format);
380 }
381 
382 
383 /*!
384  * \brief   pixWriteStream()
385  *
386  * \param[in]    fp file stream
387  * \param[in]    pix
388  * \param[in]    format
389  * \return  0 if OK; 1 on error.
390  */
391 l_int32
pixWriteStream(FILE * fp,PIX * pix,l_int32 format)392 pixWriteStream(FILE    *fp,
393                PIX     *pix,
394                l_int32  format)
395 {
396     PROCNAME("pixWriteStream");
397 
398     if (!fp)
399         return ERROR_INT("stream not defined", procName, 1);
400     if (!pix)
401         return ERROR_INT("pix not defined", procName, 1);
402 
403     if (format == IFF_DEFAULT)
404         format = pixChooseOutputFormat(pix);
405 
406     switch(format)
407     {
408     case IFF_BMP:
409         pixWriteStreamBmp(fp, pix);
410         break;
411 
412     case IFF_JFIF_JPEG:   /* default quality; baseline sequential */
413         return pixWriteStreamJpeg(fp, pix, var_JPEG_QUALITY, 0);
414         break;
415 
416     case IFF_PNG:   /* no gamma value stored */
417         return pixWriteStreamPng(fp, pix, 0.0);
418         break;
419 
420     case IFF_TIFF:           /* uncompressed */
421     case IFF_TIFF_PACKBITS:  /* compressed, binary only */
422     case IFF_TIFF_RLE:       /* compressed, binary only */
423     case IFF_TIFF_G3:        /* compressed, binary only */
424     case IFF_TIFF_G4:        /* compressed, binary only */
425     case IFF_TIFF_LZW:       /* compressed, all depths */
426     case IFF_TIFF_ZIP:       /* compressed, all depths */
427         return pixWriteStreamTiff(fp, pix, format);
428         break;
429 
430     case IFF_PNM:
431         return pixWriteStreamPnm(fp, pix);
432         break;
433 
434     case IFF_PS:
435         return pixWriteStreamPS(fp, pix, NULL, 0, DEFAULT_SCALING);
436         break;
437 
438     case IFF_GIF:
439         return pixWriteStreamGif(fp, pix);
440         break;
441 
442     case IFF_JP2:
443         return pixWriteStreamJp2k(fp, pix, 34, 4, 0, 0);
444         break;
445 
446     case IFF_WEBP:
447         return pixWriteStreamWebP(fp, pix, 80, 0);
448         break;
449 
450     case IFF_LPDF:
451         return pixWriteStreamPdf(fp, pix, 0, NULL);
452         break;
453 
454     case IFF_SPIX:
455         return pixWriteStreamSpix(fp, pix);
456         break;
457 
458     default:
459         return ERROR_INT("unknown format", procName, 1);
460         break;
461     }
462 
463     return 0;
464 }
465 
466 
467 /*!
468  * \brief   pixWriteImpliedFormat()
469  *
470  * \param[in]    filename
471  * \param[in]    pix
472  * \param[in]    quality iff JPEG; 1 - 100, 0 for default
473  * \param[in]    progressive iff JPEG; 0 for baseline seq., 1 for progressive
474  * \return  0 if OK; 1 on error
475  *
476  * <pre>
477  * Notes:
478  *      (1) This determines the output format from the filename extension.
479  *      (2) The last two args are ignored except for requests for jpeg files.
480  *      (3) The jpeg default quality is 75.
481  * </pre>
482  */
483 l_int32
pixWriteImpliedFormat(const char * filename,PIX * pix,l_int32 quality,l_int32 progressive)484 pixWriteImpliedFormat(const char  *filename,
485                       PIX         *pix,
486                       l_int32      quality,
487                       l_int32      progressive)
488 {
489 l_int32  format;
490 
491     PROCNAME("pixWriteImpliedFormat");
492 
493     if (!filename)
494         return ERROR_INT("filename not defined", procName, 1);
495     if (!pix)
496         return ERROR_INT("pix not defined", procName, 1);
497 
498         /* Determine output format */
499     format = getImpliedFileFormat(filename);
500     if (format == IFF_UNKNOWN) {
501         format = IFF_PNG;
502     } else if (format == IFF_TIFF) {
503         if (pixGetDepth(pix) == 1)
504             format = IFF_TIFF_G4;
505         else
506 #ifdef _WIN32
507             format = IFF_TIFF_LZW;  /* poor compression */
508 #else
509             format = IFF_TIFF_ZIP;  /* native windows tools can't handle this */
510 #endif  /* _WIN32 */
511     }
512 
513     if (format == IFF_JFIF_JPEG) {
514         quality = L_MIN(quality, 100);
515         quality = L_MAX(quality, 0);
516         if (progressive != 0 && progressive != 1) {
517             progressive = 0;
518             L_WARNING("invalid progressive; setting to baseline\n", procName);
519         }
520         if (quality == 0)
521             quality = 75;
522         pixWriteJpeg (filename, pix, quality, progressive);
523     } else {
524         pixWrite(filename, pix, format);
525     }
526 
527     return 0;
528 }
529 
530 
531 /*---------------------------------------------------------------------*
532  *          Selection of output format if default is requested         *
533  *---------------------------------------------------------------------*/
534 /*!
535  * \brief   pixChooseOutputFormat()
536  *
537  * \param[in]    pix
538  * \return  output format, or 0 on error
539  *
540  * <pre>
541  * Notes:
542  *      (1) This should only be called if the requested format is IFF_DEFAULT.
543  *      (2) If the pix wasn't read from a file, its input format value
544  *          will be IFF_UNKNOWN, and in that case it is written out
545  *          in a compressed but lossless format.
546  * </pre>
547  */
548 l_int32
pixChooseOutputFormat(PIX * pix)549 pixChooseOutputFormat(PIX  *pix)
550 {
551 l_int32  d, format;
552 
553     PROCNAME("pixChooseOutputFormat");
554 
555     if (!pix)
556         return ERROR_INT("pix not defined", procName, 0);
557 
558     d = pixGetDepth(pix);
559     format = pixGetInputFormat(pix);
560     if (format == IFF_UNKNOWN) {  /* output lossless */
561         if (d == 1)
562             format = IFF_TIFF_G4;
563         else
564             format = IFF_PNG;
565     }
566 
567     return format;
568 }
569 
570 
571 /*!
572  * \brief   getImpliedFileFormat()
573  *
574  * \param[in]    filename
575  * \return  output format, or IFF_UNKNOWN on error or invalid extension.
576  *
577  * <pre>
578  * Notes:
579  *      (1) This determines the output file format from the extension
580  *          of the input filename.
581  * </pre>
582  */
583 l_int32
getImpliedFileFormat(const char * filename)584 getImpliedFileFormat(const char  *filename)
585 {
586 char    *extension;
587 int      i, numext;
588 l_int32  format = IFF_UNKNOWN;
589 
590     if (splitPathAtExtension (filename, NULL, &extension))
591         return IFF_UNKNOWN;
592 
593     numext = sizeof(extension_map) / sizeof(extension_map[0]);
594     for (i = 0; i < numext; i++) {
595         if (!strcmp(extension, extension_map[i].extension)) {
596             format = extension_map[i].format;
597             break;
598         }
599     }
600 
601     LEPT_FREE(extension);
602     return format;
603 }
604 
605 
606 /*!
607  * \brief   pixGetAutoFormat()
608  *
609  * \param[in]    pix
610  * \param[in]    &format
611  * \return  0 if OK, 1 on error
612  *
613  * <pre>
614  * Notes:
615  *      (1) The output formats are restricted to tiff, jpeg and png
616  *          because these are the most commonly used image formats and
617  *          the ones that are typically installed with leptonica.
618  *      (2) This decides what compression to use based on the pix.
619  *          It chooses tiff-g4 if 1 bpp without a colormap, jpeg with
620  *          quality 75 if grayscale, rgb or rgba (where it loses
621  *          the alpha layer), and lossless png for all other situations.
622  * </pre>
623  */
624 l_int32
pixGetAutoFormat(PIX * pix,l_int32 * pformat)625 pixGetAutoFormat(PIX      *pix,
626                  l_int32  *pformat)
627 {
628 l_int32   d;
629 PIXCMAP  *cmap;
630 
631     PROCNAME("pixGetAutoFormat");
632 
633     if (!pformat)
634         return ERROR_INT("&format not defined", procName, 0);
635     *pformat = IFF_UNKNOWN;
636     if (!pix)
637         return ERROR_INT("pix not defined", procName, 0);
638 
639     d = pixGetDepth(pix);
640     cmap = pixGetColormap(pix);
641     if (d == 1 && !cmap) {
642         *pformat = IFF_TIFF_G4;
643     } else if ((d == 8 && !cmap) || d == 24 || d == 32) {
644         *pformat = IFF_JFIF_JPEG;
645     } else {
646         *pformat = IFF_PNG;
647     }
648 
649     return 0;
650 }
651 
652 
653 /*!
654  * \brief   getFormatExtension()
655  *
656  * \param[in]    format integer
657  * \return  extension string, or NULL if format is out of range
658  *
659  * <pre>
660  * Notes:
661  *      (1) This string is NOT owned by the caller; it is just a pointer
662  *          to a global string.  Do not free it.
663  * </pre>
664  */
665 const char *
getFormatExtension(l_int32 format)666 getFormatExtension(l_int32  format)
667 {
668     PROCNAME("getFormatExtension");
669 
670     if (format < 0 || format >= NumImageFileFormatExtensions)
671         return (const char *)ERROR_PTR("invalid format", procName, NULL);
672 
673     return ImageFileFormatExtensions[format];
674 }
675 
676 
677 /*---------------------------------------------------------------------*
678  *                            Write to memory                          *
679  *---------------------------------------------------------------------*/
680 /*!
681  * \brief   pixWriteMem()
682  *
683  * \param[out]   pdata data of tiff compressed image
684  * \param[out]   psize size of returned data
685  * \param[in]    pix
686  * \param[in]    format  defined in imageio.h
687  * \return  0 if OK, 1 on error
688  *
689  * <pre>
690  * Notes:
691  *      (1) On windows, this will only write tiff and PostScript to memory.
692  *          For other formats, it requires open_memstream(3).
693  *      (2) PostScript output is uncompressed, in hex ascii.
694  *          Most printers support level 2 compression (tiff_g4 for 1 bpp,
695  *          jpeg for 8 and 32 bpp).
696  *      (3) The default jpeg quality is 75.  For some other value,
697  *          Use l_jpegSetQuality().
698  * </pre>
699  */
700 l_int32
pixWriteMem(l_uint8 ** pdata,size_t * psize,PIX * pix,l_int32 format)701 pixWriteMem(l_uint8  **pdata,
702             size_t    *psize,
703             PIX       *pix,
704             l_int32    format)
705 {
706 l_int32  ret;
707 
708     PROCNAME("pixWriteMem");
709 
710     if (!pdata)
711         return ERROR_INT("&data not defined", procName, 1 );
712     if (!psize)
713         return ERROR_INT("&size not defined", procName, 1 );
714     if (!pix)
715         return ERROR_INT("&pix not defined", procName, 1 );
716 
717     if (format == IFF_DEFAULT)
718         format = pixChooseOutputFormat(pix);
719 
720     switch(format)
721     {
722     case IFF_BMP:
723         ret = pixWriteMemBmp(pdata, psize, pix);
724         break;
725 
726     case IFF_JFIF_JPEG:   /* default quality; baseline sequential */
727         ret = pixWriteMemJpeg(pdata, psize, pix, var_JPEG_QUALITY, 0);
728         break;
729 
730     case IFF_PNG:   /* no gamma value stored */
731         ret = pixWriteMemPng(pdata, psize, pix, 0.0);
732         break;
733 
734     case IFF_TIFF:           /* uncompressed */
735     case IFF_TIFF_PACKBITS:  /* compressed, binary only */
736     case IFF_TIFF_RLE:       /* compressed, binary only */
737     case IFF_TIFF_G3:        /* compressed, binary only */
738     case IFF_TIFF_G4:        /* compressed, binary only */
739     case IFF_TIFF_LZW:       /* compressed, all depths */
740     case IFF_TIFF_ZIP:       /* compressed, all depths */
741         ret = pixWriteMemTiff(pdata, psize, pix, format);
742         break;
743 
744     case IFF_PNM:
745         ret = pixWriteMemPnm(pdata, psize, pix);
746         break;
747 
748     case IFF_PS:
749         ret = pixWriteMemPS(pdata, psize, pix, NULL, 0, DEFAULT_SCALING);
750         break;
751 
752     case IFF_GIF:
753         ret = pixWriteMemGif(pdata, psize, pix);
754         break;
755 
756     case IFF_JP2:
757         ret = pixWriteMemJp2k(pdata, psize, pix, 34, 0, 0, 0);
758         break;
759 
760     case IFF_WEBP:
761         ret = pixWriteMemWebP(pdata, psize, pix, 80, 0);
762         break;
763 
764     case IFF_LPDF:
765         ret = pixWriteMemPdf(pdata, psize, pix, 0, NULL);
766         break;
767 
768     case IFF_SPIX:
769         ret = pixWriteMemSpix(pdata, psize, pix);
770         break;
771 
772     default:
773         return ERROR_INT("unknown format", procName, 1);
774         break;
775     }
776 
777     return ret;
778 }
779 
780 
781 /*---------------------------------------------------------------------*
782  *                      Image display for debugging                    *
783  *---------------------------------------------------------------------*/
784 /*!
785  * \brief   l_fileDisplay()
786  *
787  * \param[in]    fname
788  * \param[in]    x, y  location of display frame on the screen
789  * \param[in]    scale  scale factor (use 0 to skip display)
790  * \return  0 if OK; 1 on error
791  *
792  * <pre>
793  * Notes:
794  *      (1) This is a convenient wrapper for displaying image files.
795  *      (2) Set %scale = 0 to disable display.
796  *      (3) This downscales 1 bpp to gray.
797  * </pre>
798  */
799 l_int32
l_fileDisplay(const char * fname,l_int32 x,l_int32 y,l_float32 scale)800 l_fileDisplay(const char  *fname,
801               l_int32      x,
802               l_int32      y,
803               l_float32    scale)
804 {
805 PIX  *pixs, *pixd;
806 
807     PROCNAME("l_fileDisplay");
808 
809     if (scale == 0.0)
810         return 0;
811 
812     if (scale < 0.0)
813         return ERROR_INT("invalid scale factor", procName, 1);
814     if ((pixs = pixRead(fname)) == NULL)
815         return ERROR_INT("pixs not read", procName, 1);
816 
817     if (scale == 1.0) {
818         pixd = pixClone(pixs);
819     } else {
820         if (scale < 1.0 && pixGetDepth(pixs) == 1)
821             pixd = pixScaleToGray(pixs, scale);
822         else
823             pixd = pixScale(pixs, scale, scale);
824     }
825     pixDisplay(pixd, x, y);
826     pixDestroy(&pixs);
827     pixDestroy(&pixd);
828     return 0;
829 }
830 
831 
832 /*!
833  * \brief   pixDisplay()
834  *
835  * \param[in]    pix 1, 2, 4, 8, 16, 32 bpp
836  * \param[in]    x, y  location of display frame on the screen
837  * \return  0 if OK; 1 on error
838  *
839  * <pre>
840  * Notes:
841  *      (1) This is debugging code that displays an image on the screen.
842  *          It uses a static internal variable to number the output files
843  *          written by a single process.  Behavior with a shared library
844  *          may be unpredictable.
845  *      (2) It uses these programs to display the image:
846  *             On Unix: xzgv, xli or xv
847  *             On Windows: i_view
848  *          The display program must be on your $PATH variable.  It is
849  *          chosen by setting the global var_DISPLAY_PROG, using
850  *          l_chooseDisplayProg().  Default on Unix is xzgv.
851  *      (3) Images with dimensions larger than MAX_DISPLAY_WIDTH or
852  *          MAX_DISPLAY_HEIGHT are downscaled to fit those constraints.
853  *          This is particularly important for displaying 1 bpp images
854  *          with xv, because xv automatically downscales large images
855  *          by subsampling, which looks poor.  For 1 bpp, we use
856  *          scale-to-gray to get decent-looking anti-aliased images.
857  *          In all cases, we write a temporary file to /tmp/lept/disp,
858  *          that is read by the display program.
859  *      (4) The temporary file is written as png if, after initial
860  *          processing for special cases, any of these obtain:
861  *            * pix dimensions are smaller than some thresholds
862  *            * pix depth is less than 8 bpp
863  *            * pix is colormapped
864  *      (5) For spp == 4, we call pixDisplayLayersRGBA() to show 3
865  *          versions of the image: the image with a fully opaque
866  *          alpha, the alpha, and the image as it would appear with
867  *          a white background.
868  * </pre>
869  */
870 l_int32
pixDisplay(PIX * pixs,l_int32 x,l_int32 y)871 pixDisplay(PIX     *pixs,
872            l_int32  x,
873            l_int32  y)
874 {
875     return pixDisplayWithTitle(pixs, x, y, NULL, 1);
876 }
877 
878 
879 /*!
880  * \brief   pixDisplayWithTitle()
881  *
882  * \param[in]    pix 1, 2, 4, 8, 16, 32 bpp
883  * \param[in]    x, y  location of display frame
884  * \param[in]    title [optional] on frame; can be NULL;
885  * \param[in]    dispflag 1 to write, else disabled
886  * \return  0 if OK; 1 on error
887  *
888  * <pre>
889  * Notes:
890  *      (1) See notes for pixDisplay().
891  *      (2) This displays the image if dispflag == 1; otherwise it punts.
892  * </pre>
893  */
894 l_int32
pixDisplayWithTitle(PIX * pixs,l_int32 x,l_int32 y,const char * title,l_int32 dispflag)895 pixDisplayWithTitle(PIX         *pixs,
896                     l_int32      x,
897                     l_int32      y,
898                     const char  *title,
899                     l_int32      dispflag)
900 {
901 char           *tempname;
902 char            buffer[L_BUFSIZE];
903 static l_int32  index = 0;  /* caution: not .so or thread safe */
904 l_int32         w, h, d, spp, maxheight, opaque, threeviews, ignore;
905 l_float32       ratw, rath, ratmin;
906 PIX            *pix0, *pix1, *pix2;
907 PIXCMAP        *cmap;
908 #ifndef _WIN32
909 l_int32         wt, ht;
910 #else
911 char           *pathname;
912 char            fullpath[_MAX_PATH];
913 #endif  /* _WIN32 */
914 
915     PROCNAME("pixDisplayWithTitle");
916 
917     if (!LeptDebugOK) {
918         L_INFO("displaying files is disabled\n", procName);
919         return 0;
920     }
921 
922     if (dispflag != 1) return 0;
923     if (!pixs)
924         return ERROR_INT("pixs not defined", procName, 1);
925     if (var_DISPLAY_PROG != L_DISPLAY_WITH_XZGV &&
926         var_DISPLAY_PROG != L_DISPLAY_WITH_XLI &&
927         var_DISPLAY_PROG != L_DISPLAY_WITH_XV &&
928         var_DISPLAY_PROG != L_DISPLAY_WITH_IV &&
929         var_DISPLAY_PROG != L_DISPLAY_WITH_OPEN) {
930         return ERROR_INT("no program chosen for display", procName, 1);
931     }
932 
933         /* Display with three views if either spp = 4 or if colormapped
934          * and the alpha component is not fully opaque */
935     opaque = TRUE;
936     if ((cmap = pixGetColormap(pixs)) != NULL)
937         pixcmapIsOpaque(cmap, &opaque);
938     spp = pixGetSpp(pixs);
939     threeviews = (spp == 4 || !opaque) ? TRUE : FALSE;
940 
941         /* If colormapped and not opaque, remove the colormap to RGBA */
942     if (!opaque)
943         pix0 = pixRemoveColormap(pixs, REMOVE_CMAP_WITH_ALPHA);
944     else
945         pix0 = pixClone(pixs);
946 
947         /* Scale if necessary; this will also remove a colormap */
948     pixGetDimensions(pix0, &w, &h, &d);
949     maxheight = (threeviews) ? MAX_DISPLAY_HEIGHT / 3 : MAX_DISPLAY_HEIGHT;
950     if (w <= MAX_DISPLAY_WIDTH && h <= maxheight) {
951         if (d == 16)  /* take MSB */
952             pix1 = pixConvert16To8(pix0, 1);
953         else
954             pix1 = pixClone(pix0);
955     } else {
956         ratw = (l_float32)MAX_DISPLAY_WIDTH / (l_float32)w;
957         rath = (l_float32)maxheight / (l_float32)h;
958         ratmin = L_MIN(ratw, rath);
959         if (ratmin < 0.125 && d == 1)
960             pix1 = pixScaleToGray8(pix0);
961         else if (ratmin < 0.25 && d == 1)
962             pix1 = pixScaleToGray4(pix0);
963         else if (ratmin < 0.33 && d == 1)
964             pix1 = pixScaleToGray3(pix0);
965         else if (ratmin < 0.5 && d == 1)
966             pix1 = pixScaleToGray2(pix0);
967         else
968             pix1 = pixScale(pix0, ratmin, ratmin);
969     }
970     pixDestroy(&pix0);
971     if (!pix1)
972         return ERROR_INT("pix1 not made", procName, 1);
973 
974         /* Generate the three views if required */
975     if (threeviews)
976         pix2 = pixDisplayLayersRGBA(pix1, 0xffffff00, 0);
977     else
978         pix2 = pixClone(pix1);
979 
980     if (index == 0) {  /* erase any existing images */
981         lept_rmdir("lept/disp");
982         lept_mkdir("lept/disp");
983     }
984 
985     index++;
986     if (pixGetDepth(pix2) < 8 || pixGetColormap(pix2) ||
987         (w < MAX_SIZE_FOR_PNG && h < MAX_SIZE_FOR_PNG)) {
988         snprintf(buffer, L_BUFSIZE, "/tmp/lept/disp/write.%03d.png", index);
989         pixWrite(buffer, pix2, IFF_PNG);
990     } else {
991         snprintf(buffer, L_BUFSIZE, "/tmp/lept/disp/write.%03d.jpg", index);
992         pixWrite(buffer, pix2, IFF_JFIF_JPEG);
993     }
994     tempname = genPathname(buffer, NULL);
995 
996 #ifndef _WIN32
997 
998         /* Unix */
999     if (var_DISPLAY_PROG == L_DISPLAY_WITH_XZGV) {
1000             /* no way to display title */
1001         pixGetDimensions(pix2, &wt, &ht, NULL);
1002         snprintf(buffer, L_BUFSIZE,
1003                  "xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10,
1004                  x, y, tempname);
1005     } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XLI) {
1006         if (title) {
1007             snprintf(buffer, L_BUFSIZE,
1008                "xli -dispgamma 1.0 -quiet -geometry +%d+%d -title \"%s\" %s &",
1009                x, y, title, tempname);
1010         } else {
1011             snprintf(buffer, L_BUFSIZE,
1012                "xli -dispgamma 1.0 -quiet -geometry +%d+%d %s &",
1013                x, y, tempname);
1014         }
1015     } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XV) {
1016         if (title) {
1017             snprintf(buffer, L_BUFSIZE,
1018                      "xv -quit -geometry +%d+%d -name \"%s\" %s &",
1019                      x, y, title, tempname);
1020         } else {
1021             snprintf(buffer, L_BUFSIZE,
1022                      "xv -quit -geometry +%d+%d %s &", x, y, tempname);
1023         }
1024     } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_OPEN) {
1025         snprintf(buffer, L_BUFSIZE, "open %s &", tempname);
1026     }
1027 #ifndef OS_IOS /* iOS 11 does not support system() */
1028     ignore = system(buffer);
1029 #endif /* !OS_IOS */
1030 
1031 #else  /* _WIN32 */
1032 
1033         /* Windows: L_DISPLAY_WITH_IV */
1034     pathname = genPathname(tempname, NULL);
1035     _fullpath(fullpath, pathname, sizeof(fullpath));
1036     if (title) {
1037         snprintf(buffer, L_BUFSIZE,
1038                  "i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"",
1039                  fullpath, x, y, title);
1040     } else {
1041         snprintf(buffer, L_BUFSIZE, "i_view32.exe \"%s\" /pos=(%d,%d)",
1042                  fullpath, x, y);
1043     }
1044     ignore = system(buffer);
1045     LEPT_FREE(pathname);
1046 
1047 #endif  /* _WIN32 */
1048 
1049     pixDestroy(&pix1);
1050     pixDestroy(&pix2);
1051     LEPT_FREE(tempname);
1052     return 0;
1053 }
1054 
1055 
1056 /*!
1057  * \brief   pixSaveTiled()
1058  *
1059  * \param[in]    pixs 1, 2, 4, 8, 32 bpp
1060  * \param[in]    pixa the pix are accumulated here
1061  * \param[in]    scalefactor 0.0 to disable; otherwise this is a scale factor
1062  * \param[in]    newrow 0 if placed on the same row as previous; 1 otherwise
1063  * \param[in]    space horizontal and vertical spacing, in pixels
1064  * \param[in]    dp depth of pixa; 8 or 32 bpp; only used on first call
1065  * \return  0 if OK, 1 on error.
1066  */
1067 l_int32
pixSaveTiled(PIX * pixs,PIXA * pixa,l_float32 scalefactor,l_int32 newrow,l_int32 space,l_int32 dp)1068 pixSaveTiled(PIX       *pixs,
1069              PIXA      *pixa,
1070              l_float32  scalefactor,
1071              l_int32    newrow,
1072              l_int32    space,
1073              l_int32    dp)
1074 {
1075         /* Save without an outline */
1076     return pixSaveTiledOutline(pixs, pixa, scalefactor, newrow, space, 0, dp);
1077 }
1078 
1079 
1080 /*!
1081  * \brief   pixSaveTiledOutline()
1082  *
1083  * \param[in]    pixs 1, 2, 4, 8, 32 bpp
1084  * \param[in]    pixa the pix are accumulated here
1085  * \param[in]    scalefactor 0.0 to disable; otherwise this is a scale factor
1086  * \param[in]    newrow 0 if placed on the same row as previous; 1 otherwise
1087  * \param[in]    space horizontal and vertical spacing, in pixels
1088  * \param[in]    linewidth width of added outline for image; 0 for no outline
1089  * \param[in]    dp depth of pixa; 8 or 32 bpp; only used on first call
1090  * \return  0 if OK, 1 on error.
1091  *
1092  * <pre>
1093  * Notes:
1094  *      (1) Before calling this function for the first time, use
1095  *          pixaCreate() to make the %pixa that will accumulate the pix.
1096  *          This is passed in each time pixSaveTiled() is called.
1097  *      (2) %scalefactor scales the input image.  After scaling and
1098  *          possible depth conversion, the image is saved in the input
1099  *          pixa, along with a box that specifies the location to
1100  *          place it when tiled later.  Disable saving the pix by
1101  *          setting %scalefactor == 0.0.
1102  *      (3) %newrow and %space specify the location of the new pix
1103  *          with respect to the last one(s) that were entered.
1104  *      (4) %dp specifies the depth at which all pix are saved.  It can
1105  *          be only 8 or 32 bpp.  Any colormap is removed.  This is only
1106  *          used at the first invocation.
1107  *      (5) This function uses two variables from call to call.
1108  *          If they were static, the function would not be .so or thread
1109  *          safe, and furthermore, there would be interference with two or
1110  *          more pixa accumulating images at a time.  Consequently,
1111  *          we use the first pix in the pixa to store and obtain both
1112  *          the depth and the current position of the bottom (one pixel
1113  *          below the lowest image raster line when laid out using
1114  *          the boxa).  The bottom variable is stored in the input format
1115  *          field, which is the only field available for storing an int.
1116  * </pre>
1117  */
1118 l_int32
pixSaveTiledOutline(PIX * pixs,PIXA * pixa,l_float32 scalefactor,l_int32 newrow,l_int32 space,l_int32 linewidth,l_int32 dp)1119 pixSaveTiledOutline(PIX       *pixs,
1120                     PIXA      *pixa,
1121                     l_float32  scalefactor,
1122                     l_int32    newrow,
1123                     l_int32    space,
1124                     l_int32    linewidth,
1125                     l_int32    dp)
1126 {
1127 l_int32  n, top, left, bx, by, bw, w, h, depth, bottom;
1128 BOX     *box;
1129 PIX     *pix1, *pix2, *pix3, *pix4;
1130 
1131     PROCNAME("pixSaveTiledOutline");
1132 
1133     if (scalefactor == 0.0) return 0;
1134 
1135     if (!pixs)
1136         return ERROR_INT("pixs not defined", procName, 1);
1137     if (!pixa)
1138         return ERROR_INT("pixa not defined", procName, 1);
1139 
1140     n = pixaGetCount(pixa);
1141     if (n == 0) {
1142         bottom = 0;
1143         if (dp != 8 && dp != 32) {
1144             L_WARNING("dp not 8 or 32 bpp; using 32\n", procName);
1145             depth = 32;
1146         } else {
1147             depth = dp;
1148         }
1149     } else {  /* extract the depth and bottom params from the first pix */
1150         pix1 = pixaGetPix(pixa, 0, L_CLONE);
1151         depth = pixGetDepth(pix1);
1152         bottom = pixGetInputFormat(pix1);  /* not typical usage! */
1153         pixDestroy(&pix1);
1154     }
1155 
1156         /* Remove colormap if it exists; otherwise a copy.  This
1157          * guarantees that pix4 is not a clone of pixs. */
1158     pix1 = pixRemoveColormapGeneral(pixs, REMOVE_CMAP_BASED_ON_SRC, L_COPY);
1159 
1160         /* Scale and convert to output depth */
1161     if (scalefactor == 1.0) {
1162         pix2 = pixClone(pix1);
1163     } else if (scalefactor > 1.0) {
1164         pix2 = pixScale(pix1, scalefactor, scalefactor);
1165     } else {  /* scalefactor < 1.0) */
1166         if (pixGetDepth(pix1) == 1)
1167             pix2 = pixScaleToGray(pix1, scalefactor);
1168         else
1169             pix2 = pixScale(pix1, scalefactor, scalefactor);
1170     }
1171     pixDestroy(&pix1);
1172     if (depth == 8)
1173         pix3 = pixConvertTo8(pix2, 0);
1174     else
1175         pix3 = pixConvertTo32(pix2);
1176     pixDestroy(&pix2);
1177 
1178         /* Add black outline */
1179     if (linewidth > 0)
1180         pix4 = pixAddBorder(pix3, linewidth, 0);
1181     else
1182         pix4 = pixClone(pix3);
1183     pixDestroy(&pix3);
1184 
1185         /* Find position of current pix (UL corner plus size) */
1186     if (n == 0) {
1187         top = 0;
1188         left = 0;
1189     } else if (newrow == 1) {
1190         top = bottom + space;
1191         left = 0;
1192     } else {  /* n > 0 */
1193         pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL);
1194         top = by;
1195         left = bx + bw + space;
1196     }
1197 
1198     pixGetDimensions(pix4, &w, &h, NULL);
1199     bottom = L_MAX(bottom, top + h);
1200     box = boxCreate(left, top, w, h);
1201     pixaAddPix(pixa, pix4, L_INSERT);
1202     pixaAddBox(pixa, box, L_INSERT);
1203 
1204         /* Save the new bottom value */
1205     pix1 = pixaGetPix(pixa, 0, L_CLONE);
1206     pixSetInputFormat(pix1, bottom);  /* not typical usage! */
1207     pixDestroy(&pix1);
1208     return 0;
1209 }
1210 
1211 
1212 /*!
1213  * \brief   pixSaveTiledWithText()
1214  *
1215  * \param[in]    pixs 1, 2, 4, 8, 32 bpp
1216  * \param[in]    pixa the pix are accumulated here; as 32 bpp
1217  * \param[in]    outwidth in pixels; use 0 to disable entirely
1218  * \param[in]    newrow 1 to start a new row; 0 to go on same row as previous
1219  * \param[in]    space horizontal and vertical spacing, in pixels
1220  * \param[in]    linewidth width of added outline for image; 0 for no outline
1221  * \param[in]    bmf [optional] font struct
1222  * \param[in]    textstr [optional] text string to be added
1223  * \param[in]    val color to set the text
1224  * \param[in]    location L_ADD_ABOVE, L_ADD_AT_TOP, L_ADD_AT_BOT, L_ADD_BELOW
1225  * \return  0 if OK, 1 on error.
1226  *
1227  * <pre>
1228  * Notes:
1229  *      (1) Before calling this function for the first time, use
1230  *          pixaCreate() to make the %pixa that will accumulate the pix.
1231  *          This is passed in each time pixSaveTiled() is called.
1232  *      (2) %outwidth is the scaled width.  After scaling, the image is
1233  *          saved in the input pixa, along with a box that specifies
1234  *          the location to place it when tiled later.  Disable saving
1235  *          the pix by setting %outwidth == 0.
1236  *      (3) %newrow and %space specify the location of the new pix
1237  *          with respect to the last one(s) that were entered.
1238  *      (4) All pix are saved as 32 bpp RGB.
1239  *      (5) If both %bmf and %textstr are defined, this generates a pix
1240  *          with the additional text; otherwise, no text is written.
1241  *      (6) The text is written before scaling, so it is properly
1242  *          antialiased in the scaled pix.  However, if the pix on
1243  *          different calls have different widths, the size of the
1244  *          text will vary.
1245  *      (7) See pixSaveTiledOutline() for other implementation details.
1246  * </pre>
1247  */
1248 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)1249 pixSaveTiledWithText(PIX         *pixs,
1250                      PIXA        *pixa,
1251                      l_int32      outwidth,
1252                      l_int32      newrow,
1253                      l_int32      space,
1254                      l_int32      linewidth,
1255                      L_BMF       *bmf,
1256                      const char  *textstr,
1257                      l_uint32     val,
1258                      l_int32      location)
1259 {
1260 PIX  *pix1, *pix2, *pix3, *pix4;
1261 
1262     PROCNAME("pixSaveTiledWithText");
1263 
1264     if (outwidth == 0) return 0;
1265 
1266     if (!pixs)
1267         return ERROR_INT("pixs not defined", procName, 1);
1268     if (!pixa)
1269         return ERROR_INT("pixa not defined", procName, 1);
1270 
1271     pix1 = pixConvertTo32(pixs);
1272     if (linewidth > 0)
1273         pix2 = pixAddBorder(pix1, linewidth, 0);
1274     else
1275         pix2 = pixClone(pix1);
1276     if (bmf && textstr)
1277         pix3 = pixAddSingleTextblock(pix2, bmf, textstr, val, location, NULL);
1278     else
1279         pix3 = pixClone(pix2);
1280     pix4 = pixScaleToSize(pix3, outwidth, 0);
1281     pixSaveTiled(pix4, pixa, 1.0, newrow, space, 32);
1282     pixDestroy(&pix1);
1283     pixDestroy(&pix2);
1284     pixDestroy(&pix3);
1285     pixDestroy(&pix4);
1286     return 0;
1287 }
1288 
1289 
1290 void
l_chooseDisplayProg(l_int32 selection)1291 l_chooseDisplayProg(l_int32  selection)
1292 {
1293     if (selection == L_DISPLAY_WITH_XLI ||
1294         selection == L_DISPLAY_WITH_XZGV ||
1295         selection == L_DISPLAY_WITH_XV ||
1296         selection == L_DISPLAY_WITH_IV ||
1297         selection == L_DISPLAY_WITH_OPEN) {
1298         var_DISPLAY_PROG = selection;
1299     } else {
1300         L_ERROR("invalid display program\n", "l_chooseDisplayProg");
1301     }
1302     return;
1303 }
1304 
1305 
1306 /*---------------------------------------------------------------------*
1307  *                Deprecated pix output for debugging                  *
1308  *---------------------------------------------------------------------*/
1309 /*!
1310  * \brief   pixDisplayWrite()
1311  *
1312  * \param[in]    pix 1, 2, 4, 8, 16, 32 bpp
1313  * \param[in]    reduction -1 to reset/erase; 0 to disable;
1314  *                         otherwise this is a reduction factor
1315  * \return  0 if OK; 1 on error
1316  *
1317  * <pre>
1318  * Notes:
1319  *      (0) Deprecated.
1320  *      (1) This is a simple interface for writing a set of files.
1321  *      (2) This uses jpeg output for pix that are 32 bpp or 8 bpp
1322  *          without a colormap; otherwise, it uses png.
1323  *      (3) To erase any previously written files in the output directory:
1324  *             pixDisplayWrite(NULL, -1);
1325  *      (4) If reduction > 1 and depth == 1, this does a scale-to-gray
1326  *          reduction.
1327  *      (5) This function uses a static internal variable to number
1328  *          output files written by a single process.  Behavior
1329  *          with a shared library may be unpredictable.
1330  *      (6) For 16 bpp, this displays the full dynamic range with log scale.
1331  *          Alternative image transforms to generate 8 bpp pix are:
1332  *             pix8 = pixMaxDynamicRange(pixt, L_LINEAR_SCALE);
1333  *             pix8 = pixConvert16To8(pixt, 0);  // low order byte
1334  *             pix8 = pixConvert16To8(pixt, 1);  // high order byte
1335  * </pre>
1336  */
1337 l_int32
pixDisplayWrite(PIX * pixs,l_int32 reduction)1338 pixDisplayWrite(PIX     *pixs,
1339                 l_int32  reduction)
1340 {
1341 char            buf[L_BUFSIZE];
1342 char           *fname;
1343 l_float32       scale;
1344 PIX            *pix1, *pix2;
1345 static l_int32  index = 0;  /* caution: not .so or thread safe */
1346 
1347     PROCNAME("pixDisplayWrite");
1348 
1349     if (reduction == 0) return 0;
1350     if (reduction < 0) {  /* initialize */
1351         lept_rmdir("lept/display");
1352         index = 0;
1353         return 0;
1354     }
1355     if (!pixs)
1356         return ERROR_INT("pixs not defined", procName, 1);
1357     if (index == 0)
1358         lept_mkdir("lept/display");
1359     index++;
1360 
1361     if (reduction == 1) {
1362         pix1 = pixClone(pixs);
1363     } else {
1364         scale = 1. / (l_float32)reduction;
1365         if (pixGetDepth(pixs) == 1)
1366             pix1 = pixScaleToGray(pixs, scale);
1367         else
1368             pix1 = pixScale(pixs, scale, scale);
1369     }
1370 
1371     if (pixGetDepth(pix1) == 16) {
1372         pix2 = pixMaxDynamicRange(pix1, L_LOG_SCALE);
1373         snprintf(buf, L_BUFSIZE, "file.%03d.png", index);
1374         fname = pathJoin("/tmp/lept/display", buf);
1375         pixWrite(fname, pix2, IFF_PNG);
1376         pixDestroy(&pix2);
1377     } else if (pixGetDepth(pix1) < 8 || pixGetColormap(pix1)) {
1378         snprintf(buf, L_BUFSIZE, "file.%03d.png", index);
1379         fname = pathJoin("/tmp/lept/display", buf);
1380         pixWrite(fname, pix1, IFF_PNG);
1381     } else {
1382         snprintf(buf, L_BUFSIZE, "file.%03d.jpg", index);
1383         fname = pathJoin("/tmp/lept/display", buf);
1384         pixWrite(fname, pix1, IFF_JFIF_JPEG);
1385     }
1386     LEPT_FREE(fname);
1387     pixDestroy(&pix1);
1388     return 0;
1389 }
1390