I'm trying to draw a bitmap into a section of a CDC. I load the bmp, BitBlt the CDC with the bitmap information and nothing shows up, this is my code:
CWnd* pWindow = GetParent()->GetDlgItem(IDC_DIAGRAM);
CClientDC windowDC(pWindow);
CDC* CDC = windowDC;
// I draw the graph and other objects I need here, everything works perfectly so far
HANDLE hBitMap = ::LoadImage(0, L"Diagram.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
CBitmap bmp;
bmp.Attach((HBITMAP)hBitMap);
// For testing purposes I tried drawing this bmp into a separate picture control with the bmp property turned on and it drew fine, So i know the data is loaded properly.
CDC->SelectObject(bmp);
// start Position = (0,0) , width = 500, height = 500, imageStartPos = (0,0)
CDC->BitBlt(0, 0, 500, 500, &CDC, 0, 0, SRCCOPY);
After the following code nothing shows up, what im expecting is:
_____________
| | |
| BMP | |
|_____| |
| |
| CDC |
| |
| |
______________
Is there any way to do this without having to stack picture controls on top of each other?
edit: Got it to work, the working code is:
CDC* CDC = windowDC;
CDC* bmDC;
CClientDC dc(this);
// Load bmp the same way as above
bmDC->SelectObject(bmp);
bmDC->CreateCompatibleDC(&dc);
CDC->BitBlt(0, 0, 500, 500, bmDC, 0, 0, SRCCOPY);
Related
I need to display an 'Empty' Bitmap (all values zero for RGBA channels) in a dialog.
Rough code:
HWND img = ::GetDlgItem( hwnd_, 228 );
unsigned char* charFrame = new unsigned char[frame_height*frame_width*4];
HWND hImage = CreateWindow(L"STATIC", L"", WS_VISIBLE | WS_CHILD | SS_BITMAP,
0, 0, frame_width, frame_height, img, (HMENU)1,GetWindowInstance( hwnd_ ),0);
HBITMAP bmp1 = CreateBitmap(frame_width,frame_height,1,32,charFrame);
SendMessage(hImage, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) (HANDLE) bmp1);
DeleteObject((HBITMAP)bmp1);
In the above code, charFrame variable is an unsigned char * pointer which has all the values set to zero. hwnd_ is the window handle.
Upon executing the code snippet, the window to which the BITMAP is sent, is not displaying the alpha content of the image. It displays a black image(R G B=0 0 0).
Any help is appreciated.
I'm creating a window and I'm defining the height as 768:
RECT windowRect;
windowRect.left = (long)0;
windowRect.right = 1366;
windowRect.top = (long)0;
windowRect.bottom = 768;
...and the following styles:
dwExStyle = WS_EX_APPWINDOW | WS_EX_CLIENTEDGE;
dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
...and finally doing this:
AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
//windowRect.left = -11;
//windowRect.right = 1377;
//windowRect.top = -38;
//windowRect.bottom = 779;
And then I'm creating another window trying to fill the whole screen, like this:
hTest = CreateWindow(L"STATIC", L"Testing", WS_CHILD | WS_VISIBLE | SS_BITMAP, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, hWnd, NULL, hInstance, 0);
With width of 1366 and height of 768. My computer's resolution is 1920 x 1080.
The width is perfectly aligned, exactly 1366.
The height is weird though, at 768 there's about a 10px margin below. If I use values up to 779, nothing changes, the same 10px margin in the bottom is still there. Then if I change to 780px, it's fully covered.
Why does this happen and what's the best way to deal with this? I'd like to have a window menu with exactly the same client height available.
EDIT: I've tried:
RECT r;
GetClientRect(hWnd, &r);
//r.right = 1366
//r.bottom = 768
I don't know why this weird area in bottom is showing, even though the numbers say it's fine...
It's the black border below (the white area is the new child window I'm creating):
Although it's in a bad quality (for size), there's exactly 1366 x 768 pixels of client area, which is what I requested in the CreateWindow function.
Creating a child window for this main window with 768 pixels of height causes the image above.
Adding a padding at top to check if the top 0 is actually 0 (and it isn't using the title border) shows that it is actually 0, that is, if I add any positionX to the child window I see the black background above it.
Quick background: I have a TreeView which I have created using Windows API calls in C++ (Visual Studio 2008, though that shouldn't make a difference):
hTreeview = CreateWindowEx(0, WC_TREEVIEW, L"My Treeview", WS_CHILD | WS_VISIBLE | TVS_INFOTIP | TVS_NOHSCROLL | TVS_SHOWSELALWAYS | TVS_EDITLABELS | TVS_SINGLEEXPAND, m_tx, m_ty, m_tw, m_th, hWindow, (HMENU)2, hInstance, NULL);
I have successfully assigned an ImageList to it using:
m_hImageList = ImageList_Create(cx, cy, ILC_COLOR24, n, n);
TreeView_SetImageList(hTreeview, m_hImageList, TVSIL_NORMAL);
where cx, cy, and n are all specified (in this case, 18, 18, and 5, respectively). This all works fine, as I can see because now there is space set aside next to my items for the image.
What I am trying to accomplish is to then copy a subsection of another bitmap (from a file). The code that I have tried (but does not work) is this:
HBITMAP hSkin = (HBITMAP)LoadImage(NULL, szPathBmp, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
for (long i = 0; i < n; i++)
{
HDC dcDest = CreateCompatibleDC(NULL);
HBITMAP hIcon = CreateCompatibleBitmap(dcDest, cx, cy);
HDC dcSrc = CreateCompatibleDC(NULL);
SelectObject(dcSrc, hSkin);
BitBlt(dcDest, 0, 0, cx, cy, dcSrc, x, y, SRCCOPY);
*pIcon = ImageList_Add(m_hImageList, hIcon, NULL);
DeleteObject(hIcon);
DeleteDC(dcSrc);
DeleteDC(dcDest);
}
DeleteObject(hSkin);
I have left out the error checking code for brevity, and it can be assumed that all of the listed variables have been set somewhere else in the program (forgive me for not providing a working source file, but this is a very large project and I have tried to include only the parts that are relevant).
All I get in the Treeview is black squares (which happens to be the background color of the Treeview), so I am assuming that something is going wrong with the last block of code--the one that loads the skin and tries to BitBlt a portion of it into a new bitmap to save to the ImageList. Can anyone either tell me what I'm doing wrong, or tell me a better way of accomplishing what I'm trying to do?
I am using C++ and the Windows API exclusively, no .NET, MFC, or Windows Form Designer.
Thanks in advance for your help, and if I've left anything out, I apologize; this is one of my first posts.
There are a few problems with your code.
Firstly, when you create a new DC it starts out with a monochrome bitmap in it, so your CreateCompatibleBitmap call will also produce a monochrome one. Instead, you probably want to create the bitmap based on the window or screen DC.
Secondly, you never actually select the bitmap into dcDest, so nothing will be drawn into hIcon anyway.
Thirdly, ImageList_Add will fail if the bitmap is currently selected into a device context, so you have to deselect hIcon from dcDest before you add the icon to the image list.
Lastly, you are also neglecting to save the original bitmaps and restore them, so this will also cause a GDI leak.
Try something like this:
HDC hdcWindow = GetDC(hWnd);
HDC dcDest = CreateCompatibleDC(hDCWindow);
HBITMAP hIcon = CreateCompatibleBitmap(hDCWindow, cx, cy);
HDC dcSrc = CreateCompatibleDC(NULL);
HGDIOBJ hOldSourceBmp = SelectObject(dcSrc, hSkin);
HGDIOBJ hOldDestBmp = SelectObject(dcDest, hIcon);
BitBlt(dcDest, 0, 0, cx, cy, dcSrc, x, y, SRCCOPY);
SelectObject(dcDest, hOldDestBmp);
SelectObject(dcSrc, hOldSourceBmp);
*pIcon = ImageList_Add(m_hImageList, hIcon, NULL);
DeleteObject(hIcon);
DeleteDC(dcSrc);
DeleteDC(dcDest);
ReleaseDC(hWnd, hDCWindow);
I want to load a BitMap image in a pic box I created inside a window...picBoxDisp is created using following mechanism..
picBoxDisp = CreateWindow("STATIC", "image box",
WS_VISIBLE |WS_CHILD | SS_BITMAP |WS_TABSTOP | WS_BORDER,
50, 50, 250, 300, hwnd , (HMENU)10000, NULL, NULL);
Now Next I created a hBitmap object and loaded an image in to it...
hBitmap = (HBITMAP) LoadImage(NULL,szFileName,IMAGE_BITMAP,0,0,
LR_LOADFROMFILE| LR_DEFAULTSIZE);
SendMessage(picBoxDisp,STM_SETIMAGE,(WPARAM) IMAGE_BITMAP,(LPARAM) NULL);
//now assign the new image
//Create a compatible DC for the original size bitmap, for example originalMemDc.
HDC originalDC = GetDC((HWND)hBitmap);
HDC originalMemDC = CreateCompatibleDC(originalDC);
if(originalMemDC==NULL){
MessageBox(NULL,"Problem while creating DC.","Error",MB_OK);
}
//Select hBitmap into originalMemDc.
SelectObject(originalMemDC,hBitmap);
//Create a compatible DC for the resized bitmap, for example resizedMemDc.
HDC picBoxDC = GetDC(picBoxDisp);
HDC resizedMemDC = CreateCompatibleDC(picBoxDC);
//Create a compatible bitmap of the wanted size for the resized bitmap,
HBITMAP hResizedBitmap = CreateCompatibleBitmap(picBoxDC,250,300);
//Select hResizedBitmap into resizedMemDc.
SelectObject(resizedMemDC,hResizedBitmap);
//Stretch-blit from originalMemDc to resizedMemDc.
//BitBlt(resizedMemDC,0,0,250,300,originalMemDC,0,0,SRCCOPY);
BITMAP bmp_old,bmp_new;
GetObject(hBitmap,sizeof(bmp_old),&bmp_old);
GetObject(hResizedBitmap,sizeof(bmp_new),&bmp_new);
StretchBlt ( resizedMemDC,0,0,bmp_new.bmWidth,bmp_new.bmHeight,
originalMemDC,0,0,bmp_old.bmWidth,bmp_new.bmHeight,
SRCCOPY);
////De-select the bitmaps.
if((resizedMemDC==NULL)||(hResizedBitmap == NULL)) {
MessageBox(NULL,"Something is NULL","Error",MB_OK);
}
else
//Set hResizedBitmap as the label image with STM_SETIMAGE
SendMessage(picBoxDisp,STM_SETIMAGE, (WPARAM) IMAGE_BITMAP,(LPARAM) hResizedBitmap);
I just cant understand, why the above code is not working ?
Thanks in advance,
You misunderstood the STM_SETIMAGE usage. Do this:
hBitmap = (HBITMAP)::LoadImage(NULL, szFileName, IMAGE_BITMAP,
0, 0, LR_LOADFROMFILE| LR_DEFAULTSIZE);
if (hBitmap != NULL)
{
::SendMessage(picBoxDisp, STM_SETIMAGE,
(WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
}
EDIT: If you want to resize the bitmap before setting it as the label image, then follow this scheme for the simplest possible way to do it (with sub-optimal quality in the resized image...):
Create a compatible DC for the original size bitmap, for example originalMemDc.
Select hBitmap into originalMemDc.
Create a compatible DC for the resized bitmap, for example resizedMemDc.
Create a compatible bitmap of the wanted size for the resized bitmap, for example hResizedBitmap.
Select hResizedBitmap into resizedMemDc.
Stretch-blit from originalMemDc to resizedMemDc.
De-select the bitmaps.
Set hResizedBitmap as the label image with STM_SETIMAGE
Should work!
The static control won't stretch the image to its size. You could use SS_CENTERIMAGE but it either clips or fills the empty space with the color of the top left pixel (see http://msdn.microsoft.com/en-US/library/b7w5x74z.aspx). You'd have to stretch the bitmap yourself before sending it to the static control.
You're trying to assign an image to a static control, so you don't need to draw the image but to set the image on it.
// set the image
HBITMAP hold = (HBITMAP)SendMessage(hpicBoxDc, STM_SETIMAGE, IMAGE_BITMAP, LPARAM(hBitmap));
// clear the old image
if(hold && hold != hBitmap)
DeleteObject(hold );
I'm creating what should be a very simple Win32 C++ app whose sole purpose it to ONLY display a semi-transparent PNG. The window shouldn't have any chrome, and all the opacity should be controlled in the PNG itself.
My problem is that the window doesn't repaint when the content under the window changes, so the transparent areas of the PNG are "stuck" with what was under the window when the application was initially started.
Here's the line where I setup the new window:
hWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_POPUP, 0, height/2 - 20, 40, 102, NULL, NULL, hInstance, 0);
For the call to RegisterClassEx, I have this set for the background:
wcex.hbrBackground = (HBRUSH)0;
Here is my handler for WM_PAINT message:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
Gdiplus::Graphics graphics(hdc);
graphics.DrawImage(*m_pBitmap, 0, 0);
EndPaint(hWnd, &ps);
break;
}
One thing to note is that the application is always docked to the left of the screen and doesn't move. But, what's underneath the application may change as the user opens, closes or moves windows under it.
When the application first starts, it looks perfect. The transparent (and simi-transparent) parts of the PNG show through perfectly. BUT, when the background underneath the application changes, the background DOESN'T update, it just stays the same from when the application first started. In fact, WM_PAINT (or WM_ERASEBKGND does not get called when the background changes).
I've been playing with this for quite a while and have gotten close to getting 100% right, but not quite there. For instance, I've tried setting the background to (HBRUSH) NULL_BRUSH and I've tried handling WM_ERASEBKGND.
What can be done to get the window to repaint when the contents under it changes?
I was able to do exactly what I wanted by using the code from Part 1 and Part 2 of this series:
Displaying a Splash Screen with C++
Part 1: Creating a HBITMAP archive
Part 2: Displaying the window archive
Those blog posts are talking about displaying a splash screen in Win32 C++, but it was almost identical to what I needed to do. I believe the part that I was missing was that instead of just painting the PNG to the window using GDI+, I needed to use the UpdateLayeredWindow function with the proper BLENDFUNCTION parameter. I'll paste the SetSplashImage method below, which can be found in Part 2 in the link above:
void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash)
{
// get the size of the bitmap
BITMAP bm;
GetObject(hbmpSplash, sizeof(bm), &bm);
SIZE sizeSplash = { bm.bmWidth, bm.bmHeight };
// get the primary monitor's info
POINT ptZero = { 0 };
HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorinfo = { 0 };
monitorinfo.cbSize = sizeof(monitorinfo);
GetMonitorInfo(hmonPrimary, &monitorinfo);
// center the splash screen in the middle of the primary work area
const RECT & rcWork = monitorinfo.rcWork;
POINT ptOrigin;
ptOrigin.x = 0;
ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2;
// create a memory DC holding the splash bitmap
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash);
// use the source image's alpha channel for blending
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// paint the window (in the right location) with the alpha-blended bitmap
UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash,
hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);
// delete temporary objects
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
}
Use the SetLayeredWindowAttributesarchive function, this allows you to set a mask color that will become transparent, thus allowing the background to show through.
You will also need to configure your window with the layered flag, e.g.:
SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
After that it's fairly simple:
// Make red pixels transparent:
SetLayeredWindowAttributes(hwnd, RGB(255,0,0), 0, LWA_COLORKEY);
When your PNG contains semi-transparent pixels that you want to blend with the background, this becomes more complicated. You could try looking at the approach in this CodeProject article:
Cool, Semi-transparent and Shaped Dialogs with Standard Controls for Windows 2000 and Above