Sign in to follow this  
Khaosifix

I'm a little fuzzy on how to draw directly to the display...[SDL]

Recommended Posts

Hello, I was reading the SDL documentations and came across a method to draw directly to the display. This method involved an application-defined function with the prototype : void putpixel(SDL_Surface * Surface, int x, int y, Uint32 pixel ). What was so confusing to me about this function was its definition which as follows :
/*
 * Set the pixel at (x, y) to the given value
 * NOTE: The surface must be locked before calling this!
 */
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to set */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        *p = pixel;
        break;

    case 2:
        *(Uint16 *)p = pixel;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        } else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}

Initially when I started programming SDL I used to think that modifying a surface would require 1) First locking it and 2) directly modifying its pixels like so :
typedef SDL_Surface* SURFACE ;

// Loads a red 50x50px box.
SURFACE bmpBox = SDL_LoadBMP( "../images/Box.bmp" ) ;

// Lock the surface for direct access.
SDL_LockSurface( bmpBox ) ;

// Change the pixel at 25x25 to blue using the formula, y*WIDTH+x to access the pixel position.
bmpBox->pixels[ 25*50+25 ] = (void*)SDL_MapRGB( bmp->pixels, 0x00, 0x00, 0xFF ) ;  // or (void*)0x0000FF

SDL_UnlockSurface( bmpBox ) ;

// Draw the surface.

SDL_FreeSurface( bmpBox ) ;

Can someone explain how 'assigning pointers'[thats all putpixel seems to be doing] can modify the pixels of a surface? Also, what is wrong with my method? It seems intuitive to me. ~Khaosifix

Share this post


Link to post
Share on other sites
Quote:
Also, what is wrong with my method? It seems intuitive to me.


Your version does not take into consideration the surface pitch or the bytes per pixel and will give you access to the wrong pixel or go out of bounds depending on the bytes per pixel.
This line:

Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

vs yours:

bmpBox->pixels[ 25*50+25 ] = (void*)SDL_MapRGB( bmp->pixels, 0x00, 0x00, 0xFF ) ; // or (void*)0x0000FF


Also, I don't know if this was a typing error or not but could be, but when you call SDL_MapRGB the first argument should be bmp->format, not bmp->pixels.

I also think the (void*) typecast may be asking for trouble vs the way it is done in the SDL doc version but I could be off base on the way that works.

Share this post


Link to post
Share on other sites
Yeah I'm beginning to understand it now.


int array[3] = { 1, 2, 3 } ;

int number = *(array + 1) ; // variable 'number' should equal to 2 now



Something like that?

But all putpixel does is assign *p to some value. Just by looking at the code it doesn't really call an SDL function to render anything.

Share this post


Link to post
Share on other sites
Quote:
Original post by Khaosifix
Something like that?
Correct.

Quote:
But all putpixel does is assign *p to some value. Just by looking at the code it doesn't really call an SDL function to render anything.
You have to understand how video hardware works, fundamentally. There is a region of system memory that is set aside for the representation of the display, starting at the address 0xB000000 in PCs. In linear modes, successive bytes of memory represent successive pixels on the display, wrapping around to the next line at the end of each scanline. In more complex modes, like most modern graphics modes, the memory is bank-switched, meaning that the physical address is mapped to another address in the video RAM on the GPU.

Skipping all the details, this means that once you have a pointer to a valid offset from the physical start of video memory (which may no longer actually represent video memory), you can write to it as with any other pointer-memory operation.

Share this post


Link to post
Share on other sites
Hmm this may seem like a dumb question but OpenGL is considered a low-level graphic API. Why is it that plotting a pixel in OpenGL


glBegin( GL_POINTS ) ;
glColor3f( 1.0f, 0.0f, 0.0f ) ;
glVertex3f( 0.0f, 0.0f, 0.0f ) ;
glEnd() ;



is much easier than plotting a pixel in SDL? My guess is that OpenGL takes care of this linear mode, complex mode, bank-switched thingy you were talking about. If this is so then OpenGL isn't really low-leveled...

But thanks anyways.

Share this post


Link to post
Share on other sites
Quote:
Original post by Khaosifix
Hmm this may seem like a dumb question but OpenGL is considered a low-level graphic API. Why is it that plotting a pixel in OpenGL

*** Source Snippet Removed ***

is much easier than plotting a pixel in SDL? My guess is that OpenGL takes care of this linear mode, complex mode, bank-switched thingy you were talking about. If this is so then OpenGL isn't really low-leveled...

But thanks anyways.


You should never draw like that; it is incredibly slow. It sends each pixel to the graphics card as two floats for position and three for color and has to process and transform every pixel.

Also look at the setup for OpenGL. Different settings will change the way it is displayed, and it won't be exactly what you tell it to plot.

OpenGL is a 3D graphics API, not a 2D graphics API. It works in a fundamentally different way. Instead, to do 2D with OpenGL, you should create buffers with your images and upload them to video memory, then use textured quads to draw them, using alpha blending if necessary. It is more complicated than SDL, but it also gives cool hardware-acclerated effects.

Share this post


Link to post
Share on other sites
Quote:

You should never draw like that; it is incredibly slow. It sends each pixel to the graphics card as two floats for position and three for color and has to process and transform every pixel.


You mean three floats for position?

And for that little OpenGL example I posted it was necessary to draw the point like that. Other alternatives include vector arrays and buffer objects.........but its only a single red point............Is there a better way to draw a single point in OpenGL that you might suggest?

Share this post


Link to post
Share on other sites
Quote:
And for that little OpenGL example I posted it was necessary to draw the point like that. Other alternatives include vector arrays and buffer objects.........but its only a single red point............Is there a better way to draw a single point in OpenGL that you might suggest?


My only suggestion is that if you are using a lot of pixel operations in OpenGL is that you might be using the wrong API for the job.

I know it sounds counter intuitive, but per pixel operations are actually faster in software than on todays hardware. For example, test it out with SDL. The easiest way to see the results is by using SDL's alpha blending stuff which behind the scenes does per pixel operations (not as optimized as they could be but in my own case more so than I could do on my own).

Make sure it is a full screen application and request a hardware screen buffer. Then load a bitmap as a hardware surface as well. Check the flags of the surface after it is created to make sure it is created in hardware. Make sure you use SDL_DisplayFormat on the surface to optimize the blitting. Then set the per surface alpha using SDL_SetAlpha on the bitmap surface to something int the range of 1 - 127. Then loop blitting the bitmap surface to the screen for 60 seconds or however long and determine the framerate.

Now try the same thing using a software surface for the bitmap surface.

Now try it again with the software surface for the bitmap and software surface for the screen buffer in windowed mode.

Run the results and let us know what you came up with.

Share this post


Link to post
Share on other sites
Quote:
Original post by Khaosifix
In the SDL documentations SDL_Surface::pixels is defined as a void*. How is it possible to perform arithmetics on a void*? C++ is a weird language...


void* simply means "some pointer", alone it means nothing, but if you type cast it, you can actually use it. Using void* is an advance topic that takes some time to understand, so don't worry if you dont get it, you wont use it in your own code.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Drew_Benton
Quote:
Original post by Khaosifix
In the SDL documentations SDL_Surface::pixels is defined as a void*. How is it possible to perform arithmetics on a void*? C++ is a weird language...


void* simply means "some pointer", alone it means nothing, but if you type cast it, you can actually use it. Using void* is an advance topic that takes some time to understand, so don't worry if you dont get it, you wont use it in your own code.


Elaborating further: A pointer is just a number representing a location in memory. Wether p is pointing to a Uint32 or a Uint8 doesn't matter, p just stores the location of the first byte. Because of this we can use a typeless pointer (void *p) to point to anything we want, then simply cast it back to work with it. For an example of how it can be used, look at the SDL_AddTimer function:

//SDL_AddTimer
SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void *param)

//SDL_NewTimerCallback
typedef Uint32 (*SDL_NewTimerCallback)(Uint32 interval, void *param);


SDL_AddTimer installs a new timer, which will call "callback" every "interval" miliseconds. If you are unfamilliar with the term, a callback is a pointer to a function to be called when an event occurs, in this case when the interval elapses. As you can see, when you call SDL_AddTimer you provide it with a void pointer to something. It doesn't know what something is and it doesn't care either, thats for your callback function to deal with. The callback function will cast the pointer to whatever data it was that it expected, like say "Uint32 *pData = (Uint32)p;". It might even dereference it, depending on the situation.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this