![]() |
|
Problem with Transparency Mask |
crowbar
Member #14,200
April 2012
|
Hello, I am new to Allegro and I am trying to make a "flashlight"-like effect. Where I put a 50% transparent black png over my background image, then I want to be able to shine multiple "flashlights" over the image and where ever a "flashlight" is, the black png is erased at that location of that frame. I tried using The attachment (example.jpg) is an example of what I want, just imagine the circles of light moving in real time. Any suggestions? Thanks! Crowbar |
Max Savenkov
Member #4,613
May 2004
![]() |
You'll have to do this: 1) Create a screen-sized bitmap (once) 1ALLEGRO_BITMAP *old = al_get_target_bitmap();
2al_set_target_bitmap( mask );
3al_clear_to_color( al_map_rgba( 0,0,0,128 ) );
4int a,b,c;
5al_get_blender( &a, &b, &c );
6al_set_blender(ALLEGRO_DEST_MINUS_SRC , ALLEGRO_ONE , ALLEGRO_ONE);
7// For N spotlights:
8al_draw_filled_circle( x1, y1, radius1, al_map_rgba( 255,255,255,255 ) );
9...
10al_draw_filled_circle( xN, yN, radiusN, al_map_rgba( 255,255,255,255 ) );
11al_set_blender(a,b,c);
12al_set_target_bitmap( old );
13// Draw your scene
14// ...
15al_draw_bitmap( mask, 0, 0, 0 );
As an optimization, you can only clear mask & redraw spotlights if they have moved more than 0 pixels in this frame, or try to limit clearing/re-drawing to dirty rectangles, but it should not be necessary on modern hardware.
|
crowbar
Member #14,200
April 2012
|
Max, Out of curiosity, I am wondering how it works, I think I understand just about everything except: "al_set_blender(ALLEGRO_DEST_MINUS_SRC , ALLEGRO_ONE , ALLEGRO_ONE);" What is a blender, and what do the settings do that you gave it? Again, thanks so much! Crowbar |
Max Savenkov
Member #4,613
May 2004
![]() |
Blender is operation which is used to add pixel colors on source and destination bitmaps. First argument is type of operation, and the rest are multipliers used. It works like that: ResultColor = BLENDING_OPERATION( SourceColor * SourceMultiplier, DestinationColor * DestinationMultiplier ) In this case, you get: RC = DestColor * 1 - SrcColor * 1 (Blending clamps values, so that they wouldn't be less than 0) Because you are drawing (255,255,255,255) (Source) over (0,0,0,128) (Destination), your result would be (0,0,0,0) - a complete transparency. So now you have a transparent circle, just as you want. (In fact, you can use (0,0,0,255) instead of (255,255,255,255) as long as your Destination has only Alpha component to be erased)
|
crowbar
Member #14,200
April 2012
|
Max, I have another question, This is my current objective: 1al_set_target_bitmap(imageCopy);//redraw image (refresh it)
2 al_draw_bitmap(image,0,0,0);
3
4 al_set_target_bitmap(combineOfDraw);//combine all my shapes before blending alpha
5 al_clear_to_color(al_map_rgba(0,0,0,0));
6
7 al_draw_filled_circle(ev.mouse.x,ev.mouse.y, 200, al_map_rgba ( 255,255,255,255));//this one gets moved by the mouse
8 al_draw_filled_circle(500,500, 150, al_map_rgba ( 255,255,255,255));
9 al_set_target_bitmap(imageCopy);
10 int a,b,c;
11 al_get_blender(&a,&b,&c);//get default blender
12 al_set_blender(ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA);//set blender for "mask"
13
14
15 al_draw_bitmap(combineOfDraw, 0, 0, 0);
16
17 al_set_blender(a,b,c);//reset blender
18 //set target back to back buffer
19 al_set_target_bitmap(al_get_backbuffer(display));
20
21 //draw!
22 al_draw_bitmap(imageCopy, 0, 0, 0);
It works but, I am having a big problem: Thanks in advance! Crowbar UPDATE: My desired effect is "A in B" as shown in this image: http://en.wikipedia.org/wiki/File:Alpha_compositing.svg Original site: http://en.wikipedia.org/wiki/Alpha_compositing |
Max Savenkov
Member #4,613
May 2004
![]() |
I don't think it's blending operation that is slow, but some of the surrounding code. I'm not quite sure, but I think of two possible problems. First, I see that this procedure is done when some mouse event is handled (I assume it's MOUSE_MOVE, or whatever Allegro calls it). I don't remember how exactly it works, but it might be that you can get SEVERAL such messages per frame, therefore redoing all the work several times. This could be solved quite easily by only saving new mouse coordinates in message handler code and then using them after ALL messages are handled to construct picture. Second, you probably would be better off avoiding al_get_backbuffer. I'm not sure, but it may have a performance penalty! Instead, just do as I have done in my example and save previous target bitmap from al_get_target_bitmap(). Hope this helps. If it does not, you may try to use a profiler to see where all the time is wasted. I recommend two open-source profilers for Windows: Very Sleepy and Luke Stackwalker. The later is especially nice, it allowed me to track a nasty little inside Allegro which was causing it to be extremely slow on Intel videocards.
|
crowbar
Member #14,200
April 2012
|
Hrm, okay. I will get rid of al_get_backbuffer and see if there is a change. I will look into the Mouse events as well. I was talking to a friend of mine yesterday who is proficient in C++ (but unfamiliar with Allegro) who said that if al_set_target_bitmap() changes the render target, then it is probably the line that is causing the most work (especially since I am calling it 4 times). He said I could achieve the same effect by simply drawing to the target, saving that as a bitmap elsewhere, clearing the target and using it over again, and then blend them all together. Is this plausible. I intend to look into it later today. (but i'm on my laptop right now and can't compile yet) I might be able to rewrite it as the following, and reduce the amount of al_set_target_bitmap(). 1ALLEGRO_BITMAP *old = al_get_target_bitmap();
2al_set_target_bitmap(composite);
3al_draw_bitmap(image,0,0,0);
4ALLEGRO_BITMAP *imageSave = al_get_target_bitmap();
5
6al_clear_to_color(al_map_rgba(0,0,0,0));
7al_draw_filled_circle(ev.mouse.x,ev.mouse.y, 200, al_map_rgba ( 255,255,255,255));
8al_draw_filled_circle(500,500, 150, al_map_rgba ( 255,255,255,255));
9ALLEGRO_BITMAP *maskSave = al_get_target_bitmap();
10
11al_clear_to_color(al_map_rgba(0,0,0,0));
12al_draw_bitmap(imageSave,0,0,0);
13int a,b,c;
14al_get_blender(&a,&b,&c);
15al_set_blender(ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA);
16al_draw_bitmap(maskSave,0,0,0);
17
18//revert
19al_set_blender(a,b,c);
20al_set_target_bitmap(old);
21al_draw_bitmap(composite,0,0,0);
This isn't the most efficient way to order the commands in the above code, but I am writing it as a direct translation to my earlier post using the method suggested by m friend. Once I have a chance to compile this I think it may solve my problem. So, I got rid of the call to al_get_backbuffer and I combined this with my friends suggestion. Then again, I am calling get_target_bitmap() a lot. Is this expensive too? -Crowbar UPDATE: I found this: "You can create and manipulate bitmaps in system RAM, or you can write to the special `screen' bitmap which represents the video memory in your graphics card." on http://alleg.sourceforge.net/stabledocs/en/alleg009.html Now I'm reading about memory bitmaps, system bitmaps, video memory bitmaps, and sub-bitmaps, I get the feeling that what ever one I am using is not the right one. The trick is that I need to combine all the circles onto a single bitmap before I blend so that it will erase everything on the current target except where the circles are (including the circles, that's why I pass ALLEGRO_ZERO, ALLEGRO_ALPHA.) I am using the circles as a mask. Should I use "al_hold_bitmap_drawing" or should I just use a different type of bitmap? |
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
crowbar - you're looking at the manual for allegro 4, but you're using allegro 5 Here is the Allegro 5 Manual When A5 is slow, it is usually because you're using memory bitmaps, or not consolidating drawing sources into a single source and using al_hold_bitmap_drawing. Post the full code for your drawing routine, not just this part. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
crowbar
Member #14,200
April 2012
|
Ok, so here is the code (below) for the one that does work but its really slow . I tried implementing the other code that I posted above, but I got some weird errors, and I haven't learned exception handling in C++ yet, so I will take some time to do that tomorrow and try to figure out the problem. Edgar, Again, Note: "shade.png" is irrelevant cause I just end up clearing it before I use it, and "backdrop.jpg" is just any random background. A forest or something would work well to demonstrate what I am trying to do, just something with detail. 1#include <stdio.h>
2#include <allegro5\allegro.h>
3#include <allegro5\allegro_image.h>
4#include <allegro5\allegro_primitives.h>
5
6
7int main(void)
8{
9 //variables
10 int width = 800;
11 int height = 400;
12 bool done = false;
13 int x,y;
14 int imageWidth = 0;
15 int imageHeight = 0;
16 int lightRad = 100;
17
18 //allegro variable
19 ALLEGRO_DISPLAY *display = NULL;
20 ALLEGRO_EVENT_QUEUE *event_queue = NULL;
21 ALLEGRO_BITMAP *image = NULL;
22 ALLEGRO_BITMAP *imageCopy = NULL;
23 ALLEGRO_BITMAP *combineOfDraw = NULL;
24 //program init
25 if(!al_init()) //initialize Allegro
26 return -1;
27
28 display = al_create_display(width, height); //create our display object
29
30 if(!display) //test display object
31 return -1;
32
33 //addon init
34 al_install_keyboard();
35 al_init_image_addon();
36 al_install_mouse();
37 al_init_primitives_addon();
38
39 image = al_load_bitmap("backdrop.jpg");
40 imageCopy = al_load_bitmap("backdrop.jpg");
41
42 imageWidth = al_get_bitmap_width(image);
43 imageHeight = al_get_bitmap_height(image);
44
45 x = width / 2 - imageWidth / 2;
46 y = height / 2 - imageHeight / 2;
47
48 combineOfDraw = al_load_bitmap("shade.png");
49
50
51 event_queue = al_create_event_queue();
52
53 al_register_event_source(event_queue, al_get_mouse_event_source());
54 al_register_event_source(event_queue, al_get_keyboard_event_source());
55
56 while(!done)
57 {
58 ALLEGRO_EVENT ev;
59 al_wait_for_event(event_queue, &ev);
60
61
62 if(ev.type == ALLEGRO_EVENT_MOUSE_AXES)
63 {
64 //fprintf(stderr, "x: %d, y: %d\n", ev.mouse.x, ev.mouse.y);
65
66 al_set_target_bitmap(imageCopy);//redraw image (refresh it)
67 al_draw_bitmap(image,0,0,0);
68
69 al_set_target_bitmap(combineOfDraw);//combine all my shapes before blending alpha
70 al_clear_to_color(al_map_rgba(0,0,0,0));
71 //al_draw_filled_rectangle(50,50,500,500,al_map_rgba(215,124,25,0));
72 al_draw_filled_circle(ev.mouse.x,ev.mouse.y, 200, al_map_rgba ( 255,255,255,255));
73 al_draw_filled_circle(500,500, 150, al_map_rgba ( 255,255,255,255));
74 al_set_target_bitmap(imageCopy);
75 int a,b,c;
76 al_get_blender(&a,&b,&c);//get default blender
77 al_set_blender(ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA);//set blender for "mask"
78 /*
79 al_set_blender(ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA); works perfectly, but the problem is
80 that it only considers pixels that you are drawing, (like the circle), not the empty space
81 and I need it to consider the (0,0,0,0) space
82 */
83 //this right now works like I thought it would, BLENDING HAPPENS AS SOON AS YOU DRAW
84 //there for, when I draw the rectangle (with alpha 0, on ALLEGRO_ZERO, ALLEGRO_ADD)
85 //it subtracts "image" where it overlaps, like I thought
86 //but then when I draw filled_circle in an attempt to not erase where it is
87 //the rectangle has ALREADY been subtracted, so now all the pixels there are alpha = 0
88 //solution? Draw the alpha = 0 (rectangle) and the alpha = 1 (or 255, circles) on
89 //and imageFile THEN draw that imageFile to "image" with blend options ALLEGRO_ZERO
90 //ALLEGRO_ALPHA
91 /*al_draw_filled_rectangle(50,50,500,500,al_map_rgba(215,124,25,0));
92 al_draw_filled_circle(ev.mouse.x,ev.mouse.y, 200, al_map_rgba ( 255,255,255,255));
93 al_draw_filled_circle(500,500, 150, al_map_rgba ( 255,255,255,255));*/
94 al_draw_bitmap(combineOfDraw, 0, 0, 0);
95
96 al_set_blender(a,b,c);//reset blender
97 ///////////////
98 //set target back to back buffer
99 al_set_target_bitmap(al_get_backbuffer(display));
100
101 //draw!
102 al_draw_bitmap(imageCopy, 0, 0, 0);
103
104
105
106 }
107 else if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
108 {
109
110 switch(ev.keyboard.keycode)
111 {
112 case ALLEGRO_KEY_ESCAPE:
113 done = true;
114 break;
115 }
116 }
117
118 al_flip_display();
119 al_clear_to_color(al_map_rgb(255,255,200));
120 }
121 al_destroy_event_queue(event_queue);
122
123 al_destroy_bitmap(image);
124 al_destroy_bitmap(imageCopy);
125 al_destroy_bitmap(combineOfDraw);
126 al_destroy_display(display); //destroy our display object
127
128 return 0;
129}
|
Max Savenkov
Member #4,613
May 2004
![]() |
This code works resonably fast on my machine. But it could be done in a more simple way: 1 if(ev.type == ALLEGRO_EVENT_MOUSE_AXES)
2 {
3 // Save blender
4 int a, b, c;
5 al_get_blender( &a, &b, &c );
6
7 // Draw image to imageCopy, but set Alpha of all pixels
8 // to 0. set_separete_blender allows us to set different rules
9 // for blending RGB component of colors and Alpha component.
10 al_set_separate_blender( a,b, c, ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ZERO );
11 ALLEGRO_BITMAP *old = al_get_target_bitmap();
12 al_set_target_bitmap( imageCopy );
13 al_draw_bitmap( image, 0, 0, 0 );
14
15 // Draw circle on imageCopy, but only set Alpha of pixels inside
16 // it.
17 al_set_separate_blender( ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ONE, ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO );
18 al_draw_filled_circle( ev.mouse.x, ev.mouse.y, 100, al_map_rgba(0,0,0,255) );
19
20 // Now we are drawing on backbuffer again
21 al_set_target_bitmap( old );
22
23 // Draw imageCopy on backbuffer in such way that only pixels
24 // with non-zero Alpha are drawn.
25 al_set_blender( ALLEGRO_ADD, ALLEGRO_ALPHA , ALLEGRO_ONE );
26 al_draw_bitmap( imageCopy,0,0,0 );
27
28 // Restore blender
29 al_set_blender( a,b ,c );
30 }
|
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
crowbar said:
Edgar, In A5, there really is no proper use of memory bitmaps - they are slow as molasses... Thing is though, bitmaps are video bitmaps by default, unless you load them before you create the display, and unless they aren't auto converted to video bitmaps by the latest A5 code (A5.0.6 or later?). al_hold_bitmap_drawing is for when you are drawing multiple images off of the same parent bitmap. You get a speed up because you don't have to continually rebind the texture that is being drawn. For example, drawing many sub bitmaps off of a parent bitmap (like tiles on a tilemap...). As for your drawing code, the only thing I can see to be improved is the number of calls to al_set_target_bitmap, which are expensive... - Draw to combineOfDraw first, then draw to imageCopy, and then draw to the backbuffer and flip the display. Right now you are drawing to imageCopy, then combineOfDraw, then imageCopy, and then the backbuffer, which is one more step than you need... When you say it is slow, do you mean it doesn't respond quickly to mouse moves? Because that is the main time that you are drawing to the screen. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
crowbar
Member #14,200
April 2012
|
Max, Edgar, I did try getting rid of that extra call, but it did me no good. As an experiment to see if the al_set_target_bitmap calls were the thing slowing me down, I added a bunch of them to see what it would do, but it didn't slow enough to make me think that those lines are the ones that keep the circle from following the mouse in real time. Yes, what I mean is that the circle lags far behind the mouse, it doesn't keep up with it which is what happens if you just draw a black circle on the position of the mouse without any special blending. In my final product I will be drawing according to time (probably 30 frames/sec) but I used the mouse just for testing purposes. Crowbar |
Max Savenkov
Member #4,613
May 2004
![]() |
I'm out of ideas then. For me, both version work very good. Did you try profiling?
|
crowbar
Member #14,200
April 2012
|
I finally found what was slowing it down. (No, I haven't gotten to profiling yet, to be honest I forgot you mentioned it, so I will be looking into that). But what I finally figured out is that the thing that was making it take so long was simply using the blending (ALLEGRO_DEST_MINUS_SRC, ALLEGRO_ONE, ALLEGRO_ONE) or (ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ALPHA) on any bitmap. The program runs just fine when I blend on a bitmap that has been cleared But for whatever reason, trying to draw a bitmap on any other bitmap with these blending options is taking forever. Now that I found the problem, I might not need to use the profiler after all, but I will still look into it. I'm going to do some more digging and see if I can't figure out why |
Max Savenkov
Member #4,613
May 2004
![]() |
What's your hardware? If blending a single bitmap takes a very long time it's probably the case of Allegro taking a software code branch. Do you, by chance, run it on Intel GMA video card? If so, then probably your problem is the same one I encountered a month ago. There is a slight bug in Allegro, which will be fixed in 5.0.7 or 5.2, which leads Allegro to have very slow performance on video cards which do not support separate alpha blending, even when it does not need it.
|
crowbar
Member #14,200
April 2012
|
Im using ATI Radeon HD 4850 |
Max Savenkov
Member #4,613
May 2004
![]() |
That's it, then. According to this, HD4850 does not support separate alpha blend, which leads to VERY bad Allegro performance because of bug. You can wait for the next version of Allegro, or compile your own version of Allegro with fix included (just remove a check for _al_d3d_supports_separate_alpha_blend in d3d_draw_bitmap_region in win/d3d_bmp.cpp)
|
crowbar
Member #14,200
April 2012
|
Ahhh, that's very helpful to know. Once I started to understand how the blending works I was wondering why performance would be so bad. Do you know when the Allegro update is coming out? I've got a lot of stuff I cant work on in the mean time so I don't need to compile it myself, ill just wait. Crowbar |
Max Savenkov
Member #4,613
May 2004
![]() |
Unfortunately, I don't know when the next version will come out. So we both will have to wait for it.
|
|