libgnomecanvas clipping



Hello!

I ported full clipping implementation from gnome-1.4 print preview
to libgnomecanvas. It is not optimal, but IMHO is the way to go for
now.

- Instead of passing clip-SVP around, it generates temporary render
  buffer, and blits its content into parent buffer - so it works
  for all user-defined items automatically.
- It can probably be optimized, by using ArtRender and doing some
  backround/bbox calculations.
- It allocates buffers permanently, so program loses 65K for each
  clipping level. This could probably be optimized by some timeout
  based cache - or allocating/reallocating on-the-fly
- It is really needed for print preview (both gnumeric and sodipodi
  use clipping to cut out unneeded printout).

The real solution would be to extend GnomeCanvasItem class to include
::render_rgba method, that paints into rgba buffer - so we could
manipulate more advanced things, like gray masks and global opacity
as well. I have such generic framework in action inside sodipodi, but
it is too premature, to make it its own library yet.

May I commit it to libgnomecanvas? If you do not think, it is good
idea, I'll create custom clip item inside libgnomeprintui.

Best wishes,
Lauris Kaplinski



Index: libgnomecanvas/ChangeLog
===================================================================
RCS file: /cvs/gnome/libgnomecanvas/libgnomecanvas/ChangeLog,v
retrieving revision 1.98
diff -u -r1.98 ChangeLog
--- libgnomecanvas/ChangeLog	2001/12/03 23:38:11	1.98
+++ libgnomecanvas/ChangeLog	2001/12/06 17:53:31
@@ -1,3 +1,16 @@
+2001-12-06  Lauris Kaplinski  <lauris ximian com>
+
+	* gnome-canvas-clipgroup.c (gnome_canvas_clipgroup_class_init): Moved
+	gobject virtual method setup before property installation
+	(gnome_canvas_clipgroup_update): Invoke parent method with NULL
+	clippath, recalculate bounding box
+	(gnome_canvas_clipgroup_render): Implement, do buffer/buffer
+	clipped composition here
+	(gcg_buf_new): Quick'n'dirty buffer cache
+	(gcg_buf_free): Ditto
+	(gcg_mask_new): Ditto
+	(gcg_mask_free): Ditto
+
 2001-12-02  Anders Carlsson  <andersca gnu org>
 
 	* gnome-canvas.h: Remove unused signals from vtable and
Index: libgnomecanvas/gnome-canvas-clipgroup.c
===================================================================
RCS file: /cvs/gnome/libgnomecanvas/libgnomecanvas/gnome-canvas-clipgroup.c,v
retrieving revision 1.3
diff -u -r1.3 gnome-canvas-clipgroup.c
--- libgnomecanvas/gnome-canvas-clipgroup.c	2001/06/17 23:02:10	1.3
+++ libgnomecanvas/gnome-canvas-clipgroup.c	2001/12/06 17:53:33
@@ -20,6 +20,7 @@
 #include <gtk/gtkobject.h>
 #include <gtk/gtkwidget.h>
 
+#include <libart_lgpl/art_misc.h>
 #include <libart_lgpl/art_rect.h>
 #include <libart_lgpl/art_vpath.h>
 #include <libart_lgpl/art_bpath.h>
@@ -27,9 +28,9 @@
 #include <libart_lgpl/art_vpath_bpath.h>
 #include <libart_lgpl/art_svp.h>
 #include <libart_lgpl/art_svp_vpath.h>
-#include <libart_lgpl/art_vpath_dash.h>
+#include <libart_lgpl/art_rect_svp.h>
+#include <libart_lgpl/art_gray_svp.h>
 #include <libart_lgpl/art_svp_wind.h>
-#include <libart_lgpl/art_svp_point.h>
 #include <libart_lgpl/art_svp_ops.h>
 
 #include "gnome-canvas.h"
@@ -58,6 +59,35 @@
                                                     ArtSVP                    *clip_path,
                                                     int                        flags);
 
+/*
+ * Generic clipping stuff
+ *
+ * This is somewhat slow and memory-hungry - we add extra
+ * composition, extra SVP render and allocate 65536
+ * bytes for each clip level. It could be done more
+ * efficently per-object basis - but to make clipping
+ * universal, there is no alternative to double
+ * buffering (although it should be done into RGBA
+ * buffer by other method than ::render to make global
+ * opacity possible).
+ * Using art-render could possibly optimize that a bit,
+ * although I am not sure.
+ */
+
+#define GCG_BUF_WIDTH 128
+#define GCG_BUF_HEIGHT 128
+#define GCG_BUF_PIXELS (GCG_BUF_WIDTH * GCG_BUF_HEIGHT)
+#define GCG_BUF_SIZE (GCG_BUF_WIDTH * GCG_BUF_HEIGHT * 3)
+
+#define noSHOW_SHADOW
+
+static guchar *gcg_buf_new (void);
+static void gcg_buf_free (guchar *buf);
+static guchar *gcg_mask_new (void);
+static void gcg_mask_free (guchar *mask);
+
+static void gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
 static GnomeCanvasGroupClass *parent_class;
 
 GtkType
@@ -96,6 +126,11 @@
 
 	parent_class = gtk_type_class (gnome_canvas_group_get_type ());
 
+	object_class->destroy = gnome_canvas_clipgroup_destroy;
+
+	gobject_class->set_property = gnome_canvas_clipgroup_set_property;
+	gobject_class->get_property = gnome_canvas_clipgroup_get_property;
+
         g_object_class_install_property (gobject_class,
                                          PROP_PATH,
                                          g_param_spec_pointer ("path", NULL, NULL,
@@ -106,12 +141,8 @@
                                                             0, G_MAXUINT, 0,
                                                             (G_PARAM_READABLE | G_PARAM_WRITABLE)));
 
-	object_class->destroy = gnome_canvas_clipgroup_destroy;
-
-	gobject_class->set_property = gnome_canvas_clipgroup_set_property;
-	gobject_class->get_property = gnome_canvas_clipgroup_get_property;
-
 	item_class->update = gnome_canvas_clipgroup_update;
+	item_class->render = gnome_canvas_clipgroup_render;
 }
 
 static void
@@ -213,14 +244,12 @@
 static void
 gnome_canvas_clipgroup_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
 {
-	GnomeCanvasGroup * group;
-	GnomeCanvasClipgroup * clipgroup;
-	ArtBpath * bp;
-	ArtBpath * bpath;
-	ArtVpath * vpath1, * vpath2;
-	ArtSVP * svp, * svp1, * svp2;
+	GnomeCanvasClipgroup *clipgroup;
+	ArtBpath *bp;
+	ArtBpath *bpath;
+	ArtVpath *vpath1, *vpath2;
+	ArtSVP *svp, *svp1, *svp2;
 
-	group = GNOME_CANVAS_GROUP (item);
 	clipgroup = GNOME_CANVAS_CLIPGROUP (item);
 
 	if (clipgroup->svp) {
@@ -254,13 +283,172 @@
 			svp = svp1;
 		}
 
+		clipgroup->svp = svp;
+	}
+
+	if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
+		(GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, NULL, flags);
+
+	if (clipgroup->svp) {
+		ArtDRect cbox;
+		art_drect_svp (&cbox, clipgroup->svp);
+		item->x1 = MAX (item->x1, cbox.x0 - 1.0);
+		item->y1 = MAX (item->y1, cbox.y0 - 1.0);
+		item->x2 = MIN (item->x2, cbox.x1 + 1.0);
+		item->y2 = MIN (item->y2, cbox.y1 + 1.0);
+	}
+}
+
+/* non-premultiplied composition into RGB */
+
+#define COMPOSEN11(fc,fa,bc) (((255 - (guint) (fa)) * (guint) (bc) + (guint) (fc) * (guint) (fa) + 127) / 255)
+
+static void
+gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+	GnomeCanvasClipgroup *cg;
+	GnomeCanvasBuf lbuf;
+	guchar *mask;
+
+	cg = GNOME_CANVAS_CLIPGROUP (item);
+
+	if (cg->svp) {
+		gint bw, bh, sw, sh;
+		gint x, y;
+
+		/* fixme: We could optimize background handling (lauris) */
+
+		if (buf->is_bg) {
+			gnome_canvas_buf_ensure_buf (buf);
+			buf->is_bg = FALSE;
+			buf->is_buf = TRUE;
+		}
+
+		bw = buf->rect.x1 - buf->rect.x0;
+		bh = buf->rect.y1 - buf->rect.y0;
+		if ((bw < 1) || (bh < 1)) return;
+
+		if (bw * bh <= GCG_BUF_PIXELS) {
+			/* We can go with single buffer */
+			sw = bw;
+			sh = bh;
+		} else if (bw <= (GCG_BUF_PIXELS >> 3)) {
+			/* Go with row buffer */
+			sw = bw;
+			sh =  GCG_BUF_PIXELS / bw;
+		} else if (bh <= (GCG_BUF_PIXELS >> 3)) {
+			/* Go with column buffer */
+			sw = GCG_BUF_PIXELS / bh;
+			sh = bh;
+		} else {
+			/* Tile buffer */
+			sw = GCG_BUF_WIDTH;
+			sh = GCG_BUF_HEIGHT;
+		}
+
+		/* Set up local buffer */
+		lbuf.buf = gcg_buf_new ();
+		lbuf.bg_color = buf->bg_color;
+		lbuf.is_bg = FALSE;
+		lbuf.is_buf = TRUE;
+		/* Allocate mask */
+		mask = gcg_mask_new ();
+
+		for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
+			for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
+				gint r, xx, yy;
+				/* Set up local buffer */
+				lbuf.rect.x0 = x;
+				lbuf.rect.y0 = y;
+				lbuf.rect.x1 = MIN (x + sw, buf->rect.x1);
+				lbuf.rect.y1 = MIN (y + sh, buf->rect.y1);
+				lbuf.buf_rowstride = 3 * (lbuf.rect.x1 - lbuf.rect.x0);
+				/* Copy background */
+				for (r = lbuf.rect.y0; r < lbuf.rect.y1; r++) {
+					memcpy (lbuf.buf + (r - lbuf.rect.y0) * lbuf.buf_rowstride,
+						buf->buf + (r - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3,
+						(lbuf.rect.x1 - lbuf.rect.x0) * 3);
+				}
+				/* Invoke render method */
+				if (((GnomeCanvasItemClass *) parent_class)->render)
+					((GnomeCanvasItemClass *) parent_class)->render (item, &lbuf);
+				/* Render mask */
+				art_gray_svp_aa (cg->svp, lbuf.rect.x0, lbuf.rect.y0, lbuf.rect.x1, lbuf.rect.y1,
+						 mask, lbuf.rect.x1 - lbuf.rect.x0);
+				/* Combine */
+				for (yy = lbuf.rect.y0; yy < lbuf.rect.y1; yy++) {
+					guchar *s, *m, *d;
+					s = lbuf.buf + (yy - lbuf.rect.y0) * lbuf.buf_rowstride;
+					m = mask + (yy - lbuf.rect.y0) * (lbuf.rect.x1 - lbuf.rect.x0);
+					d = buf->buf + (yy - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
+					for (xx = lbuf.rect.x0; xx < lbuf.rect.x1; xx++) {
+#ifndef SHOW_SHADOW
+						d[0] = COMPOSEN11 (s[0], m[0], d[0]);
+						d[1] = COMPOSEN11 (s[1], m[0], d[1]);
+						d[2] = COMPOSEN11 (s[2], m[0], d[2]);
+#else
+						d[0] = COMPOSEN11 (s[0], m[0] | 0x7f, d[0]);
+						d[1] = COMPOSEN11 (s[1], m[0] | 0x7f, d[1]);
+						d[2] = COMPOSEN11 (s[2], m[0] | 0x7f, d[2]);
+#endif
+						s += 3;
+						m += 1;
+						d += 3;
+					}
+				}
+			}
+		}
+		/* Free buffers */
+		gcg_mask_free (mask);
+		gcg_buf_free (lbuf.buf);
 	} else {
-		svp = clip_path;
+		if (((GnomeCanvasItemClass *) parent_class)->render)
+			((GnomeCanvasItemClass *) parent_class)->render (item, buf);
 	}
+}
 
-	clipgroup->svp = svp;
+static GSList *gcg_buffers = NULL;
+static GSList *gcg_masks = NULL;
 
-	if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
-		(GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, svp, flags);
+static guchar *
+gcg_buf_new (void)
+{
+	guchar *buf;
+
+	if (!gcg_buffers) {
+		buf = g_new (guchar, GCG_BUF_SIZE);
+	} else {
+		buf = (guchar *) gcg_buffers->data;
+		gcg_buffers = g_slist_remove (gcg_buffers, buf);
+	}
+
+	return buf;
+}
+
+static void
+gcg_buf_free (guchar *buf)
+{
+	gcg_buffers = g_slist_prepend (gcg_buffers, buf);
+}
 
+static guchar *
+gcg_mask_new (void)
+{
+	guchar *mask;
+
+	if (!gcg_masks) {
+		mask = g_new (guchar, GCG_BUF_PIXELS);
+	} else {
+		mask = (guchar *) gcg_masks->data;
+		gcg_masks = g_slist_remove (gcg_masks, mask);
+	}
+
+	return mask;
+}
+
+static void
+gcg_mask_free (guchar *mask)
+{
+	gcg_masks = g_slist_prepend (gcg_masks, mask);
 }
+


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