package openmods.utils; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.util.Collection; import java.util.Locale; import java.util.Map; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraftforge.oredict.OreDictionary; import org.apache.commons.lang3.text.WordUtils; public class ColorUtils { public static enum ColorMeta { BLACK("black", 0x1E1B1B), RED("red", 0xB3312C), GREEN("green", 0x3B511A), BROWN("brown", 0x51301A), BLUE("blue", 0x253192), PURPLE("purple", 0x7B2FBE), CYAN("cyan", 0x287697), LIGHT_GRAY("lightGray", "silver", 0xABABAB), GRAY("gray", 0x434343), PINK("pink", 0xD88198), LIME("lime", 0x41CD34), YELLOW("yellow", 0xDECF2A), LIGHT_BLUE("lightBlue", "light_blue", 0x6689D3), MAGENTA("magenta", 0xC354CD), ORANGE("orange", 0xEB8844), WHITE("white", 0xF0F0F0); public final int rgb; public final int vanillaDyeId; public final int vanillaBlockId; public final int oreId; public final int bitmask; public final String oreName; public final String name; public final String unlocalizedName; public final String textureName; public final RGB rgbWrap; public final CYMK cymkWrap; public ItemStack createStack(Block block, int amount) { return new ItemStack(block, amount, vanillaBlockId); } public ItemStack createStack(Item item, int amount) { return new ItemStack(item, amount, vanillaDyeId); } private ColorMeta(String name, int rgb) { this(name, name, rgb); } private ColorMeta(String name, String textureName, int rgb) { this.oreName = "dye" + WordUtils.capitalize(name); this.oreId = OreDictionary.getOreID(oreName); this.textureName = textureName; this.name = name.toLowerCase(Locale.ENGLISH); this.unlocalizedName = "openmodslib.color." + name; this.rgb = rgb; final int index = ordinal(); this.vanillaDyeId = index; this.vanillaBlockId = ~index & 15; this.bitmask = 1 << vanillaBlockId; this.rgbWrap = new RGB(rgb); this.cymkWrap = rgbWrap.toCYMK(); } private static final ColorMeta[] COLORS = ColorMeta.values(); } private static final Map<String, ColorMeta> COLORS_BY_ORE_NAME = Maps.newHashMap(); private static final Map<String, ColorMeta> COLORS_BY_NAME = Maps.newHashMap(); private static final Map<Integer, ColorMeta> COLORS_BY_ORE_ID = Maps.newHashMap(); private static final Map<Integer, ColorMeta> COLORS_BY_BITMASK = Maps.newHashMap(); static { for (ColorMeta color : ColorMeta.COLORS) { COLORS_BY_NAME.put(color.name, color); COLORS_BY_ORE_NAME.put(color.oreName, color); COLORS_BY_ORE_ID.put(color.oreId, color); COLORS_BY_BITMASK.put(color.bitmask, color); } } public static Set<ColorMeta> stackToColor(ItemStack stack) { Set<ColorMeta> result = Sets.newIdentityHashSet(); for (int oreId : OreDictionary.getOreIDs(stack)) { ColorMeta meta = COLORS_BY_ORE_ID.get(oreId); if (meta != null) result.add(meta); } return result; } public static ColorMeta oreIdToColor(int oreId) { return COLORS_BY_ORE_ID.get(oreId); } public static ColorMeta oreNameToColor(String oreName) { return COLORS_BY_ORE_NAME.get(oreName); } public static ColorMeta nameToColor(String name) { return COLORS_BY_NAME.get(name.toLowerCase(Locale.ENGLISH)); } public static ColorMeta bitmaskToColor(int bitmask) { return COLORS_BY_BITMASK.get(bitmask); } public static ColorMeta vanillaBlockToColor(int vanillaId) { return ColorMeta.COLORS[~vanillaId & 0xF]; } public static ColorMeta vanillaDyeToColor(int vanillaId) { return ColorMeta.COLORS[vanillaId & 0xF]; } public static Collection<ColorMeta> getAllColors() { return ImmutableList.copyOf(ColorMeta.COLORS); } public static int bitmaskToVanilla(int color) { int high = Integer.numberOfLeadingZeros(color); int low = Integer.numberOfTrailingZeros(color); Preconditions.checkArgument(high == 31 - low && low <= 16, "Invalid color value: %sb", Integer.toBinaryString(color)); return low; } public static ColorMeta findNearestColor(RGB target, int tolernace) { ColorMeta result = null; int distSq = Integer.MAX_VALUE; for (ColorMeta meta : ColorMeta.COLORS) { final int currentDistSq = meta.rgbWrap.distanceSq(target); if (currentDistSq < distSq) { result = meta; distSq = currentDistSq; } } return (distSq < 3 * tolernace * tolernace)? result : null; } public static class RGB { public int r; public int g; public int b; public RGB(float r, float g, float b) { this.r = ((int)(r * 255)) & 0xFF; this.g = ((int)(g * 255)) & 0xFF; this.b = ((int)(b * 255)) & 0xFF; } public RGB(int r, int g, int b) { this.r = r & 0xFF; this.g = g & 0xFF; this.b = b & 0xFF; } public RGB(int color) { this(((color & 0xFF0000) >> 16), ((color & 0x00FF00) >> 8), (color & 0x0000FF)); } public RGB() {} public void setColor(int color) { r = (color & 0xFF0000) >> 16; g = (color & 0x00FF00) >> 8; b = color & 0x0000FF; } public int getColor() { return r << 16 | g << 8 | b; } public float getR() { return r / 255f; } public float getG() { return g / 255f; } public float getB() { return b / 255f; } public RGB interpolate(RGB other, double amount) { int iPolR = (int)(r * (1D - amount) + other.r * amount); int iPolG = (int)(g * (1D - amount) + other.g * amount); int iPolB = (int)(b * (1D - amount) + other.b * amount); return new RGB(iPolR, iPolG, iPolB); } public CYMK toCYMK() { float cyan = 1f - (r / 255f); float magenta = 1f - (g / 255f); float yellow = 1f - (b / 255f); float K = 1; if (cyan < K) { K = cyan; } if (magenta < K) { K = magenta; } if (yellow < K) { K = yellow; } if (K == 1) { cyan = 0; magenta = 0; yellow = 0; } else { cyan = (cyan - K) / (1f - K); magenta = (magenta - K) / (1f - K); yellow = (yellow - K) / (1f - K); } return new CYMK(cyan, yellow, magenta, K); } public int distanceSq(RGB other) { // Formula taken from http://www.compuphase.com/cmetric.htm int meanR = (this.r + other.r) >>> 1; int dR = this.r - other.r; int dG = this.g - other.g; int dB = this.b - other.b; int dR2 = dR * dR; int dG2 = dG * dG; int dB2 = dB * dB; return Math.abs((((512 + meanR) * dR2) >> 8) + (dG2 << 2) + (((767 - meanR) * dB2) >> 8)); } } public static class CYMK { private float cyan, yellow, magenta, key; public CYMK(float c, float y, float m, float k) { this.cyan = c; this.yellow = y; this.magenta = m; this.key = k; } public float getCyan() { return cyan; } public void setCyan(float cyan) { this.cyan = cyan; } public float getYellow() { return yellow; } public void setYellow(float yellow) { this.yellow = yellow; } public float getMagenta() { return magenta; } public void setMagenta(float magenta) { this.magenta = magenta; } public float getKey() { return key; } public void setKey(float key) { this.key = key; } } }