Code

eliminate the use of sodipodi:docbase (use doc->base instead); emit warning when...
[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"
25 //Added for preserveAspectRatio support -- EAF
26 #include "enums.h"
27 #include "attributes.h"
29 #include "print.h"
30 #include "brokenimage.xpm"
31 #include "document.h"
32 #include "sp-image.h"
33 #include <glibmm/i18n.h>
34 #include "xml/quote.h"
35 #include <xml/repr.h>
37 #include "io/sys.h"
38 #include <png.h>
39 #if ENABLE_LCMS
40 #include "color-profile-fns.h"
41 #include "color-profile.h"
42 //#define DEBUG_LCMS
43 #ifdef DEBUG_LCMS
44 #include "prefs-utils.h"
45 #include <gtk/gtkmessagedialog.h>
46 #endif // DEBUG_LCMS
47 #endif // ENABLE_LCMS
48 /*
49  * SPImage
50  */
53 static void sp_image_class_init (SPImageClass * klass);
54 static void sp_image_init (SPImage * image);
56 static void sp_image_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
57 static void sp_image_release (SPObject * object);
58 static void sp_image_set (SPObject *object, unsigned int key, const gchar *value);
59 static void sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags);
60 static Inkscape::XML::Node *sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
62 static void sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
63 static void sp_image_print (SPItem * item, SPPrintContext *ctx);
64 static gchar * sp_image_description (SPItem * item);
65 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p);
66 static NRArenaItem *sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
67 static NR::Matrix sp_image_set_transform (SPItem *item, NR::Matrix const &xform);
69 GdkPixbuf *sp_image_repr_read_image (const gchar *href, const gchar *absref, const gchar *base);
70 static GdkPixbuf *sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf);
71 static void sp_image_update_canvas_image (SPImage *image);
72 static GdkPixbuf * sp_image_repr_read_dataURI (const gchar * uri_data);
73 static GdkPixbuf * sp_image_repr_read_b64 (const gchar * uri_data);
75 static SPItemClass *parent_class;
78 extern "C"
79 {
80     void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length );
81     void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length );
82     void user_flush_data( png_structp png_ptr );
84 }
87 #ifdef DEBUG_LCMS
88 extern guint update_in_progress;
89 #define DEBUG_MESSAGE(key, ...) \
90 {\
91     gint dump = prefs_get_int_attribute_limited("options.scislac", #key, 0, 0, 1);\
92     gint dumpD = prefs_get_int_attribute_limited("options.scislac", #key"D", 0, 0, 1);\
93     gint dumpD2 = prefs_get_int_attribute_limited("options.scislac", #key"D2", 0, 0, 1);\
94     dumpD &= ( (update_in_progress == 0) || dumpD2 );\
95     if ( dump )\
96     {\
97         g_message( __VA_ARGS__ );\
98 \
99     }\
100     if ( dumpD )\
101     {\
102         GtkWidget *dialog = gtk_message_dialog_new(NULL,\
103                                                    GTK_DIALOG_DESTROY_WITH_PARENT, \
104                                                    GTK_MESSAGE_INFO,    \
105                                                    GTK_BUTTONS_OK,      \
106                                                    __VA_ARGS__          \
107                                                    );\
108         g_signal_connect_swapped(dialog, "response",\
109                                  G_CALLBACK(gtk_widget_destroy),        \
110                                  dialog);                               \
111         gtk_widget_show_all( dialog );\
112     }\
114 #endif // DEBUG_LCMS
116 namespace Inkscape {
117 namespace IO {
119 class PushPull
121 public:
122     gboolean    first;
123     FILE*       fp;
124     guchar*     scratch;
125     gsize       size;
126     gsize       used;
127     gsize       offset;
128     GdkPixbufLoader *loader;
130     PushPull() : first(TRUE),
131                  fp(0),
132                  scratch(0),
133                  size(0),
134                  used(0),
135                  offset(0),
136                  loader(0) {};
138     gboolean readMore()
139     {
140         gboolean good = FALSE;
141         if ( offset )
142         {
143             g_memmove( scratch, scratch + offset, used - offset );
144             used -= offset;
145             offset = 0;
146         }
147         if ( used < size )
148         {
149             gsize space = size - used;
150             gsize got = fread( scratch + used, 1, space, fp );
151             if ( got )
152             {
153                 if ( loader )
154                 {
155                     GError *err = NULL;
156                     //g_message( " __read %d bytes", (int)got );
157                     if ( !gdk_pixbuf_loader_write( loader, scratch + used, got, &err ) )
158                     {
159                         //g_message("_error writing pixbuf data");
160                     }
161                 }
163                 used += got;
164                 good = TRUE;
165             }
166             else
167             {
168                 good = FALSE;
169             }
170         }
171         return good;
172     }
174     gsize available() const
175     {
176         return (used - offset);
177     }
179     gsize readOut( gpointer data, gsize length )
180     {
181         gsize giving = available();
182         if ( length < giving )
183         {
184             giving = length;
185         }
186         g_memmove( data, scratch + offset, giving );
187         offset += giving;
188         if ( offset >= used )
189         {
190             offset = 0;
191             used = 0;
192         }
193         return giving;
194     }
196     void clear()
197     {
198         offset = 0;
199         used = 0;
200     }
202 private:
203     PushPull& operator = (const PushPull& other);
204     PushPull(const PushPull& other);
205 };
207 void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length )
209 //    g_message( "user_read_data(%d)", length );
211     PushPull* youme = (PushPull*)png_get_io_ptr(png_ptr);
213     gsize filled = 0;
214     gboolean canRead = TRUE;
216     while ( filled < length && canRead )
217     {
218         gsize some = youme->readOut( data + filled, length - filled );
219         filled += some;
220         if ( filled < length )
221         {
222             canRead &= youme->readMore();
223         }
224     }
225 //    g_message("things out");
228 void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length )
230     //g_message( "user_write_data(%d)", length );
233 void user_flush_data( png_structp png_ptr )
235     //g_message( "user_flush_data" );
238 GdkPixbuf*  pixbuf_new_from_file( const char *filename, GError **error )
240     GdkPixbuf* buf = NULL;
241     PushPull youme;
242     gint dpiX = 0;
243     gint dpiY = 0;
245     //buf = gdk_pixbuf_new_from_file( filename, error );
246     dump_fopen_call( filename, "pixbuf_new_from_file" );
247     FILE* fp = fopen_utf8name( filename, "r" );
248     if ( fp )
249     {
250         GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
251         if ( loader )
252         {
253             GError *err = NULL;
255             // short buffer
256             guchar scratch[1024];
257             gboolean latter = FALSE;
258             gboolean isPng = FALSE;
259             png_structp pngPtr = NULL;
260             png_infop infoPtr = NULL;
261             //png_infop endPtr = NULL;
263             youme.fp = fp;
264             youme.scratch = scratch;
265             youme.size = sizeof(scratch);
266             youme.used = 0;
267             youme.offset = 0;
268             youme.loader = loader;
270             while ( !feof(fp) )
271             {
272                 if ( youme.readMore() )
273                 {
274                     if ( youme.first )
275                     {
276                         //g_message( "First data chunk" );
277                         youme.first = FALSE;
278                         isPng = !png_sig_cmp( scratch + youme.offset, 0, youme.available() );
279                         //g_message( "  png? %s", (isPng ? "Yes":"No") );
280                         if ( isPng )
281                         {
282                             pngPtr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
283                                                              NULL,//(png_voidp)user_error_ptr,
284                                                              NULL,//user_error_fn,
285                                                              NULL//user_warning_fn
286                                 );
287                             if ( pngPtr )
288                             {
289                                 infoPtr = png_create_info_struct( pngPtr );
290                                 //endPtr = png_create_info_struct( pngPtr );
292                                 png_set_read_fn( pngPtr, &youme, user_read_data );
293                                 //g_message( "In" );
295                                 //png_read_info( pngPtr, infoPtr );
296                                 png_read_png( pngPtr, infoPtr, PNG_TRANSFORM_IDENTITY, NULL );
298                                 //g_message("out");
300                                 //png_read_end(pngPtr, endPtr);
302                                 /*
303                                 if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_pHYs ) )
304                                 {
305                                     g_message("pHYs chunk now valid" );
306                                 }
307                                 if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_sCAL ) )
308                                 {
309                                     g_message("sCAL chunk now valid" );
310                                 }
311                                 */
313                                 png_uint_32 res_x = 0;
314                                 png_uint_32 res_y = 0;
315                                 int unit_type = 0;
316                                 if ( png_get_pHYs( pngPtr, infoPtr, &res_x, &res_y, &unit_type) )
317                                 {
318 //                                     g_message( "pHYs yes (%d, %d) %d (%s)", (int)res_x, (int)res_y, unit_type,
319 //                                                (unit_type == 1? "per meter" : "unknown")
320 //                                         );
322 //                                     g_message( "    dpi: (%d, %d)",
323 //                                                (int)(0.5 + ((double)res_x)/39.37),
324 //                                                (int)(0.5 + ((double)res_y)/39.37) );
325                                     if ( unit_type == PNG_RESOLUTION_METER )
326                                     {
327                                         // TODO come up with a more accurate DPI setting
328                                         dpiX = (int)(0.5 + ((double)res_x)/39.37);
329                                         dpiY = (int)(0.5 + ((double)res_y)/39.37);
330                                     }
331                                 }
332                                 else
333                                 {
334 //                                     g_message( "pHYs no" );
335                                 }
337 /*
338                                 double width = 0;
339                                 double height = 0;
340                                 int unit = 0;
341                                 if ( png_get_sCAL(pngPtr, infoPtr, &unit, &width, &height) )
342                                 {
343                                     gchar* vals[] = {
344                                         "unknown", // PNG_SCALE_UNKNOWN
345                                         "meter", // PNG_SCALE_METER
346                                         "radian", // PNG_SCALE_RADIAN
347                                         "last", //
348                                         NULL
349                                     };
351                                     g_message( "sCAL: (%f, %f) %d (%s)",
352                                                width, height, unit,
353                                                ((unit >= 0 && unit < 3) ? vals[unit]:"???")
354                                         );
355                                 }
356 */
358                                 // now clean it up.
359                                 png_destroy_read_struct( &pngPtr, &infoPtr, NULL );//&endPtr );
360                             }
361                             else
362                             {
363                                 g_message("Error when creating PNG read struct");
364                             }
365                         }
366                     }
367                     else if ( !latter )
368                     {
369                         latter = TRUE;
370                         //g_message("  READing latter");
371                     }
372                     // Now clear out the buffer so we can read more.
373                     // (dumping out unused)
374                     youme.clear();
375                 }
376             }
378             gboolean ok = gdk_pixbuf_loader_close(loader, &err);
379             if ( ok )
380             {
381                 buf = gdk_pixbuf_loader_get_pixbuf( loader );
382                 if ( buf )
383                 {
384                     g_object_ref(buf);
386                     if ( dpiX )
387                     {
388                         gchar *tmp = g_strdup_printf( "%d", dpiX );
389                         if ( tmp )
390                         {
391                             //gdk_pixbuf_set_option( buf, "Inkscape::DpiX", tmp );
392                             g_free( tmp );
393                         }
394                     }
395                     if ( dpiY )
396                     {
397                         gchar *tmp = g_strdup_printf( "%d", dpiY );
398                         if ( tmp )
399                         {
400                             //gdk_pixbuf_set_option( buf, "Inkscape::DpiY", tmp );
401                             g_free( tmp );
402                         }
403                     }
404                 }
405             }
406             else
407             {
408                 // do something
409                 g_message("error loading pixbuf at close");
410             }
412             g_object_unref(loader);
413         }
414         else
415         {
416             g_message("error when creating pixbuf loader");
417         }
418         fclose( fp );
419         fp = NULL;
420     }
421     else
422     {
423         g_warning ("Unable to open linked file: %s", filename);
424     }
426 /*
427     if ( buf )
428     {
429         const gchar* bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiX" );
430         if ( bloop )
431         {
432             g_message("DPI X is [%s]", bloop);
433         }
434         bloop = gdk_pixbuf_get_option( buf, "Inkscape::DpiY" );
435         if ( bloop )
436         {
437             g_message("DPI Y is [%s]", bloop);
438         }
439     }
440 */
442     return buf;
448 GType
449 sp_image_get_type (void)
451         static GType image_type = 0;
452         if (!image_type) {
453                 GTypeInfo image_info = {
454                         sizeof (SPImageClass),
455                         NULL,   /* base_init */
456                         NULL,   /* base_finalize */
457                         (GClassInitFunc) sp_image_class_init,
458                         NULL,   /* class_finalize */
459                         NULL,   /* class_data */
460                         sizeof (SPImage),
461                         16,     /* n_preallocs */
462                         (GInstanceInitFunc) sp_image_init,
463                         NULL,   /* value_table */
464                 };
465                 image_type = g_type_register_static (sp_item_get_type (), "SPImage", &image_info, (GTypeFlags)0);
466         }
467         return image_type;
470 static void
471 sp_image_class_init (SPImageClass * klass)
473         GObjectClass * gobject_class;
474         SPObjectClass * sp_object_class;
475         SPItemClass * item_class;
477         gobject_class = (GObjectClass *) klass;
478         sp_object_class = (SPObjectClass *) klass;
479         item_class = (SPItemClass *) klass;
481         parent_class = (SPItemClass*)g_type_class_ref (sp_item_get_type ());
483         sp_object_class->build = sp_image_build;
484         sp_object_class->release = sp_image_release;
485         sp_object_class->set = sp_image_set;
486         sp_object_class->update = sp_image_update;
487         sp_object_class->write = sp_image_write;
489         item_class->bbox = sp_image_bbox;
490         item_class->print = sp_image_print;
491         item_class->description = sp_image_description;
492         item_class->show = sp_image_show;
493         item_class->snappoints = sp_image_snappoints;
494         item_class->set_transform = sp_image_set_transform;
497 static void
498 sp_image_init (SPImage *image)
500         image->x.unset();
501         image->y.unset();
502         image->width.unset();
503         image->height.unset();
504         image->aspect_align = SP_ASPECT_NONE;
507 static void
508 sp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
510         if (((SPObjectClass *) parent_class)->build)
511                 ((SPObjectClass *) parent_class)->build (object, document, repr);
513         sp_object_read_attr (object, "xlink:href");
514         sp_object_read_attr (object, "x");
515         sp_object_read_attr (object, "y");
516         sp_object_read_attr (object, "width");
517         sp_object_read_attr (object, "height");
518         sp_object_read_attr (object, "preserveAspectRatio");
519         sp_object_read_attr (object, "color-profile");
521         /* Register */
522         sp_document_add_resource (document, "image", object);
525 static void
526 sp_image_release (SPObject *object)
528         SPImage *image;
530         image = SP_IMAGE (object);
532         if (SP_OBJECT_DOCUMENT (object)) {
533                 /* Unregister ourselves */
534                 sp_document_remove_resource (SP_OBJECT_DOCUMENT (object), "image", SP_OBJECT (object));
535         }
537         if (image->href) {
538                 g_free (image->href);
539                 image->href = NULL;
540         }
542         if (image->pixbuf) {
543                 gdk_pixbuf_unref (image->pixbuf);
544                 image->pixbuf = NULL;
545         }
547 #if ENABLE_LCMS
548         if (image->color_profile) {
549                 g_free (image->color_profile);
550                 image->color_profile = NULL;
551         }
552 #endif // ENABLE_LCMS
554         if (((SPObjectClass *) parent_class)->release)
555                 ((SPObjectClass *) parent_class)->release (object);
558 static void
559 sp_image_set (SPObject *object, unsigned int key, const gchar *value)
561         SPImage *image;
563         image = SP_IMAGE (object);
565         switch (key) {
566         case SP_ATTR_XLINK_HREF:
567                 g_free (image->href);
568                 image->href = (value) ? g_strdup (value) : NULL;
569                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
570                 break;
571         case SP_ATTR_X:
572                 if (!image->x.readAbsolute(value)) {
573                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
574                         image->x.unset();
575                 }
576                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
577                 break;
578         case SP_ATTR_Y:
579                 if (!image->y.readAbsolute(value)) {
580                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
581                         image->y.unset();
582                 }
583                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
584                 break;
585         case SP_ATTR_WIDTH:
586                 if (!image->width.readAbsolute(value)) {
587                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
588                         image->width.unset();
589                 }
590                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
591                 break;
592         case SP_ATTR_HEIGHT:
593                 if (!image->height.readAbsolute(value)) {
594                     /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */
595                         image->height.unset();
596                 }
597                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
598                 break;
599         case SP_ATTR_PRESERVEASPECTRATIO:
600                 /* Do setup before, so we can use break to escape */
601                 image->aspect_align = SP_ASPECT_NONE;
602                 image->aspect_clip = SP_ASPECT_MEET;
603                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
604                 if (value) {
605                         int len;
606                         gchar c[256];
607                         const gchar *p, *e;
608                         unsigned int align, clip;
609                         p = value;
610                         while (*p && *p == 32) p += 1;
611                         if (!*p) break;
612                         e = p;
613                         while (*e && *e != 32) e += 1;
614                         len = e - p;
615                         if (len > 8) break;
616                         memcpy (c, value, len);
617                         c[len] = 0;
618                         /* Now the actual part */
619                         if (!strcmp (c, "none")) {
620                                 align = SP_ASPECT_NONE;
621                         } else if (!strcmp (c, "xMinYMin")) {
622                                 align = SP_ASPECT_XMIN_YMIN;
623                         } else if (!strcmp (c, "xMidYMin")) {
624                                 align = SP_ASPECT_XMID_YMIN;
625                         } else if (!strcmp (c, "xMaxYMin")) {
626                                 align = SP_ASPECT_XMAX_YMIN;
627                         } else if (!strcmp (c, "xMinYMid")) {
628                                 align = SP_ASPECT_XMIN_YMID;
629                         } else if (!strcmp (c, "xMidYMid")) {
630                                 align = SP_ASPECT_XMID_YMID;
631                         } else if (!strcmp (c, "xMaxYMid")) {
632                                 align = SP_ASPECT_XMAX_YMID;
633                         } else if (!strcmp (c, "xMinYMax")) {
634                                 align = SP_ASPECT_XMIN_YMAX;
635                         } else if (!strcmp (c, "xMidYMax")) {
636                                 align = SP_ASPECT_XMID_YMAX;
637                         } else if (!strcmp (c, "xMaxYMax")) {
638                                 align = SP_ASPECT_XMAX_YMAX;
639                         } else {
640                                 break;
641                         }
642                         clip = SP_ASPECT_MEET;
643                         while (*e && *e == 32) e += 1;
644                         if (e) {
645                                 if (!strcmp (e, "meet")) {
646                                         clip = SP_ASPECT_MEET;
647                                 } else if (!strcmp (e, "slice")) {
648                                         clip = SP_ASPECT_SLICE;
649                                 } else {
650                                         break;
651                                 }
652                         }
653                         image->aspect_align = align;
654                         image->aspect_clip = clip;
655                 }
656                 break;
657 #if ENABLE_LCMS
658         case SP_PROP_COLOR_PROFILE:
659                 if ( image->color_profile ) {
660                     g_free (image->color_profile);
661                 }
662                 image->color_profile = (value) ? g_strdup (value) : NULL;
663 #ifdef DEBUG_LCMS
664                 if ( value ) {
665                     DEBUG_MESSAGE( lcmsFour, "<image> color-profile set to '%s'", value );
666                 } else {
667                     DEBUG_MESSAGE( lcmsFour, "<image> color-profile cleared" );
668                 }
669 #endif // DEBUG_LCMS
670                 // TODO check on this HREF_MODIFIED flag
671                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_IMAGE_HREF_MODIFIED_FLAG);
672                 break;
673 #endif // ENABLE_LCMS
674         default:
675                 if (((SPObjectClass *) (parent_class))->set)
676                         ((SPObjectClass *) (parent_class))->set (object, key, value);
677                 break;
678         }
681 static void
682 sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
684     SPImage *image;
686     image = (SPImage *) object;
687     SPDocument *doc = SP_OBJECT_DOCUMENT(object);
689         if (((SPObjectClass *) (parent_class))->update)
690                 ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
692         if (flags & SP_IMAGE_HREF_MODIFIED_FLAG) {
693                 if (image->pixbuf) {
694                         gdk_pixbuf_unref (image->pixbuf);
695                         image->pixbuf = NULL;
696                 }
697                 if (image->href) {
698                         GdkPixbuf *pixbuf;
699                         pixbuf = sp_image_repr_read_image (
700                     object->repr->attribute("xlink:href"), 
701                     object->repr->attribute("sodipodi:absref"), 
702                     doc->base);
703                         if (pixbuf) {
704                                 pixbuf = sp_image_pixbuf_force_rgba (pixbuf);
705 // BLIP
706 #if ENABLE_LCMS
707                                 if ( image->color_profile )
708                                 {
709                                     int imagewidth = gdk_pixbuf_get_width( pixbuf );
710                                     int imageheight = gdk_pixbuf_get_height( pixbuf );
711                                     int rowstride = gdk_pixbuf_get_rowstride( pixbuf );
712                                     guchar* px = gdk_pixbuf_get_pixels( pixbuf );
714                                     if ( px ) {
715 #ifdef DEBUG_LCMS
716                                         DEBUG_MESSAGE( lcmsFive, "in <image>'s sp_image_update. About to call colorprofile_get_handle()" );
717 #endif // DEBUG_LCMS
718                                         guint profIntent = Inkscape::RENDERING_INTENT_UNKNOWN;
719                                         cmsHPROFILE prof = Inkscape::colorprofile_get_handle( SP_OBJECT_DOCUMENT( object ),
720                                                                                               &profIntent,
721                                                                                               image->color_profile );
722                                         if ( prof ) {
723                                             icProfileClassSignature profileClass = cmsGetDeviceClass( prof );
724                                             if ( profileClass != icSigNamedColorClass ) {
725                                                 int intent = INTENT_PERCEPTUAL;
726                                                 switch ( profIntent ) {
727                                                     case Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC:
728                                                         intent = INTENT_RELATIVE_COLORIMETRIC;
729                                                         break;
730                                                     case Inkscape::RENDERING_INTENT_SATURATION:
731                                                         intent = INTENT_SATURATION;
732                                                         break;
733                                                     case Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC:
734                                                         intent = INTENT_ABSOLUTE_COLORIMETRIC;
735                                                         break;
736                                                     case Inkscape::RENDERING_INTENT_PERCEPTUAL:
737                                                     case Inkscape::RENDERING_INTENT_UNKNOWN:
738                                                     case Inkscape::RENDERING_INTENT_AUTO:
739                                                     default:
740                                                         intent = INTENT_PERCEPTUAL;
741                                                 }
742                                                 cmsHPROFILE destProf = cmsCreate_sRGBProfile();
743                                                 cmsHTRANSFORM transf = cmsCreateTransform( prof, 
744                                                                                            TYPE_RGBA_8,
745                                                                                            destProf,
746                                                                                            TYPE_RGBA_8,
747                                                                                            intent, 0 );
748                                                 if ( transf ) {
749                                                     guchar* currLine = px;
750                                                     for ( int y = 0; y < imageheight; y++ ) {
751                                                         // Since the types are the same size, we can do the transformation in-place
752                                                         cmsDoTransform( transf, currLine, currLine, imagewidth );
753                                                         currLine += rowstride;
754                                                     }
756                                                     cmsDeleteTransform( transf );
757                                                 }
758 #ifdef DEBUG_LCMS
759                                                 else
760                                                 {
761                                                     DEBUG_MESSAGE( lcmsSix, "in <image>'s sp_image_update. Unable to create LCMS transform." );
762                                                 }
763 #endif // DEBUG_LCMS
764                                                 cmsCloseProfile( destProf );
765                                             }
766 #ifdef DEBUG_LCMS
767                                             else
768                                             {
769                                                 DEBUG_MESSAGE( lcmsSeven, "in <image>'s sp_image_update. Profile type is named color. Can't transform." );
770                                             }
771 #endif // DEBUG_LCMS
772                                         }
773 #ifdef DEBUG_LCMS
774                                         else
775                                         {
776                                             DEBUG_MESSAGE( lcmsEight, "in <image>'s sp_image_update. No profile found." );
777                                         }
778 #endif // DEBUG_LCMS
779                                     }
780                                 }
781 #endif // ENABLE_LCMS
782                                 image->pixbuf = pixbuf;
783                         }
784                 }
785         }
786         // preserveAspectRatio calculate bounds / clipping rectangle -- EAF
787         if (image->pixbuf && (image->aspect_align != SP_ASPECT_NONE)) {
788                         int imagewidth, imageheight;
789                         double x,y;
791                         imagewidth = gdk_pixbuf_get_width (image->pixbuf);
792                         imageheight = gdk_pixbuf_get_height (image->pixbuf);
794                         switch (image->aspect_align) {
795                         case SP_ASPECT_XMIN_YMIN:
796                                 x = 0.0;
797                                 y = 0.0;
798                                 break;
799                         case SP_ASPECT_XMID_YMIN:
800                                 x = 0.5;
801                                 y = 0.0;
802                                 break;
803                         case SP_ASPECT_XMAX_YMIN:
804                                 x = 1.0;
805                                 y = 0.0;
806                                 break;
807                         case SP_ASPECT_XMIN_YMID:
808                                 x = 0.0;
809                                 y = 0.5;
810                                 break;
811                         case SP_ASPECT_XMID_YMID:
812                                 x = 0.5;
813                                 y = 0.5;
814                                 break;
815                         case SP_ASPECT_XMAX_YMID:
816                                 x = 1.0;
817                                 y = 0.5;
818                                 break;
819                         case SP_ASPECT_XMIN_YMAX:
820                                 x = 0.0;
821                                 y = 1.0;
822                                 break;
823                         case SP_ASPECT_XMID_YMAX:
824                                 x = 0.5;
825                                 y = 1.0;
826                                 break;
827                         case SP_ASPECT_XMAX_YMAX:
828                                 x = 1.0;
829                                 y = 1.0;
830                                 break;
831                         default:
832                                 x = 0.0;
833                                 y = 0.0;
834                                 break;
835                         }
837                         if (image->aspect_clip == SP_ASPECT_SLICE) {
838                                 image->viewx = image->x.computed;
839                                 image->viewy = image->y.computed;
840                                 image->viewwidth = image->width.computed;
841                                 image->viewheight = image->height.computed;
842                                 if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
843                                         // Pixels aspect is wider than bounding box
844                                         image->trimheight = imageheight;
845                                         image->trimwidth = static_cast<int>(static_cast<double>(imageheight) * image->width.computed / image->height.computed);
846                                         image->trimy = 0;
847                                         image->trimx = static_cast<int>(static_cast<double>(imagewidth - image->trimwidth) * x);
848                                 } else {
849                                         // Pixels aspect is taller than bounding box
850                                         image->trimwidth = imagewidth;
851                                         image->trimheight = static_cast<int>(static_cast<double>(imagewidth) * image->height.computed / image->width.computed);
852                                         image->trimx = 0;
853                                         image->trimy = static_cast<int>(static_cast<double>(imageheight - image->trimheight) * y);
854                                 }
855                         } else {
856                                 // Otherwise, assume SP_ASPECT_MEET
857                                 image->trimx = 0;
858                                 image->trimy = 0;
859                                 image->trimwidth = imagewidth;
860                                 image->trimheight = imageheight;
861                                 if ((imagewidth*image->height.computed)>(image->width.computed*imageheight)) {
862                                         // Pixels aspect is wider than bounding boz
863                                         image->viewwidth = image->width.computed;
864                                         image->viewheight = image->viewwidth * imageheight / imagewidth;
865                                         image->viewx=image->x.computed;
866                                         image->viewy=(image->height.computed - image->viewheight) * y + image->y.computed;
867                                 } else {
868                                         // Pixels aspect is taller than bounding box
869                                         image->viewheight = image->height.computed;
870                                         image->viewwidth = image->viewheight * imagewidth / imageheight;
871                                         image->viewy=image->y.computed;
872                                         image->viewx=(image->width.computed - image->viewwidth) * x + image->x.computed;
873                                 }
874                         }
875         }
877         sp_image_update_canvas_image ((SPImage *) object);
880 static Inkscape::XML::Node *
881 sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
883         SPImage *image;
885         image = SP_IMAGE (object);
887         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
888                 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
889                 repr = xml_doc->createElement("svg:image");
890         }
892         repr->setAttribute("xlink:href", image->href);
893         /* fixme: Reset attribute if needed (Lauris) */
894         if (image->x._set) sp_repr_set_svg_double(repr, "x", image->x.computed);
895         if (image->y._set) sp_repr_set_svg_double(repr, "y", image->y.computed);
896         if (image->width._set) sp_repr_set_svg_double(repr, "width", image->width.computed);
897         if (image->height._set) sp_repr_set_svg_double(repr, "height", image->height.computed);
898         repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
899 #if ENABLE_LCMS
900         if (image->color_profile) repr->setAttribute("color-profile", image->color_profile);
901 #endif // ENABLE_LCMS
903         if (((SPObjectClass *) (parent_class))->write)
904                 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
906         return repr;
909 static void
910 sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
912         SPImage const &image = *SP_IMAGE(item);
914         if ((image.width.computed > 0.0) && (image.height.computed > 0.0)) {
915                 double const x0 = image.x.computed;
916                 double const y0 = image.y.computed;
917                 double const x1 = x0 + image.width.computed;
918                 double const y1 = y0 + image.height.computed;
920                 nr_rect_union_pt(bbox, NR::Point(x0, y0) * transform);
921                 nr_rect_union_pt(bbox, NR::Point(x1, y0) * transform);
922                 nr_rect_union_pt(bbox, NR::Point(x1, y1) * transform);
923                 nr_rect_union_pt(bbox, NR::Point(x0, y1) * transform);
924         }
927 static void
928 sp_image_print (SPItem *item, SPPrintContext *ctx)
930         SPImage *image;
931         NRMatrix tp, ti, s, t;
932         guchar *px;
933         int w, h, rs, pixskip;
935         image = SP_IMAGE (item);
937         if (!image->pixbuf) return;
938         if ((image->width.computed <= 0.0) || (image->height.computed <= 0.0)) return;
940         px = gdk_pixbuf_get_pixels (image->pixbuf);
941         w = gdk_pixbuf_get_width (image->pixbuf);
942         h = gdk_pixbuf_get_height (image->pixbuf);
943         rs = gdk_pixbuf_get_rowstride (image->pixbuf);
944         pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
946         if (image->aspect_align == SP_ASPECT_NONE) {
947                 /* fixme: (Lauris) */
948                 nr_matrix_set_translate (&tp, image->x.computed, image->y.computed);
949                 nr_matrix_set_scale (&s, image->width.computed, -image->height.computed);
950                 nr_matrix_set_translate (&ti, 0.0, -1.0);
951         } else { // preserveAspectRatio
952                 nr_matrix_set_translate (&tp, image->viewx, image->viewy);
953                 nr_matrix_set_scale (&s, image->viewwidth, -image->viewheight);
954                 nr_matrix_set_translate (&ti, 0.0, -1.0);
955         }
957         nr_matrix_multiply (&t, &s, &tp);
958         nr_matrix_multiply (&t, &ti, &t);
960         if (image->aspect_align == SP_ASPECT_NONE)
961                 sp_print_image_R8G8B8A8_N (ctx, px, w, h, rs, &t, SP_OBJECT_STYLE (item));
962         else // preserveAspectRatio
963                 sp_print_image_R8G8B8A8_N (ctx, px + image->trimx*pixskip + image->trimy*rs, image->trimwidth, image->trimheight, rs, &t, SP_OBJECT_STYLE (item));
966 static gchar *
967 sp_image_description(SPItem *item)
969         SPImage *image = SP_IMAGE(item);
970         char *href_desc;
971         if (image->href) {
972             href_desc = (strncmp(image->href, "data:", 5) == 0)
973                 ? g_strdup(_("embedded"))
974                 : xml_quote_strdup(image->href);
975         } else {
976             g_warning("Attempting to call strncmp() with a null pointer.");
977             href_desc = g_strdup("(null_pointer)"); // we call g_free() on href_desc
978         }
980         char *ret = ( image->pixbuf == NULL
981                       ? g_strdup_printf(_("<b>Image with bad reference</b>: %s"), href_desc)
982                       : g_strdup_printf(_("<b>Image</b> %d &#215; %d: %s"),
983                                         gdk_pixbuf_get_width(image->pixbuf),
984                                         gdk_pixbuf_get_height(image->pixbuf),
985                                         href_desc) );
986         g_free(href_desc);
987         return ret;
990 static NRArenaItem *
991 sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
993         int pixskip, rs;
994         SPImage * image;
995         NRArenaItem *ai;
997         image = (SPImage *) item;
999         ai = NRArenaImage::create(arena);
1001         if (image->pixbuf) {
1002                 pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1003                 rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1004                 nr_arena_image_set_style(NR_ARENA_IMAGE(ai), SP_OBJECT_STYLE(SP_OBJECT(item)));
1005                 if (image->aspect_align == SP_ASPECT_NONE)
1006                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
1007                                            gdk_pixbuf_get_pixels (image->pixbuf),
1008                                            gdk_pixbuf_get_width (image->pixbuf),
1009                                            gdk_pixbuf_get_height (image->pixbuf),
1010                                            rs);
1011                 else // preserveAspectRatio
1012                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai),
1013                                            gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
1014                                            image->trimwidth,
1015                                            image->trimheight,
1016                                            rs);
1017         } else {
1018                 nr_arena_image_set_pixels (NR_ARENA_IMAGE (ai), NULL, 0, 0, 0);
1019         }
1020         if (image->aspect_align == SP_ASPECT_NONE)
1021                 nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->x.computed, image->y.computed, image->width.computed, image->height.computed);
1022         else // preserveAspectRatio
1023                 nr_arena_image_set_geometry (NR_ARENA_IMAGE (ai), image->viewx, image->viewy, image->viewwidth, image->viewheight);
1025         return ai;
1028 /*
1029  * utility function to try loading image from href
1030  *
1031  * docbase/relative_src
1032  * absolute_src
1033  *
1034  */
1036 GdkPixbuf *
1037 sp_image_repr_read_image (const gchar *href, const gchar *absref, const gchar *base)
1039     const gchar *filename, *docbase;
1040     gchar *fullname;
1041     GdkPixbuf *pixbuf;
1043     filename = href;
1044     if (filename != NULL) {
1045         if (strncmp (filename,"file:",5) == 0) {
1046             fullname = g_filename_from_uri(filename, NULL, NULL);
1047             if (fullname) {
1048                 // TODO check this. Was doing a UTF-8 to filename conversion here.
1049                 pixbuf = Inkscape::IO::pixbuf_new_from_file (fullname, NULL);
1050                 if (pixbuf != NULL) return pixbuf;
1051             }
1052         } else if (strncmp (filename,"data:",5) == 0) {
1053             /* data URI - embedded image */
1054             filename += 5;
1055             pixbuf = sp_image_repr_read_dataURI (filename);
1056             if (pixbuf != NULL) return pixbuf;
1057         } else {
1059             if (!g_path_is_absolute (filename)) {
1060                 /* try to load from relative pos combined with document base*/
1061                 docbase = base;
1062                 if (!docbase) docbase = ".";
1063                 fullname = g_build_filename(docbase, filename, NULL);
1065                 // document base can be wrong (on the temporary doc when importing bitmap from a 
1066                 // different dir) or unset (when doc is not saved yet), so we check for base+href existence first,
1067                 // and if it fails, we also try to use bare href regardless of its g_path_is_absolute
1068                 if (g_file_test (fullname, G_FILE_TEST_EXISTS) && !g_file_test (fullname, G_FILE_TEST_IS_DIR)) {
1069                     pixbuf = Inkscape::IO::pixbuf_new_from_file( fullname, NULL );
1070                     g_free (fullname);
1071                     if (pixbuf != NULL) return pixbuf;
1072                 }
1073             } 
1075             /* try filename as absolute */
1076             if (g_file_test (filename, G_FILE_TEST_EXISTS) && !g_file_test (filename, G_FILE_TEST_IS_DIR)) {
1077                 pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
1078                 if (pixbuf != NULL) return pixbuf;
1079             }
1080         }
1081     }
1083     /* at last try to load from sp absolute path name */
1084     filename = absref;
1085     if (filename != NULL) {
1086         // using absref is outside of SVG rules, so we must at least warn the user
1087         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);
1088         pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, NULL );
1089         if (pixbuf != NULL) return pixbuf;
1090     }
1091     /* Nope: We do not find any valid pixmap file :-( */
1092     pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) brokenimage_xpm);
1094     /* It should be included xpm, so if it still does not does load, */
1095     /* our libraries are broken */
1096     g_assert (pixbuf != NULL);
1098     return pixbuf;
1101 static GdkPixbuf *
1102 sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf)
1104         GdkPixbuf * newbuf;
1106         if (gdk_pixbuf_get_has_alpha (pixbuf)) return pixbuf;
1108         newbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
1109         gdk_pixbuf_unref (pixbuf);
1111         return newbuf;
1114 /* We assert that realpixbuf is either NULL or identical size to pixbuf */
1116 static void
1117 sp_image_update_canvas_image (SPImage *image)
1119         int rs, pixskip;
1120         SPItem *item;
1121         SPItemView *v;
1123         item = SP_ITEM (image);
1125         if (image->pixbuf) {
1126                 /* fixme: We are slightly violating spec here (Lauris) */
1127                 if (!image->width._set) {
1128                         image->width.computed = gdk_pixbuf_get_width (image->pixbuf);
1129                 }
1130                 if (!image->height._set) {
1131                         image->height.computed = gdk_pixbuf_get_height (image->pixbuf);
1132                 }
1133         }
1135         for (v = item->display; v != NULL; v = v->next) {
1136                 pixskip = gdk_pixbuf_get_n_channels (image->pixbuf) * gdk_pixbuf_get_bits_per_sample (image->pixbuf) / 8;
1137                 rs = gdk_pixbuf_get_rowstride (image->pixbuf);
1138                 nr_arena_image_set_style (NR_ARENA_IMAGE(v->arenaitem), SP_OBJECT_STYLE(SP_OBJECT(image)));
1139                 if (image->aspect_align == SP_ASPECT_NONE) {
1140                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
1141                                            gdk_pixbuf_get_pixels (image->pixbuf),
1142                                            gdk_pixbuf_get_width (image->pixbuf),
1143                                            gdk_pixbuf_get_height (image->pixbuf),
1144                                            rs);
1145                         nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
1146                                              image->x.computed, image->y.computed,
1147                                              image->width.computed, image->height.computed);
1148                 } else { // preserveAspectRatio
1149                         nr_arena_image_set_pixels (NR_ARENA_IMAGE (v->arenaitem),
1150                                            gdk_pixbuf_get_pixels (image->pixbuf) + image->trimx*pixskip + image->trimy*rs,
1151                                            image->trimwidth,
1152                                            image->trimheight,
1153                                            rs);
1154                         nr_arena_image_set_geometry (NR_ARENA_IMAGE (v->arenaitem),
1155                                              image->viewx, image->viewy,
1156                                              image->viewwidth, image->viewheight);
1157                 }
1158         }
1161 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p)
1163      if (((SPItemClass *) parent_class)->snappoints) {
1164          ((SPItemClass *) parent_class)->snappoints (item, p);
1165      }
1168 /*
1169  * Initially we'll do:
1170  * Transform x, y, set x, y, clear translation
1171  */
1173 static NR::Matrix
1174 sp_image_set_transform(SPItem *item, NR::Matrix const &xform)
1176         SPImage *image = SP_IMAGE(item);
1178         /* Calculate position in parent coords. */
1179         NR::Point pos( NR::Point(image->x.computed, image->y.computed) * xform );
1181         /* This function takes care of translation and scaling, we return whatever parts we can't
1182            handle. */
1183         NR::Matrix ret(NR::transform(xform));
1184         NR::Point const scale(hypot(ret[0], ret[1]),
1185                               hypot(ret[2], ret[3]));
1186         if ( scale[NR::X] > 1e-9 ) {
1187                 ret[0] /= scale[NR::X];
1188                 ret[1] /= scale[NR::X];
1189         } else {
1190                 ret[0] = 1.0;
1191                 ret[1] = 0.0;
1192         }
1193         if ( scale[NR::Y] > 1e-9 ) {
1194                 ret[2] /= scale[NR::Y];
1195                 ret[3] /= scale[NR::Y];
1196         } else {
1197                 ret[2] = 0.0;
1198                 ret[3] = 1.0;
1199         }
1201         image->width = image->width.computed * scale[NR::X];
1202         image->height = image->height.computed * scale[NR::Y];
1204         /* Find position in item coords */
1205         pos = pos * ret.inverse();
1206         image->x = pos[NR::X];
1207         image->y = pos[NR::Y];
1209         item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
1211         return ret;
1214 static GdkPixbuf *
1215 sp_image_repr_read_dataURI (const gchar * uri_data)
1216 {       GdkPixbuf * pixbuf = NULL;
1218         gint data_is_image = 0;
1219         gint data_is_base64 = 0;
1221         const gchar * data = uri_data;
1223         while (*data) {
1224                 if (strncmp (data,"base64",6) == 0) {
1225                         /* base64-encoding */
1226                         data_is_base64 = 1;
1227                         data_is_image = 1; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what
1228                         data += 6;
1229                 }
1230                 else if (strncmp (data,"image/png",9) == 0) {
1231                         /* PNG image */
1232                         data_is_image = 1;
1233                         data += 9;
1234                 }
1235                 else if (strncmp (data,"image/jpg",9) == 0) {
1236                         /* JPEG image */
1237                         data_is_image = 1;
1238                         data += 9;
1239                 }
1240                 else if (strncmp (data,"image/jpeg",10) == 0) {
1241                         /* JPEG image */
1242                         data_is_image = 1;
1243                         data += 10;
1244                 }
1245                 else { /* unrecognized option; skip it */
1246                         while (*data) {
1247                                 if (((*data) == ';') || ((*data) == ',')) break;
1248                                 data++;
1249                         }
1250                 }
1251                 if ((*data) == ';') {
1252                         data++;
1253                         continue;
1254                 }
1255                 if ((*data) == ',') {
1256                         data++;
1257                         break;
1258                 }
1259         }
1261         if ((*data) && data_is_image && data_is_base64) {
1262                 pixbuf = sp_image_repr_read_b64 (data);
1263         }
1265         return pixbuf;
1268 static GdkPixbuf *
1269 sp_image_repr_read_b64 (const gchar * uri_data)
1270 {       GdkPixbuf * pixbuf = NULL;
1271         GdkPixbufLoader * loader = NULL;
1273         gint j;
1274         gint k;
1275         gint l;
1276         gint b;
1277         gint len;
1278         gint eos = 0;
1279         gint failed = 0;
1281         guint32 bits;
1283         static const gchar B64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1285         const gchar* btr = uri_data;
1287         gchar ud[4];
1289         guchar bd[57];
1291         loader = gdk_pixbuf_loader_new ();
1293         if (loader == NULL) return NULL;
1295         while (eos == 0) {
1296                 l = 0;
1297                 for (j = 0; j < 19; j++) {
1298                         len = 0;
1299                         for (k = 0; k < 4; k++) {
1300                                 while (isspace ((int) (*btr))) {
1301                                         if ((*btr) == '\0') break;
1302                                         btr++;
1303                                 }
1304                                 if (eos) {
1305                                         ud[k] = 0;
1306                                         continue;
1307                                 }
1308                                 if (((*btr) == '\0') || ((*btr) == '=')) {
1309                                         eos = 1;
1310                                         ud[k] = 0;
1311                                         continue;
1312                                 }
1313                                 ud[k] = 64;
1314                                 for (b = 0; b < 64; b++) { /* There must a faster way to do this... ?? */
1315                                         if (B64[b] == (*btr)) {
1316                                                 ud[k] = (gchar) b;
1317                                                 break;
1318                                         }
1319                                 }
1320                                 if (ud[k] == 64) { /* data corruption ?? */
1321                                         eos = 1;
1322                                         ud[k] = 0;
1323                                         continue;
1324                                 }
1325                                 btr++;
1326                                 len++;
1327                         }
1328                         bits = (guint32) ud[0];
1329                         bits = (bits << 6) | (guint32) ud[1];
1330                         bits = (bits << 6) | (guint32) ud[2];
1331                         bits = (bits << 6) | (guint32) ud[3];
1332                         bd[l++] = (guchar) ((bits & 0xff0000) >> 16);
1333                         if (len > 2) {
1334                                 bd[l++] = (guchar) ((bits & 0xff00) >>  8);
1335                         }
1336                         if (len > 3) {
1337                                 bd[l++] = (guchar)  (bits & 0xff);
1338                         }
1339                 }
1341                 if (!gdk_pixbuf_loader_write (loader, (const guchar *) bd, (size_t) l, NULL)) {
1342                         failed = 1;
1343                         break;
1344                 }
1345         }
1347         gdk_pixbuf_loader_close (loader, NULL);
1349         if (!failed) pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
1351         return pixbuf;
1354 /*
1355   Local Variables:
1356   mode:c++
1357   c-file-style:"stroustrup"
1358   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1359   indent-tabs-mode:nil
1360   fill-column:99
1361   End:
1362 */
1363 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :