From: Ikke on
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
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
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
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
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