|
Java example source code file (JPEGImageReader.java)
The JPEGImageReader.java Java example source code
/*
* Copyright (c) 2000, 2013, 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 java.security.PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("jpeg");
return null;
}
});
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) {
cbLock.lock();
try {
if ((code < 0) || (code > MAX_WARNING)){
throw new InternalError("Invalid warning index");
}
processWarningOccurred
("com.sun.imageio.plugins.jpeg.JPEGImageReaderResources",
Integer.toString(code));
} finally {
cbLock.unlock();
}
}
/**
* 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) {
cbLock.lock();
try {
processWarningOccurred(msg);
} finally {
cbLock.unlock();
}
}
public void setInput(Object input,
boolean seekForwardOnly,
boolean ignoreMetadata)
{
setThreadLock();
try {
cbLock.check();
super.setInput(input, seekForwardOnly, ignoreMetadata);
this.ignoreMetadata = ignoreMetadata;
resetInternalState();
iis = (ImageInputStream) input; // Always works
setSource(structPointer);
} finally {
clearThreadLock();
}
}
/**
* This method is called from native code in order to fill
* native input buffer.
*
* We block any attempt to change the reading state during this
* method, in order to prevent a corruption of the native decoder
* state.
*
* @return number of bytes read from the stream.
*/
private int readInputData(byte[] buf, int off, int len) throws IOException {
cbLock.lock();
try {
return iis.read(buf, off, len);
} finally {
cbLock.unlock();
}
}
/**
* This method is called from the native code in order to
* skip requested number of bytes in the input stream.
*
* @param n
* @return
* @throws IOException
*/
private long skipInputBytes(long n) throws IOException {
cbLock.lock();
try {
return iis.skipBytes(n);
} finally {
cbLock.unlock();
}
}
private native void setSource(long structPointer);
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
cbLock.check();
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 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();
boolean foundFF = false;
for (int byteval = iis.read();
byteval != -1;
byteval = iis.read()) {
if (foundFF == true) {
if (byteval == JPEG.SOI) {
iis.reset();
if (debug) {
System.out.println("true");
}
return true;
}
}
foundFF = (byteval == 0xff) ? true : false;
}
// We hit the end of the stream before we hit an SOI, so no image
iis.reset();
if (debug) {
System.out.println("false");
}
return false;
}
/**
* Push back the given number of bytes to the input stream.
* Called by the native code at the end of each image so
* that the next one can be identified from Java.
*/
private void pushBack(int num) throws IOException {
if (debug) {
System.out.println("pushing back " + num + " bytes");
}
cbLock.lock();
try {
iis.seek(iis.getStreamPosition()-num);
// The buffer is clear after this, so no need to set haveSeeked.
} finally {
cbLock.unlock();
}
}
/**
* Reads header information for the given image, if possible.
*/
private void readHeader(int imageIndex, boolean reset)
throws IOException {
gotoImage(imageIndex);
readNativeHeader(reset); // Ignore return
currentImage = imageIndex;
}
private boolean readNativeHeader(boolean reset) throws IOException {
boolean retval = false;
retval = readImageHeader(structPointer, haveSeeked, reset);
haveSeeked = false;
return retval;
}
/**
* Read in the header information starting from the current
* stream position, returning <code>true if the
* header was a tables-only image. After this call, the
* native IJG decompression struct will contain the image
* information required by most query calls below
* (e.g. getWidth, getHeight, etc.), if the header was not
* a tables-only image.
* If reset is <code>true, the state of the IJG
* object is reset so that it can read a header again.
* This happens automatically if the header was a tables-only
* image.
*/
private native boolean readImageHeader(long structPointer,
boolean clearBuffer,
boolean reset)
throws IOException;
/*
* Called by the native code whenever an image header has been
* read. Whether we read metadata or not, we always need this
* information, so it is passed back independently of
* metadata, which may never be read.
*/
private void setImageData(int width,
int height,
int colorSpaceCode,
int outColorSpaceCode,
int numComponents,
byte [] iccData) {
this.width = width;
this.height = height;
this.colorSpaceCode = colorSpaceCode;
this.outColorSpaceCode = outColorSpaceCode;
this.numComponents = numComponents;
if (iccData == null) {
iccCS = null;
return;
}
ICC_Profile newProfile = null;
try {
newProfile = ICC_Profile.getInstance(iccData);
} catch (IllegalArgumentException e) {
/*
* Color profile data seems to be invalid.
* Ignore this profile.
*/
iccCS = null;
warningOccurred(WARNING_IGNORE_INVALID_ICC);
return;
}
byte[] newData = newProfile.getData();
ICC_Profile oldProfile = null;
if (iccCS instanceof ICC_ColorSpace) {
oldProfile = ((ICC_ColorSpace)iccCS).getProfile();
}
byte[] oldData = null;
if (oldProfile != null) {
oldData = oldProfile.getData();
}
/*
* At the moment we can't rely on the ColorSpace.equals()
* and ICC_Profile.equals() because they do not detect
* the case when two profiles are created from same data.
*
* So, we have to do data comparison in order to avoid
* creation of different ColorSpace instances for the same
* embedded data.
*/
if (oldData == null ||
!java.util.Arrays.equals(oldData, newData))
{
iccCS = new ICC_ColorSpace(newProfile);
// verify new color space
try {
float[] colors = iccCS.fromRGB(new float[] {1f, 0f, 0f});
} catch (CMMException e) {
/*
* Embedded profile seems to be corrupted.
* Ignore this profile.
*/
iccCS = null;
cbLock.lock();
try {
warningOccurred(WARNING_IGNORE_INVALID_ICC);
} finally {
cbLock.unlock();
}
}
}
}
public int getWidth(int imageIndex) throws IOException {
setThreadLock();
try {
if (currentImage != imageIndex) {
cbLock.check();
readHeader(imageIndex, true);
}
return width;
} finally {
clearThreadLock();
}
}
public int getHeight(int imageIndex) throws IOException {
setThreadLock();
try {
if (currentImage != imageIndex) {
cbLock.check();
readHeader(imageIndex, true);
}
return height;
} finally {
clearThreadLock();
}
}
/////////// Color Conversion and Image Types
/**
* Return an ImageTypeSpecifier corresponding to the given
* color space code, or null if the color space is unsupported.
*/
private ImageTypeProducer getImageType(int code) {
ImageTypeProducer ret = null;
if ((code > 0) && (code < JPEG.NUM_JCS_CODES)) {
ret = ImageTypeProducer.getTypeProducer(code);
}
return ret;
}
public ImageTypeSpecifier getRawImageType(int imageIndex)
throws IOException {
setThreadLock();
try {
if (currentImage != imageIndex) {
cbLock.check();
readHeader(imageIndex, true);
}
// Returns null if it can't be represented
return getImageType(colorSpaceCode).getType();
} finally {
clearThreadLock();
}
}
public Iterator getImageTypes(int imageIndex)
throws IOException {
setThreadLock();
try {
return getImageTypesOnThread(imageIndex);
} finally {
clearThreadLock();
}
}
private Iterator getImageTypesOnThread(int imageIndex)
throws IOException {
if (currentImage != imageIndex) {
cbLock.check();
readHeader(imageIndex, true);
}
// We return an iterator containing the default, any
// conversions that the library provides, and
// all the other default types with the same number
// of components, as we can do these as a post-process.
// As we convert Rasters rather than images, images
// with alpha cannot be converted in a post-process.
// If this image can't be interpreted, this method
// returns an empty Iterator.
// Get the raw ITS, if there is one. Note that this
// won't always be the same as the default.
ImageTypeProducer raw = getImageType(colorSpaceCode);
// Given the encoded colorspace, build a list of ITS's
// representing outputs you could handle starting
// with the default.
ArrayList<ImageTypeProducer> list = new ArrayList
Other Java examples (source code examples)Here is a short list of links related to this Java JPEGImageReader.java source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.