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