package uk.co.mmscomputing.imageio.gif;
import java.io.*;
public class GIFLZWInputStream extends InputStream{
private final static int MAXCODE = 4096;
private int[] prefix=new int[MAXCODE];
private int[] suffix=new int[MAXCODE];
private int[] stack =new int[MAXCODE];
private int sp=0;
private int dataSize=0;
private int clearCode=0;
private int eoiCode=0; // eoi code
private int availCode=0; // next available code
private int lastCode=MAXCODE; // last used code, set to impossible value
private int codeSize=0;
private int codeMask=0;
private int first=0;
private GIFBitInputStream in;
public GIFLZWInputStream(InputStream is) throws IOException{
dataSize =is.read();
in =new GIFBitInputStream(is);
sp=0; // reset stack pointer
first=0;
resetTables();
for(int i=0; i<clearCode; i++){ // initialize
prefix[i]=MAXCODE;
suffix[i]=i;
}
}
private void resetTables(){
clearCode = 1<<dataSize;
eoiCode = clearCode+1; // eoi code
availCode = clearCode+2; // next available code
lastCode = MAXCODE; // old code, set to impossible value
codeSize = dataSize+1;
codeMask = (1<<codeSize)-1;
//System.err.println("data size : 0x"+Integer.toHexString(dataSize));
//System.err.println("clear code : 0x"+Integer.toHexString(clearCode));
//System.err.println("avail code : 0x"+Integer.toHexString(availCode));
//System.err.println("old code : 0x"+Integer.toHexString(lastCode));
//System.err.println("code size : 0x"+Integer.toHexString(codeSize));
//System.err.println("code mask : 0x"+Integer.toHexString(codeMask));
//System.err.println("eoi code : 0x"+Integer.toHexString(eoiCode));
}
public int read()throws IOException{
int code;
if(sp>0){return stack[--sp];}
while((code=in.readBits(codeSize))>=0){
if(code==eoiCode){
code=in.read(); // make sure to read empty chunk byte
if(code!=-1){throw new IOException(getClass().getName()+".read:\n\tEOI code before end of data!");}
return -1;
}
decode(code);
if(sp>0){return stack[--sp];}
}
throw new IOException(getClass().getName()+".read:\n\tMissing eoi code.");
}
public int read(byte[] b)throws IOException{
return read(b,0,b.length);
}
public int read(byte[] b,int off,int len)throws IOException{
for(int i=0;i<len;i++){
int c=read();
if(c<0){return (i>0)?i:-1; }
b[off+i]=(byte)c;
}
return len;
}
private void decode(int code)throws IOException{
if(code==clearCode){ // reset variables
codeSize=dataSize+1;
codeMask=(1<<codeSize)-1;
availCode=clearCode+2; // next available code
lastCode=MAXCODE; // old code, set to impossible value
return;
}
if(code>availCode){
throw new IOException(getClass().getName()+"decode:\n\tIllegal LZW-Code ["+code+"] > ["+availCode+"]");
}
if(lastCode==MAXCODE){ // first code
first=suffix[code];
stack[sp++]=first;
lastCode=code;
return;
}
int inCode=code; // remember
if(code==availCode){
stack[sp++]=first;
code=lastCode;
}
while(code>clearCode){
stack[sp++]=suffix[code];
code=prefix[code];
}
first=suffix[code];
stack[sp++]=first;
prefix[availCode]=lastCode; // generate code table on fly
suffix[availCode]=first;
if(availCode<4096){ // if(availCode==4096) a clear code will reset decoder
availCode++;
}
if(((availCode&codeMask)==0)&&(availCode<4096)){
codeSize++;
codeMask+=availCode;
}
lastCode=inCode;
}
}