All formats

SDL

LanguageC (usable from C++)
Licensezlib license
Repositorygithub.com/libsdl-org/SDL
DocumentationSDL_LoadBMP, SDL_SaveBMP (SDL3 wiki)
Depends onnothing for BMP (SDL itself)
Read / writeyes, 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#

Shell
# Debian / Ubuntu (recent releases)
sudo apt install libsdl3-dev
# macOS
brew install sdl3
# Windows (vcpkg)
vcpkg install sdl3

With CMake:

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:

C
#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#

C
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:

C
// 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#

C
// 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#