windows-nt/Source/XPSP1/NT/multimedia/opengl/toolkits/libmtk/texbmp.cxx
2020-09-26 16:20:57 +08:00

651 lines
21 KiB
C++

/******************************Module*Header*******************************\
* Module Name: ssdib.c
*
* Operations on .bmp files
*
* Copyright (c) 1995 Microsoft Corporation
*
\**************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mtk.h"
#include "texture.hxx"
#define BFT_BITMAP 0x4d42 // 'BM' -- indicates structure is BITMAPFILEHEADER
// struct BITMAPFILEHEADER {
// WORD bfType
// DWORD bfSize
// WORD bfReserved1
// WORD bfReserved2
// DWORD bfOffBits
// }
#define OFFSET_bfType 0
#define OFFSET_bfSize 2
#define OFFSET_bfReserved1 6
#define OFFSET_bfReserved2 8
#define OFFSET_bfOffBits 10
#define SIZEOF_BITMAPFILEHEADER 14
// Read a WORD-aligned DWORD. Needed because BITMAPFILEHEADER has
// WORD-alignment.
#define READDWORD(pv) ( (DWORD)((PWORD)(pv))[0] \
| ((DWORD)((PWORD)(pv))[1] << 16) ) \
// Computes the number of BYTES needed to contain n number of bits.
#define BITS2BYTES(n) ( ((n) + 7) >> 3 )
/****************************************************************************
* *
* FUNCTION : DibNumColors(VOID FAR * pv) *
* *
* PURPOSE : Determines the number of colors in the DIB by looking at *
* the BitCount filed in the info block. *
* *
* RETURNS : The number of colors in the DIB. *
* *
* Stolen from SDK ShowDIB example. *
****************************************************************************/
static WORD DibNumColors(VOID FAR * pv)
{
WORD bits;
BITMAPINFOHEADER UNALIGNED *lpbi;
BITMAPCOREHEADER UNALIGNED *lpbc;
lpbi = ((LPBITMAPINFOHEADER)pv);
lpbc = ((LPBITMAPCOREHEADER)pv);
/* With the BITMAPINFO format headers, the size of the palette
* is in biClrUsed, whereas in the BITMAPCORE - style headers, it
* is dependent on the bits per pixel ( = 2 raised to the power of
* bits/pixel).
*
* Because of the way we use this call, BITMAPINFOHEADER may be out
* of alignment if it follows a BITMAPFILEHEADER. So use the macro
* to safely access DWORD fields.
*/
if (READDWORD(&lpbi->biSize) != sizeof(BITMAPCOREHEADER)){
if (READDWORD(&lpbi->biClrUsed) != 0)
{
return (WORD) READDWORD(&lpbi->biClrUsed);
}
bits = lpbi->biBitCount;
}
else
bits = lpbc->bcBitCount;
switch (bits){
case 1:
return 2;
case 4:
return 16;
case 8:
return 256;
default:
/* A 24 bitcount DIB has no color table */
return 0;
}
}
static PBYTE PackTexImage( BITMAPINFOHEADER *pbmih, PBYTE pjSrc )
{
// Note the malloc used here, for consistency with tk (app may use free() on it)
//mf: can change to overwrite pjSrc, instead of allocating new memomory, once
// no longer using TEX_BMP resource or old way of reading .bmp files
PBYTE pjTKBits = (PBYTE) malloc(pbmih->biSizeImage);
if( !pjTKBits )
{
return NULL;
}
PBYTE pjDst = pjTKBits;
// src lines end on LONG boundary - so need to skip over any padding bytes
int padBytes = pbmih->biWidth % sizeof(LONG);
int rowSize = pbmih->biWidth * sizeof(RGB8);
if( !padBytes ) {
RtlCopyMemory( pjDst, pjSrc, pbmih->biHeight * rowSize );
} else {
for (int i = 0; i < pbmih->biHeight; i++ )
{
RtlCopyMemory( pjDst, pjSrc, rowSize );
pjDst += rowSize;
pjSrc += (rowSize + padBytes );
}
}
return pjTKBits; // to be free'd by the caller
}
/******************************Public*Routine******************************\
* ss_DIBImageLoad
*
* Hacked form of tk_DIBImageLoad(), for reading a .bmp file from a resource
*
* Loads a DIB file (specified as either an ANSI or Unicode filename,
* depending on the bUnicode flag) and converts it into a TK image format.
*
* The technique used is based on CreateDIBSection and SetDIBits.
* CreateDIBSection is used to create a DIB with a format easily converted
* into the TK image format (packed 24BPP RGB). The only conversion
* required is swapping R and B in each RGB triplet (see history below)
* The resulting bitmap is selected into a memory DC.
*
* The DIB file is mapped into memory and SetDIBits called to initialize
* the memory DC bitmap. It is during this step that GDI converts the
* arbitrary DIB file format to RGB format.
*
* Finally, the RGB data in the DIB section is read out and repacked
* as 24BPP 'BGR'.
*
* Returns:
* BOOL.
*
* History:
* - 11/30/95: [marcfo]
* Modified from tkDIBImageLoad, to work on resource bmp file.
* At first I tried accessing the bitmap resource as an RT_BITMAP, where
* the resource compiler strips out file information, and leaves you simple
* bitmap data. But this only worked on the Alpha architecture, x86
* produced a resource with corrupted image data (?palette :)). So I ended
* up just slamming the entire .bmp file in as a resource.
*
\**************************************************************************/
#if 0
//mf: this is no longer needed, since bitmaps are always 'BITMAP' resources now.
BOOL
TEXTURE::DIBImageLoad(PVOID pv )
{
BOOL fSuccess = FALSE;
WORD wNumColors; // Number of colors in color table
BITMAPFILEHEADER *pbmf; // Ptr to file header
BITMAPINFOHEADER UNALIGNED *pbmihFile;
BITMAPCOREHEADER UNALIGNED *pbmchFile; // Ptr to file's core header (if it exists)
PVOID pvBits; // Ptr to bitmap bits in file
PBYTE pjBitsRGB; // Ptr to 24BPP RGB image in DIB section
PBYTE pjTKBits = (PBYTE) NULL; // Ptr to final TK image bits
// These need to be cleaned up when we exit:
HDC hdcMem = (HDC) NULL; // 24BPP mem DC
HBITMAP hbmRGB = (HBITMAP) NULL; // 24BPP RGB bitmap
BITMAPINFO *pbmiSource = (BITMAPINFO *) NULL; // Ptr to source BITMAPINFO
BITMAPINFO *pbmiRGB = (BITMAPINFO *) NULL; // Ptr to file's BITMAPINFO
int i;
// Otherwise, this may be a raw BITMAPINFOHEADER or BITMAPCOREHEADER
// followed immediately with the color table and the bitmap bits.
pbmf = (BITMAPFILEHEADER *) pv;
if ( pbmf->bfType == BFT_BITMAP )
{
pbmihFile = (BITMAPINFOHEADER *) ((PBYTE) pbmf + SIZEOF_BITMAPFILEHEADER);
// BITMAPFILEHEADER is WORD aligned, so use safe macro to read DWORD
// bfOffBits field.
pvBits = (PVOID *) ((PBYTE) pbmf
+ READDWORD((PBYTE) pbmf + OFFSET_bfOffBits));
}
else
{
pbmihFile = (BITMAPINFOHEADER *) pv;
// Determination of where the bitmaps bits are needs to wait until we
// know for sure whether we have a BITMAPINFOHEADER or a BITMAPCOREHEADER.
}
// Determine the number of colors in the DIB palette. This is non-zero
// only for 8BPP or less.
wNumColors = DibNumColors(pbmihFile);
// Create a BITMAPINFO (with color table) for the DIB file. Because the
// file may not have one (BITMAPCORE case) and potential alignment problems,
// we will create a new one in memory we allocate.
//
// We distinguish between BITMAPINFO and BITMAPCORE cases based upon
// BITMAPINFOHEADER.biSize.
pbmiSource = (BITMAPINFO *)
LocalAlloc(LMEM_FIXED, sizeof(BITMAPINFO)
+ wNumColors * sizeof(RGBQUAD));
if (!pbmiSource)
{
MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
goto tkDIBLoadImage_cleanup;
}
// Note: need to use safe READDWORD macro because pbmihFile may
// have only WORD alignment if it follows a BITMAPFILEHEADER.
switch (READDWORD(&pbmihFile->biSize))
{
case sizeof(BITMAPINFOHEADER):
// Convert WORD-aligned BITMAPINFOHEADER to aligned BITMAPINFO.
pbmiSource->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmiSource->bmiHeader.biWidth = READDWORD(&pbmihFile->biWidth);
pbmiSource->bmiHeader.biHeight = READDWORD(&pbmihFile->biHeight);
pbmiSource->bmiHeader.biPlanes = pbmihFile->biPlanes;
pbmiSource->bmiHeader.biBitCount = pbmihFile->biBitCount;
pbmiSource->bmiHeader.biCompression =
READDWORD(&pbmihFile->biCompression);
pbmiSource->bmiHeader.biSizeImage =
READDWORD(&pbmihFile->biSizeImage);
pbmiSource->bmiHeader.biXPelsPerMeter =
READDWORD(&pbmihFile->biXPelsPerMeter);
pbmiSource->bmiHeader.biYPelsPerMeter =
READDWORD(&pbmihFile->biYPelsPerMeter);
pbmiSource->bmiHeader.biClrUsed =
READDWORD(&pbmihFile->biClrUsed);
pbmiSource->bmiHeader.biClrImportant =
READDWORD(&pbmihFile->biClrImportant);
// Copy color table. It immediately follows the BITMAPINFOHEADER.
memcpy((PVOID) &pbmiSource->bmiColors[0], (PVOID) (pbmihFile + 1),
wNumColors * sizeof(RGBQUAD));
// If we haven't already determined the position of the image bits,
// we may now assume that they immediately follow the color table.
if (!pvBits)
pvBits = (PVOID) ((PBYTE) (pbmihFile + 1)
+ wNumColors * sizeof(RGBQUAD));
break;
case sizeof(BITMAPCOREHEADER):
pbmchFile = (BITMAPCOREHEADER *) pbmihFile;
// Convert BITMAPCOREHEADER to BITMAPINFOHEADER.
pbmiSource->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmiSource->bmiHeader.biWidth = (DWORD) pbmchFile->bcWidth;
pbmiSource->bmiHeader.biHeight = (DWORD) pbmchFile->bcHeight;
pbmiSource->bmiHeader.biPlanes = pbmchFile->bcPlanes;
pbmiSource->bmiHeader.biBitCount = pbmchFile->bcBitCount;
pbmiSource->bmiHeader.biCompression = BI_RGB;
pbmiSource->bmiHeader.biSizeImage = 0;
pbmiSource->bmiHeader.biXPelsPerMeter = 0;
pbmiSource->bmiHeader.biYPelsPerMeter = 0;
pbmiSource->bmiHeader.biClrUsed = wNumColors;
pbmiSource->bmiHeader.biClrImportant = wNumColors;
// Convert RGBTRIPLE color table into RGBQUAD color table.
{
RGBQUAD *rgb4 = pbmiSource->bmiColors;
RGBTRIPLE *rgb3 = (RGBTRIPLE *) (pbmchFile + 1);
for (i = 0; i < wNumColors; i++)
{
rgb4->rgbRed = rgb3->rgbtRed ;
rgb4->rgbGreen = rgb3->rgbtGreen;
rgb4->rgbBlue = rgb3->rgbtBlue ;
rgb4->rgbReserved = 0;
rgb4++;
rgb3++;
}
}
// If we haven't already determined the position of the image bits,
// we may now assume that they immediately follow the color table.
if (!pvBits)
pvBits = (PVOID) ((PBYTE) (pbmihFile + 1)
+ wNumColors * sizeof(RGBTRIPLE));
break;
default:
MESSAGEBOX(GetFocus(), "Unknown DIB file format.", "Error", MB_OK);
goto tkDIBLoadImage_cleanup;
}
// Fill in default values (for fields that can have defaults).
if (pbmiSource->bmiHeader.biSizeImage == 0)
pbmiSource->bmiHeader.biSizeImage =
BITS2BYTES( (DWORD) pbmiSource->bmiHeader.biWidth *
pbmiSource->bmiHeader.biBitCount ) *
pbmiSource->bmiHeader.biHeight;
if (pbmiSource->bmiHeader.biClrUsed == 0)
pbmiSource->bmiHeader.biClrUsed = wNumColors;
// Create memory DC.
hdcMem = CreateCompatibleDC(NULL);
if (!hdcMem) {
MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
goto tkDIBLoadImage_cleanup;
}
// Create a 24BPP RGB DIB section and select it into the memory DC.
pbmiRGB = (BITMAPINFO *)
LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(BITMAPINFO) );
if (!pbmiRGB)
{
MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
goto tkDIBLoadImage_cleanup;
}
pbmiRGB->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmiRGB->bmiHeader.biWidth = pbmiSource->bmiHeader.biWidth;
pbmiRGB->bmiHeader.biHeight = pbmiSource->bmiHeader.biHeight;
pbmiRGB->bmiHeader.biPlanes = 1;
pbmiRGB->bmiHeader.biBitCount = 24;
pbmiRGB->bmiHeader.biCompression = BI_RGB;
pbmiRGB->bmiHeader.biSizeImage = pbmiRGB->bmiHeader.biWidth
* abs(pbmiRGB->bmiHeader.biHeight) * 3;
hbmRGB = CreateDIBSection(hdcMem, pbmiRGB, DIB_RGB_COLORS,
(PVOID *) &pjBitsRGB, NULL, 0);
if (!hbmRGB)
{
MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
goto tkDIBLoadImage_cleanup;
}
if (!SelectObject(hdcMem, hbmRGB))
{
MESSAGEBOX(GetFocus(), "Out of memory.", "Error", MB_OK);
goto tkDIBLoadImage_cleanup;
}
// Slam the DIB file image into the memory DC. GDI will do the work of
// translating whatever format the DIB file has into RGB format.
if (!SetDIBits(hdcMem, hbmRGB, 0, pbmiSource->bmiHeader.biHeight,
pvBits, pbmiSource, DIB_RGB_COLORS))
{
MESSAGEBOX(GetFocus(), "Image file conversion error.", "Error", MB_OK);
goto tkDIBLoadImage_cleanup;
}
GdiFlush(); // make sure that SetDIBits executes
/// *******************************************************************
// Convert to TK image format (packed RGB format).
// Allocate with malloc to be consistent with tkRGBImageLoad (i.e., app
// can deallocate with free()).
pjTKBits = PackTexImage( &pbmiRGB->bmiHeader, pjBitsRGB );
if( !pjTKBits ) {
SS_ERROR( "ConvertToOpenGLTexFormat : memory failure\n" );
goto tkDIBLoadImage_cleanup;
}
// Initialize the texture structure
// If we get to here, we have suceeded!
width = pbmiSource->bmiHeader.biWidth;
height = pbmiSource->bmiHeader.biHeight;
format = GL_RGB;
components = 3;
// do we keep the data around , since it's not needed after the texture is
// downloaded into GL and have texObj's ?
data = pjTKBits;
pal_size = 0;
pal = NULL;
fSuccess = TRUE;
// Cleanup objects.
tkDIBLoadImage_cleanup:
{
if (hdcMem)
DeleteDC(hdcMem);
if (hbmRGB)
DeleteObject(hbmRGB);
if (pbmiRGB)
LocalFree(pbmiRGB);
if (pbmiSource)
LocalFree(pbmiSource);
}
// Check for error.
if (!fSuccess)
{
if (pjTKBits)
free(pjTKBits);
}
return fSuccess;
}
#endif
BOOL
TEXTURE::DIBImageLoad( HBITMAP hbmRGB )
{
BOOL fSuccess = FALSE;
PBYTE pjTKBits = (PBYTE) NULL; // Ptr to final TK image bits
BITMAPINFOHEADER *pbmih;
// These need to be cleaned up when we exit:
PVOID pvBits = NULL; // Ptr to bitmap bits in file
HDC hdcMem = (HDC) NULL; // 24BPP mem DC
BITMAPINFO *pbmiRGB = (BITMAPINFO *) NULL; // Ptr to file's BITMAPINFO
// Create memory DC.
hdcMem = CreateCompatibleDC(NULL);
if (!hdcMem) {
SS_ERROR( "TEXTURE::DIBImageLoad : CreateCompatibleDC failure\n" );
goto tkDIBLoadImage2_cleanup;
}
// Allocate BITMAPINFO structure
pbmiRGB = (BITMAPINFO *)
LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(BITMAPINFO) );
if (!pbmiRGB)
{
SS_ERROR( "TEXTURE::DIBImageLoad : memory failure\n" );
goto tkDIBLoadImage2_cleanup;
}
pbmih = (BITMAPINFOHEADER *) &pbmiRGB->bmiHeader;
pbmih->biSize = sizeof( BITMAPINFOHEADER );
// Query bitmap values into pbmih
if( ! GetDIBits( hdcMem, hbmRGB, 0, 0, NULL, pbmiRGB,
DIB_RGB_COLORS ) )
{
SS_ERROR( "TEXTURE::DIBImageLoad : GetDIBits failure\n" );
goto tkDIBLoadImage2_cleanup;
}
// Now pbmiRGB->bmiHeader should've been filled in with stuff
pbmih->biPlanes = 1;
pbmih->biBitCount = 24;
pbmih->biCompression = BI_RGB;
//mf:suppsedly this can be set to 0 for BI_RGB
pbmih->biSizeImage = pbmih->biWidth
* abs(pbmih->biHeight) * 3;
pvBits = (PVOID) LocalAlloc(LMEM_FIXED, pbmih->biSizeImage);
if (!pvBits)
{
SS_ERROR( "TEXTURE::DIBImageLoad : memory failure\n" );
goto tkDIBLoadImage2_cleanup;
}
// Get the image bits
if( ! GetDIBits( hdcMem, hbmRGB, 0, pbmih->biHeight, pvBits,
pbmiRGB, DIB_RGB_COLORS ) )
{
SS_ERROR( "TEXTURE::DIBImageLoad : GetDIBits failure\n" );
goto tkDIBLoadImage2_cleanup;
}
// Convert to TK image format (packed RGB format).
// Allocate with malloc to be consistent with tkRGBImageLoad (i.e., app
// can deallocate with free()).
pjTKBits = PackTexImage( pbmih, (PBYTE) pvBits );
if( !pjTKBits ) {
SS_ERROR( "ConvertToOpenGLTexFormat : memory failure\n" );
goto tkDIBLoadImage2_cleanup;
}
// Initialize the texture structure
// If we get to here, we have suceeded!
width = pbmih->biWidth;
height = pbmih->biHeight;
format = GL_BGR_EXT;
components = 3;
data = pjTKBits;
pal_size = 0;
pal = NULL;
fSuccess = TRUE;
// Cleanup objects.
tkDIBLoadImage2_cleanup:
{
if( pvBits )
LocalFree(pvBits);
if (hdcMem)
DeleteDC(hdcMem);
if (pbmiRGB)
LocalFree(pbmiRGB);
}
// Check for error.
if (!fSuccess)
{
if (pjTKBits)
free(pjTKBits);
}
return fSuccess;
}
/******************************Public*Routine******************************\
*
* bVerifyDIB
*
* Stripped down version of tkDIBImageLoadAW that verifies that a bitmap
* file is valid and, if so, returns the bitmap dimensions.
*
* Returns:
* TRUE if valid bitmap file; otherwise, FALSE.
*
\**************************************************************************/
BOOL
bVerifyDIB(LPTSTR pszFileName, ISIZE *pSize )
{
BOOL bRet = FALSE;
BITMAPFILEHEADER *pbmf; // Ptr to file header
BITMAPINFOHEADER *pbmihFile; // Ptr to file's info header (if it exists)
BITMAPCOREHEADER *pbmchFile; // Ptr to file's core header (if it exists)
// These need to be cleaned up when we exit:
HANDLE hFile = INVALID_HANDLE_VALUE; // File handle
HANDLE hMap = (HANDLE) NULL; // Mapping object handle
PVOID pvFile = (PVOID) NULL; // Ptr to mapped file
// Map the DIB file into memory.
hFile = CreateFile((LPTSTR) pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
goto bVerifyDIB_cleanup;
hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (!hMap)
goto bVerifyDIB_cleanup;
pvFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
if (!pvFile)
goto bVerifyDIB_cleanup;
// Check the file header. If the BFT_BITMAP magic number is there,
// then the file format is a BITMAPFILEHEADER followed immediately
// by either a BITMAPINFOHEADER or a BITMAPCOREHEADER. The bitmap
// bits, in this case, are located at the offset bfOffBits from the
// BITMAPFILEHEADER.
//
// Otherwise, this may be a raw BITMAPINFOHEADER or BITMAPCOREHEADER
// followed immediately with the color table and the bitmap bits.
pbmf = (BITMAPFILEHEADER *) pvFile;
if ( pbmf->bfType == BFT_BITMAP )
pbmihFile = (BITMAPINFOHEADER *) ((PBYTE) pbmf + SIZEOF_BITMAPFILEHEADER);
else
pbmihFile = (BITMAPINFOHEADER *) pvFile;
// Get the width and height from whatever header we have.
//
// We distinguish between BITMAPINFO and BITMAPCORE cases based upon
// BITMAPINFOHEADER.biSize.
// Note: need to use safe READDWORD macro because pbmihFile may
// have only WORD alignment if it follows a BITMAPFILEHEADER.
switch (READDWORD(&pbmihFile->biSize))
{
case sizeof(BITMAPINFOHEADER):
if( pSize != NULL ) {
pSize->width = READDWORD(&pbmihFile->biWidth);
pSize->height = READDWORD(&pbmihFile->biHeight);
}
bRet = TRUE;
break;
case sizeof(BITMAPCOREHEADER):
pbmchFile = (BITMAPCOREHEADER *) pbmihFile;
// Convert BITMAPCOREHEADER to BITMAPINFOHEADER.
if( pSize != NULL ) {
pSize->width = (DWORD) pbmchFile->bcWidth;
pSize->height = (DWORD) pbmchFile->bcHeight;
}
bRet = TRUE;
break;
default:
break;
}
bVerifyDIB_cleanup:
if (pvFile)
UnmapViewOfFile(pvFile);
if (hMap)
CloseHandle(hMap);
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
return bRet;
}