Help with image flick

I’m writing a simple test application to move an image using keyboard, but
when the image moves, it flicks most of the time. I have used Flip (this
seems slower) and UpdateRects in windowed and fullscreen modes, but anyway
it flicks a lot. I have tested the parallax examples and runs fine, why I
can’t get the same effect?–

Roger D. Vargas

You have to use a double buffered display surface. If you don’t (and you’re
not forced to use a software back surface for some reason), you’re drawing
directly on the visible display “page”, which means that anything you do is
potentially visible on screen. Flip won’t do anything with a setup like that,
as there is only one display page, and no back surface.

The reason why the parallax examples don’t flicker even without double
buffering (which is the default; examples 2 and 3 support the ‘-d’ switch to
enable it) is that they’re rendering the screen from top to bottom without
erasing anything first.

The third example even does it without overdraw, and thus cannot flicker at
all - all you can get is some tearing. (Actually not entirely true; the
planets will force some overdraw because they’re colorkeyed. The overdraw
elimination algo “thinks” in tiles rather than spans or pixels.)

//David Olofson — Programmer, Reologica Instruments AB

.- M A I A -------------------------------------------------.
| Multimedia Application Integration Architecture |
| A Free/Open Source Plugin API for Professional Multimedia |
----------------------> http://www.linuxaudiodev.com/maia -' .- David Olofson -------------------------------------------. | Audio Hacker - Open Source Advocate - Singer - Songwriter |--------------------------------------> david at linuxdj.com -'On Friday 15 June 2001 15:21, Roger D. Vargas wrote:

I’m writing a simple test application to move an image using keyboard, but
when the image moves, it flicks most of the time. I have used Flip (this
seems slower) and UpdateRects in windowed and fullscreen modes, but anyway
it flicks a lot. I have tested the parallax examples and runs fine, why I
can’t get the same effect?

You have to use a double buffered display surface. If you don’t (and you’re
not forced to use a software back surface for some reason), you’re drawing
directly on the visible display “page”, which means that anything you do is
potentially visible on screen. Flip won’t do anything with a setup like that,
as there is only one display page, and no back surface.
I have tried with SDL_DOUBLEBUF flag in initialization, but for some reason it
is not supported (neither HWSURFACE). I have tested it in two different video
cards, both pci: S3 Virge and ATI 3d Rage.On Tue, 19 Jun 2001, David Olofson wrote:

Roger D. Vargas
ICQ: 117641572
Linux User: 180787

Strange… If you can’t get a h/w surface, you should effectively get double
buffering whether you want it or not (which would eliminate the flickering no
matter what rendering algo you’re using), as you’re forced to render into a
software back surface.

Can you post your init code?

Another idea; you aren’t calling Flip() more than once per loop, are you?

//David Olofson — Programmer, Reologica Instruments AB

.- M A I A -------------------------------------------------.
| Multimedia Application Integration Architecture |
| A Free/Open Source Plugin API for Professional Multimedia |
----------------------> http://www.linuxaudiodev.com/maia -' .- David Olofson -------------------------------------------. | Audio Hacker - Open Source Advocate - Singer - Songwriter |--------------------------------------> david at linuxdj.com -'On Wednesday 20 June 2001 17:45, Roger Dura?ona Vargas wrote:

On Tue, 19 Jun 2001, David Olofson wrote:

You have to use a double buffered display surface. If you don’t (and
you’re not forced to use a software back surface for some reason), you’re
drawing directly on the visible display “page”, which means that
anything you do is potentially visible on screen. Flip won’t do
anything with a setup like that, as there is only one display page, and
no back surface.

I have tried with SDL_DOUBLEBUF flag in initialization, but for some reason
it is not supported (neither HWSURFACE). I have tested it in two different
video cards, both pci: S3 Virge and ATI 3d Rage.

Here is the full code. Sorry, itis a little large, but so everybody can
look for some mistake.

#include <stdio.h>
#include <stdlib.h>
#include “SDL.h”

SDL_Surface *sp, *screen;

void loadimg()
{
SDL_Surface *tmp;
//Load background from a bitmap
tmp=SDL_LoadBMP(“backg.bmp”);
tmp=SDL_DisplayFormat(tmp);
SDL_BlitSurface(tmp, NULL, screen, NULL);
//now load the image to move
SDL_UpdateRect(screen,0,0,0,0);
tmp=SDL_LoadBMP(“sail.bmp”);

/* Set transparent pixel as the pixel at (0,0) */
if ( tmp->format->palette ) {
SDL_SetColorKey(tmp, (SDL_SRCCOLORKEY|SDL_RLEACCEL),
*(Uint8 *)tmp->pixels);
}

// Convert image to display format
sp = SDL_DisplayFormatAlpha(tmp);
SDL_FreeSurface(tmp);
}

int main(int argc, char *argv[])
{
SDL_Surface *saved;
SDL_Rect area;
SDL_Event event;
Uint8 alpha;
int end;

/* Initialize SDL */
if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
fprintf(stderr, “Couldn’t initialize SDL: %s\n”,SDL_GetError());
exit(1);
}
atexit(SDL_Quit);
if ((screen=SDL_SetVideoMode(640,480,0,SDL_DOUBLEBUF)) == NULL ) {
fprintf(stderr, “Couldn’t set 640x480 video mode: %s\n”,SDL_GetError());
exit(2);
}
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
loadimg();
//Create a surface to save background
saved=SDL_CreateRGBSurface(SDL_HWSURFACE,sp->w,sp->h,sp->format->BitsPerPixel,0,0,0,0);
area.w=sp->w;
area.h=sp->h,
area.x=1;
area.y=1;
//Save background area
SDL_BlitSurface(screen, &area, saved, NULL);
// Then put image
SDL_BlitSurface(sp, NULL, screen, &area);
// And update screen area using fastest method
if ((screen->flags & SDL_DOUBLEBUF) != SDL_DOUBLEBUF) SDL_UpdateRects(screen,1,&area);
else SDL_Flip(screen);
end=0;
alpha=SDL_ALPHA_OPAQUE;
while (end!=1) {
//First, restore saved area
if (SDL_PollEvent(&event)>0) {
//there is an event waiting
if (event.type==SDL_KEYDOWN) {
// a key was pressed
//so, restore the screen
SDL_BlitSurface(saved, NULL, screen, &area);
if ((screen->flags & SDL_DOUBLEBUF) != SDL_DOUBLEBUF) SDL_UpdateRects(screen,1,&area);
else SDL_Flip(screen);
//See what key was and modify x&y
switch ((int)event.key.keysym.sym) {
case SDLK_LEFT:
//move left
if (area.x>1) area.x–;
break;
case SDLK_RIGHT:
//move right
if (area.x<640) area.x++;
break;
case SDLK_UP:
//move up
if (area.y>1) area.y–;
break;
case SDLK_DOWN:
//move left
if (area.y<480) area.y++;
break;
case SDLK_ESCAPE:
//stop and exit
end=1;
break;
case SDLK_KP_MINUS:
//disolve image
if (alpha>SDL_ALPHA_TRANSPARENT) alpha–;
SDL_SetAlpha(sp, SDL_SRCALPHA, alpha);
break;
case SDLK_KP_PLUS:
//darken image
if (alpha<SDL_ALPHA_OPAQUE) alpha–;
SDL_SetAlpha(sp, SDL_SRCALPHA, alpha);
break;
}

     SDL_BlitSurface(screen, &area, saved, NULL);
     SDL_BlitSurface(sp, NULL, screen, &area);
     if ((screen->flags & SDL_DOUBLEBUF) != SDL_DOUBLEBUF) SDL_UpdateRects(screen,1,&area);
     	else SDL_Flip(screen);
    } else if (event.type==SDL_MOUSEBUTTONDOWN)
     //set stop flag
     end=1;

 } //if EventPoll

}
return(0);
}On Wed, 20 Jun 2001, David Olofson wrote:

Can you post your init code?


Roger D. Vargas

Can you post your init code?

Here is the full code. Sorry, itis a little large, but so everybody can
look for some mistake.

[…]

if ((screen=SDL_SetVideoMode(640,480,0,SDL_DOUBLEBUF)) == NULL ) {
fprintf(stderr, “Couldn’t set 640x480 video mode: %s\n”,SDL_GetError());
exit(2);
}

Looks OK so far, I think, although some error checking might be a good idea.
(SDL parachute deployed first time I tried it here, as the background image
file was missing. :slight_smile:

And yeah, it flickers here too, even though I’m sure this target cannot do
double buffering with h/w page flipping.

Double buffered and no special deepth requirement; you should get a hardware
surface if possible.

//Create a surface to save background

saved=SDL_CreateRGBSurface(SDL_HWSURFACE,sp->w,sp->h,sp->format->BitsPerPix
el,0,0,0,0); area.w=sp->w;
area.h=sp->h,
area.x=1;
area.y=1;
//Save background area
SDL_BlitSurface(screen, &area, saved, NULL);

Not that it should cause a problem here, but this generally isn’t a very good
idea. Don’t ever blit from VRAM unless you’re sure it’s done by the GPU
and not the CPU. Reading VRAM with the CPU is very, very slow on pretty much
all modern hardware.

It’s usually better and simpler (for many reasons) to remove sprites by
painting over them using the background rendering code. Very simple if you’re
using a single background image, but it’s rather doable with tiles as well.
Have a look at the tile rendering “engines” of scrolling examples 2 & 3; they
should be fairly easy to use in that way, I think.

(Speaking of which; maybe I should try it myself - would make another nice
example, and would increase the probability of that Project Spitfire port
ever being done… :slight_smile:

// Then put image
SDL_BlitSurface(sp, NULL, screen, &area);
// And update screen area using fastest method
if ((screen->flags & SDL_DOUBLEBUF) != SDL_DOUBLEBUF)
SDL_UpdateRects(screen,1,&area); else SDL_Flip(screen);
end=0;
alpha=SDL_ALPHA_OPAQUE;
while (end!=1) {
//First, restore saved area
if (SDL_PollEvent(&event)>0) {
//there is an event waiting
if (event.type==SDL_KEYDOWN) {
// a key was pressed
//so, restore the screen
SDL_BlitSurface(saved, NULL, screen, &area);
if ((screen->flags & SDL_DOUBLEBUF) != SDL_DOUBLEBUF)
SDL_UpdateRects(screen,1,&area); else SDL_Flip(screen);

[…input…]

     SDL_BlitSurface(screen, &area, saved, NULL);
     SDL_BlitSurface(sp, NULL, screen, &area);
     if ((screen->flags & SDL_DOUBLEBUF) != SDL_DOUBLEBUF)

SDL_UpdateRects(screen,1,&area); else SDL_Flip(screen);

Uh oh. :slight_smile: There’s your problem! You flip twice per loop.

That is, what you’re doing here is basically

while(running)
{
	remove the sprite;
	make the change visible;	//(no sprite!)
	poll user input;
	update coordinates;
	render the sprite;
	make the change visible;	//(ok; sprite is there)
}

(Ok, I left out the fact that your “engine” stalls when there’s no user
input. My example above demonstrates a more appropriate way of doing it for
games; it will spin even if there’s no input, so that the control system can
keep animations and stuff going at all times.)

Now, if you don’t flip twice, you run into a classical double buffering
problem; your buffers will get out of sync. The solution is to have one
backsave buffer for each screen buffer, along with the coordinates needed to
do the restore blits. Workable, but not trivial. (Yes, this is one of the
reasons why I don’t like the “backsave” method. As to another reason; think
about multiple sprites that are allowed to overlap…)

//David Olofson — Programmer, Reologica Instruments AB

.- M A I A -------------------------------------------------.
| Multimedia Application Integration Architecture |
| A Free/Open Source Plugin API for Professional Multimedia |
----------------------> http://www.linuxaudiodev.com/maia -' .- David Olofson -------------------------------------------. | Audio Hacker - Open Source Advocate - Singer - Songwriter |--------------------------------------> david at linuxdj.com -'On Thursday 21 June 2001 23:20, Roger D. Vargas wrote:

On Wed, 20 Jun 2001, David Olofson wrote: