mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 11:05:29 -04:00
TMI-PCX: Initial commit.
This commit is contained in:
parent
74637105b8
commit
5313ffd6fc
25
imageio/imageio-pcx/license.txt
Executable file
25
imageio/imageio-pcx/license.txt
Executable file
@ -0,0 +1,25 @@
|
||||
Copyright (c) 2014, Harald Kuhr
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name "TwelveMonkeys" nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
imageio/imageio-pcx/pom.xml
Executable file
28
imageio/imageio-pcx/pom.xml
Executable file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>imageio-pcx</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: PCX plugin</name>
|
||||
<description>
|
||||
ImageIO plugin for ZSoft Paintbrush Format (PCX)
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<classifier>tests</classifier>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,11 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dcx;
|
||||
|
||||
/**
|
||||
* The DXC file format, is just a small header before a number of PCX streams.
|
||||
* Mostly used as a FAX format.
|
||||
*
|
||||
* @see <a href="http://www.fileformat.info/format/pcx/egff.htm#PCX-DMYID.3.8">[PCX] Related File Formats</a>
|
||||
*/
|
||||
interface DCX {
|
||||
int MAGIC = 0x3ADE68B1;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dcx;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
final class DCXHeader {
|
||||
private final int[] offsetTable;
|
||||
|
||||
DCXHeader(final int[] offsetTable) {
|
||||
this.offsetTable = offsetTable;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return offsetTable.length;
|
||||
}
|
||||
|
||||
public long getOffset(final int index) {
|
||||
return (0xffffffffL & offsetTable[index]);
|
||||
}
|
||||
|
||||
public static DCXHeader read(final ImageInputStream imageInput) throws IOException {
|
||||
// typedef struct _DcxHeader
|
||||
// {
|
||||
// DWORD Id; /* DCX Id number */
|
||||
// DWORD PageTable[1024]; /* Image offsets */
|
||||
// } DCXHEAD;
|
||||
|
||||
int magic = imageInput.readInt();
|
||||
if (magic != DCX.MAGIC) {
|
||||
throw new IIOException(String.format("Not a DCX file. Expected DCX magic %02x, read %02x", DCX.MAGIC, magic));
|
||||
}
|
||||
|
||||
int[] offsets = new int[1024];
|
||||
|
||||
int count = 0;
|
||||
do {
|
||||
offsets[count] = imageInput.readInt();
|
||||
count++;
|
||||
}
|
||||
while (offsets[count - 1] != 0 && count < offsets.length);
|
||||
|
||||
return new DCXHeader(count == offsets.length ? offsets : Arrays.copyOf(offsets, count));
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "DCXHeader{" +
|
||||
"offsetTable=" + Arrays.toString(offsetTable) +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dcx;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.event.IIOReadWarningListener;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.plugins.pcx.PCXImageReader;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
|
||||
public final class DCXImageReader extends ImageReaderBase {
|
||||
// TODO: Delegate listeners with correct index!
|
||||
|
||||
private DCXHeader header;
|
||||
|
||||
private PCXImageReader readerDelegate;
|
||||
private ProgressDelegator progressDelegator;
|
||||
|
||||
public DCXImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
readerDelegate = new PCXImageReader(provider);
|
||||
|
||||
progressDelegator = new ProgressDelegator();
|
||||
installListeners();
|
||||
}
|
||||
|
||||
private void installListeners() {
|
||||
readerDelegate.addIIOReadProgressListener(progressDelegator);
|
||||
readerDelegate.addIIOReadWarningListener(progressDelegator);
|
||||
}
|
||||
|
||||
@Override protected void resetMembers() {
|
||||
header = null;
|
||||
|
||||
readerDelegate.reset();
|
||||
installListeners();
|
||||
}
|
||||
|
||||
@Override public void dispose() {
|
||||
super.dispose();
|
||||
|
||||
readerDelegate.dispose();
|
||||
readerDelegate = null;
|
||||
}
|
||||
|
||||
@Override public int getWidth(final int imageIndex) throws IOException {
|
||||
initIndex(imageIndex);
|
||||
|
||||
return readerDelegate.getWidth(0);
|
||||
}
|
||||
|
||||
@Override public int getHeight(final int imageIndex) throws IOException {
|
||||
initIndex(imageIndex);
|
||||
|
||||
return readerDelegate.getHeight(0);
|
||||
}
|
||||
|
||||
@Override public ImageTypeSpecifier getRawImageType(final int imageIndex) throws IOException {
|
||||
initIndex(imageIndex);
|
||||
|
||||
return readerDelegate.getRawImageType(0);
|
||||
}
|
||||
|
||||
@Override public Iterator<ImageTypeSpecifier> getImageTypes(final int imageIndex) throws IOException {
|
||||
initIndex(imageIndex);
|
||||
|
||||
return readerDelegate.getImageTypes(0);
|
||||
}
|
||||
|
||||
@Override public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
|
||||
initIndex(imageIndex);
|
||||
|
||||
return readerDelegate.read(imageIndex, param);
|
||||
}
|
||||
|
||||
@Override public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||
initIndex(imageIndex);
|
||||
|
||||
return readerDelegate.getImageMetadata(0);
|
||||
}
|
||||
|
||||
@Override public synchronized void abort() {
|
||||
super.abort();
|
||||
readerDelegate.abort();
|
||||
}
|
||||
|
||||
@Override public int getNumImages(final boolean allowSearch) throws IOException {
|
||||
readHeader();
|
||||
|
||||
return header.getCount();
|
||||
}
|
||||
|
||||
private void initIndex(final int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
|
||||
imageInput.seek(header.getOffset(imageIndex));
|
||||
progressDelegator.index = imageIndex;
|
||||
readerDelegate.setInput(imageInput);
|
||||
}
|
||||
|
||||
private void readHeader() throws IOException {
|
||||
assertInput();
|
||||
|
||||
if (header == null) {
|
||||
imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
header = DCXHeader.read(imageInput);
|
||||
// System.err.println("header: " + header);
|
||||
imageInput.flushBefore(imageInput.getStreamPosition());
|
||||
}
|
||||
|
||||
imageInput.seek(imageInput.getFlushedPosition());
|
||||
}
|
||||
|
||||
private class ProgressDelegator extends ProgressListenerBase implements IIOReadWarningListener {
|
||||
private int index;
|
||||
|
||||
@Override
|
||||
public void imageComplete(ImageReader source) {
|
||||
processImageComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageProgress(ImageReader source, float percentageDone) {
|
||||
processImageProgress(percentageDone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void imageStarted(ImageReader source, int imageIndex) {
|
||||
processImageStarted(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readAborted(ImageReader source) {
|
||||
processReadAborted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sequenceComplete(ImageReader source) {
|
||||
processSequenceComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sequenceStarted(ImageReader source, int minIndex) {
|
||||
processSequenceStarted(index);
|
||||
}
|
||||
|
||||
public void warningOccurred(ImageReader source, String warning) {
|
||||
processWarningOccurred(warning);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
DCXImageReader reader = new DCXImageReader(null);
|
||||
|
||||
for (String arg : args) {
|
||||
File in = new File(arg);
|
||||
reader.setInput(ImageIO.createImageInputStream(in));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setDestinationType(reader.getImageTypes(0).next());
|
||||
// param.setSourceSubsampling(2, 3, 0, 0);
|
||||
// param.setSourceSubsampling(2, 1, 0, 0);
|
||||
//
|
||||
// int width = reader.getHdpi(0);
|
||||
// int height = reader.getVdpi(0);
|
||||
//
|
||||
// param.setSourceRegion(new Rectangle(width / 4, height / 4, width / 2, height / 2));
|
||||
// param.setSourceRegion(new Rectangle(width / 2, height / 2));
|
||||
// param.setSourceRegion(new Rectangle(width / 2, height / 2, width / 2, height / 2));
|
||||
|
||||
System.err.println("header: " + reader.header);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
System.err.println("image: " + image);
|
||||
|
||||
showIt(image, in.getName());
|
||||
|
||||
new XMLSerializer(System.out, System.getProperty("file.encoding"))
|
||||
.serialize(reader.getImageMetadata(0).getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName), false);
|
||||
|
||||
// File reference = new File(in.getParent() + "/../reference", in.getName().replaceAll("\\.p(a|b|g|p)m", ".png"));
|
||||
// if (reference.exists()) {
|
||||
// System.err.println("reference.getAbsolutePath(): " + reference.getAbsolutePath());
|
||||
// showIt(ImageIO.read(reference), reference.getName());
|
||||
// }
|
||||
|
||||
// break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package com.twelvemonkeys.imageio.plugins.dcx;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
|
||||
public final class DCXImageReaderSpi extends ImageReaderSpi {
|
||||
|
||||
/**
|
||||
* Creates a {@code DCXImageReaderSpi}.
|
||||
*/
|
||||
public DCXImageReaderSpi() {
|
||||
this(IIOUtil.getProviderInfo(DCXImageReaderSpi.class));
|
||||
}
|
||||
|
||||
private DCXImageReaderSpi(final ProviderInfo providerInfo) {
|
||||
super(
|
||||
providerInfo.getVendorName(),
|
||||
providerInfo.getVersion(),
|
||||
new String[]{
|
||||
"dcx",
|
||||
"DCX"
|
||||
},
|
||||
new String[]{"dcx"},
|
||||
new String[]{
|
||||
// No official IANA record exists
|
||||
"image/dcx",
|
||||
"image/x-dcx",
|
||||
},
|
||||
"com.twelvemkonkeys.imageio.plugins.dcx.DCXImageReader",
|
||||
new Class[] {ImageInputStream.class},
|
||||
null,
|
||||
true, // supports standard stream metadata
|
||||
null, null, // native stream format name and class
|
||||
null, null, // extra stream formats
|
||||
true, // supports standard image metadata
|
||||
null, null,
|
||||
null, null // extra image metadata formats
|
||||
);
|
||||
}
|
||||
|
||||
@Override public boolean canDecodeInput(final Object source) throws IOException {
|
||||
if (!(source instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) source;
|
||||
stream.mark();
|
||||
|
||||
try {
|
||||
ByteOrder originalByteOrder = stream.getByteOrder();
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
try {
|
||||
return stream.readInt() == DCX.MAGIC;
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalByteOrder);
|
||||
}
|
||||
}
|
||||
finally{
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public ImageReader createReaderInstance(final Object extension) throws IOException {
|
||||
return new DCXImageReader(this);
|
||||
}
|
||||
|
||||
@Override public String getDescription(final Locale locale) {
|
||||
return "Multi-page PCX fax document (DCX) image reader";
|
||||
}}
|
@ -0,0 +1,99 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||
|
||||
/**
|
||||
* 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$
|
||||
*/
|
||||
final class BitRotator {
|
||||
// TODO: Extract and merge with IFFUtil
|
||||
|
||||
/**
|
||||
* Creates a rotation table
|
||||
* @param n number of bits -1
|
||||
*
|
||||
* @return the rotation table
|
||||
*/
|
||||
private static 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
|
||||
};
|
||||
}
|
||||
|
||||
private static 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.IndexColorModel;
|
||||
|
||||
final class CGAColorModel {
|
||||
|
||||
// http://en.wikipedia.org/wiki/Color_Graphics_Adapter#Color_palette
|
||||
private static final int[] CGA_PALETTE = {
|
||||
// black, blue, green, cyan, red, magenta, brown, light gray
|
||||
0x000000, 0x0000aa, 0x00aa00, 0x00aaaa, 0xaa0000, 0xaa00aa, 0xaa5500, 0xaaaaaa,
|
||||
// gray, light b, light g, light c, light r, light m, yellow, white
|
||||
0x555555, 0x5555ff, 0x55ff55, 0x55ffff, 0xff5555, 0xff55ff, 0xffff55, 0xffffff
|
||||
};
|
||||
|
||||
static IndexColorModel create(final byte[] cgaMode, final int bitsPerPixel) {
|
||||
int[] cmap = new int[1 << bitsPerPixel];
|
||||
|
||||
byte byte0 = cgaMode[0];
|
||||
int background = (byte0 & 0xf0) >> 4;
|
||||
cmap[0] = CGA_PALETTE[background];
|
||||
|
||||
if (bitsPerPixel == 1) {
|
||||
// Monochrome
|
||||
cmap[1] = CGA_PALETTE[0];
|
||||
}
|
||||
else {
|
||||
// Configured palette
|
||||
byte byte3 = cgaMode[3];
|
||||
|
||||
System.err.printf("background: %d\n", background);
|
||||
System.err.printf("cgaMode: %02x\n", (byte3 & 0xff));
|
||||
System.err.printf("cgaMode: %d\n", (byte3 & 0x80) >> 7);
|
||||
System.err.printf("cgaMode: %d\n", (byte3 & 0x40) >> 6);
|
||||
System.err.printf("cgaMode: %d\n", (byte3 & 0x20) >> 5);
|
||||
|
||||
boolean colorBurstEnable = (byte3 & 0x80) == 0;
|
||||
boolean paletteValue = (byte3 & 0x40) != 0;
|
||||
boolean intensityValue = (byte3 & 0x20) != 0;
|
||||
|
||||
System.err.println("colorBurstEnable: " + colorBurstEnable);
|
||||
System.err.println("paletteValue: " + paletteValue);
|
||||
System.err.println("intensityValue: " + intensityValue);
|
||||
|
||||
// Set up the fixed part of the palette
|
||||
if (colorBurstEnable) {
|
||||
if (paletteValue) {
|
||||
if (intensityValue) {
|
||||
cmap[1] = CGA_PALETTE[11];
|
||||
cmap[2] = CGA_PALETTE[13];
|
||||
cmap[3] = CGA_PALETTE[15];
|
||||
} else {
|
||||
cmap[1] = CGA_PALETTE[3];
|
||||
cmap[2] = CGA_PALETTE[5];
|
||||
cmap[3] = CGA_PALETTE[7];
|
||||
}
|
||||
} else {
|
||||
if (intensityValue) {
|
||||
cmap[1] = CGA_PALETTE[10];
|
||||
cmap[2] = CGA_PALETTE[12];
|
||||
cmap[3] = CGA_PALETTE[14];
|
||||
} else {
|
||||
cmap[1] = CGA_PALETTE[2];
|
||||
cmap[2] = CGA_PALETTE[4];
|
||||
cmap[3] = CGA_PALETTE[6];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (intensityValue) {
|
||||
cmap[1] = CGA_PALETTE[11];
|
||||
cmap[2] = CGA_PALETTE[12];
|
||||
cmap[3] = CGA_PALETTE[15];
|
||||
} else {
|
||||
cmap[1] = CGA_PALETTE[4];
|
||||
cmap[2] = CGA_PALETTE[5];
|
||||
cmap[3] = CGA_PALETTE[7];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new IndexColorModel(bitsPerPixel, cmap.length, cmap, 0, false, -1, DataBuffer.TYPE_BYTE);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||
|
||||
interface PCX {
|
||||
byte MAGIC = 0x0A;
|
||||
|
||||
int HEADER_SIZE = 128;
|
||||
|
||||
byte VERSION_2_5 = 0;
|
||||
byte VERSION_2_8_PALETTE = 2;
|
||||
byte VERSION_2_8_NO_PALETTE = 3;
|
||||
byte VERSION_2_X_WINDOWS = 4;
|
||||
byte VERSION_3 = 5;
|
||||
|
||||
/** No compression, channels stored verbatim. */
|
||||
byte COMPRESSION_NONE = 0;
|
||||
/** Runlength encoed compression,
|
||||
* channels are prepended by one offset and length tables (one entry in each per scanline). */
|
||||
byte COMPRESSION_RLE = 1;
|
||||
|
||||
/** Color or BW. */
|
||||
int PALETTEINFO_COLOR = 1;
|
||||
/** Gray. */
|
||||
int PALETTEINFO_GRAY = 2;
|
||||
|
||||
/** Magic identifier for VGA palette. */
|
||||
byte VGA_PALETTE_MAGIC = 0x0c;
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
final class PCXHeader {
|
||||
private int version;
|
||||
private int compression;
|
||||
private int bitsPerPixel;
|
||||
private int width;
|
||||
private int height;
|
||||
private int hdpi;
|
||||
private int vdpi;
|
||||
private byte[] palette;
|
||||
private int channels;
|
||||
private int bytesPerLine;
|
||||
private int paletteInfo;
|
||||
private int hScreenSize;
|
||||
private int vScreenSize;
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getCompression() {
|
||||
return compression;
|
||||
}
|
||||
|
||||
public int getBitsPerPixel() {
|
||||
return bitsPerPixel;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public int getHdpi() {
|
||||
return hdpi;
|
||||
}
|
||||
|
||||
public int getVdpi() {
|
||||
return vdpi;
|
||||
}
|
||||
|
||||
public int getChannels() {
|
||||
return channels;
|
||||
}
|
||||
|
||||
public int getBytesPerLine() {
|
||||
return bytesPerLine;
|
||||
}
|
||||
|
||||
public int getPaletteInfo() {
|
||||
return paletteInfo;
|
||||
}
|
||||
|
||||
public IndexColorModel getEGAPalette() {
|
||||
// TODO: Figure out when/how to enable CGA palette... The test below isn't good enough.
|
||||
// if (channels == 1 && (bitsPerPixel == 1 || bitsPerPixel == 2)) {
|
||||
// return CGAColorModel.create(palette, bitsPerPixel);
|
||||
// }
|
||||
|
||||
int bits = channels * bitsPerPixel;
|
||||
return new IndexColorModel(bits, Math.min(16, 1 << bits), palette, 0, false);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "PCXHeader{" +
|
||||
"version=" + version +
|
||||
", compression=" + compression +
|
||||
", bitsPerPixel=" + bitsPerPixel +
|
||||
", width=" + width +
|
||||
", height=" + height +
|
||||
", hdpi=" + hdpi +
|
||||
", vdpi=" + vdpi +
|
||||
", channels=" + channels +
|
||||
", bytesPerLine=" + bytesPerLine +
|
||||
", paletteInfo=" + paletteInfo +
|
||||
", hScreenSize=" + hScreenSize +
|
||||
", vScreenSize=" + vScreenSize +
|
||||
", palette=" + Arrays.toString(palette) +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static PCXHeader read(final ImageInputStream imageInput) throws IOException {
|
||||
// typedef struct _PcxHeader
|
||||
// {
|
||||
// BYTE Identifier; /* PCX Id Number (Always 0x0A) */
|
||||
// BYTE Version; /* Version Number */
|
||||
// BYTE Encoding; /* Encoding Format */
|
||||
// BYTE BitsPerPixel; /* Bits per Pixel */
|
||||
// WORD XStart; /* Left of image */
|
||||
// WORD YStart; /* Top of Image */
|
||||
// WORD XEnd; /* Right of Image
|
||||
// WORD YEnd; /* Bottom of image */
|
||||
// WORD HorzRes; /* Horizontal Resolution */
|
||||
// WORD VertRes; /* Vertical Resolution */
|
||||
// BYTE Palette[48]; /* 16-Color EGA Palette */
|
||||
// BYTE Reserved1; /* Reserved (Always 0) */
|
||||
// BYTE NumBitPlanes; /* Number of Bit Planes */
|
||||
// WORD BytesPerLine; /* Bytes per Scan-line */
|
||||
// WORD PaletteType; /* Palette Type */
|
||||
// WORD HorzScreenSize; /* Horizontal Screen Size */
|
||||
// WORD VertScreenSize; /* Vertical Screen Size */
|
||||
// BYTE Reserved2[54]; /* Reserved (Always 0) */
|
||||
// } PCXHEAD;
|
||||
|
||||
byte magic = imageInput.readByte();
|
||||
if (magic != PCX.MAGIC) {
|
||||
throw new IIOException(String.format("Not a PCX image. Expected PCX magic %02x, read %02x", PCX.MAGIC, magic));
|
||||
}
|
||||
|
||||
PCXHeader header = new PCXHeader();
|
||||
|
||||
header.version = imageInput.readUnsignedByte();
|
||||
header.compression = imageInput.readUnsignedByte();
|
||||
header.bitsPerPixel = imageInput.readUnsignedByte();
|
||||
|
||||
int xStart = imageInput.readUnsignedShort();
|
||||
int yStart = imageInput.readUnsignedShort();
|
||||
header.width = imageInput.readUnsignedShort() - xStart + 1;
|
||||
header.height = imageInput.readUnsignedShort() - yStart + 1;
|
||||
|
||||
header.hdpi = imageInput.readUnsignedShort();
|
||||
header.vdpi = imageInput.readUnsignedShort();
|
||||
|
||||
byte[] palette = new byte[48];
|
||||
imageInput.readFully(palette); // 16 RGB triplets
|
||||
header.palette = palette;
|
||||
|
||||
imageInput.readUnsignedByte(); // Reserved, should be 0
|
||||
|
||||
header.channels = imageInput.readUnsignedByte();
|
||||
header.bytesPerLine = imageInput.readUnsignedShort(); // Must be even!
|
||||
|
||||
header.paletteInfo = imageInput.readUnsignedShort(); // 1 == Color/BW, 2 == Gray
|
||||
|
||||
header.hScreenSize = imageInput.readUnsignedShort();
|
||||
header.vScreenSize = imageInput.readUnsignedShort();
|
||||
|
||||
imageInput.skipBytes(PCX.HEADER_SIZE - imageInput.getStreamPosition());
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
@ -0,0 +1,425 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
|
||||
import com.twelvemonkeys.io.enc.DecoderStream;
|
||||
import com.twelvemonkeys.xml.XMLSerializer;
|
||||
|
||||
public final class PCXImageReader extends ImageReaderBase {
|
||||
/** 8 bit ImageTypeSpecifer used for reading bitplane images. */
|
||||
private static final ImageTypeSpecifier GRAYSCALE = ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false);
|
||||
|
||||
private PCXHeader header;
|
||||
private boolean readPalette;
|
||||
private IndexColorModel vgaPalette;
|
||||
|
||||
public PCXImageReader(final ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
header = null;
|
||||
readPalette = false;
|
||||
vgaPalette = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(final int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return header.getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(final int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return header.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(final int imageIndex) throws IOException {
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
|
||||
List<ImageTypeSpecifier> specifiers = new ArrayList<ImageTypeSpecifier>();
|
||||
|
||||
// TODO: Implement
|
||||
specifiers.add(rawType);
|
||||
|
||||
return specifiers.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageTypeSpecifier getRawImageType(final int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
int channels = header.getChannels();
|
||||
int paletteInfo = header.getPaletteInfo();
|
||||
ColorSpace cs = paletteInfo == PCX.PALETTEINFO_GRAY ? ColorSpace.getInstance(ColorSpace.CS_GRAY) : ColorSpace.getInstance(ColorSpace.CS_sRGB);
|
||||
|
||||
switch (header.getBitsPerPixel()) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
return IndexedImageTypeSpecifier.createFromIndexColorModel(header.getEGAPalette());
|
||||
case 8:
|
||||
// We may have IndexColorModel here for 1 channel images
|
||||
if (channels == 1 && paletteInfo != PCX.PALETTEINFO_GRAY) {
|
||||
IndexColorModel palette = getVGAPalette();
|
||||
if (palette == null) {
|
||||
throw new IIOException("Expected VGA palette not found");
|
||||
}
|
||||
|
||||
return IndexedImageTypeSpecifier.createFromIndexColorModel(palette);
|
||||
}
|
||||
|
||||
// PCX has 1 or 3 channels for 8 bit gray or 24 bit RGB, will be validated by ImageTypeSpecifier
|
||||
return ImageTypeSpecifier.createBanded(cs, createIndices(channels, 1), createIndices(channels, 0), DataBuffer.TYPE_BYTE, false, false);
|
||||
case 24:
|
||||
// Some sources says this is possible... Untested.
|
||||
return ImageTypeSpecifier.createInterleaved(cs, createIndices(channels, 0), DataBuffer.TYPE_BYTE, false, false);
|
||||
default:
|
||||
throw new IIOException("Unknown number of bytes per pixel: " + header.getBitsPerPixel());
|
||||
}
|
||||
}
|
||||
|
||||
private int[] createIndices(final int bands, int increment) {
|
||||
int[] indices = new int[bands];
|
||||
|
||||
for (int i = 0; i < bands; i++) {
|
||||
indices[i] = i * increment;
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(final int imageIndex, final ImageReadParam param) throws IOException {
|
||||
Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex);
|
||||
ImageTypeSpecifier rawType = getRawImageType(imageIndex);
|
||||
|
||||
if (header.getPaletteInfo() != PCX.PALETTEINFO_COLOR && header.getPaletteInfo() != PCX.PALETTEINFO_GRAY) {
|
||||
processWarningOccurred(String.format("Unsupported color mode: %d, colors may look incorrect", header.getPaletteInfo()));
|
||||
}
|
||||
|
||||
int width = getWidth(imageIndex);
|
||||
int height = getHeight(imageIndex);
|
||||
|
||||
BufferedImage destination = getDestination(param, imageTypes, width, height);
|
||||
|
||||
Rectangle srcRegion = new Rectangle();
|
||||
Rectangle destRegion = new Rectangle();
|
||||
computeRegions(param, width, height, destination, srcRegion, destRegion);
|
||||
|
||||
WritableRaster destRaster = clipToRect(destination.getRaster(), destRegion, param != null ? param.getDestinationBands() : null);
|
||||
checkReadParamBandSettings(param, rawType.getNumBands(), destRaster.getNumBands());
|
||||
|
||||
int compression = header.getCompression();
|
||||
|
||||
// Wrap input (COMPRESSION_RLE is really the only value allowed)
|
||||
DataInput input = compression == PCX.COMPRESSION_RLE
|
||||
? new DataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(imageInput), new RLEDecoder()))
|
||||
: imageInput;
|
||||
|
||||
int xSub = param != null ? param.getSourceXSubsampling() : 1;
|
||||
int ySub = param != null ? param.getSourceYSubsampling() : 1;
|
||||
|
||||
processImageStarted(imageIndex);
|
||||
|
||||
if (rawType.getColorModel() instanceof IndexColorModel && header.getChannels() > 1) {
|
||||
// Bit planes!
|
||||
// Create raster from a default 8 bit layout
|
||||
WritableRaster rowRaster = GRAYSCALE.createBufferedImage(header.getWidth(), 1).getRaster();
|
||||
|
||||
// Clip to source region
|
||||
Raster clippedRow = clipRowToRect(rowRaster, srcRegion,
|
||||
param != null ? param.getSourceBands() : null,
|
||||
param != null ? param.getSourceXSubsampling() : 1);
|
||||
|
||||
int planeWidth = header.getBytesPerLine();
|
||||
byte[] planeData = new byte[planeWidth * 8];
|
||||
|
||||
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
switch (header.getBitsPerPixel()) {
|
||||
case 1:
|
||||
readRowByte(input, srcRegion, xSub, ySub, planeData, 0, planeWidth * header.getChannels(), destRaster, clippedRow, y);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
int pixelPos = 0;
|
||||
for (int planePos = 0; planePos < planeWidth; planePos++) {
|
||||
BitRotator.bitRotateCW(planeData, planePos, planeWidth, rowDataByte, pixelPos, 1);
|
||||
pixelPos += 8;
|
||||
}
|
||||
|
||||
processImageProgress(100f * y / height);
|
||||
|
||||
if (y < srcRegion.y) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Can't use width here, as we need to take bytesPerLine into account, and re-create a width based on this
|
||||
int rowWidth = (header.getBytesPerLine() * 8) / header.getBitsPerPixel();
|
||||
WritableRaster rowRaster = rawType.createBufferedImage(rowWidth, 1).getRaster();
|
||||
|
||||
// Clip to source region
|
||||
Raster clippedRow = clipRowToRect(rowRaster, srcRegion,
|
||||
param != null ? param.getSourceBands() : null,
|
||||
param != null ? param.getSourceXSubsampling() : 1);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int c = 0; c < header.getChannels(); c++) {
|
||||
WritableRaster destChannel = destRaster.createWritableChild(destRaster.getMinX(), destRaster.getMinY(), destRaster.getWidth(), destRaster.getHeight(), 0, 0, new int[] {c});
|
||||
Raster srcChannel = clippedRow.createChild(clippedRow.getMinX(), 0, clippedRow.getWidth(), 1, 0, 0, new int[] {c});
|
||||
|
||||
switch (header.getBitsPerPixel()) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData(c);
|
||||
readRowByte(input, srcRegion, xSub, ySub, rowDataByte, 0, rowDataByte.length, destChannel, srcChannel, y);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
processImageProgress(100f * y / height * c / header.getChannels());
|
||||
|
||||
if (y < srcRegion.y) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processImageComplete();
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
private void readRowByte(final DataInput input,
|
||||
Rectangle srcRegion,
|
||||
int xSub,
|
||||
int ySub,
|
||||
byte[] rowDataByte, final int off, final int length,
|
||||
WritableRaster destChannel,
|
||||
Raster srcChannel,
|
||||
int y) throws IOException {
|
||||
// If subsampled or outside source region, skip entire row
|
||||
if (y % ySub != 0 || y < srcRegion.y || y >= srcRegion.y + srcRegion.height) {
|
||||
input.skipBytes(length);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
input.readFully(rowDataByte, off, length);
|
||||
|
||||
// Subsample horizontal
|
||||
if (xSub != 1) {
|
||||
for (int x = 0; x < srcRegion.width / xSub; x++) {
|
||||
rowDataByte[srcRegion.x + x] = rowDataByte[srcRegion.x + x * xSub];
|
||||
}
|
||||
}
|
||||
|
||||
int dstY = (y - srcRegion.y) / ySub;
|
||||
destChannel.setDataElements(0, dstY, srcChannel);
|
||||
}
|
||||
|
||||
private Raster clipRowToRect(final Raster raster, final Rectangle rect, final int[] bands, final int xSub) {
|
||||
if (rect.contains(raster.getMinX(), 0, raster.getWidth(), 1)
|
||||
&& xSub == 1
|
||||
&& bands == null /* TODO: Compare bands with that of raster */) {
|
||||
return raster;
|
||||
}
|
||||
|
||||
return raster.createChild(rect.x / xSub, 0, rect.width / xSub, 1, 0, 0, bands);
|
||||
}
|
||||
|
||||
private WritableRaster clipToRect(final WritableRaster raster, final Rectangle rect, final int[] bands) {
|
||||
if (rect.contains(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight())
|
||||
&& bands == null /* TODO: Compare bands with that of raster */) {
|
||||
return raster;
|
||||
}
|
||||
|
||||
return raster.createWritableChild(rect.x, rect.y, rect.width, rect.height, 0, 0, bands);
|
||||
}
|
||||
|
||||
private void readHeader() throws IOException {
|
||||
if (header == null) {
|
||||
imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
header = PCXHeader.read(imageInput);
|
||||
imageInput.flushBefore(imageInput.getStreamPosition());
|
||||
}
|
||||
|
||||
imageInput.seek(imageInput.getFlushedPosition());
|
||||
}
|
||||
|
||||
@Override public IIOMetadata getImageMetadata(final int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
readHeader();
|
||||
|
||||
return new PCXMetadata(header, getVGAPalette());
|
||||
}
|
||||
|
||||
private IndexColorModel getVGAPalette() throws IOException {
|
||||
if (!readPalette) {
|
||||
readHeader();
|
||||
|
||||
// Mark palette as read, to avoid further attempts
|
||||
readPalette = true;
|
||||
|
||||
// Wee can't simply skip to an offset, as the RLE compression makes the file size unpredictable
|
||||
skiptToEOF(imageInput);
|
||||
|
||||
// Seek backwards from EOF
|
||||
long paletteStart = imageInput.getStreamPosition() - 769;
|
||||
if (paletteStart <= imageInput.getFlushedPosition()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
imageInput.seek(paletteStart);
|
||||
|
||||
byte val = imageInput.readByte();
|
||||
|
||||
if (val == PCX.VGA_PALETTE_MAGIC) {
|
||||
byte[] palette = new byte[768]; // 256 * 3 for RGB
|
||||
imageInput.readFully(palette);
|
||||
|
||||
vgaPalette = new IndexColorModel(8, 256, palette, 0, false);
|
||||
|
||||
return vgaPalette;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return vgaPalette;
|
||||
}
|
||||
|
||||
// TODO: Candidate util method
|
||||
private static long skiptToEOF(final ImageInputStream stream) throws IOException {
|
||||
long length = stream.length();
|
||||
|
||||
if (length > 0) {
|
||||
// Known length, skip there and we're done.
|
||||
stream.seek(length);
|
||||
}
|
||||
else {
|
||||
// Otherwise, seek to EOF the hard way.
|
||||
// First, store stream position...
|
||||
long pos = stream.getStreamPosition();
|
||||
|
||||
// ...skip 1k blocks until we're passed EOF...
|
||||
while (stream.skipBytes(1024l) > 0) {
|
||||
if (stream.read() == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
pos = stream.getStreamPosition();
|
||||
}
|
||||
|
||||
// ...go back to last known pos...
|
||||
stream.seek(pos);
|
||||
|
||||
// ...finally seek until EOF one byte at a time. Done.
|
||||
while (stream.read() != -1) {
|
||||
}
|
||||
}
|
||||
|
||||
return stream.getStreamPosition();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
PCXImageReader reader = new PCXImageReader(null);
|
||||
|
||||
for (String arg : args) {
|
||||
File in = new File(arg);
|
||||
reader.setInput(ImageIO.createImageInputStream(in));
|
||||
|
||||
ImageReadParam param = reader.getDefaultReadParam();
|
||||
param.setDestinationType(reader.getImageTypes(0).next());
|
||||
// param.setSourceSubsampling(2, 3, 0, 0);
|
||||
// param.setSourceSubsampling(2, 1, 0, 0);
|
||||
//
|
||||
// int width = reader.getHdpi(0);
|
||||
// int height = reader.getVdpi(0);
|
||||
//
|
||||
// param.setSourceRegion(new Rectangle(width / 4, height / 4, width / 2, height / 2));
|
||||
// param.setSourceRegion(new Rectangle(width / 2, height / 2));
|
||||
// param.setSourceRegion(new Rectangle(width / 2, height / 2, width / 2, height / 2));
|
||||
|
||||
System.err.println("header: " + reader.header);
|
||||
|
||||
BufferedImage image = reader.read(0, param);
|
||||
|
||||
System.err.println("image: " + image);
|
||||
|
||||
showIt(image, in.getName());
|
||||
|
||||
new XMLSerializer(System.out, System.getProperty("file.encoding"))
|
||||
.serialize(reader.getImageMetadata(0).getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName), false);
|
||||
|
||||
// File reference = new File(in.getParent() + "/../reference", in.getName().replaceAll("\\.p(a|b|g|p)m", ".png"));
|
||||
// if (reference.exists()) {
|
||||
// System.err.println("reference.getAbsolutePath(): " + reference.getAbsolutePath());
|
||||
// showIt(ImageIO.read(reference), reference.getName());
|
||||
// }
|
||||
|
||||
// break;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ProviderInfo;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class PCXImageReaderSpi extends ImageReaderSpi {
|
||||
|
||||
/**
|
||||
* Creates a {@code PCXImageReaderSpi}.
|
||||
*/
|
||||
public PCXImageReaderSpi() {
|
||||
this(IIOUtil.getProviderInfo(PCXImageReaderSpi.class));
|
||||
}
|
||||
|
||||
private PCXImageReaderSpi(final ProviderInfo providerInfo) {
|
||||
super(
|
||||
providerInfo.getVendorName(),
|
||||
providerInfo.getVersion(),
|
||||
new String[]{
|
||||
"pcx",
|
||||
"PCX"
|
||||
},
|
||||
new String[]{"pcx"},
|
||||
new String[]{
|
||||
// No official IANA record exists
|
||||
"image/pcx",
|
||||
"image/x-pcx",
|
||||
},
|
||||
"com.twelvemkonkeys.imageio.plugins.pcx.PCXImageReader",
|
||||
new Class[] {ImageInputStream.class},
|
||||
null,
|
||||
true, // supports standard stream metadata
|
||||
null, null, // native stream format name and class
|
||||
null, null, // extra stream formats
|
||||
true, // supports standard image metadata
|
||||
null, null,
|
||||
null, null // extra image metadata formats
|
||||
);
|
||||
}
|
||||
|
||||
@Override public boolean canDecodeInput(final Object source) throws IOException {
|
||||
if (!(source instanceof ImageInputStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageInputStream stream = (ImageInputStream) source;
|
||||
|
||||
stream.mark();
|
||||
|
||||
try {
|
||||
byte magic = stream.readByte();
|
||||
|
||||
switch (magic) {
|
||||
case PCX.MAGIC:
|
||||
byte version = stream.readByte();
|
||||
|
||||
switch (version) {
|
||||
case PCX.VERSION_2_5:
|
||||
case PCX.VERSION_2_8_PALETTE:
|
||||
case PCX.VERSION_2_8_NO_PALETTE:
|
||||
case PCX.VERSION_2_X_WINDOWS:
|
||||
case PCX.VERSION_3:
|
||||
byte compression = stream.readByte();
|
||||
byte bpp = stream.readByte();
|
||||
|
||||
return (compression == PCX.COMPRESSION_NONE || compression == PCX.COMPRESSION_RLE) && (bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public ImageReader createReaderInstance(final Object extension) throws IOException {
|
||||
return new PCXImageReader(this);
|
||||
}
|
||||
|
||||
@Override public String getDescription(final Locale locale) {
|
||||
return "PC Paintbrush (PCX) image reader";
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,210 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.IndexColorModel;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
final class PCXMetadata extends IIOMetadata {
|
||||
// TODO: Clean up & extend AbstractMetadata (after moving from PSD -> Core)
|
||||
|
||||
private final PCXHeader header;
|
||||
private final IndexColorModel vgaPalette;
|
||||
|
||||
PCXMetadata(final PCXHeader header, final IndexColorModel vgaPalette) {
|
||||
this.header = header;
|
||||
this.vgaPalette = vgaPalette;
|
||||
|
||||
standardFormatSupported = true;
|
||||
}
|
||||
|
||||
@Override public boolean isReadOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public Node getAsTree(final String formatName) {
|
||||
if (formatName.equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
|
||||
return getStandardTree();
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported metadata format: " + formatName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void mergeTree(final String formatName, final Node root) {
|
||||
if (isReadOnly()) {
|
||||
throw new IllegalStateException("Metadata is read-only");
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void reset() {
|
||||
if (isReadOnly()) {
|
||||
throw new IllegalStateException("Metadata is read-only");
|
||||
}
|
||||
}
|
||||
|
||||
@Override protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
IndexColorModel palette = null;
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
switch (header.getBitsPerPixel()) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
palette = header.getEGAPalette();
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
case 8:
|
||||
// We may have IndexColorModel here for 1 channel images
|
||||
if (header.getChannels() == 1 && header.getPaletteInfo() != PCX.PALETTEINFO_GRAY) {
|
||||
palette = vgaPalette;
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
}
|
||||
if (header.getChannels() == 1) {
|
||||
csType.setAttribute("name", "GRAY");
|
||||
break;
|
||||
}
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
|
||||
case 24:
|
||||
// Some sources says this is possible... Untested.
|
||||
csType.setAttribute("name", "RGB");
|
||||
break;
|
||||
|
||||
default:
|
||||
csType.setAttribute("name", "Unknown");
|
||||
}
|
||||
|
||||
chroma.appendChild(csType);
|
||||
|
||||
if (palette != null) {
|
||||
IIOMetadataNode paletteNode = new IIOMetadataNode("Palette");
|
||||
chroma.appendChild(paletteNode);
|
||||
|
||||
for (int i = 0; i < palette.getMapSize(); i++) {
|
||||
IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
|
||||
paletteEntry.setAttribute("index", Integer.toString(i));
|
||||
|
||||
paletteEntry.setAttribute("red", Integer.toString(palette.getRed(i)));
|
||||
paletteEntry.setAttribute("green", Integer.toString(palette.getGreen(i)));
|
||||
paletteEntry.setAttribute("blue", Integer.toString(palette.getBlue(i)));
|
||||
|
||||
paletteNode.appendChild(paletteEntry);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Channels in chroma node should reflect channels in color model, not data! (see data node)
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
numChannels.setAttribute("value", Integer.toString(header.getChannels()));
|
||||
chroma.appendChild(numChannels);
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
blackIsZero.setAttribute("value", "TRUE");
|
||||
chroma.appendChild(blackIsZero);
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
// No compression
|
||||
|
||||
@Override protected IIOMetadataNode getStandardCompressionNode() {
|
||||
if (header.getCompression() != PCX.COMPRESSION_NONE) {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
compressionTypeName.setAttribute("value", header.getCompression() == PCX.COMPRESSION_RLE ? "RLE" : "Uknown");
|
||||
node.appendChild(compressionTypeName);
|
||||
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
lossless.setAttribute("value", "TRUE");
|
||||
node.appendChild(lossless);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||
|
||||
// Planar configuration only makes sense for multi-channel images
|
||||
if (header.getChannels() > 1) {
|
||||
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
planarConfiguration.setAttribute("value", "LineInterleaved");
|
||||
node.appendChild(planarConfiguration);
|
||||
}
|
||||
|
||||
// TODO: SampleFormat value = Index if colormapped/palette data
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
sampleFormat.setAttribute("value", "UnsignedIntegral");
|
||||
node.appendChild(sampleFormat);
|
||||
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
bitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(header.getBitsPerPixel())));
|
||||
node.appendChild(bitsPerSample);
|
||||
|
||||
IIOMetadataNode significantBitsPerSample = new IIOMetadataNode("SignificantBitsPerSample");
|
||||
significantBitsPerSample.setAttribute("value", createListValue(header.getChannels(), Integer.toString(header.getBitsPerPixel())));
|
||||
node.appendChild(significantBitsPerSample);
|
||||
|
||||
IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
|
||||
sampleMSB.setAttribute("value", createListValue(header.getChannels(), "0"));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private String createListValue(final int itemCount, final String... values) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (buffer.length() > 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
|
||||
buffer.append(values[i % values.length]);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Override protected IIOMetadataNode getStandardDimensionNode() {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
imageOrientation.setAttribute("value", "Normal");
|
||||
dimension.appendChild(imageOrientation);
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
// TODO: document node with version
|
||||
|
||||
// No text node
|
||||
|
||||
// No tiling
|
||||
|
||||
@Override protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
// NOTE: There doesn't seem to be any god way to determine transparency, other than by convention
|
||||
// 1 channel: Gray, 2 channel: Gray + Alpha, 3 channel: RGB, 4 channel: RGBA (hopefully never CMYK...)
|
||||
|
||||
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||
|
||||
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
||||
alpha.setAttribute("value", header.getChannels() == 1 || header.getChannels() == 3 ? "none" : "nonpremultiplied");
|
||||
transparency.appendChild(alpha);
|
||||
|
||||
return transparency;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||
|
||||
import com.twelvemonkeys.io.enc.Decoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
final class RLEDecoder implements Decoder {
|
||||
|
||||
static final int COMPRESSED_RUN_MASK = 0xc0;
|
||||
|
||||
// A rather strange and inefficient RLE encoding, but it probably made sense at the time...
|
||||
// Uses the upper two bits to flag if the next values are to be treated as a compressed run.
|
||||
// This means that any value above 0b11000000/0xc0/192 must be encoded as a compressed run,
|
||||
// even if this will make the output larger.
|
||||
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
|
||||
while (buffer.remaining() >= 64) {
|
||||
int val = stream.read();
|
||||
if (val < 0) {
|
||||
break; // EOF
|
||||
}
|
||||
|
||||
if ((val & COMPRESSED_RUN_MASK) == COMPRESSED_RUN_MASK) {
|
||||
int count = val & ~COMPRESSED_RUN_MASK;
|
||||
|
||||
int pixel = stream.read();
|
||||
if (pixel < 0) {
|
||||
break; // EOF
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
buffer.put((byte) pixel);
|
||||
}
|
||||
}
|
||||
else {
|
||||
buffer.put((byte) val);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.position();
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
com.twelvemonkeys.imageio.plugins.pcx.PCXImageReaderSpi
|
||||
com.twelvemonkeys.imageio.plugins.dcx.DCXImageReaderSpi
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.dcx;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
|
||||
/**
|
||||
* DCXImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: DCXImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
*/
|
||||
public class DCXImageReaderTest extends ImageReaderAbstractTestCase<DCXImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/dcx/input.dcx"), new Dimension(70, 46)) // RLE encoded RGB (the only sample I've found)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new DCXImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<DCXImageReader> getReaderClass() {
|
||||
return DCXImageReader.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DCXImageReader createReader() {
|
||||
return new DCXImageReader(createProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("DCX", "dcx");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("dcx");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList(
|
||||
"image/dcx", "image/x-dcx"
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.pcx;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
|
||||
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* PCXImageReaderTest
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: PCXImageReaderTest.java,v 1.0 03.07.14 22:28 haraldk Exp$
|
||||
*/
|
||||
public class PCXImageReaderTest extends ImageReaderAbstractTestCase<PCXImageReader> {
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return Arrays.asList(
|
||||
new TestData(getClassLoaderResource("/pcx/MARBLES.PCX"), new Dimension(1419, 1001)), // RLE encoded RGB
|
||||
// new TestData(getClassLoaderResource("/pcx/GMARBLES.PCX"), new Dimension(1419, 1001)) // RLE encoded gray (seems to be damanged, missing the last few scan lines)
|
||||
new TestData(getClassLoaderResource("/pcx/lena.pcx"), new Dimension(512, 512)), // RLE encoded RGB
|
||||
new TestData(getClassLoaderResource("/pcx/lena2.pcx"), new Dimension(512, 512)), // RLE encoded, 256 color indexed (8 bps/1 channel)
|
||||
new TestData(getClassLoaderResource("/pcx/lena3.pcx"), new Dimension(512, 512)), // RLE encoded, 16 color indexed (4 bps/1 channel)
|
||||
new TestData(getClassLoaderResource("/pcx/lena4.pcx"), new Dimension(512, 512)), // RLE encoded, 16 color indexed (1 bps/4 channels)
|
||||
new TestData(getClassLoaderResource("/pcx/lena5.pcx"), new Dimension(512, 512)), // RLE encoded, 256 color indexed (8 bps/1 channel)
|
||||
new TestData(getClassLoaderResource("/pcx/lena6.pcx"), new Dimension(512, 512)), // RLE encoded, 8 colorindexed (1 bps/3 channels)
|
||||
new TestData(getClassLoaderResource("/pcx/lena7.pcx"), new Dimension(512, 512)), // RLE encoded, 4 color indexed (1 bps/2 channels)
|
||||
new TestData(getClassLoaderResource("/pcx/lena8.pcx"), new Dimension(512, 512)), // RLE encoded, 4 color indexed (2 bps/1 channel)
|
||||
new TestData(getClassLoaderResource("/pcx/lena9.pcx"), new Dimension(512, 512)), // RLE encoded, 2 color indexed (1 bps/1 channel)
|
||||
new TestData(getClassLoaderResource("/pcx/lena10.pcx"), new Dimension(512, 512)), // RLE encoded, 16 color indexed (4 bps/1 channel) (uses only 8 colors)
|
||||
new TestData(getClassLoaderResource("/pcx/DARKSTAR.PCX"), new Dimension(88, 52)), // RLE encoded monochrome (1 bps/1 channel)
|
||||
// TODO: Get correct colors for CGA mode, see cga-pcx.txt (however, the text seems to be in error, the bits are not as described)
|
||||
new TestData(getClassLoaderResource("/pcx/CGA_BW.PCX"), new Dimension(640, 200)), // RLE encoded indexed (CGA mode)
|
||||
new TestData(getClassLoaderResource("/pcx/CGA_FSD.PCX"), new Dimension(320, 200)), // RLE encoded indexed (CGA mode)
|
||||
new TestData(getClassLoaderResource("/pcx/CGA_RGBI.PCX"), new Dimension(320, 200)), // RLE encoded indexed (CGA mode)
|
||||
new TestData(getClassLoaderResource("/pcx/CGA_TST1.PCX"), new Dimension(320, 200)) // RLE encoded indexed (CGA mode)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new PCXImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<PCXImageReader> getReaderClass() {
|
||||
return PCXImageReader.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PCXImageReader createReader() {
|
||||
return new PCXImageReader(createProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return Arrays.asList("PCX", "pcx");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return Arrays.asList("pcx");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return Arrays.asList(
|
||||
"image/pcx", "image/x-pcx"
|
||||
);
|
||||
}
|
||||
}
|
BIN
imageio/imageio-pcx/src/test/resources/dcx/input.dcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/dcx/input.dcx
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/CGA_BW.PCX
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/CGA_BW.PCX
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/CGA_FSD.PCX
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/CGA_FSD.PCX
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/CGA_RGBI.PCX
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/CGA_RGBI.PCX
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/CGA_TST1.PCX
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/CGA_TST1.PCX
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/DARKSTAR.PCX
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/DARKSTAR.PCX
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/GMARBLES.PCX
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/GMARBLES.PCX
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/MARBLES.PCX
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/MARBLES.PCX
Executable file
Binary file not shown.
33
imageio/imageio-pcx/src/test/resources/pcx/cga-pcx.txt
Executable file
33
imageio/imageio-pcx/src/test/resources/pcx/cga-pcx.txt
Executable file
@ -0,0 +1,33 @@
|
||||
From Jim Leonard (trixter -at- oldskool.org)
|
||||
|
||||
Here you go. I made these myself, and made sure to flex all of the
|
||||
PCX format's CGA attributes. These all load perfectly under *REAL*
|
||||
PC Paintbrush 4.0 in DOS on a CGA card, so if your decoder screws it
|
||||
up, it's the fault of your decoder!
|
||||
|
||||
They are:
|
||||
|
||||
CGA_FSD.PCX Standard lightcyan-lightmagenta-white palette (intensity
|
||||
bit set) with black background. When most people think of CGA, they're
|
||||
thinking of this.
|
||||
|
||||
0x00
|
||||
000 0 0000
|
||||
|
||||
CGA_TST1.PCX Different palette (intensite bit NOT set) and different
|
||||
background color. If you can read the text inside the box at the bottom,
|
||||
your decoder is shite :-)
|
||||
|
||||
0xf0
|
||||
111 1 0000
|
||||
|
||||
|
||||
CGA_RGBI.PCX Less commonly-used lightred-lightgreen-yellow palette
|
||||
(intensity bit set) with a blue background.
|
||||
|
||||
0xa4
|
||||
101 0 0100
|
||||
|
||||
|
||||
CGA_BW.PCX 640x200 B&W pic in case he didn't have a sample.
|
||||
Most PCX decoders handle this just fine.
|
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena.pcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena.pcx
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena10.pcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena10.pcx
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena2.pcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena2.pcx
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena3.pcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena3.pcx
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena4.pcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena4.pcx
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena5.pcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena5.pcx
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena6.pcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena6.pcx
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena7.pcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena7.pcx
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena8.pcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena8.pcx
Executable file
Binary file not shown.
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena9.pcx
Executable file
BIN
imageio/imageio-pcx/src/test/resources/pcx/lena9.pcx
Executable file
Binary file not shown.
@ -32,6 +32,7 @@
|
||||
<module>imageio-icns</module>
|
||||
<module>imageio-iff</module>
|
||||
<module>imageio-jpeg</module>
|
||||
<module>imageio-pcx</module>
|
||||
<module>imageio-pdf</module>
|
||||
<module>imageio-pict</module>
|
||||
<module>imageio-pnm</module>
|
||||
|
Loading…
x
Reference in New Issue
Block a user