1/* 2 * wrbmp.c 3 * 4 * This file was part of the Independent JPEG Group's software: 5 * Copyright (C) 1994-1996, Thomas G. Lane. 6 * libjpeg-turbo Modifications: 7 * Copyright (C) 2013, Linaro Limited. 8 * Copyright (C) 2014-2015, 2017, D. R. Commander. 9 * For conditions of distribution and use, see the accompanying README.ijg 10 * file. 11 * 12 * This file contains routines to write output images in Microsoft "BMP" 13 * format (MS Windows 3.x and OS/2 1.x flavors). 14 * Either 8-bit colormapped or 24-bit full-color format can be written. 15 * No compression is supported. 16 * 17 * These routines may need modification for non-Unix environments or 18 * specialized applications. As they stand, they assume output to 19 * an ordinary stdio stream. 20 * 21 * This code contributed by James Arthur Boucher. 22 */ 23 24#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ 25#include "jconfigint.h" 26 27#ifdef BMP_SUPPORTED 28 29 30/* 31 * To support 12-bit JPEG data, we'd have to scale output down to 8 bits. 32 * This is not yet implemented. 33 */ 34 35#if BITS_IN_JSAMPLE != 8 36 Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ 37#endif 38 39/* 40 * Since BMP stores scanlines bottom-to-top, we have to invert the image 41 * from JPEG's top-to-bottom order. To do this, we save the outgoing data 42 * in a virtual array during put_pixel_row calls, then actually emit the 43 * BMP file during finish_output. The virtual array contains one JSAMPLE per 44 * pixel if the output is grayscale or colormapped, three if it is full color. 45 */ 46 47/* Private version of data destination object */ 48 49typedef struct { 50 struct djpeg_dest_struct pub; /* public fields */ 51 52 boolean is_os2; /* saves the OS2 format request flag */ 53 54 jvirt_sarray_ptr whole_image; /* needed to reverse row order */ 55 JDIMENSION data_width; /* JSAMPLEs per row */ 56 JDIMENSION row_width; /* physical width of one row in the BMP file */ 57 int pad_bytes; /* number of padding bytes needed per row */ 58 JDIMENSION cur_output_row; /* next row# to write to virtual array */ 59} bmp_dest_struct; 60 61typedef bmp_dest_struct *bmp_dest_ptr; 62 63 64/* Forward declarations */ 65LOCAL(void) write_colormap 66 (j_decompress_ptr cinfo, bmp_dest_ptr dest, int map_colors, 67 int map_entry_size); 68 69 70static INLINE boolean is_big_endian(void) 71{ 72 int test_value = 1; 73 if (*(char *)&test_value != 1) 74 return TRUE; 75 return FALSE; 76} 77 78 79/* 80 * Write some pixel data. 81 * In this module rows_supplied will always be 1. 82 */ 83 84METHODDEF(void) 85put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, 86 JDIMENSION rows_supplied) 87/* This version is for writing 24-bit pixels */ 88{ 89 bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; 90 JSAMPARRAY image_ptr; 91 register JSAMPROW inptr, outptr; 92 register JDIMENSION col; 93 int pad; 94 95 /* Access next row in virtual array */ 96 image_ptr = (*cinfo->mem->access_virt_sarray) 97 ((j_common_ptr) cinfo, dest->whole_image, 98 dest->cur_output_row, (JDIMENSION) 1, TRUE); 99 dest->cur_output_row++; 100 101 /* Transfer data. Note destination values must be in BGR order 102 * (even though Microsoft's own documents say the opposite). 103 */ 104 inptr = dest->pub.buffer[0]; 105 outptr = image_ptr[0]; 106 107 if (cinfo->out_color_space == JCS_RGB565) { 108 boolean big_endian = is_big_endian(); 109 unsigned short *inptr2 = (unsigned short *)inptr; 110 for (col = cinfo->output_width; col > 0; col--) { 111 if (big_endian) { 112 outptr[0] = (*inptr2 >> 5) & 0xF8; 113 outptr[1] = ((*inptr2 << 5) & 0xE0) | ((*inptr2 >> 11) & 0x1C); 114 outptr[2] = *inptr2 & 0xF8; 115 } else { 116 outptr[0] = (*inptr2 << 3) & 0xF8; 117 outptr[1] = (*inptr2 >> 3) & 0xFC; 118 outptr[2] = (*inptr2 >> 8) & 0xF8; 119 } 120 outptr += 3; 121 inptr2++; 122 } 123 } else { 124 for (col = cinfo->output_width; col > 0; col--) { 125 outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */ 126 outptr[1] = *inptr++; 127 outptr[0] = *inptr++; 128 outptr += 3; 129 } 130 } 131 132 /* Zero out the pad bytes. */ 133 pad = dest->pad_bytes; 134 while (--pad >= 0) 135 *outptr++ = 0; 136} 137 138METHODDEF(void) 139put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, 140 JDIMENSION rows_supplied) 141/* This version is for grayscale OR quantized color output */ 142{ 143 bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; 144 JSAMPARRAY image_ptr; 145 register JSAMPROW inptr, outptr; 146 register JDIMENSION col; 147 int pad; 148 149 /* Access next row in virtual array */ 150 image_ptr = (*cinfo->mem->access_virt_sarray) 151 ((j_common_ptr) cinfo, dest->whole_image, 152 dest->cur_output_row, (JDIMENSION) 1, TRUE); 153 dest->cur_output_row++; 154 155 /* Transfer data. */ 156 inptr = dest->pub.buffer[0]; 157 outptr = image_ptr[0]; 158 for (col = cinfo->output_width; col > 0; col--) { 159 *outptr++ = *inptr++; /* can omit GETJSAMPLE() safely */ 160 } 161 162 /* Zero out the pad bytes. */ 163 pad = dest->pad_bytes; 164 while (--pad >= 0) 165 *outptr++ = 0; 166} 167 168 169/* 170 * Startup: normally writes the file header. 171 * In this module we may as well postpone everything until finish_output. 172 */ 173 174METHODDEF(void) 175start_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) 176{ 177 /* no work here */ 178} 179 180 181/* 182 * Finish up at the end of the file. 183 * 184 * Here is where we really output the BMP file. 185 * 186 * First, routines to write the Windows and OS/2 variants of the file header. 187 */ 188 189LOCAL(void) 190write_bmp_header (j_decompress_ptr cinfo, bmp_dest_ptr dest) 191/* Write a Windows-style BMP file header, including colormap if needed */ 192{ 193 char bmpfileheader[14]; 194 char bmpinfoheader[40]; 195#define PUT_2B(array,offset,value) \ 196 (array[offset] = (char) ((value) & 0xFF), \ 197 array[offset+1] = (char) (((value) >> 8) & 0xFF)) 198#define PUT_4B(array,offset,value) \ 199 (array[offset] = (char) ((value) & 0xFF), \ 200 array[offset+1] = (char) (((value) >> 8) & 0xFF), \ 201 array[offset+2] = (char) (((value) >> 16) & 0xFF), \ 202 array[offset+3] = (char) (((value) >> 24) & 0xFF)) 203 long headersize, bfSize; 204 int bits_per_pixel, cmap_entries; 205 206 /* Compute colormap size and total file size */ 207 if (cinfo->out_color_space == JCS_RGB) { 208 if (cinfo->quantize_colors) { 209 /* Colormapped RGB */ 210 bits_per_pixel = 8; 211 cmap_entries = 256; 212 } else { 213 /* Unquantized, full color RGB */ 214 bits_per_pixel = 24; 215 cmap_entries = 0; 216 } 217 } else if (cinfo->out_color_space == JCS_RGB565) { 218 bits_per_pixel = 24; 219 cmap_entries = 0; 220 } else { 221 /* Grayscale output. We need to fake a 256-entry colormap. */ 222 bits_per_pixel = 8; 223 cmap_entries = 256; 224 } 225 /* File size */ 226 headersize = 14 + 40 + cmap_entries * 4; /* Header and colormap */ 227 bfSize = headersize + (long) dest->row_width * (long) cinfo->output_height; 228 229 /* Set unused fields of header to 0 */ 230 MEMZERO(bmpfileheader, sizeof(bmpfileheader)); 231 MEMZERO(bmpinfoheader, sizeof(bmpinfoheader)); 232 233 /* Fill the file header */ 234 bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ 235 bmpfileheader[1] = 0x4D; 236 PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */ 237 /* we leave bfReserved1 & bfReserved2 = 0 */ 238 PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */ 239 240 /* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */ 241 PUT_2B(bmpinfoheader, 0, 40); /* biSize */ 242 PUT_4B(bmpinfoheader, 4, cinfo->output_width); /* biWidth */ 243 PUT_4B(bmpinfoheader, 8, cinfo->output_height); /* biHeight */ 244 PUT_2B(bmpinfoheader, 12, 1); /* biPlanes - must be 1 */ 245 PUT_2B(bmpinfoheader, 14, bits_per_pixel); /* biBitCount */ 246 /* we leave biCompression = 0, for none */ 247 /* we leave biSizeImage = 0; this is correct for uncompressed data */ 248 if (cinfo->density_unit == 2) { /* if have density in dots/cm, then */ 249 PUT_4B(bmpinfoheader, 24, (long) (cinfo->X_density*100)); /* XPels/M */ 250 PUT_4B(bmpinfoheader, 28, (long) (cinfo->Y_density*100)); /* XPels/M */ 251 } 252 PUT_2B(bmpinfoheader, 32, cmap_entries); /* biClrUsed */ 253 /* we leave biClrImportant = 0 */ 254 255 if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14) 256 ERREXIT(cinfo, JERR_FILE_WRITE); 257 if (JFWRITE(dest->pub.output_file, bmpinfoheader, 40) != (size_t) 40) 258 ERREXIT(cinfo, JERR_FILE_WRITE); 259 260 if (cmap_entries > 0) 261 write_colormap(cinfo, dest, cmap_entries, 4); 262} 263 264 265LOCAL(void) 266write_os2_header (j_decompress_ptr cinfo, bmp_dest_ptr dest) 267/* Write an OS2-style BMP file header, including colormap if needed */ 268{ 269 char bmpfileheader[14]; 270 char bmpcoreheader[12]; 271 long headersize, bfSize; 272 int bits_per_pixel, cmap_entries; 273 274 /* Compute colormap size and total file size */ 275 if (cinfo->out_color_space == JCS_RGB) { 276 if (cinfo->quantize_colors) { 277 /* Colormapped RGB */ 278 bits_per_pixel = 8; 279 cmap_entries = 256; 280 } else { 281 /* Unquantized, full color RGB */ 282 bits_per_pixel = 24; 283 cmap_entries = 0; 284 } 285 } else if (cinfo->out_color_space == JCS_RGB565) { 286 bits_per_pixel = 24; 287 cmap_entries = 0; 288 } else { 289 /* Grayscale output. We need to fake a 256-entry colormap. */ 290 bits_per_pixel = 8; 291 cmap_entries = 256; 292 } 293 /* File size */ 294 headersize = 14 + 12 + cmap_entries * 3; /* Header and colormap */ 295 bfSize = headersize + (long) dest->row_width * (long) cinfo->output_height; 296 297 /* Set unused fields of header to 0 */ 298 MEMZERO(bmpfileheader, sizeof(bmpfileheader)); 299 MEMZERO(bmpcoreheader, sizeof(bmpcoreheader)); 300 301 /* Fill the file header */ 302 bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ 303 bmpfileheader[1] = 0x4D; 304 PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */ 305 /* we leave bfReserved1 & bfReserved2 = 0 */ 306 PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */ 307 308 /* Fill the info header (Microsoft calls this a BITMAPCOREHEADER) */ 309 PUT_2B(bmpcoreheader, 0, 12); /* bcSize */ 310 PUT_2B(bmpcoreheader, 4, cinfo->output_width); /* bcWidth */ 311 PUT_2B(bmpcoreheader, 6, cinfo->output_height); /* bcHeight */ 312 PUT_2B(bmpcoreheader, 8, 1); /* bcPlanes - must be 1 */ 313 PUT_2B(bmpcoreheader, 10, bits_per_pixel); /* bcBitCount */ 314 315 if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14) 316 ERREXIT(cinfo, JERR_FILE_WRITE); 317 if (JFWRITE(dest->pub.output_file, bmpcoreheader, 12) != (size_t) 12) 318 ERREXIT(cinfo, JERR_FILE_WRITE); 319 320 if (cmap_entries > 0) 321 write_colormap(cinfo, dest, cmap_entries, 3); 322} 323 324 325/* 326 * Write the colormap. 327 * Windows uses BGR0 map entries; OS/2 uses BGR entries. 328 */ 329 330LOCAL(void) 331write_colormap (j_decompress_ptr cinfo, bmp_dest_ptr dest, 332 int map_colors, int map_entry_size) 333{ 334 JSAMPARRAY colormap = cinfo->colormap; 335 int num_colors = cinfo->actual_number_of_colors; 336 FILE *outfile = dest->pub.output_file; 337 int i; 338 339 if (colormap != NULL) { 340 if (cinfo->out_color_components == 3) { 341 /* Normal case with RGB colormap */ 342 for (i = 0; i < num_colors; i++) { 343 putc(GETJSAMPLE(colormap[2][i]), outfile); 344 putc(GETJSAMPLE(colormap[1][i]), outfile); 345 putc(GETJSAMPLE(colormap[0][i]), outfile); 346 if (map_entry_size == 4) 347 putc(0, outfile); 348 } 349 } else { 350 /* Grayscale colormap (only happens with grayscale quantization) */ 351 for (i = 0; i < num_colors; i++) { 352 putc(GETJSAMPLE(colormap[0][i]), outfile); 353 putc(GETJSAMPLE(colormap[0][i]), outfile); 354 putc(GETJSAMPLE(colormap[0][i]), outfile); 355 if (map_entry_size == 4) 356 putc(0, outfile); 357 } 358 } 359 } else { 360 /* If no colormap, must be grayscale data. Generate a linear "map". */ 361 for (i = 0; i < 256; i++) { 362 putc(i, outfile); 363 putc(i, outfile); 364 putc(i, outfile); 365 if (map_entry_size == 4) 366 putc(0, outfile); 367 } 368 } 369 /* Pad colormap with zeros to ensure specified number of colormap entries */ 370 if (i > map_colors) 371 ERREXIT1(cinfo, JERR_TOO_MANY_COLORS, i); 372 for (; i < map_colors; i++) { 373 putc(0, outfile); 374 putc(0, outfile); 375 putc(0, outfile); 376 if (map_entry_size == 4) 377 putc(0, outfile); 378 } 379} 380 381 382METHODDEF(void) 383finish_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) 384{ 385 bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; 386 register FILE *outfile = dest->pub.output_file; 387 JSAMPARRAY image_ptr; 388 register JSAMPROW data_ptr; 389 JDIMENSION row; 390 register JDIMENSION col; 391 cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; 392 393 /* Write the header and colormap */ 394 if (dest->is_os2) 395 write_os2_header(cinfo, dest); 396 else 397 write_bmp_header(cinfo, dest); 398 399 /* Write the file body from our virtual array */ 400 for (row = cinfo->output_height; row > 0; row--) { 401 if (progress != NULL) { 402 progress->pub.pass_counter = (long) (cinfo->output_height - row); 403 progress->pub.pass_limit = (long) cinfo->output_height; 404 (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); 405 } 406 image_ptr = (*cinfo->mem->access_virt_sarray) 407 ((j_common_ptr) cinfo, dest->whole_image, row-1, (JDIMENSION) 1, FALSE); 408 data_ptr = image_ptr[0]; 409 for (col = dest->row_width; col > 0; col--) { 410 putc(GETJSAMPLE(*data_ptr), outfile); 411 data_ptr++; 412 } 413 } 414 if (progress != NULL) 415 progress->completed_extra_passes++; 416 417 /* Make sure we wrote the output file OK */ 418 fflush(outfile); 419 if (ferror(outfile)) 420 ERREXIT(cinfo, JERR_FILE_WRITE); 421} 422 423 424/* 425 * The module selection routine for BMP format output. 426 */ 427 428GLOBAL(djpeg_dest_ptr) 429jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2) 430{ 431 bmp_dest_ptr dest; 432 JDIMENSION row_width; 433 434 /* Create module interface object, fill in method pointers */ 435 dest = (bmp_dest_ptr) 436 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, 437 sizeof(bmp_dest_struct)); 438 dest->pub.start_output = start_output_bmp; 439 dest->pub.finish_output = finish_output_bmp; 440 dest->pub.calc_buffer_dimensions = NULL; 441 dest->is_os2 = is_os2; 442 443 if (cinfo->out_color_space == JCS_GRAYSCALE) { 444 dest->pub.put_pixel_rows = put_gray_rows; 445 } else if (cinfo->out_color_space == JCS_RGB) { 446 if (cinfo->quantize_colors) 447 dest->pub.put_pixel_rows = put_gray_rows; 448 else 449 dest->pub.put_pixel_rows = put_pixel_rows; 450 } else if (cinfo->out_color_space == JCS_RGB565) { 451 dest->pub.put_pixel_rows = put_pixel_rows; 452 } else { 453 ERREXIT(cinfo, JERR_BMP_COLORSPACE); 454 } 455 456 /* Calculate output image dimensions so we can allocate space */ 457 jpeg_calc_output_dimensions(cinfo); 458 459 /* Determine width of rows in the BMP file (padded to 4-byte boundary). */ 460 if (cinfo->out_color_space == JCS_RGB565) { 461 row_width = cinfo->output_width * 2; 462 dest->row_width = dest->data_width = cinfo->output_width * 3; 463 } else { 464 row_width = cinfo->output_width * cinfo->output_components; 465 dest->row_width = dest->data_width = row_width; 466 } 467 while ((dest->row_width & 3) != 0) dest->row_width++; 468 dest->pad_bytes = (int) (dest->row_width - dest->data_width); 469 if (cinfo->out_color_space == JCS_RGB565) { 470 while ((row_width & 3) != 0) row_width++; 471 } else { 472 row_width = dest->row_width; 473 } 474 475 476 /* Allocate space for inversion array, prepare for write pass */ 477 dest->whole_image = (*cinfo->mem->request_virt_sarray) 478 ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, 479 dest->row_width, cinfo->output_height, (JDIMENSION) 1); 480 dest->cur_output_row = 0; 481 if (cinfo->progress != NULL) { 482 cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; 483 progress->total_extra_passes++; /* count file input as separate pass */ 484 } 485 486 /* Create decompressor output buffer. */ 487 dest->pub.buffer = (*cinfo->mem->alloc_sarray) 488 ((j_common_ptr) cinfo, JPOOL_IMAGE, row_width, (JDIMENSION) 1); 489 dest->pub.buffer_height = 1; 490 491 return (djpeg_dest_ptr) dest; 492} 493 494#endif /* BMP_SUPPORTED */ 495