--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,1858 @@
+/*
+ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.imageio.plugins.bmp;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.color.ICC_ColorSpace;
+import java.awt.color.ICC_Profile;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.ComponentSampleModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DataBufferUShort;
+import java.awt.image.DirectColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.MultiPixelPackedSampleModel;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
+
+import javax.imageio.IIOException;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.ImageReadParam;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.event.IIOReadProgressListener;
+import javax.imageio.event.IIOReadUpdateListener;
+import javax.imageio.event.IIOReadWarningListener;
+
+import java.io.*;
+import java.nio.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+import com.sun.imageio.plugins.common.ImageUtil;
+import com.sun.imageio.plugins.common.I18N;
+
+/** This class is the Java Image IO plugin reader for BMP images.
+ * It may subsample the image, clip the image, select sub-bands,
+ * and shift the decoded image origin if the proper decoding parameter
+ * are set in the provided <code>ImageReadParam</code>.
+ *
+ * This class supports Microsoft Windows Bitmap Version 3-5,
+ * as well as OS/2 Bitmap Version 2.x (for single-image BMP file).
+ */
+public class BMPImageReader extends ImageReader implements BMPConstants {
+ // BMP Image types
+ private static final int VERSION_2_1_BIT = 0;
+ private static final int VERSION_2_4_BIT = 1;
+ private static final int VERSION_2_8_BIT = 2;
+ private static final int VERSION_2_24_BIT = 3;
+
+ private static final int VERSION_3_1_BIT = 4;
+ private static final int VERSION_3_4_BIT = 5;
+ private static final int VERSION_3_8_BIT = 6;
+ private static final int VERSION_3_24_BIT = 7;
+
+ private static final int VERSION_3_NT_16_BIT = 8;
+ private static final int VERSION_3_NT_32_BIT = 9;
+
+ private static final int VERSION_4_1_BIT = 10;
+ private static final int VERSION_4_4_BIT = 11;
+ private static final int VERSION_4_8_BIT = 12;
+ private static final int VERSION_4_16_BIT = 13;
+ private static final int VERSION_4_24_BIT = 14;
+ private static final int VERSION_4_32_BIT = 15;
+
+ private static final int VERSION_3_XP_EMBEDDED = 16;
+ private static final int VERSION_4_XP_EMBEDDED = 17;
+ private static final int VERSION_5_XP_EMBEDDED = 18;
+
+ // BMP variables
+ private long bitmapFileSize;
+ private long bitmapOffset;
+ private long compression;
+ private long imageSize;
+ private byte palette[];
+ private int imageType;
+ private int numBands;
+ private boolean isBottomUp;
+ private int bitsPerPixel;
+ private int redMask, greenMask, blueMask, alphaMask;
+
+ private SampleModel sampleModel, originalSampleModel;
+ private ColorModel colorModel, originalColorModel;
+
+ /** The input stream where reads from */
+ private ImageInputStream iis = null;
+
+ /** Indicates whether the header is read. */
+ private boolean gotHeader = false;
+
+ /** The original image width. */
+ private int width;
+
+ /** The original image height. */
+ private int height;
+
+ /** The destination region. */
+ private Rectangle destinationRegion;
+
+ /** The source region. */
+ private Rectangle sourceRegion;
+
+ /** The metadata from the stream. */
+ private BMPMetadata metadata;
+
+ /** The destination image. */
+ private BufferedImage bi;
+
+ /** Indicates whether subsampled, subregion is required, and offset is
+ * defined
+ */
+ private boolean noTransform = true;
+
+ /** Indicates whether subband is selected. */
+ private boolean seleBand = false;
+
+ /** The scaling factors. */
+ private int scaleX, scaleY;
+
+ /** source and destination bands. */
+ private int[] sourceBands, destBands;
+
+ /** Constructs <code>BMPImageReader</code> from the provided
+ * <code>ImageReaderSpi</code>.
+ */
+ public BMPImageReader(ImageReaderSpi originator) {
+ super(originator);
+ }
+
+ /** Overrides the method defined in the superclass. */
+ public void setInput(Object input,
+ boolean seekForwardOnly,
+ boolean ignoreMetadata) {
+ super.setInput(input, seekForwardOnly, ignoreMetadata);
+ iis = (ImageInputStream) input; // Always works
+ if(iis != null)
+ iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ resetHeaderInfo();
+ }
+
+ /** Overrides the method defined in the superclass. */
+ public int getNumImages(boolean allowSearch) throws IOException {
+ if (iis == null) {
+ throw new IllegalStateException(I18N.getString("GetNumImages0"));
+ }
+ if (seekForwardOnly && allowSearch) {
+ throw new IllegalStateException(I18N.getString("GetNumImages1"));
+ }
+ return 1;
+ }
+
+ @Override
+ public int getWidth(int imageIndex) throws IOException {
+ checkIndex(imageIndex);
+ try {
+ readHeader();
+ } catch (IllegalArgumentException e) {
+ throw new IIOException(I18N.getString("BMPImageReader6"), e);
+ }
+ return width;
+ }
+
+ public int getHeight(int imageIndex) throws IOException {
+ checkIndex(imageIndex);
+ try {
+ readHeader();
+ } catch (IllegalArgumentException e) {
+ throw new IIOException(I18N.getString("BMPImageReader6"), e);
+ }
+ return height;
+ }
+
+ private void checkIndex(int imageIndex) {
+ if (imageIndex != 0) {
+ throw new IndexOutOfBoundsException(I18N.getString("BMPImageReader0"));
+ }
+ }
+
+ /**
+ * Process the image header.
+ *
+ * @exception IllegalStateException if source stream is not set.
+ *
+ * @exception IOException if image stream is corrupted.
+ *
+ * @exception IllegalArgumentException if the image stream does not contain
+ * a BMP image, or if a sample model instance to describe the
+ * image can not be created.
+ */
+ protected void readHeader() throws IOException, IllegalArgumentException {
+ if (gotHeader)
+ return;
+
+ if (iis == null) {
+ throw new IllegalStateException("Input source not set!");
+ }
+ int profileData = 0, profileSize = 0;
+
+ this.metadata = new BMPMetadata();
+ iis.mark();
+
+ // read and check the magic marker
+ byte[] marker = new byte[2];
+ iis.read(marker);
+ if (marker[0] != 0x42 || marker[1] != 0x4d)
+ throw new IllegalArgumentException(I18N.getString("BMPImageReader1"));
+
+ // Read file size
+ bitmapFileSize = iis.readUnsignedInt();
+ // skip the two reserved fields
+ iis.skipBytes(4);
+
+ // Offset to the bitmap from the beginning
+ bitmapOffset = iis.readUnsignedInt();
+ // End File Header
+
+ // Start BitmapCoreHeader
+ long size = iis.readUnsignedInt();
+
+ if (size == 12) {
+ width = iis.readShort();
+ height = iis.readShort();
+ } else {
+ width = iis.readInt();
+ height = iis.readInt();
+ }
+
+ metadata.width = width;
+ metadata.height = height;
+
+ int planes = iis.readUnsignedShort();
+ bitsPerPixel = iis.readUnsignedShort();
+
+ //metadata.colorPlane = planes;
+ metadata.bitsPerPixel = (short)bitsPerPixel;
+
+ // As BMP always has 3 rgb bands, except for Version 5,
+ // which is bgra
+ numBands = 3;
+
+ if (size == 12) {
+ // Windows 2.x and OS/2 1.x
+ metadata.bmpVersion = VERSION_2;
+
+ // Classify the image type
+ if (bitsPerPixel == 1) {
+ imageType = VERSION_2_1_BIT;
+ } else if (bitsPerPixel == 4) {
+ imageType = VERSION_2_4_BIT;
+ } else if (bitsPerPixel == 8) {
+ imageType = VERSION_2_8_BIT;
+ } else if (bitsPerPixel == 24) {
+ imageType = VERSION_2_24_BIT;
+ }
+
+ // Read in the palette
+ int numberOfEntries = (int)((bitmapOffset - 14 - size) / 3);
+ int sizeOfPalette = numberOfEntries*3;
+ palette = new byte[sizeOfPalette];
+ iis.readFully(palette, 0, sizeOfPalette);
+ metadata.palette = palette;
+ metadata.paletteSize = numberOfEntries;
+ } else {
+ compression = iis.readUnsignedInt();
+ imageSize = iis.readUnsignedInt();
+ long xPelsPerMeter = iis.readInt();
+ long yPelsPerMeter = iis.readInt();
+ long colorsUsed = iis.readUnsignedInt();
+ long colorsImportant = iis.readUnsignedInt();
+
+ metadata.compression = (int)compression;
+ metadata.xPixelsPerMeter = (int)xPelsPerMeter;
+ metadata.yPixelsPerMeter = (int)yPelsPerMeter;
+ metadata.colorsUsed = (int)colorsUsed;
+ metadata.colorsImportant = (int)colorsImportant;
+
+ if (size == 40) {
+ // Windows 3.x and Windows NT
+ switch((int)compression) {
+
+ case BI_JPEG:
+ case BI_PNG:
+ metadata.bmpVersion = VERSION_3;
+ imageType = VERSION_3_XP_EMBEDDED;
+ break;
+
+ case BI_RGB: // No compression
+ case BI_RLE8: // 8-bit RLE compression
+ case BI_RLE4: // 4-bit RLE compression
+
+ // Read in the palette
+ if (bitmapOffset < (size + 14)) {
+ throw new IIOException(I18N.getString("BMPImageReader7"));
+ }
+ int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
+ int sizeOfPalette = numberOfEntries * 4;
+ palette = new byte[sizeOfPalette];
+ iis.readFully(palette, 0, sizeOfPalette);
+
+ metadata.palette = palette;
+ metadata.paletteSize = numberOfEntries;
+
+ if (bitsPerPixel == 1) {
+ imageType = VERSION_3_1_BIT;
+ } else if (bitsPerPixel == 4) {
+ imageType = VERSION_3_4_BIT;
+ } else if (bitsPerPixel == 8) {
+ imageType = VERSION_3_8_BIT;
+ } else if (bitsPerPixel == 24) {
+ imageType = VERSION_3_24_BIT;
+ } else if (bitsPerPixel == 16) {
+ imageType = VERSION_3_NT_16_BIT;
+
+ redMask = 0x7C00;
+ greenMask = 0x3E0;
+ blueMask = (1 << 5) - 1;// 0x1F;
+ metadata.redMask = redMask;
+ metadata.greenMask = greenMask;
+ metadata.blueMask = blueMask;
+ } else if (bitsPerPixel == 32) {
+ imageType = VERSION_3_NT_32_BIT;
+ redMask = 0x00FF0000;
+ greenMask = 0x0000FF00;
+ blueMask = 0x000000FF;
+ metadata.redMask = redMask;
+ metadata.greenMask = greenMask;
+ metadata.blueMask = blueMask;
+ }
+
+ metadata.bmpVersion = VERSION_3;
+ break;
+
+ case BI_BITFIELDS:
+
+ if (bitsPerPixel == 16) {
+ imageType = VERSION_3_NT_16_BIT;
+ } else if (bitsPerPixel == 32) {
+ imageType = VERSION_3_NT_32_BIT;
+ }
+
+ // BitsField encoding
+ redMask = (int)iis.readUnsignedInt();
+ greenMask = (int)iis.readUnsignedInt();
+ blueMask = (int)iis.readUnsignedInt();
+ metadata.redMask = redMask;
+ metadata.greenMask = greenMask;
+ metadata.blueMask = blueMask;
+
+ if (colorsUsed != 0) {
+ // there is a palette
+ sizeOfPalette = (int)colorsUsed*4;
+ palette = new byte[sizeOfPalette];
+ iis.readFully(palette, 0, sizeOfPalette);
+
+ metadata.palette = palette;
+ metadata.paletteSize = (int)colorsUsed;
+ }
+ metadata.bmpVersion = VERSION_3_NT;
+
+ break;
+ default:
+ throw new
+ IIOException(I18N.getString("BMPImageReader2"));
+ }
+ } else if (size == 108 || size == 124) {
+ // Windows 4.x BMP
+ if (size == 108)
+ metadata.bmpVersion = VERSION_4;
+ else if (size == 124)
+ metadata.bmpVersion = VERSION_5;
+
+ // rgb masks, valid only if comp is BI_BITFIELDS
+ redMask = (int)iis.readUnsignedInt();
+ greenMask = (int)iis.readUnsignedInt();
+ blueMask = (int)iis.readUnsignedInt();
+ // Only supported for 32bpp BI_RGB argb
+ alphaMask = (int)iis.readUnsignedInt();
+ long csType = iis.readUnsignedInt();
+ int redX = iis.readInt();
+ int redY = iis.readInt();
+ int redZ = iis.readInt();
+ int greenX = iis.readInt();
+ int greenY = iis.readInt();
+ int greenZ = iis.readInt();
+ int blueX = iis.readInt();
+ int blueY = iis.readInt();
+ int blueZ = iis.readInt();
+ long gammaRed = iis.readUnsignedInt();
+ long gammaGreen = iis.readUnsignedInt();
+ long gammaBlue = iis.readUnsignedInt();
+
+ if (size == 124) {
+ metadata.intent = iis.readInt();
+ profileData = iis.readInt();
+ profileSize = iis.readInt();
+ iis.skipBytes(4);
+ }
+
+ metadata.colorSpace = (int)csType;
+
+ if (csType == LCS_CALIBRATED_RGB) {
+ // All the new fields are valid only for this case
+ metadata.redX = redX;
+ metadata.redY = redY;
+ metadata.redZ = redZ;
+ metadata.greenX = greenX;
+ metadata.greenY = greenY;
+ metadata.greenZ = greenZ;
+ metadata.blueX = blueX;
+ metadata.blueY = blueY;
+ metadata.blueZ = blueZ;
+ metadata.gammaRed = (int)gammaRed;
+ metadata.gammaGreen = (int)gammaGreen;
+ metadata.gammaBlue = (int)gammaBlue;
+ }
+
+ // Read in the palette
+ int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
+ int sizeOfPalette = numberOfEntries*4;
+ palette = new byte[sizeOfPalette];
+ iis.readFully(palette, 0, sizeOfPalette);
+ metadata.palette = palette;
+ metadata.paletteSize = numberOfEntries;
+
+ switch ((int)compression) {
+ case BI_JPEG:
+ case BI_PNG:
+ if (size == 108) {
+ imageType = VERSION_4_XP_EMBEDDED;
+ } else if (size == 124) {
+ imageType = VERSION_5_XP_EMBEDDED;
+ }
+ break;
+ default:
+ if (bitsPerPixel == 1) {
+ imageType = VERSION_4_1_BIT;
+ } else if (bitsPerPixel == 4) {
+ imageType = VERSION_4_4_BIT;
+ } else if (bitsPerPixel == 8) {
+ imageType = VERSION_4_8_BIT;
+ } else if (bitsPerPixel == 16) {
+ imageType = VERSION_4_16_BIT;
+ if ((int)compression == BI_RGB) {
+ redMask = 0x7C00;
+ greenMask = 0x3E0;
+ blueMask = 0x1F;
+ }
+ } else if (bitsPerPixel == 24) {
+ imageType = VERSION_4_24_BIT;
+ } else if (bitsPerPixel == 32) {
+ imageType = VERSION_4_32_BIT;
+ if ((int)compression == BI_RGB) {
+ redMask = 0x00FF0000;
+ greenMask = 0x0000FF00;
+ blueMask = 0x000000FF;
+ }
+ }
+
+ metadata.redMask = redMask;
+ metadata.greenMask = greenMask;
+ metadata.blueMask = blueMask;
+ metadata.alphaMask = alphaMask;
+ }
+ } else {
+ throw new
+ IIOException(I18N.getString("BMPImageReader3"));
+ }
+ }
+
+ if (height > 0) {
+ // bottom up image
+ isBottomUp = true;
+ } else {
+ // top down image
+ isBottomUp = false;
+ height = Math.abs(height);
+ }
+
+ // Reset Image Layout so there's only one tile.
+ //Define the color space
+ ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ if (metadata.colorSpace == PROFILE_LINKED ||
+ metadata.colorSpace == PROFILE_EMBEDDED) {
+
+ iis.mark();
+ iis.skipBytes(profileData - size);
+ byte[] profile = new byte[profileSize];
+ iis.readFully(profile, 0, profileSize);
+ iis.reset();
+
+ try {
+ if (metadata.colorSpace == PROFILE_LINKED &&
+ isLinkedProfileAllowed() &&
+ !isUncOrDevicePath(profile))
+ {
+ String path = new String(profile, "windows-1252");
+
+ colorSpace =
+ new ICC_ColorSpace(ICC_Profile.getInstance(path));
+ } else {
+ colorSpace =
+ new ICC_ColorSpace(ICC_Profile.getInstance(profile));
+ }
+ } catch (Exception e) {
+ colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+ }
+ }
+
+ if (bitsPerPixel == 0 ||
+ compression == BI_JPEG || compression == BI_PNG )
+ {
+ // the colorModel and sampleModel will be initialzed
+ // by the reader of embedded image
+ colorModel = null;
+ sampleModel = null;
+ } else if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
+ // When number of bitsPerPixel is <= 8, we use IndexColorModel.
+ numBands = 1;
+
+ if (bitsPerPixel == 8) {
+ int[] bandOffsets = new int[numBands];
+ for (int i = 0; i < numBands; i++) {
+ bandOffsets[i] = numBands -1 -i;
+ }
+ sampleModel =
+ new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
+ width, height,
+ numBands,
+ numBands * width,
+ bandOffsets);
+ } else {
+ // 1 and 4 bit pixels can be stored in a packed format.
+ sampleModel =
+ new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
+ width, height,
+ bitsPerPixel);
+ }
+
+ // Create IndexColorModel from the palette.
+ byte r[], g[], b[];
+ if (imageType == VERSION_2_1_BIT ||
+ imageType == VERSION_2_4_BIT ||
+ imageType == VERSION_2_8_BIT) {
+
+
+ size = palette.length/3;
+
+ if (size > 256) {
+ size = 256;
+ }
+
+ int off;
+ r = new byte[(int)size];
+ g = new byte[(int)size];
+ b = new byte[(int)size];
+ for (int i=0; i<(int)size; i++) {
+ off = 3 * i;
+ b[i] = palette[off];
+ g[i] = palette[off+1];
+ r[i] = palette[off+2];
+ }
+ } else {
+ size = palette.length/4;
+
+ if (size > 256) {
+ size = 256;
+ }
+
+ int off;
+ r = new byte[(int)size];
+ g = new byte[(int)size];
+ b = new byte[(int)size];
+ for (int i=0; i<size; i++) {
+ off = 4 * i;
+ b[i] = palette[off];
+ g[i] = palette[off+1];
+ r[i] = palette[off+2];
+ }
+ }
+
+ if (ImageUtil.isIndicesForGrayscale(r, g, b))
+ colorModel =
+ ImageUtil.createColorModel(null, sampleModel);
+ else
+ colorModel = new IndexColorModel(bitsPerPixel, (int)size, r, g, b);
+ } else if (bitsPerPixel == 16) {
+ numBands = 3;
+ sampleModel =
+ new SinglePixelPackedSampleModel(DataBuffer.TYPE_USHORT,
+ width, height,
+ new int[] {redMask, greenMask, blueMask});
+
+ colorModel =
+ new DirectColorModel(colorSpace,
+ 16, redMask, greenMask, blueMask, 0,
+ false, DataBuffer.TYPE_USHORT);
+
+ } else if (bitsPerPixel == 32) {
+ numBands = alphaMask == 0 ? 3 : 4;
+
+ // The number of bands in the SampleModel is determined by
+ // the length of the mask array passed in.
+ int[] bitMasks = numBands == 3 ?
+ new int[] {redMask, greenMask, blueMask} :
+ new int[] {redMask, greenMask, blueMask, alphaMask};
+
+ sampleModel =
+ new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
+ width, height,
+ bitMasks);
+
+ colorModel =
+ new DirectColorModel(colorSpace,
+ 32, redMask, greenMask, blueMask, alphaMask,
+ false, DataBuffer.TYPE_INT);
+ } else {
+ numBands = 3;
+ // Create SampleModel
+ int[] bandOffsets = new int[numBands];
+ for (int i = 0; i < numBands; i++) {
+ bandOffsets[i] = numBands -1 -i;
+ }
+
+ sampleModel =
+ new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
+ width, height,
+ numBands,
+ numBands * width,
+ bandOffsets);
+
+ colorModel =
+ ImageUtil.createColorModel(colorSpace, sampleModel);
+ }
+
+ originalSampleModel = sampleModel;
+ originalColorModel = colorModel;
+
+ // Reset to the start of bitmap; then jump to the
+ //start of image data
+ iis.reset();
+ iis.skipBytes(bitmapOffset);
+ gotHeader = true;
+ }
+
+ public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
+ throws IOException {
+ checkIndex(imageIndex);
+ try {
+ readHeader();
+ } catch (IllegalArgumentException e) {
+ throw new IIOException(I18N.getString("BMPImageReader6"), e);
+ }
+ ArrayList<ImageTypeSpecifier> list = new ArrayList<>(1);
+ list.add(new ImageTypeSpecifier(originalColorModel,
+ originalSampleModel));
+ return list.iterator();
+ }
+
+ public ImageReadParam getDefaultReadParam() {
+ return new ImageReadParam();
+ }
+
+ public IIOMetadata getImageMetadata(int imageIndex)
+ throws IOException {
+ checkIndex(imageIndex);
+ if (metadata == null) {
+ try {
+ readHeader();
+ } catch (IllegalArgumentException e) {
+ throw new IIOException(I18N.getString("BMPImageReader6"), e);
+ }
+ }
+ return metadata;
+ }
+
+ public IIOMetadata getStreamMetadata() throws IOException {
+ return null;
+ }
+
+ public boolean isRandomAccessEasy(int imageIndex) throws IOException {
+ checkIndex(imageIndex);
+ try {
+ readHeader();
+ } catch (IllegalArgumentException e) {
+ throw new IIOException(I18N.getString("BMPImageReader6"), e);
+ }
+ return metadata.compression == BI_RGB;
+ }
+
+ public BufferedImage read(int imageIndex, ImageReadParam param)
+ throws IOException {
+
+ if (iis == null) {
+ throw new IllegalStateException(I18N.getString("BMPImageReader5"));
+ }
+
+ checkIndex(imageIndex);
+ clearAbortRequest();
+ processImageStarted(imageIndex);
+
+ if (param == null)
+ param = getDefaultReadParam();
+
+ //read header
+ try {
+ readHeader();
+ } catch (IllegalArgumentException e) {
+ throw new IIOException(I18N.getString("BMPImageReader6"), e);
+ }
+
+ sourceRegion = new Rectangle(0, 0, 0, 0);
+ destinationRegion = new Rectangle(0, 0, 0, 0);
+
+ computeRegions(param, this.width, this.height,
+ param.getDestination(),
+ sourceRegion,
+ destinationRegion);
+
+ scaleX = param.getSourceXSubsampling();
+ scaleY = param.getSourceYSubsampling();
+
+ // If the destination band is set used it
+ sourceBands = param.getSourceBands();
+ destBands = param.getDestinationBands();
+
+ seleBand = (sourceBands != null) && (destBands != null);
+ noTransform =
+ destinationRegion.equals(new Rectangle(0, 0, width, height)) ||
+ seleBand;
+
+ if (!seleBand) {
+ sourceBands = new int[numBands];
+ destBands = new int[numBands];
+ for (int i = 0; i < numBands; i++)
+ destBands[i] = sourceBands[i] = i;
+ }
+
+ // If the destination is provided, then use it. Otherwise, create new one
+ bi = param.getDestination();
+
+ // Get the image data.
+ WritableRaster raster = null;
+
+ if (bi == null) {
+ if (sampleModel != null && colorModel != null) {
+ sampleModel =
+ sampleModel.createCompatibleSampleModel(destinationRegion.x +
+ destinationRegion.width,
+ destinationRegion.y +
+ destinationRegion.height);
+ if (seleBand)
+ sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
+ raster = Raster.createWritableRaster(sampleModel, new Point());
+ bi = new BufferedImage(colorModel, raster, false, null);
+ }
+ } else {
+ raster = bi.getWritableTile(0, 0);
+ sampleModel = bi.getSampleModel();
+ colorModel = bi.getColorModel();
+
+ noTransform &= destinationRegion.equals(raster.getBounds());
+ }
+
+ byte bdata[] = null; // buffer for byte data
+ short sdata[] = null; // buffer for short data
+ int idata[] = null; // buffer for int data
+
+ // the sampleModel can be null in case of embedded image
+ if (sampleModel != null) {
+ if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
+ bdata = ((DataBufferByte)raster.getDataBuffer()).getData();
+ else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
+ sdata = ((DataBufferUShort)raster.getDataBuffer()).getData();
+ else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
+ idata = ((DataBufferInt)raster.getDataBuffer()).getData();
+ }
+
+ // There should only be one tile.
+ switch(imageType) {
+
+ case VERSION_2_1_BIT:
+ // no compression
+ read1Bit(bdata);
+ break;
+
+ case VERSION_2_4_BIT:
+ // no compression
+ read4Bit(bdata);
+ break;
+
+ case VERSION_2_8_BIT:
+ // no compression
+ read8Bit(bdata);
+ break;
+
+ case VERSION_2_24_BIT:
+ // no compression
+ read24Bit(bdata);
+ break;
+
+ case VERSION_3_1_BIT:
+ // 1-bit images cannot be compressed.
+ read1Bit(bdata);
+ break;
+
+ case VERSION_3_4_BIT:
+ switch((int)compression) {
+ case BI_RGB:
+ read4Bit(bdata);
+ break;
+
+ case BI_RLE4:
+ readRLE4(bdata);
+ break;
+
+ default:
+ throw new
+ IIOException(I18N.getString("BMPImageReader1"));
+ }
+ break;
+
+ case VERSION_3_8_BIT:
+ switch((int)compression) {
+ case BI_RGB:
+ read8Bit(bdata);
+ break;
+
+ case BI_RLE8:
+ readRLE8(bdata);
+ break;
+
+ default:
+ throw new
+ IIOException(I18N.getString("BMPImageReader1"));
+ }
+
+ break;
+
+ case VERSION_3_24_BIT:
+ // 24-bit images are not compressed
+ read24Bit(bdata);
+ break;
+
+ case VERSION_3_NT_16_BIT:
+ read16Bit(sdata);
+ break;
+
+ case VERSION_3_NT_32_BIT:
+ read32Bit(idata);
+ break;
+
+ case VERSION_3_XP_EMBEDDED:
+ case VERSION_4_XP_EMBEDDED:
+ case VERSION_5_XP_EMBEDDED:
+ bi = readEmbedded((int)compression, bi, param);
+ break;
+
+ case VERSION_4_1_BIT:
+ read1Bit(bdata);
+ break;
+
+ case VERSION_4_4_BIT:
+ switch((int)compression) {
+
+ case BI_RGB:
+ read4Bit(bdata);
+ break;
+
+ case BI_RLE4:
+ readRLE4(bdata);
+ break;
+
+ default:
+ throw new
+ IIOException(I18N.getString("BMPImageReader1"));
+ }
+ break;
+
+ case VERSION_4_8_BIT:
+ switch((int)compression) {
+
+ case BI_RGB:
+ read8Bit(bdata);
+ break;
+
+ case BI_RLE8:
+ readRLE8(bdata);
+ break;
+
+ default:
+ throw new
+ IIOException(I18N.getString("BMPImageReader1"));
+ }
+ break;
+
+ case VERSION_4_16_BIT:
+ read16Bit(sdata);
+ break;
+
+ case VERSION_4_24_BIT:
+ read24Bit(bdata);
+ break;
+
+ case VERSION_4_32_BIT:
+ read32Bit(idata);
+ break;
+ }
+
+ if (abortRequested())
+ processReadAborted();
+ else
+ processImageComplete();
+
+ return bi;
+ }
+
+ public boolean canReadRaster() {
+ return true;
+ }
+
+ public Raster readRaster(int imageIndex,
+ ImageReadParam param) throws IOException {
+ BufferedImage bi = read(imageIndex, param);
+ return bi.getData();
+ }
+
+ private void resetHeaderInfo() {
+ gotHeader = false;
+ bi = null;
+ sampleModel = originalSampleModel = null;
+ colorModel = originalColorModel = null;
+ }
+
+ public void reset() {
+ super.reset();
+ iis = null;
+ resetHeaderInfo();
+ }
+
+ // Deal with 1 Bit images using IndexColorModels
+ private void read1Bit(byte[] bdata) throws IOException {
+ int bytesPerScanline = (width + 7) / 8;
+ int padding = bytesPerScanline % 4;
+ if (padding != 0) {
+ padding = 4 - padding;
+ }
+
+ int lineLength = bytesPerScanline + padding;
+
+ if (noTransform) {
+ int j = isBottomUp ? (height -1)*bytesPerScanline : 0;
+
+ for (int i=0; i<height; i++) {
+ if (abortRequested()) {
+ break;
+ }
+ iis.readFully(bdata, j, bytesPerScanline);
+ iis.skipBytes(padding);
+ j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
+ processImageUpdate(bi, 0, i,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F * i/destinationRegion.height);
+ }
+ } else {
+ byte[] buf = new byte[lineLength];
+ int lineStride =
+ ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
+
+ if (isBottomUp) {
+ int lastLine =
+ sourceRegion.y + (destinationRegion.height - 1) * scaleY;
+ iis.skipBytes(lineLength * (height - 1 - lastLine));
+ } else
+ iis.skipBytes(lineLength * sourceRegion.y);
+
+ int skipLength = lineLength * (scaleY - 1);
+
+ // cache the values to avoid duplicated computation
+ int[] srcOff = new int[destinationRegion.width];
+ int[] destOff = new int[destinationRegion.width];
+ int[] srcPos = new int[destinationRegion.width];
+ int[] destPos = new int[destinationRegion.width];
+
+ for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
+ i < destinationRegion.x + destinationRegion.width;
+ i++, j++, x += scaleX) {
+ srcPos[j] = x >> 3;
+ srcOff[j] = 7 - (x & 7);
+ destPos[j] = i >> 3;
+ destOff[j] = 7 - (i & 7);
+ }
+
+ int k = destinationRegion.y * lineStride;
+ if (isBottomUp)
+ k += (destinationRegion.height - 1) * lineStride;
+
+ for (int j = 0, y = sourceRegion.y;
+ j < destinationRegion.height; j++, y+=scaleY) {
+
+ if (abortRequested())
+ break;
+ iis.read(buf, 0, lineLength);
+ for (int i = 0; i < destinationRegion.width; i++) {
+ //get the bit and assign to the data buffer of the raster
+ int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
+ bdata[k + destPos[i]] |= v << destOff[i];
+ }
+
+ k += isBottomUp ? -lineStride : lineStride;
+ iis.skipBytes(skipLength);
+ processImageUpdate(bi, 0, j,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F*j/destinationRegion.height);
+ }
+ }
+ }
+
+ // Method to read a 4 bit BMP image data
+ private void read4Bit(byte[] bdata) throws IOException {
+
+ int bytesPerScanline = (width + 1) / 2;
+
+ // Padding bytes at the end of each scanline
+ int padding = bytesPerScanline % 4;
+ if (padding != 0)
+ padding = 4 - padding;
+
+ int lineLength = bytesPerScanline + padding;
+
+ if (noTransform) {
+ int j = isBottomUp ? (height -1) * bytesPerScanline : 0;
+
+ for (int i=0; i<height; i++) {
+ if (abortRequested()) {
+ break;
+ }
+ iis.readFully(bdata, j, bytesPerScanline);
+ iis.skipBytes(padding);
+ j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
+ processImageUpdate(bi, 0, i,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F * i/destinationRegion.height);
+ }
+ } else {
+ byte[] buf = new byte[lineLength];
+ int lineStride =
+ ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
+
+ if (isBottomUp) {
+ int lastLine =
+ sourceRegion.y + (destinationRegion.height - 1) * scaleY;
+ iis.skipBytes(lineLength * (height - 1 - lastLine));
+ } else
+ iis.skipBytes(lineLength * sourceRegion.y);
+
+ int skipLength = lineLength * (scaleY - 1);
+
+ // cache the values to avoid duplicated computation
+ int[] srcOff = new int[destinationRegion.width];
+ int[] destOff = new int[destinationRegion.width];
+ int[] srcPos = new int[destinationRegion.width];
+ int[] destPos = new int[destinationRegion.width];
+
+ for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
+ i < destinationRegion.x + destinationRegion.width;
+ i++, j++, x += scaleX) {
+ srcPos[j] = x >> 1;
+ srcOff[j] = (1 - (x & 1)) << 2;
+ destPos[j] = i >> 1;
+ destOff[j] = (1 - (i & 1)) << 2;
+ }
+
+ int k = destinationRegion.y * lineStride;
+ if (isBottomUp)
+ k += (destinationRegion.height - 1) * lineStride;
+
+ for (int j = 0, y = sourceRegion.y;
+ j < destinationRegion.height; j++, y+=scaleY) {
+
+ if (abortRequested())
+ break;
+ iis.read(buf, 0, lineLength);
+ for (int i = 0; i < destinationRegion.width; i++) {
+ //get the bit and assign to the data buffer of the raster
+ int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F;
+ bdata[k + destPos[i]] |= v << destOff[i];
+ }
+
+ k += isBottomUp ? -lineStride : lineStride;
+ iis.skipBytes(skipLength);
+ processImageUpdate(bi, 0, j,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F*j/destinationRegion.height);
+ }
+ }
+ }
+
+ // Method to read 8 bit BMP image data
+ private void read8Bit(byte[] bdata) throws IOException {
+
+ // Padding bytes at the end of each scanline
+ int padding = width % 4;
+ if (padding != 0) {
+ padding = 4 - padding;
+ }
+
+ int lineLength = width + padding;
+
+ if (noTransform) {
+ int j = isBottomUp ? (height -1) * width : 0;
+
+ for (int i=0; i<height; i++) {
+ if (abortRequested()) {
+ break;
+ }
+ iis.readFully(bdata, j, width);
+ iis.skipBytes(padding);
+ j += isBottomUp ? -width : width;
+ processImageUpdate(bi, 0, i,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F * i/destinationRegion.height);
+ }
+ } else {
+ byte[] buf = new byte[lineLength];
+ int lineStride =
+ ((ComponentSampleModel)sampleModel).getScanlineStride();
+
+ if (isBottomUp) {
+ int lastLine =
+ sourceRegion.y + (destinationRegion.height - 1) * scaleY;
+ iis.skipBytes(lineLength * (height - 1 - lastLine));
+ } else
+ iis.skipBytes(lineLength * sourceRegion.y);
+
+ int skipLength = lineLength * (scaleY - 1);
+
+ int k = destinationRegion.y * lineStride;
+ if (isBottomUp)
+ k += (destinationRegion.height - 1) * lineStride;
+ k += destinationRegion.x;
+
+ for (int j = 0, y = sourceRegion.y;
+ j < destinationRegion.height; j++, y+=scaleY) {
+
+ if (abortRequested())
+ break;
+ iis.read(buf, 0, lineLength);
+ for (int i = 0, m = sourceRegion.x;
+ i < destinationRegion.width; i++, m += scaleX) {
+ //get the bit and assign to the data buffer of the raster
+ bdata[k + i] = buf[m];
+ }
+
+ k += isBottomUp ? -lineStride : lineStride;
+ iis.skipBytes(skipLength);
+ processImageUpdate(bi, 0, j,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F*j/destinationRegion.height);
+ }
+ }
+ }
+
+ // Method to read 24 bit BMP image data
+ private void read24Bit(byte[] bdata) throws IOException {
+ // Padding bytes at the end of each scanline
+ // width * bitsPerPixel should be divisible by 32
+ int padding = width * 3 % 4;
+ if ( padding != 0)
+ padding = 4 - padding;
+
+ int lineStride = width * 3;
+ int lineLength = lineStride + padding;
+
+ if (noTransform) {
+ int j = isBottomUp ? (height -1) * width * 3 : 0;
+
+ for (int i=0; i<height; i++) {
+ if (abortRequested()) {
+ break;
+ }
+ iis.readFully(bdata, j, lineStride);
+ iis.skipBytes(padding);
+ j += isBottomUp ? -lineStride : lineStride;
+ processImageUpdate(bi, 0, i,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F * i/destinationRegion.height);
+ }
+ } else {
+ byte[] buf = new byte[lineLength];
+ lineStride =
+ ((ComponentSampleModel)sampleModel).getScanlineStride();
+
+ if (isBottomUp) {
+ int lastLine =
+ sourceRegion.y + (destinationRegion.height - 1) * scaleY;
+ iis.skipBytes(lineLength * (height - 1 - lastLine));
+ } else
+ iis.skipBytes(lineLength * sourceRegion.y);
+
+ int skipLength = lineLength * (scaleY - 1);
+
+ int k = destinationRegion.y * lineStride;
+ if (isBottomUp)
+ k += (destinationRegion.height - 1) * lineStride;
+ k += destinationRegion.x * 3;
+
+ for (int j = 0, y = sourceRegion.y;
+ j < destinationRegion.height; j++, y+=scaleY) {
+
+ if (abortRequested())
+ break;
+ iis.read(buf, 0, lineLength);
+ for (int i = 0, m = 3 * sourceRegion.x;
+ i < destinationRegion.width; i++, m += 3 * scaleX) {
+ //get the bit and assign to the data buffer of the raster
+ int n = 3 * i + k;
+ for (int b = 0; b < destBands.length; b++)
+ bdata[n + destBands[b]] = buf[m + sourceBands[b]];
+ }
+
+ k += isBottomUp ? -lineStride : lineStride;
+ iis.skipBytes(skipLength);
+ processImageUpdate(bi, 0, j,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F*j/destinationRegion.height);
+ }
+ }
+ }
+
+ private void read16Bit(short sdata[]) throws IOException {
+ // Padding bytes at the end of each scanline
+ // width * bitsPerPixel should be divisible by 32
+ int padding = width * 2 % 4;
+
+ if ( padding != 0)
+ padding = 4 - padding;
+
+ int lineLength = width + padding / 2;
+
+ if (noTransform) {
+ int j = isBottomUp ? (height -1) * width : 0;
+ for (int i=0; i<height; i++) {
+ if (abortRequested()) {
+ break;
+ }
+
+ iis.readFully(sdata, j, width);
+ iis.skipBytes(padding);
+
+ j += isBottomUp ? -width : width;
+ processImageUpdate(bi, 0, i,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F * i/destinationRegion.height);
+ }
+ } else {
+ short[] buf = new short[lineLength];
+ int lineStride =
+ ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
+
+ if (isBottomUp) {
+ int lastLine =
+ sourceRegion.y + (destinationRegion.height - 1) * scaleY;
+ iis.skipBytes(lineLength * (height - 1 - lastLine) << 1);
+ } else
+ iis.skipBytes(lineLength * sourceRegion.y << 1);
+
+ int skipLength = lineLength * (scaleY - 1) << 1;
+
+ int k = destinationRegion.y * lineStride;
+ if (isBottomUp)
+ k += (destinationRegion.height - 1) * lineStride;
+ k += destinationRegion.x;
+
+ for (int j = 0, y = sourceRegion.y;
+ j < destinationRegion.height; j++, y+=scaleY) {
+
+ if (abortRequested())
+ break;
+ iis.readFully(buf, 0, lineLength);
+ for (int i = 0, m = sourceRegion.x;
+ i < destinationRegion.width; i++, m += scaleX) {
+ //get the bit and assign to the data buffer of the raster
+ sdata[k + i] = buf[m];
+ }
+
+ k += isBottomUp ? -lineStride : lineStride;
+ iis.skipBytes(skipLength);
+ processImageUpdate(bi, 0, j,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F*j/destinationRegion.height);
+ }
+ }
+ }
+
+ private void read32Bit(int idata[]) throws IOException {
+ if (noTransform) {
+ int j = isBottomUp ? (height -1) * width : 0;
+
+ for (int i=0; i<height; i++) {
+ if (abortRequested()) {
+ break;
+ }
+ iis.readFully(idata, j, width);
+ j += isBottomUp ? -width : width;
+ processImageUpdate(bi, 0, i,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F * i/destinationRegion.height);
+ }
+ } else {
+ int[] buf = new int[width];
+ int lineStride =
+ ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
+
+ if (isBottomUp) {
+ int lastLine =
+ sourceRegion.y + (destinationRegion.height - 1) * scaleY;
+ iis.skipBytes(width * (height - 1 - lastLine) << 2);
+ } else
+ iis.skipBytes(width * sourceRegion.y << 2);
+
+ int skipLength = width * (scaleY - 1) << 2;
+
+ int k = destinationRegion.y * lineStride;
+ if (isBottomUp)
+ k += (destinationRegion.height - 1) * lineStride;
+ k += destinationRegion.x;
+
+ for (int j = 0, y = sourceRegion.y;
+ j < destinationRegion.height; j++, y+=scaleY) {
+
+ if (abortRequested())
+ break;
+ iis.readFully(buf, 0, width);
+ for (int i = 0, m = sourceRegion.x;
+ i < destinationRegion.width; i++, m += scaleX) {
+ //get the bit and assign to the data buffer of the raster
+ idata[k + i] = buf[m];
+ }
+
+ k += isBottomUp ? -lineStride : lineStride;
+ iis.skipBytes(skipLength);
+ processImageUpdate(bi, 0, j,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ processImageProgress(100.0F*j/destinationRegion.height);
+ }
+ }
+ }
+
+ private void readRLE8(byte bdata[]) throws IOException {
+ // If imageSize field is not provided, calculate it.
+ int imSize = (int)imageSize;
+ if (imSize == 0) {
+ imSize = (int)(bitmapFileSize - bitmapOffset);
+ }
+
+ int padding = 0;
+ // If width is not 32 bit aligned, then while uncompressing each
+ // scanline will have padding bytes, calculate the amount of padding
+ int remainder = width % 4;
+ if (remainder != 0) {
+ padding = 4 - remainder;
+ }
+
+ // Read till we have the whole image
+ byte values[] = new byte[imSize];
+ int bytesRead = 0;
+ iis.readFully(values, 0, imSize);
+
+ // Since data is compressed, decompress it
+ decodeRLE8(imSize, padding, values, bdata);
+ }
+
+ private void decodeRLE8(int imSize,
+ int padding,
+ byte[] values,
+ byte[] bdata) throws IOException {
+
+ byte val[] = new byte[width * height];
+ int count = 0, l = 0;
+ int value;
+ boolean flag = false;
+ int lineNo = isBottomUp ? height - 1 : 0;
+ int lineStride =
+ ((ComponentSampleModel)sampleModel).getScanlineStride();
+ int finished = 0;
+
+ while (count != imSize) {
+ value = values[count++] & 0xff;
+ if (value == 0) {
+ switch(values[count++] & 0xff) {
+
+ case 0:
+ // End-of-scanline marker
+ if (lineNo >= sourceRegion.y &&
+ lineNo < sourceRegion.y + sourceRegion.height) {
+ if (noTransform) {
+ int pos = lineNo * width;
+ for(int i = 0; i < width; i++)
+ bdata[pos++] = val[i];
+ processImageUpdate(bi, 0, lineNo,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ finished++;
+ } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
+ int currentLine = (lineNo - sourceRegion.y) / scaleY +
+ destinationRegion.y;
+ int pos = currentLine * lineStride;
+ pos += destinationRegion.x;
+ for (int i = sourceRegion.x;
+ i < sourceRegion.x + sourceRegion.width;
+ i += scaleX)
+ bdata[pos++] = val[i];
+ processImageUpdate(bi, 0, currentLine,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ finished++;
+ }
+ }
+ processImageProgress(100.0F * finished / destinationRegion.height);
+ lineNo += isBottomUp ? -1 : 1;
+ l = 0;
+
+ if (abortRequested()) {
+ flag = true;
+ }
+
+ break;
+
+ case 1:
+ // End-of-RLE marker
+ flag = true;
+ break;
+
+ case 2:
+ // delta or vector marker
+ int xoff = values[count++] & 0xff;
+ int yoff = values[count] & 0xff;
+ // Move to the position xoff, yoff down
+ l += xoff + yoff*width;
+ break;
+
+ default:
+ int end = values[count-1] & 0xff;
+ for (int i=0; i<end; i++) {
+ val[l++] = (byte)(values[count++] & 0xff);
+ }
+
+ // Whenever end pixels can fit into odd number of bytes,
+ // an extra padding byte will be present, so skip that.
+ if ((end & 1) == 1) {
+ count++;
+ }
+ }
+ } else {
+ for (int i=0; i<value; i++) {
+ val[l++] = (byte)(values[count] & 0xff);
+ }
+
+ count++;
+ }
+
+ // If End-of-RLE data, then exit the while loop
+ if (flag) {
+ break;
+ }
+ }
+ }
+
+ private void readRLE4(byte[] bdata) throws IOException {
+
+ // If imageSize field is not specified, calculate it.
+ int imSize = (int)imageSize;
+ if (imSize == 0) {
+ imSize = (int)(bitmapFileSize - bitmapOffset);
+ }
+
+ int padding = 0;
+ // If width is not 32 byte aligned, then while uncompressing each
+ // scanline will have padding bytes, calculate the amount of padding
+ int remainder = width % 4;
+ if (remainder != 0) {
+ padding = 4 - remainder;
+ }
+
+ // Read till we have the whole image
+ byte[] values = new byte[imSize];
+ iis.readFully(values, 0, imSize);
+
+ // Decompress the RLE4 compressed data.
+ decodeRLE4(imSize, padding, values, bdata);
+ }
+
+ private void decodeRLE4(int imSize,
+ int padding,
+ byte[] values,
+ byte[] bdata) throws IOException {
+ byte[] val = new byte[width];
+ int count = 0, l = 0;
+ int value;
+ boolean flag = false;
+ int lineNo = isBottomUp ? height - 1 : 0;
+ int lineStride =
+ ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
+ int finished = 0;
+
+ while (count != imSize) {
+
+ value = values[count++] & 0xFF;
+ if (value == 0) {
+
+
+ // Absolute mode
+ switch(values[count++] & 0xFF) {
+
+ case 0:
+ // End-of-scanline marker
+ // End-of-scanline marker
+ if (lineNo >= sourceRegion.y &&
+ lineNo < sourceRegion.y + sourceRegion.height) {
+ if (noTransform) {
+ int pos = lineNo * (width + 1 >> 1);
+ for(int i = 0, j = 0; i < width >> 1; i++)
+ bdata[pos++] =
+ (byte)((val[j++] << 4) | val[j++]);
+ if ((width & 1) == 1)
+ bdata[pos] |= val[width - 1] << 4;
+
+ processImageUpdate(bi, 0, lineNo,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ finished++;
+ } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
+ int currentLine = (lineNo - sourceRegion.y) / scaleY +
+ destinationRegion.y;
+ int pos = currentLine * lineStride;
+ pos += destinationRegion.x >> 1;
+ int shift = (1 - (destinationRegion.x & 1)) << 2;
+ for (int i = sourceRegion.x;
+ i < sourceRegion.x + sourceRegion.width;
+ i += scaleX) {
+ bdata[pos] |= val[i] << shift;
+ shift += 4;
+ if (shift == 4) {
+ pos++;
+ }
+ shift &= 7;
+ }
+ processImageUpdate(bi, 0, currentLine,
+ destinationRegion.width, 1, 1, 1,
+ new int[]{0});
+ finished++;
+ }
+ }
+ processImageProgress(100.0F * finished / destinationRegion.height);
+ lineNo += isBottomUp ? -1 : 1;
+ l = 0;
+
+ if (abortRequested()) {
+ flag = true;
+ }
+
+ break;
+
+ case 1:
+ // End-of-RLE marker
+ flag = true;
+ break;
+
+ case 2:
+ // delta or vector marker
+ int xoff = values[count++] & 0xFF;
+ int yoff = values[count] & 0xFF;
+ // Move to the position xoff, yoff down
+ l += xoff + yoff*width;
+ break;
+
+ default:
+ int end = values[count-1] & 0xFF;
+ for (int i=0; i<end; i++) {
+ val[l++] = (byte)(((i & 1) == 0) ? (values[count] & 0xf0) >> 4
+ : (values[count++] & 0x0f));
+ }
+
+ // When end is odd, the above for loop does not
+ // increment count, so do it now.
+ if ((end & 1) == 1) {
+ count++;
+ }
+
+ // Whenever end pixels can fit into odd number of bytes,
+ // an extra padding byte will be present, so skip that.
+ if ((((int)Math.ceil(end/2)) & 1) ==1 ) {
+ count++;
+ }
+ break;
+ }
+ } else {
+ // Encoded mode
+ int alternate[] = { (values[count] & 0xf0) >> 4,
+ values[count] & 0x0f };
+ for (int i=0; (i < value) && (l < width); i++) {
+ val[l++] = (byte)alternate[i & 1];
+ }
+
+ count++;
+ }
+
+ // If End-of-RLE data, then exit the while loop
+ if (flag) {
+ break;
+ }
+ }
+ }
+
+ /** Decodes the jpeg/png image embedded in the bitmap using any jpeg
+ * ImageIO-style plugin.
+ *
+ * @param bi The destination <code>BufferedImage</code>.
+ * @param bmpParam The <code>ImageReadParam</code> for decoding this
+ * BMP image. The parameters for subregion, band selection and
+ * subsampling are used in decoding the jpeg image.
+ */
+
+ private BufferedImage readEmbedded(int type,
+ BufferedImage bi, ImageReadParam bmpParam)
+ throws IOException {
+ String format;
+ switch(type) {
+ case BI_JPEG:
+ format = "JPEG";
+ break;
+ case BI_PNG:
+ format = "PNG";
+ break;
+ default:
+ throw new
+ IOException("Unexpected compression type: " + type);
+ }
+ ImageReader reader =
+ ImageIO.getImageReadersByFormatName(format).next();
+ if (reader == null) {
+ throw new RuntimeException(I18N.getString("BMPImageReader4") +
+ " " + format);
+ }
+ // prepare input
+ byte[] buff = new byte[(int)imageSize];
+ iis.read(buff);
+ reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(buff)));
+ if (bi == null) {
+ ImageTypeSpecifier embType = reader.getImageTypes(0).next();
+ bi = embType.createBufferedImage(destinationRegion.x +
+ destinationRegion.width,
+ destinationRegion.y +
+ destinationRegion.height);
+ }
+
+ reader.addIIOReadProgressListener(new EmbeddedProgressAdapter() {
+ public void imageProgress(ImageReader source,
+ float percentageDone)
+ {
+ processImageProgress(percentageDone);
+ }
+ });
+
+ reader.addIIOReadUpdateListener(new IIOReadUpdateListener() {
+ public void imageUpdate(ImageReader source,
+ BufferedImage theImage,
+ int minX, int minY,
+ int width, int height,
+ int periodX, int periodY,
+ int[] bands)
+ {
+ processImageUpdate(theImage, minX, minY,
+ width, height,
+ periodX, periodY, bands);
+ }
+ public void passComplete(ImageReader source,
+ BufferedImage theImage)
+ {
+ processPassComplete(theImage);
+ }
+ public void passStarted(ImageReader source,
+ BufferedImage theImage,
+ int pass,
+ int minPass, int maxPass,
+ int minX, int minY,
+ int periodX, int periodY,
+ int[] bands)
+ {
+ processPassStarted(theImage, pass, minPass, maxPass,
+ minX, minY, periodX, periodY,
+ bands);
+ }
+ public void thumbnailPassComplete(ImageReader source,
+ BufferedImage thumb) {}
+ public void thumbnailPassStarted(ImageReader source,
+ BufferedImage thumb,
+ int pass,
+ int minPass, int maxPass,
+ int minX, int minY,
+ int periodX, int periodY,
+ int[] bands) {}
+ public void thumbnailUpdate(ImageReader source,
+ BufferedImage theThumbnail,
+ int minX, int minY,
+ int width, int height,
+ int periodX, int periodY,
+ int[] bands) {}
+ });
+
+ reader.addIIOReadWarningListener(new IIOReadWarningListener() {
+ public void warningOccurred(ImageReader source, String warning)
+ {
+ processWarningOccurred(warning);
+ }
+ });
+
+ ImageReadParam param = reader.getDefaultReadParam();
+ param.setDestination(bi);
+ param.setDestinationBands(bmpParam.getDestinationBands());
+ param.setDestinationOffset(bmpParam.getDestinationOffset());
+ param.setSourceBands(bmpParam.getSourceBands());
+ param.setSourceRegion(bmpParam.getSourceRegion());
+ param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
+ bmpParam.getSourceYSubsampling(),
+ bmpParam.getSubsamplingXOffset(),
+ bmpParam.getSubsamplingYOffset());
+ reader.read(0, param);
+ return bi;
+ }
+
+ private class EmbeddedProgressAdapter implements IIOReadProgressListener {
+ public void imageComplete(ImageReader src) {}
+ public void imageProgress(ImageReader src, float percentageDone) {}
+ public void imageStarted(ImageReader src, int imageIndex) {}
+ public void thumbnailComplete(ImageReader src) {}
+ public void thumbnailProgress(ImageReader src, float percentageDone) {}
+ public void thumbnailStarted(ImageReader src, int iIdx, int tIdx) {}
+ public void sequenceComplete(ImageReader src) {}
+ public void sequenceStarted(ImageReader src, int minIndex) {}
+ public void readAborted(ImageReader src) {}
+ }
+
+ private static Boolean isLinkedProfileDisabled = null;
+
+ private static boolean isLinkedProfileAllowed() {
+ if (isLinkedProfileDisabled == null) {
+ PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ return Boolean.getBoolean("sun.imageio.plugins.bmp.disableLinkedProfiles");
+ }
+ };
+ isLinkedProfileDisabled = AccessController.doPrivileged(a);
+ }
+ return !isLinkedProfileDisabled;
+ }
+
+ private static Boolean isWindowsPlatform = null;
+
+ /**
+ * Verifies whether the byte array contans a unc path.
+ * Non-UNC path examples:
+ * c:\path\to\file - simple notation
+ * \\?\c:\path\to\file - long notation
+ *
+ * UNC path examples:
+ * \\server\share - a UNC path in simple notation
+ * \\?\UNC\server\share - a UNC path in long notation
+ * \\.\some\device - a path to device.
+ */
+ private static boolean isUncOrDevicePath(byte[] p) {
+ if (isWindowsPlatform == null) {
+ PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ String osname = System.getProperty("os.name");
+ return (osname != null &&
+ osname.toLowerCase().startsWith("win"));
+ }
+ };
+ isWindowsPlatform = AccessController.doPrivileged(a);
+ }
+
+ if (!isWindowsPlatform) {
+ /* no need for the check on platforms except windows */
+ return false;
+ }
+
+ /* normalize prefix of the path */
+ if (p[0] == '/') p[0] = '\\';
+ if (p[1] == '/') p[1] = '\\';
+ if (p[3] == '/') p[3] = '\\';
+
+
+ if ((p[0] == '\\') && (p[1] == '\\')) {
+ if ((p[2] == '?') && (p[3] == '\\')) {
+ // long path: whether unc or local
+ return ((p[4] == 'U' || p[4] == 'u') &&
+ (p[5] == 'N' || p[5] == 'n') &&
+ (p[6] == 'C' || p[6] == 'c'));
+ } else {
+ // device path or short unc notation
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+}