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__ );\
104 \
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 }\
119 }
120 #endif // DEBUG_LCMS
122 namespace Inkscape {
123 namespace IO {
125 class PushPull
126 {
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 )
214 {
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");
232 }
234 void user_write_data( png_structp /*png_ptr*/, png_bytep /*data*/, png_size_t /*length*/ )
235 {
236 //g_message( "user_write_data(%d)", length );
237 }
239 void user_flush_data( png_structp /*png_ptr*/ )
240 {
241 //g_message( "user_flush_data" );
242 }
244 GdkPixbuf* pixbuf_new_from_file( const char *filename, GError **/*error*/ )
245 {
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;
503 }
505 }
506 }
508 GType
509 sp_image_get_type (void)
510 {
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;
528 }
530 static void
531 sp_image_class_init (SPImageClass * klass)
532 {
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;
555 }
557 static void
558 sp_image_init (SPImage *image)
559 {
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;
566 }
568 static void
569 sp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
570 {
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);
584 }
586 static void
587 sp_image_release (SPObject *object)
588 {
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);
621 }
623 static void
624 sp_image_set (SPObject *object, unsigned int key, const gchar *value)
625 {
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 }
745 sp_image_set_curve(image); //creates a curve at the image's boundary for snapping
746 }
748 static void
749 sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
750 {
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);
944 }
946 static Inkscape::XML::Node *
947 sp_image_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
948 {
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;
973 }
975 static void
976 sp_image_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const /*flags*/)
977 {
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 }
991 }
993 static void
994 sp_image_print (SPItem *item, SPPrintContext *ctx)
995 {
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));
1030 }
1032 static gchar *
1033 sp_image_description(SPItem *item)
1034 {
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 × %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;
1054 }
1056 static NRArenaItem *
1057 sp_image_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
1058 {
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;
1092 }
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)
1104 {
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;
1169 }
1171 static GdkPixbuf *
1172 sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf)
1173 {
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;
1182 }
1184 /* We assert that realpixbuf is either NULL or identical size to pixbuf */
1186 static void
1187 sp_image_update_canvas_image (SPImage *image)
1188 {
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 }
1229 }
1231 static void sp_image_snappoints(SPItem const *item, SnapPointsIter p)
1232 {
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 */
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 }
1257 }
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)
1266 {
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;
1303 }
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;
1357 }
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;
1443 }
1445 static void
1446 sp_image_set_curve(SPImage *image)
1447 {
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 }
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);
1461 if (image->curve) {
1462 image->curve = sp_curve_unref(image->curve);
1463 }
1465 if (c) {
1466 image->curve = sp_curve_ref(c);
1467 }
1469 sp_curve_unref(c);
1470 }
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)
1477 {
1478 if (image->curve) {
1479 return sp_curve_copy(image->curve);
1480 }
1481 return NULL;
1482 }
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 :