• Advertisement
Sign in to follow this  

Manually Load a BMP into a HBITMAP

This topic is 4740 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I found an aricle on here and a forum dedicated to the article. Lots of revisions of the code were made in the dicussion but I cannot get either the original or the problems fixed by all the replies. This is my source and header. Everything checks out except when I try a BitBlt call to the m_hdc I get nothing. Also, when I exit my program and the class destructor is called it cleans up the allocated byte array of the bitmap I get a heap block error so something is messin up.
#pragma once
#ifndef __BITMAPFILE
#define __BITMAPFILE

#define  WIN32_LEAN_AND_MEAN
#include <Windows.h> 

#include <memory>
#include <cstdio>

#define BITMAP_ID     0x4D42
#define RGB_BYTE_SIZE 3

class CBITMAP
{
public:
    //void far          *m_pBitmap;
    char              *m_pBitmap;
    BITMAPFILEHEADER   m_FileHeader;
    BITMAPINFOHEADER   m_InfoHeader;
    HDC                m_hdc;
    HBITMAP            m_hbitmap;

    int                m_iNumColours;
    RGBQUAD           *m_pColTable;

    CBITMAP():
      m_pBitmap(0), m_hbitmap(0), m_hdc(0),
      m_iNumColours(0), m_pColTable(0) {        //Constructor
          memset(&m_FileHeader, 0, sizeof(m_FileHeader));
          memset(&m_InfoHeader, 0, sizeof(m_InfoHeader));
    }

    ~CBITMAP() {             //Destructor
        Unload();
    }

    bool CreateRawBitmap(char *filename);        //Load a bitmapfile
    bool Convert24(char* tempData, int iPadWidth, int iByteWidth);  //Turns a char* array in a 24 bitmap
    bool CreateDIB();                            //Turn the file into a DIB

    DWORD Width() {         //Get Width
            return m_InfoHeader.biWidth;
    }

    DWORD Height() {        //Get Height
            return m_InfoHeader.biHeight;
    }


    void Unload() {
        if (m_pBitmap) {
            delete [] m_pBitmap;
        }

        if (m_hbitmap) {
	        DeleteObject(m_hbitmap);
        }

        if (m_pColTable) {
            delete [] m_pColTable;
        }

        m_pBitmap       = 0;
        m_hdc           = 0;
        m_hbitmap       = 0;

        m_iNumColours   = 0;
        m_pColTable     = 0;

        memset(&m_FileHeader, 0, sizeof(m_FileHeader));
        memset(&m_InfoHeader, 0, sizeof(m_InfoHeader));
    }
};

#endif

#include "../Header/cls_Bitmap.h"


//---------------------
// Parse a bitmap into a 32 bit unsigned char* array.
// Last Modified: Febuary 28, 2005 - 23:57 - Halsafar
//---------------------
bool CBITMAP::CreateRawBitmap(char* cFilename)
{
    FILE *pFile = 0;

    //Open file for input
    pFile = fopen(cFilename, "rb");
    if (pFile == NULL) {
       return false;
    }

   //Read the fileheader
    fread(&m_FileHeader, sizeof(BITMAPFILEHEADER), 1, pFile);
    if(m_FileHeader.bfType != BITMAP_ID) {
        fclose(pFile);
        return false;
    }

    //read the info header
    fread(&m_InfoHeader, sizeof(BITMAPINFOHEADER), 1, pFile);

    //load the palette for 8 bits per pixel
    if(m_InfoHeader.biBitCount == 8) {
        m_iNumColours = 1 << m_InfoHeader.biBitCount;
        m_pColTable = new RGBQUAD[m_iNumColours];
        fread(m_pColTable, sizeof(RGBQUAD), m_iNumColours, pFile);
    }

    DWORD dwSize = 0;

    dwSize = (m_InfoHeader.biWidth * m_InfoHeader.biHeight * (unsigned int)(m_InfoHeader.biBitCount / 8.0) );
    //dwSize = m_FileHeader.bfSize - m_FileHeader.bfOffBits;

    char *pTempData = 0;
    pTempData = new char[dwSize];

    if(pTempData == NULL) {
        fclose(pFile);
        return false;
    }

    fread(pTempData, sizeof(char), dwSize, pFile);
    fclose(pFile);

    int byteWidth = 0;
    int padWidth = 0;

    byteWidth = padWidth = (int)( (float)m_InfoHeader.biWidth * (float)m_InfoHeader.biBitCount / 8.0);

    while(padWidth % 4 != 0) {      //Adjust pad width as necessary
		padWidth++;
    }

    if (m_InfoHeader.biBitCount >= 24) {
        Convert24(pTempData, padWidth, byteWidth);
    }

    delete [] pTempData;
    return true;
}
	


//---------------------
//Converts a bitmap array in a 24 - 32 bit unpadded array
// Last Modified: Febuary 28, 2005 - 23:57 - Halsafar
//---------------------
bool CBITMAP::Convert24(char* tempData, int iPadWidth, int iByteWidth) 
{
	int offset      = 0;						// padding between data
	int dataSize    = 0;						// the size of the bitmap data

	dataSize = abs(m_InfoHeader.biWidth * m_InfoHeader.biHeight * RGB_BYTE_SIZE);
    
    m_pBitmap = new char[dataSize];			// allocate the buffer for the final image data
    if(m_pBitmap == NULL) {
        delete [] m_pBitmap;
        return false;
    }

    if(m_InfoHeader.biHeight > 0) {
        offset = iPadWidth - iByteWidth;			// find the difference
        
        for(int i = 0; i < dataSize; i += 3) {  // count backwards until at front of image
            if((i+1) % iPadWidth == 0) {		// jump over the padding
				i += offset;
            }
            *(m_pBitmap+i+2) = *(tempData+i);        // transfer the data in reverse order
            *(m_pBitmap+i+1) = *(tempData+i+1);		// in 3-color blocks of RGB
            *(m_pBitmap+i) = *(tempData+i+2);
        }
    } else {									// image parser for a forward image
        offset = iPadWidth - iByteWidth;
        int j = dataSize - 3;

        for(int i = 0; i < dataSize; i += 3, j -= 3) {
            if((i+1) % iPadWidth == 0) {		// jump over padding
                i += offset;
            }
            *(m_pBitmap+j+2) = *(tempData+i);		// transfer the data ||NOTE||
            *(m_pBitmap+j+1) = *(tempData+i+1);		// that final data is stored backwards now
            *(m_pBitmap+j) = *(tempData+i+2);		// due to the difference of i and j
        }
    }

    return true;
}


//---------------------
//Turn this class into a DIB section
// Last Modified: Febuary 28, 2005 - 23:57 - Halsafar
//---------------------
bool CBITMAP::CreateDIB()
{
    //Do some error checks
    if (m_pBitmap == 0) {
        return false;
    } else if (m_FileHeader.bfType !=0x4D42) {
        return false;
    }


    int nColors = 0;
    if (m_InfoHeader.biBitCount < 16) {  //We have a grayscale
        if(m_InfoHeader.biBitCount == 8) {
            nColors= 256;
        } else if (m_InfoHeader.biBitCount == 4) {
            nColors= 16;
        } else if (m_InfoHeader.biBitCount == 1) {
            nColors= 2;
        }

        m_InfoHeader.biSizeImage = m_InfoHeader.biWidth * m_InfoHeader.biHeight * m_InfoHeader.biBitCount;
        m_InfoHeader.biClrUsed= nColors;
    }

    int iInfoSz = sizeof(BITMAPINFOHEADER);
    BITMAPINFO *Bi = (BITMAPINFO*) new unsigned char[iInfoSz + 
                                    (m_InfoHeader.biBitCount < 16 ? nColors*sizeof(RGBQUAD) : 0)];
    //BITMAPINFO Bi;
    memset(&Bi->bmiHeader, 0, iInfoSz);

	Bi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	Bi->bmiHeader.biWidth = m_InfoHeader.biWidth;
	Bi->bmiHeader.biHeight = -m_InfoHeader.biHeight;
	Bi->bmiHeader.biPlanes = 1;
	Bi->bmiHeader.biBitCount = m_InfoHeader.biBitCount;
	Bi->bmiHeader.biCompression = BI_RGB;
	Bi->bmiHeader.biSizeImage = 0;
	Bi->bmiHeader.biClrUsed = 0;
	Bi->bmiHeader.biClrImportant = 0;

    HDC hdc = GetDC(NULL); 
    HBITMAP m_hbitmap = CreateDIBSection(hdc, Bi, DIB_PAL_COLORS, (void**)&m_pBitmap, 0, 0);
    /*if(bpp < 16 && palTable && nColors && hBmp)
    {
        aDC imDC(hdc, hBmp);
        ::SetDIBColorTable(imDC.hDC, 0, nColors, (RGBQUAD*)palTable);
    }*/
    ReleaseDC(0, hdc);

	BITMAP bmp;
	memset(&bmp, 0, sizeof(bmp));
	GetObject(m_hbitmap, sizeof(BITMAP), &bmp);

	m_hdc = CreateCompatibleDC(NULL);
    if (NULL == m_hdc) {
        return false;
    }
	
	SelectObject(m_hdc, m_hbitmap);
	DeleteObject(m_hbitmap);
    return true;
}

Share this post


Link to post
Share on other sites
Advertisement
//load the palette for 8 bits per pixel
if(m_InfoHeader.biBitCount == 8) {
m_iNumColours = 1 << m_InfoHeader.biBitCount;
m_pColTable = new RGBQUAD[m_iNumColours];
fread(m_pColTable, sizeof(RGBQUAD), m_iNumColours, pFile);
}





First thing I noticed is this. In an 8 bit bitmap there will always be 256 entries in the color table and the number in the header might not always be set.
In my experience the only reliable numbers are the ID, width, height and bpp.

dwSize = (m_InfoHeader.biWidth * m_InfoHeader.biHeight * (unsigned int)(m_InfoHeader.biBitCount / 8.0) );





this will break for 4bpp and 2bpp bitmaps (dunno if you're trying to read them?)


byteWidth = padWidth = (int)( (float)m_InfoHeader.biWidth * (float)m_InfoHeader.biBitCount / 8.0);

while(padWidth % 4 != 0) { //Adjust pad width as necessary
padWidth++;
}





calculate padding using (4 - (width % 4)) % 4, each scan line has to line up on a 4 byte boundry when converted to bytes (why? i dont know). That works for all bpps though since lines are padded with pixels.

I didnt see this in your code so if it is I apologize. In an 8bit bmp each byte after the color table is a reference to an index in the color table that gives the RGB values of that pixel (in the order BGR).

Hm, after looking at the rest... I'd suggest breaking it up into smaller functions where each one does a very specific thing you can easily test. The bitmap spec is pretty darn hard to follow and you've definitely made some good progress. Dealing with 4bpp and 2bpp images with padding can be tricky too, I'd say get 24 and 8 working first (you're much more likely to use those anyway).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement