Related
I want to make UI control blur-behind effect, like css backdrop-filter:blur(). Logic is seems to be simple:
Get current ID2D1DeviceContext* render bitmap;
Crop to the area, that needed;
Create ID2D1Effect blur, and pass there bitmap;
Draw effect to current context using DrawImage.
I am facing issues on the first step.
I found two ways to get bitmap from ID2D1DeviceContext*: ID2D1DeviceContext::GetTarget(ID2D1Image**) and ID2D1Bitmap::CopyFromRenderTarget().
Seems, that calling that functions between BeginDraw() and EndDraw() returns white bitmap from calling Clear before BeginDraw(), as I assume due to double-buffering. If in the middle of render loop, add another EndDraw() and BeginDraw() pair, then, for some reason forward draw calls until the next EndDraw() will not do anything (perhaps because middle EndDraw() returned D2DERR_RECREATE_TARGET, and I currently didn't handle it.
Anyway, rendering the whole window in the middle of render loop for every blurred ui element seems to be bad idea, and also, probably it will cause flickering.
I there way to obtain current-state (compatible) bitmap from ID2D1DeviceContext* without bliting it to target? Or maybe there is better way to achieve effect I want?
Another example of blur effect in case of uwp in-app blur (not blur-behind window effect):
Minimal example with ID2D1DeviceContext::GetTarget(ID2D1Image**), that just fills target white, instead of drawing blurred red rectangle on blue background:
#include <Windows.h>
HDC hdcDevice = GetDC(NULL);
int xw = GetDeviceCaps(hdcDevice, HORZRES);
int yw = GetDeviceCaps(hdcDevice, VERTRES);
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
HWND hwnd;
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <wchar.h>
#include <math.h>
#include <d2d1_1.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <wincodec.h>
#pragma comment(lib, "d2d1")
#pragma comment(lib, "dxguid.lib")
template<class Interface>
inline void SafeRelease(
Interface** ppInterfaceToRelease)
{
if (*ppInterfaceToRelease != NULL)
{
(*ppInterfaceToRelease)->Release();
(*ppInterfaceToRelease) = NULL;
}
}
#ifndef Assert
#if defined( DEBUG ) || defined( _DEBUG )
#define Assert(b) do {if (!(b)) {OutputDebugStringA("Assert: " #b "\n");}} while(0)
#else
#define Assert(b)
#endif //DEBUG || _DEBUG
#endif
#ifndef HINST_THISCOMPONENT
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
#endif
ID2D1Factory* m_pDirect2dFactory;
ID2D1HwndRenderTarget* m_pRenderTarget;
ID2D1DeviceContext* target;
ID2D1SolidColorBrush* brush;
void Release()
{
SafeRelease(&m_pRenderTarget);
SafeRelease(&target);
SafeRelease(&brush);
}
void Init()
{
Release();
m_pDirect2dFactory = nullptr;
m_pRenderTarget = nullptr;
SUCCEEDED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory));
RECT rc;
GetClientRect(hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top);
// Create a Direct2D render target.
SUCCEEDED(m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(hwnd, size),
&m_pRenderTarget));
m_pRenderTarget->QueryInterface(&target);
m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(255,0,0),
&brush
);
}
void Render()
{
target->BeginDraw();
target->Clear(D2D1::ColorF(D2D1::ColorF::Blue));
target->SetTransform(D2D1::Matrix3x2F::Identity());
D2D1_SIZE_F rtSize = target->GetSize();
target->FillRectangle(D2D1::RectF(30, 30, 100, 100), brush);
ID2D1Effect *blur = nullptr;
target->CreateEffect(CLSID_D2D1GaussianBlur, &blur);
if (blur)
blur->SetValue(D2D1_GAUSSIANBLUR_PROP::D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, 10);
ID2D1Image* img = nullptr;
target->GetTarget(&img); // Checked, img is not nullptr, after call
blur->SetInput(0, img);
target->DrawImage(blur); // DrawImage(img) also draws white
// If to remove DrawImage call, red rectangle on blue background will be displayed,
// yet, of course, not blurred
SafeRelease(&blur);
auto hr = target->EndDraw();
if (hr == D2DERR_RECREATE_TARGET)
{
hr = S_OK;
Init();
}
}
int WINAPI WinMain(HINSTANCE hin, HINSTANCE, LPSTR, int)
{
ReleaseDC(NULL, hdcDevice);
WNDCLASS c = { NULL };
c.lpszClassName = L"GROKEN";
c.lpfnWndProc = WndProc;
c.hInstance = hin;
c.style = CS_VREDRAW | CS_HREDRAW;
c.hCursor = LoadCursor(NULL, IDC_ARROW);
c.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
RegisterClass(&c);
int cx = 500, cy = 500;
int x = xw / 2 - cx / 2, y = yw / 2 - cy / 2;
hwnd = CreateWindowEx(NULL, L"GROKEN", L"asd", WS_POPUP | WS_VISIBLE, x, y, cx, cy, NULL, NULL, hin, 0);
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
CoInitialize(NULL);
Init();
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
Render();
TranslateMessage(&msg);
DispatchMessage(&msg);
}
CoUninitialize();
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
switch (message)
{
default:
return DefWindowProc(hwnd, message, wp, lp);
}
return NULL;
}
More particulary, in my case I have array of pointers to ui controll class objects, each has own Render(ID2D1DeviceContext*), so window's render loop looks like this:
inline void WINDOW::Render()
{
Init(); // (re)Initialize target as ID2D1DeviceContext*, if needed;
target->BeginDraw();
target->Clear(D2D1::ColorF(D2D1::ColorF::Blue));
target->SetTransform(D2D1::Matrix3x2F::Identity());
for(int i = 0; i < ui_elements_count; i++)
ui_element[i]->Draw(target);
this->hr = target->EndDraw();
}
...
inline void UI_ELEMENT::Draw(ID2D1DeviceContext *target)
{
...
if(this->blurRadius > 0)
{
BlurRectangle(this->x, this->y, this->cx, this->cy, this->blurRadius);
}
}
Update
Using Simon's Mourier answer I tried to create the solution I need, but stuck, trying to SetInput backbuffer' bitmap to ID2D1Effect*. For some reasone, it either does not set in SetInput method correctly, or in DrawImage call. The saddest thing is that backbuffer bitmap is actually valid, it could be drawn by same DrawImage call. Maybe I should specify another bitmap options when create it?
// other code is same
ID2D1DeviceContext* target;
void Init()
{
Release();
m_pRenderTarget = NULL;
RECT rc;
GetClientRect(hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top);
// Create a Direct2D render target.
SUCCEEDED(m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(hwnd, size),
&m_pRenderTarget));
m_pRenderTarget->QueryInterface(&target);
}
inline void Blur(ID2D1DeviceContext* backTarget, int rad, RECT r)
// r is not used, should contain element bound box
{
RECT rc;
GetClientRect(hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top);
// Draw rectangle for test
backTarget->FillRectangle(D2D1::RectF(30, 30, 100, 100), brush);
ID2D1Bitmap1* bb = nullptr;
// Create bitmap
backTarget->CreateBitmap(size, 0, 0, D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)
), &bb);
// Copy current taget's state to created bitmap
bb->CopyFromRenderTarget(0, backTarget, 0);
ID2D1Effect* blur = nullptr;
target->CreateEffect(CLSID_D2D1GaussianBlur, &blur);
blur->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, rad);
blur->SetInput(0, bb);
// Draw blurred result. Does nothing
backTarget->DrawImage(blur);
// Just test if bb is valid, draw
// it with some offset.
// Draws correctly
auto a = D2D1::Point2F(100, 0);
backTarget->DrawImage(bb, a);
SafeRelease(&blur);
}
inline void Render()
{
RECT rc;
GetClientRect(hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top);
ID2D1BitmapRenderTarget* tar = nullptr; // Create back buffer
target->CreateCompatibleRenderTarget(&tar);
ID2D1DeviceContext* tt = nullptr;
// Get exactly back buffer as ID2D1DeviceContext*,
// because it has more draw calls, such as DrawImage()
tar->QueryInterface(&tt);
tt->CreateSolidColorBrush(
D2D1::ColorF(255, 0, 0),
&brush
);
tt->BeginDraw();
tt->Clear(D2D1::ColorF(D2D1::ColorF::Blue));
tt->SetTransform(D2D1::Matrix3x2F::Identity());
// loop through ui elements should here,
// assume we have an element with blur needed
Blur(tt, 10, RECT());
tt->EndDraw();
target->BeginDraw();
ID2D1Bitmap* bmp = nullptr;
tar->GetBitmap(&bmp);
target->DrawImage(bmp); // Draw back buffer to target
target->EndDraw();
SafeRelease(&tar);
SafeRelease(&tt);
SafeRelease(&bmp);
SafeRelease(&brush);
}
There are multiple issues in your code, but the main reason it doesn't work is because you can't use the device context's target bitmap (GPU resource) as a source (since it's a target).
Your code doesn't check for errors (you should) so for example you don't see the error from this call:
auto hr = target->EndDraw();
which returns error D2DERR_INVALID_GRAPH_CONFIGURATION:
The solution is therefore to create an intermediary bitmap (render target), render on it, and draw that bitmap on the target device context, something like this:
// at device context init time
target->CreateCompatibleRenderTarget(&bitmapTarget);
// create blur & set bitmap as input
deviceContext->CreateEffect(CLSID_D2D1GaussianBlur, &blur);
//get bitmap from bitmap rt
bitmapTarget->GetBitmap(&bitmap);
blur->SetInput(0, bitmap);
// at render time
void Render()
{
// draw to bitmap rt
bitmapTarget->BeginDraw();
bitmapTarget->Clear(D2D1::ColorF(D2D1::ColorF::Blue));
D2D1_SIZE_F rtSize = deviceContext->GetSize();
bitmapTarget->FillRectangle(D2D1::RectF(30, 30, 100, 100), brush);
bitmapTarget->EndDraw();
// draw to dc
deviceContext->BeginDraw();
// draw bitmap + effect
deviceContext->DrawImage(blur);
deviceContext->EndDraw();
}
And here is the result:
FWIW, I have put a complete correct code here:
#include <Windows.h>
#include <stdlib.h>
#include <d2d1_1.h>
#include <d2d1helper.h>
#pragma comment(lib, "d2d1")
#pragma comment(lib, "dxguid.lib")
template<class Interface>
inline void SafeRelease(Interface** ppInterfaceToRelease)
{
if (*ppInterfaceToRelease)
{
(*ppInterfaceToRelease)->Release();
(*ppInterfaceToRelease) = NULL;
}
}
ID2D1DeviceContext* deviceContext;
ID2D1SolidColorBrush* brush;
ID2D1BitmapRenderTarget* bitmapTarget;
ID2D1Effect* blur;
ID2D1Bitmap* bitmap;
void Release()
{
SafeRelease(&bitmapTarget);
SafeRelease(&deviceContext);
SafeRelease(&brush);
SafeRelease(&blur);
SafeRelease(&bitmap);
}
void Init(HWND hwnd)
{
Release();
ID2D1Factory* factory;
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
RECT rc;
GetClientRect(hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
// Create a Direct2D render deviceContext.
ID2D1HwndRenderTarget* renderTarget;
factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hwnd, size), &renderTarget);
renderTarget->QueryInterface(&deviceContext);
renderTarget->CreateSolidColorBrush(D2D1::ColorF(255, 0, 0), &brush);
renderTarget->CreateCompatibleRenderTarget(&bitmapTarget);
// create blur & set bitmap as input
deviceContext->CreateEffect(CLSID_D2D1GaussianBlur, &blur);
//get bitmap from bitmap rt
bitmapTarget->GetBitmap(&bitmap);
blur->SetInput(0, bitmap);
SafeRelease(&renderTarget);
SafeRelease(&factory);
}
void Render()
{
// draw to bitmap rt
bitmapTarget->BeginDraw();
bitmapTarget->Clear(D2D1::ColorF(D2D1::ColorF::Blue));
D2D1_SIZE_F rtSize = deviceContext->GetSize();
bitmapTarget->FillRectangle(D2D1::RectF(30, 30, 100, 100), brush);
bitmapTarget->EndDraw();
// draw to dc
deviceContext->BeginDraw();
// draw bitmap + effect
deviceContext->DrawImage(blur);
deviceContext->EndDraw();
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wp, lp);
}
return NULL;
}
int WINAPI WinMain(HINSTANCE hin, HINSTANCE, LPSTR, int)
{
WNDCLASS c = { NULL };
c.lpszClassName = L"GROKEN";
c.lpfnWndProc = WndProc;
c.hInstance = hin;
c.style = CS_VREDRAW | CS_HREDRAW;
c.hCursor = LoadCursor(NULL, IDC_ARROW);
c.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
RegisterClass(&c);
HDC hdcDevice = GetDC(NULL);
int xw = GetDeviceCaps(hdcDevice, HORZRES);
int yw = GetDeviceCaps(hdcDevice, VERTRES);
int cx = 500, cy = 500;
int x = xw / 2 - cx / 2, y = yw / 2 - cy / 2;
HWND hwnd = CreateWindowEx(NULL, L"GROKEN", L"asd", WS_OVERLAPPEDWINDOW | WS_VISIBLE, x, y, cx, cy, NULL, NULL, hin, 0);
ShowWindow(hwnd, SW_SHOW);
Init(hwnd);
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
Render();
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
I'm working with the win32 API, and am using it to make a window. This window works, but when I open it, the cursor is a loading cursor, and every time I bring my cursor to the edge to resize it, the cursor gets 'stuck' as that resizing cursor, it doesn't go back to normal. Here's a video to explain what I'm talking about:
Here's the reproducible example (compile with g++ reproducible_example.cpp -mwindows -O3 -o reproducible_example.exe):
#undef UNICODE
#undef _UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
bool isRunning = true;
void *buffer; // buffer memory
BITMAPINFO bmi; // bit map information, needed for rendering
int width, height; // main window's width and height
LRESULT __stdcall WindowProc(HWND, UINT, WPARAM, LPARAM);
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR pCmdLine, int nCmdShow) {
LPCSTR CLASS_NAME = "Class Name";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowExA(0, // Optional window styles.
"Class Name", // Window class
"Window", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, 1280, 720,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
return 0;
ShowWindow(hwnd, nCmdShow);
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
// Run the message loop.
HDC hdc = GetDC(hwnd);
while (isRunning) {
MSG msg;
if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
StretchDIBits(hdc, 0, 0, width, height, 0, 0, width, height, buffer, &bmi,
DIB_RGB_COLORS, SRCCOPY);
}
ReleaseDC(hwnd, hdc);
return 0;
}
LRESULT __stdcall WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
isRunning = false;
return 0;
case WM_SIZE: {
// Calculate window height and width
width = LOWORD(lParam);
height = HIWORD(lParam);
if (buffer) // If memory already exists
// free it
VirtualFree(buffer, 0, MEM_RELEASE);
// Allocate buffer memory
buffer = VirtualAlloc(0, width * height * sizeof(unsigned int),
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = height;
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Now, I know this isn't some weird windows bug and is definitely something with my code, because it doesn't happen in other apps which I open, and it also didn't happen when I made an equivalent window with SFML (probably because the windows api and SFML are entirely different things). Code for that window:
#include <SFML/Graphics.hpp>
int main() {
sf::RenderWindow window(sf::VideoMode(1280, 720), "Window");
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) {
window.close();
}
}
window.clear();
window.display();
}
}
I want the first window to act like the second window, but the cursor is the one discrepancy I can find. I've tried googling this, but to no avail. I followed a youtube tutorial series for the window I'm having problems with. Also, on downloading the tutorial's source code and removing some code which makes it full screen and hides the cursor, it has the same problem. How do I fix this? Thank you in advance.
Set wc.hCursor to something other than null. If it's null, the operating system will leave the cursor alone when it enters your window, and you're supposed to set it to the cursor you want.
Most likely you want the boring old arrow cursor. You can get that cursor by calling LoadCursor(NULL, IDC_ARROW).
I am able to create a window with a Tile. How can I add now new text line inside the Window ?
All what I succeed to did was only to change the title of the window which is not what I want . I want to add some text line in the window box.
SendMessage function was not working for me.
Please if somebody has some tip for this to tell me !
#include <windows.h>
const char g_szClassName[] = "myWindowClass";
//The Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
// Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"Title of window",
WS_OVERLAPPEDWINDOW,
1390, 540, 240, 120,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
To draw text in the client area, your wndProc would normally use something like DrawText or TextOut. You typically do that in response to WM_PAINT.
To be able to respond to an external message, you'd typically send a message containing the text. The window would receive that, store (a copy of) the text it received, and (usually) invalidate the window's rectangle. Since the window is now invalidated, the next chance it gets, Windows will send your window a WM_PAINT message (and then you'll draw out the text).
Handling the WM_PAINT message and drawing the text directly on the window's HDC is one option.
Another option is to create a child STATIC control in your window, and then you can assign the desired text to that child using SetWindowText() or the WM_SETTEXT message. No manual drawing needed.
In the end I figure out how to finish this:
win32 app picture
#ifndef UNICODE
#define UNICODE
#endif
using namespace std;
#include <windows.h>
#include <iostream>
#include <fstream>
#include <string>
int X_Coordinate = 215;
int Y_Coordinate = 415;
int Width = 700;
int Height = 500;
char Text[] = {"abc123"};
char Window_Title[] = "My title";
char Window_Image[] = "D:\\bitmap1.bmp";
const char* csWindow_Title = Window_Title;
const char* csWindow_Image = Window_Image;
HBITMAP bitmap; // Creates bitmap object based on a handle to a Windows Windows Graphics Device Interface (GDI) bitmap and a handle to a GDI palette.
// Utilities
bool ConvertConstChartoLPWSTR (const char* as , wchar_t* wString )
{
memset(wString,0,sizeof(wString));
MultiByteToWideChar(CP_ACP, 0, as, -1, wString, 4096);
return wString;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
//Registering the Window Class
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255)); // set window background color ( RGB ) - white
RegisterClass(&wc); // register the window class with the operating system
wchar_t* wWindow_Title=new wchar_t[4096];
memset(wWindow_Title,0,sizeof(wWindow_Title)); // init variable
ConvertConstChartoLPWSTR(csWindow_Title,wWindow_Title); // convert
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
wWindow_Title, // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
X_Coordinate, Y_Coordinate, Width, Height,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// ----------------------------- START -> SWITCH case -----------------------------------------------------
switch (uMsg)
{
// ----------------------------- START -> case WM_DESTROY -------------------------------------------------
case WM_DESTROY:
PostQuitMessage(0);
return 0;
// ----------------------------- END -> case WM_DESTROY ---------------------------------------------------
// ----------------------------- START -> case WM_PAINT ---------------------------------------------------
case WM_PAINT:
{
wchar_t* wWindow_Image=new wchar_t[4096];
memset(wWindow_Image,0,sizeof(wWindow_Image));
ConvertConstChartoLPWSTR(csWindow_Image,wWindow_Image); // convert
bitmap=(HBITMAP)LoadImage(NULL, // A handle to the module that contains the image to be loaded. To load a stand-alone resource (icon, cursor, or bitmap file)—for example, c:\myimage.bmp — set this parameter to NULL
wWindow_Image, // The image to be loaded.
IMAGE_BITMAP, // The type of image to be loaded.
690, // The width, in pixels, of the icon or cursor.
540, // he height, in pixels, of the icon or cursor.
LR_LOADFROMFILE); //Loads the stand-alone image from the file specified by lpszName (icon, cursor, or bitmap file).
PAINTSTRUCT ps; // declare structure with information for an application
HDC hdc = BeginPaint(hwnd, &ps); // prepare the specified window for painting
int index = sizeof(Text);
HDC hMemDC=CreateCompatibleDC(hdc); // create a compatible DC ( hMemDC ) o be the same like another one ( hdc )
::SelectObject(hMemDC,bitmap); // Selects an object into the specified device context (DC). The new object replaces the previous object of the same type.
long retval=SetTextAlign(hdc,TA_TOP); // alignment of written area
const char* theval;
int u = 5;
for(int b = 0; b < sizeof(Text); b++)
{
string sym(1, Text[b]); // convert char to const char*
theval = sym.c_str();
cout<<b<<theval;
wchar_t wtext[sizeof(Text)];
memset(wtext,0,sizeof(wtext));
ConvertConstChartoLPWSTR(theval,wtext); // convert
// Here application is laid out.
TextOut (hdc, 5, u, wtext, sizeof(Text));
u = u + 15;
}
index = index + u; // claculate the size of written area
BitBlt( hdc, // handler
0, // The x-coordinate, in logical units, of the upper-left corner of the destination rectangle.
index, // The y-coordinate, in logical units, of the upper-left corner of the destination rectangle.
700, // The width, in logical units, of the source and destination rectangles.
980, // The height, in logical units, of the source and the destination rectangles.
hMemDC, // handler for source ( image ).
0, // The x-coordinate, in logical units, of the upper-left corner of the source rectangle.
0, // The y-coordinate, in logical units, of the upper-left corner of the source rectangle.
SRCCOPY ); // A raster-operation code. These codes define how the color data for the source rectangle is to be combined with the color data for the destination rectangle to achieve the final color.
// SRCCOPY - Copies the source rectangle directly to the destination rectangle.
EndPaint(hwnd, &ps); // function marks the end of painting in the specified window
}
return 0;
// ----------------------------- END -> case WM_PAINT ---------------------------------------------------
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
// ----------------------------- END -> SWITCH case -----------------------------------------------------
} // END -> LRESULT CALLBACK WindowProc
It annoys me that the resizing of windows in Windows is not as "smooth" as it I'd like it to be (this is the case with Windows programs in general, not just my own. Visual Studio is a good example). It makes the OS and its programs feel "flimsy" and "cheap" (yes, I care about how programs and user interfaces feel, in the same way I care about the sound and feel of closing a car door. It's a reflection of build quality), which in my view affects the overall UX and ultimately the perception of the brand.
The redrawing of window contents simply does not keep up with mouse movement during resize. Whenever I resize a window, there is a "stuttering" / "flickering" effect, seemingly due to the previous-size-contents of the window being redrawn in the new, resized window frame before the new, resized contents are drawn.
I am building a Win32 application (x64) that uses Direct2D 1.1 to draw its UI, and given the speed of Direct2D, i think it should be unnecessary to suffer such artifacts in an OS in 2014. I am on Windows 8.1 myself, but targeting Windows 7 and up with this application.
The "previous size" effect is especially discernible when maximizing a small window (since the difference in window size is sufficiently great to easily contrast the image of the old content as it flashes briefly in the upper left corner of the larger window with the new content subsequently being painted over it).
This is what appears to be going on:
(Let's say there's a fully rendered window on screen, size 500 x 500 pixels).
I maximize the window:
The window frame is maximized
The old 500 x 500 content is drawn in the new frame, before..
..the maximized window is repainted with properly sized content.
I'm wondering if there's any way to mitigate this (i.e. get rid of step 4) - via intercepting a Windows Message, for example - and avoid the window being repainted at the new size with the old content before the final re-rendering of the new content happens. It's like Windows does the window redrawing itself, using whatever graphics it already has available, BEFORE it bothers to ask me to provide updated content with a WM_PAINT message or similar.
Can it be done?
Edit: It seems that WM_WINDOWPOSCHANGING / WM_SIZING provides "early access" to the new size data, but I still haven't managed to suppress the painting of the old content.
My WndProc looks like this:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
D2DRender();
EndPaint(hWnd, &ps);
return 0;
case WM_SIZE:
if (DeviceContext && wParam != SIZE_MINIMIZED)
{
D2DResizeTargetBitmap();
D2DRender();
}
return 0;
case WM_DISPLAYCHANGE:
D2DRender();
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
The window does not have CS_HREDRAW or CS_VREDRAW set. The swapchain is double buffered and the Present call is made with SyncInterval = 0.
I am aware that recreating the swapchain buffers every time the window size changes does create some overhead compared to plain redrawing on a static window surface. However, the "stuttering" is not caused by that, as it happens even when buffer resizing is disabled and existing window contents are simply scaled during window resizing (although that does make it keep up better with mouse movement).
There is a way to prevent the needless BitBlt mentioned in step 4 above.
Until Windows 8 it could be done either by creating your own custom implementation of WM_NCCALCSIZE to tell Windows to blit nothing (or to blit one pixel on top of itself), or alternately you could intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing. This has the same eventual effect of skipping the BitBlt.
However, nothing can be so simple. With the advent of Windows 8/10 Aero, apps now draw into an offscreen buffer which is then composited by the new, evil DWM.exe window manager. And it turns out DWM.exe will sometimes do its own BitBlt type operation on top of the one already done by the legacy XP/Vista/7 code. And stopping DWM from doing its blit is much harder; so far I have not seen any complete solutions.
So you need to get through both layers. For sample code that will break through the XP/Vista/7 layer and at least improve the performance of the 8/10 layer, see:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
What if you have a borderless childwindow (the type that only renders inside the parent) at a fixed size (same as fullscreen resolution), you should get a lot smoother results because there is no memory reallocation (which i think is what causes the jitterieness).
If it's still not perfect, look into both WM_SIZE and WM_SIZING and check if you can do some magic with them. For instance, on WM_SIZING you could return true telling Windows you handled the message (leaving the window as is) and you re-render your UI to a buffer with the size provided by WM_SIZING and when that is done, you send your own WM_SIZING but with a manipulated unused bit in WPARAM (along with its previous content) that tells you you have a pre-rendered buffer for this that you can just blit out. From the WM_SIZING documentation on msdn it looks like WPARAM should have a couple of bits at your disposal.
Hope this helps.
When calling CreateSwapChainForHwnd, ensure that you have set the swap chain description Scaling property to DXGI_SCALING_NONE. This is only supported on Windows 7 with the Platform Update, so you may need to fall back to the default DXGI_SCALING_STRETCH (the latter is what is causing the flickering).
Set WM_SETREDRAW to FALSE, do your resizing, then reenable drawing, invalidate the window and the OS will blit it.
I've done this for button enabling and disabling buttons when selecting different items from a list, never for an entire window.
This is the best I've come up with and resizes great although the backbuffer blitting causes some edge flickering, haven't tested with DX or OGL yet but it should work even better with hardware acceleration. It's a bit bulky but will do as a proof of concept.
If the canvas could be clipped without using MDI that would be even better, like using a bitmask buffer.
One thing i'm not happy about is the position coords of the child window because they might not work on all systems, but a combination of GetSystemMetrics calls to get border and caption sizes should fix that.
/* Smooth resizing of GDI+ MDI window
*
* Click window to resize, hit Escape or Alt+F4 to quit
*
* Character type is set to multibyte
* Project->Properties->Config Properties->General->Character Set = Multibyte
*
* Pritam 2014 */
// Includes
#include <Windows.h>
#include <gdiplus.h>
#pragma comment (lib,"Gdiplus.lib")
using namespace Gdiplus;
// Max resolution
#define XRES 1600
#define YRES 900
// Globals
bool resizing = false;
HWND parent, child; // child is the canvas window, parent provides clipping of child
Bitmap * buffer;
// Render
void Render() {
// Get parent client size
RECT rc;
GetClientRect(parent, &rc);
// Draw backbuffer
Graphics * g = Graphics::FromImage(buffer);
// Clear buffer
g->Clear(Color(100, 100, 100));
// Gray border
Pen pen(Color(255, 180, 180, 180));
g->DrawRectangle(&pen, 10, 10, rc.right - 20, rc.bottom - 20);
pen.SetColor(Color(255, 0, 0, 0));
g->DrawRectangle(&pen, 0, 0, rc.right - 1, rc.bottom - 1);
// Draw buffer to screen
PAINTSTRUCT ps;
HDC hdc = BeginPaint(child, &ps);
Graphics graphics(hdc);
graphics.DrawImage(buffer, Point(0, 0));
// Free
EndPaint(child, &ps);
}
// MDI Callback
LRESULT CALLBACK MDICallback(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
switch(message) {
case WM_LBUTTONDOWN:
resizing = true; // Start resizing
return 0;
break;
case WM_KEYDOWN:
if(wparam == VK_ESCAPE) { // Exit on escape
PostQuitMessage(0);
}
TranslateMessage((const MSG *)&message);
return 0;
break;
case WM_PAINT:
Render();
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
}
return DefMDIChildProc(hwnd, message, wparam, lparam);
}
// Parent window callback
LRESULT CALLBACK WndCallback(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
return DefFrameProc(hwnd, child, message, wparam, lparam);
}
// Create windows
bool CreateWindows(void) {
// Parent class
WNDCLASSEX wndclass;
ZeroMemory(&wndclass, sizeof(wndclass)); wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_NOCLOSE;
wndclass.lpfnWndProc = WndCallback;
wndclass.hInstance = GetModuleHandle(NULL);
wndclass.lpszClassName = "WNDCALLBACKPARENT";
wndclass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
if(!RegisterClassEx(&wndclass)) return false;
// MDI class
wndclass.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wndclass.lpfnWndProc = MDICallback;
wndclass.lpszClassName = "MDICALLBACKCANVAS";
if(!RegisterClassEx(&wndclass)) return false;
// Parent window styles
DWORD style = WS_POPUP | WS_CLIPCHILDREN;
DWORD exstyle = 0;
// Set initial window size and position
RECT rc;
rc.right = 640;
rc.bottom = 480;
AdjustWindowRectEx(&rc, style, false, exstyle);
rc.left = 20;
rc.top = 20;
// Create window
if(!(parent = CreateWindowEx(exstyle, "MDICLIENT", "MDI Resize", style, rc.left, rc.top, rc.right, rc.bottom, NULL, NULL, wndclass.hInstance, NULL))) return false;
// MDI window styles
style = MDIS_ALLCHILDSTYLES;
exstyle = WS_EX_MDICHILD;
// Set MDI size
rc.left = - 8; // The sizes occupied by borders and caption, if position is not correctly set an ugly caption will appear
rc.top = - 30;
rc.right = XRES;
rc.bottom = YRES;
AdjustWindowRectEx(&rc, style, false, exstyle);
// Create MDI child window
if(!(child = CreateWindowEx(exstyle, "MDICALLBACKCANVAS", "", style, rc.left, rc.top, rc.right, rc.bottom, parent, NULL, wndclass.hInstance, NULL))) return 8;
// Finalize
ShowWindow(child, SW_SHOW);
ShowWindow(parent, SW_SHOWNORMAL);
// Success
return true;
}
// Resize
void Resize(void) {
// Init
RECT rc, rcmdi;
GetClientRect(child, &rcmdi); // Use mdi window size to set max resize for parent
GetWindowRect(parent, &rc);
// Get mouse position
POINT mp;
GetCursorPos(&mp);
// Set new size
rc.right = mp.x - rc.left + 10;
rc.bottom = mp.y - rc.top + 10;
// Apply min & max size
if(rc.right < 240) rc.right = 240; if(rc.bottom < 180) rc.bottom = 180;
if(rc.right > rcmdi.right) rc.right = rcmdi.right; if(rc.bottom > rcmdi.bottom) rc.bottom = rcmdi.bottom;
// Update window size
SetWindowPos(parent, NULL, rc.left, rc.top, rc.right, rc.bottom, SWP_NOZORDER | SWP_NOMOVE);
// Make sure client is entirely repainted
GetClientRect(child, &rc);
InvalidateRect(child, &rc, false);
UpdateWindow(child);
// Stop resizing if mousebutton is up
if(!(GetKeyState(VK_LBUTTON) & 1 << (sizeof(short) * 8 - 1)))
resizing = false;
}
// Main
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE pinstance, LPSTR cmdline, int cmdshow) {
// Initiate GDI+
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
buffer = new Bitmap(XRES, YRES, PixelFormat24bppRGB);
// Create windows
if(!CreateWindows()) return 1;
// Main loop
bool running = true;
MSG message;
while(running) {
// Check message or pass them on to window callback
if(PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {
if(message.message == WM_QUIT) {
running = false;
} else {
if(!TranslateMDISysAccel(child, &message)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
}
}
// Resize
if(resizing)
Resize();
// Sleep a millisecond to spare the CPU
Sleep(1);
}
// Free memmory and exit
delete buffer;
GdiplusShutdown(gdiplusToken);
return 0;
}
Edit: Another example using "bitmask"/layered window.
// Escape to quit, left mousebutton to move window, right mousebutton to resize.
// And again char set must be multibyte
// Include
#include <Windows.h>
#include <gdiplus.h>
#pragma comment (lib,"Gdiplus.lib")
using namespace Gdiplus;
// Globals
Bitmap * backbuffer;
int xres, yres;
bool move, size;
POINT framePos, frameSize, mouseOffset;
// Renders the backbuffer
void Render(void) {
if(!backbuffer) return;
// Clear window with mask color
Graphics * gfx = Graphics::FromImage(backbuffer);
gfx->Clear(Color(255, 0, 255));
// Draw stuff
SolidBrush brush(Color(120, 120, 120));
gfx->FillRectangle(&brush, framePos.x, framePos.y, frameSize.x, frameSize.y);
}
// Paints the backbuffer to window
void Paint(HWND hwnd) {
if(!hwnd) return;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
Graphics gfx(hdc);
gfx.DrawImage(backbuffer, Point(0, 0));
EndPaint(hwnd, &ps);
}
void HandleMove(HWND hwnd) {
// Get mouse position
POINT mouse;
GetCursorPos(&mouse);
// Update frame position
framePos.x = mouse.x - mouseOffset.x;
framePos.y = mouse.y - mouseOffset.y;
// Redraw buffer and invalidate & update window
Render();
InvalidateRect(hwnd, NULL, false);
UpdateWindow(hwnd);
// Stop move
if(!(GetKeyState(VK_LBUTTON) & 1 << (sizeof(short) * 8 - 1)))
move = false;
}
void HandleSize(HWND hwnd) {
// Get mouse position
POINT mouse;
GetCursorPos(&mouse);
// Update frame size
frameSize.x = mouse.x + mouseOffset.x - framePos.x;
frameSize.y = mouse.y + mouseOffset.y - framePos.y;
//frameSize.x = mouse.x + mouseOffset.x;
//frameSize.y = mouse.y + mouseOffset.y;
// Redraw buffer and invalidate & update window
Render();
InvalidateRect(hwnd, NULL, false);
UpdateWindow(hwnd);
// Stop size
if(!(GetKeyState(VK_RBUTTON) & 1 << (sizeof(short) * 8 - 1)))
size = false;
}
LRESULT CALLBACK WindowCallback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
POINTS p;
switch(msg) {
case WM_KEYDOWN:
if(wparam == VK_ESCAPE) PostQuitMessage(0);
return 0;
break;
case WM_LBUTTONDOWN:
p = MAKEPOINTS(lparam); // Get mouse coords
mouseOffset.x = p.x - framePos.x;
mouseOffset.y = p.y - framePos.y;
move = true;
break;
case WM_RBUTTONDOWN:
p = MAKEPOINTS(lparam);
mouseOffset.x = framePos.x + frameSize.x - p.x;
mouseOffset.y = framePos.y + frameSize.y - p.y;
size = true;
break;
case WM_PAINT:
Paint(hwnd);
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
// Main
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE pinstance, LPSTR cmdline, int cmdshow) {
// Init resolution, frame
xres = GetSystemMetrics(SM_CXSCREEN);
yres = GetSystemMetrics(SM_CYSCREEN);
move = false; size = false;
framePos.x = 100; framePos.y = 80;
frameSize.x = 320; frameSize.y = 240;
mouseOffset.x = 0; mouseOffset.y = 0;
// Initiate GDI+
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// Init backbuffer
backbuffer = ::new Bitmap(xres, yres, PixelFormat24bppRGB);
Render();
// Window class
WNDCLASSEX wc; ZeroMemory(&wc, sizeof(wc)); wc.cbSize = sizeof(wc);
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = WindowCallback;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = "SingleResizeCLASS";
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
if(!RegisterClassEx(&wc)) return 1;
// Create window
HWND hwnd;
DWORD style = WS_POPUP;
DWORD exstyle = WS_EX_LAYERED;
if(!(hwnd = CreateWindowEx(exstyle, wc.lpszClassName, "Resize", style, 0, 0, xres, yres, NULL, NULL, wc.hInstance, NULL)))
return 2;
// Make window fully transparent to avoid the display of unpainted window
SetLayeredWindowAttributes(hwnd, 0, 0, LWA_ALPHA);
// Finalize
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
// Make window fully opaque, and set color mask key
SetLayeredWindowAttributes(hwnd, RGB(255, 0, 255), 0, LWA_COLORKEY);
// Main loop
MSG msg;
bool running = true;
while(running) {
// Check message
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if(msg.message == WM_QUIT) {
running = false;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Move or size frame
if(move) { HandleMove(hwnd); }
if(size) { HandleSize(hwnd); }
Sleep(1);
}
// Free memory
::delete backbuffer;
backbuffer = NULL;
GdiplusShutdown(gdiplusToken);
// Exit
return 0;
}
While your aim is laudable, I suspect that any attempt to do this will just end up as a fight between you and Windows - which you will not win (though you might manage to fight your way to an honourable draw). Sorry to be negative.
We are learning about windows raw touch events in class using Visual Studios 2012 in C++. I got my demo working and it does exactly what it is supposed to, which is drawing circles beneath your fingertips to detect that a touch event has been raised. But after a certain time has passed and if you are still pressing on the screen with the circles drawn the screen turns blue! The circles still show but they are blue as well with black contours and I can still move them around. I showed the professor and he can't seem to figure it out which is why I came here. Can anyone take a look at my code to let me know what seems to be the cause of it?
// GT_HelloWorldWin32.cpp
// compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c
#ifndef WINVER // Specifies that the minimum required platform is Windows 7.
#define WINVER 0x0601 // Change this to the appropriate value to target other versions of Windows.
#endif
#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows 7.
#define _WIN32_WINNT 0x0601 // Change this to the appropriate value to target other versions of Windows.
#endif
#include <windows.h> // for windows touch
#include <windowsx.h> // included for point conversion
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include "wtypes.h"
#include <iostream>
using namespace std;
// The main window class name.
TCHAR szWindowClass[] = _T("win32app");
// The string that appears in the application's title bar.
TCHAR szTitle[] = _T("Hello World!");
//Instancing the handler
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//Maximum ammount of touches allowed
#define MAXPOINTS 10
// You will use this array to track touch points
int points[MAXPOINTS][2];
// You will use this array to switch the color / track ids
int idLookup[MAXPOINTS];
// You can make the touch points larger
// by changing this radius value
static int radius = 30;
// There should be at least as many colors
// as there can be touch points so that you
// can have different colors for each point
COLORREF colors[] = { RGB(153,255,51),
RGB(153,0,0),
RGB(0,153,0),
RGB(255,255,0),
RGB(255,51,204),
RGB(0,0,0),
RGB(0,153,0),
RGB(153, 255, 255),
RGB(153,153,255),
RGB(0,51,153)
};
int wmId, wmEvent, i, x, y, index;
UINT cInputs;
PTOUCHINPUT pInputs;
POINT ptInput;
// This function is used to return an index given an ID
int GetContactIndex(int dwID){
for (int i=0; i < MAXPOINTS; i++){
if (idLookup[i] == -1){
idLookup[i] = dwID;
return i;
}else{
if (idLookup[i] == dwID){
return i;
}
}
}
// Out of contacts
return -1;
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX wcex;
int width = 0, height = 0; // Screen resolution
GetScreenResolution(width, height);
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}
hInst = hInstance; // Store instance handle in our global variable
// The parameters to CreateWindow explained:
// szWindowClass: the name of the application
// szTitle: the text that appears in the title bar
// WS_OVERLAPPEDWINDOW: the type of window to create
// CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y)
// 500, 100: initial size (width, length)
// NULL: the parent of this window
// NULL: this application does not have a menu bar
// hInstance: the first parameter from WinMain
// NULL: not used in this application
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
800, 600,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd) {
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}
// register the window for touch instead of gestures
RegisterTouchWindow(hWnd, 0);
// the following code initializes the points
for (int i=0; i< MAXPOINTS; i++){
points[i][0] = -1;
points[i][1] = -1;
idLookup[i] = -1;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// Main message loop:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes touch messages for the main window.
//
// WM_TOUCH - handles WM_TOUCH messages in the application
// WM_DESTROY - post a quit message and return
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// For double buffering
static HDC memDC = 0;
static HBITMAP hMemBmp = 0;
HBITMAP hOldBmp = 0;
//For drawing / fills
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_TOUCH:
//LOWORD(wParam) = number of touch points in this message
//HIWORD(wParam) = reserved for future use
//lParam = handle for use with GetTouchInputInfo
cInputs = LOWORD(wParam);
pInputs = new TOUCHINPUT[ cInputs ];
if(pInputs)
{
if( GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT)) )
{
for (int i=0; i < static_cast<INT>(cInputs); i++)
{
TOUCHINPUT ti = pInputs[i];
index = GetContactIndex(ti.dwID);
if(ti.dwID != 0 && index < MAXPOINTS )
{
//get screen corrdinates of touch
ptInput.x = TOUCH_COORD_TO_PIXEL(ti.x);
ptInput.y = TOUCH_COORD_TO_PIXEL(ti.y);
//get coordinates relative to the top left of the application window
ScreenToClient(hWnd, &ptInput);
if(ti.dwFlags & TOUCHEVENTF_UP)
{
points[index][0] = -1;
points[index][1] = -1;
}
else
{
points[index][0] = ptInput.x;
points[index][1] = ptInput.y;
}
}
}
}
CloseTouchInputHandle((HTOUCHINPUT)lParam);
delete [] pInputs;
}
InvalidateRect(hWnd, NULL, FALSE);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
RECT client;
GetClientRect(hWnd, &client);
//START DOUBLE BUFFERING
if (!memDC)
{
memDC = CreateCompatibleDC(hdc);
}
hMemBmp = CreateCompatibleBitmap(hdc, client.right, client.bottom);
hOldBmp = (HBITMAP)SelectObject(memDC, hMemBmp);
FillRect(memDC, &client, CreateSolidBrush(RGB(255,255,255)));
//Draw Touched Points
for (i=0; i < MAXPOINTS; i++)
{
SelectObject( memDC, CreateSolidBrush(colors[i]));
x = points[i][0];
y = points[i][1];
if (x >0 && y>0)
{
Ellipse(memDC, x - radius, y - radius, x + radius, y + radius);
}
}
BitBlt(hdc, 0,0, client.right, client.bottom, memDC, 0,0, SRCCOPY);
EndPaint(hWnd, &ps);
ReleaseDC(hWnd, hdc);
//DeleteObject(hMemBmp);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I see the problem now. You have a GDI resource leak.
In two places you call CreateSolidBrush but you never delete the brushes you created. You do it here
FillRect(memDC, &client, CreateSolidBrush(RGB(255,255,255)));
and here
SelectObject( memDC, CreateSolidBrush(colors[i]));
Really, you should assign the result of eachCreateSolidBrush function to a HBRUSH and then call DeleteObject on it when you've finished with it.
You also need to release your bitmap: DeleteObject(hMemBmp) which you have commented out and also remove the call to ReleaseDC as I said in the comments.
Generally, you should keep a careful track of all of the GDI objects you have created and make sure you delete them when you've finished with them.