blob: 358a0267691162956291fbdbb7e8512f614df324 [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.
Jonathan Wright24e31052021-04-26 12:10:48 +01009 * Copyright (C) 2015, 2017-2018, 2021, 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
Jonathan Wright24e31052021-04-26 12:10:48 +0100427 int bfOffBits;
428 int headerSize;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400429 int biWidth;
430 int biHeight;
431 unsigned short biPlanes;
432 unsigned int biCompression;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800433 int biXPelsPerMeter, biYPelsPerMeter;
Jonathan Wright24e31052021-04-26 12:10:48 +0100434 int biClrUsed = 0;
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400435 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);
Jonathan Wright24e31052021-04-26 12:10:48 +0100453 if (headerSize < 12 || headerSize > 64 || (headerSize + 14) > bfOffBits)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000454 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);
Jonathan Wright24e31052021-04-26 12:10:48 +0100525#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
526 if (sinfo->max_pixels &&
527 (unsigned long long)biWidth * biHeight > sinfo->max_pixels)
528 ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
529#endif
hbono@chromium.org98626972011-08-03 03:13:08 +0000530 if (biPlanes != 1)
531 ERREXIT(cinfo, JERR_BMP_BADPLANES);
532
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000533 /* Compute distance to bitmap data --- will adjust for colormap below */
534 bPad = bfOffBits - (headerSize + 14);
535
536 /* Read the colormap, if any */
537 if (mapentrysize > 0) {
538 if (biClrUsed <= 0)
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400539 biClrUsed = 256; /* assume it's 256 */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000540 else if (biClrUsed > 256)
541 ERREXIT(cinfo, JERR_BMP_BADCMAP);
542 /* Allocate space to store the colormap */
543 source->colormap = (*cinfo->mem->alloc_sarray)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800544 ((j_common_ptr)cinfo, JPOOL_IMAGE, (JDIMENSION)biClrUsed, (JDIMENSION)3);
545 source->cmap_length = (int)biClrUsed;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000546 /* and read it from the file */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800547 read_colormap(source, (int)biClrUsed, mapentrysize);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000548 /* account for size of colormap */
549 bPad -= biClrUsed * mapentrysize;
550 }
551
552 /* Skip any remaining pad bytes */
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400553 if (bPad < 0) /* incorrect bfOffBits value? */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000554 ERREXIT(cinfo, JERR_BMP_BADHEADER);
555 while (--bPad >= 0) {
Chris Blumecca8c4d2019-03-01 01:09:50 -0800556 (void)read_byte(source);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000557 }
558
559 /* Compute row width in file, including padding to 4-byte boundary */
Chris Blumecca8c4d2019-03-01 01:09:50 -0800560 switch (source->bits_per_pixel) {
561 case 8:
562 if (cinfo->in_color_space == JCS_UNKNOWN)
563 cinfo->in_color_space = JCS_EXT_RGB;
564 if (IsExtRGB(cinfo->in_color_space))
565 cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
566 else if (cinfo->in_color_space == JCS_GRAYSCALE)
567 cinfo->input_components = 1;
568 else if (cinfo->in_color_space == JCS_CMYK)
569 cinfo->input_components = 4;
570 else
571 ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
572 row_width = (JDIMENSION)biWidth;
573 break;
574 case 24:
575 if (cinfo->in_color_space == JCS_UNKNOWN)
576 cinfo->in_color_space = JCS_EXT_BGR;
577 if (IsExtRGB(cinfo->in_color_space))
578 cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
579 else if (cinfo->in_color_space == JCS_CMYK)
580 cinfo->input_components = 4;
581 else
582 ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
Jonathan Wright24e31052021-04-26 12:10:48 +0100583 if ((unsigned long long)biWidth * 3ULL > 0xFFFFFFFFULL)
584 ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
585 row_width = (JDIMENSION)biWidth * 3;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800586 break;
587 case 32:
588 if (cinfo->in_color_space == JCS_UNKNOWN)
589 cinfo->in_color_space = JCS_EXT_BGRA;
590 if (IsExtRGB(cinfo->in_color_space))
591 cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
592 else if (cinfo->in_color_space == JCS_CMYK)
593 cinfo->input_components = 4;
594 else
595 ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
Jonathan Wright24e31052021-04-26 12:10:48 +0100596 if ((unsigned long long)biWidth * 4ULL > 0xFFFFFFFFULL)
597 ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
598 row_width = (JDIMENSION)biWidth * 4;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800599 break;
600 default:
601 ERREXIT(cinfo, JERR_BMP_BADDEPTH);
602 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000603 while ((row_width & 3) != 0) row_width++;
604 source->row_width = row_width;
605
Chris Blumecca8c4d2019-03-01 01:09:50 -0800606 if (source->use_inversion_array) {
607 /* Allocate space for inversion array, prepare for preload pass */
608 source->whole_image = (*cinfo->mem->request_virt_sarray)
609 ((j_common_ptr)cinfo, JPOOL_IMAGE, FALSE,
610 row_width, (JDIMENSION)biHeight, (JDIMENSION)1);
611 source->pub.get_pixel_rows = preload_image;
612 if (cinfo->progress != NULL) {
613 cd_progress_ptr progress = (cd_progress_ptr)cinfo->progress;
614 progress->total_extra_passes++; /* count file input as separate pass */
615 }
616 } else {
617 source->iobuffer = (U_CHAR *)
618 (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE, row_width);
619 switch (source->bits_per_pixel) {
620 case 8:
621 source->pub.get_pixel_rows = get_8bit_row;
622 break;
623 case 24:
624 source->pub.get_pixel_rows = get_24bit_row;
625 break;
626 case 32:
627 source->pub.get_pixel_rows = get_32bit_row;
628 break;
629 default:
630 ERREXIT(cinfo, JERR_BMP_BADDEPTH);
631 }
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000632 }
633
Chris Blumecca8c4d2019-03-01 01:09:50 -0800634 /* Ensure that biWidth * cinfo->input_components doesn't exceed the maximum
635 value of the JDIMENSION type. This is only a danger with BMP files, since
636 their width and height fields are 32-bit integers. */
637 if ((unsigned long long)biWidth *
638 (unsigned long long)cinfo->input_components > 0xFFFFFFFFULL)
639 ERREXIT(cinfo, JERR_WIDTH_OVERFLOW);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000640 /* Allocate one-row buffer for returned data */
641 source->pub.buffer = (*cinfo->mem->alloc_sarray)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800642 ((j_common_ptr)cinfo, JPOOL_IMAGE,
Jonathan Wright24e31052021-04-26 12:10:48 +0100643 (JDIMENSION)biWidth * (JDIMENSION)cinfo->input_components, (JDIMENSION)1);
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000644 source->pub.buffer_height = 1;
645
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000646 cinfo->data_precision = 8;
Chris Blumecca8c4d2019-03-01 01:09:50 -0800647 cinfo->image_width = (JDIMENSION)biWidth;
648 cinfo->image_height = (JDIMENSION)biHeight;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000649}
650
651
652/*
653 * Finish up at the end of the file.
654 */
655
656METHODDEF(void)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800657finish_input_bmp(j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000658{
659 /* no work */
660}
661
662
663/*
664 * The module selection routine for BMP format input.
665 */
666
667GLOBAL(cjpeg_source_ptr)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800668jinit_read_bmp(j_compress_ptr cinfo, boolean use_inversion_array)
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000669{
670 bmp_source_ptr source;
671
672 /* Create module interface object */
673 source = (bmp_source_ptr)
Chris Blumecca8c4d2019-03-01 01:09:50 -0800674 (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE,
675 sizeof(bmp_source_struct));
Tom Hudson0d47d2d2016-05-04 13:22:56 -0400676 source->cinfo = cinfo; /* make back link for subroutines */
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000677 /* Fill in method ptrs, except get_pixel_rows which start_input sets */
678 source->pub.start_input = start_input_bmp;
679 source->pub.finish_input = finish_input_bmp;
Jonathan Wright24e31052021-04-26 12:10:48 +0100680#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
681 source->pub.max_pixels = 0;
682#endif
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000683
Chris Blumecca8c4d2019-03-01 01:09:50 -0800684 source->use_inversion_array = use_inversion_array;
685
686 return (cjpeg_source_ptr)source;
hbono@chromium.orgf0c4f332010-11-01 05:14:55 +0000687}
688
689#endif /* BMP_SUPPORTED */