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

Started by
10 comments, last by GameDev.net 19 years, 4 months ago
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
---http://www.michaelbolton.comI constantly dream about Michael Bolton.
Advertisement
this:

bmpBox->pixels[ 25*50+25 ]

is exactly the same as this:

*(bmpBox->pixels + 25*50 + 25)

Do you see now how it works?
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...
---http://www.michaelbolton.comI constantly dream about Michael Bolton.
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.
Evillive2
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.
---http://www.michaelbolton.comI constantly dream about Michael Bolton.
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.
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.
---http://www.michaelbolton.comI constantly dream about Michael Bolton.
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.
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?
---http://www.michaelbolton.comI constantly dream about Michael Bolton.
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.
Evillive2

This topic is closed to new replies.

Advertisement