• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * rdbmp.c
3  *
4  * This file was part of the Independent JPEG Group's software:
5  * Copyright (C) 1994-1996, Thomas G. Lane.
6  * Modified 2009-2010 by Guido Vollbeding.
7  * libjpeg-turbo Modifications:
8  * Modified 2011 by Siarhei Siamashka.
9  * Copyright (C) 2015, D. R. Commander.
10  * For conditions of distribution and use, see the accompanying README.ijg
11  * file.
12  *
13  * This file contains routines to read input images in Microsoft "BMP"
14  * format (MS Windows 3.x, OS/2 1.x, and OS/2 2.x flavors).
15  * Currently, only 8-bit and 24-bit images are supported, not 1-bit or
16  * 4-bit (feeding such low-depth images into JPEG would be silly anyway).
17  * Also, we don't support RLE-compressed files.
18  *
19  * These routines may need modification for non-Unix environments or
20  * specialized applications.  As they stand, they assume input from
21  * an ordinary stdio stream.  They further assume that reading begins
22  * at the start of the file; start_input may need work if the
23  * user interface has already read some data (e.g., to determine that
24  * the file is indeed BMP format).
25  *
26  * This code contributed by James Arthur Boucher.
27  */
28 
29 #include "cdjpeg.h"             /* Common decls for cjpeg/djpeg applications */
30 
31 #ifdef BMP_SUPPORTED
32 
33 
34 /* Macros to deal with unsigned chars as efficiently as compiler allows */
35 
36 #ifdef HAVE_UNSIGNED_CHAR
37 typedef unsigned char U_CHAR;
38 #define UCH(x)  ((int) (x))
39 #else /* !HAVE_UNSIGNED_CHAR */
40 #ifdef __CHAR_UNSIGNED__
41 typedef char U_CHAR;
42 #define UCH(x)  ((int) (x))
43 #else
44 typedef char U_CHAR;
45 #define UCH(x)  ((int) (x) & 0xFF)
46 #endif
47 #endif /* HAVE_UNSIGNED_CHAR */
48 
49 
50 #define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len)))
51 
52 
53 /* Private version of data source object */
54 
55 typedef struct _bmp_source_struct *bmp_source_ptr;
56 
57 typedef struct _bmp_source_struct {
58   struct cjpeg_source_struct pub; /* public fields */
59 
60   j_compress_ptr cinfo;         /* back link saves passing separate parm */
61 
62   JSAMPARRAY colormap;          /* BMP colormap (converted to my format) */
63 
64   jvirt_sarray_ptr whole_image; /* Needed to reverse row order */
65   JDIMENSION source_row;        /* Current source row number */
66   JDIMENSION row_width;         /* Physical width of scanlines in file */
67 
68   int bits_per_pixel;           /* remembers 8- or 24-bit format */
69 } bmp_source_struct;
70 
71 
72 LOCAL(int)
read_byte(bmp_source_ptr sinfo)73 read_byte (bmp_source_ptr sinfo)
74 /* Read next byte from BMP file */
75 {
76   register FILE *infile = sinfo->pub.input_file;
77   register int c;
78 
79   if ((c = getc(infile)) == EOF)
80     ERREXIT(sinfo->cinfo, JERR_INPUT_EOF);
81   return c;
82 }
83 
84 
85 LOCAL(void)
read_colormap(bmp_source_ptr sinfo,int cmaplen,int mapentrysize)86 read_colormap (bmp_source_ptr sinfo, int cmaplen, int mapentrysize)
87 /* Read the colormap from a BMP file */
88 {
89   int i;
90 
91   switch (mapentrysize) {
92   case 3:
93     /* BGR format (occurs in OS/2 files) */
94     for (i = 0; i < cmaplen; i++) {
95       sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo);
96       sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo);
97       sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo);
98     }
99     break;
100   case 4:
101     /* BGR0 format (occurs in MS Windows files) */
102     for (i = 0; i < cmaplen; i++) {
103       sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo);
104       sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo);
105       sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo);
106       (void) read_byte(sinfo);
107     }
108     break;
109   default:
110     ERREXIT(sinfo->cinfo, JERR_BMP_BADCMAP);
111     break;
112   }
113 }
114 
115 
116 /*
117  * Read one row of pixels.
118  * The image has been read into the whole_image array, but is otherwise
119  * unprocessed.  We must read it out in top-to-bottom row order, and if
120  * it is an 8-bit image, we must expand colormapped pixels to 24bit format.
121  */
122 
123 METHODDEF(JDIMENSION)
get_8bit_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)124 get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
125 /* This version is for reading 8-bit colormap indexes */
126 {
127   bmp_source_ptr source = (bmp_source_ptr) sinfo;
128   register JSAMPARRAY colormap = source->colormap;
129   JSAMPARRAY image_ptr;
130   register int t;
131   register JSAMPROW inptr, outptr;
132   register JDIMENSION col;
133 
134   /* Fetch next row from virtual array */
135   source->source_row--;
136   image_ptr = (*cinfo->mem->access_virt_sarray)
137     ((j_common_ptr) cinfo, source->whole_image,
138      source->source_row, (JDIMENSION) 1, FALSE);
139 
140   /* Expand the colormap indexes to real data */
141   inptr = image_ptr[0];
142   outptr = source->pub.buffer[0];
143   for (col = cinfo->image_width; col > 0; col--) {
144     t = GETJSAMPLE(*inptr++);
145     *outptr++ = colormap[0][t]; /* can omit GETJSAMPLE() safely */
146     *outptr++ = colormap[1][t];
147     *outptr++ = colormap[2][t];
148   }
149 
150   return 1;
151 }
152 
153 
154 METHODDEF(JDIMENSION)
get_24bit_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)155 get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
156 /* This version is for reading 24-bit pixels */
157 {
158   bmp_source_ptr source = (bmp_source_ptr) sinfo;
159   JSAMPARRAY image_ptr;
160   register JSAMPROW inptr, outptr;
161   register JDIMENSION col;
162 
163   /* Fetch next row from virtual array */
164   source->source_row--;
165   image_ptr = (*cinfo->mem->access_virt_sarray)
166     ((j_common_ptr) cinfo, source->whole_image,
167      source->source_row, (JDIMENSION) 1, FALSE);
168 
169   /* Transfer data.  Note source values are in BGR order
170    * (even though Microsoft's own documents say the opposite).
171    */
172   inptr = image_ptr[0];
173   outptr = source->pub.buffer[0];
174   for (col = cinfo->image_width; col > 0; col--) {
175     outptr[2] = *inptr++;       /* can omit GETJSAMPLE() safely */
176     outptr[1] = *inptr++;
177     outptr[0] = *inptr++;
178     outptr += 3;
179   }
180 
181   return 1;
182 }
183 
184 
185 METHODDEF(JDIMENSION)
get_32bit_row(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)186 get_32bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
187 /* This version is for reading 32-bit pixels */
188 {
189   bmp_source_ptr source = (bmp_source_ptr) sinfo;
190   JSAMPARRAY image_ptr;
191   register JSAMPROW inptr, outptr;
192   register JDIMENSION col;
193 
194   /* Fetch next row from virtual array */
195   source->source_row--;
196   image_ptr = (*cinfo->mem->access_virt_sarray)
197     ((j_common_ptr) cinfo, source->whole_image,
198      source->source_row, (JDIMENSION) 1, FALSE);
199   /* Transfer data.  Note source values are in BGR order
200    * (even though Microsoft's own documents say the opposite).
201    */
202   inptr = image_ptr[0];
203   outptr = source->pub.buffer[0];
204   for (col = cinfo->image_width; col > 0; col--) {
205     outptr[2] = *inptr++;       /* can omit GETJSAMPLE() safely */
206     outptr[1] = *inptr++;
207     outptr[0] = *inptr++;
208     inptr++;                    /* skip the 4th byte (Alpha channel) */
209     outptr += 3;
210   }
211 
212   return 1;
213 }
214 
215 
216 /*
217  * This method loads the image into whole_image during the first call on
218  * get_pixel_rows.  The get_pixel_rows pointer is then adjusted to call
219  * get_8bit_row, get_24bit_row, or get_32bit_row on subsequent calls.
220  */
221 
222 METHODDEF(JDIMENSION)
preload_image(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)223 preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
224 {
225   bmp_source_ptr source = (bmp_source_ptr) sinfo;
226   register FILE *infile = source->pub.input_file;
227   register JSAMPROW out_ptr;
228   JSAMPARRAY image_ptr;
229   JDIMENSION row;
230   cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
231 
232   /* Read the data into a virtual array in input-file row order. */
233   for (row = 0; row < cinfo->image_height; row++) {
234     if (progress != NULL) {
235       progress->pub.pass_counter = (long) row;
236       progress->pub.pass_limit = (long) cinfo->image_height;
237       (*progress->pub.progress_monitor) ((j_common_ptr) cinfo);
238     }
239     image_ptr = (*cinfo->mem->access_virt_sarray)
240       ((j_common_ptr) cinfo, source->whole_image,
241        row, (JDIMENSION) 1, TRUE);
242     out_ptr = image_ptr[0];
243     if (fread(out_ptr, 1, source->row_width, infile) != source->row_width) {
244       if (feof(infile))
245         ERREXIT(cinfo, JERR_INPUT_EOF);
246       else
247         ERREXIT(cinfo, JERR_FILE_READ);
248     }
249   }
250   if (progress != NULL)
251     progress->completed_extra_passes++;
252 
253   /* Set up to read from the virtual array in top-to-bottom order */
254   switch (source->bits_per_pixel) {
255   case 8:
256     source->pub.get_pixel_rows = get_8bit_row;
257     break;
258   case 24:
259     source->pub.get_pixel_rows = get_24bit_row;
260     break;
261   case 32:
262     source->pub.get_pixel_rows = get_32bit_row;
263     break;
264   default:
265     ERREXIT(cinfo, JERR_BMP_BADDEPTH);
266   }
267   source->source_row = cinfo->image_height;
268 
269   /* And read the first row */
270   return (*source->pub.get_pixel_rows) (cinfo, sinfo);
271 }
272 
273 
274 /*
275  * Read the file header; return image size and component count.
276  */
277 
278 METHODDEF(void)
start_input_bmp(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)279 start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
280 {
281   bmp_source_ptr source = (bmp_source_ptr) sinfo;
282   U_CHAR bmpfileheader[14];
283   U_CHAR bmpinfoheader[64];
284 #define GET_2B(array,offset)  ((unsigned short) UCH(array[offset]) + \
285                                (((unsigned short) UCH(array[offset+1])) << 8))
286 #define GET_4B(array,offset)  ((unsigned int) UCH(array[offset]) + \
287                                (((unsigned int) UCH(array[offset+1])) << 8) + \
288                                (((unsigned int) UCH(array[offset+2])) << 16) + \
289                                (((unsigned int) UCH(array[offset+3])) << 24))
290   unsigned int bfOffBits;
291   unsigned int headerSize;
292   int biWidth;
293   int biHeight;
294   unsigned short biPlanes;
295   unsigned int biCompression;
296   int biXPelsPerMeter,biYPelsPerMeter;
297   unsigned int biClrUsed = 0;
298   int mapentrysize = 0;         /* 0 indicates no colormap */
299   int bPad;
300   JDIMENSION row_width;
301 
302   /* Read and verify the bitmap file header */
303   if (! ReadOK(source->pub.input_file, bmpfileheader, 14))
304     ERREXIT(cinfo, JERR_INPUT_EOF);
305   if (GET_2B(bmpfileheader,0) != 0x4D42) /* 'BM' */
306     ERREXIT(cinfo, JERR_BMP_NOT);
307   bfOffBits = GET_4B(bmpfileheader,10);
308   /* We ignore the remaining fileheader fields */
309 
310   /* The infoheader might be 12 bytes (OS/2 1.x), 40 bytes (Windows),
311    * or 64 bytes (OS/2 2.x).  Check the first 4 bytes to find out which.
312    */
313   if (! ReadOK(source->pub.input_file, bmpinfoheader, 4))
314     ERREXIT(cinfo, JERR_INPUT_EOF);
315   headerSize = GET_4B(bmpinfoheader,0);
316   if (headerSize < 12 || headerSize > 64)
317     ERREXIT(cinfo, JERR_BMP_BADHEADER);
318   if (! ReadOK(source->pub.input_file, bmpinfoheader+4, headerSize-4))
319     ERREXIT(cinfo, JERR_INPUT_EOF);
320 
321   switch (headerSize) {
322   case 12:
323     /* Decode OS/2 1.x header (Microsoft calls this a BITMAPCOREHEADER) */
324     biWidth = (int) GET_2B(bmpinfoheader,4);
325     biHeight = (int) GET_2B(bmpinfoheader,6);
326     biPlanes = GET_2B(bmpinfoheader,8);
327     source->bits_per_pixel = (int) GET_2B(bmpinfoheader,10);
328 
329     switch (source->bits_per_pixel) {
330     case 8:                     /* colormapped image */
331       mapentrysize = 3;         /* OS/2 uses RGBTRIPLE colormap */
332       TRACEMS2(cinfo, 1, JTRC_BMP_OS2_MAPPED, biWidth, biHeight);
333       break;
334     case 24:                    /* RGB image */
335       TRACEMS2(cinfo, 1, JTRC_BMP_OS2, biWidth, biHeight);
336       break;
337     default:
338       ERREXIT(cinfo, JERR_BMP_BADDEPTH);
339       break;
340     }
341     break;
342   case 40:
343   case 64:
344     /* Decode Windows 3.x header (Microsoft calls this a BITMAPINFOHEADER) */
345     /* or OS/2 2.x header, which has additional fields that we ignore */
346     biWidth = (int) GET_4B(bmpinfoheader,4);
347     biHeight = (int) GET_4B(bmpinfoheader,8);
348     biPlanes = GET_2B(bmpinfoheader,12);
349     source->bits_per_pixel = (int) GET_2B(bmpinfoheader,14);
350     biCompression = GET_4B(bmpinfoheader,16);
351     biXPelsPerMeter = (int) GET_4B(bmpinfoheader,24);
352     biYPelsPerMeter = (int) GET_4B(bmpinfoheader,28);
353     biClrUsed = GET_4B(bmpinfoheader,32);
354     /* biSizeImage, biClrImportant fields are ignored */
355 
356     switch (source->bits_per_pixel) {
357     case 8:                     /* colormapped image */
358       mapentrysize = 4;         /* Windows uses RGBQUAD colormap */
359       TRACEMS2(cinfo, 1, JTRC_BMP_MAPPED, biWidth, biHeight);
360       break;
361     case 24:                    /* RGB image */
362       TRACEMS2(cinfo, 1, JTRC_BMP, biWidth, biHeight);
363       break;
364     case 32:                    /* RGB image + Alpha channel */
365       TRACEMS2(cinfo, 1, JTRC_BMP, biWidth, biHeight);
366       break;
367     default:
368       ERREXIT(cinfo, JERR_BMP_BADDEPTH);
369       break;
370     }
371     if (biCompression != 0)
372       ERREXIT(cinfo, JERR_BMP_COMPRESSED);
373 
374     if (biXPelsPerMeter > 0 && biYPelsPerMeter > 0) {
375       /* Set JFIF density parameters from the BMP data */
376       cinfo->X_density = (UINT16) (biXPelsPerMeter/100); /* 100 cm per meter */
377       cinfo->Y_density = (UINT16) (biYPelsPerMeter/100);
378       cinfo->density_unit = 2;  /* dots/cm */
379     }
380     break;
381   default:
382     ERREXIT(cinfo, JERR_BMP_BADHEADER);
383     return;
384   }
385 
386   if (biWidth <= 0 || biHeight <= 0)
387     ERREXIT(cinfo, JERR_BMP_EMPTY);
388   if (biPlanes != 1)
389     ERREXIT(cinfo, JERR_BMP_BADPLANES);
390 
391   /* Compute distance to bitmap data --- will adjust for colormap below */
392   bPad = bfOffBits - (headerSize + 14);
393 
394   /* Read the colormap, if any */
395   if (mapentrysize > 0) {
396     if (biClrUsed <= 0)
397       biClrUsed = 256;          /* assume it's 256 */
398     else if (biClrUsed > 256)
399       ERREXIT(cinfo, JERR_BMP_BADCMAP);
400     /* Allocate space to store the colormap */
401     source->colormap = (*cinfo->mem->alloc_sarray)
402       ((j_common_ptr) cinfo, JPOOL_IMAGE,
403        (JDIMENSION) biClrUsed, (JDIMENSION) 3);
404     /* and read it from the file */
405     read_colormap(source, (int) biClrUsed, mapentrysize);
406     /* account for size of colormap */
407     bPad -= biClrUsed * mapentrysize;
408   }
409 
410   /* Skip any remaining pad bytes */
411   if (bPad < 0)                 /* incorrect bfOffBits value? */
412     ERREXIT(cinfo, JERR_BMP_BADHEADER);
413   while (--bPad >= 0) {
414     (void) read_byte(source);
415   }
416 
417   /* Compute row width in file, including padding to 4-byte boundary */
418   if (source->bits_per_pixel == 24)
419     row_width = (JDIMENSION) (biWidth * 3);
420   else if (source->bits_per_pixel == 32)
421     row_width = (JDIMENSION) (biWidth * 4);
422   else
423     row_width = (JDIMENSION) biWidth;
424   while ((row_width & 3) != 0) row_width++;
425   source->row_width = row_width;
426 
427   /* Allocate space for inversion array, prepare for preload pass */
428   source->whole_image = (*cinfo->mem->request_virt_sarray)
429     ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
430      row_width, (JDIMENSION) biHeight, (JDIMENSION) 1);
431   source->pub.get_pixel_rows = preload_image;
432   if (cinfo->progress != NULL) {
433     cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
434     progress->total_extra_passes++; /* count file input as separate pass */
435   }
436 
437   /* Allocate one-row buffer for returned data */
438   source->pub.buffer = (*cinfo->mem->alloc_sarray)
439     ((j_common_ptr) cinfo, JPOOL_IMAGE,
440      (JDIMENSION) (biWidth * 3), (JDIMENSION) 1);
441   source->pub.buffer_height = 1;
442 
443   cinfo->in_color_space = JCS_RGB;
444   cinfo->input_components = 3;
445   cinfo->data_precision = 8;
446   cinfo->image_width = (JDIMENSION) biWidth;
447   cinfo->image_height = (JDIMENSION) biHeight;
448 }
449 
450 
451 /*
452  * Finish up at the end of the file.
453  */
454 
455 METHODDEF(void)
finish_input_bmp(j_compress_ptr cinfo,cjpeg_source_ptr sinfo)456 finish_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
457 {
458   /* no work */
459 }
460 
461 
462 /*
463  * The module selection routine for BMP format input.
464  */
465 
466 GLOBAL(cjpeg_source_ptr)
jinit_read_bmp(j_compress_ptr cinfo)467 jinit_read_bmp (j_compress_ptr cinfo)
468 {
469   bmp_source_ptr source;
470 
471   /* Create module interface object */
472   source = (bmp_source_ptr)
473       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
474                                   sizeof(bmp_source_struct));
475   source->cinfo = cinfo;        /* make back link for subroutines */
476   /* Fill in method ptrs, except get_pixel_rows which start_input sets */
477   source->pub.start_input = start_input_bmp;
478   source->pub.finish_input = finish_input_bmp;
479 
480   return (cjpeg_source_ptr) source;
481 }
482 
483 #endif /* BMP_SUPPORTED */
484