I attampted to draw a irregular window with the UpdateLayeredWindow(), in msvc2008, xp sp3.
Here is part of my code:
//Add something(CreateWindowEx()):
hwndCyauWnd = CreateWindowEx(
/*WS_EX_TOOLWINDOW |*/ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_LAYERED,
lpwsCyauClassName,
lpwsCyauWndName,
WS_CLIPSIBLINGS | WS_POPUP,
GetSystemMetrics(SM_CXSCREEN)-320,
GetSystemMetrics(SM_CYSCREEN)-232,
320, 200,
NULL,
NULL,
hInstance,
NULL);
//Skip Lines
HDC hdcCyauWnd = GetDC(hwndCyauWnd);
HDC hdcBuffer = CreateCompatibleDC(hdcCyauWnd);
//HBITMAP hbmCyau = CreateCompatibleBitmap(hdcBuffer,120, 93);
//SelectObject(hdcBuffer, hbmCyau);
POINT ptZero = {0, 0};
POINT ptDrawPos = {0, 0};
RECT rctCyauWnd;
GetWindowRect(hwndCyauWnd, &rctCyauWnd);
SIZE szCyauWnd={rctCyauWnd.right - rctCyauWnd.left, rctCyauWnd.bottom - rctCyauWnd.top};
BLENDFUNCTION blendPixelFunction = { AC_SRC_OVER, 0, 100, AC_SRC_ALPHA};
Graphics gphCyauWnd(hdcBuffer);
Image imgCyau(L"surface0000.png");
gphCyauWnd.DrawImage(&imgCyau, 0, 0, 125, 93);
UpdateLayeredWindow(hwndCyauWnd,
hdcCyauWnd, &ptZero,
&szCyauWnd,
hdcBuffer, &ptZero,
0, //RGB(255, 255, 255),
&blendPixelFunction,
ULW_ALPHA);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
I have tried several method to use this function, but all failed, NOTHING APPEAR on the screen.
Could anybody tell me what happens and how to slove it?
Add:
Whole source file have been upload to my skydrive, anyone can edit, much appreciation! (I have become a poor underdog now...)
You mixed up GDI and GDI+, which is not a good idea. Here is a working example:
hWnd = CreateWindowEx(WS_EX_LAYERED, szWindowClass, szTitle, 0,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
// Load PNG
CImage img;
img.Load("BACKGR.png");
// Get image sizes
int nWidth = img.GetWidth();
int nHeight = img.GetHeight();
// Create memory DC
HDC hdcScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hdcScreen);
// Create memory bitmap
HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, nWidth, nHeight);
HBITMAP hBmpOld = (HBITMAP)SelectObject(hDC, hBmp);
// Draw image to memory bitmap (currently selected in memory DC)
img.Draw(hDC, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight);
// Call UpdateLayeredWindow
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 128;// half transparent
blend.AlphaFormat = AC_SRC_ALPHA;
POINT ptLocation = {0, 0};
SIZE szWnd = {nWidth, nHeight};
POINT ptSrc = {0, 0};
UpdateLayeredWindow(hWnd, hdcScreen, &ptLocation, &szWnd, hDC, &ptSrc, 0, &blend, ULW_ALPHA);
ShowWindow(hWnd, SW_SHOW);
SelectObject(hDC, hBmpOld);
DeleteObject(hBmp);
DeleteDC(hDC);
ReleaseDC(NULL, hdcScreen);
If you want GDI+ to draw to an image with an alpha channel, you have to draw to a Bitmap, not an HDC, and you have to specify that the Bitmap's format has alpha. To do that with an HBITMAP, you have to also point GDI+ to the bitmap bits.
Something like this:
BITMAPINFOHEADER bih;
HBITMAP hbmp;
HDC hdc;
void *bits;
bih.biSize = sizeof(bih);
bih.biWidth = width;
bih.biHeight = -height;
bih.biPlanes = 1;
bih.biBitCount = 32;
bih.biCompression = BI_RGB;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
hdc = CreateCompatibleDC(NULL);
hbmp = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS, &bits, NULL, 0);
Bitmap bitmap(width, height, 0, PixelFormat32bppPARGB, bits);
Graphics graphics(bitmap);
graphics->DrawWhatever(...);
graphics->Flush();
SelectObject(hdc, hbitmap);
UpdateLayeredWindow(hwnd, hdc, ...
Related
I'm studying WinAPI double buffering, and when I run the code below, the screen flickers too much. I think I did it right, but where is the problem?
Draw memdc1 and memdc2 to memdc, and memdc to hdc.
switch (iMsg)
{
case WM_CREATE:
hdc = GetDC(hwnd);
GetClientRect(hwnd, &clientRt);
PlaceCenterScreen(hwnd);
SetTimer(hwnd, 1, 10, NULL);
hMixBit = CreateCompatibleBitmap(hdc, 819, 614);
hHouseBit = (HBITMAP)LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_HOUSE));
hTigerBit = (HBITMAP)LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_TIGER));
ReleaseDC(hwnd, hdc);
break;
case WM_TIMER:
posX += 3;
InvalidateRgn(hwnd, NULL, true);
break;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
memdc = CreateCompatibleDC(hdc);
memdc1 = CreateCompatibleDC(memdc);
memdc2 = CreateCompatibleDC(memdc);
oldHouseBit = (HBITMAP)SelectObject(memdc1, hHouseBit);
oldTigerBit = (HBITMAP)SelectObject(memdc2, hTigerBit);
oldMixBit = (HBITMAP)SelectObject(memdc, hMixBit);
BitBlt(memdc, 0, 0, 819, 614, memdc1, 0, 0, SRCCOPY);
BitBlt(memdc, posX, 0, 67, 47, memdc2, 0, 0, SRCCOPY);
BitBlt(hdc, 0, 0, 819, 614, memdc, 0, 0, SRCCOPY);
SelectObject(memdc1, oldHouseBit);
SelectObject(memdc2, oldTigerBit);
SelectObject(memdc, oldMixBit);
I am very new to the win10 dev, and I am trying to create a win10 app which will create a floating window in all the monitors the system is connected to. So far I am successful in using GDI API to create window in my primary monitor, but it is not working on my secondary monitor, I cannot get why it is not working even when I am using the API as mentioned in the microsoft page.
Below is my code
// Register the window class.
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = DXGIDraw::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = HINST_THISCOMPONENT;
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.hCursor = LoadCursor(NULL, IDI_APPLICATION);
wcex.lpszClassName = L"DemoApp";
RegisterClassEx(&wcex);
m_hwnd = CreateWindowEx(WS_EX_LAYERED,
L"DemoApp",
L"Demo App",
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
HWND_DESKTOP,
NULL,
HINST_THISCOMPONENT,
NULL
);
const int nWidth = GetSystemMetrics(SM_CXSCREEN);
const int nHeight = GetSystemMetrics(SM_CYSCREEN);
CImage img;
DISPLAY_DEVICE ddd;
ZeroMemory(&ddd, sizeof(ddd));
ddd.cb = sizeof(ddd);
for (int i = 0; EnumDisplayDevices(NULL, i, &ddd, 0); i++)
{
if (ddd.StateFlags & DISPLAY_DEVICE_ACTIVE) {
//Active monitor
}
if (ddd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
//Primary monitor
}
else {
//other types
}
}
//The following API supposed to work for all the monitors, but this API is not working. This is where I need attention.
HDC hdcScreen = CreateDC(ddd.DeviceName, NULL, NULL, NULL);
//But if I use below API I can get it working for the primary monitor only, still trying to understand why
//HDC hdcScreen = CreateDC(L"DISPLAY", NULL, NULL, NULL);
HDC hDC = CreateCompatibleDC(hdcScreen);
HBITMAP hBmp = CreateCompatibleBitmap(hdcScreen, nWidth, nHeight);
HBITMAP hBmpOld = (HBITMAP)SelectObject(hDC, hBmp);
img.Draw(hDC, 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight);
//Add layered window
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
POINT ptLocation = { 0, 0 };
SIZE szWnd = { nWidth, nHeight };
POINT ptSrc = { 0, 0 };
BOOL status1 = UpdateLayeredWindow(m_hwnd, hdcScreen, &ptLocation, &szWnd, hDC, &ptSrc, 0, &blend, ULW_ALPHA);
//Set window's position
BOOL status = SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
hr = m_hwnd ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
BOOL status;
status = ShowWindow(m_hwnd, SW_MAXIMIZE);
status = UpdateWindow(m_hwnd);
}
SelectObject(hDC, hBmpOld);
DeleteObject(hBmp);
DeleteDC(hDC);
ReleaseDC(NULL, hdcScreen);
}
EnumDisplayDevices is the wrong API for this job. You need to use EnumDisplayMonitors which gives you the monitor coordinates directly.
You need to retreive the bounding rectangle for the desired monitor within the virtual screen, and then you can position a window within that rectangle as desired. Use EnumDisplayMonitors() to get the rectangle of each moinitor. Or, if you have an HMONITOR handle to a specific monitor, you can use GetMonitorInfo().
On Windows, I'm trying to use CEF (Chromium Embedded Framework) to create a window application with parent window to be transparent and its child window to be opaque (I want to have a rounded corner and an arrow pointing to the status bar in the child window). Something similar to:
I tried to use SetLayeredWindowAttributes to make the parent window transparent but it also makes the child window transparent. Is there a way to make this happen on Windows?
SetLayeredWindowAttributes needs a color for transparency. Make sure the transparency color is not used by the child window. You can pick a random color, for example RGB(255, 0, 254) and assume the child window is not using it.
If you have no control over the child window, and you can't be sure what colors it might use, then SetWindowRgn is another option to create non-rectangular windows.
The example below shows how to set the region such that the corners are round with a triangle on top.
You can use GDI+ to gain more flexibility for drawing the region, and for anti-aliasing effect so that the borders look more smooth.
#include <Windows.h>
int triangle_height = 30;
int corner_size = 20;
int caption_height = 60;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_CREATE:
{
//create a child button for testing
CreateWindowW(L"BUTTON", L"Close", WS_CHILD | WS_VISIBLE,
10, caption_height + 10, 100, 30,
hwnd, HMENU(100), GetModuleHandle(NULL), NULL);
RECT rc;
GetClientRect(hwnd, &rc);
//create a triangle region
int w = rc.right;
int h = rc.bottom;
int z = triangle_height;
POINT pt[3];
pt[0] = { w / 2, 0 };
pt[1] = { w / 2 - z, z };
pt[2] = { w / 2 + z, z };
HRGN htri = CreatePolygonRgn(pt, 3, WINDING);
//create a round rectangle region
HRGN hrgn = CreateRoundRectRgn(0, z, w, h - z, corner_size, corner_size);
//combine the triangle with round rectangle
CombineRgn(hrgn, htri, hrgn, RGN_OR);
//set the new region
SetWindowRgn(hwnd, hrgn, TRUE);
DeleteObject(htri);
DeleteObject(hrgn);
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
//we don't have a standard title bar, paint one here:
rc.bottom = caption_height;
SetDCBrushColor(hdc, RGB(80, 80, 80));
FillRect(hdc, &rc, (HBRUSH)GetStockObject(DC_BRUSH));
//paint the background
rc = ps.rcPaint;
rc.top = caption_height;
SetDCBrushColor(hdc, RGB(240, 240, 240));
FillRect(hdc, &rc, (HBRUSH)GetStockObject(DC_BRUSH));
//use FrameRgn to paint a border around the region
HRGN hrgn = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hwnd, hrgn);
SetDCBrushColor(hdc, RGB(128, 128, 128));
FrameRgn(hdc, hrgn, (HBRUSH)GetStockObject(DC_BRUSH), 1, 1);
DeleteObject(hrgn);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCHITTEST:
{
//we don't have a standard title-bar
//respond to our custome title-bar manually:
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hwnd, &pt);
if(pt.y < caption_height)
return HTCAPTION;
break;
}
case WM_COMMAND:
if(HIWORD(wparam) == BN_CLICKED)
if(LOWORD(wparam) == 100)
SendMessage(hwnd, WM_CLOSE, 0, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
WNDCLASSEXW wcex = { sizeof(wcex) };
wcex.style = CS_DROPSHADOW;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszClassName = L"classname";
RegisterClassExW(&wcex);
CreateWindowW(wcex.lpszClassName, L"Test", WS_VISIBLE | WS_POPUP,
200, 200, 600, 400, 0, 0, hInstance, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
i'm sorry for what i did. i edited.
i'd like to use Fillrect on 32bit HBITMAP which is Created with CreateDIBSection
but i can't make rect visible in color that i want to.
(i Drawed a fillrect with CreateSolidBrush blue(RGB(0, 0, 255)) on 32bit HBITMAP(hdcbmp), but it doesn't appear blue.)
here is source code
is there anyway to show rect color that i want to?
sorry for my poor english.
void DrawAlphaBitmap(HWND hWnd, ULONG uWidth, ULONG uHeight)
{
BLENDFUNCTION bf;
HBITMAP hbitmap;
HBITMAP hOldBitmap;
BITMAPINFO bmi;
PVOID pvBits;
HDC hdcwnd = GetDC(hWnd);
HDC hdcbmp = CreateCompatibleDC(hdcwnd);
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = uWidth;
bmi.bmiHeader.biHeight = uHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = bmi.bmiHeader.biWidth * bmi.bmiHeader.biHeight * 4;
hbitmap = CreateDIBSection(hdcbmp, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0);
hOldBitmap = (HBITMAP)SelectObject(hdcbmp, hbitmap);
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xff;
bf.AlphaFormat = AC_SRC_ALPHA;
RECT rc2 = { 100, 100, 200, 200 };
FillRect(hdcbmp, &rc2, CreateSolidBrush(RGB(0, 0, 255)));
AlphaBlend(hdcwnd, 0, 0, uWidth, uHeight, hdcbmp, 0, 0, uWidth, uHeight, bf);
SelectObject(hdcbmp, hOldBitmap);
DeleteObject(hbitmap);
DeleteDC(hdcbmp);
ReleaseDC(hWnd, hdcwnd);
}
From documentation for BLENDFUNCTION:
AlphaFormat:
This flag is set when the bitmap has an Alpha channel (that is, per-pixel alpha).
In this case, alpha channel is not set. CreateDIBSection initializes the alpha values to zero. When AC_SRC_ALPHA is set, AlphaBlend ignores pixels whose alpha value is zero. Modify your code as follows:
//bf.AlphaFormat = AC_SRC_ALPHA; <- remove
bf.AlphaFormat = 0; //replace with 0
Side note, you have resource leak in creation of HBRUSH handle. Change the code to
HBRUSH hbrush = CreateSolidBrush(RGB(0, 0, 255));
RECT rc2 = { 0, 0, w, h };
FillRect(memdc, &rc2, hbrush);
DeleteObject(hbrush);
Ideally, your function prototype should be void DrawAlphaBitmap(HDC hdc, ULONG uWidth, ULONG uHeight); so that HDC can be passed directly, for example from BeginPaint/EndPaint in WM_PAINT message.
I'm trying to create a launcher window with 5 "invisible" buttons that have a smaller partially transparent image centered inside them. The image inside changes depending on whether the user is hovering over the button. In this example the images are resources IDB_Website1 and IDB_Website2.
The images are in .png format. I'm using GDI+ and loading PNG files from resources with http://www.codeproject.com/Articles/3537/Loading-JPG-PNG-resources-using-GDI.
I've used BS_OWNERDRAW to handle the graphics of the button. The problem is that I don't know how to link the "PNG from resources" code to the parent's WM_DRAWITEM. With the current way I've done the code, I lose the transparency of the .png by going through HBITMAP.
Global variable:
HBITMAP globalpic;
Main wndproc:
case WM_CREATE:
{
HWND hwndButton = CreateWindow(TEXT("button"), NULL,
WS_CHILD | WS_VISIBLE | BS_OWNERDRAW,
190, 230, 260, 50, // <- the larger rect size
hWnd, (HMENU) HT_BUTTON1, hInst, NULL);
HTButton = (WNDPROC) SetWindowLong(hwndButton, GWL_WNDPROC, (LONG) ButtonProc);
}
case WM_DRAWITEM:
{
DRAWITEMSTRUCT* lpDrawItem = (DRAWITEMSTRUCT*)lParam;
HBITMAP hBmp;
HDC hdc = CreateCompatibleDC(NULL);
if (hovering) // <- mousehover detection, not included
{
gdi.CreateBM(IDB_Website2, _T("PNG"), hInst, hdc, 62, 100, 200, 40);
} else {
gdi.CreateBM(IDB_Website1, _T("PNG"), hInst, hdc, 62, 100, 200, 40);
}
hBmp = globalpic; // get the global var gdi.CreateBM created
DeleteDC(hdc);
BITMAP bm;
GetObject(hBmp, sizeof(bm), &bm);
HDC hMemDC = CreateCompatibleDC(NULL);
HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hBmp);
StretchBlt (lpDrawItem->hDC,
lpDrawItem->rcItem.left + 30,
lpDrawItem->rcItem.top + 5,
200, <- the width of image
40, <- the height of image
hMemDC,
0, 0,
bm.bmWidth,
bm.bmHeight,
SRCCOPY);
SelectObject(hMemDC, hOldBmp);
DeleteDC(hMemDC);
DeleteObject(hBmp);
break;
}
class GDI's method CreateBM.
Currently, since I don't know of a better way, it sends the HBITMAP to the global variable globalpic. The problem is that by doing this, I lose transparency.
void CreateBM(UINT menuid, LPCTSTR pType, HMODULE hInst, HDC hdc, int x, int y, int w, int h)
{
CGdiPlusBitmapResource m_pBitmap;
m_pBitmap.Load(menuid, pType, hInst);
m_pBitmap.m_pBitmap->GetHBITMAP(RGB(255, 255, 0), &globalpic);
//m_pBitmap.m_pBitmap->GetHBITMAP(GetBkColor(hdc), &globalpic); // <- no-go either
}
Any way I could do this more directly? I feel that going through the HBITMAP route is completely unnecessary but I couldn't for the life of me figure out how I should do it.
Then again, if using the HBITMAP way is fine, any pointers on how I could make the color (I guess (RGB(255, 255, 0) ...?) transparent with the current implementation?
Answering to Hans:
I also have a GDI method like this, but it's unused at the moment because I couldn't get the image removed after drawing it (as is needed when hovering changes the image):
void Create(UINT menuid, LPCTSTR pType, HMODULE hInst, HDC hdc, int x, int y, int w, int h)
{
Graphics grpx(hdc);
ht_img = new CGdiPlusBitmapResource();
ht_img -> Load(menuid, pType, hInst);
grpx.DrawImage(*ht_img, x, y, w, h);
delete ht_img;
}