How to use draw_text to create mask?
Max Savenkov

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

I haven't used the blenders before, but I think this will do what you want :

al_set_blender(ALLEGRO_ADD , ALLEGRO_ONE , ALLEGRO_ZERO);
al_set_target_bitmap(overlay);
al_clear_to_color(al_map_rgb(255,0,0,255));
al_draw_text(/*...*/ , al_map_rgb(0,0,0,0));

al_set_target_bitmap(al_get_backbuffer(display));
al_draw_bitmap(overlay , 0 , 0 , 0);
al_flip_display();

Mark Oates

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

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

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

jmasterx

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

Edgar Reynaldo
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

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

Thread #608992. Printed from Allegro.cc