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