1 /*
2  * $RCSfile: CommandStream.java,v $
3  *
4  * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * - Redistribution of source code must retain the above copyright
11  *   notice, this list of conditions and the following disclaimer.
12  *
13  * - Redistribution in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  *
18  * Neither the name of Sun Microsystems, Inc. or the names of
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * This software is provided "AS IS," without a warranty of any
23  * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
24  * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
26  * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
27  * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
28  * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
29  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
30  * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
31  * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
32  * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
33  * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGES.
35  *
36  * You acknowledge that this software is not designed, licensed or
37  * intended for use in the design, construction, operation or
38  * maintenance of any nuclear facility.
39  *
40  * $Revision: 1.5 $
41  * $Date: 2007/02/09 17:20:16 $
42  * $State: Exp $
43  */
44 
45 package com.sun.j3d.utils.compression;
46 
47 /**
48  * This class is used to build the bit-level compression command stream which
49  * is the final result of the compression process.  It defines the bit
50  * representations of the compression commands and provides a mechanism for
51  * the interleaving and forwarding of command headers and bodies required by
52  * the geometry compression specification.
53  */
54 class CommandStream {
55     // Geometry compression commands.
56     static final int SET_NORM     = 0xC0 ;
57     static final int SET_COLOR    = 0x80 ;
58     static final int VERTEX       = 0x40 ;
59     static final int MESH_B_R     = 0x20 ;
60     static final int SET_STATE    = 0x18 ;
61     static final int SET_TABLE    = 0x10 ;
62     static final int V_NO_OP      = 0x01 ;
63 
64     // Huffman table indices.
65     static final int POSITION_TABLE = 0 ;
66     static final int COLOR_TABLE    = 1 ;
67     static final int NORMAL_TABLE   = 2 ;
68 
69     // The buffer of compressed data and the current offset.
70     private byte bytes[] ;
71     private int byteOffset ;
72     private int bitOffset ;
73 
74     // Last command body for header forwarding.
75     private long lastBody ;
76     private int lastBodyLength ;
77 
78     /**
79      * Create an empty CommandStream with a default initial size.
80      */
CommandStream()81     CommandStream() {
82 	this(65536) ;
83     }
84 
85     /**
86      * Create an empty CommandStream with the given initial size.
87      *
88      * @param initSize initial capacity of CommandStream in bytes
89      */
CommandStream(int initSize)90     CommandStream(int initSize) {
91 	bytes = new byte[initSize] ;
92 	clear() ;
93     }
94 
95     /**
96      * Mark the CommandStream as empty so that its storage will be reused.
97      */
clear()98     void clear() {
99 	// 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      */
addCommand(int header, int headerLength, long body, int bodyLength)140     void addCommand(int header, int headerLength, long body, int bodyLength) {
141 	addByte(header, headerLength) ;
142 	addLong(lastBody, lastBodyLength) ;
143 
144 	lastBody = body ;
145 	lastBodyLength = bodyLength ;
146     }
147 
148     //
149     // Add the rightmost bitCount bits of b to the end of the command stream.
150     //
addByte(int b, int bitCount)151     private void addByte(int b, int bitCount) {
152 	int bitsEmpty = 8 - bitOffset ;
153 	b &= (int)CompressionStreamElement.lengthMask[bitCount] ;
154 
155 	if (bitCount <= bitsEmpty) {
156 	    bytes[byteOffset] |= (b << (bitsEmpty - bitCount)) ;
157 	    bitOffset += bitCount ;
158 	    return ;
159 	}
160 
161 	if (bytes.length == byteOffset + 1) {
162 	    byte newBytes[] = new byte[bytes.length * 2] ;
163 	    System.arraycopy(bytes, 0, newBytes, 0, bytes.length) ;
164 	    bytes = newBytes ;
165 	}
166 
167 	bitOffset = bitCount - bitsEmpty ;
168 	bytes[byteOffset] |= (b >>> bitOffset) ;
169 
170 	byteOffset++ ;
171 	bytes[byteOffset] = (byte)(b << (8 - bitOffset)) ;
172     }
173 
174     //
175     // Add the rightmost bitCount bits of l to the end of the command stream.
176     //
addLong(long l, int bitCount)177     private void addLong(long l, int bitCount) {
178 	int byteCount = bitCount / 8 ;
179 	int excessBits = bitCount - byteCount * 8 ;
180 
181 	if (excessBits > 0)
182 	    addByte((int)(l >>> (byteCount * 8)), excessBits) ;
183 
184 	while (byteCount > 0) {
185 	    addByte((int)((l >>> ((byteCount - 1) * 8)) & 0xff), 8) ;
186 	    byteCount-- ;
187 	}
188     }
189 
190     /**
191      * Add a no-op and the last command body.  Pad out with additional no-ops
192      * to a 64-bit boundary if necessary.  A call to this method is required
193      * in order to create a valid compression command stream.
194      */
end()195     void end() {
196 	int excessBytes, padBits ;
197 
198 	// Add the 1st no-op and the last body.
199 	addByte(V_NO_OP, 8) ;
200 	addLong(lastBody, lastBodyLength) ;
201 
202 	excessBytes = (byteOffset + 1) % 8 ;
203 	if (excessBytes == 0 && bitOffset == 8)
204 	    // No padding necessary.
205 	    return ;
206 
207 	// Need to add padding with a 2nd no-op.
208 	addByte(V_NO_OP, 8) ;
209 	excessBytes = (byteOffset + 1) % 8 ;
210 
211 	if (excessBytes == 0)
212 	    padBits = 8 - bitOffset ;
213 	else {
214 	    int fillBytes = 8 - excessBytes ;
215 	    padBits = (8 * fillBytes) + (8 - bitOffset) ;
216 	}
217 
218 	// The minimum length for a no-op command body is 5 bits.
219 	if (padBits < 5)
220 	    // Have to cross the next 64-bit boundary.
221 	    padBits += 64 ;
222 
223 	// The maximum length of a no-op body is a 5-bit length + 31 bits of
224 	// fill for a total of 36.
225 	if (padBits < 37) {
226 	    // Pad with the body of the 1st no-op.
227 	    addLong((padBits - 5) << (padBits - 5), padBits) ;
228 	    return ;
229 	}
230 
231 	// The number of bits to pad at this point is [37..68].  Knock off 24
232 	// bits with the body of the 1st no-op to reduce the number of pad
233 	// bits to [13..44], which can be filled with 1 more no-op.
234 	addLong(19 << 19, 24) ;
235 	padBits -= 24 ;
236 
237 	// Add a 3rd no-op.
238 	addByte(V_NO_OP, 8) ;
239 	padBits -= 8 ;
240 
241 	// Complete padding with the body of the 2nd no-op.
242 	addLong((padBits - 5) << (padBits - 5), padBits) ;
243     }
244 
245     /**
246      * Get the number of bytes in the compression command stream.
247      *
248      * @return size of compressed data in bytes
249      */
getByteCount()250     int getByteCount() {
251 	if (byteOffset + bitOffset == 0)
252 	    return 0 ;
253 	else
254 	    return byteOffset + 1 ;
255     }
256 
257     /**
258      * Get the bytes composing the compression command stream.
259      *
260      * @return reference to array of bytes containing the compressed data
261      */
getBytes()262     byte[] getBytes() {
263 	return bytes ;
264     }
265 }
266