8007918: Better image writing
authorbae
Tue, 26 Feb 2013 01:41:36 +0400
changeset 16882 c9b0f63bb215
parent 16881 f105ea43e3aa
child 16883 8c290711866f
8007918: Better image writing Reviewed-by: mschoene, prr, jgodinez
jdk/src/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java
jdk/src/share/native/sun/awt/image/jpeg/imageioJPEG.c
--- a/jdk/src/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java	Tue Feb 26 00:41:40 2013 +0400
+++ b/jdk/src/share/classes/com/sun/imageio/plugins/jpeg/JPEGImageWriter.java	Tue Feb 26 01:41:36 2013 +0400
@@ -183,8 +183,7 @@
                     return null;
                 }
             });
-        initWriterIDs(ImageOutputStream.class,
-                      JPEGQTable.class,
+        initWriterIDs(JPEGQTable.class,
                       JPEGHuffmanTable.class);
     }
 
@@ -200,11 +199,13 @@
     public void setOutput(Object output) {
         setThreadLock();
         try {
+            cbLock.check();
+
             super.setOutput(output); // validates output
             resetInternalState();
             ios = (ImageOutputStream) output; // so this will always work
             // Set the native destination
-            setDest(structPointer, ios);
+            setDest(structPointer);
         } finally {
             clearThreadLock();
         }
@@ -359,6 +360,8 @@
                       ImageWriteParam param) throws IOException {
         setThreadLock();
         try {
+            cbLock.check();
+
             writeOnThread(streamMetadata, image, param);
         } finally {
             clearThreadLock();
@@ -1082,13 +1085,18 @@
                              haveMetadata,
                              restartInterval);
 
-        if (aborted) {
-            processWriteAborted();
-        } else {
-            processImageComplete();
+        cbLock.lock();
+        try {
+            if (aborted) {
+                processWriteAborted();
+            } else {
+                processImageComplete();
+            }
+
+            ios.flush();
+        } finally {
+            cbLock.unlock();
         }
-
-        ios.flush();
         currentImage++;  // After a successful write
     }
 
@@ -1096,6 +1104,8 @@
         throws IOException {
         setThreadLock();
         try {
+            cbLock.check();
+
             prepareWriteSequenceOnThread(streamMetadata);
         } finally {
             clearThreadLock();
@@ -1175,6 +1185,8 @@
         throws IOException {
         setThreadLock();
         try {
+            cbLock.check();
+
             if (sequencePrepared == false) {
                 throw new IllegalStateException("sequencePrepared not called!");
             }
@@ -1188,6 +1200,8 @@
     public void endWriteSequence() throws IOException {
         setThreadLock();
         try {
+            cbLock.check();
+
             if (sequencePrepared == false) {
                 throw new IllegalStateException("sequencePrepared not called!");
             }
@@ -1200,6 +1214,10 @@
     public synchronized void abort() {
         setThreadLock();
         try {
+            /**
+             * NB: we do not check the call back lock here, we allow to abort
+             * the reader any time.
+             */
             super.abort();
             abortWrite(structPointer);
         } finally {
@@ -1223,6 +1241,8 @@
     public void reset() {
         setThreadLock();
         try {
+            cbLock.check();
+
             super.reset();
         } finally {
             clearThreadLock();
@@ -1232,6 +1252,8 @@
     public void dispose() {
         setThreadLock();
         try {
+            cbLock.check();
+
             if (structPointer != 0) {
                 disposerRecord.dispose();
                 structPointer = 0;
@@ -1251,13 +1273,18 @@
      * sending warnings to listeners.
      */
     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
+                (currentImage,
+                 "com.sun.imageio.plugins.jpeg.JPEGImageWriterResources",
+                Integer.toString(code));
+        } finally {
+            cbLock.unlock();
         }
-        processWarningOccurred
-            (currentImage,
-             "com.sun.imageio.plugins.jpeg.JPEGImageWriterResources",
-             Integer.toString(code));
     }
 
     /**
@@ -1274,21 +1301,41 @@
      * library warnings from being printed to stderr.
      */
     void warningWithMessage(String msg) {
-        processWarningOccurred(currentImage, msg);
+        cbLock.lock();
+        try {
+            processWarningOccurred(currentImage, msg);
+        } 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();
+        }
     }
 
     ///////// End of Package-access API
@@ -1615,16 +1662,14 @@
     ////////////// Native methods and callbacks
 
     /** Sets up static native structures. */
-    private static native void initWriterIDs(Class iosClass,
-                                             Class qTableClass,
+    private static native void initWriterIDs(Class qTableClass,
                                              Class huffClass);
 
     /** Sets up per-writer native structure and returns a pointer to it. */
     private native long initJPEGImageWriter();
 
     /** Sets up native structures for output stream */
-    private native void setDest(long structPointer,
-                                ImageOutputStream ios);
+    private native void setDest(long structPointer);
 
     /**
      * Returns <code>true</code> if the write was aborted.
@@ -1749,7 +1794,12 @@
         }
         raster.setRect(sourceLine);
         if ((y > 7) && (y%8 == 0)) {  // Every 8 scanlines
-            processImageProgress((float) y / (float) sourceHeight * 100.0F);
+            cbLock.lock();
+            try {
+                processImageProgress((float) y / (float) sourceHeight * 100.0F);
+            } finally {
+                cbLock.unlock();
+            }
         }
     }
 
@@ -1777,6 +1827,25 @@
         }
     }
 
+    /**
+     * This method is called from native code in order to write encoder
+     * output to the destination.
+     *
+     * We block any attempt to change the writer state during this
+     * method, in order to prevent a corruption of the native encoder
+     * state.
+     */
+    private void writeOutputData(byte[] data, int offset, int len)
+            throws IOException
+    {
+        cbLock.lock();
+        try {
+            ios.write(data, offset, len);
+        } finally {
+            cbLock.unlock();
+        }
+    }
+
     private Thread theThread = null;
     private int theLockCount = 0;
 
@@ -1811,4 +1880,34 @@
             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 writer is not allowed");
+            }
+        }
+
+        private void lock() {
+            lockState = State.Locked;
+        }
+
+        private void unlock() {
+            lockState = State.Unlocked;
+        }
+
+        private static enum State {
+            Unlocked,
+            Locked
+        }
+    }
 }
--- a/jdk/src/share/native/sun/awt/image/jpeg/imageioJPEG.c	Tue Feb 26 00:41:40 2013 +0400
+++ b/jdk/src/share/native/sun/awt/image/jpeg/imageioJPEG.c	Tue Feb 26 01:41:36 2013 +0400
@@ -66,7 +66,7 @@
 static jmethodID JPEGImageReader_pushBackID;
 static jmethodID JPEGImageReader_passStartedID;
 static jmethodID JPEGImageReader_passCompleteID;
-static jmethodID ImageOutputStream_writeID;
+static jmethodID JPEGImageWriter_writeOutputDataID;
 static jmethodID JPEGImageWriter_warningOccurredID;
 static jmethodID JPEGImageWriter_warningWithMessageID;
 static jmethodID JPEGImageWriter_writeMetadataID;
@@ -2290,7 +2290,7 @@
 
     (*env)->CallVoidMethod(env,
                            sb->stream,
-                           ImageOutputStream_writeID,
+                           JPEGImageWriter_writeOutputDataID,
                            sb->hstreamBuffer,
                            0,
                            sb->bufferLength);
@@ -2327,7 +2327,7 @@
 
         (*env)->CallVoidMethod(env,
                                sb->stream,
-                               ImageOutputStream_writeID,
+                               JPEGImageWriter_writeOutputDataID,
                                sb->hstreamBuffer,
                                0,
                                datacount);
@@ -2365,13 +2365,12 @@
 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_initWriterIDs
     (JNIEnv *env,
      jclass cls,
-     jclass IOSClass,
      jclass qTableClass,
      jclass huffClass) {
 
-    ImageOutputStream_writeID = (*env)->GetMethodID(env,
-                                                    IOSClass,
-                                                    "write",
+    JPEGImageWriter_writeOutputDataID = (*env)->GetMethodID(env,
+                                                    cls,
+                                                    "writeOutputData",
                                                     "([BII)V");
 
     JPEGImageWriter_warningOccurredID = (*env)->GetMethodID(env,
@@ -2495,8 +2494,7 @@
 Java_com_sun_imageio_plugins_jpeg_JPEGImageWriter_setDest
     (JNIEnv *env,
      jobject this,
-     jlong ptr,
-     jobject destination) {
+     jlong ptr) {
 
     imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
     j_compress_ptr cinfo;
@@ -2510,7 +2508,7 @@
 
     cinfo = (j_compress_ptr) data->jpegObj;
 
-    imageio_set_stream(env, data->jpegObj, data, destination);
+    imageio_set_stream(env, data->jpegObj, data, this);
 
 
     // Don't call the init method, as that depends on pinned arrays