Encapsulated Dib API

This article describes a class (CDib) encapsulating the Dib API distributed with the DIBLOOK sample. The class methods are mostly the same global functions declared in the dibapi.h file. The names of the functions were slighty modified to reflect encapsulation (e.g. the dib sufix was removed). The code is almost the same, with some modifications.

The use of a class instead of global functions allowed for some extra features:

  • The Dib maintains a CPalette member that updates automatically
  • The BITMAPINFOHEADER structure and the image bits are kept in diferent data members (which allows for an easier access if image proccesing capabilities are added)
  • Improved clipboard support implementation
  • The Paint function (former PaintDIB) now includes palette support
  • Serialization support (Serialize function implemented)

This class could be a starting point to a more complex image class. It has been used (among other things) to implement a static control to display images and a completely object oriented DIBLOOK. These articles can be found on the static control section and the samples section respectively.

Download Source Code


Last updated: 24 June 1998



Comments

  • Bug in clipboard support

    Posted by Legacy on 11/21/2000 12:00am

    Originally posted by: Thomas Fritsch

    I have encountered a major problem concerning the
    
    copy/paste support of the DibLookEx application.
    Copy/paste from DibLookEx to another Windows-application
    (for example MS-PhotoEditor, MS-Paint, or the original
    DibLook-application) and vice versa does not work at all.
    Only copy/paste from DibLookEx to DibLookEx works fine.

    The reason for this problem is the following discrepancy:
    The clipboard format CF_DIB as specified by Microsoft
    contains no(!) leading BITMAPFILEHEADER. But the method
    CDib::CopyToHandle writes and method CDib::ReadFromHandle
    expects to read a memory block with(!) a leading
    BITMAPFILEHEADER.

    As a fix I have rewritten these two methods.
    In order to avoid writing a BITMAPFILEHEADER the method
    CDib::CopyToHandle no longer calls CDib::Save(CFile);
    and in order to avoid expecting a BITMAPFILEHEADER the
    method CDib::ReadFromHandle no longer calls CDib::Read(CFile).


    HGLOBAL CDib::CopyToHandle() const
    {
    if (m_pBMI == NULL)
    return 0;

    CSharedFile file;
    try
    {
    // Write the DIB header
    UINT nCount = sizeof(BITMAPINFO) + (NumColors()-1)*sizeof(RGBQUAD);
    file.Write(m_pBMI, nCount);

    // Write the DIB bits
    DWORD dwBytes = m_pBMI->bmiHeader.biBitCount * Width();
    // 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();
    file.WriteHuge(m_pBits, nCount);

    return file.Detach();
    }
    catch (CFileException* e)
    {
    e->Delete();
    file.Detach();
    return 0;
    }
    }


    DWORD CDib::ReadFromHandle(HGLOBAL hGlobal)
    {
    // Ensures no memory leaks will occur
    Free();

    LPBITMAPINFO pBMI = (LPBITMAPINFO)GlobalLock(hGlobal);
    if (pBMI == 0)
    return 0;

    // Read DIB header.
    ASSERT(pBMI->bmiHeader.biSize == sizeof(BITMAPINFOHEADER));
    m_pBMI = (LPBITMAPINFO)GlobalAllocPtr(GHND, pBMI->bmiHeader.biSize + 256*sizeof(RGBQUAD));
    if (m_pBMI == 0)
    {
    GlobalUnlock(hGlobal);
    return 0;
    }
    memcpy(&m_pBMI->bmiHeader, &pBMI->bmiHeader, pBMI->bmiHeader.biSize);
    memcpy(&m_pBMI->bmiColors, &pBMI->bmiColors, PaletteSize());
    DWORD dwReadBytes = pBMI->bmiHeader.biSize + PaletteSize();

    // Read DIB bits.
    m_pBits = (LPBYTE)GlobalAllocPtr(GHND, pBMI->bmiHeader.biSizeImage);
    if (m_pBits == 0)
    {
    GlobalUnlock(hGlobal);
    GlobalFreePtr(m_pBMI);
    m_pBMI = NULL;
    return 0;
    }
    LPBYTE pBits = (LPBYTE)&pBMI->bmiColors + PaletteSize();
    memcpy(m_pBits, pBits, pBMI->bmiHeader.biSizeImage);
    dwReadBytes += pBMI->bmiHeader.biSizeImage;

    GlobalUnlock(hGlobal);
    CreatePalette();
    return dwReadBytes;
    }

    Reply
  • Bug in saving compressed bmps

    Posted by Legacy on 11/07/2000 12:00am

    Originally posted by: Arnt Witteveen

    The save function in the CDIB class asserts if it tries to 
    
    save a compressed bitmap. The reason is in the calculation
    of the image size: it is done correctly at first, and then
    it is calculated again, forgetting the possibility of RLE
    compressed bitmaps entirely. This causes it to try to write
    so much data it ends up outside of it's memory space.

    I also fixed a minor bug causing it to write 4 bytes to much.

    Adapted save function is below.


    DWORD CDib::Save(CFile& file) const
    {

    BITMAPFILEHEADER bmfHdr; // Header for Bitmap file
    DWORD dwDIBSize;
    DWORD dwBmBitsSize; // Size of Bitmap Bits only

    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)
    bmfHdr.bfType = DIB_HEADER_MARKER; // "BM"

    // Calculating the size of the DIB is a bit tricky (if we want to
    // do it right). The easiest way to do this is to call GlobalSize()
    // on our global handle, but since the size of our global memory may have
    // been padded a few bytes, we may end up writing out a few too
    // many bytes to the file (which may cause problems with some apps).
    //
    // So, instead let's calculate the size manually (if we can)
    //
    // First, find size of header plus size of color table. Since the
    // first DWORD in both BITMAPINFOHEADER and BITMAPCOREHEADER conains
    // the size of the structure, let's use this.
    dwDIBSize = *(LPDWORD)&m_pBMI->bmiHeader + PaletteSize(); // Partial Calculation

    // Now calculate the size of the image
    if ((m_pBMI->bmiHeader.biCompression == BI_RLE8) || (m_pBMI->bmiHeader.biCompression == BI_RLE4))
    {
    // It's an RLE bitmap, we can't calculate size, so trust the
    // biSizeImage field
    dwBmBitsSize = m_pBMI->bmiHeader.biSizeImage;
    }
    else
    {
    // 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;

    // 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;
    }

    dwDIBSize += 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();

    // Write the file header
    file.Write((LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER));
    DWORD dwBytesSaved = sizeof(BITMAPFILEHEADER);

    // Write the DIB header
    UINT nCount = m_pBMI->bmiHeader.biSize + PaletteSize();
    dwBytesSaved += nCount;
    file.Write(m_pBMI, nCount);

    // Write the DIB bits
    dwBytesSaved += dwBmBitsSize;
    file.WriteHuge(m_pBits, dwBmBitsSize);

    return dwBytesSaved;
    }

    Reply
  • How can I extend it to read the RGB value for each pixel ?

    Posted by Legacy on 04/26/2000 12:00am

    Originally posted by: Shahzad Alam

    How can I extend it to read the RGB value for each pixel ? Should I write some new function and how....
    

    Reply
  • CDib cannot read certain files (RLE8 encoded) + fix

    Posted by Legacy on 10/12/1999 12:00am

    Originally posted by: Arnt Witteveen

    CDib cannot read certain files (RLE8 encoded), 
    
    because it does not take into account the compression
    when calculating the file size. In this case, and
    in most other cases, the actual size can be read from:
    m_pBMI->bmiHeader.biSizeImage. Only if the type is
    uncrompressed, it is allowed that this member does
    not point to the actual size, and then it should be 0.
    So I made a small change to CDib::Read to allow for this.
    The most important part of my change is marked
    "change by Arnt". (The rest is just formatting and
    a closing brace.) The class already implements the
    reading & decompressing of these files, it just
    can't get them from a file.

    Anyway, here it is:

    (in DWORD CDib::Read(CFile& file) )


    // Calculate the rest of the image

    //Change By Arnt: Do not calculate size if it is in the header already.

    UINT nCount = 0;
    if ( (nCount = m_pBMI->bmiHeader.biSizeImage) == 0) {
    // note: 0 is allowed ONLY when type is BI_RGB,
    //i.e. m_pBMI->bmiHeader.biCompression == BI_RGB == 0

    //End of change By Arnt

    DWORD dwBytes = m_pBMI->bmiHeader.biBitCount * Width();
    // 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();
    }


    Reply
  • Bug fix

    Posted by Legacy on 07/08/1999 12:00am

    Originally posted by: Arnt & Kurt

    Hi,

    Thx for the code. Finally a readable CDib class :))
    We found two bugs in your code though.
    One is already mentioned : the fact that "Save(CFile& file)" uses the length of the file is wrong.
    Here's the other one :
    You gotta flush the archive before using ar.GetFile().
    (press F1 while standing on GetFile, and look at the remark)
    Otherwise you bmp will be saved in the beginning of the file that you are archiving.

    void CDib::Serialize(CArchive& ar)
    {
    ar.Flush();
    CFile* pFile = ar.GetFile();
    ASSERT(pFile != NULL);
    if (ar.IsStoring())
    { // storing code
    Save(*pFile);
    }
    else
    { // loading code
    Read(*pFile);
    }
    }

    Greetings
    Arnt & Kurt

    Reply
  • Minor Bug CDibStatic::PaintDIB

    Posted by Legacy on 03/08/1999 12:00am

    Originally posted by: Reinhard Bosch

    I found a minor Bug in PaintDIB. If the image fits into the destination window,
    the nDestWidth is set to m_DIB.Height() and nDestHeight to m_DIB.Width().

    Recently I had tried to encapsulate the same DIBLOOK-Sample, but your
    version is much more better.

    Reinhard

    Reply
  • Encountered an issue with Serialization

    Posted by Legacy on 01/03/1999 12:00am

    Originally posted by: Will Baldwin

    UPDATE	FIX: CDib::FileRead(CFile &file)
    
    

    I replaced the line:
    DWORD dwLength = file.GetLength();

    with:
    DWORD dwLength = bmfHeader.bfSize; //=file.GetLength();

    Apparently the problem was that when dwLength used = file.GetLength() to calculate the number of total bytes for the bitmap it returned the total size of the file, not what you want of you are using serilization to save many CObjects into a single file. The ogigional code works great if you are using CDib::FileRead(CFile &file) to read a DIB file that only containes a single CDIB!

    However, my CDocument saves many CObjects into the same file, breaking the origional CDib::FileRead(CFile &file) code. I replaced the code to calculate the DIB size from the bmfHeader using the bfSize member var.

    Please let me know if this will be a problem....
    wbaldwin@oz.oznet.ksu.edu


    =======================================================================ORIGIONAL ISSUE

    CDib::FileRead(CFile &file), when used to read a DIB from disk works fine (with out using serilization).

    However, CDib::FileRead(CFile &file) fails when called from CDib::Serialize(CArchive& ar). Specifically,

    if (file.ReadHuge(m_pBits, dwLength-bmfHeader.bfOffBits) !=
    (dwLength - bmfHeader.bfOffBits))

    returns false when reading the bitmap bits. This is because the bytes actually read (the return value to ReadHuge()) is 41 bytes less than the expected number of bytes to read (dwLength - bmfHeader.bfOffBits).

    Thank you for providing your source, I learn more every time I read through it.


    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds