mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-04 20:15:28 -04:00
TMI-98: Fix PSDMetadata to include layer info + Cleanup and other minor fixes.
This commit is contained in:
parent
1cbfb1a074
commit
c3cafc63d8
@ -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...
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user