Skip to content

Support saving 1- and 4-bit MSB indexed surfaces as BMP - #16211

Open
madebr wants to merge 3 commits into
libsdl-org:mainfrom
madebr:bmp-1-4-bit-indexed-formats
Open

Support saving 1- and 4-bit MSB indexed surfaces as BMP#16211
madebr wants to merge 3 commits into
libsdl-org:mainfrom
madebr:bmp-1-4-bit-indexed-formats

Conversation

@madebr

@madebr madebr commented Aug 28, 2026

Copy link
Copy Markdown
Contributor
  • I confirm that I am the author of this code and release it to the SDL project under the Zlib license. This contribution does not contain code from other sources, including code generated by a Large Language Model ("AI").

Description

  • Only 1-bit and 4-bit MSB indexed surfaces are supported.
    1-bit LSB surfaces would have to be bit flipped, and 4-bit surfaces nibble swapped.
  • While writing tests, I noticed SDL_ReadSurfacePixel does not support 1-bit, 2-bit or 4-bit indexed surfaces, so I fixed that as well.

Here's a little app showing what it generates:

#include <SDL3/SDL.h>

static void write_bmp_1bit(void)
{
    SDL_Surface *surface = SDL_CreateSurface(128, 128, SDL_PIXELFORMAT_INDEX1MSB);
    SDL_Palette *pal = SDL_CreateSurfacePalette(surface);

    const SDL_Color colors[2] = {
        {0xff, 0xff, 0xff, 0xff, },
        {0x00, 0x00, 0x00, 0xff, },
    };

    SDL_SetPaletteColors(pal, colors, 0, 2);

    for (int y = 0; y < surface->h; ++y) {
        Uint8 *row = (Uint8 *)surface->pixels + y * surface->pitch;
        for (int x = 0; x < surface->w; x += 8) {
            Uint8 p0 = (Uint8)((((x+0) + y) & 0xf) >= 8);
            Uint8 p1 = (Uint8)((((x+1) + y) & 0xf) >= 8);
            Uint8 p2 = (Uint8)((((x+2) + y) & 0xf) >= 8);
            Uint8 p3 = (Uint8)((((x+3) + y) & 0xf) >= 8);
            Uint8 p4 = (Uint8)((((x+4) + y) & 0xf) >= 8);
            Uint8 p5 = (Uint8)((((x+5) + y) & 0xf) >= 8);
            Uint8 p6 = (Uint8)((((x+6) + y) & 0xf) >= 8);
            Uint8 p7 = (Uint8)((((x+7) + y) & 0xf) >= 8);
            row[x / 8] = (p0 << 7) | (p1 << 6) | (p2 << 5) | (p3 << 4) | (p4 << 3) | (p5 << 2) | (p6 << 1) | (p7 << 0);
        }
    }

    const char *filename = "pattern_1bit.bmp";
    if (SDL_SaveBMP(surface, filename)) {
        SDL_Log("Wrote %s", filename);
    } else {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_SaveBMP failed (%s)", SDL_GetError());
    }
    SDL_DestroySurface(surface);
}
static void write_bmp_4bit(void)
{
    SDL_Surface *surface = SDL_CreateSurface(128, 128, SDL_PIXELFORMAT_INDEX4MSB);
    SDL_Palette *pal = SDL_CreateSurfacePalette(surface);

    const SDL_Color colors[16] = {
        {0xff, 0xff, 0xff, 0xff, },
        {0xee, 0xee, 0xee, 0xff, },
        {0xdd, 0xdd, 0xdd, 0xff, },
        {0xcc, 0xcc, 0xcc, 0xff, },
        {0xbb, 0xbb, 0xbb, 0xff, },
        {0xaa, 0xaa, 0xaa, 0xff, },
        {0x99, 0x99, 0x99, 0xff, },
        {0x88, 0x88, 0x88, 0xff, },
        {0x77, 0x77, 0x77, 0xff, },
        {0x66, 0x66, 0x66, 0xff, },
        {0x55, 0x55, 0x55, 0xff, },
        {0x44, 0x44, 0x44, 0xff, },
        {0x33, 0x33, 0x33, 0xff, },
        {0x22, 0x22, 0x22, 0xff, },
        {0x11, 0x11, 0x11, 0xff, },
        {0x00, 0x00, 0x00, 0xff, },
    };

    SDL_SetPaletteColors(pal, colors, 0, 16);

    for (int y = 0; y < surface->h; ++y) {
        Uint8 *row = (Uint8 *)surface->pixels + y * surface->pitch;
        for (int x = 0; x < surface->w; x += 2) {
            Uint8 p0 = (Uint8)(((x + 0) + y) & 0xf);
            Uint8 p1 = (Uint8)(((x + 1) + y) & 0xf);
            row[x / 2] = (p0 << 4) | (p1 << 0);
        }
    }

    const char *filename = "pattern_4bit.bmp";
    if (SDL_SaveBMP(surface, filename)) {
        SDL_Log("Wrote %s", filename);
    } else {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_SaveBMP failed (%s)", SDL_GetError());
    }
    SDL_DestroySurface(surface);
}

int main(void)
{
    SDL_Init(0);

    write_bmp_1bit();
    write_bmp_4bit();

    SDL_Quit();
    return 0;
}

1-bit BMP:
pattern_1bit.bmp

4-bit BMP:
pattern_4bit.bmp

Existing Issue(s)

@icculus

icculus commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Oh man, I literally ran into this today, good timing!!

@madebr
madebr marked this pull request as draft September 2, 2026 19:08
@madebr
madebr force-pushed the bmp-1-4-bit-indexed-formats branch from d3162a0 to b370865 Compare September 2, 2026 19:42
@madebr
madebr marked this pull request as ready for review September 2, 2026 19:42
@madebr

madebr commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

I pushed a fix for widths that are a non-multiple of 2 (for 4MSB) and 8 (for 1MSB).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants