diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java index 2d3ec3f6..f474cd36 100755 --- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java +++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDAlphaChannelInfo.java @@ -49,7 +49,7 @@ final class PSDAlphaChannelInfo extends PSDImageResource { @Override protected void readData(final ImageInputStream pInput) throws IOException { - names = new ArrayList(); + names = new ArrayList<>(); long left = size; while (left > 0) { diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java index cdd31def..8892a6a8 100755 --- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java +++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDHeader.java @@ -40,8 +40,8 @@ import java.io.IOException; * @version $Id: PSDHeader.java,v 1.0 Apr 29, 2008 5:18:22 PM haraldk Exp$ */ final class PSDHeader { - static final int PSD_MAX_SIZE = 30000; - static final int PSB_MAX_SIZE = 300000; + private static final int PSD_MAX_SIZE = 30000; + private static final int PSB_MAX_SIZE = 300000; // The header is 26 bytes in length and is structured as follows: // // typedef struct _PSD_HEADER @@ -87,8 +87,8 @@ final class PSDHeader { pInput.readFully(reserved); // We don't really care channels = pInput.readShort(); - if (channels <= 0) { - throw new IIOException(String.format("Unsupported number of channels: %d", channels)); + if (channels < 1 || channels > 56) { + throw new IIOException(String.format("Unsupported number of channels for PSD: %d", channels)); } height = pInput.readInt(); // Rows 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 1357cd51..0b50a536 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 @@ -170,23 +170,21 @@ public final class PSDImageReader extends ImageReaderBase { cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); } - if (header.channels == 1 && header.bits == 8) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0}, new int[] {0}, DataBuffer.TYPE_BYTE, false, false); - } - else if (header.channels == 2 && header.bits == 8) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_BYTE, true, false); - } - else if (header.channels == 1 && header.bits == 16) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0}, new int[] {0}, DataBuffer.TYPE_USHORT, false, false); - } - else if (header.channels == 2 && header.bits == 16) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_USHORT, true, false); - } - else if (header.channels == 1 && header.bits == 32) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0}, new int[] {0}, DataBuffer.TYPE_INT, false, false); - } - else if (header.channels == 2 && header.bits == 32) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_INT, true, false); + if (header.channels >= 1) { + switch (header.bits) { + case 8: + return metadata.hasAlpha() && header.channels > 1 + ? ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_BYTE, true, false) + : ImageTypeSpecifiers.createBanded(cs, new int[] {0}, new int[] {0}, DataBuffer.TYPE_BYTE, false, false); + case 16: + return metadata.hasAlpha() && header.channels > 1 + ? ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_USHORT, true, false) + : ImageTypeSpecifiers.createBanded(cs, new int[] {0}, new int[] {0}, DataBuffer.TYPE_USHORT, false, false); + case 32: + return metadata.hasAlpha() && header.channels > 1 + ? ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1}, new int[] {0, 0}, DataBuffer.TYPE_INT, true, false) + : ImageTypeSpecifiers.createBanded(cs, new int[] {0}, new int[] {0}, DataBuffer.TYPE_INT, false, false); + } } throw new IIOException(String.format("Unsupported channel count/bit depth for Gray Scale PSD: %d channels/%d bits", header.channels, header.bits)); @@ -197,23 +195,22 @@ public final class PSDImageReader extends ImageReaderBase { cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); } - if (header.channels == 3 && header.bits == 8) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2}, new int[] {0, 0, 0}, DataBuffer.TYPE_BYTE, false, false); - } - else if (header.channels >= 4 && header.bits == 8) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_BYTE, true, false); - } - else if (header.channels == 3 && header.bits == 16) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2}, new int[] {0, 0, 0}, DataBuffer.TYPE_USHORT, false, false); - } - else if (header.channels >= 4 && header.bits == 16) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_USHORT, true, false); - } - else if (header.channels == 3 && header.bits == 32) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2}, new int[] {0, 0, 0}, DataBuffer.TYPE_INT, false, false); - } - else if (header.channels >= 4 && header.bits == 32) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_INT, true, false); + if (header.channels >= 3) { + switch (header.bits) { + case 8: + return metadata.hasAlpha() && header.channels > 3 + ? ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_BYTE, true, false) + : ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2}, new int[] {0, 0, 0}, DataBuffer.TYPE_BYTE, false, false); + case 16: + return metadata.hasAlpha() && header.channels > 3 + ? ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_USHORT, true, false) + : ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2}, new int[] {0, 0, 0}, DataBuffer.TYPE_USHORT, false, false); + case 32: + return metadata.hasAlpha() && header.channels > 3 + ? ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_INT, true, false) + : ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2}, new int[] {0, 0, 0}, DataBuffer.TYPE_INT, false, false); + + } } throw new IIOException(String.format("Unsupported channel count/bit depth for RGB PSD: %d channels/%d bits", header.channels, header.bits)); @@ -224,23 +221,22 @@ public final class PSDImageReader extends ImageReaderBase { cs = ColorSpaces.getColorSpace(ColorSpaces.CS_GENERIC_CMYK); } - if (header.channels == 4 && header.bits == 8) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_BYTE, false, false); - } - else if (header.channels >= 5 && header.bits == 8) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, DataBuffer.TYPE_BYTE, true, false); - } - else if (header.channels == 4 && header.bits == 16) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_USHORT, false, false); - } - else if (header.channels >= 5 && header.bits == 16) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, DataBuffer.TYPE_USHORT, true, false); - } - else if (header.channels == 4 && header.bits == 32) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_INT, false, false); - } - else if (header.channels >= 5 && header.bits == 32) { - return ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, DataBuffer.TYPE_INT, true, false); + if (header.channels >= 4) { + switch (header.bits) { + case 8: + return metadata.hasAlpha() && header.channels > 4 + ? ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, DataBuffer.TYPE_BYTE, true, false) + : ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_BYTE, false, false); + case 16: + return metadata.hasAlpha() && header.channels > 4 + ? ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, DataBuffer.TYPE_USHORT, true, false) + : ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_USHORT, false, false); + case 32: + return metadata.hasAlpha() && header.channels > 4 + ? ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3, 4}, new int[] {0, 0, 0, 0, 0}, DataBuffer.TYPE_INT, true, false) + : ImageTypeSpecifiers.createBanded(cs, new int[] {0, 1, 2, 3}, new int[] {0, 0, 0, 0}, DataBuffer.TYPE_INT, false, false); + + } } throw new IIOException(String.format("Unsupported channel count/bit depth for CMYK PSD: %d channels/%d bits", header.channels, header.bits)); @@ -465,19 +461,19 @@ public final class PSDImageReader extends ImageReaderBase { switch (header.bits) { case 1: byte[] row1 = ((DataBufferByte) rowRaster.getDataBuffer()).getData(); - read1bitChannel(c, header.channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row1, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, pCompression == PSD.COMPRESSION_RLE); + read1bitChannel(c, channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row1, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, pCompression == PSD.COMPRESSION_RLE); break; case 8: byte[] row8 = ((DataBufferByte) rowRaster.getDataBuffer()).getData(); - read8bitChannel(c, header.channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row8, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE); + read8bitChannel(c, channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row8, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE); break; case 16: short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData(); - read16bitChannel(c, header.channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row16, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE); + read16bitChannel(c, channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row16, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE); break; case 32: int[] row32 = ((DataBufferInt) rowRaster.getDataBuffer()).getData(); - read32bitChannel(c, header.channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row32, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE); + read32bitChannel(c, channels, destRaster.getDataBuffer(), interleavedBands, bandOffset, pSourceCM, row32, pSource, pDest, pXSub, pYSub, header.width, header.height, pByteCounts, c * header.height, pCompression == PSD.COMPRESSION_RLE); break; default: throw new IIOException(String.format("Unsupported PSD bit depth: %s", header.bits)); @@ -494,8 +490,8 @@ public final class PSDImageReader extends ImageReaderBase { } // NOTE: ColorSpace uses Object.equals(), so we rely on using same instances! - if (!pSourceCM.getColorSpace().equals(destination.getColorModel().getColorSpace())) { - convertToDestinationCS(pSourceCM, destination.getColorModel(), destRaster); + if (!pSourceCM.getColorSpace().equals(destCM.getColorSpace())) { + convertToDestinationCS(pSourceCM, destCM, destRaster); } } @@ -505,8 +501,8 @@ public final class PSDImageReader extends ImageReaderBase { // Color conversion from embedded color space, to destination color space WritableRaster alphaMaskedRaster = destinationCM.hasAlpha() ? raster.createWritableChild(0, 0, raster.getWidth(), raster.getHeight(), - raster.getMinX(), raster.getMinY(), - createBandList(sourceCM.getColorSpace().getNumComponents())) + raster.getMinX(), raster.getMinY(), + createBandList(sourceCM.getColorSpace().getNumComponents())) : raster; new ColorConvertOp(sourceCM.getColorSpace(), destinationCM.getColorSpace(), null) @@ -890,34 +886,32 @@ public final class PSDImageReader extends ImageReaderBase { private void readImageResources(final boolean pParseData) throws IOException { readHeader(); - if (pParseData || metadata.layerAndMaskInfoStart == 0) { + if (pParseData && metadata.imageResources == null || metadata.layerAndMaskInfoStart == 0) { imageInput.seek(metadata.imageResourcesStart); long imageResourcesLength = imageInput.readUnsignedInt(); - if (pParseData && imageResourcesLength > 0) { - if (metadata.imageResources == null) { - metadata.imageResources = new ArrayList<>(); - long expectedEnd = imageInput.getStreamPosition() + imageResourcesLength; + if (pParseData && metadata.imageResources == null && imageResourcesLength > 0) { + metadata.imageResources = new ArrayList<>(); + long expectedEnd = imageInput.getStreamPosition() + imageResourcesLength; - while (imageInput.getStreamPosition() < expectedEnd) { - PSDImageResource resource = PSDImageResource.read(imageInput); - metadata.imageResources.add(resource); - } - - if (DEBUG) { - System.out.println("imageResources: " + metadata.imageResources); - } - - if (imageInput.getStreamPosition() != expectedEnd) { - throw new IIOException("Corrupt PSD document"); // ..or maybe just a bug in the reader.. ;-) - } + while (imageInput.getStreamPosition() < expectedEnd) { + PSDImageResource resource = PSDImageResource.read(imageInput); + metadata.imageResources.add(resource); } - // TODO: We should now be able to flush input -// imageInput.flushBefore(metadata.imageResourcesStart + imageResourcesLength + 4); + if (DEBUG) { + System.out.println("imageResources: " + metadata.imageResources); + } + + if (imageInput.getStreamPosition() != expectedEnd) { + throw new IIOException("Corrupt PSD document"); // ..or maybe just a bug in the reader.. ;-) + } } + // TODO: We should now be able to flush input +// imageInput.flushBefore(metadata.imageResourcesStart + imageResourcesLength + 4); + metadata.layerAndMaskInfoStart = metadata.imageResourcesStart + imageResourcesLength + 4; // + 4 for the length field itself } } @@ -927,7 +921,7 @@ public final class PSDImageReader extends ImageReaderBase { private void readLayerAndMaskInfo(final boolean pParseData) throws IOException { readImageResources(false); - if (pParseData || metadata.imageDataStart == 0) { + if (pParseData && (metadata.layerInfo == null || metadata.globalLayerMask == null) || metadata.imageDataStart == 0) { imageInput.seek(metadata.layerAndMaskInfoStart); long layerAndMaskInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt(); @@ -937,55 +931,62 @@ public final class PSDImageReader extends ImageReaderBase { // is alo not as per spec, as layer count should be included if there's a layer info // block, so minimum size should be either 0 or 14 (or 16 if multiple of 4 for PSB))... - if (pParseData && layerAndMaskInfoLength > 0) { + if (layerAndMaskInfoLength > 0) { long pos = imageInput.getStreamPosition(); - if (metadata.layerInfo == null) { - long layerInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt(); + //if (metadata.layerInfo == null) { + long layerInfoLength = header.largeFormat ? imageInput.readLong() : imageInput.readUnsignedInt(); - if (layerInfoLength > 0) { - // "Layer count. If it is a negative number, its absolute value is the number of - // layers and the first alpha channel contains the transparency data for the - // merged result." - int layerCount = imageInput.readShort(); + if (layerInfoLength > 0) { + // "Layer count. If it is a negative number, its absolute value is the number of + // layers and the first alpha channel contains the transparency data for the + // merged result." + int layerCount = imageInput.readShort(); + metadata.layerCount = layerCount; + if (pParseData && metadata.layerInfo == null) { PSDLayerInfo[] layerInfos = new PSDLayerInfo[Math.abs(layerCount)]; for (int i = 0; i < layerInfos.length; i++) { layerInfos[i] = new PSDLayerInfo(header.largeFormat, imageInput); } + metadata.layerInfo = Arrays.asList(layerInfos); metadata.layersStart = imageInput.getStreamPosition(); - long read = imageInput.getStreamPosition() - pos; - - long diff = layerInfoLength - (read - (header.largeFormat - ? 8 - : 4)); // - 4 for the layerInfoLength field itself - - imageInput.skipBytes(diff); - } - else { - metadata.layerInfo = Collections.emptyList(); } - // Global LayerMaskInfo (18 bytes or more..?) - // 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad) - long globalLayerMaskInfoLength = imageInput.readUnsignedInt(); // NOTE: Not long for PSB! + long read = imageInput.getStreamPosition() - pos; + long diff = layerInfoLength - (read - (header.largeFormat ? 8 : 4)); // - 8 or 4 for the layerInfoLength field itself - if (globalLayerMaskInfoLength > 0) { + imageInput.skipBytes(diff); + } + else { + metadata.layerInfo = Collections.emptyList(); + } + + + // Global LayerMaskInfo (18 bytes or more..?) + // 4 (length), 2 (colorSpace), 8 (4 * 2 byte color components), 2 (opacity %), 1 (kind), variable (pad) + long globalLayerMaskInfoLength = imageInput.readUnsignedInt(); // NOTE: Not long for PSB! + + if (globalLayerMaskInfoLength > 0) { + if (pParseData && metadata.globalLayerMask == null) { metadata.globalLayerMask = new PSDGlobalLayerMask(imageInput, globalLayerMaskInfoLength); } + // TODO: Else skip? + } - // TODO: Parse "Additional layer information" + // TODO: Parse "Additional layer information" - // TODO: We should now be able to flush input + // TODO: We should now be able to flush input // imageInput.seek(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4)); // imageInput.flushBefore(metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4)); - if (DEBUG) { - System.out.println("layerInfo: " + metadata.layerInfo); - } + if (DEBUG) { + System.out.println("layerInfo: " + metadata.layerInfo); + System.out.println("globalLayerMask: " + metadata.globalLayerMask); } + //} } metadata.imageDataStart = metadata.layerAndMaskInfoStart + layerAndMaskInfoLength + (header.largeFormat ? 8 : 4); @@ -1003,8 +1004,6 @@ public final class PSDImageReader extends ImageReaderBase { } PSDLayerInfo layerInfo = metadata.layerInfo.get(layerIndex); -// final int width = layerInfo.right - layerInfo.left; -// final int height = layerInfo.bottom - layerInfo.top; // Even if raw/imageType has no alpha, the layers may still have alpha... ImageTypeSpecifier imageType = getRawImageTypeForLayer(layerIndex); @@ -1035,8 +1034,7 @@ public final class PSDImageReader extends ImageReaderBase { // Skip layer if we can't read it // channelId // -1 = transparency mask; -2 = user supplied layer mask, -3 = real user supplied layer mask (when both a user mask and a vector mask are present) - if (width <= 0 || height <= 0 || channelInfo.channelId < -1 || - (compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) { // TODO: ZIP Compressions! + if (channelInfo.channelId < -1 || (compression != PSD.COMPRESSION_NONE && compression != PSD.COMPRESSION_RLE)) { // TODO: ZIP Compressions! imageInput.skipBytes(channelInfo.length - 2); } else { @@ -1080,17 +1078,17 @@ public final class PSDImageReader extends ImageReaderBase { case 8: byte[] row8 = ((DataBufferByte) rowRaster.getDataBuffer()).getData(); read8bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row8, area, area, xsub, - ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE); + ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE); break; case 16: short[] row16 = ((DataBufferUShort) rowRaster.getDataBuffer()).getData(); read16bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row16, area, area, xsub, - ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE); + ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE); break; case 32: int[] row32 = ((DataBufferInt) rowRaster.getDataBuffer()).getData(); read32bitChannel(c, imageType.getNumBands(), raster.getDataBuffer(), interleavedBands, bandOffset, sourceCM, row32, area, area, xsub, - ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE); + ysub, width, height, byteCounts, 0, compression == PSD.COMPRESSION_RLE); break; default: throw new IIOException(String.format("Unknown PSD bit depth: %s", header.bits)); @@ -1147,26 +1145,13 @@ public final class PSDImageReader extends ImageReaderBase { /// Layer support - @Override - protected void checkBounds(final int index) throws IOException { - // Avoid parsing layer stuff, if we just want to read the composite data - if (index == 0) { - assertInput(); - readLayerAndMaskInfo(false); - } - else { - super.checkBounds(index); - } - } - @Override public int getNumImages(boolean allowSearch) throws IOException { // NOTE: Spec says this method should throw IllegalStateException if allowSearch && isSeekForwardOnly() // But that makes no sense for a format (like PSD) that does not need to search, right? + readLayerAndMaskInfo(false); - readLayerAndMaskInfo(true); - - return metadata.layerInfo != null ? metadata.layerInfo.size() + 1 : 1; // TODO: Only plus one, if "has real merged data"? + return metadata.getLayerCount() + 1; // TODO: Only plus one, if "has real merged data"? } /// Metadata support @@ -1374,7 +1359,7 @@ public final class PSDImageReader extends ImageReaderBase { param.setSourceSubsampling(subsampleFactor, subsampleFactor, 0, 0); } - // param.setDestinationType(imageReader.getRawImageType(0)); + // param.setDestinationType(imageReader.getRawImageType(0)); BufferedImage image = imageReader.read(0, param); System.out.println("read time: " + (System.currentTimeMillis() - start)); 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 2e2e4901..2f2ba8be 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 @@ -64,6 +64,7 @@ public final class PSDMetadata extends AbstractMetadata { PSDGlobalLayerMask globalLayerMask; List layerInfo; + int layerCount; long imageResourcesStart; long layerAndMaskInfoStart; long layersStart; @@ -170,7 +171,7 @@ public final class PSDMetadata extends AbstractMetadata { node = new IIOMetadataNode("DisplayInfo"); node.setAttribute("colorSpace", DISPLAY_INFO_CS[displayInfo.colorSpace]); - + StringBuilder builder = new StringBuilder(); for (short color : displayInfo.colors) { @@ -307,7 +308,7 @@ public final class PSDMetadata extends AbstractMetadata { } else if (imageResource instanceof PSDXMPData) { // TODO: Revise/rethink this... Would it be possible to parse XMP as IIOMetadataNodes? Or is that just stupid... - // Or maybe use the Directory approach used by IPTC and EXIF.. + // Or maybe use the Directory approach used by IPTC and EXIF.. PSDXMPData xmp = (PSDXMPData) imageResource; node = new IIOMetadataNode("DirectoryResource"); @@ -627,7 +628,7 @@ public final class PSDMetadata extends AbstractMetadata { // TODO: If no PSDResolutionInfo, this might still be available in the EXIF data... Iterator resolutionInfos = getResources(PSDResolutionInfo.class); - if (!resolutionInfos.hasNext()) { + if (resolutionInfos.hasNext()) { PSDResolutionInfo resolutionInfo = resolutionInfos.next(); node = new IIOMetadataNode("HorizontalPixelSize"); @@ -643,7 +644,7 @@ public final class PSDMetadata extends AbstractMetadata { } private static float asMM(final short unit, final float resolution) { - // Unit: 1 -> pixels per inch, 2 -> pixels pr cm + // Unit: 1 -> pixels per inch, 2 -> pixels pr cm return (unit == 1 ? 25.4f : 10) / resolution; } @@ -795,12 +796,15 @@ public final class PSDMetadata extends AbstractMetadata { return transparencyNode; } - private boolean hasAlpha() { - return header.mode == PSD.COLOR_MODE_RGB && header.channels > 3 || - header.mode == PSD.COLOR_MODE_CMYK & header.channels > 4; + boolean hasAlpha() { + return layerCount < 0; } - Iterator getResources(final Class resourceType) { + int getLayerCount() { + return Math.abs(layerCount); + } + + private Iterator getResources(final Class resourceType) { // NOTE: The cast here is wrong, strictly speaking, but it does not matter... @SuppressWarnings({"unchecked"}) Iterator iterator = (Iterator) imageResources.iterator(); @@ -812,7 +816,7 @@ public final class PSDMetadata extends AbstractMetadata { }); } - Iterator getResources(final int... resourceTypes) { + private Iterator getResources(final int... resourceTypes) { Iterator iterator = imageResources.iterator(); return new FilterIterator<>(iterator, new FilterIterator.Filter() { diff --git a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUnicodeAlphaNames.java b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUnicodeAlphaNames.java index 23705172..6a6bf90c 100644 --- a/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUnicodeAlphaNames.java +++ b/imageio/imageio-psd/src/main/java/com/twelvemonkeys/imageio/plugins/psd/PSDUnicodeAlphaNames.java @@ -49,7 +49,7 @@ final class PSDUnicodeAlphaNames extends PSDImageResource { @Override protected void readData(final ImageInputStream pInput) throws IOException { - names = new ArrayList(); + names = new ArrayList<>(); long left = size; while (left > 0) { @@ -58,4 +58,11 @@ final class PSDUnicodeAlphaNames extends PSDImageResource { left -= name.length() * 2 + 4; } } + + @Override + public String toString() { + StringBuilder builder = toStringBuilder(); + builder.append(", alpha channels: ").append(names).append("]"); + return builder.toString(); + } } 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 e3baf3a5..fdeaf75e 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 @@ -83,7 +83,7 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest // 3 channel, RGB, 32 bit samples new TestData(getClassLoaderResource("/psd/32bit5x5.psd"), new Dimension(5, 5)), // 3 channel, RGB, 8 bit samples ("Large Document Format" aka PSB) - new TestData(getClassLoaderResource("/psd/test_original.psb"), new Dimension(710, 512)), + new TestData(getClassLoaderResource("/psb/test_original.psb"), new Dimension(710, 512)), // From http://telegraphics.com.au/svn/psdparse/trunk/psd/ new TestData(getClassLoaderResource("/psd/adobehq.psd"), new Dimension(341, 512)), new TestData(getClassLoaderResource("/psd/adobehq_ind.psd"), new Dimension(341, 512)), @@ -93,7 +93,10 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest new TestData(getClassLoaderResource("/psd/adobehq-5.5.psd"), new Dimension(341, 512)), new TestData(getClassLoaderResource("/psd/adobehq-7.0.psd"), new Dimension(341, 512)), // From https://github.com/kmike/psd-tools/tree/master/tests/psd_files - new TestData(getClassLoaderResource("/psd/masks2.psd"), new Dimension(640, 1136)) // TODO: Test read layers! + new TestData(getClassLoaderResource("/psd/masks2.psd"), new Dimension(640, 1136)), + // RGB, multiple alpha channels, no transparency + new TestData(getClassLoaderResource("/psd/rgb-multichannel-no-transparency.psd"), new Dimension(100, 100)), + new TestData(getClassLoaderResource("/psb/rgb-multichannel-no-transparency.psb"), new Dimension(100, 100)) // TODO: Need uncompressed PSD // TODO: Need more recent ZIP compressed PSD files from CS2/CS3+ ); @@ -453,6 +456,34 @@ public class PSDImageReaderTest extends ImageReaderAbstractTest } } + @Test + public void testMultiChannelNoTransparencyPSD() throws IOException { + PSDImageReader imageReader = createReader(); + + // The following PSD is RGB, has 4 channels (1 alpha/auxillary channel), but should be treated as opaque + try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psd/rgb-multichannel-no-transparency.psd"))) { + imageReader.setInput(stream); + + BufferedImage image = imageReader.read(0); + + assertEquals(Transparency.OPAQUE, image.getTransparency()); + } + } + + @Test + public void testMultiChannelNoTransparencyPSB() throws IOException { + PSDImageReader imageReader = createReader(); + + // The following PSB is RGB, has 4 channels (1 alpha/auxillary channel), but should be treated as opaque + try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psb/rgb-multichannel-no-transparency.psb"))) { + imageReader.setInput(stream); + + BufferedImage image = imageReader.read(0); + + assertEquals(Transparency.OPAQUE, image.getTransparency()); + } + } + @Test public void testReadUnicodeLayerName() throws IOException { PSDImageReader imageReader = createReader(); diff --git a/imageio/imageio-psd/src/test/resources/psb/rgb-multichannel-no-transparency.psb b/imageio/imageio-psd/src/test/resources/psb/rgb-multichannel-no-transparency.psb new file mode 100755 index 00000000..d097b54e Binary files /dev/null and b/imageio/imageio-psd/src/test/resources/psb/rgb-multichannel-no-transparency.psb differ diff --git a/imageio/imageio-psd/src/test/resources/psd/test_original.psb b/imageio/imageio-psd/src/test/resources/psb/test_original.psb similarity index 100% rename from imageio/imageio-psd/src/test/resources/psd/test_original.psb rename to imageio/imageio-psd/src/test/resources/psb/test_original.psb diff --git a/imageio/imageio-psd/src/test/resources/psd/rgb-multichannel-no-transparency.psd b/imageio/imageio-psd/src/test/resources/psd/rgb-multichannel-no-transparency.psd new file mode 100755 index 00000000..e424b2c1 Binary files /dev/null and b/imageio/imageio-psd/src/test/resources/psd/rgb-multichannel-no-transparency.psd differ