blob: e40c4a4df89d38ea983e449884e0d64d9e26fb16 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2017, 2018 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.tracecompass.tmf.core.presentation;
import org.eclipse.jdt.annotation.Nullable;
/**
* This class represents a color with its red, green and blue component. The
* red, green and blue values must be between 0 and 255.
*
* @author Yonni Chen
* @since 4.0
*/
public class RGBAColor {
/**
* Size of a circle in radians
*/
public static final double TAU = Math.PI * 2;
private static final int BYTEMASK = 0xFF;
private final short fRed;
private final short fGreen;
private final short fBlue;
private final short fAlpha;
/**
* Generate an RGBColor from an HSV (or HSB) value
*
* @param hue
* the hue
* @param saturation
* the saturation
* @param brightness
* the brightness
* @param alpha
* the alpha
* @return the color
*/
public static final RGBAColor fromHSBA(float hue, float saturation, float brightness, float alpha) {
if (hue < 0 || hue > TAU || saturation < 0 || saturation > 1 ||
brightness < 0 || brightness > 1) {
throw new IllegalArgumentException(String.format("Invalid color value (%f,%f,%f,%f)", hue, saturation, brightness, alpha)); //$NON-NLS-1$
}
double r, g, b;
if (saturation == 0) {
r = g = b = brightness;
} else {
double min = Math.min(TAU, hue);
double quadHue = (min / (Math.PI / 3.0));
int i = (int) quadHue;
double f = quadHue - i;
double p = brightness * (1 - saturation);
double q = brightness * (1 - saturation * f);
double t = brightness * (1 - saturation * (1 - f));
switch (i) {
case 0:
r = brightness;
g = t;
b = p;
break;
case 1:
r = q;
g = brightness;
b = p;
break;
case 2:
r = p;
g = brightness;
b = t;
break;
case 3:
r = p;
g = q;
b = brightness;
break;
case 4:
r = t;
g = p;
b = brightness;
break;
case 5:
default:
r = brightness;
g = p;
b = q;
break;
}
}
return new RGBAColor((int) Math.round(r * 255), (int) Math.round(g * 255), (int) Math.round(b * 255), Math.round(alpha * 255));
}
/**
* String parser, parses the output of {@link RGBAColor#toString()}
*
* @param toString
* the color
* @return the RGBA or null if invalid
* @since 4.1
*/
public static @Nullable RGBAColor fromString(String toString) {
try {
return new RGBAColor(Long.decode(toString).intValue());
} catch (NumberFormatException ne) {
return null;
}
}
/**
* Constructor
*
* @param red
* The red component of the color
* @param green
* The green component of the color
* @param blue
* The blue component of the color
* @param alpha
* The alpha (transparency) component of the color
*/
public RGBAColor(int red, int green, int blue, int alpha) {
if (red > 255 || red < 0) {
throw new IllegalArgumentException("Red component must be between 0 and 255. Was : " + red); //$NON-NLS-1$
}
if (green > 255 || green < 0) {
throw new IllegalArgumentException("Green component must be between 0 and 255. Was : " + green); //$NON-NLS-1$
}
if (blue > 255 || blue < 0) {
throw new IllegalArgumentException("Blue component must be between 0 and 255. Was : " + blue); //$NON-NLS-1$
}
if (alpha > 255 || alpha < 0) {
throw new IllegalArgumentException("Alpha component must be between 0 and 255. Was : " + alpha); //$NON-NLS-1$
}
fRed = (short) red;
fGreen = (short) green;
fBlue = (short) blue;
fAlpha = (short) alpha;
}
/**
* Constructor
*
* @param red
* The red component of the color
* @param green
* The green component of the color
* @param blue
* The blue component of the color
*/
public RGBAColor(int red, int green, int blue) {
this(red, green, blue, BYTEMASK);
}
/**
* Constructor. This constructor extract RGB components from an integer.
*
* <li>0 maps to 0x00000000 or rgba(0, 0, 0, 0)</li>
* <li>-1 maps to 0xFFFFFFFF or rgba(255, 255, 255, 255)</li> <br/>
*
* @param color
* The color as an integer
*/
public RGBAColor(int color) {
fRed = (short) ((color >> 24) & BYTEMASK);
fGreen = (short) ((color >> 16) & BYTEMASK);
fBlue = (short) ((color >> 8) & BYTEMASK);
fAlpha = (short) (color & BYTEMASK);
}
/**
* Gets the red component of the color
*
* @return The red component of the color
*/
public short getRed() {
return fRed;
}
/**
* Gets the green component of the color
*
* @return The green component of the color
*/
public short getGreen() {
return fGreen;
}
/**
* Gets the blue component of the color
*
* @return The blue component of the color
*/
public short getBlue() {
return fBlue;
}
/**
* Gets the alpha component of the color
*
* @return the alpha component of the color
*/
public short getAlpha() {
return fAlpha;
}
/**
* Get the HSBA in encoded floats
*
* @return a <em>float[]</em> of size 4
* <ol>
* <li>the hue in radians</li>
* <li>the saturation between 0 and 1</li>
* <li>the brightness or value between 0 and 1</li>
* <li>the alpha between 0 and 1</li>
* </ol>
*/
public float[] getHSBA() {
float r = fRed / 255f;
float g = fGreen / 255f;
float b = fBlue / 255f;
float max = Math.max(Math.max(r, g), b);
float min = Math.min(Math.min(r, g), b);
float delta = max - min;
float hue = 0;
float brightness = max;
float saturation = max == 0 ? 0 : (max - min) / max;
if (delta != 0) {
if (r == max) {
hue = (g - b) / delta;
} else {
if (g == max) {
hue = 2 + (b - r) / delta;
} else {
hue = 4 + (r - g) / delta;
}
}
hue *= TAU / 6;
if (hue < 0) {
hue += TAU;
}
}
return new float[] { hue, saturation, brightness, fAlpha / 255f };
}
/**
* Get the integer (web color) representation of an RGBColor
*
* @return the integer representation of a color
*/
public int toInt() {
return (getRed() << 24) | (getGreen() << 16) | (getBlue() << 8) | getAlpha();
}
@Override
public String toString() {
return String.format("#%08X", toInt()); //$NON-NLS-1$
}
}