glyph texture atlas (2)

This commit is contained in:
minjaesong
2026-03-14 16:06:31 +09:00
parent f4e1db5846
commit 662dc5b093
2 changed files with 92 additions and 13 deletions

View File

@@ -13,7 +13,7 @@ data class AtlasRegion(
class GlyphAtlas(val atlasWidth: Int, val atlasHeight: Int) {
val pixmap = Pixmap(atlasWidth, atlasHeight, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None }
val pixmap = Pixmap(atlasWidth, atlasHeight, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.SourceOver }
private val regions = HashMap<Long, AtlasRegion>()
@@ -21,25 +21,78 @@ class GlyphAtlas(val atlasWidth: Int, val atlasHeight: Int) {
private var cursorY = 0
private var shelfHeight = 0
private val pendingCells = ArrayList<PendingCell>()
private class PendingCell(
val sheetID: Int,
val cellX: Int,
val cellY: Int,
val cropped: Pixmap,
val offsetX: Int,
val offsetY: Int
)
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
/** Scans the cell for its non-transparent bounding box, crops, and queues for deferred packing. */
fun queueCell(sheetID: Int, cellX: Int, cellY: Int, cellPixmap: Pixmap) {
var minX = cellPixmap.width
var minY = cellPixmap.height
var maxX = -1
var maxY = -1
if (cursorX + w > atlasWidth) {
cursorX = 0
cursorY += shelfHeight
shelfHeight = 0
for (y in 0 until cellPixmap.height) {
for (x in 0 until cellPixmap.width) {
if (cellPixmap.getPixel(x, y) and 0xFF != 0) {
if (x < minX) minX = x
if (y < minY) minY = y
if (x > maxX) maxX = x
if (y > maxY) maxY = y
}
}
}
pixmap.drawPixmap(cellPixmap, cursorX, cursorY)
if (maxX < 0) return // entirely transparent, skip
regions[atlasKey(sheetID, cellX, cellY)] = AtlasRegion(cursorX, cursorY, w, h)
val cropW = maxX - minX + 1
val cropH = maxY - minY + 1
val cropped = Pixmap(cropW, cropH, Pixmap.Format.RGBA8888)
cropped.drawPixmap(cellPixmap, 0, 0, minX, minY, cropW, cropH)
cursorX += w
if (h > shelfHeight) shelfHeight = h
pendingCells.add(PendingCell(sheetID, cellX, cellY, cropped, minX, minY))
}
/** Sorts queued cells by height desc then width desc, and packs into shelves. */
fun packAllQueued() {
pendingCells.sortWith(
compareByDescending<PendingCell> { it.cropped.height }
.thenByDescending { it.cropped.width }
)
for (cell in pendingCells) {
val w = cell.cropped.width
val h = cell.cropped.height
// start new shelf if cell doesn't fit horizontally
if (cursorX + w > atlasWidth) {
cursorX = 0
cursorY += shelfHeight
shelfHeight = 0
}
pixmap.drawPixmap(cell.cropped, cursorX, cursorY)
regions[atlasKey(cell.sheetID, cell.cellX, cell.cellY)] =
AtlasRegion(cursorX, cursorY, w, h, cell.offsetX, cell.offsetY)
cursorX += w
if (h > shelfHeight) shelfHeight = h
cell.cropped.dispose()
}
pendingCells.clear()
}
fun blitSheet(sheetID: Int, sheetPixmap: Pixmap, cellW: Int, cellH: Int, cols: Int, rows: Int) {