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