1 /* 2 * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.imageio.plugins.gif; 27 28 import java.awt.Point; 29 import java.awt.Rectangle; 30 import java.awt.image.BufferedImage; 31 import java.awt.image.DataBuffer; 32 import java.awt.image.WritableRaster; 33 import java.io.EOFException; 34 import java.io.IOException; 35 import java.nio.ByteOrder; 36 import java.util.ArrayList; 37 import java.util.Iterator; 38 import java.util.List; 39 import javax.imageio.IIOException; 40 import javax.imageio.ImageReader; 41 import javax.imageio.ImageReadParam; 42 import javax.imageio.ImageTypeSpecifier; 43 import javax.imageio.metadata.IIOMetadata; 44 import javax.imageio.spi.ImageReaderSpi; 45 import javax.imageio.stream.ImageInputStream; 46 import com.sun.imageio.plugins.common.ReaderUtil; 47 import java.awt.image.ColorModel; 48 import java.awt.image.IndexColorModel; 49 import java.awt.image.MultiPixelPackedSampleModel; 50 import java.awt.image.PixelInterleavedSampleModel; 51 import java.awt.image.SampleModel; 52 53 public class GIFImageReader extends ImageReader { 54 55 // The current ImageInputStream source. 56 ImageInputStream stream = null; 57 58 // Per-stream settings 59 60 // True if the file header including stream metadata has been read. 61 boolean gotHeader = false; 62 63 // Global metadata, read once per input setting. 64 GIFStreamMetadata streamMetadata = null; 65 66 // The current image index 67 int currIndex = -1; 68 69 // Metadata for image at 'currIndex', or null. 70 GIFImageMetadata imageMetadata = null; 71 72 // A List of Longs indicating the stream positions of the 73 // start of the metadata for each image. Entries are added 74 // as needed. 75 List imageStartPosition = new ArrayList(); 76 77 // Length of metadata for image at 'currIndex', valid only if 78 // imageMetadata != null. 79 int imageMetadataLength; 80 81 // The number of images in the stream, if known, otherwise -1. 82 int numImages = -1; 83 84 // Variables used by the LZW decoding process 85 byte[] block = new byte[255]; 86 int blockLength = 0; 87 int bitPos = 0; 88 int nextByte = 0; 89 int initCodeSize; 90 int clearCode; 91 int eofCode; 92 93 // 32-bit lookahead buffer 94 int next32Bits = 0; 95 96 // Try if the end of the data blocks has been found, 97 // and we are simply draining the 32-bit buffer 98 boolean lastBlockFound = false; 99 100 // The image to be written. 101 BufferedImage theImage = null; 102 103 // The image's tile. 104 WritableRaster theTile = null; 105 106 // The image dimensions (from the stream). 107 int width = -1, height = -1; 108 109 // The pixel currently being decoded (in the stream's coordinates). 110 int streamX = -1, streamY = -1; 111 112 // The number of rows decoded 113 int rowsDone = 0; 114 115 // The current interlace pass, starting with 0. 116 int interlacePass = 0; 117 118 // End per-stream settings 119 120 // Constants used to control interlacing. 121 static final int[] interlaceIncrement = { 8, 8, 4, 2, -1 }; 122 static final int[] interlaceOffset = { 0, 4, 2, 1, -1 }; 123 124 public GIFImageReader(ImageReaderSpi originatingProvider) { 125 super(originatingProvider); 126 } 127 128 // Take input from an ImageInputStream 129 public void setInput(Object input, 130 boolean seekForwardOnly, 131 boolean ignoreMetadata) { 132 super.setInput(input, seekForwardOnly, ignoreMetadata); 133 if (input != null) { 134 if (!(input instanceof ImageInputStream)) { 135 throw new IllegalArgumentException 136 ("input not an ImageInputStream!"); 137 } 138 this.stream = (ImageInputStream)input; 139 } else { 140 this.stream = null; 141 } 142 143 // Clear all values based on the previous stream contents 144 resetStreamSettings(); 145 } 146 147 public int getNumImages(boolean allowSearch) throws IIOException { 148 if (stream == null) { 149 throw new IllegalStateException("Input not set!"); 150 } 151 if (seekForwardOnly && allowSearch) { 152 throw new IllegalStateException 153 ("seekForwardOnly and allowSearch can't both be true!"); 154 } 155 156 if (numImages > 0) { 157 return numImages; 158 } 159 if (allowSearch) { 160 this.numImages = locateImage(Integer.MAX_VALUE) + 1; 161 } 162 return numImages; 163 } 164 165 // Throw an IndexOutOfBoundsException if index < minIndex, 166 // and bump minIndex if required. 167 private void checkIndex(int imageIndex) { 168 if (imageIndex < minIndex) { 169 throw new IndexOutOfBoundsException("imageIndex < minIndex!"); 170 } 171 if (seekForwardOnly) { 172 minIndex = imageIndex; 173 } 174 } 175 176 public int getWidth(int imageIndex) throws IIOException { 177 checkIndex(imageIndex); 178 179 int index = locateImage(imageIndex); 180 if (index != imageIndex) { 181 throw new IndexOutOfBoundsException(); 182 } 183 readMetadata(); 184 return imageMetadata.imageWidth; 185 } 186 187 public int getHeight(int imageIndex) throws IIOException { 188 checkIndex(imageIndex); 189 190 int index = locateImage(imageIndex); 191 if (index != imageIndex) { 192 throw new IndexOutOfBoundsException(); 193 } 194 readMetadata(); 195 return imageMetadata.imageHeight; 196 } 197 198 // We don't check all parameters as ImageTypeSpecifier.createIndexed do 199 // since this method is private and we pass consistent data here 200 private ImageTypeSpecifier createIndexed(byte[] r, byte[] g, byte[] b, 201 int bits) { 202 ColorModel colorModel; 203 if (imageMetadata.transparentColorFlag) { 204 // Some files erroneously have a transparent color index 205 // of 255 even though there are fewer than 256 colors. 206 int idx = Math.min(imageMetadata.transparentColorIndex, 207 r.length - 1); 208 colorModel = new IndexColorModel(bits, r.length, r, g, b, idx); 209 } else { 210 colorModel = new IndexColorModel(bits, r.length, r, g, b); 211 } 212 213 SampleModel sampleModel; 214 if (bits == 8) { 215 int[] bandOffsets = {0}; 216 sampleModel = 217 new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, 218 1, 1, 1, 1, 219 bandOffsets); 220 } else { 221 sampleModel = 222 new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, 223 1, 1, bits); 224 } 225 return new ImageTypeSpecifier(colorModel, sampleModel); 226 } 227 228 public Iterator getImageTypes(int imageIndex) throws IIOException { 229 checkIndex(imageIndex); 230 231 int index = locateImage(imageIndex); 232 if (index != imageIndex) { 233 throw new IndexOutOfBoundsException(); 234 } 235 readMetadata(); 236 237 List l = new ArrayList(1); 238 239 byte[] colorTable; 240 if (imageMetadata.localColorTable != null) { 241 colorTable = imageMetadata.localColorTable; 242 } else { 243 colorTable = streamMetadata.globalColorTable; 244 } 245 246 // Normalize color table length to 2^1, 2^2, 2^4, or 2^8 247 int length = colorTable.length/3; 248 int bits; 249 if (length == 2) { 250 bits = 1; 251 } else if (length == 4) { 252 bits = 2; 253 } else if (length == 8 || length == 16) { 254 // Bump from 3 to 4 bits 255 bits = 4; 256 } else { 257 // Bump to 8 bits 258 bits = 8; 259 } 260 int lutLength = 1 << bits; 261 byte[] r = new byte[lutLength]; 262 byte[] g = new byte[lutLength]; 263 byte[] b = new byte[lutLength]; 264 265 // Entries from length + 1 to lutLength - 1 will be 0 266 int rgbIndex = 0; 267 for (int i = 0; i < length; i++) { 268 r[i] = colorTable[rgbIndex++]; 269 g[i] = colorTable[rgbIndex++]; 270 b[i] = colorTable[rgbIndex++]; 271 } 272 273 l.add(createIndexed(r, g, b, bits)); 274 return l.iterator(); 275 } 276 277 public ImageReadParam getDefaultReadParam() { 278 return new ImageReadParam(); 279 } 280 281 public IIOMetadata getStreamMetadata() throws IIOException { 282 readHeader(); 283 return streamMetadata; 284 } 285 286 public IIOMetadata getImageMetadata(int imageIndex) throws IIOException { 287 checkIndex(imageIndex); 288 289 int index = locateImage(imageIndex); 290 if (index != imageIndex) { 291 throw new IndexOutOfBoundsException("Bad image index!"); 292 } 293 readMetadata(); 294 return imageMetadata; 295 } 296 297 // BEGIN LZW STUFF 298 299 private void initNext32Bits() { 300 next32Bits = block[0] & 0xff; 301 next32Bits |= (block[1] & 0xff) << 8; 302 next32Bits |= (block[2] & 0xff) << 16; 303 next32Bits |= block[3] << 24; 304 nextByte = 4; 305 } 306 307 // Load a block (1-255 bytes) at a time, and maintain 308 // a 32-bit lookahead buffer that is filled from the left 309 // and extracted from the right. 310 // 311 // When the last block is found, we continue to 312 // 313 private int getCode(int codeSize, int codeMask) throws IOException { 314 if (bitPos + codeSize > 32) { 315 return eofCode; // No more data available 316 } 317 318 int code = (next32Bits >> bitPos) & codeMask; 319 bitPos += codeSize; 320 321 // Shift in a byte of new data at a time 322 while (bitPos >= 8 && !lastBlockFound) { 323 next32Bits >>>= 8; 324 bitPos -= 8; 325 326 // Check if current block is out of bytes 327 if (nextByte >= blockLength) { 328 // Get next block size 329 blockLength = stream.readUnsignedByte(); 330 if (blockLength == 0) { 331 lastBlockFound = true; 332 return code; 333 } else { 334 int left = blockLength; 335 int off = 0; 336 while (left > 0) { 337 int nbytes = stream.read(block, off, left); 338 off += nbytes; 339 left -= nbytes; 340 } 341 nextByte = 0; 342 } 343 } 344 345 next32Bits |= block[nextByte++] << 24; 346 } 347 348 return code; 349 } 350 351 public void initializeStringTable(int[] prefix, 352 byte[] suffix, 353 byte[] initial, 354 int[] length) { 355 int numEntries = 1 << initCodeSize; 356 for (int i = 0; i < numEntries; i++) { 357 prefix[i] = -1; 358 suffix[i] = (byte)i; 359 initial[i] = (byte)i; 360 length[i] = 1; 361 } 362 363 // Fill in the entire table for robustness against 364 // out-of-sequence codes. 365 for (int i = numEntries; i < 4096; i++) { 366 prefix[i] = -1; 367 length[i] = 1; 368 } 369 370 // tableIndex = numEntries + 2; 371 // codeSize = initCodeSize + 1; 372 // codeMask = (1 << codeSize) - 1; 373 } 374 375 Rectangle sourceRegion; 376 int sourceXSubsampling; 377 int sourceYSubsampling; 378 int sourceMinProgressivePass; 379 int sourceMaxProgressivePass; 380 381 Point destinationOffset; 382 Rectangle destinationRegion; 383 384 // Used only if IIOReadUpdateListeners are present 385 int updateMinY; 386 int updateYStep; 387 388 boolean decodeThisRow = true; 389 int destY = 0; 390 391 byte[] rowBuf; 392 393 private void outputRow() { 394 // Clip against ImageReadParam 395 int width = Math.min(sourceRegion.width, 396 destinationRegion.width*sourceXSubsampling); 397 int destX = destinationRegion.x; 398 399 if (sourceXSubsampling == 1) { 400 theTile.setDataElements(destX, destY, width, 1, rowBuf); 401 } else { 402 for (int x = 0; x < width; x += sourceXSubsampling, destX++) { 403 theTile.setSample(destX, destY, 0, rowBuf[x] & 0xff); 404 } 405 } 406 407 // Update IIOReadUpdateListeners, if any 408 if (updateListeners != null) { 409 int[] bands = { 0 }; 410 // updateYStep will have been initialized if 411 // updateListeners is non-null 412 processImageUpdate(theImage, 413 destX, destY, 414 width, 1, 1, updateYStep, 415 bands); 416 } 417 } 418 419 private void computeDecodeThisRow() { 420 this.decodeThisRow = 421 (destY < destinationRegion.y + destinationRegion.height) && 422 (streamY >= sourceRegion.y) && 423 (streamY < sourceRegion.y + sourceRegion.height) && 424 (((streamY - sourceRegion.y) % sourceYSubsampling) == 0); 425 } 426 427 private void outputPixels(byte[] string, int len) { 428 if (interlacePass < sourceMinProgressivePass || 429 interlacePass > sourceMaxProgressivePass) { 430 return; 431 } 432 433 for (int i = 0; i < len; i++) { 434 if (streamX >= sourceRegion.x) { 435 rowBuf[streamX - sourceRegion.x] = string[i]; 436 } 437 438 // Process end-of-row 439 ++streamX; 440 if (streamX == width) { 441 // Update IIOReadProgressListeners 442 ++rowsDone; 443 processImageProgress(100.0F*rowsDone/height); 444 445 if (decodeThisRow) { 446 outputRow(); 447 } 448 449 streamX = 0; 450 if (imageMetadata.interlaceFlag) { 451 streamY += interlaceIncrement[interlacePass]; 452 if (streamY >= height) { 453 // Inform IIOReadUpdateListeners of end of pass 454 if (updateListeners != null) { 455 processPassComplete(theImage); 456 } 457 458 ++interlacePass; 459 if (interlacePass > sourceMaxProgressivePass) { 460 return; 461 } 462 streamY = interlaceOffset[interlacePass]; 463 startPass(interlacePass); 464 } 465 } else { 466 ++streamY; 467 } 468 469 // Determine whether pixels from this row will 470 // be written to the destination 471 this.destY = destinationRegion.y + 472 (streamY - sourceRegion.y)/sourceYSubsampling; 473 computeDecodeThisRow(); 474 } 475 } 476 } 477 478 // END LZW STUFF 479 480 private void readHeader() throws IIOException { 481 if (gotHeader) { 482 return; 483 } 484 if (stream == null) { 485 throw new IllegalStateException("Input not set!"); 486 } 487 488 // Create an object to store the stream metadata 489 this.streamMetadata = new GIFStreamMetadata(); 490 491 try { 492 stream.setByteOrder(ByteOrder.LITTLE_ENDIAN); 493 494 byte[] signature = new byte[6]; 495 stream.readFully(signature); 496 497 StringBuffer version = new StringBuffer(3); 498 version.append((char)signature[3]); 499 version.append((char)signature[4]); 500 version.append((char)signature[5]); 501 streamMetadata.version = version.toString(); 502 503 streamMetadata.logicalScreenWidth = stream.readUnsignedShort(); 504 streamMetadata.logicalScreenHeight = stream.readUnsignedShort(); 505 506 int packedFields = stream.readUnsignedByte(); 507 boolean globalColorTableFlag = (packedFields & 0x80) != 0; 508 streamMetadata.colorResolution = ((packedFields >> 4) & 0x7) + 1; 509 streamMetadata.sortFlag = (packedFields & 0x8) != 0; 510 int numGCTEntries = 1 << ((packedFields & 0x7) + 1); 511 512 streamMetadata.backgroundColorIndex = stream.readUnsignedByte(); 513 streamMetadata.pixelAspectRatio = stream.readUnsignedByte(); 514 515 if (globalColorTableFlag) { 516 streamMetadata.globalColorTable = new byte[3*numGCTEntries]; 517 stream.readFully(streamMetadata.globalColorTable); 518 } else { 519 streamMetadata.globalColorTable = null; 520 } 521 522 // Found position of metadata for image 0 523 imageStartPosition.add(Long.valueOf(stream.getStreamPosition())); 524 } catch (IOException e) { 525 throw new IIOException("I/O error reading header!", e); 526 } 527 528 gotHeader = true; 529 } 530 531 private boolean skipImage() throws IIOException { 532 // Stream must be at the beginning of an image descriptor 533 // upon exit 534 535 try { 536 while (true) { 537 int blockType = stream.readUnsignedByte(); 538 539 if (blockType == 0x2c) { 540 stream.skipBytes(8); 541 542 int packedFields = stream.readUnsignedByte(); 543 if ((packedFields & 0x80) != 0) { 544 // Skip color table if any 545 int bits = (packedFields & 0x7) + 1; 546 stream.skipBytes(3*(1 << bits)); 547 } 548 549 stream.skipBytes(1); 550 551 int length = 0; 552 do { 553 length = stream.readUnsignedByte(); 554 stream.skipBytes(length); 555 } while (length > 0); 556 557 return true; 558 } else if (blockType == 0x3b) { 559 return false; 560 } else if (blockType == 0x21) { 561 int label = stream.readUnsignedByte(); 562 563 int length = 0; 564 do { 565 length = stream.readUnsignedByte(); 566 stream.skipBytes(length); 567 } while (length > 0); 568 } else if (blockType == 0x0) { 569 // EOF 570 return false; 571 } else { 572 int length = 0; 573 do { 574 length = stream.readUnsignedByte(); 575 stream.skipBytes(length); 576 } while (length > 0); 577 } 578 } 579 } catch (EOFException e) { 580 return false; 581 } catch (IOException e) { 582 throw new IIOException("I/O error locating image!", e); 583 } 584 } 585 586 private int locateImage(int imageIndex) throws IIOException { 587 readHeader(); 588 589 try { 590 // Find closest known index 591 int index = Math.min(imageIndex, imageStartPosition.size() - 1); 592 593 // Seek to that position 594 Long l = (Long)imageStartPosition.get(index); 595 stream.seek(l.longValue()); 596 597 // Skip images until at desired index or last image found 598 while (index < imageIndex) { 599 if (!skipImage()) { 600 --index; 601 return index; 602 } 603 604 Long l1 = new Long(stream.getStreamPosition()); 605 imageStartPosition.add(l1); 606 ++index; 607 } 608 } catch (IOException e) { 609 throw new IIOException("Couldn't seek!", e); 610 } 611 612 if (currIndex != imageIndex) { 613 imageMetadata = null; 614 } 615 currIndex = imageIndex; 616 return imageIndex; 617 } 618 619 // Read blocks of 1-255 bytes, stop at a 0-length block 620 private byte[] concatenateBlocks() throws IOException { 621 byte[] data = new byte[0]; 622 while (true) { 623 int length = stream.readUnsignedByte(); 624 if (length == 0) { 625 break; 626 } 627 byte[] newData = new byte[data.length + length]; 628 System.arraycopy(data, 0, newData, 0, data.length); 629 stream.readFully(newData, data.length, length); 630 data = newData; 631 } 632 633 return data; 634 } 635 636 // Stream must be positioned at start of metadata for 'currIndex' 637 private void readMetadata() throws IIOException { 638 if (stream == null) { 639 throw new IllegalStateException("Input not set!"); 640 } 641 642 try { 643 // Create an object to store the image metadata 644 this.imageMetadata = new GIFImageMetadata(); 645 646 long startPosition = stream.getStreamPosition(); 647 while (true) { 648 int blockType = stream.readUnsignedByte(); 649 if (blockType == 0x2c) { // Image Descriptor 650 imageMetadata.imageLeftPosition = 651 stream.readUnsignedShort(); 652 imageMetadata.imageTopPosition = 653 stream.readUnsignedShort(); 654 imageMetadata.imageWidth = stream.readUnsignedShort(); 655 imageMetadata.imageHeight = stream.readUnsignedShort(); 656 657 int idPackedFields = stream.readUnsignedByte(); 658 boolean localColorTableFlag = 659 (idPackedFields & 0x80) != 0; 660 imageMetadata.interlaceFlag = (idPackedFields & 0x40) != 0; 661 imageMetadata.sortFlag = (idPackedFields & 0x20) != 0; 662 int numLCTEntries = 1 << ((idPackedFields & 0x7) + 1); 663 664 if (localColorTableFlag) { 665 // Read color table if any 666 imageMetadata.localColorTable = 667 new byte[3*numLCTEntries]; 668 stream.readFully(imageMetadata.localColorTable); 669 } else { 670 imageMetadata.localColorTable = null; 671 } 672 673 // Record length of this metadata block 674 this.imageMetadataLength = 675 (int)(stream.getStreamPosition() - startPosition); 676 677 // Now positioned at start of LZW-compressed pixels 678 return; 679 } else if (blockType == 0x21) { // Extension block 680 int label = stream.readUnsignedByte(); 681 682 if (label == 0xf9) { // Graphics Control Extension 683 int gceLength = stream.readUnsignedByte(); // 4 684 int gcePackedFields = stream.readUnsignedByte(); 685 imageMetadata.disposalMethod = 686 (gcePackedFields >> 2) & 0x3; 687 imageMetadata.userInputFlag = 688 (gcePackedFields & 0x2) != 0; 689 imageMetadata.transparentColorFlag = 690 (gcePackedFields & 0x1) != 0; 691 692 imageMetadata.delayTime = stream.readUnsignedShort(); 693 imageMetadata.transparentColorIndex 694 = stream.readUnsignedByte(); 695 696 int terminator = stream.readUnsignedByte(); 697 } else if (label == 0x1) { // Plain text extension 698 int length = stream.readUnsignedByte(); 699 imageMetadata.hasPlainTextExtension = true; 700 imageMetadata.textGridLeft = 701 stream.readUnsignedShort(); 702 imageMetadata.textGridTop = 703 stream.readUnsignedShort(); 704 imageMetadata.textGridWidth = 705 stream.readUnsignedShort(); 706 imageMetadata.textGridHeight = 707 stream.readUnsignedShort(); 708 imageMetadata.characterCellWidth = 709 stream.readUnsignedByte(); 710 imageMetadata.characterCellHeight = 711 stream.readUnsignedByte(); 712 imageMetadata.textForegroundColor = 713 stream.readUnsignedByte(); 714 imageMetadata.textBackgroundColor = 715 stream.readUnsignedByte(); 716 imageMetadata.text = concatenateBlocks(); 717 } else if (label == 0xfe) { // Comment extension 718 byte[] comment = concatenateBlocks(); 719 if (imageMetadata.comments == null) { 720 imageMetadata.comments = new ArrayList(); 721 } 722 imageMetadata.comments.add(comment); 723 } else if (label == 0xff) { // Application extension 724 int blockSize = stream.readUnsignedByte(); 725 byte[] applicationID = new byte[8]; 726 byte[] authCode = new byte[3]; 727 728 // read available data 729 byte[] blockData = new byte[blockSize]; 730 stream.readFully(blockData); 731 732 int offset = copyData(blockData, 0, applicationID); 733 offset = copyData(blockData, offset, authCode); 734 735 byte[] applicationData = concatenateBlocks(); 736 737 if (offset < blockSize) { 738 int len = blockSize - offset; 739 byte[] data = 740 new byte[len + applicationData.length]; 741 742 System.arraycopy(blockData, offset, data, 0, len); 743 System.arraycopy(applicationData, 0, data, len, 744 applicationData.length); 745 746 applicationData = data; 747 } 748 749 // Init lists if necessary 750 if (imageMetadata.applicationIDs == null) { 751 imageMetadata.applicationIDs = new ArrayList(); 752 imageMetadata.authenticationCodes = 753 new ArrayList(); 754 imageMetadata.applicationData = new ArrayList(); 755 } 756 imageMetadata.applicationIDs.add(applicationID); 757 imageMetadata.authenticationCodes.add(authCode); 758 imageMetadata.applicationData.add(applicationData); 759 } else { 760 // Skip over unknown extension blocks 761 int length = 0; 762 do { 763 length = stream.readUnsignedByte(); 764 stream.skipBytes(length); 765 } while (length > 0); 766 } 767 } else if (blockType == 0x3b) { // Trailer 768 throw new IndexOutOfBoundsException 769 ("Attempt to read past end of image sequence!"); 770 } else { 771 throw new IIOException("Unexpected block type " + 772 blockType + "!"); 773 } 774 } 775 } catch (IIOException iioe) { 776 throw iioe; 777 } catch (IOException ioe) { 778 throw new IIOException("I/O error reading image metadata!", ioe); 779 } 780 } 781 782 private int copyData(byte[] src, int offset, byte[] dst) { 783 int len = dst.length; 784 int rest = src.length - offset; 785 if (len > rest) { 786 len = rest; 787 } 788 System.arraycopy(src, offset, dst, 0, len); 789 return offset + len; 790 } 791 792 private void startPass(int pass) { 793 if (updateListeners == null) { 794 return; 795 } 796 797 int y = 0; 798 int yStep = 1; 799 if (imageMetadata.interlaceFlag) { 800 y = interlaceOffset[interlacePass]; 801 yStep = interlaceIncrement[interlacePass]; 802 } 803 804 int[] vals = ReaderUtil. 805 computeUpdatedPixels(sourceRegion, 806 destinationOffset, 807 destinationRegion.x, 808 destinationRegion.y, 809 destinationRegion.x + 810 destinationRegion.width - 1, 811 destinationRegion.y + 812 destinationRegion.height - 1, 813 sourceXSubsampling, 814 sourceYSubsampling, 815 0, 816 y, 817 destinationRegion.width, 818 (destinationRegion.height + yStep - 1)/yStep, 819 1, 820 yStep); 821 822 // Initialized updateMinY and updateYStep 823 this.updateMinY = vals[1]; 824 this.updateYStep = vals[5]; 825 826 // Inform IIOReadUpdateListeners of new pass 827 int[] bands = { 0 }; 828 829 processPassStarted(theImage, 830 interlacePass, 831 sourceMinProgressivePass, 832 sourceMaxProgressivePass, 833 0, 834 updateMinY, 835 1, 836 updateYStep, 837 bands); 838 } 839 840 public BufferedImage read(int imageIndex, ImageReadParam param) 841 throws IIOException { 842 if (stream == null) { 843 throw new IllegalStateException("Input not set!"); 844 } 845 checkIndex(imageIndex); 846 847 int index = locateImage(imageIndex); 848 if (index != imageIndex) { 849 throw new IndexOutOfBoundsException("imageIndex out of bounds!"); 850 } 851 852 clearAbortRequest(); 853 readMetadata(); 854 855 // A null ImageReadParam means we use the default 856 if (param == null) { 857 param = getDefaultReadParam(); 858 } 859 860 // Initialize the destination image 861 Iterator imageTypes = getImageTypes(imageIndex); 862 this.theImage = getDestination(param, 863 imageTypes, 864 imageMetadata.imageWidth, 865 imageMetadata.imageHeight); 866 this.theTile = theImage.getWritableTile(0, 0); 867 this.width = imageMetadata.imageWidth; 868 this.height = imageMetadata.imageHeight; 869 this.streamX = 0; 870 this.streamY = 0; 871 this.rowsDone = 0; 872 this.interlacePass = 0; 873 874 // Get source region, taking subsampling offsets into account, 875 // and clipping against the true source bounds 876 877 this.sourceRegion = new Rectangle(0, 0, 0, 0); 878 this.destinationRegion = new Rectangle(0, 0, 0, 0); 879 computeRegions(param, width, height, theImage, 880 sourceRegion, destinationRegion); 881 this.destinationOffset = new Point(destinationRegion.x, 882 destinationRegion.y); 883 884 this.sourceXSubsampling = param.getSourceXSubsampling(); 885 this.sourceYSubsampling = param.getSourceYSubsampling(); 886 this.sourceMinProgressivePass = 887 Math.max(param.getSourceMinProgressivePass(), 0); 888 this.sourceMaxProgressivePass = 889 Math.min(param.getSourceMaxProgressivePass(), 3); 890 891 this.destY = destinationRegion.y + 892 (streamY - sourceRegion.y)/sourceYSubsampling; 893 computeDecodeThisRow(); 894 895 // Inform IIOReadProgressListeners of start of image 896 processImageStarted(imageIndex); 897 startPass(0); 898 899 this.rowBuf = new byte[width]; 900 901 try { 902 // Read and decode the image data, fill in theImage 903 this.initCodeSize = stream.readUnsignedByte(); 904 905 // Read first data block 906 this.blockLength = stream.readUnsignedByte(); 907 int left = blockLength; 908 int off = 0; 909 while (left > 0) { 910 int nbytes = stream.read(block, off, left); 911 left -= nbytes; 912 off += nbytes; 913 } 914 915 this.bitPos = 0; 916 this.nextByte = 0; 917 this.lastBlockFound = false; 918 this.interlacePass = 0; 919 920 // Init 32-bit buffer 921 initNext32Bits(); 922 923 this.clearCode = 1 << initCodeSize; 924 this.eofCode = clearCode + 1; 925 926 int code, oldCode = 0; 927 928 int[] prefix = new int[4096]; 929 byte[] suffix = new byte[4096]; 930 byte[] initial = new byte[4096]; 931 int[] length = new int[4096]; 932 byte[] string = new byte[4096]; 933 934 initializeStringTable(prefix, suffix, initial, length); 935 int tableIndex = (1 << initCodeSize) + 2; 936 int codeSize = initCodeSize + 1; 937 int codeMask = (1 << codeSize) - 1; 938 939 while (!abortRequested()) { 940 code = getCode(codeSize, codeMask); 941 942 if (code == clearCode) { 943 initializeStringTable(prefix, suffix, initial, length); 944 tableIndex = (1 << initCodeSize) + 2; 945 codeSize = initCodeSize + 1; 946 codeMask = (1 << codeSize) - 1; 947 948 code = getCode(codeSize, codeMask); 949 if (code == eofCode) { 950 // Inform IIOReadProgressListeners of end of image 951 processImageComplete(); 952 return theImage; 953 } 954 } else if (code == eofCode) { 955 // Inform IIOReadProgressListeners of end of image 956 processImageComplete(); 957 return theImage; 958 } else { 959 int newSuffixIndex; 960 if (code < tableIndex) { 961 newSuffixIndex = code; 962 } else { // code == tableIndex 963 newSuffixIndex = oldCode; 964 if (code != tableIndex) { 965 // warning - code out of sequence 966 // possibly data corruption 967 processWarningOccurred("Out-of-sequence code!"); 968 } 969 } 970 971 int ti = tableIndex; 972 int oc = oldCode; 973 974 prefix[ti] = oc; 975 suffix[ti] = initial[newSuffixIndex]; 976 initial[ti] = initial[oc]; 977 length[ti] = length[oc] + 1; 978 979 ++tableIndex; 980 if ((tableIndex == (1 << codeSize)) && 981 (tableIndex < 4096)) { 982 ++codeSize; 983 codeMask = (1 << codeSize) - 1; 984 } 985 } 986 987 // Reverse code 988 int c = code; 989 int len = length[c]; 990 for (int i = len - 1; i >= 0; i--) { 991 string[i] = suffix[c]; 992 c = prefix[c]; 993 } 994 995 outputPixels(string, len); 996 oldCode = code; 997 } 998 999 processReadAborted(); 1000 return theImage; 1001 } catch (IOException e) { 1002 e.printStackTrace(); 1003 throw new IIOException("I/O error reading image!", e); 1004 } 1005 } 1006 1007 /** 1008 * Remove all settings including global settings such as 1009 * <code>Locale</code>s and listeners, as well as stream settings. 1010 */ 1011 public void reset() { 1012 super.reset(); 1013 resetStreamSettings(); 1014 } 1015 1016 /** 1017 * Remove local settings based on parsing of a stream. 1018 */ 1019 private void resetStreamSettings() { 1020 gotHeader = false; 1021 streamMetadata = null; 1022 currIndex = -1; 1023 imageMetadata = null; 1024 imageStartPosition = new ArrayList(); 1025 numImages = -1; 1026 1027 // No need to reinitialize 'block' 1028 blockLength = 0; 1029 bitPos = 0; 1030 nextByte = 0; 1031 1032 next32Bits = 0; 1033 lastBlockFound = false; 1034 1035 theImage = null; 1036 theTile = null; 1037 width = -1; 1038 height = -1; 1039 streamX = -1; 1040 streamY = -1; 1041 rowsDone = 0; 1042 interlacePass = 0; 1043 } 1044 }