Skip to content

Commit a203e7d

Browse files
jcupittkleisauke
andauthored
Fix high dpi2 (#52)
* enable highdpi display though mag display and compute needs doing too * magnification display accounts for display scale * scale image zoom setting by display density * enclose drawing ion save/restore * small rename * Update src/imagedisplay.c Co-authored-by: Kleis Auke Wolthuizen <[email protected]> * better clipping for client views clip clients, client should use regular coordinate space --------- Co-authored-by: Kleis Auke Wolthuizen <[email protected]>
1 parent ecc8c84 commit a203e7d

File tree

6 files changed

+112
-39
lines changed

6 files changed

+112
-39
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
master
22

33
- do our own tile snapping if gtk has no snap mechanism
4+
- high-dpi support
45

56
## 4.1.0 26/07/25
67

src/imagedisplay.c

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ struct _Imagedisplay {
7979
double scale;
8080
double x, y;
8181

82+
/* The size of physical display pixels in gtk coordinates, eg. for a 200%
83+
* desktop this would be 0.5.
84+
*/
85+
double pixel_size;
86+
8287
/* Draw the screen in debug mode.
8388
*/
8489
gboolean debug;
@@ -126,6 +131,9 @@ enum {
126131
*/
127132
PROP_DEBUG,
128133

134+
/* Read out display density with this.
135+
*/
136+
PROP_PIXEL_SIZE,
129137
};
130138

131139
enum {
@@ -476,6 +484,8 @@ imagedisplay_set_property(GObject *object,
476484
{
477485
Imagedisplay *imagedisplay = (Imagedisplay *) object;
478486

487+
double d;
488+
479489
#ifdef DEBUG
480490
{
481491
g_autofree char *str = g_strdup_value_contents(value);
@@ -573,6 +583,15 @@ imagedisplay_set_property(GObject *object,
573583
gtk_widget_queue_draw(GTK_WIDGET(imagedisplay));
574584
break;
575585

586+
case PROP_PIXEL_SIZE:
587+
d = g_value_get_double(value);
588+
if (imagedisplay->pixel_size != d) {
589+
imagedisplay->pixel_size = d;
590+
imagedisplay_layout(imagedisplay);
591+
gtk_widget_queue_draw(GTK_WIDGET(imagedisplay));
592+
}
593+
break;
594+
576595
default:
577596
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
578597
break;
@@ -632,6 +651,10 @@ imagedisplay_get_property(GObject *object,
632651
g_value_set_boolean(value, imagedisplay->debug);
633652
break;
634653

654+
case PROP_PIXEL_SIZE:
655+
g_value_set_double(value, imagedisplay->pixel_size);
656+
break;
657+
635658
default:
636659
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
637660
break;
@@ -649,40 +672,49 @@ imagedisplay_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
649672

650673
GTK_WIDGET_CLASS(imagedisplay_parent_class)->snapshot(widget, snapshot);
651674

652-
#ifdef HAVE_GTK_SNAPSHOT_SET_SNAP
653-
gtk_snapshot_set_snap(snapshot, GSK_RECT_SNAP_ROUND);
654-
#endif /*HAVE_GTK_SNAPSHOT_SET_SNAP*/
655-
656675
/* Clip to the widget area, or we may paint over the display control
657676
* bar.
658677
*/
659678
gtk_snapshot_push_clip(snapshot,
660679
&GRAPHENE_RECT_INIT(0, 0,
661680
gtk_widget_get_width(widget), gtk_widget_get_height(widget)));
662681

663-
graphene_rect_t paint;
664-
paint.origin.x = imagedisplay->paint_rect.left;
665-
paint.origin.y = imagedisplay->paint_rect.top;
666-
paint.size.width = imagedisplay->paint_rect.width;
667-
paint.size.height = imagedisplay->paint_rect.height;
682+
gtk_snapshot_save(snapshot);
668683

669-
/* If there's no gtk snapping, we do our own based on the hardware pixel
670-
* size for the surface this snapshot will be rendered to.
684+
/* This can change on each repaint as windows are dragged.
671685
*/
672686
GtkNative *native = gtk_widget_get_native(widget);
673687
GdkSurface *surface = gtk_native_get_surface(native);
674-
double pixel_size = 1.0 / gdk_surface_get_scale_factor(surface);
688+
double pixel_size = 1.0 / gdk_surface_get_scale(surface);
689+
g_object_set(imagedisplay, "pixel-size", pixel_size, NULL);
690+
691+
#ifdef HAVE_GTK_SNAPSHOT_SET_SNAP
692+
gtk_snapshot_set_snap(snapshot, GSK_RECT_SNAP_ROUND);
693+
#endif /*HAVE_GTK_SNAPSHOT_SET_SNAP*/
694+
695+
gtk_snapshot_scale(snapshot, pixel_size, pixel_size);
696+
697+
graphene_rect_t paint;
698+
paint.origin.x = imagedisplay->paint_rect.left / pixel_size;
699+
paint.origin.y = imagedisplay->paint_rect.top / pixel_size;
700+
paint.size.width = imagedisplay->paint_rect.width / pixel_size;
701+
paint.size.height = imagedisplay->paint_rect.height / pixel_size;
675702

676703
if (imagedisplay->tilecache &&
677704
imagedisplay->tilecache->n_levels > 0)
678705
tilecache_snapshot(imagedisplay->tilecache, snapshot,
679-
pixel_size,
680-
imagedisplay->scale, imagedisplay->x, imagedisplay->y,
706+
imagedisplay->scale / pixel_size,
707+
imagedisplay->x / pixel_size,
708+
imagedisplay->y / pixel_size,
681709
&paint, imagedisplay->debug);
682710

683-
// draw any overlays
711+
// undo snap and scale
712+
gtk_snapshot_restore(snapshot);
713+
714+
// draw any overlays back in the regular coordinate space
684715
imagedisplay_overlay_snapshot(imagedisplay, snapshot);
685716

717+
// end of clip
686718
gtk_snapshot_pop(snapshot);
687719
}
688720

@@ -828,6 +860,13 @@ imagedisplay_class_init(ImagedisplayClass *class)
828860
FALSE,
829861
G_PARAM_READWRITE));
830862

863+
g_object_class_install_property(gobject_class, PROP_PIXEL_SIZE,
864+
g_param_spec_double("pixel-size",
865+
_("Pixel size"),
866+
_("Size of hardware display pixels in gtk coordinates"),
867+
0.0, 10.0, 0.0,
868+
G_PARAM_READWRITE));
869+
831870
g_object_class_override_property(gobject_class,
832871
PROP_HADJUSTMENT, "hadjustment");
833872
g_object_class_override_property(gobject_class,

src/imageui.c

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ enum {
125125
PROP_ZOOM,
126126
PROP_X,
127127
PROP_Y,
128+
PROP_PIXEL_SIZE,
128129

129130
PROP_LAST
130131
};
@@ -194,6 +195,10 @@ imageui_property_name(guint prop_id)
194195
return "Y";
195196
break;
196197

198+
case PROP_PIXEL_SIZE:
199+
return "PIXEL_SIZE";
200+
break;
201+
197202
default:
198203
return "<unknown>";
199204
}
@@ -253,6 +258,11 @@ imageui_set_property(GObject *object,
253258
"y", value);
254259
break;
255260

261+
case PROP_PIXEL_SIZE:
262+
g_object_set_property(G_OBJECT(imageui->imagedisplay),
263+
"pixel-size", value);
264+
break;
265+
256266
default:
257267
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
258268
break;
@@ -296,6 +306,11 @@ imageui_get_property(GObject *object,
296306
g_object_get_property(G_OBJECT(imageui->imagedisplay), "y", value);
297307
break;
298308

309+
case PROP_PIXEL_SIZE:
310+
g_object_get_property(G_OBJECT(imageui->imagedisplay),
311+
"pixel-size", value);
312+
break;
313+
299314
default:
300315
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
301316
break;
@@ -429,6 +444,18 @@ imageui_get_zoom(Imageui *imageui)
429444
return zoom;
430445
}
431446

447+
double
448+
imageui_get_pixel_size(Imageui *imageui)
449+
{
450+
double pixel_size;
451+
452+
g_object_get(imageui,
453+
"pixel-size", &pixel_size,
454+
NULL);
455+
456+
return pixel_size;
457+
}
458+
432459
static gboolean
433460
imageui_tick(GtkWidget *widget, GdkFrameClock *frame_clock, gpointer user_data)
434461
{
@@ -551,7 +578,7 @@ imageui_magout(Imageui *imageui)
551578
void
552579
imageui_oneone(Imageui *imageui)
553580
{
554-
imageui_zoom_to_eased(imageui, 1.0);
581+
imageui_zoom_to_eased(imageui, imageui_get_pixel_size(imageui));
555582
}
556583

557584
static void
@@ -785,7 +812,8 @@ imageui_key_pressed(GtkEventControllerKey *self,
785812
if (state & GDK_CONTROL_MASK)
786813
zoom = 1.0 / zoom;
787814

788-
imageui_zoom_to_eased(imageui, zoom);
815+
imageui_zoom_to_eased(imageui,
816+
zoom * imageui_get_pixel_size(imageui));
789817

790818
handled = TRUE;
791819
break;
@@ -1011,6 +1039,13 @@ imageui_class_init(ImageuiClass *class)
10111039
-VIPS_MAX_COORD, VIPS_MAX_COORD, 0,
10121040
G_PARAM_READWRITE));
10131041

1042+
g_object_class_install_property(gobject_class, PROP_PIXEL_SIZE,
1043+
g_param_spec_double("pixel_size",
1044+
_("Pixel size"),
1045+
_("Size of hardware display pixels in gtk coordinates"),
1046+
0.0, 10.0, 0.0,
1047+
G_PARAM_READWRITE));
1048+
10141049
imageui_signals[SIG_CHANGED] = g_signal_new("changed",
10151050
G_TYPE_FROM_CLASS(class),
10161051
G_SIGNAL_RUN_LAST,

src/imagewindow.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,19 +377,29 @@ imagewindow_files_set(Imagewindow *win, char **files, int n_files)
377377
}
378378
}
379379

380+
/* Get the effective zoom, ie. the zoom considering the hardware pixel display
381+
* density.
382+
*/
380383
double
381384
imagewindow_get_zoom(Imagewindow *win)
382385
{
383386
double zoom;
384-
385387
if (win->imageui)
386388
g_object_get(win->imageui,
387389
"zoom", &zoom,
388390
NULL);
389391
else
390392
zoom = 1.0;
391393

392-
return zoom;
394+
double pixel_size;
395+
if (win->imageui)
396+
g_object_get(win->imageui,
397+
"pixel_size", &pixel_size,
398+
NULL);
399+
else
400+
pixel_size = 1.0;
401+
402+
return zoom / pixel_size;
393403
}
394404

395405
void

src/tilecache.c

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -836,19 +836,13 @@ tilecache_draw_bounds(GtkSnapshot *snapshot,
836836
}
837837

838838
#ifndef HAVE_GTK_SNAPSHOT_SET_SNAP
839-
/* Snap a graphene rect to a hardware pixel boundary on the output surface. We
840-
* need to do this if the gtk snap mechachanism is missing or we'll get thin
841-
* white lines on tile edges.
842-
*/
843839
static void
844-
tilecache_snap_rect_to_boundary(graphene_rect_t *bounds, double pixel_size)
840+
tilecache_snap_rect(graphene_rect_t *bounds)
845841
{
846-
double left = rint(bounds->origin.x * pixel_size) / pixel_size;
847-
double top = rint(bounds->origin.y * pixel_size) / pixel_size;
848-
double right =
849-
rint((bounds->origin.x + bounds->size.width) * pixel_size) / pixel_size;
850-
double bottom =
851-
rint((bounds->origin.y + bounds->size.height) * pixel_size) / pixel_size;
842+
double left = rint(bounds->origin.x);
843+
double top = rint(bounds->origin.y);
844+
double right = rint(bounds->origin.x + bounds->size.width);
845+
double bottom = rint(bounds->origin.y + bounds->size.height);
852846

853847
bounds->origin.x = left;
854848
bounds->origin.y = top;
@@ -860,16 +854,12 @@ tilecache_snap_rect_to_boundary(graphene_rect_t *bounds, double pixel_size)
860854
/* Scale is how much the level0 image has been scaled, x/y is the position of
861855
* the top-left corner of @paint in the scaled image.
862856
*
863-
* @pixel_scale is gdk_surface_get_scale() for the surface this snapshot will
864-
* be rendered to.
865-
*
866857
* @paint is the pixel area in gtk coordinates that we paint in the widget.
867858
*
868859
* Set debug to draw tile boundaries for debugging.
869860
*/
870861
void
871862
tilecache_snapshot(Tilecache *tilecache, GtkSnapshot *snapshot,
872-
double pixel_size,
873863
double scale, double x, double y, graphene_rect_t *paint, gboolean debug)
874864
{
875865
/* In debug mode, scale and offset so we can see tile clipping.
@@ -941,7 +931,7 @@ tilecache_snapshot(Tilecache *tilecache, GtkSnapshot *snapshot,
941931
*/
942932
graphene_rect_t backdrop = *paint;
943933
#ifndef HAVE_GTK_SNAPSHOT_SET_SNAP
944-
tilecache_snap_rect_to_boundary(&backdrop, pixel_size);
934+
tilecache_snap_rect(&backdrop);
945935
#endif /*!HAVE_GTK_SNAPSHOT_SET_SNAP*/
946936
gtk_snapshot_push_repeat(snapshot, &backdrop, NULL);
947937

@@ -965,7 +955,7 @@ tilecache_snapshot(Tilecache *tilecache, GtkSnapshot *snapshot,
965955
* blur the image. For zooming out, we want trilinear to get
966956
* mipmaps and antialiasing.
967957
*/
968-
GskScalingFilter filter = scale >= pixel_size ?
958+
GskScalingFilter filter = scale >= 1.0 ?
969959
GSK_SCALING_FILTER_NEAREST : GSK_SCALING_FILTER_TRILINEAR;
970960

971961
graphene_rect_t bounds;
@@ -976,9 +966,8 @@ tilecache_snapshot(Tilecache *tilecache, GtkSnapshot *snapshot,
976966
bounds.size.height = tile->bounds0.height * scale;
977967

978968
#ifndef HAVE_GTK_SNAPSHOT_SET_SNAP
979-
tilecache_snap_rect_to_boundary(&bounds, pixel_size);
969+
tilecache_snap_rect(&bounds);
980970
#endif /*!HAVE_GTK_SNAPSHOT_SET_SNAP*/
981-
982971
gtk_snapshot_append_scaled_texture(snapshot,
983972
tile_get_texture(tile), filter, &bounds);
984973

src/tilecache.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ Tilecache *tilecache_new();
110110
/* Render the tiles to a snapshot.
111111
*/
112112
void tilecache_snapshot(Tilecache *tilecache, GtkSnapshot *snapshot,
113-
double pixel_size,
114113
double scale, double x, double y, graphene_rect_t *paint, gboolean debug);
115114

116115
#endif /*__TILECACHE_H*/

0 commit comments

Comments
 (0)