Coverage Report

Created: 2025-06-13 06:48

/src/leptonica/src/writefile.c
Line
Count
Source (jump to first uncovered line)
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     getFormatFromExtension()
48
 *        l_int32     pixGetAutoFormat()
49
 *        const char *getFormatExtension()
50
 *
51
 *     Write to memory
52
 *        l_int32     pixWriteMem()
53
 *
54
 *     Image display for debugging
55
 *        l_int32     l_fileDisplay()
56
 *        l_int32     pixDisplay()
57
 *        l_int32     pixDisplayWithTitle()
58
 *        PIX        *pixMakeColorSquare()
59
 *        void        l_chooseDisplayProg()
60
 *
61
 *     Change format for missing library
62
 *        void        changeFormatForMissingLib()
63
 *
64
 *     Nonfunctional stub of pix output for debugging
65
 *        l_int32     pixDisplayWrite()
66
 *
67
 *  Supported file formats:
68
 *  (1) Writing is supported without any external libraries:
69
 *          bmp
70
 *          pnm   (including pbm, pgm, etc)
71
 *          spix  (raw serialized)
72
 *  (2) Writing is supported with installation of external libraries:
73
 *          png
74
 *          jpg   (standard jfif version)
75
 *          tiff  (including most varieties of compression)
76
 *          gif
77
 *          webp
78
 *          jp2 (jpeg2000)
79
 *  (3) Writing is supported through special interfaces:
80
 *          ps (PostScript, in psio1.c, psio2.c):
81
 *              level 1 (uncompressed)
82
 *              level 2 (g4 and dct encoding: requires tiff, jpg)
83
 *              level 3 (g4, dct and flate encoding: requires tiff, jpg, zlib)
84
 *          pdf (PDF, in pdfio.c):
85
 *              level 1 (g4 and dct encoding: requires tiff, jpg)
86
 *              level 2 (g4, dct and flate encoding: requires tiff, jpg, zlib)
87
 */
88
89
#ifdef HAVE_CONFIG_H
90
#include <config_auto.h>
91
#endif  /* HAVE_CONFIG_H */
92
93
#include <string.h>
94
#include "allheaders.h"
95
96
    /* Set defaults for the display program (xv, xli, xzgv, open, irfanview)
97
     * that is invoked by pixDisplay()  */
98
#ifdef _WIN32
99
static l_int32  var_DISPLAY_PROG = L_DISPLAY_WITH_IV;  /* default */
100
#elif  defined(__APPLE__)
101
static l_int32  var_DISPLAY_PROG = L_DISPLAY_WITH_OPEN;  /* default */
102
#else
103
static l_int32  var_DISPLAY_PROG = L_DISPLAY_WITH_XZGV;  /* default */
104
#endif  /* _WIN32 */
105
106
0
#define Bufsize 512
107
static const l_int32  MaxDisplayWidth = 1000;
108
static const l_int32  MaxDisplayHeight = 800;
109
static const l_int32  MaxSizeForPng = 200;
110
111
    /* PostScript output for printing */
112
static const l_float32  DefaultScaling = 1.0;
113
114
    /* Global array of image file format extension names.                */
115
    /* This is in 1-1 correspondence with format enum in imageio.h.      */
116
    /* The empty string at the end represents the serialized format,     */
117
    /* which has no recognizable extension name, but the array must      */
118
    /* be padded to agree with the format enum.                          */
119
    /* (Note on 'const': The size of the array can't be defined 'const'  */
120
    /* because that makes it static.  The 'const' in the definition of   */
121
    /* the array refers to the strings in the array; the ptr to the      */
122
    /* array is not const and can be used 'extern' in other files.)      */
123
LEPT_DLL l_int32  NumImageFileFormatExtensions = 20;  /* array size */
124
LEPT_DLL const char *ImageFileFormatExtensions[] =
125
         {"unknown",
126
          "bmp",
127
          "jpg",
128
          "png",
129
          "tif",
130
          "tif",
131
          "tif",
132
          "tif",
133
          "tif",
134
          "tif",
135
          "tif",
136
          "pnm",
137
          "ps",
138
          "gif",
139
          "jp2",
140
          "webp",
141
          "pdf",
142
          "tif",
143
          "default",
144
          ""};
145
146
    /* Local map of image file name extension to output format.
147
     * Note that the extension string always includes a '.'  */
148
struct ExtensionMap
149
{
150
    char     extension[16];
151
    l_int32  format;
152
};
153
static const struct ExtensionMap extension_map[] =
154
                            { { ".bmp",      IFF_BMP       },
155
                              { ".jpg",      IFF_JFIF_JPEG },
156
                              { ".jpeg",     IFF_JFIF_JPEG },
157
                              { ".JPG",      IFF_JFIF_JPEG },
158
                              { ".png",      IFF_PNG       },
159
                              { ".tif",      IFF_TIFF      },
160
                              { ".tiff",     IFF_TIFF      },
161
                              { ".tiffg4",   IFF_TIFF_G4   },
162
                              { ".pbm",      IFF_PNM       },
163
                              { ".pgm",      IFF_PNM       },
164
                              { ".pnm",      IFF_PNM       },
165
                              { ".gif",      IFF_GIF       },
166
                              { ".jp2",      IFF_JP2       },
167
                              { ".j2k",      IFF_JP2       },
168
                              { ".ps",       IFF_PS        },
169
                              { ".pdf",      IFF_LPDF      },
170
                              { ".webp",     IFF_WEBP      } };
171
172
173
/*---------------------------------------------------------------------*
174
 *           Set jpeg quality for pixWrite() and pixWriteMem()         *
175
 *---------------------------------------------------------------------*/
176
    /* Parameter that controls jpeg quality for high-level calls. */
177
static l_int32  var_JPEG_QUALITY = 75;   /* default */
178
179
/*!
180
 * \brief   l_jpegSetQuality()
181
 *
182
 * \param[in]    new_quality    1 - 100; 75 is default; 0 defaults to 75
183
 * \return       prev           previous quality
184
 *
185
 * <pre>
186
 * Notes:
187
 *      (1) This variable is used in pixWriteStream() and pixWriteMem(),
188
 *          to control the jpeg quality.  The default is 75.
189
 *      (2) It returns the previous quality, so for example:
190
 *           l_int32  prev = l_jpegSetQuality(85);  //sets to 85
191
 *           pixWriteStream(...);
192
 *           l_jpegSetQuality(prev);   // resets to previous value
193
 *      (3) On error, logs a message and does not change the variable.
194
 */
195
l_int32
196
l_jpegSetQuality(l_int32  new_quality)
197
0
{
198
0
l_int32  prevq, newq;
199
200
0
    prevq = var_JPEG_QUALITY;
201
0
    newq = (new_quality == 0) ? 75 : new_quality;
202
0
    if (newq < 1 || newq > 100)
203
0
        L_ERROR("invalid jpeg quality; unchanged\n", __func__);
204
0
    else
205
0
        var_JPEG_QUALITY = newq;
206
0
    return prevq;
207
0
}
208
209
210
/*----------------------------------------------------------------------*
211
 *    Set global variable LeptDebugOK for writing to named temp files   *
212
 *----------------------------------------------------------------------*/
213
LEPT_DLL l_int32 LeptDebugOK = 0;  /* default value */
214
/*!
215
 * \brief   setLeptDebugOK()
216
 *
217
 * \param[in]    allow     TRUE (1) or FALSE (0)
218
 * \return       void
219
 *
220
 * <pre>
221
 * Notes:
222
 *      (1) This sets or clears the global variable LeptDebugOK, to
223
 *          control writing files in a temp directory with names that
224
 *          are compiled in.
225
 *      (2) The default in the library distribution is 0.  Call with
226
 *          %allow = 1 for development and debugging.
227
 */
228
void
229
setLeptDebugOK(l_int32  allow)
230
0
{
231
0
    if (allow != 0) allow = 1;
232
0
    LeptDebugOK = allow;
233
0
}
234
235
236
/*---------------------------------------------------------------------*
237
 *           Top-level procedures for writing images to file           *
238
 *---------------------------------------------------------------------*/
239
/*!
240
 * \brief   pixaWriteFiles()
241
 *
242
 * \param[in]    rootname
243
 * \param[in]    pixa
244
 * \param[in]    format  defined in imageio.h; see notes for default
245
 * \return  0 if OK; 1 on error
246
 *
247
 * <pre>
248
 * Notes:
249
 *      (1) Use %format = IFF_DEFAULT to decide the output format
250
 *          individually for each pix.
251
 * </pre>
252
 */
253
l_ok
254
pixaWriteFiles(const char  *rootname,
255
               PIXA        *pixa,
256
               l_int32      format)
257
0
{
258
0
char     bigbuf[Bufsize];
259
0
l_int32  i, n, pixformat;
260
0
PIX     *pix;
261
262
0
    if (!rootname)
263
0
        return ERROR_INT("rootname not defined", __func__, 1);
264
0
    if (!pixa)
265
0
        return ERROR_INT("pixa not defined", __func__, 1);
266
0
    if (format < 0 || format == IFF_UNKNOWN ||
267
0
        format >= NumImageFileFormatExtensions)
268
0
        return ERROR_INT("invalid format", __func__, 1);
269
270
0
    n = pixaGetCount(pixa);
271
0
    for (i = 0; i < n; i++) {
272
0
        pix = pixaGetPix(pixa, i, L_CLONE);
273
0
        if (format == IFF_DEFAULT)
274
0
            pixformat = pixChooseOutputFormat(pix);
275
0
        else
276
0
            pixformat = format;
277
0
        snprintf(bigbuf, Bufsize, "%s%03d.%s", rootname, i,
278
0
                 ImageFileFormatExtensions[pixformat]);
279
0
        pixWrite(bigbuf, pix, pixformat);
280
0
        pixDestroy(&pix);
281
0
    }
282
283
0
    return 0;
284
0
}
285
286
287
/*!
288
 * \brief   pixWriteDebug()
289
 *
290
 * \param[in]    fname
291
 * \param[in]    pix
292
 * \param[in]    format  defined in imageio.h
293
 * \return  0 if OK; 1 on error
294
 *
295
 * <pre>
296
 * Notes:
297
 *      (1) Debug version, intended for use in the library when writing
298
 *          to files in a temp directory with names that are compiled in.
299
 *          This is used instead of pixWrite() for all such library calls.
300
 *      (2) The global variable LeptDebugOK defaults to 0, and can be set
301
 *          or cleared by the function setLeptDebugOK().
302
 * </pre>
303
 */
304
l_ok
305
pixWriteDebug(const char  *fname,
306
              PIX         *pix,
307
              l_int32      format)
308
0
{
309
0
    if (LeptDebugOK) {
310
0
        return pixWrite(fname, pix, format);
311
0
    } else {
312
0
        L_INFO("write to named temp file %s is disabled\n", __func__, fname);
313
0
        return 0;
314
0
    }
315
0
}
316
317
318
/*!
319
 * \brief   pixWrite()
320
 *
321
 * \param[in]    fname
322
 * \param[in]    pix
323
 * \param[in]    format  defined in imageio.h
324
 * \return  0 if OK; 1 on error
325
 *
326
 * <pre>
327
 * Notes:
328
 *      (1) Open for write using binary mode (with the "b" flag)
329
 *          to avoid having Windows automatically translate the NL
330
 *          into CRLF, which corrupts image files.  On non-Windows
331
 *          systems this flag should be ignored, per ISO C90.
332
 *          Thanks to Dave Bryan for pointing this out.
333
 *      (2) If the default image format IFF_DEFAULT is requested:
334
 *          use the input format if known; otherwise, use a lossless format.
335
 *      (3) The default jpeg quality is 75.  For some other value,
336
 *          Use l_jpegSetQuality().
337
 * </pre>
338
 */
339
l_ok
340
pixWrite(const char  *fname,
341
         PIX         *pix,
342
         l_int32      format)
343
0
{
344
0
l_int32  ret;
345
0
FILE    *fp;
346
347
0
    if (!pix)
348
0
        return ERROR_INT("pix not defined", __func__, 1);
349
0
    if (!fname)
350
0
        return ERROR_INT("fname not defined", __func__, 1);
351
352
0
    if ((fp = fopenWriteStream(fname, "wb+")) == NULL)
353
0
        return ERROR_INT_1("stream not opened", fname, __func__, 1);
354
355
0
    ret = pixWriteStream(fp, pix, format);
356
0
    fclose(fp);
357
0
    if (ret)
358
0
        return ERROR_INT_1("pix not written to stream", fname, __func__, 1);
359
0
    return 0;
360
0
}
361
362
363
/*!
364
 * \brief   pixWriteAutoFormat()
365
 *
366
 * \param[in]    filename
367
 * \param[in]    pix
368
 * \return  0 if OK; 1 on error
369
 */
370
l_ok
371
pixWriteAutoFormat(const char  *filename,
372
                   PIX         *pix)
373
0
{
374
0
l_int32  format;
375
376
0
    if (!pix)
377
0
        return ERROR_INT("pix not defined", __func__, 1);
378
0
    if (!filename)
379
0
        return ERROR_INT("filename not defined", __func__, 1);
380
381
0
    if (pixGetAutoFormat(pix, &format))
382
0
        return ERROR_INT("auto format not returned", __func__, 1);
383
0
    return pixWrite(filename, pix, format);
384
0
}
385
386
387
/*!
388
 * \brief   pixWriteStream()
389
 *
390
 * \param[in]    fp file stream
391
 * \param[in]    pix
392
 * \param[in]    format
393
 * \return  0 if OK; 1 on error.
394
 */
395
l_ok
396
pixWriteStream(FILE    *fp,
397
               PIX     *pix,
398
               l_int32  format)
399
0
{
400
0
    if (!fp)
401
0
        return ERROR_INT("stream not defined", __func__, 1);
402
0
    if (!pix)
403
0
        return ERROR_INT("pix not defined", __func__, 1);
404
405
0
    if (format == IFF_DEFAULT)
406
0
        format = pixChooseOutputFormat(pix);
407
408
        /* Use bmp format for testing if library for requested
409
         * format for jpeg, png or tiff is not available */
410
0
    changeFormatForMissingLib(&format);
411
412
0
    switch(format)
413
0
    {
414
0
    case IFF_BMP:
415
0
        pixWriteStreamBmp(fp, pix);
416
0
        break;
417
418
0
    case IFF_JFIF_JPEG:   /* default quality; baseline sequential */
419
0
        return pixWriteStreamJpeg(fp, pix, var_JPEG_QUALITY, 0);
420
421
0
    case IFF_PNG:   /* no gamma value stored */
422
0
        return pixWriteStreamPng(fp, pix, 0.0);
423
424
0
    case IFF_TIFF:           /* uncompressed */
425
0
    case IFF_TIFF_PACKBITS:  /* compressed, binary only */
426
0
    case IFF_TIFF_RLE:       /* compressed, binary only */
427
0
    case IFF_TIFF_G3:        /* compressed, binary only */
428
0
    case IFF_TIFF_G4:        /* compressed, binary only */
429
0
    case IFF_TIFF_LZW:       /* compressed, all depths */
430
0
    case IFF_TIFF_ZIP:       /* compressed, all depths */
431
0
    case IFF_TIFF_JPEG:      /* compressed, 8 bpp gray and 32 bpp rgb */
432
0
        return pixWriteStreamTiff(fp, pix, format);
433
434
0
    case IFF_PNM:
435
0
        return pixWriteStreamPnm(fp, pix);
436
437
0
    case IFF_PS:
438
0
        return pixWriteStreamPS(fp, pix, NULL, 0, DefaultScaling);
439
440
0
    case IFF_GIF:
441
0
        return pixWriteStreamGif(fp, pix);
442
443
0
    case IFF_JP2:
444
0
        return pixWriteStreamJp2k(fp, pix, 34, 0, L_JP2_CODEC, 0, 0);
445
446
0
    case IFF_WEBP:
447
0
        return pixWriteStreamWebP(fp, pix, 80, 0);
448
449
0
    case IFF_LPDF:
450
0
        return pixWriteStreamPdf(fp, pix, 0, NULL);
451
452
0
    case IFF_SPIX:
453
0
        return pixWriteStreamSpix(fp, pix);
454
455
0
    default:
456
0
        return ERROR_INT("unknown format", __func__, 1);
457
0
    }
458
459
0
    return 0;
460
0
}
461
462
463
/*!
464
 * \brief   pixWriteImpliedFormat()
465
 *
466
 * \param[in]    filename
467
 * \param[in]    pix
468
 * \param[in]    quality iff JPEG; 1 - 100, 0 for default
469
 * \param[in]    progressive iff JPEG; 0 for baseline seq., 1 for progressive
470
 * \return  0 if OK; 1 on error
471
 *
472
 * <pre>
473
 * Notes:
474
 *      (1) This determines the output format from the filename extension.
475
 *      (2) The last two args are ignored except for requests for jpeg files.
476
 *      (3) The jpeg default quality is 75.
477
 * </pre>
478
 */
479
l_ok
480
pixWriteImpliedFormat(const char  *filename,
481
                      PIX         *pix,
482
                      l_int32      quality,
483
                      l_int32      progressive)
484
0
{
485
0
l_int32  format;
486
487
0
    if (!filename)
488
0
        return ERROR_INT("filename not defined", __func__, 1);
489
0
    if (!pix)
490
0
        return ERROR_INT("pix not defined", __func__, 1);
491
492
        /* Determine output format */
493
0
    format = getImpliedFileFormat(filename);
494
0
    if (format == IFF_UNKNOWN) {
495
0
        format = IFF_PNG;
496
0
    } else if (format == IFF_TIFF) {
497
0
        if (pixGetDepth(pix) == 1)
498
0
            format = IFF_TIFF_G4;
499
0
        else
500
#ifdef _WIN32
501
            format = IFF_TIFF_LZW;  /* poor compression */
502
#else
503
0
            format = IFF_TIFF_ZIP;  /* native Windows tools can't handle this */
504
0
#endif  /* _WIN32 */
505
0
    }
506
507
0
    if (format == IFF_JFIF_JPEG) {
508
0
        quality = L_MIN(quality, 100);
509
0
        quality = L_MAX(quality, 0);
510
0
        if (progressive != 0 && progressive != 1) {
511
0
            progressive = 0;
512
0
            L_WARNING("invalid progressive; setting to baseline\n", __func__);
513
0
        }
514
0
        if (quality == 0)
515
0
            quality = 75;
516
0
        pixWriteJpeg (filename, pix, quality, progressive);
517
0
    } else {
518
0
        pixWrite(filename, pix, format);
519
0
    }
520
521
0
    return 0;
522
0
}
523
524
525
/*---------------------------------------------------------------------*
526
 *          Selection of output format if default is requested         *
527
 *---------------------------------------------------------------------*/
528
/*!
529
 * \brief   pixChooseOutputFormat()
530
 *
531
 * \param[in]    pix
532
 * \return  output format, or 0 on error
533
 *
534
 * <pre>
535
 * Notes:
536
 *      (1) This should only be called if the requested format is IFF_DEFAULT.
537
 *      (2) If the pix wasn't read from a file, its input format value
538
 *          will be IFF_UNKNOWN, and in that case it is written out
539
 *          in a compressed but lossless format.
540
 * </pre>
541
 */
542
l_int32
543
pixChooseOutputFormat(PIX  *pix)
544
0
{
545
0
l_int32  d, format;
546
547
0
    if (!pix)
548
0
        return ERROR_INT("pix not defined", __func__, 0);
549
550
0
    d = pixGetDepth(pix);
551
0
    format = pixGetInputFormat(pix);
552
0
    if (format == IFF_UNKNOWN) {  /* output lossless */
553
0
        if (d == 1)
554
0
            format = IFF_TIFF_G4;
555
0
        else
556
0
            format = IFF_PNG;
557
0
    }
558
559
0
    return format;
560
0
}
561
562
563
/*!
564
 * \brief   getImpliedFileFormat()
565
 *
566
 * \param[in]    filename
567
 * \return  output format, or IFF_UNKNOWN on error or invalid extension.
568
 *
569
 * <pre>
570
 * Notes:
571
 *      (1) This determines the output file format from the extension
572
 *          of the input filename.
573
 * </pre>
574
 */
575
l_int32
576
getImpliedFileFormat(const char  *filename)
577
0
{
578
0
char    *extension;
579
0
l_int32  format = IFF_UNKNOWN;
580
581
0
    if (!filename)
582
0
        return ERROR_INT("extension not defined", __func__, IFF_UNKNOWN);
583
584
0
    if (splitPathAtExtension (filename, NULL, &extension))
585
0
        return IFF_UNKNOWN;
586
587
0
    format = getFormatFromExtension(extension);
588
0
    LEPT_FREE(extension);
589
0
    return format;
590
0
}
591
592
593
/*!
594
 * \brief   getFormatFromExtension()
595
 *
596
 * \param[in]    extension
597
 * \return  output format, or IFF_UNKNOWN on error or invalid extension.
598
 *
599
 * <pre>
600
 * Notes:
601
 *      (1) This determines the integer for writing in a format that
602
 *          corresponds to the image file type extension.  For example,
603
 *          the integer code corresponding to the extension "jpg" is 2;
604
 *          it is used to write with jpeg encoding.
605
 * </pre>
606
 */
607
l_int32
608
getFormatFromExtension(const char  *extension)
609
0
{
610
0
int      i, numext;
611
0
l_int32  format = IFF_UNKNOWN;
612
613
0
    if (!extension)
614
0
        return ERROR_INT("extension not defined", __func__, IFF_UNKNOWN);
615
616
0
    numext = sizeof(extension_map) / sizeof(extension_map[0]);
617
0
    for (i = 0; i < numext; i++) {
618
0
        if (!strcmp(extension, extension_map[i].extension)) {
619
0
            format = extension_map[i].format;
620
0
            break;
621
0
        }
622
0
    }
623
0
    return format;
624
0
}
625
626
627
/*!
628
 * \brief   pixGetAutoFormat()
629
 *
630
 * \param[in]    pix
631
 * \param[in]    &format
632
 * \return  0 if OK, 1 on error
633
 *
634
 * <pre>
635
 * Notes:
636
 *      (1) The output formats are restricted to tiff, jpeg and png
637
 *          because these are the most commonly used image formats and
638
 *          the ones that are typically installed with leptonica.
639
 *      (2) This decides what compression to use based on the pix.
640
 *          It chooses tiff-g4 if 1 bpp without a colormap, jpeg with
641
 *          quality 75 if grayscale, rgb or rgba (where it loses
642
 *          the alpha layer), and lossless png for all other situations.
643
 * </pre>
644
 */
645
l_ok
646
pixGetAutoFormat(PIX      *pix,
647
                 l_int32  *pformat)
648
0
{
649
0
l_int32   d;
650
0
PIXCMAP  *cmap;
651
652
0
    if (!pformat)
653
0
        return ERROR_INT("&format not defined", __func__, 0);
654
0
    *pformat = IFF_UNKNOWN;
655
0
    if (!pix)
656
0
        return ERROR_INT("pix not defined", __func__, 0);
657
658
0
    d = pixGetDepth(pix);
659
0
    cmap = pixGetColormap(pix);
660
0
    if (d == 1 && !cmap) {
661
0
        *pformat = IFF_TIFF_G4;
662
0
    } else if ((d == 8 && !cmap) || d == 24 || d == 32) {
663
0
        *pformat = IFF_JFIF_JPEG;
664
0
    } else {
665
0
        *pformat = IFF_PNG;
666
0
    }
667
668
0
    return 0;
669
0
}
670
671
672
/*!
673
 * \brief   getFormatExtension()
674
 *
675
 * \param[in]    format integer
676
 * \return  extension string, or NULL if format is out of range
677
 *
678
 * <pre>
679
 * Notes:
680
 *      (1) This string is NOT owned by the caller; it is just a pointer
681
 *          to a global string.  Do not free it.
682
 * </pre>
683
 */
684
const char *
685
getFormatExtension(l_int32  format)
686
0
{
687
0
    if (format < 0 || format >= NumImageFileFormatExtensions)
688
0
        return (const char *)ERROR_PTR("invalid format", __func__, NULL);
689
690
0
    return ImageFileFormatExtensions[format];
691
0
}
692
693
694
/*---------------------------------------------------------------------*
695
 *                            Write to memory                          *
696
 *---------------------------------------------------------------------*/
697
/*!
698
 * \brief   pixWriteMem()
699
 *
700
 * \param[out]   pdata data of tiff compressed image
701
 * \param[out]   psize size of returned data
702
 * \param[in]    pix
703
 * \param[in]    format  defined in imageio.h
704
 * \return  0 if OK, 1 on error
705
 *
706
 * <pre>
707
 * Notes:
708
 *      (1) On Windows, this will only write tiff and PostScript to memory.
709
 *          For other formats, it requires open_memstream(3).
710
 *      (2) PostScript output is uncompressed, in hex ascii.
711
 *          Most printers support level 2 compression (tiff_g4 for 1 bpp,
712
 *          jpeg for 8 and 32 bpp).
713
 *      (3) The default jpeg quality is 75.  For some other value,
714
 *          Use l_jpegSetQuality().
715
 * </pre>
716
 */
717
l_ok
718
pixWriteMem(l_uint8  **pdata,
719
            size_t    *psize,
720
            PIX       *pix,
721
            l_int32    format)
722
0
{
723
0
l_int32  ret;
724
725
0
    if (!pdata)
726
0
        return ERROR_INT("&data not defined", __func__, 1 );
727
0
    if (!psize)
728
0
        return ERROR_INT("&size not defined", __func__, 1 );
729
0
    if (!pix)
730
0
        return ERROR_INT("&pix not defined", __func__, 1 );
731
732
0
    if (format == IFF_DEFAULT)
733
0
        format = pixChooseOutputFormat(pix);
734
735
        /* Use bmp format for testing if library for requested
736
         * format for jpeg, png or tiff is not available */
737
0
    changeFormatForMissingLib(&format);
738
739
0
    switch(format)
740
0
    {
741
0
    case IFF_BMP:
742
0
        ret = pixWriteMemBmp(pdata, psize, pix);
743
0
        break;
744
745
0
    case IFF_JFIF_JPEG:   /* default quality; baseline sequential */
746
0
        ret = pixWriteMemJpeg(pdata, psize, pix, var_JPEG_QUALITY, 0);
747
0
        break;
748
749
0
    case IFF_PNG:   /* no gamma value stored */
750
0
        ret = pixWriteMemPng(pdata, psize, pix, 0.0);
751
0
        break;
752
753
0
    case IFF_TIFF:           /* uncompressed */
754
0
    case IFF_TIFF_PACKBITS:  /* compressed, binary only */
755
0
    case IFF_TIFF_RLE:       /* compressed, binary only */
756
0
    case IFF_TIFF_G3:        /* compressed, binary only */
757
0
    case IFF_TIFF_G4:        /* compressed, binary only */
758
0
    case IFF_TIFF_LZW:       /* compressed, all depths */
759
0
    case IFF_TIFF_ZIP:       /* compressed, all depths */
760
0
    case IFF_TIFF_JPEG:      /* compressed, 8 bpp gray or 32 bpp rgb */
761
0
        ret = pixWriteMemTiff(pdata, psize, pix, format);
762
0
        break;
763
764
0
    case IFF_PNM:
765
0
        ret = pixWriteMemPnm(pdata, psize, pix);
766
0
        break;
767
768
0
    case IFF_PS:
769
0
        ret = pixWriteMemPS(pdata, psize, pix, NULL, 0, DefaultScaling);
770
0
        break;
771
772
0
    case IFF_GIF:
773
0
        ret = pixWriteMemGif(pdata, psize, pix);
774
0
        break;
775
776
0
    case IFF_JP2:
777
0
        ret = pixWriteMemJp2k(pdata, psize, pix, 34, 0, 0, 0);
778
0
        break;
779
780
0
    case IFF_WEBP:
781
0
        ret = pixWriteMemWebP(pdata, psize, pix, 80, 0);
782
0
        break;
783
784
0
    case IFF_LPDF:
785
0
        ret = pixWriteMemPdf(pdata, psize, pix, 0, NULL);
786
0
        break;
787
788
0
    case IFF_SPIX:
789
0
        ret = pixWriteMemSpix(pdata, psize, pix);
790
0
        break;
791
792
0
    default:
793
0
        return ERROR_INT("unknown format", __func__, 1);
794
0
    }
795
796
0
    return ret;
797
0
}
798
799
800
/*---------------------------------------------------------------------*
801
 *                      Image display for debugging                    *
802
 *---------------------------------------------------------------------*/
803
/*!
804
 * \brief   l_fileDisplay()
805
 *
806
 * \param[in]    fname
807
 * \param[in]    x, y  location of display frame on the screen
808
 * \param[in]    scale  scale factor (use 0 to skip display)
809
 * \return  0 if OK; 1 on error
810
 *
811
 * <pre>
812
 * Notes:
813
 *      (1) This is a convenient wrapper for displaying image files.
814
 *      (2) It does nothing unless LeptDebugOK == TRUE.
815
 *      (2) Set %scale = 0 to disable display.
816
 *      (3) This downscales 1 bpp to gray.
817
 * </pre>
818
 */
819
l_ok
820
l_fileDisplay(const char  *fname,
821
              l_int32      x,
822
              l_int32      y,
823
              l_float32    scale)
824
0
{
825
0
PIX  *pixs, *pixd;
826
827
0
    if (!LeptDebugOK) {
828
0
        L_INFO("displaying files is disabled; "
829
0
               "use setLeptDebugOK(1) to enable\n", __func__);
830
0
        return 0;
831
0
    }
832
0
    if (scale == 0.0)
833
0
        return 0;
834
0
    if (scale < 0.0)
835
0
        return ERROR_INT("invalid scale factor", __func__, 1);
836
0
    if ((pixs = pixRead(fname)) == NULL)
837
0
        return ERROR_INT("pixs not read", __func__, 1);
838
839
0
    if (scale == 1.0) {
840
0
        pixd = pixClone(pixs);
841
0
    } else {
842
0
        if (scale < 1.0 && pixGetDepth(pixs) == 1)
843
0
            pixd = pixScaleToGray(pixs, scale);
844
0
        else
845
0
            pixd = pixScale(pixs, scale, scale);
846
0
    }
847
0
    pixDisplay(pixd, x, y);
848
0
    pixDestroy(&pixs);
849
0
    pixDestroy(&pixd);
850
0
    return 0;
851
0
}
852
853
854
/*!
855
 * \brief   pixDisplay()
856
 *
857
 * \param[in]    pix 1, 2, 4, 8, 16, 32 bpp
858
 * \param[in]    x, y  location of display frame on the screen
859
 * \return  0 if OK; 1 on error
860
 *
861
 * <pre>
862
 * Notes:
863
 *      (1) This is debugging code that displays an image on the screen.
864
 *          It uses a static internal variable to number the output files
865
 *          written by a single process.  Behavior with a shared library
866
 *          may be unpredictable.
867
 *      (2) It does nothing unless LeptDebugOK == TRUE.
868
 *      (3) It uses these programs to display the image:
869
 *             On Unix: xzgv, xli, xv or (for apple) open
870
 *             On Windows: i_view or the application currently registered
871
 *                         as the default viewer (the browser 'open' action
872
 *                         based on the extension of the image file).
873
 *          The display program must be on your $PATH variable.  It is
874
 *          chosen by setting the global var_DISPLAY_PROG, using
875
 *          l_chooseDisplayProg().  Default on Unix is xzgv.
876
 *      (4) Images with dimensions larger than MaxDisplayWidth or
877
 *          MaxDisplayHeight are downscaled to fit those constraints.
878
 *          This is particularly important for displaying 1 bpp images
879
 *          with xv, because xv automatically downscales large images
880
 *          by subsampling, which looks poor.  For 1 bpp, we use
881
 *          scale-to-gray to get decent-looking anti-aliased images.
882
 *          In all cases, we write a temporary file to /tmp/lept/disp,
883
 *          that is read by the display program.
884
 *      (5) The temporary file is written as png if, after initial
885
 *          processing for special cases, any of these obtain:
886
 *            * pix dimensions are smaller than some thresholds
887
 *            * pix depth is less than 8 bpp
888
 *            * pix is colormapped
889
 *      (6) For spp == 4, we call pixDisplayLayersRGBA() to show 3
890
 *          versions of the image: the image with a fully opaque
891
 *          alpha, the alpha, and the image as it would appear with
892
 *          a white background.
893
 *      (7) pixDisplay() can be inactivated at runtime by calling:
894
 *               l_chooseDisplayProg(L_DISPLAY_WITH_NONE);
895
 * </pre>
896
 */
897
l_ok
898
pixDisplay(PIX     *pixs,
899
           l_int32  x,
900
           l_int32  y)
901
0
{
902
0
    return pixDisplayWithTitle(pixs, x, y, NULL, 1);
903
0
}
904
905
906
/*!
907
 * \brief   pixDisplayWithTitle()
908
 *
909
 * \param[in]    pix 1, 2, 4, 8, 16, 32 bpp
910
 * \param[in]    x, y  location of display frame
911
 * \param[in]    title [optional] on frame; can be NULL;
912
 * \param[in]    dispflag 1 to write, else disabled
913
 * \return  0 if OK; 1 on error
914
 *
915
 * <pre>
916
 * Notes:
917
 *      (1) See notes for pixDisplay().
918
 *      (2) This displays the image if dispflag == 1 and the global
919
 *          var_DISPLAY_PROG != L_DISPLAY_WITH_NONE; otherwise it punts.
920
 * </pre>
921
 */
922
l_ok
923
pixDisplayWithTitle(PIX         *pixs,
924
                    l_int32      x,
925
                    l_int32      y,
926
                    const char  *title,
927
                    l_int32      dispflag)
928
0
{
929
0
char           *tempname;
930
0
char            buffer[Bufsize];
931
0
static l_atomic index = 0;  /* caution: not .so safe */
932
0
l_int32         w, h, d, spp, maxheight, opaque, threeviews;
933
0
l_float32       ratw, rath, ratmin;
934
0
PIX            *pix0, *pix1, *pix2;
935
0
PIXCMAP        *cmap;
936
0
#ifndef _WIN32
937
0
l_int32         wt, ht;
938
#else
939
char           *pathname;
940
char            fullpath[_MAX_PATH];
941
#endif  /* _WIN32 */
942
943
0
    if (!LeptDebugOK) {
944
0
        L_INFO("displaying images is disabled;\n      "
945
0
               "use setLeptDebugOK(1) to enable\n", __func__);
946
0
        return 0;
947
0
    }
948
949
#ifdef OS_IOS /* iOS 11 does not support system() */
950
    return ERROR_INT("iOS 11 does not support system()", __func__, 1);
951
#endif /* OS_IOS */
952
953
0
    if (dispflag != 1 || var_DISPLAY_PROG == L_DISPLAY_WITH_NONE)
954
0
        return 0;
955
0
    if (!pixs)
956
0
        return ERROR_INT("pixs not defined", __func__, 1);
957
958
0
#ifndef _WIN32  /* unix */
959
0
    if (var_DISPLAY_PROG != L_DISPLAY_WITH_XZGV &&
960
0
        var_DISPLAY_PROG != L_DISPLAY_WITH_XLI &&
961
0
        var_DISPLAY_PROG != L_DISPLAY_WITH_XV &&
962
0
        var_DISPLAY_PROG != L_DISPLAY_WITH_OPEN)
963
0
        return ERROR_INT("invalid unix program chosen for display",
964
0
                         __func__, 1);
965
#else  /* _WIN32 */
966
    if (var_DISPLAY_PROG != L_DISPLAY_WITH_IV &&
967
        var_DISPLAY_PROG != L_DISPLAY_WITH_OPEN)
968
        return ERROR_INT("invalid windows program chosen for display",
969
                         __func__, 1);
970
#endif  /* _WIN32 */
971
972
        /* Display with three views if either spp = 4 or if colormapped
973
         * and the alpha component is not fully opaque */
974
0
    opaque = TRUE;
975
0
    if ((cmap = pixGetColormap(pixs)) != NULL)
976
0
        pixcmapIsOpaque(cmap, &opaque);
977
0
    spp = pixGetSpp(pixs);
978
0
    threeviews = (spp == 4 || !opaque) ? TRUE : FALSE;
979
980
        /* If colormapped and not opaque, remove the colormap to RGBA */
981
0
    if (!opaque)
982
0
        pix0 = pixRemoveColormap(pixs, REMOVE_CMAP_WITH_ALPHA);
983
0
    else
984
0
        pix0 = pixClone(pixs);
985
986
        /* Scale if necessary; this will also remove a colormap */
987
0
    pixGetDimensions(pix0, &w, &h, &d);
988
0
    maxheight = (threeviews) ? MaxDisplayHeight / 3 : MaxDisplayHeight;
989
0
    if (w <= MaxDisplayWidth && h <= maxheight) {
990
0
        if (d == 16)  /* take MSB */
991
0
            pix1 = pixConvert16To8(pix0, L_MS_BYTE);
992
0
        else
993
0
            pix1 = pixClone(pix0);
994
0
    } else {
995
0
        ratw = (l_float32)MaxDisplayWidth / (l_float32)w;
996
0
        rath = (l_float32)maxheight / (l_float32)h;
997
0
        ratmin = L_MIN(ratw, rath);
998
0
        if (ratmin < 0.125 && d == 1)
999
0
            pix1 = pixScaleToGray8(pix0);
1000
0
        else if (ratmin < 0.25 && d == 1)
1001
0
            pix1 = pixScaleToGray4(pix0);
1002
0
        else if (ratmin < 0.33 && d == 1)
1003
0
            pix1 = pixScaleToGray3(pix0);
1004
0
        else if (ratmin < 0.5 && d == 1)
1005
0
            pix1 = pixScaleToGray2(pix0);
1006
0
        else
1007
0
            pix1 = pixScale(pix0, ratmin, ratmin);
1008
0
    }
1009
0
    pixDestroy(&pix0);
1010
0
    if (!pix1)
1011
0
        return ERROR_INT("pix1 not made", __func__, 1);
1012
1013
        /* Generate the three views if required */
1014
0
    if (threeviews)
1015
0
        pix2 = pixDisplayLayersRGBA(pix1, 0xffffff00, 0);
1016
0
    else
1017
0
        pix2 = pixClone(pix1);
1018
1019
0
    if (index == 0) {  /* erase any existing images */
1020
0
        lept_rmdir("lept/disp");
1021
0
        lept_mkdir("lept/disp");
1022
0
    }
1023
1024
0
    index++;
1025
0
    if (pixGetDepth(pix2) < 8 || pixGetColormap(pix2) ||
1026
0
        (w < MaxSizeForPng && h < MaxSizeForPng)) {
1027
0
        snprintf(buffer, Bufsize, "/tmp/lept/disp/write.%03d.png", index);
1028
0
        pixWrite(buffer, pix2, IFF_PNG);
1029
0
    } else {
1030
0
        snprintf(buffer, Bufsize, "/tmp/lept/disp/write.%03d.jpg", index);
1031
0
        pixWrite(buffer, pix2, IFF_JFIF_JPEG);
1032
0
    }
1033
0
    tempname = genPathname(buffer, NULL);
1034
1035
0
#ifndef _WIN32
1036
1037
        /* Unix */
1038
0
    if (var_DISPLAY_PROG == L_DISPLAY_WITH_XZGV) {
1039
            /* no way to display title */
1040
0
        pixGetDimensions(pix2, &wt, &ht, NULL);
1041
0
        snprintf(buffer, Bufsize,
1042
0
                 "xzgv --geometry %dx%d+%d+%d %s &", wt + 10, ht + 10,
1043
0
                 x, y, tempname);
1044
0
    } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XLI) {
1045
0
        if (title) {
1046
0
            snprintf(buffer, Bufsize,
1047
0
               "xli -dispgamma 1.0 -quiet -geometry +%d+%d -title \"%s\" %s &",
1048
0
               x, y, title, tempname);
1049
0
        } else {
1050
0
            snprintf(buffer, Bufsize,
1051
0
               "xli -dispgamma 1.0 -quiet -geometry +%d+%d %s &",
1052
0
               x, y, tempname);
1053
0
        }
1054
0
    } else if (var_DISPLAY_PROG == L_DISPLAY_WITH_XV) {
1055
0
        if (title) {
1056
0
            snprintf(buffer, Bufsize,
1057
0
                     "xv -quit -geometry +%d+%d -name \"%s\" %s &",
1058
0
                     x, y, title, tempname);
1059
0
        } else {
1060
0
            snprintf(buffer, Bufsize,
1061
0
                     "xv -quit -geometry +%d+%d %s &", x, y, tempname);
1062
0
        }
1063
0
    } else {  /* L_DISPLAY_WITH_OPEN */
1064
0
        snprintf(buffer, Bufsize, "open %s &", tempname);
1065
0
    }
1066
0
    callSystemDebug(buffer);
1067
1068
#else  /* _WIN32 */
1069
1070
        /* Windows: L_DISPLAY_WITH_IV || L_DISPLAY_WITH_OPEN */
1071
    pathname = genPathname(tempname, NULL);
1072
    _fullpath(fullpath, pathname, sizeof(fullpath));
1073
    if (var_DISPLAY_PROG == L_DISPLAY_WITH_IV) {
1074
        if (title) {
1075
            snprintf(buffer, Bufsize,
1076
                     "i_view32.exe \"%s\" /pos=(%d,%d) /title=\"%s\"",
1077
                     fullpath, x, y, title);
1078
        } else {
1079
            snprintf(buffer, Bufsize, "i_view32.exe \"%s\" /pos=(%d,%d)",
1080
                     fullpath, x, y);
1081
        }
1082
    } else {  /* L_DISPLAY_WITH_OPEN */
1083
        snprintf(buffer, Bufsize, "explorer.exe /open,\"%s\"", fullpath);
1084
    }
1085
    callSystemDebug(buffer);
1086
    LEPT_FREE(pathname);
1087
1088
#endif  /* _WIN32 */
1089
1090
0
    pixDestroy(&pix1);
1091
0
    pixDestroy(&pix2);
1092
0
    LEPT_FREE(tempname);
1093
0
    return 0;
1094
0
}
1095
1096
1097
/*!
1098
 * \brief   pixMakeColorSquare()
1099
 *
1100
 * \param[in]    color      in 0xrrggbb00 format
1101
 * \param[in]    size       in pixels; >= 100; use 0 for default (min size)
1102
 * \param[in]    addlabel   use 1 to display the color component values
1103
 * \param[in]    location   of text: L_ADD_ABOVE, etc; ignored if %addlabel == 0
1104
 * \param[in]    textcolor  of text label; in 0xrrggbb00 format
1105
 * \return  32 bpp rgb pixd if OK; NULL on error
1106
 *
1107
 * <pre>
1108
 * Notes:
1109
 *      (1) If %addlabel == 0, %location and %textcolor are ignored.
1110
 *      (2) To make an array of color squares, use pixDisplayColorArray().
1111
 * </pre>
1112
 */
1113
PIX *
1114
pixMakeColorSquare(l_uint32  color,
1115
                   l_int32   size,
1116
                   l_int32   addlabel,
1117
                   l_int32   location,
1118
                   l_uint32  textcolor)
1119
0
{
1120
0
char     buf[32];
1121
0
l_int32  w, rval, gval, bval;
1122
0
L_BMF   *bmf;
1123
0
PIX     *pix1, *pix2;
1124
1125
0
    w = (size <= 0) ? 100 : size;
1126
0
    if (addlabel && w < 100) {
1127
0
        L_WARNING("size too small for label; omitting label\n", __func__);
1128
0
        addlabel = 0;
1129
0
    }
1130
1131
0
    if ((pix1 = pixCreate(w, w, 32)) == NULL)
1132
0
        return (PIX *)ERROR_PTR("pix1 not madel", __func__, NULL);
1133
0
    pixSetAllArbitrary(pix1, color);
1134
0
    if (!addlabel)
1135
0
        return pix1;
1136
1137
        /* Adding text of color component values */
1138
0
    if (location != L_ADD_ABOVE && location != L_ADD_AT_TOP &&
1139
0
        location != L_ADD_AT_BOT && location != L_ADD_BELOW) {
1140
0
        L_ERROR("invalid location: adding below\n", __func__);
1141
0
        location = L_ADD_BELOW;
1142
0
    }
1143
0
    bmf = bmfCreate(NULL, 4);
1144
0
    extractRGBValues(color, &rval, &gval, &bval);
1145
0
    snprintf(buf, sizeof(buf), "%d,%d,%d", rval, gval, bval);
1146
0
    pix2 = pixAddSingleTextblock(pix1, bmf, buf, textcolor, location, NULL);
1147
0
    pixDestroy(&pix1);
1148
0
    bmfDestroy(&bmf);
1149
0
    return pix2;
1150
0
}
1151
1152
1153
void
1154
l_chooseDisplayProg(l_int32  selection)
1155
0
{
1156
0
    if (selection == L_DISPLAY_WITH_XLI ||
1157
0
        selection == L_DISPLAY_WITH_XZGV ||
1158
0
        selection == L_DISPLAY_WITH_XV ||
1159
0
        selection == L_DISPLAY_WITH_IV ||
1160
0
        selection == L_DISPLAY_WITH_OPEN) {
1161
0
        var_DISPLAY_PROG = selection;
1162
0
    } else {
1163
0
        L_ERROR("invalid display program\n", "l_chooseDisplayProg");
1164
0
    }
1165
0
}
1166
1167
1168
/*---------------------------------------------------------------------*
1169
 *                   Change format for missing lib                     *
1170
 *---------------------------------------------------------------------*/
1171
/*!
1172
 * \brief   changeFormatForMissingLib()
1173
 *
1174
 * \param[in,out]    pformat    addr of requested output image format
1175
 * \return  void
1176
 *
1177
 * <pre>
1178
 * Notes:
1179
 *      (1) This is useful for testing functionality when the library for
1180
 *          the requested output format (jpeg, png or tiff) is not linked.
1181
 *          In that case, the output format is changed to bmp.
1182
 * </pre>
1183
 */
1184
void
1185
changeFormatForMissingLib(l_int32  *pformat)
1186
0
{
1187
#if !defined(HAVE_LIBJPEG)
1188
    if (*pformat == IFF_JFIF_JPEG) {
1189
        L_WARNING("jpeg library missing; output bmp format\n", __func__);
1190
        *pformat = IFF_BMP;
1191
    }
1192
#endif  /* !defined(HAVE_LIBJPEG) */
1193
#if !defined(HAVE_LIBPNG)
1194
    if (*pformat == IFF_PNG) {
1195
        L_WARNING("png library missing; output bmp format\n", __func__);
1196
        *pformat = IFF_BMP;
1197
    }
1198
#endif  /* !defined(HAVE_LIBPNG) */
1199
#if !defined(HAVE_LIBTIFF)
1200
    if (L_FORMAT_IS_TIFF(*pformat)) {
1201
        L_WARNING("tiff library missing; output bmp format\n", __func__);
1202
        *pformat = IFF_BMP;
1203
    }
1204
#endif  /* !defined(HAVE_LIBTIFF) */
1205
0
}
1206
1207
1208
/*---------------------------------------------------------------------*
1209
 *                Deprecated pix output for debugging                  *
1210
 *---------------------------------------------------------------------*/
1211
/*!
1212
 * \brief   pixDisplayWrite()
1213
 *
1214
 * \param[in]    pix
1215
 * \param[in]    reduction
1216
 * \return  1 (error)
1217
 *
1218
 * <pre>
1219
 * Notes:
1220
 *      As of 1.80, this is a non-functional stub.
1221
 * </pre>
1222
 */
1223
l_ok
1224
pixDisplayWrite(PIX     *pixs,
1225
                l_int32  reduction)
1226
0
{
1227
0
    lept_stderr("\n########################################################\n"
1228
0
                "  pixDisplayWrite() was last used in tesseract 3.04,"
1229
0
                "  in Feb 2016.  As of 1.80, it is a non-functional stub\n"
1230
0
                "########################################################"
1231
0
                "\n\n\n");
1232
0
    return 1;
1233
0
}