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
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 getImageTypes(int imageIndex) throws IIOException {
231 checkIndex(imageIndex);
232
233 int index = locateImage(imageIndex);
234 if (index != imageIndex) {
235 throw new IndexOutOfBoundsException();
236 }
237 readMetadata();
238
239 List l = new ArrayList(1);
240
241 byte[] colorTable;
242 if (imageMetadata.localColorTable != null) {
243 colorTable = imageMetadata.localColorTable;
244 fallbackColorTable = imageMetadata.localColorTable;
245 } else {
246 colorTable = streamMetadata.globalColorTable;
247 }
248
249 if (colorTable == null) {
250 if (fallbackColorTable == null) {
251 this.processWarningOccurred("Use default color table.");
252
253 // no color table, the spec allows to use any palette.
254 fallbackColorTable = getDefaultPalette();
255 }
256
257 colorTable = fallbackColorTable;
258 }
259
588 length = stream.readUnsignedByte();
589 stream.skipBytes(length);
590 } while (length > 0);
591 }
592 }
593 } catch (EOFException e) {
594 return false;
595 } catch (IOException e) {
596 throw new IIOException("I/O error locating image!", e);
597 }
598 }
599
600 private int locateImage(int imageIndex) throws IIOException {
601 readHeader();
602
603 try {
604 // Find closest known index
605 int index = Math.min(imageIndex, imageStartPosition.size() - 1);
606
607 // Seek to that position
608 Long l = (Long)imageStartPosition.get(index);
609 stream.seek(l.longValue());
610
611 // Skip images until at desired index or last image found
612 while (index < imageIndex) {
613 if (!skipImage()) {
614 --index;
615 return index;
616 }
617
618 Long l1 = new Long(stream.getStreamPosition());
619 imageStartPosition.add(l1);
620 ++index;
621 }
622 } catch (IOException e) {
623 throw new IIOException("Couldn't seek!", e);
624 }
625
626 if (currIndex != imageIndex) {
627 imageMetadata = null;
628 }
714 imageMetadata.textGridLeft =
715 stream.readUnsignedShort();
716 imageMetadata.textGridTop =
717 stream.readUnsignedShort();
718 imageMetadata.textGridWidth =
719 stream.readUnsignedShort();
720 imageMetadata.textGridHeight =
721 stream.readUnsignedShort();
722 imageMetadata.characterCellWidth =
723 stream.readUnsignedByte();
724 imageMetadata.characterCellHeight =
725 stream.readUnsignedByte();
726 imageMetadata.textForegroundColor =
727 stream.readUnsignedByte();
728 imageMetadata.textBackgroundColor =
729 stream.readUnsignedByte();
730 imageMetadata.text = concatenateBlocks();
731 } else if (label == 0xfe) { // Comment extension
732 byte[] comment = concatenateBlocks();
733 if (imageMetadata.comments == null) {
734 imageMetadata.comments = new ArrayList();
735 }
736 imageMetadata.comments.add(comment);
737 } else if (label == 0xff) { // Application extension
738 int blockSize = stream.readUnsignedByte();
739 byte[] applicationID = new byte[8];
740 byte[] authCode = new byte[3];
741
742 // read available data
743 byte[] blockData = new byte[blockSize];
744 stream.readFully(blockData);
745
746 int offset = copyData(blockData, 0, applicationID);
747 offset = copyData(blockData, offset, authCode);
748
749 byte[] applicationData = concatenateBlocks();
750
751 if (offset < blockSize) {
752 int len = blockSize - offset;
753 byte[] data =
754 new byte[len + applicationData.length];
755
756 System.arraycopy(blockData, offset, data, 0, len);
757 System.arraycopy(applicationData, 0, data, len,
758 applicationData.length);
759
760 applicationData = data;
761 }
762
763 // Init lists if necessary
764 if (imageMetadata.applicationIDs == null) {
765 imageMetadata.applicationIDs = new ArrayList();
766 imageMetadata.authenticationCodes =
767 new ArrayList();
768 imageMetadata.applicationData = new ArrayList();
769 }
770 imageMetadata.applicationIDs.add(applicationID);
771 imageMetadata.authenticationCodes.add(authCode);
772 imageMetadata.applicationData.add(applicationData);
773 } else {
774 // Skip over unknown extension blocks
775 int length = 0;
776 do {
777 length = stream.readUnsignedByte();
778 stream.skipBytes(length);
779 } while (length > 0);
780 }
781 } else if (blockType == 0x3b) { // Trailer
782 throw new IndexOutOfBoundsException
783 ("Attempt to read past end of image sequence!");
784 } else {
785 throw new IIOException("Unexpected block type " +
786 blockType + "!");
787 }
788 }
851 throws IIOException {
852 if (stream == null) {
853 throw new IllegalStateException("Input not set!");
854 }
855 checkIndex(imageIndex);
856
857 int index = locateImage(imageIndex);
858 if (index != imageIndex) {
859 throw new IndexOutOfBoundsException("imageIndex out of bounds!");
860 }
861
862 clearAbortRequest();
863 readMetadata();
864
865 // A null ImageReadParam means we use the default
866 if (param == null) {
867 param = getDefaultReadParam();
868 }
869
870 // Initialize the destination image
871 Iterator imageTypes = getImageTypes(imageIndex);
872 this.theImage = getDestination(param,
873 imageTypes,
874 imageMetadata.imageWidth,
875 imageMetadata.imageHeight);
876 this.theTile = theImage.getWritableTile(0, 0);
877 this.width = imageMetadata.imageWidth;
878 this.height = imageMetadata.imageHeight;
879 this.streamX = 0;
880 this.streamY = 0;
881 this.rowsDone = 0;
882 this.interlacePass = 0;
883
884 // Get source region, taking subsampling offsets into account,
885 // and clipping against the true source bounds
886
887 this.sourceRegion = new Rectangle(0, 0, 0, 0);
888 this.destinationRegion = new Rectangle(0, 0, 0, 0);
889 computeRegions(param, width, height, theImage,
890 sourceRegion, destinationRegion);
891 this.destinationOffset = new Point(destinationRegion.x,
1014 }
1015 }
1016
1017 /**
1018 * Remove all settings including global settings such as
1019 * <code>Locale</code>s and listeners, as well as stream settings.
1020 */
1021 public void reset() {
1022 super.reset();
1023 resetStreamSettings();
1024 }
1025
1026 /**
1027 * Remove local settings based on parsing of a stream.
1028 */
1029 private void resetStreamSettings() {
1030 gotHeader = false;
1031 streamMetadata = null;
1032 currIndex = -1;
1033 imageMetadata = null;
1034 imageStartPosition = new ArrayList();
1035 numImages = -1;
1036
1037 // No need to reinitialize 'block'
1038 blockLength = 0;
1039 bitPos = 0;
1040 nextByte = 0;
1041
1042 next32Bits = 0;
1043 lastBlockFound = false;
1044
1045 theImage = null;
1046 theTile = null;
1047 width = -1;
1048 height = -1;
1049 streamX = -1;
1050 streamY = -1;
1051 rowsDone = 0;
1052 interlacePass = 0;
1053
1054 fallbackColorTable = null;
|
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
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
589 length = stream.readUnsignedByte();
590 stream.skipBytes(length);
591 } while (length > 0);
592 }
593 }
594 } catch (EOFException e) {
595 return false;
596 } catch (IOException e) {
597 throw new IIOException("I/O error locating image!", e);
598 }
599 }
600
601 private int locateImage(int imageIndex) throws IIOException {
602 readHeader();
603
604 try {
605 // Find closest known index
606 int index = Math.min(imageIndex, imageStartPosition.size() - 1);
607
608 // Seek to that position
609 Long l = imageStartPosition.get(index);
610 stream.seek(l.longValue());
611
612 // Skip images until at desired index or last image found
613 while (index < imageIndex) {
614 if (!skipImage()) {
615 --index;
616 return index;
617 }
618
619 Long l1 = new Long(stream.getStreamPosition());
620 imageStartPosition.add(l1);
621 ++index;
622 }
623 } catch (IOException e) {
624 throw new IIOException("Couldn't seek!", e);
625 }
626
627 if (currIndex != imageIndex) {
628 imageMetadata = null;
629 }
715 imageMetadata.textGridLeft =
716 stream.readUnsignedShort();
717 imageMetadata.textGridTop =
718 stream.readUnsignedShort();
719 imageMetadata.textGridWidth =
720 stream.readUnsignedShort();
721 imageMetadata.textGridHeight =
722 stream.readUnsignedShort();
723 imageMetadata.characterCellWidth =
724 stream.readUnsignedByte();
725 imageMetadata.characterCellHeight =
726 stream.readUnsignedByte();
727 imageMetadata.textForegroundColor =
728 stream.readUnsignedByte();
729 imageMetadata.textBackgroundColor =
730 stream.readUnsignedByte();
731 imageMetadata.text = concatenateBlocks();
732 } else if (label == 0xfe) { // Comment extension
733 byte[] comment = concatenateBlocks();
734 if (imageMetadata.comments == null) {
735 imageMetadata.comments = new ArrayList<>();
736 }
737 imageMetadata.comments.add(comment);
738 } else if (label == 0xff) { // Application extension
739 int blockSize = stream.readUnsignedByte();
740 byte[] applicationID = new byte[8];
741 byte[] authCode = new byte[3];
742
743 // read available data
744 byte[] blockData = new byte[blockSize];
745 stream.readFully(blockData);
746
747 int offset = copyData(blockData, 0, applicationID);
748 offset = copyData(blockData, offset, authCode);
749
750 byte[] applicationData = concatenateBlocks();
751
752 if (offset < blockSize) {
753 int len = blockSize - offset;
754 byte[] data =
755 new byte[len + applicationData.length];
756
757 System.arraycopy(blockData, offset, data, 0, len);
758 System.arraycopy(applicationData, 0, data, len,
759 applicationData.length);
760
761 applicationData = data;
762 }
763
764 // Init lists if necessary
765 if (imageMetadata.applicationIDs == null) {
766 imageMetadata.applicationIDs = new ArrayList<>();
767 imageMetadata.authenticationCodes =
768 new ArrayList<>();
769 imageMetadata.applicationData = new ArrayList<>();
770 }
771 imageMetadata.applicationIDs.add(applicationID);
772 imageMetadata.authenticationCodes.add(authCode);
773 imageMetadata.applicationData.add(applicationData);
774 } else {
775 // Skip over unknown extension blocks
776 int length = 0;
777 do {
778 length = stream.readUnsignedByte();
779 stream.skipBytes(length);
780 } while (length > 0);
781 }
782 } else if (blockType == 0x3b) { // Trailer
783 throw new IndexOutOfBoundsException
784 ("Attempt to read past end of image sequence!");
785 } else {
786 throw new IIOException("Unexpected block type " +
787 blockType + "!");
788 }
789 }
852 throws IIOException {
853 if (stream == null) {
854 throw new IllegalStateException("Input not set!");
855 }
856 checkIndex(imageIndex);
857
858 int index = locateImage(imageIndex);
859 if (index != imageIndex) {
860 throw new IndexOutOfBoundsException("imageIndex out of bounds!");
861 }
862
863 clearAbortRequest();
864 readMetadata();
865
866 // A null ImageReadParam means we use the default
867 if (param == null) {
868 param = getDefaultReadParam();
869 }
870
871 // Initialize the destination image
872 Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex);
873 this.theImage = getDestination(param,
874 imageTypes,
875 imageMetadata.imageWidth,
876 imageMetadata.imageHeight);
877 this.theTile = theImage.getWritableTile(0, 0);
878 this.width = imageMetadata.imageWidth;
879 this.height = imageMetadata.imageHeight;
880 this.streamX = 0;
881 this.streamY = 0;
882 this.rowsDone = 0;
883 this.interlacePass = 0;
884
885 // Get source region, taking subsampling offsets into account,
886 // and clipping against the true source bounds
887
888 this.sourceRegion = new Rectangle(0, 0, 0, 0);
889 this.destinationRegion = new Rectangle(0, 0, 0, 0);
890 computeRegions(param, width, height, theImage,
891 sourceRegion, destinationRegion);
892 this.destinationOffset = new Point(destinationRegion.x,
1015 }
1016 }
1017
1018 /**
1019 * Remove all settings including global settings such as
1020 * <code>Locale</code>s and listeners, as well as stream settings.
1021 */
1022 public void reset() {
1023 super.reset();
1024 resetStreamSettings();
1025 }
1026
1027 /**
1028 * Remove local settings based on parsing of a stream.
1029 */
1030 private void resetStreamSettings() {
1031 gotHeader = false;
1032 streamMetadata = null;
1033 currIndex = -1;
1034 imageMetadata = null;
1035 imageStartPosition = new ArrayList<>();
1036 numImages = -1;
1037
1038 // No need to reinitialize 'block'
1039 blockLength = 0;
1040 bitPos = 0;
1041 nextByte = 0;
1042
1043 next32Bits = 0;
1044 lastBlockFound = false;
1045
1046 theImage = null;
1047 theTile = null;
1048 width = -1;
1049 height = -1;
1050 streamX = -1;
1051 streamY = -1;
1052 rowsDone = 0;
1053 interlacePass = 0;
1054
1055 fallbackColorTable = null;
|