LCDBitmap to standard image format (BMP, PNG...)?

I am looking to record a large screenshot of my game world, which is way too big to fit on-screen.

I had wanted to do this by stepping the camera over the world in-game, calling LCDBitmap* playdate->graphics->getDisplayFrameBitmap(void); , draw this into a larger bitmap, move the camera, repeat...

I would then have populated a large LCDBitmap with pixels, I guess I could open a file handle at this point and stream this out...

But are there any PC tools which could help to convert this to any regular image format, BMP for example?


1 Like

If you are using Windows or don't mind opening the resulting bitmap with Gimp, you can use the function below.

It is writing the given LCDBitmap as a BMP file using an old Windows BMP format.

I am using an old version of the BMP format because it is way easier/shorter to use but if you want to go with a newer version, the full spec is available here.

typedef struct {
    char magic[2];
    int32_t fileSize;
    int32_t reserved;
    int32_t contentPosition;
} BitmapFileHeader;

typedef struct {
    int32_t headerLength;
    int16_t width;
    int16_t height;
    int16_t planes;
    int16_t bitsPerPixel;
} BitmapCoreHeader;

void SaveLCDBitmapToFile(SDFile * _Nonnull file, LCDBitmap * _Nonnull bitmap) {
    const int32_t fileHeaderLength = 14;
    const int32_t coreHeaderLength = 12;
    const int32_t bitsInAByte = 8;
    const int32_t bytesPerColors = 3;

    int width, height, rowBytes;
    uint8_t *data;
    playdate->graphics->getBitmapData(bitmap, &width, &height, &rowBytes, NULL, &data);

    BitmapFileHeader fileHeader = (BitmapFileHeader) {
        .magic = {'B', 'M'},
        .fileSize = fileHeaderLength + coreHeaderLength + width * height * bytesPerColors,
        .contentPosition = fileHeaderLength + coreHeaderLength
    BitmapCoreHeader coreHeader = (BitmapCoreHeader) {
        .headerLength = coreHeaderLength,
        .width = width,
        .height = height,
        .planes = 1,
        .bitsPerPixel = bitsInAByte * bytesPerColors
    playdate->file->write(file, &fileHeader.magic, sizeof(char[2]));
    playdate->file->write(file, &fileHeader.fileSize, sizeof(int32_t));
    playdate->file->write(file, &fileHeader.reserved, sizeof(int32_t));
    playdate->file->write(file, &fileHeader.contentPosition, sizeof(int32_t));
    playdate->file->write(file, &coreHeader.headerLength, sizeof(int32_t));
    playdate->file->write(file, &coreHeader.width, sizeof(int16_t));
    playdate->file->write(file, &coreHeader.height, sizeof(int16_t));
    playdate->file->write(file, &coreHeader.planes, sizeof(int16_t));
    playdate->file->write(file, &coreHeader.bitsPerPixel, sizeof(int16_t));

    const int32_t count = width * height;
    for (int32_t index = 0; index < count; index++) {
        const int32_t x = index % width;
        const int32_t y = height - (index / width) - 1;

        const int32_t byteIndex = x / bitsInAByte + y * rowBytes;
        const int bitIndex = (1 << (bitsInAByte - 1)) >> (x % bitsInAByte);

        const uint8_t color = data[byteIndex] & bitIndex ? 0xFF : 0x00;
        playdate->file->write(file, &color, sizeof(uint8_t)); // Red
        playdate->file->write(file, &color, sizeof(uint8_t)); // Green
        playdate->file->write(file, &color, sizeof(uint8_t)); // Blue

Usage example:

LCDBitmap *displayBuffer = playdate->graphics->getDisplayBufferBitmap();
SDFile *out = playdate->file->open("out.bmp", kFileWrite);
SaveLCDBitmapToFile(out, displayBuffer);
1 Like

@Daeke that is truly perfect! Thanks!

I will try it out later.

I tried my code this morning with macOS and it works with Preview too.

This a simplified version of a function I wrote to create BMP with premultiplied alpha transparency. Alpha transparency is not supported by Preview so I thought the result of this function would not work with macOS as well but I was wrong. It works :playdate_sideways_smile:


Just to cross post here too - this worked perfectly! And I found that Ubuntu & Windows's default image previews both were fine with this simple format.

It was much more work to properly compose the smaller individual grabs of the frame buffer into the larger canvas in a sensible way which worked with my chunk system. But I think it's all up and running OK now.

Result: In Development: Factory Farming - #14 by timboe

1 Like