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.jpeg;
27  
28  import javax.imageio.IIOException;
29  import javax.imageio.ImageReader;
30  import javax.imageio.ImageReadParam;
31  import javax.imageio.ImageTypeSpecifier;
32  import javax.imageio.metadata.IIOMetadata;
33  import javax.imageio.spi.ImageReaderSpi;
34  import javax.imageio.stream.ImageInputStream;
35  import javax.imageio.plugins.jpeg.JPEGImageReadParam;
36  import javax.imageio.plugins.jpeg.JPEGQTable;
37  import javax.imageio.plugins.jpeg.JPEGHuffmanTable;
38  
39  import java.awt.Point;
40  import java.awt.Rectangle;
41  import java.awt.color.ColorSpace;
42  import java.awt.color.ICC_Profile;
43  import java.awt.color.ICC_ColorSpace;
44  import java.awt.color.CMMException;
45  import java.awt.image.BufferedImage;
46  import java.awt.image.Raster;
47  import java.awt.image.WritableRaster;
48  import java.awt.image.DataBuffer;
49  import java.awt.image.DataBufferByte;
50  import java.awt.image.ColorModel;
51  import java.awt.image.IndexColorModel;
52  import java.awt.image.ColorConvertOp;
53  import java.io.IOException;
54  import java.util.List;
55  import java.util.Iterator;
56  import java.util.ArrayList;
57  import java.util.NoSuchElementException;
58  
59  import sun.java2d.Disposer;
60  import sun.java2d.DisposerRecord;
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("javajpeg");
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
102     // as keys to get localized resources from JPEGImageReaderResources
103     // and its children.
104 
105     /**
106      * Warning code to be passed to warningOccurred to indicate
107      * that the EOI marker is missing from the end of the stream.
108      * This usually signals that the stream is corrupted, but
109      * everything up to the last MCU should be usable.
110      */
111     protected static final int WARNING_NO_EOI = 0;
112 
113     /**
114      * Warning code to be passed to warningOccurred to indicate
115      * that a JFIF segment was encountered inside a JFXX JPEG
116      * thumbnail and is being ignored.
117      */
118     protected static final int WARNING_NO_JFIF_IN_THUMB = 1;
119 
120     /**
121      * Warning code to be passed to warningOccurred to indicate
122      * that embedded ICC profile is invalid and will be ignored.
123      */
124     protected static final int WARNING_IGNORE_INVALID_ICC = 2;
125 
126     private static final int MAX_WARNING = WARNING_IGNORE_INVALID_ICC;
127 
128     /**
129      * Image index of image for which header information
130      * is available.
131      */
132     private int currentImage = -1;
133 
134     // The following is copied out from C after reading the header.
135     // Unlike metadata, which may never be retrieved, we need this
136     // if we are to read an image at all.
137 
138     /** Set by setImageData native code callback */
139     private int width;
140     /** Set by setImageData native code callback */
141     private int height;
142     /**
143      * Set by setImageData native code callback.  A modified
144      * IJG+NIFTY colorspace code.
145      */
146     private int colorSpaceCode;
147     /**
148      * Set by setImageData native code callback.  A modified
149      * IJG+NIFTY colorspace code.
150      */
151     private int outColorSpaceCode;
152     /** Set by setImageData native code callback */
153     private int numComponents;
154     /** Set by setImageData native code callback */
155     private ColorSpace iccCS = null;
156 
157 
158     /** If we need to post-convert in Java, convert with this op */
159     private ColorConvertOp convert = null;
160 
161     /** The image we are going to fill */
162     private BufferedImage image = null;
163 
164     /** An intermediate Raster to hold decoded data */
165     private WritableRaster raster = null;
166 
167     /** A view of our target Raster that we can setRect to */
168     private WritableRaster target = null;
169 
170     /** The databuffer for the above Raster */
171     private DataBufferByte buffer = null;
172 
173     /** The region in the destination where we will write pixels */
174     private Rectangle destROI = null;
175 
176     /** The list of destination bands, if any */
177     private int [] destinationBands = null;
178 
179     /** Stream metadata, cached, even when the stream is changed. */
180     private JPEGMetadata streamMetadata = null;
181 
182     /** Image metadata, valid for the imageMetadataIndex only. */
183     private JPEGMetadata imageMetadata = null;
184     private int imageMetadataIndex = -1;
185 
186     /**
187      * Set to true every time we seek in the stream; used to
188      * invalidate the native buffer contents in C.
189      */
190     private boolean haveSeeked = false;
191 
192     /**
193      * Tables that have been read from a tables-only image at the
194      * beginning of a stream.
195      */
196     private JPEGQTable [] abbrevQTables = null;
197     private JPEGHuffmanTable[] abbrevDCHuffmanTables = null;
198     private JPEGHuffmanTable[] abbrevACHuffmanTables = null;
199 
200     private int minProgressivePass = 0;
201     private int maxProgressivePass = Integer.MAX_VALUE;
202 
203     /**
204      * Variables used by progress monitoring.
205      */
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)){
249                 throw new InternalError("Invalid warning index");
250             }
251             processWarningOccurred
252                 ("com.sun.imageio.plugins.jpeg.JPEGImageReaderResources",
253                  Integer.toString(code));
254         } finally {
255             cbLock.unlock();
256         }
257     }
258 
259     /**
260      * The library has it's own error facility that emits warning messages.
261      * This routine is called by the native code when it has already
262      * formatted a string for output.
263      * XXX  For truly complete localization of all warning messages,
264      * the sun_jpeg_output_message routine in the native code should
265      * send only the codes and parameters to a method here in Java,
266      * which will then format and send the warnings, using localized
267      * strings.  This method will have to deal with all the parameters
268      * and formats (%u with possibly large numbers, %02d, %02x, etc.)
269      * that actually occur in the JPEG library.  For now, this prevents
270      * library warnings from being printed to stderr.
271      */
272     protected void warningWithMessage(String msg) {
273         cbLock.lock();
274         try {
275             processWarningOccurred(msg);
276         } finally {
277             cbLock.unlock();
278         }
279     }
280 
281     public void setInput(Object input,
282                          boolean seekForwardOnly,
283                          boolean ignoreMetadata)
284     {
285         setThreadLock();
286         try {
287             cbLock.check();
288 
289             super.setInput(input, seekForwardOnly, ignoreMetadata);
290             this.ignoreMetadata = ignoreMetadata;
291             resetInternalState();
292             iis = (ImageInputStream) input; // Always works
293             setSource(structPointer);
294         } finally {
295             clearThreadLock();
296         }
297     }
298 
299     /**
300      * This method is called from native code in order to fill
301      * native input buffer.
302      *
303      * We block any attempt to change the reading state during this
304      * method, in order to prevent a corruption of the native decoder
305      * state.
306      *
307      * @return number of bytes read from the stream.
308      */
309     private int readInputData(byte[] buf, int off, int len) throws IOException {
310         cbLock.lock();
311         try {
312             return iis.read(buf, off, len);
313         } finally {
314             cbLock.unlock();
315         }
316     }
317 
318     /**
319      * This method is called from the native code in order to
320      * skip requested number of bytes in the input stream.
321      *
322      * @param n
323      * @return
324      * @throws IOException
325      */
326     private long skipInputBytes(long n) throws IOException {
327         cbLock.lock();
328         try {
329             return iis.skipBytes(n);
330         } finally {
331             cbLock.unlock();
332         }
333     }
334 
335     private native void setSource(long structPointer);
336 
337     private void checkTablesOnly() throws IOException {
338         if (debug) {
339             System.out.println("Checking for tables-only image");
340         }
341         long savePos = iis.getStreamPosition();
342         if (debug) {
343             System.out.println("saved pos is " + savePos);
344             System.out.println("length is " + iis.length());
345         }
346         // Read the first header
347         boolean tablesOnly = readNativeHeader(true);
348         if (tablesOnly) {
349             if (debug) {
350                 System.out.println("tables-only image found");
351                 long pos = iis.getStreamPosition();
352                 System.out.println("pos after return from native is " + pos);
353             }
354             // This reads the tables-only image twice, once from C
355             // and once from Java, but only if ignoreMetadata is false
356             if (ignoreMetadata == false) {
357                 iis.seek(savePos);
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(iis.getStreamPosition());
371             }
372         } else { // Not tables only, so add original pos to the list
373             imagePositions.add(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 void skipPastImage(int imageIndex) {
396         cbLock.lock();
397         try {
398             gotoImage(imageIndex);
399             skipImage();
400         } catch (IOException | IndexOutOfBoundsException e) {
401         } finally {
402             cbLock.unlock();
403         }
404     }
405 
406     @SuppressWarnings("fallthrough")
407     private int getNumImagesOnThread(boolean allowSearch)
408       throws IOException {
409         if (numImages != 0) {
410             return numImages;
411         }
412         if (iis == null) {
413             throw new IllegalStateException("Input not set");
414         }
415         if (allowSearch == true) {
416             if (seekForwardOnly) {
417                 throw new IllegalStateException(
418                     "seekForwardOnly and allowSearch can't both be true!");
419             }
420             // Otherwise we have to read the entire stream
421 
422             if (!tablesOnlyChecked) {
423                 checkTablesOnly();
424             }
425 
426             iis.mark();
427 
428             gotoImage(0);
429 
430             JPEGBuffer buffer = new JPEGBuffer(iis);
431             buffer.loadBuf(0);
432 
433             boolean done = false;
434             while (!done) {
435                 done = buffer.scanForFF(this);
436                 switch (buffer.buf[buffer.bufPtr] & 0xff) {
437                 case JPEG.SOI:
438                     numImages++;
439                     // FALL THROUGH to decrement buffer vars
440                     // This first set doesn't have a length
441                 case 0: // not a marker, just a data 0xff
442                 case JPEG.RST0:
443                 case JPEG.RST1:
444                 case JPEG.RST2:
445                 case JPEG.RST3:
446                 case JPEG.RST4:
447                 case JPEG.RST5:
448                 case JPEG.RST6:
449                 case JPEG.RST7:
450                 case JPEG.EOI:
451                     buffer.bufAvail--;
452                     buffer.bufPtr++;
453                     break;
454                     // All the others have a length
455                 default:
456                     buffer.bufAvail--;
457                     buffer.bufPtr++;
458                     buffer.loadBuf(2);
459                     int length = ((buffer.buf[buffer.bufPtr++] & 0xff) << 8) |
460                         (buffer.buf[buffer.bufPtr++] & 0xff);
461                     buffer.bufAvail -= 2;
462                     length -= 2; // length includes itself
463                     buffer.skipData(length);
464                 }
465             }
466 
467 
468             iis.reset();
469 
470             return numImages;
471         }
472 
473         return -1;  // Search is necessary for JPEG
474     }
475 
476     /**
477      * Sets the input stream to the start of the requested image.
478      * <pre>
479      * @exception IllegalStateException if the input source has not been
480      * set.
481      * @exception IndexOutOfBoundsException if the supplied index is
482      * out of bounds.
483      * </pre>
484      */
485     private void gotoImage(int imageIndex) throws IOException {
486         if (iis == null) {
487             throw new IllegalStateException("Input not set");
488         }
489         if (imageIndex < minIndex) {
490             throw new IndexOutOfBoundsException();
491         }
492         if (!tablesOnlyChecked) {
493             checkTablesOnly();
494         }
495         if (imageIndex < imagePositions.size()) {
496             iis.seek(imagePositions.get(imageIndex).longValue());
497         } else {
498             // read to start of image, saving positions
499             // First seek to the last position we already have, and skip the
500             // entire image
501             Long pos = imagePositions.get(imagePositions.size()-1);
502             iis.seek(pos.longValue());
503             skipImage();
504             // Now add all intervening positions, skipping images
505             for (int index = imagePositions.size();
506                  index <= imageIndex;
507                  index++) {
508                 // Is there an image?
509                 if (!hasNextImage()) {
510                     throw new IndexOutOfBoundsException();
511                 }
512                 pos = iis.getStreamPosition();
513                 imagePositions.add(pos);
514                 if (seekForwardOnly) {
515                     iis.flushBefore(pos.longValue());
516                 }
517                 if (index < imageIndex) {
518                     skipImage();
519                 }  // Otherwise we are where we want to be
520             }
521         }
522 
523         if (seekForwardOnly) {
524             minIndex = imageIndex;
525         }
526 
527         haveSeeked = true;  // No way is native buffer still valid
528     }
529 
530     /**
531      * Skip over a complete image in the stream, leaving the stream
532      * positioned such that the next byte to be read is the first
533      * byte of the next image. For JPEG, this means that we read
534      * until we encounter an EOI marker or until the end of the stream.
535      * We can find data same as EOI marker in some headers
536      * or comments, so we have to skip bytes related to these headers.
537      * If the stream ends before an EOI marker is encountered,
538      * an IndexOutOfBoundsException is thrown.
539      */
540     private void skipImage() throws IOException {
541         if (debug) {
542             System.out.println("skipImage called");
543         }
544         // verify if image starts with an SOI marker
545         int initialFF = iis.read();
546         if (initialFF == 0xff) {
547             int soiMarker = iis.read();
548             if (soiMarker != JPEG.SOI) {
549                 throw new IOException("skipImage : Invalid image doesn't "
550                         + "start with SOI marker");
551             }
552         } else {
553             throw new IOException("skipImage : Invalid image doesn't start "
554                     + "with 0xff");
555         }
556         boolean foundFF = false;
557         String IOOBE = "skipImage : Reached EOF before we got EOI marker";
558         int markerLength = 2;
559         for (int byteval = iis.read();
560              byteval != -1;
561              byteval = iis.read()) {
562 
563             if (foundFF == true) {
564                 switch (byteval) {
565                     case JPEG.EOI:
566                         if (debug) {
567                             System.out.println("skipImage : Found EOI at " +
568                                     (iis.getStreamPosition() - markerLength));
569                         }
570                         return;
571                     case JPEG.SOI:
572                         throw new IOException("skipImage : Found extra SOI"
573                                 + " marker before getting to EOI");
574                     case 0:
575                     case 255:
576                     // markers which doesn't contain length data
577                     case JPEG.RST0:
578                     case JPEG.RST1:
579                     case JPEG.RST2:
580                     case JPEG.RST3:
581                     case JPEG.RST4:
582                     case JPEG.RST5:
583                     case JPEG.RST6:
584                     case JPEG.RST7:
585                     case JPEG.TEM:
586                         break;
587                     // markers which contains length data
588                     case JPEG.SOF0:
589                     case JPEG.SOF1:
590                     case JPEG.SOF2:
591                     case JPEG.SOF3:
592                     case JPEG.DHT:
593                     case JPEG.SOF5:
594                     case JPEG.SOF6:
595                     case JPEG.SOF7:
596                     case JPEG.JPG:
597                     case JPEG.SOF9:
598                     case JPEG.SOF10:
599                     case JPEG.SOF11:
600                     case JPEG.DAC:
601                     case JPEG.SOF13:
602                     case JPEG.SOF14:
603                     case JPEG.SOF15:
604                     case JPEG.SOS:
605                     case JPEG.DQT:
606                     case JPEG.DNL:
607                     case JPEG.DRI:
608                     case JPEG.DHP:
609                     case JPEG.EXP:
610                     case JPEG.APP0:
611                     case JPEG.APP1:
612                     case JPEG.APP2:
613                     case JPEG.APP3:
614                     case JPEG.APP4:
615                     case JPEG.APP5:
616                     case JPEG.APP6:
617                     case JPEG.APP7:
618                     case JPEG.APP8:
619                     case JPEG.APP9:
620                     case JPEG.APP10:
621                     case JPEG.APP11:
622                     case JPEG.APP12:
623                     case JPEG.APP13:
624                     case JPEG.APP14:
625                     case JPEG.APP15:
626                     case JPEG.COM:
627                         // read length of header from next 2 bytes
628                         int lengthHigherBits, lengthLowerBits, length;
629                         lengthHigherBits = iis.read();
630                         if (lengthHigherBits != (-1)) {
631                             lengthLowerBits = iis.read();
632                             if (lengthLowerBits != (-1)) {
633                                 length = (lengthHigherBits << 8) |
634                                         lengthLowerBits;
635                                 // length contains already read 2 bytes
636                                 length -= 2;
637                             } else {
638                                 throw new IndexOutOfBoundsException(IOOBE);
639                             }
640                         } else {
641                             throw new IndexOutOfBoundsException(IOOBE);
642                         }
643                         // skip the length specified in marker
644                         iis.skipBytes(length);
645                         break;
646                     case (-1):
647                         throw new IndexOutOfBoundsException(IOOBE);
648                     default:
649                         throw new IOException("skipImage : Invalid marker "
650                                 + "starting with ff "
651                                 + Integer.toHexString(byteval));
652                 }
653             }
654             foundFF = (byteval == 0xff);
655         }
656         throw new IndexOutOfBoundsException(IOOBE);
657     }
658 
659     /**
660      * Returns {@code true} if there is an image beyond
661      * the current stream position.  Does not disturb the
662      * stream position.
663      */
664     private boolean hasNextImage() throws IOException {
665         if (debug) {
666             System.out.print("hasNextImage called; returning ");
667         }
668         iis.mark();
669         boolean foundFF = false;
670         for (int byteval = iis.read();
671              byteval != -1;
672              byteval = iis.read()) {
673 
674             if (foundFF == true) {
675                 if (byteval == JPEG.SOI) {
676                     iis.reset();
677                     if (debug) {
678                         System.out.println("true");
679                     }
680                     return true;
681                 }
682             }
683             foundFF = (byteval == 0xff) ? true : false;
684         }
685         // We hit the end of the stream before we hit an SOI, so no image
686         iis.reset();
687         if (debug) {
688             System.out.println("false");
689         }
690         return false;
691     }
692 
693     /**
694      * Push back the given number of bytes to the input stream.
695      * Called by the native code at the end of each image so
696      * that the next one can be identified from Java.
697      */
698     private void pushBack(int num) throws IOException {
699         if (debug) {
700             System.out.println("pushing back " + num + " bytes");
701         }
702         cbLock.lock();
703         try {
704             iis.seek(iis.getStreamPosition()-num);
705             // The buffer is clear after this, so no need to set haveSeeked.
706         } finally {
707             cbLock.unlock();
708         }
709     }
710 
711     /**
712      * Reads header information for the given image, if possible.
713      */
714     private void readHeader(int imageIndex, boolean reset)
715         throws IOException {
716         gotoImage(imageIndex);
717         readNativeHeader(reset); // Ignore return
718         currentImage = imageIndex;
719     }
720 
721     private boolean readNativeHeader(boolean reset) throws IOException {
722         boolean retval = false;
723         retval = readImageHeader(structPointer, haveSeeked, reset);
724         haveSeeked = false;
725         return retval;
726     }
727 
728     /**
729      * Read in the header information starting from the current
730      * stream position, returning {@code true} if the
731      * header was a tables-only image.  After this call, the
732      * native IJG decompression struct will contain the image
733      * information required by most query calls below
734      * (e.g. getWidth, getHeight, etc.), if the header was not
735      * a tables-only image.
736      * If reset is {@code true}, the state of the IJG
737      * object is reset so that it can read a header again.
738      * This happens automatically if the header was a tables-only
739      * image.
740      */
741     private native boolean readImageHeader(long structPointer,
742                                            boolean clearBuffer,
743                                            boolean reset)
744         throws IOException;
745 
746     /*
747      * Called by the native code whenever an image header has been
748      * read.  Whether we read metadata or not, we always need this
749      * information, so it is passed back independently of
750      * metadata, which may never be read.
751      */
752     private void setImageData(int width,
753                               int height,
754                               int colorSpaceCode,
755                               int outColorSpaceCode,
756                               int numComponents,
757                               byte [] iccData) {
758         this.width = width;
759         this.height = height;
760         this.colorSpaceCode = colorSpaceCode;
761         this.outColorSpaceCode = outColorSpaceCode;
762         this.numComponents = numComponents;
763 
764         if (iccData == null) {
765             iccCS = null;
766             return;
767         }
768 
769         ICC_Profile newProfile = null;
770         try {
771             newProfile = ICC_Profile.getInstance(iccData);
772         } catch (IllegalArgumentException e) {
773             /*
774              * Color profile data seems to be invalid.
775              * Ignore this profile.
776              */
777             iccCS = null;
778             warningOccurred(WARNING_IGNORE_INVALID_ICC);
779 
780             return;
781         }
782         byte[] newData = newProfile.getData();
783 
784         ICC_Profile oldProfile = null;
785         if (iccCS instanceof ICC_ColorSpace) {
786             oldProfile = ((ICC_ColorSpace)iccCS).getProfile();
787         }
788         byte[] oldData = null;
789         if (oldProfile != null) {
790             oldData = oldProfile.getData();
791         }
792 
793         /*
794          * At the moment we can't rely on the ColorSpace.equals()
795          * and ICC_Profile.equals() because they do not detect
796          * the case when two profiles are created from same data.
797          *
798          * So, we have to do data comparison in order to avoid
799          * creation of different ColorSpace instances for the same
800          * embedded data.
801          */
802         if (oldData == null ||
803             !java.util.Arrays.equals(oldData, newData))
804         {
805             iccCS = new ICC_ColorSpace(newProfile);
806             // verify new color space
807             try {
808                 float[] colors = iccCS.fromRGB(new float[] {1f, 0f, 0f});
809             } catch (CMMException e) {
810                 /*
811                  * Embedded profile seems to be corrupted.
812                  * Ignore this profile.
813                  */
814                 iccCS = null;
815                 cbLock.lock();
816                 try {
817                     warningOccurred(WARNING_IGNORE_INVALID_ICC);
818                 } finally {
819                     cbLock.unlock();
820                 }
821             }
822         }
823     }
824 
825     public int getWidth(int imageIndex) throws IOException {
826         setThreadLock();
827         try {
828             if (currentImage != imageIndex) {
829                 cbLock.check();
830                 readHeader(imageIndex, true);
831             }
832             return width;
833         } finally {
834             clearThreadLock();
835         }
836     }
837 
838     public int getHeight(int imageIndex) throws IOException {
839         setThreadLock();
840         try {
841             if (currentImage != imageIndex) {
842                 cbLock.check();
843                 readHeader(imageIndex, true);
844             }
845             return height;
846         } finally {
847             clearThreadLock();
848         }
849     }
850 
851     /////////// Color Conversion and Image Types
852 
853     /**
854      * Return an ImageTypeSpecifier corresponding to the given
855      * color space code, or null if the color space is unsupported.
856      */
857     private ImageTypeProducer getImageType(int code) {
858         ImageTypeProducer ret = null;
859 
860         if ((code > 0) && (code < JPEG.NUM_JCS_CODES)) {
861             ret = ImageTypeProducer.getTypeProducer(code);
862         }
863         return ret;
864     }
865 
866     public ImageTypeSpecifier getRawImageType(int imageIndex)
867         throws IOException {
868         setThreadLock();
869         try {
870             if (currentImage != imageIndex) {
871                 cbLock.check();
872 
873                 readHeader(imageIndex, true);
874             }
875 
876             // Returns null if it can't be represented
877             return getImageType(colorSpaceCode).getType();
878         } finally {
879             clearThreadLock();
880         }
881     }
882 
883     public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
884         throws IOException {
885         setThreadLock();
886         try {
887             return getImageTypesOnThread(imageIndex);
888         } finally {
889             clearThreadLock();
890         }
891     }
892 
893     private Iterator<ImageTypeSpecifier> getImageTypesOnThread(int imageIndex)
894         throws IOException {
895         if (currentImage != imageIndex) {
896             cbLock.check();
897             readHeader(imageIndex, true);
898         }
899 
900         // We return an iterator containing the default, any
901         // conversions that the library provides, and
902         // all the other default types with the same number
903         // of components, as we can do these as a post-process.
904         // As we convert Rasters rather than images, images
905         // with alpha cannot be converted in a post-process.
906 
907         // If this image can't be interpreted, this method
908         // returns an empty Iterator.
909 
910         // Get the raw ITS, if there is one.  Note that this
911         // won't always be the same as the default.
912         ImageTypeProducer raw = getImageType(colorSpaceCode);
913 
914         // Given the encoded colorspace, build a list of ITS's
915         // representing outputs you could handle starting
916         // with the default.
917 
918         ArrayList<ImageTypeProducer> list = new ArrayList<ImageTypeProducer>(1);
919 
920         switch (colorSpaceCode) {
921         case JPEG.JCS_GRAYSCALE:
922             list.add(raw);
923             list.add(getImageType(JPEG.JCS_RGB));
924             break;
925         case JPEG.JCS_RGB:
926             list.add(raw);
927             list.add(getImageType(JPEG.JCS_GRAYSCALE));
928             list.add(getImageType(JPEG.JCS_YCC));
929             break;
930         case JPEG.JCS_RGBA:
931             list.add(raw);
932             break;
933         case JPEG.JCS_YCC:
934             if (raw != null) {  // Might be null if PYCC.pf not installed
935                 list.add(raw);
936                 list.add(getImageType(JPEG.JCS_RGB));
937             }
938             break;
939         case JPEG.JCS_YCCA:
940             if (raw != null) {  // Might be null if PYCC.pf not installed
941                 list.add(raw);
942             }
943             break;
944         case JPEG.JCS_YCbCr:
945             // As there is no YCbCr ColorSpace, we can't support
946             // the raw type.
947 
948             // due to 4705399, use RGB as default in order to avoid
949             // slowing down of drawing operations with result image.
950             list.add(getImageType(JPEG.JCS_RGB));
951 
952             if (iccCS != null) {
953                 list.add(new ImageTypeProducer() {
954                     protected ImageTypeSpecifier produce() {
955                         return ImageTypeSpecifier.createInterleaved
956                          (iccCS,
957                           JPEG.bOffsRGB,  // Assume it's for RGB
958                           DataBuffer.TYPE_BYTE,
959                           false,
960                           false);
961                     }
962                 });
963 
964             }
965 
966             list.add(getImageType(JPEG.JCS_GRAYSCALE));
967             list.add(getImageType(JPEG.JCS_YCC));
968             break;
969         case JPEG.JCS_YCbCrA:  // Default is to convert to RGBA
970             // As there is no YCbCr ColorSpace, we can't support
971             // the raw type.
972             list.add(getImageType(JPEG.JCS_RGBA));
973             break;
974         }
975 
976         return new ImageTypeIterator(list.iterator());
977     }
978 
979     /**
980      * Checks the implied color conversion between the stream and
981      * the target image, altering the IJG output color space if necessary.
982      * If a java color conversion is required, then this sets up
983      * {@code convert}.
984      * If bands are being rearranged at all (either source or destination
985      * bands are specified in the param), then the default color
986      * conversions are assumed to be correct.
987      * Throws an IIOException if there is no conversion available.
988      */
989     private void checkColorConversion(BufferedImage image,
990                                       ImageReadParam param)
991         throws IIOException {
992 
993         // If we are rearranging channels at all, the default
994         // conversions remain in place.  If the user wants
995         // raw channels then he should do this while reading
996         // a Raster.
997         if (param != null) {
998             if ((param.getSourceBands() != null) ||
999                 (param.getDestinationBands() != null)) {
1000                 // Accept default conversions out of decoder, silently
1001                 return;
1002             }
1003         }
1004 
1005         // XXX - We do not currently support any indexed color models,
1006         // though we could, as IJG will quantize for us.
1007         // This is a performance and memory-use issue, as
1008         // users can read RGB and then convert to indexed in Java.
1009 
1010         ColorModel cm = image.getColorModel();
1011 
1012         if (cm instanceof IndexColorModel) {
1013             throw new IIOException("IndexColorModel not supported");
1014         }
1015 
1016         // Now check the ColorSpace type against outColorSpaceCode
1017         // We may want to tweak the default
1018         ColorSpace cs = cm.getColorSpace();
1019         int csType = cs.getType();
1020         convert = null;
1021         switch (outColorSpaceCode) {
1022         case JPEG.JCS_GRAYSCALE:  // Its gray in the file
1023             if  (csType == ColorSpace.TYPE_RGB) { // We want RGB
1024                 // IJG can do this for us more efficiently
1025                 setOutColorSpace(structPointer, JPEG.JCS_RGB);
1026                 // Update java state according to changes
1027                 // in the native part of decoder.
1028                 outColorSpaceCode = JPEG.JCS_RGB;
1029                 numComponents = 3;
1030             } else if (csType != ColorSpace.TYPE_GRAY) {
1031                 throw new IIOException("Incompatible color conversion");
1032             }
1033             break;
1034         case JPEG.JCS_RGB:  // IJG wants to go to RGB
1035             if (csType ==  ColorSpace.TYPE_GRAY) {  // We want gray
1036                 if (colorSpaceCode == JPEG.JCS_YCbCr) {
1037                     // If the jpeg space is YCbCr, IJG can do it
1038                     setOutColorSpace(structPointer, JPEG.JCS_GRAYSCALE);
1039                     // Update java state according to changes
1040                     // in the native part of decoder.
1041                     outColorSpaceCode = JPEG.JCS_GRAYSCALE;
1042                     numComponents = 1;
1043                 }
1044             } else if ((iccCS != null) &&
1045                        (cm.getNumComponents() == numComponents) &&
1046                        (cs != iccCS)) {
1047                 // We have an ICC profile but it isn't used in the dest
1048                 // image.  So convert from the profile cs to the target cs
1049                 convert = new ColorConvertOp(iccCS, cs, null);
1050                 // Leave IJG conversion in place; we still need it
1051             } else if ((iccCS == null) &&
1052                        (!cs.isCS_sRGB()) &&
1053                        (cm.getNumComponents() == numComponents)) {
1054                 // Target isn't sRGB, so convert from sRGB to the target
1055                 convert = new ColorConvertOp(JPEG.JCS.sRGB, cs, null);
1056             } else if (csType != ColorSpace.TYPE_RGB) {
1057                 throw new IIOException("Incompatible color conversion");
1058             }
1059             break;
1060         case JPEG.JCS_RGBA:
1061             // No conversions available; image must be RGBA
1062             if ((csType != ColorSpace.TYPE_RGB) ||
1063                 (cm.getNumComponents() != numComponents)) {
1064                 throw new IIOException("Incompatible color conversion");
1065             }
1066             break;
1067         case JPEG.JCS_YCC:
1068             {
1069                 ColorSpace YCC = JPEG.JCS.getYCC();
1070                 if (YCC == null) { // We can't do YCC at all
1071                     throw new IIOException("Incompatible color conversion");
1072                 }
1073                 if ((cs != YCC) &&
1074                     (cm.getNumComponents() == numComponents)) {
1075                     convert = new ColorConvertOp(YCC, cs, null);
1076                 }
1077             }
1078             break;
1079         case JPEG.JCS_YCCA:
1080             {
1081                 ColorSpace YCC = JPEG.JCS.getYCC();
1082                 // No conversions available; image must be YCCA
1083                 if ((YCC == null) || // We can't do YCC at all
1084                     (cs != YCC) ||
1085                     (cm.getNumComponents() != numComponents)) {
1086                     throw new IIOException("Incompatible color conversion");
1087                 }
1088             }
1089             break;
1090         default:
1091             // Anything else we can't handle at all
1092             throw new IIOException("Incompatible color conversion");
1093         }
1094     }
1095 
1096     /**
1097      * Set the IJG output space to the given value.  The library will
1098      * perform the appropriate colorspace conversions.
1099      */
1100     private native void setOutColorSpace(long structPointer, int id);
1101 
1102     /////// End of Color Conversion & Image Types
1103 
1104     public ImageReadParam getDefaultReadParam() {
1105         return new JPEGImageReadParam();
1106     }
1107 
1108     public IIOMetadata getStreamMetadata() throws IOException {
1109         setThreadLock();
1110         try {
1111             if (!tablesOnlyChecked) {
1112                 cbLock.check();
1113                 checkTablesOnly();
1114             }
1115             return streamMetadata;
1116         } finally {
1117             clearThreadLock();
1118         }
1119     }
1120 
1121     public IIOMetadata getImageMetadata(int imageIndex)
1122         throws IOException {
1123         setThreadLock();
1124         try {
1125             // imageMetadataIndex will always be either a valid index or
1126             // -1, in which case imageMetadata will not be null.
1127             // So we can leave checking imageIndex for gotoImage.
1128             if ((imageMetadataIndex == imageIndex)
1129                 && (imageMetadata != null)) {
1130                 return imageMetadata;
1131             }
1132 
1133             cbLock.check();
1134 
1135             gotoImage(imageIndex);
1136 
1137             imageMetadata = new JPEGMetadata(false, false, iis, this);
1138 
1139             imageMetadataIndex = imageIndex;
1140 
1141             return imageMetadata;
1142         } finally {
1143             clearThreadLock();
1144         }
1145     }
1146 
1147     public BufferedImage read(int imageIndex, ImageReadParam param)
1148         throws IOException {
1149         setThreadLock();
1150         try {
1151             cbLock.check();
1152             try {
1153                 readInternal(imageIndex, param, false);
1154             } catch (RuntimeException e) {
1155                 resetLibraryState(structPointer);
1156                 throw e;
1157             } catch (IOException e) {
1158                 resetLibraryState(structPointer);
1159                 throw e;
1160             }
1161 
1162             BufferedImage ret = image;
1163             image = null;  // don't keep a reference here
1164             return ret;
1165         } finally {
1166             clearThreadLock();
1167         }
1168     }
1169 
1170     private Raster readInternal(int imageIndex,
1171                                 ImageReadParam param,
1172                                 boolean wantRaster) throws IOException {
1173         readHeader(imageIndex, false);
1174 
1175         WritableRaster imRas = null;
1176         int numImageBands = 0;
1177 
1178         if (!wantRaster){
1179             // Can we read this image?
1180             Iterator<ImageTypeSpecifier> imageTypes = getImageTypes(imageIndex);
1181             if (imageTypes.hasNext() == false) {
1182                 throw new IIOException("Unsupported Image Type");
1183             }
1184 
1185             image = getDestination(param, imageTypes, width, height);
1186             imRas = image.getRaster();
1187 
1188             // The destination may still be incompatible.
1189 
1190             numImageBands = image.getSampleModel().getNumBands();
1191 
1192             // Check whether we can handle any implied color conversion
1193 
1194             // Throws IIOException if the stream and the image are
1195             // incompatible, and sets convert if a java conversion
1196             // is necessary
1197             checkColorConversion(image, param);
1198 
1199             // Check the source and destination bands in the param
1200             checkReadParamBandSettings(param, numComponents, numImageBands);
1201         } else {
1202             // Set the output color space equal to the input colorspace
1203             // This disables all conversions
1204             setOutColorSpace(structPointer, colorSpaceCode);
1205             image = null;
1206         }
1207 
1208         // Create an intermediate 1-line Raster that will hold the decoded,
1209         // subsampled, clipped, band-selected image data in a single
1210         // byte-interleaved buffer.  The above transformations
1211         // will occur in C for performance.  Every time this Raster
1212         // is filled we will call back to acceptPixels below to copy
1213         // this to whatever kind of buffer our image has.
1214 
1215         int [] srcBands = JPEG.bandOffsets[numComponents-1];
1216         int numRasterBands = (wantRaster ? numComponents : numImageBands);
1217         destinationBands = null;
1218 
1219         Rectangle srcROI = new Rectangle(0, 0, 0, 0);
1220         destROI = new Rectangle(0, 0, 0, 0);
1221         computeRegions(param, width, height, image, srcROI, destROI);
1222 
1223         int periodX = 1;
1224         int periodY = 1;
1225 
1226         minProgressivePass = 0;
1227         maxProgressivePass = Integer.MAX_VALUE;
1228 
1229         if (param != null) {
1230             periodX = param.getSourceXSubsampling();
1231             periodY = param.getSourceYSubsampling();
1232 
1233             int[] sBands = param.getSourceBands();
1234             if (sBands != null) {
1235                 srcBands = sBands;
1236                 numRasterBands = srcBands.length;
1237             }
1238             if (!wantRaster) {  // ignore dest bands for Raster
1239                 destinationBands = param.getDestinationBands();
1240             }
1241 
1242             minProgressivePass = param.getSourceMinProgressivePass();
1243             maxProgressivePass = param.getSourceMaxProgressivePass();
1244 
1245             if (param instanceof JPEGImageReadParam) {
1246                 JPEGImageReadParam jparam = (JPEGImageReadParam) param;
1247                 if (jparam.areTablesSet()) {
1248                     abbrevQTables = jparam.getQTables();
1249                     abbrevDCHuffmanTables = jparam.getDCHuffmanTables();
1250                     abbrevACHuffmanTables = jparam.getACHuffmanTables();
1251                 }
1252             }
1253         }
1254 
1255         int lineSize = destROI.width*numRasterBands;
1256 
1257         buffer = new DataBufferByte(lineSize);
1258 
1259         int [] bandOffs = JPEG.bandOffsets[numRasterBands-1];
1260 
1261         raster = Raster.createInterleavedRaster(buffer,
1262                                                 destROI.width, 1,
1263                                                 lineSize,
1264                                                 numRasterBands,
1265                                                 bandOffs,
1266                                                 null);
1267 
1268         // Now that we have the Raster we'll decode to, get a view of the
1269         // target Raster that will permit a simple setRect for each scanline
1270         if (wantRaster) {
1271             target =  Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
1272                                                      destROI.width,
1273                                                      destROI.height,
1274                                                      lineSize,
1275                                                      numRasterBands,
1276                                                      bandOffs,
1277                                                      null);
1278         } else {
1279             target = imRas;
1280         }
1281         int [] bandSizes = target.getSampleModel().getSampleSize();
1282         for (int i = 0; i < bandSizes.length; i++) {
1283             if (bandSizes[i] <= 0 || bandSizes[i] > 8) {
1284                 throw new IIOException("Illegal band size: should be 0 < size <= 8");
1285             }
1286         }
1287 
1288         /*
1289          * If the process is sequential, and we have restart markers,
1290          * we could skip to the correct restart marker, if the library
1291          * lets us.  That's an optimization to investigate later.
1292          */
1293 
1294         // Check for update listeners (don't call back if none)
1295         boolean callbackUpdates = ((updateListeners != null)
1296                                    || (progressListeners != null));
1297 
1298         // Set up progression data
1299         initProgressData();
1300         // if we have a metadata object, we can count the scans
1301         // and set knownPassCount
1302         if (imageIndex == imageMetadataIndex) { // We have metadata
1303             knownPassCount = 0;
1304             for (Iterator<MarkerSegment> iter =
1305                     imageMetadata.markerSequence.iterator(); iter.hasNext();) {
1306                 if (iter.next() instanceof SOSMarkerSegment) {
1307                     knownPassCount++;
1308                 }
1309             }
1310         }
1311         progInterval = Math.max((target.getHeight()-1) / 20, 1);
1312         if (knownPassCount > 0) {
1313             progInterval *= knownPassCount;
1314         } else if (maxProgressivePass != Integer.MAX_VALUE) {
1315             progInterval *= (maxProgressivePass - minProgressivePass + 1);
1316         }
1317 
1318         if (debug) {
1319             System.out.println("**** Read Data *****");
1320             System.out.println("numRasterBands is " + numRasterBands);
1321             System.out.print("srcBands:");
1322             for (int i = 0; i<srcBands.length;i++)
1323                 System.out.print(" " + srcBands[i]);
1324             System.out.println();
1325             System.out.println("destination bands is " + destinationBands);
1326             if (destinationBands != null) {
1327                 for (int i = 0; i < destinationBands.length; i++) {
1328                     System.out.print(" " + destinationBands[i]);
1329                 }
1330                 System.out.println();
1331             }
1332             System.out.println("sourceROI is " + srcROI);
1333             System.out.println("destROI is " + destROI);
1334             System.out.println("periodX is " + periodX);
1335             System.out.println("periodY is " + periodY);
1336             System.out.println("minProgressivePass is " + minProgressivePass);
1337             System.out.println("maxProgressivePass is " + maxProgressivePass);
1338             System.out.println("callbackUpdates is " + callbackUpdates);
1339         }
1340 
1341         /*
1342          * All the Jpeg processing happens in native, we should clear
1343          * abortFlag of imageIODataStruct in imageioJPEG.c. And we need to
1344          * clear abortFlag because if in previous read() if we had called
1345          * reader.abort() that will continue to be valid for present call also.
1346          */
1347         clearNativeReadAbortFlag(structPointer);
1348         processImageStarted(currentImage);
1349         /*
1350          * Note that getData disables acceleration on buffer, but it is
1351          * just a 1-line intermediate data transfer buffer that will not
1352          * affect the acceleration of the resulting image.
1353          */
1354         boolean aborted = readImage(imageIndex,
1355                                     structPointer,
1356                                     buffer.getData(),
1357                                     numRasterBands,
1358                                     srcBands,
1359                                     bandSizes,
1360                                     srcROI.x, srcROI.y,
1361                                     srcROI.width, srcROI.height,
1362                                     periodX, periodY,
1363                                     abbrevQTables,
1364                                     abbrevDCHuffmanTables,
1365                                     abbrevACHuffmanTables,
1366                                     minProgressivePass, maxProgressivePass,
1367                                     callbackUpdates);
1368 
1369         if (aborted) {
1370             processReadAborted();
1371         } else {
1372             processImageComplete();
1373         }
1374 
1375         return target;
1376 
1377     }
1378 
1379     /**
1380      * This method is called back from C when the intermediate Raster
1381      * is full.  The parameter indicates the scanline in the target
1382      * Raster to which the intermediate Raster should be copied.
1383      * After the copy, we notify update listeners.
1384      */
1385     private void acceptPixels(int y, boolean progressive) {
1386         if (convert != null) {
1387             convert.filter(raster, raster);
1388         }
1389         target.setRect(destROI.x, destROI.y + y, raster);
1390 
1391         cbLock.lock();
1392         try {
1393             processImageUpdate(image,
1394                                destROI.x, destROI.y+y,
1395                                raster.getWidth(), 1,
1396                                1, 1,
1397                                destinationBands);
1398             if ((y > 0) && (y%progInterval == 0)) {
1399                 int height = target.getHeight()-1;
1400                 float percentOfPass = ((float)y)/height;
1401                 if (progressive) {
1402                     if (knownPassCount != UNKNOWN) {
1403                         processImageProgress((pass + percentOfPass)*100.0F
1404                                              / knownPassCount);
1405                     } else if (maxProgressivePass != Integer.MAX_VALUE) {
1406                         // Use the range of allowed progressive passes
1407                         processImageProgress((pass + percentOfPass)*100.0F
1408                                              / (maxProgressivePass - minProgressivePass + 1));
1409                     } else {
1410                         // Assume there are a minimum of MIN_ESTIMATED_PASSES
1411                         // and that there is always one more pass
1412                         // Compute the percentage as the percentage at the end
1413                         // of the previous pass, plus the percentage of this
1414                         // pass scaled to be the percentage of the total remaining,
1415                         // assuming a minimum of MIN_ESTIMATED_PASSES passes and
1416                         // that there is always one more pass.  This is monotonic
1417                         // and asymptotic to 1.0, which is what we need.
1418                         int remainingPasses = // including this one
1419                             Math.max(2, MIN_ESTIMATED_PASSES-pass);
1420                         int totalPasses = pass + remainingPasses-1;
1421                         progInterval = Math.max(height/20*totalPasses,
1422                                                 totalPasses);
1423                         if (y%progInterval == 0) {
1424                             percentToDate = previousPassPercentage +
1425                                 (1.0F - previousPassPercentage)
1426                                 * (percentOfPass)/remainingPasses;
1427                             if (debug) {
1428                                 System.out.print("pass= " + pass);
1429                                 System.out.print(", y= " + y);
1430                                 System.out.print(", progInt= " + progInterval);
1431                                 System.out.print(", % of pass: " + percentOfPass);
1432                                 System.out.print(", rem. passes: "
1433                                                  + remainingPasses);
1434                                 System.out.print(", prev%: "
1435                                                  + previousPassPercentage);
1436                                 System.out.print(", %ToDate: " + percentToDate);
1437                                 System.out.print(" ");
1438                             }
1439                             processImageProgress(percentToDate*100.0F);
1440                         }
1441                     }
1442                 } else {
1443                     processImageProgress(percentOfPass * 100.0F);
1444                 }
1445             }
1446         } finally {
1447             cbLock.unlock();
1448         }
1449     }
1450 
1451     private void initProgressData() {
1452         knownPassCount = UNKNOWN;
1453         pass = 0;
1454         percentToDate = 0.0F;
1455         previousPassPercentage = 0.0F;
1456         progInterval = 0;
1457     }
1458 
1459     private void passStarted (int pass) {
1460         cbLock.lock();
1461         try {
1462             this.pass = pass;
1463             previousPassPercentage = percentToDate;
1464             processPassStarted(image,
1465                                pass,
1466                                minProgressivePass,
1467                                maxProgressivePass,
1468                                0, 0,
1469                                1,1,
1470                                destinationBands);
1471         } finally {
1472             cbLock.unlock();
1473         }
1474     }
1475 
1476     private void passComplete () {
1477         cbLock.lock();
1478         try {
1479             processPassComplete(image);
1480         } finally {
1481             cbLock.unlock();
1482         }
1483     }
1484 
1485     void thumbnailStarted(int thumbnailIndex) {
1486         cbLock.lock();
1487         try {
1488             processThumbnailStarted(currentImage, thumbnailIndex);
1489         } finally {
1490             cbLock.unlock();
1491         }
1492     }
1493 
1494     // Provide access to protected superclass method
1495     void thumbnailProgress(float percentageDone) {
1496         cbLock.lock();
1497         try {
1498             processThumbnailProgress(percentageDone);
1499         } finally {
1500             cbLock.unlock();
1501         }
1502     }
1503 
1504     // Provide access to protected superclass method
1505     void thumbnailComplete() {
1506         cbLock.lock();
1507         try {
1508             processThumbnailComplete();
1509         } finally {
1510             cbLock.unlock();
1511         }
1512     }
1513 
1514     /**
1515      * Returns {@code true} if the read was aborted.
1516      */
1517     private native boolean readImage(int imageIndex,
1518                                      long structPointer,
1519                                      byte [] buffer,
1520                                      int numRasterBands,
1521                                      int [] srcBands,
1522                                      int [] bandSizes,
1523                                      int sourceXOffset, int sourceYOffset,
1524                                      int sourceWidth, int sourceHeight,
1525                                      int periodX, int periodY,
1526                                      JPEGQTable [] abbrevQTables,
1527                                      JPEGHuffmanTable [] abbrevDCHuffmanTables,
1528                                      JPEGHuffmanTable [] abbrevACHuffmanTables,
1529                                      int minProgressivePass,
1530                                      int maxProgressivePass,
1531                                      boolean wantUpdates);
1532 
1533     /*
1534      * We should call clearNativeReadAbortFlag() before we start reading
1535      * jpeg image as image processing happens at native side.
1536      */
1537     private native void clearNativeReadAbortFlag(long structPointer);
1538 
1539     public void abort() {
1540         setThreadLock();
1541         try {
1542             /**
1543              * NB: we do not check the call back lock here,
1544              * we allow to abort the reader any time.
1545              */
1546 
1547             super.abort();
1548             abortRead(structPointer);
1549         } finally {
1550             clearThreadLock();
1551         }
1552     }
1553 
1554     /** Set the C level abort flag. Keep it atomic for thread safety. */
1555     private native void abortRead(long structPointer);
1556 
1557     /** Resets library state when an exception occurred during a read. */
1558     private native void resetLibraryState(long structPointer);
1559 
1560     public boolean canReadRaster() {
1561         return true;
1562     }
1563 
1564     public Raster readRaster(int imageIndex, ImageReadParam param)
1565         throws IOException {
1566         setThreadLock();
1567         Raster retval = null;
1568         try {
1569             cbLock.check();
1570             /*
1571              * This could be further optimized by not resetting the dest.
1572              * offset and creating a translated raster in readInternal()
1573              * (see bug 4994702 for more info).
1574              */
1575 
1576             // For Rasters, destination offset is logical, not physical, so
1577             // set it to 0 before calling computeRegions, so that the destination
1578             // region is not clipped.
1579             Point saveDestOffset = null;
1580             if (param != null) {
1581                 saveDestOffset = param.getDestinationOffset();
1582                 param.setDestinationOffset(new Point(0, 0));
1583             }
1584             retval = readInternal(imageIndex, param, true);
1585             // Apply the destination offset, if any, as a logical offset
1586             if (saveDestOffset != null) {
1587                 target = target.createWritableTranslatedChild(saveDestOffset.x,
1588                                                               saveDestOffset.y);
1589             }
1590         } catch (RuntimeException e) {
1591             resetLibraryState(structPointer);
1592             throw e;
1593         } catch (IOException e) {
1594             resetLibraryState(structPointer);
1595             throw e;
1596         } finally {
1597             clearThreadLock();
1598         }
1599         return retval;
1600     }
1601 
1602     public boolean readerSupportsThumbnails() {
1603         return true;
1604     }
1605 
1606     public int getNumThumbnails(int imageIndex) throws IOException {
1607         setThreadLock();
1608         try {
1609             cbLock.check();
1610 
1611             getImageMetadata(imageIndex);  // checks iis state for us
1612             // Now check the jfif segments
1613             JFIFMarkerSegment jfif =
1614                 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1615                 (JFIFMarkerSegment.class, true);
1616             int retval = 0;
1617             if (jfif != null) {
1618                 retval = (jfif.thumb == null) ? 0 : 1;
1619                 retval += jfif.extSegments.size();
1620             }
1621             return retval;
1622         } finally {
1623             clearThreadLock();
1624         }
1625     }
1626 
1627     public int getThumbnailWidth(int imageIndex, int thumbnailIndex)
1628         throws IOException {
1629         setThreadLock();
1630         try {
1631             cbLock.check();
1632 
1633             if ((thumbnailIndex < 0)
1634                 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1635                 throw new IndexOutOfBoundsException("No such thumbnail");
1636             }
1637             // Now we know that there is a jfif segment
1638             JFIFMarkerSegment jfif =
1639                 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1640                 (JFIFMarkerSegment.class, true);
1641             return  jfif.getThumbnailWidth(thumbnailIndex);
1642         } finally {
1643             clearThreadLock();
1644         }
1645     }
1646 
1647     public int getThumbnailHeight(int imageIndex, int thumbnailIndex)
1648         throws IOException {
1649         setThreadLock();
1650         try {
1651             cbLock.check();
1652 
1653             if ((thumbnailIndex < 0)
1654                 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1655                 throw new IndexOutOfBoundsException("No such thumbnail");
1656             }
1657             // Now we know that there is a jfif segment
1658             JFIFMarkerSegment jfif =
1659                 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1660                 (JFIFMarkerSegment.class, true);
1661             return  jfif.getThumbnailHeight(thumbnailIndex);
1662         } finally {
1663             clearThreadLock();
1664         }
1665     }
1666 
1667     public BufferedImage readThumbnail(int imageIndex,
1668                                        int thumbnailIndex)
1669         throws IOException {
1670         setThreadLock();
1671         try {
1672             cbLock.check();
1673 
1674             if ((thumbnailIndex < 0)
1675                 || (thumbnailIndex >= getNumThumbnails(imageIndex))) {
1676                 throw new IndexOutOfBoundsException("No such thumbnail");
1677             }
1678             // Now we know that there is a jfif segment and that iis is good
1679             JFIFMarkerSegment jfif =
1680                 (JFIFMarkerSegment) imageMetadata.findMarkerSegment
1681                 (JFIFMarkerSegment.class, true);
1682             return  jfif.getThumbnail(iis, thumbnailIndex, this);
1683         } finally {
1684             clearThreadLock();
1685         }
1686     }
1687 
1688     private void resetInternalState() {
1689         // reset C structures
1690         resetReader(structPointer);
1691 
1692         // reset local Java structures
1693         numImages = 0;
1694         imagePositions = new ArrayList<>();
1695         currentImage = -1;
1696         image = null;
1697         raster = null;
1698         target = null;
1699         buffer = null;
1700         destROI = null;
1701         destinationBands = null;
1702         streamMetadata = null;
1703         imageMetadata = null;
1704         imageMetadataIndex = -1;
1705         haveSeeked = false;
1706         tablesOnlyChecked = false;
1707         iccCS = null;
1708         initProgressData();
1709     }
1710 
1711     public void reset() {
1712         setThreadLock();
1713         try {
1714             cbLock.check();
1715             super.reset();
1716         } finally {
1717             clearThreadLock();
1718         }
1719     }
1720 
1721     private native void resetReader(long structPointer);
1722 
1723     public void dispose() {
1724         setThreadLock();
1725         try {
1726             cbLock.check();
1727 
1728             if (structPointer != 0) {
1729                 disposerRecord.dispose();
1730                 structPointer = 0;
1731             }
1732         } finally {
1733             clearThreadLock();
1734         }
1735     }
1736 
1737     private static native void disposeReader(long structPointer);
1738 
1739     private static class JPEGReaderDisposerRecord implements DisposerRecord {
1740         private long pData;
1741 
1742         public JPEGReaderDisposerRecord(long pData) {
1743             this.pData = pData;
1744         }
1745 
1746         public synchronized void dispose() {
1747             if (pData != 0) {
1748                 disposeReader(pData);
1749                 pData = 0;
1750             }
1751         }
1752     }
1753 
1754     private Thread theThread = null;
1755     private int theLockCount = 0;
1756 
1757     private synchronized void setThreadLock() {
1758         Thread currThread = Thread.currentThread();
1759         if (theThread != null) {
1760             if (theThread != currThread) {
1761                 // it looks like that this reader instance is used
1762                 // by multiple threads.
1763                 throw new IllegalStateException("Attempt to use instance of " +
1764                                                 this + " locked on thread " +
1765                                                 theThread + " from thread " +
1766                                                 currThread);
1767             } else {
1768                 theLockCount ++;
1769             }
1770         } else {
1771             theThread = currThread;
1772             theLockCount = 1;
1773         }
1774     }
1775 
1776     private synchronized void clearThreadLock() {
1777         Thread currThread = Thread.currentThread();
1778         if (theThread == null || theThread != currThread) {
1779             throw new IllegalStateException("Attempt to clear thread lock " +
1780                                             " form wrong thread." +
1781                                             " Locked thread: " + theThread +
1782                                             "; current thread: " + currThread);
1783         }
1784         theLockCount --;
1785         if (theLockCount == 0) {
1786             theThread = null;
1787         }
1788     }
1789 
1790     private CallBackLock cbLock = new CallBackLock();
1791 
1792     private static class CallBackLock {
1793 
1794         private State lockState;
1795 
1796         CallBackLock() {
1797             lockState = State.Unlocked;
1798         }
1799 
1800         void check() {
1801             if (lockState != State.Unlocked) {
1802                 throw new IllegalStateException("Access to the reader is not allowed");
1803             }
1804         }
1805 
1806         private void lock() {
1807             lockState = State.Locked;
1808         }
1809 
1810         private void unlock() {
1811             lockState = State.Unlocked;
1812         }
1813 
1814         private static enum State {
1815             Unlocked,
1816             Locked
1817         }
1818     }
1819 }
1820 
1821 /**
1822  * An internal helper class that wraps producer's iterator
1823  * and extracts specifier instances on demand.
1824  */
1825 class ImageTypeIterator implements Iterator<ImageTypeSpecifier> {
1826      private Iterator<ImageTypeProducer> producers;
1827      private ImageTypeSpecifier theNext = null;
1828 
1829      public ImageTypeIterator(Iterator<ImageTypeProducer> producers) {
1830          this.producers = producers;
1831      }
1832 
1833      public boolean hasNext() {
1834          if (theNext != null) {
1835              return true;
1836          }
1837          if (!producers.hasNext()) {
1838              return false;
1839          }
1840          do {
1841              theNext = producers.next().getType();
1842          } while (theNext == null && producers.hasNext());
1843 
1844          return (theNext != null);
1845      }
1846 
1847      public ImageTypeSpecifier next() {
1848          if (theNext != null || hasNext()) {
1849              ImageTypeSpecifier t = theNext;
1850              theNext = null;
1851              return t;
1852          } else {
1853              throw new NoSuchElementException();
1854          }
1855      }
1856 
1857      public void remove() {
1858          producers.remove();
1859      }
1860 }
1861 
1862 /**
1863  * An internal helper class that provides means for deferred creation
1864  * of ImageTypeSpecifier instance required to describe available
1865  * destination types.
1866  *
1867  * This implementation only supports standard
1868  * jpeg color spaces (defined by corresponding JCS color space code).
1869  *
1870  * To support other color spaces one can override produce() method to
1871  * return custom instance of ImageTypeSpecifier.
1872  */
1873 class ImageTypeProducer {
1874 
1875     private ImageTypeSpecifier type = null;
1876     boolean failed = false;
1877     private int csCode;
1878 
1879     public ImageTypeProducer(int csCode) {
1880         this.csCode = csCode;
1881     }
1882 
1883     public ImageTypeProducer() {
1884         csCode = -1; // undefined
1885     }
1886 
1887     public synchronized ImageTypeSpecifier getType() {
1888         if (!failed && type == null) {
1889             try {
1890                 type = produce();
1891             } catch (Throwable e) {
1892                 failed = true;
1893             }
1894         }
1895         return type;
1896     }
1897 
1898     private static final ImageTypeProducer [] defaultTypes =
1899             new ImageTypeProducer [JPEG.NUM_JCS_CODES];
1900 
1901     public static synchronized ImageTypeProducer getTypeProducer(int csCode) {
1902         if (csCode < 0 || csCode >= JPEG.NUM_JCS_CODES) {
1903             return null;
1904         }
1905         if (defaultTypes[csCode] == null) {
1906             defaultTypes[csCode] = new ImageTypeProducer(csCode);
1907         }
1908         return defaultTypes[csCode];
1909     }
1910 
1911     protected ImageTypeSpecifier produce() {
1912         switch (csCode) {
1913             case JPEG.JCS_GRAYSCALE:
1914                 return ImageTypeSpecifier.createFromBufferedImageType
1915                         (BufferedImage.TYPE_BYTE_GRAY);
1916             case JPEG.JCS_YCbCr:
1917             //there is no YCbCr raw type so by default we assume it as RGB
1918             case JPEG.JCS_RGB:
1919                 return ImageTypeSpecifier.createInterleaved(JPEG.JCS.sRGB,
1920                         JPEG.bOffsRGB,
1921                         DataBuffer.TYPE_BYTE,
1922                         false,
1923                         false);
1924             case JPEG.JCS_RGBA:
1925                 return ImageTypeSpecifier.createPacked(JPEG.JCS.sRGB,
1926                         0xff000000,
1927                         0x00ff0000,
1928                         0x0000ff00,
1929                         0x000000ff,
1930                         DataBuffer.TYPE_INT,
1931                         false);
1932             case JPEG.JCS_YCC:
1933                 if (JPEG.JCS.getYCC() != null) {
1934                     return ImageTypeSpecifier.createInterleaved(
1935                             JPEG.JCS.getYCC(),
1936                         JPEG.bandOffsets[2],
1937                         DataBuffer.TYPE_BYTE,
1938                         false,
1939                         false);
1940                 } else {
1941                     return null;
1942                 }
1943             case JPEG.JCS_YCCA:
1944                 if (JPEG.JCS.getYCC() != null) {
1945                     return ImageTypeSpecifier.createInterleaved(
1946                             JPEG.JCS.getYCC(),
1947                         JPEG.bandOffsets[3],
1948                         DataBuffer.TYPE_BYTE,
1949                         true,
1950                         false);
1951                 } else {
1952                     return null;
1953                 }
1954             default:
1955                 return null;
1956         }
1957     }
1958 }