| Scaler |

    package ij.plugin;
    import ij.*;
    import ij.gui.*;
    import ij.process.*;
    import ij.measure.*;
    import ij.util.Tools;
    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;

    /** This plugin implements the Edit/Scale command. */
    public class Scaler implements PlugIn, TextListener, FocusListener {
        private ImagePlus imp;
        private static String xstr = "0.5";
        private static String ystr = "0.5";
        private static int newWidth, newHeight;
        private static boolean newWindow = true;
        private static boolean interpolate = true;
        private static boolean fillWithBackground;
        private static boolean processStack = true;
        private double xscale;
        private double yscale;
        private String title = "Untitled";
        private Vector fields;
        private double bgValue;
        private boolean constainAspectRatio = true;
        private TextField xField, yField, widthField, heightField;
        private Rectangle r;
        private Object fieldWithFocus;

        public void run(String arg) {
            imp = IJ.getImage();
            Roi roi = imp.getRoi();
            if (roi!=null && !roi.isArea())
                imp.killRoi(); // ignore any line selection
            ImageProcessor ip = imp.getProcessor();
            if (!showDialog(ip))
                return;
            if (ip.getWidth()>1 && ip.getHeight()>1)
                ip.setInterpolate(interpolate);
            else
                ip.setInterpolate(false);
            ip.setBackgroundValue(bgValue);
            imp.startTiming();
            try {
                if (newWindow && imp.getStackSize()>1 && processStack)
                    createNewStack(imp, ip);
                else
                    scale(ip);
            }
            catch(OutOfMemoryError o) {
                IJ.outOfMemory("Scale");
            }
            IJ.showProgress(1.0);
        }

        void createNewStack(ImagePlus imp, ImageProcessor ip) {
            Rectangle r = ip.getRoi();
            boolean crop = r.width!=imp.getWidth() || r.height!=imp.getHeight();
            int nSlices = imp.getStackSize();
            ImageStack stack1 = imp.getStack();
            ImageStack stack2 = new ImageStack(newWidth, newHeight);
            ImageProcessor ip1, ip2;
            boolean interp = interpolate;
            if (imp.getWidth()==1 || imp.getHeight()==1)
                interp = false;
            for (int i=1; i<=nSlices; i++) {
                IJ.showStatus("Scale: " + i + "/" + nSlices);
                ip1 = stack1.getProcessor(i);
                String label = stack1.getSliceLabel(i);
                if (crop) {
                    ip1.setRoi(r);
                    ip1 = ip1.crop();
                }
                ip1.setInterpolate(interp);
                ip2 = ip1.resize(newWidth, newHeight);
                if (ip2!=null)
                    stack2.addSlice(label, ip2);
                IJ.showProgress(i, nSlices);
            }
            ImagePlus imp2 = imp.createImagePlus();
            imp2.setStack(title, stack2);
            Calibration cal = imp2.getCalibration();
            if (cal.scaled()) {
                cal.pixelWidth *= 1.0/xscale;
                cal.pixelHeight *= 1.0/yscale;
            }
            int[] dim = imp.getDimensions();
            imp2.setDimensions(dim[2], dim[3], dim[4]);
            IJ.showProgress(1.0);
            imp2.show();
            imp2.changes = true;
        }

        void scale(ImageProcessor ip) {
            if (newWindow) {
                Rectangle r = ip.getRoi();
                ImagePlus imp2 = imp.createImagePlus();
                imp2.setProcessor(title, ip.resize(newWidth, newHeight));
                Calibration cal = imp2.getCalibration();
                if (cal.scaled()) {
                    cal.pixelWidth *= 1.0/xscale;
                    cal.pixelHeight *= 1.0/yscale;
                }
                imp2.show();
                imp.trimProcessor();
                imp2.trimProcessor();
                imp2.changes = true;
            } else {
                if (processStack && imp.getStackSize()>1) {
                    Undo.reset();
                    StackProcessor sp = new StackProcessor(imp.getStack(), ip);
                    sp.scale(xscale, yscale, bgValue);
                } else {
                    ip.snapshot();
                    Undo.setup(Undo.FILTER, imp);
                    ip.setSnapshotCopyMode(true);
                    ip.scale(xscale, yscale);
                    ip.setSnapshotCopyMode(false);
                }
                imp.killRoi();
                imp.updateAndDraw();
                imp.changes = true;
            }
        }

        boolean showDialog(ImageProcessor ip) {
            int bitDepth = imp.getBitDepth();
            boolean isStack = imp.getStackSize()>1;
            r = ip.getRoi();
            int width = newWidth;
            if (width==0) width = r.width;
            int height = (int)((double)width*r.height/r.width);
            xscale = Tools.parseDouble(xstr, 0.0);
            yscale = Tools.parseDouble(ystr, 0.0);
            if (xscale!=0.0 && yscale!=0.0) {
                width = (int)(r.width*xscale);
                height = (int)(r.height*yscale);
            } else {
                xstr = "-";
                ystr = "-";
            }
            GenericDialog gd = new GenericDialog("Scale");
            gd.addStringField("X Scale (0.05-25):", xstr);
            gd.addStringField("Y Scale (0.05-25):", ystr);
            gd.setInsets(5, 0, 5);
            gd.addStringField("Width (pixels):", ""+width);
            gd.addStringField("Height (pixels):", ""+height);
            fields = gd.getStringFields();
            for (int i=0; i<3; i++) {
                ((TextField)fields.elementAt(i)).addTextListener(this);
                ((TextField)fields.elementAt(i)).addFocusListener(this);
            }
            xField = (TextField)fields.elementAt(0);
            yField = (TextField)fields.elementAt(1);
            widthField = (TextField)fields.elementAt(2);
            heightField = (TextField)fields.elementAt(3);
            fieldWithFocus = xField;
            gd.addCheckbox("Interpolate", interpolate);
            if (bitDepth==8 || bitDepth==24)
                gd.addCheckbox("Fill with Background Color", fillWithBackground);
            if (isStack)
                gd.addCheckbox("Process Entire Stack", processStack);
            gd.addCheckbox("Create New Window", newWindow);
            title = WindowManager.getUniqueName(imp.getTitle());
            gd.setInsets(10, 0, 0);
            gd.addStringField("Title:", title, 12);
            gd.showDialog();
            if (gd.wasCanceled())
                return false;
            xstr = gd.getNextString();
            ystr = gd.getNextString();
            xscale = Tools.parseDouble(xstr, 0.0);
            yscale = Tools.parseDouble(ystr, 0.0);
            String wstr = gd.getNextString();
            newWidth = (int)Tools.parseDouble(wstr, 0);
            newHeight = (int)Tools.parseDouble(gd.getNextString(), 0);
            if (newHeight!=0 && (wstr.equals("-") || wstr.equals("0")))
                    newWidth= (int)(newHeight*(double)r.width/r.height);
            if (newWidth==0 || newHeight==0) {
                IJ.error("Invalid width or height entered");
                return false;
            }
            if (xscale>25.0) xscale = 25.0;
            if (yscale>25.0) yscale = 25.0;
            if (xscale>0.0 && yscale>0.0) {
                newWidth = (int)(r.width*xscale);
                newHeight = (int)(r.height*yscale);
            }
            interpolate = gd.getNextBoolean();
            if (bitDepth==8 || bitDepth==24)
                fillWithBackground = gd.getNextBoolean();
            if (isStack)
                processStack = gd.getNextBoolean();
            newWindow = gd.getNextBoolean();
            if (!newWindow && xscale==0.0) {
                xscale = (double)newWidth/r.width;
                yscale = (double)newHeight/r.height;
            }
            title = gd.getNextString();

            if (fillWithBackground) {
                Color bgc = Toolbar.getBackgroundColor();
                if (bitDepth==8)
                    bgValue = ip.getBestIndex(bgc);
                else if (bitDepth==24)
                    bgValue = bgc.getRGB();
            } else {
                if (bitDepth==8)
                    bgValue = ip.isInvertedLut()?0.0:255.0; // white
                else if (bitDepth==24)
                    bgValue = 0xffffffff; // white
            }
            return true;
        }

        public void textValueChanged(TextEvent e) {
            Object source = e.getSource();
            double newXScale = xscale;
            double newYScale = yscale;
            if (source==xField && fieldWithFocus==xField) {
                String newXText = xField.getText();
                newXScale = Tools.parseDouble(newXText,0);
                if (newXScale==0) return;
                if (newXScale!=xscale) {
                    int newWidth = (int)(newXScale*r.width);
                    widthField.setText(""+newWidth);
                    if (constainAspectRatio) {
                        yField.setText(newXText);
                        int newHeight = (int)(newXScale*r.height);
                        heightField.setText(""+newHeight);
                    }
                }
            } else if (source==yField && fieldWithFocus==yField) {
                String newYText = yField.getText();
                newYScale = Tools.parseDouble(newYText,0);
                if (newYScale==0) return;
                if (newYScale!=yscale) {
                    int newHeight = (int)(newYScale*r.height);
                    heightField.setText(""+newHeight);
                }
            } else if (source==widthField && fieldWithFocus==widthField) {
                int newWidth = (int)Tools.parseDouble(widthField.getText(), 0.0);
                if (newWidth!=0) {
                    int newHeight = (int)(newWidth*(double)r.height/r.width);
                    heightField.setText(""+newHeight);
                    xField.setText("-");
                    yField.setText("-");
                    newXScale = 0.0;
                    newYScale = 0.0;
                }
            }
            xscale = newXScale;
            yscale = newYScale;
        }

        public void focusGained(FocusEvent e) {
            fieldWithFocus = e.getSource();
            if (fieldWithFocus==widthField)
                constainAspectRatio = true;
            else if (fieldWithFocus==yField)
                constainAspectRatio = false;
        }

        public void focusLost(FocusEvent e) {}

    }

| Scaler |