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