[gtk+] quartz dnd: add hidpi support for gtk_drag_set_icon_surface()



commit 4b23ba53c51a087851788c67f852281b085c9c33
Author: Christoph Reiter <reiter christoph gmail com>
Date:   Fri Jul 24 15:09:48 2015 +0200

    quartz dnd: add hidpi support for gtk_drag_set_icon_surface()
    
    Instead of storing the dnd icon as a pixbuf store it
    as a cairo surface and transfer the device scale
    when converting it to a NSImage. In the pixbuf/stock/named
    setters convert to a surface instead (no hidpi support yet)

 gtk/gtkdnd-quartz.c |   86 +++++++++++++++----------------------------
 gtk/gtkquartz.c     |  100 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkquartz.h     |    2 +
 3 files changed, 132 insertions(+), 56 deletions(-)
---
diff --git a/gtk/gtkdnd-quartz.c b/gtk/gtkdnd-quartz.c
index 97841d0..f7f5182 100644
--- a/gtk/gtkdnd-quartz.c
+++ b/gtk/gtkdnd-quartz.c
@@ -96,7 +96,7 @@ struct _GtkDragSourceInfo
   GdkDragContext    *context;    /* drag context */
   NSEvent           *nsevent;     /* what started it */
   gint hot_x, hot_y;             /* Hot spot for drag */
-  GdkPixbuf         *icon_pixbuf;
+  cairo_surface_t   *icon_surface;
   gboolean           success;
   gboolean           delete;
 };
@@ -1162,7 +1162,8 @@ gtk_drag_begin_idle (gpointer arg)
   /* FIXME: If the event isn't a mouse event, use the global cursor position instead */
   point = [info->nsevent locationInWindow];
 
-  drag_image = _gtk_quartz_create_image_from_pixbuf (info->icon_pixbuf);
+  drag_image = _gtk_quartz_create_image_from_surface (info->icon_surface);
+
   if (drag_image == NULL)
     {
       g_object_unref (info->context);
@@ -1290,7 +1291,7 @@ gtk_drag_begin_internal (GtkWidget         *widget,
    * application may have set one in ::drag_begin, or it may
    * not have set one.
    */
-  if (!info->icon_pixbuf)
+  if (!info->icon_surface)
     {
       if (!site || site->icon_type == GTK_IMAGE_EMPTY)
         gtk_drag_set_icon_default (context);
@@ -1804,6 +1805,8 @@ set_icon_stock_pixbuf (GdkDragContext    *context,
                       gint               hot_y)
 {
   GtkDragSourceInfo *info;
+  cairo_surface_t *surface;
+  cairo_t *cr;
 
   info = gtk_drag_get_source_info (context, FALSE);
 
@@ -1821,11 +1824,19 @@ set_icon_stock_pixbuf (GdkDragContext    *context,
   else
     g_object_ref (pixbuf);
 
-  if (info->icon_pixbuf)
-    g_object_unref (info->icon_pixbuf);
-  info->icon_pixbuf = pixbuf;
-  info->hot_x = hot_x;
-  info->hot_y = hot_y;
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        gdk_pixbuf_get_width (pixbuf),
+                                        gdk_pixbuf_get_height (pixbuf));
+
+  cr = cairo_create (surface);
+  gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+  g_object_unref (pixbuf);
+
+  cairo_surface_set_device_offset (surface, -hot_x, -hot_y);
+  gtk_drag_set_icon_surface (context, surface);
+  cairo_surface_destroy (surface);
 }
 
 /**
@@ -1873,44 +1884,6 @@ gtk_drag_set_icon_stock  (GdkDragContext *context,
   set_icon_stock_pixbuf (context, stock_id, NULL, hot_x, hot_y);
 }
 
-
-/* XXX: This function is in gdk, too. Should it be in Cairo? */
-static gboolean
-_gtk_cairo_surface_extents (cairo_surface_t *surface,
-                            GdkRectangle *extents)
-{
-  double x1, x2, y1, y2;
-  cairo_t *cr;
-
-  g_return_val_if_fail (surface != NULL, FALSE);
-  g_return_val_if_fail (extents != NULL, FALSE);
-
-  cr = cairo_create (surface);
-  cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
-
-  x1 = floor (x1);
-  y1 = floor (y1);
-  x2 = ceil (x2);
-  y2 = ceil (y2);
-  x2 -= x1;
-  y2 -= y1;
-  
-  if (x1 < G_MININT || x1 > G_MAXINT ||
-      y1 < G_MININT || y1 > G_MAXINT ||
-      x2 > G_MAXINT || y2 > G_MAXINT)
-    {
-      extents->x = extents->y = extents->width = extents->height = 0;
-      return FALSE;
-    }
-
-  extents->x = x1;
-  extents->y = y1;
-  extents->width = x2;
-  extents->height = y2;
-
-  return TRUE;
-}
-
 /**
  * gtk_drag_set_icon_surface:
  * @context: the context for a drag. (This must be called
@@ -1930,21 +1903,22 @@ void
 gtk_drag_set_icon_surface (GdkDragContext  *context,
                            cairo_surface_t *surface)
 {
-  GdkPixbuf *pixbuf;
-  GdkRectangle extents;
   double x_offset, y_offset;
+  GtkDragSourceInfo *info;
 
   g_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
   g_return_if_fail (surface != NULL);
 
-  _gtk_cairo_surface_extents (surface, &extents);
   cairo_surface_get_device_offset (surface, &x_offset, &y_offset);
+  info = gtk_drag_get_source_info (context, FALSE);
+  cairo_surface_reference (surface);
 
-  pixbuf = gdk_pixbuf_get_from_surface (surface,
-                                        extents.x, extents.y,
-                                        extents.width, extents.height);
-  gtk_drag_set_icon_pixbuf (context, pixbuf, -x_offset, -y_offset);
-  g_object_unref (pixbuf);
+  if (info->icon_surface)
+    cairo_surface_destroy (info->icon_surface);
+
+  info->icon_surface = surface;
+  info->hot_x = -x_offset;
+  info->hot_y = -y_offset;
 }
 
 /**
@@ -2021,8 +1995,8 @@ gtk_drag_source_info_destroy (GtkDragSourceInfo *info)
   NSPasteboard *pasteboard;
   NSAutoreleasePool *pool;
 
-  if (info->icon_pixbuf)
-    g_object_unref (info->icon_pixbuf);
+  if (info->icon_surface)
+    cairo_surface_destroy (info->icon_surface);
 
   g_signal_emit_by_name (info->widget, "drag-end", 
                         info->context);
diff --git a/gtk/gtkquartz.c b/gtk/gtkquartz.c
index acd1f18..253e8a9 100644
--- a/gtk/gtkquartz.c
+++ b/gtk/gtkquartz.c
@@ -22,6 +22,106 @@
 #include "gtkselectionprivate.h"
 #include <gdk/quartz/gdkquartz.h>
 
+
+static gboolean
+_cairo_surface_extents (cairo_surface_t *surface,
+                            GdkRectangle *extents)
+{
+  double x1, x2, y1, y2;
+  cairo_t *cr;
+
+  g_return_val_if_fail (surface != NULL, FALSE);
+  g_return_val_if_fail (extents != NULL, FALSE);
+
+  cr = cairo_create (surface);
+  cairo_clip_extents (cr, &x1, &y1, &x2, &y2);
+
+  x1 = floor (x1);
+  y1 = floor (y1);
+  x2 = ceil (x2);
+  y2 = ceil (y2);
+  x2 -= x1;
+  y2 -= y1;
+
+  if (x1 < G_MININT || x1 > G_MAXINT ||
+      y1 < G_MININT || y1 > G_MAXINT ||
+      x2 > G_MAXINT || y2 > G_MAXINT)
+    {
+      extents->x = extents->y = extents->width = extents->height = 0;
+      return FALSE;
+    }
+
+  extents->x = x1;
+  extents->y = y1;
+  extents->width = x2;
+  extents->height = y2;
+
+  return TRUE;
+}
+
+static void
+_data_provider_release_cairo_surface (void* info, const void* data, size_t size)
+{
+  cairo_surface_destroy ((cairo_surface_t *)info);
+}
+
+/* Returns a new NSImage or %NULL in case of an error.
+ * The device scale factor will be transfered to the NSImage (hidpi)
+ */
+NSImage *
+_gtk_quartz_create_image_from_surface (cairo_surface_t *surface)
+{
+  CGColorSpaceRef colorspace;
+  CGDataProviderRef data_provider;
+  CGImageRef image;
+  void *data;
+  NSImage *nsimage;
+  double sx, sy;
+  cairo_t *cr;
+  cairo_surface_t *img_surface;
+  cairo_rectangle_int_t extents;
+  int width, height, rowstride;
+
+  if (!_cairo_surface_extents (surface, &extents))
+    return NULL;
+
+  cairo_surface_get_device_scale (surface, &sx, &sy);
+  width = extents.width * sx;
+  height = extents.height * sy;
+
+  img_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+  cr = cairo_create (img_surface);
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+  cairo_scale (cr, sx, sy);
+  cairo_set_source_surface (cr, surface, -extents.x, -extents.y);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+
+  cairo_surface_flush (img_surface);
+  rowstride = cairo_image_surface_get_stride (img_surface);
+  data = cairo_image_surface_get_data (img_surface);
+
+  colorspace = CGColorSpaceCreateDeviceRGB ();
+  /* Note: the release callback will only be called after NSImage below dies */
+  data_provider = CGDataProviderCreateWithData (surface, data, height * rowstride,
+                                                _data_provider_release_cairo_surface);
+
+  image = CGImageCreate (width, height, 8,
+                         32, rowstride,
+                         colorspace,
+                         /* XXX: kCGBitmapByteOrderDefault gives wrong colors..?? */
+                         kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst,
+                         data_provider, NULL, FALSE,
+                         kCGRenderingIntentDefault);
+  CGDataProviderRelease (data_provider);
+  CGColorSpaceRelease (colorspace);
+
+  nsimage = [[NSImage alloc] initWithCGImage:image size:NSMakeSize (extents.width, extents.height)];
+  CGImageRelease (image);
+
+  return nsimage;
+}
+
 NSImage *
 _gtk_quartz_create_image_from_pixbuf (GdkPixbuf *pixbuf)
 {
diff --git a/gtk/gtkquartz.h b/gtk/gtkquartz.h
index 70f9268..e739508 100644
--- a/gtk/gtkquartz.h
+++ b/gtk/gtkquartz.h
@@ -39,6 +39,8 @@ void _gtk_quartz_set_selection_data_for_pasteboard (NSPasteboard *pasteboard,
 
 NSImage *_gtk_quartz_create_image_from_pixbuf (GdkPixbuf *pixbuf);
 
+NSImage *_gtk_quartz_create_image_from_surface (cairo_surface_t *surface);
+
 G_END_DECLS
 
 #endif /* __GTK_QUARTZ_H__ */


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