blob: 6ba584af2257500803996bad9090522da9137fbb [file] [log] [blame]
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +00001/*
2 * rdbmp.c
3 *
noel@chromium.org3395bcc2014-04-14 06:56:00 +00004 * This file was part of the Independent JPEG Group's software:
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +00005 * Copyright (C) 1994-1996, Thomas G. Lane.
Chris Blumecca8c4d2019-03-01 01:09:50 -08006 * Modified 2009-2017 by Guido Vollbeding.
noel@chromium.org3395bcc2014-04-14 06:56:00 +00007 * libjpeg-turbo Modifications:
hbono@chromium.org98626972011-08-03 03:13:08 +00008 * Modified 2011 by Siarhei Siamashka.
Chris Blumecca8c4d2019-03-01 01:09:50 -08009 * Copyright (C) 2015, 2017-2018, D. R. Commander.
Tom Hudson0d47d2d2016-05-04 13:22:56 -040010 * For conditions of distribution and use, see the accompanying README.ijg
11 * file.
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000012 *
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).
Jonathan Wrightbbb82822020-11-25 13:36:43 +000015 * Currently, only 8-, 24-, and 32-bit images are supported, not 1-bit or
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000016 * 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
Chris Blumecca8c4d2019-03-01 01:09:50 -080029#include "cmyk.h"
Tom Hudson0d47d2d2016-05-04 13:22:56 -040030#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000031
32#ifdef BMP_SUPPORTED
33
34
35/* Macros to deal with unsigned chars as efficiently as compiler allows */
36
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000037typedef unsigned char U_CHAR;
Chris Blumecca8c4d2019-03-01 01:09:50 -080038#define UCH(x) ((int)(x))
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000039
40
Chris Blumecca8c4d2019-03-01 01:09:50 -080041#define ReadOK(file, buffer, len) \
42 (JFREAD(file, buffer, len) == ((size_t)(len)))
43
44static int alpha_index[JPEG_NUMCS] = {
45 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0, -1
46};
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000047
48
49/* Private version of data source object */
50
Tom Hudson0d47d2d2016-05-04 13:22:56 -040051typedef struct _bmp_source_struct *bmp_source_ptr;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000052
53typedef struct _bmp_source_struct {
54 struct cjpeg_source_struct pub; /* public fields */
55
Tom Hudson0d47d2d2016-05-04 13:22:56 -040056 j_compress_ptr cinfo; /* back link saves passing separate parm */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000057
Tom Hudson0d47d2d2016-05-04 13:22:56 -040058 JSAMPARRAY colormap; /* BMP colormap (converted to my format) */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000059
Tom Hudson0d47d2d2016-05-04 13:22:56 -040060 jvirt_sarray_ptr whole_image; /* Needed to reverse row order */
61 JDIMENSION source_row; /* Current source row number */
62 JDIMENSION row_width; /* Physical width of scanlines in file */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000063
Jonathan Wrightbbb82822020-11-25 13:36:43 +000064 int bits_per_pixel; /* remembers 8-, 24-, or 32-bit format */
Chris Blumecca8c4d2019-03-01 01:09:50 -080065 int cmap_length; /* colormap length */
66
67 boolean use_inversion_array; /* TRUE = preload the whole image, which is
68 stored in bottom-up order, and feed it to
69 the calling program in top-down order
70
71 FALSE = the calling program will maintain
72 its own image buffer and read the rows in
73 bottom-up order */
74
75 U_CHAR *iobuffer; /* I/O buffer (used to buffer a single row from
76 disk if use_inversion_array == FALSE) */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000077} bmp_source_struct;
78
79
80LOCAL(int)
Chris Blumecca8c4d2019-03-01 01:09:50 -080081read_byte(bmp_source_ptr sinfo)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000082/* Read next byte from BMP file */
83{
84 register FILE *infile = sinfo->pub.input_file;
85 register int c;
86
87 if ((c = getc(infile)) == EOF)
88 ERREXIT(sinfo->cinfo, JERR_INPUT_EOF);
89 return c;
90}
91
92
93LOCAL(void)
Chris Blumecca8c4d2019-03-01 01:09:50 -080094read_colormap(bmp_source_ptr sinfo, int cmaplen, int mapentrysize)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000095/* Read the colormap from a BMP file */
96{
Chris Blumecca8c4d2019-03-01 01:09:50 -080097 int i, gray = 1;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +000098
99 switch (mapentrysize) {
100 case 3:
101 /* BGR format (occurs in OS/2 files) */
102 for (i = 0; i < cmaplen; i++) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800103 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 if (sinfo->colormap[2][i] != sinfo->colormap[1][i] ||
107 sinfo->colormap[1][i] != sinfo->colormap[0][i])
108 gray = 0;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000109 }
110 break;
111 case 4:
112 /* BGR0 format (occurs in MS Windows files) */
113 for (i = 0; i < cmaplen; i++) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800114 sinfo->colormap[2][i] = (JSAMPLE)read_byte(sinfo);
115 sinfo->colormap[1][i] = (JSAMPLE)read_byte(sinfo);
116 sinfo->colormap[0][i] = (JSAMPLE)read_byte(sinfo);
117 (void)read_byte(sinfo);
118 if (sinfo->colormap[2][i] != sinfo->colormap[1][i] ||
119 sinfo->colormap[1][i] != sinfo->colormap[0][i])
120 gray = 0;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000121 }
122 break;
123 default:
124 ERREXIT(sinfo->cinfo, JERR_BMP_BADCMAP);
125 break;
126 }
Chris Blumecca8c4d2019-03-01 01:09:50 -0800127
128 if (sinfo->cinfo->in_color_space == JCS_UNKNOWN && gray)
129 sinfo->cinfo->in_color_space = JCS_GRAYSCALE;
130
131 if (sinfo->cinfo->in_color_space == JCS_GRAYSCALE && !gray)
132 ERREXIT(sinfo->cinfo, JERR_BAD_IN_COLORSPACE);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000133}
134
135
136/*
137 * Read one row of pixels.
138 * The image has been read into the whole_image array, but is otherwise
139 * unprocessed. We must read it out in top-to-bottom row order, and if
140 * it is an 8-bit image, we must expand colormapped pixels to 24bit format.
141 */
142
143METHODDEF(JDIMENSION)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800144get_8bit_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000145/* This version is for reading 8-bit colormap indexes */
146{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800147 bmp_source_ptr source = (bmp_source_ptr)sinfo;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000148 register JSAMPARRAY colormap = source->colormap;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800149 int cmaplen = source->cmap_length;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000150 JSAMPARRAY image_ptr;
151 register int t;
152 register JSAMPROW inptr, outptr;
153 register JDIMENSION col;
154
Chris Blumecca8c4d2019-03-01 01:09:50 -0800155 if (source->use_inversion_array) {
156 /* Fetch next row from virtual array */
157 source->source_row--;
158 image_ptr = (*cinfo->mem->access_virt_sarray)
159 ((j_common_ptr)cinfo, source->whole_image,
160 source->source_row, (JDIMENSION)1, FALSE);
161 inptr = image_ptr[0];
162 } else {
163 if (!ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
164 ERREXIT(cinfo, JERR_INPUT_EOF);
165 inptr = source->iobuffer;
166 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000167
168 /* Expand the colormap indexes to real data */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000169 outptr = source->pub.buffer[0];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800170 if (cinfo->in_color_space == JCS_GRAYSCALE) {
171 for (col = cinfo->image_width; col > 0; col--) {
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000172 t = *inptr++;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800173 if (t >= cmaplen)
174 ERREXIT(cinfo, JERR_BMP_OUTOFRANGE);
175 *outptr++ = colormap[0][t];
176 }
177 } else if (cinfo->in_color_space == JCS_CMYK) {
178 for (col = cinfo->image_width; col > 0; col--) {
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000179 t = *inptr++;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800180 if (t >= cmaplen)
181 ERREXIT(cinfo, JERR_BMP_OUTOFRANGE);
182 rgb_to_cmyk(colormap[0][t], colormap[1][t], colormap[2][t], outptr,
183 outptr + 1, outptr + 2, outptr + 3);
184 outptr += 4;
185 }
186 } else {
187 register int rindex = rgb_red[cinfo->in_color_space];
188 register int gindex = rgb_green[cinfo->in_color_space];
189 register int bindex = rgb_blue[cinfo->in_color_space];
190 register int aindex = alpha_index[cinfo->in_color_space];
191 register int ps = rgb_pixelsize[cinfo->in_color_space];
192
193 if (aindex >= 0) {
194 for (col = cinfo->image_width; col > 0; col--) {
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000195 t = *inptr++;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800196 if (t >= cmaplen)
197 ERREXIT(cinfo, JERR_BMP_OUTOFRANGE);
198 outptr[rindex] = colormap[0][t];
199 outptr[gindex] = colormap[1][t];
200 outptr[bindex] = colormap[2][t];
201 outptr[aindex] = 0xFF;
202 outptr += ps;
203 }
204 } else {
205 for (col = cinfo->image_width; col > 0; col--) {
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000206 t = *inptr++;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800207 if (t >= cmaplen)
208 ERREXIT(cinfo, JERR_BMP_OUTOFRANGE);
209 outptr[rindex] = colormap[0][t];
210 outptr[gindex] = colormap[1][t];
211 outptr[bindex] = colormap[2][t];
212 outptr += ps;
213 }
214 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000215 }
216
217 return 1;
218}
219
220
221METHODDEF(JDIMENSION)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800222get_24bit_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000223/* This version is for reading 24-bit pixels */
224{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800225 bmp_source_ptr source = (bmp_source_ptr)sinfo;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000226 JSAMPARRAY image_ptr;
227 register JSAMPROW inptr, outptr;
228 register JDIMENSION col;
229
Chris Blumecca8c4d2019-03-01 01:09:50 -0800230 if (source->use_inversion_array) {
231 /* Fetch next row from virtual array */
232 source->source_row--;
233 image_ptr = (*cinfo->mem->access_virt_sarray)
234 ((j_common_ptr)cinfo, source->whole_image,
235 source->source_row, (JDIMENSION)1, FALSE);
236 inptr = image_ptr[0];
237 } else {
238 if (!ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
239 ERREXIT(cinfo, JERR_INPUT_EOF);
240 inptr = source->iobuffer;
241 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000242
243 /* Transfer data. Note source values are in BGR order
244 * (even though Microsoft's own documents say the opposite).
245 */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000246 outptr = source->pub.buffer[0];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800247 if (cinfo->in_color_space == JCS_EXT_BGR) {
248 MEMCOPY(outptr, inptr, source->row_width);
249 } else if (cinfo->in_color_space == JCS_CMYK) {
250 for (col = cinfo->image_width; col > 0; col--) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800251 JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
252 rgb_to_cmyk(r, g, b, outptr, outptr + 1, outptr + 2, outptr + 3);
253 outptr += 4;
254 }
255 } else {
256 register int rindex = rgb_red[cinfo->in_color_space];
257 register int gindex = rgb_green[cinfo->in_color_space];
258 register int bindex = rgb_blue[cinfo->in_color_space];
259 register int aindex = alpha_index[cinfo->in_color_space];
260 register int ps = rgb_pixelsize[cinfo->in_color_space];
261
262 if (aindex >= 0) {
263 for (col = cinfo->image_width; col > 0; col--) {
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000264 outptr[bindex] = *inptr++;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800265 outptr[gindex] = *inptr++;
266 outptr[rindex] = *inptr++;
267 outptr[aindex] = 0xFF;
268 outptr += ps;
269 }
270 } else {
271 for (col = cinfo->image_width; col > 0; col--) {
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000272 outptr[bindex] = *inptr++;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800273 outptr[gindex] = *inptr++;
274 outptr[rindex] = *inptr++;
275 outptr += ps;
276 }
277 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000278 }
279
280 return 1;
281}
282
283
hbono@chromium.org98626972011-08-03 03:13:08 +0000284METHODDEF(JDIMENSION)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800285get_32bit_row(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
hbono@chromium.org98626972011-08-03 03:13:08 +0000286/* This version is for reading 32-bit pixels */
287{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800288 bmp_source_ptr source = (bmp_source_ptr)sinfo;
hbono@chromium.org98626972011-08-03 03:13:08 +0000289 JSAMPARRAY image_ptr;
290 register JSAMPROW inptr, outptr;
291 register JDIMENSION col;
292
Chris Blumecca8c4d2019-03-01 01:09:50 -0800293 if (source->use_inversion_array) {
294 /* Fetch next row from virtual array */
295 source->source_row--;
296 image_ptr = (*cinfo->mem->access_virt_sarray)
297 ((j_common_ptr)cinfo, source->whole_image,
298 source->source_row, (JDIMENSION)1, FALSE);
299 inptr = image_ptr[0];
300 } else {
301 if (!ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
302 ERREXIT(cinfo, JERR_INPUT_EOF);
303 inptr = source->iobuffer;
304 }
305
hbono@chromium.org98626972011-08-03 03:13:08 +0000306 /* Transfer data. Note source values are in BGR order
307 * (even though Microsoft's own documents say the opposite).
308 */
hbono@chromium.org98626972011-08-03 03:13:08 +0000309 outptr = source->pub.buffer[0];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800310 if (cinfo->in_color_space == JCS_EXT_BGRX ||
311 cinfo->in_color_space == JCS_EXT_BGRA) {
312 MEMCOPY(outptr, inptr, source->row_width);
313 } else if (cinfo->in_color_space == JCS_CMYK) {
314 for (col = cinfo->image_width; col > 0; col--) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800315 JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
316 rgb_to_cmyk(r, g, b, outptr, outptr + 1, outptr + 2, outptr + 3);
317 inptr++; /* skip the 4th byte (Alpha channel) */
318 outptr += 4;
319 }
320 } else {
321 register int rindex = rgb_red[cinfo->in_color_space];
322 register int gindex = rgb_green[cinfo->in_color_space];
323 register int bindex = rgb_blue[cinfo->in_color_space];
324 register int aindex = alpha_index[cinfo->in_color_space];
325 register int ps = rgb_pixelsize[cinfo->in_color_space];
326
327 if (aindex >= 0) {
328 for (col = cinfo->image_width; col > 0; col--) {
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000329 outptr[bindex] = *inptr++;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800330 outptr[gindex] = *inptr++;
331 outptr[rindex] = *inptr++;
332 outptr[aindex] = *inptr++;
333 outptr += ps;
334 }
335 } else {
336 for (col = cinfo->image_width; col > 0; col--) {
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000337 outptr[bindex] = *inptr++;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800338 outptr[gindex] = *inptr++;
339 outptr[rindex] = *inptr++;
340 inptr++; /* skip the 4th byte (Alpha channel) */
341 outptr += ps;
342 }
343 }
hbono@chromium.org98626972011-08-03 03:13:08 +0000344 }
345
346 return 1;
347}
348
349
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000350/*
351 * This method loads the image into whole_image during the first call on
352 * get_pixel_rows. The get_pixel_rows pointer is then adjusted to call
hbono@chromium.org98626972011-08-03 03:13:08 +0000353 * get_8bit_row, get_24bit_row, or get_32bit_row on subsequent calls.
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000354 */
355
356METHODDEF(JDIMENSION)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800357preload_image(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000358{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800359 bmp_source_ptr source = (bmp_source_ptr)sinfo;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000360 register FILE *infile = source->pub.input_file;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000361 register JSAMPROW out_ptr;
362 JSAMPARRAY image_ptr;
hbono@chromium.org98626972011-08-03 03:13:08 +0000363 JDIMENSION row;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800364 cd_progress_ptr progress = (cd_progress_ptr)cinfo->progress;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000365
366 /* Read the data into a virtual array in input-file row order. */
367 for (row = 0; row < cinfo->image_height; row++) {
368 if (progress != NULL) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800369 progress->pub.pass_counter = (long)row;
370 progress->pub.pass_limit = (long)cinfo->image_height;
371 (*progress->pub.progress_monitor) ((j_common_ptr)cinfo);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000372 }
373 image_ptr = (*cinfo->mem->access_virt_sarray)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800374 ((j_common_ptr)cinfo, source->whole_image, row, (JDIMENSION)1, TRUE);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000375 out_ptr = image_ptr[0];
hbono@chromium.org98626972011-08-03 03:13:08 +0000376 if (fread(out_ptr, 1, source->row_width, infile) != source->row_width) {
377 if (feof(infile))
378 ERREXIT(cinfo, JERR_INPUT_EOF);
379 else
380 ERREXIT(cinfo, JERR_FILE_READ);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000381 }
382 }
383 if (progress != NULL)
384 progress->completed_extra_passes++;
385
386 /* Set up to read from the virtual array in top-to-bottom order */
387 switch (source->bits_per_pixel) {
388 case 8:
389 source->pub.get_pixel_rows = get_8bit_row;
390 break;
391 case 24:
392 source->pub.get_pixel_rows = get_24bit_row;
393 break;
hbono@chromium.org98626972011-08-03 03:13:08 +0000394 case 32:
395 source->pub.get_pixel_rows = get_32bit_row;
396 break;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000397 default:
398 ERREXIT(cinfo, JERR_BMP_BADDEPTH);
399 }
400 source->source_row = cinfo->image_height;
401
402 /* And read the first row */
403 return (*source->pub.get_pixel_rows) (cinfo, sinfo);
404}
405
406
407/*
408 * Read the file header; return image size and component count.
409 */
410
411METHODDEF(void)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800412start_input_bmp(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000413{
Chris Blumecca8c4d2019-03-01 01:09:50 -0800414 bmp_source_ptr source = (bmp_source_ptr)sinfo;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000415 U_CHAR bmpfileheader[14];
416 U_CHAR bmpinfoheader[64];
Chris Blumecca8c4d2019-03-01 01:09:50 -0800417
418#define GET_2B(array, offset) \
419 ((unsigned short)UCH(array[offset]) + \
420 (((unsigned short)UCH(array[offset + 1])) << 8))
421#define GET_4B(array, offset) \
422 ((unsigned int)UCH(array[offset]) + \
423 (((unsigned int)UCH(array[offset + 1])) << 8) + \
424 (((unsigned int)UCH(array[offset + 2])) << 16) + \
425 (((unsigned int)UCH(array[offset + 3])) << 24))
426
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400427 unsigned int bfOffBits;
428 unsigned int headerSize;
429 int biWidth;
430 int biHeight;
431 unsigned short biPlanes;
432 unsigned int biCompression;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800433 int biXPelsPerMeter, biYPelsPerMeter;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400434 unsigned int biClrUsed = 0;
435 int mapentrysize = 0; /* 0 indicates no colormap */
436 int bPad;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800437 JDIMENSION row_width = 0;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000438
439 /* Read and verify the bitmap file header */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800440 if (!ReadOK(source->pub.input_file, bmpfileheader, 14))
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000441 ERREXIT(cinfo, JERR_INPUT_EOF);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800442 if (GET_2B(bmpfileheader, 0) != 0x4D42) /* 'BM' */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000443 ERREXIT(cinfo, JERR_BMP_NOT);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800444 bfOffBits = GET_4B(bmpfileheader, 10);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000445 /* We ignore the remaining fileheader fields */
446
447 /* The infoheader might be 12 bytes (OS/2 1.x), 40 bytes (Windows),
448 * or 64 bytes (OS/2 2.x). Check the first 4 bytes to find out which.
449 */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800450 if (!ReadOK(source->pub.input_file, bmpinfoheader, 4))
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000451 ERREXIT(cinfo, JERR_INPUT_EOF);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800452 headerSize = GET_4B(bmpinfoheader, 0);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000453 if (headerSize < 12 || headerSize > 64)
454 ERREXIT(cinfo, JERR_BMP_BADHEADER);
Chris Blumecca8c4d2019-03-01 01:09:50 -0800455 if (!ReadOK(source->pub.input_file, bmpinfoheader + 4, headerSize - 4))
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000456 ERREXIT(cinfo, JERR_INPUT_EOF);
457
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400458 switch (headerSize) {
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000459 case 12:
460 /* Decode OS/2 1.x header (Microsoft calls this a BITMAPCOREHEADER) */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800461 biWidth = (int)GET_2B(bmpinfoheader, 4);
462 biHeight = (int)GET_2B(bmpinfoheader, 6);
463 biPlanes = GET_2B(bmpinfoheader, 8);
464 source->bits_per_pixel = (int)GET_2B(bmpinfoheader, 10);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000465
466 switch (source->bits_per_pixel) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400467 case 8: /* colormapped image */
468 mapentrysize = 3; /* OS/2 uses RGBTRIPLE colormap */
469 TRACEMS2(cinfo, 1, JTRC_BMP_OS2_MAPPED, biWidth, biHeight);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000470 break;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400471 case 24: /* RGB image */
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000472 case 32: /* RGB image + Alpha channel */
473 TRACEMS3(cinfo, 1, JTRC_BMP_OS2, biWidth, biHeight,
474 source->bits_per_pixel);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000475 break;
476 default:
477 ERREXIT(cinfo, JERR_BMP_BADDEPTH);
478 break;
479 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000480 break;
481 case 40:
482 case 64:
483 /* Decode Windows 3.x header (Microsoft calls this a BITMAPINFOHEADER) */
484 /* or OS/2 2.x header, which has additional fields that we ignore */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800485 biWidth = (int)GET_4B(bmpinfoheader, 4);
486 biHeight = (int)GET_4B(bmpinfoheader, 8);
487 biPlanes = GET_2B(bmpinfoheader, 12);
488 source->bits_per_pixel = (int)GET_2B(bmpinfoheader, 14);
489 biCompression = GET_4B(bmpinfoheader, 16);
490 biXPelsPerMeter = (int)GET_4B(bmpinfoheader, 24);
491 biYPelsPerMeter = (int)GET_4B(bmpinfoheader, 28);
492 biClrUsed = GET_4B(bmpinfoheader, 32);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000493 /* biSizeImage, biClrImportant fields are ignored */
494
495 switch (source->bits_per_pixel) {
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400496 case 8: /* colormapped image */
497 mapentrysize = 4; /* Windows uses RGBQUAD colormap */
498 TRACEMS2(cinfo, 1, JTRC_BMP_MAPPED, biWidth, biHeight);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000499 break;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400500 case 24: /* RGB image */
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400501 case 32: /* RGB image + Alpha channel */
Jonathan Wrightbbb82822020-11-25 13:36:43 +0000502 TRACEMS3(cinfo, 1, JTRC_BMP, biWidth, biHeight, source->bits_per_pixel);
hbono@chromium.org98626972011-08-03 03:13:08 +0000503 break;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000504 default:
505 ERREXIT(cinfo, JERR_BMP_BADDEPTH);
506 break;
507 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000508 if (biCompression != 0)
509 ERREXIT(cinfo, JERR_BMP_COMPRESSED);
510
511 if (biXPelsPerMeter > 0 && biYPelsPerMeter > 0) {
512 /* Set JFIF density parameters from the BMP data */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800513 cinfo->X_density = (UINT16)(biXPelsPerMeter / 100); /* 100 cm per meter */
514 cinfo->Y_density = (UINT16)(biYPelsPerMeter / 100);
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400515 cinfo->density_unit = 2; /* dots/cm */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000516 }
517 break;
518 default:
519 ERREXIT(cinfo, JERR_BMP_BADHEADER);
hbono@chromium.org98626972011-08-03 03:13:08 +0000520 return;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000521 }
522
hbono@chromium.org98626972011-08-03 03:13:08 +0000523 if (biWidth <= 0 || biHeight <= 0)
524 ERREXIT(cinfo, JERR_BMP_EMPTY);
525 if (biPlanes != 1)
526 ERREXIT(cinfo, JERR_BMP_BADPLANES);
527
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000528 /* Compute distance to bitmap data --- will adjust for colormap below */
529 bPad = bfOffBits - (headerSize + 14);
530
531 /* Read the colormap, if any */
532 if (mapentrysize > 0) {
533 if (biClrUsed <= 0)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400534 biClrUsed = 256; /* assume it's 256 */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000535 else if (biClrUsed > 256)
536 ERREXIT(cinfo, JERR_BMP_BADCMAP);
537 /* Allocate space to store the colormap */
538 source->colormap = (*cinfo->mem->alloc_sarray)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800539 ((j_common_ptr)cinfo, JPOOL_IMAGE, (JDIMENSION)biClrUsed, (JDIMENSION)3);
540 source->cmap_length = (int)biClrUsed;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000541 /* and read it from the file */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800542 read_colormap(source, (int)biClrUsed, mapentrysize);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000543 /* account for size of colormap */
544 bPad -= biClrUsed * mapentrysize;
545 }
546
547 /* Skip any remaining pad bytes */
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400548 if (bPad < 0) /* incorrect bfOffBits value? */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000549 ERREXIT(cinfo, JERR_BMP_BADHEADER);
550 while (--bPad >= 0) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800551 (void)read_byte(source);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000552 }
553
554 /* Compute row width in file, including padding to 4-byte boundary */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800555 switch (source->bits_per_pixel) {
556 case 8:
557 if (cinfo->in_color_space == JCS_UNKNOWN)
558 cinfo->in_color_space = JCS_EXT_RGB;
559 if (IsExtRGB(cinfo->in_color_space))
560 cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
561 else if (cinfo->in_color_space == JCS_GRAYSCALE)
562 cinfo->input_components = 1;
563 else if (cinfo->in_color_space == JCS_CMYK)
564 cinfo->input_components = 4;
565 else
566 ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
567 row_width = (JDIMENSION)biWidth;
568 break;
569 case 24:
570 if (cinfo->in_color_space == JCS_UNKNOWN)
571 cinfo->in_color_space = JCS_EXT_BGR;
572 if (IsExtRGB(cinfo->in_color_space))
573 cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
574 else if (cinfo->in_color_space == JCS_CMYK)
575 cinfo->input_components = 4;
576 else
577 ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
578 row_width = (JDIMENSION)(biWidth * 3);
579 break;
580 case 32:
581 if (cinfo->in_color_space == JCS_UNKNOWN)
582 cinfo->in_color_space = JCS_EXT_BGRA;
583 if (IsExtRGB(cinfo->in_color_space))
584 cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
585 else if (cinfo->in_color_space == JCS_CMYK)
586 cinfo->input_components = 4;
587 else
588 ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
589 row_width = (JDIMENSION)(biWidth * 4);
590 break;
591 default:
592 ERREXIT(cinfo, JERR_BMP_BADDEPTH);
593 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000594 while ((row_width & 3) != 0) row_width++;
595 source->row_width = row_width;
596
Chris Blumecca8c4d2019-03-01 01:09:50 -0800597 if (source->use_inversion_array) {
598 /* Allocate space for inversion array, prepare for preload pass */
599 source->whole_image = (*cinfo->mem->request_virt_sarray)
600 ((j_common_ptr)cinfo, JPOOL_IMAGE, FALSE,
601 row_width, (JDIMENSION)biHeight, (JDIMENSION)1);
602 source->pub.get_pixel_rows = preload_image;
603 if (cinfo->progress != NULL) {
604 cd_progress_ptr progress = (cd_progress_ptr)cinfo->progress;
605 progress->total_extra_passes++; /* count file input as separate pass */
606 }
607 } else {
608 source->iobuffer = (U_CHAR *)
609 (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, row_width);
610 switch (source->bits_per_pixel) {
611 case 8:
612 source->pub.get_pixel_rows = get_8bit_row;
613 break;
614 case 24:
615 source->pub.get_pixel_rows = get_24bit_row;
616 break;
617 case 32:
618 source->pub.get_pixel_rows = get_32bit_row;
619 break;
620 default:
621 ERREXIT(cinfo, JERR_BMP_BADDEPTH);
622 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000623 }
624
Chris Blumecca8c4d2019-03-01 01:09:50 -0800625 /* Ensure that biWidth * cinfo->input_components doesn't exceed the maximum
626 value of the JDIMENSION type. This is only a danger with BMP files, since
627 their width and height fields are 32-bit integers. */
628 if ((unsigned long long)biWidth *
629 (unsigned long long)cinfo->input_components > 0xFFFFFFFFULL)
630 ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000631 /* Allocate one-row buffer for returned data */
632 source->pub.buffer = (*cinfo->mem->alloc_sarray)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800633 ((j_common_ptr)cinfo, JPOOL_IMAGE,
634 (JDIMENSION)(biWidth * cinfo->input_components), (JDIMENSION)1);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000635 source->pub.buffer_height = 1;
636
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000637 cinfo->data_precision = 8;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800638 cinfo->image_width = (JDIMENSION)biWidth;
639 cinfo->image_height = (JDIMENSION)biHeight;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000640}
641
642
643/*
644 * Finish up at the end of the file.
645 */
646
647METHODDEF(void)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800648finish_input_bmp(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000649{
650 /* no work */
651}
652
653
654/*
655 * The module selection routine for BMP format input.
656 */
657
658GLOBAL(cjpeg_source_ptr)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800659jinit_read_bmp(j_compress_ptr cinfo, boolean use_inversion_array)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000660{
661 bmp_source_ptr source;
662
663 /* Create module interface object */
664 source = (bmp_source_ptr)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800665 (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE,
666 sizeof(bmp_source_struct));
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400667 source->cinfo = cinfo; /* make back link for subroutines */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000668 /* Fill in method ptrs, except get_pixel_rows which start_input sets */
669 source->pub.start_input = start_input_bmp;
670 source->pub.finish_input = finish_input_bmp;
671
Chris Blumecca8c4d2019-03-01 01:09:50 -0800672 source->use_inversion_array = use_inversion_array;
673
674 return (cjpeg_source_ptr)source;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000675}
676
677#endif /* BMP_SUPPORTED */