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