OK, since AppGameKit is still working on Ubuntu:
// project: txtUTF8
// created: 2016-10-15
// license: MIT
// abstract: analogues for AGK text functions to work with UTF-8 strings
// copyright (c) 2016, Simon Grim
// tip me via bitcoin:1C5NZCMkjJTf8v7t41QwW9EeCjJcLEbf5s or https://paypal.me/harder
// to use UTF-8 text functions instead those internal AGK commands
// redefine these constants, _txtFontNameDefault at least:
#constant _txtFontBaseDir 'fonts' // and so your fonts must be placed in '/media/fonts/'
#constant _txtFontImageExt 'png'
#constant _txtFontNameDefault 'Russo One' // FIXME or put your default font into '/media/fonts/Russo One/'
#constant _txtDepthDefault 9 // 0-10000
#constant _txtSizeDefault 3 // default height of texts, in world units
#constant _txtFilterMinDefault 1 // 0 for nearest pixel, 1 for linear filter
#constant _txtFilterMagDefault 1 // 0 for nearest pixel, 1 for linear filter
// place following lines to your initialization code (uncomment them of course):
/*
#include 'UTF8.agc'
initUTF8()
#include 'txtUTF8.agc'
initTxtUTF8()
*/
// pick any of available functions:
/*
txtCreate(iFontID, iSize, fPositionX, fPositionY, szString) returns iTextID // iFontID below 0 to set font to _txtFontDefault
txtDelete(iTextID)
txtGetAlignmentH(iTextID) returns iAlignmentModeHorizontal // 0 for left, 1 for center, 2 for right
txtGetAlignmentV(iTextID) returns iAlignmentModeVertical // 0 for top, 1 for center, 2 for bottom
txtGetAngle(iTextID) returns fAngle
txtGetColorR(iTextID) returns iComponentRed
txtGetColorG(iTextID) returns iComponentGreen
txtGetColorB(iTextID) returns iComponentBlue
txtGetColorA(iTextID) returns iComponentAlpha
txtGetDepth(iTextID) returns iDepth
txtGetExists(iTextID) returns 0 or 1
txtGetFont(iTextID) returns iFontID
txtGetHeight(iTextID) returns fHeight // in world units
txtGetHitTest(iTextID, fPositionX, fPositionY) returns 0 or 1 // positions in world coordinates, use ScreenToWorldX() and ScreenToWorldY() if you have mouse coordinates to check
txtGetPositionX(iTextID) returns fPositionX
txtGetPositionY(iTextID) returns fPositionY
txtGetSize(iTextID) returns fSize // height of text, in world units
txtGetScaleX(iTextID) returns fScaleX
txtGetScaleY(iTextID) returns fScaleY
txtGetSticky(iTextID) returns iFixToScreenMode
txtGetString(iTextID) returns szString
txtGetVisible(iTextID) returns iVisibilityMode
txtGetWidth(iTextID) returns fWidth // in world units
txtFontLoad(szFontName) returns iFontID // 'COOL' to load from '/media/' + _txtFontBaseDir + '/COOL.' + _txtFontImageExt
txtFontGetImage(iFontID) returns iFontImage
txtFontGetName(iFontID) returns szFontName
txtSetAlignment(iTextID, iAlignmentModeHorizontal, iAlignmentModeVertical)
txtSetAngle(iTextID, fAngle) // rotation isn't perfect but quite good
txtSetColor(iTextID, iComponentRed, iComponentGreen, iComponentBlue, iComponentAlpha)
txtSetColorA(iTextID, iComponentAlpha)
txtSetDepth(iTextID, iDepth) // 0-10000, iDepth below 0 to reset depth to _txtDepthDefault
txtSetFont(iTextID, iFontID) // iFontID below 0 to reset font to _txtFontDefault // WARN costly operation
txtSetPosition(iTextID, fPositionX, fPositionY)
txtSetScale(iTextID, fScaleX, fScaleY)
txtSetScissor(iTextID, fPositionX, fPositionY, fPositionX2, fPositionY2) // all positions to 0 to turn off the text scissor
txtSetSize(iTextID, fSize) // fSize below 0 to reset size to original
txtSetSticky(iTextID, iFixToScreenMode)
txtSetString(iTextID, szString) // WARN costly operation
txtSetVisible(iTextID, iVisibilityMode)
*/
type txtFontDatType
char as string
img as integer
endtype
type txtFontType
img as integer
name as string
dat as txtFontDatType[]
endtype
type txtType
spr as integer[0]
x as float
y as float
angle as float
alignX as integer
alignY as integer
depth as integer
font as integer
s as string
sLen as integer
scaleX as float
scaleY as float
size as float
sizeDefault as float
width as float
sticky as integer
viz as integer
r as integer
g as integer
b as integer
a as integer
scissorX as integer
scissorY as integer
scissorX2 as integer
scissorY2 as integer
endtype
type txtFontLoadSpecialCharsType
char as string
name as string
endtype
global _txtAspectRatio as float
global _txtFontDefault as integer
global _txtImgUnknownChar as integer
global _txtDeleted as integer[]
global _txtFont as txtFontType[]
global _txt as txtType[]
global _txtFontLoadSpecialChars as txtFontLoadSpecialCharsType[]
function initTxtUTF8()
c as txtFontLoadSpecialCharsType
h# = getDeviceHeight()
_txtAspectRatio = getDeviceWidth() / h#
c.char = ':' : c.name = 'colon'
_txtFontLoadSpecialChars.insertSorted(c)
//c.char = ' ' : c.name = 'space'
// _txtFontLoadSpecialChars.insertSorted(c)
_txtFontDefault = txtFontLoad(_txtFontNameDefault)
img = _txtFont[_txtFontDefault].dat[_txtFont[_txtFontDefault].dat.find(' ')].img
w = getImageWidth(img)
h = getImageHeight(img)
l = 12 + w * h * 4
m = createMemblock(l)
setMemblockByte(m, 0, w)
setMemblockByte(m, 4, h)
setMemblockByte(m, 8, 32)
dec l
for i = 12 to l step 4
setMemblockByte(m, i, 255)
setMemblockByte(m, i + 1, 0)
setMemblockByte(m, i + 2, 0)
setMemblockByte(m, i + 3, 255)
next
_txtImgUnknownChar = createImageFromMemblock(m)
deleteMemblock(m)
endfunction
function txtDelete(i as integer)
txtCheck(i)
_txtDeleted.insert(i)
l = _txt[i].spr.length
for j = 1 to l
deleteSprite(_txt[i].spr[j])
next
_txt[i].font = -1
endfunction
function txtCreate(font as integer, size as integer, x as float, y as float, s as string)
if font < 0 then font = _txtFontDefault
txtFontCheck(font)
if size < 0 then size = _txtSizeDefault
if _txtDeleted.length > -1
i = _txtDeleted[_txtDeleted.length]
_txtDeleted.remove()
else
_txt.length = _txt.length + 1
i = _txt.length
endif
_txt[i].x = x
_txt[i].y = y
_txt[i].depth = _txtDepthDefault
_txt[i].font = font
_txt[i].scaleX = 1
_txt[i].scaleY = 1
_txt[i].size = size
_txt[i].sizeDefault = size
_txt[i].s = s
_txt[i].viz = 1
_txt[i].r = 255
_txt[i].g = 255
_txt[i].b = 255
_txt[i].a = 255
txtBake(i)
endfunction i
function txtGetExists(i as integer)
if i < 0 or i > _txt.length then exitfunction 0
if _txt[i].font = -1 then exitfunction 0
endfunction 1
function txtCheck(i as integer)
if i < 0
message('text #' + str(i) + ' is not exists: negative ID number')
end
endif
if i > _txt.length
message('text #' + str(i) + ' is not exists: ID number is higher than length value of texts\' array')
end
endif
if _txt[i].font = -1
message('text #' + str(i) + ' is not exists: it was deleted')
end
endif
l = _txt[i].sLen
for j = 1 to l
if getSpriteExists(_txt[i].spr[j]) = 0
message('text #' + str(i) + ' is not OK: sprite for char #' + str(j) + ' from its string \'' + _txt[i].s + '\' was deleted')
end
endif
next
endfunction
function txtBake(i as integer)
l = strLen(_txt[i].s)
_txt[i].sLen = l
k = _txt[i].spr.length
for j = l + 1 to k
setSpriteVisible(_txt[i].spr[j], 0)
next
_txt[i].width = 0
if l > 0
for j = 1 to l
k = _txtFont[_txt[i].font].dat.find(strMid(_txt[i].s, j, 1))
if k > -1
img = _txtFont[_txt[i].font].dat[k].img
else
img = _txtImgUnknownChar
endif
if j > _txt[i].spr.length
spr = createSprite(img)
_txt[i].spr.insert(spr)
fixSpriteToScreen(spr, _txt[i].sticky)
setSpriteDepth(spr, _txt[i].depth)
setSpriteColor(spr, _txt[i].r, _txt[i].g, _txt[i].b, _txt[i].a)
setSpriteSize(spr, -1, _txt[i].size)
setSpriteScale(spr, _txt[i].scaleX, _txt[i].scaleY)
setSpriteAngle(spr, _txt[i].angle)
setSpriteScissor(spr, _txt[i].scissorX, _txt[i].scissorY, _txt[i].scissorX2, _txt[i].scissorY2)
else
spr = _txt[i].spr[j]
if getSpriteImageID(spr) <> img
setSpriteImage(spr, img)
setSpriteSize(spr, -1, _txt[i].size)
setSpriteScale(spr, _txt[i].scaleX, _txt[i].scaleY)
setSpriteAngle(spr, _txt[i].angle)
endif
endif
setSpriteVisible(spr, _txt[i].viz)
_txt[i].width = _txt[i].width + getSpriteWidth(spr)
next
txtRepos(i)
endif
endfunction
function txtRepos(i as integer)
a as float
d as float
x as float
y as float
l = _txt[i].sLen
if l = 0 then exitfunction
if _txt[i].angle = 0
select _txt[i].alignX
case 0
x = _txt[i].x
endcase
case 1
x = _txt[i].x - _txt[i].width / 2.0
endcase
case 2
x = _txt[i].x - _txt[i].width
endcase
endselect
select _txt[i].alignY
case 0
y = _txt[i].y
endcase
case 1
y = _txt[i].y - getSpriteHeight(_txt[i].spr[1]) / 2.0
endcase
case 2
y = _txt[i].y - getSpriteHeight(_txt[i].spr[1])
endcase
endselect
for j = 1 to l
setSpritePosition(_txt[i].spr[j], x, y)
x = x + getSpriteWidth(_txt[i].spr[j])
next
else
x = _txt[i].x
select _txt[i].alignX
case 0
d = 0
endcase
case 1
d = - _txt[i].width / 2.0
endcase
case 2
d = - _txt[i].width
endcase
endselect
select _txt[i].alignY
case 0
y = _txt[i].y
endcase
case 1
y = _txt[i].y - getSpriteHeight(_txt[i].spr[1]) / 2.0
endcase
case 2
y = _txt[i].y - getSpriteHeight(_txt[i].spr[1])
endcase
endselect
a = _txt[i].angle + 90.0
for j = 1 to l
setSpritePosition(_txt[i].spr[j], x + d * sin(a), y - d * cos(a) * _txtAspectRatio)
d = d + getSpriteWidth(_txt[i].spr[j])
next
endif
endfunction
function txtSetString(i as integer, s as string)
txtCheck(i)
if _txt[i].s = s then exitfunction
_txt[i].s = s
txtBake(i)
endfunction
function txtGetString(i as integer)
res as string
txtCheck(i)
res = _txt[i].s
endfunction res
function txtSetSize(i as integer, size as float)
txtCheck(i)
if size < 0 then size = _txt[i].sizeDefault
_txt[i].size = size
_txt[i].width = 0
l = _txt[i].sLen
for j = 1 to l
setSpriteSize(_txt[i].spr[j], -1, size)
_txt[i].width = _txt[i].width + getSpriteWidth(_txt[i].spr[j])
_txt[i].scaleX = 1
_txt[i].scaleY = 1
next
txtRepos(i)
endfunction
function txtGetSize(i as integer)
res as float
txtCheck(i)
res = _txt[i].size
endfunction res
function txtGetWidth(i as integer)
res as float
txtCheck(i)
res = _txt[i].width
endfunction res
function txtGetHeight(i as integer)
res as float
txtCheck(i)
if _txt[i].sLen > 0 then res = getSpriteHeight(_txt[i].spr[1])
endfunction res
function txtSetScale(i as integer, x as float, y as float)
txtCheck(i)
_txt[i].scaleX = x
_txt[i].scaleY = y
_txt[i].width = 0
l = _txt[i].sLen
for j = 1 to l
setSpriteScale(_txt[i].spr[j], x, y)
_txt[i].width = _txt[i].width + getSpriteWidth(_txt[i].spr[j])
next
txtRepos(i)
endfunction
function txtGetScaleX(i as integer)
res as float
txtCheck(i)
res = _txt[i].scaleX
endfunction res
function txtGetScaleY(i as integer)
res as float
txtCheck(i)
res = _txt[i].scaleY
endfunction res
function txtSetFont(i as integer, font as integer)
txtCheck(i)
if font < 0 then font = _txtFontDefault
if font = _txt[i].font then exitfunction
_txt[i].font = font
txtBake(i)
endfunction
function txtGetFont(i as integer)
res as integer
txtCheck(i)
res = _txt[i].font
endfunction res
function txtSetPosition(i as integer, x as float, y as float)
txtCheck(i)
_txt[i].x = x
_txt[i].y = y
txtRepos(i)
endfunction
function txtGetPositionX(i as integer)
res as float
txtCheck(i)
res = _txt[i].x
endfunction res
function txtGetPositionY(i as integer)
res as float
txtCheck(i)
res = _txt[i].y
endfunction res
function txtSetDepth(i as integer, depth as integer)
txtCheck(i)
if depth < 0 then depth = _txtDepthDefault
if depth = _txt[i].depth then exitfunction
_txt[i].depth = depth
l = _txt[i].spr.length
for j = 1 to l
setSpriteDepth(_txt[i].spr[j], depth)
next
endfunction
function txtGetDepth(i as integer)
res as integer
txtCheck(i)
res = _txt[i].depth
endfunction res
function txtSetAngle(i as integer, a as float)
txtCheck(i)
_txt[i].angle = a
l = _txt[i].spr.length
for j = 1 to l
setSpriteAngle(_txt[i].spr[j], a)
next
txtRepos(i)
endfunction
function txtGetAngle(i as integer)
res as float
txtCheck(i)
res = _txt[i].angle
endfunction res
function txtSetSticky(i as integer, mode as integer)
txtCheck(i)
if mode < 0 or mode > 1 or mode = _txt[i].sticky then exitfunction
_txt[i].sticky = mode
l = _txt[i].spr.length
for j = 1 to l
fixSpriteToScreen(_txt[i].spr[j], mode)
next
endfunction
function txtGetSticky(i as integer)
res as integer
txtCheck(i)
res = _txt[i].sticky
endfunction res
function txtSetAlignment(i as integer, h as integer, v as integer)
x as float
y as float
txtCheck(i)
if h > -1 and h < 3 then _txt[i].alignX = h
if v > -1 and v < 3 then _txt[i].alignY = v
txtRepos(i)
endfunction
function txtGetAlignmentH(i as integer)
res as integer
txtCheck(i)
res = _txt[i].alignX
endfunction res
function txtGetAlignmentV(i as integer)
res as integer
txtCheck(i)
res = _txt[i].alignY
endfunction res
function txtSetScissor(i as integer, x as float, y as float, x2 as float, y2 as float)
txtCheck(i)
_txt[i].scissorX = x
_txt[i].scissorY = y
_txt[i].scissorX2 = x2
_txt[i].scissorY2 = y2
l = _txt[i].spr.length
for j = 1 to l
setSpriteScissor(_txt[i].spr[j], x, y, x2, y2)
next
endfunction
function txtSetVisible(i as integer, viz as integer)
txtCheck(i)
if viz = _txt[i].viz then exitfunction
_txt[i].viz = viz
l = _txt[i].slen
for j = 1 to l
setSpriteVisible(_txt[i].spr[j], viz)
next
endfunction
function txtGetVisible(i as integer)
res as integer
txtCheck(i)
res = _txt[i].viz
endfunction res
function txtSetColor(i as integer, r as integer, g as integer, b as integer, a as integer)
txtCheck(i)
_txt[i].r = r
_txt[i].g = g
_txt[i].b = b
_txt[i].a = a
l = _txt[i].sLen
for j = 1 to l
setSpriteColor(_txt[i].spr[j], r, g, b, a)
next
endfunction
function txtSetColorA(i as integer, a as integer)
txtCheck(i)
_txt[i].a = a
l = _txt[i].sLen
for j = 1 to l
setSpriteColorAlpha(_txt[i].spr[j], a)
next
endfunction
function txtGetColorR(i as integer)
res as integer
txtCheck(i)
res = _txt[i].r
endfunction res
function txtGetColorG(i as integer)
res as integer
txtCheck(i)
res = _txt[i].g
endfunction res
function txtGetColorB(i as integer)
res as integer
txtCheck(i)
res = _txt[i].b
endfunction res
function txtGetColorA(i as integer)
res as integer
txtCheck(i)
res = _txt[i].a
endfunction res
function txtGetHitTest(i as integer, x as float, y as float)
res as integer
txtCheck(i)
l = _txt[i].sLen
for j = 1 to l
if getSpriteHitTest(_txt[i].spr[j], x, y) = 1 then exitfunction 1
next
endfunction 0
function txtFontLoad(s as string)
c as string
fileNameImage as string
fileNameImageData as string
chars as string[]
dat as txtFontDatType
for i = 0 to _txtFont.length
if s = _txtFont[i].name then exitfunction i
next
fileNameImage = '/media/' + _txtFontBaseDir + '/' + s + '.' + _txtFontImageExt
fileNameImageData = '/media/' + _txtFontBaseDir + '/' + s + ' subimages.txt'
if getFileExists(fileNameImage) = 0
message('can\'t locate file "' + fileNameImage + '"')
end
endif
if getFileExists(fileNameImageData) = 0
message('can\'t locate file "' + fileNameImageData + '"')
end
endif
_txtFont.length = _txtFont.length + 1
i = _txtFont.length
_txtFont[i].name = s
_txtFont[i].img = loadImage(fileNameImage)
setImageMinFilter(_txtFont[i].img, _txtFilterMinDefault)
setImageMagFilter(_txtFont[i].img, _txtFilterMagDefault)
l = 0
f = openToRead(fileNameImageData)
while fileEoF(f) = 0
inc l
s = readLine(f) `CR (\n) terminated string
if s = ''
continue
elseif strLeft(s, 2) = '//'
continue
elseif strMid(s, 2, 1) = ':'
c = strLeft(s, 1)
j = _txtFontLoadSpecialChars.find(c)
if j > - 1
message('can\'t parse file "' + fileNameImageData + '", line #' + str(l) + ' has incorrect syntax:' + chr(10) + chr(10) + chr(9) + '"' + s + '"' + chr(10) + chr(10) + 'each line must have the format "char:x:y:width:height" with the delimiter ":" used between the fields.' + chr(10) + chr(10) + '* the line for char "' + _txtFontLoadSpecialChars[j].char + '" must contain string "' + _txtFontLoadSpecialChars[j].name + '" as char field.')
end
endif
if chars.find(c) > -1
message('can\'t parse file "' + fileNameImageData + '", line #' + str(l) + ', char "' + c + '" is defined already:' + chr(10) + chr(10) + chr(9) + '"' + s + '"')
end
endif
chars.insertSorted(c)
else
for j = 0 to _txtFontLoadSpecialChars.length
c = _txtFontLoadSpecialChars[j].name
if findString(s, c + ':') = 1
if chars.find(c) > -1
message('can\'t parse file "' + fileNameImageData + '", line #' + str(l) + ', char "' + c + '" is defined already:' + chr(10) + chr(10) + chr(9) + '"' + s + '"')
end
endif
chars.insertSorted(c)
j = _txtFontLoadSpecialChars.length + 1
endif
next
if j - _txtFontLoadSpecialChars.length = 1
c = ''
for j = 0 to _txtFontLoadSpecialChars.length
c = c + chr(10) + '* the line for char "' + _txtFontLoadSpecialChars[j].char + '" must contain string "' + _txtFontLoadSpecialChars[j].name + '" as char field.'
next
message('can\'t parse file "' + fileNameImageData + '", line #' + str(l) + ' has incorrect syntax:' + chr(10) + chr(10) + chr(9) + '"' + s + '"' + chr(10) + chr(10) + 'each line must have the format "char:x:y:width:height" with the delimiter ":" used between the fields.' + chr(10) + c)
end
endif
endif
endwhile
closeFile(f)
for j = 0 to _txtFontLoadSpecialChars.length
l = chars.find(_txtFontLoadSpecialChars[j].name)
if l = -1
message('can\'t parse file "' + fileNameImageData + '", char "' + _txtFontLoadSpecialChars[j].char + '" is not defined.' + chr(10) + chr(10) + 'each line must have the format "char:x:y:width:height" with the delimiter ":" used between the fields.' + chr(10) + chr(10) + '* the line for char "' + _txtFontLoadSpecialChars[j].char + '" must contain string "' + _txtFontLoadSpecialChars[j].name + '" as char field.')
end
endif
dat.char = _txtFontLoadSpecialChars[j].char
dat.img = loadSubImage(_txtFont[i].img, _txtFontLoadSpecialChars[j].name)
w = getImageWidth(dat.img)
if w = 0
message('can\'t parse file "' + fileNameImageData + '", char "' + _txtFontLoadSpecialChars[j].char + '" has incorrect width value (' + str(w) + ').' + chr(10) + chr(10) + 'each line must have the format "char:x:y:width:height" with the delimiter ":" used between the fields.' + chr(10) + chr(10) + '* width must be positive')
end
endif
h = getImageHeight(dat.img)
if hMax = 0 then hMax = h
if h = 0
message('can\'t parse file "' + fileNameImageData + '", char "' + _txtFontLoadSpecialChars[j].char + '" has incorrect height value (' + str(h) + ').' + chr(10) + chr(10) + 'each line must have the format "char:x:y:width:height" with the delimiter ":" used between the fields.' + chr(10) + chr(10) + '* height must be positive')
end
endif
if h <> hMax
message('can\'t parse file "' + fileNameImageData + '", char "' + _txtFontLoadSpecialChars[j].char + '" has incorrect height value (' + str(h) + ').' + chr(10) + chr(10) + 'each line must have the format "char:x:y:width:height" with the delimiter ":" used between the fields.' + chr(10) + chr(10) + '* all heights must share same value (' + str(hMax) + ' was specified first)')
end
endif
_txtFont[i].dat.insertSorted(dat)
chars.remove(l)
next
l = chars.length
for j = 0 to l
dat.char = chars[j]
dat.img = loadSubImage(_txtFont[i].img, chars[j])
w = getImageWidth(dat.img)
if w = 0
message('can\'t parse file "' + fileNameImageData + '", char "' + chars[j] + '" has incorrect width value (' + str(w) + ').' + chr(10) + chr(10) + 'each line must have the format "char:x:y:width:height" with the delimiter ":" used between the fields.' + chr(10) + chr(10) + '* width must be positive')
end
endif
h = getImageHeight(dat.img)
if h = 0
message('can\'t parse file "' + fileNameImageData + '", char "' + chars[j] + '" has incorrect height value (' + str(h) + ').' + chr(10) + chr(10) + 'each line must have the format "char:x:y:width:height" with the delimiter ":" used between the fields.' + chr(10) + chr(10) + '* height must be positive')
end
endif
if h <> hMax
message('can\'t parse file "' + fileNameImageData + '", char "' + chars[j] + '" has incorrect height value (' + str(h) + ').' + chr(10) + chr(10) + 'each line must have the format "char:x:y:width:height" with the delimiter ":" used between the fields.' + chr(10) + chr(10) + '* all heights must share same value (' + str(hMax) + ' was specified first)')
end
endif
_txtFont[i].dat.insertSorted(dat)
next
endfunction i
function txtFontCheck(i as integer)
if i < 0
message('text font #' + str(i) + ' is not exists: negative ID number')
end
endif
if i > _txtFont.length
message('text font #' + str(i) + ' is not exists: ID number is higher than length value of text fonts\' array')
end
endif
endfunction
function txtFontGetImage(i as integer)
res as integer
txtFontCheck(i)
res = _txtFont[i].img
endfunction res
function txtFontGetName(i as integer)
res as string
txtFontCheck(i)
res = _txtFont[i].name
endfunction res
gist.
you need UTF8.agc to get it working. and some font image with well prepared 'subimages.txt' of course.
there is an example project with UTF8.agc and 'Russo One' font (incomplete but still) in attached archive.
have fun. NO FUN ANYMORE —
that's all, folks!error #1:
'too many stars, too many stares. disembody.'
Unicode yours