Coverage Report

Created: 2024-07-27 06:27

/src/leptonica/src/bmpio.c
Line
Count
Source (jump to first uncovered line)
1
/*====================================================================*
2
 -  Copyright (C) 2001 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
 * \file bmpio.c
29
 * <pre>
30
 *
31
 *      Read bmp
32
 *           PIX          *pixReadStreamBmp()
33
 *           PIX          *pixReadMemBmp()
34
 *
35
 *      Write bmp
36
 *           l_int32       pixWriteStreamBmp()
37
 *           l_int32       pixWriteMemBmp()
38
 *
39
 * </pre>
40
 */
41
42
#ifdef HAVE_CONFIG_H
43
#include <config_auto.h>
44
#endif  /* HAVE_CONFIG_H */
45
46
#include <string.h>
47
#include "allheaders.h"
48
#include "pix_internal.h"
49
#include "bmp.h"
50
51
/* --------------------------------------------*/
52
#if  USE_BMPIO   /* defined in environ.h */
53
/* --------------------------------------------*/
54
55
    /* Here we're setting the pixel value 0 to white (255) and the
56
     * value 1 to black (0).  This is the convention for grayscale, but
57
     * the opposite of the convention for 1 bpp, where 0 is white
58
     * and 1 is black.  Both colormap entries are opaque (alpha = 255) */
59
RGBA_QUAD   bwmap[2] = { {255,255,255,255}, {0,0,0,255} };
60
61
    /* Image dimension limits */
62
static const l_int32  L_MAX_ALLOWED_WIDTH = 1000000;
63
static const l_int32  L_MAX_ALLOWED_HEIGHT = 1000000;
64
static const l_int64  L_MAX_ALLOWED_PIXELS = 400000000LL;
65
static const l_int32  L_MAX_ALLOWED_RES = 10000000;  /* pixels/meter */
66
67
#ifndef  NO_CONSOLE_IO
68
#define  DEBUG     0
69
#endif  /* ~NO_CONSOLE_IO */
70
71
/*--------------------------------------------------------------*
72
 *                              Read bmp                        *
73
 *--------------------------------------------------------------*/
74
/*!
75
 * \brief   pixReadStreamBmp()
76
 *
77
 * \param[in]    fp file stream opened for read
78
 * \return  pix, or NULL on error
79
 *
80
 * <pre>
81
 * Notes:
82
 *      (1) Here are references on the bmp file format:
83
 *          http://en.wikipedia.org/wiki/BMP_file_format
84
 *          http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
85
 * </pre>
86
 */
87
PIX *
88
pixReadStreamBmp(FILE  *fp)
89
0
{
90
0
l_uint8  *data;
91
0
size_t    size;
92
0
PIX      *pix;
93
94
0
    if (!fp)
95
0
        return (PIX *)ERROR_PTR("fp not defined", __func__, NULL);
96
97
        /* Read data from file and decode into Y,U,V arrays */
98
0
    rewind(fp);
99
0
    if ((data = l_binaryReadStream(fp, &size)) == NULL)
100
0
        return (PIX *)ERROR_PTR("data not read", __func__, NULL);
101
102
0
    pix = pixReadMemBmp(data, size);
103
0
    LEPT_FREE(data);
104
0
    return pix;
105
0
}
106
107
108
/*!
109
 * \brief   pixReadMemBmp()
110
 *
111
 * \param[in]    cdata    bmp data
112
 * \param[in]    size     number of bytes of bmp-formatted data
113
 * \return  pix, or NULL on error
114
 *
115
 * <pre>
116
 * Notes:
117
 *      (1) The BMP file is organized as follows:
118
 *          * 14 byte fileheader
119
 *          * Variable size infoheader: 40, 108 or 124 bytes.
120
 *            We only use data in he first 40 bytes.
121
 *          * Optional colormap, with size 4 * ncolors (in bytes)
122
 *          * Image data
123
 *      (2) 2 bpp bmp files are not valid in the original spec, but they
124
 *          are valid in later versions.
125
 *      (3) We support reading rgb files with bpp = 24 and rgba files
126
 *          with 32 bpp.  For the latter, the transparency component of
127
 *          the generated pix is saved; however, for rgba images with
128
 *          non-opaque transparent components, png provides more flexibility.
129
 * </pre>
130
 */
131
PIX *
132
pixReadMemBmp(const l_uint8  *cdata,
133
              size_t          size)
134
0
{
135
0
l_uint8    pel[4];
136
0
l_uint8   *cmapBuf, *fdata, *data;
137
0
l_int16    bftype, depth, d;
138
0
l_int32    offset, ihbytes, width, height, height_neg, xres, yres, spp;
139
0
l_int32    compression, imagebytes, fdatabytes, cmapbytes, ncolors, maxcolors;
140
0
l_int32    fdatabpl, extrabytes, filebpp, pixWpl, pixBpl, i, j, k;
141
0
l_uint32  *line, *pixdata, *pword;
142
0
l_int64    npixels;
143
0
BMP_FH    *bmpfh;
144
0
#if defined(__GNUC__)
145
0
BMP_HEADER *bmph;
146
0
#define bmpih (&bmph->bmpih)
147
#else
148
BMP_IH    *bmpih;
149
#endif
150
0
PIX       *pix, *pix1;
151
0
PIXCMAP   *cmap;
152
153
0
    if (!cdata)
154
0
        return (PIX *)ERROR_PTR("cdata not defined", __func__, NULL);
155
0
    if (size < sizeof(BMP_FH) + sizeof(BMP_IH))
156
0
        return (PIX *)ERROR_PTR("bmf size error", __func__, NULL);
157
158
        /* Verify this is an uncompressed bmp */
159
0
    bmpfh = (BMP_FH *)cdata;
160
0
    bftype = bmpfh->bfType[0] + ((l_int32)bmpfh->bfType[1] << 8);
161
0
    if (bftype != BMP_ID)
162
0
        return (PIX *)ERROR_PTR("not bmf format", __func__, NULL);
163
0
#if defined(__GNUC__)
164
0
    bmph = (BMP_HEADER *)bmpfh;
165
#else
166
    bmpih = (BMP_IH *)(cdata + BMP_FHBYTES);
167
#endif
168
0
    compression = convertOnBigEnd32(bmpih->biCompression);
169
0
    if (compression != 0)
170
0
        return (PIX *)ERROR_PTR("cannot read compressed BMP files",
171
0
                                __func__, NULL);
172
173
        /* Find the offset from the beginning of the file to the image data */
174
0
    offset = bmpfh->bfOffBits[0];
175
0
    offset += (l_int32)bmpfh->bfOffBits[1] << 8;
176
0
    offset += (l_int32)bmpfh->bfOffBits[2] << 16;
177
0
    offset += (l_uint32)bmpfh->bfOffBits[3] << 24;
178
179
        /* Read the remaining useful data in the infoheader.
180
         * Note that the first 4 bytes give the infoheader size. */
181
0
    ihbytes = convertOnBigEnd32(*(l_uint32 *)(bmpih));
182
0
    width = convertOnBigEnd32(bmpih->biWidth);
183
0
    height = convertOnBigEnd32(bmpih->biHeight);
184
0
    depth = convertOnBigEnd16(bmpih->biBitCount);
185
0
    imagebytes = convertOnBigEnd32(bmpih->biSizeImage);
186
0
    xres = convertOnBigEnd32(bmpih->biXPelsPerMeter);
187
0
    yres = convertOnBigEnd32(bmpih->biYPelsPerMeter);
188
189
        /* Some sanity checking.  We impose limits on the image
190
         * dimensions, resolution and number of pixels.  We make sure the
191
         * file is the correct size to hold the amount of uncompressed data
192
         * that is specified in the header.  The number of colormap
193
         * entries is checked: it can be either 0 (no cmap) or some
194
         * number between 2 and 256.
195
         * Note that the imagebytes for uncompressed images is either
196
         * 0 or the size of the file data.  (The fact that it can
197
         * be 0 is perhaps some legacy glitch). */
198
0
    if (width < 1)
199
0
        return (PIX *)ERROR_PTR("width < 1", __func__, NULL);
200
0
    if (width > L_MAX_ALLOWED_WIDTH)
201
0
        return (PIX *)ERROR_PTR("width too large", __func__, NULL);
202
0
    if (height == 0 || height < -L_MAX_ALLOWED_HEIGHT ||
203
0
        height > L_MAX_ALLOWED_HEIGHT)
204
0
        return (PIX *)ERROR_PTR("invalid height", __func__, NULL);
205
0
    if (xres < 0 || xres > L_MAX_ALLOWED_RES ||
206
0
        yres < 0 || yres > L_MAX_ALLOWED_RES)
207
0
        return (PIX *)ERROR_PTR("invalid resolution", __func__, NULL);
208
0
    height_neg = 0;
209
0
    if (height < 0) {
210
0
        height_neg = 1;
211
0
        height = -height;
212
0
    }
213
0
    if (ihbytes != 40 && ihbytes != 108 && ihbytes != 124) {
214
0
        L_ERROR("invalid ihbytes = %d; not in {40, 108, 124}\n",
215
0
                __func__, ihbytes);
216
0
        return NULL;
217
0
    }
218
0
    npixels = 1LL * width * height;
219
0
    if (npixels > L_MAX_ALLOWED_PIXELS)
220
0
        return (PIX *)ERROR_PTR("npixels too large", __func__, NULL);
221
0
    if (depth != 1 && depth != 2 && depth != 4 && depth != 8 &&
222
0
        depth != 16 && depth != 24 && depth != 32) {
223
0
        L_ERROR("invalid depth = %d; not in {1, 2, 4, 8, 16, 24, 32}\n",
224
0
                __func__, depth);
225
0
        return NULL;
226
0
    }
227
0
    fdatabpl = 4 * ((1LL * width * depth + 31)/32);
228
0
    fdatabytes = fdatabpl * height;
229
0
    if (imagebytes != 0 && imagebytes != fdatabytes) {
230
0
        L_ERROR("invalid imagebytes = %d; not equal to fdatabytes = %d\n",
231
0
                __func__, imagebytes, fdatabytes);
232
0
        return NULL;
233
0
    }
234
235
        /* In the original spec, BITMAPINFOHEADER is 40 bytes.
236
         * There have been a number of revisions, to capture more information.
237
         * For example, the fifth version, BITMAPV5HEADER, adds 84 bytes
238
         * of ICC color profiles.  We use the size of the infoheader
239
         * to accommodate these newer formats.  Knowing the size of the
240
         * infoheader gives more opportunity to sanity check input params. */
241
0
    cmapbytes = offset - BMP_FHBYTES - ihbytes;
242
0
    ncolors = cmapbytes / sizeof(RGBA_QUAD);
243
0
    if (ncolors < 0 || ncolors == 1)
244
0
        return (PIX *)ERROR_PTR("invalid: cmap size < 0 or 1", __func__, NULL);
245
0
    if (ncolors > 0 && depth > 8)
246
0
        return (PIX *)ERROR_PTR("can't have cmap for d > 8", __func__, NULL);
247
0
    maxcolors = (depth <= 8) ? 1 << depth : 0;
248
0
    if (ncolors > maxcolors) {
249
0
        L_ERROR("cmap too large for depth %d: ncolors = %d > maxcolors = %d\n",
250
0
                __func__, depth, ncolors, maxcolors);
251
0
        return NULL;
252
0
    }
253
0
    if (size != 1LL * offset + 1LL * fdatabytes)
254
0
        return (PIX *)ERROR_PTR("size incommensurate with image data",
255
0
                                __func__,NULL);
256
257
        /* Handle the colormap */
258
0
    cmapBuf = NULL;
259
0
    if (ncolors > 0) {
260
0
        if ((cmapBuf = (l_uint8 *)LEPT_CALLOC(ncolors, sizeof(RGBA_QUAD)))
261
0
                 == NULL)
262
0
            return (PIX *)ERROR_PTR("cmapBuf alloc fail", __func__, NULL );
263
264
            /* Read the colormap entry data from bmp. The RGBA_QUAD colormap
265
             * entries are used for both bmp and leptonica colormaps. */
266
0
        memcpy(cmapBuf, cdata + BMP_FHBYTES + ihbytes,
267
0
               ncolors * sizeof(RGBA_QUAD));
268
0
    }
269
270
        /* Make a 32 bpp pix if file depth is 24 bpp */
271
0
    d = (depth == 24) ? 32 : depth;
272
0
    if ((pix = pixCreate(width, height, d)) == NULL) {
273
0
        LEPT_FREE(cmapBuf);
274
0
        return (PIX *)ERROR_PTR( "pix not made", __func__, NULL);
275
0
    }
276
0
    pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5));  /* to ppi */
277
0
    pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5));  /* to ppi */
278
0
    pixSetInputFormat(pix, IFF_BMP);
279
0
    pixWpl = pixGetWpl(pix);
280
0
    pixBpl = 4 * pixWpl;
281
0
    if (depth <= 16)
282
0
        spp = 1;
283
0
    else if (depth == 24)
284
0
        spp = 3;
285
0
    else  /* depth == 32 */
286
0
        spp = 4;
287
0
    pixSetSpp(pix, spp);
288
289
        /* Convert the bmp colormap to a pixcmap */
290
0
    cmap = NULL;
291
0
    if (ncolors > 0) {  /* import the colormap to the pix cmap */
292
0
        cmap = pixcmapCreate(L_MIN(d, 8));
293
0
        LEPT_FREE(cmap->array);  /* remove generated cmap array */
294
0
        cmap->array  = (void *)cmapBuf;  /* and replace */
295
0
        cmap->n = L_MIN(ncolors, 256);
296
0
        for (i = 0; i < cmap->n; i++)   /* set all colors opaque */
297
0
            pixcmapSetAlpha (cmap, i, 255);
298
0
    }
299
0
    if (pixSetColormap(pix, cmap)) {
300
0
        pixDestroy(&pix);
301
0
        return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL);
302
0
    }
303
304
        /* Acquire the image data.  Image origin for bmp is at lower right. */
305
0
    fdata = (l_uint8 *)cdata + offset;  /* start of the bmp image data */
306
0
    pixdata = pixGetData(pix);
307
0
    if (depth != 24 && depth != 32) {  /* typ. 1 or 8 bpp */
308
0
        data = (l_uint8 *)pixdata + pixBpl * (height - 1);
309
0
        for (i = 0; i < height; i++) {
310
0
            memcpy(data, fdata, fdatabpl);
311
0
            fdata += fdatabpl;
312
0
            data -= pixBpl;
313
0
        }
314
0
    } else {  /*  24 or 32 bpp file; 32 bpp pix
315
             *  Note: for rgb bmp files, pel[0] is blue, pel[1] is green,
316
             *  and pel[2] is red.  This is opposite to the storage
317
             *  in the pix, which puts the red pixel in the 0 byte,
318
             *  the green in the 1 byte and the blue in the 2 byte.
319
             *  Note also that all words are endian flipped after
320
             *  assignment on L_LITTLE_ENDIAN platforms.
321
             *
322
             *  We can then make these assignments for little endians:
323
             *      SET_DATA_BYTE(pword, 1, pel[0]);      blue
324
             *      SET_DATA_BYTE(pword, 2, pel[1]);      green
325
             *      SET_DATA_BYTE(pword, 3, pel[2]);      red
326
             *  This looks like:
327
             *          3  (R)     2  (G)        1  (B)        0
328
             *      |-----------|------------|-----------|-----------|
329
             *  and after byte flipping:
330
             *           3          2  (B)     1  (G)        0  (R)
331
             *      |-----------|------------|-----------|-----------|
332
             *
333
             *  For big endians we set:
334
             *      SET_DATA_BYTE(pword, 2, pel[0]);      blue
335
             *      SET_DATA_BYTE(pword, 1, pel[1]);      green
336
             *      SET_DATA_BYTE(pword, 0, pel[2]);      red
337
             *  This looks like:
338
             *          0  (R)     1  (G)        2  (B)        3
339
             *      |-----------|------------|-----------|-----------|
340
             *  so in both cases we get the correct assignment in the PIX.
341
             *
342
             *  Can we do a platform-independent assignment?
343
             *  Yes, set the bytes without using macros:
344
             *      *((l_uint8 *)pword) = pel[2];           red
345
             *      *((l_uint8 *)pword + 1) = pel[1];       green
346
             *      *((l_uint8 *)pword + 2) = pel[0];       blue
347
             *  For little endians, before flipping, this looks again like:
348
             *          3  (R)     2  (G)        1  (B)        0
349
             *      |-----------|------------|-----------|-----------|
350
             *
351
             *  For reading an spp == 4 file with a transparency component,
352
             *  the code below shows where the alpha component is located
353
             *  in each pixel.
354
             */
355
0
        filebpp = (depth == 24) ? 3 : 4;
356
0
        extrabytes = fdatabpl - filebpp * width;
357
0
        line = pixdata + pixWpl * (height - 1);
358
0
        for (i = 0; i < height; i++) {
359
0
            for (j = 0; j < width; j++) {
360
0
                pword = line + j;
361
0
                memcpy(&pel, fdata, filebpp);
362
0
                fdata += filebpp;
363
0
                *((l_uint8 *)pword + COLOR_RED) = pel[2];
364
0
                *((l_uint8 *)pword + COLOR_GREEN) = pel[1];
365
0
                *((l_uint8 *)pword + COLOR_BLUE) = pel[0];
366
                    /* Set the alpha byte to opaque for rgb */
367
0
                if (depth == 24)
368
0
                    *((l_uint8 *)pword + L_ALPHA_CHANNEL) = 255;
369
0
                else
370
0
                    *((l_uint8 *)pword + L_ALPHA_CHANNEL) = pel[3];
371
0
            }
372
0
            if (extrabytes) {
373
0
                for (k = 0; k < extrabytes; k++) {
374
0
                    memcpy(&pel, fdata, 1);
375
0
                    fdata++;
376
0
                }
377
0
            }
378
0
            line -= pixWpl;
379
0
        }
380
0
    }
381
382
0
    pixEndianByteSwap(pix);
383
0
    if (height_neg)
384
0
        pixFlipTB(pix, pix);
385
386
        /* ----------------------------------------------
387
         * We do not use 1 bpp pix with colormaps in leptonica.
388
         * The colormap must be removed in such a way that the pixel
389
         * values are not changed.  If the values are only black and
390
         * white, return a 1 bpp image; if gray, return an 8 bpp pix;
391
         * otherwise, return a 32 bpp rgb pix.
392
         * ---------------------------------------------- */
393
0
    if (depth == 1 && cmap) {
394
0
        L_INFO("removing opaque cmap from 1 bpp\n", __func__);
395
0
        pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
396
0
        pixDestroy(&pix);
397
0
        pix = pix1;  /* rename */
398
0
    }
399
400
0
    return pix;
401
0
}
402
403
404
/*--------------------------------------------------------------*
405
 *                            Write bmp                         *
406
 *--------------------------------------------------------------*/
407
/*!
408
 * \brief   pixWriteStreamBmp()
409
 *
410
 * \param[in]    fp     file stream
411
 * \param[in]    pix    all depths
412
 * \return  0 if OK, 1 on error
413
 */
414
l_ok
415
pixWriteStreamBmp(FILE  *fp,
416
                  PIX   *pix)
417
0
{
418
0
l_uint8  *data;
419
0
size_t    size, nbytes;
420
421
0
    if (!fp)
422
0
        return ERROR_INT("stream not defined", __func__, 1);
423
0
    if (!pix)
424
0
        return ERROR_INT("pix not defined", __func__, 1);
425
426
0
    pixWriteMemBmp(&data, &size, pix);
427
0
    rewind(fp);
428
0
    nbytes = fwrite(data, 1, size, fp);
429
0
    free(data);
430
0
    if (nbytes != size)
431
0
        return ERROR_INT("Write error", __func__, 1);
432
0
    return 0;
433
0
}
434
435
436
/*!
437
 * \brief   pixWriteMemBmp()
438
 *
439
 * \param[out]   pfdata   data of bmp formatted image
440
 * \param[out]   pfsize    size of returned data
441
 * \param[in]    pixs      1, 2, 4, 8, 16, 32 bpp
442
 * \return  0 if OK, 1 on error
443
 *
444
 * <pre>
445
 * Notes:
446
 *      (1) 2 bpp bmp files are not valid in the original spec, and are
447
 *          written as 8 bpp.
448
 *      (2) pix with depth <= 8 bpp are written with a colormap.
449
 *          16 bpp gray and 32 bpp rgb pix are written without a colormap.
450
 *      (3) The transparency component in an rgba (spp = 4) pix is written.
451
 *      (4) The bmp colormap entries, RGBA_QUAD, are the same as
452
 *          the ones used for colormaps in leptonica.  This allows
453
 *          a simple memcpy for bmp output.
454
 * </pre>
455
 */
456
l_ok
457
pixWriteMemBmp(l_uint8  **pfdata,
458
               size_t    *pfsize,
459
               PIX       *pixs)
460
0
{
461
0
l_uint8     pel[4];
462
0
l_uint8    *cta = NULL;     /* address of the bmp color table array */
463
0
l_uint8    *fdata, *data, *fmdata;
464
0
l_int32     cmaplen;      /* number of bytes in the bmp colormap */
465
0
l_int32     ncolors, val, stepsize, w, h, d, fdepth, xres, yres, valid;
466
0
l_int32     pixWpl, pixBpl, extrabytes, spp, fBpl, fWpl, i, j, k;
467
0
l_int32     heapcm;  /* extra copy of cta on the heap ? 1 : 0 */
468
0
l_uint32    offbytes, fimagebytes;
469
0
l_uint32   *line, *pword;
470
0
size_t      fsize;
471
0
BMP_FH     *bmpfh;
472
0
#if defined(__GNUC__)
473
0
BMP_HEADER *bmph;
474
0
#define bmpih (&bmph->bmpih)
475
#else
476
BMP_IH     *bmpih;
477
#endif
478
0
PIX        *pix;
479
0
PIXCMAP    *cmap;
480
0
RGBA_QUAD  *pquad;
481
482
0
    if (pfdata) *pfdata = NULL;
483
0
    if (pfsize) *pfsize = 0;
484
0
    if (!pfdata)
485
0
        return ERROR_INT("&fdata not defined", __func__, 1 );
486
0
    if (!pfsize)
487
0
        return ERROR_INT("&fsize not defined", __func__, 1 );
488
0
    if (!pixs)
489
0
        return ERROR_INT("pixs not defined", __func__, 1);
490
491
        /* Verify validity of colormap */
492
0
    if ((cmap = pixGetColormap(pixs)) != NULL) {
493
0
        pixcmapIsValid(cmap, pixs, &valid);
494
0
        if (!valid)
495
0
            return ERROR_INT("colormap is not valid", __func__, 1);
496
0
    }
497
498
0
    pixGetDimensions(pixs, &w, &h, &d);
499
0
    spp = pixGetSpp(pixs);
500
0
    if (spp != 1 && spp != 3 && spp != 4) {
501
0
        L_ERROR("unsupported spp = %d\n", __func__, spp);
502
0
        return 1;
503
0
    } 
504
0
    if (d == 2) {
505
0
        L_WARNING("2 bpp files can't be read; converting to 8 bpp\n",
506
0
                  __func__);
507
0
        pix = pixConvert2To8(pixs, 0, 85, 170, 255, 1);
508
0
        d = 8;
509
0
    } else if (d == 24) {
510
0
        pix = pixConvert24To32(pixs);
511
0
        d = 32;
512
0
    } else {
513
0
        pix = pixCopy(NULL, pixs);
514
0
    }
515
516
        /* Find the bits/pixel to be written to file */
517
0
    if (spp == 1)
518
0
        fdepth = d;
519
0
    else if (spp == 3)
520
0
        fdepth = 24;
521
0
    else  /* spp == 4 */
522
0
        fdepth = 32;
523
524
        /* Resolution is given in pixels/meter */
525
0
    xres = (l_int32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5);
526
0
    yres = (l_int32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5);
527
528
0
    pixWpl = pixGetWpl(pix);
529
0
    pixBpl = 4 * pixWpl;
530
0
    fWpl = (w * fdepth + 31) / 32;
531
0
    fBpl = 4 * fWpl;
532
0
    fimagebytes = h * fBpl;
533
0
    if (fimagebytes > 4LL * L_MAX_ALLOWED_PIXELS) {
534
0
        pixDestroy(&pix);
535
0
        return ERROR_INT("image data is too large", __func__, 1);
536
0
    }
537
538
        /* If not rgb or 16 bpp, the bmp data is required to have a colormap */
539
0
    heapcm = 0;
540
0
    if (d == 32 || d == 16) {   /* 24 bpp rgb or 16 bpp: no colormap */
541
0
        ncolors = 0;
542
0
        cmaplen = 0;
543
0
    } else if ((cmap = pixGetColormap(pix))) {   /* existing colormap */
544
0
        ncolors = pixcmapGetCount(cmap);
545
0
        cmaplen = ncolors * sizeof(RGBA_QUAD);
546
0
        cta = (l_uint8 *)cmap->array;
547
0
    } else {   /* no existing colormap; d <= 8; make a binary or gray one */
548
0
        if (d == 1) {
549
0
            cmaplen  = sizeof(bwmap);
550
0
            ncolors = 2;
551
0
            cta = (l_uint8 *)bwmap;
552
0
        } else {   /* d = 2,4,8; use a grayscale output colormap */
553
0
            ncolors = 1 << d;
554
0
            cmaplen = ncolors * sizeof(RGBA_QUAD);
555
0
            heapcm = 1;
556
0
            cta = (l_uint8 *)LEPT_CALLOC(cmaplen, 1);
557
0
            stepsize = 255 / (ncolors - 1);
558
0
            for (i = 0, val = 0, pquad = (RGBA_QUAD *)cta;
559
0
                 i < ncolors;
560
0
                 i++, val += stepsize, pquad++) {
561
0
                pquad->blue = pquad->green = pquad->red = val;
562
0
                pquad->alpha = 255;  /* opaque */
563
0
            }
564
0
        }
565
0
    }
566
567
#if DEBUG
568
    if (pixGetColormap(pix) != NULL) {
569
        l_uint8  *pcmptr;
570
        pcmptr = (l_uint8 *)pixGetColormap(pix)->array;
571
        lept_stderr("Pix colormap[0] = %c%c%c%d\n",
572
                    pcmptr[0], pcmptr[1], pcmptr[2], pcmptr[3]);
573
        lept_stderr("Pix colormap[1] = %c%c%c%d\n",
574
                    pcmptr[4], pcmptr[5], pcmptr[6], pcmptr[7]);
575
    }
576
#endif  /* DEBUG */
577
578
0
    offbytes = BMP_FHBYTES + BMP_IHBYTES + cmaplen;
579
0
    fsize = offbytes + fimagebytes;
580
0
    fdata = (l_uint8 *)LEPT_CALLOC(fsize, 1);
581
0
    *pfdata = fdata;
582
0
    *pfsize = fsize;
583
584
        /* Write little-endian file header data */
585
0
    bmpfh = (BMP_FH *)fdata;
586
0
    bmpfh->bfType[0] = (l_uint8)(BMP_ID >> 0);
587
0
    bmpfh->bfType[1] = (l_uint8)(BMP_ID >> 8);
588
0
    bmpfh->bfSize[0] = (l_uint8)(fsize >>  0);
589
0
    bmpfh->bfSize[1] = (l_uint8)(fsize >>  8);
590
0
    bmpfh->bfSize[2] = (l_uint8)(fsize >> 16);
591
0
    bmpfh->bfSize[3] = (l_uint8)(fsize >> 24);
592
0
    bmpfh->bfOffBits[0] = (l_uint8)(offbytes >>  0);
593
0
    bmpfh->bfOffBits[1] = (l_uint8)(offbytes >>  8);
594
0
    bmpfh->bfOffBits[2] = (l_uint8)(offbytes >> 16);
595
0
    bmpfh->bfOffBits[3] = (l_uint8)(offbytes >> 24);
596
597
        /* Convert to little-endian and write the info header data */
598
0
#if defined(__GNUC__)
599
0
    bmph = (BMP_HEADER *)bmpfh;
600
#else
601
    bmpih = (BMP_IH *)(fdata + BMP_FHBYTES);
602
#endif
603
0
    bmpih->biSize = convertOnBigEnd32(BMP_IHBYTES);
604
0
    bmpih->biWidth = convertOnBigEnd32(w);
605
0
    bmpih->biHeight = convertOnBigEnd32(h);
606
0
    bmpih->biPlanes = convertOnBigEnd16(1);
607
0
    bmpih->biBitCount = convertOnBigEnd16(fdepth);
608
0
    bmpih->biSizeImage = convertOnBigEnd32(fimagebytes);
609
0
    bmpih->biXPelsPerMeter = convertOnBigEnd32(xres);
610
0
    bmpih->biYPelsPerMeter = convertOnBigEnd32(yres);
611
0
    bmpih->biClrUsed = convertOnBigEnd32(ncolors);
612
0
    bmpih->biClrImportant = convertOnBigEnd32(ncolors);
613
614
        /* Copy the colormap data and free the cta if necessary */
615
0
    if (ncolors > 0) {
616
0
        memcpy(fdata + BMP_FHBYTES + BMP_IHBYTES, cta, cmaplen);
617
0
        if (heapcm) LEPT_FREE(cta);
618
0
    }
619
620
        /* When you write a binary image with a colormap
621
         * that sets BLACK to 0, you must invert the data */
622
0
    if (fdepth == 1 && cmap && ((l_uint8 *)(cmap->array))[0] == 0x0) {
623
0
        pixInvert(pix, pix);
624
0
    }
625
626
        /* An endian byte swap is also required */
627
0
    pixEndianByteSwap(pix);
628
629
        /* Transfer the image data.  Image origin for bmp is at lower right. */
630
0
    fmdata = fdata + offbytes;
631
0
    if (fdepth != 24 && fdepth != 32) {   /* typ 1 or 8 bpp */
632
0
        data = (l_uint8 *)pixGetData(pix) + pixBpl * (h - 1);
633
0
        for (i = 0; i < h; i++) {
634
0
            memcpy(fmdata, data, fBpl);
635
0
            data -= pixBpl;
636
0
            fmdata += fBpl;
637
0
        }
638
0
    } else {  /* 32 bpp pix; 24 bpp or 32 bpp file
639
             *  See the comments in pixReadStreamBmp() to
640
             *  understand the logic behind the pixel ordering below.
641
             *  Note that we have again done an endian swap on
642
             *  little endian machines before arriving here, so that
643
             *  the bytes are ordered on both platforms as:
644
             *           Red        Green        Blue         --
645
             *      |-----------|------------|-----------|-----------|
646
             *
647
             *  For writing an spp == 4 file, the code below shows where
648
             *  the alpha component is written to file in each pixel.
649
             */
650
0
        extrabytes = fBpl - spp * w;
651
0
        line = pixGetData(pix) + pixWpl * (h - 1);
652
0
        for (i = 0; i < h; i++) {
653
0
            for (j = 0; j < w; j++) {
654
0
                pword = line + j;
655
0
                pel[2] = *((l_uint8 *)pword + COLOR_RED);
656
0
                pel[1] = *((l_uint8 *)pword + COLOR_GREEN);
657
0
                pel[0] = *((l_uint8 *)pword + COLOR_BLUE);
658
0
                if (spp == 4)
659
0
                    pel[3] = *((l_uint8 *)pword + L_ALPHA_CHANNEL);
660
0
                memcpy(fmdata, &pel, spp);
661
0
                fmdata += spp;
662
0
            }
663
0
            if (extrabytes) {
664
0
                for (k = 0; k < extrabytes; k++) {
665
0
                    memcpy(fmdata, &pel, 1);
666
0
                    fmdata++;
667
0
                }
668
0
            }
669
0
            line -= pixWpl;
670
0
        }
671
0
    }
672
673
0
    pixDestroy(&pix);
674
0
    return 0;
675
0
}
676
677
/* --------------------------------------------*/
678
#endif  /* USE_BMPIO */