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 58a716c597e..24178bd35c9 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 @@ -15270,6 +15270,18 @@ JNIEXPORT void JNICALL OS_NATIVE(swt_1fixed_1restack) } #endif +#ifndef NO_swt_1scaled_1paintable_1new +JNIEXPORT jlong JNICALL OS_NATIVE(swt_1scaled_1paintable_1new) + (JNIEnv *env, jclass that, jlong arg0, jint arg1, jint arg2) +{ + jlong rc = 0; + OS_NATIVE_ENTER(env, that, swt_1scaled_1paintable_1new_FUNC); + rc = (jlong)swt_scaled_paintable_new((GdkTexture*)arg0, arg1, arg2); + OS_NATIVE_EXIT(env, that, swt_1scaled_1paintable_1new_FUNC); + return rc; +} +#endif + #ifndef NO_swt_1set_1lock_1functions JNIEXPORT void JNICALL OS_NATIVE(swt_1set_1lock_1functions) (JNIEnv *env, jclass that) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.h index 594476277f3..6f8011af45e 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.h +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.h @@ -335,6 +335,9 @@ #define NO_swt_1fixed_1add #define NO_swt_1fixed_1remove +// No GdkPaintable on GTK3 +#define NO_swt_1scaled_1paintable_1new + #endif #include "os_custom.h" diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.c index 10364a365f7..0b2f7acce92 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.c @@ -2263,3 +2263,96 @@ JNIEXPORT jlong JNICALL OS_NATIVE(content_1providers_1create_1gvalue) OS_NATIVE_EXIT(env, that, content_1providers_1create_1gvalue_FUNC) return (jlong)v; } + +#if defined(GTK4) +/* + * SwtScaledPaintable: a GdkPaintable wrapping a GdkTexture that advertises a + * fixed logical (point) intrinsic size. GtkPicture measures a plain GdkTexture + * using its device-pixel size, which inflates widgets at HiDPI. By reporting the + * logical size here while drawing the full-resolution texture, images keep their + * correct logical footprint and render crisply (no down/up-scaling). + */ +#define SWT_TYPE_SCALED_PAINTABLE (swt_scaled_paintable_get_type ()) +G_DECLARE_FINAL_TYPE (SwtScaledPaintable, swt_scaled_paintable, SWT, SCALED_PAINTABLE, GObject) + +struct _SwtScaledPaintable +{ + GObject parent_instance; + GdkTexture* texture; + int width; + int height; +}; + +static void swt_scaled_paintable_paintable_init (GdkPaintableInterface* iface); + +G_DEFINE_TYPE_WITH_CODE (SwtScaledPaintable, swt_scaled_paintable, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, swt_scaled_paintable_paintable_init)) + +static void swt_scaled_paintable_snapshot (GdkPaintable* paintable, GdkSnapshot* snapshot, double width, double height) +{ + SwtScaledPaintable* self = SWT_SCALED_PAINTABLE (paintable); + if (self->texture != NULL) { + /* Draw the full device-resolution texture into the (logical) box. */ + gdk_paintable_snapshot (GDK_PAINTABLE (self->texture), snapshot, width, height); + } +} + +static int swt_scaled_paintable_get_intrinsic_width (GdkPaintable* paintable) +{ + return SWT_SCALED_PAINTABLE (paintable)->width; +} + +static int swt_scaled_paintable_get_intrinsic_height (GdkPaintable* paintable) +{ + return SWT_SCALED_PAINTABLE (paintable)->height; +} + +static double swt_scaled_paintable_get_intrinsic_aspect_ratio (GdkPaintable* paintable) +{ + SwtScaledPaintable* self = SWT_SCALED_PAINTABLE (paintable); + if (self->height <= 0) return 0.0; + return (double) self->width / (double) self->height; +} + +static GdkPaintableFlags swt_scaled_paintable_get_flags (GdkPaintable* paintable) +{ + return GDK_PAINTABLE_STATIC_SIZE | GDK_PAINTABLE_STATIC_CONTENTS; +} + +static void swt_scaled_paintable_paintable_init (GdkPaintableInterface* iface) +{ + iface->snapshot = swt_scaled_paintable_snapshot; + iface->get_intrinsic_width = swt_scaled_paintable_get_intrinsic_width; + iface->get_intrinsic_height = swt_scaled_paintable_get_intrinsic_height; + iface->get_intrinsic_aspect_ratio = swt_scaled_paintable_get_intrinsic_aspect_ratio; + iface->get_flags = swt_scaled_paintable_get_flags; +} + +static void swt_scaled_paintable_dispose (GObject* object) +{ + SwtScaledPaintable* self = SWT_SCALED_PAINTABLE (object); + g_clear_object (&self->texture); + G_OBJECT_CLASS (swt_scaled_paintable_parent_class)->dispose (object); +} + +static void swt_scaled_paintable_class_init (SwtScaledPaintableClass* klass) +{ + G_OBJECT_CLASS (klass)->dispose = swt_scaled_paintable_dispose; +} + +static void swt_scaled_paintable_init (SwtScaledPaintable* self) +{ + self->texture = NULL; + self->width = 0; + self->height = 0; +} + +GdkPaintable* swt_scaled_paintable_new (GdkTexture* texture, int width, int height) +{ + SwtScaledPaintable* self = g_object_new (SWT_TYPE_SCALED_PAINTABLE, NULL); + self->texture = texture != NULL ? g_object_ref (texture) : NULL; + self->width = width; + self->height = height; + return GDK_PAINTABLE (self); +} +#endif /* GTK4 */ diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h index 7196913c75e..0c077513368 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_custom.h @@ -135,6 +135,15 @@ struct _SwtFixedClass GType swt_fixed_get_type (void) G_GNUC_CONST; +#if defined(GTK4) +/* + * GdkPaintable that wraps a GdkTexture but advertises a fixed logical (point) + * intrinsic size. Used so GtkPicture measures images in logical points (avoiding + * HiDPI inflation) while still drawing the full device-resolution texture crisply. + */ +GdkPaintable* swt_scaled_paintable_new (GdkTexture* texture, int width, int height); +#endif + #if defined(GTK4) void swt_fixed_add(SwtFixed* fixed, GtkWidget* widget); void swt_fixed_remove(SwtFixed* fixed, GtkWidget* widget); 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 442262a9d75..f30ca89d0ad 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 @@ -1233,6 +1233,7 @@ typedef enum { swt_1fixed_1remove_FUNC, swt_1fixed_1resize_FUNC, swt_1fixed_1restack_FUNC, + swt_1scaled_1paintable_1new_FUNC, swt_1set_1lock_1functions_FUNC, ubuntu_1menu_1proxy_1get_FUNC, } OS_FUNCS; 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 bc668ae3105..59edf503279 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 @@ -579,6 +579,12 @@ public static String getEnvironmentalVariable (String envVarName) { /** @category custom */ public static final native long swt_fixed_get_type(); + /** + * @param texture cast=(GdkTexture*) + * @category custom + */ + public static final native long swt_scaled_paintable_new(long texture, int width, int height); + /** @category custom */ public static final native long swt_fixed_accessible_get_type(); /** diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/ImageList.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/ImageList.java index eaa575e8baa..16b3044bc5f 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/ImageList.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/ImageList.java @@ -176,13 +176,6 @@ public static long createPixbuf(Image image) { } } Cairo.cairo_surface_destroy(surface); - if (GTK.GTK4) { - //Scaling needed as the image surface will be different from the image size depending on the display scale - long scaledPixbuf = GDK.gdk_pixbuf_scale_simple(pixbuf, image.getBounds().width, image.getBounds().height, GDK.GDK_INTERP_BILINEAR); - OS.g_object_unref(pixbuf); - pixbuf = scaledPixbuf; - } - return pixbuf; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java index 75261db53ad..d503ec4344f 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java @@ -1158,8 +1158,11 @@ private void _setImage (Image image) { long pixbuf = ImageList.createPixbuf(image); long texture = GDK.gdk_texture_new_for_pixbuf(pixbuf); OS.g_object_unref(pixbuf); - GTK4.gtk_picture_set_paintable(imageHandle, texture); + Rectangle bounds = image.getBounds(); + long paintable = OS.swt_scaled_paintable_new(texture, bounds.width, bounds.height); OS.g_object_unref(texture); + GTK4.gtk_picture_set_paintable(imageHandle, paintable); + OS.g_object_unref(paintable); } else { GTK3.gtk_image_set_from_surface(imageHandle, image.surface); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ExpandItem.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ExpandItem.java index 00316ae2b2a..33fa4e1e9bc 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ExpandItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ExpandItem.java @@ -518,7 +518,11 @@ public void setImage (Image image) { long pixbuf = ImageList.createPixbuf(image); long texture = GDK.gdk_texture_new_for_pixbuf(pixbuf); OS.g_object_unref(pixbuf); - GTK4.gtk_picture_set_paintable(imageHandle, texture); + Rectangle bounds = image.getBounds(); + long paintable = OS.swt_scaled_paintable_new(texture, bounds.width, bounds.height); + OS.g_object_unref(texture); + GTK4.gtk_picture_set_paintable(imageHandle, paintable); + OS.g_object_unref(paintable); } else { GTK3.gtk_image_set_from_surface(imageHandle, image.surface); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Label.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Label.java index 12806dfee32..b227e86c608 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Label.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Label.java @@ -650,8 +650,11 @@ public void setImage (Image image) { long pixbuf = ImageList.createPixbuf(image); long texture = GDK.gdk_texture_new_for_pixbuf(pixbuf); OS.g_object_unref(pixbuf); - GTK4.gtk_picture_set_paintable(imageHandle, texture); + Rectangle bounds = image.getBounds(); + long paintable = OS.swt_scaled_paintable_new(texture, bounds.width, bounds.height); OS.g_object_unref(texture); + GTK4.gtk_picture_set_paintable(imageHandle, paintable); + OS.g_object_unref(paintable); } else { GTK3.gtk_image_set_from_surface(imageHandle, image.surface); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java index 67fa95b10b4..d02f04f91f5 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java @@ -781,8 +781,11 @@ void gtk4_enter_event(long controller, double x, double y, long event) { long pixbuf = ImageList.createPixbuf(hotImage); long texture = GDK.gdk_texture_new_for_pixbuf(pixbuf); OS.g_object_unref(pixbuf); - GTK4.gtk_picture_set_paintable(imageHandle, texture); + Rectangle bounds = hotImage.getBounds(); + long paintable = OS.swt_scaled_paintable_new(texture, bounds.width, bounds.height); OS.g_object_unref(texture); + GTK4.gtk_picture_set_paintable(imageHandle, paintable); + OS.g_object_unref(paintable); } } } @@ -853,8 +856,11 @@ void gtk4_leave_event(long controller, long event) { long pixbuf = ImageList.createPixbuf(image); long texture = GDK.gdk_texture_new_for_pixbuf(pixbuf); OS.g_object_unref(pixbuf); - GTK4.gtk_picture_set_paintable(imageHandle, texture); + Rectangle bounds = image.getBounds(); + long paintable = OS.swt_scaled_paintable_new(texture, bounds.width, bounds.height); OS.g_object_unref(texture); + GTK4.gtk_picture_set_paintable(imageHandle, paintable); + OS.g_object_unref(paintable); } } } @@ -1376,24 +1382,22 @@ void _setImage (Image image) { long pixbuf = ImageList.createPixbuf(image); long texture = GDK.gdk_texture_new_for_pixbuf(pixbuf); OS.g_object_unref(pixbuf); - GTK4.gtk_picture_set_paintable(imageHandle, texture); - OS.g_object_unref(texture); /* - * Pin the GtkPicture to the image's logical (point) size so it occupies the - * same footprint as the previously used GtkImage. The texture is created - * from a device-scaled pixbuf whose intrinsic size is in pixels; without a - * size request GtkPicture would measure to those pixel dimensions, inflating - * the ToolItem and causing overlaps (e.g. the CTabFolder chevron toolbar). + * Wrap the full-resolution texture in a paintable that reports the image's + * logical (point) size, so the GtkPicture keeps the same footprint as the + * previously used GtkImage and renders crisply at HiDPI. */ Rectangle bounds = image.getBounds(); - GTK.gtk_widget_set_size_request(imageHandle, bounds.width, bounds.height); + long paintable = OS.swt_scaled_paintable_new(texture, bounds.width, bounds.height); + OS.g_object_unref(texture); + GTK4.gtk_picture_set_paintable(imageHandle, paintable); + OS.g_object_unref(paintable); } else { GTK3.gtk_image_set_from_surface(imageHandle, imageList.getSurface(imageIndex)); } } else { if(GTK.GTK4) { GTK4.gtk_picture_set_paintable(imageHandle, 0); - GTK.gtk_widget_set_size_request(imageHandle, -1, -1); gtk_widget_hide(imageHandle); } else { GTK3.gtk_image_set_from_surface(imageHandle, 0);