| RoiEncoder |

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

    /** 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 {
        String path;
        OutputStream f;
        static final int HEADER_SIZE = 64;
        static final int VERSION = 217;
        final int polygon=0, rect=1, oval=2, line=3, freeline=4, polyline=5, noRoi=6, freehand=7,
            traced=8, angle=9, point=10;
        byte[] data;

        /** 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;
        }

        /** 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();
            }
        }

        void write(Roi roi, OutputStream f) throws IOException {
            int roiType = roi.getType();
            int type = rect;
            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);
                return;
            }

            int n=0;
            int[] x=null,y=null;
            if (roi instanceof PolygonRoi) {
                PolygonRoi p = (PolygonRoi)roi;
                n = p.getNCoordinates();
                x = p.getXCoordinates();
                y = p.getYCoordinates();
            }
            data = new byte[HEADER_SIZE+n*4];

            Rectangle r = roi.getBounds();

            data[0]=73; data[1]=111; data[2]=117; data[3]=116; // "Iout"
            putShort(4, VERSION);
            data[6] = (byte)type;
            putShort(8, r.y);           //top
            putShort(10, r.x);          //left
            putShort(12, r.y+r.height); //bottom
            putShort(14, r.x+r.width);  //right
            putShort(16, n);

            if (roi instanceof Line) {
                Line l = (Line)roi;
                putFloat(18, l.x1);
                putFloat(22, l.y1);
                putFloat(26, l.x2);
                putFloat(30, l.y2);
            }

            if (n>0) {
                int base1 = 64;
                int base2 = base1+2*n;
                for (int i=0; i<n; i++) {
                    putShort(base1+i*2, x[i]);
                    putShort(base2+i*2, y[i]);
                }
            }

            f.write(data);
        }

        void saveShapeRoi(Roi roi, int type, OutputStream f) throws IOException {
            float[] shapeArray = ((ShapeRoi)roi).getShapeAsArray();
            if (shapeArray==null) return;
            BufferedOutputStream bout = new BufferedOutputStream(f);
            Rectangle r = roi.getBounds();
            data  = new byte[HEADER_SIZE + shapeArray.length*4];
            data[0]=73; data[1]=111; data[2]=117; data[3]=116; // "Iout"
            putShort(4, VERSION);
            data[6] = (byte)type;
            putShort(8, r.y);           //top
            putShort(10, r.x);          //left
            putShort(12, r.y+r.height); //bottom
            putShort(14, r.x+r.width);  //right
            //putShort(16, n);
            putInt(36, shapeArray.length); // non-zero segment count indicate composite type

            // 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; i<shapeArray.length; i++) {
                putFloat(base, shapeArray[i]);
                base += 4;
            }
            bout.write(data,0,data.length);
            bout.flush();
        }

        void putShort(int base, int v) {
            data[base] = (byte)(v>>>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;
        }

    }

| RoiEncoder |