Skip to content

Commit 7188573

Browse files
committed
Failsafe Emoji loading
1 parent 82deac4 commit 7188573

File tree

2 files changed

+50
-46
lines changed

2 files changed

+50
-46
lines changed

common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/LambdaEmoji.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ enum class LambdaEmoji(private val zipUrl: String) {
88

99
lateinit var glyphs: EmojiGlyphs
1010

11-
operator fun get(emoji: String) = glyphs.getEmoji(emoji)
11+
operator fun get(emoji: String) = glyphs.emojiFromString(emoji)
1212

1313
fun loadGlyphs() {
1414
glyphs = EmojiGlyphs(zipUrl)

common/src/main/kotlin/com/lambda/graphics/renderer/gui/font/glyph/EmojiGlyphs.kt

Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import com.google.common.math.IntMath.pow
44
import com.lambda.Lambda.LOG
55
import com.lambda.graphics.texture.MipmapTexture
66
import com.lambda.module.modules.client.RenderSettings
7+
import com.lambda.threading.runGameScheduled
8+
import com.lambda.threading.runIO
79
import com.lambda.util.math.Vec2d
810
import java.awt.Color
911
import java.awt.Graphics2D
@@ -20,70 +22,74 @@ import kotlin.system.measureTimeMillis
2022
// TODO: AbstractGlyphs to use for both Font & Emoji glyphs?
2123
class EmojiGlyphs(zipUrl: String) {
2224
private val emojiMap = mutableMapOf<String, GlyphInfo>()
23-
private val fontTexture: MipmapTexture
25+
private lateinit var fontTexture: MipmapTexture
2426

25-
private val image: BufferedImage
26-
private val graphics: Graphics2D
27+
private lateinit var image: BufferedImage
28+
private lateinit var graphics: Graphics2D
2729

2830
init {
29-
var x = 0
30-
var y = 0
31+
runCatching {
32+
val time = measureTimeMillis {
33+
downloadAndProcessZip(zipUrl)
34+
}
35+
LOG.info("Loaded ${emojiMap.size} emojis in $time ms")
36+
}.onFailure {
37+
LOG.error("Failed to load emojis: ${it.message}", it)
38+
}
39+
}
3140

32-
val time = measureTimeMillis {
33-
val file = File.createTempFile("emoji", ".zip")
34-
file.deleteOnExit()
41+
private fun downloadAndProcessZip(zipUrl: String) {
42+
val file = File.createTempFile("emoji", ".zip").apply { deleteOnExit() }
3543

44+
URL(zipUrl).openStream().use { input ->
3645
file.outputStream().use { output ->
37-
URL(zipUrl).openStream().use { input ->
38-
input.copyTo(output)
39-
}
46+
input.copyTo(output)
4047
}
48+
}
4149

42-
ZipFile(file).use { zip ->
43-
val firstImage = ImageIO.read(zip.getInputStream(zip.entries().nextElement()))
50+
ZipFile(file).use { zip ->
51+
val firstImage = ImageIO.read(zip.getInputStream(zip.entries().nextElement()))
52+
val length = zip.size().toDouble()
4453

45-
val length = zip.size().toDouble()
54+
val textureDimensionLength: (Int) -> Int = { dimLength ->
55+
pow(2, ceil(log2((dimLength + STEP) * sqrt(length))).toInt())
56+
}
4657

47-
fun getTextureDimensionLength(dimLength: Int) =
48-
pow(2, ceil(log2((dimLength + STEP) * sqrt(length))).toInt())
58+
val width = textureDimensionLength(firstImage.width)
59+
val height = textureDimensionLength(firstImage.height)
60+
val texelSize = Vec2d.ONE / Vec2d(width, height)
4961

50-
val width = getTextureDimensionLength(firstImage.width)
51-
val height = getTextureDimensionLength(firstImage.height)
52-
val texelSize = Vec2d.ONE / Vec2d(width, height)
62+
image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
63+
graphics = image.graphics as Graphics2D
64+
graphics.color = Color(0, 0, 0, 0)
5365

54-
image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
55-
graphics = image.graphics as Graphics2D
56-
graphics.color = Color(0, 0, 0, 0)
66+
var x = 0
67+
var y = 0
5768

58-
for (entry in zip.entries()) {
59-
val name = entry.name.substringAfterLast("/").substringBeforeLast(".")
60-
val emoji = ImageIO.read(zip.getInputStream(entry))
69+
for (entry in zip.entries()) {
70+
val name = entry.name.substringAfterLast("/").substringBeforeLast(".")
71+
val emoji = ImageIO.read(zip.getInputStream(entry))
6172

62-
if (x + emoji.width >= image.width) {
63-
y += emoji.height + STEP
64-
x = 0
65-
}
73+
if (x + emoji.width >= image.width) {
74+
y += emoji.height + STEP
75+
x = 0
76+
}
6677

67-
check(y + emoji.height < image.height) { "Can't load emoji glyphs. Texture size is too small" }
78+
check(y + emoji.height < image.height) { "Can't load emoji glyphs. Texture size is too small" }
6879

69-
graphics.drawImage(emoji, x, y, null)
80+
graphics.drawImage(emoji, x, y, null)
7081

71-
val size = Vec2d(emoji.width, emoji.height)
72-
val uv1 = Vec2d(x, y) * texelSize
73-
val uv2 = Vec2d(x, y).plus(size) * texelSize
82+
val size = Vec2d(emoji.width, emoji.height)
83+
val uv1 = Vec2d(x, y) * texelSize
84+
val uv2 = Vec2d(x, y).plus(size) * texelSize
7485

75-
emojiMap[name] = GlyphInfo(size, -uv1, -uv2)
86+
emojiMap[name] = GlyphInfo(size, -uv1, -uv2)
7687

77-
x += emoji.width + STEP
78-
}
88+
x += emoji.width + STEP
7989
}
80-
81-
//ImageIO.write(image, "png", File("emoji.png"))
82-
83-
fontTexture = MipmapTexture(image)
8490
}
8591

86-
LOG.info("Loaded ${emojiMap.size} emojis in $time ms")
92+
fontTexture = MipmapTexture(image)
8793
}
8894

8995
fun bind() {
@@ -93,12 +99,10 @@ class EmojiGlyphs(zipUrl: String) {
9399
}
94100
}
95101

96-
fun getEmoji(emoji: String): GlyphInfo? =
97-
emojiMap[emoji]
102+
fun emojiFromString(emoji: String) = emojiMap[emoji]
98103

99104
companion object {
100105
private const val STEP = 2
101-
102106
private const val GL_TEXTURE_SLOT = 1 // TODO: Texture slot borrowing
103107
}
104108
}

0 commit comments

Comments
 (0)