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) 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<ImageTypeSpecifier> 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 = 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<ImageTypeSpecifier> 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;
|