Created
May 16, 2023 15:42
-
-
Save IllusionTheDev/b730cdbeba8d022ba83262cc817371ad to your computer and use it in GitHub Desktop.
Minecraft map color matching
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.awt.*; | |
import java.util.Comparator; | |
/** | |
* The basic euclidean comparator is a simple comparator that compares two colors | |
* based on their euclidean distance. | |
* | |
* @author Illusion | |
*/ | |
public class BasicEuclideanComparator implements Comparator<Color> { | |
@Override | |
public int compare(Color color, Color t1) { | |
int r1 = color.getRed(); | |
int g1 = color.getGreen(); | |
int b1 = color.getBlue(); | |
int r2 = t1.getRed(); | |
int g2 = t1.getGreen(); | |
int b2 = t1.getBlue(); | |
return (int) Math.sqrt(Math.pow(r1 - r2, 2) + Math.pow(g1 - g2, 2) + Math.pow(b1 - b2, 2)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.awt.*; | |
import java.util.Comparator; | |
import java.util.Map; | |
import java.util.concurrent.ConcurrentHashMap; | |
/** | |
* The color matcher class is used to match a color to a Minecraft map color. | |
* You can define a custom comparator to use for matching colors, which can be tuned to your liking. | |
* The default comparator is the {@link BasicEuclideanComparator}. | |
* Another option is the {@link WeightedEuclideanComparator}, which produces brighter yellows and oranges. | |
* <p> | |
* These colors are defined up to 1.18.2, and it is expected that you will update this class when a new Minecraft version with new map colors is released. | |
* | |
* @author Illusion | |
*/ | |
public class ColorMatcher { | |
private static final Color[] MAP_COLORS; | |
private static final Comparator<Color> COMPARATOR = new BasicEuclideanComparator(); | |
private static final Map<Color, Integer> matchedColors = new ConcurrentHashMap<>(); | |
static { | |
final Color[] baseMapColors = new Color[] | |
{ | |
new Color(0, 0, 0, 0), // transparent | |
new Color(127, 178, 56), // grass | |
new Color(247, 233, 163), // sand | |
new Color(199, 199, 199), // wool | |
new Color(255, 0, 0), // fire | |
new Color(160, 160, 255), // ice | |
new Color(167, 167, 167), // metal | |
new Color(0, 124, 0), // Plant | |
new Color(255, 255, 255), // Snow | |
new Color(164, 168, 184), // Clay | |
new Color(151, 109, 77), // Dirt | |
new Color(112, 112, 112), // Stone | |
new Color(64, 64, 255), // Water | |
new Color(143, 119, 72), // Wood | |
//new 1.7 colors (13w42a/13w42b) | |
new Color(255, 252, 245), // Quartz | |
new Color(216, 127, 51), // COLOR_ORANGE | |
new Color(178, 76, 216), // COLOR_MAGENTA | |
new Color(102, 153, 216), // COLOR_LIGHT_BLUE | |
new Color(229, 229, 51), // COLOR_YELLOW | |
new Color(127, 204, 25), // COLOR_LIGHT_GREEN | |
new Color(242, 127, 165), // COLOR_PINK | |
new Color(76, 76, 76), // COLOR_GREY | |
new Color(153, 153, 153), // COLOR_LIGHT_GREY | |
new Color(76, 127, 153), // COLOR_CYAN | |
new Color(127, 63, 178), // COLOR_PURPLE | |
new Color(51, 76, 178), // COLOR_BLUE | |
new Color(102, 76, 51), // COLOR_BROWN | |
new Color(102, 127, 51), // COLOR_GREEN | |
new Color(153, 51, 51), // COLOR_RED | |
new Color(25, 25, 25), // COLOR_BLACK | |
new Color(250, 238, 77), // GOLD | |
new Color(92, 219, 213), // Diamond | |
new Color(74, 128, 255), // lapis | |
new Color(0, 217, 58), // emerald | |
new Color(129, 86, 49), // podzol | |
new Color(112, 2, 0), // NETHER | |
//new 1.8 colors | |
new Color(209, 177, 161), // TERRACOTTA_WHITE | |
new Color(159, 82, 36), // TERRACOTTA_ORANGE | |
new Color(149, 87, 108), // terracotta magenta | |
new Color(112, 108, 138), // terracotta_light_blue | |
new Color(186, 133, 36), // terracotta_yellow | |
new Color(103, 117, 53), // terracotta_light_green | |
new Color(160, 77, 78), // terracotta_pink | |
new Color(57, 41, 35), // terracotta_grey | |
new Color(135, 107, 98), // terracotta_light_grey | |
new Color(87, 92, 92), // terracotta_cyan | |
new Color(122, 73, 88), // terracotta_purple | |
new Color(76, 62, 92), // terracotta_blue | |
new Color(76, 50, 35), // terracotta_brown | |
new Color(76, 50, 35), // terracotta_green | |
new Color(142, 60, 46), // terracotta_red | |
new Color(37, 22, 16), // terracotta_black | |
//1.16 | |
new Color(189, 48, 49), // CRIMson_nylium | |
new Color(148, 63, 97), // crimson_stem | |
new Color(92, 25, 29), // crimson_hyphae | |
new Color(22, 126, 134), // warped_nylium | |
new Color(58, 142, 140), // warped_stem | |
new Color(86, 44, 62), // warped_hyphae | |
new Color(20, 180, 133), // warped_wart_block | |
// cliffs update (1.18 / 1.19) | |
new Color(100, 100, 100), // deepslate | |
new Color(216, 175, 147), // raw iron | |
new Color(127, 167, 150), // glow lichen | |
}; | |
MAP_COLORS = new Color[baseMapColors.length * 4]; | |
for (int index = 0; index < baseMapColors.length; ++index) { | |
Color baseColor = baseMapColors[index]; | |
MAP_COLORS[index * 4] = new Color( | |
(int) (baseColor.getRed() * 180.0 / 255.0 + 0.5), | |
(int) (baseColor.getGreen() * 180.0 / 255.0 + 0.5), | |
(int) (baseColor.getBlue() * 180.0 / 255.0 + 0.5), | |
baseColor.getAlpha() | |
); | |
MAP_COLORS[index * 4 + 1] = new Color( | |
(int) (baseColor.getRed() * 220.0 / 255.0 + 0.5), | |
(int) (baseColor.getGreen() * 220.0 / 255.0 + 0.5), | |
(int) (baseColor.getBlue() * 220.0 / 255.0 + 0.5), | |
baseColor.getAlpha() | |
); | |
MAP_COLORS[index * 4 + 2] = baseColor; | |
MAP_COLORS[index * 4 + 3] = new Color( | |
(int) (baseColor.getRed() * 135.0 / 255.0 + 0.5), | |
(int) (baseColor.getGreen() * 135.0 / 255.0 + 0.5), | |
(int) (baseColor.getBlue() * 135.0 / 255.0 + 0.5), | |
baseColor.getAlpha() | |
); | |
} | |
} | |
/** | |
* Returns the closest color index (in the map colors palette) for the given color. | |
* Results are cached. | |
* | |
* @param color the color to match | |
* @return the closest color index | |
*/ | |
public static byte getColor(Color color) { | |
if (color.getAlpha() == 0) { | |
return 0; | |
} | |
byte cached = matchedColors.getOrDefault(color, -1).byteValue(); | |
if (cached != -1) { | |
return cached; | |
} | |
int best = 0; | |
int bestDistance = Integer.MAX_VALUE; | |
for (int i = 0; i < MAP_COLORS.length; ++i) { | |
Color mappedColor = MAP_COLORS[i]; | |
if (mappedColor.getAlpha() == 0) { | |
continue; // skip transparent colors | |
} | |
int distance = COMPARATOR.compare(color, mappedColor); | |
if (distance < bestDistance) { | |
bestDistance = distance; | |
best = i; | |
} | |
} | |
matchedColors.put(color, best); | |
return (byte) best; | |
} | |
/** | |
* Returns the color for the given index. | |
* | |
* @param original the original color | |
* @return the closest color available in the map colors palette | |
*/ | |
public static Color getClosestColor(Color original) { | |
if (original.getAlpha() == 0) { | |
return new Color(0, 0, 0, 0); | |
} | |
int best = 0; | |
int bestDistance = Integer.MAX_VALUE; | |
for (int i = 0; i < MAP_COLORS.length; ++i) { | |
Color mappedColor = MAP_COLORS[i]; | |
if (mappedColor.getAlpha() == 0) { | |
continue; // skip transparent colors | |
} | |
int distance = COMPARATOR.compare(original, mappedColor); | |
if (distance < bestDistance) { | |
bestDistance = distance; | |
best = i; | |
} | |
} | |
return MAP_COLORS[best]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.awt.*; | |
import java.util.Comparator; | |
/** | |
* The weighted euclidean comparator is a comparator that compares two colors | |
* based on their euclidean distance, but with a weighted variance. | |
* | |
* @author Illusion | |
*/ | |
public class WeightedEuclideanComparator implements Comparator<Color> { | |
@Override | |
public int compare(Color color, Color t1) { | |
int r1 = color.getRed(); | |
int g1 = color.getGreen(); | |
int b1 = color.getBlue(); | |
int r2 = t1.getRed(); | |
int g2 = t1.getGreen(); | |
int b2 = t1.getBlue(); | |
int deltaR = r1 - r2; | |
int deltaG = g1 - g2; | |
int deltaB = b1 - b2; | |
int redVariance = (int) (1 / 2f * (r1 + r2)); | |
if (redVariance < 128) { | |
return (int) (Math.sqrt( | |
Math.pow(2 * deltaR, 2) + Math.pow(4 * deltaG, 2) + Math.pow(3 * deltaB, 2))); | |
} | |
return (int) (Math.sqrt( | |
Math.pow(3 * deltaR, 2) + Math.pow(4 * deltaG, 2) + Math.pow(2 * deltaB, 2))); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment