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