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