~vaifrax/inkscape/bugfix170049

« back to all changes in this revision

Viewing changes to src/sp-pattern.cpp

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SP_PATTERN_C__
 
2
 
 
3
/*
 
4
 * SVG <pattern> implementation
 
5
 *
 
6
 * Author:
 
7
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
8
 *   bulia byak <buliabyak@users.sf.net>
 
9
 *
 
10
 * Copyright (C) 2002 Lauris Kaplinski
 
11
 *
 
12
 * Released under GNU GPL, read the file 'COPYING' for more information
 
13
 */
 
14
 
 
15
#include "config.h"
 
16
 
 
17
#include <libnr/nr-matrix-ops.h>
 
18
#include "libnr/nr-matrix-fns.h"
 
19
#include <libnr/nr-translate-matrix-ops.h>
 
20
#include "macros.h"
 
21
#include "svg/svg.h"
 
22
#include "display/nr-arena.h"
 
23
#include "display/nr-arena-group.h"
 
24
#include "attributes.h"
 
25
#include "document-private.h"
 
26
#include "uri.h"
 
27
#include "sp-pattern.h"
 
28
#include "xml/repr.h"
 
29
 
 
30
/*
 
31
 * Pattern
 
32
 */
 
33
 
 
34
class SPPatPainter;
 
35
 
 
36
struct SPPatPainter {
 
37
        SPPainter painter;
 
38
        SPPattern *pat;
 
39
 
 
40
        NRMatrix ps2px;
 
41
        NRMatrix px2ps;
 
42
        NRMatrix pcs2px;
 
43
 
 
44
        NRArena *arena;
 
45
        unsigned int dkey;
 
46
        NRArenaItem *root;
 
47
        
 
48
        bool         use_cached_tile;
 
49
        NRMatrix     ca2pa;
 
50
        NRMatrix     pa2ca;
 
51
        NRRectL      cached_bbox;
 
52
        NRPixBlock   cached_tile;
 
53
};
 
54
 
 
55
static void sp_pattern_class_init (SPPatternClass *klass);
 
56
static void sp_pattern_init (SPPattern *gr);
 
57
 
 
58
static void sp_pattern_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
 
59
static void sp_pattern_release (SPObject *object);
 
60
static void sp_pattern_set (SPObject *object, unsigned int key, const gchar *value);
 
61
static void sp_pattern_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
 
62
static void sp_pattern_update (SPObject *object, SPCtx *ctx, unsigned int flags);
 
63
static void sp_pattern_modified (SPObject *object, unsigned int flags);
 
64
 
 
65
static void pattern_ref_changed(SPObject *old_ref, SPObject *ref, SPPattern *pat);
 
66
static void pattern_ref_modified (SPObject *ref, guint flags, SPPattern *pattern);
 
67
 
 
68
static SPPainter *sp_pattern_painter_new (SPPaintServer *ps, NR::Matrix const &full_transform, NR::Matrix const &parent_transform, const NRRect *bbox);
 
69
static void sp_pattern_painter_free (SPPaintServer *ps, SPPainter *painter);
 
70
 
 
71
static SPPaintServerClass * pattern_parent_class;
 
72
 
 
73
GType
 
74
sp_pattern_get_type (void)
 
75
{
 
76
        static GType pattern_type = 0;
 
77
        if (!pattern_type) {
 
78
                GTypeInfo pattern_info = {
 
79
                        sizeof (SPPatternClass),
 
80
                        NULL,   /* base_init */
 
81
                        NULL,   /* base_finalize */
 
82
                        (GClassInitFunc) sp_pattern_class_init,
 
83
                        NULL,   /* class_finalize */
 
84
                        NULL,   /* class_data */
 
85
                        sizeof (SPPattern),
 
86
                        16,     /* n_preallocs */
 
87
                        (GInstanceInitFunc) sp_pattern_init,
 
88
                        NULL,   /* value_table */
 
89
                };
 
90
                pattern_type = g_type_register_static (SP_TYPE_PAINT_SERVER, "SPPattern", &pattern_info, (GTypeFlags)0);
 
91
        }
 
92
        return pattern_type;
 
93
}
 
94
 
 
95
static void
 
96
sp_pattern_class_init (SPPatternClass *klass)
 
97
{
 
98
        SPObjectClass *sp_object_class;
 
99
        SPPaintServerClass *ps_class;
 
100
 
 
101
        sp_object_class = (SPObjectClass *) klass;
 
102
        ps_class = (SPPaintServerClass *) klass;
 
103
 
 
104
        pattern_parent_class = (SPPaintServerClass*)g_type_class_ref (SP_TYPE_PAINT_SERVER);
 
105
 
 
106
        sp_object_class->build = sp_pattern_build;
 
107
        sp_object_class->release = sp_pattern_release;
 
108
        sp_object_class->set = sp_pattern_set;
 
109
        sp_object_class->child_added = sp_pattern_child_added;
 
110
        sp_object_class->update = sp_pattern_update;
 
111
        sp_object_class->modified = sp_pattern_modified;
 
112
 
 
113
        // do we need _write? seems to work without it
 
114
 
 
115
        ps_class->painter_new = sp_pattern_painter_new;
 
116
        ps_class->painter_free = sp_pattern_painter_free;
 
117
}
 
118
 
 
119
static void
 
120
sp_pattern_init (SPPattern *pat)
 
121
{
 
122
        pat->ref = new SPPatternReference(SP_OBJECT(pat));
 
123
        pat->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(pattern_ref_changed), pat));
 
124
 
 
125
        pat->patternUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX;
 
126
        pat->patternUnits_set = FALSE;
 
127
 
 
128
        pat->patternContentUnits = SP_PATTERN_UNITS_USERSPACEONUSE;
 
129
        pat->patternContentUnits_set = FALSE;
 
130
 
 
131
        pat->patternTransform = NR::identity();
 
132
        pat->patternTransform_set = FALSE;
 
133
 
 
134
        pat->x.unset();
 
135
        pat->y.unset();
 
136
        pat->width.unset();
 
137
        pat->height.unset();
 
138
 
 
139
        pat->viewBox_set = FALSE;
 
140
}
 
141
 
 
142
static void
 
143
sp_pattern_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
 
144
{
 
145
        if (((SPObjectClass *) pattern_parent_class)->build)
 
146
                (* ((SPObjectClass *) pattern_parent_class)->build) (object, document, repr);
 
147
 
 
148
        sp_object_read_attr (object, "patternUnits");
 
149
        sp_object_read_attr (object, "patternContentUnits");
 
150
        sp_object_read_attr (object, "patternTransform");
 
151
        sp_object_read_attr (object, "x");
 
152
        sp_object_read_attr (object, "y");
 
153
        sp_object_read_attr (object, "width");
 
154
        sp_object_read_attr (object, "height");
 
155
        sp_object_read_attr (object, "viewBox");
 
156
        sp_object_read_attr (object, "xlink:href");
 
157
 
 
158
        /* Register ourselves */
 
159
        sp_document_add_resource (document, "pattern", object);
 
160
}
 
161
 
 
162
static void
 
163
sp_pattern_release (SPObject *object)
 
164
{
 
165
        SPPattern *pat;
 
166
 
 
167
        pat = (SPPattern *) object;
 
168
 
 
169
        if (SP_OBJECT_DOCUMENT (object)) {
 
170
                /* Unregister ourselves */
 
171
                sp_document_remove_resource (SP_OBJECT_DOCUMENT (object), "pattern", SP_OBJECT (object));
 
172
        }
 
173
 
 
174
        if (pat->ref) {
 
175
                if (pat->ref->getObject())
 
176
                        sp_signal_disconnect_by_data(pat->ref->getObject(), pat);
 
177
                pat->ref->detach();
 
178
                delete pat->ref;
 
179
                pat->ref = NULL;
 
180
        }
 
181
 
 
182
        if (((SPObjectClass *) pattern_parent_class)->release)
 
183
                ((SPObjectClass *) pattern_parent_class)->release (object);
 
184
}
 
185
 
 
186
static void
 
187
sp_pattern_set (SPObject *object, unsigned int key, const gchar *value)
 
188
{
 
189
        SPPattern *pat = SP_PATTERN (object);
 
190
 
 
191
        switch (key) {
 
192
        case SP_ATTR_PATTERNUNITS:
 
193
                if (value) {
 
194
                        if (!strcmp (value, "userSpaceOnUse")) {
 
195
                                pat->patternUnits = SP_PATTERN_UNITS_USERSPACEONUSE;
 
196
                        } else {
 
197
                                pat->patternUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX;
 
198
                        }
 
199
                        pat->patternUnits_set = TRUE;
 
200
                } else {
 
201
                        pat->patternUnits_set = FALSE;
 
202
                }
 
203
                object->requestModified(SP_OBJECT_MODIFIED_FLAG);
 
204
                break;
 
205
        case SP_ATTR_PATTERNCONTENTUNITS:
 
206
                if (value) {
 
207
                        if (!strcmp (value, "userSpaceOnUse")) {
 
208
                                pat->patternContentUnits = SP_PATTERN_UNITS_USERSPACEONUSE;
 
209
                        } else {
 
210
                                pat->patternContentUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX;
 
211
                        }
 
212
                        pat->patternContentUnits_set = TRUE;
 
213
                } else {
 
214
                        pat->patternContentUnits_set = FALSE;
 
215
                }
 
216
                object->requestModified(SP_OBJECT_MODIFIED_FLAG);
 
217
                break;
 
218
        case SP_ATTR_PATTERNTRANSFORM: {
 
219
                NR::Matrix t;
 
220
                if (value && sp_svg_transform_read (value, &t)) {
 
221
                        pat->patternTransform = t;
 
222
                        pat->patternTransform_set = TRUE;
 
223
                } else {
 
224
                        pat->patternTransform = NR::identity();
 
225
                        pat->patternTransform_set = FALSE;
 
226
                }
 
227
                object->requestModified(SP_OBJECT_MODIFIED_FLAG);
 
228
                break;
 
229
        }
 
230
        case SP_ATTR_X:
 
231
                pat->x.readOrUnset(value);
 
232
                object->requestModified(SP_OBJECT_MODIFIED_FLAG);
 
233
                break;
 
234
        case SP_ATTR_Y:
 
235
                pat->y.readOrUnset(value);
 
236
                object->requestModified(SP_OBJECT_MODIFIED_FLAG);
 
237
                break;
 
238
        case SP_ATTR_WIDTH:
 
239
                pat->width.readOrUnset(value);
 
240
                object->requestModified(SP_OBJECT_MODIFIED_FLAG);
 
241
                break;
 
242
        case SP_ATTR_HEIGHT:
 
243
                pat->height.readOrUnset(value);
 
244
                object->requestModified(SP_OBJECT_MODIFIED_FLAG);
 
245
                break;
 
246
        case SP_ATTR_VIEWBOX: {
 
247
                /* fixme: Think (Lauris) */
 
248
                double x, y, width, height;
 
249
                char *eptr;
 
250
 
 
251
                if (value) {
 
252
                        eptr = (gchar *) value;
 
253
                        x = g_ascii_strtod (eptr, &eptr);
 
254
                        while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
 
255
                        y = g_ascii_strtod (eptr, &eptr);
 
256
                        while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
 
257
                        width = g_ascii_strtod (eptr, &eptr);
 
258
                        while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
 
259
                        height = g_ascii_strtod (eptr, &eptr);
 
260
                        while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
 
261
                        if ((width > 0) && (height > 0)) {
 
262
                                pat->viewBox.x0 = x;
 
263
                                pat->viewBox.y0 = y;
 
264
                                pat->viewBox.x1 = x + width;
 
265
                                pat->viewBox.y1 = y + height;
 
266
                                pat->viewBox_set = TRUE;
 
267
                        } else {
 
268
                                pat->viewBox_set = FALSE;
 
269
                        }
 
270
                } else {
 
271
                        pat->viewBox_set = FALSE;
 
272
                }
 
273
                object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
 
274
                break;
 
275
        }
 
276
        case SP_ATTR_XLINK_HREF:
 
277
                if ( value && pat->href && ( strcmp(value, pat->href) == 0 ) ) {
 
278
                        /* Href unchanged, do nothing. */
 
279
                } else {
 
280
                        g_free(pat->href);
 
281
                        pat->href = NULL;
 
282
                        if (value) {
 
283
                                // First, set the href field; it's only used in the "unchanged" check above.
 
284
                                pat->href = g_strdup(value);
 
285
                                // Now do the attaching, which emits the changed signal.
 
286
                                if (value) {
 
287
                                        try {
 
288
                                                pat->ref->attach(Inkscape::URI(value));
 
289
                                        } catch (Inkscape::BadURIException &e) {
 
290
                                                g_warning("%s", e.what());
 
291
                                                pat->ref->detach();
 
292
                                        }
 
293
                                } else {
 
294
                                        pat->ref->detach();
 
295
                                }
 
296
                        }
 
297
                }
 
298
                break;
 
299
        default:
 
300
                if (((SPObjectClass *) pattern_parent_class)->set)
 
301
                        ((SPObjectClass *) pattern_parent_class)->set (object, key, value);
 
302
                break;
 
303
        }
 
304
}
 
305
 
 
306
static void
 
307
sp_pattern_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
 
308
{
 
309
        SPPattern *pat = SP_PATTERN (object);
 
310
 
 
311
        if (((SPObjectClass *) (pattern_parent_class))->child_added)
 
312
                (* ((SPObjectClass *) (pattern_parent_class))->child_added) (object, child, ref);
 
313
 
 
314
        SPObject *ochild = sp_object_get_child_by_repr(object, child);
 
315
        if (SP_IS_ITEM (ochild)) {
 
316
 
 
317
                SPPaintServer *ps = SP_PAINT_SERVER (pat);
 
318
                unsigned position = sp_item_pos_in_parent(SP_ITEM(ochild));
 
319
 
 
320
                for (SPPainter *p = ps->painters; p != NULL; p = p->next) {
 
321
 
 
322
                        SPPatPainter *pp = (SPPatPainter *) p;
 
323
                        NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (ochild), pp->arena, pp->dkey, SP_ITEM_REFERENCE_FLAGS);
 
324
 
 
325
                        if (ai) {
 
326
                                nr_arena_item_add_child (pp->root, ai, NULL);
 
327
                                nr_arena_item_set_order (ai, position);
 
328
                                nr_arena_item_unref (ai);
 
329
                        }
 
330
                }
 
331
        }
 
332
}
 
333
 
 
334
/* TODO: do we need a ::remove_child handler? */
 
335
 
 
336
/* fixme: We need ::order_changed handler too (Lauris) */
 
337
 
 
338
GSList *
 
339
pattern_getchildren (SPPattern *pat)
 
340
{
 
341
        GSList *l = NULL;
 
342
 
 
343
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
344
                if (sp_object_first_child(SP_OBJECT(pat_i))) { // find the first one with children
 
345
                        for (SPObject *child = sp_object_first_child(SP_OBJECT (pat)) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) {
 
346
                                l = g_slist_prepend (l, child);
 
347
                        }
 
348
                        break; // do not go further up the chain if children are found
 
349
                }
 
350
        }
 
351
 
 
352
        return l;
 
353
}
 
354
 
 
355
static void
 
356
sp_pattern_update (SPObject *object, SPCtx *ctx, unsigned int flags)
 
357
{
 
358
        SPPattern *pat = SP_PATTERN (object);
 
359
 
 
360
        if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
 
361
        flags &= SP_OBJECT_MODIFIED_CASCADE;
 
362
 
 
363
        GSList *l = pattern_getchildren (pat);
 
364
        l = g_slist_reverse (l);
 
365
 
 
366
        while (l) {
 
367
                SPObject *child = SP_OBJECT (l->data);
 
368
                sp_object_ref (child, NULL);
 
369
                l = g_slist_remove (l, child);
 
370
                if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
 
371
                        child->updateDisplay(ctx, flags);
 
372
                }
 
373
                sp_object_unref (child, NULL);
 
374
        }
 
375
}
 
376
 
 
377
static void
 
378
sp_pattern_modified (SPObject *object, guint flags)
 
379
{
 
380
        SPPattern *pat = SP_PATTERN (object);
 
381
 
 
382
        if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
 
383
        flags &= SP_OBJECT_MODIFIED_CASCADE;
 
384
 
 
385
        GSList *l = pattern_getchildren (pat);
 
386
        l = g_slist_reverse (l);
 
387
 
 
388
        while (l) {
 
389
                SPObject *child = SP_OBJECT (l->data);
 
390
                sp_object_ref (child, NULL);
 
391
                l = g_slist_remove (l, child);
 
392
                if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
 
393
                        child->emitModified(flags);
 
394
                }
 
395
                sp_object_unref (child, NULL);
 
396
        }
 
397
}
 
398
 
 
399
/**
 
400
Gets called when the pattern is reattached to another <pattern>
 
401
*/
 
402
static void
 
403
pattern_ref_changed(SPObject *old_ref, SPObject *ref, SPPattern *pat)
 
404
{
 
405
        if (old_ref) {
 
406
                sp_signal_disconnect_by_data(old_ref, pat);
 
407
        }
 
408
        if (SP_IS_PATTERN (ref)) {
 
409
                g_signal_connect(G_OBJECT (ref), "modified", G_CALLBACK (pattern_ref_modified), pat);
 
410
        }
 
411
 
 
412
        pattern_ref_modified (ref, 0, pat);
 
413
}
 
414
 
 
415
/**
 
416
Gets called when the referenced <pattern> is changed
 
417
*/
 
418
static void
 
419
pattern_ref_modified (SPObject *ref, guint flags, SPPattern *pattern)
 
420
{
 
421
        if (SP_IS_OBJECT (pattern))
 
422
                SP_OBJECT (pattern)->requestModified(SP_OBJECT_MODIFIED_FLAG);
 
423
}
 
424
 
 
425
guint
 
426
pattern_users (SPPattern *pattern)
 
427
{
 
428
        return SP_OBJECT (pattern)->hrefcount;
 
429
}
 
430
 
 
431
SPPattern *
 
432
pattern_chain (SPPattern *pattern)
 
433
{
 
434
        SPDocument *document = SP_OBJECT_DOCUMENT (pattern);
 
435
        Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (SP_DOCUMENT_DEFS (document));
 
436
 
 
437
        Inkscape::XML::Node *repr = sp_repr_new ("svg:pattern");
 
438
        repr->setAttribute("inkscape:collect", "always");
 
439
        gchar *parent_ref = g_strconcat ("#", SP_OBJECT_REPR(pattern)->attribute("id"), NULL);
 
440
        repr->setAttribute("xlink:href",  parent_ref);
 
441
        g_free (parent_ref);
 
442
 
 
443
        defsrepr->addChild(repr, NULL);
 
444
        const gchar *child_id = repr->attribute("id");
 
445
        SPObject *child = document->getObjectById(child_id);
 
446
        g_assert (SP_IS_PATTERN (child));
 
447
 
 
448
        return SP_PATTERN (child);
 
449
}
 
450
 
 
451
SPPattern *
 
452
sp_pattern_clone_if_necessary (SPItem *item, SPPattern *pattern, const gchar *property)
 
453
{
 
454
        if (pattern_users(pattern) > 1) {
 
455
                pattern = pattern_chain (pattern);
 
456
                gchar *href = g_strconcat ("url(#", SP_OBJECT_REPR (pattern)->attribute("id"), ")", NULL);
 
457
 
 
458
                SPCSSAttr *css = sp_repr_css_attr_new ();
 
459
                sp_repr_css_set_property (css, property, href);
 
460
                sp_repr_css_change_recursive (SP_OBJECT_REPR (item), css, "style");
 
461
        } 
 
462
        return pattern;
 
463
}
 
464
 
 
465
void
 
466
sp_pattern_transform_multiply (SPPattern *pattern, NR::Matrix postmul, bool set)
 
467
{
 
468
        // this formula is for a different interpretation of pattern transforms as described in (*) in sp-pattern.cpp
 
469
        // for it to work, we also need    sp_object_read_attr (SP_OBJECT (item), "transform");
 
470
        //pattern->patternTransform = premul * item->transform * pattern->patternTransform * item->transform.inverse() * postmul;
 
471
 
 
472
        // otherwise the formula is much simpler
 
473
        if (set) {
 
474
                pattern->patternTransform = postmul;
 
475
        } else {
 
476
                pattern->patternTransform = pattern_patternTransform(pattern) * postmul;
 
477
        }
 
478
        pattern->patternTransform_set = TRUE;
 
479
 
 
480
        gchar c[256];
 
481
        if (sp_svg_transform_write(c, 256, pattern->patternTransform)) {
 
482
                SP_OBJECT_REPR(pattern)->setAttribute("patternTransform", c);
 
483
        } else {
 
484
                SP_OBJECT_REPR(pattern)->setAttribute("patternTransform", NULL);
 
485
        }
 
486
}
 
487
 
 
488
const gchar *
 
489
pattern_tile (GSList *reprs, NR::Rect bounds, SPDocument *document, NR::Matrix transform, NR::Matrix move)
 
490
{
 
491
        Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (SP_DOCUMENT_DEFS (document));
 
492
 
 
493
        Inkscape::XML::Node *repr = sp_repr_new ("svg:pattern");
 
494
        repr->setAttribute("patternUnits", "userSpaceOnUse");
 
495
        sp_repr_set_svg_double(repr, "width", bounds.extent(NR::X));
 
496
        sp_repr_set_svg_double(repr, "height", bounds.extent(NR::Y));
 
497
 
 
498
        gchar t[256];
 
499
        if (sp_svg_transform_write(t, 256, transform)) {
 
500
                repr->setAttribute("patternTransform", t);
 
501
        } else {
 
502
                repr->setAttribute("patternTransform", NULL);
 
503
        }
 
504
 
 
505
 
 
506
        defsrepr->appendChild(repr);
 
507
        const gchar *pat_id = repr->attribute("id");
 
508
        SPObject *pat_object = document->getObjectById(pat_id);
 
509
 
 
510
        for (GSList *i = reprs; i != NULL; i = i->next) {
 
511
                Inkscape::XML::Node *node = (Inkscape::XML::Node *)(i->data);
 
512
                SPItem *copy = SP_ITEM(pat_object->appendChildRepr(node));
 
513
 
 
514
                NR::Matrix dup_transform;
 
515
                if (!sp_svg_transform_read (node->attribute("transform"), &dup_transform)) 
 
516
                        dup_transform = NR::identity();
 
517
                dup_transform *= move;
 
518
 
 
519
                sp_item_write_transform(copy, SP_OBJECT_REPR(copy), dup_transform);
 
520
        }
 
521
 
 
522
        Inkscape::GC::release(repr);
 
523
        return pat_id;
 
524
}
 
525
 
 
526
SPPattern *
 
527
pattern_getroot (SPPattern *pat) 
 
528
{
 
529
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
530
                if (sp_object_first_child(SP_OBJECT(pat_i))) { // find the first one with children
 
531
                        return pat_i;
 
532
                }
 
533
        }
 
534
        return pat; // document is broken, we can't get to root; but at least we can return pat which is supposedly a valid pattern
 
535
}
 
536
 
 
537
 
 
538
 
 
539
// Access functions that look up fields up the chain of referenced patterns and return the first one which is set
 
540
 
 
541
guint pattern_patternUnits (SPPattern *pat)
 
542
{
 
543
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
544
                if (pat_i->patternUnits_set)
 
545
                        return pat_i->patternUnits;
 
546
        }
 
547
        return pat->patternUnits;
 
548
}
 
549
 
 
550
guint pattern_patternContentUnits (SPPattern *pat)
 
551
{
 
552
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
553
                if (pat_i->patternContentUnits_set)
 
554
                        return pat_i->patternContentUnits;
 
555
        }
 
556
        return pat->patternContentUnits;
 
557
}
 
558
 
 
559
NR::Matrix const &pattern_patternTransform(SPPattern const *pat)
 
560
{
 
561
        for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
562
                if (pat_i->patternTransform_set)
 
563
                        return pat_i->patternTransform;
 
564
        }
 
565
        return pat->patternTransform;
 
566
}
 
567
 
 
568
gdouble pattern_x (SPPattern *pat)
 
569
{
 
570
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
571
                if (pat_i->x._set)
 
572
                        return pat_i->x.computed;
 
573
        }
 
574
        return 0;
 
575
}
 
576
 
 
577
gdouble pattern_y (SPPattern *pat)
 
578
{
 
579
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
580
                if (pat_i->y._set)
 
581
                        return pat_i->y.computed;
 
582
        }
 
583
        return 0;
 
584
}
 
585
 
 
586
gdouble pattern_width (SPPattern *pat)
 
587
{
 
588
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
589
                if (pat_i->width._set)
 
590
                        return pat_i->width.computed;
 
591
        }
 
592
        return 0;
 
593
}
 
594
 
 
595
gdouble pattern_height (SPPattern *pat)
 
596
{
 
597
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
598
                if (pat_i->height._set)
 
599
                        return pat_i->height.computed;
 
600
        }
 
601
        return 0;
 
602
}
 
603
 
 
604
NRRect *pattern_viewBox (SPPattern *pat)
 
605
{
 
606
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
607
                if (pat_i->viewBox_set)
 
608
                        return &(pat_i->viewBox);
 
609
        }
 
610
        return &(pat->viewBox);
 
611
}
 
612
 
 
613
bool pattern_hasItemChildren (SPPattern *pat)
 
614
{
 
615
        for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
 
616
                if (SP_IS_ITEM (child)) {
 
617
                        return true;
 
618
                }
 
619
        }
 
620
        return false;
 
621
}
 
622
 
 
623
 
 
624
 
 
625
/* Painter */
 
626
 
 
627
static void sp_pat_fill (SPPainter *painter, NRPixBlock *pb);
 
628
 
 
629
/**
 
630
Creates a painter (i.e. the thing that does actual filling at the given zoom).
 
631
See (*) below for why the parent_transform may be necessary.
 
632
*/
 
633
static SPPainter *
 
634
sp_pattern_painter_new (SPPaintServer *ps, NR::Matrix const &full_transform, NR::Matrix const &parent_transform, const NRRect *bbox)
 
635
{
 
636
        SPPattern *pat = SP_PATTERN (ps);
 
637
        SPPatPainter *pp = g_new (SPPatPainter, 1);
 
638
 
 
639
        pp->painter.type = SP_PAINTER_IND;
 
640
        pp->painter.fill = sp_pat_fill;
 
641
 
 
642
        pp->pat = pat;
 
643
 
 
644
        if (pattern_patternUnits (pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
 
645
                /* BBox to user coordinate system */
 
646
                NR::Matrix bbox2user (bbox->x1 - bbox->x0, 0.0, 0.0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0);
 
647
 
 
648
                // the final patternTransform, taking into account bbox
 
649
                NR::Matrix const ps2user(pattern_patternTransform(pat) * bbox2user);
 
650
 
 
651
                // see (*) comment below
 
652
                NR::Matrix ps2px = ps2user * full_transform;
 
653
 
 
654
                ps2px.copyto (&pp->ps2px);
 
655
 
 
656
        } else {
 
657
                /* Problem: What to do, if we have mixed lengths and percentages? */
 
658
                /* Currently we do ignore percentages at all, but that is not good (lauris) */
 
659
 
 
660
                /* fixme: We may try to normalize here too, look at linearGradient (Lauris) */
 
661
 
 
662
                // (*) The spec says, "This additional transformation matrix [patternTransform] is
 
663
                // post-multiplied to (i.e., inserted to the right of) any previously defined
 
664
                // transformations, including the implicit transformation necessary to convert from
 
665
                // object bounding box units to user space." To me, this means that the order should be:
 
666
                // item_transform * patternTransform * parent_transform
 
667
                // However both Batik and Adobe plugin use:
 
668
                // patternTransform * item_transform * parent_transform
 
669
                // So here I comply with the majority opinion, but leave my interpretation commented out below.
 
670
                // (To get item_transform, I subtract parent from full.)
 
671
 
 
672
                //NR::Matrix ps2px = (full_transform / parent_transform) * pattern_patternTransform(pat) * parent_transform;
 
673
                NR::Matrix ps2px = pattern_patternTransform(pat) * full_transform;
 
674
 
 
675
                ps2px.copyto (&pp->ps2px);
 
676
        }
 
677
 
 
678
        nr_matrix_invert (&pp->px2ps, &pp->ps2px);
 
679
 
 
680
        if (pat->viewBox_set) {
 
681
                gdouble tmp_x = (pattern_viewBox(pat)->x1 - pattern_viewBox(pat)->x0) / pattern_width (pat);
 
682
                gdouble tmp_y = (pattern_viewBox(pat)->y1 - pattern_viewBox(pat)->y0) / pattern_height (pat);
 
683
 
 
684
                // FIXME: preserveAspectRatio must be taken into account here too!
 
685
                NR::Matrix vb2ps (tmp_x, 0.0, 0.0, tmp_y, pattern_x(pat) - pattern_viewBox(pat)->x0, pattern_y(pat) - pattern_viewBox(pat)->y0);
 
686
 
 
687
                NR::Matrix vb2us = vb2ps * pattern_patternTransform(pat);
 
688
 
 
689
                // see (*)
 
690
                NR::Matrix pcs2px = vb2us * full_transform;
 
691
 
 
692
                pcs2px.copyto (&pp->pcs2px);
 
693
        } else {
 
694
                NR::Matrix pcs2px;
 
695
 
 
696
                /* No viewbox, have to parse units */
 
697
                if (pattern_patternContentUnits (pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) {
 
698
                        /* BBox to user coordinate system */
 
699
                        NR::Matrix bbox2user (bbox->x1 - bbox->x0, 0.0, 0.0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0);
 
700
 
 
701
                        NR::Matrix pcs2user = pattern_patternTransform(pat) * bbox2user;
 
702
 
 
703
                        // see (*)
 
704
                        pcs2px = pcs2user * full_transform;
 
705
                } else {
 
706
                        // see (*)
 
707
                        //pcs2px = (full_transform / parent_transform) * pattern_patternTransform(pat) * parent_transform;
 
708
                        pcs2px = pattern_patternTransform(pat) * full_transform;
 
709
                }
 
710
 
 
711
                pcs2px = NR::translate (pattern_x (pat), pattern_y (pat)) * pcs2px; 
 
712
 
 
713
                pcs2px.copyto (&pp->pcs2px);
 
714
        }
 
715
 
 
716
        /* Create arena */
 
717
        pp->arena = NRArena::create();
 
718
 
 
719
        pp->dkey = sp_item_display_key_new (1);
 
720
 
 
721
        /* Create group */
 
722
        pp->root = NRArenaGroup::create(pp->arena);
 
723
 
 
724
        /* Show items */
 
725
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
726
                if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
 
727
                        for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
 
728
                                if (SP_IS_ITEM (child)) {
 
729
                                        NRArenaItem *cai;
 
730
                                        cai = sp_item_invoke_show (SP_ITEM (child), pp->arena, pp->dkey, SP_ITEM_REFERENCE_FLAGS);
 
731
                                        nr_arena_item_append_child (pp->root, cai);
 
732
                                        nr_arena_item_unref (cai);
 
733
                                }
 
734
                        }
 
735
                        break; // do not go further up the chain if children are found
 
736
                }
 
737
        }
 
738
 
 
739
        {
 
740
                NRRect    one_tile,tr_tile;
 
741
                one_tile.x0=pattern_x(pp->pat);
 
742
                one_tile.y0=pattern_y(pp->pat);
 
743
                one_tile.x1=one_tile.x0+pattern_width (pp->pat);
 
744
                one_tile.y1=one_tile.y0+pattern_height (pp->pat);
 
745
                nr_rect_d_matrix_transform (&tr_tile, &one_tile, &pp->ps2px);
 
746
                int       tr_width=(int)ceil(1.3*(tr_tile.x1-tr_tile.x0));
 
747
                int       tr_height=(int)ceil(1.3*(tr_tile.y1-tr_tile.y0));
 
748
//              if ( tr_width < 10000 && tr_height < 10000 && tr_width*tr_height < 1000000 ) {
 
749
                pp->use_cached_tile=false;//true;
 
750
                        if ( tr_width > 1000 ) tr_width=1000;
 
751
                        if ( tr_height > 1000 ) tr_height=1000;
 
752
                        pp->cached_bbox.x0=0;
 
753
                        pp->cached_bbox.y0=0;
 
754
                        pp->cached_bbox.x1=tr_width;
 
755
                        pp->cached_bbox.y1=tr_height;
 
756
 
 
757
                        if (pp->use_cached_tile) {
 
758
                                nr_pixblock_setup (&pp->cached_tile,NR_PIXBLOCK_MODE_R8G8B8A8N, pp->cached_bbox.x0, pp->cached_bbox.y0, pp->cached_bbox.x1, pp->cached_bbox.y1,TRUE);
 
759
                        }
 
760
 
 
761
                        pp->pa2ca.c[0]=((double)tr_width)/(one_tile.x1-one_tile.x0);
 
762
                        pp->pa2ca.c[1]=0;
 
763
                        pp->pa2ca.c[2]=0;
 
764
                        pp->pa2ca.c[3]=((double)tr_height)/(one_tile.y1-one_tile.y0);
 
765
                        pp->pa2ca.c[4]=-one_tile.x0*pp->pa2ca.c[0];
 
766
                        pp->pa2ca.c[5]=-one_tile.y0*pp->pa2ca.c[1];
 
767
                        pp->ca2pa.c[0]=(one_tile.x1-one_tile.x0)/((double)tr_width);
 
768
                        pp->ca2pa.c[1]=0;
 
769
                        pp->ca2pa.c[2]=0;
 
770
                        pp->ca2pa.c[3]=(one_tile.y1-one_tile.y0)/((double)tr_height);
 
771
                        pp->ca2pa.c[4]=one_tile.x0;
 
772
                        pp->ca2pa.c[5]=one_tile.y0;
 
773
//              } else {
 
774
//                      pp->use_cached_tile=false;
 
775
//              }
 
776
        }
 
777
        
 
778
        NRGC gc(NULL);
 
779
        if ( pp->use_cached_tile ) {
 
780
                gc.transform=pp->pa2ca;
 
781
        } else {
 
782
                gc.transform = pp->pcs2px;
 
783
        }
 
784
        nr_arena_item_invoke_update (pp->root, NULL, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_ALL);
 
785
        if ( pp->use_cached_tile ) {
 
786
                nr_arena_item_invoke_render (pp->root, &pp->cached_bbox, &pp->cached_tile, 0);
 
787
        } else {
 
788
                // nothing to do now
 
789
        }
 
790
        
 
791
        return (SPPainter *) pp;
 
792
}
 
793
 
 
794
static void
 
795
sp_pattern_painter_free (SPPaintServer *ps, SPPainter *painter)
 
796
{
 
797
        SPPatPainter *pp = (SPPatPainter *) painter;
 
798
        SPPattern *pat = pp->pat;
 
799
 
 
800
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
 
801
                if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { // find the first one with item children
 
802
                        for (SPObject *child = sp_object_first_child(SP_OBJECT(pat_i)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
 
803
                                if (SP_IS_ITEM (child)) {
 
804
                                                sp_item_invoke_hide (SP_ITEM (child), pp->dkey);
 
805
                                }
 
806
                        }
 
807
                        break; // do not go further up the chain if children are found
 
808
                }
 
809
        }
 
810
        if ( pp->use_cached_tile ) nr_pixblock_release(&pp->cached_tile);
 
811
        g_free (pp);
 
812
}
 
813
 
 
814
void
 
815
get_cached_tile_pixel(SPPatPainter* pp,double x,double y,unsigned char &r,unsigned char &g,unsigned char &b,unsigned char &a)
 
816
{
 
817
        int    ca_h=(int)floor(x);
 
818
        int    ca_v=(int)floor(y);
 
819
        int    r_x=(int)floor(16*(x-floor(x)));
 
820
        int    r_y=(int)floor(16*(y-floor(y)));
 
821
        unsigned int    tl_m=(16-r_x)*(16-r_y);
 
822
        unsigned int    bl_m=(16-r_x)*r_y;
 
823
        unsigned int    tr_m=r_x*(16-r_y);
 
824
        unsigned int    br_m=r_x*r_y;
 
825
        int    cb_h=ca_h+1;
 
826
        int    cb_v=ca_v+1;
 
827
        if ( cb_h >= pp->cached_bbox.x1 ) cb_h=0;
 
828
        if ( cb_v >= pp->cached_bbox.y1 ) cb_v=0;
 
829
        
 
830
        unsigned char* tlx=NR_PIXBLOCK_PX(&pp->cached_tile)+(ca_v*pp->cached_tile.rs)+4*ca_h;
 
831
        unsigned char* trx=NR_PIXBLOCK_PX(&pp->cached_tile)+(ca_v*pp->cached_tile.rs)+4*cb_h;
 
832
        unsigned char* blx=NR_PIXBLOCK_PX(&pp->cached_tile)+(cb_v*pp->cached_tile.rs)+4*ca_h;
 
833
        unsigned char* brx=NR_PIXBLOCK_PX(&pp->cached_tile)+(cb_v*pp->cached_tile.rs)+4*cb_h;
 
834
        
 
835
        unsigned int tl_c=tlx[0];
 
836
        unsigned int tr_c=trx[0];
 
837
        unsigned int bl_c=blx[0];
 
838
        unsigned int br_c=brx[0];
 
839
        unsigned int f_c=(tl_m*tl_c+tr_m*tr_c+bl_m*bl_c+br_m*br_c)>>8;
 
840
        r=f_c;
 
841
        tl_c=tlx[1];
 
842
        tr_c=trx[1];
 
843
        bl_c=blx[1];
 
844
        br_c=brx[1];
 
845
        f_c=(tl_m*tl_c+tr_m*tr_c+bl_m*bl_c+br_m*br_c)>>8;
 
846
        g=f_c;
 
847
        tl_c=tlx[2];
 
848
        tr_c=trx[2];
 
849
        bl_c=blx[2];
 
850
        br_c=brx[2];
 
851
        f_c=(tl_m*tl_c+tr_m*tr_c+bl_m*bl_c+br_m*br_c)>>8;
 
852
        b=f_c;
 
853
        tl_c=tlx[3];
 
854
        tr_c=trx[3];
 
855
        bl_c=blx[3];
 
856
        br_c=brx[3];
 
857
        f_c=(tl_m*tl_c+tr_m*tr_c+bl_m*bl_c+br_m*br_c)>>8;
 
858
        a=f_c;
 
859
}
 
860
 
 
861
static void
 
862
sp_pat_fill (SPPainter *painter, NRPixBlock *pb)
 
863
{
 
864
        SPPatPainter *pp;
 
865
        NRRect ba, psa;
 
866
        NRRectL area;
 
867
        double x, y;
 
868
 
 
869
        pp = (SPPatPainter *) painter;
 
870
 
 
871
        if (pattern_width (pp->pat) < NR_EPSILON) return;
 
872
        if (pattern_height (pp->pat) < NR_EPSILON) return;
 
873
 
 
874
        /* Find buffer area in gradient space */
 
875
        /* fixme: This is suboptimal (Lauris) */
 
876
 
 
877
        if ( pp->use_cached_tile ) {
 
878
                double   pat_w=pattern_width (pp->pat);
 
879
                double   pat_h=pattern_height (pp->pat);
 
880
                if ( pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N || pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P ) { // same thing because it's filling an empty pixblock
 
881
                        unsigned char*  lpx=NR_PIXBLOCK_PX(pb);
 
882
                        double          px_y=pb->area.y0;
 
883
                        for (int j=pb->area.y0;j<pb->area.y1;j++) {
 
884
                                unsigned char* cpx=lpx;
 
885
                                double         px_x = pb->area.x0;
 
886
                                
 
887
                                double ps_x=pp->px2ps.c[0]*px_x+pp->px2ps.c[2]*px_y+pp->px2ps.c[4];
 
888
                                double ps_y=pp->px2ps.c[1]*px_x+pp->px2ps.c[3]*px_y+pp->px2ps.c[5];
 
889
                                for (int i=pb->area.x0;i<pb->area.x1;i++) {
 
890
                                        while ( ps_x > pat_w ) ps_x-=pat_w;
 
891
                                        while ( ps_x < 0 ) ps_x+=pat_w;
 
892
                                        while ( ps_y > pat_h ) ps_y-=pat_h;
 
893
                                        while ( ps_y < 0 ) ps_y+=pat_h;
 
894
                                        double ca_x=pp->pa2ca.c[0]*ps_x+pp->pa2ca.c[2]*ps_y+pp->pa2ca.c[4];
 
895
                                        double ca_y=pp->pa2ca.c[1]*ps_x+pp->pa2ca.c[3]*ps_y+pp->pa2ca.c[5];
 
896
                                        unsigned char n_a,n_r,n_g,n_b;
 
897
                                        get_cached_tile_pixel(pp,ca_x,ca_y,n_r,n_g,n_b,n_a);
 
898
                                        cpx[0]=n_r;
 
899
                                        cpx[1]=n_g;
 
900
                                        cpx[2]=n_b;
 
901
                                        cpx[3]=n_a;
 
902
                                        
 
903
                                        px_x+=1.0;
 
904
                                        ps_x+=pp->px2ps.c[0];
 
905
                                        ps_y+=pp->px2ps.c[1];
 
906
                                        cpx+=4;
 
907
                                }
 
908
                                px_y+=1.0;
 
909
                                lpx+=pb->rs;
 
910
                        }
 
911
                } else if ( pb->mode == NR_PIXBLOCK_MODE_R8G8B8 ) {
 
912
                        unsigned char*  lpx=NR_PIXBLOCK_PX(pb);
 
913
                        double          px_y=pb->area.y0;
 
914
                        for (int j=pb->area.y0;j<pb->area.y1;j++) {
 
915
                                unsigned char* cpx=lpx;
 
916
                                double         px_x = pb->area.x0;
 
917
                                
 
918
                                double ps_x=pp->px2ps.c[0]*px_x+pp->px2ps.c[2]*px_y+pp->px2ps.c[4];
 
919
                                double ps_y=pp->px2ps.c[1]*px_x+pp->px2ps.c[3]*px_y+pp->px2ps.c[5];
 
920
                                for (int i=pb->area.x0;i<pb->area.x1;i++) {
 
921
                                        while ( ps_x > pat_w ) ps_x-=pat_w;
 
922
                                        while ( ps_x < 0 ) ps_x+=pat_w;
 
923
                                        while ( ps_y > pat_h ) ps_y-=pat_h;
 
924
                                        while ( ps_y < 0 ) ps_y+=pat_h;
 
925
                                        double ca_x=pp->pa2ca.c[0]*ps_x+pp->pa2ca.c[2]*ps_y+pp->pa2ca.c[4];
 
926
                                        double ca_y=pp->pa2ca.c[1]*ps_x+pp->pa2ca.c[3]*ps_y+pp->pa2ca.c[5];
 
927
                                        unsigned char n_a,n_r,n_g,n_b;
 
928
                                        get_cached_tile_pixel(pp,ca_x,ca_y,n_r,n_g,n_b,n_a);
 
929
                                        cpx[0]=n_r;
 
930
                                        cpx[1]=n_g;
 
931
                                        cpx[2]=n_b;
 
932
                                        
 
933
                                        px_x+=1.0;
 
934
                                        ps_x+=pp->px2ps.c[0];
 
935
                                        ps_y+=pp->px2ps.c[1];
 
936
                                        cpx+=4;
 
937
                                }
 
938
                                px_y+=1.0;
 
939
                                lpx+=pb->rs;
 
940
                        }
 
941
                }
 
942
        } else {
 
943
                ba.x0 = pb->area.x0;
 
944
                ba.y0 = pb->area.y0;
 
945
                ba.x1 = pb->area.x1;
 
946
                ba.y1 = pb->area.y1;
 
947
                nr_rect_d_matrix_transform (&psa, &ba, &pp->px2ps);
 
948
                
 
949
                psa.x0 = floor ((psa.x0 - pattern_x (pp->pat)) / pattern_width (pp->pat)) -1;
 
950
                psa.y0 = floor ((psa.y0 - pattern_y (pp->pat)) / pattern_height (pp->pat)) -1;
 
951
                psa.x1 = ceil ((psa.x1 - pattern_x (pp->pat)) / pattern_width (pp->pat)) +1;
 
952
                psa.y1 = ceil ((psa.y1 - pattern_y (pp->pat)) / pattern_height (pp->pat)) +1;
 
953
                
 
954
                for (y = psa.y0; y < psa.y1; y++) {
 
955
                        for (x = psa.x0; x < psa.x1; x++) {
 
956
                                NRPixBlock ppb;
 
957
                                double psx, psy;
 
958
                                
 
959
                                psx = x * pattern_width (pp->pat);
 
960
                                psy = y * pattern_height (pp->pat);
 
961
                                
 
962
                                area.x0 = (gint32)(pb->area.x0 - (pp->ps2px.c[0] * psx + pp->ps2px.c[2] * psy));
 
963
                                area.y0 = (gint32)(pb->area.y0 - (pp->ps2px.c[1] * psx + pp->ps2px.c[3] * psy));
 
964
                                area.x1 = area.x0 + pb->area.x1 - pb->area.x0;
 
965
                                area.y1 = area.y0 + pb->area.y1 - pb->area.y0;
 
966
                                
 
967
                                // We do not update here anymore 
 
968
 
 
969
                                // Set up buffer 
 
970
                                // fixme: (Lauris) 
 
971
                                nr_pixblock_setup_extern (&ppb, pb->mode, area.x0, area.y0, area.x1, area.y1, NR_PIXBLOCK_PX (pb), pb->rs, FALSE, FALSE);
 
972
                                
 
973
                                nr_arena_item_invoke_render (pp->root, &area, &ppb, 0);
 
974
                                
 
975
                                nr_pixblock_release (&ppb);
 
976
                        }
 
977
                }
 
978
        }
 
979
}