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 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<ImageTypeSpecifier> 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<ImageTypeSpecifier> 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 = ((DataBufferByte)raster.getDataBuffer()).getData(); 810 else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT) 811 sdata = ((DataBufferUShort)raster.getDataBuffer()).getData(); 812 else if (sampleModel.getDataType() == DataBuffer.TYPE_INT) 813 idata = ((DataBufferInt)raster.getDataBuffer()).getData(); 814 } 815 816 // There should only be one tile. 817 switch(imageType) { 818 819 case VERSION_2_1_BIT: 820 // no compression 821 read1Bit(bdata); 822 break; 823 824 case VERSION_2_4_BIT: 825 // no compression 826 read4Bit(bdata); 827 break; 828 829 case VERSION_2_8_BIT: 830 // no compression 831 read8Bit(bdata); 832 break; 833 834 case VERSION_2_24_BIT: 835 // no compression 836 read24Bit(bdata); 837 break; 838 839 case VERSION_3_1_BIT: 840 // 1-bit images cannot be compressed. 841 read1Bit(bdata); 842 break; 843 844 case VERSION_3_4_BIT: 845 switch((int)compression) { 846 case BI_RGB: 847 read4Bit(bdata); 848 break; 849 850 case BI_RLE4: 851 readRLE4(bdata); 852 break; 853 854 default: 855 throw new 856 IIOException(I18N.getString("BMPImageReader1")); 857 } 858 break; 859 860 case VERSION_3_8_BIT: 861 switch((int)compression) { 862 case BI_RGB: 863 read8Bit(bdata); 864 break; 865 866 case BI_RLE8: 867 readRLE8(bdata); 868 break; 869 870 default: 871 throw new 872 IIOException(I18N.getString("BMPImageReader1")); 873 } 874 875 break; 876 877 case VERSION_3_24_BIT: 878 // 24-bit images are not compressed 879 read24Bit(bdata); 880 break; 881 882 case VERSION_3_NT_16_BIT: 883 read16Bit(sdata); 884 break; 885 886 case VERSION_3_NT_32_BIT: 887 read32Bit(idata); 888 break; 889 890 case VERSION_3_XP_EMBEDDED: 891 case VERSION_4_XP_EMBEDDED: 892 case VERSION_5_XP_EMBEDDED: 893 bi = readEmbedded((int)compression, bi, param); 894 break; 895 896 case VERSION_4_1_BIT: 897 read1Bit(bdata); 898 break; 899 900 case VERSION_4_4_BIT: 901 switch((int)compression) { 902 903 case BI_RGB: 904 read4Bit(bdata); 905 break; 906 907 case BI_RLE4: 908 readRLE4(bdata); 909 break; 910 911 default: 912 throw new 913 IIOException(I18N.getString("BMPImageReader1")); 914 } 915 break; 916 917 case VERSION_4_8_BIT: 918 switch((int)compression) { 919 920 case BI_RGB: 921 read8Bit(bdata); 922 break; 923 924 case BI_RLE8: 925 readRLE8(bdata); 926 break; 927 928 default: 929 throw new 930 IIOException(I18N.getString("BMPImageReader1")); 931 } 932 break; 933 934 case VERSION_4_16_BIT: 935 read16Bit(sdata); 936 break; 937 938 case VERSION_4_24_BIT: 939 read24Bit(bdata); 940 break; 941 942 case VERSION_4_32_BIT: 943 read32Bit(idata); 944 break; 945 } 946 947 if (abortRequested()) 948 processReadAborted(); 949 else 950 processImageComplete(); 951 952 return bi; 953 } 954 955 public boolean canReadRaster() { 956 return true; 957 } 958 959 public Raster readRaster(int imageIndex, 960 ImageReadParam param) throws IOException { 961 BufferedImage bi = read(imageIndex, param); 962 return bi.getData(); 963 } 964 965 private void resetHeaderInfo() { 966 gotHeader = false; 967 bi = null; 968 sampleModel = originalSampleModel = null; 969 colorModel = originalColorModel = null; 970 } 971 972 public void reset() { 973 super.reset(); 974 iis = null; 975 resetHeaderInfo(); 976 } 977 978 // Deal with 1 Bit images using IndexColorModels 979 private void read1Bit(byte[] bdata) throws IOException { 980 int bytesPerScanline = (width + 7) / 8; 981 int padding = bytesPerScanline % 4; 982 if (padding != 0) { 983 padding = 4 - padding; 984 } 985 986 int lineLength = bytesPerScanline + padding; 987 988 if (noTransform) { 989 int j = isBottomUp ? (height -1)*bytesPerScanline : 0; 990 991 for (int i=0; i<height; i++) { 992 if (abortRequested()) { 993 break; 994 } 995 iis.readFully(bdata, j, bytesPerScanline); 996 iis.skipBytes(padding); 997 j += isBottomUp ? -bytesPerScanline : bytesPerScanline; 998 processImageUpdate(bi, 0, i, 999 destinationRegion.width, 1, 1, 1, 1000 new int[]{0}); 1001 processImageProgress(100.0F * i/destinationRegion.height); 1002 } 1003 } else { 1004 byte[] buf = new byte[lineLength]; 1005 int lineStride = 1006 ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride(); 1007 1008 if (isBottomUp) { 1009 int lastLine = 1010 sourceRegion.y + (destinationRegion.height - 1) * scaleY; 1011 iis.skipBytes(lineLength * (height - 1 - lastLine)); 1012 } else 1013 iis.skipBytes(lineLength * sourceRegion.y); 1014 1015 int skipLength = lineLength * (scaleY - 1); 1016 1017 // cache the values to avoid duplicated computation 1018 int[] srcOff = new int[destinationRegion.width]; 1019 int[] destOff = new int[destinationRegion.width]; 1020 int[] srcPos = new int[destinationRegion.width]; 1021 int[] destPos = new int[destinationRegion.width]; 1022 1023 for (int i = destinationRegion.x, x = sourceRegion.x, j = 0; 1024 i < destinationRegion.x + destinationRegion.width; 1025 i++, j++, x += scaleX) { 1026 srcPos[j] = x >> 3; 1027 srcOff[j] = 7 - (x & 7); 1028 destPos[j] = i >> 3; 1029 destOff[j] = 7 - (i & 7); 1030 } 1031 1032 int k = destinationRegion.y * lineStride; 1033 if (isBottomUp) 1034 k += (destinationRegion.height - 1) * lineStride; 1035 1036 for (int j = 0, y = sourceRegion.y; 1037 j < destinationRegion.height; j++, y+=scaleY) { 1038 1039 if (abortRequested()) 1040 break; 1041 iis.read(buf, 0, lineLength); 1042 for (int i = 0; i < destinationRegion.width; i++) { 1043 //get the bit and assign to the data buffer of the raster 1044 int v = (buf[srcPos[i]] >> srcOff[i]) & 1; 1045 bdata[k + destPos[i]] |= v << destOff[i]; 1046 } 1047 1048 k += isBottomUp ? -lineStride : lineStride; 1049 iis.skipBytes(skipLength); 1050 processImageUpdate(bi, 0, j, 1051 destinationRegion.width, 1, 1, 1, 1052 new int[]{0}); 1053 processImageProgress(100.0F*j/destinationRegion.height); 1054 } 1055 } 1056 } 1057 1058 // Method to read a 4 bit BMP image data 1059 private void read4Bit(byte[] bdata) throws IOException { 1060 1061 int bytesPerScanline = (width + 1) / 2; 1062 1063 // Padding bytes at the end of each scanline 1064 int padding = bytesPerScanline % 4; 1065 if (padding != 0) 1066 padding = 4 - padding; 1067 1068 int lineLength = bytesPerScanline + padding; 1069 1070 if (noTransform) { 1071 int j = isBottomUp ? (height -1) * bytesPerScanline : 0; 1072 1073 for (int i=0; i<height; i++) { 1074 if (abortRequested()) { 1075 break; 1076 } 1077 iis.readFully(bdata, j, bytesPerScanline); 1078 iis.skipBytes(padding); 1079 j += isBottomUp ? -bytesPerScanline : bytesPerScanline; 1080 processImageUpdate(bi, 0, i, 1081 destinationRegion.width, 1, 1, 1, 1082 new int[]{0}); 1083 processImageProgress(100.0F * i/destinationRegion.height); 1084 } 1085 } else { 1086 byte[] buf = new byte[lineLength]; 1087 int lineStride = 1088 ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride(); 1089 1090 if (isBottomUp) { 1091 int lastLine = 1092 sourceRegion.y + (destinationRegion.height - 1) * scaleY; 1093 iis.skipBytes(lineLength * (height - 1 - lastLine)); 1094 } else 1095 iis.skipBytes(lineLength * sourceRegion.y); 1096 1097 int skipLength = lineLength * (scaleY - 1); 1098 1099 // cache the values to avoid duplicated computation 1100 int[] srcOff = new int[destinationRegion.width]; 1101 int[] destOff = new int[destinationRegion.width]; 1102 int[] srcPos = new int[destinationRegion.width]; 1103 int[] destPos = new int[destinationRegion.width]; 1104 1105 for (int i = destinationRegion.x, x = sourceRegion.x, j = 0; 1106 i < destinationRegion.x + destinationRegion.width; 1107 i++, j++, x += scaleX) { 1108 srcPos[j] = x >> 1; 1109 srcOff[j] = (1 - (x & 1)) << 2; 1110 destPos[j] = i >> 1; 1111 destOff[j] = (1 - (i & 1)) << 2; 1112 } 1113 1114 int k = destinationRegion.y * lineStride; 1115 if (isBottomUp) 1116 k += (destinationRegion.height - 1) * lineStride; 1117 1118 for (int j = 0, y = sourceRegion.y; 1119 j < destinationRegion.height; j++, y+=scaleY) { 1120 1121 if (abortRequested()) 1122 break; 1123 iis.read(buf, 0, lineLength); 1124 for (int i = 0; i < destinationRegion.width; i++) { 1125 //get the bit and assign to the data buffer of the raster 1126 int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F; 1127 bdata[k + destPos[i]] |= v << destOff[i]; 1128 } 1129 1130 k += isBottomUp ? -lineStride : lineStride; 1131 iis.skipBytes(skipLength); 1132 processImageUpdate(bi, 0, j, 1133 destinationRegion.width, 1, 1, 1, 1134 new int[]{0}); 1135 processImageProgress(100.0F*j/destinationRegion.height); 1136 } 1137 } 1138 } 1139 1140 // Method to read 8 bit BMP image data 1141 private void read8Bit(byte[] bdata) throws IOException { 1142 1143 // Padding bytes at the end of each scanline 1144 int padding = width % 4; 1145 if (padding != 0) { 1146 padding = 4 - padding; 1147 } 1148 1149 int lineLength = width + padding; 1150 1151 if (noTransform) { 1152 int j = isBottomUp ? (height -1) * width : 0; 1153 1154 for (int i=0; i<height; i++) { 1155 if (abortRequested()) { 1156 break; 1157 } 1158 iis.readFully(bdata, j, width); 1159 iis.skipBytes(padding); 1160 j += isBottomUp ? -width : width; 1161 processImageUpdate(bi, 0, i, 1162 destinationRegion.width, 1, 1, 1, 1163 new int[]{0}); 1164 processImageProgress(100.0F * i/destinationRegion.height); 1165 } 1166 } else { 1167 byte[] buf = new byte[lineLength]; 1168 int lineStride = 1169 ((ComponentSampleModel)sampleModel).getScanlineStride(); 1170 1171 if (isBottomUp) { 1172 int lastLine = 1173 sourceRegion.y + (destinationRegion.height - 1) * scaleY; 1174 iis.skipBytes(lineLength * (height - 1 - lastLine)); 1175 } else 1176 iis.skipBytes(lineLength * sourceRegion.y); 1177 1178 int skipLength = lineLength * (scaleY - 1); 1179 1180 int k = destinationRegion.y * lineStride; 1181 if (isBottomUp) 1182 k += (destinationRegion.height - 1) * lineStride; 1183 k += destinationRegion.x; 1184 1185 for (int j = 0, y = sourceRegion.y; 1186 j < destinationRegion.height; j++, y+=scaleY) { 1187 1188 if (abortRequested()) 1189 break; 1190 iis.read(buf, 0, lineLength); 1191 for (int i = 0, m = sourceRegion.x; 1192 i < destinationRegion.width; i++, m += scaleX) { 1193 //get the bit and assign to the data buffer of the raster 1194 bdata[k + i] = buf[m]; 1195 } 1196 1197 k += isBottomUp ? -lineStride : lineStride; 1198 iis.skipBytes(skipLength); 1199 processImageUpdate(bi, 0, j, 1200 destinationRegion.width, 1, 1, 1, 1201 new int[]{0}); 1202 processImageProgress(100.0F*j/destinationRegion.height); 1203 } 1204 } 1205 } 1206 1207 // Method to read 24 bit BMP image data 1208 private void read24Bit(byte[] bdata) throws IOException { 1209 // Padding bytes at the end of each scanline 1210 // width * bitsPerPixel should be divisible by 32 1211 int padding = width * 3 % 4; 1212 if ( padding != 0) 1213 padding = 4 - padding; 1214 1215 int lineStride = width * 3; 1216 int lineLength = lineStride + padding; 1217 1218 if (noTransform) { 1219 int j = isBottomUp ? (height -1) * width * 3 : 0; 1220 1221 for (int i=0; i<height; i++) { 1222 if (abortRequested()) { 1223 break; 1224 } 1225 iis.readFully(bdata, j, lineStride); 1226 iis.skipBytes(padding); 1227 j += isBottomUp ? -lineStride : lineStride; 1228 processImageUpdate(bi, 0, i, 1229 destinationRegion.width, 1, 1, 1, 1230 new int[]{0}); 1231 processImageProgress(100.0F * i/destinationRegion.height); 1232 } 1233 } else { 1234 byte[] buf = new byte[lineLength]; 1235 lineStride = 1236 ((ComponentSampleModel)sampleModel).getScanlineStride(); 1237 1238 if (isBottomUp) { 1239 int lastLine = 1240 sourceRegion.y + (destinationRegion.height - 1) * scaleY; 1241 iis.skipBytes(lineLength * (height - 1 - lastLine)); 1242 } else 1243 iis.skipBytes(lineLength * sourceRegion.y); 1244 1245 int skipLength = lineLength * (scaleY - 1); 1246 1247 int k = destinationRegion.y * lineStride; 1248 if (isBottomUp) 1249 k += (destinationRegion.height - 1) * lineStride; 1250 k += destinationRegion.x * 3; 1251 1252 for (int j = 0, y = sourceRegion.y; 1253 j < destinationRegion.height; j++, y+=scaleY) { 1254 1255 if (abortRequested()) 1256 break; 1257 iis.read(buf, 0, lineLength); 1258 for (int i = 0, m = 3 * sourceRegion.x; 1259 i < destinationRegion.width; i++, m += 3 * scaleX) { 1260 //get the bit and assign to the data buffer of the raster 1261 int n = 3 * i + k; 1262 for (int b = 0; b < destBands.length; b++) 1263 bdata[n + destBands[b]] = buf[m + sourceBands[b]]; 1264 } 1265 1266 k += isBottomUp ? -lineStride : lineStride; 1267 iis.skipBytes(skipLength); 1268 processImageUpdate(bi, 0, j, 1269 destinationRegion.width, 1, 1, 1, 1270 new int[]{0}); 1271 processImageProgress(100.0F*j/destinationRegion.height); 1272 } 1273 } 1274 } 1275 1276 private void read16Bit(short sdata[]) throws IOException { 1277 // Padding bytes at the end of each scanline 1278 // width * bitsPerPixel should be divisible by 32 1279 int padding = width * 2 % 4; 1280 1281 if ( padding != 0) 1282 padding = 4 - padding; 1283 1284 int lineLength = width + padding / 2; 1285 1286 if (noTransform) { 1287 int j = isBottomUp ? (height -1) * width : 0; 1288 for (int i=0; i<height; i++) { 1289 if (abortRequested()) { 1290 break; 1291 } 1292 1293 iis.readFully(sdata, j, width); 1294 iis.skipBytes(padding); 1295 1296 j += isBottomUp ? -width : width; 1297 processImageUpdate(bi, 0, i, 1298 destinationRegion.width, 1, 1, 1, 1299 new int[]{0}); 1300 processImageProgress(100.0F * i/destinationRegion.height); 1301 } 1302 } else { 1303 short[] buf = new short[lineLength]; 1304 int lineStride = 1305 ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride(); 1306 1307 if (isBottomUp) { 1308 int lastLine = 1309 sourceRegion.y + (destinationRegion.height - 1) * scaleY; 1310 iis.skipBytes(lineLength * (height - 1 - lastLine) << 1); 1311 } else 1312 iis.skipBytes(lineLength * sourceRegion.y << 1); 1313 1314 int skipLength = lineLength * (scaleY - 1) << 1; 1315 1316 int k = destinationRegion.y * lineStride; 1317 if (isBottomUp) 1318 k += (destinationRegion.height - 1) * lineStride; 1319 k += destinationRegion.x; 1320 1321 for (int j = 0, y = sourceRegion.y; 1322 j < destinationRegion.height; j++, y+=scaleY) { 1323 1324 if (abortRequested()) 1325 break; 1326 iis.readFully(buf, 0, lineLength); 1327 for (int i = 0, m = sourceRegion.x; 1328 i < destinationRegion.width; i++, m += scaleX) { 1329 //get the bit and assign to the data buffer of the raster 1330 sdata[k + i] = buf[m]; 1331 } 1332 1333 k += isBottomUp ? -lineStride : lineStride; 1334 iis.skipBytes(skipLength); 1335 processImageUpdate(bi, 0, j, 1336 destinationRegion.width, 1, 1, 1, 1337 new int[]{0}); 1338 processImageProgress(100.0F*j/destinationRegion.height); 1339 } 1340 } 1341 } 1342 1343 private void read32Bit(int idata[]) throws IOException { 1344 if (noTransform) { 1345 int j = isBottomUp ? (height -1) * width : 0; 1346 1347 for (int i=0; i<height; i++) { 1348 if (abortRequested()) { 1349 break; 1350 } 1351 iis.readFully(idata, j, width); 1352 j += isBottomUp ? -width : width; 1353 processImageUpdate(bi, 0, i, 1354 destinationRegion.width, 1, 1, 1, 1355 new int[]{0}); 1356 processImageProgress(100.0F * i/destinationRegion.height); 1357 } 1358 } else { 1359 int[] buf = new int[width]; 1360 int lineStride = 1361 ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride(); 1362 1363 if (isBottomUp) { 1364 int lastLine = 1365 sourceRegion.y + (destinationRegion.height - 1) * scaleY; 1366 iis.skipBytes(width * (height - 1 - lastLine) << 2); 1367 } else 1368 iis.skipBytes(width * sourceRegion.y << 2); 1369 1370 int skipLength = width * (scaleY - 1) << 2; 1371 1372 int k = destinationRegion.y * lineStride; 1373 if (isBottomUp) 1374 k += (destinationRegion.height - 1) * lineStride; 1375 k += destinationRegion.x; 1376 1377 for (int j = 0, y = sourceRegion.y; 1378 j < destinationRegion.height; j++, y+=scaleY) { 1379 1380 if (abortRequested()) 1381 break; 1382 iis.readFully(buf, 0, width); 1383 for (int i = 0, m = sourceRegion.x; 1384 i < destinationRegion.width; i++, m += scaleX) { 1385 //get the bit and assign to the data buffer of the raster 1386 idata[k + i] = buf[m]; 1387 } 1388 1389 k += isBottomUp ? -lineStride : lineStride; 1390 iis.skipBytes(skipLength); 1391 processImageUpdate(bi, 0, j, 1392 destinationRegion.width, 1, 1, 1, 1393 new int[]{0}); 1394 processImageProgress(100.0F*j/destinationRegion.height); 1395 } 1396 } 1397 } 1398 1399 private void readRLE8(byte bdata[]) throws IOException { 1400 // If imageSize field is not provided, calculate it. 1401 int imSize = (int)imageSize; 1402 if (imSize == 0) { 1403 imSize = (int)(bitmapFileSize - bitmapOffset); 1404 } 1405 1406 int padding = 0; 1407 // If width is not 32 bit aligned, then while uncompressing each 1408 // scanline will have padding bytes, calculate the amount of padding 1409 int remainder = width % 4; 1410 if (remainder != 0) { 1411 padding = 4 - remainder; 1412 } 1413 1414 // Read till we have the whole image 1415 byte values[] = new byte[imSize]; 1416 int bytesRead = 0; 1417 iis.readFully(values, 0, imSize); 1418 1419 // Since data is compressed, decompress it 1420 decodeRLE8(imSize, padding, values, bdata); 1421 } 1422 1423 private void decodeRLE8(int imSize, 1424 int padding, 1425 byte[] values, 1426 byte[] bdata) throws IOException { 1427 1428 byte val[] = new byte[width * height]; 1429 int count = 0, l = 0; 1430 int value; 1431 boolean flag = false; 1432 int lineNo = isBottomUp ? height - 1 : 0; 1433 int lineStride = 1434 ((ComponentSampleModel)sampleModel).getScanlineStride(); 1435 int finished = 0; 1436 1437 while (count != imSize) { 1438 value = values[count++] & 0xff; 1439 if (value == 0) { 1440 switch(values[count++] & 0xff) { 1441 1442 case 0: 1443 // End-of-scanline marker 1444 if (lineNo >= sourceRegion.y && 1445 lineNo < sourceRegion.y + sourceRegion.height) { 1446 if (noTransform) { 1447 int pos = lineNo * width; 1448 for(int i = 0; i < width; i++) 1449 bdata[pos++] = val[i]; 1450 processImageUpdate(bi, 0, lineNo, 1451 destinationRegion.width, 1, 1, 1, 1452 new int[]{0}); 1453 finished++; 1454 } else if ((lineNo - sourceRegion.y) % scaleY == 0) { 1455 int currentLine = (lineNo - sourceRegion.y) / scaleY + 1456 destinationRegion.y; 1457 int pos = currentLine * lineStride; 1458 pos += destinationRegion.x; 1459 for (int i = sourceRegion.x; 1460 i < sourceRegion.x + sourceRegion.width; 1461 i += scaleX) 1462 bdata[pos++] = val[i]; 1463 processImageUpdate(bi, 0, currentLine, 1464 destinationRegion.width, 1, 1, 1, 1465 new int[]{0}); 1466 finished++; 1467 } 1468 } 1469 processImageProgress(100.0F * finished / destinationRegion.height); 1470 lineNo += isBottomUp ? -1 : 1; 1471 l = 0; 1472 1473 if (abortRequested()) { 1474 flag = true; 1475 } 1476 1477 break; 1478 1479 case 1: 1480 // End-of-RLE marker 1481 flag = true; 1482 break; 1483 1484 case 2: 1485 // delta or vector marker 1486 int xoff = values[count++] & 0xff; 1487 int yoff = values[count] & 0xff; 1488 // Move to the position xoff, yoff down 1489 l += xoff + yoff*width; 1490 break; 1491 1492 default: 1493 int end = values[count-1] & 0xff; 1494 for (int i=0; i<end; i++) { 1495 val[l++] = (byte)(values[count++] & 0xff); 1496 } 1497 1498 // Whenever end pixels can fit into odd number of bytes, 1499 // an extra padding byte will be present, so skip that. 1500 if ((end & 1) == 1) { 1501 count++; 1502 } 1503 } 1504 } else { 1505 for (int i=0; i<value; i++) { 1506 val[l++] = (byte)(values[count] & 0xff); 1507 } 1508 1509 count++; 1510 } 1511 1512 // If End-of-RLE data, then exit the while loop 1513 if (flag) { 1514 break; 1515 } 1516 } 1517 } 1518 1519 private void readRLE4(byte[] bdata) throws IOException { 1520 1521 // If imageSize field is not specified, calculate it. 1522 int imSize = (int)imageSize; 1523 if (imSize == 0) { 1524 imSize = (int)(bitmapFileSize - bitmapOffset); 1525 } 1526 1527 int padding = 0; 1528 // If width is not 32 byte aligned, then while uncompressing each 1529 // scanline will have padding bytes, calculate the amount of padding 1530 int remainder = width % 4; 1531 if (remainder != 0) { 1532 padding = 4 - remainder; 1533 } 1534 1535 // Read till we have the whole image 1536 byte[] values = new byte[imSize]; 1537 iis.readFully(values, 0, imSize); 1538 1539 // Decompress the RLE4 compressed data. 1540 decodeRLE4(imSize, padding, values, bdata); 1541 } 1542 1543 private void decodeRLE4(int imSize, 1544 int padding, 1545 byte[] values, 1546 byte[] bdata) throws IOException { 1547 byte[] val = new byte[width]; 1548 int count = 0, l = 0; 1549 int value; 1550 boolean flag = false; 1551 int lineNo = isBottomUp ? height - 1 : 0; 1552 int lineStride = 1553 ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride(); 1554 int finished = 0; 1555 1556 while (count != imSize) { 1557 1558 value = values[count++] & 0xFF; 1559 if (value == 0) { 1560 1561 1562 // Absolute mode 1563 switch(values[count++] & 0xFF) { 1564 1565 case 0: 1566 // End-of-scanline marker 1567 // End-of-scanline marker 1568 if (lineNo >= sourceRegion.y && 1569 lineNo < sourceRegion.y + sourceRegion.height) { 1570 if (noTransform) { 1571 int pos = lineNo * (width + 1 >> 1); 1572 for(int i = 0, j = 0; i < width >> 1; i++) 1573 bdata[pos++] = 1574 (byte)((val[j++] << 4) | val[j++]); 1575 if ((width & 1) == 1) 1576 bdata[pos] |= val[width - 1] << 4; 1577 1578 processImageUpdate(bi, 0, lineNo, 1579 destinationRegion.width, 1, 1, 1, 1580 new int[]{0}); 1581 finished++; 1582 } else if ((lineNo - sourceRegion.y) % scaleY == 0) { 1583 int currentLine = (lineNo - sourceRegion.y) / scaleY + 1584 destinationRegion.y; 1585 int pos = currentLine * lineStride; 1586 pos += destinationRegion.x >> 1; 1587 int shift = (1 - (destinationRegion.x & 1)) << 2; 1588 for (int i = sourceRegion.x; 1589 i < sourceRegion.x + sourceRegion.width; 1590 i += scaleX) { 1591 bdata[pos] |= val[i] << shift; 1592 shift += 4; 1593 if (shift == 4) { 1594 pos++; 1595 } 1596 shift &= 7; 1597 } 1598 processImageUpdate(bi, 0, currentLine, 1599 destinationRegion.width, 1, 1, 1, 1600 new int[]{0}); 1601 finished++; 1602 } 1603 } 1604 processImageProgress(100.0F * finished / destinationRegion.height); 1605 lineNo += isBottomUp ? -1 : 1; 1606 l = 0; 1607 1608 if (abortRequested()) { 1609 flag = true; 1610 } 1611 1612 break; 1613 1614 case 1: 1615 // End-of-RLE marker 1616 flag = true; 1617 break; 1618 1619 case 2: 1620 // delta or vector marker 1621 int xoff = values[count++] & 0xFF; 1622 int yoff = values[count] & 0xFF; 1623 // Move to the position xoff, yoff down 1624 l += xoff + yoff*width; 1625 break; 1626 1627 default: 1628 int end = values[count-1] & 0xFF; 1629 for (int i=0; i<end; i++) { 1630 val[l++] = (byte)(((i & 1) == 0) ? (values[count] & 0xf0) >> 4 1631 : (values[count++] & 0x0f)); 1632 } 1633 1634 // When end is odd, the above for loop does not 1635 // increment count, so do it now. 1636 if ((end & 1) == 1) { 1637 count++; 1638 } 1639 1640 // Whenever end pixels can fit into odd number of bytes, 1641 // an extra padding byte will be present, so skip that. 1642 if ((((int)Math.ceil(end/2)) & 1) ==1 ) { 1643 count++; 1644 } 1645 break; 1646 } 1647 } else { 1648 // Encoded mode 1649 int alternate[] = { (values[count] & 0xf0) >> 4, 1650 values[count] & 0x0f }; 1651 for (int i=0; (i < value) && (l < width); i++) { 1652 val[l++] = (byte)alternate[i & 1]; 1653 } 1654 1655 count++; 1656 } 1657 1658 // If End-of-RLE data, then exit the while loop 1659 if (flag) { 1660 break; 1661 } 1662 } 1663 } 1664 1665 /** Decodes the jpeg/png image embedded in the bitmap using any jpeg 1666 * ImageIO-style plugin. 1667 * 1668 * @param bi The destination <code>BufferedImage</code>. 1669 * @param bmpParam The <code>ImageReadParam</code> for decoding this 1670 * BMP image. The parameters for subregion, band selection and 1671 * subsampling are used in decoding the jpeg image. 1672 */ 1673 1674 private BufferedImage readEmbedded(int type, 1675 BufferedImage bi, ImageReadParam bmpParam) 1676 throws IOException { 1677 String format; 1678 switch(type) { 1679 case BI_JPEG: 1680 format = "JPEG"; 1681 break; 1682 case BI_PNG: 1683 format = "PNG"; 1684 break; 1685 default: 1686 throw new 1687 IOException("Unexpected compression type: " + type); 1688 } 1689 ImageReader reader = 1690 ImageIO.getImageReadersByFormatName(format).next(); 1691 if (reader == null) { 1692 throw new RuntimeException(I18N.getString("BMPImageReader4") + 1693 " " + format); 1694 } 1695 // prepare input 1696 byte[] buff = new byte[(int)imageSize]; 1697 iis.read(buff); 1698 reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(buff))); 1699 if (bi == null) { 1700 ImageTypeSpecifier embType = reader.getImageTypes(0).next(); 1701 bi = embType.createBufferedImage(destinationRegion.x + 1702 destinationRegion.width, 1703 destinationRegion.y + 1704 destinationRegion.height); 1705 } 1706 1707 reader.addIIOReadProgressListener(new EmbeddedProgressAdapter() { 1708 public void imageProgress(ImageReader source, 1709 float percentageDone) 1710 { 1711 processImageProgress(percentageDone); 1712 } 1713 }); 1714 1715 reader.addIIOReadUpdateListener(new IIOReadUpdateListener() { 1716 public void imageUpdate(ImageReader source, 1717 BufferedImage theImage, 1718 int minX, int minY, 1719 int width, int height, 1720 int periodX, int periodY, 1721 int[] bands) 1722 { 1723 processImageUpdate(theImage, minX, minY, 1724 width, height, 1725 periodX, periodY, bands); 1726 } 1727 public void passComplete(ImageReader source, 1728 BufferedImage theImage) 1729 { 1730 processPassComplete(theImage); 1731 } 1732 public void passStarted(ImageReader source, 1733 BufferedImage theImage, 1734 int pass, 1735 int minPass, int maxPass, 1736 int minX, int minY, 1737 int periodX, int periodY, 1738 int[] bands) 1739 { 1740 processPassStarted(theImage, pass, minPass, maxPass, 1741 minX, minY, periodX, periodY, 1742 bands); 1743 } 1744 public void thumbnailPassComplete(ImageReader source, 1745 BufferedImage thumb) {} 1746 public void thumbnailPassStarted(ImageReader source, 1747 BufferedImage thumb, 1748 int pass, 1749 int minPass, int maxPass, 1750 int minX, int minY, 1751 int periodX, int periodY, 1752 int[] bands) {} 1753 public void thumbnailUpdate(ImageReader source, 1754 BufferedImage theThumbnail, 1755 int minX, int minY, 1756 int width, int height, 1757 int periodX, int periodY, 1758 int[] bands) {} 1759 }); 1760 1761 reader.addIIOReadWarningListener(new IIOReadWarningListener() { 1762 public void warningOccurred(ImageReader source, String warning) 1763 { 1764 processWarningOccurred(warning); 1765 } 1766 }); 1767 1768 ImageReadParam param = reader.getDefaultReadParam(); 1769 param.setDestination(bi); 1770 param.setDestinationBands(bmpParam.getDestinationBands()); 1771 param.setDestinationOffset(bmpParam.getDestinationOffset()); 1772 param.setSourceBands(bmpParam.getSourceBands()); 1773 param.setSourceRegion(bmpParam.getSourceRegion()); 1774 param.setSourceSubsampling(bmpParam.getSourceXSubsampling(), 1775 bmpParam.getSourceYSubsampling(), 1776 bmpParam.getSubsamplingXOffset(), 1777 bmpParam.getSubsamplingYOffset()); 1778 reader.read(0, param); 1779 return bi; 1780 } 1781 1782 private class EmbeddedProgressAdapter implements IIOReadProgressListener { 1783 public void imageComplete(ImageReader src) {} 1784 public void imageProgress(ImageReader src, float percentageDone) {} 1785 public void imageStarted(ImageReader src, int imageIndex) {} 1786 public void thumbnailComplete(ImageReader src) {} 1787 public void thumbnailProgress(ImageReader src, float percentageDone) {} 1788 public void thumbnailStarted(ImageReader src, int iIdx, int tIdx) {} 1789 public void sequenceComplete(ImageReader src) {} 1790 public void sequenceStarted(ImageReader src, int minIndex) {} 1791 public void readAborted(ImageReader src) {} 1792 } 1793 1794 private static Boolean isLinkedProfileDisabled = null; 1795 1796 private static boolean isLinkedProfileAllowed() { 1797 if (isLinkedProfileDisabled == null) { 1798 PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() { 1799 public Boolean run() { 1800 return Boolean.getBoolean("sun.imageio.plugins.bmp.disableLinkedProfiles"); 1801 } 1802 }; 1803 isLinkedProfileDisabled = AccessController.doPrivileged(a); 1804 } 1805 return !isLinkedProfileDisabled; 1806 } 1807 1808 private static Boolean isWindowsPlatform = null; 1809 1810 /** 1811 * Verifies whether the byte array contans a unc path. 1812 * Non-UNC path examples: 1813 * c:\path\to\file - simple notation 1814 * \\?\c:\path\to\file - long notation 1815 * 1816 * UNC path examples: 1817 * \\server\share - a UNC path in simple notation 1818 * \\?\UNC\server\share - a UNC path in long notation 1819 * \\.\some\device - a path to device. 1820 */ 1821 private static boolean isUncOrDevicePath(byte[] p) { 1822 if (isWindowsPlatform == null) { 1823 PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() { 1824 public Boolean run() { 1825 String osname = System.getProperty("os.name"); 1826 return (osname != null && 1827 osname.toLowerCase().startsWith("win")); 1828 } 1829 }; 1830 isWindowsPlatform = AccessController.doPrivileged(a); 1831 } 1832 1833 if (!isWindowsPlatform) { 1834 /* no need for the check on platforms except windows */ 1835 return false; 1836 } 1837 1838 /* normalize prefix of the path */ 1839 if (p[0] == '/') p[0] = '\\'; 1840 if (p[1] == '/') p[1] = '\\'; 1841 if (p[3] == '/') p[3] = '\\'; 1842 1843 1844 if ((p[0] == '\\') && (p[1] == '\\')) { 1845 if ((p[2] == '?') && (p[3] == '\\')) { 1846 // long path: whether unc or local 1847 return ((p[4] == 'U' || p[4] == 'u') && 1848 (p[5] == 'N' || p[5] == 'n') && 1849 (p[6] == 'C' || p[6] == 'c')); 1850 } else { 1851 // device path or short unc notation 1852 return true; 1853 } 1854 } else { 1855 return false; 1856 } 1857 } 1858 }