aboutsummaryrefslogtreecommitdiffstats
path: root/libgnomecanvas/gnome-canvas-clipgroup.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgnomecanvas/gnome-canvas-clipgroup.c')
-rw-r--r--libgnomecanvas/gnome-canvas-clipgroup.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/libgnomecanvas/gnome-canvas-clipgroup.c b/libgnomecanvas/gnome-canvas-clipgroup.c
new file mode 100644
index 0000000000..adfc749e47
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-clipgroup.c
@@ -0,0 +1,450 @@
+#define GNOME_CANVAS_CLIPGROUP_C
+
+/* Clipping group for GnomeCanvas
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@ximian.com>
+ */
+
+/* These includes are set up for standalone compile. If/when this codebase
+ is integrated into libgnomeui, the includes will need to change. */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.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>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_vpath_bpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_rect_svp.h>
+#include <libart_lgpl/art_gray_svp.h>
+#include <libart_lgpl/art_svp_intersect.h>
+#include <libart_lgpl/art_svp_ops.h>
+
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-clipgroup.h"
+
+enum {
+ PROP_0,
+ PROP_PATH,
+ PROP_WIND
+};
+
+static void gnome_canvas_clipgroup_class_init (GnomeCanvasClipgroupClass *klass);
+static void gnome_canvas_clipgroup_init (GnomeCanvasClipgroup *clipgroup);
+static void gnome_canvas_clipgroup_destroy (GtkObject *object);
+static void gnome_canvas_clipgroup_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_clipgroup_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gnome_canvas_clipgroup_update (GnomeCanvasItem *item,
+ double *affine,
+ 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;
+
+GType
+gnome_canvas_clipgroup_get_type (void)
+{
+ static GType clipgroup_type;
+
+ if (!clipgroup_type) {
+ const GTypeInfo object_info = {
+ sizeof (GnomeCanvasClipgroupClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gnome_canvas_clipgroup_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+ sizeof (GnomeCanvasClipgroup),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gnome_canvas_clipgroup_init,
+ NULL /* value_table */
+ };
+
+ clipgroup_type = g_type_register_static (GNOME_TYPE_CANVAS_GROUP, "GnomeCanvasClipgroup",
+ &object_info, 0);
+ }
+
+ return clipgroup_type;
+}
+
+static void
+gnome_canvas_clipgroup_class_init (GnomeCanvasClipgroupClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ object_class = (GtkObjectClass *) klass;
+ item_class = (GnomeCanvasItemClass *) klass;
+ parent_class = g_type_class_ref (GNOME_TYPE_CANVAS_GROUP);
+
+ 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;
+
+ g_object_class_install_property (gobject_class,
+ PROP_PATH,
+ g_param_spec_pointer ("path", NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ g_object_class_install_property (gobject_class,
+ PROP_WIND,
+ g_param_spec_uint ("wind", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static void
+gnome_canvas_clipgroup_init (GnomeCanvasClipgroup *clipgroup)
+{
+ clipgroup->path = NULL;
+ clipgroup->wind = ART_WIND_RULE_NONZERO; /* default winding rule */
+ clipgroup->svp = NULL;
+}
+
+static void
+gnome_canvas_clipgroup_destroy (GtkObject *object)
+{
+ GnomeCanvasClipgroup *clipgroup;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GNOME_IS_CANVAS_CLIPGROUP (object));
+
+ clipgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+ if (clipgroup->path) {
+ gnome_canvas_path_def_unref (clipgroup->path);
+ clipgroup->path = NULL;
+ }
+
+ if (clipgroup->svp) {
+ art_svp_free (clipgroup->svp);
+ clipgroup->svp = NULL;
+ }
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+static void
+gnome_canvas_clipgroup_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasItem *item;
+ GnomeCanvasClipgroup *cgroup;
+ GnomeCanvasPathDef *gpp;
+
+ item = GNOME_CANVAS_ITEM (object);
+ cgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+ switch (param_id) {
+ case PROP_PATH:
+ gpp = g_value_get_pointer (value);
+
+ if (cgroup->path) {
+ gnome_canvas_path_def_unref (cgroup->path);
+ cgroup->path = NULL;
+ }
+ if (gpp != NULL) {
+ cgroup->path = gnome_canvas_path_def_closed_parts (gpp);
+ }
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case PROP_WIND:
+ cgroup->wind = g_value_get_uint (value);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+gnome_canvas_clipgroup_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GnomeCanvasClipgroup * cgroup;
+
+ cgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+ switch (param_id) {
+ case PROP_PATH:
+ g_value_set_pointer (value, cgroup->path);
+ break;
+
+ case PROP_WIND:
+ g_value_set_uint (value, cgroup->wind);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gnome_canvas_clipgroup_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+ GnomeCanvasClipgroup *clipgroup;
+ ArtSvpWriter *swr;
+ ArtBpath *bp;
+ ArtBpath *bpath;
+ ArtVpath *vpath;
+ ArtSVP *svp, *svp1, *svp2;
+
+ clipgroup = GNOME_CANVAS_CLIPGROUP (item);
+
+ if (clipgroup->svp) {
+ art_svp_free (clipgroup->svp);
+ clipgroup->svp = NULL;
+ }
+
+ if (clipgroup->path) {
+ bp = gnome_canvas_path_def_bpath (clipgroup->path);
+ bpath = art_bpath_affine_transform (bp, affine);
+
+ vpath = art_bez_path_to_vec (bpath, 0.25);
+ art_free (bpath);
+
+ svp1 = art_svp_from_vpath (vpath);
+ art_free (vpath);
+
+ swr = art_svp_writer_rewind_new (clipgroup->wind);
+ art_svp_intersector (svp1, swr);
+
+ svp2 = art_svp_writer_rewind_reap (swr);
+ art_svp_free (svp1);
+
+ if (clip_path != NULL) {
+ svp = art_svp_intersect (svp2, clip_path);
+ art_svp_free (svp2);
+ } else {
+ svp = svp2;
+ }
+
+ 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 {
+ if (((GnomeCanvasItemClass *) parent_class)->render)
+ ((GnomeCanvasItemClass *) parent_class)->render (item, buf);
+ }
+}
+
+static GSList *gcg_buffers = NULL;
+static GSList *gcg_masks = NULL;
+
+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);
+}