mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
Terrarum Sans Bitmap imple on GDX -- wip
This commit is contained in:
7
src/net/torvald/terrarum/TerrarumGDX.kt
Normal file
7
src/net/torvald/terrarum/TerrarumGDX.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package net.torvald.terrarum
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2017-06-15.
|
||||
*/
|
||||
object TerrarumGDX {
|
||||
}
|
||||
@@ -6,8 +6,9 @@ import com.badlogic.gdx.backends.lwjgl.LwjglApplication
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration
|
||||
import com.badlogic.gdx.graphics.GL20
|
||||
import com.badlogic.gdx.graphics.Texture
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
|
||||
import net.torvald.terrarumsansbitmap.gdx.GameFontBase
|
||||
|
||||
|
||||
/**
|
||||
@@ -18,17 +19,26 @@ class TestTestTest : ApplicationAdapter() {
|
||||
lateinit var batch: SpriteBatch
|
||||
lateinit var img: Texture
|
||||
|
||||
lateinit var gameFont: BitmapFont
|
||||
|
||||
override fun create() {
|
||||
batch = SpriteBatch()
|
||||
img = Texture("assets/test_texture.tga")
|
||||
|
||||
gameFont = GameFontBase(false)
|
||||
//gameFont = BitmapFont()
|
||||
}
|
||||
|
||||
override fun render() {
|
||||
Gdx.gl.glClearColor(1f, 0f, 0f, 1f)
|
||||
Gdx.gl.glClearColor(0f, 0f, 0f, 1f)
|
||||
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
|
||||
batch.begin()
|
||||
batch.draw(img, 0f, 0f)
|
||||
batch.end()
|
||||
|
||||
Gdx.graphics.setTitle("$GAME_NAME — F: ${Gdx.graphics.framesPerSecond}")
|
||||
|
||||
batch.inBatch {
|
||||
|
||||
gameFont.draw(batch, "Hello, world!", 10f, 30f)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
@@ -36,9 +46,18 @@ class TestTestTest : ApplicationAdapter() {
|
||||
img.dispose()
|
||||
}
|
||||
|
||||
|
||||
private inline fun SpriteBatch.inBatch(action: () -> Unit) {
|
||||
this.begin()
|
||||
action()
|
||||
this.end()
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) { // LWJGL 3 won't work? java.lang.VerifyError
|
||||
val config = LwjglApplicationConfiguration()
|
||||
config.useGL30 = true
|
||||
config.vSyncEnabled = false
|
||||
//config.foregroundFPS = 9999
|
||||
LwjglApplication(TestTestTest(), config)
|
||||
}
|
||||
544
src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt
Normal file
544
src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt
Normal file
@@ -0,0 +1,544 @@
|
||||
package net.torvald.terrarumsansbitmap.gdx
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.Pixmap
|
||||
import com.badlogic.gdx.graphics.Texture
|
||||
import com.badlogic.gdx.graphics.g2d.*
|
||||
import com.badlogic.gdx.utils.Array
|
||||
import net.torvald.spriteanimation.TextureRegionPack
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.zip.GZIPInputStream
|
||||
|
||||
/**
|
||||
* LibGDX port of Terrarum Sans Bitmap implementation
|
||||
*
|
||||
* Filename and Extension for the spritesheet is hard-coded, which are:
|
||||
*
|
||||
* - ascii_variable.tga
|
||||
* - hangul_johab.tga
|
||||
* - LatinExtA_variable.tga
|
||||
* - LatinExtB_variable.tga
|
||||
* - kana.tga
|
||||
* - cjkpunct.tga
|
||||
* - wenquanyi.tga.gz
|
||||
* - cyrillic_variable.tga
|
||||
* - fullwidth_forms.tga
|
||||
* - unipunct_variable.tga
|
||||
* - greek_variable.tga
|
||||
* - thai_variable.tga
|
||||
* - puae000-e0ff.tga
|
||||
*
|
||||
*
|
||||
* Glyphs are drawn lazily (calculated on-the-fly, rather than load up all), which is inevitable as we just can't load
|
||||
* up 40k+ characters on the machine, which will certainly make loading time painfully long.
|
||||
*
|
||||
* Created by minjaesong on 2017-06-15.
|
||||
*/
|
||||
class GameFontBase(val noShadow: Boolean) : BitmapFont() {
|
||||
|
||||
private fun getHanChosung(hanIndex: Int) = hanIndex / (JUNG_COUNT * JONG_COUNT)
|
||||
private fun getHanJungseong(hanIndex: Int) = hanIndex / JONG_COUNT % JUNG_COUNT
|
||||
private fun getHanJongseong(hanIndex: Int) = hanIndex % JONG_COUNT
|
||||
|
||||
private val jungseongWide = arrayOf(8, 12, 13, 17, 18, 21)
|
||||
private val jungseongComplex = arrayOf(9, 10, 11, 14, 15, 16, 22)
|
||||
|
||||
private fun isJungseongWide(hanIndex: Int) = jungseongWide.contains(getHanJungseong(hanIndex))
|
||||
private fun isJungseongComplex(hanIndex: Int) = jungseongComplex.contains(getHanJungseong(hanIndex))
|
||||
|
||||
private fun getHanInitialRow(hanIndex: Int): Int {
|
||||
val ret: Int
|
||||
|
||||
if (isJungseongWide(hanIndex))
|
||||
ret = 2
|
||||
else if (isJungseongComplex(hanIndex))
|
||||
ret = 4
|
||||
else
|
||||
ret = 0
|
||||
|
||||
return if (getHanJongseong(hanIndex) == 0) ret else ret + 1
|
||||
}
|
||||
|
||||
private fun getHanMedialRow(hanIndex: Int) = if (getHanJongseong(hanIndex) == 0) 6 else 7
|
||||
|
||||
private fun getHanFinalRow(hanIndex: Int): Int {
|
||||
val jungseongIndex = getHanJungseong(hanIndex)
|
||||
|
||||
return if (jungseongWide.contains(jungseongIndex))
|
||||
8
|
||||
else
|
||||
9
|
||||
}
|
||||
|
||||
private fun isHangul(c: Char) = c.toInt() in 0xAC00..0xD7A3
|
||||
private fun isAscii(c: Char) = c.toInt() in 0x20..0xFF
|
||||
//private fun isRunic(c: Char) = runicList.contains(c)
|
||||
private fun isExtA(c: Char) = c.toInt() in 0x100..0x17F
|
||||
private fun isExtB(c: Char) = c.toInt() in 0x180..0x24F
|
||||
private fun isKana(c: Char) = c.toInt() in 0x3040..0x30FF
|
||||
private fun isCJKPunct(c: Char) = c.toInt() in 0x3000..0x303F
|
||||
private fun isUniHan(c: Char) = c.toInt() in 0x3400..0x9FFF
|
||||
private fun isCyrilic(c: Char) = c.toInt() in 0x400..0x45F
|
||||
private fun isFullwidthUni(c: Char) = c.toInt() in 0xFF00..0xFF1F
|
||||
private fun isUniPunct(c: Char) = c.toInt() in 0x2000..0x206F
|
||||
private fun isGreek(c: Char) = c.toInt() in 0x370..0x3CE
|
||||
private fun isThai(c: Char) = c.toInt() in 0xE00..0xE7F
|
||||
private fun isThaiDiacritics(c: Char) = c.toInt() in 0xE34..0xE3A
|
||||
|| c.toInt() in 0xE47..0xE4E
|
||||
|| c.toInt() == 0xE31
|
||||
private fun isCustomSym(c: Char) = c.toInt() in 0xE000..0xE0FF
|
||||
|
||||
|
||||
|
||||
private fun extAindexX(c: Char) = (c.toInt() - 0x100) % 16
|
||||
private fun extAindexY(c: Char) = (c.toInt() - 0x100) / 16
|
||||
|
||||
private fun extBindexX(c: Char) = (c.toInt() - 0x180) % 16
|
||||
private fun extBindexY(c: Char) = (c.toInt() - 0x180) / 16
|
||||
|
||||
//private fun runicIndexX(c: Char) = runicList.indexOf(c) % 16
|
||||
//private fun runicIndexY(c: Char) = runicList.indexOf(c) / 16
|
||||
|
||||
private fun kanaIndexX(c: Char) = (c.toInt() - 0x3040) % 16
|
||||
private fun kanaIndexY(c: Char) = (c.toInt() - 0x3040) / 16
|
||||
|
||||
private fun cjkPunctIndexX(c: Char) = (c.toInt() - 0x3000) % 16
|
||||
private fun cjkPunctIndexY(c: Char) = (c.toInt() - 0x3000) / 16
|
||||
|
||||
private fun cyrilicIndexX(c: Char) = (c.toInt() - 0x400) % 16
|
||||
private fun cyrilicIndexY(c: Char) = (c.toInt() - 0x400) / 16
|
||||
|
||||
private fun fullwidthUniIndexX(c: Char) = (c.toInt() - 0xFF00) % 16
|
||||
private fun fullwidthUniIndexY(c: Char) = (c.toInt() - 0xFF00) / 16
|
||||
|
||||
private fun uniPunctIndexX(c: Char) = (c.toInt() - 0x2000) % 16
|
||||
private fun uniPunctIndexY(c: Char) = (c.toInt() - 0x2000) / 16
|
||||
|
||||
private fun unihanIndexX(c: Char) = (c.toInt() - 0x3400) % 256
|
||||
private fun unihanIndexY(c: Char) = (c.toInt() - 0x3400) / 256
|
||||
|
||||
private fun greekIndexX(c: Char) = (c.toInt() - 0x370) % 16
|
||||
private fun greekIndexY(c: Char) = (c.toInt() - 0x370) / 16
|
||||
|
||||
private fun thaiIndexX(c: Char) = (c.toInt() - 0xE00) % 16
|
||||
private fun thaiIndexY(c: Char) = (c.toInt() - 0xE00) / 16
|
||||
|
||||
private fun symbolIndexX(c: Char) = (c.toInt() - 0xE000) % 16
|
||||
private fun symbolIndexY(c: Char) = (c.toInt() - 0xE000) / 16
|
||||
|
||||
private val unihanWidthSheets = arrayOf(
|
||||
SHEET_UNIHAN,
|
||||
SHEET_FW_UNI,
|
||||
SHEET_UNIHAN
|
||||
)
|
||||
private val variableWidthSheets = arrayOf(
|
||||
SHEET_ASCII_VARW,
|
||||
SHEET_CYRILIC_VARW,
|
||||
SHEET_EXTA_VARW,
|
||||
SHEET_GREEK_VARW,
|
||||
SHEET_EXTB_VARW,
|
||||
SHEET_THAI_VARW
|
||||
)
|
||||
|
||||
private val fontParentDir = "assets/graphics/fonts/"
|
||||
private val fileList = arrayOf( // MUST BE MATCHING WITH SHEET INDICES!!
|
||||
"ascii_variable.tga",
|
||||
"hangul_johab.tga",
|
||||
"LatinExtA_variable.tga",
|
||||
"LatinExtB_variable.tga",
|
||||
"kana.tga",
|
||||
"cjkpunct.tga",
|
||||
"wenquanyi.tga.gz",
|
||||
"cyrilic_variable.tga",
|
||||
"fullwidth_forms.tga",
|
||||
"unipunct_variable.tga",
|
||||
"greek_variable.tga",
|
||||
"thai_variable.tga",
|
||||
"puae000-e0ff.tga"
|
||||
)
|
||||
private val codeRange = arrayOf( // MUST BE MATCHING WITH SHEET INDICES!!
|
||||
0..0xFF,
|
||||
0xAC00..0xD7A4,
|
||||
0x100..0x17F,
|
||||
0x180..0x24F,
|
||||
0x3040..0x30FF,
|
||||
0x3000..0x303F,
|
||||
0x3400..0x9FFF,
|
||||
0x400..0x45F,
|
||||
0xFF00..0xFF1F,
|
||||
0x2000..0x206F,
|
||||
0x370..0x3CE,
|
||||
0xE00..0xE7F,
|
||||
0xE000..0xE0FF
|
||||
)
|
||||
private val sheets = arrayOf( // MUST BE MATCHING WITH SHEET INDICES!!
|
||||
asciiSheet,
|
||||
hangulSheet,
|
||||
extASheet,
|
||||
extBSheet,
|
||||
kanaSheet,
|
||||
cjkPunct,
|
||||
uniHan,
|
||||
cyrilic,
|
||||
fullwidthForms,
|
||||
uniPunct,
|
||||
greekSheet,
|
||||
thaiSheet,
|
||||
customSheet
|
||||
)
|
||||
|
||||
|
||||
init {
|
||||
// first we create pixmap to read pixels, then make texture using pixmap
|
||||
fileList.forEachIndexed { index, it ->
|
||||
val isVariable = it.endsWith("_variable.tga")
|
||||
val pixmap: Pixmap
|
||||
|
||||
// unpack gz if applicable
|
||||
if (it.endsWith(".gz")) {
|
||||
val gzi = GZIPInputStream(Gdx.files.internal(fontParentDir + it).read(8192))
|
||||
val wholeFile = gzi.readBytes()
|
||||
gzi.close()
|
||||
val fos = BufferedOutputStream(FileOutputStream("tmp_wenquanyi.tga"))
|
||||
fos.write(wholeFile)
|
||||
fos.flush()
|
||||
fos.close()
|
||||
|
||||
pixmap = Pixmap(Gdx.files.internal("tmp_wenquanyi.tga"))
|
||||
|
||||
File("tmp_wenquanyi.tga").delete()
|
||||
}
|
||||
else {
|
||||
pixmap = Pixmap(Gdx.files.internal(fontParentDir + it))
|
||||
}
|
||||
|
||||
|
||||
if (isVariable) {
|
||||
buildWidthTable(pixmap, codeRange[index].first, codeRange[index])
|
||||
}
|
||||
|
||||
|
||||
val texture = Texture(pixmap)
|
||||
val texRegPack = if (isVariable) {
|
||||
TextureRegionPack(texture, W_VAR_INIT, H, HGAP_VAR, 0)
|
||||
}
|
||||
else if (index == SHEET_UNIHAN || index == SHEET_FW_UNI) {
|
||||
TextureRegionPack(texture, W_UNIHAN, H_UNIHAN)
|
||||
}
|
||||
else if (index == SHEET_CJK_PUNCT) {
|
||||
TextureRegionPack(texture, W_ASIAN_PUNCT, H)
|
||||
}
|
||||
else if (index == SHEET_KANA) {
|
||||
TextureRegionPack(texture, W_KANA, H)
|
||||
}
|
||||
else if (index == SHEET_HANGUL) {
|
||||
TextureRegionPack(texture, W_HANGUL, H)
|
||||
}
|
||||
else if (index == SHEET_CUSTOM_SYM) {
|
||||
TextureRegionPack(texture, SIZE_CUSTOM_SYM, SIZE_CUSTOM_SYM) // TODO variable
|
||||
}
|
||||
else throw IllegalArgumentException("Unknown sheet index: $index")
|
||||
|
||||
|
||||
sheets[index] = texRegPack
|
||||
|
||||
pixmap.dispose() // you are terminated
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getLineHeight(): Float = H.toFloat()
|
||||
|
||||
override fun getXHeight() = lineHeight
|
||||
override fun getCapHeight() = lineHeight
|
||||
override fun getAscent() = 0f
|
||||
override fun getDescent() = 0f
|
||||
|
||||
override fun isFlipped() = false
|
||||
|
||||
|
||||
override fun setFixedWidthGlyphs(glyphs: CharSequence) {
|
||||
throw UnsupportedOperationException("Nope, no monospace, and figures are already fixed width, bruv.")
|
||||
}
|
||||
|
||||
|
||||
init {
|
||||
setUseIntegerPositions(true)
|
||||
setOwnsTexture(true)
|
||||
}
|
||||
|
||||
private var microBuffer: CharSequence = ""
|
||||
private var microBWidth = intArrayOf() // absolute posX of glyphs from print-origin
|
||||
|
||||
override fun draw(batch: Batch, str: CharSequence, x: Float, y: Float): GlyphLayout? {
|
||||
if (microBuffer != str) {
|
||||
microBuffer = str
|
||||
val widths = getWidthOfCharSeq(str)
|
||||
|
||||
microBWidth = Array(str.length, { charIndex ->
|
||||
if (charIndex == 0)
|
||||
0
|
||||
else {
|
||||
var acc = 0
|
||||
(0..charIndex - 1).forEach { acc += widths[it] }
|
||||
/*return*/acc
|
||||
}
|
||||
}).toIntArray()
|
||||
}
|
||||
|
||||
|
||||
//print("[gamefontbase] widthTable for $microBuffer: ")
|
||||
//microBWidth.forEach { print("$it ") }; println()
|
||||
|
||||
microBuffer.forEachIndexed { index, c ->
|
||||
val sheetID = getSheetType(c)
|
||||
val sheetXY = getSheetwisePosition(c)
|
||||
batch.draw(
|
||||
sheets[sheetID]!!.get(sheetXY[0], sheetXY[1]),
|
||||
x + microBWidth[index], y
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
override fun dispose() {
|
||||
super.dispose()
|
||||
|
||||
sheets.forEach { it!!.dispose() }
|
||||
}
|
||||
|
||||
private fun getWidthOfCharSeq(s: CharSequence): IntArray {
|
||||
val len = IntArray(s.length)
|
||||
for (i in 0..s.lastIndex) {
|
||||
val chr = s[i]
|
||||
val ctype = getSheetType(s[i])
|
||||
|
||||
if (variableWidthSheets.contains(ctype)) {
|
||||
len[i] = try {
|
||||
glyphWidths[chr.toInt()]!!
|
||||
}
|
||||
catch (e: kotlin.KotlinNullPointerException) {
|
||||
println("KotlinNullPointerException on glyph number ${Integer.toHexString(chr.toInt()).toUpperCase()}")
|
||||
//System.exit(1)
|
||||
W_LATIN_WIDE // failsafe
|
||||
}
|
||||
}
|
||||
else if (ctype == SHEET_CJK_PUNCT)
|
||||
len[i] = W_ASIAN_PUNCT
|
||||
else if (ctype == SHEET_HANGUL)
|
||||
len[i] = W_HANGUL
|
||||
else if (ctype == SHEET_KANA)
|
||||
len[i] = W_KANA
|
||||
else if (unihanWidthSheets.contains(ctype))
|
||||
len[i] = W_UNIHAN
|
||||
else if (isThaiDiacritics(s[i]))
|
||||
len[i] = 0 // set width of the glyph as -W_LATIN_WIDE
|
||||
else if (ctype == SHEET_CUSTOM_SYM)
|
||||
len[i] = SIZE_CUSTOM_SYM
|
||||
else
|
||||
len[i] = W_LATIN_WIDE
|
||||
|
||||
if (scale > 1) len[i] *= scale
|
||||
|
||||
if (i < s.lastIndex) len[i] += interchar
|
||||
}
|
||||
return len
|
||||
}
|
||||
|
||||
private fun getSheetType(c: Char): Int {
|
||||
if (isHangul(c))
|
||||
return SHEET_HANGUL
|
||||
else if (isKana(c))
|
||||
return SHEET_KANA
|
||||
else if (isUniHan(c))
|
||||
return SHEET_UNIHAN
|
||||
else if (isAscii(c))
|
||||
return SHEET_ASCII_VARW
|
||||
else if (isExtA(c))
|
||||
return SHEET_EXTA_VARW
|
||||
else if (isExtB(c))
|
||||
return SHEET_EXTB_VARW
|
||||
else if (isCyrilic(c))
|
||||
return SHEET_CYRILIC_VARW
|
||||
else if (isUniPunct(c))
|
||||
return SHEET_UNI_PUNCT
|
||||
else if (isCJKPunct(c))
|
||||
return SHEET_CJK_PUNCT
|
||||
else if (isFullwidthUni(c))
|
||||
return SHEET_FW_UNI
|
||||
else if (isGreek(c))
|
||||
return SHEET_GREEK_VARW
|
||||
else if (isThai(c))
|
||||
return SHEET_THAI_VARW
|
||||
else if (isCustomSym(c))
|
||||
return SHEET_CUSTOM_SYM
|
||||
else
|
||||
return SHEET_UNKNOWN// fixed width punctuations
|
||||
// fixed width
|
||||
// fallback
|
||||
}
|
||||
|
||||
private fun getSheetwisePosition(ch: Char): IntArray {
|
||||
val sheetX: Int; val sheetY: Int
|
||||
when (getSheetType(ch)) {
|
||||
SHEET_EXTA_VARW -> {
|
||||
sheetX = extAindexX(ch)
|
||||
sheetY = extAindexY(ch)
|
||||
}
|
||||
SHEET_EXTB_VARW -> {
|
||||
sheetX = extBindexX(ch)
|
||||
sheetY = extBindexY(ch)
|
||||
}
|
||||
SHEET_KANA -> {
|
||||
sheetX = kanaIndexX(ch)
|
||||
sheetY = kanaIndexY(ch)
|
||||
}
|
||||
SHEET_CJK_PUNCT -> {
|
||||
sheetX = cjkPunctIndexX(ch)
|
||||
sheetY = cjkPunctIndexY(ch)
|
||||
}
|
||||
SHEET_CYRILIC_VARW -> {
|
||||
sheetX = cyrilicIndexX(ch)
|
||||
sheetY = cyrilicIndexY(ch)
|
||||
}
|
||||
SHEET_FW_UNI -> {
|
||||
sheetX = fullwidthUniIndexX(ch)
|
||||
sheetY = fullwidthUniIndexY(ch)
|
||||
}
|
||||
SHEET_UNI_PUNCT -> {
|
||||
sheetX = uniPunctIndexX(ch)
|
||||
sheetY = uniPunctIndexY(ch)
|
||||
}
|
||||
SHEET_GREEK_VARW -> {
|
||||
sheetX = greekIndexX(ch)
|
||||
sheetY = greekIndexY(ch)
|
||||
}
|
||||
SHEET_THAI_VARW -> {
|
||||
sheetX = thaiIndexX(ch)
|
||||
sheetY = thaiIndexY(ch)
|
||||
}
|
||||
SHEET_CUSTOM_SYM -> {
|
||||
sheetX = symbolIndexX(ch)
|
||||
sheetY = symbolIndexY(ch)
|
||||
}
|
||||
else -> {
|
||||
sheetX = ch.toInt() % 16
|
||||
sheetY = ch.toInt() / 16
|
||||
}
|
||||
}
|
||||
|
||||
return intArrayOf(sheetX, sheetY)
|
||||
}
|
||||
|
||||
fun buildWidthTable(pixmap: Pixmap, codeOffset: Int, codeRange: IntRange, rows: Int = 16) {
|
||||
val binaryCodeOffset = 15
|
||||
|
||||
val cellW = 16
|
||||
val cellH = 20
|
||||
|
||||
// control chars
|
||||
for (ccode in codeRange) {
|
||||
val glyphX = ccode % rows
|
||||
val glyphY = ccode / rows
|
||||
|
||||
val codeStartX = (glyphX * cellW) + binaryCodeOffset
|
||||
val codeStartY = (glyphY * cellH)
|
||||
|
||||
var glyphWidth = 0
|
||||
for (downCtr in 0..3) {
|
||||
// if ALPHA is not zero, assume it's 1
|
||||
if (pixmap.getPixel(codeStartX, codeStartY + downCtr).and(0xFF) != 0) {
|
||||
glyphWidth = glyphWidth or (1 shl downCtr)
|
||||
}
|
||||
}
|
||||
|
||||
glyphWidths[codeOffset + ccode] = glyphWidth
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
internal val glyphWidths: HashMap<Int, Int> = HashMap()
|
||||
|
||||
internal var hangulSheet: TextureRegionPack? = null
|
||||
internal var asciiSheet: TextureRegionPack? = null
|
||||
internal var runicSheet: TextureRegionPack? = null
|
||||
internal var extASheet: TextureRegionPack? = null
|
||||
internal var extBSheet: TextureRegionPack? = null
|
||||
internal var kanaSheet: TextureRegionPack? = null
|
||||
internal var cjkPunct: TextureRegionPack? = null
|
||||
internal var uniHan: TextureRegionPack? = null
|
||||
internal var cyrilic: TextureRegionPack? = null
|
||||
internal var fullwidthForms: TextureRegionPack? = null
|
||||
internal var uniPunct: TextureRegionPack? = null
|
||||
internal var greekSheet: TextureRegionPack? = null
|
||||
internal var thaiSheet: TextureRegionPack? = null
|
||||
internal var customSheet: TextureRegionPack? = null
|
||||
|
||||
internal val JUNG_COUNT = 21
|
||||
internal val JONG_COUNT = 28
|
||||
|
||||
internal val W_ASIAN_PUNCT = 10
|
||||
internal val W_HANGUL = 12
|
||||
internal val W_KANA = 12
|
||||
internal val W_UNIHAN = 16
|
||||
internal val W_LATIN_WIDE = 9 // width of regular letters
|
||||
internal val W_VAR_INIT = 15
|
||||
|
||||
internal val HGAP_VAR = 1
|
||||
|
||||
internal val H = 20
|
||||
internal val H_UNIHAN = 16
|
||||
|
||||
internal val SIZE_CUSTOM_SYM = 18
|
||||
|
||||
internal val SHEET_ASCII_VARW = 0
|
||||
internal val SHEET_HANGUL = 1
|
||||
internal val SHEET_EXTA_VARW = 2
|
||||
internal val SHEET_EXTB_VARW = 3
|
||||
internal val SHEET_KANA = 4
|
||||
internal val SHEET_CJK_PUNCT = 5
|
||||
internal val SHEET_UNIHAN = 6
|
||||
internal val SHEET_CYRILIC_VARW = 7
|
||||
internal val SHEET_FW_UNI = 8
|
||||
internal val SHEET_UNI_PUNCT = 9
|
||||
internal val SHEET_GREEK_VARW = 10
|
||||
internal val SHEET_THAI_VARW = 11
|
||||
internal val SHEET_CUSTOM_SYM = 12
|
||||
|
||||
internal val SHEET_UNKNOWN = 254
|
||||
|
||||
/**
|
||||
* Runic letters list used for game. The set is
|
||||
* Younger Futhark + Medieval rune 'e' + Punct + Runic Almanac
|
||||
|
||||
* BEWARE OF SIMILAR-LOOKING RUNES, especially:
|
||||
|
||||
* * Algiz ᛉ instead of Maðr ᛘ
|
||||
|
||||
* * Short-Twig Hagall ᚽ instead of Runic Letter E ᛂ
|
||||
|
||||
* * Runic Letter OE ᚯ instead of Óss ᚬ
|
||||
|
||||
* Examples:
|
||||
* ᛭ᛋᛁᚴᚱᛁᚦᛦ᛭
|
||||
* ᛭ᛂᛚᛋᛅ᛭ᛏᚱᚢᛏᚾᛁᚾᚴᚢᚾᛅ᛬ᛅᚱᚾᛅᛏᛅᛚᛋ
|
||||
*/
|
||||
//internal val runicList = arrayOf('ᚠ', 'ᚢ', 'ᚦ', 'ᚬ', 'ᚱ', 'ᚴ', 'ᚼ', 'ᚾ', 'ᛁ', 'ᛅ', 'ᛋ', 'ᛏ', 'ᛒ', 'ᛘ', 'ᛚ', 'ᛦ', 'ᛂ', '᛬', '᛫', '᛭', 'ᛮ', 'ᛯ', 'ᛰ')
|
||||
// TODO expand to full Unicode runes
|
||||
|
||||
var interchar = 0
|
||||
var scale = 1
|
||||
set(value) {
|
||||
if (value > 0) field = value
|
||||
else throw IllegalArgumentException("Font scale cannot be zero or negative (input: $value)")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
45
src/net/torvald/terrarumsansbitmap/gdx/TextureRegionPack.kt
Normal file
45
src/net/torvald/terrarumsansbitmap/gdx/TextureRegionPack.kt
Normal file
@@ -0,0 +1,45 @@
|
||||
package net.torvald.terrarumsansbitmap.gdx
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2017-06-15.
|
||||
*/
|
||||
class TextureRegionPack(
|
||||
val texture: Texture,
|
||||
val tileW: Int,
|
||||
val tileH: Int,
|
||||
val hGap: Int = 0,
|
||||
val vGap: Int = 0,
|
||||
val hFrame: Int = 0,
|
||||
val vFrame: Int = 0
|
||||
) {
|
||||
|
||||
val regions: Array<TextureRegion>
|
||||
|
||||
private val horizontalCount = (texture.width - 2 * hFrame + hGap) / (tileW + hGap)
|
||||
private val verticalCount = (texture.height - 2 * vFrame + vGap) / (tileH + vGap)
|
||||
|
||||
init {
|
||||
println("texture: $texture, dim: ${texture.width} x ${texture.height}, grid: $horizontalCount x $verticalCount, cellDim: $tileW x $tileH")
|
||||
|
||||
regions = Array<TextureRegion>(horizontalCount * verticalCount, {
|
||||
val region = TextureRegion()
|
||||
val rx = (it % horizontalCount * (tileW + hGap)) + hFrame
|
||||
val ry = (it / horizontalCount * (tileH + vGap)) + vFrame
|
||||
|
||||
region.setRegion(texture)
|
||||
region.setRegion(rx, ry, tileW, tileH)
|
||||
|
||||
/*return*/region
|
||||
})
|
||||
}
|
||||
|
||||
fun get(x: Int, y: Int) = regions[y * horizontalCount + x]
|
||||
|
||||
fun dispose() {
|
||||
texture.dispose()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.torvald.imagefont
|
||||
package net.torvald.terrarumsansbitmap.slick2d
|
||||
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.getPixel
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.newdawn.slick.*
|
||||
@@ -11,17 +10,8 @@ import java.util.*
|
||||
*/
|
||||
open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
|
||||
private fun getHan(hanIndex: Int): IntArray {
|
||||
val han_x = hanIndex % JONG_COUNT
|
||||
val han_y = hanIndex / JONG_COUNT
|
||||
val ret = intArrayOf(han_x, han_y)
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun getHanChosung(hanIndex: Int) = hanIndex / (JUNG_COUNT * JONG_COUNT)
|
||||
|
||||
private fun getHanJungseong(hanIndex: Int) = hanIndex / JONG_COUNT % JUNG_COUNT
|
||||
|
||||
private fun getHanJongseong(hanIndex: Int) = hanIndex % JONG_COUNT
|
||||
|
||||
private val jungseongWide = arrayOf(8, 12, 13, 17, 18, 21)
|
||||
@@ -70,8 +60,7 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
private fun isThaiDiacritics(c: Char) = c.toInt() in 0xE34..0xE3A
|
||||
|| c.toInt() in 0xE47..0xE4E
|
||||
|| c.toInt() == 0xE31
|
||||
private fun isThaiEF(c: Char) = c.toInt() == 0xE40
|
||||
private fun isKeycap(c: Char) = c.toInt() in 0xE000..0xE0FF
|
||||
private fun isCustomSym(c: Char) = c.toInt() in 0xE000..0xE0FF
|
||||
|
||||
|
||||
|
||||
@@ -108,9 +97,6 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
private fun thaiIndexX(c: Char) = (c.toInt() - 0xE00) % 16
|
||||
private fun thaiIndexY(c: Char) = (c.toInt() - 0xE00) / 16
|
||||
|
||||
private fun thaiNarrowIndexX(c: Char) = 3
|
||||
private fun thaiNarrowIndexY(c: Char) = 0
|
||||
|
||||
private fun keycapIndexX(c: Char) = (c.toInt() - 0xE000) % 16
|
||||
private fun keycapIndexY(c: Char) = (c.toInt() - 0xE000) / 16
|
||||
|
||||
@@ -119,15 +105,13 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
SHEET_FW_UNI,
|
||||
SHEET_UNIHAN
|
||||
)
|
||||
private val zeroWidthSheets = arrayOf(
|
||||
SHEET_COLOURCODE
|
||||
)
|
||||
private val variableWidthSheets = arrayOf(
|
||||
SHEET_ASCII_VARW,
|
||||
SHEET_CYRILIC_VARW,
|
||||
SHEET_EXTA_VARW,
|
||||
SHEET_GREEK_VARW,
|
||||
SHEET_EXTB_VARW
|
||||
SHEET_EXTB_VARW,
|
||||
SHEET_THAI_VARW
|
||||
)
|
||||
|
||||
|
||||
@@ -141,7 +125,7 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
|
||||
if (variableWidthSheets.contains(ctype)) {
|
||||
len += try {
|
||||
asciiWidths[chr.toInt()]!!
|
||||
glyphWidths[chr.toInt()]!!
|
||||
}
|
||||
catch (e: kotlin.KotlinNullPointerException) {
|
||||
println("KotlinNullPointerException on glyph number ${Integer.toHexString(chr.toInt()).toUpperCase()}")
|
||||
@@ -149,8 +133,6 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
W_LATIN_WIDE // failsafe
|
||||
}
|
||||
}
|
||||
else if (zeroWidthSheets.contains(ctype))
|
||||
len += 0
|
||||
else if (ctype == SHEET_CJK_PUNCT)
|
||||
len += W_ASIAN_PUNCT
|
||||
else if (ctype == SHEET_HANGUL)
|
||||
@@ -161,7 +143,7 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
len += W_UNIHAN
|
||||
else if (isThaiDiacritics(s[i]))
|
||||
len += 0 // set width of the glyph as -W_LATIN_WIDE
|
||||
else if (ctype == SHEET_KEYCAP)
|
||||
else if (ctype == SHEET_CUSTOM_SYM)
|
||||
len += SIZE_KEYCAP
|
||||
else
|
||||
len += W_LATIN_WIDE
|
||||
@@ -272,10 +254,6 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
val sheetX: Int
|
||||
val sheetY: Int
|
||||
when (prevInstance) {
|
||||
SHEET_RUNIC -> {
|
||||
sheetX = runicIndexX(ch)
|
||||
sheetY = runicIndexY(ch)
|
||||
}
|
||||
SHEET_EXTA_VARW -> {
|
||||
sheetX = extAindexX(ch)
|
||||
sheetY = extAindexY(ch)
|
||||
@@ -308,11 +286,11 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
sheetX = greekIndexX(ch)
|
||||
sheetY = greekIndexY(ch)
|
||||
}
|
||||
SHEET_THAI_WIDE -> {
|
||||
SHEET_THAI_VARW -> {
|
||||
sheetX = thaiIndexX(ch)
|
||||
sheetY = thaiIndexY(ch)
|
||||
}
|
||||
SHEET_KEYCAP -> {
|
||||
SHEET_CUSTOM_SYM -> {
|
||||
sheetX = keycapIndexX(ch)
|
||||
sheetY = keycapIndexY(ch)
|
||||
}
|
||||
@@ -329,7 +307,7 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
|
||||
// to deal with the height difference of the sheets
|
||||
Math.round(y).toFloat() +
|
||||
(if (prevInstance == SHEET_KEYCAP) (H - SIZE_KEYCAP) / 2 // completely legit height adjustment
|
||||
(if (prevInstance == SHEET_CUSTOM_SYM) (H - SIZE_KEYCAP) / 2 // completely legit height adjustment
|
||||
else 0).toFloat(),
|
||||
|
||||
scale.toFloat(), thisCol, noShadow
|
||||
@@ -353,11 +331,7 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
}
|
||||
|
||||
private fun getSheetType(c: Char): Int {
|
||||
if (isThaiEF(c))
|
||||
return SHEET_EXTA_VARW // will use fourth glyph in EXTA_EF
|
||||
else if (isRunic(c))
|
||||
return SHEET_RUNIC
|
||||
else if (isHangul(c))
|
||||
if (isHangul(c))
|
||||
return SHEET_HANGUL
|
||||
else if (isKana(c))
|
||||
return SHEET_KANA
|
||||
@@ -380,11 +354,9 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
else if (isGreek(c))
|
||||
return SHEET_GREEK_VARW
|
||||
else if (isThai(c))
|
||||
return SHEET_THAI_WIDE
|
||||
else if (c.isColourCode())
|
||||
return SHEET_COLOURCODE
|
||||
else if (isKeycap(c))
|
||||
return SHEET_KEYCAP
|
||||
return SHEET_THAI_VARW
|
||||
else if (isCustomSym(c))
|
||||
return SHEET_CUSTOM_SYM
|
||||
else
|
||||
return SHEET_UNKNOWN// fixed width punctuations
|
||||
// fixed width
|
||||
@@ -437,17 +409,16 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
}
|
||||
}
|
||||
|
||||
asciiWidths[codeOffset + ccode] = glyphWidth
|
||||
glyphWidths[codeOffset + ccode] = glyphWidth
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
internal val glyphWidths: HashMap<Int, Int> = HashMap()
|
||||
|
||||
lateinit internal var hangulSheet: SpriteSheet
|
||||
lateinit internal var asciiSheet: SpriteSheet
|
||||
|
||||
internal val asciiWidths: HashMap<Int, Int> = HashMap()
|
||||
|
||||
lateinit internal var runicSheet: SpriteSheet
|
||||
lateinit internal var extASheet: SpriteSheet
|
||||
lateinit internal var extBSheet: SpriteSheet
|
||||
@@ -459,7 +430,7 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
lateinit internal var uniPunct: SpriteSheet
|
||||
lateinit internal var greekSheet: SpriteSheet
|
||||
lateinit internal var thaiSheet: SpriteSheet
|
||||
lateinit internal var keycapSheet: SpriteSheet
|
||||
lateinit internal var customSheet: SpriteSheet
|
||||
|
||||
internal val JUNG_COUNT = 21
|
||||
internal val JONG_COUNT = 28
|
||||
@@ -475,24 +446,21 @@ open class GameFontBase(val noShadow: Boolean) : Font {
|
||||
|
||||
internal val SIZE_KEYCAP = 18
|
||||
|
||||
internal val SHEET_ASCII_VARW = 0
|
||||
internal val SHEET_HANGUL = 1
|
||||
internal val SHEET_RUNIC = 2
|
||||
internal val SHEET_EXTA_VARW = 3
|
||||
internal val SHEET_EXTB_VARW = 4
|
||||
internal val SHEET_KANA = 5
|
||||
internal val SHEET_CJK_PUNCT = 6
|
||||
internal val SHEET_UNIHAN = 7
|
||||
internal val SHEET_CYRILIC_VARW = 8
|
||||
internal val SHEET_FW_UNI = 9
|
||||
internal val SHEET_UNI_PUNCT = 10
|
||||
internal val SHEET_GREEK_VARW = 11
|
||||
internal val SHEET_THAI_WIDE = 12
|
||||
internal val SHEET_THAI_NARROW = 13
|
||||
internal val SHEET_KEYCAP = 14
|
||||
internal val SHEET_ASCII_VARW = 0
|
||||
internal val SHEET_HANGUL = 1
|
||||
internal val SHEET_EXTA_VARW = 2
|
||||
internal val SHEET_EXTB_VARW = 3
|
||||
internal val SHEET_KANA = 4
|
||||
internal val SHEET_CJK_PUNCT = 5
|
||||
internal val SHEET_UNIHAN = 6
|
||||
internal val SHEET_CYRILIC_VARW = 7
|
||||
internal val SHEET_FW_UNI = 8
|
||||
internal val SHEET_UNI_PUNCT = 9
|
||||
internal val SHEET_GREEK_VARW = 10
|
||||
internal val SHEET_THAI_VARW = 11
|
||||
internal val SHEET_CUSTOM_SYM = 12
|
||||
|
||||
internal val SHEET_UNKNOWN = 254
|
||||
internal val SHEET_COLOURCODE = 255
|
||||
|
||||
lateinit internal var sheetKey: Array<SpriteSheet?>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.torvald.imagefont
|
||||
package net.torvald.terrarumsansbitmap.slick2d
|
||||
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import org.newdawn.slick.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-20.
|
||||
@@ -10,37 +9,37 @@ class GameFontImpl(noShadow: Boolean = false) : GameFontBase(noShadow = noShadow
|
||||
|
||||
init {
|
||||
|
||||
GameFontBase.hangulSheet = SpriteSheet(
|
||||
GameFontBase.hangulSheet = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/hangul_johab.tga", GameFontBase.W_HANGUL, GameFontBase.H)
|
||||
GameFontBase.asciiSheet = SpriteSheet(
|
||||
GameFontBase.asciiSheet = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/ascii_variable.tga", 15, 19, 1)
|
||||
GameFontBase.runicSheet = SpriteSheet(
|
||||
GameFontBase.runicSheet = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/futhark.tga", GameFontBase.W_LATIN_WIDE, GameFontBase.H)
|
||||
GameFontBase.extASheet = SpriteSheet(
|
||||
GameFontBase.extASheet = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/LatinExtA_variable.tga", 15, 19, 1)
|
||||
GameFontBase.extBSheet = SpriteSheet(
|
||||
GameFontBase.extBSheet = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/LatinExtB_variable.tga", 15, 19, 1)
|
||||
GameFontBase.kanaSheet = SpriteSheet(
|
||||
GameFontBase.kanaSheet = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/kana.tga", GameFontBase.W_KANA, GameFontBase.H)
|
||||
GameFontBase.cjkPunct = SpriteSheet(
|
||||
GameFontBase.cjkPunct = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/cjkpunct.tga", GameFontBase.W_ASIAN_PUNCT, GameFontBase.H)
|
||||
GameFontBase.cyrilic = SpriteSheet(
|
||||
GameFontBase.cyrilic = org.newdawn.slick.SpriteSheet(
|
||||
when (Terrarum.gameLocale.substring(0..1)) {
|
||||
"bg" -> "./assets/graphics/fonts/cyrilic_bulgarian_variable.tga"
|
||||
"sr" -> "./assets/graphics/fonts/cyrilic_serbian_variable.tga"
|
||||
else -> "./assets/graphics/fonts/cyrilic_variable.tga"
|
||||
}, 15, 19, 1)
|
||||
GameFontBase.fullwidthForms = SpriteSheet(
|
||||
GameFontBase.fullwidthForms = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/fullwidth_forms.tga", GameFontBase.W_UNIHAN, GameFontBase.H_UNIHAN)
|
||||
GameFontBase.uniPunct = SpriteSheet(
|
||||
GameFontBase.uniPunct = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/unipunct.tga", GameFontBase.W_LATIN_WIDE, GameFontBase.H)
|
||||
GameFontBase.uniHan = SpriteSheet(
|
||||
GameFontBase.uniHan = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/wenquanyi.tga.gz", 16, 16) // ~32 MB
|
||||
GameFontBase.greekSheet = SpriteSheet(
|
||||
GameFontBase.greekSheet = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/greek_variable.tga", 15, 19, 1)
|
||||
GameFontBase.thaiSheet = SpriteSheet(
|
||||
"./assets/graphics/fonts/thai_variable.tga",15, 19, 1)
|
||||
GameFontBase.keycapSheet = SpriteSheet(
|
||||
GameFontBase.thaiSheet = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/thai_variable.tga", 15, 19, 1)
|
||||
GameFontBase.customSheet = org.newdawn.slick.SpriteSheet(
|
||||
"./assets/graphics/fonts/puae000-e0ff.tga", GameFontBase.SIZE_KEYCAP, GameFontBase.SIZE_KEYCAP)
|
||||
|
||||
val shk = arrayOf(
|
||||
@@ -58,21 +57,21 @@ class GameFontImpl(noShadow: Boolean = false) : GameFontBase(noShadow = noShadow
|
||||
GameFontBase.greekSheet,
|
||||
GameFontBase.thaiSheet,
|
||||
null, // Thai EF, filler because not being used right now
|
||||
GameFontBase.keycapSheet
|
||||
GameFontBase.customSheet
|
||||
)
|
||||
GameFontBase.sheetKey = shk
|
||||
|
||||
|
||||
buildWidthTable(asciiSheet, 0, 0..0xFF)
|
||||
buildWidthTable(extASheet, 0x100, 0..0x7F)
|
||||
buildWidthTable(extBSheet, 0x180, 0..0xCF)
|
||||
buildWidthTable(cyrilic, 0x400, 0..0x5F)
|
||||
buildWidthTable(greekSheet, 0x370, 0..0x5F)
|
||||
buildWidthTable(GameFontBase.asciiSheet, 0, 0..0xFF)
|
||||
buildWidthTable(GameFontBase.extASheet, 0x100, 0..0x7F)
|
||||
buildWidthTable(GameFontBase.extBSheet, 0x180, 0..0xCF)
|
||||
buildWidthTable(GameFontBase.cyrilic, 0x400, 0..0x5F)
|
||||
buildWidthTable(GameFontBase.greekSheet, 0x370, 0..0x5F)
|
||||
}
|
||||
|
||||
fun reload() {
|
||||
GameFontBase.cyrilic.destroy()
|
||||
GameFontBase.cyrilic = SpriteSheet(
|
||||
GameFontBase.cyrilic = org.newdawn.slick.SpriteSheet(
|
||||
when (Terrarum.gameLocale.substring(0..1)) {
|
||||
"bg" -> "./assets/graphics/fonts/cyrilic_bulgarian_variable.tga"
|
||||
"sr" -> "./assets/graphics/fonts/cyrilic_serbian_variable.tga"
|
||||
Reference in New Issue
Block a user