Code

Adding axis detection to new input dialog
[inkscape.git] / src / sp-pattern.cpp
index beea89b02ced8f5c62ace242fde0f9d03ef8ab5f..c3d35de7230c579bc29b6a5161764f58551f4ff6 100644 (file)
  * Released under GNU GPL, read the file 'COPYING' for more information
  */
 
-#include "config.h"
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
 
+#include <cstring>
+#include <string>
 #include <libnr/nr-matrix-ops.h>
 #include "libnr/nr-matrix-fns.h"
 #include <libnr/nr-translate-matrix-ops.h>
@@ -27,6 +31,9 @@
 #include "sp-pattern.h"
 #include "xml/repr.h"
 
+#include <sigc++/functors/ptr_fun.h>
+#include <sigc++/adaptors/bind.h>
+
 /*
  * Pattern
  */
@@ -137,6 +144,8 @@ sp_pattern_init (SPPattern *pat)
        pat->height.unset();
 
        pat->viewBox_set = FALSE;
+
+       new (&pat->modified_connection) sigc::connection();
 }
 
 static void
@@ -172,13 +181,14 @@ sp_pattern_release (SPObject *object)
        }
 
        if (pat->ref) {
-               if (pat->ref->getObject())
-                       sp_signal_disconnect_by_data(pat->ref->getObject(), pat);
+               pat->modified_connection.disconnect();
                pat->ref->detach();
                delete pat->ref;
                pat->ref = NULL;
        }
 
+       pat->modified_connection.~connection();
+
        if (((SPObjectClass *) pattern_parent_class)->release)
                ((SPObjectClass *) pattern_parent_class)->release (object);
 }
@@ -332,7 +342,7 @@ sp_pattern_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::
 }
 
 /* TODO: do we need a ::remove_child handler? */
+
 /* fixme: We need ::order_changed handler too (Lauris) */
 
 GSList *
@@ -403,10 +413,10 @@ static void
 pattern_ref_changed(SPObject *old_ref, SPObject *ref, SPPattern *pat)
 {
        if (old_ref) {
-               sp_signal_disconnect_by_data(old_ref, pat);
+               pat->modified_connection.disconnect();
        }
        if (SP_IS_PATTERN (ref)) {
-               g_signal_connect(G_OBJECT (ref), "modified", G_CALLBACK (pattern_ref_modified), pat);
+               pat->modified_connection = ref->connectModified(sigc::bind<2>(sigc::ptr_fun(&pattern_ref_modified), pat));
        }
 
        pattern_ref_modified (ref, 0, pat);
@@ -416,7 +426,7 @@ pattern_ref_changed(SPObject *old_ref, SPObject *ref, SPPattern *pat)
 Gets called when the referenced <pattern> is changed
 */
 static void
-pattern_ref_modified (SPObject *ref, guint flags, SPPattern *pattern)
+pattern_ref_modified (SPObject */*ref*/, guint /*flags*/, SPPattern *pattern)
 {
        if (SP_IS_OBJECT (pattern))
                SP_OBJECT (pattern)->requestModified(SP_OBJECT_MODIFIED_FLAG);
@@ -432,9 +442,10 @@ SPPattern *
 pattern_chain (SPPattern *pattern)
 {
        SPDocument *document = SP_OBJECT_DOCUMENT (pattern);
+        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
        Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (SP_DOCUMENT_DEFS (document));
 
-       Inkscape::XML::Node *repr = sp_repr_new ("svg:pattern");
+       Inkscape::XML::Node *repr = xml_doc->createElement("svg:pattern");
        repr->setAttribute("inkscape:collect", "always");
        gchar *parent_ref = g_strconcat ("#", SP_OBJECT_REPR(pattern)->attribute("id"), NULL);
        repr->setAttribute("xlink:href",  parent_ref);
@@ -458,7 +469,7 @@ sp_pattern_clone_if_necessary (SPItem *item, SPPattern *pattern, const gchar *pr
                SPCSSAttr *css = sp_repr_css_attr_new ();
                sp_repr_css_set_property (css, property, href);
                sp_repr_css_change_recursive (SP_OBJECT_REPR (item), css, "style");
-       } 
+       }
        return pattern;
 }
 
@@ -477,31 +488,25 @@ sp_pattern_transform_multiply (SPPattern *pattern, NR::Matrix postmul, bool set)
        }
        pattern->patternTransform_set = TRUE;
 
-       gchar c[256];
-       if (sp_svg_transform_write(c, 256, pattern->patternTransform)) {
-               SP_OBJECT_REPR(pattern)->setAttribute("patternTransform", c);
-       } else {
-               SP_OBJECT_REPR(pattern)->setAttribute("patternTransform", NULL);
-       }
+       gchar *c=sp_svg_transform_write(pattern->patternTransform);
+       SP_OBJECT_REPR(pattern)->setAttribute("patternTransform", c);
+       g_free(c);
 }
 
 const gchar *
 pattern_tile (GSList *reprs, NR::Rect bounds, SPDocument *document, NR::Matrix transform, NR::Matrix move)
 {
+       Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
        Inkscape::XML::Node *defsrepr = SP_OBJECT_REPR (SP_DOCUMENT_DEFS (document));
 
-       Inkscape::XML::Node *repr = sp_repr_new ("svg:pattern");
+       Inkscape::XML::Node *repr = xml_doc->createElement("svg:pattern");
        repr->setAttribute("patternUnits", "userSpaceOnUse");
        sp_repr_set_svg_double(repr, "width", bounds.extent(NR::X));
        sp_repr_set_svg_double(repr, "height", bounds.extent(NR::Y));
 
-       gchar t[256];
-       if (sp_svg_transform_write(t, 256, transform)) {
-               repr->setAttribute("patternTransform", t);
-       } else {
-               repr->setAttribute("patternTransform", NULL);
-       }
-
+       gchar *t=sp_svg_transform_write(transform);
+       repr->setAttribute("patternTransform", t);
+       g_free(t);
 
        defsrepr->appendChild(repr);
        const gchar *pat_id = repr->attribute("id");
@@ -512,7 +517,7 @@ pattern_tile (GSList *reprs, NR::Rect bounds, SPDocument *document, NR::Matrix t
                SPItem *copy = SP_ITEM(pat_object->appendChildRepr(node));
 
                NR::Matrix dup_transform;
-               if (!sp_svg_transform_read (node->attribute("transform"), &dup_transform)) 
+               if (!sp_svg_transform_read (node->attribute("transform"), &dup_transform))
                        dup_transform = NR::identity();
                dup_transform *= move;
 
@@ -524,7 +529,7 @@ pattern_tile (GSList *reprs, NR::Rect bounds, SPDocument *document, NR::Matrix t
 }
 
 SPPattern *
-pattern_getroot (SPPattern *pat) 
+pattern_getroot (SPPattern *pat)
 {
        for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) {
                if (sp_object_first_child(SP_OBJECT(pat_i))) { // find the first one with children
@@ -537,6 +542,7 @@ 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)
 {
@@ -631,7 +637,7 @@ Creates a painter (i.e. the thing that does actual filling at the given zoom).
 See (*) below for why the parent_transform may be necessary.
 */
 static SPPainter *
-sp_pattern_painter_new (SPPaintServer *ps, NR::Matrix const &full_transform, NR::Matrix const &parent_transform, const NRRect *bbox)
+sp_pattern_painter_new (SPPaintServer *ps, NR::Matrix const &full_transform, NR::Matrix const &/*parent_transform*/, const NRRect *bbox)
 {
        SPPattern *pat = SP_PATTERN (ps);
        SPPatPainter *pp = g_new (SPPatPainter, 1);
@@ -708,7 +714,7 @@ sp_pattern_painter_new (SPPaintServer *ps, NR::Matrix const &full_transform, NR:
                        pcs2px = pattern_patternTransform(pat) * full_transform;
                }
 
-               pcs2px = NR::translate (pattern_x (pat), pattern_y (pat)) * pcs2px; 
+               pcs2px = NR::translate (pattern_x (pat), pattern_y (pat)) * pcs2px;
 
                pcs2px.copyto (&pp->pcs2px);
        }
@@ -783,7 +789,7 @@ sp_pattern_painter_new (SPPaintServer *ps, NR::Matrix const &full_transform, NR:
        }
        nr_arena_item_invoke_update (pp->root, NULL, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_ALL);
        if ( pp->use_cached_tile ) {
-               nr_arena_item_invoke_render (pp->root, &pp->cached_bbox, &pp->cached_tile, 0);
+               nr_arena_item_invoke_render (NULL, pp->root, &pp->cached_bbox, &pp->cached_tile, 0);
        } else {
                // nothing to do now
        }
@@ -792,7 +798,7 @@ sp_pattern_painter_new (SPPaintServer *ps, NR::Matrix const &full_transform, NR:
 }
 
 static void
-sp_pattern_painter_free (SPPaintServer *ps, SPPainter *painter)
+sp_pattern_painter_free (SPPaintServer */*ps*/, SPPainter *painter)
 {
        SPPatPainter *pp = (SPPatPainter *) painter;
        SPPattern *pat = pp->pat;
@@ -944,36 +950,52 @@ sp_pat_fill (SPPainter *painter, NRPixBlock *pb)
                ba.y0 = pb->area.y0;
                ba.x1 = pb->area.x1;
                ba.y1 = pb->area.y1;
-               nr_rect_d_matrix_transform (&psa, &ba, &pp->px2ps);
                
-               psa.x0 = floor ((psa.x0 - pattern_x (pp->pat)) / pattern_width (pp->pat)) -1;
-               psa.y0 = floor ((psa.y0 - pattern_y (pp->pat)) / pattern_height (pp->pat)) -1;
-               psa.x1 = ceil ((psa.x1 - pattern_x (pp->pat)) / pattern_width (pp->pat)) +1;
-               psa.y1 = ceil ((psa.y1 - pattern_y (pp->pat)) / pattern_height (pp->pat)) +1;
-               
-               for (y = psa.y0; y < psa.y1; y++) {
-                       for (x = psa.x0; x < psa.x1; x++) {
-                               NRPixBlock ppb;
-                               double psx, psy;
-                               
-                               psx = x * pattern_width (pp->pat);
-                               psy = y * pattern_height (pp->pat);
-                               
-                               area.x0 = (gint32)(pb->area.x0 - (pp->ps2px.c[0] * psx + pp->ps2px.c[2] * psy));
-                               area.y0 = (gint32)(pb->area.y0 - (pp->ps2px.c[1] * psx + pp->ps2px.c[3] * psy));
-                               area.x1 = area.x0 + pb->area.x1 - pb->area.x0;
-                               area.y1 = area.y0 + pb->area.y1 - pb->area.y0;
-                               
-                               // We do not update here anymore 
-
-                               // Set up buffer 
-                               // fixme: (Lauris) 
-                               nr_pixblock_setup_extern (&ppb, pb->mode, area.x0, area.y0, area.x1, area.y1, NR_PIXBLOCK_PX (pb), pb->rs, FALSE, FALSE);
-                               
-                               nr_arena_item_invoke_render (pp->root, &area, &ppb, 0);
-                               
-                               nr_pixblock_release (&ppb);
-                       }
-               }
+        // Trying to solve this bug: https://bugs.launchpad.net/inkscape/+bug/167416
+        // Bail out if the transformation matrix has extreme values. If we bail out
+        // however, then something (which was meaningless anyway) won't be rendered, 
+        // which is better than getting stuck in a virtually infinite loop
+        if (fabs(pp->px2ps.c[0]) < 1e6 && 
+            fabs(pp->px2ps.c[3]) < 1e6 &&
+            fabs(pp->px2ps.c[4]) < 1e6 &&
+            fabs(pp->px2ps.c[5]) < 1e6) 
+        {
+            nr_rect_d_matrix_transform (&psa, &ba, &pp->px2ps);
+               
+               psa.x0 = floor ((psa.x0 - pattern_x (pp->pat)) / pattern_width (pp->pat)) -1;
+               psa.y0 = floor ((psa.y0 - pattern_y (pp->pat)) / pattern_height (pp->pat)) -1;
+               psa.x1 = ceil ((psa.x1 - pattern_x (pp->pat)) / pattern_width (pp->pat)) +1;
+               psa.y1 = ceil ((psa.y1 - pattern_y (pp->pat)) / pattern_height (pp->pat)) +1;
+               
+            // If psa is too wide or tall, then something must be wrong! This is due to
+            // nr_rect_d_matrix_transform (&psa, &ba, &pp->px2ps) using a weird transformation matrix pp->px2ps.
+            g_assert(std::abs(psa.x1 - psa.x0) < 1e6);
+            g_assert(std::abs(psa.y1 - psa.y0) < 1e6);
+            
+            for (y = psa.y0; y < psa.y1; y++) {
+                       for (x = psa.x0; x < psa.x1; x++) {
+                               NRPixBlock ppb;
+                               double psx, psy;
+                               
+                               psx = x * pattern_width (pp->pat);
+                               psy = y * pattern_height (pp->pat);
+                               
+                               area.x0 = (gint32)(pb->area.x0 - (pp->ps2px.c[0] * psx + pp->ps2px.c[2] * psy));
+                               area.y0 = (gint32)(pb->area.y0 - (pp->ps2px.c[1] * psx + pp->ps2px.c[3] * psy));
+                               area.x1 = area.x0 + pb->area.x1 - pb->area.x0;
+                               area.y1 = area.y0 + pb->area.y1 - pb->area.y0;
+                               
+                               // We do not update here anymore
+    
+                               // Set up buffer
+                               // fixme: (Lauris)
+                               nr_pixblock_setup_extern (&ppb, pb->mode, area.x0, area.y0, area.x1, area.y1, NR_PIXBLOCK_PX (pb), pb->rs, FALSE, FALSE);
+                               
+                               nr_arena_item_invoke_render (NULL, pp->root, &area, &ppb, 0);
+                               
+                               nr_pixblock_release (&ppb);
+                       }
+               }
+        } 
        }
 }