Initial attempt at libopenjpeg2 support

OpenJPEG 2 has a new pkg-config name and API.

- Update configure.ac to find openjpeg 2 and provide V1/v2 macros
- Update JPEG2000Stream to use new API depending on openjpeg v1/v2 macros
- OpenJPEG 2.1 changed the API so provide a version macro to make it easier
  to handle the the 2.1 change and any future changes.
- Move openjpeg.h into the .cc file
diff --git a/configure.ac b/configure.ac
index 54dacf1..47e8aac 100644
--- a/configure.ac
+++ b/configure.ac
@@ -147,32 +147,57 @@
 fi
 
 
-dnl Test for libopenjpeg. Versions prior to 1.4 do not provide a pkgconfig file.
+dnl ##### Test for libopenjpeg. Versions prior to 1.4 do not provide a pkgconfig file.
+openjpeg1="no"
+openjpeg2="no"
 AC_ARG_ENABLE(libopenjpeg,
-	      AC_HELP_STRING([--disable-libopenjpeg],
-	                     [Don't build against libopenjpeg.]),
-              enable_libopenjpeg=$enableval,
-              enable_libopenjpeg="try")
-if test x$enable_libopenjpeg = xyes; then
+	      AC_HELP_STRING([--enable-libopenjpeg=@<:@auto/openjpeg1/openjpeg2/none@:>@],
+                             [Use openjpeg for JPEG2000 images. 'auto' prefers openjpeg1 over openjpeg2 if both are available due to regressions in openjpeg2 [[default=auto]]]),
+              [enable_libopenjpeg=$enableval],
+              [enable_libopenjpeg="auto"])
+
+openjpeg_header=yes
+
+dnl test for libopenjpeg1
+if test x$enable_libopenjpeg = xopenjpeg1 || test x$enable_libopenjpeg = xauto; then
   PKG_CHECK_MODULES(LIBOPENJPEG, libopenjpeg,
-  		    [],
-    		    [AC_CHECK_LIB([openjpeg], [opj_cio_open],
-  			LIBOPENJPEG_LIBS="-lopenjpeg",
-		        AC_MSG_ERROR("*** libopenjpeg library not found ***"))
-		     AC_CHECK_HEADERS([openjpeg.h],,
-		     	AC_MSG_ERROR("*** libopenjpeg headers not found ***"))])
-elif test x$enable_libopenjpeg = xtry; then
-  PKG_CHECK_MODULES(LIBOPENJPEG, libopenjpeg,
-                    [enable_libopenjpeg="yes"],
-  		    [AC_CHECK_LIB([openjpeg], [opj_cio_open],
-		        [enable_libopenjpeg="yes"
-  			 LIBOPENJPEG_LIBS="-lopenjpeg"],
-	                [enable_libopenjpeg="no"])
-		     AC_CHECK_HEADERS([openjpeg.h],,
-		         [enable_libopenjpeg="no"])])
+    [openjpeg1="yes"],
+    [AC_CHECK_LIB([openjpeg], [opj_cio_open],
+      [openjpeg1="yes"
+       LIBOPENJPEG_LIBS="-lopenjpeg"],[openjpeg_header=no])
+     AC_CHECK_HEADERS([openjpeg.h],,
+		      [openjpeg="no"])])
 fi
 
-if test x$enable_libopenjpeg = xyes; then
+dnl test for libopenjpeg2
+if test x$openjpeg1 = xno; then
+  if test x$enable_libopenjpeg = xopenjpeg2 || test x$enable_libopenjpeg = xauto; then
+    PKG_CHECK_MODULES(LIBOPENJPEG, libopenjp2,
+                      [openjpeg2=yes],[])
+  fi
+fi
+
+if test x$enable_libopenjpeg = xopenjpeg1 && test x$openjpeg1 = xno; then
+  if test x$openjpeg_header = xno; then
+    AC_MSG_ERROR("*** libopenjpeg headers not found ***")
+  else
+    AC_MSG_ERROR("*** libopenjpeg library not found ***")
+  fi
+fi
+
+if test x$enable_libopenjpeg = xopenjpeg2 && test x$openjpeg2 = xno; then
+  AC_MSG_ERROR("*** libopenjp2 library not found ***")
+fi
+
+if test x$openjpeg1 = xyes || test x$openjpeg2 = xyes; then
+  enable_libopenjpeg=yes
+  if test x$openjpeg1 = xyes; then
+    AC_DEFINE(USE_OPENJPEG1, 1, [Defined if using openjpeg1])
+  fi
+  if test x$openjpeg2 = xyes; then
+    AC_DEFINE(USE_OPENJPEG2, 1, [Defined if using openjpeg2])
+  fi
+
   AC_SUBST(LIBOPENJPEG_CFLAGS)
   AC_SUBST(LIBOPENJPEG_LIBS)
   AC_DEFINE(ENABLE_LIBOPENJPEG)
@@ -182,9 +207,11 @@
        [AC_DEFINE(WITH_OPENJPEG_IGNORE_PCLR_CMAP_CDEF_FLAG, 1, [OpenJPEG with the OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG flag.])],
        [])
       ])
+else
+  enable_libopenjpeg=no
 fi
 
-AM_CONDITIONAL(BUILD_LIBOPENJPEG, test x$enable_libopenjpeg = xyes)
+AM_CONDITIONAL(BUILD_LIBOPENJPEG, test x$openjpeg1 = xyes || test x$openjpeg2 = xyes)
 AH_TEMPLATE([ENABLE_LIBOPENJPEG],
 	    [Use libopenjpeg instead of builtin jpeg2000 decoder.])
 
@@ -924,6 +951,13 @@
 echo "  use zlib:           $enable_zlib"
 echo "  use libcurl:        $enable_libcurl"
 echo "  use libopenjpeg:    $enable_libopenjpeg"
+if test x$enable_libopenjpeg = xyes;then
+    if test x$openjpeg1 = xyes;then
+        echo "      with openjpeg1"
+    else
+        echo "      with openjpeg2"
+    fi
+fi
 echo "  use cms:            $enable_cms"
 if test x$enable_cms = xyes;then
     if test x$lcms1 = xyes;then
diff --git a/poppler/JPEG2000Stream.cc b/poppler/JPEG2000Stream.cc
index 703d471..77a153b 100644
--- a/poppler/JPEG2000Stream.cc
+++ b/poppler/JPEG2000Stream.cc
@@ -6,49 +6,87 @@
 //
 // Copyright 2008-2010, 2012 Albert Astals Cid <aacid@kde.org>
 // Copyright 2011 Daniel Glöckner <daniel-gl@gmx.net>
-// Copyright 2013 Adrian Johnson <ajohnson@redneon.com>
+// Copyright 2013,2014 Adrian Johnson <ajohnson@redneon.com>
 //
 // Licensed under GPLv2 or later
 //
 //========================================================================
 
-#include "JPEG2000Stream.h"
-
 #include "config.h"
+#include "JPEG2000Stream.h"
+#include <openjpeg.h>
 
-JPXStream::JPXStream(Stream *strA) : FilterStream(strA)
-{
-  inited = gFalse;
-  image = NULL;
-  dinfo = NULL;
-  npixels = 0;
-  ncomps = 0;
+#define OPENJPEG_VERSION_ENCODE(major, minor, micro) (	\
+	  ((major) * 10000)				\
+	+ ((minor) *   100)				\
+	+ ((micro) *     1))
+
+#ifdef USE_OPENJPEG2
+#ifdef OPJ_VERSION_MAJOR
+#define OPENJPEG_VERSION OPENJPEG_VERSION_ENCODE(OPJ_VERSION_MAJOR, OPJ_VERSION_MINOR, OPJ_VERSION_BUILD)
+#else
+// OpenJPEG started providing version macros in version 2.1.
+// If the version macro is not found, set the version to 2.0.0 and
+// assume there will be no API changes in 2.0.x.
+#define OPENJPEG_VERSION OPENJPEG_VERSION_ENCODE(2, 0, 0)
+#endif
+#endif
+
+struct JPXStreamPrivate {
+  opj_image_t *image;
+  int counter;
+  int ccounter;
+  int npixels;
+  int ncomps;
+  GBool inited;
+#ifdef USE_OPENJPEG1
+  opj_dinfo_t *dinfo;
+  void init2(unsigned char *buf, int bufLen, OPJ_CODEC_FORMAT format);
+#endif
+#ifdef USE_OPENJPEG2
+  void init2(opj_stream_t *stream, OPJ_CODEC_FORMAT format);
+#endif
+};
+
+JPXStream::JPXStream(Stream *strA) : FilterStream(strA) {
+  priv = new JPXStreamPrivate;
+  priv->inited = gFalse;
+  priv->image = NULL;
+  priv->npixels = 0;
+  priv->ncomps = 0;
+#ifdef USE_OPENJPEG1
+  priv->dinfo = NULL;
+#endif
 }
 
 JPXStream::~JPXStream() {
   delete str;
   close();
+  delete priv;
 }
 
 void JPXStream::reset() {
-  counter = 0;
-  ccounter = 0;
+  priv->counter = 0;
+  priv->ccounter = 0;
 }
 
 void JPXStream::close() {
-  if (image != NULL) {
-    opj_image_destroy(image);
-    image = NULL;
-    npixels = 0;
+  if (priv->image != NULL) {
+    opj_image_destroy(priv->image);
+    priv->image = NULL;
+    priv->npixels = 0;
   }
-  if (dinfo != NULL) {
-    opj_destroy_decompress(dinfo);
-    dinfo = NULL;
+
+#ifdef USE_OPENJPEG1
+  if (priv->dinfo != NULL) {
+    opj_destroy_decompress(priv->dinfo);
+    priv->dinfo = NULL;
   }
+#endif
 }
 
 Goffset JPXStream::getPos() {
-  return counter * ncomps + ccounter;
+  return priv->counter * priv->ncomps + priv->ccounter;
 }
 
 int JPXStream::getChars(int nChars, Guchar *buffer) {
@@ -64,6 +102,59 @@
   return doGetChar();
 }
 
+int JPXStream::doLookChar() {
+  if (unlikely(priv->inited == gFalse))
+    init();
+
+  if (unlikely(priv->counter >= priv->npixels))
+    return EOF;
+
+  return ((unsigned char *)priv->image->comps[priv->ccounter].data)[priv->counter];
+}
+
+int JPXStream::lookChar() {
+  return doLookChar();
+}
+
+int JPXStream::doGetChar() {
+  int result = doLookChar();
+  if (++priv->ccounter == priv->ncomps) {
+    priv->ccounter = 0;
+    ++priv->counter;
+  }
+  return result;
+}
+
+GooString *JPXStream::getPSFilter(int psLevel, const char *indent) {
+  return NULL;
+}
+
+GBool JPXStream::isBinary(GBool last) {
+  return str->isBinary(gTrue);
+}
+
+void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) {
+  if (priv->inited == gFalse)
+    init();
+
+  *bitsPerComponent = 8;
+  if (priv->image && priv->image->numcomps == 3)
+    *csMode = streamCSDeviceRGB;
+  else
+    *csMode = streamCSDeviceGray;
+}
+
+
+static void libopenjpeg_error_callback(const char *msg, void * /*client_data*/) {
+  error(errSyntaxError, -1, "{0:s}", msg);
+}
+
+static void libopenjpeg_warning_callback(const char *msg, void * /*client_data*/) {
+  error(errSyntaxWarning, -1, "{0:s}", msg);
+}
+
+#ifdef USE_OPENJPEG1
+
 #define BUFFER_INITIAL_SIZE 4096
 
 void JPXStream::init()
@@ -75,29 +166,28 @@
   if (oLen.isInt()) bufSize = oLen.getInt();
   oLen.free();
 
-  
   int length = 0;
   unsigned char *buf = str->toUnsignedChars(&length, bufSize);
-  init2(buf, length, CODEC_JP2);
+  priv->init2(buf, length, CODEC_JP2);
   free(buf);
 
-  if (image) {
-    npixels = image->comps[0].w * image->comps[0].h;
-    ncomps = image->numcomps;
-    for (int component = 0; component < ncomps; component++) {
-      if (image->comps[component].data == NULL) {
+  if (priv->image) {
+    priv->npixels = priv->image->comps[0].w * priv->image->comps[0].h;
+    priv->ncomps = priv->image->numcomps;
+    for (int component = 0; component < priv->ncomps; component++) {
+      if (priv->image->comps[component].data == NULL) {
         close();
         break;
       }
-      unsigned char *cdata = (unsigned char *)image->comps[component].data;
+      unsigned char *cdata = (unsigned char *)priv->image->comps[component].data;
       int adjust = 0;
-      if (image->comps[component].prec > 8)
-	adjust = image->comps[component].prec - 8;
+      if (priv->image->comps[component].prec > 8)
+	adjust = priv->image->comps[component].prec - 8;
       int sgndcorr = 0;
-      if (image->comps[component].sgnd)
-	sgndcorr = 1 << (image->comps[0].prec - 1);
-      for (int i = 0; i < npixels; i++) {
-	int r = image->comps[component].data[i];
+      if (priv->image->comps[component].sgnd)
+	sgndcorr = 1 << (priv->image->comps[0].prec - 1);
+      for (int i = 0; i < priv->npixels; i++) {
+	int r = priv->image->comps[component].data[i];
 	r += sgndcorr;
 	if (adjust) {
 	  r = (r >> adjust)+((r >> (adjust-1))%2);
@@ -108,22 +198,14 @@
       }
     }
   } else
-    npixels = 0;
+    priv->npixels = 0;
 
-  counter = 0;
-  ccounter = 0;
-  inited = gTrue;
+  priv->counter = 0;
+  priv->ccounter = 0;
+  priv->inited = gTrue;
 }
 
-static void libopenjpeg_error_callback(const char *msg, void * /*client_data*/) {
-  error(errSyntaxError, -1, "{0:s}", msg);
-}
-
-static void libopenjpeg_warning_callback(const char *msg, void * /*client_data*/) {
-  error(errSyntaxWarning, -1, "{0:s}", msg);
-}
-
-void JPXStream::init2(unsigned char *buf, int bufLen, OPJ_CODEC_FORMAT format)
+void JPXStreamPrivate::init2(unsigned char *buf, int bufLen, OPJ_CODEC_FORMAT format)
 {
   opj_cio_t *cio = NULL;
 
@@ -174,24 +256,135 @@
     error(errSyntaxError, -1, "Did no succeed opening JPX Stream.");
   }
 }
+#endif
 
-int JPXStream::lookChar() {
-  return doLookChar();
+
+#ifdef USE_OPENJPEG2
+static OPJ_SIZE_T readStream_callback(void *buffer, OPJ_SIZE_T nBytes, void *userData)
+{
+  int len;
+  JPXStream *p = (JPXStream *)userData;
+
+  len = p->readStream(nBytes, (Guchar*)buffer);
+  if (len == 0)
+    return (OPJ_SIZE_T)-1;
+  else
+    return len;
 }
 
-GooString *JPXStream::getPSFilter(int psLevel, const char *indent) {
-  return NULL;
+void JPXStream::init()
+{
+  opj_stream_t *stream;
+
+  str->reset();
+  stream = opj_stream_default_create(OPJ_TRUE);
+
+#if OPENJPEG_VERSION >= OPENJPEG_VERSION_ENCODE(2, 1, 0)
+  opj_stream_set_user_data (stream, this, NULL);
+#else
+  opj_stream_set_user_data (stream, this);
+#endif
+
+  opj_stream_set_read_function(stream, readStream_callback);
+  priv->init2(stream, OPJ_CODEC_JP2);
+
+  opj_stream_destroy(stream);
+
+  if (priv->image) {
+    priv->npixels = priv->image->comps[0].w * priv->image->comps[0].h;
+    priv->ncomps = priv->image->numcomps;
+    for (int component = 0; component < priv->ncomps; component++) {
+      if (priv->image->comps[component].data == NULL) {
+        close();
+        break;
+      }
+      unsigned char *cdata = (unsigned char *)priv->image->comps[component].data;
+      int adjust = 0;
+      if (priv->image->comps[component].prec > 8)
+	adjust = priv->image->comps[component].prec - 8;
+      int sgndcorr = 0;
+      if (priv->image->comps[component].sgnd)
+	sgndcorr = 1 << (priv->image->comps[0].prec - 1);
+      for (int i = 0; i < priv->npixels; i++) {
+	int r = priv->image->comps[component].data[i];
+	r += sgndcorr;
+	if (adjust) {
+	  r = (r >> adjust)+((r >> (adjust-1))%2);
+	  if (unlikely(r > 255))
+	    r = 255;
+        }
+	*(cdata++) = r;
+      }
+    }
+  } else {
+    priv->npixels = 0;
+  }
+
+  priv->counter = 0;
+  priv->ccounter = 0;
+  priv->inited = gTrue;
 }
 
-GBool JPXStream::isBinary(GBool last) {
-  return str->isBinary(gTrue);
+void JPXStreamPrivate::init2(opj_stream_t *stream, OPJ_CODEC_FORMAT format)
+{
+  opj_codec_t *decoder;
+
+  /* Use default decompression parameters */
+  opj_dparameters_t parameters;
+  opj_set_default_decoder_parameters(&parameters);
+  parameters.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
+
+  /* Get the decoder handle of the format */
+  decoder = opj_create_decompress(format);
+  if (decoder == NULL) {
+    error(errSyntaxWarning, -1, "Unable to create decoder");
+    goto error;
+  }
+
+  /* Catch events using our callbacks */
+  opj_set_warning_handler(decoder, libopenjpeg_warning_callback, NULL);
+  opj_set_error_handler(decoder, libopenjpeg_error_callback, NULL);
+
+  /* Setup the decoder decoding parameters */
+  if (!opj_setup_decoder(decoder, &parameters)) {
+    error(errSyntaxWarning, -1, "Unable to set decoder parameters");
+    goto error;
+  }
+
+  /* Decode the stream and fill the image structure */
+  image = NULL;
+  if (!opj_read_header(stream, decoder, &image)) {
+    error(errSyntaxWarning, -1, "Unable to read header");
+    goto error;
+  }
+
+  /* Optional if you want decode the entire image */
+  if (!opj_set_decode_area(decoder, image, parameters.DA_x0,
+                           parameters.DA_y0, parameters.DA_x1, parameters.DA_y1)){
+    error(errSyntaxWarning, -1, "X2");
+    goto error;
+  }
+
+  /* Get the decoded image */
+  if (!(opj_decode(decoder, stream, image) && opj_end_decompress(decoder, stream))) {
+    error(errSyntaxWarning, -1, "Unable to decode image");
+    goto error;
+  }
+
+  opj_destroy_codec(decoder);
+
+  if (image != NULL)
+    return;
+
+error:
+  if (format == OPJ_CODEC_JP2) {
+    error(errSyntaxWarning, -1, "Did no succeed opening JPX Stream as JP2, trying as J2K.");
+    init2(stream, OPJ_CODEC_J2K);
+  } else if (format == OPJ_CODEC_J2K) {
+    error(errSyntaxWarning, -1, "Did no succeed opening JPX Stream as J2K, trying as JPT.");
+    init2(stream, OPJ_CODEC_JPT);
+  } else {
+    error(errSyntaxError, -1, "Did no succeed opening JPX Stream.");
+  }
 }
-
-void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) {
-  if (inited == gFalse) init();
-
-  *bitsPerComponent = 8;
-  if (image && image->numcomps == 3) *csMode = streamCSDeviceRGB;
-  else *csMode = streamCSDeviceGray;
-}
-
+#endif
diff --git a/poppler/JPEG2000Stream.h b/poppler/JPEG2000Stream.h
index 3feccbe..50b7586 100644
--- a/poppler/JPEG2000Stream.h
+++ b/poppler/JPEG2000Stream.h
@@ -6,7 +6,7 @@
 //
 // Copyright 2008, 2010 Albert Astals Cid <aacid@kde.org>
 // Copyright 2011 Daniel Glöckner <daniel-gl@gmx.net>
-// Copyright 2013 Adrian Johnson <ajohnson@redneon.com>
+// Copyright 2013,2014 Adrian Johnson <ajohnson@redneon.com>
 //
 // Licensed under GPLv2 or later
 //
@@ -16,12 +16,13 @@
 #ifndef JPEG2000STREAM_H
 #define JPEG2000STREAM_H
 
-#include <openjpeg.h>
-
+#include "config.h"
 #include "goo/gtypes.h"
 #include "Object.h"
 #include "Stream.h"
 
+struct JPXStreamPrivate;
+
 class JPXStream: public FilterStream {
 public:
 
@@ -37,37 +38,21 @@
   virtual GBool isBinary(GBool last = gTrue);
   virtual void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode);
 
+  int readStream(int nChars, Guchar *buffer) {
+    return str->doGetChars(nChars, buffer);
+  }
 private:
-  void init();
-  void init2(unsigned char *buf, int bufLen, OPJ_CODEC_FORMAT format);
+  JPXStream(const JPXStream &other);
+  JPXStream& operator=(const JPXStream &other);
+  JPXStreamPrivate *priv;
 
+  void init();
   virtual GBool hasGetChars() { return true; }
   virtual int getChars(int nChars, Guchar *buffer);
 
-  inline int doGetChar() {
-    int result = doLookChar();
-    if (++ccounter == ncomps) {
-      ccounter = 0;
-      ++counter;
-    }
-    return result;
-  }
+  int doGetChar();
 
-  inline int doLookChar() {
-    if (unlikely(inited == gFalse)) init();
-
-    if (unlikely(counter >= npixels)) return EOF;
-
-    return ((unsigned char *)image->comps[ccounter].data)[counter];
-  }
-
-  opj_image_t *image;
-  opj_dinfo_t *dinfo;
-  int counter;
-  int ccounter;
-  int npixels;
-  int ncomps;
-  GBool inited;
+  int doLookChar();
 };
 
 #endif