Login or Signup

Code archives/Graphics/TFont Antialias 2D font system for realtime

This code has been declared by its author to be Public Domain code.

Download source code

TFont Antialias 2D font system for realtime by Pinete(Posted 1+ years ago)
I have write this small library in order to obtain very beauty fonts even with small characters.
The Library can works prefectly for realtime and the results are impressive, from my point of view.
The functions could be very useful for games that needs to give a special attention to the text, like sports managers or something similar, even every scoreboard, popup windows in your games...
I hope you find this useful and someone improve this easy library!!
; ************************************************************
; * Project Name: TFont2D - Antialiased 2D text v1.0 for realtime
; * Author(s): Pinete
; * Date Started: -
; * Last Updated: -
; * Website: -
; * Email: 
; * Version: 1.0
; * Copyright: Donated to the public domain
; * Trademark: -
; * Product: -
; ************************************************************
; Based on the piece of code called "Anti-Alias Simulation"
; by Daniel Nobis who gives thanks to Triton für die Idee.
; I think I have improved the code to obtain a useful library for scoreboards,
; sports management games or, in general, games that need to use very small fonts or
; the text is important.
; I have tried to comment all the code.
; The library and all its functions are really easy to use and modify.
; The code is not brilliant, of course, and I'm sure that everybody with
; a little more knowledge than me can improve the functions a lot.
; At least I have achieve my first goal, doing my first contribution to the community.
; How it works?
; First a blank image is created, with the size of the character we will draw within.
; After that, we draw a rectangle with the color we want to antialiased with.
; Scale the image by half.
; Now, we mask the image with the background color.
; The result is the character with the antialiased pixels but whitout the background.
; That's the action that we will do with all the characters beetween 31 and 128
; And we will do again with differents background colors (from black to white)
; in order to obtain fonts with antialias for different backgrounds (five by default)
; We will do the same with the shadow.
; The last point is to draw the text.
; To do that, it reads the pixel of the graphic buffer (backbuffer, for example, and after draw the background)
; with the goal of obtain the most similar generated antialias to draw the character on to that background.
; From my point of view its usage is very comfortable and easy to manage and modify for a personal adaptation.
; It's very far to be fantastic but it is a way to print beauty strings at screen whitout use the 3Ds and
; with a very nice result.
; :)
; Should be fantastic if somebody could improve it in terms of speed or flexibility.
; Thanks to all that support blitbasic and blitzcoder community.

; The code has been writed using BB3D v1.87 and Protean Editor
; using four or five hours more or less.
; I hope you find this useful!

; List of functions
; -------------------
; TFontCreate
; TFontDraw
; TFontSet
; TfontGetWidth
; TFontShadow
; TFontShadowDistance
; TFontSetAutoGrad
; TFontSetGrad
; TFontDebugON
; TFontDebugOFF

Type TipoFont
	Field Tchar[255]
	Field Tlongx[255]
	Field Tlongy[255]
	Field TShadow[255]
	Field ent
	Field ref
End Type

Type TipoShadow
	Field Tchar[255]
	Field Tlongx[255]
	Field Tlongy[255]
	Field TShadow[255]
	Field ent
	Field ref
End Type

; Internal variables

Const GradSteps = 5						; Number of steps in which the antialias is precalculated (from black to white)
Const MaxFonts = 255					; Maximum fonts to be host
Dim fnt.TipoFont(GradSteps,MaxFonts)	; Host the fonts
Dim shw.TipoFont(GradSteps,MaxFonts)	; Host its shadows
Global TBoolShadow = -1					; Shadow Bool. -1 = OFF, 1 = ON
Global TShadowDistX = 1
Global TShadowDistY = 1
Global TFontDefaultGrad = 3				; Sets the default grad if it is not automatic
Global TFontFont = 1					; Current Font
Global TFontBoolDEBUG = -1				; Debug for each string
										; it will show the current grad level, x,y, 2D width and 2D Height

; Framerate variables

Const FPS=85
Const debug=0
Global FPS_Oldtime, FPS_Newtime, FPS_Ticks
Global FPS_Current,FPS_Final
Global FPS_SampleRate   = 5  ;Take a sample every N ticks
Global FPS_Samples      = 10 ;Samples to average (res of the average)
Global FPS_BufferIndex  = 1
Global FPS_Font
Dim FPS_Buffer(10)

Function TFontCreate.TipoFont(ref = 1,font,r1=255,g1=255,b1=255)
	; You need to call this function before your main loop starts.
	; To create a font, depending of the computer specs will need
	; 1 second (aprox). Surely you will need various fonts in your
	; game, and using 1 second per font, the creation of five fonts
	; would take about 5 or 6 seconds, more or less.
	;ref 	-	Is the "reference" of the fonts. It is a number that will identify the font.
	;			Each font need a different reference when it is going to be created
	;font 	-	The font handle returned by the BB function "Loadfont"
	;r1,g1,b1 - The RGB color of the font. The fonts cannot change its color in realtime because
	;			is a very slow process. In order to have various colours for the fonts, they need to
	;			be generated previously.

	; Init RGB and misc vars for the background antialias
	Local CharBegin = 31
	Local CharFinal = 122
	r = 0
	g = 0
	b = 0
	grad = 1
	; Select the font
	SetFont font
	; Generate the fonts with the antialias for the diferent gradients
	For gr = 1 To GradSteps
		fnt(gr,ref) = New TipoFont
		For c = CharBegin To CharFinal
			char$ = Chr$(c)
			fnt(grad,ref)\Tchar[c]  = CreateImage(StringWidth(char$),StringHeight(char$))
			fnt(grad,ref)\Tlongx[c] = StringWidth(char$)/2
			fnt(grad,ref)\Tlongy[c] = StringHeight(char$)
			SetBuffer ImageBuffer(fnt(grad,ref)\Tchar[c])
			Color r,g,b
			Rect 0,0,ImageWidth(fnt(grad,ref)\Tchar[c]),ImageHeight(fnt(grad,ref)\Tchar[c])
			Color r1,g1,b1
			Text 0,0,char$
			MaskImage fnt(grad,ref)\Tchar[c],r,g,b
			ScaleImage fnt(grad,ref)\Tchar[c],0.5,0.5
		;DebugLog ("-> Antialias created for RGB: "+Str$(r)+","+Str$(g)+","+Str$(b))
		r = r + Int(255/(GradSteps-1));63
		g = g + Int(255/(GradSteps-1))
		b = b + Int(255/(GradSteps-1))
		grad = grad + 1
	; Generate the Shadows for each font, again, with the same number of antialias levels
	; init RGB for the background antialias
	r = 0
	g = 0
	b = 0
	grad = 1
	SetFont font
	; Generate the antialias for the diferent gradients
	For gr = 1 To GradSteps
		shw(gr,ref) = New TipoFont
		For c = CharBegin To CharFinal
			char$ = Chr$(c)
			shw(grad,ref)\Tchar[c]  = CreateImage(StringWidth(char$),StringHeight(char$))
			shw(grad,ref)\Tlongx[c] = StringWidth(char$)/2
			shw(grad,ref)\Tlongy[c] = StringHeight(char$)
			SetBuffer ImageBuffer(shw(grad,ref)\Tchar[c])
			Color r,g,b
			Rect 0,0,ImageWidth(shw(grad,ref)\Tchar[c]),ImageHeight(shw(grad,ref)\Tchar[c])
			Color 50,50,50 ;The shadow color can be altered. 50,50,50 is not a pure black.
			Text 0,0,char$
			MaskImage shw(grad,ref)\Tchar[c],r,g,b
			ScaleImage shw(grad,ref)\Tchar[c],0.5,0.5
		;DebugLog ("-> Shadow Antialias created for RGB: "+Str$(r)+","+Str$(g)+","+Str$(b))
		r = r + Int(255/(GradSteps-1))
		g = g + Int(255/(GradSteps-1))
		b = b + Int(255/(GradSteps-1))
		grad = grad + 1
	SetBuffer BackBuffer()
End Function

Function TFontDraw(cad$,x,y)
	; It works like the standard BB function.
	; Just to pass the string (cad$) and its 2D coordinates.
	; Of course, it's a need to create the font with TcreateFont(...) previous to this call
	; GradType = 0 for automatic selection of Antialias Gradient
	; GradType = 1 to 5 for manual selection
	;			 1...Darkest (black background)
	;			 5...White

	Local cx,cy
	Local ref
	cx = x
	cy = y
	Local numchar
	Local GradTypeMode 
	GradTypeMode = TFontDefaultGrad
	ref = TFontFont
	; Let's see if we are debugging strings...
	If TFontBoolDEBUG > 0
		cad$ = cad$ + " -> "+Str$(TFontDefaultGrad)+", "+Str$(x)+", "+Str$(y)+", "+Str$(TfontGetWidth(cad$))
	If GradTypeMode <> 0 Then GradType = GradTypeMode
	For r = 1 To Len(cad$)
		numchar = Asc(Mid(cad$,r,1))
		If GradTypeMode = 0 Then GradType = TExtractColorSingle(cx+1,cy+1)
		If TBoolShadow > 0 Then DrawImage shw(GradType,ref)\Tchar[numchar],cx+TShadowDistX,cy+TShadowDistY
		DrawImage fnt(GradType,ref)\Tchar[numchar],cx,cy
		cx = cx + fnt(GradType,ref)\Tlongx[numchar]

End Function

Function TFontSet(nFont)
	; Sets the current font to use.
	TFontFont = nFont
End Function

Function TfontGetWidth(wcad$)
	; Return the 2D width in pixels of the string with the current font and with
	; This could be useful to detect clicks and something similar...
	Local ms
	Local numch
	Local ref
	ms = 0
	ref = TFontFont
	For r = 1 To Len(wcad$)
		numch = Asc(Mid(wcad$,r,1))
		ms = ms + fnt(1,ref)\Tlongx[numch]
	Return ms
End Function

Function TFontShadow(bs)
	; Activate or Deactivate Shadow for the current font.
	; Using a parameter lower or equal to 0 the shadows will be OFF
	; Using a parameter higher than 0 the shadows will be ON
	; Remember, using shadows is a bit slower than drawing the normal text
	; because you are drawing two strings!
	; However, if you are not managing a really great quantity of text at screen,
	; you could use shadows whitout any problem.
	If bs > 0 Or bs = True Then TBoolShadow = 1
	If bs <= 0 Or bs = False Then TBoolShadow = -1
End Function

Function TFontShadowDistance(xd,yd)

	; Just define the distance of the shadows
	; xd is the x distance in pixels
	; yd is the y distance in pixels
	; normal values are 1,1 or 2,2 for both parameters.

	TShadowDistX = xd
	TShadowDistY = yd
End Function

Function TExtractColorSingle(xe,ye)
	; Basically this function makes a very simple task.
	; Just read the pixel at the coordinates in which the character will be printed
	; in order to obtain its color information.
	; After that, it look for the best gradient of the generated text in order to
	; select the better visual result.
	; Check for the first point
	TRead1 = ReadPixelFast(xe,ye,BackBuffer())
	red#  =((TRead1 Shr 16) And $FF)
	green#=((TRead1 Shr 8)And $FF)
	blue# =((TRead1 And $FF))
	res1 = red + green + blue
	Pass = Int(255/(GradSteps-1))
	;Rgrad1 = (res1+63)/3/63
	Rgrad1 = (res1+Pass)/3/Pass
	RetGrad = Int(Rgrad1)+1
	If RetGrad>GradSteps Then RetGrad = GradSteps
	Return RetGrad
End Function

Function TExtractColorMultiple(xe,ye)
	; This function is identical to the previous one, just with the difference
	; that this time, it read two pixel and extract the media.
	; Check for the first point
	TRead1 = ReadPixelFast(xe+2,ye+2,BackBuffer())
	red#  =((TRead1 Shr 16) And $FF)
	green#=((TRead1 Shr 8)And $FF)
	blue# =((TRead1 And $FF))
	res1 = red + green + blue
	Rgrad1 = (res1+Int(255/(GradSteps-1)))/3/(Int(255/(GradSteps-1)))
	; Check for the second one
	TRead2 = ReadPixelFast(xe+6,ye+6,BackBuffer())
	red#  =((TRead2 Shr 16) And $FF)
	green#=((TRead2 Shr 8)And $FF)
	blue# =((TRead2 And $FF))
	res2 = red + green + blue
	Rgrad2 = (res2+Int(255/(GradSteps-1)))/3/(Int(255/(GradSteps-1)))
	; Make the media
	Rgrad = (Rgrad1 + Rgrad2) / 2
	RetGrad = Int(Rgrad)+1
	If RetGrad>GradSteps Then RetGrad = GradSteps
	Return RetGrad;Int(Rgrad)+1
End Function

Function TFontSetGrad(defgrad = 0)

	; Sets manually the gradient to use with the current font.
	; Default gradients works in the range from 1 to 5.
	; If the selected gradient is 0 the selection turn automatic for each character looking for
	; the best result-
	; Default is automatic (=0)
	If defgrad > GradSteps Then defgrad = GradSteps
	If defgrad < 0 Then deafgrad = 0
	TFontDefaultGrad = defgrad
	Return defgrad
End Function

Function TFontSetAutoGrad()
	; No parameters. Just call this function to set the automatic selection
	; of gradient for each character.
	TFontDefaultGrad = 0
End Function

Function TFontDebugON()
	; Activate the debug mode for strings.
	; Its a silly function that show, after draw each string, an addon that
	; contains info about its gradient, x and y coordinates and 2D width.
	TFontBoolDEBUG = -1
End Function

Function TFontDebugOFF()
	; Deactivate the debug mode for strings.
	TFontBoolDEBUG = 1
End Function

Function Get_FPS(PosX#=10,PosY#=2)
	; This function is not of my property.
	; It's a very useful function to scan the media of FPS.
	; I dont remember who is the original autor.
	; The credits goes for him as well, of course.
	; I'm sorry for this.
         FPS_Newtime = MilliSecs()
         FPS_Ticks = FPS_Ticks + 1
         If FPS_Ticks > FPS_SampleRate Then
            FPS_Current = FPS_Newtime - FPS_Oldtime
            If FPS_Current = 0 Then FPS_Current = 1000 Else FPS_Current = 1000/FPS_Current
            FPS_Buffer(FPS_BufferIndex) = FPS_Current
            FPS_BufferIndex = FPS_BufferIndex + 1
            If FPS_BufferIndex > FPS_Samples Then
                 For FPS_Count = 1 To FPS_Samples
                     FPS_Master = FPS_Master + FPS_Buffer(FPS_Count)                      
                 FPS_Final = FPS_Master / FPS_Samples
                 FPS_BufferIndex = 1
            FPS_Ticks = 0
         FPS_Oldtime = MilliSecs()
End Function


Ian Thompson(Posted 1+ years ago)
Excellent stuff, a wee hidden gem! :)

Code Archives Forum