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