Can't draw compatible target bitmap as effect to this compatible target - c++

I want is to create backdrop-blur effect as in UWP apps in-app blur for my ui library, but I've been stuck with it for a few days.
For some reason, it either does not set backbuffer bitmap 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?
My current code:
#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_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 in future,
// so backtarget->DrawImage will only draw part that
// belongs to element's area
{
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, 10);
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 call, 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());
// for(int i = 0; i < ui_elements_count; i++)
// {
// ui_element->DrawBlurredAreaBehindIt(tt);
// ui_element->DrawInnerText(tt);
// ui_element->DrawBorder(tt);
// ui_element->DrawSomethingElse(tt);
// }
// loop through ui elements should be 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);
SafeRelease(&tar);
SafeRelease(&tt);
SafeRelease(&bmp);
SafeRelease(&brush);
target->EndDraw();
}
int WINAPI WinMain(HINSTANCE hin, HINSTANCE, LPSTR, int)
{
ReleaseDC(NULL, hdcDevice);
WNDCLASS c = { NULL };
c.lpszClassName = L"asd";
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"asd", L"asd", WS_POPUP | WS_VISIBLE, x, y, cx, cy, NULL, NULL, hin, 0);
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
CoInitialize(NULL);
SUCCEEDED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory));
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;
}
To obtain current drawing bitmap ID2D1BitmapRenderTarget* need to be created as backbuffer. But, this interface does not have all draw methods suchs as DrawImage, or CreateEffect, so I have tried to QueryInterface ID2D1DeviceContext from it, and it actually works.
For getting backbuffer bitmap I use ID2D1Bitmap::CopyFromRenderTarget because if to draw bitmap from ID2D1BitmapRenderTarget::GetBitmap it will draw just nothing.
Important update
I just changed blur effect to scale effect, and fortunately or unfortunately, with scale effect it works. Please, don't tell it is a direct2d bug, I sure I doing here something wrong.
I noticed that in all examples this effect created and initialized by SetInput and SetValue calls before the render loop, out of BeginDraw and EndDraw. Maybe after that calls the image is somehow prepared asynchronously, so it just does not have time be ready to be drawn being in render loop? But it sounds crazy.

You will not believe. YOU WILL NOT BELIEVE.
The problem was that, the the D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION value in blur->SetValue call was in int, and should in float. And it is not regular reverse case when You trying to pass 0.5 to some int value and it casted to 1. Just think about this stupidnes.
That's not a joke,
blur->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, 3.0f); // works
blur->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, 3); // does not work
What? Why? Because of what? - I don't know answer to these questions as You, seems like that's just how things work.
And yes, on MSDN it is written that D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION type is FLOAT, but how? How 10 can not work, but 10.0f works?

Related

Get current Direct2D render bitmap

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;
}

Layered window size bigger than the size of the contents

I want to create a layered window, the size of which is bigger than the size of its contents (hdcSrc). So that the window is for example 900x900, and we can place a 300x300 image inside of it anywhere we want, while the rest of the window is transparent.
Here is my code so far:
#include <Windows.h>
#include <string>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment( lib, "Gdiplus.lib" )
HWND hwnd;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
void updateTestWindow();
LRESULT CALLBACK windowProcedure(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
{
MSG msg;
// GDI+ elements
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
// Initialize GDI+.
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// Register Window Class
WNDCLASS testWindowClass;
testWindowClass.lpszClassName = TEXT("TestWindow");
testWindowClass.hInstance = hInstance;
testWindowClass.style = CS_HREDRAW | CS_VREDRAW;
testWindowClass.lpfnWndProc = windowProcedure;
testWindowClass.hIcon = 0;
testWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
testWindowClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
testWindowClass.cbClsExtra = 0;
testWindowClass.cbWndExtra = 0;
testWindowClass.lpszMenuName = NULL;
RegisterClass(&testWindowClass);
// Create a layered window
hwnd = CreateWindowEx(
WS_EX_LAYERED,
TEXT("TestWindow"), // window class name
TEXT("TestWindow"), // window caption
WS_POPUP, // window style
0, // initial x position
0, // initial y position
screenWidth, // initial x size
screenHeight, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hwnd, iCmdShow);
// Update the layered window using a custom function
updateTestWindow();
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
void updateTestWindow()
{
// Let's imagine that in reality we are drawing an image, but to test it, just draw a rectangle
int imageWidth = 300;
int imageHeight = 300;
HDC screenDC = GetDC(NULL);
// Create a DC for drawing
HDC drawingDC = CreateCompatibleDC(screenDC);
// Create a new bitmap for drawing on it
HBITMAP newBitmap = CreateCompatibleBitmap(screenDC, imageWidth, imageHeight);
// Select the new bitmap to draw on it and save the old one
HBITMAP oldBitmap = (HBITMAP)SelectObject(drawingDC, newBitmap);
// Create graphics object
Graphics graphics(drawingDC);
// Draw a rectangle
Pen redPen(Color(255, 255, 0, 0), 15);
Rect windowRect(0, 0, imageWidth, imageWidth);
graphics.DrawRectangle(&redPen, windowRect);
// The position of the layered window
POINT windowPosition = { 0, 0 };
// The size of the layered window
SIZE windowSize = { imageWidth, imageHeight }; // <----------------------------------!!!!
POINT drawingDCPosition = { 0,0 };
// Create a blend function
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
// Call UpdateLayeredWindow
UpdateLayeredWindow(hwnd, screenDC, &windowPosition, &windowSize,
drawingDC, &drawingDCPosition, 0, &blend, ULW_ALPHA);
// Clean up
SelectObject(drawingDC, oldBitmap);
DeleteObject(newBitmap);
DeleteDC(drawingDC);
ReleaseDC(NULL, screenDC);
}
This creates a window that is exactly the size of the image that's drawn on it.
We can reduce the window size: SIZE windowSize = { imageWidth-100, imageHeight-100 }; and the window will be drawn as expected - smaller size, while the image is the same size but now it's clipped (in this case, the rectangle is clipped).
However, if we want to achieve what I described in the beginning, SIZE windowSize = { imageWidth+100, imageHeight+100 }; doesn't work. The window is just not rendered at all, yet it opens (window taskbar icon is visible).
I probably just can't understand how a layered window works completely yet, which is why I can't understand why in this example it's not being rendered.
Is it possible to create such a layered window at all? And if yes, then how?
Or maybe this is not something I should be doing at all?
Just as a note: The reason I want to do this, is to use the layered window as a sort of an overlay, that would constantly be the size of the screen but the images, figures and text would be drawn on to it, updated, moved and so on.

Direct X Cropping Bitmap Based on Window Position and Rendering it back to window

I was working on a project which uses DirectX and Direct Composition. I am trying to create a similar effect like windows acrylic blur. Using direct composition I was able to apply the blur effect, saturation and blending to a given input. windows acrylic blur use the content behind the window as input to blur but it is not possible in win32 so I decided to use desktop background as input for my blur function. But the problem is that I was able to crop the background based on window position but if I moved the window to new position the bitmap is cropped based on the new RECT of window but there is flickering while moving to new position.
Here is the previews:
Problem : Flickering while moving the window
Solution: This is what I need to get
Concept : This is what I am trying to Achieve
Here is the screenshot of actual window:
Here the red box contains the bitmap saturated and cropped using the window RECT
(See the color difference).
Here is a simple code to reproduce the problem:
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <wrl.h>
#include <dxgi1_3.h>
#include <d3d11_2.h>
#include <d2d1_2.h>
#include <d2d1_1.h>
#include <d2d1_2helper.h>
#include <dcomp.h>
#include <dwmapi.h>
#include <wincodec.h>
#pragma comment(lib, "dxgi")
#pragma comment(lib, "d3d11")
#pragma comment(lib, "d2d1")
#pragma comment(lib, "dwmapi")
#pragma comment(lib, "dxguid")
#pragma comment(lib, "dcomp")
using namespace Microsoft::WRL;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
ComPtr<ID3D11Device> direct3dDevice;
ComPtr<IDXGIDevice> dxgiDevice;
ComPtr<IDXGIFactory2> dxFactory;
ComPtr<IDXGISwapChain1> swapChain;
ComPtr<ID2D1Factory2> d2Factory;
ComPtr<ID2D1Device1> d2Device;
ComPtr<IDCompositionTarget> target;
ComPtr<ID2D1DeviceContext> dc;
//Direct Composition Device,Visual
ComPtr<IDCompositionDevice> dcompDevice;
ComPtr<IDCompositionDevice3> dcompDevice3;
ComPtr<IDCompositionVisual> visual;
//Direct Composition Effects
ComPtr<IDCompositionGaussianBlurEffect> blur;
ComPtr<IDCompositionSaturationEffect> saturation;
IWICImagingFactory* d2dWICFactory = NULL;
IWICBitmapDecoder* d2dDecoder = NULL;
IWICFormatConverter* d2dConverter = NULL;
IWICBitmapFrameDecode* d2dBmpSrc = NULL;
ID2D1Bitmap* d2dBmp = NULL;
HWND hwnd;
RECT windowRect;
void LoadBackground();
void Render();
struct ComException
{
HRESULT result;
ComException(HRESULT const value) : result(value)
{}
};
void HR(HRESULT const result)
{
if (S_OK != result)
{
throw ComException(result);
}
}
void CreateDevice(HWND hwnd)
{
HR(D3D11CreateDevice(nullptr, // Adapter
D3D_DRIVER_TYPE_HARDWARE,
nullptr, // Module
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
nullptr, 0, // Highest available feature level
D3D11_SDK_VERSION,
&direct3dDevice,
nullptr, // Actual feature level
nullptr)); // Device context
HR(direct3dDevice.As(&dxgiDevice));
HR(CreateDXGIFactory2(
DXGI_CREATE_FACTORY_DEBUG,
__uuidof(dxFactory),
reinterpret_cast<void**>(dxFactory.GetAddressOf())));
DXGI_SWAP_CHAIN_DESC1 description = {};
description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
description.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
description.BufferCount = 2;
description.SampleDesc.Count = 1;
description.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
RECT rect = {};
GetClientRect(hwnd, &rect);
description.Width = rect.right - rect.left;
description.Height = rect.bottom - rect.top;
HR(dxFactory->CreateSwapChainForComposition(dxgiDevice.Get(),&description,nullptr,swapChain.GetAddressOf()));
D2D1_FACTORY_OPTIONS const options = { D2D1_DEBUG_LEVEL_INFORMATION };
HR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,options,d2Factory.GetAddressOf()));
HR(d2Factory->CreateDevice(dxgiDevice.Get(),d2Device.GetAddressOf()));
HR(d2Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE,dc.GetAddressOf()));
ComPtr<IDXGISurface2> surface;
HR(swapChain->GetBuffer(0,__uuidof(surface),reinterpret_cast<void**>(surface.GetAddressOf())));
D2D1_BITMAP_PROPERTIES1 properties = {};
properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
properties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
ComPtr<ID2D1Bitmap1> bitmap;
HR(dc->CreateBitmapFromDxgiSurface(surface.Get(),properties,bitmap.GetAddressOf()));
LoadBackground(); // loads my desktop background to d2dBmp using WIC
dc->SetTarget(bitmap.Get());
Render(); //render once
//Creating Direct Compostion Devices and Visual
HR(DCompositionCreateDevice(dxgiDevice.Get(),__uuidof(dcompDevice),reinterpret_cast<void**>(dcompDevice.GetAddressOf())));
HR(DCompositionCreateDevice3(dxgiDevice.Get(), __uuidof(dcompDevice),reinterpret_cast<void**>(dcompDevice.GetAddressOf())));
HR(dcompDevice->QueryInterface(__uuidof(IDCompositionDevice3), (LPVOID*)&dcompDevice3)); // use IDCompositionDevice3 here
HR(dcompDevice3->CreateSaturationEffect(saturation.GetAddressOf()));
HR(dcompDevice3->CreateGaussianBlurEffect(blur.GetAddressOf()));
//setting effect properties
blur->SetStandardDeviation(30.0f); // blur amount
blur->SetBorderMode(D2D1_BORDER_MODE_HARD);
saturation->SetSaturation(2.0f); //saturationamount
HR(dcompDevice->CreateTargetForHwnd(hwnd,true,target.GetAddressOf()));
blur->SetInput(NULL, bitmap.Get(), NULL);
saturation->SetInput(NULL, blur.Get(), NULL);
HR(dcompDevice->CreateVisual(visual.GetAddressOf()));
HR(visual->SetContent(swapChain.Get()));
visual->SetEffect(saturation.Get());
HR(target->SetRoot(visual.Get()));
HR(dcompDevice->Commit());
}
void LoadBackground()
{
HR(CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, __uuidof(IWICImagingFactory), (void**)(&d2dWICFactory)));
HR(d2dWICFactory->CreateDecoderFromFilename(L"C:/Users/selas/Downloads/wallpaper.jpg", NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &d2dDecoder));
HR(d2dWICFactory->CreateFormatConverter(&d2dConverter));
HR(d2dDecoder->GetFrame(0, &d2dBmpSrc));
HR(d2dConverter->Initialize(d2dBmpSrc, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut));
HR(dc->CreateBitmapFromWicBitmap(d2dConverter, NULL, &d2dBmp));
}
void Render()
{
if (dc)
{
dc->BeginDraw();
dc->Clear();
D2D1_POINT_2F offset = D2D1::Point2F(0, 0);
D2D1_RECT_F imgRect = D2D1::RectF(windowRect.left, windowRect.top, windowRect.right, windowRect.bottom);
dc->DrawImage(d2dBmp, offset, imgRect, D2D1_INTERPOLATION_MODE_LINEAR, D2D1_COMPOSITE_MODE_SOURCE_OVER);
HR(dc->EndDraw());
HR(swapChain->Present(1, 0));
}
}
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 = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
hwnd = CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP,
wc.lpszClassName, L"Sample",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 500,
nullptr, nullptr, hInstance, nullptr);
if (hwnd == NULL)
{
return 0;
}
CreateDevice(hwnd);
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOVING:
{
GetWindowRect(hwnd, &windowRect);
Render();
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
d2dWICFactory->Release();
d2dDecoder->Release();
d2dConverter->Release();
d2dBmpSrc->Release();
d2dBmp->Release();
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Please don't forget to replace "C:/Users/selas/Downloads/wallpaper.jpg" with your desktop wallpaper
Note: The sample videos are actually edited ones its not the output I've got.
Finally I figured it out. Now I am able to move the window without any flickering to the image I rendered as background. The problem was in two places in my code.
GetWindowRect():
In WM_MOVING I was using GetWindowRect() to get the window Rectangle, this causes delay to obtain the window Rectangle and caused the flickering issue. The Solution I found was simple, I Converted the lParm to RECT by typecasting and using it as the value to update the location solved the issue. The code I used to solve the issue is given below :
RECT *hostRect = reinterpret_cast<RECT*>(lParam)
MagSetWindowSource(): This is an interesting thing. I Don't know what is the use of magnifier in this. The code will not remove the flicker until I call MagSetWindowSource() function, I don't know why it is required, that alone consumes more memory in my program. if I removed this line of code it again starts to flicker. The input to the function doesn't matter you just have to call the function that's it.
Updated :
Finally figured out what is MagSetWindowSource() is doing, it actually
calls a DWM function called DwmFlush() which Issues a flush call that
blocks the caller until the next present. and that solved my issue.
now it is flickerless and smooth.
Here is the fully updated code:
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <wrl.h>
#include <dxgi1_3.h>
#include <d3d11_2.h>
#include <d2d1_2.h>
#include <d2d1_1.h>
#include <d2d1_2helper.h>
#include <dcomp.h>
#include <dwmapi.h>
#include <wincodec.h>
#include <magnification.h>
#pragma comment(lib, "dxgi")
#pragma comment(lib, "d3d11")
#pragma comment(lib, "d2d1")
#pragma comment(lib, "dwmapi")
#pragma comment(lib, "dxguid")
#pragma comment(lib, "dcomp")
#pragma comment(lib, "magnification")
using namespace Microsoft::WRL;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
ComPtr<ID3D11Device> direct3dDevice;
ComPtr<IDXGIDevice> dxgiDevice;
ComPtr<IDXGIFactory2> dxFactory;
ComPtr<IDXGISwapChain1> swapChain;
ComPtr<ID2D1Factory2> d2Factory;
ComPtr<ID2D1Device1> d2Device;
ComPtr<IDCompositionTarget> target;
ComPtr<ID2D1DeviceContext> dc;
//Direct Composition Device,Visual
ComPtr<IDCompositionDevice> dcompDevice;
ComPtr<IDCompositionDevice3> dcompDevice3;
ComPtr<IDCompositionVisual> visual;
//Direct Composition Effects
ComPtr<IDCompositionGaussianBlurEffect> blur;
ComPtr<IDCompositionSaturationEffect> saturation;
IWICImagingFactory* d2dWICFactory = NULL;
IWICBitmapDecoder* d2dDecoder = NULL;
IWICFormatConverter* d2dConverter = NULL;
IWICBitmapFrameDecode* d2dBmpSrc = NULL;
ID2D1Bitmap* d2dBmp = NULL;
HWND hwnd;
RECT windowRect,temp;
RECT* hostRect;
void LoadBackground();
void Render();
struct ComException
{
HRESULT result;
ComException(HRESULT const value) : result(value)
{}
};
void HR(HRESULT const result)
{
if (S_OK != result)
{
throw ComException(result);
}
}
void CreateDevice(HWND hwnd)
{
HR(D3D11CreateDevice(nullptr, // Adapter
D3D_DRIVER_TYPE_HARDWARE,
nullptr, // Module
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
nullptr, 0, // Highest available feature level
D3D11_SDK_VERSION,
&direct3dDevice,
nullptr, // Actual feature level
nullptr)); // Device context
HR(direct3dDevice.As(&dxgiDevice));
HR(CreateDXGIFactory2(
DXGI_CREATE_FACTORY_DEBUG,
__uuidof(dxFactory),
reinterpret_cast<void**>(dxFactory.GetAddressOf())));
DXGI_SWAP_CHAIN_DESC1 description = {};
description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
description.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
description.BufferCount = 2;
description.SampleDesc.Count = 1;
description.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
RECT rect = {};
GetClientRect(hwnd, &rect);
description.Width = rect.right - rect.left;
description.Height = rect.bottom - rect.top;
HR(dxFactory->CreateSwapChainForComposition(dxgiDevice.Get(), &description, nullptr, swapChain.GetAddressOf()));
D2D1_FACTORY_OPTIONS const options = { D2D1_DEBUG_LEVEL_INFORMATION };
HR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, options, d2Factory.GetAddressOf()));
HR(d2Factory->CreateDevice(dxgiDevice.Get(), d2Device.GetAddressOf()));
HR(d2Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, dc.GetAddressOf()));
ComPtr<IDXGISurface2> surface;
HR(swapChain->GetBuffer(0, __uuidof(surface), reinterpret_cast<void**>(surface.GetAddressOf())));
D2D1_BITMAP_PROPERTIES1 properties = {};
properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
properties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
ComPtr<ID2D1Bitmap1> bitmap;
HR(dc->CreateBitmapFromDxgiSurface(surface.Get(), properties, bitmap.GetAddressOf()));
LoadBackground(); // loads my desktop background to d2dBmp using WIC
dc->SetTarget(bitmap.Get());
Render(); //render once
//Creating Direct Compostion Devices and Visual
HR(DCompositionCreateDevice(dxgiDevice.Get(), __uuidof(dcompDevice), reinterpret_cast<void**>(dcompDevice.GetAddressOf())));
HR(DCompositionCreateDevice3(dxgiDevice.Get(), __uuidof(dcompDevice), reinterpret_cast<void**>(dcompDevice.GetAddressOf())));
HR(dcompDevice->QueryInterface(__uuidof(IDCompositionDevice3), (LPVOID*)&dcompDevice3)); // use IDCompositionDevice3 here
HR(dcompDevice3->CreateSaturationEffect(saturation.GetAddressOf()));
HR(dcompDevice3->CreateGaussianBlurEffect(blur.GetAddressOf()));
//setting effect properties
blur->SetStandardDeviation(0.0f); // blur amount
blur->SetBorderMode(D2D1_BORDER_MODE_HARD);
saturation->SetSaturation(2.0f); //saturationamount
HR(dcompDevice->CreateTargetForHwnd(hwnd, true, target.GetAddressOf()));
blur->SetInput(NULL, bitmap.Get(), NULL);
saturation->SetInput(NULL, blur.Get(), NULL);
HR(dcompDevice->CreateVisual(visual.GetAddressOf()));
HR(visual->SetContent(swapChain.Get()));
visual->SetEffect(saturation.Get());
HR(target->SetRoot(visual.Get()));
HR(dcompDevice->Commit());
}
void LoadBackground()
{
HR(CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, __uuidof(IWICImagingFactory), (void**)(&d2dWICFactory)));
HR(d2dWICFactory->CreateDecoderFromFilename(L"C:/Users/selas/Downloads/wallpaper.jpg", NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &d2dDecoder));
HR(d2dWICFactory->CreateFormatConverter(&d2dConverter));
HR(d2dDecoder->GetFrame(0, &d2dBmpSrc));
HR(d2dConverter->Initialize(d2dBmpSrc, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut));
HR(dc->CreateBitmapFromWicBitmap(d2dConverter, NULL, &d2dBmp));
}
void Render()
{
if (dc)
{
dc->BeginDraw();
dc->Clear();
if (hostRect)
{
D2D1_POINT_2F offset = D2D1::Point2F(0, 0);
D2D1_RECT_F imgRect = D2D1::RectF(hostRect->left, hostRect->top, hostRect->right, hostRect->bottom);
dc->DrawImage(d2dBmp, offset, imgRect, D2D1_INTERPOLATION_MODE_LINEAR, D2D1_COMPOSITE_MODE_SOURCE_OVER);
}
HR(dc->EndDraw());
HR(swapChain->Present(1, 0));
}
}
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 = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
hwnd = CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP,
wc.lpszClassName, L"Sample",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 500,
nullptr, nullptr, hInstance, nullptr);
if (hwnd == NULL)
{
return 0;
}
CreateDevice(hwnd);
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOVING:
{
hostRect = reinterpret_cast<RECT*>(lParam);
windowRect.left = hostRect->left;
windowRect.top = hostRect->top;
windowRect.right = hostRect->right;
windowRect.bottom = hostRect->bottom;
if (hostRect->left == 0 || hostRect->top == 0)
{
GetWindowRect(hwnd, &temp);
hostRect->left = temp.left;
hostRect->top = temp.top;
}
DwmFlush();
Render();
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
{
PostQuitMessage(0);
d2dWICFactory->Release();
d2dDecoder->Release();
d2dConverter->Release();
d2dBmpSrc->Release();
d2dBmp->Release();
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Windows API Rendering an Image From File (Beginner)

I am trying to understand how to load and render an image from a file using the Windows API, Direct2D, and Visual C++. I have been more or less attempting to follow an MSDN article on this topic. I am new to both C++ (experienced in C) and the Windows API.
I wrote 3 functions.
HRESULT imagefactorysetup(IWICImagingFactory * pImageFactory)
{
HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID *) &pImageFactory);
return hr;
}
HRESULT imageload(LPCWSTR filename, IWICImagingFactory * pImageFactory, IWICBitmapFrameDecode * pFrame)
{
IWICBitmapDecoder * pDecoder = NULL;
HRESULT hr = pImageFactory->CreateDecoderFromFilename(filename, NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &pDecoder);
if (SUCCEEDED(hr))
hr = pDecoder->GetFrame(0, &pFrame);
//Format convert the frame to 32bppPBGRA
IWICFormatConverter * pFormatConverter = NULL;
if (SUCCEEDED(hr))
{
SafeRelease(&pFormatConverter);
hr = pImageFactory->CreateFormatConverter(&pFormatConverter);
}
if (SUCCEEDED(hr))
hr = pFormatConverter->Initialize(pFrame, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeCustom);
return hr;
}
HRESULT imagerender(HWND hWnd, IWICBitmapFrameDecode * pFrame, int x, int y)
{
//Create a D2D render target properties
D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties = D2D1::RenderTargetProperties();
//Set the DPI to be the default system DPI to allow direct mapping
//between image pixels and desktop pixels in different system DPI settings
renderTargetProperties.dpiX = DEFAULT_DPI;
renderTargetProperties.dpiY = DEFAULT_DPI;
//Create a D2D render target
D2D1_SIZE_U sz = D2D1::SizeU(MAINWINDOWWIDTH, MAINWINDOWHEIGHT); //Change size
ID2D1Factory * pD2DFactory;
HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory1), NULL, (LPVOID *) &pD2DFactory);
ID2D1RenderTarget * pRenderTarget;
//renderTargetProperties, D2D1::HwndRenderTargetProperties(hWnd, sz), &pRenderTarget);
hr = pD2DFactory->CreateHwndRenderTarget(&renderTargetProperties, D2D1::HwndRenderTargetProperties(hWnd, sz), &pRenderTarget);
ID2D1Bitmap * pD2DBitmap = NULL;
pRenderTarget->CreateBitmapFromWicBitmap(pFrame, NULL, &pD2DBitmap);
D2D1_SIZE_F size = pD2DBitmap->GetSize();
D2D1_POINT_2F origin = D2D1::Point2F((float) x, (float) y);
if (pD2DBitmap)
pRenderTarget->DrawBitmap(pD2DBitmap, D2D1::RectF(origin.x, origin.y, origin.x + size.width, origin.y + size.height));
return hr;
}
Question:
1) The following line gives me an error. I tried reading some documentation on MSDN but am unsure what the issue is.
hr = pD2DFactory->CreateHwndRenderTarget(&renderTargetProperties, D2D1::HwndRenderTargetProperties(hWnd, sz), &pRenderTarget);
Error:
IntelliSense: no instance of overloaded function "ID2D1Factory::CreateHwndRenderTarget" matches the argument list
argument types are: (D2D1_RENDER_TARGET_PROPERTIES *, D2D1_HWND_RENDER_TARGET_PROPERTIES, ID2D1RenderTarget **)
object type is: ID2D1Factory 68 18
2) In general, is there a more idiomatic / efficient way of approaching the problem of rendering an image from a file onto a window than what I have attempted? My previous programming experience has been strictly in C.
No problem. You can use GDI+ to load any image that windows supports natively.
You can then use GDI to draw it.
Here's a short example of drawing a (transparent) PNG to the background of a dialog. I've built it using MinGW32 and Code::Blocks. You'll need to link the msimg32 and gdiplus libraries to make use of AlphaBlend and the Bitmap class (and the functions to init/shutdown GDI+).
Points that may be worth mentioning are:
mLoadImage will load anything that windows will show in Windows Photo
Viewer (// BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF) - it uses
the Bitmap class, as found in Gdiplus.
The WM_ERASEBKGND message comes with wParam holding a device context
that you can draw straight into - that's why there's no need to get
one by usig BeginPaint, as we do in response to a WM_PAINT message.
You can use BitBlt or StretchBlt for images that dont contain transparent areas.
Main.cpp
#define WINVER 0x0500 // for AlphaBlend
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <gdiplus.h>
#include "resource.h"
using namespace Gdiplus;
HINSTANCE hInst;
void setClientSize(HWND mHwnd, int width, int height)
{
RECT wndRect, clientRect, mRect;
int clientX, clientY, windowX, windowY, difX, difY;
GetWindowRect(mHwnd, &wndRect);
GetClientRect(mHwnd, &clientRect);
clientX = clientRect.right - clientRect.left;
clientY = clientRect.bottom - clientRect.top;
windowX = wndRect.right - wndRect.left;
windowY = wndRect.bottom - wndRect.top;
difX = windowX - clientX;
difY = windowY - clientY;
// GetWindowRect(mHwnd, &mRect);
POINT topLeft = {wndRect.left, wndRect.top};
// ScreenToClient(mParentHwnd, &topLeft);
SetWindowPos(mHwnd, HWND_TOP, topLeft.x, topLeft.y, width+difX, height+difY, SWP_NOZORDER);
}
HBITMAP mLoadImg(wchar_t *filename)
{
Bitmap mBitmap(filename,false);
HBITMAP result;
mBitmap.GetHBITMAP(0x00000000, &result);
return result;
}
void onPaint(HWND hwnd, HBITMAP bkg)
{
HDC memDC, hdc;
PAINTSTRUCT ps;
HBITMAP old;
RECT clientRect;
int width, height;
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &clientRect);
width = clientRect.right - clientRect.left;
height = clientRect.bottom - clientRect.top;
memDC = CreateCompatibleDC(NULL);
old = (HBITMAP)SelectObject(memDC, bkg);
byte alpha = 255;
BLENDFUNCTION bf = {AC_SRC_OVER,0,alpha,AC_SRC_ALPHA};
AlphaBlend(hdc, 0,0,width,height, memDC, 0,0, width,height, bf);
// try the below instead of AlphaBlend - they each rely on the fact I've resized the
// client area to the same size as the image I'll draw on it.
// BitBlt(hdc, 0,0, clientRect.right,clientRect.bottom, memDC, 0,0, SRCCOPY);
SelectObject(memDC, old);
DeleteDC(memDC);
EndPaint(hwnd, &ps);
}
BOOL CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HBITMAP mBkg;
switch(uMsg)
{
case WM_INITDIALOG:
{
mBkg = mLoadImg(L"wiki.png");
BITMAP bm;
GetObject(mBkg, sizeof(bm), &bm);
setClientSize(hwndDlg, bm.bmWidth, bm.bmHeight);
}
return TRUE;
case WM_ERASEBKGND:
{
RECT clientRect;
HBRUSH bkgBrush = CreateSolidBrush( RGB(255,0,0) );
GetClientRect(hwndDlg, &clientRect);
FillRect( (HDC)wParam, &clientRect, bkgBrush);
DeleteObject(bkgBrush);
}
return 1;
case WM_PAINT:
onPaint(hwndDlg, mBkg);
return 0;
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
}
}
return TRUE;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
hInst=hInstance;
InitCommonControls();
int retVal = DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
GdiplusShutdown(gdiplusToken);
return retVal;
}
resource.h
#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif
#define DLG_MAIN 100
resource.rc
// Generated by ResEdit 1.6.2
// Copyright (C) 2006-2014
// http://www.resedit.net
#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"
//
// Dialog resources
//
DLG_MAIN DIALOG 0, 0, 186, 95
STYLE DS_3DLOOK | DS_CENTER | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_THICKFRAME | WS_SYSMENU
EXSTYLE WS_EX_WINDOWEDGE
CAPTION "Dialog"
FONT 8, "Ms Shell Dlg"
{
}
Wiki.png
Result

Background changes after images are drawn on the screen

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.