| CalibrationBar |

    package ij.plugin.filter;
    //import ij.plugin.filter.PlugInFilter;
    import ij.*;
    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.*;

    /** This plugin displays a calibration bar on the active image.
        Bob Dougherty, OptiNav, Inc., 4/14/2002
        Based largely on HistogramWindow.java by Wayne Rasband.
        Version 0  4/14/2002
        Version 1  6/14/2002  Many revisions, including addition of the legend to the image.
        Version 2  6/15/2002  Control over colors, including none.  Automatic box width.
        Version 3  6/17/2002  0 decimal places by default.  Option for manual box width.

        July 2002: Modified by Daniel Marsh and renamed CalibrationBar.
    */

    public class CalibrationBar implements PlugInFilter {
        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 int LINE_WIDTH = 1;
        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"};
        static final int UPPER_RIGHT=0, LOWER_RIGHT=1, LOWER_LEFT=2, UPPER_LEFT=3, AT_SELECTION=4;

        static String fillColor = colors[0];
        static String textColor = colors[3];
        static String location = locations[UPPER_RIGHT];
        static double zoom = 1;
        static int numLabels = 5;
        static int fontSize = 12;
        static int decimalPlaces = 0;

        ImagePlus imp;
        ImagePlus impOriginal;

        LiveDialog gd;

        ImageStatistics stats;
        Calibration cal;
        int[] histogram;
        LookUpTable lut;
        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 boldText = false;
        Object backupPixels;
        byte[] byteStorage;
        int[] intStorage;
        short[] shortStorage;
        float[] floatStorage;
        String boxOutlineColor = colors[8];
        String barOutlineColor = colors[3];

        ImagePlus impData;
        ImageProcessor ip;
        String[] fieldNames = null;
        int insetPad;
        boolean decimalPlacesChanged;

        public int setup(String arg, ImagePlus imp) {
            if(imp!=null) {
                this.imp = imp;
                impData = imp;
                if(imp.getRoi()!=null)
                    location = locations[AT_SELECTION];
            }
            return DOES_ALL-DOES_RGB+NO_CHANGES;
        }

        public void run(ImageProcessor ipPassed) {
            ImageCanvas ic = imp.getCanvas();
            double mag = (ic!=null)?ic.getMagnification():1.0;
            if (zoom<=1 && mag<1)
                zoom = (double) 1.0/mag;
            ip = ipPassed.duplicate().convertToRGB();
            impOriginal = imp;
            imp = new ImagePlus(imp.getTitle()+" with bar", ip);
            imp.setCalibration(impData.getCalibration());
            if(impOriginal.getRoi()!=null)
                imp.setRoi(impOriginal.getRoi());
            imp.show();
            ip.snapshot();
            insetPad = imp.getWidth()/50;
            if (insetPad<4)
                insetPad = 4;
            updateColorBar();
            if(!showDialog()) {
                imp.hide();
                ip.reset();
                this.imp.updateAndDraw();
                return;
            }
            updateColorBar();

        }

        private void updateColorBar() {
            ip.reset();
            Roi roi = imp.getRoi();
            if (roi!=null &&  location.equals(locations[AT_SELECTION])) {
                Rectangle r = roi.getBounds();
                drawColorBar(imp,r.x,r.y);
            } else if( location.equals(locations[UPPER_LEFT]))
                drawColorBar(imp,insetPad,insetPad);
            else if(location.equals(locations[UPPER_RIGHT])) {
                calculateWidth();
                drawColorBar(imp,imp.getWidth()-insetPad-win_width,insetPad);
            } else if( location.equals(locations[LOWER_LEFT]) )
                drawColorBar(imp,insetPad,imp.getHeight() - (int)(WIN_HEIGHT*zoom + 2*(int)(YMARGIN*zoom)) - (int)(insetPad*zoom));
            else if(location.equals(locations[LOWER_RIGHT])) {
                calculateWidth();
                drawColorBar(imp,imp.getWidth()-win_width-insetPad,
                             imp.getHeight() - (int)(WIN_HEIGHT*zoom + 2*(int)(YMARGIN*zoom)) - 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);
            gd.addCheckbox("Bold Text", boldText);
            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();
            return true;
        }

        public void verticalColorBar(ImageProcessor ip, 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 = lut.getColorModel();
            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 = impOriginal.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;

                ip.setColor(new Color(rLUT[iMap]&0xff, gLUT[iMap]&0xff, bLUT[iMap]&0xff));
                int j = (int)(BAR_LENGTH*zoom) - i - 1;
                ip.drawLine(x, j+y, thickness+x, j+y);
            }

            Color c = getColor(barOutlineColor);
            if (c != null) {
                ip.setColor(c);
                ip.moveTo(x,y);
                ip.lineTo(x+width,y);
                ip.lineTo(x+width,y+height);
                ip.lineTo(x,y+height);
                ip.lineTo(x,y);
            }
        }

        protected void drawColorBar(ImageProcessor ip, int xOffset, int yOffset) {
            int x, y;

            ip.setColor(Color.black);
            if (decimalPlaces == -1)
                decimalPlaces = Analyzer.getPrecision();
            x = (int)(XMARGIN*zoom) + xOffset;
            y = (int)(YMARGIN*zoom) + yOffset;

            verticalColorBar(ip, x, y, (int)(BAR_THICKNESS*zoom), (int)(BAR_LENGTH*zoom) );
            drawText(ip, x + (int)(BAR_THICKNESS*zoom), y, true);

            Color c = getColor(boxOutlineColor);
            if (c != null && !fillColor.equals("None")) {
                ip.setColor(c);
                ip.setLineWidth(LINE_WIDTH);
                ip.moveTo(xOffset+BOX_PAD,yOffset+BOX_PAD);
                ip.lineTo(xOffset+win_width-BOX_PAD,yOffset+BOX_PAD);
                ip.lineTo(xOffset+win_width-BOX_PAD,yOffset+(int)(WIN_HEIGHT*zoom + 2*(int)(YMARGIN*zoom))-BOX_PAD);
                ip.lineTo(xOffset+BOX_PAD,yOffset+(int)(WIN_HEIGHT*zoom + 2*(int)(YMARGIN*zoom))-BOX_PAD);
                ip.lineTo(xOffset+BOX_PAD,yOffset+BOX_PAD);
            }

        }

        int drawText(ImageProcessor ip, int x, int y, boolean active) {

            Color c = getColor(textColor);
            if (c == null)
                return 0;
            ip.setColor(c);

            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));
            ip.setFont(font);
            ip.setAntialiasedText(true);
            int maxLength = 0;

            //Blank offscreen image for font metrics
            Image img = GUI.createBlankImage(128, 64);
            Graphics g = img.getGraphics();
            FontMetrics metrics = g.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 = impOriginal.getCalibration();
                //s = cal.getValueUnit();
                ImageProcessor ipOrig = impOriginal.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;
                }

                if (active)
                    ip.drawString(d2s(grayLabel), x + 5, yLabel + fontHeight/2);

                int iLength = metrics.stringWidth(d2s(grayLabel));
                if(iLength > maxLength)
                    maxLength = iLength;

            }
            return maxLength;
        }

        String d2s(double d) {
                return IJ.d2s(d,decimalPlaces);
        }

        int getFontHeight() {
            Image img = GUI.createBlankImage(64, 64); //dummy version to get fontHeight
            Graphics g = img.getGraphics();
            int fontType = boldText?Font.BOLD:Font.PLAIN;
            Font font = new Font("SansSerif", fontType, (int) (fontSize*zoom) );
            FontMetrics metrics = g.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() {
            drawColorBar(imp, -1, -1);
        }

        public void drawColorBar(ImagePlus imp, int x, int y) {
            Roi roi = impOriginal.getRoi();
            if (roi!=null)
                impOriginal.killRoi();
            stats = impOriginal.getStatistics(Measurements.MIN_MAX, nBins);
            if (roi!=null)
                impOriginal.setRoi(roi);
            histogram = stats.histogram;
            lut = impOriginal.createLut();
            cal = impOriginal.getCalibration();

            int maxTextWidth = drawText(ip, 0, 0, false);
            win_width = (int)(XMARGIN*zoom) + 5 + (int)(BAR_THICKNESS*zoom) + maxTextWidth + (int)((XMARGIN/2)*zoom);
            if (x==-1 && y==-1)
                return;  // return if calculating width

            Color c = getColor(fillColor);
            if (c != null) {
                ip.setColor(c);
                ip.setRoi(x, y, win_width, (int)(WIN_HEIGHT*zoom + 2*(int)(YMARGIN*zoom)) );
                ip.fill();
            }
            ip.resetRoi();

            drawColorBar(ip,x,y);
            imp.updateAndDraw();

            ip.setColor(Color.black);

        }

        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();
                updateColorBar();
            }

        } //LiveDialog inner class

    }

| CalibrationBar |