mirror of
https://github.com/curioustorvald/Terrarum-sans-bitmap.git
synced 2026-03-14 23:16:08 +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. */
|
||||
private val glyphProps = HashMap<CodePoint, GlyphProps>()
|
||||
private val textReplaces = HashMap<CodePoint, CodePoint>()
|
||||
private val sheets: Array<PixmapRegionPack>
|
||||
private lateinit var atlas: GlyphAtlas
|
||||
|
||||
// private var charsetOverride = 0
|
||||
|
||||
@@ -326,9 +326,10 @@ class TerrarumSansBitmap(
|
||||
// private val tempFiles = ArrayList<String>()
|
||||
|
||||
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 ->
|
||||
val isVariable = it.endsWith("_variable.tga")
|
||||
val isXYSwapped = it.contains("xyswap", true)
|
||||
@@ -346,32 +347,6 @@ class TerrarumSansBitmap(
|
||||
else
|
||||
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 {
|
||||
pixmap = Pixmap(Gdx.files.classpath("assets/$it"))
|
||||
}
|
||||
@@ -387,7 +362,6 @@ class TerrarumSansBitmap(
|
||||
System.exit(1)
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
if (isVariable) buildWidthTable(pixmap, codeRange[index], if (isExtraWide) 32 else 16)
|
||||
buildWidthTableFixed()
|
||||
@@ -395,40 +369,50 @@ class TerrarumSansBitmap(
|
||||
|
||||
setupDynamicTextReplacer()
|
||||
|
||||
/*if (!noShadow) {
|
||||
makeShadowForSheet(pixmap)
|
||||
}*/
|
||||
if (index == SHEET_UNIHAN) {
|
||||
// defer wenquanyi packing to after all other sheets
|
||||
unihanPixmap = pixmap
|
||||
}
|
||||
else {
|
||||
val texRegPack = if (isExtraWide)
|
||||
PixmapRegionPack(pixmap, W_WIDEVAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped)
|
||||
else if (isVariable)
|
||||
PixmapRegionPack(pixmap, W_VAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped)
|
||||
else if (index == SHEET_HANGUL)
|
||||
PixmapRegionPack(pixmap, W_HANGUL_BASE, H)
|
||||
else if (index == SHEET_CUSTOM_SYM)
|
||||
PixmapRegionPack(pixmap, SIZE_CUSTOM_SYM, SIZE_CUSTOM_SYM)
|
||||
else if (index == SHEET_RUNIC)
|
||||
PixmapRegionPack(pixmap, W_LATIN_WIDE, H)
|
||||
else throw IllegalArgumentException("Unknown sheet index: $index")
|
||||
|
||||
for (cy in 0 until texRegPack.verticalCount) {
|
||||
for (cx in 0 until texRegPack.horizontalCount) {
|
||||
atlas.packCell(index, cx, cy, texRegPack.get(cx, cy))
|
||||
}
|
||||
}
|
||||
|
||||
//val texture = Texture(pixmap)
|
||||
val texRegPack = if (isExtraWide)
|
||||
PixmapRegionPack(pixmap, W_WIDEVAR_INIT, H, HGAP_VAR, 0, xySwapped = isXYSwapped)
|
||||
else if (isVariable)
|
||||
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)
|
||||
PixmapRegionPack(pixmap, W_HANGUL_BASE, H)
|
||||
else if (index == SHEET_CUSTOM_SYM)
|
||||
PixmapRegionPack(pixmap, SIZE_CUSTOM_SYM, SIZE_CUSTOM_SYM) // TODO variable
|
||||
else if (index == SHEET_RUNIC)
|
||||
PixmapRegionPack(pixmap, W_LATIN_WIDE, H)
|
||||
else throw IllegalArgumentException("Unknown sheet index: $index")
|
||||
|
||||
//texRegPack.texture.setFilter(minFilter, magFilter)
|
||||
|
||||
sheetsPack.add(texRegPack)
|
||||
|
||||
pixmap.dispose() // you are terminated
|
||||
texRegPack.dispose()
|
||||
pixmap.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
sheets = sheetsPack.toTypedArray()
|
||||
// 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)
|
||||
sheets[SHEET_ASCII_VARW].regions[0].setColor(0)
|
||||
sheets[SHEET_ASCII_VARW].regions[0].fill()
|
||||
atlas.getRegion(SHEET_ASCII_VARW, 0, 0)?.let { atlas.clearRegion(it) }
|
||||
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
|
||||
@@ -599,39 +583,31 @@ class TerrarumSansBitmap(
|
||||
|
||||
val (indexCho, indexJung, indexJong) = indices
|
||||
val (choRow, jungRow, jongRow) = rows
|
||||
val hangulSheet = sheets[SHEET_HANGUL]
|
||||
|
||||
|
||||
|
||||
val choTex = hangulSheet.get(indexCho, choRow)
|
||||
val jungTex = hangulSheet.get(indexJung, jungRow)
|
||||
val jongTex = hangulSheet.get(indexJong, jongRow)
|
||||
|
||||
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)
|
||||
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 {
|
||||
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
||||
}
|
||||
atlas.getRegion(SHEET_HANGUL, indexJong, jongRow)?.let {
|
||||
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
||||
}
|
||||
|
||||
|
||||
index += hangulLength - 1
|
||||
|
||||
}
|
||||
else {
|
||||
try {
|
||||
val posY = posmap.y[index].flipY() +
|
||||
if (sheetID == SHEET_UNIHAN) // evil exceptions
|
||||
offsetUnihan
|
||||
else if (sheetID == SHEET_CUSTOM_SYM)
|
||||
offsetCustomSym
|
||||
else 0
|
||||
val posY = posmap.y[index].flipY() +
|
||||
if (sheetID == SHEET_UNIHAN) // evil exceptions
|
||||
offsetUnihan
|
||||
else if (sheetID == SHEET_CUSTOM_SYM)
|
||||
offsetCustomSym
|
||||
else 0
|
||||
|
||||
val posX = posmap.x[index]
|
||||
val texture = sheets[sheetID].get(sheetX, sheetY)
|
||||
|
||||
linotypePixmap.drawPixmap(texture, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
|
||||
|
||||
|
||||
}
|
||||
catch (noSuchGlyph: ArrayIndexOutOfBoundsException) {
|
||||
val posX = posmap.x[index]
|
||||
atlas.getRegion(sheetID, sheetX, sheetY)?.let {
|
||||
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,39 +691,31 @@ class TerrarumSansBitmap(
|
||||
|
||||
val (indexCho, indexJung, indexJong) = indices
|
||||
val (choRow, jungRow, jongRow) = rows
|
||||
val hangulSheet = sheets[SHEET_HANGUL]
|
||||
|
||||
|
||||
|
||||
val choTex = hangulSheet.get(indexCho, choRow)
|
||||
val jungTex = hangulSheet.get(indexJung, jungRow)
|
||||
val jongTex = hangulSheet.get(indexJong, jongRow)
|
||||
|
||||
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)
|
||||
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 {
|
||||
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
||||
}
|
||||
atlas.getRegion(SHEET_HANGUL, indexJong, jongRow)?.let {
|
||||
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posmap.x[index] + linotypePaddingX, linotypePaddingY, renderCol)
|
||||
}
|
||||
|
||||
|
||||
index += hangulLength - 1
|
||||
|
||||
}
|
||||
else {
|
||||
try {
|
||||
val posY = posmap.y[index].flipY() +
|
||||
if (sheetID == SHEET_UNIHAN) // evil exceptions
|
||||
offsetUnihan
|
||||
else if (sheetID == SHEET_CUSTOM_SYM)
|
||||
offsetCustomSym
|
||||
else 0
|
||||
val posY = posmap.y[index].flipY() +
|
||||
if (sheetID == SHEET_UNIHAN) // evil exceptions
|
||||
offsetUnihan
|
||||
else if (sheetID == SHEET_CUSTOM_SYM)
|
||||
offsetCustomSym
|
||||
else 0
|
||||
|
||||
val posX = posmap.x[index]
|
||||
val texture = sheets[sheetID].get(sheetX, sheetY)
|
||||
|
||||
linotypePixmap.drawPixmap(texture, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
|
||||
|
||||
|
||||
}
|
||||
catch (noSuchGlyph: ArrayIndexOutOfBoundsException) {
|
||||
val posX = posmap.x[index]
|
||||
atlas.getRegion(sheetID, sheetX, sheetY)?.let {
|
||||
linotypePixmap.drawFromAtlas(atlas.pixmap, it, posX + linotypePaddingX, posY + linotypePaddingY, renderCol)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -826,7 +794,7 @@ class TerrarumSansBitmap(
|
||||
override fun dispose() {
|
||||
super.dispose()
|
||||
textCache.values.forEach { it.dispose() }
|
||||
sheets.forEach { it.dispose() }
|
||||
atlas.dispose()
|
||||
}
|
||||
|
||||
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() =
|
||||
(this.r * 255f).toInt().shl(24) 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