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

25
imageio/imageio-sgi/license.txt Executable file
View File

@ -0,0 +1,25 @@
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.

28
imageio/imageio-sgi/pom.xml Executable file
View File

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.1-SNAPSHOT</version>
</parent>
<artifactId>imageio-sgi</artifactId>
<name>TwelveMonkeys :: ImageIO :: SGI plugin</name>
<description>
ImageIO plugin for Silicon Graphics Image Format (SGI)
</description>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
<classifier>tests</classifier>
</dependency>
</dependencies>
</project>

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.

View File

@ -36,6 +36,7 @@
<module>imageio-pict</module> <module>imageio-pict</module>
<module>imageio-pnm</module> <module>imageio-pnm</module>
<module>imageio-psd</module> <module>imageio-psd</module>
<module>imageio-sgi</module>
<module>imageio-tga</module> <module>imageio-tga</module>
<module>imageio-thumbsdb</module> <module>imageio-thumbsdb</module>
<module>imageio-tiff</module> <module>imageio-tiff</module>