I have a windowed Direct2D app and added a statusbar to the window from common controls:
InitCommonControls();
HWND hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
hWnd, (HMENU)ID_STATUSBAR, GetModuleHandle(NULL), NULL);
The statusbar is showing up just fine, but as soon as I activate the BeginDraw()&EndDRaw() functions in my message loop, the statusbar is painted over, despite the fact I defined the height of the renderTarget when initialising it
res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
res = factory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(windowHandle, rectRender),
&renderTarget);
I also created a resize function
RECT rectWindow{ 0 }, rectStatus{ 0 };
D2D1_SIZE_U rectRender{ 0 };
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
renderTarget->Resize(rectRender);
InvalidateRect(windowHandle, NULL, FALSE);
and called in in WM_SIZING and WM_SIZE:
case WM_SIZE:
case WM_SIZING:
hStatus = GetDlgItem(hWnd, ID_STATUSBAR);
gfx->Resize(hWnd, hStatus);
SendMessage(hStatus, WM_SIZE, 0, 0);
Doesn't BeginDraw() respect the dimensions of the rendertarget and just take the entire window? And if so, should I consider using layers or is there something wrong in my code?
EDIT: I received some downvotes for this question. If there's something wrong with my post, do let me know and I'll try to improve. I'm still fresh in the win32 world, but I've learned a lot from this platform. I would love to contribute with interesting questions and answers, but a simple -1 doesn't give me a clue what to improve. I've read 2 evenings about the subject on MSDN and various forums but didn't see what I do wrong. I tried be as complete as possible by writing an complete example code that illustrates the issue.
For reference the entire code
#include <windows.h>
#include <CommCtrl.h>
#include <d2d1.h>
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "d2d1.lib")
#define ID_STATUSBAR 1000
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class Graphics
{
ID2D1Factory* factory;
ID2D1HwndRenderTarget* renderTarget;
ID2D1SolidColorBrush* brush;
public:
Graphics()
{
factory = NULL;
renderTarget = NULL;
brush = NULL;
}
~Graphics()
{
if (factory) factory->Release();
if (renderTarget) renderTarget->Release();
if (brush) brush->Release();
}
bool Init(HWND windowHandle, HWND statusHandle);
void BeginDraw() { renderTarget->BeginDraw(); }
void EndDraw() { renderTarget->EndDraw(); }
void Resize(HWND windowHandle, HWND statusHandle);
void DrawCircle(float x, float y, float r);
};
HINSTANCE hInstance;
Graphics* gfx;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
InitCommonControls();
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = TEXT("mainwindow");
RegisterClass(&wc);
HWND hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, TEXT("mainwindow"),
TEXT("MainWindow"), WS_OVERLAPPEDWINDOW, 100, 100, 800, 600,
NULL, NULL, hInstance, NULL);
if (!hWnd) return -1;
HWND hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
hWnd, (HMENU)ID_STATUSBAR, GetModuleHandle(NULL), NULL);
gfx = new Graphics;
if (!gfx->Init(hWnd, hStatus))
{
delete gfx;
return -1;
}
ShowWindow(hWnd, nCmdShow);
MSG message{ 0 };
bool runGame = true;
while (runGame)
{
while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
if (message.message == WM_QUIT)
runGame = false;
}
gfx->BeginDraw();
gfx->DrawCircle(400.0f, 100.0f, 100.0f);
gfx->DrawCircle(400.0f, 300.0f, 100.0f);
gfx->DrawCircle(400.0f, 500.0f, 100.0f);
gfx->EndDraw();
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HWND hStatus;
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_SIZE:
case WM_SIZING:
hStatus = GetDlgItem(hWnd, ID_STATUSBAR);
gfx->Resize(hWnd, hStatus);
SendMessage(hStatus, WM_SIZE, 0, 0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
bool Graphics::Init(HWND windowHandle, HWND statusHandle)
{
RECT rectWindow{ 0 }, rectStatus{ 0 };
D2D1_SIZE_U rectRender{ 0 };
HRESULT res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
if (res != S_OK) return false;
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
res = factory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(windowHandle, rectRender),
&renderTarget);
if (res != S_OK) return false;
res = renderTarget->CreateSolidColorBrush(D2D1::ColorF(1, 0, 0, 0), &brush);
if (res != S_OK) return false;
return true;
}
void Graphics::Resize(HWND windowHandle, HWND statusHandle)
{
if (renderTarget != NULL)
{
RECT rectWindow{ 0 }, rectStatus{ 0 };
D2D1_SIZE_U rectRender{ 0 };
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
renderTarget->Resize(rectRender);
}
}
void Graphics::DrawCircle(float x, float y, float r)
{
brush->SetColor(D2D1::ColorF(1.0f, 0.0f, 0.0f, 1.0f));
renderTarget->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(x, y), r, r), brush, 1.0f);
}
You can add WS_CLIPCHILDREN window style to your MainWindow:
HWND hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, TEXT("mainwindow"),
TEXT("MainWindow"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, 100, 100, 800, 600,
NULL, NULL, hInstance, NULL);
Alternatively, you can create another child window (sibling to status bar) and use that for your Direct2D target.
Related
When I call GLClear without swapping the buffer, it is causing memory leak.
I am not sure if this is a driver issue or if I am doing something wrong.
If anyone can give it a try or have any idea, please tell me.
TL;DR, this is the code summarized. You can find all the code after this block
static bool CloseFlag = false;
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static PAINTSTRUCT ps;
switch(uMsg)
{
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
case WM_CHAR:
//Handle input....
return 0;
case WM_CLOSE:
CloseFlag = true;
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
HWND CreateOpenGLWindow(char* title, int x, int y, int width, int height,
BYTE type, DWORD flags)
{
//Create window (with double buffer) and register it...
}
int main ()
{
hWnd = CreateOpenGLWindow("minimal", 0, 0, 256, 256, PFD_TYPE_RGBA, 0);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
gladLoadWGL(hDC)
gladLoadGL()
ShowWindow(hWnd, SW_SHOW);
while(!CloseFlag)
{
//Handles input
while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
glClear(GL_COLOR_BUFFER_BIT);
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(hWnd, hDC);
wglDeleteContext(hRC);
DestroyWindow(hWnd);
return 0;
}
Here's a minimum code for reproducing it that uses Win32.
#ifndef UNICODE
#define UNICODE
#define _UNICODE
#endif
#include <windows.h> /* must include this before GL/gl.h */
#include "glad/glad.h"
#include "glad/glad_wgl.h"
#include "wglext.h"
#include <string>
static bool CloseFlag = false;
void display(HDC hDC)
{
/* rotate a triangle around */
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2i(0, 1);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2i(-1, -1);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2i(1, -1);
glEnd();
glFlush();
SwapBuffers(hDC);
}
void JustClear()
{
glClear(GL_COLOR_BUFFER_BIT);
}
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static PAINTSTRUCT ps;
switch(uMsg)
{
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
case WM_CHAR:
switch (wParam)
{
case 27: /* ESC key */
//PostQuitMessage(0);
CloseFlag = true;
break;
}
return 0;
case WM_CLOSE:
CloseFlag = true;
//PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
HWND CreateOpenGLWindow(char* title, int x, int y, int width, int height,
BYTE type, DWORD flags)
{
int pf;
HDC hDC;
HWND hWnd;
WNDCLASS wc;
PIXELFORMATDESCRIPTOR pfd;
static HINSTANCE hInstance = 0;
/* only register the window class once - use hInstance as a flag. */
if (!hInstance) {
hInstance = GetModuleHandle(NULL);
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"OpenGL";
if (!RegisterClass(&wc)) {
return NULL;
}
}
std::wstring className = L"OpenGL";
hWnd = CreateWindow(className.c_str(), className.c_str(), WS_OVERLAPPEDWINDOW |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
x, y, width, height, NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
return NULL;
}
hDC = GetDC(hWnd);
/* there is no guarantee that the contents of the stack that become
the pfd are zeroed, therefore _make sure_ to clear these bits. */
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | flags;
pfd.iPixelType = type;
pfd.cColorBits = 32;
pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
return 0;
}
if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
return 0;
}
DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
ReleaseDC(hWnd, hDC);
return hWnd;
}
int main ()
{
HDC hDC; /* device context */
HGLRC hRC; /* opengl context */
HWND hWnd; /* window */
MSG msg; /* message */
hWnd = CreateOpenGLWindow("minimal", 0, 0, 256, 256, PFD_TYPE_RGBA, 0);
if (hWnd == NULL)
exit(1);
hDC = GetDC(hWnd);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
if(!gladLoadWGL(hDC))
{
return -1;
}
if (!gladLoadGL()) //Load Glad
{
return -1;
}
ShowWindow(hWnd, SW_SHOW);
while(!CloseFlag)
{
//Handles input
while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//Render
//display(hDC);
JustClear();
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(hWnd, hDC);
wglDeleteContext(hRC);
DestroyWindow(hWnd);
return 0;
}
wglext.h is from here
Here's the OpenGL glad config
/*
OpenGL loader generated by glad 0.1.36 on Fri Oct 28 11:12:34 2022.
Language/Generator: C/C++
Specification: gl
APIs: gl=3.3
Profile: compatibility
Extensions:
Loader: True
Local files: False
Omit khrplatform: False
Reproducible: False
Commandline:
--profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions=""
Online:
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3
*/
and Glad wgl 1.0 with all the extensions
and here's the CMake I used
cmake_minimum_required(VERSION 3.14)
set (CMAKE_CXX_STANDARD 11)
# For Clang to do parsing
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# set the project name
project(Example)
add_executable(EXAMPLE_EXE ${CMAKE_CURRENT_LIST_DIR}/Src/main.cpp)
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/External/glad_v0.1.36")
find_package(OpenGL REQUIRED)
target_include_directories(EXAMPLE_EXE PUBLIC "${CMAKE_CURRENT_LIST_DIR}/External/wglExt")
target_link_libraries(EXAMPLE_EXE PUBLIC OpenGL::GL glad)
I created a child window using WinAPI, now I'm trying to draw a triangle there. It is drawn, but its scale is not correct.
I think this is due to the fact that I have not properly installed orthogonal system. It is set in the init() function, I call it when the child window is created in the WM_CREATE message, it is triggered, but the orthographic projection is still not set to the desired size. So I only see the bottom of the triangle.
#include <Windows.h>
#include <GL/GL.h>
#include <GL/GLU.h>
#pragma comment(lib, "OpenGL32.lib")
#pragma comment(lib, "glu32.lib")
//opengl values
int windowWidth = 600, windowHeight = 800, windowDepth = 600;
void init();
HWND childOpenGLWindowHWND = NULL;
HWND CreateOpenGLChildWindow(wchar_t* title, int x, int y, int width, int height,
BYTE type, DWORD flags, HWND hWndParent, HINSTANCE hInstance, WNDPROC wndProc)
{
int pf;
HDC hDC;
HWND hWnd;
PIXELFORMATDESCRIPTOR pfd;
WNDCLASS wc;
wc.style = CS_OWNDC;
wc.lpfnWndProc = wndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"OpenGL";
if (!RegisterClass(&wc)) {
MessageBox(NULL, L"RegisterClass() failed: "
L"Cannot register window class.", L"Error", MB_OK);
return NULL;
}
hWnd = CreateWindow(
L"OpenGL",
title,
WS_CHILD,
x,
y,
width,
height,
hWndParent,
NULL,
NULL,
NULL
);
if (!hWnd) {
MessageBox(NULL, L"CreateWindow() failed: Cannot create a window.",
L"Error", MB_OK);
return NULL;
}
hDC = GetDC(hWnd);
/* there is no guarantee that the contents of the stack that become
the pfd are zeroed, therefore _make sure_ to clear these bits. */
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_TYPE_RGBA | flags;
pfd.iPixelType = type;
pfd.cColorBits = 32;
pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
MessageBox(NULL, L"ChoosePixelFormat() failed: "
"Cannot find a suitable pixel format.", L"Error", MB_OK);
return 0;
}
if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
MessageBox(NULL, L"SetPixelFormat() failed: "
"Cannot set format specified.", L"Error", MB_OK);
return 0;
}
DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
ReleaseDC(hWnd, hDC);
return hWnd;
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
wchar_t WinName[] = L"MainFrame";
int WINAPI WinMain(
HINSTANCE This,
HINSTANCE Prev,
LPSTR cmd,
int mode
)
{
HDC hDC; /* device context */
HGLRC hRC; /* opengl context */
HWND hWnd;
MSG msg;
WNDCLASS wc;
wc.hInstance = This;
wc.lpszClassName = WinName;
wc.lpfnWndProc = WndProc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszMenuName = NULL;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
if (!RegisterClass(&wc)) return NULL;
int windowWidth = 800;
int windowHeight = 800;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
hWnd = CreateWindow(
WinName,
L"Title",
WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX,
(screenWidth - windowWidth) / 2,
(screenHeight - windowHeight) / 2,
windowWidth,
windowHeight,
HWND_DESKTOP,
NULL,
This,
NULL
);
if (!hWnd)
{
MessageBox(NULL, L"MAIN HWND ERROR!!!",
L"Error", MB_OK);
exit(1);
}
childOpenGLWindowHWND = CreateOpenGLChildWindow(
WinName,
0,
0,
600,
800,
NULL,
NULL,
hWnd,
This,
WndProc
);
if (!childOpenGLWindowHWND)
{
MessageBox(NULL, L"Child window init error", L"Error", MB_OK);
return 0;
}
hDC = GetDC(childOpenGLWindowHWND);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
SendMessage(hWnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS), NULL);
ShowWindow(hWnd, mode);
ShowWindow(childOpenGLWindowHWND, mode);
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(childOpenGLWindowHWND, hDC);
wglDeleteContext(hRC);
DestroyWindow(childOpenGLWindowHWND);
return msg.wParam;
//return NULL;
}
void init()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-windowWidth / 2, windowWidth / 2, -windowHeight / 2, windowHeight / 2);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(0, 1, 0);
glVertex3f(-50, 0, 0);
glVertex3f(50, 0, 0);
glVertex3f(0, 50, 0);
glEnd();
glFlush();
}
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
PAINTSTRUCT ps;
switch (message)
{
case WM_CREATE:
init();
case WM_PAINT:
display();
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return NULL;
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return NULL;
case WM_CHAR:
switch (wParam) {
case 27:
PostQuitMessage(0);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(NULL);
break;
default: return DefWindowProc(hWnd, message, wParam, lParam);
}
return NULL;
}
The WM_CREATE message is triggered by CreateWindow. This is before the OpenGL Context is created by wglCreateContext and made current by wglMakeCurrent.
Thus not any OpenGL instruction takes effect at this point.
I recommend to implement the WM_SHOWWINDOW message instead:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hDC;
switch (message)
{
// [...]
case WM_SHOWWINDOW:
BeginPaint(hWnd, &ps);
init();
EndPaint(hWnd, &ps);
return NULL;
case WM_PAINT:
BeginPaint(hWnd, &ps);
display();
EndPaint(hWnd, &ps);
return NULL;
// [...]
}
return NULL;
}
I wrote a simple directX9 app (code below) which is working fine for at least 25 Windows 10 setups but on one the background of the window is just black instead of transparent (with the test draw on it).
All the systems are running dedicated NVIDIA GPUs (10 series) so this should not be the problem and they use the latest Windows 10 version with the latest Nvidia drivers.
A workaround is to add LWA_COLORKEY to SetLayeredWindowAttributes but I would like to know how I can fix the system instead of using LWA_COLORKEY. (Because adding LWA_COLORKEY costs a lot of performance).
SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA | LWA_COLORKEY);
This is a sample that can replicate the problem:
#pragma once
#include <Windows.h>
#include <string>
#include <iostream>
// DirectX9
#pragma warning( push )
#pragma warning( disable : 4005)
#include <d3d9.h>
#include <d3dx9.h>
#include <Dwmapi.h>
#include <TlHelp32.h>
#include <Directxmath.h>
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "Dwmapi.lib")
#pragma warning( pop )
using namespace std;
using namespace DirectX;
int s_width = 800;
int s_height = 600;
#define CENTERX (GetSystemMetrics(SM_CXSCREEN)/2)-(s_width/2)
#define CENTERY (GetSystemMetrics(SM_CYSCREEN)/2)-(s_height/2)
LPDIRECT3D9 d3d;
LPDIRECT3DDEVICE9 d3ddev;
HWND hWnd;
LPD3DXFONT pFont;
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void initD3D(HWND hWnd)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferWidth = s_width;
d3dpp.BackBufferHeight = s_height;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.FullScreen_RefreshRateInHz = 0;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.MultiSampleQuality = DEFAULT_QUALITY;
d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);
D3DXCreateFont(d3ddev, 50, 0, FW_BOLD, 1, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial", &pFont);
}
void DrawString(int x, int y, DWORD color, LPD3DXFONT g_pFont, const char *fmt, ...)
{
RECT FontPos = { x, y, x + 120, y + 16 };
char buf[1024] = { '\0' };
va_list va_alist;
va_start(va_alist, fmt);
vsprintf_s(buf, fmt, va_alist);
va_end(va_alist);
g_pFont->DrawText(NULL, buf, -1, &FontPos, DT_NOCLIP, color);
}
void render()
{
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0);
d3ddev->BeginScene();
DrawString(10, 50, D3DCOLOR_ARGB(100, 255, 0, 0), pFont, "Test rendering");
d3ddev->EndScene();
d3ddev->Present(NULL, NULL, NULL, NULL);
}
int main()
{
RECT rc = { 0 };
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hInstance = NULL;
wc.lpfnWndProc = WindowProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0));
wc.lpszClassName = "WindowClass";
RegisterClassEx(&wc);
hWnd = CreateWindowExA(WS_EX_LAYERED | WS_EX_TRANSPARENT, "WindowClass", "Title", WS_POPUP | WS_VISIBLE, rc.left, rc.top, s_width, s_height, NULL, NULL, hInstance, NULL);
SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);
//DWM_BLURBEHIND bb = { DWM_BB_ENABLE | DWM_BB_BLURREGION, true, CreateRectRgn(0, 0, -1, -1), true };
//if (DwmEnableBlurBehindWindow(hWnd, &bb) != S_OK)
MARGINS margin = { -1 };
DwmExtendFrameIntoClientArea(hWnd, &margin);
initD3D(hWnd);
MSG msg;
while (TRUE)
{
render();
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (msg.message == WM_QUIT)
exit(0);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
}break;
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
I have the "simple" goal of drawing a bitmap with some transparency around it on the screen. That bit wasn't so hard:
#include <windows.h>
#include "BBKG.h"
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static int wH = 156;
static int wW = 166;
HBITMAP CreateBitmapMask(HBITMAP hbmColour, COLORREF crTransparent)
{
HDC mem0, mem1;
HBITMAP hbmMask;
BITMAP bm;
GetObject(hbmColour, sizeof(BITMAP), &bm);
hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
mem0 = CreateCompatibleDC(0);
mem1 = CreateCompatibleDC(0);
SelectObject(mem0, hbmColour);
SelectObject(mem1, hbmMask);
SetBkColor(mem0, crTransparent);
BitBlt(mem1, 0, 0, bm.bmWidth, bm.bmHeight, mem0, 0, 0, SRCCOPY);
BitBlt(mem0, 0, 0, bm.bmWidth, bm.bmHeight, mem1, 0, 0, SRCINVERT);
DeleteDC(mem0);
DeleteDC(mem1);
return hbmMask;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
hInst = hInstance;
MSG msg;
HWND hwnd;
WNDCLASSW wc;
wc.style = 0;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.lpszClassName = L"nope";
wc.hInstance = hInst;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassW(&wc);
hwnd = CreateWindowW(wc.lpszClassName, L"",
WS_VISIBLE | WS_POPUP| WS_EX_TRANSPARENT,
100, 100, wW, wH, NULL, NULL, hInst, NULL);
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
//Workaround for focusables stealing my Esc key
if (msg.message == WM_KEYDOWN){
if (msg.wParam == VK_ESCAPE) {
SendMessage(hwnd, WM_CLOSE, 0, 0);
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
static int px;
static int py;
static HBITMAP bhbm;
static RECT nRect = { 0, 0, wW, wH };
switch (msg)
{
case WM_CREATE:
{
HWND bb = CreateWindowW(L"STATIC", L"",
WS_VISIBLE | WS_CHILD ,
0, 0, wW, wH,
hwnd, (HMENU)11, hInst, NULL);
//SetTimer(hwnd, 1, 80, NULL);
return 0;
}
case WM_PAINT: {
//Vars
RECT wRect;
if (GetUpdateRect(hwnd, &wRect, FALSE) == 0) {
return 0; //Nothing to paint
}
PAINTSTRUCT gps;
PAINTSTRUCT ps;
BeginPaint(hwnd, &gps);
HWND bb = GetDlgItem(hwnd, 11);
HDC bbhdc = BeginPaint(bb, &ps);
HDC mdc = CreateCompatibleDC(bbhdc);
//Load Image
BITMAP pBM;
HBITMAP pHBM = (HBITMAP)LoadImage(NULL, L"twi00.bmp", 0, 0, 0, LR_LOADFROMFILE);
HBITMAP pMBM = CreateBitmapMask((HBITMAP)pHBM, 0x00000000);
GetObject(pHBM, sizeof(pBM), &pBM);
//Paint
HBITMAP oldBM = (HBITMAP)SelectObject(mdc, pMBM);
BitBlt(bbhdc, 0, 0, pBM.bmWidth, pBM.bmHeight, mdc, 0, 0, SRCAND);
SelectObject(mdc, pHBM);
BitBlt(bbhdc, 0, 0, pBM.bmWidth, pBM.bmHeight, mdc, 0, 0, SRCPAINT);
//Cleanup
SelectObject(mdc, oldBM);
DeleteObject(pHBM);
DeleteObject(pMBM);
DeleteDC(mdc);
EndPaint(bb, &ps);
EndPaint(hwnd, &gps);
return 1;
}
case WM_ERASEBKGND: {
return 0;
}
case WM_DESTROY:
{
DeleteObject(bhbm);
PostQuitMessage(0);
return 0;
}
case WM_LBUTTONDOWN:
SetCapture(hwnd);
px = LOWORD(lParam);
py = HIWORD(lParam);
return 1;
case WM_LBUTTONUP:
{
ReleaseCapture();
return 1;
}
case WM_MOUSEMOVE:
{
if (GetCapture() == hwnd)
{
RECT rcWindow;
GetWindowRect(hwnd, &rcWindow);
SetWindowPos(hwnd, NULL, rcWindow.left + LOWORD(lParam) - px, rcWindow.top + HIWORD(lParam) - py, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
break;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
Use any generic bmp with a black border will do, I used this:
Now the question is, how can I make it so that when I move the window (click/drag) the background updates? I was hoping for something like putting the bitmap into a transparent window so that it's overlayed on top of things but it seems to just grab the pixels of what ever is behind it.
I'm attempting to do this without GDI+ or other libraries, if possible.
CreateWindow() does not accept extended window styles, such as WS_EX_TRANSPARENT (which is why it has EX in its name). You have to use CreateWindowEx() instead:
hwnd = CreateWindowExW(WS_EX_TRANSPARENT,
wc.lpszClassName, L"",
WS_VISIBLE | WS_POPUP,
100, 100, wW, wH, NULL, NULL, hInst, NULL);
A better option is to create a layered window (see also this) by using the WS_EX_LAYERED extended style). Then you can use the UpdateLayeredWindow() function to provide the window with the bitmap and the transparent color (you can also specify alpha as well). Let the window manage all of the hard work of drawing the bitmap transparently for you.
Your WndProc() can also respond to the WM_NCHITTEST message to tell the OS that all clicks on the window should be treated as if the user were clicking on the window's titlebar. Let the window handle the mouse tracking and auto-positioning for you.
Try something more like this:
#include <windows.h>
HINSTANCE hInst;
static int wH = 156;
static int wW = 166;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
hInst = hInstance;
WNDCLASSW wc = {0};
wc.lpszClassName = L"nope";
wc.hInstance = hInst;
wc.lpfnWndProc = WndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassW(&wc);
HWND hwnd = CreateWindowEx(WS_EX_LAYERED,
wc.lpszClassName, L"",
WS_POPUP, 100, 100, wW, wH, NULL, NULL,
hInst, NULL);
HBITMAP hBmp = (HBITMAP) LoadImage(NULL, L"twi00.bmp", 0, 0, 0, LR_LOADFROMFILE);
HDC hdcScreen = GetDC(0);
HDC hdcBmp = CreateCompatibleDC(hdcScreen);
HBITMAP oldBM = (HBITMAP) SelectObject(hdcBmp, hBmp);
POINT pt = {0};
UpdateLayeredWindow(hwnd,
hdcScreen,
NULL, NULL,
hdcBmp, &pt,
RGB(0, 0, 0), // black
NULL, ULW_COLORKEY
);
SelectObject(hdcBmp, oldBM);
DeleteDC(hdcBmp);
ReleaseDC(0, hdcScreen);
DeleteObject(hBmp);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
//Workaround for focusables stealing my Esc key
if ((msg.message == WM_KEYDOWN) && (msg.wParam == VK_ESCAPE) {
SendMessage(hwnd, WM_CLOSE, 0, 0);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_NCHITTEST:
{
return HTCAPTION;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
I am currently learning DirectX so I am a starter and I am stuck at one code. I am studying from a book and I have written this code. It should draw a bitmap on the window but its giving me a blank screen. Moreover when I click esc button it gives an error but if I move or stretch the window before pressing esc, it doesnt give an error. Any help appreciated. I am using Visual Studio 2010 and C++. I have one assumption that the error might be at D3DXCreateSurfaceFromFile. Here is the code;
//Header files to include
#include <d3d9.h>
#include <time.h>
#include <d3dx9.h>
//Application title
#define APPTITLE L"Load_Bitmap"
//Screen Resolution
#define WIDTH 640
#define HEIGHT 480
//Forward Declarations
LRESULT WINAPI WinProc( HWND, UINT, WPARAM, LPARAM);
ATOM MyRegisterClass( HINSTANCE);
int GameInit(HWND);
void GameRun(HWND);
void GameEnd(HWND);
//Direct3d objects
LPDIRECT3D9 d3d = NULL;
LPDIRECT3DDEVICE9 d3ddev = NULL;
LPDIRECT3DSURFACE9 backbuffer = NULL;
LPDIRECT3DSURFACE9 surface = NULL;
//Macros to read the keyboard asynchronously
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
//Window Event Callback Function
LRESULT WINAPI WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
GameEnd( hWnd);
PostQuitMessage(0);
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam);
}
//Helper function to set up the window properties
ATOM MyRegisterClass( HINSTANCE hInstance)
{
WNDCLASSEX wc;
wc.cbSize = sizeof( WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WinProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = APPTITLE;
wc.hIconSm = NULL;
//Set up the window with the class info
return RegisterClassEx(&wc);
}
//Entry point for a windows program
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstancem, LPSTR lpCmdLine, int nCmdShow)
{
//Declare variables
MSG msg;
//Register the class
MyRegisterClass( hInstance);
//Initialize Application
HWND hWnd;
//Create new Window
hWnd = CreateWindow( APPTITLE, APPTITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstance, NULL);
if( !hWnd)
return FALSE;
//Display the Window
ShowWindow( hWnd, nCmdShow);
UpdateWindow( hWnd);
//Initialize the Game
if( !GameInit( hWnd))
return FALSE;
//Main Message Loop
int done = 0;
while(!done)
{
if(PeekMessage( &msg, hWnd, 0, 0, PM_REMOVE))
{
//Look for quit message
if( msg.message == WM_QUIT)
done = 1;
//Decode and pass messages on to WndProc
TranslateMessage( &msg);
DispatchMessage( &msg);
}
else
//Process game loop( else prevents running after window is closed)
GameRun(hWnd);
}
return msg.wParam;
}
int GameInit( HWND hWnd)
{
HRESULT result;
//Initialize Direct3d
d3d = Direct3DCreate9(D3D_SDK_VERSION);
if( d3d == NULL)
{
MessageBox( hWnd, L"Error initializing Direct3d", L"Error", MB_OK);
return 0;
}
//Set Direct3D presentation parameters
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferWidth = WIDTH;
d3dpp.BackBufferHeight = HEIGHT;
d3dpp.hDeviceWindow = hWnd;
//Create Direct3D device
d3d->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);
if( d3ddev == NULL)
{
MessageBox( hWnd, L"Error creating Direct3d device", L"Error", MB_OK);
return 0;
}
//Set Random number seed
//srand( time(NULL));
//Clear the backbuffer to black
d3ddev->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0,0,0), 1.0f, 0);
//Create pointer to the back buffer
d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);
//Create surface
result = d3ddev->CreateOffscreenPlainSurface( 640, 480, D3DFMT_X8R8G8B8,
D3DPOOL_DEFAULT, &surface, NULL);
if( result != D3D_OK)
return 1;
//load surface from file
result = D3DXLoadSurfaceFromFile(
surface, NULL, NULL, L"c.bmp", NULL, D3DX_DEFAULT, 0, NULL);
//Make sure file was loaded okay
if( result != D3D_OK)
return 1;
d3ddev->StretchRect( surface, NULL, backbuffer, NULL, D3DTEXF_NONE);
//Return okay
return 1;
}
void GameRun(HWND hWnd)
{
//Make Sure the Direct3d device is valid
if( d3ddev == NULL)
return;
//Start Rendering
if( d3ddev->BeginScene())
{
//Create pointer to the back buffer
d3ddev->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);
//Draw surface to the backbuffer
d3ddev->StretchRect( surface, NULL, backbuffer, NULL, D3DTEXF_NONE);
//StopRendering
d3ddev->EndScene();
}
//Display the back buffer on the screen
d3ddev->Present( NULL, NULL, NULL, NULL);
//Check for escape key( to exit program)
if( KEY_DOWN(VK_ESCAPE))
PostMessage(hWnd, WM_DESTROY, 0, 0);
}
void GameEnd(HWND hWnd)
{
//free the surface
if( surface != NULL)
surface->Release();
//Release the Direct3D device
if( d3ddev != NULL)
d3ddev->Release();
if( d3d != NULL)
d3d->Release();
}
Post WM_QUIT instead of WM_DESTROY when you check the escape key. As it stands now the message-loop will never quit since it depends on WM_QUIT being posted, and it will keep calling GameRun even after the surfaces are deleted.