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);
playdate->file->close(out);