new tagging system
@@ -50,17 +50,53 @@ Rightmost vertical column (should be 20 px tall) contains the tags. Tags are def
|
||||
K -,
|
||||
K |= Tags used by the "Keming Machine"
|
||||
K |
|
||||
K -' ,-Nudging control bit (see below)
|
||||
N --'
|
||||
X -, write-on-top and centre-aligned: Align to this X pos of prev char
|
||||
X | (if this is zero, floorOf(width/2) will be used instead)
|
||||
X | NOT write-on-top: nudge the texture by this pixels to the
|
||||
X -' left (if N is unset) or right (if N is set)
|
||||
K -' ,- Nudging Bits (see below)
|
||||
n --'
|
||||
X -,
|
||||
X |= Diacritics Anchor Points (see below)
|
||||
X |
|
||||
X -'
|
||||
A -,_ 0 Align 1 Align 0 Align 1 Align before
|
||||
A -' 0 left 0 right 1 centre 1 the glyph
|
||||
D --write-on-top, usually it's diatritics but not always (e.g. devanagari vowel sign O)
|
||||
S -,_ 0 Stack 1 Stack 0 Before 1 Up &
|
||||
(MSB) S -' 0 up 0 down 1 &After 1 Down (e.g. U+0C48)
|
||||
|
||||
TODO:
|
||||
c - Nudging
|
||||
Y - Anchor point Y for undefined, undefined, undefined
|
||||
X - Anchor point X for undefined, undefined, undefined
|
||||
Y - Anchor point Y for centre-aligned diacritics, undefined, undefined
|
||||
X - Anchor point X for centre-aligned diacritics, undefined, undefined
|
||||
|
||||
* Nudging Bits encoding:
|
||||
|
||||
<MSB,Red> SXXXXXXX SYYYYYYY 00000000 <LSB,Blue>
|
||||
|
||||
Each X and Y numbers are Signed 8-Bit Integer.
|
||||
X-positive: nudges towards left
|
||||
Y-positive: nudges towards up
|
||||
|
||||
* Diacritics Anchor Point Encoding:
|
||||
|
||||
<MSB,Red> 1Y1Y1Y1Y 1Y2Y2Y2Y 1Y3Y3Y3Y <LSB,Blue>
|
||||
<MSB,Red> 1X1X1X1X 1X2X2X2X 1X3X3X3X <LSB,Blue>
|
||||
|
||||
where Red is first, Green is second, Blue is the third diacritics.
|
||||
MSB for each word must be set to indicate the value is being used.
|
||||
|
||||
-= NOTE =-
|
||||
|
||||
This encoding involves one HACK: using 0th diacritics' X-anchor pos as a type selector
|
||||
This hack applies only when write-on-top bit is set.
|
||||
Interpretation:
|
||||
DIA_OVERLAY = 1
|
||||
DIA_JOINER = 2
|
||||
|
||||
Right now, only the type-0 diacritics anchor point is used by the font.
|
||||
|
||||
TODO: use D-bit to give each diacritic a type
|
||||
|
||||
```
|
||||
|
||||
#### Stack Up/Down
|
||||
|
||||
|
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 320 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
BIN
demo.PNG
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
@@ -1,22 +1,35 @@
|
||||
package net.torvald.terrarumsansbitmap
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2021-11-25.
|
||||
*/
|
||||
data class DiacriticsAnchor(val type: Int, val x: Int, val y: Int, val xUsed: Boolean, val yUsed: Boolean)
|
||||
/**
|
||||
* Created by minjaesong on 2018-08-07.
|
||||
*/
|
||||
data class GlyphProps(
|
||||
val width: Int,
|
||||
val writeOnTop: Boolean,
|
||||
val alignWhere: Int, // ALIGN_LEFT..ALIGN_BEFORE
|
||||
val alignXPos: Int, // 0..15 or DIA_OVERLAY/DIA_JOINER depends on the context
|
||||
val rtl: Boolean = false,
|
||||
|
||||
val isLowheight: Boolean = false,
|
||||
|
||||
val nudgeX: Int = 0,
|
||||
val nudgeY: Int = 0,
|
||||
|
||||
val diacriticsAnchors: Array<DiacriticsAnchor> = Array(6) { DiacriticsAnchor(it, 0, 0, false, false) },
|
||||
|
||||
val alignWhere: Int = 0, // ALIGN_LEFT..ALIGN_BEFORE
|
||||
|
||||
val writeOnTop: Boolean = false,
|
||||
|
||||
val stackWhere: Int = 0, // STACK_UP..STACK_UP_N_DOWN
|
||||
var nudgeRight: Boolean = false,
|
||||
var extInfo: IntArray? = null,
|
||||
|
||||
val extInfo: IntArray = DEFAULT_EXTINFO,
|
||||
|
||||
val hasKernData: Boolean = false,
|
||||
val isLowheight: Boolean = false,
|
||||
val isKernYtype: Boolean = false,
|
||||
val kerningMask: Int = 255
|
||||
val kerningMask: Int = 255,
|
||||
|
||||
val rtl: Boolean = false,
|
||||
) {
|
||||
companion object {
|
||||
const val ALIGN_LEFT = 0
|
||||
@@ -33,9 +46,11 @@ data class GlyphProps(
|
||||
const val DIA_JOINER = 2
|
||||
|
||||
private fun Boolean.toInt() = if (this) 1 else 0
|
||||
|
||||
val DEFAULT_EXTINFO = IntArray(15)
|
||||
}
|
||||
|
||||
constructor(width: Int, tags: Int) : this(
|
||||
/*constructor(width: Int, tags: Int) : this(
|
||||
width,
|
||||
tags.ushr(7).and(1) == 1,
|
||||
tags.ushr(5).and(3),
|
||||
@@ -59,21 +74,30 @@ data class GlyphProps(
|
||||
isLowheight,
|
||||
isKernYtype,
|
||||
kerningMask
|
||||
)
|
||||
)*/
|
||||
|
||||
fun isOverlay() = writeOnTop && alignXPos == 1
|
||||
// fun isOverlay() = writeOnTop && alignXPos == 1
|
||||
|
||||
override fun hashCode(): Int {
|
||||
val tags = rtl.toInt() or alignXPos.shl(1) or alignWhere.shl(5) or
|
||||
val tags = rtl.toInt() or alignWhere.shl(5) or
|
||||
writeOnTop.toInt().shl(7) or stackWhere.shl(8)
|
||||
|
||||
var hash = -2128831034
|
||||
|
||||
extInfo?.forEach {
|
||||
extInfo.forEach {
|
||||
hash = hash xor it
|
||||
hash = hash * 16777619
|
||||
}
|
||||
|
||||
diacriticsAnchors.forEach {
|
||||
hash = hash xor it.type
|
||||
hash = hash * 16777619
|
||||
hash = hash xor (it.x or (if (it.xUsed) 128 else 0))
|
||||
hash = hash * 16777619
|
||||
hash = hash xor (it.y or (if (it.yUsed) 128 else 0))
|
||||
hash = hash * 16777619
|
||||
}
|
||||
|
||||
hash = hash xor tags
|
||||
hash = hash * 167677619
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.badlogic.gdx.graphics.Pixmap
|
||||
import com.badlogic.gdx.graphics.Texture
|
||||
import com.badlogic.gdx.graphics.g2d.*
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||
import net.torvald.terrarumsansbitmap.DiacriticsAnchor
|
||||
import net.torvald.terrarumsansbitmap.GlyphProps
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.FileOutputStream
|
||||
@@ -302,7 +303,7 @@ class TerrarumSansBitmap(
|
||||
// make sure null char is actually null (draws nothing and has zero width)
|
||||
sheets[SHEET_ASCII_VARW].regions[0].setColor(0)
|
||||
sheets[SHEET_ASCII_VARW].regions[0].fill()
|
||||
glyphProps[0] = GlyphProps(0, 0)
|
||||
glyphProps[0] = GlyphProps(0)
|
||||
}
|
||||
|
||||
override fun getLineHeight(): Float = H.toFloat()
|
||||
@@ -331,7 +332,7 @@ class TerrarumSansBitmap(
|
||||
|
||||
private lateinit var tempLinotype: Texture
|
||||
|
||||
private var nullProp = GlyphProps(15, 0)
|
||||
private var nullProp = GlyphProps(15)
|
||||
|
||||
private val pixmapOffsetY = 10
|
||||
|
||||
@@ -342,6 +343,8 @@ class TerrarumSansBitmap(
|
||||
|
||||
fun drawNormalised(batch: Batch, codepoints: CodepointSequence, x: Float, y: Float): GlyphLayout? {
|
||||
|
||||
// codepoints.forEach { dbgprn("${it.charInfo()} ${glyphProps[it]}") }
|
||||
|
||||
// Q&D fix for issue #12
|
||||
// When the line ends with a diacritics, the whole letter won't render
|
||||
// If the line starts with a letter-with-diacritic, it will error out
|
||||
@@ -696,6 +699,9 @@ class TerrarumSansBitmap(
|
||||
return intArrayOf(sheetX, sheetY)
|
||||
}
|
||||
|
||||
private fun Boolean.toInt() = if (this) 1 else 0
|
||||
private fun Int.tagify() = if (this and 255 == 0) 0 else this
|
||||
|
||||
private fun buildWidthTable(pixmap: Pixmap, codeRange: Iterable<Int>, cols: Int = 16) {
|
||||
val binaryCodeOffset = W_VAR_INIT
|
||||
|
||||
@@ -709,64 +715,63 @@ class TerrarumSansBitmap(
|
||||
|
||||
val codeStartX = cellX + binaryCodeOffset
|
||||
val codeStartY = cellY
|
||||
val tagStartY = codeStartY + 10
|
||||
|
||||
var width = 0
|
||||
var tags = 0
|
||||
|
||||
for (y in 0..4) {
|
||||
// if ALPHA is not zero, assume it's 1
|
||||
if (pixmap.getPixel(codeStartX, codeStartY + y).and(0xFF) != 0) {
|
||||
width = width or (1 shl y)
|
||||
}
|
||||
}
|
||||
|
||||
for (y in 0..9) {
|
||||
// if ALPHA is not zero, assume it's 1
|
||||
if (pixmap.getPixel(codeStartX, tagStartY + y).and(0xFF) != 0) {
|
||||
tags = tags or (1 shl y)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// lowheight bit
|
||||
val isLowHeight = (pixmap.getPixel(codeStartX, codeStartY + 5).and(0xFF) != 0)
|
||||
val width = (0..4).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y).and(255) != 0).toInt() shl y) }
|
||||
val isLowHeight = (pixmap.getPixel(codeStartX, codeStartY + 5).and(255) != 0)
|
||||
|
||||
// Keming machine parameters
|
||||
val kerningBit1 = pixmap.getPixel(codeStartX, codeStartY + 6)
|
||||
val kerningBit2 = pixmap.getPixel(codeStartX, codeStartY + 7)
|
||||
val kerningBit3 = pixmap.getPixel(codeStartX, codeStartY + 8)
|
||||
val isKerningYtype = ((kerningBit1 and 0x80000000.toInt()) != 0)
|
||||
val kerningMask = kerningBit1.ushr(8).and(0xFFFFFF)
|
||||
val hasKerningBit = kerningBit1 and 255 != 0//(kerningBit1 and 255 != 0 && kerningMask != 0xFFFF)
|
||||
val kerningBit1 = pixmap.getPixel(codeStartX, codeStartY + 6).tagify()
|
||||
val kerningBit2 = pixmap.getPixel(codeStartX, codeStartY + 7).tagify()
|
||||
val kerningBit3 = pixmap.getPixel(codeStartX, codeStartY + 8).tagify()
|
||||
val kerningBit4 = pixmap.getPixel(codeStartX, codeStartY + 9).tagify()
|
||||
var isKernYtype = ((kerningBit1 and 0x80000000.toInt()) != 0)
|
||||
var kerningMask = kerningBit1.ushr(8).and(0xFFFFFF)
|
||||
val hasKernData = kerningBit1 and 255 != 0//(kerningBit1 and 255 != 0 && kerningMask != 0xFFFF)
|
||||
if (!hasKernData) {
|
||||
isKernYtype = false
|
||||
kerningMask = 255
|
||||
}
|
||||
|
||||
val nudgingBits = pixmap.getPixel(codeStartX, codeStartY + 10).tagify()
|
||||
val nudgeX = nudgingBits.ushr(24).toByte().toInt() // signed 8-bit int
|
||||
val nudgeY = nudgingBits.ushr(16).toByte().toInt() // signed 8-bit int
|
||||
|
||||
//dbgprn("$code: Width $width, tags $tags")
|
||||
if (hasKerningBit)
|
||||
dbgprn("U+${code.toString(16).padStart(4, '0').toUpperCase()}: W $width, tags $tags, low? $isLowHeight, kern ${kerningMask.toString(16).padStart(6,'0')} (raw: ${kerningBit1.toLong().and(4294967295).toString(16).padStart(8,'0')})")
|
||||
val diacriticsAnchors = (0..5).map {
|
||||
val yPos = 11 + (it / 3) * 2
|
||||
val shift = (3 - (it % 3)) * 8
|
||||
val yPixel = pixmap.getPixel(codeStartX, codeStartY + yPos).tagify()
|
||||
val xPixel = pixmap.getPixel(codeStartX, codeStartY + yPos + 1).tagify()
|
||||
val y = (yPixel ushr shift) and 127
|
||||
val x = (xPixel ushr shift) and 127
|
||||
val yUsed = (yPixel ushr shift) >= 128
|
||||
val xUsed = (yPixel ushr shift) >= 128
|
||||
|
||||
/*val isDiacritics = pixmap.getPixel(codeStartX, codeStartY + H - 1).and(0xFF) != 0
|
||||
if (isDiacritics)
|
||||
glyphWidth = -glyphWidth*/
|
||||
DiacriticsAnchor(it, x, y, xUsed, yUsed)
|
||||
}.toTypedArray()
|
||||
|
||||
glyphProps[code] = if (hasKerningBit) GlyphProps(width, tags, isLowHeight, isKerningYtype, kerningMask) else GlyphProps(width, tags)
|
||||
val alignWhere = (0..1).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y + 15).and(255) != 0).toInt() shl y) }
|
||||
|
||||
val writeOnTop = pixmap.getPixel(codeStartX, codeStartY + 17).and(255) != 0
|
||||
|
||||
val stackWhere = (0..1).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y + 18).and(255) != 0).toInt() shl y) }
|
||||
|
||||
glyphProps[code] = GlyphProps(width, isLowHeight, nudgeX, nudgeY, diacriticsAnchors, alignWhere, writeOnTop, stackWhere, GlyphProps.DEFAULT_EXTINFO, hasKernData, isKernYtype, kerningMask)
|
||||
|
||||
// if (nudgingBits != 0) dbgprn("${code.charInfo()} nudgeX=$nudgeX, nudgeY=$nudgeY, nudgingBits=0x${nudgingBits.toString(16)}")
|
||||
|
||||
// extra info
|
||||
val extCount = glyphProps[code]?.requiredExtInfoCount() ?: 0
|
||||
if (extCount > 0) {
|
||||
|
||||
glyphProps[code]?.extInfo = IntArray(extCount)
|
||||
|
||||
for (x in 0 until extCount) {
|
||||
var info = 0
|
||||
for (y in 0..18) {
|
||||
// if ALPHA is not zero, assume it's 1
|
||||
if (pixmap.getPixel(cellX + x, cellY + y).and(0xFF) != 0) {
|
||||
if (pixmap.getPixel(cellX + x, cellY + y).and(255) != 0) {
|
||||
info = info or (1 shl y)
|
||||
}
|
||||
}
|
||||
|
||||
glyphProps[code]!!.extInfo!![x] = info
|
||||
glyphProps[code]!!.extInfo[x] = info
|
||||
}
|
||||
}
|
||||
|
||||
@@ -775,27 +780,27 @@ class TerrarumSansBitmap(
|
||||
|
||||
private fun buildWidthTableFixed() {
|
||||
// fixed-width props
|
||||
codeRange[SHEET_CJK_PUNCT].forEach { glyphProps[it] = GlyphProps(W_ASIAN_PUNCT, 0) }
|
||||
codeRange[SHEET_CUSTOM_SYM].forEach { glyphProps[it] = GlyphProps(20, 0) }
|
||||
codeRange[SHEET_FW_UNI].forEach { glyphProps[it] = GlyphProps(W_UNIHAN, 0) }
|
||||
codeRange[SHEET_HANGUL].forEach { glyphProps[it] = GlyphProps(W_HANGUL_BASE, 0) }
|
||||
codeRangeHangulCompat.forEach { glyphProps[it] = GlyphProps(W_HANGUL_BASE, 0) }
|
||||
codeRange[SHEET_KANA].forEach { glyphProps[it] = GlyphProps(W_KANA, 0) }
|
||||
codeRange[SHEET_RUNIC].forEach { glyphProps[it] = GlyphProps(9, 0) }
|
||||
codeRange[SHEET_UNIHAN].forEach { glyphProps[it] = GlyphProps(W_UNIHAN, 0) }
|
||||
(0xD800..0xDFFF).forEach { glyphProps[it] = GlyphProps(0, 0) }
|
||||
(0x100000..0x10FFFF).forEach { glyphProps[it] = GlyphProps(0, 0) }
|
||||
(0xFFFA0..0xFFFFF).forEach { glyphProps[it] = GlyphProps(0, 0) }
|
||||
codeRange[SHEET_CJK_PUNCT].forEach { glyphProps[it] = GlyphProps(W_ASIAN_PUNCT) }
|
||||
codeRange[SHEET_CUSTOM_SYM].forEach { glyphProps[it] = GlyphProps(20) }
|
||||
codeRange[SHEET_FW_UNI].forEach { glyphProps[it] = GlyphProps(W_UNIHAN) }
|
||||
codeRange[SHEET_HANGUL].forEach { glyphProps[it] = GlyphProps(W_HANGUL_BASE) }
|
||||
codeRangeHangulCompat.forEach { glyphProps[it] = GlyphProps(W_HANGUL_BASE) }
|
||||
codeRange[SHEET_KANA].forEach { glyphProps[it] = GlyphProps(W_KANA) }
|
||||
codeRange[SHEET_RUNIC].forEach { glyphProps[it] = GlyphProps(9) }
|
||||
codeRange[SHEET_UNIHAN].forEach { glyphProps[it] = GlyphProps(W_UNIHAN) }
|
||||
(0xD800..0xDFFF).forEach { glyphProps[it] = GlyphProps(0) }
|
||||
(0x100000..0x10FFFF).forEach { glyphProps[it] = GlyphProps(0) }
|
||||
(0xFFFA0..0xFFFFF).forEach { glyphProps[it] = GlyphProps(0) }
|
||||
|
||||
|
||||
// manually add width of one orphan insular letter
|
||||
// WARNING: glyphs in 0xA770..0xA778 has invalid data, further care is required
|
||||
glyphProps[0x1D79] = GlyphProps(9, 0)
|
||||
glyphProps[0x1D79] = GlyphProps(9)
|
||||
|
||||
|
||||
// U+007F is DEL originally, but this font stores bitmap of Replacement Character (U+FFFD)
|
||||
// to this position. String replacer will replace U+FFFD into U+007F.
|
||||
glyphProps[0x7F] = GlyphProps(15, 0)
|
||||
glyphProps[0x7F] = GlyphProps(15)
|
||||
|
||||
}
|
||||
|
||||
@@ -884,7 +889,7 @@ class TerrarumSansBitmap(
|
||||
posXbuffer[charIndex] = posXbuffer[nonDiacriticCounter]
|
||||
}
|
||||
else if (!thisProp.writeOnTop) {
|
||||
posXbuffer[charIndex] = ((if (thisProp.nudgeRight) 1 else -1) * thisProp.alignXPos) +
|
||||
posXbuffer[charIndex] = -thisProp.nudgeX +
|
||||
when (itsProp.alignWhere) {
|
||||
GlyphProps.ALIGN_RIGHT ->
|
||||
posXbuffer[nonDiacriticCounter] + W_VAR_INIT + alignmentOffset + interchar + kerning + extraWidth
|
||||
@@ -898,9 +903,10 @@ class TerrarumSansBitmap(
|
||||
|
||||
stackUpwardCounter = 0
|
||||
stackDownwardCounter = 0
|
||||
extraWidth = (if (thisProp.nudgeRight) -1 else 1) * thisProp.alignXPos // NOTE: sign is flipped!
|
||||
extraWidth = thisProp.nudgeX // NOTE: sign is flipped!
|
||||
}
|
||||
else if (thisProp.writeOnTop && thisProp.alignXPos == GlyphProps.DIA_JOINER) {
|
||||
// FIXME HACK: using 0th diacritics' X-anchor pos as a type selector
|
||||
else if (thisProp.writeOnTop && thisProp.diacriticsAnchors[0].x == GlyphProps.DIA_JOINER) {
|
||||
posXbuffer[charIndex] = when (itsProp.alignWhere) {
|
||||
GlyphProps.ALIGN_RIGHT ->
|
||||
posXbuffer[nonDiacriticCounter] + W_VAR_INIT + alignmentOffset
|
||||
@@ -919,7 +925,7 @@ class TerrarumSansBitmap(
|
||||
posXbuffer[nonDiacriticCounter] - (W_VAR_INIT - itsProp.width)
|
||||
}
|
||||
GlyphProps.ALIGN_CENTRE -> {
|
||||
val alignXPos = if (itsProp.alignXPos == 0) itsProp.width.div(2) else itsProp.alignXPos
|
||||
val alignXPos = if (itsProp.diacriticsAnchors[0].x == 0) itsProp.width.div(2) else itsProp.diacriticsAnchors[0].x
|
||||
|
||||
if (itsProp.alignWhere == GlyphProps.ALIGN_RIGHT) {
|
||||
posXbuffer[nonDiacriticCounter] + alignXPos + (itsProp.width + 1).div(2)
|
||||
@@ -948,7 +954,7 @@ class TerrarumSansBitmap(
|
||||
//dbgprn("AAARRRRHHHH for character ${thisChar.toHex()}")
|
||||
//dbgprn("lastNonDiacriticChar: ${lastNonDiacriticChar.toHex()}")
|
||||
//dbgprn("cond: ${thisProp.alignXPos == GlyphProps.DIA_OVERLAY}, charIndex: $charIndex")
|
||||
if (thisProp.alignXPos == GlyphProps.DIA_OVERLAY)
|
||||
if (thisProp.diacriticsAnchors[0].x == GlyphProps.DIA_OVERLAY)
|
||||
posYbuffer[charIndex] -= H_OVERLAY_LOWERCASE_SHIFTDOWN * (!flipY).toSign() // if minus-assign doesn't work, try plus-assign
|
||||
else
|
||||
posYbuffer[charIndex] -= H_STACKUP_LOWERCASE_SHIFTDOWN * (!flipY).toSign() // if minus-assign doesn't work, try plus-assign
|
||||
@@ -978,10 +984,10 @@ class TerrarumSansBitmap(
|
||||
posXbuffer[posXbuffer.lastIndex] = 1 + posXbuffer[posXbuffer.lastIndex - 1] + // adding 1 to house the shadow
|
||||
if (lastCharProp?.writeOnTop == true) {
|
||||
val realDiacriticWidth = if (lastCharProp.alignWhere == GlyphProps.ALIGN_CENTRE) {
|
||||
(lastCharProp.width).div(2) + penultCharProp.alignXPos
|
||||
(lastCharProp.width).div(2) + penultCharProp.diacriticsAnchors[0].x
|
||||
}
|
||||
else if (lastCharProp.alignWhere == GlyphProps.ALIGN_RIGHT) {
|
||||
(lastCharProp.width) + penultCharProp.alignXPos
|
||||
(lastCharProp.width) + penultCharProp.diacriticsAnchors[0].x
|
||||
}
|
||||
else 0
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.badlogic.gdx.graphics.g2d.Batch
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont
|
||||
import com.badlogic.gdx.graphics.g2d.GlyphLayout
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||
import net.torvald.terrarumsansbitmap.DiacriticsAnchor
|
||||
import net.torvald.terrarumsansbitmap.GlyphProps
|
||||
import net.torvald.terrarumsansbitmap.gdx.*
|
||||
import net.torvald.terrarumsansbitmap.gdx.CodePoint
|
||||
@@ -178,7 +179,7 @@ class TerrarumTypewriterBitmap(
|
||||
pixmap.dispose() // you are terminated
|
||||
}
|
||||
|
||||
glyphProps[0] = GlyphProps(0, 0)
|
||||
glyphProps[0] = GlyphProps(0)
|
||||
|
||||
}
|
||||
|
||||
@@ -188,6 +189,8 @@ class TerrarumTypewriterBitmap(
|
||||
intArrayOf(coff % 16, coff / 16)
|
||||
}
|
||||
|
||||
private fun Boolean.toInt() = if (this) 1 else 0
|
||||
private fun Int.tagify() = if (this and 255 == 0) 0 else this
|
||||
|
||||
private fun buildWidthTable(pixmap: Pixmap, codeRange: Iterable<Int>, cols: Int = 16) {
|
||||
val binaryCodeOffset = TerrarumSansBitmap.W_VAR_INIT
|
||||
@@ -202,53 +205,65 @@ class TerrarumTypewriterBitmap(
|
||||
|
||||
val codeStartX = cellX + binaryCodeOffset
|
||||
val codeStartY = cellY
|
||||
val tagStartY = codeStartY + 10
|
||||
|
||||
var width = 0
|
||||
var tags = 0
|
||||
val width = (0..4).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y).and(255) != 0).toInt() shl y) }
|
||||
val isLowHeight = (pixmap.getPixel(codeStartX, codeStartY + 5).and(255) != 0)
|
||||
|
||||
for (y in 0..3) {
|
||||
// if ALPHA is not zero, assume it's 1
|
||||
if (pixmap.getPixel(codeStartX, codeStartY + y).and(0xFF) != 0) {
|
||||
width = width or (1 shl y)
|
||||
}
|
||||
// Keming machine parameters
|
||||
val kerningBit1 = pixmap.getPixel(codeStartX, codeStartY + 6).tagify()
|
||||
val kerningBit2 = pixmap.getPixel(codeStartX, codeStartY + 7).tagify()
|
||||
val kerningBit3 = pixmap.getPixel(codeStartX, codeStartY + 8).tagify()
|
||||
val kerningBit4 = pixmap.getPixel(codeStartX, codeStartY + 9).tagify()
|
||||
var isKernYtype = ((kerningBit1 and 0x80000000.toInt()) != 0)
|
||||
var kerningMask = kerningBit1.ushr(8).and(0xFFFFFF)
|
||||
val hasKernData = kerningBit1 and 255 != 0//(kerningBit1 and 255 != 0 && kerningMask != 0xFFFF)
|
||||
if (!hasKernData) {
|
||||
isKernYtype = false
|
||||
kerningMask = 255
|
||||
}
|
||||
|
||||
for (y in 0..9) {
|
||||
// if ALPHA is not zero, assume it's 1
|
||||
if (pixmap.getPixel(codeStartX, tagStartY + y).and(0xFF) != 0) {
|
||||
tags = tags or (1 shl y)
|
||||
}
|
||||
}
|
||||
val nudgingBits = pixmap.getPixel(codeStartX, codeStartY + 10).tagify()
|
||||
val nudgeX = nudgingBits.ushr(16).toByte().toInt() // signed 8-bit int
|
||||
val nudgeY = nudgingBits.ushr(8).toByte().toInt() // signed 8-bit int
|
||||
|
||||
if (code and 127 == 67) width *= -1 // the backspace key
|
||||
if (debug) println("${code.charInfo()}: Width $width, tags $tags")
|
||||
val diacriticsAnchors = (0..5).map {
|
||||
val yPos = 11 + (it / 3)
|
||||
val shift = (2 - (it % 3)) * 8
|
||||
val yPixel = pixmap.getPixel(codeStartX, codeStartY + yPos).tagify()
|
||||
val xPixel = pixmap.getPixel(codeStartX, codeStartY + yPos + 1).tagify()
|
||||
val y = (yPixel ushr shift) and 127
|
||||
val x = (xPixel ushr shift) and 127
|
||||
val yUsed = (yPixel ushr shift) >= 128
|
||||
val xUsed = (yPixel ushr shift) >= 128
|
||||
|
||||
/*val isDiacritics = pixmap.getPixel(codeStartX, codeStartY + H - 1).and(0xFF) != 0
|
||||
if (isDiacritics)
|
||||
glyphWidth = -glyphWidth*/
|
||||
DiacriticsAnchor(it, x, y, xUsed, yUsed)
|
||||
}.toTypedArray()
|
||||
|
||||
val alignWhere = (11..12).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y).and(255) != 0).toInt() shl y) }
|
||||
|
||||
glyphProps[code] = GlyphProps(width, tags)
|
||||
val writeOnTop = pixmap.getPixel(codeStartX, codeStartY + 13).and(255) != 0
|
||||
|
||||
val stackWhere = (14..15).fold(0) { acc, y -> acc or ((pixmap.getPixel(codeStartX, codeStartY + y).and(255) != 0).toInt() shl y) }
|
||||
|
||||
glyphProps[code] = GlyphProps(width, isLowHeight, nudgeX, nudgeY, diacriticsAnchors, alignWhere, writeOnTop, stackWhere, GlyphProps.DEFAULT_EXTINFO, hasKernData, isKernYtype, kerningMask)
|
||||
// if (code < 256) dbgprn("${code.charInfo()} width: $width, tags: ${glyphProps[code]}")
|
||||
|
||||
// extra info
|
||||
val extCount = glyphProps[code]?.requiredExtInfoCount() ?: 0
|
||||
if (extCount > 0) {
|
||||
|
||||
glyphProps[code]?.extInfo = IntArray(extCount)
|
||||
|
||||
for (x in 0 until extCount) {
|
||||
var info = 0
|
||||
for (y in 0..18) {
|
||||
// if ALPHA is not zero, assume it's 1
|
||||
if (pixmap.getPixel(cellX + x, cellY + y).and(0xFF) != 0) {
|
||||
if (pixmap.getPixel(cellX + x, cellY + y).and(255) != 0) {
|
||||
info = info or (1 shl y)
|
||||
}
|
||||
}
|
||||
|
||||
glyphProps[code]!!.extInfo!![x] = info
|
||||
glyphProps[code]!!.extInfo[x] = info
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +272,7 @@ class TerrarumTypewriterBitmap(
|
||||
private var flagFirstRun = true
|
||||
private var textBuffer = CodepointSequence(256)
|
||||
private lateinit var tempLinotype: Texture
|
||||
private var nullProp = GlyphProps(15, 0)
|
||||
private var nullProp = GlyphProps(15)
|
||||
|
||||
|
||||
fun draw(batch: Batch, codepoints: CodepointSequence, x: Float, y: Float): GlyphLayout? {
|
||||
@@ -424,7 +439,7 @@ class TerrarumTypewriterBitmap(
|
||||
|
||||
|
||||
if (!thisProp.writeOnTop) {
|
||||
posXbuffer[charIndex] = ((if (thisProp.nudgeRight) 1 else -1) * thisProp.alignXPos) +
|
||||
posXbuffer[charIndex] = thisProp.nudgeX +
|
||||
when (itsProp.alignWhere) {
|
||||
GlyphProps.ALIGN_RIGHT ->
|
||||
posXbuffer[nonDiacriticCounter] + TerrarumSansBitmap.W_VAR_INIT + alignmentOffset + interchar + kerning + extraWidth
|
||||
@@ -438,9 +453,9 @@ class TerrarumTypewriterBitmap(
|
||||
|
||||
stackUpwardCounter = 0
|
||||
stackDownwardCounter = 0
|
||||
extraWidth = (if (thisProp.nudgeRight) -1 else 1) * thisProp.alignXPos // NOTE: sign is flipped!
|
||||
extraWidth = -thisProp.nudgeX // NOTE: sign is flipped!
|
||||
}
|
||||
else if (thisProp.writeOnTop && thisProp.alignXPos == GlyphProps.DIA_JOINER) {
|
||||
else if (thisProp.writeOnTop && thisProp.diacriticsAnchors[0].x == GlyphProps.DIA_JOINER) {
|
||||
posXbuffer[charIndex] = when (itsProp.alignWhere) {
|
||||
GlyphProps.ALIGN_RIGHT ->
|
||||
posXbuffer[nonDiacriticCounter] + TerrarumSansBitmap.W_VAR_INIT + alignmentOffset
|
||||
@@ -459,7 +474,7 @@ class TerrarumTypewriterBitmap(
|
||||
posXbuffer[nonDiacriticCounter] - (TerrarumSansBitmap.W_VAR_INIT - itsProp.width)
|
||||
}
|
||||
GlyphProps.ALIGN_CENTRE -> {
|
||||
val alignXPos = if (itsProp.alignXPos == 0) itsProp.width.div(2) else itsProp.alignXPos
|
||||
val alignXPos = if (itsProp.diacriticsAnchors[0].x == 0) itsProp.width.div(2) else itsProp.diacriticsAnchors[0].x
|
||||
|
||||
if (itsProp.alignWhere == GlyphProps.ALIGN_RIGHT) {
|
||||
posXbuffer[nonDiacriticCounter] + alignXPos + (itsProp.width + 1).div(2)
|
||||
@@ -516,10 +531,10 @@ class TerrarumTypewriterBitmap(
|
||||
posXbuffer[posXbuffer.lastIndex] = 1 + posXbuffer[posXbuffer.lastIndex - 1] + // adding 1 to house the shadow
|
||||
if (lastCharProp?.writeOnTop == true) {
|
||||
val realDiacriticWidth = if (lastCharProp.alignWhere == GlyphProps.ALIGN_CENTRE) {
|
||||
(lastCharProp.width).div(2) + penultCharProp.alignXPos
|
||||
(lastCharProp.width).div(2) + penultCharProp.diacriticsAnchors[0].x
|
||||
}
|
||||
else if (lastCharProp.alignWhere == GlyphProps.ALIGN_RIGHT) {
|
||||
(lastCharProp.width) + penultCharProp.alignXPos
|
||||
(lastCharProp.width) + penultCharProp.diacriticsAnchors[0].x
|
||||
}
|
||||
else 0
|
||||
|
||||
|
||||
BIN
testing.PNG
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |