package org.expeditee.core; import org.expeditee.items.Text; /** * Class representing all colour information in Expeditee. * Internally uses 15-bit channels, but has helper methods for converting * to and from standard 8-bit channels. * * @author cts16 * */ public class Colour implements Representable { private short r, g, b, a; public static final short COMPONENT_MAX_VALUE = Short.MAX_VALUE; public static final short COMPONENT_MIN_VALUE = (short) 0; // Constant colour definitions public static final Colour WHITE = new Colour(COMPONENT_MAX_VALUE, COMPONENT_MAX_VALUE, COMPONENT_MAX_VALUE, COMPONENT_MAX_VALUE); public static final Colour RED = new Colour(COMPONENT_MAX_VALUE, COMPONENT_MIN_VALUE, COMPONENT_MIN_VALUE, COMPONENT_MAX_VALUE); public static final Colour GREEN = new Colour(COMPONENT_MIN_VALUE, COMPONENT_MAX_VALUE, COMPONENT_MIN_VALUE, COMPONENT_MAX_VALUE); public static final Colour BLUE = new Colour(COMPONENT_MIN_VALUE, COMPONENT_MIN_VALUE, COMPONENT_MAX_VALUE, COMPONENT_MAX_VALUE); public static final Colour CYAN = new Colour(COMPONENT_MIN_VALUE, COMPONENT_MAX_VALUE, COMPONENT_MAX_VALUE, COMPONENT_MAX_VALUE); public static final Colour MAGENTA = new Colour(COMPONENT_MAX_VALUE, COMPONENT_MIN_VALUE, COMPONENT_MAX_VALUE, COMPONENT_MAX_VALUE); public static final Colour YELLOW = new Colour(COMPONENT_MAX_VALUE, COMPONENT_MAX_VALUE, COMPONENT_MIN_VALUE, COMPONENT_MAX_VALUE); public static final Colour BLACK = new Colour(COMPONENT_MIN_VALUE, COMPONENT_MIN_VALUE, COMPONENT_MIN_VALUE, COMPONENT_MAX_VALUE); public static final Colour GREY = FromRGBA255(128, 128, 128, 255); public static final Colour DARK_GREY = FromRGBA255(64, 64, 64, 255); public static final Colour LIGHT_GREY = FromRGBA255(192, 192, 192, 255); public static final Colour ORANGE = FromRGBA255(255, 200, 0, 255); public static final Colour PINK = FromRGBA255(255, 175, 175, 255); public static final Colour TRANSPARENT = new Colour(); // Spelling translations of 'grey' to 'gray' to conform to AWT Color names // (because these can be reflected and so might break something if not available) public static final Colour GRAY = GREY; public static final Colour DARK_GRAY = DARK_GREY; public static final Colour LIGHT_GRAY = LIGHT_GREY; /** * Default construct gives transparent black. */ public Colour() { this(0, 0, 0, 0); } /** * Standard constructor taking each component value as a parameter. * * @param r Red channel * @param g Green channel * @param b Blue channel * @param a Alpha channel */ public Colour(short r, short g, short b, short a) { assert(COMPONENT_MIN_VALUE <= r && r <= COMPONENT_MAX_VALUE); assert(COMPONENT_MIN_VALUE <= g && g <= COMPONENT_MAX_VALUE); assert(COMPONENT_MIN_VALUE <= b && b <= COMPONENT_MAX_VALUE); assert(COMPONENT_MIN_VALUE <= a && a <= COMPONENT_MAX_VALUE); this.r = r; this.g = g; this.b = b; this.a = a; } /** Clone constructor. */ public Colour(Colour other) { this(other.r, other.g, other.b, other.a); } /** Convenience constructor taking r,g,b,a as ints. */ public Colour(int r, int g, int b, int a) { this((short) r,(short) g,(short) b,(short) a); } /** Convenience constructor taking r,g,b,a as floats in the range 0.0f to 1.0f. */ public Colour(float r, float g, float b, float a) { this(FromComponentFloat(r), FromComponentFloat(g), FromComponentFloat(b), FromComponentFloat(a)); } /** Constructor defaults alpha to completely opaque. */ public Colour(short r, short g, short b) { this(r, g, b, COMPONENT_MAX_VALUE); } /** Constructor defaults alpha to completely opaque. */ public Colour(int r, int g, int b) { this((short) r,(short) g,(short) b); } /** Constructor defaults alpha to completely opaque. */ public Colour(float r, float g, float b) { this(r, g, b, 1.0f); } /** Creates a colour from r,g,b,a values in the range 0 - 255. */ public static Colour FromRGBA255(int r, int g, int b, int a) { return new Colour(FromComponent255(r), FromComponent255(g), FromComponent255(b), FromComponent255(a)); } /** Creates a colour from r,g,b values in the range 0 - 255. */ public static Colour FromRGB255(int r, int g, int b) { return new Colour(FromComponent255(r), FromComponent255(g), FromComponent255(b)); } /** * Converts a single component value from the range 0 - 255 into * the internal colour range. */ public static short FromComponent255(int component) { assert(0 <= component && component <= 255); return (short) (component << 7); } /** * Converts a single component value from the internal colour range into * the range 0 - 255. */ public static int ToComponent255(short component) { assert(COMPONENT_MIN_VALUE <= component && component <= COMPONENT_MAX_VALUE); return ((int) component) >> 7; } /** * Converts a single component value from the range 0.0f - 1.0f into * the internal colour range. */ public static short FromComponentFloat(float component) { assert(0.0f <= component && component <= 1.0f); return (short) (component * COMPONENT_MAX_VALUE); } /** * Converts a single component value from the internal colour range into * the range 0.0f - 1.0f. */ public static float ToComponentFloat(short component) { assert(COMPONENT_MIN_VALUE <= component && component <= COMPONENT_MAX_VALUE); return ((float) component) / COMPONENT_MAX_VALUE; } @Override public boolean equals(Object other) { if (other instanceof Colour) { Colour c = (Colour) other; return (r == c.r && g == c.g && b == c.b && a == c.a); } return false; } /** Returns a new colour which is this colour made brighter. */ public Colour brighter() { int r = getRed(); int g = getGreen(); int b = getBlue(); int alpha = getAlpha(); int i = FromComponent255((int)(1.0/0.7)); if ( r == 0 && g == 0 && b == 0) { return new Colour(i, i, i, alpha); } if ( r > 0 && r < i ) r = i; if ( g > 0 && g < i ) g = i; if ( b > 0 && b < i ) b = i; return new Colour(r, g, b, alpha).scale(1.0/0.7); } /** Returns a new colour which is this colour made darker. */ public Colour darker() { return scale(0.7); } /** * Returns a new colour which is this colour with the r,g,b * components by the given scale factor. */ public Colour scale(double factor) { if (factor < 0.0) return null; double newR = r * factor; double newG = g * factor; double newB = b * factor; if (newR > COMPONENT_MAX_VALUE) newR = COMPONENT_MAX_VALUE; if (newG > COMPONENT_MAX_VALUE) newG = COMPONENT_MAX_VALUE; if (newB > COMPONENT_MAX_VALUE) newB = COMPONENT_MAX_VALUE; return new Colour((short) newR, (short) newG, (short) newB, a); } public short getRed() { return r; } public short getGreen() { return g; } public short getBlue() { return b; } public short getAlpha() { return a; } public void setRed(short r) { this.r = r; } public void setGreen(short g) { this.g = g; } public void setBlue(short b) { this.b = b; } public void setAlpha(short a) { this.a = a; } public int getRed255() { return ToComponent255(r); } public int getGreen255() { return ToComponent255(g); } public int getBlue255() { return ToComponent255(b); } public int getAlpha255() { return ToComponent255(a); } public float getRedFloat() { return ToComponentFloat(r); } public float getGreenFloat() { return ToComponentFloat(g); } public float getBlueFloat() { return ToComponentFloat(b); } public float getAlphaFloat() { return ToComponentFloat(a); } /** Creates a new colour which is the inverse of this colour. */ public Colour inverse() { return new Colour(COMPONENT_MAX_VALUE - r, COMPONENT_MAX_VALUE - g, COMPONENT_MAX_VALUE - b, a); } /** * Decodes a string into a colour. The format of the string should be * a packed r,g,b integer value e.g. "0xrrggbb". */ public static Colour decode(String nm) throws NumberFormatException { Integer intval = Integer.decode(nm); int i = intval.intValue(); return fromRGB24BitPacked(i); } /** * Returns an integer representing this colour as a 32-bit packed * integer i.e. 0xaarrggbb */ public int getARGB32BitPacked() { return getBlue255() | (getGreen255() << 8) | (getRed255() << 16) | (getAlpha255() << 24); } /** Creates a colour from a 32-bit packed integer in to form 0xaarrggbb. */ public static Colour fromARGB32BitPacked(int rgba) { int a = (rgba >> 24) & 0xFF; int r = (rgba >> 16) & 0xFF; int g = (rgba >> 8) & 0xFF; int b = rgba & 0xFF; return FromRGBA255(r, g, b, a); } /** Creates a colour from a 24-bit packed integer in to form 0xrrggbb. */ public static Colour fromRGB24BitPacked(int rgb) { return fromARGB32BitPacked(rgb | 0xFF000000); } public Colour clone() { return new Colour(this); } @Override public Text generateRepresentation(String content, String frameset) { Text t = new Text(content); t.setColor(this); return t; } @Override public String toString() { return "[Expeditee Colour: " + getRed255() + " " + getGreen255() + " " + getBlue255() + " " + getAlpha255() + "]"; } }