Skip to content

Commit 1908d4b

Browse files
committed
Better emoji loading
1 parent a5a99e7 commit 1908d4b

File tree

3 files changed

+55
-42
lines changed

3 files changed

+55
-42
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import com.lambda.graphics.renderer.gui.font.glyph.EmojiGlyphs
66
enum class LambdaMoji(private val zipUrl: String) {
77
Twemoji("https://github.com/Edouard127/emoji-generator/releases/latest/download/emojis.zip");
88

9-
lateinit var glyphs: EmojiGlyphs
9+
lateinit var glyphs: EmojiGlyphs // TODO: Support multiple emoji pools
1010

1111
operator fun get(emoji: String) = glyphs.getEmoji(emoji)
1212

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

Lines changed: 51 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import com.lambda.util.math.Vec2d
77
import java.awt.Color
88
import java.awt.Graphics2D
99
import java.awt.image.BufferedImage
10+
import java.io.File
1011
import java.net.URL
11-
import java.nio.file.Files
1212
import java.util.zip.ZipFile
1313
import javax.imageio.ImageIO
1414
import kotlin.math.ceil
@@ -19,52 +19,63 @@ class EmojiGlyphs(zipUrl: String) {
1919
private val emojiMap = mutableMapOf<String, CharInfo>()
2020
private val fontTexture: MipmapTexture
2121

22-
init {
23-
val file = Files.createTempFile("emoji", ".zip").toFile()
24-
val url = URL(zipUrl)
25-
file.writeBytes(url.readBytes())
26-
27-
ZipFile(file).use { zip ->
28-
// someone please refactor this
29-
val first = ImageIO.read(zip.getInputStream(zip.entries().nextElement()))
30-
31-
val size = zip.entries().asSequence().count()
32-
val dimensions = Vec2d(first.width.toDouble(), first.height.toDouble())
33-
val texelSize = 1.0 / size
34-
val width = first.width * ceil(sqrt(size.toDouble())).toInt()
35-
val height = first.height * ceil(sqrt(size.toDouble())).toInt()
36-
37-
val image = BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)
38-
val graphics = image.graphics as Graphics2D
39-
graphics.background = Color(0, 0, 0, 0)
40-
41-
var x = 0
42-
var y = 0
43-
44-
val time = measureTimeMillis {
45-
zip.entries().asSequence().forEach { entry ->
46-
val name = entry.name.substringAfterLast("/").substringBeforeLast(".")
47-
val emoji = ImageIO.read(zip.getInputStream(entry))
48-
49-
if (x + emoji.width >= width) {
50-
y += emoji.height
51-
x = 0
52-
}
22+
private val image: BufferedImage
23+
private val graphics: Graphics2D
5324

54-
graphics.drawImage(emoji, x, y, null)
25+
init {
26+
var x = 0
27+
var y = 0
5528

56-
val uv1 = Vec2d(x.toDouble(), y.toDouble()) * texelSize
57-
val uv2 = Vec2d(x, y).plus(dimensions) * texelSize
58-
emojiMap[name] = CharInfo(dimensions, uv1 * -1.0, uv2 * -1.0)
29+
val time = measureTimeMillis {
30+
val file = File.createTempFile("emoji", ".zip")
31+
file.deleteOnExit()
5932

60-
x += emoji.width
33+
file.outputStream().use { output ->
34+
URL(zipUrl).openStream().use { input ->
35+
input.copyTo(output)
6136
}
6237
}
6338

64-
fontTexture = MipmapTexture(image)
39+
ZipFile(file).use { zip ->
40+
val firstImage = ImageIO.read(zip.getInputStream(zip.entries().nextElement()))
41+
42+
val length = zip.size().toDouble()
43+
val squaredLength = ceil(sqrt(length)).toInt()
44+
45+
val texelSize = 1.0 / length
46+
val width = firstImage.width
47+
val height = firstImage.height
48+
49+
image = BufferedImage(width * squaredLength, height * squaredLength, BufferedImage.TYPE_INT_ARGB)
50+
graphics = image.graphics as Graphics2D
51+
graphics.color = Color(0, 0, 0, 0)
52+
53+
zip.entries().asSequence()
54+
.forEach { entry ->
55+
val name = entry.name.substringAfterLast("/").substringBeforeLast(".")
56+
val emoji = ImageIO.read(zip.getInputStream(entry))
57+
58+
if (x + emoji.width >= image.width) {
59+
y += emoji.height
60+
x = 0
61+
}
6562

66-
LOG.info("Loaded $size emojis in $time ms")
63+
graphics.drawImage(emoji, x, y, null)
64+
65+
val size = Vec2d(emoji.width, emoji.height)
66+
val uv1 = Vec2d(-x, -y) * texelSize
67+
val uv2 = Vec2d(-x, -y).minus(size) * texelSize
68+
69+
emojiMap[name] = CharInfo(size, uv1, uv2)
70+
71+
x += emoji.width
72+
}
73+
}
74+
75+
fontTexture = MipmapTexture(image)
6776
}
77+
78+
LOG.info("Loaded ${emojiMap.size} emojis in $time ms")
6879
}
6980

7081
fun bind() {
@@ -78,6 +89,6 @@ class EmojiGlyphs(zipUrl: String) {
7889
emojiMap[emoji]
7990

8091
companion object {
81-
private const val GL_TEXTURE_SLOT = 1
92+
private const val GL_TEXTURE_SLOT = 1 // TODO: Texture slot borrowing
8293
}
8394
}

common/src/main/kotlin/com/lambda/util/math/Vec2d.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ data class Vec2d(val x: Double, val y: Double) {
44
constructor(x: Int, y: Int) : this(x.toDouble(), y.toDouble())
55
constructor(x: Float, y: Float) : this(x.toDouble(), y.toDouble())
66

7+
operator fun unaryPlus() = this
78
operator fun plus(vec2d: Vec2d) = plus(vec2d.x, vec2d.y)
89
operator fun plus(add: Double) = plus(add, add)
910
fun plus(x: Double, y: Double) = Vec2d(this.x + x, this.y + y)
1011

12+
operator fun unaryMinus() = Vec2d(-x, -y)
1113
operator fun minus(vec2d: Vec2d) = minus(vec2d.x, vec2d.y)
1214
operator fun minus(sub: Double) = minus(sub, sub)
1315
fun minus(x: Double, y: Double) = plus(-x, -y)
@@ -29,4 +31,4 @@ data class Vec2d(val x: Double, val y: Double) {
2931
val TOP = Vec2d(0.0, 1.0)
3032
val BOTTOM = Vec2d(0.0, -1.0)
3133
}
32-
}
34+
}

0 commit comments

Comments
 (0)