source: trunk/dll/loadbmp.c@ 1673

Last change on this file since 1673 was 1673, checked in by Gregg Young, 13 years ago

Update to Doxygen comment style Ticket 55. Also some minor code cleanup.

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