How to Debug Draw using the C API?

,

I was trying to draw debug stuff in the simulator via the C API, but I noticed that in the documentation there's only this function that is somewhat related to debug drawing:

playdate->graphics->getDebugBitmap(void);

But how to use it?
Does it have any purpose on its own?
I tried to draw on it using pushContext but that seems to cause an error.

Many things seem to still be missing in the C API compared to Lua, is debug drawing one of them?

C API is severely lacking in documentation department.

The only way I managed to get C API to draw anything on the debug bitmap is by manipulating pixels directly. There can be a way to draw stuff using more high-level API functions, but I did not research this angle, since my game does not use any.

To draw pixels directly, you need to get the raw debug bitmap first:

uint8_t* debug_bitmap;
playdate->graphics->getBitmapData(pd->graphics->getDebugBitmap(), NULL, NULL, NULL, NULL, &debug_bitmap);

Directly clearing the debug bitmap messes up the drawing context for Lua. If you do not use Lua, you can just do pd->graphics->clearBitmap(pd->graphics->getDebugBitmap(), kColorBlack); to clear the bitmap. My game does use both C and Lua, so keeping contexts separate is essential. If you game uses this as well, you can do memset(debug_bitmap, 0, 240 * 52); to clear the bitmap.

Finally, you can draw some pixels on the debug bitmap by setting appropriate bits in the bitmap array. From C API documentation:

Rows are 32-bit aligned, so the row stride is 52 bytes, with the extra 2 bytes per row ignored. Bytes are MSB-ordered; i.e., the pixel in column 0 is the 0x80 bit of the first byte of the row.

It took me a good 30 minutes to unpack this phrase and debug the code for it. Basically, you can use this function to set a pixel in the bitmap:

void set_pixel(uint8_t debug_bitmap, int x, int y) {
	uint8_t* bitmap_row = (uint8_t*)&debug_bitmap[y * 52];
        bitmap_row[x / 8] |= 1 << (7 - (x % 8));
}

I think this sets the pixel to "on" value, but I do not remember. If it does not work the way you want, replace the last line with bitmap_row[x / 8] &= ~(1 << (7 - (x % 8)));.

4 Likes

What is "debug stuff"? How is the debug bitmap different from any other bitmap?
What makes it debuggy ?
Is it painted on top or suchlike?
:thinking:

Debug bitmap is supported only in the simulator and it draws red instead of black/white.

1 Like

Yeah, in Lua you can even choose the color of the overlay.

debugDraw documentation

1 Like

Any updates on this? I’m debugging some collision code right now and this would make things a lot easier :+1:

I took @laplab's hacky bits and created a couple ugly functions so you can debug draw rectangles without too much pain.

typedef uint8_t u8;
typedef int32_t i32;
void draw_vert_line(u8* debug_bitmap, i32 x, i32 y, i32 extent) {
    if (extent <= 0 || x >= 400 || x < 0 || y >= 240 || y + extent < 0)
        return;
    if (y + extent >= 240)
        extent = 240 - y;
    if (y < 0) {
        extent += y;
        y = 0;
    }
    for (int i = 0; i < extent; i++) {
        u8* bitmap_row = (u8*)debug_bitmap + ((y + i) * 52);
        bitmap_row[x / 8] |= 1 << (7 - (x % 8));
    }
}

void draw_horiz_line(u8* debug_bitmap, i32 x, i32 y, i32 extent) {
    if (extent <= 0 || y >= 240 || y < 0 || x >= 400 || x + extent < 0)
        return;
    if (x + extent >= 400)
        extent = 400 - x;
    if (x < 0) {
        extent += x;
        x = 0;
    }
    u8* bitmap_row = (u8*)debug_bitmap + (y * 52);
    for (int i = 0; i < extent; i++) {
        bitmap_row[(x + i) / 8] |= 1 << (7 - ((x + i) % 8));
    }
}

void draw_rect(u8* debug_bitmap, i32 x, i32 y, i32 width, i32 height) {
    draw_horiz_line(debug_bitmap, x, y, width);
    draw_horiz_line(debug_bitmap, x, y + height - 1, width);
    draw_vert_line(debug_bitmap, x, y, height);
    draw_vert_line(debug_bitmap, x + width - 1, y, height);
}

static void debug_draw(PlaydateAPI* pd) {
    u8* debug_bitmap;
    LCDBitmap* simulator = pd->graphics->getDebugBitmap();
    if (simulator) {
        pd->graphics->getBitmapData(simulator, NULL, NULL, NULL, NULL, &debug_bitmap);
        draw_rect(debug_bitmap, 50, 50, 200, 100);
    }
}

pacman

Full gist: playdate-debug-draw-c · GitHub

1 Like

Have you tried calling gfx->pushContext(gfx->getDebugBitmap()) then using the system drawing functions?

1 Like

I figured it out. The debug bitmap draws in kColorWhite not kColorBlack. So this works:.

pd->graphics->drawRect(x, y, x , width, height kColorWhite);

I feel silly, but it was a fun adventure in implementing bitwise drawing primitives.

Perhaps the docs should note getDebugBitmap() returns an inverted LCDBitmap and thus all drawing should be done in kColorWhite

Thanks @dave.

1 Like

Great call. In the Lua docs we say White pixels are drawn in the debugDrawColor. Black pixels are transparent but there's no mention in the C docs. I'll fix that now and get it in the queue. Thanks!

1 Like

Is it expected that getDebugBitmap clears anything previously drawn on the debug bitmap?

I'm betting what happened there is while I was implementing that my test grabbed the debug bitmap once at the beginning and stored it in a global, and when I ran it there was garbage data so I added the clear call to the function. It didn't cross my mind that someone might call the function more than once. Or more than once per frame, maybe? I'm not sure if I'd expect data in the debug bitmap to persist across update cycles.. It's possible that people are currently calling this once per frame and relying on the clearing behavior, so we can't change that--which means only clearing it on the first call each frame? This is getting kind of hairy..

I'll file this, though the "fix" will probably wind up being calling out the behavior in the docs instead of changing the code.

1 Like

Personally, I'd expect the debug bitmap to either be cleared on each frame, or persist until cleared explicitly. I understand if a different choice made sense at the time though!

My reasoning for having a function that I can call many times in a frame is, that'd allow me to add debug draws to my rendering logic which is now decomposed into many files. I suppose I could store the bitmap and have a new API for accessing it, and clearing it between frames, though.

Any chance this could be implemented with new functions? That way existing behavior wouldn't be affected.

1 Like

I'm wondering if keeping the current behavior is really necessary.
This is functionality that can only be used in the simulator and only (in theory) to draw debug stuff, if I'm not mistaken.

It should not have any effects on users, and developers already relying on it will have at max some easily fixable overdraw.

2 Likes