package jnpf.fileStorage.dms;
|
|
import org.junit.Test;
|
|
import javax.imageio.ImageIO;
|
import java.awt.image.BufferedImage;
|
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayOutputStream;
|
import java.util.Arrays;
|
|
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertSame;
|
|
public class DmsJpegExifOrientationTest {
|
|
@Test
|
public void readsAndNormalizesAllEightExifOrientations() throws Exception {
|
String[][] expected = {
|
null,
|
{"ABC", "DEF"},
|
{"CBA", "FED"},
|
{"FED", "CBA"},
|
{"DEF", "ABC"},
|
{"AD", "BE", "CF"},
|
{"DA", "EB", "FC"},
|
{"FC", "EB", "DA"},
|
{"CF", "BE", "AD"}
|
};
|
BufferedImage source = pattern();
|
byte[] jpeg = jpeg();
|
|
for (int orientation = 1; orientation <= 8; orientation++) {
|
byte[] tagged = withExifOrientation(jpeg, orientation, orientation % 2 == 0);
|
assertNotNull(ImageIO.read(new ByteArrayInputStream(tagged)));
|
int parsed = DmsJpegExifOrientation.readOrientation(tagged);
|
|
assertEquals(orientation, parsed);
|
assertGrid(expected[orientation], DmsJpegExifOrientation.normalize(source, parsed));
|
}
|
}
|
|
@Test
|
public void defaultsToOneForMissingOrMalformedExif() throws Exception {
|
byte[] jpeg = jpeg();
|
byte[] invalidOrientation = withExifOrientation(jpeg, 9, true);
|
byte[] truncated = Arrays.copyOf(withExifOrientation(jpeg, 6, false), 14);
|
|
assertEquals(1, DmsJpegExifOrientation.readOrientation(null));
|
assertEquals(1, DmsJpegExifOrientation.readOrientation(new byte[]{1, 2, 3}));
|
assertEquals(1, DmsJpegExifOrientation.readOrientation(jpeg));
|
assertEquals(1, DmsJpegExifOrientation.readOrientation(invalidOrientation));
|
assertEquals(1, DmsJpegExifOrientation.readOrientation(truncated));
|
}
|
|
@Test
|
public void returnsOriginalImageForIdentityInvalidAndNullInputs() {
|
BufferedImage source = pattern();
|
|
assertSame(source, DmsJpegExifOrientation.normalize(source, 1));
|
assertSame(source, DmsJpegExifOrientation.normalize(source, 0));
|
assertSame(source, DmsJpegExifOrientation.normalize(source, 9));
|
assertNull(DmsJpegExifOrientation.normalize(null, 6));
|
}
|
|
private BufferedImage pattern() {
|
BufferedImage image = new BufferedImage(3, 2, BufferedImage.TYPE_INT_ARGB);
|
String[] rows = {"ABC", "DEF"};
|
for (int y = 0; y < rows.length; y++) {
|
for (int x = 0; x < rows[y].length(); x++) {
|
image.setRGB(x, y, color(rows[y].charAt(x)));
|
}
|
}
|
return image;
|
}
|
|
private void assertGrid(String[] expected, BufferedImage image) {
|
assertEquals(expected[0].length(), image.getWidth());
|
assertEquals(expected.length, image.getHeight());
|
for (int y = 0; y < expected.length; y++) {
|
for (int x = 0; x < expected[y].length(); x++) {
|
assertEquals("pixel at " + x + "," + y,
|
color(expected[y].charAt(x)), image.getRGB(x, y));
|
}
|
}
|
}
|
|
private int color(char label) {
|
return 0xFF000000 | label * 0x00010101;
|
}
|
|
private byte[] jpeg() throws Exception {
|
BufferedImage image = new BufferedImage(4, 3, BufferedImage.TYPE_INT_RGB);
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
ImageIO.write(image, "jpeg", output);
|
return output.toByteArray();
|
}
|
|
private byte[] withExifOrientation(byte[] jpeg, int orientation, boolean littleEndian) {
|
byte[] payload = new byte[32];
|
payload[0] = 'E';
|
payload[1] = 'x';
|
payload[2] = 'i';
|
payload[3] = 'f';
|
int tiff = 6;
|
payload[tiff] = littleEndian ? (byte) 'I' : (byte) 'M';
|
payload[tiff + 1] = payload[tiff];
|
putShort(payload, tiff + 2, 42, littleEndian);
|
putInt(payload, tiff + 4, 8, littleEndian);
|
putShort(payload, tiff + 8, 1, littleEndian);
|
int entry = tiff + 10;
|
putShort(payload, entry, 0x0112, littleEndian);
|
putShort(payload, entry + 2, 3, littleEndian);
|
putInt(payload, entry + 4, 1, littleEndian);
|
putShort(payload, entry + 8, orientation, littleEndian);
|
|
int segmentLength = payload.length + 2;
|
byte[] result = new byte[jpeg.length + payload.length + 4];
|
result[0] = jpeg[0];
|
result[1] = jpeg[1];
|
result[2] = (byte) 0xFF;
|
result[3] = (byte) 0xE1;
|
result[4] = (byte) (segmentLength >>> 8);
|
result[5] = (byte) segmentLength;
|
System.arraycopy(payload, 0, result, 6, payload.length);
|
System.arraycopy(jpeg, 2, result, 6 + payload.length, jpeg.length - 2);
|
return result;
|
}
|
|
private void putShort(byte[] data, int offset, int value, boolean littleEndian) {
|
if (littleEndian) {
|
data[offset] = (byte) value;
|
data[offset + 1] = (byte) (value >>> 8);
|
} else {
|
data[offset] = (byte) (value >>> 8);
|
data[offset + 1] = (byte) value;
|
}
|
}
|
|
private void putInt(byte[] data, int offset, int value, boolean littleEndian) {
|
if (littleEndian) {
|
data[offset] = (byte) value;
|
data[offset + 1] = (byte) (value >>> 8);
|
data[offset + 2] = (byte) (value >>> 16);
|
data[offset + 3] = (byte) (value >>> 24);
|
} else {
|
data[offset] = (byte) (value >>> 24);
|
data[offset + 1] = (byte) (value >>> 16);
|
data[offset + 2] = (byte) (value >>> 8);
|
data[offset + 3] = (byte) value;
|
}
|
}
|
}
|