org.apache.commons.imaging.mylzw.MyLzwDecompressor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-imaging Show documentation
Show all versions of commons-imaging Show documentation
Apache Commons Imaging (previously Sanselan) is a pure-Java image library.
The newest version!
/*
* 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.mylzw;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteOrder;
import java.util.Arrays;
import org.apache.commons.imaging.ImagingException;
import org.apache.commons.imaging.common.Allocator;
public final class MyLzwDecompressor {
public interface Listener {
void code(int code);
void init(int clearCode, int eoiCode);
}
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 ByteOrder byteOrder;
private final Listener listener;
private final int clearCode;
private final int eoiCode;
private int written;
private final boolean tiffLZWMode;
public MyLzwDecompressor(final int initialCodeSize, final ByteOrder byteOrder, final boolean tiffLZWMode) throws ImagingException {
this(initialCodeSize, byteOrder, tiffLZWMode, null);
}
public MyLzwDecompressor(final int initialCodeSize, final ByteOrder byteOrder, final boolean tiffLZWMode, final Listener listener) throws ImagingException {
this.listener = listener;
this.byteOrder = byteOrder;
this.tiffLZWMode = tiffLZWMode;
this.initialCodeSize = initialCodeSize;
table = new byte[MAX_TABLE_SIZE][];
clearCode = 1 << initialCodeSize;
eoiCode = clearCode + 1;
if (null != listener) {
listener.init(clearCode, eoiCode);
}
initializeTable();
}
private void addStringToTable(final byte[] bytes) {
if (codes < 1 << codeSize) {
table[codes] = bytes;
codes++;
}
// If the table already full, then we simply ignore these bytes
// per the https://www.w3.org/Graphics/GIF/spec-gif89a.txt 'spec'.
checkCodeSize();
}
private byte[] appendBytes(final byte[] bytes, final byte b) {
final byte[] result = Arrays.copyOf(bytes, bytes.length + 1);
result[result.length - 1] = b;
return result;
}
private void checkCodeSize() {
int limit = 1 << codeSize;
if (tiffLZWMode) {
limit--;
}
if (codes == limit) {
incrementCodeSize();
}
}
private void clearTable() {
codes = (1 << initialCodeSize) + 2;
codeSize = initialCodeSize;
incrementCodeSize();
}
public byte[] decompress(final InputStream is, final int expectedLength) throws IOException {
int code;
int oldCode = -1;
try (MyBitInputStream mbis = new MyBitInputStream(is, byteOrder, tiffLZWMode);
ByteArrayOutputStream baos = new ByteArrayOutputStream(Allocator.checkByteArray(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));
} else if (isInTable(code)) {
writeToResult(baos, stringFromCode(code));
addStringToTable(appendBytes(stringFromCode(oldCode), firstChar(stringFromCode(code))));
} else {
final byte[] outString = appendBytes(stringFromCode(oldCode), firstChar(stringFromCode(oldCode)));
writeToResult(baos, outString);
addStringToTable(outString);
}
oldCode = code;
if (written >= expectedLength) {
break;
}
}
return baos.toByteArray();
}
}
private byte firstChar(final byte[] bytes) {
return bytes[0];
}
private int getNextCode(final MyBitInputStream is) throws IOException {
final int code = is.readBits(codeSize);
if (null != listener) {
listener.code(code);
}
return code;
}
private void incrementCodeSize() {
if (codeSize != 12) {
codeSize++;
}
}
private void initializeTable() throws ImagingException {
codeSize = initialCodeSize;
final int initialEntriesCount = 1 << codeSize + 2;
if (initialEntriesCount > table.length) {
throw new ImagingException(String.format("Invalid Lzw table length [%d]; entries count is [%d]", table.length, initialEntriesCount));
}
for (int i = 0; i < initialEntriesCount; i++) {
table[i] = new byte[] { (byte) i, };
}
}
private boolean isInTable(final int code) {
return code < codes;
}
private byte[] stringFromCode(final int code) throws ImagingException {
if (code >= codes || code < 0) {
throw new ImagingException("Bad Code: " + code + " codes: " + codes + " code_size: " + codeSize + ", table: " + table.length);
}
return table[code];
}
private void writeToResult(final OutputStream os, final byte[] bytes) throws IOException {
os.write(bytes);
written += bytes.length;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy