JPEG Exif/thumbnail refactoring pt II.

This commit is contained in:
Harald Kuhr
2021-02-26 18:27:58 +01:00
parent 85fb9e6af3
commit 0286fa4268
9 changed files with 254 additions and 115 deletions
@@ -39,7 +39,6 @@ import java.io.IOException;
import java.net.URL;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
/**
* AbstractThumbnailReaderTest
@@ -53,8 +52,6 @@ public abstract class AbstractThumbnailReaderTest {
IIORegistry.getDefaultInstance().registerServiceProvider(new URLImageInputStreamSpi());
}
protected final JPEGSegmentWarningListener listener = mock(JPEGSegmentWarningListener.class);
protected abstract ThumbnailReader createReader(ImageInputStream stream) throws IOException;
protected final ImageInputStream createStream(final String name) throws IOException {
@@ -30,14 +30,21 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.AbstractCompoundDirectory;
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
import com.twelvemonkeys.imageio.metadata.tiff.IFD;
import com.twelvemonkeys.imageio.metadata.tiff.TIFF;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFEntry;
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
import org.junit.After;
import org.junit.Test;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
@@ -45,6 +52,8 @@ import java.awt.image.BufferedImage;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.*;
@@ -60,6 +69,153 @@ public class EXIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
private final ImageReader thumbnailReader = ImageIO.getImageReadersByFormatName("jpeg").next();
@After
public void tearDown() {
thumbnailReader.dispose();
}
@Test
public void testFromNullSegment() throws IOException {
assertNull(EXIFThumbnail.from(null, null, thumbnailReader));
}
@Test
public void testFromNullIFD() throws IOException {
assertNull(EXIFThumbnail.from(new EXIF(new byte[0]), null, thumbnailReader));
}
@Test
public void testFromEmptyIFD() throws IOException {
assertNull(EXIFThumbnail.from(new EXIF(new byte[0]), new EXIFDirectory(), thumbnailReader));
}
@Test
public void testFromSingleIFD() throws IOException {
assertNull(EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList())), thumbnailReader));
}
@Test(expected = IIOException.class)
public void testFromMissingThumbnail() throws IOException {
EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(Collections.<Entry>emptyList())), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromUnsupportedThumbnailCompression() throws IOException {
List<TIFFEntry> entries = Collections.singletonList(new TIFFEntry(TIFF.TAG_COMPRESSION, 42));
EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromMissingOffsetUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9)
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromMissingWidthUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9)
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromMissingHeightUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16)
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromUnsupportedPhotometricUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9),
new TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, 42)
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromUnsupportedBitsPerSampleUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9),
new TIFFEntry(TIFF.TAG_BITS_PER_SAMPLE, new int[]{5, 6, 5})
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromUnsupportedSamplesPerPixelUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 160),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 90),
new TIFFEntry(TIFF.TAG_SAMPLES_PER_PIXEL, 1)
);
EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromTruncatedUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 160),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 90)
);
EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test
public void testValidUncompressed() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 1),
new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, 0),
new TIFFEntry(TIFF.TAG_IMAGE_WIDTH, 16),
new TIFFEntry(TIFF.TAG_IMAGE_HEIGHT, 9)
);
ThumbnailReader reader = EXIFThumbnail.from(new EXIF(new byte[6 + 16 * 9 * 3]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
assertNotNull(reader);
// Sanity check below
assertEquals(16, reader.getWidth());
assertEquals(9, reader.getHeight());
assertNotNull(reader.read());
}
@Test(expected = IIOException.class)
public void testFromMissingOffsetJPEG() throws IOException {
List<TIFFEntry> entries = Collections.singletonList(new TIFFEntry(TIFF.TAG_COMPRESSION, 6));
EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Test(expected = IIOException.class)
public void testFromTruncatedJPEG() throws IOException {
List<TIFFEntry> entries = Arrays.asList(
new TIFFEntry(TIFF.TAG_COMPRESSION, 6),
new TIFFEntry(TIFF.TAG_JPEG_INTERCHANGE_FORMAT, 0)
);
EXIFThumbnail.from(new EXIF(new byte[42]), new EXIFDirectory(new IFD(Collections.<Entry>emptyList()), new IFD(entries)), thumbnailReader);
}
@Override
protected ThumbnailReader createReader(final ImageInputStream stream) throws IOException {
List<JPEGSegment> segments = JPEGSegmentUtil.readSegments(stream, JPEG.APP1, "Exif");
@@ -74,7 +230,7 @@ public class EXIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
new DataInputStream(data).readFully(exifData);
EXIF exif = new EXIF(exifData);
return EXIFThumbnail.from(exif, (CompoundDirectory) new TIFFReader().read(exif.exifData()), thumbnailReader, listener);
return EXIFThumbnail.from(exif, (CompoundDirectory) new TIFFReader().read(exif.exifData()), thumbnailReader);
}
@Test
@@ -102,4 +258,10 @@ public class EXIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
assertEquals(80, thumbnail.getWidth());
assertEquals(60, thumbnail.getHeight());
}
private static class EXIFDirectory extends AbstractCompoundDirectory {
public EXIFDirectory(IFD... ifds) {
super(Arrays.asList(ifds));
}
}
}
@@ -36,6 +36,7 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
import org.junit.Test;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.DataInputStream;
@@ -43,7 +44,6 @@ import java.io.IOException;
import java.util.List;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* JFIFThumbnailReaderTest
@@ -64,44 +64,34 @@ public class JFIFThumbnailReaderTest extends AbstractThumbnailReaderTest {
JPEGSegment segment = segments.get(0);
return JFIFThumbnail.from(JFIF.read(new DataInputStream(segment.segmentData()), segment.segmentLength()), listener);
return JFIFThumbnail.from(JFIF.read(new DataInputStream(segment.segmentData()), segment.segmentLength()));
}
@Test
public void testFromNull() {
assertNull(JFIFThumbnail.from(null, listener));
verify(listener, never()).warningOccurred(anyString());
public void testFromNull() throws IOException {
assertNull(JFIFThumbnail.from(null));
}
@Test
public void testFromNullThumbnail() {
assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 0, 0, null), listener));
verify(listener, never()).warningOccurred(anyString());
public void testFromNullThumbnail() throws IOException {
assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 0, 0, null)));
}
@Test
public void testFromEmpty() {
assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 0, 0, new byte[0]), listener));
verify(listener, never()).warningOccurred(anyString());
public void testFromEmpty() throws IOException {
assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 0, 0, new byte[0])));
}
@Test
public void testFromTruncated() {
assertNull(JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 255, 170, new byte[99]), listener));
verify(listener, only()).warningOccurred(anyString());
@Test(expected = IIOException.class)
public void testFromTruncated() throws IOException {
JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 255, 170, new byte[99]));
}
@Test
public void testFromValid() throws IOException {
ThumbnailReader reader = JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 30, 20, new byte[30 * 20 * 3]), listener);
ThumbnailReader reader = JFIFThumbnail.from(new JFIF(1, 1, 0, 1, 1, 30, 20, new byte[30 * 20 * 3]));
assertNotNull(reader);
verify(listener, never()).warningOccurred(anyString());
// Sanity check below
assertEquals(30, reader.getWidth());
assertEquals(20, reader.getHeight());
@@ -37,6 +37,7 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
import org.junit.After;
import org.junit.Test;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
@@ -46,8 +47,6 @@ import java.io.IOException;
import java.util.List;
import static org.junit.Assert.*;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
/**
* JFXXThumbnailReaderTest
@@ -60,7 +59,7 @@ public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest {
private final ImageReader thumbnailReader = ImageIO.getImageReadersByFormatName("jpeg").next();
@Override
protected ThumbnailReader createReader(ImageInputStream stream) throws IOException {
protected ThumbnailReader createReader(final ImageInputStream stream) throws IOException {
List<JPEGSegment> segments = JPEGSegmentUtil.readSegments(stream, JPEG.APP0, "JFXX");
stream.close();
@@ -68,7 +67,7 @@ public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest {
assertFalse(segments.isEmpty());
JPEGSegment jfxx = segments.get(0);
return JFXXThumbnail.from(JFXX.read(new DataInputStream(jfxx.segmentData()), jfxx.length()), thumbnailReader, listener);
return JFXXThumbnail.from(JFXX.read(new DataInputStream(jfxx.segmentData()), jfxx.length()), thumbnailReader);
}
@After
@@ -77,51 +76,41 @@ public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest {
}
@Test
public void testFromNull() {
assertNull(JFXXThumbnail.from(null, thumbnailReader, listener));
verify(listener, never()).warningOccurred(anyString());
public void testFromNull() throws IOException {
assertNull(JFXXThumbnail.from(null, thumbnailReader));
}
@Test
public void testFromNullThumbnail() {
assertNull(JFXXThumbnail.from(new JFXX(JFXX.JPEG, null), thumbnailReader, listener));
verify(listener, only()).warningOccurred(anyString());
@Test(expected = IIOException.class)
public void testFromNullThumbnail() throws IOException {
JFXXThumbnail.from(new JFXX(JFXX.JPEG, null), thumbnailReader);
}
@Test
public void testFromEmpty() {
assertNull(JFXXThumbnail.from(new JFXX(JFXX.JPEG, new byte[0]), thumbnailReader, listener));
verify(listener, only()).warningOccurred(anyString());
@Test(expected = IIOException.class)
public void testFromEmpty() throws IOException {
JFXXThumbnail.from(new JFXX(JFXX.JPEG, new byte[0]), thumbnailReader);
}
@Test
public void testFromTruncatedJPEG() {
assertNull(JFXXThumbnail.from(new JFXX(JFXX.JPEG, new byte[99]), thumbnailReader, listener));
verify(listener, only()).warningOccurred(anyString());
@Test(expected = IIOException.class)
public void testFromTruncatedJPEG() throws IOException {
JFXXThumbnail.from(new JFXX(JFXX.JPEG, new byte[99]), thumbnailReader);
}
@Test
public void testFromTruncatedRGB() {
@Test(expected = IIOException.class)
public void testFromTruncatedRGB() throws IOException {
byte[] thumbnail = new byte[765];
thumbnail[0] = (byte) 160;
thumbnail[1] = 90;
assertNull(JFXXThumbnail.from(new JFXX(JFXX.RGB, thumbnail), thumbnailReader, listener));
verify(listener, only()).warningOccurred(anyString());
JFXXThumbnail.from(new JFXX(JFXX.RGB, thumbnail), thumbnailReader);
}
@Test
public void testFromTruncatedIndexed() {
@Test(expected = IIOException.class)
public void testFromTruncatedIndexed() throws IOException {
byte[] thumbnail = new byte[365];
thumbnail[0] = (byte) 160;
thumbnail[1] = 90;
assertNull(JFXXThumbnail.from(new JFXX(JFXX.INDEXED, thumbnail), thumbnailReader, listener));
verify(listener, only()).warningOccurred(anyString());
JFXXThumbnail.from(new JFXX(JFXX.INDEXED, thumbnail), thumbnailReader);
}
@Test
@@ -129,11 +118,9 @@ public class JFXXThumbnailReaderTest extends AbstractThumbnailReaderTest {
byte[] thumbnail = new byte[14];
thumbnail[0] = 2;
thumbnail[1] = 2;
ThumbnailReader reader = JFXXThumbnail.from(new JFXX(JFXX.RGB, thumbnail), thumbnailReader, listener);
ThumbnailReader reader = JFXXThumbnail.from(new JFXX(JFXX.RGB, thumbnail), thumbnailReader);
assertNotNull(reader);
verify(listener, never()).warningOccurred(anyString());
// Sanity check below
assertEquals(2, reader.getWidth());
assertEquals(2, reader.getHeight());