Sdl Blitting Issues


TrevorBradley

Active Member
Joined
Nov 6, 2007
Messages
732
I'm making good progress on my SDL game, but I've hit a roadblock and could use some help.

What I'd like to be able to do is to clone an SDL surface, draw onto that clone, and SDL_Flip the surface onto the screen. The original surface is very complex and it would be best if I could render it once instead of with every frame displayed.

The problem is, I can't seem to clone an SDL Surface, or when I attempt to write to the clone it writes on the original surface.

Here's a bit of test code I've written to try to explain what I'm doing...

CODE
#include "SDL/SDL.h"
#include "SDL/SDL_gfxPrimitives.h"

#include <stdio.h>
#include <stdlib.h>

#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define SCREEN_CENTER 300
#define SCREEN_DEPTH 16

int main(int argc, char *argv[]) {
SDL_Surface *screen1;
SDL_Surface *screen2;
SDL_Event event;

bool toggle=false;

SDL_Rect screen_rect;

screen_rect.x=0;
screen_rect.y=0;
screen_rect.h=SCREEN_HEIGHT;
screen_rect.w=SCREEN_WIDTH;

screen1 = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, SDL_SWSURFACE);

filledCircleColor(screen1,SCREEN_CENTER,SCREEN_CENTER,200,0xFFFFFFFF);

SDL_Flip(screen1);

while(1) {

if( SDL_PollEvent( &event ) ) {
if( event.type == SDL_QUIT ) {
exit(0);
}
if ( event.type == SDL_KEYDOWN ) {
switch( event.key.keysym.sym ) {
case SDLK_SPACE:
toggle=!toggle;
break;
case SDLK_ESCAPE:
case SDLK_q:
exit(0);
break;
}
}
}

SDL_BlitSurface(screen1,&screen_rect,screen2,&screen_rect);

if (toggle)
filledCircleColor(screen2,SCREEN_CENTER,SCREEN_CENTER+100,100,0xFF0000FF);
else
filledCircleColor(screen2,SCREEN_CENTER,SCREEN_CENTER-100,100,0xFF0000FF);

SDL_Flip(screen2);
}
}



Here I want to preserve screen1 and leave it untouched. I want to copy screen1 to screen2, write on screen2, and then start over again, copying screen1 over screen2 again. But the code above appears to modify screen1 as well.

I thought there was some kind of surface alpha issue, but I have a feeling I'm doing something dumb...

Can anyone see what I'm doing wrong here? Thanks in advance!
 
I've only had a very very quick skim, but it doesn't look like you create screen2...

To clone the surface you have the right idea with blitting, but screen2 must have a created surface in order to blit to.

You need this function to create your secondary surface: http://www.libsdl.org/cgi/docwiki.cgi/SDL_CreateRGBSurface
 
Hmm, reading the documentation:

screen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH,
0xFF000000,0x00FF0000,0x0000FF00,0x000000FF);

Doesn't appear to do anything.. :(

I'll play with it... see if I can get it going...

EDIT: Simplifying my code.. I don't see what's wrong:

CODE
#include "SDL/SDL.h"
#include "SDL/SDL_gfxPrimitives.h"

#include <stdlib.h>

#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
#define SCREEN_CENTER 300
#define SCREEN_DEPTH 16

int main(int argc, char *argv[]) {
SDL_Surface *screen1,*screen2;
SDL_Event event;

screen1 = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, SDL_SWSURFACE);
filledCircleColor(screen1,SCREEN_CENTER,SCREEN_CENTER,200,0xFFFFFFFF);
SDL_Flip(screen1);
screen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH,
0xFF000000,0x00FF0000,0x0000FF00,0x000000FF);
SDL_BlitSurface(screen1,NULL,screen2,NULL);
filledCircleColor(screen2,SCREEN_CENTER,SCREEN_CENTER,100,0xFF0000FF);
SDL_Flip(screen2);
while(1) {
if ((SDL_PollEvent(&event)) && ((( event.type == SDL_QUIT )||( event.type == SDL_KEYDOWN ))))
exit(0);
}
}


This should draw a red circle inside a larger white circle, but that's not what's happening. All I see is the larger white circle that was blitted on screen1.

Compile line is g++ filename.gcc -lSDL -lSDL_gfx, btw...
 
Try
CODE
screen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, 0, 0, 0, 0);


You're in 16bit mode, so the colour masks can't be 32bit like 0xFF000000. Putting values of 0 will give you default masks which should work.
 
Alex. said:
You're in 16bit mode, so the colour masks can't be 32bit like 0xFF000000. Putting values of 0 will give you default masks which should work.
I think I previously tried the 0,0,0,0 setting as well as moving to 32 bit depth. Neither changed the result.

I'll double check. *EDIT*: Verified.. No change.

It's something small and dumb we're all missing.
 
Last edited by a moderator:
Figured it out after popping in to the SDL IRC channel.

My problem is that I'm using screens incorrectly. The screen created by SDL_SetVideoMode is special. SDL_Flip's parameter MUST be this screen.

The correct solution is to create two screens with SDL_CreateRGBSurface, and blit them on to the screen created by SDL_SetVideoMode in sequence.
 
Quiest said:
Why creating three screens? o_O
The first screen (Screen0) is created by SDL_SetVideoMode. That's my buffer.. that's the screen that gets displayed. Apparently you can't SDL_Flip any other created screen. (I had to verify this with the SDL folks on IRC)

Screen 1 is complicated, takes a good portion of a second to render in some extreme cases, and it's to my benefit not to render it over and over again. When I render screen 1 frame by frame, my CPU usage goes to 100%. That's not SDL.. it's the math that's slow.

Screen 2 is my overlay, where all my interface bits that change from frame to frame go. I Blit Screen 1 to Screen 2, and then draw on Screen 2, and then eventually Blit Screen 2 to Screen0 and flip Screen0.

As to what I'm actually doing, I'm going to keep that a secret for now. It's an idea in one form or another I've been thinking about for about 10 years now. What I've got now after just a week of work is jaw droppingly awesome, but I want to solidify gameplay before letting any hype build.

A hint.. I'm rendering tens of thousands of SDL_gfx primitives on this complex Screen 1 per frame. That alone makes it nigh impossible to make a video with compressed frames. The code scales down and runs on my GP2x, but isn't quite as interesting, so I'm waiting for the Pandora. All that RAM is going to come in handy. :D
 
Last edited by a moderator:
PokeParadox said:
It certainly sounds interesting!
It's "Crap, it's 4:30AM and I'm still coding!" interesting... :) At the very least I'm having fun making the thing...
 
Last edited by a moderator:
Trevor Bradley said:
PokeParadox said:
It certainly sounds interesting!
It's "Crap, it's 4:30AM and I'm still coding!" interesting... :) At the very least I'm having fun making the thing...

Looking over the first posted code, I think I see the problem. #1 you need to create a third surface 1 for the screen as to be displayed, 1 for the complex layer that you want to render once, and 1 for the overlay layer.

What you're doing now is just drawing a circle to the screen (screen1) since screen 1 is the screen buffer flipping the screen buffer (tada white circle). Drawing a circle to screen 2, and then flipping screen 2, but since screen 2 isn't a screen buffer (it's just a surface). Nothing actually gets written to screen buffer (screen 1).

What you need to do is:
Create the screen surface (SCREEN)
Create a surface for the white circle (SURFACE1)
Create a surface for the red circle (SURFACE2)
(begin render loop)
clear SCREEN at the beginning of the loop (preventing ghosting and Hall of Mirror ugliness)
Blit SURFACE 1 to SCREEN to get the white circle on the screen
Blit SURFACE 2 to SCREEN to get the red circle on the screen
Flip SCREEN
Limit the Framerate to match the LCD refresh to prevent flicker
(end render loop)

Hope that helps.
 
Last edited by a moderator:
mindlord said:
What you need to do is:
Create the screen surface (SCREEN)
Create a surface for the white circle (SURFACE1)
Create a surface for the red circle (SURFACE2)
(begin render loop)
clear SCREEN at the beginning of the loop (preventing ghosting and Hall of Mirror ugliness)
Blit SURFACE 1 to SCREEN to get the white circle on the screen
Blit SURFACE 2 to SCREEN to get the red circle on the screen
Flip SCREEN
Limit the Framerate to match the LCD refresh to prevent flicker
(end render loop)

Hope that helps.
This doesn't quite work. What you're talking about is this:

CODE

screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, SDL_SWSURFACE);
screen1 = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH,0,0,0,0);
screen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH,0,0,0,0);
filledCircleColor(screen1,SCREEN_CENTER,SCREEN_CENTER,100,0xFFFF00FF);
filledCircleColor(screen2,SCREEN_CENTER,SCREEN_CENTER,50,0xFFFF00FF);
SDL_BlitSurface(screen1,NULL,screen,NULL);
SDL_BlitSurface(screen2,NULL,screen,NULL);
SDL_Flip(screen);



But for some reason blitting screen2 to screen overwrites screen completely. It's almost certainly an alpha problem.... my SDL_CreateRGBSurface must have a black background instead of a transparent one. This code shows a yellow dot (from screen2) only.

At the moment for my real code, I blit the pre-rendered screen1 to screen2, draw on screen2 with each frame, and then blit screen2 to screen. It works for me at the moment, but it would be nice to be able to do what you've described.

Any ideas on an alternate call to SDL_CreateRGB that would properly give screen2 a transparent background instead of a black one?

Thanks for all your help.
 
Last edited by a moderator:
SDL's alpha blitting is horrible. It doesn't actually do an alpha combine, it just uses the alpha channel of the destination surface. The results are almost always bad. If at all possible do not use 24+8bit alpha for your surfaces settle on a color key as Eniko suggests and don't do alpha blitting at all. If you absolutely MUST do alpha blitting and you want it to come out right, you'll need to make (or use someone else's) software alpha blitting routine. It will be slow slow slow, So you can't use it for full screen blits and expect any sort of reasonable framerate, but for smallish sections of the screen it can work okay.
 
Multiplex said:
I think you need to either set SDL_SRCALPHA when creating screen2, or use SDL_SetAlpha on it afterwards.
(http://www.libsdl.org/cgi/docwiki.cgi/SDL_CreateRGBSurface)



Tried both already and it didn't work. Or at least I'm uncertain what it should be set to. I'll play with it some more.

I can verify this is an alpha problem. If I reduce the size of Screen2 and blit it to screen on top of screen1 in a geometrically smaller area, there is a black border around my yellow dot.

Actually I think I'll drop by the SDL IRC channel again and see if I can get some help there... back in a few minutes...

EDIT: Err... they weren't particularly helpful.. Managed to get into a flamewar over the definition of transparent and it went downhill from there... :( Apparently according to the SDL experts SDL sucks and I should use something else. Whatever... I should stick with forums if I'm having a bad day.

I did manage to find out more though....

I can set the screen transparency through SDL_SetAlpha(screen2,SDL_SRCALPHA,SDL_ALPHA_TRANSPARENT), but it makes the *whole* surface transparent... if you draw something opaque on it it takes on the surfaces transparency (setting alpha to 127 is a good way of seeing this... everything on the surface takes on an alpha of 127... the black background becomes 0x0000007F which shades whatever it's drawn on top of).

What I need is a simple codeblock to create an SDL_Surface with a transparent background (alpha=0), with an opaque object on top (alpha=255). I have all the other pieces of the puzzle.

EDIT2: Eniko nailed it. SDL_COLORKEY works great... THANK YOU!!!!!

SDL_SetColorKey(screen2, SDL_SRCCOLORKEY, 0x00000000);

Takes all "Black" pixels on a screen and sets them to alpha=0.
 
Last edited by a moderator:
Back
Top