From f9a1edd6b943998e3e101d2022ffd604a6ffe420 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 16 Jun 2017 15:09:19 +0900 Subject: [PATCH] Terrarum Sans Bitmap imple on GDX -- wip --- src/net/torvald/terrarum/TerrarumGDX.kt | 7 + src/net/torvald/terrarum/TestTestTest.kt | 29 +- .../terrarumsansbitmap/gdx/GameFontBase.kt | 544 ++++++++++++++++++ .../gdx/TextureRegionPack.kt | 45 ++ .../slick2d}/GameFontBase.kt | 92 +-- .../slick2d}/GameFontImpl.kt | 47 +- 6 files changed, 673 insertions(+), 91 deletions(-) create mode 100644 src/net/torvald/terrarum/TerrarumGDX.kt create mode 100644 src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt create mode 100644 src/net/torvald/terrarumsansbitmap/gdx/TextureRegionPack.kt rename src/net/torvald/{imagefont => terrarumsansbitmap/slick2d}/GameFontBase.kt (89%) rename src/net/torvald/{imagefont => terrarumsansbitmap/slick2d}/GameFontImpl.kt (64%) diff --git a/src/net/torvald/terrarum/TerrarumGDX.kt b/src/net/torvald/terrarum/TerrarumGDX.kt new file mode 100644 index 000000000..58afc7b0a --- /dev/null +++ b/src/net/torvald/terrarum/TerrarumGDX.kt @@ -0,0 +1,7 @@ +package net.torvald.terrarum + +/** + * Created by minjaesong on 2017-06-15. + */ +object TerrarumGDX { +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/TestTestTest.kt b/src/net/torvald/terrarum/TestTestTest.kt index d2c2fb0eb..ec83a2b44 100644 --- a/src/net/torvald/terrarum/TestTestTest.kt +++ b/src/net/torvald/terrarum/TestTestTest.kt @@ -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) { // LWJGL 3 won't work? java.lang.VerifyError val config = LwjglApplicationConfiguration() + config.useGL30 = true + config.vSyncEnabled = false + //config.foregroundFPS = 9999 LwjglApplication(TestTestTest(), config) } \ No newline at end of file diff --git a/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt b/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt new file mode 100644 index 000000000..5d2c67a61 --- /dev/null +++ b/src/net/torvald/terrarumsansbitmap/gdx/GameFontBase.kt @@ -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 = 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)") + } + } + +} \ No newline at end of file diff --git a/src/net/torvald/terrarumsansbitmap/gdx/TextureRegionPack.kt b/src/net/torvald/terrarumsansbitmap/gdx/TextureRegionPack.kt new file mode 100644 index 000000000..5e46bd5fe --- /dev/null +++ b/src/net/torvald/terrarumsansbitmap/gdx/TextureRegionPack.kt @@ -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 + + 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(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() + } + +} \ No newline at end of file diff --git a/src/net/torvald/imagefont/GameFontBase.kt b/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt similarity index 89% rename from src/net/torvald/imagefont/GameFontBase.kt rename to src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt index 66595d22b..f6c879dfc 100644 --- a/src/net/torvald/imagefont/GameFontBase.kt +++ b/src/net/torvald/terrarumsansbitmap/slick2d/GameFontBase.kt @@ -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 = HashMap() + lateinit internal var hangulSheet: SpriteSheet lateinit internal var asciiSheet: SpriteSheet - - internal val asciiWidths: HashMap = 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 diff --git a/src/net/torvald/imagefont/GameFontImpl.kt b/src/net/torvald/terrarumsansbitmap/slick2d/GameFontImpl.kt similarity index 64% rename from src/net/torvald/imagefont/GameFontImpl.kt rename to src/net/torvald/terrarumsansbitmap/slick2d/GameFontImpl.kt index 66d1eeb16..e09e284aa 100644 --- a/src/net/torvald/imagefont/GameFontImpl.kt +++ b/src/net/torvald/terrarumsansbitmap/slick2d/GameFontImpl.kt @@ -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"