From cb2ce97afa2f18427c1b4b3857fd56f69ce83d97 Mon Sep 17 00:00:00 2001
From: mio <stigma@disroot.org>
Date: Sat, 21 Sep 2024 12:10:59 +1000
Subject: Improve JasPer 3/4 support

Versions prior to 3 registered an atexit hander which performed resource
cleanup with 'jas_cleanup', this was removed in version 3 meaning we
were leaking resources.

The 'jas_init' and 'jas_cleanup' functions were deprecated in version 3,
so we use the newer jas_init_library/jas_init_thread functions.

The max memory that JasPer can use has been limited to (at most) 512 MB.
Without this change, JasPer will use whatever JAS_DEFAULT_MAX_MEM_USAGE
is configured to, which is 1 GB by default.

Signed-off-by: mio <stigma@disroot.org>
---
 kimgio/jp2.cpp | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 84 insertions(+), 7 deletions(-)

diff --git a/kimgio/jp2.cpp b/kimgio/jp2.cpp
index ff64f9263..7cd6879b1 100644
--- a/kimgio/jp2.cpp
+++ b/kimgio/jp2.cpp
@@ -15,6 +15,7 @@
 #ifdef HAVE_STDINT_H
 #include <stdint.h>
 #endif
+#include <kdebug.h>
 #include <tdetempfile.h>
 #include <tqcolor.h>
 #include <tqcstring.h>
@@ -57,7 +58,7 @@ read_image( const TQImageIO* io )
         tempf = new KTempFile();
         if( tempf->status() != 0 ) {
             delete tempf;
-            return 0;
+            return nullptr;
         } // if
         tempf->setAutoDelete( true );
         TQFile* out = tempf->file();
@@ -76,7 +77,7 @@ read_image( const TQImageIO* io )
     } // else
     if( !in ) {
         delete tempf;
-        return 0;
+        return nullptr;
     } // if
 
     jas_image_t* image = jas_image_decode( in, -1, 0 );
@@ -148,16 +149,77 @@ render_view( gs_t& gs, TQImage& qti )
     return true;
 } // render_view
 
+static bool initializeJasper()
+{
+#if defined(JAS_VERSION_MAJOR) && (JAS_VERSION_MAJOR >= 3)
+	jas_conf_clear();
+
+	// Limit JasPer memory usage to at most 512 MB
+	size_t memoryLimit = (512 * 1024) * 1024;
+	size_t jasperTotalMemory = jas_get_total_mem_size();
+	if (!jasperTotalMemory)
+	{
+		jasperTotalMemory = JAS_DEFAULT_MAX_MEM_USAGE;
+	}
+	memoryLimit = memoryLimit < jasperTotalMemory ? memoryLimit : jasperTotalMemory;
+	jas_conf_set_max_mem_usage(memoryLimit);
+
+	if (jas_init_library())
+	{
+		return false;
+	}
+
+	if (jas_init_thread())
+	{
+		jas_cleanup_library();
+		return false;
+	}
+
+#else
+	if (jas_init())
+	{
+		return false;
+	}
+#endif  // defined(JAS_VERSION_MAJOR) && (JAS_VERSION_MAJOR >= 3)
+
+	return true;
+}
+
+static void cleanupJasper()
+{
+#if defined(JAS_VERSION_MAJOR) && (JAS_VERSION_MAJOR >= 3)
+	jas_cleanup_thread();
+	jas_cleanup_library();
+#endif
+}
+
+
 
 TDE_EXPORT void
 kimgio_jp2_read( TQImageIO* io )
 {
-	if( jas_init() ) return;
+	if (!initializeJasper())
+	{
+		kdError(399) << "Failed to initialize JasPer library" << endl;
+		return;
+	}
 
 	gs_t gs;
-	if( !(gs.image = read_image( io )) ) return;
+	gs.image = read_image(io);
 
-	if( !convert_colorspace( gs ) ) return;
+	if (!gs.image)
+	{
+		kdError(399) << "Failed to read JP2 image from IO." << endl;
+		cleanupJasper();
+		return;
+	}
+
+	if (!convert_colorspace(gs))
+	{
+		kdError(399) << "Could not convert JP2 colorspace." << endl;
+		cleanupJasper();
+		return;
+	}
 
 	TQImage image;
 	render_view( gs, image );
@@ -165,6 +227,8 @@ kimgio_jp2_read( TQImageIO* io )
 	if( gs.image ) jas_image_destroy( gs.image );
 	if( gs.altimage ) jas_image_destroy( gs.altimage );
 
+	cleanupJasper();
+
 	io->setImage( image );
 	io->setStatus( 0 );
 } // kimgio_jp2_read
@@ -236,7 +300,11 @@ write_components( jas_image_t* ji, const TQImage& qi )
 TDE_EXPORT void
 kimgio_jp2_write( TQImageIO* io )
 {
-	if( jas_init() ) return;
+	if (!initializeJasper())
+	{
+		kdError(399) << "Failed to initialize JasPer library." << endl;
+		return;
+	}
 
 	// open the stream. we write directly to the file if possible, to a
 	// temporary file otherwise.
@@ -255,12 +323,19 @@ kimgio_jp2_write( TQImageIO* io )
 
 
 	// by here, a jas_stream_t is open
-	if( !stream ) return;
+	if (!stream)
+	{
+		kdError(399)
+			<< "Failed to create a stream to write JP2 image" << endl;
+		cleanupJasper();
+		return;
+	}
 
 	jas_image_t* ji = create_image( io->image() );
 	if( !ji ) {
 		delete ktempf;
 		jas_stream_close( stream );
+		cleanupJasper();
 		return;
 	} // if
 
@@ -268,6 +343,7 @@ kimgio_jp2_write( TQImageIO* io )
 		delete ktempf;
 		jas_stream_close( stream );
 		jas_image_destroy( ji );
+		cleanupJasper();
 		return;
 	} // if
 
@@ -292,6 +368,7 @@ kimgio_jp2_write( TQImageIO* io )
 
 	jas_image_destroy( ji );
 	jas_stream_close( stream );
+	cleanupJasper();
 
 	if( i != 0 ) { delete ktempf; return; }
 
-- 
cgit v1.2.3

