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