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