source: trunk/dll/loadbmp.c@ 1438

Last change on this file since 1438 was 1438, checked in by Gregg Young, 16 years ago

Improved drivebar changes; Added AddBackslashToPath() to remove repeatative code. replaced "
" with PCSZ variable; ANY_OBJ added the DosAlloc... (experimental)

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