Support Group Layer in psd (#648)

(cherry picked from commit dc0bdcbd5bb2506ef69262126fb6e5830d5e9e4c)
This commit is contained in:
Jack Yun 2021-12-27 21:39:39 +09:00 committed by Harald Kuhr
parent 6c27ec6b30
commit c0b2769e3b
7 changed files with 128 additions and 3 deletions

View File

@ -708,4 +708,5 @@ interface PSD extends com.twelvemonkeys.imageio.metadata.psd.PSD {
int PxSD = 'P' << 24 | 'x' << 16 | 'S' << 8 | 'D'; int PxSD = 'P' << 24 | 'x' << 16 | 'S' << 8 | 'D';
int luni = 'l' << 24 | 'u' << 16 | 'n' << 8 | 'i'; int luni = 'l' << 24 | 'u' << 16 | 'n' << 8 | 'i';
int lyid = 'l' << 24 | 'y' << 16 | 'i' << 8 | 'd'; int lyid = 'l' << 24 | 'y' << 16 | 'i' << 8 | 'd';
int lsct = 'l' << 24 | 's' << 16 | 'c' << 8 | 't';
} }

View File

@ -947,8 +947,42 @@ public final class PSDImageReader extends ImageReaderBase {
if (pParseData && metadata.layerInfo == null) { if (pParseData && metadata.layerInfo == null) {
PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layerCount)]; PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layerCount)];
Stack<List<PSDLayerInfo>> groupStack = new Stack<>();
List<PSDLayerInfo> groupedLayerInfo = null;
for (int i = 0; i < layerInfos.length; i++) { for (int i = 0; i < layerInfos.length; i++) {
layerInfos[i] = new PSDLayerInfo(header.largeFormat, imageInput); PSDLayerInfo layerInfo = new PSDLayerInfo(header.largeFormat, imageInput);
layerInfos[i] = layerInfo;
if (layerInfo.sectionDividerSettingType == PSDLayerInfo.SectionDividerSetting.BOUNDING_SECTION_DIVIDER) {
if (groupedLayerInfo == null) {
groupedLayerInfo = new LinkedList<>();
groupStack.add(groupedLayerInfo);
} else {
groupStack.add(groupedLayerInfo);
groupedLayerInfo = new LinkedList<>();
}
layerInfo.sectionDivider = true;
}
else if (layerInfo.sectionDividerSettingType == PSDLayerInfo.SectionDividerSetting.OPEN_FOLDER ||
layerInfo.sectionDividerSettingType == PSDLayerInfo.SectionDividerSetting.CLOSED_FOLDER) {
// can't happen but for defense logic
if (groupedLayerInfo == null) {
continue;
}
for (PSDLayerInfo info : groupedLayerInfo) {
info.groupLayerId = layerInfo.getLayerId();
}
groupedLayerInfo = groupStack.pop();
groupedLayerInfo.add(layerInfo);
layerInfo.group = true;
}
else {
if (groupedLayerInfo != null) {
groupedLayerInfo.add(layerInfo);
}
}
} }
metadata.layerInfo = Arrays.asList(layerInfos); metadata.layerInfo = Arrays.asList(layerInfos);

View File

@ -57,6 +57,12 @@ final class PSDLayerInfo {
private String unicodeLayerName; private String unicodeLayerName;
private int layerId; private int layerId;
Integer groupLayerId;
Boolean group = false;
Boolean sectionDivider = false;
SectionDividerSetting sectionDividerSettingType;
PSDLayerInfo(final boolean largeFormat, final 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();
@ -64,7 +70,7 @@ final class PSDLayerInfo {
right = pInput.readInt(); right = pInput.readInt();
int channels = pInput.readUnsignedShort(); int channels = pInput.readUnsignedShort();
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();
@ -132,6 +138,7 @@ final class PSDLayerInfo {
// System.out.println("key: " + PSDUtil.intToStr(resourceKey)); // System.out.println("key: " + PSDUtil.intToStr(resourceKey));
// System.out.println("length: " + resourceLength); // System.out.println("length: " + resourceLength);
switch (resourceKey) { switch (resourceKey) {
case PSD.luni: case PSD.luni:
unicodeLayerName = PSDUtil.readUnicodeString(pInput); unicodeLayerName = PSDUtil.readUnicodeString(pInput);
@ -144,7 +151,10 @@ final class PSDLayerInfo {
} }
layerId = pInput.readInt(); layerId = pInput.readInt();
break; break;
case PSD.lsct:
sectionDividerSettingType = SectionDividerSetting.valueOf(pInput.readInt());
pInput.skipBytes(resourceLength - 4);
break;
default: default:
// TODO: Parse more data... // TODO: Parse more data...
pInput.skipBytes(resourceLength); pInput.skipBytes(resourceLength);
@ -191,4 +201,22 @@ final class PSDLayerInfo {
builder.append("]"); builder.append("]");
return builder.toString(); return builder.toString();
} }
public enum SectionDividerSetting {
LAYER(0), OPEN_FOLDER(1), CLOSED_FOLDER(2), BOUNDING_SECTION_DIVIDER(3);
SectionDividerSetting(int value) { this.value = value;}
private final int value;
public int value() { return value; }
public static SectionDividerSetting valueOf(int value){
for(SectionDividerSetting rt : SectionDividerSetting.values()){
if(rt.value == value){
return rt;
}
}
return null;
}
}
} }

View File

@ -387,12 +387,22 @@ public final class PSDMetadata extends AbstractMetadata {
node.setAttribute("left", String.valueOf(psdLayerInfo.left)); node.setAttribute("left", String.valueOf(psdLayerInfo.left));
node.setAttribute("bottom", String.valueOf(psdLayerInfo.bottom)); node.setAttribute("bottom", String.valueOf(psdLayerInfo.bottom));
node.setAttribute("right", String.valueOf(psdLayerInfo.right)); node.setAttribute("right", String.valueOf(psdLayerInfo.right));
node.setAttribute("layerId", String.valueOf(psdLayerInfo.getLayerId()));
node.setAttribute("groupLayerId", String.valueOf(psdLayerInfo.groupLayerId));
node.setAttribute("blendMode", PSDUtil.intToStr(psdLayerInfo.blendMode.blendMode)); node.setAttribute("blendMode", PSDUtil.intToStr(psdLayerInfo.blendMode.blendMode));
node.setAttribute("opacity", String.valueOf(psdLayerInfo.blendMode.opacity)); // 0-255 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("clipping", getClippingValue(psdLayerInfo.blendMode.clipping)); // Enum: 0: Base, 1: Non-base, n: unknown
node.setAttribute("flags", String.valueOf(psdLayerInfo.blendMode.flags)); node.setAttribute("flags", String.valueOf(psdLayerInfo.blendMode.flags));
if ((psdLayerInfo.group)){
node.setAttribute("group", String.valueOf(psdLayerInfo.group));
}
if ((psdLayerInfo.sectionDivider)) {
node.setAttribute("sectionDivider", String.valueOf(psdLayerInfo.sectionDivider));
}
if ((psdLayerInfo.blendMode.flags & 0x01) != 0) { if ((psdLayerInfo.blendMode.flags & 0x01) != 0) {
node.setAttribute("transparencyProtected", "true"); node.setAttribute("transparencyProtected", "true");
} }

View File

@ -247,6 +247,11 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl {
addAttribute("LayerInfo", "opacity", DATATYPE_INTEGER, false, "0"); addAttribute("LayerInfo", "opacity", DATATYPE_INTEGER, false, "0");
addAttribute("LayerInfo", "clipping", DATATYPE_STRING, false, "base", Arrays.asList("base", "non-base")); addAttribute("LayerInfo", "clipping", DATATYPE_STRING, false, "base", Arrays.asList("base", "non-base"));
addAttribute("LayerInfo", "flags", DATATYPE_INTEGER, false, "0"); addAttribute("LayerInfo", "flags", DATATYPE_INTEGER, false, "0");
addAttribute("LayerInfo", "layerId", DATATYPE_INTEGER, false, null);
addAttribute("LayerInfo", "groupLayerId", DATATYPE_INTEGER, false, null);
addAttribute("LayerInfo", "group", DATATYPE_BOOLEAN, false, "false");
addAttribute("LayerInfo", "sectionDivider", DATATYPE_INTEGER, false, "false");
// Redundant (derived from flags) // Redundant (derived from flags)
addAttribute("LayerInfo", "transparencyProtected", DATATYPE_BOOLEAN, false, "false"); addAttribute("LayerInfo", "transparencyProtected", DATATYPE_BOOLEAN, false, "false");

View File

@ -532,4 +532,51 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader>
assertEquals("If_The_Layer_Name_Is_Really_Long_Oh_No_What_Do_I_Do", ((IIOMetadataNode) layerInfo.item(0)).getAttribute("name")); assertEquals("If_The_Layer_Name_Is_Really_Long_Oh_No_What_Do_I_Do", ((IIOMetadataNode) layerInfo.item(0)).getAttribute("name"));
} }
} }
@Test
public void testGroupLayerRead() throws IOException {
PSDImageReader imageReader = createReader();
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psd/layer_group_32bit5x5.psd"))) {
imageReader.setInput(stream);
IIOMetadata metadata = imageReader.getImageMetadata(0);
List<PSDLayerInfo> layerInfos = ((PSDMetadata) metadata).layerInfo;
assertEquals(layerInfos.size(), 5);
PSDLayerInfo groupedLayer = null;
PSDLayerInfo groupLayer = null;
PSDLayerInfo sectionDividerLayer = null;
for (PSDLayerInfo layerInfo : layerInfos) {
if (layerInfo.getLayerId() == 5) {
groupedLayer = layerInfo;
}
if (layerInfo.getLayerId() == 6) {
groupLayer = layerInfo;
}
if (layerInfo.getLayerId() == 7) {
sectionDividerLayer = layerInfo;
}
}
assertNotNull(groupedLayer);
assertEquals((int) groupedLayer.groupLayerId, 6);
assertEquals(groupedLayer.group, false);
assertEquals(groupedLayer.sectionDivider, false);
assertNotNull(groupLayer);
assertNull(groupLayer.groupLayerId);
assertEquals(groupLayer.group, true);
assertEquals(groupLayer.sectionDivider, false);
assertNotNull(sectionDividerLayer);
assertNull(sectionDividerLayer.groupLayerId);
assertEquals(sectionDividerLayer.group, false);
assertEquals(sectionDividerLayer.sectionDivider, true);
}
}
} }