source: trunk/third/jpeg/wrbmp.c @ 15227

Revision 15227, 13.6 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15226, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * wrbmp.c
3 *
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.
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
44typedef 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
56typedef bmp_dest_struct * bmp_dest_ptr;
57
58
59/* Forward declarations */
60LOCAL(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
70METHODDEF(void)
71put_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,
84     dest->cur_output_row, (JDIMENSION) 1, TRUE);
85  dest->cur_output_row++;
86
87  /* Transfer data.  Note destination values must be in BGR order
88   * (even though Microsoft's own documents say the opposite).
89   */
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 */
94    outptr[1] = *inptr++;
95    outptr[0] = *inptr++;
96    outptr += 3;
97  }
98
99  /* Zero out the pad bytes. */
100  pad = dest->pad_bytes;
101  while (--pad >= 0)
102    *outptr++ = 0;
103}
104
105METHODDEF(void)
106put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
107               JDIMENSION rows_supplied)
108/* This version is for grayscale OR quantized color output */
109{
110  bmp_dest_ptr dest = (bmp_dest_ptr) dinfo;
111  JSAMPARRAY image_ptr;
112  register JSAMPROW inptr, outptr;
113  register JDIMENSION col;
114  int pad;
115
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++;
121
122  /* Transfer data. */
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 */
127  }
128
129  /* Zero out the pad bytes. */
130  pad = dest->pad_bytes;
131  while (--pad >= 0)
132    *outptr++ = 0;
133}
134
135
136/*
137 * Startup: normally writes the file header.
138 * In this module we may as well postpone everything until finish_output.
139 */
140
141METHODDEF(void)
142start_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
143{
144  /* no work here */
145}
146
147
148/*
149 * Finish up at the end of the file.
150 *
151 * Here is where we really output the BMP file.
152 *
153 * First, routines to write the Windows and OS/2 variants of the file header.
154 */
155
156LOCAL(void)
157write_bmp_header (j_decompress_ptr cinfo, bmp_dest_ptr dest)
158/* Write a Windows-style BMP file header, including colormap if needed */
159{
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;
172
173  /* Compute colormap size and total file size */
174  if (cinfo->out_color_space == JCS_RGB) {
175    if (cinfo->quantize_colors) {
176      /* Colormapped RGB */
177      bits_per_pixel = 8;
178      cmap_entries = 256;
179    } else {
180      /* Unquantized, full color RGB */
181      bits_per_pixel = 24;
182      cmap_entries = 0;
183    }
184  } else {
185    /* Grayscale output.  We need to fake a 256-entry colormap. */
186    bits_per_pixel = 8;
187    cmap_entries = 256;
188  }
189  /* File size */
190  headersize = 14 + 40 + cmap_entries * 4; /* Header and colormap */
191  bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height;
192 
193  /* Set unused fields of header to 0 */
194  MEMZERO(bmpfileheader, SIZEOF(bmpfileheader));
195  MEMZERO(bmpinfoheader, SIZEOF(bmpinfoheader));
196
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 */
203
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 */
215  }
216  PUT_2B(bmpinfoheader, 32, cmap_entries); /* biClrUsed */
217  /* we leave biClrImportant = 0 */
218
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);
223
224  if (cmap_entries > 0)
225    write_colormap(cinfo, dest, cmap_entries, 4);
226}
227
228
229LOCAL(void)
230write_os2_header (j_decompress_ptr cinfo, bmp_dest_ptr dest)
231/* Write an OS2-style BMP file header, including colormap if needed */
232{
233  char bmpfileheader[14];
234  char bmpcoreheader[12];
235  INT32 headersize, bfSize;
236  int bits_per_pixel, cmap_entries;
237
238  /* Compute colormap size and total file size */
239  if (cinfo->out_color_space == JCS_RGB) {
240    if (cinfo->quantize_colors) {
241      /* Colormapped RGB */
242      bits_per_pixel = 8;
243      cmap_entries = 256;
244    } else {
245      /* Unquantized, full color RGB */
246      bits_per_pixel = 24;
247      cmap_entries = 0;
248    }
249  } else {
250    /* Grayscale output.  We need to fake a 256-entry colormap. */
251    bits_per_pixel = 8;
252    cmap_entries = 256;
253  }
254  /* File size */
255  headersize = 14 + 12 + cmap_entries * 3; /* Header and colormap */
256  bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height;
257 
258  /* Set unused fields of header to 0 */
259  MEMZERO(bmpfileheader, SIZEOF(bmpfileheader));
260  MEMZERO(bmpcoreheader, SIZEOF(bmpcoreheader));
261
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 */
268
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 */
275
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);
280
281  if (cmap_entries > 0)
282    write_colormap(cinfo, dest, cmap_entries, 3);
283}
284
285
286/*
287 * Write the colormap.
288 * Windows uses BGR0 map entries; OS/2 uses BGR entries.
289 */
290
291LOCAL(void)
292write_colormap (j_decompress_ptr cinfo, bmp_dest_ptr dest,
293                int map_colors, int map_entry_size)
294{
295  JSAMPARRAY colormap = cinfo->colormap;
296  int num_colors = cinfo->actual_number_of_colors;
297  FILE * outfile = dest->pub.output_file;
298  int i;
299
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)
308          putc(0, outfile);
309      }
310    } else {
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)
317          putc(0, outfile);
318      }
319    }
320  } else {
321    /* If no colormap, must be grayscale data.  Generate a linear "map". */
322    for (i = 0; i < 256; i++) {
323      putc(i, outfile);
324      putc(i, outfile);
325      putc(i, outfile);
326      if (map_entry_size == 4)
327        putc(0, outfile);
328    }
329  }
330  /* Pad colormap with zeros to ensure specified number of colormap entries */
331  if (i > map_colors)
332    ERREXIT1(cinfo, JERR_TOO_MANY_COLORS, i);
333  for (; i < map_colors; i++) {
334    putc(0, outfile);
335    putc(0, outfile);
336    putc(0, outfile);
337    if (map_entry_size == 4)
338      putc(0, outfile);
339  }
340}
341
342
343METHODDEF(void)
344finish_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
345{
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;
350  JDIMENSION row;
351  register JDIMENSION col;
352  cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
353
354  /* Write the header and colormap */
355  if (dest->is_os2)
356    write_os2_header(cinfo, dest);
357  else
358    write_bmp_header(cinfo, dest);
359
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);
366    }
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);
372      data_ptr++;
373    }
374  }
375  if (progress != NULL)
376    progress->completed_extra_passes++;
377
378  /* Make sure we wrote the output file OK */
379  fflush(outfile);
380  if (ferror(outfile))
381    ERREXIT(cinfo, JERR_FILE_WRITE);
382}
383
384
385/*
386 * The module selection routine for BMP format output.
387 */
388
389GLOBAL(djpeg_dest_ptr)
390jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2)
391{
392  bmp_dest_ptr dest;
393  JDIMENSION row_width;
394
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;
402
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;
408    else
409      dest->pub.put_pixel_rows = put_pixel_rows;
410  } else {
411    ERREXIT(cinfo, JERR_BMP_COLORSPACE);
412  }
413
414  /* Calculate output image dimensions so we can allocate space */
415  jpeg_calc_output_dimensions(cinfo);
416
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);
423
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 */
432  }
433
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;
438
439  return (djpeg_dest_ptr) dest;
440}
441
442#endif /* BMP_SUPPORTED */
Note: See TracBrowser for help on using the repository browser.