1675 lines · 58.8 KB · Java
RawDownload
/* * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.imageio.plugins.jpeg; import javax.imageio.IIOException;import javax.imageio.ImageReader;import javax.imageio.ImageReadParam;import javax.imageio.ImageTypeSpecifier;import javax.imageio.metadata.IIOMetadata;import javax.imageio.spi.ImageReaderSpi;import javax.imageio.stream.ImageInputStream;import javax.imageio.plugins.jpeg.JPEGImageReadParam;import javax.imageio.plugins.jpeg.JPEGQTable;import javax.imageio.plugins.jpeg.JPEGHuffmanTable; import java.awt.Point;import java.awt.Rectangle;import java.awt.color.ColorSpace;import java.awt.color.ICC_Profile;import java.awt.color.ICC_ColorSpace;import java.awt.color.CMMException;import java.awt.image.BufferedImage;import java.awt.image.Raster;import java.awt.image.WritableRaster;import java.awt.image.DataBuffer;import java.awt.image.DataBufferByte;import java.awt.image.ColorModel;import java.awt.image.IndexColorModel;import java.awt.image.ColorConvertOp;import java.io.IOException;import java.util.List;import java.util.Iterator;import java.util.ArrayList;import java.util.NoSuchElementException; import sun.java2d.Disposer;import sun.java2d.DisposerRecord; public class JPEGImageReader extends ImageReader {     private boolean debug = false;     /**     * The following variable contains a pointer to the IJG library     * structure for this reader.  It is assigned in the constructor     * and then is passed in to every native call.  It is set to 0     * by dispose to avoid disposing twice.     */    private long structPointer = 0;     /** The input stream we read from */    private ImageInputStream iis = null;     /**     * List of stream positions for images, reinitialized every time     * a new input source is set.     */    private List imagePositions = null;     /**     * The number of images in the stream, or 0.     */    private int numImages = 0;     static {        java.security.AccessController.doPrivileged(            new sun.security.action.LoadLibraryAction("jpeg"));        initReaderIDs(ImageInputStream.class,                      JPEGQTable.class,                      JPEGHuffmanTable.class);    }     // The following warnings are converted to strings when used    // as keys to get localized resources from JPEGImageReaderResources    // and its children.     /**     * Warning code to be passed to warningOccurred to indicate     * that the EOI marker is missing from the end of the stream.     * This usually signals that the stream is corrupted, but     * everything up to the last MCU should be usable.     */    protected static final int WARNING_NO_EOI = 0;     /**     * Warning code to be passed to warningOccurred to indicate     * that a JFIF segment was encountered inside a JFXX JPEG     * thumbnail and is being ignored.     */    protected static final int WARNING_NO_JFIF_IN_THUMB = 1;     /**     * Warning code to be passed to warningOccurred to indicate     * that embedded ICC profile is invalid and will be ignored.     */    protected static final int WARNING_IGNORE_INVALID_ICC = 2;     private static final int MAX_WARNING = WARNING_IGNORE_INVALID_ICC;     /**     * Image index of image for which header information     * is available.     */    private int currentImage = -1;     // The following is copied out from C after reading the header.    // Unlike metadata, which may never be retrieved, we need this    // if we are to read an image at all.     /** Set by setImageData native code callback */    private int width;    /** Set by setImageData native code callback */    private int height;    /**     * Set by setImageData native code callback.  A modified     * IJG+NIFTY colorspace code.     */    private int colorSpaceCode;    /**     * Set by setImageData native code callback.  A modified     * IJG+NIFTY colorspace code.     */    private int outColorSpaceCode;    /** Set by setImageData native code callback */    private int numComponents;    /** Set by setImageData native code callback */    private ColorSpace iccCS = null;      /** If we need to post-convert in Java, convert with this op */    private ColorConvertOp convert = null;     /** The image we are going to fill */    private BufferedImage image = null;     /** An intermediate Raster to hold decoded data */    private WritableRaster raster = null;     /** A view of our target Raster that we can setRect to */    private WritableRaster target = null;     /** The databuffer for the above Raster */    private DataBufferByte buffer = null;     /** The region in the destination where we will write pixels */    private Rectangle destROI = null;     /** The list of destination bands, if any */    private int [] destinationBands = null;     /** Stream metadata, cached, even when the stream is changed. */    private JPEGMetadata streamMetadata = null;     /** Image metadata, valid for the imageMetadataIndex only. */    private JPEGMetadata imageMetadata = null;    private int imageMetadataIndex = -1;     /**     * Set to true every time we seek in the stream; used to     * invalidate the native buffer contents in C.     */    private boolean haveSeeked = false;     /**     * Tables that have been read from a tables-only image at the     * beginning of a stream.     */    private JPEGQTable [] abbrevQTables = null;    private JPEGHuffmanTable[] abbrevDCHuffmanTables = null;    private JPEGHuffmanTable[] abbrevACHuffmanTables = null;     private int minProgressivePass = 0;    private int maxProgressivePass = Integer.MAX_VALUE;     /**     * Variables used by progress monitoring.     */    private static final int UNKNOWN = -1;  // Number of passes    private static final int MIN_ESTIMATED_PASSES = 10; // IJG default    private int knownPassCount = UNKNOWN;    private int pass = 0;    private float percentToDate = 0.0F;    private float previousPassPercentage = 0.0F;    private int progInterval = 0;     /**     * Set to true once stream has been checked for stream metadata     */    private boolean tablesOnlyChecked = false;     /** The referent to be registered with the Disposer. */    private Object disposerReferent = new Object();     /** The DisposerRecord that handles the actual disposal of this reader. */    private DisposerRecord disposerRecord;     /** Sets up static C structures. */    private static native void initReaderIDs(Class iisClass,                                             Class qTableClass,                                             Class huffClass);     public JPEGImageReader(ImageReaderSpi originator) {        super(originator);        structPointer = initJPEGImageReader();        disposerRecord = new JPEGReaderDisposerRecord(structPointer);        Disposer.addRecord(disposerReferent, disposerRecord);    }     /** Sets up per-reader C structure and returns a pointer to it. */    private native long initJPEGImageReader();     /**     * Called by the native code or other classes to signal a warning.     * The code is used to lookup a localized message to be used when     * sending warnings to listeners.     */    protected void warningOccurred(int code) {        if ((code < 0) || (code > MAX_WARNING)){            throw new InternalError("Invalid warning index");        }        processWarningOccurred            ("com.sun.imageio.plugins.jpeg.JPEGImageReaderResources",             Integer.toString(code));    }     /**     * The library has it's own error facility that emits warning messages.     * This routine is called by the native code when it has already     * formatted a string for output.     * XXX  For truly complete localization of all warning messages,     * the sun_jpeg_output_message routine in the native code should     * send only the codes and parameters to a method here in Java,     * which will then format and send the warnings, using localized     * strings.  This method will have to deal with all the parameters     * and formats (%u with possibly large numbers, %02d, %02x, etc.)     * that actually occur in the JPEG library.  For now, this prevents     * library warnings from being printed to stderr.     */    protected void warningWithMessage(String msg) {        processWarningOccurred(msg);    }     public void setInput(Object input,                         boolean seekForwardOnly,                         boolean ignoreMetadata)    {        setThreadLock();        try {            super.setInput(input, seekForwardOnly, ignoreMetadata);            this.ignoreMetadata = ignoreMetadata;            resetInternalState();            iis = (ImageInputStream) input; // Always works            setSource(structPointer, iis);        } finally {            clearThreadLock();        }    }     private native void setSource(long structPointer,                                  ImageInputStream source);     private void checkTablesOnly() throws IOException {        if (debug) {            System.out.println("Checking for tables-only image");        }        long savePos = iis.getStreamPosition();        if (debug) {            System.out.println("saved pos is " + savePos);            System.out.println("length is " + iis.length());        }        // Read the first header        boolean tablesOnly = readNativeHeader(true);        if (tablesOnly) {            if (debug) {                System.out.println("tables-only image found");                long pos = iis.getStreamPosition();                System.out.println("pos after return from native is " + pos);            }            // This reads the tables-only image twice, once from C            // and once from Java, but only if ignoreMetadata is false            if (ignoreMetadata == false) {                iis.seek(savePos);                haveSeeked = true;                streamMetadata = new JPEGMetadata(true, false,                                                  iis, this);                long pos = iis.getStreamPosition();                if (debug) {                    System.out.println                        ("pos after constructing stream metadata is " + pos);                }            }            // Now we are at the first image if there are any, so add it            // to the list            if (hasNextImage()) {                imagePositions.add(new Long(iis.getStreamPosition()));            }        } else { // Not tables only, so add original pos to the list            imagePositions.add(new Long(savePos));            // And set current image since we've read it now            currentImage = 0;        }        if (seekForwardOnly) {            Long pos = (Long) imagePositions.get(imagePositions.size()-1);            iis.flushBefore(pos.longValue());        }        tablesOnlyChecked = true;    }     public int getNumImages(boolean allowSearch) throws IOException {        setThreadLock();        try { // locked thread            return getNumImagesOnThread(allowSearch);        } finally {            clearThreadLock();        }    }     private int getNumImagesOnThread(boolean allowSearch)      throws IOException {        if (numImages != 0) {            return numImages;        }        if (iis == null) {            throw new IllegalStateException("Input not set");        }        if (allowSearch == true) {            if (seekForwardOnly) {                throw new IllegalStateException(                    "seekForwardOnly and allowSearch can't both be true!");            }            // Otherwise we have to read the entire stream             if (!tablesOnlyChecked) {                checkTablesOnly();            }             iis.mark();             gotoImage(0);             JPEGBuffer buffer = new JPEGBuffer(iis);            buffer.loadBuf(0);             boolean done = false;            while (!done) {                done = buffer.scanForFF(this);                switch (buffer.buf[buffer.bufPtr] & 0xff) {                case JPEG.SOI:                    numImages++;                    // FALL THROUGH to decrement buffer vars                    // This first set doesn't have a length                case 0: // not a marker, just a data 0xff                case JPEG.RST0:                case JPEG.RST1:                case JPEG.RST2:                case JPEG.RST3:                case JPEG.RST4:                case JPEG.RST5:                case JPEG.RST6:                case JPEG.RST7:                case JPEG.EOI:                    buffer.bufAvail--;                    buffer.bufPtr++;                    break;                    // All the others have a length                default:                    buffer.bufAvail--;                    buffer.bufPtr++;                    buffer.loadBuf(2);                    int length = ((buffer.buf[buffer.bufPtr++] & 0xff) << 8) |                        (buffer.buf[buffer.bufPtr++] & 0xff);                    buffer.bufAvail -= 2;                    length -= 2; // length includes itself                    buffer.skipData(length);                }            }              iis.reset();             return numImages;        }         return -1;  // Search is necessary for JPEG    }     /**     * Sets the input stream to the start of the requested image.     * <pre>     * @exception IllegalStateException if the input source has not been     * set.     * @exception IndexOutOfBoundsException if the supplied index is     * out of bounds.     * </pre>     */    private void gotoImage(int imageIndex) throws IOException {        if (iis == null) {            throw new IllegalStateException("Input not set");        }        if (imageIndex < minIndex) {            throw new IndexOutOfBoundsException();        }        if (!tablesOnlyChecked) {            checkTablesOnly();        }        if (imageIndex < imagePositions.size()) {            iis.seek(((Long)(imagePositions.get(imageIndex))).longValue());        } else {            // read to start of image, saving positions            // First seek to the last position we already have, and skip the            // entire image            Long pos = (Long) imagePositions.get(imagePositions.size()-1);            iis.seek(pos.longValue());            skipImage();            // Now add all intervening positions, skipping images            for (int index = imagePositions.size();                 index <= imageIndex;                 index++) {                // Is there an image?                if (!hasNextImage()) {                    throw new IndexOutOfBoundsException();                }                pos = new Long(iis.getStreamPosition());                imagePositions.add(pos);                if (seekForwardOnly) {                    iis.flushBefore(pos.longValue());                }                if (index < imageIndex) {                    skipImage();                }  // Otherwise we are where we want to be            }        }         if (seekForwardOnly) {            minIndex = imageIndex;        }         haveSeeked = true;  // No way is native buffer still valid    }     /**     * Skip over a complete image in the stream, leaving the stream     * positioned such that the next byte to be read is the first     * byte of the next image.  For JPEG, this means that we read     * until we encounter an EOI marker or until the end of the stream.     * If the stream ends before an EOI marker is encountered, an     * IndexOutOfBoundsException is thrown.     */    private void skipImage() throws IOException {        if (debug) {            System.out.println("skipImage called");        }        boolean foundFF = false;        for (int byteval = iis.read();             byteval != -1;             byteval = iis.read()) {             if (foundFF == true) {                if (byteval == JPEG.EOI) {                    return;                }            }            foundFF = (byteval == 0xff) ? true : false;        }        throw new IndexOutOfBoundsException();    }     /**     * Returns <code>true</code> if there is an image beyond     * the current stream position.  Does not disturb the     * stream position.     */    private boolean hasNextImage() throws IOException {        if (debug) {            System.out.print("hasNextImage called; returning ");        }        iis.mark();

Lines 1–500 of 1675.