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