diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCEntry.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCEntry.java
index be129c3e..6bca3f91 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCEntry.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/iptc/IPTCEntry.java
@@ -49,6 +49,12 @@ class IPTCEntry extends AbstractEntry {
return "RecordVersion";
case IPTC.TAG_SOURCE:
return "Source";
+ case IPTC.TAG_CAPTION:
+ return "Caption";
+ case IPTC.TAG_COPYRIGHT_NOTICE:
+ return "CopyrightNotice";
+ case IPTC.TAG_BY_LINE:
+ return "ByLine";
// TODO: More tags...
}
diff --git a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDEntry.java b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDEntry.java
index bd444e39..b89d4eb4 100644
--- a/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDEntry.java
+++ b/imageio/imageio-metadata/src/main/java/com/twelvemonkeys/imageio/metadata/psd/PSDEntry.java
@@ -31,6 +31,8 @@ package com.twelvemonkeys.imageio.metadata.psd;
import com.twelvemonkeys.imageio.metadata.AbstractEntry;
import com.twelvemonkeys.lang.StringUtil;
+import java.lang.reflect.Field;
+
/**
* PhotoshopEntry
*
@@ -53,6 +55,39 @@ class PSDEntry extends AbstractEntry {
@Override
public String getFieldName() {
+ Class[] classes = new Class[] {getPSDClass()};
+
+ for (Class cl : classes) {
+ Field[] fields = cl.getDeclaredFields();
+
+ for (Field field : fields) {
+ try {
+ if (field.getType() == Integer.TYPE && field.getName().startsWith("RES_")) {
+ field.setAccessible(true);
+
+ if (field.get(null).equals(getIdentifier())) {
+ return StringUtil.lispToCamel(field.getName().substring(4).replace("_", "-").toLowerCase(), true);
+ }
+ }
+ }
+ catch (IllegalAccessException e) {
+ // Should never happen, but in case, abort
+ break;
+ }
+ }
+ }
+
return name;
}
+
+ private Class> getPSDClass() {
+ // TODO: Instead, move members to metadata module PSD class!
+ try {
+ return Class.forName("com.twelvemonkeys.imageio.plugins.psd.PSD");
+ }
+ catch (ClassNotFoundException ignore) {
+ }
+
+ return PSD.class;
+ }
}
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/AbstractMetadata.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/AbstractMetadata.java
index c5f3a528..091246c7 100644
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/AbstractMetadata.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/AbstractMetadata.java
@@ -129,4 +129,9 @@ abstract class AbstractMetadata extends IIOMetadata implements Cloneable {
String.format("Bad format name: \"%s\". Expected one of %s", pFormatName, Arrays.toString(metadataFormatNames))
);
}
+
+ protected static String toListString(short[] values) {
+ String string = Arrays.toString(values);
+ return string.substring(1, string.length() - 1);
+ }
}
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java
index 8c676663..b1c0539a 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSD.java
@@ -36,15 +36,15 @@ package com.twelvemonkeys.imageio.plugins.psd;
* @version $Id: PSD.java,v 1.0 Apr 29, 2008 4:47:47 PM haraldk Exp$
*
* @see Adobe Photoshop File Formats Specification
- * @see http://www.fileformat.info/format/psd/egff.htm
+ * @see Adobe Photoshop File Format Summary
*/
interface PSD {
/** PSD 2+ Native format (.PSD) identifier "8BPS" */
int SIGNATURE_8BPS = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'S';
- // TODO: Is this ever used??! Spec says (and sample files uses) 8BPS + version == 2 for PSB...
- /** PSD 5+ Large Document Format (.PSB) identifier "8BPB" */
- int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B';
+ // This is never used, it seems. Spec says (and sample files uses) 8BPS + version == 2 for PSB...
+ //** PSD 5+ Large Document Format (.PSB) identifier "8BPB" */
+ //int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B';
int VERSION_PSD = 1;
int VERSION_PSB = 2;
@@ -53,6 +53,9 @@ interface PSD {
int RESOURCE_TYPE = ('8' << 24) + ('B' << 16) + ('I' << 8) + 'M';
// Blending modes
+ /** Pass through blending mode "pass"*/
+ int BLEND_PASS = ('p' << 24) + ('a' << 16) + ('s' << 8) + 's';
+
/** Normal blending mode "norm"*/
int BLEND_NORM = ('n' << 24) + ('o' << 16) + ('r' << 8) + 'm';
@@ -75,7 +78,7 @@ interface PSD {
int BLEND_LUM = ('l' << 24) + ('u' << 16) + ('m' << 8) + ' ';
/** Multiply blending mode "mul " */
- int BELND_MUL = ('m' << 24) + ('u' << 16) + ('l' << 8) + ' ';
+ int BLEND_MUL = ('m' << 24) + ('u' << 16) + ('l' << 8) + ' ';
/** Screen blending mode "scrn" */
int BLEND_SCRN = ('s' << 24) + ('c' << 16) + ('r' << 8) + 'n';
@@ -95,6 +98,45 @@ interface PSD {
/** Difference blending mode "diff" */
int BLEND_DIFF = ('d' << 24) + ('i' << 16) + ('f' << 8) + 'f';
+ /** Color burn blending mode "idiv" */
+ int BLEND_IDIV = ('i' << 24) + ('d' << 16) + ('i' << 8) + 'v';
+
+ /** Linear burn blending mode "lbrn" */
+ int BLEND_LBRN = ('l' << 24) + ('b' << 16) + ('r' << 8) + 'n';
+
+ /** Darker color blending mode "dkCl" */
+ int BLEND_DKCL = ('d' << 24) + ('k' << 16) + ('C' << 8) + 'l';
+
+ /** Color dodge blending mode "div " */
+ int BLEND_DIV = ('d' << 24) + ('i' << 16) + ('v' << 8) + ' ';
+
+ /** Linear dodge blending mode "lddg" */
+ int BLEND_LDDG = ('l' << 24) + ('d' << 16) + ('d' << 8) + 'g';
+
+ /** Lighter color blending mode "lgCl" */
+ int BLEND_LGCL = ('l' << 24) + ('g' << 16) + ('C' << 8) + 'l';
+
+ /** Vivid light blending mode "vLit" */
+ int BLEND_VLIT = ('v' << 24) + ('L' << 16) + ('i' << 8) + 't';
+
+ /** Linear light blending mode "lLit" */
+ int BLEND_LLIT = ('l' << 24) + ('L' << 16) + ('i' << 8) + 't';
+
+ /** Pin light blending mode "pLit" */
+ int BLEND_PLIT = ('p' << 24) + ('L' << 16) + ('i' << 8) + 't';
+
+ /** Hard mix blending mode "hMix" */
+ int BLEND_HMIX = ('h' << 24) + ('M' << 16) + ('i' << 8) + 'x';
+
+ /** Exclusion blending mode "smud" */
+ int BLEND_SMUD = ('s' << 24) + ('m' << 16) + ('u' << 8) + 'd';
+
+ /** Subtract blending mode "fsub" */
+ int BLEND_FSUB = ('f' << 24) + ('s' << 16) + ('u' << 8) + 'b';
+
+ /** Divide blending mode "fdiv" */
+ int BLEND_FDIV = ('f' << 24) + ('d' << 16) + ('i' << 8) + 'v';
+
// Compression modes
/** No compression */
int COMPRESSION_NONE = 0;
@@ -504,12 +546,17 @@ interface PSD {
/**
* (Photoshop CS) Pixel Aspect Ratio
* 4 bytes (version = 1), 8 bytes double, x / y of a pixel
- * 0x0429 1065 (Photoshop CS) Layer Comps
- * 4 bytes (descriptor version = 16), Descriptor (see ?Descriptor structure?
- * on page57)
*/
int RES_PIXEL_ASPECT_RATIO = 0x0428;
+
+ // 1065
+ /**
+ * (Photoshop CS) Layer Comps
+ * 4 bytes (descriptor version = 16), Descriptor.
+ */
+ int RES_LAYER_COMPS = 0x0429;
+
// 1066
/**
* (Photoshop CS) Alternate Duotone Colors
@@ -554,5 +601,4 @@ interface PSD {
/** 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;
-
}
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDChannelSourceDestinationRange.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDChannelSourceDestinationRange.java
index 3bff3bba..406d8ccb 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDChannelSourceDestinationRange.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDChannelSourceDestinationRange.java
@@ -45,7 +45,7 @@ final class PSDChannelSourceDestinationRange {
private short destBlack;
private short destWhite;
- public PSDChannelSourceDestinationRange(ImageInputStream pInput, String pChannel) throws IOException {
+ public PSDChannelSourceDestinationRange(final ImageInputStream pInput, final String pChannel) throws IOException {
channel = pChannel;
sourceBlack = pInput.readShort();
sourceWhite = pInput.readShort();
@@ -56,7 +56,7 @@ final class PSDChannelSourceDestinationRange {
@Override
public String toString() {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
-
+
builder.append("[(").append(channel);
builder.append("): sourceBlack: ").append(Integer.toHexString(sourceBlack & 0xffff));
builder.append(", sourceWhite: ").append(Integer.toHexString(sourceWhite & 0xffff));
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java
index 68366faf..6d26f396 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDGlobalLayerMask.java
@@ -39,29 +39,23 @@ import java.io.IOException;
* @version $Id: PSDGlobalLayerMask.java,v 1.0 May 8, 2008 5:33:48 PM haraldk Exp$
*/
final class PSDGlobalLayerMask {
- final int colorSpace;
- final int color1;
- final int color2;
- final int color3;
- final int color4;
+ final int colorSpace;
+ final short[] colors = new short[4];
final int opacity;
final int kind;
- PSDGlobalLayerMask(final ImageInputStream pInput) throws IOException {
+ PSDGlobalLayerMask(final ImageInputStream pInput, final long globalLayerMaskLength) throws IOException {
colorSpace = pInput.readUnsignedShort(); // Undocumented
- color1 = pInput.readUnsignedShort();
- color2 = pInput.readUnsignedShort();
- color3 = pInput.readUnsignedShort();
- color4 = pInput.readUnsignedShort();
+ pInput.readFully(colors, 0, colors.length);
opacity = pInput.readUnsignedShort(); // 0-100
- kind = pInput.readUnsignedByte(); // 0: Selected (ie inverted), 1: Color protected, 128: Use value stored per layer
+ // Kind: 0: Selected (ie inverted), 1: Color protected, 128: Use value stored per layer (actually, the value is 80, not 0x80)
+ kind = pInput.readUnsignedByte();
- // TODO: Variable: Filler zeros
-
- pInput.readByte(); // Pad
+ // Skip "Variable: Filler zeros"
+ pInput.skipBytes(globalLayerMaskLength - 17);
}
@Override
@@ -69,13 +63,20 @@ final class PSDGlobalLayerMask {
StringBuilder builder = new StringBuilder(getClass().getSimpleName());
builder.append("[");
builder.append("color space: 0x").append(Integer.toHexString(colorSpace));
- builder.append(", colors: [0x").append(Integer.toHexString(color1));
- builder.append(", 0x").append(Integer.toHexString(color2));
- builder.append(", 0x").append(Integer.toHexString(color3));
- builder.append(", 0x").append(Integer.toHexString(color4));
+ builder.append(", colors: [");
+
+ for (int i = 0; i < colors.length; i++) {
+ if (i > 0) {
+ builder.append(", ");
+ }
+
+ builder.append("0x").append(Integer.toHexString(colors[i]));
+ }
+
builder.append("], opacity: ").append(opacity);
builder.append(", kind: ").append(kind);
builder.append("]");
+
return builder.toString();
}
}
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java
index 7fa2b9e1..ea1d40d3 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReader.java
@@ -54,6 +54,7 @@ import java.util.List;
/**
* ImageReader for Adobe Photoshop Document (PSD) format.
*
+ * @see Adobe Photoshop File Formats Specification
* @see Adobe Photoshop File Format Summary
* @author Harald Kuhr
* @author last modified by $Author: haraldk$
@@ -593,6 +594,7 @@ public final class PSDImageReader extends ImageReaderBase {
if (abortRequested()) {
break;
}
+
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
}
}
@@ -654,6 +656,7 @@ public final class PSDImageReader extends ImageReaderBase {
if (abortRequested()) {
break;
}
+
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
}
}
@@ -712,6 +715,7 @@ public final class PSDImageReader extends ImageReaderBase {
if (abortRequested()) {
break;
}
+
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
}
}
@@ -791,6 +795,7 @@ public final class PSDImageReader extends ImageReaderBase {
if (abortRequested()) {
break;
}
+
processImageProgressForChannel(pChannel, pChannelCount, y, pChannelHeight);
}
}
@@ -974,10 +979,10 @@ public final class PSDImageReader extends ImageReaderBase {
// 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!
+ long globalLayerMaskInfoLength = imageInput.readUnsignedInt(); // NOTE: Not long for PSB!
- if (layerMaskInfoLength > 0) {
- metadata.globalLayerMask = new PSDGlobalLayerMask(imageInput);
+ if (globalLayerMaskInfoLength > 0) {
+ metadata.globalLayerMask = new PSDGlobalLayerMask(imageInput, globalLayerMaskInfoLength);
}
// TODO: Parse "Additional layer information"
@@ -986,12 +991,6 @@ public final class PSDImageReader extends ImageReaderBase {
// imageInput.seek(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
// imageInput.flushBefore(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4));
}
-
-// read = imageInput.getStreamPosition() - pos;
-//
-// long toSkip = layerAndMaskInfoLength - read;
-// System.out.println("toSkip: " + toSkip);
-// imageInput.skipBytes(toSkip);
}
metadata.imageDataStart = metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4);
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerBlendMode.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerBlendMode.java
index e6035e69..043d23d6 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerBlendMode.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerBlendMode.java
@@ -28,8 +28,8 @@
package com.twelvemonkeys.imageio.plugins.psd;
-import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException;
+import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
/**
@@ -43,11 +43,11 @@ final class PSDLayerBlendMode {
final int blendMode;
final int opacity; // 0-255
final int clipping; // 0: base, 1: non-base
- final int flags;
+ final byte flags;
public PSDLayerBlendMode(final ImageInputStream pInput) throws IOException {
int blendModeSig = pInput.readInt();
- if (blendModeSig != PSD.RESOURCE_TYPE) { // TODO: Is this really just a resource?
+ if (blendModeSig != PSD.RESOURCE_TYPE) {
throw new IIOException("Illegal PSD Blend Mode signature, expected 8BIM: " + PSDUtil.intToStr(blendModeSig));
}
@@ -55,7 +55,7 @@ final class PSDLayerBlendMode {
opacity = pInput.readUnsignedByte();
clipping = pInput.readUnsignedByte();
- flags = pInput.readUnsignedByte();
+ flags = pInput.readByte();
pInput.readByte(); // Pad
}
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java
index 5b50196b..1fc8fd82 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerInfo.java
@@ -28,8 +28,8 @@
package com.twelvemonkeys.imageio.plugins.psd;
-import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException;
+import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Arrays;
@@ -70,10 +70,8 @@ final class PSDLayerInfo {
blendMode = new PSDLayerBlendMode(pInput);
- // Lenght of layer mask data
+ // Length of layer mask data
long extraDataSize = pInput.readUnsignedInt();
- // TODO: Allow skipping the rest here?
- // pInput.skipBytes(extraDataSize);
// Layer mask/adjustment layer data
int layerMaskDataSize = pInput.readInt(); // May be 0, 20 or 36 bytes...
@@ -94,7 +92,6 @@ final class PSDLayerInfo {
ranges[i] = new PSDChannelSourceDestinationRange(pInput, (i == 0 ? "Gray" : "Channel " + (i - 1)));
}
-
layerName = PSDUtil.readPascalString(pInput);
int layerNameSize = layerName.length() + 1;
@@ -106,8 +103,7 @@ final class PSDLayerInfo {
layerNameSize += skip;
}
- // TODO: There's some data skipped here...
- // Adjustment layer info etc...
+ // TODO: Consider reading this: Adjustment layer info etc...
pInput.skipBytes(extraDataSize - layerMaskDataSize - 4 - layerBlendingDataSize - 4 - layerNameSize);
}
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerMaskData.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerMaskData.java
index 55782e8c..aa13943c 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerMaskData.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDLayerMaskData.java
@@ -55,10 +55,11 @@ final class PSDLayerMaskData {
private int realBottom;
private int realRight;
- PSDLayerMaskData(ImageInputStream pInput, int pSize) throws IOException {
+ PSDLayerMaskData(final ImageInputStream pInput, final int pSize) throws IOException {
if (pSize != 20 && pSize != 36) {
- throw new IIOException("Illegal PSD Layer Mask data size: " + pSize + " (expeced 20 or 36)");
+ throw new IIOException("Illegal PSD Layer Mask data size: " + pSize + " (expected 20 or 36)");
}
+
top = pInput.readInt();
left = pInput.readInt();
bottom = pInput.readInt();
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java
index 1f6bbffd..2ca29956 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadata.java
@@ -52,7 +52,6 @@ import java.util.List;
*/
public final class PSDMetadata extends AbstractMetadata {
- // TODO: Decide on image/stream metadata...
static final String NATIVE_METADATA_FORMAT_NAME = "com_twelvemonkeys_imageio_psd_image_1.0";
static final String NATIVE_METADATA_FORMAT_CLASS_NAME = "com.twelvemonkeys.imageio.plugins.psd.PSDMetadataFormat";
// TODO: Support TIFF metadata, based on EXIF/XMP + merge in PSD specifics
@@ -93,7 +92,7 @@ public final class PSDMetadata extends AbstractMetadata {
static final String[] PRINT_SCALE_STYLES = {"centered", "scaleToFit", "userDefined"};
protected PSDMetadata() {
- // TODO: Allow XMP, EXIF and IPTC as extra formats?
+ // TODO: Allow XMP, EXIF (TIFF) and IPTC as extra formats?
super(true, NATIVE_METADATA_FORMAT_NAME, NATIVE_METADATA_FORMAT_CLASS_NAME, null, null);
}
@@ -112,7 +111,15 @@ public final class PSDMetadata extends AbstractMetadata {
if (imageResources != null && !imageResources.isEmpty()) {
root.appendChild(createImageResourcesNode());
}
-
+
+ if (layerInfo != null && !layerInfo.isEmpty()) {
+ root.appendChild(createLayerInfoNode());
+ }
+
+ if (globalLayerMask != null) {
+ root.appendChild(createGlobalLayerMaskNode());
+ }
+
return root;
}
@@ -291,7 +298,7 @@ public final class PSDMetadata extends AbstractMetadata {
PSDEXIF1Data exif = (PSDEXIF1Data) imageResource;
node = new IIOMetadataNode("DirectoryResource");
- node.setAttribute("type", "EXIF");
+ node.setAttribute("type", "TIFF");
// TODO: Set byte[] data instead
node.setUserObject(exif.directory);
@@ -326,10 +333,6 @@ public final class PSDMetadata extends AbstractMetadata {
resource.appendChild(node);
}
- // TODO: Layers and layer info
-
- // TODO: Global mask etc..
-
return resource;
}
@@ -363,6 +366,86 @@ public final class PSDMetadata extends AbstractMetadata {
}
}
+ private Node createLayerInfoNode() {
+ IIOMetadataNode layers = new IIOMetadataNode("Layers");
+ IIOMetadataNode node;
+
+
+ for (PSDLayerInfo psdLayerInfo : layerInfo) {
+ // TODO: Group in layer and use sub node for blend mode?
+ node = new IIOMetadataNode("LayerInfo");
+ node.setAttribute("name", psdLayerInfo.layerName);
+ node.setAttribute("top", String.valueOf(psdLayerInfo.top));
+ node.setAttribute("left", String.valueOf(psdLayerInfo.left));
+ node.setAttribute("bottom", String.valueOf(psdLayerInfo.bottom));
+ node.setAttribute("right", String.valueOf(psdLayerInfo.right));
+
+ node.setAttribute("blendMode", PSDUtil.intToStr(psdLayerInfo.blendMode.blendMode));
+ node.setAttribute("opacity", String.valueOf(psdLayerInfo.blendMode.opacity)); // 0-255
+ node.setAttribute("clipping", getClippingValue(psdLayerInfo.blendMode.clipping)); // Enum: 0: Base, 1: Non-base, n: unknown
+ node.setAttribute("flags", String.valueOf(psdLayerInfo.blendMode.flags));
+
+ if ((psdLayerInfo.blendMode.flags & 0x01) != 0) {
+ node.setAttribute("transparencyProtected", "true");
+ }
+ if ((psdLayerInfo.blendMode.flags & 0x02) != 0) {
+ node.setAttribute("visible", "true");
+ }
+ if ((psdLayerInfo.blendMode.flags & 0x04) != 0) {
+ node.setAttribute("obsolete", "true");
+ }
+ if ((psdLayerInfo.blendMode.flags & 0x08) != 0 && (psdLayerInfo.blendMode.flags & 0x10) != 0) {
+ node.setAttribute("pixelDataIrrelevant", "true");
+ }
+
+ // Skip channelInfo
+ // Skip layerMaskData
+ // TODO: Consider adding psdLayerInfo.ranges as it may be useful for composing
+
+ layers.appendChild(node);
+ }
+
+ return layers;
+ }
+
+ private String getClippingValue(final int clipping) {
+ switch (clipping) {
+ case 0:
+ return "base";
+ case 1:
+ return "non-base";
+ default:
+ }
+
+ return String.valueOf(clipping);
+ }
+
+ private Node createGlobalLayerMaskNode() {
+ IIOMetadataNode globalLayerMaskNode = new IIOMetadataNode("GlobalLayerMask");
+
+ globalLayerMaskNode.setAttribute("colorSpace", String.valueOf(globalLayerMask.colorSpace));
+ globalLayerMaskNode.setAttribute("colors", toListString(globalLayerMask.colors));
+ globalLayerMaskNode.setAttribute("opacity", String.valueOf(globalLayerMask.opacity)); // 0-100
+ globalLayerMaskNode.setAttribute("kind", getGlobalLayerMaskKind(globalLayerMask.kind)); // (selected|protected|layer)
+
+ return globalLayerMaskNode;
+ }
+
+ private String getGlobalLayerMaskKind(final int kind) {
+ switch (kind) {
+ case 0:
+ return "selected";
+ case 1:
+ return "protected";
+ case 80: // Spec says 128 (which is 0x80, probably a bug in the implementation...)
+ case 0x80:
+ return "layer";
+ default:
+ }
+
+ return String.valueOf(kind);
+ }
+
/// Standard format support
@Override
@@ -620,7 +703,7 @@ public final class PSDMetadata extends AbstractMetadata {
// TODO: Reader/writer (PSDVersionInfo)
while (textResources.hasNext()) {
- PSDImageResource textResource = textResources.next();
+ final PSDImageResource textResource = textResources.next();
if (textResource instanceof PSDIPTCData) {
PSDIPTCData iptc = (PSDIPTCData) textResource;
@@ -630,6 +713,10 @@ public final class PSDMetadata extends AbstractMetadata {
Integer tagId = (Integer) pEntry.getIdentifier();
switch (tagId) {
+ // Older PSD versions have only these, map to TIFF counterparts
+ case IPTC.TAG_CAPTION: // Use if TIFF.TAG_IMAGE_DESCRIPTION is missing
+ case IPTC.TAG_BY_LINE: // Use if TIFF.TAG_ARTIST is missing
+ case IPTC.TAG_COPYRIGHT_NOTICE: // Use if TIFF.TAG_COPYRIGHT is missing
case IPTC.TAG_SOURCE:
return true;
default:
@@ -649,6 +736,7 @@ public final class PSDMetadata extends AbstractMetadata {
case TIFF.TAG_SOFTWARE:
case TIFF.TAG_ARTIST:
case TIFF.TAG_COPYRIGHT:
+ case TIFF.TAG_IMAGE_DESCRIPTION:
return true;
default:
return false;
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadataFormat.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadataFormat.java
index c6df5c3e..50e99677 100755
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadataFormat.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDMetadataFormat.java
@@ -35,6 +35,7 @@ import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import java.awt.image.BufferedImage;
import java.util.Arrays;
+import java.util.List;
/**
* PSDMetadataFormat
@@ -47,6 +48,19 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
private static final PSDMetadataFormat instance = new PSDMetadataFormat();
+ static final List PSD_BLEND_MODES = Arrays.asList(
+ PSDUtil.intToStr(PSD.BLEND_PASS), PSDUtil.intToStr(PSD.BLEND_NORM), PSDUtil.intToStr(PSD.BLEND_DISS),
+ PSDUtil.intToStr(PSD.BLEND_DARK), PSDUtil.intToStr(PSD.BLEND_MUL), PSDUtil.intToStr(PSD.BLEND_IDIV),
+ PSDUtil.intToStr(PSD.BLEND_LBRN), PSDUtil.intToStr(PSD.BLEND_DKCL), PSDUtil.intToStr(PSD.BLEND_LITE),
+ PSDUtil.intToStr(PSD.BLEND_SCRN), PSDUtil.intToStr(PSD.BLEND_DIV), PSDUtil.intToStr(PSD.BLEND_LDDG),
+ PSDUtil.intToStr(PSD.BLEND_LGCL), PSDUtil.intToStr(PSD.BLEND_OVER), PSDUtil.intToStr(PSD.BLEND_SLIT),
+ PSDUtil.intToStr(PSD.BLEND_HLIT), PSDUtil.intToStr(PSD.BLEND_VLIT), PSDUtil.intToStr(PSD.BLEND_LLIT),
+ PSDUtil.intToStr(PSD.BLEND_PLIT), PSDUtil.intToStr(PSD.BLEND_HMIX), PSDUtil.intToStr(PSD.BLEND_DIFF),
+ PSDUtil.intToStr(PSD.BLEND_SMUD), PSDUtil.intToStr(PSD.BLEND_FSUB), PSDUtil.intToStr(PSD.BLEND_FDIV),
+ PSDUtil.intToStr(PSD.BLEND_HUE), PSDUtil.intToStr(PSD.BLEND_SAT), PSDUtil.intToStr(PSD.BLEND_COLR),
+ PSDUtil.intToStr(PSD.BLEND_LUM)
+ );
+
/**
* Private constructor.
*
@@ -214,14 +228,36 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
// TODO: Incorporate XMP metadata here somehow (or treat as opaque bytes?)
addObjectValue("XMP", Document.class, true, null);
- // TODO: Layers
- //addElement("ChannelSourceDestinationRange", "LayerSomething", CHILD_POLICY_CHOICE);
+ // root -> Layers
+ addElement("Layers", PSDMetadata.NATIVE_METADATA_FORMAT_NAME, CHILD_POLICY_REPEAT);
- // TODO: Global layer mask info
+ // root -> Layers -> LayerInfo
+ addElement("LayerInfo", "Layers", CHILD_POLICY_REPEAT);
+ addAttribute("LayerInfo", "name", DATATYPE_STRING, false, "");
+ addAttribute("LayerInfo", "top", DATATYPE_INTEGER, false, "0");
+ addAttribute("LayerInfo", "left", DATATYPE_INTEGER, false, "0");
+ addAttribute("LayerInfo", "bottom", DATATYPE_INTEGER, false, "0");
+ addAttribute("LayerInfo", "right", DATATYPE_INTEGER, false, "0");
+ addAttribute("LayerInfo", "blendmode", DATATYPE_STRING, false, PSDUtil.intToStr(PSD.BLEND_NORM), PSD_BLEND_MODES);
+ addAttribute("LayerInfo", "opacity", DATATYPE_INTEGER, false, "0");
+ addAttribute("LayerInfo", "clipping", DATATYPE_STRING, false, "base", Arrays.asList("base", "non-base"));
+ addAttribute("LayerInfo", "flags", DATATYPE_INTEGER, false, "0");
+
+ // Redundant (derived from flags)
+ addAttribute("LayerInfo", "transparencyProtected", DATATYPE_BOOLEAN, false, "false");
+ addAttribute("LayerInfo", "visible", DATATYPE_BOOLEAN, false, "false");
+ addAttribute("LayerInfo", "obsolete", DATATYPE_BOOLEAN, false, "false"); // Spec doesn't say if the flag is obsolete, or if this means the layer is obsolete...?
+ addAttribute("LayerInfo", "pixelDataIrrelevant", DATATYPE_BOOLEAN, false, "false");
+
+
+ // root -> GlobalLayerMask
+ addElement("GlobalLayerMask", PSDMetadata.NATIVE_METADATA_FORMAT_NAME, CHILD_POLICY_EMPTY);
+ addAttribute("GlobalLayerMask", "colorSpace", DATATYPE_INTEGER, false, "0");
+ addAttribute("GlobalLayerMask", "colors", DATATYPE_INTEGER, false, 4, 4);
+ addAttribute("GlobalLayerMask", "opacity", DATATYPE_INTEGER, false, "0");
+ addAttribute("GlobalLayerMask", "kind", DATATYPE_STRING, false, "layer", Arrays.asList("selected", "protected", "layer"));
}
-
-
@Override
public boolean canNodeAppear(final String elementName, final ImageTypeSpecifier imageType) {
// TODO: PSDColorData and PaletteEntry only for indexed color model
diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java
index 55f0c840..7945f837 100644
--- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java
+++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUtil.java
@@ -48,13 +48,13 @@ import java.util.zip.ZipInputStream;
*/
final class PSDUtil {
// TODO: Duplicated code from IFF plugin, move to some common util?
- static String intToStr(int pChunkId) {
+ static String intToStr(int value) {
return new String(
new byte[]{
- (byte) ((pChunkId & 0xff000000) >> 24),
- (byte) ((pChunkId & 0x00ff0000) >> 16),
- (byte) ((pChunkId & 0x0000ff00) >> 8),
- (byte) ((pChunkId & 0x000000ff))
+ (byte) ((value & 0xff000000) >> 24),
+ (byte) ((value & 0x00ff0000) >> 16),
+ (byte) ((value & 0x0000ff00) >> 8),
+ (byte) ((value & 0x000000ff))
}
);
}
@@ -73,7 +73,7 @@ final class PSDUtil {
return StringUtil.decode(bytes, 0, bytes.length, "ASCII");
}
- // TODO: Proably also useful for PICT reader, move to some common util?
+ // TODO: Probably also useful for PICT reader, move to some common util?
static String readUnicodeString(final DataInput pInput) throws IOException {
int length = pInput.readInt();
@@ -92,7 +92,6 @@ final class PSDUtil {
}
static DataInputStream createZipStream(final ImageInputStream pInput, long pLength) {
- //return new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(pInput, pLength), new InflateDecoder()));
return new DataInputStream(new ZipInputStream(IIOUtil.createStreamAdapter(pInput, pLength)));
}