package uk.co.mmscomputing.imageio.jpeg; import java.io.*; public class JPEGBitInputStream extends FilterInputStream implements JPEGConstants{ JPEGInputStream jpeg; // wrapping jpeg input stream. if marked segment then call this object private int bitBuffer; // entropy buffer; huffman coding private int bitCount; public JPEGBitInputStream(InputStream in,JPEGInputStream jpeg)throws IOException{ super(in);this.jpeg=jpeg; } private int readIn()throws IOException{ int b=in.read(); if(b==-1){ IOException ioe=new IOException(getClass().getName()+"readIn:\n\tUnexpected end of file."); ioe.printStackTrace(); throw ioe; } return b; } protected InputStream readMarkedSegment()throws IOException{ int length =((readIn()<<8)|readIn())-2; byte[] data = new byte[length]; int len = in.read(data); if(len!=length){throw new IOException(getClass().getName()+".readMarkedSegment:\n\tUnexpected end of file.");} return new ByteArrayInputStream(data); } // JPEG Syntax: // 1-coded image data // 2-marked segments for additional information: // a-0xFF 0x00 => 'byte stuffing' 0xFF 0x00 is interpreted as image data 0xFF // b-0xFF 0xmarker // c-0xFF 0xmarker 0xlenHigh 0xlenLow MarkerPayload[0xlenHigh<<8 | 0xlenLow] public int readCompressed()throws IOException{ int b,marker; while(true){ b=readIn(); if(b==MARK){ do{marker=readIn();}while(marker==MARK); // [1]p.30 B.1.1.2 switch(marker){ case 0: /*System.out.println("b="+Integer.toBinaryString(b));*/ return 0x00FF;// byte stuffing case SOF0: // BaseLine sequential DCT, non differential, Huffman coding case SOF1: // Extended sequential DCT, non differential, Huffman coding jpeg.startOfFrame(readMarkedSegment(),marker&0x000F); break; case DHT: jpeg.defineHuffmanTables(readMarkedSegment()); break; // Define Hufman Tables case DAC: jpeg.defineArithmeticConditioning(readMarkedSegment()); break; // Define Arithmetic Conditioning case RST0:case RST1:case RST2:case RST3:case RST4:case RST5:case RST6:case RST7: jpeg.restartIntervalTermination(marker&0x0007); bitBuffer=0;bitCount=0; break; case SOI: jpeg.startOfImage(); break; // Start Of Image case EOI: jpeg.endOfImage(); return -1; // End Of Image case SOS: jpeg.startOfScan(readMarkedSegment());bitCount = 0; break; // Start of Scan;reset entropy decoder buffer case DQT: jpeg.defineQuantizationTables(readMarkedSegment()); break; // Define Quantization Tables case DNL: jpeg.defineNumberOfLines(readMarkedSegment()); break; // Define Number of Lines case DRI: jpeg.defineRestartInterval(readMarkedSegment()); break; // Define Restart Interval case DHP: jpeg.defineHierarchicalProgression(readMarkedSegment());break; // Define Hierarchical Progression case EXP: jpeg.expandReferenceComponents(readMarkedSegment()); break; // Expand reference component(s) case APP0: jpeg.app0(readMarkedSegment()); break; // Application i.e. JFIF case APP1: jpeg.app1(readMarkedSegment()); break; // Application i.e. Exif case APP2: jpeg.app2(readMarkedSegment()); break; // Application case APP3: jpeg.app3(readMarkedSegment()); break; // Application case APP4: jpeg.app4(readMarkedSegment()); break; // Application case APP5: jpeg.app5(readMarkedSegment()); break; // Application case APP6: jpeg.app6(readMarkedSegment()); break; // Application case APP7: jpeg.app7(readMarkedSegment()); break; // Application case APP8: jpeg.app8(readMarkedSegment()); break; // Application case APP9: jpeg.app9(readMarkedSegment()); break; // Application case APP10:jpeg.app10(readMarkedSegment()); break; // Application case APP11:jpeg.app11(readMarkedSegment()); break; // Application case APP12:jpeg.app12(readMarkedSegment()); break; // Application case APP13:jpeg.app13(readMarkedSegment()); break; // Application case APP14:jpeg.app14(readMarkedSegment()); break; // Application case APP15:jpeg.app15(readMarkedSegment()); break; // Application case COM: jpeg.comment(readMarkedSegment()); break; // Comment default: throw new IOException(getClass().getName()+".readCompressed:\n\tUnknown marker = "+Integer.toHexString(marker)); } }else{ // System.out.println("b="+Integer.toBinaryString(b)); return b; // coded image data } } } public void start()throws IOException{ int b=readCompressed(); if(b==-1){return;} // no image data as such; [1] p.47 B.5 abbreviated format for table specification data; i.e. TIFF JPEGTables tag bitBuffer=(b<<24); bitCount=8; } public int readBit()throws IOException{ if(bitCount==0){ int b=readCompressed(); if(b==-1){return -1;} bitBuffer=(b<<24); bitCount=8; } int bit=bitBuffer>>>31; bitBuffer<<=1; bitCount--; return bit; } public int readBits(int neededBits)throws IOException{ if(neededBits==0){return 0;} while(bitCount<neededBits){ int b=readCompressed(); if(b==-1){return -1;} bitBuffer|=(b<<(24-bitCount)); bitCount+=8; } int bits=bitBuffer>>>(32-neededBits); bitBuffer<<=neededBits; bitCount-=neededBits; return bits; } } // [1] 'JPEG' : ISO/IEC IS 10918-1 // ITU-T Recommendation T.81 // http://www.w3.org/Graphics/JPEG/itu-t81.pdf