source: trunk/dll/loadbmp.c@ 1036

Last change on this file since 1036 was 1009, checked in by Steven Levine, 17 years ago

Add xfree xstrdup Fortify support
Add MT capable Fortify scope logic

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