How to Draw a Point in C++ / SDL
Drawing a point on the screen is the first function that one may search in graphics programming. Plotting a pixel is the basic unit of all other graphical objects we see in everyday computing. In this article, we will take a look at how it is done using SDL's surface buffer.
Assuming you've already configured your SDL "includes" and 'libraries", we start the program by initializing the SDL libraries. Initializing SDL Video is the first step before using any of its graphic functions. You do this by:
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
SDL_Log("Error Initializing the Video:\n%s", SDL_GetError());
returnMain = 1;
}
Make sure to trap the error and return failure upon an unsuccessful attempt to initialize. The SDL_GetError() function returns the error message that you can then use to display in your logs.
The second step is to initialize the window using the SDL_CreateWindow() function.
SDL_Window* window = SDL_CreateWindow("Pixel Demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
if (window == NULL)
{
SDL_Log("Error Initializing the Window:\n%s", SDL_GetError());
returnMain = 1;
}
Just like the initialization, error trapping has to be done in order to let the users and/or the programmer know that an error occurs while trying to open a window. SDL_CreateWindow returns NULL if unsuccessful.
The first parameter refers to the title of the graphical window to be displayed, the next two refer to the position of the window on the screen, then the next two defines the width and height of the window screen in pixels. The last parameter are additional flags to display the window, just default it to SDL_WINDOW_SHOWN. You may also refer to a complete list of flags that can be OR'd here.
After the window is initialized and shown, the next step is to get a reference to the window's surface buffer. The surface can be thought of as a staging area or a memory location where graphic manipulation at the pixel level can be made before actually displaying it on the screen. Every SDL window has this referenced object.
SDL_Surface* surface = SDL_GetWindowSurface(window);
if (surface == NULL)
{
SDL_Log("Error obtaining the window surface:\n%s", SDL_GetError());
returnMain = 1;
}
Once the surface has been successfully obtained from the window, we reference a handler to the pixel buffer. The size of the pixel buffer in bytes can be computed as:
Uint32 bufferSize = surface->w * surface->h * surface->format->BytesPerPixel;
SDL_Log("Buffer Size: %i", bufferSize);
So if we have an 800 x 600 with RGBA representation (4 bytes) of a pixel, then the size of the pixel buffer is 800 x 600 x 4 = 1,920,000 bytes. As you may have guessed by now, in order to get/modify a pixel at position (x, y) of the screen you multiply y by surface->w and by bytes-per-pixel (depending on the type of your pixel handler) then after that, you add x.
Also, the upper-left of the screen is identified as (0, 0), which means if you have an 800 x 600 screen, the ranges of x and y are 0-799 and 0-599 respectively.
For a 4-byte (Uint32) handler, pixel at (10, 10) can be defined as *(pixel + (10 * surface->w * surface->format->BytesPerPixel / sizeof(Uint32) ) + 10), or if you're sure that your screen bytes-per-pixel is 4 bytes, then you may omit the surface format and division of sizeof(Uint32) - *(pixel + (10 * surface->w) + 10) to remove the computational complexity.
For a 1-byte (Uint8) handler, pixel at (10, 10) can be defined as *(pixel + (10 * surface->w * surface->format->BytesPerPixel) + 10);
The data type of handler selection is critical and must be thought of well by the programmer. For example, in filling a screen, If you reference an unsigned int of 8-bytes to fetch each individual R, G, B, and A of a pixel, it will take you 1,920,000 iterations to fill the entire screen with color.
// Referencing an Uint8 to surface->pixels
Uint8* pixelC = (Uint8*)surface->pixels;
Uint8* pixelCBound = (Uint8*)surface->pixels + bufferSize;
// Put a pixel on each RGBA component of a pixel for the entire buffer size
do
{
*pixelC = 255;
} while (++pixelC < pixelCBound);
If you're filling in only with one color let's say a white (255, 0, 0, 255), I recommend you use an unsigned int of 32-bytes as a pointer. It will only take you 480,000 iterations.
// 32-Bit Handler
Uint32* pixelRGBA = (Uint32*)surface->pixels;
Uint32* pixelRGBABound = (Uint32*)surface->pixels + bufferSize / sizeof(Uint32);
Uint32 color = SDL_MapRGBA(surface->format, 255, 0, 0, 255);
do
{
*pixelRGBA = color;
} while (++pixelRGBA < pixelRGBABound);
Referring an int of 8-bytes to the pixel buffer has its own application, i.e., you want to read/modify only the blue channel of the pixel.
// Referencing Uint8 to blue channel
pixelC = (Uint8*)surface->pixels;
// Put a pixel on each RGBA component of a pixel for the entire buffer size
do
{
*pixelC = 255;
pixelC += 4;
} while (pixelC < pixelCBound);
Now it's time to display whatever is in the buffer to the screen. You do this by the following command:
SDL_UpdateWindowSurface(window);
SDL_Delay(2000);
We use SDL_Delay() to show the screen for 2,000 milliseconds (or simply 2 seconds). It is important to take note not to use SDL_UpdateWindowSurface(window) if you're not done with your pixel manipulation, as it will be very slow and will flicker the screen on a smaller resolution, unless it is the effect you desired.
Once you've done copying the surface buffer to the screen, don't forget to clean up the mess by issuing the following SDL Commands.
returnMain = 0;
SDL_DestroyWindow(window);
SDL_Quit();
Alright, so there you have it a complete guideline on plotting a pixel on screen using C/C++ and SDL. You may download the entire source code in the download section below.