package de.lessvoid.nifty.tools; import javax.annotation.Nonnull; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; /** * Color helper class to manage colors. * * @author void */ public class Color { private static final Logger log = Logger.getLogger(Color.class.getName()); /** * scale short mode factor (converts 0x5 to 0x55). */ private static final int SCALE_SHORT_MODE = 0x11; /** * the default empty color. */ public static final Color NONE = new Color(0.0f, 0.0f, 0.0f, 0.0f); /** * a white color. */ public static final Color WHITE = new Color(1.0f, 1.0f, 1.0f, 1.0f); /** * a black color. */ public static final Color BLACK = new Color(0.0f, 0.0f, 0.0f, 1.0f); /** * max value for conversion. */ private static final float MAX_INT_VALUE = 255.0f; /** * hex base to convert numbers. */ private static final int HEX_BASE = 16; /** * red component. */ private float red = 0.0f; /** * green component. */ private float green = 0.0f; /** * blue component. */ private float blue = 0.0f; /** * alpha component. */ private float alpha = 0.0f; private String colorString; /** * Create a color from a color String formated like in html * code but with alpha, e.g.: "#ff00ffff". * * @param color the color string */ public Color(@Nonnull final String color) { fromString(color); } /** * Create a color from components. * * @param newRed red component * @param newGreen green component * @param newBlue blue component * @param newAlpha alpha component */ public Color(final float newRed, final float newGreen, final float newBlue, final float newAlpha) { this.red = newRed; this.green = newGreen; this.blue = newBlue; this.alpha = newAlpha; this.colorString = fromRGBA(red, green, blue, alpha); } /** * Create a color from an encoded int value alpha + R + G + B. * * @param color color value */ public Color(final int color) { this.alpha = (color >> 24) & 0xFF; this.red = (color >> 16) & 0xFF; this.green = (color >> 8) & 0xFF; this.blue = (color) & 0xFF; this.colorString = fromRGBA(red, green, blue, alpha); } /** * Create a color from another color, using the given alpha value. * * @param newColor color * @param newAlpha alpha component */ public Color(@Nonnull final Color newColor, final float newAlpha) { this.red = newColor.getRed(); this.green = newColor.getGreen(); this.blue = newColor.getBlue(); this.alpha = newAlpha; this.colorString = fromRGBA(red, green, blue, alpha); } public Color(@Nonnull final Color colorParam) { this.red = colorParam.getRed(); this.green = colorParam.getGreen(); this.blue = colorParam.getBlue(); this.alpha = colorParam.getAlpha(); this.colorString = colorParam.getColorString(); } @Nonnull private String fromRGBA(float redValue, float greenValue, float blueValue, float alphaValue) { return "#" + toHex(redValue) + toHex(greenValue) + toHex(blueValue) + toHex(alphaValue); } private String toHex(final float redValue) { return Integer.toHexString((int) (redValue * 15)); } public String getColorString() { return colorString; } public void setColorString(final String colorString) { this.colorString = colorString; } @Nonnull public String getColorStringWithoutAlpha() { return colorString.substring(0, colorString.length() - 1); } /** * linear interpolate between the start color and the end color and updates this color. * * @param start start color * @param end end color * @param t t in [0,1] */ public void linear(@Nonnull final Color start, @Nonnull final Color end, final float t) { this.red = start.getRed() + t * (end.red - start.getRed()); this.green = start.getGreen() + t * (end.green - start.getGreen()); this.blue = start.getBlue() + t * (end.blue - start.getBlue()); this.alpha = start.getAlpha() + t * (end.alpha - start.getAlpha()); } /** * get the red component. * * @return red */ public float getRed() { return red; } /** * get the green component. * * @return green */ public float getGreen() { return green; } /** * get the blue component. * * @return blue */ public float getBlue() { return blue; } /** * get alpha value. * * @return alpha */ public float getAlpha() { return alpha; } /** * helper to get red from a string value. * * @param color color string * @return extracted red value */ private float getRFromString(@Nonnull final String color) { if (isShortMode(color)) { return (Integer.parseInt(color.substring(1, 2), HEX_BASE) * SCALE_SHORT_MODE) / MAX_INT_VALUE; } else { return Integer.parseInt(color.substring(1, 3), HEX_BASE) / MAX_INT_VALUE; } } /** * helper to get green from a string value. * * @param color color string * @return extracted green */ private float getGFromString(@Nonnull final String color) { if (isShortMode(color)) { return (Integer.parseInt(color.substring(2, 3), HEX_BASE) * SCALE_SHORT_MODE) / MAX_INT_VALUE; } else { return Integer.parseInt(color.substring(3, 5), HEX_BASE) / MAX_INT_VALUE; } } /** * helper to get blue from a string value. * * @param color color string * @return extracted blue */ private float getBFromString(@Nonnull final String color) { if (isShortMode(color)) { return (Integer.parseInt(color.substring(3, 4), HEX_BASE) * SCALE_SHORT_MODE) / MAX_INT_VALUE; } else { return Integer.parseInt(color.substring(5, 7), HEX_BASE) / MAX_INT_VALUE; } } /** * helper to get alpha from a string value. * * @param color color string * @return alpha value */ private float getAFromString(@Nonnull final String color) { if (isShortMode(color)) { return (Integer.parseInt(color.substring(4, 5), HEX_BASE) * SCALE_SHORT_MODE) / MAX_INT_VALUE; } else { return Integer.parseInt(color.substring(7, 9), HEX_BASE) / MAX_INT_VALUE; } } /** * Returns true when the given string is from format: #ffff and false when #ffffffff. * * @param color the color string * @return true or false */ private boolean isShortMode(final String color) { return ColorValidator.isShortMode(color) || ColorValidator.isShortModeWithoutAlpha(color); } /** * Multiply all components with the given factor. * * @param color the color used as base colors * @param factor factor to multiply */ public void multiply(@Nonnull final Color color, final float factor) { red = color.red * factor; green = color.green * factor; blue = color.blue * factor; alpha = color.alpha * factor; } /** * Multiply all components with the given factor. * * @param color the color used as base colors * @param factor factor to multiply * @deprecated Typo in function name. Use {@link #multiply(Color, float)} * TODO: Remove in Nifty 2.0 */ @Deprecated public void mutiply(@Nonnull final Color color, final float factor) { multiply(color, factor); } /** * convert color to string. * * @return string representation */ @Override @Nonnull public String toString() { return "(" + red + "," + green + "," + blue + "," + alpha + ")"; } /** * Set color alpha. * * @param newColorAlpha new color alpha */ @Nonnull public Color setAlpha(final float newColorAlpha) { alpha = newColorAlpha; return this; } /** * Set red. * * @param newRed new red value */ public void setRed(final float newRed) { red = newRed; } /** * Set green. * * @param newGreen new green value */ public void setGreen(final float newGreen) { green = newGreen; } /** * Set blue. * * @param newBlue new blue value */ public void setBlue(final float newBlue) { blue = newBlue; } public static boolean check(final String color) { if (ColorValidator.isShortModeWithoutAlpha(color) || ColorValidator.isLongModeWithoutAlpha(color) || ColorValidator.isValid(color)) { return true; } return false; } public void fromString(@Nonnull final String color) { colorString = color; if (ColorValidator.isShortModeWithoutAlpha(color)) { red = getRFromString(color); green = getGFromString(color); blue = getBFromString(color); alpha = 1.0f; if (log.isLoggable(Level.FINE)) { log.fine("found short mode color [" + color + "] with missing alpha value automatically adjusted with alpha " + "value of [#f]"); } } else if (ColorValidator.isLongModeWithoutAlpha(color)) { red = getRFromString(color); green = getGFromString(color); blue = getBFromString(color); alpha = 1.0f; if (log.isLoggable(Level.FINE)) { log.fine("found long mode color [" + color + "] with missing alpha value automatically adjusted with alpha " + "value of [#ff]"); } } else if (ColorValidator.isValid(color)) { red = getRFromString(color); green = getGFromString(color); blue = getBFromString(color); alpha = getAFromString(color); } else { if (log.isLoggable(Level.FINE)) { log.fine("error parsing color [" + color + "] automatically adjusted to white [#ffffffff]"); } red = green = blue = alpha = 1.0f; } } public void fromStringWithoutAlpha(@Nonnull final String color) { colorString = color + toHex(alpha); if (ColorValidator.isShortModeWithoutAlpha(color)) { red = getRFromString(color); green = getGFromString(color); blue = getBFromString(color); } else if (ColorValidator.isLongModeWithoutAlpha(color)) { red = getRFromString(color); green = getGFromString(color); blue = getBFromString(color); } else if (ColorValidator.isValid(color)) { red = getRFromString(color); green = getGFromString(color); blue = getBFromString(color); } else { red = green = blue = 1.0f; } } @Nonnull public static Color randomColor() { Random random = new Random(); return new Color(random.nextFloat(), random.nextFloat(), random.nextFloat(), 1.f); } }