package org.esa.snap.timeseries.ui.matrix;
import org.esa.snap.core.datamodel.ImageInfo;
import org.esa.snap.core.datamodel.RasterDataNode;
import org.esa.snap.jai.ImageManager;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.text.DecimalFormat;
import static java.lang.Math.*;
class MatrixCellRenderer extends DefaultTableCellRenderer {
private final MatrixTableModel tableModel;
private final DecimalFormat valueFormatter;
private final Font boldFont;
private Color invalidColor;
MatrixCellRenderer(MatrixTableModel tableModel) {
this.tableModel = tableModel;
valueFormatter = new DecimalFormat("0.0000");
boldFont = getFont().deriveFont(Font.BOLD);
}
public void setInvalidColor(Color invalidColor) {
this.invalidColor = invalidColor;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
final JLabel label = (JLabel) super.getTableCellRendererComponent(table, value,
isSelected, hasFocus,
row, column);
Double rasterValue = (Double) value;
final RasterDataNode raster = tableModel.getBand();
final String labelText;
Color bgColor;
if (raster != null && rasterValue != null) {
if (Double.isNaN(rasterValue)) { // value is invalid
bgColor = invalidColor;
labelText = "NaN";
} else {
final ImageInfo imageInfo = raster.getImageInfo();
bgColor = ImageManager.computeColor(imageInfo, rasterValue);
// bgColor = raster.getImageInfo().getColorPaletteDef().computeColor(raster, rasterValue);
labelText = valueFormatter.format(rasterValue);
}
} else { // outside of image or no raster set
bgColor = Color.WHITE;
labelText = "";
}
label.setBackground(bgColor);
label.setForeground(findForegroundColor(bgColor));
label.setText(labelText);
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVerticalAlignment(SwingConstants.CENTER);
label.setFont(boldFont);
return label;
}
// From: http://www.w3.org/TR/AERT#color-contrast
// Two colors provide good color visibility if the brightness difference and the color difference between
// the two colors are greater than a set range.
// Color brightness is determined by the following formula:
// ((Red value X 299) + (Green value X 587) + (Blue value X 114)) / 1000
// Note: This algorithm is taken from a formula for converting RGB values to YIQ values. This brightness
// value gives a perceived brightness for a color.
//
// Color difference is determined by the following formula:
// (maximum (Red value 1, Red value 2) - minimum (Red value 1, Red value 2)) +
// (maximum (Green value 1, Green value 2) - minimum (Green value 1, Green value 2)) +
// (maximum (Blue value 1, Blue value 2) - minimum (Blue value 1, Blue value 2))
//
// The threshold for color brightness difference is 125. The threshold for color difference is 500.
// Both values should be over their threshold.
// Good page to experiment with colors: http://contrast-a.dasplankton.com/
private Color findForegroundColor(Color bgColor) {
final int colorDiffWhite = getColorDifference(Color.WHITE, bgColor);
final int colorBrightDiffWhite = getColorBrightnessDifference(Color.WHITE, bgColor);
final int colorDiffBlack = getColorDifference(Color.BLACK, bgColor);
final int colorBrightDiffBlack = getColorBrightnessDifference(Color.BLACK, bgColor);
final boolean colorDiffWhiteGood = colorDiffWhite >= 500;
final boolean colorDiffBlackGood = colorDiffBlack >= 500;
final boolean colorBrightDiffWhiteGood = colorBrightDiffWhite >= 125;
final boolean colorBrightDiffBlackGood = colorBrightDiffBlack >= 125;
// test if both values are good
if (colorDiffWhiteGood && colorBrightDiffWhiteGood) {
return Color.WHITE;
}
if (colorDiffBlackGood && colorBrightDiffBlackGood) {
return Color.BLACK;
}
// test if at least one value is good
if (colorDiffWhiteGood || colorBrightDiffWhiteGood) {
return Color.WHITE;
}
if (colorDiffBlackGood || colorBrightDiffBlackGood) {
return Color.BLACK;
}
// if non of above applies use the best.
if (colorDiffWhite + colorBrightDiffWhite > colorDiffBlack + colorBrightDiffBlack) {
return Color.WHITE;
}
return Color.BLACK;
}
private int getColorBrightnessDifference(Color color1, Color color2) {
int colorBright1 = (color1.getRed() * 299 + color1.getGreen() * 587 + color1.getBlue() * 114) / 1000;
int colorBright2 = (color2.getRed() * 299 + color2.getGreen() * 587 + color2.getBlue() * 114) / 1000;
return abs(colorBright1 - colorBright2);
}
private static int getColorDifference(Color color1, Color color2) {
return max(color1.getRed(), color2.getRed()) - min(color1.getRed(), color2.getRed()) +
max(color1.getGreen(), color2.getGreen()) - min(color1.getGreen(), color2.getGreen()) +
max(color1.getBlue(), color2.getBlue()) - min(color1.getBlue(), color2.getBlue());
}
}