8007667: Better image reading

Reviewed-by: prr, jgodinez, mschoene
This commit is contained in:
Andrew Brygin 2013-02-26 00:41:40 +04:00
parent 243470f47e
commit 57d870834f
2 changed files with 231 additions and 87 deletions

View File

@ -243,12 +243,17 @@ public class JPEGImageReader extends ImageReader {
* sending warnings to listeners.
*/
protected void warningOccurred(int code) {
if ((code < 0) || (code > MAX_WARNING)){
throw new InternalError("Invalid warning index");
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();
}
processWarningOccurred
("com.sun.imageio.plugins.jpeg.JPEGImageReaderResources",
Integer.toString(code));
}
/**
@ -265,7 +270,12 @@ public class JPEGImageReader extends ImageReader {
* library warnings from being printed to stderr.
*/
protected void warningWithMessage(String msg) {
processWarningOccurred(msg);
cbLock.lock();
try {
processWarningOccurred(msg);
} finally {
cbLock.unlock();
}
}
public void setInput(Object input,
@ -274,18 +284,55 @@ public class JPEGImageReader extends ImageReader {
{
setThreadLock();
try {
cbLock.check();
super.setInput(input, seekForwardOnly, ignoreMetadata);
this.ignoreMetadata = ignoreMetadata;
resetInternalState();
iis = (ImageInputStream) input; // Always works
setSource(structPointer, iis);
setSource(structPointer);
} finally {
clearThreadLock();
}
}
private native void setSource(long structPointer,
ImageInputStream source);
/**
* 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) {
@ -337,6 +384,8 @@ public class JPEGImageReader extends ImageReader {
public int getNumImages(boolean allowSearch) throws IOException {
setThreadLock();
try { // locked thread
cbLock.check();
return getNumImagesOnThread(allowSearch);
} finally {
clearThreadLock();
@ -536,8 +585,13 @@ public class JPEGImageReader extends ImageReader {
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.
cbLock.lock();
try {
iis.seek(iis.getStreamPosition()-num);
// The buffer is clear after this, so no need to set haveSeeked.
} finally {
cbLock.unlock();
}
}
/**
@ -644,7 +698,12 @@ public class JPEGImageReader extends ImageReader {
* Ignore this profile.
*/
iccCS = null;
warningOccurred(WARNING_IGNORE_INVALID_ICC);
cbLock.lock();
try {
warningOccurred(WARNING_IGNORE_INVALID_ICC);
} finally {
cbLock.unlock();
}
}
}
}
@ -653,6 +712,7 @@ public class JPEGImageReader extends ImageReader {
setThreadLock();
try {
if (currentImage != imageIndex) {
cbLock.check();
readHeader(imageIndex, true);
}
return width;
@ -665,6 +725,7 @@ public class JPEGImageReader extends ImageReader {
setThreadLock();
try {
if (currentImage != imageIndex) {
cbLock.check();
readHeader(imageIndex, true);
}
return height;
@ -693,6 +754,8 @@ public class JPEGImageReader extends ImageReader {
setThreadLock();
try {
if (currentImage != imageIndex) {
cbLock.check();
readHeader(imageIndex, true);
}
@ -716,6 +779,7 @@ public class JPEGImageReader extends ImageReader {
private Iterator getImageTypesOnThread(int imageIndex)
throws IOException {
if (currentImage != imageIndex) {
cbLock.check();
readHeader(imageIndex, true);
}
@ -931,6 +995,7 @@ public class JPEGImageReader extends ImageReader {
setThreadLock();
try {
if (!tablesOnlyChecked) {
cbLock.check();
checkTablesOnly();
}
return streamMetadata;
@ -951,6 +1016,8 @@ public class JPEGImageReader extends ImageReader {
return imageMetadata;
}
cbLock.check();
gotoImage(imageIndex);
imageMetadata = new JPEGMetadata(false, false, iis, this);
@ -967,6 +1034,7 @@ public class JPEGImageReader extends ImageReader {
throws IOException {
setThreadLock();
try {
cbLock.check();
try {
readInternal(imageIndex, param, false);
} catch (RuntimeException e) {
@ -1196,58 +1264,63 @@ public class JPEGImageReader extends ImageReader {
}
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(" ");
cbLock.lock();
try {
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);
}
processImageProgress(percentToDate*100.0F);
}
} else {
processImageProgress(percentOfPass * 100.0F);
}
} else {
processImageProgress(percentOfPass * 100.0F);
}
} finally {
cbLock.unlock();
}
}
@ -1260,33 +1333,58 @@ public class JPEGImageReader extends ImageReader {
}
private void passStarted (int pass) {
this.pass = pass;
previousPassPercentage = percentToDate;
processPassStarted(image,
pass,
minProgressivePass,
maxProgressivePass,
0, 0,
1,1,
destinationBands);
cbLock.lock();
try {
this.pass = pass;
previousPassPercentage = percentToDate;
processPassStarted(image,
pass,
minProgressivePass,
maxProgressivePass,
0, 0,
1,1,
destinationBands);
} finally {
cbLock.unlock();
}
}
private void passComplete () {
processPassComplete(image);
cbLock.lock();
try {
processPassComplete(image);
} finally {
cbLock.unlock();
}
}
void thumbnailStarted(int thumbnailIndex) {
processThumbnailStarted(currentImage, thumbnailIndex);
cbLock.lock();
try {
processThumbnailStarted(currentImage, thumbnailIndex);
} finally {
cbLock.unlock();
}
}
// Provide access to protected superclass method
void thumbnailProgress(float percentageDone) {
processThumbnailProgress(percentageDone);
cbLock.lock();
try {
processThumbnailProgress(percentageDone);
} finally {
cbLock.unlock();
}
}
// Provide access to protected superclass method
void thumbnailComplete() {
processThumbnailComplete();
cbLock.lock();
try {
processThumbnailComplete();
} finally {
cbLock.unlock();
}
}
/**
@ -1310,6 +1408,11 @@ public class JPEGImageReader extends ImageReader {
public void abort() {
setThreadLock();
try {
/**
* NB: we do not check the call back lock here,
* we allow to abort the reader any time.
*/
super.abort();
abortRead(structPointer);
} finally {
@ -1332,6 +1435,7 @@ public class JPEGImageReader extends ImageReader {
setThreadLock();
Raster retval = null;
try {
cbLock.check();
/*
* This could be further optimized by not resetting the dest.
* offset and creating a translated raster in readInternal()
@ -1371,6 +1475,8 @@ public class JPEGImageReader extends ImageReader {
public int getNumThumbnails(int imageIndex) throws IOException {
setThreadLock();
try {
cbLock.check();
getImageMetadata(imageIndex); // checks iis state for us
// Now check the jfif segments
JFIFMarkerSegment jfif =
@ -1391,6 +1497,8 @@ public class JPEGImageReader extends ImageReader {
throws IOException {
setThreadLock();
try {
cbLock.check();
if ((thumbnailIndex < 0)
|| (thumbnailIndex >= getNumThumbnails(imageIndex))) {
throw new IndexOutOfBoundsException("No such thumbnail");
@ -1409,6 +1517,8 @@ public class JPEGImageReader extends ImageReader {
throws IOException {
setThreadLock();
try {
cbLock.check();
if ((thumbnailIndex < 0)
|| (thumbnailIndex >= getNumThumbnails(imageIndex))) {
throw new IndexOutOfBoundsException("No such thumbnail");
@ -1428,6 +1538,8 @@ public class JPEGImageReader extends ImageReader {
throws IOException {
setThreadLock();
try {
cbLock.check();
if ((thumbnailIndex < 0)
|| (thumbnailIndex >= getNumThumbnails(imageIndex))) {
throw new IndexOutOfBoundsException("No such thumbnail");
@ -1468,6 +1580,7 @@ public class JPEGImageReader extends ImageReader {
public void reset() {
setThreadLock();
try {
cbLock.check();
super.reset();
} finally {
clearThreadLock();
@ -1479,6 +1592,8 @@ public class JPEGImageReader extends ImageReader {
public void dispose() {
setThreadLock();
try {
cbLock.check();
if (structPointer != 0) {
disposerRecord.dispose();
structPointer = 0;
@ -1540,6 +1655,36 @@ public class JPEGImageReader extends ImageReader {
theThread = null;
}
}
private CallBackLock cbLock = new CallBackLock();
private static class CallBackLock {
private State lockState;
CallBackLock() {
lockState = State.Unlocked;
}
void check() {
if (lockState != State.Unlocked) {
throw new IllegalStateException("Access to the reader is not allowed");
}
}
private void lock() {
lockState = State.Locked;
}
private void unlock() {
lockState = State.Unlocked;
}
private static enum State {
Unlocked,
Locked
}
}
}
/**

View File

@ -57,8 +57,8 @@
#define MAX(a,b) ((a) > (b) ? (a) : (b))
/* Cached Java method ids */
static jmethodID ImageInputStream_readID;
static jmethodID ImageInputStream_skipBytesID;
static jmethodID JPEGImageReader_readInputDataID;
static jmethodID JPEGImageReader_skipInputBytesID;
static jmethodID JPEGImageReader_warningOccurredID;
static jmethodID JPEGImageReader_warningWithMessageID;
static jmethodID JPEGImageReader_setImageDataID;
@ -923,7 +923,7 @@ imageio_fill_input_buffer(j_decompress_ptr cinfo)
RELEASE_ARRAYS(env, data, src->next_input_byte);
ret = (*env)->CallIntMethod(env,
sb->stream,
ImageInputStream_readID,
JPEGImageReader_readInputDataID,
sb->hstreamBuffer, 0,
sb->bufferLength);
if ((*env)->ExceptionOccurred(env)
@ -1013,7 +1013,7 @@ imageio_fill_suspended_buffer(j_decompress_ptr cinfo)
}
ret = (*env)->CallIntMethod(env, sb->stream,
ImageInputStream_readID,
JPEGImageReader_readInputDataID,
sb->hstreamBuffer,
offset, buflen);
if ((*env)->ExceptionOccurred(env)
@ -1107,7 +1107,7 @@ imageio_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
RELEASE_ARRAYS(env, data, src->next_input_byte);
ret = (*env)->CallLongMethod(env,
sb->stream,
ImageInputStream_skipBytesID,
JPEGImageReader_skipInputBytesID,
(jlong) num_bytes);
if ((*env)->ExceptionOccurred(env)
|| !GET_ARRAYS(env, data, &(src->next_input_byte))) {
@ -1382,13 +1382,13 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_initReaderIDs
jclass qTableClass,
jclass huffClass) {
ImageInputStream_readID = (*env)->GetMethodID(env,
ImageInputStreamClass,
"read",
JPEGImageReader_readInputDataID = (*env)->GetMethodID(env,
cls,
"readInputData",
"([BII)I");
ImageInputStream_skipBytesID = (*env)->GetMethodID(env,
ImageInputStreamClass,
"skipBytes",
JPEGImageReader_skipInputBytesID = (*env)->GetMethodID(env,
cls,
"skipInputBytes",
"(J)J");
JPEGImageReader_warningOccurredID = (*env)->GetMethodID(env,
cls,
@ -1531,8 +1531,7 @@ JNIEXPORT void JNICALL
Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_setSource
(JNIEnv *env,
jobject this,
jlong ptr,
jobject source) {
jlong ptr) {
imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
j_common_ptr cinfo;
@ -1546,7 +1545,7 @@ Java_com_sun_imageio_plugins_jpeg_JPEGImageReader_setSource
cinfo = data->jpegObj;
imageio_set_stream(env, cinfo, data, source);
imageio_set_stream(env, cinfo, data, this);
imageio_init_source((j_decompress_ptr) cinfo);
}