TMI-PSD: Added support for PSB (aka "Large Document Format")

Added support for 32 bit channels.
Added test cases + fixed a few bugs
General code clean-up
This commit is contained in:
Harald Kuhr
2014-09-09 16:36:18 +02:00
parent 7e88a6f7e3
commit 06674d1273
46 changed files with 1067 additions and 459 deletions
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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 org.w3c.dom.Node;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,7 +42,7 @@ import java.io.InputStream;
* @author last modified by $Author: haraldk$
* @version $Id: ICCProfile.java,v 1.0 May 20, 2008 6:24:10 PM haraldk Exp$
*/
class ICCProfile extends PSDImageResource {
final class ICCProfile extends PSDImageResource {
private ICC_Profile profile;
ICCProfile(final short pId, final ImageInputStream pInput) throws IOException {
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,15 +35,20 @@ package com.twelvemonkeys.imageio.plugins.psd;
* @author last modified by $Author: haraldk$
* @version $Id: PSD.java,v 1.0 Apr 29, 2008 4:47:47 PM haraldk Exp$
*
* @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">http://www.fileformat.info/format/psd/egff.htm</a>
*/
interface PSD {
/** PSD 2+ Native format (.PSD) identifier "8BPS" */
int SIGNATURE_8BPS = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'S';
// TODO: Is this ever used??! Spec says (and sample files uses) 8BPS + version == 2 for PSB...
/** PSD 5+ Large Document Format (.PSB) identifier "8BPB" */
int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B';
int VERSION_PSD = 1;
int VERSION_PSB = 2;
/** PSD Resource type identifier "8BIM" */
int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
@@ -105,7 +110,7 @@ interface PSD {
// Color Modes
/** Bitmap (monochrome) */
short COLOR_MODE_MONOCHROME = 0;
short COLOR_MODE_BITMAP = 0;
/** Gray-scale */
short COLOR_MODE_GRAYSCALE = 1;
@@ -541,4 +546,13 @@ interface PSD {
*/
int RES_PRINT_FLAGS_INFORMATION = 0x2710;
int RES_PATH_INFO_MAX = 0x0bb6;
int RES_PATH_INFO_MIN = 0x07d0;
/** Plug-In resource(s). Resources added by a plug-in. See the plug-in API found in the SDK documentation */
int RES_PLUGIN_MIN = 0x0fa0;
/** Plug-In resource(s). Resources added by a plug-in. See the plug-in API found in the SDK documentation */
int RES_PLUGIN_MAX = 0x1387;
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -40,7 +40,7 @@ import java.util.List;
* @author last modified by $Author: haraldk$
* @version $Id: PSDAlphaChannelInfo.java,v 1.0 May 2, 2008 5:33:40 PM haraldk Exp$
*/
class PSDAlphaChannelInfo extends PSDImageResource {
final class PSDAlphaChannelInfo extends PSDImageResource {
List<String> names;
public PSDAlphaChannelInfo(short pId, final ImageInputStream pInput) throws IOException {
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,7 +35,7 @@ package com.twelvemonkeys.imageio.plugins.psd;
* @author last modified by $Author: haraldk$
* @version $Id: PSDChannelInfo.java,v 1.0 May 6, 2008 2:46:23 PM haraldk Exp$
*/
class PSDChannelInfo {
final class PSDChannelInfo {
final short channelId;
final long length;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -38,7 +38,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: PSDChannelSourceDestinationRange.java,v 1.0 May 6, 2008 5:14:13 PM haraldk Exp$
*/
class PSDChannelSourceDestinationRange {
final class PSDChannelSourceDestinationRange {
private String channel;
private short sourceBlack;
private short sourceWhite;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,7 +43,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: PSDColorData.java,v 1.0 Apr 29, 2008 5:33:01 PM haraldk Exp$
*/
class PSDColorData {
final class PSDColorData {
final byte[] colors;
private IndexColorModel colorModel;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$
*/
class PSDDisplayInfo extends PSDImageResource {
final class PSDDisplayInfo extends PSDImageResource {
// TODO: Size of this struct should be 14.. Does not compute... Something bogus here
// ColorSpace definitions:
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.metadata.Directory;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -38,8 +38,8 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: PSDGlobalLayerMask.java,v 1.0 May 8, 2008 5:33:48 PM haraldk Exp$
*/
class PSDGlobalLayerMask {
final int colorSpace;
final class PSDGlobalLayerMask {
final int colorSpace;
final int color1;
final int color2;
final int color3;
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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 javax.imageio.stream.ImageInputStream;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,8 +28,9 @@
package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.IOException;
/**
@@ -39,19 +40,21 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: PSDHeader.java,v 1.0 Apr 29, 2008 5:18:22 PM haraldk Exp$
*/
class PSDHeader {
// The header is 26 bytes in length and is structured as follows:
final class PSDHeader {
static final int PSD_MAX_SIZE = 30000;
static final int PSB_MAX_SIZE = 300000;
// The header is 26 bytes in length and is structured as follows:
//
// typedef struct _PSD_HEADER
// {
// BYTE Signature[4]; /* File ID "8BPS" */
// WORD Version; /* Version number, always 1 */
// WORD Version; /* Version number, always 1. 2 for PSB */
// BYTE Reserved[6]; /* Reserved, must be zeroed */
// WORD Channels; /* Number of color channels (1-24) including alpha
// WORD Channels; /* Number of color channels (1-56) including alpha
// channels */
// LONG Rows; /* Height of image in pixels (1-30000) */
// LONG Columns; /* Width of image in pixels (1-30000) */
// WORD Depth; /* Number of bits per channel (1, 8, and 16) */
// LONG Rows; /* Height of image in pixels (1-30000/1-300000 for PSB) */
// LONG Columns; /* Width of image in pixels (1-30000/1-300000 for PSB) */
// WORD Depth; /* Number of bits per channel (1, 8, 16 or 32) */
// WORD Mode; /* Color mode */
// } PSD_HEADER;
@@ -60,8 +63,9 @@ class PSDHeader {
final int height;
final short bits;
final short mode;
final boolean largeFormat;
PSDHeader(final ImageInputStream pInput) throws IOException {
PSDHeader(final DataInput pInput) throws IOException {
int signature = pInput.readInt();
if (signature != PSD.SIGNATURE_8BPS) {
throw new IIOException("Not a PSD document, expected signature \"8BPS\": \"" + PSDUtil.intToStr(signature) + "\" (0x" + Integer.toHexString(signature) + ")");
@@ -70,67 +74,105 @@ class PSDHeader {
int version = pInput.readUnsignedShort();
switch (version) {
case 1:
case PSD.VERSION_PSD:
largeFormat = false;
break;
case PSD.VERSION_PSB:
largeFormat = true;
break;
case 2:
throw new IIOException("Photoshop Large Document Format (PSB) not supported yet.");
default:
throw new IIOException(String.format("Unknown PSD version, expected 1 or 2: 0x%08x", version));
}
byte[] reserved = new byte[6];
pInput.readFully(reserved);
pInput.readFully(reserved); // We don't really care
channels = pInput.readShort();
if (channels <= 0) {
throw new IIOException(String.format("Unsupported number of channels: %d", channels));
}
height = pInput.readInt(); // Rows
width = pInput.readInt(); // Columns
bits = pInput.readShort();
switch (bits) {
case 1:
case 8:
case 16:
case 32:
break;
default:
throw new IIOException(String.format("Unsupported bit depth for PSD: %d bits", bits));
}
mode = pInput.readShort();
switch (mode) {
case PSD.COLOR_MODE_BITMAP:
case PSD.COLOR_MODE_GRAYSCALE:
case PSD.COLOR_MODE_INDEXED:
case PSD.COLOR_MODE_RGB:
case PSD.COLOR_MODE_CMYK:
case PSD.COLOR_MODE_MULTICHANNEL:
case PSD.COLOR_MODE_DUOTONE:
case PSD.COLOR_MODE_LAB:
break;
default:
throw new IIOException(String.format("Unsupported mode depth for PSD: %d", mode));
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
builder.append("[Channels: ");
builder.append(channels);
builder.append(", width: ");
builder.append(width);
builder.append(", height: ");
builder.append(height);
builder.append(", depth: ");
builder.append(bits);
builder.append(", mode: ");
builder.append(mode);
switch (mode) {
case PSD.COLOR_MODE_MONOCHROME:
builder.append(" (Monochrome)");
break;
case PSD.COLOR_MODE_GRAYSCALE:
builder.append(" (Grayscale)");
break;
case PSD.COLOR_MODE_INDEXED:
builder.append(" (Indexed)");
break;
case PSD.COLOR_MODE_RGB:
builder.append(" (RGB)");
break;
case PSD.COLOR_MODE_CMYK:
builder.append(" (CMYK)");
break;
case PSD.COLOR_MODE_MULTICHANNEL:
builder.append(" (Multi channel)");
break;
case PSD.COLOR_MODE_DUOTONE:
builder.append(" (Duotone)");
break;
case PSD.COLOR_MODE_LAB:
builder.append(" (Lab color)");
break;
default:
builder.append(" (Unkown mode)");
}
builder.append("]");
return new StringBuilder(getClass().getSimpleName())
.append("[version: ")
.append(largeFormat ? "2" : "1")
.append(", channels: ")
.append(channels)
.append(", width: ")
.append(width)
.append(", height: ")
.append(height)
.append(", depth: ")
.append(bits)
.append(", mode: ")
.append(mode)
.append(" (")
.append(modeAsString())
.append(")]")
.toString();
}
return builder.toString();
int getMaxSize() {
return largeFormat ? PSB_MAX_SIZE : PSD_MAX_SIZE;
}
boolean hasValidDimensions() {
return width <= getMaxSize() && height <= getMaxSize();
}
private String modeAsString() {
switch (mode) {
case PSD.COLOR_MODE_BITMAP:
return "Monochrome";
case PSD.COLOR_MODE_GRAYSCALE:
return "Grayscale";
case PSD.COLOR_MODE_INDEXED:
return "Indexed";
case PSD.COLOR_MODE_RGB:
return "RGB";
case PSD.COLOR_MODE_CMYK:
return "CMYK";
case PSD.COLOR_MODE_MULTICHANNEL:
return "Multi channel";
case PSD.COLOR_MODE_DUOTONE:
return "Duotone";
case PSD.COLOR_MODE_LAB:
return "Lab color";
default:
return "Unkown mode";
}
}
}
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.metadata.Directory;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,15 +32,12 @@ import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.color.ColorSpaces;
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
import com.twelvemonkeys.xml.XMLSerializer;
import org.w3c.dom.Node;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
@@ -70,12 +67,12 @@ import java.util.List;
// See http://www.codeproject.com/KB/graphics/PSDParser.aspx
// See http://www.adobeforums.com/webx?14@@.3bc381dc/0
// Done: Allow reading the extra alpha channels (index after composite data)
public class PSDImageReader extends ImageReaderBase {
public final class PSDImageReader extends ImageReaderBase {
private PSDHeader header;
private ICC_ColorSpace colorSpace;
protected PSDMetadata metadata;
private PSDMetadata metadata;
protected PSDImageReader(final ImageReaderSpi originatingProvider) {
PSDImageReader(final ImageReaderSpi originatingProvider) {
super(originatingProvider);
}
@@ -108,10 +105,7 @@ public class PSDImageReader extends ImageReaderBase {
}
private int getLayerWidth(int layerIndex) throws IOException {
if (metadata == null || metadata.layerInfo == null) {
readImageResources(false);
readLayerAndMaskInfo(true);
}
readLayerAndMaskInfo(true);
PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
@@ -119,10 +113,7 @@ public class PSDImageReader extends ImageReaderBase {
}
private int getLayerHeight(int layerIndex) throws IOException {
if (metadata == null || metadata.layerInfo == null) {
readImageResources(false);
readLayerAndMaskInfo(true);
}
readLayerAndMaskInfo(true);
PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
@@ -136,14 +127,10 @@ public class PSDImageReader extends ImageReaderBase {
private ImageTypeSpecifier getRawImageTypeInternal(final int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
// Image index above 0, means a layer
if (imageIndex > 0) {
if (metadata == null || metadata.layerInfo == null) {
readImageResources(false);
readLayerAndMaskInfo(true);
}
readLayerAndMaskInfo(true);
return getRawImageTypeForLayer(imageIndex - 1);
}
@@ -154,46 +141,56 @@ public class PSDImageReader extends ImageReaderBase {
private ImageTypeSpecifier getRawImageTypeForCompositeLayer() throws IOException {
ColorSpace cs;
switch (header.mode) {
case PSD.COLOR_MODE_MONOCHROME:
case PSD.COLOR_MODE_BITMAP:
if (header.channels == 1 && header.bits == 1) {
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY);
}
throw new IIOException(
String.format("Unsupported channel count/bit depth for Monochrome PSD: %d channels/%d bits", header.channels, header.bits)
);
throw new IIOException(String.format("Unsupported channel count/bit depth for Monochrome PSD: %d channels/%d bits", header.channels, header.bits));
case PSD.COLOR_MODE_INDEXED:
// TODO: 16 bit indexed?! Does it exist?
if (header.channels == 1 && header.bits == 8) {
return IndexedImageTypeSpecifier.createFromIndexColorModel(metadata.colorData.getIndexColorModel());
}
throw new IIOException(
String.format("Unsupported channel count/bit depth for Indexed Color PSD: %d channels/%d bits", header.channels, header.bits)
);
throw new IIOException(String.format("Unsupported channel count/bit depth for Indexed Color PSD: %d channels/%d bits", header.channels, header.bits));
case PSD.COLOR_MODE_DUOTONE:
// NOTE: Duotone (whatever that is) should be treated as gray scale
// Fall-through
case PSD.COLOR_MODE_GRAYSCALE:
cs = getEmbeddedColorSpace();
if (cs == null) {
cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
}
if (header.channels == 1 && header.bits == 8) {
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
}
else if (header.channels == 2 && header.bits == 8) {
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_BYTE, true, false);
}
else if (header.channels == 1 && header.bits == 16) {
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY);
}
else if (header.channels == 2 && header.bits == 16) {
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_USHORT, true, false);
}
else if (header.channels == 1 && header.bits == 32) {
return ImageTypeSpecifier.createBanded(cs, new int[] {0}, new int[] {0}, DataBuffer.TYPE_INT, false, false);
}
else if (header.channels == 2 && header.bits == 32) {
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_INT, true, false);
}
throw new IIOException(
String.format("Unsupported channel count/bit depth for Gray Scale PSD: %d channels/%d bits", header.channels, header.bits)
);
throw new IIOException(String.format("Unsupported channel count/bit depth for Gray Scale PSD: %d channels/%d bits", header.channels, header.bits));
case PSD.COLOR_MODE_RGB:
cs = getEmbeddedColorSpace();
if (cs == null) {
// TODO: Should probably be Adobe RGB (1998), not sRGB. Or..? Can't find any spec saying either...
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); // ColorSpaces.getColorSpace(ColorSpaces.CS_ADOBE_RGB_1998); ?
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
}
if (header.channels == 3 && header.bits == 8) {
@@ -208,10 +205,14 @@ public class PSDImageReader extends ImageReaderBase {
else if (header.channels >= 4 && header.bits == 16) {
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_USHORT, true, false);
}
else if (header.channels == 3 && header.bits == 32) {
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2}, new int[] {0, 0, 0}, DataBuffer.TYPE_INT, false, false);
}
else if (header.channels >= 4 && header.bits == 32) {
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_INT, true, false);
}
throw new IIOException(
String.format("Unsupported channel count/bit depth for RGB PSD: %d channels/%d bits", header.channels, header.bits)
);
throw new IIOException(String.format("Unsupported channel count/bit depth for RGB PSD: %d channels/%d bits", header.channels, header.bits));
case PSD.COLOR_MODE_CMYK:
cs = getEmbeddedColorSpace();
@@ -231,10 +232,14 @@ public class PSDImageReader extends ImageReaderBase {
else if (header.channels == 5 && header.bits == 16) {
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, DataBuffer.TYPE_USHORT, true, false);
}
else if (header.channels == 4 && header.bits == 32) {
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_INT, false, false);
}
else if (header.channels == 5 && header.bits == 32) {
return ImageTypeSpecifier.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, DataBuffer.TYPE_INT, true, false);
}
throw new IIOException(
String.format("Unsupported channel count/bit depth for CMYK PSD: %d channels/%d bits", header.channels, header.bits)
);
throw new IIOException(String.format("Unsupported channel count/bit depth for CMYK PSD: %d channels/%d bits", header.channels, header.bits));
case PSD.COLOR_MODE_MULTICHANNEL:
// TODO: Implement
@@ -242,9 +247,7 @@ public class PSDImageReader extends ImageReaderBase {
// TODO: Implement
// TODO: If there's a color profile embedded, it should be easy, otherwise we're out of luck...
default:
throw new IIOException(
String.format("Unsupported PSD MODE: %s (%d channels/%d bits)", header.mode, header.channels, header.bits)
);
throw new IIOException(String.format("Unsupported PSD MODE: %s (%d channels/%d bits)", header.mode, header.channels, header.bits));
}
}
@@ -330,8 +333,6 @@ public class PSDImageReader extends ImageReaderBase {
private ColorSpace getEmbeddedColorSpace() throws IOException {
readImageResources(true);
// TODO: Skip this, requires storing some stream offsets
readLayerAndMaskInfo(false);
if (colorSpace == null) {
ICC_Profile profile = null;
@@ -351,27 +352,12 @@ public class PSDImageReader extends ImageReaderBase {
public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
checkBounds(imageIndex);
readHeader();
readImageResources(false);
// readLayerAndMaskInfo(false);
readLayerAndMaskInfo(imageIndex > 0);
// TODO: What about the extra alpha channels possibly present? Read as gray scale as extra images?
// Layer hacks... For now, any index above 0 is considered to be a layer...
// TODO: Support layer in index 0, if "has real merged data" flag is false?
// TODO: Param support in layer code (more duping/cleanup..)
if (imageIndex > 0) {
// ImageTypeSpecifier compositeType = getRawImageTypeForCompositeLayer();
// ImageTypeSpecifier imageType = getImageTypes(0).next();
// int layerIndex = imageIndex - 1;
// PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
//
// imageInput.seek(findLayerStartPos(layerIndex));
// return readLayerData(layerIndex, layerInfo, compositeType, imageType, param);
return readLayerData(imageIndex - 1, param);
}
@@ -421,13 +407,11 @@ public class PSDImageReader extends ImageReaderBase {
ySub = param.getSourceYSubsampling();
}
processImageStarted(imageIndex);
int[] byteCounts = null;
imageInput.seek(metadata.imageDataStart);
int compression = imageInput.readShort();
// TODO: Need to make sure compression is set in metadata, even without reading the image data!
metadata.compression = compression;
int[] byteCounts = null;
switch (compression) {
case PSD.COMPRESSION_NONE:
break;
@@ -435,14 +419,12 @@ public class PSDImageReader extends ImageReaderBase {
// NOTE: Byte counts will allow us to easily skip rows before AOI
byteCounts = new int[header.channels * header.height];
for (int i = 0; i < byteCounts.length; i++) {
byteCounts[i] = imageInput.readUnsignedShort();
byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
}
break;
case PSD.COMPRESSION_ZIP:
// TODO: Could probably use the ZIPDecoder (DeflateDecoder) here..
case PSD.COMPRESSION_ZIP_PREDICTION:
// TODO: Look at TIFF prediction reading
// Could be same as PNG prediction? Read up...
// TODO: Could probably use the ZIPDecoder (DeflateDecoder) here.. Look at TIFF prediction reading
throw new IIOException("PSD with ZIP compression not supported");
default:
throw new IIOException(
@@ -453,6 +435,8 @@ public class PSDImageReader extends ImageReaderBase {
);
}
processImageStarted(imageIndex);
// What we read here is the "composite layer" of the PSD file
readImageData(image, rawType.getColorModel(), source, dest, xSub, ySub, byteCounts, compression);
@@ -512,8 +496,12 @@ public class PSDImageReader extends ImageReaderBase {
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
read16bitChannel(c, header.channels, raster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row16, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE);
break;
case 32:
int[] row32 = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
read32bitChannel(c, header.channels, raster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row32, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE);
break;
default:
throw new IIOException(String.format("Unknown PSD bit depth: %s", header.bits));
throw new IIOException(String.format("Unsupported PSD bit depth: %s", header.bits));
}
if (abortRequested()) {
@@ -531,6 +519,67 @@ public class PSDImageReader extends ImageReaderBase {
processImageProgress(100f * channel / channelCount + 100f * y / (height * channelCount));
}
private void read32bitChannel(final int pChannel, final int pChannelCount,
final DataBuffer pData, final int pBands, final int pBandOffset,
final ColorModel pSourceColorModel,
final int[] pRow,
final Rectangle pSource, final Rectangle pDest,
final int pXSub, final int pYSub,
final int pChannelWidth, final int pChannelHeight,
final int[] pRowByteCounts, final int pRowOffset,
final boolean pRLECompressed) throws IOException {
final boolean isCMYK = pSourceColorModel.getColorSpace().getType() == ColorSpace.TYPE_CMYK;
final int colorComponents = pSourceColorModel.getColorSpace().getNumComponents();
final boolean banded = pData.getNumBanks() > 1;
for (int y = 0; y < pChannelHeight; y++) {
// NOTE: Length is in *16 bit values* (shorts)
int length = 2 * (pRLECompressed ? pRowByteCounts[pRowOffset + y] : pChannelWidth);
// TODO: Sometimes need to read the line y == source.y + source.height...
// Read entire line, if within source region and sampling
if (y >= pSource.y && y < pSource.y + pSource.height && y % pYSub == 0) {
if (pRLECompressed) {
DataInputStream input = PSDUtil.createPackBitsStream(imageInput, length);
try {
for (int x = 0; x < pChannelWidth; x++) {
pRow[x] = input.readInt();
}
}
finally {
input.close();
}
}
else {
imageInput.readFully(pRow, 0, pChannelWidth);
}
// TODO: Destination offset...??
// Copy line sub sampled into real data
int offset = (y - pSource.y) / pYSub * pDest.width * pBands + pBandOffset;
for (int x = 0; x < pDest.width; x++) {
int value = pRow[pSource.x + x * pXSub];
// CMYK values are stored inverted, but alpha is not
if (isCMYK && pChannel < colorComponents) {
value = 0xffffffff - value;
}
pData.setElem(banded ? pChannel : 0, offset + x * pBands, value);
}
}
else {
imageInput.skipBytes(length);
}
if (abortRequested()) {
break;
}
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
}
}
private void read16bitChannel(final int pChannel, final int pChannelCount,
final DataBuffer pData, final int pBands, final int pBandOffset,
final ColorModel pSourceColorModel,
@@ -731,7 +780,7 @@ public class PSDImageReader extends ImageReaderBase {
private void decomposeAlpha(final ColorModel pModel, final DataBuffer pBuffer,
final int pWidth, final int pHeight, final int pChannels) {
// TODO: Is the document background always white!?
// NOTE: It seems that the document background always white..?!
// TODO: What about CMYK + alpha?
if (pModel.hasAlpha() && pModel.getColorSpace().getType() == ColorSpace.TYPE_RGB) {
@@ -791,9 +840,16 @@ public class PSDImageReader extends ImageReaderBase {
private void readHeader() throws IOException {
assertInput();
if (header == null) {
header = new PSDHeader(imageInput);
if (!header.hasValidDimensions()) {
processWarningOccurred(String.format("Dimensions exceed maximum allowed for %s: %dx%d (max %dx%d)",
header.largeFormat ? "PSB" : "PSD",
header.width, header.height, header.getMaxSize(), header.getMaxSize()));
}
metadata = new PSDMetadata();
metadata.header = header;
@@ -818,6 +874,8 @@ public class PSDImageReader extends ImageReaderBase {
imageInput.skipBytes(length);
}
metadata.imageResourcesStart = imageInput.getStreamPosition();
// Don't need the header again
imageInput.flushBefore(imageInput.getStreamPosition());
}
@@ -826,85 +884,104 @@ public class PSDImageReader extends ImageReaderBase {
// TODO: Flags or list of interesting resources to parse
// TODO: Obey ignoreMetadata
private void readImageResources(final boolean pParseData) throws IOException {
// TODO: Avoid unnecessary stream repositioning
long pos = imageInput.getFlushedPosition();
imageInput.seek(pos);
readHeader();
long length = imageInput.readUnsignedInt();
if (pParseData || metadata.layerAndMaskInfoStart == 0) {
imageInput.seek(metadata.imageResourcesStart);
if (pParseData && length > 0) {
if (metadata.imageResources == null) {
metadata.imageResources = new ArrayList<PSDImageResource>();
long expectedEnd = imageInput.getStreamPosition() + length;
long imageResourcesLength = imageInput.readUnsignedInt();
while (imageInput.getStreamPosition() < expectedEnd) {
// TODO: Have PSDImageResources defer actual parsing? (Just store stream offsets)
PSDImageResource resource = PSDImageResource.read(imageInput);
metadata.imageResources.add(resource);
if (pParseData && imageResourcesLength > 0) {
if (metadata.imageResources == null) {
metadata.imageResources = new ArrayList<PSDImageResource>();
long expectedEnd = imageInput.getStreamPosition() + imageResourcesLength;
while (imageInput.getStreamPosition() < expectedEnd) {
PSDImageResource resource = PSDImageResource.read(imageInput);
metadata.imageResources.add(resource);
}
if (imageInput.getStreamPosition() != expectedEnd) {
throw new IIOException("Corrupt PSD document"); // ..or maybe just a bug in the reader.. ;-)
}
}
if (imageInput.getStreamPosition() != expectedEnd) {
throw new IIOException("Corrupt PSD document"); // ..or maybe just a bug in the reader.. ;-)
}
// TODO: We should now be able to flush input
// imageInput.flushBefore(metadata.imageResourcesStart + imageResourcesLength + 4);
}
}
imageInput.seek(pos + length + 4);
metadata.layerAndMaskInfoStart = metadata.imageResourcesStart + imageResourcesLength + 4; // + 4 for the length field itself
}
}
// TODO: Flags or list of interesting resources to parse
// TODO: Obey ignoreMetadata
private void readLayerAndMaskInfo(final boolean pParseData) throws IOException {
// TODO: Make sure we are positioned correctly
// TODO: Avoid unnecessary stream repositioning
long length = imageInput.readUnsignedInt();
if (pParseData && length > 0) {
long pos = imageInput.getStreamPosition();
readImageResources(false);
long read;
if (metadata.layerInfo == null) {
long layerInfoLength = imageInput.readUnsignedInt();
if (pParseData || metadata.imageDataStart == 0) {
imageInput.seek(metadata.layerAndMaskInfoStart);
/*
"Layer count. If it is a negative number, its absolute value is the number of
layers and the first alpha channel contains the transparency data for the
merged result."
*/
// TODO: Figure out what the last part of that sentence means in practice...
int layers = imageInput.readShort();
long layerAndMaskInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt();
PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layers)];
for (int i = 0; i < layerInfos.length; i++) {
layerInfos[i] = new PSDLayerInfo(imageInput);
// 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
// is alo not as per spec, as layer count should be included if there's a layer info
// block, so minimum size should be either 0 or 14 (or 16 if multiple of 4 for PSB))...
if (pParseData && layerAndMaskInfoLength > 0) {
long pos = imageInput.getStreamPosition();
if (metadata.layerInfo == null) {
long layerInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt();
if (layerInfoLength > 0) {
/*
"Layer count. If it is a negative number, its absolute value is the number of
layers and the first alpha channel contains the transparency data for the
merged result."
*/
int layerCount = imageInput.readShort();
PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layerCount)];
for (int i = 0; i < layerInfos.length; i++) {
layerInfos[i] = new PSDLayerInfo(header.largeFormat, imageInput);
}
metadata.layerInfo = Arrays.asList(layerInfos);
metadata.layersStart = imageInput.getStreamPosition();
long read = imageInput.getStreamPosition() - pos;
long diff = layerInfoLength - (read - (header.largeFormat ? 8 : 4)); // - 4 for the layerInfoLength field itself
imageInput.skipBytes(diff);
} else {
metadata.layerInfo = Collections.emptyList();
}
// Global LayerMaskInfo (18 bytes or more..?)
// 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad)
long layerMaskInfoLength = imageInput.readUnsignedInt(); // NOTE: Not long for PSB!
if (layerMaskInfoLength > 0) {
metadata.globalLayerMask = new PSDGlobalLayerMask(imageInput);
}
// TODO: Parse "Additional layer information"
// TODO: We should now be able to flush input
// imageInput.seek(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
// imageInput.flushBefore(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
}
metadata.layerInfo = Arrays.asList(layerInfos);
metadata.layersStart = imageInput.getStreamPosition();
read = imageInput.getStreamPosition() - pos;
long diff = layerInfoLength - (read - 4); // - 4 for the layerInfoLength field itself
// System.out.println("diff: " + diff);
imageInput.skipBytes(diff);
// TODO: Global LayerMaskInfo (18 bytes or more..?)
// 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad)
long layerMaskInfoLength = imageInput.readUnsignedInt();
// System.out.println("GlobalLayerMaskInfo length: " + layerMaskInfoLength);
if (layerMaskInfoLength > 0) {
metadata.globalLayerMask = new PSDGlobalLayerMask(imageInput);
// System.err.println("globalLayerMask: " + metadata.globalLayerMask);
}
// read = imageInput.getStreamPosition() - pos;
//
// long toSkip = layerAndMaskInfoLength - read;
// System.out.println("toSkip: " + toSkip);
// imageInput.skipBytes(toSkip);
}
read = imageInput.getStreamPosition() - pos;
long toSkip = length - read;
// System.out.println("toSkip: " + toSkip);
imageInput.skipBytes(toSkip);
}
else {
// Skip entire layer and mask section
imageInput.skipBytes(length);
metadata.imageDataStart = metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4);
}
}
@@ -949,19 +1026,22 @@ public class PSDImageReader extends ImageReaderBase {
final boolean banded = raster.getDataBuffer().getNumBanks() > 1;
final int interleavedBands = banded ? 1 : raster.getNumBands();
// TODO: progress for layers!
// TODO: Consider creating a method in PSDLayerInfo that can tell how many channels we really want to decode
for (PSDChannelInfo channelInfo : layerInfo.channelInfo) {
int compression = imageInput.readUnsignedShort();
// Skip layer if we can't read it
// channelId == -2 means "user supplied layer mask", whatever that is...
if (width <= 0 || height <= 0 || channelInfo.channelId == -2 ||
(compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) {
// 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)
if (width <= 0 || height <= 0 || channelInfo.channelId < -1 ||
(compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) { // TODO: ZIP Compressions!
imageInput.skipBytes(channelInfo.length - 2);
}
else {
// 0 = red, 1 = green, etc
// -1 = transparency mask; -2 = user supplied layer mask
int c = channelInfo.channelId == -1 ? layerInfo.channelInfo.length - 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
// byte counts are written for all channels before the image data.
@@ -975,10 +1055,10 @@ public class PSDImageReader extends ImageReaderBase {
case PSD.COMPRESSION_RLE:
// If RLE, the the image data starts with the byte counts
// for all the scan lines in the channel (LayerBottom-LayerTop), with
// each count stored as a two*byte value.
// each count stored as a two*byte (four for PSB) value.
byteCounts = new int[layerInfo.bottom - layerInfo.top];
for (int i = 0; i < byteCounts.length; i++) {
byteCounts[i] = imageInput.readUnsignedShort();
byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
}
break;
@@ -998,11 +1078,18 @@ public class PSDImageReader extends ImageReaderBase {
break;
case 8:
byte[] row8 = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
read8bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row8, area, area, xsub, ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
read8bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row8, area, area, xsub,
ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
break;
case 16:
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
read16bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row16, area, area, xsub, ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
read16bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row16, area, area, xsub,
ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
break;
case 32:
int[] row32 = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
read32bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row32, area, area, xsub,
ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE);
break;
default:
throw new IIOException(String.format("Unknown PSD bit depth: %s", header.bits));
@@ -1024,16 +1111,16 @@ public class PSDImageReader extends ImageReaderBase {
// If layer has more channels than composite data, it's normally extra alpha...
if (layerInfo.channelInfo.length > compositeType.getNumBands()) {
// ...but, it could also be just the user mask...
boolean userMask = false;
// ...but, it could also be just one of the user masks...
int newBandNum = 0;
for (PSDChannelInfo channelInfo : layerInfo.channelInfo) {
if (channelInfo.channelId == -2) {
userMask = true;
break;
// -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) {
newBandNum++;
}
}
int newBandNum = layerInfo.channelInfo.length - (userMask ? 1 : 0);
// If there really is more channels, then create new imageTypeSpec
if (newBandNum > compositeType.getNumBands()) {
@@ -1056,30 +1143,28 @@ public class PSDImageReader extends ImageReaderBase {
/// Layer support
@Override protected void checkBounds(final int index) throws IOException {
// Avoid parsing layer stuff, if we just want to read the composite data
if (index == 0) {
assertInput();
readLayerAndMaskInfo(false);
}
else {
super.checkBounds(index);
}
}
@Override
public int getNumImages(boolean allowSearch) throws IOException {
// NOTE: Spec says this method should throw IllegalStateException if allowSearch && isSeekForwardOnly()
// But that makes no sense for a format (like PSD) that does not need to search, right?
readHeader();
readImageResources(false);
readLayerAndMaskInfo(true); // TODO: Consider quicker reading of just the number of layers.
readLayerAndMaskInfo(true);
return metadata.layerInfo != null ? metadata.layerInfo.size() + 1 : 1; // TODO: Only plus one, if "has real merged data"?
}
// TODO: For now, leave as Metadata
/*
// ?
Point getOffset(int pImageIndex) throws IOException;
// Return 0, 0 for index 0, otherwise use layer offset
*/
/// Metadata support
// TODO
@Override
public IIOMetadata getStreamMetadata() throws IOException {
@@ -1090,20 +1175,15 @@ public class PSDImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
// TODO: Implement
checkBounds(imageIndex);
readHeader();
readImageResources(true);
readLayerAndMaskInfo(true);
// TODO: Need to make sure compression is set in metadata, even without reading the image data!
// NOTE: Need to make sure compression is set in metadata, even without reading the image data!
imageInput.seek(metadata.imageDataStart);
metadata.compression = imageInput.readShort();
// metadata.header = header;
// metadata.colorData = colorData;
// metadata.imageResources = imageResources;
return metadata; // TODO: clone if we change to mutable metadata
}
@@ -1129,8 +1209,6 @@ public class PSDImageReader extends ImageReaderBase {
if (metadata.imageResources == null) {
// TODO: Need flag here, to specify what resources to read...
readImageResources(true);
// TODO: Skip this, requires storing some stream offsets
readLayerAndMaskInfo(false);
}
for (PSDImageResource resource : metadata.imageResources) {
@@ -1236,84 +1314,89 @@ public class PSDImageReader extends ImageReaderBase {
PSDImageReader imageReader = new PSDImageReader(null);
File file = new File(pArgs[idx]);
ImageInputStream stream = ImageIO.createImageInputStream(file);
imageReader.setInput(stream);
for (; idx < pArgs.length; idx++) {
File file = new File(pArgs[idx]);
System.out.println();
System.out.println("file: " + file.getAbsolutePath());
imageReader.readHeader();
// System.out.println("imageReader.header: " + imageReader.header);
ImageInputStream stream = ImageIO.createImageInputStream(file);
imageReader.setInput(stream);
imageReader.readHeader();
System.out.println("imageReader.header: " + imageReader.header);
imageReader.readImageResources(true);
System.out.println("imageReader.imageResources: " + imageReader.metadata.imageResources);
System.out.println();
imageReader.readImageResources(true);
System.out.println("imageReader.imageResources: " + imageReader.metadata.imageResources);
System.out.println();
imageReader.readLayerAndMaskInfo(true);
System.out.println("imageReader.layerInfo: " + imageReader.metadata.layerInfo);
// System.out.println("imageReader.globalLayerMask: " + imageReader.globalLayerMask);
System.out.println();
imageReader.readLayerAndMaskInfo(true);
System.out.println("imageReader.layerInfo: " + imageReader.metadata.layerInfo);
/*
// System.out.println("imageReader.globalLayerMask: " + imageReader.globalLayerMask);
System.out.println();
IIOMetadata metadata = imageReader.getImageMetadata(0);
Node node;
XMLSerializer serializer;
IIOMetadata metadata = imageReader.getImageMetadata(0);
Node node;
XMLSerializer serializer;
node = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
serializer.serialize(node, true);
System.out.println();
node = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
serializer.serialize(node, true);
System.out.println();
node = metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME);
// serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
serializer.serialize(node, true);
if (readThumbnails && imageReader.hasThumbnails(0)) {
int thumbnails = imageReader.getNumThumbnails(0);
for (int i = 0; i < thumbnails; i++) {
showIt(imageReader.readThumbnail(0, i), String.format("Thumbnail %d", i));
node = metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME);
// serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
serializer.serialize(node, true);
*/
if (readThumbnails && imageReader.hasThumbnails(0)) {
int thumbnails = imageReader.getNumThumbnails(0);
for (int i = 0; i < thumbnails; i++) {
showIt(imageReader.readThumbnail(0, i), String.format("Thumbnail %d", i));
}
}
}
long start = System.currentTimeMillis();
long start = System.currentTimeMillis();
ImageReadParam param = imageReader.getDefaultReadParam();
ImageReadParam param = imageReader.getDefaultReadParam();
if (sourceRegion != null) {
param.setSourceRegion(sourceRegion);
}
if (subsampleFactor > 1) {
param.setSourceSubsampling(subsampleFactor, subsampleFactor, 0, 0);
}
// param.setDestinationType(imageReader.getRawImageType(0));
BufferedImage image = imageReader.read(0, param);
System.out.println("read time: " + (System.currentTimeMillis() - start));
System.out.println("image: " + image);
if (image.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
try {
ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null);
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
image = op.filter(image, gc.createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency()));
if (sourceRegion != null) {
param.setSourceRegion(sourceRegion);
}
catch (Exception e) {
e.printStackTrace();
image = ImageUtil.accelerate(image);
if (subsampleFactor > 1) {
param.setSourceSubsampling(subsampleFactor, subsampleFactor, 0, 0);
}
System.out.println("conversion time: " + (System.currentTimeMillis() - start));
// param.setDestinationType(imageReader.getRawImageType(0));
BufferedImage image = imageReader.read(0, param);
System.out.println("read time: " + (System.currentTimeMillis() - start));
System.out.println("image: " + image);
}
showIt(image, file.getName());
if (image.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
try {
ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null);
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
image = op.filter(image, gc.createCompatibleImage(image.getWidth(), image.getHeight(), image.getTransparency()));
}
catch (Exception e) {
e.printStackTrace();
image = ImageUtil.accelerate(image);
}
System.out.println("conversion time: " + (System.currentTimeMillis() - start));
System.out.println("image: " + image);
}
if (readLayers) {
int images = imageReader.getNumImages(true);
for (int i = 1; i < images; i++) {
start = System.currentTimeMillis();
BufferedImage layer = imageReader.read(i);
System.out.println("layer read time: " + (System.currentTimeMillis() - start));
System.err.println("layer: " + layer);
showIt(layer, "layer " + i);
showIt(image, file.getName());
if (readLayers) {
int images = imageReader.getNumImages(true);
for (int i = 1; i < images; i++) {
start = System.currentTimeMillis();
BufferedImage layer = imageReader.read(i);
System.out.println("layer read time: " + (System.currentTimeMillis() - start));
System.err.println("layer: " + layer);
showIt(layer, "layer " + i);
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -44,7 +44,7 @@ import java.util.Locale;
* @author last modified by $Author: haraldk$
* @version $Id: PSDImageReaderSpi.java,v 1.0 Apr 29, 2008 4:49:03 PM haraldk Exp$
*/
public class PSDImageReaderSpi extends ImageReaderSpi {
final public class PSDImageReaderSpi extends ImageReaderSpi {
/**
* Creates a {@code PSDImageReaderSpi}.
@@ -57,18 +57,18 @@ public class PSDImageReaderSpi extends ImageReaderSpi {
super(
providerInfo.getVendorName(),
providerInfo.getVersion(),
new String[]{"psd", "PSD"},
new String[]{"psd"},
new String[]{
new String[] {"psd", "PSD"},
new String[] {"psd"},
new String[] {
"image/vnd.adobe.photoshop", // Official, IANA registered
"application/vnd.adobe.photoshop", // Used in XMP
"image/x-psd",
"application/x-photoshop",
"application/vnd.adobe.photoshop", // Used in XMP
"image/x-psd",
"application/x-photoshop",
"image/x-photoshop"
},
"com.twelvemkonkeys.imageio.plugins.psd.PSDImageReader",
new Class[] {ImageInputStream.class},
// new String[]{"com.twelvemkonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
// new String[] {"com.twelvemkonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
null,
true, // supports standard stream metadata
null, null, // native stream format name and class
@@ -87,9 +87,23 @@ public class PSDImageReaderSpi extends ImageReaderSpi {
ImageInputStream stream = (ImageInputStream) pSource;
stream.mark();
try {
return stream.readInt() == PSD.SIGNATURE_8BPS;
// TODO: Test more of the header, see PSDImageReader#readHeader
if (stream.readInt() == PSD.SIGNATURE_8BPS) {
int version = stream.readUnsignedShort();
switch (version) {
case PSD.VERSION_PSD:
case PSD.VERSION_PSB:
break;
default:
return false;
}
return true;
}
return false;
}
finally {
stream.reset();
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -110,7 +110,7 @@ public class PSDImageResource {
builder.append("[ID: 0x");
builder.append(Integer.toHexString(id));
if (name != null && name.trim().length() != 0) {
if (name != null && !name.trim().isEmpty()) {
builder.append(", name: \"");
builder.append(name);
builder.append("\"");
@@ -139,6 +139,13 @@ public class PSDImageResource {
case PSD.RES_PRINT_FLAGS_INFORMATION:
return null;
default:
if (pId >= PSD.RES_PATH_INFO_MIN && pId <= PSD.RES_PATH_INFO_MAX) {
return "PathInformationResource";
}
if (pId >= PSD.RES_PLUGIN_MIN && pId <= PSD.RES_PLUGIN_MAX) {
return "PluginResource";
}
try {
for (Field field : PSD.class.getDeclaredFields()) {
if (field.getName().startsWith("RES_") && field.getInt(null) == pId) {
@@ -149,7 +156,7 @@ public class PSDImageResource {
}
catch (IllegalAccessException ignore) {
}
return "UnknownResource";
}
}
@@ -160,9 +167,9 @@ public class PSDImageResource {
throw new IIOException(String.format("Wrong image resource type, expected '8BIM': '%s'", PSDUtil.intToStr(type)));
}
// TODO: Process more of the resource stuff, most important are IPTC, EXIF and XMP data,
// version info, and thumbnail for thumbnail-support.
// TODO: Have PSDImageResources defer actual parsing? (Just store stream offsets)
short id = pInput.readShort();
switch (id) {
case PSD.RES_RESOLUTION_INFO:
return new PSDResolutionInfo(id, pInput);
@@ -196,9 +203,8 @@ public class PSDImageResource {
case PSD.RES_PRINT_FLAGS_INFORMATION:
return new PSDPrintFlagsInformation(id, pInput);
default:
if (id >= 0x07d0 && id <= 0x0bb6) {
// TODO: Parse saved path information
return new PSDImageResource(id, pInput);
if (id >= PSD.RES_PATH_INFO_MIN && id <= PSD.RES_PATH_INFO_MAX) {
return new PSDPathResource(id, pInput);
}
else {
return new PSDImageResource(id, pInput);
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: PSDLayerBlendMode.java,v 1.0 May 8, 2008 4:34:35 PM haraldk Exp$
*/
class PSDLayerBlendMode {
final class PSDLayerBlendMode {
final int blendMode;
final int opacity; // 0-255
final int clipping; // 0: base, 1: non-base
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -40,7 +40,7 @@ import java.util.Arrays;
* @author last modified by $Author: haraldk$
* @version $Id: PSDLayerInfo.java,v 1.0 Apr 29, 2008 6:01:12 PM haraldk Exp$
*/
class PSDLayerInfo {
final class PSDLayerInfo {
final int top;
final int left;
final int bottom;
@@ -52,7 +52,7 @@ class PSDLayerInfo {
final PSDChannelSourceDestinationRange[] ranges;
final String layerName;
PSDLayerInfo(ImageInputStream pInput) throws IOException {
PSDLayerInfo(final boolean largeFormat, final ImageInputStream pInput) throws IOException {
top = pInput.readInt();
left = pInput.readInt();
bottom = pInput.readInt();
@@ -63,7 +63,7 @@ class PSDLayerInfo {
channelInfo = new PSDChannelInfo[channels];
for (int i = 0; i < channels; i++) {
short channelId = pInput.readShort();
long length = pInput.readUnsignedInt();
long length = largeFormat ? pInput.readLong() : pInput.readUnsignedInt();
channelInfo[i] = new PSDChannelInfo(channelId, length);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: PSDLayerMaskData.java,v 1.0 May 6, 2008 5:15:05 PM haraldk Exp$
*/
class PSDLayerMaskData {
final class PSDLayerMaskData {
private int top;
private int left;
private int bottom;
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.metadata.Directory;
@@ -35,7 +63,11 @@ public final class PSDMetadata extends AbstractMetadata {
List<PSDImageResource> imageResources;
PSDGlobalLayerMask globalLayerMask;
List<PSDLayerInfo> layerInfo;
long imageResourcesStart;
long layerAndMaskInfoStart;
long layersStart;
long imageDataStart;
static final String[] COLOR_MODES = {
"MONOCHROME", "GRAYSCALE", "INDEXED", "RGB", "CMYK", null, null, "MULTICHANNEL", "DUOTONE", "LAB"
@@ -85,17 +117,17 @@ public final class PSDMetadata extends AbstractMetadata {
}
private Node createHeaderNode() {
IIOMetadataNode header = new IIOMetadataNode("Header");
IIOMetadataNode headerNode = new IIOMetadataNode("Header");
header.setAttribute("type", "PSD");
header.setAttribute("version", "1");
header.setAttribute("channels", Integer.toString(this.header.channels));
header.setAttribute("height", Integer.toString(this.header.height));
header.setAttribute("width", Integer.toString(this.header.width));
header.setAttribute("bits", Integer.toString(this.header.bits));
header.setAttribute("mode", COLOR_MODES[this.header.mode]);
headerNode.setAttribute("type", "PSD");
headerNode.setAttribute("version", header.largeFormat ? "2" : "1");
headerNode.setAttribute("channels", Integer.toString(header.channels));
headerNode.setAttribute("height", Integer.toString(header.height));
headerNode.setAttribute("width", Integer.toString(header.width));
headerNode.setAttribute("bits", Integer.toString(header.bits));
headerNode.setAttribute("mode", COLOR_MODES[header.mode]);
return header;
return headerNode;
}
private Node createImageResourcesNode() {
@@ -112,18 +144,6 @@ public final class PSDMetadata extends AbstractMetadata {
// TODO: Format spec
node = new IIOMetadataNode("ICCProfile");
node.setAttribute("colorSpaceType", JAVA_CS[profile.getProfile().getColorSpaceType()]);
//
// FastByteArrayOutputStream data = new FastByteArrayOutputStream(0);
// EncoderStream base64 = new EncoderStream(data, new Base64Encoder(), true);
//
// try {
// base64.write(profile.getProfile().getData());
// }
// catch (IOException ignore) {
// }
//
// byte[] bytes = data.toByteArray();
// node.setAttribute("data", StringUtil.decode(bytes, 0, bytes.length, "ASCII"));
node.setUserObject(profile.getProfile());
}
else if (imageResource instanceof PSDAlphaChannelInfo) {
@@ -347,13 +367,12 @@ public final class PSDMetadata extends AbstractMetadata {
@Override
protected IIOMetadataNode getStandardChromaNode() {
IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
IIOMetadataNode node; // scratch node
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
node = new IIOMetadataNode("ColorSpaceType");
IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType");
String cs;
switch (header.mode) {
case PSD.COLOR_MODE_MONOCHROME:
case PSD.COLOR_MODE_BITMAP:
case PSD.COLOR_MODE_GRAYSCALE:
case PSD.COLOR_MODE_DUOTONE: // Rationale: Spec says treat as gray...
cs = "GRAY";
@@ -374,25 +393,24 @@ public final class PSDMetadata extends AbstractMetadata {
default:
throw new AssertionError("Unreachable");
}
node.setAttribute("name", cs);
chroma_node.appendChild(node);
colorSpaceType.setAttribute("name", cs);
chroma.appendChild(colorSpaceType);
// TODO: Channels might be 5 for RGB + A + Mask... Probably not correct
node = new IIOMetadataNode("NumChannels");
node.setAttribute("value", Integer.toString(header.channels));
chroma_node.appendChild(node);
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
numChannels.setAttribute("value", Integer.toString(header.channels));
chroma.appendChild(numChannels);
// TODO: Check if this is correct with bitmap (monchrome)
node = new IIOMetadataNode("BlackIsZero");
node.setAttribute("value", "true");
chroma_node.appendChild(node);
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
blackIsZero.setAttribute("value", "true");
chroma.appendChild(blackIsZero);
if (header.mode == PSD.COLOR_MODE_INDEXED) {
node = createPaletteNode();
chroma_node.appendChild(node);
IIOMetadataNode paletteNode = createPaletteNode();
chroma.appendChild(paletteNode);
}
// TODO: Hardcode background color to white?
// TODO: Use
// if (bKGD_present) {
// if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
// node = new IIOMetadataNode("BackgroundIndex");
@@ -415,24 +433,25 @@ public final class PSDMetadata extends AbstractMetadata {
// chroma_node.appendChild(node);
// }
return chroma_node;
return chroma;
}
private IIOMetadataNode createPaletteNode() {
IIOMetadataNode node = new IIOMetadataNode("Palette");
IIOMetadataNode palette = new IIOMetadataNode("Palette");
IndexColorModel cm = colorData.getIndexColorModel();
for (int i = 0; i < cm.getMapSize(); i++) {
IIOMetadataNode entry = new IIOMetadataNode("PaletteEntry");
entry.setAttribute("index", Integer.toString(i));
entry.setAttribute("red", Integer.toString(cm.getRed(i)));
entry.setAttribute("green", Integer.toString(cm.getGreen(i)));
entry.setAttribute("blue", Integer.toString(cm.getBlue(i)));
node.appendChild(entry);
palette.appendChild(entry);
}
return node;
return palette;
}
private String getMultiChannelCS(short channels) {
@@ -446,33 +465,33 @@ public final class PSDMetadata extends AbstractMetadata {
@Override
protected IIOMetadataNode getStandardCompressionNode() {
IIOMetadataNode compressionNode = new IIOMetadataNode("Compression");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("CompressionTypeName");
String compression;
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
String compressionName;
switch (this.compression) {
switch (compression) {
case PSD.COMPRESSION_NONE:
compression = "none";
compressionName = "none";
break;
case PSD.COMPRESSION_RLE:
compression = "PackBits";
compressionName = "PackBits";
break;
case PSD.COMPRESSION_ZIP:
case PSD.COMPRESSION_ZIP_PREDICTION:
compression = "Deflate"; // TODO: ZLib? (TIFF native metadata format specifies both.. :-P)
compressionName = "Zip";
break;
default:
throw new AssertionError("Unreachable");
}
node.setAttribute("value", compression);
compressionNode.appendChild(node);
compressionTypeName.setAttribute("value", compressionName);
compressionNode.appendChild(compressionTypeName);
// TODO: Does it make sense to specify lossless for compression "none"?
node = new IIOMetadataNode("Lossless");
node.setAttribute("value", "true");
compressionNode.appendChild(node);
if (compression != PSD.COMPRESSION_NONE) {
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
lossless.setAttribute("value", "true");
compressionNode.appendChild(lossless);
}
return compressionNode;
}
@@ -480,15 +499,14 @@ public final class PSDMetadata extends AbstractMetadata {
@Override
protected IIOMetadataNode getStandardDataNode() {
IIOMetadataNode dataNode = new IIOMetadataNode("Data");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("PlanarConfiguration");
node.setAttribute("value", "PlaneInterleaved"); // TODO: Check with spec
dataNode.appendChild(node);
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
planarConfiguration.setAttribute("value", "PlaneInterleaved");
dataNode.appendChild(planarConfiguration);
node = new IIOMetadataNode("SampleFormat");
node.setAttribute("value", header.mode == PSD.COLOR_MODE_INDEXED ? "Index" : "UnsignedIntegral");
dataNode.appendChild(node);
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
sampleFormat.setAttribute("value", header.mode == PSD.COLOR_MODE_INDEXED ? "Index" : "UnsignedIntegral");
dataNode.appendChild(sampleFormat);
String bitDepth = Integer.toString(header.bits); // bits per plane
@@ -496,11 +514,9 @@ public final class PSDMetadata extends AbstractMetadata {
String[] bps = new String[header.channels];
Arrays.fill(bps, bitDepth);
node = new IIOMetadataNode("BitsPerSample");
node.setAttribute("value", StringUtil.toCSVString(bps, " "));
dataNode.appendChild(node);
// TODO: SampleMSB? Or is network (aka Motorola/big endian) byte order assumed?
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
bitsPerSample.setAttribute("value", StringUtil.toCSVString(bps, " "));
dataNode.appendChild(bitsPerSample);
return dataNode;
}
@@ -542,33 +558,6 @@ public final class PSDMetadata extends AbstractMetadata {
dimensionNode.appendChild(node);
}
// TODO:
/*
<!ELEMENT "HorizontalPixelOffset" EMPTY>
<!-- The horizonal position, in pixels, where the image should be
rendered onto a raster display -->
<!ATTLIST "HorizontalPixelOffset" "value" #CDATA #REQUIRED>
<!-- Data type: Integer -->
<!ELEMENT "VerticalPixelOffset" EMPTY>
<!-- The vertical position, in pixels, where the image should be
rendered onto a raster display -->
<!ATTLIST "VerticalPixelOffset" "value" #CDATA #REQUIRED>
<!-- Data type: Integer -->
<!ELEMENT "HorizontalScreenSize" EMPTY>
<!-- The width, in pixels, of the raster display into which the
image should be rendered -->
<!ATTLIST "HorizontalScreenSize" "value" #CDATA #REQUIRED>
<!-- Data type: Integer -->
<!ELEMENT "VerticalScreenSize" EMPTY>
<!-- The height, in pixels, of the raster display into which the
image should be rendered -->
<!ATTLIST "VerticalScreenSize" "value" #CDATA #REQUIRED>
<!-- Data type: Integer -->
*/
return dimensionNode;
}
@@ -580,11 +569,10 @@ public final class PSDMetadata extends AbstractMetadata {
@Override
protected IIOMetadataNode getStandardDocumentNode() {
IIOMetadataNode document_node = new IIOMetadataNode("Document");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("FormatVersion");
node.setAttribute("value", "1"); // PSD format version is always 1
document_node.appendChild(node);
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
formatVersion.setAttribute("value", header.largeFormat ? "2" : "1"); // PSD format version is always 1, PSB is 2
document_node.appendChild(formatVersion);
// Get EXIF data if present
Iterator<PSDEXIF1Data> exif = getResources(PSDEXIF1Data.class);
@@ -594,18 +582,18 @@ public final class PSDMetadata extends AbstractMetadata {
// Get the EXIF DateTime (aka ModifyDate) tag if present
Entry dateTime = data.directory.getEntryById(TIFF.TAG_DATE_TIME);
if (dateTime != null) {
node = new IIOMetadataNode("ImageCreationTime"); // As TIFF, but could just as well be ImageModificationTime
IIOMetadataNode imageCreationTime = new IIOMetadataNode("ImageCreationTime"); // As TIFF, but could just as well be ImageModificationTime
// Format: "YYYY:MM:DD hh:mm:ss"
String value = dateTime.getValueAsString();
node.setAttribute("year", value.substring(0, 4));
node.setAttribute("month", value.substring(5, 7));
node.setAttribute("day", value.substring(8, 10));
node.setAttribute("hour", value.substring(11, 13));
node.setAttribute("minute", value.substring(14, 16));
node.setAttribute("second", value.substring(17, 19));
imageCreationTime.setAttribute("year", value.substring(0, 4));
imageCreationTime.setAttribute("month", value.substring(5, 7));
imageCreationTime.setAttribute("day", value.substring(8, 10));
imageCreationTime.setAttribute("hour", value.substring(11, 13));
imageCreationTime.setAttribute("minute", value.substring(14, 16));
imageCreationTime.setAttribute("second", value.substring(17, 19));
document_node.appendChild(node);
document_node.appendChild(imageCreationTime);
}
}
@@ -614,7 +602,7 @@ public final class PSDMetadata extends AbstractMetadata {
@Override
protected IIOMetadataNode getStandardTextNode() {
// TODO: TIFF uses
// NOTE: TIFF uses
// DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright:
// /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
// Example: TIFF Software field => /Text/TextEntry@keyword = "Software",
@@ -679,9 +667,10 @@ public final class PSDMetadata extends AbstractMetadata {
}
private void appendTextEntriesFlat(final IIOMetadataNode node, final Directory directory, final FilterIterator.Filter<Entry> filter) {
FilterIterator<Entry> pEntries = new FilterIterator<Entry>(directory.iterator(), filter);
while (pEntries.hasNext()) {
Entry entry = pEntries.next();
FilterIterator<Entry> entries = new FilterIterator<Entry>(directory.iterator(), filter);
while (entries.hasNext()) {
Entry entry = entries.next();
if (entry.getValue() instanceof Directory) {
appendTextEntriesFlat(node, (Directory) entry.getValue(), filter);
@@ -694,7 +683,7 @@ public final class PSDMetadata extends AbstractMetadata {
tag.setAttribute("keyword", fieldName);
}
else {
// TODO: This should never happen, as we filter out only specific nodes
// NOTE: This should never happen, as we filter out only specific nodes
tag.setAttribute("keyword", String.format("%s", entry.getIdentifier()));
}
@@ -711,14 +700,13 @@ public final class PSDMetadata extends AbstractMetadata {
@Override
protected IIOMetadataNode getStandardTransparencyNode() {
IIOMetadataNode transparency_node = new IIOMetadataNode("Transparency");
IIOMetadataNode node; // scratch node
IIOMetadataNode transparencyNode = new IIOMetadataNode("Transparency");
node = new IIOMetadataNode("Alpha");
node.setAttribute("value", hasAlpha() ? "nonpremultiplied" : "none"); // TODO: Check spec
transparency_node.appendChild(node);
IIOMetadataNode node = new IIOMetadataNode("Alpha");
node.setAttribute("value", hasAlpha() ? "premultiplied" : "none");
transparencyNode.appendChild(node);
return transparency_node;
return transparencyNode;
}
private boolean hasAlpha() {
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.metadata.Directory;
@@ -17,7 +45,7 @@ import java.util.Arrays;
*/
public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
private final static PSDMetadataFormat sInstance = new PSDMetadataFormat();
private static final PSDMetadataFormat instance = new PSDMetadataFormat();
/**
* Private constructor.
@@ -207,6 +235,6 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
* @see javax.imageio.metadata.IIOMetadata#getMetadataFormat
*/
public static PSDMetadataFormat getInstance() {
return sInstance;
return instance;
}
}
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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 javax.imageio.stream.ImageInputStream;
import java.io.IOException;
/**
* PSDPathResource
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PSDPathResource.java,v 1.0 Sept 4, 2014 8:23:09 PM haraldk Exp$
*/
final class PSDPathResource extends PSDImageResource {
PSDPathResource(final short resourceId, final ImageInputStream input) throws IOException {
super(resourceId, input);
}
}
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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 javax.imageio.stream.ImageInputStream;
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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 javax.imageio.stream.ImageInputStream;
@@ -33,9 +61,13 @@ final class PSDPrintFlags extends PSDImageResource {
negative = pInput.readBoolean();
flip = pInput.readBoolean();
interpolate = pInput.readBoolean();
caption = pInput.readBoolean();
pInput.skipBytes(size - 8);
// Photoshop 2.5 and before has shorter struct
if (size > 7) {
caption = pInput.readBoolean();
pInput.skipBytes(size - 8);
}
}
@Override
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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 javax.imageio.stream.ImageInputStream;
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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 javax.imageio.stream.ImageInputStream;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$
*/
class PSDResolutionInfo extends PSDImageResource {
final class PSDResolutionInfo extends PSDImageResource {
// typedef struct _ResolutionInfo
// {
// LONG hRes; /* Fixed-point number: pixels per inch */
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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 javax.imageio.IIOException;
@@ -16,7 +44,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: PSDThumbnail.java,v 1.0 Jul 29, 2009 4:41:06 PM haraldk Exp$
*/
class PSDThumbnail extends PSDImageResource {
final class PSDThumbnail extends PSDImageResource {
private int format;
private int width;
private int height;
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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 javax.imageio.stream.ImageInputStream;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, Harald Kuhr
* Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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 javax.imageio.stream.ImageInputStream;
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2014, 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 "TwelveMonkeys" 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 OWNER 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.metadata.Directory;