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