/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9.0b2)
- * Copyright (C) 2015 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
package jalview.util;
import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Random;
public class ColorUtils
{
+ private static final int MAX_CACHE_SIZE = 1729;
+ /*
+ * a cache for colours generated from text strings
+ */
+ static Map<String, Color> myColours = new HashMap<>();
/**
* Generates a random color, will mix with input color. Code taken from
return col == null ? null : col.brighter().brighter().brighter();
}
+ /**
+ * Returns a color between minColour and maxColour; the RGB values are in
+ * proportion to where 'value' lies between minValue and maxValue
+ *
+ * @param value
+ * @param minValue
+ * @param minColour
+ * @param maxValue
+ * @param maxColour
+ * @return
+ */
+ public static Color getGraduatedColour(float value, float minValue,
+ Color minColour, float maxValue, Color maxColour)
+ {
+ if (minValue == maxValue)
+ {
+ return minColour;
+ }
+ if (value < minValue)
+ {
+ value = minValue;
+ }
+ if (value > maxValue)
+ {
+ value = maxValue;
+ }
+
+ /*
+ * prop = proportion of the way value is from minValue to maxValue
+ */
+ float prop = (value - minValue) / (maxValue - minValue);
+ float r = minColour.getRed()
+ + prop * (maxColour.getRed() - minColour.getRed());
+ float g = minColour.getGreen()
+ + prop * (maxColour.getGreen() - minColour.getGreen());
+ float b = minColour.getBlue()
+ + prop * (maxColour.getBlue() - minColour.getBlue());
+ return new Color(r / 255, g / 255, b / 255);
+ }
+
+ /**
+ * 'Fades' the given colour towards white by the specified proportion. A
+ * factor of 1 or more results in White, a factor of 0 leaves the colour
+ * unchanged, and a factor between 0 and 1 results in a proportionate change
+ * of RGB values towards (255, 255, 255).
+ * <p>
+ * A negative bleachFactor can be specified to darken the colour towards Black
+ * (0, 0, 0).
+ *
+ * @param colour
+ * @param bleachFactor
+ * @return
+ */
+ public static Color bleachColour(Color colour, float bleachFactor)
+ {
+ if (bleachFactor >= 1f)
+ {
+ return Color.WHITE;
+ }
+ if (bleachFactor <= -1f)
+ {
+ return Color.BLACK;
+ }
+ if (bleachFactor == 0f)
+ {
+ return colour;
+ }
+
+ int red = colour.getRed();
+ int green = colour.getGreen();
+ int blue = colour.getBlue();
+
+ if (bleachFactor > 0)
+ {
+ red += (255 - red) * bleachFactor;
+ green += (255 - green) * bleachFactor;
+ blue += (255 - blue) * bleachFactor;
+ return new Color(red, green, blue);
+ }
+ else
+ {
+ float factor = 1 + bleachFactor;
+ red *= factor;
+ green *= factor;
+ blue *= factor;
+ return new Color(red, green, blue);
+ }
+ }
+
+ /**
+ * Parses a string into a Color, where the accepted formats are
+ * <ul>
+ * <li>an AWT colour name e.g. white</li>
+ * <li>a hex colour value (without prefix) e.g. ff0000</li>
+ * <li>an rgb triple e.g. 100,50,150</li>
+ * </ul>
+ *
+ * @param colour
+ * @return the parsed colour, or null if parsing fails
+ */
+ public static Color parseColourString(String colour)
+ {
+ if (colour == null)
+ {
+ return null;
+ }
+ colour = colour.trim();
+
+ Color col = null;
+ try
+ {
+ int value = Integer.parseInt(colour, 16);
+ col = new Color(value);
+ } catch (NumberFormatException ex)
+ {
+ }
+
+ if (col == null)
+ {
+ col = ColorUtils.getAWTColorFromName(colour);
+ }
+
+ if (col == null)
+ {
+ try
+ {
+ String[] tokens = colour.split(",");
+ if (tokens.length == 3)
+ {
+ int r = Integer.parseInt(tokens[0].trim());
+ int g = Integer.parseInt(tokens[1].trim());
+ int b = Integer.parseInt(tokens[2].trim());
+ col = new Color(r, g, b);
+ }
+ } catch (Exception ex)
+ {
+ // non-numeric token or out of 0-255 range
+ }
+ }
+
+ return col;
+ }
+
+ /**
+ * Constructs a colour from a text string. The hashcode of the whole string is
+ * scaled to the range 0-135. This is added to RGB values made from the
+ * hashcode of each third of the string, and scaled to the range 20-229.
+ *
+ * @param name
+ * @return
+ */
+ public static Color createColourFromName(String name)
+ {
+ if (name == null)
+ {
+ return Color.white;
+ }
+ if (myColours.containsKey(name))
+ {
+ return myColours.get(name);
+ }
+ int lsize = name.length();
+ int start = 0;
+ int end = lsize / 3;
+
+ int rgbOffset = Math.abs(name.hashCode() % 10) * 15; // 0-135
+
+ /*
+ * red: first third
+ */
+ int r = Math.abs(name.substring(start, end).hashCode() + rgbOffset)
+ % 210 + 20;
+ start = end;
+ end += lsize / 3;
+ if (end > lsize)
+ {
+ end = lsize;
+ }
+
+ /*
+ * green: second third
+ */
+ int g = Math.abs(name.substring(start, end).hashCode() + rgbOffset)
+ % 210 + 20;
+
+ /*
+ * blue: third third
+ */
+ int b = Math.abs(name.substring(end).hashCode() + rgbOffset) % 210 + 20;
+
+ Color color = new Color(r, g, b);
+
+ if (myColours.size() < MAX_CACHE_SIZE)
+ {
+ myColours.put(name, color);
+ }
+
+ return color;
+ }
+
+ /**
+ * Returns the Color constant for a given colour name e.g. "pink", or null if
+ * the name is not recognised
+ *
+ * @param name
+ * @return
+ */
+ public static Color getAWTColorFromName(String name)
+ {
+ if (name == null)
+ {
+ return null;
+ }
+ Color col = null;
+ name = name.toLowerCase();
+
+ // or make a static map; or use reflection on the field name
+ switch (name)
+ {
+ case "black":
+ col = Color.black;
+ break;
+ case "blue":
+ col = Color.blue;
+ break;
+ case "cyan":
+ col = Color.cyan;
+ break;
+ case "darkgray":
+ col = Color.darkGray;
+ break;
+ case "gray":
+ col = Color.gray;
+ break;
+ case "green":
+ col = Color.green;
+ break;
+ case "lightgray":
+ col = Color.lightGray;
+ break;
+ case "magenta":
+ col = Color.magenta;
+ break;
+ case "orange":
+ col = Color.orange;
+ break;
+ case "pink":
+ col = Color.pink;
+ break;
+ case "red":
+ col = Color.red;
+ break;
+ case "white":
+ col = Color.white;
+ break;
+ case "yellow":
+ col = Color.yellow;
+ break;
+ }
+
+ return col;
+ }
}