001/* 002 * $RCSfile: TIFFLZWDecompressor.java,v $ 003 * 004 * 005 * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 011 * - Redistribution of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 014 * - Redistribution in binary form must reproduce the above copyright 015 * notice, this list of conditions and the following disclaimer in 016 * the documentation and/or other materials provided with the 017 * distribution. 018 * 019 * Neither the name of Sun Microsystems, Inc. or the names of 020 * contributors may be used to endorse or promote products derived 021 * from this software without specific prior written permission. 022 * 023 * This software is provided "AS IS," without a warranty of any 024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 035 * POSSIBILITY OF SUCH DAMAGES. 036 * 037 * You acknowledge that this software is not designed or intended for 038 * use in the design, construction, operation or maintenance of any 039 * nuclear facility. 040 * 041 * $Revision: 1.1 $ 042 * $Date: 2005/02/11 05:01:48 $ 043 * $State: Exp $ 044 */ 045package com.github.jaiimageio.impl.plugins.tiff; 046 047import java.io.IOException; 048import java.nio.ByteOrder; 049 050import javax.imageio.IIOException; 051 052import com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet; 053import com.github.jaiimageio.plugins.tiff.TIFFDecompressor; 054import com.github.jaiimageio.plugins.tiff.TIFFField; 055 056public class TIFFLZWDecompressor extends TIFFDecompressor { 057 058 private static final boolean DEBUG = false; 059 private static final boolean TRACE = false; 060 061 private static final int andTable[] = { 062 511, 063 1023, 064 2047, 065 4095 066 }; 067 068 int predictor; 069 070 byte[] srcData; 071 byte[] dstData; 072 073 int srcIndex; 074 int dstIndex; 075 076 byte stringTable[][]; 077 int tableIndex, bitsToGet = 9; 078 079 int nextData = 0; 080 int nextBits = 0; 081 082 boolean isLSB = false; 083 084 public TIFFLZWDecompressor(int predictor) throws IIOException { 085 super(); 086 087 if (predictor != BaselineTIFFTagSet.PREDICTOR_NONE && 088 predictor != 089 BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) { 090 throw new IIOException("Illegal value for Predictor in " + 091 "TIFF file"); 092 } 093 094 if(DEBUG) { 095 System.out.println("Using horizontal differencing predictor"); 096 } 097 098 this.predictor = predictor; 099 } 100 101 public void decodeRaw(byte[] b, 102 int dstOffset, 103 int bitsPerPixel, 104 int scanlineStride) throws IOException { 105 106 // Check bitsPerSample. 107 if (predictor == 108 BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) { 109 int len = bitsPerSample.length; 110 for(int i = 0; i < len; i++) { 111 if(bitsPerSample[i] != 8) { 112 throw new IIOException 113 (bitsPerSample[i] + "-bit samples "+ 114 "are not supported for Horizontal "+ 115 "differencing Predictor"); 116 } 117 } 118 } 119 120 stream.seek(offset); 121 122 byte[] sdata = new byte[byteCount]; 123 stream.readFully(sdata); 124 125 int bytesPerRow = (srcWidth*bitsPerPixel + 7)/8; 126 byte[] buf; 127 int bufOffset; 128 if(bytesPerRow == scanlineStride) { 129 buf = b; 130 bufOffset = dstOffset; 131 } else { 132 buf = new byte[bytesPerRow*srcHeight]; 133 bufOffset = 0; 134 } 135 136 int numBytesDecoded = decode(sdata, 0, buf, bufOffset); 137 138 if(bytesPerRow != scanlineStride) { 139 if(DEBUG) { 140 System.out.println("bytesPerRow != scanlineStride"); 141 } 142 int off = 0; 143 for (int y = 0; y < srcHeight; y++) { 144 System.arraycopy(buf, off, b, dstOffset, bytesPerRow); 145 off += bytesPerRow; 146 dstOffset += scanlineStride; 147 } 148 } 149 } 150 151 public int decode(byte[] sdata, int srcOffset, 152 byte[] ddata, int dstOffset) 153 throws IOException { 154 if (sdata[0] == (byte)0x00 && sdata[1] == (byte)0x01) { 155 throw new IIOException 156 ("TIFF 5.0-style LZW compression is not supported!"); 157 } 158 159 this.srcData = sdata; 160 this.dstData = ddata; 161 162 this.srcIndex = srcOffset; 163 this.dstIndex = dstOffset; 164 165 this.nextData = 0; 166 this.nextBits = 0; 167 168 isLSB = false ; 169 if(null != reader ) { 170 if( reader instanceof TIFFImageReader) { 171 isLSB = ((TIFFImageReader)reader).isLsb(); 172 } 173 } 174 initializeStringTable(); 175 176 int code, oldCode = 0; 177 byte[] string; 178 if(TRACE) { 179 System.out.println("start: "); 180 } 181 while ((code = getNextCode()) != 257) { 182 if(TRACE) { 183 System.out.println("Code: "+code); 184 } 185 if (code == 256) { 186 if(TRACE) { 187 System.out.println("reset"); 188 } 189 initializeStringTable(); 190 code = getNextCode(); 191 if(TRACE) { 192 System.out.println("Code2: "+code); 193 } 194 if (code == 257) { 195 break; 196 } 197 198 writeString(stringTable[code]); 199 oldCode = code; 200 } else { 201 if (code < tableIndex) { 202 string = stringTable[code]; 203 204 writeString(string); 205 addStringToTable(stringTable[oldCode], string[0]); 206 oldCode = code; 207 } else { 208 string = stringTable[oldCode]; 209 string = composeString(string, string[0]); 210 writeString(string); 211 addStringToTable(string); 212 oldCode = code; 213 } 214 } 215 } 216 217 if (predictor == 218 BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING) { 219 220 for (int j = 0; j < srcHeight; j++) { 221 222 int count = dstOffset + samplesPerPixel * (j * srcWidth + 1); 223 224 for (int i = samplesPerPixel; i < srcWidth * samplesPerPixel; i++) { 225 226 dstData[count] += dstData[count - samplesPerPixel]; 227 count++; 228 } 229 } 230 } 231 232 return dstIndex - dstOffset; 233 } 234 235 /** 236 * Initialize the string table. 237 */ 238 public void initializeStringTable() { 239 stringTable = new byte[4096][]; 240 241 for (int i = 0; i < 256; i++) { 242 stringTable[i] = new byte[1]; 243 stringTable[i][0] = (byte)i; 244 } 245 246 tableIndex = 258; 247 bitsToGet = 9; 248 249 } 250 251 /** 252 * Write out the string just uncompressed. 253 */ 254 public void writeString(byte string[]) { 255 if(dstIndex < dstData.length) { 256 int maxIndex = Math.min(string.length, 257 dstData.length - dstIndex); 258 259 for (int i=0; i < maxIndex; i++) { 260 dstData[dstIndex++] = string[i]; 261 } 262 } 263 } 264 265 /** 266 * Add a new string to the string table. 267 */ 268 public void addStringToTable(byte oldString[], byte newString) { 269 int length = oldString.length; 270 byte string[] = new byte[length + 1]; 271 System.arraycopy(oldString, 0, string, 0, length); 272 string[length] = newString; 273 274 // Add this new String to the table 275 stringTable[tableIndex++] = string; 276 277 if (tableIndex == 511) { 278 bitsToGet = 10; 279 } else if (tableIndex == 1023) { 280 bitsToGet = 11; 281 } else if (tableIndex == 2047) { 282 bitsToGet = 12; 283 } 284 } 285 286 /** 287 * Add a new string to the string table. 288 */ 289 public void addStringToTable(byte string[]) { 290 // Add this new String to the table 291 stringTable[tableIndex++] = string; 292 293 if (tableIndex == 511) { 294 bitsToGet = 10; 295 } else if (tableIndex == 1023) { 296 bitsToGet = 11; 297 } else if (tableIndex == 2047) { 298 bitsToGet = 12; 299 } 300 } 301 302 /** 303 * Append <code>newString</code> to the end of <code>oldString</code>. 304 */ 305 public byte[] composeString(byte oldString[], byte newString) { 306 int length = oldString.length; 307 byte string[] = new byte[length + 1]; 308 System.arraycopy(oldString, 0, string, 0, length); 309 string[length] = newString; 310 311 return string; 312 } 313 314 public int reverseBits(int inp) 315 { 316 return TIFFFillOrder.reverseTable[inp] ; 317 } 318 319 // This code to build the fillOrder table 320 public static void generateBitsreverseBits() 321 { 322 for( int iOuter = 0 ; iOuter < 256 ; iOuter ++ ) { 323 int iz = 0 ; 324 int po2 = 1; 325 int rev = 0x80; 326 for( int i = 0 ; i < 8 ; i++) { 327 if( (iOuter & po2 ) != 0 ) { 328 iz += rev; 329 } 330 po2 <<= 1 ; 331 rev >>= 1; 332 } 333 byte b = (byte)iz ; 334 String value = String.format("0x%02X,", b & 0x00FF); 335 System.out.println(value+" //"+ iOuter+" " 336 +String.format("%8s", Integer.toBinaryString(iOuter)).replace(" ", "0") 337 +" -> " 338 +String.format("%8s", Integer.toBinaryString(iz)).replace(" ", "0")); 339 } 340 } 341 342 // Returns the next 9, 10, 11 or 12 bits 343 public int getNextCode() { 344 // Attempt to get the next code. The exception is caught to make 345 // this robust to cases wherein the EndOfInformation code has been 346 // omitted from a strip. Examples of such cases have been observed 347 // in practice. 348 349 try { 350 if(!isLSB) { 351 int iz = srcData[srcIndex++] &0x00FF; 352 nextData = (nextData << 8) | (iz& 0xff); 353 nextBits += 8; 354 355 if (nextBits < bitsToGet) { 356 iz = srcData[srcIndex++] &0x00FF; 357 nextData = (nextData << 8) | (iz & 0xff); 358 nextBits += 8; 359 } 360 361 int code = 362 (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet - 9]; 363 nextBits -= bitsToGet; 364 365 return code; 366 } else { 367 int iz = reverseBits( srcData[srcIndex++] &0x00FF); 368 nextData = (nextData << 8) | (iz& 0xff); 369 nextBits += 8; 370 371 if (nextBits < bitsToGet) { 372 iz = reverseBits( srcData[srcIndex++] &0x00FF); 373 nextData = (nextData << 8) | (iz & 0xff); 374 nextBits += 8; 375 } 376 377 int code = 378 (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet - 9]; 379 nextBits -= bitsToGet; 380 381 return code; 382 } 383 } catch (ArrayIndexOutOfBoundsException e) { 384 // Strip not terminated as expected: return EndOfInformation code. 385 return 257; 386 } 387 } 388 389} 390