0001: /*
0002: * $RCSfile: BMPImageReader.java,v $
0003: *
0004: *
0005: * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
0006: *
0007: * Redistribution and use in source and binary forms, with or without
0008: * modification, are permitted provided that the following conditions
0009: * are met:
0010: *
0011: * - Redistribution of source code must retain the above copyright
0012: * notice, this list of conditions and the following disclaimer.
0013: *
0014: * - Redistribution in binary form must reproduce the above copyright
0015: * notice, this list of conditions and the following disclaimer in
0016: * the documentation and/or other materials provided with the
0017: * distribution.
0018: *
0019: * Neither the name of Sun Microsystems, Inc. or the names of
0020: * contributors may be used to endorse or promote products derived
0021: * from this software without specific prior written permission.
0022: *
0023: * This software is provided "AS IS," without a warranty of any
0024: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0025: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0026: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0027: * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0028: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0029: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0030: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0031: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0032: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0033: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0034: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0035: * POSSIBILITY OF SUCH DAMAGES.
0036: *
0037: * You acknowledge that this software is not designed or intended for
0038: * use in the design, construction, operation or maintenance of any
0039: * nuclear facility.
0040: *
0041: * $Revision: 1.2 $
0042: * $Date: 2006/04/14 21:29:14 $
0043: * $State: Exp $
0044: */
0045:
0046: package com.sun.media.imageioimpl.plugins.bmp;
0047:
0048: import java.awt.Point;
0049: import java.awt.Rectangle;
0050: import java.awt.Transparency;
0051: import java.awt.color.ColorSpace;
0052: import java.awt.color.ICC_ColorSpace;
0053: import java.awt.color.ICC_Profile;
0054: import java.awt.image.BufferedImage;
0055: import java.awt.image.ColorModel;
0056: import java.awt.image.ComponentColorModel;
0057: import java.awt.image.ComponentSampleModel;
0058: import java.awt.image.DataBuffer;
0059: import java.awt.image.DataBufferByte;
0060: import java.awt.image.DataBufferInt;
0061: import java.awt.image.DataBufferUShort;
0062: import java.awt.image.DirectColorModel;
0063: import java.awt.image.IndexColorModel;
0064: import java.awt.image.MultiPixelPackedSampleModel;
0065: import java.awt.image.PixelInterleavedSampleModel;
0066: import java.awt.image.Raster;
0067: import java.awt.image.SampleModel;
0068: import java.awt.image.SinglePixelPackedSampleModel;
0069: import java.awt.image.WritableRaster;
0070:
0071: import javax.imageio.IIOException;
0072: import javax.imageio.ImageIO;
0073: import javax.imageio.ImageReader;
0074: import javax.imageio.ImageReadParam;
0075: import javax.imageio.ImageTypeSpecifier;
0076: import javax.imageio.metadata.IIOMetadata;
0077: import javax.imageio.spi.ImageReaderSpi;
0078: import javax.imageio.stream.ImageInputStream;
0079: import javax.imageio.event.IIOReadProgressListener;
0080: import javax.imageio.event.IIOReadUpdateListener;
0081: import javax.imageio.event.IIOReadWarningListener;
0082:
0083: import java.io.*;
0084: import java.nio.*;
0085: import java.util.ArrayList;
0086: import java.util.Iterator;
0087: import java.util.StringTokenizer;
0088:
0089: import com.sun.media.imageioimpl.common.ImageUtil;
0090:
0091: /** This class is the Java Image IO plugin reader for BMP images.
0092: * It may subsample the image, clip the image, select sub-bands,
0093: * and shift the decoded image origin if the proper decoding parameter
0094: * are set in the provided <code>ImageReadParam</code>.
0095: *
0096: * This class supports Microsoft Windows Bitmap Version 3-5,
0097: * as well as OS/2 Bitmap Version 2.x (for single-image BMP file).
0098: */
0099: public class BMPImageReader extends ImageReader implements BMPConstants {
0100: // BMP Image types
0101: private static final int VERSION_2_1_BIT = 0;
0102: private static final int VERSION_2_4_BIT = 1;
0103: private static final int VERSION_2_8_BIT = 2;
0104: private static final int VERSION_2_24_BIT = 3;
0105:
0106: private static final int VERSION_3_1_BIT = 4;
0107: private static final int VERSION_3_4_BIT = 5;
0108: private static final int VERSION_3_8_BIT = 6;
0109: private static final int VERSION_3_24_BIT = 7;
0110:
0111: private static final int VERSION_3_NT_16_BIT = 8;
0112: private static final int VERSION_3_NT_32_BIT = 9;
0113:
0114: private static final int VERSION_4_1_BIT = 10;
0115: private static final int VERSION_4_4_BIT = 11;
0116: private static final int VERSION_4_8_BIT = 12;
0117: private static final int VERSION_4_16_BIT = 13;
0118: private static final int VERSION_4_24_BIT = 14;
0119: private static final int VERSION_4_32_BIT = 15;
0120:
0121: private static final int VERSION_3_XP_EMBEDDED = 16;
0122: private static final int VERSION_4_XP_EMBEDDED = 17;
0123: private static final int VERSION_5_XP_EMBEDDED = 18;
0124:
0125: // BMP variables
0126: private long bitmapFileSize;
0127: private long bitmapOffset;
0128: private long compression;
0129: private long imageSize;
0130: private byte palette[];
0131: private int imageType;
0132: private int numBands;
0133: private boolean isBottomUp;
0134: private int bitsPerPixel;
0135: private int redMask, greenMask, blueMask, alphaMask;
0136:
0137: private SampleModel sampleModel, originalSampleModel;
0138: private ColorModel colorModel, originalColorModel;
0139:
0140: /** The input stream where reads from */
0141: private ImageInputStream iis = null;
0142:
0143: /** Indicates whether the header is read. */
0144: private boolean gotHeader = false;
0145:
0146: /** The stream position where the image data starts. */
0147: private long imageDataOffset;
0148:
0149: /** The original image width. */
0150: private int width;
0151:
0152: /** The original image height. */
0153: private int height;
0154:
0155: /** The destination region. */
0156: private Rectangle destinationRegion;
0157:
0158: /** The source region. */
0159: private Rectangle sourceRegion;
0160:
0161: /** The metadata from the stream. */
0162: private BMPMetadata metadata;
0163:
0164: /** The destination image. */
0165: private BufferedImage bi;
0166:
0167: /** Indicates whether subsampled, subregion is required, and offset is
0168: * defined
0169: */
0170: private boolean noTransform = true;
0171:
0172: /** Indicates whether subband is selected. */
0173: private boolean seleBand = false;
0174:
0175: /** The scaling factors. */
0176: private int scaleX, scaleY;
0177:
0178: /** source and destination bands. */
0179: private int[] sourceBands, destBands;
0180:
0181: /** Constructs <code>BMPImageReader</code> from the provided
0182: * <code>ImageReaderSpi</code>.
0183: */
0184: public BMPImageReader(ImageReaderSpi originator) {
0185: super (originator);
0186: }
0187:
0188: /** Overrides the method defined in the superclass. */
0189: public void setInput(Object input, boolean seekForwardOnly,
0190: boolean ignoreMetadata) {
0191: super .setInput(input, seekForwardOnly, ignoreMetadata);
0192: iis = (ImageInputStream) input; // Always works
0193: if (iis != null)
0194: iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
0195: resetHeaderInfo();
0196: }
0197:
0198: /** Overrides the method defined in the superclass. */
0199: public int getNumImages(boolean allowSearch) throws IOException {
0200: if (iis == null) {
0201: throw new IllegalStateException(I18N
0202: .getString("GetNumImages0"));
0203: }
0204: if (seekForwardOnly && allowSearch) {
0205: throw new IllegalStateException(I18N
0206: .getString("GetNumImages1"));
0207: }
0208: return 1;
0209: }
0210:
0211: public int getWidth(int imageIndex) throws IOException {
0212: checkIndex(imageIndex);
0213: readHeader();
0214: return width;
0215: }
0216:
0217: public int getHeight(int imageIndex) throws IOException {
0218: checkIndex(imageIndex);
0219: readHeader();
0220: return height;
0221: }
0222:
0223: private void checkIndex(int imageIndex) {
0224: if (imageIndex != 0) {
0225: throw new IndexOutOfBoundsException(I18N
0226: .getString("BMPImageReader0"));
0227: }
0228: }
0229:
0230: public void readHeader() throws IOException {
0231: if (gotHeader) {
0232: // Seek to where the image data starts, since that is where
0233: // the stream pointer should be after header is read
0234: iis.seek(imageDataOffset);
0235: return;
0236: }
0237:
0238: if (iis == null) {
0239: throw new IllegalStateException(I18N
0240: .getString("BMPImageReader5"));
0241: }
0242: int profileData = 0, profileSize = 0;
0243:
0244: this .metadata = new BMPMetadata();
0245: iis.mark();
0246:
0247: // read and check the magic marker
0248: byte[] marker = new byte[2];
0249: iis.read(marker);
0250: if (marker[0] != 0x42 || marker[1] != 0x4d)
0251: throw new IllegalArgumentException(I18N
0252: .getString("BMPImageReader1"));
0253:
0254: // Read file size
0255: bitmapFileSize = iis.readUnsignedInt();
0256: // skip the two reserved fields
0257: iis.skipBytes(4);
0258:
0259: // Offset to the bitmap from the beginning
0260: bitmapOffset = iis.readUnsignedInt();
0261: // End File Header
0262:
0263: // Start BitmapCoreHeader
0264: long size = iis.readUnsignedInt();
0265:
0266: if (size == 12) {
0267: width = iis.readShort();
0268: height = iis.readShort();
0269: } else {
0270: width = iis.readInt();
0271: height = iis.readInt();
0272: }
0273:
0274: metadata.width = width;
0275: metadata.height = height;
0276:
0277: int planes = iis.readUnsignedShort();
0278: bitsPerPixel = iis.readUnsignedShort();
0279:
0280: //metadata.colorPlane = planes;
0281: metadata.bitsPerPixel = (short) bitsPerPixel;
0282:
0283: // As BMP always has 3 rgb bands, except for Version 5,
0284: // which is bgra
0285: numBands = 3;
0286:
0287: if (size == 12) {
0288: // Windows 2.x and OS/2 1.x
0289: metadata.bmpVersion = VERSION_2;
0290:
0291: // Classify the image type
0292: if (bitsPerPixel == 1) {
0293: imageType = VERSION_2_1_BIT;
0294: } else if (bitsPerPixel == 4) {
0295: imageType = VERSION_2_4_BIT;
0296: } else if (bitsPerPixel == 8) {
0297: imageType = VERSION_2_8_BIT;
0298: } else if (bitsPerPixel == 24) {
0299: imageType = VERSION_2_24_BIT;
0300: }
0301:
0302: // Read in the palette
0303: int numberOfEntries = (int) ((bitmapOffset - 14 - size) / 3);
0304: int sizeOfPalette = numberOfEntries * 3;
0305: palette = new byte[sizeOfPalette];
0306: iis.readFully(palette, 0, sizeOfPalette);
0307: metadata.palette = palette;
0308: metadata.paletteSize = numberOfEntries;
0309: } else {
0310: compression = iis.readUnsignedInt();
0311: imageSize = iis.readUnsignedInt();
0312: long xPelsPerMeter = iis.readInt();
0313: long yPelsPerMeter = iis.readInt();
0314: long colorsUsed = iis.readUnsignedInt();
0315: long colorsImportant = iis.readUnsignedInt();
0316:
0317: metadata.compression = (int) compression;
0318: metadata.imageSize = (int) imageSize;
0319: metadata.xPixelsPerMeter = (int) xPelsPerMeter;
0320: metadata.yPixelsPerMeter = (int) yPelsPerMeter;
0321: metadata.colorsUsed = (int) colorsUsed;
0322: metadata.colorsImportant = (int) colorsImportant;
0323:
0324: if (size == 40) {
0325: // Windows 3.x and Windows NT
0326: switch ((int) compression) {
0327:
0328: case BI_JPEG:
0329: case BI_PNG:
0330: metadata.bmpVersion = VERSION_3;
0331: imageType = VERSION_3_XP_EMBEDDED;
0332: break;
0333:
0334: case BI_RGB: // No compression
0335: case BI_RLE8: // 8-bit RLE compression
0336: case BI_RLE4: // 4-bit RLE compression
0337:
0338: // Read in the palette
0339: int numberOfEntries = (int) ((bitmapOffset - 14 - size) / 4);
0340: int sizeOfPalette = numberOfEntries * 4;
0341: palette = new byte[sizeOfPalette];
0342: iis.readFully(palette, 0, sizeOfPalette);
0343:
0344: metadata.palette = palette;
0345: metadata.paletteSize = numberOfEntries;
0346:
0347: if (bitsPerPixel == 1) {
0348: imageType = VERSION_3_1_BIT;
0349: } else if (bitsPerPixel == 4) {
0350: imageType = VERSION_3_4_BIT;
0351: } else if (bitsPerPixel == 8) {
0352: imageType = VERSION_3_8_BIT;
0353: } else if (bitsPerPixel == 24) {
0354: imageType = VERSION_3_24_BIT;
0355: } else if (bitsPerPixel == 16) {
0356: imageType = VERSION_3_NT_16_BIT;
0357:
0358: redMask = 0x7C00;
0359: greenMask = 0x3E0;
0360: blueMask = (1 << 5) - 1;// 0x1F;
0361: metadata.redMask = redMask;
0362: metadata.greenMask = greenMask;
0363: metadata.blueMask = blueMask;
0364: } else if (bitsPerPixel == 32) {
0365: imageType = VERSION_3_NT_32_BIT;
0366: redMask = 0x00FF0000;
0367: greenMask = 0x0000FF00;
0368: blueMask = 0x000000FF;
0369: metadata.redMask = redMask;
0370: metadata.greenMask = greenMask;
0371: metadata.blueMask = blueMask;
0372: }
0373:
0374: metadata.bmpVersion = VERSION_3;
0375: break;
0376:
0377: case BI_BITFIELDS:
0378:
0379: if (bitsPerPixel == 16) {
0380: imageType = VERSION_3_NT_16_BIT;
0381: } else if (bitsPerPixel == 32) {
0382: imageType = VERSION_3_NT_32_BIT;
0383: }
0384:
0385: // BitsField encoding
0386: redMask = (int) iis.readUnsignedInt();
0387: greenMask = (int) iis.readUnsignedInt();
0388: blueMask = (int) iis.readUnsignedInt();
0389: metadata.redMask = redMask;
0390: metadata.greenMask = greenMask;
0391: metadata.blueMask = blueMask;
0392:
0393: if (colorsUsed != 0) {
0394: // there is a palette
0395: sizeOfPalette = (int) colorsUsed * 4;
0396: palette = new byte[sizeOfPalette];
0397: iis.readFully(palette, 0, sizeOfPalette);
0398: metadata.palette = palette;
0399: metadata.paletteSize = (int) colorsUsed;
0400: }
0401: metadata.bmpVersion = VERSION_3_NT;
0402:
0403: break;
0404: default:
0405: throw new RuntimeException(I18N
0406: .getString("BMPImageReader2"));
0407: }
0408: } else if (size == 108 || size == 124) {
0409: // Windows 4.x BMP
0410: if (size == 108)
0411: metadata.bmpVersion = VERSION_4;
0412: else if (size == 124)
0413: metadata.bmpVersion = VERSION_5;
0414:
0415: // rgb masks, valid only if comp is BI_BITFIELDS
0416: redMask = (int) iis.readUnsignedInt();
0417: greenMask = (int) iis.readUnsignedInt();
0418: blueMask = (int) iis.readUnsignedInt();
0419: // Only supported for 32bpp BI_RGB argb
0420: alphaMask = (int) iis.readUnsignedInt();
0421: long csType = iis.readUnsignedInt();
0422: int redX = iis.readInt();
0423: int redY = iis.readInt();
0424: int redZ = iis.readInt();
0425: int greenX = iis.readInt();
0426: int greenY = iis.readInt();
0427: int greenZ = iis.readInt();
0428: int blueX = iis.readInt();
0429: int blueY = iis.readInt();
0430: int blueZ = iis.readInt();
0431: long gammaRed = iis.readUnsignedInt();
0432: long gammaGreen = iis.readUnsignedInt();
0433: long gammaBlue = iis.readUnsignedInt();
0434:
0435: if (size == 124) {
0436: metadata.intent = iis.readInt();
0437: profileData = iis.readInt();
0438: profileSize = iis.readInt();
0439: iis.skipBytes(4);
0440: }
0441:
0442: metadata.colorSpace = (int) csType;
0443:
0444: if (csType == LCS_CALIBRATED_RGB) {
0445: // All the new fields are valid only for this case
0446: metadata.redX = redX;
0447: metadata.redY = redY;
0448: metadata.redZ = redZ;
0449: metadata.greenX = greenX;
0450: metadata.greenY = greenY;
0451: metadata.greenZ = greenZ;
0452: metadata.blueX = blueX;
0453: metadata.blueY = blueY;
0454: metadata.blueZ = blueZ;
0455: metadata.gammaRed = (int) gammaRed;
0456: metadata.gammaGreen = (int) gammaGreen;
0457: metadata.gammaBlue = (int) gammaBlue;
0458: }
0459:
0460: // Read in the palette
0461: int numberOfEntries = (int) ((bitmapOffset - 14 - size) / 4);
0462: int sizeOfPalette = numberOfEntries * 4;
0463: palette = new byte[sizeOfPalette];
0464: iis.readFully(palette, 0, sizeOfPalette);
0465: metadata.palette = palette;
0466: metadata.paletteSize = numberOfEntries;
0467:
0468: switch ((int) compression) {
0469: case BI_JPEG:
0470: case BI_PNG:
0471: if (size == 108) {
0472: imageType = VERSION_4_XP_EMBEDDED;
0473: } else if (size == 124) {
0474: imageType = VERSION_5_XP_EMBEDDED;
0475: }
0476: break;
0477: default:
0478: if (bitsPerPixel == 1) {
0479: imageType = VERSION_4_1_BIT;
0480: } else if (bitsPerPixel == 4) {
0481: imageType = VERSION_4_4_BIT;
0482: } else if (bitsPerPixel == 8) {
0483: imageType = VERSION_4_8_BIT;
0484: } else if (bitsPerPixel == 16) {
0485: imageType = VERSION_4_16_BIT;
0486: if ((int) compression == BI_RGB) {
0487: redMask = 0x7C00;
0488: greenMask = 0x3E0;
0489: blueMask = 0x1F;
0490: }
0491: } else if (bitsPerPixel == 24) {
0492: imageType = VERSION_4_24_BIT;
0493: } else if (bitsPerPixel == 32) {
0494: imageType = VERSION_4_32_BIT;
0495: if ((int) compression == BI_RGB) {
0496: redMask = 0x00FF0000;
0497: greenMask = 0x0000FF00;
0498: blueMask = 0x000000FF;
0499: }
0500: }
0501:
0502: metadata.redMask = redMask;
0503: metadata.greenMask = greenMask;
0504: metadata.blueMask = blueMask;
0505: metadata.alphaMask = alphaMask;
0506: }
0507: } else {
0508: throw new RuntimeException(I18N
0509: .getString("BMPImageReader3"));
0510: }
0511: }
0512:
0513: if (height > 0) {
0514: // bottom up image
0515: isBottomUp = true;
0516: } else {
0517: // top down image
0518: isBottomUp = false;
0519: height = Math.abs(height);
0520: }
0521:
0522: // Reset Image Layout so there's only one tile.
0523: //Define the color space
0524: ColorSpace colorSpace = ColorSpace
0525: .getInstance(ColorSpace.CS_sRGB);
0526: if (metadata.colorSpace == PROFILE_LINKED
0527: || metadata.colorSpace == PROFILE_EMBEDDED) {
0528:
0529: iis.mark();
0530: iis.skipBytes(profileData - size);
0531: byte[] profile = new byte[profileSize];
0532: iis.readFully(profile, 0, profileSize);
0533: iis.reset();
0534:
0535: try {
0536: if (metadata.colorSpace == PROFILE_LINKED)
0537: colorSpace = new ICC_ColorSpace(ICC_Profile
0538: .getInstance(new String(profile)));
0539: else
0540: colorSpace = new ICC_ColorSpace(ICC_Profile
0541: .getInstance(profile));
0542: } catch (Exception e) {
0543: colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
0544: }
0545: }
0546:
0547: if (bitsPerPixel == 0 || compression == BI_JPEG
0548: || compression == BI_PNG) {
0549: // the colorModel and sampleModel will be initialzed
0550: // by the reader of embedded image
0551: colorModel = null;
0552: sampleModel = null;
0553: } else if (bitsPerPixel == 1 || bitsPerPixel == 4
0554: || bitsPerPixel == 8) {
0555: // When number of bitsPerPixel is <= 8, we use IndexColorModel.
0556: numBands = 1;
0557:
0558: if (bitsPerPixel == 8) {
0559: int[] bandOffsets = new int[numBands];
0560: for (int i = 0; i < numBands; i++) {
0561: bandOffsets[i] = numBands - 1 - i;
0562: }
0563: sampleModel = new PixelInterleavedSampleModel(
0564: DataBuffer.TYPE_BYTE, width, height, numBands,
0565: numBands * width, bandOffsets);
0566: } else {
0567: // 1 and 4 bit pixels can be stored in a packed format.
0568: sampleModel = new MultiPixelPackedSampleModel(
0569: DataBuffer.TYPE_BYTE, width, height,
0570: bitsPerPixel);
0571: }
0572:
0573: // Create IndexColorModel from the palette.
0574: byte r[], g[], b[];
0575: if (imageType == VERSION_2_1_BIT
0576: || imageType == VERSION_2_4_BIT
0577: || imageType == VERSION_2_8_BIT) {
0578:
0579: size = palette.length / 3;
0580:
0581: if (size > 256) {
0582: size = 256;
0583: }
0584:
0585: int off;
0586: r = new byte[(int) size];
0587: g = new byte[(int) size];
0588: b = new byte[(int) size];
0589: for (int i = 0; i < (int) size; i++) {
0590: off = 3 * i;
0591: b[i] = palette[off];
0592: g[i] = palette[off + 1];
0593: r[i] = palette[off + 2];
0594: }
0595: } else {
0596: size = palette.length / 4;
0597:
0598: if (size > 256) {
0599: size = 256;
0600: }
0601:
0602: int off;
0603: r = new byte[(int) size];
0604: g = new byte[(int) size];
0605: b = new byte[(int) size];
0606: for (int i = 0; i < size; i++) {
0607: off = 4 * i;
0608: b[i] = palette[off];
0609: g[i] = palette[off + 1];
0610: r[i] = palette[off + 2];
0611: }
0612: }
0613:
0614: if (ImageUtil.isIndicesForGrayscale(r, g, b))
0615: colorModel = ImageUtil.createColorModel(null,
0616: sampleModel);
0617: else
0618: colorModel = new IndexColorModel(bitsPerPixel,
0619: (int) size, r, g, b);
0620: } else if (bitsPerPixel == 16) {
0621: numBands = 3;
0622: sampleModel = new SinglePixelPackedSampleModel(
0623: DataBuffer.TYPE_USHORT, width, height, new int[] {
0624: redMask, greenMask, blueMask });
0625:
0626: colorModel = new DirectColorModel(colorSpace, 16, redMask,
0627: greenMask, blueMask, 0, false,
0628: DataBuffer.TYPE_USHORT);
0629:
0630: } else if (bitsPerPixel == 32) {
0631: numBands = alphaMask == 0 ? 3 : 4;
0632:
0633: if (redMask == 0 || greenMask == 0 || blueMask == 0) {
0634: redMask = 0xFF0000;
0635: greenMask = 0xFF00;
0636: blueMask = 0xFF;
0637: alphaMask = 0xFF000000;
0638: }
0639:
0640: // The number of bands in the SampleModel is determined by
0641: // the length of the mask array passed in.
0642: int[] bitMasks = numBands == 3 ? new int[] { redMask,
0643: greenMask, blueMask } : new int[] { redMask,
0644: greenMask, blueMask, alphaMask };
0645:
0646: sampleModel = new SinglePixelPackedSampleModel(
0647: DataBuffer.TYPE_INT, width, height, bitMasks);
0648:
0649: colorModel = new DirectColorModel(colorSpace, 32, redMask,
0650: greenMask, blueMask, alphaMask, false,
0651: DataBuffer.TYPE_INT);
0652: } else {
0653: numBands = 3;
0654: // Create SampleModel
0655: int[] bandOffsets = new int[numBands];
0656: for (int i = 0; i < numBands; i++) {
0657: bandOffsets[i] = numBands - 1 - i;
0658: }
0659:
0660: sampleModel = new PixelInterleavedSampleModel(
0661: DataBuffer.TYPE_BYTE, width, height, numBands,
0662: numBands * width, bandOffsets);
0663:
0664: colorModel = ImageUtil.createColorModel(colorSpace,
0665: sampleModel);
0666: }
0667:
0668: originalSampleModel = sampleModel;
0669: originalColorModel = colorModel;
0670:
0671: // Reset to the start of bitmap; then jump to the
0672: //start of image data
0673: iis.reset();
0674: iis.skipBytes(bitmapOffset);
0675: gotHeader = true;
0676:
0677: // Store the stream position where the image data starts
0678: imageDataOffset = iis.getStreamPosition();
0679: }
0680:
0681: public Iterator getImageTypes(int imageIndex) throws IOException {
0682: checkIndex(imageIndex);
0683: readHeader();
0684: ArrayList list = new ArrayList(1);
0685: list.add(new ImageTypeSpecifier(originalColorModel,
0686: originalSampleModel));
0687: return list.iterator();
0688: }
0689:
0690: public ImageReadParam getDefaultReadParam() {
0691: return new ImageReadParam();
0692: }
0693:
0694: public IIOMetadata getImageMetadata(int imageIndex)
0695: throws IOException {
0696: checkIndex(imageIndex);
0697: if (metadata == null) {
0698: readHeader();
0699: }
0700: return metadata;
0701: }
0702:
0703: public IIOMetadata getStreamMetadata() throws IOException {
0704: return null;
0705: }
0706:
0707: public boolean isRandomAccessEasy(int imageIndex)
0708: throws IOException {
0709: checkIndex(imageIndex);
0710: readHeader();
0711: return metadata.compression == BI_RGB;
0712: }
0713:
0714: public BufferedImage read(int imageIndex, ImageReadParam param)
0715: throws IOException {
0716:
0717: if (iis == null) {
0718: throw new IllegalStateException(I18N
0719: .getString("BMPImageReader5"));
0720: }
0721:
0722: checkIndex(imageIndex);
0723: clearAbortRequest();
0724: processImageStarted(imageIndex);
0725:
0726: if (param == null)
0727: param = getDefaultReadParam();
0728:
0729: //read header
0730: readHeader();
0731:
0732: sourceRegion = new Rectangle(0, 0, 0, 0);
0733: destinationRegion = new Rectangle(0, 0, 0, 0);
0734:
0735: computeRegions(param, this .width, this .height, param
0736: .getDestination(), sourceRegion, destinationRegion);
0737:
0738: scaleX = param.getSourceXSubsampling();
0739: scaleY = param.getSourceYSubsampling();
0740:
0741: // If the destination band is set used it
0742: sourceBands = param.getSourceBands();
0743: destBands = param.getDestinationBands();
0744:
0745: seleBand = (sourceBands != null) && (destBands != null);
0746: noTransform = destinationRegion.equals(new Rectangle(0, 0,
0747: width, height))
0748: || seleBand;
0749:
0750: if (!seleBand) {
0751: sourceBands = new int[numBands];
0752: destBands = new int[numBands];
0753: for (int i = 0; i < numBands; i++)
0754: destBands[i] = sourceBands[i] = i;
0755: }
0756:
0757: // If the destination is provided, then use it. Otherwise, create new one
0758: bi = param.getDestination();
0759:
0760: // Get the image data.
0761: WritableRaster raster = null;
0762:
0763: if (bi == null) {
0764: if (sampleModel != null && colorModel != null) {
0765: sampleModel = sampleModel.createCompatibleSampleModel(
0766: destinationRegion.x + destinationRegion.width,
0767: destinationRegion.y + destinationRegion.height);
0768: if (seleBand)
0769: sampleModel = sampleModel
0770: .createSubsetSampleModel(sourceBands);
0771: raster = Raster.createWritableRaster(sampleModel,
0772: new Point());
0773: bi = new BufferedImage(colorModel, raster, false, null);
0774: }
0775: } else {
0776: raster = bi.getWritableTile(0, 0);
0777: sampleModel = bi.getSampleModel();
0778: colorModel = bi.getColorModel();
0779:
0780: noTransform &= destinationRegion.equals(raster.getBounds());
0781: }
0782:
0783: byte bdata[] = null; // buffer for byte data
0784: short sdata[] = null; // buffer for short data
0785: int idata[] = null; // buffer for int data
0786:
0787: // the sampleModel can be null in case of embedded image
0788: if (sampleModel != null) {
0789: if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
0790: bdata = (byte[]) ((DataBufferByte) raster
0791: .getDataBuffer()).getData();
0792: else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
0793: sdata = (short[]) ((DataBufferUShort) raster
0794: .getDataBuffer()).getData();
0795: else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
0796: idata = (int[]) ((DataBufferInt) raster.getDataBuffer())
0797: .getData();
0798: }
0799:
0800: // There should only be one tile.
0801: switch (imageType) {
0802:
0803: case VERSION_2_1_BIT:
0804: // no compression
0805: read1Bit(bdata);
0806: break;
0807:
0808: case VERSION_2_4_BIT:
0809: // no compression
0810: read4Bit(bdata);
0811: break;
0812:
0813: case VERSION_2_8_BIT:
0814: // no compression
0815: read8Bit(bdata);
0816: break;
0817:
0818: case VERSION_2_24_BIT:
0819: // no compression
0820: read24Bit(bdata);
0821: break;
0822:
0823: case VERSION_3_1_BIT:
0824: // 1-bit images cannot be compressed.
0825: read1Bit(bdata);
0826: break;
0827:
0828: case VERSION_3_4_BIT:
0829: switch ((int) compression) {
0830: case BI_RGB:
0831: read4Bit(bdata);
0832: break;
0833:
0834: case BI_RLE4:
0835: readRLE4(bdata);
0836: break;
0837:
0838: default:
0839: throw new RuntimeException(I18N
0840: .getString("BMPImageReader1"));
0841: }
0842: break;
0843:
0844: case VERSION_3_8_BIT:
0845: switch ((int) compression) {
0846: case BI_RGB:
0847: read8Bit(bdata);
0848: break;
0849:
0850: case BI_RLE8:
0851: readRLE8(bdata);
0852: break;
0853:
0854: default:
0855: throw new RuntimeException(I18N
0856: .getString("BMPImageReader1"));
0857: }
0858:
0859: break;
0860:
0861: case VERSION_3_24_BIT:
0862: // 24-bit images are not compressed
0863: read24Bit(bdata);
0864: break;
0865:
0866: case VERSION_3_NT_16_BIT:
0867: read16Bit(sdata);
0868: break;
0869:
0870: case VERSION_3_NT_32_BIT:
0871: read32Bit(idata);
0872: break;
0873:
0874: case VERSION_3_XP_EMBEDDED:
0875: case VERSION_4_XP_EMBEDDED:
0876: case VERSION_5_XP_EMBEDDED:
0877: bi = readEmbedded((int) compression, bi, param);
0878: break;
0879:
0880: case VERSION_4_1_BIT:
0881: read1Bit(bdata);
0882: break;
0883:
0884: case VERSION_4_4_BIT:
0885: switch ((int) compression) {
0886:
0887: case BI_RGB:
0888: read4Bit(bdata);
0889: break;
0890:
0891: case BI_RLE4:
0892: readRLE4(bdata);
0893: break;
0894:
0895: default:
0896: throw new RuntimeException(I18N
0897: .getString("BMPImageReader1"));
0898: }
0899:
0900: case VERSION_4_8_BIT:
0901: switch ((int) compression) {
0902:
0903: case BI_RGB:
0904: read8Bit(bdata);
0905: break;
0906:
0907: case BI_RLE8:
0908: readRLE8(bdata);
0909: break;
0910:
0911: default:
0912: throw new RuntimeException(I18N
0913: .getString("BMPImageReader1"));
0914: }
0915: break;
0916:
0917: case VERSION_4_16_BIT:
0918: read16Bit(sdata);
0919: break;
0920:
0921: case VERSION_4_24_BIT:
0922: read24Bit(bdata);
0923: break;
0924:
0925: case VERSION_4_32_BIT:
0926: read32Bit(idata);
0927: break;
0928: }
0929:
0930: if (abortRequested())
0931: processReadAborted();
0932: else
0933: processImageComplete();
0934:
0935: return bi;
0936: }
0937:
0938: public boolean canReadRaster() {
0939: return true;
0940: }
0941:
0942: public Raster readRaster(int imageIndex, ImageReadParam param)
0943: throws IOException {
0944: BufferedImage bi = read(imageIndex, param);
0945: return bi.getData();
0946: }
0947:
0948: private void resetHeaderInfo() {
0949: gotHeader = false;
0950: bi = null;
0951: sampleModel = originalSampleModel = null;
0952: colorModel = originalColorModel = null;
0953: }
0954:
0955: public void reset() {
0956: super .reset();
0957: iis = null;
0958: resetHeaderInfo();
0959: }
0960:
0961: // Deal with 1 Bit images using IndexColorModels
0962: private void read1Bit(byte[] bdata) throws IOException {
0963: int bytesPerScanline = (width + 7) / 8;
0964: int padding = bytesPerScanline % 4;
0965: if (padding != 0) {
0966: padding = 4 - padding;
0967: }
0968:
0969: int lineLength = bytesPerScanline + padding;
0970:
0971: if (noTransform) {
0972: int j = isBottomUp ? (height - 1) * bytesPerScanline : 0;
0973:
0974: for (int i = 0; i < height; i++) {
0975: if (abortRequested()) {
0976: break;
0977: }
0978: iis.readFully(bdata, j, bytesPerScanline);
0979: iis.skipBytes(padding);
0980: j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
0981: processImageUpdate(bi, 0, i, destinationRegion.width,
0982: 1, 1, 1, new int[] { 0 });
0983: processImageProgress(100.0F * i
0984: / destinationRegion.height);
0985: }
0986: } else {
0987: byte[] buf = new byte[lineLength];
0988: int lineStride = ((MultiPixelPackedSampleModel) sampleModel)
0989: .getScanlineStride();
0990:
0991: if (isBottomUp) {
0992: int lastLine = sourceRegion.y
0993: + (destinationRegion.height - 1) * scaleY;
0994: iis.skipBytes(lineLength * (height - 1 - lastLine));
0995: } else
0996: iis.skipBytes(lineLength * sourceRegion.y);
0997:
0998: int skipLength = lineLength * (scaleY - 1);
0999:
1000: // cache the values to avoid duplicated computation
1001: int[] srcOff = new int[destinationRegion.width];
1002: int[] destOff = new int[destinationRegion.width];
1003: int[] srcPos = new int[destinationRegion.width];
1004: int[] destPos = new int[destinationRegion.width];
1005:
1006: for (int i = destinationRegion.x, x = sourceRegion.x, j = 0; i < destinationRegion.x
1007: + destinationRegion.width; i++, j++, x += scaleX) {
1008: srcPos[j] = x >> 3;
1009: srcOff[j] = 7 - (x & 7);
1010: destPos[j] = i >> 3;
1011: destOff[j] = 7 - (i & 7);
1012: }
1013:
1014: int k = destinationRegion.y * lineStride;
1015: if (isBottomUp)
1016: k += (destinationRegion.height - 1) * lineStride;
1017:
1018: for (int j = 0, y = sourceRegion.y; j < destinationRegion.height; j++, y += scaleY) {
1019:
1020: if (abortRequested())
1021: break;
1022: iis.read(buf, 0, lineLength);
1023: for (int i = 0; i < destinationRegion.width; i++) {
1024: //get the bit and assign to the data buffer of the raster
1025: int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
1026: bdata[k + destPos[i]] |= v << destOff[i];
1027: }
1028:
1029: k += isBottomUp ? -lineStride : lineStride;
1030: iis.skipBytes(skipLength);
1031: processImageUpdate(bi, 0, j, destinationRegion.width,
1032: 1, 1, 1, new int[] { 0 });
1033: processImageProgress(100.0F * j
1034: / destinationRegion.height);
1035: }
1036: }
1037: }
1038:
1039: // Method to read a 4 bit BMP image data
1040: private void read4Bit(byte[] bdata) throws IOException {
1041:
1042: int bytesPerScanline = (width + 1) / 2;
1043:
1044: // Padding bytes at the end of each scanline
1045: int padding = bytesPerScanline % 4;
1046: if (padding != 0)
1047: padding = 4 - padding;
1048:
1049: int lineLength = bytesPerScanline + padding;
1050:
1051: if (noTransform) {
1052: int j = isBottomUp ? (height - 1) * bytesPerScanline : 0;
1053:
1054: for (int i = 0; i < height; i++) {
1055: if (abortRequested()) {
1056: break;
1057: }
1058: iis.readFully(bdata, j, bytesPerScanline);
1059: iis.skipBytes(padding);
1060: j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
1061: processImageUpdate(bi, 0, i, destinationRegion.width,
1062: 1, 1, 1, new int[] { 0 });
1063: processImageProgress(100.0F * i
1064: / destinationRegion.height);
1065: }
1066: } else {
1067: byte[] buf = new byte[lineLength];
1068: int lineStride = ((MultiPixelPackedSampleModel) sampleModel)
1069: .getScanlineStride();
1070:
1071: if (isBottomUp) {
1072: int lastLine = sourceRegion.y
1073: + (destinationRegion.height - 1) * scaleY;
1074: iis.skipBytes(lineLength * (height - 1 - lastLine));
1075: } else
1076: iis.skipBytes(lineLength * sourceRegion.y);
1077:
1078: int skipLength = lineLength * (scaleY - 1);
1079:
1080: // cache the values to avoid duplicated computation
1081: int[] srcOff = new int[destinationRegion.width];
1082: int[] destOff = new int[destinationRegion.width];
1083: int[] srcPos = new int[destinationRegion.width];
1084: int[] destPos = new int[destinationRegion.width];
1085:
1086: for (int i = destinationRegion.x, x = sourceRegion.x, j = 0; i < destinationRegion.x
1087: + destinationRegion.width; i++, j++, x += scaleX) {
1088: srcPos[j] = x >> 1;
1089: srcOff[j] = (1 - (x & 1)) << 2;
1090: destPos[j] = i >> 1;
1091: destOff[j] = (1 - (i & 1)) << 2;
1092: }
1093:
1094: int k = destinationRegion.y * lineStride;
1095: if (isBottomUp)
1096: k += (destinationRegion.height - 1) * lineStride;
1097:
1098: for (int j = 0, y = sourceRegion.y; j < destinationRegion.height; j++, y += scaleY) {
1099:
1100: if (abortRequested())
1101: break;
1102: iis.read(buf, 0, lineLength);
1103: for (int i = 0; i < destinationRegion.width; i++) {
1104: //get the bit and assign to the data buffer of the raster
1105: int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F;
1106: bdata[k + destPos[i]] |= v << destOff[i];
1107: }
1108:
1109: k += isBottomUp ? -lineStride : lineStride;
1110: iis.skipBytes(skipLength);
1111: processImageUpdate(bi, 0, j, destinationRegion.width,
1112: 1, 1, 1, new int[] { 0 });
1113: processImageProgress(100.0F * j
1114: / destinationRegion.height);
1115: }
1116: }
1117: }
1118:
1119: // Method to read 8 bit BMP image data
1120: private void read8Bit(byte[] bdata) throws IOException {
1121:
1122: // Padding bytes at the end of each scanline
1123: int padding = width % 4;
1124: if (padding != 0) {
1125: padding = 4 - padding;
1126: }
1127:
1128: int lineLength = width + padding;
1129:
1130: if (noTransform) {
1131: int j = isBottomUp ? (height - 1) * width : 0;
1132:
1133: for (int i = 0; i < height; i++) {
1134: if (abortRequested()) {
1135: break;
1136: }
1137: iis.readFully(bdata, j, width);
1138: iis.skipBytes(padding);
1139: j += isBottomUp ? -width : width;
1140: processImageUpdate(bi, 0, i, destinationRegion.width,
1141: 1, 1, 1, new int[] { 0 });
1142: processImageProgress(100.0F * i
1143: / destinationRegion.height);
1144: }
1145: } else {
1146: byte[] buf = new byte[lineLength];
1147: int lineStride = ((ComponentSampleModel) sampleModel)
1148: .getScanlineStride();
1149:
1150: if (isBottomUp) {
1151: int lastLine = sourceRegion.y
1152: + (destinationRegion.height - 1) * scaleY;
1153: iis.skipBytes(lineLength * (height - 1 - lastLine));
1154: } else
1155: iis.skipBytes(lineLength * sourceRegion.y);
1156:
1157: int skipLength = lineLength * (scaleY - 1);
1158:
1159: int k = destinationRegion.y * lineStride;
1160: if (isBottomUp)
1161: k += (destinationRegion.height - 1) * lineStride;
1162: k += destinationRegion.x;
1163:
1164: for (int j = 0, y = sourceRegion.y; j < destinationRegion.height; j++, y += scaleY) {
1165:
1166: if (abortRequested())
1167: break;
1168: iis.read(buf, 0, lineLength);
1169: for (int i = 0, m = sourceRegion.x; i < destinationRegion.width; i++, m += scaleX) {
1170: //get the bit and assign to the data buffer of the raster
1171: bdata[k + i] = buf[m];
1172: }
1173:
1174: k += isBottomUp ? -lineStride : lineStride;
1175: iis.skipBytes(skipLength);
1176: processImageUpdate(bi, 0, j, destinationRegion.width,
1177: 1, 1, 1, new int[] { 0 });
1178: processImageProgress(100.0F * j
1179: / destinationRegion.height);
1180: }
1181: }
1182: }
1183:
1184: // Method to read 24 bit BMP image data
1185: private void read24Bit(byte[] bdata) throws IOException {
1186: // Padding bytes at the end of each scanline
1187: // width * bitsPerPixel should be divisible by 32
1188: int padding = width * 3 % 4;
1189: if (padding != 0)
1190: padding = 4 - padding;
1191:
1192: int lineStride = width * 3;
1193: int lineLength = lineStride + padding;
1194:
1195: if (noTransform) {
1196: int j = isBottomUp ? (height - 1) * width * 3 : 0;
1197:
1198: for (int i = 0; i < height; i++) {
1199: if (abortRequested()) {
1200: break;
1201: }
1202: iis.readFully(bdata, j, lineStride);
1203: iis.skipBytes(padding);
1204: j += isBottomUp ? -lineStride : lineStride;
1205: processImageUpdate(bi, 0, i, destinationRegion.width,
1206: 1, 1, 1, new int[] { 0 });
1207: processImageProgress(100.0F * i
1208: / destinationRegion.height);
1209: }
1210: } else {
1211: byte[] buf = new byte[lineLength];
1212: lineStride = ((ComponentSampleModel) sampleModel)
1213: .getScanlineStride();
1214:
1215: if (isBottomUp) {
1216: int lastLine = sourceRegion.y
1217: + (destinationRegion.height - 1) * scaleY;
1218: iis.skipBytes(lineLength * (height - 1 - lastLine));
1219: } else
1220: iis.skipBytes(lineLength * sourceRegion.y);
1221:
1222: int skipLength = lineLength * (scaleY - 1);
1223:
1224: int k = destinationRegion.y * lineStride;
1225: if (isBottomUp)
1226: k += (destinationRegion.height - 1) * lineStride;
1227: k += destinationRegion.x * 3;
1228:
1229: for (int j = 0, y = sourceRegion.y; j < destinationRegion.height; j++, y += scaleY) {
1230:
1231: if (abortRequested())
1232: break;
1233: iis.read(buf, 0, lineLength);
1234: for (int i = 0, m = 3 * sourceRegion.x; i < destinationRegion.width; i++, m += 3 * scaleX) {
1235: //get the bit and assign to the data buffer of the raster
1236: int n = 3 * i + k;
1237: for (int b = 0; b < destBands.length; b++)
1238: bdata[n + destBands[b]] = buf[m
1239: + sourceBands[b]];
1240: }
1241:
1242: k += isBottomUp ? -lineStride : lineStride;
1243: iis.skipBytes(skipLength);
1244: processImageUpdate(bi, 0, j, destinationRegion.width,
1245: 1, 1, 1, new int[] { 0 });
1246: processImageProgress(100.0F * j
1247: / destinationRegion.height);
1248: }
1249: }
1250: }
1251:
1252: private void read16Bit(short sdata[]) throws IOException {
1253: // Padding bytes at the end of each scanline
1254: // width * bitsPerPixel should be divisible by 32
1255: int padding = width * 2 % 4;
1256:
1257: if (padding != 0)
1258: padding = 4 - padding;
1259:
1260: int lineLength = width + padding / 2;
1261:
1262: if (noTransform) {
1263: int j = isBottomUp ? (height - 1) * width : 0;
1264: for (int i = 0; i < height; i++) {
1265: if (abortRequested()) {
1266: break;
1267: }
1268:
1269: iis.readFully(sdata, j, width);
1270: iis.skipBytes(padding);
1271: j += isBottomUp ? -width : width;
1272: processImageUpdate(bi, 0, i, destinationRegion.width,
1273: 1, 1, 1, new int[] { 0 });
1274: processImageProgress(100.0F * i
1275: / destinationRegion.height);
1276: }
1277: } else {
1278: short[] buf = new short[lineLength];
1279: int lineStride = ((SinglePixelPackedSampleModel) sampleModel)
1280: .getScanlineStride();
1281:
1282: if (isBottomUp) {
1283: int lastLine = sourceRegion.y
1284: + (destinationRegion.height - 1) * scaleY;
1285: iis
1286: .skipBytes(lineLength * (height - 1 - lastLine) << 1);
1287: } else
1288: iis.skipBytes(lineLength * sourceRegion.y << 1);
1289:
1290: int skipLength = lineLength * (scaleY - 1) << 1;
1291:
1292: int k = destinationRegion.y * lineStride;
1293: if (isBottomUp)
1294: k += (destinationRegion.height - 1) * lineStride;
1295: k += destinationRegion.x;
1296:
1297: for (int j = 0, y = sourceRegion.y; j < destinationRegion.height; j++, y += scaleY) {
1298:
1299: if (abortRequested())
1300: break;
1301: iis.readFully(buf, 0, lineLength);
1302: for (int i = 0, m = sourceRegion.x; i < destinationRegion.width; i++, m += scaleX) {
1303: //get the bit and assign to the data buffer of the raster
1304: sdata[k + i] = buf[m];
1305: }
1306:
1307: k += isBottomUp ? -lineStride : lineStride;
1308: iis.skipBytes(skipLength);
1309: processImageUpdate(bi, 0, j, destinationRegion.width,
1310: 1, 1, 1, new int[] { 0 });
1311: processImageProgress(100.0F * j
1312: / destinationRegion.height);
1313: }
1314: }
1315: }
1316:
1317: private void read32Bit(int idata[]) throws IOException {
1318: if (noTransform) {
1319: int j = isBottomUp ? (height - 1) * width : 0;
1320:
1321: for (int i = 0; i < height; i++) {
1322: if (abortRequested()) {
1323: break;
1324: }
1325: iis.readFully(idata, j, width);
1326: j += isBottomUp ? -width : width;
1327: processImageUpdate(bi, 0, i, destinationRegion.width,
1328: 1, 1, 1, new int[] { 0 });
1329: processImageProgress(100.0F * i
1330: / destinationRegion.height);
1331: }
1332: } else {
1333: int[] buf = new int[width];
1334: int lineStride = ((SinglePixelPackedSampleModel) sampleModel)
1335: .getScanlineStride();
1336:
1337: if (isBottomUp) {
1338: int lastLine = sourceRegion.y
1339: + (destinationRegion.height - 1) * scaleY;
1340: iis.skipBytes(width * (height - 1 - lastLine) << 2);
1341: } else
1342: iis.skipBytes(width * sourceRegion.y << 2);
1343:
1344: int skipLength = width * (scaleY - 1) << 2;
1345:
1346: int k = destinationRegion.y * lineStride;
1347: if (isBottomUp)
1348: k += (destinationRegion.height - 1) * lineStride;
1349: k += destinationRegion.x;
1350:
1351: for (int j = 0, y = sourceRegion.y; j < destinationRegion.height; j++, y += scaleY) {
1352:
1353: if (abortRequested())
1354: break;
1355: iis.readFully(buf, 0, width);
1356: for (int i = 0, m = sourceRegion.x; i < destinationRegion.width; i++, m += scaleX) {
1357: //get the bit and assign to the data buffer of the raster
1358: idata[k + i] = buf[m];
1359: }
1360:
1361: k += isBottomUp ? -lineStride : lineStride;
1362: iis.skipBytes(skipLength);
1363: processImageUpdate(bi, 0, j, destinationRegion.width,
1364: 1, 1, 1, new int[] { 0 });
1365: processImageProgress(100.0F * j
1366: / destinationRegion.height);
1367: }
1368: }
1369: }
1370:
1371: private void readRLE8(byte bdata[]) throws IOException {
1372: // If imageSize field is not provided, calculate it.
1373: int imSize = (int) imageSize;
1374: if (imSize == 0) {
1375: imSize = (int) (bitmapFileSize - bitmapOffset);
1376: }
1377:
1378: int padding = 0;
1379: // If width is not 32 bit aligned, then while uncompressing each
1380: // scanline will have padding bytes, calculate the amount of padding
1381: int remainder = width % 4;
1382: if (remainder != 0) {
1383: padding = 4 - remainder;
1384: }
1385:
1386: // Read till we have the whole image
1387: byte values[] = new byte[imSize];
1388: int bytesRead = 0;
1389: iis.readFully(values, 0, imSize);
1390:
1391: // Since data is compressed, decompress it
1392: decodeRLE8(imSize, padding, values, bdata);
1393: }
1394:
1395: private void decodeRLE8(int imSize, int padding, byte[] values,
1396: byte[] bdata) throws IOException {
1397:
1398: byte val[] = new byte[width * height];
1399: int count = 0, l = 0;
1400: int value;
1401: boolean flag = false;
1402: int lineNo = isBottomUp ? height - 1 : 0;
1403: int lineStride = ((ComponentSampleModel) sampleModel)
1404: .getScanlineStride();
1405: int finished = 0;
1406:
1407: while (count != imSize) {
1408: value = values[count++] & 0xff;
1409: if (value == 0) {
1410: switch (values[count++] & 0xff) {
1411:
1412: case 0:
1413: case 1:
1414: // 0 is End-of-scanline marker, 1 is End-of-RLE marker
1415: // In either case, we want to copy the just decoded
1416: // scanline from val array to bdata array
1417: if (lineNo >= sourceRegion.y
1418: && lineNo < sourceRegion.y
1419: + sourceRegion.height) {
1420: if (noTransform) {
1421: int pos = lineNo * width;
1422: for (int i = 0; i < width; i++)
1423: bdata[pos++] = val[i];
1424: processImageUpdate(bi, 0, lineNo,
1425: destinationRegion.width, 1, 1, 1,
1426: new int[] { 0 });
1427: finished++;
1428: } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1429: int currentLine = (lineNo - sourceRegion.y)
1430: / scaleY + destinationRegion.y;
1431: int pos = currentLine * lineStride;
1432: pos += destinationRegion.x;
1433: for (int i = sourceRegion.x; i < sourceRegion.x
1434: + sourceRegion.width; i += scaleX)
1435: bdata[pos++] = val[i];
1436: processImageUpdate(bi, 0, currentLine,
1437: destinationRegion.width, 1, 1, 1,
1438: new int[] { 0 });
1439: finished++;
1440: }
1441: }
1442: processImageProgress(100.0F * finished
1443: / destinationRegion.height);
1444: lineNo += isBottomUp ? -1 : 1;
1445: l = 0;
1446:
1447: if (abortRequested()) {
1448: break;
1449: }
1450:
1451: // End-of-RLE marker
1452: if ((values[count - 1] & 0xff) == 1)
1453: flag = true;
1454:
1455: break;
1456:
1457: case 2:
1458: // delta or vector marker
1459: int xoff = values[count++] & 0xff;
1460: int yoff = values[count] & 0xff;
1461: // Move to the position xoff, yoff down
1462: l += xoff + yoff * width;
1463: break;
1464:
1465: default:
1466: int end = values[count - 1] & 0xff;
1467: for (int i = 0; i < end; i++) {
1468: val[l++] = (byte) (values[count++] & 0xff);
1469: }
1470:
1471: // Whenever end pixels can fit into odd number of bytes,
1472: // an extra padding byte will be present, so skip that.
1473: if ((end & 1) == 1) {
1474: count++;
1475: }
1476: }
1477: } else {
1478: for (int i = 0; i < value; i++) {
1479: val[l++] = (byte) (values[count] & 0xff);
1480: }
1481:
1482: count++;
1483: }
1484:
1485: // If End-of-RLE data, then exit the while loop
1486: if (flag) {
1487: break;
1488: }
1489: }
1490: }
1491:
1492: private void readRLE4(byte[] bdata) throws IOException {
1493:
1494: // If imageSize field is not specified, calculate it.
1495: int imSize = (int) imageSize;
1496: if (imSize == 0) {
1497: imSize = (int) (bitmapFileSize - bitmapOffset);
1498: }
1499:
1500: int padding = 0;
1501: // If width is not 32 byte aligned, then while uncompressing each
1502: // scanline will have padding bytes, calculate the amount of padding
1503: int remainder = width % 4;
1504: if (remainder != 0) {
1505: padding = 4 - remainder;
1506: }
1507:
1508: // Read till we have the whole image
1509: byte[] values = new byte[imSize];
1510: iis.readFully(values, 0, imSize);
1511:
1512: // Decompress the RLE4 compressed data.
1513: decodeRLE4(imSize, padding, values, bdata);
1514: }
1515:
1516: private void decodeRLE4(int imSize, int padding, byte[] values,
1517: byte[] bdata) throws IOException {
1518: byte[] val = new byte[width];
1519: int count = 0, l = 0;
1520: int value;
1521: boolean flag = false;
1522: int lineNo = isBottomUp ? height - 1 : 0;
1523: int lineStride = ((MultiPixelPackedSampleModel) sampleModel)
1524: .getScanlineStride();
1525: int finished = 0;
1526:
1527: while (count != imSize) {
1528:
1529: value = values[count++] & 0xFF;
1530: if (value == 0) {
1531:
1532: // Absolute mode
1533: switch (values[count++] & 0xFF) {
1534:
1535: case 0:
1536: case 1:
1537: // 0 is End-of-scanline marker, 1 is End-of-RLE marker
1538: // In either case, we want to copy the just decoded
1539: // scanline from val array to bdata array
1540: if (lineNo >= sourceRegion.y
1541: && lineNo < sourceRegion.y
1542: + sourceRegion.height) {
1543: if (noTransform) {
1544: int pos = lineNo * (width + 1 >> 1);
1545: for (int i = 0, j = 0; i < width >> 1; i++)
1546: bdata[pos++] = (byte) ((val[j++] << 4) | val[j++]);
1547: if ((width & 1) == 1)
1548: bdata[pos] |= val[width - 1] << 4;
1549:
1550: processImageUpdate(bi, 0, lineNo,
1551: destinationRegion.width, 1, 1, 1,
1552: new int[] { 0 });
1553: finished++;
1554: } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1555: int currentLine = (lineNo - sourceRegion.y)
1556: / scaleY + destinationRegion.y;
1557: int pos = currentLine * lineStride;
1558: pos += destinationRegion.x >> 1;
1559: int shift = (1 - (destinationRegion.x & 1)) << 2;
1560: for (int i = sourceRegion.x; i < sourceRegion.x
1561: + sourceRegion.width; i += scaleX) {
1562: bdata[pos] |= val[i] << shift;
1563: shift += 4;
1564: if (shift == 4) {
1565: pos++;
1566: }
1567: shift &= 7;
1568: }
1569: processImageUpdate(bi, 0, currentLine,
1570: destinationRegion.width, 1, 1, 1,
1571: new int[] { 0 });
1572: finished++;
1573: }
1574: }
1575: processImageProgress(100.0F * finished
1576: / destinationRegion.height);
1577: lineNo += isBottomUp ? -1 : 1;
1578: l = 0;
1579:
1580: if (abortRequested()) {
1581: break;
1582: }
1583:
1584: // End-of-RLE marker
1585: if ((values[count - 1] & 0xff) == 1)
1586: flag = true;
1587: break;
1588:
1589: case 2:
1590: // delta or vector marker
1591: int xoff = values[count++] & 0xFF;
1592: int yoff = values[count] & 0xFF;
1593: // Move to the position xoff, yoff down
1594: l += xoff + yoff * width;
1595: break;
1596:
1597: default:
1598: int end = values[count - 1] & 0xFF;
1599: for (int i = 0; i < end; i++) {
1600: val[l++] = (byte) (((i & 1) == 0) ? (values[count] & 0xf0) >> 4
1601: : (values[count++] & 0x0f));
1602: }
1603:
1604: // When end is odd, the above for loop does not
1605: // increment count, so do it now.
1606: if ((end & 1) == 1) {
1607: count++;
1608: }
1609:
1610: // Whenever end pixels can fit into odd number of bytes,
1611: // an extra padding byte will be present, so skip that.
1612: if ((((int) Math.ceil(end / 2)) & 1) == 1) {
1613: count++;
1614: }
1615: break;
1616: }
1617: } else {
1618: // Encoded mode
1619: int alternate[] = { (values[count] & 0xf0) >> 4,
1620: values[count] & 0x0f };
1621: for (int i = 0; (i < value) && (l < width); i++) {
1622: val[l++] = (byte) alternate[i & 1];
1623: }
1624:
1625: count++;
1626: }
1627:
1628: // If End-of-RLE data, then exit the while loop
1629: if (flag) {
1630: break;
1631: }
1632: }
1633: }
1634:
1635: /** Decodes the jpeg/png image embedded in the bitmap using any jpeg
1636: * ImageIO-style plugin.
1637: *
1638: * @param bi The destination <code>BufferedImage</code>.
1639: * @param bmpParam The <code>ImageReadParam</code> for decoding this
1640: * BMP image. The parameters for subregion, band selection and
1641: * subsampling are used in decoding the jpeg image.
1642: */
1643:
1644: private BufferedImage readEmbedded(int type, BufferedImage bi,
1645: ImageReadParam bmpParam) throws IOException {
1646: String format;
1647: switch (type) {
1648: case BI_JPEG:
1649: format = "JPEG";
1650: break;
1651: case BI_PNG:
1652: format = "PNG";
1653: break;
1654: default:
1655: throw new IOException("Unexpected compression type: "
1656: + type);
1657: }
1658: ImageReader reader = (ImageReader) ImageIO
1659: .getImageReadersByFormatName(format).next();
1660: if (reader == null) {
1661: throw new RuntimeException(I18N
1662: .getString("BMPImageReader4")
1663: + " " + format);
1664: }
1665: // prepare input
1666: byte[] buff = new byte[(int) imageSize];
1667: iis.read(buff);
1668: reader
1669: .setInput(ImageIO
1670: .createImageInputStream(new ByteArrayInputStream(
1671: buff)));
1672: if (bi == null) {
1673: ImageTypeSpecifier embType = (ImageTypeSpecifier) reader
1674: .getImageTypes(0).next();
1675: bi = embType.createBufferedImage(destinationRegion.x
1676: + destinationRegion.width, destinationRegion.y
1677: + destinationRegion.height);
1678: }
1679:
1680: reader
1681: .addIIOReadProgressListener(new EmbeddedProgressAdapter() {
1682: public void imageProgress(ImageReader source,
1683: float percentageDone) {
1684: processImageProgress(percentageDone);
1685: }
1686: });
1687:
1688: reader.addIIOReadUpdateListener(new IIOReadUpdateListener() {
1689: public void imageUpdate(ImageReader source,
1690: BufferedImage theImage, int minX, int minY,
1691: int width, int height, int periodX, int periodY,
1692: int[] bands) {
1693: processImageUpdate(theImage, minX, minY, width, height,
1694: periodX, periodY, bands);
1695: }
1696:
1697: public void passComplete(ImageReader source,
1698: BufferedImage theImage) {
1699: processPassComplete(theImage);
1700: }
1701:
1702: public void passStarted(ImageReader source,
1703: BufferedImage theImage, int pass, int minPass,
1704: int maxPass, int minX, int minY, int periodX,
1705: int periodY, int[] bands) {
1706: processPassStarted(theImage, pass, minPass, maxPass,
1707: minX, minY, periodX, periodY, bands);
1708: }
1709:
1710: public void thumbnailPassComplete(ImageReader source,
1711: BufferedImage thumb) {
1712: }
1713:
1714: public void thumbnailPassStarted(ImageReader source,
1715: BufferedImage thumb, int pass, int minPass,
1716: int maxPass, int minX, int minY, int periodX,
1717: int periodY, int[] bands) {
1718: }
1719:
1720: public void thumbnailUpdate(ImageReader source,
1721: BufferedImage theThumbnail, int minX, int minY,
1722: int width, int height, int periodX, int periodY,
1723: int[] bands) {
1724: }
1725: });
1726:
1727: reader.addIIOReadWarningListener(new IIOReadWarningListener() {
1728: public void warningOccurred(ImageReader source,
1729: String warning) {
1730: processWarningOccurred(warning);
1731: }
1732: });
1733:
1734: ImageReadParam param = reader.getDefaultReadParam();
1735: param.setDestination(bi);
1736: param.setDestinationBands(bmpParam.getDestinationBands());
1737: param.setDestinationOffset(bmpParam.getDestinationOffset());
1738: param.setSourceBands(bmpParam.getSourceBands());
1739: param.setSourceRegion(bmpParam.getSourceRegion());
1740: param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
1741: bmpParam.getSourceYSubsampling(), bmpParam
1742: .getSubsamplingXOffset(), bmpParam
1743: .getSubsamplingYOffset());
1744: reader.read(0, param);
1745: return bi;
1746: }
1747:
1748: private class EmbeddedProgressAdapter implements
1749: IIOReadProgressListener {
1750: public void imageComplete(ImageReader src) {
1751: }
1752:
1753: public void imageProgress(ImageReader src, float percentageDone) {
1754: }
1755:
1756: public void imageStarted(ImageReader src, int imageIndex) {
1757: }
1758:
1759: public void thumbnailComplete(ImageReader src) {
1760: }
1761:
1762: public void thumbnailProgress(ImageReader src,
1763: float percentageDone) {
1764: }
1765:
1766: public void thumbnailStarted(ImageReader src, int iIdx, int tIdx) {
1767: }
1768:
1769: public void sequenceComplete(ImageReader src) {
1770: }
1771:
1772: public void sequenceStarted(ImageReader src, int minIndex) {
1773: }
1774:
1775: public void readAborted(ImageReader src) {
1776: }
1777: }
1778: }
|