How can I draw text on a TImage and retain Alpha?

BlitzMax Forums/BlitzMax Programming/How can I draw text on a TImage and retain Alpha?

Grey Alien(Posted 2006) [#1]
Hi. I have an image of a scroll which is a png so the edges are alphablended and there are full transparent areas too. I'd like to draw some text onto the scroll but I have no idea how to do this!

I could cls the screen, draw the scroll, then the text, then regrab the whole thing but then the grabbed image will have no alpha data so I won't be able to use it properly in-game anymore...

So is there a way to draw text directly (using DrawText) onto an image using maybe Pixmaps or some DirectX render to texture jiggery pokery? I'm using draw text because the actual text changes all the time, I can't predraw it outside of the game.

Any advice much appreciated. Thanks :-)


AlexO(Posted 2006) [#2]
didn't know grabimage lost alpha data :o.
hmmm, I know blitzplus allowed this to be solved easily where in bMax it takes a little more effort. Alot of times if I want combined images I just make a type with a custom render() method that just renders each part inidivually but keeps them relative to each other. Would that work or do you specifically need the image to contain the text in its pixels?


tonyg(Posted 2006) [#3]
Why not draw the scroll, draw the text, grab a rectangular portion of the scroll containing the text but not the transparent bits?


Grey Alien(Posted 2006) [#4]
Alex O. well I haven't tested it, but it MUST loose transparency as soon as it's drawn you see.

Tonyg, ah yes good plan, thanks! Will work well unless I have wide text say at the top of the scroll, and the middle of the scroll curves in more than the wide text at the top. So in principle the question still remains for general use even though it's solved for my use for now...


tonyg(Posted 2006) [#5]
There's this...
Pixmap paste with mask although it's not quick for large images. RTT is a possibility.
For the wider than the top stuff can't you...
1) Draw rectangle with, for example, colour 255,0,255
2) Draw the text
3) Grab the area of text.
4) Setmaskcolor 255,0,255
5) Loadimage the text.
6) Draw Scroll
7) Draw Text image.


Grey Alien(Posted 2006) [#6]
tonyg: Thanks. I do need to predraw each frame so the pixmap paste with mask could be a bit slow I guess. As for the other idea it sounds viable except that the text is normally drawn with alphablend onto the scroll for nice edges and wouldn't that mask technique result in a hard edge?


tonyg(Posted 2006) [#7]
How often will you be changing the text? How much text is there? How big is the scroll? How often will the scroll move? How many ms do you have to work with?
How about posting some code pointing out what you want to improve?
How about using a bitmap font system where your font is created over a transparent background?
How about using DX multitexturing? What about the Tim's RTT module?


Grey Alien(Posted 2006) [#8]
well like I said for me your first solution will work. I was just wondering for the future if it would be possible to have an irregular shaped image and draw text on it retaining all alpha (text/image) without using a viewport, but I guess you could draw each line of text and capture each line in separate rectangle of course.

Looks liek I'll also have to look into RTT soon as I still haven't done it.


ImaginaryHuman(Posted 2006) [#9]
If your display is 32-bit color, and only that mode, your grabbing of the image should copy the alpha channel as well. Otherwise there is no alpha to grab in the destination.

At least in GL you can draw to a 32-bit screen that has an alpha channel, using a 32-bit scroll image with alpha, but as `solidblend` and with alpha channel enabled. Then draw the text the same. Then glcopyTexsubimage2d() would copy it back into the texture as RGB with alpha. I don't know how you'd do it in direct x.

You have to have some way of drawing the alpha, from both sources, into the alpha of your destination, whether its the backbuffer or the original texture.

Using pixmaps you can do a custom copy routine which does an ADD on the alpha channel and a REPLACE on the RGB channels. Might need to divide the alpha by 2. Or make a `combiner` routine, takes the two sources and outputs to a third pixmap that you use to draw with.

OR you can just have two images. Your original scroll image, render your text to another image with alpha channel, then just drawimage twice. ????


tonyg(Posted 2006) [#10]
Still can't se why you can't draw the text to a black screen, grab it into an image and then draw them both.
Rubbish example but it might give you an idea.
Graphics 640 , 480
SetColor 200 , 150 , 150
DrawRect 0 , 0 , 400 , 400
image:TImage = LoadImage(GrabPixmap(0 , 0 , 400 , 400))
Cls
SetColor 0 , 0 , 0
DrawRect 0 , 0 , 400 , 400
SetColor 255 , 255 , 255
DrawText "Hello World" , 100 , 100
image2:TImage = LoadImage(GrabPixmap(0 , 0 , 400 , 400) )
SetClsColor 255,0,0
Cls
DrawImage image , 150 , 0
DrawImage image2 , 0 , 0
Flip
WaitKey()


p.s. the text can be drawn with a bitmap font with transparent background... I think.


Grey Alien(Posted 2006) [#11]
AngelDaniel@ Oh, so a 32-bit display retains alpha information, I should have realised that. Hmm, the only problem is my game may start in 16 bit mode if 32 mode cannot be found on the PC, so I can't rely on that :-( Also if it's in windowed mode the desktop could be at 24bit or 16bit of course.

Tonyg: Because I'm drawing in a bigger font with alpha blend on for smooth edges and if I do that on black then grab it and redraw on an image, it'll have a dark outline round the text instead of blending with the underlying image.


tonyg(Posted 2006) [#12]
ahhh OK so I'd go back to having a seperate image for the scroll paper and the edges.


Grey Alien(Posted 2006) [#13]
yeah I think that solution was a winner for it's simplicity (which I couldn't see haha)


Kernle 32DLL(Posted 2010) [#14]
Altough this thread is 3 years old, I have now the same problem. Since DrawText is pretty slow compared to the other Draw functions i intended to precache frequently used texts as an image.

Right now, i create this image kinda like this:

TextImage = CreateImage(TextWidth(Text) , TextHeight(Text))
SetColor(0,0,0) ; DrawRect(0 , 0 , TextWidth(Text) , TextHeight(Text))
SetColor(255,255,255) ; DrawText(Text,0,0)
GrabImage(TextImage,0,0)

But as stated in the posts above, the alpha-channel isnt "grabbed" (i suppose the backbuffer has no alpha-channel, which is understandable).

Looking up the actual method of drawing the text, i quickly gave up due to the fact that the drawing is (of course) handled trough the driver, which i cant reconstruct (e.g. for pixmap use).

So, same question - 3 years later - how do i draw text onto an image or pixmap?


ImaginaryHuman(Posted 2010) [#15]
If you open a 32-bit screen the backbuffer DOES have an alpha channel. It's just a matter of whether blitz will transfer the alpha channel. Make sure to add ALPHA_BUFFER as a flag when opening your screen, then try it.


Dreamora(Posted 2010) [#16]
Though the alpha_buffer flag does not work on all cards so if this is for BF targeted game, you potentially won't be able to use it due to their regular no end hardware requirements.


Kernle 32DLL(Posted 2010) [#17]
Either this wont work or i'm just stupid and doing it wrong.

I backed a little code with a sample font, and i got those nasty black borders, you can download it HERE

You may notice that using the default (Null) Font dosnt produce any borders. Check it out.

Hold L.Shift to toggle between using the created image or drawtext, you should clearly see the difference.


Jesse(Posted 2010) [#18]
I am shure that you can figure out the rest:
Graphics 800,600
SetImageFont(LoadImageFont("Default.ttf" , 32))
Global font:timagefont = GetImageFont()
Global g:TMax2DGraphics = tmax2dgraphics.Current()
SetRotation 45
SetScale 4,4
draw("this is a test",300,300)
Flip()
WaitKey()
Function Draw( text$,x#,y#)
	For Local i%=0 Until text.length
	
		Local n%=font.CharToGlyph( text[i] )
		If n<0 Continue
		Local glyph:TImageGlyph=font.LoadGlyph(n)
		If glyph._image
			Local tx#=glyph._x*g.tform_ix+glyph._y*g.tform_iy
			Local ty#=glyph._x*g.tform_jx+glyph._y*g.tform_jy			
			DrawImage glyph._image,x+tx,y+ty
		EndIf
		x:+glyph._advance*g.tform_ix
		y:+glyph._advance*g.tform_jx
	Next
End Function

use your own font or comment it out for the default BB font.
this is really low level stuff so if Mark desides to modify the way the font module works this code will break.
[edited]
updated the code
I just copied this from one of my files it contained unneeded instructions.