diff --git a/imageio/imageio-ico/license.txt b/imageio/imageio-bmp/license.txt
similarity index 93%
rename from imageio/imageio-ico/license.txt
rename to imageio/imageio-bmp/license.txt
index 2d8ee79c..fe399516 100755
--- a/imageio/imageio-ico/license.txt
+++ b/imageio/imageio-bmp/license.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2009, Harald Kuhr
+Copyright (c) 2014, Harald Kuhr
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/imageio/imageio-ico/pom.xml b/imageio/imageio-bmp/pom.xml
similarity index 80%
rename from imageio/imageio-ico/pom.xml
rename to imageio/imageio-bmp/pom.xml
index 226983cb..50234fdf 100644
--- a/imageio/imageio-ico/pom.xml
+++ b/imageio/imageio-bmp/pom.xml
@@ -6,9 +6,9 @@
imageio
3.1-SNAPSHOT
- imageio-ico
- TwelveMonkeys :: ImageIO :: ICO plugin
- ImageIO plugin for Windows Icon (ICO) and Cursor (CUR) format.
+ imageio-bmp
+ TwelveMonkeys :: ImageIO :: BMP plugin
+ ImageIO plugin for Microsoft Device Independent Bitmap (BMP/DIB) format.
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/AbstractRLEDecoder.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/AbstractRLEDecoder.java
similarity index 64%
rename from common/common-io/src/main/java/com/twelvemonkeys/io/enc/AbstractRLEDecoder.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/AbstractRLEDecoder.java
index a43711a8..1404df31 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/AbstractRLEDecoder.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/AbstractRLEDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, Harald Kuhr
+ * Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,24 +26,29 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.io.enc;
+package com.twelvemonkeys.imageio.plugins.bmp;
+
+import com.twelvemonkeys.io.enc.Decoder;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
+import java.util.Arrays;
/**
* Abstract base class for RLE decoding as specified by in the Windows BMP (aka DIB) file format.
*
*
* @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/AbstractRLEDecoder.java#1 $
+ * @version $Id: AbstractRLEDecoder.java#1 $
*/
-// TODO: Move to other package or make public
abstract class AbstractRLEDecoder implements Decoder {
- protected final byte[] row;
protected final int width;
+ protected final int bitsPerSample;
+
+ protected final byte[] row;
+
protected int srcX;
protected int srcY;
protected int dstX;
@@ -52,36 +57,26 @@ abstract class AbstractRLEDecoder implements Decoder {
/**
* Creates an RLEDecoder. As RLE encoded BMPs may contain x and y deltas,
* etc, we need to know height and width of the image.
- *
- * @param pWidth width of the image
- * @param pHeight height of the image
+ * @param width width of the image
+ * @param bitsPerSample pits per sample
*/
- AbstractRLEDecoder(final int pWidth, final int pHeight) {
- width = pWidth;
- int bytesPerRow = width;
- int mod = bytesPerRow % 4;
-
- if (mod != 0) {
- bytesPerRow += 4 - mod;
- }
+ AbstractRLEDecoder(final int width, final int bitsPerSample) {
+ this.width = width;
+ this.bitsPerSample = bitsPerSample;
+ // Pad row to multiple of 4
+ int bytesPerRow = ((bitsPerSample * this.width + 31) / 32) * 4;
row = new byte[bytesPerRow];
-
- srcX = 0;
- srcY = pHeight - 1;
-
- dstX = srcX;
- dstY = srcY;
}
/**
* Decodes one full row of image data.
*
- * @param pStream the input stream containing RLE data
+ * @param stream the input stream containing RLE data
*
* @throws IOException if an I/O related exception occurs while reading
*/
- protected abstract void decodeRow(final InputStream pStream) throws IOException;
+ protected abstract void decodeRow(final InputStream stream) throws IOException;
/**
* Decodes as much data as possible, from the stream into the buffer.
@@ -91,31 +86,35 @@ abstract class AbstractRLEDecoder implements Decoder {
*
* @return the number of bytes decoded from the stream, to the buffer
*
- * @throws IOException if an I/O related exception ocurs while reading
+ * @throws IOException if an I/O related exception occurs while reading
*/
public final int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
- while (buffer.hasRemaining() && dstY >= 0) {
+ // TODO: Allow decoding < row.length at a time and get rid of this assertion...
+ if (buffer.capacity() < row.length) {
+ throw new AssertionError("This decoder needs a buffer.capacity() of at least one row");
+ }
+
+ while (buffer.remaining() >= row.length && srcY >= 0) {
// NOTE: Decode only full rows, don't decode if y delta
if (dstX == 0 && srcY == dstY) {
decodeRow(stream);
}
- int length = Math.min(row.length - dstX, buffer.remaining());
-// System.arraycopy(row, dstX, buffer, decoded, length);
+ int length = Math.min(row.length - (dstX * bitsPerSample) / 8, buffer.remaining());
buffer.put(row, 0, length);
- dstX += length;
-// decoded += length;
+ dstX += (length * 8) / bitsPerSample;
- if (dstX == row.length) {
+ if (dstX == (row.length * 8) / bitsPerSample) {
dstX = 0;
- dstY--;
+ dstY++;
- // NOTE: If src Y is < dst Y, we have a delta, and have to fill the
+ // NOTE: If src Y is > dst Y, we have a delta, and have to fill the
// gap with zero-bytes
- if (dstY > srcY) {
- for (int i = 0; i < row.length; i++) {
- row[i] = 0x00;
- }
+ if (srcX > dstX) {
+ Arrays.fill(row, 0, (srcX * bitsPerSample) / 8, (byte) 0);
+ }
+ if (srcY > dstY) {
+ Arrays.fill(row, (byte) 0);
}
}
}
@@ -126,16 +125,16 @@ abstract class AbstractRLEDecoder implements Decoder {
/**
* Checks a read byte for EOF marker.
*
- * @param pByte the byte to check
- * @return the value of {@code pByte} if positive.
+ * @param val the byte to check
+ * @return the value of {@code val} if positive.
*
- * @throws EOFException if {@code pByte} is negative
+ * @throws EOFException if {@code val} is negative
*/
- protected static int checkEOF(final int pByte) throws EOFException {
- if (pByte < 0) {
+ protected static int checkEOF(final int val) throws EOFException {
+ if (val < 0) {
throw new EOFException("Premature end of file");
}
- return pByte;
+ return val;
}
}
diff --git a/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BMPImageReader.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BMPImageReader.java
new file mode 100755
index 00000000..8999640e
--- /dev/null
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BMPImageReader.java
@@ -0,0 +1,677 @@
+/*
+ * 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.bmp;
+
+import com.twelvemonkeys.imageio.ImageReaderBase;
+import com.twelvemonkeys.imageio.stream.SubImageInputStream;
+import com.twelvemonkeys.imageio.util.IIOUtil;
+import com.twelvemonkeys.imageio.util.IndexedImageTypeSpecifier;
+import com.twelvemonkeys.io.LittleEndianDataInputStream;
+import com.twelvemonkeys.io.enc.DecoderStream;
+import com.twelvemonkeys.xml.XMLSerializer;
+
+import javax.imageio.*;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataFormatImpl;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
+import java.awt.*;
+import java.awt.color.ColorSpace;
+import java.awt.image.*;
+import java.io.DataInput;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * ImageReader for Microsoft Windows Bitmap (BMP) format.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: CURImageReader.java,v 1.0 Apr 20, 2009 11:54:28 AM haraldk Exp$
+ *
+ * @see com.twelvemonkeys.imageio.plugins.bmp.ICOImageReader
+ */
+public final class BMPImageReader extends ImageReaderBase {
+ private long pixelOffset;
+ private DIBHeader header;
+
+ private transient ImageReader jpegReaderDelegate;
+ private transient ImageReader pngReaderDelegate;
+
+ public BMPImageReader() {
+ super(new BMPImageReaderSpi());
+ }
+
+ protected BMPImageReader(final ImageReaderSpi pProvider) {
+ super(pProvider);
+ }
+
+ @Override
+ protected void resetMembers() {
+ pixelOffset = 0;
+ header = null;
+
+ if (pngReaderDelegate != null) {
+ pngReaderDelegate.dispose();
+ pngReaderDelegate = null;
+ }
+ if (jpegReaderDelegate != null) {
+ jpegReaderDelegate.dispose();
+ jpegReaderDelegate = null;
+ }
+ }
+
+ @Override
+ public int getNumImages(boolean allowSearch) throws IOException {
+ readHeader();
+
+ return 1;
+ }
+
+ private void readHeader() throws IOException {
+ assertInput();
+
+ if (header == null) {
+ // BMP files have Intel origin, always little endian
+ imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+
+ // Read BMP file header
+ byte[] fileHeader = new byte[DIB.BMP_FILE_HEADER_SIZE - 4]; // We'll read the last 4 bytes later
+ imageInput.readFully(fileHeader);
+
+ if (fileHeader[0] != 'B' || fileHeader[1] != 'M') {
+ throw new IIOException("Not a BMP");
+ }
+
+ // Ignore rest of data, it's redundant...
+ pixelOffset = imageInput.readUnsignedInt();
+
+ // Read DIB header
+ header = DIBHeader.read(imageInput);
+ }
+ }
+
+ @Override
+ public int getWidth(int pImageIndex) throws IOException {
+ checkBounds(pImageIndex);
+
+ return header.getWidth();
+ }
+
+ @Override
+ public int getHeight(int pImageIndex) throws IOException {
+ checkBounds(pImageIndex);
+
+ return header.getHeight();
+ }
+
+ @Override
+ public Iterator getImageTypes(int pImageIndex) throws IOException {
+ checkBounds(pImageIndex);
+
+ // TODO: Better implementation, include INT_RGB types for 3BYTE_BGR and 4BYTE_ABGR for INT_ARGB
+ return Arrays.asList(getRawImageType(pImageIndex)).iterator();
+ }
+
+ private void readColorMap(final BitmapIndexed pBitmap) throws IOException {
+ int offset = DIB.BMP_FILE_HEADER_SIZE + header.getSize();
+ if (offset != imageInput.getStreamPosition()) {
+ imageInput.seek(offset);
+ }
+
+ switch (header.getCompression()) {
+ case DIB.COMPRESSION_RGB:
+ case DIB.COMPRESSION_RLE4:
+ case DIB.COMPRESSION_RLE8:
+ break;
+ default:
+ throw new IIOException("Unsupported compression for palette: " + header.getCompression());
+ }
+
+ int colorCount = pBitmap.getColorCount();
+
+ if (header.getSize() == DIB.BITMAP_CORE_HEADER_SIZE) {
+ // Byte triplets in BGR form
+ for (int i = 0; i < colorCount; i++) {
+ int b = imageInput.readUnsignedByte();
+ int g = imageInput.readUnsignedByte();
+ int r = imageInput.readUnsignedByte();
+ pBitmap.colors[i] = r << 16 | g << 8 | b | 0xff000000;
+ }
+ }
+ else {
+ // Byte quadruples in BGRa (or ints in aRGB) form (where a is "Reserved")
+ for (int i = 0; i < colorCount; i++) {
+ pBitmap.colors[i] = (imageInput.readInt() & 0xffffff) | 0xff000000;
+ }
+ }
+ }
+
+ @Override
+ public ImageTypeSpecifier getRawImageType(int pImageIndex) throws IOException {
+ checkBounds(pImageIndex);
+
+ if (header.getPlanes() != 1) {
+ throw new IIOException("Multiple planes not supported");
+ }
+
+ switch (header.getBitCount()) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ // TODO: Get rid of the fake DirectoryEntry and support color maps directly
+ BitmapIndexed indexed = new BitmapIndexed(new DirectoryEntry() {}, header);
+ readColorMap(indexed);
+ return IndexedImageTypeSpecifier.createFromIndexColorModel(indexed.createColorModel());
+
+ case 16:
+ if (header.hasMasks()) {
+ int[] masks = getMasks();
+
+ return ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ masks[0],
+ masks[1],
+ masks[2],
+ masks[3],
+ DataBuffer.TYPE_USHORT,
+ false);
+ }
+
+ // Default if no mask is 555
+ return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_USHORT_555_RGB);
+
+ case 24:
+ if (header.getCompression() != DIB.COMPRESSION_RGB) {
+ throw new IIOException("Unsupported compression for RGB: " + header.getCompression());
+ }
+
+ return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
+
+ case 32:
+ if (header.hasMasks()) {
+ int[] masks = getMasks();
+
+ return ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ masks[0],
+ masks[1],
+ masks[2],
+ masks[3],
+ DataBuffer.TYPE_INT,
+ false);
+ }
+
+ // Default if no mask
+ return ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
+
+ case 0:
+ if (header.getCompression() == DIB.COMPRESSION_JPEG || header.getCompression() == DIB.COMPRESSION_PNG) {
+ return initReaderDelegate(header.getCompression()).getRawImageType(0);
+ }
+ default:
+ throw new IIOException("Unsupported bit count: " + header.getBitCount());
+ }
+ }
+
+ private int[] getMasks() throws IOException {
+ if (header.masks != null) {
+ // Get mask and create either 555, 565 or 444/4444 etc
+ return header.masks;
+ }
+
+ switch (header.getCompression()) {
+ case DIB.COMPRESSION_BITFIELDS:
+ case DIB.COMPRESSION_ALPHA_BITFIELDS:
+ // Consult BITFIELDS/ALPHA_BITFIELDS
+ return readBitFieldsMasks();
+ default:
+ return null;
+ }
+ }
+
+ private int[] readBitFieldsMasks() throws IOException {
+ long offset = DIB.BMP_FILE_HEADER_SIZE + header.getSize();
+
+ if (offset != imageInput.getStreamPosition()) {
+ imageInput.seek(offset);
+ }
+
+ int[] masks = DIBHeader.readMasks(imageInput);
+
+ if (header.getCompression() != DIB.COMPRESSION_ALPHA_BITFIELDS) {
+ masks[3] = 0;
+ }
+
+ return masks;
+ }
+
+ @Override
+ public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
+ checkBounds(imageIndex);
+
+ // Delegate reading for JPEG/PNG compression
+ if (header.getCompression() == DIB.COMPRESSION_JPEG || header.getCompression() == DIB.COMPRESSION_PNG) {
+ return readUsingDelegate(header.getCompression(), param);
+ }
+
+ int width = getWidth(imageIndex);
+ int height = getHeight(imageIndex);
+
+ ImageTypeSpecifier rawType = getRawImageType(imageIndex);
+ BufferedImage destination = getDestination(param, getImageTypes(imageIndex), width, height);
+
+ // BMP rows are padded to 4 byte boundary
+ int rowSizeBytes = ((header.getBitCount() * width + 31) / 32) * 4;
+
+ // Wrap
+ imageInput.seek(pixelOffset);
+ DataInput input;
+
+ switch (header.getCompression()) {
+ case DIB.COMPRESSION_RLE4:
+ if (header.getBitCount() != 4) {
+ throw new IIOException(String.format("Unsupported combination of bitCount/compression: %s/%s", header.getBitCount(), header.getCompression()));
+ }
+
+ input = new LittleEndianDataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(imageInput), new RLE4Decoder(width), rowSizeBytes));
+ break;
+
+ case DIB.COMPRESSION_RLE8:
+ if (header.getBitCount() != 8) {
+ throw new IIOException(String.format("Unsupported combination of bitCount/compression: %s/%s", header.getBitCount(), header.getCompression()));
+ }
+ input = new LittleEndianDataInputStream(new DecoderStream(IIOUtil.createStreamAdapter(imageInput), new RLE8Decoder(width), rowSizeBytes));
+ break;
+
+ case DIB.COMPRESSION_BITFIELDS:
+ case DIB.COMPRESSION_ALPHA_BITFIELDS:
+ // TODO: Validate bitCount for these
+ case DIB.COMPRESSION_RGB:
+ input = imageInput;
+ break;
+
+ default:
+ throw new IIOException("Unsupported compression: " + header.getCompression());
+
+ }
+
+ 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());
+
+ WritableRaster rowRaster;
+
+ switch (header.getBitCount()) {
+ case 1:
+ case 2:
+ case 4:
+ rowRaster = Raster.createPackedRaster(new DataBufferByte(rowSizeBytes), width, 1, header.getBitCount(), null);
+ break;
+ case 8:
+ case 24:
+ rowRaster = Raster.createInterleavedRaster(new DataBufferByte(rowSizeBytes), width, 1, rowSizeBytes, header.getBitCount() / 8, createOffsets(rawType.getNumBands()), null);
+ break;
+ case 16:
+ case 32:
+ rowRaster = rawType.createBufferedImage(width, 1).getRaster();
+ break;
+ default:
+ throw new IIOException("Unsupported pixel depth: " + header.getBitCount());
+ }
+
+ // Clip to source region
+ Raster clippedRow = clipRowToRect(rowRaster, srcRegion,
+ param != null ? param.getSourceBands() : null,
+ param != null ? param.getSourceXSubsampling() : 1);
+
+ int xSub = param != null ? param.getSourceXSubsampling() : 1;
+ int ySub = param != null ? param.getSourceYSubsampling() : 1;
+
+ processImageStarted(imageIndex);
+ for (int y = 0; y < height; y++) {
+ switch (header.getBitCount()) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 24:
+ byte[] rowDataByte = ((DataBufferByte) rowRaster.getDataBuffer()).getData();
+ try {
+ readRowByte(input, height, srcRegion, xSub, ySub, rowDataByte, destRaster, clippedRow, y);
+ }
+ catch (IndexOutOfBoundsException ioob) {
+ System.err.println("IOOB: " + ioob);
+ System.err.println("y: " + y);
+ }
+ catch (EOFException eof) {
+ System.err.println("EOF: " + eof);
+ System.err.println("y: " + y);
+ }
+ break;
+
+ case 16:
+ short[] rowDataUShort = ((DataBufferUShort) rowRaster.getDataBuffer()).getData();
+ readRowUShort(input, height, srcRegion, xSub, ySub, rowDataUShort, destRaster, clippedRow, y);
+ break;
+
+ case 32:
+ int[] rowDataInt = ((DataBufferInt) rowRaster.getDataBuffer()).getData();
+ readRowInt(input, height, srcRegion, xSub, ySub, rowDataInt, destRaster, clippedRow, y);
+ break;
+
+ default:
+ throw new AssertionError("Unsupported pixel depth: " + header.getBitCount());
+ }
+
+ processImageProgress(100f * y / height);
+
+ if (height - 1 - y < srcRegion.y) {
+ break;
+ }
+
+ if (abortRequested()) {
+ processReadAborted();
+ break;
+ }
+ }
+
+ processImageComplete();
+
+ return destination;
+ }
+
+ private BufferedImage readUsingDelegate(final int compression, final ImageReadParam param) throws IOException {
+ ImageReader reader = initReaderDelegate(compression);
+
+ return reader.read(0, param);
+ }
+
+ private ImageReader initReaderDelegate(int compression) throws IOException {
+ ImageReader reader = getImageReaderDelegate(compression);
+
+ imageInput.seek(pixelOffset);
+ reader.setInput(new SubImageInputStream(imageInput, header.getImageSize()));
+
+ return reader;
+ }
+
+ private ImageReader getImageReaderDelegate(int compression) throws IIOException {
+ String format;
+
+ switch (compression) {
+ case DIB.COMPRESSION_JPEG:
+ if (jpegReaderDelegate != null) {
+ return jpegReaderDelegate;
+ }
+
+ format = "JPEG";
+ break;
+
+ case DIB.COMPRESSION_PNG:
+ if (pngReaderDelegate != null) {
+ return pngReaderDelegate;
+ }
+
+ format = "PNG";
+ break;
+
+ default:
+ throw new AssertionError("Unsupported BMP compression: " + compression);
+ }
+
+ // Consider looking for specific PNG and JPEG implementations.
+ Iterator readers = ImageIO.getImageReadersByFormatName(format);
+ if (!readers.hasNext()) {
+ throw new IIOException(String.format("Delegate ImageReader for %s format not found", format));
+ }
+
+ ImageReader reader = readers.next();
+
+ // Cache for later use
+ switch (compression) {
+ case DIB.COMPRESSION_JPEG:
+ jpegReaderDelegate = reader;
+ break;
+ case DIB.COMPRESSION_PNG:
+ pngReaderDelegate = reader;
+ break;
+ }
+
+ return reader;
+ }
+
+ private int[] createOffsets(int numBands) {
+ int[] offsets = new int[numBands];
+
+ for (int i = 0; i < numBands; i++) {
+ offsets[i] = numBands - i - 1;
+ }
+
+ return offsets;
+ }
+
+ private void readRowByte(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
+ final byte[] rowDataByte, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
+ // If subsampled or outside source region, skip entire row
+ if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
+ input.skipBytes(rowDataByte.length);
+
+ return;
+ }
+
+ input.readFully(rowDataByte, 0, rowDataByte.length);
+
+ // Subsample horizontal
+ if (xSub != 1) {
+ for (int x = 0; x < srcRegion.width / xSub; x++) {
+ rowDataByte[srcRegion.x + x] = rowDataByte[srcRegion.x + x * xSub];
+ }
+ }
+
+ if (header.topDown) {
+ destChannel.setDataElements(0, y, srcChannel);
+ }
+ else {
+ // Flip into position
+ int dstY = (height - 1 - y - srcRegion.y) / ySub;
+ destChannel.setDataElements(0, dstY, srcChannel);
+ }
+ }
+
+ private void readRowUShort(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
+ final short[] rowDataUShort, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
+ // If subsampled or outside source region, skip entire row
+ if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
+ input.skipBytes(rowDataUShort.length * 2 + (rowDataUShort.length % 2) * 2);
+
+ return;
+ }
+
+ readFully(input, rowDataUShort);
+
+ // Skip 2 bytes, if not ending on 32 bit/4 byte boundary
+ if (rowDataUShort.length % 2 != 0) {
+ input.skipBytes(2);
+ }
+
+ // Subsample horizontal
+ if (xSub != 1) {
+ for (int x = 0; x < srcRegion.width / xSub; x++) {
+ rowDataUShort[srcRegion.x + x] = rowDataUShort[srcRegion.x + x * xSub];
+ }
+ }
+
+ if (header.topDown) {
+ destChannel.setDataElements(0, y, srcChannel);
+ }
+ else {
+ // Flip into position
+ int dstY = (height - 1 - y - srcRegion.y) / ySub;
+ destChannel.setDataElements(0, dstY, srcChannel);
+ }
+ }
+
+ private void readRowInt(final DataInput input, final int height, final Rectangle srcRegion, final int xSub, final int ySub,
+ final int [] rowDataInt, final WritableRaster destChannel, final Raster srcChannel, final int y) throws IOException {
+ // If subsampled or outside source region, skip entire row
+ if (y % ySub != 0 || height - 1 - y < srcRegion.y || height - 1 - y >= srcRegion.y + srcRegion.height) {
+ input.skipBytes(rowDataInt.length * 4);
+
+ return;
+ }
+
+ readFully(input, rowDataInt);
+
+ // Subsample horizontal
+ if (xSub != 1) {
+ for (int x = 0; x < srcRegion.width / xSub; x++) {
+ rowDataInt[srcRegion.x + x] = rowDataInt[srcRegion.x + x * xSub];
+ }
+ }
+
+ if (header.topDown) {
+ destChannel.setDataElements(0, y, srcChannel);
+ }
+ else {
+ // Flip into position
+ int dstY = (height - 1 - y - srcRegion.y) / ySub;
+ destChannel.setDataElements(0, dstY, srcChannel);
+ }
+ }
+
+ // TODO: Candidate util method
+ private static void readFully(final DataInput input, final short[] shorts) throws IOException {
+ if (input instanceof ImageInputStream) {
+ // Optimization for ImageInputStreams, read all in one go
+ ((ImageInputStream) input).readFully(shorts, 0, shorts.length);
+ }
+ else {
+ for (int i = 0; i < shorts.length; i++) {
+ shorts[i] = input.readShort();
+ }
+ }
+ }
+
+ // TODO: Candidate util method
+ private static void readFully(final DataInput input, final int[] ints) throws IOException {
+ if (input instanceof ImageInputStream) {
+ // Optimization for ImageInputStreams, read all in one go
+ ((ImageInputStream) input).readFully(ints, 0, ints.length);
+ }
+ else {
+ for (int i = 0; i < ints.length; i++) {
+ ints[i] = input.readInt();
+ }
+ }
+ }
+
+ 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);
+ }
+
+ public static void main(String[] args) throws IOException {
+ BMPImageReaderSpi provider = new BMPImageReaderSpi();
+ BMPImageReader reader = new BMPImageReader(provider);
+
+ for (String arg : args) {
+ try {
+ File in = new File(arg);
+ ImageInputStream stream = ImageIO.createImageInputStream(in);
+
+ System.err.println("Can read?: " + provider.canDecodeInput(stream));
+
+ reader.reset();
+ reader.setInput(stream);
+
+ 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.getWidth(0);
+ // int height = reader.getHeight(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("reader.header: " + reader.header);
+
+ BufferedImage image = reader.read(0, param);
+
+ System.err.println("image: " + image);
+
+ showIt(image, in.getName());
+
+
+ IIOMetadata imageMetadata = reader.getImageMetadata(0);
+ if (imageMetadata != null) {
+ new XMLSerializer(System.out, System.getProperty("file.encoding")).serialize(imageMetadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName), false);
+ }
+ }
+ catch (Throwable t) {
+ if (args.length > 1) {
+ System.err.println("---");
+ System.err.println("---> " + t.getClass().getSimpleName() + ": " + t.getMessage() + " for " + arg);
+ System.err.println("---");
+ }
+ else {
+ throwAs(RuntimeException.class, t);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "UnusedDeclaration"})
+ static void throwAs(final Class pType, final Throwable pThrowable) throws T {
+ throw (T) pThrowable;
+ }
+}
diff --git a/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BMPImageReaderSpi.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BMPImageReaderSpi.java
new file mode 100755
index 00000000..1a26ca84
--- /dev/null
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BMPImageReaderSpi.java
@@ -0,0 +1,159 @@
+/*
+ * 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.bmp;
+
+import com.twelvemonkeys.imageio.spi.ProviderInfo;
+import com.twelvemonkeys.imageio.util.IIOUtil;
+
+import javax.imageio.ImageReader;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.spi.ServiceRegistry;
+import javax.imageio.stream.ImageInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Iterator;
+import java.util.Locale;
+
+/**
+ * BMPImageReaderSpi
+ *
+ * @author Harald Kuhr
+ * @version $Id: BMPImageReaderSpi.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
+ */
+public final class BMPImageReaderSpi extends ImageReaderSpi {
+ public BMPImageReaderSpi() {
+ this(IIOUtil.getProviderInfo(BMPImageReaderSpi.class));
+ }
+
+ private BMPImageReaderSpi(final ProviderInfo pProviderInfo) {
+ super(
+ pProviderInfo.getVendorName(),
+ pProviderInfo.getVersion(),
+ new String[]{"bmp", "BMP"},
+ new String[]{"bmp", "rle"},
+ new String[]{
+ "image/bmp",
+ "image/x-bmp"
+// "image/vnd.microsoft.bitmap", // TODO: Official IANA MIME
+ },
+ "com.twelvemonkeys.imageio.plugins.bmp.BMPImageReader",
+ new Class[]{ImageInputStream.class},
+ null,
+ true, null, null, null, null,
+ true,
+ null, null,
+ null, null
+ );
+ }
+
+ static ImageReaderSpi lookupDefaultProvider(final ServiceRegistry registry) {
+ Iterator providers = registry.getServiceProviders(ImageReaderSpi.class, true);
+
+ while (providers.hasNext()) {
+ ImageReaderSpi provider = providers.next();
+
+ if (provider.getClass().getName().equals("com.sun.imageio.plugins.bmp.BMPImageReaderSpi")) {
+ return provider;
+ }
+ }
+
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void onRegistration(final ServiceRegistry registry, final Class> category) {
+ ImageReaderSpi defaultProvider = lookupDefaultProvider(registry);
+
+ if (defaultProvider != null) {
+ // Order before com.sun provider, to aid ImageIO in selecting our reader
+ registry.setOrdering((Class) category, this, defaultProvider);
+ }
+ }
+
+ public boolean canDecodeInput(final Object pSource) throws IOException {
+ return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource);
+ }
+
+ private static boolean canDecode(final ImageInputStream pInput) throws IOException {
+ byte[] fileHeader = new byte[18]; // Strictly: file header (14 bytes) + BMP header size field (4 bytes)
+
+ try {
+ pInput.mark();
+ pInput.readFully(fileHeader);
+
+ // Magic: BM
+ if (fileHeader[0] != 'B' || fileHeader[1] != 'M') {
+ return false;
+ }
+
+ ByteBuffer header = ByteBuffer.wrap(fileHeader);
+ header.order(ByteOrder.LITTLE_ENDIAN);
+
+ int fileSize = header.getInt(2);
+ if (fileSize <= 0) {
+ return false;
+ }
+
+ // Ignore hot-spots etc..
+
+ int offset = header.getInt(10);
+ if (offset <= 0) {
+ return false;
+ }
+
+ int headerSize = header.getInt(14);
+ switch (headerSize) {
+ case DIB.BITMAP_CORE_HEADER_SIZE:
+ case DIB.OS2_V2_HEADER_16_SIZE:
+ case DIB.OS2_V2_HEADER_SIZE:
+ case DIB.BITMAP_INFO_HEADER_SIZE:
+ case DIB.BITMAP_V2_INFO_HEADER_SIZE:
+ case DIB.BITMAP_V3_INFO_HEADER_SIZE:
+ case DIB.BITMAP_V4_INFO_HEADER_SIZE:
+ case DIB.BITMAP_V5_INFO_HEADER_SIZE:
+ return true;
+ default:
+ return false;
+ }
+ }
+ finally {
+ pInput.reset();
+ }
+ }
+
+ public ImageReader createReaderInstance(final Object pExtension) throws IOException {
+ return new BMPImageReader(this);
+ }
+
+ public String getDescription(final Locale pLocale) {
+ return "Windows Device Independent Bitmap Format (BMP) Reader";
+ }
+}
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapDescriptor.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapDescriptor.java
similarity index 98%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapDescriptor.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapDescriptor.java
index de3645b4..3587d662 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapDescriptor.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapDescriptor.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.lang.Validate;
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapIndexed.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapIndexed.java
similarity index 97%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapIndexed.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapIndexed.java
index 17fdc28f..c5d33293 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapIndexed.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapIndexed.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.image.InverseColorMapIndexColorModel;
@@ -107,7 +107,7 @@ class BitmapIndexed extends BitmapDescriptor {
// Try to avoid USHORT transfertype, as it results in BufferedImage TYPE_CUSTOM
// NOTE: This code assumes icons are small, and is NOT optimized for performance...
if (colors > (1 << getBitCount())) {
- int index = BitmapIndexed.findTransIndexMaybeRemap(this.colors, this.bits);
+ int index = findTransIndexMaybeRemap(this.colors, this.bits);
if (index == -1) {
// No duplicate found, increase bitcount
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapMask.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapMask.java
similarity index 98%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapMask.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapMask.java
index 874dcf56..c4831680 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapMask.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapMask.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import java.awt.image.BufferedImage;
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapRGB.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapRGB.java
similarity index 97%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapRGB.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapRGB.java
index aaa75359..11dc17d4 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapRGB.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapRGB.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import java.awt.image.BufferedImage;
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapUnsupported.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapUnsupported.java
similarity index 97%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapUnsupported.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapUnsupported.java
index 84078090..7498976e 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/BitmapUnsupported.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/BitmapUnsupported.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import java.awt.image.BufferedImage;
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReader.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/CURImageReader.java
similarity index 95%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReader.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/CURImageReader.java
index c58aca14..14eafe78 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReader.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/CURImageReader.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import javax.imageio.spi.ImageReaderSpi;
import java.awt.*;
@@ -56,7 +56,7 @@ public final class CURImageReader extends DIBImageReader {
* @param pImageIndex the index of the cursor in the current input.
* @return the hot spot location for the cursor
*
- * @throws IOException if an I/O exception occurs during reading of image meta data
+ * @throws java.io.IOException if an I/O exception occurs during reading of image meta data
* @throws IndexOutOfBoundsException if {@code pImageIndex} is less than {@code 0} or greater than/equal to
* the number of cursors in the file
*/
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderSpi.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/CURImageReaderSpi.java
similarity index 96%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderSpi.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/CURImageReaderSpi.java
index af4c9dc8..ea5b89f0 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderSpi.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/CURImageReaderSpi.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
@@ -60,7 +60,7 @@ public final class CURImageReaderSpi extends ImageReaderSpi {
"image/x-cursor", // Common extension MIME
"image/cursor" // Unofficial, but common
},
- "com.twelvemonkeys.imageio.plugins.ico.CURImageReader",
+ "com.twelvemonkeys.imageio.plugins.bmp.CURImageReader",
new Class[] {ImageInputStream.class},
null,
true, null, null, null, null,
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIB.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DIB.java
similarity index 65%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIB.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DIB.java
index 3a02d970..0fab58fe 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIB.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DIB.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
/**
* DIB
@@ -40,26 +40,54 @@ package com.twelvemonkeys.imageio.plugins.ico;
*/
interface DIB {
int TYPE_UNKNOWN = 0;
+
int TYPE_ICO = 1;
int TYPE_CUR = 2;
+ int BMP_FILE_HEADER_SIZE = 14;
+
/** BITMAPCOREHEADER size, OS/2 V1 */
- int OS2_V1_HEADER_SIZE = 12;
+ int BITMAP_CORE_HEADER_SIZE = 12;
+
+ /** Strange BITMAPCOREHEADER size, OS/2 V2, but only first 16 bytes... */
+ int OS2_V2_HEADER_16_SIZE = 16;
/** BITMAPCOREHEADER size, OS/2 V2 */
int OS2_V2_HEADER_SIZE = 64;
/**
* BITMAPINFOHEADER size, Windows 3.0 and later.
- * This is the most commonly used header for persistent bitmaps
+ * This is the most commonly used header for persistent bitmaps.
*/
- int WINDOWS_V3_HEADER_SIZE = 40;
+ int BITMAP_INFO_HEADER_SIZE = 40;
- /** BITMAPV4HEADER size, Windows 95/NT4 and later */
- int WINDOWS_V4_HEADER_SIZE = 108;
+ int BITMAP_V2_INFO_HEADER_SIZE = 52; // Undocumented, written by Photoshop
- /** BITMAPV5HEADER size, Windows 98/2000 and later */
- int WINDOWS_V5_HEADER_SIZE = 124;
+ int BITMAP_V3_INFO_HEADER_SIZE = 56; // Undocumented, written by Photoshop
+
+ /** BITMAPV4HEADER size, Windows 95/NT4 and later. */
+ int BITMAP_V4_INFO_HEADER_SIZE = 108;
+
+ /** BITMAPV5HEADER size, Windows 98/2000 and later. */
+ int BITMAP_V5_INFO_HEADER_SIZE = 124;
+
+ /** BI_RGB: No compression. Default. */
+ int COMPRESSION_RGB = 0;
+ /** BI_RLE8: 8 bit run-length encoding (RLE). */
+ int COMPRESSION_RLE8 = 1;
+ /** BI_RLE4: 4 bit run-length encoding (RLE). */
+ int COMPRESSION_RLE4 = 2;
+ /** BI_BITFIELDS, OS2_V2: Huffman 1D compression. V2: RGB bit field masks, V3+: RGBA. */
+ int COMPRESSION_BITFIELDS = 3;
+ int COMPRESSION_JPEG = 4;
+ int COMPRESSION_PNG = 5;
+ /** RGBA bitfield masks. */
+ int COMPRESSION_ALPHA_BITFIELDS = 6;
+
+ // Unused for Windows Metafiles using CMYK colorspace:
+ // int COMPRESSION_CMYK = 11;
+ // int COMPRESSION_CMYK_RLE8 = 12;
+ // int COMPRESSION_CMYK_RLE5 = 13;
/** PNG "magic" identifier */
long PNG_MAGIC = 0x89l << 56 | (long) 'P' << 48 | (long) 'N' << 40 | (long) 'G' << 32 | 0x0dl << 24 | 0x0al << 16 | 0x1al << 8 | 0x0al;
diff --git a/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DIBHeader.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DIBHeader.java
new file mode 100755
index 00000000..2a858f9a
--- /dev/null
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DIBHeader.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2009, 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.bmp;
+
+import javax.imageio.IIOException;
+import java.io.DataInput;
+import java.io.IOException;
+
+/**
+ * Represents the DIB (Device Independent Bitmap) Information header structure.
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: DIBHeader.java,v 1.0 May 5, 2009 10:45:31 AM haraldk Exp$
+ * @see BMP file format (Wikipedia)
+ */
+abstract class DIBHeader {
+ protected int size;
+ protected int width;
+ // NOTE: If a bitmask is present, this value includes the height of the mask
+ // (so often header.height = entry.height * 2)
+ protected int height;
+ protected boolean topDown = false;
+
+ protected int planes;
+ protected int bitCount;
+
+ /**
+ * 0 = BI_RGB: No compression
+ * 1 = BI_RLE8: 8 bit RLE Compression (8 bit only)
+ * 2 = BI_RLE4: 4 bit RLE Compression (4 bit only)
+ * 3 = BI_BITFIELDS: No compression (16 & 32 bit only)
+ */
+ protected int compression;
+
+ // May be 0 if not known
+ protected int imageSize;
+
+ protected int xPixelsPerMeter;
+ protected int yPixelsPerMeter;
+
+ protected int colorsUsed;
+
+ // 0 means all colors are important
+ protected int colorsImportant;
+
+ protected int[] masks;
+
+ protected DIBHeader() {
+ }
+
+ public static DIBHeader read(final DataInput pStream) throws IOException {
+ int size = pStream.readInt();
+
+ DIBHeader header = createHeader(size);
+ header.read(size, pStream);
+
+ return header;
+ }
+
+ private static DIBHeader createHeader(final int pSize) throws IOException {
+ switch (pSize) {
+ case DIB.BITMAP_CORE_HEADER_SIZE:
+ return new BitmapCoreHeader();
+ case DIB.OS2_V2_HEADER_16_SIZE:
+ case DIB.OS2_V2_HEADER_SIZE:
+ return new BitmapCoreHeaderV2();
+ case DIB.BITMAP_INFO_HEADER_SIZE:
+ // ICO and CUR always uses the Microsoft Windows 3.0 DIB header, which is 40 bytes.
+ // This is also the most common format for persistent BMPs.
+ return new BitmapInfoHeader();
+ case DIB.BITMAP_V3_INFO_HEADER_SIZE:
+ return new BitmapV3InfoHeader();
+ case DIB.BITMAP_V4_INFO_HEADER_SIZE:
+ return new BitmapV4InfoHeader();
+ case DIB.BITMAP_V5_INFO_HEADER_SIZE:
+ return new BitmapV5InfoHeader();
+ case DIB.BITMAP_V2_INFO_HEADER_SIZE:
+ throw new IIOException(String.format("Windows Bitmap Information Header (size: %s) not supported", pSize));
+ default:
+ throw new IIOException(String.format("Unknown Bitmap Information Header (size: %s)", pSize));
+ }
+ }
+
+ protected abstract void read(int pSize, DataInput pStream) throws IOException;
+
+ public final int getSize() {
+ return size;
+ }
+
+ public final int getWidth() {
+ return width;
+ }
+
+ public final int getHeight() {
+ return height;
+ }
+
+ public final int getPlanes() {
+ return planes;
+ }
+
+ public final int getBitCount() {
+ return bitCount;
+ }
+
+ public int getCompression() {
+ return compression;
+ }
+
+ public int getImageSize() {
+ return imageSize != 0 ? imageSize : ((bitCount * width + 31) / 32) * 4 * height;
+ }
+
+ public int getXPixelsPerMeter() {
+ return xPixelsPerMeter;
+ }
+
+ public int getYPixelsPerMeter() {
+ return yPixelsPerMeter;
+ }
+
+ public int getColorsUsed() {
+ return colorsUsed;
+ }
+
+ public int getColorsImportant() {
+ return colorsImportant;
+ }
+
+ public boolean hasMasks() {
+ return masks != null || compression == DIB.COMPRESSION_BITFIELDS || compression == DIB.COMPRESSION_ALPHA_BITFIELDS;
+ }
+
+ public String toString() {
+ return String.format(
+ "%s: size: %d bytes, " +
+ "width: %d, height: %d, planes: %d, bit count: %d, compression: %d, " +
+ "image size: %d%s, " +
+ "X pixels per m: %d, Y pixels per m: %d, " +
+ "colors used: %d%s, colors important: %d%s",
+ getClass().getSimpleName(),
+ getSize(), getWidth(), getHeight(), getPlanes(), getBitCount(), getCompression(),
+ getImageSize(), (getImageSize() == 0 ? " (unknown)" : ""),
+ getXPixelsPerMeter(), getYPixelsPerMeter(),
+ getColorsUsed(), (getColorsUsed() == 0 ? " (unknown)" : ""),
+ getColorsImportant(), (getColorsImportant() == 0 ? " (all)" : "")
+ );
+ }
+
+ static int[] readMasks(final DataInput pStream) throws IOException {
+ int[] masks = new int[4];
+ for (int i = 0; i < masks.length; i++) {
+ masks[i] = pStream.readInt();
+ }
+
+ return masks;
+ }
+
+ // TODO: Get rid of code duplication below...
+
+ static final class BitmapCoreHeader extends DIBHeader {
+ protected void read(final int pSize, final DataInput pStream) throws IOException {
+ if (pSize != DIB.BITMAP_CORE_HEADER_SIZE) {
+ throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.BITMAP_CORE_HEADER_SIZE));
+ }
+
+ size = pSize;
+
+ // NOTE: Unlike all other headers, width and height are unsigned SHORT values (16 bit)!
+ width = pStream.readUnsignedShort();
+ height = pStream.readUnsignedShort();
+
+ if (height < 0) {
+ height = -height;
+ topDown = true;
+ }
+
+ planes = pStream.readUnsignedShort();
+ bitCount = pStream.readUnsignedShort();
+
+ // Roughly 72 DPI
+ xPixelsPerMeter = 2835;
+ yPixelsPerMeter = 2835;
+ }
+ }
+
+ /**
+ * OS/2 BitmapCoreHeader Version 2.
+ *
+ * NOTE: According to the docs this header is variable size.
+ * However, it seems that the size is either 16, 40 or 64, which is covered
+ * (40 is the size of the normal {@link BitmapInfoHeader}, and has the same layout).
+ *
+ * @see OS/2 Bitmap File Format Summary
+ */
+ static final class BitmapCoreHeaderV2 extends DIBHeader {
+ protected void read(final int pSize, final DataInput pStream) throws IOException {
+ if (pSize != DIB.OS2_V2_HEADER_SIZE && pSize != DIB.OS2_V2_HEADER_16_SIZE) {
+ throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.OS2_V2_HEADER_SIZE));
+ }
+
+ size = pSize;
+
+ width = pStream.readInt();
+ height = pStream.readInt();
+
+ if (height < 0) {
+ height = -height;
+ topDown = true;
+ }
+
+ planes = pStream.readUnsignedShort();
+ bitCount = pStream.readUnsignedShort();
+
+ if (pSize == DIB.OS2_V2_HEADER_16_SIZE) {
+ // Roughly 72 DPI
+ xPixelsPerMeter = 2835;
+ yPixelsPerMeter = 2835;
+ }
+ else {
+ compression = pStream.readInt();
+
+ imageSize = pStream.readInt();
+
+ xPixelsPerMeter = pStream.readInt();
+ yPixelsPerMeter = pStream.readInt();
+
+ colorsUsed = pStream.readInt();
+ colorsImportant = pStream.readInt();
+ }
+
+ int units = pStream.readShort();
+ int reserved = pStream.readShort();
+ int recording = pStream.readShort(); // Recording algorithm
+ int rendering = pStream.readShort(); // Halftoning algorithm
+ int size1 = pStream.readInt(); // Reserved for halftoning use
+ int size2 = pStream.readInt(); // Reserved for halftoning use
+ int colorEncoding = pStream.readInt(); // Color model used in bitmap
+ int identifier = pStream.readInt(); // Reserved for application use
+ }
+ }
+
+
+ /**
+ * Represents the DIB (Device Independent Bitmap) Windows 3.0 Bitmap Information header structure.
+ * This is the common format for persistent DIB structures, even if Windows
+ * may use the later versions at run-time.
+ *
+ *
+ * @author Harald Kuhr
+ * @version $Id: DIBHeader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
+ * @see BMP file format (Wikipedia)
+ */
+ static final class BitmapInfoHeader extends DIBHeader {
+ protected void read(final int pSize, final DataInput pStream) throws IOException {
+ if (pSize != DIB.BITMAP_INFO_HEADER_SIZE) {
+ throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.BITMAP_INFO_HEADER_SIZE));
+ }
+
+ size = pSize;
+
+ width = pStream.readInt();
+ height = pStream.readInt();
+
+ if (height < 0) {
+ height = -height;
+ topDown = true;
+ }
+
+ planes = pStream.readUnsignedShort();
+ bitCount = pStream.readUnsignedShort();
+ compression = pStream.readInt();
+
+ imageSize = pStream.readInt();
+
+ xPixelsPerMeter = pStream.readInt();
+ yPixelsPerMeter = pStream.readInt();
+
+ colorsUsed = pStream.readInt();
+ colorsImportant = pStream.readInt();
+ }
+ }
+
+ /**
+ * Represents the semi-undocumented structure BITMAPV3INFOHEADER.
+ * @see BITMAPV3INFOHEADER
+ */
+ static final class BitmapV3InfoHeader extends DIBHeader {
+ protected void read(final int pSize, final DataInput pStream) throws IOException {
+ if (pSize != DIB.BITMAP_V3_INFO_HEADER_SIZE) {
+ throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.BITMAP_V3_INFO_HEADER_SIZE));
+ }
+
+ size = pSize;
+
+ width = pStream.readInt();
+ height = pStream.readInt();
+
+ if (height < 0) {
+ height = -height;
+ topDown = true;
+ }
+
+ planes = pStream.readUnsignedShort();
+ bitCount = pStream.readUnsignedShort();
+ compression = pStream.readInt();
+
+ imageSize = pStream.readInt();
+
+ xPixelsPerMeter = pStream.readInt();
+ yPixelsPerMeter = pStream.readInt();
+
+ colorsUsed = pStream.readInt();
+ colorsImportant = pStream.readInt();
+
+ masks = readMasks(pStream);
+ }
+ }
+
+ /**
+ * Represents the BITMAPV4INFOHEADER structure.
+ */
+ static final class BitmapV4InfoHeader extends DIBHeader {
+ protected void read(final int pSize, final DataInput pStream) throws IOException {
+ if (pSize != DIB.BITMAP_V4_INFO_HEADER_SIZE) {
+ throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.BITMAP_V4_INFO_HEADER_SIZE));
+ }
+
+ size = pSize;
+
+ width = pStream.readInt();
+ height = pStream.readInt();
+
+ if (height < 0) {
+ height = -height;
+ topDown = true;
+ }
+
+ planes = pStream.readUnsignedShort();
+ bitCount = pStream.readUnsignedShort();
+ compression = pStream.readInt();
+
+ imageSize = pStream.readInt();
+
+ xPixelsPerMeter = pStream.readInt();
+ yPixelsPerMeter = pStream.readInt();
+
+ colorsUsed = pStream.readInt();
+ colorsImportant = pStream.readInt();
+
+ masks = readMasks(pStream);
+
+ byte[] data = new byte[52];
+ pStream.readFully(data);
+
+// System.out.println("data = " + Arrays.toString(data));
+ }
+ }
+
+ /**
+ * Represents the BITMAPV5INFOHEADER structure.
+ */
+ static final class BitmapV5InfoHeader extends DIBHeader {
+ protected void read(final int pSize, final DataInput pStream) throws IOException {
+ if (pSize != DIB.BITMAP_V5_INFO_HEADER_SIZE) {
+ throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.BITMAP_V5_INFO_HEADER_SIZE));
+ }
+
+ size = pSize;
+
+ width = pStream.readInt();
+ height = pStream.readInt();
+
+ if (height < 0) {
+ height = -height;
+ topDown = true;
+ }
+
+ planes = pStream.readUnsignedShort();
+ bitCount = pStream.readUnsignedShort();
+ compression = pStream.readInt();
+
+ imageSize = pStream.readInt();
+
+ xPixelsPerMeter = pStream.readInt();
+ yPixelsPerMeter = pStream.readInt();
+
+ colorsUsed = pStream.readInt();
+ colorsImportant = pStream.readInt();
+
+ masks = readMasks(pStream);
+
+ byte[] data = new byte[68];
+ pStream.readFully(data);
+
+// System.out.println("data = " + Arrays.toString(data));
+ }
+ }
+}
\ No newline at end of file
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBImageReader.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DIBImageReader.java
similarity index 99%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBImageReader.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DIBImageReader.java
index 11be7c15..b1c4e612 100644
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBImageReader.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DIBImageReader.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.image.ImageUtil;
import com.twelvemonkeys.imageio.ImageReaderBase;
@@ -290,7 +290,7 @@ abstract class DIBImageReader extends ImageReaderBase {
// Palette style
case 1:
case 4:
- case 8:
+ case 8: // TODO: Gray!
descriptor = new BitmapIndexed(pEntry, header);
readBitmapIndexed((BitmapIndexed) descriptor);
break;
@@ -454,6 +454,7 @@ abstract class DIBImageReader extends ImageReaderBase {
// TODO: No idea if this actually works..
short[] pixels = new short[pBitmap.getWidth() * pBitmap.getHeight()];
+ // TODO: Support TYPE_USHORT_565 and the RGB 444/ARGB 4444 layouts
// Will create TYPE_USHORT_555;
DirectColorModel cm = new DirectColorModel(16, 0x7C00, 0x03E0, 0x001F);
DataBuffer buffer = new DataBufferShort(pixels, pixels.length);
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/Directory.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/Directory.java
similarity index 98%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/Directory.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/Directory.java
index 861224cd..7865dad6 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/Directory.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/Directory.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import java.io.DataInput;
import java.io.IOException;
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DirectoryEntry.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DirectoryEntry.java
similarity index 98%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DirectoryEntry.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DirectoryEntry.java
index 6b023762..5675d94f 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DirectoryEntry.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/DirectoryEntry.java
@@ -26,12 +26,12 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import javax.imageio.IIOException;
+import java.awt.*;
import java.io.DataInput;
import java.io.IOException;
-import java.awt.*;
/**
* DirectoryEntry
@@ -50,7 +50,7 @@ abstract class DirectoryEntry {
private int size;
private int offset;
- private DirectoryEntry() {
+ DirectoryEntry() {
}
public static DirectoryEntry read(final int pType, final DataInput pStream) throws IOException {
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/ICOImageReader.java
similarity index 97%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/ICOImageReader.java
index 9d228565..042ada42 100644
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReader.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/ICOImageReader.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import javax.imageio.spi.ImageReaderSpi;
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderSpi.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/ICOImageReaderSpi.java
similarity index 97%
rename from imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderSpi.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/ICOImageReaderSpi.java
index 2e5adfb0..ed01ea59 100755
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderSpi.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/ICOImageReaderSpi.java
@@ -26,7 +26,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.spi.ProviderInfo;
import com.twelvemonkeys.imageio.util.IIOUtil;
@@ -60,7 +60,7 @@ public final class ICOImageReaderSpi extends ImageReaderSpi {
"image/x-icon", // Common extension MIME
"image/ico" // Unofficial, but common
},
- "com.twelvemonkeys.imageio.plugins.ico.ICOImageReader",
+ "com.twelvemonkeys.imageio.plugins.bmp.ICOImageReader",
new Class[] {ImageInputStream.class},
null,
true, null, null, null, null,
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/RLE4Decoder.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/RLE4Decoder.java
similarity index 64%
rename from common/common-io/src/main/java/com/twelvemonkeys/io/enc/RLE4Decoder.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/RLE4Decoder.java
index fc902061..b3cef980 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/RLE4Decoder.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/RLE4Decoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, Harald Kuhr
+ * Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,32 +26,38 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.io.enc;
+package com.twelvemonkeys.imageio.plugins.bmp;
import java.io.InputStream;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
/**
* Implements 4 bit RLE decoding as specified by in the Windows BMP (aka DIB) file format.
*
*
* @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/RLE4Decoder.java#1 $
+ * @version $Id: RLE4Decoder.java#1 $
*/
-// TODO: Move to other package or make public
final class RLE4Decoder extends AbstractRLEDecoder {
+ final static int BIT_MASKS[] = {0xf0, 0x0f};
+ final static int BIT_SHIFTS[] = {4, 0};
- public RLE4Decoder(final int pWidth, final int pHeight) {
- super((pWidth + 1) / 2, pHeight);
+ public RLE4Decoder(final int width) {
+ super(width, 4);
}
- protected void decodeRow(final InputStream pInput) throws IOException {
+ protected void decodeRow(final InputStream stream) throws IOException {
+ // Just clear row now, and be done with it...
+ Arrays.fill(row, (byte) 0);
+
int deltaX = 0;
int deltaY = 0;
while (srcY >= 0) {
- int byte1 = pInput.read();
- int byte2 = checkEOF(pInput.read());
+ int byte1 = stream.read();
+ int byte2 = checkEOF(stream.read());
if (byte1 == 0x00) {
switch (byte2) {
@@ -59,64 +65,65 @@ final class RLE4Decoder extends AbstractRLEDecoder {
// End of line
// NOTE: Some BMPs have double EOLs..
if (srcX != 0) {
- srcX = row.length;
+ srcX = row.length * 2;
}
+
break;
+
case 0x01:
// End of bitmap
- srcX = row.length;
- srcY = 0;
+ srcX = row.length * 2;
+ srcY = -1;
break;
+
case 0x02:
// Delta
- deltaX = srcX + pInput.read();
- deltaY = srcY - checkEOF(pInput.read());
- srcX = row.length;
+ deltaX = srcX + stream.read();
+ deltaY = srcY + checkEOF(stream.read());
+
+ srcX = row.length * 2;
+
break;
+
default:
// Absolute mode
- // Copy the next byte2 (3..255) bytes from file to output
+ // Copy the next byte2 (3..255) nibbles from file to output
// Two samples are packed into one byte
- // If the number of bytes used to pack is not a mulitple of 2,
+ // If the *number of bytes* used to pack is not a multiple of 2,
// an additional padding byte is in the stream and must be skipped
boolean paddingByte = (((byte2 + 1) / 2) % 2) != 0;
- while (byte2 > 1) {
- int packed = checkEOF(pInput.read());
- row[srcX++] = (byte) packed;
- byte2 -= 2;
- }
- if (byte2 == 1) {
- // TODO: Half byte alignment? Seems to be ok...
- int packed = checkEOF(pInput.read());
- row[srcX++] = (byte) (packed & 0xf0);
+
+ int packed = 0;
+ for (int i = 0; i < byte2; i++) {
+ if (i % 2 == 0) {
+ packed = checkEOF(stream.read());
+ }
+
+ row[srcX / 2] |= (byte) (((packed & BIT_MASKS[i % 2]) >> BIT_SHIFTS[i % 2])<< BIT_SHIFTS[srcX % 2]);
+ srcX++;
}
+
if (paddingByte) {
- checkEOF(pInput.read());
+ checkEOF(stream.read());
}
- break;
}
}
else {
// Encoded mode
// Replicate the two samples in byte2 as many times as byte1 says
- while (byte1 > 1) {
- row[srcX++] = (byte) byte2;
- byte1 -= 2;
- }
-
- if (byte1 == 1) {
- // TODO: Half byte alignment? Seems to be ok...
- row[srcX++] = (byte) (byte2 & 0xf0);
+ for (int i = 0; i < byte1; i++) {
+ row[srcX / 2] |= (byte) (((byte2 & BIT_MASKS[i % 2]) >> BIT_SHIFTS[i % 2]) << BIT_SHIFTS[srcX % 2]);
+ srcX++;
}
}
// If we're done with a complete row, copy the data
- if (srcX == row.length) {
+ if (srcX >= row.length * 2) {
// Move to new position, either absolute (delta) or next line
if (deltaX != 0 || deltaY != 0) {
- srcX = (deltaX + 1) / 2;
+ srcX = deltaX;
- if (deltaY > srcY) {
+ if (deltaY != srcY) {
srcY = deltaY;
break;
}
@@ -124,9 +131,12 @@ final class RLE4Decoder extends AbstractRLEDecoder {
deltaX = 0;
deltaY = 0;
}
+ else if (srcY == -1) {
+ break;
+ }
else {
srcX = 0;
- srcY--;
+ srcY++;
break;
}
}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/RLE8Decoder.java b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/RLE8Decoder.java
similarity index 72%
rename from common/common-io/src/main/java/com/twelvemonkeys/io/enc/RLE8Decoder.java
rename to imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/RLE8Decoder.java
index 7529ba29..c4d38eab 100644
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/RLE8Decoder.java
+++ b/imageio/imageio-bmp/src/main/java/com/twelvemonkeys/imageio/plugins/bmp/RLE8Decoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, Harald Kuhr
+ * Copyright (c) 2014, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,32 +26,31 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.twelvemonkeys.io.enc;
+package com.twelvemonkeys.imageio.plugins.bmp;
import java.io.InputStream;
import java.io.IOException;
+import java.util.Arrays;
/**
* Implements 8 bit RLE decoding as specified by in the Windows BMP (aka DIB) file format.
*
*
* @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/RLE8Decoder.java#1 $
+ * @version $Id: RLE8Decoder.java#1 $
*/
-// TODO: Move to other package or make public
final class RLE8Decoder extends AbstractRLEDecoder {
-
- public RLE8Decoder(final int pWidth, final int pHeight) {
- super(pWidth, pHeight);
+ public RLE8Decoder(final int width) {
+ super(width, 8);
}
- protected void decodeRow(final InputStream pInput) throws IOException {
+ protected void decodeRow(final InputStream stream) throws IOException {
int deltaX = 0;
int deltaY = 0;
while (srcY >= 0) {
- int byte1 = pInput.read();
- int byte2 = checkEOF(pInput.read());
+ int byte1 = stream.read();
+ int byte2 = checkEOF(stream.read());
if (byte1 == 0x00) {
switch (byte2) {
@@ -59,29 +58,47 @@ final class RLE8Decoder extends AbstractRLEDecoder {
// End of line
// NOTE: Some BMPs have double EOLs..
if (srcX != 0) {
+ Arrays.fill(row, srcX, row.length, (byte) 0);
srcX = row.length;
}
+
break;
+
case 0x01:
// End of bitmap
+ Arrays.fill(row, srcX, row.length, (byte) 0);
srcX = row.length;
- srcY = 0;
+ srcY = -1; // TODO: Do we need to allow reading more (and thus re-introduce height parameter)..?
+
break;
+
case 0x02:
// Delta
- deltaX = srcX + pInput.read();
- deltaY = srcY - checkEOF(pInput.read());
- srcX = row.length;
+ deltaX = srcX + stream.read();
+ deltaY = srcY + checkEOF(stream.read());
+
+ Arrays.fill(row, srcX, deltaX, (byte) 0);
+
+ // TODO: Handle x delta inline!
+// if (deltaY != srcY) {
+ srcX = row.length;
+// }
+
break;
+
default:
// Absolute mode
// Copy the next byte2 (3..255) bytes from file to output
+ // If the number bytes is not a multiple of 2,
+ // an additional padding byte is in the stream and must be skipped
boolean paddingByte = (byte2 % 2) != 0;
+
while (byte2-- > 0) {
- row[srcX++] = (byte) checkEOF(pInput.read());
+ row[srcX++] = (byte) checkEOF(stream.read());
}
+
if (paddingByte) {
- checkEOF(pInput.read());
+ checkEOF(stream.read());
}
}
}
@@ -95,21 +112,25 @@ final class RLE8Decoder extends AbstractRLEDecoder {
}
// If we're done with a complete row, copy the data
- if (srcX == row.length) {
-
+ if (srcX >= row.length) {
// Move to new position, either absolute (delta) or next line
if (deltaX != 0 || deltaY != 0) {
srcX = deltaX;
+
if (deltaY != srcY) {
srcY = deltaY;
break;
}
+
deltaX = 0;
deltaY = 0;
}
+ else if (srcY == -1) {
+ break;
+ }
else {
srcX = 0;
- srcY--;
+ srcY++;
break;
}
}
diff --git a/imageio/imageio-bmp/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/imageio/imageio-bmp/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi
new file mode 100755
index 00000000..f2a7b76e
--- /dev/null
+++ b/imageio/imageio-bmp/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi
@@ -0,0 +1,3 @@
+com.twelvemonkeys.imageio.plugins.bmp.BMPImageReaderSpi
+com.twelvemonkeys.imageio.plugins.bmp.CURImageReaderSpi
+com.twelvemonkeys.imageio.plugins.bmp.ICOImageReaderSpi
diff --git a/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/BMPImageReaderTest.java b/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/BMPImageReaderTest.java
new file mode 100755
index 00000000..8f44135a
--- /dev/null
+++ b/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/BMPImageReaderTest.java
@@ -0,0 +1,163 @@
+package com.twelvemonkeys.imageio.plugins.bmp;
+
+import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
+import org.junit.Test;
+
+import javax.imageio.ImageReader;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.spi.ImageReaderSpi;
+import java.awt.*;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * BMPImageReaderTest
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: BMPImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
+ */
+public class BMPImageReaderTest extends ImageReaderAbstractTestCase {
+ protected List getTestData() {
+ return Arrays.asList(
+ // BMP Suite "Good"
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal1.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal1bg.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal1wb.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal4.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal4rle.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8-0.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8nonsquare.bmp"), new Dimension(127, 32)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8os2.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8rle.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8topdown.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8v4.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8v5.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8w124.bmp"), new Dimension(124, 61)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8w125.bmp"), new Dimension(125, 62)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/pal8w126.bmp"), new Dimension(126, 63)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/rgb16-565.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/rgb16-565pal.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/rgb16.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/rgb24.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/rgb24pal.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/rgb32.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/g/rgb32bf.bmp"), new Dimension(127, 64)),
+
+ // BMP Suite "Questionable"
+ new TestData(getClassLoaderResource("/bmpsuite/q/pal1p1.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/pal2.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/pal4rletrns.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/pal8offs.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/pal8os2sp.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/pal8os2v2.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/pal8os2v2-16.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/pal8oversizepal.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/pal8rletrns.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgb16-231.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgba16-4444.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgb24jpeg.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgb24largepal.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgb24lprof.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgb24png.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgb24prof.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgb32-111110.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgb32fakealpha.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgba32abf.bmp"), new Dimension(127, 64)),
+ new TestData(getClassLoaderResource("/bmpsuite/q/rgba32.bmp"), new Dimension(127, 64)),
+
+ // OS/2 samples
+ new TestData(getClassLoaderResource("/os2/money-2-(os2).bmp"), new Dimension(455, 341)),
+ new TestData(getClassLoaderResource("/os2/money-16-(os2).bmp"), new Dimension(455, 341)),
+ new TestData(getClassLoaderResource("/os2/money-256-(os2).bmp"), new Dimension(455, 341)),
+ new TestData(getClassLoaderResource("/os2/money-24bit-os2.bmp"), new Dimension(455, 341)),
+
+ // Vaious other samples
+ new TestData(getClassLoaderResource("/bmp/Blue Lace 16.bmp"), new Dimension(48, 48)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_mono.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_4.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_4.rle"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_8.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_8.rle"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_8-IM.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_gray.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_16.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_16_bitmask444.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_16_bitmask555.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_16_bitmask565.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_24.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_32.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_32_bitmask888.bmp"), new Dimension(301, 331)),
+ new TestData(getClassLoaderResource("/bmp/blauesglas_32_bitmask888_reversed.bmp"), new Dimension(301, 331))
+ );
+ }
+
+ protected ImageReaderSpi createProvider() {
+ return new BMPImageReaderSpi();
+ }
+
+ @Override
+ protected BMPImageReader createReader() {
+ return new BMPImageReader(createProvider());
+ }
+
+ protected Class getReaderClass() {
+ return BMPImageReader.class;
+ }
+
+ protected List getFormatNames() {
+ return Arrays.asList("bmp");
+ }
+
+ protected List getSuffixes() {
+ return Arrays.asList("bmp", "rle");
+ }
+
+ protected List getMIMETypes() {
+ return Arrays.asList("image/bmp");
+ }
+
+ @Override
+ @Test
+ public void testGetTypeSpecifiers() throws IOException {
+ final ImageReader reader = createReader();
+ for (TestData data : getTestData()) {
+ reader.setInput(data.getInputStream());
+
+ ImageTypeSpecifier rawType = reader.getRawImageType(0);
+
+ // As the JPEGImageReader we delegate to returns null for YCbCr, we'll have to do the same
+ if (rawType == null && data.getInput().toString().contains("jpeg")) {
+ continue;
+ }
+ assertNotNull(rawType);
+
+ Iterator types = reader.getImageTypes(0);
+
+ assertNotNull(types);
+ assertTrue(types.hasNext());
+
+ // TODO: This might fail even though the specifiers are obviously equal, if the
+ // color spaces they use are not the SAME instance, as ColorSpace uses identity equals
+ // and Interleaved ImageTypeSpecifiers are only equal if color spaces are equal...
+ boolean rawFound = false;
+ while (types.hasNext()) {
+ ImageTypeSpecifier type = types.next();
+ if (type.equals(rawType)) {
+ rawFound = true;
+ break;
+ }
+ }
+
+ assertTrue("ImageTypeSepcifier from getRawImageType should be in the iterator from getImageTypes", rawFound);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/imageio/imageio-ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderTestCase.java b/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/CURImageReaderTest.java
similarity index 93%
rename from imageio/imageio-ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderTestCase.java
rename to imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/CURImageReaderTest.java
index 8a6fc296..45a20c02 100755
--- a/imageio/imageio-ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/CURImageReaderTestCase.java
+++ b/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/CURImageReaderTest.java
@@ -1,4 +1,4 @@
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import org.junit.Ignore;
@@ -15,13 +15,13 @@ import java.util.List;
import static org.junit.Assert.*;
/**
- * CURImageReaderTestCase
+ * CURImageReaderTest
*
* @author Harald Kuhr
* @author last modified by $Author: haraldk$
- * @version $Id: CURImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
+ * @version $Id: CURImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
-public class CURImageReaderTestCase extends ImageReaderAbstractTestCase {
+public class CURImageReaderTest extends ImageReaderAbstractTestCase {
protected List getTestData() {
return Arrays.asList(
new TestData(getClassLoaderResource("/cur/hand.cur"), new Dimension(32, 32)),
diff --git a/imageio/imageio-ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderTestCase.java b/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/ICOImageReaderTest.java
similarity index 91%
rename from imageio/imageio-ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderTestCase.java
rename to imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/ICOImageReaderTest.java
index 4ccb4e43..858a26a5 100755
--- a/imageio/imageio-ico/src/test/java/com/twelvemonkeys/imageio/plugins/ico/ICOImageReaderTestCase.java
+++ b/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/ICOImageReaderTest.java
@@ -1,4 +1,4 @@
-package com.twelvemonkeys.imageio.plugins.ico;
+package com.twelvemonkeys.imageio.plugins.bmp;
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import org.junit.Ignore;
@@ -11,13 +11,13 @@ import java.util.Arrays;
import java.util.List;
/**
- * ICOImageReaderTestCase
+ * ICOImageReaderTest
*
* @author Harald Kuhr
* @author last modified by $Author: haraldk$
- * @version $Id: ICOImageReaderTestCase.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
+ * @version $Id: ICOImageReaderTest.java,v 1.0 Apr 1, 2008 10:39:17 PM haraldk Exp$
*/
-public class ICOImageReaderTestCase extends ImageReaderAbstractTestCase {
+public class ICOImageReaderTest extends ImageReaderAbstractTestCase {
protected List getTestData() {
return Arrays.asList(
new TestData(
diff --git a/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/RLE4DecoderTest.java b/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/RLE4DecoderTest.java
new file mode 100644
index 00000000..b3348a3c
--- /dev/null
+++ b/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/RLE4DecoderTest.java
@@ -0,0 +1,160 @@
+package com.twelvemonkeys.imageio.plugins.bmp;
+
+import com.twelvemonkeys.io.enc.Decoder;
+import com.twelvemonkeys.io.enc.DecoderStream;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class RLE4DecoderTest {
+
+ public static final byte[] RLE_ENCODED = new byte[]{
+ 0x03, 0x04, 0x05, 0x06, 0x00, 0x06, 0x45, 0x56, 0x67, 0x00, 0x04, 0x78, 0x00, 0x02, 0x05, 0x01,
+ 0x04, 0x78, 0x00, 0x00, 0x09, 0x1E, 0x00, 0x01,
+ };
+
+ public static final byte[] DECODED = new byte[]{
+ 0x04, 0x00, 0x60, 0x60, 0x45, 0x56, 0x67, 0x78, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, (byte) 0x87, (byte) 0x80, 0x00, 0x00,
+ 0x1E, 0x1E, 0x1E, 0x1E, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ @Test
+ public void decodeBuffer() throws IOException {
+ // Setup:
+ InputStream rleStream = getClass().getResourceAsStream("/bmpsuite/g/pal4rle.bmp");
+ long rleOffset = 102;
+
+ InputStream plainSream = getClass().getResourceAsStream("/bmpsuite/g/pal4.bmp");
+ long plainOffset = 102;
+
+ skipFully(rleStream, rleOffset);
+ skipFully(plainSream, plainOffset);
+
+ ByteBuffer decoded = ByteBuffer.allocate(64);
+ Decoder decoder = new RLE4Decoder(127);
+
+ ByteBuffer plain = ByteBuffer.allocate(64);
+ ReadableByteChannel channel = Channels.newChannel(plainSream);
+
+ for (int i = 0; i < 64; i++) {
+ int d = decoder.decode(rleStream, decoded);
+ decoded.rewind();
+ int r = channel.read(plain);
+ plain.rewind();
+
+ assertEquals("Difference at line " + i, r, d);
+ assertArrayEquals("Difference at line " + i, plain.array(), decoded.array());
+ }
+ }
+
+ @Test
+ public void decodeStream() throws IOException {
+ // Setup:
+ InputStream rleStream = getClass().getResourceAsStream("/bmpsuite/g/pal4rle.bmp");
+ long rleOffset = 102;
+
+ InputStream plainSream = getClass().getResourceAsStream("/bmpsuite/g/pal4.bmp");
+ long plainOffset = 102;
+
+ skipFully(rleStream, rleOffset);
+ skipFully(plainSream, plainOffset);
+
+ InputStream decoded = new DecoderStream(rleStream, new RLE4Decoder(127));
+
+ int pos = 0;
+ while (true) {
+ int expected = plainSream.read();
+ assertEquals("Differs at " + pos, expected, decoded.read());
+
+ if (expected < 0) {
+ break;
+ }
+
+ pos++;
+ }
+
+ assertEquals(64 * 64, pos);
+ }
+
+ @Test
+ public void decodeStreamWeird() throws IOException {
+ // Setup:
+ InputStream rleStream = getClass().getResourceAsStream("/bmp/blauesglas_4.rle");
+ long rleOffset = 118;
+
+ InputStream plainSream = getClass().getResourceAsStream("/bmp/blauesglas_4.bmp");
+ long plainOffset = 118;
+
+ skipFully(rleStream, rleOffset);
+ skipFully(plainSream, plainOffset);
+
+ InputStream decoded = new DecoderStream(rleStream, new RLE4Decoder(301));
+
+ int pos = 0;
+ int w = ((301 * 4 + 31) / 32) * 4;
+ int h = 331;
+ int size = w * h;
+
+ while (pos < size) {
+ int expected = plainSream.read();
+ int actual = decoded.read();
+// assertEquals("Differs at " + pos, expected, actual);
+ // Seems the initial RLE-encoding screwed up on some pixels...
+
+ if (expected < 0) {
+ break;
+ }
+
+ pos++;
+ }
+
+ // Rubbish assertion...
+ assertEquals(size, pos);
+ }
+
+ @Test
+ public void decodeExampleW27() throws IOException {
+ Decoder decoder = new RLE4Decoder(27); // Can be 27, 28, 29, 30, 31 or 32, and should all be the same.
+ ByteBuffer buffer = ByteBuffer.allocate(1024);
+ int count = decoder.decode(new ByteArrayInputStream(RLE_ENCODED), buffer);
+
+ assertArrayEquals(DECODED, Arrays.copyOfRange(buffer.array(), 0, count));
+ }
+
+ @Test
+ public void decodeExampleW28to31() throws IOException {
+ for (int i = 28; i < 32; i++) {
+ Decoder decoder = new RLE4Decoder(i); // Can be 27, 28, 29, 30, 31 or 32, and should all be the same.
+ ByteBuffer buffer = ByteBuffer.allocate(64);
+ int count = decoder.decode(new ByteArrayInputStream(RLE_ENCODED), buffer);
+
+ assertArrayEquals(DECODED, Arrays.copyOfRange(buffer.array(), 0, count));
+ }
+ }
+
+ @Test
+ public void decodeExampleW32() throws IOException {
+ Decoder decoder = new RLE4Decoder(32); // Can be 27, 28, 29, 30, 31 or 32, and should all be the same.
+ ByteBuffer buffer = ByteBuffer.allocate(1024);
+ int count = decoder.decode(new ByteArrayInputStream(RLE_ENCODED), buffer);
+
+ assertArrayEquals(DECODED, Arrays.copyOfRange(buffer.array(), 0, count));
+ }
+
+ private void skipFully(final InputStream stream, final long toSkip) throws IOException {
+ long skipped = 0;
+ while (skipped < toSkip) {
+ skipped += stream.skip(toSkip - skipped);
+ }
+ }
+}
diff --git a/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/RLE8DecoderTest.java b/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/RLE8DecoderTest.java
new file mode 100644
index 00000000..e353d33d
--- /dev/null
+++ b/imageio/imageio-bmp/src/test/java/com/twelvemonkeys/imageio/plugins/bmp/RLE8DecoderTest.java
@@ -0,0 +1,127 @@
+package com.twelvemonkeys.imageio.plugins.bmp;
+
+import com.twelvemonkeys.io.enc.Decoder;
+import com.twelvemonkeys.io.enc.DecoderStream;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class RLE8DecoderTest {
+
+ public static final byte[] RLE_ENCODED = new byte[]{
+ 0x03, 0x04, 0x05, 0x06, 0x00, 0x03, 0x45, 0x56, 0x67, 0x00, 0x02, 0x78,
+ 0x00, 0x02, 0x05, 0x01,
+ 0x02, 0x78, 0x00, 0x00, // EOL
+ 0x09, 0x1E,
+ 0x00, 0x01, // EOF
+ };
+
+ public static final byte[] DECODED = new byte[]{
+ 0x04, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x45, 0x56, 0x67, 0x78, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x78,
+ 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ @Test
+ public void decodeBuffer() throws IOException {
+ // Setup:
+ InputStream rleStream = getClass().getResourceAsStream("/bmpsuite/g/pal8rle.bmp");
+ long rleOffset = 1062;
+
+ InputStream plainSream = getClass().getResourceAsStream("/bmpsuite/g/pal8.bmp");
+ long plainOffset = 1062;
+
+ skipFully(rleStream, rleOffset);
+ skipFully(plainSream, plainOffset);
+
+ ByteBuffer decoded = ByteBuffer.allocate(128);
+ Decoder decoder = new RLE8Decoder(127);
+
+ ByteBuffer plain = ByteBuffer.allocate(128);
+ ReadableByteChannel channel = Channels.newChannel(plainSream);
+
+ for (int i = 0; i < 64; i++) {
+ int d = decoder.decode(rleStream, decoded);
+ decoded.rewind();
+ int r = channel.read(plain);
+ plain.rewind();
+
+ assertEquals(r, d);
+ assertArrayEquals(plain.array(), decoded.array());
+ }
+ }
+
+ @Test
+ public void decodeStream() throws IOException {
+ // Setup:
+ InputStream rleStream = getClass().getResourceAsStream("/bmpsuite/g/pal8rle.bmp");
+ long rleOffset = 1062;
+
+ InputStream plainSream = getClass().getResourceAsStream("/bmpsuite/g/pal8.bmp");
+ long plainOffset = 1062;
+
+ skipFully(rleStream, rleOffset);
+ skipFully(plainSream, plainOffset);
+
+ InputStream decoded = new DecoderStream(rleStream, new RLE8Decoder(127));
+
+ int pos = 0;
+ while (true) {
+ int expected = plainSream.read();
+
+ assertEquals("Differs at " + pos, expected, decoded.read());
+
+ if (expected < 0) {
+ break;
+ }
+
+ pos++;
+ }
+
+ assertEquals(128 * 64, pos);
+ }
+ @Test
+ public void decodeExampleW20() throws IOException {
+ Decoder decoder = new RLE8Decoder(20);
+ ByteBuffer buffer = ByteBuffer.allocate(1024);
+ int count = decoder.decode(new ByteArrayInputStream(RLE_ENCODED), buffer);
+
+ assertArrayEquals(DECODED, Arrays.copyOfRange(buffer.array(), 0, count));
+ }
+
+// @Test
+// public void decodeExampleW28to31() throws IOException {
+// for (int i = 28; i < 32; i++) {
+// Decoder decoder = new RLE8Decoder(i); // Can be 27, 28, 29, 30, 31 or 32, and should all be the same.
+// ByteBuffer buffer = ByteBuffer.allocate(64);
+// int count = decoder.decode(new ByteArrayInputStream(RLE_ENCODED), buffer);
+//
+// assertArrayEquals(DECODED, Arrays.copyOfRange(buffer.array(), 0, count));
+// }
+// }
+//
+// @Test
+// public void decodeExampleW32() throws IOException {
+// Decoder decoder = new RLE8Decoder(32); // Can be 27, 28, 29, 30, 31 or 32, and should all be the same.
+// ByteBuffer buffer = ByteBuffer.allocate(1024);
+// int count = decoder.decode(new ByteArrayInputStream(RLE_ENCODED), buffer);
+//
+// assertArrayEquals(DECODED, Arrays.copyOfRange(buffer.array(), 0, count));
+// }
+
+ private void skipFully(final InputStream stream, final long toSkip) throws IOException {
+ long skipped = 0;
+ while (skipped < toSkip) {
+ skipped += stream.skip(toSkip - skipped);
+ }
+ }
+}
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/Blue Lace 16.bmp b/imageio/imageio-bmp/src/test/resources/bmp/Blue Lace 16.bmp
new file mode 100755
index 00000000..680bc27a
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/Blue Lace 16.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16.bmp
new file mode 100755
index 00000000..f1b09908
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16_bitmask444.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16_bitmask444.bmp
new file mode 100755
index 00000000..584af94d
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16_bitmask444.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16_bitmask555.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16_bitmask555.bmp
new file mode 100755
index 00000000..f1b09908
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16_bitmask555.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16_bitmask565.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16_bitmask565.bmp
new file mode 100755
index 00000000..1bd00ff6
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_16_bitmask565.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_24.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_24.bmp
new file mode 100755
index 00000000..903176f1
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_24.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_32.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_32.bmp
new file mode 100755
index 00000000..40f0b3d7
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_32.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_32_bitmask888.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_32_bitmask888.bmp
new file mode 100755
index 00000000..fc06d157
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_32_bitmask888.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_32_bitmask888_reversed.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_32_bitmask888_reversed.bmp
new file mode 100755
index 00000000..71a5ed6e
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_32_bitmask888_reversed.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_4-IM.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_4-IM.bmp
new file mode 100755
index 00000000..ca8db5ce
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_4-IM.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_4.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_4.bmp
new file mode 100755
index 00000000..84012e48
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_4.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_4.rle b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_4.rle
new file mode 100755
index 00000000..e0efa7c2
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_4.rle differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_8-IM.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_8-IM.bmp
new file mode 100755
index 00000000..5a820296
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_8-IM.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_8.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_8.bmp
new file mode 100755
index 00000000..2027b80c
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_8.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_8.rle b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_8.rle
new file mode 100755
index 00000000..dea7e8d7
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_8.rle differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_gray.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_gray.bmp
new file mode 100755
index 00000000..4a2a9483
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_gray.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_mono.bmp b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_mono.bmp
new file mode 100755
index 00000000..8c978c3f
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/blauesglas_mono.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmp/lena512.bmp b/imageio/imageio-bmp/src/test/resources/bmp/lena512.bmp
new file mode 100644
index 00000000..5641e759
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmp/lena512.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badbitcount.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badbitcount.bmp
new file mode 100644
index 00000000..d4fa4e8b
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badbitcount.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badbitssize.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badbitssize.bmp
new file mode 100644
index 00000000..0a99a605
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badbitssize.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/baddens1.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/baddens1.bmp
new file mode 100644
index 00000000..a6150a6f
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/baddens1.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/baddens2.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/baddens2.bmp
new file mode 100644
index 00000000..f2c1dfb6
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/baddens2.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badfilesize.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badfilesize.bmp
new file mode 100644
index 00000000..da52cb51
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badfilesize.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badheadersize.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badheadersize.bmp
new file mode 100644
index 00000000..2a4083a6
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badheadersize.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badpalettesize.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badpalettesize.bmp
new file mode 100644
index 00000000..7d9d1b74
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badpalettesize.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badplanes.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badplanes.bmp
new file mode 100644
index 00000000..92d2855b
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badplanes.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badrle.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badrle.bmp
new file mode 100644
index 00000000..cbf8fdc2
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badrle.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badwidth.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badwidth.bmp
new file mode 100644
index 00000000..9fca005d
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/badwidth.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/pal8badindex.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/pal8badindex.bmp
new file mode 100644
index 00000000..efe16c05
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/pal8badindex.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/reallybig.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/reallybig.bmp
new file mode 100644
index 00000000..101e0b49
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/reallybig.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/rletopdown.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/rletopdown.bmp
new file mode 100644
index 00000000..21a909fd
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/rletopdown.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/b/shortfile.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/shortfile.bmp
new file mode 100644
index 00000000..73960797
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/b/shortfile.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal1.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal1.bmp
new file mode 100644
index 00000000..4776f827
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal1.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal1bg.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal1bg.bmp
new file mode 100644
index 00000000..466d0ba7
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal1bg.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal1wb.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal1wb.bmp
new file mode 100644
index 00000000..56cb9320
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal1wb.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal4.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal4.bmp
new file mode 100644
index 00000000..7fd36303
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal4.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal4rle.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal4rle.bmp
new file mode 100644
index 00000000..a5672aeb
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal4rle.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8-0.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8-0.bmp
new file mode 100644
index 00000000..ab8815a3
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8-0.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8.bmp
new file mode 100644
index 00000000..96b2f866
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8nonsquare.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8nonsquare.bmp
new file mode 100644
index 00000000..0aa8de04
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8nonsquare.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8os2.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8os2.bmp
new file mode 100644
index 00000000..14901b38
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8os2.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8rle.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8rle.bmp
new file mode 100644
index 00000000..d4310149
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8rle.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8topdown.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8topdown.bmp
new file mode 100644
index 00000000..4b2f8e01
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8topdown.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8v4.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8v4.bmp
new file mode 100644
index 00000000..7064be31
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8v4.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8v5.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8v5.bmp
new file mode 100644
index 00000000..c54647a3
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8v5.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8w124.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8w124.bmp
new file mode 100644
index 00000000..b7cc2d8b
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8w124.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8w125.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8w125.bmp
new file mode 100644
index 00000000..06efed74
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8w125.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8w126.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8w126.bmp
new file mode 100644
index 00000000..112aa9fe
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/pal8w126.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb16-565.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb16-565.bmp
new file mode 100644
index 00000000..c03a2797
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb16-565.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb16-565pal.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb16-565pal.bmp
new file mode 100644
index 00000000..e7632e34
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb16-565pal.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb16.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb16.bmp
new file mode 100644
index 00000000..6bfe47af
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb16.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb24.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb24.bmp
new file mode 100644
index 00000000..40f8bb09
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb24.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb24pal.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb24pal.bmp
new file mode 100644
index 00000000..102e971d
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb24pal.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb32.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb32.bmp
new file mode 100644
index 00000000..5d57eaae
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb32.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb32bf.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb32bf.bmp
new file mode 100644
index 00000000..20fa9a13
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/g/rgb32bf.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal1p1.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal1p1.bmp
new file mode 100644
index 00000000..b68321c4
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal1p1.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal2.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal2.bmp
new file mode 100644
index 00000000..983e9fa9
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal2.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal4rletrns.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal4rletrns.bmp
new file mode 100644
index 00000000..58994e92
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal4rletrns.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8offs.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8offs.bmp
new file mode 100644
index 00000000..8673e974
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8offs.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8os2sp.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8os2sp.bmp
new file mode 100644
index 00000000..e532c898
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8os2sp.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8os2v2-16.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8os2v2-16.bmp
new file mode 100644
index 00000000..95a1d234
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8os2v2-16.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8os2v2.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8os2v2.bmp
new file mode 100644
index 00000000..1324a40d
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8os2v2.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8oversizepal.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8oversizepal.bmp
new file mode 100644
index 00000000..93b8187c
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8oversizepal.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8rletrns.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8rletrns.bmp
new file mode 100644
index 00000000..a2af88d8
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/pal8rletrns.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb16-231.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb16-231.bmp
new file mode 100644
index 00000000..6300f69f
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb16-231.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24jpeg.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24jpeg.bmp
new file mode 100644
index 00000000..87d73d75
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24jpeg.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24largepal.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24largepal.bmp
new file mode 100644
index 00000000..d5e418c2
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24largepal.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24lprof.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24lprof.bmp
new file mode 100644
index 00000000..b868b88f
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24lprof.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24png.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24png.bmp
new file mode 100644
index 00000000..e87ec7ad
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24png.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24prof.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24prof.bmp
new file mode 100644
index 00000000..627e676e
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb24prof.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb32-111110.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb32-111110.bmp
new file mode 100644
index 00000000..ec07d89b
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb32-111110.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb32fakealpha.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb32fakealpha.bmp
new file mode 100644
index 00000000..cb544da5
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgb32fakealpha.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgba16-4444.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgba16-4444.bmp
new file mode 100644
index 00000000..051ff235
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgba16-4444.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgba32.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgba32.bmp
new file mode 100644
index 00000000..829c7c7e
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgba32.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgba32abf.bmp b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgba32abf.bmp
new file mode 100644
index 00000000..d9bb0189
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/bmpsuite/q/rgba32abf.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/bmpsuite/readme.txt b/imageio/imageio-bmp/src/test/resources/bmpsuite/readme.txt
new file mode 100644
index 00000000..31723c13
--- /dev/null
+++ b/imageio/imageio-bmp/src/test/resources/bmpsuite/readme.txt
@@ -0,0 +1,19 @@
+BMP sample files are taken from BMP Suite Version 2.3 by Jason Summers
+(http://entropymine.com/jason/bmpsuite/).
+
+From the readme.txt:
+
+"Image files generated by this program are not covered by this license, and are
+in the public domain.
+
+Files in the "g" directory I consider to be "good":
+Your program should definitely support them if it claims to support BMP.
+
+Files in the "q" directory I consider to be "questionable":
+These may be really unusual, or technically violate the documentation in some
+way, or I may be unsure about their validity.
+
+Files in the "b" directory I consider to be "bad":
+These are clearly invalid or unreasonable. Make sure your program doesn't crash
+when reading them."
+
diff --git a/imageio/imageio-ico/src/test/resources/cur/hand.cur b/imageio/imageio-bmp/src/test/resources/cur/hand.cur
similarity index 100%
rename from imageio/imageio-ico/src/test/resources/cur/hand.cur
rename to imageio/imageio-bmp/src/test/resources/cur/hand.cur
diff --git a/imageio/imageio-ico/src/test/resources/cur/zoom.cur b/imageio/imageio-bmp/src/test/resources/cur/zoom.cur
similarity index 100%
rename from imageio/imageio-ico/src/test/resources/cur/zoom.cur
rename to imageio/imageio-bmp/src/test/resources/cur/zoom.cur
diff --git a/imageio/imageio-ico/src/test/resources/ico/JavaCup.ico b/imageio/imageio-bmp/src/test/resources/ico/JavaCup.ico
similarity index 100%
rename from imageio/imageio-ico/src/test/resources/ico/JavaCup.ico
rename to imageio/imageio-bmp/src/test/resources/ico/JavaCup.ico
diff --git a/imageio/imageio-ico/src/test/resources/ico/colors.ico b/imageio/imageio-bmp/src/test/resources/ico/colors.ico
similarity index 100%
rename from imageio/imageio-ico/src/test/resources/ico/colors.ico
rename to imageio/imageio-bmp/src/test/resources/ico/colors.ico
diff --git a/imageio/imageio-ico/src/test/resources/ico/down.ico b/imageio/imageio-bmp/src/test/resources/ico/down.ico
similarity index 100%
rename from imageio/imageio-ico/src/test/resources/ico/down.ico
rename to imageio/imageio-bmp/src/test/resources/ico/down.ico
diff --git a/imageio/imageio-ico/src/test/resources/ico/favicon.ico b/imageio/imageio-bmp/src/test/resources/ico/favicon.ico
similarity index 100%
rename from imageio/imageio-ico/src/test/resources/ico/favicon.ico
rename to imageio/imageio-bmp/src/test/resources/ico/favicon.ico
diff --git a/imageio/imageio-ico/src/test/resources/ico/joypad.ico b/imageio/imageio-bmp/src/test/resources/ico/joypad.ico
similarity index 100%
rename from imageio/imageio-ico/src/test/resources/ico/joypad.ico
rename to imageio/imageio-bmp/src/test/resources/ico/joypad.ico
diff --git a/imageio/imageio-bmp/src/test/resources/os2/money-16-(os2).bmp b/imageio/imageio-bmp/src/test/resources/os2/money-16-(os2).bmp
new file mode 100644
index 00000000..6dbc0e97
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/os2/money-16-(os2).bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/os2/money-2-(os2).bmp b/imageio/imageio-bmp/src/test/resources/os2/money-2-(os2).bmp
new file mode 100644
index 00000000..59b9e799
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/os2/money-2-(os2).bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/os2/money-24bit-os2.bmp b/imageio/imageio-bmp/src/test/resources/os2/money-24bit-os2.bmp
new file mode 100644
index 00000000..b33f5dff
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/os2/money-24bit-os2.bmp differ
diff --git a/imageio/imageio-bmp/src/test/resources/os2/money-256-(os2).bmp b/imageio/imageio-bmp/src/test/resources/os2/money-256-(os2).bmp
new file mode 100644
index 00000000..564524d0
Binary files /dev/null and b/imageio/imageio-bmp/src/test/resources/os2/money-256-(os2).bmp differ
diff --git a/imageio/imageio-bmp/todo.txt b/imageio/imageio-bmp/todo.txt
new file mode 100644
index 00000000..24750857
--- /dev/null
+++ b/imageio/imageio-bmp/todo.txt
@@ -0,0 +1,3 @@
+Rework slightly:
+ - BMPImageReader should work without Directory/DirectoryEntry!
+ - ICO/CURImageReader could delegate individual reading to the BMPImageReader
diff --git a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBHeader.java b/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBHeader.java
deleted file mode 100755
index 974fba5d..00000000
--- a/imageio/imageio-ico/src/main/java/com/twelvemonkeys/imageio/plugins/ico/DIBHeader.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (c) 2009, 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.ico;
-
-import javax.imageio.IIOException;
-import java.io.DataInput;
-import java.io.IOException;
-
-/**
- * Represents the DIB (Device Independent Bitmap) Information header structure.
- *
- * @author Harald Kuhr
- * @author last modified by $Author: haraldk$
- * @version $Id: DIBHeader.java,v 1.0 May 5, 2009 10:45:31 AM haraldk Exp$
- * @see BMP file format (Wikipedia)
- */
-abstract class DIBHeader {
- protected int size;
-
- protected int width;
-
- // NOTE: If a bitmask is present, this value includes the height of the mask
- // (so often header.height = entry.height * 2)
- protected int height;
-
- protected int planes;
- protected int bitCount;
-
- /**
- * 0 = BI_RGB: No compression
- * 1 = BI_RLE8: 8 bit RLE Compression (8 bit only)
- * 2 = BI_RLE4: 4 bit RLE Compression (4 bit only)
- * 3 = BI_BITFIELDS: No compression (16 & 32 bit only)
- */
- protected int compression;
-
- // May be 0 if not known
- protected int imageSize;
-
- protected int xPixelsPerMeter;
- protected int yPixelsPerMeter;
-
- protected int colorsUsed;
-
- // 0 means all colors are important
- protected int colorsImportant;
-
- protected DIBHeader() {
- }
-
- public static DIBHeader read(final DataInput pStream) throws IOException {
- int size = pStream.readInt();
-
- // ICO always uses the Microsoft Windows V3 DIB header, which is 40 bytes
- DIBHeader header = createHeader(size);
- header.read(size, pStream);
-
- return header;
- }
-
- private static DIBHeader createHeader(final int pSize) throws IOException {
- switch (pSize) {
- case DIB.OS2_V1_HEADER_SIZE:
- case DIB.OS2_V2_HEADER_SIZE:
- throw new IIOException(String.format("OS/2 Bitmap Information Header (size: %s) not supported", pSize));
- case DIB.WINDOWS_V3_HEADER_SIZE:
- return new WindowsV3DIBHeader();
- case DIB.WINDOWS_V4_HEADER_SIZE:
- case DIB.WINDOWS_V5_HEADER_SIZE:
- throw new IIOException(String.format("Windows Bitmap Information Header (size: %s) not supported", pSize));
- default:
- throw new IIOException(String.format("Unknown Bitmap Information Header (size: %s)", pSize));
- }
- }
-
- protected abstract void read(int pSize, DataInput pStream) throws IOException;
-
- public final int getSize() {
- return size;
- }
-
- public final int getWidth() {
- return width;
- }
-
- public final int getHeight() {
- return height;
- }
-
- public final int getPlanes() {
- return planes;
- }
-
- public final int getBitCount() {
- return bitCount;
- }
-
- public int getCompression() {
- return compression;
- }
-
- public int getImageSize() {
- return imageSize;
- }
-
- public int getXPixelsPerMeter() {
- return xPixelsPerMeter;
- }
-
- public int getYPixelsPerMeter() {
- return yPixelsPerMeter;
- }
-
- public int getColorsUsed() {
- return colorsUsed;
- }
-
- public int getColorsImportant() {
- return colorsImportant;
- }
-
- public String toString() {
- return String.format(
- "%s: size: %d bytes, " +
- "width: %d, height: %d, planes: %d, bit count: %d, compression: %d, " +
- "image size: %d%s, " +
- "X pixels per m: %d, Y pixels per m: %d, " +
- "colors used: %d, colors important: %d%s",
- getClass().getSimpleName(),
- getSize(), getWidth(), getHeight(), getPlanes(), getBitCount(), getCompression(),
- getImageSize(), (getImageSize() == 0 ? " (unknown)" : ""),
- getXPixelsPerMeter(), getYPixelsPerMeter(),
- getColorsUsed(), getColorsImportant(), (getColorsImportant() == 0 ? " (all)" : "")
- );
- }
-
- /**
- * Represents the DIB (Device Independent Bitmap) Windows V3 Bitmap Information header structure.
- * This is the common format for persistent DIB structures, even if Windows
- * may use the later versions at run-time.
- *
- *
- * @author Harald Kuhr
- * @version $Id: DIBHeader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$
- * @see BMP file format (Wikipedia)
- */
- static final class WindowsV3DIBHeader extends DIBHeader {
- protected void read(final int pSize, final DataInput pStream) throws IOException {
- if (pSize != DIB.WINDOWS_V3_HEADER_SIZE) {
- throw new IIOException(String.format("Size: %s !=: %s", pSize, DIB.WINDOWS_V3_HEADER_SIZE));
- }
-
- size = pSize;
-
- width = pStream.readInt();
- height = pStream.readInt();
-
- planes = pStream.readUnsignedShort();
- bitCount = pStream.readUnsignedShort();
- compression = pStream.readInt();
-
- imageSize = pStream.readInt();
-
- xPixelsPerMeter = pStream.readInt();
- yPixelsPerMeter = pStream.readInt();
-
- colorsUsed = pStream.readInt();
- colorsImportant = pStream.readInt();
- }
- }
-}
\ No newline at end of file
diff --git a/imageio/imageio-ico/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi b/imageio/imageio-ico/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi
deleted file mode 100755
index e06c9b76..00000000
--- a/imageio/imageio-ico/src/main/resources/META-INF/services/javax.imageio.spi.ImageReaderSpi
+++ /dev/null
@@ -1,2 +0,0 @@
-com.twelvemonkeys.imageio.plugins.ico.ICOImageReaderSpi
-com.twelvemonkeys.imageio.plugins.ico.CURImageReaderSpi
\ No newline at end of file
diff --git a/imageio/imageio-ico/todo.txt b/imageio/imageio-ico/todo.txt
deleted file mode 100755
index 39697cb9..00000000
--- a/imageio/imageio-ico/todo.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-- Support all DIB formats?
-- Use Sun's BMP metadata format for image metadata + separate stream metadata?
diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterSpi.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterSpi.java
new file mode 100644
index 00000000..81ccfc5f
--- /dev/null
+++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageWriterSpi.java
@@ -0,0 +1,89 @@
+/*
+ * 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.tiff;
+
+import com.twelvemonkeys.imageio.spi.ProviderInfo;
+import com.twelvemonkeys.imageio.util.IIOUtil;
+
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.ImageWriter;
+import javax.imageio.spi.ImageWriterSpi;
+import javax.imageio.stream.ImageOutputStream;
+import java.io.IOException;
+import java.util.Locale;
+
+/**
+ * TIFFImageWriterSpi
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: TIFFImageWriterSpi.java,v 1.0 18.09.13 12:46 haraldk Exp$
+ */
+public final class TIFFImageWriterSpi extends ImageWriterSpi {
+ // TODO: Implement canEncodeImage better
+
+ public TIFFImageWriterSpi() {
+ this(IIOUtil.getProviderInfo(TIFFImageWriterSpi.class));
+ }
+
+ private TIFFImageWriterSpi(final ProviderInfo providerInfo) {
+ super(
+ providerInfo.getVendorName(), providerInfo.getVersion(),
+ new String[] {"tiff", "TIFF"},
+ new String[] {"tif"},
+ new String[] {"image/tiff", "image/x-tiff"},
+ "com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriter",
+ new Class>[] {ImageOutputStream.class},
+ new String[] {"com.twelvemonkeys.imageio.plugins.tiff.TIFFImageReaderSpi"},
+ 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 canEncodeImage(ImageTypeSpecifier type) {
+ // TODO: Test bit depths compatibility
+
+ return true;
+ }
+
+ @Override
+ public ImageWriter createWriterInstance(Object extension) throws IOException {
+ return new TIFFImageWriter(this);
+ }
+
+ @Override
+ public String getDescription(Locale locale) {
+ return "Aldus/Adobe Tagged Image File Format (TIFF) image writer";
+ }
+}
diff --git a/imageio/pom.xml b/imageio/pom.xml
index 12089180..88e541c6 100644
--- a/imageio/pom.xml
+++ b/imageio/pom.xml
@@ -28,7 +28,7 @@
imageio-metadata
- imageio-ico
+ imageio-bmp
imageio-icns
imageio-iff
imageio-jpeg