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