Hi
I've been hoping to steal commons-compress's cleaner and faster LZW
decompressor, and use it in commons-imaging for TIFF and GIF files,
and I've finally managed to make a patch to that effect.
This requires moving LZWInputStream to a
org.apache.commons.compress.compressors.lzw package and making it
public. Any objections to this?
Also imaging would have to depend on a SNAPSHOT of compress to be able
to import LZWInputStream, at least until the next release. Which is
when?
I am attaching patches in case anyone wants to have a look, but I can
commit them myself if there are no objections.
Thank you
Damjan
Index:
src/main/java/org/apache/commons/compress/archivers/zip/UnshrinkingInputStream.java
===================================================================
---
src/main/java/org/apache/commons/compress/archivers/zip/UnshrinkingInputStream.java
(revision 1632885)
+++
src/main/java/org/apache/commons/compress/archivers/zip/UnshrinkingInputStream.java
(working copy)
@@ -20,8 +20,9 @@
import java.io.IOException;
import java.io.InputStream;
+import java.nio.ByteOrder;
-import
org.apache.commons.compress.compressors.z._internal_.InternalLZWInputStream;
+import org.apache.commons.compress.compressors.lzw.LZWInputStream;
/**
* Input stream that decompresses ZIP method 1 (unshrinking). A variation of
the LZW algorithm, with some twists.
@@ -28,13 +29,13 @@
* @NotThreadSafe
* @since 1.7
*/
-class UnshrinkingInputStream extends InternalLZWInputStream {
+class UnshrinkingInputStream extends LZWInputStream {
private static final int MAX_CODE_SIZE = 13;
private static final int MAX_TABLE_SIZE = 1 << MAX_CODE_SIZE;
private final boolean[] isUsed;
public UnshrinkingInputStream(InputStream inputStream) throws IOException {
- super(inputStream);
+ super(inputStream, ByteOrder.LITTLE_ENDIAN);
setClearCode(codeSize);
initializeTables(MAX_CODE_SIZE);
isUsed = new boolean[prefixes.length];
Index:
src/main/java/org/apache/commons/compress/compressors/lzw/BitInputStream.java
===================================================================
---
src/main/java/org/apache/commons/compress/compressors/lzw/BitInputStream.java
(revision 0)
+++
src/main/java/org/apache/commons/compress/compressors/lzw/BitInputStream.java
(working copy)
@@ -0,0 +1,72 @@
+/*
+ * 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.compress.compressors.lzw;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteOrder;
+
+public class BitInputStream implements Closeable {
+ private final InputStream in;
+ private final ByteOrder byteOrder;
+ private int bitsCached = 0;
+ private int bitsCachedSize = 0;
+
+ public BitInputStream(final InputStream in, final ByteOrder byteOrder) {
+ this.in = in;
+ this.byteOrder = byteOrder;
+ }
+
+ public void close() throws IOException {
+ in.close();
+ }
+
+ public void clearBitCache() {
+ bitsCached = 0;
+ bitsCachedSize = 0;
+ }
+
+ public int readBits(final int count) throws IOException {
+ while (bitsCachedSize < count) {
+ final int nextByte = in.read();
+ if (nextByte < 0) {
+ return nextByte;
+ }
+ if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
+ bitsCached |= (nextByte << bitsCachedSize);
+ } else {
+ bitsCached <<= 8;
+ bitsCached |= nextByte;
+ }
+ bitsCachedSize += 8;
+ }
+
+ final int mask = (1 << count) - 1;
+ final int bitsOut;
+ if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
+ bitsOut = (bitsCached & mask);
+ bitsCached >>>= count;
+ } else {
+ bitsOut = (bitsCached >> (bitsCachedSize - count)) & mask;
+ }
+ bitsCachedSize -= count;
+ return bitsOut;
+ }
+}
Property changes on:
src/main/java/org/apache/commons/compress/compressors/lzw/BitInputStream.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index:
src/main/java/org/apache/commons/compress/compressors/lzw/LZWInputStream.java
===================================================================
---
src/main/java/org/apache/commons/compress/compressors/lzw/LZWInputStream.java
(revision 0)
+++
src/main/java/org/apache/commons/compress/compressors/lzw/LZWInputStream.java
(working copy)
@@ -0,0 +1,179 @@
+/*
+ * 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.compress.compressors.lzw;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteOrder;
+
+import org.apache.commons.compress.compressors.CompressorInputStream;
+
+/**
+ * <p>Generic LZW implementation. It is used internally for
+ * the Z compressor and the Unshrinking Zip file compression method,
+ * but may be useful for third-party projects in implementing their own LZW
variations.</p>
+ *
+ * @NotThreadSafe
+ * @since 1.7
+ */
+public abstract class LZWInputStream extends CompressorInputStream {
+ private final byte[] oneByte = new byte[1];
+
+ protected final BitInputStream in;
+ protected int clearCode = -1;
+ protected int codeSize = 9;
+ protected int previousCode = -1;
+ protected int tableSize = 0;
+ protected int[] prefixes;
+ protected byte[] characters;
+ private byte[] outputStack;
+ private int outputStackLocation;
+
+ protected LZWInputStream(final InputStream inputStream, final ByteOrder
byteOrder) {
+ this.in = new BitInputStream(inputStream, byteOrder);
+ }
+
+ @Override
+ public void close() throws IOException {
+ in.close();
+ }
+
+ @Override
+ public int read() throws IOException {
+ int ret = read(oneByte);
+ if (ret < 0) {
+ return ret;
+ }
+ return 0xff & oneByte[0];
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int bytesRead = readFromStack(b, off, len);
+ while (len - bytesRead > 0) {
+ int result = decompressNextSymbol();
+ if (result < 0) {
+ if (bytesRead > 0) {
+ count(bytesRead);
+ return bytesRead;
+ }
+ return result;
+ }
+ bytesRead += readFromStack(b, off + bytesRead, len - bytesRead);
+ }
+ count(bytesRead);
+ return bytesRead;
+ }
+
+ /**
+ * Read the next code and expand it.
+ */
+ protected abstract int decompressNextSymbol() throws IOException;
+
+ /**
+ * Add a new entry to the dictionary.
+ */
+ protected abstract int addEntry(int previousCode, byte character)
+ throws IOException;
+
+ /**
+ * Sets the clear code based on the code size.
+ */
+ protected void setClearCode(int codeSize) {
+ clearCode = (1 << (codeSize - 1));
+ }
+
+ /**
+ * Initializes the arrays based on the maximum code size.
+ */
+ protected void initializeTables(int maxCodeSize) {
+ final int maxTableSize = 1 << maxCodeSize;
+ prefixes = new int[maxTableSize];
+ characters = new byte[maxTableSize];
+ outputStack = new byte[maxTableSize];
+ outputStackLocation = maxTableSize;
+ final int max = 1 << 8;
+ for (int i = 0; i < max; i++) {
+ prefixes[i] = -1;
+ characters[i] = (byte) i;
+ }
+ }
+
+ /**
+ * Reads the next code from the stream.
+ */
+ protected int readNextCode() throws IOException {
+ return in.readBits(codeSize);
+ }
+
+ /**
+ * Adds a new entry if the maximum table size hasn't been exceeded
+ * and returns the new index.
+ */
+ protected int addEntry(int previousCode, byte character, int maxTableSize)
{
+ if (tableSize < maxTableSize) {
+ prefixes[tableSize] = previousCode;
+ characters[tableSize] = character;
+ return tableSize++;
+ }
+ return -1;
+ }
+
+ /**
+ * Add entry for repeat of previousCode we haven't added, yet.
+ */
+ protected int addRepeatOfPreviousCode() throws IOException {
+ if (previousCode == -1) {
+ // can't have a repeat for the very first code
+ throw new IOException("The first code can't be a reference to its
preceding code");
+ }
+ byte firstCharacter = 0;
+ for (int last = previousCode; last >= 0; last = prefixes[last]) {
+ firstCharacter = characters[last];
+ }
+ return addEntry(previousCode, firstCharacter);
+ }
+
+ /**
+ * Expands the entry with index code to the output stack and may
+ * create a new entry
+ */
+ protected int expandCodeToOutputStack(int code, boolean
addedUnfinishedEntry)
+ throws IOException {
+ for (int entry = code; entry >= 0; entry = prefixes[entry]) {
+ outputStack[--outputStackLocation] = characters[entry];
+ }
+ if (previousCode != -1 && !addedUnfinishedEntry) {
+ addEntry(previousCode, outputStack[outputStackLocation]);
+ }
+ previousCode = code;
+ return outputStackLocation;
+ }
+
+ private int readFromStack(byte[] b, int off, int len) {
+ int remainingInStack = outputStack.length - outputStackLocation;
+ if (remainingInStack > 0) {
+ int maxLength = Math.min(remainingInStack, len);
+ System.arraycopy(outputStack, outputStackLocation, b, off,
maxLength);
+ outputStackLocation += maxLength;
+ return maxLength;
+ }
+ return 0;
+ }
+}
Property changes on:
src/main/java/org/apache/commons/compress/compressors/lzw/LZWInputStream.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: src/main/java/org/apache/commons/compress/compressors/lzw/package.html
===================================================================
--- src/main/java/org/apache/commons/compress/compressors/lzw/package.html
(revision 0)
+++ src/main/java/org/apache/commons/compress/compressors/lzw/package.html
(working copy)
@@ -0,0 +1,23 @@
+<html>
+<!--
+
+ 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.
+
+-->
+ <body>
+ <p>Generic LZW implementation.</p>
+ </body>
+</html>
Property changes on:
src/main/java/org/apache/commons/compress/compressors/lzw/package.html
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index:
src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java
===================================================================
---
src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java
(revision 1632885)
+++
src/main/java/org/apache/commons/compress/compressors/z/ZCompressorInputStream.java
(working copy)
@@ -20,14 +20,16 @@
import java.io.IOException;
import java.io.InputStream;
-import
org.apache.commons.compress.compressors.z._internal_.InternalLZWInputStream;
+import java.nio.ByteOrder;
+import org.apache.commons.compress.compressors.lzw.LZWInputStream;
+
/**
* Input stream that decompresses .Z files.
* @NotThreadSafe
* @since 1.7
*/
-public class ZCompressorInputStream extends InternalLZWInputStream {
+public class ZCompressorInputStream extends LZWInputStream {
private static final int MAGIC_1 = 0x1f;
private static final int MAGIC_2 = 0x9d;
private static final int BLOCK_MODE_MASK = 0x80;
@@ -37,10 +39,10 @@
private long totalCodesRead = 0;
public ZCompressorInputStream(InputStream inputStream) throws IOException {
- super(inputStream);
- int firstByte = in.read();
- int secondByte = in.read();
- int thirdByte = in.read();
+ super(inputStream, ByteOrder.LITTLE_ENDIAN);
+ int firstByte = in.readBits(8);
+ int secondByte = in.readBits(8);
+ int thirdByte = in.readBits(8);
if (firstByte != MAGIC_1 || secondByte != MAGIC_2 || thirdByte < 0) {
throw new IOException("Input is not in .Z format");
}
@@ -87,8 +89,7 @@
for (long i = 0; i < codeReadsToThrowAway; i++) {
readNextCode();
}
- bitsCached = 0;
- bitsCachedSize = 0;
+ in.clearBitCache();
}
/**
Index:
src/main/java/org/apache/commons/compress/compressors/z/_internal_/InternalLZWInputStream.java
===================================================================
---
src/main/java/org/apache/commons/compress/compressors/z/_internal_/InternalLZWInputStream.java
(revision 1632885)
+++
src/main/java/org/apache/commons/compress/compressors/z/_internal_/InternalLZWInputStream.java
(working copy)
@@ -1,197 +0,0 @@
-/*
- * 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.compress.compressors.z._internal_;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.commons.compress.compressors.CompressorInputStream;
-
-/**
- * <strong>This class is only public for technical reasons and is not
- * part of Commons Compress' published API - it may change or
- * disappear without warning.</strong>
- *
- * <p>Base-class for traditional Unix ".Z" compression and the
- * Unshrinking method of ZIP archive.</p>
- *
- * @NotThreadSafe
- * @since 1.7
- */
-public abstract class InternalLZWInputStream extends CompressorInputStream {
- private final byte[] oneByte = new byte[1];
-
- protected final InputStream in;
- protected int clearCode = -1;
- protected int codeSize = 9;
- protected int bitsCached = 0;
- protected int bitsCachedSize = 0;
- protected int previousCode = -1;
- protected int tableSize = 0;
- protected int[] prefixes;
- protected byte[] characters;
- private byte[] outputStack;
- private int outputStackLocation;
-
- protected InternalLZWInputStream(InputStream inputStream) {
- this.in = inputStream;
- }
-
- @Override
- public void close() throws IOException {
- in.close();
- }
-
- @Override
- public int read() throws IOException {
- int ret = read(oneByte);
- if (ret < 0) {
- return ret;
- }
- return 0xff & oneByte[0];
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int bytesRead = readFromStack(b, off, len);
- while (len - bytesRead > 0) {
- int result = decompressNextSymbol();
- if (result < 0) {
- if (bytesRead > 0) {
- count(bytesRead);
- return bytesRead;
- }
- return result;
- }
- bytesRead += readFromStack(b, off + bytesRead, len - bytesRead);
- }
- count(bytesRead);
- return bytesRead;
- }
-
- /**
- * Read the next code and expand it.
- */
- protected abstract int decompressNextSymbol() throws IOException;
-
- /**
- * Add a new entry to the dictionary.
- */
- protected abstract int addEntry(int previousCode, byte character)
- throws IOException;
-
- /**
- * Sets the clear code based on the code size.
- */
- protected void setClearCode(int codeSize) {
- clearCode = (1 << (codeSize - 1));
- }
-
- /**
- * Initializes the arrays based on the maximum code size.
- */
- protected void initializeTables(int maxCodeSize) {
- final int maxTableSize = 1 << maxCodeSize;
- prefixes = new int[maxTableSize];
- characters = new byte[maxTableSize];
- outputStack = new byte[maxTableSize];
- outputStackLocation = maxTableSize;
- final int max = 1 << 8;
- for (int i = 0; i < max; i++) {
- prefixes[i] = -1;
- characters[i] = (byte) i;
- }
- }
-
- /**
- * Reads the next code from the stream.
- */
- protected int readNextCode() throws IOException {
- while (bitsCachedSize < codeSize) {
- final int nextByte = in.read();
- if (nextByte < 0) {
- return nextByte;
- }
- bitsCached |= (nextByte << bitsCachedSize);
- bitsCachedSize += 8;
- }
- final int mask = (1 << codeSize) - 1;
- final int code = (bitsCached & mask);
- bitsCached >>>= codeSize;
- bitsCachedSize -= codeSize;
- return code;
- }
-
- /**
- * Adds a new entry if the maximum table size hasn't been exceeded
- * and returns the new index.
- */
- protected int addEntry(int previousCode, byte character, int maxTableSize)
{
- if (tableSize < maxTableSize) {
- final int index = tableSize;
- prefixes[tableSize] = previousCode;
- characters[tableSize] = character;
- tableSize++;
- return index;
- }
- return -1;
- }
-
- /**
- * Add entry for repeat of previousCode we haven't added, yet.
- */
- protected int addRepeatOfPreviousCode() throws IOException {
- if (previousCode == -1) {
- // can't have a repeat for the very first code
- throw new IOException("The first code can't be a reference to its
preceding code");
- }
- byte firstCharacter = 0;
- for (int last = previousCode; last >= 0; last = prefixes[last]) {
- firstCharacter = characters[last];
- }
- return addEntry(previousCode, firstCharacter);
- }
-
- /**
- * Expands the entry with index code to the output stack and may
- * create a new entry
- */
- protected int expandCodeToOutputStack(int code, boolean
addedUnfinishedEntry)
- throws IOException {
- for (int entry = code; entry >= 0; entry = prefixes[entry]) {
- outputStack[--outputStackLocation] = characters[entry];
- }
- if (previousCode != -1 && !addedUnfinishedEntry) {
- addEntry(previousCode, outputStack[outputStackLocation]);
- }
- previousCode = code;
- return outputStackLocation;
- }
-
- private int readFromStack(byte[] b, int off, int len) {
- int remainingInStack = outputStack.length - outputStackLocation;
- if (remainingInStack > 0) {
- int maxLength = Math.min(remainingInStack, len);
- System.arraycopy(outputStack, outputStackLocation, b, off,
maxLength);
- outputStackLocation += maxLength;
- return maxLength;
- }
- return 0;
- }
-}
Index:
src/main/java/org/apache/commons/compress/compressors/z/_internal_/package.html
===================================================================
---
src/main/java/org/apache/commons/compress/compressors/z/_internal_/package.html
(revision 1632885)
+++
src/main/java/org/apache/commons/compress/compressors/z/_internal_/package.html
(working copy)
@@ -1,25 +0,0 @@
-<html>
-<!--
-
- 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.
-
--->
- <body>
- <p><strong>This package is not part of Commons Compress' published
- API. It may change without warning.</strong> Contains classes
- used by Commons Compress internally.</p>
- </body>
-</html>
Index: pom.xml
===================================================================
--- pom.xml (revision 1632877)
+++ pom.xml (working copy)
@@ -95,6 +95,29 @@
</configuration>
</plugin>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>2.3</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <createDependencyReducedPom>false</createDependencyReducedPom>
+ <minimizeJar>true</minimizeJar>
+ <relocations>
+ <relocation>
+ <pattern>org.apache.commons.compress</pattern>
+
<shadedPattern>org.apache.commons.compress.SHADED</shadedPattern>
+ </relocation>
+ </relocations>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
@@ -207,6 +230,12 @@
<version>2.4</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <!-- maven-shade-plugin takes it out later: -->
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.10-SNAPSHOT</version>
+ </dependency>
</dependencies>
<reporting>
Index: src/main/java/org/apache/commons/imaging/common/BinaryFunctions.java
===================================================================
--- src/main/java/org/apache/commons/imaging/common/BinaryFunctions.java
(revision 1632875)
+++ src/main/java/org/apache/commons/imaging/common/BinaryFunctions.java
(working copy)
@@ -313,7 +313,11 @@
}
public static byte[] getStreamBytes(final InputStream is) throws
IOException {
- final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ return getStreamBytes(is, 32);
+ }
+
+ public static byte[] getStreamBytes(final InputStream is, int
expectedSize) throws IOException {
+ final ByteArrayOutputStream os = new
ByteArrayOutputStream(expectedSize);
copyStreamToStream(is, os);
return os.toByteArray();
}
Index: src/main/java/org/apache/commons/imaging/formats/gif/GifImageParser.java
===================================================================
--- src/main/java/org/apache/commons/imaging/formats/gif/GifImageParser.java
(revision 1632875)
+++ src/main/java/org/apache/commons/imaging/formats/gif/GifImageParser.java
(working copy)
@@ -37,6 +37,7 @@
import org.apache.commons.imaging.ImageParser;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.ImageWriteException;
+import org.apache.commons.imaging.common.BinaryFunctions;
import org.apache.commons.imaging.common.BinaryOutputStream;
import org.apache.commons.imaging.common.IImageMetadata;
import org.apache.commons.imaging.common.ImageBuilder;
@@ -47,6 +48,8 @@
import org.apache.commons.imaging.palette.PaletteFactory;
import org.apache.commons.imaging.util.IoUtils;
+import sun.net.www.content.image.gif;
+
import static org.apache.commons.imaging.ImagingConstants.*;
import static org.apache.commons.imaging.common.BinaryFunctions.*;
@@ -383,9 +386,12 @@
final InputStream bais = new ByteArrayInputStream(bytes);
final int size = imageWidth * imageHeight;
- final MyLzwDecompressor myLzwDecompressor = new MyLzwDecompressor(
- lzwMinimumCodeSize, ByteOrder.LITTLE_ENDIAN);
- imageData = myLzwDecompressor.decompress(bais, size);
+ GifLzwInputStream gifLzwInputStream = new GifLzwInputStream(bais,
lzwMinimumCodeSize);
+ try {
+ imageData = BinaryFunctions.getStreamBytes(gifLzwInputStream,
size);
+ } finally {
+ IoUtils.closeQuietly(false, gifLzwInputStream);
+ }
} else {
final int LZWMinimumCodeSize = is.read();
if (getDebug()) {
Index:
src/main/java/org/apache/commons/imaging/formats/gif/GifLzwInputStream.java
===================================================================
--- src/main/java/org/apache/commons/imaging/formats/gif/GifLzwInputStream.java
(revision 0)
+++ src/main/java/org/apache/commons/imaging/formats/gif/GifLzwInputStream.java
(working copy)
@@ -0,0 +1,88 @@
+/*
+ * 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.formats.gif;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteOrder;
+import org.apache.commons.compress.compressors.lzw.LZWInputStream;
+
+public class GifLzwInputStream extends LZWInputStream {
+ private static final int MAX_CODE_SIZE = 12;
+ private static final int MAX_TABLE_SIZE = 1 << MAX_CODE_SIZE;
+
+ private final int initialCodeSize;
+ private final int eoiCode;
+
+ public GifLzwInputStream(final InputStream in, final int
initialCodeSize) {
+ super(in, ByteOrder.LITTLE_ENDIAN);
+ initializeTables(MAX_CODE_SIZE);
+ this.initialCodeSize = initialCodeSize + 1;
+ codeSize = initialCodeSize + 1;
+ clearCode = 1 << initialCodeSize;
+ eoiCode = clearCode + 1;
+ tableSize = eoiCode + 1;
+ }
+
+ private void clearEntries() {
+ tableSize = eoiCode + 1;
+ }
+
+ @Override
+ protected int addEntry(int previousCode, byte character) throws
IOException {
+ final int maxTableSize = 1 << codeSize;
+ int r = addEntry(previousCode, character, maxTableSize);
+ if (tableSize == maxTableSize && codeSize < MAX_CODE_SIZE) {
+ codeSize++;
+ }
+ return r;
+ }
+
+ @Override
+ protected int decompressNextSymbol() throws IOException {
+ //
+ // table entry table entry
+ // _____________ _____
+ // table entry / \ / \
+ // ____________/ \ \
+ // / / \ / \ \
+ // +---+---+---+---+---+---+---+---+---+---+
+ // | . | . | . | . | . | . | . | . | . | . |
+ // +---+---+---+---+---+---+---+---+---+---+
+ // |<--------->|<------------->|<----->|<->|
+ // symbol symbol symbol symbol
+ //
+ final int code = readNextCode();
+ if (code < 0) {
+ return -1;
+ } else if (code == clearCode) {
+ clearEntries();
+ codeSize = initialCodeSize;
+ previousCode = -1;
+ return 0;
+ } else if (code == eoiCode) {
+ return -1;
+ } else {
+ boolean addedUnfinishedEntry = false;
+ if (code >= tableSize) {
+ addRepeatOfPreviousCode();
+ addedUnfinishedEntry = true;
+ }
+ return expandCodeToOutputStack(code, addedUnfinishedEntry);
+ }
+ }
+}
Property changes on:
src/main/java/org/apache/commons/imaging/formats/gif/GifLzwInputStream.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index:
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReader.java
===================================================================
---
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReader.java
(revision 1632875)
+++
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReader.java
(working copy)
@@ -16,25 +16,34 @@
*/
package org.apache.commons.imaging.formats.tiff.datareaders;
+import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_CCITT_1D;
+import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_CCITT_GROUP_3;
+import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_CCITT_GROUP_4;
+import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_LZW;
+import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_PACKBITS;
+import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_UNCOMPRESSED;
+import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_FLAG_T4_OPTIONS_2D;
+import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_FLAG_T4_OPTIONS_FILL;
+import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_FLAG_T4_OPTIONS_UNCOMPRESSED_MODE;
+import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_FLAG_T6_OPTIONS_UNCOMPRESSED_MODE;
+
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.ByteOrder;
import org.apache.commons.imaging.ImageReadException;
+import org.apache.commons.imaging.common.BinaryFunctions;
import org.apache.commons.imaging.common.ImageBuilder;
import org.apache.commons.imaging.common.PackBits;
import org.apache.commons.imaging.common.itu_t4.T4AndT6Compression;
-import org.apache.commons.imaging.common.mylzw.MyLzwDecompressor;
import org.apache.commons.imaging.formats.tiff.TiffDirectory;
import org.apache.commons.imaging.formats.tiff.TiffField;
import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
import
org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreter;
+import org.apache.commons.imaging.util.IoUtils;
-import static
org.apache.commons.imaging.formats.tiff.constants.TiffConstants.*;
-
public abstract class DataReader {
protected final TiffDirectory directory;
protected final PhotometricInterpreter photometricInterpreter;
@@ -181,15 +190,12 @@
case TIFF_COMPRESSION_LZW: // LZW
{
final InputStream is = new ByteArrayInputStream(compressed);
-
- final int lzwMinimumCodeSize = 8;
-
- final MyLzwDecompressor myLzwDecompressor = new MyLzwDecompressor(
- lzwMinimumCodeSize, ByteOrder.BIG_ENDIAN);
-
- myLzwDecompressor.setTiffLZWMode();
-
- return myLzwDecompressor.decompress(is, expectedSize);
+ final TiffLzwInputStream tiffLzwInputStream = new
TiffLzwInputStream(is);
+ try {
+ return BinaryFunctions.getStreamBytes(tiffLzwInputStream,
expectedSize);
+ } finally {
+ IoUtils.closeQuietly(false, tiffLzwInputStream);
+ }
}
case TIFF_COMPRESSION_PACKBITS: // Packbits
Index:
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/TiffLzwInputStream.java
===================================================================
---
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/TiffLzwInputStream.java
(revision 0)
+++
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/TiffLzwInputStream.java
(working copy)
@@ -0,0 +1,88 @@
+/*
+ * 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.formats.tiff.datareaders;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteOrder;
+import org.apache.commons.compress.compressors.lzw.LZWInputStream;
+
+public class TiffLzwInputStream extends LZWInputStream {
+ private static final int MAX_CODE_SIZE = 12;
+ private static final int MAX_TABLE_SIZE = 1 << MAX_CODE_SIZE;
+
+ private final int eoiCode;
+
+ public TiffLzwInputStream(final InputStream in) {
+ super(in, ByteOrder.BIG_ENDIAN);
+ initializeTables(MAX_CODE_SIZE);
+ setClearCode(codeSize);
+ eoiCode = clearCode + 1;
+ tableSize = eoiCode + 1;
+ }
+
+ private void clearEntries() {
+ tableSize = eoiCode + 1;
+ }
+
+ @Override
+ protected int addEntry(int previousCode, byte character) throws
IOException {
+ final int maxTableSize = 1 << codeSize;
+ int r = addEntry(previousCode, character, maxTableSize);
+ // early change:
+ if (tableSize == (maxTableSize - 1) && codeSize < MAX_CODE_SIZE) {
+ codeSize++;
+ }
+ return r;
+ }
+
+ @Override
+ protected int decompressNextSymbol() throws IOException {
+ //
+ // table entry table entry
+ // _____________ _____
+ // table entry / \ / \
+ // ____________/ \ \
+ // / / \ / \ \
+ // +---+---+---+---+---+---+---+---+---+---+
+ // | . | . | . | . | . | . | . | . | . | . |
+ // +---+---+---+---+---+---+---+---+---+---+
+ // |<--------->|<------------->|<----->|<->|
+ // symbol symbol symbol symbol
+ //
+ final int code = readNextCode();
+ if (code < 0) {
+ return -1;
+ } else if (code == clearCode) {
+ clearEntries();
+ codeSize = 9;
+ previousCode = -1;
+ return 0;
+ } else if (code == eoiCode) {
+ return -1;
+ } else {
+ boolean addedUnfinishedEntry = false;
+ // Unbelievably, TIFF *really* needs us to treat every out of
range code as
+ // the "concatenation of previousCode with its own first
character" case!!!
+ if (code >= tableSize) {
+ addRepeatOfPreviousCode();
+ addedUnfinishedEntry = true;
+ }
+ return expandCodeToOutputStack(code, addedUnfinishedEntry);
+ }
+ }
+}
Property changes on:
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/TiffLzwInputStream.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index:
src/test/java/org/apache/commons/imaging/formats/tiff/TiffRoundtripTest.java
===================================================================
---
src/test/java/org/apache/commons/imaging/formats/tiff/TiffRoundtripTest.java
(revision 1632875)
+++
src/test/java/org/apache/commons/imaging/formats/tiff/TiffRoundtripTest.java
(working copy)
@@ -65,8 +65,21 @@
params);
final BufferedImage image2 =
Imaging.getBufferedImage(tempFile);
assertNotNull(image2);
+ compareImages(image, image2);
}
}
}
+
+ private void compareImages(BufferedImage image1, BufferedImage image2) {
+ int[] row1 = new int[image1.getWidth()];
+ int[] row2 = new int[image1.getWidth()];
+ for (int y = 0; y < image1.getHeight(); y++) {
+ image1.getRGB(0, y, image1.getWidth(), 1, row1, 0,
image1.getWidth());
+ image2.getRGB(0, y, image1.getWidth(), 1, row2, 0,
image1.getWidth());
+ for (int x = 0; x < row1.length; x++) {
+ assertEquals(row1[x], row2[x]);
+ }
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]