Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » How to use draw_text to create mask?

Credits go to Edgar Reynaldo and jmasterx for helping out!
This thread is locked; no one can reply to it. rss feed Print
How to use draw_text to create mask?
Max Savenkov
Member #4,613
May 2004
avatar

How do I "cut out" text from bitmap, so background would show through after this:

render.ClearBackbuffer( Color(255,0,0) );
render.DrawImage( mask, 0, 0 );

So far, I can't find the right blending mode. I always end up with the WHOLE glyph being transparent, not just where text is. So the end result looks like a set of red rectangles of varying height and width.

I thought that

render.SetBlender( ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ZERO, ALLEGRO_ADD, ALLEGRO_INVERSE_ALPHA, ALLEGRO_ZERO );

should do it with text being drawn with color (0,0,0,255), but it does not work.

Edgar Reynaldo
Member #8,592
May 2007
avatar

Mark Oates
Member #1,146
March 2001
avatar

Short version, the only way I know to do "cut out" text is by drawing the text to a texture and processing each pixel, inverting the alpha. You might be able to do it with 5.1's new blenders.

Otherwise, this is the code I use to mask images:

#SelectExpand
1static ALLEGRO_BITMAP *create_masked_image(ALLEGRO_BITMAP *top_image, ALLEGRO_BITMAP *bottom_image, int op, int src, int dst, int alpha_op, int alpha_src, int alpha_dst, ALLEGRO_TRANSFORM *top_transform=NULL, ALLEGRO_TRANSFORM *bottom_transform=NULL) 2{ 3 static ALLEGRO_TRANSFORM identity_transform; 4 al_identity_transform(&identity_transform); 5 if (!top_image || !bottom_image) return NULL; 6 7 ALLEGRO_STATE state; 8 al_store_state(&state, ALLEGRO_STATE_TARGET_BITMAP | ALLEGRO_STATE_BLENDER | ALLEGRO_STATE_TRANSFORM); 9 10 ALLEGRO_BITMAP *dest = al_create_bitmap(al_get_bitmap_width(top_image), al_get_bitmap_height(top_image)); 11 al_set_target_bitmap(dest); 12 al_clear_to_color(al_map_rgba_f(0,0,0,0)); 13 14 15 // draw the contents 16 if (bottom_transform) al_use_transform(bottom_transform); 17 else al_use_transform(&identity_transform); 18 al_draw_bitmap(bottom_image, 0, 0, NULL); 19 al_set_separate_blender(op, src, dst, alpha_op, alpha_src, alpha_dst); 20 if (top_transform) al_use_transform(top_transform); 21 else al_use_transform(&identity_transform); 22 al_draw_bitmap(top_image, 0, 0, NULL); 23 al_restore_state(&state); 24 25 return dest; 26} 27 28 29 30static ALLEGRO_BITMAP *create_masked_image(ALLEGRO_BITMAP *top_image, ALLEGRO_BITMAP *bottom_image) 31{ 32 return create_masked_image(top_image, bottom_image, 0, 0, 2, 2, 0, 2, NULL, NULL); 33} 34 35 36static ALLEGRO_BITMAP *create_masked_image(ALLEGRO_BITMAP *top_image, ALLEGRO_BITMAP *bottom_image, ALLEGRO_TRANSFORM *top_transform, ALLEGRO_TRANSFORM *bottom_transform) 37{ 38 return create_masked_image(top_image, bottom_image, 0, 0, 2, 2, 0, 2, top_transform, bottom_transform); 39} 40 41 42static void draw_masked_image(ALLEGRO_BITMAP *destination, ALLEGRO_BITMAP *top_image, ALLEGRO_BITMAP *bottom_image, int op, int src, int dst, int alpha_op, int alpha_src, int alpha_dst, ALLEGRO_TRANSFORM *top_transform=NULL, ALLEGRO_TRANSFORM *bottom_transform=NULL) 43{ 44 static ALLEGRO_TRANSFORM identity_transform; 45 al_identity_transform(&identity_transform); 46 if (!destination || !top_image || !bottom_image) return; 47 48 ALLEGRO_STATE state; 49 al_store_state(&state, ALLEGRO_STATE_TARGET_BITMAP | ALLEGRO_STATE_BLENDER | ALLEGRO_STATE_TRANSFORM); 50 51 //ALLEGRO_BITMAP *dest = al_create_bitmap(al_get_bitmap_width(top_image), al_get_bitmap_height(top_image)); 52 al_set_target_bitmap(destination); 53 al_clear_to_color(al_map_rgba_f(0,0,0,0)); 54 55 56 // draw the contents 57 if (bottom_transform) al_use_transform(bottom_transform); 58 else al_use_transform(&identity_transform); 59 al_draw_bitmap(bottom_image, 0, 0, NULL); 60 al_set_separate_blender(op, src, dst, alpha_op, alpha_src, alpha_dst); 61 if (top_transform) al_use_transform(top_transform); 62 else al_use_transform(&identity_transform); 63 al_draw_bitmap(top_image, 0, 0, NULL); 64 al_restore_state(&state); 65 66 return; 67} 68 69 70static void draw_masked_image(ALLEGRO_BITMAP *destination, ALLEGRO_BITMAP *top_image, ALLEGRO_BITMAP *bottom_image) 71{ 72 draw_masked_image(destination, top_image, bottom_image, 0, 0, 2, 2, 0, 2, NULL, NULL); 73} 74 75 76static void draw_masked_image(ALLEGRO_BITMAP *destination, ALLEGRO_BITMAP *top_image, ALLEGRO_BITMAP *bottom_image, ALLEGRO_TRANSFORM *top_transform, ALLEGRO_TRANSFORM *bottom_transform) 77{ 78 draw_masked_image(destination, top_image, bottom_image, 0, 0, 2, 2, 0, 2, top_transform, bottom_transform); 79}

static ALLEGRO_BITMAP *create_masked_image(ALLEGRO_BITMAP *top_image, ALLEGRO_BITMAP *bottom_image); creates an image by combining the opacity of both images, retaining the pixel colors of the bottom (top?) image. draw_masked_image() does not create a new bitmap, but performs the same operation to a given destination bitmap.

However, this will not enable you to "cut out" text from a solid texture. For that you will need to have 5.1, which includes more ALLEGRO_INVERSE_* operators.

Edgar Reynaldo
Member #8,592
May 2007
avatar

Nevermind, my way doesn't work. I've tried just about every blender I can think of, but the problem is that ALLEGRO_INVERSE_ALPHA writes the zero alpha values of the glyph background onto the destination, so unless you want a black mask, I don't know what you can do.

The only way I can think of doing it is to write your own custom shader that ignores the glyphs background zero alpha values and writes zero alpha values for the text pixels that have non-zero alpha. (Use ALLEGRO_TTF_MONOCHROME when loading the font).

Arthur Kalliokoski
Second in Command
February 2005
avatar

If you're using Open GL, you could use the stencil buffer.

“Throughout history, poverty is the normal condition of man. Advances which permit this norm to be exceeded — here and there, now and then — are the work of an extremely small minority, frequently despised, often condemned, and almost always opposed by all right-thinking people. Whenever this tiny minority is kept from creating, or (as sometimes happens) is driven out of a society, the people then slip back into abject poverty. This is known as "bad luck.”

― Robert A. Heinlein

jmasterx
Member #11,410
October 2009

I thought Allegro 5.0 could do subtractive blending, isn't that essentially what needs to be done here?

Edgar Reynaldo
Member #8,592
May 2007
avatar

jmasterx said:

I thought Allegro 5.0 could do subtractive blending, isn't that essentially what needs to be done here?

I think you're on to something there :

If you make src ALLEGRO_ONE and use ALLEGRO_DEST_MINUS_SRC, then you have

ALLEGRO_DEST_MINUS_SRC :
r = dr * dst - sr
g = dg * dst - sg
b = db * dst - sb
a = da * dst - sa

Clear black source pixels (like the glyph background) would have no effect. If you gave them an alpha of 255 and left them black, then they would essentially write zero alpha pixels, which is what you want.

This test program shows how to make a masked overlay using this method :

#SelectExpand
1 2#include <cstdio> 3 4#include "allegro5/allegro.h" 5#include "allegro5/allegro_font.h" 6#include "allegro5/allegro_ttf.h" 7#include "allegro5/allegro_direct3d.h" 8#include "allegro5/allegro_opengl.h" 9#include "allegro5/allegro_image.h" 10#include "allegro5/allegro_primitives.h" 11 12class AllegroSetup { 13private : 14 ALLEGRO_DISPLAY* display; 15 ALLEGRO_TIMER* timer; 16 ALLEGRO_EVENT_QUEUE* event_queue; 17 bool ready; 18public : 19 AllegroSetup(int w , int h , double tick_time) : 20 display(0), 21 timer(0), 22 event_queue(0), 23 ready(false) 24 { 25 if (!al_init()) {printf("Failed to initialize Allegro.\n");return;} 26 al_set_new_display_flags(ALLEGRO_WINDOWED | ALLEGRO_OPENGL); 27 if (!(display = al_create_display(w,h))) { 28 printf("Failed to create %i X %i display.\n" , w , h); 29 return; 30 } 31 if (!(timer = al_create_timer(tick_time))) { 32 printf("Failed to create %lg paced timer.\n" , tick_time); 33 return; 34 } 35 if (!al_install_keyboard()) { 36 printf("Failed to initialize keyboard.\n"); 37 return; 38 } 39 if (!al_install_mouse()) { 40 printf("Failed to install mouse.\n"); 41 return; 42 } 43 if (!(event_queue = al_create_event_queue())) { 44 printf("Failed to create event queue.\n"); 45 return; 46 } 47 al_register_event_source(event_queue , al_get_keyboard_event_source()); 48 al_register_event_source(event_queue , al_get_display_event_source(display)); 49 al_register_event_source(event_queue , al_get_mouse_event_source()); 50 al_register_event_source(event_queue , al_get_timer_event_source(timer)); 51 ready = true; 52 } 53 54 ~AllegroSetup() { 55 if (event_queue) {al_destroy_event_queue(event_queue);} 56 if (display) {al_destroy_display(display);} 57 } 58 59 bool Ready() {return ready;} 60 61 inline operator ALLEGRO_DISPLAY* () {return display;} 62 inline operator ALLEGRO_EVENT_QUEUE* () {return event_queue;} 63 inline operator ALLEGRO_TIMER* () {return timer;} 64}; 65 66 67int readkey(ALLEGRO_EVENT_QUEUE* eq) { 68 while (1) { 69 ALLEGRO_EVENT ev; 70 al_wait_for_event(eq , &ev); 71 if (ev.type == ALLEGRO_EVENT_KEY_CHAR) { 72 return ev.keyboard.keycode; 73 } 74 } 75 return 0; 76} 77 78int main(int argc , char** argv) { 79 80 AllegroSetup allegro(800,600,1.0/60.0); 81 82 al_init_font_addon(); 83 al_init_ttf_addon(); 84 ALLEGRO_FONT* font = al_load_ttf_font("consola.ttf" , -54 , ALLEGRO_TTF_MONOCHROME); 85 86 ALLEGRO_BITMAP* overlay = al_create_bitmap(800,600); 87 al_set_target_bitmap(overlay); 88 al_set_blender(ALLEGRO_ADD , ALLEGRO_ONE , ALLEGRO_ZERO);// overwrite blender 89 al_clear_to_color(al_map_rgba(255,255,255,255)); 90 al_set_blender(ALLEGRO_DEST_MINUS_SRC , ALLEGRO_ONE , ALLEGRO_ONE);// subtract source values 91 al_draw_text(font , al_map_rgba(0,0,0,255) , 20 , 377 , 0 , "Invisible text overlay"); 92 93 al_set_target_bitmap(al_get_backbuffer(allegro)); 94 al_set_blender(ALLEGRO_ADD , ALLEGRO_ONE , ALLEGRO_ZERO);// overwrite 95 al_clear_to_color(al_map_rgba(0,0,0,0)); 96 al_set_blender(ALLEGRO_ADD , ALLEGRO_ALPHA , ALLEGRO_INVERSE_ALPHA);// non-premultiplied alpha 97 al_draw_bitmap(overlay , 0 , 0 , 0); 98 99 al_flip_display(); 100 readkey(allegro); 101 102 return 0; 103}

It creates a pure white bitmap with pixel values of 255,255,255,255 and then writes pixel values of 0,0,0,255 onto it using al_set_blender(ALLEGRO_DEST_MINUS_SRC , ALLEGRO_ONE , ALLEGRO_ONE);. It then clears the backbuffer to 0,0,0,0 and draws the white overlay onto it using non-premultiplied alpha. The black background then shows through where you have drawn the text :

{"name":"605172","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/c\/3cb230666cd77b71b1c0314fdcabbd14.png","w":812,"h":632,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/3\/c\/3cb230666cd77b71b1c0314fdcabbd14"}605172

Max Savenkov
Member #4,613
May 2004
avatar

Thanks! That works perfectly! It's a pity I still get confused by blending modes. I shouldn't be :(

Go to: