package ij.plugin;
import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.measure.Calibration;
import ij.macro.Interpreter;
import ij.io.FileInfo;
import java.awt.*;
/** Implements the AddSlice, DeleteSlice and "Stack to Images" commands. */
public class StackEditor implements PlugIn {
ImagePlus imp;
int nSlices, width, height;
static boolean deleteFrames;
public void run(String arg) {
imp = IJ.getImage();
nSlices = imp.getStackSize();
width = imp.getWidth();
height = imp.getHeight();
if (arg.equals("add"))
addSlice();
else if (arg.equals("delete"))
deleteSlice();
else if (arg.equals("toimages"))
convertStackToImages(imp);
}
void addSlice() {
if (imp.isComposite() && nSlices==imp.getNChannels()) {
addChannel();
return;
}
if (imp.isDisplayedHyperStack()) return;
if (!imp.lock()) return;
int id = 0;
ImageStack stack = imp.getStack();
if (stack.getSize()==1) {
String label = stack.getSliceLabel(1);
if (label!=null && label.indexOf("\n")!=-1)
stack.setSliceLabel(null, 1);
Object obj = imp.getProperty("Label");
if (obj!=null && (obj instanceof String))
stack.setSliceLabel((String)obj, 1);
id = imp.getID();
}
ImageProcessor ip = imp.getProcessor();
int n = imp.getCurrentSlice();
if (IJ.altKeyDown()) n--; // insert in front of current slice
stack.addSlice(null, ip.createProcessor(width, height), n);
imp.setStack(null, stack);
imp.setSlice(n+1);
imp.unlock();
if (id!=0) IJ.selectWindow(id); // prevents macros from failing
}
void deleteSlice() {
if (nSlices<2)
{IJ.error("\"Delete Slice\" requires a stack"); return;}
if (imp.isComposite() && nSlices==imp.getNChannels()) {
deleteChannel();
return;
}
if (imp.isDisplayedHyperStack()) {
deleteHyperstackSliceOrFrame();
return;
}
if (!imp.lock()) return;
ImageStack stack = imp.getStack();
int n = imp.getCurrentSlice();
stack.deleteSlice(n);
if (stack.getSize()==1) {
String label = stack.getSliceLabel(1);
if (label!=null) imp.setProperty("Label", label);
}
imp.setStack(null, stack);
if (n--<1) n = 1;
imp.setSlice(n);
imp.unlock();
}
void addChannel() {
int c = imp.getChannel();
ImageStack stack = imp.getStack();
CompositeImage ci = (CompositeImage)imp;
LUT[] luts = ci.getLuts();
ImageProcessor ip = stack.getProcessor(1);
ImageProcessor ip2 = ip.createProcessor(ip.getWidth(), ip.getHeight());
stack.addSlice(null, ip2, c);
ImagePlus imp2 = imp.createImagePlus();
imp2.setStack(stack);
int n = imp2.getStackSize();
imp2 = new CompositeImage(imp, ci.getMode());
LUT lut = LUT.createLutFromColor(Color.white);
int index = 0;
for (int i=1; i<=n; i++) {
if (c+1==index+1) {
((CompositeImage)imp2).setChannelLut(lut, i);
c = -1;
} else
((CompositeImage)imp2).setChannelLut(luts[index++], i);
}
imp.changes = false;
imp.hide();
imp2.show();
}
void deleteChannel() {
int c = imp.getChannel();
ImageStack stack = imp.getStack();
CompositeImage ci = (CompositeImage)imp;
LUT[] luts = ci.getLuts();
stack.deleteSlice(c);
ImagePlus imp2 = imp.createImagePlus();
imp2.setStack(stack);
int n = imp2.getStackSize();
int mode = ci.getMode();
if (mode==CompositeImage.COMPOSITE && n==1)
mode = CompositeImage.COLOR;
imp2 = new CompositeImage(imp, mode);
int index = 0;
for (int i=1; i<=n; i++) {
if (c==index+1) index++;
((CompositeImage)imp2).setChannelLut(luts[index++], i);
}
imp.changes = false;
imp.hide();
imp2.show();
}
void deleteHyperstackSliceOrFrame() {
int channels = imp.getNChannels();
int slices = imp.getNSlices();
int frames = imp.getNFrames();
int c1 = imp.getChannel();
int z1 = imp.getSlice();
int t1 = imp.getFrame();
if (frames>1 && slices==1)
deleteFrames = true;
else if (frames==1 && slices>1)
deleteFrames = false;
else if (slices>1 && frames>1) {
GenericDialog gd = new GenericDialog("Delete Slice");
gd.addCheckbox("Delete time point "+t1, deleteFrames);
gd.showDialog();
if (gd.wasCanceled()) return;
deleteFrames = gd.getNextBoolean();
} else
return;
if (!imp.lock()) return;
ImageStack stack = imp.getStack();
if (deleteFrames) { // delete time point
for (int z=slices; z>=1; z--) {
int index = imp.getStackIndex(channels, z, t1);
for (int i=0; i<channels; i++)
stack.deleteSlice(index-i);
}
frames--;
} else { // delete slice z1 from all volumes
for (int t=frames; t>=1; t--) {
int index = imp.getStackIndex(channels, z1, t);
for (int i=0; i<channels; i++)
stack.deleteSlice(index-i);
}
slices--;
}
imp.setDimensions(channels, slices, frames);
//for (int i=1; i<=stack.getSize(); i++)
// IJ.log(i+" "+stack.getSliceLabel(i)+" "+stack.getProcessor(i).getPixel(0,0));
imp.unlock();
}
public void convertImagesToStack() {
(new ImagesToStack()).run("");
}
public void convertStackToImages(ImagePlus imp) {
if (nSlices<2)
{IJ.error("\"Convert Stack to Images\" requires a stack"); return;}
if (!imp.lock())
return;
ImageStack stack = imp.getStack();
int size = stack.getSize();
if (size>30 && !IJ.isMacro()) {
boolean ok = IJ.showMessageWithCancel("Convert to Images?",
"Are you sure you want to convert this\nstack to "
+size+" separate windows?");
if (!ok)
{imp.unlock(); return;}
}
Calibration cal = imp.getCalibration();
CompositeImage cimg = imp.isComposite()?(CompositeImage)imp:null;
if (imp.getNChannels()!=imp.getStackSize()) cimg = null;
for (int i=1; i<=size; i++) {
String label = stack.getShortSliceLabel(i);
String title = label!=null&&!label.equals("")?label:getTitle(imp, i);
ImageProcessor ip = stack.getProcessor(i);
if (cimg!=null) {
LUT lut = cimg.getChannelLut(i);
if (lut!=null) {
ip.setColorModel(lut);
ip.setMinAndMax(lut.min, lut.max);
}
}
ImagePlus imp2 = new ImagePlus(title, ip);
imp2.setCalibration(cal);
String info = stack.getSliceLabel(i);
if (info!=null && !info.equals(label))
imp2.setProperty("Info", info);
imp2.show();
}
imp.changes = false;
ImageWindow win = imp.getWindow();
if (win!=null)
win.close();
else if (Interpreter.isBatchMode())
Interpreter.removeBatchModeImage(imp);
imp.unlock();
}
String getTitle(ImagePlus imp, int n) {
String digits = "00000000"+n;
return imp.getShortTitle()+"-"+digits.substring(digits.length()-4,digits.length());
}
}