package uk.co.mmscomputing.imageio.gif;
import java.io.*;
public class GIFLZWOutputStream extends OutputStream{
final static private int MAXCODE = 4096;
final static private int PREFIXTABLE = 16;
private int[] prefix=new int[MAXCODE];
private int[] suffix=new int[MAXCODE];
private int[][] prefixtab=new int[MAXCODE][PREFIXTABLE];
private int curPrefix, curSuffix;
private int dataSize; // 'seed' for decoder, count of first codes
private int clearCode; // decoder reset code
private int eoiCode; // end of information code
private int availCode; // next available code
private int codeSize;
private GIFBitOutputStream out;
public GIFLZWOutputStream(OutputStream os, int ds)throws IOException{
out=new GIFBitOutputStream(os);
dataSize=ds;
resetTables();
out.write(dataSize); // write data size 'seed'
curPrefix=MAXCODE; // set to impossible value
out.writeBits(clearCode,codeSize); // write clear code to reset decoder.
}
private void resetTables(){
codeSize = dataSize+1;
clearCode = 1<<dataSize;
eoiCode = clearCode+1; // end of image code {static}
availCode = clearCode+2; // next available code
for(int i=0; i<availCode; i++){
prefix[i]=MAXCODE;
suffix[i]=i; // initialize codes
}
for(int i=0;i<MAXCODE;i++){
for(int j=0;j<PREFIXTABLE;j++){
prefixtab[i][j]=MAXCODE;
}
}
}
public void write(byte[] b)throws IOException{
int len=b.length;
for(int i=0;i<len;i++){
write(b[i]&0x00FF);
}
}
public void write(byte[] b,int off,int len)throws IOException{
for(int i=0;i<len;i++){
write(b[off+i]&0x00FF);
}
}
public void write(int code)throws IOException{
if(code==clearCode){ // reset variables
curPrefix=MAXCODE; // set to impossible value
out.writeBits(code,codeSize); // write clear code
return;
}
curSuffix=code; // normal code
boolean ptfull =true;
boolean foundCode=false;
int found =MAXCODE;
if(curPrefix<MAXCODE){
int[] pt=prefixtab[curPrefix];
for(int i=0;i<PREFIXTABLE;i++){ // search in curPrefix part of the prefix table
found=pt[i];
if(found==MAXCODE){ // code with this prefix does not exist
ptfull=false; // but found free entry
break;
} // else found code with current prefix
if(suffix[found]==curSuffix){ // if suffix is correct as well
foundCode=true; // we found code
break;
}
}
}
if((curPrefix==MAXCODE) // prefix does not exist but prefix table full
||(!foundCode && ptfull) // prefix not found or (12 int) prefix table full
){
found=MAXCODE;
int i=(curPrefix==MAXCODE)?0:clearCode+2;
while(i<availCode){
if((prefix[i]==curPrefix)&&(suffix[i]==curSuffix)){
found=i;
break;
}
i++;
}
}
if(found!=MAXCODE){
curPrefix=found; // found code, remember
return;
}
if(availCode==MAXCODE){ // code table full ?
out.writeBits(curPrefix,codeSize); // write last code
out.writeBits(clearCode,codeSize); // reset Decoder
resetTables(); // reset code tables
curPrefix=code;
}else{
prefix[availCode]=curPrefix; // write code in table
suffix[availCode]=curSuffix;
int[] pt=prefixtab[curPrefix];
for(int i=0;i<PREFIXTABLE;i++){
if(pt[i]==MAXCODE){
pt[i]=availCode;
break;
}
}
out.writeBits(curPrefix,codeSize);
codeSize=getCodeLength(availCode);
availCode++;
curPrefix=curSuffix;
}
}
private int getCodeLength(int code){
if((code&2048)!=0){ return 12; }
else if((code&1024)!=0){ return 11; }
else if((code&512)!=0){ return 10; }
else if((code&256)!=0){ return 9; }
else if((code&128)!=0){ return 8; }
else if((code&64)!=0){ return 7; }
else if((code&32)!=0){ return 6; }
else if((code&16)!=0){ return 5; }
else if((code&8)!=0){ return 4; }
else { return 3; }
}
public void flush()throws IOException{ // needs to be called at end of data
out.writeBits(curPrefix,codeSize); // write last code
out.writeBits(eoiCode,codeSize); // write eoi 'end of image' code
out.flush();
}
public void close()throws IOException{
flush();
out.close();
}
}