Upgrade libjpeg-turbo to 2.0.3

Exempt-From-Owner-Approval: Upgrade library
Test: None
Change-Id: I896805e8a6c5c1ead4ad8f07397236870c3ddb49
diff --git a/.gitattributes b/.gitattributes
index 4ad67d3..2948038 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2,3 +2,4 @@
 /appveyor.yml export-ignore
 /ci export-ignore
 /.gitattributes export-ignore
+/.github export-ignore
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 0000000..d554dd0
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,95 @@
+Contributing to libjpeg-turbo
+=============================
+
+libjpeg-turbo is a stable and mature product with a worldwide reach, it is an
+ISO/ITU-T reference implementation of the JPEG standard, and its maintainer
+does not earn a salary for maintaining it.  Thus, not every code contribution
+can or will be accepted into the libjpeg-turbo code base.  In order to maximize
+the chances that a code contribution is acceptable, please adhere to the
+following guidelines:
+
+1. If the code contribution is a bug fix, then please ensure that enough
+information is provided so that the maintainer can readily reproduce and
+document the bug.  That information should include:
+    - A clear and concise description of the bug
+    - The steps and (if applicable) images necessary to reproduce the bug
+    - The compilers, operating systems, and CPUs with which the bug was
+      observed
+    - The versions of libjpeg-turbo with which the bug was observed
+    - If the bug is a regression, the specific commit that introduced the bug
+      (use `git bisect` to determine this)
+
+2. If the code contribution will implement a major new feature, then please
+contact the project maintainer (through a
+[GitHub issue](https://github.com/libjpeg-turbo/libjpeg-turbo/issues/new),
+[direct e-mail](https://libjpeg-turbo.org/About/Contact), or the
+[libjpeg-turbo-devel mailing list](https://libjpeg-turbo.org/About/MailingLists))
+prior to implementing the feature.  In general, major new features that are
+potentially disruptive to the quality of libjpeg-turbo are unlikely to be
+accepted unless:
+    - The new feature is within the existing scope of libjpeg-turbo.
+    - The new feature has been thoroughly regression tested and benchmarked on
+      all of the supported platforms that are potentially affected by it.
+    - The new feature has been documented clearly and concisely in the change
+      log and (if applicable) the libjpeg and TurboJPEG API documentation and
+      man pages.
+    - The code implementing the new feature is formatted consistently with the
+      rest of the libjpeg-turbo code base (use
+      [checkstyle](https://github.com/libjpeg-turbo/checkstyle) to validate
+      this.)
+    - The new feature does not introduce new members into the exposed libjpeg
+      API structures (doing so would break backward ABI compatibility.)
+    - The new feature does not alter existing libjpeg-turbo usage or
+      development workflows.
+    - The code implementing the new feature is elegant, easily maintainable,
+      and causes the least possible amount of disruption to the libjpeg-turbo
+      code base.
+    - The new feature is based on the dev branch of the libjpeg-turbo
+      repository.
+
+    ... OR ...
+
+    - Your organization is prepared to pay for the labor necessary to ensure
+      all of the above.  Even the most well-written patches can still require
+      a significant amount of labor to clean up, test, and integrate.  (See
+      above RE: the maintainer not earning a salary.)
+
+    Specific types of features that *will not* be accepted:
+
+    - Features that extend the scope of libjpeg-turbo to support image formats
+      other than DCT-based JPEG and JFIF
+    - Features that extend the scope of libjpeg-turbo to support image
+      processing algorithms that are not necessary for JPEG compression or
+      decompression
+    - Extensions to the JPEG format that have not been approved by the
+      appropriate standards bodies
+    - Non-trivial performance enhancements that have less than a 5% overall
+      impact on performance
+
+3. If the code contribution is a build system enhancement, then please be
+prepared to justify the enhancement.  In general, build system enhancements
+are unlikely to be accepted unless:
+    - The enhancement is potentially beneficial to a significant number of
+      upstream libjpeg-turbo users/developers.  (If the enhancement is only
+      beneficial to a downstream project, then it does not belong here.)
+    - The enhancement has been tested with the following CMake versions:
+        - The earliest version of CMake that libjpeg-turbo currently supports
+          (refer to CMakeLists.txt)
+        - The most recent major version of CMake
+        - (if applicable) The earliest version of CMake with which the
+          enhancement can be used
+    - The enhancement has been tested on all of the major platforms (Mac,
+      Linux, Windows/Visual C++, Windows/MinGW) that are potentially affected
+      by it.
+    - The enhancement does not introduce new build system requirements or CMake
+      variables unless absolutely necessary.
+    - The enhancement does not alter existing libjpeg-turbo development
+      workflows.
+
+    Specific types of build system enhancements that *will not* be accepted:
+
+    - Enhancements that allow libjpeg-turbo to be built from a subdirectory
+      of a downstream repository.  These enhancements are not maintainable in
+      the upstream libjpeg-turbo build system.  Use the CMake
+      `ExternalProject_Add()` function instead.
+    - Enhancements that introduce new (non-CMake-based) build systems
diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md
new file mode 100644
index 0000000..a2a5feb
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.md
@@ -0,0 +1,40 @@
+---
+name: Bug Report
+about: Inform the libjpeg-turbo maintainer about unexpected, reproducible behavior
+title: ''
+labels: bug
+assignees: dcommander
+
+---
+
+**Have you searched the existing issues (both open and closed) in the libjpeg-turbo issue tracker to ensure that this bug report is not a duplicate?**
+
+
+**Does this bug report describe one of the [two known and unsolvable issues with the JPEG format](https://libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf)?**
+
+
+**Clear and concise description of the bug:**
+
+
+**Steps to reproduce the bug (using *only* libjpeg-turbo):**
+
+
+**Image(s) needed in order to reproduce the bug (if applicable):**
+
+
+**Expected behavior:**
+
+
+**Observed behavior:**
+
+
+**Platform(s) (compiler version, operating system version, CPU) on which the bug was observed:**
+
+
+**libjpeg-turbo release(s), commit(s), or branch(es) in which the bug was observed (always test the tip of the master branch or the latest [stable pre-release](https://libjpeg-turbo.org/DeveloperInfo/PreReleases) to verify that the bug hasn't already been fixed):**
+
+
+**If the bug is a regression, the specific commit that introduced the regression (use `git bisect` to determine this):**
+
+
+**Additional information:**
diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md
new file mode 100644
index 0000000..eac443a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-request.md
@@ -0,0 +1,8 @@
+---
+name: Feature Request
+about: Suggest new libjpeg-turbo functionality
+title: ''
+labels: enhancement
+assignees: dcommander
+
+---
diff --git a/.travis.yml b/.travis.yml
index 2538954..35a7907 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,52 +14,64 @@
         - docker
     - os: osx
       env: BUILD_OFFICIAL=1
-      osx_image: xcode7.3
+      osx_image: xcode8.3
     - os: linux
-      dist: trusty
       compiler: clang
       env:
         CMAKE_BUILD_TYPE=RelWithDebInfo
-        CFLAGS_RELWITHDEBINFO="-O1 -g -fsanitize=address -fno-omit-frame-pointer"
+        CFLAGS_RELWITHDEBINFO="-O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer"
         CMAKE_FLAGS="-DENABLE_SHARED=0"
         ASAN_OPTIONS="detect_leaks=1 symbolize=1"
+        CTEST_OUTPUT_ON_FAILURE=1
       addons:
         apt:
           packages:
             - nasm
     - os: linux
-      dist: trusty
       compiler: gcc
-      env: CMAKE_FLAGS="-DWITH_12BIT=1"
+      env:
+        CMAKE_FLAGS="-DWITH_12BIT=1"
+        CTEST_OUTPUT_ON_FAILURE=1
     - os: linux
-      dist: trusty
       compiler: gcc
-      env: CMAKE_FLAGS="-DWITH_JPEG7=1"
+      env:
+        CMAKE_FLAGS="-DWITH_JPEG7=1"
+        CTEST_OUTPUT_ON_FAILURE=1
       addons:
         apt:
           packages:
             - nasm
     - os: linux
-      dist: trusty
       compiler: gcc
-      env: CMAKE_FLAGS="-DWITH_JPEG8=1"
+      env:
+        CMAKE_FLAGS="-DWITH_JPEG8=1"
+        CTEST_OUTPUT_ON_FAILURE=1
       addons:
         apt:
           packages:
             - nasm
     - os: linux
-      dist: trusty
-      compiler: gcc
-      env: CMAKE_FLAGS="-DWITH_SIMD=0"
+      compiler: clang
+      env:
+        CMAKE_BUILD_TYPE=RelWithDebInfo
+        CFLAGS_RELWITHDEBINFO="-O3 -g -fsanitize=memory -fPIE"
+        CMAKE_FLAGS="-DWITH_SIMD=0"
+        CTEST_OUTPUT_ON_FAILURE=1
 
 addons:
   homebrew:
     brewfile: true
     update: true
 
+cache:
+  directories:
+    - $HOME/Library/Caches/Homebrew
+
+before_cache:
+  - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then brew cleanup; fi
+
 before_install:
   - if [ "$TRAVIS_OS_NAME" = "osx" ]; then
-      ln -fs /usr/local/bin/gpg1 /usr/local/bin/gpg &&
       git clone --depth=1 https://github.com/libjpeg-turbo/gas-preprocessor.git ~/src/gas-preprocessor &&
       ln -fs /Applications/Xcode.app /Applications/Xcode72.app;
     fi
@@ -73,7 +85,7 @@
         tar xf ci/keys &&
         rm ci/keys &&
         mv ci/gpgsign ~/src/buildscripts &&
-        gpg --import ci/sign_ljt &&
+        gpg --batch --import ci/sign_ljt &&
         rm ci/sign_ljt;
       fi
     fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2bc3458..28fd443 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,7 +5,7 @@
 endif()
 
 project(libjpeg-turbo C)
-set(VERSION 2.0.2)
+set(VERSION 2.0.3)
 string(REPLACE "." ";" VERSION_TRIPLET ${VERSION})
 list(GET VERSION_TRIPLET 0 VERSION_MAJOR)
 list(GET VERSION_TRIPLET 1 VERSION_MINOR)
@@ -536,7 +536,7 @@
 endif()
 if(WITH_SIMD)
   message(STATUS "SIMD extensions: ${CPU_TYPE} (WITH_SIMD = ${WITH_SIMD})")
-  if(MSVC_IDE)
+  if(MSVC_IDE OR XCODE)
     set_source_files_properties(${SIMD_OBJS} PROPERTIES GENERATED 1)
   endif()
 else()
@@ -666,7 +666,7 @@
 
 add_subdirectory(md5)
 
-if(MSVC_IDE)
+if(MSVC_IDE OR XCODE)
   set(OBJDIR "\${CTEST_CONFIGURATION_TYPE}/")
 else()
   set(OBJDIR "")
@@ -682,7 +682,7 @@
   set(MD5_JPEG_422_IFAST_OPT 7322e3bd2f127f7de4b40d4480ce60e4)
   set(MD5_PPM_422_IFAST 79807fa552899e66a04708f533e16950)
   set(MD5_PPM_422M_IFAST 07737bfe8a7c1c87aaa393a0098d16b0)
-  set(MD5_JPEG_420_IFAST_Q100_PROG a1da220b5604081863a504297ed59e55)
+  set(MD5_JPEG_420_IFAST_Q100_PROG 008ab68d6ddbba04a8f01deee4e0f9f8)
   set(MD5_PPM_420_Q100_IFAST 1b3730122709f53d007255e8dfd3305e)
   set(MD5_PPM_420M_Q100_IFAST 980a1a3c5bf9510022869d30b7d26566)
   set(MD5_JPEG_GRAY_ISLOW 235c90707b16e2e069f37c888b2636d9)
@@ -732,7 +732,7 @@
   set(MD5_PPM_422M_IFAST 8dbc65323d62cca7c91ba02dd1cfa81d)
   set(MD5_BMP_422M_IFAST_565 3294bd4d9a1f2b3d08ea6020d0db7065)
   set(MD5_BMP_422M_IFAST_565D da98c9c7b6039511be4a79a878a9abc1)
-  set(MD5_JPEG_420_IFAST_Q100_PROG 990cbe0329c882420a2094da7e5adade)
+  set(MD5_JPEG_420_IFAST_Q100_PROG e59bb462016a8d9a748c330a3474bb55)
   set(MD5_PPM_420_Q100_IFAST 5a732542015c278ff43635e473a8a294)
   set(MD5_PPM_420M_Q100_IFAST ff692ee9323a3b424894862557c092f1)
   set(MD5_JPEG_GRAY_ISLOW 72b51f894b8f4a10b3ee3066770aa38d)
@@ -1047,7 +1047,7 @@
 
   # CC: RGB->YCC  SAMP: fullsize/h2v2  FDCT: ifast  ENT: prog huff
   add_bittest(cjpeg 420-q100-ifast-prog
-    "-sample;2x2;-quality;100;-dct;fast;-prog"
+    "-sample;2x2;-quality;100;-dct;fast;-scans;${TESTIMAGES}/test.scan"
     testout_420_q100_ifast_prog.jpg ${TESTIMAGES}/testorig.ppm
     ${MD5_JPEG_420_IFAST_Q100_PROG})
 
@@ -1290,6 +1290,8 @@
       COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -yuv -alloc
       COMMAND echo tjbenchtest -progressive
       COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive
+      COMMAND echo tjbenchtest -progressive -yuv
+      COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive -yuv
       COMMAND echo tjexampletest
       COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest
       COMMAND echo tjbenchtest.java
@@ -1298,6 +1300,9 @@
       COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java -yuv
       COMMAND echo tjbenchtest.java -progressive
       COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java -progressive
+      COMMAND echo tjexampletest.java -progressive -yuv
+      COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest.java
+        -progressive -yuv
       COMMAND echo tjexampletest.java
       COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest.java
       DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest
@@ -1313,6 +1318,10 @@
       COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -yuv
       COMMAND echo tjbenchtest -yuv -alloc
       COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -yuv -alloc
+      COMMAND echo tjbenchtest -progressive
+      COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive
+      COMMAND echo tjbenchtest -progressive -yuv
+      COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest -progressive -yuv
       COMMAND echo tjexampletest
       COMMAND ${BASH} ${CMAKE_CURRENT_BINARY_DIR}/tjexampletest
       DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/tjbenchtest)
@@ -1342,7 +1351,7 @@
     install(TARGETS turbojpeg-static ARCHIVE
       DESTINATION ${CMAKE_INSTALL_LIBDIR})
     if(NOT ENABLE_SHARED)
-      if(MSVC_IDE)
+      if(MSVC_IDE OR XCODE)
         set(DIR "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}")
       else()
         set(DIR ${CMAKE_CURRENT_BINARY_DIR})
@@ -1358,7 +1367,7 @@
 if(ENABLE_STATIC)
   install(TARGETS jpeg-static ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
   if(NOT ENABLE_SHARED)
-    if(MSVC_IDE)
+    if(MSVC_IDE OR XCODE)
       set(DIR "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}")
     else()
       set(DIR ${CMAKE_CURRENT_BINARY_DIR})
diff --git a/ChangeLog.md b/ChangeLog.md
index 7cf92c3..3667d12 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,41 @@
+2.0.3
+=====
+
+### Significant changes relative to 2.0.2:
+
+1. Fixed "using JNI after critical get" errors that occurred on Android
+platforms when passing invalid arguments to certain methods in the TurboJPEG
+Java API.
+
+2. Fixed a regression in the SIMD feature detection code, introduced by
+the AVX2 SIMD extensions (2.0 beta1[1]), that was known to cause an illegal
+instruction exception, in rare cases, on CPUs that lack support for CPUID leaf
+07H (or on which the maximum CPUID leaf has been limited by way of a BIOS
+setting.)
+
+3. The 4:4:0 (h1v2) fancy (smooth) chroma upsampling algorithm in the
+decompressor now uses a similar bias pattern to that of the 4:2:2 (h2v1) fancy
+chroma upsampling algorithm, rounding up or down the upsampled result for
+alternate pixels rather than always rounding down.  This ensures that,
+regardless of whether a 4:2:2 JPEG image is rotated or transposed prior to
+decompression (in the frequency domain) or after decompression (in the spatial
+domain), the final image will be similar.
+
+4. Fixed an integer overflow and subsequent segfault that occurred when
+attempting to compress or decompress images with more than 1 billion pixels
+using the TurboJPEG API.
+
+5. Fixed a regression introduced by 2.0 beta1[15] whereby attempting to
+generate a progressive JPEG image on an SSE2-capable CPU using a scan script
+containing one or more scans with lengths divisible by 16 would result in an
+error ("Missing Huffman code table entry") and an invalid JPEG image.
+
+6. Fixed an issue whereby `tjDecodeYUV()` and `tjDecodeYUVPlanes()` would throw
+an error ("Invalid progressive parameters") or a warning ("Inconsistent
+progression sequence") if passed a TurboJPEG instance that was previously used
+to decompress a progressive JPEG image.
+
+
 2.0.2
 =====
 
diff --git a/METADATA b/METADATA
index 8f4252e..63b1193 100644
--- a/METADATA
+++ b/METADATA
@@ -5,10 +5,10 @@
     type: GIT
     value: "https://github.com/libjpeg-turbo/libjpeg-turbo.git"
   }
-  version: "2.0.2"
+  version: "2.0.3"
   last_upgrade_date {
     year: 2019
-    month: 3
-    day: 15
+    month: 9
+    day: 4
   }
 }
diff --git a/README.md b/README.md
index a769259..c61b855 100644
--- a/README.md
+++ b/README.md
@@ -135,12 +135,11 @@
 libjpeg v7+ features, nor to produce identical output to libjpeg v7+ in all
 cases (see below.)
 
-By passing an argument of `--with-jpeg7` or `--with-jpeg8` to `configure`, or
-an argument of `-DWITH_JPEG7=1` or `-DWITH_JPEG8=1` to `cmake`, you can build a
-version of libjpeg-turbo that emulates the libjpeg v7 or v8 ABI, so that
-programs that are built against libjpeg v7 or v8 can be run with libjpeg-turbo.
-The following section describes which libjpeg v7+ features are supported and
-which aren't.
+By passing an argument of `-DWITH_JPEG7=1` or `-DWITH_JPEG8=1` to `cmake`, you
+can build a version of libjpeg-turbo that emulates the libjpeg v7 or v8 ABI, so
+that programs that are built against libjpeg v7 or v8 can be run with
+libjpeg-turbo.  The following section describes which libjpeg v7+ features are
+supported and which aren't.
 
 ### Support for libjpeg v7 and v8 Features
 
@@ -247,9 +246,8 @@
 libjpeg-turbo binaries.
 
 Those who are concerned about maintaining strict conformance with the libjpeg
-v6b or v7 API can pass an argument of `--without-mem-srcdst` to `configure` or
-an argument of `-DWITH_MEM_SRCDST=0` to `cmake` prior to building
-libjpeg-turbo.  This will restore the pre-1.3 behavior, in which
+v6b or v7 API can pass an argument of `-DWITH_MEM_SRCDST=0` to `cmake` prior to
+building libjpeg-turbo.  This will restore the pre-1.3 behavior, in which
 `jpeg_mem_src()` and `jpeg_mem_dest()` are only included when emulating the
 libjpeg v8 API/ABI.
 
@@ -344,3 +342,15 @@
 function in those cases.  This causes performance to drop by as much as 40%.
 It is therefore strongly advised that you use the slow integer forward DCT
 whenever encoding images with a JPEG quality of 98 or higher.
+
+
+Memory Debugger Pitfalls
+========================
+
+Valgrind and Memory Sanitizer (MSan) can generate false positives
+(specifically, incorrect reports of uninitialized memory accesses) when used
+with libjpeg-turbo's SIMD extensions.  It is generally recommended that the
+SIMD extensions be disabled, either by passing an argument of `-DWITH_SIMD=0`
+to `cmake` when configuring the build or by setting the environment variable
+`JSIMD_FORCENONE` to `1` at run time, when testing libjpeg-turbo with Valgrind,
+MSan, or other memory debuggers.
diff --git a/appveyor.yml b/appveyor.yml
index f3bf8b1..cee15e9 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,10 +1,10 @@
 install:
   - cmd: >-
-      mkdir c:\installers
+      if not exist c:\installers mkdir c:\installers
 
       mkdir c:\temp
 
-      curl -fSL -o c:\installers\nasm-2.10.01-win32.zip http://www.nasm.us/pub/nasm/releasebuilds/2.10.01/win32/nasm-2.10.01-win32.zip
+      if not exist c:\installers\nasm-2.10.01-win32.zip curl -fSL -o c:\installers\nasm-2.10.01-win32.zip http://www.nasm.us/pub/nasm/releasebuilds/2.10.01/win32/nasm-2.10.01-win32.zip
 
       7z x c:\installers\nasm-2.10.01-win32.zip -oc:\ > c:\installers\nasm.install.log
 
@@ -22,6 +22,9 @@
 
       git clone --depth=1 https://github.com/libjpeg-turbo/buildscripts.git -b %APPVEYOR_REPO_BRANCH% c:/buildscripts
 
+cache:
+  - c:\installers\nasm-2.10.01-win32.zip -> appveyor.yml
+
 build_script:
   - cmd: >-
       for /f %%i in ('"cygpath %CD%"') do set MINGWPATH=%%i
diff --git a/cmakescripts/BuildPackages.cmake b/cmakescripts/BuildPackages.cmake
index 57f0672..11d5426 100644
--- a/cmakescripts/BuildPackages.cmake
+++ b/cmakescripts/BuildPackages.cmake
@@ -145,6 +145,11 @@
 set(IOS_ARMV8_BUILD ${DEFAULT_IOS_ARMV8_BUILD} CACHE PATH
   "Directory containing ARMv8 iOS build to include in universal binaries (default: ${DEFAULT_IOS_ARMV8_BUILD})")
 
+set(OSX_APP_CERT_NAME "" CACHE STRING
+  "Name of the Developer ID Application certificate (in the macOS keychain) that should be used to sign the libjpeg-turbo DMG.  Leave this blank to generate an unsigned DMG.")
+set(OSX_INST_CERT_NAME "" CACHE STRING
+  "Name of the Developer ID Installer certificate (in the macOS keychain) that should be used to sign the libjpeg-turbo installer package.  Leave this blank to generate an unsigned package.")
+
 configure_file(release/makemacpkg.in pkgscripts/makemacpkg)
 configure_file(release/Distribution.xml.in pkgscripts/Distribution.xml)
 configure_file(release/uninstall.in pkgscripts/uninstall)
diff --git a/java/TJBench.java b/java/TJBench.java
index 7829e53..6fac4d4 100644
--- a/java/TJBench.java
+++ b/java/TJBench.java
@@ -121,6 +121,8 @@
     int rindex = TJ.getRedOffset(pixelFormat);
     int gindex = TJ.getGreenOffset(pixelFormat);
     int bindex = TJ.getBlueOffset(pixelFormat);
+    if ((long)w[0] * (long)h[0] * (long)ps > (long)Integer.MAX_VALUE)
+      throw new Exception("Image is too large");
     byte[] dstBuf = new byte[w[0] * h[0] * ps];
     int pixels = w[0] * h[0], dstPtr = 0, rgbPtr = 0;
 
@@ -175,8 +177,11 @@
 
     tjd = new TJDecompressor();
 
-    if (dstBuf == null)
+    if (dstBuf == null) {
+      if ((long)pitch * (long)scaledh > (long)Integer.MAX_VALUE)
+        throw new Exception("Image is too large");
       dstBuf = new byte[pitch * scaledh];
+    }
 
     /* Set the destination buffer to gray so we know whether the decompressor
        attempted to write to it */
@@ -331,6 +336,8 @@
     String pfStr = PIXFORMATSTR[pf];
     YUVImage yuvImage = null;
 
+    if ((long)pitch * (long)h > (long)Integer.MAX_VALUE)
+      throw new Exception("Image is too large");
     tmpBuf = new byte[pitch * h];
 
     if (quiet == 0)
@@ -491,6 +498,8 @@
     int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp;
 
     FileInputStream fis = new FileInputStream(fileName);
+    if (fis.getChannel().size() > (long)Integer.MAX_VALUE)
+      throw new Exception("Image is too large");
     int srcSize = (int)fis.getChannel().size();
     srcBuf = new byte[srcSize];
     fis.read(srcBuf, 0, srcSize);
@@ -711,7 +720,7 @@
     System.out.println("     bytes to which each row of each plane in the intermediate YUV image is");
     System.out.println("     padded (default = 1)");
     System.out.println("-scale M/N = Scale down the width/height of the decompressed JPEG image by a");
-    System.out.print  ("     factor of M/N (M/N = ");
+    System.out.print("     factor of M/N (M/N = ");
     for (i = 0; i < nsf; i++) {
       System.out.format("%d/%d", scalingFactors[i].getNum(),
                         scalingFactors[i].getDenom());
diff --git a/jchuff.c b/jchuff.c
index 939b3e7..526203e 100644
--- a/jchuff.c
+++ b/jchuff.c
@@ -4,7 +4,7 @@
  * This file was part of the Independent JPEG Group's software:
  * Copyright (C) 1991-1997, Thomas G. Lane.
  * libjpeg-turbo Modifications:
- * Copyright (C) 2009-2011, 2014-2016, 2018, D. R. Commander.
+ * Copyright (C) 2009-2011, 2014-2016, 2018-2019, D. R. Commander.
  * Copyright (C) 2015, Matthieu Darbois.
  * For conditions of distribution and use, see the accompanying README.ijg
  * file.
@@ -356,6 +356,8 @@
   put_buffer = (put_buffer << size) | code; \
 }
 
+#if SIZEOF_SIZE_T != 8 && !defined(_WIN64)
+
 #define CHECKBUF15() { \
   if (put_bits > 15) { \
     EMIT_BYTE() \
@@ -363,6 +365,8 @@
   } \
 }
 
+#endif
+
 #define CHECKBUF31() { \
   if (put_bits > 31) { \
     EMIT_BYTE() \
diff --git a/jcmaster.c b/jcmaster.c
index 93b3de6..998dc40 100644
--- a/jcmaster.c
+++ b/jcmaster.c
@@ -492,8 +492,8 @@
      */
     master->pass_type = output_pass;
     master->pass_number++;
-    /*FALLTHROUGH*/
 #endif
+    /*FALLTHROUGH*/
   case output_pass:
     /* Do a data-output pass. */
     /* We need not repeat per-scan setup if prior optimization pass did it. */
diff --git a/jdatadst-tj.c b/jdatadst-tj.c
index 0bd961b..f6ded64 100644
--- a/jdatadst-tj.c
+++ b/jdatadst-tj.c
@@ -5,7 +5,7 @@
  * Copyright (C) 1994-1996, Thomas G. Lane.
  * Modified 2009-2012 by Guido Vollbeding.
  * libjpeg-turbo Modifications:
- * Copyright (C) 2011, 2014, 2016, D. R. Commander.
+ * Copyright (C) 2011, 2014, 2016, 2019, D. R. Commander.
  * For conditions of distribution and use, see the accompanying README.ijg
  * file.
  *
@@ -27,6 +27,8 @@
 extern void *malloc(size_t size);
 extern void free(void *ptr);
 #endif
+void jpeg_mem_dest_tj(j_compress_ptr cinfo, unsigned char **outbuffer,
+                      unsigned long *outsize, boolean alloc);
 
 
 #define OUTPUT_BUF_SIZE  4096   /* choose an efficiently fwrite'able size */
diff --git a/jdatasrc-tj.c b/jdatasrc-tj.c
index 1c71307..69fb5ea 100644
--- a/jdatasrc-tj.c
+++ b/jdatasrc-tj.c
@@ -5,7 +5,7 @@
  * Copyright (C) 1994-1996, Thomas G. Lane.
  * Modified 2009-2011 by Guido Vollbeding.
  * libjpeg-turbo Modifications:
- * Copyright (C) 2011, 2016, D. R. Commander.
+ * Copyright (C) 2011, 2016, 2019, D. R. Commander.
  * For conditions of distribution and use, see the accompanying README.ijg
  * file.
  *
@@ -23,6 +23,9 @@
 #include "jpeglib.h"
 #include "jerror.h"
 
+void jpeg_mem_src_tj(j_decompress_ptr cinfo, const unsigned char *inbuffer,
+                     unsigned long insize);
+
 
 /*
  * Initialize source --- called by jpeg_read_header
diff --git a/jdhuff.c b/jdhuff.c
index 95f38e5..a112817 100644
--- a/jdhuff.c
+++ b/jdhuff.c
@@ -4,7 +4,7 @@
  * This file was part of the Independent JPEG Group's software:
  * Copyright (C) 1991-1997, Thomas G. Lane.
  * libjpeg-turbo Modifications:
- * Copyright (C) 2009-2011, 2016, 2018, D. R. Commander.
+ * Copyright (C) 2009-2011, 2016, 2018-2019, D. R. Commander.
  * For conditions of distribution and use, see the accompanying README.ijg
  * file.
  *
@@ -589,7 +589,11 @@
     if (entropy->dc_needed[blkn]) {
       /* Convert DC difference to actual value, update last_dc_val */
       int ci = cinfo->MCU_membership[blkn];
-      s += state.last_dc_val[ci];
+      /* This is really just
+       *   s += state.last_dc_val[ci];
+       * It is written this way in order to shut up UBSan.
+       */
+      s = (int)((unsigned int)s + (unsigned int)state.last_dc_val[ci]);
       state.last_dc_val[ci] = s;
       if (block) {
         /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */
@@ -684,7 +688,7 @@
 
     if (entropy->dc_needed[blkn]) {
       int ci = cinfo->MCU_membership[blkn];
-      s += state.last_dc_val[ci];
+      s = (int)((unsigned int)s + (unsigned int)state.last_dc_val[ci]);
       state.last_dc_val[ci] = s;
       if (block)
         (*block)[0] = (JCOEF)s;
diff --git a/jdmerge.c b/jdmerge.c
index b3fec04..dff5a35 100644
--- a/jdmerge.c
+++ b/jdmerge.c
@@ -429,8 +429,6 @@
 #define PACK_TWO_PIXELS_LE(l, r)    ((r << 16) | l)
 #define PACK_TWO_PIXELS_BE(l, r)    ((l << 16) | r)
 
-#define PACK_NEED_ALIGNMENT(ptr)    (((size_t)(ptr)) & 3)
-
 #define WRITE_TWO_PIXELS_LE(addr, pixels) { \
   ((INT16 *)(addr))[0] = (INT16)(pixels); \
   ((INT16 *)(addr))[1] = (INT16)((pixels) >> 16); \
diff --git a/jdsample.c b/jdsample.c
index 52ee9af..50a68b3 100644
--- a/jdsample.c
+++ b/jdsample.c
@@ -8,6 +8,7 @@
  * Copyright (C) 2010, 2015-2016, D. R. Commander.
  * Copyright (C) 2014, MIPS Technologies, Inc., California.
  * Copyright (C) 2015, Google, Inc.
+ * Copyright (C) 2019, Arm Limited.
  * For conditions of distribution and use, see the accompanying README.ijg
  * file.
  *
@@ -315,9 +316,9 @@
   JSAMPARRAY output_data = *output_data_ptr;
   JSAMPROW inptr0, inptr1, outptr;
 #if BITS_IN_JSAMPLE == 8
-  int thiscolsum;
+  int thiscolsum, bias;
 #else
-  JLONG thiscolsum;
+  JLONG thiscolsum, bias;
 #endif
   JDIMENSION colctr;
   int inrow, outrow, v;
@@ -327,15 +328,18 @@
     for (v = 0; v < 2; v++) {
       /* inptr0 points to nearest input row, inptr1 points to next nearest */
       inptr0 = input_data[inrow];
-      if (v == 0)               /* next nearest is row above */
+      if (v == 0) {             /* next nearest is row above */
         inptr1 = input_data[inrow - 1];
-      else                      /* next nearest is row below */
+        bias = 1;
+      } else {                  /* next nearest is row below */
         inptr1 = input_data[inrow + 1];
+        bias = 2;
+      }
       outptr = output_data[outrow++];
 
       for (colctr = 0; colctr < compptr->downsampled_width; colctr++) {
         thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++);
-        *outptr++ = (JSAMPLE)((thiscolsum + 1) >> 2);
+        *outptr++ = (JSAMPLE)((thiscolsum + bias) >> 2);
       }
     }
     inrow++;
diff --git a/md5/md5hl.c b/md5/md5hl.c
index ecd2e23..8a4a762 100644
--- a/md5/md5hl.c
+++ b/md5/md5hl.c
@@ -6,7 +6,7 @@
  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
  * ----------------------------------------------------------------------------
  * libjpeg-turbo Modifications:
- * Copyright (C)2016, 2018 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2016, 2018-2019 D. R. Commander.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -56,7 +56,7 @@
 
 #include "./md5.h"
 
-char *MD5End(MD5_CTX *ctx, char *buf)
+static char *MD5End(MD5_CTX *ctx, char *buf)
 {
   int i;
   unsigned char digest[LENGTH];
@@ -89,7 +89,7 @@
   off_t n;
 
   MD5Init(&ctx);
-#if _WIN32
+#ifdef _WIN32
   f = _open(filename, O_RDONLY | O_BINARY);
 #else
   f = open(filename, O_RDONLY);
@@ -123,12 +123,3 @@
     return 0;
   return (MD5End(&ctx, buf));
 }
-
-char *MD5Data(const void *data, unsigned int len, char *buf)
-{
-  MD5_CTX ctx;
-
-  MD5Init(&ctx);
-  MD5Update(&ctx, (unsigned char *)data, len);
-  return (MD5End(&ctx, buf));
-}
diff --git a/rdjpgcom.c b/rdjpgcom.c
index e9f31d2..620270e 100644
--- a/rdjpgcom.c
+++ b/rdjpgcom.c
@@ -118,7 +118,6 @@
 #define M_SOI    0xD8           /* Start Of Image (beginning of datastream) */
 #define M_EOI    0xD9           /* End Of Image (end of datastream) */
 #define M_SOS    0xDA           /* Start Of Scan (begins compressed data) */
-#define M_APP0   0xE0           /* Application-specific marker, type N */
 #define M_APP12  0xEC           /* (we don't bother to list all 16 APPn's) */
 #define M_COM    0xFE           /* COMment */
 
diff --git a/release/makemacpkg.in b/release/makemacpkg.in
index b0a2e23..42b455d 100644
--- a/release/makemacpkg.in
+++ b/release/makemacpkg.in
@@ -58,6 +58,8 @@
 BUILDDIRARMV7S=@IOS_ARMV7S_BUILD@
 BUILDDIRARMV8=@IOS_ARMV8_BUILD@
 WITH_JAVA=@WITH_JAVA@
+OSX_APP_CERT_NAME="@OSX_APP_CERT_NAME@"
+OSX_INST_CERT_NAME="@OSX_INST_CERT_NAME@"
 LIPO=lipo
 
 PREFIX=@CMAKE_INSTALL_PREFIX@
@@ -258,11 +260,25 @@
 mkdir $TMPDIR/dmg
 pkgbuild --root $PKGROOT --version $VERSION.$BUILD --identifier @PKGID@ \
 	$TMPDIR/pkg/$PKGNAME.pkg
+SUFFIX=
+if [ "$OSX_INST_CERT_NAME" != "" ]; then
+	SUFFIX=-unsigned
+fi
 productbuild --distribution pkgscripts/Distribution.xml \
 	--package-path $TMPDIR/pkg/ --resources $TMPDIR/pkg/ \
-	$TMPDIR/dmg/$PKGNAME.pkg
+	$TMPDIR/dmg/$PKGNAME$SUFFIX.pkg
+if [ "$OSX_INST_CERT_NAME" != "" ]; then
+	productsign --sign "$OSX_INST_CERT_NAME" --timestamp \
+		$TMPDIR/dmg/$PKGNAME$SUFFIX.pkg $TMPDIR/dmg/$PKGNAME.pkg
+	rm -r $TMPDIR/dmg/$PKGNAME$SUFFIX.pkg
+	pkgutil --check-signature $TMPDIR/dmg/$PKGNAME.pkg
+fi
 hdiutil create -fs HFS+ -volname $PKGNAME-$VERSION \
 	-srcfolder "$TMPDIR/dmg" $TMPDIR/$PKGNAME-$VERSION.dmg
+if [ "$OSX_APP_CERT_NAME" != "" ]; then
+	codesign -s "$OSX_APP_CERT_NAME" --timestamp $TMPDIR/$PKGNAME-$VERSION.dmg
+	codesign -vv $TMPDIR/$PKGNAME-$VERSION.dmg
+fi
 cp $TMPDIR/$PKGNAME-$VERSION.dmg .
 
 exit
diff --git a/sharedlib/CMakeLists.txt b/sharedlib/CMakeLists.txt
index a5c4e39..8d65e58 100755
--- a/sharedlib/CMakeLists.txt
+++ b/sharedlib/CMakeLists.txt
@@ -23,7 +23,7 @@
   set(JPEG_SRCS ${JPEG_SRCS} ../${src})
 endforeach()
 
-if(WITH_SIMD AND MSVC_IDE)
+if(WITH_SIMD AND (MSVC_IDE OR XCODE))
   # This tells CMake that the "source" files haven't been generated yet
   set_source_files_properties(${SIMD_OBJS} PROPERTIES GENERATED 1)
 endif()
diff --git a/simd/CMakeLists.txt b/simd/CMakeLists.txt
index 8dbd7f1..3472c0d 100755
--- a/simd/CMakeLists.txt
+++ b/simd/CMakeLists.txt
@@ -135,6 +135,9 @@
 if(MSVC_IDE)
   set(OBJDIR "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}")
   string(REGEX REPLACE " " ";" CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS}")
+elseif(XCODE)
+  set(OBJDIR "${CMAKE_CURRENT_BINARY_DIR}")
+  string(REGEX REPLACE " " ";" CMAKE_ASM_NASM_FLAGS "${CMAKE_ASM_NASM_FLAGS}")
 endif()
 
 file(GLOB INC_FILES nasm/*.inc)
@@ -162,13 +165,13 @@
       ${CMAKE_CURRENT_SOURCE_DIR}/${DEPFILE})
   endif()
   set(OBJECT_DEPENDS ${OBJECT_DEPENDS} ${INC_FILES})
-  if(MSVC_IDE)
+  if(MSVC_IDE OR XCODE)
     # The CMake Visual Studio generators do not work properly with the ASM_NASM
     # language, so we have to go rogue here and use a custom command like we
     # did in prior versions of libjpeg-turbo.  (This is why we can't have nice
     # things.)
     string(REGEX REPLACE "${CPU_TYPE}/" "" filename ${file})
-    set(SIMD_OBJ ${OBJDIR}/${filename}.obj)
+    set(SIMD_OBJ ${OBJDIR}/${filename}${CMAKE_C_OUTPUT_EXTENSION})
     add_custom_command(OUTPUT ${SIMD_OBJ} DEPENDS ${file} ${OBJECT_DEPENDS}
       COMMAND ${CMAKE_ASM_NASM_COMPILER} -f${CMAKE_ASM_NASM_OBJECT_FORMAT}
         ${CMAKE_ASM_NASM_FLAGS} ${CMAKE_CURRENT_SOURCE_DIR}/${file}
@@ -180,7 +183,7 @@
   endif()
 endforeach()
 
-if(MSVC_IDE)
+if(MSVC_IDE OR XCODE)
   set(SIMD_OBJS ${SIMD_OBJS} PARENT_SCOPE)
   add_library(simd OBJECT ${CPU_TYPE}/jsimd.c)
   add_custom_target(simd-objs DEPENDS ${SIMD_OBJS})
diff --git a/simd/arm/jsimd.c b/simd/arm/jsimd.c
index 0fb8197..45f9b04 100644
--- a/simd/arm/jsimd.c
+++ b/simd/arm/jsimd.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2011, Nokia Corporation and/or its subsidiary(-ies).
  * Copyright (C) 2009-2011, 2013-2014, 2016, 2018, D. R. Commander.
  * Copyright (C) 2015-2016, 2018, Matthieu Darbois.
+ * Copyright (C) 2019, Google LLC.
  *
  * Based on the x86 SIMD extension for IJG JPEG library,
  * Copyright (C) 1999-2006, MIYASAKA Masaru.
@@ -30,7 +31,7 @@
 static unsigned int simd_support = ~0;
 static unsigned int simd_huffman = 1;
 
-#if defined(__linux__) || defined(ANDROID) || defined(__ANDROID__)
+#if !defined(__ARM_NEON__) && (defined(__linux__) || defined(ANDROID) || defined(__ANDROID__))
 
 #define SOMEWHAT_SANE_PROC_CPUINFO_SIZE_LIMIT  (1024 * 1024)
 
@@ -105,7 +106,7 @@
 #ifndef NO_GETENV
   char *env = NULL;
 #endif
-#if !defined(__ARM_NEON__) && defined(__linux__) || defined(ANDROID) || defined(__ANDROID__)
+#if !defined(__ARM_NEON__) && (defined(__linux__) || defined(ANDROID) || defined(__ANDROID__))
   int bufsize = 1024; /* an initial guess for the line buffer size limit */
 #endif
 
diff --git a/simd/i386/jcphuff-sse2.asm b/simd/i386/jcphuff-sse2.asm
index 25c63c7..e35a7d8 100644
--- a/simd/i386/jcphuff-sse2.asm
+++ b/simd/i386/jcphuff-sse2.asm
@@ -329,6 +329,8 @@
     add         LUT, 16*SIZEOF_INT
     dec         K
     jnz         .BLOOP16
+    test        LEN, 15
+    je          .PADDING
 .ELOOP16:
     mov         LENEND, LEN
     and         LENEND, 7
diff --git a/simd/i386/jsimdcpu.asm b/simd/i386/jsimdcpu.asm
index faddd38..0af4eec 100644
--- a/simd/i386/jsimdcpu.asm
+++ b/simd/i386/jsimdcpu.asm
@@ -51,29 +51,14 @@
     xor         eax, edx
     jz          near .return            ; CPUID is not supported
 
-    ; Check for MMX instruction support
+    ; Check whether CPUID leaf 07H is supported
+    ; (leaf 07H is used to check for AVX2 instruction support)
     xor         eax, eax
     cpuid
     test        eax, eax
     jz          near .return
-
-    xor         eax, eax
-    inc         eax
-    cpuid
-    mov         eax, edx                ; eax = Standard feature flags
-
-    test        eax, 1<<23              ; bit23:MMX
-    jz          short .no_mmx
-    or          edi, byte JSIMD_MMX
-.no_mmx:
-    test        eax, 1<<25              ; bit25:SSE
-    jz          short .no_sse
-    or          edi, byte JSIMD_SSE
-.no_sse:
-    test        eax, 1<<26              ; bit26:SSE2
-    jz          short .no_sse2
-    or          edi, byte JSIMD_SSE2
-.no_sse2:
+    cmp         eax, 7
+    jl          short .no_avx2          ; Maximum leaf < 07H
 
     ; Check for AVX2 instruction support
     mov         eax, 7
@@ -102,6 +87,26 @@
     or          edi, JSIMD_AVX2
 .no_avx2:
 
+    ; Check CPUID leaf 01H for MMX, SSE, and SSE2 support
+    xor         eax, eax
+    inc         eax
+    cpuid
+    mov         eax, edx                ; eax = Standard feature flags
+
+    ; Check for MMX instruction support
+    test        eax, 1<<23              ; bit23:MMX
+    jz          short .no_mmx
+    or          edi, byte JSIMD_MMX
+.no_mmx:
+    test        eax, 1<<25              ; bit25:SSE
+    jz          short .no_sse
+    or          edi, byte JSIMD_SSE
+.no_sse:
+    test        eax, 1<<26              ; bit26:SSE2
+    jz          short .no_sse2
+    or          edi, byte JSIMD_SSE2
+.no_sse2:
+
     ; Check for 3DNow! instruction support
     mov         eax, 0x80000000
     cpuid
diff --git a/simd/x86_64/jcphuff-sse2.asm b/simd/x86_64/jcphuff-sse2.asm
index b17488a..a9446b7 100644
--- a/simd/x86_64/jcphuff-sse2.asm
+++ b/simd/x86_64/jcphuff-sse2.asm
@@ -322,6 +322,8 @@
     add         LUT, 16*SIZEOF_INT
     dec         K
     jnz         .BLOOP16
+    test        LEN, 15
+    je          .PADDING
 .ELOOP16:
     test        LEN, 8
     jz          .TRY7
diff --git a/simd/x86_64/jsimdcpu.asm b/simd/x86_64/jsimdcpu.asm
index 38e1a7b..a905282 100644
--- a/simd/x86_64/jsimdcpu.asm
+++ b/simd/x86_64/jsimdcpu.asm
@@ -38,14 +38,23 @@
 
     xor         rdi, rdi                ; simd support flag
 
+    ; Assume that all x86-64 processors support SSE & SSE2 instructions
+    or          rdi, JSIMD_SSE2
+    or          rdi, JSIMD_SSE
+
+    ; Check whether CPUID leaf 07H is supported
+    ; (leaf 07H is used to check for AVX2 instruction support)
+    mov         rax, 0
+    cpuid
+    cmp         rax, 7
+    jl          short .return           ; Maximum leaf < 07H
+
     ; Check for AVX2 instruction support
     mov         rax, 7
     xor         rcx, rcx
     cpuid
     mov         rax, rbx                ; rax = Extended feature flags
 
-    or          rdi, JSIMD_SSE2
-    or          rdi, JSIMD_SSE
     test        rax, 1<<5               ; bit5:AVX2
     jz          short .return
 
diff --git a/testimages/test.scan b/testimages/test.scan
new file mode 100644
index 0000000..563446d
--- /dev/null
+++ b/testimages/test.scan
@@ -0,0 +1,5 @@
+0 1 2: 0 0 0 0;
+0: 1 16 0 0;
+0: 17 63 0 0;
+1: 1 63 0 0;
+2: 1 63 0 0;
diff --git a/tjbench.c b/tjbench.c
index 0ddcd7b..be6d23c 100644
--- a/tjbench.c
+++ b/tjbench.c
@@ -32,27 +32,28 @@
 #include <ctype.h>
 #include <math.h>
 #include <errno.h>
+#include <limits.h>
 #include <cdjpeg.h>
 #include "./tjutil.h"
 #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; \
 }
-#define _throwunix(m)  _throw(m, strerror(errno))
+#define THROW_UNIX(m)  THROW(m, strerror(errno))
 
 char tjErrorStr[JMSG_LENGTH_MAX] = "\0", tjErrorMsg[JMSG_LENGTH_MAX] = "\0";
 int tjErrorLine = -1, tjErrorCode = -1;
 
-#define _throwtjg(m) { \
+#define THROW_TJG(m) { \
   printf("ERROR in line %d while %s:\n%s\n", __LINE__, m, \
          tjGetErrorStr2(NULL)); \
   retval = -1;  goto bailout; \
 }
 
-#define _throwtj(m) { \
+#define THROW_TJ(m) { \
   int _tjErrorCode = tjGetErrorCode(handle); \
   char *_tjErrorStr = tjGetErrorStr2(handle); \
   \
@@ -95,7 +96,7 @@
 double benchTime = 5.0, warmup = 1.0;
 
 
-char *formatName(int subsamp, int cs, char *buf)
+static char *formatName(int subsamp, int cs, char *buf)
 {
   if (cs == TJCS_YCbCr)
     return (char *)subNameLong[subsamp];
@@ -107,7 +108,7 @@
 }
 
 
-char *sigfig(double val, int figs, char *buf, int len)
+static char *sigfig(double val, int figs, char *buf, int len)
 {
   char format[80];
   int digitsAfterDecimal = figs - (int)ceil(log10(fabs(val)));
@@ -122,9 +123,9 @@
 
 
 /* Custom DCT filter which produces a negative of the image */
-int dummyDCTFilter(short *coeffs, tjregion arrayRegion, tjregion planeRegion,
-                   int componentIndex, int transformIndex,
-                   tjtransform *transform)
+static int dummyDCTFilter(short *coeffs, tjregion arrayRegion,
+                          tjregion planeRegion, int componentIndex,
+                          int transformIndex, tjtransform *transform)
 {
   int i;
 
@@ -135,11 +136,12 @@
 
 
 /* Decompression test */
-int decomp(unsigned char *srcBuf, unsigned char **jpegBuf,
-           unsigned long *jpegSize, unsigned char *dstBuf, int w, int h,
-           int subsamp, int jpegQual, char *fileName, int tilew, int tileh)
+static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf,
+                  unsigned long *jpegSize, unsigned char *dstBuf, int w, int h,
+                  int subsamp, int jpegQual, char *fileName, int tilew,
+                  int tileh)
 {
-  char tempStr[1024], sizeStr[20] = "\0", qualStr[13] = "\0", *ptr;
+  char tempStr[1024], sizeStr[24] = "\0", qualStr[13] = "\0", *ptr;
   FILE *file = NULL;
   tjhandle handle = NULL;
   int row, col, iter = 0, dstBufAlloc = 0, retval = 0;
@@ -157,11 +159,14 @@
   }
 
   if ((handle = tjInitDecompress()) == NULL)
-    _throwtj("executing tjInitDecompress()");
+    THROW_TJ("executing tjInitDecompress()");
 
   if (dstBuf == NULL) {
-    if ((dstBuf = (unsigned char *)malloc(pitch * scaledh)) == NULL)
-      _throwunix("allocating destination buffer");
+    if ((unsigned long long)pitch * (unsigned long long)scaledh >
+        (unsigned long long)((size_t)-1))
+      THROW("allocating destination buffer", "Image is too large");
+    if ((dstBuf = (unsigned char *)malloc((size_t)pitch * scaledh)) == NULL)
+      THROW_UNIX("allocating destination buffer");
     dstBufAlloc = 1;
   }
   /* Set the destination buffer to gray so we know whether the decompressor
@@ -171,10 +176,12 @@
   if (doYUV) {
     int width = doTile ? tilew : scaledw;
     int height = doTile ? tileh : scaledh;
-    int yuvSize = tjBufSizeYUV2(width, yuvPad, height, subsamp);
+    unsigned long yuvSize = tjBufSizeYUV2(width, yuvPad, height, subsamp);
 
+    if (yuvSize == (unsigned long)-1)
+      THROW_TJ("allocating YUV buffer");
     if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL)
-      _throwunix("allocating YUV buffer");
+      THROW_UNIX("allocating YUV buffer");
     memset(yuvBuf, 127, yuvSize);
   }
 
@@ -197,16 +204,16 @@
 
           if (tjDecompressToYUV2(handle, jpegBuf[tile], jpegSize[tile], yuvBuf,
                                  width, yuvPad, height, flags) == -1)
-            _throwtj("executing tjDecompressToYUV2()");
+            THROW_TJ("executing tjDecompressToYUV2()");
           startDecode = getTime();
           if (tjDecodeYUV(handle, yuvBuf, yuvPad, subsamp, dstPtr2, width,
                           pitch, height, pf, flags) == -1)
-            _throwtj("executing tjDecodeYUV()");
+            THROW_TJ("executing tjDecodeYUV()");
           if (iter >= 0) elapsedDecode += getTime() - startDecode;
         } else if (tjDecompress2(handle, jpegBuf[tile], jpegSize[tile],
                                  dstPtr2, width, pitch, height, pf,
                                  flags) == -1)
-          _throwtj("executing tjDecompress2()");
+          THROW_TJ("executing tjDecompress2()");
       }
     }
     elapsed += getTime() - start;
@@ -220,7 +227,7 @@
   }
   if (doYUV) elapsed -= elapsedDecode;
 
-  if (tjDestroy(handle) == -1) _throwtj("executing tjDestroy()");
+  if (tjDestroy(handle) == -1) THROW_TJ("executing tjDestroy()");
   handle = NULL;
 
   if (quiet) {
@@ -249,10 +256,10 @@
   if (!doWrite) goto bailout;
 
   if (sf.num != 1 || sf.denom != 1)
-    snprintf(sizeStr, 20, "%d_%d", sf.num, sf.denom);
+    snprintf(sizeStr, 24, "%d_%d", sf.num, sf.denom);
   else if (tilew != w || tileh != h)
-    snprintf(sizeStr, 20, "%dx%d", tilew, tileh);
-  else snprintf(sizeStr, 20, "full");
+    snprintf(sizeStr, 24, "%dx%d", tilew, tileh);
+  else snprintf(sizeStr, 24, "full");
   if (decompOnly)
     snprintf(tempStr, 1024, "%s_%s.%s", fileName, sizeStr, ext);
   else
@@ -260,19 +267,19 @@
              qualStr, sizeStr, ext);
 
   if (tjSaveImage(tempStr, dstBuf, scaledw, 0, scaledh, pf, flags) == -1)
-    _throwtjg("saving bitmap");
+    THROW_TJG("saving bitmap");
   ptr = strrchr(tempStr, '.');
   snprintf(ptr, 1024 - (ptr - tempStr), "-err.%s", ext);
   if (srcBuf && sf.num == 1 && sf.denom == 1) {
     if (!quiet) printf("Compression error written to %s.\n", tempStr);
     if (subsamp == TJ_GRAYSCALE) {
-      int index, index2;
+      unsigned long index, index2;
 
       for (row = 0, index = 0; row < h; row++, index += pitch) {
         for (col = 0, index2 = index; col < w; col++, index2 += ps) {
-          int rindex = index2 + tjRedOffset[pf];
-          int gindex = index2 + tjGreenOffset[pf];
-          int bindex = index2 + tjBlueOffset[pf];
+          unsigned long rindex = index2 + tjRedOffset[pf];
+          unsigned long gindex = index2 + tjGreenOffset[pf];
+          unsigned long bindex = index2 + tjBlueOffset[pf];
           int y = (int)((double)srcBuf[rindex] * 0.299 +
                         (double)srcBuf[gindex] * 0.587 +
                         (double)srcBuf[bindex] * 0.114 + 0.5);
@@ -291,7 +298,7 @@
             abs(dstBuf[pitch * row + col] - srcBuf[pitch * row + col]);
     }
     if (tjSaveImage(tempStr, dstBuf, w, 0, h, pf, flags) == -1)
-      _throwtjg("saving bitmap");
+      THROW_TJG("saving bitmap");
   }
 
 bailout:
@@ -303,8 +310,8 @@
 }
 
 
-int fullTest(unsigned char *srcBuf, int w, int h, int subsamp, int jpegQual,
-             char *fileName)
+static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp,
+                    int jpegQual, char *fileName)
 {
   char tempStr[1024], tempStr2[80];
   FILE *file = NULL;
@@ -313,14 +320,17 @@
     *srcPtr2;
   double start, elapsed, elapsedEncode;
   int totalJpegSize = 0, row, col, i, tilew = w, tileh = h, retval = 0;
-  int iter, yuvSize = 0;
-  unsigned long *jpegSize = NULL;
+  int iter;
+  unsigned long *jpegSize = NULL, yuvSize = 0;
   int ps = tjPixelSize[pf];
   int ntilesw = 1, ntilesh = 1, pitch = w * ps;
   const char *pfStr = pixFormatStr[pf];
 
-  if ((tmpBuf = (unsigned char *)malloc(pitch * h)) == NULL)
-    _throwunix("allocating temporary image buffer");
+  if ((unsigned long long)pitch * (unsigned long long)h >
+      (unsigned long long)((size_t)-1))
+    THROW("allocating temporary image buffer", "Image is too large");
+  if ((tmpBuf = (unsigned char *)malloc((size_t)pitch * h)) == NULL)
+    THROW_UNIX("allocating temporary image buffer");
 
   if (!quiet)
     printf(">>>>>  %s (%s) <--> JPEG %s Q%d  <<<<<\n", pfStr,
@@ -336,18 +346,20 @@
 
     if ((jpegBuf = (unsigned char **)malloc(sizeof(unsigned char *) *
                                             ntilesw * ntilesh)) == NULL)
-      _throwunix("allocating JPEG tile array");
+      THROW_UNIX("allocating JPEG tile array");
     memset(jpegBuf, 0, sizeof(unsigned char *) * ntilesw * ntilesh);
     if ((jpegSize = (unsigned long *)malloc(sizeof(unsigned long) *
                                             ntilesw * ntilesh)) == NULL)
-      _throwunix("allocating JPEG size array");
+      THROW_UNIX("allocating JPEG size array");
     memset(jpegSize, 0, sizeof(unsigned long) * ntilesw * ntilesh);
 
     if ((flags & TJFLAG_NOREALLOC) != 0)
       for (i = 0; i < ntilesw * ntilesh; i++) {
+        if (tjBufSize(tilew, tileh, subsamp) > (unsigned long)INT_MAX)
+          THROW("getting buffer size", "Image is too large");
         if ((jpegBuf[i] = (unsigned char *)
                           tjAlloc(tjBufSize(tilew, tileh, subsamp))) == NULL)
-          _throwunix("allocating JPEG tiles");
+          THROW_UNIX("allocating JPEG tiles");
       }
 
     /* Compression test */
@@ -358,12 +370,14 @@
     for (i = 0; i < h; i++)
       memcpy(&tmpBuf[pitch * i], &srcBuf[w * ps * i], w * ps);
     if ((handle = tjInitCompress()) == NULL)
-      _throwtj("executing tjInitCompress()");
+      THROW_TJ("executing tjInitCompress()");
 
     if (doYUV) {
       yuvSize = tjBufSizeYUV2(tilew, yuvPad, tileh, subsamp);
+      if (yuvSize == (unsigned long)-1)
+        THROW_TJ("allocating YUV buffer");
       if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL)
-        _throwunix("allocating YUV buffer");
+        THROW_UNIX("allocating YUV buffer");
       memset(yuvBuf, 127, yuvSize);
     }
 
@@ -387,17 +401,17 @@
 
             if (tjEncodeYUV3(handle, srcPtr2, width, pitch, height, pf, yuvBuf,
                              yuvPad, subsamp, flags) == -1)
-              _throwtj("executing tjEncodeYUV3()");
+              THROW_TJ("executing tjEncodeYUV3()");
             if (iter >= 0) elapsedEncode += getTime() - startEncode;
             if (tjCompressFromYUV(handle, yuvBuf, width, yuvPad, height,
                                   subsamp, &jpegBuf[tile], &jpegSize[tile],
                                   jpegQual, flags) == -1)
-              _throwtj("executing tjCompressFromYUV()");
+              THROW_TJ("executing tjCompressFromYUV()");
           } else {
             if (tjCompress2(handle, srcPtr2, width, pitch, height, pf,
                             &jpegBuf[tile], &jpegSize[tile], subsamp, jpegQual,
                             flags) == -1)
-              _throwtj("executing tjCompress2()");
+              THROW_TJ("executing tjCompress2()");
           }
           totalJpegSize += jpegSize[tile];
         }
@@ -413,7 +427,7 @@
     }
     if (doYUV) elapsed -= elapsedEncode;
 
-    if (tjDestroy(handle) == -1) _throwtj("executing tjDestroy()");
+    if (tjDestroy(handle) == -1) THROW_TJ("executing tjDestroy()");
     handle = NULL;
 
     if (quiet == 1) printf("%-5d  %-5d   ", tilew, tileh);
@@ -436,7 +450,7 @@
       if (doYUV) {
         printf("Encode YUV    --> Frame rate:         %f fps\n",
                (double)iter / elapsedEncode);
-        printf("                  Output image size:  %d bytes\n", yuvSize);
+        printf("                  Output image size:  %lu bytes\n", yuvSize);
         printf("                  Compression ratio:  %f:1\n",
                (double)(w * h * ps) / (double)yuvSize);
         printf("                  Throughput:         %f Megapixels/sec\n",
@@ -460,9 +474,9 @@
       snprintf(tempStr, 1024, "%s_%s_Q%d.jpg", fileName, subName[subsamp],
                jpegQual);
       if ((file = fopen(tempStr, "wb")) == NULL)
-        _throwunix("opening reference image");
+        THROW_UNIX("opening reference image");
       if (fwrite(jpegBuf[0], jpegSize[0], 1, file) != 1)
-        _throwunix("writing reference image");
+        THROW_UNIX("writing reference image");
       fclose(file);  file = NULL;
       if (!quiet) printf("Reference image written to %s\n", tempStr);
     }
@@ -504,7 +518,7 @@
 }
 
 
-int decompTest(char *fileName)
+static int decompTest(char *fileName)
 {
   FILE *file = NULL;
   tjhandle handle = NULL;
@@ -521,28 +535,28 @@
   int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp;
 
   if ((file = fopen(fileName, "rb")) == NULL)
-    _throwunix("opening file");
+    THROW_UNIX("opening file");
   if (fseek(file, 0, SEEK_END) < 0 ||
       (srcSize = ftell(file)) == (unsigned long)-1)
-    _throwunix("determining file size");
+    THROW_UNIX("determining file size");
   if ((srcBuf = (unsigned char *)malloc(srcSize)) == NULL)
-    _throwunix("allocating memory");
+    THROW_UNIX("allocating memory");
   if (fseek(file, 0, SEEK_SET) < 0)
-    _throwunix("setting file position");
+    THROW_UNIX("setting file position");
   if (fread(srcBuf, srcSize, 1, file) < 1)
-    _throwunix("reading JPEG data");
+    THROW_UNIX("reading JPEG data");
   fclose(file);  file = NULL;
 
   temp = strrchr(fileName, '.');
   if (temp != NULL) *temp = '\0';
 
   if ((handle = tjInitTransform()) == NULL)
-    _throwtj("executing tjInitTransform()");
+    THROW_TJ("executing tjInitTransform()");
   if (tjDecompressHeader3(handle, srcBuf, srcSize, &w, &h, &subsamp,
                           &cs) == -1)
-    _throwtj("executing tjDecompressHeader3()");
+    THROW_TJ("executing tjDecompressHeader3()");
   if (w < 1 || h < 1)
-    _throw("reading JPEG header", "Invalid image dimensions");
+    THROW("reading JPEG header", "Invalid image dimensions");
   if (cs == TJCS_YCCK || cs == TJCS_CMYK) {
     pf = TJPF_CMYK;  ps = tjPixelSize[pf];
   }
@@ -570,18 +584,21 @@
 
     if ((jpegBuf = (unsigned char **)malloc(sizeof(unsigned char *) *
                                             ntilesw * ntilesh)) == NULL)
-      _throwunix("allocating JPEG tile array");
+      THROW_UNIX("allocating JPEG tile array");
     memset(jpegBuf, 0, sizeof(unsigned char *) * ntilesw * ntilesh);
     if ((jpegSize = (unsigned long *)malloc(sizeof(unsigned long) *
                                             ntilesw * ntilesh)) == NULL)
-      _throwunix("allocating JPEG size array");
+      THROW_UNIX("allocating JPEG size array");
     memset(jpegSize, 0, sizeof(unsigned long) * ntilesw * ntilesh);
 
-    if ((flags & TJFLAG_NOREALLOC) != 0 || !doTile)
+    if ((flags & TJFLAG_NOREALLOC) != 0 &&
+        (doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter))
       for (i = 0; i < ntilesw * ntilesh; i++) {
+        if (tjBufSize(tilew, tileh, subsamp) > (unsigned long)INT_MAX)
+          THROW("getting buffer size", "Image is too large");
         if ((jpegBuf[i] = (unsigned char *)
                           tjAlloc(tjBufSize(tilew, tileh, subsamp))) == NULL)
-          _throwunix("allocating JPEG tiles");
+          THROW_UNIX("allocating JPEG tiles");
       }
 
     tw = w;  th = h;  ttilew = tilew;  ttileh = tileh;
@@ -601,7 +618,7 @@
     if (doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter) {
       if ((t = (tjtransform *)malloc(sizeof(tjtransform) * ntilesw *
                                      ntilesh)) == NULL)
-        _throwunix("allocating image transform array");
+        THROW_UNIX("allocating image transform array");
 
       if (xformOp == TJXOP_TRANSPOSE || xformOp == TJXOP_TRANSVERSE ||
           xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT270) {
@@ -647,7 +664,7 @@
         start = getTime();
         if (tjTransform(handle, srcBuf, srcSize, tntilesw * tntilesh, jpegBuf,
                         jpegSize, t, flags) == -1)
-          _throwtj("executing tjTransform()");
+          THROW_TJ("executing tjTransform()");
         elapsed += getTime() - start;
         if (iter >= 0) {
           iter++;
@@ -684,7 +701,7 @@
       }
     } else {
       if (quiet == 1) printf("N/A     N/A     ");
-      tjFree(jpegBuf[0]);
+      if (jpegBuf[0]) tjFree(jpegBuf[0]);
       jpegBuf[0] = NULL;
       decompsrc = 1;
     }
@@ -699,7 +716,8 @@
     } else if (quiet == 1) printf("N/A\n");
 
     for (i = 0; i < ntilesw * ntilesh; i++) {
-      tjFree(jpegBuf[i]);  jpegBuf[i] = NULL;
+      if (jpegBuf[i]) tjFree(jpegBuf[i]);
+      jpegBuf[i] = NULL;
     }
     free(jpegBuf);  jpegBuf = NULL;
     if (jpegSize) { free(jpegSize);  jpegSize = NULL; }
@@ -724,7 +742,7 @@
 }
 
 
-void usage(char *progName)
+static void usage(char *progName)
 {
   int i;
 
@@ -803,7 +821,7 @@
   int minArg = 2, retval = 0, subsamp = -1;
 
   if ((scalingFactors = tjGetScalingFactors(&nsf)) == NULL || nsf == 0)
-    _throw("executing tjGetScalingFactors()", tjGetErrorStr());
+    THROW("executing tjGetScalingFactors()", tjGetErrorStr());
 
   if (argc < minArg) usage(argv[0]);
 
@@ -902,14 +920,14 @@
       else if (!strcasecmp(argv[i], "-copynone"))
         xformOpt |= TJXOPT_COPYNONE;
       else if (!strcasecmp(argv[i], "-benchtime") && i < argc - 1) {
-        double temp = atof(argv[++i]);
+        double tempd = atof(argv[++i]);
 
-        if (temp > 0.0) benchTime = temp;
+        if (tempd > 0.0) benchTime = tempd;
         else usage(argv[0]);
       } else if (!strcasecmp(argv[i], "-warmup") && i < argc - 1) {
-        double temp = atof(argv[++i]);
+        double tempd = atof(argv[++i]);
 
-        if (temp >= 0.0) warmup = temp;
+        if (tempd >= 0.0) warmup = tempd;
         else usage(argv[0]);
         printf("Warmup time = %.1f seconds\n\n", warmup);
       } else if (!strcasecmp(argv[i], "-alloc"))
@@ -920,16 +938,16 @@
         printf("Testing YUV planar encoding/decoding\n\n");
         doYUV = 1;
       } else if (!strcasecmp(argv[i], "-yuvpad") && i < argc - 1) {
-        int temp = atoi(argv[++i]);
+        int tempi = atoi(argv[++i]);
 
-        if (temp >= 1) yuvPad = temp;
+        if (tempi >= 1) yuvPad = tempi;
       } else if (!strcasecmp(argv[i], "-subsamp") && i < argc - 1) {
         i++;
         if (toupper(argv[i][0]) == 'G') subsamp = TJSAMP_GRAY;
         else {
-          int temp = atoi(argv[i]);
+          int tempi = atoi(argv[i]);
 
-          switch (temp) {
+          switch (tempi) {
           case 444:  subsamp = TJSAMP_444;  break;
           case 422:  subsamp = TJSAMP_422;  break;
           case 440:  subsamp = TJSAMP_440;  break;
@@ -961,7 +979,7 @@
 
   if (!decompOnly) {
     if ((srcBuf = tjLoadImage(argv[1], &w, 1, &h, &pf, flags)) == NULL)
-      _throwtjg("loading bitmap");
+      THROW_TJG("loading bitmap");
     temp = strrchr(argv[1], '.');
     if (temp != NULL) *temp = '\0';
   }
diff --git a/tjexample.c b/tjexample.c
index 61200e6..001ea49 100644
--- a/tjexample.c
+++ b/tjexample.c
@@ -1,6 +1,6 @@
 /*
- * Copyright (C)2011-2012, 2014-2015, 2017 D. R. Commander.
- *                                         All Rights Reserved.
+ * Copyright (C)2011-2012, 2014-2015, 2017, 2019 D. R. Commander.
+ *                                               All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -44,14 +44,14 @@
 #define strncasecmp  strnicmp
 #endif
 
-#define _throw(action, message) { \
+#define THROW(action, message) { \
   printf("ERROR in line %d while %s:\n%s\n", __LINE__, action, message); \
   retval = -1;  goto bailout; \
 }
 
-#define _throwtj(action)  _throw(action, tjGetErrorStr2(tjInstance))
+#define THROW_TJ(action)  THROW(action, tjGetErrorStr2(tjInstance))
 
-#define _throwunix(action)  _throw(action, strerror(errno))
+#define THROW_UNIX(action)  THROW(action, strerror(errno))
 
 #define DEFAULT_SUBSAMP  TJSAMP_444
 #define DEFAULT_QUALITY  95
@@ -71,9 +71,9 @@
 
 /* DCT filter example.  This produces a negative of the image. */
 
-int customFilter(short *coeffs, tjregion arrayRegion, tjregion planeRegion,
-                 int componentIndex, int transformIndex,
-                 tjtransform *transform)
+static int customFilter(short *coeffs, tjregion arrayRegion,
+                        tjregion planeRegion, int componentIndex,
+                        int transformIndex, tjtransform *transform)
 {
   int i;
 
@@ -84,7 +84,7 @@
 }
 
 
-void usage(char *programName)
+static void usage(char *programName)
 {
   int i;
 
@@ -172,7 +172,7 @@
   tjhandle tjInstance = NULL;
 
   if ((scalingFactors = tjGetScalingFactors(&numScalingFactors)) == NULL)
-    _throwtj("getting scaling factors");
+    THROW_TJ("getting scaling factors");
   memset(&xform, 0, sizeof(tjtransform));
 
   if (argc < 3)
@@ -266,17 +266,17 @@
 
     /* Read the JPEG file into memory. */
     if ((jpegFile = fopen(argv[1], "rb")) == NULL)
-      _throwunix("opening input file");
+      THROW_UNIX("opening input file");
     if (fseek(jpegFile, 0, SEEK_END) < 0 || ((size = ftell(jpegFile)) < 0) ||
         fseek(jpegFile, 0, SEEK_SET) < 0)
-      _throwunix("determining input file size");
+      THROW_UNIX("determining input file size");
     if (size == 0)
-      _throw("determining input file size", "Input file contains no data");
+      THROW("determining input file size", "Input file contains no data");
     jpegSize = (unsigned long)size;
     if ((jpegBuf = (unsigned char *)tjAlloc(jpegSize)) == NULL)
-      _throwunix("allocating JPEG buffer");
+      THROW_UNIX("allocating JPEG buffer");
     if (fread(jpegBuf, jpegSize, 1, jpegFile) < 1)
-      _throwunix("reading input file");
+      THROW_UNIX("reading input file");
     fclose(jpegFile);  jpegFile = NULL;
 
     if (doTransform) {
@@ -285,22 +285,22 @@
       unsigned long dstSize = 0;
 
       if ((tjInstance = tjInitTransform()) == NULL)
-        _throwtj("initializing transformer");
+        THROW_TJ("initializing transformer");
       xform.options |= TJXOPT_TRIM;
       if (tjTransform(tjInstance, jpegBuf, jpegSize, 1, &dstBuf, &dstSize,
                       &xform, flags) < 0)
-        _throwtj("transforming input image");
+        THROW_TJ("transforming input image");
       tjFree(jpegBuf);
       jpegBuf = dstBuf;
       jpegSize = dstSize;
     } else {
       if ((tjInstance = tjInitDecompress()) == NULL)
-        _throwtj("initializing decompressor");
+        THROW_TJ("initializing decompressor");
     }
 
     if (tjDecompressHeader3(tjInstance, jpegBuf, jpegSize, &width, &height,
                             &inSubsamp, &inColorspace) < 0)
-      _throwtj("reading JPEG header");
+      THROW_TJ("reading JPEG header");
 
     printf("%s Image:  %d x %d pixels, %s subsampling, %s colorspace\n",
            (doTransform ? "Transformed" : "Input"), width, height,
@@ -312,9 +312,9 @@
       /* Input image has been transformed, and no re-compression options
          have been selected.  Write the transformed image to disk and exit. */
       if ((jpegFile = fopen(argv[2], "wb")) == NULL)
-        _throwunix("opening output file");
+        THROW_UNIX("opening output file");
       if (fwrite(jpegBuf, jpegSize, 1, jpegFile) < 1)
-        _throwunix("writing output file");
+        THROW_UNIX("writing output file");
       fclose(jpegFile);  jpegFile = NULL;
       goto bailout;
     }
@@ -330,18 +330,18 @@
     pixelFormat = TJPF_BGRX;
     if ((imgBuf = (unsigned char *)tjAlloc(width * height *
                                            tjPixelSize[pixelFormat])) == NULL)
-      _throwunix("allocating uncompressed image buffer");
+      THROW_UNIX("allocating uncompressed image buffer");
 
     if (tjDecompress2(tjInstance, jpegBuf, jpegSize, imgBuf, width, 0, height,
                       pixelFormat, flags) < 0)
-      _throwtj("decompressing JPEG image");
+      THROW_TJ("decompressing JPEG image");
     tjFree(jpegBuf);  jpegBuf = NULL;
     tjDestroy(tjInstance);  tjInstance = NULL;
   } else {
     /* Input image is not a JPEG image.  Load it into memory. */
     if ((imgBuf = tjLoadImage(argv[1], &width, 1, &height, &pixelFormat,
                               0)) == NULL)
-      _throwtj("loading input image");
+      THROW_TJ("loading input image");
     if (outSubsamp < 0) {
       if (pixelFormat == TJPF_GRAY)
         outSubsamp = TJSAMP_GRAY;
@@ -355,26 +355,27 @@
 
   if (!strcasecmp(outFormat, "jpg")) {
     /* Output image format is JPEG.  Compress the uncompressed image. */
-    unsigned char *jpegBuf = NULL;  /* Dynamically allocate the JPEG buffer */
     unsigned long jpegSize = 0;
 
+    jpegBuf = NULL;  /* Dynamically allocate the JPEG buffer */
+
     if (outQual < 0)
       outQual = DEFAULT_QUALITY;
     printf(", %s subsampling, quality = %d\n", subsampName[outSubsamp],
            outQual);
 
     if ((tjInstance = tjInitCompress()) == NULL)
-      _throwtj("initializing compressor");
+      THROW_TJ("initializing compressor");
     if (tjCompress2(tjInstance, imgBuf, width, 0, height, pixelFormat,
                     &jpegBuf, &jpegSize, outSubsamp, outQual, flags) < 0)
-      _throwtj("compressing image");
+      THROW_TJ("compressing image");
     tjDestroy(tjInstance);  tjInstance = NULL;
 
     /* Write the JPEG image to disk. */
     if ((jpegFile = fopen(argv[2], "wb")) == NULL)
-      _throwunix("opening output file");
+      THROW_UNIX("opening output file");
     if (fwrite(jpegBuf, jpegSize, 1, jpegFile) < 1)
-      _throwunix("writing output file");
+      THROW_UNIX("writing output file");
     tjDestroy(tjInstance);  tjInstance = NULL;
     fclose(jpegFile);  jpegFile = NULL;
     tjFree(jpegBuf);  jpegBuf = NULL;
@@ -383,7 +384,7 @@
        directly to disk. */
     printf("\n");
     if (tjSaveImage(argv[2], imgBuf, width, 0, height, pixelFormat, 0) < 0)
-      _throwtj("saving output image");
+      THROW_TJ("saving output image");
   }
 
 bailout:
diff --git a/tjunittest.c b/tjunittest.c
index ae72e83..da7c6ff 100644
--- a/tjunittest.c
+++ b/tjunittest.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2009-2014, 2017-2018 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2009-2014, 2017-2019 D. R. Commander.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -46,7 +46,7 @@
 #endif
 
 
-void usage(char *progName)
+static void usage(char *progName)
 {
   printf("\nUSAGE: %s [options]\n\n", progName);
   printf("Options:\n");
@@ -59,16 +59,16 @@
 }
 
 
-#define _throwtj() { \
+#define THROW_TJ() { \
   printf("TurboJPEG ERROR:\n%s\n", tjGetErrorStr()); \
-  bailout() \
+  BAILOUT() \
 }
-#define _tj(f) { if ((f) == -1) _throwtj(); }
-#define _throw(m) { printf("ERROR: %s\n", m);  bailout() }
-#define _throwmd5(filename, md5sum, ref) { \
+#define TRY_TJ(f) { if ((f) == -1) THROW_TJ(); }
+#define THROW(m) { printf("ERROR: %s\n", m);  BAILOUT() }
+#define THROW_MD5(filename, md5sum, ref) { \
   printf("\n%s has an MD5 sum of %s.\n   Should be %s.\n", filename, md5sum, \
          ref); \
-  bailout() \
+  BAILOUT() \
 }
 
 const char *subNameLong[TJ_NUMSAMP] = {
@@ -93,10 +93,10 @@
 int doYUV = 0, alloc = 0, pad = 4;
 
 int exitStatus = 0;
-#define bailout() { exitStatus = -1;  goto bailout; }
+#define BAILOUT() { exitStatus = -1;  goto bailout; }
 
 
-void initBuf(unsigned char *buf, int w, int h, int pf, int flags)
+static void initBuf(unsigned char *buf, int w, int h, int pf, int flags)
 {
   int roffset = tjRedOffset[pf];
   int goffset = tjGreenOffset[pf];
@@ -151,7 +151,7 @@
 }
 
 
-#define checkval(v, cv) { \
+#define CHECKVAL(v, cv) { \
   if (v < cv - 1 || v > cv + 1) { \
     printf("\nComp. %s at %d,%d should be %d, not %d\n", #v, row, col, cv, \
            v); \
@@ -159,14 +159,14 @@
   } \
 }
 
-#define checkval0(v) { \
+#define CHECKVAL0(v) { \
   if (v > 1) { \
     printf("\nComp. %s at %d,%d should be 0, not %d\n", #v, row, col, v); \
     retval = 0;  exitStatus = -1;  goto bailout; \
   } \
 }
 
-#define checkval255(v) { \
+#define CHECKVAL255(v) { \
   if (v < 254) { \
     printf("\nComp. %s at %d,%d should be 255, not %d\n", #v, row, col, v); \
     retval = 0;  exitStatus = -1;  goto bailout; \
@@ -174,8 +174,8 @@
 }
 
 
-int checkBuf(unsigned char *buf, int w, int h, int pf, int subsamp,
-             tjscalingfactor sf, int flags)
+static int checkBuf(unsigned char *buf, int w, int h, int pf, int subsamp,
+                    tjscalingfactor sf, int flags)
 {
   int roffset = tjRedOffset[pf];
   int goffset = tjGreenOffset[pf];
@@ -200,13 +200,13 @@
         y = buf[index * ps + 2];
         k = buf[index * ps + 3];
         if (((row / blocksize) + (col / blocksize)) % 2 == 0) {
-          checkval255(c);  checkval255(m);  checkval255(y);
-          if (row < halfway) checkval255(k)
-          else checkval0(k)
+          CHECKVAL255(c);  CHECKVAL255(m);  CHECKVAL255(y);
+          if (row < halfway) CHECKVAL255(k)
+          else CHECKVAL0(k)
         } else {
-          checkval255(c);  checkval0(y);  checkval255(k);
-          if (row < halfway) checkval0(m)
-          else checkval255(m)
+          CHECKVAL255(c);  CHECKVAL0(y);  CHECKVAL255(k);
+          if (row < halfway) CHECKVAL0(m)
+          else CHECKVAL255(m)
         }
       }
     }
@@ -225,26 +225,26 @@
       a = aoffset >= 0 ? buf[index * ps + aoffset] : 0xFF;
       if (((row / blocksize) + (col / blocksize)) % 2 == 0) {
         if (row < halfway) {
-          checkval255(r);  checkval255(g);  checkval255(b);
+          CHECKVAL255(r);  CHECKVAL255(g);  CHECKVAL255(b);
         } else {
-          checkval0(r);  checkval0(g);  checkval0(b);
+          CHECKVAL0(r);  CHECKVAL0(g);  CHECKVAL0(b);
         }
       } else {
         if (subsamp == TJSAMP_GRAY) {
           if (row < halfway) {
-            checkval(r, 76);  checkval(g, 76);  checkval(b, 76);
+            CHECKVAL(r, 76);  CHECKVAL(g, 76);  CHECKVAL(b, 76);
           } else {
-            checkval(r, 226);  checkval(g, 226);  checkval(b, 226);
+            CHECKVAL(r, 226);  CHECKVAL(g, 226);  CHECKVAL(b, 226);
           }
         } else {
           if (row < halfway) {
-            checkval255(r);  checkval0(g);  checkval0(b);
+            CHECKVAL255(r);  CHECKVAL0(g);  CHECKVAL0(b);
           } else {
-            checkval255(r);  checkval255(g);  checkval0(b);
+            CHECKVAL255(r);  CHECKVAL255(g);  CHECKVAL0(b);
           }
         }
       }
-      checkval255(a);
+      CHECKVAL255(a);
     }
   }
 
@@ -270,8 +270,8 @@
 
 #define PAD(v, p)  ((v + (p) - 1) & (~((p) - 1)))
 
-int checkBufYUV(unsigned char *buf, int w, int h, int subsamp,
-                tjscalingfactor sf)
+static int checkBufYUV(unsigned char *buf, int w, int h, int subsamp,
+                       tjscalingfactor sf)
 {
   int row, col;
   int hsf = tjMCUWidth[subsamp] / 8, vsf = tjMCUHeight[subsamp] / 8;
@@ -287,16 +287,16 @@
       unsigned char y = buf[ypitch * row + col];
 
       if (((row / blocksize) + (col / blocksize)) % 2 == 0) {
-        if (row < halfway) checkval255(y)
-        else checkval0(y);
+        if (row < halfway) CHECKVAL255(y)
+        else CHECKVAL0(y);
       } else {
-        if (row < halfway) checkval(y, 76)
-        else checkval(y, 226);
+        if (row < halfway) CHECKVAL(y, 76)
+        else CHECKVAL(y, 226);
       }
     }
   }
   if (subsamp != TJSAMP_GRAY) {
-    int halfway = 16 / vsf * sf.num / sf.denom;
+    halfway = 16 / vsf * sf.num / sf.denom;
 
     for (row = 0; row < ch; row++) {
       for (col = 0; col < cw; col++) {
@@ -304,12 +304,12 @@
           v = buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)];
 
         if (((row * vsf / blocksize) + (col * hsf / blocksize)) % 2 == 0) {
-          checkval(u, 128);  checkval(v, 128);
+          CHECKVAL(u, 128);  CHECKVAL(v, 128);
         } else {
           if (row < halfway) {
-            checkval(u, 85);  checkval255(v);
+            CHECKVAL(u, 85);  CHECKVAL255(v);
           } else {
-            checkval0(u);  checkval(v, 149);
+            CHECKVAL0(u);  CHECKVAL(v, 149);
           }
         }
       }
@@ -342,13 +342,14 @@
 }
 
 
-void writeJPEG(unsigned char *jpegBuf, unsigned long jpegSize, char *filename)
+static void writeJPEG(unsigned char *jpegBuf, unsigned long jpegSize,
+                      char *filename)
 {
   FILE *file = fopen(filename, "wb");
 
   if (!file || fwrite(jpegBuf, jpegSize, 1, file) != 1) {
     printf("ERROR: Could not write to %s.\n%s\n", filename, strerror(errno));
-    bailout()
+    BAILOUT()
   }
 
 bailout:
@@ -356,9 +357,9 @@
 }
 
 
-void compTest(tjhandle handle, unsigned char **dstBuf, unsigned long *dstSize,
-              int w, int h, int pf, char *basename, int subsamp, int jpegQual,
-              int flags)
+static void compTest(tjhandle handle, unsigned char **dstBuf,
+                     unsigned long *dstSize, int w, int h, int pf,
+                     char *basename, int subsamp, int jpegQual, int flags)
 {
   char tempStr[1024];
   unsigned char *srcBuf = NULL, *yuvBuf = NULL;
@@ -368,7 +369,7 @@
   const char *buStr = (flags & TJFLAG_BOTTOMUP) ? "BU" : "TD";
 
   if ((srcBuf = (unsigned char *)malloc(w * h * tjPixelSize[pf])) == NULL)
-    _throw("Memory allocation failure");
+    THROW("Memory allocation failure");
   initBuf(srcBuf, w, h, pf, flags);
 
   if (*dstBuf && *dstSize > 0) memset(*dstBuf, 0, *dstSize);
@@ -379,28 +380,28 @@
     tjscalingfactor sf = { 1, 1 };
     tjhandle handle2 = tjInitCompress();
 
-    if (!handle2) _throwtj();
+    if (!handle2) THROW_TJ();
 
     if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL)
-      _throw("Memory allocation failure");
+      THROW("Memory allocation failure");
     memset(yuvBuf, 0, yuvSize);
 
     printf("%s %s -> YUV %s ... ", pfStr, buStrLong, subNameLong[subsamp]);
-    _tj(tjEncodeYUV3(handle2, srcBuf, w, 0, h, pf, yuvBuf, pad, subsamp,
-                     flags));
+    TRY_TJ(tjEncodeYUV3(handle2, srcBuf, w, 0, h, pf, yuvBuf, pad, subsamp,
+                        flags));
     tjDestroy(handle2);
     if (checkBufYUV(yuvBuf, w, h, subsamp, sf)) printf("Passed.\n");
     else printf("FAILED!\n");
 
     printf("YUV %s %s -> JPEG Q%d ... ", subNameLong[subsamp], buStrLong,
            jpegQual);
-    _tj(tjCompressFromYUV(handle, yuvBuf, w, pad, h, subsamp, dstBuf, dstSize,
-                          jpegQual, flags));
+    TRY_TJ(tjCompressFromYUV(handle, yuvBuf, w, pad, h, subsamp, dstBuf,
+                             dstSize, jpegQual, flags));
   } else {
     printf("%s %s -> %s Q%d ... ", pfStr, buStrLong, subNameLong[subsamp],
            jpegQual);
-    _tj(tjCompress2(handle, srcBuf, w, 0, h, pf, dstBuf, dstSize, subsamp,
-                    jpegQual, flags));
+    TRY_TJ(tjCompress2(handle, srcBuf, w, 0, h, pf, dstBuf, dstSize, subsamp,
+                       jpegQual, flags));
   }
 
   snprintf(tempStr, 1024, "%s_enc_%s_%s_%s_Q%d.jpg", basename, pfStr, buStr,
@@ -414,9 +415,10 @@
 }
 
 
-void _decompTest(tjhandle handle, unsigned char *jpegBuf,
-                 unsigned long jpegSize, int w, int h, int pf, char *basename,
-                 int subsamp, int flags, tjscalingfactor sf)
+static void _decompTest(tjhandle handle, unsigned char *jpegBuf,
+                        unsigned long jpegSize, int w, int h, int pf,
+                        char *basename, int subsamp, int flags,
+                        tjscalingfactor sf)
 {
   unsigned char *dstBuf = NULL, *yuvBuf = NULL;
   int _hdrw = 0, _hdrh = 0, _hdrsubsamp = -1;
@@ -424,14 +426,14 @@
   int scaledHeight = TJSCALED(h, sf);
   unsigned long dstSize = 0;
 
-  _tj(tjDecompressHeader2(handle, jpegBuf, jpegSize, &_hdrw, &_hdrh,
-                          &_hdrsubsamp));
+  TRY_TJ(tjDecompressHeader2(handle, jpegBuf, jpegSize, &_hdrw, &_hdrh,
+                             &_hdrsubsamp));
   if (_hdrw != w || _hdrh != h || _hdrsubsamp != subsamp)
-    _throw("Incorrect JPEG header");
+    THROW("Incorrect JPEG header");
 
   dstSize = scaledWidth * scaledHeight * tjPixelSize[pf];
   if ((dstBuf = (unsigned char *)malloc(dstSize)) == NULL)
-    _throw("Memory allocation failure");
+    THROW("Memory allocation failure");
   memset(dstBuf, 0, dstSize);
 
   if (doYUV) {
@@ -439,26 +441,26 @@
                                           subsamp);
     tjhandle handle2 = tjInitDecompress();
 
-    if (!handle2) _throwtj();
+    if (!handle2) THROW_TJ();
 
     if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL)
-      _throw("Memory allocation failure");
+      THROW("Memory allocation failure");
     memset(yuvBuf, 0, yuvSize);
 
     printf("JPEG -> YUV %s ", subNameLong[subsamp]);
     if (sf.num != 1 || sf.denom != 1)
       printf("%d/%d ... ", sf.num, sf.denom);
     else printf("... ");
-    _tj(tjDecompressToYUV2(handle, jpegBuf, jpegSize, yuvBuf, scaledWidth, pad,
-                           scaledHeight, flags));
+    TRY_TJ(tjDecompressToYUV2(handle, jpegBuf, jpegSize, yuvBuf, scaledWidth,
+                              pad, scaledHeight, flags));
     if (checkBufYUV(yuvBuf, scaledWidth, scaledHeight, subsamp, sf))
       printf("Passed.\n");
     else printf("FAILED!\n");
 
     printf("YUV %s -> %s %s ... ", subNameLong[subsamp], pixFormatStr[pf],
            (flags & TJFLAG_BOTTOMUP) ? "Bottom-Up" : "Top-Down ");
-    _tj(tjDecodeYUV(handle2, yuvBuf, pad, subsamp, dstBuf, scaledWidth, 0,
-                    scaledHeight, pf, flags));
+    TRY_TJ(tjDecodeYUV(handle2, yuvBuf, pad, subsamp, dstBuf, scaledWidth, 0,
+                       scaledHeight, pf, flags));
     tjDestroy(handle2);
   } else {
     printf("JPEG -> %s %s ", pixFormatStr[pf],
@@ -466,8 +468,8 @@
     if (sf.num != 1 || sf.denom != 1)
       printf("%d/%d ... ", sf.num, sf.denom);
     else printf("... ");
-    _tj(tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth, 0,
-                      scaledHeight, pf, flags));
+    TRY_TJ(tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth, 0,
+                         scaledHeight, pf, flags));
   }
 
   if (checkBuf(dstBuf, scaledWidth, scaledHeight, pf, subsamp, sf, flags))
@@ -481,14 +483,14 @@
 }
 
 
-void decompTest(tjhandle handle, unsigned char *jpegBuf,
-                unsigned long jpegSize, int w, int h, int pf, char *basename,
-                int subsamp, int flags)
+static void decompTest(tjhandle handle, unsigned char *jpegBuf,
+                       unsigned long jpegSize, int w, int h, int pf,
+                       char *basename, int subsamp, int flags)
 {
   int i, n = 0;
   tjscalingfactor *sf = tjGetScalingFactors(&n);
 
-  if (!sf || !n) _throwtj();
+  if (!sf || !n) THROW_TJ();
 
   for (i = 0; i < n; i++) {
     if (subsamp == TJSAMP_444 || subsamp == TJSAMP_GRAY ||
@@ -505,8 +507,8 @@
 }
 
 
-void doTest(int w, int h, const int *formats, int nformats, int subsamp,
-            char *basename)
+static void doTest(int w, int h, const int *formats, int nformats, int subsamp,
+                   char *basename)
 {
   tjhandle chandle = NULL, dhandle = NULL;
   unsigned char *dstBuf = NULL;
@@ -517,11 +519,11 @@
     size = tjBufSize(w, h, subsamp);
   if (size != 0)
     if ((dstBuf = (unsigned char *)tjAlloc(size)) == NULL)
-      _throw("Memory allocation failure.");
+      THROW("Memory allocation failure.");
 
   if ((chandle = tjInitCompress()) == NULL ||
       (dhandle = tjInitDecompress()) == NULL)
-    _throwtj();
+    THROW_TJ();
 
   for (pfi = 0; pfi < nformats; pfi++) {
     for (i = 0; i < 2; i++) {
@@ -552,14 +554,50 @@
 }
 
 
-void bufSizeTest(void)
+#if SIZEOF_SIZE_T == 8
+#define CHECKSIZE(function) { \
+  if ((unsigned long long)size < (unsigned long long)0xFFFFFFFF) \
+    THROW(#function " overflow"); \
+}
+#else
+#define CHECKSIZE(function) { \
+  if (size != (unsigned long)(-1) || \
+      !strcmp(tjGetErrorStr2(NULL), "No error")) \
+    THROW(#function " overflow"); \
+}
+#endif
+
+static void overflowTest(void)
+{
+  /* Ensure that the various buffer size functions don't overflow */
+  unsigned long size;
+
+  size = tjBufSize(26755, 26755, TJSAMP_444);
+  CHECKSIZE(tjBufSize());
+  size = TJBUFSIZE(26755, 26755);
+  CHECKSIZE(TJBUFSIZE());
+  size = tjBufSizeYUV2(37838, 1, 37838, TJSAMP_444);
+  CHECKSIZE(tjBufSizeYUV2());
+  size = TJBUFSIZEYUV(37838, 37838, TJSAMP_444);
+  CHECKSIZE(TJBUFSIZEYUV());
+  size = tjBufSizeYUV(37838, 37838, TJSAMP_444);
+  CHECKSIZE(tjBufSizeYUV());
+  size = tjPlaneSizeYUV(0, 65536, 0, 65536, TJSAMP_444);
+  CHECKSIZE(tjPlaneSizeYUV());
+
+bailout:
+  return;
+}
+
+
+static void bufSizeTest(void)
 {
   int w, h, i, subsamp;
   unsigned char *srcBuf = NULL, *dstBuf = NULL;
   tjhandle handle = NULL;
   unsigned long dstSize = 0;
 
-  if ((handle = tjInitCompress()) == NULL) _throwtj();
+  if ((handle = tjInitCompress()) == NULL) THROW_TJ();
 
   printf("Buffer size regression test\n");
   for (subsamp = 0; subsamp < TJ_NUMSAMP; subsamp++) {
@@ -569,12 +607,12 @@
       for (h = 1; h < maxh; h++) {
         if (h % 100 == 0) printf("%.4d x %.4d\b\b\b\b\b\b\b\b\b\b\b", w, h);
         if ((srcBuf = (unsigned char *)malloc(w * h * 4)) == NULL)
-          _throw("Memory allocation failure");
+          THROW("Memory allocation failure");
         if (!alloc || doYUV) {
           if (doYUV) dstSize = tjBufSizeYUV2(w, pad, h, subsamp);
           else dstSize = tjBufSize(w, h, subsamp);
           if ((dstBuf = (unsigned char *)tjAlloc(dstSize)) == NULL)
-            _throw("Memory allocation failure");
+            THROW("Memory allocation failure");
         }
 
         for (i = 0; i < w * h * 4; i++) {
@@ -583,12 +621,12 @@
         }
 
         if (doYUV) {
-          _tj(tjEncodeYUV3(handle, srcBuf, w, 0, h, TJPF_BGRX, dstBuf, pad,
-                           subsamp, 0));
+          TRY_TJ(tjEncodeYUV3(handle, srcBuf, w, 0, h, TJPF_BGRX, dstBuf, pad,
+                              subsamp, 0));
         } else {
-          _tj(tjCompress2(handle, srcBuf, w, 0, h, TJPF_BGRX, &dstBuf,
-                          &dstSize, subsamp, 100,
-                          alloc ? 0 : TJFLAG_NOREALLOC));
+          TRY_TJ(tjCompress2(handle, srcBuf, w, 0, h, TJPF_BGRX, &dstBuf,
+                             &dstSize, subsamp, 100,
+                             alloc ? 0 : TJFLAG_NOREALLOC));
         }
         free(srcBuf);  srcBuf = NULL;
         if (!alloc || doYUV) {
@@ -596,12 +634,12 @@
         }
 
         if ((srcBuf = (unsigned char *)malloc(h * w * 4)) == NULL)
-          _throw("Memory allocation failure");
+          THROW("Memory allocation failure");
         if (!alloc || doYUV) {
           if (doYUV) dstSize = tjBufSizeYUV2(h, pad, w, subsamp);
           else dstSize = tjBufSize(h, w, subsamp);
           if ((dstBuf = (unsigned char *)tjAlloc(dstSize)) == NULL)
-            _throw("Memory allocation failure");
+            THROW("Memory allocation failure");
         }
 
         for (i = 0; i < h * w * 4; i++) {
@@ -610,12 +648,12 @@
         }
 
         if (doYUV) {
-          _tj(tjEncodeYUV3(handle, srcBuf, h, 0, w, TJPF_BGRX, dstBuf, pad,
-                           subsamp, 0));
+          TRY_TJ(tjEncodeYUV3(handle, srcBuf, h, 0, w, TJPF_BGRX, dstBuf, pad,
+                              subsamp, 0));
         } else {
-          _tj(tjCompress2(handle, srcBuf, h, 0, w, TJPF_BGRX, &dstBuf,
-                          &dstSize, subsamp, 100,
-                          alloc ? 0 : TJFLAG_NOREALLOC));
+          TRY_TJ(tjCompress2(handle, srcBuf, h, 0, w, TJPF_BGRX, &dstBuf,
+                             &dstSize, subsamp, 100,
+                             alloc ? 0 : TJFLAG_NOREALLOC));
         }
         free(srcBuf);  srcBuf = NULL;
         if (!alloc || doYUV) {
@@ -633,8 +671,8 @@
 }
 
 
-void initBitmap(unsigned char *buf, int width, int pitch, int height, int pf,
-                int flags)
+static void initBitmap(unsigned char *buf, int width, int pitch, int height,
+                       int pf, int flags)
 {
   int roffset = tjRedOffset[pf];
   int goffset = tjGreenOffset[pf];
@@ -667,8 +705,8 @@
 }
 
 
-int cmpBitmap(unsigned char *buf, int width, int pitch, int height, int pf,
-              int flags, int gray2rgb)
+static int cmpBitmap(unsigned char *buf, int width, int pitch, int height,
+                     int pf, int flags, int gray2rgb)
 {
   int roffset = tjRedOffset[pf];
   int goffset = tjGreenOffset[pf];
@@ -718,8 +756,8 @@
 }
 
 
-int doBmpTest(const char *ext, int width, int align, int height, int pf,
-              int flags)
+static int doBmpTest(const char *ext, int width, int align, int height, int pf,
+                     int flags)
 {
   char filename[80], *md5sum, md5buf[65];
   int ps = tjPixelSize[pf], pitch = PAD(width * ps, align), loadWidth = 0,
@@ -736,20 +774,20 @@
   }
 
   if ((buf = (unsigned char *)tjAlloc(pitch * height)) == NULL)
-    _throw("Could not allocate memory");
+    THROW("Could not allocate memory");
   initBitmap(buf, width, pitch, height, pf, flags);
 
   snprintf(filename, 80, "test_bmp_%s_%d_%s.%s", pixFormatStr[pf], align,
            (flags & TJFLAG_BOTTOMUP) ? "bu" : "td", ext);
-  _tj(tjSaveImage(filename, buf, width, pitch, height, pf, flags));
+  TRY_TJ(tjSaveImage(filename, buf, width, pitch, height, pf, flags));
   md5sum = MD5File(filename, md5buf);
   if (strcasecmp(md5sum, md5ref))
-    _throwmd5(filename, md5sum, md5ref);
+    THROW_MD5(filename, md5sum, md5ref);
 
   tjFree(buf);  buf = NULL;
   if ((buf = tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf,
                          flags)) == NULL)
-    _throwtj();
+    THROW_TJ();
   if (width != loadWidth || height != loadHeight) {
     printf("\n   Image dimensions of %s are bogus\n", filename);
     retval = -1;  goto bailout;
@@ -763,7 +801,7 @@
     pf = TJPF_XBGR;
     if ((buf = tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf,
                            flags)) == NULL)
-      _throwtj();
+      THROW_TJ();
     pitch = PAD(width * tjPixelSize[pf], align);
     if (!cmpBitmap(buf, width, pitch, height, pf, flags, 1)) {
       printf("\n   Converting %s to RGB failed\n", filename);
@@ -774,7 +812,7 @@
     pf = TJPF_CMYK;
     if ((buf = tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf,
                            flags)) == NULL)
-      _throwtj();
+      THROW_TJ();
     pitch = PAD(width * tjPixelSize[pf], align);
     if (!cmpBitmap(buf, width, pitch, height, pf, flags, 1)) {
       printf("\n   Converting %s to CMYK failed\n", filename);
@@ -788,7 +826,7 @@
   pixelFormat = TJPF_UNKNOWN;
   if ((buf = tjLoadImage(filename, &loadWidth, align, &loadHeight,
                          &pixelFormat, flags)) == NULL)
-    _throwtj();
+    THROW_TJ();
   if ((pf == TJPF_GRAY && pixelFormat != TJPF_GRAY) ||
       (pf != TJPF_GRAY && !strcasecmp(ext, "bmp") &&
        pixelFormat != TJPF_BGR) ||
@@ -807,7 +845,7 @@
 }
 
 
-int bmpTest(void)
+static int bmpTest(void)
 {
   int align, width = 35, height = 39, format;
 
@@ -863,6 +901,7 @@
   }
   if (alloc) printf("Testing automatic buffer allocation\n");
   if (doYUV) num4bf = 4;
+  overflowTest();
   doTest(35, 39, _3byteFormats, 2, TJSAMP_444, "test");
   doTest(39, 41, _4byteFormats, num4bf, TJSAMP_444, "test");
   doTest(41, 35, _3byteFormats, 2, TJSAMP_422, "test");
diff --git a/tjutil.c b/tjutil.c
index b44086d..2018160 100644
--- a/tjutil.c
+++ b/tjutil.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2011 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2011, 2019 D. R. Commander.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -29,6 +29,7 @@
 #ifdef _WIN32
 
 #include <windows.h>
+#include "tjutil.h"
 
 static double getFreq(void)
 {
@@ -56,6 +57,7 @@
 
 #include <stdlib.h>
 #include <sys/time.h>
+#include "tjutil.h"
 
 double getTime(void)
 {
diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c
index d0a0935..9992479 100644
--- a/turbojpeg-jni.c
+++ b/turbojpeg-jni.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C)2011-2018 D. R. Commander.  All Rights Reserved.
+ * Copyright (C)2011-2019 D. R. Commander.  All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -35,59 +35,58 @@
 #include <jni.h>
 #include "java/org_libjpegturbo_turbojpeg_TJCompressor.h"
 #include "java/org_libjpegturbo_turbojpeg_TJDecompressor.h"
+#include "java/org_libjpegturbo_turbojpeg_TJTransformer.h"
 #include "java/org_libjpegturbo_turbojpeg_TJ.h"
 
-#define PAD(v, p)  ((v + (p) - 1) & (~((p) - 1)))
-
-#define bailif0(f) { \
+#define BAILIF0(f) { \
   if (!(f) || (*env)->ExceptionCheck(env)) { \
     goto bailout; \
   } \
 }
 
-#define _throw(msg, exceptionClass) { \
+#define THROW(msg, exceptionClass) { \
   jclass _exccls = (*env)->FindClass(env, exceptionClass); \
   \
-  bailif0(_exccls); \
+  BAILIF0(_exccls); \
   (*env)->ThrowNew(env, _exccls, msg); \
   goto bailout; \
 }
 
-#define _throwtj() { \
+#define THROW_TJ() { \
   jclass _exccls; \
   jmethodID _excid; \
   jobject _excobj; \
   jstring _errstr; \
   \
-  bailif0(_errstr = (*env)->NewStringUTF(env, tjGetErrorStr2(handle))); \
-  bailif0(_exccls = (*env)->FindClass(env, \
+  BAILIF0(_errstr = (*env)->NewStringUTF(env, tjGetErrorStr2(handle))); \
+  BAILIF0(_exccls = (*env)->FindClass(env, \
     "org/libjpegturbo/turbojpeg/TJException")); \
-  bailif0(_excid = (*env)->GetMethodID(env, _exccls, "<init>", \
+  BAILIF0(_excid = (*env)->GetMethodID(env, _exccls, "<init>", \
                                        "(Ljava/lang/String;I)V")); \
-  bailif0(_excobj = (*env)->NewObject(env, _exccls, _excid, _errstr, \
+  BAILIF0(_excobj = (*env)->NewObject(env, _exccls, _excid, _errstr, \
                                       tjGetErrorCode(handle))); \
   (*env)->Throw(env, _excobj); \
   goto bailout; \
 }
 
-#define _throwarg(msg)  _throw(msg, "java/lang/IllegalArgumentException")
+#define THROW_ARG(msg)  THROW(msg, "java/lang/IllegalArgumentException")
 
-#define _throwmem() \
-  _throw("Memory allocation failure", "java/lang/OutOfMemoryError");
+#define THROW_MEM() \
+  THROW("Memory allocation failure", "java/lang/OutOfMemoryError");
 
-#define gethandle() \
+#define GET_HANDLE() \
   jclass _cls = (*env)->GetObjectClass(env, obj); \
   jfieldID _fid; \
   \
-  bailif0(_cls); \
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "handle", "J")); \
+  BAILIF0(_cls); \
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "handle", "J")); \
   handle = (tjhandle)(size_t)(*env)->GetLongField(env, obj, _fid);
 
 #ifdef _WIN32
 #define setenv(envvar, value, dummy)  _putenv_s(envvar, value)
 #endif
 
-#define prop2env(property, envvar) { \
+#define PROP2ENV(property, envvar) { \
   if ((jName = (*env)->NewStringUTF(env, property)) != NULL && \
       (jValue = (*env)->CallStaticObjectMethod(env, cls, mid, \
                                                jName)) != NULL) { \
@@ -98,21 +97,27 @@
   } \
 }
 
-int ProcessSystemProperties(JNIEnv *env)
+#define SAFE_RELEASE(javaArray, cArray) { \
+  if (javaArray && cArray) \
+    (*env)->ReleasePrimitiveArrayCritical(env, javaArray, (void *)cArray, 0); \
+  cArray = NULL; \
+}
+
+static int ProcessSystemProperties(JNIEnv *env)
 {
   jclass cls;
   jmethodID mid;
   jstring jName, jValue;
   const char *value;
 
-  bailif0(cls = (*env)->FindClass(env, "java/lang/System"));
-  bailif0(mid = (*env)->GetStaticMethodID(env, cls, "getProperty",
+  BAILIF0(cls = (*env)->FindClass(env, "java/lang/System"));
+  BAILIF0(mid = (*env)->GetStaticMethodID(env, cls, "getProperty",
     "(Ljava/lang/String;)Ljava/lang/String;"));
 
-  prop2env("turbojpeg.optimize", "TJ_OPTIMIZE");
-  prop2env("turbojpeg.arithmetic", "TJ_ARITHMETIC");
-  prop2env("turbojpeg.restart", "TJ_RESTART");
-  prop2env("turbojpeg.progressive", "TJ_PROGRESSIVE");
+  PROP2ENV("turbojpeg.optimize", "TJ_OPTIMIZE");
+  PROP2ENV("turbojpeg.arithmetic", "TJ_ARITHMETIC");
+  PROP2ENV("turbojpeg.restart", "TJ_RESTART");
+  PROP2ENV("turbojpeg.progressive", "TJ_PROGRESSIVE");
   return 0;
 
 bailout:
@@ -125,7 +130,7 @@
 {
   jint retval = (jint)tjBufSize(width, height, jpegSubsamp);
 
-  if (retval == -1) _throwarg(tjGetErrorStr());
+  if (retval == -1) THROW_ARG(tjGetErrorStr());
 
 bailout:
   return retval;
@@ -137,7 +142,7 @@
 {
   jint retval = (jint)tjBufSizeYUV2(width, pad, height, subsamp);
 
-  if (retval == -1) _throwarg(tjGetErrorStr());
+  if (retval == -1) THROW_ARG(tjGetErrorStr());
 
 bailout:
   return retval;
@@ -160,7 +165,7 @@
   jint retval = (jint)tjPlaneSizeYUV(componentID, width, stride, height,
                                      subsamp);
 
-  if (retval == -1) _throwarg(tjGetErrorStr());
+  if (retval == -1) THROW_ARG(tjGetErrorStr());
 
 bailout:
   return retval;
@@ -172,7 +177,7 @@
 {
   jint retval = (jint)tjPlaneWidth(componentID, width, subsamp);
 
-  if (retval == -1) _throwarg(tjGetErrorStr());
+  if (retval == -1) THROW_ARG(tjGetErrorStr());
 
 bailout:
   return retval;
@@ -184,7 +189,7 @@
 {
   jint retval = (jint)tjPlaneHeight(componentID, height, subsamp);
 
-  if (retval == -1) _throwarg(tjGetErrorStr());
+  if (retval == -1) THROW_ARG(tjGetErrorStr());
 
 bailout:
   return retval;
@@ -199,10 +204,10 @@
   tjhandle handle;
 
   if ((handle = tjInitCompress()) == NULL)
-    _throw(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException");
+    THROW(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException");
 
-  bailif0(cls = (*env)->GetObjectClass(env, obj));
-  bailif0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
+  BAILIF0(cls = (*env)->GetObjectClass(env, obj));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
   (*env)->SetLongField(env, obj, fid, (size_t)handle);
 
 bailout:
@@ -219,35 +224,38 @@
   jsize arraySize = 0, actualPitch;
   unsigned char *srcBuf = NULL, *jpegBuf = NULL;
 
-  gethandle();
+  GET_HANDLE();
 
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
       height < 1 || pitch < 0)
-    _throwarg("Invalid argument in compress()");
+    THROW_ARG("Invalid argument in compress()");
   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
-    _throwarg("Mismatch between Java and C API");
+    THROW_ARG("Mismatch between Java and C API");
 
   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
   if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
-    _throwarg("Source buffer is not large enough");
+    THROW_ARG("Source buffer is not large enough");
   jpegSize = tjBufSize(width, height, jpegSubsamp);
   if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize)
-    _throwarg("Destination buffer is not large enough");
+    THROW_ARG("Destination buffer is not large enough");
 
   if (ProcessSystemProperties(env) < 0) goto bailout;
 
-  bailif0(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
-  bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
+  BAILIF0(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
+  BAILIF0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
 
   if (tjCompress2(handle, &srcBuf[y * actualPitch + x * tjPixelSize[pf]],
                   width, pitch, height, pf, &jpegBuf, &jpegSize, jpegSubsamp,
-                  jpegQual, flags | TJFLAG_NOREALLOC) == -1)
-    _throwtj();
+                  jpegQual, flags | TJFLAG_NOREALLOC) == -1) {
+    SAFE_RELEASE(dst, jpegBuf);
+    SAFE_RELEASE(src, srcBuf);
+    THROW_TJ();
+  }
 
 bailout:
-  if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, jpegBuf, 0);
-  if (srcBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, srcBuf, 0);
+  SAFE_RELEASE(dst, jpegBuf);
+  SAFE_RELEASE(src, srcBuf);
   return (jint)jpegSize;
 }
 
@@ -278,9 +286,9 @@
    jint jpegQual, jint flags)
 {
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
-    _throwarg("Invalid argument in compress()");
+    THROW_ARG("Invalid argument in compress()");
   if (tjPixelSize[pf] != sizeof(jint))
-    _throwarg("Pixel format must be 32-bit when compressing from an integer buffer.");
+    THROW_ARG("Pixel format must be 32-bit when compressing from an integer buffer.");
 
   return TJCompressor_compress(env, obj, src, sizeof(jint), x, y, width,
                                stride * sizeof(jint), height, pf, dst,
@@ -297,9 +305,9 @@
    jint flags)
 {
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
-    _throwarg("Invalid argument in compress()");
+    THROW_ARG("Invalid argument in compress()");
   if (tjPixelSize[pf] != sizeof(jint))
-    _throwarg("Pixel format must be 32-bit when compressing from an integer buffer.");
+    THROW_ARG("Pixel format must be 32-bit when compressing from an integer buffer.");
 
   return TJCompressor_compress(env, obj, src, sizeof(jint), 0, 0, width,
                                stride * sizeof(jint), height, pf, dst,
@@ -323,66 +331,76 @@
   int *srcOffsets = NULL, *srcStrides = NULL;
   int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i;
 
-  gethandle();
+  GET_HANDLE();
 
   if (subsamp < 0 || subsamp >= org_libjpegturbo_turbojpeg_TJ_NUMSAMP)
-    _throwarg("Invalid argument in compressFromYUV()");
+    THROW_ARG("Invalid argument in compressFromYUV()");
   if (org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
-    _throwarg("Mismatch between Java and C API");
+    THROW_ARG("Mismatch between Java and C API");
 
   if ((*env)->GetArrayLength(env, srcobjs) < nc)
-    _throwarg("Planes array is too small for the subsampling type");
+    THROW_ARG("Planes array is too small for the subsampling type");
   if ((*env)->GetArrayLength(env, jSrcOffsets) < nc)
-    _throwarg("Offsets array is too small for the subsampling type");
+    THROW_ARG("Offsets array is too small for the subsampling type");
   if ((*env)->GetArrayLength(env, jSrcStrides) < nc)
-    _throwarg("Strides array is too small for the subsampling type");
+    THROW_ARG("Strides array is too small for the subsampling type");
 
   jpegSize = tjBufSize(width, height, subsamp);
   if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize)
-    _throwarg("Destination buffer is not large enough");
+    THROW_ARG("Destination buffer is not large enough");
 
   if (ProcessSystemProperties(env) < 0) goto bailout;
 
-  bailif0(srcOffsets = (*env)->GetPrimitiveArrayCritical(env, jSrcOffsets, 0));
-  bailif0(srcStrides = (*env)->GetPrimitiveArrayCritical(env, jSrcStrides, 0));
+#define RELEASE_ARRAYS_COMPRESSFROMYUV() { \
+  SAFE_RELEASE(dst, jpegBuf); \
+  for (i = 0; i < nc; i++) \
+    SAFE_RELEASE(jSrcPlanes[i], srcPlanes[i]); \
+  SAFE_RELEASE(jSrcStrides, srcStrides); \
+  SAFE_RELEASE(jSrcOffsets, srcOffsets); \
+}
+
+  BAILIF0(srcOffsets = (*env)->GetPrimitiveArrayCritical(env, jSrcOffsets, 0));
+  BAILIF0(srcStrides = (*env)->GetPrimitiveArrayCritical(env, jSrcStrides, 0));
   for (i = 0; i < nc; i++) {
     int planeSize = tjPlaneSizeYUV(i, width, srcStrides[i], height, subsamp);
     int pw = tjPlaneWidth(i, width, subsamp);
 
-    if (planeSize < 0 || pw < 0)
-      _throwarg(tjGetErrorStr());
+    if (planeSize < 0 || pw < 0) {
+      RELEASE_ARRAYS_COMPRESSFROMYUV();
+      THROW_ARG(tjGetErrorStr());
+    }
 
-    if (srcOffsets[i] < 0)
-      _throwarg("Invalid argument in compressFromYUV()");
-    if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0)
-      _throwarg("Negative plane stride would cause memory to be accessed below plane boundary");
+    if (srcOffsets[i] < 0) {
+      RELEASE_ARRAYS_COMPRESSFROMYUV();
+      THROW_ARG("Invalid argument in compressFromYUV()");
+    }
+    if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0) {
+      RELEASE_ARRAYS_COMPRESSFROMYUV();
+      THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
+    }
 
-    bailif0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
-    if ((*env)->GetArrayLength(env, jSrcPlanes[i]) < srcOffsets[i] + planeSize)
-      _throwarg("Source plane is not large enough");
+    BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
+    if ((*env)->GetArrayLength(env, jSrcPlanes[i]) <
+        srcOffsets[i] + planeSize) {
+      RELEASE_ARRAYS_COMPRESSFROMYUV();
+      THROW_ARG("Source plane is not large enough");
+    }
 
-    bailif0(srcPlanes[i] =
+    BAILIF0(srcPlanes[i] =
             (*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0));
     srcPlanes[i] = &srcPlanes[i][srcOffsets[i]];
   }
-  bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
+  BAILIF0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
 
   if (tjCompressFromYUVPlanes(handle, srcPlanes, width, srcStrides, height,
                               subsamp, &jpegBuf, &jpegSize, jpegQual,
-                              flags | TJFLAG_NOREALLOC) == -1)
-    _throwtj();
+                              flags | TJFLAG_NOREALLOC) == -1) {
+    RELEASE_ARRAYS_COMPRESSFROMYUV();
+    THROW_TJ();
+  }
 
 bailout:
-  if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, jpegBuf, 0);
-  for (i = 0; i < nc; i++) {
-    if (srcPlanes[i] && jSrcPlanes[i])
-      (*env)->ReleasePrimitiveArrayCritical(env, jSrcPlanes[i],
-                                            (unsigned char *)srcPlanes[i], 0);
-  }
-  if (srcStrides)
-    (*env)->ReleasePrimitiveArrayCritical(env, jSrcStrides, srcStrides, 0);
-  if (srcOffsets)
-    (*env)->ReleasePrimitiveArrayCritical(env, jSrcOffsets, srcOffsets, 0);
+  RELEASE_ARRAYS_COMPRESSFROMYUV();
   return (jint)jpegSize;
 }
 
@@ -398,68 +416,78 @@
   int *dstOffsets = NULL, *dstStrides = NULL;
   int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i;
 
-  gethandle();
+  GET_HANDLE();
 
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
       height < 1 || pitch < 0 || subsamp < 0 ||
       subsamp >= org_libjpegturbo_turbojpeg_TJ_NUMSAMP)
-    _throwarg("Invalid argument in encodeYUV()");
+    THROW_ARG("Invalid argument in encodeYUV()");
   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF ||
       org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
-    _throwarg("Mismatch between Java and C API");
+    THROW_ARG("Mismatch between Java and C API");
 
   if ((*env)->GetArrayLength(env, dstobjs) < nc)
-    _throwarg("Planes array is too small for the subsampling type");
+    THROW_ARG("Planes array is too small for the subsampling type");
   if ((*env)->GetArrayLength(env, jDstOffsets) < nc)
-    _throwarg("Offsets array is too small for the subsampling type");
+    THROW_ARG("Offsets array is too small for the subsampling type");
   if ((*env)->GetArrayLength(env, jDstStrides) < nc)
-    _throwarg("Strides array is too small for the subsampling type");
+    THROW_ARG("Strides array is too small for the subsampling type");
 
   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
   if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
-    _throwarg("Source buffer is not large enough");
+    THROW_ARG("Source buffer is not large enough");
 
-  bailif0(dstOffsets = (*env)->GetPrimitiveArrayCritical(env, jDstOffsets, 0));
-  bailif0(dstStrides = (*env)->GetPrimitiveArrayCritical(env, jDstStrides, 0));
+#define RELEASE_ARRAYS_ENCODEYUV() { \
+  SAFE_RELEASE(src, srcBuf); \
+  for (i = 0; i < nc; i++) \
+    SAFE_RELEASE(jDstPlanes[i], dstPlanes[i]); \
+  SAFE_RELEASE(jDstStrides, dstStrides); \
+  SAFE_RELEASE(jDstOffsets, dstOffsets); \
+}
+
+  BAILIF0(dstOffsets = (*env)->GetPrimitiveArrayCritical(env, jDstOffsets, 0));
+  BAILIF0(dstStrides = (*env)->GetPrimitiveArrayCritical(env, jDstStrides, 0));
   for (i = 0; i < nc; i++) {
     int planeSize = tjPlaneSizeYUV(i, width, dstStrides[i], height, subsamp);
     int pw = tjPlaneWidth(i, width, subsamp);
 
-    if (planeSize < 0 || pw < 0)
-      _throwarg(tjGetErrorStr());
+    if (planeSize < 0 || pw < 0) {
+      RELEASE_ARRAYS_ENCODEYUV();
+      THROW_ARG(tjGetErrorStr());
+    }
 
-    if (dstOffsets[i] < 0)
-      _throwarg("Invalid argument in encodeYUV()");
-    if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0)
-      _throwarg("Negative plane stride would cause memory to be accessed below plane boundary");
+    if (dstOffsets[i] < 0) {
+      RELEASE_ARRAYS_ENCODEYUV();
+      THROW_ARG("Invalid argument in encodeYUV()");
+    }
+    if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0) {
+      RELEASE_ARRAYS_ENCODEYUV();
+      THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
+    }
 
-    bailif0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
-    if ((*env)->GetArrayLength(env, jDstPlanes[i]) < dstOffsets[i] + planeSize)
-      _throwarg("Destination plane is not large enough");
+    BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
+    if ((*env)->GetArrayLength(env, jDstPlanes[i]) <
+        dstOffsets[i] + planeSize) {
+      RELEASE_ARRAYS_ENCODEYUV();
+      THROW_ARG("Destination plane is not large enough");
+    }
 
-    bailif0(dstPlanes[i] =
+    BAILIF0(dstPlanes[i] =
             (*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0));
     dstPlanes[i] = &dstPlanes[i][dstOffsets[i]];
   }
-  bailif0(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
+  BAILIF0(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
 
   if (tjEncodeYUVPlanes(handle, &srcBuf[y * actualPitch + x * tjPixelSize[pf]],
                         width, pitch, height, pf, dstPlanes, dstStrides,
-                        subsamp, flags) == -1)
-    _throwtj();
+                        subsamp, flags) == -1) {
+    RELEASE_ARRAYS_ENCODEYUV();
+    THROW_TJ();
+  }
 
 bailout:
-  if (srcBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, srcBuf, 0);
-  for (i = 0; i < nc; i++) {
-    if (dstPlanes[i] && jDstPlanes[i])
-      (*env)->ReleasePrimitiveArrayCritical(env, jDstPlanes[i], dstPlanes[i],
-                                            0);
-  }
-  if (dstStrides)
-    (*env)->ReleasePrimitiveArrayCritical(env, jDstStrides, dstStrides, 0);
-  if (dstOffsets)
-    (*env)->ReleasePrimitiveArrayCritical(env, jDstOffsets, dstOffsets, 0);
+  RELEASE_ARRAYS_ENCODEYUV();
 }
 
 /* TurboJPEG 1.4.x: TJCompressor::encodeYUV() byte source */
@@ -479,9 +507,9 @@
    jintArray jDstOffsets, jintArray jDstStrides, jint subsamp, jint flags)
 {
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
-    _throwarg("Invalid argument in encodeYUV()");
+    THROW_ARG("Invalid argument in encodeYUV()");
   if (tjPixelSize[pf] != sizeof(jint))
-    _throwarg("Pixel format must be 32-bit when encoding from an integer buffer.");
+    THROW_ARG("Pixel format must be 32-bit when encoding from an integer buffer.");
 
   TJCompressor_encodeYUV(env, obj, src, sizeof(jint), x, y, width,
                          stride * sizeof(jint), height, pf, dstobjs,
@@ -491,7 +519,7 @@
   return;
 }
 
-JNIEXPORT void JNICALL TJCompressor_encodeYUV_12
+static void JNICALL TJCompressor_encodeYUV_12
   (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint width,
    jint pitch, jint height, jint pf, jbyteArray dst, jint subsamp, jint flags)
 {
@@ -499,31 +527,34 @@
   jsize arraySize = 0;
   unsigned char *srcBuf = NULL, *dstBuf = NULL;
 
-  gethandle();
+  GET_HANDLE();
 
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
       height < 1 || pitch < 0)
-    _throwarg("Invalid argument in encodeYUV()");
+    THROW_ARG("Invalid argument in encodeYUV()");
   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
-    _throwarg("Mismatch between Java and C API");
+    THROW_ARG("Mismatch between Java and C API");
 
   arraySize = (pitch == 0) ? width * tjPixelSize[pf] * height : pitch * height;
   if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
-    _throwarg("Source buffer is not large enough");
+    THROW_ARG("Source buffer is not large enough");
   if ((*env)->GetArrayLength(env, dst) <
       (jsize)tjBufSizeYUV(width, height, subsamp))
-    _throwarg("Destination buffer is not large enough");
+    THROW_ARG("Destination buffer is not large enough");
 
-  bailif0(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
-  bailif0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
+  BAILIF0(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
+  BAILIF0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
 
   if (tjEncodeYUV2(handle, srcBuf, width, pitch, height, pf, dstBuf, subsamp,
-                   flags) == -1)
-    _throwtj();
+                   flags) == -1) {
+    SAFE_RELEASE(dst, dstBuf);
+    SAFE_RELEASE(src, srcBuf);
+    THROW_TJ();
+  }
 
 bailout:
-  if (dstBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, dstBuf, 0);
-  if (srcBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, srcBuf, 0);
+  SAFE_RELEASE(dst, dstBuf);
+  SAFE_RELEASE(src, srcBuf);
 }
 
 /* TurboJPEG 1.2.x: TJCompressor::encodeYUV() byte source */
@@ -541,9 +572,9 @@
    jint height, jint pf, jbyteArray dst, jint subsamp, jint flags)
 {
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
-    _throwarg("Invalid argument in encodeYUV()");
+    THROW_ARG("Invalid argument in encodeYUV()");
   if (tjPixelSize[pf] != sizeof(jint))
-    _throwarg("Pixel format must be 32-bit when encoding from an integer buffer.");
+    THROW_ARG("Pixel format must be 32-bit when encoding from an integer buffer.");
 
   TJCompressor_encodeYUV_12(env, obj, src, sizeof(jint), width,
                             stride * sizeof(jint), height, pf, dst, subsamp,
@@ -559,9 +590,9 @@
 {
   tjhandle handle = 0;
 
-  gethandle();
+  GET_HANDLE();
 
-  if (tjDestroy(handle) == -1) _throwtj();
+  if (tjDestroy(handle) == -1) THROW_TJ();
   (*env)->SetLongField(env, obj, _fid, 0);
 
 bailout:
@@ -577,10 +608,10 @@
   tjhandle handle;
 
   if ((handle = tjInitDecompress()) == NULL)
-    _throw(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException");
+    THROW(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException");
 
-  bailif0(cls = (*env)->GetObjectClass(env, obj));
-  bailif0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
+  BAILIF0(cls = (*env)->GetObjectClass(env, obj));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
   (*env)->SetLongField(env, obj, fid, (size_t)handle);
 
 bailout:
@@ -599,17 +630,17 @@
   jobjectArray sfjava = NULL;
 
   if ((sf = tjGetScalingFactors(&n)) == NULL || n == 0)
-    _throwarg(tjGetErrorStr());
+    THROW_ARG(tjGetErrorStr());
 
-  bailif0(sfcls = (*env)->FindClass(env,
+  BAILIF0(sfcls = (*env)->FindClass(env,
     "org/libjpegturbo/turbojpeg/TJScalingFactor"));
-  bailif0(sfjava = (jobjectArray)(*env)->NewObjectArray(env, n, sfcls, 0));
+  BAILIF0(sfjava = (jobjectArray)(*env)->NewObjectArray(env, n, sfcls, 0));
 
   for (i = 0; i < n; i++) {
-    bailif0(sfobj = (*env)->AllocObject(env, sfcls));
-    bailif0(fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
+    BAILIF0(sfobj = (*env)->AllocObject(env, sfcls));
+    BAILIF0(fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
     (*env)->SetIntField(env, sfobj, fid, sf[i].num);
-    bailif0(fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
+    BAILIF0(fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
     (*env)->SetIntField(env, sfobj, fid, sf[i].denom);
     (*env)->SetObjectArrayElement(env, sfjava, i, sfobj);
   }
@@ -626,33 +657,34 @@
   unsigned char *jpegBuf = NULL;
   int width = 0, height = 0, jpegSubsamp = -1, jpegColorspace = -1;
 
-  gethandle();
+  GET_HANDLE();
 
   if ((*env)->GetArrayLength(env, src) < jpegSize)
-    _throwarg("Source buffer is not large enough");
+    THROW_ARG("Source buffer is not large enough");
 
-  bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
+  BAILIF0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
 
   if (tjDecompressHeader3(handle, jpegBuf, (unsigned long)jpegSize, &width,
-                          &height, &jpegSubsamp, &jpegColorspace) == -1)
-    _throwtj();
+                          &height, &jpegSubsamp, &jpegColorspace) == -1) {
+    SAFE_RELEASE(src, jpegBuf);
+    THROW_TJ();
+  }
 
-  (*env)->ReleasePrimitiveArrayCritical(env, src, jpegBuf, 0);
-  jpegBuf = NULL;
+  SAFE_RELEASE(src, jpegBuf);
 
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
   (*env)->SetIntField(env, obj, _fid, jpegSubsamp);
   if ((_fid = (*env)->GetFieldID(env, _cls, "jpegColorspace", "I")) == 0)
     (*env)->ExceptionClear(env);
   else
     (*env)->SetIntField(env, obj, _fid, jpegColorspace);
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
   (*env)->SetIntField(env, obj, _fid, width);
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
   (*env)->SetIntField(env, obj, _fid, height);
 
 bailout:
-  if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, jpegBuf, 0);
+  SAFE_RELEASE(src, jpegBuf);
 }
 
 static void TJDecompressor_decompress
@@ -664,31 +696,34 @@
   jsize arraySize = 0, actualPitch;
   unsigned char *jpegBuf = NULL, *dstBuf = NULL;
 
-  gethandle();
+  GET_HANDLE();
 
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
-    _throwarg("Invalid argument in decompress()");
+    THROW_ARG("Invalid argument in decompress()");
   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
-    _throwarg("Mismatch between Java and C API");
+    THROW_ARG("Mismatch between Java and C API");
 
   if ((*env)->GetArrayLength(env, src) < jpegSize)
-    _throwarg("Source buffer is not large enough");
+    THROW_ARG("Source buffer is not large enough");
   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
   if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize)
-    _throwarg("Destination buffer is not large enough");
+    THROW_ARG("Destination buffer is not large enough");
 
-  bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
-  bailif0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
+  BAILIF0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
+  BAILIF0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
 
   if (tjDecompress2(handle, jpegBuf, (unsigned long)jpegSize,
                     &dstBuf[y * actualPitch + x * tjPixelSize[pf]], width,
-                    pitch, height, pf, flags) == -1)
-    _throwtj();
+                    pitch, height, pf, flags) == -1) {
+    SAFE_RELEASE(dst, dstBuf);
+    SAFE_RELEASE(src, jpegBuf);
+    THROW_TJ();
+  }
 
 bailout:
-  if (dstBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, dstBuf, 0);
-  if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, jpegBuf, 0);
+  SAFE_RELEASE(dst, dstBuf);
+  SAFE_RELEASE(src, jpegBuf);
 }
 
 /* TurboJPEG 1.3.x: TJDecompressor::decompress() byte destination */
@@ -715,9 +750,9 @@
    jint x, jint y, jint width, jint stride, jint height, jint pf, jint flags)
 {
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
-    _throwarg("Invalid argument in decompress()");
+    THROW_ARG("Invalid argument in decompress()");
   if (tjPixelSize[pf] != sizeof(jint))
-    _throwarg("Pixel format must be 32-bit when decompressing to an integer buffer.");
+    THROW_ARG("Pixel format must be 32-bit when decompressing to an integer buffer.");
 
   TJDecompressor_decompress(env, obj, src, jpegSize, dst, sizeof(jint), x, y,
                             width, stride * sizeof(jint), height, pf, flags);
@@ -732,9 +767,9 @@
    jint width, jint stride, jint height, jint pf, jint flags)
 {
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
-    _throwarg("Invalid argument in decompress()");
+    THROW_ARG("Invalid argument in decompress()");
   if (tjPixelSize[pf] != sizeof(jint))
-    _throwarg("Pixel format must be 32-bit when decompressing to an integer buffer.");
+    THROW_ARG("Pixel format must be 32-bit when decompressing to an integer buffer.");
 
   TJDecompressor_decompress(env, obj, src, jpegSize, dst, sizeof(jint), 0, 0,
                             width, stride * sizeof(jint), height, pf, flags);
@@ -757,15 +792,15 @@
   int nc = 0, i, width, height, scaledWidth, scaledHeight, nsf = 0;
   tjscalingfactor *sf;
 
-  gethandle();
+  GET_HANDLE();
 
   if ((*env)->GetArrayLength(env, src) < jpegSize)
-    _throwarg("Source buffer is not large enough");
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
+    THROW_ARG("Source buffer is not large enough");
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
   jpegSubsamp = (int)(*env)->GetIntField(env, obj, _fid);
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
   jpegWidth = (int)(*env)->GetIntField(env, obj, _fid);
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
   jpegHeight = (int)(*env)->GetIntField(env, obj, _fid);
 
   nc = (jpegSubsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3);
@@ -776,7 +811,7 @@
   if (height == 0) height = jpegHeight;
   sf = tjGetScalingFactors(&nsf);
   if (!sf || nsf < 1)
-    _throwarg(tjGetErrorStr());
+    THROW_ARG(tjGetErrorStr());
   for (i = 0; i < nsf; i++) {
     scaledWidth = TJSCALED(jpegWidth, sf[i]);
     scaledHeight = TJSCALED(jpegHeight, sf[i]);
@@ -784,49 +819,59 @@
       break;
   }
   if (i >= nsf)
-    _throwarg("Could not scale down to desired image dimensions");
+    THROW_ARG("Could not scale down to desired image dimensions");
 
-  bailif0(dstOffsets = (*env)->GetPrimitiveArrayCritical(env, jDstOffsets, 0));
-  bailif0(dstStrides = (*env)->GetPrimitiveArrayCritical(env, jDstStrides, 0));
+#define RELEASE_ARRAYS_DECOMPRESSTOYUV() { \
+  SAFE_RELEASE(src, jpegBuf); \
+  for (i = 0; i < nc; i++) \
+    SAFE_RELEASE(jDstPlanes[i], dstPlanes[i]); \
+  SAFE_RELEASE(jDstStrides, dstStrides); \
+  SAFE_RELEASE(jDstOffsets, dstOffsets); \
+}
+
+  BAILIF0(dstOffsets = (*env)->GetPrimitiveArrayCritical(env, jDstOffsets, 0));
+  BAILIF0(dstStrides = (*env)->GetPrimitiveArrayCritical(env, jDstStrides, 0));
   for (i = 0; i < nc; i++) {
     int planeSize = tjPlaneSizeYUV(i, scaledWidth, dstStrides[i], scaledHeight,
                                    jpegSubsamp);
     int pw = tjPlaneWidth(i, scaledWidth, jpegSubsamp);
 
-    if (planeSize < 0 || pw < 0)
-      _throwarg(tjGetErrorStr());
+    if (planeSize < 0 || pw < 0) {
+      RELEASE_ARRAYS_DECOMPRESSTOYUV();
+      THROW_ARG(tjGetErrorStr());
+    }
 
-    if (dstOffsets[i] < 0)
-      _throwarg("Invalid argument in decompressToYUV()");
-    if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0)
-      _throwarg("Negative plane stride would cause memory to be accessed below plane boundary");
+    if (dstOffsets[i] < 0) {
+      RELEASE_ARRAYS_DECOMPRESSTOYUV();
+      THROW_ARG("Invalid argument in decompressToYUV()");
+    }
+    if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0) {
+      RELEASE_ARRAYS_DECOMPRESSTOYUV();
+      THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
+    }
 
-    bailif0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
-    if ((*env)->GetArrayLength(env, jDstPlanes[i]) < dstOffsets[i] + planeSize)
-      _throwarg("Destination plane is not large enough");
+    BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
+    if ((*env)->GetArrayLength(env, jDstPlanes[i]) <
+        dstOffsets[i] + planeSize) {
+      RELEASE_ARRAYS_DECOMPRESSTOYUV();
+      THROW_ARG("Destination plane is not large enough");
+    }
 
-    bailif0(dstPlanes[i] =
+    BAILIF0(dstPlanes[i] =
             (*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0));
     dstPlanes[i] = &dstPlanes[i][dstOffsets[i]];
   }
-  bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
+  BAILIF0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
 
   if (tjDecompressToYUVPlanes(handle, jpegBuf, (unsigned long)jpegSize,
                               dstPlanes, desiredWidth, dstStrides,
-                              desiredHeight, flags) == -1)
-    _throwtj();
+                              desiredHeight, flags) == -1) {
+    RELEASE_ARRAYS_DECOMPRESSTOYUV();
+    THROW_TJ();
+  }
 
 bailout:
-  if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, jpegBuf, 0);
-  for (i = 0; i < nc; i++) {
-    if (dstPlanes[i] && jDstPlanes[i])
-      (*env)->ReleasePrimitiveArrayCritical(env, jDstPlanes[i], dstPlanes[i],
-                                            0);
-  }
-  if (dstStrides)
-    (*env)->ReleasePrimitiveArrayCritical(env, jDstStrides, dstStrides, 0);
-  if (dstOffsets)
-    (*env)->ReleasePrimitiveArrayCritical(env, jDstOffsets, dstOffsets, 0);
+  RELEASE_ARRAYS_DECOMPRESSTOYUV();
 }
 
 /* TurboJPEG 1.2.x: TJDecompressor::decompressToYUV() */
@@ -838,30 +883,33 @@
   unsigned char *jpegBuf = NULL, *dstBuf = NULL;
   int jpegSubsamp = -1, jpegWidth = 0, jpegHeight = 0;
 
-  gethandle();
+  GET_HANDLE();
 
   if ((*env)->GetArrayLength(env, src) < jpegSize)
-    _throwarg("Source buffer is not large enough");
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
+    THROW_ARG("Source buffer is not large enough");
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
   jpegSubsamp = (int)(*env)->GetIntField(env, obj, _fid);
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
   jpegWidth = (int)(*env)->GetIntField(env, obj, _fid);
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
   jpegHeight = (int)(*env)->GetIntField(env, obj, _fid);
   if ((*env)->GetArrayLength(env, dst) <
       (jsize)tjBufSizeYUV(jpegWidth, jpegHeight, jpegSubsamp))
-    _throwarg("Destination buffer is not large enough");
+    THROW_ARG("Destination buffer is not large enough");
 
-  bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
-  bailif0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
+  BAILIF0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
+  BAILIF0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
 
   if (tjDecompressToYUV(handle, jpegBuf, (unsigned long)jpegSize, dstBuf,
-                        flags) == -1)
-    _throwtj();
+                        flags) == -1) {
+    SAFE_RELEASE(dst, dstBuf);
+    SAFE_RELEASE(src, jpegBuf);
+    THROW_TJ();
+  }
 
 bailout:
-  if (dstBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, dstBuf, 0);
-  if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, jpegBuf, 0);
+  SAFE_RELEASE(dst, dstBuf);
+  SAFE_RELEASE(src, jpegBuf);
 }
 
 static void TJDecompressor_decodeYUV
@@ -877,67 +925,77 @@
   int *srcOffsets = NULL, *srcStrides = NULL;
   int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i;
 
-  gethandle();
+  GET_HANDLE();
 
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || subsamp < 0 ||
       subsamp >= org_libjpegturbo_turbojpeg_TJ_NUMSAMP)
-    _throwarg("Invalid argument in decodeYUV()");
+    THROW_ARG("Invalid argument in decodeYUV()");
   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF ||
       org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
-    _throwarg("Mismatch between Java and C API");
+    THROW_ARG("Mismatch between Java and C API");
 
   if ((*env)->GetArrayLength(env, srcobjs) < nc)
-    _throwarg("Planes array is too small for the subsampling type");
+    THROW_ARG("Planes array is too small for the subsampling type");
   if ((*env)->GetArrayLength(env, jSrcOffsets) < nc)
-    _throwarg("Offsets array is too small for the subsampling type");
+    THROW_ARG("Offsets array is too small for the subsampling type");
   if ((*env)->GetArrayLength(env, jSrcStrides) < nc)
-    _throwarg("Strides array is too small for the subsampling type");
+    THROW_ARG("Strides array is too small for the subsampling type");
 
   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
   if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize)
-    _throwarg("Destination buffer is not large enough");
+    THROW_ARG("Destination buffer is not large enough");
 
-  bailif0(srcOffsets = (*env)->GetPrimitiveArrayCritical(env, jSrcOffsets, 0));
-  bailif0(srcStrides = (*env)->GetPrimitiveArrayCritical(env, jSrcStrides, 0));
+#define RELEASE_ARRAYS_DECODEYUV() { \
+  SAFE_RELEASE(dst, dstBuf); \
+  for (i = 0; i < nc; i++) \
+    SAFE_RELEASE(jSrcPlanes[i], srcPlanes[i]); \
+  SAFE_RELEASE(jSrcStrides, srcStrides); \
+  SAFE_RELEASE(jSrcOffsets, srcOffsets); \
+}
+
+  BAILIF0(srcOffsets = (*env)->GetPrimitiveArrayCritical(env, jSrcOffsets, 0));
+  BAILIF0(srcStrides = (*env)->GetPrimitiveArrayCritical(env, jSrcStrides, 0));
   for (i = 0; i < nc; i++) {
     int planeSize = tjPlaneSizeYUV(i, width, srcStrides[i], height, subsamp);
     int pw = tjPlaneWidth(i, width, subsamp);
 
-    if (planeSize < 0 || pw < 0)
-      _throwarg(tjGetErrorStr());
+    if (planeSize < 0 || pw < 0) {
+      RELEASE_ARRAYS_DECODEYUV();
+      THROW_ARG(tjGetErrorStr());
+    }
 
-    if (srcOffsets[i] < 0)
-      _throwarg("Invalid argument in decodeYUV()");
-    if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0)
-      _throwarg("Negative plane stride would cause memory to be accessed below plane boundary");
+    if (srcOffsets[i] < 0) {
+      RELEASE_ARRAYS_DECODEYUV();
+      THROW_ARG("Invalid argument in decodeYUV()");
+    }
+    if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0) {
+      RELEASE_ARRAYS_DECODEYUV();
+      THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
+    }
 
-    bailif0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
-    if ((*env)->GetArrayLength(env, jSrcPlanes[i]) < srcOffsets[i] + planeSize)
-      _throwarg("Source plane is not large enough");
+    BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
+    if ((*env)->GetArrayLength(env, jSrcPlanes[i]) <
+        srcOffsets[i] + planeSize) {
+      RELEASE_ARRAYS_DECODEYUV();
+      THROW_ARG("Source plane is not large enough");
+    }
 
-    bailif0(srcPlanes[i] =
+    BAILIF0(srcPlanes[i] =
             (*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0));
     srcPlanes[i] = &srcPlanes[i][srcOffsets[i]];
   }
-  bailif0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
+  BAILIF0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
 
   if (tjDecodeYUVPlanes(handle, srcPlanes, srcStrides, subsamp,
                         &dstBuf[y * actualPitch + x * tjPixelSize[pf]], width,
-                        pitch, height, pf, flags) == -1)
-    _throwtj();
+                        pitch, height, pf, flags) == -1) {
+    RELEASE_ARRAYS_DECODEYUV();
+    THROW_TJ();
+  }
 
 bailout:
-  if (dstBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, dstBuf, 0);
-  for (i = 0; i < nc; i++) {
-    if (srcPlanes[i] && jSrcPlanes[i])
-      (*env)->ReleasePrimitiveArrayCritical(env, jSrcPlanes[i],
-                                            (unsigned char *)srcPlanes[i], 0);
-  }
-  if (srcStrides)
-    (*env)->ReleasePrimitiveArrayCritical(env, jSrcStrides, srcStrides, 0);
-  if (srcOffsets)
-    (*env)->ReleasePrimitiveArrayCritical(env, jSrcOffsets, srcOffsets, 0);
+  RELEASE_ARRAYS_DECODEYUV();
 }
 
 /* TurboJPEG 1.4.x: TJDecompressor::decodeYUV() byte destination */
@@ -958,9 +1016,9 @@
    jint width, jint stride, jint height, jint pf, jint flags)
 {
   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
-    _throwarg("Invalid argument in decodeYUV()");
+    THROW_ARG("Invalid argument in decodeYUV()");
   if (tjPixelSize[pf] != sizeof(jint))
-    _throwarg("Pixel format must be 32-bit when decoding to an integer buffer.");
+    THROW_ARG("Pixel format must be 32-bit when decoding to an integer buffer.");
 
   TJDecompressor_decodeYUV(env, obj, srcobjs, jSrcOffsets, jSrcStrides,
                            subsamp, dst, sizeof(jint), x, y, width,
@@ -979,10 +1037,10 @@
   tjhandle handle;
 
   if ((handle = tjInitTransform()) == NULL)
-    _throw(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException");
+    THROW(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException");
 
-  bailif0(cls = (*env)->GetObjectClass(env, obj));
-  bailif0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
+  BAILIF0(cls = (*env)->GetObjectClass(env, obj));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
   (*env)->SetLongField(env, obj, fid, (size_t)handle);
 
 bailout:
@@ -1007,43 +1065,43 @@
   jmethodID mid;
   jfieldID fid;
 
-  bailif0(bufobj = (*env)->NewDirectByteBuffer(env, coeffs,
+  BAILIF0(bufobj = (*env)->NewDirectByteBuffer(env, coeffs,
     sizeof(short) * arrayRegion.w * arrayRegion.h));
-  bailif0(cls = (*env)->FindClass(env, "java/nio/ByteOrder"));
-  bailif0(mid = (*env)->GetStaticMethodID(env, cls, "nativeOrder",
+  BAILIF0(cls = (*env)->FindClass(env, "java/nio/ByteOrder"));
+  BAILIF0(mid = (*env)->GetStaticMethodID(env, cls, "nativeOrder",
                                           "()Ljava/nio/ByteOrder;"));
-  bailif0(borobj = (*env)->CallStaticObjectMethod(env, cls, mid));
-  bailif0(cls = (*env)->GetObjectClass(env, bufobj));
-  bailif0(mid = (*env)->GetMethodID(env, cls, "order",
+  BAILIF0(borobj = (*env)->CallStaticObjectMethod(env, cls, mid));
+  BAILIF0(cls = (*env)->GetObjectClass(env, bufobj));
+  BAILIF0(mid = (*env)->GetMethodID(env, cls, "order",
     "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"));
   (*env)->CallObjectMethod(env, bufobj, mid, borobj);
-  bailif0(mid = (*env)->GetMethodID(env, cls, "asShortBuffer",
+  BAILIF0(mid = (*env)->GetMethodID(env, cls, "asShortBuffer",
                                     "()Ljava/nio/ShortBuffer;"));
-  bailif0(bufobj = (*env)->CallObjectMethod(env, bufobj, mid));
+  BAILIF0(bufobj = (*env)->CallObjectMethod(env, bufobj, mid));
 
-  bailif0(cls = (*env)->FindClass(env, "java/awt/Rectangle"));
-  bailif0(arrayRegionObj = (*env)->AllocObject(env, cls));
-  bailif0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
+  BAILIF0(cls = (*env)->FindClass(env, "java/awt/Rectangle"));
+  BAILIF0(arrayRegionObj = (*env)->AllocObject(env, cls));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.x);
-  bailif0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.y);
-  bailif0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.w);
-  bailif0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.h);
 
-  bailif0(planeRegionObj = (*env)->AllocObject(env, cls));
-  bailif0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
+  BAILIF0(planeRegionObj = (*env)->AllocObject(env, cls));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.x);
-  bailif0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.y);
-  bailif0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.w);
-  bailif0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
+  BAILIF0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.h);
 
-  bailif0(cls = (*env)->GetObjectClass(env, cfobj));
-  bailif0(mid = (*env)->GetMethodID(env, cls, "customFilter",
+  BAILIF0(cls = (*env)->GetObjectClass(env, cfobj));
+  BAILIF0(mid = (*env)->GetMethodID(env, cls, "customFilter",
     "(Ljava/nio/ShortBuffer;Ljava/awt/Rectangle;Ljava/awt/Rectangle;IILorg/libjpegturbo/turbojpeg/TJTransform;)V"));
   (*env)->CallVoidMethod(env, cfobj, mid, bufobj, arrayRegionObj,
                          planeRegionObj, componentIndex, transformIndex, tobj);
@@ -1070,33 +1128,33 @@
   jint *dstSizesi = NULL;
   JNICustomFilterParams *params = NULL;
 
-  gethandle();
+  GET_HANDLE();
 
   if ((*env)->GetArrayLength(env, jsrcBuf) < jpegSize)
-    _throwarg("Source buffer is not large enough");
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
+    THROW_ARG("Source buffer is not large enough");
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
   jpegWidth = (int)(*env)->GetIntField(env, obj, _fid);
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
   jpegHeight = (int)(*env)->GetIntField(env, obj, _fid);
-  bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
+  BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
   jpegSubsamp = (int)(*env)->GetIntField(env, obj, _fid);
 
   n = (*env)->GetArrayLength(env, dstobjs);
   if (n != (*env)->GetArrayLength(env, tobjs))
-    _throwarg("Mismatch between size of transforms array and destination buffers array");
+    THROW_ARG("Mismatch between size of transforms array and destination buffers array");
 
   if ((dstBufs =
        (unsigned char **)malloc(sizeof(unsigned char *) * n)) == NULL)
-    _throwmem();
+    THROW_MEM();
   if ((jdstBufs = (jbyteArray *)malloc(sizeof(jbyteArray) * n)) == NULL)
-    _throwmem();
+    THROW_MEM();
   if ((dstSizes = (unsigned long *)malloc(sizeof(unsigned long) * n)) == NULL)
-    _throwmem();
+    THROW_MEM();
   if ((t = (tjtransform *)malloc(sizeof(tjtransform) * n)) == NULL)
-    _throwmem();
+    THROW_MEM();
   if ((params = (JNICustomFilterParams *)malloc(sizeof(JNICustomFilterParams) *
                                                 n)) == NULL)
-    _throwmem();
+    THROW_MEM();
   for (i = 0; i < n; i++) {
     dstBufs[i] = NULL;  jdstBufs[i] = NULL;  dstSizes[i] = 0;
     memset(&t[i], 0, sizeof(tjtransform));
@@ -1106,22 +1164,22 @@
   for (i = 0; i < n; i++) {
     jobject tobj, cfobj;
 
-    bailif0(tobj = (*env)->GetObjectArrayElement(env, tobjs, i));
-    bailif0(_cls = (*env)->GetObjectClass(env, tobj));
-    bailif0(_fid = (*env)->GetFieldID(env, _cls, "op", "I"));
+    BAILIF0(tobj = (*env)->GetObjectArrayElement(env, tobjs, i));
+    BAILIF0(_cls = (*env)->GetObjectClass(env, tobj));
+    BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "op", "I"));
     t[i].op = (*env)->GetIntField(env, tobj, _fid);
-    bailif0(_fid = (*env)->GetFieldID(env, _cls, "options", "I"));
+    BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "options", "I"));
     t[i].options = (*env)->GetIntField(env, tobj, _fid);
-    bailif0(_fid = (*env)->GetFieldID(env, _cls, "x", "I"));
+    BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "x", "I"));
     t[i].r.x = (*env)->GetIntField(env, tobj, _fid);
-    bailif0(_fid = (*env)->GetFieldID(env, _cls, "y", "I"));
+    BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "y", "I"));
     t[i].r.y = (*env)->GetIntField(env, tobj, _fid);
-    bailif0(_fid = (*env)->GetFieldID(env, _cls, "width", "I"));
+    BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "width", "I"));
     t[i].r.w = (*env)->GetIntField(env, tobj, _fid);
-    bailif0(_fid = (*env)->GetFieldID(env, _cls, "height", "I"));
+    BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "height", "I"));
     t[i].r.h = (*env)->GetIntField(env, tobj, _fid);
 
-    bailif0(_fid = (*env)->GetFieldID(env, _cls, "cf",
+    BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "cf",
       "Lorg/libjpegturbo/turbojpeg/TJCustomFilter;"));
     cfobj = (*env)->GetObjectField(env, tobj, _fid);
     if (cfobj) {
@@ -1138,29 +1196,30 @@
 
     if (t[i].r.w != 0) w = t[i].r.w;
     if (t[i].r.h != 0) h = t[i].r.h;
-    bailif0(jdstBufs[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
+    BAILIF0(jdstBufs[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
     if ((unsigned long)(*env)->GetArrayLength(env, jdstBufs[i]) <
         tjBufSize(w, h, jpegSubsamp))
-      _throwarg("Destination buffer is not large enough");
+      THROW_ARG("Destination buffer is not large enough");
   }
-  bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0));
+  BAILIF0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0));
   for (i = 0; i < n; i++)
-    bailif0(dstBufs[i] =
+    BAILIF0(dstBufs[i] =
             (*env)->GetPrimitiveArrayCritical(env, jdstBufs[i], 0));
 
   if (tjTransform(handle, jpegBuf, jpegSize, n, dstBufs, dstSizes, t,
-                  flags | TJFLAG_NOREALLOC) == -1)
-    _throwtj();
-
-  for (i = 0; i < n; i++) {
-    (*env)->ReleasePrimitiveArrayCritical(env, jdstBufs[i], dstBufs[i], 0);
-    dstBufs[i] = NULL;
+                  flags | TJFLAG_NOREALLOC) == -1) {
+    for (i = 0; i < n; i++)
+      SAFE_RELEASE(jdstBufs[i], dstBufs[i]);
+    SAFE_RELEASE(jsrcBuf, jpegBuf);
+    THROW_TJ();
   }
-  (*env)->ReleasePrimitiveArrayCritical(env, jsrcBuf, jpegBuf, 0);
-  jpegBuf = NULL;
+
+  for (i = 0; i < n; i++)
+    SAFE_RELEASE(jdstBufs[i], dstBufs[i]);
+  SAFE_RELEASE(jsrcBuf, jpegBuf);
 
   jdstSizes = (*env)->NewIntArray(env, n);
-  bailif0(dstSizesi = (*env)->GetIntArrayElements(env, jdstSizes, 0));
+  BAILIF0(dstSizesi = (*env)->GetIntArrayElements(env, jdstSizes, 0));
   for (i = 0; i < n; i++) dstSizesi[i] = (int)dstSizes[i];
 
 bailout:
@@ -1172,7 +1231,7 @@
     }
     free(dstBufs);
   }
-  if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, jsrcBuf, jpegBuf, 0);
+  SAFE_RELEASE(jsrcBuf, jpegBuf);
   if (jdstBufs) free(jdstBufs);
   if (dstSizes) free(dstSizes);
   if (t) free(t);
diff --git a/turbojpeg.c b/turbojpeg.c
index b3caa0d..3a1e3a9 100644
--- a/turbojpeg.c
+++ b/turbojpeg.c
@@ -50,7 +50,7 @@
                             unsigned long);
 
 #define PAD(v, p)  ((v + (p) - 1) & (~((p) - 1)))
-#define isPow2(x)  (((x) & (x - 1)) == 0)
+#define IS_POW2(x)  (((x) & (x - 1)) == 0)
 
 
 /* Error handling (based on example in example.txt) */
@@ -164,20 +164,20 @@
   TJPF_UNKNOWN
 };
 
-#define _throwg(m) { \
+#define THROWG(m) { \
   snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
   retval = -1;  goto bailout; \
 }
-#define _throwunix(m) { \
+#define THROW_UNIX(m) { \
   snprintf(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, strerror(errno)); \
   retval = -1;  goto bailout; \
 }
-#define _throw(m) { \
+#define THROW(m) { \
   snprintf(this->errStr, JMSG_LENGTH_MAX, "%s", m); \
-  this->isInstanceError = TRUE;  _throwg(m) \
+  this->isInstanceError = TRUE;  THROWG(m) \
 }
 
-#define getinstance(handle) \
+#define GET_INSTANCE(handle) \
   tjinstance *this = (tjinstance *)handle; \
   j_compress_ptr cinfo = NULL; \
   j_decompress_ptr dinfo = NULL; \
@@ -190,7 +190,7 @@
   this->jerr.warning = FALSE; \
   this->isInstanceError = FALSE;
 
-#define getcinstance(handle) \
+#define GET_CINSTANCE(handle) \
   tjinstance *this = (tjinstance *)handle; \
   j_compress_ptr cinfo = NULL; \
   \
@@ -202,7 +202,7 @@
   this->jerr.warning = FALSE; \
   this->isInstanceError = FALSE;
 
-#define getdinstance(handle) \
+#define GET_DINSTANCE(handle) \
   tjinstance *this = (tjinstance *)handle; \
   j_decompress_ptr dinfo = NULL; \
   \
@@ -237,7 +237,9 @@
                            int subsamp, int jpegQual, int flags)
 {
   int retval = 0;
+#ifndef NO_GETENV
   char *env = NULL;
+#endif
 
   cinfo->in_color_space = pf2cs[pixelFormat];
   cinfo->input_components = tjPixelSize[pixelFormat];
@@ -413,7 +415,7 @@
 
 DLLEXPORT int tjDestroy(tjhandle handle)
 {
-  getinstance(handle);
+  GET_INSTANCE(handle);
 
   if (setjmp(this->jerr.setjmp_buffer)) return -1;
   if (this->init & COMPRESS) jpeg_destroy_compress(cinfo);
@@ -489,11 +491,11 @@
 
 DLLEXPORT unsigned long tjBufSize(int width, int height, int jpegSubsamp)
 {
-  unsigned long retval = 0;
+  unsigned long long retval = 0;
   int mcuw, mcuh, chromasf;
 
   if (width < 1 || height < 1 || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT)
-    _throwg("tjBufSize(): Invalid argument");
+    THROWG("tjBufSize(): Invalid argument");
 
   /* This allows for rare corner cases in which a JPEG image can actually be
      larger than the uncompressed input (we wouldn't mention it if it hadn't
@@ -501,36 +503,41 @@
   mcuw = tjMCUWidth[jpegSubsamp];
   mcuh = tjMCUHeight[jpegSubsamp];
   chromasf = jpegSubsamp == TJSAMP_GRAY ? 0 : 4 * 64 / (mcuw * mcuh);
-  retval = PAD(width, mcuw) * PAD(height, mcuh) * (2 + chromasf) + 2048;
+  retval = PAD(width, mcuw) * PAD(height, mcuh) * (2ULL + chromasf) + 2048ULL;
+  if (retval > (unsigned long long)((unsigned long)-1))
+    THROWG("tjBufSize(): Image is too large");
 
 bailout:
-  return retval;
+  return (unsigned long)retval;
 }
 
 DLLEXPORT unsigned long TJBUFSIZE(int width, int height)
 {
-  unsigned long retval = 0;
+  unsigned long long retval = 0;
 
   if (width < 1 || height < 1)
-    _throwg("TJBUFSIZE(): Invalid argument");
+    THROWG("TJBUFSIZE(): Invalid argument");
 
   /* This allows for rare corner cases in which a JPEG image can actually be
      larger than the uncompressed input (we wouldn't mention it if it hadn't
      happened before.) */
-  retval = PAD(width, 16) * PAD(height, 16) * 6 + 2048;
+  retval = PAD(width, 16) * PAD(height, 16) * 6ULL + 2048ULL;
+  if (retval > (unsigned long long)((unsigned long)-1))
+    THROWG("TJBUFSIZE(): Image is too large");
 
 bailout:
-  return retval;
+  return (unsigned long)retval;
 }
 
 
 DLLEXPORT unsigned long tjBufSizeYUV2(int width, int pad, int height,
                                       int subsamp)
 {
-  int retval = 0, nc, i;
+  unsigned long long retval = 0;
+  int nc, i;
 
   if (subsamp < 0 || subsamp >= NUMSUBOPT)
-    _throwg("tjBufSizeYUV2(): Invalid argument");
+    THROWG("tjBufSizeYUV2(): Invalid argument");
 
   nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
   for (i = 0; i < nc; i++) {
@@ -539,11 +546,13 @@
     int ph = tjPlaneHeight(i, height, subsamp);
 
     if (pw < 0 || ph < 0) return -1;
-    else retval += stride * ph;
+    else retval += (unsigned long long)stride * ph;
   }
+  if (retval > (unsigned long long)((unsigned long)-1))
+    THROWG("tjBufSizeYUV2(): Image is too large");
 
 bailout:
-  return retval;
+  return (unsigned long)retval;
 }
 
 DLLEXPORT unsigned long tjBufSizeYUV(int width, int height, int subsamp)
@@ -562,10 +571,10 @@
   int pw, nc, retval = 0;
 
   if (width < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
-    _throwg("tjPlaneWidth(): Invalid argument");
+    THROWG("tjPlaneWidth(): Invalid argument");
   nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
   if (componentID < 0 || componentID >= nc)
-    _throwg("tjPlaneWidth(): Invalid argument");
+    THROWG("tjPlaneWidth(): Invalid argument");
 
   pw = PAD(width, tjMCUWidth[subsamp] / 8);
   if (componentID == 0)
@@ -583,10 +592,10 @@
   int ph, nc, retval = 0;
 
   if (height < 1 || subsamp < 0 || subsamp >= TJ_NUMSAMP)
-    _throwg("tjPlaneHeight(): Invalid argument");
+    THROWG("tjPlaneHeight(): Invalid argument");
   nc = (subsamp == TJSAMP_GRAY ? 1 : 3);
   if (componentID < 0 || componentID >= nc)
-    _throwg("tjPlaneHeight(): Invalid argument");
+    THROWG("tjPlaneHeight(): Invalid argument");
 
   ph = PAD(height, tjMCUHeight[subsamp] / 8);
   if (componentID == 0)
@@ -602,11 +611,11 @@
 DLLEXPORT unsigned long tjPlaneSizeYUV(int componentID, int width, int stride,
                                        int height, int subsamp)
 {
-  unsigned long retval = 0;
+  unsigned long long retval = 0;
   int pw, ph;
 
   if (width < 1 || height < 1 || subsamp < 0 || subsamp >= NUMSUBOPT)
-    _throwg("tjPlaneSizeYUV(): Invalid argument");
+    THROWG("tjPlaneSizeYUV(): Invalid argument");
 
   pw = tjPlaneWidth(componentID, width, subsamp);
   ph = tjPlaneHeight(componentID, height, subsamp);
@@ -615,10 +624,12 @@
   if (stride == 0) stride = pw;
   else stride = abs(stride);
 
-  retval = stride * (ph - 1) + pw;
+  retval = (unsigned long long)stride * (ph - 1) + pw;
+  if (retval > (unsigned long long)((unsigned long)-1))
+    THROWG("tjPlaneSizeYUV(): Image is too large");
 
 bailout:
-  return retval;
+  return (unsigned long)retval;
 }
 
 
@@ -630,21 +641,21 @@
   int i, retval = 0, alloc = 1;
   JSAMPROW *row_pointer = NULL;
 
-  getcinstance(handle)
+  GET_CINSTANCE(handle)
   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
   if ((this->init & COMPRESS) == 0)
-    _throw("tjCompress2(): Instance has not been initialized for compression");
+    THROW("tjCompress2(): Instance has not been initialized for compression");
 
   if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
       pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
       jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
       jpegQual < 0 || jpegQual > 100)
-    _throw("tjCompress2(): Invalid argument");
+    THROW("tjCompress2(): Invalid argument");
 
   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
 
   if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
-    _throw("tjCompress2(): Memory allocation failure");
+    THROW("tjCompress2(): Memory allocation failure");
 
   if (setjmp(this->jerr.setjmp_buffer)) {
     /* If we get here, the JPEG code has signaled an error. */
@@ -670,9 +681,9 @@
   jpeg_start_compress(cinfo, TRUE);
   for (i = 0; i < height; i++) {
     if (flags & TJFLAG_BOTTOMUP)
-      row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * pitch];
+      row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
     else
-      row_pointer[i] = (JSAMPROW)&srcBuf[i * pitch];
+      row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
   }
   while (cinfo->next_scanline < cinfo->image_height)
     jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
@@ -723,7 +734,7 @@
   JSAMPLE *ptr;
   jpeg_component_info *compptr;
 
-  getcinstance(handle);
+  GET_CINSTANCE(handle);
   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
 
   for (i = 0; i < MAX_COMPONENTS; i++) {
@@ -732,17 +743,17 @@
   }
 
   if ((this->init & COMPRESS) == 0)
-    _throw("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
+    THROW("tjEncodeYUVPlanes(): Instance has not been initialized for compression");
 
   if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
       pixelFormat < 0 || pixelFormat >= TJ_NUMPF || !dstPlanes ||
       !dstPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT)
-    _throw("tjEncodeYUVPlanes(): Invalid argument");
+    THROW("tjEncodeYUVPlanes(): Invalid argument");
   if (subsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
-    _throw("tjEncodeYUVPlanes(): Invalid argument");
+    THROW("tjEncodeYUVPlanes(): Invalid argument");
 
   if (pixelFormat == TJPF_CMYK)
-    _throw("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
+    THROW("tjEncodeYUVPlanes(): Cannot generate YUV images from CMYK pixels");
 
   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
 
@@ -767,7 +778,7 @@
      to write the file headers, which could overflow the output buffer if the
      YUV image were very small. */
   if (cinfo->global_state != CSTATE_START)
-    _throw("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
+    THROW("tjEncodeYUVPlanes(): libjpeg API is in the wrong state");
   (*cinfo->err->reset_error_mgr) ((j_common_ptr)cinfo);
   jinit_c_master_control(cinfo, FALSE);
   jinit_color_converter(cinfo);
@@ -778,12 +789,12 @@
   ph0 = PAD(height, cinfo->max_v_samp_factor);
 
   if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
-    _throw("tjEncodeYUVPlanes(): Memory allocation failure");
+    THROW("tjEncodeYUVPlanes(): Memory allocation failure");
   for (i = 0; i < height; i++) {
     if (flags & TJFLAG_BOTTOMUP)
-      row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * pitch];
+      row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
     else
-      row_pointer[i] = (JSAMPROW)&srcBuf[i * pitch];
+      row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
   }
   if (height < ph0)
     for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
@@ -795,11 +806,11 @@
           compptr->h_samp_factor, 32) *
       cinfo->max_v_samp_factor + 32);
     if (!_tmpbuf[i])
-      _throw("tjEncodeYUVPlanes(): Memory allocation failure");
+      THROW("tjEncodeYUVPlanes(): Memory allocation failure");
     tmpbuf[i] =
       (JSAMPROW *)malloc(sizeof(JSAMPROW) * cinfo->max_v_samp_factor);
     if (!tmpbuf[i])
-      _throw("tjEncodeYUVPlanes(): Memory allocation failure");
+      THROW("tjEncodeYUVPlanes(): Memory allocation failure");
     for (row = 0; row < cinfo->max_v_samp_factor; row++) {
       unsigned char *_tmpbuf_aligned =
         (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
@@ -812,10 +823,10 @@
       (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
                         compptr->v_samp_factor + 32);
     if (!_tmpbuf2[i])
-      _throw("tjEncodeYUVPlanes(): Memory allocation failure");
+      THROW("tjEncodeYUVPlanes(): Memory allocation failure");
     tmpbuf2[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
     if (!tmpbuf2[i])
-      _throw("tjEncodeYUVPlanes(): Memory allocation failure");
+      THROW("tjEncodeYUVPlanes(): Memory allocation failure");
     for (row = 0; row < compptr->v_samp_factor; row++) {
       unsigned char *_tmpbuf2_aligned =
         (unsigned char *)PAD((size_t)_tmpbuf2[i], 32);
@@ -827,7 +838,7 @@
     ph[i] = ph0 * compptr->v_samp_factor / cinfo->max_v_samp_factor;
     outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
     if (!outbuf[i])
-      _throw("tjEncodeYUVPlanes(): Memory allocation failure");
+      THROW("tjEncodeYUVPlanes(): Memory allocation failure");
     ptr = dstPlanes[i];
     for (row = 0; row < ph[i]; row++) {
       outbuf[i][row] = ptr;
@@ -877,12 +888,12 @@
   int pw0, ph0, strides[3], retval = -1;
   tjinstance *this = (tjinstance *)handle;
 
-  if (!this) _throwg("tjEncodeYUV3(): Invalid handle");
+  if (!this) THROWG("tjEncodeYUV3(): Invalid handle");
   this->isInstanceError = FALSE;
 
-  if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 || !isPow2(pad) ||
-      subsamp < 0 || subsamp >= NUMSUBOPT)
-    _throw("tjEncodeYUV3(): Invalid argument");
+  if (width <= 0 || height <= 0 || dstBuf == NULL || pad < 0 ||
+      !IS_POW2(pad) || subsamp < 0 || subsamp >= NUMSUBOPT)
+    THROW("tjEncodeYUV3(): Invalid argument");
 
   pw0 = tjPlaneWidth(0, width, subsamp);
   ph0 = tjPlaneHeight(0, height, subsamp);
@@ -939,7 +950,7 @@
   JSAMPLE *_tmpbuf = NULL, *ptr;
   JSAMPROW *inbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
 
-  getcinstance(handle)
+  GET_CINSTANCE(handle)
   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
 
   for (i = 0; i < MAX_COMPONENTS; i++) {
@@ -947,14 +958,14 @@
   }
 
   if ((this->init & COMPRESS) == 0)
-    _throw("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
+    THROW("tjCompressFromYUVPlanes(): Instance has not been initialized for compression");
 
   if (!srcPlanes || !srcPlanes[0] || width <= 0 || height <= 0 ||
       subsamp < 0 || subsamp >= NUMSUBOPT || jpegBuf == NULL ||
       jpegSize == NULL || jpegQual < 0 || jpegQual > 100)
-    _throw("tjCompressFromYUVPlanes(): Invalid argument");
+    THROW("tjCompressFromYUVPlanes(): Invalid argument");
   if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
-    _throw("tjCompressFromYUVPlanes(): Invalid argument");
+    THROW("tjCompressFromYUVPlanes(): Invalid argument");
 
   if (setjmp(this->jerr.setjmp_buffer)) {
     /* If we get here, the JPEG code has signaled an error. */
@@ -993,7 +1004,7 @@
     th[i] = compptr->v_samp_factor * DCTSIZE;
     tmpbufsize += iw[i] * th[i];
     if ((inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
-      _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
+      THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
     ptr = (JSAMPLE *)srcPlanes[i];
     for (row = 0; row < ph[i]; row++) {
       inbuf[i][row] = ptr;
@@ -1002,11 +1013,11 @@
   }
   if (usetmpbuf) {
     if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
-      _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
+      THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
     ptr = _tmpbuf;
     for (i = 0; i < cinfo->num_components; i++) {
       if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
-        _throw("tjCompressFromYUVPlanes(): Memory allocation failure");
+        THROW("tjCompressFromYUVPlanes(): Memory allocation failure");
       for (row = 0; row < th[i]; row++) {
         tmpbuf[i][row] = ptr;
         ptr += iw[i];
@@ -1070,12 +1081,12 @@
   int pw0, ph0, strides[3], retval = -1;
   tjinstance *this = (tjinstance *)handle;
 
-  if (!this) _throwg("tjCompressFromYUV(): Invalid handle");
+  if (!this) THROWG("tjCompressFromYUV(): Invalid handle");
   this->isInstanceError = FALSE;
 
   if (srcBuf == NULL || width <= 0 || pad < 1 || height <= 0 || subsamp < 0 ||
       subsamp >= NUMSUBOPT)
-    _throw("tjCompressFromYUV(): Invalid argument");
+    THROW("tjCompressFromYUV(): Invalid argument");
 
   pw0 = tjPlaneWidth(0, width, subsamp);
   ph0 = tjPlaneHeight(0, height, subsamp);
@@ -1154,13 +1165,13 @@
 {
   int retval = 0;
 
-  getdinstance(handle);
+  GET_DINSTANCE(handle);
   if ((this->init & DECOMPRESS) == 0)
-    _throw("tjDecompressHeader3(): Instance has not been initialized for decompression");
+    THROW("tjDecompressHeader3(): Instance has not been initialized for decompression");
 
   if (jpegBuf == NULL || jpegSize <= 0 || width == NULL || height == NULL ||
       jpegSubsamp == NULL || jpegColorspace == NULL)
-    _throw("tjDecompressHeader3(): Invalid argument");
+    THROW("tjDecompressHeader3(): Invalid argument");
 
   if (setjmp(this->jerr.setjmp_buffer)) {
     /* If we get here, the JPEG code has signaled an error. */
@@ -1185,11 +1196,11 @@
   jpeg_abort_decompress(dinfo);
 
   if (*jpegSubsamp < 0)
-    _throw("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
+    THROW("tjDecompressHeader3(): Could not determine subsampling type for JPEG image");
   if (*jpegColorspace < 0)
-    _throw("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
+    THROW("tjDecompressHeader3(): Could not determine colorspace of JPEG image");
   if (*width < 1 || *height < 1)
-    _throw("tjDecompressHeader3(): Invalid data returned in header");
+    THROW("tjDecompressHeader3(): Invalid data returned in header");
 
 bailout:
   if (this->jerr.warning) retval = -1;
@@ -1238,14 +1249,14 @@
   JSAMPROW *row_pointer = NULL;
   int i, retval = 0, jpegwidth, jpegheight, scaledw, scaledh;
 
-  getdinstance(handle);
+  GET_DINSTANCE(handle);
   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
   if ((this->init & DECOMPRESS) == 0)
-    _throw("tjDecompress2(): Instance has not been initialized for decompression");
+    THROW("tjDecompress2(): Instance has not been initialized for decompression");
 
   if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
       pitch < 0 || height < 0 || pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
-    _throw("tjDecompress2(): Invalid argument");
+    THROW("tjDecompress2(): Invalid argument");
 
 #ifndef NO_PUTENV
   if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
@@ -1274,7 +1285,7 @@
       break;
   }
   if (i >= NUMSF)
-    _throw("tjDecompress2(): Could not scale down to desired image dimensions");
+    THROW("tjDecompress2(): Could not scale down to desired image dimensions");
   width = scaledw;  height = scaledh;
   dinfo->scale_num = sf[i].num;
   dinfo->scale_denom = sf[i].denom;
@@ -1284,16 +1295,16 @@
 
   if ((row_pointer =
        (JSAMPROW *)malloc(sizeof(JSAMPROW) * dinfo->output_height)) == NULL)
-    _throw("tjDecompress2(): Memory allocation failure");
+    THROW("tjDecompress2(): Memory allocation failure");
   if (setjmp(this->jerr.setjmp_buffer)) {
     /* If we get here, the JPEG code has signaled an error. */
     retval = -1;  goto bailout;
   }
   for (i = 0; i < (int)dinfo->output_height; i++) {
     if (flags & TJFLAG_BOTTOMUP)
-      row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * pitch];
+      row_pointer[i] = &dstBuf[(dinfo->output_height - i - 1) * (size_t)pitch];
     else
-      row_pointer[i] = &dstBuf[i * pitch];
+      row_pointer[i] = &dstBuf[i * (size_t)pitch];
   }
   while (dinfo->output_scanline < dinfo->output_height)
     jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline],
@@ -1362,12 +1373,12 @@
 }
 
 
-int my_read_markers(j_decompress_ptr dinfo)
+static int my_read_markers(j_decompress_ptr dinfo)
 {
   return JPEG_REACHED_SOS;
 }
 
-void my_reset_marker_reader(j_decompress_ptr dinfo)
+static void my_reset_marker_reader(j_decompress_ptr dinfo)
 {
 }
 
@@ -1386,7 +1397,7 @@
   int (*old_read_markers) (j_decompress_ptr);
   void (*old_reset_marker_reader) (j_decompress_ptr);
 
-  getdinstance(handle);
+  GET_DINSTANCE(handle);
   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
 
   for (i = 0; i < MAX_COMPONENTS; i++) {
@@ -1394,14 +1405,14 @@
   }
 
   if ((this->init & DECOMPRESS) == 0)
-    _throw("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
+    THROW("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");
 
   if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
       dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
       pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
-    _throw("tjDecodeYUVPlanes(): Invalid argument");
+    THROW("tjDecodeYUVPlanes(): Invalid argument");
   if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
-    _throw("tjDecodeYUVPlanes(): Invalid argument");
+    THROW("tjDecodeYUVPlanes(): Invalid argument");
 
   if (setjmp(this->jerr.setjmp_buffer)) {
     /* If we get here, the JPEG code has signaled an error. */
@@ -1409,7 +1420,7 @@
   }
 
   if (pixelFormat == TJPF_CMYK)
-    _throw("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
+    THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");
 
   if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
   dinfo->image_width = width;
@@ -1421,6 +1432,9 @@
   else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
 #endif
 
+  dinfo->progressive_mode = dinfo->inputctl->has_multiple_scans = FALSE;
+  dinfo->Ss = dinfo->Ah = dinfo->Al = 0;
+  dinfo->Se = DCTSIZE2 - 1;
   if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
     retval = -1;  goto bailout;
   }
@@ -1445,12 +1459,12 @@
   if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];
 
   if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
-    _throw("tjDecodeYUVPlanes(): Memory allocation failure");
+    THROW("tjDecodeYUVPlanes(): Memory allocation failure");
   for (i = 0; i < height; i++) {
     if (flags & TJFLAG_BOTTOMUP)
-      row_pointer[i] = &dstBuf[(height - i - 1) * pitch];
+      row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
     else
-      row_pointer[i] = &dstBuf[i * pitch];
+      row_pointer[i] = &dstBuf[i * (size_t)pitch];
   }
   if (height < ph0)
     for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];
@@ -1461,10 +1475,10 @@
       (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
                         compptr->v_samp_factor + 32);
     if (!_tmpbuf[i])
-      _throw("tjDecodeYUVPlanes(): Memory allocation failure");
+      THROW("tjDecodeYUVPlanes(): Memory allocation failure");
     tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
     if (!tmpbuf[i])
-      _throw("tjDecodeYUVPlanes(): Memory allocation failure");
+      THROW("tjDecodeYUVPlanes(): Memory allocation failure");
     for (row = 0; row < compptr->v_samp_factor; row++) {
       unsigned char *_tmpbuf_aligned =
         (unsigned char *)PAD((size_t)_tmpbuf[i], 32);
@@ -1476,7 +1490,7 @@
     ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
     inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
     if (!inbuf[i])
-      _throw("tjDecodeYUVPlanes(): Memory allocation failure");
+      THROW("tjDecodeYUVPlanes(): Memory allocation failure");
     ptr = (JSAMPLE *)srcPlanes[i];
     for (row = 0; row < ph[i]; row++) {
       inbuf[i][row] = ptr;
@@ -1525,12 +1539,12 @@
   int pw0, ph0, strides[3], retval = -1;
   tjinstance *this = (tjinstance *)handle;
 
-  if (!this) _throwg("tjDecodeYUV(): Invalid handle");
+  if (!this) THROWG("tjDecodeYUV(): Invalid handle");
   this->isInstanceError = FALSE;
 
-  if (srcBuf == NULL || pad < 0 || !isPow2(pad) || subsamp < 0 ||
+  if (srcBuf == NULL || pad < 0 || !IS_POW2(pad) || subsamp < 0 ||
       subsamp >= NUMSUBOPT || width <= 0 || height <= 0)
-    _throw("tjDecodeYUV(): Invalid argument");
+    THROW("tjDecodeYUV(): Invalid argument");
 
   pw0 = tjPlaneWidth(0, width, subsamp);
   ph0 = tjPlaneHeight(0, height, subsamp);
@@ -1569,7 +1583,7 @@
   JSAMPROW *outbuf[MAX_COMPONENTS], *tmpbuf[MAX_COMPONENTS];
   int dctsize;
 
-  getdinstance(handle);
+  GET_DINSTANCE(handle);
   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
 
   for (i = 0; i < MAX_COMPONENTS; i++) {
@@ -1577,11 +1591,11 @@
   }
 
   if ((this->init & DECOMPRESS) == 0)
-    _throw("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
+    THROW("tjDecompressToYUVPlanes(): Instance has not been initialized for decompression");
 
   if (jpegBuf == NULL || jpegSize <= 0 || !dstPlanes || !dstPlanes[0] ||
       width < 0 || height < 0)
-    _throw("tjDecompressToYUVPlanes(): Invalid argument");
+    THROW("tjDecompressToYUVPlanes(): Invalid argument");
 
 #ifndef NO_PUTENV
   if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
@@ -1601,10 +1615,10 @@
   this->headerRead = 0;
   jpegSubsamp = getSubsamp(dinfo);
   if (jpegSubsamp < 0)
-    _throw("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
+    THROW("tjDecompressToYUVPlanes(): Could not determine subsampling type for JPEG image");
 
   if (jpegSubsamp != TJSAMP_GRAY && (!dstPlanes[1] || !dstPlanes[2]))
-    _throw("tjDecompressToYUVPlanes(): Invalid argument");
+    THROW("tjDecompressToYUVPlanes(): Invalid argument");
 
   jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
   if (width == 0) width = jpegwidth;
@@ -1616,9 +1630,9 @@
       break;
   }
   if (i >= NUMSF)
-    _throw("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
+    THROW("tjDecompressToYUVPlanes(): Could not scale down to desired image dimensions");
   if (dinfo->num_components > 3)
-    _throw("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
+    THROW("tjDecompressToYUVPlanes(): JPEG image must have 3 or fewer components");
 
   width = scaledw;  height = scaledh;
   dinfo->scale_num = sf[i].num;
@@ -1642,7 +1656,7 @@
     th[i] = compptr->v_samp_factor * dctsize;
     tmpbufsize += iw[i] * th[i];
     if ((outbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i])) == NULL)
-      _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
+      THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
     ptr = dstPlanes[i];
     for (row = 0; row < ph[i]; row++) {
       outbuf[i][row] = ptr;
@@ -1651,11 +1665,11 @@
   }
   if (usetmpbuf) {
     if ((_tmpbuf = (JSAMPLE *)malloc(sizeof(JSAMPLE) * tmpbufsize)) == NULL)
-      _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
+      THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
     ptr = _tmpbuf;
     for (i = 0; i < dinfo->num_components; i++) {
       if ((tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * th[i])) == NULL)
-        _throw("tjDecompressToYUVPlanes(): Memory allocation failure");
+        THROW("tjDecompressToYUVPlanes(): Memory allocation failure");
       for (row = 0; row < th[i]; row++) {
         tmpbuf[i][row] = ptr;
         ptr += iw[i];
@@ -1736,12 +1750,12 @@
   int pw0, ph0, strides[3], retval = -1, jpegSubsamp = -1;
   int i, jpegwidth, jpegheight, scaledw, scaledh;
 
-  getdinstance(handle);
+  GET_DINSTANCE(handle);
   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
 
   if (jpegBuf == NULL || jpegSize <= 0 || dstBuf == NULL || width < 0 ||
-      pad < 1 || !isPow2(pad) || height < 0)
-    _throw("tjDecompressToYUV2(): Invalid argument");
+      pad < 1 || !IS_POW2(pad) || height < 0)
+    THROW("tjDecompressToYUV2(): Invalid argument");
 
   if (setjmp(this->jerr.setjmp_buffer)) {
     /* If we get here, the JPEG code has signaled an error. */
@@ -1752,7 +1766,7 @@
   jpeg_read_header(dinfo, TRUE);
   jpegSubsamp = getSubsamp(dinfo);
   if (jpegSubsamp < 0)
-    _throw("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
+    THROW("tjDecompressToYUV2(): Could not determine subsampling type for JPEG image");
 
   jpegwidth = dinfo->image_width;  jpegheight = dinfo->image_height;
   if (width == 0) width = jpegwidth;
@@ -1765,7 +1779,7 @@
       break;
   }
   if (i >= NUMSF)
-    _throw("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
+    THROW("tjDecompressToYUV2(): Could not scale down to desired image dimensions");
 
   pw0 = tjPlaneWidth(0, width, jpegSubsamp);
   ph0 = tjPlaneHeight(0, height, jpegSubsamp);
@@ -1830,14 +1844,14 @@
   jvirt_barray_ptr *srccoefs, *dstcoefs;
   int retval = 0, i, jpegSubsamp, saveMarkers = 0;
 
-  getinstance(handle);
+  GET_INSTANCE(handle);
   this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
   if ((this->init & COMPRESS) == 0 || (this->init & DECOMPRESS) == 0)
-    _throw("tjTransform(): Instance has not been initialized for transformation");
+    THROW("tjTransform(): Instance has not been initialized for transformation");
 
   if (jpegBuf == NULL || jpegSize <= 0 || n < 1 || dstBufs == NULL ||
       dstSizes == NULL || t == NULL || flags < 0)
-    _throw("tjTransform(): Invalid argument");
+    THROW("tjTransform(): Invalid argument");
 
 #ifndef NO_PUTENV
   if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
@@ -1847,7 +1861,7 @@
 
   if ((xinfo =
        (jpeg_transform_info *)malloc(sizeof(jpeg_transform_info) * n)) == NULL)
-    _throw("tjTransform(): Memory allocation failure");
+    THROW("tjTransform(): Memory allocation failure");
   MEMZERO(xinfo, sizeof(jpeg_transform_info) * n);
 
   if (setjmp(this->jerr.setjmp_buffer)) {
@@ -1885,11 +1899,11 @@
   jpeg_read_header(dinfo, TRUE);
   jpegSubsamp = getSubsamp(dinfo);
   if (jpegSubsamp < 0)
-    _throw("tjTransform(): Could not determine subsampling type for JPEG image");
+    THROW("tjTransform(): Could not determine subsampling type for JPEG image");
 
   for (i = 0; i < n; i++) {
     if (!jtransform_request_workspace(dinfo, &xinfo[i]))
-      _throw("tjTransform(): Transform is not perfect");
+      THROW("tjTransform(): Transform is not perfect");
 
     if (xinfo[i].crop) {
       if ((t[i].r.x % xinfo[i].iMCU_sample_width) != 0 ||
@@ -1952,7 +1966,7 @@
           for (y = 0; y < compptr->v_samp_factor; y++) {
             if (t[i].customFilter(barray[y][0], arrayRegion, planeRegion, ci,
                                   i, &t[i]) == -1)
-              _throw("tjTransform(): Error in custom filter");
+              THROW("tjTransform(): Error in custom filter");
             arrayRegion.y += DCTSIZE;
           }
         }
@@ -1989,21 +2003,21 @@
 
   if (!filename || !width || align < 1 || !height || !pixelFormat ||
       *pixelFormat < TJPF_UNKNOWN || *pixelFormat >= TJ_NUMPF)
-    _throwg("tjLoadImage(): Invalid argument");
+    THROWG("tjLoadImage(): Invalid argument");
   if ((align & (align - 1)) != 0)
-    _throwg("tjLoadImage(): Alignment must be a power of 2");
+    THROWG("tjLoadImage(): Alignment must be a power of 2");
 
   if ((handle = tjInitCompress()) == NULL) return NULL;
   this = (tjinstance *)handle;
   cinfo = &this->cinfo;
 
   if ((file = fopen(filename, "rb")) == NULL)
-    _throwunix("tjLoadImage(): Cannot open input file");
+    THROW_UNIX("tjLoadImage(): Cannot open input file");
 
   if ((tempc = getc(file)) < 0 || ungetc(tempc, file) == EOF)
-    _throwunix("tjLoadImage(): Could not read input file")
+    THROW_UNIX("tjLoadImage(): Could not read input file")
   else if (tempc == EOF)
-    _throwg("tjLoadImage(): Input file contains no data");
+    THROWG("tjLoadImage(): Input file contains no data");
 
   if (setjmp(this->jerr.setjmp_buffer)) {
     /* If we get here, the JPEG code has signaled an error. */
@@ -2014,14 +2028,14 @@
   else cinfo->in_color_space = pf2cs[*pixelFormat];
   if (tempc == 'B') {
     if ((src = jinit_read_bmp(cinfo, FALSE)) == NULL)
-      _throwg("tjLoadImage(): Could not initialize bitmap loader");
+      THROWG("tjLoadImage(): Could not initialize bitmap loader");
     invert = (flags & TJFLAG_BOTTOMUP) == 0;
   } else if (tempc == 'P') {
     if ((src = jinit_read_ppm(cinfo)) == NULL)
-      _throwg("tjLoadImage(): Could not initialize bitmap loader");
+      THROWG("tjLoadImage(): Could not initialize bitmap loader");
     invert = (flags & TJFLAG_BOTTOMUP) != 0;
   } else
-    _throwg("tjLoadImage(): Unsupported file type");
+    THROWG("tjLoadImage(): Unsupported file type");
 
   src->input_file = file;
   (*src->start_input) (cinfo, src);
@@ -2034,7 +2048,7 @@
   if ((unsigned long long)pitch * (unsigned long long)(*height) >
       (unsigned long long)((size_t)-1) ||
       (dstBuf = (unsigned char *)malloc(pitch * (*height))) == NULL)
-    _throwg("tjLoadImage(): Memory allocation failure");
+    THROWG("tjLoadImage(): Memory allocation failure");
 
   if (setjmp(this->jerr.setjmp_buffer)) {
     /* If we get here, the JPEG code has signaled an error. */
@@ -2081,7 +2095,7 @@
 
   if (!filename || !buffer || width < 1 || pitch < 0 || height < 1 ||
       pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
-    _throwg("tjSaveImage(): Invalid argument");
+    THROWG("tjSaveImage(): Invalid argument");
 
   if ((handle = tjInitDecompress()) == NULL)
     return -1;
@@ -2089,7 +2103,7 @@
   dinfo = &this->dinfo;
 
   if ((file = fopen(filename, "wb")) == NULL)
-    _throwunix("tjSaveImage(): Cannot open output file");
+    THROW_UNIX("tjSaveImage(): Cannot open output file");
 
   if (setjmp(this->jerr.setjmp_buffer)) {
     /* If we get here, the JPEG code has signaled an error. */
@@ -2104,11 +2118,11 @@
   ptr = strrchr(filename, '.');
   if (ptr && !strcasecmp(ptr, ".bmp")) {
     if ((dst = jinit_write_bmp(dinfo, FALSE, FALSE)) == NULL)
-      _throwg("tjSaveImage(): Could not initialize bitmap writer");
+      THROWG("tjSaveImage(): Could not initialize bitmap writer");
     invert = (flags & TJFLAG_BOTTOMUP) == 0;
   } else {
     if ((dst = jinit_write_ppm(dinfo)) == NULL)
-      _throwg("tjSaveImage(): Could not initialize PPM writer");
+      THROWG("tjSaveImage(): Could not initialize PPM writer");
     invert = (flags & TJFLAG_BOTTOMUP) != 0;
   }