From 1d1093ba1f236d7cd4ef49fceca0518ab73dca45 Mon Sep 17 00:00:00 2001 From: OmeWillem Date: Sat, 6 Jul 2024 02:41:10 +0200 Subject: [PATCH] Fixed that colors actually match for the most part --- .../geyserutils/geyser/GeyserUtils.java | 56 ++++++++---- .../geyserutils/geyser/util/DeltaUtils.java | 91 +++++++++++++++++++ 2 files changed, 128 insertions(+), 19 deletions(-) create mode 100644 geyser/src/main/java/me/zimzaza4/geyserutils/geyser/util/DeltaUtils.java diff --git a/geyser/src/main/java/me/zimzaza4/geyserutils/geyser/GeyserUtils.java b/geyser/src/main/java/me/zimzaza4/geyserutils/geyser/GeyserUtils.java index baf507d..63e50d6 100644 --- a/geyser/src/main/java/me/zimzaza4/geyserutils/geyser/GeyserUtils.java +++ b/geyser/src/main/java/me/zimzaza4/geyserutils/geyser/GeyserUtils.java @@ -22,6 +22,7 @@ import me.zimzaza4.geyserutils.geyser.replace.JavaAddEntityTranslatorReplace; import me.zimzaza4.geyserutils.geyser.scoreboard.EntityScoreboard; import me.zimzaza4.geyserutils.geyser.translator.NPCFormResponseTranslator; import me.zimzaza4.geyserutils.geyser.util.Converter; +import me.zimzaza4.geyserutils.geyser.util.DeltaUtils; import me.zimzaza4.geyserutils.geyser.util.ReflectionUtils; import net.kyori.adventure.text.format.TextColor; import org.cloudburstmc.math.vector.Vector3f; @@ -609,28 +610,45 @@ public class GeyserUtils implements Extension { } private static int getColor(int argb) { - TextColor color = TextColor.color(argb); - List colors = Arrays.asList( - TextColor.color(255, 255, 255), // 0: White - TextColor.color(255, 170, 0), // 1: Orange -> Gold - TextColor.color(255, 85, 255), // 2: Magenta -> Light Purple - TextColor.color(85, 85, 255), // 3: Light Blue -> Blue - TextColor.color(255, 255, 85), // 4: Yellow - TextColor.color(85, 255, 85), // 5: Lime -> Green - TextColor.color(255, 85, 85), // 6: Pink -> Red - TextColor.color(170, 170, 170), // 7: Gray - TextColor.color(85, 85, 85), // 8: Light Gray -> Dark Gray - TextColor.color(85, 255, 255), // 9: Cyan -> Aqua - TextColor.color(0, 170, 0), // 10: Green > Dark Green - TextColor.color(0, 0, 170), // 11: Blue -> Dark Blue - TextColor.color(170, 0, 170), // 12: Purple -> Dark Purple - TextColor.color(0, 170, 0), // 13: Green -> Dark Green - TextColor.color(170, 0, 0), // 14: Red -> Dark Red - TextColor.color(0, 0, 0) // 15: Black + int r = (argb >> 16) & 0xFF; + int g = (argb >> 8) & 0xFF; + int b = argb & 0xFF; + double[] colorLab = DeltaUtils.rgbToLab(r, g, b); + + List colors = Arrays.asList( + new int[]{249, 255, 254}, // 0: White + new int[]{249, 128, 29}, // 1: Orange + new int[]{199, 78, 189}, // 2: Magenta + new int[]{58, 179, 218}, // 3: Light Blue + new int[]{254, 216, 61}, // 4: Yellow + new int[]{128, 199, 31}, // 5: Lime + new int[]{243, 139, 170}, // 6: Pink + new int[]{71, 79, 82}, // 7: Gray + new int[]{159, 157, 151}, // 8: Light Gray + new int[]{22, 156, 156}, // 9: Cyan + new int[]{137, 50, 184}, // 10: Purple + new int[]{60, 68, 170}, // 11: Blue + new int[]{131, 84, 50}, // 12: Brown + new int[]{94, 124, 22}, // 13: Green + new int[]{176, 46, 38}, // 14: Red + new int[]{29, 29, 33} // 15: Black ); - return colors.indexOf(TextColor.nearestColorTo(colors, color)); + int closestColorIndex = -1; + double minDeltaE = Double.MAX_VALUE; + + for (int i = 0; i < colors.size(); i++) { + int[] rgb = colors.get(i); + double[] lab = DeltaUtils.rgbToLab(rgb[0], rgb[1], rgb[2]); + double deltaE = DeltaUtils.calculateDeltaE(colorLab, lab); + if (deltaE < minDeltaE) { + minDeltaE = deltaE; + closestColorIndex = i; + } + } + + return closestColorIndex; } } diff --git a/geyser/src/main/java/me/zimzaza4/geyserutils/geyser/util/DeltaUtils.java b/geyser/src/main/java/me/zimzaza4/geyserutils/geyser/util/DeltaUtils.java new file mode 100644 index 0000000..08a6f08 --- /dev/null +++ b/geyser/src/main/java/me/zimzaza4/geyserutils/geyser/util/DeltaUtils.java @@ -0,0 +1,91 @@ +package me.zimzaza4.geyserutils.geyser.util; + +public class DeltaUtils { + public static double calculateDeltaE(double[] lab1, double[] lab2) { + // CIEDE2000 algorithm implementation + double deltaL = lab2[0] - lab1[0]; + double lBar = (lab1[0] + lab2[0]) / 2.0; + double c1 = Math.sqrt(lab1[1] * lab1[1] + lab1[2] * lab1[2]); + double c2 = Math.sqrt(lab2[1] * lab2[1] + lab2[2] * lab2[2]); + double cBar = (c1 + c2) / 2.0; + double a1Prime = lab1[1] + lab1[1] / 2.0 * (1 - Math.sqrt(Math.pow(cBar, 7) / (Math.pow(cBar, 7) + Math.pow(25, 7)))); + double a2Prime = lab2[1] + lab2[1] / 2.0 * (1 - Math.sqrt(Math.pow(cBar, 7) / (Math.pow(cBar, 7) + Math.pow(25, 7)))); + double c1Prime = Math.sqrt(a1Prime * a1Prime + lab1[2] * lab1[2]); + double c2Prime = Math.sqrt(a2Prime * a2Prime + lab2[2] * lab2[2]); + double cBarPrime = (c1Prime + c2Prime) / 2.0; + double deltaCPrime = c2Prime - c1Prime; + double h1Prime = Math.atan2(lab1[2], a1Prime); + if (h1Prime < 0) h1Prime += 2 * Math.PI; + double h2Prime = Math.atan2(lab2[2], a2Prime); + if (h2Prime < 0) h2Prime += 2 * Math.PI; + double deltahPrime = h2Prime - h1Prime; + if (Math.abs(deltahPrime) > Math.PI) deltahPrime -= 2 * Math.PI * Math.signum(deltahPrime); + double deltaHPrime = 2 * Math.sqrt(c1Prime * c2Prime) * Math.sin(deltahPrime / 2.0); + double lBarPrime = (lab1[0] + lab2[0]) / 2.0; + double cBarPrimeDelta = (c1Prime + c2Prime) / 2.0; + double hBarPrime = (h1Prime + h2Prime) / 2.0; + if (Math.abs(h1Prime - h2Prime) > Math.PI) hBarPrime -= Math.PI; + double t = 1 - 0.17 * Math.cos(hBarPrime - Math.PI / 6) + 0.24 * Math.cos(2 * hBarPrime) + 0.32 * Math.cos(3 * hBarPrime + Math.PI / 30) - 0.20 * Math.cos(4 * hBarPrime - 63 * Math.PI / 180); + double deltaTheta = 30 * Math.exp(-((hBarPrime - 275 * Math.PI / 180) / 25 * Math.PI / 180) * ((hBarPrime - 275 * Math.PI / 180) / 25 * Math.PI / 180)); + double rC = 2 * Math.sqrt(Math.pow(cBarPrimeDelta, 7) / (Math.pow(cBarPrimeDelta, 7) + Math.pow(25, 7))); + double sL = 1 + (0.015 * (lBarPrime - 50) * (lBarPrime - 50)) / Math.sqrt(20 + (lBarPrime - 50) * (lBarPrime - 50)); + double sC = 1 + 0.045 * cBarPrimeDelta; + double sH = 1 + 0.015 * cBarPrimeDelta * t; + double rT = -Math.sin(2 * deltaTheta) * rC; + return Math.sqrt((deltaL / sL) * (deltaL / sL) + (deltaCPrime / sC) * (deltaCPrime / sC) + (deltaHPrime / sH) * (deltaHPrime / sH) + rT * (deltaCPrime / sC) * (deltaHPrime / sH)); + } + + public static double[] rgbToLab(int r, int g, int b) { + // Convert RGB to XYZ + double[] xyz = rgbToXyz(r, g, b); + // Convert XYZ to Lab + return xyzToLab(xyz[0], xyz[1], xyz[2]); + } + + private static double[] rgbToXyz(int r, int g, int b) { + double var_R = (r / 255.0); + double var_G = (g / 255.0); + double var_B = (b / 255.0); + + if (var_R > 0.04045) var_R = Math.pow((var_R + 0.055) / 1.055, 2.4); + else var_R = var_R / 12.92; + if (var_G > 0.04045) var_G = Math.pow((var_G + 0.055) / 1.055, 2.4); + else var_G = var_G / 12.92; + if (var_B > 0.04045) var_B = Math.pow((var_B + 0.055) / 1.055, 2.4); + else var_B = var_B / 12.92; + + var_R = var_R * 100.0; + var_G = var_G * 100.0; + var_B = var_B * 100.0; + + // Observer. = 2°, Illuminant = D65 + double x = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805; + double y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722; + double z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505; + + return new double[]{x, y, z}; + } + + private static double[] xyzToLab(double x, double y, double z) { + double ref_X = 95.047; + double ref_Y = 100.000; + double ref_Z = 108.883; + + double var_X = x / ref_X; + double var_Y = y / ref_Y; + double var_Z = z / ref_Z; + + if (var_X > 0.008856) var_X = Math.pow(var_X, 1.0/3.0); + else var_X = (7.787 * var_X) + (16.0 / 116.0); + if (var_Y > 0.008856) var_Y = Math.pow(var_Y, 1.0/3.0); + else var_Y = (7.787 * var_Y) + (16.0 / 116.0); + if (var_Z > 0.008856) var_Z = Math.pow(var_Z, 1.0/3.0); + else var_Z = (7.787 * var_Z) + (16.0 / 116.0); + + double l = (116.0 * var_Y) - 16.0; + double a = 500.0 * (var_X - var_Y); + double b = 200.0 * (var_Y - var_Z); + + return new double[]{l, a, b}; + } +}