source: trunk/dll/loadbmp.c@ 1102

Last change on this file since 1102 was 1082, checked in by Gregg Young, 17 years ago

Changes so FM2 will use TMP/TEMP directory for all temp files; Replaced save_dir2 with global variable so BldFullPathName could easily replace code that performed the same function; Added #ifdef FORTIFY to free_ function that are only used when fortified.

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