import java.awt.Point; import java.awt.Rectangle; import java.awt.event.*; import java.util.*; import ij.*; import ij.plugin.filter.PlugInFilter; import ij.process.*; import ij.gui.*; /** * Warp is a plugin filter for ImageJ that warps an image * along a straight line selection. * The warping code has been taken from the AlexWarp applet * and rewritten for ImageJ by Ulf Dittmer. * It is hereby released under the same terms as ImageJ. */ public class Warp_ implements PlugInFilter, MouseListener { int mPixels[], mOldPixels[]; int mWidth, mHeight; boolean smooth = true; final int kHOffset = 0; final int kVOffset = 0; ImagePlus imp; ImageProcessor ip; static Vector images = new Vector(); public Warp_() { } public int setup (String _arg, ImagePlus _imp) { this.imp = _imp; IJ.register(Warp_.class); return DOES_ALL; } public void run (ImageProcessor _ip) { Integer id = new Integer(imp.getID()); ImageWindow win = WindowManager.getCurrentImage().getWindow(); if (images.contains(id)) { images.removeElement(id); IJ.log("Warp OFF for " + win.getTitle()); } else { win.getCanvas().addMouseListener(this); images.addElement(id); IJ.log("Warp ON for " + win.getTitle()); } } public void mousePressed (MouseEvent e) {} public void mouseClicked (MouseEvent e) {} public void mouseEntered (MouseEvent e) {} public void mouseExited (MouseEvent e) {} public void mouseReleased (MouseEvent e) { if ((Toolbar.getToolId() != Toolbar.LINE) || (! images.contains(new Integer(imp.getID())))) return; Roi roi = imp.getRoi(); if (roi==null || roi.getType()!=Roi.LINE) return; this.ip = imp.getProcessor(); int x1=0, y1=0, x2=0, y2=0; Line lineRoi = (Line) roi; x1 = lineRoi.x1; y1 = lineRoi.y1; x2 = lineRoi.x2; y2 = lineRoi.y2; Undo.reset(); Undo.setup(Undo.FILTER, imp); mWidth = ip.getWidth(); mHeight = ip.getHeight(); IJ.showStatus("Warping..."); mPixels = new int[mWidth * mHeight]; mOldPixels = new int[mWidth * mHeight]; for (int x=0; x= kVOffset + mHeight) y = (kVOffset + mHeight) - 1; } else if (dy == 0) { if (x < kHOffset) x = kHOffset; if (x >= kHOffset + mWidth) x = (kHOffset + mWidth) - 1; } else { double slope = (double)dy / (double)dx; if (x < kHOffset) { x = kHOffset; y = from.y + (int)(slope * (double)(x - from.x)); } if (x >= kHOffset + mWidth) { x = (kHOffset + mWidth) - 1; y = from.y + (int)(slope * (double)(x - from.x)); } if (y < kVOffset) { y = kVOffset; x = from.x + (int)((double)(y - from.y) / slope); } if (y >= kVOffset + mHeight) { y = (kVOffset + mHeight) - 1; x = from.x + (int)((double)(y - from.y) / slope); } } return new Point(x, y); } class ImageWarper extends Thread { Warp_ parent; Point mFromPoint, mToPoint; int mFromPixels[], mToPixels[]; int mWidth, mHeight; MyColor col; boolean smooth; ImageWarper (Warp_ _parent, int fromPixels[], int toPixels[], int w, int h, Point fromPoint, Point toPoint, boolean _smooth) { parent = _parent; smooth = _smooth; mFromPixels = fromPixels; mToPixels = toPixels; mFromPoint = fromPoint; mToPoint = toPoint; mWidth = w; mHeight = h; col = new MyColor(); } public void run() { WarpPixels(); parent.DoneWithWarping(); } void WarpPixels() { int dx = mToPoint.x - mFromPoint.x; int dy = mToPoint.y - mFromPoint.y; int dist = (int)Math.sqrt(dx * dx + dy * dy) * 2; Rectangle r = new Rectangle(); Point ne = new Point(0, 0); Point nw = new Point(0, 0); Point se = new Point(0, 0); Point sw = new Point(0, 0); System.arraycopy(mFromPixels, 0, mToPixels, 0, mWidth * mHeight); if (dist == 0) { return; } else { SetRect(r, mFromPoint.x - dist, mFromPoint.y - dist, mFromPoint.x, mFromPoint.y); ClipRect(r, mWidth, mHeight); SetPt(ne, r.x, r.y); SetPt(nw, r.x + r.width, r.y); SetPt(se, r.x, r.y + r.height); SetPt(sw, mToPoint.x, mToPoint.y); WarpRegion(r, nw, ne, sw, se); SetRect(r, mFromPoint.x, mFromPoint.y - dist, mFromPoint.x + dist, mFromPoint.y); ClipRect(r, mWidth, mHeight); SetPt(ne, r.x, r.y); SetPt(nw, r.x + r.width, r.y); SetPt(se, mToPoint.x, mToPoint.y); SetPt(sw, r.x + r.width, r.y + r.height); WarpRegion(r, nw, ne, sw, se); SetRect(r, mFromPoint.x - dist, mFromPoint.y, mFromPoint.x, mFromPoint.y + dist); ClipRect(r, mWidth, mHeight); SetPt(ne, r.x, r.y); SetPt(nw, mToPoint.x, mToPoint.y); SetPt(se, r.x, r.y + r.height); SetPt(sw, r.x + r.width, r.y + r.height); WarpRegion(r, nw, ne, sw, se); SetRect(r, mFromPoint.x, mFromPoint.y, mFromPoint.x + dist, mFromPoint.y + dist); ClipRect(r, mWidth, mHeight); SetPt(ne, mToPoint.x, mToPoint.y); SetPt(nw, r.x + r.width, r.y); SetPt(se, r.x, r.y + r.height); SetPt(sw, r.x + r.width, r.y + r.height); WarpRegion(r, nw, ne, sw, se); return; } } void WarpRegion (Rectangle fromRect, Point nw, Point ne, Point sw, Point se) { int dx = fromRect.width; int dy = fromRect.height; double invDX = 1.0D / (double)dx; double invDY = 1.0D / (double)dy; boolean doInterpolate = smooth && (imp.getType() == ImagePlus.COLOR_RGB); for (int a = 0; a < dx; a++) { double aa = (double)a * invDX; double x1 = (double)ne.x + (double)(nw.x - ne.x) * aa; double y1 = (double)ne.y + (double)(nw.y - ne.y) * aa; double x2 = (double)se.x + (double)(sw.x - se.x) * aa; double y2 = (double)se.y + (double)(sw.y - se.y) * aa; double xin = x1; double yin = y1; double dxin = (x2 - x1) * invDY; double dyin = (y2 - y1) * invDY; int toPixel = fromRect.x + a + fromRect.y * mWidth; for (int b = 0; b < dy; b++) { if (doInterpolate) { mToPixels[toPixel] = interpolatePixel(xin, yin); } else { if (xin < 0.0D) xin = 0.0D; if (xin >= (double)mWidth) xin = mWidth - 1; if (yin < 0.0D) yin = 0.0D; if (yin >= (double)mHeight) yin = mHeight - 1; mToPixels[toPixel] = mFromPixels[(int)xin + (int)yin * mWidth]; } xin += dxin; yin += dyin; toPixel += mWidth; } } } private final int interpolatePixel (double d, double d1) { int i = (int)d; int j = (int)d1; double d2 = d - (double)i - 0.5D; double d3 = d1 - (double)j - 0.5D; double d4 = d2 <= 0.0D ? d2 + 1.0D : 1.0D - d2; double d5 = d3 <= 0.0D ? d3 + 1.0D : 1.0D - d3; int k = d2 <= 0.0D ? i - 1 : i + 1; int l = d3 <= 0.0D ? j - 1 : j + 1; double d6 = 1.0D - d4; double d7 = 1.0D - d5; col.reset(); col.add(getPixel(i, j), d4 * d5); col.add(getPixel(k, j), d6 * d5); col.add(getPixel(i, l), d4 * d7); col.add(getPixel(k, l), d6 * d7); col.add(getPixel(i, j)); col.times(0.5D); return col.toInt(); } private final int getPixel (int i, int j) { if (i < 0) i = 0; if (i >= mWidth) i = mWidth - 1; if (j < 0) j = 0; if (j >= mHeight) j = mHeight - 1; return mFromPixels[i + j * mWidth]; } void ClipRect (Rectangle r, int w, int h) { if (r.x < 0) { r.width += r.x; r.x = 0; } if (r.y < 0) { r.height += r.y; r.y = 0; } if (r.x + r.width >= w) r.width = w - r.x - 1; if (r.y + r.height >= h) r.height = h - r.y - 1; } void SetRect (Rectangle r, int left, int top, int right, int bottom) { r.x = left; r.y = top; r.width = right - left; r.height = bottom - top; } void SetPt (Point pt, int x, int y) { pt.x = x; pt.y = y; } } class MyColor { double red, green, blue; MyColor() { } void add (int i) { red += (i & 0xff0000) >> 16; green += (i & 0xff00) >> 8; blue += i & 0xff; } void add (int i, double d) { red += (double)((i & 0xff0000) >> 16) * d; green += (double)((i & 0xff00) >> 8) * d; blue += (double)(i & 0xff) * d; } void times (double d) { red *= d; green *= d; blue *= d; } int toInt() { return 0xff000000 | (int)red << 16 | (int)green << 8 | (int)blue; } void reset() { red = green = blue = 0.0D; } } }