/*
* ome.formats.model.ColorsFactory
*
* Copyright 2006 University of Dundee. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.formats.model;
import static ome.formats.model.UnitsFactory.makeLength;
import java.util.Iterator;
import java.util.List;
import omero.RInt;
import omero.model.Channel;
import omero.model.Filter;
import omero.model.Laser;
import omero.model.LightSource;
import omero.model.LogicalChannel;
import omero.model.TransmittanceRange;
import omero.model.Length;
/**
* Utility class to determine the color usually associated to a specified
* channel depending on its emission wavelength. Ported from the server side
* omeis.providers.re.ColorsFactory.
*
* @author Chris Allan <callan at blackcat dot ca>
*/
public class ColorsFactory {
/** Index of the red component of a color. */
public static final int RED_INDEX = 0;
/** Index of the red component of a color. */
public static final int GREEN_INDEX = 1;
/** Index of the red component of a color. */
public static final int BLUE_INDEX = 2;
/** Index of the red component of a color. */
public static final int ALPHA_INDEX = 3;
/** The Default value for the alpha component. */
static final int DEFAULT_ALPHA = 255;
/**
* Lower bound of the wavelength interval corresponding to a
* <code>BLUE</code> color.
*/
private static final int BLUE_MIN = 400;
/**
* Upper bound of the wavelength interval corresponding to a
* <code>BLUE</code> color and lower bound of the wavelength
* interval corresponding to a <code>GREEN</code> color.
*/
private static final int BLUE_TO_GREEN_MIN = 500;
/**
* Upper bound of the wavelength interval corresponding to a
* <code>GREEN</code> color and lower bound of the wavelength
* interval corresponding to a <code>RED</code> color.
*/
private static final int GREEN_TO_RED_MIN = 560;
/**
* Upper bound of the wavelength interval corresponding to a
* <code>RED</code> color.
*/
private static final int RED_MAX = 700;
/** The value to add to the cut-in to determine the color. */
private static final int RANGE = 15;
/**
* Returns <code>true</code> if the wavelength is in the blue
* color band, <code>false</code> otherwise.
*
* @param wavelength The wavelength to handle.
* @return See above.
*/
private static boolean rangeBlue(double wavelength) {
return wavelength < BLUE_TO_GREEN_MIN;
}
/**
* Returns <code>true</code> if the wavelength is in the green
* color band, <code>false</code> otherwise.
*
* @param wavelength The wavelength to handle.
* @return See above.
*/
private static boolean rangeGreen(double wavelength) {
return wavelength >= BLUE_TO_GREEN_MIN && wavelength < GREEN_TO_RED_MIN;
}
/**
* Returns <code>true</code> if the wavelength is in the red
* color band, <code>false</code> otherwise.
*
* @param wavelength The wavelength to handle.
* @return See above.
*/
private static boolean rangeRed(double wavelength) {
return wavelength >= GREEN_TO_RED_MIN;
}
/**
* Returns the concrete value of an OMERO rtype.
* @param value OMERO rtype to get the value of.
* @return Concrete value of <code>value</code> or <code>null</code> if
* <code>value == null</code>.
*/
private static Integer getValue(RInt value) {
return value == null? null : value.getValue();
}
/**
* Returns <code>true</code> if the channel has emission metadata,
* <code>false</code> otherwise.
*
* @param f The filter to handle.
* @return See above.
*/
private static boolean isFilterHasEmissionData(Filter f) {
if (f == null) return false;
TransmittanceRange transmittance = f.getTransmittanceRange();
if (transmittance == null) return false;
return transmittance.getCutIn() != null;
}
/**
* Returns <code>true</code> if the channel has emission and/or excitation
* information, <code>false</code> otherwise.
*
* @param channelData Channel data to use to determine a color for.
* @param full Pass <code>true</code> to check emission and excitation,
* <code>false</code> to only check emission.
* @return See above.
*/
private static boolean hasEmissionExcitationData(
ChannelData channelData, boolean full)
{
LogicalChannel lc = channelData.getLogicalChannel();
if (lc == null) return false;
if (lc.getEmissionWave() != null) return true;
List<Filter> filters = channelData.getLightPathEmissionFilters();
Iterator<Filter> i;
if (filters != null) {
i = filters.iterator();
while (i.hasNext()) {
if (isFilterHasEmissionData(i.next()))
return true;
}
}
if (channelData.getFilterSet() != null) {
Filter f = channelData.getFilterSetEmissionFilter();
if (isFilterHasEmissionData(f)) return true;
}
if (!full) return false;
//Excitation
//Laser
if (channelData.getLightSource() != null) {
LightSource src = channelData.getLightSource();
if (src instanceof Laser) {
Laser laser = (Laser) src;
if (laser.getWavelength() != null) return true;
}
}
if (lc.getExcitationWave() != null) return true;
filters = channelData.getLightPathExcitationFilters();
if (filters != null) {
i = filters.iterator();
while (i.hasNext()) {
if (isFilterHasEmissionData(i.next()))
return true;
}
}
if (channelData.getFilterSet() != null) {
Filter f = channelData.getFilterSetExcitationFilter();
if (isFilterHasEmissionData(f)) return true;
}
return false;
}
/**
* Returns the range of the wavelength or <code>null</code>.
*
* @param filter The filter to handle.
* @param emission Passed <code>true</code> to indicate that the filter is
* an emission filter, <code>false</code> otherwise.
* @return See above.
*/
static Length getValueFromFilter(Filter filter, boolean emission) {
if (filter == null) return null;
TransmittanceRange transmittance = filter.getTransmittanceRange();
if (transmittance == null) return null;
Length cutIn = transmittance.getCutIn();
if (emission) {
if (cutIn == null) return null;
return makeLength(cutIn.getValue()+RANGE, cutIn.getUnit());
}
Length cutOut = transmittance.getCutOut();
if (cutOut == null) return null;
if (cutIn == null || cutIn.getValue() == 0)
cutIn = makeLength(cutOut.getValue()-2*RANGE, cutOut.getUnit());
// FIXME: are these in the same unit?
Length v = makeLength(
(cutIn.getValue()+cutOut.getValue())/2, cutIn.getUnit());
if (v.getValue() < 0)
return makeLength(
0.0, ome.formats.model.UnitsFactory.TransmittanceRange_CutIn);
return v;
}
/**
* Determines the color usually associated to the specified
* wavelength or explicitly defined for a particular channel.
*
* @param channelData Channel data to use to determine a color for.
*/
public static int[] getColor(ChannelData channelData) {
LogicalChannel lc = channelData.getLogicalChannel();
Channel channel = channelData.getChannel();
if (lc == null) return null;
if (!hasEmissionExcitationData(channelData, true)) {
Integer red = getValue(channel.getRed());
Integer green = getValue(channel.getGreen());
Integer blue = getValue(channel.getBlue());
Integer alpha = getValue(channel.getAlpha());
if (red != null && green != null && blue != null && alpha != null) {
// We've got a color image of some type that has explicitly
// specified which channel is Red, Green, Blue or some other wacky
// color.
//if (red == 0 && green == 0 && blue == 0 && alpha == 0)
// alpha = DEFAULT_ALPHA;
return new int[] { red, green, blue, alpha };
}
// XXX: Is commenting this out right?
//return null;
}
Length valueWavelength = lc.getEmissionWave();
//First we check the emission wavelength.
if (valueWavelength != null) return determineColor(valueWavelength);
Length valueFilter = null;
//First check the emission filter.
//First check if filter
//light path first
List<Filter> filters = channelData.getLightPathEmissionFilters();
Iterator<Filter> i;
if (filters != null) {
i = filters.iterator();
while (valueFilter == null && i.hasNext()) {
valueFilter = getValueFromFilter(i.next(), true);
}
}
if (valueFilter == null)
valueFilter = getValueFromFilter(
channelData.getFilterSetEmissionFilter(), true);
//Laser
if (valueWavelength == null &&
valueFilter == null &&
channelData.getLightSource() != null) {
LightSource ls = channelData.getLightSource();
if (ls instanceof Laser) {
valueWavelength = ((Laser) ls).getWavelength();
}
}
if (valueWavelength != null) return determineColor(valueWavelength);
//Excitation
valueWavelength = lc.getExcitationWave();
if (valueWavelength != null) return determineColor(valueWavelength);
if (valueFilter == null) {
filters = channelData.getLightPathExcitationFilters();
if (filters != null) {
i = filters.iterator();
while (valueFilter == null && i.hasNext()) {
valueFilter = getValueFromFilter(i.next(), false);
}
}
}
if (valueFilter == null)
valueFilter = getValueFromFilter(
channelData.getFilterSetExcitationFilter(), false);
int[] toReturn = determineColor(valueFilter);
if (toReturn != null) {
return toReturn;
}
switch (channelData.getChannelIndex()) {
case 0:
return newRedColor();
case 1:
return newGreenColor();
default:
return newBlueColor();
/*
case 1: return newBlueColor();
default: return newGreenColor();
*/
}
}
/**
* Determines the color corresponding to the passed value.
*
* @param value The value to handle.
*/
public static int[] determineColor(Length value) {
if (value == null) return null;
if (rangeBlue(value.getValue())) return newBlueColor();
if (rangeGreen(value.getValue())) return newGreenColor();
if (rangeRed(value.getValue())) return newRedColor();
return null;
}
/**
* Returns <code>true</code> if the channel has emission metadata,
* <code>false</code> otherwise.
*
* @param channelData Channel data to use to determine a color for.
* @return See above.
*/
public static boolean hasEmissionData(ChannelData channelData) {
return hasEmissionExcitationData(channelData, false);
}
/**
* Creates a new <i>Red</i> Color object.
*
* @return An RGBA array representation of the color Red.
*/
public static int[] newRedColor() {
return new int[] { 255, 0, 0, DEFAULT_ALPHA };
}
/**
* Creates a new <i>Green</i> Color object.
*
* @return An RGBA array representation of the color Green.
*/
public static int[] newGreenColor() {
return new int[] { 0, 255, 0, DEFAULT_ALPHA };
}
/**
* Creates a new <i>Blue</i> Color object.
*
* @return An RGBA array representation of the color Blue.
*/
public static int[] newBlueColor() {
return new int[] { 0, 0, 255, DEFAULT_ALPHA };
}
/**
* Creates a new <i>Grey</i> Color object.
*
* @return An RGBA array representation of the color Blue.
*/
public static int[] newGreyColor() {
return new int[] { 128, 128, 128, DEFAULT_ALPHA };
}
/**
* Creates a new <i>White</i> Color object.
*
* @return An RGBA array representation of the color Blue.
*/
public static int[] newWhiteColor() {
return new int[] { 255, 255, 255, DEFAULT_ALPHA };
}
}