summaryrefslogtreecommitdiffstats
path: root/src/sp-image.cpp
diff options
context:
space:
mode:
authorMenTaLguY <mental@rydia.net>2006-01-16 02:36:01 +0000
committermental <mental@users.sourceforge.net>2006-01-16 02:36:01 +0000
commit179fa413b047bede6e32109e2ce82437c5fb8d34 (patch)
treea5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/sp-image.cpp
downloadinkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz
inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/sp-image.cpp')
-rw-r--r--src/sp-image.cpp1200
1 files changed, 1200 insertions, 0 deletions
diff --git a/src/sp-image.cpp b/src/sp-image.cpp
new file mode 100644
index 000000000..869981567
--- /dev/null
+++ b/src/sp-image.cpp
@@ -0,0 +1,1200 @@
+#define __SP_IMAGE_C__
+
+/*
+ * SVG <image> implementation
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Edward Flick (EAF)
+ *
+ * Copyright (C) 1999-2005 Authors
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <libnr/nr-matrix-fns.h>
+
+//#define GDK_PIXBUF_ENABLE_BACKEND 1
+//#include <gdk-pixbuf/gdk-pixbuf-io.h>
+#include "display/nr-arena-image.h"
+
+//Added for preserveAspectRatio support -- EAF
+#include "enums.h"
+#include "attributes.h"
+
+#include "print.h"
+#include "brokenimage.xpm"
+#include "document.h"
+#include "sp-image.h"
+#include <glibmm/i18n.h>
+#include "xml/quote.h"
+#include <xml/repr.h>
+
+#include "io/sys.h"
+#include <png.h>
+
+/*
+ * SPImage
+ */
+
+
+static void sp_image_class_init (SPImageClass * klass);
+static void sp_image_init (SPImage * image);
+
+static void sp_image_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
+static void sp_image_release (SPObject * object);
+static void sp_image_set (SPObject *object, unsigned int key, const gchar *value);
+static void sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags);
+static Inkscape::XML::Node *sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
+
+static void sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
+static void sp_image_print (SPItem * item, SPPrintContext *ctx);
+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);
+
+GdkPixbuf * sp_image_repr_read_image (Inkscape::XML::Node * repr);
+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);
+static GdkPixbuf * sp_image_repr_read_b64 (const gchar * uri_data);
+
+static SPItemClass *parent_class;
+
+
+extern "C"
+{
+ void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length );
+ void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length );
+ void user_flush_data( png_structp png_ptr );
+
+}
+
+namespace Inkscape {
+namespace IO {
+
+class PushPull
+{
+public:
+ gboolean first;
+ FILE* fp;
+ guchar* scratch;
+ gsize size;
+ gsize used;
+ gsize offset;
+ GdkPixbufLoader *loader;
+
+ PushPull() : first(TRUE),
+ fp(0),
+ scratch(0),
+ size(0),
+ used(0),
+ offset(0),
+ loader(0) {};
+
+ gboolean readMore()
+ {
+ gboolean good = FALSE;
+ if ( offset )
+ {
+ g_memmove( scratch, scratch + offset, used - offset );
+ used -= offset;
+ offset = 0;
+ }
+ if ( used < size )
+ {
+ gsize space = size - used;
+ gsize got = fread( scratch + used, 1, space, fp );
+ if ( got )
+ {
+ if ( loader )
+ {
+ GError *err = NULL;
+ //g_message( " __read %d bytes", (int)got );
+ if ( !gdk_pixbuf_loader_write( loader, scratch + used, got, &err ) )
+ {
+ //g_message("_error writing pixbuf data");
+ }
+ }
+
+ used += got;
+ good = TRUE;
+ }
+ else
+ {
+ good = FALSE;
+ }
+ }
+ return good;
+ }
+
+ gsize available() const
+ {
+ return (used - offset);
+ }
+
+ gsize readOut( gpointer data, gsize length )
+ {
+ gsize giving = available();
+ if ( length < giving )
+ {
+ giving = length;
+ }
+ g_memmove( data, scratch + offset, giving );
+ offset += giving;
+ if ( offset >= used )
+ {
+ offset = 0;
+ used = 0;
+ }
+ return giving;
+ }
+
+ void clear()
+ {
+ offset = 0;
+ used = 0;
+ }
+
+private:
+ PushPull& operator = (const PushPull& other);
+ PushPull(const PushPull& other);
+};
+
+void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length )
+{
+// g_message( "user_read_data(%d)", length );
+
+ PushPull* youme = (PushPull*)png_get_io_ptr(png_ptr);
+
+ gsize filled = 0;
+ gboolean canRead = TRUE;
+
+ while ( filled < length && canRead )
+ {
+ gsize some = youme->readOut( data + filled, length - filled );
+ filled += some;
+ if ( filled < length )
+ {
+ canRead &= youme->readMore();
+ }
+ }
+// g_message("things out");
+}
+
+void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length )
+{
+ //g_message( "user_write_data(%d)", length );
+}
+
+void user_flush_data( png_structp png_ptr )
+{
+ //g_message( "user_flush_data" );
+}
+
+GdkPixbuf* pixbuf_new_from_file( const char *filename, GError **error )
+{
+ GdkPixbuf* buf = NULL;
+ PushPull youme;
+ gint dpiX = 0;
+ gint dpiY = 0;
+
+ //buf = gdk_pixbuf_new_from_file( filename, error );
+ dump_fopen_call( filename, "pixbuf_new_from_file" );
+ FILE* fp = fopen_utf8name( filename, "r" );
+ if ( fp )
+ {
+ GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+ if ( loader )
+ {
+ GError *err = NULL;
+
+ // short buffer
+ guchar scratch[1024];
+ gboolean latter = FALSE;
+ gboolean isPng = FALSE;
+ png_structp pngPtr = NULL;
+ png_infop infoPtr = NULL;
+ //png_infop endPtr = NULL;
+
+ youme.fp = fp;
+ youme.scratch = scratch;
+ youme.size = sizeof(scratch);
+ youme.used = 0;
+ youme.offset = 0;
+ youme.loader = loader;
+
+ while ( !feof(fp) )
+ {
+ if ( youme.readMore() )
+ {
+ if ( youme.first )
+ {
+ //g_message( "First data chunk" );
+ youme.first = FALSE;
+ isPng = !png_sig_cmp( scratch + youme.offset, 0, youme.available() );
+ //g_message( " png? %s", (isPng ? "Yes":"No") );
+ if ( isPng )
+ {
+ pngPtr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
+ NULL,//(png_voidp)user_error_ptr,
+ NULL,//user_error_fn,
+ NULL//user_warning_fn
+ );
+ if ( pngPtr )
+ {
+ infoPtr = png_create_info_struct( pngPtr );
+ //endPtr = png_create_info_struct( pngPtr );
+
+ png_set_read_fn( pngPtr, &youme, user_read_data );
+ //g_message( "In" );
+
+ //png_read_info( pngPtr, infoPtr );
+ png_read_png( pngPtr, infoPtr, PNG_TRANSFORM_IDENTITY, NULL );
+
+ //g_message("out");
+
+ //png_read_end(pngPtr, endPtr);
+
+ /*
+ if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_pHYs ) )
+ {
+ g_message("pHYs chunk now valid" );
+ }
+ if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_sCAL ) )
+ {
+ g_message("sCAL chunk now valid" );
+ }
+ */
+
+ png_uint_32 res_x = 0;
+ png_uint_32 res_y = 0;
+ int unit_type = 0;
+ if ( png_get_pHYs( pngPtr, infoPtr, &res_x, &res_y, &unit_type) )
+ {
+// g_message( "pHYs yes (%d, %d) %d (%s)", (int)res_x, (int)res_y, unit_type,
+// (unit_type == 1? "per meter" : "unknown")
+// );
+
+// g_message( " dpi: (%d, %d)",
+// (int)(0.5 + ((double)res_x)/39.37),
+// (int)(0.5 + ((double)res_y)/39.37) );
+ if ( unit_type == PNG_RESOLUTION_METER )
+ {
+ // TODO come up with a more accurate DPI setting
+ dpiX = (int)(0.5 + ((double)res_x)/39.37);
+ dpiY = (int)(0.5 + ((double)res_y)/39.37);
+ }
+ }
+ else
+ {
+// g_message( "pHYs no" );
+ }
+
+/*
+ double width = 0;
+ double height = 0;
+ int unit = 0;
+ if ( png_get_sCAL(pngPtr, infoPtr, &unit, &width, &height) )
+ {
+ gchar* vals[] = {
+ "unknown", // PNG_SCALE_UNKNOWN
+ "meter", // PNG_SCALE_METER
+ "radian", // PNG_SCALE_RADIAN
+ "last", //
+ NULL
+ };
+
+ g_message( "sCAL: (%f, %f) %d (%s)",
+ width, height, unit,
+ ((unit >= 0 && unit < 3) ? vals[unit]:"???")
+ );
+ }
+*/
+
+ // now clean it up.
+ png_destroy_read_struct( &pngPtr, &infoPtr, NULL );//&endPtr );
+ }
+ else
+ {
+ g_message("Error when creating PNG read struct");
+ }
+ }
+ }
+ else if ( !latter )
+ {
+ latter = TRUE;
+ //g_message(" READing latter");
+ }
+ // Now clear out the buffer so we can read more.
+ // (dumping out unused)
+ youme.clear();
+ }
+ }
+
+ gboolean ok = gdk_pixbuf_loader_close(loader, &err);
+ if ( ok )
+ {
+ buf = gdk_pixbuf_loader_get_pixbuf( loader );
+ if ( buf )
+ {
+ g_object_ref(buf);
+
+ if ( dpiX )
+ {
+ gchar *tmp = g_strdup_printf( "%d", dpiX );
+ if ( tmp )
+ {
+ //gdk_pixbuf_set_option( buf, "Inkscape::DpiX", tmp );
+ g_free( tmp );
+ }
+ }
+ if ( dpiY )
+ {
+ gchar *tmp = g_strdup_printf( "%d", dpiY );
+ if ( tmp )
+ {
+ //gdk_pixbuf_set_option( buf, "Inkscape::DpiY", tmp );
+ g_free( tmp );
+ }
+ }
+ }
+ }
+ else
+ {
+ // do something
+ g_message("error loading pixbuf at close");
+ }
+
+ g_object_unref(loader);
+ }
+ else
+ {
+ g_message("error when creating pixbuf loader");
+ }
+ fclose( fp );
+ fp = NULL;
+ }
+ else
+ {
+ g_warning ("unable to open file: %s", filename);
+ }
+
+/*
+ if ( buf )
+ {
+ const gchar* bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiX" );
+ if ( bloop )
+ {
+ g_message("DPI X is [%s]", bloop);
+ }
+ bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiY" );
+ if ( bloop )
+ {
+ g_message("DPI Y is [%s]", bloop);
+ }
+ }
+*/
+
+ return buf;
+}
+
+}
+}
+
+GType
+sp_image_get_type (void)
+{
+ static GType image_type = 0;
+ if (!image_type) {
+ GTypeInfo image_info = {
+ sizeof (SPImageClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) sp_image_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (SPImage),
+ 16, /* n_preallocs */
+ (GInstanceInitFunc) sp_image_init,
+ NULL, /* value_table */
+ };
+ image_type = g_type_register_static (sp_item_get_type (), "SPImage", &image_info, (GTypeFlags)0);
+ }
+ return image_type;
+}
+
+static void
+sp_image_class_init (SPImageClass * klass)
+{
+ GObjectClass * gobject_class;
+ SPObjectClass * sp_object_class;
+ SPItemClass * item_class;
+
+ gobject_class = (GObjectClass *) klass;
+ sp_object_class = (SPObjectClass *) klass;
+ item_class = (SPItemClass *) klass;
+
+ parent_class = (SPItemClass*)g_type_class_ref (sp_item_get_type ());
+
+ sp_object_class->build = sp_image_build;
+ sp_object_class->release = sp_image_release;
+ sp_object_class->set = sp_image_set;
+ sp_object_class->update = sp_image_update;
+ sp_object_class->write = sp_image_write;
+
+ item_class->bbox = sp_image_bbox;
+ item_class->print = sp_image_print;
+ item_class->description = sp_image_description;
+ item_class->show = sp_image_show;
+ item_class->snappoints = sp_image_snappoints;
+ item_class->set_transform = sp_image_set_transform;
+}
+
+static void
+sp_image_init (SPImage *image)
+{
+ image->x.unset();
+ image->y.unset();
+ image->width.unset();
+ image->height.unset();
+ image->aspect_align = SP_ASPECT_NONE;
+}
+
+static void
+sp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+{
+ if (((SPObjectClass *) parent_class)->build)
+ ((SPObjectClass *) parent_class)->build (object, document, repr);
+
+ sp_object_read_attr (object, "xlink:href");
+ sp_object_read_attr (object, "x");
+ sp_object_read_attr (object, "y");
+ sp_object_read_attr (object, "width");
+ sp_object_read_attr (object, "height");
+ sp_object_read_attr (object, "preserveAspectRatio");
+
+ /* Register */
+ sp_document_add_resource (document, "image", object);
+}
+
+static void
+sp_image_release (SPObject *object)
+{
+ SPImage *image;
+
+ image = SP_IMAGE (object);
+
+ if (SP_OBJECT_DOCUMENT (object)) {
+ /* Unregister ourselves */
+ sp_document_remove_resource (SP_OBJECT_DOCUMENT (object), "image", SP_OBJECT (object));
+ }
+
+ if (image->href) {
+ g_free (image->href);
+ image->href = NULL;
+ }
+
+ if (image->pixbuf) {
+ gdk_pixbuf_unref (image->pixbuf);
+ image->pixbuf = NULL;
+ }
+
+ if (((SPObjectClass *) parent_class)->release)
+ ((SPObjectClass *) parent_class)->release (object);
+}
+
+static void
+sp_image_set (SPObject *object, unsigned int key, const gchar *value)
+{
+ SPImage *image;
+
+ image = SP_IMAGE (object);
+
+ switch (key) {
+ case SP_ATTR_XLINK_HREF:
+ g_free (image->href);
+ image->href = (value) ? g_strdup (value) : NULL;
+ object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
+ break;
+ case SP_ATTR_X:
+ if (!image->x.readAbsolute(value)) {
+ /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
+ image->x.unset();
+ }
+ object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
+ case SP_ATTR_Y:
+ if (!image->y.readAbsolute(value)) {
+ /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
+ image->y.unset();
+ }
+ object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
+ case SP_ATTR_WIDTH:
+ if (!image->width.readAbsolute(value)) {
+ /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
+ image->width.unset();
+ }
+ object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
+ case SP_ATTR_HEIGHT:
+ if (!image->height.readAbsolute(value)) {
+ /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
+ image->height.unset();
+ }
+ object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ break;
+ case SP_ATTR_PRESERVEASPECTRATIO:
+ /* Do setup before, so we can use break to escape */
+ image->aspect_align = SP_ASPECT_NONE;
+ image->aspect_clip = SP_ASPECT_MEET;
+ object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
+ if (value) {
+ int len;
+ gchar c[256];
+ const gchar *p, *e;
+ unsigned int align, clip;
+ p = value;
+ while (*p && *p == 32) p += 1;
+ if (!*p) break;
+ e = p;
+ while (*e && *e != 32) e += 1;
+ len = e - p;
+ if (len > 8) break;
+ memcpy (c, value, len);
+ c[len] = 0;
+ /* Now the actual part */
+ if (!strcmp (c, "none")) {
+ align = SP_ASPECT_NONE;
+ } else if (!strcmp (c, "xMinYMin")) {
+ align = SP_ASPECT_XMIN_YMIN;
+ } else if (!strcmp (c, "xMidYMin")) {
+ align = SP_ASPECT_XMID_YMIN;
+ } else if (!strcmp (c, "xMaxYMin")) {
+ align = SP_ASPECT_XMAX_YMIN;
+ } else if (!strcmp (c, "xMinYMid")) {
+ align = SP_ASPECT_XMIN_YMID;
+ } else if (!strcmp (c, "xMidYMid")) {
+ align = SP_ASPECT_XMID_YMID;
+ } else if (!strcmp (c, "xMaxYMid")) {
+ align = SP_ASPECT_XMAX_YMID;
+ } else if (!strcmp (c, "xMinYMax")) {
+ align = SP_ASPECT_XMIN_YMAX;
+ } else if (!strcmp (c, "xMidYMax")) {
+ align = SP_ASPECT_XMID_YMAX;
+ } else if (!strcmp (c, "xMaxYMax")) {
+ align = SP_ASPECT_XMAX_YMAX;
+ } else {
+ break;
+ }
+ clip = SP_ASPECT_MEET;
+ while (*e && *e == 32) e += 1;
+ if (e) {
+ if (!strcmp (e, "meet")) {
+ clip = SP_ASPECT_MEET;
+ } else if (!strcmp (e, "slice")) {
+ clip = SP_ASPECT_SLICE;
+ } else {
+ break;
+ }
+ }
+ image->aspect_align = align;
+ image->aspect_clip = clip;
+ }
+ break;
+ default:
+ if (((SPObjectClass *) (parent_class))->set)
+ ((SPObjectClass *) (parent_class))->set (object, key, value);
+ break;
+ }
+}
+
+static void
+sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
+{
+ SPImage *image;
+
+ image = (SPImage *) object;
+
+ if (((SPObjectClass *) (parent_class))->update)
+ ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
+
+ if (flags & SP_IMAGE_HREF_MODIFIED_FLAG) {
+ if (image->pixbuf) {
+ gdk_pixbuf_unref (image->pixbuf);
+ image->pixbuf = NULL;
+ }
+ if (image->href) {
+ GdkPixbuf *pixbuf;
+ pixbuf = sp_image_repr_read_image (object->repr);
+ if (pixbuf) {
+ pixbuf = sp_image_pixbuf_force_rgba (pixbuf);
+ image->pixbuf = pixbuf;
+ }
+ }
+ }
+ // preserveAspectRatio calculate bounds / clipping rectangle -- EAF
+ if (image->pixbuf && (image->aspect_align != SP_ASPECT_NONE)) {
+ int imagewidth, imageheight;
+ double x,y;
+
+ imagewidth = gdk_pixbuf_get_width (image->pixbuf);
+ imageheight = gdk_pixbuf_get_height (image->pixbuf);
+
+ switch (image->aspect_align) {
+ case SP_ASPECT_XMIN_YMIN:
+ x = 0.0;
+ y = 0.0;
+ break;
+ case SP_ASPECT_XMID_YMIN:
+ x = 0.5;
+ y = 0.0;
+ break;
+ case SP_ASPECT_XMAX_YMIN:
+ x = 1.0;
+ y = 0.0;
+ break;
+ case SP_ASPECT_XMIN_YMID:
+ x = 0.0;
+ y = 0.5;
+ break;
+ case SP_ASPECT_XMID_YMID:
+ x = 0.5;
+ y = 0.5;
+ break;
+ case SP_ASPECT_XMAX_YMID:
+ x = 1.0;
+ y = 0.5;
+ break;
+ case SP_ASPECT_XMIN_YMAX:
+ x = 0.0;
+ y = 1.0;
+ break;
+ case SP_ASPECT_XMID_YMAX:
+ x = 0.5;
+ y = 1.0;
+ break;
+ case SP_ASPECT_XMAX_YMAX:
+ x = 1.0;
+ y = 1.0;
+ break;
+ default:
+ x = 0.0;
+ y = 0.0;
+ break;
+ }
+
+ if (image->aspect_clip == SP_ASPECT_SLICE) {
+ image->viewx = image->x.computed;
+ image->viewy = image->y.computed;
+ image->viewwidth = image->width.computed;
+ image->viewheight = image->height.computed;
+ if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
+ // Pixels aspect is wider than bounding box
+ image->trimheight = imageheight;
+ image->trimwidth = static_cast<int>(static_cast<double>(imageheight) * image->width.computed / image->height.computed);
+ image->trimy = 0;
+ image->trimx = static_cast<int>(static_cast<double>(imagewidth - image->trimwidth) * x);
+ } else {
+ // Pixels aspect is taller than bounding box
+ image->trimwidth = imagewidth;
+ image->trimheight = static_cast<int>(static_cast<double>(imagewidth) * image->height.computed / image->width.computed);
+ image->trimx = 0;
+ image->trimy = static_cast<int>(static_cast<double>(imageheight - image->trimheight) * y);
+ }
+ } else {
+ // Otherwise, assume SP_ASPECT_MEET
+ image->trimx = 0;
+ image->trimy = 0;
+ image->trimwidth = imagewidth;
+ image->trimheight = imageheight;
+ if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
+ // Pixels aspect is wider than bounding boz
+ image->viewwidth = image->width.computed;
+ image->viewheight = image->viewwidth * imageheight / imagewidth;
+ image->viewx=image->x.computed;
+ image->viewy=(image->height.computed - image->viewheight) * y + image->y.computed;
+ } else {
+ // Pixels aspect is taller than bounding box
+ image->viewheight = image->height.computed;
+ image->viewwidth = image->viewheight * imagewidth / imageheight;
+ image->viewy=image->y.computed;
+ image->viewx=(image->width.computed - image->viewwidth) * x + image->x.computed;
+ }
+ }
+ }
+
+ sp_image_update_canvas_image ((SPImage *) object);
+}
+
+static Inkscape::XML::Node *
+sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
+{
+ SPImage *image;
+
+ image = SP_IMAGE (object);
+
+ if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ repr = sp_repr_new ("svg:image");
+ }
+
+ repr->setAttribute("xlink:href", image->href);
+ /* fixme: Reset attribute if needed (Lauris) */
+ if (image->x._set) sp_repr_set_svg_double(repr, "x", image->x.computed);
+ if (image->y._set) sp_repr_set_svg_double(repr, "y", image->y.computed);
+ if (image->width._set) sp_repr_set_svg_double(repr, "width", image->width.computed);
+ if (image->height._set) sp_repr_set_svg_double(repr, "height", image->height.computed);
+ repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
+
+ if (((SPObjectClass *) (parent_class))->write)
+ ((SPObjectClass *) (parent_class))->write (object, repr, flags);
+
+ return repr;
+}
+
+static void
+sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
+{
+ SPImage const &image = *SP_IMAGE(item);
+
+ if ((image.width.computed > 0.0) && (image.height.computed > 0.0)) {
+ 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_rect_union_pt(bbox, NR::Point(x0, y0) * transform);
+ nr_rect_union_pt(bbox, NR::Point(x1, y0) * transform);
+ nr_rect_union_pt(bbox, NR::Point(x1, y1) * transform);
+ nr_rect_union_pt(bbox, NR::Point(x0, y1) * transform);
+ }
+}
+
+static void
+sp_image_print (SPItem *item, SPPrintContext *ctx)
+{
+ SPImage *image;
+ NRMatrix tp, ti, s, t;
+ guchar *px;
+ int w, h, rs, pixskip;
+
+ image = SP_IMAGE (item);
+
+ if (!image->pixbuf) return;
+ if ((image->width.computed <= 0.0) || (image->height.computed <= 0.0)) return;
+
+ px = gdk_pixbuf_get_pixels (image->pixbuf);
+ w = gdk_pixbuf_get_width (image->pixbuf);
+ h = gdk_pixbuf_get_height (image->pixbuf);
+ rs = gdk_pixbuf_get_rowstride (image->pixbuf);
+ pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
+
+ if (image->aspect_align == SP_ASPECT_NONE) {
+ /* fixme: (Lauris) */
+ nr_matrix_set_translate (&tp, image->x.computed, image->y.computed);
+ nr_matrix_set_scale (&s, image->width.computed, -image->height.computed);
+ nr_matrix_set_translate (&ti, 0.0, -1.0);
+ } else { // preserveAspectRatio
+ nr_matrix_set_translate (&tp, image->viewx, image->viewy);
+ nr_matrix_set_scale (&s, image->viewwidth, -image->viewheight);
+ nr_matrix_set_translate (&ti, 0.0, -1.0);
+ }
+
+ nr_matrix_multiply (&t, &s, &tp);
+ nr_matrix_multiply (&t, &ti, &t);
+
+ if (image->aspect_align == SP_ASPECT_NONE)
+ sp_print_image_R8G8B8A8_N (ctx, px, w, h, rs, &t, SP_OBJECT_STYLE (item));
+ else // preserveAspectRatio
+ sp_print_image_R8G8B8A8_N (ctx, px + image->trimx*pixskip + image->trimy*rs, image->trimwidth, image->trimheight, rs, &t, SP_OBJECT_STYLE (item));
+}
+
+static gchar *
+sp_image_description(SPItem *item)
+{
+ SPImage *image = SP_IMAGE(item);
+ char *href_desc;
+ if (image->href) {
+ href_desc = (strncmp(image->href, "data:", 5) == 0)
+ ? g_strdup(_("embedded"))
+ : 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
+ }
+
+ char *ret = ( image->pixbuf == NULL
+ ? g_strdup_printf(_("<b>Image with bad reference</b>: %s"), href_desc)
+ : g_strdup_printf(_("<b>Image</b> %d &#215; %d: %s"),
+ gdk_pixbuf_get_width(image->pixbuf),
+ gdk_pixbuf_get_height(image->pixbuf),
+ href_desc) );
+ g_free(href_desc);
+ return ret;
+}
+
+static NRArenaItem *
+sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
+{
+ int pixskip, rs;
+ SPImage * image;
+ NRArenaItem *ai;
+
+ image = (SPImage *) item;
+
+ ai = NRArenaImage::create(arena);
+
+ 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);
+ if (image->aspect_align == SP_ASPECT_NONE)
+ nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
+ gdk_pixbuf_get_pixels (image->pixbuf),
+ gdk_pixbuf_get_width (image->pixbuf),
+ gdk_pixbuf_get_height (image->pixbuf),
+ rs);
+ else // preserveAspectRatio
+ nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
+ gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
+ image->trimwidth,
+ image->trimheight,
+ rs);
+ } else {
+ nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai), NULL, 0, 0, 0);
+ }
+ if (image->aspect_align == SP_ASPECT_NONE)
+ nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->x.computed, image->y.computed, image->width.computed, image->height.computed);
+ else // preserveAspectRatio
+ nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->viewx, image->viewy, image->viewwidth, image->viewheight);
+
+ return ai;
+}
+
+/*
+ * utility function to try loading image from href
+ *
+ * docbase/relative_src
+ * absolute_src
+ *
+ */
+
+GdkPixbuf *
+sp_image_repr_read_image (Inkscape::XML::Node * repr)
+{
+ 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);
+
+ /* 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 *
+sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf)
+{
+ GdkPixbuf * newbuf;
+
+ if (gdk_pixbuf_get_has_alpha (pixbuf)) return pixbuf;
+
+ newbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
+ gdk_pixbuf_unref (pixbuf);
+
+ return newbuf;
+}
+
+/* We assert that realpixbuf is either NULL or identical size to pixbuf */
+
+static void
+sp_image_update_canvas_image (SPImage *image)
+{
+ int rs, pixskip;
+ SPItem *item;
+ SPItemView *v;
+
+ item = SP_ITEM (image);
+
+ if (image->pixbuf) {
+ /* fixme: We are slightly violating spec here (Lauris) */
+ if (!image->width._set) {
+ image->width.computed = gdk_pixbuf_get_width (image->pixbuf);
+ }
+ if (!image->height._set) {
+ image->height.computed = gdk_pixbuf_get_height (image->pixbuf);
+ }
+ }
+
+ 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);
+ if (image->aspect_align == SP_ASPECT_NONE) {
+ nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
+ gdk_pixbuf_get_pixels (image->pixbuf),
+ gdk_pixbuf_get_width (image->pixbuf),
+ gdk_pixbuf_get_height (image->pixbuf),
+ rs);
+ nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
+ image->x.computed, image->y.computed,
+ image->width.computed, image->height.computed);
+ } else { // preserveAspectRatio
+ nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
+ gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
+ image->trimwidth,
+ image->trimheight,
+ rs);
+ nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
+ image->viewx, image->viewy,
+ image->viewwidth, image->viewheight);
+ }
+ }
+}
+
+static void sp_image_snappoints(SPItem const *item, SnapPointsIter p)
+{
+ if (((SPItemClass *) parent_class)->snappoints) {
+ ((SPItemClass *) parent_class)->snappoints (item, p);
+ }
+}
+
+/*
+ * Initially we'll do:
+ * Transform x, y, set x, y, clear translation
+ */
+
+static NR::Matrix
+sp_image_set_transform(SPItem *item, NR::Matrix const &xform)
+{
+ SPImage *image = SP_IMAGE(item);
+
+ /* Calculate position in parent coords. */
+ NR::Point pos( NR::Point(image->x.computed, image->y.computed) * xform );
+
+ /* This function takes care of translation and scaling, we return whatever parts we can't
+ handle. */
+ NR::Matrix ret(NR::transform(xform));
+ NR::Point const scale(hypot(ret[0], ret[1]),
+ hypot(ret[2], ret[3]));
+ if ( scale[NR::X] > 1e-9 ) {
+ ret[0] /= scale[NR::X];
+ ret[1] /= scale[NR::X];
+ } else {
+ ret[0] = 1.0;
+ ret[1] = 0.0;
+ }
+ if ( scale[NR::Y] > 1e-9 ) {
+ ret[2] /= scale[NR::Y];
+ ret[3] /= scale[NR::Y];
+ } else {
+ ret[2] = 0.0;
+ ret[3] = 1.0;
+ }
+
+ image->width = image->width.computed * scale[NR::X];
+ image->height = image->height.computed * scale[NR::Y];
+
+ /* Find position in item coords */
+ pos = pos * ret.inverse();
+ image->x = pos[NR::X];
+ image->y = pos[NR::Y];
+
+ item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+
+ return ret;
+}
+
+static GdkPixbuf *
+sp_image_repr_read_dataURI (const gchar * uri_data)
+{ GdkPixbuf * pixbuf = NULL;
+
+ gint data_is_image = 0;
+ gint data_is_base64 = 0;
+
+ const gchar * data = uri_data;
+
+ while (*data) {
+ if (strncmp (data,"base64",6) == 0) {
+ /* base64-encoding */
+ data_is_base64 = 1;
+ data_is_image = 1; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what
+ data += 6;
+ }
+ else if (strncmp (data,"image/png",9) == 0) {
+ /* PNG image */
+ data_is_image = 1;
+ data += 9;
+ }
+ else if (strncmp (data,"image/jpg",9) == 0) {
+ /* JPEG image */
+ data_is_image = 1;
+ data += 9;
+ }
+ else if (strncmp (data,"image/jpeg",10) == 0) {
+ /* JPEG image */
+ data_is_image = 1;
+ data += 10;
+ }
+ else { /* unrecognized option; skip it */
+ while (*data) {
+ if (((*data) == ';') || ((*data) == ',')) break;
+ data++;
+ }
+ }
+ if ((*data) == ';') {
+ data++;
+ continue;
+ }
+ if ((*data) == ',') {
+ data++;
+ break;
+ }
+ }
+
+ if ((*data) && data_is_image && data_is_base64) {
+ pixbuf = sp_image_repr_read_b64 (data);
+ }
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+sp_image_repr_read_b64 (const gchar * uri_data)
+{ GdkPixbuf * pixbuf = NULL;
+ GdkPixbufLoader * loader = NULL;
+
+ gint j;
+ gint k;
+ gint l;
+ gint b;
+ gint len;
+ gint eos = 0;
+ gint failed = 0;
+
+ guint32 bits;
+
+ static const gchar B64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ const gchar* btr = uri_data;
+
+ gchar ud[4];
+
+ guchar bd[57];
+
+ loader = gdk_pixbuf_loader_new ();
+
+ if (loader == NULL) return NULL;
+
+ while (eos == 0) {
+ l = 0;
+ for (j = 0; j < 19; j++) {
+ len = 0;
+ for (k = 0; k < 4; k++) {
+ while (isspace ((int) (*btr))) {
+ if ((*btr) == '\0') break;
+ btr++;
+ }
+ if (eos) {
+ ud[k] = 0;
+ continue;
+ }
+ if (((*btr) == '\0') || ((*btr) == '=')) {
+ eos = 1;
+ ud[k] = 0;
+ continue;
+ }
+ ud[k] = 64;
+ for (b = 0; b < 64; b++) { /* There must a faster way to do this... ?? */
+ if (B64[b] == (*btr)) {
+ ud[k] = (gchar) b;
+ break;
+ }
+ }
+ if (ud[k] == 64) { /* data corruption ?? */
+ eos = 1;
+ ud[k] = 0;
+ continue;
+ }
+ btr++;
+ len++;
+ }
+ bits = (guint32) ud[0];
+ bits = (bits << 6) | (guint32) ud[1];
+ bits = (bits << 6) | (guint32) ud[2];
+ bits = (bits << 6) | (guint32) ud[3];
+ bd[l++] = (guchar) ((bits & 0xff0000) >> 16);
+ if (len > 2) {
+ bd[l++] = (guchar) ((bits & 0xff00) >> 8);
+ }
+ if (len > 3) {
+ bd[l++] = (guchar) (bits & 0xff);
+ }
+ }
+
+ if (!gdk_pixbuf_loader_write (loader, (const guchar *) bd, (size_t) l, NULL)) {
+ failed = 1;
+ break;
+ }
+ }
+
+ gdk_pixbuf_loader_close (loader, NULL);
+
+ if (!failed) pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+
+ return pixbuf;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :