SDL
| Language | C (usable from C++) |
|---|---|
| License | zlib license |
| Repository | github.com/libsdl-org/SDL |
| Documentation | SDL_LoadBMP, SDL_SaveBMP (SDL3 wiki) |
| Depends on | nothing for BMP (SDL itself) |
| Read / write | yes, 1-32 bit, RLE4, RLE8, bit fields / yes, 8-bit palette, 24-bit, 32-bit with alpha |
SDL is a cross-platform library for windows, input, audio and graphics, used by many games and emulators. BMP is the one image format built into SDL itself, with no extra library: SDL_LoadBMP returns an SDL_Surface and SDL_SaveBMP writes one. For PNG, JPEG and other formats SDL uses the separate SDL_image library.
The loader handles palette images (1, 4 and 8-bit), RLE4 and RLE8 compression, 16 and 32-bit bit fields, and V4/V5 headers. The writer saves 8-bit palette, 24-bit and 32-bit surfaces directly and converts other formats to 24-bit, or to 32-bit with a V5 header and alpha mask when the surface has alpha. The examples use SDL3; in SDL2 the functions have the same names but return int and use SDL_FreeSurface.
Installation#
# Debian / Ubuntu (recent releases)
sudo apt install libsdl3-dev
# macOS
brew install sdl3
# Windows (vcpkg)
vcpkg install sdl3
With CMake:
find_package(SDL3 REQUIRED CONFIG)
target_link_libraries(myapp PRIVATE SDL3::SDL3)
Basic usage: load, change and save a BMP#
The surface functions don’t need SDL_Init:
#include <SDL3/SDL.h>
#include <stdio.h>
int main(void) {
SDL_Surface *loaded = SDL_LoadBMP("in.bmp");
if (!loaded) {
fprintf(stderr, "load failed: %s\n", SDL_GetError());
return 1;
}
printf("%d x %d, %s\n", loaded->w, loaded->h, SDL_GetPixelFormatName(loaded->format));
// convert to 4 bytes per pixel in R, G, B, A byte order
SDL_Surface *rgba = SDL_ConvertSurface(loaded, SDL_PIXELFORMAT_RGBA32);
SDL_DestroySurface(loaded);
if (!rgba) {
fprintf(stderr, "convert failed: %s\n", SDL_GetError());
return 1;
}
// invert colors; rows are pitch bytes apart
for (int y = 0; y < rgba->h; y++) {
Uint8 *row = (Uint8 *)rgba->pixels + y * rgba->pitch;
for (int x = 0; x < rgba->w * 4; x += 4) {
row[x] = 255 - row[x];
row[x + 1] = 255 - row[x + 1];
row[x + 2] = 255 - row[x + 2];
}
}
if (!SDL_SaveBMP(rgba, "out.bmp")) {
fprintf(stderr, "save failed: %s\n", SDL_GetError());
}
SDL_DestroySurface(rgba);
return 0;
}
How to read one pixel#
Uint8 r, g, b, a;
if (SDL_ReadSurfacePixel(surface, 10, 20, &r, &g, &b, &a)) {
printf("pixel at (10, 20): %d %d %d %d\n", r, g, b, a);
}
SDL_ReadSurfacePixel works with any pixel format, including palette surfaces.
How to save a pixel buffer as BMP#
Wrap your buffer in a surface without copying it:
// pixels: width * height * 3 bytes, R, G, B order, top row first
SDL_Surface *s = SDL_CreateSurfaceFrom(width, height, SDL_PIXELFORMAT_RGB24, pixels, width * 3);
if (s) {
SDL_SaveBMP(s, "out.bmp"); // converted to a 24-bit BMP
SDL_DestroySurface(s);
}
How to load a BMP from memory#
// data/size: BMP file contents, e.g. embedded in the executable
SDL_IOStream *io = SDL_IOFromConstMem(data, size);
SDL_Surface *surface = SDL_LoadBMP_IO(io, true); // true: close the stream when done
Tips#
- Set the hint
SDL_HINT_BMP_SAVE_LEGACY_FORMATto"1"to save 32-bit surfaces with a 40-byte header, for old programs that don’t read V5 headers. - To show a BMP in a window, create a texture with
SDL_CreateTextureFromSurface(renderer, surface). - For other formats, SDL_image’s
IMG_Loadreturns the sameSDL_Surface.