source: trunk/third/libgnomecanvas/libgnomecanvas/gnome-canvas-clipgroup.c @ 20821

Revision 20821, 12.6 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20820, which included commits to RCS files with non-trunk default branches.
Line 
1#define GNOME_CANVAS_CLIPGROUP_C
2
3/* Clipping group for GnomeCanvas
4 *
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.
7 *
8 * Copyright (C) 1998,1999 The Free Software Foundation
9 *
10 * Author:
11 *          Lauris Kaplinski <lauris@ximian.com>
12 */
13
14/* These includes are set up for standalone compile. If/when this codebase
15   is integrated into libgnomeui, the includes will need to change. */
16
17#include <math.h>
18#include <string.h>
19
20#include <gtk/gtkobject.h>
21#include <gtk/gtkwidget.h>
22
23#include <libart_lgpl/art_misc.h>
24#include <libart_lgpl/art_rect.h>
25#include <libart_lgpl/art_vpath.h>
26#include <libart_lgpl/art_bpath.h>
27#include <libart_lgpl/art_vpath.h>
28#include <libart_lgpl/art_vpath_bpath.h>
29#include <libart_lgpl/art_svp.h>
30#include <libart_lgpl/art_svp_vpath.h>
31#include <libart_lgpl/art_rect_svp.h>
32#include <libart_lgpl/art_gray_svp.h>
33#include <libart_lgpl/art_svp_intersect.h>
34#include <libart_lgpl/art_svp_ops.h>
35
36#include "gnome-canvas.h"
37#include "gnome-canvas-util.h"
38#include "gnome-canvas-clipgroup.h"
39
40enum {
41        PROP_0,
42        PROP_PATH,
43        PROP_WIND
44};
45
46static void gnome_canvas_clipgroup_class_init      (GnomeCanvasClipgroupClass *klass);
47static void gnome_canvas_clipgroup_init            (GnomeCanvasClipgroup      *clipgroup);
48static void gnome_canvas_clipgroup_destroy         (GtkObject                 *object);
49static void gnome_canvas_clipgroup_set_property    (GObject                   *object,
50                                                    guint                      param_id,
51                                                    const GValue              *value,
52                                                    GParamSpec                *pspec);
53static void gnome_canvas_clipgroup_get_property    (GObject                   *object,
54                                                    guint                      param_id,
55                                                    GValue                    *value,
56                                                    GParamSpec                *pspec);
57static void gnome_canvas_clipgroup_update          (GnomeCanvasItem           *item,
58                                                    double                    *affine,
59                                                    ArtSVP                    *clip_path,
60                                                    int                        flags);
61
62/*
63 * Generic clipping stuff
64 *
65 * This is somewhat slow and memory-hungry - we add extra
66 * composition, extra SVP render and allocate 65536
67 * bytes for each clip level. It could be done more
68 * efficently per-object basis - but to make clipping
69 * universal, there is no alternative to double
70 * buffering (although it should be done into RGBA
71 * buffer by other method than ::render to make global
72 * opacity possible).
73 * Using art-render could possibly optimize that a bit,
74 * although I am not sure.
75 */
76
77#define GCG_BUF_WIDTH 128
78#define GCG_BUF_HEIGHT 128
79#define GCG_BUF_PIXELS (GCG_BUF_WIDTH * GCG_BUF_HEIGHT)
80#define GCG_BUF_SIZE (GCG_BUF_WIDTH * GCG_BUF_HEIGHT * 3)
81
82#define noSHOW_SHADOW
83
84static guchar *gcg_buf_new (void);
85static void gcg_buf_free (guchar *buf);
86static guchar *gcg_mask_new (void);
87static void gcg_mask_free (guchar *mask);
88
89static void gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
90
91static GnomeCanvasGroupClass *parent_class;
92
93GType
94gnome_canvas_clipgroup_get_type (void)
95{
96        static GType clipgroup_type;
97
98        if (!clipgroup_type) {
99                static const GTypeInfo object_info = {
100                        sizeof (GnomeCanvasClipgroupClass),
101                        (GBaseInitFunc) NULL,
102                        (GBaseFinalizeFunc) NULL,
103                        (GClassInitFunc) gnome_canvas_clipgroup_class_init,
104                        (GClassFinalizeFunc) NULL,
105                        NULL,                   /* class_data */
106                        sizeof (GnomeCanvasClipgroup),
107                        0,                      /* n_preallocs */
108                        (GInstanceInitFunc) gnome_canvas_clipgroup_init,
109                        NULL                    /* value_table */
110                };
111
112                clipgroup_type = g_type_register_static (GNOME_TYPE_CANVAS_GROUP, "GnomeCanvasClipgroup",
113                                                         &object_info, 0);
114        }
115
116        return clipgroup_type;
117}
118
119static void
120gnome_canvas_clipgroup_class_init (GnomeCanvasClipgroupClass *klass)
121{
122        GObjectClass *gobject_class;
123        GtkObjectClass *object_class;
124        GnomeCanvasItemClass *item_class;
125
126        gobject_class = (GObjectClass *) klass;
127        object_class = (GtkObjectClass *) klass;
128        item_class = (GnomeCanvasItemClass *) klass;
129        parent_class = g_type_class_ref (GNOME_TYPE_CANVAS_GROUP);
130
131        object_class->destroy       = gnome_canvas_clipgroup_destroy;
132        gobject_class->set_property = gnome_canvas_clipgroup_set_property;
133        gobject_class->get_property = gnome_canvas_clipgroup_get_property;
134        item_class->update          = gnome_canvas_clipgroup_update;
135        item_class->render          = gnome_canvas_clipgroup_render;
136
137        g_object_class_install_property (gobject_class,
138                                         PROP_PATH,
139                                         g_param_spec_pointer ("path", NULL, NULL,
140                                                               (G_PARAM_READABLE | G_PARAM_WRITABLE)));
141        g_object_class_install_property (gobject_class,
142                                         PROP_WIND,
143                                         g_param_spec_uint ("wind", NULL, NULL,
144                                                            0, G_MAXUINT, 0,
145                                                            (G_PARAM_READABLE | G_PARAM_WRITABLE)));
146}
147
148static void
149gnome_canvas_clipgroup_init (GnomeCanvasClipgroup *clipgroup)
150{
151        clipgroup->path = NULL;
152        clipgroup->wind = ART_WIND_RULE_NONZERO; /* default winding rule */
153        clipgroup->svp = NULL;
154}
155
156static void
157gnome_canvas_clipgroup_destroy (GtkObject *object)
158{
159        GnomeCanvasClipgroup *clipgroup;
160
161        g_return_if_fail (object != NULL);
162        g_return_if_fail (GNOME_IS_CANVAS_CLIPGROUP (object));
163
164        clipgroup = GNOME_CANVAS_CLIPGROUP (object);
165
166        if (clipgroup->path) {
167                gnome_canvas_path_def_unref (clipgroup->path);
168                clipgroup->path = NULL;
169        }
170       
171        if (clipgroup->svp) {
172                art_svp_free (clipgroup->svp);
173                clipgroup->svp = NULL;
174        }
175
176        if (GTK_OBJECT_CLASS (parent_class)->destroy)
177                (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
178}
179
180
181static void
182gnome_canvas_clipgroup_set_property (GObject      *object,
183                                     guint         param_id,
184                                     const GValue *value,
185                                     GParamSpec   *pspec)
186{
187        GnomeCanvasItem *item;
188        GnomeCanvasClipgroup *cgroup;
189        GnomeCanvasPathDef *gpp;
190
191        item = GNOME_CANVAS_ITEM (object);
192        cgroup = GNOME_CANVAS_CLIPGROUP (object);
193
194        switch (param_id) {
195        case PROP_PATH:
196                gpp = g_value_get_pointer (value);
197
198                if (cgroup->path) {
199                        gnome_canvas_path_def_unref (cgroup->path);
200                        cgroup->path = NULL;
201                }
202                if (gpp != NULL) {
203                        cgroup->path = gnome_canvas_path_def_closed_parts (gpp);
204                }
205
206                gnome_canvas_item_request_update (item);
207                break;
208
209        case PROP_WIND:
210                cgroup->wind = g_value_get_uint (value);
211                gnome_canvas_item_request_update (item);
212                break;
213
214        default:
215                break;
216        }
217}
218
219static void
220gnome_canvas_clipgroup_get_property (GObject    *object,
221                                     guint       param_id,
222                                     GValue     *value,
223                                     GParamSpec *pspec)
224{
225        GnomeCanvasClipgroup * cgroup;
226
227        cgroup = GNOME_CANVAS_CLIPGROUP (object);
228
229        switch (param_id) {
230        case PROP_PATH:
231                g_value_set_pointer (value, cgroup->path);
232                break;
233
234        case PROP_WIND:
235                g_value_set_uint (value, cgroup->wind);
236                break;
237
238        default:
239                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
240                break;
241        }
242}
243
244static void
245gnome_canvas_clipgroup_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
246{
247        GnomeCanvasClipgroup *clipgroup;
248        ArtSvpWriter *swr;
249        ArtBpath *bp;
250        ArtBpath *bpath;
251        ArtVpath *vpath;
252        ArtSVP *svp, *svp1, *svp2;
253
254        clipgroup = GNOME_CANVAS_CLIPGROUP (item);
255
256        if (clipgroup->svp) {
257                art_svp_free (clipgroup->svp);
258                clipgroup->svp = NULL;
259        }
260
261        if (clipgroup->path) {
262                bp = gnome_canvas_path_def_bpath (clipgroup->path);
263                bpath = art_bpath_affine_transform (bp, affine);
264
265                vpath = art_bez_path_to_vec (bpath, 0.25);
266                art_free (bpath);
267
268                svp1 = art_svp_from_vpath (vpath);
269                art_free (vpath);
270               
271                swr = art_svp_writer_rewind_new (clipgroup->wind);
272                art_svp_intersector (svp1, swr);
273
274                svp2 = art_svp_writer_rewind_reap (swr);
275                art_svp_free (svp1);
276               
277                if (clip_path != NULL) {
278                        svp = art_svp_intersect (svp2, clip_path);
279                        art_svp_free (svp2);
280                } else {
281                        svp = svp2;
282                }
283
284                clipgroup->svp = svp;
285        }
286
287        if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
288                (GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, NULL, flags);
289
290        if (clipgroup->svp) {
291                ArtDRect cbox;
292                art_drect_svp (&cbox, clipgroup->svp);
293                item->x1 = MAX (item->x1, cbox.x0 - 1.0);
294                item->y1 = MAX (item->y1, cbox.y0 - 1.0);
295                item->x2 = MIN (item->x2, cbox.x1 + 1.0);
296                item->y2 = MIN (item->y2, cbox.y1 + 1.0);
297        }
298}
299
300/* non-premultiplied composition into RGB */
301
302#define COMPOSEN11(fc,fa,bc) (((255 - (guint) (fa)) * (guint) (bc) + (guint) (fc) * (guint) (fa) + 127) / 255)
303
304static void
305gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
306{
307        GnomeCanvasClipgroup *cg;
308        GnomeCanvasBuf lbuf;
309        guchar *mask;
310
311        cg = GNOME_CANVAS_CLIPGROUP (item);
312
313        if (cg->svp) {
314                gint bw, bh, sw, sh;
315                gint x, y;
316
317                /* fixme: We could optimize background handling (lauris) */
318
319                if (buf->is_bg) {
320                        gnome_canvas_buf_ensure_buf (buf);
321                        buf->is_bg = FALSE;
322                        buf->is_buf = TRUE;
323                }
324
325                bw = buf->rect.x1 - buf->rect.x0;
326                bh = buf->rect.y1 - buf->rect.y0;
327                if ((bw < 1) || (bh < 1)) return;
328
329                if (bw * bh <= GCG_BUF_PIXELS) {
330                        /* We can go with single buffer */
331                        sw = bw;
332                        sh = bh;
333                } else if (bw <= (GCG_BUF_PIXELS >> 3)) {
334                        /* Go with row buffer */
335                        sw = bw;
336                        sh =  GCG_BUF_PIXELS / bw;
337                } else if (bh <= (GCG_BUF_PIXELS >> 3)) {
338                        /* Go with column buffer */
339                        sw = GCG_BUF_PIXELS / bh;
340                        sh = bh;
341                } else {
342                        /* Tile buffer */
343                        sw = GCG_BUF_WIDTH;
344                        sh = GCG_BUF_HEIGHT;
345                }
346
347                /* Set up local buffer */
348                lbuf.buf = gcg_buf_new ();
349                lbuf.bg_color = buf->bg_color;
350                lbuf.is_bg = FALSE;
351                lbuf.is_buf = TRUE;
352                /* Allocate mask */
353                mask = gcg_mask_new ();
354
355                for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
356                        for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
357                                gint r, xx, yy;
358                                /* Set up local buffer */
359                                lbuf.rect.x0 = x;
360                                lbuf.rect.y0 = y;
361                                lbuf.rect.x1 = MIN (x + sw, buf->rect.x1);
362                                lbuf.rect.y1 = MIN (y + sh, buf->rect.y1);
363                                lbuf.buf_rowstride = 3 * (lbuf.rect.x1 - lbuf.rect.x0);
364                                /* Copy background */
365                                for (r = lbuf.rect.y0; r < lbuf.rect.y1; r++) {
366                                        memcpy (lbuf.buf + (r - lbuf.rect.y0) * lbuf.buf_rowstride,
367                                                buf->buf + (r - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3,
368                                                (lbuf.rect.x1 - lbuf.rect.x0) * 3);
369                                }
370                                /* Invoke render method */
371                                if (((GnomeCanvasItemClass *) parent_class)->render)
372                                        ((GnomeCanvasItemClass *) parent_class)->render (item, &lbuf);
373                                /* Render mask */
374                                art_gray_svp_aa (cg->svp, lbuf.rect.x0, lbuf.rect.y0, lbuf.rect.x1, lbuf.rect.y1,
375                                                 mask, lbuf.rect.x1 - lbuf.rect.x0);
376                                /* Combine */
377                                for (yy = lbuf.rect.y0; yy < lbuf.rect.y1; yy++) {
378                                        guchar *s, *m, *d;
379                                        s = lbuf.buf + (yy - lbuf.rect.y0) * lbuf.buf_rowstride;
380                                        m = mask + (yy - lbuf.rect.y0) * (lbuf.rect.x1 - lbuf.rect.x0);
381                                        d = buf->buf + (yy - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
382                                        for (xx = lbuf.rect.x0; xx < lbuf.rect.x1; xx++) {
383#ifndef SHOW_SHADOW
384                                                d[0] = COMPOSEN11 (s[0], m[0], d[0]);
385                                                d[1] = COMPOSEN11 (s[1], m[0], d[1]);
386                                                d[2] = COMPOSEN11 (s[2], m[0], d[2]);
387#else
388                                                d[0] = COMPOSEN11 (s[0], m[0] | 0x7f, d[0]);
389                                                d[1] = COMPOSEN11 (s[1], m[0] | 0x7f, d[1]);
390                                                d[2] = COMPOSEN11 (s[2], m[0] | 0x7f, d[2]);
391#endif
392                                                s += 3;
393                                                m += 1;
394                                                d += 3;
395                                        }
396                                }
397                        }
398                }
399                /* Free buffers */
400                gcg_mask_free (mask);
401                gcg_buf_free (lbuf.buf);
402        } else {
403                if (((GnomeCanvasItemClass *) parent_class)->render)
404                        ((GnomeCanvasItemClass *) parent_class)->render (item, buf);
405        }
406}
407
408static GSList *gcg_buffers = NULL;
409static GSList *gcg_masks = NULL;
410
411static guchar *
412gcg_buf_new (void)
413{
414        guchar *buf;
415
416        if (!gcg_buffers) {
417                buf = g_new (guchar, GCG_BUF_SIZE);
418        } else {
419                buf = (guchar *) gcg_buffers->data;
420                gcg_buffers = g_slist_remove (gcg_buffers, buf);
421        }
422
423        return buf;
424}
425
426static void
427gcg_buf_free (guchar *buf)
428{
429        gcg_buffers = g_slist_prepend (gcg_buffers, buf);
430}
431
432static guchar *
433gcg_mask_new (void)
434{
435        guchar *mask;
436
437        if (!gcg_masks) {
438                mask = g_new (guchar, GCG_BUF_PIXELS);
439        } else {
440                mask = (guchar *) gcg_masks->data;
441                gcg_masks = g_slist_remove (gcg_masks, mask);
442        }
443
444        return mask;
445}
446
447static void
448gcg_mask_free (guchar *mask)
449{
450        gcg_masks = g_slist_prepend (gcg_masks, mask);
451}
Note: See TracBrowser for help on using the repository browser.