C macros for working with Playdate bitmap data

Going to share this here as I've seen a few people ask in various threads, and honestly I've had to implement this a few times.

The bitmap data of an image or the display's frame buffer is encoded as 1 pixel per bit, in 32 bit values. It can be a bit tricky to work with and basically puts every bitwise operator to work.

Here are the ways you can get the data from an image:

LCDBitmap* bitmap = ...;
uint8_t* bitmap_data = NULL;
int bitmap_rowbytes = 0;
playdate->graphics->getBitmapData(bitmap, NULL, NULL, &bitmap_rowbytes, NULL, &bitmap_data);

Or use the display's frame buffer:

uint8_t* bitmap_data = playdate->graphics->getFrame();
int display_rowbytes = LCD_ROWSIZE

Now here are the macros:

// Determine pixel at x, y is black or white.
#define samplepixel(data, x, y, rowbytes) (((data[(y)*rowbytes+(x)/8] & (1 << (uint8_t)(7 - ((x) % 8)))) != 0) ? kColorWhite : kColorBlack)

// Set the pixel at x, y to black.
#define setpixel(data, x, y, rowbytes) (data[(y)*rowbytes+(x)/8] &= ~(1 << (uint8_t)(7 - ((x) % 8))))

// Set the pixel at x, y to white.
#define clearpixel(data, x, y, rowbytes) (data[(y)*rowbytes+(x)/8] |= (1 << (uint8_t)(7 - ((x) % 8))))

// Set the pixel at x, y to the specified color.
#define drawpixel(data, x, y, rowbytes, color) (((color) == kColorBlack) ? setpixel((data), (x), (y), (rowbytes)) : clearpixel((data), (x), (y), (rowbytes)))

That's it, just pass the appropriate bitmap_data and bitmap_rowbytes into these macros with the x, y you want to modify.

Keep in mind that these macros do not do bounds checking, so make sure the x, y is within bounds of the bitmap before using. :slight_smile:

4 Likes

I get itchy when I see integer multiplications and divisions by powers of 2. I don't know if LLVM converts those to bitwise operations automatically, but I've heard plenty of stories of people getting bitten by assuming the compiler would and then the compiler didn't.

The following:

(data[(y)*rowbytes+(x)/8] & (1 << (uint8_t)(7 - ((x) % 8))))

can be replaced with:

(data[(y)*rowbytes+((x)>>3)] & (1 << (uint8_t)(7 - ((x) & 7))))

If it compiles with the actual division operations, it will be a lot slower.

I’ve experimented with this a bit in Gamekid and haven’t found a speed up using bitwise ops vs division on power of 2. I think the compiler knows what to do, especially with a relatively basic optimization. But! I haven’t inspected the output assembly so I don’t know for sure.

I tend to lean towards readable code vs optimized unless we find it actually improves speed. Not that this code is super readable anyway. :sweat_smile:

1 Like