/**
TrakEM2 plugin for ImageJ(C).
Copyright (C) 2005-2009 Albert Cardona and Rodney Douglas.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation (http://www.gnu.org/licenses/gpl.txt )
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
You may contact Albert Cardona at acardona at ini.phys.ethz.ch
Institute of Neuroinformatics, University of Zurich / ETH, Switzerland.
**/
package ini.trakem2.display;
import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.Roi;
import ij.measure.Calibration;
import ij.process.ByteProcessor;
import ij.process.ImageProcessor;
import ij.process.ImageStatistics;
import ini.trakem2.utils.IJError;
import ini.trakem2.utils.ProjectToolbar;
import ini.trakem2.utils.Utils;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.util.Collection;
/** Need a non-null ImagePlus for the ImageCanvas, even if fake. */
public class FakeImagePlus extends ImagePlus {
private int w;
private int h;
private Display display;
private int type;
public FakeImagePlus(int width, int height, Display display) {
w = width;
h = height;
this.display = display;
setProcessor("", new FakeProcessor(width, height));
type = ImagePlus.GRAY8;
}
public void setProcessor(String title, ImageProcessor ip) {
if (! (ip instanceof FakeProcessor)) return;
super.setProcessor(title, ip);
}
public void flush() {} // disabled
protected Display getDisplay() {
return display;
}
public int getType() { return type; }
public int getWidth() {
// trick the canvas, but not the ROIs
//Class dc = null; try { dc = Class.forName("ini.trakem2.DisplayCanvas"); } catch (Exception e) {}
if ((Utils.caller(this)).endsWith("ImageCanvas")) return 4;
return w;
}
public int getHeight() {
// trick the canvas, but not the ROIs
//Class dc = null; try { dc = Class.forName("ini.trakem2.DisplayCanvas"); } catch (Exception e) {}
if ((Utils.caller(this)).endsWith("ImageCanvas")) return 4;
return h;
}
/** Used to resize the canvas. */
public void setDimensions(int width, int height) {
this.w = width;
this.h = height;
}
public int[] getPixel(int x, int y) {
try {
//return display.getLayer().getPixel(x, y, display.getCanvas().getMagnification());
return ((FakeProcessor)getProcessor()).getPixel(display.getCanvas().getMagnification(), x, y, null);
} catch (Exception e) {
IJError.print(e);
}
return new int[4];
}
private class FakeProcessor extends ByteProcessor {
FakeProcessor(int width, int height) {
// create a 4x4 processor (just because, perhaps to skip nulls)
super(4,4);
}
/** Override to return the pixel of the Patch under x,y, if any. */
public int getPixel(final int x, final int y) {
return getPixel(display.getCanvas().getMagnification(), x, y);
}
public int getPixel(double mag, final int x, final int y) {
final Collection<? extends Displayable> under = display.getLayer().find(Patch.class, x, y, true);
if (null == under || under.isEmpty()) return 0; // zeros
for (final Patch p : (Collection<Patch>)under) {
if (!p.isVisible()) continue;
FakeImagePlus.this.type = p.getType(); // for proper value string display
// TODO: edit here when adding layer mipmaps
return p.getPixel(mag, x, y);
}
// Outside images, hence reset:
FakeImagePlus.this.type = ImagePlus.GRAY8;
return 0;
}
/** @param iArray is ignored. */
public int[] getPixel(int x, int y, int[] iArray) {
return getPixel(1.0, x, y, iArray);
}
/** @param iArray is ignored. */
public int[] getPixel(double mag, int x, int y, int[] iArray) {
final Collection<? extends Displayable> under = display.getLayer().find(Patch.class, x, y, true);
if (null != under && !under.isEmpty()) {
for (final Patch p : (Collection<Patch>)under) {
if (!p.isVisible()) continue;
FakeImagePlus.this.type = p.getType(); // for proper value string display
return p.getPixel(mag, x, y, iArray);
}
}
// Outside images, hence reset:
FakeImagePlus.this.type = ImagePlus.GRAY8;
return new int[4];
}
public int getWidth() { return w; }
public int getHeight() { return h; }
public void setColorModel(ColorModel cm) {
display.getSelection().setLut(cm);
}
@Override
public void setPixels(Object ob) {} // disabled
}
// Like ImagePlus.d2s, which is private
private final String doubleToString(final double n) {
return n == (int)n ? Integer.toString((int)n)
: IJ.d2s(n);
}
@Override
public void mouseMoved(final int x, final int y) {
final Calibration cal = getCalibration();
final StringBuilder sb = new StringBuilder(64).append("x=").append(doubleToString(cal.getX(x))).append(' ').append(cal.getUnit())
.append(", y=").append(doubleToString(cal.getY(y))).append(' ').append(cal.getUnit());
if (ProjectToolbar.getToolId() <= ProjectToolbar.SELECT) {
sb.append(", value=");
final int[] v = getPixel(x, y);
switch (type) {
case ImagePlus.GRAY8:
case ImagePlus.GRAY16:
sb.append(v[0]);
break;
case ImagePlus.COLOR_256:
case ImagePlus.COLOR_RGB:
sb.append(v[0]).append(',').append(v[1]).append(',').append(v[2]);
break;
case ImagePlus.GRAY32:
sb.append(Float.intBitsToFloat(v[0]));
break;
default:
sb.setLength(sb.length() -8); // no value info
break;
}
}
// Utils.showStatus would be too slow at reporting, because it waits for fast subsequent calls.
IJ.showStatus(sb.toString());
}
// TODO: use layerset virtualization
public ImageStatistics getStatistics(int mOptions, int nBins, double histMin, double histMax) {
Displayable active = display.getActive();
if (null == active || !(active instanceof Patch)) {
Utils.log("No patch selected.");
return super.getStatistics(mOptions, nBins, histMin, histMax); // TODO can't return null, but something should be done about it.
}
ImagePlus imp = active.getProject().getLoader().fetchImagePlus((Patch)active);
ImageProcessor ip = imp.getProcessor(); // don't create a new onw every time // ((Patch)active).getProcessor();
Roi roi = super.getRoi();
if (null != roi) {
// translate ROI to be meaningful for the Patch
int patch_x = (int)active.getX();
int patch_y = (int)active.getY();
Rectangle r = roi.getBounds();
roi.setLocation(patch_x - r.x, patch_y - r.y);
}
ip.setRoi(roi); // even if null, to reset
ip.setHistogramSize(nBins);
Calibration cal = getCalibration();
if (getType()==GRAY16&& !(histMin==0.0&&histMax==0.0))
{histMin=cal.getRawValue(histMin); histMax=cal.getRawValue(histMax);}
ip.setHistogramRange(histMin, histMax);
ImageStatistics stats = ImageStatistics.getStatistics(ip, mOptions, cal);
ip.setHistogramSize(256);
ip.setHistogramRange(0.0, 0.0);
return stats;
}
/** Returns a virtual stack made of boxes with the dimension of the ROI or the whole layer, so that pixels are retrieved on the fly. */
public ImageStack getStack() {
return null;
}
/** Returns 1. */
public int getStackCount() {
return 1;
//return display.getLayer().getParent().size();
}
public boolean isVirtual() { return true; }
/** Returns the super, which is a dummy 4x4 */ //Returns a virtual stack made of boxes with the dimension of the ROI or the whole layer, so that pixels are retrieved on the fly.
public ImageProcessor getProcessor() {
return super.getProcessor();
}
/** Forward to LayerSet. */
public void setCalibration(Calibration cal) {
try { super.setCalibration(cal); } catch (Throwable e) { IJError.print(e); }
display.getLayer().getParent().setCalibration(cal);
}
public void setCalibrationSuper(Calibration cal) {
super.setCalibration(cal);
}
public Calibration getCalibration() {
// initialization problems with ij1.40a
if (null == display || null == display.getLayer()) return new Calibration();
return display.getLayer().getParent().getCalibrationCopy();
}
/** Forward kill roi to the last_temp of the associated Display. */
public void killRoi() {
if (null!=roi) {
saveRoi();
roi = null;
ImageProcessor ip = getProcessor();
if (null != ip) ip.resetRoi();
}
}
public synchronized void setSlice(int slice) {}
public void updateAndRepaintWindow() {
// TODO: if a selected image is a stack, the LUT applies to it as well...
Display.repaint(display.getLayer(), display.getSelection().getBox(), 0);
}
}