blob: fa792a0add8cfd8f3a632371cd811f72bc4481d3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.imaging.common.mylzw;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public final class MyLzwDecompressor
{
private static final int MAX_TABLE_SIZE = 1 << 12;
private final byte[][] table;
private int codeSize;
private final int initialCodeSize;
private int codes = -1;
private final int byteOrder;
private final Listener listener;
public static interface Listener
{
public void code(int code);
public void init(int clearCode, int eoiCode);
}
public MyLzwDecompressor(int initialCodeSize, int byteOrder)
{
this(initialCodeSize, byteOrder, null);
}
public MyLzwDecompressor(int initialCodeSize, int byteOrder,
Listener listener)
{
this.listener = listener;
this.byteOrder = byteOrder;
this.initialCodeSize = initialCodeSize;
table = new byte[MAX_TABLE_SIZE][];
clearCode = 1 << initialCodeSize;
eoiCode = clearCode + 1;
if (null != listener)
listener.init(clearCode, eoiCode);
InitializeTable();
}
private final void InitializeTable()
{
codeSize = initialCodeSize;
int intial_entries_count = 1 << codeSize + 2;
for (int i = 0; i < intial_entries_count; i++)
table[i] = new byte[] { (byte) i, };
}
private final void clearTable()
{
codes = (1 << initialCodeSize) + 2;
codeSize = initialCodeSize;
incrementCodeSize();
}
private final int clearCode;
private final int eoiCode;
private final int getNextCode(MyBitInputStream is) throws IOException
{
int code = is.readBits(codeSize);
if (null != listener)
listener.code(code);
return code;
}
private final byte[] stringFromCode(int code) throws IOException
{
if ((code >= codes) || (code < 0))
throw new IOException("Bad Code: " + code + " codes: " + codes
+ " code_size: " + codeSize + ", table: " + table.length);
return table[code];
}
private final boolean isInTable(int Code)
{
return Code < codes;
}
private final byte firstChar(byte bytes[])
{
return bytes[0];
}
private final void addStringToTable(byte bytes[]) throws IOException
{
if (codes < (1 << codeSize))
{
table[codes] = bytes;
codes++;
} else
throw new IOException("AddStringToTable: codes: " + codes
+ " code_size: " + codeSize);
checkCodeSize();
}
private final byte[] appendBytes(byte bytes[], byte b)
{
byte result[] = new byte[bytes.length + 1];
System.arraycopy(bytes, 0, result, 0, bytes.length);
result[result.length - 1] = b;
return result;
}
private int written = 0;
private final void writeToResult(OutputStream os, byte bytes[])
throws IOException
{
os.write(bytes);
written += bytes.length;
}
private boolean tiffLZWMode = false;
public void setTiffLZWMode()
{
tiffLZWMode = true;
}
public byte[] decompress(InputStream is, int expectedLength)
throws IOException
{
int code, oldCode = -1;
MyBitInputStream mbis = new MyBitInputStream(is, byteOrder);
if (tiffLZWMode)
mbis.setTiffLZWMode();
ByteArrayOutputStream baos = new ByteArrayOutputStream(expectedLength);
clearTable();
while ((code = getNextCode(mbis)) != eoiCode)
{
if (code == clearCode)
{
clearTable();
if (written >= expectedLength)
break;
code = getNextCode(mbis);
if (code == eoiCode)
{
break;
}
writeToResult(baos, stringFromCode(code));
oldCode = code;
} // end of ClearCode case
else
{
if (isInTable(code))
{
writeToResult(baos, stringFromCode(code));
addStringToTable(appendBytes(stringFromCode(oldCode),
firstChar(stringFromCode(code))));
oldCode = code;
} else
{
byte OutString[] = appendBytes(stringFromCode(oldCode),
firstChar(stringFromCode(oldCode)));
writeToResult(baos, OutString);
addStringToTable(OutString);
oldCode = code;
}
} // end of not-ClearCode case
if (written >= expectedLength)
break;
} // end of while loop
byte result[] = baos.toByteArray();
return result;
}
private final void checkCodeSize() // throws IOException
{
int limit = (1 << codeSize);
if (tiffLZWMode)
limit--;
if (codes == limit)
incrementCodeSize();
}
private final void incrementCodeSize() // throws IOException
{
if (codeSize != 12)
codeSize++;
}
}