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