Any way I can improve on this?

BlitzMax Forums/BlitzMax Programming/Any way I can improve on this?

A few months back I posted about trying to save an image with alpha for a particle editor I've been creating. I thought I'd cracked it but it wasn't actually any good because it didn't work with images (I only tested with drawoval and setalpha). Anyway since then I've been busy redoing the interface in wxWidgets and now I'm back trying to figure this problem out. I'm very nearly there but it's still not quite perfect.

I'm using Klepto's render to texture code and have included 2 new blendmodes that use glBlendFuncSeparateEXT which separates how the colour and alpha is added to the buffer (this won't work on older cards but not really bothered about that at the moment). But also what I have to do is restore the colour values on the texture back to what they would have been before any blending took place. I think this is where inaccuracies are creeping in as I have to divide each colour by the alpha value.

Please someone tell me I'm missing something blindingly obvious that makes all this easy :)

here's my test code:

SuperStrict

TImageBuffer.Init(1024, 512)

Global Img:TImage = CreateImage(512, 512)

Local IB:TImageBuffer = TImageBuffer.SetBuffer(Img)
MidHandleImage(Img)
Local pixmap:TPixmap = CreatePixmap(512, 512, PF_BGRA8888)

'Create a nice colourful image to test with
Local testimage:TImage = testimg()
MidHandleImage(testimage)

'Draw the testimage to the imagebuffer a few times using alpha and lightblend in the same scene
IB.BindBuffer()
IB.Cls(0, 0, 0, 0)
ib.SetBlend(LIGHTBLEND)
DrawImage testimage, 256, 256
SetRotation 180
DrawImage testimage, 256, 256
ib.SetBlend(ALPHABLEND)
SetRotation 0
DrawImage testimage, 256, 256
SetRotation 180
DrawImage testimage, 256, 256

'grab the pixmap before we unbind the buffer
pixmap=GrabPixmap(0,0,512,512)

IB.UnBindBuffer() 

SetViewport 0, 0, 1024, 512

pixmap = restorecolor(pixmap)
Img = LoadImage(pixmap)
MidHandleImage(Img)
SavePixmapPNG(pixmap,"test2.png")


While Not KeyHit(KEY_ESCAPE) 
	SetClsColor 0, 0, 0

	Cls
	'recreate what we drew on the image buffer on the back backbuffer
	SetBlend LIGHTBLEND
	SetRotation 0
	DrawImage testimage, 256, 256
	SetRotation 180
	DrawImage testimage, 256, 256
	SetBlend ALPHABLEND
	SetRotation 0
	DrawImage testimage, 256, 256
	SetRotation 180
	DrawImage testimage, 256, 256
	SetRotation 0
	'and draw the image next to it to compare
	DrawImage Img, 768, 256
	Flip
	
Wend

Type TImageBuffer
	Field Image:TImage
	Field rb:Int[1]
	Field fb:Int[1]
	Field Imageframe:TGLImageframe
	Field Frame:Int = 0
	Field OrigX:Int
	Field OrigY:Int
	Field OrigW:Int
	Field OrigH:Int

	Function SetBuffer:TImageBuffer(Image:TImage,Frame:Int = 0 )
		Local IB:TImageBuffer = New TImageBuffer
		IB.Image = Image
		IB.Frame = Frame
		IB.GenerateFBO()
		IB.BindBuffer()
		Return IB
	End Function
	
	Function Init(Width:Int,Height:Int,Bit:Int=0,Mode:Int=60)
		SetGraphicsDriver(GLMax2DDriver())
		Graphics Width , Height,bit,Mode
		glewInit()
	End Function
	
	Method GenerateFBO()
		ImageFrame = TGLImageFrame(Image.frame(Frame) )
			
		imageframe.v0 = imageframe.v1
		imageframe.v1 = 0.0
	
		Local W:Int = Image.width
		Local H:Int = Image.Height
		
		AdjustTexSize(W , H) 

		
		glGenFramebuffersEXT(1, fb )
	    glGenRenderbuffersEXT(1 , rb) 
	   
	    glBindTexture(GL_TEXTURE_2D, Imageframe.name);
	    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT , fb[0]) ; 
	   
	   
	 	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,  Imageframe.name, 0);
	    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rb[0]);
	    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, W, H);
	    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT , GL_DEPTH_ATTACHMENT_EXT , GL_RENDERBUFFER_EXT , rb[0])
	   
	    Local status:Int =  glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)
	   
	    Select status
	       Case GL_FRAMEBUFFER_COMPLETE_EXT 
	          Print "all right" + " : " + Status
	       Case GL_FRAMEBUFFER_UNSUPPORTED_EXT
	          Print "choose different formats"
	       Default
	          End 
	    EndSelect 
   
	End Method
	
	Method BindBuffer()
		GetViewport(OrigX,OrigY,OrigW,OrigH)
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT , fb[0])
		glMatrixMode GL_PROJECTION
		glLoadIdentity
		glOrtho 0,Image.Width,Image.Width,0,-1,1
		glMatrixMode GL_MODELVIEW 
		glViewport(0 , 0 , Image.Width , Image.Height)
		glScissor 0,0, Image.Width , Image.Height
	End Method
	
	Method UnBindBuffer()
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT , 0)
		glMatrixMode GL_PROJECTION
		glLoadIdentity
		glOrtho 0,OrigW ,Origh,0,-1,1
		glMatrixMode GL_MODELVIEW 
		glViewport(0 , 0 , OrigW, OrigH)
		glScissor 0,0, OrigW ,OrigH
	End Method
	
	Method Cls(r#=0.0,g#=0.0,b#=0.0,a#=1.0)
		glClearColor r,g,b,a
		glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
	End Method
	
	Method BufferWidth:Int()
		Return Image.Width
	End Method
	
	Method BufferHeight:Int()
		Return Image.Height
	End Method
	
	Method SetBlend(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 Method

End Type


Function AdjustTexSize( width:Int Var,height:Int Var )
	'calc texture size
	width=Pow2Size( width )
	height=Pow2Size( height )
	Repeat
		Local t:Int
		glTexImage2D GL_PROXY_TEXTURE_2D,0,4,width,height,0,GL_RGBA,GL_UNSIGNED_BYTE,Null
		glGetTexLevelParameteriv GL_PROXY_TEXTURE_2D,0,GL_TEXTURE_WIDTH,Varptr t
		If t Return
		If width=1 And height=1 RuntimeError "Unable to calculate tex size"
		If width>1 width:/2
		If height>1 height:/2
	Forever
End Function

Function Pow2Size:Int( n:Int )
	Local t:Int=1
	While t<n
		t:*2
	Wend
	Return t
End Function

Function testimg:timage()
	Local pixmap:TPixmap = CreatePixmap(515, 512, PF_RGBA8888)
	For Local x:Int = 0 To 511
		For Local y:Int = 0 To 511
			Local RGB:Int = (y / 2 Shl 24) | (x / 2 Shl 16) | (y / 2 Shl 8) | 255 - (x / 2)
			WritePixel(pixmap, x, y, RGB)
		Next
	Next
	Return LoadImage(pixmap)
End 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
			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
			WritePixel(pixmap, x, y, RGBA)
		Next
	Next
	Return pixmap
End Function

Function Maximum:Int(m:Int, v:Int)
	If v > m
		Return m
	Else
		Return v
	End If
End Function



If you run it the image on the left is how it should look, the image on the right is the image that was rendered to and grabbed.

Thanks for any help!