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  *  bmpio.c
18  *
19  *      Read bmp from file
20  *           PIX          *pixReadStreamBmp()
21  *
22  *      Write bmp to file
23  *           l_int32       pixWriteStreamBmp()
24  *
25  *      Read/write to memory   [only on linux]
26  *           PIX          *pixReadMemBmp()
27  *           l_int32       pixWriteMemBmp()
28  */
29 
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include "allheaders.h"
34 #include "bmp.h"
35 
36 /* --------------------------------------------*/
37 #if  USE_BMPIO   /* defined in environ.h */
38 /* --------------------------------------------*/
39 
40 RGBA_QUAD   bwmap[2] = { {255,255,255,0}, {0,0,0,0} };
41 
42 #ifndef  NO_CONSOLE_IO
43 #define  DEBUG     0
44 #endif  /* ~NO_CONSOLE_IO */
45 
46 
47 /*!
48  *  pixReadStreamBmp()
49  *
50  *      Input:  stream opened for read
51  *      Return: pix, or null on error
52  */
53 PIX *
pixReadStreamBmp(FILE * fp)54 pixReadStreamBmp(FILE  *fp)
55 {
56 l_uint16   sval;
57 l_uint32   ival;
58 l_int16    bfType, bfSize, bfFill1, bfReserved1, bfReserved2;
59 l_int16    offset, bfFill2, biPlanes, depth, d;
60 l_int32    biSize, width, height, xres, yres, compression;
61 l_int32    imagebytes, biClrUsed, biClrImportant;
62 l_uint8   *colormapBuf;
63 l_int32    colormapEntries;
64 l_int32    fileBpl, extrabytes, readerror;
65 l_int32    pixWpl, pixBpl;
66 l_int32    i, j, k;
67 l_uint8    pel[4];
68 l_uint8   *data;
69 l_uint32  *line, *pword;
70 PIX        *pix, *pixt;
71 PIXCMAP   *cmap;
72 
73     PROCNAME("pixReadStreamBmp");
74 
75     if (!fp)
76         return (PIX *)ERROR_PTR("fp not defined", procName, NULL);
77 
78         /* Read bitmap file header */
79     fread((char *)&sval, 1, 2, fp);
80     bfType = convertOnBigEnd16(sval);
81     if (bfType != BMP_ID)
82         return (PIX *)ERROR_PTR("not bmf format", procName, NULL);
83 
84     fread((char *)&sval, 1, 2, fp);
85     bfSize = convertOnBigEnd16(sval);
86     fread((char *)&sval, 1, 2, fp);
87     bfFill1 = convertOnBigEnd16(sval);
88     fread((char *)&sval, 1, 2, fp);
89     bfReserved1 = convertOnBigEnd16(sval);
90     fread((char *)&sval, 1, 2, fp);
91     bfReserved2 = convertOnBigEnd16(sval);
92     fread((char *)&sval, 1, 2, fp);
93     offset = convertOnBigEnd16(sval);
94     fread((char *)&sval, 1, 2, fp);
95     bfFill2 = convertOnBigEnd16(sval);
96 
97         /* Read bitmap info header */
98     fread((char *)&ival, 1, 4, fp);
99     biSize = convertOnBigEnd32(ival);
100     fread((char *)&ival, 1, 4, fp);
101     width = convertOnBigEnd32(ival);
102     fread((char *)&ival, 1, 4, fp);
103     height = convertOnBigEnd32(ival);
104     fread((char *)&sval, 1, 2, fp);
105     biPlanes = convertOnBigEnd16(sval);
106     fread((char *)&sval, 1, 2, fp);
107     depth = convertOnBigEnd16(sval);
108     fread((char *)&ival, 1, 4, fp);
109     compression = convertOnBigEnd32(ival);
110     fread((char *)&ival, 1, 4, fp);
111     imagebytes = convertOnBigEnd32(ival);
112     fread((char *)&ival, 1, 4, fp);
113     xres = convertOnBigEnd32(ival);
114     fread((char *)&ival, 1, 4, fp);
115     yres = convertOnBigEnd32(ival);
116     fread((char *)&ival, 1, 4, fp);
117     biClrUsed = convertOnBigEnd32(ival);
118     fread((char *)&ival, 1, 4, fp);
119     biClrImportant = convertOnBigEnd32(ival);
120 
121     if (compression != 0)
122         return (PIX *)ERROR_PTR("cannot read compressed BMP files",
123                                 procName,NULL);
124 
125     colormapEntries = (offset - BMP_FHBYTES - BMP_IHBYTES) / sizeof(RGBA_QUAD);
126     if (colormapEntries > 0) {
127         if ((colormapBuf = (l_uint8 *)CALLOC(colormapEntries,
128                                              sizeof(RGBA_QUAD))) == NULL)
129             return (PIX *)ERROR_PTR("colormapBuf alloc fail", procName, NULL );
130 
131             /* Read colormap */
132         if (fread(colormapBuf, sizeof(RGBA_QUAD), colormapEntries, fp)
133                  != colormapEntries) {
134             FREE(colormapBuf);
135             return (PIX *)ERROR_PTR( "colormap read fail", procName, NULL);
136         }
137     }
138 
139         /* Make a 32 bpp pix if depth is 24 bpp */
140     d = depth;
141     if (depth == 24)
142         d = 32;
143     if ((pix = pixCreate(width, height, d)) == NULL)
144         return (PIX *)ERROR_PTR( "pix not made", procName, NULL);
145     pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5));  /* to ppi */
146     pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5));  /* to ppi */
147 
148     cmap = NULL;
149     if (colormapEntries > 256)
150         L_WARNING("more than 256 colormap entries!", procName);
151     if (colormapEntries > 0) {  /* import the colormap to the pix cmap */
152         cmap = pixcmapCreate(L_MIN(d, 8));
153         FREE(cmap->array);  /* remove generated cmap array */
154         cmap->array  = (void *)colormapBuf;  /* and replace */
155         cmap->n = L_MIN(colormapEntries, 256);
156     }
157     pixSetColormap(pix, cmap);
158 
159     fileBpl = 4 * ((width * depth + 31)/32);
160     pixWpl = pixGetWpl(pix);
161     pixBpl = 4 * pixWpl;
162 
163         /* Seek to the start of the bitmap in the file */
164     fseek(fp, offset, 0);
165 
166     if (depth != 24) {  /* typ. 1 or 8 bpp */
167         data = (l_uint8 *)pixGetData(pix) + pixBpl * (height - 1);
168         for (i = 0; i < height; i++) {
169             if (fread(data, 1, fileBpl, fp) != fileBpl) {
170                 pixDestroy(&pix);
171                 return (PIX *)ERROR_PTR("BMP read fail", procName, NULL);
172             }
173             data -= pixBpl;
174         }
175     }
176     else {  /*  24 bpp file; 32 bpp pix
177              *  Note: for bmp files, pel[0] is blue, pel[1] is green,
178              *  and pel[2] is red.  This is opposite to the storage
179              *  in the pix, which puts the red pixel in the 0 byte,
180              *  the green in the 1 byte and the blue in the 2 byte.
181              *  Note also that all words are endian flipped after
182              *  assignment on L_LITTLE_ENDIAN platforms.
183              *
184              *  We can then make these assignments for little endians:
185              *      SET_DATA_BYTE(pword, 1, pel[0]);      blue
186              *      SET_DATA_BYTE(pword, 2, pel[1]);      green
187              *      SET_DATA_BYTE(pword, 3, pel[2]);      red
188              *  This looks like:
189              *          3  (R)     2  (G)        1  (B)        0
190              *      |-----------|------------|-----------|-----------|
191              *  and after byte flipping:
192              *           3          2  (B)     1  (G)        0  (R)
193              *      |-----------|------------|-----------|-----------|
194              *
195              *  For big endians we set:
196              *      SET_DATA_BYTE(pword, 2, pel[0]);      blue
197              *      SET_DATA_BYTE(pword, 1, pel[1]);      green
198              *      SET_DATA_BYTE(pword, 0, pel[2]);      red
199              *  This looks like:
200              *          0  (R)     1  (G)        2  (B)        3
201              *      |-----------|------------|-----------|-----------|
202              *  so in both cases we get the correct assignment in the PIX.
203              *
204              *  Can we do a platform-independent assignment?
205              *  Yes, set the bytes without using macros:
206              *      *((l_uint8 *)pword) = pel[2];           red
207              *      *((l_uint8 *)pword + 1) = pel[1];       green
208              *      *((l_uint8 *)pword + 2) = pel[0];       blue
209              *  For little endians, before flipping, this looks again like:
210              *          3  (R)     2  (G)        1  (B)        0
211              *      |-----------|------------|-----------|-----------|
212              */
213         readerror = 0;
214         extrabytes = fileBpl - 3 * width;
215         line = pixGetData(pix) + pixWpl * (height - 1);
216         for (i = 0; i < height; i++) {
217             for (j = 0; j < width; j++) {
218                 pword = line + j;
219                 if (fread(&pel, 1, 3, fp) != 3)
220                     readerror = 1;
221                 *((l_uint8 *)pword + COLOR_RED) = pel[2];
222                 *((l_uint8 *)pword + COLOR_GREEN) = pel[1];
223                 *((l_uint8 *)pword + COLOR_BLUE) = pel[0];
224             }
225             if (extrabytes) {
226                 for (k = 0; k < extrabytes; k++)
227                     fread(&pel, 1, 1, fp);
228             }
229             line -= pixWpl;
230         }
231         if (readerror) {
232             pixDestroy(&pix);
233             return (PIX *)ERROR_PTR("BMP read fail", procName, NULL);
234         }
235     }
236 
237     pixEndianByteSwap(pix);
238 
239         /* ----------------------------------------------
240          * The bmp colormap determines the values of black
241          * and white pixels for binary in the following way:
242          * if black = 1 (255), white = 0
243          *      255, 255, 255, 0, 0, 0, 0, 0
244          * if black = 0, white = 1 (255)
245          *      0, 0, 0, 0, 255, 255, 255, 0
246          * We have no need for a 1 bpp pix with a colormap!
247          * ---------------------------------------------- */
248     if (depth == 1 && cmap) {
249 /*        L_INFO("Removing colormap", procName); */
250         pixt = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC);
251         pixDestroy(&pix);
252         pix = pixt;  /* rename */
253     }
254 
255     return pix;
256 }
257 
258 
259 
260 /*!
261  *  pixWriteStreamBmp()
262  *
263  *      Input:  stream opened for write
264  *              pix (1, 4, 8, 32 bpp)
265  *      Return: 0 if OK, 1 on error
266  *
267  *  Notes:
268  *      (1) We position fp at the beginning of the stream, so it
269  *          truncates any existing data
270  *      (2) 2 bpp Bmp files are apparently not valid!.  We can
271  *          write and read them, but nobody else can read ours.
272  */
273 l_int32
pixWriteStreamBmp(FILE * fp,PIX * pix)274 pixWriteStreamBmp(FILE  *fp,
275                   PIX   *pix)
276 {
277 l_uint32    offbytes, filebytes, fileimagebytes;
278 l_int32     width, height, depth, d, xres, yres;
279 l_uint16    bfType, bfSize, bfFill1, bfReserved1, bfReserved2;
280 l_uint16    bfOffBits, bfFill2, biPlanes, biBitCount;
281 l_uint16    sval;
282 l_uint32    biSize, biWidth, biHeight, biCompression, biSizeImage;
283 l_uint32    biXPelsPerMeter, biYPelsPerMeter, biClrUsed, biClrImportant;
284 l_int32     pixWpl, pixBpl, extrabytes, writeerror;
285 l_int32     fileBpl, fileWpl;
286 l_int32     i, j, k;
287 l_int32     heapcm;  /* extra copy of cta on the heap ? 1 : 0 */
288 l_uint8    *data;
289 l_uint8     pel[4];
290 l_uint32   *line, *pword;
291 PIXCMAP    *cmap;
292 l_uint8    *cta;          /* address of the bmp color table array */
293 l_int32     cmaplen;      /* number of bytes in the bmp colormap */
294 l_int32     ncolors, val, stepsize;
295 RGBA_QUAD  *pquad;
296 
297     PROCNAME("pixWriteStreamBmp");
298 
299     if (!fp)
300         return ERROR_INT("stream not defined", procName, 1);
301     if (!pix)
302         return ERROR_INT("pix not defined", procName, 1);
303 
304     width  = pixGetWidth(pix);
305     height = pixGetHeight(pix);
306     d  = pixGetDepth(pix);
307     if (d == 2)
308         L_WARNING("writing 2 bpp bmp file; nobody else can read", procName);
309     depth = d;
310     if (d == 32)
311         depth = 24;
312     xres = (l_int32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5);  /* to ppm */
313     yres = (l_int32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5);  /* to ppm */
314 
315     pixWpl = pixGetWpl(pix);
316     pixBpl = 4 * pixWpl;
317     fileWpl = (width * depth + 31) / 32;
318     fileBpl = 4 * fileWpl;
319     fileimagebytes = height * fileBpl;
320 
321     heapcm = 0;
322     if (d == 32) {   /* 24 bpp rgb; no colormap */
323         ncolors = 0;
324         cmaplen = 0;
325     }
326     else if ((cmap = pixGetColormap(pix))) {   /* existing colormap */
327         ncolors = pixcmapGetCount(cmap);
328         cmaplen = ncolors * sizeof(RGBA_QUAD);
329         cta = (l_uint8 *)cmap->array;
330     }
331     else {   /* no existing colormap; make a binary or gray one */
332         if (d == 1) {
333             cmaplen  = sizeof(bwmap);
334             ncolors = 2;
335             cta = (l_uint8 *)bwmap;
336         }
337         else {   /* d != 32; output grayscale version */
338             ncolors = 1 << depth;
339             cmaplen = ncolors * sizeof(RGBA_QUAD);
340 
341             heapcm = 1;
342             if ((cta = (l_uint8 *)CALLOC(cmaplen, 1)) == NULL)
343                 return ERROR_INT("colormap alloc fail", procName, 1);
344 
345             stepsize = 255 / (ncolors - 1);
346             for (i = 0, val = 0, pquad = (RGBA_QUAD *)cta;
347                  i < ncolors;
348                  i++, val += stepsize, pquad++) {
349                 pquad->blue = pquad->green = pquad->red = val;
350             }
351         }
352     }
353 
354 #if DEBUG
355     {l_uint8  *pcmptr;
356         pcmptr = (l_uint8 *)pixGetColormap(pix)->array;
357         fprintf(stderr, "Pix colormap[0] = %c%c%c%d\n",
358             pcmptr[0], pcmptr[1], pcmptr[2], pcmptr[3]);
359         fprintf(stderr, "Pix colormap[1] = %c%c%c%d\n",
360             pcmptr[4], pcmptr[5], pcmptr[6], pcmptr[7]);
361     }
362 #endif  /* DEBUG */
363 
364     fseek(fp, 0L, 0);
365 
366         /* Convert to little-endian and write the file header data */
367     bfType = convertOnBigEnd16(BMP_ID);
368     offbytes = BMP_FHBYTES + BMP_IHBYTES + cmaplen;
369     filebytes = offbytes + fileimagebytes;
370     sval = filebytes & 0x0000ffff;
371     bfSize = convertOnBigEnd16(sval);
372     sval = (filebytes >> 16) & 0x0000ffff;
373     bfFill1 = convertOnBigEnd16(sval);
374     bfReserved1 = 0;
375     bfReserved2 = 0;
376     sval = offbytes & 0x0000ffff;
377     bfOffBits = convertOnBigEnd16(sval);
378     sval = (offbytes >> 16) & 0x0000ffff;
379     bfFill2 = convertOnBigEnd16(sval);
380     fwrite(&bfType, 1, 2, fp);
381     fwrite(&bfSize, 1, 2, fp);
382     fwrite(&bfFill1, 1, 2, fp);
383     fwrite(&bfReserved1, 1, 2, fp);
384     fwrite(&bfReserved1, 1, 2, fp);
385     fwrite(&bfOffBits, 1, 2, fp);
386     fwrite(&bfFill2, 1, 2, fp);
387 
388         /* Convert to little-endian and write the info header data */
389     biSize = convertOnBigEnd32(BMP_IHBYTES);
390     biWidth = convertOnBigEnd32(width);
391     biHeight = convertOnBigEnd32(height);
392     biPlanes = convertOnBigEnd16(1);
393     biBitCount = convertOnBigEnd16(depth);
394     biCompression   = 0;
395     biSizeImage = convertOnBigEnd32(fileimagebytes);
396     biXPelsPerMeter = convertOnBigEnd32(xres);
397     biYPelsPerMeter = convertOnBigEnd32(yres);
398     biClrUsed = convertOnBigEnd32(ncolors);
399     biClrImportant = convertOnBigEnd32(ncolors);
400     fwrite(&biSize, 1, 4, fp);
401     fwrite(&biWidth, 1, 4, fp);
402     fwrite(&biHeight, 1, 4, fp);
403     fwrite(&biPlanes, 1, 2, fp);
404     fwrite(&biBitCount, 1, 2, fp);
405     fwrite(&biCompression, 1, 4, fp);
406     fwrite(&biSizeImage, 1, 4, fp);
407     fwrite(&biXPelsPerMeter, 1, 4, fp);
408     fwrite(&biYPelsPerMeter, 1, 4, fp);
409     fwrite(&biClrUsed, 1, 4, fp);
410     fwrite(&biClrImportant, 1, 4, fp);
411 
412         /* Write the colormap data */
413     if (ncolors > 0) {
414         if (fwrite(cta, 1, cmaplen, fp) != cmaplen) {
415             if (heapcm)
416                 FREE(cta);
417             return ERROR_INT("colormap write fail", procName, 1);
418         }
419         if (heapcm)
420             FREE(cta);
421     }
422 
423         /* When you write a binary image with a colormap
424          * that sets BLACK to 0, you must invert the data */
425     if (depth == 1 && cmap && ((l_uint8 *)(cmap->array))[0] == 0x0) {
426         pixInvert(pix, pix);
427     }
428 
429     pixEndianByteSwap(pix);
430 
431     writeerror = 0;
432     if (depth != 24) {   /* typ 1 or 8 bpp */
433         data = (l_uint8 *)pixGetData(pix) + pixBpl * (height - 1);
434         for (i = 0; i < height; i++) {
435             if (fwrite(data, 1, fileBpl, fp) != fileBpl)
436                 writeerror = 1;
437             data -= pixBpl;
438         }
439     }
440     else {  /* 32 bpp pix; 24 bpp file
441              * See the comments in pixReadStreamBMP() to
442              * understand the logic behind the pixel ordering below.
443              * Note that we have again done an endian swap on
444              * little endian machines before arriving here, so that
445              * the bytes are ordered on both platforms as:
446                         Red         Green        Blue         --
447                     |-----------|------------|-----------|-----------|
448              */
449         extrabytes = fileBpl - 3 * width;
450         line = pixGetData(pix) + pixWpl * (height - 1);
451         for (i = 0; i < height; i++) {
452             for (j = 0; j < width; j++) {
453                 pword = line + j;
454                 pel[2] = *((l_uint8 *)pword + COLOR_RED);
455                 pel[1] = *((l_uint8 *)pword + COLOR_GREEN);
456                 pel[0] = *((l_uint8 *)pword + COLOR_BLUE);
457                 if (fwrite(&pel, 1, 3, fp) != 3)
458                     writeerror = 1;
459             }
460             if (extrabytes) {
461                 for (k = 0; k < extrabytes; k++)
462                     fwrite(&pel, 1, 1, fp);
463             }
464             line -= pixWpl;
465         }
466     }
467 
468         /* Restore to original state */
469     pixEndianByteSwap(pix);
470     if (depth == 1 && cmap && ((l_uint8 *)(cmap->array))[0] == 0x0)
471         pixInvert(pix, pix);
472 
473     if (writeerror)
474         return ERROR_INT("image write fail", procName, 1);
475 
476     return 0;
477 }
478 
479 
480 /*---------------------------------------------------------------------*
481  *                         Read/write to memory                        *
482  *---------------------------------------------------------------------*/
483 #ifdef HAVE_CONFIG_H
484 #include "config_auto.h"
485 #endif  /* HAVE_CONFIG_H */
486 
487 #if HAVE_FMEMOPEN
488 
489 extern FILE *open_memstream(char **data, size_t *size);
490 extern FILE *fmemopen(void *data, size_t size, const char *mode);
491 
492 /*!
493  *  pixReadMemBmp()
494  *
495  *      Input:  cdata (const; bmp-encoded)
496  *              size (of data)
497  *      Return: pix, or null on error
498  *
499  *  Notes:
500  *      (1) The @size byte of @data must be a null character.
501  */
502 PIX *
pixReadMemBmp(const l_uint8 * cdata,size_t size)503 pixReadMemBmp(const l_uint8  *cdata,
504               size_t          size)
505 {
506 l_uint8  *data;
507 FILE     *fp;
508 PIX      *pix;
509 
510     PROCNAME("pixReadMemBmp");
511 
512     if (!cdata)
513         return (PIX *)ERROR_PTR("cdata not defined", procName, NULL);
514 
515     data = (l_uint8 *)cdata;  /* we're really not going to change this */
516     if ((fp = fmemopen(data, size, "r")) == NULL)
517         return (PIX *)ERROR_PTR("stream not opened", procName, NULL);
518     pix = pixReadStreamBmp(fp);
519     fclose(fp);
520     return pix;
521 }
522 
523 
524 /*!
525  *  pixWriteMemBmp()
526  *
527  *      Input:  &data (<return> data of tiff compressed image)
528  *              &size (<return> size of returned data)
529  *              pix
530  *      Return: 0 if OK, 1 on error
531  *
532  *  Notes:
533  *      (1) See pixWriteStreamBmp() for usage.  This version writes to
534  *          memory instead of to a file stream.
535  */
536 l_int32
pixWriteMemBmp(l_uint8 ** pdata,size_t * psize,PIX * pix)537 pixWriteMemBmp(l_uint8  **pdata,
538                size_t    *psize,
539                PIX       *pix)
540 {
541 l_int32  ret;
542 FILE    *fp;
543 
544     PROCNAME("pixWriteMemBmp");
545 
546     if (!pdata)
547         return ERROR_INT("&data not defined", procName, 1 );
548     if (!psize)
549         return ERROR_INT("&size not defined", procName, 1 );
550     if (!pix)
551         return ERROR_INT("&pix not defined", procName, 1 );
552 
553     if ((fp = open_memstream((char **)pdata, psize)) == NULL)
554         return ERROR_INT("stream not opened", procName, 1);
555     ret = pixWriteStreamBmp(fp, pix);
556     fclose(fp);
557     return ret;
558 }
559 
560 #else
561 
562 PIX *
pixReadMemBmp(const l_uint8 * cdata,size_t size)563 pixReadMemBmp(const l_uint8  *cdata,
564               size_t          size)
565 {
566     return (PIX *)ERROR_PTR(
567         "bmp read from memory not implemented on this platform",
568         "pixReadMemBmp", NULL);
569 }
570 
571 
572 l_int32
pixWriteMemBmp(l_uint8 ** pdata,size_t * psize,PIX * pix)573 pixWriteMemBmp(l_uint8  **pdata,
574                size_t    *psize,
575                PIX       *pix)
576 {
577     return ERROR_INT(
578         "bmp write to memory not implemented on this platform",
579         "pixWriteMemBmp", 1);
580 }
581 
582 #endif  /* HAVE_FMEMOPEN */
583 
584 /* --------------------------------------------*/
585 #endif  /* USE_BMPIO */
586 /* --------------------------------------------*/
587 
588