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