some more color tests and fixes

This commit is contained in:
Moritz Zwerger 2025-03-31 19:33:03 +02:00
parent 83c3337f38
commit a17f75c201
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
14 changed files with 109 additions and 41 deletions

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2024 Moritz Zwerger * Copyright (C) 2020-2025 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 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.
* *
@ -70,6 +70,7 @@ class SignBlockEntityTest {
assertEquals(entity.back.color, ChatColors.BLUE) assertEquals(entity.back.color, ChatColors.BLUE)
assertEquals(entity.back.text, arrayOf(ChatComponent.of("This is the back"), ChatComponent.of("text"), ChatComponent.of("of"), ChatComponent.of("this sign."))) assertEquals(entity.back.text, arrayOf(ChatComponent.of("This is the back"), ChatComponent.of("text"), ChatComponent.of("of"), ChatComponent.of("this sign.")))
} }
fun `nbt 1_20_4`() { fun `nbt 1_20_4`() {
val nbt = mapOf( val nbt = mapOf(
"is_waxed" to 1.toByte(), "is_waxed" to 1.toByte(),

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2024 Moritz Zwerger * Copyright (C) 2020-2025 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 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.
* *
@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.rendering.textures
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kutil.stream.InputStreamUtil.readAll import de.bixilon.kutil.stream.InputStreamUtil.readAll
import de.bixilon.kutil.unsafe.UnsafeUtil.setUnsafeAccessible import de.bixilon.kutil.unsafe.UnsafeUtil.setUnsafeAccessible
import de.bixilon.minosoft.data.text.formatting.color.RGBAColor
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.RGB8Buffer import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.RGB8Buffer
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.RGBA8Buffer import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.RGBA8Buffer
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.TextureBuffer import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.TextureBuffer
@ -69,10 +70,9 @@ class TextureReadingTest {
} }
private fun TextureBuffer.assertSand() { private fun TextureBuffer.assertSand() {
assertEquals(getRGBA(0, 0), 0xE7E4BBFF.toInt()) assertEquals(getRGBA(0, 0), RGBAColor(0xE7, 0xE4, 0xBB))
assertEquals(getRGBA(1, 0), 0xDACFA3FF.toInt()) assertEquals(getRGBA(1, 0), RGBAColor(0xDA, 0xCF, 0xA3))
assertEquals(getRGBA(0, 1), 0xD5C496FF.toInt()) assertEquals(getRGBA(0, 1), RGBAColor(0xD5, 0xC4, 0x96))
} }
fun `read1 sand rgba`() { fun `read1 sand rgba`() {

View File

@ -24,7 +24,7 @@ import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.data.text.EmptyComponent import de.bixilon.minosoft.data.text.EmptyComponent
import de.bixilon.minosoft.data.text.formatting.color.ChatColors import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.text.formatting.color.RGBColor import de.bixilon.minosoft.data.text.formatting.color.RGBAColor
import de.bixilon.minosoft.protocol.network.session.play.PlaySession import de.bixilon.minosoft.protocol.network.session.play.PlaySession
class SignBlockEntity(session: PlaySession) : BlockEntity(session) { class SignBlockEntity(session: PlaySession) : BlockEntity(session) {
@ -56,7 +56,7 @@ class SignBlockEntity(session: PlaySession) : BlockEntity(session) {
class SignTextProperties( class SignTextProperties(
var glowing: Boolean = false, var glowing: Boolean = false,
var color: RGBColor? = null, var color: RGBAColor? = null,
val text: Array<ChatComponent> = Array(LINES) { EmptyComponent }, val text: Array<ChatComponent> = Array(LINES) { EmptyComponent },
) { ) {
@ -78,7 +78,7 @@ class SignBlockEntity(session: PlaySession) : BlockEntity(session) {
} }
fun update(color: Any?, glowing: Any?) { fun update(color: Any?, glowing: Any?) {
this.color = color?.toString()?.lowercase()?.let { ChatColors.NAME_MAP[it]?.rgb() } this.color = color?.toString()?.lowercase()?.let { ChatColors.NAME_MAP[it] }
this.glowing = glowing?.toBoolean() ?: false this.glowing = glowing?.toBoolean() ?: false
} }
} }

View File

@ -130,12 +130,12 @@ open class TextComponent(
} }
fun copy(message: Any? = this.message, color: RGBAColor? = this.color, formatting: BitEnumSet<FormattingCodes> = this.formatting, clickEvent: ClickEvent? = this.clickEvent, hoverEvent: HoverEvent? = this.hoverEvent) = TextComponent( fun copy(message: Any? = this.message, color: RGBAColor? = this.color, formatting: BitEnumSet<FormattingCodes> = this.formatting, clickEvent: ClickEvent? = this.clickEvent, hoverEvent: HoverEvent? = this.hoverEvent) = TextComponent(
message = message, message = message,
color = color, color = color,
formatting = formatting, formatting = formatting,
clickEvent = clickEvent, clickEvent = clickEvent,
hoverEvent = hoverEvent, hoverEvent = hoverEvent,
) )
override val length: Int override val length: Int
get() = message.length get() = message.length

View File

@ -17,12 +17,12 @@ package de.bixilon.minosoft.data.text.formatting.color
value class RGBAArray(val array: IntArray) { value class RGBAArray(val array: IntArray) {
constructor(size: Int) : this(IntArray(size)) constructor(size: Int) : this(IntArray(size))
constructor(size: Int, init: (Int) -> RGBAColor) : this(IntArray(size) { init.invoke(it).argb }) constructor(size: Int, init: (Int) -> RGBAColor) : this(IntArray(size) { init.invoke(it).rgba })
operator fun get(index: Int) = RGBAColor(array[index]) operator fun get(index: Int) = RGBAColor(array[index])
inline fun getOrNull(index: Int) = array.getOrNull(index)?.let { RGBAColor(it) } inline fun getOrNull(index: Int) = array.getOrNull(index)?.let { RGBAColor(it) }
operator fun set(index: Int, value: RGBAColor) { operator fun set(index: Int, value: RGBAColor) {
array[index] = value.rgb array[index] = value.rgba
} }
} }

View File

@ -27,7 +27,7 @@ import de.bixilon.minosoft.data.text.formatting.color.Color.Companion.clamp
@JvmInline @JvmInline
value class RGBAColor(val argb: Int) : Color, TextFormattable { value class RGBAColor(val rgba: Int) : Color, TextFormattable {
constructor(red: Int, green: Int, blue: Int) : this(red, green, blue, MAX) constructor(red: Int, green: Int, blue: Int) : this(red, green, blue, MAX)
constructor(red: Int, green: Int, blue: Int, alpha: Int) : this(((alpha.clamp() and MASK) shl ALPHA_SHIFT) or ((red.clamp() and MASK) shl RED_SHIFT) or ((green.clamp() and MASK) shl GREEN_SHIFT) or ((blue.clamp() and MASK) shl BLUE_SHIFT)) constructor(red: Int, green: Int, blue: Int, alpha: Int) : this(((alpha.clamp() and MASK) shl ALPHA_SHIFT) or ((red.clamp() and MASK) shl RED_SHIFT) or ((green.clamp() and MASK) shl GREEN_SHIFT) or ((blue.clamp() and MASK) shl BLUE_SHIFT))
@ -35,10 +35,10 @@ value class RGBAColor(val argb: Int) : Color, TextFormattable {
constructor(red: Float, green: Float, blue: Float) : this(Color.fromFloat(red), Color.fromFloat(green), Color.fromFloat(blue)) constructor(red: Float, green: Float, blue: Float) : this(Color.fromFloat(red), Color.fromFloat(green), Color.fromFloat(blue))
constructor(red: Float, green: Float, blue: Float, alpha: Float) : this(Color.fromFloat(red), Color.fromFloat(green), Color.fromFloat(blue), Color.fromFloat(alpha)) constructor(red: Float, green: Float, blue: Float, alpha: Float) : this(Color.fromFloat(red), Color.fromFloat(green), Color.fromFloat(blue), Color.fromFloat(alpha))
override inline val red: Int get() = (argb ushr RED_SHIFT) and MASK override inline val red: Int get() = (rgba ushr RED_SHIFT) and MASK
override inline val green: Int get() = (argb ushr GREEN_SHIFT) and MASK override inline val green: Int get() = (rgba ushr GREEN_SHIFT) and MASK
override inline val blue: Int get() = (argb ushr BLUE_SHIFT) and MASK override inline val blue: Int get() = (rgba ushr BLUE_SHIFT) and MASK
inline val alpha: Int get() = (argb ushr ALPHA_SHIFT) and MASK inline val alpha: Int get() = (rgba ushr ALPHA_SHIFT) and MASK
override inline val redf get() = Color.toFloat(red) override inline val redf get() = Color.toFloat(red)
@ -46,8 +46,8 @@ value class RGBAColor(val argb: Int) : Color, TextFormattable {
override inline val bluef get() = Color.toFloat(blue) override inline val bluef get() = Color.toFloat(blue)
inline val alphaf get() = Color.toFloat(alpha) inline val alphaf get() = Color.toFloat(alpha)
override inline val rgb get() = argb and ((MASK shl RED_SHIFT) or (MASK shl GREEN_SHIFT) or (MASK shl BLUE_SHIFT)) override inline val rgb get() = rgba ushr BITS
inline val rgba get() = (argb shl BITS) or alpha inline val argb get() = (rgba ushr BITS) or (rgba shl 3 * BITS)
inline operator fun plus(value: Int) = plus(RGBAColor(value, value, value, value)) inline operator fun plus(value: Int) = plus(RGBAColor(value, value, value, value))
@ -93,22 +93,21 @@ value class RGBAColor(val argb: Int) : Color, TextFormattable {
companion object { companion object {
const val ALPHA_SHIFT = 3 * BITS const val RED_SHIFT = 3 * BITS
const val RED_SHIFT = 2 * BITS const val GREEN_SHIFT = 2 * BITS
const val GREEN_SHIFT = 1 * BITS const val BLUE_SHIFT = 1 * BITS
const val BLUE_SHIFT = 0 * BITS const val ALPHA_SHIFT = 0 * BITS
fun Vec4.color() = RGBAColor(r, g, b, a) fun Vec4.color() = RGBAColor(r, g, b, a)
inline fun Int.rgba() = RGBAColor(this shr BITS or (this and BITS shl (BITS * 3))) inline fun Int.rgba() = RGBAColor(this)
inline fun Int.argb() = RGBColor(this)
fun String.rgba(): RGBAColor { fun String.rgba(): RGBAColor {
val string = this.removePrefix("#") val string = this.removePrefix("#")
val int = Integer.parseUnsignedInt(string, 16) val int = Integer.parseUnsignedInt(string, 16)
return when (string.length) { return when (string.length) {
6 -> RGBAColor(int or (MASK shl ALPHA_SHIFT)) 6 -> ((int shl BITS) or (MASK shl ALPHA_SHIFT)).rgba()
8 -> int.rgba() 8 -> int.rgba()
else -> throw IllegalArgumentException("Invalid color string: $this") else -> throw IllegalArgumentException("Invalid color string: $this")
} }

View File

@ -13,6 +13,8 @@
package de.bixilon.minosoft.data.text.formatting.color package de.bixilon.minosoft.data.text.formatting.color
import de.bixilon.minosoft.data.text.formatting.color.RGBColor.Companion.rgb
@JvmInline @JvmInline
value class RGBArray(val array: IntArray) { value class RGBArray(val array: IntArray) {
@ -21,8 +23,8 @@ value class RGBArray(val array: IntArray) {
inline val size get() = array.size inline val size get() = array.size
inline operator fun get(index: Int) = RGBColor(array[index]) inline operator fun get(index: Int) = array[index].rgb()
inline fun getOrNull(index: Int) = array.getOrNull(index)?.let { RGBColor(it) } inline fun getOrNull(index: Int) = array.getOrNull(index)?.rgb()
inline operator fun set(index: Int, value: RGBColor) { inline operator fun set(index: Int, value: RGBColor) {
array[index] = value.rgb array[index] = value.rgb
} }

View File

@ -20,11 +20,12 @@ import de.bixilon.minosoft.data.text.formatting.color.Color.Companion.BITS
import de.bixilon.minosoft.data.text.formatting.color.Color.Companion.MASK import de.bixilon.minosoft.data.text.formatting.color.Color.Companion.MASK
import de.bixilon.minosoft.data.text.formatting.color.Color.Companion.MAX import de.bixilon.minosoft.data.text.formatting.color.Color.Companion.MAX
import de.bixilon.minosoft.data.text.formatting.color.Color.Companion.TIMES import de.bixilon.minosoft.data.text.formatting.color.Color.Companion.TIMES
import de.bixilon.minosoft.data.text.formatting.color.Color.Companion.clamp
@JvmInline @JvmInline
value class RGBColor(override val rgb: Int) : Color, TextFormattable { value class RGBColor(override val rgb: Int) : Color, TextFormattable {
constructor(red: Int, green: Int, blue: Int) : this(((red and MASK) shl RED_SHIFT) or ((green and MASK) shl GREEN_SHIFT) or ((blue and MASK) shl BLUE_SHIFT)) constructor(red: Int, green: Int, blue: Int) : this(((red.clamp() and MASK) shl RED_SHIFT) or ((green.clamp() and MASK) shl GREEN_SHIFT) or ((blue.clamp() and MASK) shl BLUE_SHIFT))
constructor(red: Float, green: Float, blue: Float) : this(Color.fromFloat(red), Color.fromFloat(green), Color.fromFloat(blue)) constructor(red: Float, green: Float, blue: Float) : this(Color.fromFloat(red), Color.fromFloat(green), Color.fromFloat(blue))
constructor(rgb: Vec3) : this(rgb.r, rgb.g, rgb.b) constructor(rgb: Vec3) : this(rgb.r, rgb.g, rgb.b)
@ -87,7 +88,7 @@ value class RGBColor(override val rgb: Int) : Color, TextFormattable {
val int = Integer.parseUnsignedInt(string, 16) val int = Integer.parseUnsignedInt(string, 16)
val rgb = when (string.length) { val rgb = when (string.length) {
6 -> int 6 -> int
8 -> int ushr BITS // rgba 8 -> int ushr BITS
else -> throw IllegalArgumentException("Invalid color string: $this") else -> throw IllegalArgumentException("Invalid color string: $this")
} }
return RGBColor(rgb) return RGBColor(rgb)

View File

@ -76,6 +76,7 @@ interface NativeShader {
is Vec3 -> setVec3(uniformName, data) is Vec3 -> setVec3(uniformName, data)
is Vec2 -> setVec2(uniformName, data) is Vec2 -> setVec2(uniformName, data)
is RGBColor -> setRGBColor(uniformName, data) is RGBColor -> setRGBColor(uniformName, data)
is RGBAColor -> setRGBAColor(uniformName, data)
is UniformBuffer -> setUniformBuffer(uniformName, data) is UniformBuffer -> setUniformBuffer(uniformName, data)
// ToDo: PNGTexture // ToDo: PNGTexture
is Boolean -> setBoolean(uniformName, data) is Boolean -> setBoolean(uniformName, data)

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * Minosoft
* Copyright (C) 2020-2024 Moritz Zwerger * Copyright (C) 2020-2025 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 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.
* *
@ -105,7 +105,7 @@ class SkinManager(private val textures: TextureManager) {
} }
private fun TextureBuffer.isReallyWide(): Boolean { private fun TextureBuffer.isReallyWide(): Boolean {
// check if pixel at arm (wide, not slim) is black. If not, its a wide skinn // check if pixel at arm (wide, not slim) is black. If not, its a wide skin
if (!this[50, 16].isBlack()) return true // left arm slim if (!this[50, 16].isBlack()) return true // left arm slim
if (!this[42, 48].isBlack()) return true // right arm slim if (!this[42, 48].isBlack()) return true // right arm slim

View File

@ -75,9 +75,9 @@ object TextureUtil {
var rgba = RGBAColor(image.raster.getSample(x, y, samples[0]), image.raster.getSample(x, y, samples[1]), image.raster.getSample(x, y, samples[2])) var rgba = RGBAColor(image.raster.getSample(x, y, samples[0]), image.raster.getSample(x, y, samples[1]), image.raster.getSample(x, y, samples[2]))
if (samples.size > 3) { if (samples.size > 3) {
rgba = rgba.with(image.raster.getSample(x, y, samples[3])) rgba = rgba.with(alpha = image.raster.getSample(x, y, samples[3]))
} else { } else {
rgba = rgba.with(image.alphaRaster?.getSample(x, y, 0) ?: 0xFF) rgba = rgba.with(alpha = image.alphaRaster?.getSample(x, y, 0) ?: 0xFF)
} }
buffer.setRGBA(x, y, rgba) buffer.setRGBA(x, y, rgba)
} }

View File

@ -0,0 +1,40 @@
/*
* Minosoft
* Copyright (C) 2020-2025 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.data.text.formatting.color
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class ChatColorsTest {
@Test
fun `get yellow`() {
assertEquals(ChatColors["yellow"], RGBAColor(255, 255, 85))
}
@Test
fun `get name of gold`() {
assertEquals(ChatColors.NAME_MAP.getKey(RGBAColor(255, 170, 0)), "gold")
}
@Test
fun `get a`() {
assertEquals(ChatColors.VALUES.getOrNull(Character.digit('a', 16)), RGBAColor(85, 255, 85))
}
@Test
fun `get char of red`() {
assertEquals(ChatColors.getChar(RGBAColor(170, 0, 0)), "4")
}
}

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.data.text.formatting.color package de.bixilon.minosoft.data.text.formatting.color
import de.bixilon.minosoft.data.text.formatting.color.RGBAColor.Companion.rgba
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -85,5 +86,16 @@ class RGBAColorTest {
assertEquals(color.rgb(), RGBColor(0x12, 0x34, 0x56)) assertEquals(color.rgb(), RGBColor(0x12, 0x34, 0x56))
} }
// TODO: operations (plus, times), conversion, toString, Int::rgb, Int::rgba, mix @Test
fun `int to rgba`() {
assertEquals(0x12345678.rgba(), RGBAColor(0x12, 0x34, 0x56, 0x78))
}
@Test
fun `string to rgba`() {
assertEquals("#123456".rgba(), RGBAColor(0x12, 0x34, 0x56))
assertEquals("#12345678".rgba(), RGBAColor(0x12, 0x34, 0x56, 0x78))
}
// TODO: operations (plus, times), toString, mix
} }

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.data.text.formatting.color package de.bixilon.minosoft.data.text.formatting.color
import de.bixilon.minosoft.data.text.formatting.color.RGBColor.Companion.rgb
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -73,5 +74,16 @@ class RGBColorTest {
assertEquals(color.rgba(), RGBAColor(0x12, 0x34, 0x56, 0xFF)) assertEquals(color.rgba(), RGBAColor(0x12, 0x34, 0x56, 0xFF))
} }
// TODO: operations (plus, times), conversion, toString, Int::rgb, Int::rgba, mix @Test
fun `int to rgb`() {
assertEquals(0x123456.rgb(), RGBColor(0x12, 0x34, 0x56))
}
@Test
fun `string to rgb`() {
assertEquals("#123456".rgb(), RGBColor(0x12, 0x34, 0x56))
assertEquals("#12345678".rgb(), RGBColor(0x12, 0x34, 0x56))
}
// TODO: operations (plus, times), toString, Int::rgb, Int::rgba, mix
} }