diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/PocketItemRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/PocketItemRenderer.java index 676f13556..2f4337aee 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/render/PocketItemRenderer.java +++ b/projects/common/src/client/java/dan200/computercraft/client/render/PocketItemRenderer.java @@ -13,6 +13,7 @@ import dan200.computercraft.core.util.Colour; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.pocket.items.PocketComputerItem; +import dan200.computercraft.shared.util.ARGB32; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.world.item.ItemStack; import org.joml.Matrix4f; @@ -92,16 +93,11 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer { } private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) { - var r = (byte) ((colour >>> 16) & 0xFF); - var g = (byte) ((colour >>> 8) & 0xFF); - var b = (byte) (colour & 0xFF); - var c = new byte[]{ r, g, b, (byte) 255 }; - var buffer = render.getBuffer(RenderTypes.TERMINAL); FixedWidthFontRenderer.drawQuad( FixedWidthFontRenderer.toVertexConsumer(transform, buffer), width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT, - c, RenderTypes.FULL_BRIGHT_LIGHTMAP + ARGB32.opaque(colour), RenderTypes.FULL_BRIGHT_LIGHTMAP ); } } diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java index 2c60efb58..195d02857 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java +++ b/projects/common/src/client/java/dan200/computercraft/client/render/text/DirectFixedWidthFontRenderer.java @@ -13,6 +13,7 @@ import dan200.computercraft.core.terminal.Palette; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.util.Colour; +import net.minecraft.util.FastColor; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; @@ -41,7 +42,7 @@ public final class DirectFixedWidthFontRenderer { private DirectFixedWidthFontRenderer() { } - private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour) { + private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour) { // Short circuit to avoid the common case - the texture should be blank here after all. if (index == '\0' || index == ' ') return; @@ -158,8 +159,8 @@ public final class DirectFixedWidthFontRenderer { return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2; } - private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) { - buffer.quad(x1, y1, x2, y2, z, rgba, u1, v1, u2, v2); + private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) { + buffer.quad(x1, y1, x2, y2, z, colour, u1, v1, u2, v2); } public interface QuadEmitter { @@ -167,7 +168,7 @@ public final class DirectFixedWidthFontRenderer { ByteBuffer buffer(); - void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2); + void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2); } public record ByteBufferEmitter(ByteBuffer buffer) implements QuadEmitter { @@ -177,12 +178,12 @@ public final class DirectFixedWidthFontRenderer { } @Override - public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) { - DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, rgba, u1, v1, u2, v2); + public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) { + DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, colour, u1, v1, u2, v2); } } - static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) { + static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) { // Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the // underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write. // This provides significant performance gains, at the cost of well, using Unsafe. @@ -196,16 +197,19 @@ public final class DirectFixedWidthFontRenderer { if (position < 0 || 112 > buffer.limit() - position) throw new IndexOutOfBoundsException(); // Require the pointer to be aligned to a 32-bit boundary. if ((addr & 3) != 0) throw new IllegalStateException("Memory is not aligned"); - // Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances. - if (rgba.length != 4) throw new IllegalStateException(); + + var red = (byte) FastColor.ARGB32.red(colour); + var green = (byte) FastColor.ARGB32.green(colour); + var blue = (byte) FastColor.ARGB32.blue(colour); + var alpha = (byte) FastColor.ARGB32.alpha(colour); memPutFloat(addr + 0, x1); memPutFloat(addr + 4, y1); memPutFloat(addr + 8, z); - memPutByte(addr + 12, rgba[0]); - memPutByte(addr + 13, rgba[1]); - memPutByte(addr + 14, rgba[2]); - memPutByte(addr + 15, (byte) 255); + memPutByte(addr + 12, red); + memPutByte(addr + 13, green); + memPutByte(addr + 14, blue); + memPutByte(addr + 15, alpha); memPutFloat(addr + 16, u1); memPutFloat(addr + 20, v1); memPutShort(addr + 24, (short) 0xF0); @@ -214,10 +218,10 @@ public final class DirectFixedWidthFontRenderer { memPutFloat(addr + 28, x1); memPutFloat(addr + 32, y2); memPutFloat(addr + 36, z); - memPutByte(addr + 40, rgba[0]); - memPutByte(addr + 41, rgba[1]); - memPutByte(addr + 42, rgba[2]); - memPutByte(addr + 43, (byte) 255); + memPutByte(addr + 40, red); + memPutByte(addr + 41, green); + memPutByte(addr + 42, blue); + memPutByte(addr + 43, alpha); memPutFloat(addr + 44, u1); memPutFloat(addr + 48, v2); memPutShort(addr + 52, (short) 0xF0); @@ -226,10 +230,10 @@ public final class DirectFixedWidthFontRenderer { memPutFloat(addr + 56, x2); memPutFloat(addr + 60, y2); memPutFloat(addr + 64, z); - memPutByte(addr + 68, rgba[0]); - memPutByte(addr + 69, rgba[1]); - memPutByte(addr + 70, rgba[2]); - memPutByte(addr + 71, (byte) 255); + memPutByte(addr + 68, red); + memPutByte(addr + 69, green); + memPutByte(addr + 70, blue); + memPutByte(addr + 71, alpha); memPutFloat(addr + 72, u2); memPutFloat(addr + 76, v2); memPutShort(addr + 80, (short) 0xF0); @@ -238,10 +242,10 @@ public final class DirectFixedWidthFontRenderer { memPutFloat(addr + 84, x2); memPutFloat(addr + 88, y1); memPutFloat(addr + 92, z); - memPutByte(addr + 96, rgba[0]); - memPutByte(addr + 97, rgba[1]); - memPutByte(addr + 98, rgba[2]); - memPutByte(addr + 99, (byte) 255); + memPutByte(addr + 96, red); + memPutByte(addr + 97, green); + memPutByte(addr + 98, blue); + memPutByte(addr + 99, alpha); memPutFloat(addr + 100, u2); memPutFloat(addr + 104, v1); memPutShort(addr + 108, (short) 0xF0); diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java index 1111d06d3..d2ecfc05d 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java +++ b/projects/common/src/client/java/dan200/computercraft/client/render/text/FixedWidthFontRenderer.java @@ -12,6 +12,7 @@ import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.util.Colour; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.FastColor; import org.joml.Matrix4f; import org.joml.Vector3f; @@ -41,7 +42,7 @@ public final class FixedWidthFontRenderer { static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH; static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; - private static final byte[] BLACK = new byte[]{ byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), (byte) 255 }; + private static final int BLACK = FastColor.ARGB32.color(255, byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR())); private static final float Z_OFFSET = 1e-3f; private FixedWidthFontRenderer() { @@ -59,7 +60,7 @@ public final class FixedWidthFontRenderer { return 15 - Terminal.getColour(c, def); } - private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour, int light) { + private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour, int light) { // Short circuit to avoid the common case - the texture should be blank here after all. if (index == '\0' || index == ' ') return; @@ -75,7 +76,7 @@ public final class FixedWidthFontRenderer { ); } - public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light) { + public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, int colour, int light) { quad(emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light); } @@ -216,10 +217,10 @@ public final class FixedWidthFontRenderer { return new QuadEmitter(transform.last().pose(), consumer); } - private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light) { + private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2, int light) { var poseMatrix = c.poseMatrix(); var consumer = c.consumer(); - byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3]; + int r = FastColor.ARGB32.red(colour), g = FastColor.ARGB32.green(colour), b = FastColor.ARGB32.blue(colour), a = FastColor.ARGB32.alpha(colour); consumer.vertex(poseMatrix, x1, y1, z).color(r, g, b, a).uv(u1, v1).uv2(light).endVertex(); consumer.vertex(poseMatrix, x1, y2, z).color(r, g, b, a).uv(u1, v2).uv2(light).endVertex(); diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/ARGB32.java b/projects/common/src/main/java/dan200/computercraft/shared/util/ARGB32.java new file mode 100644 index 000000000..0eab49649 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/util/ARGB32.java @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.util; + +import net.minecraft.util.FastColor; + +/** + * Utilities for working with 32-bit ARGB colours. + * + * @see FastColor.ARGB32 + */ +public final class ARGB32 { + private ARGB32() { + } + + /** + * Set the alpha channel to be fully opaque. + * + * @param colour The colour to make opaque. + * @return The fully-opaque colour + */ + public static int opaque(int colour) { + return 0xFF000000 | colour; + } + + /** + * Convert an ARGB32 colour to a {@linkplain FastColor.ABGR32 ABGR32} one. + * + * @param colour The colour to convert. + * @return The converted colour. + */ + public static int toABGR32(int colour) { + // Swap B and R components, converting ARGB32 to ABGR32. + return colour & 0xFF00FF00 | (colour & 0xFF0000) >> 16 | (colour & 0xFF) << 16; + } +} diff --git a/projects/core/src/main/java/dan200/computercraft/core/terminal/Palette.java b/projects/core/src/main/java/dan200/computercraft/core/terminal/Palette.java index 0dea4b521..50d75db97 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/terminal/Palette.java +++ b/projects/core/src/main/java/dan200/computercraft/core/terminal/Palette.java @@ -12,15 +12,13 @@ public class Palette { private final boolean colour; private final double[][] colours = new double[PALETTE_SIZE][3]; - private final byte[][] byteColours = new byte[PALETTE_SIZE][4]; + private final int[] byteColours = new int[PALETTE_SIZE]; public static final Palette DEFAULT = new Palette(true); public Palette(boolean colour) { this.colour = colour; resetColours(); - - for (var i = 0; i < PALETTE_SIZE; i++) byteColours[i][3] = (byte) 255; } public void setColour(int i, double r, double g, double b) { @@ -30,15 +28,17 @@ public class Palette { colours[i][2] = b; if (colour) { - byteColours[i][0] = (byte) (int) (r * 255); - byteColours[i][1] = (byte) (int) (g * 255); - byteColours[i][2] = (byte) (int) (b * 255); + byteColours[i] = packColour((int) (r * 255), (int) (g * 255), (int) (b * 255)); } else { - var grey = (byte) (int) ((r + g + b) / 3 * 255); - byteColours[i][0] = byteColours[i][1] = byteColours[i][2] = grey; + var grey = (int) ((r + g + b) / 3 * 255); + byteColours[i] = packColour(grey, grey, grey); } } + private static int packColour(int r, int g, int b) { + return 255 << 24 | (r & 255) << 16 | (g & 255) << 8 | b & 255; + } + public void setColour(int i, Colour colour) { setColour(i, colour.getR(), colour.getG(), colour.getB()); } @@ -48,26 +48,20 @@ public class Palette { } /** - * Get the colour as a set of RGB values suitable for rendering. Colours are automatically converted to greyscale + * Get the colour as a set of ARGB values suitable for rendering. Colours are automatically converted to greyscale * when using a black and white palette. *
- * This returns a byte array, suitable for being used directly by our terminal vertex format. + * This returns a packed 32-bit ARGB colour. * * @param i The colour index. - * @return The number as a tuple of bytes. + * @return The actual RGB colour. */ - public byte[] getRenderColours(int i) { + public int getRenderColours(int i) { return byteColours[i]; } - public void resetColour(int i) { - if (i >= 0 && i < PALETTE_SIZE) setColour(i, Colour.VALUES[i]); - } - public void resetColours() { - for (var i = 0; i < Colour.VALUES.length; i++) { - resetColour(i); - } + for (var i = 0; i < Colour.VALUES.length; i++) setColour(i, Colour.VALUES[i]); } public static int encodeRGB8(double[] rgb) { diff --git a/projects/core/src/main/java/dan200/computercraft/core/util/Colour.java b/projects/core/src/main/java/dan200/computercraft/core/util/Colour.java index fded29173..3c7236e41 100644 --- a/projects/core/src/main/java/dan200/computercraft/core/util/Colour.java +++ b/projects/core/src/main/java/dan200/computercraft/core/util/Colour.java @@ -4,8 +4,6 @@ package dan200.computercraft.core.util; -import javax.annotation.Nullable; - public enum Colour { BLACK(0x111111), RED(0xcc4c4c), @@ -30,15 +28,6 @@ public enum Colour { return Colour.VALUES[colour]; } - @Nullable - public static Colour fromHex(int colour) { - for (var entry : VALUES) { - if (entry.getHex() == colour) return entry; - } - - return null; - } - private final int hex; private final float red, green, blue; diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java b/projects/fabric/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java index 6ac77e078..c4a8c7663 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java @@ -8,6 +8,7 @@ import com.google.auto.service.AutoService; import com.mojang.blaze3d.vertex.VertexFormat; import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; +import dan200.computercraft.shared.util.ARGB32; import net.fabricmc.loader.api.FabricLoader; import net.irisshaders.iris.api.v0.IrisApi; import net.irisshaders.iris.api.v0.IrisTextVertexSink; @@ -54,12 +55,8 @@ public class IrisShaderMod implements ShaderMod.Provider { } @Override - public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) { - sink.quad(x1, y1, x2, y2, z, pack(rgba[0], rgba[1], rgba[2], rgba[3]), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP); - } - - private static int pack(int r, int g, int b, int a) { - return (a & 255) << 24 | (b & 255) << 16 | (g & 255) << 8 | r & 255; + public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) { + sink.quad(x1, y1, x2, y2, z, ARGB32.toABGR32(colour), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP); } } } diff --git a/projects/forge/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java b/projects/forge/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java index 948a70589..3eb2e0065 100644 --- a/projects/forge/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java +++ b/projects/forge/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java @@ -8,6 +8,7 @@ import com.google.auto.service.AutoService; import com.mojang.blaze3d.vertex.VertexFormat; import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; +import dan200.computercraft.shared.util.ARGB32; import net.irisshaders.iris.api.v0.IrisApi; import net.irisshaders.iris.api.v0.IrisTextVertexSink; import net.minecraftforge.fml.ModList; @@ -57,12 +58,8 @@ public class IrisShaderMod implements ShaderMod.Provider { } @Override - public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) { - sink.quad(x1, y1, x2, y2, z, pack(rgba[0], rgba[1], rgba[2], rgba[3]), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP); - } - - private static int pack(int r, int g, int b, int a) { - return (a & 255) << 24 | (b & 255) << 16 | (g & 255) << 8 | r & 255; + public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) { + sink.quad(x1, y1, x2, y2, z, ARGB32.toABGR32(colour), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP); } } }