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