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