CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 11 of 11
  1. #1
    Join Date
    Jan 2005
    Location
    Akron, Ohio
    Posts
    670

    [RESOLVED] Loading bitmap without using MFC

    A solution I got from a different thread has opened Pandora's Box for me. Now I'm going crazy trying to figure out a problem I'm having.

    I have struggled to understand bitmaps for quite some time. Until now, I have heavily relied on MFC to do all the bitmap details, but now I want to understand more of what is happening. Specifically, I would like to create a function that loads a bitmap from a file using only C++ and the standard template library. Yes, I am aware this is reinventing the wheel, but until I understand what is happening with the load process, I will never be happy using code from another person or agency. I have tried finding this on the internet and through FAQs but have had marginal success in the process. If there is a place I should be looking, I would appreciate a pointer in the right direction.

    The current version of the function I have is as follows. Its aim is to load the bitmap and then pass it to a CBitmap object. I am aware that CBitmap is used by MFC, but until I get this version to work, I will be hesitant to take further steps away from MFC.

    Code:
    // in CDynamicBitmapCreationDlg.h
    class FileObject
    {
        FILE* m_pFile;
        public:
            FileObject( FILE *pFile ) : m_pFile( pFile ) {}
            ~FileObject() { if (m_pFile) fclose(m_pFile); }
    };
    
    
    // in CDynamicBitmapCreationDlg.cpp
    int CDynamicBitmapCreationDlg::load_bitmap_from_file(char* file, CBitmap* p_newbitmap)
    {
    	if(!p_newbitmap)
    	{
    		MessageBox("Error location 0");
    		return 0;
    	}
    
    	//First we open the file the binary mode
        FILE* in = fopen(file,"rb");
    	FileObject fObject( in );
    
    	//Next we read the BITMAPFILEHEAR. . .
    	BITMAPFILEHEADER bmfh;
    	size_t result = fread(&bmfh,sizeof(BITMAPFILEHEADER),1,in);
    	if(result < 1)
    	{
    		MessageBox("Error location 1");
    		return 0;
    	}
    
    	if(bmfh.bfType != 19778)
    	{
    		MessageBox("Error location 2");
    		return 0;
    	}
    
    	//Then the BITMAPINFOHEADER. . .
    	BITMAPINFOHEADER bmih;
    	result = fread(&bmih,sizeof(BITMAPINFOHEADER),1,in);
    	if(result < 1)
    	{
    		MessageBox("Error location 3");
    		return 0;
    	}
    
    	//Next comes the palette	
    	std::vector<RGBQUAD> colors;
    
    	//set the number of colours
    	int number_of_colors = 1 << bmih.biBitCount;
    
    	//load the palette if the bitmap has less than 24 bits per pixel
    	if(bmih.biBitCount < 24)
    	{
    		colors.resize(number_of_colors);
    		result = fread(&colors[0], sizeof(RGBQUAD), number_of_colors,in);
    		if(result < number_of_colors)
    		{
    			MessageBox("Error location 4");
    			return 0;
    		}
    	}
    
    	//Now we read in the pixel data
    	DWORD data_size = bmfh.bfSize - bmfh.bfOffBits;
    
    	std::vector<BYTE> p_initialdata(data_size);
    	result = fread(&p_initialdata[0],sizeof(BYTE),data_size,in);
    	if(result < data_size)
    	{
    		MessageBox("Error location 6");
    		return 0;
    	}
    
    	//The data has been read.  First, check if padding is needed
    
    	//byteWidth is the width of the actual image in bytes
    	//padWidth is the width of the image plus the extra padding
    	LONG width_in_bytes = 0;
    	LONG width_plus_padding = 0;
     
    	width_in_bytes = width_plus_padding = (LONG)((float)bmih.biWidth*(float)bmih.biBitCount/8.0);
     
    	//add any extra space to bring each line to a DWORD boundary
    	while(width_plus_padding % 4 != 0) 
    	{
    	   width_plus_padding++;
    	}
    
    	//Second, we transfer the data from the temporary buffer to the final buffer,
    	// adjusting for padding and inversion, if necessary
    
    	DWORD actual_size;
    	int offset;
     
    	//set diff to the actual image size(no padding)
    	actual_size = bmih.biHeight * width_in_bytes;
    	//allocate memory for the image
    
    	BYTE* p_finaldata = new BYTE[actual_size];
    	if(bmih.biHeight > 0) 
    	{
    		// The bitmap is inverted, so we'll need to reverse the image and remove the padding
    
    	    int j = data_size - 3;
    	    offset = width_plus_padding - width_in_bytes;
    	    for( int i = 0 ; i < data_size ; i += 3 ) 
    		{
    	        if( (i+offset) % width_plus_padding == 0 ) 
    			{
    	            i += offset;
    	        }
    			*(p_finaldata + j + 2) = p_initialdata[i];
    			*(p_finaldata + j + 1) = p_initialdata[i + 1];
    			*(p_finaldata + j) = p_initialdata[i + 2];
    	        j -= 3;
    	    }
    	}
    	else 
    	{
    		// The bitmap is not inverted.  We only need to remove the padding
    
    		LONG height = bmih.biHeight * -1;
    	    offset = 0;
    	    do 
    		{
    	        memcpy((p_finaldata+(offset*width_in_bytes)),(&p_initialdata[0] + (offset*width_plus_padding)),width_in_bytes);
    	        offset++;
    	    } while(offset < height);
    	}
    
    
    /****************************************/
    /* I BELIEVE THE PROBLEM IS HERE SOMEWHERE */
    /****************************************/
    
    	//Here we begin to construct the BITMAP object
    	BITMAP bmp;
    	bmp.bmBitsPixel = bmih.biBitCount;
    	bmp.bmHeight = bmih.biHeight;
    	bmp.bmWidth = bmih.biWidth;
    	bmp.bmType = 0; 
    	bmp.bmWidthBytes = width_in_bytes;
    	bmp.bmPlanes = bmih.biPlanes;
    	bmp.bmBits = p_finaldata;
    
    	if((p_newbitmap->CreateBitmapIndirect(&bmp)) == 0)
    	{
    		delete [] p_finaldata;
    		MessageBox("Error location 8");
    		return 0;
    	}
    
    	return 1;
    }
    As far as I know, the bitmap data is properly handled up until the BITMAP structure is filled in. The problem, I think, is with CreateBitmapIndirect. When I run it, the 'if' statement does not trigger, but the bitmap still doesn't show up in my dialog. I can load the same bitmap from file using LoadImage and it appears in the dialog, so I know that part works. Here is how the two implementations look in CDynamicBitmapDialog::OnInitDialog():

    Code:
    // THIS WORKS
    m_bitmap = (HBITMAP)::LoadImage(0 ,_T("C:\\4by4bitmap.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION );
    MainImage.ModifyStyle(0,SS_BITMAP);
    if(m_bitmap)
    	MainImage.SetBitmap(m_bitmap);
    versus. . .

    Code:
    // THIS DOES NOT WORK
    CBitmap temp_bitmap;
    load_bitmap_from_file(_T("C:\\4by4bitmap.bmp"),&temp_bitmap);
    m_bitmap = (HBITMAP)temp_bitmap;
    MainImage.ModifyStyle(0,SS_BITMAP);
    if(m_bitmap)
    	MainImage.SetBitmap(m_bitmap);
    (I comment out one method while I test the other)

    I have tried SetBitmapBits and that also does nothing.

    I would be very grateful to anyone able to help. I always try to leave good reputation feedback.

    The bitmap in question can be found here: http://www.codeguru.com/forum/showthread.php?t=514930
    error C2146a : syntax error : nebulizer stained in the tower floppy apple rider. Go rubble in flee smite. Bleeble snip snip.

    Documentation says: error C2146a - This means there is an error somewhere in the course of human endeavor. Fix in the usual way.

  2. #2
    Join Date
    May 2006
    Location
    Dresden, Germany
    Posts
    458

    Re: Loading bitmap without using MFC

    Hi,

    loading a bitmap from a file isn't an easy job. Please remember (or notice) that there are many internal formats like bit count etc.

    That's why try this:
    Code:
    BOOL LoadBitmap(CBitmap& bmp, LPCTSTR szFilename) 
    { 
        ASSERT(szFilename);
        bmp.DeleteObject();
    
        HBITMAP hBitmap = NULL; 
        hBitmap = (HBITMAP)LoadImage(NULL, szFilename, IMAGE_BITMAP, 0, 0, 
            LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE); 
        return bmp.Attach(hBitmap); 
    }
    For me it's working...

    Regards
    PA

  3. #3
    Join Date
    May 2006
    Location
    Dresden, Germany
    Posts
    458

    Re: Loading bitmap without using MFC

    It's again me

    Your code does not work for a couple of reasons:

    If you really want to make a (general working) bitmap loading function, you have to manage all combinations of sizes, bitcounts etc. Please refer to the MSDN, there are descriptions for the API functions and the used structures (BITMAPINFO and BITMAPINFOHEADER) as well. Also there are (partially and fully working) examples in the net.

    IMHO it does not make sense to write such a function of your own. Either it will work only for a subset of the possible formats (that will set you in trouble in future times) or you will have a really hard work to get one image loaded succesfully.

    Modern libraries and API's are capable to handle all the used formats.

    With regards
    PA
    Last edited by ProgramArtist; August 24th, 2011 at 04:17 AM. Reason: typo...

  4. #4
    Join Date
    Aug 2003
    Location
    Louisiana
    Posts
    72

    Re: Loading bitmap without using MFC

    Paradox,

    The documentation on CBItmap::CreateBitmapIndirect indicates that it takes a pointer to a BITMAP as the only argument, and you are indeed passing this call such a pointer.

    But the BITMAP structure shows that one of its members is bmWidthBytes which is basically the width of an image line in bytes INCLUDING pad bytes.

    As your code shows, you have a good understanding of the nature of the pad bytes being a rounding up of the number of bytes until it is divisible by 4.

    Since the CreateBitmapIndirect function is expecting an argument (BITMAP) which requires padbytes, I think one of the mistakes you are making is in stripping out the padbytes from your p_finaldata array.

    So I would suggest NOT stripping out the padbytes, and change your line

    bmp.bmWidthBytes = width_in_bytes to
    bmp.bmWidthBytes = width_plus_padding

    Another problem...

    The BITMAP structure has no provision for palette, so there is no way to make use of the RBQUAD info you so carefully extracted and set aside.
    I might have missed it at first glance, but I did not see in your loop where you were making any use of or referencing the colors array you had created.

    It would seem that in your loop to fill p_finaldata you should look at the following changes...

    Ignore the effort to strip the padding, since CreateBitmapIndirect seems to be expecting it anyway.

    If the bitsperpixel equals 24, then there should be no palette and you should be able to use the same pixel info as from the loaded image.

    Something that puzzles me is the ability of CreateBitmapIndirect to create a bitmap without a palette array.

    If the bmBitsPixel member of BITMAP is set to 8, for example, and every byte represents one of 256 colors which could be 24 bits each, how does the function know what the color is without the palette?

    Skeet

  5. #5
    Join Date
    Jan 2005
    Location
    Akron, Ohio
    Posts
    670

    Re: Loading bitmap without using MFC

    Thank you for your replies, both of you.

    That is interesting food for thought. I have an updated version of the function that produces a BITMAP structure that is drawn well in an OpenGL window, so I know the algorithm works, but perhaps you are right about the padding. Perhaps I need a separate version that keeps the padding if I want to populate a CBitmap with the data. I'll give it a shot.

    I'm not sure about the palette. I extracted it because some bitmap files have it. I'll need to look into it.

    Thanks for the suggestion!
    error C2146a : syntax error : nebulizer stained in the tower floppy apple rider. Go rubble in flee smite. Bleeble snip snip.

    Documentation says: error C2146a - This means there is an error somewhere in the course of human endeavor. Fix in the usual way.

  6. #6
    Join Date
    Jan 2005
    Location
    Akron, Ohio
    Posts
    670

    Re: Loading bitmap without using MFC

    I tried that change, but it ultimately didn't work. The comment made about the color scheme not being used did push me in the right direction, though. The reason it wasn't working was because CBitmap is device dependent and uses the color scheme of the device context it is compatible with. This caused me to do a great deal of research into what DDB and DIB meant.

    I switched the code so that the CBitmap was being created as compatible to a device context, only to find the image showing up as junk. Then I switched the pixel format to 32 bit, and it showed up finally.

    My function loads a bitmap into 24 bit, no alpha channel. Apparently, the device context in question uses 32 bit, so I added an alpha channel and all is well.

    I now understand bitmaps much better. That's what you get when you learn code from the School of Hard Knocks.

    Only one question remains for me:
    Is the 32 bit scheme a system setting? How can you find out in advance if a device context is using a 32 bit color scheme?

    Ok, maybe that was two questions.

    (Thanks for your help guys!)
    error C2146a : syntax error : nebulizer stained in the tower floppy apple rider. Go rubble in flee smite. Bleeble snip snip.

    Documentation says: error C2146a - This means there is an error somewhere in the course of human endeavor. Fix in the usual way.

  7. #7
    Join Date
    Dec 2009
    Posts
    145

    Re: Loading bitmap without using MFC

    Seriously, I have had a headache to follow your posts. Your thread title states that you don't use MFC, but your main function reuses CBitmap.
    As I understand,
    Code:
     
          GDI+ ---> CImage
               \
                CBitmap
    CImage and CBitmap can be interchangeable, CBitmap and GDI+ can also be exchanged theirs handles via Bitmap and Image. LoadImage or LoadBitmap as win32API can load all type of images just fine. There is nothing like "the system sets 32 bit scheme for a color image" because you adjust the BPP yourself doesn't necessarily mean your image quality will be improved. This value is used as a parameter of whatever number when Create function is called,

    PHP Code:
    CImage img;
    if(
    SUCCEEDED(img.Load(filepath))
    {
          
    CImage cpy;
          
    cpy.Create(img.getWidth(),img.GetHight(),your-favourite-number-test);
          
    img.Draw(-copy-your-image-into-cpy);


  8. #8
    Join Date
    Jan 2005
    Location
    Akron, Ohio
    Posts
    670

    Re: Loading bitmap without using MFC

    My goal is not to cause headaches. That said, it frustrates me when people are not patient with my questions. I don't always know the most efficient way to ask them.

    In regards to your confusion: I stated in my first post: ". . . I am aware that CBitmap is used by MFC, but until I get this version to work, I will be hesitant to take further steps away from MFC."

    Until recently, the only machinery I had to display a bitmap was MFC. So, in order to test any function, I had to interface it with MFC to display it. That meant that either the function wasn't working, or my attempts to appease MFC weren't working. Having found an altered version of the function works, I just wanted to figure out what the original problem was. I'm obsessive about coding and understanding what's going on.

    Either way, it's ok. The solution was found. Thank you for your help.
    error C2146a : syntax error : nebulizer stained in the tower floppy apple rider. Go rubble in flee smite. Bleeble snip snip.

    Documentation says: error C2146a - This means there is an error somewhere in the course of human endeavor. Fix in the usual way.

  9. #9
    Join Date
    May 2006
    Location
    Dresden, Germany
    Posts
    458

    Re: Loading bitmap without using MFC

    Quote Originally Posted by paradoxresolved View Post
    Only one question remains for me:
    Is the 32 bit scheme a system setting? How can you find out in advance if a device context is using a 32 bit color scheme?
    Use the GetDeviceCaps function.

  10. #10
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Loading bitmap without using MFC

    Quote Originally Posted by paradoxresolved View Post
    My goal is not to cause headaches. That said, it frustrates me when people are not patient with my questions. I don't always know the most efficient way to ask them.

    In regards to your confusion: I stated in my first post: ". . . I am aware that CBitmap is used by MFC, but until I get this version to work, I will be hesitant to take further steps away from MFC."

    Until recently, the only machinery I had to display a bitmap was MFC. So, in order to test any function, I had to interface it with MFC to display it. That meant that either the function wasn't working, or my attempts to appease MFC weren't working. Having found an altered version of the function works, I just wanted to figure out what the original problem was. I'm obsessive about coding and understanding what's going on.

    Either way, it's ok. The solution was found. Thank you for your help.
    You know - the MFC sources are available for you to step through and figure things out. Just install them when you install Visual C++ (or reinstall and click the check box to add the sources).

    I've found them to be a great aide whenever I want to know what's going on under the covers (or when I need to reproduce some functionality that MFC is doing without using MFC).

  11. #11
    Join Date
    Nov 2000
    Location
    Voronezh, Russia
    Posts
    6,633

    Re: [RESOLVED] Loading bitmap without using MFC

    Until recently, the only machinery I had to display a bitmap was MFC.
    All the time I wondered, why MFC is constantly mentioned in the context of this fairly simple business of displaying bitmap while seemingly having nothing to do with the essence of the issue. Now I get it. Bad for you, as plain WinAPI GDI (even not GDI+!) allows you to deal with bitmaps the same perfectly fine, and MFC adds really not much on top of the latter.

    until I get this version to work, I will be hesitant to take further steps away from MFC
    Your original issue was LoadImage API (not MFC stuff, but plain WinAPI) not working on some reason for you. Considering the said above, you're going to re-invent every particular wheel in Windows world that would occur to fail for you, aren't you?
    Last edited by Igor Vartanov; August 26th, 2011 at 11:39 PM.
    Best regards,
    Igor

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured