libjpeg-turbo: Upgrade to 1.5.1

The changes from 1.4.2 to 1.5.1 include
a big amount of fixes and huge performance improvements.
As highlights there is a full ARM 64-bit (ARMv8) NEON SIMD
implementation which improves compression of full-color JPEGs
by about 2-2.5x on average on Cortex-A53 and Cortex-A57 cores.
Also  SIMD acceleration for Huffman encoding on NEON-capable
ARM 32-bit and 64-bit platforms was added.
Performance on x86/x86_64 was also improved by
adding better optimized SSE2 routines.

For the full changelog, please see the ChangeLog.md
file.

Partial decoding optimizations, the security fix
to adress b/27494207 and the fix for the AARCH64
conformance issueare present in the release.
The README.android file was edited to reflect this.

The configuration files were regenerated
and all Android specific changes were applied.

BUG:28268702

Change-Id: I538291d894df1da01d3f733771647df1fb61ec42
Signed-off-by: Alex Naidis <alex.naidis@linux.com>
diff --git a/java/org/libjpegturbo/turbojpeg/TJ.java b/java/org/libjpegturbo/turbojpeg/TJ.java
index 644a197..02d14c0 100644
--- a/java/org/libjpegturbo/turbojpeg/TJ.java
+++ b/java/org/libjpegturbo/turbojpeg/TJ.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C)2011-2013 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2015 Viktor Szathmáry.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -87,9 +88,8 @@
    * @return the MCU block width for the given level of chrominance
    * subsampling.
    */
-  public static int getMCUWidth(int subsamp) throws Exception {
-    if (subsamp < 0 || subsamp >= NUMSAMP)
-      throw new Exception("Invalid subsampling type");
+  public static int getMCUWidth(int subsamp) {
+    checkSubsampling(subsamp);
     return mcuWidth[subsamp];
   }
 
@@ -108,9 +108,8 @@
    * @return the MCU block height for the given level of chrominance
    * subsampling.
    */
-  public static int getMCUHeight(int subsamp) throws Exception {
-    if (subsamp < 0 || subsamp >= NUMSAMP)
-      throw new Exception("Invalid subsampling type");
+  public static int getMCUHeight(int subsamp) {
+    checkSubsampling(subsamp);
     return mcuHeight[subsamp];
   }
 
@@ -217,9 +216,8 @@
    *
    * @return the pixel size (in bytes) for the given pixel format.
    */
-  public static int getPixelSize(int pixelFormat) throws Exception {
-    if (pixelFormat < 0 || pixelFormat >= NUMPF)
-      throw new Exception("Invalid pixel format");
+  public static int getPixelSize(int pixelFormat) {
+    checkPixelFormat(pixelFormat);
     return pixelSize[pixelFormat];
   }
 
@@ -239,9 +237,8 @@
    *
    * @return the red offset for the given pixel format.
    */
-  public static int getRedOffset(int pixelFormat) throws Exception {
-    if (pixelFormat < 0 || pixelFormat >= NUMPF)
-      throw new Exception("Invalid pixel format");
+  public static int getRedOffset(int pixelFormat) {
+    checkPixelFormat(pixelFormat);
     return redOffset[pixelFormat];
   }
 
@@ -261,9 +258,8 @@
    *
    * @return the green offset for the given pixel format.
    */
-  public static int getGreenOffset(int pixelFormat) throws Exception {
-    if (pixelFormat < 0 || pixelFormat >= NUMPF)
-      throw new Exception("Invalid pixel format");
+  public static int getGreenOffset(int pixelFormat) {
+    checkPixelFormat(pixelFormat);
     return greenOffset[pixelFormat];
   }
 
@@ -283,9 +279,8 @@
    *
    * @return the blue offset for the given pixel format.
    */
-  public static int getBlueOffset(int pixelFormat) throws Exception {
-    if (pixelFormat < 0 || pixelFormat >= NUMPF)
-      throw new Exception("Invalid pixel format");
+  public static int getBlueOffset(int pixelFormat) {
+    checkPixelFormat(pixelFormat);
     return blueOffset[pixelFormat];
   }
 
@@ -407,8 +402,7 @@
    * @return the maximum size of the buffer (in bytes) required to hold a JPEG
    * image with the given width, height, and level of chrominance subsampling.
    */
-  public static native int bufSize(int width, int height, int jpegSubsamp)
-    throws Exception;
+  public static native int bufSize(int width, int height, int jpegSubsamp);
 
   /**
    * Returns the size of the buffer (in bytes) required to hold a YUV planar
@@ -428,15 +422,13 @@
    * image with the given width, height, and level of chrominance subsampling.
    */
   public static native int bufSizeYUV(int width, int pad, int height,
-                                      int subsamp)
-    throws Exception;
+                                      int subsamp);
 
   /**
    * @deprecated Use {@link #bufSizeYUV(int, int, int, int)} instead.
    */
   @Deprecated
-  public static native int bufSizeYUV(int width, int height, int subsamp)
-    throws Exception;
+  public static native int bufSizeYUV(int width, int height, int subsamp);
 
   /**
    * Returns the size of the buffer (in bytes) required to hold a YUV image
@@ -460,8 +452,7 @@
    * image with the given parameters.
    */
   public static native int planeSizeYUV(int componentID, int width, int stride,
-                                        int height, int subsamp)
-    throws Exception;
+                                        int height, int subsamp);
 
   /**
    * Returns the plane width of a YUV image plane with the given parameters.
@@ -477,8 +468,7 @@
    *
    * @return the plane width of a YUV image plane with the given parameters.
    */
-  public static native int planeWidth(int componentID, int width, int subsamp)
-    throws Exception;
+  public static native int planeWidth(int componentID, int width, int subsamp);
 
   /**
    * Returns the plane height of a YUV image plane with the given parameters.
@@ -495,8 +485,7 @@
    * @return the plane height of a YUV image plane with the given parameters.
    */
   public static native int planeHeight(int componentID, int height,
-                                       int subsamp)
-    throws Exception;
+                                       int subsamp);
 
   /**
    * Returns a list of fractional scaling factors that the JPEG decompressor in
@@ -505,10 +494,20 @@
    * @return a list of fractional scaling factors that the JPEG decompressor in
    * this implementation of TurboJPEG supports.
    */
-  public static native TJScalingFactor[] getScalingFactors()
-    throws Exception;
+  public static native TJScalingFactor[] getScalingFactors();
 
   static {
     TJLoader.load();
   }
-};
+
+  private static void checkPixelFormat(int pixelFormat) {
+    if (pixelFormat < 0 || pixelFormat >= NUMPF)
+      throw new IllegalArgumentException("Invalid pixel format");
+  }
+
+  private static void checkSubsampling(int subsamp) {
+    if (subsamp < 0 || subsamp >= NUMSAMP)
+      throw new IllegalArgumentException("Invalid subsampling type");
+  }
+
+}
diff --git a/java/org/libjpegturbo/turbojpeg/TJCompressor.java b/java/org/libjpegturbo/turbojpeg/TJCompressor.java
index 6ec581a..2ff8e4d 100644
--- a/java/org/libjpegturbo/turbojpeg/TJCompressor.java
+++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C)2011-2015 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2015 Viktor Szathmáry.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -30,11 +31,12 @@
 
 import java.awt.image.*;
 import java.nio.*;
+import java.io.*;
 
 /**
  * TurboJPEG compressor
  */
-public class TJCompressor {
+public class TJCompressor implements Closeable {
 
   private static final String NO_ASSOC_ERROR =
     "No source image is associated with this instance";
@@ -42,7 +44,7 @@
   /**
    * Create a TurboJPEG compressor instance.
    */
-  public TJCompressor() throws Exception {
+  public TJCompressor() throws TJException {
     init();
   }
 
@@ -67,7 +69,7 @@
    * {@link TJ#PF_RGB TJ.PF_*})
    */
   public TJCompressor(byte[] srcImage, int x, int y, int width, int pitch,
-                      int height, int pixelFormat) throws Exception {
+                      int height, int pixelFormat) throws TJException {
     setSourceImage(srcImage, x, y, width, pitch, height, pixelFormat);
   }
 
@@ -77,7 +79,7 @@
    */
   @Deprecated
   public TJCompressor(byte[] srcImage, int width, int pitch, int height,
-                      int pixelFormat) throws Exception {
+                      int pixelFormat) throws TJException {
     setSourceImage(srcImage, width, pitch, height, pixelFormat);
   }
 
@@ -102,7 +104,7 @@
    * {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
    */
   public TJCompressor(BufferedImage srcImage, int x, int y, int width,
-                      int height) throws Exception {
+                      int height) throws TJException {
     setSourceImage(srcImage, x, y, width, height);
   }
 
@@ -139,11 +141,11 @@
    */
   public void setSourceImage(byte[] srcImage, int x, int y, int width,
                              int pitch, int height, int pixelFormat)
-                             throws Exception {
+                             throws TJException {
     if (handle == 0) init();
     if (srcImage == null || x < 0 || y < 0 || width < 1 || height < 1 ||
         pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF)
-      throw new Exception("Invalid argument in setSourceImage()");
+      throw new IllegalArgumentException("Invalid argument in setSourceImage()");
     srcBuf = srcImage;
     srcWidth = width;
     if (pitch == 0)
@@ -164,7 +166,7 @@
    */
   @Deprecated
   public void setSourceImage(byte[] srcImage, int width, int pitch,
-                             int height, int pixelFormat) throws Exception {
+                             int height, int pixelFormat) throws TJException {
     setSourceImage(srcImage, 0, 0, width, pitch, height, pixelFormat);
     srcX = srcY = -1;
   }
@@ -191,16 +193,16 @@
    * height of the source image)
    */
   public void setSourceImage(BufferedImage srcImage, int x, int y, int width,
-                             int height) throws Exception {
+                             int height) throws TJException {
     if (handle == 0) init();
     if (srcImage == null || x < 0 || y < 0 || width < 0 || height < 0)
-      throw new Exception("Invalid argument in setSourceImage()");
+      throw new IllegalArgumentException("Invalid argument in setSourceImage()");
     srcX = x;
     srcY = y;
     srcWidth = (width == 0) ? srcImage.getWidth(): width;
     srcHeight = (height == 0) ? srcImage.getHeight() : height;
     if (x + width > srcImage.getWidth() || y + height > srcImage.getHeight())
-      throw new Exception("Compression region exceeds the bounds of the source image");
+      throw new IllegalArgumentException("Compression region exceeds the bounds of the source image");
 
     int pixelFormat;
     boolean intPixels = false;
@@ -229,7 +231,7 @@
           pixelFormat = TJ.PF_BGRX;
         intPixels = true;  break;
       default:
-        throw new Exception("Unsupported BufferedImage format");
+        throw new IllegalArgumentException("Unsupported BufferedImage format");
     }
     srcPixelFormat = pixelFormat;
 
@@ -246,7 +248,7 @@
         (ComponentSampleModel)srcImage.getSampleModel();
       int pixelSize = sm.getPixelStride();
       if (pixelSize != TJ.getPixelSize(pixelFormat))
-        throw new Exception("Inconsistency between pixel format and pixel size in BufferedImage");
+        throw new IllegalArgumentException("Inconsistency between pixel format and pixel size in BufferedImage");
       srcPitch = sm.getScanlineStride();
       DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
       srcBuf = db.getData();
@@ -262,10 +264,10 @@
    * @param srcImage YUV planar image to be compressed.  This image is not
    * modified.
    */
-  public void setSourceImage(YUVImage srcImage) throws Exception {
+  public void setSourceImage(YUVImage srcImage) throws TJException {
     if (handle == 0) init();
     if (srcImage == null)
-      throw new Exception("Invalid argument in setSourceImage()");
+      throw new IllegalArgumentException("Invalid argument in setSourceImage()");
     srcYUVImage = srcImage;
     srcBuf = null;
     srcBufInt = null;
@@ -292,9 +294,9 @@
    * subsequent compress/encode oeprations (one of
    * {@link TJ#SAMP_444 TJ.SAMP_*})
    */
-  public void setSubsamp(int newSubsamp) throws Exception {
+  public void setSubsamp(int newSubsamp) {
     if (newSubsamp < 0 || newSubsamp >= TJ.NUMSAMP)
-      throw new Exception("Invalid argument in setSubsamp()");
+      throw new IllegalArgumentException("Invalid argument in setSubsamp()");
     subsamp = newSubsamp;
   }
 
@@ -304,9 +306,9 @@
    * @param quality the new JPEG image quality level (1 to 100, 1 = worst,
    * 100 = best)
    */
-  public void setJPEGQuality(int quality) throws Exception {
+  public void setJPEGQuality(int quality) {
     if (quality < 1 || quality > 100)
-      throw new Exception("Invalid argument in setJPEGQuality()");
+      throw new IllegalArgumentException("Invalid argument in setJPEGQuality()");
     jpegQuality = quality;
   }
 
@@ -322,15 +324,15 @@
    * @param flags the bitwise OR of one or more of
    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
    */
-  public void compress(byte[] dstBuf, int flags) throws Exception {
+  public void compress(byte[] dstBuf, int flags) throws TJException {
     if (dstBuf == null || flags < 0)
-      throw new Exception("Invalid argument in compress()");
+      throw new IllegalArgumentException("Invalid argument in compress()");
     if (srcBuf == null && srcBufInt == null && srcYUVImage == null)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (jpegQuality < 0)
-      throw new Exception("JPEG Quality not set");
+      throw new IllegalStateException("JPEG Quality not set");
     if (subsamp < 0 && srcYUVImage == null)
-      throw new Exception("Subsampling level not set");
+      throw new IllegalStateException("Subsampling level not set");
 
     if (srcYUVImage != null)
       compressedSize = compressFromYUV(srcYUVImage.getPlanes(),
@@ -372,9 +374,8 @@
    * not be equal to the size of the JPEG image.  Use {@link
    * #getCompressedSize} to obtain the size of the JPEG image.
    */
-  public byte[] compress(int flags) throws Exception {
-    if (srcWidth < 1 || srcHeight < 1)
-      throw new Exception(NO_ASSOC_ERROR);
+  public byte[] compress(int flags) throws TJException {
+    checkSourceImage();
     byte[] buf = new byte[TJ.bufSize(srcWidth, srcHeight, subsamp)];
     compress(buf, flags);
     return buf;
@@ -387,7 +388,7 @@
    */
   @Deprecated
   public void compress(BufferedImage srcImage, byte[] dstBuf, int flags)
-                       throws Exception {
+                       throws TJException {
     setSourceImage(srcImage, 0, 0, 0, 0);
     compress(dstBuf, flags);
   }
@@ -398,7 +399,8 @@
    * {@link #compress(int)} instead.
    */
   @Deprecated
-  public byte[] compress(BufferedImage srcImage, int flags) throws Exception {
+  public byte[] compress(BufferedImage srcImage, int flags)
+                         throws TJException {
     setSourceImage(srcImage, 0, 0, 0, 0);
     return compress(flags);
   }
@@ -417,17 +419,16 @@
    * @param flags the bitwise OR of one or more of
    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
    */
-  public void encodeYUV(YUVImage dstImage, int flags) throws Exception {
+  public void encodeYUV(YUVImage dstImage, int flags) throws TJException {
     if (dstImage == null || flags < 0)
-      throw new Exception("Invalid argument in encodeYUV()");
+      throw new IllegalArgumentException("Invalid argument in encodeYUV()");
     if (srcBuf == null && srcBufInt == null)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (srcYUVImage != null)
-      throw new Exception("Source image is not correct type");
-    if (subsamp < 0)
-      throw new Exception("Subsampling level not set");
+      throw new IllegalStateException("Source image is not correct type");
+    checkSubsampling();
     if (srcWidth != dstImage.getWidth() || srcHeight != dstImage.getHeight())
-      throw new Exception("Destination image is the wrong size");
+      throw new IllegalStateException("Destination image is the wrong size");
 
     if (srcBufInt != null) {
       encodeYUV(srcBufInt, srcX, srcY, srcWidth, srcStride, srcHeight,
@@ -445,13 +446,11 @@
    * @deprecated Use {@link #encodeYUV(YUVImage, int)} instead.
    */
   @Deprecated
-  public void encodeYUV(byte[] dstBuf, int flags) throws Exception {
+  public void encodeYUV(byte[] dstBuf, int flags) throws TJException {
     if(dstBuf == null)
-      throw new Exception("Invalid argument in encodeYUV()");
-    if (srcWidth < 1 || srcHeight < 1)
-      throw new Exception(NO_ASSOC_ERROR);
-    if (subsamp < 0)
-      throw new Exception("Subsampling level not set");
+      throw new IllegalArgumentException("Invalid argument in encodeYUV()");
+    checkSourceImage();
+    checkSubsampling();
     YUVImage yuvImage = new YUVImage(dstBuf, srcWidth, 4, srcHeight, subsamp);
     encodeYUV(yuvImage, flags);
   }
@@ -473,13 +472,11 @@
    *
    * @return a YUV planar image.
    */
-  public YUVImage encodeYUV(int pad, int flags) throws Exception {
-    if (srcWidth < 1 || srcHeight < 1)
-      throw new Exception(NO_ASSOC_ERROR);
-    if (subsamp < 0)
-      throw new Exception("Subsampling level not set");
+  public YUVImage encodeYUV(int pad, int flags) throws TJException {
+    checkSourceImage();
+    checkSubsampling();
     if(pad < 1 || ((pad & (pad - 1)) != 0))
-      throw new Exception("Invalid argument in encodeYUV()");
+      throw new IllegalStateException("Invalid argument in encodeYUV()");
     YUVImage yuvImage = new YUVImage(srcWidth, pad, srcHeight, subsamp);
     encodeYUV(yuvImage, flags);
     return yuvImage;
@@ -506,11 +503,9 @@
    *
    * @return a YUV planar image.
    */
-  public YUVImage encodeYUV(int[] strides, int flags) throws Exception {
-    if (srcWidth < 1 || srcHeight < 1)
-      throw new Exception(NO_ASSOC_ERROR);
-    if (subsamp < 0)
-      throw new Exception("Subsampling level not set");
+  public YUVImage encodeYUV(int[] strides, int flags) throws TJException {
+    checkSourceImage();
+    checkSubsampling();
     YUVImage yuvImage = new YUVImage(srcWidth, strides, srcHeight, subsamp);
     encodeYUV(yuvImage, flags);
     return yuvImage;
@@ -520,11 +515,9 @@
    * @deprecated Use {@link #encodeYUV(int, int)} instead.
    */
   @Deprecated
-  public byte[] encodeYUV(int flags) throws Exception {
-    if (srcWidth < 1 || srcHeight < 1)
-      throw new Exception(NO_ASSOC_ERROR);
-    if (subsamp < 0)
-      throw new Exception("Subsampling level not set");
+  public byte[] encodeYUV(int flags) throws TJException {
+    checkSourceImage();
+    checkSubsampling();
     YUVImage yuvImage = new YUVImage(srcWidth, 4, srcHeight, subsamp);
     encodeYUV(yuvImage, flags);
     return yuvImage.getBuf();
@@ -537,7 +530,7 @@
    */
   @Deprecated
   public void encodeYUV(BufferedImage srcImage, byte[] dstBuf, int flags)
-    throws Exception {
+                        throws TJException {
     setSourceImage(srcImage, 0, 0, 0, 0);
     encodeYUV(dstBuf, flags);
   }
@@ -548,7 +541,8 @@
    * {@link #encodeYUV(int, int)} instead.
    */
   @Deprecated
-  public byte[] encodeYUV(BufferedImage srcImage, int flags) throws Exception {
+  public byte[] encodeYUV(BufferedImage srcImage, int flags)
+                          throws TJException {
     setSourceImage(srcImage, 0, 0, 0, 0);
     return encodeYUV(flags);
   }
@@ -567,68 +561,84 @@
   /**
    * Free the native structures associated with this compressor instance.
    */
-  public void close() throws Exception {
+  @Override
+  public void close() throws TJException {
     if (handle != 0)
       destroy();
   }
 
+  @Override
   protected void finalize() throws Throwable {
     try {
       close();
-    } catch(Exception e) {
+    } catch(TJException e) {
     } finally {
       super.finalize();
     }
   };
 
-  private native void init() throws Exception;
+  private native void init() throws TJException;
 
-  private native void destroy() throws Exception;
+  private native void destroy() throws TJException;
 
   // JPEG size in bytes is returned
+  @Deprecated
   private native int compress(byte[] srcBuf, int width, int pitch,
     int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp, int jpegQual,
-    int flags) throws Exception; // deprecated
+    int flags) throws TJException;
 
   private native int compress(byte[] srcBuf, int x, int y, int width,
     int pitch, int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp,
-    int jpegQual, int flags) throws Exception;
+    int jpegQual, int flags) throws TJException;
 
+  @Deprecated
   private native int compress(int[] srcBuf, int width, int stride,
     int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp, int jpegQual,
-    int flags) throws Exception; // deprecated
+    int flags) throws TJException;
 
   private native int compress(int[] srcBuf, int x, int y, int width,
     int stride, int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp,
-    int jpegQual, int flags) throws Exception;
+    int jpegQual, int flags) throws TJException;
 
   private native int compressFromYUV(byte[][] srcPlanes, int[] srcOffsets,
     int width, int[] srcStrides, int height, int subsamp, byte[] dstBuf,
     int jpegQual, int flags)
-    throws Exception;
+    throws TJException;
 
+  @Deprecated
   private native void encodeYUV(byte[] srcBuf, int width, int pitch,
     int height, int pixelFormat, byte[] dstBuf, int subsamp, int flags)
-    throws Exception; // deprecated
+    throws TJException;
 
   private native void encodeYUV(byte[] srcBuf, int x, int y, int width,
     int pitch, int height, int pixelFormat, byte[][] dstPlanes,
     int[] dstOffsets, int[] dstStrides, int subsamp, int flags)
-    throws Exception;
+    throws TJException;
 
+  @Deprecated
   private native void encodeYUV(int[] srcBuf, int width, int stride,
     int height, int pixelFormat, byte[] dstBuf, int subsamp, int flags)
-    throws Exception; // deprecated
+    throws TJException;
 
   private native void encodeYUV(int[] srcBuf, int x, int y, int width,
     int srcStride, int height, int pixelFormat, byte[][] dstPlanes,
     int[] dstOffsets, int[] dstStrides, int subsamp, int flags)
-    throws Exception;
+    throws TJException;
 
   static {
     TJLoader.load();
   }
 
+  private void checkSourceImage() {
+    if (srcWidth < 1 || srcHeight < 1)
+      throw new IllegalStateException(NO_ASSOC_ERROR);
+  }
+
+  private void checkSubsampling() {
+    if (subsamp < 0)
+      throw new IllegalStateException("Subsampling level not set");
+  }
+
   private long handle = 0;
   private byte[] srcBuf = null;
   private int[] srcBufInt = null;
@@ -645,4 +655,4 @@
   private int compressedSize = 0;
   private int yuvPad = 4;
   private ByteOrder byteOrder = null;
-};
+}
diff --git a/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java b/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java
index bf78f2e..9a34587 100644
--- a/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java
+++ b/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java
@@ -72,5 +72,5 @@
   void customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion,
                     Rectangle planeRegion, int componentID, int transformID,
                     TJTransform transform)
-    throws Exception;
+    throws TJException;
 }
diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java
index 7ec557f..bd0e694 100644
--- a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java
+++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C)2011-2015 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2015 Viktor Szathmáry.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -30,11 +31,12 @@
 
 import java.awt.image.*;
 import java.nio.*;
+import java.io.*;
 
 /**
  * TurboJPEG decompressor
  */
-public class TJDecompressor {
+public class TJDecompressor implements Closeable {
 
   private static final String NO_ASSOC_ERROR =
     "No JPEG image is associated with this instance";
@@ -42,7 +44,7 @@
   /**
    * Create a TurboJPEG decompresssor instance.
    */
-  public TJDecompressor() throws Exception {
+  public TJDecompressor() throws TJException {
     init();
   }
 
@@ -53,7 +55,7 @@
    * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to
    * be the length of the array.)  This buffer is not modified.
    */
-  public TJDecompressor(byte[] jpegImage) throws Exception {
+  public TJDecompressor(byte[] jpegImage) throws TJException {
     init();
     setSourceImage(jpegImage, jpegImage.length);
   }
@@ -67,7 +69,7 @@
    *
    * @param imageSize size of the JPEG image (in bytes)
    */
-  public TJDecompressor(byte[] jpegImage, int imageSize) throws Exception {
+  public TJDecompressor(byte[] jpegImage, int imageSize) throws TJException {
     init();
     setSourceImage(jpegImage, imageSize);
   }
@@ -80,7 +82,7 @@
    * @param yuvImage {@link YUVImage} instance containing a YUV planar
    * image to be decoded.  This image is not modified.
    */
-  public TJDecompressor(YUVImage yuvImage) throws Exception {
+  public TJDecompressor(YUVImage yuvImage) throws TJException {
     init();
     setSourceImage(yuvImage);
   }
@@ -95,9 +97,9 @@
    * @param imageSize size of the JPEG image (in bytes)
    */
   public void setSourceImage(byte[] jpegImage, int imageSize)
-    throws Exception {
+                             throws TJException {
     if (jpegImage == null || imageSize < 1)
-      throw new Exception("Invalid argument in setSourceImage()");
+      throw new IllegalArgumentException("Invalid argument in setSourceImage()");
     jpegBuf = jpegImage;
     jpegBufSize = imageSize;
     decompressHeader(jpegBuf, jpegBufSize);
@@ -108,7 +110,8 @@
    * @deprecated Use {@link #setSourceImage(byte[], int)} instead.
    */
   @Deprecated
-  public void setJPEGImage(byte[] jpegImage, int imageSize) throws Exception {
+  public void setJPEGImage(byte[] jpegImage, int imageSize)
+                           throws TJException {
     setSourceImage(jpegImage, imageSize);
   }
 
@@ -120,9 +123,9 @@
    * @param srcImage {@link YUVImage} instance containing a YUV planar image to
    * be decoded.  This image is not modified.
    */
-  public void setSourceImage(YUVImage srcImage) throws Exception {
+  public void setSourceImage(YUVImage srcImage) {
     if (srcImage == null)
-      throw new Exception("Invalid argument in setSourceImage()");
+      throw new IllegalArgumentException("Invalid argument in setSourceImage()");
     yuvImage = srcImage;
     jpegBuf = null;
     jpegBufSize = 0;
@@ -136,11 +139,11 @@
    * @return the width of the source image (JPEG or YUV) associated with this
    * decompressor instance.
    */
-  public int getWidth() throws Exception {
+  public int getWidth() {
     if (yuvImage != null)
       return yuvImage.getWidth();
     if (jpegWidth < 1)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     return jpegWidth;
   }
 
@@ -151,11 +154,11 @@
    * @return the height of the source image (JPEG or YUV) associated with this
    * decompressor instance.
    */
-  public int getHeight() throws Exception {
+  public int getHeight() {
     if (yuvImage != null)
       return yuvImage.getHeight();
     if (jpegHeight < 1)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     return jpegHeight;
   }
 
@@ -167,13 +170,13 @@
    * @return the level of chrominance subsampling used in the source image
    * (JPEG or YUV) associated with this decompressor instance.
    */
-  public int getSubsamp() throws Exception {
+  public int getSubsamp() {
     if (yuvImage != null)
       return yuvImage.getSubsamp();
     if (jpegSubsamp < 0)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (jpegSubsamp >= TJ.NUMSAMP)
-      throw new Exception("JPEG header information is invalid");
+      throw new IllegalStateException("JPEG header information is invalid");
     return jpegSubsamp;
   }
 
@@ -185,13 +188,13 @@
    * @return the colorspace used in the source image (JPEG or YUV) associated
    * with this decompressor instance.
    */
-  public int getColorspace() throws Exception {
+  public int getColorspace() {
     if (yuvImage != null)
       return TJ.CS_YCbCr;
     if (jpegColorspace < 0)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (jpegColorspace >= TJ.NUMCS)
-      throw new Exception("JPEG header information is invalid");
+      throw new IllegalStateException("JPEG header information is invalid");
     return jpegColorspace;
   }
 
@@ -200,9 +203,9 @@
    *
    * @return the JPEG image buffer associated with this decompressor instance.
    */
-  public byte[] getJPEGBuf() throws Exception {
+  public byte[] getJPEGBuf() {
     if (jpegBuf == null)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     return jpegBuf;
   }
 
@@ -213,9 +216,9 @@
    * @return the size of the JPEG image (in bytes) associated with this
    * decompressor instance.
    */
-  public int getJPEGSize() throws Exception {
+  public int getJPEGSize() {
     if (jpegBufSize < 1)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     return jpegBufSize;
   }
 
@@ -238,12 +241,11 @@
    * decompressor can generate without exceeding the desired image width and
    * height.
    */
-  public int getScaledWidth(int desiredWidth, int desiredHeight)
-                            throws Exception {
+  public int getScaledWidth(int desiredWidth, int desiredHeight) {
     if (jpegWidth < 1 || jpegHeight < 1)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (desiredWidth < 0 || desiredHeight < 0)
-      throw new Exception("Invalid argument in getScaledWidth()");
+      throw new IllegalArgumentException("Invalid argument in getScaledWidth()");
     TJScalingFactor[] sf = TJ.getScalingFactors();
     if (desiredWidth == 0)
       desiredWidth = jpegWidth;
@@ -257,7 +259,7 @@
         break;
     }
     if (scaledWidth > desiredWidth || scaledHeight > desiredHeight)
-      throw new Exception("Could not scale down to desired image dimensions");
+      throw new IllegalArgumentException("Could not scale down to desired image dimensions");
     return scaledWidth;
   }
 
@@ -280,12 +282,11 @@
    * decompressor can generate without exceeding the desired image width and
    * height.
    */
-  public int getScaledHeight(int desiredWidth, int desiredHeight)
-                             throws Exception {
+  public int getScaledHeight(int desiredWidth, int desiredHeight) {
     if (jpegWidth < 1 || jpegHeight < 1)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (desiredWidth < 0 || desiredHeight < 0)
-      throw new Exception("Invalid argument in getScaledHeight()");
+      throw new IllegalArgumentException("Invalid argument in getScaledHeight()");
     TJScalingFactor[] sf = TJ.getScalingFactors();
     if (desiredWidth == 0)
       desiredWidth = jpegWidth;
@@ -299,7 +300,7 @@
         break;
     }
     if (scaledWidth > desiredWidth || scaledHeight > desiredHeight)
-      throw new Exception("Could not scale down to desired image dimensions");
+      throw new IllegalArgumentException("Could not scale down to desired image dimensions");
     return scaledHeight;
   }
 
@@ -369,13 +370,13 @@
    */
   public void decompress(byte[] dstBuf, int x, int y, int desiredWidth,
                          int pitch, int desiredHeight, int pixelFormat,
-                         int flags) throws Exception {
+                         int flags) throws TJException {
     if (jpegBuf == null && yuvImage == null)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (dstBuf == null || x < 0 || y < 0 || pitch < 0 ||
         (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) ||
         pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
-      throw new Exception("Invalid argument in decompress()");
+      throw new IllegalArgumentException("Invalid argument in decompress()");
     if (yuvImage != null)
       decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
                 yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y,
@@ -398,7 +399,7 @@
   @Deprecated
   public void decompress(byte[] dstBuf, int desiredWidth, int pitch,
                          int desiredHeight, int pixelFormat, int flags)
-                         throws Exception {
+                         throws TJException {
     decompress(dstBuf, 0, 0, desiredWidth, pitch, desiredHeight, pixelFormat,
                flags);
   }
@@ -428,11 +429,11 @@
    * @return a buffer containing the decompressed image.
    */
   public byte[] decompress(int desiredWidth, int pitch, int desiredHeight,
-                           int pixelFormat, int flags) throws Exception {
+                           int pixelFormat, int flags) throws TJException {
     if (pitch < 0 ||
         (yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) ||
         pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
-      throw new Exception("Invalid argument in decompress()");
+      throw new IllegalArgumentException("Invalid argument in decompress()");
     int pixelSize = TJ.getPixelSize(pixelFormat);
     int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
     int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
@@ -461,20 +462,21 @@
    * @param flags the bitwise OR of one or more of
    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
    */
-  public void decompressToYUV(YUVImage dstImage, int flags) throws Exception {
+  public void decompressToYUV(YUVImage dstImage, int flags)
+                              throws TJException {
     if (jpegBuf == null)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (dstImage == null || flags < 0)
-      throw new Exception("Invalid argument in decompressToYUV()");
+      throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
     int scaledWidth = getScaledWidth(dstImage.getWidth(),
                                      dstImage.getHeight());
     int scaledHeight = getScaledHeight(dstImage.getWidth(),
                                        dstImage.getHeight());
     if (scaledWidth != dstImage.getWidth() ||
         scaledHeight != dstImage.getHeight())
-      throw new Exception("YUVImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating.");
+      throw new IllegalArgumentException("YUVImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating.");
     if (jpegSubsamp != dstImage.getSubsamp())
-      throw new Exception("YUVImage subsampling level does not match that of the JPEG image");
+      throw new IllegalArgumentException("YUVImage subsampling level does not match that of the JPEG image");
 
     decompressToYUV(jpegBuf, jpegBufSize, dstImage.getPlanes(),
                     dstImage.getOffsets(), dstImage.getWidth(),
@@ -485,7 +487,7 @@
    * @deprecated Use {@link #decompressToYUV(YUVImage, int)} instead.
    */
   @Deprecated
-  public void decompressToYUV(byte[] dstBuf, int flags) throws Exception {
+  public void decompressToYUV(byte[] dstBuf, int flags) throws TJException {
     YUVImage dstImage = new YUVImage(dstBuf, jpegWidth, 4, jpegHeight,
                                      jpegSubsamp);
     decompressToYUV(dstImage, flags);
@@ -531,15 +533,15 @@
    */
   public YUVImage decompressToYUV(int desiredWidth, int[] strides,
                                   int desiredHeight,
-                                  int flags) throws Exception {
+                                  int flags) throws TJException {
     if (flags < 0)
-      throw new Exception("Invalid argument in decompressToYUV()");
+      throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
     if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (jpegSubsamp >= TJ.NUMSAMP)
-      throw new Exception("JPEG header information is invalid");
+      throw new IllegalStateException("JPEG header information is invalid");
     if (yuvImage != null)
-      throw new Exception("Source image is the wrong type");
+      throw new IllegalStateException("Source image is the wrong type");
 
     int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
     int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
@@ -584,15 +586,15 @@
    * @return a YUV planar image.
    */
   public YUVImage decompressToYUV(int desiredWidth, int pad, int desiredHeight,
-                                  int flags) throws Exception {
+                                  int flags) throws TJException {
     if (flags < 0)
-      throw new Exception("Invalid argument in decompressToYUV()");
+      throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
     if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (jpegSubsamp >= TJ.NUMSAMP)
-      throw new Exception("JPEG header information is invalid");
+      throw new IllegalStateException("JPEG header information is invalid");
     if (yuvImage != null)
-      throw new Exception("Source image is the wrong type");
+      throw new IllegalStateException("Source image is the wrong type");
 
     int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
     int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
@@ -606,7 +608,7 @@
    * @deprecated Use {@link #decompressToYUV(int, int, int, int)} instead.
    */
   @Deprecated
-  public byte[] decompressToYUV(int flags) throws Exception {
+  public byte[] decompressToYUV(int flags) throws TJException {
     YUVImage dstImage = new YUVImage(jpegWidth, 4, jpegHeight, jpegSubsamp);
     decompressToYUV(dstImage, flags);
     return dstImage.getBuf();
@@ -676,13 +678,13 @@
    */
   public void decompress(int[] dstBuf, int x, int y, int desiredWidth,
                          int stride, int desiredHeight, int pixelFormat,
-                         int flags) throws Exception {
+                         int flags) throws TJException {
     if (jpegBuf == null && yuvImage == null)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (dstBuf == null || x < 0 || y < 0 || stride < 0 ||
         (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) ||
         pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
-      throw new Exception("Invalid argument in decompress()");
+      throw new IllegalArgumentException("Invalid argument in decompress()");
     if (yuvImage != null)
       decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
                 yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y,
@@ -709,9 +711,10 @@
    * @param flags the bitwise OR of one or more of
    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
    */
-  public void decompress(BufferedImage dstImage, int flags) throws Exception {
+  public void decompress(BufferedImage dstImage, int flags)
+                         throws TJException {
     if (dstImage == null || flags < 0)
-      throw new Exception("Invalid argument in decompress()");
+      throw new IllegalArgumentException("Invalid argument in decompress()");
     int desiredWidth = dstImage.getWidth();
     int desiredHeight = dstImage.getHeight();
     int scaledWidth, scaledHeight;
@@ -719,14 +722,14 @@
     if (yuvImage != null) {
       if (desiredWidth != yuvImage.getWidth() ||
           desiredHeight != yuvImage.getHeight())
-        throw new Exception("BufferedImage dimensions do not match the dimensions of the source image.");
+        throw new IllegalArgumentException("BufferedImage dimensions do not match the dimensions of the source image.");
       scaledWidth = yuvImage.getWidth();
       scaledHeight = yuvImage.getHeight();
     } else {
       scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
       scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
       if (scaledWidth != desiredWidth || scaledHeight != desiredHeight)
-        throw new Exception("BufferedImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating.");
+        throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating.");
     }
     int pixelFormat;  boolean intPixels = false;
     if (byteOrder == null)
@@ -759,7 +762,7 @@
           pixelFormat = TJ.PF_BGRA;
         intPixels = true;  break;
       default:
-        throw new Exception("Unsupported BufferedImage format");
+        throw new IllegalArgumentException("Unsupported BufferedImage format");
     }
     WritableRaster wr = dstImage.getRaster();
     if (intPixels) {
@@ -775,7 +778,7 @@
                   pixelFormat, flags);
       else {
         if (jpegBuf == null)
-          throw new Exception(NO_ASSOC_ERROR);
+          throw new IllegalStateException(NO_ASSOC_ERROR);
         decompress(jpegBuf, jpegBufSize, buf, 0, 0, scaledWidth, stride,
                    scaledHeight, pixelFormat, flags);
       }
@@ -784,7 +787,7 @@
         (ComponentSampleModel)dstImage.getSampleModel();
       int pixelSize = sm.getPixelStride();
       if (pixelSize != TJ.getPixelSize(pixelFormat))
-        throw new Exception("Inconsistency between pixel format and pixel size in BufferedImage");
+        throw new IllegalArgumentException("Inconsistency between pixel format and pixel size in BufferedImage");
       int pitch = sm.getScanlineStride();
       DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
       byte[] buf = db.getData();
@@ -818,10 +821,10 @@
    */
   public BufferedImage decompress(int desiredWidth, int desiredHeight,
                                   int bufferedImageType, int flags)
-                                  throws Exception {
+                                  throws TJException {
     if ((yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) ||
         flags < 0)
-      throw new Exception("Invalid argument in decompress()");
+      throw new IllegalArgumentException("Invalid argument in decompress()");
     int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
     int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
     BufferedImage img = new BufferedImage(scaledWidth, scaledHeight,
@@ -833,57 +836,62 @@
   /**
    * Free the native structures associated with this decompressor instance.
    */
-  public void close() throws Exception {
+  @Override
+  public void close() throws TJException {
     if (handle != 0)
       destroy();
   }
 
+  @Override
   protected void finalize() throws Throwable {
     try {
       close();
-    } catch(Exception e) {
+    } catch(TJException e) {
     } finally {
       super.finalize();
     }
   };
 
-  private native void init() throws Exception;
+  private native void init() throws TJException;
 
-  private native void destroy() throws Exception;
+  private native void destroy() throws TJException;
 
   private native void decompressHeader(byte[] srcBuf, int size)
-    throws Exception;
+    throws TJException;
 
+  @Deprecated
   private native void decompress(byte[] srcBuf, int size, byte[] dstBuf,
     int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)
-    throws Exception; // deprecated
+    throws TJException;
 
   private native void decompress(byte[] srcBuf, int size, byte[] dstBuf, int x,
     int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat,
-    int flags) throws Exception;
+    int flags) throws TJException;
 
+  @Deprecated
   private native void decompress(byte[] srcBuf, int size, int[] dstBuf,
     int desiredWidth, int stride, int desiredHeight, int pixelFormat,
-    int flags) throws Exception; // deprecated
+    int flags) throws TJException;
 
   private native void decompress(byte[] srcBuf, int size, int[] dstBuf, int x,
     int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat,
-    int flags) throws Exception;
+    int flags) throws TJException;
 
+  @Deprecated
   private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf,
-    int flags) throws Exception; // deprecated
+    int flags) throws TJException;
 
   private native void decompressToYUV(byte[] srcBuf, int size,
     byte[][] dstPlanes, int[] dstOffsets, int desiredWidth, int[] dstStrides,
-    int desiredheight, int flags) throws Exception;
+    int desiredheight, int flags) throws TJException;
 
   private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets,
     int[] srcStrides, int subsamp, byte[] dstBuf, int x, int y, int width,
-    int pitch, int height, int pixelFormat, int flags) throws Exception;
+    int pitch, int height, int pixelFormat, int flags) throws TJException;
 
   private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets,
     int[] srcStrides, int subsamp, int[] dstBuf, int x, int y, int width,
-    int stride, int height, int pixelFormat, int flags) throws Exception;
+    int stride, int height, int pixelFormat, int flags) throws TJException;
 
   static {
     TJLoader.load();
@@ -898,4 +906,4 @@
   protected int jpegSubsamp = -1;
   protected int jpegColorspace = -1;
   private ByteOrder byteOrder = null;
-};
+}
diff --git a/java/org/libjpegturbo/turbojpeg/TJException.java b/java/org/libjpegturbo/turbojpeg/TJException.java
new file mode 100644
index 0000000..59c2041
--- /dev/null
+++ b/java/org/libjpegturbo/turbojpeg/TJException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C)2015 Viktor Szathmáry.  All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the libjpeg-turbo Project nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.libjpegturbo.turbojpeg;
+
+import java.io.IOException;
+
+public class TJException extends IOException {
+
+  private static final long serialVersionUID = 1L;
+
+  public TJException() {
+    super();
+  }
+
+  public TJException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public TJException(String message) {
+    super(message);
+  }
+
+  public TJException(Throwable cause) {
+    super(cause);
+  }
+
+}
diff --git a/java/org/libjpegturbo/turbojpeg/TJLoader.java.in b/java/org/libjpegturbo/turbojpeg/TJLoader.java.in
index 22353a5..8397780 100644
--- a/java/org/libjpegturbo/turbojpeg/TJLoader.java.in
+++ b/java/org/libjpegturbo/turbojpeg/TJLoader.java.in
@@ -32,4 +32,4 @@
   static void load() {
     System.loadLibrary("@TURBOJPEG_DLL_NAME@");
   }
-};
+}
diff --git a/java/org/libjpegturbo/turbojpeg/TJLoader.java.tmpl b/java/org/libjpegturbo/turbojpeg/TJLoader.java.tmpl
index a4f1c87..5ef3118 100644
--- a/java/org/libjpegturbo/turbojpeg/TJLoader.java.tmpl
+++ b/java/org/libjpegturbo/turbojpeg/TJLoader.java.tmpl
@@ -56,4 +56,4 @@
       }
     }
   }
-};
+}
diff --git a/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java b/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java
index e00fdf7..ddb1d75 100644
--- a/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java
+++ b/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C)2011 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2015 Viktor Szathmáry.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -33,9 +34,9 @@
  */
 public class TJScalingFactor {
 
-  public TJScalingFactor(int num, int denom) throws Exception {
+  public TJScalingFactor(int num, int denom) {
     if (num < 1 || denom < 1)
-      throw new Exception("Numerator and denominator must be >= 1");
+      throw new IllegalArgumentException("Numerator and denominator must be >= 1");
     this.num = num;
     this.denom = denom;
   }
@@ -77,7 +78,7 @@
    * <code>other</code> have the same numerator and denominator.
    */
   public boolean equals(TJScalingFactor other) {
-    return (this.num == other.num && this.denom == other.denom);
+    return this.num == other.num && this.denom == other.denom;
   }
 
   /**
@@ -88,7 +89,7 @@
    * 1/1.
    */
   public boolean isOne() {
-    return (num == 1 && denom == 1);
+    return num == 1 && denom == 1;
   }
 
   /**
@@ -100,4 +101,4 @@
    * Denominator
    */
   private int denom = 1;
-};
+}
diff --git a/java/org/libjpegturbo/turbojpeg/TJTransform.java b/java/org/libjpegturbo/turbojpeg/TJTransform.java
index b464ffd..7381f36 100644
--- a/java/org/libjpegturbo/turbojpeg/TJTransform.java
+++ b/java/org/libjpegturbo/turbojpeg/TJTransform.java
@@ -160,7 +160,7 @@
    * TJCustomFilter} interface, or null if no custom filter is needed
    */
   public TJTransform(int x, int y, int w, int h, int op, int options,
-                     TJCustomFilter cf) throws Exception {
+                     TJCustomFilter cf) {
     super(x, y, w, h);
     this.op = op;
     this.options = options;
@@ -184,7 +184,7 @@
    * TJCustomFilter} interface, or null if no custom filter is needed
    */
   public TJTransform(Rectangle r, int op, int options,
-                     TJCustomFilter cf) throws Exception {
+                     TJCustomFilter cf) {
     super(r);
     this.op = op;
     this.options = options;
diff --git a/java/org/libjpegturbo/turbojpeg/TJTransformer.java b/java/org/libjpegturbo/turbojpeg/TJTransformer.java
index 2e17344..d76647f 100644
--- a/java/org/libjpegturbo/turbojpeg/TJTransformer.java
+++ b/java/org/libjpegturbo/turbojpeg/TJTransformer.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C)2011, 2013-2015 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2015 Viktor Szathmáry.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -36,7 +37,7 @@
   /**
    * Create a TurboJPEG lossless transformer instance.
    */
-  public TJTransformer() throws Exception {
+  public TJTransformer() throws TJException {
     init();
   }
 
@@ -47,7 +48,7 @@
    * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to
    * be the length of the array.)  This buffer is not modified.
    */
-  public TJTransformer(byte[] jpegImage) throws Exception {
+  public TJTransformer(byte[] jpegImage) throws TJException {
     init();
     setSourceImage(jpegImage, jpegImage.length);
   }
@@ -61,7 +62,7 @@
    *
    * @param imageSize size of the JPEG image (in bytes)
    */
-  public TJTransformer(byte[] jpegImage, int imageSize) throws Exception {
+  public TJTransformer(byte[] jpegImage, int imageSize) throws TJException {
     init();
     setSourceImage(jpegImage, imageSize);
   }
@@ -94,9 +95,9 @@
    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
    */
   public void transform(byte[][] dstBufs, TJTransform[] transforms,
-                        int flags) throws Exception {
+                        int flags) throws TJException {
     if (jpegBuf == null)
-      throw new Exception("JPEG buffer not initialized");
+      throw new IllegalStateException("JPEG buffer not initialized");
     transformedSizes = transform(jpegBuf, jpegBufSize, dstBufs, transforms,
                                  flags);
   }
@@ -117,10 +118,10 @@
    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
    */
   public TJDecompressor[] transform(TJTransform[] transforms, int flags)
-    throws Exception {
+                                    throws TJException {
     byte[][] dstBufs = new byte[transforms.length][];
     if (jpegWidth < 1 || jpegHeight < 1)
-      throw new Exception("JPEG buffer not initialized");
+      throw new IllegalStateException("JPEG buffer not initialized");
     for (int i = 0; i < transforms.length; i++) {
       int w = jpegWidth, h = jpegHeight;
       if ((transforms[i].options & TJTransform.OPT_CROP) != 0) {
@@ -143,20 +144,20 @@
    * @return an array containing the sizes of the transformed JPEG images
    * generated by the most recent transform operation.
    */
-  public int[] getTransformedSizes() throws Exception {
+  public int[] getTransformedSizes() {
     if (transformedSizes == null)
-      throw new Exception("No image has been transformed yet");
+      throw new IllegalStateException("No image has been transformed yet");
     return transformedSizes;
   }
 
-  private native void init() throws Exception;
+  private native void init() throws TJException;
 
   private native int[] transform(byte[] srcBuf, int srcSize, byte[][] dstBufs,
-    TJTransform[] transforms, int flags) throws Exception;
+    TJTransform[] transforms, int flags) throws TJException;
 
   static {
     TJLoader.load();
   }
 
   private int[] transformedSizes = null;
-};
+}
diff --git a/java/org/libjpegturbo/turbojpeg/YUVImage.java b/java/org/libjpegturbo/turbojpeg/YUVImage.java
index 2d790e9..1a05e62 100644
--- a/java/org/libjpegturbo/turbojpeg/YUVImage.java
+++ b/java/org/libjpegturbo/turbojpeg/YUVImage.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (C)2014 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2015 Viktor Szathmáry.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -86,8 +87,7 @@
    * @param subsamp the level of chrominance subsampling to be used in the YUV
    * image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
    */
-  public YUVImage(int width, int[] strides, int height, int subsamp)
-    throws Exception {
+  public YUVImage(int width, int[] strides, int height, int subsamp) {
     setBuf(null, null, width, strides, height, subsamp, true);
   }
 
@@ -105,8 +105,7 @@
    * @param subsamp the level of chrominance subsampling to be used in the YUV
    * image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
    */
-  public YUVImage(int width, int pad, int height, int subsamp)
-    throws Exception {
+  public YUVImage(int width, int pad, int height, int subsamp) {
     setBuf(new byte[TJ.bufSizeYUV(width, pad, height, subsamp)], width, pad,
            height, subsamp);
   }
@@ -146,7 +145,7 @@
    * image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
    */
   public YUVImage(byte[][] planes, int[] offsets, int width, int[] strides,
-                  int height, int subsamp) throws Exception {
+                  int height, int subsamp) {
     setBuf(planes, offsets, width, strides, height, subsamp, false);
   }
 
@@ -172,7 +171,7 @@
    * image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
    */
   public YUVImage(byte[] yuvImage, int width, int pad, int height,
-                  int subsamp) throws Exception {
+                  int subsamp) {
     setBuf(yuvImage, width, pad, height, subsamp);
   }
 
@@ -210,20 +209,20 @@
    * image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
    */
   public void setBuf(byte[][] planes, int[] offsets, int width, int strides[],
-                     int height, int subsamp) throws Exception {
+                     int height, int subsamp) {
     setBuf(planes, offsets, width, strides, height, subsamp, false);
   }
 
   private void setBuf(byte[][] planes, int[] offsets, int width, int strides[],
-                     int height, int subsamp, boolean alloc) throws Exception {
+                     int height, int subsamp, boolean alloc) {
     if ((planes == null && !alloc) || width < 1 || height < 1 || subsamp < 0 ||
         subsamp >= TJ.NUMSAMP)
-      throw new Exception("Invalid argument in YUVImage::setBuf()");
+      throw new IllegalArgumentException("Invalid argument in YUVImage::setBuf()");
 
     int nc = (subsamp == TJ.SAMP_GRAY ? 1 : 3);
     if (planes.length != nc || (offsets != null && offsets.length != nc) ||
         (strides != null && strides.length != nc))
-      throw new Exception("YUVImage::setBuf(): planes, offsets, or strides array is the wrong size");
+      throw new IllegalArgumentException("YUVImage::setBuf(): planes, offsets, or strides array is the wrong size");
 
     if (offsets == null)
       offsets = new int[nc];
@@ -239,15 +238,15 @@
         strides[i] = pw;
       if (alloc) {
         if (strides[i] < pw)
-          throw new Exception("Stride must be >= plane width when allocating a new YUV image");
+          throw new IllegalArgumentException("Stride must be >= plane width when allocating a new YUV image");
         planes[i] = new byte[strides[i] * ph];
       }
       if (planes[i] == null || offsets[i] < 0)
-        throw new Exception("Invalid argument in YUVImage::setBuf()");
+        throw new IllegalArgumentException("Invalid argument in YUVImage::setBuf()");
       if (strides[i] < 0 && offsets[i] - planeSize + pw < 0)
-        throw new Exception("Stride for plane " + i + " would cause memory to be accessed below plane boundary");
+        throw new IllegalArgumentException("Stride for plane " + i + " would cause memory to be accessed below plane boundary");
       if (planes[i].length < offsets[i] + planeSize)
-        throw new Exception("Image plane " + i + " is not large enough");
+        throw new IllegalArgumentException("Image plane " + i + " is not large enough");
     }
 
     yuvPlanes = planes;
@@ -279,13 +278,13 @@
    * image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
    */
   public void setBuf(byte[] yuvImage, int width, int pad, int height,
-                     int subsamp) throws Exception {
+                     int subsamp) {
     if (yuvImage == null || width < 1 || pad < 1 || ((pad & (pad - 1)) != 0) ||
         height < 1 || subsamp < 0 || subsamp >= TJ.NUMSAMP)
-      throw new Exception("Invalid argument in YUVImage::setBuf()");
+      throw new IllegalArgumentException("Invalid argument in YUVImage::setBuf()");
     if (yuvImage.length < TJ.bufSizeYUV(width, pad, height, subsamp))
-      throw new Exception("YUV image buffer is not large enough");
-    
+      throw new IllegalArgumentException("YUV image buffer is not large enough");
+
     int nc = (subsamp == TJ.SAMP_GRAY ? 1 : 3);
     byte[][] planes = new byte[nc][];
     int[] strides = new int[nc];
@@ -311,9 +310,9 @@
    *
    * @return the width of the YUV image (or subregion)
    */
-  public int getWidth() throws Exception {
+  public int getWidth() {
     if (yuvWidth < 1)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     return yuvWidth;
   }
 
@@ -322,9 +321,9 @@
    *
    * @return the height of the YUV image (or subregion)
    */
-  public int getHeight() throws Exception {
+  public int getHeight() {
     if (yuvHeight < 1)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     return yuvHeight;
   }
 
@@ -334,11 +333,11 @@
    *
    * @return the line padding used in the YUV image buffer
    */
-  public int getPad() throws Exception {
+  public int getPad() {
     if (yuvPlanes == null)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     if (yuvPad < 1 || ((yuvPad & (yuvPad - 1)) != 0))
-      throw new Exception("Image is not stored in a unified buffer");
+      throw new IllegalStateException("Image is not stored in a unified buffer");
     return yuvPad;
   }
 
@@ -347,9 +346,9 @@
    *
    * @return the number of bytes per line of each plane in the YUV image
    */
-  public int[] getStrides() throws Exception {
+  public int[] getStrides() {
     if (yuvStrides == null)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     return yuvStrides;
   }
 
@@ -360,9 +359,9 @@
    * @return the offsets (in bytes) of each plane within the planes of a larger
    * YUV image
    */
-  public int[] getOffsets() throws Exception {
+  public int[] getOffsets() {
     if (yuvOffsets == null)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     return yuvOffsets;
   }
 
@@ -372,9 +371,9 @@
    *
    * @return the level of chrominance subsampling used in the YUV image
    */
-  public int getSubsamp() throws Exception {
+  public int getSubsamp() {
     if (yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     return yuvSubsamp;
   }
 
@@ -384,9 +383,9 @@
    *
    * @return the YUV image planes
    */
-  public byte[][] getPlanes() throws Exception {
+  public byte[][] getPlanes() {
     if (yuvPlanes == null)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     return yuvPlanes;
   }
 
@@ -396,13 +395,13 @@
    *
    * @return the YUV image buffer
    */
-  public byte[] getBuf() throws Exception {
+  public byte[] getBuf() {
     if (yuvPlanes == null || yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     int nc = (yuvSubsamp == TJ.SAMP_GRAY ? 1 : 3);
     for (int i = 1; i < nc; i++) {
       if (yuvPlanes[i] != yuvPlanes[0])
-        throw new Exception("Image is not stored in a unified buffer");
+        throw new IllegalStateException("Image is not stored in a unified buffer");
     }
     return yuvPlanes[0];
   }
@@ -413,15 +412,15 @@
    *
    * @return the size (in bytes) of the YUV image buffer
    */
-  public int getSize() throws Exception {
+  public int getSize() {
     if (yuvPlanes == null || yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP)
-      throw new Exception(NO_ASSOC_ERROR);
+      throw new IllegalStateException(NO_ASSOC_ERROR);
     int nc = (yuvSubsamp == TJ.SAMP_GRAY ? 1 : 3);
     if (yuvPad < 1)
-      throw new Exception("Image is not stored in a unified buffer");
+      throw new IllegalStateException("Image is not stored in a unified buffer");
     for (int i = 1; i < nc; i++) {
       if (yuvPlanes[i] != yuvPlanes[0])
-        throw new Exception("Image is not stored in a unified buffer");
+        throw new IllegalStateException("Image is not stored in a unified buffer");
     }
     return TJ.bufSizeYUV(yuvWidth, yuvPad, yuvHeight, yuvSubsamp);
   }
@@ -438,4 +437,4 @@
   protected int yuvWidth = 0;
   protected int yuvHeight = 0;
   protected int yuvSubsamp = -1;
-};
+}