Skip to content

Commit cb610b5

Browse files
committed
Parse emojis from strings
1 parent cbe6de2 commit cb610b5

File tree

3 files changed

+83
-20
lines changed

3 files changed

+83
-20
lines changed

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

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,31 @@ import com.lambda.module.modules.client.FontSettings
1010
import com.lambda.util.math.Vec2d
1111
import java.awt.Color
1212

13-
class FontRenderer(private val font: LambdaFont) : Renderer(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) {
13+
class FontRenderer(
14+
private val font: LambdaFont,
15+
private val emojis: LambdaMoji,
16+
) : Renderer(VertexMode.TRIANGLES, VertexAttrib.Group.FONT) {
1417
var scaleMultiplier = 1.0
18+
private val emojiRegex = Regex(":[a-zA-Z0-9_]+:")
19+
20+
/**
21+
* Parses the emojis in the given text.
22+
*
23+
* @param text The text to parse.
24+
* @return A list of triples containing the emoji text, start index, and end index.
25+
*/
26+
private fun parseEmojis(text: String): List<
27+
Triple<CharInfo, Int, Int>> {
28+
val result = mutableListOf<Triple<CharInfo, Int, Int>>()
29+
val matches = emojiRegex.findAll(text)
30+
31+
matches.forEach {
32+
val index = it.value.substring(1, it.value.length - 1)
33+
result.add(Triple(emojis[index] ?: return@forEach, it.range.first, it.range.last))
34+
}
35+
36+
return result
37+
}
1538

1639
fun build(
1740
text: String,
@@ -28,7 +51,25 @@ class FontRenderer(private val font: LambdaFont) : Renderer(VertexMode.TRIANGLES
2851
var posX = 0.0
2952
val posY = getHeight(scale) * -0.5 + baselineOffset * actualScale
3053

31-
text.toCharArray().forEach { char ->
54+
val emojis = parseEmojis(text)
55+
56+
val subText = emojis.asReversed().fold(text) { acc, (
57+
charInfo, start, end
58+
) ->
59+
val emojiWidth = charInfo.size.x * actualScale
60+
val emojiHeight = charInfo.size.y * actualScale
61+
62+
val startPos = Vec2d(posX, posY)
63+
val endPos = startPos + Vec2d(emojiWidth, emojiHeight)
64+
65+
putChar(position, startPos, endPos, color, charInfo)
66+
67+
posX += emojiWidth + scaledGap
68+
69+
acc.replaceRange(start, end, " ")
70+
}
71+
72+
subText.toCharArray().forEach { char ->
3273
val charInfo = font[char] ?: return@forEach
3374
val scaledSize = charInfo.size * actualScale
3475

@@ -50,8 +91,20 @@ class FontRenderer(private val font: LambdaFont) : Renderer(VertexMode.TRIANGLES
5091
fun getWidth(text: String, scale: Double = 1.0): Double {
5192
var width = 0.0
5293

53-
text.forEach { char ->
54-
val glyph = font[char] ?: return@forEach
94+
val emojis = parseEmojis(text)
95+
96+
val subText = emojis.asReversed().fold(text) { acc, (
97+
charInfo, start, end
98+
) ->
99+
val emojiWidth = charInfo.size.x
100+
101+
width += emojiWidth + gap
102+
103+
acc.replaceRange(start, end, " ")
104+
}
105+
106+
subText.forEach {
107+
val glyph = font[it] ?: return@forEach
55108
width += glyph.size.x + gap
56109
}
57110

@@ -88,6 +141,7 @@ class FontRenderer(private val font: LambdaFont) : Renderer(VertexMode.TRIANGLES
88141
override fun render() {
89142
shader.use()
90143
font.glyphs.bind()
144+
//emojis.glyphs.bind() // You have to modify the uniform in the shader to use the correct texture
91145
super.render()
92146
}
93147

@@ -98,4 +152,4 @@ class FontRenderer(private val font: LambdaFont) : Renderer(VertexMode.TRIANGLES
98152
private val baselineOffset get() = FontSettings.baselineOffset * 2.0f - 10f
99153
private val gap get() = FontSettings.gapSetting * 0.5f - 0.8f
100154
}
101-
}
155+
}

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

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
package com.lambda.graphics.renderer.gui.font.glyph
22

3+
import com.lambda.Lambda.LOG
34
import com.lambda.graphics.texture.MipmapTexture
45
import com.lambda.module.modules.client.FontSettings
56
import com.lambda.util.math.Vec2d
67
import java.awt.Color
78
import java.awt.Graphics2D
89
import java.awt.image.BufferedImage
9-
import java.io.File
1010
import java.net.URL
1111
import java.nio.file.Files
1212
import java.util.zip.ZipFile
1313
import javax.imageio.ImageIO
1414
import kotlin.math.ceil
1515
import kotlin.math.sqrt
16+
import kotlin.system.measureTimeMillis
1617

1718
class EmojiGlyphs(zipUrl: String) {
1819
private val emojiMap = mutableMapOf<String, CharInfo>()
@@ -40,25 +41,29 @@ class EmojiGlyphs(zipUrl: String) {
4041
var x = 0
4142
var y = 0
4243

43-
zip.entries().asSequence().forEach { entry ->
44-
val name = entry.name.substringAfterLast("/").substringBeforeLast(".")
45-
val emoji = ImageIO.read(zip.getInputStream(entry))
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))
4648

47-
if (x + emoji.width >= width) {
48-
y += emoji.height
49-
x = 0
50-
}
49+
if (x + emoji.width >= width) {
50+
y += emoji.height
51+
x = 0
52+
}
5153

52-
graphics.drawImage(emoji, x, y, null)
54+
graphics.drawImage(emoji, x, y, null)
5355

54-
val uv1 = Vec2d(x.toDouble(), y.toDouble()) * texelSize
55-
val uv2 = Vec2d(x, y).plus(dimensions) * texelSize
56-
emojiMap[name] = CharInfo(dimensions, uv1, uv2)
56+
val uv1 = Vec2d(x.toDouble(), y.toDouble()) * texelSize
57+
val uv2 = Vec2d(x, y).plus(dimensions) * texelSize
58+
emojiMap[name] = CharInfo(dimensions, uv1, uv2)
5759

58-
x += emoji.width
60+
x += emoji.width
61+
}
5962
}
6063

6164
fontTexture = MipmapTexture(image)
65+
66+
LOG.info("Emoji loaded with ${emojiMap.size} characters (${time}ms)")
6267
}
6368
}
6469

common/src/main/kotlin/com/lambda/gui/api/RenderLayer.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@ package com.lambda.gui.api
22

33
import com.lambda.graphics.renderer.gui.font.FontRenderer
44
import com.lambda.graphics.renderer.gui.font.LambdaFont
5+
import com.lambda.graphics.renderer.gui.font.LambdaMoji
56
import com.lambda.graphics.renderer.gui.rect.FilledRectRenderer
67
import com.lambda.graphics.renderer.gui.rect.OutlineRectRenderer
78

89
class RenderLayer {
910
val filled = FilledRectRenderer()
1011
val outline = OutlineRectRenderer()
11-
val font = FontRenderer(LambdaFont.FiraSansRegular)
12+
val font = FontRenderer(
13+
LambdaFont.FiraSansRegular,
14+
LambdaMoji.Twemoji,
15+
)
1216

1317
fun render() {
1418
filled.render()
1519
outline.render()
1620
font.render()
1721
}
18-
}
22+
}

0 commit comments

Comments
 (0)