1 #define GNOME_CANVAS_CLIPGROUP_C
3 /* Clipping group for GnomeCanvas
5 * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
6 * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
8 * Copyright (C) 1998,1999 The Free Software Foundation
11 * Lauris Kaplinski <lauris@ximian.com>
14 /* These includes are set up for standalone compile. If/when this codebase
15 is integrated into libgnomeui, the includes will need to change. */
22 #include <libart_lgpl/art_misc.h>
23 #include <libart_lgpl/art_rect.h>
24 #include <libart_lgpl/art_vpath.h>
25 #include <libart_lgpl/art_bpath.h>
26 #include <libart_lgpl/art_vpath.h>
27 #include <libart_lgpl/art_vpath_bpath.h>
28 #include <libart_lgpl/art_svp.h>
29 #include <libart_lgpl/art_svp_vpath.h>
30 #include <libart_lgpl/art_rect_svp.h>
31 #include <libart_lgpl/art_gray_svp.h>
32 #include <libart_lgpl/art_svp_intersect.h>
33 #include <libart_lgpl/art_svp_ops.h>
35 #include "gnome-canvas.h"
36 #include "gnome-canvas-util.h"
37 #include "gnome-canvas-clipgroup.h"
45 static void gnome_canvas_clipgroup_class_init (GnomeCanvasClipgroupClass *klass);
46 static void gnome_canvas_clipgroup_init (GnomeCanvasClipgroup *clipgroup);
47 static void gnome_canvas_clipgroup_destroy (GtkObject *object);
48 static void gnome_canvas_clipgroup_set_property (GObject *object,
52 static void gnome_canvas_clipgroup_get_property (GObject *object,
56 static void gnome_canvas_clipgroup_update (GnomeCanvasItem *item,
62 * Generic clipping stuff
64 * This is somewhat slow and memory-hungry - we add extra
65 * composition, extra SVP render and allocate 65536
66 * bytes for each clip level. It could be done more
67 * efficently per-object basis - but to make clipping
68 * universal, there is no alternative to double
69 * buffering (although it should be done into RGBA
70 * buffer by other method than ::render to make global
72 * Using art-render could possibly optimize that a bit,
73 * although I am not sure.
76 #define GCG_BUF_WIDTH 128
77 #define GCG_BUF_HEIGHT 128
78 #define GCG_BUF_PIXELS (GCG_BUF_WIDTH * GCG_BUF_HEIGHT)
79 #define GCG_BUF_SIZE (GCG_BUF_WIDTH * GCG_BUF_HEIGHT * 3)
83 static guchar *gcg_buf_new (void);
84 static void gcg_buf_free (guchar *buf);
85 static guchar *gcg_mask_new (void);
86 static void gcg_mask_free (guchar *mask);
88 static void gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
90 static GnomeCanvasGroupClass *parent_class;
93 gnome_canvas_clipgroup_get_type (void)
95 static GType clipgroup_type;
97 if (!clipgroup_type) {
98 const GTypeInfo object_info = {
99 sizeof (GnomeCanvasClipgroupClass),
100 (GBaseInitFunc) NULL,
101 (GBaseFinalizeFunc) NULL,
102 (GClassInitFunc) gnome_canvas_clipgroup_class_init,
103 (GClassFinalizeFunc) NULL,
104 NULL, /* class_data */
105 sizeof (GnomeCanvasClipgroup),
107 (GInstanceInitFunc) gnome_canvas_clipgroup_init,
108 NULL /* value_table */
111 clipgroup_type = g_type_register_static (GNOME_TYPE_CANVAS_GROUP, "GnomeCanvasClipgroup",
115 return clipgroup_type;
119 gnome_canvas_clipgroup_class_init (GnomeCanvasClipgroupClass *klass)
121 GObjectClass *gobject_class;
122 GtkObjectClass *object_class;
123 GnomeCanvasItemClass *item_class;
125 gobject_class = (GObjectClass *) klass;
126 object_class = (GtkObjectClass *) klass;
127 item_class = (GnomeCanvasItemClass *) klass;
128 parent_class = g_type_class_ref (GNOME_TYPE_CANVAS_GROUP);
130 object_class->destroy = gnome_canvas_clipgroup_destroy;
131 gobject_class->set_property = gnome_canvas_clipgroup_set_property;
132 gobject_class->get_property = gnome_canvas_clipgroup_get_property;
133 item_class->update = gnome_canvas_clipgroup_update;
134 item_class->render = gnome_canvas_clipgroup_render;
136 g_object_class_install_property (gobject_class,
138 g_param_spec_pointer ("path", NULL, NULL,
139 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
140 g_object_class_install_property (gobject_class,
142 g_param_spec_uint ("wind", NULL, NULL,
144 (G_PARAM_READABLE | G_PARAM_WRITABLE)));
148 gnome_canvas_clipgroup_init (GnomeCanvasClipgroup *clipgroup)
150 clipgroup->path = NULL;
151 clipgroup->wind = ART_WIND_RULE_NONZERO; /* default winding rule */
152 clipgroup->svp = NULL;
156 gnome_canvas_clipgroup_destroy (GtkObject *object)
158 GnomeCanvasClipgroup *clipgroup;
160 g_return_if_fail (object != NULL);
161 g_return_if_fail (GNOME_IS_CANVAS_CLIPGROUP (object));
163 clipgroup = GNOME_CANVAS_CLIPGROUP (object);
165 if (clipgroup->path) {
166 gnome_canvas_path_def_unref (clipgroup->path);
167 clipgroup->path = NULL;
170 if (clipgroup->svp) {
171 art_svp_free (clipgroup->svp);
172 clipgroup->svp = NULL;
175 if (GTK_OBJECT_CLASS (parent_class)->destroy)
176 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
181 gnome_canvas_clipgroup_set_property (GObject *object,
186 GnomeCanvasItem *item;
187 GnomeCanvasClipgroup *cgroup;
188 GnomeCanvasPathDef *gpp;
190 item = GNOME_CANVAS_ITEM (object);
191 cgroup = GNOME_CANVAS_CLIPGROUP (object);
195 gpp = g_value_get_pointer (value);
198 gnome_canvas_path_def_unref (cgroup->path);
202 cgroup->path = gnome_canvas_path_def_closed_parts (gpp);
205 gnome_canvas_item_request_update (item);
209 cgroup->wind = g_value_get_uint (value);
210 gnome_canvas_item_request_update (item);
219 gnome_canvas_clipgroup_get_property (GObject *object,
224 GnomeCanvasClipgroup * cgroup;
226 cgroup = GNOME_CANVAS_CLIPGROUP (object);
230 g_value_set_pointer (value, cgroup->path);
234 g_value_set_uint (value, cgroup->wind);
238 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
244 gnome_canvas_clipgroup_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
246 GnomeCanvasClipgroup *clipgroup;
251 ArtSVP *svp, *svp1, *svp2;
253 clipgroup = GNOME_CANVAS_CLIPGROUP (item);
255 if (clipgroup->svp) {
256 art_svp_free (clipgroup->svp);
257 clipgroup->svp = NULL;
260 if (clipgroup->path) {
261 bp = gnome_canvas_path_def_bpath (clipgroup->path);
262 bpath = art_bpath_affine_transform (bp, affine);
264 vpath = art_bez_path_to_vec (bpath, 0.25);
267 svp1 = art_svp_from_vpath (vpath);
270 swr = art_svp_writer_rewind_new (clipgroup->wind);
271 art_svp_intersector (svp1, swr);
273 svp2 = art_svp_writer_rewind_reap (swr);
276 if (clip_path != NULL) {
277 svp = art_svp_intersect (svp2, clip_path);
283 clipgroup->svp = svp;
286 if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
287 (GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, NULL, flags);
289 if (clipgroup->svp) {
291 art_drect_svp (&cbox, clipgroup->svp);
292 item->x1 = MAX (item->x1, cbox.x0 - 1.0);
293 item->y1 = MAX (item->y1, cbox.y0 - 1.0);
294 item->x2 = MIN (item->x2, cbox.x1 + 1.0);
295 item->y2 = MIN (item->y2, cbox.y1 + 1.0);
299 /* non-premultiplied composition into RGB */
301 #define COMPOSEN11(fc,fa,bc) (((255 - (guint) (fa)) * (guint) (bc) + (guint) (fc) * (guint) (fa) + 127) / 255)
304 gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
306 GnomeCanvasClipgroup *cg;
310 cg = GNOME_CANVAS_CLIPGROUP (item);
316 /* fixme: We could optimize background handling (lauris) */
319 gnome_canvas_buf_ensure_buf (buf);
324 bw = buf->rect.x1 - buf->rect.x0;
325 bh = buf->rect.y1 - buf->rect.y0;
326 if ((bw < 1) || (bh < 1)) return;
328 if (bw * bh <= GCG_BUF_PIXELS) {
329 /* We can go with single buffer */
332 } else if (bw <= (GCG_BUF_PIXELS >> 3)) {
333 /* Go with row buffer */
335 sh = GCG_BUF_PIXELS / bw;
336 } else if (bh <= (GCG_BUF_PIXELS >> 3)) {
337 /* Go with column buffer */
338 sw = GCG_BUF_PIXELS / bh;
346 /* Set up local buffer */
347 lbuf.buf = gcg_buf_new ();
348 lbuf.bg_color = buf->bg_color;
352 mask = gcg_mask_new ();
354 for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
355 for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
357 /* Set up local buffer */
360 lbuf.rect.x1 = MIN (x + sw, buf->rect.x1);
361 lbuf.rect.y1 = MIN (y + sh, buf->rect.y1);
362 lbuf.buf_rowstride = 3 * (lbuf.rect.x1 - lbuf.rect.x0);
363 /* Copy background */
364 for (r = lbuf.rect.y0; r < lbuf.rect.y1; r++) {
365 memcpy (lbuf.buf + (r - lbuf.rect.y0) * lbuf.buf_rowstride,
366 buf->buf + (r - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3,
367 (lbuf.rect.x1 - lbuf.rect.x0) * 3);
369 /* Invoke render method */
370 if (((GnomeCanvasItemClass *) parent_class)->render)
371 ((GnomeCanvasItemClass *) parent_class)->render (item, &lbuf);
373 art_gray_svp_aa (cg->svp, lbuf.rect.x0, lbuf.rect.y0, lbuf.rect.x1, lbuf.rect.y1,
374 mask, lbuf.rect.x1 - lbuf.rect.x0);
376 for (yy = lbuf.rect.y0; yy < lbuf.rect.y1; yy++) {
378 s = lbuf.buf + (yy - lbuf.rect.y0) * lbuf.buf_rowstride;
379 m = mask + (yy - lbuf.rect.y0) * (lbuf.rect.x1 - lbuf.rect.x0);
380 d = buf->buf + (yy - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
381 for (xx = lbuf.rect.x0; xx < lbuf.rect.x1; xx++) {
383 d[0] = COMPOSEN11 (s[0], m[0], d[0]);
384 d[1] = COMPOSEN11 (s[1], m[0], d[1]);
385 d[2] = COMPOSEN11 (s[2], m[0], d[2]);
387 d[0] = COMPOSEN11 (s[0], m[0] | 0x7f, d[0]);
388 d[1] = COMPOSEN11 (s[1], m[0] | 0x7f, d[1]);
389 d[2] = COMPOSEN11 (s[2], m[0] | 0x7f, d[2]);
399 gcg_mask_free (mask);
400 gcg_buf_free (lbuf.buf);
402 if (((GnomeCanvasItemClass *) parent_class)->render)
403 ((GnomeCanvasItemClass *) parent_class)->render (item, buf);
407 static GSList *gcg_buffers = NULL;
408 static GSList *gcg_masks = NULL;
416 buf = g_new (guchar, GCG_BUF_SIZE);
418 buf = (guchar *) gcg_buffers->data;
419 gcg_buffers = g_slist_remove (gcg_buffers, buf);
426 gcg_buf_free (guchar *buf)
428 gcg_buffers = g_slist_prepend (gcg_buffers, buf);
437 mask = g_new (guchar, GCG_BUF_PIXELS);
439 mask = (guchar *) gcg_masks->data;
440 gcg_masks = g_slist_remove (gcg_masks, mask);
447 gcg_mask_free (guchar *mask)
449 gcg_masks = g_slist_prepend (gcg_masks, mask);