| Tenouk C & C++ | MFC Home | Bitmaps 2 | Bitmaps 4 | Download | Site Index |


 

 

 

 

 

Module 21b: The Bitmaps 3

 

 

 

 

 

This is a continuation from the previous module... Program examples compiled using Visual C++ 6.0 (MFC 6.0) compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this Tutorial are listed below:

  1. Windows Animation

  2. DIBs and the CDib Class

  3. A Few Words About Palette Programming

  4. DIBs, Pixels, and Color Tables

  5. The Structure of a DIB Within a BMP File

  6. DIB Access Functions

  7. The CDib Class

 

 

 

Windows Animation

 

MYMFC26B is a crude attempt at Windows animation. What if you wanted to move an angelfish instead of a circle? Win32 doesn't have an Angelfish function (yet), so you'd have to keep your angelfish in its own bitmap and use the StretchBlt() mask ROP codes to merge the angelfish with the background. You'd probably keep the background in its own bitmap, too. These techniques are outside the scope of this book. If you are interested in learning more about Windows Animation, run out and get Nigel Thompson's Animation Techniques in Win32 (Microsoft Press, 1995). After you read it, you can get rich writing video games for Windows!

 

DIBs and the CDib Class

 

There's an MFC class for GDI bitmaps (CBitmap), but there's no MFC class for DIBs. Don't worry, I'm giving you one here. It's a complete rewrite of the CDib class from the early editions of this book (prior to the fourth edition), and it takes advantage of Win32 features such as memory-mapped files, improved memory management, and DIB sections. It also includes palette support. Before you examine the CDib class, however, you need a little background on DIBs.

 

A Few Words About Palette Programming

 

Windows palette programming is quite complex, but you've got to deal with it if you expect your users to run their displays in the 8-bpp (bits per pixel) mode and many users will if they have video cards with 1 MB or less of memory.

Suppose you're displaying a single DIB in a window. First you must create a logical palette, a GDI object that contains the colors in the DIB. Then you must "realize" this logical palette into the hardware system palette, a table of the 256 colors the video card can display at that instant. If your program is the foreground program, the realization process tries to copy all your colors into the system palette, but it doesn't touch the 20 standard Windows colors. For the most part, your DIB looks just like you want it to look.

But what if another program is the foreground program, and what if that program has a forest scene DIB with 236 shades of green? Your program still realizes its palette, but something different happens this time. Now the system palette won't change, but Windows sets up a new mapping between your logical palette and the system palette. If your DIB contains a neon pink color, for example, Windows maps it to the standard red color. If your program forgot to realize its palette, your neon pink stuff would turn green when the other program went active.

The forest scene example is extreme because we assumed that the other program grabbed 236 colors. If instead the other program realized a logical palette with only 200 colors, Windows would let your program load 36 of its own colors, including, one hopes, neon pink.

So when is a program supposed to realize its palette? The Windows message WM_PALETTECHANGED is sent to your program's main window whenever a program, including yours, realizes its palette. Another message, WM_QUERYNEWPALETTE, is sent whenever one of the windows in your program gets the input focus. Your program should realize its palette in response to both these messages (unless your program generated the message). These palette messages are not sent to your view window, however. You must map them in your application's main frame window and then notify the view.

You call the Win32 RealizePalette() function to perform the realization, but first you must call SelectPalette() to select your DIB's logical palette into the device context. SelectPalette() has a flag parameter that you normally set to FALSE in your WM_PALETTECHANGED and WM_QUERYNEWPALETTE handlers. This flag ensures that your palette is realized as a foreground palette if your application is indeed running in the foreground. If you use a TRUE flag parameter here, you can force Windows to realize the palette as though the application were in the background.

You must also call SelectPalette() for each DIB that you display in your OnDraw() function. This time you call it with a TRUE flag parameter. Things do get complicated if you're displaying several DIBs, each with its own palette. Basically, you've got to choose a palette for one of the DIBs and realize it (by selecting it with the FALSE parameter) in the palette message handlers. The chosen DIB will end up looking better than the other DIBs. There are ways of merging palettes, but it might be easier to go out and buy more video memory.

 

DIBs, Pixels, and Color Tables

 

A DIB contains a two-dimensional array of elements called pixels. In many cases, each DIB pixel will be mapped to a display pixel, but the DIB pixel might be mapped to some logical area on the display, depending on the mapping mode and the display function stretch parameters. A pixel consists of 1, 4, 8, 16, 24, or 32 contiguous bits, depending on the color resolution of the DIB. For 16-bpp, 24-bpp, and 32-bpp DIBs, each pixel represents an RGB color. A pixel in a 16-bpp DIB typically contains 5 bits each for red, green, and blue values; a pixel in a 24-bpp DIB has 8 bits for each color value. The 16-bpp and 24-bpp DIBs are optimized for video cards that can display 65,536 or 16.7 million simultaneous colors.

A 1-bpp DIB is a monochrome DIB, but these DIBs don't have to be black and white, they can contain any two colors chosen from the color table that is built into each DIB. A monochrome bitmap has two 32-bit color table entries, each containing 8 bits for red, green, and blue values plus another 8 bits for flags. Zero (0) pixels use the first entry, and one (1) pixel uses the second. Whether you have a 65,536-color video card or a 16.7-million-color card, Windows can display the two colors directly. (Windows truncates 8-bits-per-color values to 5 bits for 65,536-color displays.) If your video card is running in 256-color palletized mode, your program can adjust the system palette to load the two specified colors.

Eight-bpp DIBs are quite common. Like a monochrome DIB, an 8-bpp DIB has a color table, but the color table has 256 (or fewer) 32-bit entries. Each pixel is an index into this color table. If you have a palletized video card, your program can create a logical palette from the 256 entries. If another program (running in the foreground) has control of the system palette, Windows does its best to match your logical palette colors to the system palette.

What if you're trying to display a 24-bpp DIB with a 256-color palletized video card? If the DIB author was nice, he or she included a color table containing the most important colors in the DIB. Your program can build a logical palette from that table, and the DIB will look fine. If the DIB has no color table, use the palette returned by the Win32 CreateHalftonePalette() function; it's better than the 20 standard colors you'd get with no palette at all. Another option is to analyze the DIB to identify the most important colors, but you can buy a utility to do that.

 

The Structure of a DIB Within a BMP File

 

You know that the DIB is the standard Windows bitmap format and that a BMP file contains a DIB. So let's look inside a BMP file to see what's there. Figure 15 shows a layout for a BMP file.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 15: The layout for a BMP file.

 

The BITMAPFILEHEADER structure contains the offset to the image bits, which you can use to compute the combined size of the BITMAPINFOHEADER structure and the color table that follows. The BITMAPFILEHEADER structure contains a file size member, but you can't depend on it because you don't know whether the size is measured in bytes, words, or double words.

The BITMAPINFOHEADER structure contains the bitmap dimensions, the bits per pixel, compression information for both 4-bpp and 8-bpp bitmaps, and the number of color table entries. If the DIB is compressed, this header contains the size of the pixel array; otherwise, you can compute the size from the dimensions and the bits per pixel. Immediately following the header is the color table (if the DIB has a color table). The DIB image comes after that. The DIB image consists of pixels arranged by column within rows, starting with the bottom row. Each row is padded to a 4-byte boundary. The only place you'll find a BITMAPFILEHEADER structure, however, is in a BMP file. If you get a DIB from the clipboard, for example, there will not be a file header. You can always count on the color table to follow the BITMAPINFOHEADER structure, but you can't count on the image to follow the color table. If you're using the CreateDIBSection() function, for example, you must allocate the bitmap info header and color table and then let Windows allocate the image somewhere else. This module and all the associated code are specific to Windows DIBs. There's also a well-documented variation of the DIB format for OS/2. If you need to process these OS/2 DIBs, you'll have to modify the CDib class.

 

DIB Access Functions

 

Windows supplies some important DIB access functions. None of these functions is wrapped by MFC, so you'll need to refer to the online Win32 documentation for details. Here's a summary:

The CDib Class

 

If DIBs look intimidating, don't worry. The CDib class makes DIB programming easy. The best way to get to know the CDib class is to look at the public member functions and data members. Listing 15 shows the CDib header and implementation files.

 

CDIB.H

#ifndef _INSIDE_VISUAL_CPP_CDIB

#define _INSIDE_VISUAL_CPP_CDIB

 

class CDib : public CObject

{

    enum Alloc {noAlloc, crtAlloc,

                heapAlloc}; // applies to BITMAPINFOHEADER

    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(const char* strPathname, BOOL bShare = FALSE);

    BOOL CopyToMapFile(const char* strPathname);

    BOOL AttachMemory(LPVOID lpvMem, BOOL bMustDelete = FALSE, HGLOBAL hGlobal = NULL);

    BOOL Draw(CDC* pDC, CPoint origin, CSize size); // until we implement 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();

};

#endif // _INSIDE_VISUAL_CPP_CDIB

 

 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

CDIB.CPP

// cdib.cpp

// new version for WIN32

#include "stdafx.h"

#include "cdib.h"

 

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

 

IMPLEMENT_SERIAL(CDib, CObject, 0);

 

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(const char* 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_READWRITE, 0, 0, NULL);

       DWORD dwErr = ::GetLastError();

       if(hMap == NULL) {

              AfxMessageBox("Empty bitmap file");

              return FALSE;

       }

       LPVOID lpvFile = ::MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0); // map whole file

       ASSERT(lpvFile != NULL);

       if(((LPBITMAPFILEHEADER) lpvFile)->bfType != 0x4d42) {

              AfxMessageBox("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(const char* 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_READWRITE, 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("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;

}

 

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("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("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 CException;

              }

              if(bmfh.bfType != 0x4d42) {

                     throw new CException;

              }

              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("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 CException;

              }

              if(bmfh.bfType != 0x4d42) {

                     throw new CException;

              }

              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 CException;

              }

              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("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("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 CException;

       }

       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;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Listing 15: The CDib class declaration (header file) and implementation (source file).

 

 

Continue on next module...part 4.

 

 

 

 

 

 

Further reading and digging:

  1. MSDN MFC 7.0 class library online documentation.

  2. MSDN MFC 9.0 class library online documentation - latest version.

  3. MSDN Library

  4. Windows data type.

  5. Win32 programming Tutorial.

  6. The best of C/C++, MFC, Windows and other related books.

  7. Unicode and Multibyte character set: Story and program examples.

 

 

 

 


 

| Tenouk C & C++ | MFC Home | Bitmaps 2 | Bitmaps 4 | Download | Site Index |