001 /* 002 * TIFFDecoderModifiedHuffman 003 * 004 * Copyright (c) 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.codecs.tiff; 009 010 import java.io.DataInput; 011 import java.io.IOException; 012 import net.sourceforge.jiu.codecs.tiff.TIFFDecoder; 013 import net.sourceforge.jiu.codecs.InvalidFileStructureException; 014 import net.sourceforge.jiu.data.BilevelImage; 015 import net.sourceforge.jiu.ops.MissingParameterException; 016 017 /** 018 * A TIFF decoder for files compresseed with the <em>Modified Huffman</em> method 019 * (also known as <em>CCITT 1D Modified Huffman Run Length Encoding</em>). 020 * This compression algorithm has the value <code>2</code> 021 * in the compression tag of an image file directory. 022 * Only bilevel images can be encoded with that method. 023 * @author Marco Schmidt 024 * @since 0.9.0 025 */ 026 public class TIFFDecoderModifiedHuffman extends TIFFDecoder 027 { 028 private DataInput in; 029 private int bitBuffer; 030 private int numBufferedBits; 031 032 public void decode() throws 033 InvalidFileStructureException, 034 IOException 035 { 036 byte[] row = new byte[getBytesPerRow()]; 037 for (int y = getY1(); y <= getY2(); y++) 038 { 039 decodeRow(row); 040 putBytes(row, 0, row.length); 041 } 042 } 043 044 private int decodeBlackRun() throws 045 InvalidFileStructureException, 046 IOException 047 { 048 return decodeRun(TIFFFaxCodes.BLACK_CODES, TIFFFaxCodes.MIN_BLACK_CODE_SIZE); 049 } 050 051 private void decodeRow(byte[] row) throws 052 InvalidFileStructureException, 053 IOException 054 { 055 reset(); 056 boolean black = false; 057 int index = 0; 058 do 059 { 060 // this will hold the accumulated run length for the current 061 // color at the end of this loop iteration 062 int completeRunLength = 0; 063 // get run lengths regarding current color until one is smaller than 64 064 int runLength; 065 do 066 { 067 if (black) 068 { 069 runLength = decodeBlackRun(); 070 } 071 else 072 { 073 runLength = decodeWhiteRun(); 074 } 075 completeRunLength += runLength; 076 } 077 while (runLength >= 64); 078 // pick color value for output row 079 byte value; 080 if (black) 081 { 082 value = (byte)BilevelImage.BLACK; 083 } 084 else 085 { 086 value = (byte)BilevelImage.WHITE; 087 } 088 // fill row buffer with value 089 while (completeRunLength-- > 0) 090 { 091 row[index++] = value; 092 } 093 // switch colors (black to white or vice versa) 094 black = !black; 095 } 096 while (index < row.length); 097 } 098 099 private int decodeRun(int[][][] codes, int minCodeSize) throws 100 InvalidFileStructureException, 101 IOException 102 { 103 int code = readBits(minCodeSize); 104 int currentCodeSize = minCodeSize; 105 for (int i = 0; i < codes.length; i++) 106 { 107 int[][] data = codes[i]; 108 int j = 0; 109 final int LENGTH = data.length; 110 while (j < LENGTH) 111 { 112 int[] pair = data[j++]; 113 if (pair[TIFFFaxCodes.INDEX_CODE_WORD] == code) 114 { 115 return pair[TIFFFaxCodes.INDEX_CODE_VALUE]; 116 } 117 } 118 code = (code << 1) | readBit(); 119 } 120 throw new InvalidFileStructureException("Could not identify Huffman code in TIFF file."); 121 } 122 123 private int decodeWhiteRun() throws 124 InvalidFileStructureException, 125 IOException 126 { 127 return decodeRun(TIFFFaxCodes.WHITE_CODES, TIFFFaxCodes.MIN_WHITE_CODE_SIZE); 128 } 129 130 public Integer[] getCompressionTypes() 131 { 132 return new Integer[] {new Integer(TIFFConstants.COMPRESSION_CCITT_GROUP3_1D_MODIFIED_HUFFMAN)}; 133 } 134 135 public void initialize() throws 136 IOException, 137 MissingParameterException 138 { 139 super.initialize(); 140 in = getInput(); 141 } 142 143 private int readBit() throws IOException 144 { 145 int result; 146 if (numBufferedBits == 0) 147 { 148 bitBuffer = in.readUnsignedByte(); 149 if ((bitBuffer & 0x80) == 0) 150 { 151 result = 0; 152 } 153 else 154 { 155 result = 1; 156 } 157 bitBuffer &= 0x7f; 158 numBufferedBits = 7; 159 } 160 else 161 { 162 numBufferedBits--; 163 result = bitBuffer >> numBufferedBits; 164 bitBuffer &= (1 << numBufferedBits) - 1; 165 } 166 return result; 167 } 168 169 private int readBits(int number) throws IOException 170 { 171 // make sure there are at least number bits 172 while (numBufferedBits < number) 173 { 174 int b = in.readUnsignedByte(); 175 bitBuffer = (bitBuffer << 8) | b; 176 numBufferedBits += 8; 177 } 178 numBufferedBits -= number; 179 int result = bitBuffer >> numBufferedBits; 180 bitBuffer &= (1 << numBufferedBits) - 1; 181 return result; 182 } 183 184 private void reset() 185 { 186 bitBuffer = 0; 187 numBufferedBits = 0; 188 } 189 }