Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Williams2019-05-30 15:28:52 -0400
committerEric Williams2019-06-10 11:25:46 -0400
commit0b22592e3f6c547b0bec5a5b491bd41a583e8e60 (patch)
treeff72a2891a1d246cb8320107f596630b7f032ab8
parent1f251edf602f89449cb3c484a6fd9218d9dcc2aa (diff)
downloadeclipse.platform.swt-0b22592e3f6c547b0bec5a5b491bd41a583e8e60.tar.gz
eclipse.platform.swt-0b22592e3f6c547b0bec5a5b491bd41a583e8e60.tar.xz
eclipse.platform.swt-0b22592e3f6c547b0bec5a5b491bd41a583e8e60.zip
Bug 545032: [GTK] Implement native ImageLoader
Re-implement the native ImageLoader based on Xi's work. Differences from this patch vs the old one: -Image and Device use CAIRO_CONTENT_COLOR_ALPHA (4 channel) surfaces -ImageData data arrays provided to us in ImageLoader.* methods are already in RGBA order, big/little endian considerations are only needed when loading/saving data from/to Cairo -GdkPixbuf expects the data in RGBA byte order -Images with bit depth < 24 seem broken, due to Image and other places hardcoding bit depth to 32. This is broken before this patch as well (see bug 38232) Tested using the test snippet attached, as well as the snippets from bug 547529. Icons in a child Eclipse look fine. Test environment is Fedora 30 with GTK3.24.8. No AllNonBrowserTest failures. Change-Id: Ia7235100784bd34643503d6b2dfa26ad08998eb5 Signed-off-by: Eric Williams <ericwill@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.java23
-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/gtk/org/eclipse/swt/graphics/Device.java2
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java2
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java681
-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_ImageLoaderTesting.java100
-rw-r--r--tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_ImageLoader.java18
12 files changed, 1546 insertions, 8 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 698a049061..f88e3fd40d 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
@@ -1527,6 +1527,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((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((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((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((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((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((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)
@@ -1537,6 +1621,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((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)
@@ -1561,6 +1681,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)
@@ -1613,6 +1745,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)
@@ -1665,6 +1821,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)
@@ -14856,6 +15024,16 @@ fail:
}
#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_1getenv
JNIEXPORT jlong JNICALL OS_NATIVE(_1g_1getenv)
(JNIEnv *env, jclass that, jbyteArray arg0)
@@ -15821,6 +15999,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 dcb6536bff..3d9d40141a 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
@@ -125,17 +125,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",
@@ -1225,6 +1239,7 @@ char * OS_nativeFunctionNames[] = {
"_1g_1filename_1from_1utf8",
"_1g_1filename_1to_1uri",
"_1g_1filename_1to_1utf8",
+ "_1g_1get_1current_1time",
"_1g_1getenv",
"_1g_1hash_1table_1get_1values",
"_1g_1icon_1new_1for_1string",
@@ -1299,6 +1314,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 c81fa779a3..acb253ec53 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
@@ -135,17 +135,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,
@@ -1199,6 +1213,7 @@ typedef enum {
_1g_1filename_1from_1utf8_FUNC,
_1g_1filename_1to_1uri_FUNC,
_1g_1filename_1to_1utf8_FUNC,
+ _1g_1get_1current_1time_FUNC,
_1g_1getenv_FUNC,
_1g_1hash_1table_1get_1values_FUNC,
_1g_1icon_1new_1for_1string_FUNC,
@@ -1273,6 +1288,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 9de1f9675d..6132c5c132 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
@@ -1459,6 +1459,72 @@ public class GDK extends OS {
lock.unlock();
}
}
+ /** @param animation cast=(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=(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=(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=(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=(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=(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 *)
@@ -1522,6 +1588,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=(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 07ff45dec4..ba04f401e8 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
@@ -1721,6 +1721,29 @@ public static final long g_getenv(byte [] variable) {
lock.unlock();
}
}
+/** @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 table cast=(GHashTable *)
*/
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..bbe5ac8d51 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/gtk/org/eclipse/swt/graphics/Device.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Device.java
index 66f7982e39..223b5060c6 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Device.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Device.java
@@ -667,7 +667,7 @@ protected void init () {
surface = Cairo.cairo_image_surface_create(Cairo.CAIRO_FORMAT_RGB24, 10, 10);
} else {
gdkResource = GDK.gdk_get_default_root_window();
- surface = GDK.gdk_window_create_similar_surface(gdkResource, Cairo.CAIRO_CONTENT_COLOR, 10, 10);
+ surface = GDK.gdk_window_create_similar_surface(gdkResource, Cairo.CAIRO_CONTENT_COLOR_ALPHA, 10, 10);
}
Cairo.cairo_surface_get_device_scale(surface, sx, sy);
DPIUtil.setUseCairoAutoScale((sx[0]*100) == scaleFactor);
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java
index f1e3372130..961d0fdd04 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java
@@ -1252,7 +1252,7 @@ void init(int width, int height) {
if (GTK.GTK4) {
surface = Cairo.cairo_image_surface_create(Cairo.CAIRO_FORMAT_RGB24, width, height);
} else {
- surface = GDK.gdk_window_create_similar_surface(GDK.gdk_get_default_root_window(), Cairo.CAIRO_CONTENT_COLOR, width, height);
+ surface = GDK.gdk_window_create_similar_surface(GDK.gdk_get_default_root_window(), Cairo.CAIRO_CONTENT_COLOR_ALPHA, width, height);
}
if (surface == 0) SWT.error(SWT.ERROR_NO_HANDLES);
// When we create a blank image we need to set it to 100 in GTK3 as we draw using 100% scale.
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..a6612becd3
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java
@@ -0,0 +1,681 @@
+/*******************************************************************************
+ * 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);
+}
+
+/**
+ * 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);
+ if (this.data == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ ImageData imgData = this.data [0];
+ int colorspace = GDK.GDK_COLORSPACE_RGB;
+ boolean alpha_supported = format == SWT.IMAGE_TIFF || format == SWT.IMAGE_PNG || format == SWT.IMAGE_ICO;
+ boolean has_alpha = imgData.alphaData != null && alpha_supported;
+ int width = imgData.width;
+ int height = imgData.height;
+ int n_channels = imgData.bytesPerLine / width; // original n_channels 3 or 4
+ int bytes_per_pixel = imgData.bytesPerLine / width; // n_channels for original ImageData (width * height * bytes_per_pixel) = imgData.length
+
+ /*
+ * Destination offsets, ImageData data provided is in RGBA format.
+ *
+ */
+ int da, dr, dg, db;
+ da = 3; dr = 0; dg = 1; db = 2;
+
+ if (has_alpha && bytes_per_pixel == 3) {
+ bytes_per_pixel = 4;
+ }
+
+ // We use alpha by default now so just hard code bytes per pixel to 4
+ byte[] srcData = new byte[(width * height * 4)];
+
+ int alpha_offset = n_channels == 4 ? 1 : 0;
+ if (has_alpha) {
+ for (int y = 0, offset = 0, new_offset = 0, alphaOffset = 0; y < height; y++) {
+ for (int x = 0; x < width; x++, offset += n_channels, new_offset += bytes_per_pixel) {
+ byte a = imgData.alphaData[alphaOffset++];
+ // ImageData data is stored in RGBA format
+ byte r = imgData.data[offset + alpha_offset + 0];
+ byte g = imgData.data[offset + alpha_offset + 1];
+ byte b = imgData.data[offset + alpha_offset + 2];
+
+ // GdkPixbuf expects RGBA format
+ srcData[new_offset + db] = b;
+ srcData[new_offset + dg] = g;
+ srcData[new_offset + dr] = r;
+ srcData[new_offset + da] = a;
+ }
+ }
+ } else {
+ for (int y = 0, offset = 0, new_offset = 0; y < height; y++) {
+ for (int x = 0; x < width; x++, offset += n_channels, new_offset += bytes_per_pixel) {
+ byte r = imgData.data[offset + alpha_offset + 0];
+ byte g = imgData.data[offset + alpha_offset + 1];
+ byte b = imgData.data[offset + alpha_offset + 2];
+ byte a = (byte) 255;
+
+ srcData[new_offset + db] = b;
+ srcData[new_offset + dg] = g;
+ srcData[new_offset + dr] = r;
+ srcData[new_offset + da] = a;
+ }
+ }
+ }
+
+ // Get GdkPixbuf from pixel data buffer
+ long buffer_ptr = OS.g_malloc(srcData.length);
+ C.memmove(buffer_ptr, srcData, srcData.length);
+ int rowstride = srcData.length / height;
+ // We use alpha in all cases, if no alpha is provided then it's just 255
+ long pixbuf = GDK.gdk_pixbuf_new_from_data (buffer_ptr, colorspace, true, 8, width, height, rowstride, 0, 0);
+ if (pixbuf == 0) {
+ OS.g_free(buffer_ptr);
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+
+ // Write pixbuf to byte array and then to OutputStream
+ String typeStr = "";
+ switch (format) {
+ case SWT.IMAGE_BMP_RLE: typeStr = "bmp"; break;
+ 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];
+ if (type == null || typeStr == "") {
+ OS.g_free(buffer_ptr);
+ SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT);
+ }
+ long [] len = new long [1];
+ 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) {
+ OS.g_free(buffer_ptr);
+ SWT.error(SWT.ERROR_IO);
+ }
+ // must free buffer_ptr last otherwise we get half/corrupted image
+ OS.g_free(buffer_ptr);
+}
+
+/**
+ * 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..636ba6d032
--- /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_ImageLoaderTesting.java b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug545032_ImageLoaderTesting.java
new file mode 100644
index 0000000000..2f63538bba
--- /dev/null
+++ b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug545032_ImageLoaderTesting.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Simeon Andreev 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:
+ * Simeon Andreev - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.tests.gtk.snippets;
+
+import java.io.File;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/*
+ * Title: Bug 545032 - [GTK] Implement native ImageLoader
+ * How to run: run snippet and two images side-by-side.
+ * Bug description: any malfunction will be on the right side of the shell.
+ * Expected results: two RGBA patterns side-by-side, looking identical.
+ * Note that the last box may be black, due to the different alpha values.
+ * GTK Version(s): all GTK versions.
+ */
+public class Bug545032_ImageLoaderTesting {
+
+ public static void main(String[] args) throws Exception {
+ File file = File.createTempFile("swt", "example");
+
+ Display display = new Display();
+ Shell shell = new Shell(display);
+ shell.setLayout(new FillLayout());
+ shell.setSize(500, 500);
+ shell.setText("Bug 545032: Native ImageLoader Testing");
+
+ shell.open();
+
+ int imageWidth = 200;
+ int imageHeight = 200;
+ int bitDepth = 32;
+ ImageData imageData = new ImageData(imageWidth, imageHeight, bitDepth, new PaletteData(0xFF0000, 0x00FF00, 0x0000FF));
+ int w = imageWidth / 2;
+ int h = imageHeight / 2;
+ for (int y = 0; y < imageHeight; ++y) {
+ for (int x = 0; x < imageWidth; ++x) {
+ // Setting this to 128 will make all colors half-transparent
+ int alpha = 255;
+ int color = 0x000000;
+ if (x < w && y < h) {
+ color = 0xFF0000;
+ } else if (x < w && y >= h) {
+ color = 0x00FF00;
+ } else if (x >= w && y < h) {
+ color = 0x0000FF;
+ } else {
+ alpha = 128;
+ }
+ imageData.setPixel(x, y, color);
+ // Comment this line to test without transparency in the last box
+ imageData.setAlpha(x, y, alpha);
+ }
+ }
+
+ Image image = new Image(display, imageData);
+ new Label(shell, SWT.BORDER).setImage(image);
+
+ ImageLoader saver = new ImageLoader();
+ saver.data = new ImageData[] { image.getImageData() };
+ file.delete();
+ saver.save(file.getAbsolutePath(), SWT.IMAGE_PNG);
+
+ ImageLoader loader = new ImageLoader();
+ ImageData[] loaded = loader.load(file.getAbsolutePath());
+ for (ImageData imageLoadData : loaded) {
+ Label l = new Label(shell, SWT.BORDER);
+ image = new Image(display, imageLoadData);
+ l.setImage(image);
+ }
+ shell.pack();
+ file.delete();
+
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+ }
+}
diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_ImageLoader.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_ImageLoader.java
index 16a5ad56bd..5100bde26c 100644
--- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_ImageLoader.java
+++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_ImageLoader.java
@@ -188,8 +188,22 @@ public void test_bug547529() {
try {
int imageWidth = 8;
int imageHeight = 8;
- int imageDepth = 24;
-
+ int imageDepth;
+ /*
+ * Native ImageLoader on GTK uses GdkPixbuf, which only supports
+ * 3/4 channels with 8 bits per channel. Since we use the alpha channel
+ * in all cases, ImageData loaded by GdkPixbuf will be 32 bit color depth.
+ * Furthermore, loaded images will result have direct PaletteData with RGB masks, since
+ * there is no way to determine indexed PaletteData info.
+ *
+ * Because of this, adjust the image depth for this test case accordingly.
+ */
+ if (SwtTestUtil.isGTK) {
+ imageDepth = 32;
+
+ } else {
+ imageDepth = 24;
+ }
int RED = 0xFF0000;
int GREEN = 0x00FF00;
int BLUE = 0x0000FF;

Back to the top