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:
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!
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!