DirectWrite rendering issues - quads seem to be overlapping, with some aliasing - windows

Screenshot of the problem:
Here are the relevant bits of code - error handling etc. are omitted for clarity.
D2D1_BITMAP_PROPERTIES1 bp;
bp.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
bp.dpiX = 96.0f;
bp.dpiY = 96.0f;
bp.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
bp.colorContext = nullptr;
...
d2dDeviceContext->SetTarget(targetBitmap);
....
writeFactory->CreateTextFormat(L"Arial", nullptr, DWRITE_FONT_WEIGHT_LIGHT, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL, 32.0f, L"en-US", &textFormatAccountCreds);
textFormatAccountCreds->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
textFormatAccountCreds->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
std::wostringstream outAccountName;
outAccountName << "Account Name:";
writeFactory->CreateTextLayout(outAccountName.str().c_str(), (UINT32)outAccountName.str().size(), textFormatAccountCreds, (float)800,
(float)600, &textLayoutAccountName);
const float color[4] = { 1.0f, 0.5f, 0.3f, 1.0f };
immediateContext->ClearRenderTargetView(renderTargetView, color);
d2dDeviceContext->BeginDraw();
d2dDeviceContext->DrawTextLayout(D2D1::Point2F(2.0f, 5.0f), textLayoutAccountName, blackBrush);
d2dDeviceContext->EndDraw();
swapChain->Present(0, 0);
As you can see in the screenshot, the text is rendering very poorly - specifically, the first "n" seems to be cut off on the left, as well as the first "u", and the capital "N" has some weird aliasing going on. I can change the size/position of the text, and the rendering issues manifest differently, but they still persist.
What am I missing here? I've been through most of the DirectWrite docs on MSDN and it's not clear what my problem is. I do see some mention that I should specify the starting position of my text relative to the dpi scale, but the example is very vague.
It's also worth mentioning that I'm using Direct3D and DirectWrite with Direct2D all together, in a similar fashion as seen in this tutorial. Not sure if that's causing my problem.
I would be very grateful for any assistance. Thanks much.

I figured it out. Cheers to VTT for pointing me in the right direction.
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
NULL,
NULL,
hInstance,
NULL
);
...
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 1;
sd.BufferDesc.Width = 800;
sd.BufferDesc.Height = 600;
sd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hWnd;
sd.SampleDesc.Count = 4;
sd.SampleDesc.Quality = m4xMsaaQuality - 1;
sd.Windowed = TRUE;
sd.Flags = 0;
Apparently the dimensions you pass into CreateWindow INCLUDE space for the window's menu and borders, so the ACTUAL dimensions of the space you can draw within the window are smaller.
RECT rect;
GetClientRect(hWnd, &rect);
// Returns 784 x 561
So the solution was to:
-Omit specifying sd.BufferDesc.Width and sd.BufferDesc.Height in DXGI_SWAP_CHAIN_DESC, because when they're set to 0, they will inherit dimensions from the parent window.
-Get the actual dimensions using GetClientRect when actual window dimensions are needed later on.

Related

Going Fullscreen in win32 without 2 WM_SIZE message

So I am creating a fullscreen function in win32 c++ doing:
uint8_t isFullscreen = 0;
RECT winRect; //Current Window Rect
RECT nonFullScreenRect; //Rect Not In Full Screen Position (used to restore window to not full screen position when coming out of fullscreen)
uint32_t screen_width = DEFAULT_SCREEN_WIDTH;
uint32_t screen_height = DEFAULT_SCREEN_HEIGHT;
void Fullscreen( HWND WindowHandle )
{
isFullscreen = isFullscreen ^ 1;
if( isFullscreen )
{
//saving off current window rect
nonFullScreenRect.left = winRect.left;
nonFullScreenRect.right = winRect.right;
nonFullScreenRect.bottom = winRect.bottom;
nonFullScreenRect.top = winRect.top;
SetWindowLongPtr( WindowHandle, GWL_STYLE, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE ); //causes a resize msg
HMONITOR hmon = MonitorFromWindow(WindowHandle, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi = { sizeof( mi ) };
GetMonitorInfo( hmon, &mi );
screen_width = mi.rcMonitor.right - mi.rcMonitor.left;
screen_height = mi.rcMonitor.bottom - mi.rcMonitor.top;
MoveWindow( WindowHandle, mi.rcMonitor.left, mi.rcMonitor.top, (int32_t)screen_width, (int32_t)screen_height, FALSE );
}
else
{
SetWindowLongPtr( WindowHandle, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE );
screen_width = nonFullScreenRect.right - nonFullScreenRect.left;
screen_height = nonFullScreenRect.bottom - nonFullScreenRect.top;
MoveWindow( WindowHandle, nonFullScreenRect.left, nonFullScreenRect.top, (int32_t)screen_width, (int32_t)screen_height, FALSE );
}
}
However when it goes fullscreen, the function generates 2 WM_SIZE messages. While when it goes windowed, it generates only 1.
Why is that the case? And how can I make it generate only 1 WM_SIZE message for the proper full screen size?
How can I update an HWND's style and position atomically? asks about it but no one answers it
The reasons I need this is because I am using DirectX12 and on WM_SIZE I wait for all the signals at the end of the command queues before resizing all the swap chain back buffers. And I don't want to have to resize the swap chain twice when switching to fullscreen mode.
case WM_SIZE:
{
screen_width = LOWORD( LParam );
screen_height = HIWORD( LParam );
//DirectX stuff here
}break;
Thanks in advance!
Updated Answer:
The Win32 API allows you to modify parameters of the window one at a time. When a parameter is modified, the API may or may not update the window and trigger a WM_SIZE that will be the size of the window given the current parameters.
Since to have a complete full screen window you need to make at least 2 calls, one to update GWL_STYLE and another to update GWL_EXSTYLE, you have a big chance of getting 2 WM_SIZE calls. One of them will give you the window size without the menu, and the other the full screen window size. It depends in which order you call SetWindowLongPtr, but you'll probably get 2 WM_SIZE and only the second one is "correct", i.e. the one you want in the end.
The more reliable solution to your problem is to use a variable at the top of Main.cpp:
int isTogglingFullScreen = false;
Then inside your full screen toggle code (note where isTogglingFullScreen is being set):
case WM_SYSKEYDOWN:
if (wParam == VK_RETURN && (lParam & 0x60000000) == 0x20000000)
{
// Implements the classic ALT+ENTER fullscreen toggle
if (s_fullscreen)
{
isTogglingFullScreen = true;
SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
isTogglingFullScreen = false;
SetWindowLongPtr(hWnd, GWL_EXSTYLE, 0);
int width = 800;
int height = 600;
if (game)
game->GetDefaultSize(width, height);
SetWindowPos(hWnd, HWND_TOP, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
ShowWindow(hWnd, SW_SHOWNORMAL);
}
else
{
isTogglingFullScreen = true;
SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST);
SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP);
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
isTogglingFullScreen = false;
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
}
s_fullscreen = !s_fullscreen;
}
break;
Finally, inside WM_SIZE, change
else if (!s_in_sizemove && game)
{
game->OnWindowSizeChanged(LOWORD(lParam), HIWORD(lParam));
}
to
else if (!s_in_sizemove && game && !isTogglingFullScreen)
{
game->OnWindowSizeChanged(LOWORD(lParam), HIWORD(lParam));
}
This will give you a single call to OnWindowSizeChanged() when you toggle full screen, and the call will be with the correct final size.
--
Old Answer:
If you only want a single WM_SIZE to trigger, when you switch to full screen then you should go for something like this:
SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST);
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
Any SetWindowLongPtr call to GWL_STYLE will trigger a WM_SIZE, so make sure it's only called with GWL_EXSTYLE. For example, if you both set GWL_EXSTYLE to what you want, and reset GWL_STYLE to 0, you'll trigger WM_SIZE twice.
To make it clearer:
Don't use GWL_STYLE in SetWindowLongPtr because it triggers a useless WM_SIZE
ShowWindow will trigger WM_SIZE
The above code will ultimately only trigger WM_SIZE once.
It turns out that YMMV. It's entirely possible that the first time you switch fullscreen, you'll get 2 WM_SIZE. One will be with the original size, and the other with the new size. Subsequent calls will trigger only one WM_SIZE.
Hence, the really bulletproof solution (which I was using anyway before playing around with the SetWindowLongPtr to answer this question, is to validate that the window size has actually changed. Because one thing that I can guarantee in the above call is that you'll never get more than 1 call with the new size. At most you'll get a WM_SIZE call with the old size, which you'll discard by checking that it's the same as the current size.
If you use the DevicesResources template for DX12, you'll see that there's a check in OnWindowSizeChanged() that does nothing if the size hasn't changed.

WinAPI + Cmake + Aero

I have a CMake project with an executable and a static library (linked to the exe). The library is responsible for implementing the creation of the window (using WinAPI for Windows OS), and the executable contains the main entry point (in this case a simple int main(...) function).
I've been googling for a day but cannot find a way to create a window with Aero support (can maximize by dropping to the top, the title bar is a little bit transparent, etc). I've read the Enabling Visual Styles MSDN article but I'm not sure how I should handle this with CMake. Especially that the window implementation is hidden to the client (since it's implemented in the library).
The windowing code is really basic right now for simplicity. Some of the code will be refactored, the point is not that right now. Here is the (almost) full code for creating the window.
bool WindowsWindow::create(const WindowCreateInfo& info)
{
// register custom window class
{
WNDCLASSEX wnd = { 0 };
wnd.cbSize = sizeof(wnd);
wnd.lpszClassName = CLASS_NAME;
wnd.hInstance = GetModuleHandle(nullptr);
wnd.lpfnWndProc = wndProc;
wnd.style = CS_OWNDC;
wnd.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
if (RegisterClassEx(&wnd) == 0) {
return false;
}
hInstance = wnd.hInstance;
}
HMONITOR monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(monitor, &monitorInfo);
LONG style = 0;
LONG exStyle = WS_EX_APPWINDOW;
int x = CW_USEDEFAULT;
int y = CW_USEDEFAULT;
int width = info.width;
int height = info.height;
if (info.isWindowed) {
style = WS_OVERLAPPED | WS_BORDER | WS_CAPTION;
if (info.hasSysMenu) {
style |= WS_SYSMENU;
}
if (info.allowMinimize) {
style |= WS_MINIMIZEBOX;
}
if (info.allowMaximize) {
style |= WS_MAXIMIZEBOX;
}
// ... positioning, adjusting size, etc.
} else {
style = WS_POPUP;
x = monitorInfo.rcMonitor.left;
y = monitorInfo.rcMonitor.top;
width = monitorInfo.rcMonitor.right - x;
height = monitorInfo.rcMonitor.bottom - y;
}
handle = CreateWindowEx(
exStyle,
CLASS_NAME,
info.title,
style,
x,
y,
width,
height,
HWND_DESKTOP,
nullptr,
hInstance,
nullptr
);
if (!handle) {
return false;
}
running = true;
ShowWindow(handle, SW_SHOW);
return true;
}
The loop is the standard Peek-Translate-Dispatch trio and the WndProc only handles the WM_CLOSE message, otherwise returns with DefWindowProc.
How could I enable the Aero support in this kind of setup?
If the proper manifest file would be the solution, how should I handle it correctly? I mean the client (the executable) should not care that the underlying library is using WinAPI or not
A CMake example would be really helpful
Current solution
I was able to find an example (actually from a Vulkan SDK) that pointed out that I need the WS_THICKFRAME style (a resize border) in order to my window become modern looking (Aero-like).

Printwindow prints with empty space

The width and height of application Melon is 438 x 615 pixels and ::GetWindowRect() function grab it correctly.
However, ::PrintWindow() function draws smaller size which is 348 x 489 pixels, rest of them filled with black blank(may be draw nothing)
...may the one picture will be better than hundreds of discription.
here is result of the code
bool result = true;
HWND appHWnd = ::FindWindow(nullptr, TEXT("Melon"));
RECT appWindowRect; ::GetWindowRect(appHWnd, &appWindowRect);
HDC appDC = ::GetWindowDC(appHWnd);
// HDC appDC = ::GetDC(appHWnd); // same issue occured either
// HDC appDC = ::GetDC(nullptr);
HDC memoryDC = ::CreateCompatibleDC(appDC);
HBITMAP capturedScreenBitmap = ::CreateCompatibleBitmap(
appDC,
appWindowRect.right - appWindowRect.left,
appWindowRect.bottom - appWindowRect.top
);
HBITMAP memoryBitmap = static_cast<HBITMAP>(::SelectObject(memoryDC, capturedScreenBitmap));
result = ::PrintWindow(appHWnd, memoryDC, 0);
//copy to clipboard
OpenClipboard(nullptr);
EmptyClipboard();
SetClipboardData(CF_BITMAP, capturedScreenBitmap);
CloseClipboard();
::SelectObject(memoryDC, memoryBitmap);
::DeleteObject(capturedScreenBitmap);
::DeleteDC(memoryDC);
::ReleaseDC(appHWnd, appDC);
Strangely, C# version of the code works correctly. import same user32 library, use same of it and output different result? why?
It will be down to DPI awareness – David Heffernan
::GetWindowRect, which used to c# project and C++ console project in Visual Studio, aren't affected from the scaling by dpi awareness. but, what used to qt studio are affected from it.
here is my solution.
RECT appWindowRect; {
::GetWindowRect(hwnd, &appWindowRect);
}
POINT appWindowSize; {
qreal dotsPerInch = QApplication::screens().at(0)->logicalDotsPerInch();
appWindowSize.x = static_cast<LONG>((appWindowRect.right - appWindowRect.left) * 96 / dotsPerInch);
appWindowSize.y = static_cast<LONG>((appWindowRect.bottom - appWindowRect.top) * 96 / dotsPerInch);
}

How to create a window and fill it with color using OpenES 2.0 + X11?

I googled as hard as I can, but I found nothing.
What I want to do:
create a window with X11 (Xlib) and show it
fill the window with color using OpenGL ES 2.0
For OpenGL ES 2.0 support on my ArchLinux, I use MESA. I know how to create a simple X window using Xlib, I have a basic knowledge of EGL and OpenGL ES, but I can't understand how to use all them (X11 + EGL + OpenGL ES 2.0) in conjuction.
I would be very thakful if someone wrote at least a short code example on how to prepare a X window and connect it with OpenGL ES 2.0 correctly and start rendering.
Create Window:
Window root;
XSetWindowAttributes swa;
XSetWindowAttributes xattr;
Atom wm_state;
XWMHints hints;
XEvent xev;
EGLConfig ecfg;
EGLint num_config;
Window win;
/*
* X11 native display initialization
*/
x_display = XOpenDisplay(NULL);
if ( x_display == NULL )
{
return EGL_FALSE;
}
root = DefaultRootWindow(x_display);
swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask;
win = XCreateWindow(
x_display, root,
0, 0, esContext->width, esContext->height, 0,
CopyFromParent, InputOutput,
CopyFromParent, CWEventMask,
&swa );
xattr.override_redirect = FALSE;
XChangeWindowAttributes ( x_display, win, CWOverrideRedirect, &xattr );
hints.input = TRUE;
hints.flags = InputHint;
XSetWMHints(x_display, win, &hints);
// make the window visible on the screen
XMapWindow (x_display, win);
XStoreName (x_display, win, title);
// get identifiers for the provided atom name strings
wm_state = XInternAtom (x_display, "_NET_WM_STATE", FALSE);
memset ( &xev, 0, sizeof(xev) );
xev.type = ClientMessage;
xev.xclient.window = win;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 1;
xev.xclient.data.l[1] = FALSE;
XSendEvent (
x_display,
DefaultRootWindow ( x_display ),
FALSE,
SubstructureNotifyMask,
&xev );
Set color:
glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );
// Set the viewport
glViewport ( 0, 0, esContext->width, esContext->height );
// Clear the color buffer
glClear ( GL_COLOR_BUFFER_BIT );
Sources:
https://github.com/danginsburg/opengles-book-samples/blob/master/LinuxX11/Chapter_2/Hello_Triangle/Hello_Triangle.c
https://github.com/danginsburg/opengles-book-samples/blob/master/LinuxX11/Common/esUtil.c
https://github.com/danginsburg/opengles-book-samples/blob/master/LinuxX11/Common/esUtil.h
The opengles-book-samples code actually fails for me. If you see the same init failures, a call to eglBindAPI(EGL_OPENGL_ES_API) seemed to fix it.

Windows Tooltips with style TTS_BALLOON not shown on some installations

I maintain a Win32 desktop application that shows tooltips. This so far works pretty well on many XP And Windows 7 installations.
We now get reports from a few customers that they do not see our tool tips. The See a rectangular tooltip (that does not have the TTS_BALLOON attribute. But those created with TTS_BALLOON are not visible. The log files sent by a customer report that CreateWindowEx returns a valid windows handle as well as the coordinates and string contained are correct.
The machine concerned runs Windows XP and is updated regularly.
Has anybody encountered a similar behavior?
How can we solve this problem?
Source Code:
gHintInfo.hwnd = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL,
(HINSTANCE)xvt_vobj_get_attr(TASK_WIN, ATTR_WIN_INSTANCE),
NULL);
Trace(1, "\n### DrawHint %d, hwnd = %08x, Text =\n%s\n###\n\n", __LINE__, gHintInfo.hwnd, tx);
if (gHintInfo.hwnd != NULL)
{
TOOLINFO ti;
ti.cbSize = sizeof (ti);
ti.uFlags = TTF_TRANSPARENT | TTF_ABSOLUTE;
ti.hwnd = hwndParent;
ti.uId = 0;
ti.hinst = NULL;
ti.lpszText = (char *) tx;
GetClientRect (hwndParent, &ti.rect);
dbgrct(ti.rect);
dbgpnt(gHintInfo.LastHintLoc);
SendMessage(gHintInfo.hwnd, TTM_TRACKPOSITION,0, MAKELONG(gHintInfo.LastHintLoc.v, gHintInfo.LastHintLoc.h));
SendMessage (gHintInfo.hwnd, TTM_ADDTOOL, 0, (long) &ti);
SendMessage (gHintInfo.hwnd, TTM_SETDELAYTIME, TTDT_AUTOMATIC, -1);
SendMessage (gHintInfo.hwnd, TTM_SETMAXTIPWIDTH, 0, 500);
SendMessage (gHintInfo.hwnd, TTM_TRACKACTIVATE, TRUE, (long) &ti);
}
The log output created by this code on the machine that does not diesplay the tooltips is:
### DrawHint 474, hwnd = 00090112, Text =
Some text with
multiple lines
###
ti.rect left = 0, top = 0, right = 1280, bottom = 978
gHintInfo.LastHintLoc h = 295, v = 539
(We set ti.rect to the coordiantes of the whole screen, as windows resizes the tool tip to the containing text anyway.)
EDIT:
We actually added a configuration property to our Software that does nothing more than adding or removing the TTS_BALLOON attribute. This solves the problem on the machines concerned.
The best solution can be found here.
To disable the tooltip balloon, set EnableBalloonTips to 1

Resources