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