1
#define __SP_PATTERN_C__
4
* SVG <pattern> implementation
7
* Lauris Kaplinski <lauris@kaplinski.com>
8
* bulia byak <buliabyak@users.sf.net>
10
* Copyright (C) 2002 Lauris Kaplinski
12
* Released under GNU GPL, read the file 'COPYING' for more information
17
#include <libnr/nr-matrix-ops.h>
18
#include "libnr/nr-matrix-fns.h"
19
#include <libnr/nr-translate-matrix-ops.h>
22
#include "display/nr-arena.h"
23
#include "display/nr-arena-group.h"
24
#include "attributes.h"
25
#include "document-private.h"
27
#include "sp-pattern.h"
52
NRPixBlock cached_tile;
55
static void sp_pattern_class_init (SPPatternClass *klass);
56
static void sp_pattern_init (SPPattern *gr);
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);
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);
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);
71
static SPPaintServerClass * pattern_parent_class;
74
sp_pattern_get_type (void)
76
static GType pattern_type = 0;
78
GTypeInfo pattern_info = {
79
sizeof (SPPatternClass),
81
NULL, /* base_finalize */
82
(GClassInitFunc) sp_pattern_class_init,
83
NULL, /* class_finalize */
84
NULL, /* class_data */
87
(GInstanceInitFunc) sp_pattern_init,
88
NULL, /* value_table */
90
pattern_type = g_type_register_static (SP_TYPE_PAINT_SERVER, "SPPattern", &pattern_info, (GTypeFlags)0);
96
sp_pattern_class_init (SPPatternClass *klass)
98
SPObjectClass *sp_object_class;
99
SPPaintServerClass *ps_class;
101
sp_object_class = (SPObjectClass *) klass;
102
ps_class = (SPPaintServerClass *) klass;
104
pattern_parent_class = (SPPaintServerClass*)g_type_class_ref (SP_TYPE_PAINT_SERVER);
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;
113
// do we need _write? seems to work without it
115
ps_class->painter_new = sp_pattern_painter_new;
116
ps_class->painter_free = sp_pattern_painter_free;
120
sp_pattern_init (SPPattern *pat)
122
pat->ref = new SPPatternReference(SP_OBJECT(pat));
123
pat->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(pattern_ref_changed), pat));
125
pat->patternUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX;
126
pat->patternUnits_set = FALSE;
128
pat->patternContentUnits = SP_PATTERN_UNITS_USERSPACEONUSE;
129
pat->patternContentUnits_set = FALSE;
131
pat->patternTransform = NR::identity();
132
pat->patternTransform_set = FALSE;
139
pat->viewBox_set = FALSE;
143
sp_pattern_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
145
if (((SPObjectClass *) pattern_parent_class)->build)
146
(* ((SPObjectClass *) pattern_parent_class)->build) (object, document, repr);
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");
158
/* Register ourselves */
159
sp_document_add_resource (document, "pattern", object);
163
sp_pattern_release (SPObject *object)
167
pat = (SPPattern *) object;
169
if (SP_OBJECT_DOCUMENT (object)) {
170
/* Unregister ourselves */
171
sp_document_remove_resource (SP_OBJECT_DOCUMENT (object), "pattern", SP_OBJECT (object));
175
if (pat->ref->getObject())
176
sp_signal_disconnect_by_data(pat->ref->getObject(), pat);
182
if (((SPObjectClass *) pattern_parent_class)->release)
183
((SPObjectClass *) pattern_parent_class)->release (object);
187
sp_pattern_set (SPObject *object, unsigned int key, const gchar *value)
189
SPPattern *pat = SP_PATTERN (object);
192
case SP_ATTR_PATTERNUNITS:
194
if (!strcmp (value, "userSpaceOnUse")) {
195
pat->patternUnits = SP_PATTERN_UNITS_USERSPACEONUSE;
197
pat->patternUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX;
199
pat->patternUnits_set = TRUE;
201
pat->patternUnits_set = FALSE;
203
object->requestModified(SP_OBJECT_MODIFIED_FLAG);
205
case SP_ATTR_PATTERNCONTENTUNITS:
207
if (!strcmp (value, "userSpaceOnUse")) {
208
pat->patternContentUnits = SP_PATTERN_UNITS_USERSPACEONUSE;
210
pat->patternContentUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX;
212
pat->patternContentUnits_set = TRUE;
214
pat->patternContentUnits_set = FALSE;
216
object->requestModified(SP_OBJECT_MODIFIED_FLAG);
218
case SP_ATTR_PATTERNTRANSFORM: {
220
if (value && sp_svg_transform_read (value, &t)) {
221
pat->patternTransform = t;
222
pat->patternTransform_set = TRUE;
224
pat->patternTransform = NR::identity();
225
pat->patternTransform_set = FALSE;
227
object->requestModified(SP_OBJECT_MODIFIED_FLAG);
231
pat->x.readOrUnset(value);
232
object->requestModified(SP_OBJECT_MODIFIED_FLAG);
235
pat->y.readOrUnset(value);
236
object->requestModified(SP_OBJECT_MODIFIED_FLAG);
239
pat->width.readOrUnset(value);
240
object->requestModified(SP_OBJECT_MODIFIED_FLAG);
243
pat->height.readOrUnset(value);
244
object->requestModified(SP_OBJECT_MODIFIED_FLAG);
246
case SP_ATTR_VIEWBOX: {
247
/* fixme: Think (Lauris) */
248
double x, y, width, height;
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)) {
264
pat->viewBox.x1 = x + width;
265
pat->viewBox.y1 = y + height;
266
pat->viewBox_set = TRUE;
268
pat->viewBox_set = FALSE;
271
pat->viewBox_set = FALSE;
273
object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
276
case SP_ATTR_XLINK_HREF:
277
if ( value && pat->href && ( strcmp(value, pat->href) == 0 ) ) {
278
/* Href unchanged, do nothing. */
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.
288
pat->ref->attach(Inkscape::URI(value));
289
} catch (Inkscape::BadURIException &e) {
290
g_warning("%s", e.what());
300
if (((SPObjectClass *) pattern_parent_class)->set)
301
((SPObjectClass *) pattern_parent_class)->set (object, key, value);
307
sp_pattern_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
309
SPPattern *pat = SP_PATTERN (object);
311
if (((SPObjectClass *) (pattern_parent_class))->child_added)
312
(* ((SPObjectClass *) (pattern_parent_class))->child_added) (object, child, ref);
314
SPObject *ochild = sp_object_get_child_by_repr(object, child);
315
if (SP_IS_ITEM (ochild)) {
317
SPPaintServer *ps = SP_PAINT_SERVER (pat);
318
unsigned position = sp_item_pos_in_parent(SP_ITEM(ochild));
320
for (SPPainter *p = ps->painters; p != NULL; p = p->next) {
322
SPPatPainter *pp = (SPPatPainter *) p;
323
NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (ochild), pp->arena, pp->dkey, SP_ITEM_REFERENCE_FLAGS);
326
nr_arena_item_add_child (pp->root, ai, NULL);
327
nr_arena_item_set_order (ai, position);
328
nr_arena_item_unref (ai);
334
/* TODO: do we need a ::remove_child handler? */
336
/* fixme: We need ::order_changed handler too (Lauris) */
339
pattern_getchildren (SPPattern *pat)
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);
348
break; // do not go further up the chain if children are found
356
sp_pattern_update (SPObject *object, SPCtx *ctx, unsigned int flags)
358
SPPattern *pat = SP_PATTERN (object);
360
if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
361
flags &= SP_OBJECT_MODIFIED_CASCADE;
363
GSList *l = pattern_getchildren (pat);
364
l = g_slist_reverse (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);
373
sp_object_unref (child, NULL);
378
sp_pattern_modified (SPObject *object, guint flags)
380
SPPattern *pat = SP_PATTERN (object);
382
if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
383
flags &= SP_OBJECT_MODIFIED_CASCADE;
385
GSList *l = pattern_getchildren (pat);
386
l = g_slist_reverse (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);
395
sp_object_unref (child, NULL);
400
Gets called when the pattern is reattached to another <pattern>
403
pattern_ref_changed(SPObject *old_ref, SPObject *ref, SPPattern *pat)
406
sp_signal_disconnect_by_data(old_ref, pat);
408
if (SP_IS_PATTERN (ref)) {
409
g_signal_connect(G_OBJECT (ref), "modified", G_CALLBACK (pattern_ref_modified), pat);
412
pattern_ref_modified (ref, 0, pat);
416
Gets called when the referenced <pattern> is changed
419
pattern_ref_modified (SPObject *ref, guint flags, SPPattern *pattern)
421
if (SP_IS_OBJECT (pattern))
422
SP_OBJECT (pattern)->requestModified(SP_OBJECT_MODIFIED_FLAG);
426
pattern_users (SPPattern *pattern)
428
return SP_OBJECT (pattern)->hrefcount;
432
pattern_chain (SPPattern *pattern)
434
SPDocument *document = SP_OBJECT_DOCUMENT (pattern);
435
Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (SP_DOCUMENT_DEFS (document));
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);
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));
448
return SP_PATTERN (child);
452
sp_pattern_clone_if_necessary (SPItem *item, SPPattern *pattern, const gchar *property)
454
if (pattern_users(pattern) > 1) {
455
pattern = pattern_chain (pattern);
456
gchar *href = g_strconcat ("url(#", SP_OBJECT_REPR (pattern)->attribute("id"), ")", NULL);
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");
466
sp_pattern_transform_multiply (SPPattern *pattern, NR::Matrix postmul, bool set)
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;
472
// otherwise the formula is much simpler
474
pattern->patternTransform = postmul;
476
pattern->patternTransform = pattern_patternTransform(pattern) * postmul;
478
pattern->patternTransform_set = TRUE;
481
if (sp_svg_transform_write(c, 256, pattern->patternTransform)) {
482
SP_OBJECT_REPR(pattern)->setAttribute("patternTransform", c);
484
SP_OBJECT_REPR(pattern)->setAttribute("patternTransform", NULL);
489
pattern_tile (GSList *reprs, NR::Rect bounds, SPDocument *document, NR::Matrix transform, NR::Matrix move)
491
Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (SP_DOCUMENT_DEFS (document));
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));
499
if (sp_svg_transform_write(t, 256, transform)) {
500
repr->setAttribute("patternTransform", t);
502
repr->setAttribute("patternTransform", NULL);
506
defsrepr->appendChild(repr);
507
const gchar *pat_id = repr->attribute("id");
508
SPObject *pat_object = document->getObjectById(pat_id);
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));
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;
519
sp_item_write_transform(copy, SP_OBJECT_REPR(copy), dup_transform);
522
Inkscape::GC::release(repr);
527
pattern_getroot (SPPattern *pat)
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
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
539
// Access functions that look up fields up the chain of referenced patterns and return the first one which is set
541
guint pattern_patternUnits (SPPattern *pat)
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;
547
return pat->patternUnits;
550
guint pattern_patternContentUnits (SPPattern *pat)
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;
556
return pat->patternContentUnits;
559
NR::Matrix const &pattern_patternTransform(SPPattern const *pat)
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;
565
return pat->patternTransform;
568
gdouble pattern_x (SPPattern *pat)
570
for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
572
return pat_i->x.computed;
577
gdouble pattern_y (SPPattern *pat)
579
for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
581
return pat_i->y.computed;
586
gdouble pattern_width (SPPattern *pat)
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;
595
gdouble pattern_height (SPPattern *pat)
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;
604
NRRect *pattern_viewBox (SPPattern *pat)
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);
610
return &(pat->viewBox);
613
bool pattern_hasItemChildren (SPPattern *pat)
615
for (SPObject *child = sp_object_first_child(SP_OBJECT(pat)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
616
if (SP_IS_ITEM (child)) {
627
static void sp_pat_fill (SPPainter *painter, NRPixBlock *pb);
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.
634
sp_pattern_painter_new (SPPaintServer *ps, NR::Matrix const &full_transform, NR::Matrix const &parent_transform, const NRRect *bbox)
636
SPPattern *pat = SP_PATTERN (ps);
637
SPPatPainter *pp = g_new (SPPatPainter, 1);
639
pp->painter.type = SP_PAINTER_IND;
640
pp->painter.fill = sp_pat_fill;
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);
648
// the final patternTransform, taking into account bbox
649
NR::Matrix const ps2user(pattern_patternTransform(pat) * bbox2user);
651
// see (*) comment below
652
NR::Matrix ps2px = ps2user * full_transform;
654
ps2px.copyto (&pp->ps2px);
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) */
660
/* fixme: We may try to normalize here too, look at linearGradient (Lauris) */
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.)
672
//NR::Matrix ps2px = (full_transform / parent_transform) * pattern_patternTransform(pat) * parent_transform;
673
NR::Matrix ps2px = pattern_patternTransform(pat) * full_transform;
675
ps2px.copyto (&pp->ps2px);
678
nr_matrix_invert (&pp->px2ps, &pp->ps2px);
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);
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);
687
NR::Matrix vb2us = vb2ps * pattern_patternTransform(pat);
690
NR::Matrix pcs2px = vb2us * full_transform;
692
pcs2px.copyto (&pp->pcs2px);
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);
701
NR::Matrix pcs2user = pattern_patternTransform(pat) * bbox2user;
704
pcs2px = pcs2user * full_transform;
707
//pcs2px = (full_transform / parent_transform) * pattern_patternTransform(pat) * parent_transform;
708
pcs2px = pattern_patternTransform(pat) * full_transform;
711
pcs2px = NR::translate (pattern_x (pat), pattern_y (pat)) * pcs2px;
713
pcs2px.copyto (&pp->pcs2px);
717
pp->arena = NRArena::create();
719
pp->dkey = sp_item_display_key_new (1);
722
pp->root = NRArenaGroup::create(pp->arena);
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)) {
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);
735
break; // do not go further up the chain if children are found
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;
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);
761
pp->pa2ca.c[0]=((double)tr_width)/(one_tile.x1-one_tile.x0);
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);
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;
774
// pp->use_cached_tile=false;
779
if ( pp->use_cached_tile ) {
780
gc.transform=pp->pa2ca;
782
gc.transform = pp->pcs2px;
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);
791
return (SPPainter *) pp;
795
sp_pattern_painter_free (SPPaintServer *ps, SPPainter *painter)
797
SPPatPainter *pp = (SPPatPainter *) painter;
798
SPPattern *pat = pp->pat;
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);
807
break; // do not go further up the chain if children are found
810
if ( pp->use_cached_tile ) nr_pixblock_release(&pp->cached_tile);
815
get_cached_tile_pixel(SPPatPainter* pp,double x,double y,unsigned char &r,unsigned char &g,unsigned char &b,unsigned char &a)
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;
827
if ( cb_h >= pp->cached_bbox.x1 ) cb_h=0;
828
if ( cb_v >= pp->cached_bbox.y1 ) cb_v=0;
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;
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;
845
f_c=(tl_m*tl_c+tr_m*tr_c+bl_m*bl_c+br_m*br_c)>>8;
851
f_c=(tl_m*tl_c+tr_m*tr_c+bl_m*bl_c+br_m*br_c)>>8;
857
f_c=(tl_m*tl_c+tr_m*tr_c+bl_m*bl_c+br_m*br_c)>>8;
862
sp_pat_fill (SPPainter *painter, NRPixBlock *pb)
869
pp = (SPPatPainter *) painter;
871
if (pattern_width (pp->pat) < NR_EPSILON) return;
872
if (pattern_height (pp->pat) < NR_EPSILON) return;
874
/* Find buffer area in gradient space */
875
/* fixme: This is suboptimal (Lauris) */
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;
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);
904
ps_x+=pp->px2ps.c[0];
905
ps_y+=pp->px2ps.c[1];
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;
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);
934
ps_x+=pp->px2ps.c[0];
935
ps_y+=pp->px2ps.c[1];
947
nr_rect_d_matrix_transform (&psa, &ba, &pp->px2ps);
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;
954
for (y = psa.y0; y < psa.y1; y++) {
955
for (x = psa.x0; x < psa.x1; x++) {
959
psx = x * pattern_width (pp->pat);
960
psy = y * pattern_height (pp->pat);
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;
967
// We do not update here anymore
971
nr_pixblock_setup_extern (&ppb, pb->mode, area.x0, area.y0, area.x1, area.y1, NR_PIXBLOCK_PX (pb), pb->rs, FALSE, FALSE);
973
nr_arena_item_invoke_render (pp->root, &area, &ppb, 0);
975
nr_pixblock_release (&ppb);