/* * Copyright (c) 2014, Harald Kuhr * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name "TwelveMonkeys" nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.twelvemonkeys.imageio.plugins.psd; import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest; import com.twelvemonkeys.imageio.util.ProgressListenerBase; import org.junit.Test; import org.w3c.dom.NodeList; import javax.imageio.ImageIO; import javax.imageio.ImageReadParam; import javax.imageio.ImageReader; import javax.imageio.ImageTypeSpecifier; import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadataNode; import javax.imageio.spi.ImageReaderSpi; import javax.imageio.stream.ImageInputStream; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.*; import java.util.List; import static org.junit.Assert.*; /** * PSDImageReaderTest * * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author last modified by $Author: haraldk$ * @version $Id: PSDImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$ */ public class PSDImageReaderTest extends ImageReaderAbstractTest<PSDImageReader> { private static final ImageReaderSpi provider = new PSDImageReaderSpi(); protected List<TestData> getTestData() { return Arrays.asList( // 5 channel, RGB new TestData(getClassLoaderResource("/psd/photoshopping.psd"), new Dimension(300, 225)), // 1 channel, gray, 8 bit samples new TestData(getClassLoaderResource("/psd/buttons.psd"), new Dimension(20, 20)), // 5 channel, CMYK new TestData(getClassLoaderResource("/psd/escenic-liquid-logo.psd"), new Dimension(595, 420)), // 3 channel RGB, "no composite layer" new TestData(getClassLoaderResource("/psd/jugware-icon.psd"), new Dimension(128, 128)), // 3 channel RGB, old data, no layer info/mask new TestData(getClassLoaderResource("/psd/MARBLES.PSD"), new Dimension(1419, 1001)), // 1 channel, indexed color new TestData(getClassLoaderResource("/psd/coral_fish.psd"), new Dimension(800, 800)), // 1 channel, bitmap, 1 bit samples new TestData(getClassLoaderResource("/psd/test_bitmap.psd"), new Dimension(710, 512)), // 1 channel, gray, 16 bit samples new TestData(getClassLoaderResource("/psd/test_gray16.psd"), new Dimension(710, 512)), // 4 channel, CMYK, 16 bit samples new TestData(getClassLoaderResource("/psd/cmyk_16bits.psd"), new Dimension(1000, 275)), // 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)), // 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)), // Contains a shorter than normal PrintFlags chunk new TestData(getClassLoaderResource("/psd/adobehq-2.5.psd"), new Dimension(341, 512)), new TestData(getClassLoaderResource("/psd/adobehq-3.0.psd"), new Dimension(341, 512)), 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! // TODO: Need uncompressed PSD // TODO: Need more recent ZIP compressed PSD files from CS2/CS3+ ); } protected ImageReaderSpi createProvider() { return provider; } @Override protected PSDImageReader createReader() { return new PSDImageReader(provider); } protected Class<PSDImageReader> getReaderClass() { return PSDImageReader.class; } protected List<String> getFormatNames() { return Collections.singletonList("psd"); } protected List<String> getSuffixes() { return Collections.singletonList("psd"); } protected List<String> getMIMETypes() { return Arrays.asList( "image/vnd.adobe.photoshop", "application/vnd.adobe.photoshop", "image/x-psd" ); } @Test public void testSupportsThumbnail() { PSDImageReader imageReader = createReader(); assertTrue(imageReader.readerSupportsThumbnails()); } @Test public void testThumbnailReading() throws IOException { PSDImageReader imageReader = createReader(); try (ImageInputStream stream = getTestData().get(0).getInputStream()) { imageReader.setInput(stream); assertEquals(1, imageReader.getNumThumbnails(0)); BufferedImage thumbnail = imageReader.readThumbnail(0, 0); assertNotNull(thumbnail); assertEquals(128, thumbnail.getWidth()); assertEquals(96, thumbnail.getHeight()); } } @Test public void testThumbnailReadingNoInput() throws IOException { PSDImageReader imageReader = createReader(); try { imageReader.getNumThumbnails(0); fail("Expected IllegalStateException"); } catch (IllegalStateException expected) { assertTrue(expected.getMessage().toLowerCase().contains("input")); } try { imageReader.getThumbnailWidth(0, 0); fail("Expected IllegalStateException"); } catch (IllegalStateException expected) { assertTrue(expected.getMessage().toLowerCase().contains("input")); } try { imageReader.getThumbnailHeight(0, 0); fail("Expected IllegalStateException"); } catch (IllegalStateException expected) { assertTrue(expected.getMessage().toLowerCase().contains("input")); } try { imageReader.readThumbnail(0, 0); fail("Expected IllegalStateException"); } catch (IllegalStateException expected) { assertTrue(expected.getMessage().toLowerCase().contains("input")); } } @Test public void testThumbnailReadingOutOfBounds() throws IOException { PSDImageReader imageReader = createReader(); try (ImageInputStream stream = getTestData().get(0).getInputStream()) { imageReader.setInput(stream); int numImages = imageReader.getNumImages(true); try { imageReader.getNumThumbnails(numImages + 1); fail("Expected IndexOutOfBoundsException"); } catch (IndexOutOfBoundsException expected) { assertTrue(expected.getMessage(), expected.getMessage().toLowerCase().contains("index")); } try { imageReader.getThumbnailWidth(-1, 0); fail("Expected IndexOutOfBoundsException"); } catch (IndexOutOfBoundsException expected) { assertTrue(expected.getMessage(), expected.getMessage().toLowerCase().contains("index")); } try { imageReader.getThumbnailHeight(0, -2); fail("Expected IndexOutOfBoundsException"); } catch (IndexOutOfBoundsException expected) { // Sloppy... assertTrue(expected.getMessage(), expected.getMessage().toLowerCase().contains("-2")); } try { imageReader.readThumbnail(numImages + 99, 42); fail("Expected IndexOutOfBoundsException"); } catch (IndexOutOfBoundsException expected) { assertTrue(expected.getMessage(), expected.getMessage().toLowerCase().contains("index")); } } } @Test public void testThumbnailDimensions() throws IOException { PSDImageReader imageReader = createReader(); try (ImageInputStream stream = getTestData().get(0).getInputStream()) { imageReader.setInput(stream); assertEquals(1, imageReader.getNumThumbnails(0)); assertEquals(128, imageReader.getThumbnailWidth(0, 0)); assertEquals(96, imageReader.getThumbnailHeight(0, 0)); } } @Test public void testThumbnailReadListeners() throws IOException { PSDImageReader imageReader = createReader(); try (ImageInputStream stream = getTestData().get(0).getInputStream()) { imageReader.setInput(stream); final List<Object> seqeunce = new ArrayList<>(); imageReader.addIIOReadProgressListener(new ProgressListenerBase() { private float mLastPercentageDone = 0; @Override public void thumbnailStarted(final ImageReader pSource, final int pImageIndex, final int pThumbnailIndex) { seqeunce.add("started"); } @Override public void thumbnailComplete(final ImageReader pSource) { seqeunce.add("complete"); } @Override public void thumbnailProgress(final ImageReader pSource, final float pPercentageDone) { // Optional assertTrue("Listener invoked out of sequence", seqeunce.size() == 1); assertTrue(pPercentageDone >= mLastPercentageDone); } }); BufferedImage thumbnail = imageReader.readThumbnail(0, 0); assertNotNull(thumbnail); assertEquals("Listeners not invoked", 2, seqeunce.size()); assertEquals("started", seqeunce.get(0)); assertEquals("complete", seqeunce.get(1)); } } @Test public void testReadLayers() throws IOException { PSDImageReader imageReader = createReader(); try (ImageInputStream stream = getTestData().get(3).getInputStream()) { imageReader.setInput(stream); int numImages = imageReader.getNumImages(true); assertEquals(3, numImages); for (int i = 0; i < numImages; i++) { BufferedImage image = imageReader.read(i); assertNotNull(image); // Make sure layers are correct size assertEquals(image.getWidth(), imageReader.getWidth(i)); assertEquals(image.getHeight(), imageReader.getHeight(i)); } } } @Test public void testImageTypesLayers() throws IOException { PSDImageReader imageReader = createReader(); try (ImageInputStream stream = getTestData().get(3).getInputStream()) { imageReader.setInput(stream); int numImages = imageReader.getNumImages(true); for (int i = 0; i < numImages; i++) { ImageTypeSpecifier rawType = imageReader.getRawImageType(i); assertNotNull(rawType); Iterator<ImageTypeSpecifier> types = imageReader.getImageTypes(i); assertNotNull(types); assertTrue(types.hasNext()); boolean found = false; while (types.hasNext()) { ImageTypeSpecifier type = types.next(); if (!found && (rawType == type || rawType.equals(type))) { found = true; } } assertTrue("RAW image type not in type iterator", found); } } } @Test public void testReadLayersExplicitType() throws IOException { PSDImageReader imageReader = createReader(); try (ImageInputStream stream = getTestData().get(3).getInputStream()) { imageReader.setInput(stream); int numImages = imageReader.getNumImages(true); for (int i = 0; i < numImages; i++) { Iterator<ImageTypeSpecifier> types = imageReader.getImageTypes(i); while (types.hasNext()) { ImageTypeSpecifier type = types.next(); ImageReadParam param = imageReader.getDefaultReadParam(); param.setDestinationType(type); BufferedImage image = imageReader.read(i, param); assertEquals(type.getBufferedImageType(), image.getType()); if (type.getBufferedImageType() == 0) { // TODO: If type.getBIT == 0, test more // Compatible color model assertEquals(type.getNumComponents(), image.getColorModel().getNumComponents()); // Same color space assertEquals(type.getColorModel().getColorSpace(), image.getColorModel().getColorSpace()); // Same number of samples assertEquals(type.getNumBands(), image.getSampleModel().getNumBands()); // Same number of bits/sample for (int j = 0; j < type.getNumBands(); j++) { assertEquals(type.getBitsPerBand(j), image.getSampleModel().getSampleSize(j)); } } } } } } @Test public void testReadLayersExplicitDestination() throws IOException { PSDImageReader imageReader = createReader(); try (ImageInputStream stream = getTestData().get(3).getInputStream()) { imageReader.setInput(stream); int numImages = imageReader.getNumImages(true); for (int i = 0; i < numImages; i++) { Iterator<ImageTypeSpecifier> types = imageReader.getImageTypes(i); int width = imageReader.getWidth(i); int height = imageReader.getHeight(i); while (types.hasNext()) { ImageTypeSpecifier type = types.next(); ImageReadParam param = imageReader.getDefaultReadParam(); BufferedImage destination = type.createBufferedImage(width, height); param.setDestination(destination); BufferedImage image = imageReader.read(i, param); assertSame(destination, image); } } } } @Test public void testGrayAlphaLayers() throws IOException { PSDImageReader imageReader = createReader(); // The expected colors for each layer int[] colors = new int[] { -1, // Don't care 0xff000000, 0xffffffff, 0xff737373, 0xff3c3c3c, 0xff656565, 0xffc9c9c9, 0xff979797, 0xff5a5a5a }; try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psd/test_grayscale_boxes.psd"))) { imageReader.setInput(stream); int numImages = imageReader.getNumImages(true); assertEquals(colors.length, numImages); // Skip reading the merged composite image for (int i = 1; i < numImages; i++) { Iterator<ImageTypeSpecifier> types = imageReader.getImageTypes(i); int width = imageReader.getWidth(i); int height = imageReader.getHeight(i); while (types.hasNext()) { ImageTypeSpecifier type = types.next(); ImageReadParam param = imageReader.getDefaultReadParam(); BufferedImage destination = type.createBufferedImage(width, height); param.setDestination(destination); BufferedImage image = imageReader.read(i, param); assertSame(destination, image); // NOTE: Allow some slack, as Java 1.7 and 1.8 color management differs slightly int rgb = image.getRGB(0, 0); assertRGBEquals("Colors differ", colors[i], rgb, 1); } } } } @Test public void testReadUnicodeLayerName() throws IOException { PSDImageReader imageReader = createReader(); try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/psd/long-layer-names.psd"))) { imageReader.setInput(stream); IIOMetadata metadata = imageReader.getImageMetadata(0); IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(PSDMetadata.NATIVE_METADATA_FORMAT_NAME); NodeList layerInfo = root.getElementsByTagName("LayerInfo"); assertEquals(1, layerInfo.getLength()); // Sanity assertEquals("If_The_Layer_Name_Is_Really_Long_Oh_No_What_Do_I_Do", ((IIOMetadataNode) layerInfo.item(0)).getAttribute("name")); } } }