Code

warning cleanup
[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 #if defined(PNG_sRGB_SUPPORTED)
365                                 {
366                                     int intent = 0;
367                                     if ( png_get_sRGB(pngPtr, infoPtr, &intent) ) {
368 //                                         g_message("Found an sRGB png chunk");
369                                     }
370                                 }
371 #endif // defined(PNG_sRGB_SUPPORTED)
373 #if defined(PNG_cHRM_SUPPORTED)
374                                 {
375                                     double white_x = 0;
376                                     double white_y = 0;
377                                     double red_x = 0;
378                                     double red_y = 0;
379                                     double green_x = 0;
380                                     double green_y = 0;
381                                     double blue_x = 0;
382                                     double blue_y = 0;
384                                     if ( png_get_cHRM(pngPtr, infoPtr,
385                                                       &white_x, &white_y,
386                                                       &red_x, &red_y,
387                                                       &green_x, &green_y,
388                                                       &blue_x, &blue_y) ) {
389 //                                         g_message("Found a cHRM png chunk");
390                                     }
391                                 }
392 #endif // defined(PNG_cHRM_SUPPORTED)
394 #if defined(PNG_gAMA_SUPPORTED)
395                                 {
396                                     double file_gamma = 0;
397                                     if ( png_get_gAMA(pngPtr, infoPtr, &file_gamma) ) {
398 //                                         g_message("Found a gAMA png chunk");
399                                     }
400                                 }
401 #endif // defined(PNG_gAMA_SUPPORTED)
403 #if defined(PNG_iCCP_SUPPORTED)
404                                 {
405                                     char* name = 0;
406                                     int compression_type = 0;
407                                     char* profile = 0;
408                                     png_uint_32 proflen = 0;
409                                     if ( png_get_iCCP(pngPtr, infoPtr, &name, &compression_type, &profile, &proflen) ) {
410 //                                         g_message("Found an iCCP chunk named [%s] with %d bytes and comp %d", name, proflen, compression_type);
411                                     }
412                                 }
413 #endif // defined(PNG_iCCP_SUPPORTED)
416                                 // now clean it up.
417                                 png_destroy_read_struct( &pngPtr, &infoPtr, NULL );//&endPtr );
418                             }
419                             else
420                             {
421 //                                 g_message("Error when creating PNG read struct");
422                             }
423                         }
424                     }
425                     else if ( !latter )
426                     {
427                         latter = TRUE;
428                         //g_message("  READing latter");
429                     }
430                     // Now clear out the buffer so we can read more.
431                     // (dumping out unused)
432                     youme.clear();
433                 }
434             }
436             gboolean ok = gdk_pixbuf_loader_close(loader, &err);
437             if ( ok )
438             {
439                 buf = gdk_pixbuf_loader_get_pixbuf( loader );
440                 if ( buf )
441                 {
442                     g_object_ref(buf);
444                     if ( dpiX )
445                     {
446                         gchar *tmp = g_strdup_printf( "%d", dpiX );
447                         if ( tmp )
448                         {
449 //                             g_message("Need to set DpiX: %s", tmp);
450                             //gdk_pixbuf_set_option( buf, "Inkscape::DpiX", tmp );
451                             g_free( tmp );
452                         }
453                     }
454                     if ( dpiY )
455                     {
456                         gchar *tmp = g_strdup_printf( "%d", dpiY );
457                         if ( tmp )
458                         {
459 //                             g_message("Need to set DpiY: %s", tmp);
460                             //gdk_pixbuf_set_option( buf, "Inkscape::DpiY", tmp );
461                             g_free( tmp );
462                         }
463                     }
464                 }
465             }
466             else
467             {
468                 // do something
469                 g_message("error loading pixbuf at close");
470             }
472             g_object_unref(loader);
473         }
474         else
475         {
476             g_message("error when creating pixbuf loader");
477         }
478         fclose( fp );
479         fp = NULL;
480     }
481     else
482     {
483         g_warning ("Unable to open linked file: %s", filename);
484     }
486 /*
487     if ( buf )
488     {
489         const gchar* bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiX" );
490         if ( bloop )
491         {
492             g_message("DPI X is [%s]", bloop);
493         }
494         bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiY" );
495         if ( bloop )
496         {
497             g_message("DPI Y is [%s]", bloop);
498         }
499     }
500 */
502     return buf;
508 GType
509 sp_image_get_type (void)
511         static GType image_type = 0;
512         if (!image_type) {
513                 GTypeInfo image_info = {
514                         sizeof (SPImageClass),
515                         NULL,   /* base_init */
516                         NULL,   /* base_finalize */
517                         (GClassInitFunc) sp_image_class_init,
518                         NULL,   /* class_finalize */
519                         NULL,   /* class_data */
520                         sizeof (SPImage),
521                         16,     /* n_preallocs */
522                         (GInstanceInitFunc) sp_image_init,
523                         NULL,   /* value_table */
524                 };
525                 image_type = g_type_register_static (sp_item_get_type (), "SPImage", &image_info, (GTypeFlags)0);
526         }
527         return image_type;
530 static void
531 sp_image_class_init (SPImageClass * klass)
533         GObjectClass * gobject_class;
534         SPObjectClass * sp_object_class;
535         SPItemClass * item_class;
537         gobject_class = (GObjectClass *) klass;
538         sp_object_class = (SPObjectClass *) klass;
539         item_class = (SPItemClass *) klass;
541         parent_class = (SPItemClass*)g_type_class_ref (sp_item_get_type ());
543         sp_object_class->build = sp_image_build;
544         sp_object_class->release = sp_image_release;
545         sp_object_class->set = sp_image_set;
546         sp_object_class->update = sp_image_update;
547         sp_object_class->write = sp_image_write;
549         item_class->bbox = sp_image_bbox;
550         item_class->print = sp_image_print;
551         item_class->description = sp_image_description;
552         item_class->show = sp_image_show;
553         item_class->snappoints = sp_image_snappoints;
554         item_class->set_transform = sp_image_set_transform;
557 static void
558 sp_image_init (SPImage *image)
560         image->x.unset();
561         image->y.unset();
562         image->width.unset();
563         image->height.unset();
564         image->aspect_align = SP_ASPECT_NONE;
565         image->curve = NULL;
568 static void
569 sp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
571         if (((SPObjectClass *) parent_class)->build)
572                 ((SPObjectClass *) parent_class)->build (object, document, repr);
574         sp_object_read_attr (object, "xlink:href");
575         sp_object_read_attr (object, "x");
576         sp_object_read_attr (object, "y");
577         sp_object_read_attr (object, "width");
578         sp_object_read_attr (object, "height");
579         sp_object_read_attr (object, "preserveAspectRatio");
580         sp_object_read_attr (object, "color-profile");
582         /* Register */
583         sp_document_add_resource (document, "image", object);
586 static void
587 sp_image_release (SPObject *object)
589         SPImage *image;
591         image = SP_IMAGE (object);
593         if (SP_OBJECT_DOCUMENT (object)) {
594                 /* Unregister ourselves */
595                 sp_document_remove_resource (SP_OBJECT_DOCUMENT (object), "image", SP_OBJECT (object));
596         }
598         if (image->href) {
599                 g_free (image->href);
600                 image->href = NULL;
601         }
603         if (image->pixbuf) {
604                 gdk_pixbuf_unref (image->pixbuf);
605                 image->pixbuf = NULL;
606         }
608 #if ENABLE_LCMS
609         if (image->color_profile) {
610                 g_free (image->color_profile);
611                 image->color_profile = NULL;
612         }
613 #endif // ENABLE_LCMS
615     if (image->curve) {
616                 image->curve = sp_curve_unref (image->curve);
617         }
619         if (((SPObjectClass *) parent_class)->release)
620                 ((SPObjectClass *) parent_class)->release (object);
623 static void
624 sp_image_set (SPObject *object, unsigned int key, const gchar *value)
626         SPImage *image;
628         image = SP_IMAGE (object);
630         switch (key) {
631         case SP_ATTR_XLINK_HREF:
632                 g_free (image->href);
633                 image->href = (value) ? g_strdup (value) : NULL;
634                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
635                 break;
636         case SP_ATTR_X:
637                 if (!image->x.readAbsolute(value)) {
638                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
639                         image->x.unset();
640                 }
641                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
642                 break;
643         case SP_ATTR_Y:
644                 if (!image->y.readAbsolute(value)) {
645                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
646                         image->y.unset();
647                 }
648                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
649                 break;
650         case SP_ATTR_WIDTH:
651                 if (!image->width.readAbsolute(value)) {
652                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
653                         image->width.unset();
654                 }
655                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
656                 break;
657         case SP_ATTR_HEIGHT:
658                 if (!image->height.readAbsolute(value)) {
659                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
660                         image->height.unset();
661                 }
662                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
663                 break;
664         case SP_ATTR_PRESERVEASPECTRATIO:
665                 /* Do setup before, so we can use break to escape */
666                 image->aspect_align = SP_ASPECT_NONE;
667                 image->aspect_clip = SP_ASPECT_MEET;
668                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
669                 if (value) {
670                         int len;
671                         gchar c[256];
672                         const gchar *p, *e;
673                         unsigned int align, clip;
674                         p = value;
675                         while (*p && *p == 32) p += 1;
676                         if (!*p) break;
677                         e = p;
678                         while (*e && *e != 32) e += 1;
679                         len = e - p;
680                         if (len > 8) break;
681                         memcpy (c, value, len);
682                         c[len] = 0;
683                         /* Now the actual part */
684                         if (!strcmp (c, "none")) {
685                                 align = SP_ASPECT_NONE;
686                         } else if (!strcmp (c, "xMinYMin")) {
687                                 align = SP_ASPECT_XMIN_YMIN;
688                         } else if (!strcmp (c, "xMidYMin")) {
689                                 align = SP_ASPECT_XMID_YMIN;
690                         } else if (!strcmp (c, "xMaxYMin")) {
691                                 align = SP_ASPECT_XMAX_YMIN;
692                         } else if (!strcmp (c, "xMinYMid")) {
693                                 align = SP_ASPECT_XMIN_YMID;
694                         } else if (!strcmp (c, "xMidYMid")) {
695                                 align = SP_ASPECT_XMID_YMID;
696                         } else if (!strcmp (c, "xMaxYMid")) {
697                                 align = SP_ASPECT_XMAX_YMID;
698                         } else if (!strcmp (c, "xMinYMax")) {
699                                 align = SP_ASPECT_XMIN_YMAX;
700                         } else if (!strcmp (c, "xMidYMax")) {
701                                 align = SP_ASPECT_XMID_YMAX;
702                         } else if (!strcmp (c, "xMaxYMax")) {
703                                 align = SP_ASPECT_XMAX_YMAX;
704                         } else {
705                                 break;
706                         }
707                         clip = SP_ASPECT_MEET;
708                         while (*e && *e == 32) e += 1;
709                         if (e) {
710                                 if (!strcmp (e, "meet")) {
711                                         clip = SP_ASPECT_MEET;
712                                 } else if (!strcmp (e, "slice")) {
713                                         clip = SP_ASPECT_SLICE;
714                                 } else {
715                                         break;
716                                 }
717                         }
718                         image->aspect_align = align;
719                         image->aspect_clip = clip;
720                 }
721                 break;
722 #if ENABLE_LCMS
723         case SP_PROP_COLOR_PROFILE:
724                 if ( image->color_profile ) {
725                     g_free (image->color_profile);
726                 }
727                 image->color_profile = (value) ? g_strdup (value) : NULL;
728 #ifdef DEBUG_LCMS
729                 if ( value ) {
730                     DEBUG_MESSAGE( lcmsFour, "<image> color-profile set to '%s'", value );
731                 } else {
732                     DEBUG_MESSAGE( lcmsFour, "<image> color-profile cleared" );
733                 }
734 #endif // DEBUG_LCMS
735                 // TODO check on this HREF_MODIFIED flag
736                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
737                 break;
738 #endif // ENABLE_LCMS
739         default:
740                 if (((SPObjectClass *) (parent_class))->set)
741                         ((SPObjectClass *) (parent_class))->set (object, key, value);
742                 break;
743         }
744         
745         sp_image_set_curve(image); //creates a curve at the image's boundary for snapping
748 static void
749 sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
751     SPImage *image;
753     image = (SPImage *) object;
754     SPDocument *doc = SP_OBJECT_DOCUMENT(object);
756         if (((SPObjectClass *) (parent_class))->update)
757                 ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
759         if (flags & SP_IMAGE_HREF_MODIFIED_FLAG) {
760                 if (image->pixbuf) {
761                         gdk_pixbuf_unref (image->pixbuf);
762                         image->pixbuf = NULL;
763                 }
764                 if (image->href) {
765                         GdkPixbuf *pixbuf;
766                         pixbuf = sp_image_repr_read_image (
767                     object->repr->attribute("xlink:href"),
768                     object->repr->attribute("sodipodi:absref"),
769                     doc->base);
770                         if (pixbuf) {
771                                 pixbuf = sp_image_pixbuf_force_rgba (pixbuf);
772 // BLIP
773 #if ENABLE_LCMS
774                                 if ( image->color_profile )
775                                 {
776                                     int imagewidth = gdk_pixbuf_get_width( pixbuf );
777                                     int imageheight = gdk_pixbuf_get_height( pixbuf );
778                                     int rowstride = gdk_pixbuf_get_rowstride( pixbuf );
779                                     guchar* px = gdk_pixbuf_get_pixels( pixbuf );
781                                     if ( px ) {
782 #ifdef DEBUG_LCMS
783                                         DEBUG_MESSAGE( lcmsFive, "in <image>'s sp_image_update. About to call colorprofile_get_handle()" );
784 #endif // DEBUG_LCMS
785                                         guint profIntent = Inkscape::RENDERING_INTENT_UNKNOWN;
786                                         cmsHPROFILE prof = Inkscape::colorprofile_get_handle( SP_OBJECT_DOCUMENT( object ),
787                                                                                               &profIntent,
788                                                                                               image->color_profile );
789                                         if ( prof ) {
790                                             icProfileClassSignature profileClass = cmsGetDeviceClass( prof );
791                                             if ( profileClass != icSigNamedColorClass ) {
792                                                 int intent = INTENT_PERCEPTUAL;
793                                                 switch ( profIntent ) {
794                                                     case Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC:
795                                                         intent = INTENT_RELATIVE_COLORIMETRIC;
796                                                         break;
797                                                     case Inkscape::RENDERING_INTENT_SATURATION:
798                                                         intent = INTENT_SATURATION;
799                                                         break;
800                                                     case Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC:
801                                                         intent = INTENT_ABSOLUTE_COLORIMETRIC;
802                                                         break;
803                                                     case Inkscape::RENDERING_INTENT_PERCEPTUAL:
804                                                     case Inkscape::RENDERING_INTENT_UNKNOWN:
805                                                     case Inkscape::RENDERING_INTENT_AUTO:
806                                                     default:
807                                                         intent = INTENT_PERCEPTUAL;
808                                                 }
809                                                 cmsHPROFILE destProf = cmsCreate_sRGBProfile();
810                                                 cmsHTRANSFORM transf = cmsCreateTransform( prof,
811                                                                                            TYPE_RGBA_8,
812                                                                                            destProf,
813                                                                                            TYPE_RGBA_8,
814                                                                                            intent, 0 );
815                                                 if ( transf ) {
816                                                     guchar* currLine = px;
817                                                     for ( int y = 0; y < imageheight; y++ ) {
818                                                         // Since the types are the same size, we can do the transformation in-place
819                                                         cmsDoTransform( transf, currLine, currLine, imagewidth );
820                                                         currLine += rowstride;
821                                                     }
823                                                     cmsDeleteTransform( transf );
824                                                 }
825 #ifdef DEBUG_LCMS
826                                                 else
827                                                 {
828                                                     DEBUG_MESSAGE( lcmsSix, "in <image>'s sp_image_update. Unable to create LCMS transform." );
829                                                 }
830 #endif // DEBUG_LCMS
831                                                 cmsCloseProfile( destProf );
832                                             }
833 #ifdef DEBUG_LCMS
834                                             else
835                                             {
836                                                 DEBUG_MESSAGE( lcmsSeven, "in <image>'s sp_image_update. Profile type is named color. Can't transform." );
837                                             }
838 #endif // DEBUG_LCMS
839                                         }
840 #ifdef DEBUG_LCMS
841                                         else
842                                         {
843                                             DEBUG_MESSAGE( lcmsEight, "in <image>'s sp_image_update. No profile found." );
844                                         }
845 #endif // DEBUG_LCMS
846                                     }
847                                 }
848 #endif // ENABLE_LCMS
849                                 image->pixbuf = pixbuf;
850                         }
851                 }
852         }
853         // preserveAspectRatio calculate bounds / clipping rectangle -- EAF
854         if (image->pixbuf && (image->aspect_align != SP_ASPECT_NONE)) {
855                         int imagewidth, imageheight;
856                         double x,y;
858                         imagewidth = gdk_pixbuf_get_width (image->pixbuf);
859                         imageheight = gdk_pixbuf_get_height (image->pixbuf);
861                         switch (image->aspect_align) {
862                         case SP_ASPECT_XMIN_YMIN:
863                                 x = 0.0;
864                                 y = 0.0;
865                                 break;
866                         case SP_ASPECT_XMID_YMIN:
867                                 x = 0.5;
868                                 y = 0.0;
869                                 break;
870                         case SP_ASPECT_XMAX_YMIN:
871                                 x = 1.0;
872                                 y = 0.0;
873                                 break;
874                         case SP_ASPECT_XMIN_YMID:
875                                 x = 0.0;
876                                 y = 0.5;
877                                 break;
878                         case SP_ASPECT_XMID_YMID:
879                                 x = 0.5;
880                                 y = 0.5;
881                                 break;
882                         case SP_ASPECT_XMAX_YMID:
883                                 x = 1.0;
884                                 y = 0.5;
885                                 break;
886                         case SP_ASPECT_XMIN_YMAX:
887                                 x = 0.0;
888                                 y = 1.0;
889                                 break;
890                         case SP_ASPECT_XMID_YMAX:
891                                 x = 0.5;
892                                 y = 1.0;
893                                 break;
894                         case SP_ASPECT_XMAX_YMAX:
895                                 x = 1.0;
896                                 y = 1.0;
897                                 break;
898                         default:
899                                 x = 0.0;
900                                 y = 0.0;
901                                 break;
902                         }
904                         if (image->aspect_clip == SP_ASPECT_SLICE) {
905                                 image->viewx = image->x.computed;
906                                 image->viewy = image->y.computed;
907                                 image->viewwidth = image->width.computed;
908                                 image->viewheight = image->height.computed;
909                                 if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
910                                         // Pixels aspect is wider than bounding box
911                                         image->trimheight = imageheight;
912                                         image->trimwidth = static_cast<int>(static_cast<double>(imageheight) * image->width.computed / image->height.computed);
913                                         image->trimy = 0;
914                                         image->trimx = static_cast<int>(static_cast<double>(imagewidth - image->trimwidth) * x);
915                                 } else {
916                                         // Pixels aspect is taller than bounding box
917                                         image->trimwidth = imagewidth;
918                                         image->trimheight = static_cast<int>(static_cast<double>(imagewidth) * image->height.computed / image->width.computed);
919                                         image->trimx = 0;
920                                         image->trimy = static_cast<int>(static_cast<double>(imageheight - image->trimheight) * y);
921                                 }
922                         } else {
923                                 // Otherwise, assume SP_ASPECT_MEET
924                                 image->trimx = 0;
925                                 image->trimy = 0;
926                                 image->trimwidth = imagewidth;
927                                 image->trimheight = imageheight;
928                                 if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
929                                         // Pixels aspect is wider than bounding boz
930                                         image->viewwidth = image->width.computed;
931                                         image->viewheight = image->viewwidth * imageheight / imagewidth;
932                                         image->viewx=image->x.computed;
933                                         image->viewy=(image->height.computed - image->viewheight) * y + image->y.computed;
934                                 } else {
935                                         // Pixels aspect is taller than bounding box
936                                         image->viewheight = image->height.computed;
937                                         image->viewwidth = image->viewheight * imagewidth / imageheight;
938                                         image->viewy=image->y.computed;
939                                         image->viewx=(image->width.computed - image->viewwidth) * x + image->x.computed;
940                                 }
941                         }
942         }
943         sp_image_update_canvas_image ((SPImage *) object);
946 static Inkscape::XML::Node *
947 sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
949         SPImage *image;
951         image = SP_IMAGE (object);
953         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
954                 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
955                 repr = xml_doc->createElement("svg:image");
956         }
958         repr->setAttribute("xlink:href", image->href);
959         /* fixme: Reset attribute if needed (Lauris) */
960         if (image->x._set) sp_repr_set_svg_double(repr, "x", image->x.computed);
961         if (image->y._set) sp_repr_set_svg_double(repr, "y", image->y.computed);
962         if (image->width._set) sp_repr_set_svg_double(repr, "width", image->width.computed);
963         if (image->height._set) sp_repr_set_svg_double(repr, "height", image->height.computed);
964         repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
965 #if ENABLE_LCMS
966         if (image->color_profile) repr->setAttribute("color-profile", image->color_profile);
967 #endif // ENABLE_LCMS
969         if (((SPObjectClass *) (parent_class))->write)
970                 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
972         return repr;
975 static void
976 sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const /*flags*/)
978         SPImage const &image = *SP_IMAGE(item);
980         if ((image.width.computed > 0.0) && (image.height.computed > 0.0)) {
981                 double const x0 = image.x.computed;
982                 double const y0 = image.y.computed;
983                 double const x1 = x0 + image.width.computed;
984                 double const y1 = y0 + image.height.computed;
986                 nr_rect_union_pt(bbox, NR::Point(x0, y0) * transform);
987                 nr_rect_union_pt(bbox, NR::Point(x1, y0) * transform);
988                 nr_rect_union_pt(bbox, NR::Point(x1, y1) * transform);
989                 nr_rect_union_pt(bbox, NR::Point(x0, y1) * transform);
990         }
993 static void
994 sp_image_print (SPItem *item, SPPrintContext *ctx)
996         SPImage *image;
997         NRMatrix tp, ti, s, t;
998         guchar *px;
999         int w, h, rs, pixskip;
1001         image = SP_IMAGE (item);
1003         if (!image->pixbuf) return;
1004         if ((image->width.computed <= 0.0) || (image->height.computed <= 0.0)) return;
1006         px = gdk_pixbuf_get_pixels (image->pixbuf);
1007         w = gdk_pixbuf_get_width (image->pixbuf);
1008         h = gdk_pixbuf_get_height (image->pixbuf);
1009         rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1010         pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1012         if (image->aspect_align == SP_ASPECT_NONE) {
1013                 /* fixme: (Lauris) */
1014                 nr_matrix_set_translate (&tp, image->x.computed, image->y.computed);
1015                 nr_matrix_set_scale (&s, image->width.computed, -image->height.computed);
1016                 nr_matrix_set_translate (&ti, 0.0, -1.0);
1017         } else { // preserveAspectRatio
1018                 nr_matrix_set_translate (&tp, image->viewx, image->viewy);
1019                 nr_matrix_set_scale (&s, image->viewwidth, -image->viewheight);
1020                 nr_matrix_set_translate (&ti, 0.0, -1.0);
1021         }
1023         nr_matrix_multiply (&t, &s, &tp);
1024         nr_matrix_multiply (&t, &ti, &t);
1026         if (image->aspect_align == SP_ASPECT_NONE)
1027                 sp_print_image_R8G8B8A8_N (ctx, px, w, h, rs, &t, SP_OBJECT_STYLE (item));
1028         else // preserveAspectRatio
1029                 sp_print_image_R8G8B8A8_N (ctx, px + image->trimx*pixskip + image->trimy*rs, image->trimwidth, image->trimheight, rs, &t, SP_OBJECT_STYLE (item));
1032 static gchar *
1033 sp_image_description(SPItem *item)
1035         SPImage *image = SP_IMAGE(item);
1036         char *href_desc;
1037         if (image->href) {
1038             href_desc = (strncmp(image->href, "data:", 5) == 0)
1039                 ? g_strdup(_("embedded"))
1040                 : xml_quote_strdup(image->href);
1041         } else {
1042             g_warning("Attempting to call strncmp() with a null pointer.");
1043             href_desc = g_strdup("(null_pointer)"); // we call g_free() on href_desc
1044         }
1046         char *ret = ( image->pixbuf == NULL
1047                       ? g_strdup_printf(_("<b>Image with bad reference</b>: %s"), href_desc)
1048                       : g_strdup_printf(_("<b>Image</b> %d &#215; %d: %s"),
1049                                         gdk_pixbuf_get_width(image->pixbuf),
1050                                         gdk_pixbuf_get_height(image->pixbuf),
1051                                         href_desc) );
1052         g_free(href_desc);
1053         return ret;
1056 static NRArenaItem *
1057 sp_image_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
1059         int pixskip, rs;
1060         SPImage * image;
1061         NRArenaItem *ai;
1063         image = (SPImage *) item;
1065         ai = NRArenaImage::create(arena);
1067         if (image->pixbuf) {
1068                 pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1069                 rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1070                 nr_arena_image_set_style(NR_ARENA_IMAGE(ai), SP_OBJECT_STYLE(SP_OBJECT(item)));
1071                 if (image->aspect_align == SP_ASPECT_NONE)
1072                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
1073                                            gdk_pixbuf_get_pixels (image->pixbuf),
1074                                            gdk_pixbuf_get_width (image->pixbuf),
1075                                            gdk_pixbuf_get_height (image->pixbuf),
1076                                            rs);
1077                 else // preserveAspectRatio
1078                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
1079                                            gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
1080                                            image->trimwidth,
1081                                            image->trimheight,
1082                                            rs);
1083         } else {
1084                 nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai), NULL, 0, 0, 0);
1085         }
1086         if (image->aspect_align == SP_ASPECT_NONE)
1087                 nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->x.computed, image->y.computed, image->width.computed, image->height.computed);
1088         else // preserveAspectRatio
1089                 nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->viewx, image->viewy, image->viewwidth, image->viewheight);
1091         return ai;
1094 /*
1095  * utility function to try loading image from href
1096  *
1097  * docbase/relative_src
1098  * absolute_src
1099  *
1100  */
1102 GdkPixbuf *
1103 sp_image_repr_read_image (const gchar *href, const gchar *absref, const gchar *base)
1105     const gchar *filename, *docbase;
1106     gchar *fullname;
1107     GdkPixbuf *pixbuf;
1109     filename = href;
1110     if (filename != NULL) {
1111         if (strncmp (filename,"file:",5) == 0) {
1112             fullname = g_filename_from_uri(filename, NULL, NULL);
1113             if (fullname) {
1114                 // TODO check this. Was doing a UTF-8 to filename conversion here.
1115                 pixbuf = Inkscape::IO::pixbuf_new_from_file (fullname, NULL);
1116                 if (pixbuf != NULL) return pixbuf;
1117             }
1118         } else if (strncmp (filename,"data:",5) == 0) {
1119             /* data URI - embedded image */
1120             filename += 5;
1121             pixbuf = sp_image_repr_read_dataURI (filename);
1122             if (pixbuf != NULL) return pixbuf;
1123         } else {
1125             if (!g_path_is_absolute (filename)) {
1126                 /* try to load from relative pos combined with document base*/
1127                 docbase = base;
1128                 if (!docbase) docbase = ".";
1129                 fullname = g_build_filename(docbase, filename, NULL);
1131                 // document base can be wrong (on the temporary doc when importing bitmap from a
1132                 // different dir) or unset (when doc is not saved yet), so we check for base+href existence first,
1133                 // and if it fails, we also try to use bare href regardless of its g_path_is_absolute
1134                 if (g_file_test (fullname, G_FILE_TEST_EXISTS) && !g_file_test (fullname, G_FILE_TEST_IS_DIR)) {
1135                     pixbuf = Inkscape::IO::pixbuf_new_from_file( fullname, NULL );
1136                     g_free (fullname);
1137                     if (pixbuf != NULL) return pixbuf;
1138                 }
1139             }
1141             /* try filename as absolute */
1142             if (g_file_test (filename, G_FILE_TEST_EXISTS) && !g_file_test (filename, G_FILE_TEST_IS_DIR)) {
1143                 pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
1144                 if (pixbuf != NULL) return pixbuf;
1145             }
1146         }
1147     }
1149     /* at last try to load from sp absolute path name */
1150     filename = absref;
1151     if (filename != NULL) {
1152         // using absref is outside of SVG rules, so we must at least warn the user
1153         if ( base != NULL && href != NULL )
1154                 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);
1155                 else
1156                     g_warning ("xlink:href did not resolve to a valid image file, now trying sodipodi:absref=\"%s\"", absref);
1158         pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
1159         if (pixbuf != NULL) return pixbuf;
1160     }
1161     /* Nope: We do not find any valid pixmap file :-( */
1162     pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) brokenimage_xpm);
1164     /* It should be included xpm, so if it still does not does load, */
1165     /* our libraries are broken */
1166     g_assert (pixbuf != NULL);
1168     return pixbuf;
1171 static GdkPixbuf *
1172 sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf)
1174         GdkPixbuf * newbuf;
1176         if (gdk_pixbuf_get_has_alpha (pixbuf)) return pixbuf;
1178         newbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
1179         gdk_pixbuf_unref (pixbuf);
1181         return newbuf;
1184 /* We assert that realpixbuf is either NULL or identical size to pixbuf */
1186 static void
1187 sp_image_update_canvas_image (SPImage *image)
1189         int rs, pixskip;
1190         SPItem *item;
1191         SPItemView *v;
1193         item = SP_ITEM (image);
1195         if (image->pixbuf) {
1196                 /* fixme: We are slightly violating spec here (Lauris) */
1197                 if (!image->width._set) {
1198                         image->width.computed = gdk_pixbuf_get_width (image->pixbuf);
1199                 }
1200                 if (!image->height._set) {
1201                         image->height.computed = gdk_pixbuf_get_height (image->pixbuf);
1202                 }
1203         }
1205         for (v = item->display; v != NULL; v = v->next) {
1206                 pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1207                 rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1208                 nr_arena_image_set_style (NR_ARENA_IMAGE(v->arenaitem), SP_OBJECT_STYLE(SP_OBJECT(image)));
1209                 if (image->aspect_align == SP_ASPECT_NONE) {
1210                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
1211                                            gdk_pixbuf_get_pixels (image->pixbuf),
1212                                            gdk_pixbuf_get_width (image->pixbuf),
1213                                            gdk_pixbuf_get_height (image->pixbuf),
1214                                            rs);
1215                         nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
1216                                              image->x.computed, image->y.computed,
1217                                              image->width.computed, image->height.computed);
1218                 } else { // preserveAspectRatio
1219                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
1220                                            gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
1221                                            image->trimwidth,
1222                                            image->trimheight,
1223                                            rs);
1224                         nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
1225                                              image->viewx, image->viewy,
1226                                              image->viewwidth, image->viewheight);
1227                 }
1228         }
1231 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p)
1233     /* An image doesn't have any nodes to snap, but still we want to be able snap one image 
1234     to another. Therefore we will create some snappoints at the corner, similar to a rect. If
1235     the image is rotated, then the snappoints will rotate with it. Again, just like a rect.
1236     */
1237      
1238     g_assert(item != NULL);
1239     g_assert(SP_IS_IMAGE(item));
1241     if (item->clip_ref->getObject()) {
1242         //We are looking at a clipped image: do not return any snappoints, as these might be
1243         //far far away from the visible part from the clipped image
1244     } else {
1245         // The image has not been clipped: return its corners, which might be rotated for example
1246         SPImage &image = *SP_IMAGE(item);
1247         double const x0 = image.x.computed;
1248                 double const y0 = image.y.computed;
1249                 double const x1 = x0 + image.width.computed;
1250                 double const y1 = y0 + image.height.computed;
1251                 NR::Matrix const i2d (sp_item_i2d_affine (item));
1252                 *p = NR::Point(x0, y0) * i2d;
1253         *p = NR::Point(x0, y1) * i2d;
1254         *p = NR::Point(x1, y1) * i2d;
1255         *p = NR::Point(x1, y0) * i2d;
1256     }
1259 /*
1260  * Initially we'll do:
1261  * Transform x, y, set x, y, clear translation
1262  */
1264 static NR::Matrix
1265 sp_image_set_transform(SPItem *item, NR::Matrix const &xform)
1267         SPImage *image = SP_IMAGE(item);
1269         /* Calculate position in parent coords. */
1270         NR::Point pos( NR::Point(image->x.computed, image->y.computed) * xform );
1272         /* This function takes care of translation and scaling, we return whatever parts we can't
1273            handle. */
1274         NR::Matrix ret(NR::transform(xform));
1275         NR::Point const scale(hypot(ret[0], ret[1]),
1276                               hypot(ret[2], ret[3]));
1277         if ( scale[NR::X] > 1e-9 ) {
1278                 ret[0] /= scale[NR::X];
1279                 ret[1] /= scale[NR::X];
1280         } else {
1281                 ret[0] = 1.0;
1282                 ret[1] = 0.0;
1283         }
1284         if ( scale[NR::Y] > 1e-9 ) {
1285                 ret[2] /= scale[NR::Y];
1286                 ret[3] /= scale[NR::Y];
1287         } else {
1288                 ret[2] = 0.0;
1289                 ret[3] = 1.0;
1290         }
1292         image->width = image->width.computed * scale[NR::X];
1293         image->height = image->height.computed * scale[NR::Y];
1295         /* Find position in item coords */
1296         pos = pos * ret.inverse();
1297         image->x = pos[NR::X];
1298         image->y = pos[NR::Y];
1300         item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1302         return ret;
1305 static GdkPixbuf *
1306 sp_image_repr_read_dataURI (const gchar * uri_data)
1307 {       GdkPixbuf * pixbuf = NULL;
1309         gint data_is_image = 0;
1310         gint data_is_base64 = 0;
1312         const gchar * data = uri_data;
1314         while (*data) {
1315                 if (strncmp (data,"base64",6) == 0) {
1316                         /* base64-encoding */
1317                         data_is_base64 = 1;
1318                         data_is_image = 1; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what
1319                         data += 6;
1320                 }
1321                 else if (strncmp (data,"image/png",9) == 0) {
1322                         /* PNG image */
1323                         data_is_image = 1;
1324                         data += 9;
1325                 }
1326                 else if (strncmp (data,"image/jpg",9) == 0) {
1327                         /* JPEG image */
1328                         data_is_image = 1;
1329                         data += 9;
1330                 }
1331                 else if (strncmp (data,"image/jpeg",10) == 0) {
1332                         /* JPEG image */
1333                         data_is_image = 1;
1334                         data += 10;
1335                 }
1336                 else { /* unrecognized option; skip it */
1337                         while (*data) {
1338                                 if (((*data) == ';') || ((*data) == ',')) break;
1339                                 data++;
1340                         }
1341                 }
1342                 if ((*data) == ';') {
1343                         data++;
1344                         continue;
1345                 }
1346                 if ((*data) == ',') {
1347                         data++;
1348                         break;
1349                 }
1350         }
1352         if ((*data) && data_is_image && data_is_base64) {
1353                 pixbuf = sp_image_repr_read_b64 (data);
1354         }
1356         return pixbuf;
1359 static GdkPixbuf *
1360 sp_image_repr_read_b64 (const gchar * uri_data)
1361 {       GdkPixbuf * pixbuf = NULL;
1362         GdkPixbufLoader * loader = NULL;
1364         gint j;
1365         gint k;
1366         gint l;
1367         gint b;
1368         gint len;
1369         gint eos = 0;
1370         gint failed = 0;
1372         guint32 bits;
1374         static const gchar B64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1376         const gchar* btr = uri_data;
1378         gchar ud[4];
1380         guchar bd[57];
1382         loader = gdk_pixbuf_loader_new ();
1384         if (loader == NULL) return NULL;
1386         while (eos == 0) {
1387                 l = 0;
1388                 for (j = 0; j < 19; j++) {
1389                         len = 0;
1390                         for (k = 0; k < 4; k++) {
1391                                 while (isspace ((int) (*btr))) {
1392                                         if ((*btr) == '\0') break;
1393                                         btr++;
1394                                 }
1395                                 if (eos) {
1396                                         ud[k] = 0;
1397                                         continue;
1398                                 }
1399                                 if (((*btr) == '\0') || ((*btr) == '=')) {
1400                                         eos = 1;
1401                                         ud[k] = 0;
1402                                         continue;
1403                                 }
1404                                 ud[k] = 64;
1405                                 for (b = 0; b < 64; b++) { /* There must a faster way to do this... ?? */
1406                                         if (B64[b] == (*btr)) {
1407                                                 ud[k] = (gchar) b;
1408                                                 break;
1409                                         }
1410                                 }
1411                                 if (ud[k] == 64) { /* data corruption ?? */
1412                                         eos = 1;
1413                                         ud[k] = 0;
1414                                         continue;
1415                                 }
1416                                 btr++;
1417                                 len++;
1418                         }
1419                         bits = (guint32) ud[0];
1420                         bits = (bits << 6) | (guint32) ud[1];
1421                         bits = (bits << 6) | (guint32) ud[2];
1422                         bits = (bits << 6) | (guint32) ud[3];
1423                         bd[l++] = (guchar) ((bits & 0xff0000) >> 16);
1424                         if (len > 2) {
1425                                 bd[l++] = (guchar) ((bits & 0xff00) >>  8);
1426                         }
1427                         if (len > 3) {
1428                                 bd[l++] = (guchar)  (bits & 0xff);
1429                         }
1430                 }
1432                 if (!gdk_pixbuf_loader_write (loader, (const guchar *) bd, (size_t) l, NULL)) {
1433                         failed = 1;
1434                         break;
1435                 }
1436         }
1438         gdk_pixbuf_loader_close (loader, NULL);
1440         if (!failed) pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
1442         return pixbuf;
1445 static void
1446 sp_image_set_curve(SPImage *image) 
1448     //create a curve at the image's boundary for snapping
1449     if ((image->height.computed < 1e-18) || (image->width.computed < 1e-18) || (image->clip_ref->getObject())) {
1450         if (image->curve) {
1451             image->curve = sp_curve_unref(image->curve);
1452         }
1453         return;
1454     }
1455     
1456     NRRect rect;
1457         sp_image_bbox(image, &rect, NR::identity(), 0);
1458         NR::Maybe<NR::Rect> rect2 = rect.upgrade();
1459         SPCurve *c = sp_curve_new_from_rect(rect2);
1460         
1461     if (image->curve) {
1462         image->curve = sp_curve_unref(image->curve);
1463     }
1464     
1465     if (c) {
1466         image->curve = sp_curve_ref(c);
1467     }
1468     
1469     sp_curve_unref(c);    
1472 /**
1473  * Return duplicate of curve (if any exists) or NULL if there is no curve
1474  */
1475 SPCurve *
1476 sp_image_get_curve (SPImage *image)
1478         if (image->curve) {
1479                 return sp_curve_copy(image->curve);
1480         }
1481         return NULL;
1484 /*
1485   Local Variables:
1486   mode:c++
1487   c-file-style:"stroustrup"
1488   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1489   indent-tabs-mode:nil
1490   fill-column:99
1491   End:
1492 */
1493 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :