Easiest way to drawn an image on another?

BlitzMax Forums/BlitzMax Programming/Easiest way to drawn an image on another?

Grey Alien(Posted 2007) [#1]
Hi it's late and I'm being a little dense so I wonder if you can help me please.

I have a newspaper with text and a picture which is a 32bit png image that I fade in and fade out over a background. I want to draw some dynamic content on the newspaper picture (so like a person). This dynamic content has to fade in and out with the main newspaper graphic and that's where the problem is.

Naturally I use SetBlend ALPHABLEND to daw the newspaper as it fades in and out. I use this to also draw the dynamic content on the newspaper. BUT it means that the dynamic content is drawn half faded over the half faded newspaper. This resulks in two things a) transparent dynamic content i.e. a see through person that you can see the newspaper picture through! and b) the newspaper picture and the dynamic contents colour values are added so that the dynamic content looks brighter!

Clearly the only way round this that I can see is to get the newspaper image and draw the dynamic content onto it beforehand and then use the resulting image to fade in/out on screen.

So how do I do that please?

I'm thinking:

1) I could draw the newspaper to the screen full alpha blend and then draw the dynamic content onto it and then grab it back to an image (via a pixmap?) but would it retain it's alpha channel so that I could draw it over a background later properly blended?

2) Draw the dynamic content directly onto the newspaper picture. Do I need the famous Render To Texture mod for that or can it be done some other way? (like with pixmaps?) The dynamic content must be drawn properly alpha blended onto the newspaper as I don't want jagged lines on it.

Any advice? Pointers? Many thanks in advance!


Derron(Posted 2007) [#2]
Although I already posted this some time ago:

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, alpha:Float = 1.0, light:Float = 1.0) 
      Local TempPix:TPixmap = Null
	  If framenr = 0 Then TempPix = LockImage(image) 
      If framenr > 0 Then TempPix = LockImage(image, Framenr) 
	  'TempPix.convert(image.pixmaps[0].format) 
	  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 sourceA = 255 * alpha Else sourceA = SourceA * alpha
	
			If sourceA <> - 1 Then
				If sourceA < - 1 Then sourceA = -sourceA
				Local destR:Int = ARGB_Blue(destpixel) 
				Local destG:Int = ARGB_Green(destpixel) 
				Local destB:Int = ARGB_Red(destpixel)
				Local SourceR:Int = ARGB_Blue(Sourcepixel)
				Local SourceG:Int = ARGB_Green(Sourcepixel)
				Local SourceB:Int = ARGB_Red(Sourcepixel)
				sourceR = Int(Float(sourceA / 255.0) * Float(sourceR * light)) + Int(Float((255 - sourceA) / 255.0) * Float(destR)) 
				sourceG = Int(Float(sourceA / 255.0) * Float(sourceG * light)) + Int(Float((255 - sourceA) / 255.0) * Float(destG)) 
				sourceB = Int(Float(sourceA / 255.0) * Float(sourceB * light)) + Int(Float((255 - sourceA) / 255.0) * Float(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



If it looks kind of akward, exchange ARGB_Blue and ARGB_Red (dunno why I switched them ... had something to do with false coloring).

The alpha-param is used to set the alpha of the "to-draw"-image. Light-param is used to make the image a bit darker (eg. 0.5 = 50% lightness).


The Image you draw onto the other one is alpha blended but to enable alpha in the image you draw to (so the newspaper) you will have to uncomment the variable in the code and add a calculation the same way it is done for r/g/b ... and finally in the write command you have to exchange 255 with the sourceA-calculation.

bye
MB


tonyg(Posted 2007) [#3]
1) Have you tried it? It seems to work with a few 'odd' missing pixels in windowed mode.Fullscreen seems OK.
2) RTT would make it easy.


Derron(Posted 2007) [#4]
Odd missing pixels?

I'm using this (so it works) to draw items on huge sprites to save some rendering-time (but to enable user-modifications WHERE some items are drawn (plants within a building, Doors...).

All Images I use are normal PNG-saved ones, loaded (or edited in the application but in same format, 24bpp).

RTT doesn't work with DX9 at the moment, else it's making problems on my onboard-chip SIS651. And you are not really able to create textures not fitting the whole screen (I was forced to create 800x600 which uses some MB of VideoRam, which wont be the thing Grey Alien wants to see ;D).


bye
MB


tonyg(Posted 2007) [#5]
I was responding to GA.
The odd missing pixels in windowed mode are a colour depth issue ... I think.

Actually, scrap that it has the nasty halo effect.
BTW : Who is using DX9? RTT works at DX7.


Grey Alien(Posted 2007) [#6]
TonyG: No I didn't try it, was seeking advice first due to limited time. However, I tried your code and it worked for me, no nasty halo effect. What are you seeing exactly?

I wonder what happens if it's run in windowed mode and the desktop depth isn't 32-bit...


Grey Alien(Posted 2007) [#7]
OK test results: Set desktop depth to 16bit and it ran fine, the alpha was fine. Wondering if my gfx card is REALLY doing 16bit? Because when I run full screen @ 16 bit I do not see the grabbed image when it is redisplayed.

Also (unrelated) I noticed that when I created a full screen graphics window at 24 bit, the called GraphicsDepth() to check it, it was actuall 32 bit! Must be my graphics card I guess.


tonyg(Posted 2007) [#8]
GA, my code is flawed. Try drawing the image on another coloured background.


Grey Alien(Posted 2007) [#9]
MichaelB: Ok I'm having trouble with this. If I load in a TPixmap with LoadPixmap and then use SetBlend ALPHABLEND before drawing it with DrawPixmap I see it on screen, but it is drawn without any alpha. This is even before I've touched your code. However I did try you code also but the object I drew on the pixmap came out with a blocky (non-alpha) outline, it was weird. Any ideas?

Here's my code:




Grey Alien(Posted 2007) [#10]
tonyg: Oh I see it grabs the entire background colour too, so clearly grabbing from the backbuffer doesn't preserve any alpha which is not what I want.


Dreamora(Posted 2007) [#11]
Pixmaps ignore alpha. Alpha only exists on Textures -> TImage.


tonyg(Posted 2007) [#12]
GA, what happens when you loadimage the pixmap and then draw the image?


Grey Alien(Posted 2007) [#13]
Dreamora: OK, thanks. Good job I know that now.

TonyG: Hey that works! I did LoadImage(p) where p is my pixmap and it draw with alpha. However the code from MicahelB still doesn't work for me.


tonyg(Posted 2007) [#14]
You really need Render-To-texture. Its VERY useful and is why quite a few people are unsure why it was never included in the first place.


Grey Alien(Posted 2007) [#15]
Sounds like it. I've just never used it before, guess I'll have to learn.


Derron(Posted 2007) [#16]
I'm checking your example... if using "LoadImage" instead LoadPixmap and then LockImage(Image) ... you can access the alpha channel.

The only thing I have to check is why the "drawn on the pixmap image" has a halo-effect... has to be something with my calculation ...
Will add alpha-calculation now.


bye
MB


Grey Alien(Posted 2007) [#17]
OK here's my code and the result I get. On the left the newspaper and gent are drawn with SetBlend as normal images, on the right I use your code to draw the gent onto the newspaper and then I draw that pixmap to the screen and it doesn't look right. Any ideas? This function of yours would be very useful if we could get it to work they way I want.




Derron(Posted 2007) [#18]
ok... after a long hour (with disturbing phone-calls) and tests after tests I finally used a small testimage and some debugprints ... until I found out that my color-functions did return wrong alpha-channel-values.

I'm also reworking the calculation because the alpha-pixel seem a bit darker than when alphablended... think it has something to do with the resulting alpha-value...

I'll post it here as soon as I got it finished.


bye
MB


Grey Alien(Posted 2007) [#19]
sounds great!


Derron(Posted 2007) [#20]
ok ... I think its not perfect (compared the "smoothed" lines of drawimage and mine drawn on the image) ... a bit darker anti-alias i think, but this may be also "their" (graphicscard) fault, not mine ;D - or just a bunch of rounding and so on.

For normal use you wont see a big difference.

The whole thing took so long to make it possible to draw an alpha-channel-image onto another image with alpha-channel.

The code below is similar to your example, but uses images from the samples, don't forget you may use light and alpha params to e.g. show "not reached" awards and so on.



Strict

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) 
      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
			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

Graphics 800, 600, 0

'Local Img1:TImage = LoadImage("colortest.png") 
Local Img1:TImage = LoadImage("samples\digesteroids\graphics\digestive.png") 
Local Img2:TImage = LoadImage("samples\breakout\media\B-Max.png") 
'Local Img2:TImage = LoadImage("colortestbg.png") 
Local PixMap:TPixmap = LockImage(Img2)
DrawOnPixmap(Img1, 0, PixMap, 170, 120, 0.5, 1.0) 
DrawOnPixmap(Img1, 0, PixMap, 70, 20, 1.0, 1.0) 
DrawOnPixmap(Img1, 0, PixMap, 270, 220, 1.0, 0.5) 
UnlockImage(Img2)
While Not KeyHit(key_Escape)
	Cls	
	SetBlend alphablend
	SetColor 255, 0, 255;DrawRect(10, 10, 300, 300) 
	SetColor 255, 0, 0;DrawRect(200, 200, 450, 300) 
	SetColor 0, 0, 255;DrawRect(300, 100, 200, 450) 
	SetColor 255,255,255
	DrawImage(Img2, MouseX() - ImageWidth(Img2) / 2, MouseY() - ImageWidth(Img2) / 2) 
	DrawImage(Img1, MouseX() - ImageWidth(Img2) / 2, MouseY() - ImageWidth(Img2) / 2) 
	Flip
Wend


bye
MB


ImaginaryHuman(Posted 2007) [#21]
There's nothing stopping you grabbing alpha from the backbuffer, eg glReadPixels() can read RGBA data, so long as the backbuffer is 32-bit not 24-bit.

I use it to test to make sure there is actually a destination alpha channel available.


Grey Alien(Posted 2007) [#22]
hmm, it is now darker and semi transparent and till has the rought edges so I guess it's a no go then.


Derron(Posted 2007) [#23]
???

Think you just forgot to set the params right:
alpha=1.0 and light=1.0

or you didnt copy the whole code (i changed the functions to get the alpha and color values)

in my 3 lines i show all 3 ones...
1.) is half alpha
2.) is normal
3.) is half brightness

DrawOnPixmap(Img1, 0, PixMap, 170, 120, 0.5, 1.0) 
DrawOnPixmap(Img1, 0, PixMap, 70, 20, 1.0, 1.0) 
DrawOnPixmap(Img1, 0, PixMap, 270, 220, 1.0, 0.5) 


Didnt the example work for you?



bye
MB


Grey Alien(Posted 2007) [#24]
Yes it works! I didn't copy the top functions, well spotted! Thanks very much, this works really well. Mind if I add it to my framework with a credit to you?


Derron(Posted 2007) [#25]
No credit needed...

Take it and post it back if you improve it (or find errors/mistakes in it).


bye
MB


Grey Alien(Posted 2007) [#26]
Very generous thanks. Your handle will be attached to the code anyway :-)


Trader3564(Posted 2008) [#27]
Perfect! Thanks a lot man, this is what i needed so badly. Really a great piece of work.

EDIT
ugh, ok i tested a bit more. But it seems that drawing RPG maps using tiles and a AnimImage even tough i have to run the DrawImage command 19*18*9 times, is faster.


TaskMaster(Posted 2008) [#28]
You are not going to draw on a Pixmap in realtime every frame. That would be very very slow.


Grey Alien(Posted 2008) [#29]
Yeah I wasn't using it realtime. I was precalcing an image from 2 separate ones that I want to scale/rotate and fade as a single image.


Trader3564(Posted 2008) [#30]
Im still using it :) for textbubbles. (single pre-render onCreate)