sp-image.cpp revision 6fc0952fcafd54a656fdc8257ebb44bad58af163
1121N/A#define __SP_IMAGE_C__
1121N/A
1121N/A/*
1121N/A * SVG <image> implementation
1121N/A *
1121N/A * Authors:
1121N/A * Lauris Kaplinski <lauris@kaplinski.com>
1121N/A * Edward Flick (EAF)
1121N/A *
1121N/A * Copyright (C) 1999-2005 Authors
1121N/A * Copyright (C) 2000-2001 Ximian, Inc.
1121N/A *
1121N/A * Released under GNU GPL, read the file 'COPYING' for more information
1121N/A */
1121N/A
1121N/A#ifdef HAVE_CONFIG_H
1121N/A# include "config.h"
1121N/A#endif
1121N/A
1121N/A#include <cstring>
1121N/A#include <string>
1121N/A#include <libnr/nr-matrix-fns.h>
1121N/A#include <libnr/nr-matrix-ops.h>
387N/A#include <libnr/nr-translate-matrix-ops.h>
1121N/A#include <libnr/nr-scale-translate-ops.h>
1121N/A#include <libnr/nr-convert2geom.h>
1121N/A#include <2geom/rect.h>
387N/A//#define GDK_PIXBUF_ENABLE_BACKEND 1
1121N/A//#include <gdk-pixbuf/gdk-pixbuf-io.h>
1121N/A#include "display/nr-arena-image.h"
1121N/A#include <display/curve.h>
1121N/A#include <glib/gstdio.h>
1121N/A
1121N/A//Added for preserveAspectRatio support -- EAF
1121N/A#include "enums.h"
1121N/A#include "attributes.h"
1121N/A
1121N/A#include "print.h"
1121N/A#include "brokenimage.xpm"
1121N/A#include "document.h"
1121N/A#include "sp-image.h"
1121N/A#include "sp-clippath.h"
1121N/A#include <glibmm/i18n.h>
1121N/A#include "xml/quote.h"
1121N/A#include <xml/repr.h>
1121N/A
1121N/A#include "libnr/nr-matrix-fns.h"
1121N/A
1121N/A#include "io/sys.h"
387N/A#include <png.h>
1121N/A#if ENABLE_LCMS
1121N/A#include "color-profile-fns.h"
1121N/A#include "color-profile.h"
1121N/A//#define DEBUG_LCMS
1121N/A#ifdef DEBUG_LCMS
1121N/A#include "prefs-utils.h"
1121N/A#include <gtk/gtkmessagedialog.h>
1121N/A#endif // DEBUG_LCMS
1121N/A#endif // ENABLE_LCMS
1121N/A/*
1121N/A * SPImage
1121N/A */
1121N/A
1121N/A
1121N/Astatic void sp_image_class_init (SPImageClass * klass);
1121N/Astatic void sp_image_init (SPImage * image);
1121N/A
1121N/Astatic void sp_image_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
1121N/Astatic void sp_image_release (SPObject * object);
1121N/Astatic void sp_image_set (SPObject *object, unsigned int key, const gchar *value);
1121N/Astatic void sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags);
1121N/Astatic Inkscape::XML::Node *sp_image_write (SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
1121N/A
1121N/Astatic void sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
1121N/Astatic void sp_image_print (SPItem * item, SPPrintContext *ctx);
387N/Astatic gchar * sp_image_description (SPItem * item);
1121N/Astatic void sp_image_snappoints(SPItem const *item, SnapPointsIter p);
1121N/Astatic NRArenaItem *sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
1121N/Astatic NR::Matrix sp_image_set_transform (SPItem *item, NR::Matrix const &xform);
1121N/Astatic void sp_image_set_curve(SPImage *image);
1121N/A
1121N/A
1121N/Astatic GdkPixbuf *sp_image_repr_read_image( time_t& modTime, gchar*& pixPath, const gchar *href, const gchar *absref, const gchar *base );
1121N/Astatic GdkPixbuf *sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf);
1121N/Astatic void sp_image_update_canvas_image (SPImage *image);
387N/Astatic GdkPixbuf * sp_image_repr_read_dataURI (const gchar * uri_data);
1121N/Astatic GdkPixbuf * sp_image_repr_read_b64 (const gchar * uri_data);
1121N/A
1121N/Astatic SPItemClass *parent_class;
1121N/A
1121N/A
1121N/Aextern "C"
1121N/A{
1121N/A void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length );
1121N/A void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length );
1121N/A void user_flush_data( png_structp png_ptr );
1121N/A
1121N/A}
1121N/A
1121N/A
1121N/A#ifdef DEBUG_LCMS
1121N/Aextern guint update_in_progress;
387N/A#define DEBUG_MESSAGE(key, ...) \
1121N/A{\
1121N/A gint dump = prefs_get_int_attribute_limited("options.scislac", #key, 0, 0, 1);\
1121N/A gint dumpD = prefs_get_int_attribute_limited("options.scislac", #key"D", 0, 0, 1);\
1121N/A gint dumpD2 = prefs_get_int_attribute_limited("options.scislac", #key"D2", 0, 0, 1);\
1121N/A dumpD &= ( (update_in_progress == 0) || dumpD2 );\
1121N/A if ( dump )\
1121N/A {\
1121N/A g_message( __VA_ARGS__ );\
1121N/A\
1121N/A }\
1121N/A if ( dumpD )\
1121N/A {\
1121N/A GtkWidget *dialog = gtk_message_dialog_new(NULL,\
1121N/A GTK_DIALOG_DESTROY_WITH_PARENT, \
1121N/A GTK_MESSAGE_INFO, \
1121N/A GTK_BUTTONS_OK, \
1121N/A __VA_ARGS__ \
1121N/A );\
1121N/A g_signal_connect_swapped(dialog, "response",\
1121N/A G_CALLBACK(gtk_widget_destroy), \
1121N/A dialog); \
1121N/A gtk_widget_show_all( dialog );\
1121N/A }\
1121N/A}
1121N/A#endif // DEBUG_LCMS
1121N/A
1121N/Anamespace Inkscape {
1121N/Anamespace IO {
1121N/A
1121N/Aclass PushPull
1121N/A{
1121N/Apublic:
1121N/A gboolean first;
1121N/A FILE* fp;
1121N/A guchar* scratch;
1121N/A gsize size;
1121N/A gsize used;
1121N/A gsize offset;
1121N/A GdkPixbufLoader *loader;
1121N/A
1121N/A PushPull() : first(TRUE),
1121N/A fp(0),
1121N/A scratch(0),
1121N/A size(0),
1121N/A used(0),
1121N/A offset(0),
1121N/A loader(0) {};
1121N/A
1121N/A gboolean readMore()
1121N/A {
387N/A gboolean good = FALSE;
1121N/A if ( offset )
1121N/A {
1121N/A g_memmove( scratch, scratch + offset, used - offset );
1121N/A used -= offset;
1121N/A offset = 0;
1121N/A }
1121N/A if ( used < size )
1121N/A {
1121N/A gsize space = size - used;
1121N/A gsize got = fread( scratch + used, 1, space, fp );
1121N/A if ( got )
1121N/A {
1121N/A if ( loader )
1121N/A {
1121N/A GError *err = NULL;
1121N/A //g_message( " __read %d bytes", (int)got );
1121N/A if ( !gdk_pixbuf_loader_write( loader, scratch + used, got, &err ) )
1121N/A {
1121N/A //g_message("_error writing pixbuf data");
1121N/A }
1121N/A }
1121N/A
1121N/A used += got;
1121N/A good = TRUE;
387N/A }
1121N/A else
1121N/A {
1121N/A good = FALSE;
1121N/A }
1121N/A }
1121N/A return good;
1121N/A }
1121N/A
1121N/A gsize available() const
1121N/A {
1121N/A return (used - offset);
1121N/A }
1121N/A
1121N/A gsize readOut( gpointer data, gsize length )
1121N/A {
1121N/A gsize giving = available();
1121N/A if ( length < giving )
1121N/A {
1121N/A giving = length;
1121N/A }
1121N/A g_memmove( data, scratch + offset, giving );
1121N/A offset += giving;
1121N/A if ( offset >= used )
1121N/A {
1121N/A offset = 0;
1121N/A used = 0;
387N/A }
1121N/A return giving;
1121N/A }
1121N/A
1121N/A void clear()
1121N/A {
1121N/A offset = 0;
1121N/A used = 0;
1121N/A }
1121N/A
1121N/Aprivate:
1121N/A PushPull& operator = (const PushPull& other);
1121N/A PushPull(const PushPull& other);
1121N/A};
1121N/A
1121N/Avoid user_read_data( png_structp png_ptr, png_bytep data, png_size_t length )
1121N/A{
1121N/A// g_message( "user_read_data(%d)", length );
1121N/A
1121N/A PushPull* youme = (PushPull*)png_get_io_ptr(png_ptr);
1121N/A
1121N/A gsize filled = 0;
1121N/A gboolean canRead = TRUE;
387N/A
1121N/A while ( filled < length && canRead )
1121N/A {
1121N/A gsize some = youme->readOut( data + filled, length - filled );
1121N/A filled += some;
1121N/A if ( filled < length )
1121N/A {
1121N/A canRead &= youme->readMore();
1121N/A }
1121N/A }
387N/A// g_message("things out");
1121N/A}
1121N/A
1121N/Avoid user_write_data( png_structp /*png_ptr*/, png_bytep /*data*/, png_size_t /*length*/ )
1121N/A{
1121N/A //g_message( "user_write_data(%d)", length );
1121N/A}
1121N/A
1121N/Avoid user_flush_data( png_structp /*png_ptr*/ )
1121N/A{
1121N/A //g_message( "user_flush_data" );
1121N/A}
1121N/A
1121N/Astatic GdkPixbuf* pixbuf_new_from_file( const char *filename, time_t &modTime, gchar*& pixPath, GError **/*error*/ )
1121N/A{
1121N/A GdkPixbuf* buf = NULL;
387N/A PushPull youme;
1121N/A gint dpiX = 0;
1121N/A gint dpiY = 0;
1121N/A modTime = 0;
1121N/A if ( pixPath ) {
1121N/A g_free(pixPath);
1121N/A pixPath = 0;
1121N/A }
1121N/A
1121N/A //buf = gdk_pixbuf_new_from_file( filename, error );
1121N/A dump_fopen_call( filename, "pixbuf_new_from_file" );
1121N/A FILE* fp = fopen_utf8name( filename, "r" );
1121N/A if ( fp )
1121N/A {
1121N/A {
1121N/A struct stat st;
1121N/A memset(&st, 0, sizeof(st));
1121N/A int val = g_stat(filename, &st);
1121N/A if ( !val ) {
1121N/A modTime = st.st_mtime;
1121N/A pixPath = g_strdup(filename);
1121N/A }
1121N/A }
1121N/A
1121N/A GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
387N/A if ( loader )
1121N/A {
1121N/A GError *err = NULL;
1121N/A
1121N/A // short buffer
1121N/A guchar scratch[1024];
1121N/A gboolean latter = FALSE;
1121N/A gboolean isPng = FALSE;
1121N/A png_structp pngPtr = NULL;
1121N/A png_infop infoPtr = NULL;
1121N/A //png_infop endPtr = NULL;
1121N/A
1121N/A youme.fp = fp;
387N/A youme.scratch = scratch;
1121N/A youme.size = sizeof(scratch);
1121N/A youme.used = 0;
1121N/A youme.offset = 0;
1121N/A youme.loader = loader;
1121N/A
1121N/A while ( !feof(fp) )
1121N/A {
1121N/A if ( youme.readMore() )
1121N/A {
1121N/A if ( youme.first )
1121N/A {
1121N/A //g_message( "First data chunk" );
1121N/A youme.first = FALSE;
1121N/A isPng = !png_sig_cmp( scratch + youme.offset, 0, youme.available() );
387N/A //g_message( " png? %s", (isPng ? "Yes":"No") );
1121N/A if ( isPng )
1121N/A {
1121N/A pngPtr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
1121N/A NULL,//(png_voidp)user_error_ptr,
1121N/A NULL,//user_error_fn,
1121N/A NULL//user_warning_fn
1121N/A );
1121N/A if ( pngPtr )
1121N/A {
387N/A infoPtr = png_create_info_struct( pngPtr );
1121N/A //endPtr = png_create_info_struct( pngPtr );
1121N/A
1121N/A png_set_read_fn( pngPtr, &youme, user_read_data );
1121N/A //g_message( "In" );
1121N/A
1121N/A //png_read_info( pngPtr, infoPtr );
1121N/A png_read_png( pngPtr, infoPtr, PNG_TRANSFORM_IDENTITY, NULL );
1121N/A
1121N/A //g_message("out");
1121N/A
1121N/A //png_read_end(pngPtr, endPtr);
1121N/A
1121N/A /*
387N/A if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_pHYs ) )
1121N/A {
1121N/A g_message("pHYs chunk now valid" );
1121N/A }
1121N/A if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_sCAL ) )
1121N/A {
1121N/A g_message("sCAL chunk now valid" );
1121N/A }
1121N/A */
1121N/A
387N/A png_uint_32 res_x = 0;
1121N/A png_uint_32 res_y = 0;
1121N/A int unit_type = 0;
1121N/A if ( png_get_pHYs( pngPtr, infoPtr, &res_x, &res_y, &unit_type) )
1121N/A {
1121N/A// g_message( "pHYs yes (%d, %d) %d (%s)", (int)res_x, (int)res_y, unit_type,
1121N/A// (unit_type == 1? "per meter" : "unknown")
1121N/A// );
1121N/A
1121N/A// g_message( " dpi: (%d, %d)",
1121N/A// (int)(0.5 + ((double)res_x)/39.37),
1121N/A// (int)(0.5 + ((double)res_y)/39.37) );
1121N/A if ( unit_type == PNG_RESOLUTION_METER )
1121N/A {
1121N/A // TODO come up with a more accurate DPI setting
1121N/A dpiX = (int)(0.5 + ((double)res_x)/39.37);
387N/A dpiY = (int)(0.5 + ((double)res_y)/39.37);
1121N/A }
1121N/A }
1121N/A else
1121N/A {
1121N/A// g_message( "pHYs no" );
1121N/A }
1121N/A
1121N/A/*
1121N/A double width = 0;
387N/A double height = 0;
1121N/A int unit = 0;
1121N/A if ( png_get_sCAL(pngPtr, infoPtr, &unit, &width, &height) )
1121N/A {
1121N/A gchar* vals[] = {
1121N/A "unknown", // PNG_SCALE_UNKNOWN
1121N/A "meter", // PNG_SCALE_METER
1121N/A "radian", // PNG_SCALE_RADIAN
1121N/A "last", //
1121N/A NULL
1121N/A };
1121N/A
1121N/A g_message( "sCAL: (%f, %f) %d (%s)",
1121N/A width, height, unit,
1121N/A ((unit >= 0 && unit < 3) ? vals[unit]:"???")
1121N/A );
1121N/A }
387N/A*/
1121N/A
1121N/A#if defined(PNG_sRGB_SUPPORTED)
1121N/A {
1121N/A int intent = 0;
1121N/A if ( png_get_sRGB(pngPtr, infoPtr, &intent) ) {
1121N/A// g_message("Found an sRGB png chunk");
1121N/A }
1121N/A }
1121N/A#endif // defined(PNG_sRGB_SUPPORTED)
387N/A
1121N/A#if defined(PNG_cHRM_SUPPORTED)
1121N/A {
1121N/A double white_x = 0;
1121N/A double white_y = 0;
1121N/A double red_x = 0;
1121N/A double red_y = 0;
1121N/A double green_x = 0;
1121N/A double green_y = 0;
1121N/A double blue_x = 0;
1121N/A double blue_y = 0;
1121N/A
1121N/A if ( png_get_cHRM(pngPtr, infoPtr,
1121N/A &white_x, &white_y,
1121N/A &red_x, &red_y,
1121N/A &green_x, &green_y,
1121N/A &blue_x, &blue_y) ) {
1121N/A// g_message("Found a cHRM png chunk");
387N/A }
1121N/A }
1121N/A#endif // defined(PNG_cHRM_SUPPORTED)
1121N/A
1121N/A#if defined(PNG_gAMA_SUPPORTED)
1121N/A {
1121N/A double file_gamma = 0;
1121N/A if ( png_get_gAMA(pngPtr, infoPtr, &file_gamma) ) {
1121N/A// g_message("Found a gAMA png chunk");
1121N/A }
1121N/A }
1121N/A#endif // defined(PNG_gAMA_SUPPORTED)
1121N/A
1121N/A#if defined(PNG_iCCP_SUPPORTED)
1121N/A {
1121N/A char* name = 0;
1121N/A int compression_type = 0;
1121N/A char* profile = 0;
1121N/A png_uint_32 proflen = 0;
1121N/A if ( png_get_iCCP(pngPtr, infoPtr, &name, &compression_type, &profile, &proflen) ) {
387N/A// g_message("Found an iCCP chunk named [%s] with %d bytes and comp %d", name, proflen, compression_type);
1121N/A }
1121N/A }
1121N/A#endif // defined(PNG_iCCP_SUPPORTED)
1121N/A
1121N/A
1121N/A // now clean it up.
1121N/A png_destroy_read_struct( &pngPtr, &infoPtr, NULL );//&endPtr );
1121N/A }
1121N/A else
1121N/A {
1121N/A// g_message("Error when creating PNG read struct");
1121N/A }
387N/A }
1121N/A }
1121N/A else if ( !latter )
1121N/A {
1121N/A latter = TRUE;
1121N/A //g_message(" READing latter");
1121N/A }
1121N/A // Now clear out the buffer so we can read more.
1121N/A // (dumping out unused)
1121N/A youme.clear();
1121N/A }
1121N/A }
1121N/A
387N/A gboolean ok = gdk_pixbuf_loader_close(loader, &err);
1121N/A if ( ok )
1121N/A {
1121N/A buf = gdk_pixbuf_loader_get_pixbuf( loader );
1121N/A if ( buf )
1121N/A {
1121N/A g_object_ref(buf);
1121N/A
1121N/A if ( dpiX )
1121N/A {
1121N/A gchar *tmp = g_strdup_printf( "%d", dpiX );
1121N/A if ( tmp )
1121N/A {
1121N/A// g_message("Need to set DpiX: %s", tmp);
1121N/A //gdk_pixbuf_set_option( buf, "Inkscape::DpiX", tmp );
1121N/A g_free( tmp );
1121N/A }
1121N/A }
1121N/A if ( dpiY )
1121N/A {
1121N/A gchar *tmp = g_strdup_printf( "%d", dpiY );
1121N/A if ( tmp )
387N/A {
1121N/A// g_message("Need to set DpiY: %s", tmp);
1121N/A //gdk_pixbuf_set_option( buf, "Inkscape::DpiY", tmp );
1121N/A g_free( tmp );
1121N/A }
1121N/A }
1121N/A }
1121N/A }
1121N/A else
1121N/A {
1121N/A // do something
1121N/A g_message("error loading pixbuf at close");
1121N/A }
1121N/A
1121N/A g_object_unref(loader);
1121N/A }
1121N/A else
1121N/A {
1121N/A g_message("error when creating pixbuf loader");
1121N/A }
1121N/A fclose( fp );
1121N/A fp = NULL;
1121N/A }
1121N/A else
1121N/A {
1121N/A g_warning ("Unable to open linked file: %s", filename);
1121N/A }
1121N/A
1121N/A/*
387N/A if ( buf )
1121N/A {
1121N/A const gchar* bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiX" );
1121N/A if ( bloop )
1121N/A {
1121N/A g_message("DPI X is [%s]", bloop);
1121N/A }
1121N/A bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiY" );
1121N/A if ( bloop )
1121N/A {
1121N/A g_message("DPI Y is [%s]", bloop);
1121N/A }
1121N/A }
1121N/A*/
1121N/A
1121N/A return buf;
1121N/A}
1121N/A
1121N/AGdkPixbuf* pixbuf_new_from_file( const char *filename, GError **error )
1121N/A{
1121N/A time_t modTime = 0;
1121N/A gchar* pixPath = 0;
1121N/A GdkPixbuf* result = pixbuf_new_from_file( filename, modTime, pixPath, error );
1121N/A if (pixPath) {
1121N/A g_free(pixPath);
387N/A }
1121N/A return result;
1121N/A}
1121N/A
1121N/A
1121N/A}
1121N/A}
1121N/A
1121N/AGType
1121N/Asp_image_get_type (void)
1121N/A{
1121N/A static GType image_type = 0;
387N/A if (!image_type) {
1121N/A GTypeInfo image_info = {
1121N/A sizeof (SPImageClass),
1121N/A NULL, /* base_init */
1121N/A NULL, /* base_finalize */
1121N/A (GClassInitFunc) sp_image_class_init,
1121N/A NULL, /* class_finalize */
1121N/A NULL, /* class_data */
1121N/A sizeof (SPImage),
1121N/A 16, /* n_preallocs */
1121N/A (GInstanceInitFunc) sp_image_init,
1121N/A NULL, /* value_table */
1121N/A };
1121N/A image_type = g_type_register_static (sp_item_get_type (), "SPImage", &image_info, (GTypeFlags)0);
1121N/A }
1121N/A return image_type;
387N/A}
1121N/A
1121N/Astatic void
1121N/Asp_image_class_init (SPImageClass * klass)
1121N/A{
1121N/A GObjectClass * gobject_class;
1121N/A SPObjectClass * sp_object_class;
1121N/A SPItemClass * item_class;
1121N/A
1121N/A gobject_class = (GObjectClass *) klass;
1121N/A sp_object_class = (SPObjectClass *) klass;
1121N/A item_class = (SPItemClass *) klass;
1121N/A
1121N/A parent_class = (SPItemClass*)g_type_class_ref (sp_item_get_type ());
1121N/A
1121N/A sp_object_class->build = sp_image_build;
1121N/A sp_object_class->release = sp_image_release;
1121N/A sp_object_class->set = sp_image_set;
1121N/A sp_object_class->update = sp_image_update;
1121N/A sp_object_class->write = sp_image_write;
1121N/A
1121N/A item_class->bbox = sp_image_bbox;
1121N/A item_class->print = sp_image_print;
1121N/A item_class->description = sp_image_description;
1121N/A item_class->show = sp_image_show;
1121N/A item_class->snappoints = sp_image_snappoints;
1121N/A item_class->set_transform = sp_image_set_transform;
387N/A}
1121N/A
1121N/Astatic void sp_image_init( SPImage *image )
1121N/A{
1121N/A image->x.unset();
1121N/A image->y.unset();
1121N/A image->width.unset();
1121N/A image->height.unset();
1121N/A image->aspect_align = SP_ASPECT_NONE;
1121N/A
1121N/A image->trimx = 0;
1121N/A image->trimy = 0;
387N/A image->trimwidth = 0;
1121N/A image->trimheight = 0;
1121N/A image->viewx = 0;
1121N/A image->viewy = 0;
387N/A image->viewwidth = 0;
1121N/A image->viewheight = 0;
1121N/A
1121N/A image->curve = NULL;
1121N/A
1121N/A image->href = 0;
1121N/A#if ENABLE_LCMS
1121N/A image->color_profile = 0;
1121N/A#endif // ENABLE_LCMS
1121N/A image->pixbuf = 0;
1121N/A image->pixPath = 0;
1121N/A image->lastMod = 0;
387N/A}
1121N/A
1121N/Astatic void
1121N/Asp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
1121N/A{
1121N/A if (((SPObjectClass *) parent_class)->build)
1121N/A ((SPObjectClass *) parent_class)->build (object, document, repr);
1121N/A
1121N/A sp_object_read_attr (object, "xlink:href");
1121N/A sp_object_read_attr (object, "x");
1121N/A sp_object_read_attr (object, "y");
1121N/A sp_object_read_attr (object, "width");
1121N/A sp_object_read_attr (object, "height");
1121N/A sp_object_read_attr (object, "preserveAspectRatio");
1121N/A sp_object_read_attr (object, "color-profile");
1121N/A
387N/A /* Register */
1121N/A sp_document_add_resource (document, "image", object);
1121N/A}
1121N/A
1121N/Astatic void
1121N/Asp_image_release (SPObject *object)
1121N/A{
1121N/A SPImage *image = SP_IMAGE(object);
1121N/A
1121N/A if (SP_OBJECT_DOCUMENT (object)) {
1121N/A /* Unregister ourselves */
1121N/A sp_document_remove_resource (SP_OBJECT_DOCUMENT (object), "image", SP_OBJECT (object));
1121N/A }
1121N/A
387N/A if (image->href) {
1121N/A g_free (image->href);
387N/A image->href = NULL;
1121N/A }
1121N/A
1121N/A if (image->pixbuf) {
1121N/A gdk_pixbuf_unref (image->pixbuf);
1121N/A image->pixbuf = NULL;
1121N/A }
1121N/A
1121N/A#if ENABLE_LCMS
1121N/A if (image->color_profile) {
1121N/A g_free (image->color_profile);
1121N/A image->color_profile = NULL;
387N/A }
1121N/A#endif // ENABLE_LCMS
1121N/A
1121N/A if (image->pixPath) {
1121N/A g_free(image->pixPath);
1121N/A image->pixPath = 0;
1121N/A }
1121N/A
1121N/A if (image->curve) {
1121N/A image->curve = image->curve->unref();
1121N/A }
1121N/A
1121N/A if (((SPObjectClass *) parent_class)->release) {
387N/A ((SPObjectClass *) parent_class)->release (object);
1121N/A }
1121N/A}
1121N/A
1121N/Astatic void
1121N/Asp_image_set (SPObject *object, unsigned int key, const gchar *value)
1121N/A{
1121N/A SPImage *image;
1121N/A
1121N/A image = SP_IMAGE (object);
1121N/A
1121N/A switch (key) {
1121N/A case SP_ATTR_XLINK_HREF:
1121N/A g_free (image->href);
387N/A image->href = (value) ? g_strdup (value) : NULL;
1121N/A object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
1121N/A break;
1121N/A case SP_ATTR_X:
1121N/A if (!image->x.readAbsolute(value)) {
1121N/A /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
1121N/A image->x.unset();
1121N/A }
1121N/A object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1121N/A break;
1121N/A case SP_ATTR_Y:
1121N/A if (!image->y.readAbsolute(value)) {
1121N/A /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
1121N/A image->y.unset();
387N/A }
1121N/A object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1121N/A break;
1121N/A case SP_ATTR_WIDTH:
1121N/A if (!image->width.readAbsolute(value)) {
1121N/A /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
1121N/A image->width.unset();
1121N/A }
1121N/A object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1121N/A break;
1121N/A case SP_ATTR_HEIGHT:
1121N/A if (!image->height.readAbsolute(value)) {
1121N/A /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
1121N/A image->height.unset();
1121N/A }
1121N/A object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1121N/A break;
1121N/A case SP_ATTR_PRESERVEASPECTRATIO:
1121N/A /* Do setup before, so we can use break to escape */
387N/A image->aspect_align = SP_ASPECT_NONE;
1121N/A image->aspect_clip = SP_ASPECT_MEET;
1121N/A object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
1121N/A if (value) {
1121N/A int len;
1121N/A gchar c[256];
1121N/A const gchar *p, *e;
1121N/A unsigned int align, clip;
1121N/A p = value;
1121N/A while (*p && *p == 32) p += 1;
1121N/A if (!*p) break;
1121N/A e = p;
1121N/A while (*e && *e != 32) e += 1;
1121N/A len = e - p;
1121N/A if (len > 8) break;
1121N/A memcpy (c, value, len);
1121N/A c[len] = 0;
1121N/A /* Now the actual part */
1121N/A if (!strcmp (c, "none")) {
1121N/A align = SP_ASPECT_NONE;
1121N/A } else if (!strcmp (c, "xMinYMin")) {
1121N/A align = SP_ASPECT_XMIN_YMIN;
1121N/A } else if (!strcmp (c, "xMidYMin")) {
1121N/A align = SP_ASPECT_XMID_YMIN;
1121N/A } else if (!strcmp (c, "xMaxYMin")) {
387N/A align = SP_ASPECT_XMAX_YMIN;
1121N/A } else if (!strcmp (c, "xMinYMid")) {
1121N/A align = SP_ASPECT_XMIN_YMID;
1121N/A } else if (!strcmp (c, "xMidYMid")) {
1121N/A align = SP_ASPECT_XMID_YMID;
1121N/A } else if (!strcmp (c, "xMaxYMid")) {
387N/A align = SP_ASPECT_XMAX_YMID;
1121N/A } else if (!strcmp (c, "xMinYMax")) {
1121N/A align = SP_ASPECT_XMIN_YMAX;
1121N/A } else if (!strcmp (c, "xMidYMax")) {
1121N/A align = SP_ASPECT_XMID_YMAX;
1121N/A } else if (!strcmp (c, "xMaxYMax")) {
1121N/A align = SP_ASPECT_XMAX_YMAX;
1121N/A } else {
1121N/A break;
1121N/A }
1121N/A clip = SP_ASPECT_MEET;
1121N/A while (*e && *e == 32) e += 1;
1121N/A if (e) {
1121N/A if (!strcmp (e, "meet")) {
1121N/A clip = SP_ASPECT_MEET;
1121N/A } else if (!strcmp (e, "slice")) {
1121N/A clip = SP_ASPECT_SLICE;
1121N/A } else {
1121N/A break;
1121N/A }
387N/A }
1121N/A image->aspect_align = align;
1121N/A image->aspect_clip = clip;
1121N/A }
1121N/A break;
1121N/A#if ENABLE_LCMS
1121N/A case SP_PROP_COLOR_PROFILE:
1121N/A if ( image->color_profile ) {
1121N/A g_free (image->color_profile);
1121N/A }
1121N/A image->color_profile = (value) ? g_strdup (value) : NULL;
1121N/A#ifdef DEBUG_LCMS
1121N/A if ( value ) {
1121N/A DEBUG_MESSAGE( lcmsFour, "<image> color-profile set to '%s'", value );
1121N/A } else {
1121N/A DEBUG_MESSAGE( lcmsFour, "<image> color-profile cleared" );
1121N/A }
1121N/A#endif // DEBUG_LCMS
1121N/A // TODO check on this HREF_MODIFIED flag
1121N/A object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
1121N/A break;
1121N/A#endif // ENABLE_LCMS
1121N/A default:
1121N/A if (((SPObjectClass *) (parent_class))->set)
1121N/A ((SPObjectClass *) (parent_class))->set (object, key, value);
1121N/A break;
387N/A }
1121N/A
1121N/A sp_image_set_curve(image); //creates a curve at the image's boundary for snapping
1121N/A}
1121N/A
1121N/Astatic void
1121N/Asp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
1121N/A{
1121N/A SPImage *image = SP_IMAGE(object);
1121N/A SPDocument *doc = SP_OBJECT_DOCUMENT(object);
1121N/A
1121N/A if (((SPObjectClass *) (parent_class))->update)
1121N/A ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
705N/A
1121N/A if (flags & SP_IMAGE_HREF_MODIFIED_FLAG) {
1121N/A if (image->pixbuf) {
1121N/A gdk_pixbuf_unref (image->pixbuf);
1121N/A image->pixbuf = NULL;
1121N/A }
1121N/A if ( image->pixPath ) {
1121N/A g_free(image->pixPath);
705N/A image->pixPath = 0;
1121N/A }
1121N/A image->lastMod = 0;
1121N/A if (image->href) {
1121N/A GdkPixbuf *pixbuf;
705N/A pixbuf = sp_image_repr_read_image (
image->lastMod,
image->pixPath,
object->repr->attribute("xlink:href"),
object->repr->attribute("sodipodi:absref"),
doc->base);
if (pixbuf) {
pixbuf = sp_image_pixbuf_force_rgba (pixbuf);
// BLIP
#if ENABLE_LCMS
if ( image->color_profile )
{
int imagewidth = gdk_pixbuf_get_width( pixbuf );
int imageheight = gdk_pixbuf_get_height( pixbuf );
int rowstride = gdk_pixbuf_get_rowstride( pixbuf );
guchar* px = gdk_pixbuf_get_pixels( pixbuf );
if ( px ) {
#ifdef DEBUG_LCMS
DEBUG_MESSAGE( lcmsFive, "in <image>'s sp_image_update. About to call colorprofile_get_handle()" );
#endif // DEBUG_LCMS
guint profIntent = Inkscape::RENDERING_INTENT_UNKNOWN;
cmsHPROFILE prof = Inkscape::colorprofile_get_handle( SP_OBJECT_DOCUMENT( object ),
&profIntent,
image->color_profile );
if ( prof ) {
icProfileClassSignature profileClass = cmsGetDeviceClass( prof );
if ( profileClass != icSigNamedColorClass ) {
int intent = INTENT_PERCEPTUAL;
switch ( profIntent ) {
case Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC:
intent = INTENT_RELATIVE_COLORIMETRIC;
break;
case Inkscape::RENDERING_INTENT_SATURATION:
intent = INTENT_SATURATION;
break;
case Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC:
intent = INTENT_ABSOLUTE_COLORIMETRIC;
break;
case Inkscape::RENDERING_INTENT_PERCEPTUAL:
case Inkscape::RENDERING_INTENT_UNKNOWN:
case Inkscape::RENDERING_INTENT_AUTO:
default:
intent = INTENT_PERCEPTUAL;
}
cmsHPROFILE destProf = cmsCreate_sRGBProfile();
cmsHTRANSFORM transf = cmsCreateTransform( prof,
TYPE_RGBA_8,
destProf,
TYPE_RGBA_8,
intent, 0 );
if ( transf ) {
guchar* currLine = px;
for ( int y = 0; y < imageheight; y++ ) {
// Since the types are the same size, we can do the transformation in-place
cmsDoTransform( transf, currLine, currLine, imagewidth );
currLine += rowstride;
}
cmsDeleteTransform( transf );
}
#ifdef DEBUG_LCMS
else
{
DEBUG_MESSAGE( lcmsSix, "in <image>'s sp_image_update. Unable to create LCMS transform." );
}
#endif // DEBUG_LCMS
cmsCloseProfile( destProf );
}
#ifdef DEBUG_LCMS
else
{
DEBUG_MESSAGE( lcmsSeven, "in <image>'s sp_image_update. Profile type is named color. Can't transform." );
}
#endif // DEBUG_LCMS
}
#ifdef DEBUG_LCMS
else
{
DEBUG_MESSAGE( lcmsEight, "in <image>'s sp_image_update. No profile found." );
}
#endif // DEBUG_LCMS
}
}
#endif // ENABLE_LCMS
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::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
{
SPImage *image;
image = SP_IMAGE (object);
if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
repr = xml_doc->createElement("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 ENABLE_LCMS
if (image->color_profile) repr->setAttribute("color-profile", image->color_profile);
#endif // ENABLE_LCMS
if (((SPObjectClass *) (parent_class))->write)
((SPObjectClass *) (parent_class))->write (object, xml_doc, 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;
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;
NR::Matrix t;
if (image->aspect_align == SP_ASPECT_NONE) {
/* fixme: (Lauris) */
NR::translate tp = NR::translate(image->x.computed, image->y.computed);
NR::scale s = NR::scale(image->width.computed, -image->height.computed);
NR::translate ti = NR::translate(0.0, -1.0);
t = s * tp;
t = ti * t;
} else { // preserveAspectRatio
NR::translate tp = NR::translate(image->viewx, image->viewy);
NR::scale s = NR::scale(image->viewwidth, -image->viewheight);
NR::translate ti = NR::translate(0.0, -1.0);
t = s * tp;
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);
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),
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( time_t& modTime, char*& pixPath, const gchar *href, const gchar *absref, const gchar *base )
{
const gchar *filename, *docbase;
gchar *fullname;
GdkPixbuf *pixbuf;
modTime = 0;
if ( pixPath ) {
g_free(pixPath);
pixPath = 0;
}
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, modTime, pixPath, 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 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, modTime, pixPath, NULL );
g_free (fullname);
if (pixbuf != NULL) 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, modTime, pixPath, 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, modTime, pixPath, 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);
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),
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)
{
/* 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
//TODO Do return snappoints, but only when within visual bounding box
} 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 (from_2geom(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;
}
}
/*
* 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;
}
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 = image->curve->unref();
}
return;
}
NRRect rect;
sp_image_bbox(image, &rect, NR::identity(), 0);
Geom::Rect rect2 = to_2geom(*rect.upgrade());
SPCurve *c = SPCurve::new_from_rect(rect2);
if (image->curve) {
image->curve = image->curve->unref();
}
if (c) {
image->curve = c->ref();
}
c->unref();
}
/**
* 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 image->curve->copy();
}
return NULL;
}
void sp_image_refresh_if_outdated( SPImage* image )
{
if ( image->href && image->lastMod ) {
// It *might* change
struct stat st;
memset(&st, 0, sizeof(st));
int val = g_stat(image->pixPath, &st);
if ( !val ) {
// stat call worked. Check time now
if ( st.st_mtime != image->lastMod ) {
SPCtx *ctx = 0;
unsigned int flags = SP_IMAGE_HREF_MODIFIED_FLAG;
sp_image_update(image, ctx, flags);
}
}
}
}
/*
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 :