Variable for array

Hi all :slightly_smiling_face:
Tell me what variable should be used to store the sprite array?
For example :

const unsigned char PROGMEM playerOne[] =
{
// width, height,
    2, 3,
// FRAME 00
0x07, 0x07, 
0x02, 0x02,
};

selectedPlayer = playerOne;
Sprites :: drawPlusMask (X, Y, selectedPlayer , 0);

What variable should selectedPlayer be?
Sprite is displayed crooked when selecting byte or int variable

Try:

uint8_t const *selectedPlayer = nullptr;
selectedPlayer = playerOne;
Sprites::drawPlusMask (X, Y, selectedPlayer , 0);
1 Like

The height of your sprite is not valid.

As per the Arduboy2 Library documentation:

The height must be a multiple of 8 pixels, but with proper masking, a sprite of any height can be created.

For a 2 x 3 pixel sprite, you need to make the height 8 and then have the mask make the unused 5 bottom pixel rows transparent.

2 Likes

@filmote @MLXXXp

guys, thanks a lot! :heart:

I think it is … its self masked.

No, you always have to declare a height that’s a multiple of 8.

Really? I never new that and have always been using whatever height the graphic is (and comes out of the Image Converter). If you are masking properly, what is the impact of not specifying a multiple of 8? It seems to work properly.

Obviously for drawOverwrite() it takes the ‘unspecified’ bits as black.

The code uses the specified height (and width) to determine the total number of bytes in a sprite to calculate the offset to a given frame.

It might just happen to work with invalid heights because of how the calculations are done. (I haven’t looked.) It’s possible that it might work for one function but not another, or work for Sprites but not SpritesB.

Also, because it’s a documented restriction, even if it works now there’s no guarantee that it wouldn’t break if the library code were refactored in the future, or ported to a different architecture.

Has that ever resulted in a sprite array that didn’t give a height that’s a multiple of 8? And, has that sprite array been more than 1 frame?

Ah that makes sense. However I think it works properly anyhow,

The TeamARG sprite converter always gives the actual height of the sprite, not a multiple of 8.

The sprite below works fine when using Sprites::drawPlusMask() - its 25 high, has the mask embedded and has two frames. I have not tested it with SpritesB though.

const uint8_t PROGMEM Player[] = {
43, 25,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xa0, 0xf0, 0x50, 0xf8, 0xa8, 0xfc, 0xa8, 0xfc, 0x88, 0xfc, 0xb0, 0xf8, 0x88, 0xfc, 0xb4, 0xfe, 0xba, 0xff, 0xba, 0xff, 0x92, 0xff, 0xaa, 0xff, 0x94, 0xfe, 0x88, 0xfc, 0xb8, 0xfc, 0xa8, 0xfc, 0xa8, 0xfc, 0x88, 0xfc, 0xa8, 0xfc, 0x88, 0xfc, 0xa8, 0xfc, 0xa8, 0xfc, 0xa8, 0xfc, 0xa8, 0xfc, 0x98, 0xfc, 0xa8, 0xfc, 0xb4, 0xfe, 0xb4, 0xfe, 0x94, 0xfe, 0xa8, 0xfc, 0x98, 0xfc, 0xa8, 0xfc, 0xa8, 0xfc, 0x50, 0xf8, 0xa0, 0xf0, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0xf0, 0xf0, 0xfc, 0x0c, 0xff, 0x13, 0xff, 0xc8, 0xff, 0x26, 0xff, 0x15, 0xff, 0x56, 0xff, 0x92, 0xff, 0x52, 0xff, 0x92, 0xff, 0x12, 0xff, 0x12, 0xff, 0x12, 0xff, 0x12, 0xff, 0x16, 0xff, 0x10, 0xff, 0x1f, 0xff, 0x10, 0xff, 0xd2, 0xff, 0x52, 0xff, 0xd2, 0xff, 0x52, 0xff, 0xd2, 0xff, 0x10, 0xff, 0x1f, 0xff, 0x10, 0xff, 0x16, 0xff, 0x12, 0xff, 0x12, 0xff, 0x12, 0xff, 0x12, 0xff, 0x92, 0xff, 0x52, 0xff, 0x92, 0xff, 0x56, 0xff, 0x15, 0xff, 0x26, 0xff, 0xcc, 0xff, 0x13, 0xff, 0x0c, 0xff, 0xf0, 0xfc, 0x00, 0xf0, 
0x00, 0x00, 0x00, 0x7f, 0x7f, 0xff, 0x80, 0xff, 0xa0, 0xff, 0xa1, 0xff, 0xa2, 0xff, 0xa2, 0xff, 0x8a, 0xff, 0x8a, 0xff, 0x6a, 0xff, 0x2a, 0x7f, 0x2a, 0x7f, 0x2a, 0x7f, 0x2a, 0x7f, 0x2a, 0x7f, 0x52, 0xff, 0x52, 0xff, 0x5a, 0xff, 0x52, 0xff, 0x52, 0xff, 0x5a, 0xff, 0x52, 0xff, 0x52, 0xff, 0x5a, 0xff, 0x52, 0xff, 0x52, 0xff, 0x6a, 0xff, 0x2a, 0x7f, 0x2a, 0x7f, 0x2a, 0x7f, 0x2a, 0x7f, 0x6a, 0xff, 0x8a, 0xff, 0xaa, 0xff, 0xa2, 0xff, 0xa2, 0xff, 0xa1, 0xff, 0xa0, 0xff, 0x80, 0xff, 0x7f, 0xff, 0x00, 0x7f, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xa0, 0xf0, 0x50, 0xf8, 0xa8, 0xfc, 0xa8, 0xfc, 0x88, 0xfc, 0xb0, 0xf8, 0x88, 0xfc, 0xb4, 0xfe, 0xba, 0xff, 0xba, 0xff, 0x92, 0xff, 0xaa, 0xff, 0x94, 0xfe, 0x88, 0xfc, 0xb8, 0xfc, 0xa8, 0xfc, 0xa8, 0xfc, 0x88, 0xfc, 0xa8, 0xfc, 0x88, 0xfc, 0xa8, 0xfc, 0xa8, 0xfc, 0xa8, 0xfc, 0xa8, 0xfc, 0x98, 0xfc, 0xa8, 0xfc, 0xb4, 0xfe, 0xb4, 0xfe, 0x94, 0xfe, 0xa8, 0xfc, 0x98, 0xfc, 0xa8, 0xfc, 0xa8, 0xfc, 0x50, 0xf8, 0xa0, 0xf0, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0xf0, 0xf0, 0xfc, 0x0c, 0xff, 0x13, 0xff, 0xc8, 0xff, 0x26, 0xff, 0x15, 0xff, 0x56, 0xff, 0x92, 0xff, 0x52, 0xff, 0x92, 0xff, 0x12, 0xff, 0x12, 0xff, 0x12, 0xff, 0x12, 0xff, 0x16, 0xff, 0x10, 0xff, 0x1f, 0xff, 0x10, 0xff, 0xd2, 0xff, 0x52, 0xff, 0xd2, 0xff, 0x52, 0xff, 0xd2, 0xff, 0x10, 0xff, 0x1f, 0xff, 0x10, 0xff, 0x16, 0xff, 0x12, 0xff, 0x12, 0xff, 0x12, 0xff, 0x12, 0xff, 0x92, 0xff, 0x52, 0xff, 0x92, 0xff, 0x56, 0xff, 0x15, 0xff, 0x26, 0xff, 0xcc, 0xff, 0x13, 0xff, 0x0c, 0xff, 0xf0, 0xfc, 0x00, 0xf0, 
0x00, 0x00, 0x00, 0x7f, 0x7f, 0xff, 0x80, 0xff, 0x94, 0xff, 0x91, 0xff, 0x92, 0xff, 0x82, 0xff, 0x8a, 0xff, 0x8a, 0xff, 0x6a, 0xff, 0x2a, 0x7f, 0x2a, 0x7f, 0x2a, 0x7f, 0x2a, 0x7f, 0x2a, 0x7f, 0x52, 0xff, 0x52, 0xff, 0x5a, 0xff, 0x52, 0xff, 0x52, 0xff, 0x5a, 0xff, 0x52, 0xff, 0x52, 0xff, 0x5a, 0xff, 0x52, 0xff, 0x52, 0xff, 0x6a, 0xff, 0x2a, 0x7f, 0x2a, 0x7f, 0x2a, 0x7f, 0x2a, 0x7f, 0x6a, 0xff, 0x8a, 0xff, 0x8a, 0xff, 0x82, 0xff, 0x92, 0xff, 0x91, 0xff, 0x90, 0xff, 0x80, 0xff, 0x7f, 0xff, 0x00, 0x7f, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
};

I definitely hit some unexpected blitting problems when I tried using 6 pixel high sprites last year and had to pad to 8 pixels.

Right?

Were they masked?

Looking back at my git history, I think they were compressed bitmaps, so the issue ended up being with the decompression when the height wasn’t a multiple of 8.

Oh ok … I have t used compressed images for a long while.

In Sprites :: drawPlusMask () I have always used sprites that are not divisible by 8, and some of them had more than one frame. Everything worked flawlessly. From my observation, only drawOverwrite () requires an 8x image. We should try to test SpriteB :slight_smile:

Again, regardless of whether it works or not, the library documentation specifies that heights must be a multiple of 8. Therefore, refactored or reimplementations of the library function could end up breaking your program.

Restricting heights to multiples of 8 gives the possibility for smaller library code size and faster speed. Sprites are encoded with heights being a multiple of bytes, so only allowing heights to be specified as a multiple of 8 means the library wouldn’t (possibly) need to include additional code to round up to the nearest byte when calculating frame offsets. It’s better to do the work at compile time.

I took a quick look at the Sprites and SpritesB code and found that the frame offset is calculated as follows:

frame_offset = (width * ( height / 8 + ( height % 8 == 0 ? 0 : 1)));

So there is currently extra code ( height % 8 == 0 ? 0 : 1) to handle heights that aren’t divisible by 8. Since the AVR architecture doesn’t have a barrel shifter, that height % 8 could be costly, let alone the test operation. Given the “multiple of 8” restriction, this could be simplified to:

frame_offset = width * (height / 8));

This would reduce code size and increase execution speed (slightly). Since it’s in keeping with the documented API specification, there’s no reason this change wouldn’t be made in a future release of the library.


In retrospect, it would have been even better to specify that the height be given in bytes. This would make the code even simpler:

frame_offset = width * height;

It would also make the multiple of bytes restriction self enforcing.

If you needed the height in bits, you could do height * 8. Needing to multiply for bytes to bits might be better than needing to do a divide for bits to bytes (although in this case both multiply and divide can be done by shifting).

Hindsight is 2020 :stuck_out_tongue_winking_eye:

2 Likes

Whoa! Thanks for the detailed clarification. Still, I think for my level the usual use of sprites is more reasonable. The correct use of sprites is more suitable for high-level programming and large-scale games, such as adventure or a section of the meta category. I am ready for the fact that at some point my game will break or slow down. This is a fair deal😁

For the example you gave in the original post, just change the height value to 8 and all should be well.

Change:

// width, height,
    2, 3,

to:

// width, height,
    2, 8,
1 Like