mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-14 09:56:37 -04:00
improved code point rendering
This commit is contained in:
parent
fa8cd40617
commit
8ef3deacbe
@ -60,16 +60,16 @@ class ChatComponentRendererTest {
|
||||
fun `3 chars`() {
|
||||
val info = render(TextComponent("bcd"))
|
||||
assertEquals(info.lineIndex, 0)
|
||||
assertEquals(info.lines, listOf(TextLineInfo(BaseComponent(TextComponent("bcd")), 4.5f)))
|
||||
assertEquals(info.size, Vec2(4.5f, 11.0f)) // b + spacing + c + spacing + d
|
||||
assertEquals(info.lines, listOf(TextLineInfo(BaseComponent(TextComponent("bcd")), 5.0f)))
|
||||
assertEquals(info.size, Vec2(5.0f, 11.0f)) // b + spacing + c + spacing + d
|
||||
assertFalse(info.cutOff)
|
||||
}
|
||||
|
||||
fun `max line size`() {
|
||||
val info = render(TextComponent("bcdef"), maxSize = Vec2(5.0f, Float.MAX_VALUE))
|
||||
val info = render(TextComponent("bcdef"), maxSize = Vec2(5.5f, Float.MAX_VALUE))
|
||||
assertEquals(info.lineIndex, 1)
|
||||
assertEquals(info.lines, listOf(TextLineInfo(BaseComponent(TextComponent("bcd")), 4.5f), TextLineInfo(BaseComponent(TextComponent("ef")), 5.0f)))
|
||||
assertEquals(info.size, Vec2(5.0f, 22.0f)) // b + spacing + c + spacing + d \n e + spacing + f
|
||||
assertEquals(info.lines, listOf(TextLineInfo(BaseComponent(TextComponent("bcd")), 5.0f), TextLineInfo(BaseComponent(TextComponent("ef")), 5.5f)))
|
||||
assertEquals(info.size, Vec2(5.5f, 22.0f)) // b + spacing + c + spacing + d \n e + spacing + f
|
||||
assertFalse(info.cutOff)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2023 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.font.renderer
|
||||
|
||||
enum class CodePointAddResult {
|
||||
FINE,
|
||||
NEW_LINE,
|
||||
BREAK,
|
||||
;
|
||||
}
|
@ -14,8 +14,14 @@
|
||||
package de.bixilon.minosoft.gui.rendering.font.renderer.code
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2
|
||||
import de.bixilon.minosoft.data.text.formatting.FormattingCodes
|
||||
import de.bixilon.minosoft.data.text.formatting.TextFormatting
|
||||
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
|
||||
import de.bixilon.minosoft.gui.rendering.font.WorldGUIConsumer
|
||||
import de.bixilon.minosoft.gui.rendering.font.renderer.CodePointAddResult
|
||||
import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextOffset
|
||||
import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextRenderInfo
|
||||
import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextRenderProperties
|
||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY
|
||||
@ -31,4 +37,39 @@ interface CodePointRenderer {
|
||||
|
||||
return calculateWidth(scale, shadow)
|
||||
}
|
||||
|
||||
private fun getVerticalSpacing(offset: TextOffset, properties: TextRenderProperties): Float {
|
||||
if (offset.offset.x == offset.initial.x) return 0.0f
|
||||
// not at line start
|
||||
return properties.charSpacing.vertical * properties.scale
|
||||
}
|
||||
|
||||
|
||||
fun render(offset: TextOffset, color: RGBColor, properties: TextRenderProperties, info: TextRenderInfo, formatting: TextFormatting, codePoint: Int, consumer: GUIVertexConsumer?, options: GUIVertexOptions?): CodePointAddResult {
|
||||
val codePointWidth = calculateWidth(properties.scale, properties.shadow)
|
||||
var width = codePointWidth + getVerticalSpacing(offset, properties)
|
||||
val height = offset.getNextLineHeight(properties)
|
||||
|
||||
val canAdd = offset.canAdd(properties, info, width, height)
|
||||
when (canAdd) {
|
||||
CodePointAddResult.FINE -> Unit
|
||||
CodePointAddResult.NEW_LINE -> {
|
||||
width = codePointWidth // new line, remove vertical spacing
|
||||
info.size.y += height
|
||||
}
|
||||
|
||||
CodePointAddResult.BREAK -> return CodePointAddResult.BREAK
|
||||
}
|
||||
|
||||
|
||||
if (consumer != null) {
|
||||
render(offset.offset, color, properties.shadow, FormattingCodes.BOLD in formatting, FormattingCodes.ITALIC in formatting, properties.scale, consumer, options)
|
||||
} else {
|
||||
info.update(offset, properties, width) // info should only be updated when we determinate text properties, we know all that already when actually rendering it
|
||||
}
|
||||
|
||||
offset.offset.x += width
|
||||
|
||||
return canAdd
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,12 @@ package de.bixilon.minosoft.gui.rendering.font.renderer.component
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.minosoft.data.text.TextComponent
|
||||
import de.bixilon.minosoft.data.text.formatting.FormattingCodes
|
||||
import de.bixilon.minosoft.data.text.formatting.TextFormatting
|
||||
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
|
||||
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
|
||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||
import de.bixilon.minosoft.gui.rendering.font.WorldGUIConsumer
|
||||
import de.bixilon.minosoft.gui.rendering.font.manager.FontManager
|
||||
import de.bixilon.minosoft.gui.rendering.font.renderer.CodePointAddResult
|
||||
import de.bixilon.minosoft.gui.rendering.font.renderer.code.CodePointRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextOffset
|
||||
import de.bixilon.minosoft.gui.rendering.font.renderer.element.TextRenderInfo
|
||||
@ -45,13 +45,6 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
private fun renderCodePoint(offset: TextOffset, renderer: CodePointRenderer, color: RGBColor, properties: TextRenderProperties, info: TextRenderInfo, formatting: TextFormatting, consumer: GUIVertexConsumer?, options: GUIVertexOptions?) {
|
||||
if (consumer != null) {
|
||||
renderer.render(offset.offset, color, properties.shadow, FormattingCodes.BOLD in formatting, FormattingCodes.ITALIC in formatting, properties.scale, consumer, options)
|
||||
}
|
||||
offset.offset.x += 8
|
||||
}
|
||||
|
||||
private fun renderStrikethrough() {
|
||||
TODO()
|
||||
}
|
||||
@ -68,6 +61,8 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
|
||||
val formatting = text.formatting
|
||||
var skipWhitespaces = false
|
||||
|
||||
val line = StringBuilder()
|
||||
|
||||
for (codePoint in text.message.codePoints()) {
|
||||
if (codePoint == '\n'.code) {
|
||||
renderNewline(offset, info)
|
||||
@ -77,10 +72,31 @@ object TextComponentRenderer : ChatComponentRenderer<TextComponent> {
|
||||
if (skipWhitespaces && Character.isWhitespace(codePoint)) {
|
||||
continue
|
||||
}
|
||||
skipWhitespaces = false
|
||||
|
||||
val renderer = getRenderer(codePoint, properties, textFont, fontManager) ?: continue
|
||||
renderCodePoint(offset, renderer, color, properties, info, formatting, consumer, options)
|
||||
val renderer = getRenderer(codePoint, properties, textFont, fontManager)
|
||||
if (renderer != null && renderer.calculateWidth(properties.scale, properties.shadow) <= 0.0f) {
|
||||
continue
|
||||
}
|
||||
skipWhitespaces = false
|
||||
if (renderer == null) {
|
||||
continue
|
||||
}
|
||||
|
||||
val lineIndex = info.lineIndex
|
||||
|
||||
val lineInfo = renderer.render(offset, color, properties, info, formatting, codePoint, consumer, options)
|
||||
if (consumer != null) continue // already know that information
|
||||
if (lineInfo == CodePointAddResult.BREAK) break
|
||||
|
||||
if (lineIndex != info.lineIndex) {
|
||||
// new line started
|
||||
info.lines[lineIndex].push(text, line) // previous line
|
||||
}
|
||||
|
||||
line.appendCodePoint(codePoint)
|
||||
}
|
||||
if (line.isNotEmpty()) {
|
||||
info.lines[info.lineIndex].push(text, line)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,15 @@
|
||||
package de.bixilon.minosoft.gui.rendering.font.renderer.element
|
||||
|
||||
import de.bixilon.minosoft.data.text.BaseComponent
|
||||
import de.bixilon.minosoft.data.text.TextComponent
|
||||
|
||||
data class TextLineInfo(
|
||||
val text: BaseComponent = BaseComponent(),
|
||||
var width: Float = 0.0f,
|
||||
)
|
||||
) {
|
||||
|
||||
fun push(component: TextComponent, builder: StringBuilder) {
|
||||
text += component.copy(message = builder.toString())
|
||||
builder.clear()
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,61 @@
|
||||
package de.bixilon.minosoft.gui.rendering.font.renderer.element
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2
|
||||
import de.bixilon.minosoft.gui.rendering.font.renderer.CodePointAddResult
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY
|
||||
|
||||
class TextOffset(
|
||||
val initial: Vec2 = Vec2.EMPTY,
|
||||
) {
|
||||
var offset = Vec2(initial)
|
||||
|
||||
private fun fits(offset: Float, initial: Float, max: Float, value: Float): Boolean {
|
||||
val size = offset - initial
|
||||
val remaining = max - size
|
||||
|
||||
return remaining >= value
|
||||
}
|
||||
|
||||
|
||||
fun fitsX(info: TextRenderInfo, width: Float): Boolean {
|
||||
return fits(offset.x, initial.x, info.maxSize.x, width)
|
||||
}
|
||||
|
||||
fun fitsY(info: TextRenderInfo, height: Float): Boolean {
|
||||
return fits(offset.y, initial.y, info.maxSize.y, height)
|
||||
}
|
||||
|
||||
fun fitsInLine(properties: TextRenderProperties, info: TextRenderInfo, width: Float): Boolean {
|
||||
return fitsX(info, width) && fitsY(info, properties.lineHeight)
|
||||
}
|
||||
|
||||
fun getNextLineHeight(properties: TextRenderProperties): Float {
|
||||
var height = properties.lineHeight
|
||||
if (offset.y != initial.y) {
|
||||
// previous line present
|
||||
height += properties.lineSpacing * properties.scale
|
||||
}
|
||||
|
||||
return height
|
||||
}
|
||||
|
||||
fun addLine(properties: TextRenderProperties, info: TextRenderInfo, height: Float): Boolean {
|
||||
if (!fitsY(info, height)) return false
|
||||
|
||||
offset.y += height
|
||||
offset.x = initial.x
|
||||
info.lines += TextLineInfo()
|
||||
info.lineIndex++
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
fun canAdd(properties: TextRenderProperties, info: TextRenderInfo, width: Float, height: Float): CodePointAddResult {
|
||||
if (fitsInLine(properties, info, width)) return CodePointAddResult.FINE
|
||||
if (addLine(properties, info, height) && fitsInLine(properties, info, width)) return CodePointAddResult.NEW_LINE
|
||||
|
||||
info.cutOff = true
|
||||
return CodePointAddResult.BREAK
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,30 @@ import de.bixilon.kotlinglm.vec2.Vec2
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2Util.EMPTY
|
||||
|
||||
class TextRenderInfo(
|
||||
val parentSize: Vec2,
|
||||
val maxSize: Vec2,
|
||||
) {
|
||||
val lines: MutableList<TextLineInfo> = mutableListOf()
|
||||
var lineIndex: Int = 0
|
||||
|
||||
var size = Vec2.EMPTY
|
||||
var cutOff = false
|
||||
|
||||
|
||||
fun update(offset: TextOffset, properties: TextRenderProperties, width: Float): TextLineInfo {
|
||||
size.x = maxOf(offset.offset.x - offset.initial.x + width, size.x)
|
||||
|
||||
val line: TextLineInfo
|
||||
if (lineIndex == 0 && lines.isEmpty()) {
|
||||
// first char of all lines
|
||||
line = TextLineInfo()
|
||||
lines += line
|
||||
size.y = properties.lineHeight
|
||||
} else {
|
||||
line = lines[lineIndex]
|
||||
}
|
||||
|
||||
line.width += width
|
||||
|
||||
return line
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,11 @@ data class TextRenderProperties(
|
||||
val fallbackColor: RGBColor = ChatColors.WHITE,
|
||||
val font: FontType? = null,
|
||||
) {
|
||||
|
||||
val lineHeight: Float
|
||||
get() = (charSpacing.top + charBaseHeight + charSpacing.bottom) * scale
|
||||
|
||||
|
||||
companion object {
|
||||
val DEFAULT = TextRenderProperties()
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user