]> granicus.if.org Git - libjpeg-turbo/commitdiff
TJBench: Recover from non-fatal errors if possible
authorDRC <information@libjpeg-turbo.org>
Thu, 29 Jun 2017 21:49:09 +0000 (16:49 -0500)
committerDRC <information@libjpeg-turbo.org>
Thu, 29 Jun 2017 22:09:53 +0000 (17:09 -0500)
Previously, -stoponwarning only had an effect on the underlying
TurboJPEG C functions, but TJBench still aborted if a non-fatal error
occurred.  This commit modifies the C version of TJBench such that it
always recovers from a non-fatal error unless -stoponwarning is
specified.  Furthermore, the benchmark stores the details of the last
non-fatal error and does not print any subsequent non-fatal error
messages unless they differ from the last one.

Due to limitations in the Java API (specifically, the fact that it
cannot communicate errors, fatal or otherwise, to the calling program
without throwing a TJException), it was only possible to make
decompression operations fully recoverable within TJBench.  With other
operations, -stoponwarning still has an effect on the underlying C
library but has no effect at the Java level.

The Java API documentation has been amended to reflect that only certain
methods are truly recoverable, regardless of the state of
TJ.FLAG_STOPONWARNING.

java/TJBench.java
java/doc/org/libjpegturbo/turbojpeg/TJ.html
java/doc/org/libjpegturbo/turbojpeg/TJDecompressor.html
java/org/libjpegturbo/turbojpeg/TJ.java
java/org/libjpegturbo/turbojpeg/TJDecompressor.java
tjbench.c

index f1de27a07b46b5e1a9eb4c0da71c64fce02620d7..07338169fc80455dcbe2f5e121a45dcbc7051efe 100644 (file)
@@ -63,6 +63,26 @@ class TJBench {
   }
 
 
+  static String tjErrorMsg;
+  static int tjErrorCode = -1;
+
+  static void handleTJException(TJException e) throws TJException {
+    String _tjErrorMsg = e.getMessage();
+    int _tjErrorCode = e.getErrorCode();
+
+    if ((flags & TJ.FLAG_STOPONWARNING) == 0 &&
+        _tjErrorCode == TJ.ERR_WARNING) {
+      if (tjErrorMsg == null || !tjErrorMsg.equals(_tjErrorMsg) ||
+          tjErrorCode != _tjErrorCode) {
+        tjErrorMsg = _tjErrorMsg;
+        tjErrorCode = _tjErrorCode;
+        System.out.println("WARNING: " + _tjErrorMsg);
+      }
+    } else
+      throw e;
+  }
+
+
   static String formatName(int subsamp, int cs) {
     if (cs == TJ.CS_YCbCr)
       return subNameLong[subsamp];
@@ -174,14 +194,21 @@ class TJBench {
           tjd.setSourceImage(jpegBuf[tile], jpegSize[tile]);
           if (doYUV) {
             yuvImage.setBuf(yuvImage.getBuf(), width, yuvpad, height, subsamp);
-            tjd.decompressToYUV(yuvImage, flags);
+            try {
+              tjd.decompressToYUV(yuvImage, flags);
+            } catch (TJException e) { handleTJException(e); }
             double startDecode = getTime();
             tjd.setSourceImage(yuvImage);
-            tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags);
+            try {
+              tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags);
+            } catch (TJException e) { handleTJException(e); }
             if (iter >= 0)
               elapsedDecode += getTime() - startDecode;
-          } else
-            tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags);
+          } else {
+            try {
+              tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags);
+            } catch (TJException e) { handleTJException(e); }
+          }
         }
       }
       elapsed += getTime() - start;
index a544179da0a00a7e650562031027adc20211b882..d5691268f78ba3b77140a347d1ec8bea2dafdd2e 100644 (file)
@@ -995,7 +995,12 @@ public static final&nbsp;int FLAG_FORCESSE3</pre>
 <div class="block">Immediately discontinue the current compression/decompression/transform
  operation if the underlying codec throws a warning (non-fatal error).  The
  default behavior is to allow the operation to complete unless a fatal
- error is encountered.</div>
+ error is encountered.
+ <p>
+ NOTE: due to the design of the TurboJPEG Java API, only certain methods
+ (specifically, <a href="../../../org/libjpegturbo/turbojpeg/TJDecompressor.html" title="class in org.libjpegturbo.turbojpeg"><code>TJDecompressor.decompress*()</code></a> methods
+ with a void return type) will complete and leave the output image in a
+ fully recoverable state after a non-fatal error occurs.</div>
 <dl><dt><span class="strong">See Also:</span></dt><dd><a href="../../../constant-values.html#org.libjpegturbo.turbojpeg.TJ.FLAG_STOPONWARNING">Constant Field Values</a></dd></dl>
 </li>
 </ul>
@@ -1032,7 +1037,12 @@ public static final&nbsp;int FLAG_FORCESSE3</pre>
 <h4>ERR_WARNING</h4>
 <pre>public static final&nbsp;int ERR_WARNING</pre>
 <div class="block">The error was non-fatal and recoverable, but the image may still be
- corrupt.</div>
+ corrupt.
+ <p>
+ NOTE: due to the design of the TurboJPEG Java API, only certain methods
+ (specifically, <a href="../../../org/libjpegturbo/turbojpeg/TJDecompressor.html" title="class in org.libjpegturbo.turbojpeg"><code>TJDecompressor.decompress*()</code></a> methods
+ with a void return type) will complete and leave the output image in a
+ fully recoverable state after a non-fatal error occurs.</div>
 <dl><dt><span class="strong">See Also:</span></dt><dd><a href="../../../constant-values.html#org.libjpegturbo.turbojpeg.TJ.ERR_WARNING">Constant Field Values</a></dd></dl>
 </li>
 </ul>
index a914de9e02009bccee2a08849ef0ce3ffaca3ec5..8e332495229845d3ce19f1f89540bf8270d266cd 100644 (file)
@@ -785,7 +785,11 @@ public&nbsp;void&nbsp;setJPEGImage(byte[]&nbsp;jpegImage,
                 throws <a href="../../../org/libjpegturbo/turbojpeg/TJException.html" title="class in org.libjpegturbo.turbojpeg">TJException</a></pre>
 <div class="block">Decompress the JPEG source image or decode the YUV source image associated
  with this decompressor instance and output a grayscale, RGB, or CMYK image
- to the given destination buffer.</div>
+ to the given destination buffer.
+ <p>
+ NOTE: The output image is fully recoverable if this method throws a
+ non-fatal <a href="../../../org/libjpegturbo/turbojpeg/TJException.html" title="class in org.libjpegturbo.turbojpeg"><code>TJException</code></a> (unless
+ <a href="../../../org/libjpegturbo/turbojpeg/TJ.html#FLAG_STOPONWARNING"><code>TJ.FLAG_STOPONWARNING</code></a> is specified.)</div>
 <dl><dt><span class="strong">Parameters:</span></dt><dd><code>dstBuf</code> - buffer that will receive the decompressed/decoded image.
  If the source image is a JPEG image, then this buffer should normally be
  <code>pitch * scaledHeight</code> bytes in size, where
@@ -895,7 +899,11 @@ public&nbsp;void&nbsp;decompress(byte[]&nbsp;dstBuf,
  <code>YUVImage</code> instance.  This method performs JPEG decompression
  but leaves out the color conversion step, so a planar YUV image is
  generated instead of an RGB or grayscale image.  This method cannot be
- used to decompress JPEG source images with the CMYK or YCCK colorspace.</div>
+ used to decompress JPEG source images with the CMYK or YCCK colorspace.
+ <p>
+ NOTE: The YUV planar output image is fully recoverable if this method
+ throws a non-fatal <a href="../../../org/libjpegturbo/turbojpeg/TJException.html" title="class in org.libjpegturbo.turbojpeg"><code>TJException</code></a> (unless
+ <a href="../../../org/libjpegturbo/turbojpeg/TJ.html#FLAG_STOPONWARNING"><code>TJ.FLAG_STOPONWARNING</code></a> is specified.)</div>
 <dl><dt><span class="strong">Parameters:</span></dt><dd><code>dstImage</code> - <a href="../../../org/libjpegturbo/turbojpeg/YUVImage.html" title="class in org.libjpegturbo.turbojpeg"><code>YUVImage</code></a> instance that will receive the YUV planar
  image.  The level of subsampling specified in this <code>YUVImage</code>
  instance must match that of the JPEG image, and the width and height
@@ -1035,7 +1043,11 @@ public&nbsp;byte[]&nbsp;decompressToYUV(int&nbsp;flags)
                 throws <a href="../../../org/libjpegturbo/turbojpeg/TJException.html" title="class in org.libjpegturbo.turbojpeg">TJException</a></pre>
 <div class="block">Decompress the JPEG source image or decode the YUV source image associated
  with this decompressor instance and output a grayscale, RGB, or CMYK image
- to the given destination buffer.</div>
+ to the given destination buffer.
+ <p>
+ NOTE: The output image is fully recoverable if this method throws a
+ non-fatal <a href="../../../org/libjpegturbo/turbojpeg/TJException.html" title="class in org.libjpegturbo.turbojpeg"><code>TJException</code></a> (unless
+ <a href="../../../org/libjpegturbo/turbojpeg/TJ.html#FLAG_STOPONWARNING"><code>TJ.FLAG_STOPONWARNING</code></a> is specified.)</div>
 <dl><dt><span class="strong">Parameters:</span></dt><dd><code>dstBuf</code> - buffer that will receive the decompressed/decoded image.
  If the source image is a JPEG image, then this buffer should normally be
  <code>stride * scaledHeight</code> pixels in size, where
@@ -1092,7 +1104,11 @@ public&nbsp;byte[]&nbsp;decompressToYUV(int&nbsp;flags)
                 throws <a href="../../../org/libjpegturbo/turbojpeg/TJException.html" title="class in org.libjpegturbo.turbojpeg">TJException</a></pre>
 <div class="block">Decompress the JPEG source image or decode the YUV source image associated
  with this decompressor instance and output a decompressed/decoded image to
- the given <code>BufferedImage</code> instance.</div>
+ the given <code>BufferedImage</code> instance.
+ <p>
+ NOTE: The output image is fully recoverable if this method throws a
+ non-fatal <a href="../../../org/libjpegturbo/turbojpeg/TJException.html" title="class in org.libjpegturbo.turbojpeg"><code>TJException</code></a> (unless
+ <a href="../../../org/libjpegturbo/turbojpeg/TJ.html#FLAG_STOPONWARNING"><code>TJ.FLAG_STOPONWARNING</code></a> is specified.)</div>
 <dl><dt><span class="strong">Parameters:</span></dt><dd><code>dstImage</code> - a <code>BufferedImage</code> instance that will receive
  the decompressed/decoded image.  If the source image is a JPEG image, then
  the width and height of the <code>BufferedImage</code> instance must match
index 045e82952bedf60d1be832ac3df48b87c66c16fa..ee000a9054fa86539991006888e70cc718dac02a 100644 (file)
@@ -391,6 +391,11 @@ public final class TJ {
    * operation if the underlying codec throws a warning (non-fatal error).  The
    * default behavior is to allow the operation to complete unless a fatal
    * error is encountered.
+   * <p>
+   * NOTE: due to the design of the TurboJPEG Java API, only certain methods
+   * (specifically, {@link TJDecompressor TJDecompressor.decompress*()} methods
+   * with a void return type) will complete and leave the output image in a
+   * fully recoverable state after a non-fatal error occurs.
    */
   public static final int FLAG_STOPONWARNING = 8192;
   /**
@@ -409,6 +414,11 @@ public final class TJ {
   /**
    * The error was non-fatal and recoverable, but the image may still be
    * corrupt.
+   * <p>
+   * NOTE: due to the design of the TurboJPEG Java API, only certain methods
+   * (specifically, {@link TJDecompressor TJDecompressor.decompress*()} methods
+   * with a void return type) will complete and leave the output image in a
+   * fully recoverable state after a non-fatal error occurs.
    */
   public static final int ERR_WARNING = 0;
   /**
index bd0e6943d977aa8243360efb181f9e2230475f48..be71cb08b5a86cf310417f5217256794e20d3c59 100644 (file)
@@ -308,6 +308,10 @@ public class TJDecompressor implements Closeable {
    * Decompress the JPEG source image or decode the YUV source image associated
    * with this decompressor instance and output a grayscale, RGB, or CMYK image
    * to the given destination buffer.
+   * <p>
+   * NOTE: The output image is fully recoverable if this method throws a
+   * non-fatal {@link TJException} (unless
+   * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
    *
    * @param dstBuf buffer that will receive the decompressed/decoded image.
    * If the source image is a JPEG image, then this buffer should normally be
@@ -451,6 +455,10 @@ public class TJDecompressor implements Closeable {
    * but leaves out the color conversion step, so a planar YUV image is
    * generated instead of an RGB or grayscale image.  This method cannot be
    * used to decompress JPEG source images with the CMYK or YCCK colorspace.
+   * <p>
+   * NOTE: The YUV planar output image is fully recoverable if this method
+   * throws a non-fatal {@link TJException} (unless
+   * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
    *
    * @param dstImage {@link YUVImage} instance that will receive the YUV planar
    * image.  The level of subsampling specified in this <code>YUVImage</code>
@@ -618,6 +626,10 @@ public class TJDecompressor implements Closeable {
    * Decompress the JPEG source image or decode the YUV source image associated
    * with this decompressor instance and output a grayscale, RGB, or CMYK image
    * to the given destination buffer.
+   * <p>
+   * NOTE: The output image is fully recoverable if this method throws a
+   * non-fatal {@link TJException} (unless
+   * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
    *
    * @param dstBuf buffer that will receive the decompressed/decoded image.
    * If the source image is a JPEG image, then this buffer should normally be
@@ -699,6 +711,10 @@ public class TJDecompressor implements Closeable {
    * Decompress the JPEG source image or decode the YUV source image associated
    * with this decompressor instance and output a decompressed/decoded image to
    * the given <code>BufferedImage</code> instance.
+   * <p>
+   * NOTE: The output image is fully recoverable if this method throws a
+   * non-fatal {@link TJException} (unless
+   * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
    *
    * @param dstImage a <code>BufferedImage</code> instance that will receive
    * the decompressed/decoded image.  If the source image is a JPEG image, then
index 450e1280dc8691d1d80c9e7b5b77cba1d44f9e6b..a62751015bfe96b9ccea8e087f5ae54213bcdb82 100644 (file)
--- a/tjbench.c
+++ b/tjbench.c
 #include "./turbojpeg.h"
 
 
-#define _throw(op, err) {  \
+#define _throw(op, err)  \
+{  \
        printf("ERROR in line %d while %s:\n%s\n", __LINE__, op, err);  \
-       retval=-1;  goto bailout;}
+       retval=-1;  goto bailout;  \
+}
 #define _throwunix(m) _throw(m, strerror(errno))
-#define _throwtj(m) {  \
-       printf("%s in line %d while %s:\n%s\n",  \
-               tjGetErrorCode(handle)==TJERR_WARNING ? "WARNING" : "ERROR", __LINE__,  \
-               m, tjGetErrorStr2(handle));  \
-       retval=-1;  goto bailout;}
+
+char tjErrorStr[JMSG_LENGTH_MAX]="\0", tjErrorMsg[JMSG_LENGTH_MAX]="\0";
+int tjErrorLine=-1, tjErrorCode=-1;
+
+#define _throwtj(m)  \
+{  \
+       int _tjErrorCode=tjGetErrorCode(handle);  \
+       char *_tjErrorStr=tjGetErrorStr2(handle);  \
+  \
+       if(!(flags&TJFLAG_STOPONWARNING) && _tjErrorCode==TJERR_WARNING)  \
+       {  \
+               if(strncmp(tjErrorStr, _tjErrorStr, JMSG_LENGTH_MAX) ||  \
+                       strncmp(tjErrorMsg, m, JMSG_LENGTH_MAX) ||  \
+                       tjErrorCode!=_tjErrorCode || tjErrorLine!=__LINE__)  \
+               {  \
+                       strncpy(tjErrorStr, _tjErrorStr, JMSG_LENGTH_MAX);  \
+                       strncpy(tjErrorMsg, m, JMSG_LENGTH_MAX);  \
+                       tjErrorCode=_tjErrorCode;  \
+                       tjErrorLine=__LINE__;  \
+                       printf("WARNING in line %d while %s:\n%s\n", __LINE__, m,  \
+                               _tjErrorStr);  \
+               }  \
+       }  \
+       else  \
+       {  \
+               printf("%s in line %d while %s:\n%s\n",  \
+                       _tjErrorCode==TJERR_WARNING ? "WARNING" : "ERROR", __LINE__, m,  \
+                       _tjErrorStr);  \
+               retval=-1;  goto bailout;  \
+       }  \
+}
+
 #define _throwbmp(m) _throw(m, bmpgeterr())
 
 int flags=TJFLAG_NOREALLOC, componly=0, decomponly=0, doyuv=0, quiet=0,