1 /*
   2  * Copyright (c) 2003, 2017, 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 boolean copyRLE8ScanlineToDst(int lineNo,
1443                                           byte[] val,
1444                                           byte[] bdata) {
1445         // Return value
1446         boolean isSuccess = false;
1447 
1448         // Reusing the code to copy 1 row of pixels or scanline to required
1449         // destination buffer.
1450         if (lineNo >= sourceRegion.y &&
1451             lineNo < sourceRegion.y + sourceRegion.height) {
1452             if (noTransform) {
1453                 int pos = lineNo * width;
1454                 for(int i = 0; i < width; i++)
1455                     bdata[pos++] = val[i];
1456                 processImageUpdate(bi, 0, lineNo,
1457                                    destinationRegion.width, 1, 1, 1,
1458                                    new int[]{0});
1459                 isSuccess = true;
1460             } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1461                 int lineStride =
1462                     ((ComponentSampleModel)sampleModel).getScanlineStride();
1463                 int currentLine = (lineNo - sourceRegion.y) / scaleY +
1464                     destinationRegion.y;
1465                 int pos = currentLine * lineStride;
1466                 pos += destinationRegion.x;
1467                 for (int i = sourceRegion.x;
1468                      i < sourceRegion.x + sourceRegion.width;
1469                      i += scaleX)
1470                     bdata[pos++] = val[i];
1471                 processImageUpdate(bi, 0, currentLine,
1472                                    destinationRegion.width, 1, 1, 1,
1473                                    new int[]{0});
1474                 isSuccess = true;
1475             }
1476             // Ensure to reset the scanline buffer once the copy is complete.
1477             for(int scIndex = 0; scIndex < width; scIndex++) {
1478                 val[scIndex] = 0;
1479             }
1480         }
1481 
1482         return isSuccess;
1483     }
1484 
1485     private void decodeRLE8(int imSize,
1486                             int padding,
1487                             byte[] values,
1488                             byte[] bdata) throws IOException {
1489 
1490         byte val[] = new byte[width];
1491         int count = 0, l = 0;
1492         int value;
1493         boolean flag = false;
1494         int lineNo = isBottomUp ? height - 1 : 0;
1495         int finished = 0;
1496 
1497         // Ensure image source has sufficient data to decode
1498         while ((count + 1) < imSize) {
1499             value = values[count++] & 0xff;
1500             if (value == 0) {
1501                 switch(values[count++] & 0xff) {
1502 
1503                 case 0:
1504                     // End-of-scanline marker
1505                     // Copy the decoded scanline to destination
1506                     if (copyRLE8ScanlineToDst(lineNo, val, bdata)) {
1507                         finished++;
1508                     }
1509                     processImageProgress(100.0F * finished / destinationRegion.height);
1510                     lineNo += isBottomUp ? -1 : 1;
1511                     l = 0;
1512 
1513                     if (abortRequested()) {
1514                         flag = true;
1515                     }
1516                     break;
1517 
1518                 case 1:
1519                     // End-of-RLE marker
1520                     flag = true;
1521 
1522                     // Check if the last decoded scanline was copied to
1523                     // destination bitmap
1524                     if (l != 0) {
1525                         // Copy the decoded scanline to destination
1526                         if (copyRLE8ScanlineToDst(lineNo, val, bdata)) {
1527                             finished++;
1528                         }
1529                         processImageProgress(100.0F * finished / destinationRegion.height);
1530                         lineNo += isBottomUp ? -1 : 1;
1531                         l = 0;
1532                     }
1533                     break;
1534 
1535                 case 2:
1536                     // delta or vector marker
1537                     if ((count+1) < imSize) {
1538                         int xoff = values[count++] & 0xff;
1539                         int yoff = values[count++] & 0xff;
1540 
1541                         // Check if the yOffset shifts the decoding to another
1542                         // row. In such cases, the decoded pixels in scanline
1543                         // buffer-val must be copied to the destination image.
1544                         if (yoff != 0) {
1545                             // Copy the decoded scanline to destination
1546                             if (copyRLE8ScanlineToDst(lineNo, val, bdata)) {
1547                                 finished++;
1548                             }
1549                             processImageProgress(100.0F * finished
1550                                                  / destinationRegion.height);
1551                             lineNo += isBottomUp ? -yoff : yoff;
1552                         }
1553 
1554                         // Move to the position xoff, yoff down
1555                         l += xoff + yoff*width;
1556                         l %= width;
1557                     }
1558                     break;
1559 
1560                 default:
1561                     int end = values[count-1] & 0xff;
1562                     byte readByte = 0;
1563                     // Ensure to check if the source index-count, does not
1564                     // exceed the source image size
1565                     for (int i=0; (i < end) && (count < imSize); i++) {
1566                         readByte = (byte)(values[count++] & 0xff);
1567                         // Ensure to check if scanline index-l, does not
1568                         // exceed the scanline buffer size (width of image)
1569                         if (l < width) {
1570                             val[l++] = readByte;
1571                         }
1572                     }
1573 
1574                     // Whenever end pixels can fit into odd number of bytes,
1575                     // an extra padding byte will be present, so skip that.
1576                     if ((end & 1) == 1) {
1577                         count++;
1578                     }
1579                     break;
1580                 }
1581             } else {
1582                 // Encoded mode
1583                 // Ensure to check if the source index-count, does not
1584                 // exceed the source image size
1585                 if (count < imSize) {
1586                     for (int i=0; (i < value) && (l < width); i++) {
1587                         val[l++] = (byte)(values[count] & 0xff);
1588                     }
1589                 }
1590 
1591                 count++;
1592             }
1593 
1594             // If End-of-RLE data, then exit the while loop
1595             if (flag) {
1596                 break;
1597             }
1598         }
1599     }
1600 
1601     private void readRLE4(byte[] bdata) throws IOException {
1602 
1603         // If imageSize field is not specified, calculate it.
1604         int imSize = (int)imageSize;
1605         if (imSize == 0) {
1606             imSize = (int)(bitmapFileSize - bitmapOffset);
1607         }
1608 
1609         int padding = 0;
1610         // If width is not 32 byte aligned, then while uncompressing each
1611         // scanline will have padding bytes, calculate the amount of padding
1612         int remainder = width % 4;
1613         if (remainder != 0) {
1614             padding = 4 - remainder;
1615         }
1616 
1617         // Read till we have the whole image
1618         byte[] values = new byte[imSize];
1619         iis.readFully(values, 0, imSize);
1620 
1621         // Decompress the RLE4 compressed data.
1622         decodeRLE4(imSize, padding, values, bdata);
1623     }
1624 
1625     private boolean copyRLE4ScanlineToDst(int lineNo,
1626                                           byte[] val,
1627                                           byte[] bdata) throws IOException {
1628         // Return value
1629         boolean isSuccess = false;
1630 
1631         // Reusing the code to copy 1 row of pixels or scanline to required
1632         // destination buffer.
1633         if (lineNo >= sourceRegion.y &&
1634             lineNo < sourceRegion.y + sourceRegion.height) {
1635             if (noTransform) {
1636                 int pos = lineNo * (width + 1 >> 1);
1637                 for(int i = 0, j = 0; i < width >> 1; i++)
1638                     bdata[pos++] =
1639                         (byte)((val[j++] << 4) | val[j++]);
1640                 if ((width & 1) == 1)
1641                     bdata[pos] |= val[width - 1] << 4;
1642 
1643                 processImageUpdate(bi, 0, lineNo,
1644                                    destinationRegion.width, 1, 1, 1,
1645                                    new int[]{0});
1646                 isSuccess = true;
1647             } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1648                 int lineStride =
1649                     ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1650                 int currentLine = (lineNo - sourceRegion.y) / scaleY +
1651                     destinationRegion.y;
1652                 int pos = currentLine * lineStride;
1653                 pos += destinationRegion.x >> 1;
1654                 int shift = (1 - (destinationRegion.x & 1)) << 2;
1655                 for (int i = sourceRegion.x;
1656                      i < sourceRegion.x + sourceRegion.width;
1657                      i += scaleX) {
1658                     bdata[pos] |= val[i] << shift;
1659                     shift += 4;
1660                     if (shift == 4) {
1661                         pos++;
1662                     }
1663                     shift &= 7;
1664                 }
1665                 processImageUpdate(bi, 0, currentLine,
1666                                    destinationRegion.width, 1, 1, 1,
1667                                    new int[]{0});
1668                 isSuccess = true;
1669             }
1670             // Ensure to reset the scanline buffer once the copy is complete.
1671             for(int scIndex = 0; scIndex < width; scIndex++) {
1672                 val[scIndex] = 0;
1673             }
1674         }
1675         return isSuccess;
1676     }
1677 
1678     private void decodeRLE4(int imSize,
1679                             int padding,
1680                             byte[] values,
1681                             byte[] bdata) throws IOException {
1682         byte[] val = new byte[width];
1683         int count = 0, l = 0;
1684         int value;
1685         boolean flag = false;
1686         int lineNo = isBottomUp ? height - 1 : 0;
1687         int finished = 0;
1688 
1689         // Ensure the image has sufficient data before proceeding to decode
1690         while ((count + 1) < imSize) {
1691 
1692             value = values[count++] & 0xFF;
1693             if (value == 0) {
1694 
1695                 // Absolute mode
1696                 switch(values[count++] & 0xFF) {
1697 
1698                 case 0:
1699                     // End-of-scanline marker
1700                     // Copy the decoded scanline to destination
1701                     if (copyRLE4ScanlineToDst(lineNo, val, bdata)) {
1702                         finished++;
1703                     }
1704                     processImageProgress(100.0F * finished / destinationRegion.height);
1705                     lineNo += isBottomUp ? -1 : 1;
1706                     l = 0;
1707 
1708                     if (abortRequested()) {
1709                         flag = true;
1710                     }
1711 
1712                     break;
1713 
1714                 case 1:
1715                     // End-of-RLE marker
1716                     flag = true;
1717 
1718                     // Check if the last decoded scanline was copied to
1719                     // destination bitmap
1720                     if (l != 0) {
1721                         // Copy the decoded scanline to destination
1722                         if (copyRLE4ScanlineToDst(lineNo, val, bdata)) {
1723                             finished++;
1724                         }
1725                         processImageProgress(100.0F * finished / destinationRegion.height);
1726                         lineNo += isBottomUp ? -1 : 1;
1727                         l = 0;
1728                     }
1729                     break;
1730 
1731                 case 2:
1732                     // delta or vector marker
1733                     if ((count + 1) < imSize) {
1734                         int xoff = values[count++] & 0xFF;
1735                         int yoff = values[count++] & 0xFF;
1736 
1737                         // Check if the yOffset shifts the decoding to another
1738                         // row. In such cases, the decoded pixels in scanline
1739                         // buffer-val must be copied to the destination image.
1740                         if (yoff != 0) {
1741                             // Copy the decoded scanline to destination
1742                             if (copyRLE4ScanlineToDst(lineNo, val, bdata)) {
1743                                 finished++;
1744                             }
1745                             processImageProgress(100.0F * finished
1746                                                  / destinationRegion.height);
1747                             lineNo += isBottomUp ? -yoff : yoff;
1748                         }
1749 
1750                         // Move to the position (xoff, yoff). Since l-is used
1751                         // to index into the scanline buffer, the accumulated
1752                         // offset is limited to the width of the scanline
1753                         l += xoff + yoff*width;
1754                         l %= width;
1755                     }
1756                     break;
1757 
1758                 default:
1759                     int end = values[count-1] & 0xFF;
1760                     byte readByte = 0;
1761                     // Ensure to check if the source index-count, does not
1762                     // exceed the source image size
1763                     for (int i = 0; (i < end) && (count < imSize); i++) {
1764                         readByte = (byte)(((i & 1) == 0) ?
1765                                         (values[count] & 0xf0) >> 4 :
1766                                         (values[count++] & 0x0f));
1767                         // Ensure to check if scanline index-l, does not
1768                         // exceed the scanline buffer size (width of image)
1769                         if (l < width) {
1770                             val[l++] = readByte;
1771                         }
1772                     }
1773 
1774                     // When end is odd, the above for loop does not
1775                     // increment count, so do it now.
1776                     if ((end & 1) == 1) {
1777                         count++;
1778                     }
1779 
1780                     // Whenever end pixels can fit into odd number of bytes,
1781                     // an extra padding byte will be present, so skip that.
1782                     if ((((end + 1) / 2) & 1) == 1) {
1783                         count++;
1784                     }
1785                     break;
1786                 }
1787             } else {
1788                 // Encoded mode
1789                 // Ensure to check if the source index-count, does not
1790                 // exceed the source image size
1791                 if (count < imSize) {
1792                     int alternate[] = { (values[count] & 0xf0) >> 4,
1793                                         values[count] & 0x0f };
1794                     for (int i=0; (i < value) && (l < width); i++) {
1795                         val[l++] = (byte)alternate[i & 1];
1796                     }
1797                 }
1798 
1799                 count++;
1800             }
1801 
1802             // If End-of-RLE data, then exit the while loop
1803             if (flag) {
1804                 break;
1805             }
1806         }
1807     }
1808 
1809     /** Decodes the jpeg/png image embedded in the bitmap using any jpeg
1810      *  ImageIO-style plugin.
1811      *
1812      * @param bi The destination {@code BufferedImage}.
1813      * @param bmpParam The {@code ImageReadParam} for decoding this
1814      *          BMP image.  The parameters for subregion, band selection and
1815      *          subsampling are used in decoding the jpeg image.
1816      */
1817 
1818     private BufferedImage readEmbedded(int type,
1819                               BufferedImage bi, ImageReadParam bmpParam)
1820       throws IOException {
1821         String format;
1822         switch(type) {
1823           case BI_JPEG:
1824               format = "JPEG";
1825               break;
1826           case BI_PNG:
1827               format = "PNG";
1828               break;
1829           default:
1830               throw new
1831                   IOException("Unexpected compression type: " + type);
1832         }
1833         ImageReader reader =
1834             ImageIO.getImageReadersByFormatName(format).next();
1835         if (reader == null) {
1836             throw new RuntimeException(I18N.getString("BMPImageReader4") +
1837                                        " " + format);
1838         }
1839         // prepare input
1840         byte[] buff = new byte[(int)imageSize];
1841         iis.read(buff);
1842         reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(buff)));
1843         if (bi == null) {
1844             ImageTypeSpecifier embType = reader.getImageTypes(0).next();
1845             bi = embType.createBufferedImage(destinationRegion.x +
1846                                              destinationRegion.width,
1847                                              destinationRegion.y +
1848                                              destinationRegion.height);
1849         }
1850 
1851         reader.addIIOReadProgressListener(new EmbeddedProgressAdapter() {
1852                 public void imageProgress(ImageReader source,
1853                                           float percentageDone)
1854                 {
1855                     processImageProgress(percentageDone);
1856                 }
1857             });
1858 
1859         reader.addIIOReadUpdateListener(new IIOReadUpdateListener() {
1860                 public void imageUpdate(ImageReader source,
1861                                         BufferedImage theImage,
1862                                         int minX, int minY,
1863                                         int width, int height,
1864                                         int periodX, int periodY,
1865                                         int[] bands)
1866                 {
1867                     processImageUpdate(theImage, minX, minY,
1868                                        width, height,
1869                                        periodX, periodY, bands);
1870                 }
1871                 public void passComplete(ImageReader source,
1872                                          BufferedImage theImage)
1873                 {
1874                     processPassComplete(theImage);
1875                 }
1876                 public void passStarted(ImageReader source,
1877                                         BufferedImage theImage,
1878                                         int pass,
1879                                         int minPass, int maxPass,
1880                                         int minX, int minY,
1881                                         int periodX, int periodY,
1882                                         int[] bands)
1883                 {
1884                     processPassStarted(theImage, pass, minPass, maxPass,
1885                                        minX, minY, periodX, periodY,
1886                                        bands);
1887                 }
1888                 public void thumbnailPassComplete(ImageReader source,
1889                                                   BufferedImage thumb) {}
1890                 public void thumbnailPassStarted(ImageReader source,
1891                                                  BufferedImage thumb,
1892                                                  int pass,
1893                                                  int minPass, int maxPass,
1894                                                  int minX, int minY,
1895                                                  int periodX, int periodY,
1896                                                  int[] bands) {}
1897                 public void thumbnailUpdate(ImageReader source,
1898                                             BufferedImage theThumbnail,
1899                                             int minX, int minY,
1900                                             int width, int height,
1901                                             int periodX, int periodY,
1902                                             int[] bands) {}
1903             });
1904 
1905         reader.addIIOReadWarningListener(new IIOReadWarningListener() {
1906                 public void warningOccurred(ImageReader source, String warning)
1907                 {
1908                     processWarningOccurred(warning);
1909                 }
1910             });
1911 
1912         ImageReadParam param = reader.getDefaultReadParam();
1913         param.setDestination(bi);
1914         param.setDestinationBands(bmpParam.getDestinationBands());
1915         param.setDestinationOffset(bmpParam.getDestinationOffset());
1916         param.setSourceBands(bmpParam.getSourceBands());
1917         param.setSourceRegion(bmpParam.getSourceRegion());
1918         param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
1919                                    bmpParam.getSourceYSubsampling(),
1920                                    bmpParam.getSubsamplingXOffset(),
1921                                    bmpParam.getSubsamplingYOffset());
1922         reader.read(0, param);
1923         return bi;
1924     }
1925 
1926     private class EmbeddedProgressAdapter implements IIOReadProgressListener {
1927         public void imageComplete(ImageReader src) {}
1928         public void imageProgress(ImageReader src, float percentageDone) {}
1929         public void imageStarted(ImageReader src, int imageIndex) {}
1930         public void thumbnailComplete(ImageReader src) {}
1931         public void thumbnailProgress(ImageReader src, float percentageDone) {}
1932         public void thumbnailStarted(ImageReader src, int iIdx, int tIdx) {}
1933         public void sequenceComplete(ImageReader src) {}
1934         public void sequenceStarted(ImageReader src, int minIndex) {}
1935         public void readAborted(ImageReader src) {}
1936     }
1937 
1938     private static Boolean isLinkedProfileDisabled = null;
1939 
1940     private static boolean isLinkedProfileAllowed() {
1941         if (isLinkedProfileDisabled == null) {
1942             PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
1943                 public Boolean run() {
1944                     return Boolean.getBoolean("sun.imageio.plugins.bmp.disableLinkedProfiles");
1945                 }
1946             };
1947             isLinkedProfileDisabled = AccessController.doPrivileged(a);
1948         }
1949         return !isLinkedProfileDisabled;
1950     }
1951 
1952     private static Boolean isWindowsPlatform = null;
1953 
1954     /**
1955      * Verifies whether the byte array contans a unc path.
1956      * Non-UNC path examples:
1957      *  c:\path\to\file  - simple notation
1958      *  \\?\c:\path\to\file - long notation
1959      *
1960      * UNC path examples:
1961      *  \\server\share - a UNC path in simple notation
1962      *  \\?\UNC\server\share - a UNC path in long notation
1963      *  \\.\some\device - a path to device.
1964      */
1965     private static boolean isUncOrDevicePath(byte[] p) {
1966         if (isWindowsPlatform == null) {
1967             PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
1968                 public Boolean run() {
1969                     String osname = System.getProperty("os.name");
1970                     return (osname != null &&
1971                             osname.toLowerCase().startsWith("win"));
1972                 }
1973             };
1974             isWindowsPlatform = AccessController.doPrivileged(a);
1975         }
1976 
1977         if (!isWindowsPlatform) {
1978             /* no need for the check on platforms except windows */
1979             return false;
1980         }
1981 
1982         /* normalize prefix of the path */
1983         if (p[0] == '/') p[0] = '\\';
1984         if (p[1] == '/') p[1] = '\\';
1985         if (p[3] == '/') p[3] = '\\';
1986 
1987 
1988         if ((p[0] == '\\') && (p[1] == '\\')) {
1989             if ((p[2] == '?') && (p[3] == '\\')) {
1990                 // long path: whether unc or local
1991                 return ((p[4] == 'U' || p[4] == 'u') &&
1992                         (p[5] == 'N' || p[5] == 'n') &&
1993                         (p[6] == 'C' || p[6] == 'c'));
1994             } else {
1995                 // device path or short unc notation
1996                 return true;
1997             }
1998         } else {
1999             return false;
2000         }
2001     }
2002 }