I have some code that positions windows to screen quadrants. It works fine on Windows XP, 7, and 8/8.1. However, on Windows 10, there is a weird gap between windows. The extra space surrounds the window on all 4 sides. I presume it has something to do with window borders, but can't figure out how to correct the problem. Any input would be highly appreciated. The code is as follows:
// Get monitor info
HMONITOR hm = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(hm, &mi);
// Set screen coordinates and dimensions of monitor's work area
DWORD x = mi.rcWork.left;
DWORD y = mi.rcWork.top;
DWORD w = mi.rcWork.right - x;
DWORD h = mi.rcWork.bottom - y;
switch (corner) {
case 0: // Left top
SetWindowPos(hWnd, HWND_TOP, x, y, w / 2, h / 2, SWP_NOZORDER);
break;
case 1: // Right top
SetWindowPos(hWnd, HWND_TOP, x + w / 2, y, w / 2, h / 2, SWP_NOZORDER);
break;
case 2: // Right bottom
SetWindowPos(hWnd, HWND_TOP, x + w / 2, y + h / 2, w / 2, h / 2, SWP_NOZORDER);
break;
case 3: // Left bottom
SetWindowPos(hWnd, HWND_TOP, x, y + h / 2, w / 2, h / 2, SWP_NOZORDER);
break;
}
I managed to correct this effect by inflating target rectangle by a calculated margin like this:
static RECT GetSystemMargin(IntPtr handle) {
HResult success = DwmGetWindowAttribute(handle, DwmApi.DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS,
out var withMargin, Marshal.SizeOf<RECT>());
if (!success.Succeeded) {
Debug.WriteLine($"DwmGetWindowAttribute: {success.GetException()}");
return new RECT();
}
if (!GetWindowRect(handle, out var noMargin)) {
Debug.WriteLine($"GetWindowRect: {new Win32Exception()}");
return new RECT();
}
return new RECT {
left = withMargin.left - noMargin.left,
top = withMargin.top - noMargin.top,
right = noMargin.right - withMargin.right,
bottom = noMargin.bottom - withMargin.bottom,
};
}
And then doing
RECT systemMargin = GetSystemMargin(this.Handle);
targetBounds.X -= systemMargin.left;
targetBounds.Y -= systemMargin.top;
targetBounds.Width += systemMargin.left + systemMargin.right;
targetBounds.Height += systemMargin.top + systemMargin.bottom;
That worked for all windows I could test it with, except Explorer windows, which I hardcoded to exclude. If I'd do that expansion on Explorer near the screen edge, window ends up spilling a large area past it to the adjacent monitor.
The default font size of windows XP/7/8/8.1 is 100%, but in windows 10 the default is to display text and items in 125%. That affects directly all the window sizes.
Go to settings, display and you will find a scroller, move it to 100% and everything should display the same way as it did in Windows 8/7/XP
Related
I noticed when I try to run BitBlt, the resulting data buffer is unexpected in two ways:
It is flipped along the y axis (the origin seems to be bottom left instead of top left)
In each RGBA grouping, the R and B values seem to be switched.
For the first issue, I noticed it when testing with my command prompt; if my command prompt was in the upper left portion of the screen, it would only say it was black when my cursor was in the lower left portion. I had to fix the inversion of the y axis by changing int offset = (y * monitor_width + x) * 4; to int offset = ((monitor_height - 1 - y) * monitor_width + x) * 4; this fixed the pixel location issue because it was showing black where I expected black.
However, the colors were still strong. I tested by trying to get the color of known pixels. I noticed every blue pixel had a very high R value and every red pixel had a very high blue value. That's when I compared with an existing tool I had and found out that the red and blue values seem to be switched in every pixel. At first I thought it was backwards or a byte alignment issue, but I also verified in a clustering of pixels that aren't uniform to make sure it's picking the right position of pixel, and it did perfectly well, just with the colors switched.
Full simplified code below (originally my tool was getting my cursor position and printing the pixel color via hotkey press; this is a simplified version that gets one specific point).
BYTE* my_pixel_data;
HDC hScreenDC = GetDC(GetDesktopWindow());
int BitsPerPixel = GetDeviceCaps(hScreenDC, BITSPIXEL);
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int monitor_width = GetSystemMetrics(SM_CXSCREEN);
int monitor_height = GetSystemMetrics(SM_CYSCREEN);
std::cout << std::format("monitor width height: {}, {}\n", monitor_width, monitor_height);
BITMAPINFO info;
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = monitor_width; // client_width;
info.bmiHeader.biHeight = monitor_height; // client_height;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = BitsPerPixel;
info.bmiHeader.biCompression = BI_RGB;
HBITMAP hbitmap = CreateDIBSection(hMemoryDC, &info, DIB_RGB_COLORS, (void**)&my_pixel_data, 0, 0);
SelectObject(hMemoryDC, hbitmap);
BitBlt(hMemoryDC, 0, 0, monitor_width, monitor_height, hScreenDC, 0, 0, SRCCOPY);
int x = 12, y = 12;
int offset = ((monitor_height - 1 - y) * monitor_width + x) * 4;
std::cout << std::format("debug: ({}, {}): ({}, {}, {})\n", x, y, (int)my_pixel_data[offset], (int)my_pixel_data[offset + 1], (int)my_pixel_data[offset + 2], (int)my_pixel_data[offset + 3]);
system("pause");
The output of this will be debug: (12, 12): (199, 76, 133) even though another program has verified the colors are actually (133, 76, 199).
I can easily fix this in my code by flipping the y axis and switching each R and B value and the program will work perfectly well. However, I am just baffled by how this happened and whether there's a more elegant fix.
I can answer the RGB (and it looks like Hans answered the inverted Y axis in a comment). Remember that RGB is stored 0xAARRGGBB, so in that 32 bit value BB is byte 0, GG is byte 1, and RR is byte 2 (alpha is byte 3 if you use it), so when you index in at +0, +1 and +2 you're actually getting the values correctly. When we say RGB we're saying the colors in opposite order of how they're stored in memory.
I am writing a win32 low level gui app that emulates a console app. I use a fixed width font, my test uses Cascadia Mono, but I have the same issue with any fixed width font.
The console app is trying to draw a horizontal line using U2500 character.
I output the characters that app is passing me one by one. When I do that I get spaces between the horizontal lines, when I output in one call to textout those gaps are filled in.
I made this using the VS c++ windows app template and added this code to the WM_PAINT handling
auto nHeight = -MulDiv(48, GetDeviceCaps(hdc, LOGPIXELSY), 72);
auto hfont = CreateFont(
nHeight,
0,
0,
0,
100,//200,
0,
0,
0,
DEFAULT_CHARSET,
OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY,
FIXED_PITCH,
L"Cascadia Mono"
);
TEXTMETRIC tm;
SelectObject(hdc, hfont);
GetTextMetrics(hdc, &tm);
auto str = L"kkkkkk─────k";
TextOut(hdc, 0, 0, L"kkkkkk─────k", 12);
for (int i = 0; i < 12; i++)
{
TextOut(hdc, i * tm.tmAveCharWidth, tm.tmHeight, &str[i], 1);
}
This displays
you can see that this is not due to me miscalculating the char cell width, the strings are exactly aligned , just there are some added pixels in the upper one, also notice some extra 'knobiness' where the joins are. V odd. Also note that the right edge of the last K before the line starts is slightly chopped off in the char by char one, but not in the all at once one.
So why am I doing it char by char, because I need to specify font weight, bg, fg for each cell.
Instead of using TextOut, you can use DrawText which is a bit more hi-level, like this:
for (int i = 0; i < 12; i++)
{
RECT rc;
rc.left = i * tm.tmAveCharWidth;
rc.top = tm.tmHeight;
rc.right = rc.left + 50; // todo: make sure this is ok
rc.bottom = rc.top + 100;
DrawText(hdc, (LPWSTR)&str[i], 1, &rc, 0);
}
And it seems to fix the "lineness" of it, although it's not 100% exactly the same (there are some pixels that show a difference):
I am trying to get a uint8_t[] of a portion of the screen. The xy coordinates of the top left is 2,3 and of bottom right is 17,18.
This is the top 30x30 pixels of my screen with regular screenshot and photoshop crop:
And this is what 2,3 to 17,18 screenshot should look like via regular screenshot and Photoshop crop:
This is what I am getting as a result of my code:
My code is in js-ctypes but there is no ctypes errors. This is a winapi thing. So I didn't tag this with ctypes as they would be confused. This is the simplified code I am using, the error checks etc have been removed:
c1 = {x:2, y:3} // top left corner
c2 = {x:17, y:18} // bottom right corner
CreateDC('DISPLAY', null, null, null);
nBPP = GetDeviceCaps(hdcScreen, BITSPIXEL);
w = c2.x - c1.x; // width = 15
h = c2.y - c1.y; // height = 15
hdcMemoryDC = CreateCompatibleDC(hdcScreen);
bmi = BITMAPINFO();
bmi.bmiHeader.biSize = BITMAPINFOHEADER.size;
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -1 * h;
bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = nBPP; // nBPP is 32
bmi.bmiHeader.biCompression = BI_RGB;
hbmp = CreateDIBSection(hdcScreen, &bmi, DIB_RGB_COLORS, (void**)&pixelBuffer, null, 0);
SelectObject(hdcMemoryDC, hbmp);
BitBlt(hdcMemoryDC, 0, 0, w, h, hdcScreen, c1.x, c1.y, SRCCOPY);
Why portion of screen bits come out wrong? If I do a full screen shot it works fine.
I had created a dialog with some controls -
IDD_DIALOG_EFFECTS DIALOGEX 0, 0, 168, 49
STYLE DS_SETFONT | WS_CHILD
FONT 8, "MS Sans Serif", 400, 0, 0x1
BEGIN
--- ---
--- ---
CTEXT "",3,200,120,60,60, WS_VISIBLE
END
In Header - file:
const int16 kItem = 3;
Now when I am trying to get the position and dimension of the control, it's not accurate.
// Retrieving the location and dimension of the control
RECT wRect_proxy;
GetWindowRect(GetDlgItem(hDlg, kItem), &wRect_proxy);
ScreenToClient (hDlg, (LPPOINT)&wRect_proxy);
ScreenToClient (hDlg, (LPPOINT)&(wRect_proxy.right));
// Output of the control as location and position that I am getting is:
wRect_proxy.left: 300 (Expected: 200)
wRect_proxy.top: 195 (Expected: 120)
wRect_proxy.right: 390 (Expected: 60)
wRect_proxy.bottom: 293 (Expected: 60)
I need to calculate width - height of the control. Seeking help ...
What you receive IS the height of the contro!
The RC file uses Dialog Base Units.
When the Dialog is created the specific font is used to find howmany Pixels are 1 DLU.
Internally MapDalogRect is used to convert the values from the RC file to the final number of Pixels.
Using MapDialogrect on CRect(0,0,4,8) gives you the base values of 1 DLU.
Now take the x-width multiply with 4 and divide by the "width" base units you just calculated. For the y-height multiply with 8 and divide by the
"height".
This can be easily done with MulDiv.
Thanks a Ton ... :)
As per your guidance and suggestion the snippet will be:
// Summarizing the code-snippet.
RECT wRect;
GetWindowRect(GetDlgItem(hDlg, kDProxyItem), &wRect);
ScreenToClient (hDlg, (LPPOINT)&wRect);
ScreenToClient (hDlg, (LPPOINT)&(wRect.right));
RECT pixel_rect;
pixel_rect.left = 0;
pixel_rect.top = 0;
pixel_rect.right = 4;
pixel_rect.bottom = 8;
bool b_check = MapDialogRect(hDlg, &pixel_rect);
LONG base_pix_width = pixel_rect.right;
LONG base_pix_height = pixel_rect.bottom;
// Calculating acctual X,Y coordinates with Width - Height of the Proxy Rectangle
RECT proxy_acc_dim;
proxy_acc_dim.left = (wRect.left * 4 / base_pix_width);
//or we can do the same by: MulDiv(wRect.left, 4, base_pix_width);
proxy_acc_dim.right = (wRect.right * 4 / base_pix_width) - proxy_acc_dim.left;
//or we can do the same by: MulDiv(wRect.right, 4, base_pix_width);
proxy_acc_dim.top = (wRect.top * 8 / base_pix_height);
//or we can do the same by: MulDiv(wRect.top, 8, base_pix_height);
proxy_acc_dim.bottom = (wRect.bottom * 8 / base_pix_height) - proxy_acc_dim.top;
//or we can do the same by: MulDiv(wRect.bottom, 8, base_pix_height);
proxy_acc_dim.left = proxy_rect.left;
proxy_acc_dim.top = proxy_rect.top;
proxy_acc_dim.right = proxy_rect.right - proxy_rect.left;
proxy_acc_dim.bottom = proxy_rect.bottom - proxy_rect.top;
It works fine. Hope it would be help full to others ...
by using directshow.net i can able to record the video and with recording i am doing text overlay for this i configured sample grabber and in buffercb method i am working on frames here is the code..
int ISampleGrabberCB.BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
Graphics g;
String s;
float sLeft;
float sTop;
SizeF d;
g = Graphics.FromImage(bitmapOverlay);
g.Clear(System.Drawing.Color.Transparent);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// Prepare to put the specified string on the image
g.DrawRectangle(System.Drawing.Pens.Transparent, 0, 0, 240 - 1, 176 - 1);
g.DrawRectangle(System.Drawing.Pens.Transparent, 1, 1, 240 - 3, 176 - 3);
d = g.MeasureString(m_String + "\n" + DateTime.Now.ToString("G"), fontOverlay);
sLeft = (240 - d.Width) / 2;
sTop = (176 - d.Height) / 2;
g.DrawString(m_String + "\n" + DateTime.Now.ToString("G"), fontOverlay, System.Drawing.Brushes.Black,
sLeft, sTop, System.Drawing.StringFormat.GenericTypographic);
// need to flip the bitmap so it's the same orientation as the
// video buffer
bitmapOverlay.RotateFlip(RotateFlipType.RotateNoneFlipY);
// create and copy the video's buffer image to a bitmap
Bitmap v;
v = new Bitmap(240, 176, 1056,
PixelFormat.Format24bppRgb, pBuffer);
g = Graphics.FromImage(v);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// draw the overlay bitmap over the video's bitmap
g.DrawImage(bitmapOverlay, 0, 0, bitmapOverlay.Width, bitmapOverlay.Height);
// dispose of the various objects
g.Dispose();
v.Dispose();
// Increment frame number. Done this way, frame are zero indexed.
m_Count++;
return 0;
}
my problem is,when i start program it shows me text overlay in preview window but when i open recorded file text overlay is not continues..i think i am missing some frames..on some frames overlay is their but its not continues..its flicking.
can any one help?
ok i got the problem!!
in the above code,BufferCB takes too long time to process the current video frame.its like let frame A is still in middle process before process complete frame B comes in.
so to minimize the processing in BufferCB i have remove the where bitmap image is set
this line of code i put into an function
g = Graphics.FromImage(bitmapOverlay);
g.Clear(System.Drawing.Color.Transparent);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// Prepare to put the specified string on the image
g.DrawRectangle(System.Drawing.Pens.Transparent, 0, 0, 240 - 1, 176 - 1);
g.DrawRectangle(System.Drawing.Pens.Transparent, 1, 1, 240 - 3, 176 - 3);
d = g.MeasureString(m_String + "\n" + DateTime.Now.ToString("G"), fontOverlay);
sLeft = (240 - d.Width) / 2;
sTop = (176 - d.Height) / 2;
g.DrawString(m_String + "\n" + DateTime.Now.ToString("G"), fontOverlay, System.Drawing.Brushes.Black,
sLeft, sTop, System.Drawing.StringFormat.GenericTypographic);
// need to flip the bitmap so it's the same orientation as the
// video buffer
bitmapOverlay.RotateFlip(RotateFlipType.RotateNoneFlipY);
and this function is called before media.run is called.