source: trunk/dll/loadbmp.c@ 907

Last change on this file since 907 was 907, checked in by Steven Levine, 18 years ago

Avoid out of memory traps in Compare Directories
Rework Compare Directories progress display for 2 second update rate
Start refactoring to reduce dependence on fm3dll.h
Add timer services (IsITimerExpired etc.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 KB
Line 
1
2/***********************************************************************
3
4 $Id: loadbmp.c 907 2008-01-06 07:26:17Z stevenhl $
5
6 Load bitmaps
7
8 Copyright (c) 1993-98 M. Kimes
9 Copyright (c) 2006, 2007 Steven H.Levine
10
11 22 Jul 06 SHL Check more run time errors
12 16 Jan 07 SHL Check more run time errors
13 16 Jan 07 SHL Sync variable names for sanity
14 16 Jan 07 SHL Open bitmap file binary - no wonder the code does not work
15 16 Jan 07 SHL Beautify with indent -i2
16
17***********************************************************************/
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22#include <share.h>
23
24#define INCL_DOS
25#define INCL_WIN
26#define INCL_GPI
27#define INCL_LONGLONG // dircnrs.h
28
29#include "errutil.h" // Dos_Error...
30#include "fm3dll.h"
31
32static PSZ pszSrcFile = __FILE__;
33
34HBITMAP LoadBitmapFromFileNum(USHORT id)
35{
36 char s[CCHMAXPATH];
37
38 save_dir2(s);
39 sprintf(s + strlen(s), "\\%u.BMP", id);
40 return LoadBitmapFromFile(s);
41}
42
43HBITMAP LoadBitmapFromFile(CHAR * pszFileName)
44{
45 HBITMAP hBmp = (HBITMAP) 0;
46 FILE *pf;
47 ULONG rc;
48 USHORT usType;
49 PBITMAPARRAYFILEHEADER2 pbmafh2 = NULL; // Must init for xfree
50 PBITMAPFILEHEADER2 pbmfh2; // No color table
51 PBITMAPINFOHEADER2 pbmih2; // No color table
52 PBITMAPINFO2 pbmi2; // Includes color table
53 BOOL is2x; // Format 1.x or 2.x
54 ULONG ulColors;
55 ULONG ulRGBOffset;
56 PBYTE pData = NULL; // Must init for xfree
57 ULONG ulDataSize;
58 SIZEL sizel;
59 HPS hPS = WinGetPS(HWND_DESKTOP);
60
61 if (!hPS) {
62 Win_Error(HWND_DESKTOP, HWND_DESKTOP, pszSrcFile, __LINE__, "WinGetPS");
63 goto ExitLoadBMP;
64 }
65
66 pf = _fsopen(pszFileName, "rb", SH_DENYWR);
67 if (!pf) {
68 // OK for file to not exist - enable following for debug as needed
69 // Runtime_Error(pszSrcFile, __LINE__, "_fsopen %s", pszFileName);
70 goto ExitLoadBMP;
71 }
72
73 // Read image type
74 // fixme to just read type2 header - this is silly and wastes time
75 rc = fread(&usType, 1, sizeof(usType), pf);
76 if (rc != sizeof(usType)) {
77 Runtime_Error(pszSrcFile, __LINE__, "fread");
78 goto ExitLoadBMP;
79 }
80
81 /* Read bitmap info header
82 Allocate enough to hold a complete 2.x bitmap array file header
83 fixme to support > 256 colors?
84 */
85 pbmafh2 =
86 xmalloc(sizeof(*pbmafh2) + 256 * sizeof(RGB2), pszSrcFile, __LINE__);
87 if (!pbmafh2)
88 goto ExitLoadBMP;
89 /* Assign pointers to the file header and bitmap info header etc.
90 Both the 1.x and 2.x structures are assigned to simplify code
91 fixme to clean this up - aliased pointers are evil
92 */
93 pbmfh2 = &pbmafh2->bfh2;
94 pbmih2 = &pbmfh2->bmp2;
95 pbmi2 = (PBITMAPINFO2) pbmih2;
96
97 switch (usType) {
98 case BFT_BMAP:
99 case BFT_ICON:
100 case BFT_POINTER:
101 case BFT_COLORICON:
102 case BFT_COLORPOINTER:
103 {
104 /* Assume image is a 2.0 image and read as a 2.x header
105 OK for 1.x file - read will not fail unless file is corrupted
106 */
107 rc = fseek(pf, 0, SEEK_SET);
108 if (rc) {
109 Runtime_Error(pszSrcFile, __LINE__, "fseek 0");
110 goto ExitLoadBMP;
111 }
112
113 rc = fread(pbmfh2, 1, sizeof(*pbmfh2), pf);
114 if (rc != sizeof(*pbmfh2)) {
115 Runtime_Error(pszSrcFile, __LINE__, "fread");
116 goto ExitLoadBMP;
117 }
118
119 is2x = pbmih2->cbFix > sizeof(BITMAPINFOHEADER); // 1.x or 2.x bitmap
120 /* We will read the color table later
121 Color table follows header but
122 location depends on the type of the bitmap (old vs new)
123 1.x header is fixed size
124 2.x header is variable sized, so offset must be calculated
125 cbFix contains actual size of BITMAPINFOHEADER2 in file
126 */
127 ulRGBOffset = is2x ? sizeof(*pbmfh2) - sizeof(*pbmih2) + pbmih2->cbFix :
128 sizeof(BITMAPFILEHEADER);
129 }
130 break;
131
132 case BFT_BITMAPARRAY:
133 {
134 /* Now we are dealing with a bitmap array which is a collection of bitmaps
135 Each bitmap has its own file header
136 */
137
138 ULONG ulCurOffset;
139 ULONG clScreenWidth;
140 ULONG clScreenHeight;
141 ULONG ulDeviceColors;
142 ULONG ulSizeDiff;
143 ULONG ulOffsetPicked = 0;
144 ULONG ulColorsPicked;
145 ULONG ulSizeDiffPicked;
146 HDC hdc;
147
148 /* Scan the array and chose the bitmap best suited
149 for the current display size and color capacities
150 */
151 hdc = GpiQueryDevice(hPS);
152 if (!hdc) {
153 Win_Error(HWND_DESKTOP, HWND_DESKTOP, pszSrcFile, __LINE__,
154 "GpiQueryDevice");
155 goto ExitLoadBMP;
156 }
157 DevQueryCaps(hdc, CAPS_COLORS, 1, (PLONG) & ulDeviceColors);
158 DevQueryCaps(hdc, CAPS_WIDTH, 1, (PLONG) & clScreenWidth);
159 DevQueryCaps(hdc, CAPS_HEIGHT, 1, (PLONG) & clScreenHeight);
160 pbmafh2->offNext = 0;
161 do {
162 ulCurOffset = pbmafh2->offNext;
163 rc = fseek(pf, pbmafh2->offNext, SEEK_SET);
164 if (rc) {
165 Runtime_Error(pszSrcFile, __LINE__, "fseek %ld", pbmafh2->offNext);
166 goto ExitLoadBMP;
167 }
168 rc = fread(pbmafh2, 1, sizeof(*pbmafh2), pf);
169 if (rc != sizeof(*pbmafh2)) {
170 Runtime_Error(pszSrcFile, __LINE__, "fread");
171 goto ExitLoadBMP;
172 }
173 is2x = pbmih2->cbFix > sizeof(BITMAPINFOHEADER);
174 if (is2x) {
175 ulColors = 1 << (pbmafh2->bfh2.bmp2.cBitCount *
176 pbmafh2->bfh2.bmp2.cPlanes);
177 }
178 else {
179 ulColors =
180 1 << (((PBITMAPARRAYFILEHEADER) pbmafh2)->bfh.bmp.cBitCount *
181 ((PBITMAPARRAYFILEHEADER) pbmafh2)->bfh.bmp.cPlanes);
182 }
183 if (pbmafh2->cxDisplay == 0 && pbmafh2->cyDisplay == 0) {
184 // This is a device independant bitmap - process it as a VGA
185 pbmafh2->cxDisplay = 640;
186 pbmafh2->cyDisplay = 480;
187 }
188 ulSizeDiff = abs(pbmafh2->cxDisplay - clScreenWidth) +
189 abs(pbmafh2->cyDisplay - clScreenHeight);
190 if (ulDeviceColors == ulColors && ulSizeDiff == 0) {
191 // We found the perfect match
192 ulOffsetPicked = ulCurOffset;
193 break; // Stop scan
194 }
195 if (ulOffsetPicked == 0 || // First time thru
196 ulSizeDiff < ulSizeDiffPicked || // Better fit than any previous
197 (ulColors > ulColorsPicked && ulColors < ulDeviceColors) || // More colors than prev & less than device
198 (ulColors < ulColorsPicked && ulColors > ulDeviceColors)) {
199 ulOffsetPicked = ulCurOffset; // Make this our current pick
200 ulColorsPicked = ulColors;
201 ulSizeDiffPicked = ulSizeDiff;
202 }
203 } while (pbmafh2->offNext != 0);
204
205 // Retrieve the selected bitmap
206 rc = fseek(pf, ulOffsetPicked, SEEK_SET);
207 if (rc) {
208 Runtime_Error(pszSrcFile, __LINE__, "fseek %ld", ulOffsetPicked);
209 goto ExitLoadBMP;
210 }
211 rc = fread(pbmafh2, 1, sizeof(*pbmafh2), pf);
212 if (rc != sizeof(*pbmafh2)) {
213 Runtime_Error(pszSrcFile, __LINE__, "fread");
214 goto ExitLoadBMP;
215 }
216
217 is2x = pbmih2->cbFix > sizeof(BITMAPINFOHEADER);
218 /* As before, we calculate offset in file stream to color table
219 This code must match single bitmap logic
220 */
221 ulRGBOffset = ulOffsetPicked;
222 ulRGBOffset +=
223 is2x ? sizeof(*pbmafh2) - sizeof(*pbmih2) +
224 pbmih2->cbFix : sizeof(BITMAPARRAYFILEHEADER);
225 }
226 break;
227
228 default:
229 Runtime_Error(pszSrcFile, __LINE__, "Bad type %u", usType);
230 goto ExitLoadBMP;
231 } // endswitch
232
233 // Position to color table
234 rc = fseek(pf, ulRGBOffset, SEEK_SET);
235 if (rc) {
236 Runtime_Error(pszSrcFile, __LINE__, "fseek %ld", ulRGBOffset);
237 goto ExitLoadBMP;
238 }
239
240 // Read color table
241 if (is2x) {
242 /* For a 2.0 bitmap, read the color table as is
243 The bitmap info structure is header + color table
244 If we have 24 bits per pel, there is usually no color table, unless
245 pbmih2->cclrUsed or pbmih2->cclrImportant are non zero
246 fixme to test this
247 */
248 if (pbmih2->cBitCount < 24) {
249 ULONG ulRGBBytes;
250
251 ulColors = 1L << pbmih2->cBitCount;
252
253 if (ulColors > 256) {
254 Runtime_Error(pszSrcFile, __LINE__, "RGB exceeds 256 colors: %lu",
255 ulColors);
256 goto ExitLoadBMP;
257 }
258 ulRGBBytes = ulColors * sizeof(RGB2);
259 rc = fread(&pbmi2->argbColor[0], 1, ulRGBBytes, pf);
260 if (rc != ulRGBBytes) {
261 Runtime_Error(pszSrcFile, __LINE__, "fread");
262 goto ExitLoadBMP;
263 }
264 } // endif
265 // Get pointer to bitmap info (header and color table)
266 pbmi2 = (PBITMAPINFO2) pbmih2;
267 }
268 else {
269 /* This is a 1.x format bitmap
270 Since the current standard format is the 2.0
271 convert the header and color table to 2.x format
272 */
273 ULONG ul;
274 RGB rgb;
275 PBITMAPINFOHEADER pbmih = &((PBITMAPARRAYFILEHEADER) pbmafh2)->bfh.bmp;
276
277 if (pbmih->cBitCount < 24) {
278 ulColors = 1 << pbmih->cBitCount;
279 if (ulColors > 256) {
280 Runtime_Error(pszSrcFile, __LINE__, "RGB exceeds 256 colors: %lu",
281 ulColors);
282 goto ExitLoadBMP;
283 }
284 // Read in 1.x color table and reformat for 2.x
285 for (ul = 0; ul < ulColors; ul++) {
286 fread(&rgb, 1, sizeof(rgb), pf);
287 pbmi2->argbColor[ul].bRed = rgb.bRed;
288 pbmi2->argbColor[ul].bGreen = rgb.bGreen;
289 pbmi2->argbColor[ul].bBlue = rgb.bBlue;
290 pbmi2->argbColor[ul].fcOptions = 0; // initialize 2.x extra byte to 0
291 } // for
292 }
293
294 // Convert the old style to the new header format
295 pbmi2->cbFix = sizeof(BITMAPINFOHEADER2);
296 pbmi2->cBitCount = pbmih->cBitCount;
297 pbmi2->cPlanes = pbmih->cPlanes;
298 pbmi2->cy = pbmih->cy;
299 pbmi2->cx = pbmih->cx;
300 // set rest to zero
301 memset((PCHAR) pbmi2 + 16, 0, sizeof(BITMAPINFOHEADER2) - 16);
302 } // if 1.x
303
304 /* The 2.0 bitmap info structure set up
305 Position to start of the bitmap data
306 */
307 rc = fseek(pf, pbmfh2->offBits, SEEK_SET);
308 if (rc) {
309 Runtime_Error(pszSrcFile, __LINE__, "fseek %ld", pbmfh2->offBits);
310 goto ExitLoadBMP;
311 }
312
313 /* Read the bitmap data
314 The read size is derived using the magic formula
315 Each bitmap scan line is aligned on a doubleword boundary
316 The size of the scan line is the number of pels times the bpp
317 After aligning it, we divide by 4 to get the number of bytes, and
318 multiply by the number of scan lines and the number of pel planes
319 */
320 if (pbmi2->ulCompression)
321 ulDataSize = pbmi2->cbImage;
322 else
323 ulDataSize = (((pbmi2->cBitCount * pbmi2->cx) + 31) / 32) * 4 *
324 pbmi2->cy * pbmi2->cPlanes;
325 pData = xmalloc(ulDataSize, pszSrcFile, __LINE__);
326 if (!pData)
327 goto ExitLoadBMP;
328 rc = fread(pData, 1, ulDataSize, pf);
329 if (rc != ulDataSize) {
330 Runtime_Error(pszSrcFile, __LINE__, "fread");
331 goto ExitLoadBMP;
332 }
333
334 // Create the GPI bitmap image
335 sizel.cx = pbmi2->cx;
336 sizel.cy = pbmi2->cy;
337
338 hBmp = GpiCreateBitmap(hPS, (PBITMAPINFOHEADER2) pbmi2, CBM_INIT,
339 pData, pbmi2);
340 if (!hBmp)
341 Win_Error(HWND_DESKTOP, HWND_DESKTOP, pszSrcFile, __LINE__,
342 "GpiCreateBitmap");
343
344ExitLoadBMP:
345
346 xfree(pData);
347 xfree(pbmafh2);
348 if (pf)
349 fclose(pf);
350 if (hPS)
351 WinReleasePS(hPS);
352 return hBmp;
353}
354
355#pragma alloc_text(LOADBITMAP,LoadBitmapFromFile,LoadBitmapFromFileNum)
Note: See TracBrowser for help on using the repository browser.