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