Code

Optimized text output by not repeating glyph coordinates when they are identical
[inkscape.git] / src / sp-image.cpp
index fe480b22b382e8171566d2f8b2d338d32eaf169c..d535874e93c34d8ba69d0b91bb6fa1774704474e 100644 (file)
@@ -21,6 +21,7 @@
 //#define GDK_PIXBUF_ENABLE_BACKEND 1
 //#include <gdk-pixbuf/gdk-pixbuf-io.h>
 #include "display/nr-arena-image.h"
+#include <display/curve.h>
 
 //Added for preserveAspectRatio support -- EAF
 #include "enums.h"
 #include "brokenimage.xpm"
 #include "document.h"
 #include "sp-image.h"
+#include "sp-clippath.h"
 #include <glibmm/i18n.h>
 #include "xml/quote.h"
 #include <xml/repr.h>
 
+#include "libnr/nr-matrix-fns.h"
+
 #include "io/sys.h"
 #include <png.h>
 #if ENABLE_LCMS
@@ -65,8 +69,10 @@ static gchar * sp_image_description (SPItem * item);
 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p);
 static NRArenaItem *sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
 static NR::Matrix sp_image_set_transform (SPItem *item, NR::Matrix const &xform);
+static void sp_image_set_curve(SPImage *image);
+
 
-GdkPixbuf * sp_image_repr_read_image (Inkscape::XML::Node * repr);
+GdkPixbuf *sp_image_repr_read_image (const gchar *href, const gchar *absref, const gchar *base);
 static GdkPixbuf *sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf);
 static void sp_image_update_canvas_image (SPImage *image);
 static GdkPixbuf * sp_image_repr_read_dataURI (const gchar * uri_data);
@@ -119,7 +125,7 @@ namespace IO {
 class PushPull
 {
 public:
-    bool    first;
+    gboolean    first;
     FILE*       fp;
     guchar*     scratch;
     gsize       size;
@@ -135,9 +141,9 @@ public:
                  offset(0),
                  loader(0) {};
 
-    bool readMore()
+    gboolean readMore()
     {
-        bool good = FALSE;
+        gboolean good = FALSE;
         if ( offset )
         {
             g_memmove( scratch, scratch + offset, used - offset );
@@ -211,7 +217,7 @@ void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length )
     PushPull* youme = (PushPull*)png_get_io_ptr(png_ptr);
 
     gsize filled = 0;
-    bool canRead = TRUE;
+    gboolean canRead = TRUE;
 
     while ( filled < length && canRead )
     {
@@ -254,8 +260,8 @@ GdkPixbuf*  pixbuf_new_from_file( const char *filename, GError **error )
 
             // short buffer
             guchar scratch[1024];
-            bool latter = FALSE;
-            bool isPng = FALSE;
+            gboolean latter = FALSE;
+            gboolean isPng = FALSE;
             png_structp pngPtr = NULL;
             png_infop infoPtr = NULL;
             //png_infop endPtr = NULL;
@@ -375,7 +381,7 @@ GdkPixbuf*  pixbuf_new_from_file( const char *filename, GError **error )
                 }
             }
 
-            bool ok = gdk_pixbuf_loader_close(loader, &err);
+            gboolean ok = gdk_pixbuf_loader_close(loader, &err);
             if ( ok )
             {
                 buf = gdk_pixbuf_loader_get_pixbuf( loader );
@@ -502,6 +508,7 @@ sp_image_init (SPImage *image)
        image->width.unset();
        image->height.unset();
        image->aspect_align = SP_ASPECT_NONE;
+       image->curve = NULL;
 }
 
 static void
@@ -551,6 +558,10 @@ sp_image_release (SPObject *object)
        }
 #endif // ENABLE_LCMS
 
+    if (image->curve) {
+               image->curve = sp_curve_unref (image->curve);
+       }
+
        if (((SPObjectClass *) parent_class)->release)
                ((SPObjectClass *) parent_class)->release (object);
 }
@@ -676,14 +687,17 @@ sp_image_set (SPObject *object, unsigned int key, const gchar *value)
                        ((SPObjectClass *) (parent_class))->set (object, key, value);
                break;
        }
+       
+       sp_image_set_curve(image); //creates a curve at the image's boundary for snapping
 }
 
 static void
 sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
 {
-       SPImage *image;
+    SPImage *image;
 
-       image = (SPImage *) object;
+    image = (SPImage *) object;
+    SPDocument *doc = SP_OBJECT_DOCUMENT(object);
 
        if (((SPObjectClass *) (parent_class))->update)
                ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
@@ -695,7 +709,10 @@ sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
                }
                if (image->href) {
                        GdkPixbuf *pixbuf;
-                       pixbuf = sp_image_repr_read_image (object->repr);
+                       pixbuf = sp_image_repr_read_image (
+                    object->repr->attribute("xlink:href"),
+                    object->repr->attribute("sodipodi:absref"),
+                    doc->base);
                        if (pixbuf) {
                                pixbuf = sp_image_pixbuf_force_rgba (pixbuf);
 // BLIP
@@ -736,7 +753,7 @@ sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
                                                         intent = INTENT_PERCEPTUAL;
                                                 }
                                                 cmsHPROFILE destProf = cmsCreate_sRGBProfile();
-                                                cmsHTRANSFORM transf = cmsCreateTransform( prof, 
+                                                cmsHTRANSFORM transf = cmsCreateTransform( prof,
                                                                                            TYPE_RGBA_8,
                                                                                            destProf,
                                                                                            TYPE_RGBA_8,
@@ -869,7 +886,6 @@ sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
                                }
                        }
        }
-
        sp_image_update_canvas_image ((SPImage *) object);
 }
 
@@ -881,7 +897,8 @@ sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
        image = SP_IMAGE (object);
 
        if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
-               repr = sp_repr_new ("svg:image");
+                Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
+               repr = xml_doc->createElement("svg:image");
        }
 
        repr->setAttribute("xlink:href", image->href);
@@ -969,7 +986,7 @@ sp_image_description(SPItem *item)
                 : xml_quote_strdup(image->href);
         } else {
             g_warning("Attempting to call strncmp() with a null pointer.");
-            href_desc = g_strdup(_("(null_pointer)")); // we call g_free() on href_desc
+            href_desc = g_strdup("(null_pointer)"); // we call g_free() on href_desc
         }
 
        char *ret = ( image->pixbuf == NULL
@@ -996,6 +1013,7 @@ sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flag
        if (image->pixbuf) {
                pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
                rs = gdk_pixbuf_get_rowstride (image->pixbuf);
+                nr_arena_image_set_style(NR_ARENA_IMAGE(ai), SP_OBJECT_STYLE(SP_OBJECT(item)));
                if (image->aspect_align == SP_ASPECT_NONE)
                        nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
                                           gdk_pixbuf_get_pixels (image->pixbuf),
@@ -1028,55 +1046,72 @@ sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flag
  */
 
 GdkPixbuf *
-sp_image_repr_read_image (Inkscape::XML::Node * repr)
+sp_image_repr_read_image (const gchar *href, const gchar *absref, const gchar *base)
 {
-       const gchar * filename, * docbase;
-       gchar * fullname;
-       GdkPixbuf * pixbuf;
-
-       filename = repr->attribute("xlink:href");
-       if (filename == NULL) filename = repr->attribute("href"); /* FIXME */
-       if (filename != NULL) {
-               if (strncmp (filename,"file:",5) == 0) {
-                       fullname = g_filename_from_uri(filename, NULL, NULL);
-                       if (fullname) {
-                               // TODO check this. Was doing a UTF-8 to filename conversion here.
-                               pixbuf = Inkscape::IO::pixbuf_new_from_file (fullname, NULL);
-                               if (pixbuf != NULL) return pixbuf;
-                       }
-               } else if (strncmp (filename,"data:",5) == 0) {
-                       /* data URI - embedded image */
-                       filename += 5;
-                       pixbuf = sp_image_repr_read_dataURI (filename);
-                       if (pixbuf != NULL) return pixbuf;
-               } else if (!g_path_is_absolute (filename)) {
-                       /* try to load from relative pos */
-                       docbase = sp_repr_document_root (sp_repr_document (repr))->attribute("sodipodi:docbase");
-                       if (!docbase) docbase = ".";
-                       fullname = g_build_filename(docbase, filename, NULL);
-                       pixbuf = Inkscape::IO::pixbuf_new_from_file( fullname, NULL );
-                       g_free (fullname);
-                       if (pixbuf != NULL) return pixbuf;
-               } else {
-                       /* try absolute filename */
-                       pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
-                       if (pixbuf != NULL) return pixbuf;
-               }
-       }
-       /* at last try to load from sp absolute path name */
-       filename = repr->attribute("sodipodi:absref");
-       if (filename != NULL) {
-               pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
-               if (pixbuf != NULL) return pixbuf;
-       }
-       /* Nope: We do not find any valid pixmap file :-( */
-       pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) brokenimage_xpm);
+    const gchar *filename, *docbase;
+    gchar *fullname;
+    GdkPixbuf *pixbuf;
+
+    filename = href;
+    if (filename != NULL) {
+        if (strncmp (filename,"file:",5) == 0) {
+            fullname = g_filename_from_uri(filename, NULL, NULL);
+            if (fullname) {
+                // TODO check this. Was doing a UTF-8 to filename conversion here.
+                pixbuf = Inkscape::IO::pixbuf_new_from_file (fullname, NULL);
+                if (pixbuf != NULL) return pixbuf;
+            }
+        } else if (strncmp (filename,"data:",5) == 0) {
+            /* data URI - embedded image */
+            filename += 5;
+            pixbuf = sp_image_repr_read_dataURI (filename);
+            if (pixbuf != NULL) return pixbuf;
+        } else {
 
-       /* It should be included xpm, so if it still does not does load, */
-       /* our libraries are broken */
-       g_assert (pixbuf != NULL);
+            if (!g_path_is_absolute (filename)) {
+                /* try to load from relative pos combined with document base*/
+                docbase = base;
+                if (!docbase) docbase = ".";
+                fullname = g_build_filename(docbase, filename, NULL);
+
+                // document base can be wrong (on the temporary doc when importing bitmap from a
+                // different dir) or unset (when doc is not saved yet), so we check for base+href existence first,
+                // and if it fails, we also try to use bare href regardless of its g_path_is_absolute
+                if (g_file_test (fullname, G_FILE_TEST_EXISTS) && !g_file_test (fullname, G_FILE_TEST_IS_DIR)) {
+                    pixbuf = Inkscape::IO::pixbuf_new_from_file( fullname, NULL );
+                    g_free (fullname);
+                    if (pixbuf != NULL) return pixbuf;
+                }
+            }
 
-       return pixbuf;
+            /* try filename as absolute */
+            if (g_file_test (filename, G_FILE_TEST_EXISTS) && !g_file_test (filename, G_FILE_TEST_IS_DIR)) {
+                pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
+                if (pixbuf != NULL) return pixbuf;
+            }
+        }
+    }
+
+    /* at last try to load from sp absolute path name */
+    filename = absref;
+    if (filename != NULL) {
+        // using absref is outside of SVG rules, so we must at least warn the user
+        if ( base != NULL && href != NULL )
+               g_warning ("<image xlink:href=\"%s\"> did not resolve to a valid image file (base dir is %s), now trying sodipodi:absref=\"%s\"", href, base, absref);
+               else
+                   g_warning ("xlink:href did not resolve to a valid image file, now trying sodipodi:absref=\"%s\"", absref);
+
+        pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
+        if (pixbuf != NULL) return pixbuf;
+    }
+    /* Nope: We do not find any valid pixmap file :-( */
+    pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) brokenimage_xpm);
+
+    /* It should be included xpm, so if it still does not does load, */
+    /* our libraries are broken */
+    g_assert (pixbuf != NULL);
+
+    return pixbuf;
 }
 
 static GdkPixbuf *
@@ -1116,6 +1151,7 @@ sp_image_update_canvas_image (SPImage *image)
        for (v = item->display; v != NULL; v = v->next) {
                pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
                rs = gdk_pixbuf_get_rowstride (image->pixbuf);
+                nr_arena_image_set_style (NR_ARENA_IMAGE(v->arenaitem), SP_OBJECT_STYLE(SP_OBJECT(image)));
                if (image->aspect_align == SP_ASPECT_NONE) {
                        nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
                                           gdk_pixbuf_get_pixels (image->pixbuf),
@@ -1140,9 +1176,30 @@ sp_image_update_canvas_image (SPImage *image)
 
 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p)
 {
-     if (((SPItemClass *) parent_class)->snappoints) {
-         ((SPItemClass *) parent_class)->snappoints (item, p);
-     }
+    /* An image doesn't have any nodes to snap, but still we want to be able snap one image 
+    to another. Therefore we will create some snappoints at the corner, similar to a rect. If
+    the image is rotated, then the snappoints will rotate with it. Again, just like a rect.
+    */
+     
+    g_assert(item != NULL);
+    g_assert(SP_IS_IMAGE(item));
+
+    if (item->clip_ref->getObject()) {
+        //We are looking at a clipped image: do not return any snappoints, as these might be
+        //far far away from the visible part from the clipped image
+    } else {
+        // The image has not been clipped: return its corners, which might be rotated for example
+        SPImage &image = *SP_IMAGE(item);
+        double const x0 = image.x.computed;
+               double const y0 = image.y.computed;
+               double const x1 = x0 + image.width.computed;
+               double const y1 = y0 + image.height.computed;
+               NR::Matrix const i2d (sp_item_i2d_affine (item));
+               *p = NR::Point(x0, y0) * i2d;
+        *p = NR::Point(x0, y1) * i2d;
+        *p = NR::Point(x1, y1) * i2d;
+        *p = NR::Point(x1, y0) * i2d;
+    }
 }
 
 /*
@@ -1331,6 +1388,45 @@ sp_image_repr_read_b64 (const gchar * uri_data)
        return pixbuf;
 }
 
+static void
+sp_image_set_curve(SPImage *image) 
+{
+    //create a curve at the image's boundary for snapping
+    if ((image->height.computed < 1e-18) || (image->width.computed < 1e-18) || (image->clip_ref->getObject())) {
+        if (image->curve) {
+            image->curve = sp_curve_unref(image->curve);
+        }
+        return;
+    }
+    
+    NRRect rect;
+       sp_image_bbox(image, &rect, NR::identity(), 0);
+       NR::Maybe<NR::Rect> rect2 = rect.upgrade();
+       SPCurve *c = sp_curve_new_from_rect(rect2);
+        
+    if (image->curve) {
+        image->curve = sp_curve_unref(image->curve);
+    }
+    
+    if (c) {
+        image->curve = sp_curve_ref(c);
+    }
+    
+    sp_curve_unref(c);    
+}
+
+/**
+ * Return duplicate of curve (if any exists) or NULL if there is no curve
+ */
+SPCurve *
+sp_image_get_curve (SPImage *image)
+{
+       if (image->curve) {
+               return sp_curve_copy(image->curve);
+       }
+       return NULL;
+}
+
 /*
   Local Variables:
   mode:c++