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