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