852 lines
24 KiB
C++
852 lines
24 KiB
C++
|
|
/*************************************************
|
|
* dib.cpp *
|
|
* *
|
|
* Copyright (C) 1995-1999 Microsoft Inc. *
|
|
* *
|
|
*************************************************/
|
|
|
|
// dib.cpp : implementation file
|
|
//
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "dib.h"
|
|
#include "malloc.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char BASED_CODE THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#define WIDTHBYTES(i) ((i+31)/32*4)
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDIB
|
|
|
|
IMPLEMENT_SERIAL(CDIB, CObject, 0 /* schema number*/ )
|
|
|
|
// Create a small DIB here so m_pBMI and m_pBits are always valid
|
|
CDIB::CDIB()
|
|
{
|
|
m_pBMI = NULL;
|
|
m_pBits = NULL;
|
|
m_bMyBits = TRUE;
|
|
Create(16, 16);
|
|
}
|
|
|
|
CDIB::~CDIB()
|
|
{
|
|
// free the memory
|
|
if (m_pBMI != NULL) free(m_pBMI);
|
|
if (m_bMyBits && (m_pBits != NULL))
|
|
free(m_pBits);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDIB serialization
|
|
|
|
// We dont' support this yet
|
|
void CDIB::Serialize(CArchive& ar)
|
|
{
|
|
ar.Flush();
|
|
CFile *fp = ar.GetFile();
|
|
|
|
if (ar.IsStoring()) {
|
|
Save(fp);
|
|
} else {
|
|
Load(fp);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Private functions
|
|
|
|
static BOOL IsWinDIB(BITMAPINFOHEADER *pBIH)
|
|
{
|
|
ASSERT(pBIH);
|
|
if (((BITMAPCOREHEADER *)pBIH)->bcSize == sizeof(BITMAPCOREHEADER)) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static int NumDIBColorEntries(BITMAPINFO *pBmpInfo)
|
|
{
|
|
BITMAPINFOHEADER *pBIH;
|
|
BITMAPCOREHEADER *pBCH;
|
|
int iColors, iBitCount;
|
|
|
|
ASSERT(pBmpInfo);
|
|
|
|
pBIH = &(pBmpInfo->bmiHeader);
|
|
pBCH = (BITMAPCOREHEADER *) pBIH;
|
|
|
|
// start off by assuming the color table size from
|
|
// the bit per pixel field
|
|
if (IsWinDIB(pBIH)) {
|
|
iBitCount = pBIH->biBitCount;
|
|
} else {
|
|
iBitCount = pBCH->bcBitCount;
|
|
}
|
|
|
|
switch (iBitCount) {
|
|
case 1:
|
|
iColors = 2;
|
|
break;
|
|
case 4:
|
|
iColors = 16;
|
|
break;
|
|
case 8:
|
|
iColors = 256;
|
|
break;
|
|
default:
|
|
iColors = 0;
|
|
break;
|
|
}
|
|
|
|
// If this is a Windows DIB, then the color table length
|
|
// is determined by the biClrUsed field if it is non-zero
|
|
if (IsWinDIB(pBIH) && (pBIH->biClrUsed != 0)) {
|
|
iColors = pBIH->biClrUsed;
|
|
}
|
|
|
|
return iColors;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDIB commands
|
|
|
|
// Create a new empty 8bpp DIB with a 256 entry color table
|
|
BOOL CDIB::Create(int iWidth, int iHeight)
|
|
{
|
|
// delete any existing stuff
|
|
if (m_pBMI != NULL) free(m_pBMI);
|
|
if (m_bMyBits && (m_pBits != NULL)) free(m_pBits);
|
|
|
|
// allocate memory for the header
|
|
m_pBMI = (BITMAPINFO *) malloc(sizeof(BITMAPINFOHEADER)
|
|
+ 256 * sizeof(RGBQUAD));
|
|
if (!m_pBMI) {
|
|
TRACE("Out of memory for DIB header");
|
|
return FALSE;
|
|
}
|
|
|
|
// allocate memory for the bits (DWORD aligned)
|
|
int iBitsSize = ((iWidth + 3) & ~3) * iHeight;
|
|
m_pBits = (BYTE *)malloc(iBitsSize);
|
|
if (!m_pBits) {
|
|
TRACE("Out of memory for DIB bits");
|
|
free(m_pBMI);
|
|
m_pBMI = NULL;
|
|
return FALSE;
|
|
}
|
|
m_bMyBits = TRUE;
|
|
|
|
// fill in the header info
|
|
BITMAPINFOHEADER *pBI = (BITMAPINFOHEADER *) m_pBMI;
|
|
pBI->biSize = sizeof(BITMAPINFOHEADER);
|
|
pBI->biWidth = iWidth;
|
|
pBI->biHeight = iHeight;
|
|
pBI->biPlanes = 1;
|
|
pBI->biBitCount = 8;
|
|
pBI->biCompression = BI_RGB;
|
|
pBI->biSizeImage = 0;
|
|
pBI->biXPelsPerMeter = 0;
|
|
pBI->biYPelsPerMeter = 0;
|
|
pBI->biClrUsed = 0;
|
|
pBI->biClrImportant = 0;
|
|
|
|
// create an arb color table (gray scale)
|
|
RGBQUAD *prgb = GetClrTabAddress();
|
|
for (int i = 0; i < 256; i++) {
|
|
prgb->rgbBlue = prgb->rgbGreen = prgb->rgbRed = (BYTE) i;
|
|
prgb->rgbReserved = 0;
|
|
prgb++;
|
|
}
|
|
|
|
// set all the bits to a known state (black)
|
|
memset(m_pBits, 0, iBitsSize);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Create a CDIB structure from existing header and bits. The DIB
|
|
// won't delete the bits and makes a copy of the header.
|
|
BOOL CDIB::Create(BITMAPINFO *pBMI, BYTE *pBits)
|
|
{
|
|
int iNumColorEntries;
|
|
|
|
ASSERT(pBMI);
|
|
ASSERT(pBits);
|
|
if (m_pBMI != NULL) free(m_pBMI);
|
|
|
|
m_pBMI = (BITMAPINFO *) malloc(sizeof(BITMAPINFOHEADER)
|
|
+ 256 * sizeof(RGBQUAD));
|
|
ASSERT(m_pBMI);
|
|
|
|
iNumColorEntries = NumDIBColorEntries(pBMI);
|
|
if ( iNumColorEntries > 256 )
|
|
iNumColorEntries = 256;
|
|
|
|
memcpy(m_pBMI, pBMI, sizeof(BITMAPINFOHEADER)+
|
|
iNumColorEntries * sizeof(RGBQUAD));
|
|
|
|
if (m_bMyBits && (m_pBits != NULL)) free(m_pBits);
|
|
m_pBits = pBits;
|
|
m_bMyBits = FALSE; // we can't delete the bits
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Load a DIB from an open file.
|
|
BOOL CDIB::Load(CFile *fp)
|
|
{
|
|
BOOL bIsPM = FALSE;
|
|
BITMAPINFO *pBmpInfo = NULL;
|
|
BYTE *pBits = NULL;
|
|
|
|
// get the current file position
|
|
DWORD dwFileStart = fp->GetPosition();
|
|
|
|
// read the file header to get the file size and to
|
|
// find where the bits start in the file
|
|
BITMAPFILEHEADER BmpFileHdr;
|
|
int iBytes;
|
|
iBytes = fp->Read(&BmpFileHdr, sizeof(BmpFileHdr));
|
|
if (iBytes != sizeof(BmpFileHdr)) {
|
|
TRACE("Failed to read file header");
|
|
goto $abort;
|
|
}
|
|
|
|
// check we have the magic 'BM' at the start
|
|
if (BmpFileHdr.bfType != 0x4D42) {
|
|
TRACE("Not a bitmap file");
|
|
goto $abort;
|
|
}
|
|
|
|
// make a wild guess that the file is in Windows DIB
|
|
// format and read the BITMAPINFOHEADER. If it turns
|
|
// out to be a PM DIB file we'll convert it later.
|
|
BITMAPINFOHEADER BmpInfoHdr;
|
|
iBytes = fp->Read(&BmpInfoHdr, sizeof(BmpInfoHdr));
|
|
if (iBytes != sizeof(BmpInfoHdr)) {
|
|
TRACE("Failed to read BITMAPINFOHEADER");
|
|
goto $abort;
|
|
}
|
|
|
|
// check we got a real Windows DIB file
|
|
if (BmpInfoHdr.biSize != sizeof(BITMAPINFOHEADER)) {
|
|
if (BmpInfoHdr.biSize != sizeof(BITMAPCOREHEADER)) {
|
|
TRACE(" File is not Windows or PM DIB format");
|
|
goto $abort;
|
|
}
|
|
|
|
// set a flag to convert PM file to Win format later
|
|
bIsPM = TRUE;
|
|
|
|
// back up the file pointer and read the BITMAPCOREHEADER
|
|
// and create the BITMAPINFOHEADER from it
|
|
fp->Seek(dwFileStart + sizeof(BITMAPFILEHEADER), CFile::begin);
|
|
BITMAPCOREHEADER BmpCoreHdr;
|
|
iBytes = fp->Read(&BmpCoreHdr, sizeof(BmpCoreHdr));
|
|
if (iBytes != sizeof(BmpCoreHdr)) {
|
|
TRACE("Failed to read BITMAPCOREHEADER");
|
|
goto $abort;
|
|
}
|
|
|
|
BmpInfoHdr.biSize = sizeof(BITMAPINFOHEADER);
|
|
BmpInfoHdr.biWidth = (int) BmpCoreHdr.bcWidth;
|
|
BmpInfoHdr.biHeight = (int) BmpCoreHdr.bcHeight;
|
|
BmpInfoHdr.biPlanes = BmpCoreHdr.bcPlanes;
|
|
BmpInfoHdr.biBitCount = BmpCoreHdr.bcBitCount;
|
|
BmpInfoHdr.biCompression = BI_RGB;
|
|
BmpInfoHdr.biSizeImage = 0;
|
|
BmpInfoHdr.biXPelsPerMeter = 0;
|
|
BmpInfoHdr.biYPelsPerMeter = 0;
|
|
BmpInfoHdr.biClrUsed = 0;
|
|
BmpInfoHdr.biClrImportant = 0;
|
|
}
|
|
|
|
// Work out how much memory we need for the BITMAPINFO
|
|
// structure, color table and then for the bits.
|
|
// Allocate the memory blocks, copy the BmpInfoHdr we have so far
|
|
// and then read in the color table from the file.
|
|
int iColors;
|
|
int iColorTableSize;
|
|
iColors = NumDIBColorEntries((LPBITMAPINFO) &BmpInfoHdr);
|
|
iColorTableSize = iColors * sizeof(RGBQUAD);
|
|
int iBitsSize;
|
|
int iBISize;
|
|
// always allocate enough room for 256 entries
|
|
iBISize = sizeof(BITMAPINFOHEADER)
|
|
+ 256 * sizeof(RGBQUAD);
|
|
iBitsSize = BmpFileHdr.bfSize -
|
|
BmpFileHdr.bfOffBits;
|
|
|
|
// allocate the memory for the header
|
|
pBmpInfo = (LPBITMAPINFO) malloc(iBISize);
|
|
if (!pBmpInfo) {
|
|
TRACE("Out of memory for DIB header");
|
|
goto $abort;
|
|
}
|
|
|
|
// copy the header we already have
|
|
memcpy(pBmpInfo, &BmpInfoHdr, sizeof(BITMAPINFOHEADER));
|
|
|
|
// now read the color table in from the file
|
|
if (bIsPM == FALSE) {
|
|
// read the color table from the file
|
|
iBytes = fp->Read(((LPBYTE) pBmpInfo) + sizeof(BITMAPINFOHEADER),
|
|
iColorTableSize);
|
|
if (iBytes != iColorTableSize) {
|
|
TRACE("Failed to read color table");
|
|
goto $abort;
|
|
}
|
|
} else {
|
|
// read each PM color table entry in turn and convert it
|
|
// to Win DIB format as we go
|
|
LPRGBQUAD lpRGB;
|
|
lpRGB = (LPRGBQUAD) ((LPBYTE) pBmpInfo + sizeof(BITMAPINFOHEADER));
|
|
int i;
|
|
RGBTRIPLE rgbt;
|
|
for (i=0; i<iColors; i++) {
|
|
iBytes = fp->Read(&rgbt, sizeof(RGBTRIPLE));
|
|
if (iBytes != sizeof(RGBTRIPLE)) {
|
|
TRACE("Failed to read RGBTRIPLE");
|
|
goto $abort;
|
|
}
|
|
lpRGB->rgbBlue = rgbt.rgbtBlue;
|
|
lpRGB->rgbGreen = rgbt.rgbtGreen;
|
|
lpRGB->rgbRed = rgbt.rgbtRed;
|
|
lpRGB->rgbReserved = 0;
|
|
lpRGB++;
|
|
}
|
|
}
|
|
|
|
// allocate the memory for the bits
|
|
// and read the bits from the file
|
|
pBits = (BYTE *) malloc(iBitsSize);
|
|
if (!pBits) {
|
|
TRACE("Out of memory for DIB bits");
|
|
goto $abort;
|
|
}
|
|
|
|
// seek to the bits in the file
|
|
fp->Seek(dwFileStart + BmpFileHdr.bfOffBits, CFile::begin);
|
|
|
|
// read the bits
|
|
iBytes = fp->Read(pBits, iBitsSize);
|
|
if (iBytes != iBitsSize) {
|
|
TRACE("Failed to read bits");
|
|
goto $abort;
|
|
}
|
|
|
|
// Everything went OK
|
|
if (m_pBMI != NULL) free(m_pBMI);
|
|
m_pBMI = pBmpInfo;
|
|
if (m_bMyBits && (m_pBits != NULL)) free (m_pBits);
|
|
m_pBits = pBits;
|
|
m_bMyBits = TRUE;
|
|
return TRUE;
|
|
|
|
$abort: // something went wrong
|
|
if (pBmpInfo) free(pBmpInfo);
|
|
if (pBits) free (pBits);
|
|
return FALSE;
|
|
}
|
|
|
|
// Load a DIB from a disk file. If no file name is given, show
|
|
// an open file dialog to get one.
|
|
BOOL CDIB::Load(LPSTR pszFileName)
|
|
{
|
|
CString strFile;
|
|
|
|
if ((pszFileName == NULL)
|
|
|| (strlen(pszFileName) == 0)) {
|
|
|
|
// Show an open file dialog to get the name
|
|
CFileDialog dlg (TRUE, // open
|
|
NULL, // no default extension
|
|
NULL, // no initial file name
|
|
OFN_FILEMUSTEXIST
|
|
| OFN_HIDEREADONLY,
|
|
"Image files (*.DIB, *.BMP)|*.DIB;*.BMP|All files (*.*)|*.*||");
|
|
if (dlg.DoModal() == IDOK) {
|
|
strFile = dlg.GetPathName();
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
// copy the supplied file path
|
|
strFile = pszFileName;
|
|
}
|
|
|
|
// Try to open the file for read access
|
|
CFile file;
|
|
if (! file.Open(strFile,
|
|
CFile::modeRead | CFile::shareDenyWrite)) {
|
|
AfxMessageBox("Failed to open file");
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL bResult = Load(&file);
|
|
file.Close();
|
|
if (!bResult) AfxMessageBox("Failed to load file");
|
|
return bResult;
|
|
}
|
|
|
|
// Draw the DIB to a given DC
|
|
void CDIB::Draw(CDC *pDC, int x, int y)
|
|
{
|
|
::StretchDIBits(pDC->GetSafeHdc(),
|
|
x, // dest x
|
|
y, // dest y
|
|
DibWidth(), // dest width
|
|
DibHeight(), // dest height
|
|
0, // src x
|
|
0, // src y
|
|
DibWidth(), // src width
|
|
DibHeight(), // src height
|
|
GetBitsAddress(), // bits
|
|
GetBitmapInfoAddress(), // BITMAPINFO
|
|
DIB_RGB_COLORS, // options
|
|
SRCCOPY); // rop
|
|
}
|
|
|
|
// get the number of color table entries
|
|
int CDIB::GetNumClrEntries()
|
|
{
|
|
return NumDIBColorEntries(m_pBMI);
|
|
}
|
|
int Power(int s)
|
|
{
|
|
return s * s;
|
|
}
|
|
|
|
BYTE GetNearest(PALETTEENTRY* pe,int nSize,int R,int G,int B)
|
|
{
|
|
int nMin = 99999999;
|
|
BYTE nIndex = 0;
|
|
for (int i=0; i<nSize; i++)
|
|
{
|
|
int s = Power(R - pe[i].peRed) +
|
|
Power(G - pe[i].peGreen) +
|
|
Power(B - pe[i].peBlue);
|
|
if (s < nMin)
|
|
{
|
|
nMin = s;
|
|
nIndex = (BYTE)i;
|
|
}
|
|
}
|
|
return nIndex;
|
|
}
|
|
|
|
void CDIB::Inverse()
|
|
{
|
|
HWND hwndActive = ::GetActiveWindow();
|
|
HDC hdcScreen = ::GetDC(hwndActive);
|
|
ASSERT(hdcScreen);
|
|
|
|
if (!(GetDeviceCaps(hdcScreen, RASTERCAPS) & RC_PALETTE))
|
|
return;
|
|
|
|
int iSysColors = GetDeviceCaps(hdcScreen, NUMCOLORS);
|
|
|
|
int nColorMode;
|
|
if (iSysColors == 16)
|
|
nColorMode = 16;
|
|
else
|
|
nColorMode = 256;
|
|
|
|
|
|
|
|
BYTE *pBits = (BYTE *)GetBitsAddress();
|
|
int iSize = StorageWidth() * DibHeight();
|
|
while (iSize--)
|
|
{
|
|
*pBits = (nColorMode - *pBits + 10) % nColorMode;
|
|
pBits++;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// NOTE: This assumes all CDIB objects have 256 color table entries
|
|
BOOL CDIB::MapColorsToPalette(CPalette *pPal)
|
|
{
|
|
if (!pPal) {
|
|
TRACE("No palette to map to");
|
|
return FALSE;
|
|
}
|
|
ASSERT(m_pBMI->bmiHeader.biBitCount == 8);
|
|
ASSERT(m_pBMI);
|
|
ASSERT(m_pBits);
|
|
LPRGBQUAD pctThis = GetClrTabAddress();
|
|
ASSERT(pctThis);
|
|
// build an index translation table to map this DIBs colors
|
|
// to those of the reference DIB
|
|
BYTE imap[256];
|
|
int iChanged = 0; // for debugging only
|
|
int i;
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
|
|
imap[i] = (BYTE) pPal->GetNearestPaletteIndex(
|
|
RGB(pctThis->rgbRed,
|
|
pctThis->rgbGreen,
|
|
pctThis->rgbBlue));
|
|
pctThis++;
|
|
if (imap[i] != i) iChanged++; // for debugging
|
|
}
|
|
// now map the DIB bits
|
|
BYTE *pBits = (BYTE *)GetBitsAddress();
|
|
int iSize = StorageWidth() * DibHeight();
|
|
while (iSize--) {
|
|
*pBits = imap[*pBits];
|
|
pBits++;
|
|
}
|
|
// Now reset the DIB color table so that its RGB values match
|
|
// those in the palette
|
|
PALETTEENTRY pe[256];
|
|
pPal->GetPaletteEntries(0, 256, pe);
|
|
pctThis = GetClrTabAddress();
|
|
for (i = 0; i < 256; i++) {
|
|
pctThis->rgbRed = pe[i].peRed;
|
|
pctThis->rgbGreen = pe[i].peGreen;
|
|
pctThis->rgbBlue = pe[i].peBlue;
|
|
pctThis++;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Get a pointer to a pixel
|
|
// NOTE: DIB scan lines are DWORD aligned. The scan line
|
|
// storage width may be wider than the scan line image width
|
|
// so calc the storage width by rounding the image width
|
|
// to the next highest dword value
|
|
void *CDIB::GetPixelAddress(int x, int y)
|
|
{
|
|
int iWidth;
|
|
// This version only deals with 8 bpp DIBs
|
|
ASSERT(m_pBMI->bmiHeader.biBitCount == 8);
|
|
// make sure it's in range and if not return zero
|
|
if ((x >= DibWidth())
|
|
|| (y >= DibHeight())) {
|
|
TRACE("Attempt to get out of range pixel addr");
|
|
return NULL;
|
|
}
|
|
|
|
// Calculate the scan line storage width
|
|
iWidth = StorageWidth();
|
|
return m_pBits + (DibHeight()-y-1) * iWidth + x;
|
|
}
|
|
|
|
// get the bounding rectangle
|
|
void CDIB::GetRect(CRect* pRect)
|
|
{
|
|
pRect->top = 0;
|
|
pRect->left = 0;
|
|
pRect->bottom = DibHeight();
|
|
pRect->right = DibWidth();
|
|
}
|
|
|
|
// Copy a rectangle of the DIB to another DIB
|
|
// Note we only support 8bpp DIBs here
|
|
void CDIB::CopyBits(CDIB* pdibDest,
|
|
int xd, int yd,
|
|
int w, int h,
|
|
int xs, int ys,
|
|
COLORREF clrTrans)
|
|
{
|
|
ASSERT(m_pBMI->bmiHeader.biBitCount == 8);
|
|
ASSERT(pdibDest);
|
|
// test for strange cases
|
|
if (w == 0 || h == 0) return;
|
|
|
|
// get pointers to the start points in the source
|
|
// and destination DIBs. Note that this will be the bottom left
|
|
// corner of the DIB as the scan lines are reversed in memory
|
|
BYTE* pSrc = (BYTE*)GetPixelAddress(xs, ys + h - 1);
|
|
ASSERT(pSrc);
|
|
if ( pSrc == NULL )
|
|
return;
|
|
|
|
BYTE* pDest = (BYTE*)pdibDest->GetPixelAddress(xd, yd + h - 1);
|
|
ASSERT(pDest);
|
|
if ( pDest == NULL )
|
|
return;
|
|
|
|
// get the scan line widths of each DIB
|
|
int iScanS = StorageWidth();
|
|
int iScanD = pdibDest->StorageWidth();
|
|
|
|
if (clrTrans == 0xFFFFFFFF) {
|
|
// copy the lines
|
|
while (h--) {
|
|
memcpy(pDest, pSrc, w);
|
|
pSrc += iScanS;
|
|
pDest += iScanD;
|
|
}
|
|
} else {
|
|
// copy lines with transparency
|
|
// We only accept a PALETTEINDEX description
|
|
// for the color definition
|
|
ASSERT((clrTrans & 0xFF000000) == 0x01000000);
|
|
BYTE bTransClr = LOBYTE(LOWORD(clrTrans));
|
|
int iSinc = iScanS - w; // source inc value
|
|
int iDinc = iScanD - w; // dest inc value
|
|
int iCount;
|
|
BYTE pixel;
|
|
while (h--) {
|
|
iCount = w; // no of pixels to scan
|
|
while (iCount--) {
|
|
pixel = *pSrc++;
|
|
// only copy pixel if not transparent
|
|
if (pixel != bTransClr) {
|
|
*pDest++ = pixel;
|
|
} else {
|
|
pDest++;
|
|
}
|
|
}
|
|
// move on to the next line
|
|
pSrc += iSinc;
|
|
pDest += iDinc;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save a DIB to a disk file
|
|
// This is somewhat simplistic because we only deal with 256 color DIBs
|
|
// and we always write a 256 color table
|
|
BOOL CDIB::Save(CFile *fp)
|
|
{
|
|
BITMAPFILEHEADER bfh;
|
|
|
|
// construct the file header
|
|
bfh.bfType = 0x4D42; // 'BM'
|
|
bfh.bfSize =
|
|
sizeof(BITMAPFILEHEADER) +
|
|
sizeof(BITMAPINFOHEADER) +
|
|
256 * sizeof(RGBQUAD) +
|
|
StorageWidth() * DibHeight();
|
|
bfh.bfReserved1 = 0;
|
|
bfh.bfReserved2 = 0;
|
|
bfh.bfOffBits =
|
|
sizeof(BITMAPFILEHEADER) +
|
|
sizeof(BITMAPINFOHEADER) +
|
|
256 * sizeof(RGBQUAD);
|
|
|
|
// write the file header
|
|
int iSize = sizeof(bfh);
|
|
TRY {
|
|
fp->Write(&bfh, iSize);
|
|
} CATCH(CFileException, e) {
|
|
TRACE("Failed to write file header");
|
|
return FALSE;
|
|
} END_CATCH
|
|
|
|
// write the BITMAPINFO
|
|
// Note: we assume there are always 256 colors in the
|
|
// color table
|
|
ASSERT(m_pBMI);
|
|
iSize =
|
|
sizeof(BITMAPINFOHEADER) +
|
|
256 * sizeof(RGBQUAD);
|
|
TRY {
|
|
fp->Write(m_pBMI, iSize);
|
|
} CATCH(CFileException, e) {
|
|
TRACE("Failed to write BITMAPINFO");
|
|
return FALSE;
|
|
} END_CATCH
|
|
|
|
// write the bits
|
|
iSize = StorageWidth() * DibHeight();
|
|
TRY {
|
|
fp->Write(m_pBits, iSize);
|
|
} CATCH(CFileException, e) {
|
|
TRACE("Failed to write bits");
|
|
return FALSE;
|
|
} END_CATCH
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Save a DIB to a disk file. If no file name is given, show
|
|
// a save file dialog to get one.
|
|
BOOL CDIB::Save(LPSTR pszFileName)
|
|
{
|
|
CString strFile;
|
|
|
|
if ((pszFileName == NULL)
|
|
|| (strlen(pszFileName) == 0)) {
|
|
|
|
// Show a save file dialog to get the name
|
|
CFileDialog dlg (FALSE, // save
|
|
NULL, // no default extension
|
|
NULL, // no initial file name
|
|
OFN_OVERWRITEPROMPT
|
|
| OFN_HIDEREADONLY,
|
|
"Image files (*.DIB, *.BMP)|*.DIB;*.BMP|All files (*.*)|*.*||");
|
|
if (dlg.DoModal() == IDOK) {
|
|
strFile = dlg.GetPathName();
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
|
|
// copy the supplied file path
|
|
strFile = pszFileName;
|
|
}
|
|
|
|
// Try to open the file for write access
|
|
CFile file;
|
|
if (! file.Open(strFile,
|
|
CFile::modeReadWrite
|
|
| CFile::modeCreate
|
|
| CFile::shareExclusive)) {
|
|
AfxMessageBox("Failed to open file");
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL bResult = Save(&file);
|
|
file.Close();
|
|
if (!bResult) AfxMessageBox("Failed to save file");
|
|
return bResult;
|
|
}
|
|
|
|
WORD DibNumColors (VOID FAR * pv)
|
|
{
|
|
INT bits;
|
|
LPBITMAPINFOHEADER lpbi;
|
|
LPBITMAPCOREHEADER 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).
|
|
*/
|
|
if (lpbi->biSize != sizeof(BITMAPCOREHEADER)){
|
|
if (lpbi->biClrUsed != 0)
|
|
return (WORD)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;
|
|
}
|
|
}
|
|
|
|
WORD PaletteSize (VOID FAR * pv)
|
|
{
|
|
LPBITMAPINFOHEADER lpbi;
|
|
WORD NumColors;
|
|
|
|
lpbi = (LPBITMAPINFOHEADER)pv;
|
|
NumColors = DibNumColors(lpbi);
|
|
|
|
if (lpbi->biSize == sizeof(BITMAPCOREHEADER))
|
|
return (WORD)(NumColors * sizeof(RGBTRIPLE));
|
|
else
|
|
return (WORD)(NumColors * sizeof(RGBQUAD));
|
|
}
|
|
|
|
|
|
BOOL CDIB::Load(CBitmap* pBmp)
|
|
{
|
|
CPalette* pPal;
|
|
BITMAP bm;
|
|
BITMAPINFOHEADER bi;
|
|
WORD biBits;
|
|
DWORD biStyle;
|
|
DWORD dwLen;
|
|
CDC dcMem;
|
|
BYTE *lpbi;
|
|
BYTE *lpBits;
|
|
|
|
if (! pBmp)
|
|
return FALSE;
|
|
|
|
pPal = CPalette::FromHandle((HPALETTE ) GetStockObject(DEFAULT_PALETTE));
|
|
|
|
pBmp->GetObject(sizeof(BITMAP),&bm);
|
|
|
|
biBits = 8;
|
|
biStyle = BI_RGB;
|
|
|
|
bi.biSize = sizeof(BITMAPINFOHEADER);
|
|
bi.biWidth = bm.bmWidth;
|
|
bi.biHeight = bm.bmHeight;
|
|
bi.biPlanes = 1;
|
|
bi.biBitCount = biBits;
|
|
bi.biCompression = biStyle;
|
|
bi.biSizeImage = 0;
|
|
bi.biXPelsPerMeter = 0;
|
|
bi.biYPelsPerMeter = 0;
|
|
bi.biClrUsed = 0;
|
|
bi.biClrImportant = 0;
|
|
|
|
dwLen = bi.biSize + PaletteSize (&bi);
|
|
|
|
dcMem.CreateCompatibleDC(NULL);
|
|
pPal = dcMem.SelectPalette(pPal,FALSE);
|
|
dcMem.RealizePalette();
|
|
|
|
lpbi = (BYTE *) malloc(dwLen);
|
|
|
|
memcpy(lpbi,&bi,sizeof(BITMAPINFOHEADER));
|
|
GetDIBits(dcMem.GetSafeHdc(),
|
|
(HBITMAP) pBmp->GetSafeHandle(),
|
|
0L,
|
|
(DWORD) bi.biHeight,
|
|
(LPBYTE) NULL,
|
|
(LPBITMAPINFO)lpbi,
|
|
(DWORD) DIB_RGB_COLORS);
|
|
|
|
memcpy(&bi,lpbi,sizeof(BITMAPINFOHEADER));
|
|
|
|
if (bi.biSizeImage == 0)
|
|
{
|
|
bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight;
|
|
if (biStyle != BI_RGB)
|
|
bi.biSizeImage = (bi.biSizeImage * 3) / 2;
|
|
}
|
|
|
|
lpBits = (BYTE *) malloc(bi.biSizeImage);
|
|
|
|
GetDIBits(dcMem.GetSafeHdc(),
|
|
(HBITMAP) pBmp->GetSafeHandle(),
|
|
0L,
|
|
(DWORD) bi.biHeight,
|
|
lpBits,
|
|
(LPBITMAPINFO)lpbi,
|
|
(DWORD) DIB_RGB_COLORS);
|
|
dcMem.DeleteDC();
|
|
|
|
// because default constructor has created 16x16 bitmap by default
|
|
// so we need to release first, otherwise it will cause memory leack !
|
|
if (m_pBMI)
|
|
delete m_pBMI;
|
|
if (m_pBits)
|
|
delete m_pBits;
|
|
|
|
m_pBMI = (LPBITMAPINFO) lpbi;
|
|
m_pBits = lpBits;
|
|
m_bMyBits = TRUE;
|
|
|
|
return TRUE;
|
|
}
|