Code

9f67670504a80eb268d1a5e22ea94d9db9bae50d
[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         }
688         
689         sp_image_set_curve(image); //creates a curve at the image's boundary for snapping
692 static void
693 sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
695     SPImage *image;
697     image = (SPImage *) object;
698     SPDocument *doc = SP_OBJECT_DOCUMENT(object);
700         if (((SPObjectClass *) (parent_class))->update)
701                 ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
703         if (flags & SP_IMAGE_HREF_MODIFIED_FLAG) {
704                 if (image->pixbuf) {
705                         gdk_pixbuf_unref (image->pixbuf);
706                         image->pixbuf = NULL;
707                 }
708                 if (image->href) {
709                         GdkPixbuf *pixbuf;
710                         pixbuf = sp_image_repr_read_image (
711                     object->repr->attribute("xlink:href"),
712                     object->repr->attribute("sodipodi:absref"),
713                     doc->base);
714                         if (pixbuf) {
715                                 pixbuf = sp_image_pixbuf_force_rgba (pixbuf);
716 // BLIP
717 #if ENABLE_LCMS
718                                 if ( image->color_profile )
719                                 {
720                                     int imagewidth = gdk_pixbuf_get_width( pixbuf );
721                                     int imageheight = gdk_pixbuf_get_height( pixbuf );
722                                     int rowstride = gdk_pixbuf_get_rowstride( pixbuf );
723                                     guchar* px = gdk_pixbuf_get_pixels( pixbuf );
725                                     if ( px ) {
726 #ifdef DEBUG_LCMS
727                                         DEBUG_MESSAGE( lcmsFive, "in <image>'s sp_image_update. About to call colorprofile_get_handle()" );
728 #endif // DEBUG_LCMS
729                                         guint profIntent = Inkscape::RENDERING_INTENT_UNKNOWN;
730                                         cmsHPROFILE prof = Inkscape::colorprofile_get_handle( SP_OBJECT_DOCUMENT( object ),
731                                                                                               &profIntent,
732                                                                                               image->color_profile );
733                                         if ( prof ) {
734                                             icProfileClassSignature profileClass = cmsGetDeviceClass( prof );
735                                             if ( profileClass != icSigNamedColorClass ) {
736                                                 int intent = INTENT_PERCEPTUAL;
737                                                 switch ( profIntent ) {
738                                                     case Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC:
739                                                         intent = INTENT_RELATIVE_COLORIMETRIC;
740                                                         break;
741                                                     case Inkscape::RENDERING_INTENT_SATURATION:
742                                                         intent = INTENT_SATURATION;
743                                                         break;
744                                                     case Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC:
745                                                         intent = INTENT_ABSOLUTE_COLORIMETRIC;
746                                                         break;
747                                                     case Inkscape::RENDERING_INTENT_PERCEPTUAL:
748                                                     case Inkscape::RENDERING_INTENT_UNKNOWN:
749                                                     case Inkscape::RENDERING_INTENT_AUTO:
750                                                     default:
751                                                         intent = INTENT_PERCEPTUAL;
752                                                 }
753                                                 cmsHPROFILE destProf = cmsCreate_sRGBProfile();
754                                                 cmsHTRANSFORM transf = cmsCreateTransform( prof,
755                                                                                            TYPE_RGBA_8,
756                                                                                            destProf,
757                                                                                            TYPE_RGBA_8,
758                                                                                            intent, 0 );
759                                                 if ( transf ) {
760                                                     guchar* currLine = px;
761                                                     for ( int y = 0; y < imageheight; y++ ) {
762                                                         // Since the types are the same size, we can do the transformation in-place
763                                                         cmsDoTransform( transf, currLine, currLine, imagewidth );
764                                                         currLine += rowstride;
765                                                     }
767                                                     cmsDeleteTransform( transf );
768                                                 }
769 #ifdef DEBUG_LCMS
770                                                 else
771                                                 {
772                                                     DEBUG_MESSAGE( lcmsSix, "in <image>'s sp_image_update. Unable to create LCMS transform." );
773                                                 }
774 #endif // DEBUG_LCMS
775                                                 cmsCloseProfile( destProf );
776                                             }
777 #ifdef DEBUG_LCMS
778                                             else
779                                             {
780                                                 DEBUG_MESSAGE( lcmsSeven, "in <image>'s sp_image_update. Profile type is named color. Can't transform." );
781                                             }
782 #endif // DEBUG_LCMS
783                                         }
784 #ifdef DEBUG_LCMS
785                                         else
786                                         {
787                                             DEBUG_MESSAGE( lcmsEight, "in <image>'s sp_image_update. No profile found." );
788                                         }
789 #endif // DEBUG_LCMS
790                                     }
791                                 }
792 #endif // ENABLE_LCMS
793                                 image->pixbuf = pixbuf;
794                         }
795                 }
796         }
797         // preserveAspectRatio calculate bounds / clipping rectangle -- EAF
798         if (image->pixbuf && (image->aspect_align != SP_ASPECT_NONE)) {
799                         int imagewidth, imageheight;
800                         double x,y;
802                         imagewidth = gdk_pixbuf_get_width (image->pixbuf);
803                         imageheight = gdk_pixbuf_get_height (image->pixbuf);
805                         switch (image->aspect_align) {
806                         case SP_ASPECT_XMIN_YMIN:
807                                 x = 0.0;
808                                 y = 0.0;
809                                 break;
810                         case SP_ASPECT_XMID_YMIN:
811                                 x = 0.5;
812                                 y = 0.0;
813                                 break;
814                         case SP_ASPECT_XMAX_YMIN:
815                                 x = 1.0;
816                                 y = 0.0;
817                                 break;
818                         case SP_ASPECT_XMIN_YMID:
819                                 x = 0.0;
820                                 y = 0.5;
821                                 break;
822                         case SP_ASPECT_XMID_YMID:
823                                 x = 0.5;
824                                 y = 0.5;
825                                 break;
826                         case SP_ASPECT_XMAX_YMID:
827                                 x = 1.0;
828                                 y = 0.5;
829                                 break;
830                         case SP_ASPECT_XMIN_YMAX:
831                                 x = 0.0;
832                                 y = 1.0;
833                                 break;
834                         case SP_ASPECT_XMID_YMAX:
835                                 x = 0.5;
836                                 y = 1.0;
837                                 break;
838                         case SP_ASPECT_XMAX_YMAX:
839                                 x = 1.0;
840                                 y = 1.0;
841                                 break;
842                         default:
843                                 x = 0.0;
844                                 y = 0.0;
845                                 break;
846                         }
848                         if (image->aspect_clip == SP_ASPECT_SLICE) {
849                                 image->viewx = image->x.computed;
850                                 image->viewy = image->y.computed;
851                                 image->viewwidth = image->width.computed;
852                                 image->viewheight = image->height.computed;
853                                 if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
854                                         // Pixels aspect is wider than bounding box
855                                         image->trimheight = imageheight;
856                                         image->trimwidth = static_cast<int>(static_cast<double>(imageheight) * image->width.computed / image->height.computed);
857                                         image->trimy = 0;
858                                         image->trimx = static_cast<int>(static_cast<double>(imagewidth - image->trimwidth) * x);
859                                 } else {
860                                         // Pixels aspect is taller than bounding box
861                                         image->trimwidth = imagewidth;
862                                         image->trimheight = static_cast<int>(static_cast<double>(imagewidth) * image->height.computed / image->width.computed);
863                                         image->trimx = 0;
864                                         image->trimy = static_cast<int>(static_cast<double>(imageheight - image->trimheight) * y);
865                                 }
866                         } else {
867                                 // Otherwise, assume SP_ASPECT_MEET
868                                 image->trimx = 0;
869                                 image->trimy = 0;
870                                 image->trimwidth = imagewidth;
871                                 image->trimheight = imageheight;
872                                 if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
873                                         // Pixels aspect is wider than bounding boz
874                                         image->viewwidth = image->width.computed;
875                                         image->viewheight = image->viewwidth * imageheight / imagewidth;
876                                         image->viewx=image->x.computed;
877                                         image->viewy=(image->height.computed - image->viewheight) * y + image->y.computed;
878                                 } else {
879                                         // Pixels aspect is taller than bounding box
880                                         image->viewheight = image->height.computed;
881                                         image->viewwidth = image->viewheight * imagewidth / imageheight;
882                                         image->viewy=image->y.computed;
883                                         image->viewx=(image->width.computed - image->viewwidth) * x + image->x.computed;
884                                 }
885                         }
886         }
887         sp_image_update_canvas_image ((SPImage *) object);
890 static Inkscape::XML::Node *
891 sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
893         SPImage *image;
895         image = SP_IMAGE (object);
897         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
898                 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
899                 repr = xml_doc->createElement("svg:image");
900         }
902         repr->setAttribute("xlink:href", image->href);
903         /* fixme: Reset attribute if needed (Lauris) */
904         if (image->x._set) sp_repr_set_svg_double(repr, "x", image->x.computed);
905         if (image->y._set) sp_repr_set_svg_double(repr, "y", image->y.computed);
906         if (image->width._set) sp_repr_set_svg_double(repr, "width", image->width.computed);
907         if (image->height._set) sp_repr_set_svg_double(repr, "height", image->height.computed);
908         repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
909 #if ENABLE_LCMS
910         if (image->color_profile) repr->setAttribute("color-profile", image->color_profile);
911 #endif // ENABLE_LCMS
913         if (((SPObjectClass *) (parent_class))->write)
914                 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
916         return repr;
919 static void
920 sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
922         SPImage const &image = *SP_IMAGE(item);
924         if ((image.width.computed > 0.0) && (image.height.computed > 0.0)) {
925                 double const x0 = image.x.computed;
926                 double const y0 = image.y.computed;
927                 double const x1 = x0 + image.width.computed;
928                 double const y1 = y0 + image.height.computed;
930                 nr_rect_union_pt(bbox, NR::Point(x0, y0) * transform);
931                 nr_rect_union_pt(bbox, NR::Point(x1, y0) * transform);
932                 nr_rect_union_pt(bbox, NR::Point(x1, y1) * transform);
933                 nr_rect_union_pt(bbox, NR::Point(x0, y1) * transform);
934         }
937 static void
938 sp_image_print (SPItem *item, SPPrintContext *ctx)
940         SPImage *image;
941         NRMatrix tp, ti, s, t;
942         guchar *px;
943         int w, h, rs, pixskip;
945         image = SP_IMAGE (item);
947         if (!image->pixbuf) return;
948         if ((image->width.computed <= 0.0) || (image->height.computed <= 0.0)) return;
950         px = gdk_pixbuf_get_pixels (image->pixbuf);
951         w = gdk_pixbuf_get_width (image->pixbuf);
952         h = gdk_pixbuf_get_height (image->pixbuf);
953         rs = gdk_pixbuf_get_rowstride (image->pixbuf);
954         pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
956         if (image->aspect_align == SP_ASPECT_NONE) {
957                 /* fixme: (Lauris) */
958                 nr_matrix_set_translate (&tp, image->x.computed, image->y.computed);
959                 nr_matrix_set_scale (&s, image->width.computed, -image->height.computed);
960                 nr_matrix_set_translate (&ti, 0.0, -1.0);
961         } else { // preserveAspectRatio
962                 nr_matrix_set_translate (&tp, image->viewx, image->viewy);
963                 nr_matrix_set_scale (&s, image->viewwidth, -image->viewheight);
964                 nr_matrix_set_translate (&ti, 0.0, -1.0);
965         }
967         nr_matrix_multiply (&t, &s, &tp);
968         nr_matrix_multiply (&t, &ti, &t);
970         if (image->aspect_align == SP_ASPECT_NONE)
971                 sp_print_image_R8G8B8A8_N (ctx, px, w, h, rs, &t, SP_OBJECT_STYLE (item));
972         else // preserveAspectRatio
973                 sp_print_image_R8G8B8A8_N (ctx, px + image->trimx*pixskip + image->trimy*rs, image->trimwidth, image->trimheight, rs, &t, SP_OBJECT_STYLE (item));
976 static gchar *
977 sp_image_description(SPItem *item)
979         SPImage *image = SP_IMAGE(item);
980         char *href_desc;
981         if (image->href) {
982             href_desc = (strncmp(image->href, "data:", 5) == 0)
983                 ? g_strdup(_("embedded"))
984                 : xml_quote_strdup(image->href);
985         } else {
986             g_warning("Attempting to call strncmp() with a null pointer.");
987             href_desc = g_strdup("(null_pointer)"); // we call g_free() on href_desc
988         }
990         char *ret = ( image->pixbuf == NULL
991                       ? g_strdup_printf(_("<b>Image with bad reference</b>: %s"), href_desc)
992                       : g_strdup_printf(_("<b>Image</b> %d &#215; %d: %s"),
993                                         gdk_pixbuf_get_width(image->pixbuf),
994                                         gdk_pixbuf_get_height(image->pixbuf),
995                                         href_desc) );
996         g_free(href_desc);
997         return ret;
1000 static NRArenaItem *
1001 sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
1003         int pixskip, rs;
1004         SPImage * image;
1005         NRArenaItem *ai;
1007         image = (SPImage *) item;
1009         ai = NRArenaImage::create(arena);
1011         if (image->pixbuf) {
1012                 pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1013                 rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1014                 nr_arena_image_set_style(NR_ARENA_IMAGE(ai), SP_OBJECT_STYLE(SP_OBJECT(item)));
1015                 if (image->aspect_align == SP_ASPECT_NONE)
1016                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
1017                                            gdk_pixbuf_get_pixels (image->pixbuf),
1018                                            gdk_pixbuf_get_width (image->pixbuf),
1019                                            gdk_pixbuf_get_height (image->pixbuf),
1020                                            rs);
1021                 else // preserveAspectRatio
1022                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
1023                                            gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
1024                                            image->trimwidth,
1025                                            image->trimheight,
1026                                            rs);
1027         } else {
1028                 nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai), NULL, 0, 0, 0);
1029         }
1030         if (image->aspect_align == SP_ASPECT_NONE)
1031                 nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->x.computed, image->y.computed, image->width.computed, image->height.computed);
1032         else // preserveAspectRatio
1033                 nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->viewx, image->viewy, image->viewwidth, image->viewheight);
1035         return ai;
1038 /*
1039  * utility function to try loading image from href
1040  *
1041  * docbase/relative_src
1042  * absolute_src
1043  *
1044  */
1046 GdkPixbuf *
1047 sp_image_repr_read_image (const gchar *href, const gchar *absref, const gchar *base)
1049     const gchar *filename, *docbase;
1050     gchar *fullname;
1051     GdkPixbuf *pixbuf;
1053     filename = href;
1054     if (filename != NULL) {
1055         if (strncmp (filename,"file:",5) == 0) {
1056             fullname = g_filename_from_uri(filename, NULL, NULL);
1057             if (fullname) {
1058                 // TODO check this. Was doing a UTF-8 to filename conversion here.
1059                 pixbuf = Inkscape::IO::pixbuf_new_from_file (fullname, NULL);
1060                 if (pixbuf != NULL) return pixbuf;
1061             }
1062         } else if (strncmp (filename,"data:",5) == 0) {
1063             /* data URI - embedded image */
1064             filename += 5;
1065             pixbuf = sp_image_repr_read_dataURI (filename);
1066             if (pixbuf != NULL) return pixbuf;
1067         } else {
1069             if (!g_path_is_absolute (filename)) {
1070                 /* try to load from relative pos combined with document base*/
1071                 docbase = base;
1072                 if (!docbase) docbase = ".";
1073                 fullname = g_build_filename(docbase, filename, NULL);
1075                 // document base can be wrong (on the temporary doc when importing bitmap from a
1076                 // different dir) or unset (when doc is not saved yet), so we check for base+href existence first,
1077                 // and if it fails, we also try to use bare href regardless of its g_path_is_absolute
1078                 if (g_file_test (fullname, G_FILE_TEST_EXISTS) && !g_file_test (fullname, G_FILE_TEST_IS_DIR)) {
1079                     pixbuf = Inkscape::IO::pixbuf_new_from_file( fullname, NULL );
1080                     g_free (fullname);
1081                     if (pixbuf != NULL) return pixbuf;
1082                 }
1083             }
1085             /* try filename as absolute */
1086             if (g_file_test (filename, G_FILE_TEST_EXISTS) && !g_file_test (filename, G_FILE_TEST_IS_DIR)) {
1087                 pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
1088                 if (pixbuf != NULL) return pixbuf;
1089             }
1090         }
1091     }
1093     /* at last try to load from sp absolute path name */
1094     filename = absref;
1095     if (filename != NULL) {
1096         // using absref is outside of SVG rules, so we must at least warn the user
1097         if ( base != NULL && href != NULL )
1098                 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);
1099                 else
1100                     g_warning ("xlink:href did not resolve to a valid image file, now trying sodipodi:absref=\"%s\"", absref);
1102         pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
1103         if (pixbuf != NULL) return pixbuf;
1104     }
1105     /* Nope: We do not find any valid pixmap file :-( */
1106     pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) brokenimage_xpm);
1108     /* It should be included xpm, so if it still does not does load, */
1109     /* our libraries are broken */
1110     g_assert (pixbuf != NULL);
1112     return pixbuf;
1115 static GdkPixbuf *
1116 sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf)
1118         GdkPixbuf * newbuf;
1120         if (gdk_pixbuf_get_has_alpha (pixbuf)) return pixbuf;
1122         newbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
1123         gdk_pixbuf_unref (pixbuf);
1125         return newbuf;
1128 /* We assert that realpixbuf is either NULL or identical size to pixbuf */
1130 static void
1131 sp_image_update_canvas_image (SPImage *image)
1133         int rs, pixskip;
1134         SPItem *item;
1135         SPItemView *v;
1137         item = SP_ITEM (image);
1139         if (image->pixbuf) {
1140                 /* fixme: We are slightly violating spec here (Lauris) */
1141                 if (!image->width._set) {
1142                         image->width.computed = gdk_pixbuf_get_width (image->pixbuf);
1143                 }
1144                 if (!image->height._set) {
1145                         image->height.computed = gdk_pixbuf_get_height (image->pixbuf);
1146                 }
1147         }
1149         for (v = item->display; v != NULL; v = v->next) {
1150                 pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1151                 rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1152                 nr_arena_image_set_style (NR_ARENA_IMAGE(v->arenaitem), SP_OBJECT_STYLE(SP_OBJECT(image)));
1153                 if (image->aspect_align == SP_ASPECT_NONE) {
1154                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
1155                                            gdk_pixbuf_get_pixels (image->pixbuf),
1156                                            gdk_pixbuf_get_width (image->pixbuf),
1157                                            gdk_pixbuf_get_height (image->pixbuf),
1158                                            rs);
1159                         nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
1160                                              image->x.computed, image->y.computed,
1161                                              image->width.computed, image->height.computed);
1162                 } else { // preserveAspectRatio
1163                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
1164                                            gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
1165                                            image->trimwidth,
1166                                            image->trimheight,
1167                                            rs);
1168                         nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
1169                                              image->viewx, image->viewy,
1170                                              image->viewwidth, image->viewheight);
1171                 }
1172         }
1175 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p)
1177     /* An image doesn't have any nodes to snap, but still we want to be able snap one image 
1178     to another. Therefore we will create some snappoints at the corner, similar to a rect. If
1179     the image is rotated, then the snappoints will rotate with it. Again, just like a rect.
1180     */
1181      
1182     g_assert(item != NULL);
1183     g_assert(SP_IS_IMAGE(item));
1185     if (item->clip_ref->getObject()) {
1186         //We are looking at a clipped image: do not return any snappoints, as these might be
1187         //far far away from the visible part from the clipped image
1188     } else {
1189         // The image has not been clipped: return its corners, which might be rotated for example
1190         SPImage &image = *SP_IMAGE(item);
1191         double const x0 = image.x.computed;
1192                 double const y0 = image.y.computed;
1193                 double const x1 = x0 + image.width.computed;
1194                 double const y1 = y0 + image.height.computed;
1195                 NR::Matrix const i2d (sp_item_i2d_affine (item));
1196                 *p = NR::Point(x0, y0) * i2d;
1197         *p = NR::Point(x0, y1) * i2d;
1198         *p = NR::Point(x1, y1) * i2d;
1199         *p = NR::Point(x1, y0) * i2d;
1200     }
1203 /*
1204  * Initially we'll do:
1205  * Transform x, y, set x, y, clear translation
1206  */
1208 static NR::Matrix
1209 sp_image_set_transform(SPItem *item, NR::Matrix const &xform)
1211         SPImage *image = SP_IMAGE(item);
1213         /* Calculate position in parent coords. */
1214         NR::Point pos( NR::Point(image->x.computed, image->y.computed) * xform );
1216         /* This function takes care of translation and scaling, we return whatever parts we can't
1217            handle. */
1218         NR::Matrix ret(NR::transform(xform));
1219         NR::Point const scale(hypot(ret[0], ret[1]),
1220                               hypot(ret[2], ret[3]));
1221         if ( scale[NR::X] > 1e-9 ) {
1222                 ret[0] /= scale[NR::X];
1223                 ret[1] /= scale[NR::X];
1224         } else {
1225                 ret[0] = 1.0;
1226                 ret[1] = 0.0;
1227         }
1228         if ( scale[NR::Y] > 1e-9 ) {
1229                 ret[2] /= scale[NR::Y];
1230                 ret[3] /= scale[NR::Y];
1231         } else {
1232                 ret[2] = 0.0;
1233                 ret[3] = 1.0;
1234         }
1236         image->width = image->width.computed * scale[NR::X];
1237         image->height = image->height.computed * scale[NR::Y];
1239         /* Find position in item coords */
1240         pos = pos * ret.inverse();
1241         image->x = pos[NR::X];
1242         image->y = pos[NR::Y];
1244         item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1246         return ret;
1249 static GdkPixbuf *
1250 sp_image_repr_read_dataURI (const gchar * uri_data)
1251 {       GdkPixbuf * pixbuf = NULL;
1253         gint data_is_image = 0;
1254         gint data_is_base64 = 0;
1256         const gchar * data = uri_data;
1258         while (*data) {
1259                 if (strncmp (data,"base64",6) == 0) {
1260                         /* base64-encoding */
1261                         data_is_base64 = 1;
1262                         data_is_image = 1; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what
1263                         data += 6;
1264                 }
1265                 else if (strncmp (data,"image/png",9) == 0) {
1266                         /* PNG image */
1267                         data_is_image = 1;
1268                         data += 9;
1269                 }
1270                 else if (strncmp (data,"image/jpg",9) == 0) {
1271                         /* JPEG image */
1272                         data_is_image = 1;
1273                         data += 9;
1274                 }
1275                 else if (strncmp (data,"image/jpeg",10) == 0) {
1276                         /* JPEG image */
1277                         data_is_image = 1;
1278                         data += 10;
1279                 }
1280                 else { /* unrecognized option; skip it */
1281                         while (*data) {
1282                                 if (((*data) == ';') || ((*data) == ',')) break;
1283                                 data++;
1284                         }
1285                 }
1286                 if ((*data) == ';') {
1287                         data++;
1288                         continue;
1289                 }
1290                 if ((*data) == ',') {
1291                         data++;
1292                         break;
1293                 }
1294         }
1296         if ((*data) && data_is_image && data_is_base64) {
1297                 pixbuf = sp_image_repr_read_b64 (data);
1298         }
1300         return pixbuf;
1303 static GdkPixbuf *
1304 sp_image_repr_read_b64 (const gchar * uri_data)
1305 {       GdkPixbuf * pixbuf = NULL;
1306         GdkPixbufLoader * loader = NULL;
1308         gint j;
1309         gint k;
1310         gint l;
1311         gint b;
1312         gint len;
1313         gint eos = 0;
1314         gint failed = 0;
1316         guint32 bits;
1318         static const gchar B64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1320         const gchar* btr = uri_data;
1322         gchar ud[4];
1324         guchar bd[57];
1326         loader = gdk_pixbuf_loader_new ();
1328         if (loader == NULL) return NULL;
1330         while (eos == 0) {
1331                 l = 0;
1332                 for (j = 0; j < 19; j++) {
1333                         len = 0;
1334                         for (k = 0; k < 4; k++) {
1335                                 while (isspace ((int) (*btr))) {
1336                                         if ((*btr) == '\0') break;
1337                                         btr++;
1338                                 }
1339                                 if (eos) {
1340                                         ud[k] = 0;
1341                                         continue;
1342                                 }
1343                                 if (((*btr) == '\0') || ((*btr) == '=')) {
1344                                         eos = 1;
1345                                         ud[k] = 0;
1346                                         continue;
1347                                 }
1348                                 ud[k] = 64;
1349                                 for (b = 0; b < 64; b++) { /* There must a faster way to do this... ?? */
1350                                         if (B64[b] == (*btr)) {
1351                                                 ud[k] = (gchar) b;
1352                                                 break;
1353                                         }
1354                                 }
1355                                 if (ud[k] == 64) { /* data corruption ?? */
1356                                         eos = 1;
1357                                         ud[k] = 0;
1358                                         continue;
1359                                 }
1360                                 btr++;
1361                                 len++;
1362                         }
1363                         bits = (guint32) ud[0];
1364                         bits = (bits << 6) | (guint32) ud[1];
1365                         bits = (bits << 6) | (guint32) ud[2];
1366                         bits = (bits << 6) | (guint32) ud[3];
1367                         bd[l++] = (guchar) ((bits & 0xff0000) >> 16);
1368                         if (len > 2) {
1369                                 bd[l++] = (guchar) ((bits & 0xff00) >>  8);
1370                         }
1371                         if (len > 3) {
1372                                 bd[l++] = (guchar)  (bits & 0xff);
1373                         }
1374                 }
1376                 if (!gdk_pixbuf_loader_write (loader, (const guchar *) bd, (size_t) l, NULL)) {
1377                         failed = 1;
1378                         break;
1379                 }
1380         }
1382         gdk_pixbuf_loader_close (loader, NULL);
1384         if (!failed) pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
1386         return pixbuf;
1389 static void
1390 sp_image_set_curve(SPImage *image) 
1392     //create a curve at the image's boundary for snapping
1393     if ((image->height.computed < 1e-18) || (image->width.computed < 1e-18) || (image->clip_ref->getObject())) {
1394         if (image->curve) {
1395             image->curve = sp_curve_unref(image->curve);
1396         }
1397         return;
1398     }
1399     
1400     SPCurve *c = sp_curve_new();
1402     double const x = image->x.computed;
1403     double const y = image->y.computed;
1404     double const w = image->width.computed;
1405     double const h = image->height.computed;
1406     
1407     sp_curve_moveto(c, x, y);
1408     sp_curve_lineto(c, x + w, y);
1409     sp_curve_lineto(c, x + w, y + h);
1410     sp_curve_lineto(c, x, y + h);
1411     sp_curve_lineto(c, x, y);
1413     sp_curve_closepath_current(c);
1414     
1415     if (image->curve) {
1416         image->curve = sp_curve_unref(image->curve);
1417     }
1418     
1419     if (c) {
1420         image->curve = sp_curve_ref(c);
1421     }
1422     
1423     sp_curve_unref(c);
1424     
1427 /**
1428  * Return duplicate of curve (if any exists) or NULL if there is no curve
1429  */
1430 SPCurve *
1431 sp_image_get_curve (SPImage *image)
1433         if (image->curve) {
1434                 return sp_curve_copy(image->curve);
1435         }
1436         return NULL;
1439 /*
1440   Local Variables:
1441   mode:c++
1442   c-file-style:"stroustrup"
1443   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1444   indent-tabs-mode:nil
1445   fill-column:99
1446   End:
1447 */
1448 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :