source: trunk/dll/loadbmp.c@ 1570

Last change on this file since 1570 was 1544, checked in by Gregg Young, 15 years ago

Changes to fopen and _fsopen to allow FM2 to be loaded in high memory

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