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



On 25.02.2005 00:57, Owen Taylor wrote:
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.

[a little more from your original mail]
The obvious response here is "that's a lot of DCs".
I don't think DC's are the critically scarce resource in current Windows that they once were, but yes, it is.

I'm one of these peoples: uhm that's *a lot* of resource.
And as I see it in many cases wasted for no good reason,
i.e. why would a pixmap - not to be drawn to - need a
permanent DC ?

But "moving more towards one-DC-per-GdkDrawable" sounds
reasonable and required for concurrent drawing Gdk *and*
Cairo on the same thing.

So the second iteration of my Gdk/Cairo integration does
exactly this :
- add hdc_get(), hdc_release() to GdkDrawableImplWin32 vtable
- make the two win32 drawable impls pixmap and window manage
  *one* refcounted DC per drawable
- replace the internal use of CreateCompatibleDC/SelectObject/
  SelectObject/DeleteDC with gdk_pixmap_impl_win32_hdc_(get|release)

For the non Cairo case this boils dowwn to one more indirection
but *no* additional resource usage. For the Cairo case the DC
will be created together with the surface and go away when
destroying the surface. It simply is reused for drawing to
another drawable, see blit_from_pixmap()


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?

It is obviously less hackish than my first approach
but - as tried to outline above - has some issue which
I'd like to avoid.

Also I don't like the use of acquire/release [1] and the
parallel API it introduces to the existing gdk_win32_hdc_get/
gdk_win32_hdc_release.

How to continue ?

	Hans

[1] (probably biased by my day job, for me when 'acquiring'
     something a camera should be involved ;-)

-------- Hans "at" Breuer "dot" Org -----------
Tell me what you need, and I'll tell you how to
get along without it.                -- Dilbert
diff --exclude-from=c:\util\tool\diff.ign -u --recursive from-cvs/gtk+/gdk/win32/gdkdrawable-win32.c my-gtk/gtk+/gdk/win32/gdkdrawable-win32.c
--- from-cvs/gtk+/gdk/win32/gdkdrawable-win32.c	Sat Jan 29 23:42:37 2005
+++ my-gtk/gtk+/gdk/win32/gdkdrawable-win32.c	Sun Feb 13 23:59:02 2005
@@ -33,6 +33,8 @@
 
 #include <pango/pangowin32.h>
 
+#include <cairo-win32.h>
+
 #include "gdkscreen.h" /* gdk_screen_get_default() */
 #include "gdkregion-generic.h"
 #include "gdkprivate-win32.h"
@@ -123,6 +125,9 @@
 				      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 +197,8 @@
   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;
 
@@ -205,11 +212,35 @@
 static void
 gdk_drawable_impl_win32_finalize (GObject *object)
 {
+  GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (object);
+
   gdk_drawable_set_colormap (GDK_DRAWABLE (object), NULL);
 
+  if (impl->cairo_surface)
+    {
+      cairo_surface_destroy (impl->cairo_surface);
+      _gdk_drawable_impl_win32_hdc_release (impl);
+      impl->cairo_surface = NULL;
+    }
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+HDC
+_gdk_drawable_impl_win32_hdc_get (GdkDrawableImplWin32* impl)
+{
+  g_return_val_if_fail (GDK_IS_DRAWABLE_IMPL_WIN32 (impl), NULL);
+
+  return GDK_DRAWABLE_IMPL_WIN32_GET_CLASS (impl)->hdc_get (impl);
+}
+
+void 
+_gdk_drawable_impl_win32_hdc_release (GdkDrawableImplWin32* impl)
+{
+  g_return_if_fail (GDK_IS_DRAWABLE_IMPL_WIN32 (impl));
+
+  GDK_DRAWABLE_IMPL_WIN32_GET_CLASS (impl)->hdc_release (impl);
+}
+
 /*****************************************************
  * Win32 specific implementations of generic functions *
  *****************************************************/
@@ -1619,7 +1662,6 @@
 		  gint         	      	height)
 {
   HDC srcdc;
-  HBITMAP holdbitmap;
   RGBQUAD oldtable[256], newtable[256];
   COLORREF bg, fg;
 
@@ -1628,15 +1670,7 @@
   
   GDK_NOTE (MISC, g_print ("blit_from_pixmap\n"));
 
-  if (!(srcdc = CreateCompatibleDC (NULL)))
-    {
-      WIN32_GDI_FAILED ("CreateCompatibleDC");
-      return;
-    }
-      
-  if (!(holdbitmap = SelectObject (srcdc, ((GdkDrawableImplWin32 *) src)->handle)))
-    WIN32_GDI_FAILED ("SelectObject");
-  else
+  if ((srcdc = _gdk_drawable_impl_win32_hdc_get (GDK_DRAWABLE_IMPL_WIN32 (src))) != NULL)
     {
       if (GDK_PIXMAP_OBJECT (src->parent_instance.wrapper)->depth <= 8)
 	{
@@ -1735,10 +1769,8 @@
 			     srcdc, oldtable_size));
 	  GDI_CALL (SetDIBColorTable, (srcdc, 0, oldtable_size, oldtable));
 	}
-      
-      GDI_CALL (SelectObject, (srcdc, holdbitmap));
+      _gdk_drawable_impl_win32_hdc_release (GDK_DRAWABLE_IMPL_WIN32 (src));
     }
-  GDI_CALL (DeleteDC, (srcdc));
 }
 
 static void
@@ -1961,6 +1993,40 @@
 gdk_win32_drawable_get_handle (GdkDrawable *drawable)
 {
   return GDK_DRAWABLE_HANDLE (drawable);
+}
+
+static cairo_surface_t *
+gdk_win32_drawable_get_cairo_surface (GdkDrawable *drawable)
+{
+  GdkDrawableImplWin32 *impl = GDK_DRAWABLE_IMPL_WIN32 (drawable);
+  HDC hdc;
+
+  if (GDK_IS_WINDOW_IMPL_WIN32 (drawable) &&
+      GDK_WINDOW_DESTROYED (impl->wrapper))
+    return NULL;
+
+  if (impl->cairo_surface)
+    {
+      /* get rid of it (apparently there is no way to update the HDC in an existing 
+       * win32 surface. And I doubt there should ;)
+       */
+      cairo_surface_destroy (impl->cairo_surface);
+      _gdk_drawable_impl_win32_hdc_release (impl);
+    }
+  hdc = _gdk_drawable_impl_win32_hdc_get (impl); 
+  if (hdc)
+    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);
 }
 
 gboolean
diff --exclude-from=c:\util\tool\diff.ign -u --recursive from-cvs/gtk+/gdk/win32/gdkdrawable-win32.h my-gtk/gtk+/gdk/win32/gdkdrawable-win32.h
--- from-cvs/gtk+/gdk/win32/gdkdrawable-win32.h	Sun Feb 17 16:57:18 2002
+++ my-gtk/gtk+/gdk/win32/gdkdrawable-win32.h	Sat Feb 12 23:53:47 2005
@@ -53,16 +53,23 @@
   GdkDrawable *wrapper;
   GdkColormap *colormap;
   HANDLE handle;
+
+  cairo_surface_t *cairo_surface;
 };
  
 struct _GdkDrawableImplWin32Class 
 {
   GdkDrawableClass parent_class;
 
+  HDC  (*hdc_get)     (GdkDrawableImplWin32* impl);
+  void (*hdc_release) (GdkDrawableImplWin32* impl);
 };
 
 GType gdk_drawable_impl_win32_get_type (void);
 
+HDC  _gdk_drawable_impl_win32_hdc_get     (GdkDrawableImplWin32* impl);
+void _gdk_drawable_impl_win32_hdc_release (GdkDrawableImplWin32* impl);
+ 
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --exclude-from=c:\util\tool\diff.ign -u --recursive from-cvs/gtk+/gdk/win32/gdkpixmap-win32.c my-gtk/gtk+/gdk/win32/gdkpixmap-win32.c
--- from-cvs/gtk+/gdk/win32/gdkpixmap-win32.c	Tue Nov 30 23:43:24 2004
+++ my-gtk/gtk+/gdk/win32/gdkpixmap-win32.c	Sun Feb 13 00:12:55 2005
@@ -39,10 +40,13 @@
 					      gint               *width,
 					      gint               *height);
 
-static void gdk_pixmap_impl_win32_init       (GdkPixmapImplWin32      *pixmap);
-static void gdk_pixmap_impl_win32_class_init (GdkPixmapImplWin32Class *klass);
-static void gdk_pixmap_impl_win32_finalize   (GObject                 *object);
+static void gdk_pixmap_impl_win32_init        (GdkPixmapImplWin32      *pixmap);
+static void gdk_pixmap_impl_win32_class_init  (GdkPixmapImplWin32Class *klass);
+static void gdk_pixmap_impl_win32_finalize    (GObject                 *object);
 
+static HDC  gdk_pixmap_impl_win32_hdc_get     (GdkPixmapImplWin32      *impl);
+static void gdk_pixmap_impl_win32_hdc_release (GdkPixmapImplWin32      *impl);
+
 static gpointer parent_class = NULL;
 
 GType
@@ -91,12 +95,46 @@
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);
+  GdkDrawableImplWin32Class *impl_class = GDK_DRAWABLE_IMPL_WIN32_CLASS (klass);
   
   parent_class = g_type_class_peek_parent (klass);
 
   object_class->finalize = gdk_pixmap_impl_win32_finalize;
 
   drawable_class->get_size = gdk_pixmap_impl_win32_get_size;
+
+  impl_class->hdc_get     = gdk_pixmap_impl_win32_hdc_get;
+  impl_class->hdc_release = gdk_pixmap_impl_win32_hdc_release;
+}
+
+static HDC
+gdk_pixmap_impl_win32_hdc_get (GdkPixmapImplWin32 *impl)
+{
+  if (impl->hdc_refs > 0)
+    {
+      impl->hdc_refs++;
+    }
+  else
+    {
+      impl->hdc = CreateCompatibleDC (NULL);
+      if (!(impl->holdbitmap = SelectObject (impl->hdc, ((GdkDrawableImplWin32 *) impl)->handle)))
+        WIN32_GDI_FAILED ("SelectObject");
+      else
+        impl->hdc_refs = 1;
+    }
+  return impl->hdc;
+}
+
+static void
+gdk_pixmap_impl_win32_hdc_release (GdkPixmapImplWin32 *impl)
+{
+  impl->hdc_refs--;
+  if (impl->hdc_refs == 0)
+    {
+      GDI_CALL (SelectObject, (impl->hdc, impl->holdbitmap));
+      GDI_CALL (DeleteDC, (impl->hdc));
+      impl->hdc = NULL;
+    }
 }
 
 static void
diff --exclude-from=c:\util\tool\diff.ign -u --recursive from-cvs/gtk+/gdk/win32/gdkpixmap-win32.h my-gtk/gtk+/gdk/win32/gdkpixmap-win32.h
--- from-cvs/gtk+/gdk/win32/gdkpixmap-win32.h	Sat Jul 19 22:58:50 2003
+++ my-gtk/gtk+/gdk/win32/gdkpixmap-win32.h	Sat Feb 12 23:31:19 2005
@@ -55,6 +55,10 @@
   gint height;
   guchar *bits;
   guint is_foreign : 1;
+
+  gint    hdc_refs;   /* how many times the hdc is shared */
+  HDC     hdc;        /* cached hdc to allow simultaneous use, e.g. Cairo and Gdk */
+  HBITMAP holdbitmap; /* keeps the bitmap to be restored to the hdc */
 };
  
 struct _GdkPixmapImplWin32Class 
diff --exclude-from=c:\util\tool\diff.ign -u --recursive from-cvs/gtk+/gdk/win32/gdkwindow-win32.c my-gtk/gtk+/gdk/win32/gdkwindow-win32.c
--- from-cvs/gtk+/gdk/win32/gdkwindow-win32.c	Sat Jan 29 23:42:39 2005
+++ my-gtk/gtk+/gdk/win32/gdkwindow-win32.c	Sat Feb 12 23:47:11 2005
@@ -78,6 +78,9 @@
 static void         gdk_window_impl_win32_get_size     (GdkDrawable *drawable,
 							gint *width,
 							gint *height);
+static HDC          gdk_window_impl_win32_hdc_get      (GdkWindowImplWin32 *impl);
+static void         gdk_window_impl_win32_hdc_release  (GdkWindowImplWin32 *impl);
+
 static GdkRegion*   gdk_window_impl_win32_get_visible_region (GdkDrawable *drawable);
 static void gdk_window_impl_win32_init       (GdkWindowImplWin32      *window);
 static void gdk_window_impl_win32_class_init (GdkWindowImplWin32Class *klass);
@@ -137,6 +140,7 @@
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);
+  GdkDrawableImplWin32Class *impl_class = GDK_DRAWABLE_IMPL_WIN32_CLASS (klass);
   
   parent_class = g_type_class_peek_parent (klass);
 
@@ -149,6 +153,9 @@
   /* Visible and clip regions are the same */
   drawable_class->get_clip_region = gdk_window_impl_win32_get_visible_region;
   drawable_class->get_visible_region = gdk_window_impl_win32_get_visible_region;
+
+  impl_class->hdc_get     = gdk_window_impl_win32_hdc_get;
+  impl_class->hdc_release = gdk_window_impl_win32_hdc_release;
 }
 
 static void
@@ -265,6 +272,35 @@
     *height = GDK_WINDOW_IMPL_WIN32 (drawable)->height;
 }
 
+static HDC
+gdk_window_impl_win32_hdc_get (GdkWindowImplWin32 *impl)
+{
+  if (impl->hdc_refs > 0)
+    {
+      impl->hdc_refs++;
+    }
+  else
+    {
+      impl->hdc = GetDC (((GdkDrawableImplWin32 *) impl)->handle);
+      if (!impl->hdc)
+        WIN32_GDI_FAILED ("GetDC");
+      else
+        impl->hdc_refs = 1;
+    }
+  return impl->hdc;
+}
+
+static void
+gdk_window_impl_win32_hdc_release (GdkWindowImplWin32 *impl)
+{
+  impl->hdc_refs--;
+  if (impl->hdc_refs == 0)
+    {
+      GDI_CALL (ReleaseDC, (((GdkDrawableImplWin32 *) impl)->handle, impl->hdc));
+      impl->hdc = NULL;
+    }
+}
+
 static GdkRegion*
 gdk_window_impl_win32_get_visible_region (GdkDrawable *drawable)
 {
diff --exclude-from=c:\util\tool\diff.ign -u --recursive from-cvs/gtk+/gdk/win32/gdkwindow-win32.h my-gtk/gtk+/gdk/win32/gdkwindow-win32.h
--- from-cvs/gtk+/gdk/win32/gdkwindow-win32.h	Sun Dec 14 12:11:24 2003
+++ my-gtk/gtk+/gdk/win32/gdkwindow-win32.h	Sat Feb 12 23:50:04 2005
@@ -84,6 +84,9 @@
   GdkGeometry hints;
 
   gboolean extension_events_selected;
+
+  gint    hdc_refs;   /* how many times the hdc is shared */
+  HDC     hdc;        /* cached hdc to allow simultaneous use, e.g. Cairo and Gdk */
 };
  
 struct _GdkWindowImplWin32Class 


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