Added multi-palette support to IFFImageReader + minor clean-up.

This commit is contained in:
Harald Kuhr 2012-03-30 16:36:32 +02:00
parent 926359d9d2
commit 4463a00667
20 changed files with 908 additions and 57 deletions

View File

@ -0,0 +1,128 @@
/*
* Copyright (c) 2012, 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.
*/
/*
* Parts of this code is based on ilbmtoppm.c
*
* Copyright (C) 1989 by Jef Poskanzer.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. This software is provided "as is" without express or
* implied warranty.
*
* Multipalette-support by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
*/
package com.twelvemonkeys.imageio.plugins.iff;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.ref.WeakReference;
/**
* AbstractMultiPaletteChunk
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: AbstractMultiPaletteChunk.java,v 1.0 30.03.12 15:57 haraldk Exp$
*/
abstract class AbstractMultiPaletteChunk extends IFFChunk implements MultiPalette {
/* scale factor maxval 15 -> maxval 255 */
static final int FACTOR_4BIT = 17;
protected MutableIndexColorModel.PaletteChange[] initialChanges;
protected MutableIndexColorModel.PaletteChange[][] changes;
protected WeakReference<IndexColorModel> originalPalette;
protected MutableIndexColorModel mutablePalette;
public AbstractMultiPaletteChunk(int pChunkId, int pChunkLength) {
super(pChunkId, pChunkLength);
}
@Override
void readChunk(final DataInput pInput) throws IOException {
if (chunkId == IFF.CHUNK_SHAM) {
pInput.readUnsignedShort(); // Version, typically 0, skipped
}
int rows = chunkLength / 32; /* sizeof(word) * 16 */
changes = new MutableIndexColorModel.PaletteChange[rows][];
for (int row = 0; row < rows; row++) {
changes[row] = new MutableIndexColorModel.PaletteChange[16];
for (int i = 0; i < 16; i++) {
changes[row][i] = new MutableIndexColorModel.PaletteChange();
}
for (int i = 0; i < 16; i++ ) {
int data = pInput.readUnsignedShort();
changes[row][i].index = i;
changes[row][i].r = (byte) (((data & 0x0f00) >> 8) * FACTOR_4BIT);
changes[row][i].g = (byte) (((data & 0x00f0) >> 4) * FACTOR_4BIT);
changes[row][i].b = (byte) (((data & 0x000f) ) * FACTOR_4BIT);
}
}
}
@Override
void writeChunk(DataOutput pOutput) throws IOException {
throw new UnsupportedOperationException("Method writeChunk not implemented");
}
public ColorModel getColorModel(final IndexColorModel colorModel, final int rowIndex, final boolean laced) {
if (mutablePalette == null || originalPalette != null && originalPalette.get() != colorModel) {
originalPalette = new WeakReference<IndexColorModel>(colorModel);
mutablePalette = new MutableIndexColorModel(colorModel);
if (initialChanges != null) {
mutablePalette.adjustColorMap(initialChanges);
}
}
int row = laced && skipLaced() ? rowIndex / 2 : rowIndex;
if (row < changes.length && changes[row] != null) {
mutablePalette.adjustColorMap(changes[row]);
}
return mutablePalette;
}
protected boolean skipLaced() {
return false;
}
}

View File

@ -40,7 +40,7 @@ import java.io.IOException;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @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$ * @version $Id: BMHDChunk.java,v 1.0 28.feb.2006 00:04:32 haku Exp$
*/ */
class BMHDChunk extends IFFChunk { final class BMHDChunk extends IFFChunk {
// //
// typedef UBYTE Masking; /* Choice of masking technique. */ // typedef UBYTE Masking; /* Choice of masking technique. */
// //

View File

@ -39,7 +39,7 @@ import java.io.DataOutput;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @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$ * @version $Id: BODYChunk.java,v 1.0 28.feb.2006 01:25:49 haku Exp$
*/ */
class BODYChunk extends IFFChunk { final class BODYChunk extends IFFChunk {
protected BODYChunk(int pChunkLength) { protected BODYChunk(int pChunkLength) {
super(IFF.CHUNK_BODY, pChunkLength); super(IFF.CHUNK_BODY, pChunkLength);
} }

View File

@ -40,7 +40,7 @@ import java.io.IOException;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @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$ * @version $Id: CAMGChunk.java,v 1.0 28.feb.2006 02:10:07 haku Exp$
*/ */
class CAMGChunk extends IFFChunk { final class CAMGChunk extends IFFChunk {
// HIRES=0x8000, LACE=0x4 // HIRES=0x8000, LACE=0x4
// #define CAMG_HAM 0x800 /* hold and modify */ // #define CAMG_HAM 0x800 /* hold and modify */
// #define CAMG_EHB 0x80 /* extra halfbrite */ // #define CAMG_EHB 0x80 /* extra halfbrite */
@ -55,6 +55,7 @@ class CAMGChunk extends IFFChunk {
if (chunkLength != 4) { if (chunkLength != 4) {
throw new IIOException("Unknown CAMG chunk length: " + chunkLength); throw new IIOException("Unknown CAMG chunk length: " + chunkLength);
} }
camg = pInput.readInt(); camg = pInput.readInt();
} }
@ -62,6 +63,14 @@ class CAMGChunk extends IFFChunk {
throw new InternalError("Not implemented: writeChunk()"); throw new InternalError("Not implemented: writeChunk()");
} }
boolean isHires() {
return (camg & 0x8000) != 0;
}
boolean isLaced() {
return (camg & 0x4) != 0;
}
boolean isHAM() { boolean isHAM() {
return (camg & 0x800) != 0; return (camg & 0x800) != 0;
} }

View File

@ -45,7 +45,7 @@ import java.io.IOException;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @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$ * @version $Id: CMAPChunk.java,v 1.0 28.feb.2006 00:38:05 haku Exp$
*/ */
class CMAPChunk extends IFFChunk { final class CMAPChunk extends IFFChunk {
// typedef struct { // typedef struct {
// UBYTE red, green, blue; /* color intensities 0..255 */ // UBYTE red, green, blue; /* color intensities 0..255 */

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2012, 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;
/**
* CTBLChunk
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: CTBLChunk.java,v 1.0 30.03.12 14:53 haraldk Exp$
*/
final class CTBLChunk extends AbstractMultiPaletteChunk {
protected CTBLChunk(int pChunkLength) {
super(IFF.CHUNK_CTBL, pChunkLength);
}
}

View File

@ -42,7 +42,7 @@ import java.io.IOException;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @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$ * @version $Id: GRABChunk.java,v 1.0 28.feb.2006 01:55:05 haku Exp$
*/ */
class GRABChunk extends IFFChunk { final class GRABChunk extends IFFChunk {
// typedef struct { // typedef struct {
// WORD x, y; /* relative coordinates (pixels) */ // WORD x, y; /* relative coordinates (pixels) */
// } Point2D; // } Point2D;

View File

@ -39,7 +39,7 @@ import java.io.DataOutput;
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @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$ * @version $Id: UnknownChunk.java,v 1.0 28.feb.2006 00:53:47 haku Exp$
*/ */
class GenericChunk extends IFFChunk { final class GenericChunk extends IFFChunk {
byte[] data; byte[] data;
@ -56,15 +56,7 @@ class GenericChunk extends IFFChunk {
void readChunk(DataInput pInput) throws IOException { void readChunk(DataInput pInput) throws IOException {
pInput.readFully(data, 0, data.length); pInput.readFully(data, 0, data.length);
int toSkip = chunkLength - data.length; skipData(pInput, chunkLength, data.length);
while (toSkip > 0) {
toSkip -= pInput.skipBytes(toSkip);
}
// Read pad
if (chunkLength % 2 != 0) {
pInput.readByte();
}
} }
void writeChunk(DataOutput pOutput) throws IOException { void writeChunk(DataOutput pOutput) throws IOException {

View File

@ -39,12 +39,22 @@ interface IFF {
/** IFF FORM group chunk */ /** IFF FORM group chunk */
int CHUNK_FORM = ('F' << 24) + ('O' << 16) + ('R' << 8) + 'M'; int CHUNK_FORM = ('F' << 24) + ('O' << 16) + ('R' << 8) + 'M';
/** IFF ILBM form type */ /** IFF ILBM form type (Interleaved bitmap) */
int TYPE_ILBM = ('I' << 24) + ('L' << 16) + ('B' << 8) + 'M'; int TYPE_ILBM = ('I' << 24) + ('L' << 16) + ('B' << 8) + 'M';
/** IFF PBM form type */ /** IFF PBM form type (Packed bitmap) */
int TYPE_PBM = ('P' << 24) + ('B' << 16) + ('M' << 8) + ' '; int TYPE_PBM = ('P' << 24) + ('B' << 16) + ('M' << 8) + ' ';
// TODO:
/** IFF DEEP form type (TVPaint) */
int TYPE_DEEP = ('D' << 24) + ('E' << 16) + ('E' << 8) + 'P';
/** IFF RGB8 form type (TurboSilver) */
int TYPE_RGB8 = ('R' << 24) + ('G' << 16) + ('B' << 8) + '8';
/** IFF RGBN form type (TurboSilver) */
int TYPE_RGBN = ('R' << 24) + ('G' << 16) + ('B' << 8) + 'N';
/** IFF ACBM form type (Amiga Basic) */
int TYPE_ACBM = ('A' << 24) + ('C' << 16) + ('B' << 8) + 'M';
/** Bitmap Header chunk */ /** Bitmap Header chunk */
int CHUNK_BMHD = ('B' << 24) + ('M' << 16) + ('H' << 8) + 'D'; int CHUNK_BMHD = ('B' << 24) + ('M' << 16) + ('H' << 8) + 'D';
@ -65,4 +75,53 @@ interface IFF {
/** Main data (body) chunk */ /** Main data (body) chunk */
int CHUNK_BODY = ('B' << 24) + ('O' << 16) + ('D' << 8) + 'Y'; int CHUNK_BODY = ('B' << 24) + ('O' << 16) + ('D' << 8) + 'Y';
/** Junk (to allow garbage data in files, without re-writing the entire file) */
int CHUNK_JUNK = ('J' << 24) + ('U' << 16) + ('N' << 8) + 'K';
/** EA IFF 85 Generic Author chunk */
int CHUNK_AUTH = ('A' << 24) + ('U' << 16) + ('T' << 8) + 'H';
/** EA IFF 85 Generic character string chunk */
int CHUNK_CHRS = ('C' << 24) + ('H' << 16) + ('R' << 8) + 'S';
/** EA IFF 85 Generic Name of art, music, etc. chunk */
int CHUNK_NAME = ('N' << 24) + ('A' << 16) + ('M' << 8) + 'E';
/** EA IFF 85 Generic unformatted ASCII text chunk */
int CHUNK_TEXT = ('T' << 24) + ('E' << 16) + ('X' << 8) + 'T';
/** EA IFF 85 Generic Copyright text chunk */
int CHUNK_COPY = ('(' << 24) + ('c' << 16) + (')' << 8) + ' ';
/** color cycling */
int CHUNK_CRNG = ('C' << 24) + ('R' << 16) + ('N' << 8) + 'G';
/** color cycling */
int CHUNK_CCRT = ('C' << 24) + ('C' << 16) + ('R' << 8) + 'T';
/** Color Lookup Table chunk */
int CHUNK_CLUT = ('C' << 24) + ('L' << 16) + ('U' << 8) + 'T';
/** Dots per inch chunk */
int CHUNK_DPI = ('D' << 24) + ('P' << 16) + ('I' << 8) + ' ';
/** DPaint perspective chunk (EA) */
int CHUNK_DPPV = ('D' << 24) + ('P' << 16) + ('P' << 8) + 'V';
/** DPaint IV enhanced color cycle chunk (EA) */
int CHUNK_DRNG = ('D' << 24) + ('R' << 16) + ('N' << 8) + 'G';
/** Encapsulated Postscript chunk */
int CHUNK_EPSF = ('E' << 24) + ('P' << 16) + ('S' << 8) + 'F';
/** Cyan, Magenta, Yellow, & Black color map (Soft-Logik) */
int CHUNK_CMYK = ('C' << 24) + ('M' << 16) + ('Y' << 8) + 'K';
/** Color naming chunk (Soft-Logik) */
int CHUNK_CNAM = ('C' << 24) + ('N' << 16) + ('A' << 8) + 'M';
/** Line by line palette control information (Sebastiano Vigna) */
int CHUNK_PCHG = ('P' << 24) + ('C' << 16) + ('H' << 8) + 'G';
/** A mini duplicate ILBM used for preview (Gary Bonham) */
int CHUNK_PRVW = ('P' << 24) + ('R' << 16) + ('V' << 8) + 'W';
/** eXtended BitMap Information (Soft-Logik) */
int CHUNK_XBMI = ('X' << 24) + ('B' << 16) + ('M' << 8) + 'I';
/** Newtek Dynamic Ham color chunk */
int CHUNK_CTBL = ('C' << 24) + ('T' << 16) + ('B' << 8) + 'L';
/** Newtek Dynamic Ham chunk */
int CHUNK_DYCP = ('D' << 24) + ('Y' << 16) + ('C' << 8) + 'P';
/** Sliced HAM color chunk */
int CHUNK_SHAM = ('S' << 24) + ('H' << 16) + ('A' << 8) + 'M';
/** ACBM body chunk */
int CHUNK_ABIT = ('A' << 24) + ('B' << 16) + ('I' << 8) + 'T';
/** unofficial direct color */
int CHUNK_DCOL = ('D' << 24) + ('C' << 16) + ('O' << 8) + 'L';
} }

View File

@ -52,6 +52,19 @@ abstract class IFFChunk {
abstract void writeChunk(DataOutput pOutput) throws IOException; abstract void writeChunk(DataOutput pOutput) throws IOException;
protected static void skipData(final DataInput pInput, final int chunkLength, final int dataReadSoFar) throws IOException {
int toSkip = chunkLength - dataReadSoFar;
while (toSkip > 0) {
toSkip -= pInput.skipBytes(toSkip);
}
// Read pad
if (chunkLength % 2 != 0) {
pInput.readByte();
}
}
public String toString() { public String toString() {
return IFFUtil.toChunkStr(chunkId) + " chunk (" + chunkLength + " bytes)"; return IFFUtil.toChunkStr(chunkId) + " chunk (" + chunkLength + " bytes)";
} }

View File

@ -28,6 +28,7 @@
package com.twelvemonkeys.imageio.plugins.iff; package com.twelvemonkeys.imageio.plugins.iff;
import com.twelvemonkeys.image.ResampleOp;
import com.twelvemonkeys.imageio.ImageReaderBase; import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream; import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil; import com.twelvemonkeys.imageio.util.IIOUtil;
@ -94,7 +95,9 @@ public class IFFImageReader extends ImageReaderBase {
// http://home.comcast.net/~erniew/lwsdk/docs/filefmts/ilbm.html // http://home.comcast.net/~erniew/lwsdk/docs/filefmts/ilbm.html
// http://www.fileformat.info/format/iff/spec/7866a9f0e53c42309af667c5da3bd426/view.htm // http://www.fileformat.info/format/iff/spec/7866a9f0e53c42309af667c5da3bd426/view.htm
// - Contains definitions of some "new" chunks, as well as alternative FORM types // - Contains definitions of some "new" chunks, as well as alternative FORM types
// http://amigan.1emu.net/reg/iff.html // http://amigan.1emu.net/index/iff.html
// TODO: Allow reading rasters for HAM6/HAM8 and multipalette images that are expanded to RGB (24 bit) during read.
private BMHDChunk header; private BMHDChunk header;
private CMAPChunk colorMap; private CMAPChunk colorMap;
@ -102,6 +105,7 @@ public class IFFImageReader extends ImageReaderBase {
@SuppressWarnings({"FieldCanBeLocal"}) @SuppressWarnings({"FieldCanBeLocal"})
private GRABChunk grab; private GRABChunk grab;
private CAMGChunk viewPort; private CAMGChunk viewPort;
private MultiPalette paletteChange;
private int formType; private int formType;
private long bodyStart; private long bodyStart;
@ -127,6 +131,7 @@ public class IFFImageReader extends ImageReaderBase {
protected void resetMembers() { protected void resetMembers() {
header = null; header = null;
colorMap = null; colorMap = null;
paletteChange = null;
body = null; body = null;
viewPort = null; viewPort = null;
formType = 0; formType = 0;
@ -144,7 +149,7 @@ public class IFFImageReader extends ImageReaderBase {
formType = imageInput.readInt(); formType = imageInput.readInt();
if (formType != IFF.TYPE_ILBM && formType != IFF.TYPE_PBM) { if (formType != IFF.TYPE_ILBM && formType != IFF.TYPE_PBM) {
throw new IIOException("Only IFF (FORM) type ILBM and PBM supported: " + IFFUtil.toChunkStr(formType)); throw new IIOException("Only IFF FORM types ILBM and PBM supported: " + IFFUtil.toChunkStr(formType));
} }
//System.out.println("IFF type FORM " + toChunkStr(type)); //System.out.println("IFF type FORM " + toChunkStr(type));
@ -200,6 +205,57 @@ public class IFFImageReader extends ImageReaderBase {
//System.out.println(viewPort); //System.out.println(viewPort);
break; break;
case IFF.CHUNK_PCHG:
if (paletteChange instanceof PCHGChunk) {
throw new IIOException("Multiple PCHG chunks not allowed");
}
PCHGChunk pchg = new PCHGChunk(length);
pchg.readChunk(imageInput);
// Always prefer PCHG style palette changes
paletteChange = pchg;
// System.out.println(paletteChange);
break;
case IFF.CHUNK_SHAM:
if (paletteChange instanceof SHAMChunk) {
throw new IIOException("Multiple SHAM chunks not allowed");
}
SHAMChunk sham = new SHAMChunk(length);
sham.readChunk(imageInput);
// NOTE: We prefer PHCG to SHAM style palette changes, if both are present
if (paletteChange == null) {
paletteChange = sham;
}
// System.out.println(paletteChange);
break;
case IFF.CHUNK_CTBL:
if (paletteChange instanceof CTBLChunk) {
throw new IIOException("Multiple CTBL chunks not allowed");
}
CTBLChunk ctbl = new CTBLChunk(length);
ctbl.readChunk(imageInput);
// NOTE: We prefer PHCG to ctbl style palette changes, if both are present
if (paletteChange == null) {
paletteChange = ctbl;
}
// System.out.println(paletteChange);
break;
case IFF.CHUNK_JUNK:
// Always skip junk chunks
IFFChunk.skipData(imageInput, length, 0);
break;
case IFF.CHUNK_BODY: case IFF.CHUNK_BODY:
if (body != null) { if (body != null) {
throw new IIOException("Multiple BODY chunks not allowed"); throw new IIOException("Multiple BODY chunks not allowed");
@ -209,16 +265,15 @@ public class IFFImageReader extends ImageReaderBase {
bodyStart = imageInput.getStreamPosition(); bodyStart = imageInput.getStreamPosition();
// NOTE: We don't read the body here, it's done later in the read(int, ImageReadParam) method // NOTE: We don't read the body here, it's done later in the read(int, ImageReadParam) method
// Done reading meta // Done reading meta
return; return;
default: default:
// TODO: We probably want to store anno chunks as Metadata // TODO: We probably want to store ANNO, TEXT, AUTH, COPY etc chunks as Metadata
// ANNO, DEST, SPRT and more // SHAM, ANNO, DEST, SPRT and more
IFFChunk generic = new GenericChunk(chunkId, length); IFFChunk generic = new GenericChunk(chunkId, length);
generic.readChunk(imageInput); generic.readChunk(imageInput);
//System.out.println(generic); // System.out.println(generic);
break; break;
} }
} }
@ -297,7 +352,7 @@ public class IFFImageReader extends ImageReaderBase {
case 8: case 8:
// 8 bit // 8 bit
// May be HAM8 // May be HAM8
if (!isHAM()) { if (!isConvertToRGB()) {
if (colorMap != null) { if (colorMap != null) {
IndexColorModel cm = colorMap.getIndexColorModel(); IndexColorModel cm = colorMap.getIndexColorModel();
specifier = IndexedImageTypeSpecifier.createFromIndexColorModel(cm); specifier = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
@ -323,6 +378,10 @@ public class IFFImageReader extends ImageReaderBase {
return specifier; return specifier;
} }
private boolean isConvertToRGB() {
return isHAM() || isPCHG() || isSHAM();
}
private void readBody(final ImageReadParam pParam) throws IOException { private void readBody(final ImageReadParam pParam) throws IOException {
imageInput.seek(bodyStart); imageInput.seek(bodyStart);
byteRunStream = null; byteRunStream = null;
@ -361,7 +420,7 @@ public class IFFImageReader extends ImageReaderBase {
} }
// Ensure band settings from param are compatible with images // Ensure band settings from param are compatible with images
checkReadParamBandSettings(pParam, isHAM() ? 3 : 1, image.getSampleModel().getNumBands()); checkReadParamBandSettings(pParam, isConvertToRGB() ? 3 : 1, image.getSampleModel().getNumBands());
WritableRaster destination = image.getRaster(); WritableRaster destination = image.getRaster();
if (destinationBands != null || offset.x != 0 || offset.y != 0) { if (destinationBands != null || offset.x != 0 || offset.y != 0) {
@ -376,7 +435,7 @@ public class IFFImageReader extends ImageReaderBase {
ColorModel cm; ColorModel cm;
WritableRaster raster; WritableRaster raster;
if (isHAM()) { if (isConvertToRGB()) {
// TODO: If HAM6, use type USHORT_444_RGB or 2BYTE_444_RGB? // TODO: If HAM6, use type USHORT_444_RGB or 2BYTE_444_RGB?
// Or create a HAMColorModel, if at all possible? // Or create a HAMColorModel, if at all possible?
// TYPE_3BYTE_BGR // TYPE_3BYTE_BGR
@ -398,9 +457,8 @@ public class IFFImageReader extends ImageReaderBase {
final byte[] row = new byte[width * 8]; final byte[] row = new byte[width * 8];
//System.out.println("Data length: " + data.length); // System.out.println("PlaneData length: " + planeData.length);
//System.out.println("PlaneData length: " + planeData.length * planeData[0].length); // System.out.println("Row length: " + row.length);
//System.out.println("Row length: " + row.length);
final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData(); final byte[] data = ((DataBufferByte) raster.getDataBuffer()).getData();
@ -433,6 +491,12 @@ public class IFFImageReader extends ImageReaderBase {
if (isHAM()) { if (isHAM()) {
hamToRGB(row, pModel, data, 0); hamToRGB(row, pModel, data, 0);
} }
else if (isConvertToRGB()) {
multiPaletteToRGB(srcY, row, pModel, data, 0);
}
else if (isSHAM()) {
throw new UnsupportedOperationException("SHAM not supported (yet)");
}
else { else {
raster.setDataElements(0, 0, width, 1, row); raster.setDataElements(0, 0, width, 1, row);
} }
@ -449,7 +513,7 @@ public class IFFImageReader extends ImageReaderBase {
int dstY = (srcY - aoi.y) / sourceYSubsampling; int dstY = (srcY - aoi.y) / sourceYSubsampling;
// Handle non-converting raster as special case for performance // Handle non-converting raster as special case for performance
if (cm.isCompatibleRaster(destination)) { if (cm.isCompatibleRaster(destination)) {
// Rasters are compatible, just write to destinaiton // Rasters are compatible, just write to destination
if (sourceXSubsampling == 1) { if (sourceXSubsampling == 1) {
destination.setRect(offset.x, dstY, sourceRow); destination.setRect(offset.x, dstY, sourceRow);
// dataElements = raster.getDataElements(aoi.x, 0, aoi.width, 1, dataElements); // dataElements = raster.getDataElements(aoi.x, 0, aoi.width, 1, dataElements);
@ -609,10 +673,12 @@ public class IFFImageReader extends ImageReaderBase {
switch (header.compressionType) { switch (header.compressionType) {
case BMHDChunk.COMPRESSION_NONE: case BMHDChunk.COMPRESSION_NONE:
pInput.readFully(pData, pOffset, pPlaneWidth); pInput.readFully(pData, pOffset, pPlaneWidth);
// Uncompressed rows must have even number of bytes // Uncompressed rows must have even number of bytes
if ((header.bitplanes * pPlaneWidth) % 2 != 0) { if ((header.bitplanes * pPlaneWidth) % 2 != 0) {
pInput.readByte(); pInput.readByte();
} }
break; break;
case BMHDChunk.COMPRESSION_BYTE_RUN: case BMHDChunk.COMPRESSION_BYTE_RUN:
@ -630,6 +696,7 @@ public class IFFImageReader extends ImageReaderBase {
) )
); );
} }
byteRunStream.readFully(pData, pOffset, pPlaneWidth); byteRunStream.readFully(pData, pOffset, pPlaneWidth);
break; break;
@ -638,8 +705,25 @@ public class IFFImageReader extends ImageReaderBase {
} }
} }
private void hamToRGB(final byte[] pIndexed, final IndexColorModel pModel, private void multiPaletteToRGB(final int srcY, final byte[] indexed, final IndexColorModel colorModel, final byte[] dest, final int destOffset) {
final byte[] pDest, final int pDestOffset) { final int width = header.width;
// TODO: Assure we have applied color change for all rows up until rowIndex, in case of source region/subsampling
ColorModel palette = paletteChange.getColorModel(colorModel, srcY, isLaced());
for (int x = 0; x < width; x++) {
int pixel = indexed[x] & 0xff;
int rgb = palette.getRGB(pixel);
int offset = (x * 3) + destOffset;
dest[2 + offset] = (byte) ((rgb >> 16) & 0xff);
dest[1 + offset] = (byte) ((rgb >> 8) & 0xff);
dest[ offset] = (byte) ( rgb & 0xff);
}
}
private void hamToRGB(final byte[] indexed, final IndexColorModel colorModel, final byte[] dest, final int destOffset) {
final int bits = header.bitplanes; final int bits = header.bitplanes;
final int width = header.width; final int width = header.width;
int lastRed = 0; int lastRed = 0;
@ -647,20 +731,18 @@ public class IFFImageReader extends ImageReaderBase {
int lastBlue = 0; int lastBlue = 0;
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
int pixel = pIndexed[x] & 0xff; int pixel = indexed[x] & 0xff;
//System.out.println("--> ham" + bits);
int paletteIndex = bits == 6 ? pixel & 0x0f : pixel & 0x3f; int paletteIndex = bits == 6 ? pixel & 0x0f : pixel & 0x3f;
int indexShift = bits == 6 ? 4 : 2; int indexShift = bits == 6 ? 4 : 2;
int colorMask = bits == 6 ? 0x0f : 0x03; int colorMask = bits == 6 ? 0x0f : 0x03;
//System.out.println("palette index=" + paletteIndex);
// Get Hold and Modify bits // Get Hold and Modify bits
switch ((pixel >> (8 - indexShift)) & 0x03) { switch ((pixel >> (8 - indexShift)) & 0x03) {
case 0x00:// HOLD case 0x00:// HOLD
lastRed = pModel.getRed(paletteIndex); lastRed = colorModel.getRed(paletteIndex);
lastGreen = pModel.getGreen(paletteIndex); lastGreen = colorModel.getGreen(paletteIndex);
lastBlue = pModel.getBlue(paletteIndex); lastBlue = colorModel.getBlue(paletteIndex);
break; break;
case 0x01:// MODIFY BLUE case 0x01:// MODIFY BLUE
lastBlue = (lastBlue & colorMask) | (paletteIndex << indexShift); lastBlue = (lastBlue & colorMask) | (paletteIndex << indexShift);
@ -672,22 +754,41 @@ public class IFFImageReader extends ImageReaderBase {
lastGreen = (lastGreen & colorMask) | (paletteIndex << indexShift); lastGreen = (lastGreen & colorMask) | (paletteIndex << indexShift);
break; break;
} }
int offset = (x * 3) + pDestOffset;
pDest[2 + offset] = (byte) lastRed; int offset = (x * 3) + destOffset;
pDest[1 + offset] = (byte) lastGreen; dest[2 + offset] = (byte) lastRed;
pDest[offset] = (byte) lastBlue; dest[1 + offset] = (byte) lastGreen;
dest[ offset] = (byte) lastBlue;
} }
} }
private boolean isSHAM() {
// TODO:
return false;
}
private boolean isPCHG() {
return paletteChange != null;
}
private boolean isHAM() { private boolean isHAM() {
return viewPort != null && viewPort.isHAM(); return viewPort != null && viewPort.isHAM();
} }
public boolean isLaced() {
return viewPort != null && viewPort.isLaced();
}
public static void main(String[] pArgs) throws IOException { public static void main(String[] pArgs) throws IOException {
ImageReader reader = new IFFImageReader(); ImageReader reader = new IFFImageReader();
// ImageInputStream input = ImageIO.createImageInputStream(new File(pArgs[0])); boolean scale = false;
ImageInputStream input = new BufferedImageInputStream(ImageIO.createImageInputStream(new File(pArgs[0]))); for (String arg : pArgs) {
if (arg.startsWith("-")) {
scale = true;
continue;
}
ImageInputStream input = new BufferedImageInputStream(ImageIO.createImageInputStream(new File(arg)));
boolean canRead = reader.getOriginatingProvider().canDecodeInput(input); boolean canRead = reader.getOriginatingProvider().canDecodeInput(input);
System.out.println("Can read: " + canRead); System.out.println("Can read: " + canRead);
@ -705,7 +806,14 @@ public class IFFImageReader extends ImageReaderBase {
BufferedImage image = reader.read(0, param); BufferedImage image = reader.read(0, param);
System.out.println("image = " + image); System.out.println("image = " + image);
showIt(image, pArgs[0]); if (scale) {
image = new ResampleOp(image.getWidth() / 2, image.getHeight(), ResampleOp.FILTER_LANCZOS).filter(image, null);
// image = ImageUtil.createResampled(image, image.getWidth(), image.getHeight() * 2, Image.SCALE_FAST);
}
showIt(image, arg);
}
} }
} }
} }

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2012, 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.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
/**
* MultiPalette
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: MultiPalette.java,v 1.0 30.03.12 15:22 haraldk Exp$
*/
interface MultiPalette {
ColorModel getColorModel(IndexColorModel colorModel, int rowIndex, boolean laced);
}

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2012, 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.
*/
/*
* Parts of this code is based on ilbmtoppm.c
*
* Copyright (C) 1989 by Jef Poskanzer.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. This software is provided "as is" without express or
* implied warranty.
*
* Multipalette-support by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
*/
package com.twelvemonkeys.imageio.plugins.iff;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
/**
* A mutable indexed color model.
* For use with images that exploits Amiga hardware to change the color
* lookup table between scan lines.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: MutableIndexColorModel.java,v 1.0 29.03.12 15:00 haraldk Exp$
*/
final class MutableIndexColorModel extends ColorModel {
final static int MP_REG_IGNORE = -1;
final int[] rgbs;
public MutableIndexColorModel(final IndexColorModel base) {
super(base.getPixelSize(), base.getComponentSize(), base.getColorSpace(), base.hasAlpha(), base.isAlphaPremultiplied(), base.getTransparency(), base.getTransferType());
this.rgbs = getRGBs(base);
}
private static int[] getRGBs(final IndexColorModel colorModel) {
int[] rgbs = new int[colorModel.getMapSize()];
colorModel.getRGBs(rgbs);
return rgbs;
}
public void adjustColorMap(final PaletteChange[] changes) {
for (int i = 0; i < changes.length; i++) {
int index = changes[i].index;
if (index >= rgbs.length) {
// TODO: Issue IIO warning
System.err.printf("warning - palette change register out of range\n");
System.err.printf(" change structure %d index=%d (max %d)\n", i, index, getMapSize() - 1);
System.err.printf(" ignoring it... colors might get messed up from here\n");
}
else if (index != MP_REG_IGNORE) {
updateRGB(index, ((changes[i].r & 0xff) << 16) | ((changes[i].g & 0xff) << 8) | (changes[i].b & 0xff));
}
}
}
@Override
public int getRGB(int pixel) {
return rgbs[pixel];
}
@Override
public int getRed(int pixel) {
return (rgbs[pixel] >> 16) & 0xff;
}
@Override
public int getGreen(int pixel) {
return (rgbs[pixel] >> 8) & 0xff;
}
@Override
public int getBlue(int pixel) {
return rgbs[pixel] & 0xff;
}
@Override
public int getAlpha(int pixel) {
return (rgbs[pixel] >> 24) & 0xff;
}
private void updateRGB(int index, int rgb) {
rgbs[index] = rgb;
}
public int getMapSize() {
return rgbs.length;
}
static class PaletteChange {
/* palette index to change */
public int index;
/* new colors for index */
public byte r;
public byte g;
public byte b;
}
}

View File

@ -0,0 +1,270 @@
/*
* Copyright (c) 2012, 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.
*/
/*
* Parts of this code is based on ilbmtoppm.c
*
* Copyright (C) 1989 by Jef Poskanzer.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. This software is provided "as is" without express or
* implied warranty.
*
* Multipalette-support by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
*/
package com.twelvemonkeys.imageio.plugins.iff;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.IOException;
/**
* PCHGChunk
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: PCHGChunk.java,v 1.0 27.03.12 13:02 haraldk Exp$
*/
final class PCHGChunk extends AbstractMultiPaletteChunk {
// NOTE: Values from ilbm2ppm.
final static int PCHG_COMP_NONE = 0;
final static int PCHG_COMP_HUFFMAN = 1;
/** Use SmallLineChanges */
final static int PCHGF_12BIT = 1; // NOTE: The beta spec refers to this as PHCGF_4BIT
/** Use BigLineChanges */
final static int PCHGF_32BIT = 2;
/** meaningful only if PCHG_32BIT is on: use the Alpha channel info */
final static int PCHGF_USE_ALPHA = 4;
private int startLine;
private int changedLines;
private int lineCount;
private int totalChanges;
private int minReg;
public PCHGChunk(int pChunkLength) {
super(IFF.CHUNK_PCHG, pChunkLength);
}
@Override
void readChunk(final DataInput pInput) throws IOException {
int compression = pInput.readUnsignedShort();
int flags = pInput.readUnsignedShort();
startLine = pInput.readShort();
lineCount = pInput.readUnsignedShort();
changedLines = pInput.readUnsignedShort();
minReg = pInput.readUnsignedShort();
int maxReg = pInput.readUnsignedShort();
int maxChanges = pInput.readUnsignedShort(); // We don't really care, as we're not limited by the Amiga display hardware
totalChanges = pInput.readInt();
// System.err.println("compression: " + compression);
// System.err.println("flags: " + Integer.toBinaryString(flags));
// System.err.println("startLine: " + startLine);
// System.err.println("lineCount: " + lineCount);
// System.err.println("changedLines: " + changedLines);
// System.err.println("minReg: " + minReg);
// System.err.println("maxReg: " + maxReg);
// System.err.println("maxChanges: " + maxChanges);
// System.err.println("totalChanges: " + totalChanges);
switch (compression) {
case PCHG_COMP_NONE:
byte[] data = new byte[chunkLength - 20];
pInput.readFully(data);
changes = new MutableIndexColorModel.PaletteChange[startLine + lineCount][];
if (startLine < 0) {
int numChanges = maxReg - minReg + 1;
initialChanges = new MutableIndexColorModel.PaletteChange[numChanges];
for (int i = 0; i < initialChanges.length; i++) {
initialChanges[i] = new MutableIndexColorModel.PaletteChange();
}
for (int i = 0; i < numChanges; i++) {
initialChanges[i].index = MutableIndexColorModel.MP_REG_IGNORE;
}
}
// TODO: Postpone conversion to actually needed
if ((flags & PCHGF_12BIT) != 0) {
convertSmallChanges(data);
}
else if ((flags & PCHGF_32BIT) != 0) {
System.err.println("BigLineChanges");
if ((flags & PCHGF_USE_ALPHA) != 0) {
System.err.println("Alpha should be used...");
}
// TODO: Implement 32 bit/alpha support
throw new UnsupportedOperationException("BigLineChanges not supported (yet)");
}
break;
case PCHG_COMP_HUFFMAN:
// TODO: Implement Huffman decoding
throw new IIOException("Huffman PCHG compression not supported");
default:
throw new IIOException("Unknown PCHG compression: " + compression);
}
}
private void convertSmallChanges(byte[] data) throws IIOException {
int thismask = 0;
int changeCount, reg;
int changeCount16, changeCount32;
int smallChange;
int totalchanges = 0;
int changedlines = changedLines;
int maskBytesLeft = 4 * ((lineCount + 31) / 32);
int maskIdx = 0;
int dataIdx = maskBytesLeft;
int dataBytesLeft = data.length - maskBytesLeft;
int bits = 0;
for (int row = startLine; changedlines != 0 && row < 0; row++) {
if (bits == 0) {
if (maskBytesLeft == 0) {
throw new IIOException("insufficient data in line mask");
}
thismask = data[maskIdx++];
--maskBytesLeft;
bits = 8;
}
if ((thismask & (1 << 7)) != 0) {
if (dataBytesLeft < 2) {
throw new IIOException("insufficient data in SmallLineChanges structures: " + dataBytesLeft);
}
changeCount16 = data[dataIdx++];
changeCount32 = data[dataIdx++];
dataBytesLeft -= 2;
changeCount = changeCount16 + changeCount32;
for (int i = 0; i < changeCount; i++) {
if (totalchanges >= this.totalChanges) {
throw new IIOException("insufficient data in SmallLineChanges structures (changeCount): " + totalchanges);
}
if (dataBytesLeft < 2) {
throw new IIOException("insufficient data in SmallLineChanges structures: " + dataBytesLeft);
}
smallChange = toShort(data, dataIdx);
dataIdx += 2;
dataBytesLeft -= 2;
reg = ((smallChange & 0xf000) >> 12) + (i >= changeCount16 ? 16 : 0);
initialChanges[reg - minReg].index = reg;
initialChanges[reg - minReg].r = (byte) (((smallChange & 0x0f00) >> 8) * FACTOR_4BIT);
initialChanges[reg - minReg].g = (byte) (((smallChange & 0x00f0) >> 4) * FACTOR_4BIT);
initialChanges[reg - minReg].b = (byte) (((smallChange & 0x000f) ) * FACTOR_4BIT);
++totalchanges;
}
--changedlines;
}
thismask <<= 1;
bits--;
}
for (int row = startLine; changedlines != 0 && row < this.changes.length; row++) {
if (bits == 0) {
if (maskBytesLeft == 0) {
throw new IIOException("insufficient data in line mask");
}
thismask = data[maskIdx++];
--maskBytesLeft;
bits = 8;
}
if ((thismask & (1 << 7)) != 0) {
if (dataBytesLeft < 2) {
throw new IIOException("insufficient data in SmallLineChanges structures: " + dataBytesLeft);
}
changeCount16 = data[dataIdx++];
changeCount32 = data[dataIdx++];
dataBytesLeft -= 2;
changeCount = changeCount16 + changeCount32;
changes[row] = new MutableIndexColorModel.PaletteChange[changeCount];
for (int i = 0; i < changeCount; i++) {
changes[row][i] = new MutableIndexColorModel.PaletteChange();
}
for (int i = 0; i < changeCount; i++) {
if (totalchanges >= this.totalChanges) {
throw new IIOException("insufficient data in SmallLineChanges structures");
}
if (dataBytesLeft < 2) {
throw new IIOException("insufficient data in SmallLineChanges structures");
}
smallChange = toShort(data, dataIdx);
dataIdx += 2;
dataBytesLeft -= 2;
reg = ((smallChange & 0xf000) >> 12) + (i >= changeCount16 ? 16 : 0);
changes[row][i].index = reg;
changes[row][i].r = (byte) (((smallChange & 0x0f00) >> 8) * FACTOR_4BIT);
changes[row][i].g = (byte) (((smallChange & 0x00f0) >> 4) * FACTOR_4BIT);
changes[row][i].b = (byte) (((smallChange & 0x000f) ) * FACTOR_4BIT);
++totalchanges;
}
--changedlines;
}
thismask <<= 1;
bits--;
}
if (totalchanges != this.totalChanges) {
// TODO: Issue IIO warning
System.err.printf("warning - got %d change structures, chunk header reports %d", totalchanges, this.totalChanges);
}
}
// TODO: Util method
private static short toShort(byte[] bytes, int idx) {
return (short) ((bytes[idx] & 0xff) << 8 | (bytes[idx + 1] & 0xff));
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2012, 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;
/**
* SHAMChunk
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: SHAMChunk.java,v 1.0 30.03.12 14:53 haraldk Exp$
*/
final class SHAMChunk extends AbstractMultiPaletteChunk {
protected SHAMChunk(int pChunkLength) {
super(IFF.CHUNK_SHAM, pChunkLength);
}
@Override
protected boolean skipLaced() {
return true;
}
}

View File

@ -52,14 +52,18 @@ public class IFFImageReaderTest extends ImageReaderAbstractTestCase<IFFImageRead
new TestData(getClassLoaderResource("/iff/survivor.iff"), new Dimension(800, 600)), // 24 bit 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) // 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 new TestData(getClassLoaderResource("/iff/A4000T_HAM6.IFF"), new Dimension(320, 512)), // ham6
// HAM8 - Ok (PackBits decoder chokes on padding byte) // HAM8 - Ok
new TestData(getClassLoaderResource("/iff/A4000T_HAM8.IFF"), new Dimension(628, 512)), // ham8 new TestData(getClassLoaderResource("/iff/A4000T_HAM8.IFF"), new Dimension(628, 512)), // ham8
// 8 color indexed - Ok // 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 new TestData(getClassLoaderResource("/iff/AmigaAmiga.iff"), new Dimension(200, 150)), // 8 color
// Ok (PackBits decoder chokes on padding byte) // HAM6 - Ok
new TestData(getClassLoaderResource("/iff/Abyss.iff"), new Dimension(320, 400)) new TestData(getClassLoaderResource("/iff/Abyss.iff"), new Dimension(320, 400)),
// 256 color indexed - Ok
new TestData(getClassLoaderResource("/iff/IKKEGOD.iff"), new Dimension(640, 256)),
// 16 color indexed, multi palette (PCHG) - Ok
new TestData(getClassLoaderResource("/iff/Manhattan.PCHG"), new Dimension(704, 440)),
// 16 color indexed, multi palette (PCHG + SHAM) - Ok
new TestData(getClassLoaderResource("/iff/Manhattan.PCHG"), new Dimension(704, 440))
); );
} }

Binary file not shown.

View File

@ -0,0 +1,5 @@
The image files in this folder may contain copyrighted artwork. However, I believe that using them for test purposes
(without actually displaying the artwork) must be considered fair use.
If you disagree for any reason, please send me a note, and I will remove your image from the distribution.
-- harald.kuhr@gmail.com