~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/display/nr-arena-item.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __NR_ARENA_ITEM_C__
 
2
 
 
3
/*
 
4
 * RGBA display list system for inkscape
 
5
 *
 
6
 * Author:
 
7
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
8
 *
 
9
 * Copyright (C) 2001-2002 Lauris Kaplinski
 
10
 * Copyright (C) 2001 Ximian, Inc.
 
11
 *
 
12
 * Released under GNU GPL, read the file 'COPYING' for more information
 
13
 */
 
14
 
 
15
#define noNR_ARENA_ITEM_VERBOSE
 
16
#define noNR_ARENA_ITEM_DEBUG_CASCADE
 
17
 
 
18
#include <cstring>
 
19
#include <string>
 
20
 
 
21
#include <libnr/nr-blit.h>
 
22
#include <libnr/nr-pixops.h>
 
23
#include "nr-arena.h"
 
24
#include "nr-arena-item.h"
 
25
#include "gc-core.h"
 
26
#include "helper/geom.h"
 
27
 
 
28
#include "nr-filter.h"
 
29
#include "nr-arena-group.h"
 
30
#include "preferences.h"
 
31
 
 
32
namespace GC = Inkscape::GC;
 
33
 
 
34
static void nr_arena_item_class_init (NRArenaItemClass *klass);
 
35
static void nr_arena_item_init (NRArenaItem *item);
 
36
static void nr_arena_item_private_finalize (NRObject *object);
 
37
 
 
38
static NRObjectClass *parent_class;
 
39
 
 
40
NRType 
 
41
nr_arena_item_get_type (void)
 
42
{
 
43
    static NRType type = 0;
 
44
    if (!type) {
 
45
        type = nr_object_register_type (NR_TYPE_OBJECT,
 
46
                                        "NRArenaItem",
 
47
                                        sizeof (NRArenaItemClass),
 
48
                                        sizeof (NRArenaItem),
 
49
                                        (void (*)(NRObjectClass *))
 
50
                                        nr_arena_item_class_init,
 
51
                                        (void (*)(NRObject *))
 
52
                                        nr_arena_item_init);
 
53
    }
 
54
    return type;
 
55
}
 
56
 
 
57
static void
 
58
nr_arena_item_class_init (NRArenaItemClass *klass)
 
59
{
 
60
    NRObjectClass *object_class;
 
61
 
 
62
    object_class = (NRObjectClass *) klass;
 
63
 
 
64
    parent_class = ((NRObjectClass *) klass)->parent;
 
65
 
 
66
    object_class->finalize = nr_arena_item_private_finalize;
 
67
    object_class->cpp_ctor = NRObject::invoke_ctor < NRArenaItem >;
 
68
}
 
69
 
 
70
static void
 
71
nr_arena_item_init (NRArenaItem *item)
 
72
{
 
73
    item->arena = NULL;
 
74
    item->parent = NULL;
 
75
    item->next = item->prev = NULL;
 
76
 
 
77
    item->key = 0;
 
78
 
 
79
    item->state = 0;
 
80
    item->sensitive = TRUE;
 
81
    item->visible = TRUE;
 
82
 
 
83
    memset (&item->bbox, 0, sizeof (item->bbox));
 
84
    memset (&item->drawbox, 0, sizeof (item->drawbox));
 
85
    item->transform = NULL;
 
86
    item->opacity = 255;
 
87
    item->render_opacity = FALSE;
 
88
 
 
89
    item->transform = NULL;
 
90
    item->clip = NULL;
 
91
    item->mask = NULL;
 
92
    item->px = NULL;
 
93
    item->data = NULL;
 
94
    item->filter = NULL;
 
95
    item->background_pb = NULL;
 
96
    item->background_new = false;
 
97
}
 
98
 
 
99
static void
 
100
nr_arena_item_private_finalize (NRObject *object)
 
101
{
 
102
    NRArenaItem *item = static_cast < NRArenaItem * >(object);
 
103
 
 
104
    item->px = NULL;
 
105
    item->transform = NULL;
 
106
 
 
107
    ((NRObjectClass *) (parent_class))->finalize (object);
 
108
}
 
109
 
 
110
NRArenaItem *
 
111
nr_arena_item_children (NRArenaItem *item)
 
112
{
 
113
    nr_return_val_if_fail (item != NULL, NULL);
 
114
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
 
115
 
 
116
    if (NR_ARENA_ITEM_VIRTUAL (item, children))
 
117
        return NR_ARENA_ITEM_VIRTUAL (item, children) (item);
 
118
 
 
119
    return NULL;
 
120
}
 
121
 
 
122
NRArenaItem *
 
123
nr_arena_item_last_child (NRArenaItem *item)
 
124
{
 
125
    nr_return_val_if_fail (item != NULL, NULL);
 
126
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
 
127
 
 
128
    if (NR_ARENA_ITEM_VIRTUAL (item, last_child)) {
 
129
        return NR_ARENA_ITEM_VIRTUAL (item, last_child) (item);
 
130
    } else {
 
131
        NRArenaItem *ref = nr_arena_item_children (item);
 
132
        if (ref)
 
133
            while (ref->next)
 
134
                ref = ref->next;
 
135
        return ref;
 
136
    }
 
137
}
 
138
 
 
139
void
 
140
nr_arena_item_add_child (NRArenaItem *item, NRArenaItem *child,
 
141
                         NRArenaItem *ref)
 
142
{
 
143
    nr_return_if_fail (item != NULL);
 
144
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
145
    nr_return_if_fail (child != NULL);
 
146
    nr_return_if_fail (NR_IS_ARENA_ITEM (child));
 
147
    nr_return_if_fail (child->parent == NULL);
 
148
    nr_return_if_fail (child->prev == NULL);
 
149
    nr_return_if_fail (child->next == NULL);
 
150
    nr_return_if_fail (child->arena == item->arena);
 
151
    nr_return_if_fail (child != ref);
 
152
    nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
 
153
    nr_return_if_fail (!ref || (ref->parent == item));
 
154
 
 
155
    if (NR_ARENA_ITEM_VIRTUAL (item, add_child))
 
156
        NR_ARENA_ITEM_VIRTUAL (item, add_child) (item, child, ref);
 
157
}
 
158
 
 
159
void
 
160
nr_arena_item_remove_child (NRArenaItem *item, NRArenaItem *child)
 
161
{
 
162
    nr_return_if_fail (item != NULL);
 
163
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
164
    nr_return_if_fail (child != NULL);
 
165
    nr_return_if_fail (NR_IS_ARENA_ITEM (child));
 
166
    nr_return_if_fail (child->parent == item);
 
167
 
 
168
    if (NR_ARENA_ITEM_VIRTUAL (item, remove_child))
 
169
        NR_ARENA_ITEM_VIRTUAL (item, remove_child) (item, child);
 
170
}
 
171
 
 
172
void
 
173
nr_arena_item_set_child_position (NRArenaItem *item, NRArenaItem *child,
 
174
                                  NRArenaItem *ref)
 
175
{
 
176
    nr_return_if_fail (item != NULL);
 
177
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
178
    nr_return_if_fail (child != NULL);
 
179
    nr_return_if_fail (NR_IS_ARENA_ITEM (child));
 
180
    nr_return_if_fail (child->parent == item);
 
181
    nr_return_if_fail (!ref || NR_IS_ARENA_ITEM (ref));
 
182
    nr_return_if_fail (!ref || (ref->parent == item));
 
183
 
 
184
    if (NR_ARENA_ITEM_VIRTUAL (item, set_child_position))
 
185
        NR_ARENA_ITEM_VIRTUAL (item, set_child_position) (item, child, ref);
 
186
}
 
187
 
 
188
NRArenaItem *
 
189
nr_arena_item_ref (NRArenaItem *item)
 
190
{
 
191
    nr_object_ref ((NRObject *) item);
 
192
 
 
193
    return item;
 
194
}
 
195
 
 
196
NRArenaItem *
 
197
nr_arena_item_unref (NRArenaItem *item)
 
198
{
 
199
    nr_object_unref ((NRObject *) item);
 
200
 
 
201
    return NULL;
 
202
}
 
203
 
 
204
unsigned int
 
205
nr_arena_item_invoke_update (NRArenaItem *item, NRRectL *area, NRGC *gc,
 
206
                             unsigned int state, unsigned int reset)
 
207
{
 
208
    NRGC childgc (gc);
 
209
    bool filter = (item->arena->rendermode == Inkscape::RENDERMODE_NORMAL);
 
210
 
 
211
    nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
 
212
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (item),
 
213
                           NR_ARENA_ITEM_STATE_INVALID);
 
214
    nr_return_val_if_fail (!(state & NR_ARENA_ITEM_STATE_INVALID),
 
215
                           NR_ARENA_ITEM_STATE_INVALID);
 
216
 
 
217
#ifdef NR_ARENA_ITEM_DEBUG_CASCADE
 
218
    printf ("Update %s:%p %x %x %x\n",
 
219
            nr_type_name_from_instance ((GTypeInstance *) item), item, state,
 
220
            item->state, reset);
 
221
#endif
 
222
 
 
223
    /* return if in error */
 
224
    if (item->state & NR_ARENA_ITEM_STATE_INVALID)
 
225
        return item->state;
 
226
    /* Set reset flags according to propagation status */
 
227
    if (item->propagate) {
 
228
        reset |= ~item->state;
 
229
        item->propagate = FALSE;
 
230
    }
 
231
    /* Reset our state */
 
232
    item->state &= ~reset;
 
233
    /* Return if NOP */
 
234
    if (!(~item->state & state))
 
235
        return item->state;
 
236
    /* Test whether to return immediately */
 
237
    if (area && (item->state & NR_ARENA_ITEM_STATE_BBOX)) {
 
238
        if (!nr_rect_l_test_intersect_ptr(area, &item->drawbox))
 
239
            return item->state;
 
240
    }
 
241
 
 
242
    /* Reset image cache, if not to be kept */
 
243
    if (!(item->state & NR_ARENA_ITEM_STATE_IMAGE) && (item->px)) {
 
244
        item->px = NULL;
 
245
    }
 
246
 
 
247
    /* Set up local gc */
 
248
    childgc = *gc;
 
249
    if (item->transform) {
 
250
        childgc.transform = (*item->transform) * childgc.transform;
 
251
    }
 
252
    /* Remember the transformation matrix */
 
253
    item->ctm = childgc.transform;
 
254
 
 
255
    /* Invoke the real method */
 
256
    // that will update bbox
 
257
    item->state = NR_ARENA_ITEM_VIRTUAL (item, update) (item, area, &childgc, state, reset);
 
258
    if (item->state & NR_ARENA_ITEM_STATE_INVALID)
 
259
        return item->state;
 
260
 
 
261
    // get a copy of bbox
 
262
    memcpy(&item->drawbox, &item->bbox, sizeof(item->bbox));
 
263
 
 
264
    /* Enlarge the drawbox to contain filter effects */
 
265
    if (item->filter && filter) {
 
266
        item->filter->bbox_enlarge (item->drawbox);
 
267
    }
 
268
    // fixme: to fix the display glitches, in outline mode bbox must be a combination of 
 
269
    // full item bbox and its clip and mask (after we have the API to get these)
 
270
 
 
271
    /* Clipping */
 
272
    if (item->clip) {
 
273
        // FIXME: since here we only need bbox, consider passing 
 
274
        // ((state & !(NR_ARENA_ITEM_STATE_RENDER)) | NR_ARENA_ITEM_STATE_BBOX)
 
275
        // instead of state, so it does not have to create rendering structures in nr_arena_shape_update
 
276
        unsigned int newstate = nr_arena_item_invoke_update (item->clip, area, &childgc, state, reset); 
 
277
        if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
 
278
            item->state |= NR_ARENA_ITEM_STATE_INVALID;
 
279
            return item->state;
 
280
        }
 
281
        // for clipping, we need geometric bbox
 
282
        nr_rect_l_intersect (&item->drawbox, &item->drawbox, &item->clip->bbox);
 
283
    }
 
284
    /* Masking */
 
285
    if (item->mask) {
 
286
        unsigned int newstate = nr_arena_item_invoke_update (item->mask, area, &childgc, state, reset);
 
287
        if (newstate & NR_ARENA_ITEM_STATE_INVALID) {
 
288
            item->state |= NR_ARENA_ITEM_STATE_INVALID;
 
289
            return item->state;
 
290
        }
 
291
        // for masking, we need full drawbox of mask
 
292
        nr_rect_l_intersect (&item->drawbox, &item->drawbox, &item->mask->drawbox);
 
293
    }
 
294
 
 
295
    // now that we know drawbox, dirty the corresponding rect on canvas:
 
296
    if (!NR_IS_ARENA_GROUP(item) || (item->filter && filter)) {
 
297
        // unless filtered, groups do not need to render by themselves, only their members
 
298
        nr_arena_item_request_render (item);
 
299
    }
 
300
 
 
301
    return item->state;
 
302
}
 
303
 
 
304
/**
 
305
 *    Render item to pixblock.
 
306
 *
 
307
 *    \return Has NR_ARENA_ITEM_STATE_RENDER set on success.
 
308
 */
 
309
 
 
310
unsigned int
 
311
nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area,
 
312
                             NRPixBlock *pb, unsigned int flags)
 
313
{
 
314
   bool outline = (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE);
 
315
    bool filter = (item->arena->rendermode == Inkscape::RENDERMODE_NORMAL);
 
316
 
 
317
    nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
 
318
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (item),
 
319
                           NR_ARENA_ITEM_STATE_INVALID);
 
320
    nr_return_val_if_fail (item->state & NR_ARENA_ITEM_STATE_BBOX,
 
321
                           item->state);
 
322
 
 
323
#ifdef NR_ARENA_ITEM_VERBOSE
 
324
    printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0,
 
325
            area->x1, area->y1);
 
326
#endif
 
327
 
 
328
    /* If we are invisible, just return successfully */
 
329
    if (!item->visible)
 
330
        return item->state | NR_ARENA_ITEM_STATE_RENDER;
 
331
 
 
332
    NRRectL carea;
 
333
    nr_rect_l_intersect (&carea, area, &item->drawbox);
 
334
    if (nr_rect_l_test_empty(carea))
 
335
        return item->state | NR_ARENA_ITEM_STATE_RENDER;
 
336
    if (item->filter && filter) {
 
337
        item->filter->area_enlarge (carea, item);
 
338
        nr_rect_l_intersect (&carea, &carea, &item->drawbox);
 
339
    }
 
340
 
 
341
    if (outline) {
 
342
    // No caching in outline mode for now; investigate if it really gives any advantage with cairo.
 
343
    // Also no attempts to clip anything; just render everything: item, clip, mask   
 
344
            // First, render the object itself 
 
345
            unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, &carea, pb, flags);
 
346
            if (state & NR_ARENA_ITEM_STATE_INVALID) {
 
347
                /* Clean up and return error */
 
348
                item->state |= NR_ARENA_ITEM_STATE_INVALID;
 
349
                return item->state;
 
350
            }
 
351
 
 
352
            // render clip and mask, if any
 
353
            guint32 saved_rgba = item->arena->outlinecolor; // save current outline color
 
354
            // render clippath as an object, using a different color
 
355
            Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
356
            if (item->clip) {
 
357
                item->arena->outlinecolor = prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff); // green clips
 
358
                NR_ARENA_ITEM_VIRTUAL (item->clip, render) (ct, item->clip, &carea, pb, flags);
 
359
            } 
 
360
            // render mask as an object, using a different color
 
361
            if (item->mask) {
 
362
                item->arena->outlinecolor = prefs->getInt("/options/wireframecolors/masks", 0x0000ffff); // blue masks
 
363
                NR_ARENA_ITEM_VIRTUAL (item->mask, render) (ct, item->mask, &carea, pb, flags);
 
364
            }
 
365
            item->arena->outlinecolor = saved_rgba; // restore outline color
 
366
 
 
367
            return item->state | NR_ARENA_ITEM_STATE_RENDER;
 
368
    }
 
369
 
 
370
    NRPixBlock cpb;
 
371
    if (item->px) {
 
372
        /* Has cache pixblock, render this and return */
 
373
        nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P,
 
374
                                  /* fixme: This probably cannot overflow, because we render only if visible */
 
375
                                  /* fixme: and pixel cache is there only for small items */
 
376
                                  /* fixme: But this still needs extra check (Lauris) */
 
377
                                  item->drawbox.x0, item->drawbox.y0,
 
378
                                  item->drawbox.x1, item->drawbox.y1,
 
379
                                  item->px,
 
380
                                  4 * (item->drawbox.x1 - item->drawbox.x0), FALSE,
 
381
                                  FALSE);
 
382
        nr_blit_pixblock_pixblock (pb, &cpb);
 
383
        nr_pixblock_release (&cpb);
 
384
        pb->empty = FALSE;
 
385
        return item->state | NR_ARENA_ITEM_STATE_RENDER;
 
386
    }
 
387
 
 
388
    NRPixBlock *dpb = pb;
 
389
 
 
390
    /* Setup cache if we can */
 
391
    if ((!(flags & NR_ARENA_ITEM_RENDER_NO_CACHE)) &&
 
392
        (carea.x0 <= item->drawbox.x0) && (carea.y0 <= item->drawbox.y0) &&
 
393
        (carea.x1 >= item->drawbox.x1) && (carea.y1 >= item->drawbox.y1) &&
 
394
        (((item->drawbox.x1 - item->drawbox.x0) * (item->drawbox.y1 -
 
395
                                             item->drawbox.y0)) <= 4096)) {
 
396
        // Item drawbox is fully in renderable area and size is acceptable
 
397
        carea.x0 = item->drawbox.x0;
 
398
        carea.y0 = item->drawbox.y0;
 
399
        carea.x1 = item->drawbox.x1;
 
400
        carea.y1 = item->drawbox.y1;
 
401
        item->px =
 
402
            new (GC::ATOMIC) unsigned char[4 * (carea.x1 - carea.x0) *
 
403
                                           (carea.y1 - carea.y0)];
 
404
        nr_pixblock_setup_extern (&cpb, NR_PIXBLOCK_MODE_R8G8B8A8P, carea.x0,
 
405
                                  carea.y0, carea.x1, carea.y1, item->px,
 
406
                                  4 * (carea.x1 - carea.x0), TRUE, TRUE);
 
407
        cpb.visible_area = pb->visible_area;
 
408
        dpb = &cpb;
 
409
        // Set nocache flag for downstream rendering
 
410
        flags |= NR_ARENA_ITEM_RENDER_NO_CACHE;
 
411
    }
 
412
 
 
413
    /* Determine, whether we need temporary buffer */
 
414
    if (item->clip || item->mask
 
415
        || ((item->opacity != 255) && !item->render_opacity)
 
416
        || (item->filter && filter) || item->background_new
 
417
        || (item->parent && item->parent->background_pb)) {
 
418
 
 
419
        /* Setup and render item buffer */
 
420
        NRPixBlock ipb;
 
421
        nr_pixblock_setup_fast (&ipb, NR_PIXBLOCK_MODE_R8G8B8A8P,
 
422
                                carea.x0, carea.y0, carea.x1, carea.y1,
 
423
                                TRUE);
 
424
 
 
425
        //  if memory allocation failed, abort render
 
426
        if (ipb.size != NR_PIXBLOCK_SIZE_TINY && ipb.data.px == NULL) {
 
427
            nr_pixblock_release (&ipb);
 
428
            return (item->state);
 
429
        }
 
430
 
 
431
        /* If background access is used, save the pixblock address.
 
432
         * This address is set to NULL at the end of this block */
 
433
        if (item->background_new ||
 
434
            (item->parent && item->parent->background_pb)) {
 
435
            item->background_pb = &ipb;
 
436
        }
 
437
 
 
438
        ipb.visible_area = pb->visible_area;
 
439
        if (item->filter && filter) {
 
440
              item->filter->area_enlarge (ipb.visible_area, item);
 
441
        }
 
442
 
 
443
        unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, &carea, &ipb, flags);
 
444
        if (state & NR_ARENA_ITEM_STATE_INVALID) {
 
445
            /* Clean up and return error */
 
446
            nr_pixblock_release (&ipb);
 
447
            if (dpb != pb)
 
448
                nr_pixblock_release (dpb);
 
449
            item->state |= NR_ARENA_ITEM_STATE_INVALID;
 
450
            return item->state;
 
451
        }
 
452
        ipb.empty = FALSE;
 
453
 
 
454
        /* Run filtering, if a filter is set for this object */
 
455
        if (item->filter && filter) {
 
456
            item->filter->render (item, &ipb);
 
457
        }
 
458
 
 
459
        if (item->clip || item->mask) {
 
460
            /* Setup mask pixblock */
 
461
            NRPixBlock mpb;
 
462
            nr_pixblock_setup_fast (&mpb, NR_PIXBLOCK_MODE_A8, carea.x0,
 
463
                                    carea.y0, carea.x1, carea.y1, TRUE);
 
464
 
 
465
            if (mpb.data.px != NULL) { // if memory allocation was successful
 
466
 
 
467
                mpb.visible_area = pb->visible_area;
 
468
                /* Do clip if needed */
 
469
                if (item->clip) {
 
470
                    state = nr_arena_item_invoke_clip (item->clip, &carea, &mpb);
 
471
                    if (state & NR_ARENA_ITEM_STATE_INVALID) {
 
472
                        /* Clean up and return error */
 
473
                        nr_pixblock_release (&mpb);
 
474
                        nr_pixblock_release (&ipb);
 
475
                        if (dpb != pb)
 
476
                            nr_pixblock_release (dpb);
 
477
                        item->state |= NR_ARENA_ITEM_STATE_INVALID;
 
478
                        return item->state;
 
479
                    }
 
480
                    mpb.empty = FALSE;
 
481
                }
 
482
                /* Do mask if needed */
 
483
                if (item->mask) {
 
484
                    NRPixBlock tpb;
 
485
                    /* Set up yet another temporary pixblock */
 
486
                    nr_pixblock_setup_fast (&tpb, NR_PIXBLOCK_MODE_R8G8B8A8N,
 
487
                                            carea.x0, carea.y0, carea.x1,
 
488
                                            carea.y1, TRUE);
 
489
 
 
490
                    if (tpb.data.px != NULL) { // if memory allocation was successful
 
491
 
 
492
                        tpb.visible_area = pb->visible_area;
 
493
                        unsigned int state = NR_ARENA_ITEM_VIRTUAL (item->mask, render) (ct, item->mask, &carea, &tpb, flags);
 
494
                        if (state & NR_ARENA_ITEM_STATE_INVALID) {
 
495
                            /* Clean up and return error */
 
496
                            nr_pixblock_release (&tpb);
 
497
                            nr_pixblock_release (&mpb);
 
498
                            nr_pixblock_release (&ipb);
 
499
                            if (dpb != pb)
 
500
                                nr_pixblock_release (dpb);
 
501
                            item->state |= NR_ARENA_ITEM_STATE_INVALID;
 
502
                            return item->state;
 
503
                        }
 
504
                        /* Composite with clip */
 
505
                        if (item->clip) {
 
506
                            int x, y;
 
507
                            for (y = carea.y0; y < carea.y1; y++) {
 
508
                                unsigned char *s, *d;
 
509
                                s = NR_PIXBLOCK_PX (&tpb) + (y -
 
510
                                                             carea.y0) * tpb.rs;
 
511
                                d = NR_PIXBLOCK_PX (&mpb) + (y -
 
512
                                                             carea.y0) * mpb.rs;
 
513
                                for (x = carea.x0; x < carea.x1; x++) {
 
514
                                    unsigned int m;
 
515
                                    m = NR_PREMUL_112 (s[0] + s[1] + s[2], s[3]);
 
516
                                    d[0] =
 
517
                                        FAST_DIV_ROUND < 3 * 255 * 255 >
 
518
                                        (NR_PREMUL_123 (d[0], m));
 
519
                                    s += 4;
 
520
                                    d += 1;
 
521
                                }
 
522
                            }
 
523
                        } else {
 
524
                            int x, y;
 
525
                            for (y = carea.y0; y < carea.y1; y++) {
 
526
                                unsigned char *s, *d;
 
527
                                s = NR_PIXBLOCK_PX (&tpb) + (y -
 
528
                                                             carea.y0) * tpb.rs;
 
529
                                d = NR_PIXBLOCK_PX (&mpb) + (y -
 
530
                                                             carea.y0) * mpb.rs;
 
531
                                for (x = carea.x0; x < carea.x1; x++) {
 
532
                                    unsigned int m;
 
533
                                    m = NR_PREMUL_112 (s[0] + s[1] + s[2], s[3]);
 
534
                                    d[0] = FAST_DIV_ROUND < 3 * 255 > (m);
 
535
                                    s += 4;
 
536
                                    d += 1;
 
537
                                }
 
538
                            }
 
539
                            mpb.empty = FALSE;
 
540
                        }
 
541
                    }
 
542
                    nr_pixblock_release (&tpb);
 
543
                }
 
544
                /* Multiply with opacity if needed */
 
545
                if ((item->opacity != 255) && !item->render_opacity
 
546
                    ) {
 
547
                    int x, y;
 
548
                    unsigned int a;
 
549
                    a = item->opacity;
 
550
                    for (y = carea.y0; y < carea.y1; y++) {
 
551
                        unsigned char *d;
 
552
                        d = NR_PIXBLOCK_PX (&mpb) + (y - carea.y0) * mpb.rs;
 
553
                        for (x = carea.x0; x < carea.x1; x++) {
 
554
                            d[0] = NR_PREMUL_111 (d[0], a);
 
555
                            d += 1;
 
556
                        }
 
557
                    }
 
558
                }
 
559
                /* Compose rendering pixblock int destination */
 
560
                nr_blit_pixblock_pixblock_mask (dpb, &ipb, &mpb);
 
561
            }
 
562
            nr_pixblock_release (&mpb);
 
563
        } else {
 
564
            if (item->render_opacity) { // opacity was already rendered in, just copy to dpb here
 
565
                nr_blit_pixblock_pixblock(dpb, &ipb);
 
566
            } else { // copy while multiplying by opacity
 
567
                nr_blit_pixblock_pixblock_alpha (dpb, &ipb, item->opacity);
 
568
            }
 
569
        }
 
570
        nr_pixblock_release (&ipb);
 
571
        dpb->empty = FALSE;
 
572
        /* This pointer wouldn't be valid outside this block, so clear it */
 
573
        item->background_pb = NULL;
 
574
    } else {
 
575
        /* Just render */
 
576
        unsigned int state = NR_ARENA_ITEM_VIRTUAL (item, render) (ct, item, &carea, dpb, flags);
 
577
        if (state & NR_ARENA_ITEM_STATE_INVALID) {
 
578
            /* Clean up and return error */
 
579
            if (dpb != pb)
 
580
                nr_pixblock_release (dpb);
 
581
            item->state |= NR_ARENA_ITEM_STATE_INVALID;
 
582
            return item->state;
 
583
        }
 
584
        dpb->empty = FALSE;
 
585
    }
 
586
 
 
587
    if (dpb != pb) {
 
588
        /* Have to blit from cache */
 
589
        nr_blit_pixblock_pixblock (pb, dpb);
 
590
        nr_pixblock_release (dpb);
 
591
        pb->empty = FALSE;
 
592
        item->state |= NR_ARENA_ITEM_STATE_IMAGE;
 
593
    }
 
594
 
 
595
    return item->state | NR_ARENA_ITEM_STATE_RENDER;
 
596
}
 
597
 
 
598
unsigned int
 
599
nr_arena_item_invoke_clip (NRArenaItem *item, NRRectL *area, NRPixBlock *pb)
 
600
{
 
601
    nr_return_val_if_fail (item != NULL, NR_ARENA_ITEM_STATE_INVALID);
 
602
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (item),
 
603
                           NR_ARENA_ITEM_STATE_INVALID);
 
604
    /* we originally short-circuited if the object state included
 
605
     * NR_ARENA_ITEM_STATE_CLIP (and showed a warning on the console);
 
606
     * anyone know why we stopped doing so?
 
607
     */
 
608
    nr_return_val_if_fail ((pb->area.x1 - pb->area.x0) >=
 
609
                           (area->x1 - area->x0),
 
610
                           NR_ARENA_ITEM_STATE_INVALID);
 
611
    nr_return_val_if_fail ((pb->area.y1 - pb->area.y0) >=
 
612
                           (area->y1 - area->y0),
 
613
                           NR_ARENA_ITEM_STATE_INVALID);
 
614
 
 
615
#ifdef NR_ARENA_ITEM_VERBOSE
 
616
    printf ("Invoke clip by %p: %d %d - %d %d, item bbox %d %d - %d %d\n",
 
617
            item, area->x0, area->y0, area->x1, area->y1, (&item->bbox)->x0,
 
618
            (&item->bbox)->y0, (&item->bbox)->x1, (&item->bbox)->y1);
 
619
#endif
 
620
 
 
621
    if (item->visible && nr_rect_l_test_intersect_ptr(area, &item->bbox)) {
 
622
        /* Need render that item */
 
623
        if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->clip) {
 
624
            return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->
 
625
                clip (item, area, pb);
 
626
        }
 
627
    }
 
628
 
 
629
    return item->state;
 
630
}
 
631
 
 
632
NRArenaItem *
 
633
nr_arena_item_invoke_pick (NRArenaItem *item, Geom::Point p, double delta,
 
634
                           unsigned int sticky)
 
635
{
 
636
    nr_return_val_if_fail (item != NULL, NULL);
 
637
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
 
638
 
 
639
    // Sometimes there's no BBOX in item->state, reason unknown (bug 992817); I made this not an assert to remove the warning
 
640
    if (!(item->state & NR_ARENA_ITEM_STATE_BBOX)
 
641
        || !(item->state & NR_ARENA_ITEM_STATE_PICK))
 
642
        return NULL;
 
643
 
 
644
    if (!sticky && !(item->visible && item->sensitive))
 
645
        return NULL;
 
646
 
 
647
    // TODO: rewrite using Geom::Rect
 
648
    const double x = p[Geom::X];
 
649
    const double y = p[Geom::Y];
 
650
 
 
651
    if (((x + delta) >= item->bbox.x0) &&
 
652
        ((x - delta) < item->bbox.x1) &&
 
653
        ((y + delta) >= item->bbox.y0) && ((y - delta) < item->bbox.y1)) {
 
654
        if (((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->pick)
 
655
            return ((NRArenaItemClass *) NR_OBJECT_GET_CLASS (item))->
 
656
                pick (item, p, delta, sticky);
 
657
    }
 
658
 
 
659
    return NULL;
 
660
}
 
661
 
 
662
void
 
663
nr_arena_item_request_update (NRArenaItem *item, unsigned int reset,
 
664
                              unsigned int propagate)
 
665
{
 
666
    nr_return_if_fail (item != NULL);
 
667
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
668
    nr_return_if_fail (!(reset & NR_ARENA_ITEM_STATE_INVALID));
 
669
 
 
670
    if (propagate && !item->propagate)
 
671
        item->propagate = TRUE;
 
672
 
 
673
    if (item->state & reset) {
 
674
        item->state &= ~reset;
 
675
        if (item->parent) {
 
676
            nr_arena_item_request_update (item->parent, reset, FALSE);
 
677
        } else {
 
678
            nr_arena_request_update (item->arena, item);
 
679
        }
 
680
    }
 
681
}
 
682
 
 
683
void
 
684
nr_arena_item_request_render (NRArenaItem *item)
 
685
{
 
686
    nr_return_if_fail (item != NULL);
 
687
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
688
 
 
689
    nr_arena_request_render_rect (item->arena, &item->drawbox);
 
690
}
 
691
 
 
692
/* Public */
 
693
 
 
694
NRArenaItem *
 
695
nr_arena_item_unparent (NRArenaItem *item)
 
696
{
 
697
    nr_return_val_if_fail (item != NULL, NULL);
 
698
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (item), NULL);
 
699
 
 
700
    nr_arena_item_request_render (item);
 
701
 
 
702
    if (item->parent) {
 
703
        nr_arena_item_remove_child (item->parent, item);
 
704
    }
 
705
 
 
706
    return NULL;
 
707
}
 
708
 
 
709
void
 
710
nr_arena_item_append_child (NRArenaItem *parent, NRArenaItem *child)
 
711
{
 
712
    nr_return_if_fail (parent != NULL);
 
713
    nr_return_if_fail (NR_IS_ARENA_ITEM (parent));
 
714
    nr_return_if_fail (child != NULL);
 
715
    nr_return_if_fail (NR_IS_ARENA_ITEM (child));
 
716
    nr_return_if_fail (parent->arena == child->arena);
 
717
    nr_return_if_fail (child->parent == NULL);
 
718
    nr_return_if_fail (child->prev == NULL);
 
719
    nr_return_if_fail (child->next == NULL);
 
720
 
 
721
    nr_arena_item_add_child (parent, child, nr_arena_item_last_child (parent));
 
722
}
 
723
 
 
724
void
 
725
nr_arena_item_set_transform (NRArenaItem *item, Geom::Matrix const &transform)
 
726
{
 
727
    Geom::Matrix const t (transform);
 
728
    nr_arena_item_set_transform (item, &t);
 
729
}
 
730
 
 
731
void
 
732
nr_arena_item_set_transform (NRArenaItem *item, Geom::Matrix const *transform)
 
733
{
 
734
    nr_return_if_fail (item != NULL);
 
735
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
736
 
 
737
    if (!transform && !item->transform)
 
738
        return;
 
739
 
 
740
    const Geom::Matrix *md = (item->transform) ? item->transform : &GEOM_MATRIX_IDENTITY;
 
741
    const Geom::Matrix *ms = (transform) ? transform : &GEOM_MATRIX_IDENTITY;
 
742
 
 
743
    if (!Geom::matrix_equalp(*md, *ms, NR_EPSILON)) {
 
744
        nr_arena_item_request_render (item);
 
745
        if (!transform || transform->isIdentity()) {
 
746
            /* Set to identity affine */
 
747
            item->transform = NULL;
 
748
        } else {
 
749
            if (!item->transform)
 
750
                item->transform = new (GC::ATOMIC) Geom::Matrix ();
 
751
            *item->transform = *transform;
 
752
        }
 
753
        nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
 
754
    }
 
755
}
 
756
 
 
757
void
 
758
nr_arena_item_set_opacity (NRArenaItem *item, double opacity)
 
759
{
 
760
    nr_return_if_fail (item != NULL);
 
761
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
762
 
 
763
    nr_arena_item_request_render (item);
 
764
 
 
765
    item->opacity = (unsigned int) (opacity * 255.9999);
 
766
}
 
767
 
 
768
void
 
769
nr_arena_item_set_sensitive (NRArenaItem *item, unsigned int sensitive)
 
770
{
 
771
    nr_return_if_fail (item != NULL);
 
772
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
773
 
 
774
    /* fixme: mess with pick/repick... */
 
775
 
 
776
    item->sensitive = sensitive;
 
777
}
 
778
 
 
779
void
 
780
nr_arena_item_set_visible (NRArenaItem *item, unsigned int visible)
 
781
{
 
782
    nr_return_if_fail (item != NULL);
 
783
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
784
 
 
785
    item->visible = visible;
 
786
 
 
787
    nr_arena_item_request_render (item);
 
788
}
 
789
 
 
790
void
 
791
nr_arena_item_set_clip (NRArenaItem *item, NRArenaItem *clip)
 
792
{
 
793
    nr_return_if_fail (item != NULL);
 
794
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
795
    nr_return_if_fail (!clip || NR_IS_ARENA_ITEM (clip));
 
796
 
 
797
    if (clip != item->clip) {
 
798
        nr_arena_item_request_render (item);
 
799
        if (item->clip)
 
800
            item->clip = nr_arena_item_detach (item, item->clip);
 
801
        if (clip)
 
802
            item->clip = nr_arena_item_attach (item, clip, NULL, NULL);
 
803
        nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
 
804
    }
 
805
}
 
806
 
 
807
void
 
808
nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask)
 
809
{
 
810
    nr_return_if_fail (item != NULL);
 
811
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
812
    nr_return_if_fail (!mask || NR_IS_ARENA_ITEM (mask));
 
813
 
 
814
    if (mask != item->mask) {
 
815
        nr_arena_item_request_render (item);
 
816
        if (item->mask)
 
817
            item->mask = nr_arena_item_detach (item, item->mask);
 
818
        if (mask)
 
819
            item->mask = nr_arena_item_attach (item, mask, NULL, NULL);
 
820
        nr_arena_item_request_update (item, NR_ARENA_ITEM_STATE_ALL, TRUE);
 
821
    }
 
822
}
 
823
 
 
824
void
 
825
nr_arena_item_set_order (NRArenaItem *item, int order)
 
826
{
 
827
    nr_return_if_fail (item != NULL);
 
828
    nr_return_if_fail (NR_IS_ARENA_ITEM (item));
 
829
 
 
830
    if (!item->parent)
 
831
        return;
 
832
 
 
833
    NRArenaItem *children = nr_arena_item_children (item->parent);
 
834
 
 
835
    NRArenaItem *ref = NULL;
 
836
    int pos = 0;
 
837
    for (NRArenaItem *child = children; child != NULL; child = child->next) {
 
838
        if (pos >= order)
 
839
            break;
 
840
        if (child != item) {
 
841
            ref = child;
 
842
            pos += 1;
 
843
        }
 
844
    }
 
845
 
 
846
    nr_arena_item_set_child_position (item->parent, item, ref);
 
847
}
 
848
 
 
849
void
 
850
nr_arena_item_set_item_bbox (NRArenaItem *item, Geom::OptRect &bbox)
 
851
{
 
852
    nr_return_if_fail(item != NULL);
 
853
    nr_return_if_fail(NR_IS_ARENA_ITEM(item));
 
854
 
 
855
    item->item_bbox = bbox;
 
856
}
 
857
 
 
858
/** Returns a background image for use with filter effects. */
 
859
NRPixBlock *
 
860
nr_arena_item_get_background (NRArenaItem const *item, int depth)
 
861
{
 
862
    NRPixBlock *pb;
 
863
    if (!item->background_pb)
 
864
        return NULL;
 
865
    if (item->background_new) {
 
866
        pb = new NRPixBlock ();
 
867
        nr_pixblock_setup_fast (pb, item->background_pb->mode,
 
868
                                item->background_pb->area.x0,
 
869
                                item->background_pb->area.y0,
 
870
                                item->background_pb->area.x1,
 
871
                                item->background_pb->area.y1, true);
 
872
        if (pb->size != NR_PIXBLOCK_SIZE_TINY && pb->data.px == NULL) // allocation failed
 
873
            return NULL;
 
874
    } else if (item->parent) {
 
875
        pb = nr_arena_item_get_background (item->parent, depth + 1);
 
876
    } else
 
877
        return NULL;
 
878
 
 
879
    if (depth > 0)
 
880
        nr_blit_pixblock_pixblock (pb, item->background_pb);
 
881
 
 
882
    return pb;
 
883
}
 
884
 
 
885
/* Helpers */
 
886
 
 
887
NRArenaItem *
 
888
nr_arena_item_attach (NRArenaItem *parent, NRArenaItem *child,
 
889
                      NRArenaItem *prev, NRArenaItem *next)
 
890
{
 
891
    nr_return_val_if_fail (parent != NULL, NULL);
 
892
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
 
893
    nr_return_val_if_fail (child != NULL, NULL);
 
894
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
 
895
    nr_return_val_if_fail (child->parent == NULL, NULL);
 
896
    nr_return_val_if_fail (child->prev == NULL, NULL);
 
897
    nr_return_val_if_fail (child->next == NULL, NULL);
 
898
    nr_return_val_if_fail (!prev || NR_IS_ARENA_ITEM (prev), NULL);
 
899
    nr_return_val_if_fail (!prev || (prev->parent == parent), NULL);
 
900
    nr_return_val_if_fail (!prev || (prev->next == next), NULL);
 
901
    nr_return_val_if_fail (!next || NR_IS_ARENA_ITEM (next), NULL);
 
902
    nr_return_val_if_fail (!next || (next->parent == parent), NULL);
 
903
    nr_return_val_if_fail (!next || (next->prev == prev), NULL);
 
904
 
 
905
    child->parent = parent;
 
906
    child->prev = prev;
 
907
    child->next = next;
 
908
 
 
909
    if (prev)
 
910
        prev->next = child;
 
911
    if (next)
 
912
        next->prev = child;
 
913
 
 
914
    return child;
 
915
}
 
916
 
 
917
NRArenaItem *
 
918
nr_arena_item_detach (NRArenaItem *parent, NRArenaItem *child)
 
919
{
 
920
    nr_return_val_if_fail (parent != NULL, NULL);
 
921
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (parent), NULL);
 
922
    nr_return_val_if_fail (child != NULL, NULL);
 
923
    nr_return_val_if_fail (NR_IS_ARENA_ITEM (child), NULL);
 
924
    nr_return_val_if_fail (child->parent == parent, NULL);
 
925
 
 
926
    NRArenaItem *prev = child->prev;
 
927
    NRArenaItem *next = child->next;
 
928
 
 
929
    child->parent = NULL;
 
930
    child->prev = NULL;
 
931
    child->next = NULL;
 
932
 
 
933
    if (prev)
 
934
        prev->next = next;
 
935
    if (next)
 
936
        next->prev = prev;
 
937
 
 
938
    return next;
 
939
}
 
940
 
 
941
/*
 
942
  Local Variables:
 
943
  mode:c++
 
944
  c-file-style:"stroustrup"
 
945
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
946
  indent-tabs-mode:nil
 
947
  fill-column:99
 
948
  End:
 
949
*/
 
950
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :