diff --git a/autobuild.xml b/autobuild.xml
index de34be725d84528c6b50435cf19d078df496749b..355ce178b3fbd32603d25f8b8a26f297e36a4117 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -2112,7 +2112,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
       <key>openjpeg</key>
       <map>
         <key>copyright</key>
-        <string>Copyright (c) 2002-2012, Communications and Remote Sensing Laboratory, Universite catholique de Louvain (UCL), Belgium, Copyright (c) 2002-2012, Professor Benoit Macq, Copyright (c) 2003-2012, Antonin Descampe, Copyright (c) 2003-2009, Francois-Olivier Devaux, Copyright (c) 2005, Herve Drolon, FreeImage Team, Copyright (c) 2002-2003, Yannick Verschueren, Copyright (c) 2001-2003, David Janssens</string>
+        <string>Copyright (c) 2002-2014, Universite catholique de Louvain, Belgium, Copyright (c) 2002-2012, Professor Benoit Macq, Copyright (c) 2003-2012, Antonin Descampe, Copyright (c) 2003-2009, Francois-Olivier Devaux, Copyright (c) 2005, Herve Drolon, FreeImage Team, Copyright (c) 2002-2003, Yannick Verschueren, Copyright (c) 2001-2003, David Janssens</string>
         <key>description</key>
         <string>The OpenJPEG library is an open-source JPEG 2000 codec written in C language.</string>
         <key>license</key>
@@ -2142,11 +2142,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>60a1ca9b97ad74a8e3a33d33caf503cdb5e7f1684d6316ed484471c02124d603878c8526f7166a16064cd7c88b9905db</string>
+              <string>80c7062c95c45e6851ddbdf19f61885280728ec40a5dfdcfbdaea9c4eed0bfe7dcbfb71b7e7e6d01fff4eb357f7c02ae</string>
               <key>hash_algorithm</key>
               <string>sha3_384</string>
               <key>url</key>
-              <string>https://git.alchemyviewer.org/api/v4/projects/105/packages/generic/openjpeg-641/1.5.1/openjpeg-1.5.1-linux64-641.tar.xz</string>
+              <string>https://git.alchemyviewer.org/api/v4/projects/147/packages/generic/openjpeg-753/2.4.0/openjpeg-2.4.0-linux64-753.tar.xz</string>
             </map>
             <key>name</key>
             <string>linux64</string>
@@ -2156,11 +2156,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>474913320ea038660a73810b6563d0b2174cef5a9445f3454fb6e8d9efac89fddb9fb0be007001a434ec3c8125917f6d</string>
+              <string>d6984ea9ff046e2e541071182833365cc0d15edfcad59c4ded1f15c3d1ba88f3627c784f5c0f789a003244a816788cde</string>
               <key>hash_algorithm</key>
               <string>sha3_384</string>
               <key>url</key>
-              <string>https://git.alchemyviewer.org/api/v4/projects/105/packages/generic/openjpeg-641/1.5.1/openjpeg-1.5.1-windows-641.tar.xz</string>
+              <string>https://git.alchemyviewer.org/api/v4/projects/147/packages/generic/openjpeg-753/2.4.0/openjpeg-2.4.0-windows-753.tar.xz</string>
             </map>
             <key>name</key>
             <string>windows</string>
@@ -2170,18 +2170,18 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>044b5655aecdc9e22f013509ec3a0a8297955cfc993465256e182d519876d55bc3b33df56b861f587a8086e79b8aa090</string>
+              <string>993058a8a028842c7e65c49612c893fcf3e6961eb13b9be88a2080f6f5b4b782791bd5bb3eed79d05ca3734b26b0102e</string>
               <key>hash_algorithm</key>
               <string>sha3_384</string>
               <key>url</key>
-              <string>https://git.alchemyviewer.org/api/v4/projects/105/packages/generic/openjpeg-641/1.5.1/openjpeg-1.5.1-windows64-641.tar.xz</string>
+              <string>https://git.alchemyviewer.org/api/v4/projects/147/packages/generic/openjpeg-753/2.4.0/openjpeg-2.4.0-windows64-753.tar.xz</string>
             </map>
             <key>name</key>
             <string>windows64</string>
           </map>
         </map>
         <key>version</key>
-        <string>1.5.1</string>
+        <string>2.4.0</string>
       </map>
       <key>openssl</key>
       <map>
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index 8b7ac2f71b2d3eae0237305fda7b429c287e58f7..0dd49e1a31f37bfc2685ec8c4feb9254731b718d 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -81,7 +81,7 @@ if(WINDOWS)
         libxml2.dll
         minizip.dll
         nghttp2.dll
-        openjpeg.dll
+        openjp2.dll
         ssleay32.dll
         uriparser.dll
         xmlrpc-epi.dll
diff --git a/indra/cmake/OpenJPEG.cmake b/indra/cmake/OpenJPEG.cmake
index dc92cdb4110592a734aaafd8bf8474833f9a7bbd..46dac7ec0f565538dfd214e9911371e1213fdf62 100644
--- a/indra/cmake/OpenJPEG.cmake
+++ b/indra/cmake/OpenJPEG.cmake
@@ -10,13 +10,10 @@ else (USESYSTEMLIBS)
   use_prebuilt_binary(openjpeg)
   
   if(WINDOWS)
-    # Windows has differently named release and debug openjpeg(d) libs.
-    set(OPENJPEG_LIBRARIES 
-        debug openjpegd
-        optimized openjpeg)
+    set(OPENJPEG_LIBRARIES openjp2)
   else(WINDOWS)
-    set(OPENJPEG_LIBRARIES openjpeg)
+    set(OPENJPEG_LIBRARIES openjp2)
   endif(WINDOWS)
   
-    set(OPENJPEG_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/openjpeg-1.5)
+    set(OPENJPEG_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/openjpeg)
 endif (USESYSTEMLIBS)
diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt
index d7706e73b24ed6033609482a852323aaf077b04b..3957ede77f01d49af6eabc3cd7d389c223c4d8d8 100644
--- a/indra/integration_tests/llui_libtest/CMakeLists.txt
+++ b/indra/integration_tests/llui_libtest/CMakeLists.txt
@@ -99,14 +99,14 @@ if (WINDOWS)
     # Copy over OpenJPEG.dll
     # *NOTE: On Windows with VS2005, only the first comment prints
     set(OPENJPEG_RELEASE
-        "${ARCH_PREBUILT_DIRS_RELEASE}/openjpeg.dll")
+        "${ARCH_PREBUILT_DIRS_RELEASE}/openjp2.dll")
     add_custom_command( TARGET llui_libtest POST_BUILD
         COMMAND ${CMAKE_COMMAND} -E copy_if_different 
             ${OPENJPEG_RELEASE} ${CMAKE_CURRENT_BINARY_DIR}
         COMMENT "Copying OpenJPEG DLLs to binary directory"
         )
     set(OPENJPEG_DEBUG
-        "${ARCH_PREBUILT_DIRS_DEBUG}/openjpegd.dll")
+        "${ARCH_PREBUILT_DIRS_DEBUG}/openjp2.dll")
     add_custom_command( TARGET llui_libtest POST_BUILD
         COMMAND ${CMAKE_COMMAND} -E copy_if_different 
             ${OPENJPEG_DEBUG} ${CMAKE_CURRENT_BINARY_DIR}
diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp
index 6e5e9d3b5b3a66370542e49066e647cf2b215e87..4db064c21ee9222186f33eb359703d6531e7edc6 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -29,9 +29,134 @@
 
 // this is defined so that we get static linking.
 #include "openjpeg.h"
+#include "cio.h"
+#include "event.h"
 
 #include "lltimer.h"
-//#include "llmemory.h"
+
+struct LLJp2StreamReader 
+{
+	LLJp2StreamReader(LLImageJ2C* pImage) : m_pImage(pImage), m_Position(0) { }
+
+	static OPJ_SIZE_T readStream(void* pBufferOut, OPJ_SIZE_T szBufferOut, void* pUserData)
+	{
+		LLJp2StreamReader* pStream = static_cast<LLJp2StreamReader*>(pUserData);
+		if (!pBufferOut || !pStream || !pStream->m_pImage)
+			return (OPJ_SIZE_T)-1;
+
+		OPJ_SIZE_T szBufferRead = llmin(szBufferOut, pStream->m_pImage->getDataSize() - pStream->m_Position);
+		if (!szBufferRead)
+			return (OPJ_SIZE_T)-1;
+
+		memcpy(pBufferOut, pStream->m_pImage->getData() + pStream->m_Position, szBufferRead);
+		pStream->m_Position += szBufferRead;
+		return szBufferRead;
+	}
+
+	static OPJ_OFF_T skipStream(OPJ_OFF_T bufferOffset, void* pUserData)
+	{
+		LLJp2StreamReader* pStream = static_cast<LLJp2StreamReader*>(pUserData);
+		if (!pStream || !pStream->m_pImage)
+			return (OPJ_OFF_T)-1;
+
+		if (bufferOffset < 0)
+		{
+			// Skipping backward
+			if (pStream->m_Position == 0)
+				return (OPJ_OFF_T)-1;              // Already at the start of the stream
+			else if (pStream->m_Position + bufferOffset < 0)
+				bufferOffset = -(OPJ_OFF_T)pStream->m_Position; // Don't underflow
+		}
+		else
+		{
+			// Skipping forward
+			OPJ_SIZE_T szRemaining = pStream->m_pImage->getDataSize() - pStream->m_Position;
+			if (!szRemaining)
+				return (OPJ_OFF_T)-1;              // Already at the end of the stream
+			else if (bufferOffset > szRemaining)
+				bufferOffset = szRemaining;          // Don't overflow
+		}
+		pStream->m_Position += bufferOffset;
+
+		return bufferOffset;
+	}
+
+	static OPJ_BOOL seekStream(OPJ_OFF_T bufferOffset, void* pUserData)
+	{
+		LLJp2StreamReader* pStream = static_cast<LLJp2StreamReader*>(pUserData);
+		if (!pStream || !pStream->m_pImage)
+			return OPJ_FALSE;
+
+		if (bufferOffset < 0 || bufferOffset > pStream->m_pImage->getDataSize())
+			return OPJ_FALSE;
+
+		pStream->m_Position = bufferOffset;
+		return OPJ_TRUE;
+	}
+
+	LLImageJ2C* m_pImage = nullptr;
+	OPJ_SIZE_T  m_Position = 0;
+};
+
+struct LLJp2StreamWriter 
+{
+	LLJp2StreamWriter(LLImageJ2C* pImage) : m_pImage(pImage), m_Position(0) { }
+
+	static OPJ_SIZE_T writeStream(void* pBufferIn, OPJ_SIZE_T szBufferIn, void* pUserData)
+	{
+		LLJp2StreamReader* pStream = static_cast<LLJp2StreamReader*>(pUserData);
+		if (!pBufferIn || !pStream || !pStream->m_pImage)
+			return (OPJ_SIZE_T)-1;
+
+		if (pStream->m_Position + szBufferIn > pStream->m_pImage->getDataSize())
+			pStream->m_pImage->reallocateData(pStream->m_Position + szBufferIn);
+
+		memcpy(pStream->m_pImage->getData() + pStream->m_Position, pBufferIn, szBufferIn);
+		pStream->m_Position += szBufferIn;
+		return szBufferIn;
+	}
+
+	static OPJ_OFF_T skipStream(OPJ_OFF_T bufferOffset, void* pUserData)
+	{
+		LLJp2StreamReader* pStream = static_cast<LLJp2StreamReader*>(pUserData);
+		if (!pStream || !pStream->m_pImage)
+			return -1;
+
+		if (bufferOffset < 0)
+		{
+			// Skipping backward
+			if (pStream->m_Position == 0)
+				return -1;                           // Already at the start of the stream
+			else if (pStream->m_Position + bufferOffset < 0)
+				bufferOffset = -(OPJ_OFF_T)pStream->m_Position; // Don't underflow
+		}
+		else
+		{
+			// Skipping forward
+			if (pStream->m_Position + bufferOffset > pStream->m_pImage->getDataSize())
+				return -1;                           // Don't allow skipping past the end of the stream
+		}
+
+		pStream->m_Position += bufferOffset;
+		return bufferOffset;
+	}
+
+	static OPJ_BOOL seekStream(OPJ_OFF_T bufferOffset, void* pUserData)
+	{
+		LLJp2StreamReader* pStream = static_cast<LLJp2StreamReader*>(pUserData);
+		if (!pStream || !pStream->m_pImage)
+			return OPJ_FALSE;
+
+		if (bufferOffset < 0 || bufferOffset > pStream->m_pImage->getDataSize())
+			return OPJ_FALSE;
+
+		pStream->m_Position = bufferOffset;
+		return OPJ_TRUE;
+	}
+
+	LLImageJ2C* m_pImage = nullptr;
+	OPJ_OFF_T m_Position = 0;
+};
 
 // Factory function: see declaration in llimagej2c.cpp
 LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl()
@@ -41,11 +166,10 @@ LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl()
 
 std::string LLImageJ2COJ::getEngineInfo() const
 {
-#ifdef OPENJPEG_VERSION
-	return std::string("OpenJPEG: " OPENJPEG_VERSION ", Runtime: ")
-		+ opj_version();
+#ifdef OPJ_PACKAGE_VERSION
+	return std::string("OpenJPEG: " OPJ_PACKAGE_VERSION ", Runtime: ") + opj_version();
 #else
-	return std::string("OpenJPEG runtime: ") + opj_version();
+	return std::string("OpenJPEG Runtime: ") + opj_version();
 #endif
 }
 
@@ -70,21 +194,21 @@ sample error callback expecting a LLFILE* client object
 */
 void error_callback(const char* msg, void*)
 {
-	LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
+	LL_WARNS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
 }
 /**
 sample warning callback expecting a LLFILE* client object
 */
 void warning_callback(const char* msg, void*)
 {
-	LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
+	LL_WARNS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
 }
 /**
 sample debug callback expecting no client object
 */
 void info_callback(const char* msg, void*)
 {
-	LL_DEBUGS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
+	LL_INFOS() << "LLImageJ2COJ: " << chomp(msg) << LL_ENDL;
 }
 
 // Divide a by 2 to the power of b and round upwards
@@ -93,13 +217,11 @@ int ceildivpow2(int a, int b)
 	return (a + (1 << b) - 1) >> b;
 }
 
-
 LLImageJ2COJ::LLImageJ2COJ()
 	: LLImageJ2CImpl()
 {
 }
 
-
 bool LLImageJ2COJ::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level, int* region)
 {
 	// No specific implementation for this method in the OpenJpeg case
@@ -114,26 +236,10 @@ bool LLImageJ2COJ::initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int block
 
 bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count)
 {
-	//
-	// FIXME: Get the comment field out of the texture
-	//
-
 	LLTimer decode_timer;
 
 	opj_dparameters_t parameters;	/* decompression parameters */
-	opj_event_mgr_t event_mgr = { };		/* event manager */
-	opj_image_t *image = nullptr;
-
-	opj_dinfo_t* dinfo = nullptr;	/* handle to a decompressor */
-	opj_cio_t *cio = nullptr;
-
-
-#if SHOW_DEBUG
-	/* configure the event callbacks (not required) */
-	event_mgr.error_handler = error_callback;
-	event_mgr.warning_handler = warning_callback;
-	event_mgr.info_handler = info_callback;
-#endif
+	opj_image_t *image = NULL;
 
 	/* set decoding parameters to default values */
 	opj_set_default_decoder_parameters(&parameters);
@@ -146,37 +252,57 @@ bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
 	/* JPEG-2000 codestream */
 
 	/* get a decoder handle */
-	dinfo = opj_create_decompress(CODEC_J2K);
+	opj_codec_t* opj_decoder_p = opj_create_decompress(OPJ_CODEC_J2K);
+	if (!opj_decoder_p)
+	{
+		LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to create decoder!" << LL_ENDL;
+		base.decodeFailed();
+		return true; // done
+	}
 
+#ifdef SHOW_DEBUG
 	/* catch events using our callbacks and give a local context */
-	opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);			
+	opj_set_error_handler(opj_decoder_p, error_callback, nullptr);
+	opj_set_warning_handler(opj_decoder_p, warning_callback, nullptr);
+	opj_set_info_handler(opj_decoder_p, info_callback, nullptr);
+#endif
 
 	/* setup the decoder decoding parameters using user parameters */
-	opj_setup_decoder(dinfo, &parameters);
+	if (!opj_setup_decoder(opj_decoder_p, &parameters))
+	{
+		LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image!" << LL_ENDL;
+		opj_destroy_codec(opj_decoder_p);
+		base.decodeFailed();
+		return true; // done
+	}
 
 	/* open a byte stream */
-	cio = opj_cio_open((opj_common_ptr)dinfo, base.getData(), base.getDataSize());
+	LLJp2StreamReader streamReader(&base);
+	opj_stream_t* opj_stream_p = opj_stream_default_create(OPJ_STREAM_READ);
+	opj_stream_set_read_function(opj_stream_p, LLJp2StreamReader::readStream);
+	opj_stream_set_skip_function(opj_stream_p, LLJp2StreamReader::skipStream);
+	opj_stream_set_seek_function(opj_stream_p, LLJp2StreamReader::seekStream);
+	opj_stream_set_user_data(opj_stream_p, &streamReader, nullptr);
+	opj_stream_set_user_data_length(opj_stream_p, base.getDataSize());
 
 	/* decode the stream and fill the image structure */
-	image = opj_decode(dinfo, cio);
+	bool success = opj_read_header(opj_stream_p, opj_decoder_p, &image) &&
+	                opj_decode(opj_decoder_p, opj_stream_p, image) &&
+					opj_end_decompress(opj_decoder_p, opj_stream_p);
 
 	/* close the byte stream */
-	opj_cio_close(cio);
+	opj_stream_destroy(opj_stream_p);
 
 	/* free remaining structures */
-	if(dinfo)
-	{
-		opj_destroy_decompress(dinfo);
-	}
+	opj_destroy_codec(opj_decoder_p);
+
 
 	// The image decode failed if the return was NULL or the component
 	// count was zero.  The latter is just a sanity check before we
 	// dereference the array.
-	if(!image || !image->numcomps)
+	if (!success || !image || !image->numcomps)
 	{
-#if SHOW_DEBUG
 		LL_DEBUGS("Texture") << "ERROR -> decodeImpl: failed to decode image!" << LL_ENDL;
-#endif
 		if (image)
 		{
 			opj_image_destroy(image);
@@ -185,23 +311,15 @@ bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
 		return true; // done
 	}
 
-	// sometimes we get bad data out of the cache - check to see if the decode succeeded
-	for (S32 i = 0; i < image->numcomps; i++)
+	if(image->numcomps <= first_channel)
 	{
-		if (image->comps[i].factor != base.getRawDiscardLevel())
+		LL_WARNS() << "trying to decode more channels than are present in image: numcomps: " << image->numcomps << " first_channel: " << first_channel << LL_ENDL;
+		if (image)
 		{
-			// if we didn't get the discard level we're expecting, fail
 			opj_image_destroy(image);
-			base.decodeFailed();
-			return true;
 		}
-	}
-	
-	if(image->numcomps <= first_channel)
-	{
-		LL_WARNS() << "trying to decode more channels than are present in image: numcomps: " << image->numcomps << " first_channel: " << first_channel << LL_ENDL;
-		opj_image_destroy(image);
 		base.decodeFailed();
+
 		return true;
 	}
 
@@ -273,16 +391,6 @@ bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, con
 {
 	const S32 MAX_COMPS = 5;
 	opj_cparameters_t parameters;	/* compression parameters */
-	opj_event_mgr_t event_mgr = { };		/* event manager */
-
-
-	/* 
-	configure the event callbacks (not required)
-	setting of each callback is optional 
-	*/
-	event_mgr.error_handler = error_callback;
-	event_mgr.warning_handler = warning_callback;
-	event_mgr.info_handler = info_callback;
 
 	/* set encoding parameters to default values */
 	opj_set_default_encoder_parameters(&parameters);
@@ -322,9 +430,9 @@ bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, con
 	//
 	// Fill in the source image from our raw image
 	//
-	OPJ_COLOR_SPACE color_space = CLRSPC_SRGB;
+	OPJ_COLOR_SPACE color_space = OPJ_CLRSPC_SRGB;
 	opj_image_cmptparm_t cmptparm[MAX_COMPS];
-	opj_image_t * image = nullptr;
+	opj_image_t * image = NULL;
 	S32 numcomps = raw_image.getComponents();
 	S32 width = raw_image.getWidth();
 	S32 height = raw_image.getHeight();
@@ -362,52 +470,61 @@ bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, con
 		}
 	}
 
-
-
 	/* encode the destination image */
 	/* ---------------------------- */
 
-	int codestream_length;
-	opj_cio_t *cio = nullptr;
-
 	/* get a J2K compressor handle */
-	opj_cinfo_t* cinfo = opj_create_compress(CODEC_J2K);
+	opj_codec_t* opj_encoder_p = opj_create_compress(OPJ_CODEC_J2K);
 
+#ifdef SHOW_DEBUG
 	/* catch events using our callbacks and give a local context */
-	opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, stderr);			
+	opj_set_error_handler(opj_encoder_p, error_callback, nullptr);
+	opj_set_warning_handler(opj_encoder_p, warning_callback, nullptr);
+	opj_set_info_handler(opj_encoder_p, info_callback, nullptr);
+#endif
 
 	/* setup the encoder parameters using the current image and using user parameters */
-	opj_setup_encoder(cinfo, &parameters, image);
+	if (!opj_setup_encoder(opj_encoder_p, &parameters, image))
+	{
+		opj_destroy_codec(opj_encoder_p);
+		opj_image_destroy(image);
+		LL_DEBUGS("Texture") << "Failed to encode image." << LL_ENDL;
+		return false;
+	}
 
 	/* open a byte stream for writing */
 	/* allocate memory for all tiles */
-	cio = opj_cio_open((opj_common_ptr)cinfo, nullptr, 0);
+	LLJp2StreamWriter streamWriter(&base);
+	opj_stream_t* opj_stream_p = opj_stream_default_create(OPJ_STREAM_WRITE);
+	opj_stream_set_write_function(opj_stream_p, LLJp2StreamWriter::writeStream);
+	opj_stream_set_skip_function(opj_stream_p, LLJp2StreamWriter::skipStream);
+	opj_stream_set_seek_function(opj_stream_p, LLJp2StreamWriter::seekStream);
+	opj_stream_set_user_data(opj_stream_p, &streamWriter, nullptr);
+	opj_stream_set_user_data_length(opj_stream_p, raw_image.getDataSize());
 
 	/* encode the image */
-	bool bSuccess = opj_encode(cinfo, cio, image, nullptr);
-	if (!bSuccess)
+	if (!opj_start_compress(opj_encoder_p, image, opj_stream_p) ||
+		!opj_encode(opj_encoder_p, opj_stream_p) ||
+		!opj_end_compress(opj_encoder_p, opj_stream_p))
 	{
-		opj_cio_close(cio);
+		opj_stream_destroy(opj_stream_p);
+		opj_destroy_codec(opj_encoder_p);
+		opj_image_destroy(image);
 		LL_DEBUGS("Texture") << "Failed to encode image." << LL_ENDL;
 		return false;
 	}
-	codestream_length = cio_tell(cio);
 
-	base.copyData(cio->buffer, codestream_length);
 	base.updateData(); // set width, height
 
 	/* close and free the byte stream */
-	opj_cio_close(cio);
+	opj_stream_destroy(opj_stream_p);
 
 	/* free remaining compression structures */
-	opj_destroy_compress(cinfo);
-
-
-	/* free user parameters structure */
-	if(parameters.cp_matrice) free(parameters.cp_matrice);
+	opj_destroy_codec(opj_encoder_p);
 
 	/* free image data */
 	opj_image_destroy(image);
+
 	return true;
 }
 
@@ -490,54 +607,69 @@ bool LLImageJ2COJ::getMetadata(LLImageJ2C &base)
 	// Do it the old and slow way, decode the image with openjpeg
 
 	opj_dparameters_t parameters;	/* decompression parameters */
-	opj_event_mgr_t event_mgr = { };		/* event manager */
 	opj_image_t *image = nullptr;
-
-	opj_dinfo_t* dinfo = nullptr;	/* handle to a decompressor */
-	opj_cio_t *cio = nullptr;
-
-
-	/* configure the event callbacks (not required) */
-	event_mgr.error_handler = error_callback;
-	event_mgr.warning_handler = warning_callback;
-	event_mgr.info_handler = info_callback;
+	opj_codec_t *opj_decoder = nullptr;	/* handle to a decompressor */
 
 	/* set decoding parameters to default values */
 	opj_set_default_decoder_parameters(&parameters);
 
-	// Only decode what's required to get the size data.
-	parameters.cp_limit_decoding=LIMIT_TO_MAIN_HEADER;
-
-	//parameters.cp_reduce = mRawDiscardLevel;
-
 	/* decode the code-stream */
 	/* ---------------------- */
 
 	/* JPEG-2000 codestream */
 
 	/* get a decoder handle */
-	dinfo = opj_create_decompress(CODEC_J2K);
+	opj_decoder = opj_create_decompress(OPJ_CODEC_J2K);
+	if (opj_decoder)
+	{
+		LL_WARNS() << "ERROR -> getMetadata: failed to create decoder!" << LL_ENDL;
+		return false;
+	}
 
+#ifdef SHOW_DEBUG
 	/* catch events using our callbacks and give a local context */
-	opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, stderr);			
+	opj_set_error_handler(opj_decoder, error_callback, nullptr);
+	opj_set_warning_handler(opj_decoder, warning_callback, nullptr);
+	opj_set_info_handler(opj_decoder, info_callback, nullptr);
+#endif
 
 	/* setup the decoder decoding parameters using user parameters */
-	opj_setup_decoder(dinfo, &parameters);
+	if (!opj_setup_decoder(opj_decoder, &parameters))
+	{
+		LL_WARNS() << "ERROR -> getMetadata: failed to decode image!" << LL_ENDL;
+		opj_destroy_codec(opj_decoder);
+		return false;
+	}
 
 	/* open a byte stream */
-	cio = opj_cio_open((opj_common_ptr)dinfo, base.getData(), base.getDataSize());
+	LLJp2StreamReader streamReader(&base);
+	opj_stream_t* decode_stream = opj_stream_default_create(OPJ_STREAM_READ);
+	opj_stream_set_read_function(decode_stream, LLJp2StreamReader::readStream);
+	opj_stream_set_skip_function(decode_stream, LLJp2StreamReader::skipStream);
+	opj_stream_set_seek_function(decode_stream, LLJp2StreamReader::seekStream);
+	opj_stream_set_user_data(decode_stream, &streamReader, nullptr);
+	opj_stream_set_user_data_length(decode_stream, base.getDataSize());
 
 	/* decode the stream and fill the image structure */
-	image = opj_decode(dinfo, cio);
+	if (!opj_read_header(decode_stream, opj_decoder, &image))
+	{
+		LL_WARNS() << "ERROR -> getMetadata: failed to decode image!" << LL_ENDL;
+
+		opj_stream_destroy(decode_stream);
+		opj_destroy_codec(opj_decoder);
+
+		if (image)
+		{
+			opj_image_destroy(image);
+		}
+		return false;
+	}
 
 	/* close the byte stream */
-	opj_cio_close(cio);
+	opj_stream_destroy(decode_stream);
 
 	/* free remaining structures */
-	if(dinfo)
-	{
-		opj_destroy_decompress(dinfo);
-	}
+	opj_destroy_codec(opj_decoder);
 
 	if(!image)
 	{
@@ -546,7 +678,6 @@ bool LLImageJ2COJ::getMetadata(LLImageJ2C &base)
 	}
 
 	// Copy image data into our raw image format (instead of the separate channel format
-
 	img_components = image->numcomps;
 	width = image->x1 - image->x0;
 	height = image->y1 - image->y0;
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index d8451407390a89287077f78d504b616be538655f..25579a0c929e252941955da545b5c4dc2468eb64 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -1890,8 +1890,8 @@ if (WINDOWS)
       ${SHARED_LIB_STAGING_DIR}/Release/libcollada14dom23.dll
       ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libcollada14dom23.dll
       #${SHARED_LIB_STAGING_DIR}/Debug/libcollada14dom23-d.dll
-      ${SHARED_LIB_STAGING_DIR}/Release/openjpeg.dll
-      ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjpeg.dll
+      ${SHARED_LIB_STAGING_DIR}/Release/openjp2.dll
+      ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/openjp2.dll
       #${SHARED_LIB_STAGING_DIR}/Debug/openjpegd.dll
       ${SHARED_LIB_STAGING_DIR}/Release/libhunspell.dll
       ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libhunspell.dll
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 14a4017ab796afee413015adf0a33e8cd93bb09d..2878dc510a25a6e75e683384ad2a5a6557565742 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -531,7 +531,7 @@ def construct(self):
             self.path("jpeg8.dll")
             self.path("libpng16*.dll")
             self.path("libwebp.dll")
-            self.path("openjpeg.dll")
+            self.path("openjp2.dll")
 
             # For OpenGL extensions
             self.path("epoxy-0.dll")
@@ -933,7 +933,7 @@ def construct(self):
                                 'libhunspell-*.dylib',
                                 'libndofdev.dylib',
                                 'libogg.*.dylib',
-                                'libopenjpeg.*.dylib',
+                                'libopenjp2.*.dylib',
                                 'liburiparser.*.dylib',
                                 'libvorbis.*.dylib',
                                 'libvorbisenc.*.dylib',
@@ -1474,7 +1474,7 @@ def construct(self):
             self.path("libexpat.so.*")
             self.path("libGLOD.so")
             self.path("libSDL2*.so*")
-            self.path("libopenjpeg.so*")
+            self.path("libopenjp2.*so*")
             self.path("libepoxy.so")
             self.path("libepoxy.so.0")
             self.path("libepoxy.so.0.0.0")
@@ -1516,7 +1516,7 @@ def construct(self):
             self.path("libexpat.so.*")
             self.path("libGLOD.so")
             self.path("libSDL2*.so*")
-            self.path("libopenjpeg.so*")
+            self.path("libopenjp2.*so*")
             self.path("libepoxy.so")
             self.path("libepoxy.so.0")
             self.path("libepoxy.so.0.0.0")