Sign in to follow this  
b1gjo3

HBITMAP to Window

Recommended Posts

hello, im trying to get an HBITMAP to display on my window. I have loaded the image from file and im trying to display it with this..
PAINTSTRUCT Ps;
myDC = BeginPaint(myHwnd, &Ps);
MemDC = CreateCompatibleDC(myDC);
SelectObject(MemDC, myBitmap);
BitBlt(myDC, 0, 0, iWidth, iHeight, MemDC, 0, 0, SRCCOPY);
DeleteDC(MemDC);
EndPaint(myHwnd, &Ps);

before, i had read all the RGB values from the HBITMAP and displayed them all using SetPixel(). It was way to slow and im trying to speed up the process. im not sure where to go from here. can someone give me some guidance. thanks

Share this post


Link to post
Share on other sites
From Game Programming Genesis Part III:


int ShowBitmapResource(HDC hDestDC, int xDest, int yDest, int nResID)
{
HDC hSrcDC; // source DC - memory device context
HBITMAP hbitmap; // handle to the bitmap resource
BITMAP bmp; // structure for bitmap info
int nHeight, nWidth; // bitmap dimensions

// first load the bitmap resource
if ((hbitmap = (HBITMAP)LoadImage(hinstance, MAKEINTRESOURCE(nResID),
IMAGE_BITMAP, 0, 0,
LR_CREATEDIBSECTION)) == NULL)
return(FALSE);

// create a DC for the bitmap to use
if ((hSrcDC = CreateCompatibleDC(NULL)) == NULL)
return(FALSE);

// select the bitmap into the DC
if (SelectObject(hSrcDC, hbitmap) == NULL)
return(FALSE);

// get image dimensions
if (GetObject(hbitmap, sizeof(BITMAP), &bmp) == 0)
return(FALSE);

nWidth = bmp.bmWidth;
nHeight = bmp.bmHeight;

// copy image from one DC to the other
if (BitBlt(hDestDC, xDest, yDest, nWidth, nHeight, hSrcDC, 0, 0,
SRCCOPY) == NULL)
return(FALSE);

// kill the memory DC
DeleteDC(hSrcDC);

// return success!
return(TRUE);
}



Also an alternative to this is to create a PictureBox control as a child of your window, and then give the control your bitmap (the control handles drawing it for you). This is handy for sticking one or two pictures in a dialog box, for instance.

Share this post


Link to post
Share on other sites
A bitmap can only be selected into one context at a time. Other reasons that the call could fail include myBitmap not actually being a valid bitmap or the DC not being a valid DC.

What does your current code look like?

Share this post


Link to post
Share on other sites
these are functions for loading an image, and also getting rgb values from an HBITMAP

#ifndef IMAGE_H
#define IMAGE_H

#include <windows.h>
#include <olectl.h>
#include <ole2.h>
#include "Color.h"

HBITMAP LoadAnImage(const char*);

ColorRGB* GetImageInfo(HBITMAP bmp, int* w, int* h)
{
//variables
BITMAP bm;
COLORREF Color;
int width, height;
int R, G, B;
int pixelCount, count = 0;

//load image
HDC dc = ::GetDC(0);
HDC MemDC = ::CreateCompatibleDC(dc);
HANDLE hBMP = bmp;
HBITMAP hOldBmp = (HBITMAP)::SelectObject(MemDC,hBMP);


//get width height
GetObject(hBMP, sizeof(BITMAP), (LPVOID)&bm);
width = bm.bmWidth;
height = bm.bmHeight;

pixelCount = width*height;
ColorRGB* imgRgbTable = new ColorRGB[pixelCount];

for (int i = 0; i < height; i++)
{
for (int y = 0; y < width; y++)
{
Color = ::GetPixel(MemDC,y,i); // X,Y
R = GetRValue(Color);
G = GetGValue(Color);
B = GetBValue(Color);
imgRgbTable[count].SetRGB(R,G,B);
count++;
}
}

::SelectObject(MemDC,hOldBmp);
::DeleteObject(hBMP);
::DeleteDC(MemDC);
::ReleaseDC(0,dc);

/*debug
for (int i = 0; i < pixelCount; i++)
cout << "R: " << imgRgbTable[i].R << " - G: " << imgRgbTable[i].G << " - B: " << imgRgbTable[i].B << endl;
*/


*w = width;
*h = height;
return imgRgbTable;
}

HBITMAP LoadAnImage(const char* FileName)
{
// Use IPicture stuff to use JPG / GIF files
IPicture* p;
IStream* s = NULL;
IPersistStream* ps = NULL;
HGLOBAL hG;
void* pp;
FILE* fp;


// Read file in memory
fopen_s(&fp, FileName,"rb");
if (!fp)
return NULL;

fseek(fp,0,SEEK_END);
int fs = ftell(fp);
fseek(fp,0,SEEK_SET);
hG = GlobalAlloc(GPTR,fs);

if (!hG)
{
fclose(fp);
return NULL;
}

pp = (void*)hG;
fread(pp,1,fs,fp);
fclose(fp);

// Create an IStream so IPicture can
CreateStreamOnHGlobal(hG,false,&s);

if (!s)
{
GlobalFree(hG);
return NULL;
}

OleLoadPicture(s,0,false,IID_IPicture,(void**)&p);

if (!p)
{
s->Release();
GlobalFree(hG);
return NULL;
}

s->Release();
GlobalFree(hG);

HBITMAP hB = 0;
p->get_Handle((unsigned int*)&hB);

// Copy the image. Necessary, because upon p's release,
// the handle is destroyed.
HBITMAP hBB = (HBITMAP)CopyImage(hB,IMAGE_BITMAP,0,0,
LR_COPYRETURNORG);

p->Release();
return hBB;
}




this is my winmain

int APIENTRY _tWinMain()
{
myBitmap = LoadAnImage("C:\\Users\\BigJoe\\Desktop\\test.bmp");
myRgb = GetImageInfo(myBitmap, &iWidth, &iHeight);

//setup window gui and create root folder
myGui = new ScanGUI("Image Scanner", iWidth, iHeight);
myGui->InitWindow();

MSG msg = {0};

while (WM_QUIT != msg.message)
{
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}




heres a portion of my WndProc

case WM_PAINT:
HDC hSrcDC; // source DC - memory device context

// create a DC for the bitmap to use
if ((hSrcDC = CreateCompatibleDC(myDC)) == NULL)
return(FALSE);

// select the bitmap into the DC
if (SelectObject(hSrcDC, myBitmap) == NULL)
return(FALSE);

// copy image from one DC to the other
if (BitBlt(myDC, 0, 0, iWidth, iHeight, hSrcDC, 0, 0, SRCCOPY) == NULL)
return(FALSE);

// kill the memory DC
DeleteDC(hSrcDC);




iWidth, iHeight, myBitmap are globals atm

Share this post


Link to post
Share on other sites
Some comments:

LoadAnImage() - why not just use LoadImage()?
GetImageInfo() - the bmBits members of the BITMAP struct has the RGB information

As for the problem:
Your WM_PAINT handler is incorrect. You should get dcSrc from BeginPaint() and let EndPaint() do what it wants to it. Create a compatible memory DC and select myBitmap into that - remembering of course to store the returned HBITMAP. Use BitBlt() from the memory DC to dcSrc. The select the stored HBITMAP back into the memory DC so that your bitmap is no longer in it and delete the memory DC.

Share this post


Link to post
Share on other sites
LoadImage can't load JPEG and GIF files.

Quote:

// Use IPicture stuff to use JPG / GIF files


My quess as Colin suggests (but I'll put a finer point on it) is that this code works ONCE on the first draw but it gets redrawn so fast that the bug you have in your code isn't obvious.

When you SelectObject(MemDc, myBitmap) you need to store the returned HBITMAP. Then after you are done with BitBlt() SelectObject(MemDc, oldBitmap) back in. If you don't you destroy the DC but your HBITMAP still thinks it is selected and so can't be selected into a new DC on the next draw. Thus your SelectObject fails.


...
HBITMAP oldBitmap; // Somewhere to store the old HBITMAP
...
oldBitmap = (HBITMAP)SelectObject(MemDC, myBitmap); // Store the old HBITMAP
BitBlt(myDC, 0, 0, iWidth, iHeight, MemDC, 0, 0, SRCCOPY);
SelectObject(MemDc, oldBitmap); // Restore the old HBITMAP and free myBitmap
...

Share this post


Link to post
Share on other sites
ok so this is what i have now


HDC MemDC = NULL;
HBITMAP oldBitmap = NULL;
PAINTSTRUCT Ps;
//check any available messages from the queue
switch (message)
{
case WM_PAINT:
myDC = BeginPaint(myHwnd, &Ps);
MemDC = CreateCompatibleDC(myDC);
oldBitmap = (HBITMAP)SelectObject(MemDC, myBitmap);
BitBlt(myDC, 0, 0, iWidth, iHeight, MemDC, 0, 0, SRCCOPY);
SelectObject(MemDC, oldBitmap);
DeleteDC(MemDC);
EndPaint(myHwnd, &Ps);
break;



(myDC is a member variable of my Window class. i get myDC in the next source code, dont know if this will help my problem)

//create the Window from the class above
myHwnd = CreateWindowEx(myWinExStyle, myWndClass.lpszClassName, myWndClass.lpszClassName,
myWinStyle, (cen.right - myWidth) / 2, (cen.bottom - myHeight) / 2,
rect.right - rect.left, rect.bottom - rect.top,
NULL, NULL, this->myInst, this);

if (!myHwnd)
return false;

myDC = GetDC(myHwnd);



thank you everyone for showing interest in my problem

Share this post


Link to post
Share on other sites
Ok well i think you solved the original problem ( SelectObject() ), but I think you may have another one.

Why do you GetDC() after you create the window? Are you doing drawing right then? All of your client drawing should be done between BeginPaint() and EndPaint() and you get your DC from there so you shouldn't need to call GetDC(). And if for some reason you really do need a DC there, are you calling ReleaseDC()?

Only in two situations do you NOT call ReleaseDC() on a DC you retrieved with GetDC() and they are when the DC is a "Class DC" or a "Private DC". Which is to say you registered your window class with the styles of CS_CLASSDC or CS_OWNDC which give you a class and private DC respectively. If these are not in the WNDCLASSEX.style property, the GetDC() is getting a common DC from the system pool. Not calling ReleaseDC() on it is a memory leak.



*Note the difference between Class DC as it relates to the Window Class as registered with RegisterClass/RegisterClassEx and the C++ window class you have implemented which has a member that is an HDC.

Share this post


Link to post
Share on other sites
ok i just got rid of myDC variable and used a HDC called SrcDC, and its working finally. Although changing the DC didnt solve my problem. I inserted LoadAnImage() right before the selectobject just to make sure the image would load correctly. and it did.

Now how come i can do this with small images, but it wont display bigger images?

thanks guys


[Edited by - b1gjo3 on May 10, 2008 4:33:40 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this