From 4b23ba53c51a087851788c67f852281b085c9c33 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 24 Jul 2015 15:09:48 +0200 Subject: [PATCH] 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 97841d0496..f7f5182127 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 acd1f182fe..253e8a9a59 100644 --- a/gtk/gtkquartz.c +++ b/gtk/gtkquartz.c @@ -22,6 +22,106 @@ #include "gtkselectionprivate.h" #include + +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 70f9268fe1..e7395082cc 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__ */