1 /*
   2  * Copyright (c) 2003, 2014, 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<ImageTypeSpecifier> 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<ImageTypeSpecifier> 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 = ((DataBufferByte)raster.getDataBuffer()).getData();
 810             else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
 811                 sdata = ((DataBufferUShort)raster.getDataBuffer()).getData();
 812             else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
 813                 idata = ((DataBufferInt)raster.getDataBuffer()).getData();
 814         }
 815 
 816         // There should only be one tile.
 817         switch(imageType) {
 818 
 819         case VERSION_2_1_BIT:
 820             // no compression
 821             read1Bit(bdata);
 822             break;
 823 
 824         case VERSION_2_4_BIT:
 825             // no compression
 826             read4Bit(bdata);
 827             break;
 828 
 829         case VERSION_2_8_BIT:
 830             // no compression
 831             read8Bit(bdata);
 832             break;
 833 
 834         case VERSION_2_24_BIT:
 835             // no compression
 836             read24Bit(bdata);
 837             break;
 838 
 839         case VERSION_3_1_BIT:
 840             // 1-bit images cannot be compressed.
 841             read1Bit(bdata);
 842             break;
 843 
 844         case VERSION_3_4_BIT:
 845             switch((int)compression) {
 846             case BI_RGB:
 847                 read4Bit(bdata);
 848                 break;
 849 
 850             case BI_RLE4:
 851                 readRLE4(bdata);
 852                 break;
 853 
 854             default:
 855                 throw new
 856                     IIOException(I18N.getString("BMPImageReader1"));
 857             }
 858             break;
 859 
 860         case VERSION_3_8_BIT:
 861             switch((int)compression) {
 862             case BI_RGB:
 863                 read8Bit(bdata);
 864                 break;
 865 
 866             case BI_RLE8:
 867                 readRLE8(bdata);
 868                 break;
 869 
 870             default:
 871                 throw new
 872                     IIOException(I18N.getString("BMPImageReader1"));
 873             }
 874 
 875             break;
 876 
 877         case VERSION_3_24_BIT:
 878             // 24-bit images are not compressed
 879             read24Bit(bdata);
 880             break;
 881 
 882         case VERSION_3_NT_16_BIT:
 883             read16Bit(sdata);
 884             break;
 885 
 886         case VERSION_3_NT_32_BIT:
 887             read32Bit(idata);
 888             break;
 889 
 890         case VERSION_3_XP_EMBEDDED:
 891         case VERSION_4_XP_EMBEDDED:
 892         case VERSION_5_XP_EMBEDDED:
 893             bi = readEmbedded((int)compression, bi, param);
 894             break;
 895 
 896         case VERSION_4_1_BIT:
 897             read1Bit(bdata);
 898             break;
 899 
 900         case VERSION_4_4_BIT:
 901             switch((int)compression) {
 902 
 903             case BI_RGB:
 904                 read4Bit(bdata);
 905                 break;
 906 
 907             case BI_RLE4:
 908                 readRLE4(bdata);
 909                 break;
 910 
 911             default:
 912                 throw new
 913                     IIOException(I18N.getString("BMPImageReader1"));
 914             }
 915             break;
 916 
 917         case VERSION_4_8_BIT:
 918             switch((int)compression) {
 919 
 920             case BI_RGB:
 921                 read8Bit(bdata);
 922                 break;
 923 
 924             case BI_RLE8:
 925                 readRLE8(bdata);
 926                 break;
 927 
 928             default:
 929                 throw new
 930                     IIOException(I18N.getString("BMPImageReader1"));
 931             }
 932             break;
 933 
 934         case VERSION_4_16_BIT:
 935             read16Bit(sdata);
 936             break;
 937 
 938         case VERSION_4_24_BIT:
 939             read24Bit(bdata);
 940             break;
 941 
 942         case VERSION_4_32_BIT:
 943             read32Bit(idata);
 944             break;
 945         }
 946 
 947         if (abortRequested())
 948             processReadAborted();
 949         else
 950             processImageComplete();
 951 
 952         return bi;
 953     }
 954 
 955     public boolean canReadRaster() {
 956         return true;
 957     }
 958 
 959     public Raster readRaster(int imageIndex,
 960                              ImageReadParam param) throws IOException {
 961         BufferedImage bi = read(imageIndex, param);
 962         return bi.getData();
 963     }
 964 
 965     private void resetHeaderInfo() {
 966         gotHeader = false;
 967         bi = null;
 968         sampleModel = originalSampleModel = null;
 969         colorModel = originalColorModel = null;
 970     }
 971 
 972     public void reset() {
 973         super.reset();
 974         iis = null;
 975         resetHeaderInfo();
 976     }
 977 
 978     // Deal with 1 Bit images using IndexColorModels
 979     private void read1Bit(byte[] bdata) throws IOException {
 980         int bytesPerScanline = (width + 7) / 8;
 981         int padding = bytesPerScanline % 4;
 982         if (padding != 0) {
 983             padding = 4 - padding;
 984         }
 985 
 986         int lineLength = bytesPerScanline + padding;
 987 
 988         if (noTransform) {
 989             int j = isBottomUp ? (height -1)*bytesPerScanline : 0;
 990 
 991             for (int i=0; i<height; i++) {
 992                 if (abortRequested()) {
 993                     break;
 994                 }
 995                 iis.readFully(bdata, j, bytesPerScanline);
 996                 iis.skipBytes(padding);
 997                 j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
 998                 processImageUpdate(bi, 0, i,
 999                                    destinationRegion.width, 1, 1, 1,
1000                                    new int[]{0});
1001                 processImageProgress(100.0F * i/destinationRegion.height);
1002             }
1003         } else {
1004             byte[] buf = new byte[lineLength];
1005             int lineStride =
1006                 ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1007 
1008             if (isBottomUp) {
1009                 int lastLine =
1010                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1011                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1012             } else
1013                 iis.skipBytes(lineLength * sourceRegion.y);
1014 
1015             int skipLength = lineLength * (scaleY - 1);
1016 
1017             // cache the values to avoid duplicated computation
1018             int[] srcOff = new int[destinationRegion.width];
1019             int[] destOff = new int[destinationRegion.width];
1020             int[] srcPos = new int[destinationRegion.width];
1021             int[] destPos = new int[destinationRegion.width];
1022 
1023             for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
1024                  i < destinationRegion.x + destinationRegion.width;
1025                  i++, j++, x += scaleX) {
1026                 srcPos[j] = x >> 3;
1027                 srcOff[j] = 7 - (x & 7);
1028                 destPos[j] = i >> 3;
1029                 destOff[j] = 7 - (i & 7);
1030             }
1031 
1032             int k = destinationRegion.y * lineStride;
1033             if (isBottomUp)
1034                 k += (destinationRegion.height - 1) * lineStride;
1035 
1036             for (int j = 0, y = sourceRegion.y;
1037                  j < destinationRegion.height; j++, y+=scaleY) {
1038 
1039                 if (abortRequested())
1040                     break;
1041                 iis.read(buf, 0, lineLength);
1042                 for (int i = 0; i < destinationRegion.width; i++) {
1043                     //get the bit and assign to the data buffer of the raster
1044                     int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
1045                     bdata[k + destPos[i]] |= v << destOff[i];
1046                 }
1047 
1048                 k += isBottomUp ? -lineStride : lineStride;
1049                 iis.skipBytes(skipLength);
1050                 processImageUpdate(bi, 0, j,
1051                                    destinationRegion.width, 1, 1, 1,
1052                                    new int[]{0});
1053                 processImageProgress(100.0F*j/destinationRegion.height);
1054             }
1055         }
1056     }
1057 
1058     // Method to read a 4 bit BMP image data
1059     private void read4Bit(byte[] bdata) throws IOException {
1060 
1061         int bytesPerScanline = (width + 1) / 2;
1062 
1063         // Padding bytes at the end of each scanline
1064         int padding = bytesPerScanline % 4;
1065         if (padding != 0)
1066             padding = 4 - padding;
1067 
1068         int lineLength = bytesPerScanline + padding;
1069 
1070         if (noTransform) {
1071             int j = isBottomUp ? (height -1) * bytesPerScanline : 0;
1072 
1073             for (int i=0; i<height; i++) {
1074                 if (abortRequested()) {
1075                     break;
1076                 }
1077                 iis.readFully(bdata, j, bytesPerScanline);
1078                 iis.skipBytes(padding);
1079                 j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
1080                 processImageUpdate(bi, 0, i,
1081                                    destinationRegion.width, 1, 1, 1,
1082                                    new int[]{0});
1083                 processImageProgress(100.0F * i/destinationRegion.height);
1084             }
1085         } else {
1086             byte[] buf = new byte[lineLength];
1087             int lineStride =
1088                 ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1089 
1090             if (isBottomUp) {
1091                 int lastLine =
1092                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1093                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1094             } else
1095                 iis.skipBytes(lineLength * sourceRegion.y);
1096 
1097             int skipLength = lineLength * (scaleY - 1);
1098 
1099             // cache the values to avoid duplicated computation
1100             int[] srcOff = new int[destinationRegion.width];
1101             int[] destOff = new int[destinationRegion.width];
1102             int[] srcPos = new int[destinationRegion.width];
1103             int[] destPos = new int[destinationRegion.width];
1104 
1105             for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
1106                  i < destinationRegion.x + destinationRegion.width;
1107                  i++, j++, x += scaleX) {
1108                 srcPos[j] = x >> 1;
1109                 srcOff[j] = (1 - (x & 1)) << 2;
1110                 destPos[j] = i >> 1;
1111                 destOff[j] = (1 - (i & 1)) << 2;
1112             }
1113 
1114             int k = destinationRegion.y * lineStride;
1115             if (isBottomUp)
1116                 k += (destinationRegion.height - 1) * lineStride;
1117 
1118             for (int j = 0, y = sourceRegion.y;
1119                  j < destinationRegion.height; j++, y+=scaleY) {
1120 
1121                 if (abortRequested())
1122                     break;
1123                 iis.read(buf, 0, lineLength);
1124                 for (int i = 0; i < destinationRegion.width; i++) {
1125                     //get the bit and assign to the data buffer of the raster
1126                     int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F;
1127                     bdata[k + destPos[i]] |= v << destOff[i];
1128                 }
1129 
1130                 k += isBottomUp ? -lineStride : lineStride;
1131                 iis.skipBytes(skipLength);
1132                 processImageUpdate(bi, 0, j,
1133                                    destinationRegion.width, 1, 1, 1,
1134                                    new int[]{0});
1135                 processImageProgress(100.0F*j/destinationRegion.height);
1136             }
1137         }
1138     }
1139 
1140     // Method to read 8 bit BMP image data
1141     private void read8Bit(byte[] bdata) throws IOException {
1142 
1143         // Padding bytes at the end of each scanline
1144         int padding = width % 4;
1145         if (padding != 0) {
1146             padding = 4 - padding;
1147         }
1148 
1149         int lineLength = width + padding;
1150 
1151         if (noTransform) {
1152             int j = isBottomUp ? (height -1) * width : 0;
1153 
1154             for (int i=0; i<height; i++) {
1155                 if (abortRequested()) {
1156                     break;
1157                 }
1158                 iis.readFully(bdata, j, width);
1159                 iis.skipBytes(padding);
1160                 j += isBottomUp ? -width : width;
1161                 processImageUpdate(bi, 0, i,
1162                                    destinationRegion.width, 1, 1, 1,
1163                                    new int[]{0});
1164                 processImageProgress(100.0F * i/destinationRegion.height);
1165             }
1166         } else {
1167             byte[] buf = new byte[lineLength];
1168             int lineStride =
1169                 ((ComponentSampleModel)sampleModel).getScanlineStride();
1170 
1171             if (isBottomUp) {
1172                 int lastLine =
1173                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1174                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1175             } else
1176                 iis.skipBytes(lineLength * sourceRegion.y);
1177 
1178             int skipLength = lineLength * (scaleY - 1);
1179 
1180             int k = destinationRegion.y * lineStride;
1181             if (isBottomUp)
1182                 k += (destinationRegion.height - 1) * lineStride;
1183             k += destinationRegion.x;
1184 
1185             for (int j = 0, y = sourceRegion.y;
1186                  j < destinationRegion.height; j++, y+=scaleY) {
1187 
1188                 if (abortRequested())
1189                     break;
1190                 iis.read(buf, 0, lineLength);
1191                 for (int i = 0, m = sourceRegion.x;
1192                      i < destinationRegion.width; i++, m += scaleX) {
1193                     //get the bit and assign to the data buffer of the raster
1194                     bdata[k + i] = buf[m];
1195                 }
1196 
1197                 k += isBottomUp ? -lineStride : lineStride;
1198                 iis.skipBytes(skipLength);
1199                 processImageUpdate(bi, 0, j,
1200                                    destinationRegion.width, 1, 1, 1,
1201                                    new int[]{0});
1202                 processImageProgress(100.0F*j/destinationRegion.height);
1203             }
1204         }
1205     }
1206 
1207     // Method to read 24 bit BMP image data
1208     private void read24Bit(byte[] bdata) throws IOException {
1209         // Padding bytes at the end of each scanline
1210         // width * bitsPerPixel should be divisible by 32
1211         int padding = width * 3 % 4;
1212         if ( padding != 0)
1213             padding = 4 - padding;
1214 
1215         int lineStride = width * 3;
1216         int lineLength = lineStride + padding;
1217 
1218         if (noTransform) {
1219             int j = isBottomUp ? (height -1) * width * 3 : 0;
1220 
1221             for (int i=0; i<height; i++) {
1222                 if (abortRequested()) {
1223                     break;
1224                 }
1225                 iis.readFully(bdata, j, lineStride);
1226                 iis.skipBytes(padding);
1227                 j += isBottomUp ? -lineStride : lineStride;
1228                 processImageUpdate(bi, 0, i,
1229                                    destinationRegion.width, 1, 1, 1,
1230                                    new int[]{0});
1231                 processImageProgress(100.0F * i/destinationRegion.height);
1232             }
1233         } else {
1234             byte[] buf = new byte[lineLength];
1235             lineStride =
1236                 ((ComponentSampleModel)sampleModel).getScanlineStride();
1237 
1238             if (isBottomUp) {
1239                 int lastLine =
1240                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1241                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1242             } else
1243                 iis.skipBytes(lineLength * sourceRegion.y);
1244 
1245             int skipLength = lineLength * (scaleY - 1);
1246 
1247             int k = destinationRegion.y * lineStride;
1248             if (isBottomUp)
1249                 k += (destinationRegion.height - 1) * lineStride;
1250             k += destinationRegion.x * 3;
1251 
1252             for (int j = 0, y = sourceRegion.y;
1253                  j < destinationRegion.height; j++, y+=scaleY) {
1254 
1255                 if (abortRequested())
1256                     break;
1257                 iis.read(buf, 0, lineLength);
1258                 for (int i = 0, m = 3 * sourceRegion.x;
1259                      i < destinationRegion.width; i++, m += 3 * scaleX) {
1260                     //get the bit and assign to the data buffer of the raster
1261                     int n = 3 * i + k;
1262                     for (int b = 0; b < destBands.length; b++)
1263                         bdata[n + destBands[b]] = buf[m + sourceBands[b]];
1264                 }
1265 
1266                 k += isBottomUp ? -lineStride : lineStride;
1267                 iis.skipBytes(skipLength);
1268                 processImageUpdate(bi, 0, j,
1269                                    destinationRegion.width, 1, 1, 1,
1270                                    new int[]{0});
1271                 processImageProgress(100.0F*j/destinationRegion.height);
1272             }
1273         }
1274     }
1275 
1276     private void read16Bit(short sdata[]) throws IOException {
1277         // Padding bytes at the end of each scanline
1278         // width * bitsPerPixel should be divisible by 32
1279         int padding = width * 2 % 4;
1280 
1281         if ( padding != 0)
1282             padding = 4 - padding;
1283 
1284         int lineLength = width + padding / 2;
1285 
1286         if (noTransform) {
1287             int j = isBottomUp ? (height -1) * width : 0;
1288             for (int i=0; i<height; i++) {
1289                 if (abortRequested()) {
1290                     break;
1291                 }
1292 
1293                 iis.readFully(sdata, j, width);
1294                 iis.skipBytes(padding);
1295 
1296                 j += isBottomUp ? -width : width;
1297                 processImageUpdate(bi, 0, i,
1298                                    destinationRegion.width, 1, 1, 1,
1299                                    new int[]{0});
1300                 processImageProgress(100.0F * i/destinationRegion.height);
1301             }
1302         } else {
1303             short[] buf = new short[lineLength];
1304             int lineStride =
1305                 ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
1306 
1307             if (isBottomUp) {
1308                 int lastLine =
1309                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1310                 iis.skipBytes(lineLength * (height - 1 - lastLine) << 1);
1311             } else
1312                 iis.skipBytes(lineLength * sourceRegion.y << 1);
1313 
1314             int skipLength = lineLength * (scaleY - 1) << 1;
1315 
1316             int k = destinationRegion.y * lineStride;
1317             if (isBottomUp)
1318                 k += (destinationRegion.height - 1) * lineStride;
1319             k += destinationRegion.x;
1320 
1321             for (int j = 0, y = sourceRegion.y;
1322                  j < destinationRegion.height; j++, y+=scaleY) {
1323 
1324                 if (abortRequested())
1325                     break;
1326                 iis.readFully(buf, 0, lineLength);
1327                 for (int i = 0, m = sourceRegion.x;
1328                      i < destinationRegion.width; i++, m += scaleX) {
1329                     //get the bit and assign to the data buffer of the raster
1330                     sdata[k + i] = buf[m];
1331                 }
1332 
1333                 k += isBottomUp ? -lineStride : lineStride;
1334                 iis.skipBytes(skipLength);
1335                 processImageUpdate(bi, 0, j,
1336                                    destinationRegion.width, 1, 1, 1,
1337                                    new int[]{0});
1338                 processImageProgress(100.0F*j/destinationRegion.height);
1339             }
1340         }
1341     }
1342 
1343     private void read32Bit(int idata[]) throws IOException {
1344         if (noTransform) {
1345             int j = isBottomUp ? (height -1) * width : 0;
1346 
1347             for (int i=0; i<height; i++) {
1348                 if (abortRequested()) {
1349                     break;
1350                 }
1351                 iis.readFully(idata, j, width);
1352                 j += isBottomUp ? -width : width;
1353                 processImageUpdate(bi, 0, i,
1354                                    destinationRegion.width, 1, 1, 1,
1355                                    new int[]{0});
1356                 processImageProgress(100.0F * i/destinationRegion.height);
1357             }
1358         } else {
1359             int[] buf = new int[width];
1360             int lineStride =
1361                 ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
1362 
1363             if (isBottomUp) {
1364                 int lastLine =
1365                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1366                 iis.skipBytes(width * (height - 1 - lastLine) << 2);
1367             } else
1368                 iis.skipBytes(width * sourceRegion.y << 2);
1369 
1370             int skipLength = width * (scaleY - 1) << 2;
1371 
1372             int k = destinationRegion.y * lineStride;
1373             if (isBottomUp)
1374                 k += (destinationRegion.height - 1) * lineStride;
1375             k += destinationRegion.x;
1376 
1377             for (int j = 0, y = sourceRegion.y;
1378                  j < destinationRegion.height; j++, y+=scaleY) {
1379 
1380                 if (abortRequested())
1381                     break;
1382                 iis.readFully(buf, 0, width);
1383                 for (int i = 0, m = sourceRegion.x;
1384                      i < destinationRegion.width; i++, m += scaleX) {
1385                     //get the bit and assign to the data buffer of the raster
1386                     idata[k + i] = buf[m];
1387                 }
1388 
1389                 k += isBottomUp ? -lineStride : lineStride;
1390                 iis.skipBytes(skipLength);
1391                 processImageUpdate(bi, 0, j,
1392                                    destinationRegion.width, 1, 1, 1,
1393                                    new int[]{0});
1394                 processImageProgress(100.0F*j/destinationRegion.height);
1395             }
1396         }
1397     }
1398 
1399     private void readRLE8(byte bdata[]) throws IOException {
1400         // If imageSize field is not provided, calculate it.
1401         int imSize = (int)imageSize;
1402         if (imSize == 0) {
1403             imSize = (int)(bitmapFileSize - bitmapOffset);
1404         }
1405 
1406         int padding = 0;
1407         // If width is not 32 bit aligned, then while uncompressing each
1408         // scanline will have padding bytes, calculate the amount of padding
1409         int remainder = width % 4;
1410         if (remainder != 0) {
1411             padding = 4 - remainder;
1412         }
1413 
1414         // Read till we have the whole image
1415         byte values[] = new byte[imSize];
1416         int bytesRead = 0;
1417         iis.readFully(values, 0, imSize);
1418 
1419         // Since data is compressed, decompress it
1420         decodeRLE8(imSize, padding, values, bdata);
1421     }
1422 
1423     private void decodeRLE8(int imSize,
1424                             int padding,
1425                             byte[] values,
1426                             byte[] bdata) throws IOException {
1427 
1428         byte val[] = new byte[width * height];
1429         int count = 0, l = 0;
1430         int value;
1431         boolean flag = false;
1432         int lineNo = isBottomUp ? height - 1 : 0;
1433         int lineStride =
1434             ((ComponentSampleModel)sampleModel).getScanlineStride();
1435         int finished = 0;
1436 
1437         while (count != imSize) {
1438             value = values[count++] & 0xff;
1439             if (value == 0) {
1440                 switch(values[count++] & 0xff) {
1441 
1442                 case 0:
1443                     // End-of-scanline marker
1444                     if (lineNo >= sourceRegion.y &&
1445                         lineNo < sourceRegion.y + sourceRegion.height) {
1446                         if (noTransform) {
1447                             int pos = lineNo * width;
1448                             for(int i = 0; i < width; i++)
1449                                 bdata[pos++] = val[i];
1450                             processImageUpdate(bi, 0, lineNo,
1451                                                destinationRegion.width, 1, 1, 1,
1452                                                new int[]{0});
1453                             finished++;
1454                         } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1455                             int currentLine = (lineNo - sourceRegion.y) / scaleY +
1456                                 destinationRegion.y;
1457                             int pos = currentLine * lineStride;
1458                             pos += destinationRegion.x;
1459                             for (int i = sourceRegion.x;
1460                                  i < sourceRegion.x + sourceRegion.width;
1461                                  i += scaleX)
1462                                 bdata[pos++] = val[i];
1463                             processImageUpdate(bi, 0, currentLine,
1464                                                destinationRegion.width, 1, 1, 1,
1465                                                new int[]{0});
1466                             finished++;
1467                         }
1468                     }
1469                     processImageProgress(100.0F * finished / destinationRegion.height);
1470                     lineNo += isBottomUp ? -1 : 1;
1471                     l = 0;
1472 
1473                     if (abortRequested()) {
1474                         flag = true;
1475                     }
1476 
1477                     break;
1478 
1479                 case 1:
1480                     // End-of-RLE marker
1481                     flag = true;
1482                     break;
1483 
1484                 case 2:
1485                     // delta or vector marker
1486                     int xoff = values[count++] & 0xff;
1487                     int yoff = values[count] & 0xff;
1488                     // Move to the position xoff, yoff down
1489                     l += xoff + yoff*width;
1490                     break;
1491 
1492                 default:
1493                     int end = values[count-1] & 0xff;
1494                     for (int i=0; i<end; i++) {
1495                         val[l++] = (byte)(values[count++] & 0xff);
1496                     }
1497 
1498                     // Whenever end pixels can fit into odd number of bytes,
1499                     // an extra padding byte will be present, so skip that.
1500                     if ((end & 1) == 1) {
1501                         count++;
1502                     }
1503                 }
1504             } else {
1505                 for (int i=0; i<value; i++) {
1506                     val[l++] = (byte)(values[count] & 0xff);
1507                 }
1508 
1509                 count++;
1510             }
1511 
1512             // If End-of-RLE data, then exit the while loop
1513             if (flag) {
1514                 break;
1515             }
1516         }
1517     }
1518 
1519     private void readRLE4(byte[] bdata) throws IOException {
1520 
1521         // If imageSize field is not specified, calculate it.
1522         int imSize = (int)imageSize;
1523         if (imSize == 0) {
1524             imSize = (int)(bitmapFileSize - bitmapOffset);
1525         }
1526 
1527         int padding = 0;
1528         // If width is not 32 byte aligned, then while uncompressing each
1529         // scanline will have padding bytes, calculate the amount of padding
1530         int remainder = width % 4;
1531         if (remainder != 0) {
1532             padding = 4 - remainder;
1533         }
1534 
1535         // Read till we have the whole image
1536         byte[] values = new byte[imSize];
1537         iis.readFully(values, 0, imSize);
1538 
1539         // Decompress the RLE4 compressed data.
1540         decodeRLE4(imSize, padding, values, bdata);
1541     }
1542 
1543     private void decodeRLE4(int imSize,
1544                             int padding,
1545                             byte[] values,
1546                             byte[] bdata) throws IOException {
1547         byte[] val = new byte[width];
1548         int count = 0, l = 0;
1549         int value;
1550         boolean flag = false;
1551         int lineNo = isBottomUp ? height - 1 : 0;
1552         int lineStride =
1553             ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1554         int finished = 0;
1555 
1556         while (count != imSize) {
1557 
1558             value = values[count++] & 0xFF;
1559             if (value == 0) {
1560 
1561 
1562                 // Absolute mode
1563                 switch(values[count++] & 0xFF) {
1564 
1565                 case 0:
1566                     // End-of-scanline marker
1567                     // End-of-scanline marker
1568                     if (lineNo >= sourceRegion.y &&
1569                         lineNo < sourceRegion.y + sourceRegion.height) {
1570                         if (noTransform) {
1571                             int pos = lineNo * (width + 1 >> 1);
1572                             for(int i = 0, j = 0; i < width >> 1; i++)
1573                                 bdata[pos++] =
1574                                     (byte)((val[j++] << 4) | val[j++]);
1575                             if ((width & 1) == 1)
1576                                 bdata[pos] |= val[width - 1] << 4;
1577 
1578                             processImageUpdate(bi, 0, lineNo,
1579                                                destinationRegion.width, 1, 1, 1,
1580                                                new int[]{0});
1581                             finished++;
1582                         } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1583                             int currentLine = (lineNo - sourceRegion.y) / scaleY +
1584                                 destinationRegion.y;
1585                             int pos = currentLine * lineStride;
1586                             pos += destinationRegion.x >> 1;
1587                             int shift = (1 - (destinationRegion.x & 1)) << 2;
1588                             for (int i = sourceRegion.x;
1589                                  i < sourceRegion.x + sourceRegion.width;
1590                                  i += scaleX) {
1591                                 bdata[pos] |= val[i] << shift;
1592                                 shift += 4;
1593                                 if (shift == 4) {
1594                                     pos++;
1595                                 }
1596                                 shift &= 7;
1597                             }
1598                             processImageUpdate(bi, 0, currentLine,
1599                                                destinationRegion.width, 1, 1, 1,
1600                                                new int[]{0});
1601                             finished++;
1602                         }
1603                     }
1604                     processImageProgress(100.0F * finished / destinationRegion.height);
1605                     lineNo += isBottomUp ? -1 : 1;
1606                     l = 0;
1607 
1608                     if (abortRequested()) {
1609                         flag = true;
1610                     }
1611 
1612                     break;
1613 
1614                 case 1:
1615                     // End-of-RLE marker
1616                     flag = true;
1617                     break;
1618 
1619                 case 2:
1620                     // delta or vector marker
1621                     int xoff = values[count++] & 0xFF;
1622                     int yoff = values[count] & 0xFF;
1623                     // Move to the position xoff, yoff down
1624                     l += xoff + yoff*width;
1625                     break;
1626 
1627                 default:
1628                     int end = values[count-1] & 0xFF;
1629                     for (int i=0; i<end; i++) {
1630                         val[l++] = (byte)(((i & 1) == 0) ? (values[count] & 0xf0) >> 4
1631                                           : (values[count++] & 0x0f));
1632                     }
1633 
1634                     // When end is odd, the above for loop does not
1635                     // increment count, so do it now.
1636                     if ((end & 1) == 1) {
1637                         count++;
1638                     }
1639 
1640                     // Whenever end pixels can fit into odd number of bytes,
1641                     // an extra padding byte will be present, so skip that.
1642                     if ((((int)Math.ceil(end/2)) & 1) ==1 ) {
1643                         count++;
1644                     }
1645                     break;
1646                 }
1647             } else {
1648                 // Encoded mode
1649                 int alternate[] = { (values[count] & 0xf0) >> 4,
1650                                     values[count] & 0x0f };
1651                 for (int i=0; (i < value) && (l < width); i++) {
1652                     val[l++] = (byte)alternate[i & 1];
1653                 }
1654 
1655                 count++;
1656             }
1657 
1658             // If End-of-RLE data, then exit the while loop
1659             if (flag) {
1660                 break;
1661             }
1662         }
1663     }
1664 
1665     /** Decodes the jpeg/png image embedded in the bitmap using any jpeg
1666      *  ImageIO-style plugin.
1667      *
1668      * @param bi The destination <code>BufferedImage</code>.
1669      * @param bmpParam The <code>ImageReadParam</code> for decoding this
1670      *          BMP image.  The parameters for subregion, band selection and
1671      *          subsampling are used in decoding the jpeg image.
1672      */
1673 
1674     private BufferedImage readEmbedded(int type,
1675                               BufferedImage bi, ImageReadParam bmpParam)
1676       throws IOException {
1677         String format;
1678         switch(type) {
1679           case BI_JPEG:
1680               format = "JPEG";
1681               break;
1682           case BI_PNG:
1683               format = "PNG";
1684               break;
1685           default:
1686               throw new
1687                   IOException("Unexpected compression type: " + type);
1688         }
1689         ImageReader reader =
1690             ImageIO.getImageReadersByFormatName(format).next();
1691         if (reader == null) {
1692             throw new RuntimeException(I18N.getString("BMPImageReader4") +
1693                                        " " + format);
1694         }
1695         // prepare input
1696         byte[] buff = new byte[(int)imageSize];
1697         iis.read(buff);
1698         reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(buff)));
1699         if (bi == null) {
1700             ImageTypeSpecifier embType = reader.getImageTypes(0).next();
1701             bi = embType.createBufferedImage(destinationRegion.x +
1702                                              destinationRegion.width,
1703                                              destinationRegion.y +
1704                                              destinationRegion.height);
1705         }
1706 
1707         reader.addIIOReadProgressListener(new EmbeddedProgressAdapter() {
1708                 public void imageProgress(ImageReader source,
1709                                           float percentageDone)
1710                 {
1711                     processImageProgress(percentageDone);
1712                 }
1713             });
1714 
1715         reader.addIIOReadUpdateListener(new IIOReadUpdateListener() {
1716                 public void imageUpdate(ImageReader source,
1717                                         BufferedImage theImage,
1718                                         int minX, int minY,
1719                                         int width, int height,
1720                                         int periodX, int periodY,
1721                                         int[] bands)
1722                 {
1723                     processImageUpdate(theImage, minX, minY,
1724                                        width, height,
1725                                        periodX, periodY, bands);
1726                 }
1727                 public void passComplete(ImageReader source,
1728                                          BufferedImage theImage)
1729                 {
1730                     processPassComplete(theImage);
1731                 }
1732                 public void passStarted(ImageReader source,
1733                                         BufferedImage theImage,
1734                                         int pass,
1735                                         int minPass, int maxPass,
1736                                         int minX, int minY,
1737                                         int periodX, int periodY,
1738                                         int[] bands)
1739                 {
1740                     processPassStarted(theImage, pass, minPass, maxPass,
1741                                        minX, minY, periodX, periodY,
1742                                        bands);
1743                 }
1744                 public void thumbnailPassComplete(ImageReader source,
1745                                                   BufferedImage thumb) {}
1746                 public void thumbnailPassStarted(ImageReader source,
1747                                                  BufferedImage thumb,
1748                                                  int pass,
1749                                                  int minPass, int maxPass,
1750                                                  int minX, int minY,
1751                                                  int periodX, int periodY,
1752                                                  int[] bands) {}
1753                 public void thumbnailUpdate(ImageReader source,
1754                                             BufferedImage theThumbnail,
1755                                             int minX, int minY,
1756                                             int width, int height,
1757                                             int periodX, int periodY,
1758                                             int[] bands) {}
1759             });
1760 
1761         reader.addIIOReadWarningListener(new IIOReadWarningListener() {
1762                 public void warningOccurred(ImageReader source, String warning)
1763                 {
1764                     processWarningOccurred(warning);
1765                 }
1766             });
1767 
1768         ImageReadParam param = reader.getDefaultReadParam();
1769         param.setDestination(bi);
1770         param.setDestinationBands(bmpParam.getDestinationBands());
1771         param.setDestinationOffset(bmpParam.getDestinationOffset());
1772         param.setSourceBands(bmpParam.getSourceBands());
1773         param.setSourceRegion(bmpParam.getSourceRegion());
1774         param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
1775                                    bmpParam.getSourceYSubsampling(),
1776                                    bmpParam.getSubsamplingXOffset(),
1777                                    bmpParam.getSubsamplingYOffset());
1778         reader.read(0, param);
1779         return bi;
1780     }
1781 
1782     private class EmbeddedProgressAdapter implements IIOReadProgressListener {
1783         public void imageComplete(ImageReader src) {}
1784         public void imageProgress(ImageReader src, float percentageDone) {}
1785         public void imageStarted(ImageReader src, int imageIndex) {}
1786         public void thumbnailComplete(ImageReader src) {}
1787         public void thumbnailProgress(ImageReader src, float percentageDone) {}
1788         public void thumbnailStarted(ImageReader src, int iIdx, int tIdx) {}
1789         public void sequenceComplete(ImageReader src) {}
1790         public void sequenceStarted(ImageReader src, int minIndex) {}
1791         public void readAborted(ImageReader src) {}
1792     }
1793 
1794     private static Boolean isLinkedProfileDisabled = null;
1795 
1796     private static boolean isLinkedProfileAllowed() {
1797         if (isLinkedProfileDisabled == null) {
1798             PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
1799                 public Boolean run() {
1800                     return Boolean.getBoolean("sun.imageio.plugins.bmp.disableLinkedProfiles");
1801                 }
1802             };
1803             isLinkedProfileDisabled = AccessController.doPrivileged(a);
1804         }
1805         return !isLinkedProfileDisabled;
1806     }
1807 
1808     private static Boolean isWindowsPlatform = null;
1809 
1810     /**
1811      * Verifies whether the byte array contans a unc path.
1812      * Non-UNC path examples:
1813      *  c:\path\to\file  - simple notation
1814      *  \\?\c:\path\to\file - long notation
1815      *
1816      * UNC path examples:
1817      *  \\server\share - a UNC path in simple notation
1818      *  \\?\UNC\server\share - a UNC path in long notation
1819      *  \\.\some\device - a path to device.
1820      */
1821     private static boolean isUncOrDevicePath(byte[] p) {
1822         if (isWindowsPlatform == null) {
1823             PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
1824                 public Boolean run() {
1825                     String osname = System.getProperty("os.name");
1826                     return (osname != null &&
1827                             osname.toLowerCase().startsWith("win"));
1828                 }
1829             };
1830             isWindowsPlatform = AccessController.doPrivileged(a);
1831         }
1832 
1833         if (!isWindowsPlatform) {
1834             /* no need for the check on platforms except windows */
1835             return false;
1836         }
1837 
1838         /* normalize prefix of the path */
1839         if (p[0] == '/') p[0] = '\\';
1840         if (p[1] == '/') p[1] = '\\';
1841         if (p[3] == '/') p[3] = '\\';
1842 
1843 
1844         if ((p[0] == '\\') && (p[1] == '\\')) {
1845             if ((p[2] == '?') && (p[3] == '\\')) {
1846                 // long path: whether unc or local
1847                 return ((p[4] == 'U' || p[4] == 'u') &&
1848                         (p[5] == 'N' || p[5] == 'n') &&
1849                         (p[6] == 'C' || p[6] == 'c'));
1850             } else {
1851                 // device path or short unc notation
1852                 return true;
1853             }
1854         } else {
1855             return false;
1856         }
1857     }
1858 }