mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 03:55:28 -04:00
#675 PSD 16/32 bit layer support
(cherry picked from commit 48691139a30baca340d5a603207491a33f183943)
This commit is contained in:
parent
3f74b2ddf3
commit
b6c76d8566
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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++;
|
||||||
|
@ -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()) {
|
||||||
|
@ -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();
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
@ -45,10 +45,13 @@ 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.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 +621,65 @@ 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());
|
||||||
|
|
||||||
|
assertRGBEquals("RGB differ at (0,0)", 0xff090b0b, layer2.getRGB(0, 0), 4);
|
||||||
|
assertRGBEquals("RGB differ at (399,0)", 0xff090b0b, layer2.getRGB(399, 0), 4);
|
||||||
|
assertRGBEquals("RGB differ at (200,95)", 0x00ffffff, layer2.getRGB(200, 95), 4); // Transparent
|
||||||
|
assertRGBEquals("RGB differ at (0,191)", 0xff090b0b, layer2.getRGB(0, 190), 4);
|
||||||
|
assertRGBEquals("RGB differ at (399,191)", 0xff090b0b, 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());
|
||||||
|
|
||||||
|
assertRGBEquals("RGB differ at (0,0)", 0xffeec335, layer3.getRGB(0, 0), 4);
|
||||||
|
assertRGBEquals("RGB differ at (399,0)", 0xffeec335, layer3.getRGB(399, 0), 4);
|
||||||
|
assertRGBEquals("RGB differ at (200,95)", 0xffdb3b3b, layer3.getRGB(200, 95), 4); // Red
|
||||||
|
assertRGBEquals("RGB differ at (0,191)", 0xffeec335, layer3.getRGB(0, 190), 4);
|
||||||
|
assertRGBEquals("RGB differ at (399,191)", 0xffeec335, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -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
|
||||||
|
};
|
||||||
|
}
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user