package uk.co.mmscomputing.imageio.jpeg; import java.io.*; public class JPEGHuffmanInputStream extends InputStream{ protected int[] BITS=new int[16]; // 16-byte list containing number of Huffman codes of each length protected int[] HUFFVAL; protected int[] HUFFSIZE; protected int[] HUFFCODE; protected int[] VALPTR = new int[16]; protected int[] MINCODE = new int[16]; protected int[] MAXCODE = new int[16]; protected JPEGBitInputStream in; public JPEGHuffmanInputStream(JPEGBitInputStream in,InputStream tables)throws IOException{ this.in=in; initialize(tables); } public JPEGHuffmanInputStream(InputStream tables)throws IOException{ this.in=null; initialize(tables); } public void setInputStream(JPEGBitInputStream in)throws IOException{ this.in=in; } private void initialize(InputStream tables)throws IOException{ int LASTK=readTableData(tables); generateSizeTable(LASTK); generateCodeTable(LASTK); generateDecoderTable(); } private int readTableData(InputStream tables)throws IOException{ // [1] p.40, p.50 int m=0; for(int i=0;i<16;i++){ // 16-byte list containing number of Huffman codes of each length BITS[i]=tables.read(); // System.out.println(Integer.toString((BITS[i]<128)?BITS[i]:BITS[i]-256))+","); m+=BITS[i]; } HUFFVAL=new int[m]; for(int i=0;i<m;i++){ HUFFVAL[i]=tables.read(); // System.out.println(Integer.toString((HUFFVAL[i]<128)?HUFFVAL[i]:HUFFVAL[i]-256)+","); } return m; } private void generateSizeTable(int LASTK){ // [1] p.51 HUFFSIZE=new int[LASTK+1]; int k=0; for(int i=0;i<16;i++){ for(int j=0;j<BITS[i];j++){ HUFFSIZE[k++]=i+1; } } HUFFSIZE[k]=0; } private void generateCodeTable(int LASTK){ // [1] p.52 HUFFCODE=new int[LASTK]; int k=0,code=0; int si=HUFFSIZE[0]; while(true){ do{ HUFFCODE[k]=code; code++;k++; }while(HUFFSIZE[k]==si); if(HUFFSIZE[k]==0){break;} do{ code<<=1;si++; }while(HUFFSIZE[k]!=si); } } private void generateDecoderTable(){ // [1] p.108 int j=0; for(int i=0;i<16;i++){ if(BITS[i]==0){ MAXCODE[i]=-1; }else{ VALPTR[i]=j; MINCODE[i]=HUFFCODE[j]; j+=BITS[i]-1; MAXCODE[i]=HUFFCODE[j]; j++; } } } public String toString(){ String s="\n"+getClass().getName()+"\n"; s+="byte[] BITS={"; for(int i=0;i<BITS.length;i++){ // 16-byte list containing number of Huffman codes of each length s+=Integer.toString((BITS[i]<128)?BITS[i]:BITS[i]-256)+","; // s+=Integer.toHexString(BITS[i])+","; } s+="};\n"; s+="byte[] HUFFVAL={"; for(int i=0;i<HUFFVAL.length;i++){ s+=Integer.toString((HUFFVAL[i]<128)?HUFFVAL[i]:HUFFVAL[i]-256)+","; // s+=Integer.toHexString(HUFFVAL[i])+","; } s+="};\n"; return s; } public int readBits(int bitSize)throws IOException{ int V = in.readBits(bitSize); int Vt=1<<(bitSize-1); // [1] p.105 extend if(V<Vt){ // if V should be negative (bit T is zero, hence V is smaller then Vt) Vt=(-1<<bitSize)+1; // sign extend: put 1 bits in front of the T bits of V V+=Vt; } return V; } private int code,index; public void restart()throws IOException{ // Encountered RST marker. This can happen in middle of read() code=0;index=0; // Hence need to declare code and index as class variables. } public int read()throws IOException{ // [1] p.109 decode int b; index=0; code=in.readBit(); if(code==-1){return -1;} while(code>MAXCODE[index++]){ b=in.readBit(); if(b==-1){return -1;} code=(code<<1)|b; } int j=VALPTR[--index]; j+=code-MINCODE[index]; return HUFFVAL[j]; } } // [1] 'JPEG' : ISO/IEC IS 10918-1 // ITU-T Recommendation T.81 // http://www.w3.org/Graphics/JPEG/itu-t81.pdf