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