Code

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