001: /*
002: * $RCSfile: CommandStream.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.5 $
041: * $Date: 2007/02/09 17:20:16 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.compression;
046:
047: /**
048: * This class is used to build the bit-level compression command stream which
049: * is the final result of the compression process. It defines the bit
050: * representations of the compression commands and provides a mechanism for
051: * the interleaving and forwarding of command headers and bodies required by
052: * the geometry compression specification.
053: */
054: class CommandStream {
055: // Geometry compression commands.
056: static final int SET_NORM = 0xC0;
057: static final int SET_COLOR = 0x80;
058: static final int VERTEX = 0x40;
059: static final int MESH_B_R = 0x20;
060: static final int SET_STATE = 0x18;
061: static final int SET_TABLE = 0x10;
062: static final int V_NO_OP = 0x01;
063:
064: // Huffman table indices.
065: static final int POSITION_TABLE = 0;
066: static final int COLOR_TABLE = 1;
067: static final int NORMAL_TABLE = 2;
068:
069: // The buffer of compressed data and the current offset.
070: private byte bytes[];
071: private int byteOffset;
072: private int bitOffset;
073:
074: // Last command body for header forwarding.
075: private long lastBody;
076: private int lastBodyLength;
077:
078: /**
079: * Create an empty CommandStream with a default initial size.
080: */
081: CommandStream() {
082: this (65536);
083: }
084:
085: /**
086: * Create an empty CommandStream with the given initial size.
087: *
088: * @param initSize initial capacity of CommandStream in bytes
089: */
090: CommandStream(int initSize) {
091: bytes = new byte[initSize];
092: clear();
093: }
094:
095: /**
096: * Mark the CommandStream as empty so that its storage will be reused.
097: */
098: void clear() {
099: // Initialize the first byte to 0.
100: // Subsequent bytes are cleared as they are written.
101: bytes[0] = 0;
102:
103: // Reset the number of valid bits.
104: bitOffset = 0;
105: byteOffset = 0;
106:
107: // The first command header is always followed by the body of an
108: // implicit variable length no-op to start the header-forwarding
109: // interleave required by hardware decompressor implementations. The
110: // only necessary bits are 5 bits of length set to zeros to indicate a
111: // fill of zero length.
112: lastBody = 0;
113: lastBodyLength = 5;
114: }
115:
116: /**
117: * Add a compression command to this instance.<p>
118: *
119: * A compression command includes an 8-bit header and can range up to 72
120: * bits in length. The command with the maximum length is a 2-bit color
121: * command with a 6-bit tag in the header, followed by four 16-bit color
122: * components of data.<p>
123: *
124: * A subcommand is either a position, normal, or color, though in practice
125: * a position subcommand can only be part of a vertex command. Normal and
126: * color subcommands can be parts of separate global normal and color
127: * commands as well as parts of a vertex command.<p>
128: *
129: * A subcommand includes a 6-bit header. Its length is 2 bits less than
130: * the length of the corresponding command.
131: *
132: * @param header contains compression command header bits, right-justified
133: * within the bits of the int
134: * @param headerLength number of bits in header, either 8 for commands or
135: * 6 for subcommands
136: * @param body contains the body of the compression command,
137: * right-justified within the bits of the long
138: * @param bodyLength number of bits in the body
139: */
140: void addCommand(int header, int headerLength, long body,
141: int bodyLength) {
142: addByte(header, headerLength);
143: addLong(lastBody, lastBodyLength);
144:
145: lastBody = body;
146: lastBodyLength = bodyLength;
147: }
148:
149: //
150: // Add the rightmost bitCount bits of b to the end of the command stream.
151: //
152: private void addByte(int b, int bitCount) {
153: int bitsEmpty = 8 - bitOffset;
154: b &= (int) CompressionStreamElement.lengthMask[bitCount];
155:
156: if (bitCount <= bitsEmpty) {
157: bytes[byteOffset] |= (b << (bitsEmpty - bitCount));
158: bitOffset += bitCount;
159: return;
160: }
161:
162: if (bytes.length == byteOffset + 1) {
163: byte newBytes[] = new byte[bytes.length * 2];
164: System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
165: bytes = newBytes;
166: }
167:
168: bitOffset = bitCount - bitsEmpty;
169: bytes[byteOffset] |= (b >>> bitOffset);
170:
171: byteOffset++;
172: bytes[byteOffset] = (byte) (b << (8 - bitOffset));
173: }
174:
175: //
176: // Add the rightmost bitCount bits of l to the end of the command stream.
177: //
178: private void addLong(long l, int bitCount) {
179: int byteCount = bitCount / 8;
180: int excessBits = bitCount - byteCount * 8;
181:
182: if (excessBits > 0)
183: addByte((int) (l >>> (byteCount * 8)), excessBits);
184:
185: while (byteCount > 0) {
186: addByte((int) ((l >>> ((byteCount - 1) * 8)) & 0xff), 8);
187: byteCount--;
188: }
189: }
190:
191: /**
192: * Add a no-op and the last command body. Pad out with additional no-ops
193: * to a 64-bit boundary if necessary. A call to this method is required
194: * in order to create a valid compression command stream.
195: */
196: void end() {
197: int excessBytes, padBits;
198:
199: // Add the 1st no-op and the last body.
200: addByte(V_NO_OP, 8);
201: addLong(lastBody, lastBodyLength);
202:
203: excessBytes = (byteOffset + 1) % 8;
204: if (excessBytes == 0 && bitOffset == 8)
205: // No padding necessary.
206: return;
207:
208: // Need to add padding with a 2nd no-op.
209: addByte(V_NO_OP, 8);
210: excessBytes = (byteOffset + 1) % 8;
211:
212: if (excessBytes == 0)
213: padBits = 8 - bitOffset;
214: else {
215: int fillBytes = 8 - excessBytes;
216: padBits = (8 * fillBytes) + (8 - bitOffset);
217: }
218:
219: // The minimum length for a no-op command body is 5 bits.
220: if (padBits < 5)
221: // Have to cross the next 64-bit boundary.
222: padBits += 64;
223:
224: // The maximum length of a no-op body is a 5-bit length + 31 bits of
225: // fill for a total of 36.
226: if (padBits < 37) {
227: // Pad with the body of the 1st no-op.
228: addLong((padBits - 5) << (padBits - 5), padBits);
229: return;
230: }
231:
232: // The number of bits to pad at this point is [37..68]. Knock off 24
233: // bits with the body of the 1st no-op to reduce the number of pad
234: // bits to [13..44], which can be filled with 1 more no-op.
235: addLong(19 << 19, 24);
236: padBits -= 24;
237:
238: // Add a 3rd no-op.
239: addByte(V_NO_OP, 8);
240: padBits -= 8;
241:
242: // Complete padding with the body of the 2nd no-op.
243: addLong((padBits - 5) << (padBits - 5), padBits);
244: }
245:
246: /**
247: * Get the number of bytes in the compression command stream.
248: *
249: * @return size of compressed data in bytes
250: */
251: int getByteCount() {
252: if (byteOffset + bitOffset == 0)
253: return 0;
254: else
255: return byteOffset + 1;
256: }
257:
258: /**
259: * Get the bytes composing the compression command stream.
260: *
261: * @return reference to array of bytes containing the compressed data
262: */
263: byte[] getBytes() {
264: return bytes;
265: }
266: }
|