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