001 package org.bukkit; 002 003 import java.util.Map; 004 005 import org.apache.commons.lang.Validate; 006 import org.bukkit.configuration.serialization.ConfigurationSerializable; 007 import org.bukkit.configuration.serialization.SerializableAs; 008 009 import com.google.common.collect.ImmutableMap; 010 011 /** 012 * A container for a color palette. This class is immutable; the set methods 013 * return a new color. The color names listed as fields are HTML4 standards, 014 * but subject to change. 015 */ 016 @SerializableAs("Color") 017 public final class Color implements ConfigurationSerializable { 018 private static final int BIT_MASK = 0xff; 019 020 /** 021 * White, or (0xFF,0xFF,0xFF) in (R,G,B) 022 */ 023 public static final Color WHITE = fromRGB(0xFFFFFF); 024 025 /** 026 * Silver, or (0xC0,0xC0,0xC0) in (R,G,B) 027 */ 028 public static final Color SILVER = fromRGB(0xC0C0C0); 029 030 /** 031 * Gray, or (0x80,0x80,0x80) in (R,G,B) 032 */ 033 public static final Color GRAY = fromRGB(0x808080); 034 035 /** 036 * Black, or (0x00,0x00,0x00) in (R,G,B) 037 */ 038 public static final Color BLACK = fromRGB(0x000000); 039 040 /** 041 * Red, or (0xFF,0x00,0x00) in (R,G,B) 042 */ 043 public static final Color RED = fromRGB(0xFF0000); 044 045 /** 046 * Maroon, or (0x80,0x00,0x00) in (R,G,B) 047 */ 048 public static final Color MAROON = fromRGB(0x800000); 049 050 /** 051 * Yellow, or (0xFF,0xFF,0x00) in (R,G,B) 052 */ 053 public static final Color YELLOW = fromRGB(0xFFFF00); 054 055 /** 056 * Olive, or (0x80,0x80,0x00) in (R,G,B) 057 */ 058 public static final Color OLIVE = fromRGB(0x808000); 059 060 /** 061 * Lime, or (0x00,0xFF,0x00) in (R,G,B) 062 */ 063 public static final Color LIME = fromRGB(0x00FF00); 064 065 /** 066 * Green, or (0x00,0x80,0x00) in (R,G,B) 067 */ 068 public static final Color GREEN = fromRGB(0x008000); 069 070 /** 071 * Aqua, or (0x00,0xFF,0xFF) in (R,G,B) 072 */ 073 public static final Color AQUA = fromRGB(0x00FFFF); 074 075 /** 076 * Teal, or (0x00,0x80,0x80) in (R,G,B) 077 */ 078 public static final Color TEAL = fromRGB(0x008080); 079 080 /** 081 * Blue, or (0x00,0x00,0xFF) in (R,G,B) 082 */ 083 public static final Color BLUE = fromRGB(0x0000FF); 084 085 /** 086 * Navy, or (0x00,0x00,0x80) in (R,G,B) 087 */ 088 public static final Color NAVY = fromRGB(0x000080); 089 090 /** 091 * Fuchsia, or (0xFF,0x00,0xFF) in (R,G,B) 092 */ 093 public static final Color FUCHSIA = fromRGB(0xFF00FF); 094 095 /** 096 * Purple, or (0x80,0x00,0x80) in (R,G,B) 097 */ 098 public static final Color PURPLE = fromRGB(0x800080); 099 100 /** 101 * Orange, or (0xFF,0xA5,0x00) in (R,G,B) 102 */ 103 public static final Color ORANGE = fromRGB(0xFFA500); 104 105 private final byte red; 106 private final byte green; 107 private final byte blue; 108 109 /** 110 * Creates a new Color object from a red, green, and blue 111 * 112 * @param red integer from 0-255 113 * @param green integer from 0-255 114 * @param blue integer from 0-255 115 * @return a new Color object for the red, green, blue 116 * @throws IllegalArgumentException if any value is strictly >255 or <0 117 */ 118 public static Color fromRGB(int red, int green, int blue) throws IllegalArgumentException { 119 return new Color(red, green, blue); 120 } 121 122 /** 123 * Creates a new Color object from a blue, green, and red 124 * 125 * @param blue integer from 0-255 126 * @param green integer from 0-255 127 * @param red integer from 0-255 128 * @return a new Color object for the red, green, blue 129 * @throws IllegalArgumentException if any value is strictly >255 or <0 130 */ 131 public static Color fromBGR(int blue, int green, int red) throws IllegalArgumentException { 132 return new Color(red, green, blue); 133 } 134 135 /** 136 * Creates a new color object from an integer that contains the red, 137 * green, and blue bytes in the lowest order 24 bits. 138 * 139 * @param rgb the integer storing the red, green, and blue values 140 * @return a new color object for specified values 141 * @throws IllegalArgumentException if any data is in the highest order 8 142 * bits 143 */ 144 public static Color fromRGB(int rgb) throws IllegalArgumentException { 145 Validate.isTrue((rgb >> 24) == 0, "Extrenuous data in: ", rgb); 146 return fromRGB(rgb >> 16 & BIT_MASK, rgb >> 8 & BIT_MASK, rgb >> 0 & BIT_MASK); 147 } 148 149 /** 150 * Creates a new color object from an integer that contains the blue, 151 * green, and red bytes in the lowest order 24 bits. 152 * 153 * @param bgr the integer storing the blue, green, and red values 154 * @return a new color object for specified values 155 * @throws IllegalArgumentException if any data is in the highest order 8 156 * bits 157 */ 158 public static Color fromBGR(int bgr) throws IllegalArgumentException { 159 Validate.isTrue((bgr >> 24) == 0, "Extrenuous data in: ", bgr); 160 return fromBGR(bgr >> 16 & BIT_MASK, bgr >> 8 & BIT_MASK, bgr >> 0 & BIT_MASK); 161 } 162 163 private Color(int red, int green, int blue) { 164 Validate.isTrue(red >= 0 && red <= BIT_MASK, "Red is not between 0-255: ", red); 165 Validate.isTrue(green >= 0 && green <= BIT_MASK, "Green is not between 0-255: ", green); 166 Validate.isTrue(blue >= 0 && blue <= BIT_MASK, "Blue is not between 0-255: ", blue); 167 168 this.red = (byte) red; 169 this.green = (byte) green; 170 this.blue = (byte) blue; 171 } 172 173 /** 174 * Gets the red component 175 * 176 * @return red component, from 0 to 255 177 */ 178 public int getRed() { 179 return BIT_MASK & red; 180 } 181 182 /** 183 * Creates a new Color object with specified component 184 * 185 * @param red the red component, from 0 to 255 186 * @return a new color object with the red component 187 */ 188 public Color setRed(int red) { 189 return fromRGB(red, getGreen(), getBlue()); 190 } 191 192 /** 193 * Gets the green component 194 * 195 * @return green component, from 0 to 255 196 */ 197 public int getGreen() { 198 return BIT_MASK & green; 199 } 200 201 /** 202 * Creates a new Color object with specified component 203 * 204 * @param green the red component, from 0 to 255 205 * @return a new color object with the red component 206 */ 207 public Color setGreen(int green) { 208 return fromRGB(getRed(), green, getBlue()); 209 } 210 211 /** 212 * Gets the blue component 213 * 214 * @return blue component, from 0 to 255 215 */ 216 public int getBlue() { 217 return BIT_MASK & blue; 218 } 219 220 /** 221 * Creates a new Color object with specified component 222 * 223 * @param blue the red component, from 0 to 255 224 * @return a new color object with the red component 225 */ 226 public Color setBlue(int blue) { 227 return fromRGB(getRed(), getGreen(), blue); 228 } 229 230 /** 231 * 232 * @return An integer representation of this color, as 0xRRGGBB 233 */ 234 public int asRGB() { 235 return getRed() << 16 | getGreen() << 8 | getBlue() << 0; 236 } 237 238 /** 239 * 240 * @return An integer representation of this color, as 0xBBGGRR 241 */ 242 public int asBGR() { 243 return getBlue() << 16 | getGreen() << 8 | getRed() << 0; 244 } 245 246 /** 247 * Creates a new color with its RGB components changed as if it was dyed 248 * with the colors passed in, replicating vanilla workbench dyeing 249 * 250 * @param colors The DyeColors to dye with 251 * @return A new color with the changed rgb components 252 */ 253 // TODO: Javadoc what this method does, not what it mimics. API != Implementation 254 public Color mixDyes(DyeColor... colors) { 255 Validate.noNullElements(colors, "Colors cannot be null"); 256 257 Color[] toPass = new Color[colors.length]; 258 for (int i = 0; i < colors.length; i++) { 259 toPass[i] = colors[i].getColor(); 260 } 261 262 return mixColors(toPass); 263 } 264 265 /** 266 * Creates a new color with its RGB components changed as if it was dyed 267 * with the colors passed in, replicating vanilla workbench dyeing 268 * 269 * @param colors The colors to dye with 270 * @return A new color with the changed rgb components 271 */ 272 // TODO: Javadoc what this method does, not what it mimics. API != Implementation 273 public Color mixColors(Color... colors) { 274 Validate.noNullElements(colors, "Colors cannot be null"); 275 276 int totalRed = this.getRed(); 277 int totalGreen = this.getGreen(); 278 int totalBlue = this.getBlue(); 279 int totalMax = Math.max(Math.max(totalRed, totalGreen), totalBlue); 280 for (Color color : colors) { 281 totalRed += color.getRed(); 282 totalGreen += color.getGreen(); 283 totalBlue += color.getBlue(); 284 totalMax += Math.max(Math.max(color.getRed(), color.getGreen()), color.getBlue()); 285 } 286 287 float averageRed = totalRed / (colors.length + 1); 288 float averageGreen = totalGreen / (colors.length + 1); 289 float averageBlue = totalBlue / (colors.length + 1); 290 float averageMax = totalMax / (colors.length + 1); 291 292 float maximumOfAverages = Math.max(Math.max(averageRed, averageGreen), averageBlue); 293 float gainFactor = averageMax / maximumOfAverages; 294 295 return Color.fromRGB((int) (averageRed * gainFactor), (int) (averageGreen * gainFactor), (int) (averageBlue * gainFactor)); 296 } 297 298 @Override 299 public boolean equals(Object o) { 300 if (!(o instanceof Color)) { 301 return false; 302 } 303 final Color that = (Color) o; 304 return this.blue == that.blue && this.green == that.green && this.red == that.red; 305 } 306 307 @Override 308 public int hashCode() { 309 return asRGB() ^ Color.class.hashCode(); 310 } 311 312 public Map<String, Object> serialize() { 313 return ImmutableMap.<String, Object>of( 314 "RED", getRed(), 315 "BLUE", getBlue(), 316 "GREEN", getGreen() 317 ); 318 } 319 320 @SuppressWarnings("javadoc") 321 public static Color deserialize(Map<String, Object> map) { 322 return fromRGB( 323 asInt("RED", map), 324 asInt("GREEN", map), 325 asInt("BLUE", map) 326 ); 327 } 328 329 private static int asInt(String string, Map<String, Object> map) { 330 Object value = map.get(string); 331 if (value == null) { 332 throw new IllegalArgumentException(string + " not in map " + map); 333 } 334 if (!(value instanceof Number)) { 335 throw new IllegalArgumentException(string + '(' + value + ") is not a number"); 336 } 337 return ((Number) value).intValue(); 338 } 339 340 @Override 341 public String toString() { 342 return "Color:[rgb0x" + Integer.toHexString(getRed()).toUpperCase() + Integer.toHexString(getGreen()).toUpperCase() + Integer.toHexString(getBlue()).toUpperCase() + "]"; 343 } 344 }