source: trunk/dll/loadbmp.c@ 1158

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

Ticket 187: Draft 1: Functions only

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