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