diff --git a/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/StandardImageMetadataSupport.java b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/StandardImageMetadataSupport.java
new file mode 100644
index 00000000..29c3c05b
--- /dev/null
+++ b/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/StandardImageMetadataSupport.java
@@ -0,0 +1,586 @@
+package com.twelvemonkeys.imageio;
+
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataNode;
+import java.awt.*;
+import java.awt.color.*;
+import java.awt.image.*;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Map;
+
+import static com.twelvemonkeys.imageio.StandardImageMetadataSupport.ColorSpaceType.*;
+import static com.twelvemonkeys.lang.Validate.isTrue;
+import static com.twelvemonkeys.lang.Validate.notNull;
+
+/**
+ * Base class for easy read-only implementation of the standard image metadata format.
+ * Chroma, Data and Transparency nodes values are based on the required
+ * {@link ImageTypeSpecifier}.
+ * Other values or overrides may be specified using the builder.
+ *
+ * @author Harald Kuhr
+ */
+public class StandardImageMetadataSupport extends AbstractMetadata {
+
+ // The only required field, most standard metadata can be extracted from the type
+ private final ImageTypeSpecifier type;
+ protected final ColorSpaceType colorSpaceType;
+ protected final boolean blackIsZero;
+ private final IndexColorModel palette;
+ protected final String compressionName;
+ protected final boolean compressionLossless;
+ protected final PlanarConfiguration planarConfiguration;
+ private final int[] bitsPerSample;
+ private final int[] significantBits;
+ private final int[] sampleMSB;
+ protected final Double pixelAspectRatio;
+ protected final ImageOrientation orientation;
+ protected final String formatVersion;
+ protected final SubimageInterpretation subimageInterpretation;
+ private final Calendar documentCreationTime; // TODO: This field should be a LocalDateTime or other java.time type, Consider a long timestamp + TimeZone to avoid messing up the API...
+ private final Collection> textEntries;
+
+ protected StandardImageMetadataSupport(Builder builder) {
+ notNull(builder, "builder");
+
+ // Baseline
+ type = builder.type;
+
+ // Chroma
+ colorSpaceType = builder.colorSpaceType;
+ blackIsZero = builder.blackIsZero;
+ palette = builder.palette;
+
+ // Compression
+ compressionName = builder.compressionName;
+ compressionLossless = builder.compressionLossless;
+
+ // Data
+ planarConfiguration = builder.planarConfiguration;
+ bitsPerSample = builder.bitsPerSample;
+ significantBits = builder.significantBits;
+ sampleMSB = builder.sampleMSB;
+
+ // Dimension
+ orientation = builder.orientation;
+ pixelAspectRatio = builder.pixelAspectRatio;
+
+ // Document
+ formatVersion = builder.formatVersion;
+ documentCreationTime = builder.documentCreationTime;
+ subimageInterpretation = builder.subimageInterpretation;
+
+ // Text
+ textEntries = builder.textEntries;
+ }
+
+ public static Builder builder(ImageTypeSpecifier type) {
+ return new Builder(type);
+ }
+
+ public static class Builder {
+ private final ImageTypeSpecifier type;
+ private ColorSpaceType colorSpaceType;
+ private boolean blackIsZero = true;
+ private IndexColorModel palette;
+ private String compressionName;
+ private boolean compressionLossless = true;
+ private PlanarConfiguration planarConfiguration;
+ public int[] bitsPerSample;
+ private int[] significantBits;
+ private int[] sampleMSB;
+ private Double pixelAspectRatio;
+ private ImageOrientation orientation = ImageOrientation.Normal;
+ private String formatVersion;
+ private SubimageInterpretation subimageInterpretation;
+ private Calendar documentCreationTime; // TODO: This field should be a LocalDateTime or other java.time type
+ private final Collection> textEntries = new ArrayList<>();
+
+ protected Builder(ImageTypeSpecifier type) {
+ this.type = notNull(type, "type");
+ }
+
+ public Builder withColorSpaceType(ColorSpaceType colorSpaceType) {
+ this.colorSpaceType = colorSpaceType;
+
+ return this;
+ }
+
+ public Builder withBlackIsZero(boolean blackIsZero) {
+ this.blackIsZero = blackIsZero;
+
+ return this;
+ }
+
+ public Builder withPalette(IndexColorModel palette) {
+ this.palette = palette;
+
+ return this;
+ }
+
+ public Builder withCompressionName(String compressionName) {
+ this.compressionName = notNull(compressionName, "compressionName").equalsIgnoreCase("none") ? null : compressionName;
+
+ return this;
+ }
+
+ public Builder withCompressionLossless(boolean lossless) {
+ if (!lossless && compressionName == null) {
+ throw new IllegalStateException("Lossy compression requires compression name");
+ }
+
+ this.compressionLossless = lossless;
+
+ return this;
+ }
+
+ public Builder withPlanarConfiguration(PlanarConfiguration planarConfiguration) {
+ this.planarConfiguration = planarConfiguration;
+
+ return this;
+ }
+
+ public Builder withBitsPerSample(int... bitsPerSample) {
+ this.bitsPerSample = bitsPerSample;
+
+ return this;
+ }
+
+ public Builder withSignificantBitsPerSample(int... significantBits) {
+ this.significantBits = isTrue(significantBits.length == 1 || significantBits.length == type.getNumBands(),
+ significantBits,
+ String.format("single value or %d values expected", type.getNumBands()));
+
+ return this;
+ }
+
+ public Builder withSampleMSB(int... sampleMSB) {
+ this.sampleMSB = isTrue(sampleMSB.length == 1 || sampleMSB.length == type.getNumBands(),
+ sampleMSB,
+ String.format("single value or %d values expected", type.getNumBands()));
+
+ return this;
+ }
+
+ public Builder withPixelAspectRatio(Double pixelAspectRatio) {
+ this.pixelAspectRatio = pixelAspectRatio;
+
+ return this;
+ }
+
+ public Builder withOrientation(ImageOrientation orientation) {
+ this.orientation = notNull(orientation, "orientation");
+
+ return this;
+ }
+
+ public Builder withFormatVersion(String formatVersion) {
+ this.formatVersion = notNull(formatVersion, "formatVersion");
+
+ return this;
+ }
+
+ public Builder withSubimageInterpretation(SubimageInterpretation interpretation) {
+ this.subimageInterpretation = interpretation;
+
+ return this;
+ }
+
+ public Builder withDocumentCreationTime(Calendar creationTime) {
+ this.documentCreationTime = creationTime;
+
+ return this;
+ }
+
+ public Builder withTextEntries(Map entries) {
+ return withTextEntries(notNull(entries, "entries").entrySet());
+ }
+
+ public Builder withTextEntries(Collection> entries) {
+ this.textEntries.addAll(notNull(entries, "entries"));
+
+ return this;
+ }
+
+ public Builder withTextEntry(String keyword, String value) {
+ if (value != null && !value.isEmpty()) {
+ this.textEntries.add(new SimpleImmutableEntry<>(notNull(keyword, "keyword"), value));
+ }
+
+ return this;
+ }
+
+ public IIOMetadata build() {
+ return new StandardImageMetadataSupport(this);
+ }
+ }
+
+ protected enum ColorSpaceType {
+ XYZ(3),
+ Lab(3),
+ Luv(3),
+ YCbCr(3),
+ Yxy(3),
+ YCCK(4),
+ PhotoYCC(3),
+ RGB(3),
+ GRAY(1),
+ HSV(3),
+ HLS(3),
+ CMYK(3),
+ CMY(3),
+
+ // Generic types (so much extra work, because Java names can't start with a number, phew...)
+ GENERIC_2CLR(2, "2CLR"),
+ GENERIC_3CLR(3, "3CLR"),
+ GENERIC_4CLR(4, "4CLR"),
+ GENERIC_5CLR(5, "5CLR"),
+ GENERIC_6CLR(6, "6CLR"),
+ GENERIC_7CLR(7, "7CLR"),
+ GENERIC_8CLR(8, "8CLR"),
+ GENERIC_9CLR(9, "9CLR"),
+ GENERIC_ACLR(0xA, "ACLR"),
+ GENERIC_BCLR(0xB, "BCLR"),
+ GENERIC_CCLR(0xC, "CCLR"),
+ GENERIC_DCLR(0xD, "DCLR"),
+ GENERIC_ECLR(0xE, "ECLR"),
+ GENERIC_FCLR(0xF, "FCLR");
+
+ final int numChannels;
+ private final String nameOverride;
+
+ ColorSpaceType(int numChannels) {
+ this(numChannels, null);
+ }
+ ColorSpaceType(int numChannels, String nameOverride) {
+ this.numChannels = numChannels;
+ this.nameOverride = nameOverride;
+ }
+
+ @Override
+ public String toString() {
+ return nameOverride != null ? nameOverride : super.toString();
+ }
+ }
+
+ protected enum PlanarConfiguration {
+ PixelInterleaved,
+ PlaneInterleaved,
+ LineInterleaved,
+ TileInterleaved
+ }
+
+ protected enum ImageOrientation {
+ Normal,
+ Rotate90,
+ Rotate180,
+ Rotate270,
+ FlipH,
+ FlipV,
+ FlipHRotate90,
+ FlipVRotate90
+ }
+
+ protected enum SubimageInterpretation {
+ Standalone,
+ SinglePage,
+ FullResolution,
+ ReducedResolution,
+ PyramidLayer,
+ Preview,
+ VolumeSlice,
+ ObjectView,
+ Panorama,
+ AnimationFrame,
+ TransparencyMask,
+ CompositingLayer,
+ SpectralSlice,
+ Unknown
+ }
+
+ @Override
+ protected IIOMetadataNode getStandardChromaNode() {
+ IIOMetadataNode chromaNode = new IIOMetadataNode("Chroma");
+
+ ColorModel colorModel = colorSpaceType != null ? null : type.getColorModel();
+ ColorSpaceType csType = colorSpaceType != null ? colorSpaceType : colorSpaceType(colorModel.getColorSpace());
+ int numComponents = colorSpaceType != null ? colorSpaceType.numChannels : colorModel.getNumComponents();
+
+ IIOMetadataNode colorSpaceTypeNode = new IIOMetadataNode("ColorSpaceType");
+ chromaNode.appendChild(colorSpaceTypeNode);
+ colorSpaceTypeNode.setAttribute("name", csType.toString());
+
+ IIOMetadataNode numChannelsNode = new IIOMetadataNode("NumChannels");
+ numChannelsNode.setAttribute("value", String.valueOf(numComponents));
+ chromaNode.appendChild(numChannelsNode);
+
+ IIOMetadataNode blackIsZeroNode = new IIOMetadataNode("BlackIsZero");
+ blackIsZeroNode.setAttribute("value", booleanString(blackIsZero));
+ chromaNode.appendChild(blackIsZeroNode);
+
+ if (colorModel instanceof IndexColorModel || palette != null) {
+ IndexColorModel colorMap = palette != null ? palette : (IndexColorModel) colorModel;
+
+ IIOMetadataNode paletteNode = new IIOMetadataNode("Palette");
+ chromaNode.appendChild(paletteNode);
+
+ for (int i = 0; i < colorMap.getMapSize(); i++) {
+ IIOMetadataNode paletteEntryNode = new IIOMetadataNode("PaletteEntry");
+ paletteNode.appendChild(paletteEntryNode);
+
+ paletteEntryNode.setAttribute("index", Integer.toString(i));
+ paletteEntryNode.setAttribute("red", Integer.toString(colorMap.getRed(i)));
+ paletteEntryNode.setAttribute("green", Integer.toString(colorMap.getGreen(i)));
+ paletteEntryNode.setAttribute("blue", Integer.toString(colorMap.getBlue(i)));
+
+ // Assumption: BITMASK transparency will use single transparent pixel
+ if (colorMap.getTransparency() == Transparency.TRANSLUCENT) {
+ paletteEntryNode.setAttribute("alpha", Integer.toString(colorMap.getAlpha(i)));
+ }
+ }
+
+ if (colorMap.getTransparentPixel() != -1) {
+ IIOMetadataNode backgroundIndexNode = new IIOMetadataNode("BackgroundIndex");
+ chromaNode.appendChild(backgroundIndexNode);
+ backgroundIndexNode.setAttribute("value", Integer.toString(colorMap.getTransparentPixel()));
+ }
+ }
+
+ // TODO: BackgroundColor?
+
+ return chromaNode;
+ }
+
+ private static ColorSpaceType colorSpaceType(ColorSpace colorSpace) {
+ switch (colorSpace.getType()) {
+ case ColorSpace.TYPE_XYZ:
+ return XYZ;
+ case ColorSpace.TYPE_Lab:
+ return Lab;
+ case ColorSpace.TYPE_Luv:
+ return Luv;
+ case ColorSpace.TYPE_YCbCr:
+ return YCbCr;
+ case ColorSpace.TYPE_Yxy:
+ return Yxy;
+ // Note: Can't map to YCCK or PhotoYCC, as there's no corresponding constant in java.awt.ColorSpace
+ case ColorSpace.TYPE_RGB:
+ return RGB;
+ case ColorSpace.TYPE_GRAY:
+ return GRAY;
+ case ColorSpace.TYPE_HSV:
+ return HSV;
+ case ColorSpace.TYPE_HLS:
+ return HLS;
+ case ColorSpace.TYPE_CMYK:
+ return CMYK;
+ case ColorSpace.TYPE_CMY:
+ return CMY;
+ default:
+ int numComponents = colorSpace.getNumComponents();
+ if (numComponents == 1) {
+ return GRAY;
+ }
+ else if (numComponents < 16) {
+ return ColorSpaceType.valueOf("GENERIC_" + Integer.toHexString(numComponents) + "CLR");
+ }
+ }
+
+ throw new IllegalArgumentException("Unknown ColorSpace type: " + colorSpace);
+ }
+
+ @Override
+ protected IIOMetadataNode getStandardCompressionNode() {
+ if (compressionName == null) {
+ return null;
+ }
+
+ IIOMetadataNode node = new IIOMetadataNode("Compression");
+
+ IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
+ compressionTypeName.setAttribute("value", compressionName);
+ node.appendChild(compressionTypeName);
+
+ IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
+ lossless.setAttribute("value", booleanString(compressionLossless));
+ node.appendChild(lossless);
+
+ return node;
+ }
+
+ protected static String booleanString(boolean booleanValue) {
+ return booleanValue ? "TRUE" : "FALSE";
+ }
+
+ @Override
+ protected IIOMetadataNode getStandardDataNode() {
+ IIOMetadataNode dataNode = new IIOMetadataNode("Data");
+
+ IIOMetadataNode planarConfigurationNode = new IIOMetadataNode("PlanarConfiguration");
+ dataNode.appendChild(planarConfigurationNode);
+ planarConfigurationNode.setAttribute("value", planarConfiguration != null ? planarConfiguration.toString() :
+ (type.getSampleModel() instanceof BandedSampleModel ? "PlaneInterleaved" : "PixelInterleaved"));
+
+ String sampleFormatValue = colorSpaceType == null && type.getColorModel() instanceof IndexColorModel
+ ? "Index"
+ : sampleFormat(type.getSampleModel());
+
+ if (sampleFormatValue != null) {
+ IIOMetadataNode sampleFormatNode = new IIOMetadataNode("SampleFormat");
+ sampleFormatNode.setAttribute("value", sampleFormatValue);
+ dataNode.appendChild(sampleFormatNode);
+ }
+
+ int[] bitsPerSample = this.bitsPerSample != null ? this.bitsPerSample : type.getSampleModel().getSampleSize();
+ IIOMetadataNode bitsPerSampleNode = new IIOMetadataNode("BitsPerSample");
+ bitsPerSampleNode.setAttribute("value", createListValue(bitsPerSample.length, bitsPerSample));
+ dataNode.appendChild(bitsPerSampleNode);
+
+ if (significantBits != null) {
+ String significantBitsValue = createListValue(type.getNumBands(), significantBits);
+ if (!significantBitsValue.equals(bitsPerSampleNode.getAttribute("value"))) {
+ IIOMetadataNode significantBitsPerSampleNode = new IIOMetadataNode("SignificantBitsPerSample");
+ significantBitsPerSampleNode.setAttribute("value", significantBitsValue);
+ dataNode.appendChild(significantBitsPerSampleNode);
+ }
+ }
+
+ if (sampleMSB != null) {
+ // TODO: Only if different from default!
+ IIOMetadataNode sampleMSBNode = new IIOMetadataNode("SampleMSB");
+ sampleMSBNode.setAttribute("value", createListValue(type.getNumBands(), sampleMSB));
+ dataNode.appendChild(sampleMSBNode);
+ }
+
+ return dataNode;
+ }
+
+ private static String createListValue(final int itemCount, final int... values) {
+ StringBuilder buffer = new StringBuilder();
+
+ for (int i = 0; i < itemCount; i++) {
+ if (buffer.length() > 0) {
+ buffer.append(' ');
+ }
+
+ buffer.append(values[i % values.length]);
+ }
+
+ return buffer.toString();
+ }
+
+ private static String sampleFormat(SampleModel sampleModel) {
+ switch (sampleModel.getDataType()) {
+ case DataBuffer.TYPE_SHORT:
+ case DataBuffer.TYPE_INT:
+ if (sampleModel instanceof ComponentSampleModel) {
+ return "SignedIntegral";
+ }
+ // Otherwise fall-through, most likely a *PixelPackedSampleModel
+ case DataBuffer.TYPE_BYTE:
+ case DataBuffer.TYPE_USHORT:
+ return "UnsignedIntegral";
+ case DataBuffer.TYPE_FLOAT:
+ case DataBuffer.TYPE_DOUBLE:
+ return "Real";
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected IIOMetadataNode getStandardDimensionNode() {
+ IIOMetadataNode dimensionNode = new IIOMetadataNode("Dimension");
+
+ if (pixelAspectRatio != null) {
+ IIOMetadataNode pixelAspectRatioNode = new IIOMetadataNode("PixelAspectRatio");
+ pixelAspectRatioNode.setAttribute("value", String.valueOf(pixelAspectRatio));
+ dimensionNode.appendChild(pixelAspectRatioNode);
+ }
+
+ IIOMetadataNode imageOrientationNode = new IIOMetadataNode("ImageOrientation");
+ imageOrientationNode.setAttribute("value", orientation.toString());
+ dimensionNode.appendChild(imageOrientationNode);
+
+ return dimensionNode.hasChildNodes() ? dimensionNode : null;
+ }
+
+ @Override
+ protected IIOMetadataNode getStandardDocumentNode() {
+ IIOMetadataNode documentNode = new IIOMetadataNode("Document");
+
+ if (formatVersion != null) {
+ IIOMetadataNode formatVersionNode = new IIOMetadataNode("FormatVersion");
+ documentNode.appendChild(formatVersionNode);
+ formatVersionNode.setAttribute("value", formatVersion);
+ }
+
+ if (subimageInterpretation != null) {
+ IIOMetadataNode subImageInterpretationNode = new IIOMetadataNode("SubimageInterpretation");
+ documentNode.appendChild(subImageInterpretationNode);
+ subImageInterpretationNode.setAttribute("value", subimageInterpretation.toString());
+ }
+
+ if (documentCreationTime != null) {
+ IIOMetadataNode imageCreationTimeNode = new IIOMetadataNode("ImageCreationTime");
+ documentNode.appendChild(imageCreationTimeNode);
+
+ imageCreationTimeNode.setAttribute("year", String.valueOf(documentCreationTime.get(Calendar.YEAR)));
+ imageCreationTimeNode.setAttribute("month", String.valueOf(documentCreationTime.get(Calendar.MONTH) + 1));
+ imageCreationTimeNode.setAttribute("day", String.valueOf(documentCreationTime.get(Calendar.DAY_OF_MONTH)));
+ imageCreationTimeNode.setAttribute("hour", String.valueOf(documentCreationTime.get(Calendar.HOUR_OF_DAY)));
+ imageCreationTimeNode.setAttribute("minute", String.valueOf(documentCreationTime.get(Calendar.MINUTE)));
+ imageCreationTimeNode.setAttribute("second", String.valueOf(documentCreationTime.get(Calendar.SECOND)));
+ }
+
+ return documentNode.hasChildNodes() ? documentNode : null;
+ }
+
+ @Override
+ protected IIOMetadataNode getStandardTextNode() {
+ if (textEntries.isEmpty()) {
+ return null;
+ }
+
+ IIOMetadataNode textNode = new IIOMetadataNode("Text");
+
+ // DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright:
+ // /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
+
+ for (Map.Entry entry : textEntries) {
+ IIOMetadataNode textEntryNode = new IIOMetadataNode("TextEntry");
+ textNode.appendChild(textEntryNode);
+ textEntryNode.setAttribute("keyword", entry.getKey());
+ textEntryNode.setAttribute("value", entry.getValue());
+ }
+
+ return textNode;
+
+ }
+
+ @Override
+ protected IIOMetadataNode getStandardTransparencyNode() {
+ IIOMetadataNode transparencyNode = new IIOMetadataNode("Transparency");
+
+ ColorModel colorModel = type.getColorModel();
+
+ IIOMetadataNode alphaNode = new IIOMetadataNode("Alpha");
+ transparencyNode.appendChild(alphaNode);
+ alphaNode.setAttribute("value", colorModel.hasAlpha() ? (colorModel.isAlphaPremultiplied() ? "premultiplied" : "nonpremultiplied") : "none");
+
+ if (colorModel instanceof IndexColorModel) {
+ IndexColorModel icm = (IndexColorModel) colorModel;
+ if (icm.getTransparentPixel() != -1) {
+ IIOMetadataNode transparentIndexNode = new IIOMetadataNode("TransparentIndex");
+ transparencyNode.appendChild(transparentIndexNode);
+ transparentIndexNode.setAttribute("value", Integer.toString(icm.getTransparentPixel()));
+ }
+ }
+
+ return transparencyNode;
+ }
+}
diff --git a/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/StandardImageMetadataSupportTest.java b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/StandardImageMetadataSupportTest.java
new file mode 100644
index 00000000..94d8c90f
--- /dev/null
+++ b/imageio/imageio-core/src/test/java/com/twelvemonkeys/imageio/StandardImageMetadataSupportTest.java
@@ -0,0 +1,140 @@
+package com.twelvemonkeys.imageio;
+
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport.ColorSpaceType;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport.ImageOrientation;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport.PlanarConfiguration;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport.SubimageInterpretation;
+import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
+
+import org.junit.Test;
+
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataNode;
+import java.awt.image.*;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static com.twelvemonkeys.imageio.StandardImageMetadataSupport.builder;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class StandardImageMetadataSupportTest {
+ @Test(expected = IllegalArgumentException.class)
+ public void createNullBuilder() {
+ new StandardImageMetadataSupport(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void createNullType() {
+ new StandardImageMetadataSupport(builder(null));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void builderNullType() {
+ builder(null).build();
+ }
+
+ @Test
+ public void createValid() {
+ IIOMetadata metadata = new StandardImageMetadataSupport(builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB)));
+ assertNotNull(metadata);
+ }
+
+ @Test
+ public void builderValid() {
+ IIOMetadata metadata = builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB))
+ .build();
+ assertNotNull(metadata);
+ }
+
+
+ @Test
+ public void withPlanarColorspaceType() {
+ // See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html
+ Collection allowedValues = Arrays.asList(
+ "XYZ", "Lab", "Luv", "YCbCr", "Yxy", "YCCK", "PhotoYCC",
+ "RGB", "GRAY", "HSV", "HLS", "CMYK", "CMY",
+ "2CLR", "3CLR", "4CLR", "5CLR", "6CLR", "7CLR", "8CLR",
+ "9CLR", "ACLR", "BCLR", "CCLR", "DCLR", "ECLR", "FCLR"
+ );
+
+ for (ColorSpaceType value : ColorSpaceType.values()) {
+ StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
+ .withColorSpaceType(value)
+ .build();
+ assertNotNull(metadata);
+
+ IIOMetadataNode documentNode = metadata.getStandardChromaNode();
+ assertNotNull(documentNode);
+
+ IIOMetadataNode subImageInterpretation = (IIOMetadataNode) documentNode.getElementsByTagName("ColorSpaceType").item(0);
+ assertEquals(value.toString(), subImageInterpretation.getAttribute("name")); // Format oddity: Why is this not "value"?
+ assertTrue(allowedValues.contains(value.toString()));
+ }
+ }
+
+ @Test
+ public void withPlanarConfiguration() {
+ // See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html
+ Collection allowedValues = Arrays.asList("PixelInterleaved", "PlaneInterleaved", "LineInterleaved", "TileInterleaved");
+
+ for (PlanarConfiguration value : PlanarConfiguration.values()) {
+ StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR))
+ .withPlanarConfiguration(value)
+ .build();
+ assertNotNull(metadata);
+
+ IIOMetadataNode documentNode = metadata.getStandardDataNode();
+ assertNotNull(documentNode);
+
+ IIOMetadataNode subImageInterpretation = (IIOMetadataNode) documentNode.getElementsByTagName("PlanarConfiguration").item(0);
+ assertEquals(value.toString(), subImageInterpretation.getAttribute("value"));
+ assertTrue(allowedValues.contains(value.toString()));
+ }
+ }
+
+ @Test
+ public void withImageOrientation() {
+ // See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html
+ Collection allowedValues = Arrays.asList("Normal", "Rotate90", "Rotate180", "Rotate270", "FlipH", "FlipV", "FlipHRotate90", "FlipVRotate90");
+
+ for (ImageOrientation value : ImageOrientation.values()) {
+ StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY))
+ .withOrientation(value)
+ .build();
+ assertNotNull(metadata);
+
+ IIOMetadataNode documentNode = metadata.getStandardDimensionNode();
+ assertNotNull(documentNode);
+
+ IIOMetadataNode subImageInterpretation = (IIOMetadataNode) documentNode.getElementsByTagName("ImageOrientation").item(0);
+ assertEquals(value.toString(), subImageInterpretation.getAttribute("value"));
+ assertTrue(allowedValues.contains(value.toString()));
+ }
+ }
+
+ @Test
+ public void withSubimageInterpretation() {
+ // See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html
+ Collection allowedValues = Arrays.asList(
+ "Standalone", "SinglePage", "FullResolution", "ReducedResolution", "PyramidLayer",
+ "Preview", "VolumeSlice", "ObjectView", "Panorama", "AnimationFrame",
+ "TransparencyMask", "CompositingLayer", "SpectralSlice", "Unknown"
+ );
+
+ for (SubimageInterpretation value : SubimageInterpretation.values()) {
+ StandardImageMetadataSupport metadata = (StandardImageMetadataSupport) builder(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB))
+ .withSubimageInterpretation(value)
+ .build();
+ assertNotNull(metadata);
+
+ IIOMetadataNode documentNode = metadata.getStandardDocumentNode();
+ assertNotNull(documentNode);
+
+ IIOMetadataNode subImageInterpretation = (IIOMetadataNode) documentNode.getElementsByTagName("SubimageInterpretation").item(0);
+ assertEquals(value.toString(), subImageInterpretation.getAttribute("value"));
+ assertTrue(allowedValues.contains(value.toString()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/imageio/imageio-hdr/src/main/java/com/twelvemonkeys/imageio/plugins/hdr/HDRImageReader.java b/imageio/imageio-hdr/src/main/java/com/twelvemonkeys/imageio/plugins/hdr/HDRImageReader.java
index b1af2080..f7e33590 100644
--- a/imageio/imageio-hdr/src/main/java/com/twelvemonkeys/imageio/plugins/hdr/HDRImageReader.java
+++ b/imageio/imageio-hdr/src/main/java/com/twelvemonkeys/imageio/plugins/hdr/HDRImageReader.java
@@ -40,11 +40,8 @@ import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
-import java.awt.color.ColorSpace;
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBuffer;
-import java.awt.image.Raster;
-import java.awt.image.WritableRaster;
+import java.awt.color.*;
+import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
@@ -244,10 +241,7 @@ public final class HDRImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
- checkBounds(imageIndex);
- readHeader();
-
- return new HDRMetadata(header);
+ return new HDRMetadata(getRawImageType(imageIndex), header);
}
public static void main(final String[] args) throws IOException {
diff --git a/imageio/imageio-hdr/src/main/java/com/twelvemonkeys/imageio/plugins/hdr/HDRMetadata.java b/imageio/imageio-hdr/src/main/java/com/twelvemonkeys/imageio/plugins/hdr/HDRMetadata.java
index b783c68f..ec35918f 100755
--- a/imageio/imageio-hdr/src/main/java/com/twelvemonkeys/imageio/plugins/hdr/HDRMetadata.java
+++ b/imageio/imageio-hdr/src/main/java/com/twelvemonkeys/imageio/plugins/hdr/HDRMetadata.java
@@ -1,83 +1,19 @@
-/*
- * Copyright (c) 2015, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
package com.twelvemonkeys.imageio.plugins.hdr;
-import com.twelvemonkeys.imageio.AbstractMetadata;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
+import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadataNode;
-final class HDRMetadata extends AbstractMetadata {
- private final HDRHeader header;
-
- HDRMetadata(final HDRHeader header) {
- this.header = header;
- }
-
- @Override
- protected IIOMetadataNode getStandardChromaNode() {
- IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
-
- IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
- chroma.appendChild(csType);
- csType.setAttribute("name", "RGB");
- // TODO: Support XYZ
-
- IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
- numChannels.setAttribute("value", "3");
- chroma.appendChild(numChannels);
-
- IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
- blackIsZero.setAttribute("value", "TRUE");
- chroma.appendChild(blackIsZero);
-
- return chroma;
- }
-
- // No compression
-
- @Override
- protected IIOMetadataNode getStandardCompressionNode() {
- IIOMetadataNode node = new IIOMetadataNode("Compression");
-
- IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
- compressionTypeName.setAttribute("value", "RLE");
- node.appendChild(compressionTypeName);
-
- IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
- lossless.setAttribute("value", "TRUE");
- node.appendChild(lossless);
-
- return node;
+public class HDRMetadata extends StandardImageMetadataSupport {
+ public HDRMetadata(ImageTypeSpecifier type, HDRHeader header) {
+ super(builder(type)
+ .withCompressionName("RLE")
+ .withTextEntry("Software", header.getSoftware()));
}
+ // For HDR, the stored sample data is UnsignedIntegral and data is 4 channels (RGB+Exp),
+ // but decoded to Real (float) 3 chanel RGB
@Override
protected IIOMetadataNode getStandardDataNode() {
IIOMetadataNode node = new IIOMetadataNode("Data");
@@ -92,38 +28,4 @@ final class HDRMetadata extends AbstractMetadata {
return node;
}
-
- @Override
- protected IIOMetadataNode getStandardDimensionNode() {
- IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
-
- // TODO: Support other orientations
- IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
- imageOrientation.setAttribute("value", "Normal");
- dimension.appendChild(imageOrientation);
-
- return dimension;
- }
-
- // No document node
-
- @Override
- protected IIOMetadataNode getStandardTextNode() {
- if (header.getSoftware() != null) {
- IIOMetadataNode text = new IIOMetadataNode("Text");
-
- IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
- textEntry.setAttribute("keyword", "Software");
- textEntry.setAttribute("value", header.getSoftware());
- text.appendChild(textEntry);
-
- return text;
- }
-
- return null;
- }
-
- // No tiling
-
- // No transparency
}
diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageMetadata.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageMetadata.java
new file mode 100644
index 00000000..20cf3783
--- /dev/null
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageMetadata.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2022, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.imageio.plugins.icns;
+
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
+
+import javax.imageio.ImageTypeSpecifier;
+
+final class ICNSImageMetadata extends StandardImageMetadataSupport {
+ ICNSImageMetadata(ImageTypeSpecifier type, String compressionName) {
+ super(builder(type).withCompressionName(compressionName));
+ }
+}
diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReader.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReader.java
index 82962c98..8ceb1faf 100644
--- a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReader.java
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageReader.java
@@ -35,11 +35,16 @@ import com.twelvemonkeys.imageio.stream.SubImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
-import javax.imageio.*;
+import javax.imageio.IIOException;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
-import java.awt.color.ColorSpace;
+import java.awt.color.*;
import java.awt.image.*;
import java.io.DataInputStream;
import java.io.File;
@@ -61,10 +66,9 @@ import java.util.List;
* @see Apple Icon Image format (Wikipedia)
*/
public final class ICNSImageReader extends ImageReaderBase {
- // TODO: Support ToC resource for faster parsing/faster determine number of icons?
// TODO: Subsampled reading for completeness, even if never used?
- private List icons = new ArrayList();
- private List masks = new ArrayList();
+ private final List icons = new ArrayList<>();
+ private final List masks = new ArrayList<>();
private IconResource lastResourceRead;
private int length;
@@ -136,7 +140,7 @@ public final class ICNSImageReader extends ImageReaderBase {
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
IconResource resource = readIconResource(imageIndex);
- List specifiers = new ArrayList();
+ List specifiers = new ArrayList<>();
switch (resource.depth()) {
case 1:
@@ -230,14 +234,9 @@ public final class ICNSImageReader extends ImageReaderBase {
packedSize -= 4;
}
- InputStream input = IIOUtil.createStreamAdapter(imageInput, packedSize);
-
- try {
+ try (InputStream input = IIOUtil.createStreamAdapter(imageInput, packedSize)) {
ICNSUtil.decompress(new DataInputStream(input), data, 0, (data.length * 24) / 32); // 24 bit data
}
- finally {
- input.close();
- }
}
else {
data = new byte[resource.length - ICNS.RESOURCE_HEADER_SIZE];
@@ -491,7 +490,7 @@ public final class ICNSImageReader extends ImageReaderBase {
String format;
- if (Arrays.equals(ICNS.PNG_MAGIC, magic)) {
+ if (Arrays.equals(ICNS.PNG_MAGIC, Arrays.copyOfRange(magic, 0, ICNS.PNG_MAGIC.length))) {
format = "PNG";
}
else if (Arrays.equals(ICNS.JPEG_2000_MAGIC, magic)) {
@@ -527,7 +526,6 @@ public final class ICNSImageReader extends ImageReaderBase {
IconResource resource = IconResource.read(imageInput);
if (resource.isTOC()) {
- // TODO: IconResource.readTOC()?
int resourceCount = (resource.length - ICNS.RESOURCE_HEADER_SIZE) / ICNS.RESOURCE_HEADER_SIZE;
long pos = resource.start + resource.length;
@@ -570,6 +568,23 @@ public final class ICNSImageReader extends ImageReaderBase {
}
}
+ @Override
+ public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
+ IconResource resource = readIconResource(imageIndex);
+
+ String compressionName;
+ if (resource.isForeignFormat()) {
+ // Special handling of PNG/JPEG 2000 icons
+ imageInput.seek(resource.start + ICNS.RESOURCE_HEADER_SIZE);
+ compressionName = getForeignFormat(imageInput);
+ }
+ else {
+ compressionName = resource.isCompressed() ? "RLE" : "None";
+ }
+
+ return new ICNSImageMetadata(getRawImageType(imageIndex), compressionName);
+ }
+
private static final class ICNSBitMaskColorModel extends IndexColorModel {
static final IndexColorModel INSTANCE = new ICNSBitMaskColorModel();
@@ -578,7 +593,6 @@ public final class ICNSImageReader extends ImageReaderBase {
}
}
- @SuppressWarnings({"UnusedAssignment"})
public static void main(String[] args) throws IOException {
int argIndex = 0;
diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageWriter.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageWriter.java
index 56641a40..be78fb08 100644
--- a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageWriter.java
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/ICNSImageWriter.java
@@ -34,7 +34,13 @@ import com.twelvemonkeys.imageio.ImageWriterBase;
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
-import javax.imageio.*;
+import javax.imageio.IIOException;
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
import javax.imageio.event.IIOWriteWarningListener;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
@@ -104,6 +110,7 @@ public final class ICNSImageWriter extends ImageWriterBase {
sequenceIndex = 0;
}
+ @SuppressWarnings("RedundantThrows")
@Override
public void endWriteSequence() throws IOException {
assertOutput();
diff --git a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/SipsJP2Reader.java b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/SipsJP2Reader.java
index 9b1fb949..ef129ddf 100644
--- a/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/SipsJP2Reader.java
+++ b/imageio/imageio-icns/src/main/java/com/twelvemonkeys/imageio/plugins/icns/SipsJP2Reader.java
@@ -38,8 +38,13 @@ import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
-import java.awt.image.BufferedImage;
-import java.io.*;
+import java.awt.image.*;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.util.Iterator;
/**
@@ -140,17 +145,12 @@ final class SipsJP2Reader {
}
private static String checkErrorMessage(final Process process) throws IOException {
- InputStream stream = process.getErrorStream();
-
- try {
+ try (InputStream stream = process.getErrorStream()) {
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String message = reader.readLine();
return message != null && message.startsWith("Error: ") ? message.substring(7) : null;
}
- finally {
- stream.close();
- }
}
private static String[] buildCommand(final File sipsCommand, final File tempFile) {
@@ -159,19 +159,13 @@ final class SipsJP2Reader {
};
}
-
private static File dumpToFile(final ImageInputStream stream) throws IOException {
File tempFile = File.createTempFile("imageio-icns-", ".png");
tempFile.deleteOnExit();
- FileOutputStream out = new FileOutputStream(tempFile);
-
- try {
+ try (FileOutputStream out = new FileOutputStream(tempFile)) {
FileUtil.copy(IIOUtil.createStreamAdapter(stream), out);
}
- finally {
- out.close();
- }
return tempFile;
}
diff --git a/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/Form.java b/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/Form.java
index 1462d237..c43b821b 100644
--- a/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/Form.java
+++ b/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/Form.java
@@ -1,13 +1,13 @@
package com.twelvemonkeys.imageio.plugins.iff;
import javax.imageio.IIOException;
-import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.IndexColorModel;
+import java.awt.image.*;
import java.util.ArrayList;
import java.util.List;
+import static com.twelvemonkeys.imageio.plugins.iff.IFF.*;
import static com.twelvemonkeys.imageio.plugins.iff.IFFUtil.toChunkStr;
+import static com.twelvemonkeys.lang.Validate.isTrue;
/**
* Form.
@@ -27,7 +27,7 @@ abstract class Form {
abstract int width();
abstract int height();
- abstract float aspect();
+ abstract double aspect();
abstract int bitplanes();
abstract int compressionType();
@@ -118,7 +118,7 @@ abstract class Form {
}
private ILBMForm(final int formType, final BMHDChunk bitmapHeader, final CAMGChunk viewMode, final CMAPChunk colorMap, final AbstractMultiPaletteChunk multiPalette, final XS24Chunk thumbnail, final BODYChunk body) {
- super(formType);
+ super(isTrue(validFormType(formType), formType, "Unknown IFF Form type: %s"));
this.bitmapHeader = bitmapHeader;
this.viewMode = viewMode;
this.colorMap = colorMap;
@@ -127,6 +127,19 @@ abstract class Form {
this.body = body;
}
+ private static boolean validFormType(int formType) {
+ switch (formType) {
+ case TYPE_ACBM:
+ case TYPE_ILBM:
+ case TYPE_PBM:
+ case TYPE_RGB8:
+ case TYPE_RGBN:
+ return true;
+ default:
+ return false;
+ }
+ }
+
@Override
int width() {
return bitmapHeader.width;
@@ -148,8 +161,8 @@ abstract class Form {
}
@Override
- float aspect() {
- return bitmapHeader.yAspect == 0 ? 0 : (bitmapHeader.xAspect / (float) bitmapHeader.yAspect);
+ double aspect() {
+ return bitmapHeader.yAspect == 0 ? 0 : (bitmapHeader.xAspect / (double) bitmapHeader.yAspect);
}
@Override
@@ -297,7 +310,7 @@ abstract class Form {
}
private DEEPForm(final int formType, final DGBLChunk deepGlobal, final DLOCChunk deepLocation, final DPELChunk deepPixel, final XS24Chunk thumbnail, final BODYChunk body) {
- super(formType);
+ super(isTrue(validFormType(formType), formType, "Unknown IFF Form type: %s"));
this.deepGlobal = deepGlobal;
this.deepLocation = deepLocation;
this.deepPixel = deepPixel;
@@ -305,6 +318,15 @@ abstract class Form {
this.body = body;
}
+ private static boolean validFormType(int formType) {
+ switch (formType) {
+ case TYPE_DEEP:
+ case TYPE_TVPP:
+ return true;
+ default:
+ return false;
+ }
+ }
@Override
int width() {
@@ -337,8 +359,8 @@ abstract class Form {
}
@Override
- float aspect() {
- return deepGlobal.yAspect == 0 ? 0 : deepGlobal.xAspect / (float) deepGlobal.yAspect;
+ double aspect() {
+ return deepGlobal.yAspect == 0 ? 0 : deepGlobal.xAspect / (double) deepGlobal.yAspect;
}
@Override
diff --git a/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageMetadata.java b/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageMetadata.java
index 239c5215..8ab39545 100644
--- a/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageMetadata.java
+++ b/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageMetadata.java
@@ -1,188 +1,85 @@
+/*
+ * Copyright (c) 2020, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
package com.twelvemonkeys.imageio.plugins.iff;
-import com.twelvemonkeys.imageio.AbstractMetadata;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
-import javax.imageio.metadata.IIOMetadataNode;
-import java.awt.*;
-import java.awt.image.IndexColorModel;
+import javax.imageio.ImageTypeSpecifier;
+import java.awt.image.*;
import java.nio.charset.StandardCharsets;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import static com.twelvemonkeys.imageio.plugins.iff.IFF.*;
-import static com.twelvemonkeys.lang.Validate.isTrue;
+import static com.twelvemonkeys.imageio.plugins.iff.IFFUtil.toChunkStr;
import static com.twelvemonkeys.lang.Validate.notNull;
+import static java.util.Collections.emptyList;
-final class IFFImageMetadata extends AbstractMetadata {
- private final Form header;
- private final IndexColorModel colorMap;
- private final List meta;
-
- IFFImageMetadata(Form header, IndexColorModel colorMap) {
- this.header = notNull(header, "header");
- isTrue(validFormType(header.formType), header.formType, "Unknown IFF Form type: %s");
- this.colorMap = colorMap;
- this.meta = header.meta;
+final class IFFImageMetadata extends StandardImageMetadataSupport {
+ IFFImageMetadata(ImageTypeSpecifier type, Form header, IndexColorModel palette) {
+ this(builder(type), notNull(header, "header"), palette);
}
- private boolean validFormType(int formType) {
- switch (formType) {
- default:
- return false;
- case TYPE_ACBM:
- case TYPE_DEEP:
- case TYPE_ILBM:
- case TYPE_PBM:
- case TYPE_RGB8:
- case TYPE_RGBN:
- case TYPE_TVPP:
- return true;
- }
+ private IFFImageMetadata(Builder builder, Form header, IndexColorModel palette) {
+ super(builder.withPalette(palette)
+ .withCompressionName(compressionName(header))
+ .withBitsPerSample(bitsPerSample(header))
+ .withPlanarConfiguration(planarConfiguration(header))
+ .withPixelAspectRatio(header.aspect() != 0 ? header.aspect() : null)
+ .withFormatVersion("1.0")
+ .withTextEntries(textEntries(header)));
}
- @Override
- protected IIOMetadataNode getStandardChromaNode() {
- IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
-
- IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
- chroma.appendChild(csType);
-
- switch (header.bitplanes()) {
- case 8:
- if (colorMap == null) {
- csType.setAttribute("name", "GRAY");
- break;
- }
- case 1:
- case 2:
- case 3:
+ private static String compressionName(Form header) {
+ switch (header.compressionType()) {
+ case BMHDChunk.COMPRESSION_NONE:
+ return "None";
+ case BMHDChunk.COMPRESSION_BYTE_RUN:
+ return "RLE";
case 4:
- case 5:
- case 6:
- case 7:
- case 24:
- case 25:
- case 32:
- csType.setAttribute("name", "RGB");
- break;
+ // Compression type 4 means different things for different FORM types, we support
+ // Impulse RGB8 RLE compression: 24 bit RGB + 1 bit mask + 7 bit run count
+ if (header.formType == TYPE_RGB8) {
+ return "RGB8";
+ }
default:
- csType.setAttribute("name", "Unknown");
+ return "Unknown";
}
-
- // NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
- IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
- chroma.appendChild(numChannels);
- if (colorMap == null && header.bitplanes() == 8) {
- numChannels.setAttribute("value", Integer.toString(1));
- }
- else if (header.bitplanes() == 25 || header.bitplanes() == 32) {
- numChannels.setAttribute("value", Integer.toString(4));
- }
- else {
- numChannels.setAttribute("value", Integer.toString(3));
- }
-
- IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
- chroma.appendChild(blackIsZero);
- blackIsZero.setAttribute("value", "TRUE");
-
- // NOTE: TGA files may contain a color map, even if true color...
- // Not sure if this is a good idea to expose to the meta data,
- // as it might be unexpected... Then again...
- if (colorMap != null) {
- IIOMetadataNode palette = new IIOMetadataNode("Palette");
- chroma.appendChild(palette);
-
- for (int i = 0; i < colorMap.getMapSize(); i++) {
- IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
- palette.appendChild(paletteEntry);
- paletteEntry.setAttribute("index", Integer.toString(i));
-
- paletteEntry.setAttribute("red", Integer.toString(colorMap.getRed(i)));
- paletteEntry.setAttribute("green", Integer.toString(colorMap.getGreen(i)));
- paletteEntry.setAttribute("blue", Integer.toString(colorMap.getBlue(i)));
- }
-
- if (colorMap.getTransparentPixel() != -1) {
- IIOMetadataNode backgroundIndex = new IIOMetadataNode("BackgroundIndex");
- chroma.appendChild(backgroundIndex);
- backgroundIndex.setAttribute("value", Integer.toString(colorMap.getTransparentPixel()));
- }
- }
-
- // TODO: TVPP TVPaint Project files have a MIXR chunk with a background color
- // and also a BGP1 (background pen 1?) and BGP2 chunks
-// if (extensions != null && extensions.getBackgroundColor() != 0) {
-// Color background = new Color(extensions.getBackgroundColor(), true);
-//
-// IIOMetadataNode backgroundColor = new IIOMetadataNode("BackgroundColor");
-// chroma.appendChild(backgroundColor);
-//
-// backgroundColor.setAttribute("red", Integer.toString(background.getRed()));
-// backgroundColor.setAttribute("green", Integer.toString(background.getGreen()));
-// backgroundColor.setAttribute("blue", Integer.toString(background.getBlue()));
-// }
-
- return chroma;
}
- @Override
- protected IIOMetadataNode getStandardCompressionNode() {
- if (header.compressionType() == BMHDChunk.COMPRESSION_NONE) {
- return null; // All defaults
- }
+ private static int[] bitsPerSample(Form header) {
+ int bitplanes = header.bitplanes();
- IIOMetadataNode node = new IIOMetadataNode("Compression");
-
- IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
- compressionTypeName.setAttribute("value", "RLE");
- node.appendChild(compressionTypeName);
-
- IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
- lossless.setAttribute("value", "TRUE");
- node.appendChild(lossless);
-
- return node;
- }
-
- @Override
- protected IIOMetadataNode getStandardDataNode() {
- IIOMetadataNode data = new IIOMetadataNode("Data");
-
- // PlanarConfiguration
- IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
- switch (header.formType) {
- case TYPE_DEEP:
- case TYPE_TVPP:
- case TYPE_RGB8:
- case TYPE_PBM:
- planarConfiguration.setAttribute("value", "PixelInterleaved");
- break;
- case TYPE_ILBM:
- planarConfiguration.setAttribute("value", "PlaneInterleaved");
- break;
- default:
- planarConfiguration.setAttribute("value", "Unknown " + IFFUtil.toChunkStr(header.formType));
- break;
- }
- data.appendChild(planarConfiguration);
-
- IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
- sampleFormat.setAttribute("value", colorMap != null ? "Index" : "UnsignedIntegral");
- data.appendChild(sampleFormat);
-
- // BitsPerSample
- IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
- String value = bitsPerSampleValue(header.bitplanes());
- bitsPerSample.setAttribute("value", value);
- data.appendChild(bitsPerSample);
-
- // SignificantBitsPerSample not in format
- // SampleMSB not in format
-
- return data;
- }
-
- private String bitsPerSampleValue(int bitplanes) {
switch (bitplanes) {
case 1:
case 2:
@@ -192,91 +89,47 @@ final class IFFImageMetadata extends AbstractMetadata {
case 6:
case 7:
case 8:
- return Integer.toString(bitplanes);
+ return new int[] {bitplanes};
case 24:
- return "8 8 8";
+ return new int[] {8, 8, 8};
case 25:
if (header.formType != TYPE_RGB8) {
throw new IllegalArgumentException(String.format("25 bit depth only supported for FORM type RGB8: %s", IFFUtil.toChunkStr(header.formType)));
}
- return "8 8 8 1";
+ return new int[] {8, 8, 8, 1};
case 32:
- return "8 8 8 8";
+ return new int[] {8, 8, 8, 8};
default:
throw new IllegalArgumentException("Unknown bit count: " + bitplanes);
}
}
- @Override
- protected IIOMetadataNode getStandardDimensionNode() {
- if (header.aspect() == 0) {
- return null;
+ private static PlanarConfiguration planarConfiguration(Form header) {
+ switch (header.formType) {
+ case TYPE_DEEP:
+ case TYPE_TVPP:
+ case TYPE_RGB8:
+ case TYPE_PBM:
+ return PlanarConfiguration.PixelInterleaved;
+ case TYPE_ILBM:
+ return PlanarConfiguration.PlaneInterleaved;
+ default:
+ return null;
}
-
- IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
-
- // PixelAspectRatio
- IIOMetadataNode pixelAspectRatio = new IIOMetadataNode("PixelAspectRatio");
- pixelAspectRatio.setAttribute("value", String.valueOf(header.aspect()));
- dimension.appendChild(pixelAspectRatio);
-
- // TODO: HorizontalScreenSize?
- // TODO: VerticalScreenSize?
-
- return dimension;
}
- @Override
- protected IIOMetadataNode getStandardDocumentNode() {
- IIOMetadataNode document = new IIOMetadataNode("Document");
-
- IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
- document.appendChild(formatVersion);
- formatVersion.setAttribute("value", "1.0");
-
- return document;
- }
-
- @Override
- protected IIOMetadataNode getStandardTextNode() {
- if (meta.isEmpty()) {
- return null;
+ private static List> textEntries(Form header) {
+ if (header.meta.isEmpty()) {
+ return emptyList();
}
- IIOMetadataNode text = new IIOMetadataNode("Text");
-
- // /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
- for (GenericChunk chunk : meta) {
- IIOMetadataNode node = new IIOMetadataNode("TextEntry");
- node.setAttribute("keyword", IFFUtil.toChunkStr(chunk.chunkId));
- node.setAttribute("value", new String(chunk.data, chunk.chunkId == IFF.CHUNK_UTF8 ? StandardCharsets.UTF_8 : StandardCharsets.US_ASCII));
- text.appendChild(node);
+ List> text = new ArrayList<>();
+ for (GenericChunk chunk : header.meta) {
+ text.add(new SimpleImmutableEntry<>(toChunkStr(chunk.chunkId),
+ new String(chunk.data, chunk.chunkId == IFF.CHUNK_UTF8 ? StandardCharsets.UTF_8 : StandardCharsets.US_ASCII)));
}
return text;
}
-
- @Override
- protected IIOMetadataNode getStandardTransparencyNode() {
- if ((colorMap == null || !colorMap.hasAlpha()) && header.bitplanes() != 32 && header.bitplanes() != 25) {
- return null;
- }
-
- IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
-
- if (header.bitplanes() == 25 || header.bitplanes() == 32) {
- IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
- alpha.setAttribute("value", header.premultiplied() ? "premultiplied" : "nonpremultiplied");
- transparency.appendChild(alpha);
- }
-
- if (colorMap != null && colorMap.getTransparency() == Transparency.BITMASK) {
- IIOMetadataNode transparentIndex = new IIOMetadataNode("TransparentIndex");
- transparentIndex.setAttribute("value", Integer.toString(colorMap.getTransparentPixel()));
- transparency.appendChild(transparentIndex);
- }
-
- return transparency;
- }
}
diff --git a/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java b/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java
index 6705de12..6ccc1f68 100755
--- a/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java
+++ b/imageio/imageio-iff/src/main/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageReader.java
@@ -38,14 +38,19 @@ import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.io.enc.PackBitsDecoder;
-import javax.imageio.*;
+import javax.imageio.IIOException;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
-import java.awt.color.ColorSpace;
+import java.awt.color.*;
import java.awt.image.*;
import java.io.DataInputStream;
+import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
@@ -350,9 +355,7 @@ public final class IFFImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
- init(imageIndex);
-
- return new IFFImageMetadata(header, header.colorMap());
+ return new IFFImageMetadata(getRawImageType(imageIndex), header, header.colorMap());
}
@Override
diff --git a/imageio/imageio-iff/src/test/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageMetadataTest.java b/imageio/imageio-iff/src/test/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageMetadataTest.java
index 151857e1..6d378ff2 100644
--- a/imageio/imageio-iff/src/test/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageMetadataTest.java
+++ b/imageio/imageio-iff/src/test/java/com/twelvemonkeys/imageio/plugins/iff/IFFImageMetadataTest.java
@@ -1,24 +1,37 @@
package com.twelvemonkeys.imageio.plugins.iff;
+import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
+
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import javax.imageio.IIOException;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
-import java.awt.image.IndexColorModel;
+import java.awt.image.*;
import java.nio.charset.StandardCharsets;
+import static java.awt.image.BufferedImage.*;
import static org.junit.Assert.*;
public class IFFImageMetadataTest {
+
+ private static final ImageTypeSpecifier TYPE_8_BIT_GRAY = ImageTypeSpecifiers.createFromBufferedImageType(TYPE_BYTE_GRAY);
+ private static final ImageTypeSpecifier TYPE_8_BIT_PALETTE = ImageTypeSpecifiers.createFromBufferedImageType(TYPE_BYTE_INDEXED);
+ private static final ImageTypeSpecifier TYPE_24_BIT_RGB = ImageTypeSpecifiers.createFromBufferedImageType(TYPE_3BYTE_BGR);
+ private static final ImageTypeSpecifier TYPE_32_BIT_ARGB = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
+ private static final ImageTypeSpecifier TYPE_32_BIT_ARGB_DEEP = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR_PRE);
+
@Test
public void testStandardFeatures() throws IIOException {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- final IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ final IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
// Standard metadata format
assertTrue(metadata.isStandardMetadataFormatSupported());
@@ -51,9 +64,9 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_GRAY, header, header.colorMap());
- IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(3, chroma.getLength());
@@ -78,9 +91,9 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
- IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(3, chroma.getLength());
@@ -106,9 +119,10 @@ public class IFFImageMetadataTest {
.with(new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1));
byte[] bw = {0, (byte) 0xff};
- IFFImageMetadata metadata = new IFFImageMetadata(header, new IndexColorModel(header.bitplanes(), bw.length, bw, bw, bw, header.transparentIndex()));
+ ImageTypeSpecifier fromIndexColorModel = ImageTypeSpecifiers.createFromIndexColorModel(new IndexColorModel(header.bitplanes(), bw.length, bw, bw, bw, header.transparentIndex()));
+ IFFImageMetadata metadata = new IFFImageMetadata(fromIndexColorModel, header, header.colorMap());
- IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(5, chroma.getLength());
@@ -119,7 +133,7 @@ public class IFFImageMetadataTest {
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
assertEquals("NumChannels", numChannels.getNodeName());
- assertEquals("3", numChannels.getAttribute("value"));
+ assertEquals("4", numChannels.getAttribute("value"));
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
assertEquals("BlackIsZero", blackIsZero.getNodeName());
@@ -153,9 +167,9 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
- IIOMetadataNode compression = metadata.getStandardCompressionNode();
+ IIOMetadataNode compression = getStandardNode(metadata, "Compression");
assertNotNull(compression);
assertEquals("Compression", compression.getNodeName());
assertEquals(2, compression.getLength());
@@ -176,9 +190,9 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_NONE, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
- assertNull(metadata.getStandardCompressionNode()); // No compression, all default...
+ assertNull(getStandardNode(metadata, "Compression")); // No compression, all default...
}
@Test
@@ -186,9 +200,9 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_GRAY, header, header.colorMap());
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -213,9 +227,9 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -240,9 +254,9 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 32, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_32_BIT_ARGB, header, header.colorMap());
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -269,9 +283,10 @@ public class IFFImageMetadataTest {
.with(new BMHDChunk(300, 200, i, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
byte[] rgb = new byte[2 << i]; // Colors doesn't really matter here
- IFFImageMetadata metadata = new IFFImageMetadata(header, new IndexColorModel(header.bitplanes(), rgb.length, rgb, rgb, rgb, 0));
+ ImageTypeSpecifier fromIndexColorModel = ImageTypeSpecifiers.createFromIndexColorModel(new IndexColorModel(header.bitplanes(), rgb.length, rgb, rgb, rgb, 0));
+ IFFImageMetadata metadata = new IFFImageMetadata(fromIndexColorModel, header, header.colorMap());
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -297,9 +312,9 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_PBM)
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_GRAY, header, header.colorMap());
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -324,9 +339,9 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_PBM)
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -356,10 +371,20 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(bitmapHeader);
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
- IIOMetadataNode dimension = metadata.getStandardDimensionNode();
- assertNull(dimension);
+ IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
+
+ if (dimension != null) {
+ assertEquals("Dimension", dimension.getNodeName());
+ assertEquals(1, dimension.getLength());
+
+ IIOMetadataNode imageOrientation = (IIOMetadataNode) dimension.getFirstChild();
+ assertEquals("ImageOrientation", imageOrientation.getNodeName());
+ assertEquals("Normal", imageOrientation.getAttribute("value"));
+
+ assertNull(imageOrientation.getNextSibling()); // No more children
+ }
}
@Test
@@ -368,20 +393,24 @@ public class IFFImageMetadataTest {
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
.with(new CAMGChunk(4));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
- IIOMetadataNode dimension = metadata.getStandardDimensionNode();
+ IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
// No Dimension node is okay, or one with an aspect ratio of 1.0
if (dimension != null) {
assertEquals("Dimension", dimension.getNodeName());
- assertEquals(1, dimension.getLength());
+ assertEquals(2, dimension.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
+ assertEquals("ImageOrientation", imageOrientation.getNodeName());
+ assertEquals("Normal", imageOrientation.getAttribute("value"));
+
+ assertNull(imageOrientation.getNextSibling()); // No more children
}
}
@@ -398,18 +427,22 @@ public class IFFImageMetadataTest {
.with(bitmapHeader)
.with(viewPort);
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
- IIOMetadataNode dimension = metadata.getStandardDimensionNode();
+ IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
assertNotNull(dimension);
assertEquals("Dimension", dimension.getNodeName());
- assertEquals(1, dimension.getLength());
+ assertEquals(2, dimension.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("2.0", pixelAspectRatio.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
+ assertEquals("ImageOrientation", imageOrientation.getNodeName());
+ assertEquals("Normal", imageOrientation.getAttribute("value"));
+
+ assertNull(imageOrientation.getNextSibling()); // No more children
}
@Test
@@ -425,18 +458,22 @@ public class IFFImageMetadataTest {
.with(bitmapHeader)
.with(viewPort);
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
- IIOMetadataNode dimension = metadata.getStandardDimensionNode();
+ IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
assertNotNull(dimension);
assertEquals("Dimension", dimension.getNodeName());
- assertEquals(1, dimension.getLength());
+ assertEquals(2, dimension.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("0.5", pixelAspectRatio.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
+ assertEquals("ImageOrientation", imageOrientation.getNodeName());
+ assertEquals("Normal", imageOrientation.getAttribute("value"));
+
+ assertNull(imageOrientation.getNextSibling()); // No more children
}
@Test
@@ -447,18 +484,22 @@ public class IFFImageMetadataTest {
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
.with(viewPort);
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
- IIOMetadataNode dimension = metadata.getStandardDimensionNode();
+ IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
assertNotNull(dimension);
assertEquals("Dimension", dimension.getNodeName());
- assertEquals(1, dimension.getLength());
+ assertEquals(2, dimension.getLength());
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
+ assertEquals("ImageOrientation", imageOrientation.getNodeName());
+ assertEquals("Normal", imageOrientation.getAttribute("value"));
+
+ assertNull(imageOrientation.getNextSibling()); // No more children
}
@Test
@@ -466,32 +507,33 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
- IIOMetadataNode document = metadata.getStandardDocumentNode();
+ IIOMetadataNode document = getStandardNode(metadata, "Document");
assertNotNull(document);
assertEquals("Document", document.getNodeName());
assertEquals(1, document.getLength());
- IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) document.getFirstChild();
- assertEquals("FormatVersion", pixelAspectRatio.getNodeName());
- assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
+ IIOMetadataNode formatVersion = (IIOMetadataNode) document.getFirstChild();
+ assertEquals("FormatVersion", formatVersion.getNodeName());
+ assertEquals("1.0", formatVersion.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ assertNull(formatVersion.getNextSibling()); // No more children
}
@Test
public void testStandardText() throws IIOException {
- int[] chunks = {IFF.CHUNK_ANNO, IFF.CHUNK_UTF8};
- String[] texts = {"annotation", "äñnótâtïøñ"};
+ int[] chunks = {IFF.CHUNK_ANNO, IFF.CHUNK_ANNO, IFF.CHUNK_UTF8};
+ String[] texts = {"annotation", "dupe", "äñnótâtïøñ"};
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
.with(new GenericChunk(chunks[0], texts[0].getBytes(StandardCharsets.US_ASCII)))
- .with(new GenericChunk(chunks[1], texts[1].getBytes(StandardCharsets.UTF_8)));
+ .with(new GenericChunk(chunks[1], texts[1].getBytes(StandardCharsets.US_ASCII)))
+ .with(new GenericChunk(chunks[2], texts[2].getBytes(StandardCharsets.UTF_8)));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_8_BIT_PALETTE, header, header.colorMap());
- IIOMetadataNode text = metadata.getStandardTextNode();
+ IIOMetadataNode text = getStandardNode(metadata, "Text");
assertNotNull(text);
assertEquals("Text", text.getNodeName());
assertEquals(texts.length, text.getLength());
@@ -509,10 +551,21 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_24_BIT_RGB, header, header.colorMap());
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
- assertNull(transparency); // No transparency, just defaults
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
+
+ if (transparency != null) {
+ assertEquals("Transparency", transparency.getNodeName());
+ assertEquals(1, transparency.getLength());
+
+ IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
+ assertEquals("Alpha", alpha.getNodeName());
+ assertEquals("none", alpha.getAttribute("value"));
+
+ assertNull(alpha.getNextSibling()); // No more children
+ }
+ // Otherwise, no transparency, just defaults
}
@Test
@@ -520,18 +573,18 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_ILBM)
.with(new BMHDChunk(300, 200, 32, BMHDChunk.MASK_HAS_MASK, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_32_BIT_ARGB, header, header.colorMap());
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
assertNotNull(transparency);
assertEquals("Transparency", transparency.getNodeName());
assertEquals(1, transparency.getLength());
- IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
- assertEquals("Alpha", pixelAspectRatio.getNodeName());
- assertEquals("nonpremultiplied", pixelAspectRatio.getAttribute("value"));
+ IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
+ assertEquals("Alpha", alpha.getNodeName());
+ assertEquals("nonpremultiplied", alpha.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ assertNull(alpha.getNextSibling()); // No more children
}
@Test
@@ -540,28 +593,33 @@ public class IFFImageMetadataTest {
.with(new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1));
byte[] bw = {0, (byte) 0xff};
- IFFImageMetadata metadata = new IFFImageMetadata(header, new IndexColorModel(header.bitplanes(), bw.length, bw, bw, bw, header.transparentIndex()));
+ ImageTypeSpecifier fromIndexColorModel = ImageTypeSpecifiers.createFromIndexColorModel(new IndexColorModel(header.bitplanes(), bw.length, bw, bw, bw, header.transparentIndex()));
+ IFFImageMetadata metadata = new IFFImageMetadata(fromIndexColorModel, header, header.colorMap());
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
assertNotNull(transparency);
assertEquals("Transparency", transparency.getNodeName());
- assertEquals(1, transparency.getLength());
+ assertEquals(2, transparency.getLength());
- IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
- assertEquals("TransparentIndex", pixelAspectRatio.getNodeName());
- assertEquals("1", pixelAspectRatio.getAttribute("value"));
+ IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
+ assertEquals("Alpha", alpha.getNodeName());
+ assertEquals("nonpremultiplied", alpha.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ IIOMetadataNode transparentIndex = (IIOMetadataNode) alpha.getNextSibling();
+ assertEquals("TransparentIndex", transparentIndex.getNodeName());
+ assertEquals("1", transparentIndex.getAttribute("value"));
+
+ assertNull(transparentIndex.getNextSibling()); // No more children
}
@Test
public void testStandardRGB8() throws IIOException {
Form header = Form.ofType(IFF.TYPE_RGB8)
.with(new BMHDChunk(300, 200, 25, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_32_BIT_ARGB, header, header.colorMap());
// Chroma
- IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(3, chroma.getLength());
@@ -581,7 +639,7 @@ public class IFFImageMetadataTest {
assertNull(blackIsZero.getNextSibling()); // No more children
// Data
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -601,7 +659,7 @@ public class IFFImageMetadataTest {
assertNull(bitsPerSample.getNextSibling()); // No more children
// Transparency
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
assertNotNull(transparency);
assertEquals("Transparency", transparency.getNodeName());
assertEquals(1, transparency.getLength());
@@ -624,10 +682,10 @@ public class IFFImageMetadataTest {
Form header = Form.ofType(IFF.TYPE_DEEP)
.with(new DGBLChunk(8))
.with(dpel);
- IFFImageMetadata metadata = new IFFImageMetadata(header, null);
+ IFFImageMetadata metadata = new IFFImageMetadata(TYPE_32_BIT_ARGB_DEEP, header, header.colorMap());
// Chroma
- IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(3, chroma.getLength());
@@ -649,7 +707,7 @@ public class IFFImageMetadataTest {
assertNull(blackIsZero.getNextSibling()); // No more children
// Data
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -669,7 +727,7 @@ public class IFFImageMetadataTest {
assertNull(bitsPerSample.getNextSibling()); // No more children
// Transparency
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
assertNotNull(transparency);
assertEquals("Transparency", transparency.getNodeName());
assertEquals(1, transparency.getLength());
@@ -680,4 +738,13 @@ public class IFFImageMetadataTest {
assertNull(alpha.getNextSibling()); // No more children
}
+
+ // TODO: Test RGB8 + ColorMap
+
+ private IIOMetadataNode getStandardNode(IIOMetadata metadata, String nodeName) {
+ IIOMetadataNode asTree = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
+ NodeList nodes = asTree.getElementsByTagName(nodeName);
+
+ return nodes.getLength() > 0 ? (IIOMetadataNode) nodes.item(0) : null;
+ }
}
\ No newline at end of file
diff --git a/imageio/imageio-pcx/src/main/java/com/twelvemonkeys/imageio/plugins/pcx/PCXImageReader.java b/imageio/imageio-pcx/src/main/java/com/twelvemonkeys/imageio/plugins/pcx/PCXImageReader.java
index c6e2e2d3..45bb0427 100755
--- a/imageio/imageio-pcx/src/main/java/com/twelvemonkeys/imageio/plugins/pcx/PCXImageReader.java
+++ b/imageio/imageio-pcx/src/main/java/com/twelvemonkeys/imageio/plugins/pcx/PCXImageReader.java
@@ -45,7 +45,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
-import java.awt.color.ColorSpace;
+import java.awt.color.*;
import java.awt.image.*;
import java.io.DataInput;
import java.io.DataInputStream;
@@ -377,10 +377,12 @@ public final class PCXImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
- checkBounds(imageIndex);
- readHeader();
-
- return new PCXMetadata(header, getVGAPalette());
+// checkBounds(imageIndex);
+// readHeader();
+//
+// return new PCXMetadata(header, getVGAPalette());
+ ImageTypeSpecifier rawType = getRawImageType(imageIndex);
+ return new PCXMetadata(rawType, header);
}
private IndexColorModel getVGAPalette() throws IOException {
diff --git a/imageio/imageio-pcx/src/main/java/com/twelvemonkeys/imageio/plugins/pcx/PCXMetadata.java b/imageio/imageio-pcx/src/main/java/com/twelvemonkeys/imageio/plugins/pcx/PCXMetadata.java
index cd5f6e20..68352b41 100755
--- a/imageio/imageio-pcx/src/main/java/com/twelvemonkeys/imageio/plugins/pcx/PCXMetadata.java
+++ b/imageio/imageio-pcx/src/main/java/com/twelvemonkeys/imageio/plugins/pcx/PCXMetadata.java
@@ -1,236 +1,30 @@
-/*
- * Copyright (c) 2014, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
package com.twelvemonkeys.imageio.plugins.pcx;
-import com.twelvemonkeys.imageio.AbstractMetadata;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
-import javax.imageio.metadata.IIOMetadataNode;
-import java.awt.image.IndexColorModel;
+import javax.imageio.ImageTypeSpecifier;
-final class PCXMetadata extends AbstractMetadata {
- private final PCXHeader header;
- private final IndexColorModel vgaPalette;
-
- PCXMetadata(final PCXHeader header, final IndexColorModel vgaPalette) {
- this.header = header;
- this.vgaPalette = vgaPalette;
+final class PCXMetadata extends StandardImageMetadataSupport {
+ public PCXMetadata(ImageTypeSpecifier type, PCXHeader header) {
+ super(builder(type)
+ .withPlanarConfiguration(planarConfiguration(header))
+ .withCompressionName(compressionName(header))
+ .withFormatVersion(String.valueOf(header.getVersion())));
}
- @Override
- protected IIOMetadataNode getStandardChromaNode() {
- IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
+ private static PlanarConfiguration planarConfiguration(PCXHeader header) {
+ System.out.println("header = " + header);
+ return header.getChannels() > 1 ? PlanarConfiguration.LineInterleaved : null;
+ }
- IndexColorModel palette = null;
- boolean gray = false;
-
- IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
- switch (header.getBitsPerPixel()) {
- case 1:
- case 2:
- case 4:
- palette = header.getEGAPalette();
- csType.setAttribute("name", "RGB");
- break;
- case 8:
- // We may have IndexColorModel here for 1 channel images
- if (header.getChannels() == 1 && vgaPalette != null) {
- palette = vgaPalette;
- csType.setAttribute("name", "RGB");
- break;
- }
- if (header.getChannels() == 1) {
- csType.setAttribute("name", "GRAY");
- gray = true;
- break;
- }
- csType.setAttribute("name", "RGB");
- break;
-
- case 24:
- // Some sources says this is possible... Untested.
- csType.setAttribute("name", "RGB");
- break;
-
- default:
- csType.setAttribute("name", "Unknown");
+ private static String compressionName(PCXHeader header) {
+ switch (header.getCompression()) {
+ case PCX.COMPRESSION_NONE:
+ return "None";
+ case PCX.COMPRESSION_RLE:
+ return "RLE";
}
- chroma.appendChild(csType);
-
- // NOTE: Channels in chroma node reflects channels in color model, not data! (see data node)
- IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
- numChannels.setAttribute("value", gray ? "1" : "3");
- chroma.appendChild(numChannels);
-
- IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
- blackIsZero.setAttribute("value", "TRUE");
- chroma.appendChild(blackIsZero);
-
- if (palette != null) {
- IIOMetadataNode paletteNode = new IIOMetadataNode("Palette");
- chroma.appendChild(paletteNode);
-
- for (int i = 0; i < palette.getMapSize(); i++) {
- IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
- paletteEntry.setAttribute("index", Integer.toString(i));
-
- paletteEntry.setAttribute("red", Integer.toString(palette.getRed(i)));
- paletteEntry.setAttribute("green", Integer.toString(palette.getGreen(i)));
- paletteEntry.setAttribute("blue", Integer.toString(palette.getBlue(i)));
-
- paletteNode.appendChild(paletteEntry);
- }
- }
-
- return chroma;
- }
-
- // No compression
-
- @Override
- protected IIOMetadataNode getStandardCompressionNode() {
- if (header.getCompression() != PCX.COMPRESSION_NONE) {
- IIOMetadataNode node = new IIOMetadataNode("Compression");
-
- IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
- compressionTypeName.setAttribute("value", header.getCompression() == PCX.COMPRESSION_RLE ? "RLE" : "Uknown");
- node.appendChild(compressionTypeName);
-
- IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
- lossless.setAttribute("value", "TRUE");
- node.appendChild(lossless);
-
- return node;
- }
-
- return null;
- }
-
- @Override
- protected IIOMetadataNode getStandardDataNode() {
- IIOMetadataNode node = new IIOMetadataNode("Data");
-
- // Planar configuration only makes sense for multi-channel images
- if (header.getChannels() > 1) {
- IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
- planarConfiguration.setAttribute("value", "LineInterleaved");
- node.appendChild(planarConfiguration);
- }
-
- IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
-
- switch (header.getBitsPerPixel()) {
- case 1:
- case 2:
- case 4:
- sampleFormat.setAttribute("value", "Index");
- break;
- case 8:
- if (header.getChannels() == 1 && vgaPalette != null) {
- sampleFormat.setAttribute("value", "Index");
- break;
- }
- // Else fall through for GRAY
- default:
- sampleFormat.setAttribute("value", "UnsignedIntegral");
- break;
- }
-
- node.appendChild(sampleFormat);
-
- IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
- bitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(header.getBitsPerPixel())));
- node.appendChild(bitsPerSample);
-
- IIOMetadataNode significantBitsPerSample = new IIOMetadataNode("SignificantBitsPerSample");
- significantBitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(header.getBitsPerPixel())));
- node.appendChild(significantBitsPerSample);
-
- IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
- sampleMSB.setAttribute("value", createListValue(header.getChannels(), "0"));
-
- return node;
- }
-
- private String createListValue(final int itemCount, final String... values) {
- StringBuilder buffer = new StringBuilder();
-
- for (int i = 0; i < itemCount; i++) {
- if (buffer.length() > 0) {
- buffer.append(' ');
- }
-
- buffer.append(values[i % values.length]);
- }
-
- return buffer.toString();
- }
-
- @Override
- protected IIOMetadataNode getStandardDimensionNode() {
- IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
-
- IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
- imageOrientation.setAttribute("value", "Normal");
- dimension.appendChild(imageOrientation);
-
- return dimension;
- }
-
- @Override
- protected IIOMetadataNode getStandardDocumentNode() {
- IIOMetadataNode dimension = new IIOMetadataNode("Document");
-
- IIOMetadataNode imageOrientation = new IIOMetadataNode("FormatVersion");
- imageOrientation.setAttribute("value", String.valueOf(header.getVersion()));
- dimension.appendChild(imageOrientation);
-
- return dimension;
- }
-
- // No text node
-
- // No tiling
-
- @Override
- protected IIOMetadataNode getStandardTransparencyNode() {
- // NOTE: There doesn't seem to be any god way to determine transparency, other than by convention
- // 1 channel: Gray, 2 channel: Gray + Alpha, 3 channel: RGB, 4 channel: RGBA (hopefully never CMYK...)
-
- IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
-
- IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
- alpha.setAttribute("value", header.getChannels() == 1 || header.getChannels() == 3 ? "none" : "nonpremultiplied");
- transparency.appendChild(alpha);
-
- return transparency;
+ return "Uknown";
}
}
diff --git a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java
index e2dda333..647a4f10 100644
--- a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java
+++ b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTImageReader.java
@@ -67,18 +67,28 @@ import com.twelvemonkeys.io.enc.Decoder;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.io.enc.PackBitsDecoder;
-import javax.imageio.*;
+import javax.imageio.IIOException;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
-import java.awt.color.ColorSpace;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Area;
+import java.awt.color.*;
+import java.awt.geom.*;
import java.awt.image.*;
-import java.io.*;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
-import java.util.*;
/**
* Reader for Apple Mac Paint Picture (PICT) format.
@@ -2611,7 +2621,9 @@ public final class PICTImageReader extends ImageReaderBase {
return getYPtCoord(getPICTFrame().height);
}
- public Iterator getImageTypes(int pIndex) {
+ public Iterator getImageTypes(int imageIndex) throws IOException {
+ checkBounds(imageIndex);
+
// TODO: The images look slightly different in Preview.. Could indicate the color space is wrong...
return Collections.singletonList(
ImageTypeSpecifiers.createPacked(
@@ -2623,10 +2635,10 @@ public final class PICTImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
- checkBounds(imageIndex);
+ ImageTypeSpecifier rawType = getRawImageType(imageIndex);
getPICTFrame(); // TODO: Would probably be better to use readPictHeader here, but it isn't cached
- return new PICTMetadata(version, screenImageXRatio, screenImageYRatio);
+ return new PICTMetadata(rawType, version, screenImageXRatio, screenImageYRatio);
}
protected static void showIt(final BufferedImage pImage, final String pTitle) {
diff --git a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTMetadata.java b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTMetadata.java
index 8a882a48..6ffbc481 100644
--- a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTMetadata.java
+++ b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pict/PICTMetadata.java
@@ -1,8 +1,38 @@
+/*
+ * Copyright (c) 2022, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
package com.twelvemonkeys.imageio.plugins.pict;
-import com.twelvemonkeys.imageio.AbstractMetadata;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
-import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.ImageTypeSpecifier;
/**
* PICTMetadata.
@@ -11,82 +41,13 @@ import javax.imageio.metadata.IIOMetadataNode;
* @author last modified by $Author: haraldk$
* @version $Id: PICTMetadata.java,v 1.0 23/03/2021 haraldk Exp$
*/
-public class PICTMetadata extends AbstractMetadata {
-
- private final int version;
- private final double screenImageXRatio;
- private final double screenImageYRatio;
-
- PICTMetadata(final int version, final double screenImageXRatio, final double screenImageYRatio) {
- this.version = version;
- this.screenImageXRatio = screenImageXRatio;
- this.screenImageYRatio = screenImageYRatio;
- }
-
- @Override
- protected IIOMetadataNode getStandardChromaNode() {
- IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
-
- IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
- chroma.appendChild(csType);
- csType.setAttribute("name", "RGB");
-
- // NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
- IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
- chroma.appendChild(numChannels);
- numChannels.setAttribute("value", "3");
-
- IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
- chroma.appendChild(blackIsZero);
- blackIsZero.setAttribute("value", "TRUE");
-
- return chroma;
- }
-
- @Override
- protected IIOMetadataNode getStandardDimensionNode() {
- if (screenImageXRatio > 0.0d && screenImageYRatio > 0.0d) {
- IIOMetadataNode node = new IIOMetadataNode("Dimension");
- double ratio = screenImageXRatio / screenImageYRatio;
- IIOMetadataNode subNode = new IIOMetadataNode("PixelAspectRatio");
- subNode.setAttribute("value", "" + ratio);
- node.appendChild(subNode);
-
- return node;
- }
- return null;
- }
-
- @Override
- protected IIOMetadataNode getStandardDataNode() {
- IIOMetadataNode data = new IIOMetadataNode("Data");
-
- // As this is a vector-ish format, with possibly multiple regions of pixel data, this makes no sense... :-P
- // This is, however, consistent with the getRawImageTyp/getImageTypes
-
- IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
- planarConfiguration.setAttribute("value", "PixelInterleaved");
- data.appendChild(planarConfiguration);
-
- IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
- sampleFormat.setAttribute("value", "UnsignedIntegral");
- data.appendChild(sampleFormat);
-
- IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
- bitsPerSample.setAttribute("value", "32");
- data.appendChild(bitsPerSample);
-
- return data;
- }
-
- @Override
- protected IIOMetadataNode getStandardDocumentNode() {
- IIOMetadataNode document = new IIOMetadataNode("Document");
-
- IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
- document.appendChild(formatVersion);
- formatVersion.setAttribute("value", Integer.toString(version));
-
- return document;
+final class PICTMetadata extends StandardImageMetadataSupport {
+ PICTMetadata(final ImageTypeSpecifier type, final int version, final double screenImageXRatio, final double screenImageYRatio) {
+ super(builder(type)
+ .withPixelAspectRatio(screenImageXRatio > 0.0d && screenImageYRatio > 0.0d ? screenImageXRatio / screenImageYRatio : 1)
+ .withFormatVersion(Integer.toString(version))
+ );
+ // As this is a vector-ish format, some of the data makes no sense... :-P
+ // It is, however, consistent with the getRawImageTyp/getImageTypes
}
}
diff --git a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGImageReader.java b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGImageReader.java
index 1c697054..c16e42dd 100644
--- a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGImageReader.java
+++ b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGImageReader.java
@@ -1,3 +1,33 @@
+/*
+ * Copyright (c) 2021, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
package com.twelvemonkeys.imageio.plugins.pntg;
import com.twelvemonkeys.imageio.ImageReaderBase;
@@ -34,7 +64,7 @@ public final class PNTGImageReader extends ImageReaderBase {
private static final Set IMAGE_TYPES =
Collections.singleton(ImageTypeSpecifiers.createIndexed(new int[] {-1, 0}, false, -1, 1, DataBuffer.TYPE_BYTE));
- protected PNTGImageReader(final ImageReaderSpi provider) {
+ PNTGImageReader(final ImageReaderSpi provider) {
super(provider);
}
@@ -123,9 +153,7 @@ public final class PNTGImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
- checkBounds(imageIndex);
-
- return new PNTGMetadata();
+ return new PNTGMetadata(getRawImageType(imageIndex));
}
private void readHeader() throws IOException {
diff --git a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGImageReaderSpi.java b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGImageReaderSpi.java
index 27f852ec..3c7b1955 100644
--- a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGImageReaderSpi.java
+++ b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGImageReaderSpi.java
@@ -1,3 +1,33 @@
+/*
+ * Copyright (c) 2021, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
package com.twelvemonkeys.imageio.plugins.pntg;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
diff --git a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGMetadata.java b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGMetadata.java
index 90f22b95..70bdccdb 100644
--- a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGMetadata.java
+++ b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGMetadata.java
@@ -1,8 +1,38 @@
+/*
+ * Copyright (c) 2022, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
package com.twelvemonkeys.imageio.plugins.pntg;
-import com.twelvemonkeys.imageio.AbstractMetadata;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
-import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.ImageTypeSpecifier;
/**
* PNTGMetadata.
@@ -11,76 +41,11 @@ import javax.imageio.metadata.IIOMetadataNode;
* @author last modified by $Author: haraldk$
* @version $Id: PNTGMetadata.java,v 1.0 23/03/2021 haraldk Exp$
*/
-public class PNTGMetadata extends AbstractMetadata {
- @Override
- protected IIOMetadataNode getStandardChromaNode() {
- IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
-
- IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
- chroma.appendChild(csType);
- csType.setAttribute("name", "GRAY");
-
- // NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
- IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
- chroma.appendChild(numChannels);
- numChannels.setAttribute("value", "1");
-
- IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
- chroma.appendChild(blackIsZero);
- blackIsZero.setAttribute("value", "FALSE");
-
- return chroma;
- }
-
- @Override
- protected IIOMetadataNode getStandardCompressionNode() {
- IIOMetadataNode compressionNode = new IIOMetadataNode("Compression");
-
- IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
- compressionTypeName.setAttribute("value", "PackBits"); // RLE?
- compressionNode.appendChild(compressionTypeName);
- compressionNode.appendChild(new IIOMetadataNode("Lossless"));
- // "value" defaults to TRUE
-
- return compressionNode;
- }
-
- @Override
- protected IIOMetadataNode getStandardDataNode() {
- IIOMetadataNode data = new IIOMetadataNode("Data");
-
- // PlanarConfiguration
- IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
- planarConfiguration.setAttribute("value", "PixelInterleaved");
- data.appendChild(planarConfiguration);
-
- IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
- sampleFormat.setAttribute("value", "UnsignedIntegral");
- data.appendChild(sampleFormat);
-
- IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
- bitsPerSample.setAttribute("value", "1");
- data.appendChild(bitsPerSample);
-
- return data;
- }
-
- @Override
- protected IIOMetadataNode getStandardDocumentNode() {
- IIOMetadataNode document = new IIOMetadataNode("Document");
-
- IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
- document.appendChild(formatVersion);
- formatVersion.setAttribute("value", "1.0");
-
- // TODO: We could get the file creation time from MacBinary header here...
-
- return document;
- }
-
- @Override
- protected IIOMetadataNode getStandardTextNode() {
- // TODO: We could get the file name from MacBinary header here...
- return super.getStandardTextNode();
+final class PNTGMetadata extends StandardImageMetadataSupport {
+ public PNTGMetadata(ImageTypeSpecifier type) {
+ super(builder(type)
+ .withBlackIsZero(false)
+ .withCompressionName("PackBits")
+ .withFormatVersion("1.0"));
}
}
diff --git a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGProviderInfo.java b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGProviderInfo.java
index a30edcae..aad07ae7 100644
--- a/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGProviderInfo.java
+++ b/imageio/imageio-pict/src/main/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGProviderInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Harald Kuhr
+ * Copyright (c) 2021, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/imageio/imageio-pict/src/test/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGMetadataTest.java b/imageio/imageio-pict/src/test/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGMetadataTest.java
index 27cf8f9a..ba757b99 100644
--- a/imageio/imageio-pict/src/test/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGMetadataTest.java
+++ b/imageio/imageio-pict/src/test/java/com/twelvemonkeys/imageio/plugins/pntg/PNTGMetadataTest.java
@@ -1,7 +1,11 @@
package com.twelvemonkeys.imageio.plugins.pntg;
+import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
+
import org.junit.Test;
+import java.awt.image.*;
+
/**
* PNTGMetadataTest.
*
@@ -12,6 +16,6 @@ import org.junit.Test;
public class PNTGMetadataTest {
@Test
public void testCreate() {
- new PNTGMetadata();
+ new PNTGMetadata(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY));
}
}
\ No newline at end of file
diff --git a/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMImageReader.java b/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMImageReader.java
index f432f7c9..3cf4f51b 100755
--- a/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMImageReader.java
+++ b/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMImageReader.java
@@ -43,7 +43,7 @@ import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
-import java.awt.color.ColorSpace;
+import java.awt.color.*;
import java.awt.image.*;
import java.io.DataInput;
import java.io.DataInputStream;
@@ -468,10 +468,7 @@ public final class PNMImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
- checkBounds(imageIndex);
- readHeader();
-
- return new PNMMetadata(header);
+ return new PNMMetadata(getRawImageType(imageIndex), header);
}
public static void main(String[] args) throws IOException {
diff --git a/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMMetadata.java b/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMMetadata.java
index cc11bc03..96dfb17b 100755
--- a/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMMetadata.java
+++ b/imageio/imageio-pnm/src/main/java/com/twelvemonkeys/imageio/plugins/pnm/PNMMetadata.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Harald Kuhr
+ * Copyright (c) 2022, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,92 +30,35 @@
package com.twelvemonkeys.imageio.plugins.pnm;
-import com.twelvemonkeys.imageio.AbstractMetadata;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
+import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadataNode;
-import java.awt.*;
-import java.awt.image.DataBuffer;
+import java.awt.image.*;
import java.nio.ByteOrder;
-final class PNMMetadata extends AbstractMetadata {
+/**
+ * PNMMetadata.
+ *
+ * @author Harald Kuhr
+ */
+final class PNMMetadata extends StandardImageMetadataSupport {
private final PNMHeader header;
- PNMMetadata(final PNMHeader header) {
+ PNMMetadata(ImageTypeSpecifier type, PNMHeader header) {
+ super(builder(type)
+ .withColorSpaceType(colorSpace(header))
+ // TODO: Might make sense to set gamma?
+ .withBlackIsZero(header.getTupleType() != TupleType.BLACKANDWHITE_WHITE_IS_ZERO)
+ .withSignificantBitsPerSample(significantBits(header))
+ .withSampleMSB(header.getByteOrder() == ByteOrder.BIG_ENDIAN ? 0 : header.getBitsPerSample() - 1)
+ .withOrientation(orientation(header))
+ );
+
this.header = header;
}
- @Override
- protected IIOMetadataNode getStandardChromaNode() {
- IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
-
- IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
- switch (header.getTupleType()) {
- case BLACKANDWHITE:
- case BLACKANDWHITE_ALPHA:
- case BLACKANDWHITE_WHITE_IS_ZERO:
- case GRAYSCALE:
- case GRAYSCALE_ALPHA:
- csType.setAttribute("name", "GRAY");
- break;
- case RGB:
- case RGB_ALPHA:
- csType.setAttribute("name", "RGB");
- break;
- case CMYK:
- case CMYK_ALPHA:
- csType.setAttribute("name", "CMYK");
- break;
- }
-
- if (csType.getAttribute("name") != null) {
- chroma.appendChild(csType);
- }
-
- IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
- numChannels.setAttribute("value", Integer.toString(header.getSamplesPerPixel()));
- chroma.appendChild(numChannels);
-
- // TODO: Might make sense to set gamma?
-
- IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
- blackIsZero.setAttribute("value", header.getTupleType() == TupleType.BLACKANDWHITE_WHITE_IS_ZERO
- ? "FALSE"
- : "TRUE");
- chroma.appendChild(blackIsZero);
-
- return chroma;
- }
-
- // No compression
-
- @Override
- protected IIOMetadataNode getStandardDataNode() {
- IIOMetadataNode node = new IIOMetadataNode("Data");
-
- IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
- sampleFormat.setAttribute("value", header.getTransferType() == DataBuffer.TYPE_FLOAT
- ? "Real"
- : "UnsignedIntegral");
- node.appendChild(sampleFormat);
-
- IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
- bitsPerSample.setAttribute("value", createListValue(header.getSamplesPerPixel(), Integer.toString(header.getBitsPerSample())));
- node.appendChild(bitsPerSample);
-
- IIOMetadataNode significantBitsPerSample = new IIOMetadataNode("SignificantBitsPerSample");
- significantBitsPerSample.setAttribute("value", createListValue(header.getSamplesPerPixel(), Integer.toString(computeSignificantBits())));
- node.appendChild(significantBitsPerSample);
-
- String msb = header.getByteOrder() == ByteOrder.BIG_ENDIAN
- ? "0"
- : Integer.toString(header.getBitsPerSample() - 1);
- IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
- sampleMSB.setAttribute("value", createListValue(header.getSamplesPerPixel(), msb));
-
- return node;
- }
-
- private int computeSignificantBits() {
+ private static int significantBits(PNMHeader header) {
if (header.getTransferType() == DataBuffer.TYPE_FLOAT) {
return header.getBitsPerSample();
}
@@ -132,38 +75,30 @@ final class PNMMetadata extends AbstractMetadata {
return significantBits;
}
- private String createListValue(final int itemCount, final String... values) {
- StringBuilder buffer = new StringBuilder();
-
- for (int i = 0; i < itemCount; i++) {
- if (buffer.length() > 0) {
- buffer.append(' ');
- }
-
- buffer.append(values[i % values.length]);
+ private static ColorSpaceType colorSpace(PNMHeader header) {
+ switch (header.getTupleType()) {
+ case BLACKANDWHITE:
+ case BLACKANDWHITE_ALPHA:
+ case BLACKANDWHITE_WHITE_IS_ZERO:
+ case GRAYSCALE:
+ case GRAYSCALE_ALPHA:
+ return ColorSpaceType.GRAY;
+ default:
+ return null; // Fall back to color model's type
}
-
- return buffer.toString();
}
- @Override
- protected IIOMetadataNode getStandardDimensionNode() {
- IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
-
- IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
- imageOrientation.setAttribute("value",
- header.getFileType() == PNM.PFM_GRAY || header.getFileType() == PNM.PFM_RGB
- ? "FlipH"
- : "Normal");
- dimension.appendChild(imageOrientation);
-
- return dimension;
+ private static ImageOrientation orientation(PNMHeader header) {
+ // For some reason, the float values are stored bottom-up
+ return header.getFileType() == PNM.PFM_GRAY || header.getFileType() == PNM.PFM_RGB
+ ? ImageOrientation.FlipH
+ : ImageOrientation.Normal;
}
- // No document node
-
@Override
protected IIOMetadataNode getStandardTextNode() {
+ // TODO: Could avoid this override, by changing the StandardImageMetadataSupport to
+ // use List> instead of Map (we use duplicate "comment"s).
if (!header.getComments().isEmpty()) {
IIOMetadataNode text = new IIOMetadataNode("Text");
@@ -179,17 +114,4 @@ final class PNMMetadata extends AbstractMetadata {
return null;
}
-
- // No tiling
-
- @Override
- protected IIOMetadataNode getStandardTransparencyNode() {
- IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
-
- IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
- alpha.setAttribute("value", header.getTransparency() == Transparency.OPAQUE ? "none" : "nonpremultiplied");
- transparency.appendChild(alpha);
-
- return transparency;
- }
}
diff --git a/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIHeader.java b/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIHeader.java
index 8b893c06..d61fc44b 100755
--- a/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIHeader.java
+++ b/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIHeader.java
@@ -33,7 +33,7 @@ package com.twelvemonkeys.imageio.plugins.sgi;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
final class SGIHeader {
private int compression;
@@ -157,6 +157,6 @@ final class SGIHeader {
}
}
- return new String(bytes, 0, len, Charset.forName("ASCII"));
+ return new String(bytes, 0, len, StandardCharsets.US_ASCII);
}
}
diff --git a/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIImageReader.java b/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIImageReader.java
index b173e910..67a1e5ca 100755
--- a/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIImageReader.java
+++ b/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIImageReader.java
@@ -45,7 +45,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
-import java.awt.color.ColorSpace;
+import java.awt.color.*;
import java.awt.image.*;
import java.io.DataInput;
import java.io.DataInputStream;
@@ -378,11 +378,10 @@ public final class SGIImageReader extends ImageReaderBase {
imageInput.seek(imageInput.getFlushedPosition());
}
- @Override public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
- checkBounds(imageIndex);
- readHeader();
-
- return new SGIMetadata(header);
+ @Override
+ public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
+ ImageTypeSpecifier rawType = getRawImageType(imageIndex);
+ return new SGIMetadata(rawType, header);
}
public static void main(String[] args) throws IOException {
diff --git a/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIMetadata.java b/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIMetadata.java
index 42cb005a..8f613d46 100755
--- a/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIMetadata.java
+++ b/imageio/imageio-sgi/src/main/java/com/twelvemonkeys/imageio/plugins/sgi/SGIMetadata.java
@@ -1,209 +1,39 @@
-/*
- * Copyright (c) 2014, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
package com.twelvemonkeys.imageio.plugins.sgi;
-import com.twelvemonkeys.imageio.AbstractMetadata;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
-import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.ImageTypeSpecifier;
-final class SGIMetadata extends AbstractMetadata {
- private final SGIHeader header;
-
- SGIMetadata(final SGIHeader header) {
- this.header = header;
+final class SGIMetadata extends StandardImageMetadataSupport {
+ public SGIMetadata(ImageTypeSpecifier type, SGIHeader header) {
+ super(builder(type)
+ .withSignificantBitsPerSample(computeSignificantBits(header))
+ .withCompressionName(compressionName(header))
+ .withOrientation(ImageOrientation.FlipV)
+ .withTextEntry("DocumentName", header.getName())
+ );
}
- @Override
- protected IIOMetadataNode getStandardChromaNode() {
- IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
-
- // NOTE: There doesn't seem to be any god way to determine color space, other than by convention
- // 1 channel: Gray, 2 channel: Gray + Alpha, 3 channel: RGB, 4 channel: RGBA (hopefully never CMYK...)
- IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
- switch (header.getColorMode()) {
- case SGI.COLORMODE_NORMAL:
- switch (header.getChannels()) {
- case 1:
- case 2:
- csType.setAttribute("name", "GRAY");
- break;
- case 3:
- case 4:
- csType.setAttribute("name", "RGB");
- break;
- default:
- csType.setAttribute("name", Integer.toHexString(header.getChannels()).toUpperCase() + "CLR");
- break;
- }
- break;
-
- // SGIIMAGE.TXT describes these as RGB
- case SGI.COLORMODE_DITHERED:
- case SGI.COLORMODE_SCREEN:
- case SGI.COLORMODE_COLORMAP:
- csType.setAttribute("name", "RGB");
- break;
- }
-
- if (csType.getAttribute("name") != null) {
- chroma.appendChild(csType);
- }
-
- IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
- numChannels.setAttribute("value", Integer.toString(header.getChannels()));
- chroma.appendChild(numChannels);
-
- IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
- blackIsZero.setAttribute("value", "TRUE");
- chroma.appendChild(blackIsZero);
-
- return chroma;
- }
-
- // No compression
-
- @Override
- protected IIOMetadataNode getStandardCompressionNode() {
- if (header.getCompression() != SGI.COMPRESSION_NONE) {
- IIOMetadataNode node = new IIOMetadataNode("Compression");
-
- IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
- compressionTypeName.setAttribute("value", header.getCompression() == SGI.COMPRESSION_RLE
- ? "RLE"
- : "Uknown");
- node.appendChild(compressionTypeName);
-
- IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
- lossless.setAttribute("value", "TRUE");
- node.appendChild(lossless);
-
- return node;
- }
-
- return null;
- }
-
- @Override
- protected IIOMetadataNode getStandardDataNode() {
- IIOMetadataNode node = new IIOMetadataNode("Data");
-
- IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
- sampleFormat.setAttribute("value", "UnsignedIntegral");
- node.appendChild(sampleFormat);
-
- IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
- bitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(header.getBytesPerPixel() * 8)));
- node.appendChild(bitsPerSample);
-
- IIOMetadataNode significantBitsPerSample = new IIOMetadataNode("SignificantBitsPerSample");
- significantBitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(computeSignificantBits())));
- node.appendChild(significantBitsPerSample);
-
- IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
- sampleMSB.setAttribute("value", createListValue(header.getChannels(), "0"));
-
- return node;
- }
-
- private int computeSignificantBits() {
- int significantBits = 0;
-
+ private static int computeSignificantBits(SGIHeader header) {
int maxSample = header.getMaxValue();
- while (maxSample > 0) {
- maxSample >>>= 1;
+ int significantBits = 1;
+
+ while ((maxSample >>>= 1) != 0) {
significantBits++;
}
return significantBits;
}
- private String createListValue(final int itemCount, final String... values) {
- StringBuilder buffer = new StringBuilder();
-
- for (int i = 0; i < itemCount; i++) {
- if (buffer.length() > 0) {
- buffer.append(' ');
- }
-
- buffer.append(values[i % values.length]);
+ private static String compressionName(SGIHeader header) {
+ switch (header.getCompression()) {
+ case SGI.COMPRESSION_NONE:
+ return "None";
+ case SGI.COMPRESSION_RLE:
+ return "RLE";
}
- return buffer.toString();
- }
-
- @Override
- protected IIOMetadataNode getStandardDimensionNode() {
- IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
-
- IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
- imageOrientation.setAttribute("value", "FlipV");
- dimension.appendChild(imageOrientation);
-
- return dimension;
- }
-
- // No document node
-
- @Override
- protected IIOMetadataNode getStandardTextNode() {
- if (!header.getName().isEmpty()) {
- IIOMetadataNode text = new IIOMetadataNode("Text");
-
- IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
- textEntry.setAttribute("keyword", "name");
- textEntry.setAttribute("value", header.getName());
- text.appendChild(textEntry);
-
- return text;
- }
-
- return null;
- }
-
- // No tiling
-
- @Override
- protected IIOMetadataNode getStandardTransparencyNode() {
- // NOTE: There doesn't seem to be any god way to determine transparency, other than by convention
- // 1 channel: Gray, 2 channel: Gray + Alpha, 3 channel: RGB, 4 channel: RGBA (hopefully never CMYK...)
-
- IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
-
- IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
- alpha.setAttribute("value", header.getChannels() == 1 || header.getChannels() == 3
- ? "none"
- : "nonpremultiplied");
- transparency.appendChild(alpha);
-
- return transparency;
+ return "Uknown";
}
}
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAHeader.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAHeader.java
index ec0fbf4f..5fcb674e 100755
--- a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAHeader.java
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAHeader.java
@@ -31,18 +31,16 @@
package com.twelvemonkeys.imageio.plugins.tga;
import javax.imageio.IIOException;
+import javax.imageio.ImageTypeSpecifier;
import javax.imageio.stream.ImageInputStream;
-import java.awt.image.ColorModel;
-import java.awt.image.IndexColorModel;
-import java.awt.image.RenderedImage;
+import java.awt.image.*;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static com.twelvemonkeys.lang.Validate.notNull;
-import static java.awt.color.ColorSpace.TYPE_GRAY;
-import static java.awt.color.ColorSpace.TYPE_RGB;
+import static java.awt.color.ColorSpace.*;
final class TGAHeader {
@@ -118,10 +116,14 @@ final class TGAHeader {
'}';
}
- static TGAHeader from(final RenderedImage image, final boolean compressed) {
- notNull(image, "image");
+ static TGAHeader from(final ImageTypeSpecifier type, final boolean compressed) {
+ return from(type, 0, 0, compressed);
+ }
- ColorModel colorModel = image.getColorModel();
+ static TGAHeader from(final ImageTypeSpecifier type, int width, int height, final boolean compressed) {
+ notNull(type, "type");
+
+ ColorModel colorModel = type.getColorModel();
IndexColorModel colorMap = colorModel instanceof IndexColorModel ? (IndexColorModel) colorModel : null;
TGAHeader header = new TGAHeader();
@@ -135,8 +137,8 @@ final class TGAHeader {
header.x = 0;
header.y = 0;
- header.width = image.getWidth(); // TODO: Param source region/subsampling might affect this
- header.height = image.getHeight(); // // TODO: Param source region/subsampling might affect this
+ header.width = width;
+ header.height = height;
header.pixelDepth = colorModel.getPixelSize() == 15 ? 16 : colorModel.getPixelSize();
header.origin = TGA.ORIGIN_UPPER_LEFT; // TODO: Allow parameter to control this?
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageReader.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageReader.java
index e819ece7..79bb5e7b 100755
--- a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageReader.java
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageReader.java
@@ -47,7 +47,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
-import java.awt.color.ColorSpace;
+import java.awt.color.*;
import java.awt.image.*;
import java.io.DataInput;
import java.io.File;
@@ -538,10 +538,8 @@ final class TGAImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
- checkBounds(imageIndex);
- readHeader();
-
- return new TGAMetadata(header, extensions);
+ ImageTypeSpecifier rawType = getRawImageType(imageIndex);
+ return new TGAMetadata(rawType, header, extensions);
}
public static void main(String[] args) throws IOException {
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriter.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriter.java
index 9dbedc87..d1c4763c 100644
--- a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriter.java
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriter.java
@@ -37,12 +37,16 @@ import com.twelvemonkeys.io.LittleEndianDataOutputStream;
import com.twelvemonkeys.io.enc.EncoderStream;
import com.twelvemonkeys.lang.Validate;
-import javax.imageio.*;
+import javax.imageio.IIOException;
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.ImageWriteParam;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
-import java.awt.color.ColorSpace;
+import java.awt.color.*;
import java.awt.image.*;
import java.io.DataOutput;
import java.io.File;
@@ -65,8 +69,7 @@ final class TGAImageWriter extends ImageWriterBase {
public IIOMetadata getDefaultImageMetadata(final ImageTypeSpecifier imageType, final ImageWriteParam param) {
Validate.notNull(imageType, "imageType");
- TGAHeader header = TGAHeader.from(imageType.createBufferedImage(1, 1), isRLE(param, null));
- return new TGAMetadata(header, null);
+ return new TGAMetadata(imageType, TGAHeader.from(imageType, isRLE(param, null)), null);
}
@Override
@@ -107,7 +110,8 @@ final class TGAImageWriter extends ImageWriterBase {
final boolean compressed = isRLE(param, image.getMetadata());
RenderedImage renderedImage = image.getRenderedImage();
- TGAHeader header = TGAHeader.from(renderedImage, compressed);
+ ImageTypeSpecifier type = ImageTypeSpecifiers.createFromRenderedImage(renderedImage);
+ TGAHeader header = TGAHeader.from(type, renderedImage.getWidth(), renderedImage.getHeight(), compressed);
header.write(imageOutput);
@@ -117,7 +121,7 @@ final class TGAImageWriter extends ImageWriterBase {
? ImageTypeSpecifiers.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {2, 1, 0, 3}, DataBuffer.TYPE_BYTE, true, false).createBufferedImage(renderedImage.getWidth(), 1).getRaster()
: renderedImage.getSampleModel().getTransferType() == DataBuffer.TYPE_INT
? ImageTypeSpecifiers.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {2, 1, 0}, DataBuffer.TYPE_BYTE, false, false).createBufferedImage(renderedImage.getWidth(), 1).getRaster()
- : ImageTypeSpecifier.createFromRenderedImage(renderedImage).createBufferedImage(renderedImage.getWidth(), 1).getRaster();
+ : type.createBufferedImage(renderedImage.getWidth(), 1).getRaster();
final DataBuffer buffer = rowRaster.getDataBuffer();
@@ -135,7 +139,7 @@ final class TGAImageWriter extends ImageWriterBase {
break;
}
- DataOutput imageOutput = compressed ? createRLEStream(header, this.imageOutput) : this.imageOutput;
+ DataOutput imageOutput = compressed ? createRLEStream(this.imageOutput, header.getPixelDepth()) : this.imageOutput;
switch (buffer.getDataType()) {
case DataBuffer.TYPE_BYTE:
@@ -174,8 +178,8 @@ final class TGAImageWriter extends ImageWriterBase {
processImageComplete();
}
- private static LittleEndianDataOutputStream createRLEStream(final TGAHeader header, final ImageOutputStream stream) {
- return new LittleEndianDataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(stream), new RLEEncoder(header.getPixelDepth())));
+ private static LittleEndianDataOutputStream createRLEStream(final ImageOutputStream stream, int pixelDepth) {
+ return new LittleEndianDataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(stream), new RLEEncoder(pixelDepth)));
}
// TODO: Refactor to common util
diff --git a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadata.java b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadata.java
index 98a0a74f..08654426 100755
--- a/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadata.java
+++ b/imageio/imageio-tga/src/main/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadata.java
@@ -1,340 +1,83 @@
-/*
- * Copyright (c) 2014, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
package com.twelvemonkeys.imageio.plugins.tga;
-import com.twelvemonkeys.imageio.AbstractMetadata;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
-import javax.imageio.metadata.IIOMetadataNode;
-import java.awt.*;
-import java.awt.image.IndexColorModel;
+import javax.imageio.ImageTypeSpecifier;
import java.util.Calendar;
+import java.util.LinkedHashMap;
+import java.util.Map;
-import static com.twelvemonkeys.lang.Validate.notNull;
-
-final class TGAMetadata extends AbstractMetadata {
- private final TGAHeader header;
- private final TGAExtensions extensions;
-
- TGAMetadata(final TGAHeader header, final TGAExtensions extensions) {
- this.header = notNull(header, "header");
- this.extensions = extensions;
+final class TGAMetadata extends StandardImageMetadataSupport {
+ TGAMetadata(ImageTypeSpecifier type, TGAHeader header, TGAExtensions extensions) {
+ super(builder(type)
+ .withCompressionName(compressionName(header))
+ .withPixelAspectRatio(pixelAspectRatio(extensions))
+ .withOrientation(orientation(header))
+ .withFormatVersion(extensions == null ? "1.0" : "2.0")
+ .withDocumentCreationTime(documentCreationTime(extensions))
+ .withTextEntries(textEntries(header, extensions))
+ );
}
- @Override
- protected IIOMetadataNode getStandardChromaNode() {
- IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
-
- IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
- chroma.appendChild(csType);
-
+ private static String compressionName(TGAHeader header) {
switch (header.getImageType()) {
- case TGA.IMAGETYPE_MONOCHROME:
- case TGA.IMAGETYPE_MONOCHROME_RLE:
- csType.setAttribute("name", "GRAY");
- break;
-
+ case TGA.IMAGETYPE_NONE:
+ case TGA.IMAGETYPE_COLORMAPPED:
case TGA.IMAGETYPE_TRUECOLOR:
- case TGA.IMAGETYPE_TRUECOLOR_RLE:
- case TGA.IMAGETYPE_COLORMAPPED:
- case TGA.IMAGETYPE_COLORMAPPED_RLE:
- case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN:
- case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE:
- csType.setAttribute("name", "RGB");
- break;
- default:
- csType.setAttribute("name", "Unknown");
- }
-
- // NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
- IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
- chroma.appendChild(numChannels);
- switch (header.getPixelDepth()) {
- case 8:
- if (header.getImageType() == TGA.IMAGETYPE_MONOCHROME || header.getImageType() == TGA.IMAGETYPE_MONOCHROME_RLE) {
- numChannels.setAttribute("value", Integer.toString(1));
- }
- else {
- numChannels.setAttribute("value", Integer.toString(3));
- }
- break;
- case 16:
- if (header.getAttributeBits() > 0 && extensions != null && extensions.hasAlpha()) {
- numChannels.setAttribute("value", Integer.toString(4));
- }
- else {
- numChannels.setAttribute("value", Integer.toString(3));
- }
- break;
- case 24:
- numChannels.setAttribute("value", Integer.toString(3));
- break;
- case 32:
- numChannels.setAttribute("value", Integer.toString(4));
- break;
- }
-
- IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
- chroma.appendChild(blackIsZero);
- blackIsZero.setAttribute("value", "TRUE");
-
- // NOTE: TGA files may contain a color map, even if true color...
- // Not sure if this is a good idea to expose to the meta data,
- // as it might be unexpected... Then again...
- IndexColorModel colorMap = header.getColorMap();
- if (colorMap != null) {
- IIOMetadataNode palette = new IIOMetadataNode("Palette");
- chroma.appendChild(palette);
-
- for (int i = 0; i < colorMap.getMapSize(); i++) {
- IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
- palette.appendChild(paletteEntry);
- paletteEntry.setAttribute("index", Integer.toString(i));
-
- paletteEntry.setAttribute("red", Integer.toString(colorMap.getRed(i)));
- paletteEntry.setAttribute("green", Integer.toString(colorMap.getGreen(i)));
- paletteEntry.setAttribute("blue", Integer.toString(colorMap.getBlue(i)));
- }
- }
-
- if (extensions != null && extensions.getBackgroundColor() != 0) {
- Color background = new Color(extensions.getBackgroundColor(), true);
-
- IIOMetadataNode backgroundColor = new IIOMetadataNode("BackgroundColor");
- chroma.appendChild(backgroundColor);
-
- backgroundColor.setAttribute("red", Integer.toString(background.getRed()));
- backgroundColor.setAttribute("green", Integer.toString(background.getGreen()));
- backgroundColor.setAttribute("blue", Integer.toString(background.getBlue()));
- }
-
- return chroma;
- }
-
- @Override
- protected IIOMetadataNode getStandardCompressionNode() {
- switch (header.getImageType()) {
+ case TGA.IMAGETYPE_MONOCHROME:
+ return "None";
case TGA.IMAGETYPE_COLORMAPPED_RLE:
case TGA.IMAGETYPE_TRUECOLOR_RLE:
case TGA.IMAGETYPE_MONOCHROME_RLE:
+ return "RLE";
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN:
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE:
- IIOMetadataNode node = new IIOMetadataNode("Compression");
-
- IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
- node.appendChild(compressionTypeName);
- String value = header.getImageType() == TGA.IMAGETYPE_COLORMAPPED_HUFFMAN || header.getImageType() == TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE
- ? "Unknown" : "RLE";
- compressionTypeName.setAttribute("value", value);
-
- IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
- node.appendChild(lossless);
- lossless.setAttribute("value", "TRUE");
-
- return node;
default:
- // No compression
- return null;
+ return "Unknown";
}
}
- @Override
- protected IIOMetadataNode getStandardDataNode() {
- IIOMetadataNode node = new IIOMetadataNode("Data");
-
- IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
- node.appendChild(planarConfiguration);
- planarConfiguration.setAttribute("value", "PixelInterleaved");
-
- IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
- node.appendChild(sampleFormat);
-
- switch (header.getImageType()) {
- case TGA.IMAGETYPE_COLORMAPPED:
- case TGA.IMAGETYPE_COLORMAPPED_RLE:
- case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN:
- case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE:
- sampleFormat.setAttribute("value", "Index");
- break;
- default:
- sampleFormat.setAttribute("value", "UnsignedIntegral");
- break;
- }
-
- IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
- node.appendChild(bitsPerSample);
-
- switch (header.getPixelDepth()) {
- case 8:
- bitsPerSample.setAttribute("value", createListValue(1, "8"));
- break;
- case 16:
- if (header.getImageType() == TGA.IMAGETYPE_MONOCHROME || header.getImageType() == TGA.IMAGETYPE_MONOCHROME_RLE) {
- bitsPerSample.setAttribute("value", "16");
- }
- else if (header.getAttributeBits() > 0 && extensions != null && extensions.hasAlpha()) {
- bitsPerSample.setAttribute("value", "5, 5, 5, 1");
- }
- else {
- bitsPerSample.setAttribute("value", createListValue(3, "5"));
- }
- break;
- case 24:
- bitsPerSample.setAttribute("value", createListValue(3, "8"));
- break;
- case 32:
- bitsPerSample.setAttribute("value", createListValue(4, "8"));
- break;
- }
-
- return node;
+ private static double pixelAspectRatio(TGAExtensions extensions) {
+ return extensions != null ? extensions.getPixelAspectRatio() : 1f;
}
- private String createListValue(final int itemCount, final String... values) {
- StringBuilder buffer = new StringBuilder();
-
- for (int i = 0; i < itemCount; i++) {
- if (buffer.length() > 0) {
- buffer.append(' ');
- }
-
- buffer.append(values[i % values.length]);
- }
-
- return buffer.toString();
- }
-
- @Override
- protected IIOMetadataNode getStandardDimensionNode() {
- IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
-
- IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
- dimension.appendChild(imageOrientation);
-
- switch (header.getOrigin()) {
+ private static ImageOrientation orientation(TGAHeader header) {
+ switch (header.origin) {
case TGA.ORIGIN_LOWER_LEFT:
- imageOrientation.setAttribute("value", "FlipH");
- break;
+ return ImageOrientation.FlipH;
case TGA.ORIGIN_LOWER_RIGHT:
- imageOrientation.setAttribute("value", "Rotate180");
- break;
+ return ImageOrientation.Rotate180;
case TGA.ORIGIN_UPPER_LEFT:
- imageOrientation.setAttribute("value", "Normal");
- break;
+ return ImageOrientation.Normal;
case TGA.ORIGIN_UPPER_RIGHT:
- imageOrientation.setAttribute("value", "FlipV");
- break;
+ return ImageOrientation.FlipV;
+ default:
+ throw new IllegalArgumentException("Unknown orientation: " + header.origin);
}
-
- IIOMetadataNode pixelAspectRatio = new IIOMetadataNode("PixelAspectRatio");
- dimension.appendChild(pixelAspectRatio);
- pixelAspectRatio.setAttribute("value", extensions != null ? String.valueOf(extensions.getPixelAspectRatio()) : "1.0");
-
- return dimension;
}
- @Override
- protected IIOMetadataNode getStandardDocumentNode() {
- IIOMetadataNode document = new IIOMetadataNode("Document");
-
- IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
- document.appendChild(formatVersion);
- formatVersion.setAttribute("value", extensions == null ? "1.0" : "2.0");
-
- // ImageCreationTime from extensions date
- if (extensions != null && extensions.getCreationDate() != null) {
- IIOMetadataNode imageCreationTime = new IIOMetadataNode("ImageCreationTime");
- document.appendChild(imageCreationTime);
-
- Calendar date = extensions.getCreationDate();
-
- imageCreationTime.setAttribute("year", String.valueOf(date.get(Calendar.YEAR)));
- imageCreationTime.setAttribute("month", String.valueOf(date.get(Calendar.MONTH) + 1));
- imageCreationTime.setAttribute("day", String.valueOf(date.get(Calendar.DAY_OF_MONTH)));
- imageCreationTime.setAttribute("hour", String.valueOf(date.get(Calendar.HOUR_OF_DAY)));
- imageCreationTime.setAttribute("minute", String.valueOf(date.get(Calendar.MINUTE)));
- imageCreationTime.setAttribute("second", String.valueOf(date.get(Calendar.SECOND)));
- }
-
- return document;
+ private static Calendar documentCreationTime(TGAExtensions extensions) {
+ return extensions != null ? extensions.creationDate : null;
}
- @Override
- protected IIOMetadataNode getStandardTextNode() {
- IIOMetadataNode text = new IIOMetadataNode("Text");
+ private static Map textEntries(TGAHeader header, TGAExtensions extensions) {
+ LinkedHashMap textEntries = new LinkedHashMap<>();
- // NOTE: Names corresponds to equivalent fields in TIFF
- appendTextEntry(text, "DocumentName", header.getIdentification());
+ // NOTE: Keywords follow TIFF standard naming
+ putIfValue(textEntries, "DocumentName", header.getIdentification());
if (extensions != null) {
- appendTextEntry(text, "Software", extensions.getSoftwareVersion() == null ? extensions.getSoftware() : (extensions.getSoftware() + " " + extensions.getSoftwareVersion()));
- appendTextEntry(text, "Artist", extensions.getAuthorName());
- appendTextEntry(text, "UserComment", extensions.getAuthorComments());
+ putIfValue(textEntries, "Software", extensions.getSoftwareVersion() == null ? extensions.getSoftware() : (extensions.getSoftware() + " " + extensions.getSoftwareVersion()));
+ putIfValue(textEntries, "Artist", extensions.getAuthorName());
+ putIfValue(textEntries, "UserComment", extensions.getAuthorComments());
}
- return text.hasChildNodes() ? text : null;
+ return textEntries;
}
- private void appendTextEntry(final IIOMetadataNode parent, final String keyword, final String value) {
+ private static void putIfValue(final Map textEntries, final String keyword, final String value) {
if (value != null && !value.isEmpty()) {
- IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
- parent.appendChild(textEntry);
- textEntry.setAttribute("keyword", keyword);
- textEntry.setAttribute("value", value);
+ textEntries.put(keyword, value);
}
}
-
- // No tiling
-
- @Override
- protected IIOMetadataNode getStandardTransparencyNode() {
- IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
-
- IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
- transparency.appendChild(alpha);
-
- if (extensions != null) {
- if (extensions.hasAlpha()) {
- alpha.setAttribute("value", extensions.isAlphaPremultiplied() ? "premultiplied" : "nonpremultiplied");
- }
- else {
- alpha.setAttribute("value", "none");
- }
- }
- else if (header.getAttributeBits() == 8) {
- alpha.setAttribute("value", "nonpremultiplied");
- }
- else {
- alpha.setAttribute("value", "none");
- }
-
- return transparency;
- }
}
diff --git a/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParamTest.java b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParamTest.java
index d8ff3059..59cf7cbc 100644
--- a/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParamTest.java
+++ b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAImageWriteParamTest.java
@@ -30,12 +30,17 @@
package com.twelvemonkeys.imageio.plugins.tga;
+import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
+
import org.junit.Test;
+import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
-import java.awt.image.BufferedImage;
+import java.awt.image.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
/**
@@ -46,6 +51,9 @@ import static org.junit.Assume.assumeFalse;
* @version $Id: TGAImageWriteParamTest.java,v 1.0 08/04/2021 haraldk Exp$
*/
public class TGAImageWriteParamTest {
+
+ private static final ImageTypeSpecifier TYPE_3BYTE_BGR = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
+
@Test
public void testDefaultCopyFromMetadata() {
TGAImageWriteParam param = new TGAImageWriteParam();
@@ -107,8 +115,8 @@ public class TGAImageWriteParamTest {
ImageWriteParam param = new ImageWriteParam(null);
assumeFalse(param.canWriteCompressed());
- assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false), null)));
- assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
+ assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, false), null)));
+ assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, true), null)));
}
@Test
@@ -116,7 +124,7 @@ public class TGAImageWriteParamTest {
ImageWriteParam param = new TGAImageWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
- assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
+ assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, true), null)));
}
@Test
@@ -124,7 +132,7 @@ public class TGAImageWriteParamTest {
ImageWriteParam param = new TGAImageWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
- assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
+ assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, true), null)));
}
@Test
@@ -132,12 +140,12 @@ public class TGAImageWriteParamTest {
TGAImageWriteParam param = new TGAImageWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
- assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false), null)));
- assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
+ assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, false), null)));
+ assertFalse(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, true), null)));
param.setCompressionType("RLE");
- assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false), null)));
- assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true), null)));
+ assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, false), null)));
+ assertTrue(TGAImageWriteParam.isRLE(param, new TGAMetadata(TYPE_3BYTE_BGR, TGAHeader.from(TYPE_3BYTE_BGR, true), null)));
}
}
\ No newline at end of file
diff --git a/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadataTest.java b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadataTest.java
index c1bd18e1..4d87e57a 100644
--- a/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadataTest.java
+++ b/imageio/imageio-tga/src/test/java/com/twelvemonkeys/imageio/plugins/tga/TGAMetadataTest.java
@@ -30,14 +30,18 @@
package com.twelvemonkeys.imageio.plugins.tga;
+import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
+
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
-import java.awt.image.BufferedImage;
-import java.awt.image.IndexColorModel;
+import java.awt.image.*;
import java.util.Calendar;
import static org.junit.Assert.*;
@@ -50,10 +54,15 @@ import static org.junit.Assert.*;
* @version $Id: TGAMetadataTest.java,v 1.0 08/04/2021 haraldk Exp$
*/
public class TGAMetadataTest {
+
+ private static final ImageTypeSpecifier TYPE_BYTE_GRAY = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
+
+ private static final ImageTypeSpecifier TYPE_3BYTE_BGR = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
+
@Test
public void testStandardFeatures() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
- final TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, false);
+ final TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
// Standard metadata format
assertTrue(metadata.isStandardMetadataFormatSupported());
@@ -83,10 +92,10 @@ public class TGAMetadataTest {
@Test
public void testStandardChromaGray() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), false);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, false);
+ TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
- IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(3, chroma.getLength());
@@ -108,10 +117,10 @@ public class TGAMetadataTest {
@Test
public void testStandardChromaRGB() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, false);
+ TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
- IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(3, chroma.getLength());
@@ -135,10 +144,11 @@ public class TGAMetadataTest {
public void testStandardChromaPalette() {
byte[] bw = {0, (byte) 0xff};
IndexColorModel indexColorModel = new IndexColorModel(8, bw.length, bw, bw, bw, -1);
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED, indexColorModel), false);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ ImageTypeSpecifier type = ImageTypeSpecifiers.createFromIndexColorModel(indexColorModel);
+ TGAHeader header = TGAHeader.from(type, false);
+ TGAMetadata metadata = new TGAMetadata(type, header, null);
- IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(4, chroma.getLength());
@@ -174,10 +184,10 @@ public class TGAMetadataTest {
@Test
public void testStandardCompressionRLE() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, true);
+ TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
- IIOMetadataNode compression = metadata.getStandardCompressionNode();
+ IIOMetadataNode compression = getStandardNode(metadata, "Compression");
assertNotNull(compression);
assertEquals("Compression", compression.getNodeName());
assertEquals(2, compression.getLength());
@@ -195,18 +205,18 @@ public class TGAMetadataTest {
@Test
public void testStandardCompressionNone() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, false);
+ TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
- assertNull(metadata.getStandardCompressionNode()); // No compression, all default...
+ assertNull(getStandardNode(metadata, "Compression")); // No compression, all default...
}
@Test
public void testStandardDataGray() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
+ TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -228,10 +238,10 @@ public class TGAMetadataTest {
@Test
public void testStandardDataRGB() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, true);
+ TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -253,10 +263,11 @@ public class TGAMetadataTest {
@Test
public void testStandardDataRGBA() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB), true);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ ImageTypeSpecifier type = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
+ TGAHeader header = TGAHeader.from(type, true);
+ TGAMetadata metadata = new TGAMetadata(type, header, null);
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -280,10 +291,11 @@ public class TGAMetadataTest {
public void testStandardDataPalette() {
byte[] rgb = new byte[1 << 8]; // Colors doesn't really matter here
IndexColorModel indexColorModel = new IndexColorModel(8, rgb.length, rgb, rgb, rgb, 0);
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED, indexColorModel), true);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ ImageTypeSpecifier type = ImageTypeSpecifiers.createFromIndexColorModel(indexColorModel);
+ TGAHeader header = TGAHeader.from(type, true);
+ TGAMetadata metadata = new TGAMetadata(type, header, null);
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -305,53 +317,56 @@ public class TGAMetadataTest {
@Test
public void testStandardDimensionNormal() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
+ TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
- IIOMetadataNode dimension = metadata.getStandardDimensionNode();
+ IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
assertNotNull(dimension);
assertEquals("Dimension", dimension.getNodeName());
assertEquals(2, dimension.getLength());
- IIOMetadataNode imageOrientation = (IIOMetadataNode) dimension.getFirstChild();
- assertEquals("ImageOrientation", imageOrientation.getNodeName());
- assertEquals("Normal", imageOrientation.getAttribute("value"));
-
- IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) imageOrientation.getNextSibling();
+ IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
+ assertEquals("ImageOrientation", imageOrientation.getNodeName());
+ assertEquals("Normal", imageOrientation.getAttribute("value"));
+
+
+ assertNull(imageOrientation.getNextSibling()); // No more children
}
@Test
public void testStandardDimensionFlipH() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
+ TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
header.origin = TGA.ORIGIN_LOWER_LEFT;
- TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
- IIOMetadataNode dimension = metadata.getStandardDimensionNode();
+ IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
assertNotNull(dimension);
assertEquals("Dimension", dimension.getNodeName());
assertEquals(2, dimension.getLength());
- IIOMetadataNode imageOrientation = (IIOMetadataNode) dimension.getFirstChild();
- assertEquals("ImageOrientation", imageOrientation.getNodeName());
- assertEquals("FlipH", imageOrientation.getAttribute("value"));
- IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) imageOrientation.getNextSibling();
+ IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
+ assertEquals("ImageOrientation", imageOrientation.getNodeName());
+ assertEquals("FlipH", imageOrientation.getAttribute("value"));
+
+
+ assertNull(imageOrientation.getNextSibling()); // No more children
}
@Test
public void testStandardDocument() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
+ TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
- IIOMetadataNode document = metadata.getStandardDocumentNode();
+ IIOMetadataNode document = getStandardNode(metadata, "Document");
assertNotNull(document);
assertEquals("Document", document.getNodeName());
assertEquals(1, document.getLength());
@@ -365,13 +380,13 @@ public class TGAMetadataTest {
@Test
public void testStandardDocumentExtensions() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
+ TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
TGAExtensions extensions = new TGAExtensions();
extensions.creationDate = Calendar.getInstance();
extensions.creationDate.set(2021, Calendar.APRIL, 8, 18, 55, 0);
- TGAMetadata metadata = new TGAMetadata(header, extensions);
+ TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, extensions);
- IIOMetadataNode document = metadata.getStandardDocumentNode();
+ IIOMetadataNode document = getStandardNode(metadata, "Document");
assertNotNull(document);
assertEquals("Document", document.getNodeName());
assertEquals(2, document.getLength());
@@ -394,7 +409,7 @@ public class TGAMetadataTest {
@Test
public void testStandardText() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
+ TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
header.identification = "MY_FILE.TGA";
TGAExtensions extensions = new TGAExtensions();
@@ -402,9 +417,9 @@ public class TGAMetadataTest {
extensions.authorName = "Harald K";
extensions.authorComments = "Comments, comments... ";
- TGAMetadata metadata = new TGAMetadata(header, extensions);
+ TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, extensions);
- IIOMetadataNode text = metadata.getStandardTextNode();
+ IIOMetadataNode text = getStandardNode(metadata, "Text");
assertNotNull(text);
assertEquals("Text", text.getNodeName());
assertEquals(4, text.getLength());
@@ -432,10 +447,10 @@ public class TGAMetadataTest {
@Test
public void testStandardTransparencyRGB() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, true);
+ TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
assertNotNull(transparency);
assertEquals("Transparency", transparency.getNodeName());
assertEquals(1, transparency.getLength());
@@ -449,10 +464,11 @@ public class TGAMetadataTest {
@Test
public void testStandardTransparencyRGBA() {
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR), true);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ ImageTypeSpecifier type = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
+ TGAHeader header = TGAHeader.from(type, true);
+ TGAMetadata metadata = new TGAMetadata(type, header, null);
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
assertNotNull(transparency);
assertEquals("Transparency", transparency.getNodeName());
assertEquals(1, transparency.getLength());
@@ -468,19 +484,30 @@ public class TGAMetadataTest {
public void testStandardTransparencyPalette() {
byte[] bw = {0, (byte) 0xff};
IndexColorModel indexColorModel = new IndexColorModel(8, bw.length, bw, bw, bw, 1);
- TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_INDEXED, indexColorModel), true);
- TGAMetadata metadata = new TGAMetadata(header, null);
+ ImageTypeSpecifier type = ImageTypeSpecifiers.createFromIndexColorModel(indexColorModel);
+ TGAHeader header = TGAHeader.from(type, true);
+ TGAMetadata metadata = new TGAMetadata(type, header, null);
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
assertNotNull(transparency);
assertEquals("Transparency", transparency.getNodeName());
- assertEquals(1, transparency.getLength());
+ assertEquals(2, transparency.getLength());
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
assertEquals("Alpha", alpha.getNodeName());
assertEquals("nonpremultiplied", alpha.getAttribute("value"));
- assertNull(alpha.getNextSibling()); // No more children
+ IIOMetadataNode transparentIndex = (IIOMetadataNode) alpha.getNextSibling();
+ assertEquals("TransparentIndex", transparentIndex.getNodeName());
+ assertEquals("1", transparentIndex.getAttribute("value"));
+
+ assertNull(transparentIndex.getNextSibling()); // No more children
}
+ private IIOMetadataNode getStandardNode(IIOMetadata metadata, String nodeName) {
+ IIOMetadataNode asTree = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
+ NodeList nodes = asTree.getElementsByTagName(nodeName);
+
+ return nodes.getLength() > 0 ? (IIOMetadataNode) nodes.item(0) : null;
+ }
}
\ No newline at end of file
diff --git a/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageMetadata.java b/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageMetadata.java
index 21e47bea..39842c93 100644
--- a/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageMetadata.java
+++ b/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageMetadata.java
@@ -1,184 +1,19 @@
-/*
- * Copyright (c) 2017, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
package com.twelvemonkeys.imageio.plugins.webp;
-import com.twelvemonkeys.imageio.AbstractMetadata;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
-import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.ImageTypeSpecifier;
import static com.twelvemonkeys.lang.Validate.notNull;
-/**
- * WebPMetadata
- */
-final class WebPImageMetadata extends AbstractMetadata {
- private final VP8xChunk header;
-
- WebPImageMetadata(final VP8xChunk header) {
- this.header = notNull(header, "header");
+final class WebPImageMetadata extends StandardImageMetadataSupport {
+ WebPImageMetadata(ImageTypeSpecifier type, VP8xChunk header) {
+ super(builder(type)
+ .withCompressionName(notNull(header, "header").isLossless ? "VP8L" : "VP8")
+ .withCompressionLossless(header.isLossless)
+ .withPixelAspectRatio(1.0)
+ .withFormatVersion("1.0")
+ // TODO: Get useful text nodes from EXIF or XMP
+ );
}
-
- @Override
- protected IIOMetadataNode getStandardChromaNode() {
- IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
-
- IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
- chroma.appendChild(csType);
- csType.setAttribute("name", "RGB");
-
- // NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
- IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
- chroma.appendChild(numChannels);
- numChannels.setAttribute("value", Integer.toString(header.containsALPH ? 4 : 3));
-
- IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
- chroma.appendChild(blackIsZero);
- blackIsZero.setAttribute("value", "TRUE");
-
- return chroma;
- }
-
- @Override
- protected IIOMetadataNode getStandardCompressionNode() {
- IIOMetadataNode node = new IIOMetadataNode("Compression");
-
- IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
- node.appendChild(compressionTypeName);
-
- String value = header.isLossless ? "VP8L" : "VP8"; // TODO: Naming: VP8L and VP8 or WebP and WebP Lossless?
- compressionTypeName.setAttribute("value", value);
-
- // TODO: VP8 + lossless alpha!
- IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
- node.appendChild(lossless);
- lossless.setAttribute("value", header.isLossless ? "TRUE" : "FALSE");
-
- return node;
- }
-
- @Override
- protected IIOMetadataNode getStandardDataNode() {
- IIOMetadataNode node = new IIOMetadataNode("Data");
-
- // TODO: WebP seems to support planar as well?
- IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
- node.appendChild(planarConfiguration);
- planarConfiguration.setAttribute("value", "PixelInterleaved");
-
- IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
- node.appendChild(sampleFormat);
- sampleFormat.setAttribute("value", "UnsignedIntegral");
-
- IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
- node.appendChild(bitsPerSample);
-
- bitsPerSample.setAttribute("value", createListValue(header.containsALPH ? 4 : 3, Integer.toString(8)));
-
- return node;
- }
-
- private String createListValue(final int itemCount, final String... values) {
- StringBuilder buffer = new StringBuilder();
-
- for (int i = 0; i < itemCount; i++) {
- if (buffer.length() > 0) {
- buffer.append(' ');
- }
-
- buffer.append(values[i % values.length]);
- }
-
- return buffer.toString();
- }
-
- @Override
- protected IIOMetadataNode getStandardDimensionNode() {
- IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
-
- IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
- dimension.appendChild(imageOrientation);
- imageOrientation.setAttribute("value", "Normal");
-
- IIOMetadataNode pixelAspectRatio = new IIOMetadataNode("PixelAspectRatio");
- dimension.appendChild(pixelAspectRatio);
- pixelAspectRatio.setAttribute("value", "1.0");
-
- return dimension;
- }
-
- @Override
- protected IIOMetadataNode getStandardDocumentNode() {
- IIOMetadataNode document = new IIOMetadataNode("Document");
-
- IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
- document.appendChild(formatVersion);
- formatVersion.setAttribute("value", "1.0");
-
- return document;
- }
-
- @Override
- protected IIOMetadataNode getStandardTextNode() {
- IIOMetadataNode text = new IIOMetadataNode("Text");
-
- // TODO: Get useful text nodes from EXIF or XMP
- // NOTE: Names corresponds to equivalent fields in TIFF
-
- return text.hasChildNodes() ? text : null;
- }
-
-// private void appendTextEntry(final IIOMetadataNode parent, final String keyword, final String value) {
-// if (value != null) {
-// IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
-// parent.appendChild(textEntry);
-// textEntry.setAttribute("keyword", keyword);
-// textEntry.setAttribute("value", value);
-// }
-// }
-
- // No tiling
-
- @Override
- protected IIOMetadataNode getStandardTransparencyNode() {
- if (header.containsALPH) {
- IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
- IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
- transparency.appendChild(alpha);
- alpha.setAttribute("value", "nonpremultiplied");
- return transparency;
- }
-
- return null;
- }
-
- // TODO: Define native WebP metadata format (probably use RIFF structure)
}
diff --git a/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageReader.java b/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageReader.java
index 5f35eb27..dca166b5 100644
--- a/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageReader.java
+++ b/imageio/imageio-webp/src/main/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageReader.java
@@ -661,10 +661,7 @@ final class WebPImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
- readHeader(imageIndex);
- readMeta();
-
- return new WebPImageMetadata(header);
+ return new WebPImageMetadata(getRawImageType(imageIndex), header);
}
private void readMeta() throws IOException {
diff --git a/imageio/imageio-webp/src/test/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageMetadataTest.java b/imageio/imageio-webp/src/test/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageMetadataTest.java
index 41b83899..17aee33d 100644
--- a/imageio/imageio-webp/src/test/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageMetadataTest.java
+++ b/imageio/imageio-webp/src/test/java/com/twelvemonkeys/imageio/plugins/webp/WebPImageMetadataTest.java
@@ -1,11 +1,17 @@
package com.twelvemonkeys.imageio.plugins.webp;
+import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
+
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
+import java.awt.image.*;
import static org.junit.Assert.*;
@@ -17,10 +23,14 @@ import static org.junit.Assert.*;
* @version $Id: WebPImageMetadataTest.java,v 1.0 21/11/2020 haraldk Exp$
*/
public class WebPImageMetadataTest {
+
+ private static final ImageTypeSpecifier TYPE_3BYTE_BGR = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
+ private static final ImageTypeSpecifier TYPE_4BYTE_ABGR = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
+
@Test
public void testStandardFeatures() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
- final WebPImageMetadata metadata = new WebPImageMetadata(header);
+ final WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
// Standard metadata format
assertTrue(metadata.isStandardMetadataFormatSupported());
@@ -51,9 +61,9 @@ public class WebPImageMetadataTest {
@Test
public void testStandardChromaRGB() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
- IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(3, chroma.getLength());
@@ -77,9 +87,9 @@ public class WebPImageMetadataTest {
public void testStandardChromaRGBA() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
header.containsALPH = true;
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_4BYTE_ABGR, header);
- IIOMetadataNode chroma = metadata.getStandardChromaNode();
+ IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
assertNotNull(chroma);
assertEquals("Chroma", chroma.getNodeName());
assertEquals(3, chroma.getLength());
@@ -103,9 +113,9 @@ public class WebPImageMetadataTest {
@Test
public void testStandardCompressionVP8() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
- IIOMetadataNode compression = metadata.getStandardCompressionNode();
+ IIOMetadataNode compression = getStandardNode(metadata, "Compression");
assertNotNull(compression);
assertEquals("Compression", compression.getNodeName());
assertEquals(2, compression.getLength());
@@ -125,9 +135,9 @@ public class WebPImageMetadataTest {
public void testStandardCompressionVP8L() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8L, 27, 33);
header.isLossless = true;
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
- IIOMetadataNode compression = metadata.getStandardCompressionNode();
+ IIOMetadataNode compression = getStandardNode(metadata, "Compression");
assertNotNull(compression);
assertEquals("Compression", compression.getNodeName());
assertEquals(2, compression.getLength());
@@ -146,9 +156,9 @@ public class WebPImageMetadataTest {
@Test
public void testStandardCompressionVP8X() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
- IIOMetadataNode compression = metadata.getStandardCompressionNode();
+ IIOMetadataNode compression = getStandardNode(metadata, "Compression");
assertNotNull(compression);
assertEquals("Compression", compression.getNodeName());
assertEquals(2, compression.getLength());
@@ -168,9 +178,9 @@ public class WebPImageMetadataTest {
public void testStandardCompressionVP8XLossless() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
header.isLossless = true;
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
- IIOMetadataNode compression = metadata.getStandardCompressionNode();
+ IIOMetadataNode compression = getStandardNode(metadata, "Compression");
assertNotNull(compression);
assertEquals("Compression", compression.getNodeName());
assertEquals(2, compression.getLength());
@@ -189,9 +199,9 @@ public class WebPImageMetadataTest {
@Test
public void testStandardDataRGB() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -215,9 +225,9 @@ public class WebPImageMetadataTest {
public void testStandardDataRGBA() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
header.containsALPH = true;
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_4BYTE_ABGR, header);
- IIOMetadataNode data = metadata.getStandardDataNode();
+ IIOMetadataNode data = getStandardNode(metadata, "Data");
assertNotNull(data);
assertEquals("Data", data.getNodeName());
assertEquals(3, data.getLength());
@@ -240,78 +250,109 @@ public class WebPImageMetadataTest {
@Test
public void testStandardDimensionNormal() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
- IIOMetadataNode dimension = metadata.getStandardDimensionNode();
+ IIOMetadataNode dimension = getStandardNode(metadata, "Dimension");
assertNotNull(dimension);
assertEquals("Dimension", dimension.getNodeName());
assertEquals(2, dimension.getLength());
- IIOMetadataNode imageOrientation = (IIOMetadataNode) dimension.getFirstChild();
- assertEquals("ImageOrientation", imageOrientation.getNodeName());
- assertEquals("Normal", imageOrientation.getAttribute("value"));
-
- IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) imageOrientation.getNextSibling();
+ IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ IIOMetadataNode imageOrientation = (IIOMetadataNode) pixelAspectRatio.getNextSibling();
+ assertEquals("ImageOrientation", imageOrientation.getNodeName());
+ assertEquals("Normal", imageOrientation.getAttribute("value"));
+
+ assertNull(imageOrientation.getNextSibling()); // No more children
}
@Test
public void testStandardDocument() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
- IIOMetadataNode document = metadata.getStandardDocumentNode();
+ IIOMetadataNode document = getStandardNode(metadata, "Document");
assertNotNull(document);
assertEquals("Document", document.getNodeName());
assertEquals(1, document.getLength());
- IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) document.getFirstChild();
- assertEquals("FormatVersion", pixelAspectRatio.getNodeName());
- assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
+ IIOMetadataNode formatVersion = (IIOMetadataNode) document.getFirstChild();
+ assertEquals("FormatVersion", formatVersion.getNodeName());
+ assertEquals("1.0", formatVersion.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ assertNull(formatVersion.getNextSibling()); // No more children
}
@Test
public void testStandardText() {
+ // No text node yet...
}
@Test
public void testStandardTransparencyVP8() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
- assertNull(transparency); // No transparency, just defaults
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
+
+ if (transparency != null) {
+ assertNotNull(transparency);
+ assertEquals("Transparency", transparency.getNodeName());
+ assertEquals(1, transparency.getLength());
+
+ IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
+ assertEquals("Alpha", alpha.getNodeName());
+ assertEquals("none", alpha.getAttribute("value"));
+
+ assertNull(alpha.getNextSibling()); // No more children
+ }
+ // Else no transparency, just defaults
}
@Test
public void testStandardTransparencyVP8L() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_3BYTE_BGR, header);
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
- assertNull(transparency); // No transparency, just defaults
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
+ if (transparency != null) {
+ assertNotNull(transparency);
+ assertEquals("Transparency", transparency.getNodeName());
+ assertEquals(1, transparency.getLength());
+
+ IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
+ assertEquals("Alpha", alpha.getNodeName());
+ assertEquals("none", alpha.getAttribute("value"));
+
+ assertNull(alpha.getNextSibling()); // No more children
+ }
+ // Else no transparency, just defaults
}
@Test
public void testStandardTransparencyVP8X() {
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
header.containsALPH = true;
- WebPImageMetadata metadata = new WebPImageMetadata(header);
+ WebPImageMetadata metadata = new WebPImageMetadata(TYPE_4BYTE_ABGR, header);
- IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
+ IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
assertNotNull(transparency);
assertEquals("Transparency", transparency.getNodeName());
assertEquals(1, transparency.getLength());
- IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
- assertEquals("Alpha", pixelAspectRatio.getNodeName());
- assertEquals("nonpremultiplied", pixelAspectRatio.getAttribute("value"));
+ IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
+ assertEquals("Alpha", alpha.getNodeName());
+ assertEquals("nonpremultiplied", alpha.getAttribute("value"));
- assertNull(pixelAspectRatio.getNextSibling()); // No more children
+ assertNull(alpha.getNextSibling()); // No more children
+ }
+
+ private IIOMetadataNode getStandardNode(IIOMetadata metadata, String nodeName) {
+ IIOMetadataNode asTree = (IIOMetadataNode) metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
+ NodeList nodes = asTree.getElementsByTagName(nodeName);
+
+ return nodes.getLength() > 0 ? (IIOMetadataNode) nodes.item(0) : null;
}
}
\ No newline at end of file
diff --git a/imageio/imageio-xwd/src/main/java/com/twelvemonkeys/imageio/plugins/xwd/XWDImageMetadata.java b/imageio/imageio-xwd/src/main/java/com/twelvemonkeys/imageio/plugins/xwd/XWDImageMetadata.java
index 020ad941..986b4eed 100644
--- a/imageio/imageio-xwd/src/main/java/com/twelvemonkeys/imageio/plugins/xwd/XWDImageMetadata.java
+++ b/imageio/imageio-xwd/src/main/java/com/twelvemonkeys/imageio/plugins/xwd/XWDImageMetadata.java
@@ -1,139 +1,16 @@
package com.twelvemonkeys.imageio.plugins.xwd;
-import com.twelvemonkeys.imageio.AbstractMetadata;
+import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
-import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.ImageTypeSpecifier;
import java.nio.ByteOrder;
-import static com.twelvemonkeys.lang.Validate.notNull;
-
-final class XWDImageMetadata extends AbstractMetadata {
- private final XWDX11Header header;
-
- XWDImageMetadata(XWDX11Header header) {
- super(true, null, null, null, null);
- this.header = notNull(header, "header");
- }
-
- @Override
- protected IIOMetadataNode getStandardChromaNode() {
- IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
- IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType");
-
- switch (header.visualClass) {
- case X11.VISUAL_CLASS_STATIC_GRAY:
- case X11.VISUAL_CLASS_GRAY_SCALE:
- colorSpaceType.setAttribute("name", "GRAY");
- break;
- default:
- colorSpaceType.setAttribute("name", "RGB");
- }
-
- chroma.appendChild(colorSpaceType);
-
- // TODO: Depending on visual class OR the presence of color mop!?
- switch (header.visualClass) {
- case X11.VISUAL_CLASS_STATIC_COLOR:
- case X11.VISUAL_CLASS_PSEUDO_COLOR:
- IIOMetadataNode palette = new IIOMetadataNode("Palette");
- chroma.appendChild(palette);
-
- for (int i = 0; i < header.colorMap.getMapSize(); i++) {
- IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
- paletteEntry.setAttribute("index", Integer.toString(i));
-
- paletteEntry.setAttribute("red", Integer.toString(header.colorMap.getRed(i)));
- paletteEntry.setAttribute("green", Integer.toString(header.colorMap.getGreen(i)));
- paletteEntry.setAttribute("blue", Integer.toString(header.colorMap.getBlue(i)));
-
- palette.appendChild(paletteEntry);
- }
- break;
-
- default:
- // No palette
- }
-
-
- return chroma;
- }
-
- @Override
- protected IIOMetadataNode getStandardDataNode() {
- IIOMetadataNode node = new IIOMetadataNode("Data");
-
- IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
- planarConfiguration.setAttribute("value", "PixelInterleaved");
- node.appendChild(planarConfiguration);
-
- IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
- node.appendChild(sampleFormat);
-
- switch (header.visualClass) {
- case X11.VISUAL_CLASS_STATIC_COLOR:
- case X11.VISUAL_CLASS_PSEUDO_COLOR:
- sampleFormat.setAttribute("value", "Index");
- break;
- default:
- sampleFormat.setAttribute("value", "UnsignedIntegral");
- break;
- }
-
- IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
- node.appendChild(bitsPerSample);
-
- int numComponents = header.numComponents();
- bitsPerSample.setAttribute("value", createListValue(numComponents, Integer.toString(header.bitsPerPixel / numComponents)));
-
- // SampleMSB
- if (header.bitsPerRGB < 8 && header.bitFillOrder == ByteOrder.LITTLE_ENDIAN) {
- IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
- node.appendChild(sampleMSB);
- sampleMSB.setAttribute("value", createListValue(header.numComponents(), "0"));
- }
-
- return node;
- }
-
-
- @Override
- protected IIOMetadataNode getStandardDocumentNode() {
- IIOMetadataNode document = new IIOMetadataNode("Document");
-
- // The only format we support is the X11 format, and it's version is 7.
- IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
- document.appendChild(formatVersion);
- formatVersion.setAttribute("value", "7");
-
- return document;
- }
-
- @Override
- protected IIOMetadataNode getStandardTextNode() {
- IIOMetadataNode text = new IIOMetadataNode("Text");
-
- if (header.windowName != null) {
- IIOMetadataNode node = new IIOMetadataNode("TextEntry");
- text.appendChild(node);
- node.setAttribute("keyword", "DocumentName"); // For TIFF interop. :-)
- node.setAttribute("value", header.windowName);
- }
-
- return text.hasChildNodes() ? text : null;
- }
-
- // TODO: Candidate superclass method!
- private String createListValue(final int itemCount, final String... values) {
- StringBuilder buffer = new StringBuilder();
-
- for (int i = 0; i < itemCount; i++) {
- if (buffer.length() > 0) {
- buffer.append(' ');
- }
-
- buffer.append(values[i % values.length]);
- }
-
- return buffer.toString();
+final class XWDImageMetadata extends StandardImageMetadataSupport {
+ XWDImageMetadata(ImageTypeSpecifier type, XWDX11Header header) {
+ super(builder(type)
+ .withSampleMSB(header.bitsPerRGB < 8 && header.bitFillOrder == ByteOrder.LITTLE_ENDIAN ? 0 : 7) // TODO: This is unlikely to be correct...
+ .withFormatVersion("7.0") // The only format we support is the X11 format, and it's version is 7
+ .withTextEntry("DocumentName", header.windowName) // For TIFF interop :-)
+ );
}
}
diff --git a/imageio/imageio-xwd/src/main/java/com/twelvemonkeys/imageio/plugins/xwd/XWDImageReader.java b/imageio/imageio-xwd/src/main/java/com/twelvemonkeys/imageio/plugins/xwd/XWDImageReader.java
index ebc62f01..b8b0feae 100644
--- a/imageio/imageio-xwd/src/main/java/com/twelvemonkeys/imageio/plugins/xwd/XWDImageReader.java
+++ b/imageio/imageio-xwd/src/main/java/com/twelvemonkeys/imageio/plugins/xwd/XWDImageReader.java
@@ -9,7 +9,7 @@ import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
-import java.awt.color.ColorSpace;
+import java.awt.color.*;
import java.awt.image.*;
import java.io.IOException;
import java.nio.ByteOrder;
@@ -48,10 +48,7 @@ final class XWDImageReader extends ImageReaderBase {
@Override
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
- checkBounds(imageIndex);
- readHeader();
-
- return new XWDImageMetadata(header);
+ return new XWDImageMetadata(getRawImageType(imageIndex), header);
}
@Override