Back to index

plt-scheme  4.2.1
wrbmp.c
Go to the documentation of this file.
00001 /*
00002  * wrbmp.c
00003  *
00004  * Copyright (C) 1994-1996, Thomas G. Lane.
00005  * This file is part of the Independent JPEG Group's software.
00006  * For conditions of distribution and use, see the accompanying README file.
00007  *
00008  * This file contains routines to write output images in Microsoft "BMP"
00009  * format (MS Windows 3.x and OS/2 1.x flavors).
00010  * Either 8-bit colormapped or 24-bit full-color format can be written.
00011  * No compression is supported.
00012  *
00013  * These routines may need modification for non-Unix environments or
00014  * specialized applications.  As they stand, they assume output to
00015  * an ordinary stdio stream.
00016  *
00017  * This code contributed by James Arthur Boucher.
00018  */
00019 
00020 #include "cdjpeg.h"         /* Common decls for cjpeg/djpeg applications */
00021 
00022 #ifdef BMP_SUPPORTED
00023 
00024 
00025 /*
00026  * To support 12-bit JPEG data, we'd have to scale output down to 8 bits.
00027  * This is not yet implemented.
00028  */
00029 
00030 #if BITS_IN_JSAMPLE != 8
00031   Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */
00032 #endif
00033 
00034 /*
00035  * Since BMP stores scanlines bottom-to-top, we have to invert the image
00036  * from JPEG's top-to-bottom order.  To do this, we save the outgoing data
00037  * in a virtual array during put_pixel_row calls, then actually emit the
00038  * BMP file during finish_output.  The virtual array contains one JSAMPLE per
00039  * pixel if the output is grayscale or colormapped, three if it is full color.
00040  */
00041 
00042 /* Private version of data destination object */
00043 
00044 typedef struct {
00045   struct djpeg_dest_struct pub;    /* public fields */
00046 
00047   boolean is_os2;           /* saves the OS2 format request flag */
00048 
00049   jvirt_sarray_ptr whole_image;    /* needed to reverse row order */
00050   JDIMENSION data_width;    /* JSAMPLEs per row */
00051   JDIMENSION row_width;            /* physical width of one row in the BMP file */
00052   int pad_bytes;            /* number of padding bytes needed per row */
00053   JDIMENSION cur_output_row;       /* next row# to write to virtual array */
00054 } bmp_dest_struct;
00055 
00056 typedef bmp_dest_struct * bmp_dest_ptr;
00057 
00058 
00059 /* Forward declarations */
00060 LOCAL(void) write_colormap
00061        JPP((j_decompress_ptr cinfo, bmp_dest_ptr dest,
00062             int map_colors, int map_entry_size));
00063 
00064 
00065 /*
00066  * Write some pixel data.
00067  * In this module rows_supplied will always be 1.
00068  */
00069 
00070 METHODDEF(void)
00071 put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
00072               JDIMENSION rows_supplied)
00073 /* This version is for writing 24-bit pixels */
00074 {
00075   bmp_dest_ptr dest = (bmp_dest_ptr) dinfo;
00076   JSAMPARRAY image_ptr;
00077   register JSAMPROW inptr, outptr;
00078   register JDIMENSION col;
00079   int pad;
00080 
00081   /* Access next row in virtual array */
00082   image_ptr = (*cinfo->mem->access_virt_sarray)
00083     ((j_common_ptr) cinfo, dest->whole_image,
00084      dest->cur_output_row, (JDIMENSION) 1, TRUE);
00085   dest->cur_output_row++;
00086 
00087   /* Transfer data.  Note destination values must be in BGR order
00088    * (even though Microsoft's own documents say the opposite).
00089    */
00090   inptr = dest->pub.buffer[0];
00091   outptr = image_ptr[0];
00092   for (col = cinfo->output_width; col > 0; col--) {
00093     outptr[2] = *inptr++;   /* can omit GETJSAMPLE() safely */
00094     outptr[1] = *inptr++;
00095     outptr[0] = *inptr++;
00096     outptr += 3;
00097   }
00098 
00099   /* Zero out the pad bytes. */
00100   pad = dest->pad_bytes;
00101   while (--pad >= 0)
00102     *outptr++ = 0;
00103 }
00104 
00105 METHODDEF(void)
00106 put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
00107               JDIMENSION rows_supplied)
00108 /* This version is for grayscale OR quantized color output */
00109 {
00110   bmp_dest_ptr dest = (bmp_dest_ptr) dinfo;
00111   JSAMPARRAY image_ptr;
00112   register JSAMPROW inptr, outptr;
00113   register JDIMENSION col;
00114   int pad;
00115 
00116   /* Access next row in virtual array */
00117   image_ptr = (*cinfo->mem->access_virt_sarray)
00118     ((j_common_ptr) cinfo, dest->whole_image,
00119      dest->cur_output_row, (JDIMENSION) 1, TRUE);
00120   dest->cur_output_row++;
00121 
00122   /* Transfer data. */
00123   inptr = dest->pub.buffer[0];
00124   outptr = image_ptr[0];
00125   for (col = cinfo->output_width; col > 0; col--) {
00126     *outptr++ = *inptr++;   /* can omit GETJSAMPLE() safely */
00127   }
00128 
00129   /* Zero out the pad bytes. */
00130   pad = dest->pad_bytes;
00131   while (--pad >= 0)
00132     *outptr++ = 0;
00133 }
00134 
00135 
00136 /*
00137  * Startup: normally writes the file header.
00138  * In this module we may as well postpone everything until finish_output.
00139  */
00140 
00141 METHODDEF(void)
00142 start_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
00143 {
00144   /* no work here */
00145 }
00146 
00147 
00148 /*
00149  * Finish up at the end of the file.
00150  *
00151  * Here is where we really output the BMP file.
00152  *
00153  * First, routines to write the Windows and OS/2 variants of the file header.
00154  */
00155 
00156 LOCAL(void)
00157 write_bmp_header (j_decompress_ptr cinfo, bmp_dest_ptr dest)
00158 /* Write a Windows-style BMP file header, including colormap if needed */
00159 {
00160   char bmpfileheader[14];
00161   char bmpinfoheader[40];
00162 #define PUT_2B(array,offset,value)  \
00163        (array[offset] = (char) ((value) & 0xFF), \
00164         array[offset+1] = (char) (((value) >> 8) & 0xFF))
00165 #define PUT_4B(array,offset,value)  \
00166        (array[offset] = (char) ((value) & 0xFF), \
00167         array[offset+1] = (char) (((value) >> 8) & 0xFF), \
00168         array[offset+2] = (char) (((value) >> 16) & 0xFF), \
00169         array[offset+3] = (char) (((value) >> 24) & 0xFF))
00170   INT32 headersize, bfSize;
00171   int bits_per_pixel, cmap_entries;
00172 
00173   /* Compute colormap size and total file size */
00174   if (cinfo->out_color_space == JCS_RGB) {
00175     if (cinfo->quantize_colors) {
00176       /* Colormapped RGB */
00177       bits_per_pixel = 8;
00178       cmap_entries = 256;
00179     } else {
00180       /* Unquantized, full color RGB */
00181       bits_per_pixel = 24;
00182       cmap_entries = 0;
00183     }
00184   } else {
00185     /* Grayscale output.  We need to fake a 256-entry colormap. */
00186     bits_per_pixel = 8;
00187     cmap_entries = 256;
00188   }
00189   /* File size */
00190   headersize = 14 + 40 + cmap_entries * 4; /* Header and colormap */
00191   bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height;
00192   
00193   /* Set unused fields of header to 0 */
00194   MEMZERO(bmpfileheader, SIZEOF(bmpfileheader));
00195   MEMZERO(bmpinfoheader, SIZEOF(bmpinfoheader));
00196 
00197   /* Fill the file header */
00198   bmpfileheader[0] = 0x42;  /* first 2 bytes are ASCII 'B', 'M' */
00199   bmpfileheader[1] = 0x4D;
00200   PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */
00201   /* we leave bfReserved1 & bfReserved2 = 0 */
00202   PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */
00203 
00204   /* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */
00205   PUT_2B(bmpinfoheader, 0, 40);    /* biSize */
00206   PUT_4B(bmpinfoheader, 4, cinfo->output_width); /* biWidth */
00207   PUT_4B(bmpinfoheader, 8, cinfo->output_height); /* biHeight */
00208   PUT_2B(bmpinfoheader, 12, 1);    /* biPlanes - must be 1 */
00209   PUT_2B(bmpinfoheader, 14, bits_per_pixel); /* biBitCount */
00210   /* we leave biCompression = 0, for none */
00211   /* we leave biSizeImage = 0; this is correct for uncompressed data */
00212   if (cinfo->density_unit == 2) { /* if have density in dots/cm, then */
00213     PUT_4B(bmpinfoheader, 24, (INT32) (cinfo->X_density*100)); /* XPels/M */
00214     PUT_4B(bmpinfoheader, 28, (INT32) (cinfo->Y_density*100)); /* XPels/M */
00215   }
00216   PUT_2B(bmpinfoheader, 32, cmap_entries); /* biClrUsed */
00217   /* we leave biClrImportant = 0 */
00218 
00219   if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14)
00220     ERREXIT(cinfo, JERR_FILE_WRITE);
00221   if (JFWRITE(dest->pub.output_file, bmpinfoheader, 40) != (size_t) 40)
00222     ERREXIT(cinfo, JERR_FILE_WRITE);
00223 
00224   if (cmap_entries > 0)
00225     write_colormap(cinfo, dest, cmap_entries, 4);
00226 }
00227 
00228 
00229 LOCAL(void)
00230 write_os2_header (j_decompress_ptr cinfo, bmp_dest_ptr dest)
00231 /* Write an OS2-style BMP file header, including colormap if needed */
00232 {
00233   char bmpfileheader[14];
00234   char bmpcoreheader[12];
00235   INT32 headersize, bfSize;
00236   int bits_per_pixel, cmap_entries;
00237 
00238   /* Compute colormap size and total file size */
00239   if (cinfo->out_color_space == JCS_RGB) {
00240     if (cinfo->quantize_colors) {
00241       /* Colormapped RGB */
00242       bits_per_pixel = 8;
00243       cmap_entries = 256;
00244     } else {
00245       /* Unquantized, full color RGB */
00246       bits_per_pixel = 24;
00247       cmap_entries = 0;
00248     }
00249   } else {
00250     /* Grayscale output.  We need to fake a 256-entry colormap. */
00251     bits_per_pixel = 8;
00252     cmap_entries = 256;
00253   }
00254   /* File size */
00255   headersize = 14 + 12 + cmap_entries * 3; /* Header and colormap */
00256   bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height;
00257   
00258   /* Set unused fields of header to 0 */
00259   MEMZERO(bmpfileheader, SIZEOF(bmpfileheader));
00260   MEMZERO(bmpcoreheader, SIZEOF(bmpcoreheader));
00261 
00262   /* Fill the file header */
00263   bmpfileheader[0] = 0x42;  /* first 2 bytes are ASCII 'B', 'M' */
00264   bmpfileheader[1] = 0x4D;
00265   PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */
00266   /* we leave bfReserved1 & bfReserved2 = 0 */
00267   PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */
00268 
00269   /* Fill the info header (Microsoft calls this a BITMAPCOREHEADER) */
00270   PUT_2B(bmpcoreheader, 0, 12);    /* bcSize */
00271   PUT_2B(bmpcoreheader, 4, cinfo->output_width); /* bcWidth */
00272   PUT_2B(bmpcoreheader, 6, cinfo->output_height); /* bcHeight */
00273   PUT_2B(bmpcoreheader, 8, 1);     /* bcPlanes - must be 1 */
00274   PUT_2B(bmpcoreheader, 10, bits_per_pixel); /* bcBitCount */
00275 
00276   if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14)
00277     ERREXIT(cinfo, JERR_FILE_WRITE);
00278   if (JFWRITE(dest->pub.output_file, bmpcoreheader, 12) != (size_t) 12)
00279     ERREXIT(cinfo, JERR_FILE_WRITE);
00280 
00281   if (cmap_entries > 0)
00282     write_colormap(cinfo, dest, cmap_entries, 3);
00283 }
00284 
00285 
00286 /*
00287  * Write the colormap.
00288  * Windows uses BGR0 map entries; OS/2 uses BGR entries.
00289  */
00290 
00291 LOCAL(void)
00292 write_colormap (j_decompress_ptr cinfo, bmp_dest_ptr dest,
00293               int map_colors, int map_entry_size)
00294 {
00295   JSAMPARRAY colormap = cinfo->colormap;
00296   int num_colors = cinfo->actual_number_of_colors;
00297   FILE * outfile = dest->pub.output_file;
00298   int i;
00299 
00300   if (colormap != NULL) {
00301     if (cinfo->out_color_components == 3) {
00302       /* Normal case with RGB colormap */
00303       for (i = 0; i < num_colors; i++) {
00304        putc(GETJSAMPLE(colormap[2][i]), outfile);
00305        putc(GETJSAMPLE(colormap[1][i]), outfile);
00306        putc(GETJSAMPLE(colormap[0][i]), outfile);
00307        if (map_entry_size == 4)
00308          putc(0, outfile);
00309       }
00310     } else {
00311       /* Grayscale colormap (only happens with grayscale quantization) */
00312       for (i = 0; i < num_colors; i++) {
00313        putc(GETJSAMPLE(colormap[0][i]), outfile);
00314        putc(GETJSAMPLE(colormap[0][i]), outfile);
00315        putc(GETJSAMPLE(colormap[0][i]), outfile);
00316        if (map_entry_size == 4)
00317          putc(0, outfile);
00318       }
00319     }
00320   } else {
00321     /* If no colormap, must be grayscale data.  Generate a linear "map". */
00322     for (i = 0; i < 256; i++) {
00323       putc(i, outfile);
00324       putc(i, outfile);
00325       putc(i, outfile);
00326       if (map_entry_size == 4)
00327        putc(0, outfile);
00328     }
00329   }
00330   /* Pad colormap with zeros to ensure specified number of colormap entries */ 
00331   if (i > map_colors)
00332     ERREXIT1(cinfo, JERR_TOO_MANY_COLORS, i);
00333   for (; i < map_colors; i++) {
00334     putc(0, outfile);
00335     putc(0, outfile);
00336     putc(0, outfile);
00337     if (map_entry_size == 4)
00338       putc(0, outfile);
00339   }
00340 }
00341 
00342 
00343 METHODDEF(void)
00344 finish_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
00345 {
00346   bmp_dest_ptr dest = (bmp_dest_ptr) dinfo;
00347   register FILE * outfile = dest->pub.output_file;
00348   JSAMPARRAY image_ptr;
00349   register JSAMPROW data_ptr;
00350   JDIMENSION row;
00351   register JDIMENSION col;
00352   cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
00353 
00354   /* Write the header and colormap */
00355   if (dest->is_os2)
00356     write_os2_header(cinfo, dest);
00357   else
00358     write_bmp_header(cinfo, dest);
00359 
00360   /* Write the file body from our virtual array */
00361   for (row = cinfo->output_height; row > 0; row--) {
00362     if (progress != NULL) {
00363       progress->pub.pass_counter = (long) (cinfo->output_height - row);
00364       progress->pub.pass_limit = (long) cinfo->output_height;
00365       (*progress->pub.progress_monitor) ((j_common_ptr) cinfo);
00366     }
00367     image_ptr = (*cinfo->mem->access_virt_sarray)
00368       ((j_common_ptr) cinfo, dest->whole_image, row-1, (JDIMENSION) 1, FALSE);
00369     data_ptr = image_ptr[0];
00370     for (col = dest->row_width; col > 0; col--) {
00371       putc(GETJSAMPLE(*data_ptr), outfile);
00372       data_ptr++;
00373     }
00374   }
00375   if (progress != NULL)
00376     progress->completed_extra_passes++;
00377 
00378   /* Make sure we wrote the output file OK */
00379   fflush(outfile);
00380   if (ferror(outfile))
00381     ERREXIT(cinfo, JERR_FILE_WRITE);
00382 }
00383 
00384 
00385 /*
00386  * The module selection routine for BMP format output.
00387  */
00388 
00389 GLOBAL(djpeg_dest_ptr)
00390 jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2)
00391 {
00392   bmp_dest_ptr dest;
00393   JDIMENSION row_width;
00394 
00395   /* Create module interface object, fill in method pointers */
00396   dest = (bmp_dest_ptr)
00397       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
00398                               SIZEOF(bmp_dest_struct));
00399   dest->pub.start_output = start_output_bmp;
00400   dest->pub.finish_output = finish_output_bmp;
00401   dest->is_os2 = is_os2;
00402 
00403   if (cinfo->out_color_space == JCS_GRAYSCALE) {
00404     dest->pub.put_pixel_rows = put_gray_rows;
00405   } else if (cinfo->out_color_space == JCS_RGB) {
00406     if (cinfo->quantize_colors)
00407       dest->pub.put_pixel_rows = put_gray_rows;
00408     else
00409       dest->pub.put_pixel_rows = put_pixel_rows;
00410   } else {
00411     ERREXIT(cinfo, JERR_BMP_COLORSPACE);
00412   }
00413 
00414   /* Calculate output image dimensions so we can allocate space */
00415   jpeg_calc_output_dimensions(cinfo);
00416 
00417   /* Determine width of rows in the BMP file (padded to 4-byte boundary). */
00418   row_width = cinfo->output_width * cinfo->output_components;
00419   dest->data_width = row_width;
00420   while ((row_width & 3) != 0) row_width++;
00421   dest->row_width = row_width;
00422   dest->pad_bytes = (int) (row_width - dest->data_width);
00423 
00424   /* Allocate space for inversion array, prepare for write pass */
00425   dest->whole_image = (*cinfo->mem->request_virt_sarray)
00426     ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
00427      row_width, cinfo->output_height, (JDIMENSION) 1);
00428   dest->cur_output_row = 0;
00429   if (cinfo->progress != NULL) {
00430     cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
00431     progress->total_extra_passes++; /* count file input as separate pass */
00432   }
00433 
00434   /* Create decompressor output buffer. */
00435   dest->pub.buffer = (*cinfo->mem->alloc_sarray)
00436     ((j_common_ptr) cinfo, JPOOL_IMAGE, row_width, (JDIMENSION) 1);
00437   dest->pub.buffer_height = 1;
00438 
00439   return (djpeg_dest_ptr) dest;
00440 }
00441 
00442 #endif /* BMP_SUPPORTED */