src/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageReader.java

Print this page
rev 9230 : imported patch 8033716
   1 /*
   2  * Copyright (c) 2000, 2012, 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


  61 
  62 public class JPEGImageReader extends ImageReader {
  63 
  64     private boolean debug = false;
  65 
  66     /**
  67      * The following variable contains a pointer to the IJG library
  68      * structure for this reader.  It is assigned in the constructor
  69      * and then is passed in to every native call.  It is set to 0
  70      * by dispose to avoid disposing twice.
  71      */
  72     private long structPointer = 0;
  73 
  74     /** The input stream we read from */
  75     private ImageInputStream iis = null;
  76 
  77     /**
  78      * List of stream positions for images, reinitialized every time
  79      * a new input source is set.
  80      */
  81     private List imagePositions = null;
  82 
  83     /**
  84      * The number of images in the stream, or 0.
  85      */
  86     private int numImages = 0;
  87 
  88     static {
  89         java.security.AccessController.doPrivileged(
  90             new java.security.PrivilegedAction<Void>() {
  91                 public Void run() {
  92                     System.loadLibrary("jpeg");
  93                     return null;
  94                 }
  95             });
  96         initReaderIDs(ImageInputStream.class,
  97                       JPEGQTable.class,
  98                       JPEGHuffmanTable.class);
  99     }
 100 
 101     // The following warnings are converted to strings when used


 206     private static final int UNKNOWN = -1;  // Number of passes
 207     private static final int MIN_ESTIMATED_PASSES = 10; // IJG default
 208     private int knownPassCount = UNKNOWN;
 209     private int pass = 0;
 210     private float percentToDate = 0.0F;
 211     private float previousPassPercentage = 0.0F;
 212     private int progInterval = 0;
 213 
 214     /**
 215      * Set to true once stream has been checked for stream metadata
 216      */
 217     private boolean tablesOnlyChecked = false;
 218 
 219     /** The referent to be registered with the Disposer. */
 220     private Object disposerReferent = new Object();
 221 
 222     /** The DisposerRecord that handles the actual disposal of this reader. */
 223     private DisposerRecord disposerRecord;
 224 
 225     /** Sets up static C structures. */
 226     private static native void initReaderIDs(Class iisClass,
 227                                              Class qTableClass,
 228                                              Class huffClass);
 229 
 230     public JPEGImageReader(ImageReaderSpi originator) {
 231         super(originator);
 232         structPointer = initJPEGImageReader();
 233         disposerRecord = new JPEGReaderDisposerRecord(structPointer);
 234         Disposer.addRecord(disposerReferent, disposerRecord);
 235     }
 236 
 237     /** Sets up per-reader C structure and returns a pointer to it. */
 238     private native long initJPEGImageReader();
 239 
 240     /**
 241      * Called by the native code or other classes to signal a warning.
 242      * The code is used to lookup a localized message to be used when
 243      * sending warnings to listeners.
 244      */
 245     protected void warningOccurred(int code) {
 246         cbLock.lock();
 247         try {
 248             if ((code < 0) || (code > MAX_WARNING)){


 358                 haveSeeked = true;
 359                 streamMetadata = new JPEGMetadata(true, false,
 360                                                   iis, this);
 361                 long pos = iis.getStreamPosition();
 362                 if (debug) {
 363                     System.out.println
 364                         ("pos after constructing stream metadata is " + pos);
 365                 }
 366             }
 367             // Now we are at the first image if there are any, so add it
 368             // to the list
 369             if (hasNextImage()) {
 370                 imagePositions.add(new Long(iis.getStreamPosition()));
 371             }
 372         } else { // Not tables only, so add original pos to the list
 373             imagePositions.add(new Long(savePos));
 374             // And set current image since we've read it now
 375             currentImage = 0;
 376         }
 377         if (seekForwardOnly) {
 378             Long pos = (Long) imagePositions.get(imagePositions.size()-1);
 379             iis.flushBefore(pos.longValue());
 380         }
 381         tablesOnlyChecked = true;
 382     }
 383 
 384     public int getNumImages(boolean allowSearch) throws IOException {
 385         setThreadLock();
 386         try { // locked thread
 387             cbLock.check();
 388 
 389             return getNumImagesOnThread(allowSearch);
 390         } finally {
 391             clearThreadLock();
 392         }
 393     }
 394 
 395     private int getNumImagesOnThread(boolean allowSearch)
 396       throws IOException {
 397         if (numImages != 0) {
 398             return numImages;


 464     /**
 465      * Sets the input stream to the start of the requested image.
 466      * <pre>
 467      * @exception IllegalStateException if the input source has not been
 468      * set.
 469      * @exception IndexOutOfBoundsException if the supplied index is
 470      * out of bounds.
 471      * </pre>
 472      */
 473     private void gotoImage(int imageIndex) throws IOException {
 474         if (iis == null) {
 475             throw new IllegalStateException("Input not set");
 476         }
 477         if (imageIndex < minIndex) {
 478             throw new IndexOutOfBoundsException();
 479         }
 480         if (!tablesOnlyChecked) {
 481             checkTablesOnly();
 482         }
 483         if (imageIndex < imagePositions.size()) {
 484             iis.seek(((Long)(imagePositions.get(imageIndex))).longValue());
 485         } else {
 486             // read to start of image, saving positions
 487             // First seek to the last position we already have, and skip the
 488             // entire image
 489             Long pos = (Long) imagePositions.get(imagePositions.size()-1);
 490             iis.seek(pos.longValue());
 491             skipImage();
 492             // Now add all intervening positions, skipping images
 493             for (int index = imagePositions.size();
 494                  index <= imageIndex;
 495                  index++) {
 496                 // Is there an image?
 497                 if (!hasNextImage()) {
 498                     throw new IndexOutOfBoundsException();
 499                 }
 500                 pos = new Long(iis.getStreamPosition());
 501                 imagePositions.add(pos);
 502                 if (seekForwardOnly) {
 503                     iis.flushBefore(pos.longValue());
 504                 }
 505                 if (index < imageIndex) {
 506                     skipImage();
 507                 }  // Otherwise we are where we want to be
 508             }
 509         }


 749         return ret;
 750     }
 751 
 752     public ImageTypeSpecifier getRawImageType(int imageIndex)
 753         throws IOException {
 754         setThreadLock();
 755         try {
 756             if (currentImage != imageIndex) {
 757                 cbLock.check();
 758 
 759                 readHeader(imageIndex, true);
 760             }
 761 
 762             // Returns null if it can't be represented
 763             return getImageType(colorSpaceCode).getType();
 764         } finally {
 765             clearThreadLock();
 766         }
 767     }
 768 
 769     public Iterator getImageTypes(int imageIndex)
 770         throws IOException {
 771         setThreadLock();
 772         try {
 773             return getImageTypesOnThread(imageIndex);
 774         } finally {
 775             clearThreadLock();
 776         }
 777     }
 778 
 779     private Iterator getImageTypesOnThread(int imageIndex)
 780         throws IOException {
 781         if (currentImage != imageIndex) {
 782             cbLock.check();
 783             readHeader(imageIndex, true);
 784         }
 785 
 786         // We return an iterator containing the default, any
 787         // conversions that the library provides, and
 788         // all the other default types with the same number
 789         // of components, as we can do these as a post-process.
 790         // As we convert Rasters rather than images, images
 791         // with alpha cannot be converted in a post-process.
 792 
 793         // If this image can't be interpreted, this method
 794         // returns an empty Iterator.
 795 
 796         // Get the raw ITS, if there is one.  Note that this
 797         // won't always be the same as the default.
 798         ImageTypeProducer raw = getImageType(colorSpaceCode);
 799 


1046             }
1047 
1048             BufferedImage ret = image;
1049             image = null;  // don't keep a reference here
1050             return ret;
1051         } finally {
1052             clearThreadLock();
1053         }
1054     }
1055 
1056     private Raster readInternal(int imageIndex,
1057                                 ImageReadParam param,
1058                                 boolean wantRaster) throws IOException {
1059         readHeader(imageIndex, false);
1060 
1061         WritableRaster imRas = null;
1062         int numImageBands = 0;
1063 
1064         if (!wantRaster){
1065             // Can we read this image?
1066             Iterator imageTypes = getImageTypes(imageIndex);
1067             if (imageTypes.hasNext() == false) {
1068                 throw new IIOException("Unsupported Image Type");
1069             }
1070 
1071             image = getDestination(param, imageTypes, width, height);
1072             imRas = image.getRaster();
1073 
1074             // The destination may still be incompatible.
1075 
1076             numImageBands = image.getSampleModel().getNumBands();
1077 
1078             // Check whether we can handle any implied color conversion
1079 
1080             // Throws IIOException if the stream and the image are
1081             // incompatible, and sets convert if a java conversion
1082             // is necessary
1083             checkColorConversion(image, param);
1084 
1085             // Check the source and destination bands in the param
1086             checkReadParamBandSettings(param, numComponents, numImageBands);


1170                 throw new IIOException("Illegal band size: should be 0 < size <= 8");
1171             }
1172         }
1173 
1174         /*
1175          * If the process is sequential, and we have restart markers,
1176          * we could skip to the correct restart marker, if the library
1177          * lets us.  That's an optimization to investigate later.
1178          */
1179 
1180         // Check for update listeners (don't call back if none)
1181         boolean callbackUpdates = ((updateListeners != null)
1182                                    || (progressListeners != null));
1183 
1184         // Set up progression data
1185         initProgressData();
1186         // if we have a metadata object, we can count the scans
1187         // and set knownPassCount
1188         if (imageIndex == imageMetadataIndex) { // We have metadata
1189             knownPassCount = 0;
1190             for (Iterator iter = imageMetadata.markerSequence.iterator();
1191                  iter.hasNext();) {
1192                 if (iter.next() instanceof SOSMarkerSegment) {
1193                     knownPassCount++;
1194                 }
1195             }
1196         }
1197         progInterval = Math.max((target.getHeight()-1) / 20, 1);
1198         if (knownPassCount > 0) {
1199             progInterval *= knownPassCount;
1200         } else if (maxProgressivePass != Integer.MAX_VALUE) {
1201             progInterval *= (maxProgressivePass - minProgressivePass + 1);
1202         }
1203 
1204         if (debug) {
1205             System.out.println("**** Read Data *****");
1206             System.out.println("numRasterBands is " + numRasterBands);
1207             System.out.print("srcBands:");
1208             for (int i = 0; i<srcBands.length;i++)
1209                 System.out.print(" " + srcBands[i]);
1210             System.out.println();


1548             if ((thumbnailIndex < 0)
1549                 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1550                 throw new IndexOutOfBoundsException("No such thumbnail");
1551             }
1552             // Now we know that there is a jfif segment and that iis is good
1553             JFIFMarkerSegment jfif =
1554                 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1555                 (JFIFMarkerSegment.class, true);
1556             return  jfif.getThumbnail(iis, thumbnailIndex, this);
1557         } finally {
1558             clearThreadLock();
1559         }
1560     }
1561 
1562     private void resetInternalState() {
1563         // reset C structures
1564         resetReader(structPointer);
1565 
1566         // reset local Java structures
1567         numImages = 0;
1568         imagePositions = new ArrayList();
1569         currentImage = -1;
1570         image = null;
1571         raster = null;
1572         target = null;
1573         buffer = null;
1574         destROI = null;
1575         destinationBands = null;
1576         streamMetadata = null;
1577         imageMetadata = null;
1578         imageMetadataIndex = -1;
1579         haveSeeked = false;
1580         tablesOnlyChecked = false;
1581         iccCS = null;
1582         initProgressData();
1583     }
1584 
1585     public void reset() {
1586         setThreadLock();
1587         try {
1588             cbLock.check();


   1 /*
   2  * Copyright (c) 2000, 2014, 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


  61 
  62 public class JPEGImageReader extends ImageReader {
  63 
  64     private boolean debug = false;
  65 
  66     /**
  67      * The following variable contains a pointer to the IJG library
  68      * structure for this reader.  It is assigned in the constructor
  69      * and then is passed in to every native call.  It is set to 0
  70      * by dispose to avoid disposing twice.
  71      */
  72     private long structPointer = 0;
  73 
  74     /** The input stream we read from */
  75     private ImageInputStream iis = null;
  76 
  77     /**
  78      * List of stream positions for images, reinitialized every time
  79      * a new input source is set.
  80      */
  81     private List<Long> imagePositions = null;
  82 
  83     /**
  84      * The number of images in the stream, or 0.
  85      */
  86     private int numImages = 0;
  87 
  88     static {
  89         java.security.AccessController.doPrivileged(
  90             new java.security.PrivilegedAction<Void>() {
  91                 public Void run() {
  92                     System.loadLibrary("jpeg");
  93                     return null;
  94                 }
  95             });
  96         initReaderIDs(ImageInputStream.class,
  97                       JPEGQTable.class,
  98                       JPEGHuffmanTable.class);
  99     }
 100 
 101     // The following warnings are converted to strings when used


 206     private static final int UNKNOWN = -1;  // Number of passes
 207     private static final int MIN_ESTIMATED_PASSES = 10; // IJG default
 208     private int knownPassCount = UNKNOWN;
 209     private int pass = 0;
 210     private float percentToDate = 0.0F;
 211     private float previousPassPercentage = 0.0F;
 212     private int progInterval = 0;
 213 
 214     /**
 215      * Set to true once stream has been checked for stream metadata
 216      */
 217     private boolean tablesOnlyChecked = false;
 218 
 219     /** The referent to be registered with the Disposer. */
 220     private Object disposerReferent = new Object();
 221 
 222     /** The DisposerRecord that handles the actual disposal of this reader. */
 223     private DisposerRecord disposerRecord;
 224 
 225     /** Sets up static C structures. */
 226     private static native void initReaderIDs(Class<?> iisClass,
 227                                              Class<?> qTableClass,
 228                                              Class<?> huffClass);
 229 
 230     public JPEGImageReader(ImageReaderSpi originator) {
 231         super(originator);
 232         structPointer = initJPEGImageReader();
 233         disposerRecord = new JPEGReaderDisposerRecord(structPointer);
 234         Disposer.addRecord(disposerReferent, disposerRecord);
 235     }
 236 
 237     /** Sets up per-reader C structure and returns a pointer to it. */
 238     private native long initJPEGImageReader();
 239 
 240     /**
 241      * Called by the native code or other classes to signal a warning.
 242      * The code is used to lookup a localized message to be used when
 243      * sending warnings to listeners.
 244      */
 245     protected void warningOccurred(int code) {
 246         cbLock.lock();
 247         try {
 248             if ((code < 0) || (code > MAX_WARNING)){


 358                 haveSeeked = true;
 359                 streamMetadata = new JPEGMetadata(true, false,
 360                                                   iis, this);
 361                 long pos = iis.getStreamPosition();
 362                 if (debug) {
 363                     System.out.println
 364                         ("pos after constructing stream metadata is " + pos);
 365                 }
 366             }
 367             // Now we are at the first image if there are any, so add it
 368             // to the list
 369             if (hasNextImage()) {
 370                 imagePositions.add(new Long(iis.getStreamPosition()));
 371             }
 372         } else { // Not tables only, so add original pos to the list
 373             imagePositions.add(new Long(savePos));
 374             // And set current image since we've read it now
 375             currentImage = 0;
 376         }
 377         if (seekForwardOnly) {
 378             Long pos = imagePositions.get(imagePositions.size()-1);
 379             iis.flushBefore(pos.longValue());
 380         }
 381         tablesOnlyChecked = true;
 382     }
 383 
 384     public int getNumImages(boolean allowSearch) throws IOException {
 385         setThreadLock();
 386         try { // locked thread
 387             cbLock.check();
 388 
 389             return getNumImagesOnThread(allowSearch);
 390         } finally {
 391             clearThreadLock();
 392         }
 393     }
 394 
 395     private int getNumImagesOnThread(boolean allowSearch)
 396       throws IOException {
 397         if (numImages != 0) {
 398             return numImages;


 464     /**
 465      * Sets the input stream to the start of the requested image.
 466      * <pre>
 467      * @exception IllegalStateException if the input source has not been
 468      * set.
 469      * @exception IndexOutOfBoundsException if the supplied index is
 470      * out of bounds.
 471      * </pre>
 472      */
 473     private void gotoImage(int imageIndex) throws IOException {
 474         if (iis == null) {
 475             throw new IllegalStateException("Input not set");
 476         }
 477         if (imageIndex < minIndex) {
 478             throw new IndexOutOfBoundsException();
 479         }
 480         if (!tablesOnlyChecked) {
 481             checkTablesOnly();
 482         }
 483         if (imageIndex < imagePositions.size()) {
 484             iis.seek(imagePositions.get(imageIndex).longValue());
 485         } else {
 486             // read to start of image, saving positions
 487             // First seek to the last position we already have, and skip the
 488             // entire image
 489             Long pos = imagePositions.get(imagePositions.size()-1);
 490             iis.seek(pos.longValue());
 491             skipImage();
 492             // Now add all intervening positions, skipping images
 493             for (int index = imagePositions.size();
 494                  index <= imageIndex;
 495                  index++) {
 496                 // Is there an image?
 497                 if (!hasNextImage()) {
 498                     throw new IndexOutOfBoundsException();
 499                 }
 500                 pos = new Long(iis.getStreamPosition());
 501                 imagePositions.add(pos);
 502                 if (seekForwardOnly) {
 503                     iis.flushBefore(pos.longValue());
 504                 }
 505                 if (index < imageIndex) {
 506                     skipImage();
 507                 }  // Otherwise we are where we want to be
 508             }
 509         }


 749         return ret;
 750     }
 751 
 752     public ImageTypeSpecifier getRawImageType(int imageIndex)
 753         throws IOException {
 754         setThreadLock();
 755         try {
 756             if (currentImage != imageIndex) {
 757                 cbLock.check();
 758 
 759                 readHeader(imageIndex, true);
 760             }
 761 
 762             // Returns null if it can't be represented
 763             return getImageType(colorSpaceCode).getType();
 764         } finally {
 765             clearThreadLock();
 766         }
 767     }
 768 
 769     public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
 770         throws IOException {
 771         setThreadLock();
 772         try {
 773             return getImageTypesOnThread(imageIndex);
 774         } finally {
 775             clearThreadLock();
 776         }
 777     }
 778 
 779     private Iterator<ImageTypeSpecifier> getImageTypesOnThread(int imageIndex)
 780         throws IOException {
 781         if (currentImage != imageIndex) {
 782             cbLock.check();
 783             readHeader(imageIndex, true);
 784         }
 785 
 786         // We return an iterator containing the default, any
 787         // conversions that the library provides, and
 788         // all the other default types with the same number
 789         // of components, as we can do these as a post-process.
 790         // As we convert Rasters rather than images, images
 791         // with alpha cannot be converted in a post-process.
 792 
 793         // If this image can't be interpreted, this method
 794         // returns an empty Iterator.
 795 
 796         // Get the raw ITS, if there is one.  Note that this
 797         // won't always be the same as the default.
 798         ImageTypeProducer raw = getImageType(colorSpaceCode);
 799 


1046             }
1047 
1048             BufferedImage ret = image;
1049             image = null;  // don't keep a reference here
1050             return ret;
1051         } finally {
1052             clearThreadLock();
1053         }
1054     }
1055 
1056     private Raster readInternal(int imageIndex,
1057                                 ImageReadParam param,
1058                                 boolean wantRaster) throws IOException {
1059         readHeader(imageIndex, false);
1060 
1061         WritableRaster imRas = null;
1062         int numImageBands = 0;
1063 
1064         if (!wantRaster){
1065             // Can we read this image?
1066             Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex);
1067             if (imageTypes.hasNext() == false) {
1068                 throw new IIOException("Unsupported Image Type");
1069             }
1070 
1071             image = getDestination(param, imageTypes, width, height);
1072             imRas = image.getRaster();
1073 
1074             // The destination may still be incompatible.
1075 
1076             numImageBands = image.getSampleModel().getNumBands();
1077 
1078             // Check whether we can handle any implied color conversion
1079 
1080             // Throws IIOException if the stream and the image are
1081             // incompatible, and sets convert if a java conversion
1082             // is necessary
1083             checkColorConversion(image, param);
1084 
1085             // Check the source and destination bands in the param
1086             checkReadParamBandSettings(param, numComponents, numImageBands);


1170                 throw new IIOException("Illegal band size: should be 0 < size <= 8");
1171             }
1172         }
1173 
1174         /*
1175          * If the process is sequential, and we have restart markers,
1176          * we could skip to the correct restart marker, if the library
1177          * lets us.  That's an optimization to investigate later.
1178          */
1179 
1180         // Check for update listeners (don't call back if none)
1181         boolean callbackUpdates = ((updateListeners != null)
1182                                    || (progressListeners != null));
1183 
1184         // Set up progression data
1185         initProgressData();
1186         // if we have a metadata object, we can count the scans
1187         // and set knownPassCount
1188         if (imageIndex == imageMetadataIndex) { // We have metadata
1189             knownPassCount = 0;
1190             for (Iterator<MarkerSegment> iter = imageMetadata.markerSequence.iterator();
1191                  iter.hasNext();) {
1192                 if (iter.next() instanceof SOSMarkerSegment) {
1193                     knownPassCount++;
1194                 }
1195             }
1196         }
1197         progInterval = Math.max((target.getHeight()-1) / 20, 1);
1198         if (knownPassCount > 0) {
1199             progInterval *= knownPassCount;
1200         } else if (maxProgressivePass != Integer.MAX_VALUE) {
1201             progInterval *= (maxProgressivePass - minProgressivePass + 1);
1202         }
1203 
1204         if (debug) {
1205             System.out.println("**** Read Data *****");
1206             System.out.println("numRasterBands is " + numRasterBands);
1207             System.out.print("srcBands:");
1208             for (int i = 0; i<srcBands.length;i++)
1209                 System.out.print(" " + srcBands[i]);
1210             System.out.println();


1548             if ((thumbnailIndex < 0)
1549                 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1550                 throw new IndexOutOfBoundsException("No such thumbnail");
1551             }
1552             // Now we know that there is a jfif segment and that iis is good
1553             JFIFMarkerSegment jfif =
1554                 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1555                 (JFIFMarkerSegment.class, true);
1556             return  jfif.getThumbnail(iis, thumbnailIndex, this);
1557         } finally {
1558             clearThreadLock();
1559         }
1560     }
1561 
1562     private void resetInternalState() {
1563         // reset C structures
1564         resetReader(structPointer);
1565 
1566         // reset local Java structures
1567         numImages = 0;
1568         imagePositions = new ArrayList<>();
1569         currentImage = -1;
1570         image = null;
1571         raster = null;
1572         target = null;
1573         buffer = null;
1574         destROI = null;
1575         destinationBands = null;
1576         streamMetadata = null;
1577         imageMetadata = null;
1578         imageMetadataIndex = -1;
1579         haveSeeked = false;
1580         tablesOnlyChecked = false;
1581         iccCS = null;
1582         initProgressData();
1583     }
1584 
1585     public void reset() {
1586         setThreadLock();
1587         try {
1588             cbLock.check();