Adding the twelvemonkeys-imageio sub-project

This commit is contained in:
Harald Kuhr
2009-09-03 20:49:59 +02:00
parent 2523f31a8c
commit 4e7316886b
304 changed files with 27557 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
Copyright (c) 2009, 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.

View File

@@ -0,0 +1,32 @@
<?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>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-iff</artifactId>
<version>2.1</version>
<name>TwelveMonkeys ImageIO IFF plugin</name>
<description>
ImageIO plugin for Amiga/Electronic Arts Interchange Filed Format (IFF)
type ILBM and PBM format.
</description>
<parent>
<artifactId>twelvemonkeys-imageio</artifactId>
<groupId>com.twelvemonkeys</groupId>
<version>2.1</version>
</parent>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>twelvemonkeys-imageio-core</artifactId>
<classifier>tests</classifier>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,180 @@
/*
* Copyright (c) 2008, 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.iff;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* BMHDChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BMHDChunk.java,v 1.0 28.feb.2006 00:04:32 haku Exp$
*/
class BMHDChunk extends IFFChunk {
//
// typedef UBYTE Masking; /* Choice of masking technique. */
//
// #define mskNone 0
// #define mskHasMask 1
// #define mskHasTransparentColor 2
// #define mskLasso 3
//
// typedef UBYTE Compression; /* Choice of compression algorithm
// applied to the rows of all source and mask planes. "cmpByteRun1"
// is the byte run encoding described in Appendix C. Do not compress
// across rows! */
// #define cmpNone 0
// #define cmpByteRun1 1
//
// typedef struct {
// UWORD w, h; /* raster width & height in pixels */
// WORD x, y; /* pixel position for this image */
// UBYTE nPlanes; /* # source bitplanes */
// Masking masking;
// Compression compression;
// UBYTE pad1; /* unused; ignore on read, write as 0 */
// UWORD transparentColor; /* transparent "color number" (sort of) */
// UBYTE xAspect, yAspect; /* pixel aspect, a ratio width : height */
// WORD pageWidth, pageHeight; /* source "page" size in pixels */
// } BitMapHeader;*/
static final int MASK_NONE = 0;
static final int MASK_HAS_MASK = 1;
static final int MASK_TRANSPARENT_COLOR = 2;
static final int MASK_LASSO = 3;
static final int COMPRESSION_NONE = 0;
// RLE
static final int COMPRESSION_BYTE_RUN = 1;
// NOTE: Each row of the image is stored in an integral number of 16 bit
// words. The number of words per row is words=((w+15)/16)
// Dimensions of raster
int mWidth;
int mHeight;
// Source offsets
// Hmm.. Consider making these Image.properties?
int mXPos;
int mYPos;
// The number of source bitplanes in the BODY chunk (see below) is stored in
// nPlanes. An ILBM with a CMAP but no BODY and nPlanes = 0 is the
// recommended way to store a color map.
int mBitplanes;
int mMaskType;
int mCompressionType;
int mTransparentIndex;
// NOTE: Typical values are 10:11 (320 x 200)
int mXAspect;
int mYAspect;
// Source page dimension
// NOTE: The image may be larger than the page, probably ignore these
int mPageWidth;
int mPageHeight;
protected BMHDChunk(int pChunkLength) {
super(IFF.CHUNK_BMHD, pChunkLength);
}
protected BMHDChunk(int pWidth, int pHeight, int pBitplanes,
int pMaskType, int pCompressionType,
int pTransparentIndex) {
super(IFF.CHUNK_BMHD, 20);
mWidth = pWidth;
mHeight = pHeight;
mXPos = 0;
mYPos = 0;
mBitplanes = pBitplanes;
mMaskType = pMaskType;
mCompressionType = pCompressionType;
mTransparentIndex = pTransparentIndex;
mXAspect = 1;
mYAspect = 1;
mPageWidth = Math.min(pWidth, Short.MAX_VALUE); // For some reason, these are signed?
mPageHeight = Math.min(pHeight, Short.MAX_VALUE);
}
void readChunk(DataInput pInput) throws IOException {
if (mChunkLength != 20) {
throw new IIOException("Unknown BMHD chunk length: " + mChunkLength);
}
mWidth = pInput.readUnsignedShort();
mHeight = pInput.readUnsignedShort();
mXPos = pInput.readShort();
mYPos = pInput.readShort();
mBitplanes = pInput.readUnsignedByte();
mMaskType = pInput.readUnsignedByte();
mCompressionType = pInput.readUnsignedByte();
pInput.readByte(); // PAD
mTransparentIndex = pInput.readUnsignedShort();
mXAspect = pInput.readUnsignedByte();
mYAspect = pInput.readUnsignedByte();
mPageWidth = pInput.readShort();
mPageHeight = pInput.readShort();
}
void writeChunk(DataOutput pOutput) throws IOException {
pOutput.writeInt(mChunkId);
pOutput.writeInt(mChunkLength);
pOutput.writeShort(mWidth);
pOutput.writeShort(mHeight);
pOutput.writeShort(mXPos);
pOutput.writeShort(mYPos);
pOutput.writeByte(mBitplanes);
pOutput.writeByte(mMaskType);
pOutput.writeByte(mCompressionType);
pOutput.writeByte(0); // PAD
pOutput.writeShort(mTransparentIndex);
pOutput.writeByte(mXAspect);
pOutput.writeByte(mYAspect);
pOutput.writeShort(mPageWidth);
pOutput.writeShort(mPageHeight);
}
public String toString() {
return super.toString()
+ " {w=" + mWidth + ", h=" + mHeight
+ ", x=" + mXPos + ", y=" + mYPos
+ ", planes=" + mBitplanes + ", mask=" + mMaskType
+ ", compression=" + mCompressionType + ", trans=" + mTransparentIndex
+ ", xAspect=" + mXAspect + ", yAspect=" + mYAspect
+ ", pageWidth=" + mPageWidth + ", pageHeight=" + mPageHeight + "}";
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2008, 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.iff;
import java.io.DataInput;
import java.io.IOException;
import java.io.DataOutput;
/**
* BODYChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: BODYChunk.java,v 1.0 28.feb.2006 01:25:49 haku Exp$
*/
class BODYChunk extends IFFChunk {
protected BODYChunk(int pChunkLength) {
super(IFF.CHUNK_BODY, pChunkLength);
}
void readChunk(DataInput pInput) throws IOException {
throw new InternalError("BODY chunk should only be read from IFFImageReader");
}
void writeChunk(DataOutput pOutput) throws IOException {
throw new InternalError("BODY chunk should only be written from IFFImageWriter");
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2008, 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.iff;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* CAMGChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CAMGChunk.java,v 1.0 28.feb.2006 02:10:07 haku Exp$
*/
class CAMGChunk extends IFFChunk {
// #define CAMG_HAM 0x800 /* hold and modify */
// #define CAMG_EHB 0x80 /* extra halfbrite */
private int mCAMG;
public CAMGChunk(int pLength) {
super(IFF.CHUNK_CAMG, pLength);
}
void readChunk(DataInput pInput) throws IOException {
if (mChunkLength != 4) {
throw new IIOException("Unknown CAMG chunk length: " + mChunkLength);
}
mCAMG = pInput.readInt();
}
void writeChunk(DataOutput pOutput) throws IOException {
throw new InternalError("Not implemented: writeChunk()");
}
boolean isHAM() {
return (mCAMG & 0x800) != 0;
}
boolean isEHB() {
return (mCAMG & 0x80) != 0;
}
public String toString() {
return super.toString() + " {mode=" + (isHAM() ? "HAM" : isEHB() ? "EHB" : "Normal") + "}";
}
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright (c) 2008, 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.iff;
import com.twelvemonkeys.image.InverseColorMapIndexColorModel;
import javax.imageio.IIOException;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* CMAPChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: CMAPChunk.java,v 1.0 28.feb.2006 00:38:05 haku Exp$
*/
class CMAPChunk extends IFFChunk {
// typedef struct {
// UBYTE red, green, blue; /* color intensities 0..255 */
// } ColorRegister; /* size = 3 bytes */
//
// typedef ColorRegister ColorMap[n]; /* size = 3n bytes */
byte[] mReds;
byte[] mGreens;
byte[] mBlues;
boolean mEHB;
final private BMHDChunk mHeader;
final private CAMGChunk mCamg;
private IndexColorModel mModel;
protected CMAPChunk(int pChunkLength, BMHDChunk pHeader, CAMGChunk pCamg) {
super(IFF.CHUNK_CMAP, pChunkLength);
mHeader = pHeader;
mCamg = pCamg;
}
public CMAPChunk(IndexColorModel pModel) {
super(IFF.CHUNK_CMAP, pModel.getMapSize() * 3);
mModel = pModel;
mHeader = null;
mCamg = null;
}
void readChunk(DataInput pInput) throws IOException {
int numColors = mChunkLength / 3;
int paletteSize = numColors;
boolean isEHB = mCamg != null && mCamg.isEHB();
if (isEHB) {
if (numColors == 32) {
paletteSize = 64;
}
else {
throw new IIOException("Unknown number of colors for EHB: " + numColors);
}
}
mReds = new byte[paletteSize];
mGreens = mReds.clone();
mBlues = mReds.clone();
for (int i = 0; i < numColors; i++) {
mReds[i] = pInput.readByte();
mGreens[i] = pInput.readByte();
mBlues[i] = pInput.readByte();
}
if (isEHB) {
for (int i = 0; i < numColors; i++) {
mReds[i + numColors] = (byte) ((mReds[i] & 0xff) / 2);
mGreens[i + numColors] = (byte) ((mGreens[i] & 0xff) / 2);
mBlues[i + numColors] = (byte) ((mBlues[i] & 0xff) / 2);
}
}
// TODO: When reading in a CMAP for 8-bit-per-gun display or
// manipulation, you may want to assume that any CMAP which has 0 values
// for the low bits of all guns for all registers was stored shifted
// rather than scaled, and provide your own scaling.
// Use defaults if the color map is absent or has fewer color registers
// than you need. Ignore any extra color registers.
// R8 := (Rn x 255 ) / maxColor
// All chunks are WORD aligned (even sized), may need to read pad...
if (mChunkLength % 2 != 0) {
pInput.readByte();
}
// TODO: Bitmask transparency
// Would it work to double to numbers of colors, and create an indexcolormodel,
// with alpha, where all colors above the original color is all transparent?
// This is a waste of time and space, of course...
int trans = mHeader.mMaskType == BMHDChunk.MASK_TRANSPARENT_COLOR ? mHeader.mTransparentIndex : -1;
mModel = new InverseColorMapIndexColorModel(mHeader.mBitplanes, mReds.length, mReds, mGreens, mBlues, trans);
}
void writeChunk(DataOutput pOutput) throws IOException {
pOutput.writeInt(mChunkId);
pOutput.writeInt(mChunkLength);
final int length = mModel.getMapSize();
for (int i = 0; i < length; i++) {
pOutput.writeByte(mModel.getRed(i));
pOutput.writeByte(mModel.getGreen(i));
pOutput.writeByte(mModel.getBlue(i));
}
if (mChunkLength % 2 != 0) {
pOutput.writeByte(0); // PAD
}
}
public String toString() {
return super.toString() + " {colorMap=" + mModel + "}";
}
IndexColorModel getIndexColorModel() {
return mModel;
}
public BufferedImage createPaletteImage() {
// Create a 1 x colors.length image
IndexColorModel cm = getIndexColorModel();
WritableRaster raster = cm.createCompatibleWritableRaster(cm.getMapSize(), 1);
byte[] pixel = null;
for (int x = 0; x < cm.getMapSize(); x++) {
pixel = (byte[]) cm.getDataElements(cm.getRGB(x), pixel);
raster.setDataElements(x, 0, pixel);
}
return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2008, 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.iff;
import javax.imageio.IIOException;
import java.awt.*;
import java.awt.geom.Point2D;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
/**
* GRABChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: GRABChunk.java,v 1.0 28.feb.2006 01:55:05 haku Exp$
*/
class GRABChunk extends IFFChunk {
// typedef struct {
// WORD x, y; /* relative coordinates (pixels) */
// } Point2D;
Point2D mPoint;
protected GRABChunk(int pChunkLength) {
super(IFF.CHUNK_GRAB, pChunkLength);
}
protected GRABChunk(Point2D pPoint) {
super(IFF.CHUNK_GRAB, 4);
mPoint = pPoint;
}
void readChunk(DataInput pInput) throws IOException {
if (mChunkLength != 4) {
throw new IIOException("Unknown GRAB chunk size: " + mChunkLength);
}
mPoint = new Point(pInput.readShort(), pInput.readShort());
}
void writeChunk(DataOutput pOutput) throws IOException {
pOutput.writeShort((int) mPoint.getX());
pOutput.writeShort((int) mPoint.getY());
}
public String toString() {
return super.toString() + " {point=" + mPoint + "}";
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2008, 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.iff;
import java.io.DataInput;
import java.io.IOException;
import java.io.DataOutput;
/**
* UnknownChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: UnknownChunk.java,v 1.0 28.feb.2006 00:53:47 haku Exp$
*/
class GenericChunk extends IFFChunk {
byte[] mData;
protected GenericChunk(int pChunkId, int pChunkLength) {
super(pChunkId, pChunkLength);
mData = new byte[pChunkLength <= 50 ? pChunkLength : 47];
}
protected GenericChunk(int pChunkId, byte[] pChunkData) {
super(pChunkId, pChunkData.length);
mData = pChunkData;
}
void readChunk(DataInput pInput) throws IOException {
pInput.readFully(mData, 0, mData.length);
int toSkip = mChunkLength - mData.length;
while (toSkip > 0) {
toSkip -= pInput.skipBytes(toSkip);
}
// Read pad
if (mChunkLength % 2 != 0) {
pInput.readByte();
}
}
void writeChunk(DataOutput pOutput) throws IOException {
pOutput.writeInt(mChunkId);
pOutput.writeInt(mChunkLength);
pOutput.write(mData, 0, mData.length);
if (mData.length % 2 != 0) {
pOutput.writeByte(0); // PAD
}
}
public String toString() {
return super.toString() + " {value=\""
+ new String(mData, 0, mData.length <= 50 ? mData.length : 47)
+ (mChunkLength <= 50 ? "" : "...") + "\"}";
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2008, 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.iff;
/**
* IFF format constants.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: IFF.java,v 1.0 07.mar.2006 15:31:48 haku Exp$
*/
interface IFF {
/** IFF FORM group chunk */
int CHUNK_FORM = ('F' << 24) + ('O' << 16) + ('R' << 8) + 'M';
/** IFF ILBM form type */
int TYPE_ILBM = ('I' << 24) + ('L' << 16) + ('B' << 8) + 'M';
/** IFF PBM form type */
int TYPE_PBM = ('P' << 24) + ('B' << 16) + ('M' << 8) + ' ';
/** Bitmap Header chunk */
int CHUNK_BMHD = ('B' << 24) + ('M' << 16) + ('H' << 8) + 'D';
/** Color map chunk */
int CHUNK_CMAP = ('C' << 24) + ('M' << 16) + ('A' << 8) + 'P';
/** Hotspot chunk (cursors, brushes) */
int CHUNK_GRAB = ('G' << 24) + ('R' << 16) + ('A' << 8) + 'B';
/** Destination merge data chunk */
int CHUNK_DEST = ('D' << 24) + ('E' << 16) + ('S' << 8) + 'T';
/** Sprite information chunk */
int CHUNK_SPRT = ('S' << 24) + ('P' << 16) + ('R' << 8) + 'T';
/** Commodore Amiga viewport mode chunk (used to determine HAM and EHB modes) */
int CHUNK_CAMG = ('C' << 24) + ('A' << 16) + ('M' << 8) + 'G';
/** Main data (body) chunk */
int CHUNK_BODY = ('B' << 24) + ('O' << 16) + ('D' << 8) + 'Y';
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2008, 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.iff;
import java.io.DataInput;
import java.io.IOException;
import java.io.DataOutput;
/**
* IFFChunk
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: IFFChunk.java,v 1.0 28.feb.2006 00:00:45 haku Exp$
*/
abstract class IFFChunk {
int mChunkId;
int mChunkLength;
protected IFFChunk(int pChunkId, int pChunkLength) {
mChunkId = pChunkId;
mChunkLength = pChunkLength;
}
abstract void readChunk(DataInput pInput) throws IOException;
abstract void writeChunk(DataOutput pOutput) throws IOException;
public String toString() {
return IFFUtil.toChunkStr(mChunkId) + " chunk (" + mChunkLength + " bytes)";
}
}

View File

@@ -0,0 +1,693 @@
/*
* Copyright (c) 2008, 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.iff;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
import com.twelvemonkeys.io.enc.DecoderStream;
import com.twelvemonkeys.io.enc.PackBitsDecoder;
import javax.imageio.*;
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.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Reader for Amiga (Electronic Arts) IFF ILBM (InterLeaved BitMap) and PBM
* format (Packed BitMap).
* The IFF format (Interchange File Format) is the standard file format
* supported by allmost all image software for the Amiga computer.
* <p/>
* This reader supports the original palette-based 1-8 bit formats, including
* EHB (Extra Halfbright), HAM (Hold and Modify), and the more recent "deep"
* formats, 8 bit gray, 24 bit RGB and 32 bit ARGB.
* Uncompressed and ByteRun1 compressed (run lenght encoding) files are
* supported.
* <p/>
* Palette based images are read as {@code BufferedImage} of
* {@link BufferedImage#TYPE_BYTE_INDEXED TYPE_BYTE_INDEXED} or
* {@link BufferedImage#TYPE_BYTE_BINARY BufferedImage#}
* depending on the bit depth.
* Gray images are read as
* {@link BufferedImage#TYPE_BYTE_GRAY TYPE_BYTE_GRAY}.
* 24 bit true-color images are read as
* {@link BufferedImage#TYPE_3BYTE_BGR TYPE_3BYTE_BGR}.
* 32 bit true-color images are read as
* {@link BufferedImage#TYPE_4BYTE_ABGR TYPE_4BYTE_ABGR}.
* <p/>
* Issues: HAM and HAM8 (Hold and Modify) formats are converted to RGB (24 bit),
* as it seems to be very hard to create an {@code IndexColorModel} subclass
* that would correctly describe these formats.
* These formats utilizes the special display hardware in the Amiga computers.
* HAM (6 bits) needs 12 bits storage/pixel, if unpacked to RGB (4 bits/gun).
* HAM8 (8 bits) needs 18 bits storage/pixel, if unpacked to RGB (6 bits/gun).
* See <a href="http://en.wikipedia.org/wiki/Hold_And_Modify">Wikipedia: HAM</a>
* for more information.
* <br/>
* EHB palette is expanded to an {@link IndexColorModel} with 64 entries.
* See <a href="http://en.wikipedia.org/wiki/Extra_Half-Brite">Wikipedia: EHB</a>
* for more information.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: IFFImageReader.java,v 1.0 29.aug.2004 20:26:58 haku Exp $
* @see <a href="http://en.wikipedia.org/wiki/Interchange_File_Format">Wikipedia: IFF</a>
* @see <a href="http://en.wikipedia.org/wiki/ILBM">Wikipedia: IFF ILBM</a>
*/
public class IFFImageReader extends ImageReaderBase {
private BMHDChunk mHeader;
private CMAPChunk mColorMap;
private BODYChunk mBody;
private GRABChunk mGrab;
private CAMGChunk mViewPort;
private int mFormType;
private long mBodyStart;
private BufferedImage mImage;
private DataInputStream mByteRunStream;
public IFFImageReader() {
super(IFFImageReaderSpi.sharedProvider());
}
protected IFFImageReader(ImageReaderSpi pProvider) {
super(pProvider);
}
private void init(int pIndex) throws IOException {
checkBounds(pIndex);
if (mHeader == null) {
readMeta();
}
}
protected void resetMembers() {
mHeader = null;
mColorMap = null;
mBody = null;
mViewPort = null;
mFormType = 0;
mImage = null;
mByteRunStream = null;
}
private void readMeta() throws IOException {
if (mImageInput.readInt() != IFF.CHUNK_FORM) {
throw new IIOException("Unknown file format for IFFImageReader");
}
int remaining = mImageInput.readInt() - 4; // We'll read 4 more in a sec
mFormType = mImageInput.readInt();
if (mFormType != IFF.TYPE_ILBM && mFormType != IFF.TYPE_PBM) {
throw new IIOException("Only IFF (FORM) type ILBM and PBM supported: " + IFFUtil.toChunkStr(mFormType));
}
//System.out.println("IFF type FORM " + toChunkStr(type));
mGrab = null;
mViewPort = null;
while (remaining > 0) {
int chunkId = mImageInput.readInt();
int length = mImageInput.readInt();
remaining -= 8;
remaining -= length % 2 == 0 ? length : length + 1;
//System.out.println("Next chunk: " + toChunkStr(chunkId) + " lenght: " + length);
//System.out.println("Remaining bytes after chunk: " + remaining);
switch (chunkId) {
case IFF.CHUNK_BMHD:
if (mHeader != null) {
throw new IIOException("Multiple BMHD chunks not allowed");
}
mHeader = new BMHDChunk(length);
mHeader.readChunk(mImageInput);
//System.out.println(mHeader);
break;
case IFF.CHUNK_CMAP:
if (mColorMap != null) {
throw new IIOException("Multiple CMAP chunks not allowed");
}
mColorMap = new CMAPChunk(length, mHeader, mViewPort);
mColorMap.readChunk(mImageInput);
//System.out.println(mColorMap);
break;
case IFF.CHUNK_GRAB:
if (mGrab != null) {
throw new IIOException("Multiple GRAB chunks not allowed");
}
mGrab = new GRABChunk(length);
mGrab.readChunk(mImageInput);
//System.out.println(mGrab);
break;
case IFF.CHUNK_CAMG:
if (mViewPort != null) {
throw new IIOException("Multiple CAMG chunks not allowed");
}
mViewPort = new CAMGChunk(length);
mViewPort.readChunk(mImageInput);
//System.out.println(mViewPort);
break;
case IFF.CHUNK_BODY:
if (mBody != null) {
throw new IIOException("Multiple BODY chunks not allowed");
}
mBody = new BODYChunk(length);
mBodyStart = mImageInput.getStreamPosition();
// NOTE: We don't read the body here, it's done later in the
// read(int, ImageReadParam) method
// Done reading meta
return;
default:
// TODO: We probably want to store anno chunks as Metadata
// ANNO, DEST, SPRT and more
IFFChunk generic = new GenericChunk(chunkId, length);
generic.readChunk(mImageInput);
//System.out.println(generic);
break;
}
}
}
public BufferedImage read(int pIndex, ImageReadParam pParam) throws IOException {
init(pIndex);
processImageStarted(pIndex);
mImage = getDestination(pParam, getImageTypes(pIndex), mHeader.mWidth, mHeader.mHeight);
//System.out.println(mBody);
if (mBody != null) {
//System.out.println("Read body");
readBody(pParam);
}
else {
// In the rare case of an ILBM containing nothing but a CMAP
//System.out.println(mColorMap);
if (mColorMap != null) {
//System.out.println("Creating palette!");
mImage = mColorMap.createPaletteImage();
}
}
BufferedImage result = mImage;
processImageComplete();
return result;
}
public int getWidth(int pIndex) throws IOException {
init(pIndex);
return mHeader.mWidth;
}
public int getHeight(int pIndex) throws IOException {
init(pIndex);
return mHeader.mHeight;
}
public Iterator<ImageTypeSpecifier> getImageTypes(int pIndex) throws IOException {
init(pIndex);
List<ImageTypeSpecifier> types = Arrays.asList(
getRawImageType(pIndex),
ImageTypeSpecifier.createFromBufferedImageType(mHeader.mBitplanes == 32 ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR)
// TODO: ImageTypeSpecifier.createFromBufferedImageType(mHeader.mBitplanes == 32 ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB),
// TODO: Allow 32 bit always. Allow RGB and discard alpha, if present?
);
return types.iterator();
}
@Override
public ImageTypeSpecifier getRawImageType(int pIndex) throws IOException {
init(pIndex);
// TODO: Stay DRY...
// TODO: Use this for creating the Image/Buffer in the read code below...
// NOTE: mColorMap may be null for 8 bit (gray), 24 bit or 32 bit only
ImageTypeSpecifier specifier;
switch (mHeader.mBitplanes) {
case 1:
// 1 bit
case 2:
// 2 bit
case 3:
case 4:
// 4 bit
case 5:
case 6:
// May be HAM6
// May be EHB
case 7:
case 8:
// 8 bit
// May be HAM8
if (!isHAM()) {
IndexColorModel cm = mColorMap.getIndexColorModel();
if (cm != null) {
specifier = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
break;
}
else {
specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
break;
}
}
case 24:
// 24 bit RGB
specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
break;
case 32:
// 32 bit ARGB
specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
break;
default:
throw new IIOException("Bit depth not implemented: " + mHeader.mBitplanes);
}
return specifier;
}
private void readBody(ImageReadParam pParam) throws IOException {
mImageInput.seek(mBodyStart); // 8 for the header before length in stream
mByteRunStream = null;
// NOTE: mColorMap may be null for 8 bit (gray), 24 bit or 32 bit only
if (mColorMap != null) {
IndexColorModel cm = mColorMap.getIndexColorModel();
readIndexed(pParam, mImageInput, cm);
}
else {
readTrueColor(pParam, mImageInput);
}
}
private void readIndexed(ImageReadParam pParam, final ImageInputStream pInput, final IndexColorModel pModel) throws IOException {
final int width = mHeader.mWidth;
final int height = mHeader.mHeight;
final Rectangle aoi = getSourceRegion(pParam, width, height);
final Point offset = pParam == null ? new Point(0, 0) : pParam.getDestinationOffset();
// Set everything to default values
int sourceXSubsampling = 1;
int sourceYSubsampling = 1;
int[] sourceBands = null;
int[] destinationBands = null;
// Get values from the ImageReadParam, if any
if (pParam != null) {
sourceXSubsampling = pParam.getSourceXSubsampling();
sourceYSubsampling = pParam.getSourceYSubsampling();
sourceBands = pParam.getSourceBands();
destinationBands = pParam.getDestinationBands();
}
// Ensure band settings from param are compatible with images
checkReadParamBandSettings(pParam, isHAM() ? 3 : 1, mImage.getSampleModel().getNumBands());
WritableRaster destination = mImage.getRaster();
if (destinationBands != null || offset.x != 0 || offset.y != 0) {
destination = destination.createWritableChild(0, 0, destination.getWidth(), destination.getHeight(), offset.x, offset.y, destinationBands);
}
int planeWidth = (width + 7) / 8;
final byte[] planeData = new byte[8 * planeWidth];
ColorModel cm;
WritableRaster raster;
if (isHAM()) {
// TODO: If HAM6, use type USHORT_444_RGB or 2BYTE_444_RGB?
// Or create a HAMColorModel, if at all possible?
// TYPE_3BYTE_BGR
cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[]{8, 8, 8},
false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
// Create a byte raster with BGR order
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, width, 1, width * 3, 3, new int[]{2, 1, 0}, null);
}
else {
// TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED
cm = pModel;
raster = pModel.createCompatibleWritableRaster(width, 1);
}
Raster sourceRow = raster.createChild(aoi.x, 0, aoi.width, 1, 0, 0, sourceBands);
final byte[] row = new byte[width * 8];
//System.out.println("Data length: " + data.length);
//System.out.println("PlaneData length: " + planeData.length * planeData[0].length);
//System.out.println("Row length: " + row.length);
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
final int planes = mHeader.mBitplanes;
Object dataElements = null;
Object outDataElements = null;
ColorConvertOp converter = null;
for (int srcY = 0; srcY < height; srcY++) {
for (int p = 0; p < planes; p++) {
try {
readPlaneData(pInput, planeData, p * planeWidth, planeWidth);
}
catch (IOException e) {
// TODO: Add warning? Probbably a bug somewhere, should not catch!
}
}
// Skip rows outside AOI
if (srcY < aoi.y || (srcY - aoi.y) % sourceYSubsampling != 0) {
continue;
}
else if (srcY >= (aoi.y + aoi.height)) {
return;
}
if (mFormType == IFF.TYPE_ILBM) {
int pixelPos = 0;
int planePos = 0;
for (int i = 0; i < planeWidth; i++) {
IFFUtil.bitRotateCW(planeData, planePos, planeWidth, row, pixelPos, 1);
pixelPos += 8;
planePos++;
}
if (isHAM()) {
hamToRGB(row, pModel, data, 0);
}
else {
raster.setDataElements(0, 0, width, 1, row);
}
}
else /*if (mType == IFFImageReader.TYPE_PBM)*/ {
// TODO: Arraycopy might not be neccessary, if it's okay with row larger than width
System.arraycopy(planeData, 0, row, 0, mHeader.mBitplanes * planeWidth);
raster.setDataElements(0, 0, width, 1, row);
}
int dstY = (srcY - aoi.y) / sourceYSubsampling;
// Handle non-converting raster as special case for performance
if (cm.isCompatibleRaster(destination)) {
// Rasters are compatible, just write to destinaiton
if (sourceXSubsampling == 1) {
destination.setRect(offset.x, dstY, sourceRow);
// dataElements = raster.getDataElements(aoi.x, 0, aoi.width, 1, dataElements);
// destination.setDataElements(offset.x, offset.y + (srcY - aoi.y) / sourceYSubsampling, aoi.width, 1, dataElements);
}
else {
for (int srcX = 0; srcX < sourceRow.getWidth(); srcX += sourceXSubsampling) {
dataElements = sourceRow.getDataElements(srcX, 0, dataElements);
int dstX = /*offset.x +*/ srcX / sourceXSubsampling;
destination.setDataElements(dstX, dstY, dataElements);
}
}
}
else {
if (cm instanceof IndexColorModel) {
// TODO: Optimize this thing... Maybe it's faster to just get the data indexed, and use drawImage?
IndexColorModel icm = (IndexColorModel) cm;
for (int srcX = 0; srcX < sourceRow.getWidth(); srcX += sourceXSubsampling) {
dataElements = sourceRow.getDataElements(srcX, 0, dataElements);
int rgb = icm.getRGB(dataElements);
outDataElements = mImage.getColorModel().getDataElements(rgb, outDataElements);
int dstX = srcX / sourceXSubsampling;
destination.setDataElements(dstX, dstY, outDataElements);
}
}
else {
// TODO: This branch is never tested, and is probably "dead"
// ColorConvertOp
if (converter == null) {
converter = new ColorConvertOp(cm.getColorSpace(), mImage.getColorModel().getColorSpace(), null);
}
converter.filter(
raster.createChild(aoi.x, 0, aoi.width, 1, 0, 0, null),
destination.createWritableChild(offset.x, offset.y + srcY - aoi.y, aoi.width, 1, 0, 0, null)
);
}
}
processImageProgress(srcY * 100f / mHeader.mWidth);
if (abortRequested()) {
processReadAborted();
break;
}
}
}
// One row from each of the 24 bitplanes is written before moving to the
// next scanline. For each scanline, the red bitplane rows are stored first,
// followed by green and blue. The first plane holds the least significant
// bit of the red value for each pixel, and the last holds the most
// significant bit of the blue value.
private void readTrueColor(ImageReadParam pParam, final ImageInputStream pInput) throws IOException {
final int width = mHeader.mWidth;
final int height = mHeader.mHeight;
final Rectangle aoi = getSourceRegion(pParam, width, height);
final Point offset = pParam == null ? new Point(0, 0) : pParam.getDestinationOffset();
// Set everything to default values
int sourceXSubsampling = 1;
int sourceYSubsampling = 1;
int[] sourceBands = null;
int[] destinationBands = null;
// Get values from the ImageReadParam, if any
if (pParam != null) {
sourceXSubsampling = pParam.getSourceXSubsampling();
sourceYSubsampling = pParam.getSourceYSubsampling();
sourceBands = pParam.getSourceBands();
destinationBands = pParam.getDestinationBands();
}
// Ensure band settings from param are compatible with images
checkReadParamBandSettings(pParam, mHeader.mBitplanes / 8, mImage.getSampleModel().getNumBands());
int planeWidth = (width + 7) / 8;
final byte[] planeData = new byte[8 * planeWidth];
WritableRaster destination = mImage.getRaster();
if (destinationBands != null || offset.x != 0 || offset.y != 0) {
destination = destination.createWritableChild(0, 0, destination.getWidth(), destination.getHeight(), offset.x, offset.y, destinationBands);
}
WritableRaster raster = mImage.getRaster().createCompatibleWritableRaster(width, 1);
Raster sourceRow = raster.createChild(aoi.x, 0, aoi.width, 1, 0, 0, sourceBands);
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
final int channels = (mHeader.mBitplanes + 7) / 8;
final int planesPerChannel = 8;
Object dataElements = null;
for (int srcY = 0; srcY < height; srcY++) {
for (int c = 0; c < channels; c++) {
for (int p = 0; p < planesPerChannel; p++) {
readPlaneData(pInput, planeData, p * planeWidth, planeWidth);
}
// Skip rows outside AOI
if (srcY < aoi.y || (srcY - aoi.y) % sourceYSubsampling != 0) {
continue;
}
else if (srcY >= (aoi.y + aoi.height)) {
return;
}
if (mFormType == IFF.TYPE_ILBM) {
// NOTE: Using (channels - c - 1) instead of just c,
// effectively reverses the channel order from RGBA to ABGR
int off = (channels - c - 1);
int pixelPos = 0;
int planePos = 0;
for (int i = 0; i < planeWidth; i++) {
IFFUtil.bitRotateCW(planeData, planePos, planeWidth, data, off + pixelPos * channels, channels);
pixelPos += 8;
planePos++;
}
}
else /*if (mType == IFFImageReader.TYPE_PBM)*/ {
System.arraycopy(planeData, 0, data, srcY * 8 * planeWidth, planeWidth);
}
}
int dstY = (srcY - aoi.y) / sourceYSubsampling;
// TODO: Support conversion to INT (A)RGB rasters (maybe using ColorConvertOp?)
// TODO: Avoid createChild if no region?
if (sourceXSubsampling == 1) {
destination.setRect(0, dstY, sourceRow);
// dataElements = raster.getDataElements(aoi.x, 0, aoi.width, 1, dataElements);
// destination.setDataElements(offset.x, offset.y + (srcY - aoi.y) / sourceYSubsampling, aoi.width, 1, dataElements);
}
else {
for (int srcX = 0; srcX < sourceRow.getWidth(); srcX += sourceXSubsampling) {
dataElements = sourceRow.getDataElements(srcX, 0, dataElements);
int dstX = srcX / sourceXSubsampling;
destination.setDataElements(dstX, dstY, dataElements);
}
}
processImageProgress(srcY * 100f / mHeader.mWidth);
if (abortRequested()) {
processReadAborted();
break;
}
}
}
private void readPlaneData(final ImageInputStream pInput, final byte[] pData, final int pOffset, final int pPlaneWidth)
throws IOException {
switch (mHeader.mCompressionType) {
case BMHDChunk.COMPRESSION_NONE:
pInput.readFully(pData, pOffset, pPlaneWidth);
// Uncompressed rows must have even number of bytes
if ((mHeader.mBitplanes * pPlaneWidth) % 2 != 0) {
pInput.readByte();
}
break;
case BMHDChunk.COMPRESSION_BYTE_RUN:
if (mByteRunStream == null) {
mByteRunStream = new DataInputStream(new DecoderStream(
IIOUtil.createStreamAdapter(pInput, mBody.mChunkLength),
new PackBitsDecoder(true)
));
}
mByteRunStream.readFully(pData, pOffset, pPlaneWidth);
break;
default:
throw new IIOException("Unknown compression type: " + mHeader.mCompressionType);
}
}
private void hamToRGB(final byte[] pIndexed, final IndexColorModel pModel,
final byte[] pDest, final int pDestOffset) {
final int bits = mHeader.mBitplanes;
final int width = mHeader.mWidth;
int lastRed = 0;
int lastGreen = 0;
int lastBlue = 0;
for (int x = 0; x < width; x++) {
int pixel = pIndexed[x] & 0xff;
//System.out.println("--> ham" + bits);
int paletteIndex = bits == 6 ? pixel & 0x0f : pixel & 0x3f;
int indexShift = bits == 6 ? 4 : 2;
int colorMask = bits == 6 ? 0x0f : 0x03;
//System.out.println("palette index=" + paletteIndex);
// Get Hold and Modify bits
switch ((pixel >> (8 - indexShift)) & 0x03) {
case 0x00:// HOLD
lastRed = pModel.getRed(paletteIndex);
lastGreen = pModel.getGreen(paletteIndex);
lastBlue = pModel.getBlue(paletteIndex);
break;
case 0x01:// MODIFY BLUE
lastBlue = (lastBlue & colorMask) | (paletteIndex << indexShift);
break;
case 0x02:// MODIFY RED
lastRed = (lastRed & colorMask) | (paletteIndex << indexShift);
break;
case 0x03:// MODIFY GREEN
lastGreen = (lastGreen & colorMask) | (paletteIndex << indexShift);
break;
}
int offset = (x * 3) + pDestOffset;
pDest[2 + offset] = (byte) lastRed;
pDest[1 + offset] = (byte) lastGreen;
pDest[offset] = (byte) lastBlue;
}
}
private boolean isHAM() {
return mViewPort != null && mViewPort.isHAM();
}
public static void main(String[] pArgs) throws IOException {
ImageReader reader = new IFFImageReader(new IFFImageReaderSpi());
// ImageInputStream input = ImageIO.createImageInputStream(new File(pArgs[0]));
ImageInputStream input = new BufferedImageInputStream(ImageIO.createImageInputStream(new File(pArgs[0])));
boolean canRead = reader.getOriginatingProvider().canDecodeInput(input);
System.out.println("Can read: " + canRead);
if (canRead) {
reader.setInput(input);
ImageReadParam param = reader.getDefaultReadParam();
// param.setSourceRegion(new Rectangle(0, 0, 160, 200));
// param.setSourceRegion(new Rectangle(160, 200, 160, 200));
// param.setSourceRegion(new Rectangle(80, 100, 160, 200));
// param.setDestinationOffset(new Point(80, 100));
// param.setSourceSubsampling(3, 3, 0, 0);
// param.setSourceBands(new int[]{0, 1, 2});
// param.setDestinationBands(new int[]{1, 0, 2});
BufferedImage image = reader.read(0, param);
System.out.println("image = " + image);
showIt(image, "");
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 2008, 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.iff;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
/**
* IFFImageReaderSpi
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: IFFImageWriterSpi.java,v 1.0 28.feb.2006 19:21:05 haku Exp$
*/
public class IFFImageReaderSpi extends ImageReaderSpi {
static IFFImageReaderSpi mSharedInstance;
/**
* Creates an IFFImageReaderSpi
*/
public IFFImageReaderSpi() {
super(
"TwelveMonkeys",
"2.0",
new String[]{"iff", "IFF"},
new String[]{"iff", "lbm", "ham", "ham8", "ilbm"},
new String[]{"image/iff", "image/x-iff"},
"com.twelvemonkeys.imageio.plugins.iff.IFFImageReader",
STANDARD_INPUT_TYPE,
new String[]{"com.twelvemonkeys.imageio.plugins.iff.IFFImageWriterSpi"},
true, null, null, null, null,
true, null, null, null, null
);
if (mSharedInstance == null) {
mSharedInstance = this;
}
}
public boolean canDecodeInput(Object pSource) throws IOException {
return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource);
}
private static boolean canDecode(ImageInputStream pInput) throws IOException {
pInput.mark();
try {
// Is it IFF
if (pInput.readInt() == IFF.CHUNK_FORM) {
pInput.readInt();// Skip length field
int type = pInput.readInt();
// Is it ILBM or PBM
if (type == IFF.TYPE_ILBM || type == IFF.TYPE_PBM) {
return true;
}
}
}
finally {
pInput.reset();
}
return false;
}
public ImageReader createReaderInstance(Object pExtension) throws IOException {
return new IFFImageReader(this);
}
public String getDescription(Locale pLocale) {
return "Amiga (Electronic Arts) Image Interchange Format (IFF) image reader";
}
public static ImageReaderSpi sharedProvider() {
if (mSharedInstance == null) {
new IFFImageReaderSpi();
}
return mSharedInstance;
}
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright (c) 2008, 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.iff;
import com.twelvemonkeys.imageio.ImageWriterBase;
import com.twelvemonkeys.imageio.util.IIOUtil;
import com.twelvemonkeys.io.FastByteArrayOutputStream;
import com.twelvemonkeys.io.enc.EncoderStream;
import com.twelvemonkeys.io.enc.PackBitsEncoder;
import javax.imageio.*;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageWriterSpi;
import java.awt.*;
import java.awt.image.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
/**
* Writer for Amiga (Electronic Arts) IFF ILBM (InterLeaved BitMap) format.
* The IFF format (Interchange File Format) is the standard file format
* supported by allmost all image software for the Amiga computer.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: IFFImageWriter.java,v 1.0 02.mar.2006 13:32:30 haku Exp$
*
* @see <a href="http://en.wikipedia.org/wiki/Interchange_File_Format">Wikipedia: IFF</a>
* @see <a href="http://en.wikipedia.org/wiki/ILBM">Wikipedia: IFF ILBM</a>
*/
public class IFFImageWriter extends ImageWriterBase {
private static final byte[] ANNO_DATA = "Written by TwelveMonkeys IFFImageWriter 1.0 for Java (javax.imageio).".getBytes();
public IFFImageWriter() {
this(null);
}
protected IFFImageWriter(ImageWriterSpi pProvider) {
super(pProvider);
}
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) {
throw new UnsupportedOperationException("Method getDefaultImageMetadata not implemented");// TODO: Implement
}
public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) {
throw new UnsupportedOperationException("Method convertImageMetadata not implemented");// TODO: Implement
}
public void write(IIOMetadata pStreamMetadata, IIOImage pImage, ImageWriteParam pParam) throws IOException {
assertOutput();
if (pImage.hasRaster()) {
throw new UnsupportedOperationException("Cannot write raster");
}
processImageStarted(0);
// Prepare image data to be written
ByteArrayOutputStream imageData = new FastByteArrayOutputStream(1024);
packImageData(imageData, pImage.getRenderedImage(), pParam);
//System.out.println("Image data: " + imageData.size());
// Write metadata
writeMeta(pImage.getRenderedImage(), imageData.size());
// Write image data
writeBody(imageData);
processImageComplete();
}
private void writeBody(ByteArrayOutputStream pImageData) throws IOException {
mImageOutput.writeInt(IFF.CHUNK_BODY);
mImageOutput.writeInt(pImageData.size());
// NOTE: This is much faster than mOutput.write(pImageData.toByteArray())
// as the data array is not duplicated
pImageData.writeTo(IIOUtil.createStreamAdapter(mImageOutput));
if (pImageData.size() % 2 == 0) {
mImageOutput.writeByte(0); // PAD
}
// NOTE: Most progress is done in packImageData, however, as we need to
// buffer, to write correct size, we defer the last 10 percent until now.
processImageProgress(100f);
mImageOutput.flush();
}
private void packImageData(OutputStream pOutput, RenderedImage pImage, ImageWriteParam pParam) throws IOException {
// TODO: Allow param to dictate uncompressed
// TODO: Subsample/AOI
final boolean compress = shouldCompress(pImage);
final OutputStream output = compress ? new EncoderStream(pOutput, new PackBitsEncoder(), true) : pOutput;
final ColorModel model = pImage.getColorModel();
final Raster raster = pImage.getData();
final int width = pImage.getWidth();
final int height = pImage.getHeight();
// Store each row of pixels
// 0. Loop pr channel
// 1. Convert to planar
// 2. Perform byteRun1 compression for each plane separately
// 3. Write the plane data for each plane
final int planeWidth = (width + 7) / 8;
final byte[] planeData = new byte[8 * planeWidth];
final int channels = (model.getPixelSize() + 7) / 8;
final int planesPerChannel = channels == 1 ? model.getPixelSize() : 8;
int[] pixels = new int[8 * planeWidth];
// NOTE: I'm a little unsure if this is correct for 4 channel (RGBA)
// data, but it is at least consistent with the IFFImageReader for now...
for (int y = 0; y < height; y++) {
for (int c = 0; c < channels; c++) {
pixels = raster.getSamples(0, y, width, 1, c, pixels);
int pixelPos = 0;
int planePos = 0;
for (int i = 0; i < planeWidth; i++) {
IFFUtil.bitRotateCCW(pixels, pixelPos, 1,
planeData, planePos, planeWidth);
pixelPos += 8;
planePos++;
}
for (int p = 0; p < planesPerChannel; p++) {
output.write(planeData, p * planeWidth, planeWidth);
if (!compress && planeWidth % 2 != 0) {
output.write(0); // PAD
}
}
}
processImageProgress(y * 90f / height);
}
output.flush();
}
private void writeMeta(RenderedImage pImage, int pBodyLength) throws IOException {
// Annotation ANNO chunk, 8 + annoData.length bytes
GenericChunk anno = new GenericChunk(IFFUtil.toInt("ANNO".getBytes()), ANNO_DATA);
ColorModel cm = pImage.getColorModel();
IndexColorModel icm = null;
// Bitmap header BMHD chunk, 8 + 20 bytes
// By default, don't compress narrow images
int compression = shouldCompress(pImage) ? BMHDChunk.COMPRESSION_BYTE_RUN : BMHDChunk.COMPRESSION_NONE;
BMHDChunk header;
if (cm instanceof IndexColorModel) {
//System.out.println("IndexColorModel");
icm = (IndexColorModel) cm;
int trans = icm.getTransparency() == Transparency.BITMASK ? BMHDChunk.MASK_TRANSPARENT_COLOR : BMHDChunk.MASK_NONE;
int transPixel = icm.getTransparency() == Transparency.BITMASK ? icm.getTransparentPixel() : 0;
header = new BMHDChunk(pImage.getWidth(), pImage.getHeight(), icm.getPixelSize(),
trans, compression, transPixel);
}
else {
//System.out.println(cm.getClass().getName());
header = new BMHDChunk(pImage.getWidth(), pImage.getHeight(), cm.getPixelSize(),
BMHDChunk.MASK_NONE, compression, 0);
}
// Colormap CMAP chunk, 8 + icm.getMapSize() * 3 bytes (+ 1 optional pad).
CMAPChunk cmap = null;
if (icm != null) {
//System.out.println("CMAP!");
cmap = new CMAPChunk(icm);
}
// ILBM(4) + anno(8+len) + header(8+20) + cmap(8+len)? + body(8+len);
int size = 4 + 8 + anno.mChunkLength + 28 + 8 + pBodyLength;
if (cmap != null) {
size += 8 + cmap.mChunkLength;
}
mImageOutput.writeInt(IFF.CHUNK_FORM);
mImageOutput.writeInt(size);
mImageOutput.writeInt(IFF.TYPE_ILBM);
anno.writeChunk(mImageOutput);
header.writeChunk(mImageOutput);
if (cmap != null) {
//System.out.println("CMAP written");
cmap.writeChunk(mImageOutput);
}
}
private boolean shouldCompress(RenderedImage pImage) {
return pImage.getWidth() >= 32;
}
public static void main(String[] pArgs) throws IOException {
BufferedImage image = ImageIO.read(new File(pArgs[0]));
ImageWriter writer = new IFFImageWriter(new IFFImageWriterSpi());
writer.setOutput(ImageIO.createImageOutputStream(new File(pArgs[1])));
//writer.addIIOWriteProgressListener(new ProgressListenerBase() {
// int mCurrPct = 0;
//
// public void imageComplete(ImageWriter pSource) {
// mCurrPct = 100;
// printProgress(mCurrPct);
// }
//
// public void imageProgress(ImageWriter pSource, float pPercentageDone) {
// if ((int) pPercentageDone > mCurrPct) {
// printProgress((int) pPercentageDone);
// mCurrPct = (int) pPercentageDone;
// }
// }
//
// private void printProgress(int pCurrPct) {
// if (mCurrPct == 0) {
// System.out.print("[");
// }
// for (int i = mCurrPct / 2; i < pCurrPct / 2; i++) {
// System.out.print(".");
// }
// if (mCurrPct == 100) {
// System.out.println("]");
// }
// }
//});
//image = com.twelvemonkeys.image.ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_ARGB);
writer.write(image);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2008, 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.iff;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriter;
import javax.imageio.spi.ImageWriterSpi;
import java.io.IOException;
import java.util.Locale;
/**
* IFFImageWriterSpi
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: IFFImageWriterSpi.java,v 1.0 02.mar.2006 19:21:05 haku Exp$
*/
public class IFFImageWriterSpi extends ImageWriterSpi {
/**
* Creates an IFFImageWriterSpi
*/
public IFFImageWriterSpi() {
super(
"TwelveMonkeys",
"$Revision: 1.0 $",
new String[]{"iff", "IFF"},
new String[]{"iff", "lbm", "ham", "ham8", "ilbm"},
new String[]{"image/iff", "image/x-iff"},
"com.twelvemonkeys.imageio.plugins.iff.IFFImageWriter",
STANDARD_OUTPUT_TYPE,
new String[]{"com.twelvemonkeys.imageio.plugins.iff.IFFImageReaderSpi"},
true, null, null, null, null,
true, null, null, null, null
);
}
public boolean canEncodeImage(ImageTypeSpecifier pType) {
// TODO: Probably can't store 16 bit types etc...
return true;
}
public ImageWriter createWriterInstance(Object pExtension) throws IOException {
return new IFFImageWriter(this);
}
public String getDescription(Locale pLocale) {
return "Amiga (Electronic Arts) IFF image writer";
}
}

View File

@@ -0,0 +1,255 @@
/*
* Copyright (c) 2008, 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.
*/
/*
* Fast 90-degree bit rotation routines.
*
* Based on Sue-Ken Yap, "A Fast 90-Degree Bitmap Rotator," in GRAPHICS
* GEMS II, James Arvo ed., Academic Press, 1991, ISBN 0-12-064480-0.
*/
package com.twelvemonkeys.imageio.plugins.iff;
/**
* IFFUtil
* <p/>
* Bit rotate methods based on Sue-Ken Yap, "A Fast 90-Degree Bitmap Rotator,"
* in GRAPHICS GEMS II, James Arvo ed., Academic Press, 1991, ISBN 0-12-064480-0.
*
* @author Unascribed (C version)
* @author Harald Kuhr (Java port)
* @version $Id: IFFUtil.java,v 1.0 06.mar.2006 13:31:35 haku Exp$
*/
class IFFUtil {
/**
* Creates a rotation table
* @param n
*
* @return the rotation table
*/
static private long[] rtable(int n) {
return new long[]{
0x00000000l << n, 0x00000001l << n, 0x00000100l << n, 0x00000101l << n,
0x00010000l << n, 0x00010001l << n, 0x00010100l << n, 0x00010101l << n,
0x01000000l << n, 0x01000001l << n, 0x01000100l << n, 0x01000101l << n,
0x01010000l << n, 0x01010001l << n, 0x01010100l << n, 0x01010101l << n};
}
static private final long[][] RTABLE = {
rtable(0), rtable(1), rtable(2), rtable(3),
rtable(4), rtable(5), rtable(6), rtable(7)
};
/**
* Rotate bits clockwise.
* The IFFImageReader uses this to convert pixel bits from planar to chunky.
* Bits from the source are rotated 90 degrees clockwise written to the
* destination.
*
* @param pSrc source pixel data
* @param pSrcPos starting index of 8 x 8 bit source tile
* @param pSrcStep byte offset between adjacent rows in source
* @param pDst destination pixel data
* @param pDstPos starting index of 8 x 8 bit destination tile
* @param pDstStep byte offset between adjacent rows in destination
*/
static void bitRotateCW(final byte[] pSrc, int pSrcPos, int pSrcStep,
final byte[] pDst, int pDstPos, int pDstStep) {
int idx = pSrcPos;
int lonyb;
int hinyb;
long lo = 0;
long hi = 0;
for (int i = 0; i < 8; i++) {
lonyb = pSrc[idx] & 0xF;
hinyb = (pSrc[idx] >> 4) & 0xF;
lo |= RTABLE[i][lonyb];
hi |= RTABLE[i][hinyb];
idx += pSrcStep;
}
idx = pDstPos;
pDst[idx] = (byte)((hi >> 24) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)((hi >> 16) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)((hi >> 8) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)(hi & 0xFF);
idx += pDstStep;
}
}
}
if (idx < pDst.length) {
pDst[idx] = (byte)((lo >> 24) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)((lo >> 16) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)((lo >> 8) & 0xFF);
idx += pDstStep;
if (idx < pDst.length) {
pDst[idx] = (byte)(lo & 0xFF);
}
}
}
}
}
/**
* Rotate bits counterclockwise.
* The IFFImageWriter uses this to convert pixel bits from chunky to planar.
*
* @param pSrc source pixel data (only lower 8 bits used)
* @param pSrcPos starting index of 8 x 8 bit source tile
* @param pSrcStep byte offset between adjacent rows in source
* @param pDst destination pixel data
* @param pDstPos starting index of 8 x 8 bit destination tile
* @param pDstStep byte offset between adjacent rows in destination
*/
static void bitRotateCCW(final int[] pSrc, int pSrcPos, int pSrcStep,
final byte[] pDst, int pDstPos, int pDstStep) {
int idx = pSrcPos;
int lonyb;
int hinyb;
long lo = 0;
long hi = 0;
for (int i = 7; i >= 0; i--) {
lonyb = pSrc[idx] & 0xF;
hinyb = (pSrc[idx] >> 4) & 0xF;
lo |= RTABLE[i][lonyb];
hi |= RTABLE[i][hinyb];
idx += pSrcStep;
}
idx = pDstPos;
pDst[idx] = (byte)(lo & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 8) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 16) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 24) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)(hi & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 8) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 16) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 24) & 0xFF);
}
/**
* Rotate bits counterclockwise.
* The IFFImageWriter uses this to convert pixel bits from chunky to planar.
*
* @param pSrc source pixel data
* @param pSrcPos starting index of 8 x 8 bit source tile
* @param pSrcStep byte offset between adjacent rows in source
* @param pDst destination pixel data
* @param pDstPos starting index of 8 x 8 bit destination tile
* @param pDstStep byte offset between adjacent rows in destination
*/
static void bitRotateCCW(final byte[] pSrc, int pSrcPos, int pSrcStep,
final byte[] pDst, int pDstPos, int pDstStep) {
int idx = pSrcPos;
int lonyb;
int hinyb;
long lo = 0;
long hi = 0;
for (int i = 7; i >= 0; i--) {
lonyb = pSrc[idx] & 0xF;
hinyb = (pSrc[idx] >> 4) & 0xF;
lo |= RTABLE[i][lonyb];
hi |= IFFUtil.RTABLE[i][hinyb];
idx += pSrcStep;
}
idx = pDstPos;
pDst[idx] = (byte)(lo & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 8) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 16) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((lo >> 24) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)(hi & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 8) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 16) & 0xFF);
idx += pDstStep;
pDst[idx] = (byte)((hi >> 24) & 0xFF);
}
/**
* Converts a byte array to an int.
*
* @param pBytes a byte array of length 4
* @return the bytes converted to an int
*
* @throws ArrayIndexOutOfBoundsException if length is < 4
*/
static int toInt(final byte[] pBytes) {
return (pBytes[0] & 0xff) << 24 | (pBytes[1] & 0xff) << 16
| (pBytes[2] & 0xff) << 8 | (pBytes[3] & 0xff);
}
/**
* Converts an int to a four letter String.
*
* @param pChunkId
* @return a String
*/
static String toChunkStr(int pChunkId) {
return new String(new byte[] {(byte) ((pChunkId & 0xff000000) >> 24),
(byte) ((pChunkId & 0x00ff0000) >> 16),
(byte) ((pChunkId & 0x0000ff00) >> 8),
(byte) ((pChunkId & 0x000000ff))});
}
}

View File

@@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.iff.IFFImageReaderSpi

View File

@@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.iff.IFFImageWriterSpi

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2008, 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.iff;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
/**
* IFFImageReaderTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IFFImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
public class IFFImageReaderTestCase extends ImageReaderAbstractTestCase<IFFImageReader> {
// TODO: Need test for IFF PBM
protected List<TestData> getTestData() {
return Arrays.asList(
// 32 bit - Ok
new TestData(getClassLoaderResource("/iff/test.iff"), new Dimension(300, 200)), // 32 bit
// 24 bit - Ok
new TestData(getClassLoaderResource("/iff/survivor.iff"), new Dimension(800, 600)), // 24 bit
// HAM6 - Ok (a lot of visual "fringe", would be interesting to see on a real HAM display)
new TestData(getClassLoaderResource("/iff/A4000T_HAM6.IFF"), new Dimension(320, 512)), // ham6
// HAM8 - Passes tests, but visuals are trashed. Have other HAM8 files that are ok
new TestData(getClassLoaderResource("/iff/A4000T_HAM8.IFF"), new Dimension(628, 512)), // ham8
// 8 color indexed - Passes tests, but trashed. Must be something special with these images
new TestData(getClassLoaderResource("/iff/AmigaBig.iff"), new Dimension(300, 200)), // 8 color
// 8 color indexed - Ok
new TestData(getClassLoaderResource("/iff/AmigaAmiga.iff"), new Dimension(200, 150)), // 8 color
// Breaks completely... Could be bug in the packbits decoder?
new TestData(getClassLoaderResource("/iff/Abyss.iff"), new Dimension(320, 400))
);
}
protected ImageReaderSpi createProvider() {
return new IFFImageReaderSpi();
}
protected Class<IFFImageReader> getReaderClass() {
return IFFImageReader.class;
}
protected List<String> getFormatNames() {
return Arrays.asList("iff");
}
protected List<String> getSuffixes() {
return Arrays.asList("iff", "ilbm", "ham", "ham8", "lbm");
}
protected List<String> getMIMETypes() {
return Arrays.asList("image/iff", "image/x-iff");
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
- Fix the crashes with "java.io.EOFException: Unexpected end of PackBits stream"
- Fix the bugs with trashed images
- Have a look at Werner Randelshofer's ANIM applet, maybe we can "borrow" the the ANIM support. :-)
DONE: