我从网上下载了一个保存位图的VC++类CDib,其可以将内存位图保存为2、16、256或24位真彩色,但我在使用时发现保存为真彩色时颜色是正确的,但保存为2、16、256色的图像时颜色不对,不知该如何解决?相关部分代码如下。
// dib.h
class CDib : public CObject
{
DECLARE_SERIAL(CDib)private:
char /*huge*/* m_lpBuf; // DIB数据缓存,包括文件头(DIB data buffer)
DWORD m_dwLength; // 总缓存大小,包括文件头(total buffer length, including file header)
int m_nBits; // 每个象素的颜色数(number of color bits per pixel) //pointers for internal use
LPBITMAPFILEHEADER m_lpBMFH; // BITMAP文件头
LPBITMAPINFOHEADER m_lpBMIH; // BITMAP信息头
LPBITMAPINFO m_lpBMI; // BITMAP信息
LPSTR m_lpData; // 指向图像数据的“指针”public:
CDib();
CDib(CDC* pDC, int nBt = 0, BOOL bCompr = TRUE);// nBt = 0 means use default bits/pixel
~CDib(); BOOL Write(CFile* pFile);private:
BOOL AllocateMemory(BOOL bRealloc = FALSE);
};// dib.cpp#include "stdafx.h"
#include "resource.h"
#include "cdib.h"
#include <windowsx.h> // for GlobalAllocPtrIMPLEMENT_SERIAL(CDib, CObject, 0)///////////////////////////////////////////////////////////////////
CDib::CDib()
{
m_dwLength = 0L;
m_nBits = 0;
m_lpBuf = NULL;
}
///////////////////////////////////////////////////////////////////
// pDC is memory DC ptr
// nBt is color bits per pixel (default = 0)
// bCompr is compression (default = TRUE)
CDib::CDib(CDC* pDC, int nBt, BOOL bCompr)
{
BITMAP bm;
int nPaletteSize; CBitmap* pEmptyBitmap = new CBitmap;
pEmptyBitmap->CreateCompatibleBitmap(pDC, 0, 0);
CBitmap* pBitmap = (CBitmap*) (pDC->SelectObject(pEmptyBitmap));
pBitmap->GetObject(sizeof(bm), &bm); if ((nBt == 1) || (nBt == 4) || (nBt == 8) || (nBt == 24))
{
m_nBits = nBt;
}
else
{ // nBt = 0
m_nBits = bm.bmPlanes * bm.bmBitsPixel; // color bits per pixel
} if (m_nBits == 1)
{
nPaletteSize = 2;
}
else
{
if (m_nBits == 4)
{
nPaletteSize = 16;
}
else
{
if (m_nBits == 8)
{
nPaletteSize = 256;
}
else
{
nPaletteSize = 0; // no palette for 24-bit display
}
}
} DWORD dwBytes = ((DWORD) bm.bmWidth * nPaletteSize) / 32;
if (((DWORD) bm.bmWidth * m_nBits) % 32)
{
dwBytes ++;
}
dwBytes *= 4;
m_dwLength = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * nPaletteSize;
if (!AllocateMemory())
return;
m_lpBMIH->biSize = sizeof(BITMAPINFOHEADER);
m_lpBMIH->biWidth = bm.bmWidth;
m_lpBMIH->biHeight = bm.bmHeight;
m_lpBMIH->biPlanes = 1;
m_lpBMIH->biBitCount = m_nBits; // 1, 4, 8, or 24
if (bCompr && (m_nBits == 4))
{
m_lpBMIH->biCompression = BI_RLE4;
}
else
{
if (bCompr && (m_nBits == 8))
{
m_lpBMIH->biCompression = BI_RLE8;
}
else
{
m_lpBMIH->biCompression = BI_RGB;
}
} m_lpBMIH->biSizeImage = 0;
m_lpBMIH->biXPelsPerMeter = 0;
m_lpBMIH->biYPelsPerMeter = 0;
m_lpBMIH->biClrUsed = 0;
m_lpBMIH->biClrImportant = 0;
// calls GetDIBits with null data pointer to get size of DIB
::GetDIBits(pDC->GetSafeHdc(), (HBITMAP) pBitmap->GetSafeHandle(),
0, (WORD) bm.bmHeight, NULL, m_lpBMI, DIB_RGB_COLORS);
if (m_lpBMIH->biSizeImage == 0)
{
m_dwLength += dwBytes * bm.bmHeight;
m_lpBMIH->biCompression = BI_RGB;
// escape route for device drivers that don't do compression
TRACE("Can't do compression\n");
}
else
{
m_dwLength += m_lpBMIH->biSizeImage;
}
if (!AllocateMemory(TRUE))
{
return;
}
m_lpData = (LPSTR) m_lpBMIH + sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * nPaletteSize;
m_lpBMFH->bfType = 0x4d42; // 'BM'
m_lpBMFH->bfSize = m_dwLength;
m_lpBMFH->bfReserved1 = 0;
m_lpBMFH->bfReserved2 = 0;
m_lpBMFH->bfOffBits = (char *) m_lpData - m_lpBuf;
// second GetDIBits call to make DIB
if (!::GetDIBits(pDC->GetSafeHdc(), (HBITMAP)pBitmap->GetSafeHandle(),
0, (WORD) bm.bmHeight, m_lpData, m_lpBMI, DIB_RGB_COLORS))
{
m_dwLength = 0L;
}
delete pDC->SelectObject(pBitmap); // delete pEmptyBitmap
}///////////////////////////////////////////////////////////////////
CDib::~CDib()
{
if (m_lpBuf)
{
GlobalFreePtr(m_lpBuf); // free the DIB memory
}
}///////////////////////////////////////////////////////////////////
BOOL CDib::Write(CFile* pFile)
{
TRY
{
pFile->WriteHuge(m_lpBuf, m_dwLength);
}
CATCH (CException, e)
{
AfxMessageBox("Write error--possible disk full condition");
return FALSE;
}
END_CATCH
return TRUE;
}///////////////////////////////////////////////////////////////////
BOOL CDib::AllocateMemory(BOOL bRealloc) // bRealloc default = FALSE
{
if (bRealloc)
{
m_lpBuf = (char /*huge*/*) GlobalReAllocPtr(m_lpBuf,m_dwLength, GHND);
}
else
{
m_lpBuf = (char /*huge*/*) GlobalAllocPtr(GHND, m_dwLength);
} if (!m_lpBuf)
{
AfxMessageBox("Unable to allocate DIB memory");
m_dwLength = 0L;
m_nBits = 0;
return FALSE;
} m_lpBMFH = (LPBITMAPFILEHEADER) m_lpBuf;
m_lpBMIH = (LPBITMAPINFOHEADER) (m_lpBuf + sizeof(BITMAPFILEHEADER));
m_lpBMI = (LPBITMAPINFO) m_lpBMIH;
return TRUE;
}
// dib.h
class CDib : public CObject
{
DECLARE_SERIAL(CDib)private:
char /*huge*/* m_lpBuf; // DIB数据缓存,包括文件头(DIB data buffer)
DWORD m_dwLength; // 总缓存大小,包括文件头(total buffer length, including file header)
int m_nBits; // 每个象素的颜色数(number of color bits per pixel) //pointers for internal use
LPBITMAPFILEHEADER m_lpBMFH; // BITMAP文件头
LPBITMAPINFOHEADER m_lpBMIH; // BITMAP信息头
LPBITMAPINFO m_lpBMI; // BITMAP信息
LPSTR m_lpData; // 指向图像数据的“指针”public:
CDib();
CDib(CDC* pDC, int nBt = 0, BOOL bCompr = TRUE);// nBt = 0 means use default bits/pixel
~CDib(); BOOL Write(CFile* pFile);private:
BOOL AllocateMemory(BOOL bRealloc = FALSE);
};// dib.cpp#include "stdafx.h"
#include "resource.h"
#include "cdib.h"
#include <windowsx.h> // for GlobalAllocPtrIMPLEMENT_SERIAL(CDib, CObject, 0)///////////////////////////////////////////////////////////////////
CDib::CDib()
{
m_dwLength = 0L;
m_nBits = 0;
m_lpBuf = NULL;
}
///////////////////////////////////////////////////////////////////
// pDC is memory DC ptr
// nBt is color bits per pixel (default = 0)
// bCompr is compression (default = TRUE)
CDib::CDib(CDC* pDC, int nBt, BOOL bCompr)
{
BITMAP bm;
int nPaletteSize; CBitmap* pEmptyBitmap = new CBitmap;
pEmptyBitmap->CreateCompatibleBitmap(pDC, 0, 0);
CBitmap* pBitmap = (CBitmap*) (pDC->SelectObject(pEmptyBitmap));
pBitmap->GetObject(sizeof(bm), &bm); if ((nBt == 1) || (nBt == 4) || (nBt == 8) || (nBt == 24))
{
m_nBits = nBt;
}
else
{ // nBt = 0
m_nBits = bm.bmPlanes * bm.bmBitsPixel; // color bits per pixel
} if (m_nBits == 1)
{
nPaletteSize = 2;
}
else
{
if (m_nBits == 4)
{
nPaletteSize = 16;
}
else
{
if (m_nBits == 8)
{
nPaletteSize = 256;
}
else
{
nPaletteSize = 0; // no palette for 24-bit display
}
}
} DWORD dwBytes = ((DWORD) bm.bmWidth * nPaletteSize) / 32;
if (((DWORD) bm.bmWidth * m_nBits) % 32)
{
dwBytes ++;
}
dwBytes *= 4;
m_dwLength = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * nPaletteSize;
if (!AllocateMemory())
return;
m_lpBMIH->biSize = sizeof(BITMAPINFOHEADER);
m_lpBMIH->biWidth = bm.bmWidth;
m_lpBMIH->biHeight = bm.bmHeight;
m_lpBMIH->biPlanes = 1;
m_lpBMIH->biBitCount = m_nBits; // 1, 4, 8, or 24
if (bCompr && (m_nBits == 4))
{
m_lpBMIH->biCompression = BI_RLE4;
}
else
{
if (bCompr && (m_nBits == 8))
{
m_lpBMIH->biCompression = BI_RLE8;
}
else
{
m_lpBMIH->biCompression = BI_RGB;
}
} m_lpBMIH->biSizeImage = 0;
m_lpBMIH->biXPelsPerMeter = 0;
m_lpBMIH->biYPelsPerMeter = 0;
m_lpBMIH->biClrUsed = 0;
m_lpBMIH->biClrImportant = 0;
// calls GetDIBits with null data pointer to get size of DIB
::GetDIBits(pDC->GetSafeHdc(), (HBITMAP) pBitmap->GetSafeHandle(),
0, (WORD) bm.bmHeight, NULL, m_lpBMI, DIB_RGB_COLORS);
if (m_lpBMIH->biSizeImage == 0)
{
m_dwLength += dwBytes * bm.bmHeight;
m_lpBMIH->biCompression = BI_RGB;
// escape route for device drivers that don't do compression
TRACE("Can't do compression\n");
}
else
{
m_dwLength += m_lpBMIH->biSizeImage;
}
if (!AllocateMemory(TRUE))
{
return;
}
m_lpData = (LPSTR) m_lpBMIH + sizeof(BITMAPINFOHEADER) +
sizeof(RGBQUAD) * nPaletteSize;
m_lpBMFH->bfType = 0x4d42; // 'BM'
m_lpBMFH->bfSize = m_dwLength;
m_lpBMFH->bfReserved1 = 0;
m_lpBMFH->bfReserved2 = 0;
m_lpBMFH->bfOffBits = (char *) m_lpData - m_lpBuf;
// second GetDIBits call to make DIB
if (!::GetDIBits(pDC->GetSafeHdc(), (HBITMAP)pBitmap->GetSafeHandle(),
0, (WORD) bm.bmHeight, m_lpData, m_lpBMI, DIB_RGB_COLORS))
{
m_dwLength = 0L;
}
delete pDC->SelectObject(pBitmap); // delete pEmptyBitmap
}///////////////////////////////////////////////////////////////////
CDib::~CDib()
{
if (m_lpBuf)
{
GlobalFreePtr(m_lpBuf); // free the DIB memory
}
}///////////////////////////////////////////////////////////////////
BOOL CDib::Write(CFile* pFile)
{
TRY
{
pFile->WriteHuge(m_lpBuf, m_dwLength);
}
CATCH (CException, e)
{
AfxMessageBox("Write error--possible disk full condition");
return FALSE;
}
END_CATCH
return TRUE;
}///////////////////////////////////////////////////////////////////
BOOL CDib::AllocateMemory(BOOL bRealloc) // bRealloc default = FALSE
{
if (bRealloc)
{
m_lpBuf = (char /*huge*/*) GlobalReAllocPtr(m_lpBuf,m_dwLength, GHND);
}
else
{
m_lpBuf = (char /*huge*/*) GlobalAllocPtr(GHND, m_dwLength);
} if (!m_lpBuf)
{
AfxMessageBox("Unable to allocate DIB memory");
m_dwLength = 0L;
m_nBits = 0;
return FALSE;
} m_lpBMFH = (LPBITMAPFILEHEADER) m_lpBuf;
m_lpBMIH = (LPBITMAPINFOHEADER) (m_lpBuf + sizeof(BITMAPFILEHEADER));
m_lpBMI = (LPBITMAPINFO) m_lpBMIH;
return TRUE;
}
LPBYTE m_pBits;
LPBITMAPINFO m_pBMI;
//位图操作变量
//位图信息结构
BITMAPINFOHEADER bmpH;
bmpH.biSize = sizeof(BITMAPINFOHEADER);
bmpH.biWidth = Image->iWidth;
bmpH.biHeight = Image->iHeight;
bmpH.biPlanes= 1; ////每个象素的位平面数,只能是1
if(Image->MediaSubType==RGB24 || Image->MediaSubType==RGB8)
{
bmpH.biBitCount = 24;
}
else if(Image->MediaSubType== RGB32)
{
bmpH.biBitCount = 32;
}
bmpH.biCompression = BI_RGB; //普通格式无压缩
bmpH.biSizeImage = 0; //图像数据大小
bmpH.biClrUsed = 0; //调色板中实际使用的颜色数,通常为0
bmpH.biClrImportant = 0;//显示位图时必需的颜色数,通常为0
//位图信息结构
m_pBMI=(BITMAPINFO*)&bmpH;
//图像信息
m_pBits=Image->LP;
CFile file;
if(file.Open("c:\\20040318.bmp",CFile::modeWrite |CFile::modeCreate,NULL))
{
BITMAPFILEHEADER bmfHdr; // bmp文件头结构
DWORD dwDIBSize;
if (m_pBMI == NULL)
return 0;
// Fill in the fields of the file header
// Fill in file type (first 2 bytes must be "BM" for a bitmap)//前两个字节必须是BM
bmfHdr.bfType = DIB_HEADER_MARKER; // "BM"
dwDIBSize = *(LPDWORD)&m_pBMI->bmiHeader + PaletteSize(Image->MediaSubType,m_pBMI); // Partial Calculation
// Now calculate the size of the image
if ((m_pBMI->bmiHeader.biCompression == BI_RLE8) || (m_pBMI->bmiHeader.biCompression == BI_RLE4))
{
dwDIBSize += m_pBMI->bmiHeader.biSizeImage;
}
else
{
DWORD dwBmBitsSize; // Size of Bitmap Bits only
// It's not RLE, so size is Width (DWORD aligned) * Height
dwBmBitsSize = WIDTHBYTES((m_pBMI->bmiHeader.biWidth)*((DWORD)m_pBMI->bmiHeader.biBitCount)) * m_pBMI->bmiHeader.biHeight;
dwDIBSize += dwBmBitsSize; // Now, since we have calculated the correct size, why don't we
// fill in the biSizeImage field (this will fix any .BMP files which
// have this field incorrect).
m_pBMI->bmiHeader.biSizeImage = dwBmBitsSize;
} // Calculate the file size by adding the DIB size to sizeof(BITMAPFILEHEADER)
bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER);
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0; /*
* Now, calculate the offset the actual bitmap bits will be in
* the file -- It's the Bitmap file header plus the DIB header,
* plus the size of the color table.
*/
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + m_pBMI->bmiHeader.biSize + PaletteSize(Image->MediaSubType,m_pBMI); // Write the file header
file.Write((LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER));
DWORD dwBytesSaved = sizeof(BITMAPFILEHEADER); // Write the DIB header
UINT nCount = sizeof(BITMAPINFO) + (NumColors(m_pBMI)-1)*sizeof(RGBQUAD);
dwBytesSaved += nCount;
file.Write(m_pBMI, nCount);
// Write the DIB bits
DWORD dwBytes = m_pBMI->bmiHeader.biBitCount * Width(m_pBMI);
// Calculate the number of bytes per line
if (dwBytes%32 == 0)
dwBytes /= 8;
else
dwBytes = dwBytes/8 + (32-dwBytes%32)/8 + (((32-dwBytes%32)%8 > 0) ? 1 : 0);
nCount = dwBytes * Height(m_pBMI);
dwBytesSaved += nCount;
file.WriteHuge(m_pBits, Image->iByteCount);
}
return TRUE;