mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-10 13:51:53 +09:00
Former-commit-id: d5be0e95ba259d566d6d5d20eb576010a149ae7d Former-commit-id: 9502c7cd7e738147e31d2e9824e48bea24d00abf
622 lines
19 KiB
Java
622 lines
19 KiB
Java
package com.Torvald.ImageFont;
|
||
|
||
import com.Torvald.Terrarum.Terrarum;
|
||
import org.lwjgl.opengl.GL11;
|
||
import org.newdawn.slick.*;
|
||
|
||
import java.util.Arrays;
|
||
|
||
/**
|
||
* Created by minjaesong on 16-01-27.
|
||
*/
|
||
public class GameFontBase implements Font {
|
||
|
||
static SpriteSheet hangulSheet;
|
||
static SpriteSheet asciiSheet;
|
||
static SpriteSheet asciiSheetEF;
|
||
static SpriteSheet runicSheet;
|
||
static SpriteSheet extASheet;
|
||
static SpriteSheet extASheetEF;
|
||
static SpriteSheet kanaSheet;
|
||
static SpriteSheet cjkPunct;
|
||
// static SpriteSheet uniHan;
|
||
static SpriteSheet cyrilic;
|
||
static SpriteSheet cyrilicEF;
|
||
static SpriteSheet fullwidthForms;
|
||
static SpriteSheet uniPunct;
|
||
static SpriteSheet wenQuanYi_1;
|
||
static SpriteSheet wenQuanYi_2;
|
||
|
||
static final int JUNG_COUNT = 21;
|
||
static final int JONG_COUNT = 28;
|
||
|
||
static final int W_CJK = 10;
|
||
static final int W_UNIHAN = 16;
|
||
static final int W_LATIN_WIDE = 9; // width of regular letters, including m
|
||
static final int W_LATIN_NARROW = 5; // width of letter f, t, i, l
|
||
static final int H = 20;
|
||
static final int H_HANGUL = 16;
|
||
static final int H_UNIHAN = 16;
|
||
static final int H_KANA = 20;
|
||
|
||
static final int SHEET_ASCII_EM = 0;
|
||
static final int SHEET_ASCII_EF = 1;
|
||
static final int SHEET_HANGUL = 2;
|
||
static final int SHEET_RUNIC = 3;
|
||
static final int SHEET_EXTA_EM = 4;
|
||
static final int SHEET_EXTA_EF = 5;
|
||
static final int SHEET_KANA = 6;
|
||
static final int SHEET_CJK_PUNCT = 7;
|
||
static final int SHEET_UNIHAN = 8;
|
||
static final int SHEET_CYRILIC_EM = 9;
|
||
static final int SHEET_CYRILIC_EF = 10;
|
||
static final int SHEET_FW_UNI = 11;
|
||
static final int SHEET_UNI_PUNCT = 12;
|
||
static final int SHEET_WENQUANYI_1 = 13;
|
||
static final int SHEET_WENQUANYI_2 = 14;
|
||
|
||
static SpriteSheet[] sheetKey;
|
||
static final Character[] asciiEFList = {
|
||
' ','!','"','\'','(',')',',','.',':',';','I','[',']','`','f','i'
|
||
,'j','l','t','{','|','}',0xA1,'Ì','Í','Î','Ï','ì','í','î','ï','·'
|
||
};
|
||
|
||
static final Character[] extAEFList = {
|
||
0x12E, 0x12F, 0x130, 0x131, 0x135, 0x13A, 0x13C, 0x142, 0x163, 0x167, 0x17F
|
||
};
|
||
|
||
static final Character[] cyrilecEFList = {
|
||
0x406, 0x407, 0x456, 0x457, 0x458
|
||
};
|
||
|
||
/**
|
||
* 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:
|
||
* ᛭ᛋᛁᚴᚱᛁᚦᛦ᛭
|
||
* ᛭ᛂᛚᛋᛅ᛭ᛏᚱᚢᛏᚾᛁᚾᚴᚢᚾᛅ᛬ᛅᚱᚾᛅᛏᛅᛚᛋ
|
||
*/
|
||
static final Character[] runicList = {
|
||
'ᚠ','ᚢ','ᚦ','ᚬ','ᚱ','ᚴ','ᚼ','ᚾ','ᛁ','ᛅ','ᛋ','ᛏ','ᛒ','ᛘ','ᛚ','ᛦ'
|
||
,'ᛂ','᛬','᛫','᛭','ᛮ','ᛯ','ᛰ'
|
||
};
|
||
|
||
static int interchar = 0;
|
||
|
||
|
||
public GameFontBase() throws SlickException {
|
||
|
||
}
|
||
|
||
private int[] getHan(int hanIndex) {
|
||
int han_x = hanIndex % JONG_COUNT;
|
||
int han_y = hanIndex / JONG_COUNT;
|
||
int[] ret = {han_x, han_y};
|
||
return ret;
|
||
}
|
||
|
||
private int getHanChosung(int hanIndex) {
|
||
return hanIndex / (JUNG_COUNT * JONG_COUNT);
|
||
}
|
||
|
||
private int getHanJungseong(int hanIndex) {
|
||
return hanIndex / (JONG_COUNT) % JUNG_COUNT;
|
||
}
|
||
|
||
private int getHanJongseong(int hanIndex) {
|
||
return hanIndex % JONG_COUNT;
|
||
}
|
||
|
||
private int getHanChoseongRow(int hanIndex) {
|
||
int jungseongIndex = getHanJungseong(hanIndex);
|
||
Integer[] jungseongWide = {8, 12, 13, 17, 18, 21};
|
||
Integer[] jungseongComplex = {9, 10, 11, 14, 15, 16, 22};
|
||
int ret;
|
||
|
||
if (Arrays.asList(jungseongWide).contains(jungseongIndex)) {
|
||
ret = 2;
|
||
}
|
||
else if (Arrays.asList(jungseongComplex).contains(jungseongIndex)) {
|
||
ret = 4;
|
||
}
|
||
else {
|
||
ret = 0;
|
||
}
|
||
return (getHanJongseong(hanIndex) == 0) ? ret : ret + 1;
|
||
}
|
||
|
||
private int getHanJungseongRow(int hanIndex) {
|
||
return (getHanJongseong(hanIndex) == 0) ? 6 : 7;
|
||
}
|
||
|
||
private int getHanJongseongRow() {
|
||
return 8;
|
||
}
|
||
|
||
private boolean isAsciiEF(char c) {
|
||
return (Arrays.asList(asciiEFList).contains(c));
|
||
}
|
||
|
||
private boolean isExtAEF(char c) {
|
||
return (Arrays.asList(extAEFList).contains(c));
|
||
}
|
||
|
||
private boolean isHangul(char c) {
|
||
return (c >= 0xAC00 && c < 0xD7A4);
|
||
}
|
||
|
||
private boolean isAscii(char c) { return (c > 0 && c <= 0xFF); }
|
||
|
||
private boolean isRunic(char c) {
|
||
return (Arrays.asList(runicList).contains(c));
|
||
}
|
||
|
||
private boolean isExtA(char c) {
|
||
return (c >= 0x100 && c < 0x180);
|
||
}
|
||
|
||
private boolean isKana(char c) {
|
||
return (c >= 0x3040 && c < 0x3100);
|
||
}
|
||
|
||
private boolean isCJKPunct(char c) {
|
||
return (c >= 0x3000 && c < 0x3040);
|
||
}
|
||
|
||
private boolean isUniHan(char c) {
|
||
return (c >= 0x3400 && c < 0xA000);
|
||
}
|
||
|
||
private boolean isCyrilic(char c) {
|
||
return (c >= 0x400 && c < 0x460);
|
||
}
|
||
|
||
private boolean isCyrilicEF(char c) {
|
||
return (Arrays.asList(cyrilecEFList).contains(c));
|
||
}
|
||
|
||
private boolean isFullwidthUni(char c) {
|
||
return (c >= 0xFF00 && c < 0xFF20);
|
||
}
|
||
|
||
private boolean isUniPunct(char c) {
|
||
return (c >= 0x2000 && c < 0x2070);
|
||
}
|
||
|
||
private boolean isWenQuanYi1(char c) {
|
||
return (c >= 0x33F3 && c <= 0x69FC);
|
||
}
|
||
|
||
private boolean isWenQuanYi2(char c) {
|
||
return (c >= 0x69FD && c <= 0x9FDC);
|
||
}
|
||
|
||
/** */
|
||
|
||
private int asciiEFindexX(char c) {
|
||
return (Arrays.asList(asciiEFList).indexOf(c) % 16);
|
||
}
|
||
|
||
private int asciiEFindexY(char c) {
|
||
return (Arrays.asList(asciiEFList).indexOf(c) / 16);
|
||
}
|
||
|
||
private int extAEFindexX(char c) {
|
||
return (Arrays.asList(extAEFList).indexOf(c) % 16);
|
||
}
|
||
|
||
private int extAEFindexY(char c) {
|
||
return (Arrays.asList(extAEFList).indexOf(c) / 16);
|
||
}
|
||
|
||
private int runicIndexX(char c) {
|
||
return (Arrays.asList(runicList).indexOf(c) % 16);
|
||
}
|
||
|
||
private int runicIndexY(char c) {
|
||
return (Arrays.asList(runicList).indexOf(c) / 16);
|
||
}
|
||
|
||
private int kanaIndexX(char c) {
|
||
return (c - 0x3040) % 16;
|
||
}
|
||
|
||
private int kanaIndexY(char c) {
|
||
return (c - 0x3040) / 16;
|
||
}
|
||
|
||
private int cjkPunctIndexX(char c) {
|
||
return (c - 0x3000) % 16;
|
||
}
|
||
|
||
private int cjkPunctIndexY(char c) {
|
||
return (c - 0x3000) / 16;
|
||
}
|
||
|
||
private int uniHanIndexX(char c) {
|
||
return (c - 0x3400) % 256;
|
||
}
|
||
|
||
private int uniHanIndexY(char c) {
|
||
return (c - 0x3400) / 256;
|
||
}
|
||
|
||
private int cyrilicIndexX(char c) {
|
||
return (c - 0x400) % 16;
|
||
}
|
||
|
||
private int cyrilicIndexY(char c) {
|
||
return (c - 0x400) / 16;
|
||
}
|
||
|
||
private int cyrilicEFindexX(char c) {
|
||
return (Arrays.asList(cyrilecEFList).indexOf(c) % 16);
|
||
}
|
||
|
||
private int cyrilicEFindexY(char c) {
|
||
return (Arrays.asList(cyrilecEFList).indexOf(c) / 16);
|
||
}
|
||
|
||
private int fullwidthUniIndexX(char c) {
|
||
return (c - 0xFF00) % 16;
|
||
}
|
||
|
||
private int fullwidthUniIndexY(char c) {
|
||
return (c - 0xFF00) / 16;
|
||
}
|
||
|
||
private int uniPunctIndexX(char c) {
|
||
return (c - 0x2000) % 16;
|
||
}
|
||
|
||
private int uniPunctIndexY(char c) {
|
||
return (c - 0x2000) / 16;
|
||
}
|
||
|
||
private int wenQuanYiIndexX(char c) {
|
||
// v Ext1 v Unihan
|
||
return (c - (c <= 0x4DB5 ? 0x33F3 : 0x33F3 + 0x4A)) % 32;
|
||
}
|
||
|
||
private int wenQuanYi1IndexY(char c) {
|
||
return (c - (0x33F3 + 0x4A)) / 32;
|
||
}
|
||
|
||
private int wenQuanYi2IndexY(char c) {
|
||
return (c - 0x69FD) / 32;
|
||
}
|
||
|
||
@Override
|
||
public int getWidth(String s) {
|
||
return getWidthSubstr(s, s.length());
|
||
}
|
||
|
||
private int getWidthSubstr(String s, int endIndex) {
|
||
int len = 0;
|
||
for (int i = 0; i < endIndex; i++) {
|
||
int c = getSheetType(s.charAt(i));
|
||
|
||
if (i > 0 && s.charAt(i) > 0x20) { // Kerning
|
||
int cpre = getSheetType(s.charAt(i - 1));
|
||
if (
|
||
((cpre == SHEET_UNIHAN || cpre == SHEET_HANGUL)
|
||
&& !(c == SHEET_UNIHAN || c == SHEET_HANGUL))
|
||
|
||
|| ((c == SHEET_UNIHAN || c == SHEET_HANGUL)
|
||
&& !(cpre == SHEET_UNIHAN || cpre == SHEET_HANGUL))
|
||
) {
|
||
// margin after/before hangul/unihan
|
||
len += 2;
|
||
}
|
||
else if ((c == SHEET_HANGUL || c == SHEET_KANA)
|
||
&& (cpre == SHEET_HANGUL || cpre == SHEET_KANA)) {
|
||
// margin between hangul/kana
|
||
len += 1;
|
||
}
|
||
|
||
}
|
||
|
||
if (c == SHEET_ASCII_EF || c == SHEET_EXTA_EF || c == SHEET_CYRILIC_EF)
|
||
len += W_LATIN_NARROW;
|
||
else if (c == SHEET_KANA || c == SHEET_HANGUL || c == SHEET_CJK_PUNCT)
|
||
len += W_CJK;
|
||
else if (c == SHEET_UNIHAN || c == SHEET_FW_UNI || c == SHEET_WENQUANYI_1 || c == SHEET_WENQUANYI_2)
|
||
len += W_UNIHAN;
|
||
else
|
||
len += W_LATIN_WIDE;
|
||
|
||
if (i < endIndex - 1) len += interchar;
|
||
}
|
||
return len;
|
||
}
|
||
|
||
@Override
|
||
public int getHeight(String s) {
|
||
return H;
|
||
}
|
||
|
||
@Override
|
||
public int getLineHeight() {
|
||
return H;
|
||
}
|
||
|
||
@Override
|
||
public void drawString(float x, float y, String s) {
|
||
drawString(x, y, s, Color.white);
|
||
}
|
||
|
||
@Override
|
||
public void drawString(float x, float y, String s, Color color) {
|
||
// hangul fonts first
|
||
hangulSheet.startUse();
|
||
// JOHAB
|
||
for (int i = 0; i < s.length(); i++) {
|
||
char ch = s.charAt(i);
|
||
|
||
if (isHangul(ch)) {
|
||
int hIndex = ch - 0xAC00;
|
||
|
||
int indexCho = getHanChosung(hIndex);
|
||
int indexJung = getHanJungseong(hIndex);
|
||
int indexJong = getHanJongseong(hIndex);
|
||
|
||
int choRow = getHanChoseongRow(hIndex);
|
||
int jungRow = getHanJungseongRow(hIndex);
|
||
int jongRow = getHanJongseongRow();
|
||
|
||
int glyphW = getWidth("" + ch);
|
||
|
||
// chosung
|
||
hangulSheet.renderInUse(
|
||
Math.round(x
|
||
+ getWidthSubstr(s, i + 1) - glyphW
|
||
)
|
||
, Math.round((H - H_HANGUL) / 2 + y + 1)
|
||
, indexCho
|
||
, choRow
|
||
);
|
||
// jungseong
|
||
hangulSheet.renderInUse(
|
||
Math.round(x
|
||
+ getWidthSubstr(s, i + 1) - glyphW
|
||
)
|
||
, Math.round((H - H_HANGUL) / 2 + y + 1)
|
||
, indexJung
|
||
, jungRow
|
||
);
|
||
// jongseong
|
||
hangulSheet.renderInUse(
|
||
Math.round(x
|
||
+ getWidthSubstr(s, i + 1) - glyphW
|
||
)
|
||
, Math.round((H - H_HANGUL) / 2 + y + 1)
|
||
, indexJong
|
||
, jongRow
|
||
);
|
||
}
|
||
}
|
||
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 (int i = 0; i < s.length(); i++) {
|
||
char ch = s.charAt(i);
|
||
|
||
if (isWenQuanYi1(ch)) {
|
||
int glyphW = getWidth("" + ch);
|
||
wenQuanYi_1.renderInUse(
|
||
Math.round(x
|
||
+ getWidthSubstr(s, i + 1) - glyphW
|
||
)
|
||
, Math.round((H - H_UNIHAN) / 2 + y - 1)
|
||
, wenQuanYiIndexX(ch)
|
||
, wenQuanYi1IndexY(ch)
|
||
);
|
||
}
|
||
}
|
||
|
||
wenQuanYi_1.endUse();
|
||
wenQuanYi_2.startUse();
|
||
|
||
for (int i = 0; i < s.length(); i++) {
|
||
char ch = s.charAt(i);
|
||
|
||
if (isWenQuanYi2(ch)) {
|
||
int glyphW = getWidth("" + ch);
|
||
wenQuanYi_2.renderInUse(
|
||
Math.round(x
|
||
+ getWidthSubstr(s, i + 1) - glyphW
|
||
)
|
||
, Math.round((H - H_UNIHAN) / 2 + y - 1)
|
||
, wenQuanYiIndexX(ch)
|
||
, wenQuanYi2IndexY(ch)
|
||
);
|
||
}
|
||
}
|
||
|
||
wenQuanYi_2.endUse();
|
||
|
||
//ascii fonts
|
||
int prevInstance = -1;
|
||
for (int i = 0; i < s.length(); i++) {
|
||
char ch = s.charAt(i);
|
||
|
||
if (!isHangul(ch) && !isUniHan(ch)) {
|
||
|
||
// if not init, enduse first
|
||
if (prevInstance != -1) {
|
||
sheetKey[prevInstance].endUse();
|
||
}
|
||
sheetKey[getSheetType(ch)].startUse();
|
||
prevInstance = getSheetType(ch);
|
||
|
||
int sheetX;
|
||
int sheetY;
|
||
switch (prevInstance) {
|
||
case SHEET_ASCII_EF:
|
||
sheetX = asciiEFindexX(ch);
|
||
sheetY = asciiEFindexY(ch);
|
||
break;
|
||
case SHEET_EXTA_EF:
|
||
sheetX = extAEFindexX(ch);
|
||
sheetY = extAEFindexY(ch);
|
||
break;
|
||
case SHEET_RUNIC:
|
||
sheetX = runicIndexX(ch);
|
||
sheetY = runicIndexY(ch);
|
||
break;
|
||
case SHEET_EXTA_EM:
|
||
sheetX = (ch - 0x100) % 16;
|
||
sheetY = (ch - 0x100) / 16;
|
||
break;
|
||
case SHEET_KANA:
|
||
sheetX = kanaIndexX(ch);
|
||
sheetY = kanaIndexY(ch);
|
||
break;
|
||
case SHEET_CJK_PUNCT:
|
||
sheetX = cjkPunctIndexX(ch);
|
||
sheetY = cjkPunctIndexY(ch);
|
||
break;
|
||
case SHEET_CYRILIC_EM:
|
||
sheetX = cyrilicIndexX(ch);
|
||
sheetY = cyrilicIndexY(ch);
|
||
break;
|
||
case SHEET_CYRILIC_EF:
|
||
sheetX = cyrilicEFindexX(ch);
|
||
sheetY = cyrilicEFindexY(ch);
|
||
break;
|
||
case SHEET_FW_UNI:
|
||
sheetX = fullwidthUniIndexX(ch);
|
||
sheetY = fullwidthUniIndexY(ch);
|
||
break;
|
||
case SHEET_UNI_PUNCT:
|
||
sheetX = uniPunctIndexX(ch);
|
||
sheetY = uniPunctIndexY(ch);
|
||
break;
|
||
default:
|
||
sheetX = ch % 16;
|
||
sheetY = ch / 16;
|
||
break;
|
||
}
|
||
|
||
try {
|
||
int glyphW = getWidth("" + ch);
|
||
sheetKey[prevInstance].renderInUse(
|
||
Math.round(x + getWidthSubstr(s, i + 1) - glyphW)
|
||
// Interchar: pull punct right next to hangul to the left
|
||
+ ((i > 0 && isHangul(s.charAt(i - 1))) ? -3 : 0)
|
||
, Math.round(y)
|
||
+ ((prevInstance == SHEET_CJK_PUNCT) ? -1 :
|
||
(prevInstance == SHEET_FW_UNI) ? (H - H_HANGUL) / 2 : 0)
|
||
, sheetX
|
||
, sheetY
|
||
);
|
||
}
|
||
catch (ArrayIndexOutOfBoundsException e) {
|
||
// System.out.println("ArrayIndexOutOfBoundsException")
|
||
// System.out.println("char: '" + ch + "' (" + String.valueOf((int) ch) + ")");
|
||
// System.out.println("sheet: " + prevInstance);
|
||
// System.out.println("sheetX: " + sheetX);
|
||
// System.out.println("sheetY: " + sheetY);
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
if (prevInstance != -1) {
|
||
sheetKey[prevInstance].endUse();
|
||
}
|
||
}
|
||
|
||
private int getSheetType(char c) {
|
||
// EFs
|
||
if (isAsciiEF(c)) return SHEET_ASCII_EF;
|
||
else if (isExtAEF(c)) return SHEET_EXTA_EF;
|
||
else if (isCyrilicEF(c)) return SHEET_CYRILIC_EF;
|
||
// fixed width
|
||
else if (isRunic(c)) return SHEET_RUNIC;
|
||
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_EM;
|
||
else if (isExtA(c)) return SHEET_EXTA_EM;
|
||
else if (isCyrilic(c)) return SHEET_CYRILIC_EM;
|
||
else if (isUniPunct(c)) return SHEET_UNI_PUNCT;
|
||
// fixed width punctuations
|
||
else if (isCJKPunct(c)) return SHEET_CJK_PUNCT;
|
||
else if (isFullwidthUni(c)) return SHEET_FW_UNI;
|
||
|
||
else return SHEET_ASCII_EM; // 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
|
||
public void drawString(float x, float y, String s, Color color, int startIndex, int endIndex) {
|
||
String unprintedHead = s.substring(0, startIndex);
|
||
String printedBody = s.substring(startIndex, endIndex);
|
||
int xoff = getWidth(unprintedHead);
|
||
drawString(x + xoff, y, printedBody, color);
|
||
}
|
||
|
||
public void reloadUnihan() throws SlickException {
|
||
|
||
}
|
||
|
||
/**
|
||
* Set margin between characters
|
||
* @param margin
|
||
*/
|
||
public void setInterchar(int margin) {
|
||
interchar = margin;
|
||
}
|
||
|
||
private void setBlendModeMul() {
|
||
GL11.glEnable(GL11.GL_BLEND);
|
||
GL11.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||
}
|
||
|
||
private void setBlendModeNormal() {
|
||
GL11.glDisable(GL11.GL_BLEND);
|
||
Terrarum.appgc.getGraphics().setDrawMode(Graphics.MODE_NORMAL);
|
||
}
|
||
}
|