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 1160c297..da7cf425 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 @@ -708,4 +708,5 @@ interface PSD extends com.twelvemonkeys.imageio.metadata.psd.PSD { int PxSD = 'P' << 24 | 'x' << 16 | 'S' << 8 | 'D'; int luni = 'l' << 24 | 'u' << 16 | 'n' << 8 | 'i'; int lyid = 'l' << 24 | 'y' << 16 | 'i' << 8 | 'd'; + int lsct = 'l' << 24 | 's' << 16 | 'c' << 8 | 't'; } 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 61c22027..6b2a43e2 100644 --- 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 @@ -947,8 +947,42 @@ public final class PSDImageReader extends ImageReaderBase { if (pParseData && metadata.layerInfo == null) { PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layerCount)]; + + Stack> groupStack = new Stack<>(); + List groupedLayerInfo = null; + 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); 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 ccc0559a..f24a8fa4 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 @@ -57,6 +57,12 @@ final class PSDLayerInfo { private String unicodeLayerName; private int layerId; + Integer groupLayerId; + Boolean group = false; + Boolean sectionDivider = false; + + SectionDividerSetting sectionDividerSettingType; + PSDLayerInfo(final boolean largeFormat, final ImageInputStream pInput) throws IOException { top = pInput.readInt(); left = pInput.readInt(); @@ -64,7 +70,7 @@ final class PSDLayerInfo { right = pInput.readInt(); int channels = pInput.readUnsignedShort(); - + channelInfo = new PSDChannelInfo[channels]; for (int i = 0; i < channels; i++) { short channelId = pInput.readShort(); @@ -132,6 +138,7 @@ final class PSDLayerInfo { // System.out.println("key: " + PSDUtil.intToStr(resourceKey)); // System.out.println("length: " + resourceLength); + switch (resourceKey) { case PSD.luni: unicodeLayerName = PSDUtil.readUnicodeString(pInput); @@ -144,7 +151,10 @@ final class PSDLayerInfo { } layerId = pInput.readInt(); break; - + case PSD.lsct: + sectionDividerSettingType = SectionDividerSetting.valueOf(pInput.readInt()); + pInput.skipBytes(resourceLength - 4); + break; default: // TODO: Parse more data... pInput.skipBytes(resourceLength); @@ -191,4 +201,22 @@ final class PSDLayerInfo { builder.append("]"); 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; + } + } } 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 25d54980..f57a7c16 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 @@ -387,12 +387,22 @@ public final class PSDMetadata extends AbstractMetadata { node.setAttribute("left", String.valueOf(psdLayerInfo.left)); node.setAttribute("bottom", String.valueOf(psdLayerInfo.bottom)); 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("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.group)){ + node.setAttribute("group", String.valueOf(psdLayerInfo.group)); + } + + if ((psdLayerInfo.sectionDivider)) { + node.setAttribute("sectionDivider", String.valueOf(psdLayerInfo.sectionDivider)); + } + if ((psdLayerInfo.blendMode.flags & 0x01) != 0) { node.setAttribute("transparencyProtected", "true"); } 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 cf6a9fee..03b82fbe 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 @@ -247,6 +247,11 @@ public final class PSDMetadataFormat extends IIOMetadataFormatImpl { 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"); + 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) addAttribute("LayerInfo", "transparencyProtected", DATATYPE_BOOLEAN, false, "false"); diff --git a/imageio/imageio-psd/src/test/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderTest.java b/imageio/imageio-psd/src/test/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderTest.java index c21ca4e4..d2f5edd8 100755 --- a/imageio/imageio-psd/src/test/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderTest.java +++ b/imageio/imageio-psd/src/test/java/com/twelvemonkeys/imageio/plugins/psd/PSDImageReaderTest.java @@ -532,4 +532,51 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest 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 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); + + } + } } \ No newline at end of file diff --git a/imageio/imageio-psd/src/test/resources/psd/layer_group_32bit5x5.psd b/imageio/imageio-psd/src/test/resources/psd/layer_group_32bit5x5.psd new file mode 100644 index 00000000..97ce355f Binary files /dev/null and b/imageio/imageio-psd/src/test/resources/psd/layer_group_32bit5x5.psd differ