diff options
| author | Martin Owens <doctormo@gmail.com> | 2014-03-27 01:33:44 +0000 |
|---|---|---|
| committer | Martin Owens <doctormo@gmail.com> | 2014-03-27 01:33:44 +0000 |
| commit | 5a4fb2325f60d292b47330f540b26a3279341c90 (patch) | |
| tree | d2aa7967be25450b83e625025366c618101ae49f /src/sp-pattern.cpp | |
| parent | The Polar Arrange Tab of the Arrange Dialog now hides the parametric (diff) | |
| parent | Remove Snap menu item and improve grid menu item text (diff) | |
| download | inkscape-5a4fb2325f60d292b47330f540b26a3279341c90.tar.gz inkscape-5a4fb2325f60d292b47330f540b26a3279341c90.zip | |
Commit a merge to trunk, with probabal errors
(bzr r11073.1.36)
Diffstat (limited to 'src/sp-pattern.cpp')
| -rw-r--r-- | src/sp-pattern.cpp | 643 |
1 files changed, 309 insertions, 334 deletions
diff --git a/src/sp-pattern.cpp b/src/sp-pattern.cpp index 03afc1bf3..e465565c4 100644 --- a/src/sp-pattern.cpp +++ b/src/sp-pattern.cpp @@ -40,312 +40,251 @@ /* * Pattern */ - -static void sp_pattern_class_init (SPPatternClass *klass); -static void sp_pattern_init (SPPattern *gr); - -static void sp_pattern_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); -static void sp_pattern_release (SPObject *object); -static void sp_pattern_set (SPObject *object, unsigned int key, const gchar *value); -static void sp_pattern_update (SPObject *object, SPCtx *ctx, unsigned int flags); -static void sp_pattern_modified (SPObject *object, unsigned int flags); - static void pattern_ref_changed(SPObject *old_ref, SPObject *ref, SPPattern *pat); static void pattern_ref_modified (SPObject *ref, guint flags, SPPattern *pattern); -static cairo_pattern_t *sp_pattern_create_pattern(SPPaintServer *ps, cairo_t *ct, Geom::OptRect const &bbox, double opacity); - -static SPPaintServerClass * pattern_parent_class; +#include "sp-factory.h" -GType -sp_pattern_get_type (void) -{ - static GType pattern_type = 0; - if (!pattern_type) { - GTypeInfo pattern_info = { - sizeof (SPPatternClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) sp_pattern_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (SPPattern), - 16, /* n_preallocs */ - (GInstanceInitFunc) sp_pattern_init, - NULL, /* value_table */ - }; - pattern_type = g_type_register_static (SP_TYPE_PAINT_SERVER, "SPPattern", &pattern_info, (GTypeFlags)0); +namespace { + SPObject* createPattern() { + return new SPPattern(); } - return pattern_type; -} - -static void -sp_pattern_class_init (SPPatternClass *klass) -{ - SPObjectClass *sp_object_class; - SPPaintServerClass *ps_class; - - sp_object_class = (SPObjectClass *) klass; - ps_class = (SPPaintServerClass *) klass; - - pattern_parent_class = (SPPaintServerClass*)g_type_class_ref (SP_TYPE_PAINT_SERVER); - - sp_object_class->build = sp_pattern_build; - sp_object_class->release = sp_pattern_release; - sp_object_class->set = sp_pattern_set; - sp_object_class->update = sp_pattern_update; - sp_object_class->modified = sp_pattern_modified; - - // do we need _write? seems to work without it - ps_class->pattern_new = sp_pattern_create_pattern; + bool patternRegistered = SPFactory::instance().registerObject("svg:pattern", createPattern); } -static void -sp_pattern_init (SPPattern *pat) -{ - pat->ref = new SPPatternReference(pat); - pat->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(pattern_ref_changed), pat)); +SPPattern::SPPattern() : SPPaintServer(), SPViewBox() { + this->href = NULL; - pat->patternUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX; - pat->patternUnits_set = FALSE; + this->ref = new SPPatternReference(this); + this->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(pattern_ref_changed), this)); - pat->patternContentUnits = SP_PATTERN_UNITS_USERSPACEONUSE; - pat->patternContentUnits_set = FALSE; + this->patternUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX; + this->patternUnits_set = FALSE; - pat->patternTransform = Geom::identity(); - pat->patternTransform_set = FALSE; + this->patternContentUnits = SP_PATTERN_UNITS_USERSPACEONUSE; + this->patternContentUnits_set = FALSE; - pat->x.unset(); - pat->y.unset(); - pat->width.unset(); - pat->height.unset(); + this->patternTransform = Geom::identity(); + this->patternTransform_set = FALSE; - pat->viewBox_set = FALSE; + this->x.unset(); + this->y.unset(); + this->width.unset(); + this->height.unset(); +} - new (&pat->modified_connection) sigc::connection(); +SPPattern::~SPPattern() { } -static void -sp_pattern_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) -{ - if (((SPObjectClass *) pattern_parent_class)->build) - (* ((SPObjectClass *) pattern_parent_class)->build) (object, document, repr); - - object->readAttr( "patternUnits" ); - object->readAttr( "patternContentUnits" ); - object->readAttr( "patternTransform" ); - object->readAttr( "x" ); - object->readAttr( "y" ); - object->readAttr( "width" ); - object->readAttr( "height" ); - object->readAttr( "viewBox" ); - object->readAttr( "xlink:href" ); +void SPPattern::build(SPDocument* doc, Inkscape::XML::Node* repr) { + SPPaintServer::build(doc, repr); + + this->readAttr( "patternUnits" ); + this->readAttr( "patternContentUnits" ); + this->readAttr( "patternTransform" ); + this->readAttr( "x" ); + this->readAttr( "y" ); + this->readAttr( "width" ); + this->readAttr( "height" ); + this->readAttr( "viewBox" ); + this->readAttr( "preserveAspectRatio" ); + this->readAttr( "xlink:href" ); /* Register ourselves */ - document->addResource("pattern", object); + doc->addResource("pattern", this); } -static void sp_pattern_release(SPObject *object) -{ - SPPattern *pat = reinterpret_cast<SPPattern *>(object); - - if (object->document) { +void SPPattern::release() { + if (this->document) { // Unregister ourselves - object->document->removeResource("pattern", object); + this->document->removeResource("pattern", this); } - if (pat->ref) { - pat->modified_connection.disconnect(); - pat->ref->detach(); - delete pat->ref; - pat->ref = NULL; + if (this->ref) { + this->modified_connection.disconnect(); + this->ref->detach(); + delete this->ref; + this->ref = NULL; } - pat->modified_connection.~connection(); - - if (((SPObjectClass *) pattern_parent_class)->release) { - ((SPObjectClass *) pattern_parent_class)->release (object); - } + SPPaintServer::release(); } -static void -sp_pattern_set (SPObject *object, unsigned int key, const gchar *value) -{ - SPPattern *pat = SP_PATTERN (object); - +void SPPattern::set(unsigned int key, const gchar* value) { switch (key) { case SP_ATTR_PATTERNUNITS: if (value) { if (!strcmp (value, "userSpaceOnUse")) { - pat->patternUnits = SP_PATTERN_UNITS_USERSPACEONUSE; + this->patternUnits = SP_PATTERN_UNITS_USERSPACEONUSE; } else { - pat->patternUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX; + this->patternUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX; } - pat->patternUnits_set = TRUE; + + this->patternUnits_set = TRUE; } else { - pat->patternUnits_set = FALSE; + this->patternUnits_set = FALSE; } - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + + this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SP_ATTR_PATTERNCONTENTUNITS: if (value) { if (!strcmp (value, "userSpaceOnUse")) { - pat->patternContentUnits = SP_PATTERN_UNITS_USERSPACEONUSE; + this->patternContentUnits = SP_PATTERN_UNITS_USERSPACEONUSE; } else { - pat->patternContentUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX; + this->patternContentUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX; } - pat->patternContentUnits_set = TRUE; + + this->patternContentUnits_set = TRUE; } else { - pat->patternContentUnits_set = FALSE; + this->patternContentUnits_set = FALSE; } - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + + this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SP_ATTR_PATTERNTRANSFORM: { Geom::Affine t; + if (value && sp_svg_transform_read (value, &t)) { - pat->patternTransform = t; - pat->patternTransform_set = TRUE; + this->patternTransform = t; + this->patternTransform_set = TRUE; } else { - pat->patternTransform = Geom::identity(); - pat->patternTransform_set = FALSE; + this->patternTransform = Geom::identity(); + this->patternTransform_set = FALSE; } - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + + this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; } case SP_ATTR_X: - pat->x.readOrUnset(value); - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + this->x.readOrUnset(value); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SP_ATTR_Y: - pat->y.readOrUnset(value); - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + this->y.readOrUnset(value); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SP_ATTR_WIDTH: - pat->width.readOrUnset(value); - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + this->width.readOrUnset(value); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SP_ATTR_HEIGHT: - pat->height.readOrUnset(value); - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + this->height.readOrUnset(value); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; - case SP_ATTR_VIEWBOX: { - /* fixme: Think (Lauris) */ - double x, y, width, height; - char *eptr; - if (value) { - eptr = (gchar *) value; - x = g_ascii_strtod (eptr, &eptr); - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++; - y = g_ascii_strtod (eptr, &eptr); - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++; - width = g_ascii_strtod (eptr, &eptr); - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++; - height = g_ascii_strtod (eptr, &eptr); - while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++; - if ((width > 0) && (height > 0)) { - pat->viewBox = Geom::Rect::from_xywh(x, y, width, height); - pat->viewBox_set = TRUE; - } else { - pat->viewBox_set = FALSE; - } - } else { - pat->viewBox_set = FALSE; - } - object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); - break; - } + case SP_ATTR_VIEWBOX: + set_viewBox( value ); + this->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); + break; + + case SP_ATTR_PRESERVEASPECTRATIO: + set_preserveAspectRatio( value ); + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); + break; + case SP_ATTR_XLINK_HREF: - if ( value && pat->href && ( strcmp(value, pat->href) == 0 ) ) { + if ( value && this->href && ( strcmp(value, this->href) == 0 ) ) { /* Href unchanged, do nothing. */ } else { - g_free(pat->href); - pat->href = NULL; + g_free(this->href); + this->href = NULL; + if (value) { // First, set the href field; it's only used in the "unchanged" check above. - pat->href = g_strdup(value); + this->href = g_strdup(value); // Now do the attaching, which emits the changed signal. if (value) { try { - pat->ref->attach(Inkscape::URI(value)); + this->ref->attach(Inkscape::URI(value)); } catch (Inkscape::BadURIException &e) { g_warning("%s", e.what()); - pat->ref->detach(); + this->ref->detach(); } } else { - pat->ref->detach(); + this->ref->detach(); } } } break; + default: - if (((SPObjectClass *) pattern_parent_class)->set) - ((SPObjectClass *) pattern_parent_class)->set (object, key, value); + SPPaintServer::set(key, value); break; } } + /* TODO: do we need a ::remove_child handler? */ /* fixme: We need ::order_changed handler too (Lauris) */ -GSList *pattern_getchildren(SPPattern *pat) +static GSList *pattern_getchildren(SPPattern *pat) { GSList *l = NULL; for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { if (pat_i->firstChild()) { // find the first one with children - for (SPObject *child = pat->firstChild() ; child ; child = child->getNext() ) { - l = g_slist_prepend (l, child); - } - break; // do not go further up the chain if children are found - } + for (SPObject *child = pat->firstChild() ; child ; child = child->getNext() ) { + l = g_slist_prepend (l, child); + } + break; // do not go further up the chain if children are found + } } - return l; + return l; } -static void -sp_pattern_update (SPObject *object, SPCtx *ctx, unsigned int flags) -{ - SPPattern *pat = SP_PATTERN (object); +void SPPattern::update(SPCtx* ctx, unsigned int flags) { + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } - if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; flags &= SP_OBJECT_MODIFIED_CASCADE; - GSList *l = pattern_getchildren (pat); + GSList *l = pattern_getchildren (this); l = g_slist_reverse (l); while (l) { SPObject *child = SP_OBJECT (l->data); + sp_object_ref (child, NULL); l = g_slist_remove (l, child); + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { child->updateDisplay(ctx, flags); } + sp_object_unref (child, NULL); } } -static void -sp_pattern_modified (SPObject *object, guint flags) -{ - SPPattern *pat = SP_PATTERN (object); +void SPPattern::modified(unsigned int flags) { + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } - if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; flags &= SP_OBJECT_MODIFIED_CASCADE; - GSList *l = pattern_getchildren (pat); + GSList *l = pattern_getchildren (this); l = g_slist_reverse (l); while (l) { SPObject *child = SP_OBJECT (l->data); + sp_object_ref (child, NULL); l = g_slist_remove (l, child); + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { child->emitModified(flags); } + sp_object_unref (child, NULL); } } @@ -359,11 +298,12 @@ pattern_ref_changed(SPObject *old_ref, SPObject *ref, SPPattern *pat) if (old_ref) { pat->modified_connection.disconnect(); } + if (SP_IS_PATTERN (ref)) { pat->modified_connection = ref->connectModified(sigc::bind<2>(sigc::ptr_fun(&pattern_ref_modified), pat)); } - pattern_ref_modified (ref, 0, pat); + pattern_ref_modified (ref, 0, pat); } /** @@ -381,7 +321,7 @@ static void pattern_ref_modified (SPObject */*ref*/, guint /*flags*/, SPPattern /** Count how many times pat is used by the styles of o and its descendants */ -guint +static guint count_pattern_hrefs(SPObject *o, SPPattern *pat) { if (!o) @@ -414,56 +354,56 @@ count_pattern_hrefs(SPObject *o, SPPattern *pat) SPPattern *pattern_chain(SPPattern *pattern) { - SPDocument *document = pattern->document; + SPDocument *document = pattern->document; Inkscape::XML::Document *xml_doc = document->getReprDoc(); - Inkscape::XML::Node *defsrepr = document->getDefs()->getRepr(); + Inkscape::XML::Node *defsrepr = document->getDefs()->getRepr(); - Inkscape::XML::Node *repr = xml_doc->createElement("svg:pattern"); - repr->setAttribute("inkscape:collect", "always"); - gchar *parent_ref = g_strconcat("#", pattern->getRepr()->attribute("id"), NULL); - repr->setAttribute("xlink:href", parent_ref); - g_free (parent_ref); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:pattern"); + repr->setAttribute("inkscape:collect", "always"); + gchar *parent_ref = g_strconcat("#", pattern->getRepr()->attribute("id"), NULL); + repr->setAttribute("xlink:href", parent_ref); + g_free (parent_ref); - defsrepr->addChild(repr, NULL); - const gchar *child_id = repr->attribute("id"); - SPObject *child = document->getObjectById(child_id); - g_assert (SP_IS_PATTERN (child)); + defsrepr->addChild(repr, NULL); + const gchar *child_id = repr->attribute("id"); + SPObject *child = document->getObjectById(child_id); + g_assert (SP_IS_PATTERN (child)); - return SP_PATTERN (child); + return SP_PATTERN (child); } SPPattern * sp_pattern_clone_if_necessary (SPItem *item, SPPattern *pattern, const gchar *property) { - if (!pattern->href || pattern->hrefcount > count_pattern_hrefs(item, pattern)) { - pattern = pattern_chain (pattern); - gchar *href = g_strconcat("url(#", pattern->getRepr()->attribute("id"), ")", NULL); + if (!pattern->href || pattern->hrefcount > count_pattern_hrefs(item, pattern)) { + pattern = pattern_chain (pattern); + gchar *href = g_strconcat("url(#", pattern->getRepr()->attribute("id"), ")", NULL); - SPCSSAttr *css = sp_repr_css_attr_new (); - sp_repr_css_set_property (css, property, href); - sp_repr_css_change_recursive(item->getRepr(), css, "style"); - } - return pattern; + SPCSSAttr *css = sp_repr_css_attr_new (); + sp_repr_css_set_property (css, property, href); + sp_repr_css_change_recursive(item->getRepr(), css, "style"); + } + return pattern; } void sp_pattern_transform_multiply (SPPattern *pattern, Geom::Affine postmul, bool set) { - // this formula is for a different interpretation of pattern transforms as described in (*) in sp-pattern.cpp - // for it to work, we also need sp_object_read_attr( item, "transform"); - //pattern->patternTransform = premul * item->transform * pattern->patternTransform * item->transform.inverse() * postmul; - - // otherwise the formula is much simpler - if (set) { - pattern->patternTransform = postmul; - } else { - pattern->patternTransform = pattern_patternTransform(pattern) * postmul; - } - pattern->patternTransform_set = TRUE; + // this formula is for a different interpretation of pattern transforms as described in (*) in sp-pattern.cpp + // for it to work, we also need sp_object_read_attr( item, "transform"); + //pattern->patternTransform = premul * item->transform * pattern->patternTransform * item->transform.inverse() * postmul; + + // otherwise the formula is much simpler + if (set) { + pattern->patternTransform = postmul; + } else { + pattern->patternTransform = pattern_patternTransform(pattern) * postmul; + } + pattern->patternTransform_set = TRUE; - gchar *c=sp_svg_transform_write(pattern->patternTransform); - pattern->getRepr()->setAttribute("patternTransform", c); - g_free(c); + gchar *c=sp_svg_transform_write(pattern->patternTransform); + pattern->getRepr()->setAttribute("patternTransform", c); + g_free(c); } const gchar *pattern_tile(GSList *reprs, Geom::Rect bounds, SPDocument *document, Geom::Affine transform, Geom::Affine move) @@ -471,33 +411,33 @@ const gchar *pattern_tile(GSList *reprs, Geom::Rect bounds, SPDocument *document Inkscape::XML::Document *xml_doc = document->getReprDoc(); Inkscape::XML::Node *defsrepr = document->getDefs()->getRepr(); - Inkscape::XML::Node *repr = xml_doc->createElement("svg:pattern"); - repr->setAttribute("patternUnits", "userSpaceOnUse"); - sp_repr_set_svg_double(repr, "width", bounds.dimensions()[Geom::X]); - sp_repr_set_svg_double(repr, "height", bounds.dimensions()[Geom::Y]); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:pattern"); + repr->setAttribute("patternUnits", "userSpaceOnUse"); + sp_repr_set_svg_double(repr, "width", bounds.dimensions()[Geom::X]); + sp_repr_set_svg_double(repr, "height", bounds.dimensions()[Geom::Y]); - gchar *t=sp_svg_transform_write(transform); - repr->setAttribute("patternTransform", t); - g_free(t); + gchar *t=sp_svg_transform_write(transform); + repr->setAttribute("patternTransform", t); + g_free(t); - defsrepr->appendChild(repr); - const gchar *pat_id = repr->attribute("id"); - SPObject *pat_object = document->getObjectById(pat_id); + defsrepr->appendChild(repr); + const gchar *pat_id = repr->attribute("id"); + SPObject *pat_object = document->getObjectById(pat_id); - for (GSList *i = reprs; i != NULL; i = i->next) { - Inkscape::XML::Node *node = (Inkscape::XML::Node *)(i->data); - SPItem *copy = SP_ITEM(pat_object->appendChildRepr(node)); + for (GSList *i = reprs; i != NULL; i = i->next) { + Inkscape::XML::Node *node = (Inkscape::XML::Node *)(i->data); + SPItem *copy = SP_ITEM(pat_object->appendChildRepr(node)); - Geom::Affine dup_transform; - if (!sp_svg_transform_read (node->attribute("transform"), &dup_transform)) - dup_transform = Geom::identity(); - dup_transform *= move; + Geom::Affine dup_transform; + if (!sp_svg_transform_read (node->attribute("transform"), &dup_transform)) + dup_transform = Geom::identity(); + dup_transform *= move; copy->doWriteTransform(copy->getRepr(), dup_transform, NULL, false); } - Inkscape::GC::release(repr); - return pat_id; + Inkscape::GC::release(repr); + return pat_id; } SPPattern *pattern_getroot(SPPattern *pat) @@ -515,73 +455,73 @@ SPPattern *pattern_getroot(SPPattern *pat) // Access functions that look up fields up the chain of referenced patterns and return the first one which is set // FIXME: all of them must use chase_hrefs the same as in SPGradient, to avoid lockup on circular refs -guint pattern_patternUnits (SPPattern *pat) +guint pattern_patternUnits (SPPattern const *pat) { - for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->patternUnits_set) - return pat_i->patternUnits; - } - return pat->patternUnits; + for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->patternUnits_set) + return pat_i->patternUnits; + } + return pat->patternUnits; } -guint pattern_patternContentUnits (SPPattern *pat) +guint pattern_patternContentUnits (SPPattern const *pat) { - for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->patternContentUnits_set) - return pat_i->patternContentUnits; - } - return pat->patternContentUnits; + for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->patternContentUnits_set) + return pat_i->patternContentUnits; + } + return pat->patternContentUnits; } Geom::Affine const &pattern_patternTransform(SPPattern const *pat) { - for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->patternTransform_set) - return pat_i->patternTransform; - } - return pat->patternTransform; + for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->patternTransform_set) + return pat_i->patternTransform; + } + return pat->patternTransform; } -gdouble pattern_x (SPPattern *pat) +gdouble pattern_x (SPPattern const *pat) { - for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->x._set) - return pat_i->x.computed; - } - return 0; + for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->x._set) + return pat_i->x.computed; + } + return 0; } -gdouble pattern_y (SPPattern *pat) +gdouble pattern_y (SPPattern const *pat) { - for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->y._set) - return pat_i->y.computed; - } - return 0; + for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->y._set) + return pat_i->y.computed; + } + return 0; } -gdouble pattern_width (SPPattern *pat) +gdouble pattern_width (SPPattern const* pat) { - for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->width._set) - return pat_i->width.computed; - } - return 0; + for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->width._set) + return pat_i->width.computed; + } + return 0; } -gdouble pattern_height (SPPattern *pat) +gdouble pattern_height (SPPattern const *pat) { - for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->height._set) - return pat_i->height.computed; - } - return 0; + for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->height._set) + return pat_i->height.computed; + } + return 0; } -Geom::OptRect pattern_viewBox (SPPattern *pat) +Geom::OptRect pattern_viewBox (SPPattern const *pat) { Geom::OptRect viewbox; - for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { if (pat_i->viewBox_set) { viewbox = pat_i->viewBox; break; @@ -590,10 +530,10 @@ Geom::OptRect pattern_viewBox (SPPattern *pat) return viewbox; } -bool pattern_hasItemChildren (SPPattern *pat) +static bool pattern_hasItemChildren (SPPattern const *pat) { bool hasChildren = false; - for (SPObject *child = pat->firstChild() ; child && !hasChildren ; child = child->getNext() ) { + for (SPObject const *child = pat->firstChild() ; child && !hasChildren ; child = child->getNext() ) { if (SP_IS_ITEM(child)) { hasChildren = true; } @@ -601,26 +541,21 @@ bool pattern_hasItemChildren (SPPattern *pat) return hasChildren; } -static cairo_pattern_t * -sp_pattern_create_pattern(SPPaintServer *ps, - cairo_t *base_ct, - Geom::OptRect const &bbox, - double opacity) -{ - SPPattern *pat = SP_PATTERN (ps); - Geom::Affine ps2user; - Geom::Affine vb2ps = Geom::identity(); +cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &bbox, double opacity) { + bool needs_opacity = (1.0 - opacity) >= 1e-3; bool visible = opacity >= 1e-3; - if (!visible) + if (!visible) { return NULL; + } /* Show items */ SPPattern *shown = NULL; - for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + + for (SPPattern *pat_i = this; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { // find the first one with item children - if (pat_i && SP_IS_OBJECT (pat_i) && pattern_hasItemChildren(pat_i)) { + if (pat_i && SP_IS_OBJECT(pat_i) && pattern_hasItemChildren(pat_i)) { shown = pat_i; break; // do not go further up the chain if children are found } @@ -646,74 +581,114 @@ sp_pattern_create_pattern(SPPaintServer *ps, } } - if (pat->viewBox_set) { - Geom::Rect vb = *pattern_viewBox(pat); - gdouble tmp_x = pattern_width (pat) / vb.width(); - gdouble tmp_y = pattern_height (pat) / vb.height(); - - // FIXME: preserveAspectRatio must be taken into account here too! - vb2ps = Geom::Affine(tmp_x, 0.0, 0.0, tmp_y, pattern_x(pat) - vb.left() * tmp_x, pattern_y(pat) - vb.top() * tmp_y); + // ****** Geometry ****** + // + // * "width" and "height" determine tile size. + // * "viewBox" (if defined) or "patternContentUnits" determines placement of content inside + // tile. + // * "x", "y", and "patternTransform" transform tile to user space after tile is generated. + + // These functions recursively search up the tree to find the values. + double tile_x = pattern_x(this); + double tile_y = pattern_y(this); + double tile_width = pattern_width(this); + double tile_height = pattern_height(this); + if (pattern_patternUnits(this) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) { + tile_x *= bbox->width(); + tile_y *= bbox->height(); + tile_width *= bbox->width(); + tile_height *= bbox->height(); } - ps2user = pattern_patternTransform(pat); - if (!pat->viewBox_set && pattern_patternContentUnits (pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) { - /* BBox to user coordinate system */ - Geom::Affine bbox2user (bbox->width(), 0.0, 0.0, bbox->height(), bbox->left(), bbox->top()); - ps2user *= bbox2user; + // Pattern size in pattern space + Geom::Rect pattern_tile = Geom::Rect::from_xywh(0, 0, tile_width, tile_height); + + // Content to tile (pattern space) + Geom::Affine content2ps; + if (this->viewBox_set) { + // viewBox to pattern server (using SPViewBox) + viewBox = *pattern_viewBox(this); + c2p.setIdentity(); + apply_viewbox( pattern_tile ); + content2ps = c2p; + } else { + + // Content to bbox + if (pattern_patternContentUnits (this) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) { + content2ps = Geom::Affine(bbox->width(), 0.0, 0.0, bbox->height(), 0,0); + } } - ps2user = Geom::Translate (pattern_x (pat), pattern_y (pat)) * ps2user; - Geom::Rect pattern_tile = Geom::Rect::from_xywh(pattern_x(pat), pattern_y(pat), - pattern_width(pat), pattern_height(pat)); - if (pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) { - // interpret x, y, width, height in relation to bbox - Geom::Affine bbox2user(bbox->width(), 0.0, 0.0, bbox->height(), bbox->left(), bbox->top()); - pattern_tile = pattern_tile * bbox2user; - } + // Tile (pattern space) to user. + Geom::Affine ps2user = Geom::Translate(tile_x,tile_y) * pattern_patternTransform(this); + + // Transform of object with pattern (includes screen scaling) cairo_matrix_t cm; cairo_get_matrix(base_ct, &cm); Geom::Affine full(cm.xx, cm.yx, cm.xy, cm.yy, 0, 0); - // oversample the pattern slightly + // The DrawingSurface class handles the mapping from "logical space" + // (coordinates in the rendering) to "physical space" (surface pixels). + // An oversampling is done as the pattern may not pixel align with the final surface. + // The cairo surface is created when the DrawingContext is declared. + + // Oversample the pattern // TODO: find optimum value // TODO: this is lame. instead of using descrim(), we should extract // the scaling component from the complete matrix and use it // to find the optimum tile size for rendering - Geom::Point c(pattern_tile.dimensions()*vb2ps.descrim()*ps2user.descrim()*full.descrim()*1.1); - c[Geom::X] = ceil(c[Geom::X]); - c[Geom::Y] = ceil(c[Geom::Y]); - + // c is number of pixels in buffer x and y. + // Scale factor of 1.1 is too small... see bug #1251039 + Geom::Point c(pattern_tile.dimensions()*ps2user.descrim()*full.descrim()*2.0); + + // Create drawing surface with size of pattern tile (in pattern space) but with number of pixels + // based on required resolution (c). + Inkscape::DrawingSurface pattern_surface(pattern_tile, c.ceil()); + Inkscape::DrawingContext dc(pattern_surface); + + pattern_tile *= pattern_surface.drawingTransform(); Geom::IntRect one_tile = pattern_tile.roundOutwards(); - Inkscape::DrawingSurface temp(pattern_tile, c.ceil()); - Inkscape::DrawingContext ct(temp); - // render pattern. + // Render pattern. if (needs_opacity) { - ct.pushGroup(); // this group is for pattern + opacity + dc.pushGroup(); // this group is for pattern + opacity } // TODO: make sure there are no leaks. - Inkscape::UpdateContext ctx; - ctx.ctm = vb2ps; + Inkscape::UpdateContext ctx; // UpdateContext is structure with only ctm! + ctx.ctm = content2ps * pattern_surface.drawingTransform(); + dc.transform( pattern_surface.drawingTransform().inverse() ); drawing.update(Geom::IntRect::infinite(), ctx); - drawing.render(ct, one_tile); + + // Render drawing to pattern_surface via drawing context, this calls root->render + // which is really DrawingItem->render(). + drawing.render(dc, one_tile); for (SPObject *child = shown->firstChild() ; child != NULL; child = child->getNext() ) { if (SP_IS_ITEM (child)) { SP_ITEM(child)->invoke_hide(dkey); } } + // Uncomment to debug + // cairo_surface_t* raw = pattern_surface.raw(); + // std::cout << " cairo_surface (sp-pattern): " + // << " width: " << cairo_image_surface_get_width( raw ) + // << " height: " << cairo_image_surface_get_height( raw ) + // << std::endl; + // std::string filename = "sp-pattern-" + (std::string)getId() + ".png"; + // cairo_surface_write_to_png( pattern_surface.raw(), filename.c_str() ); + if (needs_opacity) { - ct.popGroupToSource(); // pop raw pattern - ct.paint(opacity); // apply opacity + dc.popGroupToSource(); // pop raw pattern + dc.paint(opacity); // apply opacity } - cairo_pattern_t *cp = cairo_pattern_create_for_surface(temp.raw()); - + cairo_pattern_t *cp = cairo_pattern_create_for_surface(pattern_surface.raw()); // Apply transformation to user space. Also compensate for oversampling. - ink_cairo_pattern_set_matrix(cp, ps2user.inverse() * temp.drawingTransform()); + ink_cairo_pattern_set_matrix(cp, ps2user.inverse() * pattern_surface.drawingTransform() ); + cairo_pattern_set_extend(cp, CAIRO_EXTEND_REPEAT); return cp; @@ -728,4 +703,4 @@ sp_pattern_create_pattern(SPPaintServer *ps, fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |
