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



commit 8ff8f1d44266142683e754efa9473ab5d049e92c
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 via
    gdk_cairo_surface_create_from_pixbuf() and
    gdk_cursor_new_from_surface().
    
    Manually scale up the default cursor if there is no "x2" image, using
    NEAREST interpolation, which looks better than the default smooth
    scaling done by gdk_cursor_new_from_pixbuf() on HiDPI monitors.
    
    Next: adding better HiDPI cursor images.

 app/widgets/gimpcursor.c | 315 ++++++++++++++++++++++++++++-------------------
 1 file changed, 187 insertions(+), 128 deletions(-)
---
diff --git a/app/widgets/gimpcursor.c b/app/widgets/gimpcursor.c
index 57515329e6..525f1e526c 100644
--- a/app/widgets/gimpcursor.c
+++ b/app/widgets/gimpcursor.c
@@ -24,17 +24,17 @@
 #include "gimpcursor.h"
 
 
-#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;
@@ -42,9 +42,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;
 };
 
 
@@ -53,100 +55,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
   }
 };
 
@@ -155,43 +157,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[] =
@@ -199,47 +201,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 *
@@ -255,6 +294,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);
@@ -363,7 +405,12 @@ gimp_cursor_new (GdkWindow          *window,
       bmmodifier = &gimp_cursor_modifiers[modifier];
     }
 
-  pixbuf = gdk_pixbuf_copy (get_cursor_pixbuf (bmcursor));
+  scale_factor = gdk_window_get_scale_factor (window);
+
+  /* we only support x2 scaling right now */
+  scale_factor = CLAMP (scale_factor, 1, 2);
+
+  pixbuf = gdk_pixbuf_copy (get_cursor_pixbuf (bmcursor, scale_factor));
 
   if (bmmodifier || bmtool)
     {
@@ -371,18 +418,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)
@@ -390,16 +442,23 @@ 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);
+      g_object_unref (pixbuf);
+      pixbuf = flipped;
+
+      hot_x = (width - 1) - hot_x;
+    }
+
+  if (scale_factor > 1)
+    {
+      cairo_surface_t *surface =
+        gdk_cairo_surface_create_from_pixbuf (pixbuf, scale_factor, NULL);
+
+      cursor = gdk_cursor_new_from_surface (display, surface, hot_x, hot_y);
+      cairo_surface_destroy (surface);
     }
   else
     {
-      cursor = gdk_cursor_new_from_pixbuf (display, pixbuf,
-                                           bmcursor->x_hot,
-                                           bmcursor->y_hot);
+      cursor = gdk_cursor_new_from_pixbuf (display, pixbuf, hot_x, hot_y);
     }
 
   g_object_unref (pixbuf);


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