source: trunk/dll/loadbmp.c@ 1181

Last change on this file since 1181 was 1181, checked in by John Small, 17 years ago

Ticket 187: Draft 2: Move remaining function declarations

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