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 }