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