QT: Scroll widget that renders directly to the DC - windows

I'm trying to create a widget which paints directly to the windows Device Context by calling getDC() and painting an HBITMAP to it.
The widget I'm painting resides inside a scroll widget.
I've implemented the paintEvent() and it does seem to paint but immediatly after painting the widget gets painted over again with a blank gray color.
I've tried setting WA_PaintOnScreen and Qt::WA_NoSystemBackground but none of those help.
In theory this should be possible since this is basically how the GLWidget works.
What am I missing?

Found the answer here:
http://www.qtchina.net/qt4c++guiprogramming/ch20lev1sec1.html/
void GdiControl::paintEvent(QPaintEvent * /* event */)
{
RECT rect;
GetClientRect(winId(), &rect);
HDC hdc = GetDC(winId());
FillRect(hdc, &rect, HBRUSH(COLOR_WINDOW + 1));
SetTextAlign(hdc, TA_CENTER | TA_BASELINE);
TextOutW(hdc, width() / 2, height() / 2, text.utf16(), text.size());
ReleaseDC(winId(), hdc);
}
For this to work, we must also
reimplement
QPaintDevice::paintEngine() to return
a null pointer and set the
Qt::WA_PaintOnScreen attribute in the
widget's constructor.

Related

How to prevent the win32 jagged-edges?

I'm creating a round button, win32 has the HRGN object to define the region of window.
So here is what I do:
int Button_Test(HWND hwnd)
{
//button
HWND btn=CreateWindowEx(NULL,WC_BUTTON,"Test",(WS_TABSTOP|WS_CHILD|WS_VISIBLE|BS_OWNERDRAW|WS_CLIPSIBLINGS),
10,10,80,80,hwnd,(HMENU)ID_BTN01,
(HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),NULL);
//set region.
RECT rc;
GetWindowRect(btn,&rc);
HRGN rgn=CreateEllipticRgn(0,0,rc.right-rc.left,rc.bottom-rc.top);
if(rgn==NULL) MessageBox(hwnd,"Create region failed.","Sorry...",MB_OK|MB_ICONERROR);
else SetWindowRgn(btn,rgn,FALSE);
return 0;
}
The button has the jagged-edges like this.
So how to prevent the jagged-edges effect? I mean how to make the edge more smoothly , without saw edges.
using gdiplus smoothly-mode pixels combine regions, it looks like this, as far as I see , it works:

Reduce Drag & Drop Flickering

I've a problem with my code. I've created a class for Drag & Drop but during the dragging of the object i see a annoying flickering effect for the object that i'm dragging
void CDragDropListBox::DrawDragRect(CPoint point, CDragItem* DragItem,SIZE Size)
{
CDC* pDC = GetDC();
ScreenToClient(&point);
//Rect centered under mouse pointer
point.x -= Size.cx/2;
point.y -= Size.cy/2;
CRect rectFull(point,m_RectSize);
//Delete previous rect
ClientToScreen(&m_OldRect);
_DstWnd->ScreenToClient(&m_OldRect);
_DstWnd->InvalidateRect(m_OldRect, true);
_DstWnd->UpdateWindow();
//Draw new rect based on mouse position
DrawSelectFrame(pDC,rectFull);
DrawSingleItem(DragItem,pDC,rectFull);
m_OldRect = rectFull;
}
In my code every time i move the mouse i delete the previously drawn drag-rect and paint a new one, but the flickering is very fastidious...
There is anything i can do?
The main problem here is, that the erasing and painting is done in two steps.
And even if it is executed fast. It looks like flickering.
So don't use WM_ERASEBKGND, and use double buffering (with a memory DC) in WM_PAINT.
See CMemDC in Codeproject for a good and easy class.
I've edited my code for remove the use of UpdateWindow()
void CDragDropListBox::DrawDragRect(CPoint point, CDragItem* DragItem,SIZE Size)
{
CDC* pDC = _DstWnd->GetDC();
CDC dcMemory;
ScreenToClient(&point);
dcMemory.CreateCompatibleDC(pDC);
CDC* olddc= pDC;
CRect tmprect;
pDC->GetClipBox(&tmprect);
CBitmap tmpbmp;
tmpbmp.CreateCompatibleBitmap(pDC, tmprect.Width(), tmprect.Height());
CBitmap* OldBmp;
OldBmp = dcMemory.SelectObject(&tmpbmp);
point.x -= Size.cx/2;
point.y -= Size.cy/2;
CRect rectFull(point,m_RectSize);
ClientToScreen(&m_OldRect);
_DstWnd->ScreenToClient(&m_OldRect);
_DstWnd->InvalidateRect(m_OldRect, TRUE);
m_OldRect = rectFull;
ClientToScreen(&rectFull);
_DstWnd->ScreenToClient(&rectFull);
DrawSelectFrame(pDC,rectFull);
DrawSingleItem(DragItem,pDC,rectFull);
_DstWnd->ValidateRect(rectFull);
//_DstWnd->UpdateWindow();
olddc->BitBlt(tmprect.left, tmprect.top, tmprect.Width(), tmprect.Height(), pDC, tmprect.left, tmprect.top, SRCCOPY);
dcMemory.SelectObject(OldBmp);
}
Flickering less, but can i further improve?

display obscured control on bitmap

I have the HDC=hdc of a bit map, a rectangle R with logical coordinates in hdc, and the HWND=hwnd of a scroll control created by CreateWindow with SBS_HORZ. The scroll control is is the child of another window. I want to display the scroll control on the bitmap in rectangle R.
I obtained a HDC for the scroll control and used BitBlt to copy the control to the rectangle. All works well if the entire scroll control is visible in it's parent BUT if the scroll bar is obscured, I get what ever is on top of the bar. If the control is off the screen I get nothing.
This is all part of an effort to periodically save a screen image of the app in case you are wondering how the scroll bar can be obscured. I do not want to bring the scroll bar's parent to the front.
Is there anyway I can get a true image of the scroll bar in these conditions?
Or alternatively, could I somehow make a scroll bar that wasn't displayed who's contents I could copy? I do know all the parameters needed.
I found the following seems to work even if the control is obscured or off the screen. Create a DC and compatible bitmap from the control. Send the control a WM_PRINT message asking it to print itself in the DC/Bitmap. Then copy the bitmap using BitBlt.
Pretty ugly! Is there a better way?
Something like this...
HDC hdcScroll;
WINDOWPLACEMENT WP;
HDC memdc;
HBITMAP membit;
hdcScroll = GetDC (hwndScroll);
GetWindowPlacement (hwndScroll, &WP);
int Height = WP.rcNormalPosition.bottom - WP.rcNormalPosition.top;
int Width = WP.rcNormalPosition.right - WP.rcNormalPosition.left;
memdc = CreateCompatibleDC(hdcScroll); // destination DC
membit = CreateCompatibleBitmap(hdcScroll, Width, Height); // destination bitmap
HBITMAP hOldBitmap =(HBITMAP) SelectObject(memdc, membit); // add bitmap to DC
SendMessage (hwndScroll,WM_PRINT,(WPARAM) memdc, PRF_CLIENT);
BitBlt
(hdc, // destination HDC
rt_scroll.left, // dest upper left corner X
rt_scroll.top, // dest upper left corner Y
rt_scroll.right-rt_scroll.left+1, // width of dest rectangle
rt_scroll.bottom-rt_scroll.top+1, // height of dest rectangle
memdc, // source HDC
0, // source upper left corner X
0, // source upper left cornet Y
SRCCOPY
);
SelectObject(memdc, hOldBitMap);
DeleteObject (membit);
DeleteDC (memdc);
ReleaseDC (hwndScroll, hdcScroll);

If window spans multiple monitors, I can't draw to it

If I have a window that spans both monitors on a multimonitor system, I can't seem to erase (paint black) the entire window. Instead, only the primary window is drawn black. The secondary remains the original white color. Has anyone seen this behavior?
wxwidgets:
wxClientDC dc(this);
Erase(dc);
void SpriteWindowFrame::Erase(wxDC& dc)
{
dc.SetBackground(*wxBLACK_BRUSH);
dc.SetBrush(*wxBLACK_BRUSH);
dc.Clear();
//wxLogDebug("Erase called. Rect is %i, %i w:%i, h:%i", GetPosition().x, GetPosition().y, GetSize().GetWidth(), GetSize().GetHeight());
}
Inside dc.Clear() function, there is this code
wxwidgets:
void wxDC::Clear()
{
WXMICROWIN_CHECK_HDC
RECT rect;
if ( m_canvas )
{
GetClientRect((HWND) m_canvas->GetHWND(), &rect);
}
else
{
// No, I think we should simply ignore this if printing on e.g.
// a printer DC.
// wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
if (!m_selectedBitmap.Ok())
return;
rect.left = -m_deviceOriginX; rect.top = -m_deviceOriginY;
rect.right = m_selectedBitmap.GetWidth()-m_deviceOriginX;
rect.bottom = m_selectedBitmap.GetHeight()-m_deviceOriginY;
}
#ifndef __WXWINCE__
(void) ::SetMapMode(GetHdc(), MM_TEXT);
#endif
DWORD colour = ::GetBkColor(GetHdc());
HBRUSH brush = ::CreateSolidBrush(colour);
::FillRect(GetHdc(), &rect, brush);
::DeleteObject(brush);
#ifndef __WXWINCE__
int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
::SetMapMode(GetHdc(), MM_ANISOTROPIC);
::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
::SetWindowExtEx(GetHdc(), width, height, NULL);
::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
#endif
}
Using the debugger, I checked what GetClientRect returned and sure enough it returns a rectange with location 0 and width/height of the combined two monitors so it's right. Maybe fillrect function is not capable of drawing to two displays?
Can you trace into the constructor of the wxClientDC?
wxClientDC dc(this);
A lot depends on what type of DC wx has given you. The windows API to retrieve a window DC is hdc = GetDC(hwnd), and, on multimonitor systems, it retrieves a handle to a 'mirror driver' DC, thats meant to reflect calls to all the underlying display device DCs that the monitor spans.
The only possible reason I can think of for this behaviour is wx is somehow retrieving a display DC rather than a window DC.
I'm sure Chris is correct, that the "overlapping window" case is handled somewhere for you. But where?
Rendering with windows GDI and "display contexts" such as you mention is very primitive and prone to all sorts of problems. GDI is one of poorest interfaces ever seen, poor even for Microsoft. Since most "window" programs work OK on multiple monitors, think of animating things in a "window" - and how that "window" makes its way to the "display" is best left a mystery.
Maybe DC is fundamentally not multi-monitor capable. Look for anything that allows multiple DCs to be treated uniformly. Rending graphics onto a grid of paper sheets would be like a tiled "printer DC". A video wall would be a tiled "display DC" and you would be happy with a 2-monitor hack, i.e. "multimon dc" echoes to "owning" display and "another one" if a window spans both.
If you want to do "real" animation on windows, you will need to move to DirectX. It is also a lot to learn, but much more capable: scene graphs, textures, video, alpha channels, ...

Black pictures when making screenshots with PrintWindow

I am doing the screen shots of IE using PrintWindow. The problem is that some times I get images with black areas. It may be a whole html content what is black, some times only certain areas are black.
The content of the IE is NOT changed between taking shots.
What is strange is that on some computers I get black images very oftern, on some I never get them.
I tested with Fx, and had same black images.
HBITMAP ShootWindow(HWND hWnd)
{
RECT rect = {0};
GetWindowRect(hWnd, & rect);
HDC hDC = GetDC(hWnd);
if(hDC == NULL)
throw "GetDC failed.";
HDC hTargetDC = CreateCompatibleDC(hDC);
if(hTargetDC == NULL)
throw "CreateCompatibleDC failed.";
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, rect.right - rect.left, rect.bottom - rect.top);
if(hBitmap == NULL)
throw "CreateCompatibleBitmap failed.";
if(!SelectObject(hTargetDC, hBitmap))
throw "SelectObject failed.";
if(!PrintWindow(hWnd, hTargetDC, 0))
throw "PrintWindow failed.";
ReleaseDC(hWnd, hDC);
ReleaseDC(hWnd, hTargetDC);
return hBitmap;
}
I have found some links, but they give no answer:
http://www.vbforums.com/showthread.php?t=555250
http://www.codeguru.com/forum/archive/index.php/t-357211.html
http://social.msdn.microsoft.com/forums/en-US/winforms/thread/3e3decd8-ced1-4f17-a745-466e5aa91391/
This seems to be common when taking screenshots of applications that are using the GPU. BitBlt can successfully copy pixels where PrintWindow fails.
WINDOWINFO wi;
GetWindowInfo(hWnd, &wi);
BitBlt(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hDC, wi.rcClient.left, wi.rcClient.top, SRCCOPY);
The issue is that not all programs provide the needed functions to redraw the window when given the PrintWindow function or the WM_PRINT message.
use SetWindowLong to set WS_EX_COMPOSITED do the PrintWindow() set it back to what was before (or leave it with COMPOSITED to speed up... but that will affect the visibility of the real window unless hw acc is disabled) maybe trying to see if WS_EX_LAYERED and setting opacity to 254 would work better
(forgot to say... that this works, but only for the top level window, trying to PrintWindow some child wont work, even if you set the composited on the top level window)
You might take a look at Windows.Graphics.Capture. This a fairly new API that requires Windows 10 version 1803 or better. There is a some code example here.
It should work with applications that use GPU acceleration, such as Chrome.
This is what OBS use being the scenes when you chose "Windows 10" capture method.

Resources