From 6c6577dd6bc598d229abd5e9957581a1dc70200c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 8 Feb 2024 11:42:38 -0500 Subject: [PATCH] Add a devicepixels test This is a very simple test that tries out rendering textures 1:1 to device pixels. --- tests/devicepixels.c | 261 +++++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 2 files changed, 262 insertions(+) create mode 100644 tests/devicepixels.c diff --git a/tests/devicepixels.c b/tests/devicepixels.c new file mode 100644 index 0000000000..6add906f29 --- /dev/null +++ b/tests/devicepixels.c @@ -0,0 +1,261 @@ +#include + +#define DEMO_TYPE_IMAGE (demo_image_get_type ()) + +G_DECLARE_FINAL_TYPE (DemoImage, demo_image, DEMO, IMAGE, GtkWidget) + +struct _DemoImage { + GtkWidget parent_instance; + + GdkTexture *texture; +}; + +G_DEFINE_TYPE (DemoImage, demo_image, GTK_TYPE_WIDGET) + +static void +demo_image_init (DemoImage *demo) +{ +} + +static void +demo_image_dispose (GObject *object) +{ + DemoImage *demo = DEMO_IMAGE (object); + + g_clear_object (&demo->texture); + + G_OBJECT_CLASS (demo_image_parent_class)->dispose (object); +} + +static void +demo_image_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + DemoImage *demo = DEMO_IMAGE (widget); + double scale; + + g_print ("measure\n"); + scale = gdk_surface_get_scale (gtk_native_get_surface (gtk_widget_get_native (widget))); + + if (orientation == GTK_ORIENTATION_VERTICAL) + { + *minimum = *natural = (int) ceil (gdk_texture_get_height (demo->texture) / scale); + g_print ("requesting height: %d\n", *minimum); + } + else + { + *minimum = *natural = (int) ceil (gdk_texture_get_width (demo->texture) / scale); + g_print ("requesting width: %d\n", *minimum); + } +} + +static void +demo_image_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + DemoImage *demo = DEMO_IMAGE (widget); + GtkNative *native = gtk_widget_get_native (widget); + graphene_point_t point; + double scale; + double ox, oy; + double x, y, width, height; + + g_print ("snapshot\n"); + scale = gdk_surface_get_scale (gtk_native_get_surface (native)); + + g_print ("scale %f\n", scale); + + gtk_native_get_surface_transform (native, &ox, &oy); + + g_print ("surface transform %f %f\n", ox, oy); + + if (!gtk_widget_compute_point (widget, GTK_WIDGET (native), &GRAPHENE_POINT_INIT (0, 0), &point)) + return; + + x = point.x; + y = point.y; + + g_print ("window (app) coordinates: %f %f\n", x, y); + + x += ox; + y += oy; + + g_print ("surface (app) coordinates: %f %f\n", x, y); + + x *= scale; + y *= scale; + + g_print ("surface (device) coordinates: %f %f\n", x, y); + + /* Now x, y are the surface (device) coordinates of the widget's origin */ + + /* Round up to the next full device pixel */ + + x = ceil (x); + y = ceil (y); + + g_print ("rounded up: %f %f\n", x, y); + + /* And back to widget coordinates */ + + x /= scale; + y /= scale; + + x -= ox; + y -= oy; + + x -= point.x; + y -= point.y; + + /* width and height that give us 1-1 mapping to device pixels */ + + width = gdk_texture_get_width (demo->texture) / scale; + height = gdk_texture_get_height (demo->texture) / scale; + + g_print ("bounds: %f %f %f %f\n", x, y, width, height); + + gtk_snapshot_append_texture (snapshot, demo->texture, + &GRAPHENE_RECT_INIT (x, y, width, height)); +} + +static void +notify_scale (GObject *object, + GParamSpec *pspec, + GtkWidget *widget) +{ + g_print ("scale change!\n"); + + gtk_widget_queue_resize (widget); +} + +static void +demo_image_realize (GtkWidget *widget) +{ + GtkNative *native; + GdkSurface *surface; + + GTK_WIDGET_CLASS (demo_image_parent_class)->realize (widget); + + g_print ("realize\n"); + + native = gtk_widget_get_native (widget); + surface = gtk_native_get_surface (native); + g_signal_connect (surface, "notify::scale", + G_CALLBACK (notify_scale), widget); +} + +static void +demo_image_unrealize (GtkWidget *widget) +{ + GtkNative *native; + GdkSurface *surface; + + native = gtk_widget_get_native (widget); + surface = gtk_native_get_surface (native); + g_signal_handlers_disconnect_by_func (surface, notify_scale, widget); + + GTK_WIDGET_CLASS (demo_image_parent_class)->unrealize (widget); +} + +static void +demo_image_class_init (DemoImageClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + object_class->dispose = demo_image_dispose; + + widget_class->realize = demo_image_realize; + widget_class->unrealize = demo_image_unrealize; + + widget_class->measure = demo_image_measure; + widget_class->snapshot = demo_image_snapshot; + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); +} + +static GtkWidget * +demo_image_new (GdkTexture *texture) +{ + DemoImage *demo; + + demo = g_object_new (DEMO_TYPE_IMAGE, NULL); + + demo->texture = g_object_ref (texture); + + g_print ("texture size %dx%d\n", + gdk_texture_get_width (texture), + gdk_texture_get_height (texture)); + + return GTK_WIDGET (demo); +} + +static GdkTexture * +make_checkerboard_texture (int width, int height) +{ + guint32 *data, *row; + GBytes *bytes; + GdkTexture *texture; + + data = (guint32 *) g_new (guint32, width * height); + + for (int y = 0; y < height; y++) + { + row = data + y * width; + for (int x = 0; x < width; x++) + { + if ((x + y) % 2) + row[x] = 0xffffffff; + else + row[x] = 0xff000000; + } + } + + bytes = g_bytes_new_take (data, height * width * 4); + + texture = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, width * 4); + + g_bytes_unref (bytes); + + return texture; +} + +int +main (int argc, char *argv[]) +{ + GtkWidget *window; + GdkTexture *texture; + GError *error = NULL; + + gtk_init (); + + if (argc > 1) + { + texture = gdk_texture_new_from_filename (argv[1], &error); + if (!texture) + g_error ("%s", error->message); + } + else + { + texture = make_checkerboard_texture (100, 100); + } + + window = gtk_window_new (); + gtk_window_set_default_size (GTK_WINDOW (window), 797, 601); + + gtk_window_set_child (GTK_WINDOW (window), demo_image_new (texture)); + + g_object_unref (texture); + + gtk_window_present (GTK_WINDOW (window)); + + while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0) + g_main_context_iteration (NULL, TRUE); + + return 0; +} diff --git a/tests/meson.build b/tests/meson.build index 5d717cafd3..253640c461 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,5 +1,6 @@ gtk_tests = [ # testname, optional extra sources + ['devicepixels'], ['testsections'], ['testfilelauncher'], ['input'],