/*
* HSBColor.java, translates RGB colors into Hue-Saturation-Brightness
* colors.
* Copyright (C) 2004 - 2011 Achim Westermann.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* If you modify or optimize the code in a useful way please let me know.
* Achim.Westermann@gmx.de
*
*/
package info.monitorenter.gui.util;
/**
* Color that internally works with the Hue Saturation Luminance color space.
* <p>
*
* @author <a href="mailto:Achim.Westermann@gmx.de">Achim Westermann </a>
*
* @version $Revision: 1.7 $
*/
public class HSBColor implements java.io.Serializable, Cloneable {
/**
* Generated <code>serialVersionUID</code>.
*/
private static final long serialVersionUID = 3257288036910903863L;
/**
* Inspired by
* <code>float[] java.awt.Color.RGBtoHSB(int r,int g, int b, float[]hsbvals)</code>
* except that algorithm is tuned <br>
* Testing results showed about 25% speed up. Therefore the sources have
* become harder to understand.
*
* @param color
* the <code>java.awt.Color</code> (that follows the RGB model) and
* should be transformed to a color instance in the
* hue-saturation-luminance model.
*
* @return the transformed values of the RGB colors in that order:
* hue,saturation,brightness.
*/
public static HSBColor rgbToHSB(final java.awt.Color color) {
// TODO: Fix alpha treatment!!!!
final int rgb = color.getRGB();
final int r = (rgb & 0xFF0000) >> 16;
final int g = (rgb & 0xFF00) >> 8;
final int b = (rgb & 0xFF);
final HSBColor ret = new HSBColor();
final int cmax = (r >= g) ? (r >= b) ? r : b : (g >= b) ? g : b;
final int cmin = (r <= g) ? (r <= b) ? r : b : (g <= b) ? g : b;
ret.m_lum = (cmax) / 255f;
if (cmax != cmin) {
final float difference = (cmax - cmin);
ret.m_sat = difference / (cmax);
if (r == cmax) {
ret.m_hue = (g - b) / difference;
} else if (g == cmax) {
ret.m_hue = (b - r) / difference + 2.0f;
} else {
ret.m_hue = (r - g) / difference + 4.0f;
}
ret.m_hue /= 6.0f;
if (ret.m_hue < 0) {
ret.m_hue += 1.0f;
}
} else {
ret.m_sat = 0;
ret.m_hue = 0;
}
ret.m_alpha = color.getAlpha();
return ret;
}
/** Hue value between 0.0 and 1.0. */
protected double m_hue;
/** Luminance value between 0.0 and 1.0. */
protected double m_lum;
/** Saturation value between 0.0 and 1.0. */
protected double m_sat;
/**
* The unused alpha channel between 0 and 255: stored here for allow
* java.awt.Color instances to be transformed to instances of this class and
* be re - transformed with preserving their alpha setting.
*/
protected double m_alpha;
/**
* Constructor for internal use only.
* <p>
*/
private HSBColor() {
// nop
}
/**
* Creates an instance with the given values for hue saturation and luminance.
* <p>
*
* @param hue
* the hue component of the HSBColor
* @param saturation
* the saturation component of the HSBColor
* @param brightness
* the brightness component of the HSBColor
*/
HSBColor(final double hue, final double saturation, final double brightness) {
this(hue, saturation, brightness, 255);
}
/**
* Creates an instance with the given values for hue saturation, luminance and
* alpha.
* <p>
*
* @param hue
* the hue component of the HSBColor
*
* @param saturation
* the saturation component of the HSBColor
*
* @param brightness
* the brightness component of the HSBColor
*
* @param alpha
* the alpha channed between 0.0 and 1.0.
*/
HSBColor(final double hue, final double saturation, final double brightness, final int alpha) {
this.m_hue = hue;
this.m_sat = saturation;
this.m_lum = brightness;
this.m_alpha = alpha;
}
/**
* Creates an instance transformed from the rgb color.
* <p>
*
* @param rgbcolor
* standard java rgb color.
*/
public HSBColor(final java.awt.Color rgbcolor) {
final int rgb = rgbcolor.getRGB();
final int r = (rgb & 0xFF0000) >> 16;
final int g = (rgb & 0xFF00) >> 8;
final int b = (rgb & 0xFF);
final int cmax = (r >= g) ? (r >= b) ? r : b : (g >= b) ? g : b;
final int cmin = (r <= g) ? (r <= b) ? r : b : (g <= b) ? g : b;
this.m_lum = (cmax) / 255f;
if (cmax != cmin) {
final float difference = (cmax - cmin);
this.m_sat = difference / (cmax);
if (r == cmax) {
this.m_hue = (g - b) / difference;
} else if (g == cmax) {
this.m_hue = (b - r) / difference + 2.0;
} else {
this.m_hue = (r - g) / difference + 4.0;
}
this.m_hue /= 6.0;
if (this.m_hue < 0) {
this.m_hue += 1.0;
}
} else {
this.m_sat = 0;
this.m_hue = 0;
}
this.m_alpha = rgbcolor.getAlpha();
}
/**
* Clone implementation.
* <p>
*
* Following statements are true: <br>
* <code>
* x.clone() != x
* x.clone().getClass() == x.getClass()
* x.clone().equals(x)
* </code> A deep copy of this HSBColor is returned.
* <p>
*
* @return an instance copied from this one.
*/
@Override
public Object clone() {
HSBColor result = null;
try {
result = (HSBColor) super.clone();
result.m_hue = this.m_hue;
result.m_lum = this.m_lum;
result.m_sat = this.m_sat;
result.m_alpha = this.m_alpha;
} catch (final CloneNotSupportedException e) {
result = new HSBColor((float) this.m_hue, (float) this.m_sat, (float) this.m_lum, (int) Math
.round(this.m_alpha));
}
return result;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final HSBColor other = (HSBColor) obj;
if (Double.doubleToLongBits(this.m_alpha) != Double.doubleToLongBits(other.m_alpha)) {
return false;
}
if (Double.doubleToLongBits(this.m_hue) != Double.doubleToLongBits(other.m_hue)) {
return false;
}
if (Double.doubleToLongBits(this.m_lum) != Double.doubleToLongBits(other.m_lum)) {
return false;
}
if (Double.doubleToLongBits(this.m_sat) != Double.doubleToLongBits(other.m_sat)) {
return false;
}
return true;
}
/**
* Returns the transformation of this color to the rgb color.
* <p>
*
* @return the transformation of this color to the rgb color.
*/
public java.awt.Color getRGBColor() {
final int rgb = java.awt.Color.HSBtoRGB((float) this.m_hue, (float) this.m_sat,
(float) this.m_lum);
// This does not work as it filters out the alpha channel!
// return new java.awt.Color(rgb);
final int r = (rgb & 0xff0000) >> 16;
final int g = (rgb & 0x00ff00) >> 8;
final int b = (rgb & 0x0000ff);
return new java.awt.Color(r, g, b, (int) Math.round(this.m_alpha));
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(this.m_alpha);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(this.m_hue);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(this.m_lum);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(this.m_sat);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
}