A GPU-accelerated painting application built with SDL2, featuring layers, drawing tools, and JavaScript scripting support.
https://ozkl.github.io/qpaint/
- GPU-Accelerated Rendering - Drawing happens on GPU
- Layer System - Support for up to 8 independent layers with opacity and visibility controls
- Advanced Brush Engine - Configurable brush size, feathering, and blend modes
- Color Management - Dual color system (foreground/background) with alpha channel support
- Brush Tool - Smooth, interpolated brush strokes with customizable size and feathering
- Pen Tool - Pixel-perfect hard-edged drawing
- Eraser Tool - Remove pixels with adjustable strength
- Fill Tool - Flood fill with tolerance settings
- Line Tool - Draw straight lines with multiple styles (solid, dashed, dotted)
- Rectangle Tool - Outline and filled rectangles with rounded corners
- Ellipse Tool - Perfect circles and ellipses
- Gradient Tool - Linear and radial gradients with multiple interpolation modes
- Text Tool - Render text with custom fonts and alignment
- Selection Tools - Rectangle, polygon, and magic wand selections
- Eyedropper - Sample colors from the canvas
- Able to load .ttf font files (just load and use)
- Scripting System
- JavaScript scripting powered by QuickJS
- Programmatically draw shapes and patterns
- Access canvas dimensions and layer information
- Query and manipulate selections
- Built-in API documentation
- Undo/Redo - Full history with configurable depth
- Clipboard Support - Copy/paste with browser clipboard integration
- File Formats - PNG, BMP, JPEG, TGA, and custom QPIF (multi-layer) format
- Zoom & Pan - Navigate large canvases with ease
- Grid & Rulers - Precision alignment tools
- Web Build - Runs in browser via WebAssembly
Native Build (macOS/Linux):
- SDL2
- SDL2_ttf
- C compiler (gcc/clang)
Web Build (Emscripten):
- Emscripten SDK
- SDL2 (provided by Emscripten)
# Native build
make
# Run native build
make run
# WebAssembly build
make web
# Serve web build locally (http://localhost:8000)
make serve
# Clean build artifacts
make clean
# Build without scripting support
make ENABLE_SCRIPTING=0# Install dependencies via Homebrew
brew install sdl2 sdl2_ttf
# Build
make
# Run
./qpaint# Debian/Ubuntu
sudo apt-get install libsdl2-dev libsdl2-ttf-dev
# Fedora
sudo dnf install SDL2-devel SDL2_ttf-devel
# Build and run
make run- Left Click - Draw/Use active tool
- Right Click - Tool-specific secondary action (e.g., secondary color)
- Middle Mouse Button - Pan canvas (drag to move view)
- Mouse Wheel - Zoom in/out (when over canvas)
Quick Paint includes a JavaScript scripting engine for procedural drawing and automation.
// Checkerboard Pattern
let cellSize = 32;
let width = getCanvasWidth();
let height = getCanvasHeight();
let white = new Color(255, 255, 255);
let black = new Color(0, 0, 0);
for (let y = 0; y < height; y += cellSize) {
for (let x = 0; x < width; x += cellSize) {
let cellX = Math.floor(x / cellSize);
let cellY = Math.floor(y / cellSize);
let color = ((cellX + cellY) % 2 === 0) ? white : black;
fillRect(x, y, cellSize, cellSize, color);
}
}
print('Checkerboard created!');Utility Functions:
print(message)- Output to script console
Constructors:
Color(r, g, b, a=255)- Create RGBA colorPoint(x, y)- Create 2D point
Drawing Functions:
setPixel(x, y, color)- Set single pixeldrawLine(x1, y1, x2, y2, color, thickness)- Draw linedrawRect(x, y, w, h, color, thickness)- Draw rectangle outlinefillRect(x, y, w, h, color)- Fill rectangle
Canvas Queries:
getCanvasWidth()- Canvas width in pixelsgetCanvasHeight()- Canvas height in pixels
Layer Functions:
getLayerCount()- Number of layersgetActiveLayerIndex()- Current layer indexsetActiveLayerIndex(index)- Switch active layer
Selection Functions:
isInSelection(x, y)- Check if pixel is selectedgetSelectionCenter()- Get selection center point
Tool System - Uses function pointers for polymorphic behavior:
typedef struct Tool {
void (*on_mouse_down)(Tool *tool, App *A, Point canvas, Point screen, bool right);
void (*on_mouse_drag)(Tool *tool, App *A, Point from, Point to, ...);
void (*on_mouse_up)(Tool *tool, App *A, Point canvas, Point screen, bool right);
} Tool;GPU-First Rendering - All drawing uses SDL2 render targets:
- Each layer is an independent texture
- No CPU→GPU uploads during drawing
- Fast compositing with hardware acceleration
Coordinate Systems:
- Canvas coordinates: Fixed logical drawing space
- Screen coordinates: Transformed by zoom/pan
screen_to_canvas()converts between them
- SDL2 - Cross-platform multimedia library
- Nuklear - Immediate-mode GUI library
- QuickJS - Lightweight JavaScript engine
- stb_image - Image loading library
- stb_image_write - Image writing library