mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-22 00:00:03 -04:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 488d6da71a | |||
| b19e45a911 | |||
| a5b6cf898d | |||
| ce597c437d | |||
| fa4b3787d0 | |||
| 3c18e8a510 | |||
| 206481038e | |||
| cff4d88991 | |||
| 8ea8e061a9 | |||
| 101ad18f71 | |||
| 08b441a17e | |||
| b6c76d8566 | |||
| 3f74b2ddf3 | |||
| 46b48f32c3 | |||
| a07d0285fe | |||
| 98de4ad4ec | |||
| aa82612765 | |||
| 9213da3184 | |||
| a5e2226a5a | |||
| 773bedccca | |||
| 6bcc17a020 | |||
| 37d1da9b9d | |||
| 8cf1405dfc | |||
| 8c37d19928 | |||
| 87cd506fdd | |||
| e0c7edebbd | |||
| 5d13bd653f | |||
| 2d974874a9 | |||
| f625622b10 | |||
| dbdd7ae3f1 | |||
| 73883ebf99 | |||
| 970b238066 | |||
| 6cb8ac4b68 | |||
| 1a2a4edfe8 | |||
| a12a1f73b5 | |||
| 46bfdd93d8 |
@@ -7,14 +7,14 @@ assignees: ''
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
**Is your feature request related to a use case or a problem you are working on? Please describe.**
|
||||||
A clear and concise description of what the problem or use case is.
|
A clear and concise description of what the problem or use case is. Understanding the rationale is key, to be able to implemeent the right solution.
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
**Describe the solution you'd like**
|
||||||
A clear and concise description of what you want to happen.
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
**Describe alternatives you've considered**
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
A clear and concise description of any alternative solutions or features you've already considered, and why they won't work.
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here, like links to specifications or sample files.
|
Add any other context or screenshots about the feature request here, like links to specifications or sample files.
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>com.twelvemonkeys.bom</groupId>
|
<groupId>com.twelvemonkeys.bom</groupId>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-image</artifactId>
|
<artifactId>common-image</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jmagick</groupId>
|
<groupId>jmagick</groupId>
|
||||||
<artifactId>jmagick</artifactId>
|
<artifactId>jmagick</artifactId>
|
||||||
<version>6.2.4</version>
|
<version>6.6.9</version>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-io</artifactId>
|
<artifactId>common-io</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
|
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version
|
||||||
@@ -42,11 +43,8 @@ import java.io.OutputStream;
|
|||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: FastByteArrayOutputStream.java#2 $
|
* @version $Id: FastByteArrayOutputStream.java#2 $
|
||||||
*/
|
*/
|
||||||
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
|
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
|
||||||
public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||||
/** Max grow size (unless if writing more than this amount of bytes) */
|
|
||||||
protected int maxGrowSize = 1024 * 1024; // 1 MB
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
|
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
|
||||||
* size.
|
* size.
|
||||||
@@ -97,10 +95,8 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
|
|
||||||
private void growIfNeeded(int pNewCount) {
|
private void growIfNeeded(int pNewCount) {
|
||||||
if (pNewCount > buf.length) {
|
if (pNewCount > buf.length) {
|
||||||
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
|
int newSize = Math.max(buf.length << 1, pNewCount);
|
||||||
byte[] newBuf = new byte[newSize];
|
buf = Arrays.copyOf(buf, newSize);
|
||||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
|
||||||
buf = newBuf;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,10 +109,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
// Non-synchronized version of toByteArray
|
// Non-synchronized version of toByteArray
|
||||||
@Override
|
@Override
|
||||||
public byte[] toByteArray() {
|
public byte[] toByteArray() {
|
||||||
byte[] newBuf = new byte[count];
|
return Arrays.copyOf(buf, count);
|
||||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
|
||||||
|
|
||||||
return newBuf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -45,39 +45,39 @@ import java.nio.ByteBuffer;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java#2 $
|
||||||
*/
|
*/
|
||||||
public final class DecoderStream extends FilterInputStream {
|
public final class DecoderStream extends FilterInputStream {
|
||||||
protected final ByteBuffer buffer;
|
private final ByteBuffer buffer;
|
||||||
protected final Decoder decoder;
|
private final Decoder decoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new decoder stream and chains it to the
|
* Creates a new decoder stream and chains it to the
|
||||||
* input stream specified by the {@code pStream} argument.
|
* input stream specified by the {@code stream} argument.
|
||||||
* The stream will use a default decode buffer size.
|
* The stream will use a default decode buffer size.
|
||||||
*
|
*
|
||||||
* @param pStream the underlying input stream.
|
* @param stream the underlying input stream.
|
||||||
* @param pDecoder the decoder that will be used to decode the underlying stream
|
* @param decoder the decoder that will be used to decode the underlying stream
|
||||||
*
|
*
|
||||||
* @see java.io.FilterInputStream#in
|
* @see java.io.FilterInputStream#in
|
||||||
*/
|
*/
|
||||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder) {
|
public DecoderStream(final InputStream stream, final Decoder decoder) {
|
||||||
// TODO: Let the decoder decide preferred buffer size
|
// TODO: Let the decoder decide preferred buffer size
|
||||||
this(pStream, pDecoder, 1024);
|
this(stream, decoder, 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new decoder stream and chains it to the
|
* Creates a new decoder stream and chains it to the
|
||||||
* input stream specified by the {@code pStream} argument.
|
* input stream specified by the {@code stream} argument.
|
||||||
*
|
*
|
||||||
* @param pStream the underlying input stream.
|
* @param stream the underlying input stream.
|
||||||
* @param pDecoder the decoder that will be used to decode the underlying stream
|
* @param decoder the decoder that will be used to decode the underlying stream
|
||||||
* @param pBufferSize the size of the decode buffer
|
* @param bufferSize the size of the decode buffer
|
||||||
*
|
*
|
||||||
* @see java.io.FilterInputStream#in
|
* @see java.io.FilterInputStream#in
|
||||||
*/
|
*/
|
||||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
|
public DecoderStream(final InputStream stream, final Decoder decoder, final int bufferSize) {
|
||||||
super(pStream);
|
super(stream);
|
||||||
|
|
||||||
decoder = pDecoder;
|
this.decoder = decoder;
|
||||||
buffer = ByteBuffer.allocate(pBufferSize);
|
buffer = ByteBuffer.allocate(bufferSize);
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,15 +95,15 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
return buffer.get() & 0xff;
|
return buffer.get() & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(final byte pBytes[], final int pOffset, final int pLength) throws IOException {
|
public int read(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||||
if (pBytes == null) {
|
if (bytes == null) {
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
}
|
}
|
||||||
else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
else if ((offset < 0) || (offset > bytes.length) || (length < 0) ||
|
||||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
((offset + length) > bytes.length) || ((offset + length) < 0)) {
|
||||||
throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength);
|
throw new IndexOutOfBoundsException("bytes.length=" + bytes.length + " offset=" + offset + " length=" + length);
|
||||||
}
|
}
|
||||||
else if (pLength == 0) {
|
else if (length == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,11 +114,11 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read until we have read pLength bytes, or have reached EOF
|
// Read until we have read length bytes, or have reached EOF
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int off = pOffset;
|
int off = offset;
|
||||||
|
|
||||||
while (pLength > count) {
|
while (length > count) {
|
||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
if (fill() < 0) {
|
if (fill() < 0) {
|
||||||
break;
|
break;
|
||||||
@@ -126,8 +126,8 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy as many bytes as possible
|
// Copy as many bytes as possible
|
||||||
int dstLen = Math.min(pLength - count, buffer.remaining());
|
int dstLen = Math.min(length - count, buffer.remaining());
|
||||||
buffer.get(pBytes, off, dstLen);
|
buffer.get(bytes, off, dstLen);
|
||||||
|
|
||||||
// Update offset (rest)
|
// Update offset (rest)
|
||||||
off += dstLen;
|
off += dstLen;
|
||||||
@@ -139,7 +139,7 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long skip(final long pLength) throws IOException {
|
public long skip(final long length) throws IOException {
|
||||||
// End of file?
|
// End of file?
|
||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
if (fill() < 0) {
|
if (fill() < 0) {
|
||||||
@@ -147,10 +147,10 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip until we have skipped pLength bytes, or have reached EOF
|
// Skip until we have skipped length bytes, or have reached EOF
|
||||||
long total = 0;
|
long total = 0;
|
||||||
|
|
||||||
while (total < pLength) {
|
while (total < length) {
|
||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
if (fill() < 0) {
|
if (fill() < 0) {
|
||||||
break;
|
break;
|
||||||
@@ -158,7 +158,7 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe
|
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe
|
||||||
int skipped = (int) Math.min(pLength - total, buffer.remaining());
|
int skipped = (int) Math.min(length - total, buffer.remaining());
|
||||||
buffer.position(buffer.position() + skipped);
|
buffer.position(buffer.position() + skipped);
|
||||||
total += skipped;
|
total += skipped;
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,7 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
*
|
*
|
||||||
* @throws IOException if an I/O error occurs
|
* @throws IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
protected int fill() throws IOException {
|
private int fill() throws IOException {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
int read = decoder.decode(in, buffer);
|
int read = decoder.decode(in, buffer);
|
||||||
|
|
||||||
|
|||||||
@@ -45,41 +45,39 @@ import java.nio.ByteBuffer;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java#2 $
|
||||||
*/
|
*/
|
||||||
public final class EncoderStream extends FilterOutputStream {
|
public final class EncoderStream extends FilterOutputStream {
|
||||||
// TODO: This class need a test case ASAP!!!
|
|
||||||
|
|
||||||
protected final Encoder encoder;
|
private final Encoder encoder;
|
||||||
private final boolean flushOnWrite;
|
private final boolean flushOnWrite;
|
||||||
|
|
||||||
protected final ByteBuffer buffer;
|
private final ByteBuffer buffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an output stream filter built on top of the specified
|
* Creates an output stream filter built on top of the specified
|
||||||
* underlying output stream.
|
* underlying output stream.
|
||||||
*
|
*
|
||||||
* @param pStream the underlying output stream
|
* @param stream the underlying output stream
|
||||||
* @param pEncoder the encoder to use
|
* @param encoder the encoder to use
|
||||||
*/
|
*/
|
||||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
|
public EncoderStream(final OutputStream stream, final Encoder encoder) {
|
||||||
this(pStream, pEncoder, false);
|
this(stream, encoder, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an output stream filter built on top of the specified
|
* Creates an output stream filter built on top of the specified
|
||||||
* underlying output stream.
|
* underlying output stream.
|
||||||
*
|
*
|
||||||
* @param pStream the underlying output stream
|
* @param stream the underlying output stream
|
||||||
* @param pEncoder the encoder to use
|
* @param encoder the encoder to use
|
||||||
* @param pFlushOnWrite if {@code true}, calls to the byte-array
|
* @param flushOnWrite if {@code true}, calls to the byte-array
|
||||||
* {@code write} methods will automatically flush the buffer.
|
* {@code write} methods will automatically flush the buffer.
|
||||||
*/
|
*/
|
||||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
|
public EncoderStream(final OutputStream stream, final Encoder encoder, final boolean flushOnWrite) {
|
||||||
super(pStream);
|
super(stream);
|
||||||
|
|
||||||
encoder = pEncoder;
|
this.encoder = encoder;
|
||||||
flushOnWrite = pFlushOnWrite;
|
this.flushOnWrite = flushOnWrite;
|
||||||
|
|
||||||
buffer = ByteBuffer.allocate(1024);
|
buffer = ByteBuffer.allocate(1024);
|
||||||
buffer.flip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
@@ -104,33 +102,33 @@ public final class EncoderStream extends FilterOutputStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void write(final byte[] pBytes) throws IOException {
|
public void write(final byte[] bytes) throws IOException {
|
||||||
write(pBytes, 0, pBytes.length);
|
write(bytes, 0, bytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Verify that this works for the general case (it probably won't)...
|
// TODO: Verify that this works for the general case (it probably won't)...
|
||||||
// TODO: We might need a way to explicitly flush the encoder, or specify
|
// TODO: We might need a way to explicitly flush the encoder, or specify
|
||||||
// that the encoder can't buffer. In that case, the encoder should probably
|
// that the encoder can't buffer. In that case, the encoder should probably
|
||||||
// tell the EncoderStream how large buffer it prefers...
|
// tell the EncoderStream how large buffer it prefers...
|
||||||
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
public void write(final byte[] values, final int offset, final int length) throws IOException {
|
||||||
if (!flushOnWrite && pLength < buffer.remaining()) {
|
if (!flushOnWrite && length < buffer.remaining()) {
|
||||||
// Buffer data
|
// Buffer data
|
||||||
buffer.put(pBytes, pOffset, pLength);
|
buffer.put(values, offset, length);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Encode data already in the buffer
|
// Encode data already in the buffer
|
||||||
encodeBuffer();
|
encodeBuffer();
|
||||||
|
|
||||||
// Encode rest without buffering
|
// Encode rest without buffering
|
||||||
encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength));
|
encoder.encode(out, ByteBuffer.wrap(values, offset, length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final int pByte) throws IOException {
|
public void write(final int value) throws IOException {
|
||||||
if (!buffer.hasRemaining()) {
|
if (!buffer.hasRemaining()) {
|
||||||
encodeBuffer(); // Resets bufferPos to 0
|
encodeBuffer(); // Resets bufferPos to 0
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.put((byte) pByte);
|
buffer.put((byte) value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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.io.enc;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class DecoderStreamTest {
|
||||||
|
|
||||||
|
private final Random rng = new Random(5467809876546L);
|
||||||
|
|
||||||
|
private byte[] createData(final int length) {
|
||||||
|
byte[] data = new byte[length];
|
||||||
|
rng.nextBytes(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecodeSingleBytes() throws IOException {
|
||||||
|
byte[] data = createData(1327);
|
||||||
|
|
||||||
|
InputStream source = new ByteArrayInputStream(data);
|
||||||
|
try (InputStream stream = new DecoderStream(source, new NullDecoder())) {
|
||||||
|
for (byte datum : data) {
|
||||||
|
int read = stream.read();
|
||||||
|
assertNotEquals(-1, read);
|
||||||
|
assertEquals(datum, (byte) read);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecodeArray() throws IOException {
|
||||||
|
int length = 793;
|
||||||
|
byte[] data = createData(length * 10);
|
||||||
|
|
||||||
|
InputStream source = new ByteArrayInputStream(data);
|
||||||
|
byte[] result = new byte[477];
|
||||||
|
|
||||||
|
try (InputStream stream = new DecoderStream(source, new NullDecoder())) {
|
||||||
|
int dataOffset = 0;
|
||||||
|
while (dataOffset < data.length) {
|
||||||
|
int count = stream.read(result);
|
||||||
|
|
||||||
|
assertFalse(count <= 0);
|
||||||
|
assertArrayEquals(Arrays.copyOfRange(data, dataOffset, dataOffset + count), Arrays.copyOfRange(result, 0, count));
|
||||||
|
|
||||||
|
dataOffset += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecodeArrayOffset() throws IOException {
|
||||||
|
int length = 793;
|
||||||
|
byte[] data = createData(length * 10);
|
||||||
|
|
||||||
|
InputStream source = new ByteArrayInputStream(data);
|
||||||
|
byte[] result = new byte[477];
|
||||||
|
|
||||||
|
try (InputStream stream = new DecoderStream(source, new NullDecoder())) {
|
||||||
|
int dataOffset = 0;
|
||||||
|
while (dataOffset < data.length) {
|
||||||
|
int resultOffset = dataOffset % result.length;
|
||||||
|
int count = stream.read(result, resultOffset, result.length - resultOffset);
|
||||||
|
|
||||||
|
assertFalse(count <= 0);
|
||||||
|
assertArrayEquals(Arrays.copyOfRange(data, dataOffset + resultOffset, dataOffset + count), Arrays.copyOfRange(result, resultOffset, count));
|
||||||
|
|
||||||
|
dataOffset += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class NullDecoder implements Decoder {
|
||||||
|
@Override
|
||||||
|
public int decode(InputStream stream, ByteBuffer buffer) throws IOException {
|
||||||
|
int read = stream.read(buffer.array(), buffer.arrayOffset(), buffer.remaining());
|
||||||
|
|
||||||
|
if (read > 0) {
|
||||||
|
// Set position, should be equivalent to using buffer.put(stream.read()) until EOF or buffer full
|
||||||
|
buffer.position(read);
|
||||||
|
}
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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.io.enc;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
|
public class EncoderStreamTest {
|
||||||
|
|
||||||
|
private final Random rng = new Random(5467809876546L);
|
||||||
|
|
||||||
|
private byte[] createData(final int length) {
|
||||||
|
byte[] data = new byte[length];
|
||||||
|
rng.nextBytes(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeSingleBytes() throws IOException {
|
||||||
|
byte[] data = createData(1327);
|
||||||
|
|
||||||
|
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||||
|
try (OutputStream stream = new EncoderStream(result, new NullEncoder())) {
|
||||||
|
for (byte datum : data) {
|
||||||
|
stream.write(datum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertArrayEquals(data, result.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeArray() throws IOException {
|
||||||
|
byte[] data = createData(1793);
|
||||||
|
|
||||||
|
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||||
|
try (OutputStream stream = new EncoderStream(result, new NullEncoder())) {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
stream.write(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] encoded = result.toByteArray();
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertArrayEquals(data, Arrays.copyOfRange(encoded, i * data.length, (i + 1) * data.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeArrayOffset() throws IOException {
|
||||||
|
byte[] data = createData(87);
|
||||||
|
|
||||||
|
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||||
|
try (OutputStream stream = new EncoderStream(result, new NullEncoder())) {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
stream.write(data, 13, 59);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] original = Arrays.copyOfRange(data, 13, 13 + 59);
|
||||||
|
byte[] encoded = result.toByteArray();
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
assertArrayEquals(original, Arrays.copyOfRange(encoded, i * original.length, (i + 1) * original.length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class NullEncoder implements Encoder {
|
||||||
|
@Override
|
||||||
|
public void encode(OutputStream stream, ByteBuffer buffer) throws IOException {
|
||||||
|
stream.write(buffer.array(), buffer.arrayOffset(), buffer.remaining());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-lang</artifactId>
|
<artifactId>common-lang</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.twelvemonkeys.contrib</groupId>
|
<groupId>com.twelvemonkeys.contrib</groupId>
|
||||||
<artifactId>contrib</artifactId>
|
<artifactId>contrib</artifactId>
|
||||||
|
|||||||
@@ -30,9 +30,14 @@ import static com.twelvemonkeys.contrib.tiff.TIFFUtilities.applyOrientation;
|
|||||||
public class EXIFUtilities {
|
public class EXIFUtilities {
|
||||||
/**
|
/**
|
||||||
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||||
|
* The returned {@code IIOImage} will always contain an image and no raster, and
|
||||||
|
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
|
||||||
|
*
|
||||||
|
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
|
||||||
*
|
*
|
||||||
* @param input a {@code URL}
|
* @param input a {@code URL}
|
||||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info, or
|
||||||
|
* {@code null}.
|
||||||
* @throws IOException if an error occurs during reading.
|
* @throws IOException if an error occurs during reading.
|
||||||
*/
|
*/
|
||||||
public static IIOImage readWithOrientation(final URL input) throws IOException {
|
public static IIOImage readWithOrientation(final URL input) throws IOException {
|
||||||
@@ -43,9 +48,14 @@ public class EXIFUtilities {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||||
|
* The returned {@code IIOImage} will always contain an image and no raster, and
|
||||||
|
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
|
||||||
|
*
|
||||||
|
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
|
||||||
*
|
*
|
||||||
* @param input an {@code InputStream}
|
* @param input an {@code InputStream}
|
||||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info, or
|
||||||
|
* {@code null}.
|
||||||
* @throws IOException if an error occurs during reading.
|
* @throws IOException if an error occurs during reading.
|
||||||
*/
|
*/
|
||||||
public static IIOImage readWithOrientation(final InputStream input) throws IOException {
|
public static IIOImage readWithOrientation(final InputStream input) throws IOException {
|
||||||
@@ -56,9 +66,14 @@ public class EXIFUtilities {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||||
|
* The returned {@code IIOImage} will always contain an image and no raster, and
|
||||||
|
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
|
||||||
|
*
|
||||||
|
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
|
||||||
*
|
*
|
||||||
* @param input a {@code File}
|
* @param input a {@code File}
|
||||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info or
|
||||||
|
* {@code null}.
|
||||||
* @throws IOException if an error occurs during reading.
|
* @throws IOException if an error occurs during reading.
|
||||||
*/
|
*/
|
||||||
public static IIOImage readWithOrientation(final File input) throws IOException {
|
public static IIOImage readWithOrientation(final File input) throws IOException {
|
||||||
@@ -69,9 +84,14 @@ public class EXIFUtilities {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
* Reads image and metadata, applies Exif orientation to image, and returns everything as an {@code IIOImage}.
|
||||||
|
* The returned {@code IIOImage} will always contain an image and no raster, and
|
||||||
|
* the {@code RenderedImage} may be safely cast to a {@code BufferedImage}.
|
||||||
|
*
|
||||||
|
* If no registered {@code ImageReader} claims to be able to read the input, {@code null} is returned.
|
||||||
*
|
*
|
||||||
* @param input an {@code ImageInputStream}
|
* @param input an {@code ImageInputStream}
|
||||||
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info.
|
* @return an {@code IIOImage} containing the correctly oriented image and metadata including rotation info, or
|
||||||
|
* {@code null}.
|
||||||
* @throws IOException if an error occurs during reading.
|
* @throws IOException if an error occurs during reading.
|
||||||
*/
|
*/
|
||||||
public static IIOImage readWithOrientation(final ImageInputStream input) throws IOException {
|
public static IIOImage readWithOrientation(final ImageInputStream input) throws IOException {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-batik</artifactId>
|
<artifactId>imageio-batik</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: Batik Plugin</name>
|
||||||
@@ -27,9 +27,9 @@
|
|||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
<systemPropertyVariables>
|
<systemPropertyVariables>
|
||||||
<com.twelvemonkeys.imageio.plugins.svg.allowexternalresources>
|
<com.twelvemonkeys.imageio.plugins.svg.allowExternalResources>
|
||||||
true
|
true
|
||||||
</com.twelvemonkeys.imageio.plugins.svg.allowexternalresources>
|
</com.twelvemonkeys.imageio.plugins.svg.allowExternalResources>
|
||||||
</systemPropertyVariables>
|
</systemPropertyVariables>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|||||||
+109
-103
@@ -30,21 +30,10 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.svg;
|
package com.twelvemonkeys.imageio.plugins.svg;
|
||||||
|
|
||||||
import java.awt.*;
|
import com.twelvemonkeys.image.ImageUtil;
|
||||||
import java.awt.geom.AffineTransform;
|
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||||
import java.awt.geom.Rectangle2D;
|
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||||
import java.awt.image.BufferedImage;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
|
||||||
import javax.imageio.ImageReadParam;
|
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
|
||||||
|
|
||||||
import org.apache.batik.anim.dom.SVGDOMImplementation;
|
import org.apache.batik.anim.dom.SVGDOMImplementation;
|
||||||
import org.apache.batik.anim.dom.SVGOMDocument;
|
import org.apache.batik.anim.dom.SVGOMDocument;
|
||||||
@@ -68,10 +57,19 @@ import org.w3c.dom.DOMImplementation;
|
|||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.svg.SVGSVGElement;
|
import org.w3c.dom.svg.SVGSVGElement;
|
||||||
|
|
||||||
import com.twelvemonkeys.image.ImageUtil;
|
import javax.imageio.IIOException;
|
||||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
import javax.imageio.ImageReadParam;
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.geom.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image reader for SVG document fragments.
|
* Image reader for SVG document fragments.
|
||||||
@@ -79,12 +77,13 @@ import com.twelvemonkeys.lang.StringUtil;
|
|||||||
* @author Harald Kuhr
|
* @author Harald Kuhr
|
||||||
* @author Inpspired by code from the Batik Team
|
* @author Inpspired by code from the Batik Team
|
||||||
* @version $Id: $
|
* @version $Id: $
|
||||||
* @see <A href="http://www.mail-archive.com/batik-dev@xml.apache.org/msg00992.html">batik-dev</A>
|
* @see <a href="http://www.mail-archive.com/batik-dev@xml.apache.org/msg00992.html">batik-dev</a>
|
||||||
*/
|
*/
|
||||||
public class SVGImageReader extends ImageReaderBase {
|
public class SVGImageReader extends ImageReaderBase {
|
||||||
|
|
||||||
final static boolean DEFAULT_ALLOW_EXTERNAL_RESOURCES =
|
final static boolean DEFAULT_ALLOW_EXTERNAL_RESOURCES =
|
||||||
"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowexternalresources"));
|
"true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowExternalResources",
|
||||||
|
System.getProperty("com.twelvemonkeys.imageio.plugins.svg.allowexternalresources")));
|
||||||
|
|
||||||
private Rasterizer rasterizer;
|
private Rasterizer rasterizer;
|
||||||
private boolean allowExternalResources = DEFAULT_ALLOW_EXTERNAL_RESOURCES;
|
private boolean allowExternalResources = DEFAULT_ALLOW_EXTERNAL_RESOURCES;
|
||||||
@@ -150,29 +149,23 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
BufferedImage destination = getDestination(pParam, getImageTypes(pIndex), size.width, size.height);
|
BufferedImage destination = getDestination(pParam, getImageTypes(pIndex), size.width, size.height);
|
||||||
|
|
||||||
// Read in the image, using the Batik Transcoder
|
// Read in the image, using the Batik Transcoder
|
||||||
|
processImageStarted(pIndex);
|
||||||
|
|
||||||
|
BufferedImage image = rasterizer.getImage();
|
||||||
|
|
||||||
|
Graphics2D g = destination.createGraphics();
|
||||||
try {
|
try {
|
||||||
processImageStarted(pIndex);
|
g.setComposite(AlphaComposite.Src);
|
||||||
|
g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
|
||||||
BufferedImage image = rasterizer.getImage();
|
g.drawImage(image, 0, 0, null); // TODO: Dest offset?
|
||||||
|
|
||||||
Graphics2D g = destination.createGraphics();
|
|
||||||
try {
|
|
||||||
g.setComposite(AlphaComposite.Src);
|
|
||||||
g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
|
|
||||||
g.drawImage(image, 0, 0, null); // TODO: Dest offset?
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
g.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
processImageComplete();
|
|
||||||
|
|
||||||
return destination;
|
|
||||||
}
|
}
|
||||||
catch (TranscoderException e) {
|
finally {
|
||||||
Throwable cause = unwrapException(e);
|
g.dispose();
|
||||||
throw new IIOException(cause.getMessage(), cause);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processImageComplete();
|
||||||
|
|
||||||
|
return destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Throwable unwrapException(TranscoderException ex) {
|
private static Throwable unwrapException(TranscoderException ex) {
|
||||||
@@ -187,11 +180,11 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
// Set dimensions
|
// Set dimensions
|
||||||
Dimension size = pParam.getSourceRenderSize();
|
Dimension size = pParam.getSourceRenderSize();
|
||||||
Dimension origSize = new Dimension(getWidth(0), getHeight(0));
|
Rectangle viewBox = rasterizer.getViewBox();
|
||||||
if (size == null) {
|
if (size == null) {
|
||||||
// SVG is not a pixel based format, but we'll scale it, according to
|
// SVG is not a pixel based format, but we'll scale it, according to
|
||||||
// the subsampling for compatibility
|
// the subsampling for compatibility
|
||||||
size = getSourceRenderSizeFromSubsamping(pParam, origSize);
|
size = getSourceRenderSizeFromSubsamping(pParam, viewBox.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size != null) {
|
if (size != null) {
|
||||||
@@ -211,8 +204,8 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Need to resize here...
|
// Need to resize here...
|
||||||
double xScale = size.getWidth() / origSize.getWidth();
|
double xScale = size.getWidth() / viewBox.getWidth();
|
||||||
double yScale = size.getHeight() / origSize.getHeight();
|
double yScale = size.getHeight() / viewBox.getHeight();
|
||||||
|
|
||||||
hints.put(ImageTranscoder.KEY_WIDTH, (float) (region.getWidth() * xScale));
|
hints.put(ImageTranscoder.KEY_WIDTH, (float) (region.getWidth() * xScale));
|
||||||
hints.put(ImageTranscoder.KEY_HEIGHT, (float) (region.getHeight() * yScale));
|
hints.put(ImageTranscoder.KEY_HEIGHT, (float) (region.getHeight() * yScale));
|
||||||
@@ -220,7 +213,7 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
else if (size != null) {
|
else if (size != null) {
|
||||||
// Allow non-uniform scaling
|
// Allow non-uniform scaling
|
||||||
hints.put(ImageTranscoder.KEY_AOI, new Rectangle(origSize));
|
hints.put(ImageTranscoder.KEY_AOI, viewBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background color
|
// Background color
|
||||||
@@ -235,7 +228,7 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
private Dimension getSourceRenderSizeFromSubsamping(ImageReadParam pParam, Dimension pOrigSize) {
|
private Dimension getSourceRenderSizeFromSubsamping(ImageReadParam pParam, Dimension pOrigSize) {
|
||||||
if (pParam.getSourceXSubsampling() > 1 || pParam.getSourceYSubsampling() > 1) {
|
if (pParam.getSourceXSubsampling() > 1 || pParam.getSourceYSubsampling() > 1) {
|
||||||
return new Dimension((int) (pOrigSize.width / (float) pParam.getSourceXSubsampling()),
|
return new Dimension((int) (pOrigSize.width / (float) pParam.getSourceXSubsampling()),
|
||||||
(int) (pOrigSize.height / (float) pParam.getSourceYSubsampling()));
|
(int) (pOrigSize.height / (float) pParam.getSourceYSubsampling()));
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -246,22 +239,13 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
public int getWidth(int pIndex) throws IOException {
|
public int getWidth(int pIndex) throws IOException {
|
||||||
checkBounds(pIndex);
|
checkBounds(pIndex);
|
||||||
try {
|
|
||||||
return rasterizer.getDefaultWidth();
|
return rasterizer.getDefaultWidth();
|
||||||
}
|
|
||||||
catch (TranscoderException e) {
|
|
||||||
throw new IIOException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHeight(int pIndex) throws IOException {
|
public int getHeight(int pIndex) throws IOException {
|
||||||
checkBounds(pIndex);
|
checkBounds(pIndex);
|
||||||
try {
|
return rasterizer.getDefaultHeight();
|
||||||
return rasterizer.getDefaultHeight();
|
|
||||||
}
|
|
||||||
catch (TranscoderException e) {
|
|
||||||
throw new IIOException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) {
|
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) {
|
||||||
@@ -275,12 +259,11 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
* and needs major refactoring!
|
* and needs major refactoring!
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
private class Rasterizer extends SVGAbstractTranscoder /*ImageTranscoder*/ {
|
private class Rasterizer extends SVGAbstractTranscoder {
|
||||||
|
|
||||||
private BufferedImage image;
|
private BufferedImage image;
|
||||||
private TranscoderInput transcoderInput;
|
private TranscoderInput transcoderInput;
|
||||||
private float defaultWidth;
|
private final Rectangle2D viewBox = new Rectangle2D.Float();
|
||||||
private float defaultHeight;
|
private final Dimension defaultSize = new Dimension();
|
||||||
private boolean initialized = false;
|
private boolean initialized = false;
|
||||||
private SVGOMDocument document;
|
private SVGOMDocument document;
|
||||||
private String uri;
|
private String uri;
|
||||||
@@ -341,54 +324,66 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
// ----
|
// ----
|
||||||
SVGSVGElement rootElement = svgDoc.getRootElement();
|
SVGSVGElement rootElement = svgDoc.getRootElement();
|
||||||
|
|
||||||
// get the 'width' and 'height' attributes of the SVG document
|
// Get the viewBox
|
||||||
UnitProcessor.Context uctx
|
String viewBoxStr = rootElement.getAttributeNS(null, SVGConstants.SVG_VIEW_BOX_ATTRIBUTE);
|
||||||
= UnitProcessor.createContext(ctx, rootElement);
|
if (viewBoxStr.length() != 0) {
|
||||||
|
float[] rect = ViewBox.parseViewBoxAttribute(rootElement, viewBoxStr, null);
|
||||||
|
viewBox.setFrame(rect[0], rect[1], rect[2], rect[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the 'width' and 'height' attributes of the SVG document
|
||||||
|
double width = 0;
|
||||||
|
double height = 0;
|
||||||
|
UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, rootElement);
|
||||||
String widthStr = rootElement.getAttributeNS(null, SVGConstants.SVG_WIDTH_ATTRIBUTE);
|
String widthStr = rootElement.getAttributeNS(null, SVGConstants.SVG_WIDTH_ATTRIBUTE);
|
||||||
String heightStr = rootElement.getAttributeNS(null, SVGConstants.SVG_HEIGHT_ATTRIBUTE);
|
String heightStr = rootElement.getAttributeNS(null, SVGConstants.SVG_HEIGHT_ATTRIBUTE);
|
||||||
if (!StringUtil.isEmpty(widthStr)) {
|
if (!StringUtil.isEmpty(widthStr)) {
|
||||||
defaultWidth = UnitProcessor.svgToUserSpace(widthStr, SVGConstants.SVG_WIDTH_ATTRIBUTE, UnitProcessor.HORIZONTAL_LENGTH, uctx);
|
width = UnitProcessor.svgToUserSpace(widthStr, SVGConstants.SVG_WIDTH_ATTRIBUTE, UnitProcessor.HORIZONTAL_LENGTH, uctx);
|
||||||
}
|
}
|
||||||
if(!StringUtil.isEmpty(heightStr)){
|
if (!StringUtil.isEmpty(heightStr)) {
|
||||||
defaultHeight = UnitProcessor.svgToUserSpace(heightStr, SVGConstants.SVG_HEIGHT_ATTRIBUTE, UnitProcessor.VERTICAL_LENGTH, uctx);
|
height = UnitProcessor.svgToUserSpace(heightStr, SVGConstants.SVG_HEIGHT_ATTRIBUTE, UnitProcessor.VERTICAL_LENGTH, uctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasWidth = defaultWidth > 0.0;
|
boolean hasWidth = width > 0.0;
|
||||||
boolean hasHeight = defaultHeight > 0.0;
|
boolean hasHeight = height > 0.0;
|
||||||
|
|
||||||
if (!hasWidth || !hasHeight) {
|
if (!hasWidth || !hasHeight) {
|
||||||
String viewBoxStr = rootElement.getAttributeNS
|
if (!viewBox.isEmpty()) {
|
||||||
(null, SVGConstants.SVG_VIEW_BOX_ATTRIBUTE);
|
// If one dimension is given, calculate other by aspect ratio in viewBox
|
||||||
if (viewBoxStr.length() != 0) {
|
|
||||||
float[] rect = ViewBox.parseViewBoxAttribute(rootElement, viewBoxStr, null);
|
|
||||||
// if one dimension is given, calculate other by aspect ratio in viewBox
|
|
||||||
// or use viewBox if no dimension is given
|
|
||||||
if (hasWidth) {
|
if (hasWidth) {
|
||||||
defaultHeight = defaultWidth * rect[3] / rect[2];
|
height = width * viewBox.getHeight() / viewBox.getWidth();
|
||||||
}
|
}
|
||||||
else if (hasHeight) {
|
else if (hasHeight) {
|
||||||
defaultWidth = defaultHeight * rect[2] / rect[3];
|
width = height * viewBox.getWidth() / viewBox.getHeight();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
defaultWidth = rect[2];
|
// ...or use viewBox if no dimension is given
|
||||||
defaultHeight = rect[3];
|
width = viewBox.getWidth();
|
||||||
|
height = viewBox.getHeight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// No viewBox, just assume square size
|
||||||
if (hasHeight) {
|
if (hasHeight) {
|
||||||
defaultWidth = defaultHeight;
|
width = height;
|
||||||
}
|
}
|
||||||
else if (hasWidth) {
|
else if (hasWidth) {
|
||||||
defaultHeight = defaultWidth;
|
height = width;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// fallback to batik default sizes
|
// ...or finally fall back to Batik default sizes
|
||||||
defaultWidth = 400;
|
width = 400;
|
||||||
defaultHeight = 400;
|
height = 400;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We now have a size, in the rare case we don't have a viewBox; set it to this size
|
||||||
|
defaultSize.setSize(width, height);
|
||||||
|
if (viewBox.isEmpty()) {
|
||||||
|
viewBox.setRect(0, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
// Hack to work around exception above
|
// Hack to work around exception above
|
||||||
if (root != null) {
|
if (root != null) {
|
||||||
gvtRoot = root;
|
gvtRoot = root;
|
||||||
@@ -401,7 +396,7 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
ctx = null;
|
ctx = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedImage readImage() throws TranscoderException {
|
private BufferedImage readImage() throws IOException {
|
||||||
init();
|
init();
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
@@ -426,7 +421,8 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gvtRoot == null) {
|
if (gvtRoot == null) {
|
||||||
throw exception;
|
Throwable cause = unwrapException(exception);
|
||||||
|
throw new IIOException(cause.getMessage(), cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx = context;
|
ctx = context;
|
||||||
@@ -444,7 +440,7 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
setImageSize(defaultWidth, defaultHeight);
|
setImageSize(defaultSize.width, defaultSize.height);
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
processReadAborted();
|
processReadAborted();
|
||||||
@@ -458,18 +454,17 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Px = ViewBox.getViewTransform(ref, root, width, height, null);
|
Px = ViewBox.getViewTransform(ref, root, width, height, null);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (BridgeException ex) {
|
catch (BridgeException ex) {
|
||||||
throw new TranscoderException(ex);
|
throw new IIOException(ex.getMessage(), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Px.isIdentity() && (width != defaultWidth || height != defaultHeight)) {
|
if (Px.isIdentity() && (width != defaultSize.width || height != defaultSize.height)) {
|
||||||
// The document has no viewBox, we need to resize it by hand.
|
// The document has no viewBox, we need to resize it by hand.
|
||||||
// we want to keep the document size ratio
|
// we want to keep the document size ratio
|
||||||
float xscale, yscale;
|
float xscale, yscale;
|
||||||
xscale = width / defaultWidth;
|
xscale = width / defaultSize.width;
|
||||||
yscale = height / defaultHeight;
|
yscale = height / defaultSize.height;
|
||||||
float scale = Math.min(xscale, yscale);
|
float scale = Math.min(xscale, yscale);
|
||||||
Px = AffineTransform.getScaleInstance(scale, scale);
|
Px = AffineTransform.getScaleInstance(scale, scale);
|
||||||
}
|
}
|
||||||
@@ -519,7 +514,7 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (BridgeException ex) {
|
catch (BridgeException ex) {
|
||||||
throw new TranscoderException(ex);
|
throw new IIOException(ex.getMessage(), ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root = gvtRoot;
|
this.root = gvtRoot;
|
||||||
@@ -588,7 +583,7 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
throw new TranscoderException(ex.getMessage(), ex);
|
throw new IIOException(ex.getMessage(), ex);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
@@ -597,7 +592,7 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void init() throws TranscoderException {
|
private synchronized void init() throws IIOException {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
if (transcoderInput == null) {
|
if (transcoderInput == null) {
|
||||||
throw new IllegalStateException("input == null");
|
throw new IllegalStateException("input == null");
|
||||||
@@ -605,11 +600,17 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
|
||||||
super.transcode(transcoderInput, null);
|
try {
|
||||||
|
super.transcode(transcoderInput, null);
|
||||||
|
}
|
||||||
|
catch (TranscoderException e) {
|
||||||
|
Throwable cause = unwrapException(e);
|
||||||
|
throw new IIOException(cause.getMessage(), cause);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BufferedImage getImage() throws TranscoderException {
|
private BufferedImage getImage() throws IOException {
|
||||||
if (image == null) {
|
if (image == null) {
|
||||||
image = readImage();
|
image = readImage();
|
||||||
}
|
}
|
||||||
@@ -617,14 +618,19 @@ public class SVGImageReader extends ImageReaderBase {
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getDefaultWidth() throws TranscoderException {
|
int getDefaultWidth() throws IOException {
|
||||||
init();
|
init();
|
||||||
return (int) Math.ceil(defaultWidth);
|
return defaultSize.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getDefaultHeight() throws TranscoderException {
|
int getDefaultHeight() throws IOException {
|
||||||
init();
|
init();
|
||||||
return (int) Math.ceil(defaultHeight);
|
return defaultSize.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle getViewBox() throws IOException {
|
||||||
|
init();
|
||||||
|
return viewBox.getBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(final TranscoderInput pInput) {
|
void setInput(final TranscoderInput pInput) {
|
||||||
|
|||||||
+143
-141
@@ -1,141 +1,143 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
* Copyright (c) 2008, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
*
|
*
|
||||||
* * Redistributions of source code must retain the above copyright notice, this
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
* list of conditions and the following disclaimer.
|
* list of conditions and the following disclaimer.
|
||||||
*
|
*
|
||||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
* and/or other materials provided with the distribution.
|
* and/or other materials provided with the distribution.
|
||||||
*
|
*
|
||||||
* * Neither the name of the copyright holder nor the names of its
|
* * Neither the name of the copyright holder nor the names of its
|
||||||
* contributors may be used to endorse or promote products derived from
|
* contributors may be used to endorse or promote products derived from
|
||||||
* this software without specific prior written permission.
|
* this software without specific prior written permission.
|
||||||
*
|
*
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
* 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
|
* 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.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.wmf;
|
package com.twelvemonkeys.imageio.plugins.wmf;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||||
import com.twelvemonkeys.imageio.plugins.svg.SVGImageReader;
|
import com.twelvemonkeys.imageio.plugins.svg.SVGImageReader;
|
||||||
import com.twelvemonkeys.imageio.plugins.svg.SVGReadParam;
|
import com.twelvemonkeys.imageio.plugins.svg.SVGReadParam;
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||||
import org.apache.batik.transcoder.TranscoderException;
|
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||||
import org.apache.batik.transcoder.TranscoderInput;
|
|
||||||
import org.apache.batik.transcoder.TranscoderOutput;
|
import org.apache.batik.transcoder.TranscoderException;
|
||||||
import org.apache.batik.transcoder.wmf.tosvg.WMFTranscoder;
|
import org.apache.batik.transcoder.TranscoderInput;
|
||||||
|
import org.apache.batik.transcoder.TranscoderOutput;
|
||||||
import javax.imageio.IIOException;
|
import org.apache.batik.transcoder.wmf.tosvg.WMFTranscoder;
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import javax.imageio.ImageReadParam;
|
import javax.imageio.IIOException;
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
import javax.imageio.ImageReadParam;
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import java.awt.image.BufferedImage;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import java.io.*;
|
import java.awt.image.*;
|
||||||
import java.util.Iterator;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
/**
|
import java.io.OutputStreamWriter;
|
||||||
* WMFImageReader class description.
|
import java.io.Writer;
|
||||||
*
|
import java.nio.charset.StandardCharsets;
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
import java.util.Iterator;
|
||||||
* @author last modified by $Author: haku $
|
|
||||||
* @version $Id: WMFImageReader.java,v 1.0 29.jul.2004 13:00:59 haku Exp $
|
/**
|
||||||
*/
|
* WMFImageReader class description.
|
||||||
// TODO: Probably possible to do less wrapping/unwrapping of data...
|
*
|
||||||
// TODO: Consider using temp file instead of in-memory stream
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
public final class WMFImageReader extends ImageReaderBase {
|
* @author last modified by $Author: haku $
|
||||||
|
* @version $Id: WMFImageReader.java,v 1.0 29.jul.2004 13:00:59 haku Exp $
|
||||||
private SVGImageReader reader = null;
|
*/
|
||||||
|
// TODO: Probably possible to do less wrapping/unwrapping of data...
|
||||||
public WMFImageReader(final ImageReaderSpi pProvider) {
|
public final class WMFImageReader extends ImageReaderBase {
|
||||||
super(pProvider);
|
|
||||||
}
|
private SVGImageReader reader = null;
|
||||||
|
|
||||||
protected void resetMembers() {
|
public WMFImageReader(final ImageReaderSpi pProvider) {
|
||||||
if (reader != null) {
|
super(pProvider);
|
||||||
reader.dispose();
|
}
|
||||||
}
|
|
||||||
|
protected void resetMembers() {
|
||||||
reader = null;
|
if (reader != null) {
|
||||||
}
|
reader.dispose();
|
||||||
|
}
|
||||||
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
|
|
||||||
init();
|
reader = null;
|
||||||
|
}
|
||||||
processImageStarted(pIndex);
|
|
||||||
|
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
|
||||||
BufferedImage image = reader.read(pIndex, pParam);
|
init();
|
||||||
if (abortRequested()) {
|
|
||||||
processReadAborted();
|
processImageStarted(pIndex);
|
||||||
return image;
|
|
||||||
}
|
BufferedImage image = reader.read(pIndex, pParam);
|
||||||
processImageProgress(100f);
|
if (abortRequested()) {
|
||||||
|
processReadAborted();
|
||||||
processImageComplete();
|
return image;
|
||||||
|
}
|
||||||
return image;
|
processImageProgress(100f);
|
||||||
}
|
|
||||||
|
processImageComplete();
|
||||||
private synchronized void init() throws IOException {
|
|
||||||
// Need the extra test, to avoid throwing an IOException from the Transcoder
|
return image;
|
||||||
if (imageInput == null) {
|
}
|
||||||
throw new IllegalStateException("input == null");
|
|
||||||
}
|
private void init() throws IOException {
|
||||||
|
// Need the extra test, to avoid throwing an IOException from the Transcoder
|
||||||
if (reader == null) {
|
if (imageInput == null) {
|
||||||
WMFTranscoder transcoder = new WMFTranscoder();
|
throw new IllegalStateException("input == null");
|
||||||
|
}
|
||||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
||||||
Writer writer = new OutputStreamWriter(output, "UTF8");
|
if (reader == null) {
|
||||||
try {
|
WMFTranscoder transcoder = new WMFTranscoder();
|
||||||
TranscoderInput in = new TranscoderInput(IIOUtil.createStreamAdapter(imageInput));
|
ByteArrayOutputStream output = new ByteArrayOutputStream(8192);
|
||||||
TranscoderOutput out = new TranscoderOutput(writer);
|
|
||||||
|
try (Writer writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
|
||||||
// TODO: Transcodinghints?
|
TranscoderInput in = new TranscoderInput(IIOUtil.createStreamAdapter(imageInput));
|
||||||
|
TranscoderOutput out = new TranscoderOutput(writer);
|
||||||
transcoder.transcode(in, out);
|
|
||||||
}
|
// TODO: Transcodinghints?
|
||||||
catch (TranscoderException e) {
|
|
||||||
throw new IIOException(e.getMessage(), e);
|
transcoder.transcode(in, out);
|
||||||
}
|
}
|
||||||
|
catch (TranscoderException e) {
|
||||||
reader = new SVGImageReader(getOriginatingProvider());
|
throw new IIOException(e.getMessage(), e);
|
||||||
reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(output.toByteArray())));
|
}
|
||||||
}
|
|
||||||
}
|
reader = new SVGImageReader(getOriginatingProvider());
|
||||||
|
reader.setInput(new ByteArrayImageInputStream(output.toByteArray()));
|
||||||
@Override
|
}
|
||||||
public ImageReadParam getDefaultReadParam() {
|
}
|
||||||
return new SVGReadParam();
|
|
||||||
}
|
@Override
|
||||||
|
public ImageReadParam getDefaultReadParam() {
|
||||||
public int getWidth(int pIndex) throws IOException {
|
return new SVGReadParam();
|
||||||
init();
|
}
|
||||||
return reader.getWidth(pIndex);
|
|
||||||
}
|
public int getWidth(int pIndex) throws IOException {
|
||||||
|
init();
|
||||||
public int getHeight(int pIndex) throws IOException {
|
return reader.getWidth(pIndex);
|
||||||
init();
|
}
|
||||||
return reader.getHeight(pIndex);
|
|
||||||
}
|
public int getHeight(int pIndex) throws IOException {
|
||||||
|
init();
|
||||||
public Iterator<ImageTypeSpecifier> getImageTypes(final int pImageIndex) throws IOException {
|
return reader.getHeight(pIndex);
|
||||||
init();
|
}
|
||||||
return reader.getImageTypes(pImageIndex);
|
|
||||||
}
|
public Iterator<ImageTypeSpecifier> getImageTypes(final int pImageIndex) throws IOException {
|
||||||
|
init();
|
||||||
}
|
return reader.getImageTypes(pImageIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+72
-5
@@ -43,8 +43,7 @@ import javax.imageio.event.IIOReadWarningListener;
|
|||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.*;
|
||||||
import java.awt.image.ImagingOpException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
@@ -53,7 +52,10 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -192,12 +194,12 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
|||||||
TestData redSquare = new TestData(getClassLoaderResource("/svg/red-square.svg"), dim);
|
TestData redSquare = new TestData(getClassLoaderResource("/svg/red-square.svg"), dim);
|
||||||
reader.setInput(redSquare.getInputStream());
|
reader.setInput(redSquare.getInputStream());
|
||||||
BufferedImage imageRed = reader.read(0, param);
|
BufferedImage imageRed = reader.read(0, param);
|
||||||
assertEquals(0xFF0000, imageRed.getRGB(50, 50) & 0xFFFFFF);
|
assertRGBEquals("Expected all red", 0xFF0000, imageRed.getRGB(50, 50) & 0xFFFFFF, 0);
|
||||||
|
|
||||||
TestData blueSquare = new TestData(getClassLoaderResource("/svg/blue-square.svg"), dim);
|
TestData blueSquare = new TestData(getClassLoaderResource("/svg/blue-square.svg"), dim);
|
||||||
reader.setInput(blueSquare.getInputStream());
|
reader.setInput(blueSquare.getInputStream());
|
||||||
BufferedImage imageBlue = reader.read(0, param);
|
BufferedImage imageBlue = reader.read(0, param);
|
||||||
assertEquals(0x0000FF, imageBlue.getRGB(50, 50) & 0xFFFFFF);
|
assertRGBEquals("Expected all blue", 0x0000FF, imageBlue.getRGB(50, 50) & 0xFFFFFF, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -337,4 +339,69 @@ public class SVGImageReaderTest extends ImageReaderAbstractTest<SVGImageReader>
|
|||||||
reader.dispose();
|
reader.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadWitSourceRenderSize() throws IOException {
|
||||||
|
URL resource = getClassLoaderResource("/svg/circle.svg");
|
||||||
|
|
||||||
|
SVGImageReader reader = createReader();
|
||||||
|
|
||||||
|
TestData data = new TestData(resource, (Dimension) null);
|
||||||
|
try (ImageInputStream stream = data.getInputStream()) {
|
||||||
|
reader.setInput(stream);
|
||||||
|
|
||||||
|
SVGReadParam param = reader.getDefaultReadParam();
|
||||||
|
param.setSourceRenderSize(new Dimension(100, 100));
|
||||||
|
BufferedImage image = reader.read(0, param);
|
||||||
|
|
||||||
|
assertNotNull(image);
|
||||||
|
assertEquals(100, image.getWidth());
|
||||||
|
assertEquals(100, image.getHeight());
|
||||||
|
|
||||||
|
// Some quick samples
|
||||||
|
assertRGBEquals("Expected transparent corner", 0, image.getRGB(0, 0), 0);
|
||||||
|
assertRGBEquals("Expected transparent corner", 0, image.getRGB(99, 0), 0);
|
||||||
|
assertRGBEquals("Expected transparent corner", 0, image.getRGB(0, 99), 0);
|
||||||
|
assertRGBEquals("Expected transparent corner", 0, image.getRGB(99, 99), 0);
|
||||||
|
assertRGBEquals("Expected red center", 0xffff0000, image.getRGB(50, 50), 0);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadWitSourceRenderSizeViewBoxNegativeXY() throws IOException {
|
||||||
|
URL resource = getClassLoaderResource("/svg/Android_robot.svg");
|
||||||
|
|
||||||
|
SVGImageReader reader = createReader();
|
||||||
|
|
||||||
|
TestData data = new TestData(resource, (Dimension) null);
|
||||||
|
try (ImageInputStream stream = data.getInputStream()) {
|
||||||
|
reader.setInput(stream);
|
||||||
|
|
||||||
|
SVGReadParam param = reader.getDefaultReadParam();
|
||||||
|
param.setSourceRenderSize(new Dimension(219, 256)); // Aspect scaled to 256 boxed
|
||||||
|
BufferedImage image = reader.read(0, param);
|
||||||
|
|
||||||
|
assertNotNull(image);
|
||||||
|
assertEquals(219, image.getWidth());
|
||||||
|
assertEquals(256, image.getHeight());
|
||||||
|
|
||||||
|
// Some quick samples
|
||||||
|
assertRGBEquals("Expected transparent corner", 0, image.getRGB(0, 0), 0);
|
||||||
|
assertRGBEquals("Expected transparent corner", 0, image.getRGB(218, 0), 0);
|
||||||
|
assertRGBEquals("Expected transparent corner", 0, image.getRGB(0, 255), 0);
|
||||||
|
assertRGBEquals("Expected transparent corner", 0, image.getRGB(218, 255), 0);
|
||||||
|
assertRGBEquals("Expected green head", 0xffa4c639, image.getRGB(109, 20), 25);
|
||||||
|
assertRGBEquals("Expected green center", 0xffa4c639, image.getRGB(109, 128), 25);
|
||||||
|
assertRGBEquals("Expected green feet", 0xffa4c639, image.getRGB(80, 246), 25);
|
||||||
|
assertRGBEquals("Expected green feet", 0xffa4c639, image.getRGB(130, 246), 25);
|
||||||
|
assertRGBEquals("Expected white edge", 0xffffffff, image.getRGB(0, 128), 0);
|
||||||
|
assertRGBEquals("Expected white edge", 0xffffffff, image.getRGB(218, 128), 0);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 50 50" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xml:space="preserve"
|
||||||
|
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||||
|
<circle cx="25" cy="25" r="25" fill="red"/></svg>
|
||||||
|
After Width: | Height: | Size: 436 B |
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-bmp</artifactId>
|
<artifactId>imageio-bmp</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: BMP plugin</name>
|
||||||
|
|||||||
+16
-9
@@ -39,7 +39,11 @@ import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
|||||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||||
import com.twelvemonkeys.xml.XMLSerializer;
|
import com.twelvemonkeys.xml.XMLSerializer;
|
||||||
|
|
||||||
import javax.imageio.*;
|
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.IIOReadUpdateListener;
|
||||||
import javax.imageio.event.IIOReadWarningListener;
|
import javax.imageio.event.IIOReadWarningListener;
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
@@ -47,7 +51,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
|
|||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -77,7 +81,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
|||||||
super(new BMPImageReaderSpi());
|
super(new BMPImageReaderSpi());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BMPImageReader(final ImageReaderSpi pProvider) {
|
BMPImageReader(final ImageReaderSpi pProvider) {
|
||||||
super(pProvider);
|
super(pProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,14 +362,18 @@ public final class BMPImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
processImageStarted(imageIndex);
|
processImageStarted(imageIndex);
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
switch (header.getBitCount()) {
|
int bitCount = header.getBitCount();
|
||||||
|
switch (bitCount) {
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
case 4:
|
case 4:
|
||||||
case 8:
|
case 8:
|
||||||
case 24:
|
case 24:
|
||||||
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||||
readRowByte(input, height, srcRegion, xSub, ySub, rowDataByte, destRaster, clippedRow, y);
|
int bitsPerSample = bitCount == 24 ? 8 : bitCount;
|
||||||
|
int samplesPerPixel = bitCount == 24 ? 3 : 1;
|
||||||
|
|
||||||
|
readRowByte(input, height, srcRegion, xSub, ySub, bitsPerSample, samplesPerPixel, rowDataByte, destRaster, clippedRow, y);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 16:
|
case 16:
|
||||||
@@ -379,7 +387,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new AssertionError("Unsupported pixel depth: " + header.getBitCount());
|
throw new AssertionError("Unsupported pixel depth: " + bitCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
processImageProgress(100f * y / height);
|
processImageProgress(100f * y / height);
|
||||||
@@ -476,6 +484,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readRowByte(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
private void readRowByte(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||||
|
int bitsPerSample, int samplesPerPixel,
|
||||||
final byte[] rowDataByte, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
final byte[] rowDataByte, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
|
||||||
// Flip into position?
|
// Flip into position?
|
||||||
int srcY = !header.topDown ? height - 1 - y : y;
|
int srcY = !header.topDown ? height - 1 - y : y;
|
||||||
@@ -492,9 +501,7 @@ public final class BMPImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
// Subsample horizontal
|
// Subsample horizontal
|
||||||
if (xSub != 1) {
|
if (xSub != 1) {
|
||||||
for (int x = 0; x < srcRegion.width / xSub; x++) {
|
IIOUtil.subsampleRow(rowDataByte, srcRegion.x, srcRegion.width, rowDataByte, 0, samplesPerPixel, bitsPerSample, xSub);
|
||||||
rowDataByte[srcRegion.x + x] = rowDataByte[srcRegion.x + x * xSub];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destChannel.setDataElements(0, dstY, srcChannel);
|
destChannel.setDataElements(0, dstY, srcChannel);
|
||||||
|
|||||||
+1
-1
@@ -116,7 +116,7 @@ public final class BMPImageReaderSpi extends ImageReaderSpiBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageReader createReaderInstance(final Object pExtension) throws IOException {
|
public ImageReader createReaderInstance(final Object pExtension) {
|
||||||
return new BMPImageReader(this);
|
return new BMPImageReader(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-clippath</artifactId>
|
<artifactId>imageio-clippath</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
<name>TwelveMonkeys :: ImageIO :: Photoshop Path Support</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-core</artifactId>
|
<artifactId>imageio-core</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
<name>TwelveMonkeys :: ImageIO :: Core</name>
|
||||||
|
|||||||
+20
-20
@@ -54,8 +54,8 @@ public final class DiscreteAlphaIndexColorModel extends ColorModel {
|
|||||||
// Our IndexColorModel delegate
|
// Our IndexColorModel delegate
|
||||||
private final IndexColorModel icm;
|
private final IndexColorModel icm;
|
||||||
|
|
||||||
|
private final int extraSamples;
|
||||||
private final int samples;
|
private final int samples;
|
||||||
private final boolean hasAlpha;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code DiscreteAlphaIndexColorModel}, delegating color map look-ups
|
* Creates a {@code DiscreteAlphaIndexColorModel}, delegating color map look-ups
|
||||||
@@ -86,33 +86,33 @@ public final class DiscreteAlphaIndexColorModel extends ColorModel {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.icm = icm;
|
this.icm = icm;
|
||||||
|
this.extraSamples = extraSamples;
|
||||||
this.samples = 1 + extraSamples;
|
this.samples = 1 + extraSamples;
|
||||||
this.hasAlpha = hasAlpha;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getNumComponents() {
|
public int getNumComponents() {
|
||||||
return samples;
|
return getNumColorComponents() + extraSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getRed(final int pixel) {
|
public int getRed(final int pixel) {
|
||||||
return icm.getRed(pixel);
|
return icm.getRed(pixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getGreen(final int pixel) {
|
public int getGreen(final int pixel) {
|
||||||
return icm.getGreen(pixel);
|
return icm.getGreen(pixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getBlue(final int pixel) {
|
public int getBlue(final int pixel) {
|
||||||
return icm.getBlue(pixel);
|
return icm.getBlue(pixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getAlpha(final int pixel) {
|
public int getAlpha(final int pixel) {
|
||||||
return hasAlpha ? (int) ((((float) pixel) / ((1 << getComponentSize(3))-1)) * 255.0f + 0.5f) : 0xff;
|
return hasAlpha() ? (int) ((((float) pixel) / ((1 << getComponentSize(3)) - 1)) * 255.0f + 0.5f) : 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getSample(final Object inData, final int index) {
|
private int getSample(final Object inData, final int index) {
|
||||||
@@ -120,15 +120,15 @@ public final class DiscreteAlphaIndexColorModel extends ColorModel {
|
|||||||
|
|
||||||
switch (transferType) {
|
switch (transferType) {
|
||||||
case DataBuffer.TYPE_BYTE:
|
case DataBuffer.TYPE_BYTE:
|
||||||
byte bdata[] = (byte[]) inData;
|
byte[] bdata = (byte[]) inData;
|
||||||
pixel = bdata[index] & 0xff;
|
pixel = bdata[index] & 0xff;
|
||||||
break;
|
break;
|
||||||
case DataBuffer.TYPE_USHORT:
|
case DataBuffer.TYPE_USHORT:
|
||||||
short sdata[] = (short[]) inData;
|
short[] sdata = (short[]) inData;
|
||||||
pixel = sdata[index] & 0xffff;
|
pixel = sdata[index] & 0xffff;
|
||||||
break;
|
break;
|
||||||
case DataBuffer.TYPE_INT:
|
case DataBuffer.TYPE_INT:
|
||||||
int idata[] = (int[]) inData;
|
int[] idata = (int[]) inData;
|
||||||
pixel = idata[index];
|
pixel = idata[index];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -139,27 +139,27 @@ public final class DiscreteAlphaIndexColorModel extends ColorModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getRed(final Object inData) {
|
public int getRed(final Object inData) {
|
||||||
return getRed(getSample(inData, 0));
|
return getRed(getSample(inData, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getGreen(final Object inData) {
|
public int getGreen(final Object inData) {
|
||||||
return getGreen(getSample(inData, 0));
|
return getGreen(getSample(inData, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getBlue(final Object inData) {
|
public int getBlue(final Object inData) {
|
||||||
return getBlue(getSample(inData, 0));
|
return getBlue(getSample(inData, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getAlpha(final Object inData) {
|
public int getAlpha(final Object inData) {
|
||||||
return hasAlpha ? getAlpha(getSample(inData, 1)) : 0xff;
|
return hasAlpha() ? getAlpha(getSample(inData, 1)) : 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final SampleModel createCompatibleSampleModel(final int w, final int h) {
|
public SampleModel createCompatibleSampleModel(final int w, final int h) {
|
||||||
return new PixelInterleavedSampleModel(transferType, w, h, samples, w * samples, createOffsets(samples));
|
return new PixelInterleavedSampleModel(transferType, w, h, samples, w * samples, createOffsets(samples));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,17 +174,17 @@ public final class DiscreteAlphaIndexColorModel extends ColorModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isCompatibleSampleModel(final SampleModel sm) {
|
public boolean isCompatibleSampleModel(final SampleModel sm) {
|
||||||
return sm instanceof PixelInterleavedSampleModel && sm.getNumBands() == samples;
|
return sm instanceof PixelInterleavedSampleModel && sm.getNumBands() == samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final WritableRaster createCompatibleWritableRaster(final int w, final int h) {
|
public WritableRaster createCompatibleWritableRaster(final int w, final int h) {
|
||||||
return Raster.createWritableRaster(createCompatibleSampleModel(w, h), new Point(0, 0));
|
return Raster.createWritableRaster(createCompatibleSampleModel(w, h), new Point(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isCompatibleRaster(final Raster raster) {
|
public boolean isCompatibleRaster(final Raster raster) {
|
||||||
int size = raster.getSampleModel().getSampleSize(0);
|
int size = raster.getSampleModel().getSampleSize(0);
|
||||||
return ((raster.getTransferType() == transferType) &&
|
return ((raster.getTransferType() == transferType) &&
|
||||||
(raster.getNumBands() == samples) && ((1 << size) >= icm.getMapSize()));
|
(raster.getNumBands() == samples) && ((1 << size) >= icm.getMapSize()));
|
||||||
|
|||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||||
|
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@code ImageInputStream} that adapts an {@code InputSteam},
|
||||||
|
* by reading directly from the stream without and form of caching or buffering.
|
||||||
|
* <p>
|
||||||
|
* Note: This is <em>not</em> a general-purpose {@code ImageInputStream}, and is designed for reading large chunks,
|
||||||
|
* typically of pixel data, from an {@code InputStream}.
|
||||||
|
* It does <em>not</em> support backwards seeking, or reading bits.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public final class DirectImageInputStream extends ImageInputStreamImpl {
|
||||||
|
private final InputStream stream;
|
||||||
|
private final long length;
|
||||||
|
|
||||||
|
public DirectImageInputStream(final InputStream stream) {
|
||||||
|
this(stream, -1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectImageInputStream(final InputStream stream, long length) {
|
||||||
|
this.stream = notNull(stream, "stream");
|
||||||
|
this.length = isTrue(length >= 0L || length == -1L, length, "negative length: %d");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
bitOffset = 0;
|
||||||
|
streamPos++;
|
||||||
|
return stream.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(final byte[] bytes, int off, int len) throws IOException {
|
||||||
|
bitOffset = 0;
|
||||||
|
|
||||||
|
int read = stream.read(bytes, off, len);
|
||||||
|
if (read > 0) {
|
||||||
|
streamPos += read;
|
||||||
|
}
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void seek(long pos) throws IOException {
|
||||||
|
checkClosed();
|
||||||
|
|
||||||
|
if (pos < streamPos) {
|
||||||
|
// Handle as if flushedPos == streamPos at any time
|
||||||
|
throw new IndexOutOfBoundsException("pos < flushedPos");
|
||||||
|
}
|
||||||
|
|
||||||
|
bitOffset = 0;
|
||||||
|
|
||||||
|
while (streamPos < pos) {
|
||||||
|
long skipped = stream.skip(pos - streamPos);
|
||||||
|
|
||||||
|
if (skipped <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
streamPos += skipped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getFlushedPosition() {
|
||||||
|
// Handle as if flushedPos == streamPos at any time
|
||||||
|
return streamPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long length() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
|
@Override
|
||||||
|
public int readBit() throws IOException {
|
||||||
|
throw new UnsupportedOperationException("Bit reading not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
|
@Override
|
||||||
|
public long readBits(int numBits) throws IOException {
|
||||||
|
throw new UnsupportedOperationException("Bit reading not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
// We could seek to EOF here, but the usual case
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -81,7 +81,7 @@ class IIOInputStreamAdapter extends InputStream {
|
|||||||
|
|
||||||
private IIOInputStreamAdapter(ImageInputStream pInput, long pLength, boolean pHasLength) {
|
private IIOInputStreamAdapter(ImageInputStream pInput, long pLength, boolean pHasLength) {
|
||||||
Validate.notNull(pInput, "stream");
|
Validate.notNull(pInput, "stream");
|
||||||
Validate.isTrue(!pHasLength || pLength >= 0, pLength, "length < 0: %f");
|
Validate.isTrue(!pHasLength || pLength >= 0, pLength, "length < 0: %d");
|
||||||
|
|
||||||
input = pInput;
|
input = pInput;
|
||||||
left = pLength;
|
left = pLength;
|
||||||
|
|||||||
+22
-1
@@ -37,7 +37,9 @@ import java.awt.image.*;
|
|||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class DiscreteAlphaIndexColorModelTest {
|
public class DiscreteAlphaIndexColorModelTest {
|
||||||
|
|
||||||
@@ -204,6 +206,25 @@ public class DiscreteAlphaIndexColorModelTest {
|
|||||||
assertThat(raster.getTransferType(), CoreMatchers.equalTo(DataBuffer.TYPE_BYTE));
|
assertThat(raster.getTransferType(), CoreMatchers.equalTo(DataBuffer.TYPE_BYTE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNumComponents() {
|
||||||
|
int[] colors = createIntLut(1 << 8);
|
||||||
|
IndexColorModel icm = new IndexColorModel(8, colors.length, colors, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||||
|
|
||||||
|
ColorModel colorModelDiscreteAlpha = new DiscreteAlphaIndexColorModel(icm, 1, true);
|
||||||
|
ColorModel colorModelDiscreteAlphaExtra = new DiscreteAlphaIndexColorModel(icm, 2, true);
|
||||||
|
ColorModel colorModelNoAlphaExtra = new DiscreteAlphaIndexColorModel(icm, 42, false);
|
||||||
|
|
||||||
|
assertEquals(3, colorModelDiscreteAlpha.getNumColorComponents());
|
||||||
|
assertEquals(4, colorModelDiscreteAlpha.getNumComponents());
|
||||||
|
|
||||||
|
assertEquals(3, colorModelDiscreteAlphaExtra.getNumColorComponents());
|
||||||
|
assertEquals(5, colorModelDiscreteAlphaExtra.getNumComponents()); // Questionable
|
||||||
|
|
||||||
|
assertEquals(3, colorModelNoAlphaExtra.getNumColorComponents());
|
||||||
|
assertEquals(45, colorModelNoAlphaExtra.getNumComponents()); // Questionable
|
||||||
|
}
|
||||||
|
|
||||||
private static int[] createIntLut(final int count) {
|
private static int[] createIntLut(final int count) {
|
||||||
int[] lut = new int[count];
|
int[] lut = new int[count];
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -53,6 +53,7 @@ import static org.mockito.Mockito.*;
|
|||||||
* @author last modified by $Author: haraldk$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: BufferedImageInputStreamTest.java,v 1.0 Jun 30, 2008 3:07:42 PM haraldk Exp$
|
* @version $Id: BufferedImageInputStreamTest.java,v 1.0 Jun 30, 2008 3:07:42 PM haraldk Exp$
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class BufferedImageInputStreamTest {
|
public class BufferedImageInputStreamTest {
|
||||||
private final Random random = new Random(3450972865211L);
|
private final Random random = new Random(3450972865211L);
|
||||||
|
|
||||||
@@ -433,7 +434,7 @@ public class BufferedImageInputStreamTest {
|
|||||||
* and {@code pFirstOffset == pSecondOffset}.
|
* and {@code pFirstOffset == pSecondOffset}.
|
||||||
* Otherwise {@code false}.
|
* Otherwise {@code false}.
|
||||||
*/
|
*/
|
||||||
static boolean rangeEquals(byte[] pFirst, int pFirstOffset, byte[] pSecond, int pSecondOffset, int pLength) {
|
public static boolean rangeEquals(byte[] pFirst, int pFirstOffset, byte[] pSecond, int pSecondOffset, int pLength) {
|
||||||
if (pFirst == pSecond && pFirstOffset == pSecondOffset) {
|
if (pFirst == pSecond && pFirstOffset == pSecondOffset) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+380
@@ -0,0 +1,380 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.function.ThrowingRunnable;
|
||||||
|
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.stream.BufferedImageInputStreamTest.rangeEquals;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NonSeekableImageInputStreamTest
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: NonSeekableImageInputStreamTest.java,v 1.0 Apr 21, 2009 10:58:48 AM haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class DirectImageInputStreamTest {
|
||||||
|
private final Random random = new Random(170984354357234566L);
|
||||||
|
|
||||||
|
private InputStream randomData(byte[] data) {
|
||||||
|
random.nextBytes(data);
|
||||||
|
|
||||||
|
return new ByteArrayInputStream(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreate() throws IOException {
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(new ByteArrayInputStream(new byte[0]), 0)) {
|
||||||
|
assertEquals("Data length should be same as stream length", 0, stream.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateNullFile() throws IOException {
|
||||||
|
try (@SuppressWarnings("unused") DirectImageInputStream stream = new DirectImageInputStream(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("stream"));
|
||||||
|
assertTrue("Exception message does not contain null", message.contains("null"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead() throws IOException {
|
||||||
|
byte[] data = new byte[1024 * 1024];
|
||||||
|
InputStream input = randomData(data);
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
for (byte value : data) {
|
||||||
|
assertEquals("Wrong data read", value & 0xff, stream.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadArray() throws IOException {
|
||||||
|
byte[] data = new byte[1024 * 10];
|
||||||
|
InputStream input = randomData(data);
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
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];
|
||||||
|
InputStream input = randomData(data);
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
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[24 * 18];
|
||||||
|
InputStream input = randomData(data);
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
byte[] result = new byte[9];
|
||||||
|
|
||||||
|
for (int i = 0; i < data.length / (2 * result.length); i++) {
|
||||||
|
long newPos = i * 2 * 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
@Ignore("Bit reading requires backwards seek or buffer...")
|
||||||
|
@Test
|
||||||
|
public void testReadBitRandom() throws IOException {
|
||||||
|
byte[] bytes = new byte[8];
|
||||||
|
InputStream input = randomData(bytes);
|
||||||
|
long value = ByteBuffer.wrap(bytes).getLong();
|
||||||
|
|
||||||
|
// Create stream
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
for (int i = 1; i <= 64; i++) {
|
||||||
|
assertEquals(String.format("bit %d differ", i), (value << (i - 1L)) >>> 63L, stream.readBit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
@Ignore("Bit reading requires backwards seek or buffer...")
|
||||||
|
@Test
|
||||||
|
public void testReadBitsRandom() throws IOException {
|
||||||
|
byte[] bytes = new byte[8];
|
||||||
|
InputStream input = randomData(bytes);
|
||||||
|
long value = ByteBuffer.wrap(bytes).getLong();
|
||||||
|
|
||||||
|
// Create stream
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
@Ignore("Bit reading requires backwards seek or buffer...")
|
||||||
|
@Test
|
||||||
|
public void testReadBitsRandomOffset() throws IOException {
|
||||||
|
byte[] bytes = new byte[8];
|
||||||
|
InputStream input = randomData(bytes);
|
||||||
|
long value = ByteBuffer.wrap(bytes).getLong();
|
||||||
|
|
||||||
|
// Create stream
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
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 * 2L % 8, stream.getBitOffset());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadShort() throws IOException {
|
||||||
|
byte[] bytes = new byte[31];
|
||||||
|
InputStream input = randomData(bytes);
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(new ByteArrayInputStream(bytes))) {
|
||||||
|
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[31];
|
||||||
|
InputStream input = randomData(bytes);
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
// Create stream
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(new ByteArrayInputStream(bytes))) {
|
||||||
|
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
|
||||||
|
InputStream input = randomData(bytes);
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(new ByteArrayInputStream(bytes))) {
|
||||||
|
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];
|
||||||
|
InputStream input = randomData(bytes);
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(input)) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try (DirectImageInputStream stream = new DirectImageInputStream(new ByteArrayInputStream(bytes))) {
|
||||||
|
for (byte value : bytes) {
|
||||||
|
assertEquals(value, stream.readByte());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClose() throws IOException {
|
||||||
|
// Create wrapper stream
|
||||||
|
InputStream input = mock(InputStream.class);
|
||||||
|
ImageInputStream stream = new DirectImageInputStream(input);
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
verify(input, only()).close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-hdr</artifactId>
|
<artifactId>imageio-hdr</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: HDR plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-icns</artifactId>
|
<artifactId>imageio-icns</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: ICNS plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-iff</artifactId>
|
<artifactId>imageio-iff</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: IFF plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-jpeg-jai-interop</artifactId>
|
<artifactId>imageio-jpeg-jai-interop</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name>
|
<name>TwelveMonkeys :: ImageIO :: JPEG/JAI TIFF Interop</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-jpeg-jep262-interop</artifactId>
|
<artifactId>imageio-jpeg-jep262-interop</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name>
|
<name>TwelveMonkeys :: ImageIO :: JPEG/JEP-262 Interop</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-jpeg</artifactId>
|
<artifactId>imageio-jpeg</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: JPEG plugin</name>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>imageio-metadata</artifactId>
|
<artifactId>imageio-metadata</artifactId>
|
||||||
|
|||||||
+4
@@ -173,10 +173,14 @@ public final class TIFFEntry extends AbstractEntry {
|
|||||||
return "TileByteCounts";
|
return "TileByteCounts";
|
||||||
case TIFF.TAG_COPYRIGHT:
|
case TIFF.TAG_COPYRIGHT:
|
||||||
return "Copyright";
|
return "Copyright";
|
||||||
|
case TIFF.TAG_YCBCR_COEFFICIENTS:
|
||||||
|
return "YCbCrCoefficients";
|
||||||
case TIFF.TAG_YCBCR_SUB_SAMPLING:
|
case TIFF.TAG_YCBCR_SUB_SAMPLING:
|
||||||
return "YCbCrSubSampling";
|
return "YCbCrSubSampling";
|
||||||
case TIFF.TAG_YCBCR_POSITIONING:
|
case TIFF.TAG_YCBCR_POSITIONING:
|
||||||
return "YCbCrPositioning";
|
return "YCbCrPositioning";
|
||||||
|
case TIFF.TAG_REFERENCE_BLACK_WHITE:
|
||||||
|
return "ReferenceBlackWhite";
|
||||||
case TIFF.TAG_COLOR_MAP:
|
case TIFF.TAG_COLOR_MAP:
|
||||||
return "ColorMap";
|
return "ColorMap";
|
||||||
case TIFF.TAG_INK_SET:
|
case TIFF.TAG_INK_SET:
|
||||||
|
|||||||
+6
-6
@@ -1,16 +1,16 @@
|
|||||||
package com.twelvemonkeys.imageio.metadata.tiff;
|
package com.twelvemonkeys.imageio.metadata.tiff;
|
||||||
|
|
||||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import org.junit.Test;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HalfTest.
|
* HalfTest.
|
||||||
@@ -97,7 +97,7 @@ public class HalfTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = NullPointerException.class)
|
@Test(expected = NullPointerException.class)
|
||||||
public void testParseHAlfNull() {
|
public void testParseHalfNull() {
|
||||||
Half.parseHalf(null);
|
Half.parseHalf(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-pcx</artifactId>
|
<artifactId>imageio-pcx</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
|
||||||
|
|||||||
+4
-6
@@ -213,14 +213,12 @@ public final class PCXImageReader extends ImageReaderBase {
|
|||||||
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
switch (header.getBitsPerPixel()) {
|
if (header.getBitsPerPixel() != 1) {
|
||||||
case 1:
|
throw new AssertionError();
|
||||||
readRowByte(input, srcRegion, xSub, ySub, planeData, 0, planeWidth * header.getChannels(), destRaster, clippedRow, y);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readRowByte(input, srcRegion, xSub, ySub, planeData, 0, planeWidth * header.getChannels(), destRaster, clippedRow, y);
|
||||||
|
|
||||||
int pixelPos = 0;
|
int pixelPos = 0;
|
||||||
for (int planePos = 0; planePos < planeWidth; planePos++) {
|
for (int planePos = 0; planePos < planeWidth; planePos++) {
|
||||||
BitRotator.bitRotateCW(planeData, planePos, planeWidth, rowDataByte, pixelPos, 1);
|
BitRotator.bitRotateCW(planeData, planePos, planeWidth, rowDataByte, pixelPos, 1);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-pdf</artifactId>
|
<artifactId>imageio-pdf</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: PDF plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-pict</artifactId>
|
<artifactId>imageio-pict</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: PICT plugin</name>
|
||||||
|
|||||||
+10
-9
@@ -30,19 +30,20 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.pict;
|
package com.twelvemonkeys.imageio.plugins.pict;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
|
||||||
|
|
||||||
import javax.imageio.ImageReader;
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PICTImageReaderSpi
|
* PICTImageReaderSpi
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: PICTImageReaderSpi.java,v 1.0 28.feb.2006 19:21:05 haku Exp$
|
* @version $Id: PICTImageReaderSpi.java,v 1.0 28.feb.2006 19:21:05 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public final class PICTImageReaderSpi extends ImageReaderSpiBase {
|
public final class PICTImageReaderSpi extends ImageReaderSpiBase {
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
|
|||||||
|
|
||||||
ImageInputStream stream = (ImageInputStream) pSource;
|
ImageInputStream stream = (ImageInputStream) pSource;
|
||||||
|
|
||||||
// PICT format don't have good magic and our method often gives false positives,
|
// PICT format doesn't have good magic and our method often gives false positives,
|
||||||
// We'll check for other known formats (BMP, GIF, JPEG, PNG, PSD, TIFF) first
|
// We'll check for other known formats (BMP, GIF, JPEG, PNG, PSD, TIFF) first
|
||||||
if (isOtherFormat(stream)) {
|
if (isOtherFormat(stream)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -76,7 +77,7 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We need to reset AND set mark again, to make sure the reset call in
|
// We need to reset AND set mark again, to make sure the reset call in
|
||||||
// the finally block will not consume existing marks
|
// the finally-block will not consume existing marks
|
||||||
stream.reset();
|
stream.reset();
|
||||||
stream.mark();
|
stream.mark();
|
||||||
|
|
||||||
@@ -149,8 +150,8 @@ public final class PICTImageReaderSpi extends ImageReaderSpiBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void skipNullHeader(final ImageInputStream pStream) throws IOException {
|
static void skipNullHeader(final ImageInputStream pStream) throws IOException {
|
||||||
// NOTE: Only skip if FILE FORMAT, not needed for Mac OS DnD
|
// NOTE: Only skip if FILE FORMAT, not needed for macOS DnD
|
||||||
// Spec says "platform dependent", may not be all nulls..
|
// Spec says "platform dependent", may not be all nulls...
|
||||||
pStream.skipBytes(PICT.PICT_NULL_HEADER_SIZE);
|
pStream.skipBytes(PICT.PICT_NULL_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-pnm</artifactId>
|
<artifactId>imageio-pnm</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: PNM plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: PNM plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-psd</artifactId>
|
<artifactId>imageio-psd</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: PSD plugin</name>
|
||||||
|
|||||||
+266
@@ -0,0 +1,266 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 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.plugins.psd;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.ReadableByteChannel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A decoder for data converted using "horizontal differencing predictor".
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: HorizontalDeDifferencingStream.java,v 1.0 11.03.13 14:20 haraldk Exp$
|
||||||
|
*/
|
||||||
|
final class HorizontalDeDifferencingStream extends InputStream {
|
||||||
|
/// TODO: Create shared version with TIFF, or see if we can avoid some duplication?
|
||||||
|
// See TIFF 6.0 Specification, Section 14: "Differencing Predictor", page 64.
|
||||||
|
|
||||||
|
private final int columns;
|
||||||
|
// NOTE: PlanarConfiguration == 2 may be treated as samplesPerPixel == 1
|
||||||
|
private final int samplesPerPixel;
|
||||||
|
private final int bitsPerSample;
|
||||||
|
|
||||||
|
private final ReadableByteChannel channel;
|
||||||
|
private final ByteBuffer buffer;
|
||||||
|
|
||||||
|
public HorizontalDeDifferencingStream(final InputStream stream, final int columns, final int samplesPerPixel, final int bitsPerSample, final ByteOrder byteOrder) {
|
||||||
|
this.columns = Validate.isTrue(columns > 0, columns, "width must be greater than 0");
|
||||||
|
this.samplesPerPixel = Validate.isTrue(bitsPerSample >= 8 || samplesPerPixel == 1, samplesPerPixel, "Unsupported samples per pixel for < 8 bit samples: %s");
|
||||||
|
this.bitsPerSample = Validate.isTrue(isValidBPS(bitsPerSample), bitsPerSample, "Unsupported bits per sample value: %s");
|
||||||
|
|
||||||
|
channel = Channels.newChannel(Validate.notNull(stream, "stream"));
|
||||||
|
|
||||||
|
buffer = ByteBuffer.allocate((columns * samplesPerPixel * bitsPerSample + 7) / 8).order(byteOrder);
|
||||||
|
buffer.flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isValidBPS(final int bitsPerSample) {
|
||||||
|
switch (bitsPerSample) {
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
case 8:
|
||||||
|
case 16:
|
||||||
|
case 32:
|
||||||
|
case 64:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("StatementWithEmptyBody")
|
||||||
|
private boolean fetch() throws IOException {
|
||||||
|
buffer.clear();
|
||||||
|
|
||||||
|
// This *SHOULD* read an entire row of pixels (or nothing at all) into the buffer,
|
||||||
|
// otherwise we will throw EOFException below
|
||||||
|
while (channel.read(buffer) > 0);
|
||||||
|
|
||||||
|
if (buffer.position() > 0) {
|
||||||
|
if (buffer.hasRemaining()) {
|
||||||
|
throw new EOFException("Unexpected end of stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeRow();
|
||||||
|
buffer.flip();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer.position(buffer.capacity());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decodeRow() {
|
||||||
|
// Un-apply horizontal predictor
|
||||||
|
byte original;
|
||||||
|
int sample = 0;
|
||||||
|
byte temp;
|
||||||
|
|
||||||
|
// Optimization:
|
||||||
|
// Access array directly for <= 8 bits per sample, as buffer does extra index bounds check for every
|
||||||
|
// put/get operation... (Measures to about 100 ms difference for 4000 x 3000 image)
|
||||||
|
final byte[] array = buffer.array();
|
||||||
|
|
||||||
|
switch (bitsPerSample) {
|
||||||
|
case 1:
|
||||||
|
for (int b = 0; b < (columns + 7) / 8; b++) {
|
||||||
|
original = array[b];
|
||||||
|
sample += (original >> 7) & 0x1;
|
||||||
|
temp = (byte) ((sample << 7) & 0x80);
|
||||||
|
sample += (original >> 6) & 0x1;
|
||||||
|
temp |= (byte) ((sample << 6) & 0x40);
|
||||||
|
sample += (original >> 5) & 0x1;
|
||||||
|
temp |= (byte) ((sample << 5) & 0x20);
|
||||||
|
sample += (original >> 4) & 0x1;
|
||||||
|
temp |= (byte) ((sample << 4) & 0x10);
|
||||||
|
sample += (original >> 3) & 0x1;
|
||||||
|
temp |= (byte) ((sample << 3) & 0x08);
|
||||||
|
sample += (original >> 2) & 0x1;
|
||||||
|
temp |= (byte) ((sample << 2) & 0x04);
|
||||||
|
sample += (original >> 1) & 0x1;
|
||||||
|
temp |= (byte) ((sample << 1) & 0x02);
|
||||||
|
sample += original & 0x1;
|
||||||
|
array[b] = (byte) (temp | sample & 0x1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
for (int b = 0; b < (columns + 3) / 4; b++) {
|
||||||
|
original = array[b];
|
||||||
|
sample += (original >> 6) & 0x3;
|
||||||
|
temp = (byte) ((sample << 6) & 0xc0);
|
||||||
|
sample += (original >> 4) & 0x3;
|
||||||
|
temp |= (byte) ((sample << 4) & 0x30);
|
||||||
|
sample += (original >> 2) & 0x3;
|
||||||
|
temp |= (byte) ((sample << 2) & 0x0c);
|
||||||
|
sample += original & 0x3;
|
||||||
|
array[b] = (byte) (temp | sample & 0x3);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
for (int b = 0; b < (columns + 1) / 2; b++) {
|
||||||
|
original = array[b];
|
||||||
|
sample += (original >> 4) & 0xf;
|
||||||
|
temp = (byte) ((sample << 4) & 0xf0);
|
||||||
|
sample += original & 0x0f;
|
||||||
|
array[b] = (byte) (temp | sample & 0xf);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
for (int x = 1; x < columns; x++) {
|
||||||
|
for (int b = 0; b < samplesPerPixel; b++) {
|
||||||
|
int off = x * samplesPerPixel + b;
|
||||||
|
array[off] = (byte) (array[off - samplesPerPixel] + array[off]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 16:
|
||||||
|
for (int x = 1; x < columns; x++) {
|
||||||
|
for (int b = 0; b < samplesPerPixel; b++) {
|
||||||
|
int off = x * samplesPerPixel + b;
|
||||||
|
buffer.putShort(2 * off, (short) (buffer.getShort(2 * (off - samplesPerPixel)) + buffer.getShort(2 * off)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 32:
|
||||||
|
for (int x = 1; x < columns; x++) {
|
||||||
|
for (int b = 0; b < samplesPerPixel; b++) {
|
||||||
|
int off = x * samplesPerPixel + b;
|
||||||
|
buffer.putInt(4 * off, buffer.getInt(4 * (off - samplesPerPixel)) + buffer.getInt(4 * off));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 64:
|
||||||
|
for (int x = 1; x < columns; x++) {
|
||||||
|
for (int b = 0; b < samplesPerPixel; b++) {
|
||||||
|
int off = x * samplesPerPixel + b;
|
||||||
|
buffer.putLong(8 * off, buffer.getLong(8 * (off - samplesPerPixel)) + buffer.getLong(8 * off));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new AssertionError(String.format("Unsupported bits per sample value: %d", bitsPerSample));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
if (!buffer.hasRemaining()) {
|
||||||
|
if (!fetch()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.get() & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
if (!buffer.hasRemaining()) {
|
||||||
|
if (!fetch()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int read = Math.min(buffer.remaining(), len);
|
||||||
|
buffer.get(b, off, read);
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
if (n < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buffer.hasRemaining()) {
|
||||||
|
if (!fetch()) {
|
||||||
|
return 0; // SIC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int skipped = (int) Math.min(buffer.remaining(), n);
|
||||||
|
buffer.position(buffer.position() + skipped);
|
||||||
|
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
try {
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (channel.isOpen()) {
|
||||||
|
channel.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
-8
@@ -31,10 +31,10 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.psd;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PSDDirectoryResource
|
* PSDDirectoryResource
|
||||||
@@ -69,15 +69,24 @@ abstract class PSDDirectoryResource extends PSDImageResource {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder builder = toStringBuilder();
|
StringBuilder builder = toStringBuilder();
|
||||||
|
|
||||||
int length = Math.min(256, data.length);
|
if (directory != null) {
|
||||||
String data = StringUtil.decode(this.data, 0, length, "UTF-8").replace('\n', ' ').replaceAll("\\s+", " ");
|
builder.append(", ").append(directory);
|
||||||
builder.append(", data: \"").append(data);
|
builder.append("]");
|
||||||
|
|
||||||
if (length < this.data.length) {
|
|
||||||
builder.append("...");
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
int length = Math.min(256, data.length);
|
||||||
|
String data = new String(this.data, 0, length, StandardCharsets.US_ASCII)
|
||||||
|
.replace('\uFFFD', '.')
|
||||||
|
.replaceAll("\\s+", " ")
|
||||||
|
.replaceAll("[^\\p{Print}]", ".");
|
||||||
|
builder.append(", data: \"").append(data);
|
||||||
|
|
||||||
builder.append("\"]");
|
if (length < this.data.length) {
|
||||||
|
builder.append("...");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append("\"]");
|
||||||
|
}
|
||||||
|
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
-15
@@ -85,19 +85,4 @@ final class PSDEXIF1Data extends PSDDirectoryResource {
|
|||||||
output.writeInt((int) (afterExif - beforeExif));
|
output.writeInt((int) (afterExif - beforeExif));
|
||||||
output.seek(afterExif);
|
output.seek(afterExif);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
Directory directory = getDirectory();
|
|
||||||
|
|
||||||
if (directory == null) {
|
|
||||||
return super.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder builder = toStringBuilder();
|
|
||||||
builder.append(", ").append(directory);
|
|
||||||
builder.append("]");
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
-15
@@ -53,19 +53,4 @@ final class PSDIPTCData extends PSDDirectoryResource {
|
|||||||
Directory parseDirectory() throws IOException {
|
Directory parseDirectory() throws IOException {
|
||||||
return new IPTCReader().read(new ByteArrayImageInputStream(data));
|
return new IPTCReader().read(new ByteArrayImageInputStream(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
Directory directory = getDirectory();
|
|
||||||
|
|
||||||
if (directory == null) {
|
|
||||||
return super.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder builder = toStringBuilder();
|
|
||||||
builder.append(", ").append(directory);
|
|
||||||
builder.append("]");
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+297
-249
@@ -43,15 +43,19 @@ import javax.imageio.metadata.IIOMetadata;
|
|||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.color.ICC_ColorSpace;
|
|
||||||
import java.awt.color.ICC_Profile;
|
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.Set;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.plugins.psd.PSDUtil.createDecompressorStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ImageReader for Adobe Photoshop Document (PSD) format.
|
* ImageReader for Adobe Photoshop Document (PSD) format.
|
||||||
@@ -62,7 +66,11 @@ import java.util.*;
|
|||||||
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/">Adobe Photoshop File Formats Specification</a>
|
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/">Adobe Photoshop File Formats Specification</a>
|
||||||
* @see <a href="http://www.fileformat.info/format/psd/egff.htm">Adobe Photoshop File Format Summary</a>
|
* @see <a href="http://www.fileformat.info/format/psd/egff.htm">Adobe Photoshop File Format Summary</a>
|
||||||
*/
|
*/
|
||||||
// TODO: Implement ImageIO meta data interface
|
// TODO: Rethink metadata impl.
|
||||||
|
// * We should probably move most of the current impl to stream metadata as it belongs to the document, not the individual layers
|
||||||
|
// * Make each layer's metadata contain correct data, name, sub-image type, position etc.
|
||||||
|
// * Retain some information in the merged image/layers?
|
||||||
|
// * Completely skip the non-pixel layers in the reader (no longer return null, that's just ugly)
|
||||||
// TODO: Figure out of we should assume Adobe RGB (1998) color model, if no embedded profile?
|
// TODO: Figure out of we should assume Adobe RGB (1998) color model, if no embedded profile?
|
||||||
// TODO: Support for PSDVersionInfo hasRealMergedData=false (no real composite data, layers will be in index 0)
|
// TODO: Support for PSDVersionInfo hasRealMergedData=false (no real composite data, layers will be in index 0)
|
||||||
// TODO: Consider Romain Guy's Java 2D implementation of PS filters for the blending modes in layers
|
// TODO: Consider Romain Guy's Java 2D implementation of PS filters for the blending modes in layers
|
||||||
@@ -70,6 +78,7 @@ import java.util.*;
|
|||||||
// See http://www.codeproject.com/KB/graphics/PSDParser.aspx
|
// See http://www.codeproject.com/KB/graphics/PSDParser.aspx
|
||||||
// See http://www.adobeforums.com/webx?14@@.3bc381dc/0
|
// See http://www.adobeforums.com/webx?14@@.3bc381dc/0
|
||||||
// Done: Allow reading the extra alpha channels (index after composite data)
|
// Done: Allow reading the extra alpha channels (index after composite data)
|
||||||
|
// Done: Implement ImageIO meta data interface
|
||||||
public final class PSDImageReader extends ImageReaderBase {
|
public final class PSDImageReader extends ImageReaderBase {
|
||||||
|
|
||||||
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.psd.debug"));
|
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.psd.debug"));
|
||||||
@@ -390,21 +399,21 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
int compression = imageInput.readShort();
|
int compression = imageInput.readShort();
|
||||||
metadata.compression = compression;
|
metadata.compression = compression;
|
||||||
|
|
||||||
int[] byteCounts = null;
|
int[][] byteCounts = null;
|
||||||
switch (compression) {
|
switch (compression) {
|
||||||
|
case PSD.COMPRESSION_ZIP:
|
||||||
|
case PSD.COMPRESSION_ZIP_PREDICTION:
|
||||||
case PSD.COMPRESSION_NONE:
|
case PSD.COMPRESSION_NONE:
|
||||||
break;
|
break;
|
||||||
case PSD.COMPRESSION_RLE:
|
case PSD.COMPRESSION_RLE:
|
||||||
// NOTE: Byte counts will allow us to easily skip rows before AOI
|
// NOTE: Byte counts will allow us to easily skip rows before AOI
|
||||||
byteCounts = new int[header.channels * header.height];
|
byteCounts = new int[header.channels][header.height];
|
||||||
for (int i = 0; i < byteCounts.length; i++) {
|
for (int c = 0; c < header.channels; c++) {
|
||||||
byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
|
for (int y = 0; y < header.height; y++) {
|
||||||
|
byteCounts[c][y] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PSD.COMPRESSION_ZIP:
|
|
||||||
case PSD.COMPRESSION_ZIP_PREDICTION:
|
|
||||||
// TODO: Could probably use the ZIPDecoder (DeflateDecoder) here.. Look at TIFF prediction reading
|
|
||||||
throw new IIOException("PSD with ZIP compression not supported");
|
|
||||||
default:
|
default:
|
||||||
throw new IIOException(
|
throw new IIOException(
|
||||||
String.format(
|
String.format(
|
||||||
@@ -446,7 +455,7 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
private void readImageData(final BufferedImage destination,
|
private void readImageData(final BufferedImage destination,
|
||||||
final ColorModel pSourceCM, final Rectangle pSource, final Rectangle pDest,
|
final ColorModel pSourceCM, final Rectangle pSource, final Rectangle pDest,
|
||||||
final int pXSub, final int pYSub,
|
final int pXSub, final int pYSub,
|
||||||
final int[] pByteCounts, final int pCompression) throws IOException {
|
final int[][] byteCounts, final int compression) throws IOException {
|
||||||
|
|
||||||
WritableRaster destRaster = destination.getRaster();
|
WritableRaster destRaster = destination.getRaster();
|
||||||
ColorModel destCM = destination.getColorModel();
|
ColorModel destCM = destination.getColorModel();
|
||||||
@@ -458,31 +467,33 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
int interleavedBands = banded ? 1 : destRaster.getNumBands();
|
int interleavedBands = banded ? 1 : destRaster.getNumBands();
|
||||||
|
|
||||||
for (int c = 0; c < channels; c++) {
|
for (int c = 0; c < channels; c++) {
|
||||||
int bandOffset = banded ? 0 : interleavedBands - 1 - c;
|
try (ImageInputStream stream = createDecompressorStream(imageInput, compression, header.width, header.bits, byteCounts != null ? byteCounts[c] : null, -1)) {
|
||||||
|
int bandOffset = banded ? 0 : interleavedBands - 1 - c;
|
||||||
|
|
||||||
switch (header.bits) {
|
switch (header.bits) {
|
||||||
case 1:
|
case 1:
|
||||||
byte[] row1 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
byte[] row1 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||||
read1bitChannel(c, channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row1, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, pCompression == PSD.COMPRESSION_RLE);
|
read1bitChannel(stream, c, destRaster.getDataBuffer(), row1, pSource, pDest, pXSub, pYSub, header.width, header.height);
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
byte[] row8 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
byte[] row8 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||||
read8bitChannel(c, channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row8, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE);
|
read8bitChannel(stream, c, channels, destRaster.getDataBuffer(), c, interleavedBands, bandOffset, pSourceCM, row8, pSource, pDest, pXSub, pYSub, header.width, header.height);
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
||||||
read16bitChannel(c, channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row16, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE);
|
read16bitChannel(stream, c, channels, destRaster.getDataBuffer(), c, interleavedBands, bandOffset, pSourceCM, row16, pSource, pDest, pXSub, pYSub, header.width, header.height);
|
||||||
break;
|
break;
|
||||||
case 32:
|
case 32:
|
||||||
int[] row32 = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
|
int[] row32 = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
|
||||||
read32bitChannel(c, channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row32, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE);
|
read32bitChannel(stream, c, channels, destRaster.getDataBuffer(), c, interleavedBands, bandOffset, pSourceCM, row32, pSource, pDest, pXSub, pYSub, header.width, header.height);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IIOException(String.format("Unsupported PSD bit depth: %s", header.bits));
|
throw new IIOException(String.format("Unsupported PSD bit depth: %s", header.bits));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,222 +540,182 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
processImageProgress(100f * channel / channelCount + 100f * y / (height * channelCount));
|
processImageProgress(100f * channel / channelCount + 100f * y / (height * channelCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void read32bitChannel(final int pChannel, final int pChannelCount,
|
private void read32bitChannel(final ImageInputStream stream,
|
||||||
final DataBuffer pData, final int pBands, final int pBandOffset,
|
final int channel, final int channelCount,
|
||||||
final ColorModel pSourceColorModel,
|
final DataBuffer data,
|
||||||
final int[] pRow,
|
final int band, final int bandCount, final int bandOffset,
|
||||||
final Rectangle pSource, final Rectangle pDest,
|
final ColorModel sourceColorModel,
|
||||||
final int pXSub, final int pYSub,
|
final int[] rowData,
|
||||||
final int pChannelWidth, final int pChannelHeight,
|
final Rectangle sourceRect, final Rectangle destRect,
|
||||||
final int[] pRowByteCounts, final int pRowOffset,
|
final int xSub, final int ySub,
|
||||||
final boolean pRLECompressed) throws IOException {
|
final int channelWidth, final int channelHeight) throws IOException {
|
||||||
|
|
||||||
boolean isCMYK = pSourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
boolean isCMYK = sourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
||||||
int colorComponents = pSourceColorModel.getColorSpace().getNumComponents();
|
int colorComponents = sourceColorModel.getColorSpace().getNumComponents();
|
||||||
final boolean invert = isCMYK && pChannel < colorComponents;
|
final boolean invert = isCMYK && band < colorComponents;
|
||||||
final boolean banded = pData.getNumBanks() > 1;
|
final boolean banded = data.getNumBanks() > 1;
|
||||||
|
|
||||||
for (int y = 0; y < pChannelHeight; y++) {
|
|
||||||
int length = (pRLECompressed ? pRowByteCounts[pRowOffset + y] : 4 * pChannelWidth);
|
|
||||||
|
|
||||||
|
for (int y = 0; y < channelHeight; y++) {
|
||||||
// TODO: Sometimes need to read the line y == source.y + source.height...
|
// TODO: Sometimes need to read the line y == source.y + source.height...
|
||||||
// Read entire line, if within source region and sampling
|
// Read entire line, if within source region and sampling
|
||||||
if (y >= pSource.y && y < pSource.y + pSource.height && y % pYSub == 0) {
|
if (y >= sourceRect.y && y < sourceRect.y + sourceRect.height && y % ySub == 0) {
|
||||||
if (pRLECompressed) {
|
stream.readFully(rowData, 0, channelWidth);
|
||||||
|
|
||||||
try (DataInputStream input = PSDUtil.createPackBitsStream(imageInput, length)) {
|
|
||||||
for (int x = 0; x < pChannelWidth; x++) {
|
|
||||||
pRow[x] = input.readInt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
imageInput.readFully(pRow, 0, pChannelWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Destination offset...??
|
// TODO: Destination offset...??
|
||||||
// Copy line sub sampled into real data
|
// Copy line sub sampled into real data
|
||||||
int offset = (y - pSource.y) / pYSub * pDest.width * pBands + pBandOffset;
|
int offset = (y - sourceRect.y) / ySub * destRect.width * bandCount + bandOffset;
|
||||||
for (int x = 0; x < pDest.width; x++) {
|
for (int x = 0; x < destRect.width; x++) {
|
||||||
int value = pRow[pSource.x + x * pXSub];
|
int value = rowData[sourceRect.x + x * xSub];
|
||||||
|
|
||||||
// CMYK values are stored inverted, but alpha is not
|
// CMYK values are stored inverted, but alpha is not
|
||||||
if (invert) {
|
if (invert) {
|
||||||
value = 0xffffffff - value;
|
value = 0xffffffff - value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pData.setElem(banded ? pChannel : 0, offset + x * pBands, value);
|
data.setElem(banded ? band : 0, offset + x * bandCount, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
imageInput.skipBytes(length);
|
stream.skipBytes(4 * channelWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
|
processImageProgressForChannel(channel, channelCount, y, channelHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void read16bitChannel(final int pChannel, final int pChannelCount,
|
private void read16bitChannel(final ImageInputStream stream,
|
||||||
final DataBuffer pData, final int pBands, final int pBandOffset,
|
final int channel, final int channelCount,
|
||||||
final ColorModel pSourceColorModel,
|
final DataBuffer data,
|
||||||
final short[] pRow,
|
final int band, final int bandCount, final int bandOffset,
|
||||||
final Rectangle pSource, final Rectangle pDest,
|
final ColorModel sourceColorModel,
|
||||||
final int pXSub, final int pYSub,
|
final short[] rowData,
|
||||||
final int pChannelWidth, final int pChannelHeight,
|
final Rectangle sourceRect, final Rectangle destRect,
|
||||||
final int[] pRowByteCounts, final int pRowOffset,
|
final int xSub, final int ySub,
|
||||||
final boolean pRLECompressed) throws IOException {
|
final int channelWidth, final int channelHeight) throws IOException {
|
||||||
|
|
||||||
boolean isCMYK = pSourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
boolean isCMYK = sourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
||||||
int colorComponents = pSourceColorModel.getColorSpace().getNumComponents();
|
int colorComponents = sourceColorModel.getColorSpace().getNumComponents();
|
||||||
final boolean invert = isCMYK && pChannel < colorComponents;
|
final boolean invert = isCMYK && band < colorComponents;
|
||||||
final boolean banded = pData.getNumBanks() > 1;
|
final boolean banded = data.getNumBanks() > 1;
|
||||||
|
|
||||||
for (int y = 0; y < pChannelHeight; y++) {
|
|
||||||
int length = (pRLECompressed ? pRowByteCounts[pRowOffset + y] : 2 * pChannelWidth);
|
|
||||||
|
|
||||||
|
for (int y = 0; y < channelHeight; y++) {
|
||||||
// TODO: Sometimes need to read the line y == source.y + source.height...
|
// TODO: Sometimes need to read the line y == source.y + source.height...
|
||||||
// Read entire line, if within source region and sampling
|
// Read entire line, if within source region and sampling
|
||||||
if (y >= pSource.y && y < pSource.y + pSource.height && y % pYSub == 0) {
|
if (y >= sourceRect.y && y < sourceRect.y + sourceRect.height && y % ySub == 0) {
|
||||||
if (pRLECompressed) {
|
stream.readFully(rowData, 0, channelWidth);
|
||||||
try (DataInputStream input = PSDUtil.createPackBitsStream(imageInput, length)) {
|
|
||||||
for (int x = 0; x < pChannelWidth; x++) {
|
|
||||||
pRow[x] = input.readShort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
imageInput.readFully(pRow, 0, pChannelWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Destination offset...??
|
// TODO: Destination offset...??
|
||||||
// Copy line sub sampled into real data
|
// Copy line sub sampled into real data
|
||||||
int offset = (y - pSource.y) / pYSub * pDest.width * pBands + pBandOffset;
|
int offset = (y - sourceRect.y) / ySub * destRect.width * bandCount + bandOffset;
|
||||||
for (int x = 0; x < pDest.width; x++) {
|
for (int x = 0; x < destRect.width; x++) {
|
||||||
short value = pRow[pSource.x + x * pXSub];
|
short value = rowData[sourceRect.x + x * xSub];
|
||||||
|
|
||||||
// CMYK values are stored inverted, but alpha is not
|
// CMYK values are stored inverted, but alpha is not
|
||||||
if (invert) {
|
if (invert) {
|
||||||
value = (short) (0xffff - value & 0xffff);
|
value = (short) (0xffff - value & 0xffff);
|
||||||
}
|
}
|
||||||
|
|
||||||
pData.setElem(banded ? pChannel : 0, offset + x * pBands, value);
|
data.setElem(banded ? band : 0, offset + x * bandCount, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
imageInput.skipBytes(length);
|
stream.skipBytes(2 * channelWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
|
processImageProgressForChannel(channel, channelCount, y, channelHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void read8bitChannel(final int pChannel, final int pChannelCount,
|
private void read8bitChannel(final ImageInputStream stream,
|
||||||
final DataBuffer pData, final int pBands, final int pBandOffset,
|
final int channel, final int channelCount,
|
||||||
final ColorModel pSourceColorModel,
|
final DataBuffer data,
|
||||||
final byte[] pRow,
|
final int band, final int bandCount, final int bandOffset,
|
||||||
final Rectangle pSource, final Rectangle pDest,
|
final ColorModel sourceColorModel,
|
||||||
final int pXSub, final int pYSub,
|
final byte[] rowData,
|
||||||
final int pChannelWidth, final int pChannelHeight,
|
final Rectangle sourceRect, final Rectangle destRect,
|
||||||
final int[] pRowByteCounts, final int pRowOffset,
|
final int xSub, final int ySub,
|
||||||
final boolean pRLECompressed) throws IOException {
|
final int channelWidth, final int channelHeight) throws IOException {
|
||||||
|
|
||||||
boolean isCMYK = pSourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
boolean isCMYK = sourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
|
||||||
int colorComponents = pSourceColorModel.getColorSpace().getNumComponents();
|
int colorComponents = sourceColorModel.getColorSpace().getNumComponents();
|
||||||
final boolean invert = isCMYK && pChannel < colorComponents;
|
final boolean invert = isCMYK && band < colorComponents;
|
||||||
final boolean banded = pData.getNumBanks() > 1;
|
final boolean banded = data.getNumBanks() > 1;
|
||||||
|
|
||||||
for (int y = 0; y < pChannelHeight; y++) {
|
|
||||||
int length = pRLECompressed ? pRowByteCounts[pRowOffset + y] : pChannelWidth;
|
|
||||||
|
|
||||||
|
for (int y = 0; y < channelHeight; y++) {
|
||||||
// TODO: Sometimes need to read the line y == source.y + source.height...
|
// TODO: Sometimes need to read the line y == source.y + source.height...
|
||||||
// Read entire line, if within source region and sampling
|
// Read entire line, if within source region and sampling
|
||||||
if (y >= pSource.y && y < pSource.y + pSource.height && y % pYSub == 0) {
|
if (y >= sourceRect.y && y < sourceRect.y + sourceRect.height && y % ySub == 0) {
|
||||||
if (pRLECompressed) {
|
stream.readFully(rowData, 0, channelWidth);
|
||||||
try (DataInputStream input = PSDUtil.createPackBitsStream(imageInput, length)) {
|
|
||||||
input.readFully(pRow, 0, pChannelWidth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
imageInput.readFully(pRow, 0, pChannelWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Destination offset...??
|
// TODO: Destination offset...??
|
||||||
// Copy line sub sampled into real data
|
// Copy line sub sampled into real data
|
||||||
int offset = (y - pSource.y) / pYSub * pDest.width * pBands + pBandOffset;
|
int offset = (y - sourceRect.y) / ySub * destRect.width * bandCount + bandOffset;
|
||||||
for (int x = 0; x < pDest.width; x++) {
|
for (int x = 0; x < destRect.width; x++) {
|
||||||
byte value = pRow[pSource.x + x * pXSub];
|
byte value = rowData[sourceRect.x + x * xSub];
|
||||||
|
|
||||||
// CMYK values are stored inverted, but alpha is not
|
// CMYK values are stored inverted, but alpha is not
|
||||||
if (invert) {
|
if (invert) {
|
||||||
value = (byte) (0xff - value & 0xff);
|
value = (byte) (0xff - value & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
pData.setElem(banded ? pChannel : 0, offset + x * pBands, value);
|
data.setElem(banded ? band : 0, offset + x * bandCount, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
imageInput.skipBytes(length);
|
stream.skipBytes(channelWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
|
processImageProgressForChannel(channel, channelCount, y, channelHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"UnusedDeclaration"})
|
private void read1bitChannel(final ImageInputStream stream,
|
||||||
private void read1bitChannel(final int pChannel, final int pChannelCount,
|
final int channel,
|
||||||
final DataBuffer pData, final int pBands, final int pBandOffset,
|
final DataBuffer data,
|
||||||
final ColorModel pSourceColorModel,
|
final byte[] rowData,
|
||||||
final byte[] pRow,
|
final Rectangle sourceRect, final Rectangle destRect,
|
||||||
final Rectangle pSource, final Rectangle pDest,
|
final int xSub, final int ySub,
|
||||||
final int pXSub, final int pYSub,
|
final int channelWidth, final int channelHeight) throws IOException {
|
||||||
final int pChannelWidth, final int pChannelHeight,
|
|
||||||
final int[] pRowByteCounts, boolean pRLECompressed) throws IOException {
|
|
||||||
// NOTE: 1 bit channels only occurs once
|
// NOTE: 1 bit channels only occurs once
|
||||||
|
if (channel > 0) {
|
||||||
|
throw new IIOException("Multiple channels not supported for 1 bit data");
|
||||||
|
}
|
||||||
|
|
||||||
final int destWidth = (pDest.width + 7) / 8;
|
final int destWidth = (destRect.width + 7) / 8;
|
||||||
final boolean banded = pData.getNumBanks() > 1;
|
final boolean banded = data.getNumBanks() > 1;
|
||||||
|
|
||||||
for (int y = 0; y < pChannelHeight; y++) {
|
|
||||||
int length = pRLECompressed ? pRowByteCounts[y] : pChannelWidth;
|
|
||||||
|
|
||||||
|
for (int y = 0; y < channelHeight; y++) {
|
||||||
// TODO: Sometimes need to read the line y == source.y + source.height...
|
// TODO: Sometimes need to read the line y == source.y + source.height...
|
||||||
// Read entire line, if within source region and sampling
|
// Read entire line, if within source region and sampling
|
||||||
if (y >= pSource.y && y < pSource.y + pSource.height && y % pYSub == 0) {
|
if (y >= sourceRect.y && y < sourceRect.y + sourceRect.height && y % ySub == 0) {
|
||||||
if (pRLECompressed) {
|
stream.readFully(rowData, 0, rowData.length);
|
||||||
try (DataInputStream input = PSDUtil.createPackBitsStream(imageInput, length)) {
|
|
||||||
input.readFully(pRow, 0, pRow.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
imageInput.readFully(pRow, 0, pRow.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Destination offset...??
|
// TODO: Destination offset...??
|
||||||
int offset = (y - pSource.y) / pYSub * destWidth;
|
int offset = (y - sourceRect.y) / ySub * destWidth;
|
||||||
if (pXSub == 1 && pSource.x % 8 == 0) {
|
if (xSub == 1 && sourceRect.x % 8 == 0) {
|
||||||
// Fast normal case, no sub sampling
|
// Fast normal case, no sub sampling
|
||||||
for (int i = 0; i < destWidth; i++) {
|
for (int i = 0; i < destWidth; i++) {
|
||||||
byte value = pRow[pSource.x / 8 + i * pXSub];
|
byte value = rowData[sourceRect.x / 8 + i * xSub];
|
||||||
// NOTE: Invert bits to match Java's default monochrome
|
// NOTE: Invert bits to match Java's default monochrome
|
||||||
pData.setElem(banded ? pChannel : 0, offset + i, (byte) (~value & 0xff));
|
data.setElem(banded ? channel : 0, offset + i, (byte) (~value & 0xff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Copy line sub sampled into real data
|
// Copy line sub sampled into real data
|
||||||
final int maxX = pSource.x + pSource.width;
|
final int maxX = sourceRect.x + sourceRect.width;
|
||||||
int x = pSource.x;
|
int x = sourceRect.x;
|
||||||
for (int i = 0; i < destWidth; i++) {
|
for (int i = 0; i < destWidth; i++) {
|
||||||
byte result = 0;
|
byte result = 0;
|
||||||
|
|
||||||
@@ -756,25 +727,25 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
int destBitOff = 7 - j;
|
int destBitOff = 7 - j;
|
||||||
|
|
||||||
// Shift bit into place
|
// Shift bit into place
|
||||||
result |= ((pRow[bytePos] & mask) >> sourceBitOff) << destBitOff;
|
result |= ((rowData[bytePos] & mask) >> sourceBitOff) << destBitOff;
|
||||||
|
|
||||||
x += pXSub;
|
x += xSub;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Invert bits to match Java's default monochrome
|
// NOTE: Invert bits to match Java's default monochrome
|
||||||
pData.setElem(banded ? pChannel : 0, offset + i, (byte) (~result & 0xff));
|
data.setElem(banded ? channel : 0, offset + i, (byte) (~result & 0xff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
imageInput.skipBytes(length);
|
stream.skipBytes((channelWidth + 7) / 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
|
processImageProgressForChannel(channel, 1, y, channelHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -920,13 +891,13 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
// TODO: Flags or list of interesting resources to parse
|
// TODO: Flags or list of interesting resources to parse
|
||||||
// TODO: Obey ignoreMetadata
|
// TODO: Obey ignoreMetadata
|
||||||
private void readLayerAndMaskInfo(final boolean pParseData) throws IOException {
|
private void readLayerAndMaskInfo(final boolean parseData) throws IOException {
|
||||||
readImageResources(false);
|
readImageResources(false);
|
||||||
|
|
||||||
if (pParseData && (metadata.layerInfo == null || metadata.globalLayerMask == null) || metadata.imageDataStart == 0) {
|
if (parseData && (metadata.layerInfo == null || metadata.globalLayerMask == null) || metadata.imageDataStart == 0) {
|
||||||
imageInput.seek(metadata.layerAndMaskInfoStart);
|
imageInput.seek(metadata.layerAndMaskInfoStart);
|
||||||
|
|
||||||
long layerAndMaskInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt();
|
long layerAndMaskInfoLength = readLength(imageInput);
|
||||||
|
|
||||||
// NOTE: The spec says that if this section is empty, the length should be 0.
|
// NOTE: The spec says that if this section is empty, the length should be 0.
|
||||||
// Yet I have a PSB file that has size 12, and both contained lengths set to 0 (which
|
// Yet I have a PSB file that has size 12, and both contained lengths set to 0 (which
|
||||||
@@ -936,7 +907,7 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
if (layerAndMaskInfoLength > 0) {
|
if (layerAndMaskInfoLength > 0) {
|
||||||
long pos = imageInput.getStreamPosition();
|
long pos = imageInput.getStreamPosition();
|
||||||
|
|
||||||
long layerInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt();
|
long layerInfoLength = readLength(imageInput);
|
||||||
|
|
||||||
if (layerInfoLength > 0) {
|
if (layerInfoLength > 0) {
|
||||||
// "Layer count. If it is a negative number, its absolute value is the number of
|
// "Layer count. If it is a negative number, its absolute value is the number of
|
||||||
@@ -945,7 +916,7 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
int layerCount = imageInput.readShort();
|
int layerCount = imageInput.readShort();
|
||||||
metadata.layerCount = layerCount;
|
metadata.layerCount = layerCount;
|
||||||
|
|
||||||
if (pParseData && metadata.layerInfo == null) {
|
if (metadata.layerInfo == null) {
|
||||||
metadata.layerInfo = readLayerInfo(Math.abs(layerCount));
|
metadata.layerInfo = readLayerInfo(Math.abs(layerCount));
|
||||||
metadata.layersStart = imageInput.getStreamPosition();
|
metadata.layersStart = imageInput.getStreamPosition();
|
||||||
}
|
}
|
||||||
@@ -955,16 +926,13 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
imageInput.skipBytes(diff);
|
imageInput.skipBytes(diff);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
metadata.layerInfo = Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global LayerMaskInfo (18 bytes or more..?)
|
// Global LayerMaskInfo (18 bytes or more..?)
|
||||||
// 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad)
|
// 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad)
|
||||||
long globalLayerMaskInfoLength = imageInput.readUnsignedInt(); // NOTE: Not long for PSB!
|
long globalLayerMaskInfoLength = imageInput.readUnsignedInt(); // NOTE: Not long for PSB!
|
||||||
|
|
||||||
if (globalLayerMaskInfoLength > 0) {
|
if (globalLayerMaskInfoLength > 0) {
|
||||||
if (pParseData && metadata.globalLayerMask == null) {
|
if (parseData && metadata.globalLayerMask == null) {
|
||||||
metadata.globalLayerMask = new PSDGlobalLayerMask(imageInput, globalLayerMaskInfoLength);
|
metadata.globalLayerMask = new PSDGlobalLayerMask(imageInput, globalLayerMaskInfoLength);
|
||||||
}
|
}
|
||||||
// TODO: Else skip?
|
// TODO: Else skip?
|
||||||
@@ -973,13 +941,52 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
metadata.globalLayerMask = PSDGlobalLayerMask.NULL_MASK;
|
metadata.globalLayerMask = PSDGlobalLayerMask.NULL_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Parse "Additional layer information"
|
if (metadata.layerInfo == null) {
|
||||||
|
while (imageInput.getStreamPosition() + 12 < metadata.layerAndMaskInfoStart + layerAndMaskInfoLength) {
|
||||||
|
int resSig = imageInput.readInt();
|
||||||
|
if (resSig != PSD.RESOURCE_TYPE) {
|
||||||
|
processWarningOccurred(String.format("Bad resource alignment, expected: '8BIM' was '%s'", PSDUtil.intToStr(resSig)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int resId = imageInput.readInt();
|
||||||
|
long resLength = readLength(imageInput, resId); // In this section, resource lengths *vary* based on the resource...
|
||||||
|
// Calculate next offset, for some reason lengths are padded to 32 bit...
|
||||||
|
long nextOffset = imageInput.getStreamPosition() + 4 * ((resLength + 3) / 4);
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("resId: " + PSDUtil.intToStr(resId));
|
||||||
|
System.out.println("length = " + resLength);
|
||||||
|
System.out.printf("nextOffset = %d%n", nextOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (resId) {
|
||||||
|
case PSD.Layr:
|
||||||
|
case PSD.Lr16:
|
||||||
|
case PSD.Lr32:
|
||||||
|
short layerCount = imageInput.readShort();
|
||||||
|
|
||||||
|
metadata.layerCount = layerCount;
|
||||||
|
metadata.layerInfo = readLayerInfo(Math.abs(layerCount));
|
||||||
|
metadata.layersStart = imageInput.getStreamPosition();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
imageInput.seek(nextOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseData && metadata.layerInfo == null) {
|
||||||
|
// We have parsed but didn't find any layers
|
||||||
|
metadata.layerInfo = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: We should now be able to flush input
|
// TODO: We should now be able to flush input
|
||||||
// imageInput.seek(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
|
// imageInput.seek(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
|
||||||
// imageInput.flushBefore(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
|
// imageInput.flushBefore(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
|
||||||
|
|
||||||
if (pParseData && DEBUG) {
|
if (parseData && DEBUG) {
|
||||||
System.out.println("layerInfo: " + metadata.layerInfo);
|
System.out.println("layerInfo: " + metadata.layerInfo);
|
||||||
System.out.println("globalLayerMask: " + (metadata.globalLayerMask != PSDGlobalLayerMask.NULL_MASK ? metadata.globalLayerMask : null));
|
System.out.println("globalLayerMask: " + (metadata.globalLayerMask != PSDGlobalLayerMask.NULL_MASK ? metadata.globalLayerMask : null));
|
||||||
}
|
}
|
||||||
@@ -989,6 +996,39 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long readLength(final ImageInputStream stream) throws IOException {
|
||||||
|
return header.largeFormat
|
||||||
|
? stream.readLong()
|
||||||
|
: stream.readUnsignedInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long readLength(final ImageInputStream stream, final int resId) throws IOException {
|
||||||
|
// Only the following resources use long (64 bit) lengths:
|
||||||
|
// LMsk, Lr16, Lr32, Layr, Mt16, Mt32, Mtrn, Alph, FMsk, lnk2, FEid, FXid, PxSD
|
||||||
|
if (header.largeFormat) {
|
||||||
|
switch (resId) {
|
||||||
|
case PSD.LMsk:
|
||||||
|
case PSD.Lr16:
|
||||||
|
case PSD.Lr32:
|
||||||
|
case PSD.Layr:
|
||||||
|
case PSD.Mt16:
|
||||||
|
case PSD.Mt32:
|
||||||
|
case PSD.Mtrn:
|
||||||
|
case PSD.Alph:
|
||||||
|
case PSD.FMsk:
|
||||||
|
case PSD.lnk2:
|
||||||
|
case PSD.FEid:
|
||||||
|
case PSD.FXid:
|
||||||
|
case PSD.PxSD:
|
||||||
|
return stream.readLong();
|
||||||
|
default:
|
||||||
|
// Fall through to 32 bit length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream.readUnsignedInt();
|
||||||
|
}
|
||||||
|
|
||||||
private List<PSDLayerInfo> readLayerInfo(int layerCount) throws IOException {
|
private List<PSDLayerInfo> readLayerInfo(int layerCount) throws IOException {
|
||||||
PSDLayerInfo[] layerInfos = new PSDLayerInfo[layerCount];
|
PSDLayerInfo[] layerInfos = new PSDLayerInfo[layerCount];
|
||||||
|
|
||||||
@@ -1052,21 +1092,22 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
final boolean banded = raster.getDataBuffer().getNumBanks() > 1;
|
final boolean banded = raster.getDataBuffer().getNumBanks() > 1;
|
||||||
final int interleavedBands = banded ? 1 : raster.getNumBands();
|
final int interleavedBands = banded ? 1 : raster.getNumBands();
|
||||||
|
|
||||||
// TODO: progress for layers!
|
processImageStarted(1 + layerIndex);
|
||||||
|
|
||||||
// TODO: Consider creating a method in PSDLayerInfo that can tell how many channels we really want to decode
|
// TODO: Consider creating a method in PSDLayerInfo that can tell how many channels we really want to decode
|
||||||
for (PSDChannelInfo channelInfo : layerInfo.channelInfo) {
|
for (int channel = 0; channel < layerInfo.channelInfo.length; channel++) {
|
||||||
|
PSDChannelInfo channelInfo = layerInfo.channelInfo[channel];
|
||||||
|
|
||||||
int compression = imageInput.readUnsignedShort();
|
int compression = imageInput.readUnsignedShort();
|
||||||
|
|
||||||
// Skip layer if we can't read it
|
// Skip layer if we can't read it
|
||||||
// channelId
|
// channelId -1 = transparency mask; -2 = user supplied layer mask, -3 = real user supplied layer mask (when both a user mask and a vector mask are present)
|
||||||
// -1 = transparency mask; -2 = user supplied layer mask, -3 = real user supplied layer mask (when both a user mask and a vector mask are present)
|
if (channelInfo.channelId < -1) {
|
||||||
if (channelInfo.channelId < -1 || (compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) { // TODO: ZIP Compressions!
|
processWarningOccurred(String.format("Skipping channel %s (%s)", channelInfo.channelId, channelInfo.channelId >= -3 ? "user supplied layer mask" : "unknown channel data"));
|
||||||
imageInput.skipBytes(channelInfo.length - 2);
|
imageInput.skipBytes(channelInfo.length - 2);
|
||||||
}
|
} else {
|
||||||
else {
|
// 0 = red, 1 = green, etc -1 = transparency mask
|
||||||
// 0 = red, 1 = green, etc
|
int band = channelInfo.channelId == -1 ? rowRaster.getNumBands() - 1 : channelInfo.channelId;
|
||||||
// -1 = transparency mask; -2 = user supplied layer mask, -3 = real user supplied layer mask (when both a user mask and a vector mask are present)
|
|
||||||
int c = channelInfo.channelId == -1 ? rowRaster.getNumBands() - 1 : channelInfo.channelId;
|
|
||||||
|
|
||||||
// NOTE: For layers, byte counts are written per channel, while for the composite data
|
// NOTE: For layers, byte counts are written per channel, while for the composite data
|
||||||
// byte counts are written for all channels before the image data.
|
// byte counts are written for all channels before the image data.
|
||||||
@@ -1076,52 +1117,51 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
// 0: None, 1: PackBits RLE, 2: Zip, 3: Zip w/prediction
|
// 0: None, 1: PackBits RLE, 2: Zip, 3: Zip w/prediction
|
||||||
switch (compression) {
|
switch (compression) {
|
||||||
case PSD.COMPRESSION_NONE:
|
case PSD.COMPRESSION_NONE:
|
||||||
|
case PSD.COMPRESSION_ZIP:
|
||||||
|
case PSD.COMPRESSION_ZIP_PREDICTION:
|
||||||
break;
|
break;
|
||||||
case PSD.COMPRESSION_RLE:
|
case PSD.COMPRESSION_RLE:
|
||||||
// If RLE, the the image data starts with the byte counts
|
// If RLE, the image data starts with the byte counts
|
||||||
// for all the scan lines in the channel (LayerBottom-LayerTop), with
|
// for all the scan lines in the channel (LayerBottom-LayerTop), with
|
||||||
// each count stored as a two*byte (four for PSB) value.
|
// each count stored as a two-byte (four for PSB) value.
|
||||||
byteCounts = new int[layerInfo.bottom - layerInfo.top];
|
byteCounts = new int[layerInfo.bottom - layerInfo.top];
|
||||||
for (int i = 0; i < byteCounts.length; i++) {
|
for (int i = 0; i < byteCounts.length; i++) {
|
||||||
byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
|
byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PSD.COMPRESSION_ZIP:
|
|
||||||
case PSD.COMPRESSION_ZIP_PREDICTION:
|
|
||||||
default:
|
default:
|
||||||
// Explicitly skipped above
|
// Explicitly skipped above
|
||||||
throw new AssertionError(String.format("Unsupported layer data. Compression: %d", compression));
|
throw new AssertionError(String.format("Unsupported layer data. Compression: %d", compression));
|
||||||
}
|
}
|
||||||
|
|
||||||
int bandOffset = banded ? 0 : interleavedBands - 1 - c;
|
try (ImageInputStream stream = createDecompressorStream(imageInput, compression, width, header.bits, byteCounts, channelInfo.length - 2)) {
|
||||||
|
int bandOffset = banded ? 0 : interleavedBands - 1 - band;
|
||||||
|
|
||||||
switch (header.bits) {
|
switch (header.bits) {
|
||||||
case 1:
|
case 1:
|
||||||
byte[] row1 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
byte[] row1 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||||
read1bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row1, area, area, xsub, ysub, width, height, byteCounts, compression == PSD.COMPRESSION_RLE);
|
read1bitChannel(stream, channel, raster.getDataBuffer(), row1, area, area, xsub, ysub, width, height);
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
byte[] row8 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
byte[] row8 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||||
read8bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row8, area, area, xsub,
|
read8bitChannel(stream, channel, imageType.getNumBands(), raster.getDataBuffer(), band, interleavedBands, bandOffset, sourceCM, row8, area, area, xsub, ysub, width, height);
|
||||||
ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
break;
|
||||||
break;
|
case 16:
|
||||||
case 16:
|
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
||||||
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
|
read16bitChannel(stream, channel, imageType.getNumBands(), raster.getDataBuffer(), band, interleavedBands, bandOffset, sourceCM, row16, area, area, xsub, ysub, width, height);
|
||||||
read16bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row16, area, area, xsub,
|
break;
|
||||||
ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
case 32:
|
||||||
break;
|
int[] row32 = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
|
||||||
case 32:
|
read32bitChannel(stream, channel, imageType.getNumBands(), raster.getDataBuffer(), band, interleavedBands, bandOffset, sourceCM, row32, area, area, xsub, ysub, width, height);
|
||||||
int[] row32 = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
|
break;
|
||||||
read32bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row32, area, area, xsub,
|
default:
|
||||||
ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
|
throw new IIOException(String.format("Unknown PSD bit depth: %s", header.bits));
|
||||||
break;
|
}
|
||||||
default:
|
|
||||||
throw new IIOException(String.format("Unknown PSD bit depth: %s", header.bits));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (abortRequested()) {
|
if (abortRequested()) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1130,6 +1170,8 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
convertToDestinationCS(sourceCM, destCM, raster);
|
convertToDestinationCS(sourceCM, destCM, raster);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processImageComplete();
|
||||||
|
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1177,6 +1219,7 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
// But that makes no sense for a format (like PSD) that does not need to search, right?
|
// But that makes no sense for a format (like PSD) that does not need to search, right?
|
||||||
readLayerAndMaskInfo(false);
|
readLayerAndMaskInfo(false);
|
||||||
|
|
||||||
|
// TODO: Do we really want to include the layers that doesn't have pixel data?
|
||||||
return metadata.getLayerCount() + 1; // TODO: Only plus one, if "has real merged data"?
|
return metadata.getLayerCount() + 1; // TODO: Only plus one, if "has real merged data"?
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1311,38 +1354,43 @@ public final class PSDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
while (pArgs[idx].charAt(0) == '-') {
|
while (pArgs[idx].charAt(0) == '-') {
|
||||||
if (pArgs[idx].equals("-s") || pArgs[idx].equals("--subsampling")) {
|
switch (pArgs[idx]) {
|
||||||
subsampleFactor = Integer.parseInt(pArgs[++idx]);
|
case "-s":
|
||||||
}
|
case "--subsampling":
|
||||||
else if (pArgs[idx].equals("-r") || pArgs[idx].equals("--sourceregion")) {
|
subsampleFactor = Integer.parseInt(pArgs[++idx]);
|
||||||
int xw = Integer.parseInt(pArgs[++idx]);
|
break;
|
||||||
int yh = Integer.parseInt(pArgs[++idx]);
|
case "-r":
|
||||||
|
case "--sourceregion":
|
||||||
|
int xw = Integer.parseInt(pArgs[++idx]);
|
||||||
|
int yh = Integer.parseInt(pArgs[++idx]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int w = Integer.parseInt(pArgs[idx + 1]);
|
int w = Integer.parseInt(pArgs[idx + 1]);
|
||||||
int h = Integer.parseInt(pArgs[idx + 2]);
|
int h = Integer.parseInt(pArgs[idx + 2]);
|
||||||
|
|
||||||
idx += 2;
|
idx += 2;
|
||||||
|
|
||||||
// x y w h
|
// x y w h
|
||||||
sourceRegion = new Rectangle(xw, yh, w, h);
|
sourceRegion = new Rectangle(xw, yh, w, h);
|
||||||
}
|
}
|
||||||
catch (NumberFormatException e) {
|
catch (NumberFormatException e) {
|
||||||
// w h
|
// w h
|
||||||
sourceRegion = new Rectangle(xw, yh);
|
sourceRegion = new Rectangle(xw, yh);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("sourceRegion: " + sourceRegion);
|
System.out.println("sourceRegion: " + sourceRegion);
|
||||||
}
|
break;
|
||||||
else if (pArgs[idx].equals("-l") || pArgs[idx].equals("--layers")) {
|
case "-l":
|
||||||
readLayers = true;
|
case "--layers":
|
||||||
}
|
readLayers = true;
|
||||||
else if (pArgs[idx].equals("-t") || pArgs[idx].equals("--thumbnails")) {
|
break;
|
||||||
readThumbnails = true;
|
case "-t":
|
||||||
}
|
case "--thumbnails":
|
||||||
else {
|
readThumbnails = true;
|
||||||
System.err.println("Usage: java PSDImageReader [-s <subsample factor>] [-r [<x y>] <w h>] <image file>");
|
break;
|
||||||
System.exit(1);
|
default:
|
||||||
|
System.err.println("Usage: java PSDImageReader [-s <subsample factor>] [-r [<x y>] <w h>] [-t -l] <image file>");
|
||||||
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
idx++;
|
idx++;
|
||||||
|
|||||||
+11
-2
@@ -41,7 +41,7 @@ import com.twelvemonkeys.util.FilterIterator;
|
|||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
import java.awt.image.IndexColorModel;
|
import java.awt.image.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -101,6 +101,8 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
super(true, NATIVE_METADATA_FORMAT_NAME, NATIVE_METADATA_FORMAT_CLASS_NAME, null, null);
|
super(true, NATIVE_METADATA_FORMAT_NAME, NATIVE_METADATA_FORMAT_CLASS_NAME, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Allow creating correct metadata for layers too!
|
||||||
|
|
||||||
/// Native format support
|
/// Native format support
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -148,7 +150,7 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
|
|
||||||
for (PSDImageResource imageResource : imageResources) {
|
for (PSDImageResource imageResource : imageResources) {
|
||||||
// TODO: Always add name (if set) and id (as resourceId) to all nodes?
|
// TODO: Always add name (if set) and id (as resourceId) to all nodes?
|
||||||
// Resource Id is useful for people with access to the PSD spec..
|
// Resource Id is useful for people with access to the PSD spec...
|
||||||
|
|
||||||
if (imageResource instanceof ICCProfile) {
|
if (imageResource instanceof ICCProfile) {
|
||||||
ICCProfile profile = (ICCProfile) imageResource;
|
ICCProfile profile = (ICCProfile) imageResource;
|
||||||
@@ -675,6 +677,13 @@ public final class PSDMetadata extends AbstractMetadata {
|
|||||||
formatVersion.setAttribute("value", header.largeFormat ? "2" : "1"); // PSD format version is always 1, PSB is 2
|
formatVersion.setAttribute("value", header.largeFormat ? "2" : "1"); // PSD format version is always 1, PSB is 2
|
||||||
document_node.appendChild(formatVersion);
|
document_node.appendChild(formatVersion);
|
||||||
|
|
||||||
|
// TODO: For images other than image 0
|
||||||
|
// IIOMetadataNode subimageInterpretation = new IIOMetadataNode("SubimageInterpretation");
|
||||||
|
// subimageInterpretation.setAttribute("value", "CompositingLayer");
|
||||||
|
// document_node.appendChild(subimageInterpretation);
|
||||||
|
|
||||||
|
// TODO: Layer name?
|
||||||
|
|
||||||
// Get EXIF data if present
|
// Get EXIF data if present
|
||||||
Iterator<PSDEXIF1Data> exif = getResources(PSDEXIF1Data.class);
|
Iterator<PSDEXIF1Data> exif = getResources(PSDEXIF1Data.class);
|
||||||
if (exif.hasNext()) {
|
if (exif.hasNext()) {
|
||||||
|
|||||||
+2
-2
@@ -34,7 +34,7 @@ import javax.imageio.IIOException;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -77,7 +77,7 @@ final class PSDThumbnail extends PSDImageResource {
|
|||||||
|
|
||||||
// This data isn't really useful, unless we're dealing with raw bytes
|
// This data isn't really useful, unless we're dealing with raw bytes
|
||||||
widthBytes = pInput.readInt();
|
widthBytes = pInput.readInt();
|
||||||
int totalSize = pInput.readInt(); // Hmm.. Is this really useful at all?
|
int totalSize = pInput.readInt(); // Hmm... Is this really useful at all?
|
||||||
|
|
||||||
// Consistency check
|
// Consistency check
|
||||||
int sizeCompressed = pInput.readInt();
|
int sizeCompressed = pInput.readInt();
|
||||||
|
|||||||
+51
-15
@@ -30,16 +30,22 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.psd;
|
package com.twelvemonkeys.imageio.plugins.psd;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
import com.twelvemonkeys.imageio.stream.DirectImageInputStream;
|
||||||
|
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
|
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.SequenceInputStream;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.zip.InflaterInputStream;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.util.IIOUtil.createStreamAdapter;
|
||||||
|
import static java.nio.ByteOrder.BIG_ENDIAN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PSDUtil
|
* PSDUtil
|
||||||
@@ -89,19 +95,49 @@ final class PSDUtil {
|
|||||||
return StringUtil.decode(bytes, 0, bytes.length, "UTF-16");
|
return StringUtil.decode(bytes, 0, bytes.length, "UTF-16");
|
||||||
}
|
}
|
||||||
|
|
||||||
static DataInputStream createPackBitsStream(final ImageInputStream pInput, long pLength) {
|
|
||||||
return new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pInput, pLength), new PackBitsDecoder()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static DataInputStream createZipStream(final ImageInputStream pInput, long pLength) {
|
|
||||||
return new DataInputStream(new ZipInputStream(IIOUtil.createStreamAdapter(pInput, pLength)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static DataInputStream createZipPredictorStream(final ImageInputStream pInput, long pLength) {
|
|
||||||
throw new UnsupportedOperationException("Method createZipPredictonStream not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float fixedPointToFloat(int pFP) {
|
public static float fixedPointToFloat(int pFP) {
|
||||||
return ((pFP & 0xffff0000) >> 16) + (pFP & 0xffff) / (float) 0xffff;
|
return ((pFP & 0xffff0000) >> 16) + (pFP & 0xffff) / (float) 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ImageInputStream createDecompressorStream(final ImageInputStream stream, int compression, int columns, int bitsPerSample,
|
||||||
|
final int[] byteCounts, long compressedLength) throws IOException {
|
||||||
|
switch (compression) {
|
||||||
|
case PSD.COMPRESSION_NONE:
|
||||||
|
return new SubImageInputStream(stream, stream.length());
|
||||||
|
|
||||||
|
case PSD.COMPRESSION_RLE:
|
||||||
|
return new DirectImageInputStream(new SequenceInputStream(new LazyPackBitsStreamEnumeration(byteCounts, stream)));
|
||||||
|
|
||||||
|
case PSD.COMPRESSION_ZIP:
|
||||||
|
return new DirectImageInputStream(new InflaterInputStream(createStreamAdapter(stream, compressedLength)));
|
||||||
|
|
||||||
|
case PSD.COMPRESSION_ZIP_PREDICTION:
|
||||||
|
return new DirectImageInputStream(new HorizontalDeDifferencingStream(new InflaterInputStream(createStreamAdapter(stream, compressedLength)), columns, 1, bitsPerSample, BIG_ENDIAN));
|
||||||
|
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Unknown PSD compression: " + compression);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LazyPackBitsStreamEnumeration implements Enumeration<InputStream> {
|
||||||
|
private final ImageInputStream stream;
|
||||||
|
private final int[] byteCounts;
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
public LazyPackBitsStreamEnumeration(int[] byteCounts, ImageInputStream stream) {
|
||||||
|
this.byteCounts = byteCounts;
|
||||||
|
this.stream = stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasMoreElements() {
|
||||||
|
return index < byteCounts.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream nextElement() {
|
||||||
|
return new DecoderStream(createStreamAdapter(stream, byteCounts[index++]), new PackBitsDecoder());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+579
@@ -0,0 +1,579 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013, 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.plugins.psd;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||||
|
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
||||||
|
import com.twelvemonkeys.io.LittleEndianDataOutputStream;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.DataInput;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HorizontalDeDifferencingStreamTest
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: HorizontalDeDifferencingStreamTest.java,v 1.0 13.03.13 12:46 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class HorizontalDeDifferencingStreamTest {
|
||||||
|
@Test
|
||||||
|
public void testRead1SPP1BPS() throws IOException {
|
||||||
|
// 1 sample per pixel, 1 bits per sample (mono/indexed)
|
||||||
|
byte[] data = {
|
||||||
|
(byte) 0x80, 0x00, 0x00,
|
||||||
|
0x71, 0x11, 0x44,
|
||||||
|
};
|
||||||
|
|
||||||
|
InputStream stream = new HorizontalDeDifferencingStream(new ByteArrayInputStream(data), 24, 1, 1, ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
assertEquals(0x5e, stream.read());
|
||||||
|
assertEquals(0x1e, stream.read());
|
||||||
|
assertEquals(0x78, stream.read());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead1SPP2BPS() throws IOException {
|
||||||
|
// 1 sample per pixel, 2 bits per sample (gray/indexed)
|
||||||
|
byte[] data = {
|
||||||
|
(byte) 0xc0, 0x00, 0x00, 0x00,
|
||||||
|
0x71, 0x11, 0x44, (byte) 0xcc,
|
||||||
|
};
|
||||||
|
|
||||||
|
InputStream stream = new HorizontalDeDifferencingStream(new ByteArrayInputStream(data), 16, 1, 2, ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
assertEquals(0x41, stream.read());
|
||||||
|
assertEquals(0x6b, stream.read());
|
||||||
|
assertEquals(0x05, stream.read());
|
||||||
|
assertEquals(0x0f, stream.read());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead1SPP4BPS() throws IOException {
|
||||||
|
// 1 sample per pixel, 4 bits per sample (gray/indexed)
|
||||||
|
byte[] data = {
|
||||||
|
(byte) 0xf0, 0x00, 0x00, 0x00,
|
||||||
|
0x70, 0x11, 0x44, (byte) 0xcc,
|
||||||
|
0x00, 0x01, 0x10, (byte) 0xe0
|
||||||
|
};
|
||||||
|
|
||||||
|
InputStream stream = new HorizontalDeDifferencingStream(new ByteArrayInputStream(data), 8, 1, 4, ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
assertEquals(0x77, stream.read());
|
||||||
|
assertEquals(0x89, stream.read());
|
||||||
|
assertEquals(0xd1, stream.read());
|
||||||
|
assertEquals(0xd9, stream.read());
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
assertEquals(0x01, stream.read());
|
||||||
|
assertEquals(0x22, stream.read());
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead1SPP8BPS() throws IOException {
|
||||||
|
// 1 sample per pixel, 8 bits per sample (gray/indexed)
|
||||||
|
byte[] data = {
|
||||||
|
(byte) 0xff, 0, 0, 0,
|
||||||
|
0x7f, 1, 4, -4,
|
||||||
|
0x00, 127, 127, -127
|
||||||
|
};
|
||||||
|
|
||||||
|
InputStream stream = new HorizontalDeDifferencingStream(new ByteArrayInputStream(data), 4, 1, 8, ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x84, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
assertEquals(0xfe, stream.read());
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadArray1SPP8BPS() throws IOException {
|
||||||
|
// 1 sample per pixel, 8 bits per sample (gray/indexed)
|
||||||
|
byte[] data = {
|
||||||
|
(byte) 0xff, 0, 0, 0,
|
||||||
|
0x7f, 1, 4, -4,
|
||||||
|
0x00, 127, 127, -127
|
||||||
|
};
|
||||||
|
|
||||||
|
InputStream stream = new HorizontalDeDifferencingStream(new ByteArrayInputStream(data), 4, 1, 8, ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
byte[] result = new byte[data.length];
|
||||||
|
new DataInputStream(stream).readFully(result);
|
||||||
|
|
||||||
|
assertArrayEquals(
|
||||||
|
new byte[] {
|
||||||
|
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
|
||||||
|
0x7f, (byte) 0x80, (byte) 0x84, (byte) 0x80,
|
||||||
|
0x00, 0x7f, (byte) 0xfe, 0x7f,
|
||||||
|
},
|
||||||
|
result
|
||||||
|
);
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, stream.read(new byte[16]));
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead1SPP32BPS() throws IOException {
|
||||||
|
// 1 sample per pixel, 32 bits per sample (gray)
|
||||||
|
FastByteArrayOutputStream out = new FastByteArrayOutputStream(16);
|
||||||
|
DataOutput dataOut = new DataOutputStream(out);
|
||||||
|
dataOut.writeInt(0x00000000);
|
||||||
|
dataOut.writeInt(305419896);
|
||||||
|
dataOut.writeInt(305419896);
|
||||||
|
dataOut.writeInt(-610839792);
|
||||||
|
|
||||||
|
InputStream in = new HorizontalDeDifferencingStream(out.createInputStream(), 4, 1, 32, ByteOrder.BIG_ENDIAN);
|
||||||
|
DataInput dataIn = new DataInputStream(in);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0, dataIn.readInt());
|
||||||
|
assertEquals(305419896, dataIn.readInt());
|
||||||
|
assertEquals(610839792, dataIn.readInt());
|
||||||
|
assertEquals(0, dataIn.readInt());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, in.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead1SPP32BPSLittleEndian() throws IOException {
|
||||||
|
// 1 sample per pixel, 32 bits per sample (gray)
|
||||||
|
FastByteArrayOutputStream out = new FastByteArrayOutputStream(16);
|
||||||
|
DataOutput dataOut = new LittleEndianDataOutputStream(out);
|
||||||
|
dataOut.writeInt(0x00000000);
|
||||||
|
dataOut.writeInt(305419896);
|
||||||
|
dataOut.writeInt(305419896);
|
||||||
|
dataOut.writeInt(-610839792);
|
||||||
|
|
||||||
|
InputStream in = new HorizontalDeDifferencingStream(out.createInputStream(), 4, 1, 32, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
DataInput dataIn = new LittleEndianDataInputStream(in);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0, dataIn.readInt());
|
||||||
|
assertEquals(305419896, dataIn.readInt());
|
||||||
|
assertEquals(610839792, dataIn.readInt());
|
||||||
|
assertEquals(0, dataIn.readInt());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, in.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead1SPP64BPS() throws IOException {
|
||||||
|
// 1 sample per pixel, 64 bits per sample (gray)
|
||||||
|
FastByteArrayOutputStream out = new FastByteArrayOutputStream(32);
|
||||||
|
DataOutput dataOut = new DataOutputStream(out);
|
||||||
|
dataOut.writeLong(0x00000000);
|
||||||
|
dataOut.writeLong(81985529216486895L);
|
||||||
|
dataOut.writeLong(81985529216486895L);
|
||||||
|
dataOut.writeLong(-163971058432973790L);
|
||||||
|
|
||||||
|
InputStream in = new HorizontalDeDifferencingStream(out.createInputStream(), 4, 1, 64, ByteOrder.BIG_ENDIAN);
|
||||||
|
DataInput dataIn = new DataInputStream(in);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0, dataIn.readLong());
|
||||||
|
assertEquals(81985529216486895L, dataIn.readLong());
|
||||||
|
assertEquals(163971058432973790L, dataIn.readLong());
|
||||||
|
assertEquals(0, dataIn.readLong());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, in.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead1SPP64BPSLittleEndian() throws IOException {
|
||||||
|
// 1 sample per pixel, 64 bits per sample (gray)
|
||||||
|
FastByteArrayOutputStream out = new FastByteArrayOutputStream(32);
|
||||||
|
DataOutput dataOut = new LittleEndianDataOutputStream(out);
|
||||||
|
dataOut.writeLong(0x00000000);
|
||||||
|
dataOut.writeLong(81985529216486895L);
|
||||||
|
dataOut.writeLong(81985529216486895L);
|
||||||
|
dataOut.writeLong(-163971058432973790L);
|
||||||
|
|
||||||
|
InputStream in = new HorizontalDeDifferencingStream(out.createInputStream(), 4, 1, 64, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
DataInput dataIn = new LittleEndianDataInputStream(in);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0, dataIn.readLong());
|
||||||
|
assertEquals(81985529216486895L, dataIn.readLong());
|
||||||
|
assertEquals(163971058432973790L, dataIn.readLong());
|
||||||
|
assertEquals(0, dataIn.readLong());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, in.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead3SPP8BPS() throws IOException {
|
||||||
|
// 3 samples per pixel, 8 bits per sample (RGB)
|
||||||
|
byte[] data = {
|
||||||
|
(byte) 0xff, (byte) 0x00, (byte) 0x7f, -1, -1, -1, -4, -4, -4, 4, 4, 4,
|
||||||
|
0x7f, 0x7f, 0x7f, 1, 1, 1, 4, 4, 4, -4, -4, -4,
|
||||||
|
0x00, 0x00, 0x00, 127, -127, 0, -127, 127, 0, 0, 0, 127,
|
||||||
|
};
|
||||||
|
|
||||||
|
InputStream stream = new HorizontalDeDifferencingStream(new ByteArrayInputStream(data), 4, 3, 8, ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0xfe, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0x7e, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0xfa, stream.read());
|
||||||
|
assertEquals(0xfb, stream.read());
|
||||||
|
assertEquals(0x7a, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0xfe, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0x7e, stream.read());
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0x84, stream.read());
|
||||||
|
assertEquals(0x84, stream.read());
|
||||||
|
assertEquals(0x84, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
assertEquals(0x81, stream.read());
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead3SPP16BPS() throws IOException {
|
||||||
|
FastByteArrayOutputStream out = new FastByteArrayOutputStream(24);
|
||||||
|
DataOutput dataOut = new DataOutputStream(out);
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(4660);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(4660);
|
||||||
|
dataOut.writeShort(4660);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(4660);
|
||||||
|
dataOut.writeShort(-9320);
|
||||||
|
dataOut.writeShort(-60584);
|
||||||
|
dataOut.writeShort(-9320);
|
||||||
|
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(-60584);
|
||||||
|
dataOut.writeShort(-60584);
|
||||||
|
dataOut.writeShort(-60584);
|
||||||
|
|
||||||
|
InputStream in = new HorizontalDeDifferencingStream(out.createInputStream(), 4, 3, 16, ByteOrder.BIG_ENDIAN);
|
||||||
|
DataInput dataIn = new DataInputStream(in);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(4660, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(30292, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(4660, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(9320, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(60584, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(9320, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(30292, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(30292, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(30292, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(60584, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(60584, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(60584, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, in.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead3SPP16BPSLittleEndian() throws IOException {
|
||||||
|
FastByteArrayOutputStream out = new FastByteArrayOutputStream(24);
|
||||||
|
DataOutput dataOut = new LittleEndianDataOutputStream(out);
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(4660);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(4660);
|
||||||
|
dataOut.writeShort(4660);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(4660);
|
||||||
|
dataOut.writeShort(-9320);
|
||||||
|
dataOut.writeShort(-60584);
|
||||||
|
dataOut.writeShort(-9320);
|
||||||
|
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(0x0000);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(30292);
|
||||||
|
dataOut.writeShort(-60584);
|
||||||
|
dataOut.writeShort(-60584);
|
||||||
|
dataOut.writeShort(-60584);
|
||||||
|
|
||||||
|
InputStream in = new HorizontalDeDifferencingStream(out.createInputStream(), 4, 3, 16, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
DataInput dataIn = new LittleEndianDataInputStream(in);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(4660, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(30292, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(4660, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(9320, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(60584, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(9320, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(30292, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(30292, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(30292, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(60584, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(60584, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(60584, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
assertEquals(0, dataIn.readUnsignedShort());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, in.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead4SPP8BPS() throws IOException {
|
||||||
|
// 4 samples per pixel, 8 bits per sample (RGBA)
|
||||||
|
byte[] data = {
|
||||||
|
(byte) 0xff, (byte) 0x00, (byte) 0x7f, 0x00, -1, -1, -1, -1, -4, -4, -4, -4, 4, 4, 4, 4,
|
||||||
|
0x7f, 0x7f, 0x7f, 0x7f, 1, 1, 1, 1, 4, 4, 4, 4, -4, -4, -4, -4,
|
||||||
|
};
|
||||||
|
|
||||||
|
InputStream stream = new HorizontalDeDifferencingStream(new ByteArrayInputStream(data), 4, 4, 8, ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
// Row 1
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
assertEquals(0x00, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0xfe, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0x7e, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0xfa, stream.read());
|
||||||
|
assertEquals(0xfb, stream.read());
|
||||||
|
assertEquals(0x7a, stream.read());
|
||||||
|
assertEquals(0xfb, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0xfe, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
assertEquals(0x7e, stream.read());
|
||||||
|
assertEquals(0xff, stream.read());
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
assertEquals(0x7f, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0x84, stream.read());
|
||||||
|
assertEquals(0x84, stream.read());
|
||||||
|
assertEquals(0x84, stream.read());
|
||||||
|
assertEquals(0x84, stream.read());
|
||||||
|
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
assertEquals(0x80, stream.read());
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadArray4SPP8BPS() throws IOException {
|
||||||
|
// 4 samples per pixel, 8 bits per sample (RGBA)
|
||||||
|
byte[] data = {
|
||||||
|
(byte) 0xff, (byte) 0x00, (byte) 0x7f, 0x00, -1, -1, -1, -1, -4, -4, -4, -4, 4, 4, 4, 4,
|
||||||
|
0x7f, 0x7f, 0x7f, 0x7f, 1, 1, 1, 1, 4, 4, 4, 4, -4, -4, -4, -4,
|
||||||
|
};
|
||||||
|
|
||||||
|
InputStream stream = new HorizontalDeDifferencingStream(new ByteArrayInputStream(data), 4, 4, 8, ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
|
byte[] result = new byte[data.length];
|
||||||
|
new DataInputStream(stream).readFully(result);
|
||||||
|
|
||||||
|
assertArrayEquals(
|
||||||
|
new byte[] {
|
||||||
|
(byte) 0xff, 0x00, 0x7f, 0x00,
|
||||||
|
(byte) 0xfe, (byte) 0xff, 0x7e, (byte) 0xff,
|
||||||
|
(byte) 0xfa, (byte) 0xfb, 0x7a, (byte) 0xfb,
|
||||||
|
(byte) 0xfe, (byte) 0xff, 0x7e, (byte) 0xff,
|
||||||
|
|
||||||
|
0x7f, 0x7f, 0x7f, 0x7f,
|
||||||
|
(byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
|
||||||
|
(byte) 0x84, (byte) 0x84, (byte) 0x84, (byte) 0x84,
|
||||||
|
(byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
|
||||||
|
},
|
||||||
|
result
|
||||||
|
);
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
assertEquals(-1, stream.read(new byte[16]));
|
||||||
|
assertEquals(-1, stream.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
+104
-2
@@ -45,10 +45,14 @@ import javax.imageio.metadata.IIOMetadataNode;
|
|||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.color.*;
|
||||||
|
import java.awt.image.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@@ -618,4 +622,102 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
|
|||||||
assertFalse(layer1.isDivider);
|
assertFalse(layer1.isDivider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test16bitLr16AndZIPPredictor() throws IOException {
|
||||||
|
PSDImageReader imageReader = createReader();
|
||||||
|
|
||||||
|
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psd/fruit-cmyk-MeSa-resource.psd"))) {
|
||||||
|
imageReader.setInput(stream);
|
||||||
|
|
||||||
|
assertEquals(5, imageReader.getNumImages(true));
|
||||||
|
|
||||||
|
assertEquals(400, imageReader.getWidth(2));
|
||||||
|
assertEquals(191, imageReader.getHeight(2));
|
||||||
|
|
||||||
|
BufferedImage layer2 = imageReader.read(2);// Read the 16 bit ZIP Predictor based layer
|
||||||
|
assertNotNull(layer2);
|
||||||
|
assertEquals(400, layer2.getWidth());
|
||||||
|
assertEquals(191, layer2.getHeight());
|
||||||
|
assertEquals(ColorSpace.TYPE_CMYK, layer2.getColorModel().getColorSpace().getType());
|
||||||
|
assertEquals(5, layer2.getColorModel().getNumComponents());
|
||||||
|
|
||||||
|
// For cross-platform testing: as the PSD does not have embedded CMYK profile, we'll use built-in RGB conversion
|
||||||
|
ColorModel cmykAlpha = new ComponentColorModel(new FakeCMYKColorSpace(), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_USHORT);
|
||||||
|
layer2 = new BufferedImage(cmykAlpha, layer2.getRaster(), cmykAlpha.isAlphaPremultiplied(), null);
|
||||||
|
|
||||||
|
assertRGBEquals("RGB differ at (0,0)", 0xff060808, layer2.getRGB(0, 0), 4);
|
||||||
|
assertRGBEquals("RGB differ at (399,0)", 0xff060808, layer2.getRGB(399, 0), 4);
|
||||||
|
assertRGBEquals("RGB differ at (200,95)", 0x00ffffff, layer2.getRGB(200, 95), 4); // Transparent
|
||||||
|
assertRGBEquals("RGB differ at (0,191)", 0xff060808, layer2.getRGB(0, 190), 4);
|
||||||
|
assertRGBEquals("RGB differ at (399,191)", 0xff060808, layer2.getRGB(399, 190), 4);
|
||||||
|
|
||||||
|
assertEquals(400, imageReader.getWidth(3));
|
||||||
|
assertEquals(191, imageReader.getHeight(3));
|
||||||
|
|
||||||
|
BufferedImage layer3 = imageReader.read(3);// Read the 16 bit ZIP Predictor based layer
|
||||||
|
assertNotNull(layer3);
|
||||||
|
assertEquals(400, layer3.getWidth());
|
||||||
|
assertEquals(191, layer3.getHeight());
|
||||||
|
assertEquals(ColorSpace.TYPE_CMYK, layer3.getColorModel().getColorSpace().getType());
|
||||||
|
assertEquals(5, layer3.getColorModel().getNumComponents());
|
||||||
|
|
||||||
|
// For cross-platform testing: as the PSD does not have embedded CMYK profile, we'll use built-in RGB conversion
|
||||||
|
layer3 = new BufferedImage(cmykAlpha, layer3.getRaster(), cmykAlpha.isAlphaPremultiplied(), null);
|
||||||
|
|
||||||
|
assertRGBEquals("RGB differ at (0,0)", 0xfff5cb0c, layer3.getRGB(0, 0), 4);
|
||||||
|
assertRGBEquals("RGB differ at (399,0)", 0xfff5cb0c, layer3.getRGB(399, 0), 4);
|
||||||
|
assertRGBEquals("RGB differ at (200,95)", 0xffff152a, layer3.getRGB(200, 95), 4); // Red
|
||||||
|
assertRGBEquals("RGB differ at (0,191)", 0xfff5cb0c, layer3.getRGB(0, 190), 4);
|
||||||
|
assertRGBEquals("RGB differ at (399,191)", 0xfff5cb0c, layer3.getRGB(399, 190), 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test32bitLr32AndZIPPredictor() throws IOException {
|
||||||
|
PSDImageReader imageReader = createReader();
|
||||||
|
|
||||||
|
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psd/32bit5x5.psd"))) {
|
||||||
|
imageReader.setInput(stream);
|
||||||
|
|
||||||
|
assertEquals(4, imageReader.getNumImages(true));
|
||||||
|
|
||||||
|
assertEquals(5, imageReader.getWidth(1));
|
||||||
|
assertEquals(5, imageReader.getHeight(1));
|
||||||
|
|
||||||
|
BufferedImage image = imageReader.read(1);// Read the 32 bit ZIP Predictor based layer
|
||||||
|
assertNotNull(image);
|
||||||
|
assertEquals(5, image.getWidth());
|
||||||
|
assertEquals(5, image.getHeight());
|
||||||
|
|
||||||
|
assertRGBEquals("RGB differ at (0,0)", 0xff888888, image.getRGB(0, 0), 4);
|
||||||
|
assertRGBEquals("RGB differ at (4,4)", 0xff888888, image.getRGB(4, 4), 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final static class FakeCMYKColorSpace extends ColorSpace {
|
||||||
|
FakeCMYKColorSpace() {
|
||||||
|
super(ColorSpace.TYPE_CMYK, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] toRGB(float[] cmyk) {
|
||||||
|
return new float[] {
|
||||||
|
(1 - cmyk[0]) * (1 - cmyk[3]),
|
||||||
|
(1 - cmyk[1]) * (1 - cmyk[3]),
|
||||||
|
(1 - cmyk[2]) * (1 - cmyk[3])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] fromRGB(float[] rgb) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] toCIEXYZ(float[] cmyk) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] fromCIEXYZ(float[] cieXYZ) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+303
@@ -0,0 +1,303 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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.plugins.psd;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.imageio.stream.ImageInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.imageio.plugins.psd.PSDUtil.createDecompressorStream;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class PSDUtilDecompressorStreamTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUncompressed() throws IOException {
|
||||||
|
// Data represents 3 x 3 raster with 8 bit samples, all 0x7f's
|
||||||
|
byte[] data = new byte[] {
|
||||||
|
0x7f, 0x7f, 0x7f,
|
||||||
|
0x7f, 0x7f, 0x7f,
|
||||||
|
0x7f, 0x7f, 0x7f
|
||||||
|
};
|
||||||
|
try (ImageInputStream input = createDecompressorStream(new ByteArrayImageInputStream(data), PSD.COMPRESSION_NONE, 3, 8, null, 9)) {
|
||||||
|
byte[] row = new byte[3];
|
||||||
|
|
||||||
|
for (int y = 0; y < 3; y++) {
|
||||||
|
input.readFully(row);
|
||||||
|
|
||||||
|
for (byte b : row) {
|
||||||
|
assertEquals((byte) 0x7f, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(-1, input.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPackBits() throws IOException {
|
||||||
|
// Data represents 3 x 3 raster with 8 bit samples, all 42's
|
||||||
|
byte[] packBitsData = {
|
||||||
|
-2, 42, // 3 byte run
|
||||||
|
2, 42, 42, 42, // 3 byte literal
|
||||||
|
0, 42, -1, 42 // 1 byte literal + 2 byte run
|
||||||
|
};
|
||||||
|
try (ImageInputStream input = createDecompressorStream(new ByteArrayImageInputStream(packBitsData), PSD.COMPRESSION_RLE, 3, 8, new int[] {2, 4, 4}, packBitsData.length)) {
|
||||||
|
byte[] row = new byte[3];
|
||||||
|
|
||||||
|
for (int y = 0; y < 3; y++) {
|
||||||
|
input.readFully(row);
|
||||||
|
|
||||||
|
for (byte b : row) {
|
||||||
|
assertEquals((byte) 42, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(-1, input.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testZIP() throws IOException {
|
||||||
|
// Data represents 710 x 512 raster with 16 bit samples, first two 0xFF samples, then all 0x00's
|
||||||
|
try (ImageInputStream input = createDecompressorStream(new ByteArrayImageInputStream(ZIP_DATA), PSD.COMPRESSION_ZIP, 710, 16, null, ZIP_DATA.length)) {
|
||||||
|
byte[] row = new byte[710 * 2];
|
||||||
|
|
||||||
|
for (int y = 0; y < 512; y++) {
|
||||||
|
input.readFully(row);
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
assertEquals((byte) 0xff, row[i]);
|
||||||
|
}
|
||||||
|
for (int i = 2; i < row.length; i++) {
|
||||||
|
assertEquals((byte) 0x00, row[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(-1, input.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testZIPPredictor() throws IOException {
|
||||||
|
// Data represents 710 x 512 raster with 16 bit samples, all 0xFF's
|
||||||
|
try (ImageInputStream input = createDecompressorStream(new ByteArrayImageInputStream(ZIP_DATA), PSD.COMPRESSION_ZIP_PREDICTION, 710, 16, null, ZIP_DATA.length)) {
|
||||||
|
byte[] row = new byte[710 * 2];
|
||||||
|
|
||||||
|
for (int y = 0; y < 512; y++) {
|
||||||
|
input.readFully(row);
|
||||||
|
|
||||||
|
for (byte b : row) {
|
||||||
|
assertEquals((byte) 0xff, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(-1, input.read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final byte[] ZIP_DATA = new byte[] {
|
||||||
|
(byte) 0x48, (byte) 0x89, (byte) 0xEC, (byte) 0xD4, (byte) 0x31, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x0C, (byte) 0xC3,
|
||||||
|
(byte) 0xA0, (byte) 0xF9, (byte) 0x37, (byte) 0xDD, (byte) 0xC9, (byte) 0xC8, (byte) 0x03, (byte) 0x22, (byte) 0xD8, (byte) 0x0E,
|
||||||
|
(byte) 0x80, (byte) 0xD8, (byte) 0x5C, (byte) 0x0C, (byte) 0x90, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5,
|
||||||
|
(byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31,
|
||||||
|
(byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C,
|
||||||
|
(byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03,
|
||||||
|
(byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00,
|
||||||
|
(byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40,
|
||||||
|
(byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0,
|
||||||
|
(byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4,
|
||||||
|
(byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D,
|
||||||
|
(byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF,
|
||||||
|
(byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73,
|
||||||
|
(byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C,
|
||||||
|
(byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17,
|
||||||
|
(byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5,
|
||||||
|
(byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31,
|
||||||
|
(byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C,
|
||||||
|
(byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03,
|
||||||
|
(byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00,
|
||||||
|
(byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40,
|
||||||
|
(byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0,
|
||||||
|
(byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4,
|
||||||
|
(byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D,
|
||||||
|
(byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF,
|
||||||
|
(byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73,
|
||||||
|
(byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C,
|
||||||
|
(byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17,
|
||||||
|
(byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5,
|
||||||
|
(byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31,
|
||||||
|
(byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C,
|
||||||
|
(byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03,
|
||||||
|
(byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00,
|
||||||
|
(byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40,
|
||||||
|
(byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0,
|
||||||
|
(byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4,
|
||||||
|
(byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D,
|
||||||
|
(byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF,
|
||||||
|
(byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73,
|
||||||
|
(byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C,
|
||||||
|
(byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17,
|
||||||
|
(byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5,
|
||||||
|
(byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31,
|
||||||
|
(byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C,
|
||||||
|
(byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03,
|
||||||
|
(byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00,
|
||||||
|
(byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40,
|
||||||
|
(byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0,
|
||||||
|
(byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4,
|
||||||
|
(byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D,
|
||||||
|
(byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF,
|
||||||
|
(byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73,
|
||||||
|
(byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C,
|
||||||
|
(byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17,
|
||||||
|
(byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5,
|
||||||
|
(byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31,
|
||||||
|
(byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C,
|
||||||
|
(byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03,
|
||||||
|
(byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00,
|
||||||
|
(byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40,
|
||||||
|
(byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0,
|
||||||
|
(byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4,
|
||||||
|
(byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D,
|
||||||
|
(byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF,
|
||||||
|
(byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73,
|
||||||
|
(byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C,
|
||||||
|
(byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17,
|
||||||
|
(byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5,
|
||||||
|
(byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31,
|
||||||
|
(byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C,
|
||||||
|
(byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03,
|
||||||
|
(byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00,
|
||||||
|
(byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40,
|
||||||
|
(byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0,
|
||||||
|
(byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4,
|
||||||
|
(byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D,
|
||||||
|
(byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF,
|
||||||
|
(byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73,
|
||||||
|
(byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C,
|
||||||
|
(byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17,
|
||||||
|
(byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5,
|
||||||
|
(byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31,
|
||||||
|
(byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C,
|
||||||
|
(byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03,
|
||||||
|
(byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00,
|
||||||
|
(byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40,
|
||||||
|
(byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0,
|
||||||
|
(byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4,
|
||||||
|
(byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D,
|
||||||
|
(byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF,
|
||||||
|
(byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73,
|
||||||
|
(byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C,
|
||||||
|
(byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17,
|
||||||
|
(byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5,
|
||||||
|
(byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31,
|
||||||
|
(byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C,
|
||||||
|
(byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03,
|
||||||
|
(byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00,
|
||||||
|
(byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40,
|
||||||
|
(byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0,
|
||||||
|
(byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4,
|
||||||
|
(byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D,
|
||||||
|
(byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF,
|
||||||
|
(byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73,
|
||||||
|
(byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C,
|
||||||
|
(byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17,
|
||||||
|
(byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5,
|
||||||
|
(byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31,
|
||||||
|
(byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C,
|
||||||
|
(byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0x03,
|
||||||
|
(byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40, (byte) 0xCF, (byte) 0xC5, (byte) 0x00,
|
||||||
|
(byte) 0x3D, (byte) 0x17, (byte) 0x03, (byte) 0xF4, (byte) 0x5C, (byte) 0x0C, (byte) 0xD0, (byte) 0x73, (byte) 0x31, (byte) 0x40,
|
||||||
|
(byte) 0xCF, (byte) 0xC5, (byte) 0x00, (byte) 0x3D, (byte) 0x17, (byte) 0xC3, (byte) 0xB3, (byte) 0x53, (byte) 0xC7, (byte) 0x02,
|
||||||
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x83, (byte) 0xFC, (byte) 0xAD, (byte) 0x87, (byte) 0xB1, (byte) 0xA7,
|
||||||
|
(byte) 0x20, (byte) 0x82, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9,
|
||||||
|
(byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E,
|
||||||
|
(byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B,
|
||||||
|
(byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62,
|
||||||
|
(byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18,
|
||||||
|
(byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06,
|
||||||
|
(byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01,
|
||||||
|
(byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80,
|
||||||
|
(byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0,
|
||||||
|
(byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8,
|
||||||
|
(byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E,
|
||||||
|
(byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F,
|
||||||
|
(byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7,
|
||||||
|
(byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9,
|
||||||
|
(byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E,
|
||||||
|
(byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B,
|
||||||
|
(byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62,
|
||||||
|
(byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18,
|
||||||
|
(byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06,
|
||||||
|
(byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01,
|
||||||
|
(byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80,
|
||||||
|
(byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0,
|
||||||
|
(byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8,
|
||||||
|
(byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E,
|
||||||
|
(byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F,
|
||||||
|
(byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7,
|
||||||
|
(byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9,
|
||||||
|
(byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E,
|
||||||
|
(byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B,
|
||||||
|
(byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62,
|
||||||
|
(byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18,
|
||||||
|
(byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06,
|
||||||
|
(byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01,
|
||||||
|
(byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80,
|
||||||
|
(byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0,
|
||||||
|
(byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8,
|
||||||
|
(byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E,
|
||||||
|
(byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F,
|
||||||
|
(byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7,
|
||||||
|
(byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9,
|
||||||
|
(byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E,
|
||||||
|
(byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B,
|
||||||
|
(byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62,
|
||||||
|
(byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18,
|
||||||
|
(byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06,
|
||||||
|
(byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01,
|
||||||
|
(byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80,
|
||||||
|
(byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0,
|
||||||
|
(byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8,
|
||||||
|
(byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E,
|
||||||
|
(byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F,
|
||||||
|
(byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7,
|
||||||
|
(byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9,
|
||||||
|
(byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B, (byte) 0x01, (byte) 0x7E, (byte) 0x2E,
|
||||||
|
(byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0xE7, (byte) 0x62, (byte) 0x80, (byte) 0x9F, (byte) 0x8B,
|
||||||
|
(byte) 0x01, (byte) 0x7E, (byte) 0x2E, (byte) 0x06, (byte) 0xF8, (byte) 0xB9, (byte) 0x18, (byte) 0xE0, (byte) 0x97, (byte) 0x00,
|
||||||
|
(byte) 0x03, (byte) 0x00, (byte) 0x3E, (byte) 0xEE, (byte) 0xFC, (byte) 0x2E
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-reference</artifactId>
|
<artifactId>imageio-reference</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: JDK Reference Tests</name>
|
<name>TwelveMonkeys :: ImageIO :: JDK Reference Tests</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-sgi</artifactId>
|
<artifactId>imageio-sgi</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: SGI plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: SGI plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-tga</artifactId>
|
<artifactId>imageio-tga</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: TGA plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: TGA plugin</name>
|
||||||
|
|||||||
+1
-20
@@ -33,10 +33,10 @@ package com.twelvemonkeys.imageio.plugins.tga;
|
|||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
import static com.twelvemonkeys.imageio.plugins.tga.TGA.EXT_AREA_SIZE;
|
import static com.twelvemonkeys.imageio.plugins.tga.TGA.EXT_AREA_SIZE;
|
||||||
|
import static com.twelvemonkeys.imageio.plugins.tga.TGAHeader.readString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TGAExtensions.
|
* TGAExtensions.
|
||||||
@@ -149,25 +149,6 @@ final class TGAExtensions {
|
|||||||
return calendar;
|
return calendar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String readString(final ImageInputStream stream, final int maxLength) throws IOException {
|
|
||||||
byte[] data = new byte[maxLength];
|
|
||||||
stream.readFully(data);
|
|
||||||
|
|
||||||
return asZeroTerminatedASCIIString(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String asZeroTerminatedASCIIString(final byte[] data) {
|
|
||||||
int len = data.length;
|
|
||||||
|
|
||||||
for (int i = 0; i < data.length; i++) {
|
|
||||||
if (data[i] == 0) {
|
|
||||||
len = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new String(data, 0, len, StandardCharsets.US_ASCII);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasAlpha() {
|
public boolean hasAlpha() {
|
||||||
switch (attributeType) {
|
switch (attributeType) {
|
||||||
case 3:
|
case 3:
|
||||||
|
|||||||
+21
-4
@@ -253,10 +253,7 @@ final class TGAHeader {
|
|||||||
|
|
||||||
// Image ID section, not *really* part of the header, but let's get rid of it...
|
// Image ID section, not *really* part of the header, but let's get rid of it...
|
||||||
if (imageIdLength > 0) {
|
if (imageIdLength > 0) {
|
||||||
byte[] idBytes = new byte[imageIdLength];
|
header.identification = readString(imageInput, imageIdLength);
|
||||||
imageInput.readFully(idBytes);
|
|
||||||
|
|
||||||
header.identification = new String(idBytes, StandardCharsets.US_ASCII);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Color map, not *really* part of the header
|
// Color map, not *really* part of the header
|
||||||
@@ -267,6 +264,26 @@ final class TGAHeader {
|
|||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String readString(final ImageInputStream stream, final int maxLength) throws IOException {
|
||||||
|
byte[] data = new byte[maxLength];
|
||||||
|
stream.readFully(data);
|
||||||
|
|
||||||
|
return asZeroTerminatedASCIIString(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String asZeroTerminatedASCIIString(final byte[] data) {
|
||||||
|
int len = data.length;
|
||||||
|
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
if (data[i] == 0) {
|
||||||
|
len = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(data, 0, len, StandardCharsets.US_ASCII);
|
||||||
|
}
|
||||||
|
|
||||||
private static IndexColorModel readColorMap(final DataInput stream, final TGAHeader header) throws IOException {
|
private static IndexColorModel readColorMap(final DataInput stream, final TGAHeader header) throws IOException {
|
||||||
int size = header.colorMapSize;
|
int size = header.colorMapSize;
|
||||||
int depth = header.colorMapDepth;
|
int depth = header.colorMapDepth;
|
||||||
|
|||||||
+7
-12
@@ -136,7 +136,7 @@ final class TGAImageReader extends ImageReaderBase {
|
|||||||
case TGA.IMAGETYPE_TRUECOLOR_RLE:
|
case TGA.IMAGETYPE_TRUECOLOR_RLE:
|
||||||
ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||||
|
|
||||||
boolean hasAlpha = header.getAttributeBits() > 0 && extensions != null && extensions.hasAlpha();
|
boolean hasAlpha = header.getAttributeBits() > 0 && (extensions == null || extensions.hasAlpha());
|
||||||
boolean isAlphaPremultiplied = extensions != null && extensions.isAlphaPremultiplied();
|
boolean isAlphaPremultiplied = extensions != null && extensions.isAlphaPremultiplied();
|
||||||
|
|
||||||
switch (header.getPixelDepth()) {
|
switch (header.getPixelDepth()) {
|
||||||
@@ -393,27 +393,22 @@ final class TGAImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
// Read header
|
// Read header
|
||||||
header = TGAHeader.read(imageInput);
|
header = TGAHeader.read(imageInput);
|
||||||
|
|
||||||
// System.err.println("header: " + header);
|
|
||||||
|
|
||||||
imageInput.flushBefore(imageInput.getStreamPosition());
|
imageInput.flushBefore(imageInput.getStreamPosition());
|
||||||
|
|
||||||
// Read footer, if 2.0 format (ends with TRUEVISION-XFILE\0)
|
// Read footer, if 2.0 format (ends with TRUEVISION-XFILE\0)
|
||||||
skipToEnd(imageInput);
|
skipToEnd(imageInput);
|
||||||
imageInput.seek(imageInput.getStreamPosition() - 26);
|
imageInput.seek(imageInput.getStreamPosition() - 26);
|
||||||
|
|
||||||
long extOffset = imageInput.readInt();
|
long extOffset = imageInput.readUnsignedInt();
|
||||||
/*long devOffset = */imageInput.readInt(); // Ignored for now
|
/*long devOffset = */imageInput.readUnsignedInt(); // Ignored for now
|
||||||
|
|
||||||
byte[] magic = new byte[18];
|
byte[] magic = new byte[18];
|
||||||
imageInput.readFully(magic);
|
imageInput.readFully(magic);
|
||||||
|
|
||||||
if (Arrays.equals(magic, TGA.MAGIC)) {
|
if (Arrays.equals(magic, TGA.MAGIC) && extOffset > 0) {
|
||||||
if (extOffset > 0) {
|
imageInput.seek(extOffset);
|
||||||
imageInput.seek(extOffset);
|
int extSize = imageInput.readUnsignedShort();
|
||||||
int extSize = imageInput.readUnsignedShort();
|
extensions = extSize == 0 ? null : TGAExtensions.read(imageInput, extSize);
|
||||||
extensions = extSize == 0 ? null : TGAExtensions.read(imageInput, extSize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-5
@@ -291,12 +291,10 @@ final class TGAMetadata extends AbstractMetadata {
|
|||||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
IIOMetadataNode text = new IIOMetadataNode("Text");
|
||||||
|
|
||||||
// NOTE: Names corresponds to equivalent fields in TIFF
|
// NOTE: Names corresponds to equivalent fields in TIFF
|
||||||
if (header.getIdentification() != null && !header.getIdentification().isEmpty()) {
|
appendTextEntry(text, "DocumentName", header.getIdentification());
|
||||||
appendTextEntry(text, "DocumentName", header.getIdentification());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extensions != null) {
|
if (extensions != null) {
|
||||||
appendTextEntry(text, "Software", extensions.getSoftwareVersion() == null ? extensions.getSoftware() : extensions.getSoftware() + " " + extensions.getSoftwareVersion());
|
appendTextEntry(text, "Software", extensions.getSoftwareVersion() == null ? extensions.getSoftware() : (extensions.getSoftware() + " " + extensions.getSoftwareVersion()));
|
||||||
appendTextEntry(text, "Artist", extensions.getAuthorName());
|
appendTextEntry(text, "Artist", extensions.getAuthorName());
|
||||||
appendTextEntry(text, "UserComment", extensions.getAuthorComments());
|
appendTextEntry(text, "UserComment", extensions.getAuthorComments());
|
||||||
}
|
}
|
||||||
@@ -305,7 +303,7 @@ final class TGAMetadata extends AbstractMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void appendTextEntry(final IIOMetadataNode parent, final String keyword, final String value) {
|
private void appendTextEntry(final IIOMetadataNode parent, final String keyword, final String value) {
|
||||||
if (value != null) {
|
if (value != null && !value.isEmpty()) {
|
||||||
IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
|
IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
|
||||||
parent.appendChild(textEntry);
|
parent.appendChild(textEntry);
|
||||||
textEntry.setAttribute("keyword", keyword);
|
textEntry.setAttribute("keyword", keyword);
|
||||||
|
|||||||
+80
-7
@@ -33,17 +33,23 @@ package com.twelvemonkeys.imageio.plugins.tga;
|
|||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageReadParam;
|
import javax.imageio.ImageReadParam;
|
||||||
import javax.imageio.ImageReader;
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||||
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TGAImageReaderTest
|
* TGAImageReaderTest
|
||||||
@@ -76,20 +82,20 @@ public class TGAImageReaderTest extends ImageReaderAbstractTest<TGAImageReader>
|
|||||||
new TestData(getClassLoaderResource("/tga/CTC24.TGA"), new Dimension(128, 128)), // RLE compressed 24 bit BGR
|
new TestData(getClassLoaderResource("/tga/CTC24.TGA"), new Dimension(128, 128)), // RLE compressed 24 bit BGR
|
||||||
new TestData(getClassLoaderResource("/tga/CTC32.TGA"), new Dimension(128, 128)), // RLE compressed 32 bit BGRA
|
new TestData(getClassLoaderResource("/tga/CTC32.TGA"), new Dimension(128, 128)), // RLE compressed 32 bit BGRA
|
||||||
|
|
||||||
// Further samples from http://www.fileformat.info/format/tga/sample/index.htm
|
// More samples from http://www.fileformat.info/format/tga/sample/index.htm
|
||||||
new TestData(getClassLoaderResource("/tga/FLAG_B16.TGA"), new Dimension(124, 124)), // Uncompressed 16 bit BGR bottom/up
|
new TestData(getClassLoaderResource("/tga/FLAG_B16.TGA"), new Dimension(124, 124)), // Uncompressed 16 bit BGR bottom/up
|
||||||
new TestData(getClassLoaderResource("/tga/FLAG_B24.TGA"), new Dimension(124, 124)), // Uncompressed 24 bit BGR bottom/up
|
new TestData(getClassLoaderResource("/tga/FLAG_B24.TGA"), new Dimension(124, 124)), // Uncompressed 24 bit BGR bottom/up
|
||||||
new TestData(getClassLoaderResource("/tga/FLAG_B32.TGA"), new Dimension(124, 124)), // Uncompressed 32 bit BGRA bottom/up
|
new TestData(getClassLoaderResource("/tga/FLAG_B32.TGA"), new Dimension(124, 124)), // Uncompressed 32 bit BGRX bottom/up
|
||||||
new TestData(getClassLoaderResource("/tga/FLAG_T16.TGA"), new Dimension(124, 124)), // Uncompressed 16 bit BGR top/down
|
new TestData(getClassLoaderResource("/tga/FLAG_T16.TGA"), new Dimension(124, 124)), // Uncompressed 16 bit BGR top/down
|
||||||
// new TestData(getClassLoaderResource("/tga/FLAG_T24.TGA"), new Dimension(124, 124)), // Uncompressed 24 bit BGR top/down (missing from file set)
|
// new TestData(getClassLoaderResource("/tga/FLAG_T24.TGA"), new Dimension(124, 124)), // Uncompressed 24 bit BGR top/down (missing from file set)
|
||||||
new TestData(getClassLoaderResource("/tga/FLAG_T32.TGA"), new Dimension(124, 124)), // Uncompressed 32 bit BGRA top/down
|
new TestData(getClassLoaderResource("/tga/FLAG_T32.TGA"), new Dimension(124, 124)), // Uncompressed 32 bit BGRX top/down
|
||||||
|
|
||||||
new TestData(getClassLoaderResource("/tga/XING_B16.TGA"), new Dimension(240, 164)), // Uncompressed 16 bit BGR bottom/up
|
new TestData(getClassLoaderResource("/tga/XING_B16.TGA"), new Dimension(240, 164)), // Uncompressed 16 bit BGR bottom/up
|
||||||
new TestData(getClassLoaderResource("/tga/XING_B24.TGA"), new Dimension(240, 164)), // Uncompressed 24 bit BGR bottom/up
|
new TestData(getClassLoaderResource("/tga/XING_B24.TGA"), new Dimension(240, 164)), // Uncompressed 24 bit BGR bottom/up
|
||||||
new TestData(getClassLoaderResource("/tga/XING_B32.TGA"), new Dimension(240, 164)), // Uncompressed 32 bit BGRA bottom/up
|
new TestData(getClassLoaderResource("/tga/XING_B32.TGA"), new Dimension(240, 164)), // Uncompressed 32 bit BGRX bottom/up
|
||||||
new TestData(getClassLoaderResource("/tga/XING_T16.TGA"), new Dimension(240, 164)), // Uncompressed 16 bit BGR top/down
|
new TestData(getClassLoaderResource("/tga/XING_T16.TGA"), new Dimension(240, 164)), // Uncompressed 16 bit BGR top/down
|
||||||
new TestData(getClassLoaderResource("/tga/XING_T24.TGA"), new Dimension(240, 164)), // Uncompressed 24 bit BGR top/down
|
new TestData(getClassLoaderResource("/tga/XING_T24.TGA"), new Dimension(240, 164)), // Uncompressed 24 bit BGR top/down
|
||||||
new TestData(getClassLoaderResource("/tga/XING_T32.TGA"), new Dimension(240, 164)), // Uncompressed 32 bit BGRA top/down
|
new TestData(getClassLoaderResource("/tga/XING_T32.TGA"), new Dimension(240, 164)), // Uncompressed 32 bit BGRX top/down
|
||||||
|
|
||||||
new TestData(getClassLoaderResource("/tga/autodesk-3dsmax-extsize494.tga"), new Dimension(440, 200)), // RLE compressed 32 bit BGRA bottom/up, with extension area size 494
|
new TestData(getClassLoaderResource("/tga/autodesk-3dsmax-extsize494.tga"), new Dimension(440, 200)), // RLE compressed 32 bit BGRA bottom/up, with extension area size 494
|
||||||
|
|
||||||
@@ -97,7 +103,8 @@ public class TGAImageReaderTest extends ImageReaderAbstractTest<TGAImageReader>
|
|||||||
new TestData(getClassLoaderResource("/tga/monochrome16_top_left_rle.tga"), new Dimension(64, 64)), // RLE compressed 16 bit monochrome
|
new TestData(getClassLoaderResource("/tga/monochrome16_top_left_rle.tga"), new Dimension(64, 64)), // RLE compressed 16 bit monochrome
|
||||||
|
|
||||||
new TestData(getClassLoaderResource("/tga/692c33d1-d0c3-4fe2-a059-f199d063bc7a.tga"), new Dimension(256, 256)), // Uncompressed BGR, with colorMapDepth set to 24
|
new TestData(getClassLoaderResource("/tga/692c33d1-d0c3-4fe2-a059-f199d063bc7a.tga"), new Dimension(256, 256)), // Uncompressed BGR, with colorMapDepth set to 24
|
||||||
new TestData(getClassLoaderResource("/tga/0112eccd-2c29-4368-bcef-59c823b6e5d1.tga"), new Dimension(256, 256)) // RLE compressed BGR, with extension area size 0
|
new TestData(getClassLoaderResource("/tga/0112eccd-2c29-4368-bcef-59c823b6e5d1.tga"), new Dimension(256, 256)), // RLE compressed BGR, with extension area size 0
|
||||||
|
new TestData(getClassLoaderResource("/tga/alpha-no-extension.tga"), new Dimension(167, 64)) // Uncompressed BGRA, without extension area
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,4 +144,70 @@ public class TGAImageReaderTest extends ImageReaderAbstractTest<TGAImageReader>
|
|||||||
|
|
||||||
reader.dispose();
|
reader.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAlpha() throws IOException {
|
||||||
|
ImageReader reader = createReader();
|
||||||
|
|
||||||
|
// These samples have "attribute bits" that are *NOT* alpha, according to the extension area
|
||||||
|
for (String sample : Arrays.asList("/tga/UTC16.TGA", "/tga/UTC32.TGA", "/tga/CTC16.TGA", "/tga/CTC32.TGA")) {
|
||||||
|
try (ImageInputStream input = ImageIO.createImageInputStream(getClassLoaderResource(sample))) {
|
||||||
|
reader.setInput(input);
|
||||||
|
|
||||||
|
BufferedImage image = reader.read(0);
|
||||||
|
|
||||||
|
String message = String.format("RGB value differs for sample '%s'", sample);
|
||||||
|
assertRGBEquals(message, 0xffff0000, image.getRGB(0, 0), 0);
|
||||||
|
assertRGBEquals(message, 0xff00ff00, image.getRGB(8, 0), 0);
|
||||||
|
assertRGBEquals(message, 0xff0000ff, image.getRGB(16, 0), 0);
|
||||||
|
assertRGBEquals(message, 0xff000000, image.getRGB(24, 0), 0);
|
||||||
|
assertRGBEquals(message, 0xffff0000, image.getRGB(32, 0), 0);
|
||||||
|
assertRGBEquals(message, 0xff00ff00, image.getRGB(40, 0), 0);
|
||||||
|
assertRGBEquals(message, 0xff0000ff, image.getRGB(48, 0), 0);
|
||||||
|
assertRGBEquals(message, 0xffffffff, image.getRGB(56, 0), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This sample has "attribute bits" that *ARE* alpha, and there is no extension area
|
||||||
|
try (ImageInputStream input = ImageIO.createImageInputStream(getClassLoaderResource("/tga/alpha-no-extension.tga"))) {
|
||||||
|
reader.setInput(input);
|
||||||
|
|
||||||
|
BufferedImage image = reader.read(0);
|
||||||
|
|
||||||
|
String message = "RGB value differs for sample '/tga/alpha-no-extension.tga'";
|
||||||
|
assertRGBEquals(message, 0x00ffffff, image.getRGB(0, 0), 0);
|
||||||
|
assertRGBEquals(message, 0xffffffff, image.getRGB(48, 14), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMetadataTextEntries() throws IOException {
|
||||||
|
ImageReader reader = createReader();
|
||||||
|
|
||||||
|
try (ImageInputStream input = ImageIO.createImageInputStream(getClassLoaderResource("/tga/autodesk-3dsmax-extsize494.tga"))) {
|
||||||
|
reader.setInput(input);
|
||||||
|
IIOMetadata metadata = reader.getImageMetadata(0);
|
||||||
|
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||||
|
|
||||||
|
NodeList textEntries = root.getElementsByTagName("TextEntry");
|
||||||
|
boolean softwareFound = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < root.getLength(); i++) {
|
||||||
|
IIOMetadataNode software = (IIOMetadataNode) textEntries.item(i);
|
||||||
|
|
||||||
|
if ("Software".equals(software.getAttribute("keyword"))) {
|
||||||
|
assertEquals("Autodesk 3ds max 1.0", software.getAttribute("value"));
|
||||||
|
softwareFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue("No Software TextEntry", softwareFound);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-thumbsdb</artifactId>
|
<artifactId>imageio-thumbsdb</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: Thumbs.db plugin</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-tiff-jai-interop</artifactId>
|
<artifactId>imageio-tiff-jai-interop</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: TIFF/JAI Metadata Interop</name>
|
<name>TwelveMonkeys :: ImageIO :: TIFF/JAI Metadata Interop</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-tiff-jdk-interop</artifactId>
|
<artifactId>imageio-tiff-jdk-interop</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: TIFF/JDK JPEG Interop</name>
|
<name>TwelveMonkeys :: ImageIO :: TIFF/JDK JPEG Interop</name>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-tiff</artifactId>
|
<artifactId>imageio-tiff</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: TIFF plugin</name>
|
||||||
|
|||||||
+2
-1
@@ -63,7 +63,7 @@ final class BitPaddingStream extends FilterInputStream {
|
|||||||
|
|
||||||
private final byte[] inputBuffer;
|
private final byte[] inputBuffer;
|
||||||
private final ByteBuffer buffer;
|
private final ByteBuffer buffer;
|
||||||
private int componentSize;
|
private final int componentSize;
|
||||||
|
|
||||||
BitPaddingStream(final InputStream stream, int samplesPerPixel, final int bitsPerSample, final int colsInTile, final ByteOrder byteOrder) {
|
BitPaddingStream(final InputStream stream, int samplesPerPixel, final int bitsPerSample, final int colsInTile, final ByteOrder byteOrder) {
|
||||||
super(notNull(stream, "stream"));
|
super(notNull(stream, "stream"));
|
||||||
@@ -169,6 +169,7 @@ final class BitPaddingStream extends FilterInputStream {
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
private boolean fillBuffer() throws IOException {
|
private boolean fillBuffer() throws IOException {
|
||||||
if (!readFully(inputBuffer)) {
|
if (!readFully(inputBuffer)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+2
-1
@@ -50,6 +50,7 @@ import static com.twelvemonkeys.imageio.plugins.tiff.HorizontalDifferencingStrea
|
|||||||
* @version $Id: HorizontalDeDifferencingStream.java,v 1.0 11.03.13 14:20 haraldk Exp$
|
* @version $Id: HorizontalDeDifferencingStream.java,v 1.0 11.03.13 14:20 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
final class HorizontalDeDifferencingStream extends InputStream {
|
final class HorizontalDeDifferencingStream extends InputStream {
|
||||||
|
/// TODO: Create shared version with PSD, or see if we can avoid some duplication?
|
||||||
// See TIFF 6.0 Specification, Section 14: "Differencing Predictor", page 64.
|
// See TIFF 6.0 Specification, Section 14: "Differencing Predictor", page 64.
|
||||||
|
|
||||||
private final int columns;
|
private final int columns;
|
||||||
@@ -96,7 +97,7 @@ final class HorizontalDeDifferencingStream extends InputStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decodeRow() throws EOFException {
|
private void decodeRow() {
|
||||||
// Un-apply horizontal predictor
|
// Un-apply horizontal predictor
|
||||||
byte original;
|
byte original;
|
||||||
int sample = 0;
|
int sample = 0;
|
||||||
|
|||||||
+37
-1
@@ -1004,12 +1004,12 @@ public final class TIFFImageMetadata extends AbstractMetadata {
|
|||||||
// - If set, set res unit to pixels per cm as this better reflects values?
|
// - If set, set res unit to pixels per cm as this better reflects values?
|
||||||
// - Or, convert to DPI, if we already had values in DPI??
|
// - Or, convert to DPI, if we already had values in DPI??
|
||||||
// Also, if we have only aspect, set these values, and use unknown as unit?
|
// Also, if we have only aspect, set these values, and use unknown as unit?
|
||||||
// TODO: ImageOrientation => Orientation
|
|
||||||
NodeList children = dimensionNode.getChildNodes();
|
NodeList children = dimensionNode.getChildNodes();
|
||||||
|
|
||||||
Float aspect = null;
|
Float aspect = null;
|
||||||
Float xRes = null;
|
Float xRes = null;
|
||||||
Float yRes = null;
|
Float yRes = null;
|
||||||
|
Integer orientation = null;
|
||||||
|
|
||||||
for (int i = 0; i < children.getLength(); i++) {
|
for (int i = 0; i < children.getLength(); i++) {
|
||||||
Node child = children.item(i);
|
Node child = children.item(i);
|
||||||
@@ -1024,6 +1024,9 @@ public final class TIFFImageMetadata extends AbstractMetadata {
|
|||||||
else if ("VerticalPixelSize".equals(nodeName)) {
|
else if ("VerticalPixelSize".equals(nodeName)) {
|
||||||
yRes = Float.parseFloat(getAttribute(child, "value"));
|
yRes = Float.parseFloat(getAttribute(child, "value"));
|
||||||
}
|
}
|
||||||
|
else if ("ImageOrientation".equals(nodeName)) {
|
||||||
|
orientation = toTIFFOrientation(getAttribute(child, "value"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have one size compute the other
|
// If we have one size compute the other
|
||||||
@@ -1070,6 +1073,11 @@ public final class TIFFImageMetadata extends AbstractMetadata {
|
|||||||
new TIFFEntry(TIFF.TAG_RESOLUTION_UNIT, TIFF.TYPE_SHORT, TIFFBaseline.RESOLUTION_UNIT_NONE));
|
new TIFFEntry(TIFF.TAG_RESOLUTION_UNIT, TIFF.TYPE_SHORT, TIFFBaseline.RESOLUTION_UNIT_NONE));
|
||||||
}
|
}
|
||||||
// Else give up...
|
// Else give up...
|
||||||
|
|
||||||
|
if (orientation != null) {
|
||||||
|
entries.put(TIFF.TAG_ORIENTATION,
|
||||||
|
new TIFFEntry(TIFF.TAG_ORIENTATION, TIFF.TYPE_SHORT, orientation.shortValue()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mergeFromStandardDocumentNode(final Node documentNode, final Map<Integer, Entry> entries) {
|
private void mergeFromStandardDocumentNode(final Node documentNode, final Map<Integer, Entry> entries) {
|
||||||
@@ -1195,6 +1203,34 @@ public final class TIFFImageMetadata extends AbstractMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Integer toTIFFOrientation(String imageOrientation) {
|
||||||
|
if (imageOrientation == null) {
|
||||||
|
// malformed, empty or not readable value
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (imageOrientation.toLowerCase()) {
|
||||||
|
case "normal":
|
||||||
|
return TIFFBaseline.ORIENTATION_TOPLEFT;
|
||||||
|
case "fliph":
|
||||||
|
return TIFFExtension.ORIENTATION_TOPRIGHT;
|
||||||
|
case "rotate180":
|
||||||
|
return TIFFExtension.ORIENTATION_BOTRIGHT;
|
||||||
|
case "flipv":
|
||||||
|
return TIFFExtension.ORIENTATION_BOTLEFT;
|
||||||
|
case "fliphrotate90":
|
||||||
|
return TIFFExtension.ORIENTATION_LEFTTOP;
|
||||||
|
case "rotate270":
|
||||||
|
return TIFFExtension.ORIENTATION_RIGHTTOP;
|
||||||
|
case "flipvrotate90":
|
||||||
|
return TIFFExtension.ORIENTATION_RIGHTBOT;
|
||||||
|
case "rotate90":
|
||||||
|
return TIFFExtension.ORIENTATION_LEFTBOT;
|
||||||
|
default:
|
||||||
|
// malformed, invalid value
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private short getTIFFType(final Node node) throws IIOInvalidTreeException {
|
private short getTIFFType(final Node node) throws IIOInvalidTreeException {
|
||||||
Node containerNode = node.getFirstChild();
|
Node containerNode = node.getFirstChild();
|
||||||
if (containerNode == null) {
|
if (containerNode == null) {
|
||||||
|
|||||||
+254
-155
@@ -49,40 +49,50 @@ import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
|||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||||
import com.twelvemonkeys.imageio.metadata.xmp.XMPReader;
|
import com.twelvemonkeys.imageio.metadata.xmp.XMPReader;
|
||||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||||
|
import com.twelvemonkeys.imageio.stream.DirectImageInputStream;
|
||||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||||
import com.twelvemonkeys.io.FileUtil;
|
import com.twelvemonkeys.io.FileUtil;
|
||||||
import com.twelvemonkeys.io.LittleEndianDataInputStream;
|
|
||||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
import com.twelvemonkeys.xml.XMLSerializer;
|
|
||||||
|
|
||||||
import org.w3c.dom.Node;
|
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
import javax.imageio.*;
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import javax.imageio.event.IIOReadWarningListener;
|
import javax.imageio.event.IIOReadWarningListener;
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
import javax.imageio.plugins.jpeg.JPEGImageReadParam;
|
import javax.imageio.plugins.jpeg.JPEGImageReadParam;
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.CMMException;
|
import java.awt.color.*;
|
||||||
import java.awt.color.ColorSpace;
|
|
||||||
import java.awt.color.ICC_Profile;
|
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.*;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.DataInput;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.SequenceInputStream;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.zip.Inflater;
|
import java.util.zip.Inflater;
|
||||||
import java.util.zip.InflaterInputStream;
|
import java.util.zip.InflaterInputStream;
|
||||||
|
|
||||||
@@ -571,8 +581,13 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
if (bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
if (bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
||||||
return createImageTypeSpecifier(planarConfiguration, cs, dataType, significantSamples, samplesPerPixel, false, false);
|
return createImageTypeSpecifier(planarConfiguration, cs, dataType, significantSamples, samplesPerPixel, false, false);
|
||||||
}
|
}
|
||||||
|
else if (bitsPerSample == 2 && planarConfiguration == TIFFBaseline.PLANARCONFIG_CHUNKY) {
|
||||||
|
return ImageTypeSpecifiers.createPacked(cs, 0x30, 0xC, 0x3, 0, DataBuffer.TYPE_BYTE, false);
|
||||||
|
}
|
||||||
|
else if (bitsPerSample == 4 && planarConfiguration == TIFFBaseline.PLANARCONFIG_CHUNKY) {
|
||||||
|
return ImageTypeSpecifiers.createPacked(cs, 0xF00, 0xF0, 0xF, 0, DataBuffer.TYPE_USHORT, false);
|
||||||
|
}
|
||||||
else if (bitsPerSample > 8 && bitsPerSample % 2 == 0) {
|
else if (bitsPerSample > 8 && bitsPerSample % 2 == 0) {
|
||||||
// TODO: Support variable bits/sample?
|
|
||||||
ColorModel colorModel = new ComponentColorModel(cs, new int[] {bitsPerSample, bitsPerSample, bitsPerSample}, false, false, Transparency.OPAQUE, dataType);
|
ColorModel colorModel = new ComponentColorModel(cs, new int[] {bitsPerSample, bitsPerSample, bitsPerSample}, false, false, Transparency.OPAQUE, dataType);
|
||||||
SampleModel sampleModel = planarConfiguration == TIFFBaseline.PLANARCONFIG_CHUNKY
|
SampleModel sampleModel = planarConfiguration == TIFFBaseline.PLANARCONFIG_CHUNKY
|
||||||
? colorModel.createCompatibleSampleModel(1, 1)
|
? colorModel.createCompatibleSampleModel(1, 1)
|
||||||
@@ -583,11 +598,14 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
if (bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
if (bitsPerSample == 8 || bitsPerSample == 16 || bitsPerSample == 32) {
|
||||||
return createImageTypeSpecifier(planarConfiguration, cs, dataType, significantSamples, samplesPerPixel, true, isAlphaPremultiplied);
|
return createImageTypeSpecifier(planarConfiguration, cs, dataType, significantSamples, samplesPerPixel, true, isAlphaPremultiplied);
|
||||||
}
|
}
|
||||||
else if (significantSamples == 4 && bitsPerSample == 4) {
|
else if (bitsPerSample == 2 && planarConfiguration == TIFFBaseline.PLANARCONFIG_CHUNKY) {
|
||||||
|
return ImageTypeSpecifiers.createPacked(cs, 0xC0, 0x30, 0xC, 0x3, DataBuffer.TYPE_BYTE, isAlphaPremultiplied);
|
||||||
|
}
|
||||||
|
else if (bitsPerSample == 4 && planarConfiguration == TIFFBaseline.PLANARCONFIG_CHUNKY) {
|
||||||
return ImageTypeSpecifiers.createPacked(cs, 0xF000, 0xF00, 0xF0, 0xF, DataBuffer.TYPE_USHORT, isAlphaPremultiplied);
|
return ImageTypeSpecifiers.createPacked(cs, 0xF000, 0xF00, 0xF0, 0xF, DataBuffer.TYPE_USHORT, isAlphaPremultiplied);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new IIOException(String.format("Unsupported SamplesPerPixel/BitsPerSample combination for RGB TIFF (expected 3/8, 4/8, 3/16 or 4/16): %d/%d", samplesPerPixel, bitsPerSample));
|
throw new IIOException(String.format("Unsupported SamplesPerPixel/BitsPerSample combination for RGB TIFF (expected 3 or 4/a multiple of 2): %d/%d", samplesPerPixel, bitsPerSample));
|
||||||
}
|
}
|
||||||
case TIFFBaseline.PHOTOMETRIC_PALETTE:
|
case TIFFBaseline.PHOTOMETRIC_PALETTE:
|
||||||
// Palette
|
// Palette
|
||||||
@@ -935,7 +953,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
final int compression = getValueAsIntWithDefault(TIFF.TAG_COMPRESSION, TIFFBaseline.COMPRESSION_NONE);
|
final int compression = getValueAsIntWithDefault(TIFF.TAG_COMPRESSION, TIFFBaseline.COMPRESSION_NONE);
|
||||||
final int predictor = getValueAsIntWithDefault(TIFF.TAG_PREDICTOR, 1);
|
final int predictor = getValueAsIntWithDefault(TIFF.TAG_PREDICTOR, 1);
|
||||||
final int planarConfiguration = getValueAsIntWithDefault(TIFF.TAG_PLANAR_CONFIGURATION, TIFFBaseline.PLANARCONFIG_CHUNKY);
|
final int planarConfiguration = getValueAsIntWithDefault(TIFF.TAG_PLANAR_CONFIGURATION, TIFFBaseline.PLANARCONFIG_CHUNKY);
|
||||||
final int numBands = planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR ? 1 : rawType.getNumBands();
|
final int samplesInTile = planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR ? 1 : rawType.getNumBands();
|
||||||
|
|
||||||
// NOTE: We handle strips as tiles of tileWidth == width by tileHeight == rowsPerStrip
|
// NOTE: We handle strips as tiles of tileWidth == width by tileHeight == rowsPerStrip
|
||||||
// Strips are top/down, tiles are left/right, top/down
|
// Strips are top/down, tiles are left/right, top/down
|
||||||
@@ -1055,7 +1073,9 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
int bands = planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR ? rawType.getNumBands() : 1;
|
int bands = planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR ? rawType.getNumBands() : 1;
|
||||||
int fillOrder = getValueAsIntWithDefault(TIFF.TAG_FILL_ORDER, TIFFBaseline.FILL_LEFT_TO_RIGHT);
|
int fillOrder = getValueAsIntWithDefault(TIFF.TAG_FILL_ORDER, TIFFBaseline.FILL_LEFT_TO_RIGHT);
|
||||||
int bitsPerSample = getBitsPerSample();
|
int bitsPerSample = getBitsPerSample();
|
||||||
boolean needsBitPadding = bitsPerSample > 16 && bitsPerSample % 16 != 0 || bitsPerSample > 8 && bitsPerSample % 8 != 0 || bitsPerSample == 6;
|
boolean needsBitPadding = bitsPerSample > 16 && bitsPerSample % 16 != 0 || bitsPerSample > 8 && bitsPerSample % 8 != 0
|
||||||
|
|| samplesInTile == 1 && bitsPerSample == 6 // IndexColorModel or Gray
|
||||||
|
|| samplesInTile == 3 && (bitsPerSample == 2 || bitsPerSample == 4); // RGB/YCbCr/etc.
|
||||||
boolean needsAdapter = compression != TIFFBaseline.COMPRESSION_NONE || fillOrder != TIFFBaseline.FILL_LEFT_TO_RIGHT
|
boolean needsAdapter = compression != TIFFBaseline.COMPRESSION_NONE || fillOrder != TIFFBaseline.FILL_LEFT_TO_RIGHT
|
||||||
|| interpretation == TIFFExtension.PHOTOMETRIC_YCBCR || needsBitPadding;
|
|| interpretation == TIFFExtension.PHOTOMETRIC_YCBCR || needsBitPadding;
|
||||||
|
|
||||||
@@ -1069,12 +1089,24 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
for (int b = 0; b < bands; b++) {
|
for (int b = 0; b < bands; b++) {
|
||||||
int i = b * tilesDown * tilesAcross + y * tilesAcross + x;
|
int i = b * tilesDown * tilesAcross + y * tilesAcross + x;
|
||||||
|
|
||||||
|
// Clip the stripTile rowRaster to not exceed the srcRegion
|
||||||
|
clip.width = Math.min(colsInTile, srcRegion.width);
|
||||||
|
Raster clippedRow = clipRowToRect(rowRaster, clip,
|
||||||
|
param != null ? param.getSourceBands() : null,
|
||||||
|
param != null ? param.getSourceXSubsampling() : 1);
|
||||||
|
|
||||||
imageInput.seek(stripTileOffsets[i]);
|
imageInput.seek(stripTileOffsets[i]);
|
||||||
|
|
||||||
DataInput input;
|
ImageInputStream input;
|
||||||
if (!needsAdapter) {
|
if (!needsAdapter) {
|
||||||
// No need for transformation, fast-forward
|
// No need for transformation, fast-forward
|
||||||
input = imageInput;
|
long byteCount = stripTileHeight * (((long) stripTileWidth * bitsPerSample * samplesInTile + 7L) / 8L);
|
||||||
|
|
||||||
|
if (stripTileByteCounts != null && stripTileByteCounts[i] < byteCount) {
|
||||||
|
processWarningOccurred("strip/tileByteCount < required ( " + byteCount + "):" + stripTileByteCounts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
input = new SubImageInputStream(imageInput, byteCount);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
InputStream adapter = stripTileByteCounts != null
|
InputStream adapter = stripTileByteCounts != null
|
||||||
@@ -1082,39 +1114,44 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
: createStreamAdapter(imageInput);
|
: createStreamAdapter(imageInput);
|
||||||
|
|
||||||
adapter = createFillOrderStream(fillOrder, adapter);
|
adapter = createFillOrderStream(fillOrder, adapter);
|
||||||
adapter = createDecompressorStream(compression, stripTileWidth, numBands, adapter);
|
|
||||||
adapter = createUnpredictorStream(predictor, stripTileWidth, numBands, bitsPerSample, adapter, imageInput.getByteOrder());
|
|
||||||
|
|
||||||
if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR && rowRaster.getTransferType() == DataBuffer.TYPE_BYTE) {
|
// For subsampled planar, the compressed data will not be full width
|
||||||
adapter = new YCbCrUpsamplerStream(adapter, yCbCrSubsampling, yCbCrPos, colsInTile);
|
int compressedStripTileWidth = planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR && b > 0 && yCbCrSubsampling != null
|
||||||
}
|
? ((stripTileWidth + yCbCrSubsampling[0] - 1) / yCbCrSubsampling[0])
|
||||||
else if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR && rowRaster.getTransferType() == DataBuffer.TYPE_USHORT) {
|
: stripTileWidth;
|
||||||
adapter = new YCbCr16UpsamplerStream(adapter, yCbCrSubsampling, yCbCrPos, colsInTile, imageInput.getByteOrder());
|
adapter = createDecompressorStream(compression, compressedStripTileWidth, samplesInTile, adapter);
|
||||||
}
|
adapter = createUnpredictorStream(predictor, compressedStripTileWidth, samplesInTile, bitsPerSample, adapter, imageInput.getByteOrder());
|
||||||
else if (interpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
|
adapter = createYCbCrUpsamplerStream(interpretation, planarConfiguration, b, rowRaster.getTransferType(), yCbCrSubsampling, yCbCrPos, colsInTile, adapter, imageInput.getByteOrder());
|
||||||
// Handled in getRawImageType
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsBitPadding) {
|
if (needsBitPadding) {
|
||||||
// We'll pad "odd" bitsPerSample streams to the smallest data type (byte/short/int) larger than the input
|
// We'll pad "odd" bitsPerSample streams to the smallest data type (byte/short/int) larger than the input
|
||||||
adapter = new BitPaddingStream(adapter, numBands, bitsPerSample, colsInTile, imageInput.getByteOrder());
|
adapter = bitsPerSample < 8
|
||||||
|
? new BitPaddingStream(adapter, 1, samplesInTile * bitsPerSample, colsInTile, imageInput.getByteOrder())
|
||||||
|
: new BitPaddingStream(adapter, samplesInTile, bitsPerSample, colsInTile, imageInput.getByteOrder());
|
||||||
}
|
}
|
||||||
|
|
||||||
// According to the spec, short/long/etc should follow order of containing stream
|
// According to the spec, short/long/etc should follow order of containing stream
|
||||||
input = imageInput.getByteOrder() == ByteOrder.BIG_ENDIAN
|
input = new DirectImageInputStream(adapter);
|
||||||
? new DataInputStream(adapter)
|
|
||||||
: new LittleEndianDataInputStream(adapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clip the stripTile rowRaster to not exceed the srcRegion
|
try (ImageInputStream stream = input) {
|
||||||
clip.width = Math.min(colsInTile, srcRegion.width);
|
// Temporary set byte order to match the color model for USHORT_4444/555/565/etc...
|
||||||
Raster clippedRow = clipRowToRect(rowRaster, clip,
|
if (rawType.getColorModel() instanceof DirectColorModel && rawType.getColorModel().getTransferType() == DataBuffer.TYPE_USHORT) {
|
||||||
param != null ? param.getSourceBands() : null,
|
stream.setByteOrder(ByteOrder.BIG_ENDIAN);
|
||||||
param != null ? param.getSourceXSubsampling() : 1);
|
}
|
||||||
|
else {
|
||||||
|
// ...otherwise keep the order from the parent stream
|
||||||
|
stream.setByteOrder(imageInput.getByteOrder());
|
||||||
|
}
|
||||||
|
|
||||||
// Read a full strip/tile
|
// Read a full strip/tile
|
||||||
readStripTileData(clippedRow, srcRegion, xSub, ySub, b, numBands, interpretation, destRaster, col, srcRow, colsInTile, rowsInTile, input);
|
readStripTileData(clippedRow, srcRegion, xSub, ySub, b, samplesInTile, interpretation, destRaster, col, srcRow, colsInTile, rowsInTile, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to do color normalization after reading all bands for planar
|
||||||
|
if (planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR) {
|
||||||
|
normalizeColorPlanar(interpretation, destRaster);
|
||||||
}
|
}
|
||||||
|
|
||||||
col += colsInTile;
|
col += colsInTile;
|
||||||
@@ -1133,7 +1170,6 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TIFFExtension.COMPRESSION_JPEG:
|
case TIFFExtension.COMPRESSION_JPEG:
|
||||||
@@ -1213,10 +1249,10 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
// TODO: Refactor + duplicate this for all JPEG-in-TIFF cases
|
// TODO: Refactor + duplicate this for all JPEG-in-TIFF cases
|
||||||
switch (raster.getTransferType()) {
|
switch (raster.getTransferType()) {
|
||||||
case DataBuffer.TYPE_BYTE:
|
case DataBuffer.TYPE_BYTE:
|
||||||
normalizeColor(interpretation, ((DataBufferByte) raster.getDataBuffer()).getData());
|
normalizeColor(interpretation, samplesInTile, ((DataBufferByte) raster.getDataBuffer()).getData());
|
||||||
break;
|
break;
|
||||||
case DataBuffer.TYPE_USHORT:
|
case DataBuffer.TYPE_USHORT:
|
||||||
normalizeColor(interpretation, ((DataBufferUShort) raster.getDataBuffer()).getData());
|
normalizeColor(interpretation, samplesInTile, ((DataBufferUShort) raster.getDataBuffer()).getData());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unsupported transfer type: " + raster.getTransferType());
|
throw new IllegalStateException("Unsupported transfer type: " + raster.getTransferType());
|
||||||
@@ -1379,7 +1415,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
// Otherwise, it's likely CMYK or some other interpretation we don't need to convert.
|
// Otherwise, it's likely CMYK or some other interpretation we don't need to convert.
|
||||||
// We'll have to use readAsRaster and later apply color space conversion ourselves
|
// We'll have to use readAsRaster and later apply color space conversion ourselves
|
||||||
Raster raster = jpegReader.readRaster(0, jpegParam);
|
Raster raster = jpegReader.readRaster(0, jpegParam);
|
||||||
normalizeColor(interpretation, ((DataBufferByte) raster.getDataBuffer()).getData());
|
normalizeColor(interpretation, samplesInTile, ((DataBufferByte) raster.getDataBuffer()).getData());
|
||||||
destination.getRaster().setDataElements(offset.x, offset.y, raster);
|
destination.getRaster().setDataElements(offset.x, offset.y, raster);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1529,7 +1565,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
// Otherwise, it's likely CMYK or some other interpretation we don't need to convert.
|
// Otherwise, it's likely CMYK or some other interpretation we don't need to convert.
|
||||||
// We'll have to use readAsRaster and later apply color space conversion ourselves
|
// We'll have to use readAsRaster and later apply color space conversion ourselves
|
||||||
Raster raster = jpegReader.readRaster(0, jpegParam);
|
Raster raster = jpegReader.readRaster(0, jpegParam);
|
||||||
normalizeColor(interpretation, ((DataBufferByte) raster.getDataBuffer()).getData());
|
normalizeColor(interpretation, samplesInTile, ((DataBufferByte) raster.getDataBuffer()).getData());
|
||||||
destination.getRaster().setDataElements(offset.x, offset.y, raster);
|
destination.getRaster().setDataElements(offset.x, offset.y, raster);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1555,7 +1591,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Known, but unsupported compression types
|
// Known, but unsupported compression types
|
||||||
case TIFFCustom.COMPRESSION_NEXT:
|
case TIFFCustom.COMPRESSION_NEXT:
|
||||||
case TIFFCustom.COMPRESSION_CCITTRLEW:
|
case TIFFCustom.COMPRESSION_CCITTRLEW:
|
||||||
case TIFFCustom.COMPRESSION_THUNDERSCAN:
|
case TIFFCustom.COMPRESSION_THUNDERSCAN:
|
||||||
@@ -1570,8 +1606,8 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
case TIFFCustom.COMPRESSION_SGILOG:
|
case TIFFCustom.COMPRESSION_SGILOG:
|
||||||
case TIFFCustom.COMPRESSION_SGILOG24:
|
case TIFFCustom.COMPRESSION_SGILOG24:
|
||||||
case TIFFCustom.COMPRESSION_JPEG2000: // Doable with JPEG2000 plugin?
|
case TIFFCustom.COMPRESSION_JPEG2000: // Doable with JPEG2000 plugin?
|
||||||
|
|
||||||
throw new IIOException("Unsupported TIFF Compression value: " + compression);
|
throw new IIOException("Unsupported TIFF Compression value: " + compression);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IIOException("Unknown TIFF Compression value: " + compression);
|
throw new IIOException("Unknown TIFF Compression value: " + compression);
|
||||||
}
|
}
|
||||||
@@ -1583,6 +1619,28 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
return destination;
|
return destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private InputStream createYCbCrUpsamplerStream(int photometricInterpretation, int planarConfiguration, int plane, int transferType,
|
||||||
|
int[] yCbCrSubsampling, int yCbCrPos, int colsInTile, InputStream stream, ByteOrder byteOrder) {
|
||||||
|
if (photometricInterpretation == TIFFExtension.PHOTOMETRIC_YCBCR) {
|
||||||
|
if (planarConfiguration == TIFFExtension.PLANARCONFIG_PLANAR && transferType == DataBuffer.TYPE_BYTE) {
|
||||||
|
// For planar YCbCr, only the chroma planes are subsampled
|
||||||
|
return plane > 0 && (yCbCrSubsampling[0] != 1 || yCbCrSubsampling[1] != 1)
|
||||||
|
? new YCbCrPlanarUpsamplerStream(stream, yCbCrSubsampling, yCbCrPos, colsInTile) : stream;
|
||||||
|
}
|
||||||
|
else if (transferType == DataBuffer.TYPE_BYTE) {
|
||||||
|
return new YCbCrUpsamplerStream(stream, yCbCrSubsampling, yCbCrPos, colsInTile);
|
||||||
|
}
|
||||||
|
else if (transferType == DataBuffer.TYPE_USHORT) {
|
||||||
|
return new YCbCr16UpsamplerStream(stream, yCbCrSubsampling, yCbCrPos, colsInTile, byteOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handled in getRawImageType
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean containsZero(long[] byteCounts) {
|
private boolean containsZero(long[] byteCounts) {
|
||||||
for (long byteCount : byteCounts) {
|
for (long byteCount : byteCounts) {
|
||||||
if (byteCount <= 0) {
|
if (byteCount <= 0) {
|
||||||
@@ -1757,7 +1815,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
private IIOMetadataNode getNode(final IIOMetadataNode parent, final String tagName) {
|
private IIOMetadataNode getNode(final IIOMetadataNode parent, final String tagName) {
|
||||||
NodeList nodes = parent.getElementsByTagName(tagName);
|
NodeList nodes = parent.getElementsByTagName(tagName);
|
||||||
return nodes != null && nodes.getLength() >= 1 ? (IIOMetadataNode) nodes.item(0) : null;
|
return nodes.getLength() >= 1 ? (IIOMetadataNode) nodes.item(0) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImageReader createJPEGDelegate() throws IOException {
|
private ImageReader createJPEGDelegate() throws IOException {
|
||||||
@@ -1863,14 +1921,20 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
private void readStripTileData(final Raster tileRowRaster, final Rectangle srcRegion, final int xSub, final int ySub,
|
private void readStripTileData(final Raster tileRowRaster, final Rectangle srcRegion, final int xSub, final int ySub,
|
||||||
final int band, final int numBands, final int interpretation,
|
final int band, final int numBands, final int interpretation,
|
||||||
final WritableRaster raster, final int startCol, final int startRow,
|
final WritableRaster raster, final int startCol, final int startRow,
|
||||||
final int colsInTile, final int rowsInTile, final DataInput input)
|
final int colsInTile, final int rowsInTile, final ImageInputStream input)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
DataBuffer dataBuffer = tileRowRaster.getDataBuffer();
|
DataBuffer dataBuffer = tileRowRaster.getDataBuffer();
|
||||||
int bands = dataBuffer.getNumBanks();
|
boolean banded = dataBuffer.getNumBanks() > 1;
|
||||||
boolean banded = bands > 1;
|
|
||||||
int bitsPerSample = getBitsPerSample();
|
int bitsPerSample = getBitsPerSample();
|
||||||
|
|
||||||
|
WritableRaster destChannel = banded
|
||||||
|
? raster.createWritableChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), 0, 0, new int[] {band})
|
||||||
|
: raster;
|
||||||
|
Raster srcChannel = banded
|
||||||
|
? tileRowRaster.createChild(tileRowRaster.getMinX(), 0, tileRowRaster.getWidth(), 1, 0, 0, new int[] {band})
|
||||||
|
: tileRowRaster;
|
||||||
|
|
||||||
switch (tileRowRaster.getTransferType()) {
|
switch (tileRowRaster.getTransferType()) {
|
||||||
case DataBuffer.TYPE_BYTE:
|
case DataBuffer.TYPE_BYTE:
|
||||||
|
|
||||||
@@ -1878,12 +1942,6 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
int bank = banded ? ((BandedSampleModel) tileRowRaster.getSampleModel()).getBankIndices()[band] : band;
|
int bank = banded ? ((BandedSampleModel) tileRowRaster.getSampleModel()).getBankIndices()[band] : band;
|
||||||
|
|
||||||
byte[] rowDataByte = ((DataBufferByte) dataBuffer).getData(bank);
|
byte[] rowDataByte = ((DataBufferByte) dataBuffer).getData(bank);
|
||||||
WritableRaster destChannel = banded
|
|
||||||
? raster.createWritableChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), 0, 0, new int[] {band})
|
|
||||||
: raster;
|
|
||||||
Raster srcChannel = banded
|
|
||||||
? tileRowRaster.createChild(tileRowRaster.getMinX(), 0, tileRowRaster.getWidth(), 1, 0, 0, new int[] {band})
|
|
||||||
: tileRowRaster;
|
|
||||||
|
|
||||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||||
if (row >= srcRegion.y + srcRegion.height) {
|
if (row >= srcRegion.y + srcRegion.height) {
|
||||||
@@ -1894,7 +1952,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
if (row % ySub == 0 && row >= srcRegion.y) {
|
if (row % ySub == 0 && row >= srcRegion.y) {
|
||||||
if (!banded) {
|
if (!banded) {
|
||||||
normalizeColor(interpretation, rowDataByte);
|
normalizeColor(interpretation, numBands, rowDataByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subsample horizontal
|
// Subsample horizontal
|
||||||
@@ -1907,12 +1965,8 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (banded) {
|
|
||||||
// // TODO: Normalize colors for tile (need to know tile region and sample model)
|
|
||||||
// // Unfortunately, this will disable acceleration...
|
|
||||||
// }
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DataBuffer.TYPE_USHORT:
|
case DataBuffer.TYPE_USHORT:
|
||||||
case DataBuffer.TYPE_SHORT:
|
case DataBuffer.TYPE_SHORT:
|
||||||
/*for (int band = 0; band < bands; band++)*/ {
|
/*for (int band = 0; band < bands; band++)*/ {
|
||||||
@@ -1920,22 +1974,15 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
? ((DataBufferUShort) dataBuffer).getData(band)
|
? ((DataBufferUShort) dataBuffer).getData(band)
|
||||||
: ((DataBufferShort) dataBuffer).getData(band);
|
: ((DataBufferShort) dataBuffer).getData(band);
|
||||||
|
|
||||||
WritableRaster destChannel = banded
|
|
||||||
? raster.createWritableChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), 0, 0, new int[] {band})
|
|
||||||
: raster;
|
|
||||||
Raster srcChannel = banded
|
|
||||||
? tileRowRaster.createChild(tileRowRaster.getMinX(), 0, tileRowRaster.getWidth(), 1, 0, 0, new int[] {band})
|
|
||||||
: tileRowRaster;
|
|
||||||
|
|
||||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||||
if (row >= srcRegion.y + srcRegion.height) {
|
if (row >= srcRegion.y + srcRegion.height) {
|
||||||
break; // We're done with this tile
|
break; // We're done with this tile
|
||||||
}
|
}
|
||||||
|
|
||||||
readFully(input, rowDataShort);
|
input.readFully(rowDataShort, 0, rowDataShort.length);
|
||||||
|
|
||||||
if (row >= srcRegion.y) {
|
if (row >= srcRegion.y) {
|
||||||
normalizeColor(interpretation, rowDataShort);
|
normalizeColor(interpretation, numBands, rowDataShort);
|
||||||
|
|
||||||
// Subsample horizontal
|
// Subsample horizontal
|
||||||
subsampleRow(rowDataShort, srcRegion.x * numBands, colsInTile,
|
subsampleRow(rowDataShort, srcRegion.x * numBands, colsInTile,
|
||||||
@@ -1950,26 +1997,20 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DataBuffer.TYPE_INT:
|
case DataBuffer.TYPE_INT:
|
||||||
/*for (int band = 0; band < bands; band++)*/ {
|
/*for (int band = 0; band < bands; band++)*/ {
|
||||||
int[] rowDataInt = ((DataBufferInt) dataBuffer).getData(band);
|
int[] rowDataInt = ((DataBufferInt) dataBuffer).getData(band);
|
||||||
|
|
||||||
WritableRaster destChannel = banded
|
|
||||||
? raster.createWritableChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), 0, 0, new int[] {band})
|
|
||||||
: raster;
|
|
||||||
Raster srcChannel = banded
|
|
||||||
? tileRowRaster.createChild(tileRowRaster.getMinX(), 0, tileRowRaster.getWidth(), 1, 0, 0, new int[] {band})
|
|
||||||
: tileRowRaster;
|
|
||||||
|
|
||||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||||
if (row >= srcRegion.y + srcRegion.height) {
|
if (row >= srcRegion.y + srcRegion.height) {
|
||||||
break; // We're done with this tile
|
break; // We're done with this tile
|
||||||
}
|
}
|
||||||
|
|
||||||
readFully(input, rowDataInt);
|
input.readFully(rowDataInt, 0, rowDataInt.length);
|
||||||
|
|
||||||
if (row >= srcRegion.y) {
|
if (row >= srcRegion.y) {
|
||||||
normalizeColor(interpretation, rowDataInt);
|
normalizeColor(interpretation, numBands, rowDataInt);
|
||||||
|
|
||||||
// Subsample horizontal
|
// Subsample horizontal
|
||||||
subsampleRow(rowDataInt, srcRegion.x * numBands, colsInTile,
|
subsampleRow(rowDataInt, srcRegion.x * numBands, colsInTile,
|
||||||
@@ -1989,29 +2030,21 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
float[] rowDataFloat = ((DataBufferFloat) tileRowRaster.getDataBuffer()).getData(band);
|
float[] rowDataFloat = ((DataBufferFloat) tileRowRaster.getDataBuffer()).getData(band);
|
||||||
short[] rowDataShort = needsWidening ? new short[rowDataFloat.length] : null;
|
short[] rowDataShort = needsWidening ? new short[rowDataFloat.length] : null;
|
||||||
|
|
||||||
WritableRaster destChannel = banded
|
|
||||||
? raster.createWritableChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), 0, 0, new int[] {band})
|
|
||||||
: raster;
|
|
||||||
Raster srcChannel = banded
|
|
||||||
? tileRowRaster.createChild(tileRowRaster.getMinX(), 0, tileRowRaster.getWidth(), 1, 0, 0, new int[] {band})
|
|
||||||
: tileRowRaster;
|
|
||||||
|
|
||||||
|
|
||||||
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
for (int row = startRow; row < startRow + rowsInTile; row++) {
|
||||||
if (row >= srcRegion.y + srcRegion.height) {
|
if (row >= srcRegion.y + srcRegion.height) {
|
||||||
break; // We're done with this tile
|
break; // We're done with this tile
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsWidening) {
|
if (needsWidening) {
|
||||||
readFully(input, rowDataShort);
|
input.readFully(rowDataShort, 0, rowDataShort.length);
|
||||||
toFloat(rowDataFloat, rowDataShort);
|
toFloat(rowDataShort, rowDataFloat);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
readFully(input, rowDataFloat);
|
input.readFully(rowDataFloat, 0, rowDataFloat.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row >= srcRegion.y) {
|
if (row >= srcRegion.y) {
|
||||||
normalizeColor(interpretation, rowDataFloat);
|
normalizeColor(interpretation, numBands, rowDataFloat);
|
||||||
|
|
||||||
// Subsample horizontal
|
// Subsample horizontal
|
||||||
if (xSub != 1) {
|
if (xSub != 1) {
|
||||||
@@ -2033,7 +2066,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toFloat(final float[] rowDataFloat, final short[] rowDataShort) {
|
private void toFloat(final short[] rowDataShort, final float[] rowDataFloat) {
|
||||||
for (int i = 0; i < rowDataFloat.length; i++) {
|
for (int i = 0; i < rowDataFloat.length; i++) {
|
||||||
rowDataFloat[i] = Half.shortBitsToFloat(rowDataShort[i]);
|
rowDataFloat[i] = Half.shortBitsToFloat(rowDataShort[i]);
|
||||||
}
|
}
|
||||||
@@ -2050,46 +2083,103 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Candidate util method (with off/len + possibly byte order)
|
private void normalizeColorPlanar(int photometricInterpretation, WritableRaster raster) throws IIOException {
|
||||||
private void readFully(final DataInput input, final float[] rowDataFloat) throws IOException {
|
// TODO: Other transfer types?
|
||||||
if (input instanceof ImageInputStream) {
|
if (raster.getTransferType() != DataBuffer.TYPE_BYTE) {
|
||||||
ImageInputStream imageInputStream = (ImageInputStream) input;
|
return;
|
||||||
imageInputStream.readFully(rowDataFloat, 0, rowDataFloat.length);
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
for (int k = 0; k < rowDataFloat.length; k++) {
|
byte[] pixel = null;
|
||||||
rowDataFloat[k] = input.readFloat();
|
|
||||||
}
|
switch (photometricInterpretation) {
|
||||||
|
case TIFFExtension.PHOTOMETRIC_YCBCR:
|
||||||
|
|
||||||
|
// Default: CCIR Recommendation 601-1: 299/1000, 587/1000 and 114/1000
|
||||||
|
double[] coefficients = getValueAsDoubleArray(TIFF.TAG_YCBCR_COEFFICIENTS, "YCbCrCoefficients", false, 3);
|
||||||
|
|
||||||
|
// "Default" [0, 255, 128, 255, 128, 255] for YCbCr (real default is [0, 255, 0, 255, 0, 255] for RGB)
|
||||||
|
double[] referenceBW = getValueAsDoubleArray(TIFF.TAG_REFERENCE_BLACK_WHITE, "ReferenceBlackWhite", false, 6);
|
||||||
|
|
||||||
|
if ((coefficients == null || Arrays.equals(coefficients, CCIR_601_1_COEFFICIENTS))
|
||||||
|
&& (referenceBW == null || Arrays.equals(referenceBW, REFERENCE_BLACK_WHITE_YCC_DEFAULT))) {
|
||||||
|
|
||||||
|
// Fast, default conversion
|
||||||
|
for (int y = 0; y < raster.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < raster.getWidth(); x++) {
|
||||||
|
pixel = (byte[]) raster.getDataElements(x, y, pixel);
|
||||||
|
YCbCrConverter.convertJPEGYCbCr2RGB(pixel, pixel, 0);
|
||||||
|
raster.setDataElements(x, y, pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If one of the values are null, we'll need the other here...
|
||||||
|
if (coefficients == null) {
|
||||||
|
coefficients = CCIR_601_1_COEFFICIENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (referenceBW != null && Arrays.equals(referenceBW, REFERENCE_BLACK_WHITE_YCC_DEFAULT)) {
|
||||||
|
referenceBW = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < raster.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < raster.getWidth(); x++) {
|
||||||
|
pixel = (byte[]) raster.getDataElements(x, y, pixel);
|
||||||
|
YCbCrConverter.convertYCbCr2RGB(pixel, pixel, coefficients, referenceBW, 0);
|
||||||
|
raster.setDataElements(x, y, pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TIFFExtension.PHOTOMETRIC_CIELAB:
|
||||||
|
case TIFFExtension.PHOTOMETRIC_ICCLAB:
|
||||||
|
case TIFFExtension.PHOTOMETRIC_ITULAB:
|
||||||
|
// TODO: White point may be encoded in separate tag
|
||||||
|
CIELabColorConverter converter = new CIELabColorConverter(
|
||||||
|
photometricInterpretation == TIFFExtension.PHOTOMETRIC_CIELAB
|
||||||
|
? Illuminant.D65
|
||||||
|
: Illuminant.D50
|
||||||
|
);
|
||||||
|
|
||||||
|
float[] temp = new float[3];
|
||||||
|
|
||||||
|
for (int y = 0; y < raster.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < raster.getWidth(); x++) {
|
||||||
|
pixel = (byte[]) raster.getDataElements(x, y, pixel);
|
||||||
|
|
||||||
|
float LStar = (pixel[0] & 0xff) * 100f / 255.0f;
|
||||||
|
float aStar;
|
||||||
|
float bStar;
|
||||||
|
|
||||||
|
if (photometricInterpretation == TIFFExtension.PHOTOMETRIC_CIELAB) {
|
||||||
|
// -128...127
|
||||||
|
aStar = pixel[1];
|
||||||
|
bStar = pixel[2];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Assumes same data for ICC and ITU (unsigned)
|
||||||
|
// 0...255
|
||||||
|
aStar = (pixel[1] & 0xff) - 128;
|
||||||
|
bStar = (pixel[2] & 0xff) - 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
converter.toRGB(LStar, aStar, bStar, temp);
|
||||||
|
|
||||||
|
pixel[0] = (byte) temp[0];
|
||||||
|
pixel[1] = (byte) temp[1];
|
||||||
|
pixel[2] = (byte) temp[2];
|
||||||
|
|
||||||
|
raster.setDataElements(x, y, pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Candidate util method (with off/len + possibly byte order)
|
private void normalizeColor(int photometricInterpretation, int numBands, byte[] data) throws IOException {
|
||||||
private void readFully(final DataInput input, final int[] rowDataInt) throws IOException {
|
|
||||||
if (input instanceof ImageInputStream) {
|
|
||||||
ImageInputStream imageInputStream = (ImageInputStream) input;
|
|
||||||
imageInputStream.readFully(rowDataInt, 0, rowDataInt.length);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (int k = 0; k < rowDataInt.length; k++) {
|
|
||||||
rowDataInt[k] = input.readInt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Candidate util method (with off/len + possibly byte order)
|
|
||||||
private void readFully(final DataInput input, final short[] rowDataShort) throws IOException {
|
|
||||||
if (input instanceof ImageInputStream) {
|
|
||||||
ImageInputStream imageInputStream = (ImageInputStream) input;
|
|
||||||
imageInputStream.readFully(rowDataShort, 0, rowDataShort.length);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (int k = 0; k < rowDataShort.length; k++) {
|
|
||||||
rowDataShort[k] = input.readShort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void normalizeColor(int photometricInterpretation, byte[] data) throws IOException {
|
|
||||||
switch (photometricInterpretation) {
|
switch (photometricInterpretation) {
|
||||||
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
||||||
// NOTE: Preserve WhiteIsZero for 1 bit monochrome, for CCITT compatibility
|
// NOTE: Preserve WhiteIsZero for 1 bit monochrome, for CCITT compatibility
|
||||||
@@ -2113,7 +2203,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
);
|
);
|
||||||
float[] temp = new float[3];
|
float[] temp = new float[3];
|
||||||
|
|
||||||
for (int i = 0; i < data.length; i += 3) {
|
for (int i = 0; i < data.length; i += numBands) {
|
||||||
// Unsigned scaled form 0...100
|
// Unsigned scaled form 0...100
|
||||||
float LStar = (data[i] & 0xff) * 100f / 255.0f;
|
float LStar = (data[i] & 0xff) * 100f / 255.0f;
|
||||||
float aStar;
|
float aStar;
|
||||||
@@ -2174,7 +2264,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void normalizeColor(int photometricInterpretation, short[] data) throws IIOException {
|
private void normalizeColor(int photometricInterpretation, int numBands, short[] data) throws IIOException {
|
||||||
switch (photometricInterpretation) {
|
switch (photometricInterpretation) {
|
||||||
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
||||||
// Inverse values
|
// Inverse values
|
||||||
@@ -2197,7 +2287,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
float[] temp = new float[3];
|
float[] temp = new float[3];
|
||||||
float scaleL = photometricInterpretation == TIFFExtension.PHOTOMETRIC_CIELAB ? 65535f : 65280f; // Is for ICC lab, assumes the same for ITU....
|
float scaleL = photometricInterpretation == TIFFExtension.PHOTOMETRIC_CIELAB ? 65535f : 65280f; // Is for ICC lab, assumes the same for ITU....
|
||||||
|
|
||||||
for (int i = 0; i < data.length; i += 3) {
|
for (int i = 0; i < data.length; i += numBands) {
|
||||||
// Unsigned scaled form 0...100
|
// Unsigned scaled form 0...100
|
||||||
float LStar = (data[i] & 0xffff) * 100.0f / scaleL;
|
float LStar = (data[i] & 0xffff) * 100.0f / scaleL;
|
||||||
float aStar;
|
float aStar;
|
||||||
@@ -2245,7 +2335,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void normalizeColor(int photometricInterpretation, int[] data) {
|
private void normalizeColor(int photometricInterpretation, @SuppressWarnings("unused") int numBands, int[] data) {
|
||||||
switch (photometricInterpretation) {
|
switch (photometricInterpretation) {
|
||||||
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
case TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO:
|
||||||
// Inverse values
|
// Inverse values
|
||||||
@@ -2264,7 +2354,7 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void normalizeColor(int photometricInterpretation, float[] data) {
|
private void normalizeColor(int photometricInterpretation, @SuppressWarnings("unused") int numBands, float[] data) {
|
||||||
// TODO: Allow param to decide tone mapping strategy, like in the HDRImageReader
|
// TODO: Allow param to decide tone mapping strategy, like in the HDRImageReader
|
||||||
clamp(data);
|
clamp(data);
|
||||||
|
|
||||||
@@ -2643,10 +2733,11 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
});
|
});
|
||||||
reader.addIIOReadProgressListener(new ProgressListenerBase() {
|
reader.addIIOReadProgressListener(new ProgressListenerBase() {
|
||||||
private static final int MAX_W = 78;
|
private static final int MAX_W = 78;
|
||||||
int lastProgress = 0;
|
int lastProgress;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void imageStarted(ImageReader source, int imageIndex) {
|
public void imageStarted(ImageReader source, int imageIndex) {
|
||||||
|
lastProgress = 0;
|
||||||
System.out.print("[");
|
System.out.print("[");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2654,12 +2745,14 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
public void imageProgress(ImageReader source, float percentageDone) {
|
public void imageProgress(ImageReader source, float percentageDone) {
|
||||||
int steps = ((int) (percentageDone * MAX_W) / 100);
|
int steps = ((int) (percentageDone * MAX_W) / 100);
|
||||||
|
|
||||||
for (int i = lastProgress; i < steps; i++) {
|
if (steps > lastProgress) {
|
||||||
System.out.print(".");
|
for (int i = lastProgress; i < steps; i++) {
|
||||||
}
|
System.out.print(".");
|
||||||
|
}
|
||||||
|
|
||||||
System.out.flush();
|
System.out.flush();
|
||||||
lastProgress = steps;
|
lastProgress = steps;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2693,8 +2786,14 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
int width = reader.getWidth(imageNo);
|
// int width = reader.getWidth(imageNo);
|
||||||
int height = reader.getHeight(imageNo);
|
// int height = reader.getHeight(imageNo);
|
||||||
|
if (param.canSetSourceRenderSize()) {
|
||||||
|
int thumbSize = 512;
|
||||||
|
float aspectRatio = reader.getAspectRatio(imageNo);
|
||||||
|
param.setSourceRenderSize(aspectRatio > 1f ? new Dimension(thumbSize, (int) Math.ceil(thumbSize / aspectRatio))
|
||||||
|
: new Dimension((int) Math.ceil(thumbSize * aspectRatio), thumbSize));
|
||||||
|
}
|
||||||
// param.setSourceRegion(new Rectangle(width / 4, height / 4, width / 2, height / 2));
|
// param.setSourceRegion(new Rectangle(width / 4, height / 4, width / 2, height / 2));
|
||||||
// param.setSourceRegion(new Rectangle(100, 300, 400, 400));
|
// param.setSourceRegion(new Rectangle(100, 300, 400, 400));
|
||||||
// param.setSourceRegion(new Rectangle(95, 105, 100, 100));
|
// param.setSourceRegion(new Rectangle(95, 105, 100, 100));
|
||||||
@@ -2706,18 +2805,18 @@ public final class TIFFImageReader extends ImageReaderBase {
|
|||||||
BufferedImage image = reader.read(imageNo, param);
|
BufferedImage image = reader.read(imageNo, param);
|
||||||
System.err.println("Read time: " + (System.currentTimeMillis() - start) + " ms");
|
System.err.println("Read time: " + (System.currentTimeMillis() - start) + " ms");
|
||||||
|
|
||||||
IIOMetadata metadata = reader.getImageMetadata(imageNo);
|
// IIOMetadata metadata = reader.getImageMetadata(imageNo);
|
||||||
if (metadata != null) {
|
// if (metadata != null) {
|
||||||
if (metadata.getNativeMetadataFormatName() != null) {
|
// if (metadata.getNativeMetadataFormatName() != null) {
|
||||||
Node tree = metadata.getAsTree(metadata.getNativeMetadataFormatName());
|
// Node tree = metadata.getAsTree(metadata.getNativeMetadataFormatName());
|
||||||
replaceBytesWithUndefined((IIOMetadataNode) tree);
|
// replaceBytesWithUndefined((IIOMetadataNode) tree);
|
||||||
new XMLSerializer(System.out, "UTF-8").serialize(tree, false);
|
// new XMLSerializer(System.out, "UTF-8").serialize(tree, false);
|
||||||
}
|
// }
|
||||||
/*else*/
|
// /*else*/
|
||||||
if (metadata.isStandardMetadataFormatSupported()) {
|
// if (metadata.isStandardMetadataFormatSupported()) {
|
||||||
new XMLSerializer(System.out, "UTF-8").serialize(metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName), false);
|
// new XMLSerializer(System.out, "UTF-8").serialize(metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName), false);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
System.err.println("image: " + image);
|
System.err.println("image: " + image);
|
||||||
|
|
||||||
|
|||||||
+47
-65
@@ -35,7 +35,6 @@ import com.twelvemonkeys.imageio.ImageWriterBase;
|
|||||||
import com.twelvemonkeys.imageio.color.ColorProfiles;
|
import com.twelvemonkeys.imageio.color.ColorProfiles;
|
||||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||||
import com.twelvemonkeys.imageio.metadata.Entry;
|
import com.twelvemonkeys.imageio.metadata.Entry;
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.Rational;
|
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry;
|
import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry;
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFWriter;
|
import com.twelvemonkeys.imageio.metadata.tiff.TIFFWriter;
|
||||||
@@ -55,12 +54,18 @@ import javax.imageio.spi.ImageWriterSpi;
|
|||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import javax.imageio.stream.ImageOutputStream;
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.color.ICC_ColorSpace;
|
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.*;
|
import java.io.DataOutput;
|
||||||
import java.nio.ByteBuffer;
|
import java.io.DataOutputStream;
|
||||||
import java.util.*;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.zip.Deflater;
|
import java.util.zip.Deflater;
|
||||||
import java.util.zip.DeflaterOutputStream;
|
import java.util.zip.DeflaterOutputStream;
|
||||||
|
|
||||||
@@ -104,8 +109,6 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
// Support storing multiple images in one stream (multi-page TIFF)
|
// Support storing multiple images in one stream (multi-page TIFF)
|
||||||
// Support more of the ImageIO metadata (ie. compression from metadata, etc)
|
// Support more of the ImageIO metadata (ie. compression from metadata, etc)
|
||||||
|
|
||||||
private static final Rational STANDARD_DPI = new Rational(72);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag for active sequence writing
|
* Flag for active sequence writing
|
||||||
*/
|
*/
|
||||||
@@ -176,6 +179,8 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
throw new IllegalArgumentException("Unknown bit/bandOffsets for sample model: " + sampleModel);
|
throw new IllegalArgumentException("Unknown bit/bandOffsets for sample model: " + sampleModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
short offsetType = tiffWriter.offsetSize() == 4 ? TIFF.TYPE_LONG : TIFF.TYPE_LONG8;
|
||||||
|
|
||||||
Map<Integer, Entry> entries = new LinkedHashMap<>();
|
Map<Integer, Entry> entries = new LinkedHashMap<>();
|
||||||
// Copy metadata to output
|
// Copy metadata to output
|
||||||
Directory metadataIFD = metadata.getIFD();
|
Directory metadataIFD = metadata.getIFD();
|
||||||
@@ -189,9 +194,9 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
// TODO: RowsPerStrip - can be entire image (or even 2^32 -1), but it's recommended to write "about 8K bytes" per strip
|
// TODO: RowsPerStrip - can be entire image (or even 2^32 -1), but it's recommended to write "about 8K bytes" per strip
|
||||||
entries.put(TIFF.TAG_ROWS_PER_STRIP, new TIFFEntry(TIFF.TAG_ROWS_PER_STRIP, renderedImage.getHeight()));
|
entries.put(TIFF.TAG_ROWS_PER_STRIP, new TIFFEntry(TIFF.TAG_ROWS_PER_STRIP, renderedImage.getHeight()));
|
||||||
// StripByteCounts - for no compression, entire image data...
|
// StripByteCounts - for no compression, entire image data...
|
||||||
entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, -1)); // Updated later
|
entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, offsetType, -1)); // Updated later
|
||||||
// StripOffsets - can be offset to single strip only
|
// StripOffsets - can be offset to single strip only
|
||||||
entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, -1)); // Updated later
|
entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, offsetType, -1)); // Updated later
|
||||||
|
|
||||||
// TODO: If tiled, write tile indexes etc
|
// TODO: If tiled, write tile indexes etc
|
||||||
// Depending on param.getTilingMode
|
// Depending on param.getTilingMode
|
||||||
@@ -205,10 +210,10 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
|
|
||||||
long ifdSize = tiffWriter.computeIFDSize(entries.values());
|
long ifdSize = tiffWriter.computeIFDSize(entries.values());
|
||||||
long stripOffset = streamPosition + tiffWriter.offsetSize() + ifdSize + tiffWriter.offsetSize();
|
long stripOffset = streamPosition + tiffWriter.offsetSize() + ifdSize + tiffWriter.offsetSize();
|
||||||
long stripByteCount = ((long) renderedImage.getWidth() * renderedImage.getHeight() * pixelSize + 7L) / 8L;
|
long stripByteCount = renderedImage.getHeight() * (((long) renderedImage.getWidth() * pixelSize + 7L) / 8L);
|
||||||
|
|
||||||
entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, TIFF.TYPE_LONG, stripOffset));
|
entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, offsetType, stripOffset));
|
||||||
entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, TIFF.TYPE_LONG, stripByteCount));
|
entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, offsetType, stripByteCount));
|
||||||
|
|
||||||
long ifdPointer = tiffWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes care of ordering tags
|
long ifdPointer = tiffWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes care of ordering tags
|
||||||
nextIFDPointerOffset = imageOutput.getStreamPosition();
|
nextIFDPointerOffset = imageOutput.getStreamPosition();
|
||||||
@@ -259,8 +264,8 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
|
|
||||||
// Update IFD0-pointer, and write IFD
|
// Update IFD0-pointer, and write IFD
|
||||||
if (compression != TIFFBaseline.COMPRESSION_NONE) {
|
if (compression != TIFFBaseline.COMPRESSION_NONE) {
|
||||||
entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, TIFF.TYPE_LONG, stripOffset));
|
entries.put(TIFF.TAG_STRIP_OFFSETS, new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, offsetType, stripOffset));
|
||||||
entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, TIFF.TYPE_LONG, stripByteCount));
|
entries.put(TIFF.TAG_STRIP_BYTE_COUNTS, new TIFFEntry(TIFF.TAG_STRIP_BYTE_COUNTS, offsetType, stripByteCount));
|
||||||
|
|
||||||
long ifdPointer = tiffWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes care of ordering tags
|
long ifdPointer = tiffWriter.writeIFD(entries.values(), imageOutput); // NOTE: Writer takes care of ordering tags
|
||||||
|
|
||||||
@@ -411,7 +416,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
|
|
||||||
case TIFFExtension.COMPRESSION_LZW:
|
case TIFFExtension.COMPRESSION_LZW:
|
||||||
stream = IIOUtil.createStreamAdapter(imageOutput);
|
stream = IIOUtil.createStreamAdapter(imageOutput);
|
||||||
stream = new EncoderStream(stream, new LZWEncoder(((image.getTileWidth() * samplesPerPixel * bitPerSample + 7) / 8) * image.getTileHeight()));
|
stream = new EncoderStream(stream, new LZWEncoder((((long) image.getTileWidth() * samplesPerPixel * bitPerSample + 7) / 8) * image.getTileHeight()));
|
||||||
if (entries.containsKey(TIFF.TAG_PREDICTOR) && entries.get(TIFF.TAG_PREDICTOR).getValue().equals(TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING)) {
|
if (entries.containsKey(TIFF.TAG_PREDICTOR) && entries.get(TIFF.TAG_PREDICTOR).getValue().equals(TIFFExtension.PREDICTOR_HORIZONTAL_DIFFERENCING)) {
|
||||||
stream = new HorizontalDifferencingStream(stream, image.getTileWidth(), samplesPerPixel, bitPerSample, imageOutput.getByteOrder());
|
stream = new HorizontalDifferencingStream(stream, image.getTileWidth(), samplesPerPixel, bitPerSample, imageOutput.getByteOrder());
|
||||||
}
|
}
|
||||||
@@ -526,8 +531,6 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
final int sampleSize = renderedImage.getSampleModel().getSampleSize(0);
|
final int sampleSize = renderedImage.getSampleModel().getSampleSize(0);
|
||||||
final int numBands = renderedImage.getSampleModel().getNumBands();
|
final int numBands = renderedImage.getSampleModel().getNumBands();
|
||||||
|
|
||||||
final ByteBuffer buffer = ByteBuffer.allocate((tileWidth * numBands * sampleSize + 7) / 8);
|
|
||||||
|
|
||||||
for (int yTile = minTileY; yTile < maxYTiles; yTile++) {
|
for (int yTile = minTileY; yTile < maxYTiles; yTile++) {
|
||||||
for (int xTile = minTileX; xTile < maxXTiles; xTile++) {
|
for (int xTile = minTileX; xTile < maxXTiles; xTile++) {
|
||||||
final Raster tile = renderedImage.getTile(xTile, yTile);
|
final Raster tile = renderedImage.getTile(xTile, yTile);
|
||||||
@@ -565,21 +568,17 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
for (int s = 0; s < numBands; s++) {
|
for (int s = 0; s < numBands; s++) {
|
||||||
if (sampleSize == 8 || shift == 0) {
|
if (sampleSize == 8 || shift == 0) {
|
||||||
// Normal interleaved/planar case
|
// Normal interleaved/planar case
|
||||||
buffer.put((byte) (dataBuffer.getElem(b, xOff + bandOffsets[s]) & 0xff));
|
stream.writeByte((byte) (dataBuffer.getElem(b, xOff + bandOffsets[s]) & 0xff));
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// "Packed" case
|
// "Packed" case
|
||||||
buffer.put((byte) (rowBuffer.getElem(b, x - offsetX + bandOffsets[s]) & 0xff));
|
stream.writeByte((byte) (rowBuffer.getElem(b, x - offsetX + bandOffsets[s]) & 0xff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flushBuffer(buffer, stream);
|
flushStream(stream);
|
||||||
|
|
||||||
if (stream instanceof DataOutputStream) {
|
|
||||||
DataOutputStream dataOutputStream = (DataOutputStream) stream;
|
|
||||||
dataOutputStream.flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,25 +586,21 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
|
|
||||||
case DataBuffer.TYPE_USHORT:
|
case DataBuffer.TYPE_USHORT:
|
||||||
case DataBuffer.TYPE_SHORT:
|
case DataBuffer.TYPE_SHORT:
|
||||||
|
final int shortStride = stride / 2;
|
||||||
if (numComponents == 1) {
|
if (numComponents == 1) {
|
||||||
// System.err.println("Writing USHORT -> " + numBands * 2 + "_BYTES");
|
// System.err.println("Writing USHORT -> " + numBands * 2 + "_BYTES");
|
||||||
|
|
||||||
for (int b = 0; b < dataBuffer.getNumBanks(); b++) {
|
for (int b = 0; b < dataBuffer.getNumBanks(); b++) {
|
||||||
for (int y = offsetY; y < tileHeight + offsetY; y++) {
|
for (int y = offsetY; y < tileHeight + offsetY; y++) {
|
||||||
int yOff = y * stride / 2;
|
final int yOff = y * shortStride;
|
||||||
|
|
||||||
for (int x = offsetX; x < tileWidth + offsetX; x++) {
|
for (int x = offsetX; x < tileWidth + offsetX; x++) {
|
||||||
final int xOff = yOff + x;
|
int xOff = yOff + x;
|
||||||
|
|
||||||
buffer.putShort((short) (dataBuffer.getElem(b, xOff) & 0xffff));
|
stream.writeShort((short) (dataBuffer.getElem(b, xOff) & 0xffff));
|
||||||
}
|
}
|
||||||
|
|
||||||
flushBuffer(buffer, stream);
|
flushStream(stream);
|
||||||
|
|
||||||
if (stream instanceof DataOutputStream) {
|
|
||||||
DataOutputStream dataOutputStream = (DataOutputStream) stream;
|
|
||||||
dataOutputStream.flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -636,26 +631,21 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case DataBuffer.TYPE_INT:
|
case DataBuffer.TYPE_INT:
|
||||||
// TODO: This is incorrect for 32 bits/sample, only works for packed (INT_(A)RGB)
|
// TODO: This is incorrect for general 32 bits/sample, only works for packed (INT_(A)RGB) and single channel
|
||||||
|
final int intStride = stride / 4;
|
||||||
if (1 == numComponents) {
|
if (1 == numComponents) {
|
||||||
// System.err.println("Writing INT -> " + numBands * 4 + "_BYTES");
|
// System.err.println("Writing INT -> " + numBands * 4 + "_BYTES");
|
||||||
|
|
||||||
for (int b = 0; b < dataBuffer.getNumBanks(); b++) {
|
for (int b = 0; b < dataBuffer.getNumBanks(); b++) {
|
||||||
for (int y = offsetY; y < tileHeight + offsetY; y++) {
|
for (int y = offsetY; y < tileHeight + offsetY; y++) {
|
||||||
int yOff = y * stride / 4;
|
int yOff = y * intStride;
|
||||||
|
|
||||||
for (int x = offsetX; x < tileWidth + offsetX; x++) {
|
for (int x = offsetX; x < tileWidth + offsetX; x++) {
|
||||||
final int xOff = yOff + x;
|
int xOff = yOff + x;
|
||||||
|
|
||||||
buffer.putInt(dataBuffer.getElem(b, xOff));
|
stream.writeInt(dataBuffer.getElem(b, xOff));
|
||||||
}
|
}
|
||||||
|
|
||||||
flushBuffer(buffer, stream);
|
flushStream(stream);
|
||||||
|
|
||||||
if (stream instanceof DataOutputStream) {
|
|
||||||
DataOutputStream dataOutputStream = (DataOutputStream) stream;
|
|
||||||
dataOutputStream.flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -671,15 +661,11 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
int element = dataBuffer.getElem(b, xOff);
|
int element = dataBuffer.getElem(b, xOff);
|
||||||
|
|
||||||
for (int s = 0; s < numBands; s++) {
|
for (int s = 0; s < numBands; s++) {
|
||||||
buffer.put((byte) ((element >> bitOffsets[s]) & 0xff));
|
stream.writeByte((byte) ((element >> bitOffsets[s]) & 0xff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flushBuffer(buffer, stream);
|
flushStream(stream);
|
||||||
if (stream instanceof DataOutputStream) {
|
|
||||||
DataOutputStream dataOutputStream = (DataOutputStream) stream;
|
|
||||||
dataOutputStream.flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -690,11 +676,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Need to flush/start new compression for each row, for proper LZW/PackBits/Deflate/ZLib
|
flushStream(stream);
|
||||||
if (stream instanceof DataOutputStream) {
|
|
||||||
DataOutputStream dataOutputStream = (DataOutputStream) stream;
|
|
||||||
dataOutputStream.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Report better progress
|
// TODO: Report better progress
|
||||||
processImageProgress((100f * (yTile + 1)) / maxYTiles);
|
processImageProgress((100f * (yTile + 1)) / maxYTiles);
|
||||||
@@ -708,12 +690,12 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
processImageComplete();
|
processImageComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Would be better to solve this on stream level... But writers would then have to explicitly flush the buffer before done.
|
private void flushStream(DataOutput stream) throws IOException {
|
||||||
private void flushBuffer(final ByteBuffer buffer, final DataOutput stream) throws IOException {
|
// Need to flush/start new compression for each row, for proper LZW/PackBits/Deflate/ZLib compression
|
||||||
buffer.flip();
|
if (stream instanceof DataOutputStream) {
|
||||||
stream.write(buffer.array(), buffer.arrayOffset(), buffer.remaining());
|
DataOutputStream dataOutputStream = (DataOutputStream) stream;
|
||||||
|
dataOutputStream.flush();
|
||||||
buffer.clear();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata
|
// Metadata
|
||||||
@@ -893,14 +875,14 @@ public final class TIFFImageWriter extends ImageWriterBase {
|
|||||||
case TIFF.TAG_ARTIST:
|
case TIFF.TAG_ARTIST:
|
||||||
case TIFF.TAG_HOST_COMPUTER:
|
case TIFF.TAG_HOST_COMPUTER:
|
||||||
case TIFF.TAG_COPYRIGHT:
|
case TIFF.TAG_COPYRIGHT:
|
||||||
// Extension
|
// Extension
|
||||||
case TIFF.TAG_DOCUMENT_NAME:
|
case TIFF.TAG_DOCUMENT_NAME:
|
||||||
case TIFF.TAG_PAGE_NAME:
|
case TIFF.TAG_PAGE_NAME:
|
||||||
case TIFF.TAG_X_POSITION:
|
case TIFF.TAG_X_POSITION:
|
||||||
case TIFF.TAG_Y_POSITION:
|
case TIFF.TAG_Y_POSITION:
|
||||||
case TIFF.TAG_PAGE_NUMBER:
|
case TIFF.TAG_PAGE_NUMBER:
|
||||||
case TIFF.TAG_XMP:
|
case TIFF.TAG_XMP:
|
||||||
// Private/Custom
|
// Private/Custom
|
||||||
case TIFF.TAG_IPTC:
|
case TIFF.TAG_IPTC:
|
||||||
case TIFF.TAG_PHOTOSHOP:
|
case TIFF.TAG_PHOTOSHOP:
|
||||||
case TIFF.TAG_PHOTOSHOP_IMAGE_SOURCE_DATA:
|
case TIFF.TAG_PHOTOSHOP_IMAGE_SOURCE_DATA:
|
||||||
|
|||||||
+198
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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.plugins.tiff;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Input stream that provides on-the-fly upsampling of TIFF subsampled YCbCr samples.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: YCbCrUpsamplerStream.java,v 1.0 31.01.13 09:25 haraldk Exp$
|
||||||
|
*/
|
||||||
|
final class YCbCrPlanarUpsamplerStream extends FilterInputStream {
|
||||||
|
|
||||||
|
private final int horizChromaSub;
|
||||||
|
private final int vertChromaSub;
|
||||||
|
private final int yCbCrPos;
|
||||||
|
private final int columns;
|
||||||
|
|
||||||
|
private final int units;
|
||||||
|
private final byte[] decodedRows;
|
||||||
|
int decodedLength;
|
||||||
|
int decodedPos;
|
||||||
|
|
||||||
|
private final byte[] buffer;
|
||||||
|
int bufferLength;
|
||||||
|
int bufferPos;
|
||||||
|
|
||||||
|
public YCbCrPlanarUpsamplerStream(final InputStream stream, final int[] chromaSub, final int yCbCrPos, final int columns) {
|
||||||
|
super(Validate.notNull(stream, "stream"));
|
||||||
|
|
||||||
|
Validate.notNull(chromaSub, "chromaSub");
|
||||||
|
Validate.isTrue(chromaSub.length == 2, "chromaSub.length != 2");
|
||||||
|
|
||||||
|
this.horizChromaSub = chromaSub[0];
|
||||||
|
this.vertChromaSub = chromaSub[1];
|
||||||
|
this.yCbCrPos = yCbCrPos;
|
||||||
|
this.columns = columns;
|
||||||
|
|
||||||
|
units = (columns + horizChromaSub - 1) / horizChromaSub; // If columns % horizChromasSub != 0...
|
||||||
|
// ...each coded row will be padded to fill unit
|
||||||
|
decodedRows = new byte[columns * vertChromaSub];
|
||||||
|
buffer = new byte[units];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetch() throws IOException {
|
||||||
|
if (bufferPos >= bufferLength) {
|
||||||
|
int pos = 0;
|
||||||
|
int read;
|
||||||
|
|
||||||
|
// This *SHOULD* read an entire row of units into the buffer, otherwise decodeRows will throw EOFException
|
||||||
|
while (pos < buffer.length && (read = in.read(buffer, pos, buffer.length - pos)) > 0) {
|
||||||
|
pos += read;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferLength = pos;
|
||||||
|
bufferPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufferLength > 0) {
|
||||||
|
decodeRows();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
decodedLength = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decodeRows() throws EOFException {
|
||||||
|
decodedLength = decodedRows.length;
|
||||||
|
|
||||||
|
for (int u = 0; u < units; u++) {
|
||||||
|
if (u >= bufferLength) {
|
||||||
|
throw new EOFException("Unexpected end of stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode one unit
|
||||||
|
byte c = buffer[u];
|
||||||
|
|
||||||
|
for (int y = 0; y < vertChromaSub; y++) {
|
||||||
|
for (int x = 0; x < horizChromaSub; x++) {
|
||||||
|
// Skip padding at end of row
|
||||||
|
int column = horizChromaSub * u + x;
|
||||||
|
if (column >= columns) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pixelOff = column + columns * y;
|
||||||
|
decodedRows[pixelOff] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferPos = bufferLength;
|
||||||
|
decodedPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decodedPos >= decodedLength) {
|
||||||
|
fetch();
|
||||||
|
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodedRows[decodedPos++] & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decodedPos >= decodedLength) {
|
||||||
|
fetch();
|
||||||
|
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int read = Math.min(decodedLength - decodedPos, len);
|
||||||
|
System.arraycopy(decodedRows, decodedPos, b, off, read);
|
||||||
|
decodedPos += read;
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decodedPos >= decodedLength) {
|
||||||
|
fetch();
|
||||||
|
|
||||||
|
if (decodedLength < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int skipped = (int) Math.min(decodedLength - decodedPos, n);
|
||||||
|
decodedPos += skipped;
|
||||||
|
|
||||||
|
return skipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void reset() throws IOException {
|
||||||
|
throw new IOException("mark/reset not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
-4
@@ -213,8 +213,4 @@ final class YCbCrUpsamplerStream extends FilterInputStream {
|
|||||||
public synchronized void reset() throws IOException {
|
public synchronized void reset() throws IOException {
|
||||||
throw new IOException("mark/reset not supported");
|
throw new IOException("mark/reset not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte clamp(int val) {
|
|
||||||
return (byte) Math.max(0, Math.min(255, val));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -324,6 +324,10 @@ public class TIFFImageMetadataTest {
|
|||||||
dimensionNode.appendChild(verticalPixelSize);
|
dimensionNode.appendChild(verticalPixelSize);
|
||||||
verticalPixelSize.setAttribute("value", String.valueOf(300 / 25.4));
|
verticalPixelSize.setAttribute("value", String.valueOf(300 / 25.4));
|
||||||
|
|
||||||
|
IIOMetadataNode orientation = new IIOMetadataNode("ImageOrientation");
|
||||||
|
dimensionNode.appendChild(orientation);
|
||||||
|
orientation.setAttribute("value", "FlipV");
|
||||||
|
|
||||||
metadata.mergeTree(standardFormat, newTree);
|
metadata.mergeTree(standardFormat, newTree);
|
||||||
|
|
||||||
Directory ifd = metadata.getIFD();
|
Directory ifd = metadata.getIFD();
|
||||||
@@ -332,6 +336,8 @@ public class TIFFImageMetadataTest {
|
|||||||
assertEquals(new Rational(300), ifd.getEntryById(TIFF.TAG_X_RESOLUTION).getValue());
|
assertEquals(new Rational(300), ifd.getEntryById(TIFF.TAG_X_RESOLUTION).getValue());
|
||||||
assertNotNull(ifd.getEntryById(TIFF.TAG_Y_RESOLUTION));
|
assertNotNull(ifd.getEntryById(TIFF.TAG_Y_RESOLUTION));
|
||||||
assertEquals(new Rational(300), ifd.getEntryById(TIFF.TAG_Y_RESOLUTION).getValue());
|
assertEquals(new Rational(300), ifd.getEntryById(TIFF.TAG_Y_RESOLUTION).getValue());
|
||||||
|
assertNotNull(ifd.getEntryById(TIFF.TAG_ORIENTATION));
|
||||||
|
assertEquals(TIFFExtension.ORIENTATION_BOTLEFT, ((Number) ifd.getEntryById(TIFF.TAG_ORIENTATION).getValue()).intValue());
|
||||||
|
|
||||||
// Should keep DPI as unit
|
// Should keep DPI as unit
|
||||||
assertNotNull(ifd.getEntryById(TIFF.TAG_RESOLUTION_UNIT));
|
assertNotNull(ifd.getEntryById(TIFF.TAG_RESOLUTION_UNIT));
|
||||||
|
|||||||
+52
-7
@@ -44,7 +44,7 @@ import javax.imageio.metadata.IIOMetadata;
|
|||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
@@ -56,7 +56,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.AdditionalMatchers.and;
|
import static org.mockito.AdditionalMatchers.and;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
@@ -142,6 +145,8 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
|||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-08.tif"), new Dimension(73, 43)), // Palette 8 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-08.tif"), new Dimension(73, 43)), // Palette 8 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-16.tif"), new Dimension(73, 43)), // Palette 16 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-16.tif"), new Dimension(73, 43)), // Palette 16 bit/sample
|
||||||
// RGB Interleaved (PlanarConfiguration: 1)
|
// RGB Interleaved (PlanarConfiguration: 1)
|
||||||
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-02.tif"), new Dimension(73, 43)), // RGB 2 bit/sample
|
||||||
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-04.tif"), new Dimension(73, 43)), // RGB 4 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-08.tif"), new Dimension(73, 43)), // RGB 8 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-08.tif"), new Dimension(73, 43)), // RGB 8 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-10.tif"), new Dimension(73, 43)), // RGB 10 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-10.tif"), new Dimension(73, 43)), // RGB 10 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-12.tif"), new Dimension(73, 43)), // RGB 12 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-12.tif"), new Dimension(73, 43)), // RGB 12 bit/sample
|
||||||
@@ -171,7 +176,22 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
|||||||
new TestData(getClassLoaderResource("/tiff/jpeg-lossless-24bit-rgb.tif"), new Dimension(512, 512)), // Lossless JPEG RGB, 8 bit/sample
|
new TestData(getClassLoaderResource("/tiff/jpeg-lossless-24bit-rgb.tif"), new Dimension(512, 512)), // Lossless JPEG RGB, 8 bit/sample
|
||||||
// Custom PIXTIFF ZIP (Compression: 50013)
|
// Custom PIXTIFF ZIP (Compression: 50013)
|
||||||
new TestData(getClassLoaderResource("/tiff/pixtiff/40-8bit-gray-zip.tif"), new Dimension(801, 1313)), // ZIP Gray, 8 bit/sample
|
new TestData(getClassLoaderResource("/tiff/pixtiff/40-8bit-gray-zip.tif"), new Dimension(801, 1313)), // ZIP Gray, 8 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/part.tif"), new Dimension(50, 50)) // Gray/BlackIsZero, uncompressed, striped signed int (SampleFormat 2)
|
new TestData(getClassLoaderResource("/tiff/part.tif"), new Dimension(50, 50)), // Gray/BlackIsZero, uncompressed, striped signed int (SampleFormat 2)
|
||||||
|
// Planar YCbCr full chroma
|
||||||
|
new TestData(getClassLoaderResource("/tiff/lab-a-8.tiff"), new Dimension(589, 340)), // Lab + Alpha, uncompressed, striped
|
||||||
|
new TestData(getClassLoaderResource("/tiff/lab-a-16.tiff"), new Dimension(589, 340)), // Lab + Alpha, 16 bit uncompressed, striped
|
||||||
|
// Planar YCbCr full chroma
|
||||||
|
new TestData(getClassLoaderResource("/tiff/planar-yuv444-jpeg-uncompressed.tif"), new Dimension(256, 64)), // YCbCr, JPEG coefficients, uncompressed, striped
|
||||||
|
new TestData(getClassLoaderResource("/tiff/planar-yuv444-jpeg-lzw.tif"), new Dimension(256, 64)), // YCbCr, JPEG coefficients, LZW compressed, striped
|
||||||
|
// Planar YCbCr subsampled
|
||||||
|
new TestData(getClassLoaderResource("/tiff/planar-yuv422-bt601-uncompressed.tif"), new Dimension(256, 64)), // YCbCr, Rec.601 coefficients, uncompressed, striped
|
||||||
|
new TestData(getClassLoaderResource("/tiff/planar-yuv422-bt601-lzw.tif"), new Dimension(256, 64)), // YCbCr, Rec.601 coefficients,LZW compressed, striped
|
||||||
|
new TestData(getClassLoaderResource("/tiff/planar-yuv422-jpeg-uncompressed.tif"), new Dimension(256, 64)), // YCbCr, JPEG coefficients, uncompressed, striped
|
||||||
|
new TestData(getClassLoaderResource("/tiff/planar-yuv422-jpeg-lzw.tif"), new Dimension(256, 64)), // YCbCr, JPEG coefficients,LZW compressed, striped
|
||||||
|
new TestData(getClassLoaderResource("/tiff/planar-yuv420-jpeg-uncompressed.tif"), new Dimension(256, 64)), // YCbCr, JPEG coefficients, uncompressed, striped
|
||||||
|
new TestData(getClassLoaderResource("/tiff/planar-yuv420-jpeg-lzw.tif"), new Dimension(256, 64)), // YCbCr, JPEG coefficients,LZW compressed, striped
|
||||||
|
new TestData(getClassLoaderResource("/tiff/planar-yuv410-jpeg-uncompressed.tif"), new Dimension(256, 64)), // YCbCr, JPEG coefficients, uncompressed, striped
|
||||||
|
new TestData(getClassLoaderResource("/tiff/planar-yuv410-jpeg-lzw.tif"), new Dimension(256, 64)) // YCbCr, JPEG coefficients,LZW compressed, striped
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,6 +214,8 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
|||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-08.tif"), new Dimension(73, 43)), // Palette 8 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-08.tif"), new Dimension(73, 43)), // Palette 8 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-16.tif"), new Dimension(73, 43)), // Palette 16 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-palette-16.tif"), new Dimension(73, 43)), // Palette 16 bit/sample
|
||||||
// RGB Interleaved (PlanarConfiguration: 1)
|
// RGB Interleaved (PlanarConfiguration: 1)
|
||||||
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-02.tif"), new Dimension(73, 43)), // RGB 2 bit/sample
|
||||||
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-04.tif"), new Dimension(73, 43)), // RGB 4 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-08.tif"), new Dimension(73, 43)), // RGB 8 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-08.tif"), new Dimension(73, 43)), // RGB 8 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-10.tif"), new Dimension(73, 43)), // RGB 10 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-10.tif"), new Dimension(73, 43)), // RGB 10 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-12.tif"), new Dimension(73, 43)), // RGB 12 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-12.tif"), new Dimension(73, 43)), // RGB 12 bit/sample
|
||||||
@@ -221,9 +243,6 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
|||||||
|
|
||||||
private List<TestData> getUnsupportedTestData() {
|
private List<TestData> getUnsupportedTestData() {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
// RGB Interleaved (PlanarConfiguration: 1)
|
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-02.tif"), new Dimension(73, 43)), // RGB 2 bit/sample
|
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-contig-04.tif"), new Dimension(73, 43)), // RGB 4 bit/sample
|
|
||||||
// RGB Planar (PlanarConfiguration: 2)
|
// RGB Planar (PlanarConfiguration: 2)
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-02.tif"), new Dimension(73, 43)), // RGB 2 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-02.tif"), new Dimension(73, 43)), // RGB 2 bit/sample
|
||||||
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-04.tif"), new Dimension(73, 43)) // RGB 4 bit/sample
|
new TestData(getClassLoaderResource("/tiff/depth/flower-rgb-planar-04.tif"), new Dimension(73, 43)) // RGB 4 bit/sample
|
||||||
@@ -876,6 +895,32 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
|||||||
assertSubsampledImageDataEquals("Subsampled image data does not match expected", image, subsampled, param);
|
assertSubsampledImageDataEquals("Subsampled image data does not match expected", image, subsampled, param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadLittleEndian4444ARGB() throws IOException {
|
||||||
|
ImageReader reader = createReader();
|
||||||
|
|
||||||
|
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/tiff/little-endian-rgba-4444.tiff"))) {
|
||||||
|
reader.setInput(stream);
|
||||||
|
|
||||||
|
BufferedImage image = null;
|
||||||
|
try {
|
||||||
|
image = reader.read(0);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
failBecause("Image could not be read", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNotNull(image);
|
||||||
|
assertEquals(589, image.getWidth());
|
||||||
|
assertEquals(340, image.getHeight());
|
||||||
|
|
||||||
|
assertRGBEquals("Red", 0xffff1111, image.getRGB(124, 42), 4);
|
||||||
|
assertRGBEquals("Green", 0xff66ee11, image.getRGB(476, 100), 4);
|
||||||
|
assertRGBEquals("Yellow", 0xffffff00, image.getRGB(312, 186), 4);
|
||||||
|
assertRGBEquals("Blue", 0xff1155dd, image.getRGB(366, 192), 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadUnsupported() throws IOException {
|
public void testReadUnsupported() throws IOException {
|
||||||
ImageReader reader = createReader();
|
ImageReader reader = createReader();
|
||||||
@@ -886,7 +931,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
|
|||||||
for (int i = 0; i < data.getImageCount(); i++) {
|
for (int i = 0; i < data.getImageCount(); i++) {
|
||||||
try {
|
try {
|
||||||
reader.read(i);
|
reader.read(i);
|
||||||
fail("Sample should be moved from unsupported to normal test case");
|
fail("Sample should be moved from unsupported to normal test case: " + data);
|
||||||
}
|
}
|
||||||
catch (IIOException e) {
|
catch (IIOException e) {
|
||||||
assertThat(e.getMessage().toLowerCase(), containsString("unsupported"));
|
assertThat(e.getMessage().toLowerCase(), containsString("unsupported"));
|
||||||
|
|||||||
+125
-5
@@ -36,6 +36,7 @@ import com.twelvemonkeys.imageio.metadata.tiff.Rational;
|
|||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
|
||||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||||
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
import com.twelvemonkeys.imageio.stream.ByteArrayImageInputStream;
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
import com.twelvemonkeys.imageio.util.ImageWriterAbstractTest;
|
||||||
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
import com.twelvemonkeys.io.FastByteArrayOutputStream;
|
||||||
import com.twelvemonkeys.io.NullOutputStream;
|
import com.twelvemonkeys.io.NullOutputStream;
|
||||||
@@ -43,7 +44,12 @@ import com.twelvemonkeys.io.NullOutputStream;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
import javax.imageio.*;
|
import javax.imageio.IIOImage;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.ImageWriteParam;
|
||||||
|
import javax.imageio.ImageWriter;
|
||||||
import javax.imageio.event.IIOWriteProgressListener;
|
import javax.imageio.event.IIOWriteProgressListener;
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||||
@@ -53,10 +59,15 @@ import javax.imageio.stream.FileCacheImageOutputStream;
|
|||||||
import javax.imageio.stream.FileImageOutputStream;
|
import javax.imageio.stream.FileImageOutputStream;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import javax.imageio.stream.ImageOutputStream;
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
|
import javax.imageio.stream.ImageOutputStreamImpl;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.color.*;
|
||||||
import java.awt.image.RenderedImage;
|
import java.awt.image.*;
|
||||||
import java.io.*;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -152,6 +163,29 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTest<TIFFImageWriter
|
|||||||
assertEquals(resolutionValue, yResolution.getValue());
|
assertEquals(resolutionValue, yResolution.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testByteCountsForUncompressed() throws IOException {
|
||||||
|
// See issue #863
|
||||||
|
BufferedImage image = ImageTypeSpecifiers.createGrayscale(2, DataBuffer.TYPE_BYTE)
|
||||||
|
.createBufferedImage(43, 2); // 43 + 2 = 86 bits/line;
|
||||||
|
|
||||||
|
ImageWriter writer = createWriter();
|
||||||
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
try (ImageOutputStream stream = ImageIO.createImageOutputStream(buffer)) {
|
||||||
|
writer.setOutput(stream);
|
||||||
|
writer.write(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (ImageInputStream stream = new ByteArrayImageInputStream(buffer.toByteArray())) {
|
||||||
|
Directory entries = new TIFFReader().read(stream);
|
||||||
|
Entry byteCountsEntry = entries.getEntryById(TIFF.TAG_STRIP_BYTE_COUNTS);
|
||||||
|
assertNotNull(byteCountsEntry);
|
||||||
|
|
||||||
|
assertEquals(22, ((Number) byteCountsEntry.getValue()).intValue()); // 86 bits/line, needs 88 bits * 2 => 22 bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteWithCustomSoftwareNative() throws IOException {
|
public void testWriteWithCustomSoftwareNative() throws IOException {
|
||||||
String softwareString = "12M TIFF Test 1.0 (build $foo$)";
|
String softwareString = "12M TIFF Test 1.0 (build $foo$)";
|
||||||
@@ -638,7 +672,12 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTest<TIFFImageWriter
|
|||||||
int maxH = Math.min(300, image.getHeight());
|
int maxH = Math.min(300, image.getHeight());
|
||||||
for (int y = 0; y < maxH; y++) {
|
for (int y = 0; y < maxH; y++) {
|
||||||
for (int x = 0; x < image.getWidth(); x++) {
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
assertRGBEquals(String.format("Pixel differ: @%d,%d", x, y), orig.getRGB(x, y), image.getRGB(x, y), 0);
|
try {
|
||||||
|
assertRGBEquals("", orig.getRGB(x, y), image.getRGB(x, y), 0);
|
||||||
|
}
|
||||||
|
catch (AssertionError err) {
|
||||||
|
fail(String.format("Pixel differ: @%d,%d %s", x, y, err.getMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1266,6 +1305,50 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTest<TIFFImageWriter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShortOverflowHuge() throws IOException {
|
||||||
|
int width = 34769;
|
||||||
|
int height = 33769;
|
||||||
|
|
||||||
|
// Create a huge image without actually allocating memory...
|
||||||
|
DataBuffer buffer = new NullDataBuffer(DataBuffer.TYPE_USHORT, width * height);
|
||||||
|
WritableRaster raster = Raster.createWritableRaster(new ComponentSampleModel(buffer.getDataType(), width, height, 1, width, new int[] {0}), buffer, null);
|
||||||
|
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, buffer.getDataType());
|
||||||
|
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
|
||||||
|
|
||||||
|
// Write image without any exception
|
||||||
|
TIFFImageWriter writer = createWriter();
|
||||||
|
try (ImageOutputStream stream = new NullImageOutputStream()) {
|
||||||
|
writer.setOutput(stream);
|
||||||
|
writer.write(image);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writer.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntOverflowHuge() throws IOException {
|
||||||
|
int width = 34769;
|
||||||
|
int height = 33769;
|
||||||
|
|
||||||
|
// Create a huge image without actually allocating memory...
|
||||||
|
DataBuffer buffer = new NullDataBuffer(DataBuffer.TYPE_INT, width * height);
|
||||||
|
WritableRaster raster = Raster.createWritableRaster(new ComponentSampleModel(buffer.getDataType(), width, height, 1, width, new int[] {0}), buffer, null);
|
||||||
|
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, buffer.getDataType());
|
||||||
|
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
|
||||||
|
|
||||||
|
// Write image without any exception
|
||||||
|
TIFFImageWriter writer = createWriter();
|
||||||
|
try (ImageOutputStream stream = new NullImageOutputStream()) {
|
||||||
|
writer.setOutput(stream);
|
||||||
|
writer.write(image);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
writer.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class ImageInfo {
|
private static class ImageInfo {
|
||||||
final int width;
|
final int width;
|
||||||
final int height;
|
final int height;
|
||||||
@@ -1278,4 +1361,41 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTest<TIFFImageWriter
|
|||||||
this.compression = compression;
|
this.compression = compression;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special purpose output stream that acts as a sink
|
||||||
|
private static final class NullImageOutputStream extends ImageOutputStreamImpl {
|
||||||
|
@Override
|
||||||
|
public void write(int b) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special purpose data buffer that does not require memory, to allow very large images
|
||||||
|
private static final class NullDataBuffer extends DataBuffer {
|
||||||
|
public NullDataBuffer(int type, int size) {
|
||||||
|
super(type, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getElem(int bank, int i) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setElem(int bank, int i, int val) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-webp</artifactId>
|
<artifactId>imageio-webp</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: WebP plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: WebP plugin</name>
|
||||||
|
|||||||
+30
-22
@@ -31,6 +31,28 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.webp;
|
package com.twelvemonkeys.imageio.plugins.webp;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.color.ICC_ColorSpace;
|
||||||
|
import java.awt.color.ICC_Profile;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.ColorConvertOp;
|
||||||
|
import java.awt.image.ColorModel;
|
||||||
|
import java.awt.image.DataBuffer;
|
||||||
|
import java.awt.image.WritableRaster;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.ImageReadParam;
|
||||||
|
import javax.imageio.ImageReader;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||||
import com.twelvemonkeys.imageio.color.ColorProfiles;
|
import com.twelvemonkeys.imageio.color.ColorProfiles;
|
||||||
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
import com.twelvemonkeys.imageio.color.ColorSpaces;
|
||||||
@@ -45,23 +67,6 @@ import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
|||||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||||
import com.twelvemonkeys.imageio.util.RasterUtils;
|
import com.twelvemonkeys.imageio.util.RasterUtils;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
|
||||||
import javax.imageio.ImageReadParam;
|
|
||||||
import javax.imageio.ImageReader;
|
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.color.ICC_ColorSpace;
|
|
||||||
import java.awt.color.ICC_Profile;
|
|
||||||
import java.awt.image.*;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebPImageReader
|
* WebPImageReader
|
||||||
*/
|
*/
|
||||||
@@ -72,6 +77,7 @@ final class WebPImageReader extends ImageReaderBase {
|
|||||||
private LSBBitReader lsbBitReader;
|
private LSBBitReader lsbBitReader;
|
||||||
|
|
||||||
// Either VP8_, VP8L or VP8X chunk
|
// Either VP8_, VP8L or VP8X chunk
|
||||||
|
private long fileSize;
|
||||||
private VP8xChunk header;
|
private VP8xChunk header;
|
||||||
private ICC_Profile iccProfile;
|
private ICC_Profile iccProfile;
|
||||||
private final List<AnimationFrame> frames = new ArrayList<>();
|
private final List<AnimationFrame> frames = new ArrayList<>();
|
||||||
@@ -82,6 +88,7 @@ final class WebPImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetMembers() {
|
protected void resetMembers() {
|
||||||
|
fileSize = -1;
|
||||||
header = null;
|
header = null;
|
||||||
iccProfile = null;
|
iccProfile = null;
|
||||||
lsbBitReader = null;
|
lsbBitReader = null;
|
||||||
@@ -119,7 +126,7 @@ final class WebPImageReader extends ImageReaderBase {
|
|||||||
RIFFChunk frame = frames.isEmpty() ? header : frames.get(frames.size() - 1);
|
RIFFChunk frame = frames.isEmpty() ? header : frames.get(frames.size() - 1);
|
||||||
imageInput.seek(frame.offset + frame.length);
|
imageInput.seek(frame.offset + frame.length);
|
||||||
|
|
||||||
while (imageInput.getStreamPosition() < imageInput.length()) {
|
while (imageInput.getStreamPosition() < fileSize) {
|
||||||
int nextChunk = imageInput.readInt();
|
int nextChunk = imageInput.readInt();
|
||||||
long chunkLength = imageInput.readUnsignedInt();
|
long chunkLength = imageInput.readUnsignedInt();
|
||||||
long chunkStart = imageInput.getStreamPosition();
|
long chunkStart = imageInput.getStreamPosition();
|
||||||
@@ -184,7 +191,7 @@ final class WebPImageReader extends ImageReaderBase {
|
|||||||
throw new IIOException(String.format("Not a WebP file, invalid 'RIFF' magic: '%s'", fourCC(riff)));
|
throw new IIOException(String.format("Not a WebP file, invalid 'RIFF' magic: '%s'", fourCC(riff)));
|
||||||
}
|
}
|
||||||
|
|
||||||
imageInput.readUnsignedInt(); // Skip file size NOTE: LITTLE endian!
|
fileSize = 8 + imageInput.readUnsignedInt(); // 8 + RIFF container length (LITTLE endian) == file size
|
||||||
|
|
||||||
int webp = imageInput.readInt();
|
int webp = imageInput.readInt();
|
||||||
if (webp != WebP.WEBP_MAGIC) {
|
if (webp != WebP.WEBP_MAGIC) {
|
||||||
@@ -282,7 +289,7 @@ final class WebPImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
if (header.containsICCP) {
|
if (header.containsICCP) {
|
||||||
// ICCP chunk must be first chunk, if present
|
// ICCP chunk must be first chunk, if present
|
||||||
while (iccProfile == null && imageInput.getStreamPosition() < imageInput.length()) {
|
while (iccProfile == null && imageInput.getStreamPosition() < fileSize) {
|
||||||
int nextChunk = imageInput.readInt();
|
int nextChunk = imageInput.readInt();
|
||||||
long chunkLength = imageInput.readUnsignedInt();
|
long chunkLength = imageInput.readUnsignedInt();
|
||||||
long chunkStart = imageInput.getStreamPosition();
|
long chunkStart = imageInput.getStreamPosition();
|
||||||
@@ -305,6 +312,7 @@ final class WebPImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
|
System.out.println("file size: " + fileSize + " (stream length: " + imageInput.length() + ")");
|
||||||
System.out.println("header: " + header);
|
System.out.println("header: " + header);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -422,7 +430,7 @@ final class WebPImageReader extends ImageReaderBase {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
imageInput.seek(header.offset + header.length);
|
imageInput.seek(header.offset + header.length);
|
||||||
readVP8Extended(destination, param, imageInput.length());
|
readVP8Extended(destination, param, fileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -591,7 +599,7 @@ final class WebPImageReader extends ImageReaderBase {
|
|||||||
// TODO: WebP spec says possible EXIF and XMP chunks are always AFTER image data
|
// TODO: WebP spec says possible EXIF and XMP chunks are always AFTER image data
|
||||||
imageInput.seek(header.offset + header.length);
|
imageInput.seek(header.offset + header.length);
|
||||||
|
|
||||||
while (imageInput.getStreamPosition() < imageInput.length()) {
|
while (imageInput.getStreamPosition() < fileSize) {
|
||||||
int nextChunk = imageInput.readInt();
|
int nextChunk = imageInput.readInt();
|
||||||
long chunkLength = imageInput.readUnsignedInt();
|
long chunkLength = imageInput.readUnsignedInt();
|
||||||
long chunkStart = imageInput.getStreamPosition();
|
long chunkStart = imageInput.getStreamPosition();
|
||||||
|
|||||||
+34
-7
@@ -1,20 +1,22 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.webp;
|
package com.twelvemonkeys.imageio.plugins.webp;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
import static java.util.Arrays.asList;
|
||||||
|
|
||||||
import org.junit.Test;
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageReadParam;
|
import javax.imageio.ImageReadParam;
|
||||||
import javax.imageio.ImageTypeSpecifier;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebPImageReaderTest
|
* WebPImageReaderTest
|
||||||
@@ -102,4 +104,29 @@ public class WebPImageReaderTest extends ImageReaderAbstractTest<WebPImageReader
|
|||||||
reader.dispose();
|
reader.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadFromUnknownStreamLength() throws IOException {
|
||||||
|
// See #672, image was not decoded and returned all black, when the stream length was unknown (-1).
|
||||||
|
WebPImageReader reader = createReader();
|
||||||
|
|
||||||
|
try (ImageInputStream stream = new MemoryCacheImageInputStream(getClassLoaderResource("/webp/photo-iccp-adobergb.webp").openStream()) {
|
||||||
|
@Override public long length() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
reader.setInput(stream);
|
||||||
|
|
||||||
|
// We'll read a small portion of the image into a destination type that use sRGB
|
||||||
|
ImageReadParam param = new ImageReadParam();
|
||||||
|
param.setDestinationType(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR));
|
||||||
|
param.setSourceRegion(new Rectangle(10, 10, 20, 20));
|
||||||
|
|
||||||
|
BufferedImage image = reader.read(0, param);
|
||||||
|
assertRGBEquals("RGB values differ, image all black?", 0xFFEC9800, image.getRGB(5, 5), 8);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
reader.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
<artifactId>imageio</artifactId>
|
<artifactId>imageio</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>imageio-xwd</artifactId>
|
<artifactId>imageio-xwd</artifactId>
|
||||||
<name>TwelveMonkeys :: ImageIO :: XWD plugin</name>
|
<name>TwelveMonkeys :: ImageIO :: XWD plugin</name>
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>TwelveMonkeys</name>
|
<name>TwelveMonkeys</name>
|
||||||
<description>TwelveMonkeys parent POM</description>
|
<description>TwelveMonkeys parent POM</description>
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys</groupId>
|
<groupId>com.twelvemonkeys</groupId>
|
||||||
<artifactId>twelvemonkeys</artifactId>
|
<artifactId>twelvemonkeys</artifactId>
|
||||||
<version>3.8.2</version>
|
<version>3.8.4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|||||||
Reference in New Issue
Block a user