Going Further with DIBs

Each new version of Windows offers more DIB programming choices. Both Windows 95 and Microsoft Windows NT 4.0 provide the LoadImage and DrawDibDraw functions, which are useful alternatives to the DIB functions already described. Experiment with these functions to see if they work well in your applications.

The LoadImage Function

The LoadImage function can read a bitmap directly from a disk file, returning a DIB section handle. It can even process OS/2 format DIBs. Suppose you wanted to add an ImageLoad member function to CDib that would work like ReadSection. This is the code you would add to cdib.cpp:

BOOL CDib::ImageLoad(const char* lpszPathName, CDC* pDC)
{
    Empty();
    m_hBitmap = (HBITMAP) ::LoadImage(NULL, lpszPathName, 
        IMAGE_BITMAP, 0, 0, 
        LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
    DIBSECTION ds;
    VERIFY(::GetObject(m_hBitmap, sizeof(ds), &ds) == sizeof(ds));
    // Allocate memory for BITMAPINFOHEADER
    //  and biggest possible color table
    m_lpBMIH = (LPBITMAPINFOHEADER) new 
        char[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)];
    memcpy(m_lpBMIH, &ds.dsBmih, sizeof(BITMAPINFOHEADER));
    TRACE("CDib::LoadImage, biClrUsed = %d, biClrImportant = %d\n",
        m_lpBMIH->biClrUsed, m_lpBMIH->biClrImportant);
    ComputeMetrics(); // sets m_lpvColorTable
    m_nBmihAlloc = crtAlloc;
    m_lpImage = (LPBYTE) ds.dsBm.bmBits;
    m_nImageAlloc = noAlloc;
    // Retrieve the DIB section's color table
    //  and make a palette from it
    CDC memdc;
    memdc.CreateCompatibleDC(pDC);
    ::SelectObject(memdc.GetSafeHdc(), m_hBitmap);
    UINT nColors = ::GetDIBColorTable(memdc.GetSafeHdc(), 0, 256, 
        (RGBQUAD*) m_lpvColorTable);
    if (nColors != 0) {
        ComputePaletteSize(m_lpBMIH->biBitCount);
        MakePalette();
    }
    // memdc deleted and bitmap deselected
    return TRUE;
}

Note that this function extracts and copies the BITMAPINFOHEADER structure and sets the values of the CDib pointer data members. You must do some work to extract the palette from the DIB section, but the Win32 GetDIBColorTable function gets you started. It's interesting that GetDIBColorTable can't tell you how many palette entries a particular DIB uses. If the DIB uses only 60 entries, for example, GetDIBColorTable generates a 256-entry color table with the last 196 entries set to 0.

The DrawDibDraw Function

Windows includes the Video for Windows (VFW) component, which is supported by Visual C++. The VFW DrawDibDraw function is an alternative to StretchDIBits. One advantage of DrawDibDraw is its ability to use dithered colors. Another is its increased speed in drawing a DIB with a bpp value that does not match the current video mode. The main disadvantage is the need to link the VFW code into your process at runtime.

Shown below is a DrawDib member function for the CDib class that calls DrawDibDraw:

BOOL CDib::DrawDib(CDC* pDC, CPoint origin, CSize size)
{
    if (m_lpBMIH == NULL) return FALSE;
    if (m_hPalette != NULL) {
        ::SelectPalette(pDC->GetSafeHdc(), m_hPalette, TRUE);
    }
    HDRAWDIB hdd = ::DrawDibOpen();
    CRect rect(origin, size);
    pDC->LPtoDP(rect); // Convert DIB's rectangle
                       //  to MM_TEXT coordinates
    rect -= pDC->GetViewportOrg();
    int nMapModeOld = pDC->SetMapMode(MM_TEXT);
    ::DrawDibDraw(hdd, pDC->GetSafeHdc(), rect.left, rect.top,
        rect.Width(), rect.Height(), m_lpBMIH, m_lpImage, 0, 0,
        m_lpBMIH->biWidth, m_lpBMIH->biHeight, 0);
    pDC->SetMapMode(nMapModeOld);
    VERIFY(::DrawDibClose(hdd));
    return TRUE;
}
Note that DrawDibDraw needs MM_TEXT coordinates and the MM_TEXT mapping mode. Thus, logical coordinates must be converted not to device coordinates but to pixels with the origin at the top left of the scrolling window.

To use DrawDibDraw, your program needs an #include<vfw.h> statement, and you must add vfw32.lib to the list of linker input files. DrawDibDraw might assume the bitmap it draws is in read/write memory, a fact to keep in mind if you map the memory to the BMP file.