BitMap Fonts

BlitzMax Forums/BlitzMax Programming/BitMap Fonts

John G(Posted 2012) [#1]
Could someone kindly point me to a simple explanation for using BitMap Fonts? I've been using Windows fonts which fail when running a Win game on a Mac under CrossOver Mac. Otherwise, very promising.
Thanks


Jesse(Posted 2012) [#2]
http://www.blitzmax.com/Community/posts.php?topic=76018#849767


John G(Posted 2012) [#3]
@Jesse. Thanks so much. That's a newer thread than one I stumbled on yesterday and more focused. You're a real helper around these parts!


GfK(Posted 2012) [#4]
I really like Ziggy's FontMachine for bitmap fonts. You get it free when you buy a BLIdePlus licence, and it works with Monkey, too.


Zeke(Posted 2012) [#5]


and example:

:D Hehe VERY small bitmapfont...

Last edited 2012


John G(Posted 2012) [#6]
@GfK: I'm not trying to make fonts; just use simple, readable, available (Arial, any) fonts in 2 sizes, 26 letters -- perhaps with color. Is Brl.FreetypeFont BitMaped? Easy to try?

@Zeke: Need up to 20 (forget what they're called).
Thanks

Needs Edit: 26 capital letters A to Z plus numbers 0 to 9

Last edited 2012


Alberto-Diablo(Posted 2012) [#7]
bitmapfont #1 (monospaced, but 100% compability max2d)

Local text$ = "A regular width font"

Graphics 800 , 600 , , 60
'load font
Local font:TImageFont_FixedWidth  = LoadFixedWidthFont( "Courier_10px_regular.bmp", 32,126,8,12 )
'activate font
SetImageFont font
'calc text width in pixels
Local mx = TextWidth(text)

Flip
DrawText( "nvnsdvuisdvuis", 100, 100 )
While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)
	Cls
	DrawText( text, (GraphicsWidth()-mx)/2, 100 )
	Flip
Wend
End


Type TImageFont_FixedWidth Extends TImageFont
	'Field _glyphs:TImageGlyph[]
	Field _height#
	Field _char_start%
	Field _char_end%
	Field _char_width#
	
	Method Style%()
		Return 0
	End Method
	
	Method Height%()
		Return _height
	End Method

	Method CountGlyphs%()
		Return _glyphs.length
	End Method
	
	Method CharToGlyph%( char% )
		If char >= _char_start And char <= _char_end Return char-_char_start
		Return -1
	End Method

	Method LoadGlyph:TImageGlyph( index% )
		Assert index >= 0 And index < _glyphs.length
		Return _glyphs[index]
	End Method

	Method Draw( text$,x#,y#,ix#,iy#,jx#,jy# )

		For Local i%=0 Until text.length
		
			Local n%=CharToGlyph( text[i] )
			If n% < 0 Continue
			
			Local glyph:TImageGlyph=LoadGlyph(n)
			Local image:TImage=glyph._image
			
			If image
				Local frame:TImageFrame=image.Frame(0)
				If frame
					Local tx#=glyph._x*ix+glyph._y*iy
					Local ty#=glyph._x*jx+glyph._y*jy			
					frame.Draw 0,0,image.width,image.height,x+tx,y+ty
				EndIf
			EndIf
			
			x:+glyph._advance*ix
			y:+glyph._advance*jx
		Next
		
	End Method

End Type

Function LoadFixedWidthFont:TImageFont_FixedWidth( url:Object, char_start%=32, char_end%=126, char_width%=8, char_height%=12, flags%=-1 )
	
	Local font:TImageFont_FixedWidth = New TImageFont_FixedWidth
	
	font._char_start = char_start
	font._char_end = char_end
	
	Local length% = font._char_end - font._char_start + 1
	Assert length > 0
	
	font._char_width = char_width
	font._height = char_height
	
	Local img:TImage = LoadAnimImage(url, char_width, char_height, 0, length, flags)
	Assert img
	
	font._glyphs = New TImageGlyph[length]
	
	For Local i%=0 Until length
		
		Local gliph:TImageGlyph = New TImageGlyph

		gliph._image = LoadImage( img.pixmaps[i], flags )
		gliph._advance = char_width
		gliph._x = 0
		gliph._y = 0
		gliph._w = char_width
		gliph._h = char_height
		
		font._glyphs[i]=gliph
	Next
	
	Return font
	
End Function


bitmapfont #2 (new version)


Rem

 (с) 2012 Альберт Гаскаров. Все права защищены.
 
 Лицензионное соглашение дает Вам право распространять файлы с любыми Вашими 
 приложениями, которые в свою очередь обязаны распространяться со следующей 
 суб-лицензией: "Все файлы включенный в данное приложение является 
 интеллектуальной собственностью автора и не может распространятся отдельно 
 от данного приложения без личного разрешения автора." Также ЗАПРЕЩЕНО 
 распространять все остальные файлы, включенные в данный архив (дистрибутив)
 без личного разрешения автора.
 
 ВНИМАНИЕ! ПОЖАЛУЙСТА ПРОЧТИТЕ ВНИМАТЕЛЬНО:КОПИРУЯ ИЛИ ИСПОЛЬЗУЯ ЛЮБЫМ ИНЫМ 
 ОБРАЗОМ ПРОГРАММНЫЙ ПРОДУКТ, ПОСТАВЛЯЕМЫЙ С ДАННЫМ ЛИЦЕНЗИОННЫМ СОГЛАШЕНИЕМ, 
 ВЫ (КАК ЮРИДИЧЕСКОЕ ИЛИ ФИЗИЧЕСКОЕ ЛИЦО), СОГЛАШАЕТЕСЬ СО ВСЕМИ УСЛОВИЯМИ 
 НАСТОЯЩЕГО ЛИЦЕНЗИОННОГО СОГЛАШЕНИЯ (ДАЛЕЕ ПО ТЕКСТУ "СОГЛАШЕНИЕ"), 
 ПРИВЕДЕННЫМИ НИЖЕ, ОТНОСИТЕЛЬНО ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ПРОДУКТА 
 (ДАЛЕЕ ПО ТЕКСТУ "ПО"). ЕСЛИ ВЫ НЕ СОГЛАСНЫ ПРИНЯТЬ НА СЕБЯ УСЛОВИЯ СОГЛАШЕНИЯ,
 ВЫ НЕ ИМЕЕТЕ ПРАВА ИСПОЛЬЗОВАТЬ ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ В КАКИХ ЛИБО 
 ЦЕЛЯХ И ОБЯЗАНЫ УДАЛИТЬ ВСЕ КОПИИ ПРОГРАММНОГО ПРОДУКТА С КОМПЬЮТЕРОВ И 
 НОСИТЕЛЕЙ, ПРИНАДЛЕЖАЩИХ ВАМ.
 
EndRem

SuperStrict

Rem'
	bbdoc: Графические шрифты.
EndRem
Module api.imagefont

ModuleInfo "Версия   : 1.37"
ModuleInfo "Автор    : Альберт Гаскаров"
ModuleInfo "Лицензия : MIT"
ModuleInfo "Права    : (С) Dynamic bytes"
ModuleInfo "Сервер   : API"

ModuleInfo "История: 1.37"
ModuleInfo "История: Введена поддержка логики True & False"
ModuleInfo "История: 1.36"
ModuleInfo "История: Полностью изменен синтаксис тэгов"
ModuleInfo "История: {bold}жирный текст{\bold}"
ModuleInfo "История: [alpha, 0.5]прозрачный текст[\alpha]"
ModuleInfo "История: [color, 255, 0, 0]красный текст[\color]"
ModuleInfo "История: [blend, ALPHABLEND]текст с измененным блендом[\blend]"
ModuleInfo "История: [uline, space, offset]подчеркнутый текст[\uline]"
ModuleInfo "История: [mline, space, offset]зачеркнутый текст[\mline]"
ModuleInfo "История: 1.31"
ModuleInfo "История: Фиксирован баг в методе Width(), теперь метод не спотыкается на незакрытых тэгах"
ModuleInfo "История: 1.30"
ModuleInfo "История: Некоторые улучшения"
ModuleInfo "История: 1.28"
ModuleInfo "История: Добавлена функция SetFontBoldModel() - устанавливает модель отрисовки жирного текста текущему шрифту"
ModuleInfo "История: Функция SetBoldModel() упразнена"
ModuleInfo "История: 1.27"
ModuleInfo "История: Добавлена функция SetBoldModel() - позволяющая вручную устанавливать жирность текста"
ModuleInfo "История: 1.26"
ModuleInfo "История: Добавлен тэг <!bold, state = 0> - позволяющий рисовать жирный текст"
ModuleInfo "История: 1.24"
ModuleInfo "История: Теперь управление цветом реализован через стек, допускается любая канфигурация данных тэгов"
ModuleInfo "История: 1.20"
ModuleInfo "История: Добавлен тэг <!underline, state = 0, space = 0, offset = 0> - позволяющий подчеркивать нужный участок текста"
ModuleInfo "История: Добавлен тэг <!midleline, state = 0, space = 0, offset = 0> - позволяющий зачеркивать нужный участок текста"
ModuleInfo "История: аргумент 'state' - включает/выключает режим подчеркивания/зачеркивания"
ModuleInfo "История: аргумент 'space' - включает/выключает режим подчеркивания/зачеркивания пробелов"
ModuleInfo "История: аргумент 'offset' - устанавливает смещение рисуемой линии по оси Y"
ModuleInfo "История: если нет ни одного аргумента то режим подчеркивания/зачеркивания выключается"
ModuleInfo "История: 1.15"
ModuleInfo "История: Доработана функция FormatString(), теперь учитываются '~n' - характеры во входной строке"
ModuleInfo "История: 1.14"
ModuleInfo "История: Добавлена функция FormatStringHeight%(ls%=0)"
ModuleInfo "История: Доработан метод Height()"
ModuleInfo "История: 1.12"
ModuleInfo "История: Добавлено динамическая загрузка ключ-цвета"
ModuleInfo "История: 1.11"
ModuleInfo "История: Доработано вычисление текущего угла трансформации"
ModuleInfo "История: 1.10"
ModuleInfo "История: Класс TImageFont перемещен в приватную секцию"
ModuleInfo "История: Добавлен, приватный Null шрифт"
ModuleInfo "История: Изменена функция загрузки шрифта"
ModuleInfo "История: Проведена оптимизация отрисовки шрифтов"
ModuleInfo "История: Изменена функция StringHeight()"
ModuleInfo "История: Доработан тэг <!color>, агрументы можно пропускать"
ModuleInfo "История: Завершено документирование"
ModuleInfo "История: Модуль обновлен для BlitzMAX версии 1.39"
ModuleInfo "История: 1.06"
ModuleInfo "История: Введена поддержка форматированого текста"
ModuleInfo "История: Изменения в функциях DrawString() и DrawStringRect()"
ModuleInfo "История: 1.05"
ModuleInfo "История: Добавлен дефолтный шрифт"
ModuleInfo "История: Введена поддержка тэгов"
ModuleInfo "История: <!color, r = Red(), g = Green(), b = Blue()>  - установка цвета (количество аргументов любое)"
ModuleInfo "История: <!color>                                      - возврат к первоначальному цвету"
ModuleInfo "История: <!alpha, a = GetAlpha()>                      - установка альфы"
ModuleInfo "История: <!alpha>                                      - возврат к первоначальной альфе"
ModuleInfo "История: <!blend, blend = GetBlend()>                  - установка бленда"
ModuleInfo "История: <!blend>                                      - возврат к первоначальному бленду"
ModuleInfo "История: 1.02"
ModuleInfo "История: Добавлен вспомогательный класс TVector"
ModuleInfo "История: Введена полная поддержка трансформаций"
ModuleInfo "История: 1.0"
ModuleInfo "История: Первый выпуск"

Import brl.max2d
Import brl.pixmap

Incbin "default.png"

Public

Rem'
	bbdoc: Обьект шрифта.
	about: .
EndRem
Type TFastFont Abstract
	Rem'
	bbdoc: Кернинг шрифта (расстояние между буквами).
	about: .
	EndRem
	Field kerning:Int = 0
	
	Rem'
	bbdoc: Расстояние между строками.
	about: .
	EndRem
	Field linespace:Int = 1
	
	Rem'
	bbdoc: Расстояние между строками.
	about: .
	EndRem
	Field tags:String = "[]" '{tag} <tag> (tag) [tag] `tag` @tag@ #tag# $tag$ %tag% ^tag^ &tag& *tag* +tag+ &tag; other ?
	
	Rem'
	bbdoc: Расстояние между строками.
	about: .
	EndRem
	Field bold:Float = 0.5
	
	Rem'
	bbdoc: Загрузить файл шрифта.
	returns: Обьект шрифта.
	about: .
	EndRem
	Method Width:Int(txt:String) Abstract
	
	Rem'
	bbdoc: Загрузить файл шрифта.
	returns: Обьект шрифта.
	about: .
	EndRem
	Method Height:Int(txt:String) Abstract
	
	Rem'
	bbdoc: Загрузить файл шрифта.
	returns: Обьект шрифта.
	about: .
	EndRem
	Method Format:String(txt:String, w:Int, h:Int Var) Abstract
	
	Rem'
	bbdoc: Загрузить файл шрифта.
	returns: Обьект шрифта.
	about: .
	EndRem
	Method Draw:Int(txt:String, x:Float, y:Float, centerx:Byte = False, centery:Byte = False) Abstract
End Type

Rem'
	bbdoc: Загрузить файл шрифта.
	returns: Обьект шрифта.
	about: .
EndRem
Function LoadFont:TFastFont(url:Object = Null, flags:Int = -1)
	Return New TFontW.Load(url, flags)
End Function

Private

Type TFontW Extends TFastFont Final
	Global dpixmap:TPixmap

	Global scalex:Float
	Global scaley:Float
	Global rotation:Float
	
	Global hVector:Vector2 = New Vector2
	Global vVector:Vector2 = New Vector2
	
	'Global underlinespace:Byte
	'Global underlineoffset:Int
	'Global midlelinespace:Byte
	'Global midlelineoffset:Int
	
	Global uline:Byte
	Global mline:Byte
	Global fbold:Byte
	
	Field _height:Int
	Field _chars:Short[256, 3]
	'Field _bold:Int[] =[1, 0, 0, 0, 0, 0, 0, 0]
	Field _image:TImage
	
	Method Delete()
		kerning = Null
		linespace = Null
		tags = Null
		bold = Null
		_height = Null
		_chars = Null
		_image = Null
	End Method
	
	Function Free()
		dpixmap = Null
		scalex = Null
		scaley = Null
		rotation = Null
		hVector = Null
		vVector = Null
		uline = Null
		mline = Null
		fbold = Null
	End Function
	
	Method Load:TFontW(url:Object, flags:Int = -1)
		Local x:Int, y:Int, i:Int, xyw:Int[]
		If (url = Null)
			If Not dpixmap Then dpixmap = LoadPixmap("incbin::default.png")
			url = dpixmap
		End If
		_image = LoadImage(url, flags)
		Local pixmap:TPixmap = _image.pixmaps[0]
		Local keycolor:Int = pixmap.ReadPixel(0, 0)
		For y = 1 Until pixmap.HEIGHT
			If pixmap.ReadPixel(0, y) = keycolor
				_height = y - 1
				Exit
			End If
		Next
		y = 0
		Repeat
			xyw = ReadLetter(x, y, keycolor)
			If xyw
				_chars[i, 0] = xyw[0]
				_chars[i, 1] = xyw[1]
				_chars[i, 2] = xyw[2]
				x = xyw[0] + xyw[2]
				i:+1
			Else
				y:+_height + 1
				x = 0
			End If
		Until (i = 256)
		Return Self
	End Method
	
	Method ReadLetter:Int[] (x:Int, y:Int, keycolor:Int)
		Local xyw:Int[3]
		xyw[0] = x - ~0
		xyw[1] = y - ~0
		For Local px:Int = x + 1 Until _image.pixmaps[0].width
			If _image.pixmaps[0].ReadPixel(px, y) = keycolor
				xyw[2] = px - xyw[0]
				Return xyw
			End If
		Next
	End Method
	
	Method Width:Int(txt:String)
		Local length:Int = txt.Length
		If length = 0 Then Return 0
		Local w:Int = 0, i:Int, d:Int, lw:Int
		For i = 0 Until length
			d = txt[i]
			If d > 255 Then d:-848
			If d > 255 Then d = 32
			If tags.Length = 2
				If d = tags[0]
					i = txt.Find(tags[1..], i - ~0)
					If i = -1 Then Return i
					Continue
				End If
			End If
			If d = 10
				lw = Max(lw, w - kerning)
				w = 0
				Continue
			End If
			w:+(_chars[d, 2] + kerning)
		Next
		Return Max(lw, w - kerning)
	End Method
	
	Method Height:Int(txt:String)
		Local lines:String[] = txt.Split("~n")
		Return (lines.Length * _height) + (linespace * (lines.Length + ~0))
	End Method
	
	Method Format:String(txt:String, w:Int, h:Int Var)
		Local lastSpacePos:Int = -1
		Local cw:Int = 0
		Local lines:Int = 1
		For Local i:Int = 0 Until txt.Length
			Local d:Int = txt[i]
			If d > 255 Then d:-848
			If d = 32 Then lastSpacePos = i
			If tags.Length = 2
				If d = tags[0]
					i = txt.Find(tags[1..], i - ~0)
					If i = -1 Then Throw "Tag closer not found '" + tags[1..] + "'"
					Continue
				End If
			End If
			If d = 10
				cw = 0
				lines:-~0
				'lastSpacePos = i
				Continue
			End If
			cw:+_chars[d, 2] + kerning
			If (cw >= w)
				If (lastSpacePos <> - 1)
					txt = txt[..lastSpacePos] + "~n" + txt[lastSpacePos - ~0..]
					i = lastSpacePos
					lastSpacePos = -1
					cw = 0
					lines:-~0
				End If
			End If
		Next
		h = (lines * _height) + (linespace * (lines + ~0))
		Return txt
	End Method
	
	Method Draw:Int(txt:String, x:Float, y:Float, centerx:Byte = False, centery:Byte = False)
		If txt = "" Then Return 0
		GetScale(scalex, scaley)
		rotation = GetRotation()
		TAlpha.Start()
		TBlend.Start()
		TColor.Start()
		fbold = False
		uline = False
		mline = False
		Local lines:String[] = txt.Split("~n")
		DrawAll(lines, x, y, centerx, centery)
		rem
		If (scalex = 1.0) & (scaley = 1.0)
			If (rotation = 0.0)
				DrawSimple(lines, x, y, centerx, centery)
			Else
				DrawRotation(lines, x, y, centerx, centery)
			End If
		Else
			If (rotation = 0.0)
				DrawScale(lines, x, y, centerx, centery)
			Else
				DrawAll(lines, x, y, centerx, centery)
			End If
		End If
		end rem
		TAlpha.Finish()
		TBlend.Finish()
		TColor.Finish()
		Return ((lines.Length * _height) + (linespace * (lines.Length + ~0))) * scaley
	End Method
	
	Method Parse(args:String[]) 'Parser
		Select args[0].Trim()
			Case "bold" fbold = True
			Case "\bold", "/bold" fbold = False
			Case "alpha" If args.Length = 2 Then TAlpha.SetAlpha(args[1].ToFloat())
			Case "\alpha", "/alpha" TAlpha.GetAlpha()
			Case "color"
				Local r:Int, g:Int, b:Int
				TColor.Color(r, g, b)
				Select args.Length
					Case 2
						If args[1].Trim() <> "" Then r = args[1].ToInt()
					Case 3
						If args[1].Trim() <> "" Then r = args[1].ToInt()
						If args[2].Trim() <> "" Then g = args[2].ToInt()
					Case 4
						If args[1].Trim() <> "" Then r = args[1].ToInt()
						If args[2].Trim() <> "" Then g = args[2].ToInt()
						If args[3].Trim() <> "" Then b = args[3].ToInt()
				End Select
				TColor.SetColor(r, g, b)
			Case "\color", "/color" TColor.GetColor()
			Case "blend"
				If args.Length >= 2
					Select args[1].Trim()'.ToUpper()
						Case "maskblend" TBlend.SetBlend(MASKBLEND)
						Case "solidblend" TBlend.SetBlend(SOLIDBLEND)
						Case "alphablend" TBlend.SetBlend(ALPHABLEND)
						Case "lightblend" TBlend.SetBlend(LIGHTBLEND)
						Case "shadeblend" TBlend.SetBlend(SHADEBLEND)
						Case "" ' nothing
						Default TBlend.SetBlend(args[1].ToInt())
					End Select
				End If
			Case "\blend", "/blend" TBlend.GetBlend()
			Case "uline" uline = True
			Case "\uline", "/uline" uline = False
			Case "mline" mline = True
			Case "\mline", "/mline" mline = False
		End Select
	End Method
	
	Method DrawSimple(txt:String[], x:Float, y:Float, centerx:Byte, centery:Byte)
		Local curx:Float = x
		Local h:Int = 0
		Local line:String
		If (centery = 1) Then y:-((_height Shr 1) * txt.Length + (linespace Shr 1) * (txt.Length + ~0))
		If (centery = 2) Then y:-((_height * txt.Length) + (linespace * (txt.Length + ~0)))
		For line = EachIn txt
			Local lineLen:Int = line.Length
			If centerx
				Local w:Int = Width(line)
				If centerx = 1
					curx:-w Shr 1
				ElseIf centerx = 2
					curx:-w
				End If
			End If
			Local i:Int
			For i = 0 Until lineLen
				Local d:Int = line[i]
				If d > 255 Then d:-848
				If d > 255 Then d = 32
				If tags.Length = 2
					If d = tags[0]
						Local s:Int = i - ~0
						i = line.Find(tags[1..], s)
						If i = -1 Then Throw "Tag closer not found '" + tags[1..] + "'"
						Parse(line[s..i].ToLower().Split(","))
						Continue
					End If
				End If
				If d <> 32
					DrawSubImageRect(_image, curx, y + h, _chars[d, 2], _height, _chars[d, 0], _chars[d, 1], _chars[d, 2], _height)
					
				End If
				
				curx:+(_chars[d, 2] + kerning)
			Next
			h:+(_height + linespace)
			curx = x
		Next
	End Method
	
	Method DrawScale(txt:String[], x:Float, y:Float, centerx:Byte, centery:Byte)
		Local curx:Float = x
		Local h:Int = 0
		Local line:String
		If (centery = 1) Then y:-((_height Shr 1) * txt.Length + (linespace Shr 1) * (txt.Length + ~0)) * scaley
		If (centery = 2) Then y:-((_height * txt.Length) + (linespace * (txt.Length + ~0))) * scaley
		For line = EachIn txt
			Local lineLen:Int = line.Length
			If centerx
				Local w:Int = Width(line)
				If centerx = 1
					curx:-w Shr 1
				ElseIf centerx = 2
					curx:-w
				End If
			End If
			Local i:Int
			For i = 0 Until lineLen
				Local d:Int = line[i]
				If d > 255 Then d:-848
				If d > 255 Then d = 32
				If tags.Length = 2
					If d = tags[0]
						Local s:Int = i - ~0
						i = line.Find(tags[1..], s)
						If i = -1 Then Throw "Tag closer not found '" + tags[1..] + "'"
						Parse(line[s..i].ToLower().Split(","))
						Continue
					End If
				End If
				If d <> 32
					
				End If
				curx:+(_chars[d, 2] + kerning) * scalex
			Next
			h:+(_height + linespace) * scaley
			curx = x
		Next
	End Method
	
	Method DrawRotation(txt:String[], x:Float, y:Float, centerx:Byte, centery:Byte)
		hVector.SetAngle(rotation)
		vVector.SetAngle(rotation + 90.0)
		Local curx:Float = x, cury:Float = y
		If centery = 1 Then cury:-(((_height Shr 1) * txt.Length) + ((linespace Shr 1) * (txt.Length - 1)))
		If centery = 2 Then cury:-((_height * txt.Length) + (linespace * (txt.Length - 1)))
		Local rvVector:Vector2 = vVector.MulS(cury - y)
		Local line:String, lineLen:Int, i:Int, d:Int
		For line = EachIn txt
			lineLen = line.Length
			If centerx
				Local w:Int = Width(line)
				If centerx = 1 Then curx:-(w Shr 1)
				If centerx = 2 Then curx:-w
			End If
			For i = 0 Until lineLen
				d = line[i]
				If d > 255 Then d:-848
				If d > 255 Then d = 32
				If tags.Length = 2
					If d = tags[0]
						Local s:Int = i - ~0
						i = line.Find(tags[1..], s)
						If i = -1 Then Throw "Tag closer not found '" + tags[1..] + "'"
						Parse(line[s..i].ToLower().Split(","))
						Continue
					End If
				End If
				If d <> 32
					Local rVector:Vector2 = hVector.MulS(curx - x)
					
				End If
				curx:+(_chars[d, 2] + kerning)
			Next
			cury:+(_height + linespace)
			rvVector = vVector.MulS(cury - y)
			curx = x
		Next
	End Method
	
	Method DrawAll(txt:String[], x:Float, y:Float, centerx:Byte, centery:Byte)
		hVector.SetAngle(rotation)
		vVector.SetAngle(rotation + 90.0)
		Local curx:Float = x, cury:Float = y
		If centery = 1 Then cury:-(((_height Shr 1) * txt.Length) + ((linespace Shr 1) * (txt.Length - 1))) * scaley
		If centery = 2 Then cury:-((_height * txt.Length) + (linespace * (txt.Length - 1))) * scaley
		Local rvVector:Vector2 = vVector.MulS(cury - y)
		Local line:String, lineLen:Int, i:Int, d:Int
		For line = EachIn txt
			lineLen = line.Length
			If centerx
				Local w:Int = Width(line)
				If centerx = 1 Then curx:-(w Shr 1) * scalex
				If centerx = 2 Then curx:-w * scalex
			End If
			For i = 0 Until lineLen
				d = line[i]
				If d > 255 Then d:-848
				If d > 255 Then d = 32
				If tags.Length = 2
					If d = tags[0]
						Local s:Int = i - ~0
						i = line.Find(tags[1..], s)
						If i = -1 Then Throw "Tag closer not found '" + tags[1..] + "'"
						Parse(line[s..i].ToLower().Split(","))
						Continue
					End If
				End If
				Local rVector:Vector2 = hVector.MulS(curx - x)
				If d <> 32
					If fbold
						rem
						DrawSubImageRect(_image, x + rVector.x + rvVector.x + bold, y + rVector.y + rvVector.y + bold,  ..
						_chars[d, 2], _height, _chars[d, 0], _chars[d, 1], _chars[d, 2], _height)
						
						DrawSubImageRect(_image, x + rVector.x + rvVector.x - bold, y + rVector.y + rvVector.y - bold,  ..
						_chars[d, 2], _height, _chars[d, 0], _chars[d, 1], _chars[d, 2], _height)
						
						DrawSubImageRect(_image, x + rVector.x + rvVector.x + bold, y + rVector.y + rvVector.y - bold,  ..
						_chars[d, 2], _height, _chars[d, 0], _chars[d, 1], _chars[d, 2], _height)
						
						DrawSubImageRect(_image, x + rVector.x + rvVector.x - bold, y + rVector.y + rvVector.y + bold,  ..
						_chars[d, 2], _height, _chars[d, 0], _chars[d, 1], _chars[d, 2], _height)
						endrem
						DrawSubImageRect(_image, x + rVector.x + rvVector.x + (bold * scalex), y + rVector.y + rvVector.y,  ..
						_chars[d, 2], _height, _chars[d, 0], _chars[d, 1], _chars[d, 2], _height)
						rem
						DrawSubImageRect(_image, x + rVector.x + rvVector.x - bold, y + rVector.y + rvVector.y,  ..
						_chars[d, 2], _height, _chars[d, 0], _chars[d, 1], _chars[d, 2], _height)
						
						DrawSubImageRect(_image, x + rVector.x + rvVector.x, y + rVector.y + rvVector.y + bold,  ..
						_chars[d, 2], _height, _chars[d, 0], _chars[d, 1], _chars[d, 2], _height)
						
						DrawSubImageRect(_image, x + rVector.x + rvVector.x, y + rVector.y + rvVector.y - bold,  ..
						_chars[d, 2], _height, _chars[d, 0], _chars[d, 1], _chars[d, 2], _height)
						end rem
					End If
					DrawSubImageRect(_image, x + rVector.x + rvVector.x, y + rVector.y + rvVector.y,  ..
					_chars[d, 2], _height, _chars[d, 0], _chars[d, 1], _chars[d, 2], _height)
				End If
				
				If uline
					Local aVector:Vector2 = vVector.MulS(cury - y + _height * scaley)
					Local k:Int = kerning
					If i = lineLen - 1 Then k = 0
					DrawRect(x + rVector.x + aVector.x, y + rVector.y + aVector.y, _chars[d, 2] + k, 1.0)
				End If
				If mline
					Local mVector:Vector2 = vVector.MulS(cury - y + ((_height + 2) Shr 1) * scaley)
					Local k:Int = kerning
					If i = lineLen - 1 Then k = 0
					DrawRect(x + rVector.x + mVector.x, y + rVector.y + mVector.y, _chars[d, 2] + k, 1.0)
				End If
				
				curx:+(_chars[d, 2] + kerning) * scalex
			Next
			cury:+(_height + linespace) * scaley
			rvVector = vVector.MulS(cury - y)
			curx = x
		Next
	End Method
End Type

Type Vector2
	Field x:Float
	Field y:Float
	
	Method Delete()
		x = Null
		y = Null
	End Method
	
	Method Create:Vector2(x:Float = 0.0, y:Float = 0.0)
		Self.x = x
		Self.y = y
		Return Self
	End Method
	
	Method SetAngle(angle:Float = 0.0)
		x = Cos(angle)
		y = Sin(angle)
	End Method
	
	Method MulS:Vector2(s:Float)
		Return New Vector2.Create(x * s, y * s)
	End Method
End Type

Type TAlpha
	Global i:Byte
	Global a:Float[256]
	
	Function Free()
		i = Null
		a = Null
	End Function
	
	Function Start()
		i = 0
		a[0] =.GetAlpha()
	End Function
	
	Function Finish()
		.SetAlpha(a[0])
	End Function
	
	Function SetAlpha(v:Float)
		i:-~0
		a[i] = v
		.SetAlpha(v)
	End Function
	
	Function GetAlpha()
		i:+~0
		.SetAlpha(a[i])
	End Function
	
	Function Alpha:Float()
		Return a[i]
	End Function
End Type

Type TBlend
	Global i:Byte
	Global b:Int[256]
	
	Function Free()
		i = Null
		b = Null
	End Function
	
	Function Start()
		i = 0
		b[0] =.GetBlend()
	End Function
	
	Function Finish()
		.SetBlend(b[0])
	End Function
	
	Function SetBlend(v:Int)
		i:-~0
		b[i] = v
		.SetBlend(v)
	End Function
	
	Function GetBlend()
		i:+~0
		.SetBlend(b[i])
	End Function
	
	Function Blend:Int()
		Return b[i]
	End Function
End Type

Type TColor
	Global i:Byte
	Global Red:Int[256]
	Global Green:Int[256]
	Global Blue:Int[256]
	
	Function Free()
		i = Null
		Red = Null
		Green = Null
		Blue = Null
	End Function
	
	Function Start()
		i = 0
		.GetColor(Red[i], Green[i], Blue[i])
	End Function
	
	Function Finish()
		.SetColor(Red[0], Green[0], Blue[0])
	End Function
	
	Function SetColor(r:Int, g:Int, b:Int)
		i:-~0
		Red[i] = r
		Green[i] = g
		Blue[i] = b
		.SetColor(r, g, b)
	End Function
	
	Function GetColor()
		i:+~0
		.SetColor(Red[i], Green[i], Blue[i])
	End Function
	
	Function Color(r:Int Var, g:Int Var, b:Int Var)
		r = Red[i]
		g = Green[i]
		b = Blue[i]
	End Function
End Type

Type TBold
	Field _model:Int[6]
	
	Method DrawSubImageRect()
		
	End Method
End Type

Function Free()
	TFontW.Free()
	TAlpha.Free()
	TBlend.Free()
	TColor.Free()
End Function
OnEnd(Free)



John G(Posted 2012) [#8]
@Alberto-Diablo: Still working on Jesse's link but a fixed-width turnkey solution should work for my needs also.
Copied and pasted bitmapfont #1 into New BMax window and ran, but failed here at :
frame.Draw 0,0,image.width,image.height,x+tx,y+ty
"Missing Function Parameter sx"

Make any sense?
Thanks