mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-01 00:00:02 -04:00
It all works
This commit is contained in:
+180
@@ -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 + "}";
|
||||
}
|
||||
}
|
||||
+55
@@ -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");
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
// HIRES=0x8000, LACE=0x4
|
||||
// #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") + "}";
|
||||
}
|
||||
}
|
||||
+170
@@ -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);
|
||||
}
|
||||
}
|
||||
+76
@@ -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 + "}";
|
||||
}
|
||||
}
|
||||
Executable
+85
@@ -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 ? "" : "...") + "\"}";
|
||||
}
|
||||
}
|
||||
+68
@@ -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';
|
||||
}
|
||||
+58
@@ -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)";
|
||||
}
|
||||
}
|
||||
Executable
+716
@@ -0,0 +1,716 @@
|
||||
/*
|
||||
* 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 {
|
||||
// http://home.comcast.net/~erniew/lwsdk/docs/filefmts/ilbm.html
|
||||
// http://www.fileformat.info/format/iff/spec/7866a9f0e53c42309af667c5da3bd426/view.htm
|
||||
// - Contains definitions of some "new" chunks, as well as alternative FORM types
|
||||
|
||||
// TODO: One other existing deep bit ordering that you may encounter is the 21-bit
|
||||
// NewTek format.
|
||||
//
|
||||
// NewTek deep ILBM bit ordering:
|
||||
// saved first ------------------------------------------------------> saved last
|
||||
// R7 G7 B7 R6 G6 B6 R5 G5 B5 R4 G4 B4 R3 G3 B3 R2 G2 B2 R1 G1 B1 R0 G0 B0
|
||||
|
||||
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) + " length: " + 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 {
|
||||
// TODO: Remove this hack when we have metadata
|
||||
// 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()) {
|
||||
if (mColorMap != null) {
|
||||
IndexColorModel cm = mColorMap.getIndexColorModel();
|
||||
specifier = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
specifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// NOTE: HAM modes falls through, as they are converted to RGB
|
||||
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(String.format("Bit depth not implemented: %d", mHeader.mBitplanes));
|
||||
}
|
||||
return specifier;
|
||||
}
|
||||
|
||||
private void readBody(final ImageReadParam pParam) throws IOException {
|
||||
mImageInput.seek(mBodyStart);
|
||||
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(final 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);
|
||||
}
|
||||
|
||||
// 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)
|
||||
int planeWidth = 2 * ((width + 15) / 16);
|
||||
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++) {
|
||||
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) {
|
||||
int pixelPos = 0;
|
||||
for (int planePos = 0; planePos < planeWidth; planePos++) {
|
||||
IFFUtil.bitRotateCW(planeData, planePos, planeWidth, row, pixelPos, 1);
|
||||
pixelPos += 8;
|
||||
}
|
||||
|
||||
if (isHAM()) {
|
||||
hamToRGB(row, pModel, data, 0);
|
||||
}
|
||||
else {
|
||||
raster.setDataElements(0, 0, width, 1, row);
|
||||
}
|
||||
}
|
||||
else if (mFormType == IFF.TYPE_PBM) {
|
||||
// TODO: Arraycopy might not be necessary, 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);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError(String.format("Unsupported FORM type: %s", mFormType));
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
// 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)
|
||||
int planeWidth = 2 * ((width + 15) / 16);
|
||||
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);
|
||||
WritableRaster raster = mImage.getRaster().createCompatibleWritableRaster(8 * planeWidth, 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;
|
||||
for (int planePos = 0; planePos < planeWidth; planePos++) {
|
||||
IFFUtil.bitRotateCW(planeData, planePos, planeWidth, data, off + pixelPos * channels, channels);
|
||||
pixelPos += 8;
|
||||
}
|
||||
}
|
||||
else if (mFormType == IFF.TYPE_PBM) {
|
||||
System.arraycopy(planeData, 0, data, srcY * 8 * planeWidth, planeWidth);
|
||||
}
|
||||
else {
|
||||
throw new AssertionError(String.format("Unsupported FORM type: %s", mFormType));
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
// TODO: How do we know if the last byte in the body is a pad byte or not?!
|
||||
// The body consists of byte-run (PackBits) compressed rows of bit plane data.
|
||||
// However, we don't know how long each compressed row is, without decoding it...
|
||||
// The workaround below, is to use a decode buffer size of pPlaneWidth,
|
||||
// to make sure we don't decode anything we don't have to (shouldn't).
|
||||
if (mByteRunStream == null) {
|
||||
mByteRunStream = new DataInputStream(
|
||||
new DecoderStream(
|
||||
IIOUtil.createStreamAdapter(pInput, mBody.mChunkLength),
|
||||
new PackBitsDecoder(true),
|
||||
pPlaneWidth * mHeader.mBitplanes
|
||||
)
|
||||
);
|
||||
}
|
||||
mByteRunStream.readFully(pData, pOffset, pPlaneWidth);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IIOException(String.format("Unknown compression type: %d", 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();
|
||||
|
||||
// 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, pArgs[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.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;
|
||||
|
||||
/**
|
||||
* 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 {@code IFFImageReaderSpi}.
|
||||
*/
|
||||
public IFFImageReaderSpi() {
|
||||
this(IIOUtil.getProviderInfo(IFFImageReaderSpi.class));
|
||||
}
|
||||
|
||||
private IFFImageReaderSpi(final ProviderInfo pProviderInfo) {
|
||||
super(
|
||||
pProviderInfo.getVendorName(),
|
||||
pProviderInfo.getVersion(),
|
||||
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;
|
||||
}
|
||||
}
|
||||
Executable
+271
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* 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 almost 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 {
|
||||
|
||||
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: Allow param to dictate type PBM?
|
||||
// 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 int planeWidth = 2 * ((width + 15) / 16);
|
||||
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];
|
||||
|
||||
// TODO: The spec says "Do not compress across rows!".. I think we currently do.
|
||||
// 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
|
||||
String annotation = "Written by " + getOriginatingProvider().getDescription(null);
|
||||
GenericChunk anno = new GenericChunk(IFFUtil.toInt("ANNO".getBytes()), annotation.getBytes());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Executable
+85
@@ -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.spi.ProviderInfo;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
|
||||
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 {@code IFFImageWriterSpi}.
|
||||
*/
|
||||
public IFFImageWriterSpi() {
|
||||
this(IIOUtil.getProviderInfo(IFFImageWriterSpi.class));
|
||||
|
||||
}
|
||||
|
||||
private IFFImageWriterSpi(final ProviderInfo pProviderInfo) {
|
||||
super(
|
||||
pProviderInfo.getVendorName(),
|
||||
pProviderInfo.getVersion(),
|
||||
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(final ImageTypeSpecifier pType) {
|
||||
// TODO: Probably can't store 16 bit types etc...
|
||||
// TODO: Can't store CMYK (well.. it does, but they can't be read back)
|
||||
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";
|
||||
}
|
||||
}
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* 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 number of bits -1
|
||||
*
|
||||
* @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 the chunk identifier
|
||||
* @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))});
|
||||
}
|
||||
}
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.iff.IFFImageReaderSpi
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.iff.IFFImageWriterSpi
|
||||
Executable
+85
@@ -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 - Ok (PackBits decoder chokes on padding byte)
|
||||
new TestData(getClassLoaderResource("/iff/A4000T_HAM8.IFF"), new Dimension(628, 512)), // ham8
|
||||
// 8 color indexed - Ok
|
||||
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
|
||||
// Ok (PackBits decoder chokes on padding byte)
|
||||
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.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user