source: trunk/dll/loadbmp.c@ 1209

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

Ticket 187: Move data declarations/definitions out of fm3dll.h

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.8 KB
RevLine 
[351]1
2/***********************************************************************
3
4 $Id: loadbmp.c 1209 2008-09-13 06:51:06Z jbs $
5
6 Load bitmaps
7
8 Copyright (c) 1993-98 M. Kimes
[1082]9 Copyright (c) 2006, 2008 Steven H. Levine
[351]10
11 22 Jul 06 SHL Check more run time errors
[549]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
[1001]16 18 Apr 08 SHL LoadBitmapFromFile ensure pf initialized if no hPS
[1082]17 19 Jul 08 GKY Replace save_dir2(dir) with pFM2SaveDirectory
18
[351]19***********************************************************************/
20
[2]21#include <stdlib.h>
22#include <stdio.h>
23#include <string.h>
24#include <share.h>
[351]25
[907]26#define INCL_DOS
27#define INCL_WIN
28#define INCL_GPI
29#define INCL_LONGLONG // dircnrs.h
30
[1181]31#include "fm3dll.h"
[1209]32#include "init.h" // Data declaration(s)
[907]33#include "errutil.h" // Dos_Error...
[1158]34#include "loadbmp.h"
[1181]35#include "wrappers.h" // xfree
36#include "dirs.h" // save_dir2
[2]37
[351]38static PSZ pszSrcFile = __FILE__;
39
[1158]40static HBITMAP LoadBitmapFromFile(CHAR * pszFileName);
41
[549]42HBITMAP LoadBitmapFromFileNum(USHORT id)
[351]43{
[549]44 char s[CCHMAXPATH];
[2]45
[1082]46 strcpy(s, pFM2SaveDirectory);
[549]47 sprintf(s + strlen(s), "\\%u.BMP", id);
[2]48 return LoadBitmapFromFile(s);
49}
50
[549]51HBITMAP LoadBitmapFromFile(CHAR * pszFileName)
[351]52{
[549]53 HBITMAP hBmp = (HBITMAP) 0;
54 FILE *pf;
55 ULONG rc;
56 USHORT usType;
57 PBITMAPARRAYFILEHEADER2 pbmafh2 = NULL; // Must init for xfree
[551]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
[549]62 ULONG ulColors;
63 ULONG ulRGBOffset;
[551]64 PBYTE pData = NULL; // Must init for xfree
[549]65 ULONG ulDataSize;
66 SIZEL sizel;
67 HPS hPS = WinGetPS(HWND_DESKTOP);
[2]68
[549]69 if (!hPS) {
[551]70 Win_Error(HWND_DESKTOP, HWND_DESKTOP, pszSrcFile, __LINE__, "WinGetPS");
[1001]71 pf = NULL;
[2]72 goto ExitLoadBMP;
[549]73 }
[2]74
[549]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);
[2]79 goto ExitLoadBMP;
[549]80 }
[2]81
[549]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");
[2]87 goto ExitLoadBMP;
[549]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?
[551]93 */
[549]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
[551]101 */
[549]102 pbmfh2 = &pbmafh2->bfh2;
103 pbmih2 = &pbmfh2->bmp2;
[551]104 pbmi2 = (PBITMAPINFO2) pbmih2;
[549]105
[2]106 switch (usType) {
[549]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
[551]114 OK for 1.x file - read will not fail unless file is corrupted
115 */
[549]116 rc = fseek(pf, 0, SEEK_SET);
117 if (rc) {
118 Runtime_Error(pszSrcFile, __LINE__, "fseek 0");
119 goto ExitLoadBMP;
120 }
[2]121
[549]122 rc = fread(pbmfh2, 1, sizeof(*pbmfh2), pf);
123 if (rc != sizeof(*pbmfh2)) {
124 Runtime_Error(pszSrcFile, __LINE__, "fread");
125 goto ExitLoadBMP;
[2]126 }
127
[549]128 is2x = pbmih2->cbFix > sizeof(BITMAPINFOHEADER); // 1.x or 2.x bitmap
129 /* We will read the color table later
[551]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 */
[549]136 ulRGBOffset = is2x ? sizeof(*pbmfh2) - sizeof(*pbmih2) + pbmih2->cbFix :
[551]137 sizeof(BITMAPFILEHEADER);
[549]138 }
139 break;
[2]140
[549]141 case BFT_BITMAPARRAY:
142 {
143 /* Now we are dealing with a bitmap array which is a collection of bitmaps
[551]144 Each bitmap has its own file header
145 */
[2]146
[549]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;
[2]156
[549]157 /* Scan the array and chose the bitmap best suited
[551]158 for the current display size and color capacities
159 */
[549]160 hdc = GpiQueryDevice(hPS);
161 if (!hdc) {
162 Win_Error(HWND_DESKTOP, HWND_DESKTOP, pszSrcFile, __LINE__,
163 "GpiQueryDevice");
164 goto ExitLoadBMP;
165 }
[551]166 DevQueryCaps(hdc, CAPS_COLORS, 1, (PLONG) & ulDeviceColors);
167 DevQueryCaps(hdc, CAPS_WIDTH, 1, (PLONG) & clScreenWidth);
168 DevQueryCaps(hdc, CAPS_HEIGHT, 1, (PLONG) & clScreenHeight);
[549]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 {
[551]188 ulColors =
189 1 << (((PBITMAPARRAYFILEHEADER) pbmafh2)->bfh.bmp.cBitCount *
190 ((PBITMAPARRAYFILEHEADER) pbmafh2)->bfh.bmp.cPlanes);
[549]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) +
[551]198 abs(pbmafh2->cyDisplay - clScreenHeight);
[549]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
[551]207 (ulColors < ulColorsPicked && ulColors > ulDeviceColors)) {
[549]208 ulOffsetPicked = ulCurOffset; // Make this our current pick
209 ulColorsPicked = ulColors;
210 ulSizeDiffPicked = ulSizeDiff;
211 }
212 } while (pbmafh2->offNext != 0);
[2]213
[549]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;
[2]219 }
[549]220 rc = fread(pbmafh2, 1, sizeof(*pbmafh2), pf);
221 if (rc != sizeof(*pbmafh2)) {
222 Runtime_Error(pszSrcFile, __LINE__, "fread");
223 goto ExitLoadBMP;
224 }
[2]225
[549]226 is2x = pbmih2->cbFix > sizeof(BITMAPINFOHEADER);
227 /* As before, we calculate offset in file stream to color table
[551]228 This code must match single bitmap logic
229 */
[549]230 ulRGBOffset = ulOffsetPicked;
[551]231 ulRGBOffset +=
232 is2x ? sizeof(*pbmafh2) - sizeof(*pbmih2) +
233 pbmih2->cbFix : sizeof(BITMAPARRAYFILEHEADER);
[549]234 }
235 break;
[2]236
[549]237 default:
238 Runtime_Error(pszSrcFile, __LINE__, "Bad type %u", usType);
[2]239 goto ExitLoadBMP;
[549]240 } // endswitch
[2]241
[549]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 }
[2]248
[549]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
[551]256 */
[549]257 if (pbmih2->cBitCount < 24) {
258 ULONG ulRGBBytes;
[551]259
[549]260 ulColors = 1L << pbmih2->cBitCount;
[2]261
[549]262 if (ulColors > 256) {
[551]263 Runtime_Error(pszSrcFile, __LINE__, "RGB exceeds 256 colors: %lu",
264 ulColors);
[549]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)
[551]275 pbmi2 = (PBITMAPINFO2) pbmih2;
[2]276 }
277 else {
[549]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
[551]281 */
[549]282 ULONG ul;
283 RGB rgb;
[551]284 PBITMAPINFOHEADER pbmih = &((PBITMAPARRAYFILEHEADER) pbmafh2)->bfh.bmp;
[2]285
[549]286 if (pbmih->cBitCount < 24) {
287 ulColors = 1 << pbmih->cBitCount;
288 if (ulColors > 256) {
[551]289 Runtime_Error(pszSrcFile, __LINE__, "RGB exceeds 256 colors: %lu",
290 ulColors);
[549]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
[551]300 } // for
[549]301 }
[2]302
[549]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;
[2]309 // set rest to zero
[551]310 memset((PCHAR) pbmi2 + 16, 0, sizeof(BITMAPINFOHEADER2) - 16);
311 } // if 1.x
[2]312
[549]313 /* The 2.0 bitmap info structure set up
314 Position to start of the bitmap data
[551]315 */
[549]316 rc = fseek(pf, pbmfh2->offBits, SEEK_SET);
317 if (rc) {
318 Runtime_Error(pszSrcFile, __LINE__, "fseek %ld", pbmfh2->offBits);
[2]319 goto ExitLoadBMP;
[549]320 }
[2]321
[549]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
[551]328 */
[549]329 if (pbmi2->ulCompression)
[2]330 ulDataSize = pbmi2->cbImage;
331 else
332 ulDataSize = (((pbmi2->cBitCount * pbmi2->cx) + 31) / 32) * 4 *
[549]333 pbmi2->cy * pbmi2->cPlanes;
334 pData = xmalloc(ulDataSize, pszSrcFile, __LINE__);
335 if (!pData)
[2]336 goto ExitLoadBMP;
[549]337 rc = fread(pData, 1, ulDataSize, pf);
338 if (rc != ulDataSize) {
339 Runtime_Error(pszSrcFile, __LINE__, "fread");
[2]340 goto ExitLoadBMP;
[549]341 }
[2]342
[549]343 // Create the GPI bitmap image
[2]344 sizel.cx = pbmi2->cx;
345 sizel.cy = pbmi2->cy;
346
[549]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");
[2]352
353ExitLoadBMP:
[351]354
[1009]355 xfree(pData, pszSrcFile, __LINE__);
356 xfree(pbmafh2, pszSrcFile, __LINE__);
[549]357 if (pf)
358 fclose(pf);
359 if (hPS)
360 WinReleasePS(hPS);
[351]361 return hBmp;
[2]362}
[793]363
364#pragma alloc_text(LOADBITMAP,LoadBitmapFromFile,LoadBitmapFromFileNum)
Note: See TracBrowser for help on using the repository browser.