JPEGImageReaderpublic class JPEGImageReader extends ImageReader
Fields Summary |
---|
private boolean | debug | private long | structPointerThe 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 ImageInputStream | iisThe input stream we read from | private List | imagePositionsList of stream positions for images, reinitialized every time
a new input source is set. | private int | numImagesThe number of images in the stream, or 0. | protected static final int | WARNING_NO_EOIWarning 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_JFIF_IN_THUMBWarning 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_IGNORE_INVALID_ICCWarning code to be passed to warningOccurred to indicate
that embedded ICC profile is invalid and will be ignored. | private static final int | MAX_WARNING | private int | currentImageImage index of image for which header information
is available. | private int | widthSet by setImageData native code callback | private int | heightSet by setImageData native code callback | private int | colorSpaceCodeSet by setImageData native code callback. A modified
IJG+NIFTY colorspace code. | private int | outColorSpaceCodeSet by setImageData native code callback. A modified
IJG+NIFTY colorspace code. | private int | numComponentsSet by setImageData native code callback | private ColorSpace | iccCSSet by setImageData native code callback | private ColorConvertOp | convertIf we need to post-convert in Java, convert with this op | private BufferedImage | imageThe image we are going to fill | private WritableRaster | rasterAn intermediate Raster to hold decoded data | private WritableRaster | targetA view of our target Raster that we can setRect to | private DataBufferByte | bufferThe databuffer for the above Raster | private Rectangle | destROIThe region in the destination where we will write pixels | private int[] | destinationBandsThe list of destination bands, if any | private JPEGMetadata | streamMetadataStream metadata, cached, even when the stream is changed. | private JPEGMetadata | imageMetadataImage metadata, valid for the imageMetadataIndex only. | private int | imageMetadataIndex | private boolean | haveSeekedSet to true every time we seek in the stream; used to
invalidate the native buffer contents in C. | private JPEGQTable[] | abbrevQTablesTables that have been read from a tables-only image at the
beginning of a stream. | private JPEGHuffmanTable[] | abbrevDCHuffmanTables | private JPEGHuffmanTable[] | abbrevACHuffmanTables | private int | minProgressivePass | private int | maxProgressivePass | private static final int | UNKNOWNVariables used by progress monitoring. | private static final int | MIN_ESTIMATED_PASSES | private int | knownPassCount | private int | pass | private float | percentToDate | private float | previousPassPercentage | private int | progInterval | private boolean | tablesOnlyCheckedSet to true once stream has been checked for stream metadata | private Object | disposerReferentThe referent to be registered with the Disposer. | private DisposerRecord | disposerRecordThe DisposerRecord that handles the actual disposal of this reader. | private static final ImageTypeSpecifier[] | defaultTypesMaintain an array of the default image types corresponding to the
various supported IJG colorspace codes. |
Constructors Summary |
---|
public JPEGImageReader(ImageReaderSpi originator)
defaultTypes[JPEG.JCS_GRAYSCALE] =
ImageTypeSpecifier.createFromBufferedImageType
(BufferedImage.TYPE_BYTE_GRAY);
defaultTypes[JPEG.JCS_RGB] =
ImageTypeSpecifier.createInterleaved
(JPEG.sRGB,
JPEG.bOffsRGB,
DataBuffer.TYPE_BYTE,
false,
false);
defaultTypes[JPEG.JCS_RGBA] =
ImageTypeSpecifier.createPacked
(JPEG.sRGB,
0xff000000,
0x00ff0000,
0x0000ff00,
0x000000ff,
DataBuffer.TYPE_INT,
false);
if (JPEG.YCC != null) {
defaultTypes[JPEG.JCS_YCC] =
ImageTypeSpecifier.createInterleaved
(JPEG.YCC,
JPEG.bandOffsets[2],
DataBuffer.TYPE_BYTE,
false,
false);
defaultTypes[JPEG.JCS_YCCA] =
ImageTypeSpecifier.createInterleaved
(JPEG.YCC,
JPEG.bandOffsets[3],
DataBuffer.TYPE_BYTE,
true,
false);
}
super(originator);
structPointer = initJPEGImageReader();
disposerRecord = new JPEGReaderDisposerRecord(structPointer);
Disposer.addRecord(disposerReferent, disposerRecord);
|
Methods Summary |
---|
public void | abort()
super.abort();
abortRead(structPointer);
| private native void | abortRead(long structPointer)Set the C level abort flag. Keep it atomic for thread safety.
| private void | acceptPixels(int y, boolean progressive)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.
if (convert != null) {
convert.filter(raster, raster);
}
target.setRect(destROI.x, destROI.y + y, raster);
processImageUpdate(image,
destROI.x, destROI.y+y,
raster.getWidth(), 1,
1, 1,
destinationBands);
if ((y > 0) && (y%progInterval == 0)) {
int height = target.getHeight()-1;
float percentOfPass = ((float)y)/height;
if (progressive) {
if (knownPassCount != UNKNOWN) {
processImageProgress((pass + percentOfPass)*100.0F
/ knownPassCount);
} else if (maxProgressivePass != Integer.MAX_VALUE) {
// Use the range of allowed progressive passes
processImageProgress((pass + percentOfPass)*100.0F
/ (maxProgressivePass - minProgressivePass + 1));
} 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
Math.max(2, MIN_ESTIMATED_PASSES-pass);
int totalPasses = pass + remainingPasses-1;
progInterval = Math.max(height/20*totalPasses,
totalPasses);
if (y%progInterval == 0) {
percentToDate = previousPassPercentage +
(1.0F - previousPassPercentage)
* (percentOfPass)/remainingPasses;
if (debug) {
System.out.print("pass= " + pass);
System.out.print(", y= " + y);
System.out.print(", progInt= " + progInterval);
System.out.print(", % of pass: " + percentOfPass);
System.out.print(", rem. passes: "
+ remainingPasses);
System.out.print(", prev%: "
+ previousPassPercentage);
System.out.print(", %ToDate: " + percentToDate);
System.out.print(" ");
}
processImageProgress(percentToDate*100.0F);
}
}
} else {
processImageProgress(percentOfPass * 100.0F);
}
}
| public boolean | canReadRaster()
return true;
| private void | checkColorConversion(java.awt.image.BufferedImage image, javax.imageio.ImageReadParam param)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
convert .
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.
// 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.
if (param != null) {
if ((param.getSourceBands() != null) ||
(param.getDestinationBands() != null)) {
// 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.
ColorModel cm = image.getColorModel();
if (cm instanceof IndexColorModel) {
throw new IIOException("IndexColorModel not supported");
}
// Now check the ColorSpace type against outColorSpaceCode
// We may want to tweak the default
ColorSpace cs = cm.getColorSpace();
int csType = cs.getType();
convert = null;
switch (outColorSpaceCode) {
case JPEG.JCS_GRAYSCALE: // Its gray in the file
if (csType == ColorSpace.TYPE_RGB) { // We want RGB
// IJG can do this for us more efficiently
setOutColorSpace(structPointer, JPEG.JCS_RGB);
} else if (csType != ColorSpace.TYPE_GRAY) {
throw new IIOException("Incompatible color conversion");
}
break;
case JPEG.JCS_RGB: // IJG wants to go to RGB
if (csType == ColorSpace.TYPE_GRAY) { // We want gray
if (colorSpaceCode == JPEG.JCS_YCbCr) {
// If the jpeg space is YCbCr, IJG can do it
setOutColorSpace(structPointer, JPEG.JCS_GRAYSCALE);
}
} else if ((iccCS != null) &&
(cm.getNumComponents() == numComponents) &&
(cs != iccCS)) {
// We have an ICC profile but it isn't used in the dest
// image. So convert from the profile cs to the target cs
convert = new ColorConvertOp(iccCS, cs, null);
// Leave IJG conversion in place; we still need it
} else if ((iccCS == null) &&
(!cs.isCS_sRGB()) &&
(cm.getNumComponents() == numComponents)) {
// Target isn't sRGB, so convert from sRGB to the target
convert = new ColorConvertOp(JPEG.sRGB, cs, null);
} else if (csType != ColorSpace.TYPE_RGB) {
throw new IIOException("Incompatible color conversion");
}
break;
case JPEG.JCS_RGBA:
// No conversions available; image must be RGBA
if ((csType != ColorSpace.TYPE_RGB) ||
(cm.getNumComponents() != numComponents)) {
throw new IIOException("Incompatible color conversion");
}
break;
case JPEG.JCS_YCC:
if (JPEG.YCC == null) { // We can't do YCC at all
throw new IIOException("Incompatible color conversion");
}
if ((cs != JPEG.YCC) &&
(cm.getNumComponents() == numComponents)) {
convert = new ColorConvertOp(JPEG.YCC, cs, null);
}
break;
case JPEG.JCS_YCCA:
// No conversions available; image must be YCCA
if ((JPEG.YCC == null) || // We can't do YCC at all
(cs != JPEG.YCC) ||
(cm.getNumComponents() != numComponents)) {
throw new IIOException("Incompatible color conversion");
}
break;
default:
// Anything else we can't handle at all
throw new IIOException("Incompatible color conversion");
}
| private void | checkTablesOnly()
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 void | dispose()
if (structPointer != 0) {
disposerRecord.dispose();
structPointer = 0;
}
| private static native void | disposeReader(long structPointer)
| public javax.imageio.ImageReadParam | getDefaultReadParam()
return new JPEGImageReadParam();
| public int | getHeight(int imageIndex)
if (currentImage != imageIndex) {
readHeader(imageIndex, true);
}
return height;
| public javax.imageio.metadata.IIOMetadata | getImageMetadata(int imageIndex)
// 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;
}
gotoImage(imageIndex);
imageMetadata = new JPEGMetadata(false, false, iis, this);
imageMetadataIndex = imageIndex;
return imageMetadata;
| private javax.imageio.ImageTypeSpecifier | getImageType(int code)Return an ImageTypeSpecifier corresponding to the given
color space code, or null if the color space is unsupported.
ImageTypeSpecifier ret = null;
if ((code > 0) && (code < JPEG.NUM_JCS_CODES)) {
ret = defaultTypes[code];
}
return ret;
| public java.util.Iterator | getImageTypes(int imageIndex)
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.
ImageTypeSpecifier raw = getImageType(colorSpaceCode);
// Given the encoded colorspace, build a list of ITS's
// representing outputs you could handle starting
// with the default.
ArrayList list = new ArrayList(1);
switch (colorSpaceCode) {
case JPEG.JCS_GRAYSCALE:
list.add(raw);
list.add(getImageType(JPEG.JCS_RGB));
break;
case JPEG.JCS_RGB:
list.add(raw);
list.add(getImageType(JPEG.JCS_GRAYSCALE));
if (JPEG.YCC != null) {
list.add(getImageType(JPEG.JCS_YCC));
}
break;
case JPEG.JCS_RGBA:
list.add(raw);
break;
case JPEG.JCS_YCC:
if (raw != null) { // Might be null if PYCC.pf not installed
list.add(raw);
list.add(getImageType(JPEG.JCS_RGB));
}
break;
case JPEG.JCS_YCCA:
if (raw != null) { // Might be null if PYCC.pf not installed
list.add(raw);
}
break;
case JPEG.JCS_YCbCr:
// 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.
list.add(getImageType(JPEG.JCS_RGB));
if (iccCS != null) {
list.add(ImageTypeSpecifier.createInterleaved
(iccCS,
JPEG.bOffsRGB, // Assume it's for RGB
DataBuffer.TYPE_BYTE,
false,
false));
}
list.add(getImageType(JPEG.JCS_GRAYSCALE));
if (JPEG.YCC != null) { // Might be null if PYCC.pf not installed
list.add(getImageType(JPEG.JCS_YCC));
}
break;
case JPEG.JCS_YCbCrA: // Default is to convert to RGBA
// As there is no YCbCr ColorSpace, we can't support
// the raw type.
list.add(getImageType(JPEG.JCS_RGBA));
break;
}
return list.iterator();
| public int | getNumImages(boolean allowSearch)
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
| public int | getNumThumbnails(int imageIndex)
getImageMetadata(imageIndex); // checks iis state for us
// Now check the jfif segments
JFIFMarkerSegment jfif =
(JFIFMarkerSegment) imageMetadata.findMarkerSegment
(JFIFMarkerSegment.class, true);
int retval = 0;
if (jfif != null) {
retval = (jfif.thumb == null) ? 0 : 1;
retval += jfif.extSegments.size();
}
return retval;
| public javax.imageio.ImageTypeSpecifier | getRawImageType(int imageIndex)
if (currentImage != imageIndex) {
readHeader(imageIndex, true);
}
// Returns null if it can't be represented
return getImageType(colorSpaceCode);
| public javax.imageio.metadata.IIOMetadata | getStreamMetadata()
if (!tablesOnlyChecked) {
checkTablesOnly();
}
return streamMetadata;
| public int | getThumbnailHeight(int imageIndex, int thumbnailIndex)
if ((thumbnailIndex < 0)
|| (thumbnailIndex >= getNumThumbnails(imageIndex))) {
throw new IndexOutOfBoundsException("No such thumbnail");
}
// Now we know that there is a jfif segment
JFIFMarkerSegment jfif =
(JFIFMarkerSegment) imageMetadata.findMarkerSegment
(JFIFMarkerSegment.class, true);
return jfif.getThumbnailHeight(thumbnailIndex);
| public int | getThumbnailWidth(int imageIndex, int thumbnailIndex)
if ((thumbnailIndex < 0)
|| (thumbnailIndex >= getNumThumbnails(imageIndex))) {
throw new IndexOutOfBoundsException("No such thumbnail");
}
// Now we know that there is a jfif segment
JFIFMarkerSegment jfif =
(JFIFMarkerSegment) imageMetadata.findMarkerSegment
(JFIFMarkerSegment.class, true);
return jfif.getThumbnailWidth(thumbnailIndex);
| public int | getWidth(int imageIndex)
if (currentImage != imageIndex) {
readHeader(imageIndex, true);
}
return width;
| private void | gotoImage(int imageIndex)Sets the input stream to the start of the requested image.
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
| private boolean | hasNextImage()Returns true if there is an image beyond
the current stream position. Does not disturb the
stream position.
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;
| private native long | initJPEGImageReader()Sets up per-reader C structure and returns a pointer to it.
| private void | initProgressData()
knownPassCount = UNKNOWN;
pass = 0;
percentToDate = 0.0F;
previousPassPercentage = 0.0F;
progInterval = 0;
| private static native void | initReaderIDs(java.lang.Class iisClass, java.lang.Class qTableClass, java.lang.Class huffClass)Sets up static C structures.
| private void | passComplete()
processPassComplete(image);
| private void | passStarted(int pass)
this.pass = pass;
previousPassPercentage = percentToDate;
processPassStarted(image,
pass,
minProgressivePass,
maxProgressivePass,
0, 0,
1,1,
destinationBands);
| private void | pushBack(int num)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) {
System.out.println("pushing back " + num + " bytes");
}
iis.seek(iis.getStreamPosition()-num);
// The buffer is clear after this, so no need to set haveSeeked.
| public java.awt.image.BufferedImage | read(int imageIndex, javax.imageio.ImageReadParam param)
try {
readInternal(imageIndex, param, false);
} catch (RuntimeException e) {
resetLibraryState(structPointer);
throw e;
} catch (IOException e) {
resetLibraryState(structPointer);
throw e;
}
BufferedImage ret = image;
image = null; // don't keep a reference here
return ret;
| private void | readHeader(int imageIndex, boolean reset)Reads header information for the given image, if possible.
gotoImage(imageIndex);
readNativeHeader(reset); // Ignore return
currentImage = imageIndex;
| private native boolean | readImage(long structPointer, byte[] buffer, int numRasterBands, int[] srcBands, int[] bandSizes, int sourceXOffset, int sourceYOffset, int sourceWidth, int sourceHeight, int periodX, int periodY, javax.imageio.plugins.jpeg.JPEGQTable[] abbrevQTables, javax.imageio.plugins.jpeg.JPEGHuffmanTable[] abbrevDCHuffmanTables, javax.imageio.plugins.jpeg.JPEGHuffmanTable[] abbrevACHuffmanTables, int minProgressivePass, int maxProgressivePass, boolean wantUpdates)Returns true if the read was aborted.
| private native boolean | readImageHeader(long structPointer, boolean clearBuffer, boolean reset)Read in the header information starting from the current
stream position, returning 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 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 java.awt.image.Raster | readInternal(int imageIndex, javax.imageio.ImageReadParam param, boolean wantRaster)
readHeader(imageIndex, false);
WritableRaster imRas = null;
int numImageBands = 0;
if (!wantRaster){
// Can we read this image?
Iterator imageTypes = getImageTypes(imageIndex);
if (imageTypes.hasNext() == false) {
throw new IIOException("Unsupported Image Type");
}
image = getDestination(param, imageTypes, width, height);
imRas = image.getRaster();
// The destination may still be incompatible.
numImageBands = image.getSampleModel().getNumBands();
// 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
checkColorConversion(image, param);
// Check the source and destination bands in the param
checkReadParamBandSettings(param, numComponents, numImageBands);
} else {
// Set the output color space equal to the input colorspace
// This disables all conversions
setOutColorSpace(structPointer, colorSpaceCode);
image = null;
}
// 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 [] srcBands = JPEG.bandOffsets[numComponents-1];
int numRasterBands = (wantRaster ? numComponents : numImageBands);
destinationBands = null;
Rectangle srcROI = new Rectangle(0, 0, 0, 0);
destROI = new Rectangle(0, 0, 0, 0);
computeRegions(param, width, height, image, srcROI, destROI);
int periodX = 1;
int periodY = 1;
minProgressivePass = 0;
maxProgressivePass = Integer.MAX_VALUE;
if (param != null) {
periodX = param.getSourceXSubsampling();
periodY = param.getSourceYSubsampling();
int[] sBands = param.getSourceBands();
if (sBands != null) {
srcBands = sBands;
numRasterBands = srcBands.length;
}
if (!wantRaster) { // ignore dest bands for Raster
destinationBands = param.getDestinationBands();
}
minProgressivePass = param.getSourceMinProgressivePass();
maxProgressivePass = param.getSourceMaxProgressivePass();
if (param instanceof JPEGImageReadParam) {
JPEGImageReadParam jparam = (JPEGImageReadParam) param;
if (jparam.areTablesSet()) {
abbrevQTables = jparam.getQTables();
abbrevDCHuffmanTables = jparam.getDCHuffmanTables();
abbrevACHuffmanTables = jparam.getACHuffmanTables();
}
}
}
int lineSize = destROI.width*numRasterBands;
buffer = new DataBufferByte(lineSize);
int [] bandOffs = JPEG.bandOffsets[numRasterBands-1];
raster = Raster.createInterleavedRaster(buffer,
destROI.width, 1,
lineSize,
numRasterBands,
bandOffs,
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) {
target = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
destROI.width,
destROI.height,
lineSize,
numRasterBands,
bandOffs,
null);
} else {
target = imRas;
}
int [] bandSizes = target.getSampleModel().getSampleSize();
/*
* 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)
boolean callbackUpdates = ((updateListeners != null)
|| (progressListeners != null));
// Set up progression data
initProgressData();
// if we have a metadata object, we can count the scans
// and set knownPassCount
if (imageIndex == imageMetadataIndex) { // We have metadata
knownPassCount = 0;
for (Iterator iter = imageMetadata.markerSequence.iterator();
iter.hasNext();) {
if (iter.next() instanceof SOSMarkerSegment) {
knownPassCount++;
}
}
}
progInterval = Math.max((target.getHeight()-1) / 20, 1);
if (knownPassCount > 0) {
progInterval *= knownPassCount;
} else if (maxProgressivePass != Integer.MAX_VALUE) {
progInterval *= (maxProgressivePass - minProgressivePass + 1);
}
if (debug) {
System.out.println("**** Read Data *****");
System.out.println("numRasterBands is " + numRasterBands);
System.out.print("srcBands:");
for (int i = 0; i<srcBands.length;i++)
System.out.print(" " + srcBands[i]);
System.out.println();
System.out.println("destination bands is " + destinationBands);
if (destinationBands != null) {
for (int i = 0; i < destinationBands.length; i++) {
System.out.print(" " + destinationBands[i]);
}
System.out.println();
}
System.out.println("sourceROI is " + srcROI);
System.out.println("destROI is " + destROI);
System.out.println("periodX is " + periodX);
System.out.println("periodY is " + periodY);
System.out.println("minProgressivePass is " + minProgressivePass);
System.out.println("maxProgressivePass is " + maxProgressivePass);
System.out.println("callbackUpdates is " + callbackUpdates);
}
// Finally, we are ready to read
processImageStarted(currentImage);
boolean aborted = false;
aborted = readImage(structPointer,
buffer.getData(),
numRasterBands,
srcBands,
bandSizes,
srcROI.x, srcROI.y,
srcROI.width, srcROI.height,
periodX, periodY,
abbrevQTables,
abbrevDCHuffmanTables,
abbrevACHuffmanTables,
minProgressivePass, maxProgressivePass,
callbackUpdates);
if (aborted) {
processReadAborted();
} else {
processImageComplete();
}
return target;
| private boolean | readNativeHeader(boolean reset)
boolean retval = false;
retval = readImageHeader(structPointer, haveSeeked, reset);
haveSeeked = false;
return retval;
| public java.awt.image.Raster | readRaster(int imageIndex, javax.imageio.ImageReadParam param)
Raster retval = null;
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.
Point saveDestOffset = null;
if (param != null) {
saveDestOffset = param.getDestinationOffset();
param.setDestinationOffset(new Point(0, 0));
}
retval = readInternal(imageIndex, param, true);
// Apply the destination offset, if any, as a logical offset
if (saveDestOffset != null) {
target = target.createWritableTranslatedChild(saveDestOffset.x,
saveDestOffset.y);
}
} catch (RuntimeException e) {
resetLibraryState(structPointer);
throw e;
} catch (IOException e) {
resetLibraryState(structPointer);
throw e;
}
return retval;
| public java.awt.image.BufferedImage | readThumbnail(int imageIndex, int thumbnailIndex)
if ((thumbnailIndex < 0)
|| (thumbnailIndex >= getNumThumbnails(imageIndex))) {
throw new IndexOutOfBoundsException("No such thumbnail");
}
// Now we know that there is a jfif segment and that iis is good
JFIFMarkerSegment jfif =
(JFIFMarkerSegment) imageMetadata.findMarkerSegment
(JFIFMarkerSegment.class, true);
return jfif.getThumbnail(iis, thumbnailIndex, this);
| public boolean | readerSupportsThumbnails()
return true;
| private void | resetInternalState()
// reset C structures
resetReader(structPointer);
// reset local Java structures
numImages = 0;
imagePositions = new ArrayList();
currentImage = -1;
image = null;
raster = null;
target = null;
buffer = null;
destROI = null;
destinationBands = null;
streamMetadata = null;
imageMetadata = null;
imageMetadataIndex = -1;
haveSeeked = false;
tablesOnlyChecked = false;
iccCS = null;
initProgressData();
| private native void | resetLibraryState(long structPointer)Resets library state when an exception occurred during a read.
| private native void | resetReader(long structPointer)Note that there is no need to override reset() here, as the default
implementation will call setInput(null, false, false), which will
invoke resetInternalState().
| 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);
}
| public void | setInput(java.lang.Object input, boolean seekForwardOnly, boolean ignoreMetadata)
super.setInput(input, seekForwardOnly, ignoreMetadata);
this.ignoreMetadata = ignoreMetadata;
resetInternalState();
iis = (ImageInputStream) input; // Always works
setSource(structPointer, iis);
| private native void | setOutColorSpace(long structPointer, int id)Set the IJG output space to the given value. The library will
perform the appropriate colorspace conversions.
| private native void | setSource(long structPointer, javax.imageio.stream.ImageInputStream source)
| private void | skipImage()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) {
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();
| void | thumbnailComplete()
processThumbnailComplete();
| void | thumbnailProgress(float percentageDone)
processThumbnailProgress(percentageDone);
| void | thumbnailStarted(int thumbnailIndex)
processThumbnailStarted(currentImage, thumbnailIndex);
| protected void | warningOccurred(int code)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.
if ((code < 0) || (code > MAX_WARNING)){
throw new InternalError("Invalid warning index");
}
processWarningOccurred
("com.sun.imageio.plugins.jpeg.JPEGImageReaderResources",
Integer.toString(code));
| protected void | warningWithMessage(java.lang.String msg)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.
processWarningOccurred(msg);
|
|