Bug 1433068 - Titlebar/Linux - load and draw HiDPI titlebar icons on HiDPI screens, r?jhorak draft
authorMartin Stransky <stransky@redhat.com>
Tue, 20 Feb 2018 16:16:23 +0100
changeset 757273 6ea30354120aaa68bfd6aa6194dcdb9c3195e17b
parent 757272 8afa18d1a32d014eee623545a474b1e1bc9171af
push id99733
push userstransky@redhat.com
push dateTue, 20 Feb 2018 15:19:25 +0000
reviewersjhorak
bugs1433068
milestone60.0a1
Bug 1433068 - Titlebar/Linux - load and draw HiDPI titlebar icons on HiDPI screens, r?jhorak MozReview-Commit-ID: KxbBvf6mgo1
widget/gtk/WidgetStyleCache.cpp
widget/gtk/WidgetStyleCache.h
widget/gtk/gtk3drawing.cpp
widget/gtk/gtkdrawing.h
widget/gtk/nsNativeThemeGTK.cpp
--- a/widget/gtk/WidgetStyleCache.cpp
+++ b/widget/gtk/WidgetStyleCache.cpp
@@ -563,60 +563,79 @@ CreateHeaderBar(WidgetNodeType aWidgetTy
   // at default Adwaita theme).
   // We need to fix titlebar size calculation to also include
   // titlebar button sizes. (Bug 1419442)
   gtk_style_context_add_class(style, "default-decoration");
 
   return headerbar;
 }
 
+#define ICON_SCALE_VARIANTS 2
+
 static void
 LoadWidgetIconPixbuf(GtkWidget* aWidgetIcon)
 {
   GtkStyleContext* style = gtk_widget_get_style_context(aWidgetIcon);
 
   const gchar *iconName;
   GtkIconSize gtkIconSize;
   gtk_image_get_icon_name(GTK_IMAGE(aWidgetIcon), &iconName, &gtkIconSize);
 
   gint iconWidth, iconHeight;
   gtk_icon_size_lookup(gtkIconSize, &iconWidth, &iconHeight);
 
-  /* This is available since Gtk+ 3.10 as well as GtkHeaderBar */
+  /* Those are available since Gtk+ 3.10 as well as GtkHeaderBar */
   static auto sGtkIconThemeLookupIconForScalePtr =
     (GtkIconInfo* (*)(GtkIconTheme *, const gchar *, gint, gint, GtkIconLookupFlags))
     dlsym(RTLD_DEFAULT, "gtk_icon_theme_lookup_icon_for_scale");
+  static auto sGdkCairoSurfaceCreateFromPixbufPtr =
+    (cairo_surface_t * (*)(const GdkPixbuf *, int, GdkWindow *))
+    dlsym(RTLD_DEFAULT, "gdk_cairo_surface_create_from_pixbuf");
 
-  GtkIconInfo *gtkIconInfo =
-    sGtkIconThemeLookupIconForScalePtr(gtk_icon_theme_get_default(),
-                                       iconName,
-                                       iconWidth,
-                                       1, /* TODO: scale for HiDPI */
-                                       (GtkIconLookupFlags)0);
+  for (int scale = 1; scale < ICON_SCALE_VARIANTS+1; scale++) {
+    GtkIconInfo *gtkIconInfo =
+      sGtkIconThemeLookupIconForScalePtr(gtk_icon_theme_get_default(),
+                                         iconName,
+                                         iconWidth,
+                                         scale,
+                                         (GtkIconLookupFlags)0);
+
+    if (!gtkIconInfo) {
+      // We miss the icon, nothing to do here.
+      return;
+    }
 
-  if (!gtkIconInfo) {
-    // We miss the icon, nothing to do here.
-    return;
-  }
+    gboolean unused;
+    GdkPixbuf *iconPixbuf =
+      gtk_icon_info_load_symbolic_for_context(gtkIconInfo, style,
+                                              &unused, nullptr);
+    g_object_unref(G_OBJECT(gtkIconInfo));
 
-  gboolean unused;
-  GdkPixbuf *iconPixbuf =
-    gtk_icon_info_load_symbolic_for_context(gtkIconInfo, style,
-                                            &unused, nullptr);
-  g_object_unref(G_OBJECT(gtkIconInfo));
+    cairo_surface_t* iconSurface =
+      sGdkCairoSurfaceCreateFromPixbufPtr(iconPixbuf, scale, nullptr);
+    g_object_unref(iconPixbuf);
 
-  g_object_set_data_full(G_OBJECT(aWidgetIcon), "MozillaIconPixbuf", iconPixbuf,
-                        (GDestroyNotify)g_object_unref);
+    nsAutoCString surfaceName;
+    surfaceName = nsPrintfCString("MozillaIconSurface%d", scale);
+    g_object_set_data_full(G_OBJECT(aWidgetIcon), surfaceName.get(),
+                          iconSurface,
+                          (GDestroyNotify)cairo_surface_destroy);
+  }
 }
 
-GdkPixbuf*
-GetWidgetIconPixbuf(GtkWidget* aWidgetIcon)
+cairo_surface_t*
+GetWidgetIconSurface(GtkWidget* aWidgetIcon, int aScale)
 {
-  return (GdkPixbuf*)g_object_get_data(G_OBJECT(aWidgetIcon),
-                                       "MozillaIconPixbuf");
+  if (aScale > ICON_SCALE_VARIANTS)
+    aScale = ICON_SCALE_VARIANTS;
+
+  nsAutoCString surfaceName;
+  surfaceName = nsPrintfCString("MozillaIconSurface%d", aScale);
+  return (cairo_surface_t*)
+    g_object_get_data(G_OBJECT(aWidgetIcon), surfaceName.get());
 }
 
 // TODO - Also return style for buttons located at Maximized toolbar.
 static GtkWidget*
 CreateHeaderBarButton(WidgetNodeType aWidgetType)
 {
   MOZ_ASSERT(gtk_check_version(3, 10, 0) == nullptr,
              "GtkHeaderBar is only available on GTK 3.10+.");
--- a/widget/gtk/WidgetStyleCache.h
+++ b/widget/gtk/WidgetStyleCache.h
@@ -16,18 +16,18 @@ typedef unsigned StyleFlags;
 enum : StyleFlags {
   NO_STYLE_FLAGS,
   WHATEVER_MIGHT_BE_NEEDED = 1U << 0,
 };
 
 GtkWidget*
 GetWidget(WidgetNodeType aNodeType);
 
-GdkPixbuf*
-GetWidgetIconPixbuf(GtkWidget* aWidgetIcon);
+cairo_surface_t*
+GetWidgetIconSurface(GtkWidget* aWidgetIcon, int aScale);
 
 /*
  * Return a new style context based on aWidget, as a child of aParentStyle.
  * If aWidget still has a floating reference, then it is sunk and released.
  */
 GtkStyleContext*
 CreateStyleForWidget(GtkWidget* aWidget, GtkStyleContext* aParentStyle);
 
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -13,16 +13,17 @@
 #include <string.h>
 #include "gtkdrawing.h"
 #include "mozilla/Assertions.h"
 #include "prinrval.h"
 #include "WidgetStyleCache.h"
 #include "nsDebug.h"
 
 #include <math.h>
+#include <dlfcn.h>
 
 static gboolean checkbox_check_state;
 static gboolean notebook_has_tab_gap;
 
 static ScrollbarGTKMetrics sScrollbarMetrics[2];
 static ToggleGTKMetrics sCheckboxMetrics;
 static ToggleGTKMetrics sRadioMetrics;
 static ToolbarGTKMetrics sToolbarMetrics;
@@ -443,30 +444,35 @@ moz_gtk_header_bar_button_paint(cairo_t 
                                 WidgetNodeType aWidgetType,
                                 GtkTextDirection direction)
 {
     GtkWidget *widget = GetWidget(aWidgetType);
     InsetByMargin(rect, gtk_widget_get_style_context(widget));
     moz_gtk_button_paint(cr, rect, state, relief, widget, direction);
 
     GtkWidget* iconWidget = gtk_bin_get_child(GTK_BIN(widget));
-    GdkPixbuf* pixbuf = GetWidgetIconPixbuf(iconWidget);
-
-    if (pixbuf) {
+    cairo_surface_t *surface = GetWidgetIconSurface(iconWidget, state->scale);
+
+    if (surface) {
         GtkStyleContext* style = gtk_widget_get_style_context(iconWidget);
         GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
 
         gtk_style_context_save(style);
         gtk_style_context_set_state(style, state_flags);
 
         const ToolbarButtonGTKMetrics *metrics =
             GetToolbarButtonMetrics(aWidgetType);
 
-        gtk_render_icon(style, cr, pixbuf,
-                        metrics->iconXPosition, metrics->iconYPosition);
+        /* This is available since Gtk+ 3.10 as well as GtkHeaderBar */
+        static auto sGtkRenderIconSurfacePtr =
+          (void (*)(GtkStyleContext *, cairo_t *, cairo_surface_t *, gdouble, gdouble))
+        dlsym(RTLD_DEFAULT, "gtk_render_icon_surface");
+
+        sGtkRenderIconSurfacePtr(style, cr, surface,
+                                 metrics->iconXPosition, metrics->iconYPosition);
         gtk_style_context_restore(style);
     }
 
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect,
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -27,16 +27,17 @@ typedef struct {
   guint8 disabled;
   guint8 isDefault;
   guint8 canDefault;
   /* The depressed state is for buttons which remain active for a longer period:
    * activated toggle buttons or buttons showing a popup menu. */
   guint8 depressed;
   gint32 curpos; /* curpos and maxpos are used for scrollbars */
   gint32 maxpos;
+  gint32 scale;  /* actual widget scale */
 } GtkWidgetState;
 
 /**
  * A size in the same GTK pixel units as GtkBorder and GdkRectangle.
  */
 struct MozGtkSize {
   gint width;
   gint height;
--- a/widget/gtk/nsNativeThemeGTK.cpp
+++ b/widget/gtk/nsNativeThemeGTK.cpp
@@ -1121,16 +1121,20 @@ nsNativeThemeGTK::DrawWidgetBackground(g
   Transparency transparency = GetWidgetTransparency(aFrame, aWidgetType);
 
   // gdk rectangles are wrt the drawing rect.
   GdkRectangle gdk_rect = {-drawingRect.x/scaleFactor,
                            -drawingRect.y/scaleFactor,
                            widgetRect.width/scaleFactor,
                            widgetRect.height/scaleFactor};
 
+  // Save actual widget scale to GtkWidgetState as we don't provide
+  // nsFrame to gtk3drawing routines.
+  state.scale = scaleFactor;
+
   // translate everything so (0,0) is the top left of the drawingRect
   gfxPoint origin = rect.TopLeft() + drawingRect.TopLeft();
 
   DrawThemeWithCairo(ctx, aContext->GetDrawTarget(),
                      state, gtkWidgetType, flags, direction, scaleFactor,
                      snapped, ToPoint(origin), drawingRect.Size(),
                      gdk_rect, transparency);