Added test case for 64 color EHB (finally).

Rewrote EHB handling in CMAPChunk to fix bug.
Added test cases for 16 color indexed and 32 color indexed.
Removed obsolete test cases.
This commit is contained in:
Harald Kuhr 2012-04-03 16:58:04 +02:00
parent bf4ad6265a
commit da9b94bdf3
8 changed files with 144 additions and 88 deletions

View File

@ -37,6 +37,7 @@ import java.awt.image.WritableRaster;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
/**
* CMAPChunk
@ -53,45 +54,25 @@ final class CMAPChunk extends IFFChunk {
//
// typedef ColorRegister ColorMap[n]; /* size = 3n bytes */
byte[] reds;
byte[] greens;
byte[] blues;
boolean ehb;
final private BMHDChunk header;
final private CAMGChunk camg;
private IndexColorModel model;
protected CMAPChunk(int pChunkLength, BMHDChunk pHeader, CAMGChunk pCamg) {
protected CMAPChunk(final int pChunkLength) {
super(IFF.CHUNK_CMAP, pChunkLength);
header = pHeader;
camg = pCamg;
}
public CMAPChunk(IndexColorModel pModel) {
public CMAPChunk(final IndexColorModel pModel) {
super(IFF.CHUNK_CMAP, pModel.getMapSize() * 3);
model = pModel;
header = null;
camg = null;
}
void readChunk(DataInput pInput) throws IOException {
void readChunk(final DataInput pInput) throws IOException {
int numColors = chunkLength / 3;
int paletteSize = numColors;
boolean isEHB = camg != null && camg.isEHB();
if (isEHB) {
if (numColors == 32) {
paletteSize = 64;
}
else if (numColors != 64) {
throw new IIOException("Unknown number of colors for EHB: " + numColors);
}
}
reds = new byte[paletteSize];
reds = new byte[numColors];
greens = reds.clone();
blues = reds.clone();
@ -101,38 +82,21 @@ final class CMAPChunk extends IFFChunk {
blues[i] = pInput.readByte();
}
if (isEHB && numColors == 32) {
// Create the half-brite colors
for (int i = 0; i < numColors; i++) {
reds[i + numColors] = (byte) ((reds[i] & 0xff) / 2);
greens[i + numColors] = (byte) ((greens[i] & 0xff) / 2);
blues[i + numColors] = (byte) ((blues[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 (chunkLength % 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 = header.maskType == BMHDChunk.MASK_TRANSPARENT_COLOR ? header.transparentIndex : -1;
model = new InverseColorMapIndexColorModel(header.bitplanes, reds.length, reds, greens, blues, trans);
}
void writeChunk(DataOutput pOutput) throws IOException {
void writeChunk(final DataOutput pOutput) throws IOException {
pOutput.writeInt(chunkId);
pOutput.writeInt(chunkLength);
@ -153,15 +117,12 @@ final class CMAPChunk extends IFFChunk {
return super.toString() + " {colorMap=" + model + "}";
}
IndexColorModel getIndexColorModel() {
return model;
}
public BufferedImage createPaletteImage() {
BufferedImage createPaletteImage(final BMHDChunk header, boolean isEHB) throws IIOException {
// Create a 1 x colors.length image
IndexColorModel cm = getIndexColorModel();
IndexColorModel cm = getIndexColorModel(header, isEHB);
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);
@ -169,4 +130,38 @@ final class CMAPChunk extends IFFChunk {
return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
}
public IndexColorModel getIndexColorModel(final BMHDChunk header, boolean isEHB) throws IIOException {
if (model == null) {
int numColors = reds.length; // All arrays are same size
if (isEHB) {
if (numColors == 32) {
reds = Arrays.copyOf(reds, numColors * 2);
blues = Arrays.copyOf(blues, numColors * 2);
greens = Arrays.copyOf(greens, numColors * 2);
}
else if (numColors != 64) {
throw new IIOException("Unknown number of colors for EHB: " + numColors);
}
// Create the half-brite colors
// We do this regardless of the colors read, as the color map may contain trash values
for (int i = 0; i < 32; i++) {
reds[i + 32] = (byte) ((reds[i] & 0xff) / 2);
greens[i + 32] = (byte) ((greens[i] & 0xff) / 2);
blues[i + 32] = (byte) ((blues[i] & 0xff) / 2);
}
}
// 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 transparent = header.maskType == BMHDChunk.MASK_TRANSPARENT_COLOR ? header.transparentIndex : -1;
model = new InverseColorMapIndexColorModel(header.bitplanes, reds.length, reds, greens, blues, transparent);
}
return model;
}
}

View File

@ -183,7 +183,8 @@ public class IFFImageReader extends ImageReaderBase {
if (colorMap != null) {
throw new IIOException("Multiple CMAP chunks not allowed");
}
colorMap = new CMAPChunk(length, header, viewPort);
colorMap = new CMAPChunk(length);
colorMap.readChunk(imageInput);
//System.out.println(colorMap);
@ -217,7 +218,7 @@ public class IFFImageReader extends ImageReaderBase {
// Always prefer PCHG style palette changes
paletteChange = pchg;
// System.out.println(paletteChange);
// System.out.println(pchg);
break;
case IFF.CHUNK_SHAM:
@ -233,7 +234,7 @@ public class IFFImageReader extends ImageReaderBase {
paletteChange = sham;
}
// System.out.println(paletteChange);
// System.out.println(sham);
break;
case IFF.CHUNK_CTBL:
@ -249,7 +250,7 @@ public class IFFImageReader extends ImageReaderBase {
paletteChange = ctbl;
}
// System.out.println(paletteChange);
// System.out.println(ctbl);
break;
case IFF.CHUNK_JUNK:
@ -297,7 +298,7 @@ public class IFFImageReader extends ImageReaderBase {
//System.out.println(colorMap);
if (colorMap != null) {
//System.out.println("Creating palette!");
image = colorMap.createPaletteImage();
image = colorMap.createPaletteImage(header, isEHB());
}
}
@ -355,7 +356,7 @@ public class IFFImageReader extends ImageReaderBase {
// May be HAM8
if (!isConvertToRGB()) {
if (colorMap != null) {
IndexColorModel cm = colorMap.getIndexColorModel();
IndexColorModel cm = colorMap.getIndexColorModel(header, isEHB());
specifier = IndexedImageTypeSpecifier.createFromIndexColorModel(cm);
break;
}
@ -389,7 +390,7 @@ public class IFFImageReader extends ImageReaderBase {
// NOTE: colorMap may be null for 8 bit (gray), 24 bit or 32 bit only
if (colorMap != null) {
IndexColorModel cm = colorMap.getIndexColorModel();
IndexColorModel cm = colorMap.getIndexColorModel(header, isEHB());
readIndexed(pParam, imageInput, cm);
}
else {
@ -765,6 +766,10 @@ public class IFFImageReader extends ImageReaderBase {
return paletteChange != null;
}
private boolean isEHB() {
return viewPort != null && viewPort.isEHB();
}
private boolean isHAM() {
return viewPort != null && viewPort.isHAM();
}
@ -782,10 +787,15 @@ public class IFFImageReader extends ImageReaderBase {
scale = true;
continue;
}
ImageInputStream input = new BufferedImageInputStream(ImageIO.createImageInputStream(new File(arg)));
boolean canRead = reader.getOriginatingProvider().canDecodeInput(input);
System.out.println("Can read: " + canRead);
File file = new File(arg);
if (!file.isFile()) {
continue;
}
try {
ImageInputStream input = new BufferedImageInputStream(ImageIO.createImageInputStream(file));
boolean canRead = reader.getOriginatingProvider().canDecodeInput(input);
if (canRead) {
reader.setInput(input);
@ -807,7 +817,11 @@ public class IFFImageReader extends ImageReaderBase {
showIt(image, arg);
}
}
catch (IOException e) {
System.err.println("Error reading file: " + file);
e.printStackTrace();
}
}
}
}

View File

@ -29,12 +29,22 @@
package com.twelvemonkeys.imageio.plugins.iff;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import org.junit.Test;
import javax.imageio.ImageIO;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* IFFImageReaderTestCase
*
@ -46,21 +56,25 @@ public class IFFImageReaderTest extends ImageReaderAbstractTestCase<IFFImageRead
protected List<TestData> getTestData() {
return Arrays.asList(
// 32 bit - Ok
new TestData(getClassLoaderResource("/iff/test.iff"), new Dimension(300, 200)), // 32 bit
new TestData(getClassLoaderResource("/iff/test.iff"), new Dimension(300, 200)),
// 24 bit - Ok
new TestData(getClassLoaderResource("/iff/survivor.iff"), new Dimension(800, 600)), // 24 bit
new TestData(getClassLoaderResource("/iff/survivor.iff"), new Dimension(800, 600)),
// 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)),
// 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)),
// 8 color indexed - Ok
new TestData(getClassLoaderResource("/iff/AmigaAmiga.iff"), new Dimension(200, 150)), // 8 color
// HAM6 - Ok
new TestData(getClassLoaderResource("/iff/Abyss.iff"), new Dimension(320, 400)),
// PBM, indexed - Ok
new TestData(getClassLoaderResource("/iff/ASH.PBM"), new Dimension(320, 240)),
new TestData(getClassLoaderResource("/iff/AmigaAmiga.iff"), new Dimension(200, 150)),
// 16 color indexed - Ok
new TestData(getClassLoaderResource("/iff/Lion.iff"), new Dimension(704, 480)),
// 32 color indexed - Ok
new TestData(getClassLoaderResource("/iff/GoldPorsche.iff"), new Dimension(320, 200)),
// 64 color indexed EHB - Ok
new TestData(getClassLoaderResource("/iff/Bryce.iff"), new Dimension(320, 200)),
// 256 color indexed - Ok
new TestData(getClassLoaderResource("/iff/IKKEGOD.iff"), new Dimension(640, 256)),
// PBM, indexed - Ok
new TestData(getClassLoaderResource("/iff/ASH.PBM"), new Dimension(320, 240)),
// 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
@ -87,4 +101,37 @@ public class IFFImageReaderTest extends ImageReaderAbstractTestCase<IFFImageRead
protected List<String> getMIMETypes() {
return Arrays.asList("image/iff", "image/x-iff");
}
// Regression tests
@Test
public void testEHBColors() throws IOException {
IFFImageReader reader = createReader();
reader.setInput(ImageIO.createImageInputStream(getClassLoaderResource("/iff/Bryce.iff")));
BufferedImage image = reader.read(0);
assertEquals(BufferedImage.TYPE_BYTE_INDEXED, image.getType());
ColorModel colorModel = image.getColorModel();
assertNotNull(colorModel);
assertTrue(colorModel instanceof IndexColorModel);
IndexColorModel indexColorModel = (IndexColorModel) colorModel;
assertEquals(64, indexColorModel.getMapSize());
byte[] reds = new byte[indexColorModel.getMapSize()];
indexColorModel.getReds(reds);
byte[] blues = new byte[indexColorModel.getMapSize()];
indexColorModel.getBlues(blues);
byte[] greens = new byte[indexColorModel.getMapSize()];
indexColorModel.getGreens(greens);
for (int i = 0; i < 32; i++) {
// Make sure the color model is really EHB
assertEquals("red", (reds[i] & 0xff) / 2, reds[i + 32] & 0xff);
assertEquals("blue", (blues[i] & 0xff) / 2, blues[i + 32] & 0xff);
assertEquals("green", (greens[i] & 0xff) / 2, greens[i + 32] & 0xff);
}
}
}

Binary file not shown.

Binary file not shown.