[A5] Masked drawing?
Chris Katko

Are there any functions in Allegro 5 to do bitmasked drawing?

Ala the ground in this picture:


You'd use the alpha channel.

Chris Katko

So you're saying use the alpha channel to denote whether or not there is land at that point?

That's neat and it might work, but that doesn't really allow things like rolling textures or multiple textures. I think I'm looking for more of a "draw X bitmap given Y mask against the screen."

I guess I could use some sort of Stencil Buffer... though I can't remember if that was depreciated.


Ah... I think I'd approach this problem using a shader. Allegro definitely doesn't expose the stenciling feature in any way I know of.


It's possible to do masked blends using the features of Allegro 5 only by using al_set_blender. I do this to generate automatic blends for my game's tile maps. Here's a snippet from my code that should be helpful:

1 2/** Draw a tile into the given bitmap, which should be of size TILE_W, TILE_H 3 * applying the given mask bitmap, where the mask will 4be flipped and rotated as per the given mask_flags. The mask bitmap 5should be white, but with different alpha levels on the white 6which will be applied as the mask. Does nothing if tile is NULL. 7This requires al_hold_bitmap_drawing to be turned off! 8You can use this code snippet freely. You just do whatever you want with it, no warrantees, yadda yadda. 9 10*/ 11void tile_draw_masked_to 12(Image * result, Tile * tile, Image * mask, float angle, int mask_flags) { 13 Image * buffer; 14 15 Tileset * set; 16 Image * sheet; 17 ALLEGRO_BITMAP * target; 18 Color dcolor = al_map_rgb(0xee, 0x00, 0xee); 19 float dx, dy, sx, sy, sw, sh; 20 int bmpflags; 21 22 if (!tile) return; 23 24 bmpflags = al_get_new_bitmap_flags(); 25 al_set_new_bitmap_flags(ALLEGRO_CONVERT_BITMAP); 26 buffer = al_create_bitmap(al_get_bitmap_width(tile), 27 al_get_bitmap_height(tile)); 28 al_set_new_bitmap_flags(bmpflags); 29 30 31 /* Keep the target bitmap. */ 32 target = al_get_target_bitmap(); 33 34 /* Copy the tile into the buffer. */ 35 al_set_target_bitmap(buffer); 36 set = tile->set; 37 sheet = set->sheet; 38 dx = 0.0; 39 dy = 0.0; 40 sx = (float) tile->now.x; 41 sy = (float) tile->now.y; 42 sw = (float) TILE_W; 43 sh = (float) TILE_H; 44 /* Set blender to copy mode. */ 45 46 al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO); 47 al_draw_bitmap_region(sheet, sx, sy, sw, sh, 0, 0, 0); 48 49 /* Draw the mask over the tile, taking the alpha of the mask */ 50 al_set_blender(ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA); 51 al_draw_bitmap(mask, 0, 0, mask_flags); 52 53 /* Restore normal Allegro blending. */ 54 al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA); 55 56 sx = 0.0; 57 sy = 0.0; 58 if (angle != 0.0) { 59 sx = TILE_H / 2.0; 60 sy = TILE_W / 2.0; 61 dx += sx; 62 dy += sy; 63 } 64 65 /* Draw the tile mask buffer to the result bitmap */ 66 al_set_target_bitmap(result); 67 al_draw_rotated_bitmap(tile_mask_buffer, sx, sy, dx, dy, angle, 0); 68 /* And restore the target bitmap. */ 69 al_set_target_bitmap(target); 70 al_destroy_bitmap(buffer); 71}

Note that this will likely be slower than using a shader. Maybe masked draws would be a nice feature for a later version of Allegro....

Looks like stencil buffers are not obsolete and available in opengl
And in opengl ES 2.0 and 3.0 (though not 1.0?)
So definitely something we may be able to put an Allegro wrapper around...

Edit 3:
Also in Direct3D 9
And 10/11:


You can also look at the ex_depth_mask example, it does something similar without a shader, using the depth buffer as a mask. Stencil support definitely would be nice as well, especially in cases where you need both depth and stencil buffers at the same time.

Chris Katko

I can't get it to work, these blender options and alpha has always been confusing to me.

Do I want my mask to be in the RGB, or in the Alpha channel? I tried converting my map to alpha values, but that doesn't seem to work.

I guess I'm having trouble understanding when alpha moves. If I draw something with alpha, does that set the alpha of the destination bitmap?

1//not complete code, just relevant slices. 2 3void load_map(std::string file_path) 4 { 5 collision_map = al_load_bitmap(file_path.c_str()); 6 draw_map = al_load_bitmap(file_path.c_str()); 7 texture_bmp = al_load_bitmap("data/texture.png"); 8 draw_mask = al_create_bitmap(640, 480); 9 10 // Convert RGB to alpha 11 al_set_target_bitmap(draw_mask); 12 al_lock_bitmap(draw_mask, al_get_bitmap_format(draw_mask), ALLEGRO_LOCK_WRITEONLY); 13 al_lock_bitmap(draw_map, al_get_bitmap_format(draw_map), ALLEGRO_LOCK_READONLY); 14 for(int i = 0; i < w; i++) 15 for(int j = 0; j < h; j++) 16 { 17 ALLEGRO_COLOR temp = al_get_pixel(draw_map, i, j); 18 al_put_pixel(i, j, al_map_rgba(0,0,0, (temp.r+temp.g+temp.g)/3 )); 19 } 20 al_unlock_bitmap(draw_map); 21 al_unlock_bitmap(draw_mask); 22 al_set_target_bitmap(target); //restore stack 23 } 24 25void draw() 26 { 27 al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO); 28 al_draw_bitmap(draw_mask, 0, 0, 0); //mask 29 30 al_set_blender(ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA); 31 al_draw_bitmap(texture_bmp, 0, 0, 0); //texture over mask 32 33 al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA); //default 34 }

Edgar Reynaldo

If I draw something with alpha, does that set the alpha of the destination bitmap?

Only if you use al_set_separate_blender. Otherwise the destination alpha is untouched.


Well you're drawing the bitmap over the mask which probably doesn't work. I'm drawing the mask over the bitmap to another bitmap, which does work if you re-draw this resulting bitmap to screen. :)

Chris Katko

Thanks! It took me awhile to things to sink in (they still aren't great), but the biggest error between the sample code and mine was that I forgot to convert my "map" bitmap's black color to an alpha channel. Once I called that function, it works great!

However, there is one downside to the depth test method (other than losing normal use of the depth buffer while using it for masking). What about smooth transitions?

If you wanted something to be drawn using variable alpha levels, how would you frame it? Is it exactly like that "rounded rectangle" thread that's also going on?

Keep in mind, these alphas/blenders are still very confusing to me so please break it down and elaborate.

Thank you.

Mark Oates

Hey what am I doing?! I totally have the function you're looking for!


but use this overload for simplicity:

ALLEGRO_BITMAP *create_masked_bitmap(ALLEGRO_BITMAP *top_image, ALLEGRO_BITMAP *bottom_image)

I haven't used it in a while (blending methods changed slightly in subsequent versions of allegro) but I just tested it and it seemed to work. This image shows a mask image (top_image), a texture image (bottom_image), and the example program putting the two together on a blue background.


If you go to the header file here, you'll see there are variants of the function, too. You can apply use your own blending modes, and/or transforms to the top/bottom images, and/or supply your own surface bitmap so that it's not created each time. There isn't a variant for looping the texture on the bottom to fit the mask, but you could use draw_textured_rectangle or draw_offset_textured_rectangle for that here).

All of this is still in the works, I don't have docs for this stuff, yet.

I guess I'm having trouble understanding when alpha moves

It's very confusing. Back when I was working on this function, I had a program that generated all possible blends (hundreds of images, if I recall) just so I could find the correct blending sequence & bitmap order I was looking for. Which bitmap do you draw on the surface? Which one do you draw on top of that? Does it read the surface or the drawn bitmap? ALLEGRO_BLEND_MODE_WHUT?

F it. Just brute force. ;D


Maybe the documentation needs improving, but I figured it out rather easily by playing with ex_blend.


Chris Katko

Thanks for the help guys! I don't have time implement your most recent suggestions yet, but I'll keep you updated!

Here's a picture of it working so far (with ugly filler graphics):


Background is drawn normally, the "map" (black for nothing, any other color for land) is converted to alpha, and then a texture is printed through that. The texture is tiled 2x2 right now to show its possible. Nothing too fancy, but it's baby steps.

Thread #615317. Printed from Allegro.cc