How to prevent the win32 jagged-edges? - winapi

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:

Related

Win32 LayeredWindow gives bad visual effect

I'm developing a UI system that has all those smart features like panel tearing off and docking, etc. Right now my task is to create an overlay on the screen that shows the position where the teared off or dockable panel would land. Pretty much same thing that visual studio has got.
For that I'm using a custom layered window class that would show up when it is needed. After that I've started digging to achieve the needed effect.
I was working with standart GDI functions before and basicly they are ok. But this time I followed the documentation advice to use UpdateLayeredWindow for my tasks and to load 32bit image from bitmap instead of drawing it with GDI functions.
So here I have a 128x128pixel wide bmp with 222 in alpha channel and 255 0 0 in RGB
Here are methods which I use for initialization and drawing.
void Init(HDC in_hdc,HWND in_hwnd)
{
bf = { 0, 0, 200, AC_SRC_ALPHA };
hwnd = in_hwnd;
hdc_mem = CreateCompatibleDC(in_hdc);
hBitmap_mem = CreateCompatibleBitmap(in_hdc, canvas_size.cx, canvas_size.cy);
hBitmap_mem_default = (HBITMAP)SelectObject(hdc_mem, hBitmap_mem);
hdc_bitmap = CreateCompatibleDC(in_hdc);
}
void DrawArea(RECT& in_rect)
{
hBitmap_area_default = (HBITMAP)SelectObject(hdc_bitmap, hBitmap_area);
AlphaBlend(hdc_mem, in_rect.left, in_rect.top, in_rect.right, in_rect.bottom, hdc_bitmap, 0, 0, 2, 2, bf);
hBitmap_area = (HBITMAP)SelectObject(hdc_bitmap, hBitmap_area_default);
}
void Update()
{
POINT p = { 0, 0 };
HDC hdc_screen = GetDC(0);
UpdateLayeredWindow(hwnd, hdc_screen, &p, &canvas_size, hdc_mem, &p, 0, &bf, ULW_ALPHA);
}
The window style has this extras
WS_EX_LAYERED|WS_EX_TRANSPARENT|WS_EX_TOPMOST
And here is what I get.
So as you can see the blending that takes place DOES take into account per-pixel alpha, but it does a bad blending job.
Any ideas how to tune it?
I suspect the problem is in the source bitmap. This is the kind of effect you get when the RGB values aren't premultiplied with the alpha. But ignore that because there is a far simpler way of doing this.
Create a layered window with a solid background colour by setting hbrBackground in the WNDCLASSEX structure.
Make the window partially transparent by calling SetLayeredWindowAttributes.
Position the window where you want it.
That's it.
This answer has code that illustrates the technique for a slightly different purpose.

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);

partially covered glut window doesn't redraw correctly after it is uncovered

Using free glut on windows 7 home ultimate with video card ati mobility radeon 5650
code snippet:
void ResizeFunction(int width, int height)
{
glViewport(0, 0, width, height);
}
void RenderFunction()
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//...drawing code based on some flag, I draw a triangle or a rectangle
//the flag is toggled on pressing 't' or 'T' key
glutSwapBuffers(); //double buffering is enabled
glutPostRedisplay();
}
void KeyboardFunction(unsigned char key, int x, int y)
{
switch(key)
{
case 't':
case 'T':
{
flag = !flag;
glutPostRedisplay();
break;
}
default:
break;
}
}
problem: The triangle or the rectangle is drawn covering the entire window first time. But if I partially cover the glut window with another window (say, with some notepad window) and then uncover it, subsequently, when I toggle, the object is drawn only in the covered portion of the glut window. If I re-size the glut window, drawing works correctly as before.
Any help will be appreciated.
regards,
fs
Glut only redraws on the screen when you tell it or when it decides. That is, if you don't do anything in the window, the scene is not redrawn. Advantage of this: less cpu/gpu usage. Disadvantage: Only good for non animated applications.
If you want to constantly update the screen (which is what is done in applications with lots of animations (games for example)), you can use glutIdleFunc
http://www.opengl.org/resources/libraries/glut/spec3/node63.html
That is in the beginning of the program when you set all the functions for glut, you also write:
glutIdleFunc(RenderFunction);
This way, when glut is idle, it keeps calling your render function.
If you want to render slower than possible (for example with a fixed frame rate), you could use a timer:
void RenderFunction()
{
glutTimerFunc(YOUR_DELAY_IN_MS, RenderFunction, 0);
/* rest of code */
}
and instead of glutIdleFunc(RenderFunction);, you write
`glutTimerFunc(YOUR_DELAY_IN_MS, RenderFunction, 0);`
To simply call the render function once (you could also just write RenderFunction() once) and the function keeps setting the timer for its next run.
As a side note, I suggest using SDL instead of glut.

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, ...

Variable item height in TreeView gives broken lines

Wee.
So I finally figured out how the iIntegral member of TVITEMEX works. The MSDN docs didn't think to mention that setting it while inserting an item has no effect, but setting it after the item is inserted works. Yay!
However, when using the TVS_HASLINES style with items of variable height, the lines are only drawn for the top part of an item with iIntegral > 1. E.g. if I set TVS_HASLINES and TVS
Here's what it looks like (can't post images WTF?)
Should I manually draw more of the lines in response to NM_CUSTOMDRAW or something?
Yes, Windows doesn't do anything with the blank space obtained from changing the height.
From the MSDN:
The tree-view control does not draw in the
extra area, which appears below the
item content, but this space can be
used by the application for drawing
when using custom draw. Applications
that are not using custom draw should
set this value to 1, as otherwise the
behavior is undefined.
Alright, problem solved.
I failed to find an easy answer, but I did work around it the hard way. It's basically just drawing the extra line segments in custom draw:
// _cd is the NMTVCUSTOMDRAW structure
// ITEMHEIGHT is the fixed height set in TreeView_SetItemHeight
// linePen is HPEN of a suitable pen to draw the lines (PS_ALTERNATE etc.)
// indent is the indentation size returned from TreeView_GetIndent
case CDDS_ITEMPREPAINT : {
// Expand line because TreeView is buggy
RECT r = _cd->nmcd.rc;
HDC hdc = _cd->nmcd.hdc;
HTREEITEM hItem = (HTREEITEM) _cd->nmcd.dwItemSpec;
if( r.bottom - r.top > ITEMHEIGHT ) {
HGDIOBJ oldPen = SelectObject( hdc, linePen );
// Draw any lines left of current item
HTREEITEM hItemScan = hItem;
for( int i = _cd->iLevel; i >= 0; --i ) {
// Line should be drawn only if node has a next sibling to connect to
if( TreeView_GetNextSibling( getHWnd(), hItemScan ) ) {
// Lines seem to start 17 pixels from left edge of control. But no idea
// where that constant comes from or if it is really constant.
int x = 17 + indent * i;
MoveToEx( hdc, x, r.top + ITEMHEIGHT, 0 );
LineTo( hdc, x, r.bottom );
}
// Do the same for the parent
hItemScan = TreeView_GetParent( getHWnd(), hItemScan );
}
SelectObject( hdc, oldPen );
}
}
The pattern from the PS_ALTERNATE brush sometimes doesn't align perfectly with line drawn by the control, but that's hardly noticeable. What's worse is that even though I have the latest common controls and all the service packs and hotfixes installed, there are still bugs in TreeView documented way back in 2005. Specifically, the TreeView doesn't update its height correctly. The only workaround I've found for that is to force some collapsing/expanding of nodes and do a few calls to InvalidateRect.
If the variable-height nodes are at the root level, though, there doesn't appear to be anything you can do. Luckily I don't need that.

Resources