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