From 9c9190192c1acdafbf4e9f277dba9e2079046cc7 Mon Sep 17 00:00:00 2001 From: Ethan Vrhel Date: Sun, 16 Aug 2020 16:28:44 -0700 Subject: Fixed JNI bugs and began updating viewer to use one GS instance Fixed JNI crashes when Ghostscript calls were made from different threads. Migrated loading low res and high res images from using multiple instances to using one global instance. --- .../src/com/artifex/gsjava/util/Reference.java | 2 +- .../src/com/artifex/gsviewer/Document.java | 137 ++++++++++++++++----- .../gsviewer/src/com/artifex/gsviewer/Page.java | 8 ++ demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.cpp | 12 +- demos/java/jni/gs_jni/jni_util.cpp | 4 +- 5 files changed, 126 insertions(+), 37 deletions(-) diff --git a/demos/java/gsjava/src/com/artifex/gsjava/util/Reference.java b/demos/java/gsjava/src/com/artifex/gsjava/util/Reference.java index 5a7b04dc0..38a34ac14 100644 --- a/demos/java/gsjava/src/com/artifex/gsjava/util/Reference.java +++ b/demos/java/gsjava/src/com/artifex/gsjava/util/Reference.java @@ -40,6 +40,6 @@ public class Reference { @Override public String toString() { - return "Reference<" + (value == null ? "?" : value.getClass().getName()) + "> -> " + value.toString(); + return "Reference<" + (value == null ? "?" : value.getClass().getName()) + "> -> " + value; } } diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java index 80e14333b..d147656ba 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Document.java @@ -15,9 +15,11 @@ import java.util.Objects; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; +import com.artifex.gsjava.GSAPI; import com.artifex.gsjava.GSInstance; import com.artifex.gsjava.callbacks.DisplayCallback; import com.artifex.gsjava.util.BytePointer; +import com.artifex.gsjava.util.Reference; import com.artifex.gsviewer.ImageUtil.ImageParams; /** @@ -297,32 +299,6 @@ public class Document implements List { } } - /** - * Initializes an instance of Ghostcript. - * - * @param instanceRef A reference to the instance of Ghostscript. - * @throws IllegalStateException When any Ghostscript operation fails to execute. - */ -// private static void initDocInstance(LongReference instanceRef) throws IllegalStateException { -// int code = gsapi_new_instance(instanceRef, GS_NULL); -// if (code != GS_ERROR_OK) { -// gsapi_delete_instance(instanceRef.value); -// throw new IllegalStateException("Failed to set stdio with handle (code = " + code + ")"); -// } -// -// code = gsapi_set_arg_encoding(instanceRef.value, 1); -// if (code != GS_ERROR_OK) { -// gsapi_delete_instance(instanceRef.value); -// throw new IllegalArgumentException("Failed to set arg encoding (code = " + code + ")"); -// } -// -// code = gsapi_set_display_callback(instanceRef.value, documentLoader.reset()); -// if (code != GS_ERROR_OK) { -// gsapi_delete_instance(instanceRef.value); -// throw new IllegalStateException("Failed to set display callback code=" + code); -// } -// } - private static GSInstance initDocInstance() throws IllegalStateException { GSInstance instance = new GSInstance(); @@ -339,6 +315,19 @@ public class Document implements List { throw new IllegalStateException("Failed to set display callback (code = " + code + ")"); } + String[] gsargs = new String[] { + "gs", + "-dNOPAUSE", "-dSAFER", "-I%rom%Resource%/Init/", "-dBATCH", + "-sDEVICE=display", + "-dFirstPage=1", + "-dDisplayFormat=" + format + }; + code = instance.init_with_args(gsargs); + if (code != GS_ERROR_OK) { + instance.delete_instance(); + throw new IllegalStateException("Failed to init with args (code = " + code + ")"); + } + return instance; } @@ -370,9 +359,16 @@ public class Document implements List { return this; } + @Override + public int onDisplayPresize(long handle, long device, int width, int height, int raster, int format) { + System.out.println("Presize"); + return 0; + } + @Override public int onDisplaySize(long handle, long device, int width, int height, int raster, int format, BytePointer pimage) { + System.out.println("Size"); this.pageWidth = width; this.pageHeight = height; this.pageRaster = raster; @@ -391,10 +387,16 @@ public class Document implements List { return 0; } + + @Override + public void finalize() { + System.err.println("WARNING: The document loader has been freed by the Java VM"); + } } private File file; private List pages; + private GSInstance gsInstance; /** * Creates and loads a new document. @@ -430,25 +432,46 @@ public class Document implements List { startOperation(); - GSInstance instance = null; try { - instance = initDocInstance(); + gsInstance = initDocInstance(); } catch (IllegalStateException e) { operationDone(); } - if (instance == null) + if (gsInstance == null) { + operationDone(); throw new IllegalStateException("Failed to initialize Ghostscript"); + } documentLoader.callback = loadCallback; - int code = instance.init_with_args(gargs); + int code = gsInstance.set_param("HWResolution", Page.PAGE_LOW_DPI_STR, GS_SPT_PARSED); + if (code != GS_ERROR_OK) { + operationDone(); + throw new IllegalStateException("Failed to set HWResolution (code = " + code + ")"); + } + + Reference exitCode = new Reference<>(); + code = gsInstance.run_file(file.getAbsolutePath(), 0, exitCode); + + if (code != GS_ERROR_OK) { + operationDone(); + throw new IllegalStateException("Failed to run file (code = " + code + ")"); + } + + gsInstance.set_param("TextAlphaBits", 4, GS_SPT_INT); + gsInstance.set_param("GraphicsAlphaBits", 4, GS_SPT_INT); + + //gsInstance.exit(); + //gsInstance.delete_instance(); + + /*int code = instance.init_with_args(gargs); instance.exit(); instance.delete_instance(); if (code != GS_ERROR_OK) { operationDone(); throw new IllegalStateException("Failed to gsapi_init_with_args code=" + code); - } + }*/ this.pages = new ArrayList<>(documentLoader.images.size()); for (BufferedImage img : documentLoader.images) { @@ -565,7 +588,7 @@ public class Document implements List { "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4", "-f", file.getAbsolutePath() }; - GSInstance instance = null; + /*GSInstance instance = null; try { instance = initDocInstance(); } catch (IllegalStateException e) { @@ -581,6 +604,36 @@ public class Document implements List { if (code != GS_ERROR_OK) { operationDone(); throw new IllegalStateException("Failed to gsapi_init_with_args code=" + code); + }*/ + //gsInstance.set_display_callback(documentLoader.reset()); + //documentLoader.reset(); + documentLoader.images.clear(); + + this.setParams(Page.PAGE_HIGH_DPI, startPage, endPage); + + for (GSInstance.GSParam param : gsInstance) { + System.out.println(param); + } + + Reference ref = new Reference<>(); + gsInstance.get_param_once("FirstPage", ref, GS_SPT_INT); + System.out.println(ref); + gsInstance.get_param_once("LastPage", ref, GS_SPT_INT); + System.out.println(ref); + + Reference exitCode = new Reference<>(); + //gsInstance.set_display_callback(documentLoader.reset()); + int code = gsInstance.run_file(file.getAbsolutePath(), 0, exitCode); + + if (code != GS_ERROR_OK) { + operationDone(); + throw new IllegalStateException("Failed to run file (code = " + code + ")"); + } + + if (documentLoader.images.size() != endPage - startPage + 1) { + operationDone(); + throw new IllegalStateException("Page range mismatch (expected " + + (endPage - startPage) + ", got " + documentLoader.images.size() + ")"); } int ind = startPage - 1; @@ -847,6 +900,26 @@ public class Document implements List { throw new IndexOutOfBoundsException("end < start"); } + private void setParams(int dpi, int startPage, int lastPage) { + int code = gsInstance.set_param("HWResolution", Page.toDPIString(dpi), GS_SPT_PARSED); + if (code != GS_ERROR_OK) { + operationDone(); + throw new IllegalStateException("Failed to set HWResolution (code = " + code + ")"); + } + + code = gsInstance.set_param("FirstPage", startPage, GS_SPT_INT); + if (code != GS_ERROR_OK) { + operationDone(); + throw new IllegalStateException("Failed to set dFirstPage (code = " + code + ")"); + } + + code = gsInstance.set_param("LastPage", lastPage, GS_SPT_INT); + if (code != GS_ERROR_OK) { + operationDone(); + throw new IllegalStateException("Failed to set dEndPage (code = " + code + ")"); + } + } + // Implementations of inherited methods from java.util.List. @Override diff --git a/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java b/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java index dca755660..bdbcd04b0 100644 --- a/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java +++ b/demos/java/gsviewer/src/com/artifex/gsviewer/Page.java @@ -21,11 +21,19 @@ public class Page { */ public static final int PAGE_HIGH_DPI = 72; + public static final String PAGE_HIGH_DPI_STR = toDPIString(PAGE_HIGH_DPI); + /** * The low-resolution DPI to use. */ public static final int PAGE_LOW_DPI = 10; + public static final String PAGE_LOW_DPI_STR = toDPIString(PAGE_LOW_DPI); + + public static String toDPIString(int dpi) { + return "[" + dpi + " " + dpi + "]"; + } + private volatile BufferedImage lowRes; private volatile BufferedImage highRes; private volatile BufferedImage zoomed; diff --git a/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.cpp b/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.cpp index 5c8acb83a..f3a568e77 100644 --- a/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.cpp +++ b/demos/java/jni/gs_jni/com_artifex_gsjava_GSAPI.cpp @@ -159,6 +159,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1default_1device if (list == NULL) return throwNullPointerException(env, "list"); jboolean isCopy = false; + callbacks::setJNIEnv(env); int code = gsapi_set_default_device_list((void *)instance, (const char *)env->GetByteArrayElements(list, &isCopy), listlen); return code; @@ -191,6 +192,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1init_1with_1args if (argv == NULL) return throwNullPointerException(env, "argv"); char **cargv = jbyteArray2DToCharArray(env, argv); + callbacks::setJNIEnv(env); int code = gsapi_init_with_args((void *)instance, argc, cargv); delete2DByteArray(argc, cargv); return code; @@ -200,6 +202,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1begin (JNIEnv *env, jclass, jlong instance, jint userErrors, jobject pExitCode) { int exitCode; + callbacks::setJNIEnv(env); int code = gsapi_run_string_begin((void *)instance, userErrors, &exitCode); if (pExitCode) Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode)); @@ -214,6 +217,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1continu jboolean copy = false; int exitCode; const char *cstring = (const char *)env->GetByteArrayElements(str, ©); + callbacks::setJNIEnv(env); int code = gsapi_run_string_continue((void *)instance, cstring, length, userErrors, &exitCode); if (pExitCode) Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode)); @@ -224,6 +228,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1end (JNIEnv *env, jclass, jlong instance, jint userErrors, jobject pExitCode) { int exitCode; + callbacks::setJNIEnv(env); int code = gsapi_run_string_end((void *)instance, userErrors, &exitCode); if (pExitCode) Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode)); @@ -238,6 +243,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string_1with_1l jboolean copy = false; int exitCode; const char *cstring = (const char *)env->GetByteArrayElements(str, ©); + callbacks::setJNIEnv(env); int code = gsapi_run_string_with_length((void *)instance, cstring, length, userErrors, &exitCode); if (pExitCode) Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode)); @@ -252,6 +258,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1string jboolean copy = false; int exitCode; const char *cstring = (const char *)env->GetByteArrayElements(str, ©); + callbacks::setJNIEnv(env); int code = gsapi_run_string((void *)instance, cstring, userErrors, &exitCode); if (pExitCode) Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode)); @@ -266,6 +273,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1run_1file jboolean copy = false; int exitCode; const char *cstring = (const char *)env->GetByteArrayElements(fileName, ©); + callbacks::setJNIEnv(env); int code = gsapi_run_file((void *)instance, cstring, userErrors, &exitCode); if (pExitCode) Reference::setValueField(env, pExitCode, toWrapperType(env, (jint)exitCode)); @@ -300,6 +308,7 @@ JNIEXPORT jint JNICALL Java_com_artifex_gsjava_GSAPI_gsapi_1set_1param int exitCode; const char *cstring = (const char *)env->GetByteArrayElements(param, ©); + callbacks::setJNIEnv(env); int code = gsapi_set_param((void *)instance, cstring, data, type); free(data); @@ -526,7 +535,8 @@ void *getAsPointer(JNIEnv *env, jobject object, gs_set_param_type type, bool *su cstring = (const char *)env->GetByteArrayElements(arr, ©); len = env->GetArrayLength(arr); result = malloc(sizeof(char) * len); - ((char *)result)[len - 1] = 0; + //((char *)result)[len - 1] = 0; + memcpy(result, cstring, len); break; case gs_spt_invalid: default: diff --git a/demos/java/jni/gs_jni/jni_util.cpp b/demos/java/jni/gs_jni/jni_util.cpp index 0c1710bd8..df390a7f1 100644 --- a/demos/java/jni/gs_jni/jni_util.cpp +++ b/demos/java/jni/gs_jni/jni_util.cpp @@ -34,9 +34,7 @@ jmethodID util::getMethodID(JNIEnv *env, jobject object, const char *method, con if (env == NULL || object == NULL || method == NULL || sig == NULL) return NULL; - jobject obj = env->NewLocalRef(object); - jclass clazz = env->GetObjectClass(obj); - env->DeleteLocalRef(obj); + jclass clazz = env->GetObjectClass(object); jmethodID methodID = env->GetMethodID(clazz, method, sig); if (methodID == NULL) -- cgit v1.2.1