diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51eb804 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +demo/out/* +demo/lib/* diff --git a/Slick2d/GameFontBase.kt b/Slick2d/GameFontBase.kt index 72f43c9..0a03dc1 100644 --- a/Slick2d/GameFontBase.kt +++ b/Slick2d/GameFontBase.kt @@ -1,9 +1,9 @@ package net.torvald.imagefont -import net.torvald.terrarum.Terrarum -import net.torvald.terrarum.getPixel import org.lwjgl.opengl.GL11 import org.newdawn.slick.* +import org.newdawn.slick.opengl.Texture +import java.nio.ByteOrder import java.util.* /** @@ -418,17 +418,38 @@ open class GameFontBase : Font { drawString(x + xoff, y, printedBody, color) } - private fun setBlendModeMul() { - GL11.glEnable(GL11.GL_BLEND) - GL11.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ONE_MINUS_SRC_ALPHA) - } - - private fun setBlendModeNormal() { - GL11.glDisable(GL11.GL_BLEND) - Terrarum.appgc.graphics.setDrawMode(Graphics.MODE_NORMAL) - } - fun buildWidthTable(sheet: SpriteSheet, codeOffset: Int, codeRange: IntRange, rows: Int = 16) { + fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this) + + /** @return Intarray(R, G, B, A) */ + fun Texture.getPixel(x: Int, y: Int): IntArray { + val textureWidth = this.textureWidth + val hasAlpha = this.hasAlpha() + + val offset = (if (hasAlpha) 4 else 3) * (textureWidth * y + x) // 4: # of channels (RGBA) + + if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) { + return intArrayOf( + this.textureData[offset].toUint(), + this.textureData[offset + 1].toUint(), + this.textureData[offset + 2].toUint(), + if (hasAlpha) + this.textureData[offset + 3].toUint() + else 255 + ) + } + else { + return intArrayOf( + this.textureData[offset + 2].toUint(), + this.textureData[offset + 1].toUint(), + this.textureData[offset].toUint(), + if (hasAlpha) + this.textureData[offset + 3].toUint() + else 255 + ) + } + } + val binaryCodeOffset = 15 val cellW = sheet.getSubImage(0, 0).width + 1 // should be 16 diff --git a/Slick2d/GameFontImpl.kt b/Slick2d/GameFontImpl.kt index ece5d3a..a717267 100644 --- a/Slick2d/GameFontImpl.kt +++ b/Slick2d/GameFontImpl.kt @@ -1,5 +1,6 @@ package net.torvald.imagefont +import net.torvald.terrarum.imagefont.GameFontDemo import org.newdawn.slick.* /** @@ -27,7 +28,11 @@ class GameFontImpl : GameFontBase() { , W_UNIHAN, H_UNIHAN );*/ GameFontBase.cyrilic = SpriteSheet( - "./assets/graphics/fonts/cyrilic_variable.tga", 15, 19, 1) + when (GameFontDemo.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( "./assets/graphics/fonts/fullwidth_forms.tga", GameFontBase.W_UNIHAN, GameFontBase.H_UNIHAN) GameFontBase.uniPunct = SpriteSheet( @@ -57,7 +62,7 @@ class GameFontImpl : GameFontBase() { GameFontBase.wenQuanYi_2, GameFontBase.greekSheet, GameFontBase.romanianSheet, - GameFontBase.romanianSheetNarrow, + GameFontBase.romanianSheetNarrow ) GameFontBase.sheetKey = shk diff --git a/demo/.idea/.name b/demo/.idea/.name new file mode 100644 index 0000000..d28c669 --- /dev/null +++ b/demo/.idea/.name @@ -0,0 +1 @@ +TerrarumSansDemo \ No newline at end of file diff --git a/demo/.idea/artifacts/unnamed.xml b/demo/.idea/artifacts/unnamed.xml new file mode 100644 index 0000000..dcc4e25 --- /dev/null +++ b/demo/.idea/artifacts/unnamed.xml @@ -0,0 +1,15 @@ + + + $PROJECT_DIR$/out/artifacts/unnamed + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/.idea/libraries/KotlinJavaRuntime.xml b/demo/.idea/libraries/KotlinJavaRuntime.xml new file mode 100644 index 0000000..a83cacd --- /dev/null +++ b/demo/.idea/libraries/KotlinJavaRuntime.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/.idea/misc.xml b/demo/.idea/misc.xml new file mode 100644 index 0000000..0548357 --- /dev/null +++ b/demo/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/demo/.idea/modules.xml b/demo/.idea/modules.xml new file mode 100644 index 0000000..f14e97b --- /dev/null +++ b/demo/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/demo/.idea/workspace.xml b/demo/.idea/workspace.xml new file mode 100644 index 0000000..605e909 --- /dev/null +++ b/demo/.idea/workspace.xml @@ -0,0 +1,610 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "| + ,\n + main + + + \n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1487243351068 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + unnamed + + + + + + + + Kotlin + + + + + + + + + + + + + + + 1.8 + + + + + + + + TerrarumSansDemo + + + + + + + + 1.8 + + + + + + + + KotlinJavaRuntime + + + + + + + + \ No newline at end of file diff --git a/demo/META-INF/MANIFEST.MF b/demo/META-INF/MANIFEST.MF new file mode 100644 index 0000000..908b34f --- /dev/null +++ b/demo/META-INF/MANIFEST.MF @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Class-Path: lib/lwjgl.jar lib/lwjgl_util.jar lib/slick.jar lib/kotlin-reflect.jar lib/kotlin-runtime.jar +Main-Class: net.torvald.terrarum.imagefont.GameFontDemoKt + diff --git a/demo/TerrarumSansDemo.iml b/demo/TerrarumSansDemo.iml new file mode 100644 index 0000000..b19a530 --- /dev/null +++ b/demo/TerrarumSansDemo.iml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/config.properties b/demo/config.properties new file mode 100644 index 0000000..c22105c --- /dev/null +++ b/demo/config.properties @@ -0,0 +1 @@ +locale=enUS \ No newline at end of file diff --git a/demo/src/net/torvald/terrarum/imagefont/GameFontBase.kt b/demo/src/net/torvald/terrarum/imagefont/GameFontBase.kt new file mode 100644 index 0000000..0a03dc1 --- /dev/null +++ b/demo/src/net/torvald/terrarum/imagefont/GameFontBase.kt @@ -0,0 +1,552 @@ +package net.torvald.imagefont + +import org.lwjgl.opengl.GL11 +import org.newdawn.slick.* +import org.newdawn.slick.opengl.Texture +import java.nio.ByteOrder +import java.util.* + +/** + * Created by minjaesong on 16-01-27. + */ +open class GameFontBase : 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) + 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() >= 0xAC00 && c.toInt() < 0xD7A4 + private fun isAscii(c: Char) = c.toInt() >= 0x20 && c.toInt() <= 0xFF + private fun isExtA(c: Char) = c.toInt() >= 0x100 && c.toInt() < 0x180 + private fun isKana(c: Char) = c.toInt() >= 0x3040 && c.toInt() < 0x3100 + private fun isCJKPunct(c: Char) = c.toInt() >= 0x3000 && c.toInt() < 0x3040 + private fun isUniHan(c: Char) = c.toInt() >= 0x3400 && c.toInt() < 0xA000 + private fun isCyrilic(c: Char) = c.toInt() >= 0x400 && c.toInt() < 0x460 + private fun isFullwidthUni(c: Char) = c.toInt() >= 0xFF00 && c.toInt() < 0xFF20 + private fun isUniPunct(c: Char) = c.toInt() >= 0x2000 && c.toInt() < 0x2070 + private fun isWenQuanYi1(c: Char) = c.toInt() >= 0x33F3 && c.toInt() <= 0x69FC + private fun isWenQuanYi2(c: Char) = c.toInt() >= 0x69FD && c.toInt() <= 0x9FDC + private fun isGreek(c: Char) = c.toInt() >= 0x370 && c.toInt() <= 0x3CE + private fun isRomanian(c: Char) = c.toInt() >= 0x218 && c.toInt() <= 0x21A + private fun isRomanianNarrow(c: Char) = c.toInt() == 0x21B + + + + private fun extAindexX(c: Char) = (c.toInt() - 0x100) % 16 + private fun extAindexY(c: Char) = (c.toInt() - 0x100) / 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 uniHanIndexX(c: Char) = (c.toInt() - 0x3400) % 256 + private fun uniHanIndexY(c: Char) = (c.toInt() - 0x3400) / 256 + + 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 wenQuanYiIndexX(c: Char) = + (c.toInt() - if (c.toInt() <= 0x4DB5) 0x33F3 else 0x33F3 + 0x4A) % 32 + private fun wenQuanYi1IndexY(c: Char) = (c.toInt() - (0x33F3 + 0x4A)) / 32 + private fun wenQuanYi2IndexY(c: Char) = (c.toInt() - 0x69FD) / 32 + + private fun greekIndexX(c: Char) = (c.toInt() - 0x370) % 16 + private fun greekIndexY(c: Char) = (c.toInt() - 0x370) / 16 + + private fun romanianIndexX(c: Char) = c.toInt() - 0x218 + private fun romanianIndexY(c: Char) = 0 + + 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 val narrowWidthSheets = arrayOf( + SHEET_EXTB_ROMANIAN_NARROW + ) + private val unihanWidthSheets = arrayOf( + SHEET_UNIHAN, + SHEET_FW_UNI, + SHEET_WENQUANYI_1, + SHEET_WENQUANYI_2 + ) + private val zeroWidthSheets = arrayOf( + SHEET_COLOURCODE + ) + private val variableWidthSheets = arrayOf( + SHEET_ASCII_VARW, + SHEET_CYRILIC_VARW, + SHEET_EXTA_VARW, + SHEET_GREEK_VARW + ) + + + override fun getWidth(s: String) = getWidthSubstr(s, s.length) + + private fun getWidthSubstr(s: String, endIndex: Int): Int { + var len = 0 + for (i in 0..endIndex - 1) { + val chr = s[i] + val ctype = getSheetType(s[i]) + + if (chr.toInt() == 0x21B) // Romanian t; HAX! + len += 6 + else if (variableWidthSheets.contains(ctype)) { + try { + len += asciiWidths[chr.toInt()]!! + } + catch (e: kotlin.KotlinNullPointerException) { + println("KotlinNullPointerException on glyph number ${Integer.toHexString(chr.toInt()).toUpperCase()}") + System.exit(1) + } + } + else if (zeroWidthSheets.contains(ctype)) + len += 0 + else if (narrowWidthSheets.contains(ctype)) + len += W_LATIN_NARROW + else if (ctype == SHEET_CJK_PUNCT) + len += W_ASIAN_PUNCT + else if (ctype == SHEET_HANGUL) + len += W_HANGUL + else if (ctype == SHEET_KANA) + len += W_KANA + else if (unihanWidthSheets.contains(ctype)) + len += W_UNIHAN + else + len += W_LATIN_WIDE + + if (i < endIndex - 1) len += interchar + } + return len * scale + } + + override fun getHeight(s: String) = H * scale + + override fun getLineHeight() = H * scale + + override fun drawString(x: Float, y: Float, s: String) = drawString(x, y, s, Color.white) + + override fun drawString(x: Float, y: Float, s: String, color: Color) { + GL11.glEnable(GL11.GL_BLEND) + GL11.glColorMask(true, true, true, true) + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA) + + var thisCol = color + + // hangul fonts first + //hangulSheet.startUse() // disabling texture binding to make the font coloured + // JOHAB + for (i in 0..s.length - 1) { + val ch = s[i] + + if (isHangul(ch)) { + val hIndex = ch.toInt() - 0xAC00 + + val indexCho = getHanChosung(hIndex) + val indexJung = getHanJungseong(hIndex) + val indexJong = getHanJongseong(hIndex) + + val choRow = getHanInitialRow(hIndex) + val jungRow = getHanMedialRow(hIndex) + val jongRow = getHanFinalRow(hIndex) + + val glyphW = getWidth(ch.toString()) + + hangulSheet.getSubImage(indexCho, choRow).drawWithShadow( + Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(), + Math.round(((H - H_HANGUL) / 2).toFloat() + y + 1f).toFloat(), + scale.toFloat(), thisCol + ) + hangulSheet.getSubImage(indexJung, jungRow).drawWithShadow( + Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(), + Math.round(((H - H_HANGUL) / 2).toFloat() + y + 1f).toFloat(), + scale.toFloat(), thisCol + ) + hangulSheet.getSubImage(indexJong, jongRow).drawWithShadow( + Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(), + Math.round(((H - H_HANGUL) / 2).toFloat() + y + 1f).toFloat(), + scale.toFloat(), thisCol + ) + } + } + //hangulSheet.endUse() + + // unihan fonts + /*uniHan.startUse(); + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + + if (isUniHan(ch)) { + int glyphW = getWidth("" + ch); + uniHan.renderInUse( + Math.round(x + + getWidthSubstr(s, i + 1) - glyphW + ) + , Math.round((H - H_UNIHAN) / 2 + y) + , uniHanIndexX(ch) + , uniHanIndexY(ch) + ); + } + } + + uniHan.endUse();*/ + + // WenQuanYi 1 + //wenQuanYi_1.startUse() + + for (i in 0..s.length - 1) { + val ch = s[i] + + if (isWenQuanYi1(ch)) { + val glyphW = getWidth("" + ch) + wenQuanYi_1.getSubImage(wenQuanYiIndexX(ch), wenQuanYi1IndexY(ch)).drawWithShadow( + Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(), + Math.round((H - H_UNIHAN) / 2 + y).toFloat(), + scale.toFloat(), thisCol + ) + } + } + + //wenQuanYi_1.endUse() + // WenQuanYi 2 + //wenQuanYi_2.startUse() + + for (i in 0..s.length - 1) { + val ch = s[i] + + if (isWenQuanYi2(ch)) { + val glyphW = getWidth("" + ch) + wenQuanYi_2.getSubImage(wenQuanYiIndexX(ch), wenQuanYi2IndexY(ch)).drawWithShadow( + Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(), + Math.round((H - H_UNIHAN) / 2 + y).toFloat(), + scale.toFloat(), thisCol + ) + } + } + + //wenQuanYi_2.endUse() + + // regular fonts + var prevInstance = -1 + for (i in 0..s.length - 1) { + val ch = s[i] + + if (!isHangul(ch) && !isUniHan(ch)) { + + // if not init, endUse first + if (prevInstance != -1) { + //sheetKey[prevInstance].endUse() + } + //sheetKey[getSheetType(ch)].startUse() + prevInstance = getSheetType(ch) + + val sheetX: Int + val sheetY: Int + when (prevInstance) { + SHEET_EXTA_VARW -> { + sheetX = extAindexX(ch) + sheetY = extAindexY(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_EXTB_ROMANIAN_WIDE -> { + sheetX = romanianIndexX(ch) + sheetY = romanianIndexY(ch) + } + SHEET_EXTB_ROMANIAN_NARROW -> { + sheetX = 0 + sheetY = 0 + } + else -> { + sheetX = ch.toInt() % 16 + sheetY = ch.toInt() / 16 + } + } + + val glyphW = getWidth("" + ch) + try { + sheetKey[prevInstance]!!.getSubImage(sheetX, sheetY).drawWithShadow( + Math.round(x + getWidthSubstr(s, i + 1) - glyphW).toFloat(), + + // to deal with the height difference of the sheets + Math.round(y).toFloat() + (if (prevInstance == SHEET_CJK_PUNCT) -1 // height hack + else if (prevInstance == SHEET_FW_UNI) (H - H_HANGUL) / 2 // completely legit height adjustment + else 0).toFloat(), + + scale.toFloat(), thisCol + ) + } + catch (e: ArrayIndexOutOfBoundsException) { + // character that does not exist in the sheet. No render, pass. + } + catch (e1: RuntimeException) { + // System.err.println("[GameFontBase] RuntimeException raised while processing character '$ch' (U+${Integer.toHexString(ch.toInt()).toUpperCase()})") + // e1.printStackTrack() + } + } + + } + if (prevInstance != -1) { + //sheetKey[prevInstance].endUse() + } + + GL11.glEnd() + } + + private fun getSheetType(c: Char): Int { + // EFs + if (isRomanianNarrow(c)) + return SHEET_EXTB_ROMANIAN_NARROW + else 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 (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 (isRomanian(c)) + return SHEET_EXTB_ROMANIAN_WIDE + else + return SHEET_UNKNOWN// fixed width punctuations + // fixed width + // fallback + } + + /** + * Draw part of a string to the screen. Note that this will still position the text as though + * it's part of the bigger string. + * @param x + * * + * @param y + * * + * @param s + * * + * @param color + * * + * @param startIndex + * * + * @param endIndex + */ + override fun drawString(x: Float, y: Float, s: String, color: Color, startIndex: Int, endIndex: Int) { + val unprintedHead = s.substring(0, startIndex) + val printedBody = s.substring(startIndex, endIndex) + val xoff = getWidth(unprintedHead) + drawString(x + xoff, y, printedBody, color) + } + + fun buildWidthTable(sheet: SpriteSheet, codeOffset: Int, codeRange: IntRange, rows: Int = 16) { + fun Byte.toUint() = java.lang.Byte.toUnsignedInt(this) + + /** @return Intarray(R, G, B, A) */ + fun Texture.getPixel(x: Int, y: Int): IntArray { + val textureWidth = this.textureWidth + val hasAlpha = this.hasAlpha() + + val offset = (if (hasAlpha) 4 else 3) * (textureWidth * y + x) // 4: # of channels (RGBA) + + if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) { + return intArrayOf( + this.textureData[offset].toUint(), + this.textureData[offset + 1].toUint(), + this.textureData[offset + 2].toUint(), + if (hasAlpha) + this.textureData[offset + 3].toUint() + else 255 + ) + } + else { + return intArrayOf( + this.textureData[offset + 2].toUint(), + this.textureData[offset + 1].toUint(), + this.textureData[offset].toUint(), + if (hasAlpha) + this.textureData[offset + 3].toUint() + else 255 + ) + } + } + + val binaryCodeOffset = 15 + + val cellW = sheet.getSubImage(0, 0).width + 1 // should be 16 + val cellH = sheet.getSubImage(0, 0).height + 1 // should be 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 (sheet.texture.getPixel(codeStartX, codeStartY + downCtr)[3] == 255) { + glyphWidth = glyphWidth or (1 shl downCtr) + } + } + + asciiWidths[codeOffset + ccode] = glyphWidth + } + } + + companion object { + + lateinit internal var hangulSheet: SpriteSheet + lateinit internal var asciiSheet: SpriteSheet + + internal val asciiWidths: HashMap = HashMap() + + lateinit internal var extASheet: SpriteSheet + lateinit internal var kanaSheet: SpriteSheet + lateinit internal var cjkPunct: SpriteSheet + // static SpriteSheet uniHan; + lateinit internal var cyrilic: SpriteSheet + lateinit internal var fullwidthForms: SpriteSheet + lateinit internal var uniPunct: SpriteSheet + lateinit internal var wenQuanYi_1: SpriteSheet + lateinit internal var wenQuanYi_2: SpriteSheet + lateinit internal var greekSheet: SpriteSheet + lateinit internal var romanianSheet: SpriteSheet + lateinit internal var romanianSheetNarrow: SpriteSheet + + internal val JUNG_COUNT = 21 + internal val JONG_COUNT = 28 + + internal val W_ASIAN_PUNCT = 10 + internal val W_HANGUL = 11 + internal val W_KANA = 12 + internal val W_UNIHAN = 16 + internal val W_LATIN_WIDE = 9 // width of regular letters, including m + internal val W_LATIN_NARROW = 5 // width of letter f, t, i, l + + internal val H = 20 + internal val H_HANGUL = 16 + internal val H_UNIHAN = 16 + internal val H_KANA = 20 + + internal val SHEET_ASCII_VARW = 0 + internal val SHEET_HANGUL = 1 + internal val SHEET_EXTA_VARW = 2 + internal val SHEET_KANA = 3 + internal val SHEET_CJK_PUNCT = 4 + internal val SHEET_UNIHAN = 5 + internal val SHEET_CYRILIC_VARW = 6 + internal val SHEET_FW_UNI = 7 + internal val SHEET_UNI_PUNCT = 8 + internal val SHEET_WENQUANYI_1 = 9 + internal val SHEET_WENQUANYI_2 = 10 + internal val SHEET_GREEK_VARW = 11 + internal val SHEET_EXTB_ROMANIAN_WIDE = 12 + internal val SHEET_EXTB_ROMANIAN_NARROW = 13 + + internal val SHEET_UNKNOWN = 254 + internal val SHEET_COLOURCODE = 255 + + lateinit internal var sheetKey: Array + + internal var interchar = 0 + internal var scale = 1 + set(value) { + if (value > 0) field = value + else throw IllegalArgumentException("Font scale cannot be zero or negative (input: $value)") + } + + }// end of companion object +} + +fun Image.drawWithShadow(x: Float, y: Float, color: Color) = + this.drawWithShadow(x, y, 1f, color) + +fun Image.drawWithShadow(x: Float, y: Float, scale: Float, color: Color) { + this.draw(x + 1, y + 1, scale, color.darker(0.5f)) + this.draw(x , y + 1, scale, color.darker(0.5f)) + this.draw(x + 1, y , scale, color.darker(0.5f)) + + this.draw(x, y, scale, color) +} diff --git a/demo/src/net/torvald/terrarum/imagefont/GameFontDemo.kt b/demo/src/net/torvald/terrarum/imagefont/GameFontDemo.kt new file mode 100644 index 0000000..016a03c --- /dev/null +++ b/demo/src/net/torvald/terrarum/imagefont/GameFontDemo.kt @@ -0,0 +1,94 @@ +package net.torvald.terrarum.imagefont + +import net.torvald.imagefont.GameFontImpl +import org.newdawn.slick.* +import java.io.File +import java.io.FileInputStream +import java.nio.file.FileSystems +import java.nio.file.Files +import java.util.* + +/** + * Created by SKYHi14 on 2017-02-16. + */ + +class GameFontDemo : BasicGame("Terrarum Sans Bitmap Demo") { + + lateinit var gameFont: Font + + override fun init(p0: GameContainer?) { + gameFont = GameFontImpl() + } + + override fun update(gc: GameContainer, delta: Int) { + } + + override fun render(gc: GameContainer, g: Graphics) { + g.font = gameFont + g.background = Color(0x282828) + text.forEachIndexed { i, s -> + g.drawString(s, 10f, 10f + i * gameFont.lineHeight) + } + } + + companion object { + lateinit var gameLocale: String + var hasError: Boolean + val text = ArrayList() + + init { + try { + val prop = Properties() + prop.load(FileInputStream("./config.properties")) + + gameLocale = prop.getProperty("locale") + + if (gameLocale.length < 2) + throw IllegalArgumentException("Bad locale setting: “$gameLocale”") + + + Files.lines(FileSystems.getDefault().getPath("./text.txt")).forEach( + { text.add(it) } + ) + + hasError = false + } + catch (e: Exception) { + gameLocale = "enUS" + hasError = true + text.add("There was some problem loading the demo :(") + text.add("This is what JVM says:") + text.add("") + e.message!!.split('\n').forEach { + text.add(it) + } + } + } + } +} + +fun main(args: Array) { + + System.setProperty("java.library.path", "lib") + System.setProperty("org.lwjgl.librarypath", File("lib").absolutePath) + + val WIDTH = 1000 + val HEIGHT = 800 + + try { + val appgc = AppGameContainer(GameFontDemo()) + appgc.setDisplayMode(WIDTH, HEIGHT, false) + + appgc.setMultiSample(0) + appgc.setShowFPS(false) + + // game will run normally even if it is not focused + appgc.setUpdateOnlyWhenVisible(false) + appgc.alwaysRender = true + + appgc.start() + } + catch (ex: Exception) { + ex.printStackTrace() + } +} \ No newline at end of file diff --git a/demo/src/net/torvald/terrarum/imagefont/GameFontImpl.kt b/demo/src/net/torvald/terrarum/imagefont/GameFontImpl.kt new file mode 100644 index 0000000..a717267 --- /dev/null +++ b/demo/src/net/torvald/terrarum/imagefont/GameFontImpl.kt @@ -0,0 +1,75 @@ +package net.torvald.imagefont + +import net.torvald.terrarum.imagefont.GameFontDemo +import org.newdawn.slick.* + +/** + * Created by minjaesong on 16-01-20. + */ +class GameFontImpl : GameFontBase() { + + init { + + GameFontBase.hangulSheet = SpriteSheet( + "./assets/graphics/fonts/hangul_johab.tga", GameFontBase.W_HANGUL, GameFontBase.H_HANGUL) + GameFontBase.asciiSheet = SpriteSheet( + "./assets/graphics/fonts/ascii_variable.tga", 15, 19, 1) + GameFontBase.extASheet = SpriteSheet( + "./assets/graphics/fonts/LatinExtA_variable.tga", 15, 19, 1) + GameFontBase.kanaSheet = SpriteSheet( + "./assets/graphics/fonts/kana.tga", GameFontBase.W_KANA, GameFontBase.H_KANA) + GameFontBase.cjkPunct = SpriteSheet( + "./assets/graphics/fonts/cjkpunct.tga", GameFontBase.W_ASIAN_PUNCT, GameFontBase.H_KANA) + /*uniHan = new SpriteSheet( + "./assets/graphics/fonts/unifont_unihan" + + ((!terrarum.gameLocale.contains("zh")) + ? "_ja" : "") + +".tga" + , W_UNIHAN, H_UNIHAN + );*/ + GameFontBase.cyrilic = SpriteSheet( + when (GameFontDemo.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( + "./assets/graphics/fonts/fullwidth_forms.tga", GameFontBase.W_UNIHAN, GameFontBase.H_UNIHAN) + GameFontBase.uniPunct = SpriteSheet( + "./assets/graphics/fonts/unipunct.tga", GameFontBase.W_LATIN_WIDE, GameFontBase.H) + GameFontBase.wenQuanYi_1 = SpriteSheet( + "./assets/graphics/fonts/wenquanyi_11pt_part1.tga", 16, 18, 2) + GameFontBase.wenQuanYi_2 = SpriteSheet( + "./assets/graphics/fonts/wenquanyi_11pt_part2.tga", 16, 18, 2) + GameFontBase.greekSheet = SpriteSheet( + "./assets/graphics/fonts/greek_variable.tga", 15, 19, 1) + GameFontBase.romanianSheet = SpriteSheet( + "./assets/graphics/fonts/romana_wide.tga", GameFontBase.W_LATIN_WIDE, GameFontBase.H) + GameFontBase.romanianSheetNarrow = SpriteSheet( + "./assets/graphics/fonts/romana_narrow.tga", GameFontBase.W_LATIN_NARROW, GameFontBase.H) + + val shk = arrayOf( + GameFontBase.asciiSheet, + GameFontBase.hangulSheet, + GameFontBase.extASheet, + GameFontBase.kanaSheet, + GameFontBase.cjkPunct, + null, // Full unihan, filler because we're using WenQuanYi + GameFontBase.cyrilic, + GameFontBase.fullwidthForms, + GameFontBase.uniPunct, + GameFontBase.wenQuanYi_1, + GameFontBase.wenQuanYi_2, + GameFontBase.greekSheet, + GameFontBase.romanianSheet, + GameFontBase.romanianSheetNarrow + ) + GameFontBase.sheetKey = shk + + + buildWidthTable(asciiSheet, 0, 0..0xFF) + buildWidthTable(extASheet, 0x100, 0..0x7F) + buildWidthTable(cyrilic, 0x400, 0..0x5F) + buildWidthTable(greekSheet, 0x370, 0..0x5F) + } +} diff --git a/demo/text.txt b/demo/text.txt new file mode 100644 index 0000000..ec1567b --- /dev/null +++ b/demo/text.txt @@ -0,0 +1,37 @@ +Set locale in config.properties to “bgBG” for alternative Bulgarian letters, “srSR” for Serbian. + +ABCDEFGHIJKLM NOPQRSTUVWXYZ +abcdefghijklm nopqrstuvwxyz + +Syö salmiakkia, koska se on hyvää sinulle + +The Olympic marmot (Marmota olympus) is a rodent in the squirrel family, Sciuridae. +It lives only in the U.S. state of Washington, at middle elevations on the Olympic Peninsula. +About the size of a domestic cat, an adult weighs around 8 kg (18 lb) in summer. + +Brná je část statutárního a krajského města Ústí nad Labem v České republice, spadající +pod městský obvod Ústí nad Labem-Střekov. Nachází se asi pět kilometrů jižně od centra +města v Českém středohoří na pravém břehu řeky Labe. + +Malaysia er en forholdsvis ung stat. Sin endelige udstrækning fik den først i 1965 efter +at Singapore trak sig ud. Staten blev grundlagt ved en sammenslutning af flere tidligere +britiske besiddelser, foreløbigt i 1957 og endeligt i 1963. + +Το θάλλιο συνοδεύει κυρίως θειούχα ορυκτά βασικών μετάλλων, όπως ο σφαλερίτης, ο σιδηροπυρίτης +και ο γαληνίτης ενώ αναφέρονται και εμφανίσεις του σε κονδύλους μαγγανίου στους βυθούς των ωκεανών. +διαφυλάξτε γενικά τη ζωή σας από βαθειά ψυχικά τραύματα +ΔΙΑΦΥΛΆΞΤΕ ΓΕΝΙΚΆ ΤΗ ΖΩΉ ΣΑΣ ΑΠΌ ΒΑΘΕΙΆ ΨΥΧΙΚΆ ΤΡΑΎΜΑΤΑ +Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства. +Příliš žluťoučký kůň úpěl ďábelské ódy. Árvíztűrő tükörfúrógép. +Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich. +Pijamalı hasta yağız şoföre çabucak güvendi. Kŕdeľ ďatľov učí koňa žrať kôru. +Voix ambiguë d'un cœur qui au zéphyr préfère les jattes de kiwi. +Înjurând pițigăiat, zoofobul comandă vexat whisky și tequila. + +sjaldgæft ekki stjórnarskrárvarin + +Also supports: +‛Unicode’ „quotation marks“—dashes…‼ +으웽~. 얘! 위에 이 애 우유의 양 외워와! 아오~ 왜요? 어여! 예... 웬 초콜릿? 제가 원했던 건 뻥튀기 쬐끔과 의류예요. 얘야, 왜 또 불평? 퀡퇣풿횂 +とりなくこゑす ゆめさませ みよあけわたる ひんかしを そらいろはえて おきつへに ほふねむれゐぬ もやのうち +鳥啼く声す 夢覚ませ 見よ明け渡る 東を 空色栄えて 沖つ辺に 帆船群れゐぬ 靄の中, \ No newline at end of file