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
Related
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?
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 capturing a window using the winapi PrintWindow, and cropping only the desired region using BitBlt, but the region I'm trying to capture is a rounded rect, how I could round the corners of the image inside of the hdc?
I know about the method Graphics::FromHDC(HDC) but I'm not sure how to "round" the image got from the graphics.
RECT rc;
HWND hwnd = FindWindow(TEXT("Test"), NULL);
if (hwnd == NULL)
{
cout << "Can't find the given window" << endl;
return 0;
}
GetClientRect(hwnd, &rc);
// Capture the window screen.
HDC hdcScreen = GetDC(NULL);
HDC hdcSrc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen,
rc.right - rc.left, rc.bottom - rc.top);
SelectObject(hdcSrc , hbmp);
//Print to memory hdc
PrintWindow(hwnd, hdcSrc, PW_CLIENTONLY);
// Copy only the desired area.
HDC hdcScreen2 = GetDC(NULL);
HDC hdcDest = CreateCompatibleDC(hdcScreen2);
HBITMAP hbmp2 = CreateCompatibleBitmap(hdcScreen2,
300, 200);
SelectObject(hdcDest, hbmp2);
BitBlt(hdcDest, X, Y, W, H, hdcSrc, 0, 0, SRCCOPY);
(I wrote this answer and thought the OP wanted Gdiplus for some reason ... regular GDI is pretty much the same thing although there actually is a CreatRoundRectRgn(...) in that case)
You can set the clipping region of a Gdiplus::Graphics object with its SetClip member function which takes a Region. You can create a Region from a Path but there is apparently no out-of-the-box way in Gdi+ of creating a round rectangle shaped path so you have to do it manually. There is an answer on StackOverflow about doing this in C#, and another one that ports it to C++/Win32 here.
Code below:
#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
namespace gdi = Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
void GetRoundRectPath(gdi::GraphicsPath* pPath, gdi::Rect r, int dia)
{
// diameter can't exceed width or height
if (dia > r.Width) dia = r.Width;
if (dia > r.Height) dia = r.Height;
// define a corner
gdi::Rect Corner(r.X, r.Y, dia, dia);
// begin path
pPath->Reset();
// top left
pPath->AddArc(Corner, 180, 90);
// tweak needed for radius of 10 (dia of 20)
if (dia == 20)
{
Corner.Width += 1;
Corner.Height += 1;
r.Width -= 1; r.Height -= 1;
}
// top right
Corner.X += (r.Width - dia - 1);
pPath->AddArc(Corner, 270, 90);
// bottom right
Corner.Y += (r.Height - dia - 1);
pPath->AddArc(Corner, 0, 90);
// bottom left
Corner.X -= (r.Width - dia - 1);
pPath->AddArc(Corner, 90, 90);
// end path
pPath->CloseFigure();
}
VOID OnPaint(HDC hdc, gdi::Image* img)
{
gdi::Graphics g(hdc);
gdi::GraphicsPath path;
GetRoundRectPath(&path, { 10,10,512,512 }, 50);
gdi::Region rgn(&path);
g.SetClip(&rgn);
g.DrawImage(img, 10, 10);
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS wndClass;
gdi::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
// Initialize GDI+.
gdi::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
{
// nest everything in a scope so that the image doesnt
// get destroyed after Gdiplus has been shut down.
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = TEXT("cliptorrect");
RegisterClass(&wndClass);
gdi::Image img(TEXT("C:\\test\\lenna.png"));
hWnd = CreateWindow(
TEXT("cliptorrect"),
TEXT("clip to round rect"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
548,
572,
NULL,
NULL,
hInstance,
&img
);
ShowWindow(hWnd, iCmdShow);
UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
gdi::GdiplusShutdown(gdiplusToken);
return msg.wParam;
} // WinMain
LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE: {
CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG>(cs->lpCreateParams));
} return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
OnPaint(hdc, reinterpret_cast<gdi::Image*>(GetWindowLongPtr(hWnd, GWLP_USERDATA)));
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
The output of the above looks like
I'm not sure if there is a way in GDi+ to get that anti-aliased or not, however.
I use CreateDC / BitBlt / GetDIBits etc. to capture the screen, but the cursor is not captured. Is there some simple argument or something to have it included?
#include <Windows.h>
#include <stdio.h>
#include <assert.h>
void scrshot() {
HWND hwnd = GetDesktopWindow();
HDC hdc = GetWindowDC(hwnd);
HDC hdcMem = CreateCompatibleDC(hdc);
int cx = GetDeviceCaps(hdc, HORZRES);
int cy = GetDeviceCaps(hdc, VERTRES);
HBITMAP hbitmap(NULL);
hbitmap = CreateCompatibleBitmap(hdc, cx, cy);
SelectObject(hdcMem, hbitmap);
BitBlt(hdcMem, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY);
CURSORINFO cursor = { sizeof(cursor) };
GetCursorInfo(&cursor);
if (cursor.flags == CURSOR_SHOWING) {
RECT rect;
GetWindowRect(hwnd, &rect);
ICONINFO info = { sizeof(info) };
GetIconInfo(cursor.hCursor, &info);
const int x = cursor.ptScreenPos.x - rect.left - rect.left - info.xHotspot;
const int y = cursor.ptScreenPos.y - rect.top - rect.top - info.yHotspot;
BITMAP bmpCursor = { 0 };
GetObject(info.hbmColor, sizeof(bmpCursor), &bmpCursor);
DrawIconEx(hdcMem, x, y, cursor.hCursor, bmpCursor.bmWidth, bmpCursor.bmHeight,
0, NULL, DI_NORMAL);
}
}
int main(){
scrshot();
return 0;
}
Further to the discussion that occurred in the comments, I had the chance to further investigate the question. As a result, I came up with the following code that will grab the current cursor's HBITMAP and draw it to the screen.
Since the cursor is actually an HICON, it comes with a mask. Initially, I just did a simple BitBlt - however, I got a 32x32 black sqaure with the cursor in the top left 1/4 or so.
I then investigated using MaskBlt. Depending on where the cursor is when the app is started, I get either the wait cursor, a NS resize cursor, or the standard pointer. I guess you could start a timer and add a WM_TIMER handler to fire a couple of times a second in order to get a real-time update of the cursor as it was used in other windows in the system. It seemed like a mere curiosity to do something like that so I didn't bother.
EDIT: I actually did start a timer in WM_INITDIALOG and handle it in WM_TIMER. You can now see the image updated 10 times a second. For some reason, the I-beam cursor doesn't seem to be displayed at all - a case for further investigation as needed, I guess.
Here's the complete listing (except for resource.rc and resource.h - just create a dialog app and make sure the dialog's resource ID is used inside Main in the call to DialogBox)
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include "resource.h"
HINSTANCE hInst;
HBITMAP getCursorHBITMAP(HBITMAP *maskBmp)
{
CURSORINFO pci;
ICONINFO iconinfo;
HBITMAP result;
pci.cbSize = sizeof(pci);
GetCursorInfo(&pci);
if (GetIconInfo(pci.hCursor,&iconinfo))
{
result = iconinfo.hbmColor;
if (maskBmp)
*maskBmp = iconinfo.hbmMask;
}
else
result = NULL;
return result;
}
BOOL CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
{
SetTimer(hwndDlg, 1, 100, NULL);
}
return TRUE;
case WM_TIMER:
{
InvalidateRect(hwndDlg, NULL, true);
}
return 0;
case WM_ERASEBKGND:
{
HDC hdc = (HDC)wParam;
RECT mRect;
GetClientRect(hwndDlg, &mRect);
FillRect(hdc, &mRect, (HBRUSH)GetStockObject(GRAY_BRUSH));
}
return 1;
case WM_PAINT:
{
HBITMAP oldBm, cursorBmp, maskBmp;
cursorBmp = getCursorHBITMAP(&maskBmp);
if (cursorBmp)
{
HDC hdc;
PAINTSTRUCT ps;
HDC memDC;
BITMAP bm;
hdc = BeginPaint(hwndDlg, &ps);
memDC = CreateCompatibleDC(hdc);
oldBm = (HBITMAP) SelectObject(memDC, cursorBmp);
GetObject(cursorBmp, sizeof(bm), &bm);
// printf("Cursor size: %d x %d\n", bm.bmWidth, bm.bmHeight);
// BitBlt(hdc, 10,10, 32,32, memDC, 0,0, SRCCOPY);
MaskBlt(hdc, 10,10, bm.bmWidth, bm.bmHeight, memDC, 0,0, maskBmp, 0,0, MAKEROP4(SRCPAINT,SRCCOPY) );
SelectObject(memDC, oldBm);
DeleteDC(memDC);
EndPaint(hwndDlg, &ps);
}
}
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)
{
hInst=hInstance;
InitCommonControls();
return DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
}
Is it possible to load a PNG from a file into an HBITMAP using Win32 GDI functions? If not, what would be the lightest solution without using external libraries (like libpng)?
You can use the Windows Imaging Component to load PNG files (on Windows XP SP2 and later). See MSDN Magazine for an introduction on how to use the API and my blog post for a code sample that loads a PNG from an IStream and converts it to an HBITMAP.
There is no need to use Windows Imaging Component, GDI+ or PNG library. You can use Icon functionality.
Add new icon (ICO_PNG) to VC project resources with custom Width and Height (Resource Editor->Image->New Image Type). Copy Your png image here and use Fill Tool+transparent color to make icon transparent.
Add Picture Control (IDC_PNG) to Your dialog (Type = Owner draw).
Dialog procedure code:
switch (msg)
{
...
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
if (pDIS->CtlID == IDC_PNG)
{
HICON hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(ICO_LOGO), IMAGE_ICON, 0, 0, LR_LOADTRANSPARENT);
DrawIconEx(pDIS->hDC, 0, 0, hIcon, 0, 0, 0, NULL, DI_NORMAL);
DestroyIcon(hIcon);
return TRUE;
}
}
}
You can do it with StretchDIBits API, but limited by OS/driver availability.
Consult MSDN documentation for details:
http://msdn.microsoft.com/en-us/library/dd145121(v=VS.85).aspx
http://msdn.microsoft.com/en-us/library/dd145107(VS.85).aspx
I sincerely apologize for misleading you guys interested in this issue.
Let me correct my mistake.
No StretchDIBits for PNG drawing.
You'd better try WIC method or consider way to integrate GDI+ in your projects.
We can display png image via GDI, by the following two steps when creating your window(case WM_CREATE in window procedure function):
load png file (via libpng or stb image), pixel values saved in a variable, say buffer
create HBITMAP instance using buffer in CreateBitmap() function
Here's the runnable code, which is in pure C and main() as entry point function (libpng and zlib are from my own opencv compilation)
#include <stdio.h>
#include <windows.h>
#include "png.h"
#define CRTDBG_MAP_ALLOC
#include <crtdbg.h>
// **NB**: You may use OpenCV prebuilt package's self contained libpng.lib file
// or, maybe, you can also compile it from source (which cost time and not necessary), see: `http://www.libpng.org` and `https://www.zlib.net`
#define LIBPNG_PTH "D:/opencv_249/build/x64/vc12/staticlib/libpng.lib"
#define ZLIB_PTH "D:/opencv_249/build/x64/vc12/staticlib/zlib.lib"
#pragma comment(lib, LIBPNG_PTH)
#pragma comment(lib, ZLIB_PTH)
typedef struct MyRect {
int x, y, width, height;
} MyRect;
char bitmap_im_pth[100];
typedef struct MyWindow {
HDC dc;
//HGDIOBJ image;
HBITMAP hBmp;
unsigned char* imdata;
} MyWindow;
MyWindow* my_window;
enum ImageType {BMP, PNG};
long ReadPngData(const char *szPath, int *pnWidth, int *pnHeight, unsigned char **cbData)
{
FILE *fp = NULL;
long file_size = 0, pos = 0, mPos = 0;
int color_type = 0, x = 0, y = 0, block_size = 0;
png_infop info_ptr;
png_structp png_ptr;
png_bytep *row_point = NULL;
fp = fopen(szPath, "rb");
if (!fp) return -1;
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
info_ptr = png_create_info_struct(png_ptr);
png_init_io(png_ptr, fp);
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
*pnWidth = png_get_image_width(png_ptr, info_ptr);
*pnHeight = png_get_image_height(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
file_size = (*pnWidth) * (*pnHeight) * 4;
*cbData = (unsigned char *)malloc(file_size);
row_point = png_get_rows(png_ptr, info_ptr);
block_size = color_type == 6 ? 4 : 3;
for (x = 0; x < *pnHeight; x++)
for (y = 0; y < *pnWidth*block_size; y += block_size)
{
(*cbData)[pos++] = row_point[x][y + 2]; //B
(*cbData)[pos++] = row_point[x][y + 1]; //G
(*cbData)[pos++] = row_point[x][y + 0]; //R
(*cbData)[pos++] = row_point[x][y + 3]; //alpha
}
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
fclose(fp);
return file_size;
}
LRESULT __stdcall WindowProcedure(HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
{
int im_width, im_height;
int image_type = PNG;
switch (msg)
{
case WM_CREATE:
if (image_type == BMP) {
my_window->hBmp = (HBITMAP)LoadImage(NULL, "lena512.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
}
else if (image_type == PNG) {
ReadPngData("Lena.png", &im_width, &im_height, &my_window->imdata);
my_window->hBmp = CreateBitmap(im_width, im_height, 32, 1, my_window->imdata);
}
if (my_window->hBmp == NULL)
MessageBox(window, "Could not load image!", "Error", MB_OK | MB_ICONEXCLAMATION);
break;
case WM_PAINT:
{
BITMAP bm;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(window, &ps);
SetStretchBltMode(hdc, COLORONCOLOR);
my_window->dc = CreateCompatibleDC(hdc);
HBITMAP hbmOld = SelectObject(my_window->dc, my_window->hBmp);
GetObject(my_window->hBmp, sizeof(bm), &bm);
#if 1
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, my_window->dc, 0, 0, SRCCOPY);
#else
RECT rcClient;
GetClientRect(window, &rcClient);
int nWidth = rcClient.right - rcClient.left;
int nHeight = rcClient.bottom - rcClient.top;
StretchBlt(hdc, 0, 0, nWidth, nHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
#endif
SelectObject(my_window->dc, hbmOld);
DeleteDC(my_window->dc);
EndPaint(window, &ps);
}
break;
case WM_DESTROY:
printf("\ndestroying window\n");
PostQuitMessage(0);
return 0L;
case WM_LBUTTONDOWN:
printf("\nmouse left button down at (%d, %d)\n", LOWORD(lp), HIWORD(lp));
// fall thru
default:
//printf(".");
return DefWindowProc(window, msg, wp, lp);
}
}
const char* szWindowClass = "myclass";
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
/* Win 3.x */
wc.style = CS_DBLCLKS;
wc.lpfnWndProc = WindowProcedure;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(0);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = 0;
wc.lpszClassName = szWindowClass;
/* Win 4.0 */
wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
return RegisterClassEx(&wc);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
MyRect rect;
rect.x = 300;
rect.y = 300;
rect.width = 640;
rect.height = 480;
DWORD defStyle = WS_VISIBLE | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU;
HWND hwnd = CreateWindowEx(0, szWindowClass, "title",
defStyle, rect.x, rect.y,
rect.width, rect.height, 0, 0, hInstance, 0);
if (!hwnd)
{
return FALSE;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
return TRUE;
}
void create_my_window(MyWindow** _my_window) {
MyWindow* my_window = (MyWindow*)malloc(sizeof(MyWindow));
my_window->dc = NULL;
my_window->imdata = NULL;
my_window->hBmp = NULL;
*_my_window = my_window; // write back
}
void destroy_my_window(MyWindow* my_window) {
if (my_window) {
if (my_window->imdata) free(my_window->imdata);
free(my_window);
}
}
int main()
{
printf("hello world!\n");
HINSTANCE hInstance = GetModuleHandle(0);
int nCmdShow = SW_SHOWDEFAULT;
MyRegisterClass(hInstance);
create_my_window(&my_window);
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
DispatchMessage(&msg);
}
destroy_my_window(my_window);
return 0;
}
Reference: https://www.cnblogs.com/mr-wid/archive/2013/04/22/3034840.html
The answer by vladimir_hr is simplicity itself.
Simple steps to follow.
In the resources header file declare like:
#define IDI_PNG 1000
In the resource file *.rc have:
IDI_PNG ICON "protractor.ico"
The icon file.
Convert your (transparent) png file into an icon file by using an icon editor that support custom size instead of the standard Windows' icon sizes, save this png image as an icon image.
The rest is just blitting between DC's.