#254 Fix NPE reading TIFF Metadata when BitsPerSample not set

This commit is contained in:
Harald Kuhr 2016-05-25 10:50:42 +02:00
parent 3d36159982
commit b129117ee9
2 changed files with 76 additions and 16 deletions

View File

@ -324,12 +324,7 @@ public final class TIFFImageMetadata extends AbstractMetadata {
// Handle ColorSpaceType (RGB/CMYK/YCbCr etc)... // Handle ColorSpaceType (RGB/CMYK/YCbCr etc)...
Entry photometricTag = ifd.getEntryById(TIFF.TAG_PHOTOMETRIC_INTERPRETATION); Entry photometricTag = ifd.getEntryById(TIFF.TAG_PHOTOMETRIC_INTERPRETATION);
int photometricValue = getValueAsInt(photometricTag); // No default for this tag! int photometricValue = getValueAsInt(photometricTag); // No default for this tag!
int numChannelsValue = getSamplesPerPixelWithFallback();
Entry samplesPerPixelTag = ifd.getEntryById(TIFF.TAG_SAMPLES_PER_PIXEL);
Entry bitsPerSampleTag = ifd.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
int numChannelsValue = samplesPerPixelTag != null
? getValueAsInt(samplesPerPixelTag)
: bitsPerSampleTag.valueCount();
IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType"); IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType");
chroma.appendChild(colorSpaceType); chroma.appendChild(colorSpaceType);
@ -419,6 +414,16 @@ public final class TIFFImageMetadata extends AbstractMetadata {
return chroma; return chroma;
} }
private int getSamplesPerPixelWithFallback() {
// SamplePerPixel defaults to 1, but we'll check BitsPerSample to be sure
Entry samplesPerPixelTag = ifd.getEntryById(TIFF.TAG_SAMPLES_PER_PIXEL);
Entry bitsPerSampleTag = ifd.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
return samplesPerPixelTag != null
? getValueAsInt(samplesPerPixelTag)
: bitsPerSampleTag != null ? bitsPerSampleTag.valueCount() : 1;
}
@Override @Override
protected IIOMetadataNode getStandardCompressionNode() { protected IIOMetadataNode getStandardCompressionNode() {
IIOMetadataNode compression = new IIOMetadataNode("Compression"); IIOMetadataNode compression = new IIOMetadataNode("Compression");
@ -586,9 +591,7 @@ public final class TIFFImageMetadata extends AbstractMetadata {
// TODO: See TIFFImageReader.getBitsPerSample + fix the metadata to have getAsXxxArray methods. // TODO: See TIFFImageReader.getBitsPerSample + fix the metadata to have getAsXxxArray methods.
// BitsPerSample (not required field for Class B/Bilevel, defaults to 1) // BitsPerSample (not required field for Class B/Bilevel, defaults to 1)
Entry bitsPerSampleTag = ifd.getEntryById(TIFF.TAG_BITS_PER_SAMPLE); Entry bitsPerSampleTag = ifd.getEntryById(TIFF.TAG_BITS_PER_SAMPLE);
String bitsPerSampleValue = bitsPerSampleTag == null && String bitsPerSampleValue = bitsPerSampleTag == null
(photometricInterpretationValue == TIFFBaseline.PHOTOMETRIC_WHITE_IS_ZERO ||
photometricInterpretationValue == TIFFBaseline.PHOTOMETRIC_BLACK_IS_ZERO)
? "1" ? "1"
: bitsPerSampleTag.getValueAsString().replaceAll("\\[?\\]?,?", ""); : bitsPerSampleTag.getValueAsString().replaceAll("\\[?\\]?,?", "");
@ -596,10 +599,7 @@ public final class TIFFImageMetadata extends AbstractMetadata {
node.appendChild(bitsPerSample); node.appendChild(bitsPerSample);
bitsPerSample.setAttribute("value", bitsPerSampleValue); bitsPerSample.setAttribute("value", bitsPerSampleValue);
Entry samplesPerPixelTag = ifd.getEntryById(TIFF.TAG_SAMPLES_PER_PIXEL); int numChannelsValue = getSamplesPerPixelWithFallback();
int numChannelsValue = samplesPerPixelTag != null
? getValueAsInt(samplesPerPixelTag)
: bitsPerSampleTag.valueCount();
// SampleMSB // SampleMSB
Entry fillOrderTag = ifd.getEntryById(TIFF.TAG_FILL_ORDER); Entry fillOrderTag = ifd.getEntryById(TIFF.TAG_FILL_ORDER);

View File

@ -22,6 +22,8 @@ import javax.imageio.stream.ImageInputStream;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -228,7 +230,6 @@ public class TIFFImageMetadataTest {
ifdNode.appendChild(tiffField); ifdNode.appendChild(tiffField);
assertNodeNotEquals("Modified tree does not differ", nativeTree, nativeTree2); assertNodeNotEquals("Modified tree does not differ", nativeTree, nativeTree2);
} }
@Test @Test
@ -473,6 +474,67 @@ public class TIFFImageMetadataTest {
metadata.setFromTree(nativeFormat, new IIOMetadataNode(nativeFormat)); // Requires at least one child node metadata.setFromTree(nativeFormat, new IIOMetadataNode(nativeFormat)); // Requires at least one child node
} }
@Test
public void testStandardChromaSamplesPerPixel() {
Set<Entry> entries = new HashSet<>();
entries.add(new TIFFImageWriter.TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, TIFFBaseline.PHOTOMETRIC_RGB));
entries.add(new TIFFImageWriter.TIFFEntry(TIFF.TAG_SAMPLES_PER_PIXEL, 4));
entries.add(new TIFFImageWriter.TIFFEntry(TIFF.TAG_BITS_PER_SAMPLE, new int[] {8, 8, 8})); // This is incorrect, just making sure the correct value is selected
IIOMetadataNode chromaNode = new TIFFImageMetadata(entries).getStandardChromaNode();
assertNotNull(chromaNode);
IIOMetadataNode numChannels = (IIOMetadataNode) chromaNode.getElementsByTagName("NumChannels").item(0);
assertEquals("4", numChannels.getAttribute("value"));
}
@Test
public void testStandardChromaSamplesPerPixelFallbackBitsPerSample() {
Set<Entry> entries = new HashSet<>();
entries.add(new TIFFImageWriter.TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, TIFFBaseline.PHOTOMETRIC_RGB));
entries.add(new TIFFImageWriter.TIFFEntry(TIFF.TAG_BITS_PER_SAMPLE, new int[] {8, 8, 8}));
IIOMetadataNode chromaNode = new TIFFImageMetadata(entries).getStandardChromaNode();
assertNotNull(chromaNode);
IIOMetadataNode numChannels = (IIOMetadataNode) chromaNode.getElementsByTagName("NumChannels").item(0);
assertEquals("3", numChannels.getAttribute("value"));
}
@Test
public void testStandardChromaSamplesPerPixelFallbackDefault() {
Set<Entry> entries = new HashSet<>();
entries.add(new TIFFImageWriter.TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, TIFFBaseline.PHOTOMETRIC_BLACK_IS_ZERO));
IIOMetadataNode chromaNode = new TIFFImageMetadata(entries).getStandardChromaNode();
assertNotNull(chromaNode);
IIOMetadataNode numChannels = (IIOMetadataNode) chromaNode.getElementsByTagName("NumChannels").item(0);
assertEquals("1", numChannels.getAttribute("value"));
}
@Test
public void testStandardDataBitsPerSampleFallbackDefault() {
Set<Entry> entries = new HashSet<>();
entries.add(new TIFFImageWriter.TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, TIFFBaseline.PHOTOMETRIC_BLACK_IS_ZERO));
IIOMetadataNode dataNode = new TIFFImageMetadata(entries).getStandardDataNode();
assertNotNull(dataNode);
IIOMetadataNode numChannels = (IIOMetadataNode) dataNode.getElementsByTagName("BitsPerSample").item(0);
assertEquals("1", numChannels.getAttribute("value"));
}
@Test
public void testStandardNodeSamplesPerPixelFallbackDefault() {
Set<Entry> entries = new HashSet<>();
entries.add(new TIFFImageWriter.TIFFEntry(TIFF.TAG_PHOTOMETRIC_INTERPRETATION, TIFFBaseline.PHOTOMETRIC_RGB));
// Just to make sure we haven't accidentally missed something
IIOMetadataNode standardTree = (IIOMetadataNode) new TIFFImageMetadata(entries).getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
assertNotNull(standardTree);
}
// TODO: Test that failed set leaves metadata unchanged
private void assertSingleNodeWithValue(final NodeList fields, final int tag, int type, final String... expectedValue) { private void assertSingleNodeWithValue(final NodeList fields, final int tag, int type, final String... expectedValue) {
String tagNumber = String.valueOf(tag); String tagNumber = String.valueOf(tag);
String typeName = StringUtil.capitalize(TIFF.TYPE_NAMES[type].toLowerCase()); String typeName = StringUtil.capitalize(TIFF.TYPE_NAMES[type].toLowerCase());
@ -505,8 +567,6 @@ public class TIFFImageMetadataTest {
assertTrue("No tag " + tagNumber + " found", foundTag); assertTrue("No tag " + tagNumber + " found", foundTag);
} }
// TODO: Test that failed set leaves metadata unchanged
static void createTIFFFieldNode(final IIOMetadataNode parentIFDNode, int tag, short type, Object value) { static void createTIFFFieldNode(final IIOMetadataNode parentIFDNode, int tag, short type, Object value) {
IIOMetadataNode fieldNode = new IIOMetadataNode("TIFFField"); IIOMetadataNode fieldNode = new IIOMetadataNode("TIFFField");
parentIFDNode.appendChild(fieldNode); parentIFDNode.appendChild(fieldNode);