#290 TIFF Stream Medata support

This commit is contained in:
Harald Kuhr 2016-11-22 20:51:07 +01:00
parent 18705be7f6
commit 0b030dde52
6 changed files with 280 additions and 10 deletions

View File

@ -75,7 +75,7 @@ import java.util.*;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import static com.twelvemonkeys.imageio.util.IIOUtil.*;
import static com.twelvemonkeys.imageio.util.IIOUtil.createStreamAdapter;
import static com.twelvemonkeys.imageio.util.IIOUtil.lookupProviderByName;
/**
@ -2113,8 +2113,9 @@ public final class TIFFImageReader extends ImageReaderBase {
@Override
public IIOMetadata getStreamMetadata() throws IOException {
// TODO:
return super.getStreamMetadata();
readMetadata();
return new TIFFStreamMetadata(imageInput.getByteOrder());
}
public static void main(final String[] args) throws IOException {

View File

@ -60,6 +60,8 @@ import java.util.*;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFStreamMetadata.configureStreamByteOrder;
/**
* TIFFImageWriter
*
@ -195,9 +197,8 @@ public final class TIFFImageWriter extends ImageWriterBase {
@Override
public void write(final IIOMetadata streamMetadata, final IIOImage image, final ImageWriteParam param) throws IOException {
// TODO: Validate input
assertOutput();
// TODO: streamMetadata?
configureStreamByteOrder(streamMetadata, imageOutput);
// TODO: Make TIFFEntry and possibly TIFFDirectory? public
EXIFWriter exifWriter = new EXIFWriter();

View File

@ -35,8 +35,10 @@ import org.w3c.dom.Node;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;
import java.nio.ByteOrder;
import static com.twelvemonkeys.lang.Validate.notNull;
import static java.nio.ByteOrder.BIG_ENDIAN;
/**
@ -56,6 +58,10 @@ public final class TIFFStreamMetadata extends IIOMetadata {
super(false, SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, null, null, null);
}
TIFFStreamMetadata(final ByteOrder byteOrder) {
this();
this.byteOrder = byteOrder;
}
@Override
public boolean isReadOnly() {
@ -78,7 +84,7 @@ public final class TIFFStreamMetadata extends IIOMetadata {
@Override
public void mergeTree(final String formatName, final Node root) throws IIOInvalidTreeException {
Validate.isTrue(nativeMetadataFormatName.equals(formatName), formatName, "Unsupported metadata format: %s");
Validate.notNull(root, "root");
notNull(root, "root");
if (!nativeMetadataFormatName.equals(root.getNodeName())) {
throw new IIOInvalidTreeException("Root must be " + nativeMetadataFormatName, root);
@ -90,8 +96,8 @@ public final class TIFFStreamMetadata extends IIOMetadata {
}
NamedNodeMap attributes = node.getAttributes();
String value = attributes.getNamedItem("value").getNodeValue();
String value = attributes.getNamedItem("value").getNodeValue();
if (value == null) {
throw new IIOInvalidTreeException("Missing \"value\" attribute in \"ByteOrder\" node", node);
}
@ -121,4 +127,19 @@ public final class TIFFStreamMetadata extends IIOMetadata {
// Big endian is always the default
byteOrder = BIG_ENDIAN;
}
static void configureStreamByteOrder(final IIOMetadata streamMetadata, final ImageOutputStream imageOutput) throws IIOInvalidTreeException {
notNull(imageOutput, "imageOutput");
if (streamMetadata instanceof TIFFStreamMetadata) {
imageOutput.setByteOrder(((TIFFStreamMetadata) streamMetadata).byteOrder);
}
else if (streamMetadata != null) {
TIFFStreamMetadata metadata = new TIFFStreamMetadata();
// Will throw exception if stream format differs from native
metadata.mergeTree(metadata.nativeMetadataFormatName, streamMetadata.getAsTree(metadata.nativeMetadataFormatName));
imageOutput.setByteOrder(metadata.byteOrder);
}
// else, leave as-is
}
}

View File

@ -34,16 +34,19 @@ import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.junit.internal.matchers.StringContains.containsString;
import static org.mockito.Matchers.contains;
@ -560,4 +563,44 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTest<TIFFImageReader
}
}
}
@Test
public void testStreamMetadataNonNull() {
ImageReader reader = createReader();
for (TestData data : getTestData()) {
reader.setInput(data.getInputStream());
try {
IIOMetadata streamMetadata = reader.getStreamMetadata();
assertNotNull(streamMetadata);
assertThat(streamMetadata, is(TIFFStreamMetadata.class));
}
catch (Exception e) {
failBecause(String.format("Image %s could not be read: %s", data.getInput(), e), e);
}
}
}
@Test
public void testStreamMetadataII() throws IOException {
ImageReader reader = createReader();
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/tiff/ccitt_tolessrows.tif"))) {
reader.setInput(stream);
TIFFStreamMetadata streamMetadata = (TIFFStreamMetadata) reader.getStreamMetadata();
assertEquals(ByteOrder.LITTLE_ENDIAN, streamMetadata.byteOrder);
}
}
@Test
public void testStreamMetadataMM() throws IOException {
ImageReader reader = createReader();
try (ImageInputStream stream = ImageIO.createImageInputStream(getClassLoaderResource("/tiff/sm_colors_pb.tif"))) {
reader.setInput(stream);
TIFFStreamMetadata streamMetadata = (TIFFStreamMetadata) reader.getStreamMetadata();
assertEquals(ByteOrder.BIG_ENDIAN, streamMetadata.byteOrder);
}
}
}

View File

@ -48,10 +48,11 @@ import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.List;
@ -789,4 +790,68 @@ public class TIFFImageWriterTest extends ImageWriterAbstractTestCase {
}
}
}
@Test
public void testWriteStreamMetadataDefaultMM() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(output)) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN); // Should pass through
writer.setOutput(stream);
writer.write(null, new IIOImage(getTestData(0), null, null), null);
}
byte[] bytes = output.toByteArray();
assertArrayEquals(new byte[] {'M', 'M', 0, 42}, Arrays.copyOf(bytes, 4));
}
@Test
public void testWriteStreamMetadataDefaultII() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(output)) {
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); // Should pass through
writer.setOutput(stream);
writer.write(null, new IIOImage(getTestData(0), null, null), null);
}
byte[] bytes = output.toByteArray();
assertArrayEquals(new byte[] {'I', 'I', 42, 0}, Arrays.copyOf(bytes, 4));
}
@Test
public void testWriteStreamMetadataMM() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(output)) {
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); // Should be overridden by stream metadata
writer.setOutput(stream);
writer.write(new TIFFStreamMetadata(ByteOrder.BIG_ENDIAN), new IIOImage(getTestData(0), null, null), null);
}
byte[] bytes = output.toByteArray();
assertArrayEquals(new byte[] {'M', 'M', 0, 42}, Arrays.copyOf(bytes, 4));
}
@Test
public void testWriteStreamMetadataII() throws IOException {
ImageWriter writer = createImageWriter();
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (ImageOutputStream stream = ImageIO.createImageOutputStream(output)) {
stream.setByteOrder(ByteOrder.BIG_ENDIAN); // Should be overridden by stream metadata
writer.setOutput(stream);
writer.write(new TIFFStreamMetadata(ByteOrder.LITTLE_ENDIAN), new IIOImage(getTestData(0), null, null), null);
}
byte[] bytes = output.toByteArray();
assertArrayEquals(new byte[] {'I', 'I', 42, 0}, Arrays.copyOf(bytes, 4));
}
}

View File

@ -0,0 +1,139 @@
package com.twelvemonkeys.imageio.plugins.tiff;
import org.junit.Test;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;
import java.nio.ByteOrder;
import static com.twelvemonkeys.imageio.plugins.tiff.TIFFStreamMetadata.SUN_NATIVE_STREAM_METADATA_FORMAT_NAME;
import static java.nio.ByteOrder.BIG_ENDIAN;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
/**
* TIFFStreamMetadataTest
*/
public class TIFFStreamMetadataTest {
// Test that we configure byte order of stream correctly (MM + II)
@Test(expected = IllegalArgumentException.class)
public void testConfigureStreamNullStream() throws IIOInvalidTreeException {
TIFFStreamMetadata.configureStreamByteOrder(new TIFFStreamMetadata(), null);
}
@Test
public void testConfigureStreamNullMetadata() throws IIOInvalidTreeException {
ImageOutputStream stream = mock(ImageOutputStream.class);
TIFFStreamMetadata.configureStreamByteOrder(null, stream);
verify(stream, never()).setByteOrder(any(ByteOrder.class));
}
@Test
public void testConfigureStreamMM() throws IIOInvalidTreeException {
ImageOutputStream stream = mock(ImageOutputStream.class);
TIFFStreamMetadata.configureStreamByteOrder(new TIFFStreamMetadata(BIG_ENDIAN), stream);
verify(stream, only()).setByteOrder(BIG_ENDIAN);
}
@Test
public void testConfigureStreamII() throws IIOInvalidTreeException {
ImageOutputStream stream = mock(ImageOutputStream.class);
TIFFStreamMetadata.configureStreamByteOrder(new TIFFStreamMetadata(LITTLE_ENDIAN), stream);
verify(stream, only()).setByteOrder(LITTLE_ENDIAN);
}
@Test
public void testConfigureStreamForegin() throws IIOInvalidTreeException {
ImageOutputStream stream = mock(ImageOutputStream.class);
IIOMetadata metadata = mock(IIOMetadata.class);
when(metadata.getAsTree(eq(SUN_NATIVE_STREAM_METADATA_FORMAT_NAME))).thenReturn(createForeignTree(LITTLE_ENDIAN));
TIFFStreamMetadata.configureStreamByteOrder(metadata, stream);
verify(stream, only()).setByteOrder(LITTLE_ENDIAN);
}
private IIOMetadataNode createForeignTree(ByteOrder order) {
IIOMetadataNode root = new IIOMetadataNode(SUN_NATIVE_STREAM_METADATA_FORMAT_NAME);
IIOMetadataNode byteOrder = new IIOMetadataNode("ByteOrder");
byteOrder.setAttribute("value", order == LITTLE_ENDIAN ? "LITTLE_ENDIAN" : "BIG_ENDIAN");
root.appendChild(byteOrder);
return root;
}
// Test that we merge correctly with "forreign" metadata class, as long as format names are the same (MM + II)
@Test(expected = IllegalArgumentException.class)
public void testMergeNull() throws IIOInvalidTreeException {
new TIFFStreamMetadata().mergeTree(SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, null);
}
@Test(expected = IllegalArgumentException.class)
public void testMergeIllegal() throws IIOInvalidTreeException {
new TIFFStreamMetadata().mergeTree("com.foo.bar", createForeignTree(BIG_ENDIAN));
}
@Test
public void testMergeII() throws IIOInvalidTreeException {
TIFFStreamMetadata metadata = new TIFFStreamMetadata();
metadata.mergeTree(SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, createForeignTree(LITTLE_ENDIAN));
assertEquals(LITTLE_ENDIAN, metadata.byteOrder);
}
@Test
public void testMergeMM() throws IIOInvalidTreeException {
TIFFStreamMetadata metadata = new TIFFStreamMetadata();
metadata.byteOrder = LITTLE_ENDIAN;
metadata.mergeTree(SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, createForeignTree(BIG_ENDIAN));
assertEquals(BIG_ENDIAN, metadata.byteOrder);
}
@Test(expected = IllegalArgumentException.class)
public void testGetAsTreeNull() {
new TIFFStreamMetadata().getAsTree(null);
}
@Test(expected = IllegalArgumentException.class)
public void testGetAsTreeIllegal() {
new TIFFStreamMetadata().getAsTree("com.foo.bar");
}
@Test
public void testGetAsTreeNative() {
Node root = new TIFFStreamMetadata().getAsTree(SUN_NATIVE_STREAM_METADATA_FORMAT_NAME);
assertNotNull(root);
assertThat(root, is(IIOMetadataNode.class));
assertEquals(SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, root.getNodeName());
NodeList childNodes = root.getChildNodes();
assertEquals(1, childNodes.getLength());
assertThat(childNodes.item(0), is(IIOMetadataNode.class));
IIOMetadataNode byteOrder = (IIOMetadataNode) childNodes.item(0);
assertEquals("ByteOrder", byteOrder.getNodeName());
assertEquals("BIG_ENDIAN", byteOrder.getAttribute("value"));
}
@Test
public void testGetAsTreeNativeII() {
Node root = new TIFFStreamMetadata(LITTLE_ENDIAN).getAsTree(SUN_NATIVE_STREAM_METADATA_FORMAT_NAME);
assertNotNull(root);
assertThat(root, is(IIOMetadataNode.class));
assertEquals(SUN_NATIVE_STREAM_METADATA_FORMAT_NAME, root.getNodeName());
NodeList childNodes = root.getChildNodes();
assertEquals(1, childNodes.getLength());
assertThat(childNodes.item(0), is(IIOMetadataNode.class));
IIOMetadataNode byteOrder = (IIOMetadataNode) childNodes.item(0);
assertEquals("ByteOrder", byteOrder.getNodeName());
assertEquals("LITTLE_ENDIAN", byteOrder.getAttribute("value"));
}
}