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.
8 * This file contains routines to write output images in Microsoft "BMP"
9 * format (MS Windows 3.x and OS/2 1.x flavors).
10 * Either 8-bit colormapped or 24-bit full-color format can be written.
11 * No compression is supported.
13 * These routines may need modification for non-Unix environments or
14 * specialized applications. As they stand, they assume output to
15 * an ordinary stdio stream.
17 * This code contributed by James Arthur Boucher.
20 #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
26 * To support 12-bit JPEG data, we'd have to scale output down to 8 bits.
27 * This is not yet implemented.
30 #if BITS_IN_JSAMPLE != 8
31 Sorry
, this code only copes with
8-bit JSAMPLEs
. /* deliberate syntax err */
35 * Since BMP stores scanlines bottom-to-top, we have to invert the image
36 * from JPEG's top-to-bottom order. To do this, we save the outgoing data
37 * in a virtual array during put_pixel_row calls, then actually emit the
38 * BMP file during finish_output. The virtual array contains one JSAMPLE per
39 * pixel if the output is grayscale or colormapped, three if it is full color.
42 /* Private version of data destination object */
45 struct djpeg_dest_struct pub
; /* public fields */
47 boolean is_os2
; /* saves the OS2 format request flag */
49 jvirt_sarray_ptr whole_image
; /* needed to reverse row order */
50 JDIMENSION data_width
; /* JSAMPLEs per row */
51 JDIMENSION row_width
; /* physical width of one row in the BMP file */
52 int pad_bytes
; /* number of padding bytes needed per row */
53 JDIMENSION cur_output_row
; /* next row# to write to virtual array */
56 typedef bmp_dest_struct
* bmp_dest_ptr
;
59 /* Forward declarations */
60 LOCAL(void) write_colormap
61 JPP((j_decompress_ptr cinfo
, bmp_dest_ptr dest
,
62 int map_colors
, int map_entry_size
));
66 * Write some pixel data.
67 * In this module rows_supplied will always be 1.
71 put_pixel_rows (j_decompress_ptr cinfo
, djpeg_dest_ptr dinfo
,
72 JDIMENSION rows_supplied
)
73 /* This version is for writing 24-bit pixels */
75 bmp_dest_ptr dest
= (bmp_dest_ptr
) dinfo
;
77 register JSAMPROW inptr
, outptr
;
78 register JDIMENSION col
;
81 /* Access next row in virtual array */
82 image_ptr
= (*cinfo
->mem
->access_virt_sarray
)
83 ((j_common_ptr
) cinfo
, dest
->whole_image
,
84 dest
->cur_output_row
, (JDIMENSION
) 1, TRUE
);
85 dest
->cur_output_row
++;
87 /* Transfer data. Note destination values must be in BGR order
88 * (even though Microsoft's own documents say the opposite).
90 inptr
= dest
->pub
.buffer
[0];
91 outptr
= image_ptr
[0];
92 for (col
= cinfo
->output_width
; col
> 0; col
--) {
93 outptr
[2] = *inptr
++; /* can omit GETJSAMPLE() safely */
99 /* Zero out the pad bytes. */
100 pad
= dest
->pad_bytes
;
106 put_gray_rows (j_decompress_ptr cinfo
, djpeg_dest_ptr dinfo
,
107 JDIMENSION rows_supplied
)
108 /* This version is for grayscale OR quantized color output */
110 bmp_dest_ptr dest
= (bmp_dest_ptr
) dinfo
;
111 JSAMPARRAY image_ptr
;
112 register JSAMPROW inptr
, outptr
;
113 register JDIMENSION col
;
116 /* Access next row in virtual array */
117 image_ptr
= (*cinfo
->mem
->access_virt_sarray
)
118 ((j_common_ptr
) cinfo
, dest
->whole_image
,
119 dest
->cur_output_row
, (JDIMENSION
) 1, TRUE
);
120 dest
->cur_output_row
++;
123 inptr
= dest
->pub
.buffer
[0];
124 outptr
= image_ptr
[0];
125 for (col
= cinfo
->output_width
; col
> 0; col
--) {
126 *outptr
++ = *inptr
++; /* can omit GETJSAMPLE() safely */
129 /* Zero out the pad bytes. */
130 pad
= dest
->pad_bytes
;
137 * Startup: normally writes the file header.
138 * In this module we may as well postpone everything until finish_output.
142 start_output_bmp (j_decompress_ptr cinfo
, djpeg_dest_ptr dinfo
)
149 * Finish up at the end of the file.
151 * Here is where we really output the BMP file.
153 * First, routines to write the Windows and OS/2 variants of the file header.
157 write_bmp_header (j_decompress_ptr cinfo
, bmp_dest_ptr dest
)
158 /* Write a Windows-style BMP file header, including colormap if needed */
160 char bmpfileheader
[14];
161 char bmpinfoheader
[40];
162 #define PUT_2B(array,offset,value) \
163 (array[offset] = (char) ((value) & 0xFF), \
164 array[offset+1] = (char) (((value) >> 8) & 0xFF))
165 #define PUT_4B(array,offset,value) \
166 (array[offset] = (char) ((value) & 0xFF), \
167 array[offset+1] = (char) (((value) >> 8) & 0xFF), \
168 array[offset+2] = (char) (((value) >> 16) & 0xFF), \
169 array[offset+3] = (char) (((value) >> 24) & 0xFF))
170 INT32 headersize
, bfSize
;
171 int bits_per_pixel
, cmap_entries
;
173 /* Compute colormap size and total file size */
174 if (cinfo
->out_color_space
== JCS_RGB
) {
175 if (cinfo
->quantize_colors
) {
176 /* Colormapped RGB */
180 /* Unquantized, full color RGB */
185 /* Grayscale output. We need to fake a 256-entry colormap. */
190 headersize
= 14 + 40 + cmap_entries
* 4; /* Header and colormap */
191 bfSize
= headersize
+ (INT32
) dest
->row_width
* (INT32
) cinfo
->output_height
;
193 /* Set unused fields of header to 0 */
194 MEMZERO(bmpfileheader
, SIZEOF(bmpfileheader
));
195 MEMZERO(bmpinfoheader
, SIZEOF(bmpinfoheader
));
197 /* Fill the file header */
198 bmpfileheader
[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */
199 bmpfileheader
[1] = 0x4D;
200 PUT_4B(bmpfileheader
, 2, bfSize
); /* bfSize */
201 /* we leave bfReserved1 & bfReserved2 = 0 */
202 PUT_4B(bmpfileheader
, 10, headersize
); /* bfOffBits */
204 /* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */
205 PUT_2B(bmpinfoheader
, 0, 40); /* biSize */
206 PUT_4B(bmpinfoheader
, 4, cinfo
->output_width
); /* biWidth */
207 PUT_4B(bmpinfoheader
, 8, cinfo
->output_height
); /* biHeight */
208 PUT_2B(bmpinfoheader
, 12, 1); /* biPlanes - must be 1 */
209 PUT_2B(bmpinfoheader
, 14, bits_per_pixel
); /* biBitCount */
210 /* we leave biCompression = 0, for none */
211 /* we leave biSizeImage = 0; this is correct for uncompressed data */
212 if (cinfo
->density_unit
== 2) { /* if have density in dots/cm, then */
213 PUT_4B(bmpinfoheader
, 24, (INT32
) (cinfo
->X_density
*100)); /* XPels/M */
214 PUT_4B(bmpinfoheader
, 28, (INT32
) (cinfo
->Y_density
*100)); /* XPels/M */
216 PUT_2B(bmpinfoheader
, 32, cmap_entries
); /* biClrUsed */
217 /* we leave biClrImportant = 0 */
219 if (JFWRITE(dest
->pub
.output_file
, bmpfileheader
, 14) != (size_t) 14)
220 ERREXIT(cinfo
, JERR_FILE_WRITE
);
221 if (JFWRITE(dest
->pub
.output_file
, bmpinfoheader
, 40) != (size_t) 40)
222 ERREXIT(cinfo
, JERR_FILE_WRITE
);
224 if (cmap_entries
> 0)
225 write_colormap(cinfo
, dest
, cmap_entries
, 4);
230 write_os2_header (j_decompress_ptr cinfo
, bmp_dest_ptr dest
)
231 /* Write an OS2-style BMP file header, including colormap if needed */
233 char bmpfileheader
[14];
234 char bmpcoreheader
[12];
235 INT32 headersize
, bfSize
;
236 int bits_per_pixel
, cmap_entries
;
238 /* Compute colormap size and total file size */
239 if (cinfo
->out_color_space
== JCS_RGB
) {
240 if (cinfo
->quantize_colors
) {
241 /* Colormapped RGB */
245 /* Unquantized, full color RGB */
250 /* Grayscale output. We need to fake a 256-entry colormap. */
255 headersize
= 14 + 12 + cmap_entries
* 3; /* Header and colormap */
256 bfSize
= headersize
+ (INT32
) dest
->row_width
* (INT32
) cinfo
->output_height
;
258 /* Set unused fields of header to 0 */
259 MEMZERO(bmpfileheader
, SIZEOF(bmpfileheader
));
260 MEMZERO(bmpcoreheader
, SIZEOF(bmpcoreheader
));
262 /* Fill the file header */
263 bmpfileheader
[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */
264 bmpfileheader
[1] = 0x4D;
265 PUT_4B(bmpfileheader
, 2, bfSize
); /* bfSize */
266 /* we leave bfReserved1 & bfReserved2 = 0 */
267 PUT_4B(bmpfileheader
, 10, headersize
); /* bfOffBits */
269 /* Fill the info header (Microsoft calls this a BITMAPCOREHEADER) */
270 PUT_2B(bmpcoreheader
, 0, 12); /* bcSize */
271 PUT_2B(bmpcoreheader
, 4, cinfo
->output_width
); /* bcWidth */
272 PUT_2B(bmpcoreheader
, 6, cinfo
->output_height
); /* bcHeight */
273 PUT_2B(bmpcoreheader
, 8, 1); /* bcPlanes - must be 1 */
274 PUT_2B(bmpcoreheader
, 10, bits_per_pixel
); /* bcBitCount */
276 if (JFWRITE(dest
->pub
.output_file
, bmpfileheader
, 14) != (size_t) 14)
277 ERREXIT(cinfo
, JERR_FILE_WRITE
);
278 if (JFWRITE(dest
->pub
.output_file
, bmpcoreheader
, 12) != (size_t) 12)
279 ERREXIT(cinfo
, JERR_FILE_WRITE
);
281 if (cmap_entries
> 0)
282 write_colormap(cinfo
, dest
, cmap_entries
, 3);
287 * Write the colormap.
288 * Windows uses BGR0 map entries; OS/2 uses BGR entries.
292 write_colormap (j_decompress_ptr cinfo
, bmp_dest_ptr dest
,
293 int map_colors
, int map_entry_size
)
295 JSAMPARRAY colormap
= cinfo
->colormap
;
296 int num_colors
= cinfo
->actual_number_of_colors
;
297 FILE * outfile
= dest
->pub
.output_file
;
300 if (colormap
!= NULL
) {
301 if (cinfo
->out_color_components
== 3) {
302 /* Normal case with RGB colormap */
303 for (i
= 0; i
< num_colors
; i
++) {
304 putc(GETJSAMPLE(colormap
[2][i
]), outfile
);
305 putc(GETJSAMPLE(colormap
[1][i
]), outfile
);
306 putc(GETJSAMPLE(colormap
[0][i
]), outfile
);
307 if (map_entry_size
== 4)
311 /* Grayscale colormap (only happens with grayscale quantization) */
312 for (i
= 0; i
< num_colors
; i
++) {
313 putc(GETJSAMPLE(colormap
[0][i
]), outfile
);
314 putc(GETJSAMPLE(colormap
[0][i
]), outfile
);
315 putc(GETJSAMPLE(colormap
[0][i
]), outfile
);
316 if (map_entry_size
== 4)
321 /* If no colormap, must be grayscale data. Generate a linear "map". */
322 for (i
= 0; i
< 256; i
++) {
326 if (map_entry_size
== 4)
330 /* Pad colormap with zeros to ensure specified number of colormap entries */
332 ERREXIT1(cinfo
, JERR_TOO_MANY_COLORS
, i
);
333 for (; i
< map_colors
; i
++) {
337 if (map_entry_size
== 4)
344 finish_output_bmp (j_decompress_ptr cinfo
, djpeg_dest_ptr dinfo
)
346 bmp_dest_ptr dest
= (bmp_dest_ptr
) dinfo
;
347 register FILE * outfile
= dest
->pub
.output_file
;
348 JSAMPARRAY image_ptr
;
349 register JSAMPROW data_ptr
;
351 register JDIMENSION col
;
352 cd_progress_ptr progress
= (cd_progress_ptr
) cinfo
->progress
;
354 /* Write the header and colormap */
356 write_os2_header(cinfo
, dest
);
358 write_bmp_header(cinfo
, dest
);
360 /* Write the file body from our virtual array */
361 for (row
= cinfo
->output_height
; row
> 0; row
--) {
362 if (progress
!= NULL
) {
363 progress
->pub
.pass_counter
= (long) (cinfo
->output_height
- row
);
364 progress
->pub
.pass_limit
= (long) cinfo
->output_height
;
365 (*progress
->pub
.progress_monitor
) ((j_common_ptr
) cinfo
);
367 image_ptr
= (*cinfo
->mem
->access_virt_sarray
)
368 ((j_common_ptr
) cinfo
, dest
->whole_image
, row
-1, (JDIMENSION
) 1, FALSE
);
369 data_ptr
= image_ptr
[0];
370 for (col
= dest
->row_width
; col
> 0; col
--) {
371 putc(GETJSAMPLE(*data_ptr
), outfile
);
375 if (progress
!= NULL
)
376 progress
->completed_extra_passes
++;
378 /* Make sure we wrote the output file OK */
381 ERREXIT(cinfo
, JERR_FILE_WRITE
);
386 * The module selection routine for BMP format output.
389 GLOBAL(djpeg_dest_ptr
)
390 jinit_write_bmp (j_decompress_ptr cinfo
, boolean is_os2
)
393 JDIMENSION row_width
;
395 /* Create module interface object, fill in method pointers */
396 dest
= (bmp_dest_ptr
)
397 (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_IMAGE
,
398 SIZEOF(bmp_dest_struct
));
399 dest
->pub
.start_output
= start_output_bmp
;
400 dest
->pub
.finish_output
= finish_output_bmp
;
401 dest
->is_os2
= is_os2
;
403 if (cinfo
->out_color_space
== JCS_GRAYSCALE
) {
404 dest
->pub
.put_pixel_rows
= put_gray_rows
;
405 } else if (cinfo
->out_color_space
== JCS_RGB
) {
406 if (cinfo
->quantize_colors
)
407 dest
->pub
.put_pixel_rows
= put_gray_rows
;
409 dest
->pub
.put_pixel_rows
= put_pixel_rows
;
411 ERREXIT(cinfo
, JERR_BMP_COLORSPACE
);
414 /* Calculate output image dimensions so we can allocate space */
415 jpeg_calc_output_dimensions(cinfo
);
417 /* Determine width of rows in the BMP file (padded to 4-byte boundary). */
418 row_width
= cinfo
->output_width
* cinfo
->output_components
;
419 dest
->data_width
= row_width
;
420 while ((row_width
& 3) != 0) row_width
++;
421 dest
->row_width
= row_width
;
422 dest
->pad_bytes
= (int) (row_width
- dest
->data_width
);
424 /* Allocate space for inversion array, prepare for write pass */
425 dest
->whole_image
= (*cinfo
->mem
->request_virt_sarray
)
426 ((j_common_ptr
) cinfo
, JPOOL_IMAGE
, FALSE
,
427 row_width
, cinfo
->output_height
, (JDIMENSION
) 1);
428 dest
->cur_output_row
= 0;
429 if (cinfo
->progress
!= NULL
) {
430 cd_progress_ptr progress
= (cd_progress_ptr
) cinfo
->progress
;
431 progress
->total_extra_passes
++; /* count file input as separate pass */
434 /* Create decompressor output buffer. */
435 dest
->pub
.buffer
= (*cinfo
->mem
->alloc_sarray
)
436 ((j_common_ptr
) cinfo
, JPOOL_IMAGE
, row_width
, (JDIMENSION
) 1);
437 dest
->pub
.buffer_height
= 1;
439 return (djpeg_dest_ptr
) dest
;
442 #endif /* BMP_SUPPORTED */