/*
* @(#)CompositeColor.java
*
* Copyright (c) 2010 The authors and contributors of JHotDraw.
*
* You may not use, copy or modify this file, except in compliance with the
* accompanying license terms.
*/
package org.jhotdraw.color;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.awt.Color;
import java.awt.color.ColorSpace;
/**
* {@code CompositeColor} is identical to {@code java.awt.Color} with
* the exception that it allows to construct an instance with color components
* outside of the range of 0 and 1.
*
* @author Werner Randelshofer
* @version $Id$
*/
public class CompositeColor extends Color {
private static final long serialVersionUID = 1L;
/**
* The color value in the native <code>ColorSpace</code> as
* <code>float</code> components (no alpha).
* If <code>null</code> after object construction, this must be an
* sRGB color constructed with 8-bit precision, so compute from the
* <code>int</code> color value.
* @serial
* @see #getRGBColorComponents
* @see #getRGBComponents
*/
@Nullable
private float fvalue[] = null;
/**
* The alpha value as a <code>float</code> component.
* If <code>frgbvalue</code> is <code>null</code>, this is not valid
* data, so compute from the <code>int</code> color value.
* @serial
* @see #getRGBComponents
* @see #getComponents
*/
private float falpha = 0.0f;
/**
* The <code>ColorSpace</code>. If <code>null</code>, then it's
* default is sRGB.
* @serial
* @see #getColor
* @see #getColorSpace
* @see #getColorComponents
*/
@Nullable
private ColorSpace cs = null;
/**
* Creates a color in the specified <code>ColorSpace</code>
* with the color components specified in the <code>float</code>
* array and the specified alpha. The number of components is
* determined by the type of the <code>ColorSpace</code>. For
* example, RGB requires 3 components, but CMYK requires 4
* components.
* @param cspace the <code>ColorSpace</code> to be used to
* interpret the components
* @param components an arbitrary number of color components
* that is compatible with the <code>ColorSpace</code>
* @param alpha alpha value
* @throws IllegalArgumentException if any of the values in the
* <code>components</code> array or <code>alpha</code> is
* outside of the range supported by cspace.
* @see #getComponents
* @see #getColorComponents
*/
public CompositeColor(ColorSpace cspace, float components[], float alpha) {
super(((int) (alpha * 255) << 24) | ColorUtil.toRGB24(cspace, components), true);
boolean rangeError = false;
StringBuilder badComponentString = new StringBuilder();
int n = cspace.getNumComponents();
fvalue = new float[n];
for (int i = 0; i < n; i++) {
if (components[i] < cspace.getMinValue(i) || components[i] > cspace.getMaxValue(i)) {
rangeError = true;
badComponentString.append("Component ");
badComponentString.append(i);
badComponentString.append(' ');
} else {
fvalue[i] = components[i];
}
}
if (alpha < 0.0 || alpha > 1.0) {
rangeError = true;
badComponentString.append("Alpha");
} else {
falpha = alpha;
}
if (rangeError) {
throw new IllegalArgumentException(
"Color parameter outside of expected range: "
+ badComponentString);
}
cs = cspace;
}
/**
* Returns a <code>float</code> array containing the color and alpha
* components of the <code>Color</code>, in the
* <code>ColorSpace</code> of the <code>Color</code>.
* If <code>compArray</code> is <code>null</code>, an array with
* length equal to the number of components in the associated
* <code>ColorSpace</code> plus one is created for
* the return value. Otherwise, <code>compArray</code> must have at
* least this length and it is filled in with the components and
* returned.
* @param compArray an array that this method fills with the color and
* alpha components of this <code>Color</code> in its
* <code>ColorSpace</code> and returns
* @return the color and alpha components in a <code>float</code>
* array.
*/
@Override
public float[] getComponents(float[] compArray) {
if (fvalue == null) {
return getRGBComponents(compArray);
}
float[] f;
int n = fvalue.length;
if (compArray == null) {
f = new float[n + 1];
} else {
f = compArray;
}
for (int i = 0; i < n; i++) {
f[i] = fvalue[i];
}
f[n] = falpha;
return f;
}
/**
* Returns a <code>float</code> array containing only the color
* components of the <code>Color</code>, in the
* <code>ColorSpace</code> of the <code>Color</code>.
* If <code>compArray</code> is <code>null</code>, an array with
* length equal to the number of components in the associated
* <code>ColorSpace</code> is created for
* the return value. Otherwise, <code>compArray</code> must have at
* least this length and it is filled in with the components and
* returned.
* @param compArray an array that this method fills with the color
* components of this <code>Color</code> in its
* <code>ColorSpace</code> and returns
* @return the color components in a <code>float</code> array.
*/
@Override
public float[] getColorComponents(float[] compArray) {
if (fvalue == null) {
return getRGBColorComponents(compArray);
}
float[] f;
int n = fvalue.length;
if (compArray == null) {
f = new float[n];
} else {
f = compArray;
}
for (int i = 0; i < n; i++) {
f[i] = fvalue[i];
}
return f;
}
/**
* Returns a <code>float</code> array containing the color and alpha
* components of the <code>Color</code>, in the
* <code>ColorSpace</code> specified by the <code>cspace</code>
* parameter. If <code>compArray</code> is <code>null</code>, an
* array with length equal to the number of components in
* <code>cspace</code> plus one is created for the return value.
* Otherwise, <code>compArray</code> must have at least this
* length, and it is filled in with the components and returned.
* @param cspace a specified <code>ColorSpace</code>
* @param compArray an array that this method fills with the
* color and alpha components of this <code>Color</code> in
* the specified <code>ColorSpace</code> and returns
* @return the color and alpha components in a <code>float</code>
* array.
*/
@Override
public float[] getComponents(ColorSpace cspace, float[] compArray) {
if (cs == null) {
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
}
float f[];
if (fvalue == null) {
f = new float[3];
f[0] = ((float) getRed()) / 255f;
f[1] = ((float) getGreen()) / 255f;
f[2] = ((float) getBlue()) / 255f;
} else {
f = fvalue;
}
float tmp[] = cs.toCIEXYZ(f);
float tmpout[] = cspace.fromCIEXYZ(tmp);
if (compArray == null) {
compArray = new float[tmpout.length + 1];
}
for (int i = 0; i < tmpout.length; i++) {
compArray[i] = tmpout[i];
}
if (fvalue == null) {
compArray[tmpout.length] = ((float) getAlpha()) / 255f;
} else {
compArray[tmpout.length] = falpha;
}
return compArray;
}
/**
* Returns a <code>float</code> array containing only the color
* components of the <code>Color</code> in the
* <code>ColorSpace</code> specified by the <code>cspace</code>
* parameter. If <code>compArray</code> is <code>null</code>, an array
* with length equal to the number of components in
* <code>cspace</code> is created for the return value. Otherwise,
* <code>compArray</code> must have at least this length, and it is
* filled in with the components and returned.
* @param cspace a specified <code>ColorSpace</code>
* @param compArray an array that this method fills with the color
* components of this <code>Color</code> in the specified
* <code>ColorSpace</code>
* @return the color components in a <code>float</code> array.
*/
@Override
public float[] getColorComponents(ColorSpace cspace, float[] compArray) {
if (cs == null) {
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
}
float f[];
if (fvalue == null) {
f = new float[3];
f[0] = ((float) getRed()) / 255f;
f[1] = ((float) getGreen()) / 255f;
f[2] = ((float) getBlue()) / 255f;
} else {
f = fvalue;
}
float tmp[] = cs.toCIEXYZ(f);
float tmpout[] = cspace.fromCIEXYZ(tmp);
if (compArray == null) {
return tmpout;
}
for (int i = 0; i < tmpout.length; i++) {
compArray[i] = tmpout[i];
}
return compArray;
}
/**
* Returns the <code>ColorSpace</code> of this <code>Color</code>.
* @return this <code>Color</code> object's <code>ColorSpace</code>.
*/
@Override
public ColorSpace getColorSpace() {
if (cs == null) {
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
}
return cs;
}
}