package ij.plugin;
import ij.*;
import static ij.IJ.createImage;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.io.*;
import java.awt.datatransfer.*;
import ij.gui.*;
import ij.process.*;
import ij.measure.Measurements;
import ij.plugin.filter.Analyzer;
import ij.text.TextWindow;
import ij.measure.*;
public class CalibrationBar implements PlugIn {
public static final double STROKE_WIDTH = 1.0001;
final static int BAR_LENGTH = 128;
final static int BAR_THICKNESS = 12;
final static int XMARGIN = 10;
final static int YMARGIN = 10;
final static int WIN_HEIGHT = BAR_LENGTH;
final static int BOX_PAD = 0;
final static String CALIBRATION_BAR = "|CB|";
static int nBins = 256;
static final String[] colors = {"White","Light Gray","Dark Gray","Black","Red","Green","Blue","Yellow","None"};
static final String[] locations = {"Upper Right","Lower Right","Lower Left", "Upper Left", "At Selection", "Separate Image"};
static final int UPPER_RIGHT=0, LOWER_RIGHT=1, LOWER_LEFT=2, UPPER_LEFT=3, AT_SELECTION=4, SEPARATE_IMAGE = 5;
private static String sFillColor = colors[0];
private static String sTextColor = colors[3];
private static String sLocation = locations[UPPER_RIGHT];
private static double sZoom = 1;
private static int sNumLabels = 5;
private static int sFontSize = 12;
private static int sDecimalPlaces = 0;
private static boolean sFlatten;
private static boolean sBoldText;
private String fillColor = sFillColor;
private String textColor = sTextColor;
private String location = sLocation;
private double zoom = sZoom;
private int numLabels = sNumLabels;
private int fontSize = sFontSize;
private int decimalPlaces = sDecimalPlaces;
private boolean flatten = sFlatten;
private boolean boldText = sBoldText;
ImagePlus imp;
LiveDialog gd;
ImageStatistics stats;
Calibration cal;
int[] histogram;
Image img;
Button setup, redraw, insert, unInsert;
Checkbox ne,nw,se,sw;
CheckboxGroup locGroup;
Label value, note;
int newMaxCount;
boolean logScale;
int win_width;
int userPadding = 0;
int fontHeight = 0;
boolean showUnit;
Object backupPixels;
byte[] byteStorage;
int[] intStorage;
short[] shortStorage;
float[] floatStorage;
String boxOutlineColor = colors[8];
String barOutlineColor = colors[3];
ImageProcessor ip;
String[] fieldNames = null;
int insetPad;
boolean decimalPlacesChanged;
public void run(String arg) {
imp = IJ.getImage();
if (imp.getBitDepth()==24 || imp.getCompositeMode()==IJ.COMPOSITE) {
IJ.error("Calibration Bar", "RGB and composite images are not supported");
return;
}
if (imp.getRoi()!=null && imp.getRoi().isArea())
location = locations[AT_SELECTION];
else if (location.equals(locations[AT_SELECTION]))
location = locations[UPPER_RIGHT];
ImageCanvas ic = imp.getCanvas();
double mag = (ic!=null)?ic.getMagnification():1.0;
if (zoom<=1 && mag<1)
zoom = (double) 1.0/mag;
insetPad = (imp.getWidth()+imp.getHeight())/100;
if (insetPad<4)
insetPad = 4;
updateColorBar();
if (IJ.isMacro()) {
flatten = true;
fillColor = colors[0];
textColor = colors[3];
location = locations[UPPER_RIGHT];
zoom = 1;
numLabels = 5;
fontSize = 12;
decimalPlaces = 0;
}
if (!showDialog()) {
Overlay overlay = imp.getOverlay();
if (overlay!=null) {
overlay.remove(CALIBRATION_BAR);
overlay.setIsCalibrationBar(false);
imp.draw();
}
return;
}
updateColorBar();
boolean separate = location.equals(locations[SEPARATE_IMAGE]);
if (flatten || separate) {
imp.deleteRoi();
IJ.wait(100);
ImagePlus imp2 = null;
if(!separate){
imp2 = imp.flatten();
imp2.setTitle(imp.getTitle()+" with bar");
}
Overlay overlay = imp.getOverlay();
if (overlay!=null) {
if(separate){
Overlay overlaySep = overlay.duplicate();
overlay.setIsCalibrationBar(false);
for (int jj=overlaySep.size()-1; jj>=0; jj--) { Roi roi = overlaySep.get(jj);
if(roi.getName() == null || !roi.getName().equals(CALIBRATION_BAR))
overlaySep.remove(roi);
}
Rectangle r = overlaySep.get(0).getBounds();
overlaySep.translate(-r.x, -r.y);
ImagePlus impSep = IJ.createImage("CBar", "RGB", r.width, r.height, 1);
impSep.setOverlay(overlaySep);
impSep = impSep.flatten(); impSep.setTitle("CBar");
impSep.show();
}
overlay.remove(CALIBRATION_BAR);
imp.draw();
}
if(imp2 != null)
imp2.show();
}
}
private void updateColorBar() {
Roi roi = imp.getRoi();
if (roi!=null && location.equals(locations[AT_SELECTION])) {
Rectangle r = roi.getBounds();
drawBarAsOverlay(imp, r.x, r.y);
} else if ( location.equals(locations[UPPER_LEFT]))
drawBarAsOverlay(imp, insetPad, insetPad);
else if (location.equals(locations[UPPER_RIGHT])) {
calculateWidth();
drawBarAsOverlay(imp, imp.getWidth()-insetPad-win_width, insetPad);
} else if (location.equals(locations[LOWER_LEFT]) )
drawBarAsOverlay(imp, insetPad,imp.getHeight() - (int)(WIN_HEIGHT*zoom + 2*(int)(YMARGIN*zoom)) - (int)(insetPad*zoom));
else if(location.equals(locations[LOWER_RIGHT])) {
calculateWidth();
drawBarAsOverlay(imp, imp.getWidth()-win_width-insetPad,
imp.getHeight() - (int)(WIN_HEIGHT*zoom + 2*(int)(YMARGIN*zoom)) - insetPad);
}
else if ( location.equals(locations[SEPARATE_IMAGE])){
drawBarAsOverlay(imp, insetPad, insetPad);
}
this.imp.updateAndDraw();
}
private boolean showDialog() {
gd = new LiveDialog("Calibration Bar");
gd.addChoice("Location:", locations, location);
gd.addChoice("Fill color: ", colors, fillColor);
gd.addChoice("Label color: ", colors, textColor);
gd.addNumericField("Number of labels:", numLabels, 0);
gd.addNumericField("Decimal places:", decimalPlaces, 0);
gd.addNumericField("Font size:", fontSize, 0);
gd.addNumericField("Zoom factor:", zoom, 1);
String[] labels = {"Bold text", "Overlay", "Show unit"};
boolean[] states = {boldText, !flatten, showUnit};
gd.setInsets(10, 30, 0);
gd.addCheckboxGroup(2, 2, labels, states);
Checkbox overlayBox = (Checkbox)(gd.getCheckboxes().elementAt(1));
if (location.equals(locations[SEPARATE_IMAGE]))
overlayBox.setEnabled(false);
gd.showDialog();
if (gd.wasCanceled())
return false;
location = gd.getNextChoice();
fillColor = gd.getNextChoice();
textColor = gd.getNextChoice();
numLabels = (int)gd.getNextNumber();
decimalPlaces = (int)gd.getNextNumber();
fontSize = (int)gd.getNextNumber();
zoom = (double)gd.getNextNumber();
boldText = gd.getNextBoolean();
flatten = !gd.getNextBoolean();
showUnit = gd.getNextBoolean();
if (!IJ.isMacro()) {
sFlatten = flatten;
sFillColor = fillColor;
sTextColor = textColor;
sLocation = location;
sZoom = zoom;
sNumLabels = numLabels;
sFontSize = fontSize;
sDecimalPlaces = decimalPlaces;
sBoldText = boldText;
}
return true;
}
private void drawBarAsOverlay(ImagePlus imp, int x, int y) {
Roi roi = imp.getRoi();
if (roi!=null)
imp.deleteRoi();
stats = imp.getStatistics(Measurements.MIN_MAX, nBins);
if (roi!=null)
imp.setRoi(roi);
histogram = stats.histogram;
cal = imp.getCalibration();
Overlay overlay = imp.getOverlay();
if (overlay==null)
overlay = new Overlay();
else
overlay.remove(CALIBRATION_BAR);
int maxTextWidth = addText(null, 0, 0);
win_width = (int)(XMARGIN*zoom) + 5 + (int)(BAR_THICKNESS*zoom) + maxTextWidth + (int)((XMARGIN/2)*zoom);
if (x==-1 && y==-1)
return; Color c = getColor(fillColor);
if (c!=null) {
Roi r = new Roi(x, y, win_width, (int)(WIN_HEIGHT*zoom + 2*(int)(YMARGIN*zoom)));
r.setFillColor(c);
overlay.add(r, CALIBRATION_BAR);
}
int xOffset = x;
int yOffset = y;
if (decimalPlaces == -1)
decimalPlaces = Analyzer.getPrecision();
x = (int)(XMARGIN*zoom) + xOffset;
y = (int)(YMARGIN*zoom) + yOffset;
addVerticalColorBar(overlay, x, y, (int)(BAR_THICKNESS*zoom), (int)(BAR_LENGTH*zoom) );
addText(overlay, x + (int)(BAR_THICKNESS*zoom), y);
c = getColor(boxOutlineColor);
overlay.setIsCalibrationBar(true);
if (imp.getCompositeMode()>0) {
for (int i=0; i<overlay.size(); i++)
overlay.get(i).setPosition(imp.getC(), 0, 0);
}
imp.setOverlay(overlay);
}
private void addVerticalColorBar(Overlay overlay, int x, int y, int thickness, int length) {
int width = thickness;
int height = length;
byte[] rLUT,gLUT,bLUT;
int mapSize = 0;
java.awt.image.ColorModel cm = imp.getProcessor().getCurrentColorModel();
if (cm instanceof IndexColorModel) {
IndexColorModel m = (IndexColorModel)cm;
mapSize = m.getMapSize();
rLUT = new byte[mapSize];
gLUT = new byte[mapSize];
bLUT = new byte[mapSize];
m.getReds(rLUT);
m.getGreens(gLUT);
m.getBlues(bLUT);
} else {
mapSize = 256;
rLUT = new byte[mapSize];
gLUT = new byte[mapSize];
bLUT = new byte[mapSize];
for (int i = 0; i < mapSize; i++) {
rLUT[i] = (byte)i;
gLUT[i] = (byte)i;
bLUT[i] = (byte)i;
}
}
double colors = mapSize;
int start = 0;
ImageProcessor ipOrig =imp.getProcessor();
if (ipOrig instanceof ByteProcessor) {
int min = (int)ipOrig.getMin();
if (min<0) min = 0;
int max = (int)ipOrig.getMax();
if (max>255) max = 255;
colors = max-min+1;
start = min;
}
for (int i = 0; i<(int)(BAR_LENGTH*zoom); i++) {
int iMap = start + (int)Math.round((i*colors)/(BAR_LENGTH*zoom));
if (iMap>=mapSize)
iMap =mapSize - 1;
int j = (int)(BAR_LENGTH*zoom) - i - 1;
Line line = new Line(x, j+y, thickness+x, j+y);
line.setStrokeColor(new Color(rLUT[iMap]&0xff, gLUT[iMap]&0xff, bLUT[iMap]&0xff));
line.setStrokeWidth(STROKE_WIDTH);
overlay.add(line, CALIBRATION_BAR);
}
Color c = getColor(barOutlineColor);
if (c!=null) {
Roi r = new Roi(x, y, width, height);
r.setStrokeColor(c);
r.setStrokeWidth(1.0);
overlay.add(r, CALIBRATION_BAR);
}
}
private int addText(Overlay overlay, int x, int y) {
Color c = getColor(textColor);
if (c == null)
return 0;
double hmin = cal.getCValue(stats.histMin);
double hmax = cal.getCValue(stats.histMax);
double barStep = (double)(BAR_LENGTH*zoom) ;
if (numLabels > 2)
barStep /= (numLabels - 1);
int fontType = boldText?Font.BOLD:Font.PLAIN;
Font font = null;
if (fontSize<9)
font = new Font("SansSerif", fontType, 9);
else
font = new Font("SansSerif", fontType, (int)( fontSize*zoom));
int maxLength = 0;
FontMetrics metrics = getFontMetrics(font);
fontHeight = metrics.getHeight();
for (int i = 0; i < numLabels; i++) {
double yLabelD = (int)(YMARGIN*zoom + BAR_LENGTH*zoom - i*barStep - 1);
int yLabel = (int)(Math.round( y + BAR_LENGTH*zoom - i*barStep - 1));
Calibration cal = imp.getCalibration();
String s = "";
if (showUnit)
s = cal.getValueUnit();
ImageProcessor ipOrig = imp.getProcessor();
double min = ipOrig.getMin();
double max = ipOrig.getMax();
if (ipOrig instanceof ByteProcessor) {
if (min<0) min = 0;
if (max>255) max = 255;
}
double grayLabel = min + (max-min)/(numLabels-1) * i;
if (cal.calibrated()) {
grayLabel = cal.getCValue(grayLabel);
double cmin = cal.getCValue(min);
double cmax = cal.getCValue(max);
if (!decimalPlacesChanged && decimalPlaces==0 && ((int)cmax!=cmax||(int)cmin!=cmin))
decimalPlaces = 2;
}
String todisplay = d2s(grayLabel)+" "+s;
if (overlay!=null) {
TextRoi label = new TextRoi(todisplay, x + 5, yLabel + fontHeight/2, font);
label.setStrokeColor(c);
overlay.add(label, CALIBRATION_BAR);
}
int iLength = metrics.stringWidth(todisplay);
if (iLength > maxLength)
maxLength = iLength+5;
}
return maxLength;
}
String d2s(double d) {
return IJ.d2s(d,decimalPlaces);
}
int getFontHeight() {
int fontType = boldText?Font.BOLD:Font.PLAIN;
Font font = new Font("SansSerif", fontType, (int) (fontSize*zoom) );
FontMetrics metrics = getFontMetrics(font);
return metrics.getHeight();
}
Color getColor(String color) {
Color c = Color.white;
if (color.equals(colors[1]))
c = Color.lightGray;
else if (color.equals(colors[2]))
c = Color.darkGray;
else if (color.equals(colors[3]))
c = Color.black;
else if (color.equals(colors[4]))
c = Color.red;
else if (color.equals(colors[5]))
c = Color.green;
else if (color.equals(colors[6]))
c = Color.blue;
else if (color.equals(colors[7]))
c = Color.yellow;
else if (color.equals(colors[8]))
c = null;
return c;
}
void calculateWidth() {
drawBarAsOverlay(imp, -1, -1);
}
private FontMetrics getFontMetrics(Font font) {
BufferedImage bi =new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
Graphics g = (Graphics2D)bi.getGraphics();
g.setFont(font);
return g.getFontMetrics(font);
}
class LiveDialog extends GenericDialog {
LiveDialog(String title) {
super(title);
}
public void textValueChanged(TextEvent e) {
if (fieldNames == null) {
fieldNames = new String[4];
for(int i=0;i<4;i++)
fieldNames[i] = ((TextField)numberField.elementAt(i)).getName();
}
TextField tf = (TextField)e.getSource();
String name = tf.getName();
String value = tf.getText();
if (value.equals(""))
return;
int i=0;
boolean needsRefresh = false;
if (name.equals(fieldNames[0])) {
i = getValue( value ).intValue() ;
if(i<1)
return;
else {
needsRefresh = true;
numLabels = i;
}
} else if (name.equals(fieldNames[1])) {
i = getValue( value ).intValue() ;
if (i<0)
return;
else {
needsRefresh = true;
decimalPlaces = i;
decimalPlacesChanged = true;
}
} else if (name.equals(fieldNames[2])) {
i = getValue( value ).intValue() ;
if(i<1)
return;
else {
needsRefresh = true;
fontSize = i;
}
} else if (name.equals(fieldNames[3])) {
double d = 0;
d = getValue( "0" + value ).doubleValue() ;
if(d<=0)
return;
else {
needsRefresh = true;
zoom = d;
}
}
if (needsRefresh)
updateColorBar();
return;
}
public void itemStateChanged(ItemEvent e) {
location = ( (Choice)(choice.elementAt(0)) ).getSelectedItem();
fillColor = ( (Choice)(choice.elementAt(1)) ).getSelectedItem();
textColor = ( (Choice)(choice.elementAt(2)) ).getSelectedItem();
boldText = ( (Checkbox)(checkbox.elementAt(0)) ).getState();
flatten = !( (Checkbox)(checkbox.elementAt(1)) ).getState();
showUnit = ( (Checkbox)(checkbox.elementAt(2)) ).getState();
Checkbox overlayBox = (Checkbox)(checkbox.elementAt(1) );
if (location.equals(locations[SEPARATE_IMAGE]))
overlayBox.setEnabled(false);
else
overlayBox.setEnabled(true);
updateColorBar();
}
}
}