151 lines
5.4 KiB
Java
151 lines
5.4 KiB
Java
package tc.oc.commons.bukkit.hologram;
|
|
|
|
import com.google.common.base.Preconditions;
|
|
import com.google.common.collect.ImmutableBiMap;
|
|
import java.awt.Color;
|
|
import java.awt.image.BufferedImage;
|
|
import java.util.LinkedList;
|
|
import javax.annotation.Nullable;
|
|
import org.apache.commons.lang.StringUtils;
|
|
import org.bukkit.ChatColor;
|
|
|
|
/**
|
|
* Various {@link tc.oc.commons.bukkit.hologram.content.HologramFrame}-related utility methods
|
|
*/
|
|
public class HologramUtil {
|
|
public static final int MAX_CLOSEST = 255 * 255 * 3;
|
|
|
|
/**
|
|
* The filler content used when transparency is detected inside of an image.
|
|
*
|
|
* @see <a href="http://www.fileformat.info/info/unicode/char/23b9/index.htm">FileFormat.info reference</a>.
|
|
*/
|
|
public static final String ALPHA_FILLER_CONTENT = ChatColor.DARK_GRAY + " \u23B9";
|
|
|
|
/**
|
|
* The character used for non-transparent pixels inside of an image.
|
|
*
|
|
* @see <a href="http://www.unicodemap.org/details/0x2588/index.html">UnicodeMap.org reference</a>.
|
|
*/
|
|
public static final char PIXEL_CHAR = '\u2588';
|
|
|
|
/**
|
|
* A map of Minecraft chat colors to their closest RGB counterparts, created by asdjke.
|
|
*/
|
|
public static final ImmutableBiMap<Color, ChatColor> COLOR_MAP = ImmutableBiMap.<Color, ChatColor>builder()
|
|
.put(Color.BLACK, ChatColor.BLACK)
|
|
.put(new Color(0, 0, 170), ChatColor.DARK_BLUE)
|
|
.put(new Color(0, 170, 0), ChatColor.DARK_GREEN)
|
|
.put(new Color(0, 170, 170), ChatColor.DARK_AQUA)
|
|
.put(new Color(170, 0, 0), ChatColor.DARK_RED)
|
|
.put(new Color(170, 0, 170), ChatColor.DARK_PURPLE)
|
|
.put(new Color(255, 170, 0), ChatColor.GOLD)
|
|
.put(new Color(170, 170, 170), ChatColor.GRAY)
|
|
.put(new Color(85, 85, 85), ChatColor.DARK_GRAY)
|
|
.put(new Color(85, 85, 255), ChatColor.BLUE)
|
|
.put(new Color(85, 255, 85), ChatColor.GREEN)
|
|
.put(new Color(85, 255, 255), ChatColor.AQUA)
|
|
.put(new Color(255, 85, 85), ChatColor.RED)
|
|
.put(new Color(255, 85, 255), ChatColor.LIGHT_PURPLE)
|
|
.put(new Color(255, 255, 85), ChatColor.YELLOW)
|
|
.put(Color.WHITE, ChatColor.WHITE)
|
|
.build();
|
|
|
|
/**
|
|
* Converts a {@link java.awt.image.BufferedImage} to a multi-line text message, using {@link #COLOR_MAP}.
|
|
*
|
|
* @return A {@link java.lang.String[]} containing the message
|
|
*/
|
|
public static String[] imageToText(BufferedImage image, boolean trim) {
|
|
int height = Preconditions.checkNotNull(image, "Image").getHeight();
|
|
int width = image.getWidth();
|
|
|
|
String[][] message = new String[height][width];
|
|
LinkedList<Integer> pendingAlpha = new LinkedList<>();
|
|
for (int y = 0; y < height; y++) {
|
|
boolean fillAlpha = !trim;
|
|
boolean left = false;
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
Color color = new Color(image.getRGB(x, y), true);
|
|
|
|
if (trim) {
|
|
if (color.getAlpha() < 1) {
|
|
pendingAlpha.add(x);
|
|
left = (left || x == 0);
|
|
} else {
|
|
if (!left) {
|
|
applyPendingAlpha(pendingAlpha, message[y]);
|
|
} else {
|
|
pendingAlpha.clear();
|
|
left = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
ChatColor minecraftColor = rgbToMinecraft(closestColorMatch(color, COLOR_MAP.keySet()));
|
|
message[y][x] = minecraftColor == null ? (fillAlpha ? ALPHA_FILLER_CONTENT : "") : minecraftColor.toString() + PIXEL_CHAR;
|
|
}
|
|
|
|
if (!trim) {
|
|
applyPendingAlpha(pendingAlpha, message[y]);
|
|
}
|
|
}
|
|
|
|
String[] messageFinal = new String[height];
|
|
for (int y = 0; y < height; y++) {
|
|
messageFinal[y] = StringUtils.join(message[y]);
|
|
}
|
|
|
|
return messageFinal;
|
|
}
|
|
|
|
/**
|
|
* Attempts to find the closest {@link ChatColor} for the given {@link Color}.
|
|
*
|
|
* @param color The color
|
|
* @return The appropriate chat color
|
|
*/
|
|
public static @Nullable ChatColor rgbToMinecraft(Color color) {
|
|
return COLOR_MAP.get(color);
|
|
}
|
|
|
|
/**
|
|
* Finds the closest match for the specified {@link Color} from the provided colors. Created by asdjke.
|
|
*
|
|
* @param color The color to match
|
|
* @param colors The possible matches
|
|
* @return The closest match, or null if alpha is zero
|
|
*/
|
|
public static @Nullable Color closestColorMatch(Color color, Iterable<Color> colors) {
|
|
if (color.getAlpha() < 1) return null;
|
|
|
|
int r = color.getRed();
|
|
int g = color.getGreen();
|
|
int b = color.getBlue();
|
|
|
|
int closest = MAX_CLOSEST;
|
|
Color best = null;
|
|
|
|
for (Color key : colors) {
|
|
int rDist = Math.abs(r - key.getRed());
|
|
int gDist = Math.abs(g - key.getGreen());
|
|
int bDist = Math.abs(b - key.getBlue());
|
|
int dist = rDist * rDist + gDist * gDist + bDist * bDist;
|
|
if (dist < closest) {
|
|
best = key;
|
|
closest = dist;
|
|
}
|
|
}
|
|
|
|
return best;
|
|
}
|
|
|
|
private static void applyPendingAlpha(LinkedList<Integer> pendingAlpha, String[] message) {
|
|
for (int x : pendingAlpha) {
|
|
message[x] = ALPHA_FILLER_CONTENT;
|
|
}
|
|
pendingAlpha.clear();
|
|
}
|
|
}
|