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
al_convert_mask_to_alpha(shade, al_map_rgb(255,0,255));
but it was FAR too slow! because I had to update the location of the flash lights every frame and I was constantly recreating the "shade" bitmap.
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
You'll have to do this:
1) Create a screen-sized bitmap (once)
2) Paint it transparent black (0,0,0,128)
3) Draw a few filled circles on it with (255,255,255,255), with blending mode set to ALLEGRO_DEST_MINUS_SRC , ALLEGRO_ONE , ALLEGRO_ONE
4) Restore old blending mode
4) Draw your image
5) Draw your bitmap over it
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.
Max,
Thank you so much, I can't tell you how helpful this was! It works very smooth, this is exactly what I wanted to be able to do!
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
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
(0,0,0,0) = (0,0,0,128) * 1 - (255,255,255,255) * 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)
Max,
Thank you so much, you have been very helpful.
I have another question,
I am now trying to do something slightly different. I've been working with masks for a few days now and I am starting to understand how they work.
This is my current objective:
I now want the circles to act as a mask,so that all you see of the image is where the circle is, like everything that didn't have a black circle on it got erased.
This is my current code:
It works but, I am having a big problem:
It is really slow. I can't understand why this operation would be any slower than the one Max showed me before.
This is all that is happening: a = d.a * dst + s.a * src (becomes=>) a = d.a * (s.a)+ s.a * (0)
So that where ever a circle is drawn (s), the destination gets its alpha (1), but wherever there is no circle, the destination gets multiplied by its alpha too (0), thus being erased.
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
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.
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().
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
I think that this is what my friend is talking about. So I assume that al_set_target_bitmap() causes a draw command to draw to the video memory? But now how do I "edit" or write to a bitmap in system RAM? Am I on the right track?
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?
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.
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,
Is there a place where I can learn about proper use of memory bitmaps and al_hold_bitmap_drawing. I looked up the documentation on al_hold_bitmap_drawing but it didn't give any concrete examples so I'm not sure when I should use it. I kind of understand what it does but I don't understand why its important. So are you saying I should be using it? Could you give me an example of proper use of "non-memory bitmaps" and al_hold_bitmap_drawing? Also, if I shouldn't be using memory bitmaps (which does sound like its the problem) what should I use and how?
Again,
Thanks so much
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.
This code works resonably fast on my machine. But it could be done in a more simple way:
Edgar,
Is there a place where I can learn about proper use of memory bitmaps and al_hold_bitmap_drawing. I looked up the documentation on al_hold_bitmap_drawing but it didn't give any concrete examples so I'm not sure when I should use it. I kind of understand what it does but I don't understand why its important. So are you saying I should be using it? Could you give me an example of proper use of "non-memory bitmaps" and al_hold_bitmap_drawing? Also, if I shouldn't be using memory bitmaps (which does sound like its the problem) what should I use and how?
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.
Max,
For some reason that code does not get me the same result that mine did. It looks much different, and the circle still lags far behind the mouse (so its still really slow for me)
Edgar,
Yes, I am using allegro 5.0.6.
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
I'm out of ideas then. For me, both version work very good. Did you try profiling?
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
after a call to al_clear_to_color(al_map_rgba(0,0,0,128)); for instance.
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
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.
Im using ATI Radeon HD 4850
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)
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
Unfortunately, I don't know when the next version will come out. So we both will have to wait for it.