GNOME Bugzilla – Bug 765167
GtkCellRendererPixbuf draws pixbufs wrong size and blurry when window scale != 1 (Retina/HiDPI)
Last modified: 2018-05-02 17:02:40 UTC
When a GtkCellRendererPixbuf is set up to draw a GdkPixbuf and gdk_window_get_scale_factor() does not return 1, the GdkPixbuf is not drawn at the correct size and appears blurry. For example, for Retina/HiDPI displays, the window scale will likely be set to 2, resulting in a 32x32 pixbuf being drawn at 64x64 and blurry. The example found here: https://python-gtk-3-tutorial.readthedocs.org/en/latest/iconview.html provides a test case. Run that, open the GTK inspector, select the Visual tab, and change the value of the window scaling. Going from 1 to 2 doubles the size of the icons and renders them blurry, going the opposite direction halves the size and renders them crisp. I would expect a pixbuf to always be rendered at its actual size, regardless of the window scale, and never rendered blurry. This doesn't seem to be a problem when using a GtkCellRendererPixbuf with icon names. GTK+ 3.20.3
NB: Setting the width/height of the cell renderer does not fix the problem - if set to the actual size of the pixbuf it simply crops it.
This is intentional. Applications not taking hidpi into account will appear as before but blurry. You need to: * load the pixbuf at pixel_size * scale_factor: (Gtk.Widget.get_scale_factor()) * Convert it to a cairo surface: Gdk.cairo_surface_create_from_pixbuf() * Use Gtk.IconView.set_cell_data_func and set cell render "surface" property to the surface in the passed callback.
(In reply to Christoph Reiter (lazka) from comment #2) > This is intentional. Applications not taking hidpi into account will appear > as before but blurry. Oh yes, I take your point. However, while these parts are trivial: > * load the pixbuf at pixel_size * scale_factor: > (Gtk.Widget.get_scale_factor()) > * Convert it to a cairo surface: Gdk.cairo_surface_create_from_pixbuf() This is impossible to implement in a convent way: > * Use Gtk.IconView.set_cell_data_func and set cell render "surface" property > to the surface in the passed callback. Since cairo_surface_t is not a GObject and so cannot be added to a list store. If the above is the correct way to display pixbufs correctly on high dpi devices in GtkIconView, then the existing GtkIconView/GtkCellRendererPixbuf pixbuf infrastructure (e.g. the convenience method gtk_icon_view_set_pixbuf_column and the GtkCellRendererPixbuf "pixbuf" property) should be deprecated since it is unable to work correctly with devices where window scale != 1, which will tend to be the case in the future. A better approach would be to allow setting a scale value for GtkIconView (perhaps in a similar way as for Gtk.Image), default it appropriately to the value obtained from its GDK window, and use the existing infrastructure to obtain a pixbuf from the model and render it scaled accordingly. This way if the author if the author supplies an appropriate sized pixbuf for the current window scale, the view draws it correctly.
(In reply to Michael Gratton from comment #3) > (In reply to Christoph Reiter (lazka) from comment #2) > > This is intentional. Applications not taking hidpi into account will appear > > as before but blurry. > > Oh yes, I take your point. However, while these parts are trivial: > > > * load the pixbuf at pixel_size * scale_factor: > > (Gtk.Widget.get_scale_factor()) > > * Convert it to a cairo surface: Gdk.cairo_surface_create_from_pixbuf() > > This is impossible to implement in a convent way: > > > * Use Gtk.IconView.set_cell_data_func and set cell render "surface" property > > to the surface in the passed callback. Not impossible but you need to ignore the IconView convenience functions for pixbufs. > Since cairo_surface_t is not a GObject and so cannot be added to a list > store. > > If the above is the correct way to display pixbufs correctly on high dpi > devices in GtkIconView, then the existing GtkIconView/GtkCellRendererPixbuf > pixbuf infrastructure (e.g. the convenience method > gtk_icon_view_set_pixbuf_column and the GtkCellRendererPixbuf "pixbuf" > property) should be deprecated since it is unable to work correctly with > devices where window scale != 1, which will tend to be the case in the > future. Agreed. > A better approach would be to allow setting a scale value for GtkIconView > (perhaps in a similar way as for Gtk.Image), default it appropriately to the > value obtained from its GDK window, and use the existing infrastructure to > obtain a pixbuf from the model and render it scaled accordingly. This way if > the author if the author supplies an appropriate sized pixbuf for the > current window scale, the view draws it correctly. Sounds good as well.. but I'm not the one to decide.
Created attachment 327108 [details] hipdi example I've adapted the example you linked to support scaling. I hope that helps.
Thanks - yes it's not impossible once you've been pointed at the solution, but for a widget that is designed to show icons it's just surprising how many undocumented hoops must be jumped through to get it to show icons correctly on modern hardware. "You had one job!" and so on. :)
This is a workaround in Gtkmm, if anyone's interested. I think it's a hack though, and this issue should be fixed upstream. class MainWindowIconView : public Gtk::IconView { public: Gtk::CellRendererPixbuf cell_renderer_pixbuf; ///< Cell renderer for icons. Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > col_pixbuf; ///< Model column // ... MainWindowIconView() { // ... columns.add(col_pixbuf); // For high quality rendering with GDK_SCALE=2 this->pack_start(cell_renderer_pixbuf, false); this->set_cell_data_func(cell_renderer_pixbuf, sigc::mem_fun(this, &MainWindowIconView::on_cell_data_render)); // ... } /// Cell data renderer (needed for high quality icons in GDK_SCALE=2). /// We have to use Cairo surfaces, because pixbufs are scaled by GtkIconView. void on_cell_data_render(const Gtk::TreeModel::const_iterator& iter) { Gtk::TreeRow row = *iter; Glib::RefPtr<Gdk::Pixbuf> pixbuf = row[col_pixbuf]; // Gtkmm property_surface() doesn't work, so use plain C. cairo_surface_t* surface = gdk_cairo_surface_create_from_pixbuf( pixbuf->gobj(), get_scale_factor(), get_window()->gobj()); g_object_set(G_OBJECT(cell_renderer_pixbuf.gobj()), "surface", surface, NULL); cairo_surface_destroy(surface); } // ... };
-- GitLab Migration Automatic Message -- This bug has been migrated to GNOME's GitLab instance and has been closed from further activity. You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/gtk/issues/613.