[gimp/gimp-2-10] app: support cursors with a scale factor of 2 for HiDPI



commit 8ed8def107fccf540d406514cb640266cc7ba490
Author: Michael Natterer <mitch gimp org>
Date:   Mon Dec 31 16:10:46 2018 +0100

    app: support cursors with a scale factor of 2 for HiDPI
    
    If the scale factor is 2 or larger, look for cursor images named
    "filename-x2.png" and use them instead. Guess HiDPI based on the
    monitor resolution being > 250 ppi, ugly but so what.
    
    Manually scale up the default cursor if there is no "x2" image, using
    NEAREST interpolation, which looks better than smooth scaling on HiDPI
    monitors.
    
    Next: adding better HiDPI cursor images.
    
    (cherry picked from commit 8ff8f1d44266142683e754efa9473ab5d049e92c)

 app/widgets/gimpcursor.c | 323 ++++++++++++++++++++++++++++-------------------
 1 file changed, 192 insertions(+), 131 deletions(-)
---
diff --git a/app/widgets/gimpcursor.c b/app/widgets/gimpcursor.c
index ce00bc0eb4..4544c28c5f 100644
--- a/app/widgets/gimpcursor.c
+++ b/app/widgets/gimpcursor.c
@@ -22,21 +22,22 @@
 #include "widgets-types.h"
 
 #include "gimpcursor.h"
+#include "gimpwidgets-utils.h"
 
 #include "cursors/gimp-tool-cursors.c"
 
 
-#define cursor_default_x_hot 10
-#define cursor_default_y_hot 10
+#define cursor_default_hot_x 10
+#define cursor_default_hot_y 10
 
-#define cursor_mouse_x_hot 3
-#define cursor_mouse_y_hot 2
-#define cursor_crosshair_x_hot 15
-#define cursor_crosshair_y_hot 15
-#define cursor_zoom_x_hot 8
-#define cursor_zoom_y_hot 8
-#define cursor_color_picker_x_hot 1
-#define cursor_color_picker_y_hot 30
+#define cursor_mouse_hot_x 3
+#define cursor_mouse_hot_y 2
+#define cursor_crosshair_hot_x 15
+#define cursor_crosshair_hot_y 15
+#define cursor_zoom_hot_x 8
+#define cursor_zoom_hot_y 8
+#define cursor_color_picker_hot_x 1
+#define cursor_color_picker_hot_y 30
 
 
 typedef struct _GimpCursor GimpCursor;
@@ -44,9 +45,11 @@ typedef struct _GimpCursor GimpCursor;
 struct _GimpCursor
 {
   const gchar *resource_name;
-  const gint   x_hot, y_hot;
+  const gint   hot_x;
+  const gint   hot_y;
 
   GdkPixbuf   *pixbuf;
+  GdkPixbuf   *pixbuf_x2;
 };
 
 
@@ -55,100 +58,100 @@ static GimpCursor gimp_cursors[] =
   /* these have to match up with enum GimpCursorType in widgets-enums.h */
 
   {
-    "cursor-none.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-none",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-mouse.png",
-    cursor_mouse_x_hot, cursor_mouse_y_hot
+    "cursor-mouse",
+    cursor_mouse_hot_x, cursor_mouse_hot_y
   },
   {
-    "cursor-crosshair.png",
-    cursor_crosshair_x_hot, cursor_crosshair_y_hot
+    "cursor-crosshair",
+    cursor_crosshair_hot_x, cursor_crosshair_hot_y
   },
   {
-    "cursor-crosshair-small.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-crosshair-small",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-bad.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-bad",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-move.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-move",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-zoom.png",
-    cursor_zoom_x_hot, cursor_zoom_y_hot
+    "cursor-zoom",
+    cursor_zoom_hot_x, cursor_zoom_hot_y
   },
   {
-    "cursor-color-picker.png",
-    cursor_color_picker_x_hot, cursor_color_picker_y_hot
+    "cursor-color-picker",
+    cursor_color_picker_hot_x, cursor_color_picker_hot_y
   },
   {
-    "cursor-corner-top.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-corner-top",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-corner-top-right.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-corner-top-right",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-corner-right.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-corner-right",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-corner-bottom-right.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-corner-bottom-right",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-corner-bottom.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-corner-bottom",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-corner-bottom-left.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-corner-bottom-left",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-corner-left.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-corner-left",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-corner-top-left.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-corner-top-left",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-side-top.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-side-top",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-side-top-right.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-side-top-right",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-side-right.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-side-right",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-side-bottom-right.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-side-bottom-right",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-side-bottom.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-side-bottom",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-side-bottom-left.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-side-bottom-left",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-side-left.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-side-left",
+    cursor_default_hot_x, cursor_default_hot_y
   },
   {
-    "cursor-side-top-left.png",
-    cursor_default_x_hot, cursor_default_y_hot
+    "cursor-side-top-left",
+    cursor_default_hot_x, cursor_default_hot_y
   }
 };
 
@@ -157,43 +160,43 @@ static GimpCursor gimp_tool_cursors[] =
   /* these have to match up with enum GimpToolCursorType in widgets-enums.h */
 
   { NULL },
-  { "tool-rect-select.png" },
-  { "tool-ellipse-select.png" },
-  { "tool-free-select.png" },
-  { "tool-polygon-select.png" },
-  { "tool-fuzzy-select.png" },
-  { "tool-paths.png" },
-  { "tool-paths-anchor.png" },
-  { "tool-paths-control.png" },
-  { "tool-paths-segment.png" },
-  { "tool-iscissors.png" },
-  { "tool-move.png" },
-  { "tool-zoom.png" },
-  { "tool-crop.png" },
-  { "tool-resize.png" },
-  { "tool-rotate.png" },
-  { "tool-shear.png" },
-  { "tool-perspective.png" },
-  { "tool-flip-horizontal.png" },
-  { "tool-flip-vertical.png" },
-  { "tool-text.png" },
-  { "tool-color-picker.png" },
-  { "tool-bucket-fill.png" },
-  { "tool-gradient.png" },
-  { "tool-pencil.png" },
-  { "tool-paintbrush.png" },
-  { "tool-airbrush.png" },
-  { "tool-ink.png" },
-  { "tool-clone.png" },
-  { "tool-heal.png" },
-  { "tool-eraser.png" },
-  { "tool-smudge.png" },
-  { "tool-blur.png" },
-  { "tool-dodge.png" },
-  { "tool-burn.png" },
-  { "tool-measure.png" },
-  { "tool-warp.png" },
-  { "tool-hand.png" }
+  { "tool-rect-select" },
+  { "tool-ellipse-select" },
+  { "tool-free-select" },
+  { "tool-polygon-select" },
+  { "tool-fuzzy-select" },
+  { "tool-paths" },
+  { "tool-paths-anchor" },
+  { "tool-paths-control" },
+  { "tool-paths-segment" },
+  { "tool-iscissors" },
+  { "tool-move" },
+  { "tool-zoom" },
+  { "tool-crop" },
+  { "tool-resize" },
+  { "tool-rotate" },
+  { "tool-shear" },
+  { "tool-perspective" },
+  { "tool-flip-horizontal" },
+  { "tool-flip-vertical" },
+  { "tool-text" },
+  { "tool-color-picker" },
+  { "tool-bucket-fill" },
+  { "tool-gradient" },
+  { "tool-pencil" },
+  { "tool-paintbrush" },
+  { "tool-airbrush" },
+  { "tool-ink" },
+  { "tool-clone" },
+  { "tool-heal" },
+  { "tool-eraser" },
+  { "tool-smudge" },
+  { "tool-blur" },
+  { "tool-dodge" },
+  { "tool-burn" },
+  { "tool-measure" },
+  { "tool-warp" },
+  { "tool-hand" }
 };
 
 static GimpCursor gimp_cursor_modifiers[] =
@@ -201,47 +204,84 @@ static GimpCursor gimp_cursor_modifiers[] =
   /* these have to match up with enum GimpCursorModifier in widgets-enums.h */
 
   { NULL },
-  { "modifier-bad.png" },
-  { "modifier-plus.png" },
-  { "modifier-minus.png" },
-  { "modifier-intersect.png" },
-  { "modifier-move.png" },
-  { "modifier-resize.png" },
-  { "modifier-rotate.png" },
-  { "modifier-zoom.png" },
-  { "modifier-control.png" },
-  { "modifier-anchor.png" },
-  { "modifier-foreground.png" },
-  { "modifier-background.png" },
-  { "modifier-pattern.png" },
-  { "modifier-join.png" },
-  { "modifier-select.png" }
+  { "modifier-bad" },
+  { "modifier-plus" },
+  { "modifier-minus" },
+  { "modifier-intersect" },
+  { "modifier-move" },
+  { "modifier-resize" },
+  { "modifier-rotate" },
+  { "modifier-zoom" },
+  { "modifier-control" },
+  { "modifier-anchor" },
+  { "modifier-foreground" },
+  { "modifier-background" },
+  { "modifier-pattern" },
+  { "modifier-join" },
+  { "modifier-select" }
 };
 
 
 static const GdkPixbuf *
-get_cursor_pixbuf (GimpCursor *cursor)
+get_cursor_pixbuf (GimpCursor *cursor,
+                   gint        scale_factor)
 {
+  gchar  *resource_path;
+  GError *error = NULL;
+
   if (! cursor->pixbuf)
     {
-      gchar  *resource_path;
-      GError *error = NULL;
-
       resource_path = g_strconcat ("/org/gimp/tool-cursors/",
-                                   cursor->resource_name, NULL);
+                                   cursor->resource_name,
+                                   ".png", NULL);
 
       cursor->pixbuf = gdk_pixbuf_new_from_resource (resource_path, &error);
 
       if (! cursor->pixbuf)
         {
-          g_critical ("Failed to create cursor image: %s", error->message);
+          g_critical ("Failed to create cursor image '%s': %s",
+                      resource_path, error->message);
+          g_clear_error (&error);
+        }
+
+      g_free (resource_path);
+    }
+
+  if (scale_factor == 2 && ! cursor->pixbuf_x2)
+    {
+      resource_path = g_strconcat ("/org/gimp/tool-cursors/",
+                                   cursor->resource_name,
+                                   "-x2.png", NULL);
+
+      cursor->pixbuf_x2 = gdk_pixbuf_new_from_resource (resource_path, &error);
+
+      if (! cursor->pixbuf_x2)
+        {
+          /* no critical here until we actually have the cursor files */
+          g_printerr ("Failed to create scaled cursor image '%s' "
+                      "falling back to upscaling default cursor: %s",
+                      resource_path, error->message);
           g_clear_error (&error);
+
+          if (cursor->pixbuf)
+            {
+              gint width  = gdk_pixbuf_get_width  (cursor->pixbuf);
+              gint height = gdk_pixbuf_get_height (cursor->pixbuf);
+
+              cursor->pixbuf_x2 = gdk_pixbuf_scale_simple (cursor->pixbuf,
+                                                           width  * 2,
+                                                           height * 2,
+                                                           GDK_INTERP_NEAREST);
+            }
         }
 
       g_free (resource_path);
     }
 
-  return cursor->pixbuf;
+  if (scale_factor == 2)
+    return cursor->pixbuf_x2;
+  else
+    return cursor->pixbuf;
 }
 
 GdkCursor *
@@ -257,6 +297,9 @@ gimp_cursor_new (GdkWindow          *window,
   GimpCursor *bmtool     = NULL;
   GdkCursor  *cursor;
   GdkPixbuf  *pixbuf;
+  gint        scale_factor;
+  gint        hot_x;
+  gint        hot_y;
 
   g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
   g_return_val_if_fail (cursor_type < GIMP_CURSOR_LAST, NULL);
@@ -365,7 +408,22 @@ gimp_cursor_new (GdkWindow          *window,
       bmmodifier = &gimp_cursor_modifiers[modifier];
     }
 
-  pixbuf = gdk_pixbuf_copy (get_cursor_pixbuf (bmcursor));
+  scale_factor = 1;
+
+  /* guess HiDPI */
+  {
+    GdkScreen *screen = gdk_window_get_screen (window);
+    gdouble    xres, yres;
+
+    gimp_get_monitor_resolution (screen,
+                                 gdk_screen_get_monitor_at_window (screen, window),
+                                 &xres, &yres);
+
+    if ((xres + yres) / 2.0 > 250.0)
+      scale_factor = 2;
+  }
+
+  pixbuf = gdk_pixbuf_copy (get_cursor_pixbuf (bmcursor, scale_factor));
 
   if (bmmodifier || bmtool)
     {
@@ -373,18 +431,23 @@ gimp_cursor_new (GdkWindow          *window,
       gint height = gdk_pixbuf_get_height (pixbuf);
 
       if (bmmodifier)
-        gdk_pixbuf_composite (get_cursor_pixbuf (bmmodifier), pixbuf,
+        gdk_pixbuf_composite (get_cursor_pixbuf (bmmodifier, scale_factor),
+                              pixbuf,
                               0, 0, width, height,
                               0.0, 0.0, 1.0, 1.0,
                               GDK_INTERP_NEAREST, 200);
 
       if (bmtool)
-        gdk_pixbuf_composite (get_cursor_pixbuf (bmtool), pixbuf,
+        gdk_pixbuf_composite (get_cursor_pixbuf (bmtool, scale_factor),
+                              pixbuf,
                               0, 0, width, height,
                               0.0, 0.0, 1.0, 1.0,
                               GDK_INTERP_NEAREST, 200);
     }
 
+  hot_x = bmcursor->hot_x;
+  hot_y = bmcursor->hot_y;
+
   /*  flip the cursor if mouse setting is left-handed  */
 
   if (cursor_handedness == GIMP_HANDEDNESS_LEFT)
@@ -392,18 +455,16 @@ gimp_cursor_new (GdkWindow          *window,
       GdkPixbuf *flipped = gdk_pixbuf_flip (pixbuf, TRUE);
       gint       width   = gdk_pixbuf_get_width (flipped);
 
-      cursor = gdk_cursor_new_from_pixbuf (display, flipped,
-                                           (width - 1) - bmcursor->x_hot,
-                                           bmcursor->y_hot);
-      g_object_unref (flipped);
-    }
-  else
-    {
-      cursor = gdk_cursor_new_from_pixbuf (display, pixbuf,
-                                           bmcursor->x_hot,
-                                           bmcursor->y_hot);
+      g_object_unref (pixbuf);
+      pixbuf = flipped;
+
+      hot_x = (width - 1) - hot_x;
     }
 
+  cursor = gdk_cursor_new_from_pixbuf (display, pixbuf,
+                                       hot_x * scale_factor,
+                                       hot_y * scale_factor);
+
   g_object_unref (pixbuf);
 
   return cursor;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]