Header-only CPU graphics library for C99 or C++, with a focus on ease-of-use.
#define PNTR_PIXELFORMAT_RGBA
#define PNTR_IMPLEMENTATION
#include "pntr.h"
int main() {
pntr_image* image = pntr_new_image(200, 200);
pntr_draw_circle_fill(image, 100, 100, 80, PNTR_RED);
pntr_save_image(image, "output.png");
pntr_unload_image(image);
return 0;
}- Draw rectangles, circles, ellipses, triangles, lines, arcs, etc
- RGBA or ARGB pixel bit order
- Clipping
- Load and save images (PNG, BMP, and JPEG with
PNTR_ENABLE_JPEG) - Draw sprites with scaling and rotation
- Alpha-blending, alpha masks, invert, brightness, etc
- Font rendering (TTF, BMF, TTY)
- UTF-8
- Documentation
| Name | Description |
|---|---|
| pntr_app | Application wrapper for running the same pntr code in raylib, SDL, Web, libretro, or in a CLI |
| pntr_assetsys | Load pntr assets from .zip files with assetsys.h |
| pntr_physfs | Load pntr assets from .zip files with PhysicsFS |
| pntr_nuklear | Nuklear immediate-mode graphical user interface for pntr |
| pntr_aseprite | Use Aseprite animated sprites in pntr |
| pntr_tiled | Display Tiled 2D level editor maps in pntr |
| pntr_portablegl | Use the OpenGL-esque software rendering library, PortableGL, in pntr |
| pntr_doom | Play DOOM via PureDOOM rendered through pntr |
| pntr_pixelfont | Additional pixel fonts outside of the default |
| zig_pntr | Zig wrapper for pntr |
In order to use pntr, define PNTR_IMPLEMENTATION in one of your .c files before including pntr.h.
#define PNTR_IMPLEMENTATION
#include "pntr.h"pntr.h reports its own version, so you can display it or compile against a specific release:
printf("pntr %s\n", PNTR_VERSION); // "0.1.0"
#if PNTR_VERSION_MAJOR > 0 || (PNTR_VERSION_MAJOR == 0 && PNTR_VERSION_MINOR >= 1)
pntr_color color = pntr_image_get_color_bilinear(image, 1.5f, 1.5f);
#endif| Define | Description |
|---|---|
PNTR_VERSION |
The version as a "MAJOR.MINOR.PATCH" string |
PNTR_VERSION_MAJOR |
The major version number |
PNTR_VERSION_MINOR |
The minor version number |
PNTR_VERSION_PATCH |
The patch version number |
You can modify how pntr functions based on the following defines:
| Define | Description |
|---|---|
PNTR_IMPLEMENTATION |
Define this in one of your .c or .cpp files before including pntr.h |
PNTR_PIXELFORMAT_RGBA |
Use the RGBA format |
PNTR_PIXELFORMAT_ARGB |
Use the ARGB pixel format |
PNTR_ENABLE_DEFAULT_FONT |
Enables the default 8x8 pixel font |
PNTR_ENABLE_JPEG |
Enables JPEG image loading, when the active image backend supports it |
PNTR_ENABLE_MATH |
Enables use of C's standard math.h linked library, rather than using the built in math functions |
PNTR_ENABLE_TTF |
Enables support for loading TrueType fonts |
PNTR_ENABLE_UTF8 |
Enables UTF-8 support for font loading and text rendering |
PNTR_ENABLE_VARGS |
Adds support for functions that require variadic arguments, like pntr_draw_text_ex() |
PNTR_ERROR_STORAGE |
The storage class that holds the last error reported by pntr_set_error(). Defaults to static, which every thread shares. Define it as a thread-local storage class, like static _Thread_local, to give each thread an error of its own |
PNTR_LOAD_FILE |
Callback to use when asked to load a file in pntr_load_file(). By default, will use stdio.h. |
PNTR_LOAD_IMAGE_FROM_MEMORY |
Callback to use when loading an image from memory via pntr_load_image_from_memory(). By default, will use stb_image |
PNTR_SAVE_FILE |
Callback to use when saving a file via pntr_save_file(). By default, uses stdio.h |
PNTR_SAVE_IMAGE_TO_MEMORY |
Callback to use when saving an image to memory via pntr_save_image_to_memory(). By default, will use stb_image_write |
PNTR_NO_ALPHABLEND |
Skips alpha blending when drawing pixels |
PNTR_NO_BMP |
Drops BMP image loading, saving about 6.5kb. Saving BMP is unaffected |
PNTR_NO_STDIO |
Will disable the standard file loading/saving calls for PNTR_LOAD_FILE and PNTR_SAVE_FILE |
PNTR_NO_SAVE_IMAGE |
Disables the default behavior of image saving |
PNTR_NO_LOAD_IMAGE |
Disables the default behavior of image loading |
PNTR_NO_CUTE_PNG_IMPLEMENTATION |
Skips defining CUTE_PNG_IMPLEMENTATION. Useful if you're using cute_png elsewhere |
PNTR_NO_STB_TRUETYPE_IMPLEMENTATION |
Skips defining STB_TRUETYPE_IMPLEMENTATION. Useful if you're using stb_truetype elsewhere |
PNTR_NO_STB_IMAGE_WRITE_IMPLEMENTATION |
Skips defining STB_IMAGE_WRITE_IMPLEMENTATION. useful if you're using stb_image_write elsewhere |
PNTR_NO_STB_IMAGE_IMPLEMENTATION |
Skips defining STB_IMAGE_IMPLEMENTATION. useful if you're using stb_image_write elsewhere |
pntr_image* pntr_new_image(int width, int height);
pntr_image* pntr_gen_image_color(int width, int height, pntr_color color);
pntr_image* pntr_gen_image_gradient(int width, int height, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight);
pntr_image* pntr_image_copy(pntr_image* image);
pntr_image* pntr_image_from_image(pntr_image* image, int x, int y, int width, int height);
pntr_image* pntr_image_subimage(pntr_image* image, int x, int y, int width, int height);
void pntr_unload_image(pntr_image* image);
pntr_image* pntr_load_image(const char* fileName);
pntr_image* pntr_load_image_from_memory(pntr_image_type type, const unsigned char* fileData, unsigned int dataSize);
pntr_image* pntr_image_from_pixelformat(const void* data, int width, int height, pntr_pixelformat pixelFormat);
bool pntr_save_image(pntr_image* image, const char* fileName);
unsigned char* pntr_save_image_to_memory(pntr_image* image, pntr_image_type type, unsigned int* dataSize);
void* pntr_image_to_pixelformat(pntr_image* image, unsigned int* dataSize, pntr_pixelformat pixelFormat);
int pntr_get_pixel_data_size(int width, int height, pntr_pixelformat pixelFormat);
pntr_image_type pntr_get_file_image_type(const char* filePath);void pntr_clear_background(pntr_image* image, pntr_color color);
void pntr_draw_point(pntr_image* dst, int x, int y, pntr_color color);
void pntr_draw_point_vec(pntr_image* dst, pntr_vector* point, pntr_color color);
void pntr_draw_points(pntr_image* dst, pntr_vector* points, int pointsCount, pntr_color color);
void pntr_draw_line(pntr_image* dst, int startPosX, int startPosY, int endPosX, int endPosY, pntr_color color);
void pntr_draw_line_vec(pntr_image* dst, pntr_vector start, pntr_vector end, pntr_color color);
void pntr_draw_line_aa(pntr_image* dst, int startPosX, int startPosY, int endPosX, int endPosY, pntr_color color);
void pntr_draw_line_thick(pntr_image* dst, int startPosX, int startPosY, int endPosX, int endPosY, int thickness, pntr_color color);
void pntr_draw_line_thick_vec(pntr_image* dst, pntr_vector start, pntr_vector end, int thickness, pntr_color color);
void pntr_draw_line_horizontal(pntr_image* dst, int posX, int posY, int width, pntr_color color);
void pntr_draw_line_horizontal_thick(pntr_image* dst, int posX, int posY, int width, int thickness, pntr_color color);
void pntr_draw_line_vertical(pntr_image* dst, int posX, int posY, int height, pntr_color color);
void pntr_draw_line_vertical_thick(pntr_image* dst, int posX, int posY, int height, int thickness, pntr_color color);
void pntr_draw_line_curve(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_vector point4, int segments, pntr_color color);
void pntr_draw_line_curve_thick(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_vector point4, int segments, int thickness, pntr_color color);
void pntr_draw_rectangle(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color);
void pntr_draw_rectangle_rec(pntr_image* dst, pntr_rectangle rec, pntr_color color);
void pntr_draw_rectangle_fill(pntr_image* dst, int posX, int posY, int width, int height, pntr_color color);
void pntr_draw_rectangle_fill_rec(pntr_image* dst, pntr_rectangle rect, pntr_color color);
void pntr_draw_rectangle_thick(pntr_image* dst, int posX, int posY, int width, int height, int thickness, pntr_color color);
void pntr_draw_rectangle_thick_rec(pntr_image* dst, pntr_rectangle rect, int thickness, pntr_color color);
void pntr_draw_rectangle_gradient(pntr_image* dst, int x, int y, int width, int height, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight);
void pntr_draw_rectangle_gradient_rec(pntr_image* dst, pntr_rectangle rect, pntr_color topLeft, pntr_color topRight, pntr_color bottomLeft, pntr_color bottomRight);
void pntr_draw_rectangle_rounded(pntr_image* dst, int x, int y, int width, int height, int topLeftRadius, int topRightRadius, int bottomLeftRadius, int bottomRightRadius, pntr_color color);
void pntr_draw_rectangle_rounded_fill(pntr_image* dst, int x, int y, int width, int height, int cornerRadius, pntr_color color);
void pntr_draw_rectangle_thick_rounded(pntr_image* dst, int x, int y, int width, int height, int topLeftRadius, int topRightRadius, int bottomLeftRadius, int bottomRightRadius, int thickness, pntr_color color);
void pntr_draw_circle(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color);
void pntr_draw_circle_fill(pntr_image* dst, int centerX, int centerY, int radius, pntr_color color);
void pntr_draw_circle_thick(pntr_image* dst, int centerX, int centerY, int radius, int thickness, pntr_color color);
void pntr_draw_ellipse(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color);
void pntr_draw_ellipse_fill(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, pntr_color color);
void pntr_draw_ellipse_thick(pntr_image* dst, int centerX, int centerY, int radiusX, int radiusY, int thickness, pntr_color color);
void pntr_draw_triangle(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color);
void pntr_draw_triangle_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color);
void pntr_draw_triangle_fill(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, pntr_color color);
void pntr_draw_triangle_fill_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, pntr_color color);
void pntr_draw_triangle_thick(pntr_image* dst, int x1, int y1, int x2, int y2, int x3, int y3, int thickness, pntr_color color);
void pntr_draw_triangle_thick_vec(pntr_image* dst, pntr_vector point1, pntr_vector point2, pntr_vector point3, int thickness, pntr_color color);
void pntr_draw_arc(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color);
void pntr_draw_arc_fill(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, pntr_color color);
void pntr_draw_arc_thick(pntr_image* dst, int centerX, int centerY, float radius, float startAngle, float endAngle, int segments, int thickness, pntr_color color);
void pntr_draw_polygon(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
void pntr_draw_polygon_fill(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
void pntr_draw_polygon_thick(pntr_image* dst, pntr_vector* points, int numPoints, int thickness, pntr_color color);
void pntr_draw_polyline(pntr_image* dst, pntr_vector* points, int numPoints, pntr_color color);
void pntr_draw_polyline_thick(pntr_image* dst, pntr_vector* points, int numPoints, int thickness, pntr_color color);
void pntr_draw_image(pntr_image* dst, pntr_image* src, int posX, int posY);
void pntr_draw_image_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY);
void pntr_draw_image_tint(pntr_image* dst, pntr_image* src, int posX, int posY, pntr_color tint);
void pntr_draw_image_tint_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, pntr_color tint);
void pntr_draw_image_rotated(pntr_image* dst, pntr_image* src, int posX, int posY, float degrees, float offsetX, float offsetY, pntr_filter filter);
void pntr_draw_image_rotated_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, float degrees, float offsetX, float offsetY, pntr_filter filter);
void pntr_draw_image_flipped(pntr_image* dst, pntr_image* src, int posX, int posY, bool flipHorizontal, bool flipVertical, bool flipDiagonal);
void pntr_draw_image_flipped_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRec, int posX, int posY, bool flipHorizontal, bool flipVertical, bool flipDiagonal);
void pntr_draw_image_scaled(pntr_image* dst, pntr_image* src, int posX, int posY, float scaleX, float scaleY, float offsetX, float offsetY, pntr_filter filter, pntr_color tint);
void pntr_draw_image_scaled_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, float scaleX, float scaleY, float offsetX, float offsetY, pntr_filter filter, pntr_color tint);
void pntr_draw_image_dest_rec(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, pntr_rectangle dstRect, pntr_filter filter, pntr_color tint);
void pntr_draw_image_rotozoom(pntr_image* dst, pntr_image* src, pntr_rectangle srcRect, int posX, int posY, float rotation, float scaleX, float scaleY, float originX, float originY, pntr_filter filter, pntr_color tint);
void pntr_draw_text(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, pntr_color tint);
void pntr_draw_text_len(pntr_image* dst, pntr_font* font, const char* text, int textLength, int posX, int posY, pntr_color tint);
void pntr_draw_text_aligned(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, pntr_text_align align, pntr_color tint);
void pntr_draw_text_wrapped(pntr_image* dst, pntr_font* font, const char* text, int posX, int posY, int maxWidth, pntr_color tint);
void pntr_draw_text_ex(pntr_image* dst, pntr_font* font, int posX, int posY, pntr_color tint, int maxlen, const char* text, ...);
void pntr_draw_point_unsafe(pntr_image* dst, int x, int y, pntr_color color);
void pntr_put_horizontal_line_unsafe(pntr_image* dst, int posX, int posY, int width, pntr_color color);The _unsafe functions perform no bounds or clipping checks, so they will write outside the image if given coordinates that are out of range. Only use them when you have already confirmed the coordinates are within the image's clip region.
Whether a shape covers its far edge depends on how you describe the shape, not on whether it is an outline or a fill:
- Shapes described by their points cover every point you pass.
pntr_draw_line(image, 0, 0, 10, 0, color)paints 11 pixels, fromx = 0throughx = 10, including the degenerate case of a line whose two endpoints are the same pixel. The same holds forpntr_draw_line_aa(),pntr_draw_line_thick(),pntr_draw_line_curve(),pntr_draw_polyline(),pntr_draw_polygon()andpntr_draw_triangle(). It also holds for the matching fills, sopntr_draw_polygon_fill()andpntr_draw_triangle_fill()cover the whole bounding box of the points they are given, and therefore line up with the outline drawn from the same points. A radius behaves as a point too:pntr_draw_circle_fill(image, 50, 50, 10, color)is 21 pixels across and 21 pixels tall, and covers every pixel of the outline thatpntr_draw_circle()draws for the same center and radius.pntr_draw_ellipse_fill()andpntr_draw_ellipse()register the same way. That includes the degenerate radii: a radius of one is three pixels across, a radius of zero is the center point on its own, and an ellipse with a radius of zero on one axis is the straight line through its center. - Shapes described by an extent stop short of the far edge.
pntr_draw_rectangle(image, x, y, width, height, color)paints columnsxthroughx + width - 1and rowsythroughy + height - 1, sowidthandheightare a count of pixels rather than a second coordinate.pntr_draw_rectangle_fill(),pntr_draw_rectangle_rounded(),pntr_draw_line_horizontal(),pntr_draw_line_vertical(), and the clip region ofpntr_image_set_clip()all read their sizes the same way.
A rounded rectangle therefore occupies exactly the same pixels as a plain rectangle of the same x, y, width and height, and a thick line is the same length as the thin line through the same two endpoints.
pntr_color pntr_new_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
pntr_color pntr_get_color(unsigned int hexValue);
unsigned char pntr_color_r(pntr_color color);
unsigned char pntr_color_g(pntr_color color);
unsigned char pntr_color_b(pntr_color color);
unsigned char pntr_color_a(pntr_color color);
void pntr_color_set_r(pntr_color* color, unsigned char r);
void pntr_color_set_g(pntr_color* color, unsigned char g);
void pntr_color_set_b(pntr_color* color, unsigned char b);
void pntr_color_set_a(pntr_color* color, unsigned char a);
pntr_color pntr_image_get_color(pntr_image* image, int x, int y);
pntr_color pntr_image_get_color_bilinear(pntr_image* image, float x, float y);
pntr_color pntr_color_tint(pntr_color color, pntr_color tint);
void pntr_image_color_tint(pntr_image* image, pntr_color color);
pntr_color pntr_color_fade(pntr_color color, float alpha);
void pntr_image_color_fade(pntr_image* image, float alpha);
pntr_color pntr_color_brightness(pntr_color color, float factor);
void pntr_image_color_brightness(pntr_image* image, float factor);
pntr_color pntr_color_contrast(pntr_color color, float contrast);
void pntr_image_color_contrast(pntr_image* image, float contrast);
pntr_color pntr_color_invert(pntr_color color);
void pntr_image_color_invert(pntr_image* image);
pntr_color pntr_color_grayscale(pntr_color color);
void pntr_image_color_grayscale(pntr_image* image);
void pntr_image_color_replace(pntr_image* image, pntr_color color, pntr_color replace);
pntr_color pntr_color_alpha_blend(pntr_color dst, pntr_color src);
pntr_color pntr_color_bilinear_interpolate(pntr_color color00, pntr_color color01, pntr_color color10, pntr_color color11, float coordinateX, float coordinateY);
pntr_color pntr_get_pixel_color(void* srcPtr, pntr_pixelformat srcPixelFormat);
void pntr_set_pixel_color(void* dstPtr, pntr_pixelformat dstPixelFormat, pntr_color color);pntr_font* pntr_load_font_default(void);
pntr_font* pntr_load_font_bmf(const char* fileName, const char* characters);
pntr_font* pntr_load_font_bmf_from_image(pntr_image* image, const char* characters);
pntr_font* pntr_load_font_bmf_from_memory(const unsigned char* fileData, unsigned int dataSize, const char* characters);
pntr_font* pntr_load_font_tty(const char* fileName, int glyphWidth, int glyphHeight, const char* characters);
pntr_font* pntr_load_font_tty_from_image(pntr_image* image, int glyphWidth, int glyphHeight, const char* characters);
pntr_font* pntr_load_font_tty_from_memory(const unsigned char* fileData, unsigned int dataSize, int glyphWidth, int glyphHeight, const char* characters);
pntr_font* pntr_load_font_ttf(const char* fileName, int fontSize);
pntr_font* pntr_load_font_ttf_from_memory(const unsigned char* fileData, unsigned int dataSize, int fontSize);
pntr_font* pntr_font_copy(pntr_font* font);
pntr_font* pntr_font_scale(pntr_font* font, float scaleX, float scaleY, pntr_filter filter);
void pntr_unload_font(pntr_font* font);
int pntr_measure_text(pntr_font* font, const char* text);
pntr_vector pntr_measure_text_ex(pntr_font* font, const char* text, int textLength);
pntr_image* pntr_gen_image_text(pntr_font* font, const char* text, pntr_color tint, pntr_color backgroundColor);pntr_image* pntr_image_resize(pntr_image* image, int newWidth, int newHeight, pntr_filter filter);
pntr_image* pntr_image_scale(pntr_image* image, float scaleX, float scaleY, pntr_filter filter);
bool pntr_image_resize_canvas(pntr_image* image, int newWidth, int newHeight, int offsetX, int offsetY, pntr_color fill);
pntr_image* pntr_image_rotate(pntr_image* image, float degrees, pntr_filter filter);
void pntr_image_flip(pntr_image* image, bool horizontal, bool vertical);
bool pntr_image_crop(pntr_image* image, int x, int y, int width, int height);
void pntr_image_alpha_crop(pntr_image* image, float threshold);
pntr_rectangle pntr_image_alpha_border(pntr_image* image, float threshold);
void pntr_image_alpha_mask(pntr_image* image, pntr_image* alphaMask, int posX, int posY);
pntr_rectangle pntr_image_get_clip(pntr_image* image);
void pntr_image_set_clip(pntr_image* image, int x, int y, int width, int height);
void pntr_image_set_clip_rec(pntr_image* image, pntr_rectangle clip);
void pntr_image_reset_clip(pntr_image* image);unsigned char* pntr_load_file(const char *fileName, unsigned int *bytesRead);
void pntr_unload_file(unsigned char* fileData);
const char* pntr_load_file_text(const char *fileName);
void pntr_unload_file_text(const char* text);
bool pntr_save_file(const char *fileName, const void *data, unsigned int bytesToWrite);void* pntr_set_error(pntr_error error);
const char* pntr_get_error(void);
pntr_error pntr_get_error_code(void);void* pntr_load_memory(size_t size);
void pntr_unload_memory(void* pointer);
void* pntr_memory_copy(void* destination, void* source, size_t size);To build the tests and examples, use CMake:
# Set up the build files.
cmake -B build
# Build the tests and examples.
cmake --build build
# Run the tests
ctest --test-dir build -VTo build the documentation, use Doxygen...
doxygen .DoxyfileTo publish the documentation, use Node.js...
npm run docs- Logo by Ravenist, used with permission
- cute_png.h by Randy Gaul
- font8x8 by Daniel Hepper provides the 8x8 monochrome font
- stb_image.h by Sean Barrett
- stb_truetype.h by Sean Barrett
- utf8.h by Neil Henning
- tester from zpl provides the unit testing framework
- raylib inspired some of the design patterns
Unless stated otherwise, all works are:
- Copyright (c) 2026 Rob Loach
... and licensed under:



