mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-10-04 11:26:44 -04:00
Major ImageMetadata refactor for more consistent standard metadata support.
Fixes a few related bugs as a bonus.
This commit is contained in:
@@ -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 <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
*/
|
||||||
|
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<Map.Entry<String, String>> 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<Map.Entry<String, String>> 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<String, String> entries) {
|
||||||
|
return withTextEntries(notNull(entries, "entries").entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder withTextEntries(Collection<Map.Entry<String, String>> 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<String, String> 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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<String> 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<String> 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<String> 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<String> 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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -40,11 +40,8 @@ import javax.imageio.ImageTypeSpecifier;
|
|||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.*;
|
||||||
import java.awt.image.DataBuffer;
|
|
||||||
import java.awt.image.Raster;
|
|
||||||
import java.awt.image.WritableRaster;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -244,10 +241,7 @@ public final class HDRImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||||
checkBounds(imageIndex);
|
return new HDRMetadata(getRawImageType(imageIndex), header);
|
||||||
readHeader();
|
|
||||||
|
|
||||||
return new HDRMetadata(header);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(final String[] args) throws IOException {
|
public static void main(final String[] args) throws IOException {
|
||||||
|
@@ -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;
|
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;
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
|
|
||||||
final class HDRMetadata extends AbstractMetadata {
|
public class HDRMetadata extends StandardImageMetadataSupport {
|
||||||
private final HDRHeader header;
|
public HDRMetadata(ImageTypeSpecifier type, HDRHeader header) {
|
||||||
|
super(builder(type)
|
||||||
HDRMetadata(final HDRHeader header) {
|
.withCompressionName("RLE")
|
||||||
this.header = header;
|
.withTextEntry("Software", header.getSoftware()));
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For HDR, the stored sample data is UnsignedIntegral and data is 4 channels (RGB+Exp),
|
||||||
|
// but decoded to Real (float) 3 chanel RGB
|
||||||
@Override
|
@Override
|
||||||
protected IIOMetadataNode getStandardDataNode() {
|
protected IIOMetadataNode getStandardDataNode() {
|
||||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||||
@@ -92,38 +28,4 @@ final class HDRMetadata extends AbstractMetadata {
|
|||||||
|
|
||||||
return node;
|
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
|
|
||||||
}
|
}
|
||||||
|
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
@@ -35,11 +35,16 @@ import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
|||||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
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.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -61,10 +66,9 @@ import java.util.List;
|
|||||||
* @see <a href="http://en.wikipedia.org/wiki/Apple_Icon_Image_format">Apple Icon Image format (Wikipedia)</a>
|
* @see <a href="http://en.wikipedia.org/wiki/Apple_Icon_Image_format">Apple Icon Image format (Wikipedia)</a>
|
||||||
*/
|
*/
|
||||||
public final class ICNSImageReader extends ImageReaderBase {
|
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?
|
// TODO: Subsampled reading for completeness, even if never used?
|
||||||
private List<IconResource> icons = new ArrayList<IconResource>();
|
private final List<IconResource> icons = new ArrayList<>();
|
||||||
private List<IconResource> masks = new ArrayList<IconResource>();
|
private final List<IconResource> masks = new ArrayList<>();
|
||||||
private IconResource lastResourceRead;
|
private IconResource lastResourceRead;
|
||||||
|
|
||||||
private int length;
|
private int length;
|
||||||
@@ -136,7 +140,7 @@ public final class ICNSImageReader extends ImageReaderBase {
|
|||||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||||
IconResource resource = readIconResource(imageIndex);
|
IconResource resource = readIconResource(imageIndex);
|
||||||
|
|
||||||
List<ImageTypeSpecifier> specifiers = new ArrayList<ImageTypeSpecifier>();
|
List<ImageTypeSpecifier> specifiers = new ArrayList<>();
|
||||||
|
|
||||||
switch (resource.depth()) {
|
switch (resource.depth()) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -230,14 +234,9 @@ public final class ICNSImageReader extends ImageReaderBase {
|
|||||||
packedSize -= 4;
|
packedSize -= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream input = IIOUtil.createStreamAdapter(imageInput, packedSize);
|
try (InputStream input = IIOUtil.createStreamAdapter(imageInput, packedSize)) {
|
||||||
|
|
||||||
try {
|
|
||||||
ICNSUtil.decompress(new DataInputStream(input), data, 0, (data.length * 24) / 32); // 24 bit data
|
ICNSUtil.decompress(new DataInputStream(input), data, 0, (data.length * 24) / 32); // 24 bit data
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
input.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data = new byte[resource.length - ICNS.RESOURCE_HEADER_SIZE];
|
data = new byte[resource.length - ICNS.RESOURCE_HEADER_SIZE];
|
||||||
@@ -491,7 +490,7 @@ public final class ICNSImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
String format;
|
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";
|
format = "PNG";
|
||||||
}
|
}
|
||||||
else if (Arrays.equals(ICNS.JPEG_2000_MAGIC, magic)) {
|
else if (Arrays.equals(ICNS.JPEG_2000_MAGIC, magic)) {
|
||||||
@@ -527,7 +526,6 @@ public final class ICNSImageReader extends ImageReaderBase {
|
|||||||
IconResource resource = IconResource.read(imageInput);
|
IconResource resource = IconResource.read(imageInput);
|
||||||
|
|
||||||
if (resource.isTOC()) {
|
if (resource.isTOC()) {
|
||||||
// TODO: IconResource.readTOC()?
|
|
||||||
int resourceCount = (resource.length - ICNS.RESOURCE_HEADER_SIZE) / ICNS.RESOURCE_HEADER_SIZE;
|
int resourceCount = (resource.length - ICNS.RESOURCE_HEADER_SIZE) / ICNS.RESOURCE_HEADER_SIZE;
|
||||||
long pos = resource.start + resource.length;
|
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 {
|
private static final class ICNSBitMaskColorModel extends IndexColorModel {
|
||||||
static final IndexColorModel INSTANCE = new ICNSBitMaskColorModel();
|
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 {
|
public static void main(String[] args) throws IOException {
|
||||||
int argIndex = 0;
|
int argIndex = 0;
|
||||||
|
|
||||||
|
@@ -34,7 +34,13 @@ import com.twelvemonkeys.imageio.ImageWriterBase;
|
|||||||
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
|
import com.twelvemonkeys.imageio.stream.SubImageOutputStream;
|
||||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
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.event.IIOWriteWarningListener;
|
||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
import javax.imageio.spi.ImageWriterSpi;
|
import javax.imageio.spi.ImageWriterSpi;
|
||||||
@@ -104,6 +110,7 @@ public final class ICNSImageWriter extends ImageWriterBase {
|
|||||||
sequenceIndex = 0;
|
sequenceIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("RedundantThrows")
|
||||||
@Override
|
@Override
|
||||||
public void endWriteSequence() throws IOException {
|
public void endWriteSequence() throws IOException {
|
||||||
assertOutput();
|
assertOutput();
|
||||||
|
@@ -38,8 +38,13 @@ import javax.imageio.ImageIO;
|
|||||||
import javax.imageio.ImageReadParam;
|
import javax.imageio.ImageReadParam;
|
||||||
import javax.imageio.ImageReader;
|
import javax.imageio.ImageReader;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.*;
|
||||||
import java.io.*;
|
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;
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,17 +145,12 @@ final class SipsJP2Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String checkErrorMessage(final Process process) throws IOException {
|
private static String checkErrorMessage(final Process process) throws IOException {
|
||||||
InputStream stream = process.getErrorStream();
|
try (InputStream stream = process.getErrorStream()) {
|
||||||
|
|
||||||
try {
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
|
||||||
String message = reader.readLine();
|
String message = reader.readLine();
|
||||||
|
|
||||||
return message != null && message.startsWith("Error: ") ? message.substring(7) : null;
|
return message != null && message.startsWith("Error: ") ? message.substring(7) : null;
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
stream.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String[] buildCommand(final File sipsCommand, final File tempFile) {
|
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 {
|
private static File dumpToFile(final ImageInputStream stream) throws IOException {
|
||||||
File tempFile = File.createTempFile("imageio-icns-", ".png");
|
File tempFile = File.createTempFile("imageio-icns-", ".png");
|
||||||
tempFile.deleteOnExit();
|
tempFile.deleteOnExit();
|
||||||
|
|
||||||
FileOutputStream out = new FileOutputStream(tempFile);
|
try (FileOutputStream out = new FileOutputStream(tempFile)) {
|
||||||
|
|
||||||
try {
|
|
||||||
FileUtil.copy(IIOUtil.createStreamAdapter(stream), out);
|
FileUtil.copy(IIOUtil.createStreamAdapter(stream), out);
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
out.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return tempFile;
|
return tempFile;
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.iff;
|
package com.twelvemonkeys.imageio.plugins.iff;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.*;
|
||||||
import java.awt.image.ColorModel;
|
|
||||||
import java.awt.image.IndexColorModel;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
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.imageio.plugins.iff.IFFUtil.toChunkStr;
|
||||||
|
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form.
|
* Form.
|
||||||
@@ -27,7 +27,7 @@ abstract class Form {
|
|||||||
|
|
||||||
abstract int width();
|
abstract int width();
|
||||||
abstract int height();
|
abstract int height();
|
||||||
abstract float aspect();
|
abstract double aspect();
|
||||||
abstract int bitplanes();
|
abstract int bitplanes();
|
||||||
abstract int compressionType();
|
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) {
|
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.bitmapHeader = bitmapHeader;
|
||||||
this.viewMode = viewMode;
|
this.viewMode = viewMode;
|
||||||
this.colorMap = colorMap;
|
this.colorMap = colorMap;
|
||||||
@@ -127,6 +127,19 @@ abstract class Form {
|
|||||||
this.body = body;
|
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
|
@Override
|
||||||
int width() {
|
int width() {
|
||||||
return bitmapHeader.width;
|
return bitmapHeader.width;
|
||||||
@@ -148,8 +161,8 @@ abstract class Form {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
float aspect() {
|
double aspect() {
|
||||||
return bitmapHeader.yAspect == 0 ? 0 : (bitmapHeader.xAspect / (float) bitmapHeader.yAspect);
|
return bitmapHeader.yAspect == 0 ? 0 : (bitmapHeader.xAspect / (double) bitmapHeader.yAspect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
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.deepGlobal = deepGlobal;
|
||||||
this.deepLocation = deepLocation;
|
this.deepLocation = deepLocation;
|
||||||
this.deepPixel = deepPixel;
|
this.deepPixel = deepPixel;
|
||||||
@@ -305,6 +318,15 @@ abstract class Form {
|
|||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean validFormType(int formType) {
|
||||||
|
switch (formType) {
|
||||||
|
case TYPE_DEEP:
|
||||||
|
case TYPE_TVPP:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
int width() {
|
int width() {
|
||||||
@@ -337,8 +359,8 @@ abstract class Form {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
float aspect() {
|
double aspect() {
|
||||||
return deepGlobal.yAspect == 0 ? 0 : deepGlobal.xAspect / (float) deepGlobal.yAspect;
|
return deepGlobal.yAspect == 0 ? 0 : deepGlobal.xAspect / (double) deepGlobal.yAspect;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -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;
|
package com.twelvemonkeys.imageio.plugins.iff;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||||
|
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import java.awt.*;
|
import java.awt.image.*;
|
||||||
import java.awt.image.IndexColorModel;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.twelvemonkeys.imageio.plugins.iff.IFF.*;
|
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 com.twelvemonkeys.lang.Validate.notNull;
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
final class IFFImageMetadata extends AbstractMetadata {
|
final class IFFImageMetadata extends StandardImageMetadataSupport {
|
||||||
private final Form header;
|
IFFImageMetadata(ImageTypeSpecifier type, Form header, IndexColorModel palette) {
|
||||||
private final IndexColorModel colorMap;
|
this(builder(type), notNull(header, "header"), palette);
|
||||||
private final List<GenericChunk> 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validFormType(int formType) {
|
private IFFImageMetadata(Builder builder, Form header, IndexColorModel palette) {
|
||||||
switch (formType) {
|
super(builder.withPalette(palette)
|
||||||
default:
|
.withCompressionName(compressionName(header))
|
||||||
return false;
|
.withBitsPerSample(bitsPerSample(header))
|
||||||
case TYPE_ACBM:
|
.withPlanarConfiguration(planarConfiguration(header))
|
||||||
case TYPE_DEEP:
|
.withPixelAspectRatio(header.aspect() != 0 ? header.aspect() : null)
|
||||||
case TYPE_ILBM:
|
.withFormatVersion("1.0")
|
||||||
case TYPE_PBM:
|
.withTextEntries(textEntries(header)));
|
||||||
case TYPE_RGB8:
|
|
||||||
case TYPE_RGBN:
|
|
||||||
case TYPE_TVPP:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static String compressionName(Form header) {
|
||||||
protected IIOMetadataNode getStandardChromaNode() {
|
switch (header.compressionType()) {
|
||||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
case BMHDChunk.COMPRESSION_NONE:
|
||||||
|
return "None";
|
||||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
case BMHDChunk.COMPRESSION_BYTE_RUN:
|
||||||
chroma.appendChild(csType);
|
return "RLE";
|
||||||
|
|
||||||
switch (header.bitplanes()) {
|
|
||||||
case 8:
|
|
||||||
if (colorMap == null) {
|
|
||||||
csType.setAttribute("name", "GRAY");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
case 4:
|
case 4:
|
||||||
case 5:
|
// Compression type 4 means different things for different FORM types, we support
|
||||||
case 6:
|
// Impulse RGB8 RLE compression: 24 bit RGB + 1 bit mask + 7 bit run count
|
||||||
case 7:
|
if (header.formType == TYPE_RGB8) {
|
||||||
case 24:
|
return "RGB8";
|
||||||
case 25:
|
}
|
||||||
case 32:
|
|
||||||
csType.setAttribute("name", "RGB");
|
|
||||||
break;
|
|
||||||
default:
|
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
|
private static int[] bitsPerSample(Form header) {
|
||||||
protected IIOMetadataNode getStandardCompressionNode() {
|
int bitplanes = header.bitplanes();
|
||||||
if (header.compressionType() == BMHDChunk.COMPRESSION_NONE) {
|
|
||||||
return null; // All defaults
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
switch (bitplanes) {
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
@@ -192,91 +89,47 @@ final class IFFImageMetadata extends AbstractMetadata {
|
|||||||
case 6:
|
case 6:
|
||||||
case 7:
|
case 7:
|
||||||
case 8:
|
case 8:
|
||||||
return Integer.toString(bitplanes);
|
return new int[] {bitplanes};
|
||||||
case 24:
|
case 24:
|
||||||
return "8 8 8";
|
return new int[] {8, 8, 8};
|
||||||
case 25:
|
case 25:
|
||||||
if (header.formType != TYPE_RGB8) {
|
if (header.formType != TYPE_RGB8) {
|
||||||
throw new IllegalArgumentException(String.format("25 bit depth only supported for FORM type RGB8: %s", IFFUtil.toChunkStr(header.formType)));
|
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:
|
case 32:
|
||||||
return "8 8 8 8";
|
return new int[] {8, 8, 8, 8};
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown bit count: " + bitplanes);
|
throw new IllegalArgumentException("Unknown bit count: " + bitplanes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static PlanarConfiguration planarConfiguration(Form header) {
|
||||||
protected IIOMetadataNode getStandardDimensionNode() {
|
switch (header.formType) {
|
||||||
if (header.aspect() == 0) {
|
case TYPE_DEEP:
|
||||||
return null;
|
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
|
private static List<Map.Entry<String, String>> textEntries(Form header) {
|
||||||
protected IIOMetadataNode getStandardDocumentNode() {
|
if (header.meta.isEmpty()) {
|
||||||
IIOMetadataNode document = new IIOMetadataNode("Document");
|
return emptyList();
|
||||||
|
|
||||||
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
|
||||||
document.appendChild(formatVersion);
|
|
||||||
formatVersion.setAttribute("value", "1.0");
|
|
||||||
|
|
||||||
return document;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected IIOMetadataNode getStandardTextNode() {
|
|
||||||
if (meta.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
List<Map.Entry<String, String>> text = new ArrayList<>();
|
||||||
|
for (GenericChunk chunk : header.meta) {
|
||||||
// /Text/TextEntry@keyword = field name, /Text/TextEntry@value = field value.
|
text.add(new SimpleImmutableEntry<>(toChunkStr(chunk.chunkId),
|
||||||
for (GenericChunk chunk : meta) {
|
new String(chunk.data, chunk.chunkId == IFF.CHUNK_UTF8 ? StandardCharsets.UTF_8 : StandardCharsets.US_ASCII)));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -38,14 +38,19 @@ import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
|||||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
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.metadata.IIOMetadata;
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -350,9 +355,7 @@ public final class IFFImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||||
init(imageIndex);
|
return new IFFImageMetadata(getRawImageType(imageIndex), header, header.colorMap());
|
||||||
|
|
||||||
return new IFFImageMetadata(header, header.colorMap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -1,24 +1,37 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.iff;
|
package com.twelvemonkeys.imageio.plugins.iff;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.function.ThrowingRunnable;
|
import org.junit.function.ThrowingRunnable;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
import java.awt.image.IndexColorModel;
|
import java.awt.image.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import static java.awt.image.BufferedImage.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class IFFImageMetadataTest {
|
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
|
@Test
|
||||||
public void testStandardFeatures() throws IIOException {
|
public void testStandardFeatures() throws IIOException {
|
||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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
|
// Standard metadata format
|
||||||
assertTrue(metadata.isStandardMetadataFormatSupported());
|
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||||
@@ -51,9 +64,9 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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);
|
assertNotNull(chroma);
|
||||||
assertEquals("Chroma", chroma.getNodeName());
|
assertEquals("Chroma", chroma.getNodeName());
|
||||||
assertEquals(3, chroma.getLength());
|
assertEquals(3, chroma.getLength());
|
||||||
@@ -78,9 +91,9 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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);
|
assertNotNull(chroma);
|
||||||
assertEquals("Chroma", chroma.getNodeName());
|
assertEquals("Chroma", chroma.getNodeName());
|
||||||
assertEquals(3, chroma.getLength());
|
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));
|
.with(new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1));
|
||||||
|
|
||||||
byte[] bw = {0, (byte) 0xff};
|
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);
|
assertNotNull(chroma);
|
||||||
assertEquals("Chroma", chroma.getNodeName());
|
assertEquals("Chroma", chroma.getNodeName());
|
||||||
assertEquals(5, chroma.getLength());
|
assertEquals(5, chroma.getLength());
|
||||||
@@ -119,7 +133,7 @@ public class IFFImageMetadataTest {
|
|||||||
|
|
||||||
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
|
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
|
||||||
assertEquals("NumChannels", numChannels.getNodeName());
|
assertEquals("NumChannels", numChannels.getNodeName());
|
||||||
assertEquals("3", numChannels.getAttribute("value"));
|
assertEquals("4", numChannels.getAttribute("value"));
|
||||||
|
|
||||||
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
|
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
|
||||||
assertEquals("BlackIsZero", blackIsZero.getNodeName());
|
assertEquals("BlackIsZero", blackIsZero.getNodeName());
|
||||||
@@ -153,9 +167,9 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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);
|
assertNotNull(compression);
|
||||||
assertEquals("Compression", compression.getNodeName());
|
assertEquals("Compression", compression.getNodeName());
|
||||||
assertEquals(2, compression.getLength());
|
assertEquals(2, compression.getLength());
|
||||||
@@ -176,9 +190,9 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_NONE, 0));
|
.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
|
@Test
|
||||||
@@ -186,9 +200,9 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -213,9 +227,9 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -240,9 +254,9 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 32, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
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));
|
.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
|
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);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -297,9 +312,9 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_PBM)
|
Form header = Form.ofType(IFF.TYPE_PBM)
|
||||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -324,9 +339,9 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_PBM)
|
Form header = Form.ofType(IFF.TYPE_PBM)
|
||||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -356,10 +371,20 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(bitmapHeader);
|
.with(bitmapHeader);
|
||||||
|
|
||||||
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");
|
||||||
assertNull(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
|
@Test
|
||||||
@@ -368,20 +393,24 @@ public class IFFImageMetadataTest {
|
|||||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
||||||
.with(new CAMGChunk(4));
|
.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
|
// No Dimension node is okay, or one with an aspect ratio of 1.0
|
||||||
if (dimension != null) {
|
if (dimension != null) {
|
||||||
assertEquals("Dimension", dimension.getNodeName());
|
assertEquals("Dimension", dimension.getNodeName());
|
||||||
assertEquals(1, dimension.getLength());
|
assertEquals(2, dimension.getLength());
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
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(bitmapHeader)
|
||||||
.with(viewPort);
|
.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);
|
assertNotNull(dimension);
|
||||||
assertEquals("Dimension", dimension.getNodeName());
|
assertEquals("Dimension", dimension.getNodeName());
|
||||||
assertEquals(1, dimension.getLength());
|
assertEquals(2, dimension.getLength());
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||||
assertEquals("2.0", pixelAspectRatio.getAttribute("value"));
|
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
|
@Test
|
||||||
@@ -425,18 +458,22 @@ public class IFFImageMetadataTest {
|
|||||||
.with(bitmapHeader)
|
.with(bitmapHeader)
|
||||||
.with(viewPort);
|
.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);
|
assertNotNull(dimension);
|
||||||
assertEquals("Dimension", dimension.getNodeName());
|
assertEquals("Dimension", dimension.getNodeName());
|
||||||
assertEquals(1, dimension.getLength());
|
assertEquals(2, dimension.getLength());
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||||
assertEquals("0.5", pixelAspectRatio.getAttribute("value"));
|
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
|
@Test
|
||||||
@@ -447,18 +484,22 @@ public class IFFImageMetadataTest {
|
|||||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
||||||
.with(viewPort);
|
.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);
|
assertNotNull(dimension);
|
||||||
assertEquals("Dimension", dimension.getNodeName());
|
assertEquals("Dimension", dimension.getNodeName());
|
||||||
assertEquals(1, dimension.getLength());
|
assertEquals(2, dimension.getLength());
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
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
|
@Test
|
||||||
@@ -466,32 +507,33 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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);
|
assertNotNull(document);
|
||||||
assertEquals("Document", document.getNodeName());
|
assertEquals("Document", document.getNodeName());
|
||||||
assertEquals(1, document.getLength());
|
assertEquals(1, document.getLength());
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) document.getFirstChild();
|
IIOMetadataNode formatVersion = (IIOMetadataNode) document.getFirstChild();
|
||||||
assertEquals("FormatVersion", pixelAspectRatio.getNodeName());
|
assertEquals("FormatVersion", formatVersion.getNodeName());
|
||||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
assertEquals("1.0", formatVersion.getAttribute("value"));
|
||||||
|
|
||||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
assertNull(formatVersion.getNextSibling()); // No more children
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardText() throws IIOException {
|
public void testStandardText() throws IIOException {
|
||||||
int[] chunks = {IFF.CHUNK_ANNO, IFF.CHUNK_UTF8};
|
int[] chunks = {IFF.CHUNK_ANNO, IFF.CHUNK_ANNO, IFF.CHUNK_UTF8};
|
||||||
String[] texts = {"annotation", "äñnótâtïøñ"};
|
String[] texts = {"annotation", "dupe", "äñnótâtïøñ"};
|
||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 8, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0))
|
.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[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);
|
assertNotNull(text);
|
||||||
assertEquals("Text", text.getNodeName());
|
assertEquals("Text", text.getNodeName());
|
||||||
assertEquals(texts.length, text.getLength());
|
assertEquals(texts.length, text.getLength());
|
||||||
@@ -509,10 +551,21 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 24, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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();
|
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||||
assertNull(transparency); // No transparency, just defaults
|
|
||||||
|
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
|
@Test
|
||||||
@@ -520,18 +573,18 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_ILBM)
|
Form header = Form.ofType(IFF.TYPE_ILBM)
|
||||||
.with(new BMHDChunk(300, 200, 32, BMHDChunk.MASK_HAS_MASK, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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);
|
assertNotNull(transparency);
|
||||||
assertEquals("Transparency", transparency.getNodeName());
|
assertEquals("Transparency", transparency.getNodeName());
|
||||||
assertEquals(1, transparency.getLength());
|
assertEquals(1, transparency.getLength());
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
|
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
||||||
assertEquals("Alpha", pixelAspectRatio.getNodeName());
|
assertEquals("Alpha", alpha.getNodeName());
|
||||||
assertEquals("nonpremultiplied", pixelAspectRatio.getAttribute("value"));
|
assertEquals("nonpremultiplied", alpha.getAttribute("value"));
|
||||||
|
|
||||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
assertNull(alpha.getNextSibling()); // No more children
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -540,28 +593,33 @@ public class IFFImageMetadataTest {
|
|||||||
.with(new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1));
|
.with(new BMHDChunk(300, 200, 1, BMHDChunk.MASK_TRANSPARENT_COLOR, BMHDChunk.COMPRESSION_BYTE_RUN, 1));
|
||||||
|
|
||||||
byte[] bw = {0, (byte) 0xff};
|
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);
|
assertNotNull(transparency);
|
||||||
assertEquals("Transparency", transparency.getNodeName());
|
assertEquals("Transparency", transparency.getNodeName());
|
||||||
assertEquals(1, transparency.getLength());
|
assertEquals(2, transparency.getLength());
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
|
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
||||||
assertEquals("TransparentIndex", pixelAspectRatio.getNodeName());
|
assertEquals("Alpha", alpha.getNodeName());
|
||||||
assertEquals("1", pixelAspectRatio.getAttribute("value"));
|
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
|
@Test
|
||||||
public void testStandardRGB8() throws IIOException {
|
public void testStandardRGB8() throws IIOException {
|
||||||
Form header = Form.ofType(IFF.TYPE_RGB8)
|
Form header = Form.ofType(IFF.TYPE_RGB8)
|
||||||
.with(new BMHDChunk(300, 200, 25, BMHDChunk.MASK_NONE, BMHDChunk.COMPRESSION_BYTE_RUN, 0));
|
.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
|
// Chroma
|
||||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||||
assertNotNull(chroma);
|
assertNotNull(chroma);
|
||||||
assertEquals("Chroma", chroma.getNodeName());
|
assertEquals("Chroma", chroma.getNodeName());
|
||||||
assertEquals(3, chroma.getLength());
|
assertEquals(3, chroma.getLength());
|
||||||
@@ -581,7 +639,7 @@ public class IFFImageMetadataTest {
|
|||||||
assertNull(blackIsZero.getNextSibling()); // No more children
|
assertNull(blackIsZero.getNextSibling()); // No more children
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||||
assertNotNull(data);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -601,7 +659,7 @@ public class IFFImageMetadataTest {
|
|||||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||||
|
|
||||||
// Transparency
|
// Transparency
|
||||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||||
assertNotNull(transparency);
|
assertNotNull(transparency);
|
||||||
assertEquals("Transparency", transparency.getNodeName());
|
assertEquals("Transparency", transparency.getNodeName());
|
||||||
assertEquals(1, transparency.getLength());
|
assertEquals(1, transparency.getLength());
|
||||||
@@ -624,10 +682,10 @@ public class IFFImageMetadataTest {
|
|||||||
Form header = Form.ofType(IFF.TYPE_DEEP)
|
Form header = Form.ofType(IFF.TYPE_DEEP)
|
||||||
.with(new DGBLChunk(8))
|
.with(new DGBLChunk(8))
|
||||||
.with(dpel);
|
.with(dpel);
|
||||||
IFFImageMetadata metadata = new IFFImageMetadata(header, null);
|
IFFImageMetadata metadata = new IFFImageMetadata(TYPE_32_BIT_ARGB_DEEP, header, header.colorMap());
|
||||||
|
|
||||||
// Chroma
|
// Chroma
|
||||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||||
assertNotNull(chroma);
|
assertNotNull(chroma);
|
||||||
assertEquals("Chroma", chroma.getNodeName());
|
assertEquals("Chroma", chroma.getNodeName());
|
||||||
assertEquals(3, chroma.getLength());
|
assertEquals(3, chroma.getLength());
|
||||||
@@ -649,7 +707,7 @@ public class IFFImageMetadataTest {
|
|||||||
assertNull(blackIsZero.getNextSibling()); // No more children
|
assertNull(blackIsZero.getNextSibling()); // No more children
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||||
assertNotNull(data);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -669,7 +727,7 @@ public class IFFImageMetadataTest {
|
|||||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||||
|
|
||||||
// Transparency
|
// Transparency
|
||||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||||
assertNotNull(transparency);
|
assertNotNull(transparency);
|
||||||
assertEquals("Transparency", transparency.getNodeName());
|
assertEquals("Transparency", transparency.getNodeName());
|
||||||
assertEquals(1, transparency.getLength());
|
assertEquals(1, transparency.getLength());
|
||||||
@@ -680,4 +738,13 @@ public class IFFImageMetadataTest {
|
|||||||
|
|
||||||
assertNull(alpha.getNextSibling()); // No more children
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -45,7 +45,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
|
|||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
@@ -377,10 +377,12 @@ public final class PCXImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||||
checkBounds(imageIndex);
|
// checkBounds(imageIndex);
|
||||||
readHeader();
|
// readHeader();
|
||||||
|
//
|
||||||
return new PCXMetadata(header, getVGAPalette());
|
// return new PCXMetadata(header, getVGAPalette());
|
||||||
|
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||||
|
return new PCXMetadata(rawType, header);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndexColorModel getVGAPalette() throws IOException {
|
private IndexColorModel getVGAPalette() throws IOException {
|
||||||
|
@@ -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;
|
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||||
|
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import java.awt.image.IndexColorModel;
|
|
||||||
|
|
||||||
final class PCXMetadata extends AbstractMetadata {
|
final class PCXMetadata extends StandardImageMetadataSupport {
|
||||||
private final PCXHeader header;
|
public PCXMetadata(ImageTypeSpecifier type, PCXHeader header) {
|
||||||
private final IndexColorModel vgaPalette;
|
super(builder(type)
|
||||||
|
.withPlanarConfiguration(planarConfiguration(header))
|
||||||
PCXMetadata(final PCXHeader header, final IndexColorModel vgaPalette) {
|
.withCompressionName(compressionName(header))
|
||||||
this.header = header;
|
.withFormatVersion(String.valueOf(header.getVersion())));
|
||||||
this.vgaPalette = vgaPalette;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static PlanarConfiguration planarConfiguration(PCXHeader header) {
|
||||||
protected IIOMetadataNode getStandardChromaNode() {
|
System.out.println("header = " + header);
|
||||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
return header.getChannels() > 1 ? PlanarConfiguration.LineInterleaved : null;
|
||||||
|
}
|
||||||
|
|
||||||
IndexColorModel palette = null;
|
private static String compressionName(PCXHeader header) {
|
||||||
boolean gray = false;
|
switch (header.getCompression()) {
|
||||||
|
case PCX.COMPRESSION_NONE:
|
||||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
return "None";
|
||||||
switch (header.getBitsPerPixel()) {
|
case PCX.COMPRESSION_RLE:
|
||||||
case 1:
|
return "RLE";
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chroma.appendChild(csType);
|
return "Uknown";
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -67,18 +67,28 @@ import com.twelvemonkeys.io.enc.Decoder;
|
|||||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
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.metadata.IIOMetadata;
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.*;
|
||||||
import java.awt.geom.Area;
|
|
||||||
import java.awt.image.*;
|
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.List;
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reader for Apple Mac Paint Picture (PICT) format.
|
* Reader for Apple Mac Paint Picture (PICT) format.
|
||||||
@@ -2611,7 +2621,9 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
return getYPtCoord(getPICTFrame().height);
|
return getYPtCoord(getPICTFrame().height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterator<ImageTypeSpecifier> getImageTypes(int pIndex) {
|
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||||
|
checkBounds(imageIndex);
|
||||||
|
|
||||||
// TODO: The images look slightly different in Preview.. Could indicate the color space is wrong...
|
// TODO: The images look slightly different in Preview.. Could indicate the color space is wrong...
|
||||||
return Collections.singletonList(
|
return Collections.singletonList(
|
||||||
ImageTypeSpecifiers.createPacked(
|
ImageTypeSpecifiers.createPacked(
|
||||||
@@ -2623,10 +2635,10 @@ public final class PICTImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
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
|
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) {
|
protected static void showIt(final BufferedImage pImage, final String pTitle) {
|
||||||
|
@@ -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;
|
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.
|
* PICTMetadata.
|
||||||
@@ -11,82 +41,13 @@ import javax.imageio.metadata.IIOMetadataNode;
|
|||||||
* @author last modified by $Author: haraldk$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PICTMetadata.java,v 1.0 23/03/2021 haraldk Exp$
|
* @version $Id: PICTMetadata.java,v 1.0 23/03/2021 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class PICTMetadata extends AbstractMetadata {
|
final class PICTMetadata extends StandardImageMetadataSupport {
|
||||||
|
PICTMetadata(final ImageTypeSpecifier type, final int version, final double screenImageXRatio, final double screenImageYRatio) {
|
||||||
private final int version;
|
super(builder(type)
|
||||||
private final double screenImageXRatio;
|
.withPixelAspectRatio(screenImageXRatio > 0.0d && screenImageYRatio > 0.0d ? screenImageXRatio / screenImageYRatio : 1)
|
||||||
private final double screenImageYRatio;
|
.withFormatVersion(Integer.toString(version))
|
||||||
|
);
|
||||||
PICTMetadata(final int version, final double screenImageXRatio, final double screenImageYRatio) {
|
// As this is a vector-ish format, some of the data makes no sense... :-P
|
||||||
this.version = version;
|
// It is, however, consistent with the getRawImageTyp/getImageTypes
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||||
@@ -34,7 +64,7 @@ public final class PNTGImageReader extends ImageReaderBase {
|
|||||||
private static final Set<ImageTypeSpecifier> IMAGE_TYPES =
|
private static final Set<ImageTypeSpecifier> IMAGE_TYPES =
|
||||||
Collections.singleton(ImageTypeSpecifiers.createIndexed(new int[] {-1, 0}, false, -1, 1, DataBuffer.TYPE_BYTE));
|
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);
|
super(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,9 +153,7 @@ public final class PNTGImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||||
checkBounds(imageIndex);
|
return new PNTGMetadata(getRawImageType(imageIndex));
|
||||||
|
|
||||||
return new PNTGMetadata();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readHeader() throws IOException {
|
private void readHeader() throws IOException {
|
||||||
|
@@ -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;
|
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||||
|
@@ -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;
|
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.
|
* PNTGMetadata.
|
||||||
@@ -11,76 +41,11 @@ import javax.imageio.metadata.IIOMetadataNode;
|
|||||||
* @author last modified by $Author: haraldk$
|
* @author last modified by $Author: haraldk$
|
||||||
* @version $Id: PNTGMetadata.java,v 1.0 23/03/2021 haraldk Exp$
|
* @version $Id: PNTGMetadata.java,v 1.0 23/03/2021 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class PNTGMetadata extends AbstractMetadata {
|
final class PNTGMetadata extends StandardImageMetadataSupport {
|
||||||
@Override
|
public PNTGMetadata(ImageTypeSpecifier type) {
|
||||||
protected IIOMetadataNode getStandardChromaNode() {
|
super(builder(type)
|
||||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
.withBlackIsZero(false)
|
||||||
|
.withCompressionName("PackBits")
|
||||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
.withFormatVersion("1.0"));
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Harald Kuhr
|
* Copyright (c) 2021, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@@ -1,7 +1,11 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.pntg;
|
package com.twelvemonkeys.imageio.plugins.pntg;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PNTGMetadataTest.
|
* PNTGMetadataTest.
|
||||||
*
|
*
|
||||||
@@ -12,6 +16,6 @@ import org.junit.Test;
|
|||||||
public class PNTGMetadataTest {
|
public class PNTGMetadataTest {
|
||||||
@Test
|
@Test
|
||||||
public void testCreate() {
|
public void testCreate() {
|
||||||
new PNTGMetadata();
|
new PNTGMetadata(ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_BYTE_BINARY));
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -43,7 +43,7 @@ import javax.imageio.metadata.IIOMetadata;
|
|||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
@@ -468,10 +468,7 @@ public final class PNMImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||||
checkBounds(imageIndex);
|
return new PNMMetadata(getRawImageType(imageIndex), header);
|
||||||
readHeader();
|
|
||||||
|
|
||||||
return new PNMMetadata(header);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, Harald Kuhr
|
* Copyright (c) 2022, Harald Kuhr
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -30,92 +30,35 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.pnm;
|
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 javax.imageio.metadata.IIOMetadataNode;
|
||||||
import java.awt.*;
|
import java.awt.image.*;
|
||||||
import java.awt.image.DataBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
final class PNMMetadata extends AbstractMetadata {
|
/**
|
||||||
|
* PNMMetadata.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
*/
|
||||||
|
final class PNMMetadata extends StandardImageMetadataSupport {
|
||||||
private final PNMHeader header;
|
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;
|
this.header = header;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static int significantBits(PNMHeader header) {
|
||||||
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() {
|
|
||||||
if (header.getTransferType() == DataBuffer.TYPE_FLOAT) {
|
if (header.getTransferType() == DataBuffer.TYPE_FLOAT) {
|
||||||
return header.getBitsPerSample();
|
return header.getBitsPerSample();
|
||||||
}
|
}
|
||||||
@@ -132,38 +75,30 @@ final class PNMMetadata extends AbstractMetadata {
|
|||||||
return significantBits;
|
return significantBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createListValue(final int itemCount, final String... values) {
|
private static ColorSpaceType colorSpace(PNMHeader header) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
switch (header.getTupleType()) {
|
||||||
|
case BLACKANDWHITE:
|
||||||
for (int i = 0; i < itemCount; i++) {
|
case BLACKANDWHITE_ALPHA:
|
||||||
if (buffer.length() > 0) {
|
case BLACKANDWHITE_WHITE_IS_ZERO:
|
||||||
buffer.append(' ');
|
case GRAYSCALE:
|
||||||
}
|
case GRAYSCALE_ALPHA:
|
||||||
|
return ColorSpaceType.GRAY;
|
||||||
buffer.append(values[i % values.length]);
|
default:
|
||||||
|
return null; // Fall back to color model's type
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static ImageOrientation orientation(PNMHeader header) {
|
||||||
protected IIOMetadataNode getStandardDimensionNode() {
|
// For some reason, the float values are stored bottom-up
|
||||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
return header.getFileType() == PNM.PFM_GRAY || header.getFileType() == PNM.PFM_RGB
|
||||||
|
? ImageOrientation.FlipH
|
||||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
: ImageOrientation.Normal;
|
||||||
imageOrientation.setAttribute("value",
|
|
||||||
header.getFileType() == PNM.PFM_GRAY || header.getFileType() == PNM.PFM_RGB
|
|
||||||
? "FlipH"
|
|
||||||
: "Normal");
|
|
||||||
dimension.appendChild(imageOrientation);
|
|
||||||
|
|
||||||
return dimension;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No document node
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected IIOMetadataNode getStandardTextNode() {
|
protected IIOMetadataNode getStandardTextNode() {
|
||||||
|
// TODO: Could avoid this override, by changing the StandardImageMetadataSupport to
|
||||||
|
// use List<Entry<String, String>> instead of Map<String, String> (we use duplicate "comment"s).
|
||||||
if (!header.getComments().isEmpty()) {
|
if (!header.getComments().isEmpty()) {
|
||||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
IIOMetadataNode text = new IIOMetadataNode("Text");
|
||||||
|
|
||||||
@@ -179,17 +114,4 @@ final class PNMMetadata extends AbstractMetadata {
|
|||||||
|
|
||||||
return null;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,7 @@ package com.twelvemonkeys.imageio.plugins.sgi;
|
|||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
final class SGIHeader {
|
final class SGIHeader {
|
||||||
private int compression;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -45,7 +45,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
|
|||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
@@ -378,11 +378,10 @@ public final class SGIImageReader extends ImageReaderBase {
|
|||||||
imageInput.seek(imageInput.getFlushedPosition());
|
imageInput.seek(imageInput.getFlushedPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
@Override
|
||||||
checkBounds(imageIndex);
|
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||||
readHeader();
|
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||||
|
return new SGIMetadata(rawType, header);
|
||||||
return new SGIMetadata(header);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
|
@@ -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;
|
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 {
|
final class SGIMetadata extends StandardImageMetadataSupport {
|
||||||
private final SGIHeader header;
|
public SGIMetadata(ImageTypeSpecifier type, SGIHeader header) {
|
||||||
|
super(builder(type)
|
||||||
SGIMetadata(final SGIHeader header) {
|
.withSignificantBitsPerSample(computeSignificantBits(header))
|
||||||
this.header = header;
|
.withCompressionName(compressionName(header))
|
||||||
|
.withOrientation(ImageOrientation.FlipV)
|
||||||
|
.withTextEntry("DocumentName", header.getName())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static int computeSignificantBits(SGIHeader header) {
|
||||||
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;
|
|
||||||
|
|
||||||
int maxSample = header.getMaxValue();
|
int maxSample = header.getMaxValue();
|
||||||
|
|
||||||
while (maxSample > 0) {
|
int significantBits = 1;
|
||||||
maxSample >>>= 1;
|
|
||||||
|
while ((maxSample >>>= 1) != 0) {
|
||||||
significantBits++;
|
significantBits++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return significantBits;
|
return significantBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createListValue(final int itemCount, final String... values) {
|
private static String compressionName(SGIHeader header) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
switch (header.getCompression()) {
|
||||||
|
case SGI.COMPRESSION_NONE:
|
||||||
for (int i = 0; i < itemCount; i++) {
|
return "None";
|
||||||
if (buffer.length() > 0) {
|
case SGI.COMPRESSION_RLE:
|
||||||
buffer.append(' ');
|
return "RLE";
|
||||||
}
|
|
||||||
|
|
||||||
buffer.append(values[i % values.length]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer.toString();
|
return "Uknown";
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,18 +31,16 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.tga;
|
package com.twelvemonkeys.imageio.plugins.tga;
|
||||||
|
|
||||||
import javax.imageio.IIOException;
|
import javax.imageio.IIOException;
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.image.ColorModel;
|
import java.awt.image.*;
|
||||||
import java.awt.image.IndexColorModel;
|
|
||||||
import java.awt.image.RenderedImage;
|
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||||
import static java.awt.color.ColorSpace.TYPE_GRAY;
|
import static java.awt.color.ColorSpace.*;
|
||||||
import static java.awt.color.ColorSpace.TYPE_RGB;
|
|
||||||
|
|
||||||
final class TGAHeader {
|
final class TGAHeader {
|
||||||
|
|
||||||
@@ -118,10 +116,14 @@ final class TGAHeader {
|
|||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
static TGAHeader from(final RenderedImage image, final boolean compressed) {
|
static TGAHeader from(final ImageTypeSpecifier type, final boolean compressed) {
|
||||||
notNull(image, "image");
|
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;
|
IndexColorModel colorMap = colorModel instanceof IndexColorModel ? (IndexColorModel) colorModel : null;
|
||||||
|
|
||||||
TGAHeader header = new TGAHeader();
|
TGAHeader header = new TGAHeader();
|
||||||
@@ -135,8 +137,8 @@ final class TGAHeader {
|
|||||||
header.x = 0;
|
header.x = 0;
|
||||||
header.y = 0;
|
header.y = 0;
|
||||||
|
|
||||||
header.width = image.getWidth(); // TODO: Param source region/subsampling might affect this
|
header.width = width;
|
||||||
header.height = image.getHeight(); // // TODO: Param source region/subsampling might affect this
|
header.height = height;
|
||||||
header.pixelDepth = colorModel.getPixelSize() == 15 ? 16 : colorModel.getPixelSize();
|
header.pixelDepth = colorModel.getPixelSize() == 15 ? 16 : colorModel.getPixelSize();
|
||||||
|
|
||||||
header.origin = TGA.ORIGIN_UPPER_LEFT; // TODO: Allow parameter to control this?
|
header.origin = TGA.ORIGIN_UPPER_LEFT; // TODO: Allow parameter to control this?
|
||||||
|
@@ -47,7 +47,7 @@ import javax.imageio.metadata.IIOMetadataFormatImpl;
|
|||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import javax.imageio.stream.ImageInputStream;
|
import javax.imageio.stream.ImageInputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -538,10 +538,8 @@ final class TGAImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||||
checkBounds(imageIndex);
|
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||||
readHeader();
|
return new TGAMetadata(rawType, header, extensions);
|
||||||
|
|
||||||
return new TGAMetadata(header, extensions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
|
@@ -37,12 +37,16 @@ import com.twelvemonkeys.io.LittleEndianDataOutputStream;
|
|||||||
import com.twelvemonkeys.io.enc.EncoderStream;
|
import com.twelvemonkeys.io.enc.EncoderStream;
|
||||||
import com.twelvemonkeys.lang.Validate;
|
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.metadata.IIOMetadata;
|
||||||
import javax.imageio.spi.ImageWriterSpi;
|
import javax.imageio.spi.ImageWriterSpi;
|
||||||
import javax.imageio.stream.ImageOutputStream;
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -65,8 +69,7 @@ final class TGAImageWriter extends ImageWriterBase {
|
|||||||
public IIOMetadata getDefaultImageMetadata(final ImageTypeSpecifier imageType, final ImageWriteParam param) {
|
public IIOMetadata getDefaultImageMetadata(final ImageTypeSpecifier imageType, final ImageWriteParam param) {
|
||||||
Validate.notNull(imageType, "imageType");
|
Validate.notNull(imageType, "imageType");
|
||||||
|
|
||||||
TGAHeader header = TGAHeader.from(imageType.createBufferedImage(1, 1), isRLE(param, null));
|
return new TGAMetadata(imageType, TGAHeader.from(imageType, isRLE(param, null)), null);
|
||||||
return new TGAMetadata(header, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -107,7 +110,8 @@ final class TGAImageWriter extends ImageWriterBase {
|
|||||||
|
|
||||||
final boolean compressed = isRLE(param, image.getMetadata());
|
final boolean compressed = isRLE(param, image.getMetadata());
|
||||||
RenderedImage renderedImage = image.getRenderedImage();
|
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);
|
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()
|
? 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
|
: 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()
|
? 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();
|
final DataBuffer buffer = rowRaster.getDataBuffer();
|
||||||
|
|
||||||
@@ -135,7 +139,7 @@ final class TGAImageWriter extends ImageWriterBase {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataOutput imageOutput = compressed ? createRLEStream(header, this.imageOutput) : this.imageOutput;
|
DataOutput imageOutput = compressed ? createRLEStream(this.imageOutput, header.getPixelDepth()) : this.imageOutput;
|
||||||
|
|
||||||
switch (buffer.getDataType()) {
|
switch (buffer.getDataType()) {
|
||||||
case DataBuffer.TYPE_BYTE:
|
case DataBuffer.TYPE_BYTE:
|
||||||
@@ -174,8 +178,8 @@ final class TGAImageWriter extends ImageWriterBase {
|
|||||||
processImageComplete();
|
processImageComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LittleEndianDataOutputStream createRLEStream(final TGAHeader header, final ImageOutputStream stream) {
|
private static LittleEndianDataOutputStream createRLEStream(final ImageOutputStream stream, int pixelDepth) {
|
||||||
return new LittleEndianDataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(stream), new RLEEncoder(header.getPixelDepth())));
|
return new LittleEndianDataOutputStream(new EncoderStream(IIOUtil.createStreamAdapter(stream), new RLEEncoder(pixelDepth)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Refactor to common util
|
// TODO: Refactor to common util
|
||||||
|
@@ -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;
|
package com.twelvemonkeys.imageio.plugins.tga;
|
||||||
|
|
||||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
import com.twelvemonkeys.imageio.StandardImageMetadataSupport;
|
||||||
|
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.image.IndexColorModel;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
final class TGAMetadata extends StandardImageMetadataSupport {
|
||||||
|
TGAMetadata(ImageTypeSpecifier type, TGAHeader header, TGAExtensions extensions) {
|
||||||
final class TGAMetadata extends AbstractMetadata {
|
super(builder(type)
|
||||||
private final TGAHeader header;
|
.withCompressionName(compressionName(header))
|
||||||
private final TGAExtensions extensions;
|
.withPixelAspectRatio(pixelAspectRatio(extensions))
|
||||||
|
.withOrientation(orientation(header))
|
||||||
TGAMetadata(final TGAHeader header, final TGAExtensions extensions) {
|
.withFormatVersion(extensions == null ? "1.0" : "2.0")
|
||||||
this.header = notNull(header, "header");
|
.withDocumentCreationTime(documentCreationTime(extensions))
|
||||||
this.extensions = extensions;
|
.withTextEntries(textEntries(header, extensions))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static String compressionName(TGAHeader header) {
|
||||||
protected IIOMetadataNode getStandardChromaNode() {
|
|
||||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
|
||||||
|
|
||||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
|
||||||
chroma.appendChild(csType);
|
|
||||||
|
|
||||||
switch (header.getImageType()) {
|
switch (header.getImageType()) {
|
||||||
case TGA.IMAGETYPE_MONOCHROME:
|
case TGA.IMAGETYPE_NONE:
|
||||||
case TGA.IMAGETYPE_MONOCHROME_RLE:
|
case TGA.IMAGETYPE_COLORMAPPED:
|
||||||
csType.setAttribute("name", "GRAY");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TGA.IMAGETYPE_TRUECOLOR:
|
case TGA.IMAGETYPE_TRUECOLOR:
|
||||||
case TGA.IMAGETYPE_TRUECOLOR_RLE:
|
case TGA.IMAGETYPE_MONOCHROME:
|
||||||
case TGA.IMAGETYPE_COLORMAPPED:
|
return "None";
|
||||||
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_COLORMAPPED_RLE:
|
case TGA.IMAGETYPE_COLORMAPPED_RLE:
|
||||||
case TGA.IMAGETYPE_TRUECOLOR_RLE:
|
case TGA.IMAGETYPE_TRUECOLOR_RLE:
|
||||||
case TGA.IMAGETYPE_MONOCHROME_RLE:
|
case TGA.IMAGETYPE_MONOCHROME_RLE:
|
||||||
|
return "RLE";
|
||||||
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN:
|
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN:
|
||||||
case TGA.IMAGETYPE_COLORMAPPED_HUFFMAN_QUADTREE:
|
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:
|
default:
|
||||||
// No compression
|
return "Unknown";
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static double pixelAspectRatio(TGAExtensions extensions) {
|
||||||
protected IIOMetadataNode getStandardDataNode() {
|
return extensions != null ? extensions.getPixelAspectRatio() : 1f;
|
||||||
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 String createListValue(final int itemCount, final String... values) {
|
private static ImageOrientation orientation(TGAHeader header) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
switch (header.origin) {
|
||||||
|
|
||||||
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()) {
|
|
||||||
case TGA.ORIGIN_LOWER_LEFT:
|
case TGA.ORIGIN_LOWER_LEFT:
|
||||||
imageOrientation.setAttribute("value", "FlipH");
|
return ImageOrientation.FlipH;
|
||||||
break;
|
|
||||||
case TGA.ORIGIN_LOWER_RIGHT:
|
case TGA.ORIGIN_LOWER_RIGHT:
|
||||||
imageOrientation.setAttribute("value", "Rotate180");
|
return ImageOrientation.Rotate180;
|
||||||
break;
|
|
||||||
case TGA.ORIGIN_UPPER_LEFT:
|
case TGA.ORIGIN_UPPER_LEFT:
|
||||||
imageOrientation.setAttribute("value", "Normal");
|
return ImageOrientation.Normal;
|
||||||
break;
|
|
||||||
case TGA.ORIGIN_UPPER_RIGHT:
|
case TGA.ORIGIN_UPPER_RIGHT:
|
||||||
imageOrientation.setAttribute("value", "FlipV");
|
return ImageOrientation.FlipV;
|
||||||
break;
|
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
|
private static Calendar documentCreationTime(TGAExtensions extensions) {
|
||||||
protected IIOMetadataNode getStandardDocumentNode() {
|
return extensions != null ? extensions.creationDate : null;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static Map<String, String> textEntries(TGAHeader header, TGAExtensions extensions) {
|
||||||
protected IIOMetadataNode getStandardTextNode() {
|
LinkedHashMap<String, String> textEntries = new LinkedHashMap<>();
|
||||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
|
||||||
|
|
||||||
// NOTE: Names corresponds to equivalent fields in TIFF
|
// NOTE: Keywords follow TIFF standard naming
|
||||||
appendTextEntry(text, "DocumentName", header.getIdentification());
|
putIfValue(textEntries, "DocumentName", header.getIdentification());
|
||||||
|
|
||||||
if (extensions != null) {
|
if (extensions != null) {
|
||||||
appendTextEntry(text, "Software", extensions.getSoftwareVersion() == null ? extensions.getSoftware() : (extensions.getSoftware() + " " + extensions.getSoftwareVersion()));
|
putIfValue(textEntries, "Software", extensions.getSoftwareVersion() == null ? extensions.getSoftware() : (extensions.getSoftware() + " " + extensions.getSoftwareVersion()));
|
||||||
appendTextEntry(text, "Artist", extensions.getAuthorName());
|
putIfValue(textEntries, "Artist", extensions.getAuthorName());
|
||||||
appendTextEntry(text, "UserComment", extensions.getAuthorComments());
|
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<String, String> textEntries, final String keyword, final String value) {
|
||||||
if (value != null && !value.isEmpty()) {
|
if (value != null && !value.isEmpty()) {
|
||||||
IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
|
textEntries.put(keyword, value);
|
||||||
parent.appendChild(textEntry);
|
|
||||||
textEntry.setAttribute("keyword", keyword);
|
|
||||||
textEntry.setAttribute("value", 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -30,12 +30,17 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.tga;
|
package com.twelvemonkeys.imageio.plugins.tga;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.imageio.ImageTypeSpecifier;
|
||||||
import javax.imageio.ImageWriteParam;
|
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;
|
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$
|
* @version $Id: TGAImageWriteParamTest.java,v 1.0 08/04/2021 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class TGAImageWriteParamTest {
|
public class TGAImageWriteParamTest {
|
||||||
|
|
||||||
|
private static final ImageTypeSpecifier TYPE_3BYTE_BGR = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultCopyFromMetadata() {
|
public void testDefaultCopyFromMetadata() {
|
||||||
TGAImageWriteParam param = new TGAImageWriteParam();
|
TGAImageWriteParam param = new TGAImageWriteParam();
|
||||||
@@ -107,8 +115,8 @@ public class TGAImageWriteParamTest {
|
|||||||
ImageWriteParam param = new ImageWriteParam(null);
|
ImageWriteParam param = new ImageWriteParam(null);
|
||||||
assumeFalse(param.canWriteCompressed());
|
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(TYPE_3BYTE_BGR, TGAHeader.from(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, true), null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -116,7 +124,7 @@ public class TGAImageWriteParamTest {
|
|||||||
ImageWriteParam param = new TGAImageWriteParam();
|
ImageWriteParam param = new TGAImageWriteParam();
|
||||||
param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
|
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
|
@Test
|
||||||
@@ -124,7 +132,7 @@ public class TGAImageWriteParamTest {
|
|||||||
ImageWriteParam param = new TGAImageWriteParam();
|
ImageWriteParam param = new TGAImageWriteParam();
|
||||||
param.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);
|
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
|
@Test
|
||||||
@@ -132,12 +140,12 @@ public class TGAImageWriteParamTest {
|
|||||||
TGAImageWriteParam param = new TGAImageWriteParam();
|
TGAImageWriteParam param = new TGAImageWriteParam();
|
||||||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
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(TYPE_3BYTE_BGR, TGAHeader.from(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, true), null)));
|
||||||
|
|
||||||
param.setCompressionType("RLE");
|
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(TYPE_3BYTE_BGR, TGAHeader.from(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, true), null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -30,14 +30,18 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.imageio.plugins.tga;
|
package com.twelvemonkeys.imageio.plugins.tga;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.function.ThrowingRunnable;
|
import org.junit.function.ThrowingRunnable;
|
||||||
import org.w3c.dom.Node;
|
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.IIOMetadataFormatImpl;
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.*;
|
||||||
import java.awt.image.IndexColorModel;
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
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$
|
* @version $Id: TGAMetadataTest.java,v 1.0 08/04/2021 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class TGAMetadataTest {
|
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
|
@Test
|
||||||
public void testStandardFeatures() {
|
public void testStandardFeatures() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
|
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, false);
|
||||||
final TGAMetadata metadata = new TGAMetadata(header, null);
|
final TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||||
|
|
||||||
// Standard metadata format
|
// Standard metadata format
|
||||||
assertTrue(metadata.isStandardMetadataFormatSupported());
|
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||||
@@ -83,10 +92,10 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardChromaGray() {
|
public void testStandardChromaGray() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), false);
|
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, false);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
|
||||||
|
|
||||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||||
assertNotNull(chroma);
|
assertNotNull(chroma);
|
||||||
assertEquals("Chroma", chroma.getNodeName());
|
assertEquals("Chroma", chroma.getNodeName());
|
||||||
assertEquals(3, chroma.getLength());
|
assertEquals(3, chroma.getLength());
|
||||||
@@ -108,10 +117,10 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardChromaRGB() {
|
public void testStandardChromaRGB() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
|
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, false);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||||
|
|
||||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||||
assertNotNull(chroma);
|
assertNotNull(chroma);
|
||||||
assertEquals("Chroma", chroma.getNodeName());
|
assertEquals("Chroma", chroma.getNodeName());
|
||||||
assertEquals(3, chroma.getLength());
|
assertEquals(3, chroma.getLength());
|
||||||
@@ -135,10 +144,11 @@ public class TGAMetadataTest {
|
|||||||
public void testStandardChromaPalette() {
|
public void testStandardChromaPalette() {
|
||||||
byte[] bw = {0, (byte) 0xff};
|
byte[] bw = {0, (byte) 0xff};
|
||||||
IndexColorModel indexColorModel = new IndexColorModel(8, bw.length, bw, bw, bw, -1);
|
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);
|
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromIndexColorModel(indexColorModel);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAHeader header = TGAHeader.from(type, false);
|
||||||
|
TGAMetadata metadata = new TGAMetadata(type, header, null);
|
||||||
|
|
||||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
IIOMetadataNode chroma = getStandardNode(metadata, "Chroma");
|
||||||
assertNotNull(chroma);
|
assertNotNull(chroma);
|
||||||
assertEquals("Chroma", chroma.getNodeName());
|
assertEquals("Chroma", chroma.getNodeName());
|
||||||
assertEquals(4, chroma.getLength());
|
assertEquals(4, chroma.getLength());
|
||||||
@@ -174,10 +184,10 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardCompressionRLE() {
|
public void testStandardCompressionRLE() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
|
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, true);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||||
|
|
||||||
IIOMetadataNode compression = metadata.getStandardCompressionNode();
|
IIOMetadataNode compression = getStandardNode(metadata, "Compression");
|
||||||
assertNotNull(compression);
|
assertNotNull(compression);
|
||||||
assertEquals("Compression", compression.getNodeName());
|
assertEquals("Compression", compression.getNodeName());
|
||||||
assertEquals(2, compression.getLength());
|
assertEquals(2, compression.getLength());
|
||||||
@@ -195,18 +205,18 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardCompressionNone() {
|
public void testStandardCompressionNone() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), false);
|
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, false);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
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
|
@Test
|
||||||
public void testStandardDataGray() {
|
public void testStandardDataGray() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
|
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
|
||||||
|
|
||||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||||
assertNotNull(data);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -228,10 +238,10 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardDataRGB() {
|
public void testStandardDataRGB() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
|
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, true);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||||
|
|
||||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||||
assertNotNull(data);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -253,10 +263,11 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardDataRGBA() {
|
public void testStandardDataRGBA() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB), true);
|
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAHeader header = TGAHeader.from(type, true);
|
||||||
|
TGAMetadata metadata = new TGAMetadata(type, header, null);
|
||||||
|
|
||||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||||
assertNotNull(data);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -280,10 +291,11 @@ public class TGAMetadataTest {
|
|||||||
public void testStandardDataPalette() {
|
public void testStandardDataPalette() {
|
||||||
byte[] rgb = new byte[1 << 8]; // Colors doesn't really matter here
|
byte[] rgb = new byte[1 << 8]; // Colors doesn't really matter here
|
||||||
IndexColorModel indexColorModel = new IndexColorModel(8, rgb.length, rgb, rgb, rgb, 0);
|
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);
|
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromIndexColorModel(indexColorModel);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAHeader header = TGAHeader.from(type, true);
|
||||||
|
TGAMetadata metadata = new TGAMetadata(type, header, null);
|
||||||
|
|
||||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
IIOMetadataNode data = getStandardNode(metadata, "Data");
|
||||||
assertNotNull(data);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -305,53 +317,56 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardDimensionNormal() {
|
public void testStandardDimensionNormal() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
|
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
|
||||||
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);
|
assertNotNull(dimension);
|
||||||
assertEquals("Dimension", dimension.getNodeName());
|
assertEquals("Dimension", dimension.getNodeName());
|
||||||
assertEquals(2, dimension.getLength());
|
assertEquals(2, dimension.getLength());
|
||||||
|
|
||||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) dimension.getFirstChild();
|
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||||
assertEquals("ImageOrientation", imageOrientation.getNodeName());
|
|
||||||
assertEquals("Normal", imageOrientation.getAttribute("value"));
|
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) imageOrientation.getNextSibling();
|
|
||||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
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
|
@Test
|
||||||
public void testStandardDimensionFlipH() {
|
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;
|
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);
|
assertNotNull(dimension);
|
||||||
assertEquals("Dimension", dimension.getNodeName());
|
assertEquals("Dimension", dimension.getNodeName());
|
||||||
assertEquals(2, dimension.getLength());
|
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("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
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
|
@Test
|
||||||
public void testStandardDocument() {
|
public void testStandardDocument() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY), true);
|
TGAHeader header = TGAHeader.from(TYPE_BYTE_GRAY, true);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAMetadata metadata = new TGAMetadata(TYPE_BYTE_GRAY, header, null);
|
||||||
|
|
||||||
IIOMetadataNode document = metadata.getStandardDocumentNode();
|
IIOMetadataNode document = getStandardNode(metadata, "Document");
|
||||||
assertNotNull(document);
|
assertNotNull(document);
|
||||||
assertEquals("Document", document.getNodeName());
|
assertEquals("Document", document.getNodeName());
|
||||||
assertEquals(1, document.getLength());
|
assertEquals(1, document.getLength());
|
||||||
@@ -365,13 +380,13 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardDocumentExtensions() {
|
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();
|
TGAExtensions extensions = new TGAExtensions();
|
||||||
extensions.creationDate = Calendar.getInstance();
|
extensions.creationDate = Calendar.getInstance();
|
||||||
extensions.creationDate.set(2021, Calendar.APRIL, 8, 18, 55, 0);
|
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);
|
assertNotNull(document);
|
||||||
assertEquals("Document", document.getNodeName());
|
assertEquals("Document", document.getNodeName());
|
||||||
assertEquals(2, document.getLength());
|
assertEquals(2, document.getLength());
|
||||||
@@ -394,7 +409,7 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardText() {
|
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";
|
header.identification = "MY_FILE.TGA";
|
||||||
|
|
||||||
TGAExtensions extensions = new TGAExtensions();
|
TGAExtensions extensions = new TGAExtensions();
|
||||||
@@ -402,9 +417,9 @@ public class TGAMetadataTest {
|
|||||||
extensions.authorName = "Harald K";
|
extensions.authorName = "Harald K";
|
||||||
extensions.authorComments = "Comments, comments... ";
|
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);
|
assertNotNull(text);
|
||||||
assertEquals("Text", text.getNodeName());
|
assertEquals("Text", text.getNodeName());
|
||||||
assertEquals(4, text.getLength());
|
assertEquals(4, text.getLength());
|
||||||
@@ -432,10 +447,10 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardTransparencyRGB() {
|
public void testStandardTransparencyRGB() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR), true);
|
TGAHeader header = TGAHeader.from(TYPE_3BYTE_BGR, true);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAMetadata metadata = new TGAMetadata(TYPE_3BYTE_BGR, header, null);
|
||||||
|
|
||||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||||
assertNotNull(transparency);
|
assertNotNull(transparency);
|
||||||
assertEquals("Transparency", transparency.getNodeName());
|
assertEquals("Transparency", transparency.getNodeName());
|
||||||
assertEquals(1, transparency.getLength());
|
assertEquals(1, transparency.getLength());
|
||||||
@@ -449,10 +464,11 @@ public class TGAMetadataTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardTransparencyRGBA() {
|
public void testStandardTransparencyRGBA() {
|
||||||
TGAHeader header = TGAHeader.from(new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR), true);
|
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAHeader header = TGAHeader.from(type, true);
|
||||||
|
TGAMetadata metadata = new TGAMetadata(type, header, null);
|
||||||
|
|
||||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||||
assertNotNull(transparency);
|
assertNotNull(transparency);
|
||||||
assertEquals("Transparency", transparency.getNodeName());
|
assertEquals("Transparency", transparency.getNodeName());
|
||||||
assertEquals(1, transparency.getLength());
|
assertEquals(1, transparency.getLength());
|
||||||
@@ -468,19 +484,30 @@ public class TGAMetadataTest {
|
|||||||
public void testStandardTransparencyPalette() {
|
public void testStandardTransparencyPalette() {
|
||||||
byte[] bw = {0, (byte) 0xff};
|
byte[] bw = {0, (byte) 0xff};
|
||||||
IndexColorModel indexColorModel = new IndexColorModel(8, bw.length, bw, bw, bw, 1);
|
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);
|
ImageTypeSpecifier type = ImageTypeSpecifiers.createFromIndexColorModel(indexColorModel);
|
||||||
TGAMetadata metadata = new TGAMetadata(header, null);
|
TGAHeader header = TGAHeader.from(type, true);
|
||||||
|
TGAMetadata metadata = new TGAMetadata(type, header, null);
|
||||||
|
|
||||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||||
assertNotNull(transparency);
|
assertNotNull(transparency);
|
||||||
assertEquals("Transparency", transparency.getNodeName());
|
assertEquals("Transparency", transparency.getNodeName());
|
||||||
assertEquals(1, transparency.getLength());
|
assertEquals(2, transparency.getLength());
|
||||||
|
|
||||||
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
||||||
assertEquals("Alpha", alpha.getNodeName());
|
assertEquals("Alpha", alpha.getNodeName());
|
||||||
assertEquals("nonpremultiplied", alpha.getAttribute("value"));
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -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;
|
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;
|
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||||
|
|
||||||
/**
|
final class WebPImageMetadata extends StandardImageMetadataSupport {
|
||||||
* WebPMetadata
|
WebPImageMetadata(ImageTypeSpecifier type, VP8xChunk header) {
|
||||||
*/
|
super(builder(type)
|
||||||
final class WebPImageMetadata extends AbstractMetadata {
|
.withCompressionName(notNull(header, "header").isLossless ? "VP8L" : "VP8")
|
||||||
private final VP8xChunk header;
|
.withCompressionLossless(header.isLossless)
|
||||||
|
.withPixelAspectRatio(1.0)
|
||||||
WebPImageMetadata(final VP8xChunk header) {
|
.withFormatVersion("1.0")
|
||||||
this.header = notNull(header, "header");
|
// 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)
|
|
||||||
}
|
}
|
||||||
|
@@ -661,10 +661,7 @@ final class WebPImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||||
readHeader(imageIndex);
|
return new WebPImageMetadata(getRawImageType(imageIndex), header);
|
||||||
readMeta();
|
|
||||||
|
|
||||||
return new WebPImageMetadata(header);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readMeta() throws IOException {
|
private void readMeta() throws IOException {
|
||||||
|
@@ -1,11 +1,17 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.webp;
|
package com.twelvemonkeys.imageio.plugins.webp;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.function.ThrowingRunnable;
|
import org.junit.function.ThrowingRunnable;
|
||||||
import org.w3c.dom.Node;
|
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.IIOMetadataFormatImpl;
|
||||||
import javax.imageio.metadata.IIOMetadataNode;
|
import javax.imageio.metadata.IIOMetadataNode;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
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$
|
* @version $Id: WebPImageMetadataTest.java,v 1.0 21/11/2020 haraldk Exp$
|
||||||
*/
|
*/
|
||||||
public class WebPImageMetadataTest {
|
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
|
@Test
|
||||||
public void testStandardFeatures() {
|
public void testStandardFeatures() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
|
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
|
// Standard metadata format
|
||||||
assertTrue(metadata.isStandardMetadataFormatSupported());
|
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||||
@@ -51,9 +61,9 @@ public class WebPImageMetadataTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testStandardChromaRGB() {
|
public void testStandardChromaRGB() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
|
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);
|
assertNotNull(chroma);
|
||||||
assertEquals("Chroma", chroma.getNodeName());
|
assertEquals("Chroma", chroma.getNodeName());
|
||||||
assertEquals(3, chroma.getLength());
|
assertEquals(3, chroma.getLength());
|
||||||
@@ -77,9 +87,9 @@ public class WebPImageMetadataTest {
|
|||||||
public void testStandardChromaRGBA() {
|
public void testStandardChromaRGBA() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||||
header.containsALPH = true;
|
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);
|
assertNotNull(chroma);
|
||||||
assertEquals("Chroma", chroma.getNodeName());
|
assertEquals("Chroma", chroma.getNodeName());
|
||||||
assertEquals(3, chroma.getLength());
|
assertEquals(3, chroma.getLength());
|
||||||
@@ -103,9 +113,9 @@ public class WebPImageMetadataTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testStandardCompressionVP8() {
|
public void testStandardCompressionVP8() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
|
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);
|
assertNotNull(compression);
|
||||||
assertEquals("Compression", compression.getNodeName());
|
assertEquals("Compression", compression.getNodeName());
|
||||||
assertEquals(2, compression.getLength());
|
assertEquals(2, compression.getLength());
|
||||||
@@ -125,9 +135,9 @@ public class WebPImageMetadataTest {
|
|||||||
public void testStandardCompressionVP8L() {
|
public void testStandardCompressionVP8L() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8L, 27, 33);
|
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8L, 27, 33);
|
||||||
header.isLossless = true;
|
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);
|
assertNotNull(compression);
|
||||||
assertEquals("Compression", compression.getNodeName());
|
assertEquals("Compression", compression.getNodeName());
|
||||||
assertEquals(2, compression.getLength());
|
assertEquals(2, compression.getLength());
|
||||||
@@ -146,9 +156,9 @@ public class WebPImageMetadataTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testStandardCompressionVP8X() {
|
public void testStandardCompressionVP8X() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
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);
|
assertNotNull(compression);
|
||||||
assertEquals("Compression", compression.getNodeName());
|
assertEquals("Compression", compression.getNodeName());
|
||||||
assertEquals(2, compression.getLength());
|
assertEquals(2, compression.getLength());
|
||||||
@@ -168,9 +178,9 @@ public class WebPImageMetadataTest {
|
|||||||
public void testStandardCompressionVP8XLossless() {
|
public void testStandardCompressionVP8XLossless() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||||
header.isLossless = true;
|
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);
|
assertNotNull(compression);
|
||||||
assertEquals("Compression", compression.getNodeName());
|
assertEquals("Compression", compression.getNodeName());
|
||||||
assertEquals(2, compression.getLength());
|
assertEquals(2, compression.getLength());
|
||||||
@@ -189,9 +199,9 @@ public class WebPImageMetadataTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testStandardDataRGB() {
|
public void testStandardDataRGB() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
|
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);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -215,9 +225,9 @@ public class WebPImageMetadataTest {
|
|||||||
public void testStandardDataRGBA() {
|
public void testStandardDataRGBA() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||||
header.containsALPH = true;
|
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);
|
assertNotNull(data);
|
||||||
assertEquals("Data", data.getNodeName());
|
assertEquals("Data", data.getNodeName());
|
||||||
assertEquals(3, data.getLength());
|
assertEquals(3, data.getLength());
|
||||||
@@ -240,78 +250,109 @@ public class WebPImageMetadataTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testStandardDimensionNormal() {
|
public void testStandardDimensionNormal() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
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);
|
assertNotNull(dimension);
|
||||||
assertEquals("Dimension", dimension.getNodeName());
|
assertEquals("Dimension", dimension.getNodeName());
|
||||||
assertEquals(2, dimension.getLength());
|
assertEquals(2, dimension.getLength());
|
||||||
|
|
||||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) dimension.getFirstChild();
|
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) dimension.getFirstChild();
|
||||||
assertEquals("ImageOrientation", imageOrientation.getNodeName());
|
|
||||||
assertEquals("Normal", imageOrientation.getAttribute("value"));
|
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) imageOrientation.getNextSibling();
|
|
||||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
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
|
@Test
|
||||||
public void testStandardDocument() {
|
public void testStandardDocument() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
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);
|
assertNotNull(document);
|
||||||
assertEquals("Document", document.getNodeName());
|
assertEquals("Document", document.getNodeName());
|
||||||
assertEquals(1, document.getLength());
|
assertEquals(1, document.getLength());
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) document.getFirstChild();
|
IIOMetadataNode formatVersion = (IIOMetadataNode) document.getFirstChild();
|
||||||
assertEquals("FormatVersion", pixelAspectRatio.getNodeName());
|
assertEquals("FormatVersion", formatVersion.getNodeName());
|
||||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
assertEquals("1.0", formatVersion.getAttribute("value"));
|
||||||
|
|
||||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
assertNull(formatVersion.getNextSibling()); // No more children
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardText() {
|
public void testStandardText() {
|
||||||
|
// No text node yet...
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStandardTransparencyVP8() {
|
public void testStandardTransparencyVP8() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
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();
|
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||||
assertNull(transparency); // No transparency, just defaults
|
|
||||||
|
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
|
@Test
|
||||||
public void testStandardTransparencyVP8L() {
|
public void testStandardTransparencyVP8L() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
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();
|
IIOMetadataNode transparency = getStandardNode(metadata, "Transparency");
|
||||||
assertNull(transparency); // No transparency, just defaults
|
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
|
@Test
|
||||||
public void testStandardTransparencyVP8X() {
|
public void testStandardTransparencyVP8X() {
|
||||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||||
header.containsALPH = true;
|
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);
|
assertNotNull(transparency);
|
||||||
assertEquals("Transparency", transparency.getNodeName());
|
assertEquals("Transparency", transparency.getNodeName());
|
||||||
assertEquals(1, transparency.getLength());
|
assertEquals(1, transparency.getLength());
|
||||||
|
|
||||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
|
IIOMetadataNode alpha = (IIOMetadataNode) transparency.getFirstChild();
|
||||||
assertEquals("Alpha", pixelAspectRatio.getNodeName());
|
assertEquals("Alpha", alpha.getNodeName());
|
||||||
assertEquals("nonpremultiplied", pixelAspectRatio.getAttribute("value"));
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,139 +1,16 @@
|
|||||||
package com.twelvemonkeys.imageio.plugins.xwd;
|
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 java.nio.ByteOrder;
|
||||||
|
|
||||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
final class XWDImageMetadata extends StandardImageMetadataSupport {
|
||||||
|
XWDImageMetadata(ImageTypeSpecifier type, XWDX11Header header) {
|
||||||
final class XWDImageMetadata extends AbstractMetadata {
|
super(builder(type)
|
||||||
private final XWDX11Header header;
|
.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
|
||||||
XWDImageMetadata(XWDX11Header header) {
|
.withTextEntry("DocumentName", header.windowName) // For TIFF interop :-)
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import javax.imageio.ImageTypeSpecifier;
|
|||||||
import javax.imageio.metadata.IIOMetadata;
|
import javax.imageio.metadata.IIOMetadata;
|
||||||
import javax.imageio.spi.ImageReaderSpi;
|
import javax.imageio.spi.ImageReaderSpi;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.color.ColorSpace;
|
import java.awt.color.*;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
@@ -48,10 +48,7 @@ final class XWDImageReader extends ImageReaderBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||||
checkBounds(imageIndex);
|
return new XWDImageMetadata(getRawImageType(imageIndex), header);
|
||||||
readHeader();
|
|
||||||
|
|
||||||
return new XWDImageMetadata(header);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Reference in New Issue
Block a user