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