Drawing one image onto another
BlitzMax Forums/BlitzMax Programming/Drawing one image onto another
| ||
I'm currently drawing to backbuffer, then drawing the second image on top, then grabbing it into a pixmap. Works, but I wonder if there's a better way? Plus I've heard tales about GrabPixmap not working on some hardware. Couple of points to consider: 1. I need to draw the second image onto the first with alpha. 2. I don't need to do it on-the-fly. 3. The code needs to draw several images onto another, with a grabPixmap at the end. Repeat x 108. |
| ||
I am using the following function to combine several layers of tiles into one tile. The first index of image array is the bottom layer. All images must be of the same size. For images with diferent sizes, the read-write pixel loop should be changed. Jure |
| ||
in an older library I use:Function ARGB_Alpha:Int(ARGB:Int) Return (argb Shr 24) & $ff 'Return Int((ARGB & $FF000000:Int) / $1000000:Int) End Function Function ARGB_Red:Int(ARGB:Int) Return (argb Shr 16) & $ff ' Return Int((ARGB & $00FF0000:Int) / $10000:Int) End Function Function ARGB_Green:Int(ARGB:Int) Return (argb Shr 8) & $ff ' Return Int((ARGB & $0000FF00:Int) / $100:Int) End Function Function ARGB_Blue:Int(ARGB:Int) Return (argb & $ff) ' Return (ARGB & $000000FF:Int) End Function Function ARGB_Color:Int(alpha:Int,red:Int,green:Int,blue:Int) Return (Int(alpha * $1000000) + Int(RED * $10000) + Int(green * $100) + Int(blue)) End Function Function DrawOnPixmap(image:TImage, framenr:Int = 0, Pixmap:TPixmap, x:Int, y:Int, alpha:Float = 1.0, light:Float = 1.0, multiply:Int = 0) Local TempPix:TPixmap = Null If image = Null Then Throw "image doesnt exist" If framenr = 0 Then TempPix = LockImage(image) If framenr > 0 Then TempPix = LockImage(image, Framenr) For Local i:Int = 0 To ImageWidth(image) - 1 For Local j:Int = 0 To ImageHeight(image) - 1 If x + i < pixmap.width And y + j < pixmap.Height 'And i >= x And j >= y Local sourcepixel:Int = ReadPixel(TempPix, i, j) Local destpixel:Int = ReadPixel(pixmap, x+i,y+j) Local destA:Float = ARGB_Alpha(destpixel) Local sourceA:Float = ARGB_Alpha(sourcepixel) * alpha If sourceA = 255 Then destA = 0 'remove comment to remove unneeded calculations 'but only when light/alpha not used! ' If sourceA <> 255 And sourceA <> 0 Local destR:Float = ARGB_Red(destpixel) Local destG:Float = ARGB_Green(destpixel) Local destB:Float = ARGB_Blue(destpixel) Local SourceR:Float = ARGB_Red(Sourcepixel) Local SourceG:Float = ARGB_Green(Sourcepixel) Local SourceB:Float = ARGB_Blue(Sourcepixel) Local AlphaSum:Int = destA + sourceA If multiply = 1 sourceR = (sourceR * light * sourceA / AlphaSum) + destA / AlphaSum * (destR * destA / AlphaSum) sourceG = (sourceG * light * sourceA / AlphaSum) + destA / AlphaSum * (destG * destA / AlphaSum) sourceB = (sourceB * light * sourceA / AlphaSum) + destA / AlphaSum * (destB * destA / AlphaSum) Else sourceR = (sourceR * light * sourceA / AlphaSum) + (destR * destA / AlphaSum) sourceG = (sourceG * light * sourceA / AlphaSum) + (destG * destA / AlphaSum) sourceB = (sourceB * light * sourceA / AlphaSum) + (destB * destA / AlphaSum) EndIf If AlphaSum > 255 Then AlphaSum = 255 sourcepixel = ARGB_Color(AlphaSum, SourceR, sourceG, sourceB) ' EndIf If SourceA <> 0 Then WritePixel(Pixmap, x + i, y + j, sourcepixel) EndIf Next Next If framenr = 0 UnlockImage(image) If framenr > 0 UnlockImage(image, framenr) End Function It draws an image into a pixmap. bye MB |
| ||
Isn't the pixel-by-pixel approach incredibly slow? |
| ||
I really think somebody should nail the RTT omission. Can you use the pixmap paste method? I never know what allows alpha and what doesn't. Maybe a memcopy would work using the pixelptr value. I wrote some truly horrible code here which might be of use. |
| ||
I used one of these two (can't remember which) in Fairway. The image was pre-calced before the screen was shown, wasn't slow enough to notice:' ----------------------------------------------------------------------------- ' ccCopyImageRect: Copies part of an image onto another image ' by James Chamblin ' ----------------------------------------------------------------------------- Function ccCopyImageRect(Source:TImage,SX:Int,SY:Int,SWidth:Int,SHeight:Int,Dest:TImage,DX:Int,DY:Int) 'get the pixmap for the images Local SourcePix:TPixmap = LockImage(Source) Local DestPix:TPixmap = LockImage(Dest) 'find the dimentions Local SourceWidth:Int = PixmapWidth(SourcePix) Local SourceHeight:Int = PixmapHeight(SourcePix) Local DestWidth:Int = PixmapWidth(DestPix) Local DestHeight:Int = PixmapHeight(DestPix) If SX < SourceWidth And SY < SourceHeight And DX < DestWidth And DY < DestHeight 'make sure rects are on image If SX+SWidth > SourceWidth Then SWidth = SourceWidth - SX 'bound the coordinates to the image area If SY+SHeight > SourceHeight Then SHeight = SourceHeight - SY If DX+SWidth > DestWidth Then SWidth = DestWidth - DX 'Make sure coordinates will fit into the destination If DY+SHeight > DestHeight Then SHeight = DestHeight - DY 'find the pitch Local SourcePitch:Int = PixmapPitch(SourcePix) Local DestPitch:Int = PixmapPitch(DestPix) 'pointers To the first pixel of pixmaps Local SourcePtr:Byte Ptr = PixmapPixelPtr(SourcePix) + SY * SourcePitch + SX * 4 Local DestPtr:Byte Ptr = PixmapPixelPtr(DestPix) + DY * DestPitch + DX * 4 'copy pixels over one line at a time For Local i:Int = 1 To SHeight MemCopy(DestPtr,SourcePtr,SWidth*4) SourcePtr :+ SourcePitch DestPtr :+ DestPitch Next End If 'unlock the buffers UnlockImage(Source) UnlockImage(Dest) End Function ' ----------------------------------------------------------------------------- ' ccCopyImageToImage: Copies one TImage to another with a variety of modes ' by Dave Munsie ' ----------------------------------------------------------------------------- Function ccCopyImageToImage(Source:TImage,SX:Int,SY:Int,SWidth:Int,SHeight:Int,Dest:TImage,DX:Int,DY:Int,flags:Int=0) ' ' flags: 0 = Normal 1 = Mirror 2 = Flip 3 = Mirror and Flip ' Local SourcePix:TPixmap = LockImage(Source) Local DestPix:TPixmap = LockImage(Dest) Local SourceWidth:Int = PixmapWidth(SourcePix) Local SourceHeight:Int = PixmapHeight(SourcePix) Local DestWidth:Int = PixmapWidth(DestPix) Local DestHeight:Int = PixmapHeight(DestPix) If SX < SourceWidth And SY < SourceHeight And DX < DestWidth And DY < DestHeight If SX+SWidth > SourceWidth Then SWidth = SourceWidth - SX If SY+SHeight > SourceHeight Then SHeight = SourceHeight - SY If DX+SWidth > DestWidth Then SWidth = DestWidth - DX If DY+SHeight > DestHeight Then SHeight = DestHeight - DY Select flags Case 0 ' Normal For Local py:Int = 0 To SHeight-1 For Local px:Int = 0 To SWidth- 1 WritePixel(DestPix,DX+px,DY+py,ReadPixel(SourcePix,SX+px,SY+py)) Next Next Case 1 ' Mirror For Local py:Int = 0 To SHeight-1 For Local px:Int = 0 To SWidth- 1 WritePixel(DestPix,DX+px,DY+py,ReadPixel(SourcePix,(SX+(SWidth-1))-px,SY+py)) Next Next Case 2 ' Flip For Local py:Int = 0 To SHeight-1 For Local px:Int = 0 To SWidth- 1 WritePixel(DestPix,DX+px,DY+py,ReadPixel(SourcePix,SX+px,(SY+(SHeight-1))-py)) Next Next Case 3 ' Mirror and Flip For Local py:Int = 0 To SHeight-1 For Local px:Int = 0 To SWidth- 1 WritePixel(DestPix,DX+px,DY+py,ReadPixel(SourcePix,(SX+(SWidth-1))-px,(SY+(SHeight-1))-py)) Next Next End Select EndIf UnlockImage(Source) UnlockImage(Dest) End Function |
| ||
Can you use the pixmap paste method? I *think* I may have tried that. It's months since I did this stuff and only now am I getting to fixing it up properly. :/I'll give it a try. [edit] No alpha with tPixmap.Paste(). |
| ||
The code above works with alpha. |
| ||
The code above works with alpha. Yeah I'll have to try those tomorrow and see how they shape up speedwise. [edit] Just tried them now. Neither of them do what I want (and they're both largely the same anyway). The zero alpha pixels of the image obliterate anything else that was there before, when those pixels should remain untouched. That's exactly the same result as tPixmap.paste(). MichaelB's works, but it isn't fast. 32ms to paste a 128x128 image. Its saving grace is that it avoids the need for GrabPixmap. |
| ||
Oh, I just checked and I used ccDrawOnPixmap for Fairway and it seemed fine with a transparent image on another image, but perhaps I missed something. |
| ||
You don't really need to define your own code for copying one pixmap onto another - Blitz already provides pixmap windows/static pixmaps, where you can say that one pixmap is a window within an existing 2nd pixmap, and then using pixmap.paste should I presume clip to the window. Whether you call memcopy to copy a whole row of pixels, or do it in your own loop, probably isn't much different seeming as the bottleneck is memory accesses. |
| ||
Like mentioned within my Code... To speed it up you have to remove some comments - it's only for me using the method to precalculate some sprites which get dynamically positioned on some backgroundsprites. If you "deactivate" the calculations for multiplying and lightning my code should get a small boost - but then you end up ignoring alpha channels, but since its "pixel by pixel" it will never win the race ;D. I don't think there is a proper "faster" way of doing it without touching the whole part of allocated memory, pointers and some magic calculations with no need to read RGBA separately. So one my test if he/she can calculate the resulting color as ONE integer without separating RGBA - this will speed up things. But like mentioned, I don't use my code for real-time, just for precalculation. And yes, it's no problem to colorize the sprites giving additional parameters for percentively change RGB-Values (eg creating different colorized playersprites placed on one image for performance issues). bye MB |
| ||
.. in case you're still looking this might help. |
| ||
Somewhere in some old thread I posted cost to do an alphablended zooming rotating pixmap onto another using pointer access, it should be pretty quick. See here: http://www.blitzbasic.com/Community/posts.php?topic=59342#661050 |
| ||
Just a quick note - decided to use MichaelB's code but with some tiny modifications. I've added in a scaling parameter, and also modified it to take into account an image's handle. I don't need rotation (yet) so I didn't add it.Function ARGB_Alpha:Int(ARGB:Int) Return (argb Shr 24) & $ff 'Return Int((ARGB & $FF000000:Int) / $1000000:Int) End Function Function ARGB_Red:Int(ARGB:Int) Return (argb Shr 16) & $ff ' Return Int((ARGB & $00FF0000:Int) / $10000:Int) End Function Function ARGB_Green:Int(ARGB:Int) Return (argb Shr 8) & $ff ' Return Int((ARGB & $0000FF00:Int) / $100:Int) End Function Function ARGB_Blue:Int(ARGB:Int) Return (argb & $ff) ' Return (ARGB & $000000FF:Int) End Function Function ARGB_Color:Int(alpha:Int,red:Int,green:Int,blue:Int) Return (Int(alpha * $1000000) + Int(RED * $10000) + Int(green * $100) + Int(blue)) End Function Function DrawOnPixmap(image:TImage, framenr:Int = 0, scale:Float, Pixmap:TPixmap, x:Int, y:Int, alpha:Float = 1.0, light:Float = 1.0) Local TempPix:TPixmap = Null If image = Null Then Throw "image doesnt exist" If framenr = 0 Then TempPix = LockImage(image) If framenr > 0 Then TempPix = LockImage(image, Framenr) If scale <> 1 TempPix = ResizePixmap(TempPix,PixmapWidth(TempPix) * scale,PixmapHeight(TempPix) * scale) EndIf x:-(image.handle_x * scale) y:-(image.handle_y * scale) For Local i:Int = 0 To PixmapWidth(tempPix ) - 1 For Local j:Int = 0 To PixmapHeight(tempPix ) - 1 If x + i < Pixmap.width And y + j < Pixmap.Height Local sourcepixel:Int = ReadPixel(TempPix, i,j) Local destpixel:Int = ReadPixel(Pixmap, x+i,y+j) Local destA:Float = ARGB_Alpha(destpixel) Local sourceA:Float = ARGB_Alpha(sourcepixel) * alpha If sourceA = 255 Then destA = 0 'remove comment to remove unneeded calculations 'but only when light/alpha not used! ' If sourceA <> 255 And sourceA <> 0 Local destR:Float = ARGB_Red(destpixel) Local destG:Float = ARGB_Green(destpixel) Local destB:Float = ARGB_Blue(destpixel) Local SourceR:Float = ARGB_Red(Sourcepixel) Local SourceG:Float = ARGB_Green(Sourcepixel) Local SourceB:Float = ARGB_Blue(Sourcepixel) Local AlphaSum:Int = destA + sourceA sourceR = (sourceR * light * sourceA / AlphaSum) + destA / AlphaSum * (destR * destA / AlphaSum) sourceG = (sourceG * light * sourceA / AlphaSum) + destA / AlphaSum * (destG * destA / AlphaSum) sourceB = (sourceB * light * sourceA / AlphaSum) + destA / AlphaSum * (destB * destA / AlphaSum) If AlphaSum > 255 Then AlphaSum = 255 sourcepixel = ARGB_Color(AlphaSum, SourceR, sourceG, sourceB) ' EndIf If SourceA <> 0 Then WritePixel(Pixmap, x + i, y + j, sourcepixel) EndIf Next Next If framenr = 0 UnlockImage(image) If framenr > 0 UnlockImage(image, framenr) End Function |