Last Updated 2/26/06
We've already done some work dealing with the pixels directly, but it was just reading pixels. In this tutorial we will read/write pixels to make our own surface flipping function.
bool load_files() { //Load the image topLeft = load_image( "corner.png" ); //If there was an problem loading the image if( topLeft == NULL ) { return false; } //If eveything loaded fine return true; }
In our loading function we only load one image:
In order to show all four corners we well have to create them ourselves by flipping the image we loaded.
Uint32 get_pixel32( SDL_Surface *surface, int x, int y ) { //Convert the pixels to 32 bit Uint32 *pixels = (Uint32 *)surface->pixels; //Get the requested pixel return pixels[ ( y * surface->w ) + x ]; } void put_pixel32( SDL_Surface *surface, int x, int y, Uint32 pixel ) { //Convert the pixels to 32 bit Uint32 *pixels = (Uint32 *)surface->pixels; //Set the pixel pixels[ ( y * surface->w ) + x ] = pixel; }
Here's our functions to get and put pixels. In case you missed the bitmap font tutorial, here's a quick review on how pixel access works:
First thing we do is convert the pixel pointer from type void to 32bit integer so we can properly access them. After all, a surface's pixels are nothing more than an array of 32bit integers. Then we get or set the requested pixel.
You maybe be wondering why I don't just go "return pixels[ x ][ y ]".
The thing is the pixels aren't stored like this:
They're stored like this:
in a single dimensional array. Its because different operating systems store 2D arrays differently (At least I think that's why).
So to retrieve the red pixel from the array we multiply the y offset by the width and add the x offset.
These functions only work for 32-bit surfaces. You'll have to make one of your own if you're using a different format.
SDL_Surface *flip_surface( SDL_Surface *surface, int flags ) { //Pointer to the soon to be flipped surface SDL_Surface *flipped = NULL; //Create a blank surface flipped = SDL_CreateRGBSurface( SDL_SWSURFACE, surface->w, surface->h, surface->format->BitsPerPixel, surface->format->Rmask, surface->format->Gmask, surface->format->Bmask, surface->format->Amask );
Now here's our surface flipping function.
At the top we create a blank surface using SDL_CreateRGBSurface(). It may look complicated, but ultimately we're just creating a surface of the same size and format as the surface we are given.
Take a look at the SDL Documentation to see how SDL_CreateRGBSurface() works.
//If the surface must be locked if( SDL_MUSTLOCK( surface ) ) { //Lock the surface SDL_LockSurface( screen ); }
Before we can start altering pixels we have to check if the surface has to be locked before accessing the pixels using SDL_MUSTLOCK(). If SDL_MUSTLOCK() says we have to lock the surface, we use SDL_LockSurface() to lock the surface.
Now that the surface is locked it's time to mess with the pixels.
//Go through columns for( int x = 0, rx = flipped->w - 1; x < flipped->w; x++, rx-- ) { //Go through rows for( int y = 0, ry = flipped->h - 1; y < flipped->h; y++, ry-- ) {
Here is our nested loops used for going through the pixels. You may be wondering why we declare 2 integers in our for loops. The reason is that when you flip pixels, you have to read them in one direction and write them in reverse.
In the x loop, we declare "x" and "rx". "x" is initialized to 0, and "rx" (which stands for reverse x) is initialized to the width minus 1 which is the end of the surface.
Then in the middle the condition is normal. At the end "x" is incremented and "rx" is decremented. "x" will start at the beginning and go forward, "rx" starts at the end and goes backward.
So if the surface's width was 10, the for loop will cycle like so:
"x" : 0 1 2 3 4 5 6 7 8 9
"rx" : 9 8 7 6 5 4 3 2 1 0
I'm pretty sure you can now figure out what "y" and "ry" do.
//Get pixel Uint32 pixel = get_pixel32( surface, x, y ); //Copy pixel if( ( flags & FLIP_VERTICAL ) && ( flags & FLIP_HORIZONTAL ) ) { put_pixel32( flipped, rx, ry, pixel ); } else if( flags & FLIP_HORIZONTAL ) { put_pixel32( flipped, rx, y, pixel ); } else if( flags & FLIP_VERTICAL ) { put_pixel32( flipped, x, ry, pixel ); } } }
Here's the middle of the nested loops.
First we read in a pixel from the source surface. Then if the user passed in the FLIP_VERTICAL and the FLIP_HORIZONTAL flags, we write the pixels to the blank surface right to left, bottom to top.
If the user just passed the FLIP_VERTICAL flag, we write the pixels to the blank surface left to right, bottom to top.
If the user just passed the FLIP_HORIZONTAL flag, we write the pixels to the blank surface right to left, top to bottom.
If you're wondering what the flag values are, they're near the top of the source.
//Unlock surface if( SDL_MUSTLOCK( surface ) ) { SDL_UnlockSurface( surface ); } //Return flipped surface return flipped; }
At the end of our surface flipping function, we check if the surface had to be locked. If it did, we unlock it using SDL_UnlockSurface().
Lastly we return a pointer to our newly created flipped surface.
//Flip surfaces topRight = flip_surface( topLeft, FLIP_HORIZONTAL ); bottomLeft = flip_surface( topLeft, FLIP_VERTICAL ); bottomRight = flip_surface( topLeft, FLIP_HORIZONTAL | FLIP_VERTICAL ); //Apply the images to the screen apply_surface( 0, 0, topLeft, screen ); apply_surface( 320, 0, topRight, screen ); apply_surface( 0, 240, bottomLeft, screen ); apply_surface( 320, 240, bottomRight, screen ); //Update the screen if( SDL_Flip( screen ) == -1 ) { return 1; }
and here's the surface flipping function in action. Then the surfaces are applied and the screen is updated to show the daimond pattern.