Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXi Yan2019-03-27 12:59:53 -0400
committerAlexander Kurtakov2019-03-29 05:23:47 -0400
commit719370e9e5d83ce319f5ebade0ff5ecb80cf8497 (patch)
tree1d8fb5e1e1368d7e7c86490a7f31e119e988c7fd
parentd364c7461368ec6221b5d5c5f1905901a5cd742e (diff)
downloadeclipse.platform.swt-719370e9e5d83ce319f5ebade0ff5ecb80cf8497.tar.gz
eclipse.platform.swt-719370e9e5d83ce319f5ebade0ff5ecb80cf8497.tar.xz
eclipse.platform.swt-719370e9e5d83ce319f5ebade0ff5ecb80cf8497.zip
Bug 545032 - [GTK] Implement native ImageLoader
Implement ImageLoader on GTK with native GdkPixbuf API rather than using Java implementation. Loading images: 1) Load InputStream (or FileInputStream) into byte array 2) Write byte array to GdkPixbufLoader 3) Get GdkPixbufAnimation from loader 4) Use pixbufToImageData method to convert GdkPixbuf(s) from GdkPixbufAnimation into ImageData objects to store. Known issue: * GdkPixbuf abstracts away differences between interlaced v.s. regular images, which means ImageLoaderEvent cannot be triggered for partially loaded images. This breaks test_addImageLoaderListenerLorg_eclipse_swt_graphics_ImageLoaderListener:d. The workaround is to explicitly check the 29th byte of a PNG file, if it is zero then it is not interlaced so no ImageLoaderEvent is triggered. Saving images 1) implemented conversion from ImageData to GdkPixbuf 2) use gdk_pixbuf_save_to_bufferv to write to pixbuf to byte array which is used to write to OutputStream * Currently supports all image formats specified in SWT API, could be extended to more formats that GdkPixbuf API supports. Performance: - Loading 2MB image takes ~100ms with native implementation where as before takes ~300ms with java implementation Change-Id: If8fdc3e06f36d8747a7ccdfa1940751a9d196757 Signed-off-by: Xi Yan <xixiyan@redhat.com>
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c188
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c16
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h16
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GDK.java152
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java24
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/ImageLoader.java (renamed from bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageLoader.java)8
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java2
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java625
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java348
-rw-r--r--tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug545032_ImageLoaderBenchmark.java43
10 files changed, 1418 insertions, 4 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c
index 8bb7f2fcae..c0b33d73db 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c
@@ -1491,6 +1491,90 @@ fail:
}
#endif
+#ifndef NO__1gdk_1pixbuf_1animation_1get_1iter
+JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1animation_1get_1iter)
+ (JNIEnv *env, jclass that, jlong arg0, jlong arg1)
+{
+ jlong rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1animation_1get_1iter_FUNC);
+ rc = (jlong)gdk_pixbuf_animation_get_iter((const GdkPixbufAnimation *)arg0, (const GTimeVal *)arg1);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1animation_1get_1iter_FUNC);
+ return rc;
+}
+#endif
+
+#ifndef NO__1gdk_1pixbuf_1animation_1get_1static_1image
+JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1animation_1get_1static_1image)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jlong rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1animation_1get_1static_1image_FUNC);
+ rc = (jlong)gdk_pixbuf_animation_get_static_image((const GdkPixbufAnimation *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1animation_1get_1static_1image_FUNC);
+ return rc;
+}
+#endif
+
+#ifndef NO__1gdk_1pixbuf_1animation_1is_1static_1image
+JNIEXPORT jboolean JNICALL GDK_NATIVE(_1gdk_1pixbuf_1animation_1is_1static_1image)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jboolean rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1animation_1is_1static_1image_FUNC);
+ rc = (jboolean)gdk_pixbuf_animation_is_static_image((const GdkPixbufAnimation *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1animation_1is_1static_1image_FUNC);
+ return rc;
+}
+#endif
+
+#ifndef NO__1gdk_1pixbuf_1animation_1iter_1advance
+JNIEXPORT jboolean JNICALL GDK_NATIVE(_1gdk_1pixbuf_1animation_1iter_1advance)
+ (JNIEnv *env, jclass that, jlong arg0, jlong arg1)
+{
+ jboolean rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1animation_1iter_1advance_FUNC);
+ rc = (jboolean)gdk_pixbuf_animation_iter_advance((const GdkPixbufAnimationIter *)arg0, (const GTimeVal *)arg1);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1animation_1iter_1advance_FUNC);
+ return rc;
+}
+#endif
+
+#ifndef NO__1gdk_1pixbuf_1animation_1iter_1get_1delay_1time
+JNIEXPORT jint JNICALL GDK_NATIVE(_1gdk_1pixbuf_1animation_1iter_1get_1delay_1time)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jint rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1animation_1iter_1get_1delay_1time_FUNC);
+ rc = (jint)gdk_pixbuf_animation_iter_get_delay_time((const GdkPixbufAnimationIter *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1animation_1iter_1get_1delay_1time_FUNC);
+ return rc;
+}
+#endif
+
+#ifndef NO__1gdk_1pixbuf_1animation_1iter_1get_1pixbuf
+JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1animation_1iter_1get_1pixbuf)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jlong rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1animation_1iter_1get_1pixbuf_FUNC);
+ rc = (jlong)gdk_pixbuf_animation_iter_get_pixbuf((const GdkPixbufAnimationIter *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1animation_1iter_1get_1pixbuf_FUNC);
+ return rc;
+}
+#endif
+
+#ifndef NO__1gdk_1pixbuf_1copy
+JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1copy)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jlong rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1copy_FUNC);
+ rc = (jlong)gdk_pixbuf_copy((const GdkPixbuf *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1copy_FUNC);
+ return rc;
+}
+#endif
+
#ifndef NO__1gdk_1pixbuf_1copy_1area
JNIEXPORT void JNICALL GDK_NATIVE(_1gdk_1pixbuf_1copy_1area)
(JNIEnv *env, jclass that, jlong arg0, jint arg1, jint arg2, jint arg3, jint arg4, jlong arg5, jint arg6, jint arg7)
@@ -1501,6 +1585,42 @@ JNIEXPORT void JNICALL GDK_NATIVE(_1gdk_1pixbuf_1copy_1area)
}
#endif
+#ifndef NO__1gdk_1pixbuf_1format_1get_1name
+JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1format_1get_1name)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jlong rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1format_1get_1name_FUNC);
+ rc = (jlong)gdk_pixbuf_format_get_name((const GdkPixbufFormat *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1format_1get_1name_FUNC);
+ return rc;
+}
+#endif
+
+#ifndef NO__1gdk_1pixbuf_1get_1bits_1per_1sample
+JNIEXPORT jint JNICALL GDK_NATIVE(_1gdk_1pixbuf_1get_1bits_1per_1sample)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jint rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1get_1bits_1per_1sample_FUNC);
+ rc = (jint)gdk_pixbuf_get_bits_per_sample((const GdkPixbuf *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1get_1bits_1per_1sample_FUNC);
+ return rc;
+}
+#endif
+
+#ifndef NO__1gdk_1pixbuf_1get_1byte_1length
+JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1get_1byte_1length)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jlong rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1get_1byte_1length_FUNC);
+ rc = (jlong)gdk_pixbuf_get_byte_length((const GdkPixbuf *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1get_1byte_1length_FUNC);
+ return rc;
+}
+#endif
+
#ifndef NO__1gdk_1pixbuf_1get_1has_1alpha
JNIEXPORT jboolean JNICALL GDK_NATIVE(_1gdk_1pixbuf_1get_1has_1alpha)
(JNIEnv *env, jclass that, jlong arg0)
@@ -1525,6 +1645,18 @@ JNIEXPORT jint JNICALL GDK_NATIVE(_1gdk_1pixbuf_1get_1height)
}
#endif
+#ifndef NO__1gdk_1pixbuf_1get_1n_1channels
+JNIEXPORT jint JNICALL GDK_NATIVE(_1gdk_1pixbuf_1get_1n_1channels)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jint rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1get_1n_1channels_FUNC);
+ rc = (jint)gdk_pixbuf_get_n_channels((const GdkPixbuf *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1get_1n_1channels_FUNC);
+ return rc;
+}
+#endif
+
#ifndef NO__1gdk_1pixbuf_1get_1pixels
JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1get_1pixels)
(JNIEnv *env, jclass that, jlong arg0)
@@ -1577,6 +1709,30 @@ fail:
}
#endif
+#ifndef NO__1gdk_1pixbuf_1loader_1get_1animation
+JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1loader_1get_1animation)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jlong rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1loader_1get_1animation_FUNC);
+ rc = (jlong)gdk_pixbuf_loader_get_animation((GdkPixbufLoader *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1loader_1get_1animation_FUNC);
+ return rc;
+}
+#endif
+
+#ifndef NO__1gdk_1pixbuf_1loader_1get_1format
+JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1loader_1get_1format)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ jlong rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1loader_1get_1format_FUNC);
+ rc = (jlong)gdk_pixbuf_loader_get_format((GdkPixbufLoader *)arg0);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1loader_1get_1format_FUNC);
+ return rc;
+}
+#endif
+
#ifndef NO__1gdk_1pixbuf_1loader_1get_1pixbuf
JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1loader_1get_1pixbuf)
(JNIEnv *env, jclass that, jlong arg0)
@@ -1629,6 +1785,18 @@ JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1new)
}
#endif
+#ifndef NO__1gdk_1pixbuf_1new_1from_1data
+JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1new_1from_1data)
+ (JNIEnv *env, jclass that, jlong arg0, jint arg1, jboolean arg2, jint arg3, jint arg4, jint arg5, jint arg6, jlong arg7, jlong arg8)
+{
+ jlong rc = 0;
+ GDK_NATIVE_ENTER(env, that, _1gdk_1pixbuf_1new_1from_1data_FUNC);
+ rc = (jlong)gdk_pixbuf_new_from_data((const guchar *)arg0, (GdkColorspace)arg1, (gboolean)arg2, arg3, arg4, arg5, arg6, (GdkPixbufDestroyNotify)arg7, (gpointer)arg8);
+ GDK_NATIVE_EXIT(env, that, _1gdk_1pixbuf_1new_1from_1data_FUNC);
+ return rc;
+}
+#endif
+
#ifndef NO__1gdk_1pixbuf_1new_1from_1file
JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1pixbuf_1new_1from_1file)
(JNIEnv *env, jclass that, jbyteArray arg0, jlongArray arg1)
@@ -14849,6 +15017,16 @@ JNIEXPORT void JNICALL OS_NATIVE(_1g_1free)
}
#endif
+#ifndef NO__1g_1get_1current_1time
+JNIEXPORT void JNICALL OS_NATIVE(_1g_1get_1current_1time)
+ (JNIEnv *env, jclass that, jlong arg0)
+{
+ OS_NATIVE_ENTER(env, that, _1g_1get_1current_1time_FUNC);
+ g_get_current_time((GTimeVal *)arg0);
+ OS_NATIVE_EXIT(env, that, _1g_1get_1current_1time_FUNC);
+}
+#endif
+
#ifndef NO__1g_1hash_1table_1get_1values
JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1hash_1table_1get_1values)
(JNIEnv *env, jclass that, jlong arg0)
@@ -15808,6 +15986,16 @@ fail:
}
#endif
+#ifndef NO__1g_1time_1val_1add
+JNIEXPORT void JNICALL OS_NATIVE(_1g_1time_1val_1add)
+ (JNIEnv *env, jclass that, jlong arg0, jlong arg1)
+{
+ OS_NATIVE_ENTER(env, that, _1g_1time_1val_1add_FUNC);
+ g_time_val_add((GTimeVal *)arg0, (glong)arg1);
+ OS_NATIVE_EXIT(env, that, _1g_1time_1val_1add_FUNC);
+}
+#endif
+
#ifndef NO__1g_1timeout_1add
JNIEXPORT jint JNICALL OS_NATIVE(_1g_1timeout_1add)
(JNIEnv *env, jclass that, jint arg0, jlong arg1, jlong arg2)
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c
index 675554b9b1..2a7bb8538a 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c
@@ -122,17 +122,31 @@ char * GDK_nativeFunctionNames[] = {
"_1gdk_1monitor_1get_1workarea",
"_1gdk_1pango_1context_1get",
"_1gdk_1pango_1layout_1get_1clip_1region",
+ "_1gdk_1pixbuf_1animation_1get_1iter",
+ "_1gdk_1pixbuf_1animation_1get_1static_1image",
+ "_1gdk_1pixbuf_1animation_1is_1static_1image",
+ "_1gdk_1pixbuf_1animation_1iter_1advance",
+ "_1gdk_1pixbuf_1animation_1iter_1get_1delay_1time",
+ "_1gdk_1pixbuf_1animation_1iter_1get_1pixbuf",
+ "_1gdk_1pixbuf_1copy",
"_1gdk_1pixbuf_1copy_1area",
+ "_1gdk_1pixbuf_1format_1get_1name",
+ "_1gdk_1pixbuf_1get_1bits_1per_1sample",
+ "_1gdk_1pixbuf_1get_1byte_1length",
"_1gdk_1pixbuf_1get_1has_1alpha",
"_1gdk_1pixbuf_1get_1height",
+ "_1gdk_1pixbuf_1get_1n_1channels",
"_1gdk_1pixbuf_1get_1pixels",
"_1gdk_1pixbuf_1get_1rowstride",
"_1gdk_1pixbuf_1get_1width",
"_1gdk_1pixbuf_1loader_1close",
+ "_1gdk_1pixbuf_1loader_1get_1animation",
+ "_1gdk_1pixbuf_1loader_1get_1format",
"_1gdk_1pixbuf_1loader_1get_1pixbuf",
"_1gdk_1pixbuf_1loader_1new",
"_1gdk_1pixbuf_1loader_1write",
"_1gdk_1pixbuf_1new",
+ "_1gdk_1pixbuf_1new_1from_1data",
"_1gdk_1pixbuf_1new_1from_1file",
"_1gdk_1pixbuf_1save_1to_1bufferv",
"_1gdk_1pixbuf_1scale_1simple",
@@ -1227,6 +1241,7 @@ char * OS_nativeFunctionNames[] = {
"_1g_1filename_1to_1uri",
"_1g_1filename_1to_1utf8",
"_1g_1free",
+ "_1g_1get_1current_1time",
"_1g_1hash_1table_1get_1values",
"_1g_1icon_1new_1for_1string",
"_1g_1icon_1to_1string",
@@ -1301,6 +1316,7 @@ char * OS_nativeFunctionNames[] = {
"_1g_1string_1free",
"_1g_1string_1new_1len",
"_1g_1strtod",
+ "_1g_1time_1val_1add",
"_1g_1timeout_1add",
"_1g_1type_1add_1interface_1static",
"_1g_1type_1class_1peek",
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h
index 9714551869..8722621d2f 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h
@@ -132,17 +132,31 @@ typedef enum {
_1gdk_1monitor_1get_1workarea_FUNC,
_1gdk_1pango_1context_1get_FUNC,
_1gdk_1pango_1layout_1get_1clip_1region_FUNC,
+ _1gdk_1pixbuf_1animation_1get_1iter_FUNC,
+ _1gdk_1pixbuf_1animation_1get_1static_1image_FUNC,
+ _1gdk_1pixbuf_1animation_1is_1static_1image_FUNC,
+ _1gdk_1pixbuf_1animation_1iter_1advance_FUNC,
+ _1gdk_1pixbuf_1animation_1iter_1get_1delay_1time_FUNC,
+ _1gdk_1pixbuf_1animation_1iter_1get_1pixbuf_FUNC,
+ _1gdk_1pixbuf_1copy_FUNC,
_1gdk_1pixbuf_1copy_1area_FUNC,
+ _1gdk_1pixbuf_1format_1get_1name_FUNC,
+ _1gdk_1pixbuf_1get_1bits_1per_1sample_FUNC,
+ _1gdk_1pixbuf_1get_1byte_1length_FUNC,
_1gdk_1pixbuf_1get_1has_1alpha_FUNC,
_1gdk_1pixbuf_1get_1height_FUNC,
+ _1gdk_1pixbuf_1get_1n_1channels_FUNC,
_1gdk_1pixbuf_1get_1pixels_FUNC,
_1gdk_1pixbuf_1get_1rowstride_FUNC,
_1gdk_1pixbuf_1get_1width_FUNC,
_1gdk_1pixbuf_1loader_1close_FUNC,
+ _1gdk_1pixbuf_1loader_1get_1animation_FUNC,
+ _1gdk_1pixbuf_1loader_1get_1format_FUNC,
_1gdk_1pixbuf_1loader_1get_1pixbuf_FUNC,
_1gdk_1pixbuf_1loader_1new_FUNC,
_1gdk_1pixbuf_1loader_1write_FUNC,
_1gdk_1pixbuf_1new_FUNC,
+ _1gdk_1pixbuf_1new_1from_1data_FUNC,
_1gdk_1pixbuf_1new_1from_1file_FUNC,
_1gdk_1pixbuf_1save_1to_1bufferv_FUNC,
_1gdk_1pixbuf_1scale_1simple_FUNC,
@@ -1201,6 +1215,7 @@ typedef enum {
_1g_1filename_1to_1uri_FUNC,
_1g_1filename_1to_1utf8_FUNC,
_1g_1free_FUNC,
+ _1g_1get_1current_1time_FUNC,
_1g_1hash_1table_1get_1values_FUNC,
_1g_1icon_1new_1for_1string_FUNC,
_1g_1icon_1to_1string_FUNC,
@@ -1275,6 +1290,7 @@ typedef enum {
_1g_1string_1free_FUNC,
_1g_1string_1new_1len_FUNC,
_1g_1strtod_FUNC,
+ _1g_1time_1val_1add_FUNC,
_1g_1timeout_1add_FUNC,
_1g_1type_1add_1interface_1static_FUNC,
_1g_1type_1class_1peek_FUNC,
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GDK.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GDK.java
index 400eeb7802..ce0a2fe542 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GDK.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GDK.java
@@ -1405,6 +1405,72 @@ public class GDK extends OS {
lock.unlock();
}
}
+ /** @param animation cast=(const GdkPixbufAnimation *) */
+ public static final native boolean _gdk_pixbuf_animation_is_static_image(long animation);
+ public static final boolean gdk_pixbuf_animation_is_static_image(long animation) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_animation_is_static_image(animation);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /** @param iter cast=(const GdkPixbufAnimationIter *) */
+ public static final native int _gdk_pixbuf_animation_iter_get_delay_time(long iter);
+ public static final int gdk_pixbuf_animation_iter_get_delay_time(long iter) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_animation_iter_get_delay_time(iter);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /** @param iter cast=(const GdkPixbufAnimationIter *) */
+ public static final native long _gdk_pixbuf_animation_iter_get_pixbuf(long iter);
+ public static final long gdk_pixbuf_animation_iter_get_pixbuf(long iter) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_animation_iter_get_pixbuf(iter);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /**
+ * @param iter cast=(const GdkPixbufAnimationIter *)
+ * @param current_time cast=(const GTimeVal *)
+ */
+ public static final native boolean _gdk_pixbuf_animation_iter_advance(long iter, long current_time);
+ public static final boolean gdk_pixbuf_animation_iter_advance(long iter, long current_time) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_animation_iter_advance(iter, current_time);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /**
+ * @param animation cast=(const GdkPixbufAnimation *)
+ * @param start_time cast=(const GTimeVal *)
+ */
+ public static final native long _gdk_pixbuf_animation_get_iter(long animation, long start_time);
+ public static final long gdk_pixbuf_animation_get_iter(long animation, long start_time) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_animation_get_iter(animation, start_time);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /** @param animation cast=(const GdkPixbufAnimation *) */
+ public static final native long _gdk_pixbuf_animation_get_static_image(long animation);
+ public static final long gdk_pixbuf_animation_get_static_image(long animation) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_animation_get_static_image(animation);
+ } finally {
+ lock.unlock();
+ }
+ }
/**
* @param src_pixbuf cast=(GdkPixbuf *)
* @param dest_pixbuf cast=(GdkPixbuf *)
@@ -1468,6 +1534,92 @@ public class GDK extends OS {
lock.unlock();
}
}
+ /** @param pixbuf cast=(const GdkPixbuf *) */
+ public static final native long _gdk_pixbuf_get_byte_length(long pixbuf);
+ public static final long gdk_pixbuf_get_byte_length(long pixbuf) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_get_byte_length(pixbuf);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /** @param pixbuf cast=(const GdkPixbuf *) */
+ public static final native int _gdk_pixbuf_get_n_channels(long pixbuf);
+ public static final int gdk_pixbuf_get_n_channels(long pixbuf) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_get_n_channels(pixbuf);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /** @param pixbuf cast=(const GdkPixbuf *) */
+ public static final native int _gdk_pixbuf_get_bits_per_sample(long pixbuf);
+ public static final int gdk_pixbuf_get_bits_per_sample(long pixbuf) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_get_bits_per_sample(pixbuf);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /** @param pixbuf cast=(const GdkPixbuf *) */
+ public static final native long _gdk_pixbuf_copy(long pixbuf);
+ public static final long gdk_pixbuf_copy(long pixbuf) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_copy(pixbuf);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /** @param loader cast=(GdkPixbufLoader *) */
+ public static final native long _gdk_pixbuf_loader_get_format(long loader);
+ public static final long gdk_pixbuf_loader_get_format(long loader) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_loader_get_format(loader);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /** @param format cast=(const GdkPixbufFormat *) */
+ public static final native long _gdk_pixbuf_format_get_name(long format);
+ public static final long gdk_pixbuf_format_get_name(long format) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_format_get_name(format);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /** @param loader cast=(GdkPixbufLoader *) */
+ public static final native long _gdk_pixbuf_loader_get_animation(long loader);
+ public static final long gdk_pixbuf_loader_get_animation(long loader) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_loader_get_animation(loader);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /**
+ * @param data cast=(const guchar *)
+ * @param colorspace cast=(GdkColorspace)
+ * @param has_alpha cast=(gboolean)
+ * @param destroy_fn cast=(GdkPixbufDestroyNotify)
+ * @param destroy_fn_data cast=(gpointer)
+ */
+ public static final native long _gdk_pixbuf_new_from_data(long data, int colorspace, boolean has_alpha, int bits_per_sample, int width, int height, int rowstride, long destroy_fn, long destroy_fn_data);
+ public static final long gdk_pixbuf_new_from_data(long data, int colorspace, boolean has_alpha, int bits_per_sample, int width, int height, int rowstride, long destroy_fn, long destroy_fn_data) {
+ lock.lock();
+ try {
+ return _gdk_pixbuf_new_from_data(data, colorspace, has_alpha, bits_per_sample, width, height, rowstride, destroy_fn, destroy_fn_data);
+ } finally {
+ lock.unlock();
+ }
+ }
public static final native long _gdk_pixbuf_loader_new();
public static final long gdk_pixbuf_loader_new() {
lock.lock();
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java
index 63a9d82224..5aceaffc74 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java
@@ -588,6 +588,30 @@ public class OS extends C {
}
}
+ /** @param result cast=(GTimeVal *)*/
+ public static final native void _g_get_current_time(long result);
+ public static final void g_get_current_time(long result) {
+ lock.lock();
+ try {
+ _g_get_current_time(result);
+ } finally {
+ lock.unlock();
+ }
+ }
+ /**
+ * @param result cast=(GTimeVal *)
+ * @param microseconds cast=(glong)
+ */
+ public static final native void _g_time_val_add(long result, long microseconds);
+ public static final void g_time_val_add(long result, long microseconds) {
+ lock.lock();
+ try {
+ _g_time_val_add(result, microseconds);
+ } finally {
+ lock.unlock();
+ }
+ }
+
/** @param str cast=(const gchar *)
* @category custom
*/
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/ImageLoader.java
index 89c2cccd8b..a8914ea4af 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageLoader.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/ImageLoader.java
@@ -43,10 +43,10 @@ import org.eclipse.swt.internal.image.*;
* </ul>
*
* <p>
- * NOTE: <code>ImageLoader</code> is implemented in Java, which has certain performance
- * implications. Performance and memory sensitive applications may benefit from using
- * one of the constructors provided by <code>Image</code>, as these are implemented
- * natively.</p>
+ * NOTE: <code>ImageLoader</code> is implemented in Java on some platforms, which has
+ * certain performance implications. Performance and memory sensitive applications may
+ * benefit from using one of the constructors provided by <code>Image</code>, as these
+ * are implemented natively.</p>
*
* @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ImageAnalyzer</a>
* @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java
index 21462faadb..a6781948de 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/graphics/ImageData.java
@@ -333,6 +333,7 @@ public ImageData(int width, int height, int depth, PaletteData palette, int scan
*/
public ImageData(InputStream stream) {
ImageData[] data = ImageDataLoader.load(stream);
+ if (data == null || data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
ImageData i = data[0];
setAllFields(
@@ -379,6 +380,7 @@ public ImageData(InputStream stream) {
*/
public ImageData(String filename) {
ImageData[] data = ImageDataLoader.load(filename);
+ if (data == null || data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
if (data.length < 1) SWT.error(SWT.ERROR_INVALID_IMAGE);
ImageData i = data[0];
setAllFields(
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java
new file mode 100644
index 0000000000..b283bb63d2
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java
@@ -0,0 +1,625 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Red Hat and others. All rights reserved.
+ * The contents of this file are made available under the terms
+ * of the GNU Lesser General Public License (LGPL) Version 2.1 that
+ * accompanies this distribution (lgpl-v21.txt). The LGPL is also
+ * available at http://www.gnu.org/licenses/lgpl.html. If the version
+ * of the LGPL at http://www.gnu.org is different to the version of
+ * the LGPL accompanying this distribution and there is any conflict
+ * between the two license versions, the terms of the LGPL accompanying
+ * this distribution shall govern.
+ *
+ * Contributors:
+ * Red Hat - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.internal.*;
+import org.eclipse.swt.internal.gtk.*;
+
+/**
+ * Instances of this class are used to load images from,
+ * and save images to, a file or stream.
+ * <p>
+ * Currently supported image formats are:
+ * </p><ul>
+ * <li>BMP (Windows or OS/2 Bitmap)</li>
+ * <li>ICO (Windows Icon)</li>
+ * <li>JPEG</li>
+ * <li>GIF</li>
+ * <li>PNG</li>
+ * <li>TIFF</li>
+ * </ul>
+ * <code>ImageLoaders</code> can be used to:
+ * <ul>
+ * <li>load/save single images in all formats</li>
+ * <li>load/save multiple images (GIF/ICO/TIFF)</li>
+ * <li>load/save animated GIF images</li>
+ * <li>load interlaced GIF/PNG images</li>
+ * <li>load progressive JPEG images</li>
+ * </ul>
+ *
+ * <p>
+ * NOTE: <code>ImageLoader</code> is implemented in Java on some platforms, which has
+ * certain performance implications. Performance and memory sensitive applications may
+ * benefit from using one of the constructors provided by <code>Image</code>, as these
+ * are implemented natively.</p>
+ *
+ * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ImageAnalyzer</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ */
+public class ImageLoader {
+
+ /**
+ * the array of ImageData objects in this ImageLoader.
+ * This array is read in when the load method is called,
+ * and it is written out when the save method is called
+ */
+ public ImageData[] data;
+
+ /**
+ * the width of the logical screen on which the images
+ * reside, in pixels (this corresponds to the GIF89a
+ * Logical Screen Width value)
+ */
+ public int logicalScreenWidth;
+
+ /**
+ * the height of the logical screen on which the images
+ * reside, in pixels (this corresponds to the GIF89a
+ * Logical Screen Height value)
+ */
+ public int logicalScreenHeight;
+
+ /**
+ * the background pixel for the logical screen (this
+ * corresponds to the GIF89a Background Color Index value).
+ * The default is -1 which means 'unspecified background'
+ *
+ */
+ public int backgroundPixel;
+
+ /**
+ * the number of times to repeat the display of a sequence
+ * of animated images (this corresponds to the commonly-used
+ * GIF application extension for "NETSCAPE 2.0 01").
+ * The default is 1. A value of 0 means 'display repeatedly'
+ */
+ public int repeatCount;
+
+ /**
+ * This is the compression used when saving jpeg and png files.
+ * <p>
+ * When saving jpeg files, the value is from 1 to 100,
+ * where 1 is very high compression but low quality, and 100 is
+ * no compression and high quality; default is 75.
+ * </p><p>
+ * When saving png files, the value is from 0 to 3, but they do not impact the quality
+ * because PNG is lossless compression. 0 is uncompressed, 1 is low compression and fast,
+ * 2 is default compression, and 3 is high compression but slow.
+ * </p>
+ *
+ * @since 3.8
+ */
+ public int compression;
+
+ /**
+ * If the 29th byte of the PNG file is not zero, then it is interlaced.
+ */
+ final static int PNG_INTERLACE_METHOD_OFFSET = 28;
+
+ /*
+ * the set of ImageLoader event listeners, created on demand
+ */
+ List<ImageLoaderListener> imageLoaderListeners;
+
+/**
+ * Construct a new empty ImageLoader.
+ */
+public ImageLoader() {
+ reset();
+}
+
+/**
+ * Resets the fields of the ImageLoader, except for the
+ * <code>imageLoaderListeners</code> field.
+ */
+void reset() {
+ data = null;
+ logicalScreenWidth = 0;
+ logicalScreenHeight = 0;
+ backgroundPixel = -1;
+ repeatCount = 1;
+ compression = -1;
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. Throws an error if either an error
+ * occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ * <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ * <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ */
+public ImageData[] load(InputStream stream) {
+ if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ reset();
+ ImageData [] imgDataArray = getImageDataArrayFromStream(stream);
+ data = imgDataArray;
+ return imgDataArray;
+}
+
+/**
+ * Return true if the image is an interlaced PNG file.
+ * This is used to check whether ImageLoaderEvent should be fired when loading images.
+ * @param imageAsByteArray
+ * @return true iff 29th byte of PNG files is not zero
+ */
+boolean isInterlacedPNG(byte [] imageAsByteArray) {
+ return imageAsByteArray.length > PNG_INTERLACE_METHOD_OFFSET && imageAsByteArray[PNG_INTERLACE_METHOD_OFFSET] != 0;
+}
+
+ImageData [] getImageDataArrayFromStream(InputStream stream) {
+ byte[] buffer = new byte[2048];
+ long loader = GDK.gdk_pixbuf_loader_new();
+ int length;
+ List<ImageData> imgDataList = new ArrayList<>();
+ try {
+ // 1) Load InputStream into byte array
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ while ((length = stream.read(buffer)) > -1) {
+ baos.write(buffer, 0, length);
+ }
+ baos.flush();
+ byte[] data_buffer = baos.toByteArray();
+ if (data_buffer.length == 0) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); // empty stream
+
+ // 2) Copy byte array to C memory, write to GdkPixbufLoader
+ long buffer_ptr = OS.g_malloc(data_buffer.length);
+ C.memmove(buffer_ptr, data_buffer, data_buffer.length);
+ GDK.gdk_pixbuf_loader_write(loader, buffer_ptr, data_buffer.length, null);
+ GDK.gdk_pixbuf_loader_close(loader, null);
+
+ // 3) Get GdkPixbufAnimation from loader
+ long pixbuf_animation = GDK.gdk_pixbuf_loader_get_animation(loader);
+ if (pixbuf_animation == 0) SWT.error(SWT.ERROR_INVALID_IMAGE);
+
+ boolean isStatic = GDK.gdk_pixbuf_animation_is_static_image(pixbuf_animation);
+ if (isStatic) {
+ // Static image, get as single pixbuf and convert it to ImageData
+ long pixbuf = GDK.gdk_pixbuf_animation_get_static_image(pixbuf_animation);
+ ImageData imgData = pixbufToImageData(pixbuf);
+ imgData.type = getImageFormat(loader);
+ imgDataList.add(imgData);
+ } else {
+ // Image with multiple frames, iterate through each frame and convert
+ // each frame to ImageData
+ long start_time = OS.g_malloc(8);
+ OS.g_get_current_time(start_time);
+ long animation_iter = GDK.gdk_pixbuf_animation_get_iter (pixbuf_animation, start_time);
+ int delay_time = 0;
+ int time_offset = 0;
+ // Fix the number of GIF frames as GdkPixbufAnimation does not provide an API to
+ // determine number of frames.
+ int num_frames = 32;
+ for (int i = 0; i < num_frames; i++) {
+ // Calculate time offset from start_time to next frame
+ delay_time = GDK.gdk_pixbuf_animation_iter_get_delay_time (animation_iter);
+ time_offset += delay_time;
+ OS.g_time_val_add(start_time, time_offset * 1000);
+ boolean update = GDK.gdk_pixbuf_animation_iter_advance (animation_iter, start_time);
+ if (update) {
+ long curr_pixbuf = GDK.gdk_pixbuf_animation_iter_get_pixbuf (animation_iter);
+ long pixbuf_copy = GDK.gdk_pixbuf_copy(curr_pixbuf); // copy because curr_pixbuf might get disposed on next advance
+ ImageData imgData = pixbufToImageData(pixbuf_copy);
+ if (this.logicalScreenHeight == 0 && this.logicalScreenWidth == 0) {
+ this.logicalScreenHeight = imgData.height;
+ this.logicalScreenWidth = imgData.width;
+ }
+ OS.g_object_unref(pixbuf_copy);
+ imgData.type = getImageFormat(loader);
+ imgData.delayTime = delay_time;
+ imgDataList.add(imgData);
+ } else {
+ break;
+ }
+ }
+ }
+ ImageData [] imgDataArray = new ImageData [imgDataList.size()];
+ for (int i = 0; i < imgDataList.size(); i++) {
+ imgDataArray [i] = imgDataList.get(i);
+ // Loading completed, notify listeners
+ // listener should only be called when loading interlaced/progressive PNG/JPG/GIF ?
+ ImageData data = (ImageData) imgDataArray [i].clone();
+ if (this.hasListeners() && imgDataArray != null) {
+ if (data.type == SWT.IMAGE_PNG && isInterlacedPNG(data_buffer)) {
+ this.notifyListeners(new ImageLoaderEvent(this, data, i, true));
+ } else if (data.type != SWT.IMAGE_PNG) {
+ this.notifyListeners(new ImageLoaderEvent(this, data, i, true));
+ }
+ }
+ }
+ OS.g_free(buffer_ptr);
+ OS.g_object_unref(loader);
+ stream.close();
+ return imgDataArray;
+ } catch (IOException e) {
+ SWT.error(SWT.ERROR_IO);
+ }
+ return null;
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * file with the specified name. Throws an error if either
+ * an error occurs while loading the images, or if the images are
+ * not of a supported type. Returns the loaded image data array.
+ *
+ * @param filename the name of the file to load the images from
+ * @return an array of <code>ImageData</code> objects loaded from the specified file
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_IO - if an IO error occurs while reading from the file</li>
+ * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
+ * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
+ * </ul>
+ */
+public ImageData[] load(String filename) {
+ if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ InputStream stream = null;
+ try {
+ stream = new FileInputStream(filename);
+ return load(stream);
+ } catch (IOException e) {
+ SWT.error(SWT.ERROR_IO, e);
+ } finally {
+ try {
+ if (stream != null) stream.close();
+ } catch (IOException e) {
+ // Ignore error
+ }
+ }
+ return null;
+}
+
+/**
+ * Load GdkPixbuf directly using gdk_pixbuf_new_from_file,
+ * without FileInputStream.
+ * @param filename
+ * @return
+ */
+ImageData[] loadFromFile(String filename) {
+ long pixbuf = gdk_pixbuf_new_from_file(filename);
+ if (pixbuf == 0) return null;
+ ImageData imgData= pixbufToImageData(pixbuf);
+ return data = new ImageData[] {imgData};
+}
+
+/**
+ * Return the type of file from which the image was read
+ * by inspecting GdkPixbufFormat from GdkPixbufLoader
+ *
+ * It is expressed as one of the following values:
+ * <dl>
+ * <dt><code>IMAGE_BMP</code></dt>
+ * <dd>Windows BMP file format, no compression</dd>
+ * <dt><code>IMAGE_BMP_RLE</code></dt>
+ * <dd>Windows BMP file format, RLE compression if appropriate</dd>
+ * <dt><code>IMAGE_GIF</code></dt>
+ * <dd>GIF file format</dd>
+ * <dt><code>IMAGE_ICO</code></dt>
+ * <dd>Windows ICO file format</dd>
+ * <dt><code>IMAGE_JPEG</code></dt>
+ * <dd>JPEG file format</dd>
+ * <dt><code>IMAGE_PNG</code></dt>
+ * <dd>PNG file format</dd>
+ * </dl>
+ */
+int getImageFormat(long loader) {
+ long format = GDK.gdk_pixbuf_loader_get_format(loader);
+ long name = GDK.gdk_pixbuf_format_get_name(format);
+ String nameStr = Converter.cCharPtrToJavaString(name, false);
+ switch (nameStr) {
+ case "bmp": return SWT.IMAGE_BMP;
+ case "gif": return SWT.IMAGE_GIF;
+ case "ico": return SWT.IMAGE_ICO;
+ case "jpeg": return SWT.IMAGE_JPEG;
+ case "png": return SWT.IMAGE_PNG;
+ default: return SWT.IMAGE_UNDEFINED;
+ }
+}
+
+/**
+ * Convert GdkPixbuf pointer to Java object ImageData
+ * @param pixbuf
+ * @return ImageData with pixbuf data
+ */
+static ImageData pixbufToImageData(long pixbuf) {
+ boolean hasAlpha = GDK.gdk_pixbuf_get_has_alpha(pixbuf);
+ int width = GDK.gdk_pixbuf_get_width(pixbuf);
+ int height = GDK.gdk_pixbuf_get_height(pixbuf);
+ int stride = GDK.gdk_pixbuf_get_rowstride(pixbuf);
+ int n_channels = GDK.gdk_pixbuf_get_n_channels(pixbuf); // only 3 or 4 samples per pixel are supported
+ int bits_per_sample = GDK.gdk_pixbuf_get_bits_per_sample(pixbuf); // only 8 bit per sample are supported
+ long pixels = GDK.gdk_pixbuf_get_pixels(pixbuf);
+ /*
+ * From GDK Docs: last row in the pixbuf may not be as wide as the full rowstride,
+ * but rather just as wide as the pixel data needs to be. Compute the width in bytes
+ * of the last row to copy raw pixbuf data.
+ */
+ int lastRowWidth = width * ((n_channels * bits_per_sample + 7) / 8);
+ byte[] srcData = new byte[stride * height];
+ C.memmove(srcData, pixels, stride * (height - 1) + lastRowWidth);
+ /*
+ * Note: GdkPixbuf only supports 3/4 n_channels and 8 bits_per_sample,
+ * This means all images are of depth 24 / depth 32. This means loading
+ * images will result in a direct PaletteData with RGB masks, since
+ * there is no way to determine indexed PaletteData info.
+ *
+ * See https://www.eclipse.org/articles/Article-SWT-images/graphics-resources.html#PaletteData
+ */
+ PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF);
+ ImageData imgData = new ImageData(width, height, bits_per_sample * n_channels, palette, stride, srcData);
+ if (hasAlpha) {
+ byte[] alphaData = imgData.alphaData = new byte[width * height];
+ for (int y = 0, offset = 0, alphaOffset = 0; y < height; y++) {
+ for (int x = 0; x < width; x++, offset += n_channels) {
+ byte r = srcData[offset + 0];
+ byte g = srcData[offset + 1];
+ byte b = srcData[offset + 2];
+ byte a = srcData[offset + 3];
+ srcData[offset + 0] = 0;
+ alphaData[alphaOffset++] = a;
+ if (a != 0) {
+ srcData[offset + 1] = r;
+ srcData[offset + 2] = g;
+ srcData[offset + 3] = b;
+ }
+ }
+ }
+ } else {
+ for (int y = 0, offset = 0; y < height; y++) {
+ for (int x = 0; x < width; x++, offset += n_channels) {
+ byte r = srcData[offset + 0];
+ byte g = srcData[offset + 1];
+ byte b = srcData[offset + 2];
+ srcData[offset + 0] = r;
+ srcData[offset + 1] = g;
+ srcData[offset + 2] = b;
+ }
+ }
+ }
+ return imgData;
+}
+
+/**
+ * Returns GdkPixbuf pointer by loading an image from filename (Java string)
+ * @param filename
+ * @return
+ */
+static long gdk_pixbuf_new_from_file(String filename) {
+ int length = filename.length ();
+ char [] chars = new char [length];
+ filename.getChars (0, length, chars, 0);
+ byte [] buffer = Converter.wcsToMbcs(chars, true);
+ return GDK.gdk_pixbuf_new_from_file(buffer, null);
+}
+
+/**
+ * Convert java object ImageData to a new GdkPixbuf for saving
+ * @param imgData
+ * @return
+ */
+static long imageDataToPixbuf(ImageData imgData) {
+ int colorspace = GDK.GDK_COLORSPACE_RGB;
+ boolean has_alpha = imgData.alphaData != null;
+ int width = imgData.width;
+ int height = imgData.height;
+ int rowstride = imgData.scanlinePad;
+ long buffer_ptr = OS.g_malloc(imgData.data.length);
+ C.memmove(buffer_ptr, imgData.data, imgData.data.length);
+ long pixbuf = GDK.gdk_pixbuf_new_from_data (buffer_ptr, colorspace, has_alpha, 8, width, height, rowstride, 0, 0);
+ OS.g_free(buffer_ptr);
+ return pixbuf;
+}
+
+/**
+ * Saves the image data in this ImageLoader to the specified stream.
+ * The format parameter can have one of the following values:
+ * <dl>
+ * <dt><code>IMAGE_BMP</code></dt>
+ * <dd>Windows BMP file format, no compression</dd>
+ * <dt><code>IMAGE_BMP_RLE</code></dt>
+ * <dd>Windows BMP file format, RLE compression if appropriate</dd>
+ * <dt><code>IMAGE_GIF</code></dt>
+ * <dd>GIF file format</dd>
+ * <dt><code>IMAGE_ICO</code></dt>
+ * <dd>Windows ICO file format</dd>
+ * <dt><code>IMAGE_JPEG</code></dt>
+ * <dd>JPEG file format</dd>
+ * <dt><code>IMAGE_PNG</code></dt>
+ * <dd>PNG file format</dd>
+ * <dt><code>IMAGE_TIFF</code></dt>
+ * <dd>TIFF file format</dd>
+ * </dl>
+ *
+ * @param stream the output stream to write the images to
+ * @param format the format to write the images in
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_IO - if an IO error occurs while writing to the stream</li>
+ * <li>ERROR_INVALID_IMAGE - if the image data contains invalid data</li>
+ * <li>ERROR_UNSUPPORTED_FORMAT - if the image data cannot be saved to the requested format</li>
+ * </ul>
+ */
+public void save(OutputStream stream, int format) {
+ if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ if (format == -1) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
+ long pixbuf = imageDataToPixbuf(this.data[0]);
+ if (pixbuf == 0) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ // Write pixbuf to byte array and then to OutputStream
+ String typeStr = "";
+ switch (format) {
+ case SWT.IMAGE_BMP: typeStr = "bmp"; break;
+ case SWT.IMAGE_GIF: typeStr = "gif"; break;
+ case SWT.IMAGE_ICO: typeStr = "ico"; break;
+ case SWT.IMAGE_JPEG: typeStr = "jpeg"; break;
+ case SWT.IMAGE_PNG: typeStr = "png"; break;
+ case SWT.IMAGE_TIFF: typeStr = "tiff"; break;
+ }
+ byte[] type = Converter.wcsToMbcs(typeStr, true);
+ long [] buffer = new long [1];
+ long [] len = new long [1];
+ if (type == null) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
+ GDK.gdk_pixbuf_save_to_bufferv(pixbuf, buffer, len, type, null, null, null);
+ byte[] byteArray = new byte[(int) len[0]];
+ C.memmove(byteArray, buffer[0], byteArray.length);
+ try {
+ stream.write(byteArray);
+ } catch (IOException e) {
+ SWT.error(SWT.ERROR_IO);
+ }
+}
+
+/**
+ * Saves the image data in this ImageLoader to a file with the specified name.
+ * The format parameter can have one of the following values:
+ * <dl>
+ * <dt><code>IMAGE_BMP</code></dt>
+ * <dd>Windows BMP file format, no compression</dd>
+ * <dt><code>IMAGE_BMP_RLE</code></dt>
+ * <dd>Windows BMP file format, RLE compression if appropriate</dd>
+ * <dt><code>IMAGE_GIF</code></dt>
+ * <dd>GIF file format</dd>
+ * <dt><code>IMAGE_ICO</code></dt>
+ * <dd>Windows ICO file format</dd>
+ * <dt><code>IMAGE_JPEG</code></dt>
+ * <dd>JPEG file format</dd>
+ * <dt><code>IMAGE_PNG</code></dt>
+ * <dd>PNG file format</dd>
+ * <dt><code>IMAGE_TIFF</code></dt>
+ * <dd>TIFF file format</dd>
+ * </dl>
+ *
+ * @param filename the name of the file to write the images to
+ * @param format the format to write the images in
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_IO - if an IO error occurs while writing to the file</li>
+ * <li>ERROR_INVALID_IMAGE - if the image data contains invalid data</li>
+ * <li>ERROR_UNSUPPORTED_FORMAT - if the image data cannot be saved to the requested format</li>
+ * </ul>
+ */
+public void save(String filename, int format) {
+ if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ OutputStream stream = null;
+ try {
+ stream = new FileOutputStream(filename);
+ } catch (IOException e) {
+ SWT.error(SWT.ERROR_IO, e);
+ }
+ save(stream, format);
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+}
+
+/**
+ * Adds the listener to the collection of listeners who will be
+ * notified when image data is either partially or completely loaded.
+ * <p>
+ * An ImageLoaderListener should be added before invoking
+ * one of the receiver's load methods. The listener's
+ * <code>imageDataLoaded</code> method is called when image
+ * data has been partially loaded, as is supported by interlaced
+ * GIF/PNG or progressive JPEG images.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @see ImageLoaderListener
+ * @see ImageLoaderEvent
+ */
+public void addImageLoaderListener(ImageLoaderListener listener) {
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ if (imageLoaderListeners == null) {
+ imageLoaderListeners = new ArrayList<>();
+ }
+ imageLoaderListeners.add(listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will be
+ * notified when image data is either partially or completely loaded.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @see #addImageLoaderListener(ImageLoaderListener)
+ */
+public void removeImageLoaderListener(ImageLoaderListener listener) {
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ if (imageLoaderListeners == null) return;
+ imageLoaderListeners.remove(listener);
+}
+
+/**
+ * Returns <code>true</code> if the receiver has image loader
+ * listeners, and <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if there are <code>ImageLoaderListener</code>s, and <code>false</code> otherwise
+ *
+ * @see #addImageLoaderListener(ImageLoaderListener)
+ * @see #removeImageLoaderListener(ImageLoaderListener)
+ */
+public boolean hasListeners() {
+ return imageLoaderListeners != null && imageLoaderListeners.size() > 0;
+}
+
+/**
+ * Notifies all image loader listeners that an image loader event
+ * has occurred. Pass the specified event object to each listener.
+ *
+ * @param event the <code>ImageLoaderEvent</code> to send to each <code>ImageLoaderListener</code>
+ */
+public void notifyListeners(ImageLoaderEvent event) {
+ if (!hasListeners()) return;
+ int size = imageLoaderListeners.size();
+ for (int i = 0; i < size; i++) {
+ ImageLoaderListener listener = imageLoaderListeners.get(i);
+ listener.imageDataLoaded(event);
+ }
+}
+
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
new file mode 100644
index 0000000000..a8914ea4af
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/ImageLoader.java
@@ -0,0 +1,348 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.graphics;
+
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.internal.image.*;
+
+/**
+ * Instances of this class are used to load images from,
+ * and save images to, a file or stream.
+ * <p>
+ * Currently supported image formats are:
+ * </p><ul>
+ * <li>BMP (Windows or OS/2 Bitmap)</li>
+ * <li>ICO (Windows Icon)</li>
+ * <li>JPEG</li>
+ * <li>GIF</li>
+ * <li>PNG</li>
+ * <li>TIFF</li>
+ * </ul>
+ * <code>ImageLoaders</code> can be used to:
+ * <ul>
+ * <li>load/save single images in all formats</li>
+ * <li>load/save multiple images (GIF/ICO/TIFF)</li>
+ * <li>load/save animated GIF images</li>
+ * <li>load interlaced GIF/PNG images</li>
+ * <li>load progressive JPEG images</li>
+ * </ul>
+ *
+ * <p>
+ * NOTE: <code>ImageLoader</code> is implemented in Java on some platforms, which has
+ * certain performance implications. Performance and memory sensitive applications may
+ * benefit from using one of the constructors provided by <code>Image</code>, as these
+ * are implemented natively.</p>
+ *
+ * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ImageAnalyzer</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ */
+public class ImageLoader {
+
+ /**
+ * the array of ImageData objects in this ImageLoader.
+ * This array is read in when the load method is called,
+ * and it is written out when the save method is called
+ */
+ public ImageData[] data;
+
+ /**
+ * the width of the logical screen on which the images
+ * reside, in pixels (this corresponds to the GIF89a
+ * Logical Screen Width value)
+ */
+ public int logicalScreenWidth;
+
+ /**
+ * the height of the logical screen on which the images
+ * reside, in pixels (this corresponds to the GIF89a
+ * Logical Screen Height value)
+ */
+ public int logicalScreenHeight;
+
+ /**
+ * the background pixel for the logical screen (this
+ * corresponds to the GIF89a Background Color Index value).
+ * The default is -1 which means 'unspecified background'
+ *
+ */
+ public int backgroundPixel;
+
+ /**
+ * the number of times to repeat the display of a sequence
+ * of animated images (this corresponds to the commonly-used
+ * GIF application extension for "NETSCAPE 2.0 01").
+ * The default is 1. A value of 0 means 'display repeatedly'
+ */
+ public int repeatCount;
+
+ /**
+ * This is the compression used when saving jpeg and png files.
+ * <p>
+ * When saving jpeg files, the value is from 1 to 100,
+ * where 1 is very high compression but low quality, and 100 is
+ * no compression and high quality; default is 75.
+ * </p><p>
+ * When saving png files, the value is from 0 to 3, but they do not impact the quality
+ * because PNG is lossless compression. 0 is uncompressed, 1 is low compression and fast,
+ * 2 is default compression, and 3 is high compression but slow.
+ * </p>
+ *
+ * @since 3.8
+ */
+ public int compression;
+
+ /*
+ * the set of ImageLoader event listeners, created on demand
+ */
+ List<ImageLoaderListener> imageLoaderListeners;
+
+/**
+ * Construct a new empty ImageLoader.
+ */
+public ImageLoader() {
+ reset();
+}
+
+/**
+ * Resets the fields of the ImageLoader, except for the
+ * <code>imageLoaderListeners</code> field.
+ */
+void reset() {
+ data = null;
+ logicalScreenWidth = 0;
+ logicalScreenHeight = 0;
+ backgroundPixel = -1;
+ repeatCount = 1;
+ compression = -1;
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * specified input stream. Throws an error if either an error
+ * occurs while loading the images, or if the images are not
+ * of a supported type. Returns the loaded image data array.
+ *
+ * @param stream the input stream to load the images from
+ * @return an array of <code>ImageData</code> objects loaded from the specified input stream
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_IO - if an IO error occurs while reading from the stream</li>
+ * <li>ERROR_INVALID_IMAGE - if the image stream contains invalid data</li>
+ * <li>ERROR_UNSUPPORTED_FORMAT - if the image stream contains an unrecognized format</li>
+ * </ul>
+ */
+public ImageData[] load(InputStream stream) {
+ if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ reset();
+ data = FileFormat.load(stream, this);
+ return data;
+}
+
+/**
+ * Loads an array of <code>ImageData</code> objects from the
+ * file with the specified name. Throws an error if either
+ * an error occurs while loading the images, or if the images are
+ * not of a supported type. Returns the loaded image data array.
+ *
+ * @param filename the name of the file to load the images from
+ * @return an array of <code>ImageData</code> objects loaded from the specified file
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_IO - if an IO error occurs while reading from the file</li>
+ * <li>ERROR_INVALID_IMAGE - if the image file contains invalid data</li>
+ * <li>ERROR_UNSUPPORTED_FORMAT - if the image file contains an unrecognized format</li>
+ * </ul>
+ */
+public ImageData[] load(String filename) {
+ if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ InputStream stream = null;
+ try {
+ stream = new FileInputStream(filename);
+ return load(stream);
+ } catch (IOException e) {
+ SWT.error(SWT.ERROR_IO, e);
+ } finally {
+ try {
+ if (stream != null) stream.close();
+ } catch (IOException e) {
+ // Ignore error
+ }
+ }
+ return null;
+}
+
+/**
+ * Saves the image data in this ImageLoader to the specified stream.
+ * The format parameter can have one of the following values:
+ * <dl>
+ * <dt><code>IMAGE_BMP</code></dt>
+ * <dd>Windows BMP file format, no compression</dd>
+ * <dt><code>IMAGE_BMP_RLE</code></dt>
+ * <dd>Windows BMP file format, RLE compression if appropriate</dd>
+ * <dt><code>IMAGE_GIF</code></dt>
+ * <dd>GIF file format</dd>
+ * <dt><code>IMAGE_ICO</code></dt>
+ * <dd>Windows ICO file format</dd>
+ * <dt><code>IMAGE_JPEG</code></dt>
+ * <dd>JPEG file format</dd>
+ * <dt><code>IMAGE_PNG</code></dt>
+ * <dd>PNG file format</dd>
+ * </dl>
+ *
+ * @param stream the output stream to write the images to
+ * @param format the format to write the images in
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the stream is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_IO - if an IO error occurs while writing to the stream</li>
+ * <li>ERROR_INVALID_IMAGE - if the image data contains invalid data</li>
+ * <li>ERROR_UNSUPPORTED_FORMAT - if the image data cannot be saved to the requested format</li>
+ * </ul>
+ */
+public void save(OutputStream stream, int format) {
+ if (stream == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ FileFormat.save(stream, format, this);
+}
+
+/**
+ * Saves the image data in this ImageLoader to a file with the specified name.
+ * The format parameter can have one of the following values:
+ * <dl>
+ * <dt><code>IMAGE_BMP</code></dt>
+ * <dd>Windows BMP file format, no compression</dd>
+ * <dt><code>IMAGE_BMP_RLE</code></dt>
+ * <dd>Windows BMP file format, RLE compression if appropriate</dd>
+ * <dt><code>IMAGE_GIF</code></dt>
+ * <dd>GIF file format</dd>
+ * <dt><code>IMAGE_ICO</code></dt>
+ * <dd>Windows ICO file format</dd>
+ * <dt><code>IMAGE_JPEG</code></dt>
+ * <dd>JPEG file format</dd>
+ * <dt><code>IMAGE_PNG</code></dt>
+ * <dd>PNG file format</dd>
+ * </dl>
+ *
+ * @param filename the name of the file to write the images to
+ * @param format the format to write the images in
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the file name is null</li>
+ * </ul>
+ * @exception SWTException <ul>
+ * <li>ERROR_IO - if an IO error occurs while writing to the file</li>
+ * <li>ERROR_INVALID_IMAGE - if the image data contains invalid data</li>
+ * <li>ERROR_UNSUPPORTED_FORMAT - if the image data cannot be saved to the requested format</li>
+ * </ul>
+ */
+public void save(String filename, int format) {
+ if (filename == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ OutputStream stream = null;
+ try {
+ stream = new FileOutputStream(filename);
+ } catch (IOException e) {
+ SWT.error(SWT.ERROR_IO, e);
+ }
+ save(stream, format);
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+}
+
+/**
+ * Adds the listener to the collection of listeners who will be
+ * notified when image data is either partially or completely loaded.
+ * <p>
+ * An ImageLoaderListener should be added before invoking
+ * one of the receiver's load methods. The listener's
+ * <code>imageDataLoaded</code> method is called when image
+ * data has been partially loaded, as is supported by interlaced
+ * GIF/PNG or progressive JPEG images.
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @see ImageLoaderListener
+ * @see ImageLoaderEvent
+ */
+public void addImageLoaderListener(ImageLoaderListener listener) {
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ if (imageLoaderListeners == null) {
+ imageLoaderListeners = new ArrayList<>();
+ }
+ imageLoaderListeners.add(listener);
+}
+
+/**
+ * Removes the listener from the collection of listeners who will be
+ * notified when image data is either partially or completely loaded.
+ *
+ * @param listener the listener which should no longer be notified
+ *
+ * @exception IllegalArgumentException <ul>
+ * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+ * </ul>
+ *
+ * @see #addImageLoaderListener(ImageLoaderListener)
+ */
+public void removeImageLoaderListener(ImageLoaderListener listener) {
+ if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+ if (imageLoaderListeners == null) return;
+ imageLoaderListeners.remove(listener);
+}
+
+/**
+ * Returns <code>true</code> if the receiver has image loader
+ * listeners, and <code>false</code> otherwise.
+ *
+ * @return <code>true</code> if there are <code>ImageLoaderListener</code>s, and <code>false</code> otherwise
+ *
+ * @see #addImageLoaderListener(ImageLoaderListener)
+ * @see #removeImageLoaderListener(ImageLoaderListener)
+ */
+public boolean hasListeners() {
+ return imageLoaderListeners != null && imageLoaderListeners.size() > 0;
+}
+
+/**
+ * Notifies all image loader listeners that an image loader event
+ * has occurred. Pass the specified event object to each listener.
+ *
+ * @param event the <code>ImageLoaderEvent</code> to send to each <code>ImageLoaderListener</code>
+ */
+public void notifyListeners(ImageLoaderEvent event) {
+ if (!hasListeners()) return;
+ int size = imageLoaderListeners.size();
+ for (int i = 0; i < size; i++) {
+ ImageLoaderListener listener = imageLoaderListeners.get(i);
+ listener.imageDataLoaded(event);
+ }
+}
+
+}
diff --git a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug545032_ImageLoaderBenchmark.java b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug545032_ImageLoaderBenchmark.java
new file mode 100644
index 0000000000..3e2c712f63
--- /dev/null
+++ b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug545032_ImageLoaderBenchmark.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Red Hat and others. All rights reserved.
+ * The contents of this file are made available under the terms
+ * of the GNU Lesser General Public License (LGPL) Version 2.1 that
+ * accompanies this distribution (lgpl-v21.txt). The LGPL is also
+ * available at http://www.gnu.org/licenses/lgpl.html. If the version
+ * of the LGPL at http://www.gnu.org is different to the version of
+ * the LGPL accompanying this distribution and there is any conflict
+ * between the two license versions, the terms of the LGPL accompanying
+ * this distribution shall govern.
+ *
+ * Contributors:
+ * Red Hat - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.tests.gtk.snippets;
+
+import java.io.File;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+
+public class Bug545032_ImageLoaderBenchmark {
+ // Absolute path to a directory containing benchmark images
+ static final String BENCHMARK_DIR = new String("/home/xiyan/Pictures/benchmarks/");
+
+ public static void main(String[] args) {
+ final File folder = new File(BENCHMARK_DIR);
+
+ ImageLoader loader = new ImageLoader();
+
+ for (File fileEntry : folder.listFiles()) {
+ String filePath = fileEntry.getAbsolutePath();
+ long startTime = System.nanoTime();
+ ImageData [] imgData = loader.load(filePath);
+ long endTime = System.nanoTime();
+ long duration = (endTime - startTime) / 1000000;
+ if (imgData != null && imgData.length > 0) {
+ ImageData img = imgData[0];
+ System.out.println("Loading " + fileEntry.getName() + " (" + img.width + "x" + img.height + ") takes " + duration + "ms");
+ }
+ }
+ }
+}

Back to the top