package ij.io;
import ij.gui.*;
import ij.process.FloatPolygon;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.net.*;
import java.awt.geom.*;


/** Saves an ROI to a file or stream. RoiDecoder.java has a description of the file format.
	@see ij.io.RoiDecoder
	@see ij.plugin.RoiReader
*/
public class RoiEncoder {
	static final int HEADER_SIZE = 64;
	static final int HEADER2_SIZE = 64;
	static final int VERSION = 227; // v1.50d (point counters)
	private String path;
	private OutputStream f;
	private final int polygon=0, rect=1, oval=2, line=3, freeline=4, polyline=5, noRoi=6, freehand=7, 
		traced=8, angle=9, point=10;
	private byte[] data;
	private String roiName;
	private int roiNameSize;
	private String roiProps;
	private int roiPropsSize;
	private int countersSize;
	private int[] counters;

	
	/** Creates an RoiEncoder using the specified path. */
	public RoiEncoder(String path) {
		this.path = path;
	}

	/** Creates an RoiEncoder using the specified OutputStream. */
	public RoiEncoder(OutputStream f) {
		this.f = f;
	}
	
	/** Saves the specified ROI as a file, returning 'true' if successful. */
	public static boolean save(Roi roi, String path) {
		RoiEncoder re = new RoiEncoder(path);
		try {
			re.write(roi);
		} catch (IOException e) {
			return false;
		}
		return true;
	}

	/** Save the Roi to the file of stream. */
	public void write(Roi roi) throws IOException {
		if (f!=null) {
			write(roi, f);
		} else {
			f = new FileOutputStream(path);
			write(roi, f);
			f.close();
		}
	}
	
	/** Saves the specified ROI as a byte array. */
	public static byte[] saveAsByteArray(Roi roi) {
		if (roi==null) return null;
		byte[] bytes = null;
		try {
			ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
			RoiEncoder encoder = new RoiEncoder(out);
			encoder.write(roi);
			out.close();
			bytes = out.toByteArray(); 
		} catch (IOException e) {
			return null;
		}
		return bytes;
	}

	void write(Roi roi, OutputStream f) throws IOException {
		Rectangle r = roi.getBounds();
		if (r.width>65535||r.height>65535||r.x>65535||r.y>65535)
			roi.enableSubPixelResolution();
		int roiType = roi.getType();
		int type = rect;
		int options = 0;
		roiName = roi.getName();
		if (roiName!=null)
			roiNameSize = roiName.length()*2;
		else
			roiNameSize = 0;
		
		roiProps = roi.getProperties();
		if (roiProps!=null)
			roiPropsSize = roiProps.length()*2;
		else
			roiPropsSize = 0;

		switch (roiType) {
			case Roi.POLYGON: type=polygon; break;
			case Roi.FREEROI: type=freehand; break;
			case Roi.TRACED_ROI: type=traced; break;
			case Roi.OVAL: type=oval; break;
			case Roi.LINE: type=line; break;
			case Roi.POLYLINE: type=polyline; break;
			case Roi.FREELINE: type=freeline; break;
			case Roi.ANGLE: type=angle; break;
			case Roi.COMPOSITE: type=rect; break; // shape array size (36-39) will be >0 to indicate composite type
			case Roi.POINT: type=point; break;
			default: type = rect; break;
		}
		
		if (roiType==Roi.COMPOSITE) {
			saveShapeRoi(roi, type, f, options);
			return;
		}

		int n=0;
		int[] x=null, y=null;
		float[] xf=null, yf=null;
		int floatSize = 0;
		if (roi instanceof PolygonRoi) {
			PolygonRoi proi = (PolygonRoi)roi;
			Polygon p = proi.getNonSplineCoordinates();
			n = p.npoints;
			x = p.xpoints;
			y = p.ypoints;
			if (roi.subPixelResolution()) {
				FloatPolygon fp = null;
				if (proi.isSplineFit())
					fp = proi.getNonSplineFloatPolygon();
				else
					fp = roi.getFloatPolygon();
				if (n==fp.npoints) {
					options |= RoiDecoder.SUB_PIXEL_RESOLUTION;
					if (roi.getDrawOffset())
						options |= RoiDecoder.DRAW_OFFSET;
					xf = fp.xpoints;
					yf = fp.ypoints;
					floatSize = n*8;
				}
			}
		}
		
		if (roi instanceof PointRoi) {
			countersSize = 0;
			counters = ((PointRoi)roi).getCounters();
			if (counters!=null && counters.length>=n)
				countersSize = n*4;
		}
		
		data = new byte[HEADER_SIZE+HEADER2_SIZE+n*4+floatSize+roiNameSize+roiPropsSize+countersSize];
		data[0]=73; data[1]=111; data[2]=117; data[3]=116; // "Iout"
		putShort(RoiDecoder.VERSION_OFFSET, VERSION);
		data[RoiDecoder.TYPE] = (byte)type;
		putShort(RoiDecoder.TOP, r.y);
		putShort(RoiDecoder.LEFT, r.x);
		putShort(RoiDecoder.BOTTOM, r.y+r.height);
		putShort(RoiDecoder.RIGHT, r.x+r.width);	
		if (roi.subPixelResolution() && (type==rect||type==oval)) {
			FloatPolygon p = roi.getFloatPolygon();
			if (p.npoints==4) {
				putFloat(RoiDecoder.XD, p.xpoints[0]);
				putFloat(RoiDecoder.YD, p.ypoints[0]);
				putFloat(RoiDecoder.WIDTHD, p.xpoints[1]-p.xpoints[0]);
				putFloat(RoiDecoder.HEIGHTD, p.ypoints[2]-p.ypoints[1]);	
				options |= RoiDecoder.SUB_PIXEL_RESOLUTION;
				putShort(RoiDecoder.OPTIONS, options);
			}
		}
		if (n>65535) {
			ij.IJ.beep();
			ij.IJ.log("ROIs with more than 65k points cannot be saved.");
			n = 65535;
		}
		putShort(RoiDecoder.N_COORDINATES, n);
		putInt(RoiDecoder.POSITION, roi.getPosition());
		
		if (type==rect) {
			int arcSize = roi.getCornerDiameter();
			if (arcSize>0)
				putShort(RoiDecoder.ROUNDED_RECT_ARC_SIZE, arcSize);
		}
		
		if (roi instanceof Line) {
			Line line = (Line)roi;
			putFloat(RoiDecoder.X1, (float)line.x1d);
			putFloat(RoiDecoder.Y1, (float)line.y1d);
			putFloat(RoiDecoder.X2, (float)line.x2d);
			putFloat(RoiDecoder.Y2, (float)line.y2d);
			if (roi instanceof Arrow) {
				putShort(RoiDecoder.SUBTYPE, RoiDecoder.ARROW);
				if (((Arrow)roi).getDoubleHeaded())
					options |= RoiDecoder.DOUBLE_HEADED;
				if (((Arrow)roi).getOutline())
					options |= RoiDecoder.OUTLINE;
				putShort(RoiDecoder.OPTIONS, options);
				putByte(RoiDecoder.ARROW_STYLE, ((Arrow)roi).getStyle());
				putByte(RoiDecoder.ARROW_HEAD_SIZE, (int)((Arrow)roi).getHeadSize());
			} else {
				if (roi.getDrawOffset())
					options |= RoiDecoder.SUB_PIXEL_RESOLUTION+RoiDecoder.DRAW_OFFSET;
			}
		}
		
		if (roi instanceof PointRoi) {
			PointRoi point = (PointRoi)roi;
			putByte(RoiDecoder.POINT_TYPE, point.getPointType());
			putShort(RoiDecoder.STROKE_WIDTH, point.getSize());
		}

		if (roi instanceof RotatedRectRoi || roi instanceof EllipseRoi) {
			double[] p = null;
			if (roi instanceof RotatedRectRoi) {
				putShort(RoiDecoder.SUBTYPE, RoiDecoder.ROTATED_RECT);
				p = ((RotatedRectRoi)roi).getParams();
			} else {
				putShort(RoiDecoder.SUBTYPE, RoiDecoder.ELLIPSE);
				p = ((EllipseRoi)roi).getParams();
			}
			putFloat(RoiDecoder.X1, (float)p[0]);
			putFloat(RoiDecoder.Y1, (float)p[1]);
			putFloat(RoiDecoder.X2, (float)p[2]);
			putFloat(RoiDecoder.Y2, (float)p[3]);
			putFloat(RoiDecoder.FLOAT_PARAM, (float)p[4]);
		}
				
		// save stroke width, stroke color and fill color (1.43i or later)
		if (VERSION>=218) {
			saveStrokeWidthAndColor(roi);
			if ((roi instanceof PolygonRoi) && ((PolygonRoi)roi).isSplineFit()) {
				options |= RoiDecoder.SPLINE_FIT;
				putShort(RoiDecoder.OPTIONS, options);
			}
		}
		
		if (n==0 && roi instanceof TextRoi)
			saveTextRoi((TextRoi)roi);
		else if (n==0 && roi instanceof ImageRoi)
			options = saveImageRoi((ImageRoi)roi, options);
		else
			putHeader2(roi, HEADER_SIZE+n*4+floatSize);
			
		if (n>0) {
			int base1 = 64;
			int base2 = base1+2*n;
			for (int i=0; i=218) saveStrokeWidthAndColor(roi);
		saveOverlayOptions(roi, options);

		// handle the actual data: data are stored segment-wise, i.e.,
		// the type of the segment followed by 0-6 control point coordinates.
		int base = 64;
		for (int i=0; i0)
			putName(roi, hdr2Offset);
		double strokeWidth = roi.getStrokeWidth();
		if (roi.getStroke()==null)
			strokeWidth = 0.0;
		putFloat(hdr2Offset+RoiDecoder.FLOAT_STROKE_WIDTH, (float)strokeWidth);
		if (roiPropsSize>0)
			putProps(roi, hdr2Offset);
		if (countersSize>0)
			putPointCounters(roi, hdr2Offset);
	}

	void putName(Roi roi, int hdr2Offset) {
		int offset = hdr2Offset+HEADER2_SIZE;
		int nameLength = roiNameSize/2;
		putInt(hdr2Offset+RoiDecoder.NAME_OFFSET, offset);
		putInt(hdr2Offset+RoiDecoder.NAME_LENGTH, nameLength);
		for (int i=0; i>>8);
		data[base+1] = (byte)v;
    }

	void putFloat(int base, float v) {
		int tmp = Float.floatToIntBits(v);
		data[base]   = (byte)(tmp>>24);
		data[base+1] = (byte)(tmp>>16);
		data[base+2] = (byte)(tmp>>8);
		data[base+3] = (byte)tmp;
	}

	void putInt(int base, int i) {
		data[base]   = (byte)(i>>24);
		data[base+1] = (byte)(i>>16);
		data[base+2] = (byte)(i>>8);
		data[base+3] = (byte)i;
	}
	
	
}
 

Ads help maintain this website.