Can somebody explain masks to me?

Hi!

I got my Arduboy this week and have built a few small test apps (particle effects, bitmap rendering etc) and am currently working on a Bomberman clone using the Arduboy2 library. I’ll share my code when the tile engine is reasonably complete.

The thing that confuses me the most so far is masks. There are a bunch of masked drawing functions (drawSelfMasked, drawPlusMask etc), and most of the tools can generate masks in addition to pixel data from our png sources.

Pixel data + mask data takes up twice the memory so I assume there must be a very good reason to use it. However; even after reading the header-files I have no idea what the concept of “masking” means, or what the different masked drawing functions are for. I think it might have to do with transparency - are masked pixels transparent?

I’d be super grateful for any insights. Here’s some questions from the top of my mind:

When is a mask necessary to use?
Is drawing masked bitmapdata significantly slower?
What are the differences between the various masked drawing functions?
Is one function generally preferred (performance wise) over the others?

These forums have already been tremendously helpful, so thank you all for your time and effort!

EDIT for future visitors: Pharap provided a concise overview, and the Arduboy Magazine is a great resource! Specifically check out Page 33, Volume 7 for information about masks.

Bitmaps are square (edit: or rectangular). If you need to draw something that isn’t, you mask away the bits that aren’t supposed to be there. So, kinda like transparency, except either 1 or 0, no in-between.
Here’s an example, each hairstyle is a bitmap overlayed onto the face:
ArduboyRecording
Without the mask, you’d see a black, hairy square.

Edit:
The source files do a pretty thorough explanation, is this what you’ve been looking at? https://github.com/MLXXXp/Arduboy2/blob/master/src/Sprites.h

2 Likes

And the source files are easier to read when formatted using Doxygen
https://mlxxxp.github.io/documents/Arduino/libraries/Arduboy2/Doxygen/html/index.html

If you need a mask that isn’t derived from the image itself drawPlusMask() is preferred over drawExternalMask(). The reason to use drawExternalMask() is if you need different masks for the same image, or different images can share the same mask.

Yes, but I don’t find them clear at all. The bitmap and mask contains only bits (1 or 0) but the ASCII examples uses 0 and ., and I honestly can’t tell which symbol is meant to represent 1. This gets maddening in cases like drawErase, which claims to ignore 0 in bitmap data, but all examples show only 0… It also wouldn’t hurt to point out that the before and after ASCII represents the state of the screen buffer (I think?) before and after the draw call.

Back to the functionality of masks though. The text for, say, drawExternalMask says:

[…] Bits set to 0 in the mask will be left unchanged.

This sounds effectively like transparency - we are making the 0-masked parts of the bitmap see-through (to whatever was in the buffer previously, like a background tile).

Can I assume then, that bits set to 1 in the mask will let me overwrite whatever is in the screen buffer?

Put another way - is drawOverwrite practically the same as drawMasked where the mask is all 1’s?

In the examples 0 is 1 and . is 0.

Asuming you know about bitwise operators the process can be thought of as thus:

  1. The buffer is bitwise anded (&) with the mask.
  2. The buffer is bitwise ored (|) with the sprite.

Where 1 = white and 0 = black (unless the screen is inverted).

This is summed up best by Wikipedia’s example:

Regarding specific behaviours of the Sprites functions:

  • drawExternalMask uses a mask that is in a separate place to the main sprite. (This is intended to allow for mask re-use.)

  • drawPlusMask uses a format where the sprite data and mask data is interleaved. (One byte of sprite data followed by one byte of mask data, repeat ad nauseam.)

  • drawOverwrite behaves like a regular draw, and thus doesn’t use a mask at all. (This is equivalent to using a mask of all 1s.)

  • drawErase is a special case and possibly the least used function. Any bits that are 1 in the sprite will be set to 0 in the buffer, any bits that are 0 in the sprite won’t have any affect on the buffer.

  • drawSelfMask uses a sprite as its own mask, which means it’s roughly equivalent to using the sprite as both the sprite and the mask in drawExternalMask. (Roughly because drawExternalMask requires the sprite to have its dimensions specified but requires that the mask does not, hence the equivalent would actually be drawExternalMask(x, y, sprite, &(sprite[2]), frame, frame);.)


Is that clear enough?

2 Likes

Crystal clear! Thank you kindly. :slight_smile:

1 Like

Correct.

Correct again.

1 Like

For the next release of the Arduboy2 library, I’ve changed the characters used to represent the bits in the Sprites function example patterns:
- = 0
# = 1

I hope this makes things clearer.

1 Like

Is there a reason for not using 0 and 1 in the examples?

Because 0 and 1 makes it harder to see visually.

1 Like

You should probably add a legend now if we’re making this big a deal of this.

I did add a legend, and a note at the end of the documentation for the Sprites class.

The changes can be seen in the Arduboy2 master branch on GitHub (not released yet).

1 Like

Great job! Maybe link from the header file to Pharap’s answer too? It (and the others replies) contains a lot of helpful stuff!

1 Like

The documentation in the headers is meant to be a reference, not a tutorial. A simple internet search for “bitmap mask” would give the info.

True, but it might be worth at least mentioning that the mask is and-ed with the buffer and then the sprite is or-ed on top.

That’s probably more straight forward and potentially easier to understand than the diagram for anyone who knows how bitwise operations work (i.e. the kind of people who would be referring to the documentation rather than reading a tutorial).

(Granted someone could read the cpp to find that out, but having that mentioned in the brief (and thus the doxygen-generated documentation) would save time.)

Personally, I find the “and-ed then or-ed” explanation harder to understand than

Bits set to 1 in the mask indicate that the pixel will be set to the value of the corresponding image bit. Bits set to 0 in the mask will be left unchanged.

@ulfben seemed to understand the and-or explanation better, and I must admit that’s the explanation that has worked for me in the past (which is why I presented that explanation in the first place).

Totally. And if I might add something, a problem with this line:

… is that it assumes familiarity with the screen buffer and draw order. It does not actually explain what is “left unchanged”. Pharhap’s bitwise explanation was explicit about source data and destination, which helps a lot.

That said: bitwise operations will be beyond most beginning programmers, which is why the added image example from Wikipedia is so great. It demonstrates concretely what a binary mask accomplishes.

I would also assume many of us come from other platforms and might have very different experience of what “mask” means. Personally I come from Flash / Air, GameMaker and plain SDL (whose closest equivalent would be SDL_SetColorKey). So a brief explanation of what masks mean to Arduboy2 is probably not out of place, even in the reference.

To me, right now, a mask allows me to “cut holes” in my bitmaps. :slight_smile:

1 Like

That’s a very valid point, upon re-reading it I can see how it’s a bit ambiguous.


Hrm, I agree that bitwise operations might be beyond an absolute beginner (though I’d hope they’d be reasonably early in a beginner’s “to learn” list given how significant they are, especially for optimising on less powerful hardware), but MLXXXp is right in that the library documentation isn’t aimed at beginners, it’s aimed at minimum towards people who already know how to program and are just looking for library specifics.

The wikipedia could be linked as a ‘see also’ I guess.
(I must admit I wish more documentation would include diagrams, though that’s not always practical.)

Perhaps to cover all bases someone ought to make a tutorial for the forum (assuming there isn’t already one in the magazine), to break it down for beginners.
(If I can’t find one I might contemplate making one myself when I’ve got less concurrent projects.)

It’s a bit of a strange way of thinking about it, but a somewhat accurate analogy nonetheless.

Bitmasks for images (and technically even for general purposes) are analogous to the use of paper and masking tape when painting things.

You could also think of them as stencils.


Sidenote: I too have used SDL2. It’s a nice library.