The following user tutorial was created by Mod J., for the purpose of expanding upon the SDLTutorials.com series, and expounding upon the use of SDL. This tutorial, though not purposely a part of the SDLTutorials.com or created for the series, may be a branch or addition to the series. Please read notes by the author for any additional code and/or framework used by the author. If you wish to submit your own tutorial to this site, please visit the "User Tutorials" page.
Today we're going to learn something about extending the capabilities of SDL_Surfaces. If we take a look at OpenGL, what happens when we load a Surface and we render it specifying a width and height bigger than the Surface's original width and height? It gets automatically scaled (unless you specify you want to be repeated). In SDL there is no automatic way of scaling surfaces, so we're going to make a (relatively simple) routine that does the trick.

First, the prototype of the function:
SDL_Surface* SDL_ScaleSurface(SDL_Surface* Surface, Uint16 Width, Uint16 Height);
What are these parameters? Well:
Surface: The original surface the caller of the function wants to be scaled.
Width: The width the caller wants the scaled surface to be (we use Uint16 because this is SDL's standard for width).
Height: The height the caller wants the scaled surface to be (we use Uint16 because this is SDL's standard for height).

Got that? If you didn't, try reading it over again; if you did, then let's move on.

First, we have to check if the Surface's width and height are valid; this is simple to accomplish:
SDL_Surface* SDL_ScaleSurface(SDL_Surface* Surface, Uint16 Width, Uint16 Height)
{
    if(!Surface || !Width || !Height)
        return 0;
}
We don't want a null pointer specified, since it will make BAD things happen when we try to read data from the surface. Secondly, a surface with 0 width or 0 height wouldn't be a good idea, so let's just return a null pointer when either of these things happen.

Now we need to create a surface that suits our needs; we'll do this using the SDL_CreateRGBSurface function:
SDL_Surface* SDL_ScaleSurface(SDL_Surface* Surface, Uint16 Width, Uint16 Height)
{
    SDL_Surface *_ret = SDL_CreateRGBSurface(Surface->flags, Width, Height, Surface->format->BitsPerPixel,
        Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask, Surface->format->Amask);
}
Here we're declaring and defining the return surface. We want its specifications to be the same as the surface we got from the caller, so we do so, and then the width and the height should be the same as the ones the caller specified. Looks pretty simple to me ;-).

Now comes the math part. We need to calculate the scaling (or stretching) factor to be applied when we copy the surface's pixels. It's not as hard as it seems, we just divide the width the caller wants by the width of the surface, what have we calculated then? If you calculate the scaling factor for the widths, you'll get the number of pixels you need for each individual source pixel, in other words, each pixel you read from the source surface has to be drawn SCALING_FACTOR_X times over the width in order to stretch it (the same goes for the height).

Calculations:
SDL_Surface* SDL_ScaleSurface(SDL_Surface* Surface, Uint16 Width, Uint16 Height)
{
    double    _stretch_factor_x = (static_cast<double>(Width)  / static_cast<double>(Surface->w)),
        _stretch_factor_y = (static_cast<double>(Height) / static_cast<double>(Surface->h));
}
Casting might be bad to some of you, but it's necessary here since we need the scaling factors as a double. This probably could have been done better, like checking for w = 0 and h = 0, but this is just a simple tutorial ;-). We're just doing here what I explained before.

Now we need to do the actual drawing. This might look overwhelming, but it's not as hard as it seems. We're just going to make 2 for-loops to run across all the pixels on the source surface, and then 2 more loops for drawing STRETCHING_FACTOR pixels for each pixel that gets read from the source surface.

Here we go!
SDL_Surface* SDL_ScaleSurface(SDL_Surface *Surface, Uint16 Width, Uint16 Height)
{
    for(Sint32 y = 0; y < Surface->h; y++) //Run across all Y pixels.
        for(Sint32 x = 0; x < Surface->w; x++) //Run across all X pixels.
            for(Sint32 o_y = 0; o_y < _stretch_factor_y; ++o_y) //Draw _stretch_factor_y pixels for each Y pixel.
                for(Sint32 o_x = 0; o_x < _stretch_factor_x; ++o_x) //Draw _stretch_factor_x pixels for each X pixel.
                    DrawPixel(_ret, static_cast<Sint32>(_stretch_factor_x * x) + o_x, 
                        static_cast<Sint32>(_stretch_factor_y * y) + o_y, ReadPixel(Surface, x, y));
}
I'm not going to specify the DrawPixel and ReadPixel functions here, as they can be gotten from the SDL documentation wiki (http://www.libsdl.org/cgi/docwiki.cgi/Pixel_Access), though my prototype might be a little different, they work like this:
void DrawPixel(SDL_Surface* Target, Sint16 X, Sint16 Y, Uint16 Color); //Draws the specified color at the specified X and Y on the target surface.
Uint32 ReadPixel(SDL_Surface* Source, Sint16 X, Sint16 Y); //Returns the color of the specified surface on the specified X and Y coordinates.
The principle is pretty easy:
for(each_source_pixel) -> draw(scaling_factor_amount_of_pixels_on_the_return_value)
The DrawPixel call might look weird to you, but actually what happens inside is this: Draw a pixel on(the return value, stretching_factor_x * source_x because the destination moves each time and then blit the amount of pixels we need, same for y, what color do we need? The color that's on the source surface at the x and y position, because we need to blit it scaling_factor amount of times).

Why the strange X and Y coordinates? Well, imagine we need to make the width twice as bug, look at this crappy drawing:
Source surface:
---------------------------------------------
|x,y       |0,0       |1,0       |2,0       |
---------------------------------------------

Destination:
---------------------------------------------
|x,y       |0,0 & 1,0 |2,0 & 3,0 |4,0 & 5,0 |
---------------------------------------------
See? When at pixel (0,0) we need to draw a pixel at (0,0) and (1,0) because we need 2 pixels on the destination for each 1 pixel on the source (following the X-axis because it's the width), the same goes for the Y. We're actually just skipping what we've already drawn, _stretch_factor_x * x is actually 'Move to the corresponding pixel on the destination. It makes the (2,0) on the drawing say that it should begin its drawing at (4,0) on the destination, then it loops o_x untill it's smaller than 2, drawing first at (4,0) and then when o_x increments at (5,0). It's THAT easy.

Finally we return the desired surface:
SDL_Surface *SDL_ScaleSurface(SDL_Surface *Surface, Uint16 Width, Uint16 Height)
{
    return _ret;
}
Here an overview of the full function:
SDL_Surface *ScaleSurface(SDL_Surface *Surface, Uint16 Width, Uint16 Height)
{
    if(!Surface || !Width || !Height)
        return 0;
    
    SDL_Surface *_ret = SDL_CreateRGBSurface(Surface->flags, Width, Height, Surface->format->BitsPerPixel,
        Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask, Surface->format->Amask);

    double    _stretch_factor_x = (static_cast<double>(Width)  / static_cast<double>(Surface->w)),
        _stretch_factor_y = (static_cast<double>(Height) / static_cast<double>(Surface->h));

    for(Sint32 y = 0; y < Surface->h; y++)
        for(Sint32 x = 0; x < Surface->w; x++)
            for(Sint32 o_y = 0; o_y < _stretch_factor_y; ++o_y)
                for(Sint32 o_x = 0; o_x < _stretch_factor_x; ++o_x)
                    DrawPixel(_ret, static_cast<Sint32>(_stretch_factor_x * x) + o_x, 
                        static_cast<Sint32>(_stretch_factor_y * y) + o_y, ReadPixel(Surface, x, y));

    return _ret;
}
I hope you learned something with this tutorial, maybe I'll write more tutorials in the future (perhaps something on texture repeating? ;-)).

See ya!
Creature