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