8 * Copyright (C) 1994-1996, Thomas G. Lane.
9 * This file is part of the Independent JPEG Group's software.
10 * For conditions of distribution and use, see the accompanying README file.
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.
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.
21 * This code contributed by James Arthur Boucher.
24 #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
30 * To support 12-bit JPEG data, we'd have to scale output down to 8 bits.
31 * This is not yet implemented.
34 #if BITS_IN_JSAMPLE != 8
35 Sorry
, this code only copes with
8-bit JSAMPLEs
. /* deliberate syntax err */
39 * Since BMP stores scanlines bottom-to-top, we have to invert the image
40 * from JPEG's top-to-bottom order. To do this, we save the outgoing data
41 * in a virtual array during put_pixel_row calls, then actually emit the
42 * BMP file during finish_output. The virtual array contains one JSAMPLE per
43 * pixel if the output is grayscale or colormapped, three if it is full color.
46 /* Private version of data destination object */
49 struct djpeg_dest_struct pub
; /* public fields */
51 boolean is_os2
; /* saves the OS2 format request flag */
53 jvirt_sarray_ptr whole_image
; /* needed to reverse row order */
54 JDIMENSION data_width
; /* JSAMPLEs per row */
55 JDIMENSION row_width
; /* physical width of one row in the BMP file */
56 int pad_bytes
; /* number of padding bytes needed per row */
57 JDIMENSION cur_output_row
; /* next row# to write to virtual array */
60 typedef bmp_dest_struct
* bmp_dest_ptr
;
63 /* Forward declarations */
64 LOCAL(void) write_colormap
65 JPP((j_decompress_ptr cinfo
, bmp_dest_ptr dest
,
66 int map_colors
, int map_entry_size
));
70 * Write some pixel data.
71 * In this module rows_supplied will always be 1.
75 put_pixel_rows (j_decompress_ptr cinfo
, djpeg_dest_ptr dinfo
,
76 JDIMENSION rows_supplied
)
77 /* This version is for writing 24-bit pixels */
79 bmp_dest_ptr dest
= (bmp_dest_ptr
) dinfo
;
81 register JSAMPROW inptr
, outptr
;
82 register JDIMENSION col
;
85 /* Access next row in virtual array */
86 image_ptr
= (*cinfo
->mem
->access_virt_sarray
)
87 ((j_common_ptr
) cinfo
, dest
->whole_image
,
88 dest
->cur_output_row
, (JDIMENSION
) 1, TRUE
);
89 dest
->cur_output_row
++;
91 /* Transfer data. Note destination values must be in BGR order
92 * (even though Microsoft's own documents say the opposite).
94 inptr
= dest
->pub
.buffer
[0];
95 outptr
= image_ptr
[0];
96 for (col
= cinfo
->output_width
; col
> 0; col
--) {
97 outptr
[2] = *inptr
++; /* can omit GETJSAMPLE() safely */
103 /* Zero out the pad bytes. */
104 pad
= dest
->pad_bytes
;
110 put_gray_rows (j_decompress_ptr cinfo
, djpeg_dest_ptr dinfo
,
111 JDIMENSION rows_supplied
)
112 /* This version is for grayscale OR quantized color output */
114 bmp_dest_ptr dest
= (bmp_dest_ptr
) dinfo
;
115 JSAMPARRAY image_ptr
;
116 register JSAMPROW inptr
, outptr
;
117 register JDIMENSION col
;
120 /* Access next row in virtual array */
121 image_ptr
= (*cinfo
->mem
->access_virt_sarray
)
122 ((j_common_ptr
) cinfo
, dest
->whole_image
,
123 dest
->cur_output_row
, (JDIMENSION
) 1, TRUE
);
124 dest
->cur_output_row
++;
127 inptr
= dest
->pub
.buffer
[0];
128 outptr
= image_ptr
[0];
129 for (col
= cinfo
->output_width
; col
> 0; col
--) {
130 *outptr
++ = *inptr
++; /* can omit GETJSAMPLE() safely */
133 /* Zero out the pad bytes. */
134 pad
= dest
->pad_bytes
;
141 * Startup: normally writes the file header.
142 * In this module we may as well postpone everything until finish_output.
146 start_output_bmp (j_decompress_ptr cinfo
, djpeg_dest_ptr dinfo
)
153 * Finish up at the end of the file.
155 * Here is where we really output the BMP file.
157 * First, routines to write the Windows and OS/2 variants of the file header.
161 write_bmp_header (j_decompress_ptr cinfo
, bmp_dest_ptr dest
)
162 /* Write a Windows-style BMP file header, including colormap if needed */
164 char bmpfileheader
[14];
165 char bmpinfoheader
[40];
166 #define PUT_2B(array,offset,value) \
167 (array[offset] = (char) ((value) & 0xFF), \
168 array[offset+1] = (char) (((value) >> 8) & 0xFF))
169 #define PUT_4B(array,offset,value) \
170 (array[offset] = (char) ((value) & 0xFF), \
171 array[offset+1] = (char) (((value) >> 8) & 0xFF), \
172 array[offset+2] = (char) (((value) >> 16) & 0xFF), \
173 array[offset+3] = (char) (((value) >> 24) & 0xFF))
174 INT32 headersize
, bfSize
;
175 int bits_per_pixel
, cmap_entries
;
177 /* Compute colormap size and total file size */
178 if (cinfo
->out_color_space
== JCS_RGB
) {
179 if (cinfo
->quantize_colors
) {
180 /* Colormapped RGB */
184 /* Unquantized, full color RGB */
189 /* Grayscale output. We need to fake a 256-entry colormap. */
194 headersize
= 14 + 40 + cmap_entries
* 4; /* Header and colormap */
195 bfSize
= headersize
+ (INT32
) dest
->row_width
* (INT32
) cinfo
->output_height
;
197 /* Set unused fields of header to 0 */
198 MEMZERO(bmpfileheader
, SIZEOF(bmpfileheader
));
199 MEMZERO(bmpinfoheader
, SIZEOF(bmpinfoheader
));
201 /* Fill the file header */
202 bmpfileheader
[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */
203 bmpfileheader
[1] = 0x4D;
204 PUT_4B(bmpfileheader
, 2, bfSize
); /* bfSize */
205 /* we leave bfReserved1 & bfReserved2 = 0 */
206 PUT_4B(bmpfileheader
, 10, headersize
); /* bfOffBits */
208 /* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */
209 PUT_2B(bmpinfoheader
, 0, 40); /* biSize */
210 PUT_4B(bmpinfoheader
, 4, cinfo
->output_width
); /* biWidth */
211 PUT_4B(bmpinfoheader
, 8, cinfo
->output_height
); /* biHeight */
212 PUT_2B(bmpinfoheader
, 12, 1); /* biPlanes - must be 1 */
213 PUT_2B(bmpinfoheader
, 14, bits_per_pixel
); /* biBitCount */
214 /* we leave biCompression = 0, for none */
215 /* we leave biSizeImage = 0; this is correct for uncompressed data */
216 if (cinfo
->density_unit
== 2) { /* if have density in dots/cm, then */
217 PUT_4B(bmpinfoheader
, 24, (INT32
) (cinfo
->X_density
*100)); /* XPels/M */
218 PUT_4B(bmpinfoheader
, 28, (INT32
) (cinfo
->Y_density
*100)); /* XPels/M */
220 PUT_2B(bmpinfoheader
, 32, cmap_entries
); /* biClrUsed */
221 /* we leave biClrImportant = 0 */
223 if (JFWRITE(dest
->pub
.output_file
, bmpfileheader
, 14) != (size_t) 14)
224 ERREXIT(cinfo
, JERR_FILE_WRITE
);
225 if (JFWRITE(dest
->pub
.output_file
, bmpinfoheader
, 40) != (size_t) 40)
226 ERREXIT(cinfo
, JERR_FILE_WRITE
);
228 if (cmap_entries
> 0)
229 write_colormap(cinfo
, dest
, cmap_entries
, 4);
234 write_os2_header (j_decompress_ptr cinfo
, bmp_dest_ptr dest
)
235 /* Write an OS2-style BMP file header, including colormap if needed */
237 char bmpfileheader
[14];
238 char bmpcoreheader
[12];
239 INT32 headersize
, bfSize
;
240 int bits_per_pixel
, cmap_entries
;
242 /* Compute colormap size and total file size */
243 if (cinfo
->out_color_space
== JCS_RGB
) {
244 if (cinfo
->quantize_colors
) {
245 /* Colormapped RGB */
249 /* Unquantized, full color RGB */
254 /* Grayscale output. We need to fake a 256-entry colormap. */
259 headersize
= 14 + 12 + cmap_entries
* 3; /* Header and colormap */
260 bfSize
= headersize
+ (INT32
) dest
->row_width
* (INT32
) cinfo
->output_height
;
262 /* Set unused fields of header to 0 */
263 MEMZERO(bmpfileheader
, SIZEOF(bmpfileheader
));
264 MEMZERO(bmpcoreheader
, SIZEOF(bmpcoreheader
));
266 /* Fill the file header */
267 bmpfileheader
[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */
268 bmpfileheader
[1] = 0x4D;
269 PUT_4B(bmpfileheader
, 2, bfSize
); /* bfSize */
270 /* we leave bfReserved1 & bfReserved2 = 0 */
271 PUT_4B(bmpfileheader
, 10, headersize
); /* bfOffBits */
273 /* Fill the info header (Microsoft calls this a BITMAPCOREHEADER) */
274 PUT_2B(bmpcoreheader
, 0, 12); /* bcSize */
275 PUT_2B(bmpcoreheader
, 4, cinfo
->output_width
); /* bcWidth */
276 PUT_2B(bmpcoreheader
, 6, cinfo
->output_height
); /* bcHeight */
277 PUT_2B(bmpcoreheader
, 8, 1); /* bcPlanes - must be 1 */
278 PUT_2B(bmpcoreheader
, 10, bits_per_pixel
); /* bcBitCount */
280 if (JFWRITE(dest
->pub
.output_file
, bmpfileheader
, 14) != (size_t) 14)
281 ERREXIT(cinfo
, JERR_FILE_WRITE
);
282 if (JFWRITE(dest
->pub
.output_file
, bmpcoreheader
, 12) != (size_t) 12)
283 ERREXIT(cinfo
, JERR_FILE_WRITE
);
285 if (cmap_entries
> 0)
286 write_colormap(cinfo
, dest
, cmap_entries
, 3);
291 * Write the colormap.
292 * Windows uses BGR0 map entries; OS/2 uses BGR entries.
296 write_colormap (j_decompress_ptr cinfo
, bmp_dest_ptr dest
,
297 int map_colors
, int map_entry_size
)
299 JSAMPARRAY colormap
= cinfo
->colormap
;
300 int num_colors
= cinfo
->actual_number_of_colors
;
301 FILE * outfile
= dest
->pub
.output_file
;
304 if (colormap
!= NULL
) {
305 if (cinfo
->out_color_components
== 3) {
306 /* Normal case with RGB colormap */
307 for (i
= 0; i
< num_colors
; i
++) {
308 putc(GETJSAMPLE(colormap
[2][i
]), outfile
);
309 putc(GETJSAMPLE(colormap
[1][i
]), outfile
);
310 putc(GETJSAMPLE(colormap
[0][i
]), outfile
);
311 if (map_entry_size
== 4)
315 /* Grayscale colormap (only happens with grayscale quantization) */
316 for (i
= 0; i
< num_colors
; i
++) {
317 putc(GETJSAMPLE(colormap
[0][i
]), outfile
);
318 putc(GETJSAMPLE(colormap
[0][i
]), outfile
);
319 putc(GETJSAMPLE(colormap
[0][i
]), outfile
);
320 if (map_entry_size
== 4)
325 /* If no colormap, must be grayscale data. Generate a linear "map". */
326 for (i
= 0; i
< 256; i
++) {
330 if (map_entry_size
== 4)
334 /* Pad colormap with zeros to ensure specified number of colormap entries */
336 ERREXIT1(cinfo
, JERR_TOO_MANY_COLORS
, i
);
337 for (; i
< map_colors
; i
++) {
341 if (map_entry_size
== 4)
348 finish_output_bmp (j_decompress_ptr cinfo
, djpeg_dest_ptr dinfo
)
350 bmp_dest_ptr dest
= (bmp_dest_ptr
) dinfo
;
351 register FILE * outfile
= dest
->pub
.output_file
;
352 JSAMPARRAY image_ptr
;
353 register JSAMPROW data_ptr
;
355 register JDIMENSION col
;
356 cd_progress_ptr progress
= (cd_progress_ptr
) cinfo
->progress
;
358 /* Write the header and colormap */
360 write_os2_header(cinfo
, dest
);
362 write_bmp_header(cinfo
, dest
);
364 /* Write the file body from our virtual array */
365 for (row
= cinfo
->output_height
; row
> 0; row
--) {
366 if (progress
!= NULL
) {
367 progress
->pub
.pass_counter
= (long) (cinfo
->output_height
- row
);
368 progress
->pub
.pass_limit
= (long) cinfo
->output_height
;
369 (*progress
->pub
.progress_monitor
) ((j_common_ptr
) cinfo
);
371 image_ptr
= (*cinfo
->mem
->access_virt_sarray
)
372 ((j_common_ptr
) cinfo
, dest
->whole_image
, row
-1, (JDIMENSION
) 1, FALSE
);
373 data_ptr
= image_ptr
[0];
374 for (col
= dest
->row_width
; col
> 0; col
--) {
375 putc(GETJSAMPLE(*data_ptr
), outfile
);
379 if (progress
!= NULL
)
380 progress
->completed_extra_passes
++;
382 /* Make sure we wrote the output file OK */
385 ERREXIT(cinfo
, JERR_FILE_WRITE
);
390 * The module selection routine for BMP format output.
393 JGLOBAL(djpeg_dest_ptr
)
394 jinit_write_bmp (j_decompress_ptr cinfo
, boolean is_os2
)
397 JDIMENSION row_width
;
399 /* Create module interface object, fill in method pointers */
400 dest
= (bmp_dest_ptr
)
401 (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_IMAGE
,
402 SIZEOF(bmp_dest_struct
));
403 dest
->pub
.start_output
= start_output_bmp
;
404 dest
->pub
.finish_output
= finish_output_bmp
;
405 dest
->is_os2
= is_os2
;
407 if (cinfo
->out_color_space
== JCS_GRAYSCALE
) {
408 dest
->pub
.put_pixel_rows
= put_gray_rows
;
409 } else if (cinfo
->out_color_space
== JCS_RGB
) {
410 if (cinfo
->quantize_colors
)
411 dest
->pub
.put_pixel_rows
= put_gray_rows
;
413 dest
->pub
.put_pixel_rows
= put_pixel_rows
;
415 ERREXIT(cinfo
, JERR_BMP_COLORSPACE
);
418 /* Calculate output image dimensions so we can allocate space */
419 jpeg_calc_output_dimensions(cinfo
);
421 /* Determine width of rows in the BMP file (padded to 4-byte boundary). */
422 row_width
= cinfo
->output_width
* cinfo
->output_components
;
423 dest
->data_width
= row_width
;
424 while ((row_width
& 3) != 0) row_width
++;
425 dest
->row_width
= row_width
;
426 dest
->pad_bytes
= (int) (row_width
- dest
->data_width
);
428 /* Allocate space for inversion array, prepare for write pass */
429 dest
->whole_image
= (*cinfo
->mem
->request_virt_sarray
)
430 ((j_common_ptr
) cinfo
, JPOOL_IMAGE
, FALSE
,
431 row_width
, cinfo
->output_height
, (JDIMENSION
) 1);
432 dest
->cur_output_row
= 0;
433 if (cinfo
->progress
!= NULL
) {
434 cd_progress_ptr progress
= (cd_progress_ptr
) cinfo
->progress
;
435 progress
->total_extra_passes
++; /* count file input as separate pass */
438 /* Create decompressor output buffer. */
439 dest
->pub
.buffer
= (*cinfo
->mem
->alloc_sarray
)
440 ((j_common_ptr
) cinfo
, JPOOL_IMAGE
, row_width
, (JDIMENSION
) 1);
441 dest
->pub
.buffer_height
= 1;
443 return (djpeg_dest_ptr
) dest
;
446 #endif /* BMP_SUPPORTED */