JPEG Exif rotation in metadata + support
@@ -31,8 +31,9 @@
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
import com.twelvemonkeys.lang.StringUtil;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
import org.hamcrest.core.IsInstanceOf;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mockito.internal.matchers.GreaterThan;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -182,23 +183,27 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
public void testICCProfileCMYKClassOutputColors() throws IOException {
|
||||
// Make sure ICC profile with class output isn't converted to too bright values
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/cmyk-sample-custom-icc-bright.jpg")));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(800, 800, 64, 8));
|
||||
param.setSourceSubsampling(8, 8, 2, 2);
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/cmyk-sample-custom-icc-bright.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
assertNotNull(image);
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(800, 800, 64, 8));
|
||||
param.setSourceSubsampling(8, 8, 2, 2);
|
||||
|
||||
byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
|
||||
byte[] expectedData = {34, 37, 34, 47, 47, 44, 22, 26, 28, 23, 26, 28, 20, 23, 26, 20, 22, 25, 22, 25, 27, 18, 21, 24};
|
||||
BufferedImage image = reader.read(0, param);
|
||||
assertNotNull(image);
|
||||
|
||||
assertEquals(expectedData.length, data.length);
|
||||
byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
|
||||
byte[] expectedData = {34, 37, 34, 47, 47, 44, 22, 26, 28, 23, 26, 28, 20, 23, 26, 20, 22, 25, 22, 25, 27, 18, 21, 24};
|
||||
|
||||
assertJPEGPixelsEqual(expectedData, data, 0);
|
||||
assertEquals(expectedData.length, data.length);
|
||||
|
||||
reader.dispose();
|
||||
assertJPEGPixelsEqual(expectedData, data, 0);
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertJPEGPixelsEqual(byte[] expected, byte[] actual, int actualOffset) {
|
||||
@@ -211,38 +216,44 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
public void testICCDuplicateSequence() throws IOException {
|
||||
// Variation of the above, file contains multiple ICC chunks, with all counts and sequence numbers == 1
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/invalid-icc-duplicate-sequence-numbers-rgb-internal-kodak-srgb-jfif.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/invalid-icc-duplicate-sequence-numbers-rgb-internal-kodak-srgb-jfif.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(345, reader.getWidth(0));
|
||||
assertEquals(540, reader.getHeight(0));
|
||||
assertEquals(345, reader.getWidth(0));
|
||||
assertEquals(540, reader.getHeight(0));
|
||||
|
||||
BufferedImage image = reader.read(0);
|
||||
BufferedImage image = reader.read(0);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(345, image.getWidth());
|
||||
assertEquals(540, image.getHeight());
|
||||
|
||||
reader.dispose();
|
||||
assertNotNull(image);
|
||||
assertEquals(345, image.getWidth());
|
||||
assertEquals(540, image.getHeight());
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testICCDuplicateSequenceZeroBased() throws IOException {
|
||||
// File contains multiple ICC chunks, with all counts and sequence numbers == 0
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/invalid-icc-duplicate-sequence-numbers-rgb-xerox-dc250-heavyweight-1-progressive-jfif.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/invalid-icc-duplicate-sequence-numbers-rgb-xerox-dc250-heavyweight-1-progressive-jfif.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(3874, reader.getWidth(0));
|
||||
assertEquals(5480, reader.getHeight(0));
|
||||
assertEquals(3874, reader.getWidth(0));
|
||||
assertEquals(5480, reader.getHeight(0));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(0, 0, 3874, 16)); // Save some memory
|
||||
BufferedImage image = reader.read(0, param);
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(0, 0, 3874, 16)); // Save some memory
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(3874, image.getWidth());
|
||||
assertEquals(16, image.getHeight());
|
||||
|
||||
reader.dispose();
|
||||
assertNotNull(image);
|
||||
assertEquals(3874, image.getWidth());
|
||||
assertEquals(16, image.getHeight());
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -251,20 +262,23 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
// Profile should have been about 550 000 bytes, split into multiple chunks. Written by GIMP 2.6.11
|
||||
// See: https://bugzilla.redhat.com/show_bug.cgi?id=695246
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/cmm-exception-invalid-icc-profile-data.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/cmm-exception-invalid-icc-profile-data.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(1993, reader.getWidth(0));
|
||||
assertEquals(1038, reader.getHeight(0));
|
||||
assertEquals(1993, reader.getWidth(0));
|
||||
assertEquals(1038, reader.getHeight(0));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(reader.getWidth(0), 8));
|
||||
BufferedImage image = reader.read(0, param);
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(reader.getWidth(0), 8));
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(1993, image.getWidth());
|
||||
assertEquals(8, image.getHeight());
|
||||
|
||||
reader.dispose();
|
||||
assertNotNull(image);
|
||||
assertEquals(1993, image.getWidth());
|
||||
assertEquals(8, image.getHeight());
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -272,19 +286,23 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
// File contains CMYK ICC profile ("Coated FOGRA27 (ISO 12647-2:2004)"), but image data is 3 channel YCC/RGB
|
||||
// JFIF 1.1 with unknown origin.
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/cco-illegalargument-rgb-coated-fogra27.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/cco-illegalargument-rgb-coated-fogra27.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(281, reader.getWidth(0));
|
||||
assertEquals(449, reader.getHeight(0));
|
||||
assertEquals(281, reader.getWidth(0));
|
||||
assertEquals(449, reader.getHeight(0));
|
||||
|
||||
BufferedImage image = reader.read(0);
|
||||
BufferedImage image = reader.read(0);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(281, image.getWidth());
|
||||
assertEquals(449, image.getHeight());
|
||||
assertNotNull(image);
|
||||
assertEquals(281, image.getWidth());
|
||||
assertEquals(449, image.getHeight());
|
||||
|
||||
// TODO: Need to test colors!
|
||||
reader.dispose();
|
||||
// TODO: Need to test colors!
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -293,22 +311,27 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
// but image data is plain 3 channel YCC/RGB.
|
||||
// EXIF/TIFF metadata says Software: "Microsoft Windows Photo Gallery 6.0.6001.18000"...
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/no-image-types-rgb-us-web-coated-v2-ms-photogallery-exif.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/no-image-types-rgb-us-web-coated-v2-ms-photogallery-exif.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(1743, reader.getWidth(0));
|
||||
assertEquals(2551, reader.getHeight(0));
|
||||
assertEquals(1743, reader.getWidth(0));
|
||||
assertEquals(2551, reader.getHeight(0));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(0, 0, 1743, 16)); // Save some memory
|
||||
BufferedImage image = reader.read(0, param);
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(0, 0, 1743, 16)); // Save some memory
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(1743, image.getWidth());
|
||||
assertEquals(16, image.getHeight());
|
||||
assertNotNull(image);
|
||||
assertEquals(1743, image.getWidth());
|
||||
assertEquals(16, image.getHeight());
|
||||
|
||||
// TODO: Need to test colors!
|
||||
// TODO: Need to test colors!
|
||||
|
||||
assertTrue(reader.hasThumbnails(0)); // Should not blow up!
|
||||
assertTrue(reader.hasThumbnails(0)); // Should not blow up!
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -316,107 +339,131 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
// File contains JFIF (!), RGB ICC profile AND Adobe App14 specifying unknown conversion,
|
||||
// but image data is 4 channel CMYK (from SOF0 channel Ids 'C', 'M', 'Y', 'K').
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/jfif-cmyk-invalid-icc-profile-srgb.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/jfif-cmyk-invalid-icc-profile-srgb.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(493, reader.getWidth(0));
|
||||
assertEquals(500, reader.getHeight(0));
|
||||
assertEquals(493, reader.getWidth(0));
|
||||
assertEquals(500, reader.getHeight(0));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(0, 0, 493, 16)); // Save some memory
|
||||
BufferedImage image = reader.read(0, param);
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(0, 0, 493, 16)); // Save some memory
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(493, image.getWidth());
|
||||
assertEquals(16, image.getHeight());
|
||||
assertNotNull(image);
|
||||
assertEquals(493, image.getWidth());
|
||||
assertEquals(16, image.getHeight());
|
||||
|
||||
// TODO: Need to test colors!
|
||||
// TODO: Need to test colors!
|
||||
|
||||
assertFalse(reader.hasThumbnails(0)); // Should not blow up!
|
||||
assertFalse(reader.hasThumbnails(0)); // Should not blow up!
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWarningEmbeddedColorProfileInvalidIgnored() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/warning-embedded-color-profile-invalid-ignored-cmyk.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/warning-embedded-color-profile-invalid-ignored-cmyk.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(183, reader.getWidth(0));
|
||||
assertEquals(283, reader.getHeight(0));
|
||||
assertEquals(183, reader.getWidth(0));
|
||||
assertEquals(283, reader.getHeight(0));
|
||||
|
||||
BufferedImage image = reader.read(0);
|
||||
BufferedImage image = reader.read(0);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(183, image.getWidth());
|
||||
assertEquals(283, image.getHeight());
|
||||
assertNotNull(image);
|
||||
assertEquals(183, image.getWidth());
|
||||
assertEquals(283, image.getHeight());
|
||||
|
||||
// TODO: Need to test colors!
|
||||
// TODO: Need to test colors!
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEOFSOSSegment() throws IOException {
|
||||
// Regression...
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/eof-sos-segment-bug.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/eof-sos-segment-bug.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(266, reader.getWidth(0));
|
||||
assertEquals(400, reader.getHeight(0));
|
||||
assertEquals(266, reader.getWidth(0));
|
||||
assertEquals(400, reader.getHeight(0));
|
||||
|
||||
BufferedImage image = reader.read(0);
|
||||
BufferedImage image = reader.read(0);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(266, image.getWidth());
|
||||
assertEquals(400, image.getHeight());
|
||||
assertNotNull(image);
|
||||
assertEquals(266, image.getWidth());
|
||||
assertEquals(400, image.getHeight());
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidICCSingleChunkBadSequence() throws IOException {
|
||||
// Regression
|
||||
// Single segment ICC profile, with chunk index/count == 0
|
||||
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/invalid-icc-single-chunk-bad-sequence-number.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/invalid-icc-single-chunk-bad-sequence-number.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(1772, reader.getWidth(0));
|
||||
assertEquals(2126, reader.getHeight(0));
|
||||
assertEquals(1772, reader.getWidth(0));
|
||||
assertEquals(2126, reader.getHeight(0));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(reader.getWidth(0), 8));
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(reader.getWidth(0), 8));
|
||||
|
||||
IIOReadWarningListener warningListener = mock(IIOReadWarningListener.class);
|
||||
reader.addIIOReadWarningListener(warningListener);
|
||||
IIOReadWarningListener warningListener = mock(IIOReadWarningListener.class);
|
||||
reader.addIIOReadWarningListener(warningListener);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(1772, image.getWidth());
|
||||
assertEquals(8, image.getHeight());
|
||||
assertNotNull(image);
|
||||
assertEquals(1772, image.getWidth());
|
||||
assertEquals(8, image.getHeight());
|
||||
|
||||
verify(warningListener, atLeast(1)).warningOccurred(eq(reader), anyString());
|
||||
verify(warningListener, atLeast(1)).warningOccurred(eq(reader), anyString());
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testYCbCrNotSubsampledNonstandardChannelIndexes() throws IOException {
|
||||
// Regression: Make sure 3 channel, non-subsampled JFIF, defaults to YCbCr, even if unstandard channel indexes
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/jfif-ycbcr-no-subsampling-intel.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/jfif-ycbcr-no-subsampling-intel.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(600, reader.getWidth(0));
|
||||
assertEquals(600, reader.getHeight(0));
|
||||
assertEquals(600, reader.getWidth(0));
|
||||
assertEquals(600, reader.getHeight(0));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(8, 8));
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setSourceRegion(new Rectangle(8, 8));
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(8, image.getWidth());
|
||||
assertEquals(8, image.getHeight());
|
||||
assertNotNull(image);
|
||||
assertEquals(8, image.getWidth());
|
||||
assertEquals(8, image.getHeight());
|
||||
|
||||
// QnD test: Make sure all pixels are white (if treated as RGB, they will be pink-ish)
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
assertEquals(0xffffff, image.getRGB(x, y) & 0xffffff);
|
||||
// QnD test: Make sure all pixels are white (if treated as RGB, they will be pink-ish)
|
||||
for (int y = 0; y < image.getHeight(); y++) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
assertEquals(0xffffff, image.getRGB(x, y) & 0xffffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -424,36 +471,87 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
// Special case, throws exception below without special treatment
|
||||
// java.awt.color.CMMException: General CMM error517
|
||||
JPEGImageReader reader = createReader();
|
||||
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/cmm-exception-corbis-rgb.jpg")));
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/jpeg/cmm-exception-corbis-rgb.jpg"))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
assertEquals(512, reader.getWidth(0));
|
||||
assertEquals(384, reader.getHeight(0));
|
||||
assertEquals(512, reader.getWidth(0));
|
||||
assertEquals(384, reader.getHeight(0));
|
||||
|
||||
BufferedImage image = reader.read(0);
|
||||
BufferedImage image = reader.read(0);
|
||||
|
||||
assertNotNull(image);
|
||||
assertEquals(512, image.getWidth());
|
||||
assertEquals(384, image.getHeight());
|
||||
|
||||
reader.dispose();
|
||||
}
|
||||
|
||||
@Ignore("Known issue in com.sun...JPEGMetadata")
|
||||
@Test
|
||||
public void testStandardMetadataColorSpaceTypeRGBForYCbCr() {
|
||||
// These reports RGB in standard metadata, while the data is really YCbCr.
|
||||
// Exif files are always YCbCr AFAIK.
|
||||
fail("/jpeg/exif-jpeg-thumbnail-sony-dsc-p150-inverted-colors.jpg");
|
||||
fail("/jpeg/exif-pspro-13-inverted-colors.jpg");
|
||||
// Not Exif, but same issue: SOF comp ids are JFIF standard 1-3 and
|
||||
// *should* be interpreted as YCbCr but isn't.
|
||||
// Possible fix for this, is to insert a fake JFIF segment, as this image
|
||||
// conforms to the JFIF spec (but it won't work for the Exif samples)
|
||||
fail("/jpeg/no-jfif-ycbcr.jpg");
|
||||
assertNotNull(image);
|
||||
assertEquals(512, image.getWidth());
|
||||
assertEquals(384, image.getHeight());
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrokenReadRasterAfterGetMetadataException() throws IOException {
|
||||
public void testStandardMetadataColorSpaceTypeRGBForYCbCr() throws IOException {
|
||||
List<TestData> ycbcr = Arrays.asList(
|
||||
// This reports RGB in standard metadata, while the data is really YCbCr.
|
||||
// Exif files are always YCbCr AFAIK.
|
||||
new TestData(getClassLoaderResource("/jpeg/exif-jpeg-thumbnail-sony-dsc-p150-inverted-colors.jpg"), new Dimension(2437, 1662)),
|
||||
// Not Exif, but same issue: SOF comp ids are JFIF standard 1-3 and
|
||||
// *should* be interpreted as YCbCr but isn't.
|
||||
// Possible fix for this, is to insert a fake JFIF segment, as this image
|
||||
// conforms to the JFIF spec (but it won't work for the Exif samples)
|
||||
new TestData(getClassLoaderResource("/jpeg/no-jfif-ycbcr.jpg"), new Dimension(310, 206))
|
||||
);
|
||||
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
try {
|
||||
for (TestData broken : ycbcr) {
|
||||
reader.setInput(broken.getInputStream());
|
||||
|
||||
IIOMetadata metadata = reader.getImageMetadata(0);
|
||||
|
||||
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||
NodeList colorSpaceTypes = root.getElementsByTagName("ColorSpaceType");
|
||||
assertEquals(1, colorSpaceTypes.getLength());
|
||||
IIOMetadataNode csType = (IIOMetadataNode) colorSpaceTypes.item(0);
|
||||
assertEquals("YCbCr", csType.getAttribute("name"));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetExifOrientationFromMetadata() throws IOException {
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
// TODO: Find better sample data. Should have an uppercase F ;-)
|
||||
// Test all 9 mutations + missing Exif
|
||||
List<String> expectedOrientations = Arrays.asList("Normal", "Normal", "FlipH", "Rotate180", "FlipV", "FlipVRotate90", "Rotate270", "FlipHRotate90", "Rotate90");
|
||||
try {
|
||||
for (int i = 0; i < 9; i++) {
|
||||
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource(String.format("/exif/Landscape_%d.jpg", i)))) {
|
||||
reader.setInput(stream);
|
||||
|
||||
IIOMetadata metadata = reader.getImageMetadata(0);
|
||||
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||
|
||||
NodeList orientationNodes = root.getElementsByTagName("ImageOrientation");
|
||||
assertEquals(1, orientationNodes.getLength());
|
||||
|
||||
IIOMetadataNode orientationNode = (IIOMetadataNode) orientationNodes.item(0);
|
||||
String orientationValue = orientationNode.getAttribute("value");
|
||||
assertEquals(expectedOrientations.get(i), orientationValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrokenReadRasterAfterGetMetadataException() {
|
||||
// See issue #107, from PDFBox team
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
@@ -497,7 +595,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
// TODO: Consider wrapping the delegate in JPEGImageReader with methods that don't throw
|
||||
// runtime exceptions, and instead throw IIOException?
|
||||
@Test
|
||||
public void testBrokenGetRawImageType() throws IOException {
|
||||
public void testBrokenGetRawImageType() {
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
try {
|
||||
@@ -523,7 +621,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
}
|
||||
|
||||
@Test(timeout = 200)
|
||||
public void testBrokenGetRawImageTypeIgnoreMetadata() throws IOException {
|
||||
public void testBrokenGetRawImageTypeIgnoreMetadata() {
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
try {
|
||||
@@ -549,7 +647,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrokenGetImageTypes() throws IOException {
|
||||
public void testBrokenGetImageTypes() {
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
try {
|
||||
@@ -575,7 +673,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
}
|
||||
|
||||
@Test(timeout = 200)
|
||||
public void testBrokenGetImageTypesIgnoreMetadata() throws IOException {
|
||||
public void testBrokenGetImageTypesIgnoreMetadata() {
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
try {
|
||||
@@ -601,7 +699,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrokenRead() throws IOException {
|
||||
public void testBrokenRead() {
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
try {
|
||||
@@ -627,7 +725,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrokenGetDimensions() throws IOException {
|
||||
public void testBrokenGetDimensions() {
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
try {
|
||||
@@ -656,7 +754,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrokenGetImageMetadata() throws IOException {
|
||||
public void testBrokenGetImageMetadata() {
|
||||
JPEGImageReader reader = createReader();
|
||||
|
||||
try {
|
||||
@@ -1284,6 +1382,9 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
|
||||
// Assume that the aspect ratio is 1 if both x/y density is 0.
|
||||
IIOMetadataNode tree = (IIOMetadataNode) imageMetadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||
|
||||
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(imageMetadata.getNativeMetadataFormatName()), false);
|
||||
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(tree, false);
|
||||
NodeList dimensions = tree.getElementsByTagName("Dimension");
|
||||
assertEquals(1, dimensions.getLength());
|
||||
assertEquals("PixelAspectRatio", dimensions.item(0).getFirstChild().getNodeName());
|
||||
@@ -1321,7 +1422,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
|
||||
NodeList markerSequences = iioTree.getElementsByTagName("markerSequence");
|
||||
assertTrue(markerSequences.getLength() == 1 || markerSequences.getLength() == 2); // In case of JPEG encoded thumbnail, there will be 2
|
||||
IIOMetadataNode markerSequence = (IIOMetadataNode) markerSequences.item(0);
|
||||
IIOMetadataNode markerSequence = (IIOMetadataNode) markerSequences.item(markerSequences.getLength() - 1); // The last will be the "main" image
|
||||
assertNotNull(markerSequence);
|
||||
assertThat(markerSequence.getChildNodes().getLength(), new GreaterThan<>(0));
|
||||
|
||||
@@ -1379,6 +1480,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
|
||||
for (TestData testData : getTestData()) {
|
||||
reader.setInput(testData.getInputStream());
|
||||
assert referenceReader != null;
|
||||
referenceReader.setInput(testData.getInputStream());
|
||||
|
||||
for (int i = 0; i < reader.getNumImages(true); i++) {
|
||||
@@ -1393,6 +1495,8 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
Node referenceTree = reference.getAsTree(formatName);
|
||||
Node actualTree = metadata.getAsTree(formatName);
|
||||
|
||||
// new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(referenceTree, false);
|
||||
// System.out.println("--------");
|
||||
// new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(actualTree, false);
|
||||
assertTreesEquals(String.format("Metadata differs for %s image %s ", testData, i), referenceTree, actualTree);
|
||||
}
|
||||
@@ -1432,8 +1536,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
}
|
||||
|
||||
if (expectedTree == null) {
|
||||
assertNull(actualTree);
|
||||
return;
|
||||
fail("Expected tree is null, actual tree is non-null");
|
||||
}
|
||||
|
||||
assertEquals(String.format("%s: Node names differ", message), expectedTree.getNodeName(), actualTree.getNodeName());
|
||||
@@ -1443,7 +1546,14 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
assertEquals(String.format("%s: Number of attributes for <%s> differ", message, expectedTree.getNodeName()), expectedAttributes.getLength(), actualAttributes.getLength());
|
||||
for (int i = 0; i < expectedAttributes.getLength(); i++) {
|
||||
Node item = expectedAttributes.item(i);
|
||||
assertEquals(String.format("%s: \"%s\" attribute for <%s> differ", message, item.getNodeName(), expectedTree.getNodeName()), item.getNodeValue(), actualAttributes.getNamedItem(item.getNodeName()).getNodeValue());
|
||||
String nodeValue = item.getNodeValue();
|
||||
|
||||
// NOTE: com.sun...JPEGMetadata javax_imageio_1.0 format bug: Uses "normal" instead of "Normal" ImageOrientation
|
||||
if ("ImageOrientation".equals(expectedTree.getNodeName()) && "value".equals(item.getNodeName())) {
|
||||
nodeValue = StringUtil.capitalize(nodeValue);
|
||||
}
|
||||
|
||||
assertEquals(String.format("%s: \"%s\" attribute for <%s> differ", message, item.getNodeName(), expectedTree.getNodeName()), nodeValue, actualAttributes.getNamedItem(item.getNodeName()).getNodeValue());
|
||||
}
|
||||
|
||||
// Test for equal user objects.
|
||||
@@ -1460,6 +1570,11 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
|
||||
}
|
||||
}
|
||||
|
||||
if ("markerSequence".equals(expectedTree.getNodeName()) && "JFIFthumbJPEG".equals(expectedTree.getParentNode().getNodeName())) {
|
||||
// TODO: We haven't implemented this yet
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort nodes to make sure that sequence of equally named tags does not matter
|
||||
List<IIOMetadataNode> expectedChildren = sortNodes(expectedTree.getChildNodes());
|
||||
List<IIOMetadataNode> actualChildren = sortNodes(actualTree.getChildNodes());
|
||||
|
||||
19
imageio/imageio-jpeg/src/test/resources/exif/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2010 Dave Perrett, http://recursive-design.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
BIN
imageio/imageio-jpeg/src/test/resources/exif/Landscape_0.jpg
Normal file
|
After Width: | Height: | Size: 342 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Landscape_1.jpg
Normal file
|
After Width: | Height: | Size: 339 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Landscape_2.jpg
Normal file
|
After Width: | Height: | Size: 341 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Landscape_3.jpg
Normal file
|
After Width: | Height: | Size: 341 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Landscape_4.jpg
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Landscape_5.jpg
Normal file
|
After Width: | Height: | Size: 343 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Landscape_6.jpg
Normal file
|
After Width: | Height: | Size: 344 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Landscape_7.jpg
Normal file
|
After Width: | Height: | Size: 344 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Landscape_8.jpg
Normal file
|
After Width: | Height: | Size: 344 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Portrait_0.jpg
Normal file
|
After Width: | Height: | Size: 243 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Portrait_1.jpg
Normal file
|
After Width: | Height: | Size: 240 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Portrait_2.jpg
Normal file
|
After Width: | Height: | Size: 241 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Portrait_3.jpg
Normal file
|
After Width: | Height: | Size: 242 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Portrait_4.jpg
Normal file
|
After Width: | Height: | Size: 241 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Portrait_5.jpg
Normal file
|
After Width: | Height: | Size: 246 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Portrait_6.jpg
Normal file
|
After Width: | Height: | Size: 246 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Portrait_7.jpg
Normal file
|
After Width: | Height: | Size: 245 KiB |
BIN
imageio/imageio-jpeg/src/test/resources/exif/Portrait_8.jpg
Normal file
|
After Width: | Height: | Size: 246 KiB |
82
imageio/imageio-jpeg/src/test/resources/exif/README.markdown
Normal file
@@ -0,0 +1,82 @@
|
||||
EXIF Orientation-flag example images
|
||||
====================================
|
||||
|
||||
Example images using each of the EXIF orientation flags (0-to-8), in both landscape and portrait orientations.
|
||||
|
||||
[See here](http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/) for more information.
|
||||
|
||||
|
||||
Generating your own images
|
||||
--------------------------
|
||||
|
||||
If you would like to generate test images based on your own photos, you can use the `generate.rb` script included in the `generator` folder.
|
||||
|
||||
The instructions below assume you are running on macOS - if not, you will need to install the Ghostscript fonts (`brew install gs`) some other way.
|
||||
|
||||
To install the dependencies:
|
||||
|
||||
```
|
||||
> brew install gs exiftool imagemagick@6
|
||||
> cd generator
|
||||
> gem install bundler
|
||||
> bundle install
|
||||
```
|
||||
|
||||
To generate test images:
|
||||
|
||||
```
|
||||
> cd generator
|
||||
> ./generate.rb path/to/image.jpg
|
||||
```
|
||||
|
||||
This will create images `image_0.jpg` through to `image_8.jpg`.
|
||||
|
||||
|
||||
Re-generating sample images
|
||||
---------------------------
|
||||
|
||||
Simply run `make` to regenerate the included sample images. This will download random portrait and landscape orientation images from [unsplash.com](https://unsplash.com/) and generate sample images for each of them.
|
||||
|
||||
Generating these images depends on having the generator dependencies installed - see the *Generating your own images* section for instructions on installing dependencies.
|
||||
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
* The sample landscape image is by [Pierre Bouillot](https://unsplash.com/photos/v15iOM6pWgI).
|
||||
* The sample portrait image is by [John Salvino](https://unsplash.com/photos/1PPpwrTNkJI).
|
||||
|
||||
|
||||
Change history
|
||||
--------------
|
||||
|
||||
* **Version 2.0.0 (2017-08-05)** : Add a script to generate example images from the command line.
|
||||
* **Version 1.0.2 (2017-03-06)** : Remove Apple Copyrighted ICC profile from orientations 2-8 (thanks @mans0954!).
|
||||
* **Version 1.0.1 (2013-03-10)** : Add MIT license and some contact details.
|
||||
* **Version 1.0.0 (2012-07-28)** : 1.0 release.
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Once you've made your commits:
|
||||
|
||||
1. [Fork](http://help.github.com/fork-a-repo/) exif-orientation-examples
|
||||
2. Create a topic branch - `git checkout -b my_branch`
|
||||
3. Push to your branch - `git push origin my_branch`
|
||||
4. Create a [Pull Request](http://help.github.com/pull-requests/) from your branch
|
||||
5. That's it!
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
Dave Perrett :: hello@daveperrett.com :: [@daveperrett](http://twitter.com/daveperrett)
|
||||
|
||||
|
||||
Copyright
|
||||
---------
|
||||
|
||||
These images are licensed under the [MIT License](http://opensource.org/licenses/MIT).
|
||||
|
||||
Copyright (c) 2010 Dave Perrett. See [License](https://github.com/recurser/exif-orientation-examples/blob/master/LICENSE) for details.
|
||||
1
imageio/imageio-jpeg/src/test/resources/exif/VERSION
Normal file
@@ -0,0 +1 @@
|
||||
2.0.1
|
||||