1 /*
2 * rdbmp.c
3 *
4 * Copyright (C) 1994-1996, Thomas G. Lane.
5 * Modified 2009-2017 by Guido Vollbeding.
6 * This file is part of the Independent JPEG Group's software.
7 * For conditions of distribution and use, see the accompanying README file.
8 *
9 * This file contains routines to read input images in Microsoft "BMP"
10 * format (MS Windows 3.x, OS/2 1.x, and OS/2 2.x flavors).
11 * Currently, only 8-, 24-, and 32-bit images are supported, not 1-bit or
12 * 4-bit (feeding such low-depth images into JPEG would be silly anyway).
13 * Also, we don't support RLE-compressed files.
14 *
15 * These routines may need modification for non-Unix environments or
16 * specialized applications. As they stand, they assume input from
17 * an ordinary stdio stream. They further assume that reading begins
18 * at the start of the file; start_input may need work if the
19 * user interface has already read some data (e.g., to determine that
20 * the file is indeed BMP format).
21 *
22 * This code contributed by James Arthur Boucher.
23 */
24
25 #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
26
27 #ifdef BMP_SUPPORTED
28
29
30 /* Macros to deal with unsigned chars as efficiently as compiler allows */
31
32 #ifdef HAVE_UNSIGNED_CHAR
33 typedef unsigned char U_CHAR;
34 #define UCH(x) ((int) (x))
35 #else /* !HAVE_UNSIGNED_CHAR */
36 #ifdef CHAR_IS_UNSIGNED
37 typedef char U_CHAR;
38 #define UCH(x) ((int) (x))
39 #else
40 typedef char U_CHAR;
41 #define UCH(x) ((int) (x) & 0xFF)
42 #endif
43 #endif /* HAVE_UNSIGNED_CHAR */
44
45
46 #define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len)))
47
48
49 /* Private version of data source object */
50
51 typedef struct _bmp_source_struct * bmp_source_ptr;
52
53 typedef struct _bmp_source_struct {
54 struct cjpeg_source_struct pub; /* public fields */
55
56 j_compress_ptr cinfo; /* back link saves passing separate parm */
57
58 JSAMPARRAY colormap; /* BMP colormap (converted to my format) */
59
60 jvirt_sarray_ptr whole_image; /* Needed to reverse row order */
61 JDIMENSION source_row; /* Current source row number */
62 JDIMENSION row_width; /* Physical width of scanlines in file */
63
64 int bits_per_pixel; /* remembers 8-, 24-, or 32-bit format */
65 int cmap_length; /* colormap length */
66 } bmp_source_struct;
67
68
69 LOCAL(int)
read_byte(bmp_source_ptr sinfo)70 read_byte (bmp_source_ptr sinfo)
71 /* Read next byte from BMP file */
72 {
73 register FILE *infile = sinfo->pub.input_file;
74 register int c;
75
76 if ((c = getc(infile)) == EOF)
77 ERREXIT(sinfo->cinfo, JERR_INPUT_EOF);
78 return c;
79 }
80
81
82 LOCAL(void)
read_colormap(bmp_source_ptr sinfo,int cmaplen,int mapentrysize)83 read_colormap (bmp_source_ptr sinfo, int cmaplen, int mapentrysize)
84 /* Read the colormap from a BMP file */
85 {
86 int i;
87
88 switch (mapentrysize) {
89 case 3:
90 /* BGR format (occurs in OS/2 files) */
91 for (i = 0; i < cmaplen; i++) {
92 sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo);
93 sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo);
94 sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo);
95 }
96 break;
97 case 4:
98 /* BGR0 format (occurs in MS Windows files) */
99 for (i = 0; i < cmaplen; i++) {
100 sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo);
101 sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo);
102 sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo);
103 (void) read_byte(sinfo);
104 }
105 break;
106 default:
107 ERREXIT(sinfo->cinfo, JERR_BMP_BADCMAP);
108 break;
109 }
110 }
111
112
113 /*
114 * Read one row of pixels.
115 * The image has been read into the whole_image array, but is otherwise
116 * unprocessed. We must read it out in top-to-bottom row order, and if
117 * it is an 8-bit image, we must expand colormapped pixels to 24bit format.
118 */
119
120 METHODDEF(JDIMENSION)
get_8bit_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)121 get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
122 /* This version is for reading 8-bit colormap indexes */
123 {
124 bmp_source_ptr source = (bmp_source_ptr) sinfo;
125 JSAMPARRAY image_ptr;
126 register JSAMPROW inptr, outptr;
127 register JSAMPARRAY colormap;
128 register JDIMENSION col;
129 register int t;
130 int cmaplen;
131
132 /* Fetch next row from virtual array */
133 source->source_row--;
134 image_ptr = (*cinfo->mem->access_virt_sarray)
135 ((j_common_ptr) cinfo, source->whole_image,
136 source->source_row, (JDIMENSION) 1, FALSE);
137
138 /* Expand the colormap indexes to real data */
139 inptr = image_ptr[0];
140 outptr = source->pub.buffer[0];
141 colormap = source->colormap;
142 cmaplen = source->cmap_length;
143 for (col = cinfo->image_width; col > 0; col--) {
144 t = GETJSAMPLE(*inptr++);
145 if (t >= cmaplen)
146 ERREXIT(cinfo, JERR_BMP_OUTOFRANGE);
147 *outptr++ = colormap[0][t]; /* can omit GETJSAMPLE() safely */
148 *outptr++ = colormap[1][t];
149 *outptr++ = colormap[2][t];
150 }
151
152 return 1;
153 }
154
155
156 METHODDEF(JDIMENSION)
get_24bit_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)157 get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
158 /* This version is for reading 24-bit pixels */
159 {
160 bmp_source_ptr source = (bmp_source_ptr) sinfo;
161 JSAMPARRAY image_ptr;
162 register JSAMPROW inptr, outptr;
163 register JDIMENSION col;
164
165 /* Fetch next row from virtual array */
166 source->source_row--;
167 image_ptr = (*cinfo->mem->access_virt_sarray)
168 ((j_common_ptr) cinfo, source->whole_image,
169 source->source_row, (JDIMENSION) 1, FALSE);
170
171 /* Transfer data. Note source values are in BGR order
172 * (even though Microsoft's own documents say the opposite).
173 */
174 inptr = image_ptr[0];
175 outptr = source->pub.buffer[0];
176 for (col = cinfo->image_width; col > 0; col--) {
177 outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */
178 outptr[1] = *inptr++;
179 outptr[0] = *inptr++;
180 outptr += 3;
181 }
182
183 return 1;
184 }
185
186
187 METHODDEF(JDIMENSION)
get_32bit_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)188 get_32bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
189 /* This version is for reading 32-bit pixels */
190 {
191 bmp_source_ptr source = (bmp_source_ptr) sinfo;
192 JSAMPARRAY image_ptr;
193 register JSAMPROW inptr, outptr;
194 register JDIMENSION col;
195
196 /* Fetch next row from virtual array */
197 source->source_row--;
198 image_ptr = (*cinfo->mem->access_virt_sarray)
199 ((j_common_ptr) cinfo, source->whole_image,
200 source->source_row, (JDIMENSION) 1, FALSE);
201
202 /* Transfer data. Note source values are in BGR order
203 * (even though Microsoft's own documents say the opposite).
204 */
205 inptr = image_ptr[0];
206 outptr = source->pub.buffer[0];
207 for (col = cinfo->image_width; col > 0; col--) {
208 outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */
209 outptr[1] = *inptr++;
210 outptr[0] = *inptr++;
211 inptr++; /* skip the 4th byte (Alpha channel) */
212 outptr += 3;
213 }
214
215 return 1;
216 }
217
218
219 /*
220 * This method loads the image into whole_image during the first call on
221 * get_pixel_rows. The get_pixel_rows pointer is then adjusted to call
222 * get_8bit_row, get_24bit_row, or get_32bit_row on subsequent calls.
223 */
224
225 METHODDEF(JDIMENSION)
preload_image(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)226 preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
227 {
228 bmp_source_ptr source = (bmp_source_ptr) sinfo;
229 register FILE *infile = source->pub.input_file;
230 register int c;
231 register JSAMPROW out_ptr;
232 JSAMPARRAY image_ptr;
233 JDIMENSION row, col;
234 cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
235
236 /* Read the data into a virtual array in input-file row order. */
237 for (row = 0; row < cinfo->image_height; row++) {
238 if (progress != NULL) {
239 progress->pub.pass_counter = (long) row;
240 progress->pub.pass_limit = (long) cinfo->image_height;
241 (*progress->pub.progress_monitor) ((j_common_ptr) cinfo);
242 }
243 image_ptr = (*cinfo->mem->access_virt_sarray)
244 ((j_common_ptr) cinfo, source->whole_image,
245 row, (JDIMENSION) 1, TRUE);
246 out_ptr = image_ptr[0];
247 for (col = source->row_width; col > 0; col--) {
248 /* inline copy of read_byte() for speed */
249 if ((c = getc(infile)) == EOF)
250 ERREXIT(cinfo, JERR_INPUT_EOF);
251 *out_ptr++ = (JSAMPLE) c;
252 }
253 }
254 if (progress != NULL)
255 progress->completed_extra_passes++;
256
257 /* Set up to read from the virtual array in top-to-bottom order */
258 switch (source->bits_per_pixel) {
259 case 8:
260 source->pub.get_pixel_rows = get_8bit_row;
261 break;
262 case 24:
263 source->pub.get_pixel_rows = get_24bit_row;
264 break;
265 case 32:
266 source->pub.get_pixel_rows = get_32bit_row;
267 break;
268 default:
269 ERREXIT(cinfo, JERR_BMP_BADDEPTH);
270 }
271 source->source_row = cinfo->image_height;
272
273 /* And read the first row */
274 return (*source->pub.get_pixel_rows) (cinfo, sinfo);
275 }
276
277
278 /*
279 * Read the file header; return image size and component count.
280 */
281
282 METHODDEF(void)
start_input_bmp(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)283 start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
284 {
285 bmp_source_ptr source = (bmp_source_ptr) sinfo;
286 U_CHAR bmpfileheader[14];
287 U_CHAR bmpinfoheader[64];
288 #define GET_2B(array,offset) ((unsigned int) UCH(array[offset]) + \
289 (((unsigned int) UCH(array[offset+1])) << 8))
290 #define GET_4B(array,offset) ((INT32) UCH(array[offset]) + \
291 (((INT32) UCH(array[offset+1])) << 8) + \
292 (((INT32) UCH(array[offset+2])) << 16) + \
293 (((INT32) UCH(array[offset+3])) << 24))
294 INT32 bfOffBits;
295 INT32 headerSize;
296 INT32 biWidth;
297 INT32 biHeight;
298 unsigned int biPlanes;
299 INT32 biCompression;
300 INT32 biXPelsPerMeter,biYPelsPerMeter;
301 INT32 biClrUsed = 0;
302 int mapentrysize = 0; /* 0 indicates no colormap */
303 INT32 bPad;
304 JDIMENSION row_width;
305
306 /* Read and verify the bitmap file header */
307 if (! ReadOK(source->pub.input_file, bmpfileheader, 14))
308 ERREXIT(cinfo, JERR_INPUT_EOF);
309 if (GET_2B(bmpfileheader,0) != 0x4D42) /* 'BM' */
310 ERREXIT(cinfo, JERR_BMP_NOT);
311 bfOffBits = GET_4B(bmpfileheader,10);
312 /* We ignore the remaining fileheader fields */
313
314 /* The infoheader might be 12 bytes (OS/2 1.x), 40 bytes (Windows),
315 * or 64 bytes (OS/2 2.x). Check the first 4 bytes to find out which.
316 */
317 if (! ReadOK(source->pub.input_file, bmpinfoheader, 4))
318 ERREXIT(cinfo, JERR_INPUT_EOF);
319 headerSize = GET_4B(bmpinfoheader,0);
320 if (headerSize < 12 || headerSize > 64)
321 ERREXIT(cinfo, JERR_BMP_BADHEADER);
322 if (! ReadOK(source->pub.input_file, bmpinfoheader+4, headerSize-4))
323 ERREXIT(cinfo, JERR_INPUT_EOF);
324
325 switch ((int) headerSize) {
326 case 12:
327 /* Decode OS/2 1.x header (Microsoft calls this a BITMAPCOREHEADER) */
328 biWidth = (INT32) GET_2B(bmpinfoheader,4);
329 biHeight = (INT32) GET_2B(bmpinfoheader,6);
330 biPlanes = GET_2B(bmpinfoheader,8);
331 source->bits_per_pixel = (int) GET_2B(bmpinfoheader,10);
332
333 switch (source->bits_per_pixel) {
334 case 8: /* colormapped image */
335 mapentrysize = 3; /* OS/2 uses RGBTRIPLE colormap */
336 TRACEMS2(cinfo, 1, JTRC_BMP_OS2_MAPPED, (int) biWidth, (int) biHeight);
337 break;
338 case 24: /* RGB image */
339 case 32: /* RGB image + Alpha channel */
340 TRACEMS3(cinfo, 1, JTRC_BMP_OS2, (int) biWidth, (int) biHeight,
341 source->bits_per_pixel);
342 break;
343 default:
344 ERREXIT(cinfo, JERR_BMP_BADDEPTH);
345 break;
346 }
347 break;
348 case 40:
349 case 64:
350 /* Decode Windows 3.x header (Microsoft calls this a BITMAPINFOHEADER) */
351 /* or OS/2 2.x header, which has additional fields that we ignore */
352 biWidth = GET_4B(bmpinfoheader,4);
353 biHeight = GET_4B(bmpinfoheader,8);
354 biPlanes = GET_2B(bmpinfoheader,12);
355 source->bits_per_pixel = (int) GET_2B(bmpinfoheader,14);
356 biCompression = GET_4B(bmpinfoheader,16);
357 biXPelsPerMeter = GET_4B(bmpinfoheader,24);
358 biYPelsPerMeter = GET_4B(bmpinfoheader,28);
359 biClrUsed = GET_4B(bmpinfoheader,32);
360 /* biSizeImage, biClrImportant fields are ignored */
361
362 switch (source->bits_per_pixel) {
363 case 8: /* colormapped image */
364 mapentrysize = 4; /* Windows uses RGBQUAD colormap */
365 TRACEMS2(cinfo, 1, JTRC_BMP_MAPPED, (int) biWidth, (int) biHeight);
366 break;
367 case 24: /* RGB image */
368 case 32: /* RGB image + Alpha channel */
369 TRACEMS3(cinfo, 1, JTRC_BMP, (int) biWidth, (int) biHeight,
370 source->bits_per_pixel);
371 break;
372 default:
373 ERREXIT(cinfo, JERR_BMP_BADDEPTH);
374 break;
375 }
376 if (biCompression != 0)
377 ERREXIT(cinfo, JERR_BMP_COMPRESSED);
378
379 if (biXPelsPerMeter > 0 && biYPelsPerMeter > 0) {
380 /* Set JFIF density parameters from the BMP data */
381 cinfo->X_density = (UINT16) (biXPelsPerMeter/100); /* 100 cm per meter */
382 cinfo->Y_density = (UINT16) (biYPelsPerMeter/100);
383 cinfo->density_unit = 2; /* dots/cm */
384 }
385 break;
386 default:
387 ERREXIT(cinfo, JERR_BMP_BADHEADER);
388 return; /* avoid compiler warnings for uninitialized variables */
389 }
390
391 if (biPlanes != 1)
392 ERREXIT(cinfo, JERR_BMP_BADPLANES);
393 /* Sanity check for buffer allocation below */
394 if (biWidth <= 0 || biHeight <= 0 || (biWidth >> 24) || (biHeight >> 24))
395 ERREXIT(cinfo, JERR_BMP_OUTOFRANGE);
396
397 /* Compute distance to bitmap data --- will adjust for colormap below */
398 bPad = bfOffBits - (headerSize + 14);
399
400 /* Read the colormap, if any */
401 if (mapentrysize > 0) {
402 if (biClrUsed <= 0)
403 biClrUsed = 256; /* assume it's 256 */
404 else if (biClrUsed > 256)
405 ERREXIT(cinfo, JERR_BMP_BADCMAP);
406 /* Allocate space to store the colormap */
407 source->colormap = (*cinfo->mem->alloc_sarray)
408 ((j_common_ptr) cinfo, JPOOL_IMAGE,
409 (JDIMENSION) biClrUsed, (JDIMENSION) 3);
410 source->cmap_length = (int) biClrUsed;
411 /* and read it from the file */
412 read_colormap(source, (int) biClrUsed, mapentrysize);
413 /* account for size of colormap */
414 bPad -= biClrUsed * mapentrysize;
415 }
416
417 /* Skip any remaining pad bytes */
418 if (bPad < 0) /* incorrect bfOffBits value? */
419 ERREXIT(cinfo, JERR_BMP_BADHEADER);
420 while (--bPad >= 0) {
421 (void) read_byte(source);
422 }
423
424 /* Compute row width in file, including padding to 4-byte boundary */
425 if (source->bits_per_pixel == 24)
426 row_width = (JDIMENSION) (biWidth * 3);
427 else if (source->bits_per_pixel == 32)
428 row_width = (JDIMENSION) (biWidth * 4);
429 else
430 row_width = (JDIMENSION) biWidth;
431 while ((row_width & 3) != 0) row_width++;
432 source->row_width = row_width;
433
434 /* Allocate space for inversion array, prepare for preload pass */
435 source->whole_image = (*cinfo->mem->request_virt_sarray)
436 ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
437 row_width, (JDIMENSION) biHeight, (JDIMENSION) 1);
438 source->pub.get_pixel_rows = preload_image;
439 if (cinfo->progress != NULL) {
440 cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
441 progress->total_extra_passes++; /* count file input as separate pass */
442 }
443
444 /* Allocate one-row buffer for returned data */
445 source->pub.buffer = (*cinfo->mem->alloc_sarray)
446 ((j_common_ptr) cinfo, JPOOL_IMAGE,
447 (JDIMENSION) (biWidth * 3), (JDIMENSION) 1);
448 source->pub.buffer_height = 1;
449
450 cinfo->in_color_space = JCS_RGB;
451 cinfo->input_components = 3;
452 cinfo->data_precision = 8;
453 cinfo->image_width = (JDIMENSION) biWidth;
454 cinfo->image_height = (JDIMENSION) biHeight;
455 }
456
457
458 /*
459 * Finish up at the end of the file.
460 */
461
462 METHODDEF(void)
finish_input_bmp(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)463 finish_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
464 {
465 /* no work */
466 }
467
468
469 /*
470 * The module selection routine for BMP format input.
471 */
472
473 GLOBAL(cjpeg_source_ptr)
jinit_read_bmp(j_compress_ptr cinfo)474 jinit_read_bmp (j_compress_ptr cinfo)
475 {
476 bmp_source_ptr source;
477
478 /* Create module interface object */
479 source = (bmp_source_ptr)
480 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
481 SIZEOF(bmp_source_struct));
482 source->cinfo = cinfo; /* make back link for subroutines */
483 /* Fill in method ptrs, except get_pixel_rows which start_input sets */
484 source->pub.start_input = start_input_bmp;
485 source->pub.finish_input = finish_input_bmp;
486
487 return &source->pub;
488 }
489
490 #endif /* BMP_SUPPORTED */
491