“不占内存”的超大Bitmap读取和显示

来源:互联网 发布:黑马网络qq8134341 编辑:程序博客网 时间:2024/04/23 17:54

在VC中读取和显示Bitmap并不难,也有很多网友提供了扩展的CDib类以提供不同方式的优化和更新。我遇到的一个需求就是要求读取超大的CBitmap类,要求以最低的内存占有量和最快的显示速度来读取并显示bitmap。

 

常用的海量数据读取优化方式有二种,一种就是不直接读入数据,自己建索引表,将数据映射到索引表中,即在索引表中为每个数据元素建立地址起始和偏移对应关系。因为索引表通常只是元素的描述头和地址起始及偏移信息,占有内存不大,所以可以驻留进程内存;对数据文件的读写是只append到最后,只是在索引表中维护元素的增长和删除及修改信息。二是用Mapping File方式,将数据序列化为一个字节流,然后同样也需要建立一个索引表来对应。

 

其实两种方式比较类似,用哪种方式都可以。这里介绍的是我用的Mapping File方式。

 

以下是CDib类的定义部分。

class BASE_API CDib : public CObject
{
    enum Alloc {noAlloc, crtAlloc, heapAlloc};
    DECLARE_SERIAL(CDib)
public:
    LPVOID m_lpvColorTable;
    HBITMAP m_hBitmap;
    LPBYTE m_lpImage;  // starting address of DIB bits
    LPBITMAPINFOHEADER m_lpBMIH; //  buffer containing the BITMAPINFOHEADER
private:
    HGLOBAL m_hGlobal; // For external windows we need to free;
                       //  could be allocated by this class or allocated externally
    Alloc m_nBmihAlloc;
    Alloc m_nImageAlloc;
    DWORD m_dwSizeImage; // of bits -- not BITMAPINFOHEADER or BITMAPFILEHEADER
    int m_nColorTableEntries;
   
    HANDLE m_hFile;
    HANDLE m_hMap;
    LPVOID m_lpvFile;
    HPALETTE m_hPalette;
public:
    CDib();
    CDib(CSize size, int nBitCount);    // builds BITMAPINFOHEADER
    ~CDib();
    int GetSizeImage() {return m_dwSizeImage;}
    int GetSizeHeader()
        {return sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;}
    CSize GetDimensions();
    BOOL AttachMapFile(CString strPathname, BOOL bShare = FALSE);
    BOOL CopyToMapFile(CString strPathname);
    BOOL AttachMemory(LPVOID lpvMem, BOOL bMustDelete = FALSE, HGLOBAL hGlobal = NULL);
    BOOL Draw(CDC* pDC, CPoint origin, CSize size);  // until we implemnt CreateDibSection
    BOOL DrawEx(CDC* pDC, CPoint originSrc, CSize sizeSrc, CPoint originDes, CSize sizeDes);  // until we implemnt CreateDibSection
 //半透明地画
 BOOL DrawSemiTransparent(CDC* pDC, CPoint originSrc, CSize sizeSrc, CPoint originDes, CSize sizeDes,CSize sizeClient,BYTE nAlpha);
 
 BOOL DrawTransparent(CDC* pDC, CPoint origin, CSize size,UINT transColor); 
 BOOL DrawTransparentEx(CDC* pDC, CPoint originSrc, CSize sizeSrc, CPoint originDes, CSize sizeDes, CSize sizeClient,UINT transColor);  // until we implemnt CreateDibSection
    HBITMAP CreateSection(CDC* pDC = NULL);
    UINT UsePalette(CDC* pDC, BOOL bBackground = FALSE);
    BOOL MakePalette();
    BOOL SetSystemPalette(CDC* pDC);
    BOOL Compress(CDC* pDC, BOOL bCompress = TRUE); // FALSE means decompress
    HBITMAP CreateBitmap(CDC* pDC);
    BOOL Read(CFile* pFile);
    BOOL ReadSection(CFile* pFile, CDC* pDC = NULL);
    BOOL Write(CFile* pFile);
    void Serialize(CArchive& ar);
    void Empty();
private:
    void DetachMapFile();
    void ComputePaletteSize(int nBitCount);
    void ComputeMetrics();
};

 

以下是CDib类的实现部分

 

CDib::CDib()
{
    m_hFile = NULL;
    m_hBitmap = NULL;
    m_hPalette = NULL;
    m_nBmihAlloc = m_nImageAlloc = noAlloc;
    Empty();
}

CDib::CDib(CSize size, int nBitCount)
{
    m_hFile = NULL;
    m_hBitmap = NULL;
    m_hPalette = NULL;
    m_nBmihAlloc = m_nImageAlloc = noAlloc;
    Empty();
    ComputePaletteSize(nBitCount);
    m_lpBMIH = (LPBITMAPINFOHEADER) new
        char[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries];
    m_nBmihAlloc = crtAlloc;
    m_lpBMIH->biSize = sizeof(BITMAPINFOHEADER);
    m_lpBMIH->biWidth = size.cx;
    m_lpBMIH->biHeight = size.cy;
    m_lpBMIH->biPlanes = 1;
    m_lpBMIH->biBitCount = nBitCount;
    m_lpBMIH->biCompression = BI_RGB;
    m_lpBMIH->biSizeImage = 0;
    m_lpBMIH->biXPelsPerMeter = 0;
    m_lpBMIH->biYPelsPerMeter = 0;
    m_lpBMIH->biClrUsed = m_nColorTableEntries;
    m_lpBMIH->biClrImportant = m_nColorTableEntries;
    ComputeMetrics();
    memset(m_lpvColorTable, 0, sizeof(RGBQUAD) * m_nColorTableEntries);
    m_lpImage = NULL;  // no data yet
}

CDib::~CDib()
{
    Empty();
}

CSize CDib::GetDimensions()
{   
    if(m_lpBMIH == NULL) return CSize(0, 0);
    return CSize((int) m_lpBMIH->biWidth, (int) m_lpBMIH->biHeight);
}

BOOL CDib::AttachMapFile(CString strPathname, BOOL bShare) // for reading
{
    // if we open the same file twice, Windows treats it as 2 separate files
    // doesn't work with rare BMP files where # palette entries > biClrUsed
    HANDLE hFile = ::CreateFile(strPathname, /*GENERIC_WRITE |*/ GENERIC_READ,
        bShare ? FILE_SHARE_READ : 0,
        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    ASSERT(hFile != INVALID_HANDLE_VALUE);
    DWORD dwFileSize = ::GetFileSize(hFile, NULL);
    HANDLE hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    DWORD dwErr = ::GetLastError();
    if(hMap == NULL) {
        AfxMessageBox(L"Empty bitmap file");
        return FALSE;
    }
    LPVOID lpvFile = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); // map whole file
    ASSERT(lpvFile != NULL);
    if(((LPBITMAPFILEHEADER) lpvFile)->bfType != 0x4d42) {
        AfxMessageBox(L"Invalid bitmap file");
        DetachMapFile();
        return FALSE;
    }
    AttachMemory((LPBYTE) lpvFile + sizeof(BITMAPFILEHEADER));
    m_lpvFile = lpvFile;
    m_hFile = hFile;
    m_hMap = hMap;
    return TRUE;
}

BOOL CDib::CopyToMapFile(CString strPathname)
{
    // copies DIB to a new file, releases prior pointers
    // if you previously used CreateSection, the HBITMAP will be NULL (and unusable)
    BITMAPFILEHEADER bmfh;
    bmfh.bfType = 0x4d42;  // 'BM'
    bmfh.bfSize = m_dwSizeImage + sizeof(BITMAPINFOHEADER) +
            sizeof(RGBQUAD) * m_nColorTableEntries + sizeof(BITMAPFILEHEADER);
    // meaning of bfSize open to interpretation
    bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
    bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
            sizeof(RGBQUAD) * m_nColorTableEntries;   
    HANDLE hFile = ::CreateFile(strPathname, /*GENERIC_WRITE |*/ GENERIC_READ, 0, NULL,
        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    ASSERT(hFile != INVALID_HANDLE_VALUE);
    int nSize =  sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
                sizeof(RGBQUAD) * m_nColorTableEntries +  m_dwSizeImage;
    HANDLE hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, nSize, NULL);
    DWORD dwErr = ::GetLastError();
    ASSERT(hMap != NULL);
    LPVOID lpvFile = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0); // map whole file
    ASSERT(lpvFile != NULL);
    LPBYTE lpbCurrent = (LPBYTE) lpvFile;
    memcpy(lpbCurrent, &bmfh, sizeof(BITMAPFILEHEADER)); // file header
    lpbCurrent += sizeof(BITMAPFILEHEADER);
    LPBITMAPINFOHEADER lpBMIH = (LPBITMAPINFOHEADER) lpbCurrent;
    memcpy(lpbCurrent, m_lpBMIH,
        sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries); // info
    lpbCurrent += sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;
    memcpy(lpbCurrent, m_lpImage, m_dwSizeImage); // bit image
    DWORD dwSizeImage = m_dwSizeImage;
    Empty();
    m_dwSizeImage = dwSizeImage;
    m_nBmihAlloc = m_nImageAlloc = noAlloc;
    m_lpBMIH = lpBMIH;
    m_lpImage = lpbCurrent;
    m_hFile = hFile;
    m_hMap = hMap;
    m_lpvFile = lpvFile;
    ComputePaletteSize(m_lpBMIH->biBitCount);
    ComputeMetrics();
    MakePalette();
    return TRUE;
}

BOOL CDib::AttachMemory(LPVOID lpvMem, BOOL bMustDelete, HGLOBAL hGlobal)
{
    // assumes contiguous BITMAPINFOHEADER, color table, image
    // color table could be zero length
    Empty();
    m_hGlobal = hGlobal;
    if(bMustDelete == FALSE) {
        m_nBmihAlloc = noAlloc;
    }
    else {
        m_nBmihAlloc = ((hGlobal == NULL) ? crtAlloc : heapAlloc);
    }
    try {
        m_lpBMIH = (LPBITMAPINFOHEADER) lpvMem;
        ComputeMetrics();
        ComputePaletteSize(m_lpBMIH->biBitCount);
        m_lpImage = (LPBYTE) m_lpvColorTable + sizeof(RGBQUAD) * m_nColorTableEntries;
        MakePalette();
    }
    catch(CException* pe) {
        AfxMessageBox(L"AttachMemory error");
        pe->Delete();
        return FALSE;
    }
    return TRUE;
}

UINT CDib::UsePalette(CDC* pDC, BOOL bBackground /**//* = FALSE */)
{
    if(m_hPalette == NULL) return 0;
    HDC hdc = pDC->GetSafeHdc();
    ::SelectPalette(hdc, m_hPalette, bBackground);
    return ::RealizePalette(hdc);
}

BOOL CDib::Draw(CDC* pDC, CPoint origin, CSize size)
{
    if(m_lpBMIH == NULL) return FALSE;
    if(m_hPalette != NULL) {
        ::SelectPalette(pDC->GetSafeHdc(), m_hPalette, TRUE);
    }
    pDC->SetStretchBltMode(COLORONCOLOR);

    ::StretchDIBits(pDC->GetSafeHdc(), origin.x, origin.y, size.cx, size.cy,
        0, 0, m_lpBMIH->biWidth, m_lpBMIH->biHeight,
        m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS, SRCCOPY);

    return TRUE;
}

BOOL CDib::DrawEx(CDC* pDC, CPoint originSrc, CSize sizeSrc, CPoint originDes, CSize sizeDes)
{
    if(m_lpBMIH == NULL) return FALSE;
    if(m_hPalette != NULL) {
        ::SelectPalette(pDC->GetSafeHdc(), m_hPalette, TRUE);
    }
    pDC->SetStretchBltMode(COLORONCOLOR);

    ::StretchDIBits(pDC->GetSafeHdc(), originDes.x, originDes.y, sizeDes.cx, sizeDes.cy,
  originSrc.x, originSrc.y, sizeSrc.cx,sizeSrc.cy,
        m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS, SRCCOPY);

    return TRUE;
}

BOOL CDib::DrawTransparent(CDC* pDC, CPoint origin, CSize size,UINT transColor)
{
 CDC* pTempDC = new CDC();
 pTempDC->CreateCompatibleDC(pDC);
 CBitmap bmp;
 bmp.CreateCompatibleBitmap(pDC, m_lpBMIH->biWidth,m_lpBMIH->biHeight);
 CBitmap* pOldBMP = (CBitmap*)pTempDC->SelectObject(&bmp);

 if(m_lpBMIH == NULL) return FALSE;
   
     if(m_hPalette != NULL) {
        ::SelectPalette(pTempDC->GetSafeHdc(), m_hPalette, TRUE);
    }
   
    pTempDC->SetStretchBltMode(COLORONCOLOR);
    ::StretchDIBits(pTempDC->GetSafeHdc(), origin.x, origin.y, size.cx, size.cy,
        0, 0, m_lpBMIH->biWidth, m_lpBMIH->biHeight,
        m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS, SRCCOPY);
   
 
 pDC->TransparentBlt(origin.x,origin.y,size.cx,size.cy,pTempDC,origin.x,origin.y,size.cx,size.cy,transColor);
 //pDC->BitBlt(origin.x,origin.y,size.cx,size.cy,pTempDC,origin.x,origin.y,SRCCOPY);
 pTempDC->SelectObject(pOldBMP);

 pTempDC->DeleteDC();
 delete pTempDC;
 
 return TRUE;
}


BOOL CDib::DrawTransparentEx(CDC* pDC, CPoint originSrc, CSize sizeSrc, CPoint originDes, CSize sizeDes,CSize sizeClient,UINT transColor)
{
 CDC* pTempDC = new CDC();
 pTempDC->CreateCompatibleDC(pDC);
 CBitmap bmp;
 bmp.CreateCompatibleBitmap(pDC, sizeClient.cx,sizeClient.cy);
 CBitmap* pOldBMP = (CBitmap*)pTempDC->SelectObject(&bmp);

 if(m_lpBMIH == NULL) return FALSE;
   
    if(m_hPalette != NULL) {
        ::SelectPalette(pTempDC->GetSafeHdc(), m_hPalette, TRUE);
    }
   
    ::StretchDIBits(pTempDC->GetSafeHdc(), originDes.x, originDes.y, sizeDes.cx, sizeDes.cy,
  originSrc.x, originSrc.y, sizeSrc.cx,sizeSrc.cy,
        m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS, SRCCOPY);
     
 pDC->TransparentBlt(0,0,sizeClient.cx,sizeClient.cy,pTempDC,0,0,sizeClient.cx,sizeClient.cy,transColor);
 //pDC->BitBlt(origin.x,origin.y,size.cx,size.cy,pTempDC,origin.x,origin.y,SRCCOPY);
 pTempDC->SelectObject(pOldBMP);

 pTempDC->DeleteDC();
 delete pTempDC;
 
 return TRUE;
}

BOOL CDib::DrawSemiTransparent(CDC* pDC, CPoint originSrc, CSize sizeSrc, CPoint originDes, CSize sizeDes,CSize sizeClient,BYTE nAlpha)
{
    /*if(m_lpBMIH == NULL) return FALSE;
    if(m_hPalette != NULL) {
        ::SelectPalette(pDC->GetSafeHdc(), m_hPalette, TRUE);
    }
    pDC->SetStretchBltMode(COLORONCOLOR);

    ::StretchDIBits(pDC->GetSafeHdc(), originDes.x, originDes.y, sizeDes.cx, sizeDes.cy,
  originSrc.x, originSrc.y, sizeSrc.cx,sizeSrc.cy,
        m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS, SRCCOPY);

    return TRUE;

 BLENDFUNCTION bm;
 bm.BlendOp=AC_SRC_OVER;
 bm.BlendFlags=0;
 bm.SourceConstantAlpha = nAlpha;
 bm.AlphaFormat=0; 
 AlphaBlend(pDC->GetSafeHdc(),originDes.x,originDes.y,sizeDes.cx, sizeDes.cy,
  originSrc.x, originSrc.y, sizeSrc.cx, sizeSrc.cy,
  memDC->m_hDC,0,0,rect.Width(),rect.Height(),bm); */
 return false;
}

HBITMAP CDib::CreateSection(CDC* pDC /**//* = NULL */)
{
    if(m_lpBMIH == NULL) return NULL;
    if(m_lpImage != NULL) return NULL; // can only do this if image doesn't exist
    m_hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(), (LPBITMAPINFO) m_lpBMIH,
        DIB_RGB_COLORS,    (LPVOID*) &m_lpImage, NULL, 0);
    ASSERT(m_lpImage != NULL);
    return m_hBitmap;
}

BOOL CDib::MakePalette()
{
    // makes a logical palette (m_hPalette) from the DIB's color table
    // this palette will be selected and realized prior to drawing the DIB
    if(m_nColorTableEntries == 0) return FALSE;
    if(m_hPalette != NULL) ::DeleteObject(m_hPalette);
    TRACE("CDib::MakePalette -- m_nColorTableEntries = %d/n", m_nColorTableEntries);
    LPLOGPALETTE pLogPal = (LPLOGPALETTE) new char[2 * sizeof(WORD) +
        m_nColorTableEntries * sizeof(PALETTEENTRY)];
    pLogPal->palVersion = 0x300;
    pLogPal->palNumEntries = m_nColorTableEntries;
    LPRGBQUAD pDibQuad = (LPRGBQUAD) m_lpvColorTable;
    for(int i = 0; i < m_nColorTableEntries; i++) {
        pLogPal->palPalEntry[i].peRed = pDibQuad->rgbRed;
        pLogPal->palPalEntry[i].peGreen = pDibQuad->rgbGreen;
        pLogPal->palPalEntry[i].peBlue = pDibQuad->rgbBlue;
        pLogPal->palPalEntry[i].peFlags = 0;
        pDibQuad++;
    }
    m_hPalette = ::CreatePalette(pLogPal);
    delete pLogPal;
    return TRUE;
}   

BOOL CDib::SetSystemPalette(CDC* pDC)
{
    // if the DIB doesn't have a color table, we can use the system's halftone palette
    if(m_nColorTableEntries != 0) return FALSE;
    m_hPalette = ::CreateHalftonePalette(pDC->GetSafeHdc());
    return TRUE;
}

HBITMAP CDib::CreateBitmap(CDC* pDC)
{
    if (m_dwSizeImage == 0) return NULL;
    HBITMAP hBitmap = ::CreateDIBitmap(pDC->GetSafeHdc(), m_lpBMIH,
            CBM_INIT, m_lpImage, (LPBITMAPINFO) m_lpBMIH, DIB_RGB_COLORS);
    ASSERT(hBitmap != NULL);
    return hBitmap;
}

BOOL CDib::Compress(CDC* pDC, BOOL bCompress /**//* = TRUE */)
{
    // 1. makes GDI bitmap from existing DIB
    // 2. makes a new DIB from GDI bitmap with compression
    // 3. cleans up the original DIB
    // 4. puts the new DIB in the object
    if((m_lpBMIH->biBitCount != 4) && (m_lpBMIH->biBitCount != 8)) return FALSE;
        // compression supported only for 4 bpp and 8 bpp DIBs
    if(m_hBitmap) return FALSE; // can't compress a DIB Section!
    TRACE("Compress: original palette size = %d/n", m_nColorTableEntries);
    HDC hdc = pDC->GetSafeHdc();
    HPALETTE hOldPalette = ::SelectPalette(hdc, m_hPalette, FALSE);
    HBITMAP hBitmap;  // temporary
    if((hBitmap = CreateBitmap(pDC)) == NULL) return FALSE;
    int nSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;
    LPBITMAPINFOHEADER lpBMIH = (LPBITMAPINFOHEADER) new char[nSize];
    memcpy(lpBMIH, m_lpBMIH, nSize);  // new header
    if(bCompress) {
        switch (lpBMIH->biBitCount) {
        case 4:
            lpBMIH->biCompression = BI_RLE4;
            break;
        case 8:
            lpBMIH->biCompression = BI_RLE8;
            break;
        default:
            ASSERT(FALSE);
        }
        // calls GetDIBits with null data pointer to get size of compressed DIB
        if(!::GetDIBits(pDC->GetSafeHdc(), hBitmap, 0, (UINT) lpBMIH->biHeight,
                        NULL, (LPBITMAPINFO) lpBMIH, DIB_RGB_COLORS)) {
            AfxMessageBox(L"Unable to compress this DIB");
            // probably a problem with the color table
             ::DeleteObject(hBitmap);
            delete [] lpBMIH;
            ::SelectPalette(hdc, hOldPalette, FALSE);
            return FALSE;
        }
        if (lpBMIH->biSizeImage == 0) {
            AfxMessageBox(L"Driver can't do compression");
             ::DeleteObject(hBitmap);
            delete [] lpBMIH;
            ::SelectPalette(hdc, hOldPalette, FALSE);
            return FALSE;
        }
        else {
            m_dwSizeImage = lpBMIH->biSizeImage;
        }
    }
    else {
        lpBMIH->biCompression = BI_RGB; // decompress
        // figure the image size from the bitmap width and height
        DWORD dwBytes = ((DWORD) lpBMIH->biWidth * lpBMIH->biBitCount) / 32;
        if(((DWORD) lpBMIH->biWidth * lpBMIH->biBitCount) % 32) {
            dwBytes++;
        }
        dwBytes *= 4;
        m_dwSizeImage = dwBytes * lpBMIH->biHeight; // no compression
        lpBMIH->biSizeImage = m_dwSizeImage;
    }
    // second GetDIBits call to make DIB
    LPBYTE lpImage = (LPBYTE) new char[m_dwSizeImage];
    VERIFY(::GetDIBits(pDC->GetSafeHdc(), hBitmap, 0, (UINT) lpBMIH->biHeight,
            lpImage, (LPBITMAPINFO) lpBMIH, DIB_RGB_COLORS));
    TRACE("dib successfully created - height = %d/n", lpBMIH->biHeight);
    ::DeleteObject(hBitmap);
    Empty();
    m_nBmihAlloc = m_nImageAlloc = crtAlloc;
    m_lpBMIH = lpBMIH;
    m_lpImage = lpImage;
    ComputeMetrics();
    ComputePaletteSize(m_lpBMIH->biBitCount);
    MakePalette();
    ::SelectPalette(hdc, hOldPalette, FALSE);
    TRACE("Compress: new palette size = %d/n", m_nColorTableEntries);
    return TRUE;
}

BOOL CDib::Read(CFile* pFile)
{
    // 1. read file header to get size of info hdr + color table
    // 2. read info hdr (to get image size) and color table
    // 3. read image
    // can't use bfSize in file header
    Empty();
    int nCount, nSize;
    BITMAPFILEHEADER bmfh;
    try {
        nCount = pFile->Read((LPVOID) &bmfh, sizeof(BITMAPFILEHEADER));
        if(nCount != sizeof(BITMAPFILEHEADER)) {
            throw new CUserException;
        }
        if(bmfh.bfType != 0x4d42) {
            throw new CUserException;
        }
        nSize = bmfh.bfOffBits - sizeof(BITMAPFILEHEADER);
        m_lpBMIH = (LPBITMAPINFOHEADER) new char[nSize];
        m_nBmihAlloc = m_nImageAlloc = crtAlloc;
        nCount = pFile->Read(m_lpBMIH, nSize); // info hdr & color table
        ComputeMetrics();
        ComputePaletteSize(m_lpBMIH->biBitCount);
        MakePalette();
        m_lpImage = (LPBYTE) new char[m_dwSizeImage];
        nCount = pFile->Read(m_lpImage, m_dwSizeImage); // image only
    }
    catch(CException* pe) {
        AfxMessageBox(L"Read error");
        pe->Delete();
        return FALSE;
    }
    return TRUE;
}

BOOL CDib::ReadSection(CFile* pFile, CDC* pDC /**//* = NULL */)
{
    // new function reads BMP from disk and creates a DIB section
    //    allows modification of bitmaps from disk
    // 1. read file header to get size of info hdr + color table
    // 2. read info hdr (to get image size) and color table
    // 3. create DIB section based on header parms
    // 4. read image into memory that CreateDibSection allocates
    Empty();
    int nCount, nSize;
    BITMAPFILEHEADER bmfh;
    try {
        nCount = pFile->Read((LPVOID) &bmfh, sizeof(BITMAPFILEHEADER));
        if(nCount != sizeof(BITMAPFILEHEADER)) {
            throw new CUserException;
        }
        if(bmfh.bfType != 0x4d42) {
            throw new CUserException;
        }
        nSize = bmfh.bfOffBits - sizeof(BITMAPFILEHEADER);
        m_lpBMIH = (LPBITMAPINFOHEADER) new char[nSize];
        m_nBmihAlloc = crtAlloc;
        m_nImageAlloc = noAlloc;
        nCount = pFile->Read(m_lpBMIH, nSize); // info hdr & color table
        if(m_lpBMIH->biCompression != BI_RGB) {
            throw new CUserException;
        }
        ComputeMetrics();
        ComputePaletteSize(m_lpBMIH->biBitCount);
        MakePalette();
        UsePalette(pDC);
        m_hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(), (LPBITMAPINFO) m_lpBMIH,
            DIB_RGB_COLORS,    (LPVOID*) &m_lpImage, NULL, 0);
        ASSERT(m_lpImage != NULL);
        nCount = pFile->Read(m_lpImage, m_dwSizeImage); // image only
    }
    catch(CException* pe) {
        AfxMessageBox(L"ReadSection error");
        pe->Delete();
        return FALSE;
    }
    return TRUE;
}

BOOL CDib::Write(CFile* pFile)
{
    BITMAPFILEHEADER bmfh;
    bmfh.bfType = 0x4d42;  // 'BM'
    int nSizeHdr = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorTableEntries;
    bmfh.bfSize = 0;
//    bmfh.bfSize = sizeof(BITMAPFILEHEADER) + nSizeHdr + m_dwSizeImage;
    // meaning of bfSize open to interpretation (bytes, words, dwords?) -- we won't use it
    bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
    bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
            sizeof(RGBQUAD) * m_nColorTableEntries;   
    try {
        pFile->Write((LPVOID) &bmfh, sizeof(BITMAPFILEHEADER));
        pFile->Write((LPVOID) m_lpBMIH,  nSizeHdr);
        pFile->Write((LPVOID) m_lpImage, m_dwSizeImage);
    }
    catch(CException* pe) {
        pe->Delete();
        AfxMessageBox(L"write error");
        return FALSE;
    }
    return TRUE;
}

void CDib::Serialize(CArchive& ar)
{
    DWORD dwPos;
    dwPos = ar.GetFile()->GetPosition();
    TRACE("CDib::Serialize -- pos = %d/n", dwPos);
    ar.Flush();
    dwPos = ar.GetFile()->GetPosition();
    TRACE("CDib::Serialize -- pos = %d/n", dwPos);
    if(ar.IsStoring()) {
        Write(ar.GetFile());
    }
    else {
        Read(ar.GetFile());
    }
}

// helper functions
void CDib::ComputePaletteSize(int nBitCount)
{
    if((m_lpBMIH == NULL) || (m_lpBMIH->biClrUsed == 0)) {
        switch(nBitCount) {
            case 1:
                m_nColorTableEntries = 2;
                break;
            case 4:
                m_nColorTableEntries = 16;
                break;
            case 8:
                m_nColorTableEntries = 256;
                break;
            case 16:
            case 24:
            case 32:
                m_nColorTableEntries = 0;
                break;
            default:
                ASSERT(FALSE);
        }
    }
    else {
        m_nColorTableEntries = m_lpBMIH->biClrUsed;
    }
    ASSERT((m_nColorTableEntries >= 0) && (m_nColorTableEntries <= 256));
}

void CDib::ComputeMetrics()
{
    if(m_lpBMIH->biSize != sizeof(BITMAPINFOHEADER)) {
        TRACE("Not a valid Windows bitmap -- probably an OS/2 bitmap/n");
        throw new CUserException;
    }
    m_dwSizeImage = m_lpBMIH->biSizeImage;
    if(m_dwSizeImage == 0) {
        DWORD dwBytes = ((DWORD) m_lpBMIH->biWidth * m_lpBMIH->biBitCount) / 32;
        if(((DWORD) m_lpBMIH->biWidth * m_lpBMIH->biBitCount) % 32) {
            dwBytes++;
        }
        dwBytes *= 4;
        m_dwSizeImage = dwBytes * m_lpBMIH->biHeight; // no compression
    }
    m_lpvColorTable = (LPBYTE) m_lpBMIH + sizeof(BITMAPINFOHEADER);
}

void CDib::Empty()
{
    // this is supposed to clean up whatever is in the DIB
    DetachMapFile();
    if(m_nBmihAlloc == crtAlloc) {
        delete [] m_lpBMIH;
    }
    else if(m_nBmihAlloc == heapAlloc) {
        ::GlobalUnlock(m_hGlobal);
        ::GlobalFree(m_hGlobal);
    }
    if(m_nImageAlloc == crtAlloc) delete [] m_lpImage;
    if(m_hPalette != NULL) ::DeleteObject(m_hPalette);
    if(m_hBitmap != NULL) ::DeleteObject(m_hBitmap);
    m_nBmihAlloc = m_nImageAlloc = noAlloc;
    m_hGlobal = NULL;
    m_lpBMIH = NULL;
    m_lpImage = NULL;
    m_lpvColorTable = NULL;
    m_nColorTableEntries = 0;
    m_dwSizeImage = 0;
    m_lpvFile = NULL;
    m_hMap = NULL;
    m_hFile = NULL;
    m_hBitmap = NULL;
    m_hPalette = NULL;
}

void CDib::DetachMapFile()
{
    if(m_hFile == NULL) return;
    ::UnmapViewOfFile(m_lpvFile);
    ::CloseHandle(m_hMap);
    ::CloseHandle(m_hFile);
    m_hFile = NULL;
}

原创粉丝点击