The following tutorial was created by Blue Dino Code, for the purpose of helping people like you learn 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. SDLTutorials.com does not endorse or reject any of the coding practices outlined in the tutorial, and is not responsible for any code or files belonging to this tutorial that harms your computer.

A Guide to Graphics with SPriG

Prerequisites: Some knowledge of SDL (see Installing SDL)

The SDL Primitive Generator, SPriG, is a quick and simple interface to making graphics on SDL surfaces. In this guide, I'll be discussing the notable features of Sprig with usage examples. Not all of the functions are described here. There are plenty more to be found in the documentation once you get the hang of the stuff found here. I will, however, be describing the entire dirty rect interface.

Contents
Introduction History Installation Surface Commands Graphics Primitives Polygons Rendering Controls Image Drawing Angles Surface Transformations Dirty Rects

Introduction

Intro
Sprig does three main things: It draws graphical primitives, it rotates and scales surfaces, and it simplifies access to SDL surfaces. Once you're off and using Sprig, I suggest that you download the documentation so you have easy reference material at hand.

Along the way, I'll be leaving usage examples. In these, I'll be using SDL_ColorDef to define colors, so if you see something like this:
RGB_BLUE(mySurface)
This is just a quick replacement for the usual format-independent call in SDL:
SDL_MapRGB(mySurface->format, 0, 0, 255)
Isn't that convenient? So, instead of describing a color with red, green, and blue components, I'll just use common names.
History and Motivation

history
Sprig was originally an overhaul of SGE. I called it minSGE back then because I was not satisfied with how many peripheral features are in SGE. I cut away collision detection, fonts, C++ sprite classes, and text classes. These features are handled far better by other libraries and would be quite the hassle to maintain and upgrade properly. I assumed that most people used SGE for primitives and transforms and used other libraries for all the other stuff. A major annoyance to me were the naming conventions, so I worked on that, too. Well, that set the stage for designing Sprig. In fact, I thought that fonts were important enough that I also started NFont as a successor to SFont for drawing bitmap fonts. Sprig and NFont together are all that a simple game needs to get started. These decisions also made it easy for Sprig to become a pure C library, so that users of either C or C++ can enjoy it.

I hope you'll find that Sprig has a very comfortable feel in its naming scheme and range of convenient functions. My main goal besides providing primitives and transforms is to supplement the built-in SDL functions to make development faster with less typing.

Installation

installation
Installation for Sprig is just like any other C library. You can check out the SDL installation guide for generic details (Installing SDL). For Sprig, you can get the pre-built binaries, use 'make', or build it by hand.

First of all, download the library, of course. If you have the binaries for your system, unzip them. Copy 'sprig.h' and 'sprig_inline.h' to your compiler's /include or /include/SDL folder. Then copy the appropriate library file (libsprig.a, libsprig.so, sprig.lib) to your compiler's /lib folder. Now you can write Sprig programs by using:
#include "sprig.h"
and linking with sprig (-lsprig or sprig.lib).

When you distribute these programs, be sure to include the dynamic library appropriate to your system (sprig.dll, libsprig.so).

Using make (Linux):
Download the Sprig source, open a terminal window, and move into the unzipped Sprig source directory. Type:
make install
That should be it!

Building by hand: Download the Sprig source and unzip it. Make a new dynamic library project in your favorite compiler that contains all of the source files (.c). Add SDLmain and SDL to your linker options. Compile it and install it as above.

If you're willing to comply with the LGPL license on static linking, you can build a static link library or just add all of the Sprig source files to your project. You will then not need to distribute the library with your executable.

If you have any problems or are not on Windows XP or Debian GNU/Linux, then please send me an email!

Lurking Beneath the Surface

Sprig's surface functions make creation and control of SDL surfaces quite easy. Sprig has functions to set up SDL, create surfaces, and copy surfaces.
SDL_Surface* SPG_InitSDL(Uint16 w, Uint16 h, Uint8 bitsperpixel, Uint32 systemFlags, Uint32 screenFlags)
SDL_Surface* SPG_CreateSurface32(Uint32 flags, Uint16 width, Uint16 height)
SDL_Surface* SPG_CopySurface(SDL_Surface* surface)
void SPG_Free(SDL_Surface* surface)
void SPG_SetColorkey(SDL_Surface* surface, Uint32 color)
void SPG_SetClip(SDL_Surface* surface, const SDL_Rect clipRect)
void SPG_RestoreClip(SDL_Surface* surface)

SPG_Init is a wrapper for calls to SDL_Init and SDL_SetVideoMode. It returns the display surface or NULL if there's an error. Just like with SDL_SetVideoMode, passing 0 for the surface depth will give you the best available depth.
SDL_Surface* screen = SPG_InitSDL(800, 600, 0, SDL_INIT_VIDEO, SDL_SWSURFACE | SDL_FULLSCREEN);
if(screen == NULL)
    exit(0);
SPG_CreateSurface32 is how you can create 32-bit RGBA surfaces with minimal work.
SDL_Surface* mySurf = SPG_CreateSurface32(SDL_SWSURFACE, 100, 100);
SPG_CopySurface returns a duplicate of the given surface.
SDL_Surface* copy = SPG_CopySurface(mySurf);
SPG_Free is a wrapper for SDL_FreeSurface. It's just for less typing, nothing special. When you're done using a surface, you must free the memory using SPG_Free (or SDL_FreeSurface).
SPG_Free(mySurf);
SPG_SetColorkey sets the transparent color for a given surface. Colorkey transparency can only be used for RGB->RGB blits, RGB->RGBA blits, or RGBA->RGBA blits with SDL_SRCALPHA disabled.
SPG_SetColorkey(mySurf, RGB_BRIGHTPINK(mySurf));
SDL_BlitSurface(mySurf, NULL, screen, NULL);  // Only non-pink areas will be drawn.
SPG_SetClip sets the clipping rectangle for the given surface. When stuff is drawn to this surface, only the parts inside the clipping rect will actually be drawn. You can reset the clipping rect by using SPG_RestoreClip. This is really useful for splitscreen effects.
SDL_Rect clipRect = {40, 40, 60, 100};
SPG_SetClip(mySurf, clipRect);
SPG_RestoreClip(mySurf);  // Reset it


As Primitive as Can Be

primitives
Sprig is first and foremost a generator for graphics primitives (of course). These are pixel shapes like lines, circles, and rectangles. Sprig has functions to create:

Pixels
Lines
Rectangles
Circles
Ellipses
Arcs
Bezier curves
Trigons (Triangles)
Polygons

Each of these primitives can be drawn with anti-aliasing, different alpha-blending modes, and a specific thickness. Rects, circles, ellipses, arcs, trigons, and polygons can all be drawn with filled interiors. Check out the Controls section for more info on changing how primitives are drawn.

The naming convention that Sprig follows is like so:
SPG_(Shape)(Filled)(Blend)

For example:
SPG_RectFilled
SPG_PixelBlend
SPG_EllipseFilledBlend
The different words in each function name follow an intuitive hierarchical naming scheme, based on the magnitude of the change they make to the shape. This is especially useful when combined with many IDEs' autocompletion to save you from lots of typing.

Here are some sample drawing commands. The signature of each function can be found in the documentation.
SPG_Rect(screen, 40, 60, 140, 100, RGB_RED(screen));
SPG_CircleFilledBlend(screen, 300, 100, 20, RGB_PURPLE(screen), 200);
SPG_Line(screen, 0, 0, screen->w-1, screen->h-1, RGB_GRAY(screen));
SPG_ArcFilled(screen, 200, 500, 40, -90, 45, RGB_YELLOW(screen));
SPG_PixelBlend(screen, 60, 60, RGB_WHITE(screen), 100);

Two Sides of the Same Polygon

polygons
Polygons are great for many effects and can represent platforms or form characters in various video games. With Sprig's polygon rotations, you can easily make something like the opening screen for Zelda III... Some functions use the SPG_Point coordinate structure to simplify their calls. SPG_Point contains two floating point values, x and y. Use SPG_MakePoint to define your own points for these functions. You can also fill the struct when it's created:
SPG_Point p = {50, 40};
The functions:
void SPG_Trigon(SDL_Surface* surface, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3, Sint16 y3, Uint32 color)
void SPG_QuadTexPoints(SDL_Surface* dest, SPG_Point* dest_points, SDL_Surface* source, SPG_Point* source_points)
void SPG_Polygon(SDL_Surface* surface, Uint16 n, SPG_Point* points, Uint32 color)
void SPG_CopyPoints(Uint16 n, SPG_Point* points, SPG_Point* buffer)
void SPG_RotatePoints(Uint16 n, SPG_Point* points, float angle)
void SPG_ScalePoints(Uint16 n, SPG_Point* points, float xscale, float yscale)
void SPG_SkewPoints(Uint16 n, SPG_Point* points, float xskew, float yskew)
void SPG_TranslatePoints(Uint16 n, SPG_Point* points, float dx, float dy)

SPG_Trigon draws a triangle from three points. Just like other primitive shapes, it can be a line drawing or variable thickness, filled, alpha-blended, or anti-aliased.
SPG_Trigon(screen, 0, 0, 50, 10, 20, 20, RGB_ORANGE(screen));
SPG_QuadTexPoints draws a four-sided polygon that is filled with an image. A set of source points allows you to choose exactly what part of the source image is used. This can be used for stretching, rotating, and distorting images. There is another function, SPG_QuadTex, that uses (x,y) coordinates instead of SPG_Points.
SPG_QuadTexPoints(screen, myQuad, myImage, otherQuad);
SPG_Polygon and all of its variants draw a multi-sided shape using SPG_Point values.
SPG_Polygon(screen, 6, myHexagon, RGB_TEAL(screen));
SPG_CopyPoints makes a static copy of your points into the given buffer. The result can be messed with, drawn using SPG_Polygon, then deleted without ruining your original shape. It uses a pre-existing buffer, so it's nice and fast. You can also use this to copy part of a polygon into another polygon with some pointer arithmetic.
SPG_Point buffer[6];
SPG_CopyPoints(6, myHexagon, buffer);

SPG_CopyPoints(3, myTrigon, (myHexagon + 3)); // Copy points into myHexagon[3], [4], and [5]
SPG_RotatePoints rotates each SPG_Point about the origin by the specified angle. To rotate about a different point, you can use SPG_RotatePointsXY (or translate it back and forth).
SPG_RotatePoints(5, myPentagon, 45);
SPG_ScalePoints can make a polygon bigger or smaller. It scales about the origin (i.e. assumes the center of the polygon is at (0,0)), but you can use SPG_ScalePointsXY (or translate it back and forth) to change that.
SPG_ScalePoints(5, myPentagon, 2.0f, 2.0f);  // Double the size
SPG_SkewPoints applies a shear transform to the polygon (about the origin, as above - Try out SPG_SkewPointsXY too).
SPG_SkewPoints(4, leaningTower, 2.0f, 1.0f);
SPG_TranslatePoints moves all the points in a polygon.
SPG_TranslatePoints(4, myBox, 200, 400);

Those Controls are Stacked!

Several options are controlled through a stack system. When you change an option, you push a new value onto the stack. When you're done, you pop the stack, returning it to the old state. In this way, you can write a block of code or a function that changes the state to achieve a particular effect, then returns it to the old state for the rest of the program to use.

There are three stacks held by Sprig. The thickness stack controls the thickness of any line primitives that are drawn (default: 1). The blending stack determines the special blending mode for alpha-blended primitives and SPG_Blit (default: SPG_DEST_ALPHA). The anti-alias stack controls whether graphics primitives are anti-aliased (smoothed) or not (default: 0). The surface alpha stack controls whether or not SPG_Blit combines the per-surface alpha with the per-pixel alpha (default: 0).
void SPG_PushThickness(Uint16 state)
Uint16 SPG_PopThickness()
void SPG_PushBlend(Uint8 state)
Uint8 SPG_PopBlend()
void SPG_PushAA(SPG_bool state)
SPG_bool SPG_PopAA()
void SPG_PushSurfaceAlpha(SPG_bool state)
SPG_bool SPG_PopSurfaceAlpha()

Here's a table of the available blending modes:
Name RGB Alpha Description
SPG_DEST_ALPHA Blend Dest Blends colors normally and keeps the destination per-pixel alpha. This is the same as SDL_BlitSurface's blending.
SPG_SRC_ALPHA Blend Src Blends colors and copies the source alpha.
SPG_COMBINE_ALPHA Blend Blend Blends colors and blends the alpha. This is "True" blending and lends well to some neat compositing effects.
SPG_COPY_NO_ALPHA Src Opaque Copies the source color, sets the dest alpha to opaque.
SPG_COPY_DEST_ALPHA Src Dest Copies the source color, keeps the dest alpha.
SPG_COPY_SRC_ALPHA Src Src Copies the source color and alpha.
SPG_COPY_COMBINE_ALPHA Src Blend Copies the source color and blends the alpha.
SPG_COPY_ALPHA_ONLY Dest Src Keeps the dest color, but copies the source alpha.
SPG_COMBINE_ALPHA_ONLY Dest Blend Keeps the dest color, but blends the alpha.
SPG_REPLACE_COLORKEY Src Src If the destination surface has the SDL_SRCCOLORKEY flag, this replaces the dest colorkey color in the image with the source color and alpha. This is similar to palette-swapping, but can be used with gradients and other images.

Some examples:
SPG_PushBlend(SPG_COMBINE_ALPHA);
SPG_PushAA(1);
SPG_PushThickness(5);

SPG_PopThickness();
SPG_PopAA();
SPG_PopBlend();


All the Pi in the World...

Sprig can work with both degree angle measure and radians. By default, all functions use the more common (in everyday life) units of degrees. You can change this behavior with a call like:
SPG_EnableRadians(1);
This avoids one floating-point multiplication in some of Sprig's internal conversions (Sprig uses radians internally). To make radian measure easier to use, there are several #defines involving pi: PI_8, PI_4, PI_2, PI3_4, PI, PI5_4, PI3_2, PI7_4, PI2 A number after 'PI' indicates multiplication, and an underscore means division. So PI3_2 is "Three halves times pi" or 1.5*pi. An amount equal to pi radians is equivalent to 180 degrees. Every quarter value of pi (45 degree increments) up to 2*pi (360 degrees) is provided. Sprig also provides conversion factors for converting between degrees and radians:
float radians = degrees * RADPERDEG;
or
float degrees = radians * DEGPERRAD;
These #defines are named after the unit conversion, which is a fraction. The measure that you're converting to will be in the numerator (top), with the old unit in the denominator (bottom).

More Than Meets the Eye

Sprig transformation routines can scale, rotate, and flip SDL surfaces.
SDL_Surface* SPG_Transform(SDL_Surface *src, Uint32 bcol, float angle, float xscale, float yscale, Uint8 flags)
SDL_Rect SPG_TransformX(SDL_Surface *src, SDL_Surface *dst, float angle, float xscale, float yscale, Uint16 pivotX, Uint16 pivotY, Uint16 destX, Uint16 destY, Uint8 flags)
SDL_Surface* SPG_Rotate(SDL_Surface *src, float angle, Uint32 bgColor)
SDL_Surface* SPG_RotateAA(SDL_Surface *src, float angle, Uint32 bgColor)
SDL_Surface* SPG_Scale(SDL_Surface* src, float xscale, float yscale)
SDL_Surface* SPG_ScaleAA(SDL_Surface* src, float xscale, float yscale)

Anti-aliasing is handled a little differently for the transform functions than for primitives. This is because they operate on a different level: surfaces rather than shapes. You don't usually want your choices for one to affect your results for the other. For this reason, SPG_Transform and SPG_TransformX use bit flags to control their behavior:

SPG_NONE - Normal, same as 0.
SPG_TAA - Anti-aliasing (no more jaggies)
SPG_TSAFE - Makes incompatible surface depths work right
SPG_TTMAP - Uses texture mapping (SPG_QuadTex) for a faster, but uglier result
SPG_TSLOW - Uses a slow transformation, but is very accurate
SPG_TCOLORKEY - Ignores the colorkey value on the destination surface
SPG_TBLEND - Alpha-blend on the destination surface
SPG_TSURFACE_ALPHA - Blend with the per-surface alpha

SPG_Rotate and SPG_Scale are convenience functions that call SPG_TransformX. So, to simply rotate an image:
SDL_Surface* rotated = SPG_Rotate(myImage, 60, RGB_BLACK(myImage));
C++ users can omit the background color argument (defaults to 0x000000). To scale a surface:
SDL_Surface* scaled = SPG_Scale(myImage, 0.5f, 0.5f);  // Quarter the size (half each dimension)
To flip a surface horizontally:
SDL_Surface* flipped = SPG_Scale(myImage, -1, 1);
SPG_TransformX gives you the most control over your surfaces. Let's look at it closely:
SDL_Rect SPG_TransformX(SDL_Surface *src, SDL_Surface *dst, float angle, float xscale, float yscale, Uint16 pivotX, Uint16 pivotY, Uint16 destX, Uint16 destY, Uint8 flags);

This function takes a surface, transforms it, and draws it onto another surface. The first two arguments are the source surface and the destination surface. The third argument is a rotation angle. The next two are scaling factors. The sixth and seventh are pivot point coordinates. These determine the point about which the surface is to be rotated. The eighth and ninth arguments are the drawing destination coordinates. The last is a bitwise OR '|' combination of transformation flags.

Draw Me Close...

Sprig has plenty of handy drawing functions. These give you quick control over SDL surfaces. The ones you should take note of are:
Uint32 SPG_GetPixel(SDL_Surface* surface, Sint16 x, Sint16 y)
Uint32 SPG_ConvertColor(SDL_PixelFormat* srcfmt, Uint32 srccolor, SDL_PixelFormat* destfmt)
SDL_Rect SPG_MakeRect(Sint16 x, Sint16 y, Uint16 w, Uint16 h)
int SPG_Blit(SDL_Surface* Src, SDL_Rect* srcRect, SDL_Surface* Dest, SDL_Rect* destRect)
SDL_Surface* SPG_ReplaceColor(SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dest, SDL_Rect* destrect, Uint32 color)
void SPG_Draw(SDL_Surface* source, SDL_Surface* dest, Sint16 x, Sint16 y)
void SPG_DrawCenter(SDL_Surface* source, SDL_Surface* dest, Sint16 x, Sint16 y)
void SPG_FloodFill(SDL_Surface* dst, Sint16 x, Sint16 y, Uint32 color)

SPG_GetPixel returns the color of a target pixel, like so:
Uint32 color = SPG_GetPixel(mySurface, x, y);
Use SPG_ConvertColor to change a color from one format to another:
Uint32 screenBrown = SPG_ConvertColor(mySurf->format, myBrown, screen->format);
In many functions, like SDL_BlitSurface, you use an SDL_Rect to define the source pixels or to indicate the drawing location. SPG_MakeRect can be used to make rects for such use:
SDL_Rect rect = SPG_MakeRect(40, 30, 60, 60);  // A 60x60 rect at the location (40,30)
SPG_Blit draws an image using Sprig's custom blitter. It's pretty slow, but allows you to use several alpha-blending modes.
SDL_Rect location = {50, 50, 0, 0};
SPG_Blit(mySource, NULL, myDest, &location);
SPG_ReplaceColor lets you blit an image only to specific colored areas on the destination surface. This is an effect similar to the palette-swapping that tons of old 8-bit games used to make identical enemies of different colors.
SPG_ReplaceColor(mySource, NULL, myDest, NULL, color);
SPG_Draw is a quick wrapper call to SDL_BlitSurface.
SPG_Draw(mySurf, screen, x, y);
SPG_DrawCenter is just like SPG_Draw except that it uses the width and height of the source surface in order to draw it centered on the given coordinates. SPG_FloodFill fills in the target area (all pixels of the same color) with the given color.
SPG_FloodFill(mySurf, x, y, color);

Those Low-down Dirty Rects!

dirty rects
If a game is changing the entire screen every frame, it makes sense to update the entire screen. But if you're only moving a few sprites on a static background, then a huge time savings can be made by updating only the "dirty" parts. Sprig includes a dirty rect system to make this easy to do. David Olofson's code from Fixed Rate Pig (http://olofson.net/) turned out to be a great and simple implementation, so he deserves all the credit for what ended up in Sprig. The complete Dirty system consists of these calls:
void SPG_EnableDirty(SPG_bool enable)
void SPG_DirtyInit(Uint16 maxsize)
void SPG_DirtyAdd(SDL_Rect* rect)
SPG_DirtyTable* SPG_DirtyUpdate(SDL_Surface* screen)
void SPG_DirtySwap()
SPG_bool SPG_DirtyEnabled()
SPG_DirtyTable* SPG_DirtyMake(Uint16 maxsize)
void SPG_DirtyAddTo(SDL_Rect* rect, SPG_DirtyTable* table)
void SPG_DirtyFree(SPG_DirtyTable* table)
SPG_DirtyTable* SPG_DirtyGet()
void SPG_DirtyClear(SPG_DirtyTable* table)
void SPG_DirtyLevel(Uint16 optimizationLevel)
void SPG_DirtyClip(SDL_Surface* screen, SDL_Rect* rect)

The first group up there are the important ones. To start, call:
SPG_EnableDirty(1);
This tells all of the primitives to output dirty rects. These rects are collected by the 'front' dirty table. There are two of these tables, the 'front' and the 'back'. Everything happens on the front table and the back table is used to save old dirty rects for an extra frame. This is necessary since you must update the new position of a sprite to show where it is, but also the old position in order to show empty space where it once was. The definition of the tables looks like this:
typedef struct SPG_DirtyTable
{
    Uint16 size;  /* Table size */
    SDL_Rect *rects;  /* Table of rects */
    Uint16 count; /* # of rects currently used */
    Uint16 best;  /* Merge testing starts here! */
} SPG_DirtyTable;
You will use the count to loop over the rects when redrawing your background. Next, call:
SPG_DirtyInit(64);
This initializes the front and back tables to contain a maximum of 64 dirty rects. When it reaches that limit, some rects will be combined as more are added. Now you're ready to enter your main drawing loop. Whenever you draw something to the screen, you need to collect its dirty rect. If you use SDL_BlitSurface(), then this is pretty easy:
SDL_Rect dirty;
dirty.x = myX;
dirty.y = myY;
SDL_BlitSurface(myPic, NULL, screen, &dirty);
SPG_DirtyAdd(&dirty);
SDL_BlitSurface will write the actual dirty rect into 'dirty'. SPG_DirtyAdd() puts the given rect into the front table and does any necessary merging. You must be sure that the rects that you add to the front table are properly clipped to the screen. If not, the internal call to SDL_UpdateRects() will crash. If you're not sure about it, call:
SPG_DirtyClip(screen, &dirty);
This will make sure that your rect is safe to add. Any calls to Sprig primitives and blits will automatically do all of this. When you're all done adding dirty rects (you're done drawing to the screen), make this call:
SPG_DirtyTable* table = SPG_DirtyUpdate(screen);
Now you've updated the screen, just as if you had called SDL_Flip(screen), and you receive a pointer to the front table. This has all of the newly updated rects in it for you to redraw the screen's background:
// This is for a full-screen background image
int i;
SDL_Rect dest;
for(i = 0; i < table->count; i++)
{
    dest = table->rects[i];  // SDL_BlitSurface is destructive to your rects
    SDL_BlitSurface(myBackground, &(table->rects[i]), screen, &dest);
}
The screen is all set for more drawing. Now we have to swap the tables:
SPG_DirtySwap();
This clears and switches the tables so that they are ready for the next frame. The rest of the functions are for if you want to do fancy things with the dirty rects. You can make your own table with SPG_DirtyMake. You can add rects to it with SPG_DirtyAddTo and free it with SPG_DirtyFree when you're done.
SPG_DirtyTable* table = SPG_DirtyMake(128);
SDL_Rect rect = {30, 40, 10, 10};
SPG_DirtyAddTo(&rect, table);
SPG_DirtyFree(table);
SPG_DirtyEnabled tells you if dirty rects will be automatically generated.
SPG_bool gettingDirty = SPG_DirtyEnabled();
SPG_DirtyGet returns a pointer to the front table (just be aware that the tables are switched each time SPG_DirtySwap is called).
SPG_DirtyTable* front = SPG_DirtyGet();
// Add stuff to it or whatever
SPG_DirtyClear will reset a given table, and you can adjust the constants in the dirty algorithm with SPG_DirtyLevel. These constants determine the thresholds for merging rects.
SPG_DirtyClear(table);
SPG_DirtyLevel(200);


There you go. By now you should have a feeling of what Sprig can do for you and what kinds of awesome games and applications you can write using it. If you have any questions, suggestions, or contributions, drop me an email. Jonny D