Thread: Loading and Displaying Other Format Images (than bmp)

  1. #1
    Registered User
    Join Date
    Sep 2010
    Posts
    11

    Loading and Displaying Other Format Images (than bmp)

    I am making a game in Win32 C++, however, I have ran into a problem.

    So far I have 4 background images of levels, saved as .bmps, and it is adding about 8mbs to my program. That is way too much if this game is going to have any more levels.

    So I am wondering, what is the easiest way I could display a .jpg image? Being able to still preferably use the HDC.

    I have googled, and the closest thing I found was a picture component that is capable of displaying .jpgs, which might be the last resort...

  2. #2
    Registered User Joelito's Avatar
    Join Date
    Mar 2005
    Location
    Tijuana, BC, México
    Posts
    310
    GDI+ can open, png, gif, jpeg, tiff and some other...
    or..try CImage
    * PC: Intel Core 2 DUO E6550 @ 2.33 GHz with 2 GB RAM: Archlinux-i686 with xfce4.
    * Laptop: Intel Core 2 DUO T6600 @ 2.20 GHz with 4 GB RAM: Archlinux-x86-64 with xfce4.

  3. #3
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Something like....

    Free any current image.

    Use OLELoadPicture() to load the .jpg

    Create a memory DC and bitmap (using the screen and the image size as params)

    Use the LPPICTURE Render() method to copy the jpg image to the memory DC's bitmap.

    Release the LPPICTURE.

    Use the memory DC to draw the image as required
    OR
    SelectObject() the default BMP back into the memory DC and free the memory DC (saving the image in the HBITMAP object).

    Clean up the memory DC when the app closes.

    Example code: http://www.codeproject.com/KB/graphics/render.aspx
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  4. #4
    Registered User
    Join Date
    Sep 2010
    Posts
    11
    Thanks for the replies. I wrote up the function, but it isn't working. Fail pops up, and I am almost sure it is because my resource file is wrong. My function looks like this:

    Code:
    HBITMAP loadResourcePicture(int resid) {
        
        // variables
        HBITMAP hBitmap;
        DWORD dwFileSize;
        //ORD dwBytesRead;
        //HANDLE hFile;
        LPPICTURE gpPicture;
        LPVOID lpPicData;
        LPVOID pvData;
        LPSTREAM pstm;
        LPVOID hrlp;
        HGLOBAL hGlobal;
        HINSTANCE hInst = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);
        HDC hMem = CreateCompatibleDC(GetDC(hwnd));
        OLE_XSIZE_HIMETRIC *width;
        OLE_YSIZE_HIMETRIC *height;
    
        // find resource
        hrlp = FindResource(hInst, MAKEINTRESOURCE(resid), RT_RCDATA);
        
        // make sure it found something
        if (hrlp != 0) {
        dwFileSize = SizeofResource(hInst, (HRSRC)hrlp);
        hGlobal = LoadResource(hInst, (HRSRC)hrlp);
        
            // something was loaded
            if (hGlobal != 0) {
                lpPicData = LockResource(hGlobal);
                pvData = NULL;
                hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
                pvData = GlobalLock(hGlobal);
                CopyMemory(pvData, lpPicData, dwFileSize);
                GlobalUnlock(hGlobal);
                
                // create stream and load picture from it
                CreateStreamOnHGlobal(hGlobal, TRUE, &pstm);
                OleLoadPicture(pstm, 0, FALSE, IID_IPicture, (LPVOID*)&gpPicture);
                pstm->Release();
                
                // get picture
                gpPicture->get_Width(width);
                gpPicture->get_Height(height);
                gpPicture->Render(hMem, 0, 0, (int)width, (int)height, 0, 0, (int)width, (int)height, NULL);
                HBITMAP oneNew;
                hBitmap = (HBITMAP)SelectObject(hMem, oneNew); // for now
                
                // release picture
                gpPicture->Release();
                
                // return
                return hBitmap;
            }
        }
        
        MessageBox(hwnd, "Fail", NULL, MB_OK);
    }
    and my incorrect resource file looks like:

    Code:
    #define JPG_BACKGROUND 50000
    JPG_BACKGROUND0       JPG DISCARDABLE "http://cboard.cprogramming.com/images/bg_level1.jpg" // my image isnt on the web the url was added automatically lol
    I know almost for a fact the JPG_ prefix is wrong, and the JPG is probably also wrong. Any idea on the correct way to set it up?

  5. #5
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    You can call your resources almost anything you like (unless it collides with some name the IDE is using, which should generate an error).

    Only a quick look....

    By default the created DC contains a 1x1 black and white bitmap (which will not hold much of your image). You need a new BMP the same size as the client area.

    CreateCompatibleBitmap() [using hMem and the client area] and SelectObject() [catching the returned default HBITMAP] this HBITMAP into the hMem DC.

    Call Render() to draw on the new HBITMAP.

    Clean up by SelectObject() the default HBITMAP back into the hMem DC (which will 'push' out the created one you want to return) before using DeleteDC() the hMem DC.

    Code:
    //is this a typo in your code as well? (because these resource names don't match) 
    #define JPG_BACKGROUND 50000
    JPG_BACKGROUND0       
    
    //this is a GDI memory leak
    HDC hMem = CreateCompatibleDC(GetDC(hwnd)); 
    
    You should ReleaseDC() and DC you 'get' (and delete any you 'create').


    Narrow down where the error occurs by adding msgs to tell you/me what error occurred (not just that an error occurred).

    Use breakpoints and GetLastError() to diagnose the problem.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  6. #6
    Registered User
    Join Date
    Sep 2010
    Posts
    11
    Thanks again for the reply.

    Alright, it looks better now, and I checked quite a few of the return values, and everything looks good... but when the background is displayed its completely red, and turns white after about a minute. I don't know what that is about since the bitmap is loaded once and doesn't change.

    The one thing I know isn't right is the width and height, but the ratio looks correct... so I think theres a conversion I need for that. I think I still might be using Render wrong though, because the example I looked at has it setup like:
    Code:
    gpPicture->Render(pDC->m_hDC,
                              ptUL.x,
                              ptUL.y,
                              nWidth,
                              -nHeight,
                              0,
                              hmHeight,
                              hmWidth,
                              -hmHeight,
                              &rc);
    The &rc can be NULL, but they use odd positioning, and the - before height doesn't even seem valid.

    I looked at msdn and mine looks correct...

    any idea, or as to what I should debug further?

    Code:
    HBITMAP loadResourcePicture(int resid) {
        
        // variables
        HBITMAP hBitmap;
        HBITMAP hDefaultBmp;
        DWORD dwFileSize;
        LPPICTURE gpPicture;
        LPVOID lpPicData;
        LPVOID pvData;
        LPSTREAM pstm;
        HRSRC hrlp;
        HGLOBAL hGlobal;
        HDC hdc;
        HDC hMem;
        HINSTANCE hInst = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);
        long width;
        long height;
        
        // find resource
        hrlp = FindResource(hInst, MAKEINTRESOURCE(resid), RT_RCDATA);
        
        // make sure it found something
        if (hrlp != NULL) {
            dwFileSize = SizeofResource(hInst, hrlp);
            hGlobal = LoadResource(hInst, hrlp);
            
            // something was loaded
            if (hGlobal != 0) {
                lpPicData = LockResource(hGlobal);
                pvData = NULL;
                hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
                pvData = GlobalLock(hGlobal);
                CopyMemory(pvData, lpPicData, dwFileSize);
                GlobalUnlock(hGlobal);
                
                // create stream and load picture from it
                CreateStreamOnHGlobal(hGlobal, TRUE, &pstm); // returns s_ok
                OleLoadPicture(pstm, 0, FALSE, IID_IPicture, (LPVOID*)&gpPicture); // returns s_ok
                pstm->Release();
                
                // get picture
                hdc = GetDC(hwnd);
                hMem = CreateCompatibleDC(hdc);
                gpPicture->get_Width(&width); // returns around 40000 compared to 1500
                gpPicture->get_Height(&height); // returns around 12000 compared to 464
                width = 1500;
                height = 464;
                gpPicture->Render(hMem, 0, 0, width, height, 0, height, width, -1*height, NULL); // returns s_ok
                //hBitmap = CreateCompatibleBitmap(hMem, width, height);
                hDefaultBmp = CreateCompatibleBitmap(hMem, width, height);
                hBitmap = (HBITMAP)SelectObject(hMem, hDefaultBmp); // for now
                //hBitmap = (HBITMAP)GetCurrentObject(hMem, OBJ_BITMAP);  
                ReleaseDC(hwnd, hdc);
                
                // release picture
                gpPicture->Release();
                
                // delete dcs
                DeleteDC(hdc);
                DeleteDC(hMem);
                
                // return
                return hBitmap;
            }
    
        }
        
        MessageBox(hwnd, "Fail", NULL, MB_OK);
    }
    EDIT: Render should be fixed, and still nothing. I drew a rectangle to hMem right after the render, and still nothing. So I am guessing the HBITMAP is not being returned correctly...
    Last edited by Brokenhope; 10-07-2010 at 01:00 PM.

  7. #7
    Registered User
    Join Date
    Sep 2010
    Posts
    11
    I am now pretty sure the problem is in returning the HBITMAP.

    I did some more testing trying to draw a rectangle to hMem, and still nothing displays.

    I can also say the HBITMAP is not null...

    any ideas?

  8. #8
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    In the example they are trying to center the image (by modifing the starting positions).


    You are not creating the bitmap at the right time.

    Code:
    // get picture
    hdc = GetDC(hwnd);
    hMem = CreateCompatibleDC(hdc);
    
    //get the size of the client area
    rect rcClient = {0};
    GetClientRect(hwnd, &rcClient);
    int ClientWidth = rcClient.right-rcClient.left;
     int ClientHeight = rcClient.bottom-rcClient.top;
    //create our bitmap
    hBitmap = CreateCompatibleBitmap(hMem, ClientWidth, ClientHeight);   
    //select the bitmap into the DC      
    hDefaultBmp = (HBITMAP)SelectObject(hMem, hBitmap ); 
    
    //find the image size
    gpPicture->get_Width(&width); 
    gpPicture->get_Height(&height); 
    
    //render to our BMP
    gpPicture->Render(hMem, 0, 0, ClientWidth, ClientHeight , 0, 0, width, height, NULL);//or &rcClient 
    
    //clean up
    
    SelectObject(hMem, hDefaultBmp); // put the created mem DC back to default before deleteing it  (and 'push out' our HBITMAP)        
    
    ReleaseDC(hwnd, hdc); //do not DeleteDC this as well
                
    // release picture
    gpPicture->Release();
    DeleteDC(hMem);           
                      
     // return
     return hBitmap;
    If you want to convert from HIMETRIC to pixels (and not use the client rect area).

    Code:
    gpPicture->get_Width(&hmWidth); 
    gpPicture->get_Height(&hmHeight); 
    
    // convert himetric to pixels 
    int nWidth = MulDiv(hmWidth, GetDeviceCaps(hdc, LOGPIXELSX), HIMETRIC_INCH); 
    int nHeight = MulDiv(hmHeight, GetDeviceCaps(hdc, LOGPIXELSY), HIMETRIC_INCH);

    EDIT:
    As a HBITAMP is a void* you should be able to return a local pointer (and not create a dangling pointer).

    You can check the image with

    Code:
    BITMAP bmp={0};
    GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bmp);
    
    //what are the dimensions (bmp.bmWidth etc), are they the expected size?
    Also if you are only loading one image in the life of the app you could return the HDC (declare it with enough scope ie global to the window).

    Then use the HDC to BitBlt() the image to the screen in your WM_PAINT (ie BitBlt() to the HDC in the PAINTSTRUCT using the rect in the PAINTSTRUCT )

    NOTE: not complied let alone tested (just off the top of my head...)
    Last edited by novacain; 10-07-2010 at 11:07 PM.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  9. #9
    Registered User
    Join Date
    Sep 2010
    Posts
    11
    Thanks a lot for the help.

    It is now working.

    Function:
    Code:
    HBITMAP loadResourcePicture(int resid) {
        
        // variables
        HBITMAP hBitmap;
        DWORD dwFileSize;
        LPPICTURE gpPicture;
        LPVOID lpPicData;
        LPVOID pvData;
        LPSTREAM pstm;
        HRSRC hrlp;
        HGLOBAL hGlobal;
        HDC hdc;
        HDC hMem;
        HINSTANCE hInst = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);
        long width;
        long height;
        
        // find resource
        hrlp = FindResource(hInst, MAKEINTRESOURCE(resid), RT_RCDATA);
        
        // make sure it found something
        if (hrlp != NULL) {
            dwFileSize = SizeofResource(hInst, hrlp);
            hGlobal = LoadResource(hInst, hrlp);
            
            // something was loaded
            if (hGlobal != 0) {
                lpPicData = LockResource(hGlobal);
                pvData = NULL;
                hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
                pvData = GlobalLock(hGlobal);
                CopyMemory(pvData, lpPicData, dwFileSize);
                GlobalUnlock(hGlobal);
                
                // create stream and load picture from it
                CreateStreamOnHGlobal(hGlobal, TRUE, &pstm); // returns s_ok
                OleLoadPicture(pstm, 0, TRUE, IID_IPicture, (LPVOID*)&gpPicture); // returns s_ok
                pstm->Release();
                
                // get picture
                hdc = GetDC(hwnd);
                hMem = CreateCompatibleDC(hdc);
                gpPicture->get_Width(&width); // returns around 40000 compared to 1500
                gpPicture->get_Height(&height); // returns around 12000 compared to 464
                int nPixX = GetDeviceCaps(hMem, LOGPIXELSX);
                int nPixY = GetDeviceCaps(hMem, LOGPIXELSY);
                int realWidth = MulDiv(width, nPixX, 2540); //1500 // 2540 is HIMETRIC_INCH
                int realHeight = MulDiv(height, nPixY, 2540); //464
                SelectObject(hMem, hBitmap);
                gpPicture->Render(hMem, 0, 0, realWidth, realHeight, 0, height, width, -1*height, NULL); // returns s_ok
                hBitmap = (HBITMAP)GetCurrentObject(hMem, OBJ_BITMAP);  
                ReleaseDC(hwnd, hdc);
                
                // release picture
                gpPicture->Release();
                
                // delete dcs
                //DeleteDC(hdc);
                DeleteDC(hMem);
                
                // return
                return hBitmap;
            }
    
        }
        
        MessageBox(hwnd, "Failed Loading Picture", NULL, MB_OK);
    }
    resource
    Code:
    IDR_BACKGROUND1       RCDATA     "images/bg_level1.jpg"

  10. #10
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    You can do it with faffing with HDC's:

    Code:
    // keep everything above here
    OleLoadPicture(pstm, 0, TRUE, IID_IPicture, (LPVOID*)&gpPicture); // returns s_ok
    HBITMAP hBmCur = NULL;
    gpPicture->get_Handle((OLE_HANDLE*)&hBmCur);
    assert(GetObjectType((HGDIOBJ)hBmCur) == OBJ_BITMAP);
    HBITMAP hBitmap = (HBITMAP)CopyImage((HBITMAP)hBmCur, IMAGE_BITMAP, 0, 0, 0);
    gpPicture->Release();
    return hBitmap;

  11. #11
    Registered User
    Join Date
    Sep 2010
    Posts
    11
    Heh, that would have really simplified it. I think I have to try it now though, anyways.

    For some totally obscure reason, the function only works when I call it in my LoadImages() function, which loads all the images the game uses (bitmaps, loaded with a different function).

    I tried to use the function before the level started (LevelInit()) to get the background bitmap, and it then returns a 1 x 1 bitmap. I played around with the function iand tried using the SelectObject to get the bitmap instead, and got the same thing.

    Makes absolutely no sense... I looked through the code called between LoadImages() and LevelInit() and nothing remotely looks like it would cause a problem... or even messes with that stuff. Unless the fact that LoadImages() was called just after the window was created, and LevelInit() is called after stuff has been displayed on the screen is causing a problem...

    In which case that is beyond a major problem, I would have to load all the .jpgs from resource into bitmaps which would make the program use a tremendous amount of memory.

    Oh, and I tried using my LoadBitmap function in LevelInit() and it returned and loaded the bitmap correctly (just uses LoadImage()).

    Any ideas?

  12. #12
    Registered User
    Join Date
    Sep 2010
    Posts
    11
    Wow, thanks a lot for the replies. I was really doubting this would work, but with the change above it is now working as it should:

    Code:
    HBITMAP loadResourcePicture(int resid) {
        
        // variables
        HBITMAP hBitmap;
        DWORD dwFileSize;
        LPPICTURE gpPicture;
        LPVOID lpPicData;
        LPVOID pvData;
        LPSTREAM pstm;
        HRSRC hrlp;
        HGLOBAL hGlobal;
        //HDC hdc;
        //HDC hMem;
        HINSTANCE hInst = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);
        long width;
        long height;
        
        // find resource
        hrlp = FindResource(hInst, MAKEINTRESOURCE(resid), RT_RCDATA);
        
        // make sure it found something
        if (hrlp != NULL) {
            dwFileSize = SizeofResource(hInst, hrlp);
            hGlobal = LoadResource(hInst, hrlp);
            
            // something was loaded
            if (hGlobal != 0) {
                lpPicData = LockResource(hGlobal);
                pvData = NULL;
                hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
                pvData = GlobalLock(hGlobal);
                CopyMemory(pvData, lpPicData, dwFileSize);
                GlobalUnlock(hGlobal);
                
                // create stream and load picture from it
                CreateStreamOnHGlobal(hGlobal, TRUE, &pstm); // returns s_ok
                
                // keep everything above here
                OleLoadPicture(pstm, 0, TRUE, IID_IPicture, (LPVOID*)&gpPicture); // returns s_ok
                HBITMAP hBmCur = NULL;
                gpPicture->get_Handle((OLE_HANDLE*)&hBmCur);
                hBitmap = (HBITMAP)CopyImage((HBITMAP)hBmCur, IMAGE_BITMAP, 0, 0, 0);
                gpPicture->Release();
                return hBitmap;
    
            }
    
        }
        
        MessageBox(hwnd, "Failed Loading Picture", NULL, MB_OK);
    }
    could use more error checking, but it works fine.

    Found another problem, commented out the unused HDCs, which should have been gone anyways, but ended up causing a problem with other bitmaps loading with the LoadImage() function started to load improperly, and caused things associated with the bitmaps to act really weird.
    Last edited by Brokenhope; 10-08-2010 at 03:06 PM.

  13. #13
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Quote Originally Posted by adeyblue View Post
    You can do it with faffing with HDC's:
    I always just leave the image in a HDC for as long as I need it.

    Quote Originally Posted by Brokenhope View Post
    Code:
    DeleteDC(hMem); //you just deleted the HDC containing the hBitmap (and so probably deleted hBitmap) the delete may fail however, as you have not retunred the HDC to the default state
                
    return hBitmap;//this is probably now a dangling pointer
    You must also use DeleteObject() on the HBITMAP before loading another.

    Quote Originally Posted by Brokenhope View Post
    Found another problem, commented out the unused HDCs, which should have been gone anyways, but ended up causing a problem with other bitmaps loading with the LoadImage() function started to load improperly, and caused things associated with the bitmaps to act really weird.
    Sounds like a GDI memory leak.
    Last edited by novacain; 10-09-2010 at 03:16 AM.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  14. #14
    Registered User
    Join Date
    Jan 2011
    Location
    Venezuela
    Posts
    6
    The main issue here is not even the code. You have to change the way you are doing your game, nobody uses a big image as a background for a game map. What most people do is create tiles that represent different things like grass, floors, dirt, water, etc. You also create other "tiles" for border areas like a tile that goes from dirt to water, grass to floor and so on in each direction.

    You should put all those tiles in a single graphic, next to each other and load that graphic into memory. Make sure that each individual tile have the same size, like 20x20 and then you can make a function to get a particular tile based on its position. Now if you create a text file with something like this:

    000000000000000
    011111111111110
    011111111222220
    011111111222220
    000000000000000

    Then you just have to replace those numbers with the corresponding tile graphic to create the game map depending on where the user is. This is the way games like Mario Bros are coded in order to save memory and run on limited processing power. It has the added benefit that you can reuse the graphics and just create as many text files with different maps without having to code anything extra. And if you want to get creative, you can even use tiles with transparent areas (sprites) to impose tiles on top of each other (think little stones or debris in a dirt tile).

    Get any game programming book or read the tutorials online before you go any further and it will save you a lot of grief.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Loading & Displaying Images
    By devarishi in forum C Programming
    Replies: 9
    Last Post: 12-10-2008, 07:05 PM
  2. Loading & displaying an image
    By csonx_p in forum Windows Programming
    Replies: 1
    Last Post: 06-19-2008, 06:40 AM
  3. Loading and Displaying Bitmap
    By Master_of_Error in forum C Programming
    Replies: 1
    Last Post: 02-02-2003, 01:21 PM
  4. Loading a .bmp and displaying into a dialog?
    By Unregistered in forum C++ Programming
    Replies: 3
    Last Post: 01-30-2002, 12:49 PM