TMI-SGI: Initial commit.

This commit is contained in:
Harald Kuhr
2014-10-01 14:26:22 +02:00
parent 6967081aa1
commit 74637105b8
12 changed files with 991 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
package com.twelvemonkeys.imageio.plugins.sgi;
import com.twelvemonkeys.io.enc.Decoder;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
final class RLEDecoder implements Decoder {
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
// Adapted from c code sample in tgaffs.pdf
while (buffer.remaining() >= 0x7f) {
int val = stream.read();
if (val < 0) {
break; // EOF
}
int count = val & 0x7f;
if (count == 0) {
break; // No more data
}
if ((val & 0x80) != 0) {
for (int i = 0; i < count; i++) {
int pixel = stream.read();
if (pixel < 0) {
break; // EOF
}
buffer.put((byte) pixel);
}
}
else {
int pixel = stream.read();
if (pixel < 0) {
break; // EOF
}
for (int i = 0; i < count; i++) {
buffer.put((byte) pixel);
}
}
}
return buffer.position();
}
}

View File

@@ -0,0 +1,20 @@
package com.twelvemonkeys.imageio.plugins.sgi;
interface SGI {
short MAGIC = 474; // 0x1da
/** No compression, channels stored verbatim. */
byte COMPRESSION_NONE = 0;
/** Runlength encoed compression,
* channels are prepended by one offset and length tables (one entry in each per scanline). */
byte COMPRESSION_RLE = 1;
/** Only ColorMode NORMAL should be used. */
int COLORMODE_NORMAL = 0;
/** Obsolete. */
int COLORMODE_DITHERED = 1;
/** Obsolete. */
int COLORMODE_SCREEN = 2;
/** Obsolete. */
int COLORMODE_COLORMAP = 3;
}

View File

@@ -0,0 +1,131 @@
package com.twelvemonkeys.imageio.plugins.sgi;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
final class SGIHeader {
private int compression;
private int bytesPerPixel;
private int dimensions;
private int width;
private int height;
private int channels;
private int minValue;
private int maxValue;
private String name;
private int colorMode;
public int getCompression() {
return compression;
}
public int getBytesPerPixel() {
return bytesPerPixel;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getChannels() {
return channels;
}
public int getMinValue() {
return minValue;
}
public int getMaxValue() {
return maxValue;
}
public String getName() {
return name;
}
public int getColorMode() {
return colorMode;
}
@Override public String toString() {
return "SGIHeader{" +
"compression=" + compression +
", bytesPerPixel=" + bytesPerPixel +
", dimensions=" + dimensions +
", width=" + width +
", height=" + height +
", channels=" + channels +
", minValue=" + minValue +
", maxValue=" + maxValue +
", name='" + name + '\'' +
", colorMode=" + colorMode +
'}';
}
public static SGIHeader read(final ImageInputStream imageInput) throws IOException {
// typedef struct _SGIHeader
// {
// SHORT Magic; /* Identification number (474) */
// CHAR Storage; /* Compression flag */
// CHAR Bpc; /* Bytes per pixel */
// WORD Dimension; /* Number of image dimensions */
// WORD XSize; /* Width of image in pixels */
// WORD YSize; /* Height of image in pixels */
// WORD ZSize; /* Number of bit channels */
// LONG PixMin; /* Smallest pixel value */
// LONG PixMax; /* Largest pixel value */
// CHAR Dummy1[4]; /* Not used */
// CHAR ImageName[80]; /* Name of image */
// LONG ColorMap; /* Format of pixel data */
// CHAR Dummy2[404]; /* Not used */
// } SGIHEAD;
short magic = imageInput.readShort();
if (magic != SGI.MAGIC) {
throw new IIOException(String.format("Not an SGI image. Expected SGI magic %04x, read %04x", SGI.MAGIC, magic));
}
SGIHeader header = new SGIHeader();
header.compression = imageInput.readUnsignedByte();
header.bytesPerPixel = imageInput.readUnsignedByte();
header.dimensions = imageInput.readUnsignedShort();
header.width = imageInput.readUnsignedShort();
header.height = imageInput.readUnsignedShort();
header.channels = imageInput.readUnsignedShort();
header.minValue = imageInput.readInt();
header.maxValue = imageInput.readInt();
imageInput.readInt(); // Ignore
byte[] nameBytes = new byte[80];
imageInput.readFully(nameBytes);
header.name = toAsciiString(nameBytes);
header.colorMode = imageInput.readInt();
imageInput.skipBytes(404);
return header;
}
private static String toAsciiString(final byte[] bytes) {
// Find null-terminator
int len = bytes.length;
for (int i = 0; i < bytes.length; i++) {
if (bytes[i] == 0) {
len = i;
break;
}
}
return new String(bytes, 0, len, Charset.forName("ASCII"));
}
}

View File

@@ -0,0 +1,369 @@
package com.twelvemonkeys.imageio.plugins.sgi;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.xml.XMLSerializer;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public final class SGIImageReader extends ImageReaderBase {
private SGIHeader header;
protected SGIImageReader(final ImageReaderSpi provider) {
super(provider);
}
@Override
protected void resetMembers() {
header = null;
}
@Override
public int getWidth(final int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
return header.getWidth();
}
@Override
public int getHeight(final int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
return header.getHeight();
}
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(final int imageIndex) throws IOException {
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
List<ImageTypeSpecifier> specifiers = new ArrayList<ImageTypeSpecifier>();
// TODO: Implement
specifiers.add(rawType);
return specifiers.iterator();
}
@Override
public ImageTypeSpecifier getRawImageType(final int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
// 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...)
int channels = header.getChannels();
ColorSpace cs = channels < 3 ? ColorSpace.getInstance(ColorSpace.CS_GRAY) : ColorSpace.getInstance(ColorSpace.CS_sRGB);
switch (header.getBytesPerPixel()) {
case 1:
return ImageTypeSpecifier.createBanded(cs, createIndices(channels, 1), createIndices(channels, 0), DataBuffer.TYPE_BYTE, channels == 2 || channels == 4, false);
case 2:
return ImageTypeSpecifier.createBanded(cs, createIndices(channels, 1), createIndices(channels, 0), DataBuffer.TYPE_USHORT, channels == 2 || channels == 4, false);
default:
throw new IIOException("Unknown number of bytes per pixel: " + header.getBytesPerPixel());
}
}
private int[] createIndices(final int bands, int increment) {
int[] indices = new int[bands];
for (int i = 0; i < bands; i++) {
indices[i] = i * increment;
}
return indices;
}
@Override
public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex);
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
if (header.getColorMode() != SGI.COLORMODE_NORMAL) {
processWarningOccurred(String.format("Unsupported color mode: %d, colors may look incorrect", header.getColorMode()));
}
int width = getWidth(imageIndex);
int height = getHeight(imageIndex);
BufferedImage destination = getDestination(param, imageTypes, width, height);
Rectangle srcRegion = new Rectangle();
Rectangle destRegion = new Rectangle();
computeRegions(param, width, height, destination, srcRegion, destRegion);
WritableRaster destRaster = clipToRect(destination.getRaster(), destRegion, param != null ? param.getDestinationBands() : null);
checkReadParamBandSettings(param, rawType.getNumBands(), destRaster.getNumBands());
WritableRaster rowRaster = rawType.createBufferedImage(width, 1).getRaster();
// Clip to source region
Raster clippedRow = clipRowToRect(rowRaster, srcRegion,
param != null ? param.getSourceBands() : null,
param != null ? param.getSourceXSubsampling() : 1);
int[] scanlineOffsets;
int[] scanlineLengths;
int compression = header.getCompression();
if (compression == SGI.COMPRESSION_RLE) {
scanlineOffsets = new int[height * header.getChannels()];
scanlineLengths = new int[height * header.getChannels()];
imageInput.readFully(scanlineOffsets, 0, scanlineOffsets.length);
imageInput.readFully(scanlineLengths, 0, scanlineLengths.length);
}
else {
scanlineOffsets = null;
scanlineLengths = null;
}
int xSub = param != null ? param.getSourceXSubsampling() : 1;
int ySub = param != null ? param.getSourceYSubsampling() : 1;
processImageStarted(imageIndex);
for (int c = 0; c < header.getChannels(); c++) {
WritableRaster destChannel = destRaster.createWritableChild(destRaster.getMinX(), destRaster.getMinY(), destRaster.getWidth(), destRaster.getHeight(), 0, 0, new int[] {c});
Raster srcChannel = clippedRow.createChild(clippedRow.getMinX(), 0, clippedRow.getWidth(), 1, 0, 0, new int[] {c});
// NOTE: SGI images are store bottom/up, thus y value is opposite of destination y
for (int y = 0; y < height; y++) {
switch (header.getBytesPerPixel()) {
case 1:
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData(c);
readRowByte(height, srcRegion, scanlineOffsets, scanlineLengths, compression, xSub, ySub, c, rowDataByte, destChannel, srcChannel, y);
break;
case 2:
short[] rowDataUShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData(c);
readRowUShort(height, srcRegion, scanlineOffsets, scanlineLengths, compression, xSub, ySub, c, rowDataUShort, destChannel, srcChannel, y);
break;
default:
throw new AssertionError();
}
processImageProgress(100f * y / height * c / header.getChannels());
if (height - 1 - y < srcRegion.y) {
break;
}
if (abortRequested()) {
break;
}
}
if (abortRequested()) {
processReadAborted();
break;
}
}
processImageComplete();
return destination;
}
private void readRowByte(int height, Rectangle srcRegion, int[] scanlineOffsets, int[] scanlineLengths, int compression, int xSub, int ySub, int c, byte[] rowDataByte, WritableRaster destChannel, Raster srcChannel, int y) throws IOException {
// If subsampled or outside source region, skip entire row
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
if (compression == SGI.COMPRESSION_NONE) {
imageInput.skipBytes(rowDataByte.length);
}
return;
}
// Wrap input
DataInput input;
if (compression == SGI.COMPRESSION_RLE) {
int scanLineIndex = c * height + y;
imageInput.seek(scanlineOffsets[scanLineIndex]);
input = new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(imageInput, scanlineLengths[scanLineIndex]), new RLEDecoder()));
} else {
input = imageInput;
}
input.readFully(rowDataByte, 0, rowDataByte.length);
// Subsample horizontal
if (xSub != 1) {
for (int x = 0; x < srcRegion.width / xSub; x++) {
rowDataByte[srcRegion.x + x] = rowDataByte[srcRegion.x + x * xSub];
}
}
normalize(rowDataByte, 9, srcRegion.width / xSub);
// Flip into position (SGI images are stored bottom/up)
int dstY = (height - 1 - y - srcRegion.y) / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
}
private void readRowUShort(int height, Rectangle srcRegion, int[] scanlineOffsets, int[] scanlineLengths, int compression, int xSub, int ySub, int c, short[] rowDataUShort, WritableRaster destChannel, Raster srcChannel, int y) throws IOException {
// If subsampled or outside source region, skip entire row
if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
if (compression == SGI.COMPRESSION_NONE) {
imageInput.skipBytes(rowDataUShort.length * 2);
}
return;
}
// Wrap input
DataInput input;
if (compression == SGI.COMPRESSION_RLE) {
int scanLineIndex = c * height + y;
imageInput.seek(scanlineOffsets[scanLineIndex]);
input = new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(imageInput, scanlineLengths[scanLineIndex]), new RLEDecoder()));
} else {
input = imageInput;
}
readFully(input, rowDataUShort);
// Subsample horizontal
if (xSub != 1) {
for (int x = 0; x < srcRegion.width / xSub; x++) {
rowDataUShort[srcRegion.x + x] = rowDataUShort[srcRegion.x + x * xSub];
}
}
normalize(rowDataUShort, 9, srcRegion.width / xSub);
// Flip into position (SGI images are stored bottom/up)
int dstY = (height - 1 - y - srcRegion.y) / ySub;
destChannel.setDataElements(0, dstY, srcChannel);
}
// TODO: Candidate util method
private static void readFully(final DataInput input, final short[] shorts) throws IOException {
if (input instanceof ImageInputStream) {
// Optimization for ImageInputStreams, read all in one go
((ImageInputStream) input).readFully(shorts, 0, shorts.length);
}
else {
for (int i = 0; i < shorts.length; i++) {
shorts[i] = input.readShort();
}
}
}
private void normalize(final byte[] rowData, final int start, final int length) {
int minValue = header.getMinValue();
int maxValue = header.getMaxValue();
if (minValue != 0 && maxValue != 0xff) {
// Normalize
for (int i = start; i < length; i++) {
rowData[i] = (byte) (((rowData[i] - minValue) * 0xff) / maxValue);
}
}
}
private void normalize(final short[] rowData, final int start, final int length) {
int minValue = header.getMinValue();
int maxValue = header.getMaxValue();
if (minValue != 0 && maxValue != 0xff) {
// Normalize
for (int i = start; i < length; i++) {
rowData[i] = (byte) (((rowData[i] - minValue) * 0xff) / maxValue);
}
}
}
private Raster clipRowToRect(final Raster raster, final Rectangle rect, final int[] bands, final int xSub) {
if (rect.contains(raster.getMinX(), 0, raster.getWidth(), 1)
&& xSub == 1
&& bands == null /* TODO: Compare bands with that of raster */) {
return raster;
}
return raster.createChild(rect.x / xSub, 0, rect.width / xSub, 1, 0, 0, bands);
}
private WritableRaster clipToRect(final WritableRaster raster, final Rectangle rect, final int[] bands) {
if (rect.contains(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight())
&& bands == null /* TODO: Compare bands with that of raster */) {
return raster;
}
return raster.createWritableChild(rect.x, rect.y, rect.width, rect.height, 0, 0, bands);
}
private void readHeader() throws IOException {
if (header == null) {
header = SGIHeader.read(imageInput);
// System.err.println("header: " + header);
imageInput.flushBefore(imageInput.getStreamPosition());
}
imageInput.seek(imageInput.getFlushedPosition());
}
@Override public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
return new SGIMetadata(header);
}
public static void main(String[] args) throws IOException {
SGIImageReader reader = new SGIImageReader(null);
for (String arg : args) {
File in = new File(arg);
reader.setInput(ImageIO.createImageInputStream(in));
ImageReadParam param = reader.getDefaultReadParam();
param.setDestinationType(reader.getImageTypes(0).next());
// param.setSourceSubsampling(2, 3, 0, 0);
// param.setSourceSubsampling(2, 1, 0, 0);
//
// int width = reader.getWidth(0);
// int height = reader.getHeight(0);
//
// param.setSourceRegion(new Rectangle(width / 4, height / 4, width / 2, height / 2));
// param.setSourceRegion(new Rectangle(width / 2, height / 2));
// param.setSourceRegion(new Rectangle(width / 2, height / 2, width / 2, height / 2));
BufferedImage image = reader.read(0, param);
System.err.println("image: " + image);
showIt(image, in.getName());
new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(reader.getImageMetadata(0).getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName), false);
// File reference = new File(in.getParent() + "/../reference", in.getName().replaceAll("\\.p(a|b|g|p)m", ".png"));
// if (reference.exists()) {
// System.err.println("reference.getAbsolutePath(): " + reference.getAbsolutePath());
// showIt(ImageIO.read(reference), reference.getName());
// }
// break;
}
}
}

View File

@@ -0,0 +1,82 @@
package com.twelvemonkeys.imageio.plugins.sgi;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
public final class SGIImageReaderSpi extends ImageReaderSpi {
/**
* Creates a {@code SGIImageReaderSpi}.
*/
public SGIImageReaderSpi() {
this(IIOUtil.getProviderInfo(SGIImageReaderSpi.class));
}
private SGIImageReaderSpi(final ProviderInfo providerInfo) {
super(
providerInfo.getVendorName(),
providerInfo.getVersion(),
new String[]{
"sgi",
"SGI"
},
new String[]{"sgi"},
new String[]{
// No official IANA record exists
"image/sgi",
"image/x-sgi",
},
"com.twelvemkonkeys.imageio.plugins.sgi.SGIImageReader",
new Class[] {ImageInputStream.class},
null,
true, // supports standard stream metadata
null, null, // native stream format name and class
null, null, // extra stream formats
true, // supports standard image metadata
null, null,
null, null // extra image metadata formats
);
}
@Override public boolean canDecodeInput(final Object source) throws IOException {
if (!(source instanceof ImageInputStream)) {
return false;
}
ImageInputStream stream = (ImageInputStream) source;
stream.mark();
try {
short magic = stream.readShort();
switch (magic) {
case SGI.MAGIC:
byte compression = stream.readByte();
byte bpp = stream.readByte();
return (compression == SGI.COMPRESSION_NONE || compression == SGI.COMPRESSION_RLE) && (bpp == 1 || bpp == 2);
default:
return false;
}
}
finally {
stream.reset();
}
}
@Override public ImageReader createReaderInstance(final Object extension) throws IOException {
return new SGIImageReader(this);
}
@Override public String getDescription(final Locale locale) {
return "Silicon Graphics (SGI) image reader";
}
}

View File

@@ -0,0 +1,199 @@
package com.twelvemonkeys.imageio.plugins.sgi;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode;
import org.w3c.dom.Node;
final class SGIMetadata extends IIOMetadata {
// TODO: Clean up & extend AbstractMetadata (after moving from PSD -> Core)
private final SGIHeader header;
SGIMetadata(final SGIHeader header) {
this.header = header;
standardFormatSupported = true;
}
@Override public boolean isReadOnly() {
return true;
}
@Override public Node getAsTree(final String formatName) {
if (formatName.equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
return getStandardTree();
}
else {
throw new IllegalArgumentException("Unsupported metadata format: " + formatName);
}
}
@Override public void mergeTree(final String formatName, final Node root) {
if (isReadOnly()) {
throw new IllegalStateException("Metadata is read-only");
}
}
@Override public void reset() {
if (isReadOnly()) {
throw new IllegalStateException("Metadata is read-only");
}
}
@Override protected IIOMetadataNode getStandardChromaNode() {
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
// NOTE: There doesn't seem to be any god way to determine color space, other than by convention
// 1 channel: Gray, 2 channel: Gray + Alpha, 3 channel: RGB, 4 channel: RGBA (hopefully never CMYK...)
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
switch (header.getColorMode()) {
case SGI.COLORMODE_NORMAL:
switch (header.getChannels()) {
case 1:
case 2:
csType.setAttribute("name", "GRAY");
break;
case 3:
case 4:
csType.setAttribute("name", "RGB");
break;
default:
csType.setAttribute("name", Integer.toHexString(header.getChannels()).toUpperCase() + "CLR");
break;
}
break;
// SGIIMAGE.TXT describes these as RGB
case SGI.COLORMODE_DITHERED:
case SGI.COLORMODE_SCREEN:
case SGI.COLORMODE_COLORMAP:
csType.setAttribute("name", "RGB");
break;
}
if (csType.getAttribute("name") != null) {
chroma.appendChild(csType);
}
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
numChannels.setAttribute("value", Integer.toString(header.getChannels()));
chroma.appendChild(numChannels);
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
blackIsZero.setAttribute("value", "TRUE");
chroma.appendChild(blackIsZero);
return chroma;
}
// No compression
@Override protected IIOMetadataNode getStandardCompressionNode() {
if (header.getCompression() != SGI.COMPRESSION_NONE) {
IIOMetadataNode node = new IIOMetadataNode("Compression");
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
compressionTypeName.setAttribute("value", header.getCompression() == SGI.COMPRESSION_RLE ? "RLE" : "Uknown");
node.appendChild(compressionTypeName);
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
lossless.setAttribute("value", "TRUE");
node.appendChild(lossless);
return node;
}
return null;
}
@Override protected IIOMetadataNode getStandardDataNode() {
IIOMetadataNode node = new IIOMetadataNode("Data");
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
sampleFormat.setAttribute("value", "UnsignedIntegral");
node.appendChild(sampleFormat);
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
bitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(header.getBytesPerPixel() * 8)));
node.appendChild(bitsPerSample);
IIOMetadataNode significantBitsPerSample = new IIOMetadataNode("SignificantBitsPerSample");
significantBitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(computeSignificantBits())));
node.appendChild(significantBitsPerSample);
IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
sampleMSB.setAttribute("value", createListValue(header.getChannels(), "0"));
return node;
}
private int computeSignificantBits() {
int significantBits = 0;
int maxSample = header.getMaxValue();
while (maxSample > 0) {
maxSample >>>= 1;
significantBits++;
}
return significantBits;
}
private String createListValue(final int itemCount, final String... values) {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < itemCount; i++) {
if (buffer.length() > 0) {
buffer.append(' ');
}
buffer.append(values[i % values.length]);
}
return buffer.toString();
}
@Override protected IIOMetadataNode getStandardDimensionNode() {
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
imageOrientation.setAttribute("value", "FlipV");
dimension.appendChild(imageOrientation);
return dimension;
}
// No document node
@Override protected IIOMetadataNode getStandardTextNode() {
if (!header.getName().isEmpty()) {
IIOMetadataNode text = new IIOMetadataNode("Text");
IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
textEntry.setAttribute("keyword", "name");
textEntry.setAttribute("value", header.getName());
text.appendChild(textEntry);
return text;
}
return null;
}
// No tiling
@Override protected IIOMetadataNode getStandardTransparencyNode() {
// NOTE: There doesn't seem to be any god way to determine transparency, other than by convention
// 1 channel: Gray, 2 channel: Gray + Alpha, 3 channel: RGB, 4 channel: RGBA (hopefully never CMYK...)
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
alpha.setAttribute("value", header.getChannels() == 1 || header.getChannels() == 3 ? "none" : "nonpremultiplied");
transparency.appendChild(alpha);
return transparency;
}
}

View File

@@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.sgi.SGIImageReaderSpi

View File

@@ -0,0 +1,86 @@
/*
* 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 "TwelveMonkeys" 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 OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.sgi;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
/**
* SGIImageReaderTest
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: SGIImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
*/
public class SGIImageReaderTest extends ImageReaderAbstractTestCase<SGIImageReader> {
@Override
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/sgi/MARBLES.SGI"), new Dimension(1419, 1001)) // RLE encoded RGB
);
}
@Override
protected ImageReaderSpi createProvider() {
return new SGIImageReaderSpi();
}
@Override
protected Class<SGIImageReader> getReaderClass() {
return SGIImageReader.class;
}
@Override
protected SGIImageReader createReader() {
return new SGIImageReader(createProvider());
}
@Override
protected List<String> getFormatNames() {
return Arrays.asList("SGI", "sgi");
}
@Override
protected List<String> getSuffixes() {
return Arrays.asList(
"sgi"
);
}
@Override
protected List<String> getMIMETypes() {
return Arrays.asList(
"image/sgi", "image/x-sgi"
);
}
}

Binary file not shown.