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.