// Copyright 2001-2003 FreeHEP. package org.freehep.graphicsio.cgm; import java.awt.Color; import java.awt.geom.Point2D; import java.io.IOException; import java.io.OutputStream; import org.freehep.util.io.ActionHeader; import org.freehep.util.io.Tag; import org.freehep.util.io.TagHeader; import org.freehep.util.io.TaggedOutputStream; import org.freehep.util.io.UndefinedTagException; /** * CGM Binary Output Stream. Tags written with this OutputStream will produce a * binary CGM file. The class also holds the state for the various precisions, * defaults, etc... * * @author Mark Donszelmann * @author Charles Loomis * @author Ian Graham - support for long commands through partitioning * @version $Id: CGMOutputStream.java 8584 2006-08-10 23:06:37Z duns $ */ public class CGMOutputStream extends TaggedOutputStream { private static short COMMAND_PARTITION_SIZE = Short.MAX_VALUE; private int version; private boolean direct = true; private int colorIndexPrecision = 8; private int directColorPrecision = 8; private int indexPrecision = 16; private int integerPrecision = 16; private boolean fixedPrecision = true; private boolean doublePrecision = false; private boolean vdcReal = false; private boolean vdcFixedPrecision = true; private boolean vdcDoublePrecision = false; private int vdcIntegerPrecision = 16; private int namePrecision = 16; private int lineWidthSpecificationMode = LineWidthSpecificationMode.ABSOLUTE; private int markerSizeSpecificationMode = MarkerSizeSpecificationMode.ABSOLUTE; private int edgeWidthSpecificationMode = EdgeWidthSpecificationMode.ABSOLUTE; private int interiorStyleSpecificationMode = InteriorStyleSpecificationMode.ABSOLUTE; public CGMOutputStream(OutputStream os, int version) throws IOException { // CGM is big-endian super(os, new CGMTagSet(version), null, false); this.version = version; } public CGMOutputStream(OutputStream os) throws IOException { this(os, 1); } void setColorMode(boolean direct) { this.direct = direct; } void setColorIndexPrecision(int precision) { colorIndexPrecision = precision; } void setDirectColorPrecision(int precision) { directColorPrecision = precision; } void setIndexPrecision(int precision) { indexPrecision = precision; } void setIntegerPrecision(int precision) { integerPrecision = precision; } void setRealPrecision(boolean fixedPrecision, boolean doublePrecision) { this.fixedPrecision = fixedPrecision; this.doublePrecision = doublePrecision; } void setVDCReal(boolean real) { vdcReal = real; } void setVDCIntegerPrecision(int precision) { vdcIntegerPrecision = precision; } void setVDCRealPrecision(boolean fixedPrecision, boolean doublePrecision) { this.vdcFixedPrecision = fixedPrecision; this.vdcDoublePrecision = doublePrecision; } void setNamePrecision(int precision) { namePrecision = precision; } void setLineWidthSpecificationMode(int mode) { lineWidthSpecificationMode = mode; } int getLineWidthSpecificationMode() { return lineWidthSpecificationMode; } void setMarkerSizeSpecificationMode(int mode) { markerSizeSpecificationMode = mode; } int getMarkerSizeSpecificationMode() { return markerSizeSpecificationMode; } void setEdgeWidthSpecificationMode(int mode) { edgeWidthSpecificationMode = mode; } int getEdgeWidthSpecificationMode() { return edgeWidthSpecificationMode; } void setInteriorStyleSpecificationMode(int mode) { interiorStyleSpecificationMode = mode; } int getInteriorStyleSpecificationMode() { return interiorStyleSpecificationMode; } // FX public void writeFixedPoint(double d) throws IOException { int whole = (int) Math.floor(d); int frac = (int) Math.floor((d - whole) * 0x10000); writeShort(whole); writeShort(frac); } // FP public void writeFloatingPoint(double d) throws IOException { if (doublePrecision) { writeDouble(d); } else { writeFloat((float) d); } } // CI public void writeColorIndex(int i) throws IOException { switch (colorIndexPrecision) { case 8: writeByte(i); break; case 16: writeShort(i); break; default: case 24: // FIXME case 32: writeInt(i); break; } } // CCO public void writeColorComponent(int c) throws IOException { switch (directColorPrecision) { default: case 8: case 16: case 24: // FIXME case 32: writeUnsignedByte(c); break; } } // CD public void writeColorDirect(Color c) throws IOException { // FIXME3: depends on color model from version 3 writeColorComponent(c.getRed()); writeColorComponent(c.getGreen()); writeColorComponent(c.getBlue()); // writeColorComponent(0); // Don't do this until color model support is // provided, RGB is default } // IX public void writeIntegerIndex(int i) throws IOException { switch (indexPrecision) { case 8: writeByte(i); break; default: case 16: writeShort(i); break; case 24: // FIXME case 32: writeInt(i); break; } } // E public void writeEnumerate(int i) throws IOException { writeShort(i); } // I public void writeInteger(int i) throws IOException { switch (integerPrecision) { case 8: writeByte(i); break; default: case 16: writeShort(i); break; case 24: // FIXME case 32: writeInt(i); break; } } // R public void writeReal(double r) throws IOException { if (fixedPrecision) { writeFixedPoint(r); } else { writeFloatingPoint(r); } } // S, SF public void writeString(String s) throws IOException { int len = s.length(); if (len < 0xFF) { // short writeUnsignedByte(len); } else { // long writeUnsignedByte(0xFF); writeUnsignedShort(len & 0x7FFF); } writeBytes(s); } // D public void writeData(byte[] data) throws IOException { // FIXME: good unless strings will do encoding writeString(new String(data)); } // VDC public void writeVDC(double d) throws IOException { if (vdcReal) { if (vdcFixedPrecision) { writeFixedPoint(d); } else { if (vdcDoublePrecision) { writeDouble(d); } else { writeFloat((float) d); } } } else { switch (vdcIntegerPrecision) { default: case 16: writeShort((int) d); break; case 24: // FIXME case 32: writeInt((int) d); break; } } } // P public void writePoint(Point2D p) throws IOException { writeVDC(p.getX()); writeVDC(p.getY()); } // CO public void writeColor(Color c) throws IOException { if (direct) { writeColorDirect(c); } else { // FIXME: we should look up the color writeColorIndex((c.getRed() << 16) + (c.getGreen() << 8) + c.getBlue()); } } // N public void writeName(int name) throws IOException { switch (namePrecision) { case 8: writeByte(name); break; default: case 16: writeShort((int) name); break; case 24: // FIXME case 32: writeInt((int) name); break; } } protected void writeTagHeader(TagHeader header) throws IOException { byteAlign(); int tagID = header.getTag(); long length = header.getLength(); if (length < 0x1F) { // short form writeUnsignedShort((tagID << 5) + (int) length); } else { // long form - length is placed in partition rather than header writeUnsignedShort((tagID << 5) + 0x001F); } } public void writeTag(Tag tag) throws IOException { int tagID = tag.getTag(); if (!tagSet.exists(tagID)) throw new UndefinedTagException(tagID); pushBuffer(); tag.write(tagID, this); int align = getTagAlignment(); int pad = (align - (getBufferLength() % align)) % align; for (int i = 0; i < pad; i++) { write(0); } int len = getBufferLength(); byte[] buffer = popBufferBytes(); TagHeader header = new TagHeader(tagID, len); writeTagHeader(header); if (len <= 30) { write(buffer, 0, len); } else { writePartitioned(buffer, len); } // pad tag with 0 if necessary if ((size() % 2) != 0) { writeUnsignedByte(0); } } private void writePartitioned(byte[] buffer, int len) throws IOException { int bufferOffset = 0; final short partitionSize = COMMAND_PARTITION_SIZE; while (len > partitionSize) { writeUnsignedShort(0x8000 | partitionSize); write(buffer, bufferOffset, partitionSize); bufferOffset += partitionSize; len -= partitionSize; } writeUnsignedShort(len); write(buffer, bufferOffset, len); } protected void writeActionHeader(ActionHeader header) throws IOException { // empty } public int getVersion() { return version; } }