我从网上下载了一个保存位图的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;
}

解决方案 »

  1.   

    //位图操作变量
    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;