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