Skip to content

Commit

Permalink
Unify color mixing/swizzling utilities
Browse files Browse the repository at this point in the history
The Fabric integration code was re-implementing a lot
of the utilities that already exist in Sodium unnecessarily.

Also, improve the documentation so that ABGR and RGBA are
not used interchangeably.
  • Loading branch information
jellysquid3 committed Nov 5, 2024
1 parent 975e278 commit 16f612d
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 88 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package net.caffeinemc.mods.sodium.api.util;

import java.nio.ByteOrder;

/**
* Provides some utilities for packing and unpacking color components from packed integer colors in ABGR format, which
* is used by OpenGL for color vectors.
Expand All @@ -19,7 +21,6 @@ public class ColorABGR implements ColorU8 {
private static final int BLUE_COMPONENT_MASK = COMPONENT_MASK << BLUE_COMPONENT_OFFSET;
private static final int ALPHA_COMPONENT_MASK = COMPONENT_MASK << ALPHA_COMPONENT_OFFSET;


/**
* Packs the specified color components into ABGR format. The alpha component is fully opaque.
* @param r The red component of the color
Expand Down Expand Up @@ -106,13 +107,49 @@ public static int unpackAlpha(int color) {
}

/**
* Darkens the RGB components of the color by multiplying them with the provided factor. The alpha component is
* not modified.
* Multiplies the RGB components of the color with the provided factor. The alpha component is not modified.
*
* @param color The packed 32-bit ABGR color to be multiplied
* @param factor The darkening factor (in the range of 0..255) to multiply with
*/
public static int darken(int color, int factor) {
public static int mulRGB(int color, int factor) {
return (ColorMixer.mul(color, factor) & ~ALPHA_COMPONENT_MASK) | (color & ALPHA_COMPONENT_MASK);
}

/**
* See {@link #mulRGB(int, int)}. This function is identical, but it accepts a float in [0.0, 1.0] instead, which
* is then mapped to [0, 255].
*
* @param color The packed 32-bit ABGR color to be multiplied
* @param factor The darkening factor (in the range of 0.0..1.0) to multiply with
*/
public static int mulRGB(int color, float factor) {
return mulRGB(color, ColorU8.normalizedFloatToByte(factor));
}

private static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;

/**
* Shuffles the ordering of the ABGR color so that it can then be written out to memory in the platform's native
* byte ordering. This should be used when writing the packed color to memory (in native-order) as a 32-bit word.
*/
public static int fromNativeByteOrder(int color) {
if (BIG_ENDIAN) {
return Integer.reverseBytes(color);
} else {
return color;
}
}

/**
* Shuffles the ordering of the ABGR color from the platform's native byte ordering. This should be used when reading
* the packed color from memory (in native-order) as a 32-bit word.
*/
public static int toNativeByteOrder(int color) {
if (BIG_ENDIAN) {
return Integer.reverseBytes(color);
} else {
return color;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public class ColorARGB implements ColorU8 {
private static final int GREEN_COMPONENT_OFFSET = 8;
private static final int BLUE_COMPONENT_OFFSET = 0;

private static final int RED_COMPONENT_MASK = COMPONENT_MASK << RED_COMPONENT_OFFSET;
private static final int GREEN_COMPONENT_MASK = COMPONENT_MASK << GREEN_COMPONENT_OFFSET;
private static final int BLUE_COMPONENT_MASK = COMPONENT_MASK << BLUE_COMPONENT_OFFSET;
private static final int ALPHA_COMPONENT_MASK = COMPONENT_MASK << ALPHA_COMPONENT_OFFSET;

/**
* Packs the specified color components into big-endian format for consumption by OpenGL.
* @param r The red component of the color
Expand Down Expand Up @@ -73,23 +78,40 @@ public static int unpackBlue(int color) {
}

/**
* Re-packs the ARGB color into an ABGR color with the specified alpha component.
* Swizzles from ARGB format into ABGR format, replacing the alpha component with {@param alpha}.
*/
public static int toABGR(int color, float alpha) {
return Integer.reverseBytes(color << 8 | ColorU8.normalizedFloatToByte(alpha));
public static int toABGR(int color, int alpha) {
// shl(ARGB, 8) -> RGB0
// or(RGB0, 000A) -> RGBA
return Integer.reverseBytes(color << 8 | alpha);
}

/**
* Re-packs the ARGB color into a aBGR color with the specified alpha component.
* Swizzles from ARGB format into ABGR format, replacing the alpha component with {@param alpha}. The alpha
* component is mapped from [0.0, 1.0] to [0, 255].
*/
public static int toABGR(int color, int alpha) {
return Integer.reverseBytes(color << 8 | alpha);
public static int toABGR(int color, float alpha) {
return toABGR(color, ColorU8.normalizedFloatToByte(alpha));
}

/**
* Swizzles from ARGB format into ABGR format.
*/
public static int toABGR(int color) {
// rotateLeft(ARGB, 8) -> RGBA
// reverseBytes(RGBA) -> ABGR
return Integer.reverseBytes(Integer.rotateLeft(color, 8));
}

/**
* Swizzles from ABGR format into ARGB format.
*/
public static int fromABGR(int color) {
// reverseBytes(ABGR) -> RGBA
// rotateRight(RGBA, 8) -> ARGB
return Integer.rotateRight(Integer.reverseBytes(color), 8);
}

/**
* Packs the specified color components into ARGB format.
* @param rgb The red/green/blue component of the color
Expand All @@ -98,4 +120,25 @@ public static int toABGR(int color) {
public static int withAlpha(int rgb, int alpha) {
return (alpha << ALPHA_COMPONENT_OFFSET) | (rgb & ~(COMPONENT_MASK << ALPHA_COMPONENT_OFFSET));
}

/**
* Multiplies the RGB components of the color with the provided factor. The alpha component is not modified.
*
* @param color The packed 32-bit ABGR color to be multiplied
* @param factor The darkening factor (in the range of 0..255) to multiply with
*/
public static int mulRGB(int color, int factor) {
return (ColorMixer.mul(color, factor) & ~ALPHA_COMPONENT_MASK) | (color & ALPHA_COMPONENT_MASK);
}

/**
* See {@link #mulRGB(int, int)}. This function is identical, but it accepts a float in [0.0, 1.0] instead, which
* is then mapped to [0, 255].
*
* @param color The packed 32-bit ABGR color to be multiplied
* @param factor The darkening factor (in the range of 0.0..1.0) to multiply with
*/
public static int mulRGB(int color, float factor) {
return mulRGB(color, ColorU8.normalizedFloatToByte(factor));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,27 @@ public static int mix(int start, int end, float weight) {
}

/**
* <p>Multiplies each 8-bit component of the packed 32-bit color.</p>
* <p>Multiplies the packed 8-bit values component-wise to produce 16-bit intermediaries, and then round to the
* nearest 8-bit representation (similar to floating-point.)</p>
*
* @param color0 The first color to multiply
* @param color1 The second color to multiply
* @return The product of the two colors
*/
public static int mulComponentWise(int color0, int color1) {
int comp0 = ((((color0 >>> 0) & 0xFF) * ((color1 >>> 0) & 0xFF)) + 0xFF) >>> 8;
int comp1 = ((((color0 >>> 8) & 0xFF) * ((color1 >>> 8) & 0xFF)) + 0xFF) >>> 8;
int comp2 = ((((color0 >>> 16) & 0xFF) * ((color1 >>> 16) & 0xFF)) + 0xFF) >>> 8;
int comp3 = ((((color0 >>> 24) & 0xFF) * ((color1 >>> 24) & 0xFF)) + 0xFF) >>> 8;

return (comp0 << 0) | (comp1 << 8) | (comp2 << 16) | (comp3 << 24);
}

/**
* <p>Multiplies each 8-bit component against the factor to produce 16-bit intermediaries, and then round to the
* nearest 8-bit representation (similar to floating-point.)</p>
*
* <p>The results are undefined if {@param factor} is not within the interval [0, 255].</p>
*
* @param color The packed color values
* @param factor The multiplication factor (in 0..255 range)
Expand All @@ -50,4 +70,14 @@ public static int mul(int color, int factor) {

return (int) result;
}

/**
* See {@link #mul(int, int)}, which this function is identical to, except that it takes a floating point value in
* the interval of [0.0, 1.0] and maps it to [0, 255].
*
* <p>The results are undefined if {@param factor} is not within the interval [0.0, 1.0].</p>
*/
public static int mul(int color, float factor) {
return mul(color, ColorU8.normalizedFloatToByte(factor));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import net.caffeinemc.mods.sodium.api.util.ColorABGR;
import net.caffeinemc.mods.sodium.api.util.ColorARGB;
import net.caffeinemc.mods.sodium.api.util.ColorMixer;
import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds;
import net.caffeinemc.mods.sodium.client.model.color.ColorProvider;
import net.caffeinemc.mods.sodium.client.model.color.ColorProviderRegistry;
Expand All @@ -20,7 +21,6 @@
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.TranslucentGeometryCollector;
import net.caffeinemc.mods.sodium.client.render.chunk.vertex.builder.ChunkMeshBufferBuilder;
import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder;
import net.caffeinemc.mods.sodium.client.render.frapi.helper.ColorHelper;
import net.caffeinemc.mods.sodium.client.render.frapi.mesh.MutableQuadViewImpl;
import net.caffeinemc.mods.sodium.client.render.frapi.render.AbstractBlockRenderContext;
import net.caffeinemc.mods.sodium.client.render.texture.SpriteFinderCache;
Expand Down Expand Up @@ -149,7 +149,7 @@ private void colorizeQuad(MutableQuadViewImpl quad, int colorIndex) {
colorProvider.getColors(this.slice, this.pos, this.scratchPos, this.state, quad, vertexColors);

for (int i = 0; i < 4; i++) {
quad.color(i, ColorHelper.multiplyColor(vertexColors[i], quad.color(i)));
quad.color(i, ColorMixer.mulComponentWise(vertexColors[i], quad.color(i)));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.impl;

import net.caffeinemc.mods.sodium.api.util.ColorARGB;
import net.caffeinemc.mods.sodium.client.gl.attribute.GlVertexFormat;
import net.caffeinemc.mods.sodium.client.render.chunk.shader.ChunkShaderBindingPoints;
import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder;
import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexType;
import net.caffeinemc.mods.sodium.client.render.frapi.helper.ColorHelper;
import net.minecraft.util.Mth;
import org.lwjgl.system.MemoryUtil;

Expand Down Expand Up @@ -58,7 +58,7 @@ public ChunkVertexEncoder getEncoder() {

MemoryUtil.memPutInt(ptr + 0L, packPositionHi(x, y, z));
MemoryUtil.memPutInt(ptr + 4L, packPositionLo(x, y, z));
MemoryUtil.memPutInt(ptr + 8L, ColorHelper.multiplyRGB(vertex.color, vertex.ao));
MemoryUtil.memPutInt(ptr + 8L, ColorARGB.mulRGB(vertex.color, vertex.ao));
MemoryUtil.memPutInt(ptr + 12L, packTexture(u, v));
MemoryUtil.memPutInt(ptr + 16L, packLightAndData(light, materialBits, section));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,52 +16,18 @@

package net.caffeinemc.mods.sodium.client.render.frapi.helper;

import java.nio.ByteOrder;
import net.caffeinemc.mods.sodium.api.util.ColorABGR;
import net.caffeinemc.mods.sodium.api.util.ColorARGB;

/**
* Static routines of general utility for renderer implementations.
* Renderers are not required to use these helpers, but they were
* designed to be usable without the default renderer.
*/
public abstract class ColorHelper {
private ColorHelper() { }

private static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;

/** Component-wise multiply. Components need to be in same order in both inputs! */
public static int multiplyColor(int color1, int color2) {
if (color1 == -1) {
return color2;
} else if (color2 == -1) {
return color1;
}

final int alpha = ((color1 >>> 24) & 0xFF) * ((color2 >>> 24) & 0xFF) / 0xFF;
final int red = ((color1 >>> 16) & 0xFF) * ((color2 >>> 16) & 0xFF) / 0xFF;
final int green = ((color1 >>> 8) & 0xFF) * ((color2 >>> 8) & 0xFF) / 0xFF;
final int blue = (color1 & 0xFF) * (color2 & 0xFF) / 0xFF;

return (alpha << 24) | (red << 16) | (green << 8) | blue;
}

/** Multiplies three lowest components by shade. High byte (usually alpha) unchanged. */
public static int multiplyRGB(int color, float shade) {
final int alpha = ((color >>> 24) & 0xFF);
final int red = (int) (((color >>> 16) & 0xFF) * shade);
final int green = (int) (((color >>> 8) & 0xFF) * shade);
final int blue = (int) ((color & 0xFF) * shade);

return (alpha << 24) | (red << 16) | (green << 8) | blue;
}

/**
* Component-wise max.
*/
public static int maxBrightness(int b0, int b1) {
if (b0 == 0) return b1;
if (b1 == 0) return b0;

return Math.max(b0 & 0xFFFF, b1 & 0xFFFF) | Math.max(b0 & 0xFFFF0000, b1 & 0xFFFF0000);
return Math.max(b0 & 0x0000FFFF, b1 & 0x0000FFFF) |
Math.max(b0 & 0xFFFF0000, b1 & 0xFFFF0000);
}

/*
Expand All @@ -81,36 +47,16 @@ Vanilla color format (big endian): RGBA (0xRRGGBBAA)
*/

/**
* Converts from ARGB color to ABGR color if little endian or RGBA color if big endian.
* Converts from ARGB color to ABGR color. The result will be in the platform's native byte order.
*/
public static int toVanillaColor(int color) {
if (color == -1) {
return -1;
}

if (BIG_ENDIAN) {
// ARGB to RGBA
return ((color & 0x00FFFFFF) << 8) | ((color & 0xFF000000) >>> 24);
} else {
// ARGB to ABGR
return (color & 0xFF00FF00) | ((color & 0x00FF0000) >>> 16) | ((color & 0x000000FF) << 16);
}
return ColorABGR.toNativeByteOrder(ColorARGB.toABGR(color));
}

/**
* Converts to ARGB color from ABGR color if little endian or RGBA color if big endian.
* Converts from ABGR color to ARGB color. The input should be in the platform's native byte order.
*/
public static int fromVanillaColor(int color) {
if (color == -1) {
return -1;
}

if (BIG_ENDIAN) {
// RGBA to ARGB
return ((color & 0xFFFFFF00) >>> 8) | ((color & 0x000000FF) << 24);
} else {
// ABGR to ARGB
return (color & 0xFF00FF00) | ((color & 0x00FF0000) >>> 16) | ((color & 0x000000FF) << 16);
}
return ColorARGB.fromABGR(ColorABGR.fromNativeByteOrder(color));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.MatrixUtil;
import net.caffeinemc.mods.sodium.api.util.ColorMixer;
import net.caffeinemc.mods.sodium.client.render.frapi.helper.ColorHelper;
import net.caffeinemc.mods.sodium.client.render.frapi.mesh.EncodingFormat;
import net.caffeinemc.mods.sodium.client.render.frapi.mesh.MutableQuadViewImpl;
Expand All @@ -31,15 +32,12 @@
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.util.TriState;
import net.fabricmc.fabric.impl.renderer.VanillaModelEncoder;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.item.ItemColors;
import net.minecraft.client.renderer.*;
import net.minecraft.client.renderer.entity.ItemRenderer;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
Expand Down Expand Up @@ -173,7 +171,7 @@ private void colorizeQuad(MutableQuadViewImpl quad, int colorIndex) {
final int itemColor = colorMap.getColor(itemStack, colorIndex);

for (int i = 0; i < 4; i++) {
quad.color(i, ColorHelper.multiplyColor(itemColor, quad.color(i)));
quad.color(i, ColorMixer.mulComponentWise(itemColor, quad.color(i)));
}
}
}
Expand Down
Loading

0 comments on commit 16f612d

Please sign in to comment.