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