mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-15 10:25:06 -04:00
wip bitmap font loading + tests
This commit is contained in:
parent
ce3a678d39
commit
1a6b8d04a7
@ -0,0 +1,75 @@
|
|||||||
|
package de.bixilon.minosoft.gui.rendering.font.types.bitmap
|
||||||
|
|
||||||
|
import de.bixilon.kotlinglm.vec2.Vec2
|
||||||
|
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
|
||||||
|
import de.bixilon.minosoft.gui.rendering.font.types.empty.EmptyCodeRenderer
|
||||||
|
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
|
||||||
|
import de.bixilon.minosoft.gui.rendering.system.dummy.texture.DummyTexture
|
||||||
|
import org.testng.Assert.*
|
||||||
|
import org.testng.annotations.Test
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.util.stream.IntStream
|
||||||
|
import kotlin.reflect.full.companionObject
|
||||||
|
|
||||||
|
|
||||||
|
@Test(groups = ["font"])
|
||||||
|
class BitmapFontTypeTest {
|
||||||
|
private val LOAD = BitmapFontType::class.companionObject!!.java.getDeclaredMethod("load", AbstractTexture::class.java, Int::class.java, Int::class.java, Array<IntStream>::class.java).apply { isAccessible = true }
|
||||||
|
|
||||||
|
private fun createTexture(start: IntArray, end: IntArray, width: Int, height: Int): AbstractTexture {
|
||||||
|
check(start.size == end.size)
|
||||||
|
|
||||||
|
val buffer = ByteBuffer.allocate((start.size / 16 + 1) * 16 * width * height * 4)
|
||||||
|
|
||||||
|
for (row in 0..start.size / 16) {
|
||||||
|
for (char in 0 until 16) {
|
||||||
|
val start = start.getOrNull((row * 16) + char) ?: 0
|
||||||
|
val end = end.getOrNull((row * 16) + char) ?: width
|
||||||
|
|
||||||
|
for (pixel in 0 until width) {
|
||||||
|
buffer.put(0xFF.toByte())
|
||||||
|
buffer.put(0xFF.toByte())
|
||||||
|
buffer.put(0xFF.toByte())
|
||||||
|
|
||||||
|
if (pixel in start..end) {
|
||||||
|
buffer.put(0xFF.toByte())
|
||||||
|
} else {
|
||||||
|
buffer.put(0x00.toByte())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val texture = DummyTexture(minosoft("test"))
|
||||||
|
texture.data = buffer
|
||||||
|
|
||||||
|
return texture
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun load(start: IntArray, end: IntArray, width: Int = 8, height: Int = 8, ascent: Int = 8, chars: Array<IntArray>): BitmapFontType {
|
||||||
|
val texture = createTexture(start, end, width, height)
|
||||||
|
|
||||||
|
val fontType = LOAD(BitmapFontType, texture, height, ascent, chars.map { IntStream.of(*it) }.toTypedArray()) as BitmapFontType
|
||||||
|
|
||||||
|
|
||||||
|
return fontType
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `space size`() {
|
||||||
|
val font = load(intArrayOf(), intArrayOf(), chars = arrayOf(intArrayOf('a'.code)))
|
||||||
|
|
||||||
|
val a = font['a'.code]!! as EmptyCodeRenderer
|
||||||
|
assertEquals(a.width, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `load basic with default options`() {
|
||||||
|
val font = load(intArrayOf(1, 2, 3), intArrayOf(7, 4, 6), chars = arrayOf(intArrayOf('a'.code, 'b'.code, 'c'.code)))
|
||||||
|
|
||||||
|
val a = font['a'.code]!! as BitmapCodeRenderer
|
||||||
|
assertEquals(a.width, 7)
|
||||||
|
assertEquals(a.uvStart, Vec2(0.0078125, 0))
|
||||||
|
assertEquals(a.uvEnd, Vec2(0.0625, 0))
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
@ -35,9 +35,7 @@ class DummyTexture(
|
|||||||
override val transparency: TextureTransparencies get() = TextureTransparencies.OPAQUE
|
override val transparency: TextureTransparencies get() = TextureTransparencies.OPAQUE
|
||||||
override var properties: ImageProperties = ImageProperties()
|
override var properties: ImageProperties = ImageProperties()
|
||||||
override var renderData: TextureRenderData = DummyTextureRenderData
|
override var renderData: TextureRenderData = DummyTextureRenderData
|
||||||
override var data: ByteBuffer?
|
override var data: ByteBuffer? = null
|
||||||
get() = TODO("Not yet implemented")
|
|
||||||
set(value) {}
|
|
||||||
override var mipmapData: Array<ByteBuffer>?
|
override var mipmapData: Array<ByteBuffer>?
|
||||||
get() = TODO("Not yet implemented")
|
get() = TODO("Not yet implemented")
|
||||||
set(value) {}
|
set(value) {}
|
||||||
|
@ -17,5 +17,5 @@ import de.bixilon.kutil.latch.AbstractLatch
|
|||||||
|
|
||||||
interface PostInitFontType : FontType {
|
interface PostInitFontType : FontType {
|
||||||
|
|
||||||
fun postInit(latch: AbstractLatch) = Unit
|
fun postInit(latch: AbstractLatch)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ class BitmapCodeRenderer(
|
|||||||
override var uvStart: Vec2,
|
override var uvStart: Vec2,
|
||||||
override var uvEnd: Vec2,
|
override var uvEnd: Vec2,
|
||||||
override val width: Float,
|
override val width: Float,
|
||||||
|
val ascent1: Int,
|
||||||
) : RasterizedCodePointRenderer, AscentedCodePointRenderer {
|
) : RasterizedCodePointRenderer, AscentedCodePointRenderer {
|
||||||
override val ascent: Float
|
override val ascent: Float
|
||||||
get() = 1.0f
|
get() = 1.0f
|
||||||
|
@ -13,31 +13,47 @@
|
|||||||
|
|
||||||
package de.bixilon.minosoft.gui.rendering.font.types.bitmap
|
package de.bixilon.minosoft.gui.rendering.font.types.bitmap
|
||||||
|
|
||||||
|
import de.bixilon.kotlinglm.vec2.Vec2
|
||||||
import de.bixilon.kutil.json.JsonObject
|
import de.bixilon.kutil.json.JsonObject
|
||||||
|
import de.bixilon.kutil.latch.AbstractLatch
|
||||||
import de.bixilon.kutil.primitive.IntUtil.toInt
|
import de.bixilon.kutil.primitive.IntUtil.toInt
|
||||||
import de.bixilon.minosoft.data.registries.identified.Namespaces.minecraft
|
import de.bixilon.minosoft.data.registries.identified.Namespaces.minecraft
|
||||||
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
|
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
|
||||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||||
import de.bixilon.minosoft.gui.rendering.font.types.FontType
|
import de.bixilon.minosoft.gui.rendering.font.renderer.code.CodePointRenderer
|
||||||
|
import de.bixilon.minosoft.gui.rendering.font.types.PostInitFontType
|
||||||
|
import de.bixilon.minosoft.gui.rendering.font.types.empty.EmptyCodeRenderer
|
||||||
import de.bixilon.minosoft.gui.rendering.font.types.factory.FontTypeFactory
|
import de.bixilon.minosoft.gui.rendering.font.types.factory.FontTypeFactory
|
||||||
|
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.AbstractTexture
|
||||||
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
|
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.texture
|
||||||
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||||
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.listCast
|
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.listCast
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.util.stream.IntStream
|
||||||
|
|
||||||
class BitmapFontType(
|
class BitmapFontType(
|
||||||
val chars: Int2ObjectOpenHashMap<BitmapCodeRenderer>,
|
val chars: Int2ObjectOpenHashMap<CodePointRenderer>,
|
||||||
) : FontType {
|
) : PostInitFontType {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
chars.trim()
|
chars.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun get(codePoint: Int): BitmapCodeRenderer {
|
override fun get(codePoint: Int): CodePointRenderer? {
|
||||||
return chars[codePoint]
|
return chars[codePoint]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun postInit(latch: AbstractLatch) {
|
||||||
|
for (char in chars.values) {
|
||||||
|
if (char !is BitmapCodeRenderer) continue
|
||||||
|
char.updateArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object : FontTypeFactory<BitmapFontType> {
|
companion object : FontTypeFactory<BitmapFontType> {
|
||||||
|
private const val ROW = 16
|
||||||
override val identifier = minecraft("bitmap")
|
override val identifier = minecraft("bitmap")
|
||||||
|
|
||||||
override fun build(context: RenderContext, data: JsonObject): BitmapFontType? {
|
override fun build(context: RenderContext, data: JsonObject): BitmapFontType? {
|
||||||
@ -48,12 +64,78 @@ class BitmapFontType(
|
|||||||
return load(file, height, ascent, chars, context)
|
return load(file, height, ascent, chars, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun List<String>.codePoints(): Array<IntStream> {
|
||||||
|
return this.map { it.codePoints() }.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
private fun load(file: ResourceLocation, height: Int, ascent: Int, chars: List<String>, context: RenderContext): BitmapFontType? {
|
private fun load(file: ResourceLocation, height: Int, ascent: Int, chars: List<String>, context: RenderContext): BitmapFontType? {
|
||||||
|
if (chars.isEmpty() || height <= 0) return null
|
||||||
val texture = context.textureManager.staticTextures.createTexture(file)
|
val texture = context.textureManager.staticTextures.createTexture(file)
|
||||||
texture.load(context.connection.assetsManager) // force load it, we need to calculate the width of every char
|
texture.load(context.connection.assetsManager) // force load it, we need to calculate the width of every char
|
||||||
|
|
||||||
|
return load(texture, height, ascent, chars.codePoints())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ByteBuffer.scanLine(y: Int, width: Int, start: IntArray, end: IntArray) {
|
||||||
|
for (index in 0 until (width * ROW)) {
|
||||||
|
val alpha = this[(((ROW * width) * y) + index) * 4 + 3].toInt()
|
||||||
|
if (alpha == 0) {
|
||||||
|
// transparent
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val char = index / width
|
||||||
|
val pixel = index % width
|
||||||
|
|
||||||
|
start[char] = minOf(start[char], pixel)
|
||||||
|
end[char] = maxOf(end[char], pixel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createRenderer(texture: AbstractTexture, row: Int, column: Int, start: Int, end: Int, height: Int, ascent: Int): CodePointRenderer {
|
||||||
|
if (end < start) return EmptyCodeRenderer()
|
||||||
|
|
||||||
|
val uvStart = Vec2()
|
||||||
|
val uvEnd = Vec2(0.1f)
|
||||||
|
|
||||||
|
val width = 1.0f
|
||||||
|
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
return null
|
|
||||||
|
return BitmapCodeRenderer(texture, uvStart, uvEnd, width, ascent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun load(texture: AbstractTexture, height: Int, ascent: Int, chars: Array<IntStream>): BitmapFontType? {
|
||||||
|
val rows = chars.size
|
||||||
|
val width = texture.size.x / ROW
|
||||||
|
|
||||||
|
val start = IntArray(ROW) { width }
|
||||||
|
val end = IntArray(ROW)
|
||||||
|
|
||||||
|
val renderer = Int2ObjectOpenHashMap<CodePointRenderer>()
|
||||||
|
|
||||||
|
for (row in 0 until rows) {
|
||||||
|
val iterator = chars[row].iterator()
|
||||||
|
|
||||||
|
for (pixel in 0 until height) {
|
||||||
|
texture.data!!.scanLine(row + pixel, width, start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
var column = 0
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
val codePoint = iterator.nextInt()
|
||||||
|
renderer[codePoint] = createRenderer(texture, row, column, start[column], end[column], height, ascent)
|
||||||
|
column++
|
||||||
|
}
|
||||||
|
|
||||||
|
start.fill(width); end.fill(0) // fill with maximum values again
|
||||||
|
}
|
||||||
|
|
||||||
|
texture.data!!.rewind()
|
||||||
|
|
||||||
|
if (renderer.isEmpty()) return null
|
||||||
|
return BitmapFontType(renderer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
|||||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
|
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
|
||||||
|
|
||||||
data class EmptyCodeRenderer(
|
data class EmptyCodeRenderer(
|
||||||
val width: Int,
|
val width: Int = 4,
|
||||||
) : CodePointRenderer {
|
) : CodePointRenderer {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user