Grabbing image with Alpha Channel
BlitzMax Forums/BlitzMax Programming/Grabbing image with Alpha Channel
| ||
So, drawing a room takes around 1200 draw commands for me, that is 40x30 tiles. I figured out it would be faster if I could turn this into 4 images which are of size 20x15 tiles. The basic idea was to draw each 20x15 part of the room on the screen and GrabImage it. Whilst I successfully managed to create 4 big images which when pieced together gave the same effect that the 1200 draws above, the GrabImage omitted alpha channel. The only way I seem to be able to accomplish it is by copying the tiles pixel-by-pixel to a pixmap, but when you want to copy 480000 pixels it is kinda slow, and I wanted to be able to perform this on-fly every couple of seconds. I saw this topic: http://www.blitzmax.com/Community/posts.php?topic=63266 but that solution won't work for me (and 32-bit color depth didn't make the GrabImage grab the alpha too). I also thought about 'prerendering' all the room data and saving it to disk at the first run but I don't like this solution. So I wanted to ask if there is any other way to do it? EDIT: Ok, adding MASKEDIMAGE allows me to grab transparency color but not alpha channel unfortunately, so any better and working ideas are welcome. |
| ||
For OpenGl at least, look at glReadPixels() ... you CAN use it to read all channels including alpha. I use it to read the alpha channel's value, after having written something to the destination alpha channel, to test that the alpha channel really exists. Which gives me an idea ..... have you actually drawn anything to the alpha channel of the screen? What do you use to render your images? Just DrawImage? And in what drawing mode, AlphaBlend? I don't think (not sure) any of the Blitz drawing modes write anything to the screen's alpha channel. you could try enabling drawing to the alpha channel, in opengl. Only when there is something stored in the *screens* alpha channel (not the image you drew necessarily), will you get anything back when you read the image. |
| ||
Unfortunately my graphics card doesn't support OpenGL... Well, it supported before but not ever since I moved to Win 7 RC (from Beta 1) and installed latest drivers so I can't test it now... But I will look into changing drivers, perhaps I can somehow make it work. But I have found another, quite ugly but working way to do it using image locking and TPixmap.Paste method. I am yet to test its speed but there is no visible lag when doing it so I am full of hope. EDIT: Tested, and it takes from 4-5 milliseconds to complete. Not bad, I expected worse, on 60 FPS it takes only one third of the computing power. EDIT2:Heh, I downgraded the Driver and OpenGL works. Yes, I am using the usual max2d drawing functions with AlphaBlend, and it's not working with OpenGL too. Unfortunately my knowledge about it is very sparse so I don't know how to activate drawing to alpha channel. |
| ||
So I take it that your room has semi transparent stuff in it? Grabbing images with alpha is a bit tricky in Blitz and unfortunately not really a real-time thing. What you have to remember is that the colour blitz draws to the backbuffer will be the colour after alpha has been applied. That means that when you grab the colour you need to restore it to its original value if you want to then paste it down again. Its possible, but it might not be fast enough for you. I think for it to be fast enough some changes would have to happen to the way blitz renders stuff. Anyway, for a start you need to create your graphics with an extra flag on top of the default ones called alphabuffer: graphics 800,600,0,0,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER | GRAPHICS_ALPHABUFFER Then you need a new cls command that will make blitz clear the alpha channel: (by default it makes fills the alpha channel): Function ClsWithAlpha(r:Float = 0.0, g:Float = 0.0, b:Float = 0.0, a:Float = 1.0) glClearColor r, g, b, a glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT End Function So you can cls the screen with ClsWithAlpha(0,0,0,0) instead of a normal cls. You also need to call glewinit() to give you access to some more openGL extensions so that you can do a couple of alternative blendmodes: Function SetBlendEXT(blend:Int) Select blend Case ALPHABLEND glEnable GL_BLEND glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) glDisable GL_ALPHA_TEST Case LIGHTBLEND glEnable GL_BLEND glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) glDisable GL_ALPHA_TEST End Select End Function You need to call that instead of the normal setblend in order to separate how the alpha and colour is blended to the screen. So, then once you've drawn what you want to the screen and grabbed your pixmap you then have to restore the colour values to what they were before alpha was blended using this function: Function restorecolor:TPixmap(pixmap:TPixmap) For Local x:Int = 0 To pixmap.width - 1 For Local y:Int = 0 To pixmap.height - 1 Local RGBA:Int = ReadPixel(pixmap, x, y) Local a:Int = (RGBA Shr 24) & $ff Local r:Int = (RGBA Shr 16) & $ff Local g:Int = (RGBA Shr 8) & $ff Local b:Int = RGBA & $ff If a r = Maximum(255, Float(r) / (Float(a) / 255)) g = Maximum(255, Float(g) / (Float(a) / 255)) b = Maximum(255, Float(b) / (Float(a) / 255)) RGBA = (a Shl 24) | (r Shl 16) | (g Shl 8) | b End If WritePixel(pixmap, x, y, RGBA) Next Next Return pixmap End Function You can then just loadimage(restoredpixmap) and drawimage that to the screen. So yeh, that'd probably be slower then just drawing 1200 images :) Describe in more detail the rooms you're drawing and there may be another solution to speed things up. |
| ||
Nice |
| ||
Yea, very interesting. But even without testing I can see it is slower than using TPixmap.paste() method (it has far less code). And also yesterday I found even faster way to do it, function in CodeArchive http://www.blitzbasic.com/codearcs/codearcs.php?code=2109 which instead of writing byte by byte uses MemCopy(), and so TPixmap.paste() took 4-5 milliseconds to complete and CopyImageRect() only 1-2 (when copying 192 25x25 tiles). So yeh, that'd probably be slower then just drawing 1200 images :) Describe in more detail the rooms you're drawing and there may be another solution to speed things up. Actually the 1200 was miscalculation, I want to do 768 draws (32x24 tiles of size 25x25 on 800x600 screen). Anyway, I wanted to make an exploration platformer with infinite scrolling, so there is no room transitions like in Metroid, and I wanted to leave as much for other graphic effects (that's why I planned to compress these 768 draws into around 9 draws of bigger images which turned out to be notably faster). But after realizing how much trouble this is going to be I just gave up on this idea and will resort to the good old rooms. Thanks for help though, that was surely enlightening :) |