Glusoft

Drawing with a Graphic Tablet Using SDL3

Drawing with a tablet

In this tutorial th goal is to be able to use a graphic tablet to draw something on the screen.

Initializations

The windows and the renderer

SDL_Init(SDL_INIT_VIDEO);

SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;

if (!SDL_CreateWindowAndRenderer("SDL3 Pen Down", 800, 600, 0, &window, &renderer)) {
    SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
    return SDL_APP_FAILURE;
}

The render texture

We need a render texture to be able to draw on it with the graphic tablet

SDL_Texture *render_target = NULL;
int w, h;

SDL_GetRenderOutputSize(renderer, &w, &h);
render_target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
if (!render_target) {
    SDL_Log("Couldn't create render target: %s", SDL_GetError());
    return SDL_APP_FAILURE;
}

The default screen

We want to make the screen white if nothing is drawn.

SDL_SetRenderTarget(renderer, render_target);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
SDL_SetRenderTarget(renderer, NULL);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

Some important informations

float pressure = 0.0f;

float previous_touch_x = -1.0f;
float previous_touch_y = -1.0f;

float tilt_x = 0.0f;
float tilt_y = 0.0f;

bool running = true; // if the event loop is running

The event loop

In this part we will see how to handle the graphics

Check if the tablet is working

Before processing the motion of the tablet we will first check if everything is working by displaying some debug informations.

while (running) {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        switch (event.type) {
            case SDL_EVENT_QUIT:
                running = false;
                break;

            case SDL_EVENT_PEN_DOWN:
                printf("Pen down detected!\n");
                printf("Pen ID: %" SDL_PRIu32 "\n", event.ptouch.which);
                printf("Window ID: %" SDL_PRIu32 "\n", event.ptouch.windowID);
                printf("Position: (%f, %f)\n", event.ptouch.x, event.ptouch.y);
                break;
        }
    }
}

The motion event: SDL_EVENT_PEN_MOTION

Now it's time to use the motion event to handle the drawing of the line only if the pressure is set.

case SDL_EVENT_PEN_MOTION:
    if (pressure > 0.0f) {
        if (previous_touch_x >= 0.0f) {
            SDL_SetRenderTarget(renderer, render_target);
            SDL_SetRenderDrawColorFloat(renderer, 0, 0, 0, pressure);
            SDL_RenderLine(renderer, previous_touch_x, previous_touch_y, event.pmotion.x, event.pmotion.y);
            
        }
        previous_touch_x = event.pmotion.x;
        previous_touch_y = event.pmotion.y;
    } else {
        previous_touch_x = previous_touch_y = -1.0f;
    }
    break;

The axis event: SDL_EVENT_PEN_AXIS

In this event we set the pressure and record the tilt informations.

case SDL_EVENT_PEN_AXIS:
    if (event.paxis.axis == SDL_PEN_AXIS_PRESSURE) {
        pressure = event.paxis.value;
        printf("Pressure: %f\n", pressure);
    } else if(event.paxis.axis == SDL_PEN_AXIS_XTILT) {
        tilt_x = event.paxis.value;
    } else if(event.paxis.axis == SDL_PEN_AXIS_YTILT) {
        tilt_y = event.paxis.value;
    }
    break;

The rendering

char debug_text[1024];

// Clear the screen
SDL_SetRenderTarget(renderer, NULL);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);

// Render the render texture
SDL_RenderTexture(renderer, render_target, NULL, NULL);

// Display the tilt informations
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_snprintf(debug_text, sizeof(debug_text), "Tilt: %f %f", tilt_x, tilt_y);
SDL_RenderDebugText(renderer, 0, 8, debug_text);
SDL_RenderPresent(renderer);

Download the full project : Drawing with a tablet

Need another OS ? => Windows, Mac, Linux