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