• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*====================================================================*
2  -  Copyright (C) 2001 Leptonica.  All rights reserved.
3  -  This software is distributed in the hope that it will be
4  -  useful, but with NO WARRANTY OF ANY KIND.
5  -  No author or distributor accepts responsibility to anyone for the
6  -  consequences of using this software, or for whether it serves any
7  -  particular purpose or works at all, unless he or she says so in
8  -  writing.  Everyone is granted permission to copy, modify and
9  -  redistribute this source code, for commercial or non-commercial
10  -  purposes, with the following restrictions: (1) the origin of this
11  -  source code must not be misrepresented; (2) modified versions must
12  -  be plainly marked as such; and (3) this notice may not be removed
13  -  or altered from any source or modified source distribution.
14  *====================================================================*/
15 
16 
17 /*
18  * writefile.c
19  *
20  *     High-level procedures for writing images to file:
21  *        l_int32     pixaWriteFiles()
22  *        l_int32     pixWrite()
23  *        l_int32     pixWriteStream()
24  *        l_int32     pixWriteImpliedFormat()
25  *
26  *     Selection of output format if default is requested
27  *        l_int32     pixChooseOutputFormat()
28  *        l_int32     getImpliedFileFormat()
29  *        const char *getFormatExtension()
30  *
31  *     Write to memory
32  *        l_int32     pixWriteMem()
33  *
34  *     Image display for debugging
35  *        l_int32     pixDisplay()
36  *        l_int32     pixDisplayWithTitle()
37  *        l_int32     pixDisplayWrite()
38  *        l_int32     pixDisplayWriteFormat()
39  *        l_int32     pixSaveTiled()
40  */
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include "allheaders.h"
46 
47     /* MS VC++ can't handle array initialization with static consts ! */
48 #define L_BUF_SIZE      512
49 
50     /* For display using xv */
51 static const l_int32  MAX_DISPLAY_WIDTH = 1000;
52 static const l_int32  MAX_DISPLAY_HEIGHT = 800;
53 static const l_int32  MAX_SIZE_FOR_PNG = 200;
54 
55     /* PostScript output for printing */
56 static const l_float32  DEFAULT_SCALING = 1.0;
57 
58     /* Global array of image file format extension names.
59      * This is in 1-1 corrspondence with format enum in imageio.h. */
60 static const l_int32  NUM_EXTENSIONS = 14;
61 const char *ImageFileFormatExtensions[] = {"unknown",
62                                            "bmp",
63                                            "jpg",
64                                            "png",
65                                            "tif",
66                                            "tif",
67                                            "tif",
68                                            "tif",
69                                            "tif",
70                                            "tif",
71                                            "tif",
72                                            "pnm",
73                                            "ps",
74                                            "gif"};
75 
76     /* Local map of image file name extension to output format */
77 struct ExtensionMap
78 {
79     char     extension[8];
80     l_int32  format;
81 };
82 static const struct ExtensionMap extension_map[] =
83                             { { ".bmp",  IFF_BMP       },
84                               { ".jpg",  IFF_JFIF_JPEG },
85                               { ".jpeg", IFF_JFIF_JPEG },
86                               { ".png",  IFF_PNG       },
87                               { ".tif",  IFF_TIFF      },
88                               { ".tiff", IFF_TIFF      },
89                               { ".pnm",  IFF_PNM       },
90                               { ".gif",  IFF_GIF       },
91                               { ".ps",   IFF_PS        } };
92 
93 
94 /*---------------------------------------------------------------------*
95  *           Top-level procedures for writing images to file           *
96  *---------------------------------------------------------------------*/
97 /*!
98  *  pixaWriteFiles()
99  *
100  *      Input:  rootname
101  *              pixa
102  *              format  (defined in imageio.h)
103  *      Return: 0 if OK; 1 on error
104  */
105 l_int32
pixaWriteFiles(const char * rootname,PIXA * pixa,l_int32 format)106 pixaWriteFiles(const char  *rootname,
107                PIXA        *pixa,
108                l_int32      format)
109 {
110 char     bigbuf[L_BUF_SIZE];
111 l_int32  i, n;
112 PIX     *pix;
113 
114     PROCNAME("pixaWriteFiles");
115 
116     if (!rootname)
117         return ERROR_INT("rootname not defined", procName, 1);
118     if (!pixa)
119         return ERROR_INT("pixa not defined", procName, 1);
120 
121     n = pixaGetCount(pixa);
122     for (i = 0; i < n; i++) {
123         snprintf(bigbuf, L_BUF_SIZE, "%s%03d.%s", rootname, i,
124                  ImageFileFormatExtensions[format]);
125         pix = pixaGetPix(pixa, i, L_CLONE);
126         pixWrite(bigbuf, pix, format);
127         pixDestroy(&pix);
128     }
129 
130     return 0;
131 }
132 
133 
134 /*!
135  *  pixWrite()
136  *
137  *      Input:  filename
138  *              pix
139  *              format  (defined in imageio.h)
140  *      Return: 0 if OK; 1 on error
141  *
142  *  Notes:
143  *      (1) Open for write using binary mode (with the "b" flag)
144  *          to avoid having Windows automatically translate the NL
145  *          into CRLF, which corrupts image files.  On non-windows
146  *          systems this flag should be ignored, per ISO C90.
147  *          Thanks to Dave Bryan for pointing this out.
148  */
149 l_int32
pixWrite(const char * filename,PIX * pix,l_int32 format)150 pixWrite(const char  *filename,
151          PIX         *pix,
152          l_int32      format)
153 {
154 FILE  *fp;
155 
156     PROCNAME("pixWrite");
157 
158     if (!pix)
159         return ERROR_INT("pix not defined", procName, 1);
160     if (!filename)
161         return ERROR_INT("filename not defined", procName, 1);
162 
163     if ((fp = fopen(filename, "wb+")) == NULL)
164         return ERROR_INT("stream not opened", procName, 1);
165 
166     if (pixWriteStream(fp, pix, format)) {
167         fclose(fp);
168         return ERROR_INT("pix not written to stream", procName, 1);
169     }
170 
171     fclose(fp);
172     return 0;
173 }
174 
175 
176 /*!
177  *  pixWriteStream()
178  *
179  *      Input:  stream
180  *              pix
181  *              format
182  *      Return: 0 if OK; 1 on error.
183  */
184 l_int32
pixWriteStream(FILE * fp,PIX * pix,l_int32 format)185 pixWriteStream(FILE    *fp,
186                PIX     *pix,
187                l_int32  format)
188 {
189     PROCNAME("pixWriteStream");
190 
191     if (!fp)
192         return ERROR_INT("stream not defined", procName, 1);
193     if (!pix)
194         return ERROR_INT("pix not defined", procName, 1);
195 
196     if (format == IFF_DEFAULT)
197         format = pixChooseOutputFormat(pix);
198 
199     switch(format)
200     {
201     case IFF_BMP:
202         pixWriteStreamBmp(fp, pix);
203         break;
204 
205     case IFF_JFIF_JPEG:   /* default quality; baseline sequential */
206         return pixWriteStreamJpeg(fp, pix, 75, 0);
207         break;
208 
209     case IFF_PNG:   /* no gamma value stored */
210         return pixWriteStreamPng(fp, pix, 0.0);
211         break;
212 
213     case IFF_TIFF:           /* uncompressed */
214     case IFF_TIFF_PACKBITS:  /* compressed, binary only */
215     case IFF_TIFF_RLE:       /* compressed, binary only */
216     case IFF_TIFF_G3:        /* compressed, binary only */
217     case IFF_TIFF_G4:        /* compressed, binary only */
218     case IFF_TIFF_LZW:       /* compressed, all depths */
219     case IFF_TIFF_ZIP:       /* compressed, all depths */
220         return pixWriteStreamTiff(fp, pix, format);
221         break;
222 
223     case IFF_PNM:
224         return pixWriteStreamPnm(fp, pix);
225         break;
226 
227     case IFF_GIF:
228         return pixWriteStreamGif(fp, pix);
229         break;
230 
231     case IFF_PS:
232         return pixWriteStreamPS(fp, pix, NULL, 0, DEFAULT_SCALING);
233         break;
234 
235     default:
236         return ERROR_INT("unknown format", procName, 1);
237         break;
238     }
239 
240     return 0;
241 }
242 
243 
244 /*!
245  *  pixWriteImpliedFormat()
246  *
247  *      Input:  filename
248  *              pix
249  *              quality (iff JPEG; 1 - 100, 0 for default)
250  *              progressive (iff JPEG; 0 for baseline seq., 1 for progressive)
251  *      Return: 0 if OK; 1 on error
252  *
253  *  Notes:
254  *      (1) This determines the output format from the filename extension.
255  *      (2) The last two args are ignored except for requests for jpeg files.
256  *      (3) The jpeg default quality is 75.
257  */
258 l_int32
pixWriteImpliedFormat(const char * filename,PIX * pix,l_int32 quality,l_int32 progressive)259 pixWriteImpliedFormat(const char  *filename,
260                       PIX         *pix,
261                       l_int32      quality,
262                       l_int32      progressive)
263 {
264 l_int32  format;
265 
266     PROCNAME("pixWriteImpliedFormat");
267 
268     if (!filename)
269         return ERROR_INT ("filename not defined", procName, 1);
270     if (!pix)
271         return ERROR_INT ("pix not defined", procName, 1);
272 
273         /* Determine output format */
274     format = getImpliedFileFormat(filename);
275     if (format == IFF_UNKNOWN)
276         format = IFF_PNG;
277     else if (format == IFF_TIFF) {
278         if (pixGetDepth(pix) == 1)
279             format = IFF_TIFF_G4;
280         else
281             format = IFF_TIFF_LZW;
282     }
283 
284     if (format == IFF_JFIF_JPEG) {
285         quality = L_MIN(quality, 100);
286         quality = L_MAX(quality, 0);
287         if (progressive != 0 && progressive != 1) {
288             progressive = 0;
289             L_WARNING("invalid progressive; setting to baseline", procName);
290         }
291         if (quality == 0)
292             quality = 75;
293         pixWriteJpeg (filename, pix, quality, progressive);
294     }
295     else
296         pixWrite(filename, pix, format);
297 
298     return 0;
299 }
300 
301 
302 /*---------------------------------------------------------------------*
303  *          Selection of output format if default is requested         *
304  *---------------------------------------------------------------------*/
305 /*!
306  *  pixChooseOutputFormat()
307  *
308  *      Input:  pix
309  *      Return: output format, or 0 on error
310  *
311  *  Notes:
312  *      (1) This should only be called if the requested format is IFF_DEFAULT.
313  *      (2) If the pix wasn't read from a file, its input format value
314  *          will be IFF_UNKNOWN, and in that case it is written out
315  *          in a compressed but lossless format.
316  */
317 l_int32
pixChooseOutputFormat(PIX * pix)318 pixChooseOutputFormat(PIX  *pix)
319 {
320 l_int32  d, format;
321 
322     PROCNAME("pixChooseOutputFormat");
323 
324     if (!pix)
325         return ERROR_INT("pix not defined", procName, 0);
326 
327     d = pixGetDepth(pix);
328     format = pixGetInputFormat(pix);
329     if (format == IFF_UNKNOWN) {  /* output lossless */
330         if (d == 1)
331             format = IFF_TIFF_G4;
332         else
333             format = IFF_PNG;
334     }
335 
336     return format;
337 }
338 
339 
340 /*!
341  *  getImpliedFileFormat()
342  *
343  *      Input:  filename
344  *      Return: output format, or IFF_UNKNOWN on error or invalid extension.
345  *
346  *  Notes:
347  *      (1) This determines the output file format from the extension
348  *          of the input filename.
349  */
350 l_int32
getImpliedFileFormat(const char * filename)351 getImpliedFileFormat(const char  *filename)
352 {
353 char    *extension;
354 int      i, numext;
355 l_int32  format = IFF_UNKNOWN;
356 
357     if (splitPathAtExtension (filename, NULL, &extension))
358         return IFF_UNKNOWN;
359 
360     numext = sizeof(extension_map) / sizeof(extension_map[0]);
361     for (i = 0; i < numext; i++) {
362         if (!strcmp(extension, extension_map[i].extension)) {
363             format = extension_map[i].format;
364             break;
365         }
366     }
367 
368     FREE(extension);
369     return format;
370 }
371 
372 
373 /*!
374  *  getFormatExtension()
375  *
376  *      Input:  format (integer)
377  *      Return: extension (string), or null if format is out of range
378  *
379  *  Notes:
380  *      (1) This string is NOT owned by the caller; it is just a pointer
381  *          to a global string.  Do not free it.
382  */
383 const char *
getFormatExtension(l_int32 format)384 getFormatExtension(l_int32  format)
385 {
386     PROCNAME("getFormatExtension");
387 
388     if (format < 0 || format >= NUM_EXTENSIONS)
389         return (const char *)ERROR_PTR("format out of bounds", procName, NULL);
390 
391     return ImageFileFormatExtensions[format];
392 }
393 
394 
395 /*---------------------------------------------------------------------*
396  *                            Write to memory                          *
397  *---------------------------------------------------------------------*/
398 /*!
399  *  pixWriteMem()
400  *
401  *      Input:  &data (<return> data of tiff compressed image)
402  *              &size (<return> size of returned data)
403  *              pix
404  *              format  (defined in imageio.h)
405  *      Return: 0 if OK, 1 on error
406  *
407  *  Notes:
408  *      (1) On windows, this will only write tiff and PostScript to memory.
409  *          For other formats, it requires open_memstream(3).
410  *      (2) PostScript output is uncompressed, in hex ascii.
411  *          Most printers support level 2 compression (tiff_g4 for 1 bpp,
412  *          jpeg for 8 and 32 bpp).
413  */
414 l_int32
pixWriteMem(l_uint8 ** pdata,size_t * psize,PIX * pix,l_int32 format)415 pixWriteMem(l_uint8  **pdata,
416             size_t    *psize,
417             PIX       *pix,
418             l_int32    format)
419 {
420 l_int32  ret;
421 
422     PROCNAME("pixWriteMem");
423 
424     if (!pdata)
425         return ERROR_INT("&data not defined", procName, 1 );
426     if (!psize)
427         return ERROR_INT("&size not defined", procName, 1 );
428     if (!pix)
429         return ERROR_INT("&pix not defined", procName, 1 );
430 
431     if (format == IFF_DEFAULT)
432         format = pixChooseOutputFormat(pix);
433 
434     switch(format)
435     {
436     case IFF_BMP:
437         ret = pixWriteMemBmp(pdata, psize, pix);
438         break;
439 
440     case IFF_JFIF_JPEG:   /* default quality; baseline sequential */
441         ret = pixWriteMemJpeg(pdata, psize, pix, 75, 0);
442         break;
443 
444     case IFF_PNG:   /* no gamma value stored */
445         ret = pixWriteMemPng(pdata, psize, pix, 0.0);
446         break;
447 
448     case IFF_TIFF:           /* uncompressed */
449     case IFF_TIFF_PACKBITS:  /* compressed, binary only */
450     case IFF_TIFF_RLE:       /* compressed, binary only */
451     case IFF_TIFF_G3:        /* compressed, binary only */
452     case IFF_TIFF_G4:        /* compressed, binary only */
453     case IFF_TIFF_LZW:       /* compressed, all depths */
454     case IFF_TIFF_ZIP:       /* compressed, all depths */
455         ret = pixWriteMemTiff(pdata, psize, pix, format);
456         break;
457 
458     case IFF_PNM:
459         ret = pixWriteMemPnm(pdata, psize, pix);
460         break;
461 
462     case IFF_PS:
463         ret = pixWriteMemPS(pdata, psize, pix, NULL, 0, DEFAULT_SCALING);
464         break;
465 
466     default:
467         return ERROR_INT("unknown format", procName, 1);
468         break;
469     }
470 
471     return ret;
472 }
473 
474 
475 /*---------------------------------------------------------------------*
476  *                       Image display for debugging                   *
477  *---------------------------------------------------------------------*/
478 /*!
479  *  pixDisplay()
480  *
481  *      Input:  pix (1, 2, 4, 8, 16, 32 bpp)
482  *              x, y  (location of xv frame)
483  *      Return: 0 if OK; 1 on error
484  *
485  *  Notes:
486  *      (1) This uses xv to display.  It must be on your $PATH variable.
487  *      (2) Because xv reduces images to fit the screen, we do this
488  *          reduction in advance, and write it out to a temporary file
489  *          in the current directory with the name "junk_xv_display.*"
490  *      (3) This function uses a static internal variable to number
491  *          output files written by a single process.  Behavior
492  *          with a shared library may be unpredictable.
493  */
494 l_int32
pixDisplay(PIX * pixs,l_int32 x,l_int32 y)495 pixDisplay(PIX     *pixs,
496            l_int32  x,
497            l_int32  y)
498 {
499     return pixDisplayWithTitle(pixs, x, y, NULL, 1);
500 }
501 
502 
503 /*!
504  *  pixDisplayWithTitle()
505  *
506  *      Input:  pix (1, 2, 4, 8, 16, 32 bpp)
507  *              x, y  (location of xv frame)
508  *              title (<optional> on xv window; can be NULL);
509  *              dispflag (0 to disable; 1 to write)
510  *      Return: 0 if OK; 1 on error
511  *
512  *  Notes:
513  *      (1) See notes for pixDisplay().
514  *      (2) This displays the image if dispflag == 1.
515  */
516 l_int32
pixDisplayWithTitle(PIX * pixs,l_int32 x,l_int32 y,const char * title,l_int32 dispflag)517 pixDisplayWithTitle(PIX         *pixs,
518                     l_int32      x,
519                     l_int32      y,
520                     const char  *title,
521                     l_int32      dispflag)
522 {
523 char           *tempname;
524 char            buffer[L_BUF_SIZE];
525 static l_int32  index = 0;  /* caution: not .so or thread safe */
526 l_int32         w, h, d;
527 l_float32       ratw, rath, ratmin;
528 PIX            *pixt;
529 
530     PROCNAME("pixDisplayWithTitle");
531 
532     if (dispflag == 0) return 0;
533     if (!pixs)
534         return ERROR_INT("pixs not defined", procName, 1);
535 
536     pixGetDimensions(pixs, &w, &h, &d);
537     if (w <= MAX_DISPLAY_WIDTH && h <= MAX_DISPLAY_HEIGHT) {
538         if (d == 16)  /* take MSB */
539             pixt = pixConvert16To8(pixs, 1);
540         else
541             pixt = pixClone(pixs);
542     }
543     else {
544         ratw = (l_float32)MAX_DISPLAY_WIDTH / (l_float32)w;
545         rath = (l_float32)MAX_DISPLAY_HEIGHT / (l_float32)h;
546         ratmin = L_MIN(ratw, rath);
547         if (ratmin < 0.125 && d == 1)
548             pixt = pixScaleToGray8(pixs);
549         else if (ratmin < 0.25 && d == 1)
550             pixt = pixScaleToGray4(pixs);
551         else if (ratmin < 0.33 && d == 1)
552             pixt = pixScaleToGray3(pixs);
553         else if (ratmin < 0.5 && d == 1)
554             pixt = pixScaleToGray2(pixs);
555         else
556             pixt = pixScale(pixs, ratmin, ratmin);
557         if (!pixt)
558             return ERROR_INT("pixt not made", procName, 1);
559     }
560 
561     if (index == 0) {
562         snprintf(buffer, L_BUF_SIZE, "rm -f junk_xv_display.*");
563         system(buffer);
564     }
565 
566     index++;
567     if (pixGetDepth(pixt) < 8 ||
568         (w < MAX_SIZE_FOR_PNG && h < MAX_SIZE_FOR_PNG)) {
569         snprintf(buffer, L_BUF_SIZE, "junk_xv_display.%03d.png", index);
570         pixWrite(buffer, pixt, IFF_PNG);
571     }
572     else {
573         snprintf(buffer, L_BUF_SIZE, "junk_xv_display.%03d.jpg", index);
574         pixWrite(buffer, pixt, IFF_JFIF_JPEG);
575     }
576     tempname = stringNew(buffer);
577 
578     if (title)
579         snprintf(buffer, L_BUF_SIZE,
580                  "xv -quit -geometry +%d+%d -name \"%s\" %s &",
581                  x, y, title, tempname);
582     else
583         snprintf(buffer, L_BUF_SIZE,
584                  "xv -quit -geometry +%d+%d %s &", x, y, tempname);
585     system(buffer);
586 
587     pixDestroy(&pixt);
588     FREE(tempname);
589     return 0;
590 }
591 
592 
593 /*!
594  *  pixDisplayWrite()
595  *
596  *      Input:  pix (1, 2, 4, 8, 16, 32 bpp)
597  *              reduction (-1 to reset/erase; 0 to disable;
598  *                         otherwise this is a reduction factor)
599  *      Return: 0 if OK; 1 on error
600  *
601  *  Notes:
602  *      (1) This defaults to jpeg output for pix that are 32 bpp or
603  *          8 bpp without a colormap.  If you want to write all images
604  *          losslessly, use format == IFF_PNG in pixDisplayWriteFormat().
605  *      (2) See pixDisplayWriteFormat() for usage details.
606  */
607 l_int32
pixDisplayWrite(PIX * pixs,l_int32 reduction)608 pixDisplayWrite(PIX     *pixs,
609                 l_int32  reduction)
610 {
611     return pixDisplayWriteFormat(pixs, reduction, IFF_JFIF_JPEG);
612 }
613 
614 
615 /*!
616  *  pixDisplayWriteFormat()
617  *
618  *      Input:  pix (1, 2, 4, 8, 16, 32 bpp)
619  *              reduction (-1 to reset/erase; 0 to disable;
620  *                         otherwise this is a reduction factor)
621  *              format (IFF_PNG or IFF_JFIF_JPEG)
622  *      Return: 0 if OK; 1 on error
623  *
624  *  Notes:
625  *      (1) This writes files if reduction > 0.  These can be
626  *          displayed, ordered in a tiled representation, with,
627  *          for example, gthumb.
628  *      (2) All previously written files can be erased by calling with
629  *          reduction < 0; the value of pixs is ignored.
630  *      (3) If reduction > 1 and depth == 1, this does a scale-to-gray
631  *          reduction.
632  *      (4) This function uses a static internal variable to number
633  *          output files written by a single process.  Behavior
634  *          with a shared library may be unpredictable.
635  *      (5) Output file format is as follows:
636  *            format == IFF_JFIF_JPEG:
637  *                png if d < 8 or d == 16 or if the output pix
638  *                has a colormap.   Otherwise, output is jpg.
639  *            format == IFF_PNG:
640  *                png (lossless) on all images.
641  *      (6) For 16 bpp, the choice of full dynamic range with log scale
642  *          is the best for displaying these images.  Alternative outputs are
643  *             pix8 = pixMaxDynamicRange(pixt, L_LINEAR_SCALE);
644  *             pix8 = pixConvert16To8(pixt, 0);  // low order byte
645  *             pix8 = pixConvert16To8(pixt, 1);  // high order byte
646  */
647 l_int32
pixDisplayWriteFormat(PIX * pixs,l_int32 reduction,l_int32 format)648 pixDisplayWriteFormat(PIX     *pixs,
649                       l_int32  reduction,
650                       l_int32  format)
651 {
652 char            buffer[L_BUF_SIZE];
653 l_float32       scale;
654 PIX            *pixt, *pix8;
655 static l_int32  index = 0;  /* caution: not .so or thread safe */
656 
657     PROCNAME("pixDisplayWriteFormat");
658 
659     if (reduction == 0) return 0;
660 
661     if (reduction < 0) {
662         index = 0;  /* reset; this will cause erasure at next call to write */
663         return 0;
664     }
665 
666     if (format != IFF_JFIF_JPEG && format != IFF_PNG)
667         return ERROR_INT("invalid format", procName, 1);
668     if (!pixs)
669         return ERROR_INT("pixs not defined", procName, 1);
670 
671     if (index == 0) {
672         snprintf(buffer, L_BUF_SIZE,
673             "rm -f junk_write_display.*.png junk_write_display.*.jpg");
674         system(buffer);
675     }
676     index++;
677 
678     if (reduction == 1)
679         pixt = pixClone(pixs);
680     else {
681         scale = 1. / (l_float32)reduction;
682         if (pixGetDepth(pixs) == 1)
683             pixt = pixScaleToGray(pixs, scale);
684         else
685             pixt = pixScale(pixs, scale, scale);
686     }
687 
688     if (pixGetDepth(pixt) == 16) {
689         pix8 = pixMaxDynamicRange(pixt, L_LOG_SCALE);
690         snprintf(buffer, L_BUF_SIZE, "junk_write_display.%03d.png", index);
691         pixWrite(buffer, pix8, IFF_PNG);
692         pixDestroy(&pix8);
693     }
694     else if (pixGetDepth(pixt) < 8 || pixGetColormap(pixt)) {
695         snprintf(buffer, L_BUF_SIZE, "junk_write_display.%03d.png", index);
696         pixWrite(buffer, pixt, IFF_PNG);
697     }
698     else {
699         snprintf(buffer, L_BUF_SIZE, "junk_write_display.%03d.jpg", index);
700         pixWrite(buffer, pixt, format);
701     }
702     pixDestroy(&pixt);
703 
704     return 0;
705 }
706 
707 
708 /*!
709  *  pixSaveTiled()
710  *
711  *      Input:  pixs (1, 2, 4, 8, 32 bpp)
712  *              pixa (the pix are accumulated here)
713  *              reduction (0 to disable; otherwise this is a reduction factor)
714  *              newrow (0 if placed on the same row as previous; 1 otherwise)
715  *              space (horizontal and vertical spacing, in pixels)
716  *              dp (depth of pixa; 8 or 32 bpp; only used on first call)
717  *      Return: 0 if OK, 1 on error.
718  *
719  *  Notes:
720  *      (1) Before calling this function for the first time, use
721  *          pixaCreate() to make the @pixa that will accumulate the pix.
722  *          This is passed in each time pixSaveTiled() is called.
723  *      (2) @reduction is the integer reduction factor for the input
724  *          image.  After reduction and possible depth conversion,
725  *          the image is saved in the input pixa, along with a box
726  *          that specifies the location to place it when tiled later.
727  *          Disable saving the pix by setting reduction == 0.
728  *      (3) @newrow and @space specify the location of the new pix
729  *          with respect to the last one(s) that were entered.
730  *      (4) @dp specifies the depth at which all pix are saved.  It can
731  *          be only 8 or 32 bpp.  Any colormap is removed.  This is only
732  *          used at the first invocation.
733  *      (5) This function uses two variables from call to call.
734  *          If they were static, the function would not be .so or thread
735  *          safe, and furthermore, there would be interference with two or
736  *          more pixa accumulating images at a time.  Consequently,
737  *          we use the first pix in the pixa to store and obtain both
738  *          the depth and the current position of the bottom (one pixel
739  *          below the lowest image raster line when laid out using
740  *          the boxa).  The bottom variable is stored in the input format
741  *          field, which is the only field available for storing an int.
742  */
743 l_int32
pixSaveTiled(PIX * pixs,PIXA * pixa,l_int32 reduction,l_int32 newrow,l_int32 space,l_int32 dp)744 pixSaveTiled(PIX     *pixs,
745              PIXA    *pixa,
746              l_int32  reduction,
747              l_int32  newrow,
748              l_int32  space,
749              l_int32  dp)
750 {
751 l_int32         n, top, left, bx, by, bw, w, h, depth, bottom;
752 l_float32       scale;
753 BOX            *box;
754 PIX            *pix, *pixt1, *pixt2;
755 
756     PROCNAME("pixSaveTiled");
757 
758     if (reduction == 0) return 0;
759 
760     if (!pixs)
761         return ERROR_INT("pixs not defined", procName, 1);
762     if (!pixa)
763         return ERROR_INT("pixa not defined", procName, 1);
764 
765     n = pixaGetCount(pixa);
766     if (n == 0) {
767         bottom = 0;
768         if (dp != 8 && dp != 32) {
769             L_WARNING("dp not 8 or 32 bpp; using 32", procName);
770             depth = 32;
771         } else
772             depth = dp;
773     }
774     else {  /* extract the depth and bottom params from the first pix */
775         pix = pixaGetPix(pixa, 0, L_CLONE);
776         depth = pixGetDepth(pix);
777         bottom = pixGetInputFormat(pix);  /* not typical usage! */
778         pixDestroy(&pix);
779     }
780 
781     if (reduction == 1)
782         pixt1 = pixClone(pixs);
783     else {
784         scale = 1. / (l_float32)reduction;
785         if (pixGetDepth(pixs) == 1)
786             pixt1 = pixScaleToGray(pixs, scale);
787         else
788             pixt1 = pixScale(pixs, scale, scale);
789     }
790     if (depth == 8)
791         pixt2 = pixConvertTo8(pixt1, 0);
792     else
793         pixt2 = pixConvertTo32(pixt1);
794     pixDestroy(&pixt1);
795 
796         /* Find position of current pix (UL corner plus size) */
797     if (n == 0) {
798         top = 0;
799         left = 0;
800     }
801     else if (newrow == 1) {
802         top = bottom + space;
803         left = 0;
804     }
805     else if (n > 0) {
806         pixaGetBoxGeometry(pixa, n - 1, &bx, &by, &bw, NULL);
807         top = by;
808         left = bx + bw + space;
809     }
810 
811     pixGetDimensions(pixt2, &w, &h, NULL);
812     bottom = L_MAX(bottom, top + h);
813     box = boxCreate(left, top, w, h);
814     pixaAddPix(pixa, pixt2, L_INSERT);
815     pixaAddBox(pixa, box, L_INSERT);
816 
817         /* Save the new bottom value */
818     pix = pixaGetPix(pixa, 0, L_CLONE);
819     pixSetInputFormat(pix, bottom);  /* not typical usage! */
820     pixDestroy(&pix);
821 
822     return 0;
823 }
824 
825 
826