diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStream.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStream.java
new file mode 100644
index 00000000..0017b7fd
--- /dev/null
+++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStream.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2021, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.stream;
+
+import javax.imageio.stream.ImageInputStreamImpl;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import static com.twelvemonkeys.lang.Validate.notNull;
+import static java.lang.Math.max;
+
+/**
+ * A buffered replacement for {@link javax.imageio.stream.FileImageInputStream}
+ * that provides greatly improved performance for shorter reads, like single
+ * byte or bit reads.
+ * As with {@code javax.imageio.stream.FileImageInputStream}, either
+ * {@link File} or {@link RandomAccessFile} can be used as input.
+ *
+ * @see javax.imageio.stream.FileImageInputStream
+ */
+// TODO: Create a memory-mapped version?
+// Or not... From java.nio.channels.FileChannel.map:
+// For most operating systems, mapping a file into memory is more
+// expensive than reading or writing a few tens of kilobytes of data via
+// the usual {@link #read read} and {@link #write write} methods. From the
+// standpoint of performance it is generally only worth mapping relatively
+// large files into memory.
+public final class BufferedFileImageInputStream extends ImageInputStreamImpl {
+ static final int DEFAULT_BUFFER_SIZE = 8192;
+
+ private byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ private int bufferPos;
+ private int bufferLimit;
+
+ private final ByteBuffer integralCache = ByteBuffer.allocate(8);
+ private final byte[] integralCacheArray = integralCache.array();
+
+ private RandomAccessFile raf;
+
+ /**
+ * Constructs a BufferedFileImageInputStream
that will read from a given File
.
+ *
+ * @param file a File
to read from.
+ * @throws IllegalArgumentException if file
is null
.
+ * @throws FileNotFoundException if file
is a directory or cannot be opened for reading
+ * for any reason.
+ * @throws IOException if an I/O error occurs.
+ */
+ public BufferedFileImageInputStream(final File file) throws IOException {
+ this(new RandomAccessFile(notNull(file, "file"), "r"));
+ }
+
+ /**
+ * Constructs a BufferedFileImageInputStream
that will read from a given RandomAccessFile
.
+ *
+ * @param raf a RandomAccessFile
to read from.
+ * @throws IllegalArgumentException if raf
is null
.
+ */
+ public BufferedFileImageInputStream(final RandomAccessFile raf) {
+ this.raf = notNull(raf, "raf");
+ }
+
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ private boolean fillBuffer() throws IOException {
+ bufferPos = 0;
+ int length = raf.read(buffer, 0, buffer.length);
+ bufferLimit = max(length, 0);
+
+ return bufferLimit > 0;
+ }
+
+ private boolean bufferEmpty() {
+ return bufferPos >= bufferLimit;
+ }
+
+ @Override
+ public void setByteOrder(ByteOrder byteOrder) {
+ super.setByteOrder(byteOrder);
+ integralCache.order(byteOrder);
+ }
+
+ @Override
+ public int read() throws IOException {
+ checkClosed();
+
+ if (bufferEmpty() && !fillBuffer()) {
+ return -1;
+ }
+
+ bitOffset = 0;
+ streamPos++;
+
+ return buffer[bufferPos++] & 0xff;
+ }
+
+ @Override
+ public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
+ checkClosed();
+ bitOffset = 0;
+
+ if (bufferEmpty()) {
+ // Bypass buffer if buffer is empty for reads longer than buffer
+ if (pLength >= buffer.length) {
+ return readDirect(pBuffer, pOffset, pLength);
+ }
+ else if (!fillBuffer()) {
+ return -1;
+ }
+ }
+
+ return readBuffered(pBuffer, pOffset, pLength);
+ }
+
+ private int readDirect(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
+ // Invalidate the buffer, as its contents is no longer in sync with the stream's position.
+ bufferLimit = 0;
+ int read = raf.read(pBuffer, pOffset, pLength);
+
+ if (read > 0) {
+ streamPos += read;
+ }
+
+ return read;
+ }
+
+ private int readBuffered(final byte[] pBuffer, final int pOffset, final int pLength) {
+ // Read as much as possible from buffer
+ int length = Math.min(bufferLimit - bufferPos, pLength);
+
+ if (length > 0) {
+ System.arraycopy(buffer, bufferPos, pBuffer, pOffset, length);
+ bufferPos += length;
+ streamPos += length;
+ }
+
+ return length;
+ }
+
+ public long length() {
+ // WTF?! This method is allowed to throw IOException in the interface...
+ try {
+ checkClosed();
+ return raf.length();
+ }
+ catch (IOException ignore) {
+ }
+
+ return -1;
+ }
+
+ public void close() throws IOException {
+ super.close();
+
+ raf.close();
+
+ raf = null;
+ buffer = null;
+ }
+
+ // Need to override the readShort(), readInt() and readLong() methods,
+ // because the implementations in ImageInputStreamImpl expects the
+ // read(byte[], int, int) to always read the expected number of bytes,
+ // causing uninitialized values, alignment issues and EOFExceptions at
+ // random places...
+ // Notes:
+ // * readUnsignedXx() is covered by their signed counterparts
+ // * readChar() is covered by readShort()
+ // * readFloat() and readDouble() is covered by readInt() and readLong()
+ // respectively.
+ // * readLong() may be covered by two readInt()s, we'll override to be safe
+
+ @Override
+ public short readShort() throws IOException {
+ readFully(integralCacheArray, 0, 2);
+
+ return integralCache.getShort(0);
+ }
+
+ @Override
+ public int readInt() throws IOException {
+ readFully(integralCacheArray, 0, 4);
+
+ return integralCache.getInt(0);
+ }
+
+ @Override
+ public long readLong() throws IOException {
+ readFully(integralCacheArray, 0, 8);
+
+ return integralCache.getLong(0);
+ }
+
+ @Override
+ public void seek(long position) throws IOException {
+ checkClosed();
+
+ if (position < flushedPos) {
+ throw new IndexOutOfBoundsException("position < flushedPos!");
+ }
+
+ bitOffset = 0;
+
+ if (streamPos == position) {
+ return;
+ }
+
+ // Optimized to not invalidate buffer if new position is within current buffer
+ long newBufferPos = bufferPos + position - streamPos;
+ if (newBufferPos >= 0 && newBufferPos <= bufferLimit) {
+ bufferPos = (int) newBufferPos;
+ }
+ else {
+ // Will invalidate buffer
+ bufferLimit = 0;
+ raf.seek(position);
+ }
+
+ streamPos = position;
+ }
+}
diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamSpi.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamSpi.java
new file mode 100644
index 00000000..9574ae71
--- /dev/null
+++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamSpi.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2021, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.stream;
+
+import com.twelvemonkeys.imageio.spi.ProviderInfo;
+
+import javax.imageio.spi.ImageInputStreamSpi;
+import javax.imageio.spi.ServiceRegistry;
+import javax.imageio.stream.ImageInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Locale;
+
+/**
+ * BufferedFileImageInputStreamSpi
+ * Experimental
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: BufferedFileImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
+ */
+public class BufferedFileImageInputStreamSpi extends ImageInputStreamSpi {
+ public BufferedFileImageInputStreamSpi() {
+ this(new StreamProviderInfo());
+ }
+
+ private BufferedFileImageInputStreamSpi(ProviderInfo providerInfo) {
+ super(providerInfo.getVendorName(), providerInfo.getVersion(), File.class);
+ }
+
+ @Override
+ public void onRegistration(final ServiceRegistry registry, final Class> category) {
+ Iterator providers = registry.getServiceProviders(ImageInputStreamSpi.class, new FileInputFilter(), true);
+
+ while (providers.hasNext()) {
+ ImageInputStreamSpi provider = providers.next();
+ if (provider != this) {
+ registry.setOrdering(ImageInputStreamSpi.class, this, provider);
+ }
+ }
+ }
+
+ public ImageInputStream createInputStreamInstance(final Object input, final boolean pUseCache, final File pCacheDir) throws IOException {
+ if (input instanceof File) {
+ File file = (File) input;
+ return new BufferedFileImageInputStream(file);
+ }
+ else {
+ throw new IllegalArgumentException("Expected input of type URL: " + input);
+ }
+ }
+
+ @Override
+ public boolean canUseCacheFile() {
+ return false;
+ }
+
+ public String getDescription(final Locale pLocale) {
+ return "Service provider that instantiates an ImageInputStream from a File";
+ }
+
+ private static class FileInputFilter implements ServiceRegistry.Filter {
+ @Override
+ public boolean filter(final Object provider) {
+ return ((ImageInputStreamSpi) provider).getInputClass() == File.class;
+ }
+ }
+}
diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedImageInputStream.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedImageInputStream.java
index dca1a5ca..9b320929 100644
--- a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedImageInputStream.java
+++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedImageInputStream.java
@@ -43,15 +43,18 @@ import static com.twelvemonkeys.lang.Validate.notNull;
* A buffered {@code ImageInputStream}.
* Experimental - seems to be effective for {@link javax.imageio.stream.FileImageInputStream}
* and {@link javax.imageio.stream.FileCacheImageInputStream} when doing a lot of single-byte reads
- * (or short byte-array reads) on OS X at least.
+ * (or short byte-array reads).
* Code that uses the {@code readFully} methods are not affected by the issue.
+ *
+ * NOTE: Invoking {@code close()} will NOT close the wrapped stream.
*
* @author Harald Kuhr
* @author last modified by $Author: haraldk$
* @version $Id: BufferedFileImageInputStream.java,v 1.0 May 15, 2008 4:36:49 PM haraldk Exp$
+ *
+ * @deprecated Use {@link BufferedFileImageInputStream} instead.
*/
-// TODO: Create a provider for this (wrapping the FileIIS and FileCacheIIS classes), and disable the Sun built-in spis?
-// TODO: Test on other platforms, might be just an OS X issue
+@Deprecated
public final class BufferedImageInputStream extends ImageInputStreamImpl implements ImageInputStream {
static final int DEFAULT_BUFFER_SIZE = 8192;
diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedRAFImageInputStreamSpi.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedRAFImageInputStreamSpi.java
new file mode 100644
index 00000000..bb5e3fef
--- /dev/null
+++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/BufferedRAFImageInputStreamSpi.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2021, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.stream;
+
+import com.twelvemonkeys.imageio.spi.ProviderInfo;
+
+import javax.imageio.spi.ImageInputStreamSpi;
+import javax.imageio.spi.ServiceRegistry;
+import javax.imageio.stream.ImageInputStream;
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.util.Iterator;
+import java.util.Locale;
+
+/**
+ * BufferedRAFImageInputStreamSpi
+ * Experimental
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: BufferedRAFImageInputStreamSpi.java,v 1.0 May 15, 2008 2:14:59 PM haraldk Exp$
+ */
+public class BufferedRAFImageInputStreamSpi extends ImageInputStreamSpi {
+ public BufferedRAFImageInputStreamSpi() {
+ this(new StreamProviderInfo());
+ }
+
+ private BufferedRAFImageInputStreamSpi(ProviderInfo providerInfo) {
+ super(providerInfo.getVendorName(), providerInfo.getVersion(), RandomAccessFile.class);
+ }
+
+ @Override
+ public void onRegistration(final ServiceRegistry registry, final Class> category) {
+ Iterator providers = registry.getServiceProviders(ImageInputStreamSpi.class, new RAFInputFilter(), true);
+
+ while (providers.hasNext()) {
+ ImageInputStreamSpi provider = providers.next();
+ if (provider != this) {
+ registry.setOrdering(ImageInputStreamSpi.class, this, provider);
+ }
+ }
+ }
+
+ public ImageInputStream createInputStreamInstance(final Object input, final boolean pUseCache, final File pCacheDir) {
+ if (input instanceof RandomAccessFile) {
+ RandomAccessFile file = (RandomAccessFile) input;
+ return new BufferedFileImageInputStream(file);
+ }
+ else {
+ throw new IllegalArgumentException("Expected input of type URL: " + input);
+ }
+ }
+
+ @Override
+ public boolean canUseCacheFile() {
+ return false;
+ }
+
+ public String getDescription(final Locale pLocale) {
+ return "Service provider that instantiates an ImageInputStream from a RandomAccessFile";
+ }
+
+ private static class RAFInputFilter implements ServiceRegistry.Filter {
+ @Override
+ public boolean filter(final Object provider) {
+ return ((ImageInputStreamSpi) provider).getInputClass() == RandomAccessFile.class;
+ }
+ }
+}
diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/URLImageInputStreamSpi.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/URLImageInputStreamSpi.java
index e0426b0a..b56204c7 100644
--- a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/URLImageInputStreamSpi.java
+++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/stream/URLImageInputStreamSpi.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, Harald Kuhr
+ * Copyright (c) 2021, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,6 @@ import com.twelvemonkeys.imageio.spi.ProviderInfo;
import javax.imageio.spi.ImageInputStreamSpi;
import javax.imageio.stream.FileCacheImageInputStream;
-import javax.imageio.stream.FileImageInputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.io.File;
@@ -72,7 +71,7 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
// Special case for file protocol, a lot faster than FileCacheImageInputStream
if ("file".equals(url.getProtocol())) {
try {
- return new BufferedImageInputStream(new FileImageInputStream(new File(url.toURI())));
+ return new BufferedFileImageInputStream(new File(url.toURI()));
}
catch (URISyntaxException ignore) {
// This should never happen, but if it does, we'll fall back to using the stream
@@ -81,29 +80,29 @@ public class URLImageInputStreamSpi extends ImageInputStreamSpi {
}
// Otherwise revert to cached
- final InputStream stream = url.openStream();
+ final InputStream urlStream = url.openStream();
if (pUseCache) {
- return new BufferedImageInputStream(new FileCacheImageInputStream(stream, pCacheDir) {
+ return new FileCacheImageInputStream(urlStream, pCacheDir) {
@Override
public void close() throws IOException {
try {
super.close();
}
finally {
- stream.close(); // NOTE: If this line throws IOE, it will shadow the original..
+ urlStream.close(); // NOTE: If this line throws IOE, it will shadow the original..
}
}
- });
+ };
}
else {
- return new MemoryCacheImageInputStream(stream) {
+ return new MemoryCacheImageInputStream(urlStream) {
@Override
public void close() throws IOException {
try {
super.close();
}
finally {
- stream.close(); // NOTE: If this line throws IOE, it will shadow the original..
+ urlStream.close(); // NOTE: If this line throws IOE, it will shadow the original..
}
}
};
diff --git a/imageio/imageio-core/src/main/resources/META-INF/services/javax.imageio.spi.ImageInputStreamSpi b/imageio/imageio-core/src/main/resources/META-INF/services/javax.imageio.spi.ImageInputStreamSpi
new file mode 100644
index 00000000..c31ffbaf
--- /dev/null
+++ b/imageio/imageio-core/src/main/resources/META-INF/services/javax.imageio.spi.ImageInputStreamSpi
@@ -0,0 +1,2 @@
+com.twelvemonkeys.imageio.stream.BufferedFileImageInputStreamSpi
+com.twelvemonkeys.imageio.stream.BufferedRAFImageInputStreamSpi
diff --git a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamSpiTest.java b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamSpiTest.java
new file mode 100644
index 00000000..5622fd2f
--- /dev/null
+++ b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamSpiTest.java
@@ -0,0 +1,17 @@
+package com.twelvemonkeys.imageio.stream;
+
+import javax.imageio.spi.ImageInputStreamSpi;
+import java.io.File;
+import java.io.IOException;
+
+public class BufferedFileImageInputStreamSpiTest extends ImageInputStreamSpiTest {
+ @Override
+ protected ImageInputStreamSpi createProvider() {
+ return new BufferedFileImageInputStreamSpi();
+ }
+
+ @Override
+ protected File createInput() throws IOException {
+ return File.createTempFile("test-", ".tst");
+ }
+}
\ No newline at end of file
diff --git a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamTest.java b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamTest.java
new file mode 100755
index 00000000..822db90e
--- /dev/null
+++ b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedFileImageInputStreamTest.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2020, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.stream;
+
+import org.junit.Test;
+import org.junit.function.ThrowingRunnable;
+
+import javax.imageio.stream.ImageInputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.file.Files;
+import java.util.Random;
+
+import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * BufferedFileImageInputStreamTestCase
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
+ */
+public class BufferedFileImageInputStreamTest {
+ private final Random random = new Random(170984354357234566L);
+
+ private File randomDataToFile(byte[] data) throws IOException {
+ random.nextBytes(data);
+
+ File file = File.createTempFile("read", ".tmp");
+ Files.write(file.toPath(), data);
+ return file;
+ }
+
+ @Test
+ public void testCreate() throws IOException {
+ BufferedFileImageInputStream stream = new BufferedFileImageInputStream(File.createTempFile("empty", ".tmp"));
+ assertEquals("Data length should be same as stream length", 0, stream.length());
+ }
+
+ @Test
+ public void testCreateNullFile() throws IOException {
+ try {
+ new BufferedFileImageInputStream((File) null);
+ fail("Expected IllegalArgumentException");
+ }
+ catch (IllegalArgumentException expected) {
+ assertNotNull("Null exception message", expected.getMessage());
+ String message = expected.getMessage().toLowerCase();
+ assertTrue("Exception message does not contain parameter name", message.contains("file"));
+ assertTrue("Exception message does not contain null", message.contains("null"));
+ }
+ }
+
+ @Test
+ public void testCreateNullRAF() {
+ try {
+ new BufferedFileImageInputStream((RandomAccessFile) null);
+ fail("Expected IllegalArgumentException");
+ }
+ catch (IllegalArgumentException expected) {
+ assertNotNull("Null exception message", expected.getMessage());
+ String message = expected.getMessage().toLowerCase();
+ assertTrue("Exception message does not contain parameter name", message.contains("raf"));
+ assertTrue("Exception message does not contain null", message.contains("null"));
+ }
+ }
+
+ @Test
+ public void testRead() throws IOException {
+ byte[] data = new byte[1024 * 1024];
+ File file = randomDataToFile(data);
+
+ BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file);
+
+ assertEquals("File length should be same as stream length", file.length(), stream.length());
+
+ for (byte value : data) {
+ assertEquals("Wrong data read", value & 0xff, stream.read());
+ }
+ }
+
+ @Test
+ public void testReadArray() throws IOException {
+ byte[] data = new byte[1024 * 1024];
+ File file = randomDataToFile(data);
+
+ BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file);
+
+ assertEquals("File length should be same as stream length", file.length(), stream.length());
+
+ byte[] result = new byte[1024];
+
+ for (int i = 0; i < data.length / result.length; i++) {
+ stream.readFully(result);
+ assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
+ }
+ }
+
+ @Test
+ public void testReadSkip() throws IOException {
+ byte[] data = new byte[1024 * 14];
+ File file = randomDataToFile(data);
+
+ BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file);
+
+ assertEquals("File length should be same as stream length", file.length(), stream.length());
+
+ byte[] result = new byte[7];
+
+ for (int i = 0; i < data.length / result.length; i += 2) {
+ stream.readFully(result);
+ stream.skipBytes(result.length);
+ assertTrue("Wrong data read: " + i, rangeEquals(data, i * result.length, result, 0, result.length));
+ }
+ }
+
+ @Test
+ public void testReadSeek() throws IOException {
+ byte[] data = new byte[1024 * 18];
+ File file = randomDataToFile(data);
+
+ BufferedFileImageInputStream stream = new BufferedFileImageInputStream(file);
+
+ assertEquals("File length should be same as stream length", file.length(), stream.length());
+
+ byte[] result = new byte[9];
+
+ for (int i = 0; i < data.length / result.length; i++) {
+ // Read backwards
+ long newPos = stream.length() - result.length - i * result.length;
+ stream.seek(newPos);
+ assertEquals("Wrong stream position", newPos, stream.getStreamPosition());
+ stream.readFully(result);
+ assertTrue("Wrong data read: " + i, rangeEquals(data, (int) newPos, result, 0, result.length));
+ }
+ }
+
+ @Test
+ public void testReadBitRandom() throws IOException {
+ byte[] bytes = new byte[8];
+ File file = randomDataToFile(bytes);
+ long value = ByteBuffer.wrap(bytes).getLong();
+
+ // Create stream
+ ImageInputStream stream = new BufferedFileImageInputStream(file);
+
+ for (int i = 1; i <= 64; i++) {
+ assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
+ }
+ }
+
+ @Test
+ public void testReadBitsRandom() throws IOException {
+ byte[] bytes = new byte[8];
+ File file = randomDataToFile(bytes);
+ long value = ByteBuffer.wrap(bytes).getLong();
+
+ // Create stream
+ ImageInputStream stream = new BufferedFileImageInputStream(file);
+
+ for (int i = 1; i <= 64; i++) {
+ stream.seek(0);
+ assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
+ assertEquals(i % 8, stream.getBitOffset());
+ }
+ }
+
+ @Test
+ public void testReadBitsRandomOffset() throws IOException {
+ byte[] bytes = new byte[8];
+ File file = randomDataToFile(bytes);
+ long value = ByteBuffer.wrap(bytes).getLong();
+
+ // Create stream
+ ImageInputStream stream = new BufferedFileImageInputStream(file);
+
+ for (int i = 1; i <= 60; i++) {
+ stream.seek(0);
+ stream.setBitOffset(i % 8);
+ assertEquals(String.format("bit %d differ", i), (value << (i % 8)) >>> (64L - i), stream.readBits(i));
+ assertEquals(i * 2 % 8, stream.getBitOffset());
+ }
+ }
+
+ @Test
+ public void testReadShort() throws IOException {
+ byte[] bytes = new byte[8743]; // Slightly more than one buffer size
+ File file = randomDataToFile(bytes);
+ ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
+ final ImageInputStream stream = new BufferedFileImageInputStream(file);
+ stream.setByteOrder(ByteOrder.BIG_ENDIAN);
+
+ for (int i = 0; i < bytes.length / 2; i++) {
+ assertEquals(buffer.getShort(), stream.readShort());
+ }
+
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readShort();
+ }
+ });
+
+ stream.seek(0);
+ stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ buffer.position(0);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (int i = 0; i < bytes.length / 2; i++) {
+ assertEquals(buffer.getShort(), stream.readShort());
+ }
+
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readShort();
+ }
+ });
+ }
+
+ @Test
+ public void testReadInt() throws IOException {
+ byte[] bytes = new byte[8743]; // Slightly more than one buffer size
+ File file = randomDataToFile(bytes);
+ ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
+ final ImageInputStream stream = new BufferedFileImageInputStream(file);
+ stream.setByteOrder(ByteOrder.BIG_ENDIAN);
+
+ for (int i = 0; i < bytes.length / 4; i++) {
+ assertEquals(buffer.getInt(), stream.readInt());
+ }
+
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readInt();
+ }
+ });
+
+ stream.seek(0);
+ stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ buffer.position(0);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (int i = 0; i < bytes.length / 4; i++) {
+ assertEquals(buffer.getInt(), stream.readInt());
+ }
+
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readInt();
+ }
+ });
+ }
+
+ @Test
+ public void testReadLong() throws IOException {
+ byte[] bytes = new byte[8743]; // Slightly more than one buffer size
+ File file = randomDataToFile(bytes);
+ ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
+ final ImageInputStream stream = new BufferedFileImageInputStream(file);
+ stream.setByteOrder(ByteOrder.BIG_ENDIAN);
+
+ for (int i = 0; i < bytes.length / 8; i++) {
+ assertEquals(buffer.getLong(), stream.readLong());
+ }
+
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readLong();
+ }
+ });
+
+ stream.seek(0);
+ stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ buffer.position(0);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ for (int i = 0; i < bytes.length / 8; i++) {
+ assertEquals(buffer.getLong(), stream.readLong());
+ }
+
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readLong();
+ }
+ });
+ }
+
+ @Test
+ public void testSeekPastEOF() throws IOException {
+ byte[] bytes = new byte[9];
+ File file = randomDataToFile(bytes);
+
+ final ImageInputStream stream = new BufferedFileImageInputStream(file);
+ stream.seek(1000);
+
+ assertEquals(-1, stream.read());
+ assertEquals(-1, stream.read(new byte[1], 0, 1));
+
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readFully(new byte[1]);
+ }
+ });
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readByte();
+ }
+ });
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readShort();
+ }
+ });
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readInt();
+ }
+ });
+ assertThrows(EOFException.class, new ThrowingRunnable() {
+ @Override
+ public void run() throws Throwable {
+ stream.readLong();
+ }
+ });
+
+ stream.seek(0);
+ for (byte value : bytes) {
+ assertEquals(value, stream.readByte());
+ }
+
+ assertEquals(-1, stream.read());
+ }
+
+ @Test
+ public void testClose() throws IOException {
+ // Create wrapper stream
+ RandomAccessFile mock = mock(RandomAccessFile.class);
+ ImageInputStream stream = new BufferedFileImageInputStream(mock);
+
+ stream.close();
+ verify(mock, only()).close();
+ }
+}
diff --git a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedRAFImageInputStreamSpiTest.java b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedRAFImageInputStreamSpiTest.java
new file mode 100644
index 00000000..8441bcb8
--- /dev/null
+++ b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/BufferedRAFImageInputStreamSpiTest.java
@@ -0,0 +1,18 @@
+package com.twelvemonkeys.imageio.stream;
+
+import javax.imageio.spi.ImageInputStreamSpi;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+public class BufferedRAFImageInputStreamSpiTest extends ImageInputStreamSpiTest {
+ @Override
+ protected ImageInputStreamSpi createProvider() {
+ return new BufferedRAFImageInputStreamSpi();
+ }
+
+ @Override
+ protected RandomAccessFile createInput() throws IOException {
+ return new RandomAccessFile(File.createTempFile("test-", ".tst"), "r");
+ }
+}
\ No newline at end of file
diff --git a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStreamTest.java b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStreamTest.java
index 35a3d677..5d770994 100755
--- a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStreamTest.java
+++ b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/ByteArrayImageInputStreamTest.java
@@ -39,11 +39,11 @@ import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rang
import static org.junit.Assert.*;
/**
- * ByteArrayImageInputStreamTestCase
+ * ByteArrayImageInputStreamTest
*
* @author Harald Kuhr
* @author last modified by $Author: haraldk$
- * @version $Id: ByteArrayImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
+ * @version $Id: ByteArrayImageInputStreamTest.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
*/
public class ByteArrayImageInputStreamTest {
private final Random random = new Random(1709843507234566L);
diff --git a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/ImageInputStreamSpiTest.java b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/ImageInputStreamSpiTest.java
index df95fee9..cbc69648 100644
--- a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/ImageInputStreamSpiTest.java
+++ b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/stream/ImageInputStreamSpiTest.java
@@ -18,7 +18,7 @@ abstract class ImageInputStreamSpiTest {
protected abstract ImageInputStreamSpi createProvider();
- protected abstract T createInput();
+ protected abstract T createInput() throws IOException;
@Test
public void testInputClass() {
diff --git a/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java b/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java
index cc2f1439..d3c8f6ef 100755
--- a/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java
+++ b/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java
@@ -30,6 +30,17 @@
package com.twelvemonkeys.imageio.plugins.iff;
+import com.twelvemonkeys.image.ResampleOp;
+import com.twelvemonkeys.imageio.ImageReaderBase;
+import com.twelvemonkeys.imageio.util.IIOUtil;
+import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
+import com.twelvemonkeys.io.enc.DecoderStream;
+import com.twelvemonkeys.io.enc.PackBitsDecoder;
+
+import javax.imageio.*;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
@@ -41,23 +52,6 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
-import javax.imageio.IIOException;
-import javax.imageio.ImageIO;
-import javax.imageio.ImageReadParam;
-import javax.imageio.ImageReader;
-import javax.imageio.ImageTypeSpecifier;
-import javax.imageio.metadata.IIOMetadata;
-import javax.imageio.spi.ImageReaderSpi;
-import javax.imageio.stream.ImageInputStream;
-
-import com.twelvemonkeys.image.ResampleOp;
-import com.twelvemonkeys.imageio.ImageReaderBase;
-import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
-import com.twelvemonkeys.imageio.util.IIOUtil;
-import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
-import com.twelvemonkeys.io.enc.DecoderStream;
-import com.twelvemonkeys.io.enc.PackBitsDecoder;
-
/**
* Reader for Commodore Amiga (Electronic Arts) IFF ILBM (InterLeaved BitMap) and PBM
* format (Packed BitMap).
@@ -824,8 +818,7 @@ public final class IFFImageReader extends ImageReaderBase {
continue;
}
- try {
- ImageInputStream input = new BufferedImageInputStream(ImageIO.createImageInputStream(file));
+ try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
boolean canRead = reader.getOriginatingProvider().canDecodeInput(input);
if (canRead) {
diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java
index fc89031f..84da94f2 100644
--- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java
+++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java
@@ -30,37 +30,6 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
-import java.awt.*;
-import java.awt.color.ColorSpace;
-import java.awt.color.ICC_ColorSpace;
-import java.awt.color.ICC_Profile;
-import java.awt.image.*;
-import java.io.DataInput;
-import java.io.DataInputStream;
-import java.io.EOFException;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.SequenceInputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.imageio.IIOException;
-import javax.imageio.ImageIO;
-import javax.imageio.ImageReadParam;
-import javax.imageio.ImageReader;
-import javax.imageio.ImageTypeSpecifier;
-import javax.imageio.event.IIOReadUpdateListener;
-import javax.imageio.event.IIOReadWarningListener;
-import javax.imageio.metadata.IIOMetadata;
-import javax.imageio.metadata.IIOMetadataFormatImpl;
-import javax.imageio.metadata.IIOMetadataNode;
-import javax.imageio.spi.ImageReaderSpi;
-import javax.imageio.stream.ImageInputStream;
-
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.color.ColorSpaces;
import com.twelvemonkeys.imageio.color.YCbCrConverter;
@@ -72,7 +41,6 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
-import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
@@ -80,6 +48,23 @@ import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import com.twelvemonkeys.lang.Validate;
import com.twelvemonkeys.xml.XMLSerializer;
+import javax.imageio.*;
+import javax.imageio.event.IIOReadUpdateListener;
+import javax.imageio.event.IIOReadWarningListener;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataFormatImpl;
+import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
+import java.awt.*;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.awt.image.*;
+import java.io.*;
+import java.util.List;
+import java.util.*;
+
/**
* A JPEG {@code ImageReader} implementation based on the JRE {@code JPEGImageReader},
* that adds support and properly handles cases where the JRE version throws exceptions.
@@ -140,7 +125,7 @@ public final class JPEGImageReader extends ImageReaderBase {
private List segments;
private int currentStreamIndex = 0;
- private List streamOffsets = new ArrayList<>();
+ private final List streamOffsets = new ArrayList<>();
protected JPEGImageReader(final ImageReaderSpi provider, final ImageReader delegate) {
super(provider);
@@ -197,10 +182,10 @@ public final class JPEGImageReader extends ImageReaderBase {
return true;
}
}
- catch (IIOException ignore) {
+ catch (IIOException e) {
// May happen if no SOF is found, in case we'll just fall through
if (DEBUG) {
- ignore.printStackTrace();
+ e.printStackTrace();
}
}
@@ -747,26 +732,26 @@ public final class JPEGImageReader extends ImageReaderBase {
long lastKnownSOIOffset = streamOffsets.get(streamOffsets.size() - 1);
imageInput.seek(lastKnownSOIOffset);
- try (ImageInputStream stream = new BufferedImageInputStream(imageInput)) { // Extreme (10s -> 50ms) speedup if imageInput is FileIIS
+ try {
for (int i = streamOffsets.size() - 1; i < imageIndex; i++) {
long start = 0;
if (DEBUG) {
start = System.currentTimeMillis();
- System.out.println(String.format("Start seeking for image index %d", i + 1));
+ System.out.printf("Start seeking for image index %d%n", i + 1);
}
// Need to skip over segments, as they may contain JPEG markers (eg. JFXX or EXIF thumbnail)
- JPEGSegmentUtil.readSegments(stream, Collections.>emptyMap());
+ JPEGSegmentUtil.readSegments(imageInput, Collections.>emptyMap());
// Now, search for EOI and following SOI...
int marker;
- while ((marker = stream.read()) != -1) {
- if (marker == 0xFF && (0xFF00 | stream.readUnsignedByte()) == JPEG.EOI) {
+ while ((marker = imageInput.read()) != -1) {
+ if (marker == 0xFF && (0xFF00 | imageInput.readUnsignedByte()) == JPEG.EOI) {
// Found EOI, now the SOI should be nearby...
- while ((marker = stream.read()) != -1) {
- if (marker == 0xFF && (0xFF00 | stream.readUnsignedByte()) == JPEG.SOI) {
- long nextSOIOffset = stream.getStreamPosition() - 2;
+ while ((marker = imageInput.read()) != -1) {
+ if (marker == 0xFF && (0xFF00 | imageInput.readUnsignedByte()) == JPEG.SOI) {
+ long nextSOIOffset = imageInput.getStreamPosition() - 2;
imageInput.seek(nextSOIOffset);
streamOffsets.add(nextSOIOffset);
@@ -780,10 +765,9 @@ public final class JPEGImageReader extends ImageReaderBase {
}
if (DEBUG) {
- System.out.println(String.format("Seek in %d ms", System.currentTimeMillis() - start));
+ System.out.printf("Seek in %d ms%n", System.currentTimeMillis() - start);
}
}
-
}
catch (EOFException eof) {
IndexOutOfBoundsException ioobe = new IndexOutOfBoundsException("Image index " + imageIndex + " not found in stream");
@@ -843,9 +827,9 @@ public final class JPEGImageReader extends ImageReaderBase {
return JPEGSegmentUtil.readSegments(imageInput, JPEGSegmentUtil.ALL_SEGMENTS);
}
- catch (IIOException | IllegalArgumentException ignore) {
+ catch (IIOException | IllegalArgumentException e) {
if (DEBUG) {
- ignore.printStackTrace();
+ e.printStackTrace();
}
}
finally {
@@ -1392,7 +1376,7 @@ public final class JPEGImageReader extends ImageReaderBase {
final String arg = args[argIdx];
if (arg.charAt(0) == '-') {
- if (arg.equals("-s") || arg.equals("--subsample") && args.length > argIdx) {
+ if (arg.equals("-s") || arg.equals("--subsample") && args.length > argIdx + 1) {
String[] sub = args[++argIdx].split(",");
try {
@@ -1411,7 +1395,7 @@ public final class JPEGImageReader extends ImageReaderBase {
System.err.println("Bad sub sampling (x,y): '" + args[argIdx] + "'");
}
}
- else if (arg.equals("-r") || arg.equals("--roi") && args.length > argIdx) {
+ else if (arg.equals("-r") || arg.equals("--roi") && args.length > argIdx + 1) {
String[] region = args[++argIdx].split(",");
try {
diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGLosslessDecoderWrapper.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGLosslessDecoderWrapper.java
index 07d7d7dd..fdac72db 100644
--- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGLosslessDecoderWrapper.java
+++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGLosslessDecoderWrapper.java
@@ -31,8 +31,6 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
-import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
-
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
@@ -77,7 +75,7 @@ final class JPEGLosslessDecoderWrapper {
* @throws IOException is thrown if the decoder failed or a conversion is not supported
*/
BufferedImage readImage(final List segments, final ImageInputStream input) throws IOException {
- JPEGLosslessDecoder decoder = new JPEGLosslessDecoder(segments, createBufferedInput(input), listenerDelegate);
+ JPEGLosslessDecoder decoder = new JPEGLosslessDecoder(segments, input, listenerDelegate);
// TODO: Allow 10/12/14 bit (using a ComponentColorModel with correct bits, as in TIFF)
// TODO: Rewrite this to pass a pre-allocated buffer of correct type (byte/short)/correct bands
@@ -111,10 +109,6 @@ final class JPEGLosslessDecoderWrapper {
throw new IIOException("JPEG Lossless with " + decoder.getPrecision() + " bit precision and " + decoder.getNumComponents() + " component(s) not supported");
}
- private ImageInputStream createBufferedInput(final ImageInputStream input) throws IOException {
- return input instanceof BufferedImageInputStream ? input : new BufferedImageInputStream(input);
- }
-
Raster readRaster(final List segments, final ImageInputStream input) throws IOException {
// TODO: Can perhaps be implemented faster
return readImage(segments, input).getRaster();
diff --git a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java
index b5a95d7d..64b0d300 100644
--- a/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java
+++ b/imageio/imageio-jpeg/src/test/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReaderTest.java
@@ -1415,7 +1415,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest