aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java6
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java29
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/ColorUtils.java23
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/OkLabColor.java104
4 files changed, 135 insertions, 27 deletions
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java b/src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java
index 8285a823..96de8e2e 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java
@@ -1,7 +1,7 @@
package de.hysky.skyblocker.skyblock;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
-import de.hysky.skyblocker.skyblock.item.CustomArmorAnimatedDyes;
+import de.hysky.skyblocker.utils.OkLabColor;
import net.minecraft.entity.decoration.ArmorStandEntity;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
@@ -57,10 +57,10 @@ public class CompactDamage {
int length = prettifiedDmg.length();
for (int i = 0; i < length; i++) {
prettierCustomName.append(Text.literal(prettifiedDmg.substring(i, i + 1)).withColor(
- CustomArmorAnimatedDyes.interpolate(
+ OkLabColor.interpolate(
SkyblockerConfigManager.get().uiAndVisuals.compactDamage.critDamageGradientStart.getRGB() & 0x00FFFFFF,
SkyblockerConfigManager.get().uiAndVisuals.compactDamage.critDamageGradientEnd.getRGB() & 0x00FFFFFF,
- i / (length - 1.0)
+ i / (length - 1.0f)
)
));
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java
index 0621fd24..48f345c4 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java
@@ -9,6 +9,7 @@ import de.hysky.skyblocker.annotations.Init;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.ItemUtils;
+import de.hysky.skyblocker.utils.OkLabColor;
import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.utils.command.argumenttypes.color.ColorArgumentType;
import dev.isxander.yacl3.config.v2.api.SerialEntry;
@@ -21,7 +22,6 @@ import net.minecraft.command.CommandRegistryAccess;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.tag.ItemTags;
import net.minecraft.text.Text;
-import net.minecraft.util.math.MathHelper;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
@@ -102,25 +102,6 @@ public class CustomArmorAnimatedDyes {
return animatedDye.interpolate(trackedState);
}
- //Credit to https://codepen.io/OliverBalfour/post/programmatically-making-gradients
- public static int interpolate(int firstColor, int secondColor, double percentage) {
- int r1 = MathHelper.square((firstColor >> 16) & 0xFF);
- int g1 = MathHelper.square((firstColor >> 8) & 0xFF);
- int b1 = MathHelper.square(firstColor & 0xFF);
-
- int r2 = MathHelper.square((secondColor >> 16) & 0xFF);
- int g2 = MathHelper.square((secondColor >> 8) & 0xFF);
- int b2 = MathHelper.square(secondColor & 0xFF);
-
- double inverse = 1d - percentage;
-
- int r3 = (int) Math.floor(Math.sqrt(r1 * inverse + r2 * percentage));
- int g3 = (int) Math.floor(Math.sqrt(g1 * inverse + g2 * percentage));
- int b3 = (int) Math.floor(Math.sqrt(b1 * inverse + b2 * percentage));
-
- return (r3 << 16) | (g3 << 8 ) | b3;
- }
-
private static class AnimatedDyeStateTracker {
private int sampleCounter;
private boolean onBackCycle = false;
@@ -150,12 +131,12 @@ public class CustomArmorAnimatedDyes {
if (stateTracker.shouldCycleBack(samples, cycleBack)) stateTracker.onBackCycle = true;
if (stateTracker.onBackCycle) {
- double percent = (1d / (double) samples) * stateTracker.getAndDecrement();
+ float percent = (1f / samples) * stateTracker.getAndDecrement();
//Go back to normal cycle once we've cycled all the way back
if (stateTracker.sampleCounter == 0) stateTracker.onBackCycle = false;
- int interpolatedColor = CustomArmorAnimatedDyes.interpolate(color1, color2, percent);
+ int interpolatedColor = OkLabColor.interpolate(color1, color2, percent);
stateTracker.lastColor = interpolatedColor;
return interpolatedColor;
@@ -164,8 +145,8 @@ public class CustomArmorAnimatedDyes {
//This will only happen if cycleBack is false
if (stateTracker.sampleCounter == samples) stateTracker.sampleCounter = 0;
- double percent = (1d / (double) samples) * stateTracker.getAndIncrement();
- int interpolatedColor = CustomArmorAnimatedDyes.interpolate(color1, color2, percent);
+ float percent = (1f / samples) * stateTracker.getAndIncrement();
+ int interpolatedColor = OkLabColor.interpolate(color1, color2, percent);
stateTracker.lastColor = interpolatedColor;
diff --git a/src/main/java/de/hysky/skyblocker/utils/ColorUtils.java b/src/main/java/de/hysky/skyblocker/utils/ColorUtils.java
index 2c8f5e4a..e132f7e5 100644
--- a/src/main/java/de/hysky/skyblocker/utils/ColorUtils.java
+++ b/src/main/java/de/hysky/skyblocker/utils/ColorUtils.java
@@ -1,6 +1,7 @@
package de.hysky.skyblocker.utils;
import net.minecraft.util.DyeColor;
+import net.minecraft.util.math.MathHelper;
public class ColorUtils {
/**
@@ -23,4 +24,26 @@ public class ColorUtils {
public static float[] getFloatComponents(DyeColor dye) {
return getFloatComponents(dye.getEntityColor());
}
+
+ /**
+ * Interpolates linearly between two colours.
+ */
+ //Credit to https://codepen.io/OliverBalfour/post/programmatically-making-gradients
+ public static int interpolate(int firstColor, int secondColor, double percentage) {
+ int r1 = MathHelper.square((firstColor >> 16) & 0xFF);
+ int g1 = MathHelper.square((firstColor >> 8) & 0xFF);
+ int b1 = MathHelper.square(firstColor & 0xFF);
+
+ int r2 = MathHelper.square((secondColor >> 16) & 0xFF);
+ int g2 = MathHelper.square((secondColor >> 8) & 0xFF);
+ int b2 = MathHelper.square(secondColor & 0xFF);
+
+ double inverse = 1d - percentage;
+
+ int r3 = (int) Math.floor(Math.sqrt(r1 * inverse + r2 * percentage));
+ int g3 = (int) Math.floor(Math.sqrt(g1 * inverse + g2 * percentage));
+ int b3 = (int) Math.floor(Math.sqrt(b1 * inverse + b2 * percentage));
+
+ return (r3 << 16) | (g3 << 8 ) | b3;
+ }
}
diff --git a/src/main/java/de/hysky/skyblocker/utils/OkLabColor.java b/src/main/java/de/hysky/skyblocker/utils/OkLabColor.java
new file mode 100644
index 00000000..ce5c9f1a
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/OkLabColor.java
@@ -0,0 +1,104 @@
+package de.hysky.skyblocker.utils;
+
+/**
+ * Implements color interpolation in the OkLab color space.
+ *
+ * @see <a href="https://bottosson.github.io/posts/oklab">OkLab Colour Space</a>
+ * @see <a href="https://www.sjbrown.co.uk/posts/gamma-correct-rendering/">Gamma Correct Rendering</a>
+ */
+public class OkLabColor {
+
+ /**
+ * Converts a linear SRGB color to the OkLab color space.
+ *
+ * @param r the linearized red channel
+ * @param g the linearized green channel
+ * @param b the linearized blue channel
+ */
+ private static Lab linearSRGB2OkLab(float r, float g, float b) {
+ float l = Math.fma(0.4122214708f, r, Math.fma(0.5363325363f, g, 0.0514459929f * b));
+ float m = Math.fma(0.2119034982f, r, Math.fma(0.6806995451f, g, 0.1073969566f * b));
+ float s = Math.fma(0.0883024619f, r, Math.fma(0.2817188376f, g, 0.6299787005f * b));
+
+ float l_ = (float) Math.cbrt(l);
+ float m_ = (float) Math.cbrt(m);
+ float s_ = (float) Math.cbrt(s);
+
+ float L = Math.fma(0.2104542553f, l_, Math.fma(+0.7936177850f, m_, -0.0040720468f * s_));
+ float A = Math.fma(1.9779984951f, l_, Math.fma(-2.4285922050f, m_, +0.4505937099f * s_));
+ float B = Math.fma(0.0259040371f, l_, Math.fma(+0.7827717662f, m_, -0.8086757660f * s_));
+
+ return new Lab(L, A, B);
+ }
+
+ /**
+ * Converts a color in the OkLab color space to linear SRGB.
+ */
+ private static RGB okLab2LinearSRGB(float L, float A, float B) {
+ float l_ = L + 0.3963377774f * A + 0.2158037573f * B;
+ float m_ = L - 0.1055613458f * A - 0.0638541728f * B;
+ float s_ = L - 0.0894841775f * A - 1.2914855480f * B;
+
+ float l = l_ * l_ * l_;
+ float m = m_ * m_ * m_;
+ float s = s_ * s_ * s_;
+
+ float r = Math.fma(+4.0767416621f, l, Math.fma(-3.3077115913f, m, +0.2309699292f * s));
+ float g = Math.fma(-1.2684380046f, l, Math.fma(+2.6097574011f, m, -0.3413193965f * s));
+ float b = Math.fma(-0.0041960863f, l, Math.fma(-0.7034186147f, m, +1.7076147010f * s));
+
+ return new RGB(r, g, b);
+ }
+
+ /**
+ * Converts {@code channel} from RGB to linear SRGB.
+ */
+ private static float linearize(float channel) {
+ return channel <= 0.04045f ? channel / 12.92f : (float) Math.pow((channel + 0.055f) / 1.055f, 2.4f);
+ }
+
+ /**
+ * Converts {@code channel} from linear SRGB to RGB.
+ */
+ private static float delinearize(float channel) {
+ return channel <= 0.0031308f ? channel * 12.92f : Math.fma(1.055f, (float) Math.pow(channel, 1.0f / 2.4f), -0.055f);
+ }
+
+ /**
+ * Interpolates two colors using the OkLab color space.
+ *
+ * @param firstColor the RGB color at the left end of the gradient
+ * @param secondColor the RGB color at the right end of the gradient
+ * @param progress a float from [0, 1] representing the position in the gradient
+ *
+ * @return the interpolated color in the RGB format
+ */
+ //Escape analysis should hopefully take care of the objects :')
+ public static int interpolate(int firstColor, int secondColor, float progress) {
+ //Normalize colours to a range of [0, 1]
+ float normalizedR1 = ((firstColor >> 16) & 0xFF) / 255f;
+ float normalizedG1 = ((firstColor >> 8) & 0xFF) / 255f;
+ float normalizedB1 = (firstColor & 0xFF) / 255f;
+
+ float normalizedR2 = ((secondColor >> 16) & 0xFF) / 255f;
+ float normalizedG2 = ((secondColor >> 8) & 0xFF) / 255f;
+ float normalizedB2 = (secondColor & 0xFF) / 255f;
+
+ Lab lab1 = linearSRGB2OkLab(linearize(normalizedR1), linearize(normalizedG1), linearize(normalizedB1));
+ Lab lab2 = linearSRGB2OkLab(linearize(normalizedR2), linearize(normalizedG2), linearize(normalizedB2));
+
+ float L = Math.fma(progress, (lab2.l - lab1.l), lab1.l);
+ float A = Math.fma(progress, (lab2.a - lab1.a), lab1.a);
+ float B = Math.fma(progress, (lab2.b - lab1.b), lab1.b);
+
+ RGB rgb = okLab2LinearSRGB(L, A, B);
+ int r = Math.clamp((int) (delinearize(rgb.r) * 255f), 0, 255);
+ int g = Math.clamp((int) (delinearize(rgb.g) * 255f), 0, 255);
+ int b = Math.clamp((int) (delinearize(rgb.b) * 255f), 0, 255);
+
+ return (r << 16) | (g << 8) | b;
+ }
+
+ private record Lab(float l, float a, float b) {}
+ private record RGB(float r, float g, float b) {}
+}