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