/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
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.
*/
/** The input stream we read from */
/**
* List of stream positions for images, reinitialized every time
* a new input source is set.
*/
/**
* The number of images in the stream, or 0.
*/
static {
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.
*/
/**
* Warning code to be passed to warningOccurred to indicate
* that a JFIF segment was encountered inside a JFXX JPEG
* thumbnail and is being ignored.
*/
/**
* Warning code to be passed to warningOccurred to indicate
* that embedded ICC profile is invalid and will be ignored.
*/
/**
* Image index of image for which header information
* is available.
*/
// 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 */
/** If we need to post-convert in Java, convert with this op */
/** The image we are going to fill */
/** An intermediate Raster to hold decoded data */
/** A view of our target Raster that we can setRect to */
/** The databuffer for the above Raster */
/** The region in the destination where we will write pixels */
/** The list of destination bands, if any */
/** Stream metadata, cached, even when the stream is changed. */
/** Image metadata, valid for the imageMetadataIndex only. */
/**
* 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.
*/
/**
* Variables used by progress monitoring.
*/
/**
* Set to true once stream has been checked for stream metadata
*/
private boolean tablesOnlyChecked = false;
/** The referent to be registered with the Disposer. */
/** The DisposerRecord that handles the actual disposal of this reader. */
/** Sets up static C structures. */
super(originator);
}
/** 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.
*/
try {
throw new InternalError("Invalid warning index");
}
("com.sun.imageio.plugins.jpeg.JPEGImageReaderResources",
} finally {
}
}
/**
* 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.
*/
try {
} finally {
}
}
boolean seekForwardOnly,
boolean ignoreMetadata)
{
try {
this.ignoreMetadata = ignoreMetadata;
} finally {
}
}
/**
* 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.
*/
try {
} finally {
}
}
/**
* 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
*/
try {
} finally {
}
}
if (debug) {
}
if (debug) {
}
// Read the first header
boolean tablesOnly = readNativeHeader(true);
if (tablesOnly) {
if (debug) {
}
// This reads the tables-only image twice, once from C
// and once from Java, but only if ignoreMetadata is false
if (ignoreMetadata == false) {
haveSeeked = true;
streamMetadata = new JPEGMetadata(true, false,
iis, this);
if (debug) {
("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()) {
}
} else { // Not tables only, so add original pos to the list
// And set current image since we've read it now
currentImage = 0;
}
if (seekForwardOnly) {
}
tablesOnlyChecked = true;
}
try { // locked thread
return getNumImagesOnThread(allowSearch);
} finally {
}
}
throws IOException {
if (numImages != 0) {
return numImages;
}
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) {
}
gotoImage(0);
boolean done = false;
while (!done) {
numImages++;
// FALL THROUGH to decrement buffer vars
// This first set doesn't have a length
case 0: // not a marker, just a data 0xff
break;
// All the others have a length
default:
}
}
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>
*/
throw new IllegalStateException("Input not set");
}
if (imageIndex < minIndex) {
throw new IndexOutOfBoundsException();
}
if (!tablesOnlyChecked) {
}
} else {
// read to start of image, saving positions
// First seek to the last position we already have, and skip the
// entire image
skipImage();
// Now add all intervening positions, skipping images
index <= imageIndex;
index++) {
// Is there an image?
if (!hasNextImage()) {
throw new IndexOutOfBoundsException();
}
if (seekForwardOnly) {
}
if (index < imageIndex) {
skipImage();
} // Otherwise we are where we want to be
}
}
if (seekForwardOnly) {
}
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.
*/
if (debug) {
}
boolean foundFF = false;
byteval != -1;
if (foundFF == true) {
return;
}
}
}
throw new IndexOutOfBoundsException();
}
/**
* Returns <code>true</code> if there is an image beyond
* the current stream position. Does not disturb the
* stream position.
*/
if (debug) {
}
boolean foundFF = false;
byteval != -1;
if (foundFF == true) {
if (debug) {
}
return true;
}
}
}
// We hit the end of the stream before we hit an SOI, so no image
if (debug) {
}
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.
*/
if (debug) {
}
try {
// The buffer is clear after this, so no need to set haveSeeked.
} finally {
}
}
/**
* Reads header information for the given image, if possible.
*/
throws IOException {
}
boolean retval = false;
haveSeeked = false;
return retval;
}
/**
* Read in the header information starting from the current
* stream position, returning <code>true</code> 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</code>, 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.
*/
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.
*/
int height,
int colorSpaceCode,
int outColorSpaceCode,
int numComponents,
byte [] iccData) {
this.colorSpaceCode = colorSpaceCode;
this.outColorSpaceCode = outColorSpaceCode;
this.numComponents = numComponents;
return;
}
try {
} catch (IllegalArgumentException e) {
/*
* Color profile data seems to be invalid.
* Ignore this profile.
*/
return;
}
if (iccCS instanceof ICC_ColorSpace) {
}
if (oldProfile != null) {
}
/*
* 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.
*/
{
// verify new color space
try {
} catch (CMMException e) {
/*
* Embedded profile seems to be corrupted.
* Ignore this profile.
*/
try {
} finally {
}
}
}
}
try {
if (currentImage != imageIndex) {
readHeader(imageIndex, true);
}
return width;
} finally {
}
}
try {
if (currentImage != imageIndex) {
readHeader(imageIndex, true);
}
return height;
} finally {
}
}
/////////// Color Conversion and Image Types
/**
* Return an ImageTypeSpecifier corresponding to the given
* color space code, or null if the color space is unsupported.
*/
}
return ret;
}
throws IOException {
try {
if (currentImage != imageIndex) {
readHeader(imageIndex, true);
}
// Returns null if it can't be represented
} finally {
}
}
throws IOException {
try {
return getImageTypesOnThread(imageIndex);
} finally {
}
}
throws IOException {
if (currentImage != imageIndex) {
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.
// Given the encoded colorspace, build a list of ITS's
// representing outputs you could handle starting
// with the default.
switch (colorSpaceCode) {
case JPEG.JCS_GRAYSCALE:
break;
break;
break;
}
break;
}
break;
// As there is no YCbCr ColorSpace, we can't support
// the raw type.
// due to 4705399, use RGB as default in order to avoid
// slowing down of drawing operations with result image.
protected ImageTypeSpecifier produce() {
(iccCS,
false,
false);
}
});
}
break;
// As there is no YCbCr ColorSpace, we can't support
// the raw type.
break;
}
}
/**
* Checks the implied color conversion between the stream and
* the target image, altering the IJG output color space if necessary.
* If a java color conversion is required, then this sets up
* <code>convert</code>.
* If bands are being rearranged at all (either source or destination
* bands are specified in the param), then the default color
* conversions are assumed to be correct.
* Throws an IIOException if there is no conversion available.
*/
throws IIOException {
// If we are rearranging channels at all, the default
// conversions remain in place. If the user wants
// raw channels then he should do this while reading
// a Raster.
// Accept default conversions out of decoder, silently
return;
}
}
// XXX - We do not currently support any indexed color models,
// though we could, as IJG will quantize for us.
// This is a performance and memory-use issue, as
// users can read RGB and then convert to indexed in Java.
if (cm instanceof IndexColorModel) {
throw new IIOException("IndexColorModel not supported");
}
// Now check the ColorSpace type against outColorSpaceCode
// We may want to tweak the default
switch (outColorSpaceCode) {
// IJG can do this for us more efficiently
// Update java state according to changes
// in the native part of decoder.
numComponents = 3;
throw new IIOException("Incompatible color conversion");
}
break;
// If the jpeg space is YCbCr, IJG can do it
// Update java state according to changes
// in the native part of decoder.
numComponents = 1;
}
// We have an ICC profile but it isn't used in the dest
// image. So convert from the profile cs to the target cs
// Leave IJG conversion in place; we still need it
// Target isn't sRGB, so convert from sRGB to the target
throw new IIOException("Incompatible color conversion");
}
break;
// No conversions available; image must be RGBA
throw new IIOException("Incompatible color conversion");
}
break;
{
throw new IIOException("Incompatible color conversion");
}
}
}
break;
{
// No conversions available; image must be YCCA
throw new IIOException("Incompatible color conversion");
}
}
break;
default:
// Anything else we can't handle at all
throw new IIOException("Incompatible color conversion");
}
}
/**
* Set the IJG output space to the given value. The library will
* perform the appropriate colorspace conversions.
*/
/////// End of Color Conversion & Image Types
return new JPEGImageReadParam();
}
try {
if (!tablesOnlyChecked) {
}
return streamMetadata;
} finally {
}
}
throws IOException {
try {
// imageMetadataIndex will always be either a valid index or
// -1, in which case imageMetadata will not be null.
// So we can leave checking imageIndex for gotoImage.
if ((imageMetadataIndex == imageIndex)
&& (imageMetadata != null)) {
return imageMetadata;
}
return imageMetadata;
} finally {
}
}
throws IOException {
try {
try {
} catch (RuntimeException e) {
throw e;
} catch (IOException e) {
throw e;
}
return ret;
} finally {
}
}
boolean wantRaster) throws IOException {
readHeader(imageIndex, false);
int numImageBands = 0;
if (!wantRaster){
// Can we read this image?
if (imageTypes.hasNext() == false) {
throw new IIOException("Unsupported Image Type");
}
// The destination may still be incompatible.
// Check whether we can handle any implied color conversion
// Throws IIOException if the stream and the image are
// incompatible, and sets convert if a java conversion
// is necessary
// Check the source and destination bands in the param
} else {
// Set the output color space equal to the input colorspace
// This disables all conversions
}
// Create an intermediate 1-line Raster that will hold the decoded,
// subsampled, clipped, band-selected image data in a single
// byte-interleaved buffer. The above transformations
// will occur in C for performance. Every time this Raster
// is filled we will call back to acceptPixels below to copy
// this to whatever kind of buffer our image has.
int periodX = 1;
int periodY = 1;
minProgressivePass = 0;
}
if (!wantRaster) { // ignore dest bands for Raster
}
if (param instanceof JPEGImageReadParam) {
if (jparam.areTablesSet()) {
}
}
}
null);
// Now that we have the Raster we'll decode to, get a view of the
// target Raster that will permit a simple setRect for each scanline
if (wantRaster) {
null);
} else {
}
/*
* If the process is sequential, and we have restart markers,
* we could skip to the correct restart marker, if the library
* lets us. That's an optimization to investigate later.
*/
// Check for update listeners (don't call back if none)
|| (progressListeners != null));
// Set up progression data
// if we have a metadata object, we can count the scans
// and set knownPassCount
knownPassCount = 0;
}
}
}
if (knownPassCount > 0) {
}
if (debug) {
if (destinationBands != null) {
}
}
}
// Finally, we are ready to read
boolean aborted = false;
// Note that getData disables acceleration on buffer, but it is
// just a 1-line intermediate data transfer buffer that will not
// affect the acceleration of the resulting image.
if (aborted) {
} else {
}
return target;
}
/**
* This method is called back from C when the intermediate Raster
* is full. The parameter indicates the scanline in the target
* Raster to which the intermediate Raster should be copied.
* After the copy, we notify update listeners.
*/
}
try {
1, 1,
float percentOfPass = ((float)y)/height;
if (progressive) {
if (knownPassCount != UNKNOWN) {
/ knownPassCount);
// Use the range of allowed progressive passes
} else {
// Assume there are a minimum of MIN_ESTIMATED_PASSES
// and that there is always one more pass
// Compute the percentage as the percentage at the end
// of the previous pass, plus the percentage of this
// pass scaled to be the percentage of the total remaining,
// assuming a minimum of MIN_ESTIMATED_PASSES passes and
// that there is always one more pass. This is monotonic
// and asymptotic to 1.0, which is what we need.
int remainingPasses = // including this one
if (y%progInterval == 0) {
(1.0F - previousPassPercentage)
if (debug) {
+ remainingPasses);
}
}
}
} else {
}
}
} finally {
}
}
private void initProgressData() {
pass = 0;
percentToDate = 0.0F;
previousPassPercentage = 0.0F;
progInterval = 0;
}
try {
pass,
0, 0,
1,1,
} finally {
}
}
private void passComplete () {
try {
} finally {
}
}
try {
} finally {
}
}
// Provide access to protected superclass method
try {
} finally {
}
}
// Provide access to protected superclass method
void thumbnailComplete() {
try {
} finally {
}
}
/**
* Returns <code>true</code> if the read was aborted.
*/
byte [] buffer,
int numRasterBands,
int [] srcBands,
int [] bandSizes,
int sourceXOffset, int sourceYOffset,
int sourceWidth, int sourceHeight,
int minProgressivePass,
int maxProgressivePass,
boolean wantUpdates);
public void abort() {
try {
/**
* NB: we do not check the call back lock here,
* we allow to abort the reader any time.
*/
super.abort();
} finally {
}
}
/** Set the C level abort flag. Keep it atomic for thread safety. */
/** Resets library state when an exception occurred during a read. */
public boolean canReadRaster() {
return true;
}
throws IOException {
try {
/*
* This could be further optimized by not resetting the dest.
* offset and creating a translated raster in readInternal()
* (see bug 4994702 for more info).
*/
// For Rasters, destination offset is logical, not physical, so
// set it to 0 before calling computeRegions, so that the destination
// region is not clipped.
}
// Apply the destination offset, if any, as a logical offset
if (saveDestOffset != null) {
saveDestOffset.y);
}
} catch (RuntimeException e) {
throw e;
} catch (IOException e) {
throw e;
} finally {
}
return retval;
}
public boolean readerSupportsThumbnails() {
return true;
}
try {
// Now check the jfif segments
(JFIFMarkerSegment.class, true);
int retval = 0;
}
return retval;
} finally {
}
}
throws IOException {
try {
if ((thumbnailIndex < 0)
throw new IndexOutOfBoundsException("No such thumbnail");
}
// Now we know that there is a jfif segment
(JFIFMarkerSegment.class, true);
} finally {
}
}
throws IOException {
try {
if ((thumbnailIndex < 0)
throw new IndexOutOfBoundsException("No such thumbnail");
}
// Now we know that there is a jfif segment
(JFIFMarkerSegment.class, true);
} finally {
}
}
int thumbnailIndex)
throws IOException {
try {
if ((thumbnailIndex < 0)
throw new IndexOutOfBoundsException("No such thumbnail");
}
// Now we know that there is a jfif segment and that iis is good
(JFIFMarkerSegment.class, true);
} finally {
}
}
private void resetInternalState() {
// reset C structures
// reset local Java structures
numImages = 0;
imagePositions = new ArrayList();
currentImage = -1;
imageMetadataIndex = -1;
haveSeeked = false;
tablesOnlyChecked = false;
}
public void reset() {
try {
super.reset();
} finally {
}
}
public void dispose() {
try {
if (structPointer != 0) {
structPointer = 0;
}
} finally {
}
}
private long pData;
}
public synchronized void dispose() {
if (pData != 0) {
pData = 0;
}
}
}
private synchronized void setThreadLock() {
if (theThread != currThread) {
// it looks like that this reader instance is used
// by multiple threads.
throw new IllegalStateException("Attempt to use instance of " +
this + " locked on thread " +
theThread + " from thread " +
} else {
theLockCount ++;
}
} else {
theLockCount = 1;
}
}
private synchronized void clearThreadLock() {
throw new IllegalStateException("Attempt to clear thread lock " +
" form wrong thread." +
" Locked thread: " + theThread +
"; current thread: " + currThread);
}
theLockCount --;
if (theLockCount == 0) {
}
}
private static class CallBackLock {
CallBackLock() {
}
void check() {
throw new IllegalStateException("Access to the reader is not allowed");
}
}
private void lock() {
}
private void unlock() {
}
private static enum State {
}
}
}
/**
* An internal helper class that wraps producer's iterator
* and extracts specifier instances on demand.
*/
}
public boolean hasNext() {
return true;
}
return false;
}
do {
}
return t;
} else {
throw new NoSuchElementException();
}
}
public void remove() {
}
}
/**
* An internal helper class that provides means for deferred creation
* of ImageTypeSpecifier instance required to describe available
* destination types.
*
* This implementation only supports standard
* jpeg color spaces (defined by corresponding JCS color space code).
*
* To support other color spaces one can override produce() method to
* return custom instance of ImageTypeSpecifier.
*/
class ImageTypeProducer {
boolean failed = false;
private int csCode;
}
public ImageTypeProducer() {
}
try {
} catch (Throwable e) {
failed = true;
}
}
return type;
}
return null;
}
}
return defaultTypes[csCode];
}
switch (csCode) {
case JPEG.JCS_GRAYSCALE:
false,
false);
0xff000000,
0x00ff0000,
0x0000ff00,
0x000000ff,
false);
return ImageTypeSpecifier.createInterleaved(
false,
false);
} else {
return null;
}
return ImageTypeSpecifier.createInterleaved(
true,
false);
} else {
return null;
}
default:
return null;
}
}
}