SDL2 Pixel Drawing

This article was originally posted on 13th February 2014 at Programmer’s Ranch. It has been slightly updated here. The source code for this article is available at the Gigi Labs BitBucket repository.

Greetings! ūüôā

In “Handling Keyboard and Mouse Events in SDL2“, we saw how we could handle keyboard and mouse events to allow the user to interact with whatever we are displaying on the screen. In today’s article, we’ll learn how to draw individual pixels onto our window, and we’ll use mouse events to create a drawing program similar to a limited version of Microsoft Paint.

We’ll start off with some¬†code similar to that in¬†“Showing an Empty Window in SDL2“:

#include <SDL.h>

int main(int argc, char ** argv)
{
    bool quit = false;
    SDL_Event event;

    SDL_Init(SDL_INIT_VIDEO);

    SDL_Window * window = SDL_CreateWindow("SDL2 Pixel Drawing",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);

    while (!quit)
    {
        SDL_WaitEvent(&event);

        switch (event.type)
        {
            case SDL_QUIT:
                quit = true;
                break;
        }
    }

    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

Since we’re going to draw pixels directly rather than load an image, our course of action in this article is going to be a little different from past articles. First, we need a renderer, so we use¬†SDL_CreateRenderer()¬†as we have been doing all along:

    SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);

But then, rather than creating a texture from a surface, we’re now going to create one from scratch using¬†SDL_CreateTexture():

    SDL_Texture * texture = SDL_CreateTexture(renderer,
        SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, 640, 480);

We pass in several parameters. The first one is the renderer, and the last two are the width and height of the texture.

The second parameter, which we have set to¬†SDL_PIXELFORMAT_ARGB8888, is the pixel format. There are many possible formats (see¬†SDL_CreateTexture()¬†documentation), but in this case we’re saying that each pixel is a 32-bit value, where there are 8 bits for alpha (opacity/transparency), 8 bits for red, 8 bits for green and 8 bits for blue. These four items are collectively known as channels (e.g. the alpha channel), so each channel consists of one byte and it can range between 0 and 255. The arrangement below thus represents a single pixel:

Alpha Red Green Blue
8 bits 8 bits 8 bits 8 bits

The¬†SDL_TEXTUREACCESS_STATIC defines a method of accessing the texture. Since we’re storing our pixels in CPU memory and then copying them over to the GPU, static access is suitable. On the other hand, streaming¬†access is used to allocate pixels in a back buffer in video memory, and that’s suitable for more complex scenarios.

Finally, we initialise a set of pixels that we’ll be copying onto the window. When we draw stuff, we’ll modify these pixels, and then they’ll be copied onto the window to reflect the update.

    Uint32 * pixels = new Uint32[640 * 480];

We’ll need to clean up all the stuff we just allocated, so add the following just before the other cleanup calls at the end of the code:

    delete[] pixels;
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);

At the beginning of the while loop, we may now add the following call:

        SDL_UpdateTexture(texture, NULL, pixels, 640 * sizeof(Uint32));

In this code, we are using SDL_UpdateTexture() to copy our pixels onto the window. We pass it our texture, a region of the texture to update (in our case NULL means to update the entire texture), our array of pixels, and the size in bytes of a single row of pixels (called the pitch). In our case, our window has a width of 640 pixels. Therefore a single row consists of 640 4-byte pixels, hence the multiplication.

At the end of our while loop, we may now render our texture as we have done in previous articles:

        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, NULL, NULL);
        SDL_RenderPresent(renderer);

Great. So far, we have… uh… this:

sdl2-pixel-drawing-grey

Let’s clear the background to white, so that we can draw black pixels over it. We could do that using¬†SDL_SetRenderDrawColor()¬†as we did in “Handling Keyboard and Mouse Events in SDL2“. But since we can now manipulate pixels directly, let’s try something. First, add this at the top:

#include <iostream>

Now, we can use the standard function memset() to overwrite our pixels. Add this right after the declaration of pixels:

memset(pixels, 255, 640 * 480 * sizeof(Uint32));

Good, it’s white now:

sdl2-pixel-drawing-white

Now, let’s add some code to draw black pixels when the mouse is clicked. First, add the following flag at the beginning of main():

    bool leftMouseButtonDown = false;

Then, add this inside the switch statement:

        case SDL_MOUSEBUTTONUP:
            if (event.button.button == SDL_BUTTON_LEFT)
                leftMouseButtonDown = false;
            break;
        case SDL_MOUSEBUTTONDOWN:
            if (event.button.button == SDL_BUTTON_LEFT)
                leftMouseButtonDown = true;
        case SDL_MOUSEMOTION:
            if (leftMouseButtonDown)
            {
                int mouseX = event.motion.x;
                int mouseY = event.motion.y;
                pixels[mouseY * 640 + mouseX] = 0;
            }
            break;

Right, so we’re keeping track of whether the left mouse button is pressed. When it’s pressed (SDL_MOUSEBUTTONDOWN),¬†leftMouseButtonDown is set to true, and when it’s released (SDL_MOUSEBUTTONUP), leftMouseButtonDown is set to false.

If the mouse is moving (SDL_MOUSEMOTION) and the left mouse button is currently down, then the pixel at its location is set to black (or zero, which is the same thing). Note that I intentionally left out a break statement at the end of the SDL_MOUSEBUTTONDOWN case, so that the drawing code in SDL_MOUSEMOTION is executed along with its own.

So now we can draw our hearts out:

sdl2-pixel-drawing-final

Sweet! That was all we needed in order to draw pixels directly onto our window.

In this article, we learned how to draw pixels directly using SDL2. This involved first creating an SDL2 texture with a specified pixel format, then allocating an array to store those pixels, and finally copying the pixels from the array to the texture. We also used a variety of mouse events to allow the user to actually draw pixels onto the window with the mouse, which is like a simplified version of Microsoft Paint.

14 thoughts on “SDL2 Pixel Drawing”

  1. Note: You can draw directly to the renderer.
    if (leftMouseButtonDown)
    SDL_RenderDrawPoint(Renderer,event.motion.x,event.motion.y);

    You don’t need to set up a texture (array, memset, rendercopy).

  2. @Carsten Holtkamp
    Note that SDL_RenderDrawPoint is WAY slower. Filling a 1080p screen with pixels takes about 650ms to 635ms for me, while drawing to a texture then copying to renderer takes about 18ms to 6ms to refresh the entire screen, pixel by pixel.

    @Gigi
    Thanks so much for posting this! I’ve been searching for days trying to figure out how to draw to a texture (or any way to draw pixels quickly). Every example I’ve found uses very confusing methods of defining the pixel data, but this was very easy to follow, simple, and well explained! I had to figure out how to change some things because I am using C (like malloc instead of new), I am finally drawing pixels at a reasonable speed! Do you have a tutorial on texture streaming?

    1. Thanks for your kind words! I am not really sure what “texture streaming” actually means, and if it’s not among my list of SDL2 Tutorials, then no.

      However, I do remember that there’s an alternative to SDL_TEXTUREACCESS_STATIC which is SDL_TEXTUREACCESS_STREAMING, so you might want to look into that. See SDL_TextureAccess in the SDL docs.

      1. You are right. They even link to this tutorial from the libsdl website: http://slouken.blogspot.com/2011/02/streaming-textures-with-sdl-13.html

        But it doesn’t work for me. He’s not using pointers which causes C unknown size errors and then in the code example link at the bottom he uses completely different code with pointers, but then does not use a surface like on the tutorial page, but appears to be manipulated pixels on a void pointer, but doesn’t explain how. All very confusing! All of the tutorials I’ve found so far have similar issues. You can see why I was so glad to find your tutorial. I hope you make more!

        1. That blog post is using SDL 1.x, while mine uses the more recent SDL2. They are very different!

          By the way, if I’m not mistaken, slouken (the owner of that blog) is Sam Lantinga, the guy who wrote SDL in the first place.

  3. The link from libsdl to the blog post (https://wiki.libsdl.org/Tutorials under “Video”) says “Streaming textures with SDL 2.0”, but his actual blog says “1.3”. I assumed the blog was in error, or that it was still compatible 2.0 or they wouldn’t have linked it that way. It must just be a typo.

    I guess I can stop stressing over that post! Thanks again.

    Btw, the basic idea with texture streaming, is that you lock a texture for write-only access and somehow you can quickly make on-the-fly updates to the screen. Now that I can draw pixels thanks to your tutorial, my goal is to figure out if texture streaming is faster or slower than texture drawing.

    1. Interesting – I suppose that would give you benefits if you’re drawing e.g. a lot of sprites on the same texture, but probably not so much for just pixel drawing (it’s hard to beat just dumping pixels into a buffer and copying it to the texture in a single operation).

      Anyhow, let me know how it goes! ūüôā

  4. Looks like you are right again. I did get SDL_LockTexture working, but you apparently still have to copy the texture to the renderer and then present it. It is actually slower (about 17ms vs 6ms for SDL_UpdateTexture in my pixel plotting test). So I’m not sure what the point is. I could be missing something, but I see other people reporting UpdateTexture being faster as well.

    1. My guess (hey, I’m just a hobbyist) is that it will give you benefit when you want to write many times to the same texture. Every time you touch video memory, you are essentially beginning and ending a transaction (so to speak – but I’ve seen this concept in a number of graphics libraries). Doing this every time you copy something to the texture is expensive.

      But if you open this context/transaction/whatever once, copy everything to the texture as a batch, and then close it, it’s a lot more efficient.

      If you’re drawing pixels, you don’t need to do this, as you’re making a single update anyway. But if you have, say, a scene with a background image and 20 sprites, then you’ll get a decent speedup there.

      Again, I’m not a real game/graphics developer and this is just what I think will happen in theory. But I think it makes sense.

Leave a Reply

Your email address will not be published. Required fields are marked *