1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
/*
* TIFFDecoderModifiedHuffman
*
* Copyright (c) 2002, 2003, 2004, 2005, 2006 Marco Schmidt.
* All rights reserved.
*/
package net.sourceforge.jiu.codecs.tiff;
import java.io.DataInput;
import java.io.IOException;
import net.sourceforge.jiu.codecs.tiff.TIFFDecoder;
import net.sourceforge.jiu.codecs.InvalidFileStructureException;
import net.sourceforge.jiu.data.BilevelImage;
import net.sourceforge.jiu.ops.MissingParameterException;
/**
* A TIFF decoder for files compresseed with the <em>Modified Huffman</em> method
* (also known as <em>CCITT 1D Modified Huffman Run Length Encoding</em>).
* This compression algorithm has the value <code>2</code>
* in the compression tag of an image file directory.
* Only bilevel images can be encoded with that method.
* @author Marco Schmidt
* @since 0.9.0
*/
public class TIFFDecoderModifiedHuffman extends TIFFDecoder
{
private DataInput in;
private int bitBuffer;
private int numBufferedBits;
public void decode() throws
InvalidFileStructureException,
IOException
{
byte[] row = new byte[getBytesPerRow()];
for (int y = getY1(); y <= getY2(); y++)
{
decodeRow(row);
putBytes(row, 0, row.length);
}
}
private int decodeBlackRun() throws
InvalidFileStructureException,
IOException
{
return decodeRun(TIFFFaxCodes.BLACK_CODES, TIFFFaxCodes.MIN_BLACK_CODE_SIZE);
}
private void decodeRow(byte[] row) throws
InvalidFileStructureException,
IOException
{
reset();
boolean black = false;
int index = 0;
do
{
// this will hold the accumulated run length for the current
// color at the end of this loop iteration
int completeRunLength = 0;
// get run lengths regarding current color until one is smaller than 64
int runLength;
do
{
if (black)
{
runLength = decodeBlackRun();
}
else
{
runLength = decodeWhiteRun();
}
completeRunLength += runLength;
}
while (runLength >= 64);
// pick color value for output row
byte value;
if (black)
{
value = (byte)BilevelImage.BLACK;
}
else
{
value = (byte)BilevelImage.WHITE;
}
// fill row buffer with value
while (completeRunLength-- > 0)
{
row[index++] = value;
}
// switch colors (black to white or vice versa)
black = !black;
}
while (index < row.length);
}
private int decodeRun(int[][][] codes, int minCodeSize) throws
InvalidFileStructureException,
IOException
{
int code = readBits(minCodeSize);
//int currentCodeSize = minCodeSize;
for (int i = 0; i < codes.length; i++)
{
int[][] data = codes[i];
int j = 0;
final int LENGTH = data.length;
while (j < LENGTH)
{
int[] pair = data[j++];
if (pair[TIFFFaxCodes.INDEX_CODE_WORD] == code)
{
return pair[TIFFFaxCodes.INDEX_CODE_VALUE];
}
}
code = (code << 1) | readBit();
}
throw new InvalidFileStructureException("Could not identify Huffman code in TIFF file.");
}
private int decodeWhiteRun() throws
InvalidFileStructureException,
IOException
{
return decodeRun(TIFFFaxCodes.WHITE_CODES, TIFFFaxCodes.MIN_WHITE_CODE_SIZE);
}
public Integer[] getCompressionTypes()
{
return new Integer[] {new Integer(TIFFConstants.COMPRESSION_CCITT_GROUP3_1D_MODIFIED_HUFFMAN)};
}
public void initialize() throws
IOException,
MissingParameterException
{
super.initialize();
in = getInput();
}
private int readBit() throws IOException
{
int result;
if (numBufferedBits == 0)
{
bitBuffer = in.readUnsignedByte();
if ((bitBuffer & 0x80) == 0)
{
result = 0;
}
else
{
result = 1;
}
bitBuffer &= 0x7f;
numBufferedBits = 7;
}
else
{
numBufferedBits--;
result = bitBuffer >> numBufferedBits;
bitBuffer &= (1 << numBufferedBits) - 1;
}
return result;
}
private int readBits(int number) throws IOException
{
// make sure there are at least number bits
while (numBufferedBits < number)
{
int b = in.readUnsignedByte();
bitBuffer = (bitBuffer << 8) | b;
numBufferedBits += 8;
}
numBufferedBits -= number;
int result = bitBuffer >> numBufferedBits;
bitBuffer &= (1 << numBufferedBits) - 1;
return result;
}
private void reset()
{
bitBuffer = 0;
numBufferedBits = 0;
}
}
|