gt: 				index2 = width*(int)ys;
context: package ij.process;
  
  import java.util.*;
  import java.awt.*;
  import java.awt.image.*;
  import ij.gui.*;
  import ij.Prefs;
  
  /**
  This is an 8-bit image and methods that operate on that image. Based on the ImageProcessor class
  from "KickAss Java Programming" by Tonny Espeset.
  */
  public class ByteProcessor extends ImageProcessor {
  
  	static final int ERODE=10, DILATE=11;
  	protected byte[] pixels;
  	protected byte[] snapshotPixels;
  	private int bgColor = 255; //white
  	private boolean bgColorSet;
  	private int min=0, max=255;
      private int binaryCount, binaryBackground;
  
  	/**Creates a ByteProcessor from an AWT Image. */
  	public ByteProcessor(Image img) {
  		width = img.getWidth(null);
  		height = img.getHeight(null);
  		resetRoi();
  		pixels = new byte[width * height];
  		PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, false);
  		try {
  			pg.grabPixels();
  		} catch (InterruptedException e) {
  			System.err.println(e);
  		};
     		cm = pg.getColorModel();
  		if (cm instanceof IndexColorModel)
  			pixels = (byte[])(pg.getPixels());
  		else
  			System.err.println("ByteProcessor: not 8-bit image");
  		if (((IndexColorModel)cm).getTransparentPixel()!=-1) {
      		IndexColorModel icm = (IndexColorModel)cm;
  			int mapSize = icm.getMapSize();
  			byte[] reds = new byte[mapSize];
  			byte[] greens = new byte[mapSize];
  			byte[] blues = new byte[mapSize];	
  			icm.getReds(reds); 
  			icm.getGreens(greens); 
  			icm.getBlues(blues);
  			cm = new IndexColorModel(8, mapSize, reds, greens, blues);
  		}
  	}
  
  	/**Creates a blank ByteProcessor of the specified dimensions. */
  	public ByteProcessor(int width, int height) {
  		this(width, height, new byte[width*height], null);
  	}
  
  	/**Creates a ByteProcessor from a byte array. */
  	public ByteProcessor(int width, int height, byte[] pixels) {
  		this(width, height, pixels, null);
  	}
  
  	/**Creates a ByteProcessor from a pixel array and IndexColorModel. */
  	public ByteProcessor(int width, int height, byte[] pixels, ColorModel cm) {
  		if (pixels!=null && width*height!=pixels.length)
  			throw new IllegalArgumentException(WRONG_LENGTH);
  		this.width = width;
  		this.height = height;
  		resetRoi();
  		this.pixels = pixels;
  		this.cm = cm;
  	}
  
  	/** Creates a ByteProcessor from a TYPE_BYTE_GRAY BufferedImage. */
  	public ByteProcessor(BufferedImage bi) {
  		if (bi.getType()!=BufferedImage.TYPE_BYTE_GRAY)
  			throw new IllegalArgumentException("Type!=TYPE_BYTE_GRAYY");
  		WritableRaster raster = bi.getRaster();
  		DataBuffer buffer = raster.getDataBuffer();
  		pixels = ((DataBufferByte) buffer).getData();
  		width = raster.getWidth();
  		height = raster.getHeight();
  	}
  
  	/** Creates a ByteProcessor from an ImageProcessor. 16-bit and 32-bit
  	     pixel data are scaled from min-max to 0-255 if 'scale' is true. */
  	public ByteProcessor(ImageProcessor ip, boolean scale) {
  		ImageProcessor bp;
  		if (ip instanceof ByteProcessor)
  			bp = ip.duplicate();
  		else
  			bp = ip.convertToByte(scale);
  		this.width = bp.getWidth();
  		this.height = bp.getHeight();
  		resetRoi();
  		this.pixels = (byte[])bp.getPixels();
  		this.cm = bp.getCurrentColorModel();
  	}
  
  	public Image createImage() {
  		if (cm==null) cm = getDefaultColorModel();
  		if (ij.IJ.isJava16()) return createBufferedImage();
  		if (source==null) {
  			source = new MemoryImageSource(width, height, cm, pixels, 0, width);
  			source.setAnimated(true);
  			source.setFullBufferUpdates(true);
  			img = Toolkit.getDefaultToolkit().createImage(source);
  		} else if (newPixels) {
  			source.newPixels(pixels, cm, 0, width);
  			newPixels = false;
  		} else
  			source.newPixels();
  		return img;
  	}
  
  	Image createBufferedImage() {
  		if (raster==null) {
  			SampleModel sm = getIndexSampleModel();
  			DataBuffer db = new DataBufferByte(pixels, width*height, 0);
  			raster = Raster.createWritableRaster(sm, db, null);
  		}
  		if (image==null || cm!=cm2) {
  			if (cm==null) cm=getDefaultColorModel();
  			image = new BufferedImage(cm, raster, false, null);
  			cm2 = cm;
  		}
  		return image;
  	}
  	
  	/** Returns this image as a BufferedImage. */
  	public BufferedImage getBufferedImage() {
  		if (isDefaultLut()) {
  			BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
  			Graphics g = bi.createGraphics();
  			g.drawImage(createImage(), 0, 0, null);
  			return bi;
  		} else
  			return (BufferedImage)createBufferedImage();
  	}
  
  	/** Returns a new, blank ByteProcessor with the specified width and height. */
  	public ImageProcessor createProcessor(int width, int height) {
  		ImageProcessor ip2;
  		ip2 =  new ByteProcessor(width, height, new byte[width*height], getColorModel());
  		if (baseCM!=null)
  			ip2.setMinAndMax(min, max);
  		ip2.setInterpolationMethod(interpolationMethod);
  		return ip2;
  	}
  
  	public ImageProcessor crop() {
  		ImageProcessor ip2 = createProcessor(roiWidth, roiHeight);
  		byte[] pixels2 = (byte[])ip2.getPixels();
  		for (int ys=roiY; ys<roiY+roiHeight; ys++) {
  			int offset1 = (ys-roiY)*roiWidth;
  			int offset2 = ys*width+roiX;
  			for (int xs=0; xs<roiWidth; xs++)
  				pixels2[offset1++] = pixels[offset2++];
  		}
          return ip2;
  	}
  	
  	/** Returns a duplicate of this image. */ 
  	public synchronized ImageProcessor duplicate() { 
  		ImageProcessor ip2 = createProcessor(width, height); 
  		byte[] pixels2 = (byte[])ip2.getPixels(); 
  		System.arraycopy(pixels, 0, pixels2, 0, width*height); 
  		return ip2; 
  	} 
  
  	/**Make a snapshot of the current image.*/
  	public void snapshot() {
  		snapshotWidth=width;
  		snapshotHeight=height;
  		if (snapshotPixels==null || (snapshotPixels!=null && snapshotPixels.length!=pixels.length))
  			snapshotPixels = new byte[width * height];
  		System.arraycopy(pixels, 0, snapshotPixels, 0, width*height);
  	}
  	
  	/** Reset the image from snapshot.*/
  	public void reset() {
  		if (snapshotPixels==null)
  			return;	
          System.arraycopy(snapshotPixels,0,pixels,0,width*height);
  	}
  	
  	/** Swaps the pixel and snapshot (undo) arrays. */
  	public void swapPixelArrays() {
  		if (snapshotPixels==null) return;	
  		byte pixel;
  		for (int i=0; i<pixels.length; i++) {
  			pixel = pixels[i];
  			pixels[i] = snapshotPixels[i];
  			snapshotPixels[i] = pixel;
  		}
  	}
  
  	/** Restore pixels that are within roi but not part of mask. */
  	public void reset(ImageProcessor mask) {
  		if (mask==null || snapshotPixels==null)
  			return;	
  		if (mask.getWidth()!=roiWidth||mask.getHeight()!=roiHeight)
  			throw new IllegalArgumentException(maskSizeError(mask));
  		byte[] mpixels = (byte[])mask.getPixels();
  		for (int y=roiY, my=0; y<(roiY+roiHeight); y++, my++) {
  			int i = y * width + roiX;
  			int mi = my * roiWidth;
  			for (int x=roiX; x<(roiX+roiWidth); x++) {
  				if (mpixels[mi++]==0)
  					pixels[i] = snapshotPixels[i];
  				i++;
  			}
  		}
  	}
  
  	public void setSnapshotPixels(Object pixels) {
  		snapshotPixels = (byte[])pixels;
  		snapshotWidth=width;
  		snapshotHeight=height;
  	}
  
  	public Object getSnapshotPixels() {
  		return snapshotPixels;
  	}
  
  	/** Fills pixels that are within roi and part of the mask.
  		Does nothing if the mask is not the same size as the ROI. */
  	public void fill(ImageProcessor mask) {
  		if (mask==null)
  			{fill(); return;}
  		int roiWidth=this.roiWidth, roiHeight=this.roiHeight;
  		int roiX=this.roiX, roiY=this.roiY;
  		if (mask.getWidth()!=roiWidth||mask.getHeight()!=roiHeight) {
  			mask = getMask();
  			if (mask==null||mask.getWidth()!=roiWidth||mask.getHeight()!=roiHeight)
  				return;
  		}
  		byte[] mpixels = (byte[])mask.getPixels();
  		for (int y=roiY, my=0; y<(roiY+roiHeight); y++, my++) {
  			int i = y * width + roiX;
  			int mi = my * roiWidth;
  			for (int x=roiX; x<(roiX+roiWidth); x++) {
  				if (mpixels[mi++]!=0)
  					pixels[i] = (byte)fgColor;
  				i++;
  			}
  		}
  	}
  
  	public int getPixel(int x, int y) {
  		if (x>=0 && x<width && y>=0 && y<height)
  			return pixels[y*width+x]&0xff;
  		else
  			return 0;
  	}
  	
  	public final int get(int x, int y) {return pixels[y*width+x]&0xff;}
  	public final void set(int x, int y, int value) {pixels[y*width+x] = (byte)value;}
  	public final int get(int index) {return pixels[index]&0xff;}
  	public final void set(int index, int value) {pixels[index] = (byte)value;}
  	public final float getf(int x, int y) {return pixels[y*width+x]&0xff;}
  	public final void setf(int x, int y, float value) {pixels[y*width+x] = (byte)(value+0.5f);}
  	public final float getf(int index) {return pixels[index]&0xff;}
  	public final void setf(int index, float value) {pixels[index] = (byte)value;}
  
  	static double oldx, oldy;
  
  	/** Uses the current interpolation method (BILINEAR or BICUBIC) 
  		to calculate the pixel value at real coordinates (x,y). */
  	public double getInterpolatedPixel(double x, double y) {
  		if (interpolationMethod==BICUBIC)
  			return getBicubicInterpolatedPixel(x, y, this);
  		else {
  			if (x<0.0) x = 0.0;
  			if (x>=width-1.0) x = width-1.001;
  			if (y<0.0) y = 0.0;
  			if (y>=height-1.0) y = height-1.001;
  			return getInterpolatedPixel(x, y, pixels);
  		}
  	}
  
  	final public int getPixelInterpolated(double x, double y) {
  		if (interpolationMethod==BILINEAR) {
  			if (x<0.0 || y<0.0 || x>=width-1 || y>=height-1)
  				return 0;
  			else
  				return (int)Math.round(getInterpolatedPixel(x, y, pixels));
  		} else if (interpolationMethod==BICUBIC) {
  			int value = (int)(getBicubicInterpolatedPixel(x, y, this)+0.5);
  			if (value<0) value = 0;
  			if (value>255) value = 255;
  			return value;
  		} else
  			return getPixel((int)(x+0.5), (int)(y+0.5));
  	}
  	
   	public float getPixelValue(int x, int y) {
  		if (x>=0 && x<width && y>=0 && y<height) {
  			if (cTable==null)
  				return pixels[y*width + x]&0xff;
  			else
  				return cTable[pixels[y*width + x]&0xff];
  		} else
  			return 0f;
  	}
  
  	/** Sets the foreground drawing color. */
  	public void setColor(Color color) {
  		//if (ij.IJ.altKeyDown()) throw new IllegalArgumentException("setColor: "+color);
  		drawingColor = color;
  		fgColor = getBestIndex(color);
  	}
  
  	/** Sets the default fill/draw value, where 0<=value<=255. */
  	public void setValue(double value) {
  		fgColor = (int)value;
  		if (fgColor<0) fgColor = 0;
  		if (fgColor>255) fgColor = 255;
  	}
  
  	/** Sets the background fill value, where 0<=value<=255. */
  	public void setBackgroundValue(double value) {
  		bgColor = (int)value;
  		if (bgColor<0) bgColor = 0;
  		if (bgColor>255) bgColor = 255;
  		bgColorSet = true;
  	}
  
  	/** Returns the background fill value. */
  	public double getBackgroundValue() {
  		return bgColor;
  	}
  
  	/** Stores the specified real value at (x,y). Does
  		nothing if (x,y) is outside the image boundary.
  		Values outside the range 0-255 are clamped. */
  	public void putPixelValue(int x, int y, double value) {
  		if (x>=0 && x<width && y>=0 && y<height) {
  			if (value>255.0)
  				value = 255.0;
  			else if (value<0.0)
  				value = 0.0;
  			pixels[y*width + x] = (byte)(value+0.5);
  		}
  	}
  
  	/** Stores the specified value at (x,y). Does
  		nothing if (x,y) is outside the image boundary.
  		Values outside the range 0-255 are clamped. */
  	public final void putPixel(int x, int y, int value) {
  		if (x>=0 && x<width && y>=0 && y<height) {
  			if (value>255) value = 255;
  			if (value<0) value = 0;
  			pixels[y*width + x] = (byte)value;
  		}
  	}
  
  	/** Draws a pixel in the current foreground color. */
  	public void drawPixel(int x, int y) {
  		if (x>=clipXMin && x<=clipXMax && y>=clipYMin && y<=clipYMax)
  			pixels[y*width + x] = (byte)fgColor;
  	}
  
  	/**	Returns a reference to the byte array containing this image's
  		pixel data. To avoid sign extension, the pixel values must be
  		accessed using a mask (e.g. int i = pixels[j]&0xff). */
  	public Object getPixels() {
  		return (Object)pixels;
  	}
  
  	/** Returns a copy of the pixel data. Or returns a reference to the
  		snapshot buffer if it is not null and 'snapshotCopyMode' is true.
  		@see ImageProcessor#snapshot
  		@see ImageProcessor#setSnapshotCopyMode
  	*/
  	public Object getPixelsCopy() {
  		if (snapshotPixels!=null && snapshotCopyMode) {
  			snapshotCopyMode = false;
  			return snapshotPixels;
  		} else {
  			byte[] pixels2 = new byte[width*height];
          	System.arraycopy(pixels, 0, pixels2, 0, width*height);
  			return pixels2;
  		}
  	}
  
  	public void setPixels(Object pixels) {
  		if (pixels!=null && this.pixels!=null && (((byte[])pixels).length!=this.pixels.length))
  			throw new IllegalArgumentException("");
  		this.pixels = (byte[])pixels;
  		resetPixels(pixels);
  		if (pixels==null) snapshotPixels = null;
  		raster = null;
  		image = null;
  	}
  
  	/*
  	public void getRow(int x, int y, int[] data, int length) {
  		int j = y*width+x;
  		for (int i=0; i<length; i++)
  			data[i] = pixels[j++];
  	}
  
  	public void putRow(int x, int y, int[] data, int length) {
  		int j = y*width+x;
  		for (int i=0; i<length; i++)
  			pixels[j++] = (byte)data[i];
  	}
  	*/
  	
  	/** Returns the smallest displayed pixel value. */
  	public double getMin() {
  		return min;
  	}
  
  	/** Returns the largest displayed pixel value. */
  	public double getMax() {
  		return max;
  	}
  
  	/** Maps the entries in this image's LUT from min-max to 0-255. */
  	public void setMinAndMax(double min, double max) {
  		if (max<min)
  			return;
  		this.min = (int)min;
  		this.max = (int)max;
  		if (rLUT1==null) {
  			if (cm==null)
  				makeDefaultColorModel();
  			baseCM = cm;
  			IndexColorModel m = (IndexColorModel)cm;
  			rLUT1 = new byte[256]; gLUT1 = new byte[256]; bLUT1 = new byte[256];
  			m.getReds(rLUT1); m.getGreens(gLUT1); m.getBlues(bLUT1); 
  			rLUT2 = new byte[256]; gLUT2 = new byte[256]; bLUT2 = new byte[256];
  		}
  		if (rLUT2==null)
  			return;
  		int index;
  		for (int i=0; i<256; i++) {
  			if (i<min) {
  				rLUT2[i] = rLUT1[0];
  				gLUT2[i] = gLUT1[0];
  				bLUT2[i] = bLUT1[0];
  			} else if (i>max) {
  				rLUT2[i] = rLUT1[255];
  				gLUT2[i] = gLUT1[255];
  				bLUT2[i] = bLUT1[255];
  			} else {
  				index = i-this.min;
  				index = (int)(256.0*index/(max-min));
  				if (index < 0)
  					index = 0;
  				if (index > 255)
  					index = 255;
  				rLUT2[i] = rLUT1[index];
  				gLUT2[i] = gLUT1[index];
  				bLUT2[i] = bLUT1[index];
  			}
  		}
  		cm = new IndexColorModel(8, 256, rLUT2, gLUT2, bLUT2);
  		newPixels = true;
  		if (min==0.0 && max==255.0) source = null;
  		minThreshold = NO_THRESHOLD;
  	}
  
  	/** Resets this image's LUT. */
  	public void resetMinAndMax() {
  		setMinAndMax(0, 255);
  	}
  
  	public void setThreshold(double minThreshold, double maxThreshold, int lutUpdate) {
  		super.setThreshold(minThreshold, maxThreshold, lutUpdate);
  		if (this.minThreshold<0.0) this.minThreshold = 0.0;
  		if (this.maxThreshold>255.0) this.maxThreshold = 255.0;
  	}
  
  	/** Copies the image contained in 'ip' to (xloc, yloc) using one of
  		the transfer modes defined in the Blitter interface. */
  	public void copyBits(ImageProcessor ip, int xloc, int yloc, int mode) {
  		ip = ip.convertToByte(true);
  		new ByteBlitter(this).copyBits(ip, xloc, yloc, mode);
  	}
  
  	/* Filters start here */
  
  	public void applyTable(int[] lut) {
  		int lineStart, lineEnd;
  		for (int y=roiY; y<(roiY+roiHeight); y++) {
  			lineStart = y * width + roiX;
  			lineEnd = lineStart + roiWidth;
  			for (int i=lineEnd; --i>=lineStart;)
  				pixels[i] = (byte)lut[pixels[i]&0xff];
  		}
  	}
  
      public void convolve3x3(int[] kernel) {
          int v1, v2, v3;    //input pixel values around the current pixel
          int v4, v5, v6;
          int v7, v8, v9;
          int scale = 0;
  		int k1=kernel[0], k2=kernel[1], k3=kernel[2],
  		k4=kernel[3], k5=kernel[4], k6=kernel[5],
  		k7=kernel[6], k8=kernel[7], k9=kernel[8];
  		for (int i=0; i<kernel.length; i++)
  			scale += kernel[i];
  		if (scale==0) scale = 1;
          int inc = roiHeight/25;
          if (inc<1) inc = 1;
          byte[] pixels2 = (byte[])getPixelsCopy();
          int xEnd = roiX + roiWidth;
          int yEnd = roiY + roiHeight;
          for (int y=roiY; y<yEnd; y++) {
              int p  = roiX + y*width;            //points to current pixel
              int p6 = p - (roiX>0 ? 1 : 0);      //will point to v6, currently lower
              int p3 = p6 - (y>0 ? width : 0);    //will point to v3, currently lower
              int p9 = p6 + (y<height-1 ? width : 0); // ...  to v9, currently lower
              v2 = pixels2[p3]&0xff;
              v5 = pixels2[p6]&0xff;
              v8 = pixels2[p9]&0xff;
              if (roiX>0) { p3++; p6++; p9++; }
              v3 = pixels2[p3]&0xff;
              v6 = pixels2[p6]&0xff;
              v9 = pixels2[p9]&0xff;
  			for (int x=roiX; x<xEnd; x++,p++) {
  				if (x<width-1) { p3++; p6++; p9++; }
  				v1 = v2; v2 = v3;
  				v3 = pixels2[p3]&0xff;
  				v4 = v5; v5 = v6;
  				v6 = pixels2[p6]&0xff;
  				v7 = v8; v8 = v9;
  				v9 = pixels2[p9]&0xff;
  				int sum = k1*v1 + k2*v2 + k3*v3
  						+ k4*v4 + k5*v5 + k6*v6
  						+ k7*v7 + k8*v8 + k9*v9;
  				sum = (sum+scale/2)/scale;   //add scale/2 to round
  				if (sum>255) sum = 255;
  				if (sum<0) sum = 0;
  				pixels[p] = (byte)sum;
  			}
              if (y%inc==0)
                  showProgress((double)(y-roiY)/roiHeight);
          }
          showProgress(1.0);
      }
  
  	/** Filters using a 3x3 neighborhood. The p1, p2, etc variables, which
  		contain the values of the pixels in the neighborhood, are arranged
  		as follows:
  		<pre>
  		    p1 p2 p3
  		    p4 p5 p6
  		    p7 p8 p9
  		</pre>
  	*/
  	public void filter(int type) {
  		int p1, p2, p3, p4, p5, p6, p7, p8, p9;
  		int inc = roiHeight/25;
  		if (inc<1) inc = 1;
  		
  		byte[] pixels2 = (byte[])getPixelsCopy();
  		if (width==1) {
          	filterEdge(type, pixels2, roiHeight, roiX, roiY, 0, 1);
          	return;
  		}
  		int offset, sum1, sum2=0, sum=0;
          int[] values = new int[10];
          if (type==MEDIAN_FILTER) values = new int[10];
          int rowOffset = width;
          int count;
          int binaryForeground = 255 - binaryBackground;
  		for (int y=yMin; y<=yMax; y++) {
  			offset = xMin + y * width;
  			p2 = pixels2[offset-rowOffset-1]&0xff;
  			p3 = pixels2[offset-rowOffset]&0xff;
  			p5 = pixels2[offset-1]&0xff;
  			p6 = pixels2[offset]&0xff;
  			p8 = pixels2[offset+rowOffset-1]&0xff;
  			p9 = pixels2[offset+rowOffset]&0xff;
  
  			for (int x=xMin; x<=xMax; x++) {
  				p1 = p2; p2 = p3;
  				p3 = pixels2[offset-rowOffset+1]&0xff;
  				p4 = p5; p5 = p6;
  				p6 = pixels2[offset+1]&0xff;
  				p7 = p8; p8 = p9;
  				p9 = pixels2[offset+rowOffset+1]&0xff;
  
  				switch (type) {
  					case BLUR_MORE:
  						sum = (p1+p2+p3+p4+p5+p6+p7+p8+p9+4)/9;
  						break;
  					case FIND_EDGES: // 3x3 Sobel filter
  	        			sum1 = p1 + 2*p2 + p3 - p7 - 2*p8 - p9;
  	        			sum2 = p1  + 2*p4 + p7 - p3 - 2*p6 - p9;
  	        			sum = (int)Math.sqrt(sum1*sum1 + sum2*sum2);
  	        			if (sum> 255) sum = 255;
  						break;
  					case MEDIAN_FILTER:
  						values[1]=p1; values[2]=p2; values[3]=p3; values[4]=p4; values[5]=p5;
  						values[6]=p6; values[7]=p7; values[8]=p8; values[9]=p9;
  						sum = findMedian(values);
  						break;
  					case MIN:
  						sum = p5;
  						if (p1<sum) sum = p1;
  						if (p2<sum) sum = p2;
  						if (p3<sum) sum = p3;
  						if (p4<sum) sum = p4;
  						if (p6<sum) sum = p6;
  						if (p7<sum) sum = p7;
  						if (p8<sum) sum = p8;
  						if (p9<sum) sum = p9;
  						break;
  					case MAX:
  						sum = p5;
  						if (p1>sum) sum = p1;
  						if (p2>sum) sum = p2;
  						if (p3>sum) sum = p3;
  						if (p4>sum) sum = p4;
  						if (p6>sum) sum = p6;
  						if (p7>sum) sum = p7;
  						if (p8>sum) sum = p8;
  						if (p9>sum) sum = p9;
  						break;
  					case ERODE:
  						if (p5==binaryBackground)
  							sum = binaryBackground;
  						else {
  							count = 0;
  							if (p1==binaryBackground) count++;
  							if (p2==binaryBackground) count++;
  							if (p3==binaryBackground) count++;
  							if (p4==binaryBackground) count++;
  							if (p6==binaryBackground) count++;
  							if (p7==binaryBackground) count++;
  							if (p8==binaryBackground) count++;
  							if (p9==binaryBackground) count++;							
  							if (count>=binaryCount)
  								sum = binaryBackground;
  							else
  							sum = binaryForeground;
  						}
  						break;
  					case DILATE:
  						if (p5==binaryForeground)
  							sum = binaryForeground;
  						else {
  							count = 0;
  							if (p1==binaryForeground) count++;
  							if (p2==binaryForeground) count++;
  							if (p3==binaryForeground) count++;
  							if (p4==binaryForeground) count++;
  							if (p6==binaryForeground) count++;
  							if (p7==binaryForeground) count++;
  							if (p8==binaryForeground) count++;
  							if (p9==binaryForeground) count++;							
  							if (count>=binaryCount)
  								sum = binaryForeground;
  							else
  								sum = binaryBackground;
  						}
  						break;
  				}
  				
  				pixels[offset++] = (byte)sum;
  			}
  			//if (y%inc==0)
  			//	showProgress((double)(y-roiY)/roiHeight);
  		}
          if (xMin==1) filterEdge(type, pixels2, roiHeight, roiX, roiY, 0, 1);
          if (yMin==1) filterEdge(type, pixels2, roiWidth, roiX, roiY, 1, 0);
          if (xMax==width-2) filterEdge(type, pixels2, roiHeight, width-1, roiY, 0, 1);
          if (yMax==height-2) filterEdge(type, pixels2, roiWidth, roiX, height-1, 1, 0);
  		//showProgress(1.0);
  	}
  
  	void filterEdge(int type, byte[] pixels2, int n, int x, int y, int xinc, int yinc) {
  		int p1, p2, p3, p4, p5, p6, p7, p8, p9;
          int sum=0, sum1, sum2;
          int count;
          int binaryForeground = 255 - binaryBackground;
  		int bg = binaryBackground;
  		int fg = binaryForeground;
  		
  		for (int i=0; i<n; i++) {
  			if ((!Prefs.padEdges && type==ERODE) || type==DILATE) {
  				p1=getEdgePixel0(pixels2,bg,x-1,y-1); p2=getEdgePixel0(pixels2,bg,x,y-1); p3=getEdgePixel0(pixels2,bg,x+1,y-1);
  				p4=getEdgePixel0(pixels2,bg,x-1,y); p5=getEdgePixel0(pixels2,bg,x,y); p6=getEdgePixel0(pixels2,bg,x+1,y);
  				p7=getEdgePixel0(pixels2,bg,x-1,y+1); p8=getEdgePixel0(pixels2,bg,x,y+1); p9=getEdgePixel0(pixels2,bg,x+1,y+1);
  			}  else if (Prefs.padEdges && type==ERODE) {
  				p1=getEdgePixel1(pixels2,fg, x-1,y-1); p2=getEdgePixel1(pixels2,fg,x,y-1); p3=getEdgePixel1(pixels2,fg,x+1,y-1);
  				p4=getEdgePixel1(pixels2,fg, x-1,y); p5=getEdgePixel1(pixels2,fg,x,y); p6=getEdgePixel1(pixels2,fg,x+1,y);
  				p7=getEdgePixel1(pixels2,fg,x-1,y+1); p8=getEdgePixel1(pixels2,fg,x,y+1); p9=getEdgePixel1(pixels2,fg,x+1,y+1);
  			} else {
  				p1=getEdgePixel(pixels2,x-1,y-1); p2=getEdgePixel(pixels2,x,y-1); p3=getEdgePixel(pixels2,x+1,y-1);
  				p4=getEdgePixel(pixels2,x-1,y); p5=getEdgePixel(pixels2,x,y); p6=getEdgePixel(pixels2,x+1,y);
  				p7=getEdgePixel(pixels2,x-1,y+1); p8=getEdgePixel(pixels2,x,y+1); p9=getEdgePixel(pixels2,x+1,y+1);
  			}
              switch (type) {
                  case BLUR_MORE:
                      sum = (p1+p2+p3+p4+p5+p6+p7+p8+p9+4)/9;
                      break;
                  case FIND_EDGES: // 3x3 Sobel filter
                      sum1 = p1 + 2*p2 + p3 - p7 - 2*p8 - p9;
                      sum2 = p1  + 2*p4 + p7 - p3 - 2*p6 - p9;
                      sum = (int)Math.sqrt(sum1*sum1 + sum2*sum2);
                      if (sum> 255) sum = 255;
                      break;
                  case MIN:
                      sum = p5;
                      if (p1<sum) sum = p1;
                      if (p2<sum) sum = p2;
                      if (p3<sum) sum = p3;
                      if (p4<sum) sum = p4;
                      if (p6<sum) sum = p6;
                      if (p7<sum) sum = p7;
                      if (p8<sum) sum = p8;
                      if (p9<sum) sum = p9;
                      break;
                  case MAX:
                      sum = p5;
                      if (p1>sum) sum = p1;
                      if (p2>sum) sum = p2;
                      if (p3>sum) sum = p3;
                      if (p4>sum) sum = p4;
                      if (p6>sum) sum = p6;
                      if (p7>sum) sum = p7;
                      if (p8>sum) sum = p8;
                      if (p9>sum) sum = p9;
                      break;
  				case ERODE:
  					if (p5==binaryBackground)
  						sum = binaryBackground;
  					else {
  						count = 0;
  						if (p1==binaryBackground) count++;
  						if (p2==binaryBackground) count++;
  						if (p3==binaryBackground) count++;
  						if (p4==binaryBackground) count++;
  						if (p6==binaryBackground) count++;
  						if (p7==binaryBackground) count++;
  						if (p8==binaryBackground) count++;
  						if (p9==binaryBackground) count++;							
  						if (count>=binaryCount)
  							sum = binaryBackground;
  						else
  						sum = binaryForeground;
  					}
  					break;
  				case DILATE:
  					if (p5==binaryForeground)
  						sum = binaryForeground;
  					else {
  						count = 0;
  						if (p1==binaryForeground) count++;
  						if (p2==binaryForeground) count++;
  						if (p3==binaryForeground) count++;
  						if (p4==binaryForeground) count++;
  						if (p6==binaryForeground) count++;
  						if (p7==binaryForeground) count++;
  						if (p8==binaryForeground) count++;
  						if (p9==binaryForeground) count++;							
  						if (count>=binaryCount)
  							sum = binaryForeground;
  						else
  							sum = binaryBackground;
  					}
  					break;
              }
              pixels[x+y*width] = (byte)sum;
              x+=xinc; y+=yinc;
          }
      }
  
  	final int getEdgePixel(byte[] pixels2, int x, int y) {
  		if (x<=0) x = 0;
  		if (x>=width) x = width-1;
  		if (y<=0) y = 0;
  		if (y>=height) y = height-1;
  		return pixels2[x+y*width]&255;
  	}
  
  	final int getEdgePixel1(byte[] pixels2, int foreground, int x, int y) {
  		if (x<0 || x>width-1 || y<0 || y>height-1)
              return foreground;
          else
              return pixels2[x+y*width]&255;
  	}
  
  	final int getEdgePixel0(byte[] pixels2, int background, int x, int y) {
  		if (x<0 || x>width-1 || y<0 || y>height-1)
              return background;
          else
              return pixels2[x+y*width]&255;
  	}
  
  	public void erode() {
  		if (isInvertedLut())
  			filter(MIN);
  		else
  			filter(MAX);
  	}
  	
  	public void dilate() {
  		if (isInvertedLut())
  			filter(MAX);
  		else
  			filter(MIN);
  	}
  
  	public void erode(int count, int background) {
          binaryCount = count;
          binaryBackground = background;
          filter(ERODE);
  	}
  
  	public void dilate(int count, int background) {
          binaryCount = count;
          binaryBackground = background;
          filter(DILATE);
  	}
  
  	public void outline() {
  		new BinaryProcessor(this).outline();
  	}
  	
  	public void skeletonize() {
  		new BinaryProcessor(this).skeletonize();
  	}
  	
  	private final int findMedian (int[] values) {
  	//Finds the 5th largest of 9 values
  		for (int i = 1; i <= 4; i++) {
  			int max = 0;
  			int mj = 1;
  			for (int j = 1; j <= 9; j++)
  				if (values[j] > max) {
  					max = values[j];
  					mj = j;
  				}
  			values[mj] = 0;
  		}
  		int max = 0;
  		for (int j = 1; j <= 9; j++)
  			if (values[j] > max)
  				max = values[j];
  		return max;
  	}
  
  	public void medianFilter() {
  		filter(MEDIAN_FILTER);
  	}
  
      public void noise(double range) {
  		Random rnd=new Random();
  		int v, ran;
  		boolean inRange;
  		for (int y=roiY; y<(roiY+roiHeight); y++) {
  			int i = y * width + roiX;
  			for (int x=roiX; x<(roiX+roiWidth); x++) {
  				inRange = false;
  				do {
  					ran = (int)Math.round(rnd.nextGaussian()*range);
  					v = (pixels[i] & 0xff) + ran;
  					inRange = v>=0 && v<=255;
  					if (inRange) pixels[i] = (byte)v;
  				} while (!inRange);
  				i++;
  			}
  			if (y%20==0)
  				showProgress((double)(y-roiY)/roiHeight);
  		}
  		showProgress(1.0);
      }
  
  	/** Scales the image or selection using the specified scale factors.
  		@see ImageProcessor#setInterpolate
  	*/
  	public void scale(double xScale, double yScale) {
  		double xCenter = roiX + roiWidth/2.0;
  		double yCenter = roiY + roiHeight/2.0;
  		int xmin, xmax, ymin, ymax;
  		if (!bgColorSet && isInvertedLut()) bgColor = 0;
  		
  		if ((xScale>1.0) && (yScale>1.0)) {
  			//expand roi
  			xmin = (int)(xCenter-(xCenter-roiX)*xScale);
  			if (xmin<0) xmin = 0;
  			xmax = xmin + (int)(roiWidth*xScale) - 1;
  			if (xmax>=width) xmax = width - 1;
  			ymin = (int)(yCenter-(yCenter-roiY)*yScale);
  			if (ymin<0) ymin = 0;
  			ymax = ymin + (int)(roiHeight*yScale) - 1;
  			if (ymax>=height) ymax = height - 1;
  		} else {
  			xmin = roiX;
  			xmax = roiX + roiWidth - 1;
  			ymin = roiY;
  			ymax = roiY + roiHeight - 1;
  		}
  		byte[] pixels2 = (byte[])getPixelsCopy();
  		ImageProcessor ip2 = null;
  		if (interpolationMethod==BICUBIC) {
  			ip2 = new ByteProcessor(getWidth(), getHeight(), pixels2, null);
  			ip2.setBackgroundValue(getBackgroundValue());
  		}
  		boolean checkCoordinates = (xScale < 1.0) || (yScale < 1.0);
  		int index1, index2, xsi, ysi;
  		double ys, xs;
  		if (interpolationMethod==BICUBIC) {
  			for (int y=ymin; y<=ymax; y++) {
  				ys = (y-yCenter)/yScale + yCenter;
  				index1 = y*width + xmin;
  