diff --git a/common/source/java/ch/systemsx/cisd/common/color/HSBColor.java b/common/source/java/ch/systemsx/cisd/common/color/HSBColor.java new file mode 100644 index 0000000000000000000000000000000000000000..8b01f938efcdb6f8972d7bbc26186f7fff8e6f99 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/color/HSBColor.java @@ -0,0 +1,153 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.common.color; + +import java.awt.Color; + +/** + * A color object in the HSB (Hue, Saturation, Brightness) color space. + * + * @author Bernd Rinn + */ +public class HSBColor +{ + private final float hue; + + private final float saturation; + + private final float brightness; + + /** + * Constructs an HSBColor from the given {@link java.awt.Color}. + */ + public static HSBColor createFromRGBColor(Color color) + { + float[] hsv = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null); + return new HSBColor(hsv[0], hsv[1], hsv[2]); + } + + /** + * Constructor for a pure bright color (saturation = 1, brightness = 1). + * + * @param hue The color's hue. + */ + public HSBColor(float hue) + { + this(hue, 1.0f, 1.0f); + } + + /** + * Constructor for a pure color (saturation = 1). + * + * @param hue The color's hue. + * @param brightness The color's brightness. + */ + public HSBColor(float hue, float brightness) + { + this(hue, 1.0f, brightness); + } + + /** + * Constructor for an arbitrary color. + * + * @param hue The color's hue. + * @param saturation The color's saturation. + * @param brightness The color's brightness. + */ + public HSBColor(float hue, float saturation, float brightness) + { + this.hue = hue; + this.saturation = saturation; + this.brightness = brightness; + } + + /** + * Returns the color's hue. + * + * @return The hue normalized to 1; in the range [0,1) + */ + public float getHue() + { + return hue; + } + + /** + * Returns the color's hue. + * + * @return The hue in degree; in the range [0,360) + */ + public float getHueDegree() + { + return hue * 360f; + } + + /** + * Returns the color's saturation. + * + * @return The saturation; in the range [0,1] + */ + public float getSaturation() + { + return saturation; + } + + /** + * Returns the color's brightness. + * + * @return The brightness; in the range [0,1] + */ + public float getBrightness() + { + return brightness; + } + + /** + * Returns <code>true</code>, if the color is <i>pure</i>, that is: has a saturation of 1.0. + */ + public boolean isPure() + { + return saturation == 1.0f; + } + + /** + * Creates a pure color with the same hue and brightness as this color. + * + * @return The pure color. + */ + public HSBColor createPureColor() + { + return new HSBColor(hue, brightness); + } + + /** + * Creates a pure bright color with the same hue as this color. + * + * @return The pure bright color. + */ + public HSBColor createPureBrightColor() + { + return new HSBColor(hue); + } + + /** + * Returns an {@link java.awt.Color} from this color. + */ + public Color getColor() + { + return Color.getHSBColor(hue, saturation, brightness); + } +} diff --git a/common/source/java/ch/systemsx/cisd/common/color/MixColors.java b/common/source/java/ch/systemsx/cisd/common/color/MixColors.java new file mode 100644 index 0000000000000000000000000000000000000000..343b609bbeae765b49d9265b7d552c47af27cac8 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/color/MixColors.java @@ -0,0 +1,72 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.common.color; + +/** + * A class for calculating a mixed color from a set of pure colors of different relative + * intensities. + * <p> + * It uses an additive (physiological) color mixture. + * + * @author Bernd Rinn + */ +public class MixColors +{ + + private static float[] calcWeights(PureHSBColor[] colors, float[] intensities) + { + final float[] effectiveIntensities = new float[intensities.length]; + float sum = 0.0f; + for (int i = 0; i < intensities.length; ++i) + { + effectiveIntensities[i] = intensities[i] * colors[i].getBrightness(); + sum += effectiveIntensities[i]; + } + final float[] weights = new float[intensities.length]; + for (int i = 0; i < intensities.length; ++i) + { + weights[i] = effectiveIntensities[i] / sum; + } + return weights; + } + + /** + * Calculates a mixed color from given pure <var>colors</var>. + * + * @param colors The colors to mix. + * @param intensities The intensities of each color. Has to be the same length as + * <var>colors</var>. + * @return The mixed color. + */ + public static PureHSBColor calcMixedColor(PureHSBColor[] colors, float[] intensities) + { + assert colors.length == intensities.length; + + final float[] weights = calcWeights(colors, intensities); + + float hue = 0.0f; + float brightness = 0.0f; + for (int i = 0; i < weights.length; ++i) + { + hue += weights[i] * colors[i].getHue(); + brightness = Math.max(brightness, colors[i].getBrightness()); + } + + return new PureHSBColor(hue, brightness); + } + +} diff --git a/common/source/java/ch/systemsx/cisd/common/color/PureHSBColor.java b/common/source/java/ch/systemsx/cisd/common/color/PureHSBColor.java new file mode 100644 index 0000000000000000000000000000000000000000..2ebc326d04e4f9158eee0d1025e56a6cf176d7aa --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/color/PureHSBColor.java @@ -0,0 +1,56 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.common.color; + +import java.awt.Color; + +/** + * A pure color object in the HSB (Hue, Saturation, Brightness) color space. + * <p> + * A pure color is defined by having saturation = 1.0. + * + * @author Bernd Rinn + */ +public class PureHSBColor extends HSBColor +{ + /** + * Constructs a PureHSBColor from the given {@link java.awt.Color}. + * + * @throw {@link IllegalArgumentException} if <var>color</var> is not pure. + */ + public static PureHSBColor createFromRGBColor(Color color) throws IllegalArgumentException + { + float[] hsv = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null); + if (hsv[1] != 1.0f) + { + throw new IllegalArgumentException("Not a pure color: " + color); + } + return new PureHSBColor(hsv[0], hsv[2]); + } + + public PureHSBColor(float hue, float brightness) + { + super(hue, 1.0f, brightness); + } + + @Override + public boolean isPure() + { + return true; + } + +} diff --git a/common/source/java/ch/systemsx/cisd/common/color/WavelengthColor.java b/common/source/java/ch/systemsx/cisd/common/color/WavelengthColor.java new file mode 100644 index 0000000000000000000000000000000000000000..77e58409596d2f9b2f283d8d48fdb38d89d8ba66 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/color/WavelengthColor.java @@ -0,0 +1,157 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.common.color; + +import java.awt.Color; + +/** + * Class to calculate a physiological color for display on a computer monitor for a given wavelength + * of the light using Bruton's algorithm, see <a href="http://www.midnightkite.com/color.html">COLOR + * SCIENCE web page</a> for details. + * + * @author Bernd Rinn + */ +public class WavelengthColor +{ + private final static float NO_GAMMA = 1.0f; + + private final static int MAX_INTENSITY_VALUE = 255; + + private static final int MIN_WAVELENGTH = 380; + + private static final int MAX_WAVELENGTH = 780; + + private static float calcIntensityCorrectionFactor(int wavelength) + { + float factor; + if (wavelength >= MIN_WAVELENGTH && wavelength < 420) + { + factor = 0.3f + 0.7f * (wavelength - MIN_WAVELENGTH) / (420.0f - MIN_WAVELENGTH); + } else if (wavelength >= 420 && wavelength <= 700) + { + factor = 1.0f; + } else if (wavelength > 700 && wavelength <= MAX_WAVELENGTH) + { + factor = 0.3f + 0.7f * (MAX_WAVELENGTH - wavelength) / (MAX_WAVELENGTH - 700.0f); + } else + { + factor = 0.0f; + } + return factor; + } + + private static int adjust(double value, double factor, double gamma) + { + if (value == 0.0) + { + return 0; + } else if (gamma == 1.0) + { + return (int) Math.round(MAX_INTENSITY_VALUE * Math.pow(value * factor, gamma)); + } else + { + return (int) Math.round(MAX_INTENSITY_VALUE * value * factor); + } + } + + /** + * Creates a physiological HSB color for the given <var>wavelength<var> in nanometer. + * <p> + * Does not perform a Gamma correction. + */ + public static PureHSBColor getHSBColorForWavelength(int wavelength) + { + return PureHSBColor.createFromRGBColor(getRGBColorForWavelength(wavelength, NO_GAMMA)); + } + + /** + * Creates a physiological RGB color for the given <var>wavelength<var> in nanometer. + * <p> + * Does not perform a Gamma correction. + */ + public static Color getRGBColorForWavelength(int wavelength) + { + return getRGBColorForWavelength(wavelength, NO_GAMMA); + } + + /** + * Creates a physiological HSB color for the given <var>wavelength<var> in nano-meters. + * <p> + * Performs a Gamma correction with the given <var>gamma</var> value. + */ + public static PureHSBColor getHSBColorForWavelength(int wavelength, float gamma) + { + return PureHSBColor.createFromRGBColor(getRGBColorForWavelength(wavelength, gamma)); + } + /** + * Creates a physiological RGB color for the given <var>wavelength<var> in nano-meters. + * <p> + * Performs a Gamma correction with the given <var>gamma</var> value. + */ + public static Color getRGBColorForWavelength(int wavelength, float gamma) + { + final float red, green, blue; + + if (wavelength >= MIN_WAVELENGTH && wavelength <= 440) + { + red = -(wavelength - 440.0f) / (440.0f - MIN_WAVELENGTH); + green = 0.0f; + blue = 1.0f; + } else if (wavelength > 440 && wavelength <= 490) + { + red = 0.0f; + green = (wavelength - 440.0f) / (490.0f - 440.0f); + blue = 1.0f; + } else if (wavelength > 490 && wavelength <= 510) + { + red = 0.0f; + green = 1.0f; + blue = -(wavelength - 510.0f) / (510.0f - 490.0f); + + } else if (wavelength > 510 && wavelength <= 580) + { + red = (wavelength - 510.0f) / (580.0f - 510.0f); + green = 1.0f; + blue = 0.0f; + } else if (wavelength > 580 && wavelength <= 645) + { + red = 1.0f; + green = -(wavelength - 645.0f) / (645.0f - 580.0f); + blue = 0.0f; + } else if (wavelength > 645 && wavelength <= MAX_WAVELENGTH) + { + red = 1.0f; + green = 0.0f; + blue = 0.0f; + } else + { + // Wavelength is not visible. + red = 0.0f; + green = 0.0f; + blue = 0.0f; + } + + final double factor = calcIntensityCorrectionFactor(wavelength); + + final int r = adjust(red, factor, gamma); + final int g = adjust(green, factor, gamma); + final int b = adjust(blue, factor, gamma); + + return new Color(r, g, b); + } + +} \ No newline at end of file