Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Windows API and bitmaps 1

Status
Not open for further replies.

nikes111

Technical User
May 3, 2004
11
US
I need to use windows API to get a bitmap from a file and put it onto the background of a window. If someone could give me a function that can be passed the the filename and bitmap size, I would greatly appreciate it. I'm new to windows API, but even just some help with it would be great.
 
Ok, I assume you already can create a window and have all that figured out. To draw an image onto a window is relatively simple. First your going to get a handle to an image file using the LoadImage function:

HANDLE hImage = LoadImage(NULL, "C:\\test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

Coolness? Now you have to get a device context for the window. Now basically a device context (or a DC) is a handle to an area of memory that represents an image. So to draw something to a window, we need its DC, because we're simply writing to the section of memory that represents its appearance. So heres how to get your window's DC:

HDC hWindowDC = GetDC(hWndMain);

Pretty simply huh? hWndMain is the handle to whatever window you want the DC of. Now we have a place in memory to write to, but we need a DC to load the bitmap file onto:

HDC hImageDC = CreateCompatibleDC(hDC);

So what we did is just create a new DC that's compatible with the window's DC (meaning it is the same colordepth, etc). Now we need to find out more about our bitmap file, such as its width and height, so we can use it to copy it to the window. We do that like this:

BITMAP Bitmap;
GetObject(hImage, sizeof(BITMAP), &Bitmap);

Ok so now Bitmap has a wealth of info on our bitmap. Now lets load the image file into our DC (basically we're copying the image from the HardDrive into memory):

SelectObject(hImageDC, hImage);

So now we have the window in one DC and the image in the other so let's copy using my favorite function:

BitBlt(hDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, hImageDC, 0, 0, SRCCOPY);

Now, if you don't plan to redraw the image, we need to delete the image from memory by deleting its DC:

DeleteDC(hImageDC);

Also, another thing about DC's is that other programs can't access them when your program has control. That is when you called GetDC to get your windows DC, you took control of that DC. So if you don't give up control when you're done, windows will not be able to draw your window, and that sucks. So lets release the window's DC:

ReleaseDC(hWndMain, hWindowDC);

So now we gave control back to windows and windows is happy. If you have any questions about the functions used, look them up on msdn. I would suggest you at least look up BitBlt (If you're not familliar with it) so you know how to draw it in different ways and positions. So here's the whole thing with an added line that checks to see if the file exists basically. That is if LoadImage returns a NULL handle, the specified file probably doesn't exist, so no need to try and do the rest:
Code:
HANDLE hImage = LoadImage(NULL, "C:\\test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (hImage){
HDC hWindowDC = GetDC(hWndMain);
HDC hImageDC = CreateCompatibleDC(hDC);
BITMAP Bitmap;
GetObject(hImage, sizeof(BITMAP), &Bitmap);
SelectObject(hImageDC, hImage);
BitBlt(hDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, hImageDC, 0, 0, SRCCOPY);
DeleteDC(hImageDC);
ReleaseDC(hWndMain, hWindowDC);
}
 
Thank you so much. I cannot get it to work yet because I put your code into a function and I need to call it from a different .cpp file. I am not sure how to do this. Also, is the hDc in your code supposed to be hWindowDC?
 
I have just tried it. It compiles OK. When I step through it everything works. The only problem is the bitmap does not appear in the window. (I put the code in WM_CREATE.)
 
You put the code in the wrong place. WM_CREATE is only sent one time, when the window is first created. WM_PAINT is called repeatedly afterwards whenever the window needs to be drawn. It is where you want to put any code that draws into the window. Also you might want to handle WM_ERASEBKGND as well, otherwise you might have problems with flickering.

Also, if you put the code in WM_PAINT, don't use GetDC/ReleaseDC. Use BeginPaint/EndPaint instead. They handle setting up the clipping area for you so you don't have to worry about it.
 
It is working once I moved the code into WM_PAINT. How should I handle the WM_ERASEBKGD?
 
You might not need to handle WM_ERASEBKGND unless you are writing your code in MFC and see flickering when your window updates. This happens because the default CWnd message handler for WM_ERASEBKGND fills the window with the background color, and an instant later WM_PAINT draws the real bitmap over it. If this happens, just add a WM_ERASEBKGND handler but leave it empty -- your WM_PAINT is taking care of the background anyway.
 
Thanks a lot. I have few other questions if I may ask. Currently I display the bitmap in a normal window in a test program. I handle the WM_PAINT in the WndProc(). However, I need to display it in a control on a dialog box. So the questions are:
1. What control should I use? (frame, rectangle, bitmap..)
2. I need to scroll the image in the control.
3. I need to zoom in/out when viewing it.
4. How do I handle drawing of the image in the control compare to normal window?
5. I need to convert it to JPEG so I can store kilobytes instead of megabytes. Are there any API to do this?
If I could get a sample code in C (no MFC) to do it would be great. However, I would appreciate just suggestion to my question.
Thanks a lot again.
 
OK, I asked too many questions. Could you answer at least one, please.
I have it in dialog box in a rectangle. I can zoom in, out using StretchBlt(). However, it only redraws if the dialog box is hidden and then shown again.
 
To get it to redraw it every time, put your StretchBlt() call in the WM_PAINT handler, and then whenever you want to redraw, call Invalidate(). This marks the whole window for repainting. Windows will do the same thing as if you hid the box and then shown it again -- it will send WM_PAINT so you can redraw it.

To answer the other questions:

1. You can just make a new control by writing your own C++ class derived from CWnd.

2. If you use MDI/SDI instead of dialog box approach, you can make your view window derive from CScrollView, and the scrolling is really easy. Otherwise, you can add scroll bars to your window by turning on the WS_HSCROLL and/or WS_VSCROLL window styles. Use the SetScrollWindow() method to set the ranges/positions of the scroll bars, and handle the WM_HSCROLL and WM_VSCROLL messages to do the actual scrolling.

3. StretchBlt() is good for redrawing a window with a different scaling (zoom) factor.

4. A control is the same as a normal window. Your drawing code goes in WM_PAINT in either case.

5. I use libjpeg for JPEG compression/decompression. It is developed by the Independent JPEG group (IJG).
 
Here's a tip about using StretchBlt. You may have noticed that the image quality sucks at times. It turns out that by default StretchBlt uses a "fast but doesn't look good" mode. You can change this using the SetStretchBltMode function. I recommend setting the mode to HALFTONE. Looks much better, but may be a little slower depending on how fast your computer is. Try it out and see.
 
Thanks a lot.
I have already tried InvalidateRect(hwnd, NULL, TRUE) where as hwnd I tried both hwnd of the dialog box and hwnd of the rectangle on which I draw the bitmap. It erases the bitmap and does not paint it until I hide and show the dialog box. It does not get the WM_PAINT message.
If I ShowWindow(hwnd, FALSE), ShowWindow(hwnd, TRUE) it updates the drawing but the dialog box flicker fors a moment.
If I call InvalidateRect(NULL, NULL, whatever) it updates the drawing but all open windows on the screen flicker for a moment.
It seems to me that it would be easier to do it in C++ as you suggests. However, I program in C and to switch to C++ would take some additional time and time is scarce resource.

I want to get images from a scanner using the twain interface. I have a sample program which does that.
It uses normal window and processes scanner messages like this:
while (GetMessage((LPMSG)&msg, NULL, 0, 0))
{
if ((!TWIsDSOpen()) || (!ProcessTWMessage ((LPMSG)&msg, hMainWnd)))
{
TranslateMessage ((LPMSG)&msg);
DispatchMessage ((LPMSG)&msg);
}
}

However, in dialogbox I do not get the messages from the scanner to the dialog procedure and I do not know how to intercept them. I need to get them after the GetMessage. Is there a possibility to do it or do I have to draw the whole form in a window myself to be able to do it?



 
InvalidateRect(hwnd, NULL, TRUE) with the HWND of the dialog box or the rectange should have worked. I'm not sure why WM_PAINT was not sent. The documentation for InvalidateRect does say that WM_PAINT is not sent until there are no other messages in the application queue. Maybe your application queue is always busy for some reason?

I wish I knew more about Twain or C-only windows programming so I could help you. :(
 
Thanks for your help.

InvalidateRect(hwnd, NULL, TRUE) works OK in a normal window. In a DialogBox it behaves differently (at least for me in this case).

I have to do more reading and experimenting.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top