Corruption when drawing more than 3 different bitmaps [Solved]

So I just recently got this device, and I have to say I absolutely love it. That said, I’m still learning about how to develop for it.

I’m trying to build a simple tile-based game, which of course involves a sprite for each tile. During preliminary testing I wanted to draw all the current tiles. There weren’t many, so I just drew them one at a time.

For the record, I used @Dreamer3’s wonderful image converter here, and so my bitmaps are currently defined like so, notably with the first two bytes being width and height:

// bm_sheet_0.png
// 16x16
PROGMEM const byte bm_sheet_0[] = {
    // width, height
    16, 16,
    0xE0, 0xF8, 0x1C, ...
};

and I’m using a custom method to draw them that simply calls arduboy.drawBitmap() with some pre-defined parameters:

void drawBitmap(int x, int y, const byte* bitmap)
{
    arduboy.drawBitmap(x, y, &bitmap[2], bitmap[0], bitmap[1], WHITE);
}

the problem is when I actually go to draw them. That custom function seems fine, because it works for any of the bitmaps individually; and I know that the actual data of the bitmaps isn’t corrupted either for the same reason. I can call that function any number of times on the same bitmap, but as soon as I try to draw four different bitmaps in the same frame, none of them draw correctly, and appear horribly corrupted:

{
    //this works:
    drawBitmap(0 * 16, 0, bm_sheet_0);
    drawBitmap(1 * 16, 0, bm_sheet_1);
    drawBitmap(2 * 16, 0, bm_sheet_2);
    //drawBitmap(3 * 16, 0, bm_sheet_3);
}

{
    //this draws garbage, or sometimes nothing:
    drawBitmap(0 * 16, 0, bm_sheet_0);
    drawBitmap(1 * 16, 0, bm_sheet_1);
    drawBitmap(2 * 16, 0, bm_sheet_2);
    drawBitmap(3 * 16, 0, bm_sheet_3);
}

I don’t have any idea what’s going on, except that somehow the wrong values of width and height are being passed to arduboy.drawBitmap(); since all the sprites are 16x16, when I simply replace the width and height in the custom drawBitmap with 16’s everything seems to work fine. I don’t understand how that’s possible though, because the first two bytes are always 16’s, and I have no idea why 4 seems to be the magic number that destroys everything.

Also, I can edit this post and attach pictures if it could help.

Thanks!

1 Like

When you have something marked PROGMEM you have to use some special macros to read from them.

void drawBitmap(int x, int y, const byte* bitmap)
{
    arduboy.drawBitmap(x, y, &bitmap[2], bitmap[0], bitmap[1], WHITE);
}

In this case, bitmap[0] and bitmap[1] are in PROGMEM so this is probably turning up garbage in some cases.

To fix that, wrap them in the appropriate macros:

void drawBitmap(int x, int y, const byte* bitmap)
{
    arduboy.drawBitmap(x, y, &bitmap[2], read_pgm_byte(&bitmap[0]), read_pgm_byte(&bitmap[1]), WHITE);
}

If you haven’t already, you might want to read a bit more into PROGMEM and its associated functions.

Also consider using functions from the Sprites class (which you don’t need an instance of).
In particular drawSelfMasked will behave the same as what you’re currently using, but without you having to read the width and height (the function does that for you). E.g.

void drawBitmap(int x, int y, const byte* bitmap)
{
    Sprites::drawSelfMasked(x, y, bitmap, 0);
}

(The 0 is the frame index, which you don’t have to worry about at the moment.)

1 Like

Ah, thank you very much. I had no idea that accessing program memory was special in any way.

Also, as for the masks, could you explain what they actually are? I actually disabled the image converter from generating them, because I thought I didn’t need them, but I realize that until I actually know what they are I shouldn’t assume that.

EDIT: Nevermind, I should have read the Sprite documentation before I asked. Thank you!

1 Like

Welcome to the world of ‘embedded systems’ ¯\_(ツ)_/¯

I would have been happy to provide an explanation, but as you’ve got it under control, that saves me some typing :P

Out of interest though, ‘masking’ is a common technique for managing transparency on less powerful systems, so it’s reasonably well documented on the internet too if you get curious. (Wikipedia has a nice little section.)

You’re welcome, we’re all here to help.
(The ones who aren’t here just to play games that is :P)

1 Like

More like “welcome to the world of the Harvard Architecture”, which isn’t necessarily restricted to embedded systems, and likewise, not all embedded systems use it.

If a next generation Arduboy is ever produced, it will likely be ARM based, which doesn’t have the PROGMEM difficulties.