Skip to content
Snippets Groups Projects
Commit 944f1b2e authored by brinn's avatar brinn
Browse files

add: support for computing computer colors for a given wavelength and for computing mixed colors

SVN: 22413
parent 033d67e3
No related branches found
No related tags found
No related merge requests found
/*
* 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);
}
}
/*
* 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);
}
}
/*
* 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;
}
}
/*
* 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment