TMI-98: Fix PSDMetadata to include layer info + Cleanup and other minor fixes.

This commit is contained in:
Harald Kuhr 2015-02-26 21:51:48 +01:00
parent 1cbfb1a074
commit c3cafc63d8
13 changed files with 284 additions and 72 deletions

View File

@ -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...
}

View File

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

View File

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

View File

@ -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 <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">Adobe Photoshop File Format Summary<a>
*/
interface PSD {
/** PSD 2+ Native format (.PSD) identifier "8BPS" */
int SIGNATURE_8BPS = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'S';
// TODO: Is this ever used??! Spec says (and sample files uses) 8BPS + version == 2 for PSB...
/** PSD 5+ Large Document Format (.PSB) identifier "8BPB" */
int SIGNATURE_8BPB = ('8' << 24) + ('B' << 16) + ('P' << 8) + 'B';
// 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;
}

View File

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

View File

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

View File

@ -54,6 +54,7 @@ import java.util.List;
/**
* ImageReader for Adobe Photoshop Document (PSD) format.
*
* @see <a href="http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/">Adobe Photoshop File Formats Specification<a>
* @see <a href="http://www.fileformat.info/format/psd/egff.htm">Adobe Photoshop File Format Summary<a>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @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);

View File

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

View File

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

View File

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

View File

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

View File

@ -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<String> 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.
* <p/>
@ -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

View File

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