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

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -36,7 +36,9 @@ package com.twelvemonkeys.imageio.metadata.psd;
* @version $Id: PSD.java,v 1.0 24.01.12 16:51 haraldk Exp$ * @version $Id: PSD.java,v 1.0 24.01.12 16:51 haraldk Exp$
*/ */
public interface PSD { public interface PSD {
static final int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M'; /** PSD image resource marker "8BIM". */
int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
static final int RES_IPTC_NAA = 0x0404; /** IPTC image resource id. */
int RES_IPTC_NAA = 0x0404;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -136,6 +136,10 @@ public final class PSDReader extends MetadataReader {
pInput.readFully(data); pInput.readFully(data);
} }
public final int id() {
return id;
}
public final byte[] data() { public final byte[] data() {
return data; return data;
} }

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import org.w3c.dom.Node; import org.w3c.dom.Node;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -42,7 +42,7 @@ import java.io.InputStream;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: ICCProfile.java,v 1.0 May 20, 2008 6:24:10 PM haraldk Exp$ * @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; private ICC_Profile profile;
ICCProfile(final short pId, final ImageInputStream pInput) throws IOException { ICCProfile(final short pId, final ImageInputStream pInput) throws IOException {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -35,15 +35,20 @@ package com.twelvemonkeys.imageio.plugins.psd;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSD.java,v 1.0 Apr 29, 2008 4:47:47 PM haraldk Exp$ * @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> * @see <a href="http://www.fileformat.info/format/psd/egff.htm">http://www.fileformat.info/format/psd/egff.htm</a>
*/ */
interface PSD { interface PSD {
/** PSD 2+ Native format (.PSD) identifier "8BPS" */ /** PSD 2+ Native format (.PSD) identifier "8BPS" */
int SIGNATURE_8BPS = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'S'; 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" */ /** PSD 5+ Large Document Format (.PSB) identifier "8BPB" */
int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B'; int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B';
int VERSION_PSD = 1;
int VERSION_PSB = 2;
/** PSD Resource type identifier "8BIM" */ /** PSD Resource type identifier "8BIM" */
int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M'; int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
@@ -105,7 +110,7 @@ interface PSD {
// Color Modes // Color Modes
/** Bitmap (monochrome) */ /** Bitmap (monochrome) */
short COLOR_MODE_MONOCHROME = 0; short COLOR_MODE_BITMAP = 0;
/** Gray-scale */ /** Gray-scale */
short COLOR_MODE_GRAYSCALE = 1; short COLOR_MODE_GRAYSCALE = 1;
@@ -541,4 +546,13 @@ interface PSD {
*/ */
int RES_PRINT_FLAGS_INFORMATION = 0x2710; 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;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -40,7 +40,7 @@ import java.util.List;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDAlphaChannelInfo.java,v 1.0 May 2, 2008 5:33:40 PM haraldk Exp$ * @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; List<String> names;
public PSDAlphaChannelInfo(short pId, final ImageInputStream pInput) throws IOException { public PSDAlphaChannelInfo(short pId, final ImageInputStream pInput) throws IOException {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -35,7 +35,7 @@ package com.twelvemonkeys.imageio.plugins.psd;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDChannelInfo.java,v 1.0 May 6, 2008 2:46:23 PM haraldk Exp$ * @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 short channelId;
final long length; final long length;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -38,7 +38,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDChannelSourceDestinationRange.java,v 1.0 May 6, 2008 5:14:13 PM haraldk Exp$ * @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 String channel;
private short sourceBlack; private short sourceBlack;
private short sourceWhite; private short sourceWhite;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -43,7 +43,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDColorData.java,v 1.0 Apr 29, 2008 5:33:01 PM haraldk Exp$ * @version $Id: PSDColorData.java,v 1.0 Apr 29, 2008 5:33:01 PM haraldk Exp$
*/ */
class PSDColorData { final class PSDColorData {
final byte[] colors; final byte[] colors;
private IndexColorModel colorModel; private IndexColorModel colorModel;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$ * @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 // TODO: Size of this struct should be 14.. Does not compute... Something bogus here
// ColorSpace definitions: // ColorSpace definitions:

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -38,8 +38,8 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDGlobalLayerMask.java,v 1.0 May 8, 2008 5:33:48 PM haraldk Exp$ * @version $Id: PSDGlobalLayerMask.java,v 1.0 May 8, 2008 5:33:48 PM haraldk Exp$
*/ */
class PSDGlobalLayerMask { final class PSDGlobalLayerMask {
final int colorSpace; final int colorSpace;
final int color1; final int color1;
final int color2; final int color2;
final int color3; final int color3;

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -28,8 +28,9 @@
package com.twelvemonkeys.imageio.plugins.psd; package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException; import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.IOException; import java.io.IOException;
/** /**
@@ -39,19 +40,21 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDHeader.java,v 1.0 Apr 29, 2008 5:18:22 PM haraldk Exp$ * @version $Id: PSDHeader.java,v 1.0 Apr 29, 2008 5:18:22 PM haraldk Exp$
*/ */
class PSDHeader { final class PSDHeader {
// The header is 26 bytes in length and is structured as follows: 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 // typedef struct _PSD_HEADER
// { // {
// BYTE Signature[4]; /* File ID "8BPS" */ // 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 */ // 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 */ // channels */
// LONG Rows; /* Height of image in pixels (1-30000) */ // LONG Rows; /* Height of image in pixels (1-30000/1-300000 for PSB) */
// LONG Columns; /* Width of image in pixels (1-30000) */ // LONG Columns; /* Width of image in pixels (1-30000/1-300000 for PSB) */
// WORD Depth; /* Number of bits per channel (1, 8, and 16) */ // WORD Depth; /* Number of bits per channel (1, 8, 16 or 32) */
// WORD Mode; /* Color mode */ // WORD Mode; /* Color mode */
// } PSD_HEADER; // } PSD_HEADER;
@@ -60,8 +63,9 @@ class PSDHeader {
final int height; final int height;
final short bits; final short bits;
final short mode; final short mode;
final boolean largeFormat;
PSDHeader(final ImageInputStream pInput) throws IOException { PSDHeader(final DataInput pInput) throws IOException {
int signature = pInput.readInt(); int signature = pInput.readInt();
if (signature != PSD.SIGNATURE_8BPS) { if (signature != PSD.SIGNATURE_8BPS) {
throw new IIOException("Not a PSD document, expected signature \"8BPS\": \"" + PSDUtil.intToStr(signature) + "\" (0x" + Integer.toHexString(signature) + ")"); 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(); int version = pInput.readUnsignedShort();
switch (version) { switch (version) {
case 1: case PSD.VERSION_PSD:
largeFormat = false;
break;
case PSD.VERSION_PSB:
largeFormat = true;
break; break;
case 2:
throw new IIOException("Photoshop Large Document Format (PSB) not supported yet.");
default: default:
throw new IIOException(String.format("Unknown PSD version, expected 1 or 2: 0x%08x", version)); throw new IIOException(String.format("Unknown PSD version, expected 1 or 2: 0x%08x", version));
} }
byte[] reserved = new byte[6]; byte[] reserved = new byte[6];
pInput.readFully(reserved); pInput.readFully(reserved); // We don't really care
channels = pInput.readShort(); channels = pInput.readShort();
if (channels <= 0) {
throw new IIOException(String.format("Unsupported number of channels: %d", channels));
}
height = pInput.readInt(); // Rows height = pInput.readInt(); // Rows
width = pInput.readInt(); // Columns width = pInput.readInt(); // Columns
bits = pInput.readShort(); 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(); 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 @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName()); return new StringBuilder(getClass().getSimpleName())
builder.append("[Channels: "); .append("[version: ")
builder.append(channels); .append(largeFormat ? "2" : "1")
builder.append(", width: "); .append(", channels: ")
builder.append(width); .append(channels)
builder.append(", height: "); .append(", width: ")
builder.append(height); .append(width)
builder.append(", depth: "); .append(", height: ")
builder.append(bits); .append(height)
builder.append(", mode: "); .append(", depth: ")
builder.append(mode); .append(bits)
switch (mode) { .append(", mode: ")
case PSD.COLOR_MODE_MONOCHROME: .append(mode)
builder.append(" (Monochrome)"); .append(" (")
break; .append(modeAsString())
case PSD.COLOR_MODE_GRAYSCALE: .append(")]")
builder.append(" (Grayscale)"); .toString();
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 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";
}
} }
} }

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -32,15 +32,12 @@ import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase; import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.color.ColorSpaces; import com.twelvemonkeys.imageio.color.ColorSpaces;
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier; import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
import com.twelvemonkeys.xml.XMLSerializer;
import org.w3c.dom.Node;
import javax.imageio.IIOException; import javax.imageio.IIOException;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam; import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageReaderSpi; import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import java.awt.*; import java.awt.*;
@@ -70,12 +67,12 @@ import java.util.List;
// 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)
public class PSDImageReader extends ImageReaderBase { public final class PSDImageReader extends ImageReaderBase {
private PSDHeader header; private PSDHeader header;
private ICC_ColorSpace colorSpace; private ICC_ColorSpace colorSpace;
protected PSDMetadata metadata; private PSDMetadata metadata;
protected PSDImageReader(final ImageReaderSpi originatingProvider) { PSDImageReader(final ImageReaderSpi originatingProvider) {
super(originatingProvider); super(originatingProvider);
} }
@@ -108,10 +105,7 @@ public class PSDImageReader extends ImageReaderBase {
} }
private int getLayerWidth(int layerIndex) throws IOException { private int getLayerWidth(int layerIndex) throws IOException {
if (metadata == null || metadata.layerInfo == null) { readLayerAndMaskInfo(true);
readImageResources(false);
readLayerAndMaskInfo(true);
}
PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex); PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
@@ -119,10 +113,7 @@ public class PSDImageReader extends ImageReaderBase {
} }
private int getLayerHeight(int layerIndex) throws IOException { private int getLayerHeight(int layerIndex) throws IOException {
if (metadata == null || metadata.layerInfo == null) { readLayerAndMaskInfo(true);
readImageResources(false);
readLayerAndMaskInfo(true);
}
PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex); PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex);
@@ -136,14 +127,10 @@ public class PSDImageReader extends ImageReaderBase {
private ImageTypeSpecifier getRawImageTypeInternal(final int imageIndex) throws IOException { private ImageTypeSpecifier getRawImageTypeInternal(final int imageIndex) throws IOException {
checkBounds(imageIndex); checkBounds(imageIndex);
readHeader();
// Image index above 0, means a layer // Image index above 0, means a layer
if (imageIndex > 0) { if (imageIndex > 0) {
if (metadata == null || metadata.layerInfo == null) { readLayerAndMaskInfo(true);
readImageResources(false);
readLayerAndMaskInfo(true);
}
return getRawImageTypeForLayer(imageIndex - 1); return getRawImageTypeForLayer(imageIndex - 1);
} }
@@ -154,46 +141,56 @@ public class PSDImageReader extends ImageReaderBase {
private ImageTypeSpecifier getRawImageTypeForCompositeLayer() throws IOException { private ImageTypeSpecifier getRawImageTypeForCompositeLayer() throws IOException {
ColorSpace cs; ColorSpace cs;
switch (header.mode) { switch (header.mode) {
case PSD.COLOR_MODE_MONOCHROME: case PSD.COLOR_MODE_BITMAP:
if (header.channels == 1 && header.bits == 1) { if (header.channels == 1 && header.bits == 1) {
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY); return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY);
} }
throw new IIOException( throw new IIOException(String.format("Unsupported channel count/bit depth for Monochrome PSD: %d channels/%d bits", header.channels, header.bits));
String.format("Unsupported channel count/bit depth for Monochrome PSD: %d channels/%d bits", header.channels, header.bits)
);
case PSD.COLOR_MODE_INDEXED: case PSD.COLOR_MODE_INDEXED:
// TODO: 16 bit indexed?! Does it exist?
if (header.channels == 1 && header.bits == 8) { if (header.channels == 1 && header.bits == 8) {
return IndexedImageTypeSpecifier.createFromIndexColorModel(metadata.colorData.getIndexColorModel()); return IndexedImageTypeSpecifier.createFromIndexColorModel(metadata.colorData.getIndexColorModel());
} }
throw new IIOException( throw new IIOException(String.format("Unsupported channel count/bit depth for Indexed Color PSD: %d channels/%d bits", header.channels, header.bits));
String.format("Unsupported channel count/bit depth for Indexed Color PSD: %d channels/%d bits", header.channels, header.bits)
);
case PSD.COLOR_MODE_DUOTONE: case PSD.COLOR_MODE_DUOTONE:
// NOTE: Duotone (whatever that is) should be treated as gray scale // NOTE: Duotone (whatever that is) should be treated as gray scale
// Fall-through // Fall-through
case PSD.COLOR_MODE_GRAYSCALE: case PSD.COLOR_MODE_GRAYSCALE:
cs = getEmbeddedColorSpace();
if (cs == null) {
cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
}
if (header.channels == 1 && header.bits == 8) { if (header.channels == 1 && header.bits == 8) {
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY); 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) { else if (header.channels == 1 && header.bits == 16) {
return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_GRAY); 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( throw new IIOException(String.format("Unsupported channel count/bit depth for Gray Scale PSD: %d channels/%d bits", header.channels, header.bits));
String.format("Unsupported channel count/bit depth for Gray Scale PSD: %d channels/%d bits", header.channels, header.bits)
);
case PSD.COLOR_MODE_RGB: case PSD.COLOR_MODE_RGB:
cs = getEmbeddedColorSpace(); cs = getEmbeddedColorSpace();
if (cs == null) { 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);
cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); // ColorSpaces.getColorSpace(ColorSpaces.CS_ADOBE_RGB_1998); ?
} }
if (header.channels == 3 && header.bits == 8) { if (header.channels == 3 && header.bits == 8) {
@@ -208,10 +205,14 @@ public class PSDImageReader extends ImageReaderBase {
else if (header.channels >= 4 && header.bits == 16) { 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); 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( throw new IIOException(String.format("Unsupported channel count/bit depth for RGB PSD: %d channels/%d bits", header.channels, header.bits));
String.format("Unsupported channel count/bit depth for RGB PSD: %d channels/%d bits", header.channels, header.bits)
);
case PSD.COLOR_MODE_CMYK: case PSD.COLOR_MODE_CMYK:
cs = getEmbeddedColorSpace(); cs = getEmbeddedColorSpace();
@@ -231,10 +232,14 @@ public class PSDImageReader extends ImageReaderBase {
else if (header.channels == 5 && header.bits == 16) { 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); 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( throw new IIOException(String.format("Unsupported channel count/bit depth for CMYK PSD: %d channels/%d bits", header.channels, header.bits));
String.format("Unsupported channel count/bit depth for CMYK PSD: %d channels/%d bits", header.channels, header.bits)
);
case PSD.COLOR_MODE_MULTICHANNEL: case PSD.COLOR_MODE_MULTICHANNEL:
// TODO: Implement // TODO: Implement
@@ -242,9 +247,7 @@ public class PSDImageReader extends ImageReaderBase {
// TODO: Implement // TODO: Implement
// TODO: If there's a color profile embedded, it should be easy, otherwise we're out of luck... // TODO: If there's a color profile embedded, it should be easy, otherwise we're out of luck...
default: default:
throw new IIOException( throw new IIOException(String.format("Unsupported PSD MODE: %s (%d channels/%d bits)", header.mode, header.channels, header.bits));
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 { private ColorSpace getEmbeddedColorSpace() throws IOException {
readImageResources(true); readImageResources(true);
// TODO: Skip this, requires storing some stream offsets
readLayerAndMaskInfo(false);
if (colorSpace == null) { if (colorSpace == null) {
ICC_Profile profile = 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 { public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
checkBounds(imageIndex); 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? // 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... // 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: Support layer in index 0, if "has real merged data" flag is false?
// TODO: Param support in layer code (more duping/cleanup..) // TODO: Param support in layer code (more duping/cleanup..)
if (imageIndex > 0) { 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); return readLayerData(imageIndex - 1, param);
} }
@@ -421,13 +407,11 @@ public class PSDImageReader extends ImageReaderBase {
ySub = param.getSourceYSubsampling(); ySub = param.getSourceYSubsampling();
} }
processImageStarted(imageIndex); imageInput.seek(metadata.imageDataStart);
int[] byteCounts = null;
int compression = imageInput.readShort(); int compression = imageInput.readShort();
// TODO: Need to make sure compression is set in metadata, even without reading the image data!
metadata.compression = compression; metadata.compression = compression;
int[] byteCounts = null;
switch (compression) { switch (compression) {
case PSD.COMPRESSION_NONE: case PSD.COMPRESSION_NONE:
break; break;
@@ -435,14 +419,12 @@ public class PSDImageReader extends ImageReaderBase {
// 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 i = 0; i < byteCounts.length; i++) {
byteCounts[i] = imageInput.readUnsignedShort(); byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
} }
break; break;
case PSD.COMPRESSION_ZIP: case PSD.COMPRESSION_ZIP:
// TODO: Could probably use the ZIPDecoder (DeflateDecoder) here..
case PSD.COMPRESSION_ZIP_PREDICTION: case PSD.COMPRESSION_ZIP_PREDICTION:
// TODO: Look at TIFF prediction reading // TODO: Could probably use the ZIPDecoder (DeflateDecoder) here.. Look at TIFF prediction reading
// Could be same as PNG prediction? Read up...
throw new IIOException("PSD with ZIP compression not supported"); throw new IIOException("PSD with ZIP compression not supported");
default: default:
throw new IIOException( 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 // What we read here is the "composite layer" of the PSD file
readImageData(image, rawType.getColorModel(), source, dest, xSub, ySub, byteCounts, compression); 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(); 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); 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; 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: 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()) { if (abortRequested()) {
@@ -531,6 +519,67 @@ public 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,
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, private void read16bitChannel(final int pChannel, final int pChannelCount,
final DataBuffer pData, final int pBands, final int pBandOffset, final DataBuffer pData, final int pBands, final int pBandOffset,
final ColorModel pSourceColorModel, final ColorModel pSourceColorModel,
@@ -731,7 +780,7 @@ public class PSDImageReader extends ImageReaderBase {
private void decomposeAlpha(final ColorModel pModel, final DataBuffer pBuffer, private void decomposeAlpha(final ColorModel pModel, final DataBuffer pBuffer,
final int pWidth, final int pHeight, final int pChannels) { 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? // TODO: What about CMYK + alpha?
if (pModel.hasAlpha() && pModel.getColorSpace().getType() == ColorSpace.TYPE_RGB) { if (pModel.hasAlpha() && pModel.getColorSpace().getType() == ColorSpace.TYPE_RGB) {
@@ -791,9 +840,16 @@ public class PSDImageReader extends ImageReaderBase {
private void readHeader() throws IOException { private void readHeader() throws IOException {
assertInput(); assertInput();
if (header == null) { if (header == null) {
header = new PSDHeader(imageInput); 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 = new PSDMetadata();
metadata.header = header; metadata.header = header;
@@ -818,6 +874,8 @@ public class PSDImageReader extends ImageReaderBase {
imageInput.skipBytes(length); imageInput.skipBytes(length);
} }
metadata.imageResourcesStart = imageInput.getStreamPosition();
// Don't need the header again // Don't need the header again
imageInput.flushBefore(imageInput.getStreamPosition()); imageInput.flushBefore(imageInput.getStreamPosition());
} }
@@ -826,85 +884,104 @@ public 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 readImageResources(final boolean pParseData) throws IOException { private void readImageResources(final boolean pParseData) throws IOException {
// TODO: Avoid unnecessary stream repositioning readHeader();
long pos = imageInput.getFlushedPosition();
imageInput.seek(pos);
long length = imageInput.readUnsignedInt(); if (pParseData || metadata.layerAndMaskInfoStart == 0) {
imageInput.seek(metadata.imageResourcesStart);
if (pParseData && length > 0) { long imageResourcesLength = imageInput.readUnsignedInt();
if (metadata.imageResources == null) {
metadata.imageResources = new ArrayList<PSDImageResource>();
long expectedEnd = imageInput.getStreamPosition() + length;
while (imageInput.getStreamPosition() < expectedEnd) { if (pParseData && imageResourcesLength > 0) {
// TODO: Have PSDImageResources defer actual parsing? (Just store stream offsets) if (metadata.imageResources == null) {
PSDImageResource resource = PSDImageResource.read(imageInput); metadata.imageResources = new ArrayList<PSDImageResource>();
metadata.imageResources.add(resource); 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) { // TODO: We should now be able to flush input
throw new IIOException("Corrupt PSD document"); // ..or maybe just a bug in the reader.. ;-) // 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: 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 pParseData) throws IOException {
// TODO: Make sure we are positioned correctly readImageResources(false);
// TODO: Avoid unnecessary stream repositioning
long length = imageInput.readUnsignedInt();
if (pParseData && length > 0) {
long pos = imageInput.getStreamPosition();
long read; if (pParseData || metadata.imageDataStart == 0) {
if (metadata.layerInfo == null) { imageInput.seek(metadata.layerAndMaskInfoStart);
long layerInfoLength = imageInput.readUnsignedInt();
/* long layerAndMaskInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt();
"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();
PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layers)]; // NOTE: The spec says that if this section is empty, the length should be 0.
for (int i = 0; i < layerInfos.length; i++) { // Yet I have a PSB file that has size 12, and both contained lengths set to 0 (which
layerInfos[i] = new PSDLayerInfo(imageInput); // 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; // read = imageInput.getStreamPosition() - pos;
//
long diff = layerInfoLength - (read - 4); // - 4 for the layerInfoLength field itself // long toSkip = layerAndMaskInfoLength - read;
// System.out.println("diff: " + diff); // System.out.println("toSkip: " + toSkip);
imageInput.skipBytes(diff); // imageInput.skipBytes(toSkip);
// 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; metadata.imageDataStart = metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4);
long toSkip = length - read;
// System.out.println("toSkip: " + toSkip);
imageInput.skipBytes(toSkip);
}
else {
// Skip entire layer and mask section
imageInput.skipBytes(length);
} }
} }
@@ -949,19 +1026,22 @@ public 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!
// TODO: Consider creating a method in PSDLayerInfo that can tell how many channels we really want to decode
for (PSDChannelInfo channelInfo : layerInfo.channelInfo) { for (PSDChannelInfo channelInfo : layerInfo.channelInfo) {
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 == -2 means "user supplied layer mask", whatever that is... // channelId
if (width <= 0 || height <= 0 || channelInfo.channelId == -2 || // -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)
(compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) { if (width <= 0 || height <= 0 || channelInfo.channelId < -1 ||
(compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) { // TODO: ZIP Compressions!
imageInput.skipBytes(channelInfo.length - 2); imageInput.skipBytes(channelInfo.length - 2);
} }
else { else {
// 0 = red, 1 = green, etc // 0 = red, 1 = green, etc
// -1 = transparency mask; -2 = user supplied layer mask // -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 ? layerInfo.channelInfo.length - 1 : channelInfo.channelId; 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.
@@ -975,10 +1055,10 @@ public class PSDImageReader extends ImageReaderBase {
case PSD.COMPRESSION_RLE: case PSD.COMPRESSION_RLE:
// If RLE, the the image data starts with the byte counts // If RLE, the 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 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] = imageInput.readUnsignedShort(); byteCounts[i] = header.largeFormat ? imageInput.readInt() : imageInput.readUnsignedShort();
} }
break; break;
@@ -998,11 +1078,18 @@ public class PSDImageReader extends ImageReaderBase {
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, 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; break;
case 16: case 16:
short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData(); 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; break;
default: default:
throw new IIOException(String.format("Unknown PSD bit depth: %s", header.bits)); 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 layer has more channels than composite data, it's normally extra alpha...
if (layerInfo.channelInfo.length > compositeType.getNumBands()) { if (layerInfo.channelInfo.length > compositeType.getNumBands()) {
// ...but, it could also be just the user mask... // ...but, it could also be just one of the user masks...
boolean userMask = false; int newBandNum = 0;
for (PSDChannelInfo channelInfo : layerInfo.channelInfo) { for (PSDChannelInfo channelInfo : layerInfo.channelInfo) {
if (channelInfo.channelId == -2) { // -2 = user supplied layer mask, -3 real user supplied layer mask (when both a user mask and a vector mask are present)
userMask = true; if (channelInfo.channelId >= -1) {
break; newBandNum++;
} }
} }
int newBandNum = layerInfo.channelInfo.length - (userMask ? 1 : 0);
// If there really is more channels, then create new imageTypeSpec // If there really is more channels, then create new imageTypeSpec
if (newBandNum > compositeType.getNumBands()) { if (newBandNum > compositeType.getNumBands()) {
@@ -1056,30 +1143,28 @@ public class PSDImageReader extends ImageReaderBase {
/// Layer support /// 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 @Override
public int getNumImages(boolean allowSearch) throws IOException { public int getNumImages(boolean allowSearch) throws IOException {
// NOTE: Spec says this method should throw IllegalStateException if allowSearch && isSeekForwardOnly() // 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? // But that makes no sense for a format (like PSD) that does not need to search, right?
readHeader(); readLayerAndMaskInfo(true);
readImageResources(false);
readLayerAndMaskInfo(true); // TODO: Consider quicker reading of just the number of layers.
return metadata.layerInfo != null ? metadata.layerInfo.size() + 1 : 1; // TODO: Only plus one, if "has real merged data"? 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 /// Metadata support
// TODO
@Override @Override
public IIOMetadata getStreamMetadata() throws IOException { public IIOMetadata getStreamMetadata() throws IOException {
@@ -1090,20 +1175,15 @@ public class PSDImageReader extends ImageReaderBase {
@Override @Override
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException { public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
// TODO: Implement
checkBounds(imageIndex); checkBounds(imageIndex);
readHeader();
readImageResources(true); readImageResources(true);
readLayerAndMaskInfo(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.compression = imageInput.readShort();
// metadata.header = header;
// metadata.colorData = colorData;
// metadata.imageResources = imageResources;
return metadata; // TODO: clone if we change to mutable metadata return metadata; // TODO: clone if we change to mutable metadata
} }
@@ -1129,8 +1209,6 @@ public class PSDImageReader extends ImageReaderBase {
if (metadata.imageResources == null) { if (metadata.imageResources == null) {
// TODO: Need flag here, to specify what resources to read... // TODO: Need flag here, to specify what resources to read...
readImageResources(true); readImageResources(true);
// TODO: Skip this, requires storing some stream offsets
readLayerAndMaskInfo(false);
} }
for (PSDImageResource resource : metadata.imageResources) { for (PSDImageResource resource : metadata.imageResources) {
@@ -1236,84 +1314,89 @@ public class PSDImageReader extends ImageReaderBase {
PSDImageReader imageReader = new PSDImageReader(null); PSDImageReader imageReader = new PSDImageReader(null);
File file = new File(pArgs[idx]); for (; idx < pArgs.length; idx++) {
ImageInputStream stream = ImageIO.createImageInputStream(file); File file = new File(pArgs[idx]);
imageReader.setInput(stream); System.out.println();
System.out.println("file: " + file.getAbsolutePath());
imageReader.readHeader(); ImageInputStream stream = ImageIO.createImageInputStream(file);
// System.out.println("imageReader.header: " + imageReader.header); imageReader.setInput(stream);
imageReader.readHeader();
System.out.println("imageReader.header: " + imageReader.header);
imageReader.readImageResources(true); imageReader.readImageResources(true);
System.out.println("imageReader.imageResources: " + imageReader.metadata.imageResources); System.out.println("imageReader.imageResources: " + imageReader.metadata.imageResources);
System.out.println(); System.out.println();
imageReader.readLayerAndMaskInfo(true); imageReader.readLayerAndMaskInfo(true);
System.out.println("imageReader.layerInfo: " + imageReader.metadata.layerInfo); System.out.println("imageReader.layerInfo: " + imageReader.metadata.layerInfo);
// System.out.println("imageReader.globalLayerMask: " + imageReader.globalLayerMask); /*
System.out.println(); // System.out.println("imageReader.globalLayerMask: " + imageReader.globalLayerMask);
System.out.println();
IIOMetadata metadata = imageReader.getImageMetadata(0); IIOMetadata metadata = imageReader.getImageMetadata(0);
Node node; Node node;
XMLSerializer serializer; XMLSerializer serializer;
node = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName); node = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
serializer = new XMLSerializer(System.out, System.getProperty("file.encoding")); serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
serializer.serialize(node, true); serializer.serialize(node, true);
System.out.println(); System.out.println();
node = metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME); node = metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME);
// serializer = new XMLSerializer(System.out, System.getProperty("file.encoding")); // serializer = new XMLSerializer(System.out, System.getProperty("file.encoding"));
serializer.serialize(node, true); serializer.serialize(node, true);
*/
if (readThumbnails && imageReader.hasThumbnails(0)) { if (readThumbnails && imageReader.hasThumbnails(0)) {
int thumbnails = imageReader.getNumThumbnails(0); int thumbnails = imageReader.getNumThumbnails(0);
for (int i = 0; i < thumbnails; i++) { for (int i = 0; i < thumbnails; i++) {
showIt(imageReader.readThumbnail(0, i), String.format("Thumbnail %d", 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) { if (sourceRegion != null) {
param.setSourceRegion(sourceRegion); 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()));
} }
catch (Exception e) {
e.printStackTrace(); if (subsampleFactor > 1) {
image = ImageUtil.accelerate(image); 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); 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) { showIt(image, file.getName());
int images = imageReader.getNumImages(true);
for (int i = 1; i < images; i++) { if (readLayers) {
start = System.currentTimeMillis(); int images = imageReader.getNumImages(true);
BufferedImage layer = imageReader.read(i); for (int i = 1; i < images; i++) {
System.out.println("layer read time: " + (System.currentTimeMillis() - start)); start = System.currentTimeMillis();
System.err.println("layer: " + layer); BufferedImage layer = imageReader.read(i);
showIt(layer, "layer " + i); System.out.println("layer read time: " + (System.currentTimeMillis() - start));
System.err.println("layer: " + layer);
showIt(layer, "layer " + i);
}
} }
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -44,7 +44,7 @@ import java.util.Locale;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDImageReaderSpi.java,v 1.0 Apr 29, 2008 4:49:03 PM haraldk Exp$ * @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}. * Creates a {@code PSDImageReaderSpi}.
@@ -57,18 +57,18 @@ public class PSDImageReaderSpi extends ImageReaderSpi {
super( super(
providerInfo.getVendorName(), providerInfo.getVendorName(),
providerInfo.getVersion(), providerInfo.getVersion(),
new String[]{"psd", "PSD"}, new String[] {"psd", "PSD"},
new String[]{"psd"}, new String[] {"psd"},
new String[]{ new String[] {
"image/vnd.adobe.photoshop", // Official, IANA registered "image/vnd.adobe.photoshop", // Official, IANA registered
"application/vnd.adobe.photoshop", // Used in XMP "application/vnd.adobe.photoshop", // Used in XMP
"image/x-psd", "image/x-psd",
"application/x-photoshop", "application/x-photoshop",
"image/x-photoshop" "image/x-photoshop"
}, },
"com.twelvemkonkeys.imageio.plugins.psd.PSDImageReader", "com.twelvemkonkeys.imageio.plugins.psd.PSDImageReader",
new Class[] {ImageInputStream.class}, new Class[] {ImageInputStream.class},
// new String[]{"com.twelvemkonkeys.imageio.plugins.psd.PSDImageWriterSpi"}, // new String[] {"com.twelvemkonkeys.imageio.plugins.psd.PSDImageWriterSpi"},
null, null,
true, // supports standard stream metadata true, // supports standard stream metadata
null, null, // native stream format name and class null, null, // native stream format name and class
@@ -87,9 +87,23 @@ public class PSDImageReaderSpi extends ImageReaderSpi {
ImageInputStream stream = (ImageInputStream) pSource; ImageInputStream stream = (ImageInputStream) pSource;
stream.mark(); stream.mark();
try { try {
return stream.readInt() == PSD.SIGNATURE_8BPS; if (stream.readInt() == PSD.SIGNATURE_8BPS) {
// TODO: Test more of the header, see PSDImageReader#readHeader int version = stream.readUnsignedShort();
switch (version) {
case PSD.VERSION_PSD:
case PSD.VERSION_PSB:
break;
default:
return false;
}
return true;
}
return false;
} }
finally { finally {
stream.reset(); stream.reset();

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -110,7 +110,7 @@ public class PSDImageResource {
builder.append("[ID: 0x"); builder.append("[ID: 0x");
builder.append(Integer.toHexString(id)); 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(name); builder.append(name);
builder.append("\""); builder.append("\"");
@@ -139,6 +139,13 @@ public class PSDImageResource {
case PSD.RES_PRINT_FLAGS_INFORMATION: case PSD.RES_PRINT_FLAGS_INFORMATION:
return null; return null;
default: 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 { try {
for (Field field : PSD.class.getDeclaredFields()) { for (Field field : PSD.class.getDeclaredFields()) {
if (field.getName().startsWith("RES_") && field.getInt(null) == pId) { if (field.getName().startsWith("RES_") && field.getInt(null) == pId) {
@@ -149,7 +156,7 @@ public class PSDImageResource {
} }
catch (IllegalAccessException ignore) { catch (IllegalAccessException ignore) {
} }
return "UnknownResource"; return "UnknownResource";
} }
} }
@@ -160,9 +167,9 @@ public class PSDImageResource {
throw new IIOException(String.format("Wrong image resource type, expected '8BIM': '%s'", PSDUtil.intToStr(type))); 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, // TODO: Have PSDImageResources defer actual parsing? (Just store stream offsets)
// version info, and thumbnail for thumbnail-support.
short id = pInput.readShort(); short id = pInput.readShort();
switch (id) { switch (id) {
case PSD.RES_RESOLUTION_INFO: case PSD.RES_RESOLUTION_INFO:
return new PSDResolutionInfo(id, pInput); return new PSDResolutionInfo(id, pInput);
@@ -196,9 +203,8 @@ public class PSDImageResource {
case PSD.RES_PRINT_FLAGS_INFORMATION: case PSD.RES_PRINT_FLAGS_INFORMATION:
return new PSDPrintFlagsInformation(id, pInput); return new PSDPrintFlagsInformation(id, pInput);
default: default:
if (id >= 0x07d0 && id <= 0x0bb6) { if (id >= PSD.RES_PATH_INFO_MIN && id <= PSD.RES_PATH_INFO_MAX) {
// TODO: Parse saved path information return new PSDPathResource(id, pInput);
return new PSDImageResource(id, pInput);
} }
else { else {
return new PSDImageResource(id, pInput); return new PSDImageResource(id, pInput);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDLayerBlendMode.java,v 1.0 May 8, 2008 4:34:35 PM haraldk Exp$ * @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 blendMode;
final int opacity; // 0-255 final int opacity; // 0-255
final int clipping; // 0: base, 1: non-base final int clipping; // 0: base, 1: non-base

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -40,7 +40,7 @@ import java.util.Arrays;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDLayerInfo.java,v 1.0 Apr 29, 2008 6:01:12 PM haraldk Exp$ * @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 top;
final int left; final int left;
final int bottom; final int bottom;
@@ -52,7 +52,7 @@ class PSDLayerInfo {
final PSDChannelSourceDestinationRange[] ranges; final PSDChannelSourceDestinationRange[] ranges;
final String layerName; final String layerName;
PSDLayerInfo(ImageInputStream pInput) throws IOException { PSDLayerInfo(final boolean largeFormat, final ImageInputStream pInput) throws IOException {
top = pInput.readInt(); top = pInput.readInt();
left = pInput.readInt(); left = pInput.readInt();
bottom = pInput.readInt(); bottom = pInput.readInt();
@@ -63,7 +63,7 @@ class PSDLayerInfo {
channelInfo = new PSDChannelInfo[channels]; channelInfo = new PSDChannelInfo[channels];
for (int i = 0; i < channels; i++) { for (int i = 0; i < channels; i++) {
short channelId = pInput.readShort(); short channelId = pInput.readShort();
long length = pInput.readUnsignedInt(); long length = largeFormat ? pInput.readLong() : pInput.readUnsignedInt();
channelInfo[i] = new PSDChannelInfo(channelId, length); channelInfo[i] = new PSDChannelInfo(channelId, length);
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDLayerMaskData.java,v 1.0 May 6, 2008 5:15:05 PM haraldk Exp$ * @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 top;
private int left; private int left;
private int bottom; private int bottom;

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;
@@ -35,7 +63,11 @@ public final class PSDMetadata extends AbstractMetadata {
List<PSDImageResource> imageResources; List<PSDImageResource> imageResources;
PSDGlobalLayerMask globalLayerMask; PSDGlobalLayerMask globalLayerMask;
List<PSDLayerInfo> layerInfo; List<PSDLayerInfo> layerInfo;
long imageResourcesStart;
long layerAndMaskInfoStart;
long layersStart; long layersStart;
long imageDataStart;
static final String[] COLOR_MODES = { static final String[] COLOR_MODES = {
"MONOCHROME", "GRAYSCALE", "INDEXED", "RGB", "CMYK", null, null, "MULTICHANNEL", "DUOTONE", "LAB" "MONOCHROME", "GRAYSCALE", "INDEXED", "RGB", "CMYK", null, null, "MULTICHANNEL", "DUOTONE", "LAB"
@@ -85,17 +117,17 @@ public final class PSDMetadata extends AbstractMetadata {
} }
private Node createHeaderNode() { private Node createHeaderNode() {
IIOMetadataNode header = new IIOMetadataNode("Header"); IIOMetadataNode headerNode = new IIOMetadataNode("Header");
header.setAttribute("type", "PSD"); headerNode.setAttribute("type", "PSD");
header.setAttribute("version", "1"); headerNode.setAttribute("version", header.largeFormat ? "2" : "1");
header.setAttribute("channels", Integer.toString(this.header.channels)); headerNode.setAttribute("channels", Integer.toString(header.channels));
header.setAttribute("height", Integer.toString(this.header.height)); headerNode.setAttribute("height", Integer.toString(header.height));
header.setAttribute("width", Integer.toString(this.header.width)); headerNode.setAttribute("width", Integer.toString(header.width));
header.setAttribute("bits", Integer.toString(this.header.bits)); headerNode.setAttribute("bits", Integer.toString(header.bits));
header.setAttribute("mode", COLOR_MODES[this.header.mode]); headerNode.setAttribute("mode", COLOR_MODES[header.mode]);
return header; return headerNode;
} }
private Node createImageResourcesNode() { private Node createImageResourcesNode() {
@@ -112,18 +144,6 @@ public final class PSDMetadata extends AbstractMetadata {
// TODO: Format spec // TODO: Format spec
node = new IIOMetadataNode("ICCProfile"); node = new IIOMetadataNode("ICCProfile");
node.setAttribute("colorSpaceType", JAVA_CS[profile.getProfile().getColorSpaceType()]); 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()); node.setUserObject(profile.getProfile());
} }
else if (imageResource instanceof PSDAlphaChannelInfo) { else if (imageResource instanceof PSDAlphaChannelInfo) {
@@ -347,13 +367,12 @@ public final class PSDMetadata extends AbstractMetadata {
@Override @Override
protected IIOMetadataNode getStandardChromaNode() { protected IIOMetadataNode getStandardChromaNode() {
IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma"); IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("ColorSpaceType"); IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType");
String cs; String cs;
switch (header.mode) { switch (header.mode) {
case PSD.COLOR_MODE_MONOCHROME: case PSD.COLOR_MODE_BITMAP:
case PSD.COLOR_MODE_GRAYSCALE: case PSD.COLOR_MODE_GRAYSCALE:
case PSD.COLOR_MODE_DUOTONE: // Rationale: Spec says treat as gray... case PSD.COLOR_MODE_DUOTONE: // Rationale: Spec says treat as gray...
cs = "GRAY"; cs = "GRAY";
@@ -374,25 +393,24 @@ public final class PSDMetadata extends AbstractMetadata {
default: default:
throw new AssertionError("Unreachable"); throw new AssertionError("Unreachable");
} }
node.setAttribute("name", cs); colorSpaceType.setAttribute("name", cs);
chroma_node.appendChild(node); chroma.appendChild(colorSpaceType);
// TODO: Channels might be 5 for RGB + A + Mask... Probably not correct // TODO: Channels might be 5 for RGB + A + Mask... Probably not correct
node = new IIOMetadataNode("NumChannels"); IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
node.setAttribute("value", Integer.toString(header.channels)); numChannels.setAttribute("value", Integer.toString(header.channels));
chroma_node.appendChild(node); chroma.appendChild(numChannels);
// TODO: Check if this is correct with bitmap (monchrome) IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
node = new IIOMetadataNode("BlackIsZero"); blackIsZero.setAttribute("value", "true");
node.setAttribute("value", "true"); chroma.appendChild(blackIsZero);
chroma_node.appendChild(node);
if (header.mode == PSD.COLOR_MODE_INDEXED) { if (header.mode == PSD.COLOR_MODE_INDEXED) {
node = createPaletteNode(); IIOMetadataNode paletteNode = createPaletteNode();
chroma_node.appendChild(node); chroma.appendChild(paletteNode);
} }
// TODO: Hardcode background color to white? // TODO: Use
// if (bKGD_present) { // if (bKGD_present) {
// if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) { // if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
// node = new IIOMetadataNode("BackgroundIndex"); // node = new IIOMetadataNode("BackgroundIndex");
@@ -415,24 +433,25 @@ public final class PSDMetadata extends AbstractMetadata {
// chroma_node.appendChild(node); // chroma_node.appendChild(node);
// } // }
return chroma_node; return chroma;
} }
private IIOMetadataNode createPaletteNode() { private IIOMetadataNode createPaletteNode() {
IIOMetadataNode node = new IIOMetadataNode("Palette"); IIOMetadataNode palette = new IIOMetadataNode("Palette");
IndexColorModel cm = colorData.getIndexColorModel(); IndexColorModel cm = colorData.getIndexColorModel();
for (int i = 0; i < cm.getMapSize(); i++) { for (int i = 0; i < cm.getMapSize(); i++) {
IIOMetadataNode entry = new IIOMetadataNode("PaletteEntry"); IIOMetadataNode entry = new IIOMetadataNode("PaletteEntry");
entry.setAttribute("index", Integer.toString(i)); entry.setAttribute("index", Integer.toString(i));
entry.setAttribute("red", Integer.toString(cm.getRed(i))); entry.setAttribute("red", Integer.toString(cm.getRed(i)));
entry.setAttribute("green", Integer.toString(cm.getGreen(i))); entry.setAttribute("green", Integer.toString(cm.getGreen(i)));
entry.setAttribute("blue", Integer.toString(cm.getBlue(i))); entry.setAttribute("blue", Integer.toString(cm.getBlue(i)));
node.appendChild(entry); palette.appendChild(entry);
} }
return node; return palette;
} }
private String getMultiChannelCS(short channels) { private String getMultiChannelCS(short channels) {
@@ -446,33 +465,33 @@ public final class PSDMetadata extends AbstractMetadata {
@Override @Override
protected IIOMetadataNode getStandardCompressionNode() { protected IIOMetadataNode getStandardCompressionNode() {
IIOMetadataNode compressionNode = new IIOMetadataNode("Compression"); IIOMetadataNode compressionNode = new IIOMetadataNode("Compression");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("CompressionTypeName"); IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
String compression; String compressionName;
switch (this.compression) { switch (compression) {
case PSD.COMPRESSION_NONE: case PSD.COMPRESSION_NONE:
compression = "none"; compressionName = "none";
break; break;
case PSD.COMPRESSION_RLE: case PSD.COMPRESSION_RLE:
compression = "PackBits"; compressionName = "PackBits";
break; break;
case PSD.COMPRESSION_ZIP: case PSD.COMPRESSION_ZIP:
case PSD.COMPRESSION_ZIP_PREDICTION: case PSD.COMPRESSION_ZIP_PREDICTION:
compression = "Deflate"; // TODO: ZLib? (TIFF native metadata format specifies both.. :-P) compressionName = "Zip";
break; break;
default: default:
throw new AssertionError("Unreachable"); throw new AssertionError("Unreachable");
} }
node.setAttribute("value", compression); compressionTypeName.setAttribute("value", compressionName);
compressionNode.appendChild(node); compressionNode.appendChild(compressionTypeName);
// TODO: Does it make sense to specify lossless for compression "none"? if (compression != PSD.COMPRESSION_NONE) {
node = new IIOMetadataNode("Lossless"); IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
node.setAttribute("value", "true"); lossless.setAttribute("value", "true");
compressionNode.appendChild(node); compressionNode.appendChild(lossless);
}
return compressionNode; return compressionNode;
} }
@@ -480,15 +499,14 @@ public final class PSDMetadata extends AbstractMetadata {
@Override @Override
protected IIOMetadataNode getStandardDataNode() { protected IIOMetadataNode getStandardDataNode() {
IIOMetadataNode dataNode = new IIOMetadataNode("Data"); IIOMetadataNode dataNode = new IIOMetadataNode("Data");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("PlanarConfiguration"); IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
node.setAttribute("value", "PlaneInterleaved"); // TODO: Check with spec planarConfiguration.setAttribute("value", "PlaneInterleaved");
dataNode.appendChild(node); dataNode.appendChild(planarConfiguration);
node = new IIOMetadataNode("SampleFormat"); IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
node.setAttribute("value", header.mode == PSD.COLOR_MODE_INDEXED ? "Index" : "UnsignedIntegral"); sampleFormat.setAttribute("value", header.mode == PSD.COLOR_MODE_INDEXED ? "Index" : "UnsignedIntegral");
dataNode.appendChild(node); dataNode.appendChild(sampleFormat);
String bitDepth = Integer.toString(header.bits); // bits per plane 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]; String[] bps = new String[header.channels];
Arrays.fill(bps, bitDepth); Arrays.fill(bps, bitDepth);
node = new IIOMetadataNode("BitsPerSample"); IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
node.setAttribute("value", StringUtil.toCSVString(bps, " ")); bitsPerSample.setAttribute("value", StringUtil.toCSVString(bps, " "));
dataNode.appendChild(node); dataNode.appendChild(bitsPerSample);
// TODO: SampleMSB? Or is network (aka Motorola/big endian) byte order assumed?
return dataNode; return dataNode;
} }
@@ -542,33 +558,6 @@ public final class PSDMetadata extends AbstractMetadata {
dimensionNode.appendChild(node); 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; return dimensionNode;
} }
@@ -580,11 +569,10 @@ public final class PSDMetadata extends AbstractMetadata {
@Override @Override
protected IIOMetadataNode getStandardDocumentNode() { protected IIOMetadataNode getStandardDocumentNode() {
IIOMetadataNode document_node = new IIOMetadataNode("Document"); IIOMetadataNode document_node = new IIOMetadataNode("Document");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("FormatVersion"); IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
node.setAttribute("value", "1"); // PSD format version is always 1 formatVersion.setAttribute("value", header.largeFormat ? "2" : "1"); // PSD format version is always 1, PSB is 2
document_node.appendChild(node); document_node.appendChild(formatVersion);
// Get EXIF data if present // Get EXIF data if present
Iterator<PSDEXIF1Data> exif = getResources(PSDEXIF1Data.class); 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 // Get the EXIF DateTime (aka ModifyDate) tag if present
Entry dateTime = data.directory.getEntryById(TIFF.TAG_DATE_TIME); Entry dateTime = data.directory.getEntryById(TIFF.TAG_DATE_TIME);
if (dateTime != null) { 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" // Format: "YYYY:MM:DD hh:mm:ss"
String value = dateTime.getValueAsString(); String value = dateTime.getValueAsString();
node.setAttribute("year", value.substring(0, 4)); imageCreationTime.setAttribute("year", value.substring(0, 4));
node.setAttribute("month", value.substring(5, 7)); imageCreationTime.setAttribute("month", value.substring(5, 7));
node.setAttribute("day", value.substring(8, 10)); imageCreationTime.setAttribute("day", value.substring(8, 10));
node.setAttribute("hour", value.substring(11, 13)); imageCreationTime.setAttribute("hour", value.substring(11, 13));
node.setAttribute("minute", value.substring(14, 16)); imageCreationTime.setAttribute("minute", value.substring(14, 16));
node.setAttribute("second", value.substring(17, 19)); 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 @Override
protected IIOMetadataNode getStandardTextNode() { protected IIOMetadataNode getStandardTextNode() {
// TODO: TIFF uses // NOTE: TIFF uses
// DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright: // DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright:
// /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value. // /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
// Example: TIFF Software field => /Text/TextEntry@keyword = "Software", // 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) { private void appendTextEntriesFlat(final IIOMetadataNode node, final Directory directory, final FilterIterator.Filter<Entry> filter) {
FilterIterator<Entry> pEntries = new FilterIterator<Entry>(directory.iterator(), filter); FilterIterator<Entry> entries = new FilterIterator<Entry>(directory.iterator(), filter);
while (pEntries.hasNext()) {
Entry entry = pEntries.next(); while (entries.hasNext()) {
Entry entry = entries.next();
if (entry.getValue() instanceof Directory) { if (entry.getValue() instanceof Directory) {
appendTextEntriesFlat(node, (Directory) entry.getValue(), filter); appendTextEntriesFlat(node, (Directory) entry.getValue(), filter);
@@ -694,7 +683,7 @@ public final class PSDMetadata extends AbstractMetadata {
tag.setAttribute("keyword", fieldName); tag.setAttribute("keyword", fieldName);
} }
else { 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())); tag.setAttribute("keyword", String.format("%s", entry.getIdentifier()));
} }
@@ -711,14 +700,13 @@ public final class PSDMetadata extends AbstractMetadata {
@Override @Override
protected IIOMetadataNode getStandardTransparencyNode() { protected IIOMetadataNode getStandardTransparencyNode() {
IIOMetadataNode transparency_node = new IIOMetadataNode("Transparency"); IIOMetadataNode transparencyNode = new IIOMetadataNode("Transparency");
IIOMetadataNode node; // scratch node
node = new IIOMetadataNode("Alpha"); IIOMetadataNode node = new IIOMetadataNode("Alpha");
node.setAttribute("value", hasAlpha() ? "nonpremultiplied" : "none"); // TODO: Check spec node.setAttribute("value", hasAlpha() ? "premultiplied" : "none");
transparency_node.appendChild(node); transparencyNode.appendChild(node);
return transparency_node; return transparencyNode;
} }
private boolean hasAlpha() { private boolean hasAlpha() {

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;
@@ -17,7 +45,7 @@ import java.util.Arrays;
*/ */
public final class PSDMetadataFormat extends IIOMetadataFormatImpl { public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
private final static PSDMetadataFormat sInstance = new PSDMetadataFormat(); private static final PSDMetadataFormat instance = new PSDMetadataFormat();
/** /**
* Private constructor. * Private constructor.
@@ -207,6 +235,6 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
* @see javax.imageio.metadata.IIOMetadata#getMetadataFormat * @see javax.imageio.metadata.IIOMetadata#getMetadataFormat
*/ */
public static PSDMetadataFormat getInstance() { public static PSDMetadataFormat getInstance() {
return sInstance; return instance;
} }
} }

View File

@@ -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);
}
}

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
@@ -33,9 +61,13 @@ final class PSDPrintFlags extends PSDImageResource {
negative = pInput.readBoolean(); negative = pInput.readBoolean();
flip = pInput.readBoolean(); flip = pInput.readBoolean();
interpolate = 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 @Override

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -39,7 +39,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDResolutionInfo.java,v 1.0 May 2, 2008 3:58:19 PM haraldk Exp$ * @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 // typedef struct _ResolutionInfo
// { // {
// LONG hRes; /* Fixed-point number: pixels per inch */ // LONG hRes; /* Fixed-point number: pixels per inch */

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.IIOException; import javax.imageio.IIOException;
@@ -16,7 +44,7 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$ * @author last modified by $Author: haraldk$
* @version $Id: PSDThumbnail.java,v 1.0 Jul 29, 2009 4:41:06 PM haraldk Exp$ * @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 format;
private int width; private int width;
private int height; private int height;

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2014, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.metadata.Directory; import com.twelvemonkeys.imageio.metadata.Directory;

View File

@@ -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; package com.twelvemonkeys.imageio.plugins.psd;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
@@ -27,7 +55,7 @@ import static org.junit.Assert.*;
*/ */
public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImageReader> { public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImageReader> {
static ImageReaderSpi provider = new PSDImageReaderSpi(); private static final ImageReaderSpi provider = new PSDImageReaderSpi();
protected List<TestData> getTestData() { protected List<TestData> getTestData() {
return Arrays.asList( return Arrays.asList(
@@ -48,7 +76,21 @@ public class PSDImageReaderTestCase extends ImageReaderAbstractTestCase<PSDImage
// 1 channel, gray, 16 bit samples // 1 channel, gray, 16 bit samples
new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)), new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)),
// 4 channel, CMYK, 16 bit samples // 4 channel, CMYK, 16 bit samples
new TestData(getClassLoaderResource("/psd/cmyk_16bits.psd"), new Dimension(1000, 275)) new TestData(getClassLoaderResource("/psd/cmyk_16bits.psd"), new Dimension(1000, 275)),
// 3 channel, RGB, 32 bit samples
new TestData(getClassLoaderResource("/psd/32bit5x5.psd"), new Dimension(5, 5)),
// 3 channel, RGB, 8 bit samples ("Large Document Format" aka PSB)
new TestData(getClassLoaderResource("/psd/test_original.psb"), new Dimension(710, 512)),
// From http://telegraphics.com.au/svn/psdparse/trunk/psd/
new TestData(getClassLoaderResource("/psd/adobehq.psd"), new Dimension(341, 512)),
new TestData(getClassLoaderResource("/psd/adobehq_ind.psd"), new Dimension(341, 512)),
// Contains a shorter than normal PrintFlags chunk
new TestData(getClassLoaderResource("/psd/adobehq-2.5.psd"), new Dimension(341, 512)),
new TestData(getClassLoaderResource("/psd/adobehq-3.0.psd"), new Dimension(341, 512)),
new TestData(getClassLoaderResource("/psd/adobehq-5.5.psd"), new Dimension(341, 512)),
new TestData(getClassLoaderResource("/psd/adobehq-7.0.psd"), new Dimension(341, 512)),
// From https://github.com/kmike/psd-tools/tree/master/tests/psd_files
new TestData(getClassLoaderResource("/psd/masks2.psd"), new Dimension(640, 1136)) // TODO: Test read layers!
// TODO: Need uncompressed PSD // TODO: Need uncompressed PSD
// TODO: Need more recent ZIP compressed PSD files from CS2/CS3+ // TODO: Need more recent ZIP compressed PSD files from CS2/CS3+
); );

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.