X Window dump support.

This commit is contained in:
Harald Kuhr 2020-10-07 19:50:00 +02:00
parent 021aba1a98
commit f1810be10a
14 changed files with 725 additions and 0 deletions

27
imageio/imageio-xwd/license.txt Executable file
View File

@ -0,0 +1,27 @@
Copyright (c) 2020, 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 of the copyright holder 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 HOLDER 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.

27
imageio/imageio-xwd/pom.xml Executable file
View File

@ -0,0 +1,27 @@
<?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.7-SNAPSHOT</version>
</parent>
<artifactId>imageio-xwd</artifactId>
<name>TwelveMonkeys :: ImageIO :: XWD plugin</name>
<description>
ImageIO plugin for X11 Window Dump Format (XWD)
</description>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,20 @@
package com.twelvemonkeys.imageio.plugins.xwd;
interface X11 {
// int X10_HEADER_SIZE = 40;
// int X10_HEADER_VERSION = 0x06;
int X11_HEADER_SIZE = 100;
int X11_HEADER_VERSION = 0x07;
int PIXMAP_FORMAT_XYBITMAP = 0; // 1 bit format
int PIXMAP_FORMAT_XYPIXMAP = 1; // single plane bitmap
int PIXMAP_FORMAT_ZPIXMAP = 2; // multiple bitplanes
int VISUAL_CLASS_STATIC_GRAY = 0;
int VISUAL_CLASS_GRAY_SCALE = 1;
int VISUAL_CLASS_STATIC_COLOR = 2;
int VISUAL_CLASS_PSEUDO_COLOR = 3;
int VISUAL_CLASS_TRUE_COLOR = 4;
int VISUAL_CLASS_DIRECT_COLOR = 5;
}

View File

@ -0,0 +1,139 @@
package com.twelvemonkeys.imageio.plugins.xwd;
import com.twelvemonkeys.imageio.AbstractMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import java.nio.ByteOrder;
import static com.twelvemonkeys.lang.Validate.notNull;
final class XWDImageMetadata extends AbstractMetadata {
private final XWDX11Header header;
XWDImageMetadata(XWDX11Header header) {
super(true, null, null, null, null);
this.header = notNull(header, "header");
}
@Override
protected IIOMetadataNode getStandardChromaNode() {
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
IIOMetadataNode colorSpaceType = new IIOMetadataNode("ColorSpaceType");
switch (header.visualClass) {
case X11.VISUAL_CLASS_STATIC_GRAY:
case X11.VISUAL_CLASS_GRAY_SCALE:
colorSpaceType.setAttribute("name", "GRAY");
break;
default:
colorSpaceType.setAttribute("name", "RGB");
}
chroma.appendChild(colorSpaceType);
// TODO: Depending on visual class OR the presence of color mop!?
switch (header.visualClass) {
case X11.VISUAL_CLASS_STATIC_COLOR:
case X11.VISUAL_CLASS_PSEUDO_COLOR:
IIOMetadataNode palette = new IIOMetadataNode("Palette");
chroma.appendChild(palette);
for (int i = 0; i < header.colorMap.getMapSize(); i++) {
IIOMetadataNode paletteEntry = new IIOMetadataNode("PaletteEntry");
paletteEntry.setAttribute("index", Integer.toString(i));
paletteEntry.setAttribute("red", Integer.toString(header.colorMap.getRed(i)));
paletteEntry.setAttribute("green", Integer.toString(header.colorMap.getGreen(i)));
paletteEntry.setAttribute("blue", Integer.toString(header.colorMap.getBlue(i)));
palette.appendChild(paletteEntry);
}
break;
default:
// No palette
}
return chroma;
}
@Override
protected IIOMetadataNode getStandardDataNode() {
IIOMetadataNode node = new IIOMetadataNode("Data");
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
planarConfiguration.setAttribute("value", "PixelInterleaved");
node.appendChild(planarConfiguration);
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
node.appendChild(sampleFormat);
switch (header.visualClass) {
case X11.VISUAL_CLASS_STATIC_COLOR:
case X11.VISUAL_CLASS_PSEUDO_COLOR:
sampleFormat.setAttribute("value", "Index");
break;
default:
sampleFormat.setAttribute("value", "UnsignedIntegral");
break;
}
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
node.appendChild(bitsPerSample);
int numComponents = header.numComponents();
bitsPerSample.setAttribute("value", createListValue(numComponents, Integer.toString(header.bitsPerPixel / numComponents)));
// SampleMSB
if (header.bitsPerRGB < 8 && header.bitFillOrder == ByteOrder.LITTLE_ENDIAN) {
IIOMetadataNode sampleMSB = new IIOMetadataNode("SampleMSB");
node.appendChild(sampleMSB);
sampleMSB.setAttribute("value", createListValue(header.numComponents(), "0"));
}
return node;
}
@Override
protected IIOMetadataNode getStandardDocumentNode() {
IIOMetadataNode document = new IIOMetadataNode("Document");
// The only format we support is the X11 format, and it's version is 7.
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
document.appendChild(formatVersion);
formatVersion.setAttribute("value", "7");
return document;
}
@Override
protected IIOMetadataNode getStandardTextNode() {
IIOMetadataNode text = new IIOMetadataNode("Text");
if (header.windowName != null) {
IIOMetadataNode node = new IIOMetadataNode("TextEntry");
text.appendChild(node);
node.setAttribute("keyword", "DocumentName"); // For TIFF interop. :-)
node.setAttribute("value", header.windowName);
}
return text.hasChildNodes() ? text : null;
}
// TODO: Candidate superclass method!
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();
}
}

View File

@ -0,0 +1,225 @@
package com.twelvemonkeys.imageio.plugins.xwd;
import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import javax.imageio.IIOException;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.Collections;
import java.util.Iterator;
import static com.twelvemonkeys.imageio.util.IIOUtil.subsampleRow;
final class XWDImageReader extends ImageReaderBase {
// TODO: This table is also in c.t.i.p.tiff.ReverseInputStream, consider moving to common util?
// http://graphics.stanford.edu/~seander/bithacks.html
static final byte[] BIT_REVERSE_TABLE = {
0x00, (byte) 0x80, 0x40, (byte) 0xC0, 0x20, (byte) 0xA0, 0x60, (byte) 0xE0, 0x10, (byte) 0x90, 0x50, (byte) 0xD0, 0x30, (byte) 0xB0, 0x70, (byte) 0xF0,
0x08, (byte) 0x88, 0x48, (byte) 0xC8, 0x28, (byte) 0xA8, 0x68, (byte) 0xE8, 0x18, (byte) 0x98, 0x58, (byte) 0xD8, 0x38, (byte) 0xB8, 0x78, (byte) 0xF8,
0x04, (byte) 0x84, 0x44, (byte) 0xC4, 0x24, (byte) 0xA4, 0x64, (byte) 0xE4, 0x14, (byte) 0x94, 0x54, (byte) 0xD4, 0x34, (byte) 0xB4, 0x74, (byte) 0xF4,
0x0C, (byte) 0x8C, 0x4C, (byte) 0xCC, 0x2C, (byte) 0xAC, 0x6C, (byte) 0xEC, 0x1C, (byte) 0x9C, 0x5C, (byte) 0xDC, 0x3C, (byte) 0xBC, 0x7C, (byte) 0xFC,
0x02, (byte) 0x82, 0x42, (byte) 0xC2, 0x22, (byte) 0xA2, 0x62, (byte) 0xE2, 0x12, (byte) 0x92, 0x52, (byte) 0xD2, 0x32, (byte) 0xB2, 0x72, (byte) 0xF2,
0x0A, (byte) 0x8A, 0x4A, (byte) 0xCA, 0x2A, (byte) 0xAA, 0x6A, (byte) 0xEA, 0x1A, (byte) 0x9A, 0x5A, (byte) 0xDA, 0x3A, (byte) 0xBA, 0x7A, (byte) 0xFA,
0x06, (byte) 0x86, 0x46, (byte) 0xC6, 0x26, (byte) 0xA6, 0x66, (byte) 0xE6, 0x16, (byte) 0x96, 0x56, (byte) 0xD6, 0x36, (byte) 0xB6, 0x76, (byte) 0xF6,
0x0E, (byte) 0x8E, 0x4E, (byte) 0xCE, 0x2E, (byte) 0xAE, 0x6E, (byte) 0xEE, 0x1E, (byte) 0x9E, 0x5E, (byte) 0xDE, 0x3E, (byte) 0xBE, 0x7E, (byte) 0xFE,
0x01, (byte) 0x81, 0x41, (byte) 0xC1, 0x21, (byte) 0xA1, 0x61, (byte) 0xE1, 0x11, (byte) 0x91, 0x51, (byte) 0xD1, 0x31, (byte) 0xB1, 0x71, (byte) 0xF1,
0x09, (byte) 0x89, 0x49, (byte) 0xC9, 0x29, (byte) 0xA9, 0x69, (byte) 0xE9, 0x19, (byte) 0x99, 0x59, (byte) 0xD9, 0x39, (byte) 0xB9, 0x79, (byte) 0xF9,
0x05, (byte) 0x85, 0x45, (byte) 0xC5, 0x25, (byte) 0xA5, 0x65, (byte) 0xE5, 0x15, (byte) 0x95, 0x55, (byte) 0xD5, 0x35, (byte) 0xB5, 0x75, (byte) 0xF5,
0x0D, (byte) 0x8D, 0x4D, (byte) 0xCD, 0x2D, (byte) 0xAD, 0x6D, (byte) 0xED, 0x1D, (byte) 0x9D, 0x5D, (byte) 0xDD, 0x3D, (byte) 0xBD, 0x7D, (byte) 0xFD,
0x03, (byte) 0x83, 0x43, (byte) 0xC3, 0x23, (byte) 0xA3, 0x63, (byte) 0xE3, 0x13, (byte) 0x93, 0x53, (byte) 0xD3, 0x33, (byte) 0xB3, 0x73, (byte) 0xF3,
0x0B, (byte) 0x8B, 0x4B, (byte) 0xCB, 0x2B, (byte) 0xAB, 0x6B, (byte) 0xEB, 0x1B, (byte) 0x9B, 0x5B, (byte) 0xDB, 0x3B, (byte) 0xBB, 0x7B, (byte) 0xFB,
0x07, (byte) 0x87, 0x47, (byte) 0xC7, 0x27, (byte) 0xA7, 0x67, (byte) 0xE7, 0x17, (byte) 0x97, 0x57, (byte) 0xD7, 0x37, (byte) 0xB7, 0x77, (byte) 0xF7,
0x0F, (byte) 0x8F, 0x4F, (byte) 0xCF, 0x2F, (byte) 0xAF, 0x6F, (byte) 0xEF, 0x1F, (byte) 0x9F, 0x5F, (byte) 0xDF, 0x3F, (byte) 0xBF, 0x7F, (byte) 0xFF
};
private XWDX11Header header;
XWDImageReader(ImageReaderSpi provider) {
super(provider);
}
@Override
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
return new XWDImageMetadata(header);
}
@Override
public int getWidth(int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
return header.width;
}
@Override
public int getHeight(int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
return header.height;
}
private void readHeader() throws IOException {
assertInput();
if (header == null) {
header = XWDX11Header.read(imageInput);
}
}
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
ImageTypeSpecifier rawImageType = getRawImageType(imageIndex);
// ArrayList<ImageTypeSpecifier> specs = new ArrayList<>();
// TODO: Add TYPE_3BYTE_BGR & TYPE_4BYTE_ABGR + TYPE_INT_ types?
// if (rawImageType )
return Collections.singletonList(rawImageType).iterator();
}
@Override
public ImageTypeSpecifier getRawImageType(int imageIndex) throws IOException {
checkBounds(imageIndex);
readHeader();
switch (header.visualClass) {
case X11.VISUAL_CLASS_STATIC_GRAY:
case X11.VISUAL_CLASS_GRAY_SCALE:
return ImageTypeSpecifiers.createGrayscale(header.bitsPerPixel, DataBuffer.TYPE_BYTE, false);
case X11.VISUAL_CLASS_STATIC_COLOR:
case X11.VISUAL_CLASS_PSEUDO_COLOR:
return ImageTypeSpecifiers.createFromIndexColorModel(header.colorMap);
case X11.VISUAL_CLASS_TRUE_COLOR:
case X11.VISUAL_CLASS_DIRECT_COLOR:
int numComponents = header.numComponents();
return ImageTypeSpecifiers.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB), createBandArray(numComponents, header), DataBuffer.TYPE_BYTE, numComponents > 3, false);
default:
throw new IIOException(String.format("Unknown visual class: %d", header.visualClass));
}
}
private Raster createRowRaster(byte[] row) {
DataBuffer databuffer = new DataBufferByte(row, row.length);
switch (header.visualClass) {
case X11.VISUAL_CLASS_TRUE_COLOR:
case X11.VISUAL_CLASS_DIRECT_COLOR:
return Raster.createInterleavedRaster(databuffer, header.width, 1, header.bytesPerLine, header.bitsPerPixel / 8, createBandArray(header.numComponents(), header), null);
default:
return Raster.createPackedRaster(databuffer, header.width, 1, header.bitsPerPixel, null);
}
}
@Override
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
checkBounds(imageIndex);
readHeader();
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), header.width, header.height);
WritableRaster raster = destination.getRaster();
checkReadParamBandSettings(param, header.numComponents(), raster.getNumBands());
Rectangle srcRegion = new Rectangle();
Rectangle destRegion = new Rectangle();
computeRegions(param, header.width, header.height, destination, srcRegion, destRegion);
byte[] row = new byte[header.bytesPerLine];
Raster rowRaster = createRowRaster(row).createChild(srcRegion.x, 0, destRegion.width, 1, 0, 0, null);
final boolean reverseBits = header.bitsPerPixel < 8 && header.bitFillOrder == ByteOrder.LITTLE_ENDIAN;
final boolean clearAlpha = destination.getColorModel().hasAlpha();
final int xSub = param == null ? 1 : param.getSourceXSubsampling();
final int ySub = param == null ? 1 : param.getSourceYSubsampling();
imageInput.seek(header.pixelOffset);
processImageStarted(imageIndex);
for (int y = 0; y < srcRegion.y + srcRegion.height; y++) {
if (y < srcRegion.y || y % ySub != 0) {
// Skip row
imageInput.skipBytes(row.length);
continue;
}
imageInput.readFully(row);
if (reverseBits) {
for (int i = 0; i < row.length; i++) {
// TODO: Why do we have to inverse (XOR) as well?
row[i] = (byte) ~BIT_REVERSE_TABLE[row[i] & 0xff];
}
}
if (clearAlpha) {
// If we have alpha, inverse the alpha sample (or set it to opaque?)
// TODO: Where's the alpha! First or last depending on byte order?
for (int i = 0; i < row.length; i += rowRaster.getNumBands()) {
row[i] = (byte) ~row[i];
}
}
if (xSub != 1) {
// Horizontal subsampling
int samplesPerPixel = header.numComponents();
subsampleRow(row, srcRegion.x * samplesPerPixel, srcRegion.width, row, srcRegion.x * samplesPerPixel, samplesPerPixel, header.bitsPerRGB, xSub);
}
raster.setDataElements(0, (y - srcRegion.y) / ySub, rowRaster);
if (abortRequested()) {
processReadAborted();
break;
}
processImageProgress((y - srcRegion.y) * 100f / srcRegion.height);
}
processImageComplete();
return destination;
}
private int bytePos(int bitMask, int numBands) {
switch (bitMask) {
case 0xff:
return numBands - 1;
case 0xff00:
return numBands - 2;
case 0xff0000:
return numBands - 3;
case 0xff000000:
return numBands - 4;
default:
// We only support full byte masks for now
throw new IllegalArgumentException(String.format("Unsupported bitmask: 0x%08x", bitMask));
}
}
private int[] createBandArray(int numBands, final XWDX11Header header) {
int[] offsets = new int[numBands];
for (int i = 0; i < numBands; i++) {
offsets[i] = bytePos(header.masks[i], numBands);
}
return offsets;
}
@Override
protected void resetMembers() {
header = null;
}
}

View File

@ -0,0 +1,40 @@
package com.twelvemonkeys.imageio.plugins.xwd;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.Locale;
public final class XWDImageReaderSpi extends ImageReaderSpiBase {
public XWDImageReaderSpi() {
super(new XWDProviderInfo());
}
@Override
public boolean canDecodeInput(Object source) throws IOException {
if (!(source instanceof ImageInputStream)) {
return false;
}
ImageInputStream stream = (ImageInputStream) source;
stream.mark();
try {
return XWDX11Header.isX11(stream);
} finally {
stream.reset();
}
}
@Override
public XWDImageReader createReaderInstance(Object extension) {
return new XWDImageReader(this);
}
@Override
public String getDescription(Locale locale) {
return "X11 Window Dump Format (XWD) reader";
}
}

View File

@ -0,0 +1,28 @@
package com.twelvemonkeys.imageio.plugins.xwd;
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
final class XWDProviderInfo extends ReaderWriterProviderInfo {
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.xwd.debug"));
protected XWDProviderInfo() {
super(
XWDProviderInfo.class,
new String[] {
"XWD", "xwd"
},
new String[] {"xwd",},
new String[] {
// No official IANA record exists
"image/xwd", "image/x-xwd"
},
"com.twelvemonkeys.imageio.plugins.xwd.XWDImageReader",
new String[]{"com.twelvemonkeys.imageio.plugins.xwd.XWDImageReaderSpi"},
null,
null,
false, null, null, null, null,
true, null, null, null, null
);
}
}

View File

@ -0,0 +1,164 @@
package com.twelvemonkeys.imageio.plugins.xwd;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.io.DataInput;
import java.io.IOException;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
final class XWDX11Header {
final int width;
final int height;
final ByteOrder byteOrder;
final ByteOrder bitFillOrder;
final int bitsPerPixel;
final int bytesPerLine;
final int visualClass;
final int[] masks;
final int bitsPerRGB;
final IndexColorModel colorMap;
final String windowName;
final long pixelOffset;
private XWDX11Header(int width, int height, ByteOrder byteOrder, ByteOrder bitFillOrder,
int bitsPerPixel, int bytesPerLine, int visualClass,
int readMask, int greenMask, int blueMask, int bitsPerRGB,
final IndexColorModel colorMap, final String windowName, long pixelOffset) {
this.width = width;
this.height = height;
this.byteOrder = byteOrder;
this.bitFillOrder = bitFillOrder;
this.bitsPerPixel = bitsPerPixel;
this.bytesPerLine = bytesPerLine;
this.visualClass = visualClass;
this.masks = new int[] {readMask, greenMask, blueMask, ~(readMask | greenMask | blueMask)};
this.bitsPerRGB = bitsPerRGB;
this.colorMap = colorMap;
this.windowName = windowName;
this.pixelOffset = pixelOffset;
}
static boolean isX11(final DataInput input) throws IOException {
return input.readInt() >= X11.X11_HEADER_SIZE
&& input.readInt() == X11.X11_HEADER_VERSION;
}
static XWDX11Header read(final ImageInputStream input) throws IOException {
input.mark();
if (!isX11(input)) {
throw new IIOException("Not a valid X11 Window Dump");
}
input.reset();
long pos = input.getStreamPosition();
int length = input.readInt();
input.readInt();
int format = input.readInt(); // Pixel Format: 0 = 1 bit XYBitmap, 1 = single plane XYPixmap (gray?), 2 = two or more planes ZPixmap
int depth = input.readInt(); // Pixel Bit Depth: 1-32 (never seen above 24...)
int width = input.readInt();
int height = input.readInt();
int xOffset = input.readInt(); // Rarely used...
int byteOrder = input.readInt();
int unit = input.readInt();
int bitOrder = input.readInt(); // Same as TIFF FillOrder...
int pad = input.readInt(); // Can be inferred from bytePerLine?
int bitsPerPixel = input.readInt();
int bytePerLine = input.readInt(); // Scan line
int visualClass = input.readInt();
// TODO: Probably need these masks... Use them for creating color model?
int redMask = input.readInt();
int greenMask = input.readInt();
int blueMask = input.readInt();
// Size of each color mask in bits", basically bitPerSample
// NOTE: This field can't be trusted... :-/
int bitsPerRGB = input.readInt();
if (bitsPerPixel == 24 && bitsPerRGB == 24) {
bitsPerRGB = 8;
}
// Hmmm.. Unclear which of these that matters...
// - Can the map be larger than colors used?
// - Can numColors be > 0 for non-colormapped types?
int numColors = input.readInt();
int colorMapEntries = input.readInt();
// Not useful? Metadata?
int windowWidth = input.readInt();
int windowHeight = input.readInt();
int windowX = input.readInt();
int windowY = input.readInt();
int windowBorderWidth = input.readInt();
byte[] windowNameData = new byte[length - X11.X11_HEADER_SIZE];
input.readFully(windowNameData);
String windowName = windowNameData.length <= 1 ? null : new String(windowNameData, 0, windowNameData.length - 1, StandardCharsets.UTF_8);
if (XWDProviderInfo.DEBUG) {
System.out.println("format = " + format);
System.out.println("depth = " + depth);
System.out.println("byteOrder = " + byteOrder);
System.out.println("unit = " + unit);
System.out.println("bitOrder = " + bitOrder);
System.out.println("pad = " + pad);
System.out.println("bitsPerPixel = " + bitsPerPixel);
System.out.println("bytePerLine = " + bytePerLine);
System.out.println("visualClass = " + visualClass);
System.out.printf("redMask = 0x%08x%n", redMask);
System.out.printf("greenMask = 0x%08x%n", greenMask);
System.out.printf("blueMask = 0x%08x%n", blueMask);
System.out.println("bitsPerRGB = " + bitsPerRGB);
System.out.println("numColors = " + numColors);
System.out.println("colorMapEntries = " + colorMapEntries);
System.out.println("windowName = " + windowName);
}
byte[] colorMap = new byte[12 * colorMapEntries];
input.readFully(colorMap);
return new XWDX11Header(width, height,
byteOrder == 0 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN,
bitOrder == 0 ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN,
bitsPerPixel, bytePerLine, visualClass, redMask, greenMask, blueMask, bitsPerRGB,
createColorMap(bitsPerRGB, colorMap),
windowName, pos + length + colorMap.length);
}
private static IndexColorModel createColorMap(int bitDepth, byte[] colorMap) {
if (colorMap.length == 0) {
return null;
}
int size = colorMap.length / 12;
int[] rgb = new int[size];
for (int i = 0; i < size; i++) {
// int index = (colorMap[i * 12] & 0xff) << 24 | (colorMap[i * 12 + 1] & 0xff) << 16 | (colorMap[i * 12 + 2] & 0xff) << 8 | (colorMap[i * 12 + 3] & 0xff);
// System.out.println("index = " + index);
// We'll just use the high 8 bits, as Java don't really have good support for 16 bit index color model
rgb[i] = (colorMap[i * 12 + 4] & 0xff) << 16 | (colorMap[i * 12 + 6] & 0xff) << 8 | (colorMap[i * 12 + 8] & 0xff);
}
return new IndexColorModel(bitDepth, size, rgb, 0, false, -1, DataBuffer.TYPE_BYTE);
}
int numComponents() {
return bitsPerPixel / bitsPerRGB;
}
}

View File

@ -0,0 +1 @@
com.twelvemonkeys.imageio.plugins.xwd.XWDImageReaderSpi

View File

@ -0,0 +1,53 @@
package com.twelvemonkeys.imageio.plugins.xwd;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class XWDImageReaderTest extends ImageReaderAbstractTest<XWDImageReader> {
private final XWDImageReaderSpi provider = new XWDImageReaderSpi();
@Override
protected List<TestData> getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/xwd/input.xwd"), new Dimension(70, 46)),
new TestData(getClassLoaderResource("/xwd/brain.xwd"), new Dimension(520, 510)),
new TestData(getClassLoaderResource("/xwd/sample_640x426.xwd"), new Dimension(640, 426))
);
}
@Override
protected ImageReaderSpi createProvider() {
return provider;
}
@Override
protected Class<XWDImageReader> getReaderClass() {
return XWDImageReader.class;
}
@Override
protected XWDImageReader createReader() {
return provider.createReaderInstance(null);
}
@Override
protected List<String> getFormatNames() {
return Arrays.asList("xwd", "XWD");
}
@Override
protected List<String> getSuffixes() {
return Collections.singletonList("xwd");
}
@Override
protected List<String> getMIMETypes() {
return Arrays.asList("image/xwd", "image/x-xwd");
}
}

Binary file not shown.

Binary file not shown.

View File

@ -44,6 +44,7 @@
<module>imageio-tga</module>
<module>imageio-thumbsdb</module>
<module>imageio-tiff</module>
<module>imageio-xwd</module>
<!-- Wrappers for 3rd party libs -->
<module>imageio-batik</module>