/*********************************************************************** $Id: loadbmp.c 1009 2008-05-10 07:51:58Z stevenhl $ Load bitmaps Copyright (c) 1993-98 M. Kimes Copyright (c) 2006, 2007 Steven H. Levine 22 Jul 06 SHL Check more run time errors 16 Jan 07 SHL Check more run time errors 16 Jan 07 SHL Sync variable names for sanity 16 Jan 07 SHL Open bitmap file binary - no wonder the code does not work 16 Jan 07 SHL Beautify with indent -i2 18 Apr 08 SHL LoadBitmapFromFile ensure pf initialized if no hPS ***********************************************************************/ #include #include #include #include #define INCL_DOS #define INCL_WIN #define INCL_GPI #define INCL_LONGLONG // dircnrs.h #include "errutil.h" // Dos_Error... #include "fm3dll.h" static PSZ pszSrcFile = __FILE__; HBITMAP LoadBitmapFromFileNum(USHORT id) { char s[CCHMAXPATH]; save_dir2(s); sprintf(s + strlen(s), "\\%u.BMP", id); return LoadBitmapFromFile(s); } HBITMAP LoadBitmapFromFile(CHAR * pszFileName) { HBITMAP hBmp = (HBITMAP) 0; FILE *pf; ULONG rc; USHORT usType; PBITMAPARRAYFILEHEADER2 pbmafh2 = NULL; // Must init for xfree PBITMAPFILEHEADER2 pbmfh2; // No color table PBITMAPINFOHEADER2 pbmih2; // No color table PBITMAPINFO2 pbmi2; // Includes color table BOOL is2x; // Format 1.x or 2.x ULONG ulColors; ULONG ulRGBOffset; PBYTE pData = NULL; // Must init for xfree ULONG ulDataSize; SIZEL sizel; HPS hPS = WinGetPS(HWND_DESKTOP); if (!hPS) { Win_Error(HWND_DESKTOP, HWND_DESKTOP, pszSrcFile, __LINE__, "WinGetPS"); pf = NULL; goto ExitLoadBMP; } pf = _fsopen(pszFileName, "rb", SH_DENYWR); if (!pf) { // OK for file to not exist - enable following for debug as needed // Runtime_Error(pszSrcFile, __LINE__, "_fsopen %s", pszFileName); goto ExitLoadBMP; } // Read image type // fixme to just read type2 header - this is silly and wastes time rc = fread(&usType, 1, sizeof(usType), pf); if (rc != sizeof(usType)) { Runtime_Error(pszSrcFile, __LINE__, "fread"); goto ExitLoadBMP; } /* Read bitmap info header Allocate enough to hold a complete 2.x bitmap array file header fixme to support > 256 colors? */ pbmafh2 = xmalloc(sizeof(*pbmafh2) + 256 * sizeof(RGB2), pszSrcFile, __LINE__); if (!pbmafh2) goto ExitLoadBMP; /* Assign pointers to the file header and bitmap info header etc. Both the 1.x and 2.x structures are assigned to simplify code fixme to clean this up - aliased pointers are evil */ pbmfh2 = &pbmafh2->bfh2; pbmih2 = &pbmfh2->bmp2; pbmi2 = (PBITMAPINFO2) pbmih2; switch (usType) { case BFT_BMAP: case BFT_ICON: case BFT_POINTER: case BFT_COLORICON: case BFT_COLORPOINTER: { /* Assume image is a 2.0 image and read as a 2.x header OK for 1.x file - read will not fail unless file is corrupted */ rc = fseek(pf, 0, SEEK_SET); if (rc) { Runtime_Error(pszSrcFile, __LINE__, "fseek 0"); goto ExitLoadBMP; } rc = fread(pbmfh2, 1, sizeof(*pbmfh2), pf); if (rc != sizeof(*pbmfh2)) { Runtime_Error(pszSrcFile, __LINE__, "fread"); goto ExitLoadBMP; } is2x = pbmih2->cbFix > sizeof(BITMAPINFOHEADER); // 1.x or 2.x bitmap /* We will read the color table later Color table follows header but location depends on the type of the bitmap (old vs new) 1.x header is fixed size 2.x header is variable sized, so offset must be calculated cbFix contains actual size of BITMAPINFOHEADER2 in file */ ulRGBOffset = is2x ? sizeof(*pbmfh2) - sizeof(*pbmih2) + pbmih2->cbFix : sizeof(BITMAPFILEHEADER); } break; case BFT_BITMAPARRAY: { /* Now we are dealing with a bitmap array which is a collection of bitmaps Each bitmap has its own file header */ ULONG ulCurOffset; ULONG clScreenWidth; ULONG clScreenHeight; ULONG ulDeviceColors; ULONG ulSizeDiff; ULONG ulOffsetPicked = 0; ULONG ulColorsPicked; ULONG ulSizeDiffPicked; HDC hdc; /* Scan the array and chose the bitmap best suited for the current display size and color capacities */ hdc = GpiQueryDevice(hPS); if (!hdc) { Win_Error(HWND_DESKTOP, HWND_DESKTOP, pszSrcFile, __LINE__, "GpiQueryDevice"); goto ExitLoadBMP; } DevQueryCaps(hdc, CAPS_COLORS, 1, (PLONG) & ulDeviceColors); DevQueryCaps(hdc, CAPS_WIDTH, 1, (PLONG) & clScreenWidth); DevQueryCaps(hdc, CAPS_HEIGHT, 1, (PLONG) & clScreenHeight); pbmafh2->offNext = 0; do { ulCurOffset = pbmafh2->offNext; rc = fseek(pf, pbmafh2->offNext, SEEK_SET); if (rc) { Runtime_Error(pszSrcFile, __LINE__, "fseek %ld", pbmafh2->offNext); goto ExitLoadBMP; } rc = fread(pbmafh2, 1, sizeof(*pbmafh2), pf); if (rc != sizeof(*pbmafh2)) { Runtime_Error(pszSrcFile, __LINE__, "fread"); goto ExitLoadBMP; } is2x = pbmih2->cbFix > sizeof(BITMAPINFOHEADER); if (is2x) { ulColors = 1 << (pbmafh2->bfh2.bmp2.cBitCount * pbmafh2->bfh2.bmp2.cPlanes); } else { ulColors = 1 << (((PBITMAPARRAYFILEHEADER) pbmafh2)->bfh.bmp.cBitCount * ((PBITMAPARRAYFILEHEADER) pbmafh2)->bfh.bmp.cPlanes); } if (pbmafh2->cxDisplay == 0 && pbmafh2->cyDisplay == 0) { // This is a device independant bitmap - process it as a VGA pbmafh2->cxDisplay = 640; pbmafh2->cyDisplay = 480; } ulSizeDiff = abs(pbmafh2->cxDisplay - clScreenWidth) + abs(pbmafh2->cyDisplay - clScreenHeight); if (ulDeviceColors == ulColors && ulSizeDiff == 0) { // We found the perfect match ulOffsetPicked = ulCurOffset; break; // Stop scan } if (ulOffsetPicked == 0 || // First time thru ulSizeDiff < ulSizeDiffPicked || // Better fit than any previous (ulColors > ulColorsPicked && ulColors < ulDeviceColors) || // More colors than prev & less than device (ulColors < ulColorsPicked && ulColors > ulDeviceColors)) { ulOffsetPicked = ulCurOffset; // Make this our current pick ulColorsPicked = ulColors; ulSizeDiffPicked = ulSizeDiff; } } while (pbmafh2->offNext != 0); // Retrieve the selected bitmap rc = fseek(pf, ulOffsetPicked, SEEK_SET); if (rc) { Runtime_Error(pszSrcFile, __LINE__, "fseek %ld", ulOffsetPicked); goto ExitLoadBMP; } rc = fread(pbmafh2, 1, sizeof(*pbmafh2), pf); if (rc != sizeof(*pbmafh2)) { Runtime_Error(pszSrcFile, __LINE__, "fread"); goto ExitLoadBMP; } is2x = pbmih2->cbFix > sizeof(BITMAPINFOHEADER); /* As before, we calculate offset in file stream to color table This code must match single bitmap logic */ ulRGBOffset = ulOffsetPicked; ulRGBOffset += is2x ? sizeof(*pbmafh2) - sizeof(*pbmih2) + pbmih2->cbFix : sizeof(BITMAPARRAYFILEHEADER); } break; default: Runtime_Error(pszSrcFile, __LINE__, "Bad type %u", usType); goto ExitLoadBMP; } // endswitch // Position to color table rc = fseek(pf, ulRGBOffset, SEEK_SET); if (rc) { Runtime_Error(pszSrcFile, __LINE__, "fseek %ld", ulRGBOffset); goto ExitLoadBMP; } // Read color table if (is2x) { /* For a 2.0 bitmap, read the color table as is The bitmap info structure is header + color table If we have 24 bits per pel, there is usually no color table, unless pbmih2->cclrUsed or pbmih2->cclrImportant are non zero fixme to test this */ if (pbmih2->cBitCount < 24) { ULONG ulRGBBytes; ulColors = 1L << pbmih2->cBitCount; if (ulColors > 256) { Runtime_Error(pszSrcFile, __LINE__, "RGB exceeds 256 colors: %lu", ulColors); goto ExitLoadBMP; } ulRGBBytes = ulColors * sizeof(RGB2); rc = fread(&pbmi2->argbColor[0], 1, ulRGBBytes, pf); if (rc != ulRGBBytes) { Runtime_Error(pszSrcFile, __LINE__, "fread"); goto ExitLoadBMP; } } // endif // Get pointer to bitmap info (header and color table) pbmi2 = (PBITMAPINFO2) pbmih2; } else { /* This is a 1.x format bitmap Since the current standard format is the 2.0 convert the header and color table to 2.x format */ ULONG ul; RGB rgb; PBITMAPINFOHEADER pbmih = &((PBITMAPARRAYFILEHEADER) pbmafh2)->bfh.bmp; if (pbmih->cBitCount < 24) { ulColors = 1 << pbmih->cBitCount; if (ulColors > 256) { Runtime_Error(pszSrcFile, __LINE__, "RGB exceeds 256 colors: %lu", ulColors); goto ExitLoadBMP; } // Read in 1.x color table and reformat for 2.x for (ul = 0; ul < ulColors; ul++) { fread(&rgb, 1, sizeof(rgb), pf); pbmi2->argbColor[ul].bRed = rgb.bRed; pbmi2->argbColor[ul].bGreen = rgb.bGreen; pbmi2->argbColor[ul].bBlue = rgb.bBlue; pbmi2->argbColor[ul].fcOptions = 0; // initialize 2.x extra byte to 0 } // for } // Convert the old style to the new header format pbmi2->cbFix = sizeof(BITMAPINFOHEADER2); pbmi2->cBitCount = pbmih->cBitCount; pbmi2->cPlanes = pbmih->cPlanes; pbmi2->cy = pbmih->cy; pbmi2->cx = pbmih->cx; // set rest to zero memset((PCHAR) pbmi2 + 16, 0, sizeof(BITMAPINFOHEADER2) - 16); } // if 1.x /* The 2.0 bitmap info structure set up Position to start of the bitmap data */ rc = fseek(pf, pbmfh2->offBits, SEEK_SET); if (rc) { Runtime_Error(pszSrcFile, __LINE__, "fseek %ld", pbmfh2->offBits); goto ExitLoadBMP; } /* Read the bitmap data The read size is derived using the magic formula Each bitmap scan line is aligned on a doubleword boundary The size of the scan line is the number of pels times the bpp After aligning it, we divide by 4 to get the number of bytes, and multiply by the number of scan lines and the number of pel planes */ if (pbmi2->ulCompression) ulDataSize = pbmi2->cbImage; else ulDataSize = (((pbmi2->cBitCount * pbmi2->cx) + 31) / 32) * 4 * pbmi2->cy * pbmi2->cPlanes; pData = xmalloc(ulDataSize, pszSrcFile, __LINE__); if (!pData) goto ExitLoadBMP; rc = fread(pData, 1, ulDataSize, pf); if (rc != ulDataSize) { Runtime_Error(pszSrcFile, __LINE__, "fread"); goto ExitLoadBMP; } // Create the GPI bitmap image sizel.cx = pbmi2->cx; sizel.cy = pbmi2->cy; hBmp = GpiCreateBitmap(hPS, (PBITMAPINFOHEADER2) pbmi2, CBM_INIT, pData, pbmi2); if (!hBmp) Win_Error(HWND_DESKTOP, HWND_DESKTOP, pszSrcFile, __LINE__, "GpiCreateBitmap"); ExitLoadBMP: xfree(pData, pszSrcFile, __LINE__); xfree(pbmafh2, pszSrcFile, __LINE__); if (pf) fclose(pf); if (hPS) WinReleasePS(hPS); return hBmp; } #pragma alloc_text(LOADBITMAP,LoadBitmapFromFile,LoadBitmapFromFileNum)