mirror of
https://github.com/curioustorvald/Terrarum-sans-bitmap.git
synced 2026-06-06 05:58:30 +09:00
glyph texture atlas
This commit is contained in:
BIN
src/assets/unipunct_variable.tga
LFS
BIN
src/assets/unipunct_variable.tga
LFS
Binary file not shown.
81
src/net/torvald/terrarumsansbitmap/gdx/GlyphAtlas.kt
Normal file
81
src/net/torvald/terrarumsansbitmap/gdx/GlyphAtlas.kt
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package net.torvald.terrarumsansbitmap.gdx
|
||||||
|
|
||||||
|
import com.badlogic.gdx.graphics.Pixmap
|
||||||
|
|
||||||
|
data class AtlasRegion(
|
||||||
|
val atlasX: Int,
|
||||||
|
val atlasY: Int,
|
||||||
|
val width: Int,
|
||||||
|
val height: Int,
|
||||||
|
val offsetX: Int = 0,
|
||||||
|
val offsetY: Int = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
class GlyphAtlas(val atlasWidth: Int, val atlasHeight: Int) {
|
||||||
|
|
||||||
|
val pixmap = Pixmap(atlasWidth, atlasHeight, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None }
|
||||||
|
|
||||||
|
private val regions = HashMap<Long, AtlasRegion>()
|
||||||
|
|
||||||
|
private var cursorX = 0
|
||||||
|
private var cursorY = 0
|
||||||
|
private var shelfHeight = 0
|
||||||
|
|
||||||
|
private fun atlasKey(sheetID: Int, cellX: Int, cellY: Int): Long =
|
||||||
|
sheetID.toLong().shl(32) or cellX.toLong().shl(16) or cellY.toLong()
|
||||||
|
|
||||||
|
fun packCell(sheetID: Int, cellX: Int, cellY: Int, cellPixmap: Pixmap) {
|
||||||
|
val w = cellPixmap.width
|
||||||
|
val h = cellPixmap.height
|
||||||
|
|
||||||
|
if (cursorX + w > atlasWidth) {
|
||||||
|
cursorX = 0
|
||||||
|
cursorY += shelfHeight
|
||||||
|
shelfHeight = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pixmap.drawPixmap(cellPixmap, cursorX, cursorY)
|
||||||
|
|
||||||
|
regions[atlasKey(sheetID, cellX, cellY)] = AtlasRegion(cursorX, cursorY, w, h)
|
||||||
|
|
||||||
|
cursorX += w
|
||||||
|
if (h > shelfHeight) shelfHeight = h
|
||||||
|
}
|
||||||
|
|
||||||
|
fun blitSheet(sheetID: Int, sheetPixmap: Pixmap, cellW: Int, cellH: Int, cols: Int, rows: Int) {
|
||||||
|
if (cursorX > 0) {
|
||||||
|
cursorX = 0
|
||||||
|
cursorY += shelfHeight
|
||||||
|
shelfHeight = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
val baseY = cursorY
|
||||||
|
|
||||||
|
pixmap.drawPixmap(sheetPixmap, 0, baseY)
|
||||||
|
|
||||||
|
for (cy in 0 until rows) {
|
||||||
|
for (cx in 0 until cols) {
|
||||||
|
regions[atlasKey(sheetID, cx, cy)] = AtlasRegion(cx * cellW, baseY + cy * cellH, cellW, cellH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorY = baseY + sheetPixmap.height
|
||||||
|
cursorX = 0
|
||||||
|
shelfHeight = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRegion(sheetID: Int, cellX: Int, cellY: Int): AtlasRegion? =
|
||||||
|
regions[atlasKey(sheetID, cellX, cellY)]
|
||||||
|
|
||||||
|
fun clearRegion(region: AtlasRegion) {
|
||||||
|
for (y in 0 until region.height) {
|
||||||
|
for (x in 0 until region.width) {
|
||||||
|
pixmap.drawPixel(region.atlasX + x, region.atlasY + y, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dispose() {
|
||||||
|
pixmap.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -318,7 +318,7 @@ class TerrarumSansBitmap(
|
|||||||
/** Props of all printable Unicode points. */
|
/** Props of all printable Unicode points. */
|
||||||
private val glyphProps = HashMap<CodePoint, GlyphProps>()
|
private val glyphProps = HashMap<CodePoint, GlyphProps>()
|
||||||
private val textReplaces = HashMap<CodePoint, CodePoint>()
|
private val textReplaces = HashMap<CodePoint, CodePoint>()
|
||||||
private val sheets: Array<PixmapRegionPack>
|
private lateinit var atlas: GlyphAtlas
|
||||||
|
|
||||||
// private var charsetOverride = 0
|
// private var charsetOverride = 0
|
||||||
|
|
||||||
@@ -326,9 +326,10 @@ class TerrarumSansBitmap(
|
|||||||
// private val tempFiles = ArrayList<String>()
|
// private val tempFiles = ArrayList<String>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val sheetsPack = ArrayList<PixmapRegionPack>()
|
atlas = GlyphAtlas(4096, 4096)
|
||||||
|
var unihanPixmap: Pixmap? = null
|
||||||
|
|
||||||
// first we create pixmap to read pixels, then make texture using pixmap
|
// first we create pixmap to read pixels, then pack into atlas
|
||||||
fileList.forEachIndexed { index, it ->
|
fileList.forEachIndexed { index, it ->
|
||||||
val isVariable = it.endsWith("_variable.tga")
|
val isVariable = it.endsWith("_variable.tga")
|
||||||
val isXYSwapped = it.contains("xyswap", true)
|
val isXYSwapped = it.contains("xyswap", true)
|
||||||
@@ -346,32 +347,6 @@ class TerrarumSansBitmap(
|
|||||||
else
|
else
|
||||||
dbgprn("loading texture [STATIC] $it")
|
dbgprn("loading texture [STATIC] $it")
|
||||||
|
|
||||||
|
|
||||||
// unpack gz if applicable
|
|
||||||
/*if (it.endsWith(".gz")) {
|
|
||||||
val tmpFilePath = tempDir + "/tmp_${it.dropLast(7)}.tga"
|
|
||||||
|
|
||||||
try {
|
|
||||||
val gzi = GZIPInputStream(Gdx.files.classpath(fontParentDir + it).read(8192))
|
|
||||||
val wholeFile = gzi.readBytes()
|
|
||||||
gzi.close()
|
|
||||||
val fos = BufferedOutputStream(FileOutputStream(tmpFilePath))
|
|
||||||
fos.write(wholeFile)
|
|
||||||
fos.flush()
|
|
||||||
fos.close()
|
|
||||||
|
|
||||||
pixmap = Pixmap(Gdx.files.absolute(tmpFilePath))
|
|
||||||
// tempFiles.add(tmpFilePath)
|
|
||||||
}
|
|
||||||
catch (e: GdxRuntimeException) {
|
|
||||||
//e.printStackTrace()
|
|
||||||
dbgprn("said texture not found, skipping...")
|
|
||||||
|
|
||||||
pixmap = Pixmap(1, 1, Pixmap.Format.RGBA8888)
|
|
||||||
}
|
|
||||||
//File(tmpFileName).delete()
|
|
||||||
}
|
|
||||||
else {*/
|
|
||||||
try {
|
try {
|
||||||
pixmap = Pixmap(Gdx.files.classpath("assets/$it"))
|
pixmap = Pixmap(Gdx.files.classpath("assets/$it"))
|
||||||
}
|
}
|
||||||
@@ -387,7 +362,6 @@ class TerrarumSansBitmap(
|
|||||||
System.exit(1)
|
System.exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
if (isVariable) buildWidthTable(pixmap, codeRange[index], if (isExtraWide) 32 else 16)
|
if (isVariable) buildWidthTable(pixmap, codeRange[index], if (isExtraWide) 32 else 16)
|
||||||
buildWidthTableFixed()
|
buildWidthTableFixed()
|
||||||
@@ -395,40 +369,50 @@ class TerrarumSansBitmap(
|
|||||||
|
|
||||||
setupDynamicTextReplacer()
|
setupDynamicTextReplacer()
|
||||||
|
|
||||||
/*if (!noShadow) {
|
if (index == SHEET_UNIHAN) {
|
||||||
makeShadowForSheet(pixmap)
|
// defer wenquanyi packing to after all other sheets
|
||||||
}*/
|
unihanPixmap = pixmap
|
||||||
|
}
|
||||||
|
else {
|
||||||
//val texture = Texture(pixmap)
|
|
||||||
val texRegPack = if (isExtraWide)
|
val texRegPack = if (isExtraWide)
|
||||||
PixmapRegionPack(pixmap, W_WIDEVAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped)
|
PixmapRegionPack(pixmap, W_WIDEVAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped)
|
||||||
else if (isVariable)
|
else if (isVariable)
|
||||||
PixmapRegionPack(pixmap, W_VAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped)
|
PixmapRegionPack(pixmap, W_VAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped)
|
||||||
else if (index == SHEET_UNIHAN)
|
|
||||||
PixmapRegionPack(pixmap, W_UNIHAN, H_UNIHAN) // the only exception that is height is 16
|
|
||||||
// below they all have height of 20 'H'
|
|
||||||
else if (index == SHEET_HANGUL)
|
else if (index == SHEET_HANGUL)
|
||||||
PixmapRegionPack(pixmap, W_HANGUL_BASE, H)
|
PixmapRegionPack(pixmap, W_HANGUL_BASE, H)
|
||||||
else if (index == SHEET_CUSTOM_SYM)
|
else if (index == SHEET_CUSTOM_SYM)
|
||||||
PixmapRegionPack(pixmap, SIZE_CUSTOM_SYM, SIZE_CUSTOM_SYM) // TODO variable
|
PixmapRegionPack(pixmap, SIZE_CUSTOM_SYM, SIZE_CUSTOM_SYM)
|
||||||
else if (index == SHEET_RUNIC)
|
else if (index == SHEET_RUNIC)
|
||||||
PixmapRegionPack(pixmap, W_LATIN_WIDE, H)
|
PixmapRegionPack(pixmap, W_LATIN_WIDE, H)
|
||||||
else throw IllegalArgumentException("Unknown sheet index: $index")
|
else throw IllegalArgumentException("Unknown sheet index: $index")
|
||||||
|
|
||||||
//texRegPack.texture.setFilter(minFilter, magFilter)
|
for (cy in 0 until texRegPack.verticalCount) {
|
||||||
|
for (cx in 0 until texRegPack.horizontalCount) {
|
||||||
sheetsPack.add(texRegPack)
|
atlas.packCell(index, cx, cy, texRegPack.get(cx, cy))
|
||||||
|
}
|
||||||
pixmap.dispose() // you are terminated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sheets = sheetsPack.toTypedArray()
|
texRegPack.dispose()
|
||||||
|
pixmap.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack wenquanyi (SHEET_UNIHAN) last as a contiguous blit
|
||||||
|
unihanPixmap?.let {
|
||||||
|
val cols = it.width / W_UNIHAN
|
||||||
|
val rows = it.height / H_UNIHAN
|
||||||
|
atlas.blitSheet(SHEET_UNIHAN, it, W_UNIHAN, H_UNIHAN, cols, rows)
|
||||||
|
it.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
// make sure null char is actually null (draws nothing and has zero width)
|
// make sure null char is actually null (draws nothing and has zero width)
|
||||||
sheets[SHEET_ASCII_VARW].regions[0].setColor(0)
|
atlas.getRegion(SHEET_ASCII_VARW, 0, 0)?.let { atlas.clearRegion(it) }
|
||||||
sheets[SHEET_ASCII_VARW].regions[0].fill()
|
|
||||||
glyphProps[0] = GlyphProps(0)
|
glyphProps[0] = GlyphProps(0)
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
com.badlogic.gdx.graphics.PixmapIO.writePNG(Gdx.files.absolute("$tempDir/glyph_atlas_dump.png"), atlas.pixmap)
|
||||||
|
dbgprn("atlas dumped to $tempDir/glyph_atlas_dump.png")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLineHeight(): Float = LINE_HEIGHT.toFloat() * scale
|
override fun getLineHeight(): Float = LINE_HEIGHT.toFloat() * scale
|
||||||
@@ -599,24 +583,21 @@ class TerrarumSansBitmap(
|
|||||||
|
|
||||||
val (indexCho, indexJung, indexJong) = indices
|
val (indexCho, indexJung, indexJong) = indices
|
||||||
val (choRow, jungRow, jongRow) = rows
|
val (choRow, jungRow, jongRow) = rows
|
||||||
val hangulSheet = sheets[SHEET_HANGUL]
|
atlas.getRegion(SHEET_HANGUL, indexCho, choRow)?.let {
|
||||||
|
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
||||||
|
}
|
||||||
|
atlas.getRegion(SHEET_HANGUL, indexJung, jungRow)?.let {
|
||||||
val choTex = hangulSheet.get(indexCho, choRow)
|
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
||||||
val jungTex = hangulSheet.get(indexJung, jungRow)
|
}
|
||||||
val jongTex = hangulSheet.get(indexJong, jongRow)
|
atlas.getRegion(SHEET_HANGUL, indexJong, jongRow)?.let {
|
||||||
|
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
||||||
linotypePixmap.drawPixmap(choTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
}
|
||||||
linotypePixmap.drawPixmap(jungTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
|
||||||
linotypePixmap.drawPixmap(jongTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
|
||||||
|
|
||||||
|
|
||||||
index += hangulLength - 1
|
index += hangulLength - 1
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
|
||||||
val posY = posmap.y[index].flipY() +
|
val posY = posmap.y[index].flipY() +
|
||||||
if (sheetID == SHEET_UNIHAN) // evil exceptions
|
if (sheetID == SHEET_UNIHAN) // evil exceptions
|
||||||
offsetUnihan
|
offsetUnihan
|
||||||
@@ -625,13 +606,8 @@ class TerrarumSansBitmap(
|
|||||||
else 0
|
else 0
|
||||||
|
|
||||||
val posX = posmap.x[index]
|
val posX = posmap.x[index]
|
||||||
val texture = sheets[sheetID].get(sheetX, sheetY)
|
atlas.getRegion(sheetID, sheetX, sheetY)?.let {
|
||||||
|
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
|
||||||
linotypePixmap.drawPixmap(texture, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (noSuchGlyph: ArrayIndexOutOfBoundsException) {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,24 +691,21 @@ class TerrarumSansBitmap(
|
|||||||
|
|
||||||
val (indexCho, indexJung, indexJong) = indices
|
val (indexCho, indexJung, indexJong) = indices
|
||||||
val (choRow, jungRow, jongRow) = rows
|
val (choRow, jungRow, jongRow) = rows
|
||||||
val hangulSheet = sheets[SHEET_HANGUL]
|
atlas.getRegion(SHEET_HANGUL, indexCho, choRow)?.let {
|
||||||
|
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
||||||
|
}
|
||||||
|
atlas.getRegion(SHEET_HANGUL, indexJung, jungRow)?.let {
|
||||||
val choTex = hangulSheet.get(indexCho, choRow)
|
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
||||||
val jungTex = hangulSheet.get(indexJung, jungRow)
|
}
|
||||||
val jongTex = hangulSheet.get(indexJong, jongRow)
|
atlas.getRegion(SHEET_HANGUL, indexJong, jongRow)?.let {
|
||||||
|
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
||||||
linotypePixmap.drawPixmap(choTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
}
|
||||||
linotypePixmap.drawPixmap(jungTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
|
||||||
linotypePixmap.drawPixmap(jongTex, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
|
||||||
|
|
||||||
|
|
||||||
index += hangulLength - 1
|
index += hangulLength - 1
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
|
||||||
val posY = posmap.y[index].flipY() +
|
val posY = posmap.y[index].flipY() +
|
||||||
if (sheetID == SHEET_UNIHAN) // evil exceptions
|
if (sheetID == SHEET_UNIHAN) // evil exceptions
|
||||||
offsetUnihan
|
offsetUnihan
|
||||||
@@ -741,13 +714,8 @@ class TerrarumSansBitmap(
|
|||||||
else 0
|
else 0
|
||||||
|
|
||||||
val posX = posmap.x[index]
|
val posX = posmap.x[index]
|
||||||
val texture = sheets[sheetID].get(sheetX, sheetY)
|
atlas.getRegion(sheetID, sheetX, sheetY)?.let {
|
||||||
|
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
|
||||||
linotypePixmap.drawPixmap(texture, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (noSuchGlyph: ArrayIndexOutOfBoundsException) {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -826,7 +794,7 @@ class TerrarumSansBitmap(
|
|||||||
override fun dispose() {
|
override fun dispose() {
|
||||||
super.dispose()
|
super.dispose()
|
||||||
textCache.values.forEach { it.dispose() }
|
textCache.values.forEach { it.dispose() }
|
||||||
sheets.forEach { it.dispose() }
|
atlas.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSheetType(c: CodePoint): Int {
|
fun getSheetType(c: CodePoint): Int {
|
||||||
@@ -2163,6 +2131,16 @@ class TerrarumSansBitmap(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Pixmap.drawFromAtlas(atlas: Pixmap, region: AtlasRegion, xPos: Int, yPos: Int, col: Int) {
|
||||||
|
for (y in 0 until region.height) {
|
||||||
|
for (x in 0 until region.width) {
|
||||||
|
val pixel = atlas.getPixel(region.atlasX + x, region.atlasY + y)
|
||||||
|
val newPixel = pixel colorTimes col
|
||||||
|
this.drawPixel(xPos + x + region.offsetX, yPos + y + region.offsetY, newPixel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun Color.toRGBA8888() =
|
private fun Color.toRGBA8888() =
|
||||||
(this.r * 255f).toInt().shl(24) or
|
(this.r * 255f).toInt().shl(24) or
|
||||||
(this.g * 255f).toInt().shl(16) or
|
(this.g * 255f).toInt().shl(16) or
|
||||||
|
|||||||
BIN
work_files/cyrilic_variable.psd
LFS
BIN
work_files/cyrilic_variable.psd
LFS
Binary file not shown.
Binary file not shown.
BIN
work_files/unipunct_variable.psd
LFS
BIN
work_files/unipunct_variable.psd
LFS
Binary file not shown.
Reference in New Issue
Block a user