Prev: GetWorldTransform() screwy when DC mode is GM_COMPATIBLE
Next: What MS library should be use to manipulate Excel spreadsheets programmatically?
From: Ikke on 17 Mar 2010 16:42 Hi everyone, I'm trying to create a very basic renderer, which draws a single circle moving from left to right and back in an infinite loop. The circle starts at 10% of the width of the screen, and moves right until 90%, then back. In order to get a smooth movement, I've already tried to implement double buffering. Besides this, I've also made sure that the movement of the circle is independent from the redrawing speed - thus making the circle move at a constant speed. The problem however is that the circle doesn't move all that smooth. It's a lot better than before I implemented double buffering, and the circle indeed moves at a constant speed, but I can still see some jerkiness when it's moving. Does anybody have any suggestions as to what I should change to make the movement smoother? Here is the code I have so far: --- main.cpp --- #include <windows.h> /* Declare Windows procedure */ LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); /* Make the class name into a global variable */ char szClassName[] = "CodeBlocksWindowsApp"; float x, y, spd, mov; HPEN blackPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); HPEN redPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0)); RECT rct; HDC hdc; HDC hdcMem; PAINTSTRUCT ps; HANDLE hOld; HBITMAP hbmMem; int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) { HWND hwnd; /* This is the handle for our window */ MSG messages; /* Here messages to the application are saved */ WNDCLASSEX wincl; /* Data structure for the windowclass */ /* The Window structure */ wincl.hInstance = hThisInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */ wincl.style = CS_DBLCLKS; /* Catch double-clicks */ wincl.cbSize = sizeof (WNDCLASSEX); /* Use default icon and mouse-pointer */ wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszMenuName = NULL; /* No menu */ wincl.cbClsExtra = 0; /* No extra bytes after the window class */ wincl.cbWndExtra = 0; /* structure or the window instance */ /* Use Windows's default colour as the background of the window */ wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND; /* Register the window class, and if it fails quit the program */ if (!RegisterClassEx (&wincl)) return 0; /* The class is registered, let's create the program*/ hwnd = CreateWindowEx ( 0, /* Extended possibilites for variation */ szClassName, /* Classname */ "Code::Blocks Template Windows App", /* Title Text */ WS_OVERLAPPEDWINDOW, /* default window */ 100, // CW_USEDEFAULT /* Windows decides the position */ 100, // CW_USEDEFAULT,/* where the window ends up on the screen */ 1024, /* The programs width */ 768, /* and height in pixels */ HWND_DESKTOP, /* The window is a child-window to desktop */ NULL, /* No menu */ hThisInstance, /* Program Instance handler */ NULL /* No Window Creation data */ ); /* Make the window visible on the screen */ ShowWindow (hwnd, nCmdShow); int tick, prevTick; tick = prevTick = GetTickCount(); MSG msg; /* Here messages to the application are saved */ // Clear out the message structure ZeroMemory(&msg, sizeof(MSG)); x = 0.09; y = 0.5; spd = 0.1; mov = 0; while (msg.message != WM_QUIT) { // Check to see if any messages are waiting in the queue if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Translate the message and dispatch it to WindowProc() TranslateMessage(&msg); DispatchMessage(&msg); } if ((tick - prevTick) > 10) { if (x < 0.1) { mov = (spd * ((tick - prevTick))) / 1000; } if (x > 0.9) { mov = -(spd * ((tick - prevTick))) / 1000; } x += mov; prevTick = tick; } InvalidateRect (hwnd, NULL, TRUE); UpdateWindow (hwnd); // This will call your WM_PAINT response tick = GetTickCount(); } /* The program return-value is 0 - The value that PostQuitMessage() gave */ return messages.wParam; } /* This function is called by the Windows function DispatchMessage() */ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) /* handle the messages */ { case WM_PAINT: { hdc = BeginPaint(hwnd, &ps); hdcMem = CreateCompatibleDC(hdc); hbmMem = CreateCompatibleBitmap(hdc, 1024, 768); hOld = SelectObject(hdcMem, hbmMem); rct.left=0; rct.right=1024; rct.top=0; rct.bottom=768; FillRect(hdcMem, &rct, blackBrush); SelectObject(hdcMem, blackBrush); SelectObject(hdcMem, redPen); Ellipse(hdcMem, ((int) (x * 1024)) - 10, ((int) (y * 768)) - 10, ((int) (x * 1024)) + 10, ((int) (y * 768)) + 10); // Transfer the off-screen DC to the screen BitBlt(hdc, 0, 0, 1024, 768, hdcMem, 0, 0, SRCCOPY); // Free-up the off-screen DC SelectObject(hdcMem, hOld); DeleteObject(hbmMem); DeleteDC (hdcMem); EndPaint(hwnd, &ps); return 0; } case WM_ERASEBKGND: { // Prevent the background from being erased prior to calling WM_PAINT return 1; } case WM_DESTROY: { PostQuitMessage (0); /* send a WM_QUIT to the message queue */ break; } default: /* for messages that we don't deal with */ return DefWindowProc (hwnd, message, wParam, lParam); } return 0; } ---------------- Thanks in advance for all suggestions! Ikke
From: ScottMcP [MVP] on 17 Mar 2010 20:23 GetTickCount does not have good timing resolution. Although it returns milliseconds the actual counter only changes every 15 milliseconds, or slower. QueryPerformanceCounter updates vastly faster. You will initially have to use QueryPerformanceFrequency to translate into time periods: They differ on different machines.
From: Bob Masta on 18 Mar 2010 09:16 On Wed, 17 Mar 2010 17:23:07 -0700 (PDT), "ScottMcP [MVP]" <scottmcp(a)mvps.org> wrote: >GetTickCount does not have good timing resolution. Although it >returns milliseconds the actual counter only changes every 15 >milliseconds, or slower. > >QueryPerformanceCounter updates vastly faster. You will initially >have to use QueryPerformanceFrequency to translate into time periods: >They differ on different machines. > > Another approach is to use a multi-media timer (timeBeginPeriod, timeSetEvent, etc.), which can be set to fire every msec. (Unlike SetTimer, which has the resolution limits noted above.) The timer handler can then simply update a count of msecs which your main thread can read, without needing any translation. (Note that the things that can be done from within the handler are *very* limited.) Best regards, Bob Masta DAQARTA v5.10 Data AcQuisition And Real-Time Analysis www.daqarta.com Scope, Spectrum, Spectrogram, Sound Level Meter Frequency Counter, FREE Signal Generator Pitch Track, Pitch-to-MIDI DaqMusic - FREE MUSIC, Forever! (Some assembly required) Science (and fun!) with your sound card!
From: winapi on 19 Mar 2010 19:10 The reason your animation is jerky, is that you can only update in increments of "one" using the GDI. If you have noticed, coordinates use integer values, so the best you can hope for, is an increment of += 1, not += 0.1. This is not possible. if you try, you will see your shape remains static.You might want to try GDI+, this will give you better results than the standard GDI. See example code below, just change SetTimer value of 1000(one second) to what ever. You will why the animation is jery using GDI . . . . . . #include <windows.h> #include <math.h> LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); char szClassName[] = "CodeBlocksWindowsApp"; HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0)); HBRUSH redBrush = CreateSolidBrush(RGB(255, 0, 0)); RECT rect; HDC hdc; HDC hdcMem; PAINTSTRUCT ps; HBITMAP hBitmap; HDC hdcBg; int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) { static HWND hwnd; static MSG msg; static WNDCLASSEX wc; wc.hInstance = 0; wc.lpszClassName = szClassName; wc.lpfnWndProc = WindowProcedure; wc.style = 0; wc.cbSize = sizeof (WNDCLASSEX); wc.hIcon = LoadIcon (NULL, IDI_APPLICATION); wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wc.hCursor = LoadCursor (NULL, IDC_ARROW); wc.lpszMenuName = NULL; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hbrBackground = NULL; if (!RegisterClassEx (&wc)) return 0; hwnd = CreateWindowEx (WS_EX_STATICEDGE, szClassName, "Code::Blocks Template Windows App", WS_OVERLAPPEDWINDOW, 100, 100, 800, 600, hwnd, NULL, 0, NULL); ShowWindow (hwnd, nCmdShow); UpdateWindow (hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static int movX; static RECT cubeRect; static const int cubeSize = 100; switch (message) { case WM_CREATE: SetTimer(hwnd, 1, 1000, NULL); cubeRect.left = 0; cubeRect.right = cubeSize; cubeRect.top = 0; cubeRect.bottom = cubeSize; return 0; case WM_TIMER: GetClientRect(hwnd, &rect); hdc = GetDC(hwnd); hdcMem = CreateCompatibleDC(hdc); hBitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); SelectObject(hdcMem, hBitmap); movX += 1; cubeRect.left = movX; cubeRect.right = movX + cubeSize; SelectObject(hdcMem, redBrush); FillRect(hdcMem, &cubeRect, redBrush); SetBkMode(hdcMem, TRANSPARENT); BitBlt(hdc, 0, 100, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY); DeleteObject(hBitmap); DeleteDC(hdcMem); return 0; case WM_PAINT: hdcBg = GetDC(hwnd); BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); FillRect(hdcBg, &rect, blackBrush); DeleteDC(hdcBg); EndPaint(hwnd, &ps); return 0; case WM_ERASEBKGND: return 1; case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProc (hwnd, message, wParam, lParam); } return 0; } =================================================== "Ikke" <ikke(a)hier.be> wrote in message news:Xns9D3EDCE27A868ikkehierbe(a)69.16.176.253... > Hi everyone, > > I'm trying to create a very basic renderer, which draws a single circle > moving from left to right and back in an infinite loop. The circle starts > at 10% of the width of the screen, and moves right until 90%, then back. > > In order to get a smooth movement, I've already tried to implement double > buffering. Besides this, I've also made sure that the movement of the > circle is independent from the redrawing speed - thus making the circle > move at a constant speed. > > The problem however is that the circle doesn't move all that smooth. It's > a lot better than before I implemented double buffering, and the circle > indeed moves at a constant speed, but I can still see some jerkiness when > it's moving. > > Does anybody have any suggestions as to what I should change to make the > movement smoother? > > Here is the code I have so far: > --- main.cpp --- > #include <windows.h> > > /* Declare Windows procedure */ > LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); > > /* Make the class name into a global variable */ > char szClassName[] = "CodeBlocksWindowsApp"; > > float x, y, spd, mov; > > HPEN blackPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); > HPEN redPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); > HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0)); > RECT rct; > HDC hdc; > HDC hdcMem; > PAINTSTRUCT ps; > HANDLE hOld; > HBITMAP hbmMem; > > > int WINAPI WinMain (HINSTANCE hThisInstance, > HINSTANCE hPrevInstance, > LPSTR lpszArgument, > int nCmdShow) > { > HWND hwnd; /* This is the handle for our window */ > MSG messages; /* Here messages to the application are > saved */ > WNDCLASSEX wincl; /* Data structure for the windowclass */ > > /* The Window structure */ > wincl.hInstance = hThisInstance; > wincl.lpszClassName = szClassName; > wincl.lpfnWndProc = WindowProcedure; /* This function is called > by windows */ > wincl.style = CS_DBLCLKS; /* Catch double-clicks */ > wincl.cbSize = sizeof (WNDCLASSEX); > > /* Use default icon and mouse-pointer */ > wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); > wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); > wincl.hCursor = LoadCursor (NULL, IDC_ARROW); > wincl.lpszMenuName = NULL; /* No menu */ > wincl.cbClsExtra = 0; /* No extra bytes after > the window class */ > wincl.cbWndExtra = 0; /* structure or the window > instance */ > /* Use Windows's default colour as the background of the window */ > wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND; > > /* Register the window class, and if it fails quit the program */ > if (!RegisterClassEx (&wincl)) > return 0; > > /* The class is registered, let's create the program*/ > hwnd = CreateWindowEx ( > 0, /* Extended possibilites for variation > */ > szClassName, /* Classname */ > "Code::Blocks Template Windows App", /* Title Text */ > WS_OVERLAPPEDWINDOW, /* default window */ > 100, // CW_USEDEFAULT /* Windows decides the position */ > 100, // CW_USEDEFAULT,/* where the window ends up on the > screen */ > 1024, /* The programs width */ > 768, /* and height in pixels */ > HWND_DESKTOP, /* The window is a child-window to > desktop */ > NULL, /* No menu */ > hThisInstance, /* Program Instance handler */ > NULL /* No Window Creation data */ > ); > > /* Make the window visible on the screen */ > ShowWindow (hwnd, nCmdShow); > > int tick, prevTick; > tick = prevTick = GetTickCount(); > > MSG msg; /* Here messages to the application are > saved */ > // Clear out the message structure > ZeroMemory(&msg, sizeof(MSG)); > > x = 0.09; > y = 0.5; > spd = 0.1; > mov = 0; > > while (msg.message != WM_QUIT) > { > // Check to see if any messages are waiting in the queue > if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) > { > // Translate the message and dispatch it to WindowProc() > TranslateMessage(&msg); > DispatchMessage(&msg); > } > > if ((tick - prevTick) > 10) > { > if (x < 0.1) > { > mov = (spd * ((tick - prevTick))) / 1000; > } > > if (x > 0.9) > { > mov = -(spd * ((tick - prevTick))) / 1000; > } > > x += mov; > prevTick = tick; > } > > InvalidateRect (hwnd, NULL, TRUE); > UpdateWindow (hwnd); // This will call your WM_PAINT response > > tick = GetTickCount(); > } > > /* The program return-value is 0 - The value that PostQuitMessage() > gave */ > return messages.wParam; > } > > > /* This function is called by the Windows function DispatchMessage() */ > LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, > LPARAM lParam) > { > switch (message) /* handle the messages */ > { > case WM_PAINT: > { > hdc = BeginPaint(hwnd, &ps); > > hdcMem = CreateCompatibleDC(hdc); > hbmMem = CreateCompatibleBitmap(hdc, 1024, 768); > hOld = SelectObject(hdcMem, hbmMem); > > rct.left=0; > rct.right=1024; > rct.top=0; > rct.bottom=768; > FillRect(hdcMem, &rct, blackBrush); > > SelectObject(hdcMem, blackBrush); > SelectObject(hdcMem, redPen); > > Ellipse(hdcMem, > ((int) (x * 1024)) - 10, > ((int) (y * 768)) - 10, > ((int) (x * 1024)) + 10, > ((int) (y * 768)) + 10); > > // Transfer the off-screen DC to the screen > BitBlt(hdc, 0, 0, 1024, 768, hdcMem, 0, 0, SRCCOPY); > > // Free-up the off-screen DC > SelectObject(hdcMem, hOld); > DeleteObject(hbmMem); > DeleteDC (hdcMem); > > EndPaint(hwnd, &ps); > return 0; > } > > case WM_ERASEBKGND: > { > // Prevent the background from being erased prior to calling > WM_PAINT > return 1; > } > > case WM_DESTROY: > { > PostQuitMessage (0); /* send a WM_QUIT to the message > queue */ > break; > } > default: /* for messages that we don't deal > with */ > return DefWindowProc (hwnd, message, wParam, lParam); > } > > return 0; > } > ---------------- > > Thanks in advance for all suggestions! > > Ikke
From: Ikke on 20 Mar 2010 12:34
N0Spam(a)daqarta.com (Bob Masta) wrote in news:4ba2257d.396212(a)news.eternal- september.org: <snip> >>QueryPerformanceCounter updates vastly faster. You will initially >>have to use QueryPerformanceFrequency to translate into time periods: >>They differ on different machines. >> > Another approach is to use a multi-media timer > (timeBeginPeriod, timeSetEvent, etc.), which can > be set to fire every msec. <snip> Thank you Scott and Bob, I have changed my code to use the QueryPerformanceCounter, and I added a variable onscreen to check the improvement. When I used GetTickCount, the ellipse would be moved (on average) 65 times per second (like you said, Scott, every 15 ms). As soon as I switched to QueryPerformanceCounter, the movement become very fluent, and the ellipse is being moved at a rate of 995 times per second! Thanks again, Ikke |