Code

more unreffing temporary styles properly
[inkscape.git] / src / sp-image.cpp
1 #define __SP_IMAGE_C__
3 /*
4  * SVG <image> implementation
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Edward Flick (EAF)
9  *
10  * Copyright (C) 1999-2005 Authors
11  * Copyright (C) 2000-2001 Ximian, Inc.
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19 #include <libnr/nr-matrix-fns.h>
21 //#define GDK_PIXBUF_ENABLE_BACKEND 1
22 //#include <gdk-pixbuf/gdk-pixbuf-io.h>
23 #include "display/nr-arena-image.h"
24 #include <display/curve.h>
26 //Added for preserveAspectRatio support -- EAF
27 #include "enums.h"
28 #include "attributes.h"
30 #include "print.h"
31 #include "brokenimage.xpm"
32 #include "document.h"
33 #include "sp-image.h"
34 #include "sp-clippath.h"
35 #include <glibmm/i18n.h>
36 #include "xml/quote.h"
37 #include <xml/repr.h>
39 #include "io/sys.h"
40 #include <png.h>
41 #if ENABLE_LCMS
42 #include "color-profile-fns.h"
43 #include "color-profile.h"
44 //#define DEBUG_LCMS
45 #ifdef DEBUG_LCMS
46 #include "prefs-utils.h"
47 #include <gtk/gtkmessagedialog.h>
48 #endif // DEBUG_LCMS
49 #endif // ENABLE_LCMS
50 /*
51  * SPImage
52  */
55 static void sp_image_class_init (SPImageClass * klass);
56 static void sp_image_init (SPImage * image);
58 static void sp_image_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
59 static void sp_image_release (SPObject * object);
60 static void sp_image_set (SPObject *object, unsigned int key, const gchar *value);
61 static void sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags);
62 static Inkscape::XML::Node *sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
64 static void sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
65 static void sp_image_print (SPItem * item, SPPrintContext *ctx);
66 static gchar * sp_image_description (SPItem * item);
67 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p);
68 static NRArenaItem *sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
69 static NR::Matrix sp_image_set_transform (SPItem *item, NR::Matrix const &xform);
70 static void sp_image_set_curve(SPImage *image);
73 GdkPixbuf *sp_image_repr_read_image (const gchar *href, const gchar *absref, const gchar *base);
74 static GdkPixbuf *sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf);
75 static void sp_image_update_canvas_image (SPImage *image);
76 static GdkPixbuf * sp_image_repr_read_dataURI (const gchar * uri_data);
77 static GdkPixbuf * sp_image_repr_read_b64 (const gchar * uri_data);
79 static SPItemClass *parent_class;
82 extern "C"
83 {
84     void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length );
85     void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length );
86     void user_flush_data( png_structp png_ptr );
88 }
91 #ifdef DEBUG_LCMS
92 extern guint update_in_progress;
93 #define DEBUG_MESSAGE(key, ...) \
94 {\
95     gint dump = prefs_get_int_attribute_limited("options.scislac", #key, 0, 0, 1);\
96     gint dumpD = prefs_get_int_attribute_limited("options.scislac", #key"D", 0, 0, 1);\
97     gint dumpD2 = prefs_get_int_attribute_limited("options.scislac", #key"D2", 0, 0, 1);\
98     dumpD &= ( (update_in_progress == 0) || dumpD2 );\
99     if ( dump )\
100     {\
101         g_message( __VA_ARGS__ );\
103     }\
104     if ( dumpD )\
105     {\
106         GtkWidget *dialog = gtk_message_dialog_new(NULL,\
107                                                    GTK_DIALOG_DESTROY_WITH_PARENT, \
108                                                    GTK_MESSAGE_INFO,    \
109                                                    GTK_BUTTONS_OK,      \
110                                                    __VA_ARGS__          \
111                                                    );\
112         g_signal_connect_swapped(dialog, "response",\
113                                  G_CALLBACK(gtk_widget_destroy),        \
114                                  dialog);                               \
115         gtk_widget_show_all( dialog );\
116     }\
118 #endif // DEBUG_LCMS
120 namespace Inkscape {
121 namespace IO {
123 class PushPull
125 public:
126     gboolean    first;
127     FILE*       fp;
128     guchar*     scratch;
129     gsize       size;
130     gsize       used;
131     gsize       offset;
132     GdkPixbufLoader *loader;
134     PushPull() : first(TRUE),
135                  fp(0),
136                  scratch(0),
137                  size(0),
138                  used(0),
139                  offset(0),
140                  loader(0) {};
142     gboolean readMore()
143     {
144         gboolean good = FALSE;
145         if ( offset )
146         {
147             g_memmove( scratch, scratch + offset, used - offset );
148             used -= offset;
149             offset = 0;
150         }
151         if ( used < size )
152         {
153             gsize space = size - used;
154             gsize got = fread( scratch + used, 1, space, fp );
155             if ( got )
156             {
157                 if ( loader )
158                 {
159                     GError *err = NULL;
160                     //g_message( " __read %d bytes", (int)got );
161                     if ( !gdk_pixbuf_loader_write( loader, scratch + used, got, &err ) )
162                     {
163                         //g_message("_error writing pixbuf data");
164                     }
165                 }
167                 used += got;
168                 good = TRUE;
169             }
170             else
171             {
172                 good = FALSE;
173             }
174         }
175         return good;
176     }
178     gsize available() const
179     {
180         return (used - offset);
181     }
183     gsize readOut( gpointer data, gsize length )
184     {
185         gsize giving = available();
186         if ( length < giving )
187         {
188             giving = length;
189         }
190         g_memmove( data, scratch + offset, giving );
191         offset += giving;
192         if ( offset >= used )
193         {
194             offset = 0;
195             used = 0;
196         }
197         return giving;
198     }
200     void clear()
201     {
202         offset = 0;
203         used = 0;
204     }
206 private:
207     PushPull& operator = (const PushPull& other);
208     PushPull(const PushPull& other);
209 };
211 void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length )
213 //    g_message( "user_read_data(%d)", length );
215     PushPull* youme = (PushPull*)png_get_io_ptr(png_ptr);
217     gsize filled = 0;
218     gboolean canRead = TRUE;
220     while ( filled < length && canRead )
221     {
222         gsize some = youme->readOut( data + filled, length - filled );
223         filled += some;
224         if ( filled < length )
225         {
226             canRead &= youme->readMore();
227         }
228     }
229 //    g_message("things out");
232 void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length )
234     //g_message( "user_write_data(%d)", length );
237 void user_flush_data( png_structp png_ptr )
239     //g_message( "user_flush_data" );
242 GdkPixbuf*  pixbuf_new_from_file( const char *filename, GError **error )
244     GdkPixbuf* buf = NULL;
245     PushPull youme;
246     gint dpiX = 0;
247     gint dpiY = 0;
249     //buf = gdk_pixbuf_new_from_file( filename, error );
250     dump_fopen_call( filename, "pixbuf_new_from_file" );
251     FILE* fp = fopen_utf8name( filename, "r" );
252     if ( fp )
253     {
254         GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
255         if ( loader )
256         {
257             GError *err = NULL;
259             // short buffer
260             guchar scratch[1024];
261             gboolean latter = FALSE;
262             gboolean isPng = FALSE;
263             png_structp pngPtr = NULL;
264             png_infop infoPtr = NULL;
265             //png_infop endPtr = NULL;
267             youme.fp = fp;
268             youme.scratch = scratch;
269             youme.size = sizeof(scratch);
270             youme.used = 0;
271             youme.offset = 0;
272             youme.loader = loader;
274             while ( !feof(fp) )
275             {
276                 if ( youme.readMore() )
277                 {
278                     if ( youme.first )
279                     {
280                         //g_message( "First data chunk" );
281                         youme.first = FALSE;
282                         isPng = !png_sig_cmp( scratch + youme.offset, 0, youme.available() );
283                         //g_message( "  png? %s", (isPng ? "Yes":"No") );
284                         if ( isPng )
285                         {
286                             pngPtr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
287                                                              NULL,//(png_voidp)user_error_ptr,
288                                                              NULL,//user_error_fn,
289                                                              NULL//user_warning_fn
290                                 );
291                             if ( pngPtr )
292                             {
293                                 infoPtr = png_create_info_struct( pngPtr );
294                                 //endPtr = png_create_info_struct( pngPtr );
296                                 png_set_read_fn( pngPtr, &youme, user_read_data );
297                                 //g_message( "In" );
299                                 //png_read_info( pngPtr, infoPtr );
300                                 png_read_png( pngPtr, infoPtr, PNG_TRANSFORM_IDENTITY, NULL );
302                                 //g_message("out");
304                                 //png_read_end(pngPtr, endPtr);
306                                 /*
307                                 if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_pHYs ) )
308                                 {
309                                     g_message("pHYs chunk now valid" );
310                                 }
311                                 if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_sCAL ) )
312                                 {
313                                     g_message("sCAL chunk now valid" );
314                                 }
315                                 */
317                                 png_uint_32 res_x = 0;
318                                 png_uint_32 res_y = 0;
319                                 int unit_type = 0;
320                                 if ( png_get_pHYs( pngPtr, infoPtr, &res_x, &res_y, &unit_type) )
321                                 {
322 //                                     g_message( "pHYs yes (%d, %d) %d (%s)", (int)res_x, (int)res_y, unit_type,
323 //                                                (unit_type == 1? "per meter" : "unknown")
324 //                                         );
326 //                                     g_message( "    dpi: (%d, %d)",
327 //                                                (int)(0.5 + ((double)res_x)/39.37),
328 //                                                (int)(0.5 + ((double)res_y)/39.37) );
329                                     if ( unit_type == PNG_RESOLUTION_METER )
330                                     {
331                                         // TODO come up with a more accurate DPI setting
332                                         dpiX = (int)(0.5 + ((double)res_x)/39.37);
333                                         dpiY = (int)(0.5 + ((double)res_y)/39.37);
334                                     }
335                                 }
336                                 else
337                                 {
338 //                                     g_message( "pHYs no" );
339                                 }
341 /*
342                                 double width = 0;
343                                 double height = 0;
344                                 int unit = 0;
345                                 if ( png_get_sCAL(pngPtr, infoPtr, &unit, &width, &height) )
346                                 {
347                                     gchar* vals[] = {
348                                         "unknown", // PNG_SCALE_UNKNOWN
349                                         "meter", // PNG_SCALE_METER
350                                         "radian", // PNG_SCALE_RADIAN
351                                         "last", //
352                                         NULL
353                                     };
355                                     g_message( "sCAL: (%f, %f) %d (%s)",
356                                                width, height, unit,
357                                                ((unit >= 0 && unit < 3) ? vals[unit]:"???")
358                                         );
359                                 }
360 */
362                                 // now clean it up.
363                                 png_destroy_read_struct( &pngPtr, &infoPtr, NULL );//&endPtr );
364                             }
365                             else
366                             {
367                                 g_message("Error when creating PNG read struct");
368                             }
369                         }
370                     }
371                     else if ( !latter )
372                     {
373                         latter = TRUE;
374                         //g_message("  READing latter");
375                     }
376                     // Now clear out the buffer so we can read more.
377                     // (dumping out unused)
378                     youme.clear();
379                 }
380             }
382             gboolean ok = gdk_pixbuf_loader_close(loader, &err);
383             if ( ok )
384             {
385                 buf = gdk_pixbuf_loader_get_pixbuf( loader );
386                 if ( buf )
387                 {
388                     g_object_ref(buf);
390                     if ( dpiX )
391                     {
392                         gchar *tmp = g_strdup_printf( "%d", dpiX );
393                         if ( tmp )
394                         {
395                             //gdk_pixbuf_set_option( buf, "Inkscape::DpiX", tmp );
396                             g_free( tmp );
397                         }
398                     }
399                     if ( dpiY )
400                     {
401                         gchar *tmp = g_strdup_printf( "%d", dpiY );
402                         if ( tmp )
403                         {
404                             //gdk_pixbuf_set_option( buf, "Inkscape::DpiY", tmp );
405                             g_free( tmp );
406                         }
407                     }
408                 }
409             }
410             else
411             {
412                 // do something
413                 g_message("error loading pixbuf at close");
414             }
416             g_object_unref(loader);
417         }
418         else
419         {
420             g_message("error when creating pixbuf loader");
421         }
422         fclose( fp );
423         fp = NULL;
424     }
425     else
426     {
427         g_warning ("Unable to open linked file: %s", filename);
428     }
430 /*
431     if ( buf )
432     {
433         const gchar* bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiX" );
434         if ( bloop )
435         {
436             g_message("DPI X is [%s]", bloop);
437         }
438         bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiY" );
439         if ( bloop )
440         {
441             g_message("DPI Y is [%s]", bloop);
442         }
443     }
444 */
446     return buf;
452 GType
453 sp_image_get_type (void)
455         static GType image_type = 0;
456         if (!image_type) {
457                 GTypeInfo image_info = {
458                         sizeof (SPImageClass),
459                         NULL,   /* base_init */
460                         NULL,   /* base_finalize */
461                         (GClassInitFunc) sp_image_class_init,
462                         NULL,   /* class_finalize */
463                         NULL,   /* class_data */
464                         sizeof (SPImage),
465                         16,     /* n_preallocs */
466                         (GInstanceInitFunc) sp_image_init,
467                         NULL,   /* value_table */
468                 };
469                 image_type = g_type_register_static (sp_item_get_type (), "SPImage", &image_info, (GTypeFlags)0);
470         }
471         return image_type;
474 static void
475 sp_image_class_init (SPImageClass * klass)
477         GObjectClass * gobject_class;
478         SPObjectClass * sp_object_class;
479         SPItemClass * item_class;
481         gobject_class = (GObjectClass *) klass;
482         sp_object_class = (SPObjectClass *) klass;
483         item_class = (SPItemClass *) klass;
485         parent_class = (SPItemClass*)g_type_class_ref (sp_item_get_type ());
487         sp_object_class->build = sp_image_build;
488         sp_object_class->release = sp_image_release;
489         sp_object_class->set = sp_image_set;
490         sp_object_class->update = sp_image_update;
491         sp_object_class->write = sp_image_write;
493         item_class->bbox = sp_image_bbox;
494         item_class->print = sp_image_print;
495         item_class->description = sp_image_description;
496         item_class->show = sp_image_show;
497         item_class->snappoints = sp_image_snappoints;
498         item_class->set_transform = sp_image_set_transform;
501 static void
502 sp_image_init (SPImage *image)
504         image->x.unset();
505         image->y.unset();
506         image->width.unset();
507         image->height.unset();
508         image->aspect_align = SP_ASPECT_NONE;
509         image->curve = NULL;
512 static void
513 sp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
515         if (((SPObjectClass *) parent_class)->build)
516                 ((SPObjectClass *) parent_class)->build (object, document, repr);
518         sp_object_read_attr (object, "xlink:href");
519         sp_object_read_attr (object, "x");
520         sp_object_read_attr (object, "y");
521         sp_object_read_attr (object, "width");
522         sp_object_read_attr (object, "height");
523         sp_object_read_attr (object, "preserveAspectRatio");
524         sp_object_read_attr (object, "color-profile");
526         /* Register */
527         sp_document_add_resource (document, "image", object);
530 static void
531 sp_image_release (SPObject *object)
533         SPImage *image;
535         image = SP_IMAGE (object);
537         if (SP_OBJECT_DOCUMENT (object)) {
538                 /* Unregister ourselves */
539                 sp_document_remove_resource (SP_OBJECT_DOCUMENT (object), "image", SP_OBJECT (object));
540         }
542         if (image->href) {
543                 g_free (image->href);
544                 image->href = NULL;
545         }
547         if (image->pixbuf) {
548                 gdk_pixbuf_unref (image->pixbuf);
549                 image->pixbuf = NULL;
550         }
552 #if ENABLE_LCMS
553         if (image->color_profile) {
554                 g_free (image->color_profile);
555                 image->color_profile = NULL;
556         }
557 #endif // ENABLE_LCMS
559     if (image->curve) {
560                 image->curve = sp_curve_unref (image->curve);
561         }
563         if (((SPObjectClass *) parent_class)->release)
564                 ((SPObjectClass *) parent_class)->release (object);
567 static void
568 sp_image_set (SPObject *object, unsigned int key, const gchar *value)
570         SPImage *image;
572         image = SP_IMAGE (object);
574         switch (key) {
575         case SP_ATTR_XLINK_HREF:
576                 g_free (image->href);
577                 image->href = (value) ? g_strdup (value) : NULL;
578                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
579                 break;
580         case SP_ATTR_X:
581                 if (!image->x.readAbsolute(value)) {
582                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
583                         image->x.unset();
584                 }
585                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
586                 break;
587         case SP_ATTR_Y:
588                 if (!image->y.readAbsolute(value)) {
589                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
590                         image->y.unset();
591                 }
592                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
593                 break;
594         case SP_ATTR_WIDTH:
595                 if (!image->width.readAbsolute(value)) {
596                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
597                         image->width.unset();
598                 }
599                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
600                 break;
601         case SP_ATTR_HEIGHT:
602                 if (!image->height.readAbsolute(value)) {
603                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
604                         image->height.unset();
605                 }
606                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
607                 break;
608         case SP_ATTR_PRESERVEASPECTRATIO:
609                 /* Do setup before, so we can use break to escape */
610                 image->aspect_align = SP_ASPECT_NONE;
611                 image->aspect_clip = SP_ASPECT_MEET;
612                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
613                 if (value) {
614                         int len;
615                         gchar c[256];
616                         const gchar *p, *e;
617                         unsigned int align, clip;
618                         p = value;
619                         while (*p && *p == 32) p += 1;
620                         if (!*p) break;
621                         e = p;
622                         while (*e && *e != 32) e += 1;
623                         len = e - p;
624                         if (len > 8) break;
625                         memcpy (c, value, len);
626                         c[len] = 0;
627                         /* Now the actual part */
628                         if (!strcmp (c, "none")) {
629                                 align = SP_ASPECT_NONE;
630                         } else if (!strcmp (c, "xMinYMin")) {
631                                 align = SP_ASPECT_XMIN_YMIN;
632                         } else if (!strcmp (c, "xMidYMin")) {
633                                 align = SP_ASPECT_XMID_YMIN;
634                         } else if (!strcmp (c, "xMaxYMin")) {
635                                 align = SP_ASPECT_XMAX_YMIN;
636                         } else if (!strcmp (c, "xMinYMid")) {
637                                 align = SP_ASPECT_XMIN_YMID;
638                         } else if (!strcmp (c, "xMidYMid")) {
639                                 align = SP_ASPECT_XMID_YMID;
640                         } else if (!strcmp (c, "xMaxYMid")) {
641                                 align = SP_ASPECT_XMAX_YMID;
642                         } else if (!strcmp (c, "xMinYMax")) {
643                                 align = SP_ASPECT_XMIN_YMAX;
644                         } else if (!strcmp (c, "xMidYMax")) {
645                                 align = SP_ASPECT_XMID_YMAX;
646                         } else if (!strcmp (c, "xMaxYMax")) {
647                                 align = SP_ASPECT_XMAX_YMAX;
648                         } else {
649                                 break;
650                         }
651                         clip = SP_ASPECT_MEET;
652                         while (*e && *e == 32) e += 1;
653                         if (e) {
654                                 if (!strcmp (e, "meet")) {
655                                         clip = SP_ASPECT_MEET;
656                                 } else if (!strcmp (e, "slice")) {
657                                         clip = SP_ASPECT_SLICE;
658                                 } else {
659                                         break;
660                                 }
661                         }
662                         image->aspect_align = align;
663                         image->aspect_clip = clip;
664                 }
665                 break;
666 #if ENABLE_LCMS
667         case SP_PROP_COLOR_PROFILE:
668                 if ( image->color_profile ) {
669                     g_free (image->color_profile);
670                 }
671                 image->color_profile = (value) ? g_strdup (value) : NULL;
672 #ifdef DEBUG_LCMS
673                 if ( value ) {
674                     DEBUG_MESSAGE( lcmsFour, "<image> color-profile set to '%s'", value );
675                 } else {
676                     DEBUG_MESSAGE( lcmsFour, "<image> color-profile cleared" );
677                 }
678 #endif // DEBUG_LCMS
679                 // TODO check on this HREF_MODIFIED flag
680                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
681                 break;
682 #endif // ENABLE_LCMS
683         default:
684                 if (((SPObjectClass *) (parent_class))->set)
685                         ((SPObjectClass *) (parent_class))->set (object, key, value);
686                 break;
687         }
690 static void
691 sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
693     SPImage *image;
695     image = (SPImage *) object;
696     SPDocument *doc = SP_OBJECT_DOCUMENT(object);
698         if (((SPObjectClass *) (parent_class))->update)
699                 ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
701         if (flags & SP_IMAGE_HREF_MODIFIED_FLAG) {
702                 if (image->pixbuf) {
703                         gdk_pixbuf_unref (image->pixbuf);
704                         image->pixbuf = NULL;
705                 }
706                 if (image->href) {
707                         GdkPixbuf *pixbuf;
708                         pixbuf = sp_image_repr_read_image (
709                     object->repr->attribute("xlink:href"),
710                     object->repr->attribute("sodipodi:absref"),
711                     doc->base);
712                         if (pixbuf) {
713                                 pixbuf = sp_image_pixbuf_force_rgba (pixbuf);
714 // BLIP
715 #if ENABLE_LCMS
716                                 if ( image->color_profile )
717                                 {
718                                     int imagewidth = gdk_pixbuf_get_width( pixbuf );
719                                     int imageheight = gdk_pixbuf_get_height( pixbuf );
720                                     int rowstride = gdk_pixbuf_get_rowstride( pixbuf );
721                                     guchar* px = gdk_pixbuf_get_pixels( pixbuf );
723                                     if ( px ) {
724 #ifdef DEBUG_LCMS
725                                         DEBUG_MESSAGE( lcmsFive, "in <image>'s sp_image_update. About to call colorprofile_get_handle()" );
726 #endif // DEBUG_LCMS
727                                         guint profIntent = Inkscape::RENDERING_INTENT_UNKNOWN;
728                                         cmsHPROFILE prof = Inkscape::colorprofile_get_handle( SP_OBJECT_DOCUMENT( object ),
729                                                                                               &profIntent,
730                                                                                               image->color_profile );
731                                         if ( prof ) {
732                                             icProfileClassSignature profileClass = cmsGetDeviceClass( prof );
733                                             if ( profileClass != icSigNamedColorClass ) {
734                                                 int intent = INTENT_PERCEPTUAL;
735                                                 switch ( profIntent ) {
736                                                     case Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC:
737                                                         intent = INTENT_RELATIVE_COLORIMETRIC;
738                                                         break;
739                                                     case Inkscape::RENDERING_INTENT_SATURATION:
740                                                         intent = INTENT_SATURATION;
741                                                         break;
742                                                     case Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC:
743                                                         intent = INTENT_ABSOLUTE_COLORIMETRIC;
744                                                         break;
745                                                     case Inkscape::RENDERING_INTENT_PERCEPTUAL:
746                                                     case Inkscape::RENDERING_INTENT_UNKNOWN:
747                                                     case Inkscape::RENDERING_INTENT_AUTO:
748                                                     default:
749                                                         intent = INTENT_PERCEPTUAL;
750                                                 }
751                                                 cmsHPROFILE destProf = cmsCreate_sRGBProfile();
752                                                 cmsHTRANSFORM transf = cmsCreateTransform( prof,
753                                                                                            TYPE_RGBA_8,
754                                                                                            destProf,
755                                                                                            TYPE_RGBA_8,
756                                                                                            intent, 0 );
757                                                 if ( transf ) {
758                                                     guchar* currLine = px;
759                                                     for ( int y = 0; y < imageheight; y++ ) {
760                                                         // Since the types are the same size, we can do the transformation in-place
761                                                         cmsDoTransform( transf, currLine, currLine, imagewidth );
762                                                         currLine += rowstride;
763                                                     }
765                                                     cmsDeleteTransform( transf );
766                                                 }
767 #ifdef DEBUG_LCMS
768                                                 else
769                                                 {
770                                                     DEBUG_MESSAGE( lcmsSix, "in <image>'s sp_image_update. Unable to create LCMS transform." );
771                                                 }
772 #endif // DEBUG_LCMS
773                                                 cmsCloseProfile( destProf );
774                                             }
775 #ifdef DEBUG_LCMS
776                                             else
777                                             {
778                                                 DEBUG_MESSAGE( lcmsSeven, "in <image>'s sp_image_update. Profile type is named color. Can't transform." );
779                                             }
780 #endif // DEBUG_LCMS
781                                         }
782 #ifdef DEBUG_LCMS
783                                         else
784                                         {
785                                             DEBUG_MESSAGE( lcmsEight, "in <image>'s sp_image_update. No profile found." );
786                                         }
787 #endif // DEBUG_LCMS
788                                     }
789                                 }
790 #endif // ENABLE_LCMS
791                                 image->pixbuf = pixbuf;
792                         }
793                 }
794         }
795         // preserveAspectRatio calculate bounds / clipping rectangle -- EAF
796         if (image->pixbuf && (image->aspect_align != SP_ASPECT_NONE)) {
797                         int imagewidth, imageheight;
798                         double x,y;
800                         imagewidth = gdk_pixbuf_get_width (image->pixbuf);
801                         imageheight = gdk_pixbuf_get_height (image->pixbuf);
803                         switch (image->aspect_align) {
804                         case SP_ASPECT_XMIN_YMIN:
805                                 x = 0.0;
806                                 y = 0.0;
807                                 break;
808                         case SP_ASPECT_XMID_YMIN:
809                                 x = 0.5;
810                                 y = 0.0;
811                                 break;
812                         case SP_ASPECT_XMAX_YMIN:
813                                 x = 1.0;
814                                 y = 0.0;
815                                 break;
816                         case SP_ASPECT_XMIN_YMID:
817                                 x = 0.0;
818                                 y = 0.5;
819                                 break;
820                         case SP_ASPECT_XMID_YMID:
821                                 x = 0.5;
822                                 y = 0.5;
823                                 break;
824                         case SP_ASPECT_XMAX_YMID:
825                                 x = 1.0;
826                                 y = 0.5;
827                                 break;
828                         case SP_ASPECT_XMIN_YMAX:
829                                 x = 0.0;
830                                 y = 1.0;
831                                 break;
832                         case SP_ASPECT_XMID_YMAX:
833                                 x = 0.5;
834                                 y = 1.0;
835                                 break;
836                         case SP_ASPECT_XMAX_YMAX:
837                                 x = 1.0;
838                                 y = 1.0;
839                                 break;
840                         default:
841                                 x = 0.0;
842                                 y = 0.0;
843                                 break;
844                         }
846                         if (image->aspect_clip == SP_ASPECT_SLICE) {
847                                 image->viewx = image->x.computed;
848                                 image->viewy = image->y.computed;
849                                 image->viewwidth = image->width.computed;
850                                 image->viewheight = image->height.computed;
851                                 if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
852                                         // Pixels aspect is wider than bounding box
853                                         image->trimheight = imageheight;
854                                         image->trimwidth = static_cast<int>(static_cast<double>(imageheight) * image->width.computed / image->height.computed);
855                                         image->trimy = 0;
856                                         image->trimx = static_cast<int>(static_cast<double>(imagewidth - image->trimwidth) * x);
857                                 } else {
858                                         // Pixels aspect is taller than bounding box
859                                         image->trimwidth = imagewidth;
860                                         image->trimheight = static_cast<int>(static_cast<double>(imagewidth) * image->height.computed / image->width.computed);
861                                         image->trimx = 0;
862                                         image->trimy = static_cast<int>(static_cast<double>(imageheight - image->trimheight) * y);
863                                 }
864                         } else {
865                                 // Otherwise, assume SP_ASPECT_MEET
866                                 image->trimx = 0;
867                                 image->trimy = 0;
868                                 image->trimwidth = imagewidth;
869                                 image->trimheight = imageheight;
870                                 if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
871                                         // Pixels aspect is wider than bounding boz
872                                         image->viewwidth = image->width.computed;
873                                         image->viewheight = image->viewwidth * imageheight / imagewidth;
874                                         image->viewx=image->x.computed;
875                                         image->viewy=(image->height.computed - image->viewheight) * y + image->y.computed;
876                                 } else {
877                                         // Pixels aspect is taller than bounding box
878                                         image->viewheight = image->height.computed;
879                                         image->viewwidth = image->viewheight * imagewidth / imageheight;
880                                         image->viewy=image->y.computed;
881                                         image->viewx=(image->width.computed - image->viewwidth) * x + image->x.computed;
882                                 }
883                         }
884         }
885         sp_image_set_curve((SPImage *) object); //creates a curve at the image's boundary for snapping
886         sp_image_update_canvas_image ((SPImage *) object);
889 static Inkscape::XML::Node *
890 sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
892         SPImage *image;
894         image = SP_IMAGE (object);
896         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
897                 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
898                 repr = xml_doc->createElement("svg:image");
899         }
901         repr->setAttribute("xlink:href", image->href);
902         /* fixme: Reset attribute if needed (Lauris) */
903         if (image->x._set) sp_repr_set_svg_double(repr, "x", image->x.computed);
904         if (image->y._set) sp_repr_set_svg_double(repr, "y", image->y.computed);
905         if (image->width._set) sp_repr_set_svg_double(repr, "width", image->width.computed);
906         if (image->height._set) sp_repr_set_svg_double(repr, "height", image->height.computed);
907         repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
908 #if ENABLE_LCMS
909         if (image->color_profile) repr->setAttribute("color-profile", image->color_profile);
910 #endif // ENABLE_LCMS
912         if (((SPObjectClass *) (parent_class))->write)
913                 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
915         return repr;
918 static void
919 sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
921         SPImage const &image = *SP_IMAGE(item);
923         if ((image.width.computed > 0.0) && (image.height.computed > 0.0)) {
924                 double const x0 = image.x.computed;
925                 double const y0 = image.y.computed;
926                 double const x1 = x0 + image.width.computed;
927                 double const y1 = y0 + image.height.computed;
929                 nr_rect_union_pt(bbox, NR::Point(x0, y0) * transform);
930                 nr_rect_union_pt(bbox, NR::Point(x1, y0) * transform);
931                 nr_rect_union_pt(bbox, NR::Point(x1, y1) * transform);
932                 nr_rect_union_pt(bbox, NR::Point(x0, y1) * transform);
933         }
936 static void
937 sp_image_print (SPItem *item, SPPrintContext *ctx)
939         SPImage *image;
940         NRMatrix tp, ti, s, t;
941         guchar *px;
942         int w, h, rs, pixskip;
944         image = SP_IMAGE (item);
946         if (!image->pixbuf) return;
947         if ((image->width.computed <= 0.0) || (image->height.computed <= 0.0)) return;
949         px = gdk_pixbuf_get_pixels (image->pixbuf);
950         w = gdk_pixbuf_get_width (image->pixbuf);
951         h = gdk_pixbuf_get_height (image->pixbuf);
952         rs = gdk_pixbuf_get_rowstride (image->pixbuf);
953         pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
955         if (image->aspect_align == SP_ASPECT_NONE) {
956                 /* fixme: (Lauris) */
957                 nr_matrix_set_translate (&tp, image->x.computed, image->y.computed);
958                 nr_matrix_set_scale (&s, image->width.computed, -image->height.computed);
959                 nr_matrix_set_translate (&ti, 0.0, -1.0);
960         } else { // preserveAspectRatio
961                 nr_matrix_set_translate (&tp, image->viewx, image->viewy);
962                 nr_matrix_set_scale (&s, image->viewwidth, -image->viewheight);
963                 nr_matrix_set_translate (&ti, 0.0, -1.0);
964         }
966         nr_matrix_multiply (&t, &s, &tp);
967         nr_matrix_multiply (&t, &ti, &t);
969         if (image->aspect_align == SP_ASPECT_NONE)
970                 sp_print_image_R8G8B8A8_N (ctx, px, w, h, rs, &t, SP_OBJECT_STYLE (item));
971         else // preserveAspectRatio
972                 sp_print_image_R8G8B8A8_N (ctx, px + image->trimx*pixskip + image->trimy*rs, image->trimwidth, image->trimheight, rs, &t, SP_OBJECT_STYLE (item));
975 static gchar *
976 sp_image_description(SPItem *item)
978         SPImage *image = SP_IMAGE(item);
979         char *href_desc;
980         if (image->href) {
981             href_desc = (strncmp(image->href, "data:", 5) == 0)
982                 ? g_strdup(_("embedded"))
983                 : xml_quote_strdup(image->href);
984         } else {
985             g_warning("Attempting to call strncmp() with a null pointer.");
986             href_desc = g_strdup("(null_pointer)"); // we call g_free() on href_desc
987         }
989         char *ret = ( image->pixbuf == NULL
990                       ? g_strdup_printf(_("<b>Image with bad reference</b>: %s"), href_desc)
991                       : g_strdup_printf(_("<b>Image</b> %d &#215; %d: %s"),
992                                         gdk_pixbuf_get_width(image->pixbuf),
993                                         gdk_pixbuf_get_height(image->pixbuf),
994                                         href_desc) );
995         g_free(href_desc);
996         return ret;
999 static NRArenaItem *
1000 sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
1002         int pixskip, rs;
1003         SPImage * image;
1004         NRArenaItem *ai;
1006         image = (SPImage *) item;
1008         ai = NRArenaImage::create(arena);
1010         if (image->pixbuf) {
1011                 pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1012                 rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1013                 nr_arena_image_set_style(NR_ARENA_IMAGE(ai), SP_OBJECT_STYLE(SP_OBJECT(item)));
1014                 if (image->aspect_align == SP_ASPECT_NONE)
1015                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
1016                                            gdk_pixbuf_get_pixels (image->pixbuf),
1017                                            gdk_pixbuf_get_width (image->pixbuf),
1018                                            gdk_pixbuf_get_height (image->pixbuf),
1019                                            rs);
1020                 else // preserveAspectRatio
1021                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
1022                                            gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
1023                                            image->trimwidth,
1024                                            image->trimheight,
1025                                            rs);
1026         } else {
1027                 nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai), NULL, 0, 0, 0);
1028         }
1029         if (image->aspect_align == SP_ASPECT_NONE)
1030                 nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->x.computed, image->y.computed, image->width.computed, image->height.computed);
1031         else // preserveAspectRatio
1032                 nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->viewx, image->viewy, image->viewwidth, image->viewheight);
1034         return ai;
1037 /*
1038  * utility function to try loading image from href
1039  *
1040  * docbase/relative_src
1041  * absolute_src
1042  *
1043  */
1045 GdkPixbuf *
1046 sp_image_repr_read_image (const gchar *href, const gchar *absref, const gchar *base)
1048     const gchar *filename, *docbase;
1049     gchar *fullname;
1050     GdkPixbuf *pixbuf;
1052     filename = href;
1053     if (filename != NULL) {
1054         if (strncmp (filename,"file:",5) == 0) {
1055             fullname = g_filename_from_uri(filename, NULL, NULL);
1056             if (fullname) {
1057                 // TODO check this. Was doing a UTF-8 to filename conversion here.
1058                 pixbuf = Inkscape::IO::pixbuf_new_from_file (fullname, NULL);
1059                 if (pixbuf != NULL) return pixbuf;
1060             }
1061         } else if (strncmp (filename,"data:",5) == 0) {
1062             /* data URI - embedded image */
1063             filename += 5;
1064             pixbuf = sp_image_repr_read_dataURI (filename);
1065             if (pixbuf != NULL) return pixbuf;
1066         } else {
1068             if (!g_path_is_absolute (filename)) {
1069                 /* try to load from relative pos combined with document base*/
1070                 docbase = base;
1071                 if (!docbase) docbase = ".";
1072                 fullname = g_build_filename(docbase, filename, NULL);
1074                 // document base can be wrong (on the temporary doc when importing bitmap from a
1075                 // different dir) or unset (when doc is not saved yet), so we check for base+href existence first,
1076                 // and if it fails, we also try to use bare href regardless of its g_path_is_absolute
1077                 if (g_file_test (fullname, G_FILE_TEST_EXISTS) && !g_file_test (fullname, G_FILE_TEST_IS_DIR)) {
1078                     pixbuf = Inkscape::IO::pixbuf_new_from_file( fullname, NULL );
1079                     g_free (fullname);
1080                     if (pixbuf != NULL) return pixbuf;
1081                 }
1082             }
1084             /* try filename as absolute */
1085             if (g_file_test (filename, G_FILE_TEST_EXISTS) && !g_file_test (filename, G_FILE_TEST_IS_DIR)) {
1086                 pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
1087                 if (pixbuf != NULL) return pixbuf;
1088             }
1089         }
1090     }
1092     /* at last try to load from sp absolute path name */
1093     filename = absref;
1094     if (filename != NULL) {
1095         // using absref is outside of SVG rules, so we must at least warn the user
1096         if ( base != NULL && href != NULL )
1097                 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);
1098                 else
1099                     g_warning ("xlink:href did not resolve to a valid image file, now trying sodipodi:absref=\"%s\"", absref);
1101         pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
1102         if (pixbuf != NULL) return pixbuf;
1103     }
1104     /* Nope: We do not find any valid pixmap file :-( */
1105     pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) brokenimage_xpm);
1107     /* It should be included xpm, so if it still does not does load, */
1108     /* our libraries are broken */
1109     g_assert (pixbuf != NULL);
1111     return pixbuf;
1114 static GdkPixbuf *
1115 sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf)
1117         GdkPixbuf * newbuf;
1119         if (gdk_pixbuf_get_has_alpha (pixbuf)) return pixbuf;
1121         newbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
1122         gdk_pixbuf_unref (pixbuf);
1124         return newbuf;
1127 /* We assert that realpixbuf is either NULL or identical size to pixbuf */
1129 static void
1130 sp_image_update_canvas_image (SPImage *image)
1132         int rs, pixskip;
1133         SPItem *item;
1134         SPItemView *v;
1136         item = SP_ITEM (image);
1138         if (image->pixbuf) {
1139                 /* fixme: We are slightly violating spec here (Lauris) */
1140                 if (!image->width._set) {
1141                         image->width.computed = gdk_pixbuf_get_width (image->pixbuf);
1142                 }
1143                 if (!image->height._set) {
1144                         image->height.computed = gdk_pixbuf_get_height (image->pixbuf);
1145                 }
1146         }
1148         for (v = item->display; v != NULL; v = v->next) {
1149                 pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1150                 rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1151                 nr_arena_image_set_style (NR_ARENA_IMAGE(v->arenaitem), SP_OBJECT_STYLE(SP_OBJECT(image)));
1152                 if (image->aspect_align == SP_ASPECT_NONE) {
1153                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
1154                                            gdk_pixbuf_get_pixels (image->pixbuf),
1155                                            gdk_pixbuf_get_width (image->pixbuf),
1156                                            gdk_pixbuf_get_height (image->pixbuf),
1157                                            rs);
1158                         nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
1159                                              image->x.computed, image->y.computed,
1160                                              image->width.computed, image->height.computed);
1161                 } else { // preserveAspectRatio
1162                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
1163                                            gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
1164                                            image->trimwidth,
1165                                            image->trimheight,
1166                                            rs);
1167                         nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
1168                                              image->viewx, image->viewy,
1169                                              image->viewwidth, image->viewheight);
1170                 }
1171         }
1174 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p)
1176     /* An image doesn't have any nodes to snap, but still we want to be able snap one image 
1177     to another. Therefore we will create some snappoints at the corner, similar to a rect. If
1178     the image is rotated, then the snappoints will rotate with it. Again, just like a rect.
1179     */
1180      
1181     g_assert(item != NULL);
1182     g_assert(SP_IS_IMAGE(item));
1184     if (item->clip_ref->getObject()) {
1185         //We are looking at a clipped image: do not return any snappoints, as these might be
1186         //far far away from the visible part from the clipped image
1187     } else {
1188         // The image has not been clipped: return its corners, which might be rotated for example
1189         SPImage &image = *SP_IMAGE(item);
1190         double const x0 = image.x.computed;
1191                 double const y0 = image.y.computed;
1192                 double const x1 = x0 + image.width.computed;
1193                 double const y1 = y0 + image.height.computed;
1194                 NR::Matrix const i2d (sp_item_i2d_affine (item));
1195                 *p = NR::Point(x0, y0) * i2d;
1196         *p = NR::Point(x0, y1) * i2d;
1197         *p = NR::Point(x1, y1) * i2d;
1198         *p = NR::Point(x1, y0) * i2d;
1199     }
1202 /*
1203  * Initially we'll do:
1204  * Transform x, y, set x, y, clear translation
1205  */
1207 static NR::Matrix
1208 sp_image_set_transform(SPItem *item, NR::Matrix const &xform)
1210         SPImage *image = SP_IMAGE(item);
1212         /* Calculate position in parent coords. */
1213         NR::Point pos( NR::Point(image->x.computed, image->y.computed) * xform );
1215         /* This function takes care of translation and scaling, we return whatever parts we can't
1216            handle. */
1217         NR::Matrix ret(NR::transform(xform));
1218         NR::Point const scale(hypot(ret[0], ret[1]),
1219                               hypot(ret[2], ret[3]));
1220         if ( scale[NR::X] > 1e-9 ) {
1221                 ret[0] /= scale[NR::X];
1222                 ret[1] /= scale[NR::X];
1223         } else {
1224                 ret[0] = 1.0;
1225                 ret[1] = 0.0;
1226         }
1227         if ( scale[NR::Y] > 1e-9 ) {
1228                 ret[2] /= scale[NR::Y];
1229                 ret[3] /= scale[NR::Y];
1230         } else {
1231                 ret[2] = 0.0;
1232                 ret[3] = 1.0;
1233         }
1235         image->width = image->width.computed * scale[NR::X];
1236         image->height = image->height.computed * scale[NR::Y];
1238         /* Find position in item coords */
1239         pos = pos * ret.inverse();
1240         image->x = pos[NR::X];
1241         image->y = pos[NR::Y];
1243         item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1245         return ret;
1248 static GdkPixbuf *
1249 sp_image_repr_read_dataURI (const gchar * uri_data)
1250 {       GdkPixbuf * pixbuf = NULL;
1252         gint data_is_image = 0;
1253         gint data_is_base64 = 0;
1255         const gchar * data = uri_data;
1257         while (*data) {
1258                 if (strncmp (data,"base64",6) == 0) {
1259                         /* base64-encoding */
1260                         data_is_base64 = 1;
1261                         data_is_image = 1; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what
1262                         data += 6;
1263                 }
1264                 else if (strncmp (data,"image/png",9) == 0) {
1265                         /* PNG image */
1266                         data_is_image = 1;
1267                         data += 9;
1268                 }
1269                 else if (strncmp (data,"image/jpg",9) == 0) {
1270                         /* JPEG image */
1271                         data_is_image = 1;
1272                         data += 9;
1273                 }
1274                 else if (strncmp (data,"image/jpeg",10) == 0) {
1275                         /* JPEG image */
1276                         data_is_image = 1;
1277                         data += 10;
1278                 }
1279                 else { /* unrecognized option; skip it */
1280                         while (*data) {
1281                                 if (((*data) == ';') || ((*data) == ',')) break;
1282                                 data++;
1283                         }
1284                 }
1285                 if ((*data) == ';') {
1286                         data++;
1287                         continue;
1288                 }
1289                 if ((*data) == ',') {
1290                         data++;
1291                         break;
1292                 }
1293         }
1295         if ((*data) && data_is_image && data_is_base64) {
1296                 pixbuf = sp_image_repr_read_b64 (data);
1297         }
1299         return pixbuf;
1302 static GdkPixbuf *
1303 sp_image_repr_read_b64 (const gchar * uri_data)
1304 {       GdkPixbuf * pixbuf = NULL;
1305         GdkPixbufLoader * loader = NULL;
1307         gint j;
1308         gint k;
1309         gint l;
1310         gint b;
1311         gint len;
1312         gint eos = 0;
1313         gint failed = 0;
1315         guint32 bits;
1317         static const gchar B64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1319         const gchar* btr = uri_data;
1321         gchar ud[4];
1323         guchar bd[57];
1325         loader = gdk_pixbuf_loader_new ();
1327         if (loader == NULL) return NULL;
1329         while (eos == 0) {
1330                 l = 0;
1331                 for (j = 0; j < 19; j++) {
1332                         len = 0;
1333                         for (k = 0; k < 4; k++) {
1334                                 while (isspace ((int) (*btr))) {
1335                                         if ((*btr) == '\0') break;
1336                                         btr++;
1337                                 }
1338                                 if (eos) {
1339                                         ud[k] = 0;
1340                                         continue;
1341                                 }
1342                                 if (((*btr) == '\0') || ((*btr) == '=')) {
1343                                         eos = 1;
1344                                         ud[k] = 0;
1345                                         continue;
1346                                 }
1347                                 ud[k] = 64;
1348                                 for (b = 0; b < 64; b++) { /* There must a faster way to do this... ?? */
1349                                         if (B64[b] == (*btr)) {
1350                                                 ud[k] = (gchar) b;
1351                                                 break;
1352                                         }
1353                                 }
1354                                 if (ud[k] == 64) { /* data corruption ?? */
1355                                         eos = 1;
1356                                         ud[k] = 0;
1357                                         continue;
1358                                 }
1359                                 btr++;
1360                                 len++;
1361                         }
1362                         bits = (guint32) ud[0];
1363                         bits = (bits << 6) | (guint32) ud[1];
1364                         bits = (bits << 6) | (guint32) ud[2];
1365                         bits = (bits << 6) | (guint32) ud[3];
1366                         bd[l++] = (guchar) ((bits & 0xff0000) >> 16);
1367                         if (len > 2) {
1368                                 bd[l++] = (guchar) ((bits & 0xff00) >>  8);
1369                         }
1370                         if (len > 3) {
1371                                 bd[l++] = (guchar)  (bits & 0xff);
1372                         }
1373                 }
1375                 if (!gdk_pixbuf_loader_write (loader, (const guchar *) bd, (size_t) l, NULL)) {
1376                         failed = 1;
1377                         break;
1378                 }
1379         }
1381         gdk_pixbuf_loader_close (loader, NULL);
1383         if (!failed) pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
1385         return pixbuf;
1388 static void
1389 sp_image_set_curve(SPImage *image) 
1391     //create a curve at the image's boundary for snapping
1392     if ((image->height.computed < 1e-18) || (image->width.computed < 1e-18) || (image->clip_ref->getObject())) {
1393         if (image->curve) {
1394             image->curve = sp_curve_unref(image->curve);
1395         }
1396         return;
1397     }
1398     
1399     SPCurve *c = sp_curve_new();
1401     double const x = image->x.computed;
1402     double const y = image->y.computed;
1403     double const w = image->width.computed;
1404     double const h = image->height.computed;
1405     
1406     sp_curve_moveto(c, x, y);
1407     sp_curve_lineto(c, x + w, y);
1408     sp_curve_lineto(c, x + w, y + h);
1409     sp_curve_lineto(c, x, y + h);
1410     sp_curve_lineto(c, x, y);
1412     sp_curve_closepath_current(c);
1413     
1414     if (image->curve) {
1415         image->curve = sp_curve_unref(image->curve);
1416     }
1417     
1418     if (c) {
1419         image->curve = sp_curve_ref(c);
1420     }
1421     
1422     sp_curve_unref(c);
1423     
1426 /**
1427  * Return duplicate of curve (if any exists) or NULL if there is no curve
1428  */
1429 SPCurve *
1430 sp_image_get_curve (SPImage *image)
1432         if (image->curve) {
1433                 return sp_curve_copy(image->curve);
1434         }
1435         return NULL;
1438 /*
1439   Local Variables:
1440   mode:c++
1441   c-file-style:"stroustrup"
1442   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1443   indent-tabs-mode:nil
1444   fill-column:99
1445   End:
1446 */
1447 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :