1 /*
   2  * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.imageio.plugins.bmp;
  27 
  28 import java.awt.Point;
  29 import java.awt.Rectangle;
  30 import java.awt.Transparency;
  31 import java.awt.color.ColorSpace;
  32 import java.awt.color.ICC_ColorSpace;
  33 import java.awt.color.ICC_Profile;
  34 import java.awt.image.BufferedImage;
  35 import java.awt.image.ColorModel;
  36 import java.awt.image.ComponentColorModel;
  37 import java.awt.image.ComponentSampleModel;
  38 import java.awt.image.DataBuffer;
  39 import java.awt.image.DataBufferByte;
  40 import java.awt.image.DataBufferInt;
  41 import java.awt.image.DataBufferUShort;
  42 import java.awt.image.DirectColorModel;
  43 import java.awt.image.IndexColorModel;
  44 import java.awt.image.MultiPixelPackedSampleModel;
  45 import java.awt.image.PixelInterleavedSampleModel;
  46 import java.awt.image.Raster;
  47 import java.awt.image.SampleModel;
  48 import java.awt.image.SinglePixelPackedSampleModel;
  49 import java.awt.image.WritableRaster;
  50 
  51 import javax.imageio.IIOException;
  52 import javax.imageio.ImageIO;
  53 import javax.imageio.ImageReader;
  54 import javax.imageio.ImageReadParam;
  55 import javax.imageio.ImageTypeSpecifier;
  56 import javax.imageio.metadata.IIOMetadata;
  57 import javax.imageio.spi.ImageReaderSpi;
  58 import javax.imageio.stream.ImageInputStream;
  59 import javax.imageio.event.IIOReadProgressListener;
  60 import javax.imageio.event.IIOReadUpdateListener;
  61 import javax.imageio.event.IIOReadWarningListener;
  62 
  63 import java.io.*;
  64 import java.nio.*;
  65 import java.security.AccessController;
  66 import java.security.PrivilegedAction;
  67 import java.util.ArrayList;
  68 import java.util.Iterator;
  69 import java.util.StringTokenizer;
  70 
  71 import com.sun.imageio.plugins.common.ImageUtil;
  72 import com.sun.imageio.plugins.common.I18N;
  73 
  74 /** This class is the Java Image IO plugin reader for BMP images.
  75  *  It may subsample the image, clip the image, select sub-bands,
  76  *  and shift the decoded image origin if the proper decoding parameter
  77  *  are set in the provided <code>ImageReadParam</code>.
  78  *
  79  *  This class supports Microsoft Windows Bitmap Version 3-5,
  80  *  as well as OS/2 Bitmap Version 2.x (for single-image BMP file).
  81  */
  82 public class BMPImageReader extends ImageReader implements BMPConstants {
  83     // BMP Image types
  84     private static final int VERSION_2_1_BIT = 0;
  85     private static final int VERSION_2_4_BIT = 1;
  86     private static final int VERSION_2_8_BIT = 2;
  87     private static final int VERSION_2_24_BIT = 3;
  88 
  89     private static final int VERSION_3_1_BIT = 4;
  90     private static final int VERSION_3_4_BIT = 5;
  91     private static final int VERSION_3_8_BIT = 6;
  92     private static final int VERSION_3_24_BIT = 7;
  93 
  94     private static final int VERSION_3_NT_16_BIT = 8;
  95     private static final int VERSION_3_NT_32_BIT = 9;
  96 
  97     private static final int VERSION_4_1_BIT = 10;
  98     private static final int VERSION_4_4_BIT = 11;
  99     private static final int VERSION_4_8_BIT = 12;
 100     private static final int VERSION_4_16_BIT = 13;
 101     private static final int VERSION_4_24_BIT = 14;
 102     private static final int VERSION_4_32_BIT = 15;
 103 
 104     private static final int VERSION_3_XP_EMBEDDED = 16;
 105     private static final int VERSION_4_XP_EMBEDDED = 17;
 106     private static final int VERSION_5_XP_EMBEDDED = 18;
 107 
 108     // BMP variables
 109     private long bitmapFileSize;
 110     private long bitmapOffset;
 111     private long compression;
 112     private long imageSize;
 113     private byte palette[];
 114     private int imageType;
 115     private int numBands;
 116     private boolean isBottomUp;
 117     private int bitsPerPixel;
 118     private int redMask, greenMask, blueMask, alphaMask;
 119 
 120     private SampleModel sampleModel, originalSampleModel;
 121     private ColorModel colorModel, originalColorModel;
 122 
 123     /** The input stream where reads from */
 124     private ImageInputStream iis = null;
 125 
 126     /** Indicates whether the header is read. */
 127     private boolean gotHeader = false;
 128 
 129     /** The original image width. */
 130     private int width;
 131 
 132     /** The original image height. */
 133     private int height;
 134 
 135     /** The destination region. */
 136     private Rectangle destinationRegion;
 137 
 138     /** The source region. */
 139     private Rectangle sourceRegion;
 140 
 141     /** The metadata from the stream. */
 142     private BMPMetadata metadata;
 143 
 144     /** The destination image. */
 145     private BufferedImage bi;
 146 
 147     /** Indicates whether subsampled, subregion is required, and offset is
 148      *  defined
 149      */
 150     private boolean noTransform = true;
 151 
 152     /** Indicates whether subband is selected. */
 153     private boolean seleBand = false;
 154 
 155     /** The scaling factors. */
 156     private int scaleX, scaleY;
 157 
 158     /** source and destination bands. */
 159     private int[] sourceBands, destBands;
 160 
 161     /** Constructs <code>BMPImageReader</code> from the provided
 162      *  <code>ImageReaderSpi</code>.
 163      */
 164     public BMPImageReader(ImageReaderSpi originator) {
 165         super(originator);
 166     }
 167 
 168     /** Overrides the method defined in the superclass. */
 169     public void setInput(Object input,
 170                          boolean seekForwardOnly,
 171                          boolean ignoreMetadata) {
 172         super.setInput(input, seekForwardOnly, ignoreMetadata);
 173         iis = (ImageInputStream) input; // Always works
 174         if(iis != null)
 175             iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
 176         resetHeaderInfo();
 177     }
 178 
 179     /** Overrides the method defined in the superclass. */
 180     public int getNumImages(boolean allowSearch) throws IOException {
 181         if (iis == null) {
 182             throw new IllegalStateException(I18N.getString("GetNumImages0"));
 183         }
 184         if (seekForwardOnly && allowSearch) {
 185             throw new IllegalStateException(I18N.getString("GetNumImages1"));
 186         }
 187         return 1;
 188     }
 189 
 190     @Override
 191     public int getWidth(int imageIndex) throws IOException {
 192         checkIndex(imageIndex);
 193         try {
 194             readHeader();
 195         } catch (IllegalArgumentException e) {
 196             throw new IIOException(I18N.getString("BMPImageReader6"), e);
 197         }
 198         return width;
 199     }
 200 
 201     public int getHeight(int imageIndex) throws IOException {
 202         checkIndex(imageIndex);
 203         try {
 204             readHeader();
 205         } catch (IllegalArgumentException e) {
 206             throw new IIOException(I18N.getString("BMPImageReader6"), e);
 207         }
 208         return height;
 209     }
 210 
 211     private void checkIndex(int imageIndex) {
 212         if (imageIndex != 0) {
 213             throw new IndexOutOfBoundsException(I18N.getString("BMPImageReader0"));
 214         }
 215     }
 216 
 217     /**
 218      * Process the image header.
 219      *
 220      * @exception IllegalStateException if source stream is not set.
 221      *
 222      * @exception IOException if image stream is corrupted.
 223      *
 224      * @exception IllegalArgumentException if the image stream does not contain
 225      *             a BMP image, or if a sample model instance to describe the
 226      *             image can not be created.
 227      */
 228     protected void readHeader() throws IOException, IllegalArgumentException {
 229         if (gotHeader)
 230             return;
 231 
 232         if (iis == null) {
 233             throw new IllegalStateException("Input source not set!");
 234         }
 235         int profileData = 0, profileSize = 0;
 236 
 237         this.metadata = new BMPMetadata();
 238         iis.mark();
 239 
 240         // read and check the magic marker
 241         byte[] marker = new byte[2];
 242         iis.read(marker);
 243         if (marker[0] != 0x42 || marker[1] != 0x4d)
 244             throw new IllegalArgumentException(I18N.getString("BMPImageReader1"));
 245 
 246         // Read file size
 247         bitmapFileSize = iis.readUnsignedInt();
 248         // skip the two reserved fields
 249         iis.skipBytes(4);
 250 
 251         // Offset to the bitmap from the beginning
 252         bitmapOffset = iis.readUnsignedInt();
 253         // End File Header
 254 
 255         // Start BitmapCoreHeader
 256         long size = iis.readUnsignedInt();
 257 
 258         if (size == 12) {
 259             width = iis.readShort();
 260             height = iis.readShort();
 261         } else {
 262             width = iis.readInt();
 263             height = iis.readInt();
 264         }
 265 
 266         metadata.width = width;
 267         metadata.height = height;
 268 
 269         int planes = iis.readUnsignedShort();
 270         bitsPerPixel = iis.readUnsignedShort();
 271 
 272         //metadata.colorPlane = planes;
 273         metadata.bitsPerPixel = (short)bitsPerPixel;
 274 
 275         // As BMP always has 3 rgb bands, except for Version 5,
 276         // which is bgra
 277         numBands = 3;
 278 
 279         if (size == 12) {
 280             // Windows 2.x and OS/2 1.x
 281             metadata.bmpVersion = VERSION_2;
 282 
 283             // Classify the image type
 284             if (bitsPerPixel == 1) {
 285                 imageType = VERSION_2_1_BIT;
 286             } else if (bitsPerPixel == 4) {
 287                 imageType = VERSION_2_4_BIT;
 288             } else if (bitsPerPixel == 8) {
 289                 imageType = VERSION_2_8_BIT;
 290             } else if (bitsPerPixel == 24) {
 291                 imageType = VERSION_2_24_BIT;
 292             }
 293 
 294             // Read in the palette
 295             int numberOfEntries = (int)((bitmapOffset - 14 - size) / 3);
 296             int sizeOfPalette = numberOfEntries*3;
 297             palette = new byte[sizeOfPalette];
 298             iis.readFully(palette, 0, sizeOfPalette);
 299             metadata.palette = palette;
 300             metadata.paletteSize = numberOfEntries;
 301         } else {
 302             compression = iis.readUnsignedInt();
 303             imageSize = iis.readUnsignedInt();
 304             long xPelsPerMeter = iis.readInt();
 305             long yPelsPerMeter = iis.readInt();
 306             long colorsUsed = iis.readUnsignedInt();
 307             long colorsImportant = iis.readUnsignedInt();
 308 
 309             metadata.compression = (int)compression;
 310             metadata.xPixelsPerMeter = (int)xPelsPerMeter;
 311             metadata.yPixelsPerMeter = (int)yPelsPerMeter;
 312             metadata.colorsUsed = (int)colorsUsed;
 313             metadata.colorsImportant = (int)colorsImportant;
 314 
 315             if (size == 40) {
 316                 // Windows 3.x and Windows NT
 317                 switch((int)compression) {
 318 
 319                 case BI_JPEG:
 320                 case BI_PNG:
 321                     metadata.bmpVersion = VERSION_3;
 322                     imageType = VERSION_3_XP_EMBEDDED;
 323                     break;
 324 
 325                 case BI_RGB:  // No compression
 326                 case BI_RLE8:  // 8-bit RLE compression
 327                 case BI_RLE4:  // 4-bit RLE compression
 328 
 329                     // Read in the palette
 330                     if (bitmapOffset < (size + 14)) {
 331                         throw new IIOException(I18N.getString("BMPImageReader7"));
 332                     }
 333                     int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
 334                     int sizeOfPalette = numberOfEntries * 4;
 335                     palette = new byte[sizeOfPalette];
 336                     iis.readFully(palette, 0, sizeOfPalette);
 337 
 338                     metadata.palette = palette;
 339                     metadata.paletteSize = numberOfEntries;
 340 
 341                     if (bitsPerPixel == 1) {
 342                         imageType = VERSION_3_1_BIT;
 343                     } else if (bitsPerPixel == 4) {
 344                         imageType = VERSION_3_4_BIT;
 345                     } else if (bitsPerPixel == 8) {
 346                         imageType = VERSION_3_8_BIT;
 347                     } else if (bitsPerPixel == 24) {
 348                         imageType = VERSION_3_24_BIT;
 349                     } else if (bitsPerPixel == 16) {
 350                         imageType = VERSION_3_NT_16_BIT;
 351 
 352                         redMask = 0x7C00;
 353                         greenMask = 0x3E0;
 354                         blueMask =  (1 << 5) - 1;// 0x1F;
 355                         metadata.redMask = redMask;
 356                         metadata.greenMask = greenMask;
 357                         metadata.blueMask = blueMask;
 358                     } else if (bitsPerPixel == 32) {
 359                         imageType = VERSION_3_NT_32_BIT;
 360                         redMask   = 0x00FF0000;
 361                         greenMask = 0x0000FF00;
 362                         blueMask  = 0x000000FF;
 363                         metadata.redMask = redMask;
 364                         metadata.greenMask = greenMask;
 365                         metadata.blueMask = blueMask;
 366                     }
 367 
 368                     metadata.bmpVersion = VERSION_3;
 369                     break;
 370 
 371                 case BI_BITFIELDS:
 372 
 373                     if (bitsPerPixel == 16) {
 374                         imageType = VERSION_3_NT_16_BIT;
 375                     } else if (bitsPerPixel == 32) {
 376                         imageType = VERSION_3_NT_32_BIT;
 377                     }
 378 
 379                     // BitsField encoding
 380                     redMask = (int)iis.readUnsignedInt();
 381                     greenMask = (int)iis.readUnsignedInt();
 382                     blueMask = (int)iis.readUnsignedInt();
 383                     metadata.redMask = redMask;
 384                     metadata.greenMask = greenMask;
 385                     metadata.blueMask = blueMask;
 386 
 387                     if (colorsUsed != 0) {
 388                         // there is a palette
 389                         sizeOfPalette = (int)colorsUsed*4;
 390                         palette = new byte[sizeOfPalette];
 391                         iis.readFully(palette, 0, sizeOfPalette);
 392 
 393                         metadata.palette = palette;
 394                         metadata.paletteSize = (int)colorsUsed;
 395                     }
 396                     metadata.bmpVersion = VERSION_3_NT;
 397 
 398                     break;
 399                 default:
 400                     throw new
 401                         IIOException(I18N.getString("BMPImageReader2"));
 402                 }
 403             } else if (size == 108 || size == 124) {
 404                 // Windows 4.x BMP
 405                 if (size == 108)
 406                     metadata.bmpVersion = VERSION_4;
 407                 else if (size == 124)
 408                     metadata.bmpVersion = VERSION_5;
 409 
 410                 // rgb masks, valid only if comp is BI_BITFIELDS
 411                 redMask = (int)iis.readUnsignedInt();
 412                 greenMask = (int)iis.readUnsignedInt();
 413                 blueMask = (int)iis.readUnsignedInt();
 414                 // Only supported for 32bpp BI_RGB argb
 415                 alphaMask = (int)iis.readUnsignedInt();
 416                 long csType = iis.readUnsignedInt();
 417                 int redX = iis.readInt();
 418                 int redY = iis.readInt();
 419                 int redZ = iis.readInt();
 420                 int greenX = iis.readInt();
 421                 int greenY = iis.readInt();
 422                 int greenZ = iis.readInt();
 423                 int blueX = iis.readInt();
 424                 int blueY = iis.readInt();
 425                 int blueZ = iis.readInt();
 426                 long gammaRed = iis.readUnsignedInt();
 427                 long gammaGreen = iis.readUnsignedInt();
 428                 long gammaBlue = iis.readUnsignedInt();
 429 
 430                 if (size == 124) {
 431                     metadata.intent = iis.readInt();
 432                     profileData = iis.readInt();
 433                     profileSize = iis.readInt();
 434                     iis.skipBytes(4);
 435                 }
 436 
 437                 metadata.colorSpace = (int)csType;
 438 
 439                 if (csType == LCS_CALIBRATED_RGB) {
 440                     // All the new fields are valid only for this case
 441                     metadata.redX = redX;
 442                     metadata.redY = redY;
 443                     metadata.redZ = redZ;
 444                     metadata.greenX = greenX;
 445                     metadata.greenY = greenY;
 446                     metadata.greenZ = greenZ;
 447                     metadata.blueX = blueX;
 448                     metadata.blueY = blueY;
 449                     metadata.blueZ = blueZ;
 450                     metadata.gammaRed = (int)gammaRed;
 451                     metadata.gammaGreen = (int)gammaGreen;
 452                     metadata.gammaBlue = (int)gammaBlue;
 453                 }
 454 
 455                 // Read in the palette
 456                 int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
 457                 int sizeOfPalette = numberOfEntries*4;
 458                 palette = new byte[sizeOfPalette];
 459                 iis.readFully(palette, 0, sizeOfPalette);
 460                 metadata.palette = palette;
 461                 metadata.paletteSize = numberOfEntries;
 462 
 463                 switch ((int)compression) {
 464                 case BI_JPEG:
 465                 case BI_PNG:
 466                     if (size == 108) {
 467                         imageType = VERSION_4_XP_EMBEDDED;
 468                     } else if (size == 124) {
 469                         imageType = VERSION_5_XP_EMBEDDED;
 470                     }
 471                     break;
 472                 default:
 473                     if (bitsPerPixel == 1) {
 474                         imageType = VERSION_4_1_BIT;
 475                     } else if (bitsPerPixel == 4) {
 476                         imageType = VERSION_4_4_BIT;
 477                     } else if (bitsPerPixel == 8) {
 478                         imageType = VERSION_4_8_BIT;
 479                     } else if (bitsPerPixel == 16) {
 480                         imageType = VERSION_4_16_BIT;
 481                         if ((int)compression == BI_RGB) {
 482                             redMask = 0x7C00;
 483                             greenMask = 0x3E0;
 484                             blueMask = 0x1F;
 485                         }
 486                     } else if (bitsPerPixel == 24) {
 487                         imageType = VERSION_4_24_BIT;
 488                     } else if (bitsPerPixel == 32) {
 489                         imageType = VERSION_4_32_BIT;
 490                         if ((int)compression == BI_RGB) {
 491                             redMask   = 0x00FF0000;
 492                             greenMask = 0x0000FF00;
 493                             blueMask  = 0x000000FF;
 494                         }
 495                     }
 496 
 497                     metadata.redMask = redMask;
 498                     metadata.greenMask = greenMask;
 499                     metadata.blueMask = blueMask;
 500                     metadata.alphaMask = alphaMask;
 501                 }
 502             } else {
 503                 throw new
 504                     IIOException(I18N.getString("BMPImageReader3"));
 505             }
 506         }
 507 
 508         if (height > 0) {
 509             // bottom up image
 510             isBottomUp = true;
 511         } else {
 512             // top down image
 513             isBottomUp = false;
 514             height = Math.abs(height);
 515         }
 516 
 517         // Reset Image Layout so there's only one tile.
 518         //Define the color space
 519         ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
 520         if (metadata.colorSpace == PROFILE_LINKED ||
 521             metadata.colorSpace == PROFILE_EMBEDDED) {
 522 
 523             iis.mark();
 524             iis.skipBytes(profileData - size);
 525             byte[] profile = new byte[profileSize];
 526             iis.readFully(profile, 0, profileSize);
 527             iis.reset();
 528 
 529             try {
 530                 if (metadata.colorSpace == PROFILE_LINKED &&
 531                     isLinkedProfileAllowed() &&
 532                     !isUncOrDevicePath(profile))
 533                 {
 534                     String path = new String(profile, "windows-1252");
 535 
 536                     colorSpace =
 537                         new ICC_ColorSpace(ICC_Profile.getInstance(path));
 538                 } else {
 539                     colorSpace =
 540                         new ICC_ColorSpace(ICC_Profile.getInstance(profile));
 541                 }
 542             } catch (Exception e) {
 543                 colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
 544             }
 545         }
 546 
 547         if (bitsPerPixel == 0 ||
 548             compression == BI_JPEG || compression == BI_PNG )
 549         {
 550             // the colorModel and sampleModel will be initialzed
 551             // by the  reader of embedded image
 552             colorModel = null;
 553             sampleModel = null;
 554         } else if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
 555             // When number of bitsPerPixel is <= 8, we use IndexColorModel.
 556             numBands = 1;
 557 
 558             if (bitsPerPixel == 8) {
 559                 int[] bandOffsets = new int[numBands];
 560                 for (int i = 0; i < numBands; i++) {
 561                     bandOffsets[i] = numBands -1 -i;
 562                 }
 563                 sampleModel =
 564                     new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
 565                                                     width, height,
 566                                                     numBands,
 567                                                     numBands * width,
 568                                                     bandOffsets);
 569             } else {
 570                 // 1 and 4 bit pixels can be stored in a packed format.
 571                 sampleModel =
 572                     new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
 573                                                     width, height,
 574                                                     bitsPerPixel);
 575             }
 576 
 577             // Create IndexColorModel from the palette.
 578             byte r[], g[], b[];
 579             if (imageType == VERSION_2_1_BIT ||
 580                 imageType == VERSION_2_4_BIT ||
 581                 imageType == VERSION_2_8_BIT) {
 582 
 583 
 584                 size = palette.length/3;
 585 
 586                 if (size > 256) {
 587                     size = 256;
 588                 }
 589 
 590                 int off;
 591                 r = new byte[(int)size];
 592                 g = new byte[(int)size];
 593                 b = new byte[(int)size];
 594                 for (int i=0; i<(int)size; i++) {
 595                     off = 3 * i;
 596                     b[i] = palette[off];
 597                     g[i] = palette[off+1];
 598                     r[i] = palette[off+2];
 599                 }
 600             } else {
 601                 size = palette.length/4;
 602 
 603                 if (size > 256) {
 604                     size = 256;
 605                 }
 606 
 607                 int off;
 608                 r = new byte[(int)size];
 609                 g = new byte[(int)size];
 610                 b = new byte[(int)size];
 611                 for (int i=0; i<size; i++) {
 612                     off = 4 * i;
 613                     b[i] = palette[off];
 614                     g[i] = palette[off+1];
 615                     r[i] = palette[off+2];
 616                 }
 617             }
 618 
 619             if (ImageUtil.isIndicesForGrayscale(r, g, b))
 620                 colorModel =
 621                     ImageUtil.createColorModel(null, sampleModel);
 622             else
 623                 colorModel = new IndexColorModel(bitsPerPixel, (int)size, r, g, b);
 624         } else if (bitsPerPixel == 16) {
 625             numBands = 3;
 626             sampleModel =
 627                 new SinglePixelPackedSampleModel(DataBuffer.TYPE_USHORT,
 628                                                  width, height,
 629                                                  new int[] {redMask, greenMask, blueMask});
 630 
 631             colorModel =
 632                 new DirectColorModel(colorSpace,
 633                                      16, redMask, greenMask, blueMask, 0,
 634                                      false, DataBuffer.TYPE_USHORT);
 635 
 636         } else if (bitsPerPixel == 32) {
 637             numBands = alphaMask == 0 ? 3 : 4;
 638 
 639             // The number of bands in the SampleModel is determined by
 640             // the length of the mask array passed in.
 641             int[] bitMasks = numBands == 3 ?
 642                 new int[] {redMask, greenMask, blueMask} :
 643                 new int[] {redMask, greenMask, blueMask, alphaMask};
 644 
 645                 sampleModel =
 646                     new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
 647                                                      width, height,
 648                                                      bitMasks);
 649 
 650                 colorModel =
 651                     new DirectColorModel(colorSpace,
 652                                          32, redMask, greenMask, blueMask, alphaMask,
 653                                          false, DataBuffer.TYPE_INT);
 654         } else {
 655             numBands = 3;
 656             // Create SampleModel
 657             int[] bandOffsets = new int[numBands];
 658             for (int i = 0; i < numBands; i++) {
 659                 bandOffsets[i] = numBands -1 -i;
 660             }
 661 
 662             sampleModel =
 663                 new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
 664                                                 width, height,
 665                                                 numBands,
 666                                                 numBands * width,
 667                                                 bandOffsets);
 668 
 669             colorModel =
 670                 ImageUtil.createColorModel(colorSpace, sampleModel);
 671         }
 672 
 673         originalSampleModel = sampleModel;
 674         originalColorModel = colorModel;
 675 
 676         // Reset to the start of bitmap; then jump to the
 677         //start of image data
 678         iis.reset();
 679         iis.skipBytes(bitmapOffset);
 680         gotHeader = true;
 681     }
 682 
 683     public Iterator getImageTypes(int imageIndex)
 684       throws IOException {
 685         checkIndex(imageIndex);
 686         try {
 687             readHeader();
 688         } catch (IllegalArgumentException e) {
 689             throw new IIOException(I18N.getString("BMPImageReader6"), e);
 690         }
 691         ArrayList list = new ArrayList(1);
 692         list.add(new ImageTypeSpecifier(originalColorModel,
 693                                         originalSampleModel));
 694         return list.iterator();
 695     }
 696 
 697     public ImageReadParam getDefaultReadParam() {
 698         return new ImageReadParam();
 699     }
 700 
 701     public IIOMetadata getImageMetadata(int imageIndex)
 702       throws IOException {
 703         checkIndex(imageIndex);
 704         if (metadata == null) {
 705             try {
 706                 readHeader();
 707             } catch (IllegalArgumentException e) {
 708                 throw new IIOException(I18N.getString("BMPImageReader6"), e);
 709             }
 710         }
 711         return metadata;
 712     }
 713 
 714     public IIOMetadata getStreamMetadata() throws IOException {
 715         return null;
 716     }
 717 
 718     public boolean isRandomAccessEasy(int imageIndex) throws IOException {
 719         checkIndex(imageIndex);
 720         try {
 721             readHeader();
 722         } catch (IllegalArgumentException e) {
 723             throw new IIOException(I18N.getString("BMPImageReader6"), e);
 724         }
 725         return metadata.compression == BI_RGB;
 726     }
 727 
 728     public BufferedImage read(int imageIndex, ImageReadParam param)
 729         throws IOException {
 730 
 731         if (iis == null) {
 732             throw new IllegalStateException(I18N.getString("BMPImageReader5"));
 733         }
 734 
 735         checkIndex(imageIndex);
 736         clearAbortRequest();
 737         processImageStarted(imageIndex);
 738 
 739         if (param == null)
 740             param = getDefaultReadParam();
 741 
 742         //read header
 743         try {
 744             readHeader();
 745         } catch (IllegalArgumentException e) {
 746             throw new IIOException(I18N.getString("BMPImageReader6"), e);
 747         }
 748 
 749         sourceRegion = new Rectangle(0, 0, 0, 0);
 750         destinationRegion = new Rectangle(0, 0, 0, 0);
 751 
 752         computeRegions(param, this.width, this.height,
 753                        param.getDestination(),
 754                        sourceRegion,
 755                        destinationRegion);
 756 
 757         scaleX = param.getSourceXSubsampling();
 758         scaleY = param.getSourceYSubsampling();
 759 
 760         // If the destination band is set used it
 761         sourceBands = param.getSourceBands();
 762         destBands = param.getDestinationBands();
 763 
 764         seleBand = (sourceBands != null) && (destBands != null);
 765         noTransform =
 766             destinationRegion.equals(new Rectangle(0, 0, width, height)) ||
 767             seleBand;
 768 
 769         if (!seleBand) {
 770             sourceBands = new int[numBands];
 771             destBands = new int[numBands];
 772             for (int i = 0; i < numBands; i++)
 773                 destBands[i] = sourceBands[i] = i;
 774         }
 775 
 776         // If the destination is provided, then use it.  Otherwise, create new one
 777         bi = param.getDestination();
 778 
 779         // Get the image data.
 780         WritableRaster raster = null;
 781 
 782         if (bi == null) {
 783             if (sampleModel != null && colorModel != null) {
 784                 sampleModel =
 785                     sampleModel.createCompatibleSampleModel(destinationRegion.x +
 786                                                             destinationRegion.width,
 787                                                             destinationRegion.y +
 788                                                             destinationRegion.height);
 789                 if (seleBand)
 790                     sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
 791                 raster = Raster.createWritableRaster(sampleModel, new Point());
 792                 bi = new BufferedImage(colorModel, raster, false, null);
 793             }
 794         } else {
 795             raster = bi.getWritableTile(0, 0);
 796             sampleModel = bi.getSampleModel();
 797             colorModel = bi.getColorModel();
 798 
 799             noTransform &=  destinationRegion.equals(raster.getBounds());
 800         }
 801 
 802         byte bdata[] = null; // buffer for byte data
 803         short sdata[] = null; // buffer for short data
 804         int idata[] = null; // buffer for int data
 805 
 806         // the sampleModel can be null in case of embedded image
 807         if (sampleModel != null) {
 808             if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
 809                 bdata = (byte[])
 810                     ((DataBufferByte)raster.getDataBuffer()).getData();
 811             else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
 812                 sdata = (short[])
 813                     ((DataBufferUShort)raster.getDataBuffer()).getData();
 814             else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
 815                 idata = (int[])
 816                     ((DataBufferInt)raster.getDataBuffer()).getData();
 817         }
 818 
 819         // There should only be one tile.
 820         switch(imageType) {
 821 
 822         case VERSION_2_1_BIT:
 823             // no compression
 824             read1Bit(bdata);
 825             break;
 826 
 827         case VERSION_2_4_BIT:
 828             // no compression
 829             read4Bit(bdata);
 830             break;
 831 
 832         case VERSION_2_8_BIT:
 833             // no compression
 834             read8Bit(bdata);
 835             break;
 836 
 837         case VERSION_2_24_BIT:
 838             // no compression
 839             read24Bit(bdata);
 840             break;
 841 
 842         case VERSION_3_1_BIT:
 843             // 1-bit images cannot be compressed.
 844             read1Bit(bdata);
 845             break;
 846 
 847         case VERSION_3_4_BIT:
 848             switch((int)compression) {
 849             case BI_RGB:
 850                 read4Bit(bdata);
 851                 break;
 852 
 853             case BI_RLE4:
 854                 readRLE4(bdata);
 855                 break;
 856 
 857             default:
 858                 throw new
 859                     IIOException(I18N.getString("BMPImageReader1"));
 860             }
 861             break;
 862 
 863         case VERSION_3_8_BIT:
 864             switch((int)compression) {
 865             case BI_RGB:
 866                 read8Bit(bdata);
 867                 break;
 868 
 869             case BI_RLE8:
 870                 readRLE8(bdata);
 871                 break;
 872 
 873             default:
 874                 throw new
 875                     IIOException(I18N.getString("BMPImageReader1"));
 876             }
 877 
 878             break;
 879 
 880         case VERSION_3_24_BIT:
 881             // 24-bit images are not compressed
 882             read24Bit(bdata);
 883             break;
 884 
 885         case VERSION_3_NT_16_BIT:
 886             read16Bit(sdata);
 887             break;
 888 
 889         case VERSION_3_NT_32_BIT:
 890             read32Bit(idata);
 891             break;
 892 
 893         case VERSION_3_XP_EMBEDDED:
 894         case VERSION_4_XP_EMBEDDED:
 895         case VERSION_5_XP_EMBEDDED:
 896             bi = readEmbedded((int)compression, bi, param);
 897             break;
 898 
 899         case VERSION_4_1_BIT:
 900             read1Bit(bdata);
 901             break;
 902 
 903         case VERSION_4_4_BIT:
 904             switch((int)compression) {
 905 
 906             case BI_RGB:
 907                 read4Bit(bdata);
 908                 break;
 909 
 910             case BI_RLE4:
 911                 readRLE4(bdata);
 912                 break;
 913 
 914             default:
 915                 throw new
 916                     IIOException(I18N.getString("BMPImageReader1"));
 917             }
 918 
 919         case VERSION_4_8_BIT:
 920             switch((int)compression) {
 921 
 922             case BI_RGB:
 923                 read8Bit(bdata);
 924                 break;
 925 
 926             case BI_RLE8:
 927                 readRLE8(bdata);
 928                 break;
 929 
 930             default:
 931                 throw new
 932                     IIOException(I18N.getString("BMPImageReader1"));
 933             }
 934             break;
 935 
 936         case VERSION_4_16_BIT:
 937             read16Bit(sdata);
 938             break;
 939 
 940         case VERSION_4_24_BIT:
 941             read24Bit(bdata);
 942             break;
 943 
 944         case VERSION_4_32_BIT:
 945             read32Bit(idata);
 946             break;
 947         }
 948 
 949         if (abortRequested())
 950             processReadAborted();
 951         else
 952             processImageComplete();
 953 
 954         return bi;
 955     }
 956 
 957     public boolean canReadRaster() {
 958         return true;
 959     }
 960 
 961     public Raster readRaster(int imageIndex,
 962                              ImageReadParam param) throws IOException {
 963         BufferedImage bi = read(imageIndex, param);
 964         return bi.getData();
 965     }
 966 
 967     private void resetHeaderInfo() {
 968         gotHeader = false;
 969         bi = null;
 970         sampleModel = originalSampleModel = null;
 971         colorModel = originalColorModel = null;
 972     }
 973 
 974     public void reset() {
 975         super.reset();
 976         iis = null;
 977         resetHeaderInfo();
 978     }
 979 
 980     // Deal with 1 Bit images using IndexColorModels
 981     private void read1Bit(byte[] bdata) throws IOException {
 982         int bytesPerScanline = (width + 7) / 8;
 983         int padding = bytesPerScanline % 4;
 984         if (padding != 0) {
 985             padding = 4 - padding;
 986         }
 987 
 988         int lineLength = bytesPerScanline + padding;
 989 
 990         if (noTransform) {
 991             int j = isBottomUp ? (height -1)*bytesPerScanline : 0;
 992 
 993             for (int i=0; i<height; i++) {
 994                 if (abortRequested()) {
 995                     break;
 996                 }
 997                 iis.readFully(bdata, j, bytesPerScanline);
 998                 iis.skipBytes(padding);
 999                 j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
1000                 processImageUpdate(bi, 0, i,
1001                                    destinationRegion.width, 1, 1, 1,
1002                                    new int[]{0});
1003                 processImageProgress(100.0F * i/destinationRegion.height);
1004             }
1005         } else {
1006             byte[] buf = new byte[lineLength];
1007             int lineStride =
1008                 ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1009 
1010             if (isBottomUp) {
1011                 int lastLine =
1012                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1013                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1014             } else
1015                 iis.skipBytes(lineLength * sourceRegion.y);
1016 
1017             int skipLength = lineLength * (scaleY - 1);
1018 
1019             // cache the values to avoid duplicated computation
1020             int[] srcOff = new int[destinationRegion.width];
1021             int[] destOff = new int[destinationRegion.width];
1022             int[] srcPos = new int[destinationRegion.width];
1023             int[] destPos = new int[destinationRegion.width];
1024 
1025             for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
1026                  i < destinationRegion.x + destinationRegion.width;
1027                  i++, j++, x += scaleX) {
1028                 srcPos[j] = x >> 3;
1029                 srcOff[j] = 7 - (x & 7);
1030                 destPos[j] = i >> 3;
1031                 destOff[j] = 7 - (i & 7);
1032             }
1033 
1034             int k = destinationRegion.y * lineStride;
1035             if (isBottomUp)
1036                 k += (destinationRegion.height - 1) * lineStride;
1037 
1038             for (int j = 0, y = sourceRegion.y;
1039                  j < destinationRegion.height; j++, y+=scaleY) {
1040 
1041                 if (abortRequested())
1042                     break;
1043                 iis.read(buf, 0, lineLength);
1044                 for (int i = 0; i < destinationRegion.width; i++) {
1045                     //get the bit and assign to the data buffer of the raster
1046                     int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
1047                     bdata[k + destPos[i]] |= v << destOff[i];
1048                 }
1049 
1050                 k += isBottomUp ? -lineStride : lineStride;
1051                 iis.skipBytes(skipLength);
1052                 processImageUpdate(bi, 0, j,
1053                                    destinationRegion.width, 1, 1, 1,
1054                                    new int[]{0});
1055                 processImageProgress(100.0F*j/destinationRegion.height);
1056             }
1057         }
1058     }
1059 
1060     // Method to read a 4 bit BMP image data
1061     private void read4Bit(byte[] bdata) throws IOException {
1062 
1063         int bytesPerScanline = (width + 1) / 2;
1064 
1065         // Padding bytes at the end of each scanline
1066         int padding = bytesPerScanline % 4;
1067         if (padding != 0)
1068             padding = 4 - padding;
1069 
1070         int lineLength = bytesPerScanline + padding;
1071 
1072         if (noTransform) {
1073             int j = isBottomUp ? (height -1) * bytesPerScanline : 0;
1074 
1075             for (int i=0; i<height; i++) {
1076                 if (abortRequested()) {
1077                     break;
1078                 }
1079                 iis.readFully(bdata, j, bytesPerScanline);
1080                 iis.skipBytes(padding);
1081                 j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
1082                 processImageUpdate(bi, 0, i,
1083                                    destinationRegion.width, 1, 1, 1,
1084                                    new int[]{0});
1085                 processImageProgress(100.0F * i/destinationRegion.height);
1086             }
1087         } else {
1088             byte[] buf = new byte[lineLength];
1089             int lineStride =
1090                 ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1091 
1092             if (isBottomUp) {
1093                 int lastLine =
1094                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1095                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1096             } else
1097                 iis.skipBytes(lineLength * sourceRegion.y);
1098 
1099             int skipLength = lineLength * (scaleY - 1);
1100 
1101             // cache the values to avoid duplicated computation
1102             int[] srcOff = new int[destinationRegion.width];
1103             int[] destOff = new int[destinationRegion.width];
1104             int[] srcPos = new int[destinationRegion.width];
1105             int[] destPos = new int[destinationRegion.width];
1106 
1107             for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
1108                  i < destinationRegion.x + destinationRegion.width;
1109                  i++, j++, x += scaleX) {
1110                 srcPos[j] = x >> 1;
1111                 srcOff[j] = (1 - (x & 1)) << 2;
1112                 destPos[j] = i >> 1;
1113                 destOff[j] = (1 - (i & 1)) << 2;
1114             }
1115 
1116             int k = destinationRegion.y * lineStride;
1117             if (isBottomUp)
1118                 k += (destinationRegion.height - 1) * lineStride;
1119 
1120             for (int j = 0, y = sourceRegion.y;
1121                  j < destinationRegion.height; j++, y+=scaleY) {
1122 
1123                 if (abortRequested())
1124                     break;
1125                 iis.read(buf, 0, lineLength);
1126                 for (int i = 0; i < destinationRegion.width; i++) {
1127                     //get the bit and assign to the data buffer of the raster
1128                     int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F;
1129                     bdata[k + destPos[i]] |= v << destOff[i];
1130                 }
1131 
1132                 k += isBottomUp ? -lineStride : lineStride;
1133                 iis.skipBytes(skipLength);
1134                 processImageUpdate(bi, 0, j,
1135                                    destinationRegion.width, 1, 1, 1,
1136                                    new int[]{0});
1137                 processImageProgress(100.0F*j/destinationRegion.height);
1138             }
1139         }
1140     }
1141 
1142     // Method to read 8 bit BMP image data
1143     private void read8Bit(byte[] bdata) throws IOException {
1144 
1145         // Padding bytes at the end of each scanline
1146         int padding = width % 4;
1147         if (padding != 0) {
1148             padding = 4 - padding;
1149         }
1150 
1151         int lineLength = width + padding;
1152 
1153         if (noTransform) {
1154             int j = isBottomUp ? (height -1) * width : 0;
1155 
1156             for (int i=0; i<height; i++) {
1157                 if (abortRequested()) {
1158                     break;
1159                 }
1160                 iis.readFully(bdata, j, width);
1161                 iis.skipBytes(padding);
1162                 j += isBottomUp ? -width : width;
1163                 processImageUpdate(bi, 0, i,
1164                                    destinationRegion.width, 1, 1, 1,
1165                                    new int[]{0});
1166                 processImageProgress(100.0F * i/destinationRegion.height);
1167             }
1168         } else {
1169             byte[] buf = new byte[lineLength];
1170             int lineStride =
1171                 ((ComponentSampleModel)sampleModel).getScanlineStride();
1172 
1173             if (isBottomUp) {
1174                 int lastLine =
1175                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1176                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1177             } else
1178                 iis.skipBytes(lineLength * sourceRegion.y);
1179 
1180             int skipLength = lineLength * (scaleY - 1);
1181 
1182             int k = destinationRegion.y * lineStride;
1183             if (isBottomUp)
1184                 k += (destinationRegion.height - 1) * lineStride;
1185             k += destinationRegion.x;
1186 
1187             for (int j = 0, y = sourceRegion.y;
1188                  j < destinationRegion.height; j++, y+=scaleY) {
1189 
1190                 if (abortRequested())
1191                     break;
1192                 iis.read(buf, 0, lineLength);
1193                 for (int i = 0, m = sourceRegion.x;
1194                      i < destinationRegion.width; i++, m += scaleX) {
1195                     //get the bit and assign to the data buffer of the raster
1196                     bdata[k + i] = buf[m];
1197                 }
1198 
1199                 k += isBottomUp ? -lineStride : lineStride;
1200                 iis.skipBytes(skipLength);
1201                 processImageUpdate(bi, 0, j,
1202                                    destinationRegion.width, 1, 1, 1,
1203                                    new int[]{0});
1204                 processImageProgress(100.0F*j/destinationRegion.height);
1205             }
1206         }
1207     }
1208 
1209     // Method to read 24 bit BMP image data
1210     private void read24Bit(byte[] bdata) throws IOException {
1211         // Padding bytes at the end of each scanline
1212         // width * bitsPerPixel should be divisible by 32
1213         int padding = width * 3 % 4;
1214         if ( padding != 0)
1215             padding = 4 - padding;
1216 
1217         int lineStride = width * 3;
1218         int lineLength = lineStride + padding;
1219 
1220         if (noTransform) {
1221             int j = isBottomUp ? (height -1) * width * 3 : 0;
1222 
1223             for (int i=0; i<height; i++) {
1224                 if (abortRequested()) {
1225                     break;
1226                 }
1227                 iis.readFully(bdata, j, lineStride);
1228                 iis.skipBytes(padding);
1229                 j += isBottomUp ? -lineStride : lineStride;
1230                 processImageUpdate(bi, 0, i,
1231                                    destinationRegion.width, 1, 1, 1,
1232                                    new int[]{0});
1233                 processImageProgress(100.0F * i/destinationRegion.height);
1234             }
1235         } else {
1236             byte[] buf = new byte[lineLength];
1237             lineStride =
1238                 ((ComponentSampleModel)sampleModel).getScanlineStride();
1239 
1240             if (isBottomUp) {
1241                 int lastLine =
1242                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1243                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1244             } else
1245                 iis.skipBytes(lineLength * sourceRegion.y);
1246 
1247             int skipLength = lineLength * (scaleY - 1);
1248 
1249             int k = destinationRegion.y * lineStride;
1250             if (isBottomUp)
1251                 k += (destinationRegion.height - 1) * lineStride;
1252             k += destinationRegion.x * 3;
1253 
1254             for (int j = 0, y = sourceRegion.y;
1255                  j < destinationRegion.height; j++, y+=scaleY) {
1256 
1257                 if (abortRequested())
1258                     break;
1259                 iis.read(buf, 0, lineLength);
1260                 for (int i = 0, m = 3 * sourceRegion.x;
1261                      i < destinationRegion.width; i++, m += 3 * scaleX) {
1262                     //get the bit and assign to the data buffer of the raster
1263                     int n = 3 * i + k;
1264                     for (int b = 0; b < destBands.length; b++)
1265                         bdata[n + destBands[b]] = buf[m + sourceBands[b]];
1266                 }
1267 
1268                 k += isBottomUp ? -lineStride : lineStride;
1269                 iis.skipBytes(skipLength);
1270                 processImageUpdate(bi, 0, j,
1271                                    destinationRegion.width, 1, 1, 1,
1272                                    new int[]{0});
1273                 processImageProgress(100.0F*j/destinationRegion.height);
1274             }
1275         }
1276     }
1277 
1278     private void read16Bit(short sdata[]) throws IOException {
1279         // Padding bytes at the end of each scanline
1280         // width * bitsPerPixel should be divisible by 32
1281         int padding = width * 2 % 4;
1282 
1283         if ( padding != 0)
1284             padding = 4 - padding;
1285 
1286         int lineLength = width + padding / 2;
1287 
1288         if (noTransform) {
1289             int j = isBottomUp ? (height -1) * width : 0;
1290             for (int i=0; i<height; i++) {
1291                 if (abortRequested()) {
1292                     break;
1293                 }
1294 
1295                 iis.readFully(sdata, j, width);
1296                 iis.skipBytes(padding);
1297 
1298                 j += isBottomUp ? -width : width;
1299                 processImageUpdate(bi, 0, i,
1300                                    destinationRegion.width, 1, 1, 1,
1301                                    new int[]{0});
1302                 processImageProgress(100.0F * i/destinationRegion.height);
1303             }
1304         } else {
1305             short[] buf = new short[lineLength];
1306             int lineStride =
1307                 ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
1308 
1309             if (isBottomUp) {
1310                 int lastLine =
1311                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1312                 iis.skipBytes(lineLength * (height - 1 - lastLine) << 1);
1313             } else
1314                 iis.skipBytes(lineLength * sourceRegion.y << 1);
1315 
1316             int skipLength = lineLength * (scaleY - 1) << 1;
1317 
1318             int k = destinationRegion.y * lineStride;
1319             if (isBottomUp)
1320                 k += (destinationRegion.height - 1) * lineStride;
1321             k += destinationRegion.x;
1322 
1323             for (int j = 0, y = sourceRegion.y;
1324                  j < destinationRegion.height; j++, y+=scaleY) {
1325 
1326                 if (abortRequested())
1327                     break;
1328                 iis.readFully(buf, 0, lineLength);
1329                 for (int i = 0, m = sourceRegion.x;
1330                      i < destinationRegion.width; i++, m += scaleX) {
1331                     //get the bit and assign to the data buffer of the raster
1332                     sdata[k + i] = buf[m];
1333                 }
1334 
1335                 k += isBottomUp ? -lineStride : lineStride;
1336                 iis.skipBytes(skipLength);
1337                 processImageUpdate(bi, 0, j,
1338                                    destinationRegion.width, 1, 1, 1,
1339                                    new int[]{0});
1340                 processImageProgress(100.0F*j/destinationRegion.height);
1341             }
1342         }
1343     }
1344 
1345     private void read32Bit(int idata[]) throws IOException {
1346         if (noTransform) {
1347             int j = isBottomUp ? (height -1) * width : 0;
1348 
1349             for (int i=0; i<height; i++) {
1350                 if (abortRequested()) {
1351                     break;
1352                 }
1353                 iis.readFully(idata, j, width);
1354                 j += isBottomUp ? -width : width;
1355                 processImageUpdate(bi, 0, i,
1356                                    destinationRegion.width, 1, 1, 1,
1357                                    new int[]{0});
1358                 processImageProgress(100.0F * i/destinationRegion.height);
1359             }
1360         } else {
1361             int[] buf = new int[width];
1362             int lineStride =
1363                 ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
1364 
1365             if (isBottomUp) {
1366                 int lastLine =
1367                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1368                 iis.skipBytes(width * (height - 1 - lastLine) << 2);
1369             } else
1370                 iis.skipBytes(width * sourceRegion.y << 2);
1371 
1372             int skipLength = width * (scaleY - 1) << 2;
1373 
1374             int k = destinationRegion.y * lineStride;
1375             if (isBottomUp)
1376                 k += (destinationRegion.height - 1) * lineStride;
1377             k += destinationRegion.x;
1378 
1379             for (int j = 0, y = sourceRegion.y;
1380                  j < destinationRegion.height; j++, y+=scaleY) {
1381 
1382                 if (abortRequested())
1383                     break;
1384                 iis.readFully(buf, 0, width);
1385                 for (int i = 0, m = sourceRegion.x;
1386                      i < destinationRegion.width; i++, m += scaleX) {
1387                     //get the bit and assign to the data buffer of the raster
1388                     idata[k + i] = buf[m];
1389                 }
1390 
1391                 k += isBottomUp ? -lineStride : lineStride;
1392                 iis.skipBytes(skipLength);
1393                 processImageUpdate(bi, 0, j,
1394                                    destinationRegion.width, 1, 1, 1,
1395                                    new int[]{0});
1396                 processImageProgress(100.0F*j/destinationRegion.height);
1397             }
1398         }
1399     }
1400 
1401     private void readRLE8(byte bdata[]) throws IOException {
1402         // If imageSize field is not provided, calculate it.
1403         int imSize = (int)imageSize;
1404         if (imSize == 0) {
1405             imSize = (int)(bitmapFileSize - bitmapOffset);
1406         }
1407 
1408         int padding = 0;
1409         // If width is not 32 bit aligned, then while uncompressing each
1410         // scanline will have padding bytes, calculate the amount of padding
1411         int remainder = width % 4;
1412         if (remainder != 0) {
1413             padding = 4 - remainder;
1414         }
1415 
1416         // Read till we have the whole image
1417         byte values[] = new byte[imSize];
1418         int bytesRead = 0;
1419         iis.readFully(values, 0, imSize);
1420 
1421         // Since data is compressed, decompress it
1422         decodeRLE8(imSize, padding, values, bdata);
1423     }
1424 
1425     private void decodeRLE8(int imSize,
1426                             int padding,
1427                             byte[] values,
1428                             byte[] bdata) throws IOException {
1429 
1430         byte val[] = new byte[width * height];
1431         int count = 0, l = 0;
1432         int value;
1433         boolean flag = false;
1434         int lineNo = isBottomUp ? height - 1 : 0;
1435         int lineStride =
1436             ((ComponentSampleModel)sampleModel).getScanlineStride();
1437         int finished = 0;
1438 
1439         while (count != imSize) {
1440             value = values[count++] & 0xff;
1441             if (value == 0) {
1442                 switch(values[count++] & 0xff) {
1443 
1444                 case 0:
1445                     // End-of-scanline marker
1446                     if (lineNo >= sourceRegion.y &&
1447                         lineNo < sourceRegion.y + sourceRegion.height) {
1448                         if (noTransform) {
1449                             int pos = lineNo * width;
1450                             for(int i = 0; i < width; i++)
1451                                 bdata[pos++] = val[i];
1452                             processImageUpdate(bi, 0, lineNo,
1453                                                destinationRegion.width, 1, 1, 1,
1454                                                new int[]{0});
1455                             finished++;
1456                         } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1457                             int currentLine = (lineNo - sourceRegion.y) / scaleY +
1458                                 destinationRegion.y;
1459                             int pos = currentLine * lineStride;
1460                             pos += destinationRegion.x;
1461                             for (int i = sourceRegion.x;
1462                                  i < sourceRegion.x + sourceRegion.width;
1463                                  i += scaleX)
1464                                 bdata[pos++] = val[i];
1465                             processImageUpdate(bi, 0, currentLine,
1466                                                destinationRegion.width, 1, 1, 1,
1467                                                new int[]{0});
1468                             finished++;
1469                         }
1470                     }
1471                     processImageProgress(100.0F * finished / destinationRegion.height);
1472                     lineNo += isBottomUp ? -1 : 1;
1473                     l = 0;
1474 
1475                     if (abortRequested()) {
1476                         flag = true;
1477                     }
1478 
1479                     break;
1480 
1481                 case 1:
1482                     // End-of-RLE marker
1483                     flag = true;
1484                     break;
1485 
1486                 case 2:
1487                     // delta or vector marker
1488                     int xoff = values[count++] & 0xff;
1489                     int yoff = values[count] & 0xff;
1490                     // Move to the position xoff, yoff down
1491                     l += xoff + yoff*width;
1492                     break;
1493 
1494                 default:
1495                     int end = values[count-1] & 0xff;
1496                     for (int i=0; i<end; i++) {
1497                         val[l++] = (byte)(values[count++] & 0xff);
1498                     }
1499 
1500                     // Whenever end pixels can fit into odd number of bytes,
1501                     // an extra padding byte will be present, so skip that.
1502                     if ((end & 1) == 1) {
1503                         count++;
1504                     }
1505                 }
1506             } else {
1507                 for (int i=0; i<value; i++) {
1508                     val[l++] = (byte)(values[count] & 0xff);
1509                 }
1510 
1511                 count++;
1512             }
1513 
1514             // If End-of-RLE data, then exit the while loop
1515             if (flag) {
1516                 break;
1517             }
1518         }
1519     }
1520 
1521     private void readRLE4(byte[] bdata) throws IOException {
1522 
1523         // If imageSize field is not specified, calculate it.
1524         int imSize = (int)imageSize;
1525         if (imSize == 0) {
1526             imSize = (int)(bitmapFileSize - bitmapOffset);
1527         }
1528 
1529         int padding = 0;
1530         // If width is not 32 byte aligned, then while uncompressing each
1531         // scanline will have padding bytes, calculate the amount of padding
1532         int remainder = width % 4;
1533         if (remainder != 0) {
1534             padding = 4 - remainder;
1535         }
1536 
1537         // Read till we have the whole image
1538         byte[] values = new byte[imSize];
1539         iis.readFully(values, 0, imSize);
1540 
1541         // Decompress the RLE4 compressed data.
1542         decodeRLE4(imSize, padding, values, bdata);
1543     }
1544 
1545     private void decodeRLE4(int imSize,
1546                             int padding,
1547                             byte[] values,
1548                             byte[] bdata) throws IOException {
1549         byte[] val = new byte[width];
1550         int count = 0, l = 0;
1551         int value;
1552         boolean flag = false;
1553         int lineNo = isBottomUp ? height - 1 : 0;
1554         int lineStride =
1555             ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1556         int finished = 0;
1557 
1558         while (count != imSize) {
1559 
1560             value = values[count++] & 0xFF;
1561             if (value == 0) {
1562 
1563 
1564                 // Absolute mode
1565                 switch(values[count++] & 0xFF) {
1566 
1567                 case 0:
1568                     // End-of-scanline marker
1569                     // End-of-scanline marker
1570                     if (lineNo >= sourceRegion.y &&
1571                         lineNo < sourceRegion.y + sourceRegion.height) {
1572                         if (noTransform) {
1573                             int pos = lineNo * (width + 1 >> 1);
1574                             for(int i = 0, j = 0; i < width >> 1; i++)
1575                                 bdata[pos++] =
1576                                     (byte)((val[j++] << 4) | val[j++]);
1577                             if ((width & 1) == 1)
1578                                 bdata[pos] |= val[width - 1] << 4;
1579 
1580                             processImageUpdate(bi, 0, lineNo,
1581                                                destinationRegion.width, 1, 1, 1,
1582                                                new int[]{0});
1583                             finished++;
1584                         } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1585                             int currentLine = (lineNo - sourceRegion.y) / scaleY +
1586                                 destinationRegion.y;
1587                             int pos = currentLine * lineStride;
1588                             pos += destinationRegion.x >> 1;
1589                             int shift = (1 - (destinationRegion.x & 1)) << 2;
1590                             for (int i = sourceRegion.x;
1591                                  i < sourceRegion.x + sourceRegion.width;
1592                                  i += scaleX) {
1593                                 bdata[pos] |= val[i] << shift;
1594                                 shift += 4;
1595                                 if (shift == 4) {
1596                                     pos++;
1597                                 }
1598                                 shift &= 7;
1599                             }
1600                             processImageUpdate(bi, 0, currentLine,
1601                                                destinationRegion.width, 1, 1, 1,
1602                                                new int[]{0});
1603                             finished++;
1604                         }
1605                     }
1606                     processImageProgress(100.0F * finished / destinationRegion.height);
1607                     lineNo += isBottomUp ? -1 : 1;
1608                     l = 0;
1609 
1610                     if (abortRequested()) {
1611                         flag = true;
1612                     }
1613 
1614                     break;
1615 
1616                 case 1:
1617                     // End-of-RLE marker
1618                     flag = true;
1619                     break;
1620 
1621                 case 2:
1622                     // delta or vector marker
1623                     int xoff = values[count++] & 0xFF;
1624                     int yoff = values[count] & 0xFF;
1625                     // Move to the position xoff, yoff down
1626                     l += xoff + yoff*width;
1627                     break;
1628 
1629                 default:
1630                     int end = values[count-1] & 0xFF;
1631                     for (int i=0; i<end; i++) {
1632                         val[l++] = (byte)(((i & 1) == 0) ? (values[count] & 0xf0) >> 4
1633                                           : (values[count++] & 0x0f));
1634                     }
1635 
1636                     // When end is odd, the above for loop does not
1637                     // increment count, so do it now.
1638                     if ((end & 1) == 1) {
1639                         count++;
1640                     }
1641 
1642                     // Whenever end pixels can fit into odd number of bytes,
1643                     // an extra padding byte will be present, so skip that.
1644                     if ((((int)Math.ceil(end/2)) & 1) ==1 ) {
1645                         count++;
1646                     }
1647                     break;
1648                 }
1649             } else {
1650                 // Encoded mode
1651                 int alternate[] = { (values[count] & 0xf0) >> 4,
1652                                     values[count] & 0x0f };
1653                 for (int i=0; (i < value) && (l < width); i++) {
1654                     val[l++] = (byte)alternate[i & 1];
1655                 }
1656 
1657                 count++;
1658             }
1659 
1660             // If End-of-RLE data, then exit the while loop
1661             if (flag) {
1662                 break;
1663             }
1664         }
1665     }
1666 
1667     /** Decodes the jpeg/png image embedded in the bitmap using any jpeg
1668      *  ImageIO-style plugin.
1669      *
1670      * @param bi The destination <code>BufferedImage</code>.
1671      * @param bmpParam The <code>ImageReadParam</code> for decoding this
1672      *          BMP image.  The parameters for subregion, band selection and
1673      *          subsampling are used in decoding the jpeg image.
1674      */
1675 
1676     private BufferedImage readEmbedded(int type,
1677                               BufferedImage bi, ImageReadParam bmpParam)
1678       throws IOException {
1679         String format;
1680         switch(type) {
1681           case BI_JPEG:
1682               format = "JPEG";
1683               break;
1684           case BI_PNG:
1685               format = "PNG";
1686               break;
1687           default:
1688               throw new
1689                   IOException("Unexpected compression type: " + type);
1690         }
1691         ImageReader reader =
1692             ImageIO.getImageReadersByFormatName(format).next();
1693         if (reader == null) {
1694             throw new RuntimeException(I18N.getString("BMPImageReader4") +
1695                                        " " + format);
1696         }
1697         // prepare input
1698         byte[] buff = new byte[(int)imageSize];
1699         iis.read(buff);
1700         reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(buff)));
1701         if (bi == null) {
1702             ImageTypeSpecifier embType = reader.getImageTypes(0).next();
1703             bi = embType.createBufferedImage(destinationRegion.x +
1704                                              destinationRegion.width,
1705                                              destinationRegion.y +
1706                                              destinationRegion.height);
1707         }
1708 
1709         reader.addIIOReadProgressListener(new EmbeddedProgressAdapter() {
1710                 public void imageProgress(ImageReader source,
1711                                           float percentageDone)
1712                 {
1713                     processImageProgress(percentageDone);
1714                 }
1715             });
1716 
1717         reader.addIIOReadUpdateListener(new IIOReadUpdateListener() {
1718                 public void imageUpdate(ImageReader source,
1719                                         BufferedImage theImage,
1720                                         int minX, int minY,
1721                                         int width, int height,
1722                                         int periodX, int periodY,
1723                                         int[] bands)
1724                 {
1725                     processImageUpdate(theImage, minX, minY,
1726                                        width, height,
1727                                        periodX, periodY, bands);
1728                 }
1729                 public void passComplete(ImageReader source,
1730                                          BufferedImage theImage)
1731                 {
1732                     processPassComplete(theImage);
1733                 }
1734                 public void passStarted(ImageReader source,
1735                                         BufferedImage theImage,
1736                                         int pass,
1737                                         int minPass, int maxPass,
1738                                         int minX, int minY,
1739                                         int periodX, int periodY,
1740                                         int[] bands)
1741                 {
1742                     processPassStarted(theImage, pass, minPass, maxPass,
1743                                        minX, minY, periodX, periodY,
1744                                        bands);
1745                 }
1746                 public void thumbnailPassComplete(ImageReader source,
1747                                                   BufferedImage thumb) {}
1748                 public void thumbnailPassStarted(ImageReader source,
1749                                                  BufferedImage thumb,
1750                                                  int pass,
1751                                                  int minPass, int maxPass,
1752                                                  int minX, int minY,
1753                                                  int periodX, int periodY,
1754                                                  int[] bands) {}
1755                 public void thumbnailUpdate(ImageReader source,
1756                                             BufferedImage theThumbnail,
1757                                             int minX, int minY,
1758                                             int width, int height,
1759                                             int periodX, int periodY,
1760                                             int[] bands) {}
1761             });
1762 
1763         reader.addIIOReadWarningListener(new IIOReadWarningListener() {
1764                 public void warningOccurred(ImageReader source, String warning)
1765                 {
1766                     processWarningOccurred(warning);
1767                 }
1768             });
1769 
1770         ImageReadParam param = reader.getDefaultReadParam();
1771         param.setDestination(bi);
1772         param.setDestinationBands(bmpParam.getDestinationBands());
1773         param.setDestinationOffset(bmpParam.getDestinationOffset());
1774         param.setSourceBands(bmpParam.getSourceBands());
1775         param.setSourceRegion(bmpParam.getSourceRegion());
1776         param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
1777                                    bmpParam.getSourceYSubsampling(),
1778                                    bmpParam.getSubsamplingXOffset(),
1779                                    bmpParam.getSubsamplingYOffset());
1780         reader.read(0, param);
1781         return bi;
1782     }
1783 
1784     private class EmbeddedProgressAdapter implements IIOReadProgressListener {
1785         public void imageComplete(ImageReader src) {}
1786         public void imageProgress(ImageReader src, float percentageDone) {}
1787         public void imageStarted(ImageReader src, int imageIndex) {}
1788         public void thumbnailComplete(ImageReader src) {}
1789         public void thumbnailProgress(ImageReader src, float percentageDone) {}
1790         public void thumbnailStarted(ImageReader src, int iIdx, int tIdx) {}
1791         public void sequenceComplete(ImageReader src) {}
1792         public void sequenceStarted(ImageReader src, int minIndex) {}
1793         public void readAborted(ImageReader src) {}
1794     }
1795 
1796     private static Boolean isLinkedProfileDisabled = null;
1797 
1798     private static boolean isLinkedProfileAllowed() {
1799         if (isLinkedProfileDisabled == null) {
1800             PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
1801                 public Boolean run() {
1802                     return Boolean.getBoolean("sun.imageio.plugins.bmp.disableLinkedProfiles");
1803                 }
1804             };
1805             isLinkedProfileDisabled = AccessController.doPrivileged(a);
1806         }
1807         return !isLinkedProfileDisabled;
1808     }
1809 
1810     private static Boolean isWindowsPlatform = null;
1811 
1812     /**
1813      * Verifies whether the byte array contans a unc path.
1814      * Non-UNC path examples:
1815      *  c:\path\to\file  - simple notation
1816      *  \\?\c:\path\to\file - long notation
1817      *
1818      * UNC path examples:
1819      *  \\server\share - a UNC path in simple notation
1820      *  \\?\UNC\server\share - a UNC path in long notation
1821      *  \\.\some\device - a path to device.
1822      */
1823     private static boolean isUncOrDevicePath(byte[] p) {
1824         if (isWindowsPlatform == null) {
1825             PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
1826                 public Boolean run() {
1827                     String osname = System.getProperty("os.name");
1828                     return (osname != null &&
1829                             osname.toLowerCase().startsWith("win"));
1830                 }
1831             };
1832             isWindowsPlatform = AccessController.doPrivileged(a);
1833         }
1834 
1835         if (!isWindowsPlatform) {
1836             /* no need for the check on platforms except windows */
1837             return false;
1838         }
1839 
1840         /* normalize prefix of the path */
1841         if (p[0] == '/') p[0] = '\\';
1842         if (p[1] == '/') p[1] = '\\';
1843         if (p[3] == '/') p[3] = '\\';
1844 
1845 
1846         if ((p[0] == '\\') && (p[1] == '\\')) {
1847             if ((p[2] == '?') && (p[3] == '\\')) {
1848                 // long path: whether unc or local
1849                 return ((p[4] == 'U' || p[4] == 'u') &&
1850                         (p[5] == 'N' || p[5] == 'n') &&
1851                         (p[6] == 'C' || p[6] == 'c'));
1852             } else {
1853                 // device path or short unc notation
1854                 return true;
1855             }
1856         } else {
1857             return false;
1858         }
1859     }
1860 }