mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-18 00:00:03 -04:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ada3a84bec | |||
| 7e7aaa293e | |||
| 5d623cce9f | |||
| 055838aaaf | |||
| a8327c3c67 | |||
| 36c91f67e4 | |||
| 4cc53d822f | |||
| 9875de0383 | |||
| 6ed858a4ca | |||
| 38192ae835 | |||
| b5e8853e6b | |||
| a98224e652 | |||
| 73a58266be | |||
| edd523534c |
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.twelvemonkeys.bom</groupId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>common-image</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>common-io</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>common-lang</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.common</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<groupId>com.twelvemonkeys.contrib</groupId>
|
||||
<artifactId>contrib</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-batik</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-bmp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-clippath</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||
|
||||
+39
-14
@@ -31,6 +31,7 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import javax.imageio.stream.ImageInputStreamImpl;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -52,6 +53,10 @@ import static java.lang.Math.max;
|
||||
* for shorter reads, like single byte or bit reads.
|
||||
*/
|
||||
final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
|
||||
private static final Closeable CLOSEABLE_STUB = new Closeable() {
|
||||
@Override public void close() {}
|
||||
};
|
||||
|
||||
static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
private ByteBuffer byteBuffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
|
||||
@@ -63,6 +68,7 @@ final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
|
||||
private final byte[] integralCacheArray = integralCache.array();
|
||||
|
||||
private SeekableByteChannel channel;
|
||||
private Closeable closeable;
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code File}.
|
||||
@@ -86,49 +92,62 @@ final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
|
||||
* @throws SecurityException if a security manager is installed, and it denies read access to the file.
|
||||
*/
|
||||
public BufferedChannelImageInputStream(final Path file) throws IOException {
|
||||
this(FileChannel.open(notNull(file, "file"), StandardOpenOption.READ));
|
||||
this(FileChannel.open(notNull(file, "file"), StandardOpenOption.READ), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code RandomAccessFile}.
|
||||
* <p>
|
||||
* Closing this stream will close the {@code RandomAccessFile}.
|
||||
* </p>
|
||||
*
|
||||
* @param file a {@code RandomAccessFile} to read from.
|
||||
* @throws IllegalArgumentException if {@code file} is {@code null}.
|
||||
*/
|
||||
public BufferedChannelImageInputStream(final RandomAccessFile file) {
|
||||
// Assumption: Closing the FileChannel will also close its backing RandomAccessFile
|
||||
// (it does in the OpenJDK implementation, and it makes sense, although I can't see this is documented behaviour).
|
||||
this(notNull(file, "file").getChannel());
|
||||
// Closing the RAF is inconsistent, but emulates the behavior of javax.imageio.stream.FileImageInputStream
|
||||
this(notNull(file, "file").getChannel(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code FileInputStream}.
|
||||
* <p>
|
||||
* Closing this stream will close the {@code FileInputStream}.
|
||||
* Closing this stream will <em>not</em> close the {@code FileInputStream}.
|
||||
* </p>
|
||||
*
|
||||
* @param inputStream a {@code FileInputStream} to read from.
|
||||
* @throws IllegalArgumentException if {@code inputStream} is {@code null}.
|
||||
*/
|
||||
public BufferedChannelImageInputStream(final FileInputStream inputStream) {
|
||||
// Assumption: Closing the FileChannel will also close its backing FileInputStream (it does in the OpenJDK implementation, although I can't see this is documented).
|
||||
this(notNull(inputStream, "inputStream").getChannel());
|
||||
this(notNull(inputStream, "inputStream").getChannel(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code SeekableByteChannel}.
|
||||
* <p>
|
||||
* Closing this stream will close the {@code SeekableByteChannel}.
|
||||
* Closing this stream will <em>not</em> close the {@code SeekableByteChannel}.
|
||||
* </p>
|
||||
*
|
||||
* @param channel a {@code SeekableByteChannel} to read from.
|
||||
* @throws IllegalArgumentException if {@code channel} is {@code null}.
|
||||
*/
|
||||
public BufferedChannelImageInputStream(final SeekableByteChannel channel) {
|
||||
this(notNull(channel, "channel"), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code BufferedChannelImageInputStream} that will read from a given {@code Cache}.
|
||||
* <p>
|
||||
* Closing this stream will close the {@code Cache}.
|
||||
* </p>
|
||||
*
|
||||
* @param cache a {@code SeekableByteChannel} to read from.
|
||||
* @throws IllegalArgumentException if {@code channel} is {@code null}.
|
||||
*/
|
||||
BufferedChannelImageInputStream(final Cache cache) {
|
||||
this(notNull(cache, "cache"), true);
|
||||
}
|
||||
|
||||
private BufferedChannelImageInputStream(final SeekableByteChannel channel, boolean closeChannelOnClose) {
|
||||
this.channel = notNull(channel, "channel");
|
||||
this.closeable = closeChannelOnClose ? this.channel : CLOSEABLE_STUB;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
@@ -246,8 +265,14 @@ final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
|
||||
buffer = null;
|
||||
byteBuffer = null;
|
||||
|
||||
channel.close();
|
||||
channel = null;
|
||||
|
||||
try {
|
||||
closeable.close();
|
||||
}
|
||||
finally {
|
||||
closeable = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Need to override the readShort(), readInt() and readLong() methods,
|
||||
@@ -315,9 +340,9 @@ final class BufferedChannelImageInputStream extends ImageInputStreamImpl {
|
||||
public void flushBefore(final long pos) throws IOException {
|
||||
super.flushBefore(pos);
|
||||
|
||||
if (channel instanceof MemoryCache) {
|
||||
if (channel instanceof Cache) {
|
||||
// In case of memory cache, free up memory
|
||||
((MemoryCache) channel).flushBefore(pos);
|
||||
((Cache) channel).flushBefore(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ public final class BufferedInputStreamImageInputStreamSpi extends ImageInputStre
|
||||
}
|
||||
|
||||
// Otherwise, create a cache for backwards seeking
|
||||
return new BufferedChannelImageInputStream(useCacheFile ? new DiskCache(channel, cacheDir): new MemoryCache(channel));
|
||||
return new BufferedChannelImageInputStream(useCacheFile ? new FileCache(channel, cacheDir) : new MemoryCache(channel));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected input of type InputStream: " + input);
|
||||
|
||||
+11
-11
@@ -48,18 +48,18 @@ public final class ByteArrayImageInputStream extends ImageInputStreamImpl {
|
||||
private final int dataOffset;
|
||||
private final int dataLength;
|
||||
|
||||
public ByteArrayImageInputStream(final byte[] pData) {
|
||||
this(pData, 0, pData != null ? pData.length : -1);
|
||||
public ByteArrayImageInputStream(final byte[] data) {
|
||||
this(data, 0, data != null ? data.length : -1);
|
||||
}
|
||||
|
||||
public ByteArrayImageInputStream(final byte[] pData, int offset, int length) {
|
||||
data = notNull(pData, "data");
|
||||
dataOffset = isBetween(0, pData.length, offset, "offset");
|
||||
dataLength = isBetween(0, pData.length - offset, length, "length");
|
||||
public ByteArrayImageInputStream(final byte[] data, int offset, int length) {
|
||||
this.data = notNull(data, "data");
|
||||
dataOffset = isMax(data.length, offset, "offset");
|
||||
dataLength = isMax(data.length - offset, length, "length");
|
||||
}
|
||||
|
||||
private static int isBetween(final int low, final int high, final int value, final String name) {
|
||||
return isTrue(value >= low && value <= high, value, String.format("%s out of range [%d, %d]: %d", name, low, high, value));
|
||||
private static int isMax(final int high, final int value, final String name) {
|
||||
return isTrue(value >= 0 && value <= high, value, String.format("%s out of range [0, %d]: %d", name, high, value));
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
@@ -72,14 +72,14 @@ public final class ByteArrayImageInputStream extends ImageInputStreamImpl {
|
||||
return data[((int) streamPos++) + dataOffset] & 0xff;
|
||||
}
|
||||
|
||||
public int read(byte[] pBuffer, int pOffset, int pLength) throws IOException {
|
||||
public int read(byte[] buffer, int offset, int len) throws IOException {
|
||||
if (streamPos >= dataLength) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int length = (int) Math.min(this.dataLength - streamPos, pLength);
|
||||
int length = (int) Math.min(dataLength - streamPos, len);
|
||||
bitOffset = 0;
|
||||
System.arraycopy(data, (int) streamPos + dataOffset, pBuffer, pOffset, length);
|
||||
System.arraycopy(data, (int) streamPos + dataOffset, buffer, offset, length);
|
||||
streamPos += length;
|
||||
|
||||
return length;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.twelvemonkeys.imageio.stream;
|
||||
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
|
||||
interface Cache extends SeekableByteChannel {
|
||||
void flushBefore(long pos);
|
||||
}
|
||||
+7
-9
@@ -26,19 +26,19 @@ import static java.nio.file.StandardOpenOption.WRITE;
|
||||
// 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.
|
||||
final class DiskCache implements SeekableByteChannel {
|
||||
final class FileCache implements Cache {
|
||||
final static int BLOCK_SIZE = 1 << 13;
|
||||
|
||||
private final FileChannel cache;
|
||||
private final ReadableByteChannel channel;
|
||||
|
||||
// TODO: Perhaps skip this constructor?
|
||||
DiskCache(InputStream stream, File cacheDir) throws IOException {
|
||||
FileCache(InputStream stream, File cacheDir) throws IOException {
|
||||
// Stream will be closed with channel, documented behavior
|
||||
this(Channels.newChannel(notNull(stream, "stream")), cacheDir);
|
||||
}
|
||||
|
||||
public DiskCache(ReadableByteChannel channel, File cacheDir) throws IOException {
|
||||
public FileCache(ReadableByteChannel channel, File cacheDir) throws IOException {
|
||||
this.channel = notNull(channel, "channel");
|
||||
isTrue(cacheDir == null || cacheDir.isDirectory(), cacheDir, "%s is not a directory");
|
||||
|
||||
@@ -65,12 +65,7 @@ final class DiskCache implements SeekableByteChannel {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
cache.close();
|
||||
}
|
||||
finally {
|
||||
channel.close();
|
||||
}
|
||||
cache.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,5 +105,8 @@ final class DiskCache implements SeekableByteChannel {
|
||||
public SeekableByteChannel truncate(long size) {
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
@Override public void flushBefore(long pos) {
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -13,7 +13,7 @@ import java.util.List;
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
public final class MemoryCache implements SeekableByteChannel {
|
||||
final class MemoryCache implements Cache {
|
||||
|
||||
final static int BLOCK_SIZE = 1 << 13;
|
||||
|
||||
@@ -78,12 +78,7 @@ public final class MemoryCache implements SeekableByteChannel {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
cache.clear();
|
||||
}
|
||||
finally {
|
||||
channel.close();
|
||||
}
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,7 +130,8 @@ public final class MemoryCache implements SeekableByteChannel {
|
||||
throw new NonWritableChannelException();
|
||||
}
|
||||
|
||||
void flushBefore(long pos) {
|
||||
@Override
|
||||
public void flushBefore(long pos) {
|
||||
if (pos < start) {
|
||||
throw new IndexOutOfBoundsException("pos < flushed position");
|
||||
}
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ public final class URLImageInputStreamSpi extends ImageInputStreamSpi {
|
||||
|
||||
// Otherwise revert to cached
|
||||
InputStream urlStream = url.openStream();
|
||||
return new BufferedChannelImageInputStream(useCacheFile ? new DiskCache(urlStream, cacheDir) : new MemoryCache(urlStream));
|
||||
return new BufferedChannelImageInputStream(useCacheFile ? new FileCache(urlStream, cacheDir) : new MemoryCache(urlStream));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Expected input of type URL: " + input);
|
||||
|
||||
+20
-20
@@ -57,7 +57,7 @@ import static org.mockito.Mockito.verify;
|
||||
* @version $Id: BufferedFileImageInputStreamTestCase.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||
*/
|
||||
// TODO: Remove this test, and instead test the disk cache directly!
|
||||
public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
public class BufferedChannelImageInputStreamFileCacheTest {
|
||||
private final Random random = new Random(170984354357234566L);
|
||||
|
||||
private InputStream randomDataToInputStream(byte[] data) {
|
||||
@@ -68,7 +68,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
|
||||
@Test
|
||||
public void testCreate() throws IOException {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(new ByteArrayInputStream(new byte[0]), null))) {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(new ByteArrayInputStream(new byte[0]), null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
@Test
|
||||
public void testCreateNullStream() throws IOException {
|
||||
try {
|
||||
new DiskCache((InputStream) null, null);
|
||||
new FileCache((InputStream) null, null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
@@ -90,7 +90,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
@Test
|
||||
public void testCreateNullChannel() throws IOException {
|
||||
try {
|
||||
new DiskCache((ReadableByteChannel) null, null);
|
||||
new FileCache((ReadableByteChannel) null, null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
}
|
||||
catch (IllegalArgumentException expected) {
|
||||
@@ -106,7 +106,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
for (byte value : data) {
|
||||
@@ -122,7 +122,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
byte[] data = new byte[1024 * 1024];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] result = new byte[1024];
|
||||
@@ -141,7 +141,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
byte[] data = new byte[1024 * 14];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] result = new byte[7];
|
||||
@@ -159,7 +159,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
byte[] data = new byte[1024 * 18];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] result = new byte[9];
|
||||
@@ -180,7 +180,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
byte[] data = new byte[256];
|
||||
InputStream input = randomDataToInputStream(data);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
assertEquals("Stream length should be unknown", -1, stream.length());
|
||||
|
||||
byte[] buffer = new byte[data.length * 2];
|
||||
@@ -198,7 +198,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
|
||||
}
|
||||
@@ -212,7 +212,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
for (int i = 1; i <= 64; i++) {
|
||||
stream.seek(0);
|
||||
assertEquals(String.format("bit %d differ", i), value >>> (64L - i), stream.readBits(i));
|
||||
@@ -228,7 +228,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
long value = ByteBuffer.wrap(bytes).getLong();
|
||||
|
||||
// Create stream
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
for (int i = 1; i <= 60; i++) {
|
||||
stream.seek(0);
|
||||
stream.setBitOffset(i % 8);
|
||||
@@ -244,7 +244,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 2; i++) {
|
||||
@@ -282,7 +282,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 4; i++) {
|
||||
@@ -320,7 +320,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||
|
||||
for (int i = 0; i < bytes.length / 8; i++) {
|
||||
@@ -357,7 +357,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
byte[] bytes = new byte[9];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (final ImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
stream.seek(1000);
|
||||
|
||||
assertEquals(-1, stream.read());
|
||||
@@ -406,11 +406,11 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
@Test
|
||||
public void testClose() throws IOException {
|
||||
// Create wrapper stream
|
||||
InputStream mock = mock(InputStream.class);
|
||||
ImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(mock, null));
|
||||
Cache cache = mock(Cache.class);
|
||||
ImageInputStream stream = new BufferedChannelImageInputStream(cache);
|
||||
|
||||
stream.close();
|
||||
verify(mock, only()).close();
|
||||
verify(cache, only()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -422,7 +422,7 @@ public class BufferedChannelImageInputStreamDiskCacheTest {
|
||||
byte[] bytes = new byte[size];
|
||||
InputStream input = randomDataToInputStream(bytes);
|
||||
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new DiskCache(input, null))) {
|
||||
try (BufferedChannelImageInputStream stream = new BufferedChannelImageInputStream(new FileCache(input, null))) {
|
||||
byte[] result = new byte[size];
|
||||
int head = stream.read(result, 0, 12); // Provoke a buffered read
|
||||
int len = stream.read(result, 12, size - 12); // Rest of buffer + direct read
|
||||
+3
-3
@@ -406,11 +406,11 @@ public class BufferedChannelImageInputStreamMemoryCacheTest {
|
||||
@Test
|
||||
public void testClose() throws IOException {
|
||||
// Create wrapper stream
|
||||
InputStream mock = mock(InputStream.class);
|
||||
ImageInputStream stream = new BufferedChannelImageInputStream(new MemoryCache(mock));
|
||||
Cache cache = mock(Cache.class);
|
||||
ImageInputStream stream = new BufferedChannelImageInputStream(cache);
|
||||
|
||||
stream.close();
|
||||
verify(mock, only()).close();
|
||||
verify(cache, only()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+4
-7
@@ -47,7 +47,7 @@ import java.util.Random;
|
||||
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
@@ -403,14 +403,11 @@ public class BufferedChannelImageInputStreamTest {
|
||||
|
||||
@Test
|
||||
public void testCloseChannel() throws IOException {
|
||||
// NOTE: As the stream-based constructor is chained to the channel-based one,
|
||||
// we'll rely on the fact that closing the channel will close the stream.
|
||||
|
||||
SeekableByteChannel mock = mock(SeekableByteChannel.class);
|
||||
ImageInputStream stream = new BufferedChannelImageInputStream(mock);
|
||||
SeekableByteChannel channel = mock(SeekableByteChannel.class);
|
||||
ImageInputStream stream = new BufferedChannelImageInputStream(channel);
|
||||
|
||||
stream.close();
|
||||
verify(mock, only()).close();
|
||||
verify(channel, never()).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-hdr</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-icns</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-iff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg-jai-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg-jep262-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-jpeg</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pcx</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
|
||||
|
||||
+1
-2
@@ -13,7 +13,6 @@ final class PCXMetadata extends StandardImageMetadataSupport {
|
||||
}
|
||||
|
||||
private static PlanarConfiguration planarConfiguration(PCXHeader header) {
|
||||
System.out.println("header = " + header);
|
||||
return header.getChannels() > 1 ? PlanarConfiguration.LineInterleaved : null;
|
||||
}
|
||||
|
||||
@@ -25,6 +24,6 @@ final class PCXMetadata extends StandardImageMetadataSupport {
|
||||
return "RLE";
|
||||
}
|
||||
|
||||
return "Uknown";
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pdf</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pict</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pnm</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PNM plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-psd</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-reference</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: JDK Reference Tests</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-sgi</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: SGI plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tga</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TGA plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-thumbsdb</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tiff-jai-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF/JAI Metadata Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tiff-jdk-interop</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF/JDK JPEG Interop</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-tiff</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-webp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: WebP plugin</name>
|
||||
|
||||
+24
-26
@@ -35,6 +35,8 @@ import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
/**
|
||||
* LSBBitReader
|
||||
*
|
||||
@@ -49,14 +51,13 @@ public final class LSBBitReader {
|
||||
private long streamPosition = -1;
|
||||
|
||||
/**
|
||||
* Pre buffers up to the next 8 Bytes in input.
|
||||
* Pre-buffers up to the next 8 Bytes in input.
|
||||
* Contains valid bits in bits 63 to {@code bitOffset} (inclusive).
|
||||
* Should always be refilled to have at least 56 valid bits (if possible)
|
||||
*/
|
||||
private long buffer;
|
||||
|
||||
public LSBBitReader(ImageInputStream imageInput) {
|
||||
this.imageInput = imageInput;
|
||||
this.imageInput = notNull(imageInput);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,20 +90,16 @@ public final class LSBBitReader {
|
||||
if (bits > 56) {
|
||||
throw new IllegalArgumentException("Tried peeking over 56");
|
||||
}
|
||||
|
||||
return readBits(bits, true);
|
||||
}
|
||||
|
||||
//Driver
|
||||
private long readBits(int bits, boolean peek) throws IOException {
|
||||
if (bits <= 56) {
|
||||
|
||||
/*
|
||||
Could eliminate if we never read from the underlying InputStream outside this class after the object is
|
||||
created
|
||||
*/
|
||||
long inputStreamPosition = imageInput.getStreamPosition();
|
||||
if (streamPosition != inputStreamPosition) {
|
||||
//Need to reset buffer as stream was read in the meantime
|
||||
// Could eliminate if we never read from the underlying InputStream
|
||||
// outside this class after the object is created
|
||||
if (streamPosition != imageInput.getStreamPosition()) {
|
||||
// Need to reset buffer as stream was read in the meantime
|
||||
resetBuffer();
|
||||
}
|
||||
|
||||
@@ -110,45 +107,46 @@ public final class LSBBitReader {
|
||||
|
||||
if (!peek) {
|
||||
bitOffset += bits;
|
||||
refillBuffer();
|
||||
|
||||
if (bitOffset >= 8) {
|
||||
refillBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
else {
|
||||
//FIXME Untested
|
||||
// Peek always false in this case
|
||||
long lower = readBits(56);
|
||||
return (readBits(bits - 56) << (56)) | lower;
|
||||
}
|
||||
}
|
||||
|
||||
private void refillBuffer() throws IOException {
|
||||
// Set to stream position consistent with buffered bytes
|
||||
imageInput.readLong(); // Don't replace with skipBytes(8) or seek(+8), this will invalidate stream buffer... TODO: Fix streams to cope...
|
||||
|
||||
//Set to stream position consistent with buffered bytes
|
||||
imageInput.seek(streamPosition + 8);
|
||||
for (; bitOffset >= 8; bitOffset -= 8) {
|
||||
try {
|
||||
byte b = imageInput.readByte();
|
||||
buffer >>>= 8;
|
||||
buffer = ((long) b << 56) | buffer >>> 8;
|
||||
streamPosition++;
|
||||
buffer |= ((long) b << 56);
|
||||
}
|
||||
catch (EOFException e) {
|
||||
imageInput.seek(streamPosition);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
Reset to guarantee stream position consistent with returned bytes
|
||||
Would not need to do this seeking around when the underlying ImageInputStream is never read from outside
|
||||
this class after the object is created.
|
||||
*/
|
||||
|
||||
// Reset to guarantee stream position consistent with returned bytes
|
||||
// Would not need to do this seeking around when the underlying ImageInputStream is never read from outside
|
||||
// this class after the object is created.
|
||||
imageInput.seek(streamPosition);
|
||||
}
|
||||
|
||||
private void resetBuffer() throws IOException {
|
||||
|
||||
long inputStreamPosition = imageInput.getStreamPosition();
|
||||
|
||||
try {
|
||||
buffer = imageInput.readLong();
|
||||
bitOffset = 0;
|
||||
@@ -156,7 +154,7 @@ public final class LSBBitReader {
|
||||
imageInput.seek(inputStreamPosition);
|
||||
}
|
||||
catch (EOFException e) {
|
||||
//Retry byte by byte
|
||||
// Retry byte by byte
|
||||
streamPosition = inputStreamPosition - 8;
|
||||
bitOffset = 64;
|
||||
refillBuffer();
|
||||
@@ -164,7 +162,7 @@ public final class LSBBitReader {
|
||||
|
||||
}
|
||||
|
||||
//Left for backwards compatibility / Compatibility with ImageInputStream interface
|
||||
// Left for backwards compatibility / Compatibility with ImageInputStream interface
|
||||
public int readBit() throws IOException {
|
||||
return (int) readBits(1);
|
||||
}
|
||||
|
||||
+15
-13
@@ -96,7 +96,9 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) {
|
||||
super.setInput(input, seekForwardOnly, ignoreMetadata);
|
||||
|
||||
lsbBitReader = new LSBBitReader(imageInput);
|
||||
if (imageInput != null) {
|
||||
lsbBitReader = new LSBBitReader(imageInput);
|
||||
}
|
||||
}
|
||||
|
||||
private void readHeader(int imageIndex) throws IOException {
|
||||
@@ -272,19 +274,19 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
// RsV|I|L|E|X|A|R
|
||||
int reserved = (int) imageInput.readBits(2);
|
||||
int reserved = lsbBitReader.readBit();
|
||||
if (reserved != 0) {
|
||||
// Spec says SHOULD be 0
|
||||
throw new IIOException(String.format("Unexpected 'VP8X' chunk reserved value, expected 0: %d", reserved));
|
||||
}
|
||||
|
||||
header.containsICCP = imageInput.readBit() == 1;
|
||||
header.containsALPH = imageInput.readBit() == 1; // L -> aLpha
|
||||
header.containsEXIF = imageInput.readBit() == 1;
|
||||
header.containsXMP_ = imageInput.readBit() == 1;
|
||||
header.containsANIM = imageInput.readBit() == 1; // A -> Anim
|
||||
header.containsANIM = lsbBitReader.readBit() == 1; // A -> Anim
|
||||
header.containsXMP_ = lsbBitReader.readBit() == 1;
|
||||
header.containsEXIF = lsbBitReader.readBit() == 1;
|
||||
header.containsALPH = lsbBitReader.readBit() == 1; // L -> aLpha
|
||||
header.containsICCP = lsbBitReader.readBit() == 1;
|
||||
|
||||
reserved = (int) imageInput.readBits(25); // 1 + 24 bits reserved
|
||||
reserved = (int) lsbBitReader.readBits(26); // 2 + 24 bits reserved
|
||||
if (reserved != 0) {
|
||||
// Spec says SHOULD be 0
|
||||
throw new IIOException(String.format("Unexpected 'VP8X' chunk reserved value, expected 0: %d", reserved));
|
||||
@@ -509,16 +511,16 @@ final class WebPImageReader extends ImageReaderBase {
|
||||
}
|
||||
|
||||
private void readAlpha(BufferedImage destination, ImageReadParam param, final int width, final int height) throws IOException {
|
||||
int reserved = (int) imageInput.readBits(2);
|
||||
int compression = (int) lsbBitReader.readBits(2);
|
||||
int filtering = (int) lsbBitReader.readBits(2);
|
||||
int preProcessing = (int) lsbBitReader.readBits(2);
|
||||
int reserved = (int) lsbBitReader.readBits(2);
|
||||
|
||||
if (reserved != 0) {
|
||||
// Spec says SHOULD be 0
|
||||
processWarningOccurred(String.format("Unexpected 'ALPH' chunk reserved value, expected 0: %d", reserved));
|
||||
}
|
||||
|
||||
int preProcessing = (int) imageInput.readBits(2);
|
||||
int filtering = (int) imageInput.readBits(2);
|
||||
int compression = (int) imageInput.readBits(2);
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("preProcessing: " + preProcessing);
|
||||
System.out.println("filtering: " + filtering);
|
||||
|
||||
+2
-5
@@ -48,16 +48,14 @@ final class ColorIndexingTransform implements Transform {
|
||||
|
||||
@Override
|
||||
public void applyInverse(WritableRaster raster) {
|
||||
|
||||
int width = raster.getWidth();
|
||||
int height = raster.getHeight();
|
||||
|
||||
byte[] rgba = new byte[4];
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
//Reversed so no used elements are overridden (in case of packing)
|
||||
// Reversed so no used elements are overridden (in case of packing)
|
||||
for (int x = width - 1; x >= 0; x--) {
|
||||
|
||||
int componentSize = 8 >> bits;
|
||||
int packed = 1 << bits;
|
||||
int xC = x / packed;
|
||||
@@ -67,10 +65,9 @@ final class ColorIndexingTransform implements Transform {
|
||||
|
||||
int index = sample >> componentOffset & ((1 << componentSize) - 1);
|
||||
|
||||
//Arraycopy for 4 elements might not be beneficial
|
||||
// Arraycopy for 4 elements might not be beneficial
|
||||
System.arraycopy(colorTable, index * 4, rgba, 0, 4);
|
||||
raster.setDataElements(x, y, rgba);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -37,11 +37,11 @@ import java.awt.image.*;
|
||||
* @author Simon Kammermeier
|
||||
*/
|
||||
final class HuffmanInfo {
|
||||
public Raster huffmanMetaCodes; //Raster allows intuitive lookup by x and y
|
||||
public final Raster huffmanMetaCodes; // Raster allows intuitive lookup by x and y
|
||||
|
||||
public int metaCodeBits;
|
||||
public final int metaCodeBits;
|
||||
|
||||
public HuffmanCodeGroup[] huffmanGroups;
|
||||
public final HuffmanCodeGroup[] huffmanGroups;
|
||||
|
||||
public HuffmanInfo(Raster huffmanMetaCodes, int metaCodeBits, HuffmanCodeGroup[] huffmanGroups) {
|
||||
this.huffmanMetaCodes = huffmanMetaCodes;
|
||||
|
||||
+39
-55
@@ -83,7 +83,6 @@ final class HuffmanTable {
|
||||
* @throws IOException when reading produces an exception
|
||||
*/
|
||||
public HuffmanTable(LSBBitReader lsbBitReader, int alphabetSize) throws IOException {
|
||||
|
||||
boolean simpleLengthCode = lsbBitReader.readBit() == 1;
|
||||
|
||||
if (simpleLengthCode) {
|
||||
@@ -104,11 +103,9 @@ final class HuffmanTable {
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
code lengths also huffman coded
|
||||
first read the "first stage" code lengths
|
||||
In the following this is called the L-Code (for length code)
|
||||
*/
|
||||
// code lengths also huffman coded
|
||||
// first read the "first stage" code lengths
|
||||
// In the following this is called the L-Code (for length code)
|
||||
int numLCodeLengths = (int) (lsbBitReader.readBits(4) + 4);
|
||||
short[] lCodeLengths = new short[L_CODE_ORDER.length];
|
||||
int numPosCodeLens = 0;
|
||||
@@ -116,16 +113,15 @@ final class HuffmanTable {
|
||||
for (int i = 0; i < numLCodeLengths; i++) {
|
||||
short len = (short) lsbBitReader.readBits(3);
|
||||
lCodeLengths[L_CODE_ORDER[i]] = len;
|
||||
|
||||
if (len > 0) {
|
||||
numPosCodeLens++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Use L-Code to read the actual code lengths
|
||||
// Use L-Code to read the actual code lengths
|
||||
short[] codeLengths = readCodeLengths(lsbBitReader, lCodeLengths, alphabetSize, numPosCodeLens);
|
||||
|
||||
|
||||
buildFromLengths(codeLengths);
|
||||
}
|
||||
}
|
||||
@@ -142,25 +138,21 @@ final class HuffmanTable {
|
||||
buildFromLengths(codeLengths, numPosCodeLens);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Helper methods to allow reusing in different constructors
|
||||
*/
|
||||
|
||||
// Helper methods to allow reusing in different constructors
|
||||
private void buildFromLengths(short[] codeLengths) {
|
||||
int numPosCodeLens = 0;
|
||||
|
||||
for (short codeLength : codeLengths) {
|
||||
if (codeLength != 0) {
|
||||
numPosCodeLens++;
|
||||
}
|
||||
}
|
||||
|
||||
buildFromLengths(codeLengths, numPosCodeLens);
|
||||
}
|
||||
|
||||
private void buildFromLengths(short[] codeLengths, int numPosCodeLens) {
|
||||
|
||||
//Pack code length and corresponding symbols as described above
|
||||
|
||||
// Pack code length and corresponding symbols as described above
|
||||
int[] lengthsAndSymbols = new int[numPosCodeLens];
|
||||
|
||||
int index = 0;
|
||||
@@ -170,28 +162,25 @@ final class HuffmanTable {
|
||||
}
|
||||
}
|
||||
|
||||
//Special case: Only 1 code value
|
||||
// Special case: Only 1 code value
|
||||
if (numPosCodeLens == 1) {
|
||||
//Length is 0 so mask to clear length bits
|
||||
// Length is 0 so mask to clear length bits
|
||||
Arrays.fill(level1, lengthsAndSymbols[0] & 0xffff);
|
||||
}
|
||||
|
||||
//Due to the layout of the elements this effectively first sorts by length and then symbol.
|
||||
// Due to the layout of the elements this effectively first sorts by length and then symbol.
|
||||
Arrays.sort(lengthsAndSymbols);
|
||||
|
||||
/*
|
||||
The next code, in the bit order it would appear on the input stream, i.e. it is reversed.
|
||||
Only the lowest bits (corresponding to the bit length of the code) are considered.
|
||||
Example: code 0..010 (length 2) would appear as 0..001.
|
||||
*/
|
||||
// The next code, in the bit order it would appear on the input stream, i.e. it is reversed.
|
||||
// Only the lowest bits (corresponding to the bit length of the code) are considered.
|
||||
// Example: code 0..010 (length 2) would appear as 0..001.
|
||||
int code = 0;
|
||||
|
||||
//Used for level2 lookup
|
||||
// Used for level2 lookup
|
||||
int rootEntry = -1;
|
||||
int[] currentTable = null;
|
||||
|
||||
for (int i = 0; i < lengthsAndSymbols.length; i++) {
|
||||
|
||||
int lengthAndSymbol = lengthsAndSymbols[i];
|
||||
|
||||
int length = lengthAndSymbol >>> 16;
|
||||
@@ -202,16 +191,15 @@ final class HuffmanTable {
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Existing level2 table not fitting
|
||||
// Existing level2 table not fitting
|
||||
if ((code & ((1 << LEVEL1_BITS) - 1)) != rootEntry) {
|
||||
/*
|
||||
Figure out needed table size.
|
||||
Start at current symbol and length.
|
||||
Every symbol uses 1 slot at the current bit length.
|
||||
Going up 1 bit in length multiplies the slots by 2.
|
||||
No more open slots indicate the table size to be big enough.
|
||||
*/
|
||||
// Figure out needed table size.
|
||||
// Start at current symbol and length.
|
||||
// Every symbol uses 1 slot at the current bit length.
|
||||
// Going up 1 bit in length multiplies the slots by 2.
|
||||
// No more open slots indicate the table size to be big enough.
|
||||
int maxLength = length;
|
||||
|
||||
for (int j = i, openSlots = 1 << (length - LEVEL1_BITS);
|
||||
j < lengthsAndSymbols.length && openSlots > 0;
|
||||
j++, openSlots--) {
|
||||
@@ -230,11 +218,11 @@ final class HuffmanTable {
|
||||
rootEntry = code & ((1 << LEVEL1_BITS) - 1);
|
||||
level2.add(currentTable);
|
||||
|
||||
//Set root table indirection
|
||||
// Set root table indirection
|
||||
level1[rootEntry] = (LEVEL1_BITS + level2Size) << 16 | (level2.size() - 1);
|
||||
}
|
||||
|
||||
//Add to existing (or newly generated) 2nd level table
|
||||
// Add to existing (or newly generated) 2nd level table
|
||||
for (int j = (code >>> LEVEL1_BITS); j < currentTable.length; j += 1 << (length - LEVEL1_BITS)) {
|
||||
currentTable[j] = (length - LEVEL1_BITS) << 16 | (lengthAndSymbol & 0xffff);
|
||||
}
|
||||
@@ -256,12 +244,12 @@ final class HuffmanTable {
|
||||
private int nextCode(int code, int length) {
|
||||
int a = (~code) & ((1 << length) - 1);
|
||||
|
||||
//This will result in the highest 0-bit in the lower length bits of code set (by construction of a)
|
||||
//I.e. the lowest 0-bit in the value code represents
|
||||
// This will result in the highest 0-bit in the lower length bits of code set (by construction of a)
|
||||
// I.e. the lowest 0-bit in the value code represents
|
||||
int step = Integer.highestOneBit(a);
|
||||
|
||||
//In the represented value this clears the consecutive 1-bits starting at bit 0 and then sets the lowest 0 bit
|
||||
//This corresponds to adding 1 to the value
|
||||
// In the represented value this clears the consecutive 1-bits starting at bit 0 and then sets the lowest 0 bit
|
||||
// This corresponds to adding 1 to the value
|
||||
return (code & (step - 1)) | step;
|
||||
}
|
||||
|
||||
@@ -270,7 +258,7 @@ final class HuffmanTable {
|
||||
|
||||
HuffmanTable huffmanTable = new HuffmanTable(aCodeLengths, numPosCodeLens);
|
||||
|
||||
//Not sure where this comes from. Just adapted from the libwebp implementation
|
||||
// Not sure where this comes from. Just adapted from the libwebp implementation
|
||||
int codedSymbols;
|
||||
if (lsbBitReader.readBit() == 1) {
|
||||
int maxSymbolBitLength = (int) (2 + 2 * lsbBitReader.readBits(3));
|
||||
@@ -282,13 +270,13 @@ final class HuffmanTable {
|
||||
|
||||
short[] codeLengths = new short[alphabetSize];
|
||||
|
||||
//Default code for repeating
|
||||
// Default code for repeating
|
||||
short prevLength = 8;
|
||||
|
||||
for (int i = 0; i < alphabetSize && codedSymbols > 0; i++, codedSymbols--) {
|
||||
short len = huffmanTable.readSymbol(lsbBitReader);
|
||||
|
||||
if (len < 16) { //Literal length
|
||||
if (len < 16) { // Literal length
|
||||
codeLengths[i] = len;
|
||||
if (len != 0) {
|
||||
prevLength = len;
|
||||
@@ -300,16 +288,16 @@ final class HuffmanTable {
|
||||
int repeatOffset;
|
||||
|
||||
switch (len) {
|
||||
case 16: //Repeat previous
|
||||
case 16: // Repeat previous
|
||||
repeatSymbol = prevLength;
|
||||
extraBits = 2;
|
||||
repeatOffset = 3;
|
||||
break;
|
||||
case 17: //Repeat 0 short
|
||||
case 17: // Repeat 0 short
|
||||
extraBits = 3;
|
||||
repeatOffset = 3;
|
||||
break;
|
||||
case 18: //Repeat 0 long
|
||||
case 18: // Repeat 0 long
|
||||
extraBits = 7;
|
||||
repeatOffset = 11;
|
||||
break;
|
||||
@@ -319,7 +307,6 @@ final class HuffmanTable {
|
||||
|
||||
int repeatCount = (int) (lsbBitReader.readBits(extraBits) + repeatOffset);
|
||||
|
||||
|
||||
if (i + repeatCount > alphabetSize) {
|
||||
throw new IIOException(
|
||||
String.format(
|
||||
@@ -330,11 +317,9 @@ final class HuffmanTable {
|
||||
|
||||
Arrays.fill(codeLengths, i, i + repeatCount, repeatSymbol);
|
||||
i += repeatCount - 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return codeLengths;
|
||||
}
|
||||
|
||||
@@ -346,21 +331,20 @@ final class HuffmanTable {
|
||||
* @throws IOException when the reader throws one reading a symbol
|
||||
*/
|
||||
public short readSymbol(LSBBitReader lsbBitReader) throws IOException {
|
||||
|
||||
int index = (int) lsbBitReader.peekBits(LEVEL1_BITS);
|
||||
int lengthAndSymbol = level1[index];
|
||||
|
||||
int length = lengthAndSymbol >>> 16;
|
||||
|
||||
if (length > LEVEL1_BITS) {
|
||||
//Lvl2 lookup
|
||||
lsbBitReader.readBits(LEVEL1_BITS); //Consume bits of first level
|
||||
int level2Index = (int) lsbBitReader.peekBits(length - LEVEL1_BITS); //Peek remaining required bits
|
||||
// Lvl2 lookup
|
||||
lsbBitReader.readBits(LEVEL1_BITS); // Consume bits of first level
|
||||
int level2Index = (int) lsbBitReader.peekBits(length - LEVEL1_BITS); // Peek remaining required bits
|
||||
lengthAndSymbol = level2.get(lengthAndSymbol & 0xffff)[level2Index];
|
||||
length = lengthAndSymbol >>> 16;
|
||||
}
|
||||
|
||||
lsbBitReader.readBits(length); //Consume bits
|
||||
lsbBitReader.readBits(length); // Consume bits
|
||||
|
||||
return (short) (lengthAndSymbol & 0xffff);
|
||||
}
|
||||
|
||||
+7
-11
@@ -51,25 +51,23 @@ final class PredictorTransform implements Transform {
|
||||
|
||||
@Override
|
||||
public void applyInverse(WritableRaster raster) {
|
||||
|
||||
int width = raster.getWidth();
|
||||
int height = raster.getHeight();
|
||||
|
||||
byte[] rgba = new byte[4];
|
||||
|
||||
//Handle top and left border separately
|
||||
// Handle top and left border separately
|
||||
|
||||
//(0,0) Black (0x000000ff) predict
|
||||
// (0,0) Black (0x000000ff) predict
|
||||
raster.getDataElements(0, 0, rgba);
|
||||
rgba[3] += 0xff;
|
||||
raster.setDataElements(0, 0, rgba);
|
||||
|
||||
|
||||
byte[] predictor = new byte[4];
|
||||
byte[] predictor2 = new byte[4];
|
||||
byte[] predictor3 = new byte[4];
|
||||
|
||||
//(x,0) L predict
|
||||
// (x,0) L predict
|
||||
for (int x = 1; x < width; x++) {
|
||||
raster.getDataElements(x, 0, rgba);
|
||||
raster.getDataElements(x - 1, 0, predictor);
|
||||
@@ -78,7 +76,7 @@ final class PredictorTransform implements Transform {
|
||||
raster.setDataElements(x, 0, rgba);
|
||||
}
|
||||
|
||||
//(0,y) T predict
|
||||
// (0,y) T predict
|
||||
for (int y = 1; y < height; y++) {
|
||||
raster.getDataElements(0, y, rgba);
|
||||
raster.getDataElements(0, y - 1, predictor);
|
||||
@@ -89,16 +87,14 @@ final class PredictorTransform implements Transform {
|
||||
|
||||
for (int y = 1; y < height; y++) {
|
||||
for (int x = 1; x < width; x++) {
|
||||
|
||||
int transformType = data.getSample(x >> bits, y >> bits, 1);
|
||||
|
||||
raster.getDataElements(x, y, rgba);
|
||||
|
||||
int lX = x - 1; //x for left
|
||||
int lX = x - 1; // x for left
|
||||
int tY = y - 1; // y for top
|
||||
|
||||
int tY = y - 1; //y for top
|
||||
|
||||
//top right is not (x+1, tY) if last pixel in line instead (0, y)
|
||||
// top right is not (x+1, tY) if last pixel in line instead (0, y)
|
||||
int trX = x == width - 1 ? 0 : x + 1;
|
||||
int trY = x == width - 1 ? y : tY;
|
||||
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ package com.twelvemonkeys.imageio.plugins.webp.lossless;
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
// Hmm.. Why doesn't SUBTRACT_GREEN follow the convention?
|
||||
// Hmm... Why doesn't SUBTRACT_GREEN follow the convention?
|
||||
interface TransformType {
|
||||
int PREDICTOR_TRANSFORM = 0;
|
||||
int COLOR_TRANSFORM = 1;
|
||||
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* LSBBitReaderTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: LSBBitReaderTest.java,v 1.0 16/10/2022 haraldk Exp$
|
||||
*/
|
||||
public class LSBBitReaderTest {
|
||||
@Test
|
||||
public void testReadBit() throws IOException {
|
||||
final LSBBitReader bitReader = createBitReader(new byte[] {
|
||||
0b00010010, 0b00100001, 0b00001000, 0b00000100,
|
||||
/*TODO: Remove these, should not be needed... */ 0, 0, 0, 0
|
||||
});
|
||||
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(1, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
|
||||
assertEquals(1, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
|
||||
assertEquals(1, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(1, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(1, bitReader.readBit());
|
||||
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(1, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
assertEquals(0, bitReader.readBit());
|
||||
|
||||
// assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
// @Override
|
||||
// public void run() throws Throwable {
|
||||
// bitReader.readBits(1);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBits() throws IOException {
|
||||
final LSBBitReader bitReader = createBitReader(new byte[] {
|
||||
0b00100101, 0b01000010, 0b00010000, 0b00001000,
|
||||
0b00001000, 0b00010000, 0b01000000, 0b00000000,
|
||||
0b00000010, 0b00100000, 0b00000000, 0b00000100,
|
||||
0b00000000, 0b00000001, (byte) 0b10000000,
|
||||
});
|
||||
|
||||
assertEquals(1, bitReader.readBits(1));
|
||||
assertEquals(2, bitReader.readBits(2));
|
||||
assertEquals(4, bitReader.readBits(3));
|
||||
assertEquals(8, bitReader.readBits(4));
|
||||
assertEquals(16, bitReader.readBits(5));
|
||||
assertEquals(32, bitReader.readBits(6));
|
||||
assertEquals(64, bitReader.readBits(7));
|
||||
assertEquals(128, bitReader.readBits(8));
|
||||
assertEquals(256, bitReader.readBits(9));
|
||||
assertEquals(512, bitReader.readBits(10));
|
||||
assertEquals(1024, bitReader.readBits(11));
|
||||
assertEquals(2048, bitReader.readBits(12));
|
||||
assertEquals(4096, bitReader.readBits(13));
|
||||
assertEquals(8192, bitReader.readBits(14));
|
||||
assertEquals(16384, bitReader.readBits(15));
|
||||
|
||||
// assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
// @Override
|
||||
// public void run() throws Throwable {
|
||||
// bitReader.readBits(1);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPeekBits() throws IOException {
|
||||
final LSBBitReader bitReader = createBitReader(new byte[] {
|
||||
0b00100101, 0b01000010, 0b00010000, 0b00001000,
|
||||
0b00001000, 0b00010000, 0b01000000, 0b00000000,
|
||||
0b00000010, 0b00100000, 0b00000000, 0b00000100,
|
||||
0b00000000, 0b00000001, (byte) 0b10000000
|
||||
});
|
||||
|
||||
assertEquals(1, bitReader.peekBits(1));
|
||||
assertEquals(1, bitReader.peekBits(1));
|
||||
assertEquals(1, bitReader.readBits(1));
|
||||
|
||||
assertEquals(2, bitReader.peekBits(2));
|
||||
assertEquals(2, bitReader.readBits(2));
|
||||
|
||||
assertEquals(4, bitReader.readBits(3));
|
||||
|
||||
assertEquals(8, bitReader.peekBits(4));
|
||||
assertEquals(8, bitReader.readBits(4));
|
||||
|
||||
assertEquals(16, bitReader.peekBits(5));
|
||||
assertEquals(16, bitReader.peekBits(5));
|
||||
assertEquals(16, bitReader.readBits(5));
|
||||
|
||||
assertEquals(32, bitReader.peekBits(6));
|
||||
assertEquals(32, bitReader.readBits(6));
|
||||
|
||||
assertEquals(64, bitReader.peekBits(7));
|
||||
assertEquals(64, bitReader.peekBits(7));
|
||||
assertEquals(64, bitReader.peekBits(7));
|
||||
assertEquals(64, bitReader.peekBits(7));
|
||||
assertEquals(64, bitReader.readBits(7));
|
||||
|
||||
assertEquals(128, bitReader.peekBits(8));
|
||||
assertEquals(128, bitReader.readBits(8));
|
||||
|
||||
assertEquals(256, bitReader.readBits(9));
|
||||
|
||||
assertEquals(512, bitReader.peekBits(10));
|
||||
assertEquals(512, bitReader.readBits(10));
|
||||
|
||||
assertEquals(1024, bitReader.peekBits(11));
|
||||
assertEquals(1024, bitReader.readBits(11));
|
||||
|
||||
assertEquals(2048, bitReader.peekBits(12));
|
||||
assertEquals(2048, bitReader.peekBits(12));
|
||||
assertEquals(2048, bitReader.peekBits(12));
|
||||
assertEquals(2048, bitReader.readBits(12));
|
||||
|
||||
assertEquals(4096, bitReader.peekBits(13));
|
||||
assertEquals(4096, bitReader.readBits(13));
|
||||
|
||||
assertEquals(8192, bitReader.readBits(14));
|
||||
|
||||
assertEquals(16384, bitReader.peekBits(15));
|
||||
assertEquals(16384, bitReader.peekBits(15));
|
||||
assertEquals(16384, bitReader.readBits(15));
|
||||
|
||||
// assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
// @Override
|
||||
// public void run() throws Throwable {
|
||||
// bitReader.readBits(1);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadBetweenBits() throws IOException {
|
||||
ImageInputStream stream = createStream(new byte[] {
|
||||
0b00100101, 0b01000010, 0b00010000, 0b00001000,
|
||||
0b00001000, 0b00010000, 0b01000000, 0b00000000,
|
||||
0b00000010, 0b00100000, 0b00000000, 0b00000100,
|
||||
0b00000000, 0b00000001, (byte) 0b10000000
|
||||
});
|
||||
final LSBBitReader bitReader = new LSBBitReader(stream);
|
||||
|
||||
assertEquals(1, bitReader.peekBits(1));
|
||||
assertEquals(1, bitReader.peekBits(1));
|
||||
assertEquals(1, bitReader.readBits(1));
|
||||
|
||||
assertEquals(2, bitReader.peekBits(2));
|
||||
assertEquals(2, bitReader.readBits(2));
|
||||
|
||||
assertEquals(4, bitReader.readBits(3));
|
||||
|
||||
// We've read 6 bits, but still on the 1st byte
|
||||
assertEquals(0b00100101, stream.readByte());
|
||||
|
||||
// Start reading from the second byte (10 == 2)
|
||||
assertEquals(2, bitReader.readBits(2));
|
||||
|
||||
assertEquals(16, bitReader.peekBits(5));
|
||||
assertEquals(16, bitReader.peekBits(5));
|
||||
assertEquals(16, bitReader.readBits(5));
|
||||
|
||||
// We've now read 7 bits, but still on the second byte
|
||||
assertEquals(1, stream.getStreamPosition());
|
||||
assertEquals(0b01000010, stream.readByte());
|
||||
assertEquals(2, stream.getStreamPosition());
|
||||
|
||||
assertEquals(16, bitReader.peekBits(11));
|
||||
|
||||
assertEquals(0b00010000, stream.readByte());
|
||||
assertEquals(3, stream.getStreamPosition());
|
||||
stream.seek(2);
|
||||
assertEquals(2, stream.getStreamPosition());
|
||||
|
||||
// Start reading from the third byte (10000 == 16)
|
||||
assertEquals(16, bitReader.peekBits(5));
|
||||
assertEquals(16, bitReader.readBits(5));
|
||||
|
||||
assertEquals(64, bitReader.peekBits(7));
|
||||
assertEquals(64, bitReader.peekBits(7));
|
||||
assertEquals(64, bitReader.peekBits(7));
|
||||
assertEquals(64, bitReader.peekBits(7));
|
||||
assertEquals(64, bitReader.readBits(7));
|
||||
|
||||
assertEquals(128, bitReader.peekBits(8));
|
||||
assertEquals(128, bitReader.readBits(8));
|
||||
|
||||
assertEquals(256, bitReader.readBits(9));
|
||||
|
||||
assertEquals(512, bitReader.peekBits(10));
|
||||
assertEquals(512, bitReader.readBits(10));
|
||||
|
||||
assertEquals(1024, bitReader.peekBits(11));
|
||||
assertEquals(1024, bitReader.readBits(11));
|
||||
|
||||
assertEquals(2048, bitReader.peekBits(12));
|
||||
assertEquals(2048, bitReader.peekBits(12));
|
||||
assertEquals(2048, bitReader.peekBits(12));
|
||||
assertEquals(2048, bitReader.readBits(12));
|
||||
|
||||
assertEquals(4096, bitReader.peekBits(13));
|
||||
assertEquals(4096, bitReader.readBits(13));
|
||||
|
||||
assertEquals(8192, bitReader.readBits(14));
|
||||
|
||||
assertEquals(16384, bitReader.peekBits(15));
|
||||
assertEquals(16384, bitReader.peekBits(15));
|
||||
assertEquals(16384, bitReader.readBits(15));
|
||||
|
||||
// assertThrows(EOFException.class, new ThrowingRunnable() {
|
||||
// @Override
|
||||
// public void run() throws Throwable {
|
||||
// bitReader.readBits(1);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
private static LSBBitReader createBitReader(final byte[] data) {
|
||||
ImageInputStream stream = createStream(data);
|
||||
return new LSBBitReader(stream);
|
||||
}
|
||||
|
||||
private static ImageInputStream createStream(byte[] data) {
|
||||
ByteArrayImageInputStream stream = new ByteArrayImageInputStream(data);
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
+29
-8
@@ -1,11 +1,8 @@
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
@@ -13,10 +10,13 @@ import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* WebPImageReaderTest
|
||||
@@ -141,4 +141,25 @@ public class WebPImageReaderTest extends ImageReaderAbstractTest<WebPImageReader
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadAlphaTransparent() throws IOException {
|
||||
WebPImageReader reader = createReader();
|
||||
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/webp/1_webp_a.webp"))) {
|
||||
reader.setInput(stream);
|
||||
BufferedImage image = reader.read(0);
|
||||
|
||||
assertEquals(Transparency.TRANSLUCENT, image.getTransparency());
|
||||
|
||||
assertRGBEquals("Expected transparent corner (0, 0)", 0x00000000, image.getRGB(0, 0) & 0xFF000000, 8);
|
||||
assertRGBEquals("Expected opaque center (200, 150)", 0xff9a4e01, image.getRGB(200, 150), 8);
|
||||
assertRGBEquals("Expected transparent corner (399, 0)", 0x00000000, image.getRGB(399, 0) & 0xFF000000, 8);
|
||||
assertRGBEquals("Expected transparent corner (0, 300)", 0x00000000, image.getRGB(0, 300) & 0xFF000000, 8);
|
||||
assertRGBEquals("Expected transparent corner (399, 300)", 0x00000000, image.getRGB(399, 300) & 0xFF000000, 8);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<artifactId>imageio-xwd</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: XWD plugin</name>
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>TwelveMonkeys</name>
|
||||
<description>TwelveMonkeys parent POM</description>
|
||||
@@ -80,7 +80,7 @@
|
||||
<connection>scm:git:https://github.com/haraldk/TwelveMonkeys</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/haraldk/TwelveMonkeys</developerConnection>
|
||||
<url>https://github.com/haraldk/TwelveMonkeys</url>
|
||||
<tag>twelvemonkeys-3.9.0</tag>
|
||||
<tag>twelvemonkeys-3.9.3</tag>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys</groupId>
|
||||
<artifactId>twelvemonkeys</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<version>3.9.3</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
Reference in New Issue
Block a user