1 #define __SP_MASK_C__
3 /*
4 * SVG <mask> implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 *
9 * Copyright (C) 2003 authors
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
15 #include "display/nr-arena.h"
16 #include "display/nr-arena-group.h"
17 #include <xml/repr.h>
19 #include "enums.h"
20 #include "attributes.h"
21 #include "document.h"
22 #include "sp-item.h"
24 #include "sp-mask.h"
26 struct SPMaskView {
27 SPMaskView *next;
28 unsigned int key;
29 NRArenaItem *arenaitem;
30 NRRect bbox;
31 };
33 static void sp_mask_class_init (SPMaskClass *klass);
34 static void sp_mask_init (SPMask *mask);
36 static void sp_mask_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
37 static void sp_mask_release (SPObject * object);
38 static void sp_mask_set (SPObject *object, unsigned int key, const gchar *value);
39 static void sp_mask_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
40 static void sp_mask_update (SPObject *object, SPCtx *ctx, guint flags);
41 static void sp_mask_modified (SPObject *object, guint flags);
42 static Inkscape::XML::Node *sp_mask_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
44 SPMaskView *sp_mask_view_new_prepend (SPMaskView *list, unsigned int key, NRArenaItem *arenaitem);
45 SPMaskView *sp_mask_view_list_remove (SPMaskView *list, SPMaskView *view);
47 static SPObjectGroupClass *parent_class;
49 GType
50 sp_mask_get_type (void)
51 {
52 static GType type = 0;
53 if (!type) {
54 GTypeInfo info = {
55 sizeof (SPMaskClass),
56 NULL, NULL,
57 (GClassInitFunc) sp_mask_class_init,
58 NULL, NULL,
59 sizeof (SPMask),
60 16,
61 (GInstanceInitFunc) sp_mask_init,
62 NULL, /* value_table */
63 };
64 type = g_type_register_static (SP_TYPE_OBJECTGROUP, "SPMask", &info, (GTypeFlags)0);
65 }
66 return type;
67 }
69 static void
70 sp_mask_class_init (SPMaskClass *klass)
71 {
72 parent_class = (SPObjectGroupClass*) g_type_class_ref (SP_TYPE_OBJECTGROUP);
74 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
75 sp_object_class->build = sp_mask_build;
76 sp_object_class->release = sp_mask_release;
77 sp_object_class->set = sp_mask_set;
78 sp_object_class->child_added = sp_mask_child_added;
79 sp_object_class->update = sp_mask_update;
80 sp_object_class->modified = sp_mask_modified;
81 sp_object_class->write = sp_mask_write;
82 }
84 static void
85 sp_mask_init (SPMask *mask)
86 {
87 mask->maskUnits_set = FALSE;
88 mask->maskUnits = SP_CONTENT_UNITS_OBJECTBOUNDINGBOX;
90 mask->maskUnits_set = FALSE;
91 mask->maskUnits = SP_CONTENT_UNITS_USERSPACEONUSE;
93 mask->display = NULL;
94 }
96 static void
97 sp_mask_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
98 {
99 if (((SPObjectClass *) parent_class)->build) {
100 ((SPObjectClass *) parent_class)->build (object, document, repr);
101 }
103 sp_object_read_attr (object, "maskUnits");
104 sp_object_read_attr (object, "maskContentUnits");
106 /* Register ourselves */
107 sp_document_add_resource (document, "mask", object);
108 }
110 static void
111 sp_mask_release (SPObject * object)
112 {
113 if (SP_OBJECT_DOCUMENT (object)) {
114 /* Unregister ourselves */
115 sp_document_remove_resource (SP_OBJECT_DOCUMENT (object), "mask", object);
116 }
118 SPMask *cp = SP_MASK (object);
119 while (cp->display) {
120 /* We simply unref and let item to manage this in handler */
121 cp->display = sp_mask_view_list_remove (cp->display, cp->display);
122 }
124 if (((SPObjectClass *) (parent_class))->release) {
125 ((SPObjectClass *) parent_class)->release (object);
126 }
127 }
129 static void
130 sp_mask_set (SPObject *object, unsigned int key, const gchar *value)
131 {
132 SPMask *mask = SP_MASK (object);
134 switch (key) {
135 case SP_ATTR_MASKUNITS:
136 mask->maskUnits = SP_CONTENT_UNITS_OBJECTBOUNDINGBOX;
137 mask->maskUnits_set = FALSE;
138 if (value) {
139 if (!strcmp (value, "userSpaceOnUse")) {
140 mask->maskUnits = SP_CONTENT_UNITS_USERSPACEONUSE;
141 mask->maskUnits_set = TRUE;
142 } else if (!strcmp (value, "objectBoundingBox")) {
143 mask->maskUnits_set = TRUE;
144 }
145 }
146 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
147 break;
148 case SP_ATTR_MASKCONTENTUNITS:
149 mask->maskContentUnits = SP_CONTENT_UNITS_USERSPACEONUSE;
150 mask->maskContentUnits_set = FALSE;
151 if (value) {
152 if (!strcmp (value, "userSpaceOnUse")) {
153 mask->maskContentUnits_set = TRUE;
154 } else if (!strcmp (value, "objectBoundingBox")) {
155 mask->maskContentUnits = SP_CONTENT_UNITS_OBJECTBOUNDINGBOX;
156 mask->maskContentUnits_set = TRUE;
157 }
158 }
159 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
160 break;
161 default:
162 if (((SPObjectClass *) parent_class)->set)
163 ((SPObjectClass *) parent_class)->set (object, key, value);
164 break;
165 }
166 }
168 static void
169 sp_mask_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
170 {
171 /* Invoke SPObjectGroup implementation */
172 ((SPObjectClass *) (parent_class))->child_added (object, child, ref);
174 /* Show new object */
175 SPObject *ochild = SP_OBJECT_DOCUMENT (object)->getObjectByRepr(child);
176 if (SP_IS_ITEM (ochild)) {
177 SPMask *cp = SP_MASK (object);
178 for (SPMaskView *v = cp->display; v != NULL; v = v->next) {
179 NRArenaItem *ac = sp_item_invoke_show (SP_ITEM (ochild),
180 NR_ARENA_ITEM_ARENA (v->arenaitem),
181 v->key,
182 SP_ITEM_REFERENCE_FLAGS);
183 if (ac) {
184 nr_arena_item_add_child (v->arenaitem, ac, NULL);
185 nr_arena_item_unref (ac);
186 }
187 }
188 }
189 }
191 static void
192 sp_mask_update (SPObject *object, SPCtx *ctx, guint flags)
193 {
194 if (flags & SP_OBJECT_MODIFIED_FLAG) {
195 flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
196 }
198 flags &= SP_OBJECT_MODIFIED_CASCADE;
200 SPObjectGroup *og = SP_OBJECTGROUP (object);
201 GSList *l = NULL;
202 for (SPObject *child = sp_object_first_child(SP_OBJECT(og)); child != NULL; child = SP_OBJECT_NEXT(child)) {
203 g_object_ref (G_OBJECT (child));
204 l = g_slist_prepend (l, child);
205 }
206 l = g_slist_reverse (l);
207 while (l) {
208 SPObject *child = SP_OBJECT (l->data);
209 l = g_slist_remove (l, child);
210 if (flags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
211 child->updateDisplay(ctx, flags);
212 }
213 g_object_unref (G_OBJECT (child));
214 }
216 SPMask *mask = SP_MASK (object);
217 for (SPMaskView *v = mask->display; v != NULL; v = v->next) {
218 if (mask->maskContentUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX) {
219 NRMatrix t;
220 nr_matrix_set_scale (&t, v->bbox.x1 - v->bbox.x0, v->bbox.y1 - v->bbox.y0);
221 t.c[4] = v->bbox.x0;
222 t.c[5] = v->bbox.y0;
223 nr_arena_group_set_child_transform (NR_ARENA_GROUP (v->arenaitem), &t);
224 } else {
225 nr_arena_group_set_child_transform (NR_ARENA_GROUP (v->arenaitem), NULL);
226 }
227 }
228 }
230 static void
231 sp_mask_modified (SPObject *object, guint flags)
232 {
233 if (flags & SP_OBJECT_MODIFIED_FLAG) {
234 flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
235 }
237 flags &= SP_OBJECT_MODIFIED_CASCADE;
239 SPObjectGroup *og = SP_OBJECTGROUP (object);
240 GSList *l = NULL;
241 for (SPObject *child = sp_object_first_child(SP_OBJECT(og)); child != NULL; child = SP_OBJECT_NEXT(child)) {
242 g_object_ref (G_OBJECT (child));
243 l = g_slist_prepend (l, child);
244 }
245 l = g_slist_reverse (l);
246 while (l) {
247 SPObject *child = SP_OBJECT (l->data);
248 l = g_slist_remove (l, child);
249 if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
250 child->emitModified(flags);
251 }
252 g_object_unref (G_OBJECT (child));
253 }
254 }
256 static Inkscape::XML::Node *
257 sp_mask_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
258 {
259 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
260 repr = sp_repr_new ("svg:mask");
261 }
263 if (((SPObjectClass *) (parent_class))->write)
264 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
266 return repr;
267 }
269 NRArenaItem *
270 sp_mask_show (SPMask *mask, NRArena *arena, unsigned int key)
271 {
272 g_return_val_if_fail (mask != NULL, NULL);
273 g_return_val_if_fail (SP_IS_MASK (mask), NULL);
274 g_return_val_if_fail (arena != NULL, NULL);
275 g_return_val_if_fail (NR_IS_ARENA (arena), NULL);
277 NRArenaItem *ai = NRArenaGroup::create(arena);
278 mask->display = sp_mask_view_new_prepend (mask->display, key, ai);
280 for (SPObject *child = sp_object_first_child(SP_OBJECT(mask)) ; child != NULL; child = SP_OBJECT_NEXT(child)) {
281 if (SP_IS_ITEM (child)) {
282 NRArenaItem *ac = sp_item_invoke_show (SP_ITEM (child), arena, key, SP_ITEM_REFERENCE_FLAGS);
283 if (ac) {
284 /* The order is not important in mask */
285 nr_arena_item_add_child (ai, ac, NULL);
286 nr_arena_item_unref (ac);
287 }
288 }
289 }
291 if (mask->maskContentUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX) {
292 NRMatrix t;
293 nr_matrix_set_scale (&t, mask->display->bbox.x1 - mask->display->bbox.x0, mask->display->bbox.y1 - mask->display->bbox.y0);
294 t.c[4] = mask->display->bbox.x0;
295 t.c[5] = mask->display->bbox.y0;
296 nr_arena_group_set_child_transform (NR_ARENA_GROUP (ai), &t);
297 }
299 return ai;
300 }
302 void
303 sp_mask_hide (SPMask *cp, unsigned int key)
304 {
305 g_return_if_fail (cp != NULL);
306 g_return_if_fail (SP_IS_MASK (cp));
308 for (SPObject *child = sp_object_first_child(SP_OBJECT(cp)); child != NULL; child = SP_OBJECT_NEXT(child)) {
309 if (SP_IS_ITEM (child)) {
310 sp_item_invoke_hide (SP_ITEM (child), key);
311 }
312 }
314 for (SPMaskView *v = cp->display; v != NULL; v = v->next) {
315 if (v->key == key) {
316 /* We simply unref and let item to manage this in handler */
317 cp->display = sp_mask_view_list_remove (cp->display, v);
318 return;
319 }
320 }
322 g_assert_not_reached ();
323 }
325 void
326 sp_mask_set_bbox (SPMask *mask, unsigned int key, NRRect *bbox)
327 {
328 for (SPMaskView *v = mask->display; v != NULL; v = v->next) {
329 if (v->key == key) {
330 if (!NR_DF_TEST_CLOSE (v->bbox.x0, bbox->x0, NR_EPSILON) ||
331 !NR_DF_TEST_CLOSE (v->bbox.y0, bbox->y0, NR_EPSILON) ||
332 !NR_DF_TEST_CLOSE (v->bbox.x1, bbox->x1, NR_EPSILON) ||
333 !NR_DF_TEST_CLOSE (v->bbox.y1, bbox->y1, NR_EPSILON)) {
334 v->bbox = *bbox;
335 SP_OBJECT(mask)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
336 }
337 break;
338 }
339 }
340 }
342 /* Mask views */
344 SPMaskView *
345 sp_mask_view_new_prepend (SPMaskView *list, unsigned int key, NRArenaItem *arenaitem)
346 {
347 SPMaskView *new_mask_view = g_new (SPMaskView, 1);
349 new_mask_view->next = list;
350 new_mask_view->key = key;
351 new_mask_view->arenaitem = nr_arena_item_ref(arenaitem);
352 new_mask_view->bbox.x0 = new_mask_view->bbox.x1 = 0.0;
353 new_mask_view->bbox.y0 = new_mask_view->bbox.y1 = 0.0;
355 return new_mask_view;
356 }
358 SPMaskView *
359 sp_mask_view_list_remove (SPMaskView *list, SPMaskView *view)
360 {
361 if (view == list) {
362 list = list->next;
363 } else {
364 SPMaskView *prev;
365 prev = list;
366 while (prev->next != view) prev = prev->next;
367 prev->next = view->next;
368 }
370 nr_arena_item_unref (view->arenaitem);
371 g_free (view);
373 return list;
374 }