Re: gdk/win32 and cairo [was: Cairo now a dependency of HEAD GTK+]



On Mon, 2005-02-07 at 23:55 -0500, Owen Taylor wrote:

> The GTK+ code currently is organized around one DC per
> GC. My thought is that we should be moving more towards
> one-DC-per-GdkDrawable. If we have a DC associated with
> the GdkPixmap, then we can use that DC both for passing
> to Cairo and for BitBlt from that surface to another.
> There is no need to select the bitmap into another DC.

Here's a rough implementation of this idea ... it really
needs a couple of pending API additions in Cairo to be
fully finished, since right now, we just have to keep
the Cairo surface and DC around until the window or 
pixmap is destroyed.

This isn't a huge deal since drawing with Cairo is typically
done to a temporary pixmap, but we should be able to do
better once we have 

 cairo_surface_set_user_data(): Allows us to get notification
   when the surface is no longer used, so we can free it
   and the DC.
cairo_surface_finish(): Allows us to force the surface
   to release the DC on gdk_window_destroy().

How do you think this compares to your approach?

					Owen

? gdk.def
? win32/rc/foo.c
Index: win32/gdkdrawable-win32.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/win32/gdkdrawable-win32.c,v
retrieving revision 1.72
diff -u -p -r1.72 gdkdrawable-win32.c
--- win32/gdkdrawable-win32.c	12 Jan 2005 21:28:03 -0000	1.72
+++ win32/gdkdrawable-win32.c	24 Feb 2005 23:49:23 -0000
@@ -32,6 +32,7 @@
 #include <glib.h>
 
 #include <pango/pangowin32.h>
+#include <cairo-win32.h>
 
 #include "gdkscreen.h" /* gdk_screen_get_default() */
 #include "gdkregion-generic.h"
@@ -123,6 +124,9 @@ static void gdk_win32_draw_image     (Gd
 				      gint             width,
 				      gint             height);
 
+static void gdk_win32_set_cairo_target (GdkDrawable *drawable,
+					cairo_t     *cr);
+
 static void gdk_win32_set_colormap   (GdkDrawable    *drawable,
 				      GdkColormap    *colormap);
 
@@ -192,6 +196,8 @@ gdk_drawable_impl_win32_class_init (GdkD
   drawable_class->draw_glyphs_transformed = gdk_win32_draw_glyphs_transformed;
   drawable_class->draw_image = gdk_win32_draw_image;
   
+  drawable_class->set_cairo_target = gdk_win32_set_cairo_target;
+  
   drawable_class->set_colormap = gdk_win32_set_colormap;
   drawable_class->get_colormap = gdk_win32_get_colormap;
 
@@ -1628,12 +1634,10 @@ blit_from_pixmap (gboolean              
   
   GDK_NOTE (MISC, g_print ("blit_from_pixmap\n"));
 
-  if (!(srcdc = CreateCompatibleDC (NULL)))
-    {
-      WIN32_GDI_FAILED ("CreateCompatibleDC");
-      return;
-    }
-      
+  srcdc = _gdk_win32_drawable_acquire_dc (GDK_DRAWABLE (src));
+  if (!srcdc)
+    return;
+  
   if (!(holdbitmap = SelectObject (srcdc, ((GdkDrawableImplWin32 *) src)->handle)))
     WIN32_GDI_FAILED ("SelectObject");
   else
@@ -1738,7 +1742,8 @@ blit_from_pixmap (gboolean              
       
       GDI_CALL (SelectObject, (srcdc, holdbitmap));
     }
-  GDI_CALL (DeleteDC, (srcdc));
+  
+  _gdk_win32_drawable_release_dc (GDK_DRAWABLE (src));
 }
 
 static void
@@ -1937,6 +1942,121 @@ gdk_win32_draw_image (GdkDrawable     *d
 		   xsrc, ysrc, xdest, ydest, width, height);
 }
 
+/**
+ * _gdk_win32_drawable_acquire_dc
+ * @drawable: a Win32 #GdkDrawable implementation
+ * 
+ * Gets a DC with the given drawable selected into
+ * it.
+ *
+ * Return value: The DC, on success. Otherwise
+ *  %NULL. If this function succeeded
+ *  _gdk_win32_drawable_release_dc()  must be called
+ *  release the DC when you are done using it.
+ **/
+HDC 
+_gdk_win32_drawable_acquire_dc (GdkDrawable *drawable)
+{
+  GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (drawable);
+  
+  if (GDK_IS_WINDOW_IMPL_WIN32 (drawable) &&
+      GDK_WINDOW_DESTROYED (impl->wrapper))
+    return NULL;
+
+  if (!impl->hdc)
+    {
+      if (GDK_IS_PIXMAP_IMPL_WIN32 (impl))
+	{
+	  impl->hdc = CreateCompatibleDC (NULL);
+	  if (!impl->hdc)
+	    WIN32_GDI_FAILED ("CreateCompatibleDC");
+	  
+	  if (impl->hdc)
+	    {
+	      impl->saved_dc_bitmap = SelectObject (impl->hdc,
+						    impl->handle);
+	      if (!impl->saved_dc_bitmap)
+		{
+		  WIN32_GDI_FAILED ("CreateCompatibleDC");
+		  DeleteDC (impl->hdc);
+		  impl->hdc = NULL;
+		}
+	    }
+	}
+      else
+	{
+	  impl->hdc = GetDC (impl->handle);
+	  if (!impl->hdc)
+	    WIN32_GDI_FAILED ("GetDC");
+	}
+    }
+
+  if (impl->hdc)
+    {
+      impl->hdc_count++;
+      return impl->hdc;
+    }
+  else
+    return NULL;
+}
+
+/**
+ * _gdk_win32_drawable_release_dc
+ * @drawable: a Win32 #GdkDrawable implementation
+ * 
+ * Releases the reference count for the DC
+ * from _gdk_win32_drawable_acquire_dc()
+ **/
+void
+_gdk_win32_drawable_release_dc (GdkDrawable *drawable)
+{
+  GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (drawable);
+  
+  g_return_if_fail (impl->hdc_count > 0);
+
+  impl->hdc_count--;
+  if (impl->hdc_count == 0)
+    {
+      if (impl->saved_dc_bitmap)
+	{
+	  GDI_CALL (SelectObject, (impl->hdc, impl->saved_dc_bitmap));
+	  impl->saved_dc_bitmap = NULL;
+	}
+      
+      if (impl->hdc)
+	{
+	  GDI_CALL (DeleteDC, (impl->hdc));
+	  impl->hdc = NULL;
+	}
+    }
+}
+
+static cairo_surface_t *
+gdk_win32_drawable_get_cairo_surface (GdkDrawable *drawable)
+{
+  GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (drawable);
+
+  if (!impl->cairo_surface)
+    {
+      HDC hdc = _gdk_win32_drawable_acquire_dc (drawable);
+      if (!hdc)
+	return NULL;
+
+      impl->cairo_surface = cairo_win32_surface_create (hdc);
+    }
+
+  return impl->cairo_surface;
+}
+
+static void
+gdk_win32_set_cairo_target (GdkDrawable *drawable,
+			    cairo_t     *cr)
+{
+  cairo_surface_t *surface = gdk_win32_drawable_get_cairo_surface (drawable);
+  if (surface)
+    cairo_set_target_surface (cr, surface);
+}
+
 static gint
 gdk_win32_get_depth (GdkDrawable *drawable)
 {
@@ -1974,3 +2094,36 @@ gdk_draw_rectangle_alpha_libgtk_only (Gd
 {
   return FALSE;
 }
+
+/**
+ * _gdk_win32_drawable_release_resources
+ * @drawable: a Win32 #GdkDrawable implementation
+ * 
+ * Releases any resources allocated internally for the drawable.
+ * This is called when the drawable becomes unusable
+ * (gdk_window_destroy() for a user, or the refcount going to
+ * zero for a pixmap.)
+ **/
+void
+_gdk_win32_drawable_release_resources (GdkDrawable *drawable)
+{
+  GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (drawable);
+
+  if (impl->cairo_surface)
+    {
+      cairo_surface_destroy (impl->cairo_surface);
+      impl->cairo_surface = NULL;
+    }
+
+  /* This is just a hack until we have
+   * cairo_surface_set_user_data() and cairo_surface_finish()
+   * to allow us to properly handle tracking the DC accounted
+   * to impl->cairo_surface.
+   */
+  if (impl->hdc_count)
+    {
+      impl->hdc_count = 1;
+      _gdk_win32_drawable_release_dc (drawable);
+    }
+}
+
Index: win32/gdkdrawable-win32.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/win32/gdkdrawable-win32.h,v
retrieving revision 1.3
diff -u -p -r1.3 gdkdrawable-win32.h
--- win32/gdkdrawable-win32.h	17 Feb 2002 00:25:05 -0000	1.3
+++ win32/gdkdrawable-win32.h	24 Feb 2005 23:49:23 -0000
@@ -53,6 +53,11 @@ struct _GdkDrawableImplWin32
   GdkDrawable *wrapper;
   GdkColormap *colormap;
   HANDLE handle;
+
+  guint hdc_count;
+  HDC hdc;
+  HBITMAP saved_dc_bitmap;	/* Original bitmap for dc */
+  cairo_surface_t *cairo_surface;
 };
  
 struct _GdkDrawableImplWin32Class 
@@ -62,6 +67,10 @@ struct _GdkDrawableImplWin32Class 
 };
 
 GType gdk_drawable_impl_win32_get_type (void);
+
+HDC  _gdk_win32_drawable_acquire_dc        (GdkDrawable *drawable);
+void _gdk_win32_drawable_release_dc        (GdkDrawable *drawable);
+void _gdk_win32_drawable_release_resources (GdkDrawable *drawable);
 
 #ifdef __cplusplus
 }
Index: win32/gdkpixmap-win32.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/win32/gdkpixmap-win32.c,v
retrieving revision 1.39
diff -u -p -r1.39 gdkpixmap-win32.c
--- win32/gdkpixmap-win32.c	30 Nov 2004 22:56:35 -0000	1.39
+++ win32/gdkpixmap-win32.c	24 Feb 2005 23:49:23 -0000
@@ -108,6 +108,8 @@ gdk_pixmap_impl_win32_finalize (GObject 
   GDK_NOTE (PIXMAP, g_print ("gdk_pixmap_impl_win32_finalize: %p\n",
 			     GDK_PIXMAP_HBITMAP (wrapper)));
 
+  _gdk_win32_drawable_release_resources (GDK_DRAWABLE (object));
+
   if (!DeleteObject (GDK_PIXMAP_HBITMAP (wrapper)))
     WIN32_GDI_FAILED ("DeleteObject");
 
Index: win32/gdkwindow-win32.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/win32/gdkwindow-win32.c,v
retrieving revision 1.128
diff -u -p -r1.128 gdkwindow-win32.c
--- win32/gdkwindow-win32.c	18 Jan 2005 20:59:31 -0000	1.128
+++ win32/gdkwindow-win32.c	24 Feb 2005 23:49:23 -0000
@@ -846,6 +846,8 @@ _gdk_windowing_window_destroy (GdkWindow
 
   if (!recursing && !foreign_destroy)
     {
+      _gdk_win32_drawable_release_resources (private->impl);
+
       private->destroyed = TRUE;
       DestroyWindow (GDK_WINDOW_HWND (window));
     }


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