Copy From 1 TImage To Another?

BlitzMax Forums/BlitzMax Beginners Area/Copy From 1 TImage To Another?

MGE(Posted 2007) [#1]
Is there a way to copy a rect section from 1 TImage to another TImage? Thanks.


TomToad(Posted 2007) [#2]
You can lock the image to get a pixmap, which you can pass to PixmapWindow() to select a rectangular region, then use LoadImage() to load the pixmap into the other TImage

RectImage:TImage = LoadImage(PixmapWindow(LockImage(FirstImage),10,10,32,32))



tonyg(Posted 2007) [#3]
or the tpixmap paste method or drawimage/grabimage.


ImaginaryHuman(Posted 2007) [#4]
DrawImage and GrabImage is probably much faster.


Paposo(Posted 2007) [#5]
hello.

DrawImage and GrabImage involve plus data movement.
Please, if is plus faster i need understanding.

thanks,
Paposo


MGE(Posted 2007) [#6]
Yikes.. looks like it's going to be more difficult than I had planned. I've never used pixmaps, or locked images, so not quite sure where to go.

In a nut shell I have 2 textures (TImage) loaded, each 512x512 and I want to be able to copy any size rect from one to the other. Speed isn't an issue since it will happen between levels and not during game play. I use transparent png files so hopefully any solutions eventually will support that as well.

hmm.. in the copy function it looks as if I need to:

a) Convert both textures to Pixmaps.
b) Copy from 1 pixmap to the other as needed.
c) Convert the resulting pixmap back to the texture.

Notice I posted in the beginner section, :) so any help is much appreciated. Thanks.


Derron(Posted 2007) [#7]
Should be easy to adapt it to your needs...
think i didn't care about destinations alpha (only sources alpha).

To make it a DrawImageOnImage you will need to lock the given destination pixmap and use that instead of a given pixmap. The Rect thing has to be put into the for-loops (from i:int = xstart to xend).


Function ARGB_Alpha:Int(ARGB:Int)
 Return Int((ARGB & $FF000000:Int) / $1000000:Int)
End Function

Function ARGB_Red:Int(ARGB:Int)
 Return Int((ARGB & $00FF0000:Int) / $10000:Int)
End Function

Function ARGB_Green:Int(ARGB:Int)
 Return Int((ARGB & $0000FF00:Int) / $100:Int)
End Function

Function ARGB_Blue:Int(ARGB:Int)
 Return (ARGB & $000000FF:Int)
End Function

Function ARGB_Color:Int(alpha:Int,red:Int,green:Int,blue:Int)
 Return (Int(alpha*$1000000)+Int(blue*$10000)+Int(green*$100)+Int(RED))
End Function 

Function DrawOnPixmap(image:TImage,framenr:Int=0, Pixmap:TPixmap, x:Int, y:Int)
      Local TempPix:TPixmap = Null
	  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+1 < 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:Int = ARGB_Alpha(destpixel)
			Local sourceA:Int = ARGB_Alpha(sourcepixel)
			If sourceA <> -1 Then 
				If sourceA< -1 Then sourceA = -sourceA
				Local destR:Int = ARGB_Red(destpixel)
				Local destG:Int = ARGB_Green(destpixel)
				Local destB:Int = ARGB_Blue(destpixel)
				Local SourceR:Int = ARGB_Red(Sourcepixel)
				Local SourceG:Int = ARGB_Green(Sourcepixel)
				Local SourceB:Int = ARGB_Blue(Sourcepixel)
				sourceR = Int( Float(sourceA/255.0)*sourceR) + Int(Float((255-sourceA)/255.0)*destR)
				sourceG = Int( Float(sourceA/255.0)*sourceG) + Int(Float((255-sourceA)/255.0)*destG)
				sourceB = Int( Float(sourceA/255.0)*sourceB) + Int(Float((255-sourceA)/255.0)*destB)
				sourcepixel = ARGB_Color(255, 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




bye
MB


Grey Alien(Posted 2007) [#8]
I looked into this a while back and then realised I could code my game a different way without the need to copy an image.


TomToad(Posted 2007) [#9]
It might be best to keep a copy of both images in pixmaps and whenever the pixmaps are modified, then reload the pixmaps back into the images. That way you won't need to GrabImage() or LockImage() first.

This example uses MemCopy() to copy a row of pixels from one image to the next. Just use your own image in the example and point Const Filename to that image. The second image is just the first one flipped vertically. Click on either image to copy the cursor square from one to the other .




MGE(Posted 2007) [#10]
Thanks TomToad, the demo seems to work, but any way to wrap this up into one nice function such as:

CopyImageToImage(srcTImage:TImage,srcx:int,srcy:int,srcwidth:int,srcheight:int,dstTImage,dstx:int,dsty:int)

I would be most grateful if this could be done. And then you need to post it in the code archive for all to use. :) Thanks!


ImaginaryHuman(Posted 2007) [#11]
Anything you do with a pixmap is going to be much slower than anything you do with images. DrawImage and GrabImage are probably faster regardless of whether it seems like you're shifting more data around. The GPU is usually 10 times or more faster than dealing with pixmaps. The pixmaps have to be transferred over the graphics bus which is quite a slow process.


Grey Alien(Posted 2007) [#12]
hehe if someone "functionalises" it, I'll snaffle it too :-)

I always found GrabImage to be quite slow (especially on an 800x600 screen) like 200+ms, because it has to come down from VRAM to RAM which the GFX cards are not really designed for.


tonyg(Posted 2007) [#13]
If you're not worried about masking then you can do something like this:



Otherwise you can try this which I have cleaned up slightly:




ImaginaryHuman(Posted 2007) [#14]
GrabImage grabs a pixmap as well as an turning it into an image? It shouldn't have to. For example glCopyTexSubImage2D copies an area from the backbuffer into an existing texture without ever going to main memory and it's fast enough that you could do it at least a few times per frame at 60hz.


TomToad(Posted 2007) [#15]
thinking about this today and realized there was no need to store an extra copy of the pixmaps since the TImage already had a pixmap copy. When you LockImage(), it returns the copy of the pixmap, and the next time you draw the image, the pixmap will be copied into VRAM automatically.
So here is a Functionalized version, with some bounds checking as well.



MGE(Posted 2007) [#16]
Mr.Toad, someday when I get all BlitxMax braniac, I hope to return the favor. This is a very valuable function I'm sure others will have a need for at some point. Thank you for taking the time to code this. :)

Do you know if there is any scenario where this will not work? (16,24,32bit modes?, OGL or DX?) It appears to be cross platform compatible.

One thing I like about it, you could use it to copy over new sprite images from a source texture onto your game texture without having to change your main loop code. Speed is not an issue since this would be done between levels, etc. ;)


Grey Alien(Posted 2007) [#17]
Hot stuff. Thanks TomToad! Hope you don't mind but I've added it to my framework and credited you.


Dreamora(Posted 2007) [#18]
ImaginaryHuman: Yeah it is, but first its a GL only function and second I'm not sure if it is OpenGL 1.2 compliant which is what BM enforces on Max2D.
If one used the DX surface commands the whole stuff including setBuffer would be implementable again and work but then the GL user would be pissed again.
You see the problem? :)


TomToad(Posted 2007) [#19]
@MGE Developer:
I tried it out with 16, 24, and 32 bit modes. Both OpenGL and DirectX works. I couldn't test 24 bit on OpenGL because my card apparently only supports 16 and 32 bit modes with OpenGL.
I did think of one possible problem. It would be with byte alignment. Shouldn't be a problem now since I believe BMax is optimized for 32 bit on all platforms, but if it were to change and BMax were optimized for 64 bit, then the above code will break. I tried to locate a byte-align field for the pixmaps, but there doesn't seem to be one.
You could go the safe route and rewrite the above to use ReadPixel and WritePixel in a loop, but that would be much slower than MemCopy() and would bog down the program if you needed to copy a lot of data each loop.

@Grey Alien:
Thanks. Glad you can find some use for it. :)
In all your studying of BMAX, by chance have you figured out how to find the byte alignment of a pixmap?


Grey Alien(Posted 2007) [#20]
TomToad: No I'm afraid not. BMax is pretty big and most of my study has been high level objects or Windows API calls rather than graphics functions. I pretty sure something like that was possible in BPlus. Why not start a thread about it?


MGE(Posted 2007) [#21]
Using TomToad's suggestion of using Read/Write pixel, here's a modified version complete with 4 copy modes; Normal, Mirror,Flip,Mirror&Flip. Speed ofcourse is slower, but using it real time was never my intended use. Thanks again TomToad, this is the exact routine I had in mind when the topic was first created. :)

Function CopyImageToImage(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



Grey Alien(Posted 2007) [#22]
Hey so that's more advanced cool. Presume you've tested it. Now MGE Developer, may I place this in my framework please?


altitudems(Posted 2007) [#23]
I wonder if a render to texture routine would make this usable for realtime?


Dreamora(Posted 2007) [#24]
Yes but it will fail on a lot of systems (ie nearly any intel or other onboard which means 70%+ of all users)


Grey Alien(Posted 2007) [#25]
Why does render to texture fail on some systems?


TartanTangerine (was Indiepath)(Posted 2007) [#26]
Why does render to texture fail on some systems?
Not a problem with GFX cards nowadays, even integrated intel!


Dreamora(Posted 2007) [#27]
Explain that to those users not waisting money on new systems every 2 years.
Even thought, I bought my tablet in Jan 07 and have a GMA900 in, so don't bet on anything beeing available.

Until recently intel didn't even fullfill the T&L compliant level (GMA900 definitely does not or its drivers are just that crappy that they break the T part over and over again). And even now, Intel only supports exactly that part that is needed to get Microsofts Vista ready logo, not the least bit more *Intel X3100 CPU to VGA adapter erm GPU* (ie its ok for aero but still a class 4 GPU with any other onboard from SiS, S3 and Intel and far far behind even the worst ATI or NVIDIA onboard performance and capability wise. Even GeForce 4200 Ti were worlds faster)


ImaginaryHuman(Posted 2007) [#28]
Yes glCopyTexSubImage2D is OpenGL only, so no DX implementation, but it is a part of GL 1.2 and can be compiled with standard BlitzMax without any extensions. It's way faster than anything that could be done with the CPU.

If you are going to go with the CPU version, then I recommend getting rid of WritePixel and ReadPixel and replace them with pointer access ie basepointer[offset]=whatever. That removes the overhead of having to `look up` the pixmap for every single writepixel call and to calculate the offset based on coordinates. For a small routine that you're calling many thousands of times like that you should be looking to optimize it. Keeping one pointer and doing pointer access will show you a speed boost.


MGE(Posted 2007) [#29]
"Now MGE Developer, may I place this in my framework please?"

Ofcourse! I added it to mine as well. ;)


Grey Alien(Posted 2007) [#30]
thx


CS_TBL(Posted 2008) [#31]
*b0mp*

Function CopyImageToImage(Source:TImage ,SX:Int,SY:Int,SWidth:Int,SHeight:Int,Dest:TImage Var,DX:Int,DY:Int,flags:Int=0)

It needs Var :P I randomly needed a function like this today.. \o/