| ImageCalculator |

    package ij.plugin;
    import ij.*;
    import ij.gui.*;
    import ij.process.*;
    import ij.plugin.filter.*;
    import ij.measure.Calibration;
    import ij.plugin.frame.Recorder;

    /** This plugin implements the Process/Image Calculator command. */
    public class ImageCalculator implements PlugIn {

        private static String[] operators = {"Add","Subtract","Multiply","Divide", "AND", "OR", "XOR", "Min", "Max", "Average", "Difference", "Copy"};
        private static String[] lcOperators = {"add","sub","mul","div", "and", "or", "xor", "min", "max", "ave", "diff", "copy"};
        private static int operator;
        private static String title1 = "";
        private static String title2 = "";
        private static boolean createWindow = true;
        private static boolean floatResult;
        private boolean processStack;

        public void run(String arg) {
            int[] wList = WindowManager.getIDList();
            if (wList==null) {
                IJ.noImage();
                return;
            }
            IJ.register(ImageCalculator.class);
            String[] titles = new String[wList.length];
            for (int i=0; i<wList.length; i++) {
                ImagePlus imp = WindowManager.getImage(wList[i]);
                if (imp!=null)
                    titles[i] = imp.getTitle();
                else
                    titles[i] = "";
            }
            GenericDialog gd = new GenericDialog("Image Calculator", IJ.getInstance());
            String defaultItem;
            if (title1.equals(""))
                defaultItem = titles[0];
            else
                defaultItem = title1;
            gd.addChoice("Image1:", titles, defaultItem);
            gd.addChoice("Operation:", operators, operators[operator]);
            if (title2.equals(""))
                defaultItem = titles[0];
            else
                defaultItem = title2;
            gd.addChoice("Image2:", titles, defaultItem);
            //gd.addStringField("Result:", "Result", 10);
            gd.addCheckbox("Create New Window", createWindow);
            gd.addCheckbox("32-bit Result", floatResult);
            gd.showDialog();
            if (gd.wasCanceled())
                return;
            int index1 = gd.getNextChoiceIndex();
            title1 = titles[index1];
            operator = gd.getNextChoiceIndex();
            int index2 = gd.getNextChoiceIndex();
            //String resultTitle = gd.getNextString();
            createWindow = gd.getNextBoolean();
            floatResult = gd.getNextBoolean();
            title2 = titles[index2];
            ImagePlus img1 = WindowManager.getImage(wList[index1]);
            ImagePlus img2 = WindowManager.getImage(wList[index2]);
            calculate(img1, img2, false);
        }

        public void calculate(String params, ImagePlus img1, ImagePlus img2) {
            if (img1==null || img2==null || params==null) return;
            params = params.toLowerCase();
            int op= -1;
            if  (params.indexOf("xor")!=-1)
                op = 6;
            if (op==-1) {
                for (int i=0; i<lcOperators.length; i++) {
                    if (params.indexOf(lcOperators[i])!=-1) {
                        op = i;
                        break;
                    }
                }
            }
            if (op==-1)
                {IJ.error("Image Calclulator", "No valid operator"); return;}
            operator = op;
            createWindow = params.indexOf("create")!=-1;
            floatResult= params.indexOf("32")!=-1 || params.indexOf("float")!=-1;
            processStack = params.indexOf("stack")!=-1;
            calculate(img1, img2, true);
        }

        void calculate(ImagePlus img1, ImagePlus img2, boolean apiCall) {
            if (img1.getCalibration().isSigned16Bit() || img2.getCalibration().isSigned16Bit())
                floatResult = true;
            if (floatResult)
                createWindow = true;
            int size1 = img1.getStackSize();
            int size2 = img2.getStackSize();
            if (apiCall) {
                if (processStack && (size1>1||size2>1))
                    doStackOperation(img1, img2);
                else
                    doOperation(img1, img2);
                return;
            }
            boolean stackOp = false;
            if (size1>1) {
                int result = IJ.setupDialog(img1, 0);
                if (result==PlugInFilter.DONE)
                    return;
                if (result==PlugInFilter.DOES_STACKS) {
                    doStackOperation(img1, img2);
                    stackOp = true;
                } else
                    doOperation(img1, img2);
            } else
                doOperation(img1, img2);
            if (Recorder.record) {
                String params = operators[operator];
                if (createWindow) params += " create";
                if (floatResult) params += " 32-bit";
                if (stackOp) params += " stack";
                Recorder.record("imageCalculator", params, img1.getTitle(), img2.getTitle());
            }
        }

        /** img1 = img2 op img2 (e.g. img1 = img2/img1) */
        void doStackOperation(ImagePlus img1, ImagePlus img2) {
            int size1 = img1.getStackSize();
            int size2 = img2.getStackSize();
            if (size1>1 && size2>1 && size1!=size2) {
                IJ.error("Image Calculator", "'Image1' and 'image2' must be stacks with the same\nnumber of slices, or 'image2' must be a single image.");
                return;
            }
            if (createWindow) {
                img1 = duplicateStack(img1);
                if (img1==null) {
                    IJ.error("Calculator", "Out of memory");
                    return;
                }
                img1.show();
            }
            int mode = getBlitterMode();
            ImageWindow win = img1.getWindow();
            if (win!=null)
                WindowManager.setCurrentWindow(win);
            Undo.reset();
            ImageStack stack1 = img1.getStack();
            StackProcessor sp = new StackProcessor(stack1, img1.getProcessor());
            Calibration cal2 = img2.getCalibration();
            img2.getProcessor().setCalibrationTable(cal2.getCTable());
            try {
                if (size2==1)
                    sp.copyBits(img2.getProcessor(), 0, 0, mode);
                else
                    sp.copyBits(img2.getStack(), 0, 0, mode);
            }
            catch (IllegalArgumentException e) {
                IJ.error("\""+img1.getTitle()+"\": "+e.getMessage());
                return;
            }
            img1.setStack(null, stack1);
            if (img1.getType()!=ImagePlus.GRAY8) {
                img1.getProcessor().resetMinAndMax();
            }
            img1.updateAndDraw();
        }

        void doOperation(ImagePlus img1, ImagePlus img2) {
            int mode = getBlitterMode();
            ImageProcessor ip1 = img1.getProcessor();
            ImageProcessor ip2 = img2.getProcessor();
            Calibration cal1 = img1.getCalibration();
            Calibration cal2 = img2.getCalibration();
            if (createWindow)
                ip1 = createNewImage(ip1, ip2, cal1);
            else {
                ImageWindow win = img1.getWindow();
                if (win!=null)
                    WindowManager.setCurrentWindow(win);
                ip1.snapshot();
                Undo.setup(Undo.FILTER, img1);
            }
            if (floatResult) {
                ip2.setCalibrationTable(cal2.getCTable());
                ip2 = ip2.convertToFloat();
            }
            try {
                ip1.copyBits(ip2, 0, 0, mode);
            }
            catch (IllegalArgumentException e) {
                IJ.error("\""+img1.getTitle()+"\": "+e.getMessage());
                return;
            }
            if (!(ip1 instanceof ByteProcessor))
                ip1.resetMinAndMax();
            if (createWindow) {
                ImagePlus img3 = new ImagePlus("Result of "+img1.getShortTitle(), ip1);
                img3.setCalibration(cal1);
                img3.show();
            } else
                img1.updateAndDraw();
        }

        ImageProcessor createNewImage(ImageProcessor ip1, ImageProcessor ip2, Calibration cal) {
            int width = Math.min(ip1.getWidth(), ip2.getWidth());
            int height = Math.min(ip1.getHeight(), ip2.getHeight());
            ImageProcessor ip3 = ip1.createProcessor(width, height);
            if (floatResult) {
                ip1.setCalibrationTable(cal.getCTable());
                ip1 = ip1.convertToFloat();
                ip3 = ip3.convertToFloat();
            }
            ip3.insert(ip1, 0, 0);
            return ip3;
        }

        private int getBlitterMode() {
            int mode=0;
            switch (operator) {
                case 0: mode = Blitter.ADD; break;
                case 1: mode = Blitter.SUBTRACT; break;
                case 2: mode = Blitter.MULTIPLY; break;
                case 3: mode = Blitter.DIVIDE; break;
                case 4: mode = Blitter.AND; break;
                case 5: mode = Blitter.OR; break;
                case 6: mode = Blitter.XOR; break;
                case 7: mode = Blitter.MIN; break;
                case 8: mode = Blitter.MAX; break;
                case 9: mode = Blitter.AVERAGE; break;
                case 10: mode = Blitter.DIFFERENCE; break;
                case 11: mode = Blitter.COPY; break;
            }
            return mode;
        }

        ImagePlus duplicateStack(ImagePlus img1) {
            Calibration cal = img1.getCalibration();
            ImageStack stack1 = img1.getStack();
            int width = stack1.getWidth();
            int height = stack1.getHeight();
            int n = stack1.getSize();
            ImageStack stack2 = img1.createEmptyStack();
            try {
                for (int i=1; i<=n; i++) {
                    ImageProcessor ip1 = stack1.getProcessor(i);
                    ip1.resetRoi();
                    ImageProcessor ip2 = ip1.crop();
                    if (floatResult) {
                        ip2.setCalibrationTable(cal.getCTable());
                        ip2 = ip2.convertToFloat();
                    }
                    stack2.addSlice(stack1.getSliceLabel(i), ip2);
                }
            }
            catch(OutOfMemoryError e) {
                stack2.trim();
                stack2 = null;
                return null;
            }
            ImagePlus img3 = new ImagePlus("Result of "+img1.getShortTitle(), stack2);
            img3.setCalibration(cal);
            if (img3.getStackSize()==n) {
                int[] dim = img1.getDimensions();
                img3.setDimensions(dim[2], dim[3], dim[4]);
            }
            return img3;
        }

    }

| ImageCalculator |