draw cartesian system using win32 api - c++

I'm trying to draw sine wave using win32 api. I done this.
hDC = GetDC(hWnd);
while (TRUE)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
}
if (msg.message == WM_QUIT)
break;
wavefunc(hWnd, hDC);
}
void wavefunc(HWND hWnd, HDC hDC)
{
double full = 2 * pi * _freq;
static double _x = 0;
short int _y = 0;
short _y = (short)(sin(_x / _freq)*_amp) + 300;
if (_x >= full)
_x -= full;
SetPixel(hDC, 600, _y, blue);
ScrollWindow(hWnd, -1, 0, NULL, NULL);
Sleep(_sTime);
_x++;
}
Now i'm trying to figure out how to draw cartesian system. But with no result.
I have a window which is scrolled at each sin value.
I try to draw to lines on hdc. When window is scrolled there is no way to stop hdc to not scroll.
Then i create another hDc from windows but no succes.
How to do this? The problem is to have window scrolling but with some point fixes..

The way to do this is to paint everything all at once: clear the window, draw the grid, and then draw the entire waveform (not just the next point) over the grid. To make the waveform scroll, you keep redrawing with a different offset for the waveform.
To avoid flicker, you will likely need to do some double-buffering.
Typical WinAPI programs paint the window when handling the WM_PAINT command. The WM_PAINT message is generated when any or all of the window is invalid and needs to be repainted. Each time you scroll the window, you're creating an invalid region, but since you're ignoring the WM_PAINT message, nothing happens.

Here is how I would animate the sin() function:
Create a bitmap (the size of your window) and draw your static parts on it (axis, title, labels, etc.).
Create another bitmap (the height of your sin() and the width of your window + the width of the sin()’s period at your scale), and draw sin() on it.
In the wavefunc(), get the precise time and calculate the offset for your sin bitmap.
Use BitBlt() function to first blit the static part onto your window’s DC, then use TransparentBlt() to paint the sin, starting at pre-calculated offset.

Related

Device context size is getting reduced during drawing sometimes

We have an application in MFC.
We have come across an issue with device context.
The situation is like - we have an info display window, which is variable in size and posiion.
During some size and position changing scenarios, only part of the window is drawn, like a portion of the window is cut.
We doubted there is difference between the rect in device context and the rect returned from GetWindowRect function.
So we have logged and checked the size of the window rect which is being drawn from the device context and also
the window rect of memory DC which is used for drawing during the issue scenario.
But both returned the small window rect size.
That is device context has only the partial information of the rect at that time.
We didn't called UpdateWindow() or Invalidate().
When we focused the window using WinSpy, the whole window is present, but only that small portion is drawn.
We placed and then removed another window above this window to check whether any repainting would happen. But still the issue persists.
Can anyone please help rectify this problem?
hi, our code is like this.
BOOL InfoDisplayWindow::OnEraseBkgnd(CDC* pDC)
{
CBitmap m_bitmap; // Offscreen bitmap
CBitmap* m_oldBitmap; // bitmap originally found
CRect m_rect; // Rectangle of drawing area.
HDC hDC = CreateCompatibleDC(pDC->m_hDC);
CDC* pTmpDC = CDC::FromHandle(hDC);
pDC->GetClipBox(&m_rect);
m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(), m_rect.Height());
m_oldBitmap = pTmpDC->SelectObject(&m_bitmap);
pTmpDC->SetWindowOrg(m_rect.left, m_rect.top);
CRect rc;
GetClientRect(&rc);
pTmpDC->FillSolidRect(&rc, COLOR_KEY);
DrawFunction();// Text is displayed in this function
CPen pen(PS_SOLID, SOLID_BORDER_WIDTH, BORDER_COLOR);
CPen *old_pen = pTmpDC->SelectObject(&pen);
// Drawing the 4 boarders of the window here.
pTmpDC->MoveTo(rc.left, rc.bottom - 1);
pTmpDC->LineTo(rc.left, rc.top);
pTmpDC->LineTo(rc.right - 1, rc.top);
pTmpDC->LineTo(rc.right - 1, rc.bottom - 1);
pTmpDC->LineTo(rc.left, rc.bottom - 1);
pTmpDC->SelectObject(old_pen);
// Copy the offscreen bitmap onto the screen.
pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),
pTmpDC, m_rect.left, m_rect.top, SRCCOPY);
//Swap back the original bitmap.
pTmpDC->SelectObject(m_oldBitmap);
return TRUE;
}
I assume you get your device context (DC) either from BeginPaint (or using MFC using a CPaintDC) or from GetDC. All these variants give you the DC for your window's client area, which doesn't include the border and title bar. The corresponding rect is returned by GetClientRect.
Corresponding to GetWindowRect is GetWindowDC, which allows to draw in the full area. Be aware that GetWindowRect gives you screen coordinates, so you should transform them by ScreenToClient before applying them to your DC.

Search icon in edit control overlapped by input area

I am trying to make a search edit control in MFC that has an icon displayed in the control window all the time (regardless the state and text of the control). I have written something like this many years ago and worked very well, but the code no longer works on Windows 7 and newer (maybe even Vista, but did not try that). What happens is that the image shown in the control is overlapped with the input area (see the picture below).
The idea behind the code:
have a class derived from CEdit (that handles painting in OnPaint)
the icon is displayed on the right and the edit area is shrunk based on the size of the icon
resizing is done differently for single-line and multiline edits. For single line I call SetMargins and for multiline edits I call SetRect.
this edit resizing is applied in PreSubclassWindow(), OnSize() and OnSetFont()
This is how the edit input size is applied:
void CSymbolEdit::RecalcLayout()
{
int width = GetSystemMetrics( SM_CXSMICON );
if(m_hSymbolIcon)
{
if (GetStyle() & ES_MULTILINE)
{
CRect editRect;
GetRect(&editRect);
editRect.right -= (width + 6);
SetRect(&editRect);
}
else
{
DWORD dwMargins = GetMargins();
SetMargins(LOWORD(dwMargins), width + 6);
}
}
}
The following image shows the problem with the single line edits (the images have been zoomed in for a better view). The yellow background is for highlighting purposes only, in real code I am using the COLOR_WINDOW system color. You can see that when the single line edit has text and has the input the left side image is painted over. This does not happen with the multiline edit where SetRect correctly sets the formatting rectangle.
I have tried using ExcludeClipRect to remove the area of the edit where the image is being displayed.
CRect rc;
GetClientRect(rc);
CPaintDC dc(this);
ExcludeClipRect(dc.m_hDC, rc.right - width - 6, rc.top, rc.right, rc.bottom);
DWORD dwMargins = GetMargins();
SetMargins(LOWORD(dwMargins), width + 6);
This does not seem to have any effect on the result.
For reference, this is the painting method, written years ago and used to work well on Windows XP, but not correct any more.
void CSymbolEdit::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect( &rect );
// Clearing the background
dc.FillSolidRect( rect, GetSysColor(COLOR_WINDOW) );
DWORD dwMargins = GetMargins();
if( m_hSymbolIcon )
{
// Drawing the icon
int width = GetSystemMetrics( SM_CXSMICON );
int height = GetSystemMetrics( SM_CYSMICON );
::DrawIconEx(
dc.m_hDC,
rect.right - width - 1,
1,
m_hSymbolIcon,
width,
height,
0,
NULL,
DI_NORMAL);
rect.left += LOWORD(dwMargins) + 1;
rect.right -= (width + 7);
}
else
{
rect.left += (LOWORD(dwMargins) + 1);
rect.right -= (HIWORD(dwMargins) + 1);
}
CString text;
GetWindowText(text);
CFont* oldFont = NULL;
rect.top += 1;
if(text.GetLength() == 0)
{
if(this != GetFocus() && m_strPromptText.GetLength() > 0)
{
oldFont = dc.SelectObject(&m_fontPrompt);
COLORREF color = dc.GetTextColor();
dc.SetTextColor(m_colorPromptText);
dc.DrawText(m_strPromptText, rect, DT_LEFT|DT_SINGLELINE|DT_EDITCONTROL);
dc.SetTextColor(color);
dc.SelectObject(oldFont);
}
}
else
{
if(GetStyle() & ES_MULTILINE)
CEdit::OnPaint();
else
{
oldFont = dc.SelectObject(GetFont());
dc.DrawText(text, rect, DT_SINGLELINE | DT_INTERNAL | DT_EDITCONTROL);
dc.SelectObject(oldFont);
}
}
}
I have looked at other implementations of similar edit controls and they all have the same fault now.
Obviously, the question is how do I exclude the image area from the input area of the control?
I think what's going on is that CPaintDC calls BeginPaint(), which sends a WM_ERASEBKGND to the edit box. I wasn't able to ignore it, so I guess it's being sent to maybe an internal STATIC window? Not sure.
Calling ExcludeClipRect() in your OnPaint() handler won't do anything because EDIT will reset the clipping region to the whole client area in either BeginPaint() or its own WM_PAINT handler.
However, EDIT sends a WM_CTRCOLOREDIT to its parent just before painting itself, but seemingly after setting the clipping region. So you can call ExcludeClipRect() there. Sounds like an implementation detail which may change with future versions of the common controls. Indeed, it seems to have done so already.
I did a quick test without MFC on Windows 7, here's my window procedure:
LRESULT CALLBACK wnd_proc(HWND h, UINT m, WPARAM wp, LPARAM lp)
{
switch (m)
{
case WM_CTLCOLOREDIT:
{
const auto dc = (HDC)wp;
const auto hwnd = (HWND)lp;
RECT r;
GetClientRect(hwnd, &r);
// excluding the margin, but not the border; this assumes
// a one pixel wide border
r.left = r.right - some_margin;
--r.right;
++r.top;
--r.bottom;
ExcludeClipRect(dc, r.left, r.top, r.right, r.bottom);
return (LRESULT)GetStockObject(DC_BRUSH);
}
}
return ::DefWindowProc(h, m, wp, lp);
}
I then subclassed the EDIT window to draw my own icon in WM_PAINT, and then forwarded the message so I didn't have to draw everything else myself.
LRESULT CALLBACK edit_wnd_proc(
HWND h, UINT m, WPARAM wp, LPARAM lp,
UINT_PTR id, DWORD_PTR data)
{
switch (m)
{
case WM_PAINT:
{
const auto dc = GetDC(h);
// draw an icon
ReleaseDC(h, dc);
break;
}
}
return DefSubclassProc(h, m, wp, lp);
}
Note that I couldn't call BeginPaint() and EndPaint() (the equivalent of constructing a CPaintDC) in WM_PAINT because the border wouldn't get drawn. I'm guessing it has something to do with calling BeginPaint() twice (once manually, once by EDIT) and the handling of WM_ERASEBKGND. YMMV, especially with MFC.
Finally, I set the margins right after creating the EDIT:
SendMessage(
e, EM_SETMARGINS,
EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELPARAM(0, margin));
You might also have to update the margins again if the system font changes.
Have a look at this tutorial... from www.catch22.net. It gives a clear picture of how to insert a button into edit control. Though it is an Win32 example, this can be improvised to MFC as the basic structure of MFC is using win32 apis.
http://www.catch22.net/tuts/win32/2001-05-20-insert-buttons-into-an-edit-control/#
It uses WM_NCCALCSIZE to restrict the text control.

C++ Win32 GDI double buffering

Could you give the simplest way to achieve double buffering for this example code (to prevent flickering):
HWND hwnd = FindWindow(0, "Untitled - Notepad");
HDC hDC_Desktop = GetDC(hwnd);
...
while( )
{
RECT rect = { 10, 10, 10 + 50, 10 + 50 };
FillRect(hDC_Desktop, &rect, ColorBrush);
InvalidateRect (hwnd, NULL, TRUE);
}
The reason it's "flickering" is because the target window is getting invalidated and it is being redrawn. Since it's not your window - you don't necessarily have control over that.
If this was your own window there is a simple strategy to speed up your drawing speed and reduce flicker: Use a Memory DC to draw on and capture WM_ERASEBKGND to suppress background redraws.
In depth explanation and strategy for fixing it (in your application's window): http://www.catch22.net/tuts/win32/flicker-free-drawing
If your intent is to draw on another application, might I suggest creating a window on top of that application and draw on that.

How to draw on the windows desktop using the GDI API? [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Draw on screen with GDI+ (or GDI) similar to Inspect
I'm attempting to write a snake game that has no windows, but freezes the foreground and draws the snake on top of it. When the game ends the foreground should be unfrozen.
I have written some testing code that is supposed to draw a square on the foreground, but all it seems to do is freeze the desktop for a second and freeze the window in the foreground until I minimize, maximize, close it, or bring another window into the foreground, and it doesn't draw anything. In the code, I try to store a bitmap of the desktop so that I can essentially reset it to it's original state and paint the square in a different position. Can anybody spot the problem with my code?
//Handle to the desktop window
HWND hDesktopWindow = GetDesktopWindow();
//Lock the window to prevent other applications drawing on it
if(!LockWindowUpdate(hDesktopWindow)){
return 1;
}
//Calling GetDC with argument NULL retrieves the desktop's DC
HDC hdcDesktop = GetDCEx(hDesktopWindow, NULL, DCX_CACHE | DCX_LOCKWINDOWUPDATE);
//Create a compatible DC to allow us to store a bitmap of the desktop
HDC hdcCompatible;
if((hdcCompatible = CreateCompatibleDC(hdcDesktop)) == NULL){
return 1;
}
//Create a compatible bitmap with the same dimensions as the desktop
HBITMAP hScrBitmap;
int cx = GetSystemMetrics(SM_CXSCREEN);
int cy = GetSystemMetrics(SM_CYSCREEN);
if((hScrBitmap = CreateCompatibleBitmap(hdcDesktop, cx, cy)) == NULL){
return 1;
}
//Select the bitmap into the compatible DC
SelectObject(hdcCompatible, hScrBitmap);
//Copy the Desktop into the bitmap
if(!BitBlt(hdcCompatible, 0, 0, cx, cy, hdcDesktop, 0, 0, SRCCOPY)){
return 1;
}
//Create a DC compatible with the bitmaps DC for drawing the rectangle
HDC hdcRectangle;
if(!CreateCompatibleDC((HDC)hScrBitmap)){
return 1;
}
//Create a compatible bitmap for the rectangle to be drawn in
HBITMAP hRectangleBitmap;
if(!CreateCompatibleBitmap(hdcRectangle, 100, 100)){
return 1;
}
//Fill the rectangle bitmap
if(!FloodFill(hdcRectangle, 0, 0, RGB(255,0,0))){
return 1;
}
//Copy the rectangle onto the desktop bitmap
if(!BitBlt(hdcCompatible, 100, 100, 100, 100, hdcRectangle, 0, 0, SRCCOPY)){
return 1;
}
//Copy the bitmap onto the actual desktop
if(!BitBlt(hdcDesktop, 0, 0, cx, cy, hdcCompatible, 0, 0, SRCCOPY)){
return 1;
}
//Allow time to view the result
Sleep(1000);
//Allow other applications to draw on the desktop again
LockWindowUpdate(NULL);
//Cleanup
ReleaseDC(hDesktopWindow, hdcDesktop);
DeleteDC(hdcCompatible);
DeleteObject(hScrBitmap);
Any help would be greatly appreciated :)
Trying to do this directly on the desktop is going to be problematic. You'd be better off by taking a snapshot of the desktop, then create a window that's the size of the whole desktop, copy the snapshot to the window, and do all your drawing there. (This was a common trick done in old screensavers that did things like "erode" the desktop.)
You don't own the desktop window, so you'll always have problems with invalidation and repaints.
if(!CreateCompatibleDC((HDC)hScrBitmap)){
return 1;
}
When you write C code like this then a single-point-of-return tends to be very important. A call like this is going to return FALSE, can't cast a HBITMAP to HDC, and the show is over badly. No diagnostic and no call to unlock again.
Favor the C++ RAII pattern to ensure that you always unlock:
class DesktopLocker {
public:
DesktopLocker() { LockWindowUpdate(GetDesktopWindow()); }
~DesktopLocker() { LockWindowUpdate(NULL); }
};
void foo() {
DesktopLocker lock;
// etc...
}
There's not much wisdom of painting directly to the desktop window, there's little guarantee that whatever you draw will last. Sleeping and locking updates are just band-aids. Take a look at the source of Rainmeter, it's well done.

How do you create a button outside the WM_CREATE message in win32?

I'm using c++ with win32 and gdi+ for graphics.
When I initialize a button outside WM_CREATE, specifically in a WM_TIMER message, I can't draw anything else, after that one frame is drawn.
Here's a snippet of the code:
case WM_TIMER:
RECT client;
GetClientRect(hWnd, &client);
client.bottom-=100; //The bottom hundred pixels are static after I draw the first frame, so I never update them
if(starting==true)
{
starting=false;
hdc = GetDC(hWnd);
hdcBuf = CreateCompatibleDC(hdc);
hdcMem = CreateCompatibleDC(hdcBuf);
hbmBackBM = CreateCompatibleBitmap(hdc, client.right, client.bottom );
hbmOldBackBM = (HBITMAP)SelectObject(hdcBuf, hbmBackBM);
Graphics temp(hdc);
SolidBrush yelloworange(Color(250,225,65));
temp.FillRectangle(&yelloworange,0,client.bottom,client.right,100); //Fill the bottom with yellow
buttons[0]=CreateWindow("button","makereg", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 100, 630, 60, 20, hWnd, HMENU(IDB_MAKEREG), NULL, NULL);
//buttons[1]=CreateWindow("button","destroyreg", WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 100, 670, 80, 20, hWnd, HMENU(IDB_MAKEREG+1), NULL, NULL);
}
Graphics g(hdcBuf);
The first part is for double buffering, and the variables that I instantiate are global. I delete the HDCs and HBITMAPs in WM_DESTROY. starting is a global boolean that is instantiated as true.
I do all of my drawing in this WM_TIMER message. If I comment out just the two lines where the buttons are created, everything runs normally. With them, it only draws out what is left in this WM_TIMER, and does not draw in the next one. All of the other drawing code is done to hdcBuf or g, and hdcBuf is then BitBlt'd onto hdc.
I tried creating the button in WM_CREATE, and then showing it in WM_TIMER, but that caused the same problem. I can't create and show the window in WM_CREATE, because otherwise it gets drawn over when I fill the bottom 100 pixels with a yellow color.
Is there a way to create and show a button outside WM_CREATE and outside WM_PAINT without crashing the rest of the code?
EDIT: Here is some of the code that stops working, in WM_TIMER:
if(mousex!=uptomousex && mousey!=uptomousey && lbuttondown==true) // this code draws a rectangle between the point where the user begins holding the left mousebutton, and where the mouse is right now.
{
if(uptomousex-mousex>0 && uptomousey-mousey>0)
g.DrawRectangle(&(p[0]), mousex, mousey, uptomousex-mousex, uptomousey-mousey);
else if(uptomousex-mousex<0 && uptomousey-mousey>0)
g.DrawRectangle((&p[0]), uptomousex, mousey, mousex-uptomousex, uptomousey-mousey);
else if(uptomousex-mousex>0 && uptomousey-mousey<0)
g.DrawRectangle((&p[0]), mousex, uptomousey, uptomousex-mousex, mousey-uptomousey);
else if(uptomousex-mousex<0 && uptomousey-mousey<0)
g.DrawRectangle(&(p[0]), uptomousex, uptomousey, mousex-uptomousex, mousey-uptomousey);
}
Some global variables:
bool lbuttondown=false;
float mousex=0;
float mousey=0;
float uptomousex=0;
float uptomousey=0;
Elsewhere in WndProc...
case WM_LBUTTONDOWN:
lbuttondown=true;
mousex=(float)GET_X_LPARAM(lParam);
mousey=(float)GET_Y_LPARAM(lParam);
uptomousex=mousex;
uptomousey=mousey;
break;
case WM_MOUSEMOVE:
if(mousex!=GET_X_LPARAM(lParam) && mousey!=GET_Y_LPARAM(lParam))
{
uptomousex=(float)GET_X_LPARAM(lParam);
uptomousey=(float)GET_Y_LPARAM(lParam);
}
break;
You are creating/getting at least 3 Device Context instances on each timer call, and you never delete/release them (at least in the sample that you posted), so no surprise that you are ending by crushing the whole GDI system.
For each GetDC() call, ReleaseDC() should be called,
for each CreateCompatibleDC() call, DeleteObject() should be called.
I have not figured out why this happens, but I have created a simple patch: my own button class, which I'll share here:
class button
{
public:
int x;
int y;
int width;
int height;
string text;
void (*func)(short);
button(int px, int py, int w, int h, string txt, void (*f)(short))
{
x=px;
y=py;
width=w;
height=h;
text=txt;
func=*f;
}
};
Then, there is a global vector of buttons, allbuttons, and in the WM_LBUTTONUP message I loop through allbuttons and check if the click was in the button, and if so I call func.
Its simple, more flexible, and it actually works. However, the graphics are worse, and I would still like to find out why the windows button is dysfunctional, just out of curiosity.