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