| MontageMaker |

    package ij.plugin;
    import ij.*;
    import ij.gui.*;
    import ij.process.*;
    import ij.measure.Calibration;
    import java.awt.*;
    import java.awt.image.*;
    import java.awt.event.*;

    /** Implements the Image/Stacks/Make Montage command. */
    public class MontageMaker implements PlugIn {

        private static int columns, rows, first, last, inc, borderWidth;
        private static double scale;
        private static boolean label;
        private static boolean useForegroundColor;
        private static int saveID;

        public void run(String arg) {
            ImagePlus imp = WindowManager.getCurrentImage();
            if (imp==null || imp.getStackSize()==1)
                {IJ.error("Stack required"); return;}
            makeMontage(imp);
            saveID = imp.getID();
            IJ.register(MontageMaker.class);
        }

        public void makeMontage(ImagePlus imp) {
                int nSlices = imp.getStackSize();
                if (columns==0 || imp.getID()!=saveID) {
                    columns = (int)Math.sqrt(nSlices);
                    rows = columns;
                    int n = nSlices - columns*rows;
                    if (n>0) columns += (int)Math.ceil((double)n/rows);
                    scale = 1.0;
                    if (imp.getWidth()*columns>800)
                        scale = 0.5;
                    if (imp.getWidth()*columns>1600)
                        scale = 0.25;
                    inc = 1;
                    first = 1;
                    last = nSlices;
                }

                GenericDialog gd = new GenericDialog("Make Montage", IJ.getInstance());
                gd.addNumericField("Columns:", columns, 0);
                gd.addNumericField("Rows:", rows, 0);
                gd.addNumericField("Scale Factor:", scale, 2);
                gd.addNumericField("First Slice:", first, 0);
                gd.addNumericField("Last Slice:", last, 0);
                gd.addNumericField("Increment:", inc, 0);
                gd.addNumericField("Border Width:", borderWidth, 0);
                gd.addCheckbox("Label Slices", label);
                gd.addCheckbox("Use Foreground Color", useForegroundColor);
                gd.showDialog();
                if (gd.wasCanceled())
                    return;
                columns = (int)gd.getNextNumber();
                rows = (int)gd.getNextNumber();
                scale = gd.getNextNumber();
                first = (int)gd.getNextNumber();
                last = (int)gd.getNextNumber();
                inc = (int)gd.getNextNumber();
                borderWidth = (int)gd.getNextNumber();
                if (borderWidth<0) borderWidth = 0;
                if (first<1) first = 1;
                if (last>nSlices) last = nSlices;
                if (first>last)
                    {first=1; last=nSlices;}
                if (inc<1) inc = 1;
                if (gd.invalidNumber()) {
                    IJ.error("Invalid number");
                    return;
                }
                label = gd.getNextBoolean();
                useForegroundColor = gd.getNextBoolean();
                makeMontage(imp, columns, rows, scale, first, last, inc, borderWidth, label);
        }

        public void makeMontage(ImagePlus imp, int columns, int rows, double scale, int first, int last, int inc, int borderWidth, boolean labels) {
            int stackWidth = imp.getWidth();
            int stackHeight = imp.getHeight();
            int nSlices = imp.getStackSize();
            int width = (int)(stackWidth*scale);
            int height = (int)(stackHeight*scale);
            int montageWidth = width*columns;
            int montageHeight = height*rows;
            ImageProcessor ip = imp.getProcessor();
            ImageProcessor montage = ip.createProcessor(montageWidth+borderWidth/2, montageHeight+borderWidth/2);
            Color fgColor=Color.white;
            Color bgColor = Color.black;
            if (useForegroundColor) {
                fgColor = Toolbar.getForegroundColor();
                bgColor = Toolbar.getBackgroundColor();
            } else {
                boolean whiteBackground = false;
                if ((ip instanceof ByteProcessor) || (ip instanceof ColorProcessor)) {
                    ImageStatistics is = imp.getStatistics();
                    whiteBackground = is.mode>=200;
                    if (imp.isInvertedLut())
                        whiteBackground = !whiteBackground;
                }
                if (whiteBackground) {
                    fgColor=Color.black;
                    bgColor = Color.white;
                }
            }
            montage.setColor(bgColor);
            montage.fill();
            montage.setColor(fgColor);
            ImageStack stack = imp.getStack();
            int x = 0;
            int y = 0;
            ImageProcessor aSlice;
            int slice = first;
            while (slice<=last) {
                aSlice = stack.getProcessor(slice);
                if (scale!=1.0)
                    aSlice = aSlice.resize(width, height);
                montage.insert(aSlice, x, y);
                String label = stack.getShortSliceLabel(slice);
                if (borderWidth>0) drawBorder(montage, x, y, width, height, borderWidth);
                if (labels) drawLabel(montage, slice, label, x, y, width, height);
                x += width;
                if (x>=montageWidth) {
                    x = 0;
                    y += height;
                    if (y>=montageHeight)
                        break;
                }
                IJ.showProgress((double)(slice-first)/(last-first));
                slice += inc;
            }
            if (borderWidth>0) {
                int w2 = borderWidth/2;
                drawBorder(montage, w2, w2, montageWidth-w2, montageHeight-w2, borderWidth);
            }
            IJ.showProgress(1.0);
            ImagePlus imp2 = new ImagePlus("Montage", montage);
            imp2.setCalibration(imp.getCalibration());
            Calibration cal = imp2.getCalibration();
            if (cal.scaled()) {
                cal.pixelWidth /= scale;
                cal.pixelHeight /= scale;
            }
            imp2.setProperty("Info", "xMontage="+columns+"\nyMontage="+rows+"\n");
            imp2.show();
        }

        void drawBorder(ImageProcessor montage, int x, int y, int width, int height, int borderWidth) {
            montage.setLineWidth(borderWidth);
            montage.moveTo(x, y);
            montage.lineTo(x+width, y);
            montage.lineTo(x+width, y+height);
            montage.lineTo(x, y+height);
            montage.lineTo(x, y);
        }

        void drawLabel(ImageProcessor montage, int slice, String label, int x, int y, int width, int height) {
            if (label!=null && !label.equals("") && montage.getStringWidth(label)>=width) {
                do {
                    label = label.substring(0, label.length()-1);
                } while (label.length()>1 && montage.getStringWidth(label)>=width);
            }
            if (label==null || label.equals(""))
                label = ""+slice;
            int swidth = montage.getStringWidth(label);
            x += width/2 - swidth/2;
            y += height;
            montage.moveTo(x, y);
            montage.drawString(label);
        }
    }

| MontageMaker |