summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2013-09-14 01:59:43 +0000
committerKrzysztof KosiƄski <tweenk.pl@gmail.com>2013-09-14 01:59:43 +0000
commit35dc2e5a640375d51119f2051a240454b5f5b8c8 (patch)
tree2b35e5a1068a675c9c27cb13eaec598410a9b787 /src
parentFix serious bug in recent GdkPixbuf / Cairo interop rework (diff)
downloadinkscape-35dc2e5a640375d51119f2051a240454b5f5b8c8.tar.gz
inkscape-35dc2e5a640375d51119f2051a240454b5f5b8c8.zip
Do not recompress images when embedding and generating PDFs.
Fixes blocker bug #871563. Fixed bugs: - https://launchpad.net/bugs/871563 (bzr r12516)
Diffstat (limited to 'src')
-rw-r--r--src/display/cairo-utils.cpp5
-rw-r--r--src/display/drawing-image.cpp4
-rw-r--r--src/display/nr-filter-image.cpp2
-rw-r--r--src/extension/internal/gdkpixbuf-input.cpp81
-rw-r--r--src/extension/internal/image-resolution.cpp18
-rw-r--r--src/selection-chemistry.cpp3
-rw-r--r--src/sp-image.cpp566
-rw-r--r--src/sp-image.h2
8 files changed, 162 insertions, 519 deletions
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp
index 2d94d024f..755553033 100644
--- a/src/display/cairo-utils.cpp
+++ b/src/display/cairo-utils.cpp
@@ -423,6 +423,11 @@ ink_cairo_set_source_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y)
* The returned surface is owned by the GdkPixbuf and should not be freed.
* Calling this function causes the pixbuf to be unsuitable for use
* with GTK drawing functions until ink_pixbuf_ensure_normal() is called.
+ *
+ * @bug You have to call g_object_set_data(G_OBJECT(pb), "cairo_surface", NULL)
+ * when unrefing the last reference to the pixbuf. Otherwise there will be
+ * crashes, because cairo_surface_destroy is called after the pixbuf data
+ * is already freed.
*/
cairo_surface_t *
ink_cairo_surface_get_for_pixbuf(GdkPixbuf *pb)
diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp
index b94d48774..46f066b8e 100644
--- a/src/display/drawing-image.cpp
+++ b/src/display/drawing-image.cpp
@@ -46,7 +46,7 @@ DrawingImage::setARGB32Pixbuf(GdkPixbuf *pb)
}
if (_pixbuf != NULL) {
g_object_unref(_pixbuf);
- cairo_surface_destroy(_surface);
+ // unrefing the pixbuf also destroys surface
}
_pixbuf = pb;
_surface = pb ? ink_cairo_surface_get_for_pixbuf(pb) : NULL;
@@ -206,7 +206,7 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar
int orgstride = cairo_image_surface_get_stride(_surface);
int newstride = cairo_image_surface_get_stride(_new_surface);
- cairo_surface_flush(_surface);
+ //cairo_surface_flush(_surface);
cairo_surface_flush(_new_surface);
for(int y=0; y<newheight; y++) {
diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp
index 8b307ac00..b9d73f0ad 100644
--- a/src/display/nr-filter-image.cpp
+++ b/src/display/nr-filter-image.cpp
@@ -41,6 +41,7 @@ FilterImage::~FilterImage()
{
if (feImageHref)
g_free(feImageHref);
+ g_object_set_data(G_OBJECT(image->gobj()), "cairo_surface", NULL);
}
void FilterImage::render_cairo(FilterSlot &slot)
@@ -301,6 +302,7 @@ void FilterImage::set_href(const gchar *href){
if (feImageHref) g_free (feImageHref);
feImageHref = (href) ? g_strdup (href) : NULL;
+ g_object_set_data(G_OBJECT(image->gobj()), "cairo_surface", NULL);
image.reset();
broken_ref = false;
}
diff --git a/src/extension/internal/gdkpixbuf-input.cpp b/src/extension/internal/gdkpixbuf-input.cpp
index 994258ccc..117c2fe39 100644
--- a/src/extension/internal/gdkpixbuf-input.cpp
+++ b/src/extension/internal/gdkpixbuf-input.cpp
@@ -19,23 +19,18 @@
namespace Inkscape {
namespace IO {
-GdkPixbuf* pixbuf_new_from_file( char const *utf8name, GError **error );
+// this is defined in sp-image.cpp
+GdkPixbuf* pixbuf_new_from_file(char const *filename, time_t &modTime, gchar*& pixPath);
}
namespace Extension {
namespace Internal {
-static std::set<Glib::ustring> create_lossy_set()
-{
- std::set<Glib::ustring> lossy;
- lossy.insert(".jpg");
- lossy.insert(".jpeg");
- return lossy;
-}
-
SPDocument *
GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
{
+ // determine whether the image should be embedded
+ // TODO: this logic seems very wrong
bool embed = false;
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
Glib::ustring attr = prefs->getString("/dialogs/import/link");
@@ -52,27 +47,14 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
}
SPDocument *doc = NULL;
- GdkPixbuf *pb = Inkscape::IO::pixbuf_new_from_file( uri, NULL );
- static std::set<Glib::ustring> lossy = create_lossy_set();
-
- if (pb) { /* We are readable */
- // TODO revisit: bool is_lossy;
- Glib::ustring mime_type, ext;
- Glib::ustring u = uri;
- std::size_t dotpos = u.rfind('.');
- if (dotpos != Glib::ustring::npos) {
- ext = u.substr(dotpos, Glib::ustring::npos);
- }
+ gchar *pixpath = NULL;
+ time_t dummy;
+ GdkPixbuf *pb = Inkscape::IO::pixbuf_new_from_file(uri, dummy, pixpath);
- // HACK: replace with something better based on GIO
- if (!ext.empty() && lossy.find(ext) != lossy.end()) {
- // TODO revisit: is_lossy = true;
- mime_type = "image/jpeg";
- } else {
- // TODO revisit: is_lossy = false;
- mime_type = "image/png";
- }
+ // TODO: the pixbuf is created again from the base64-encoded attribute in SPImage.
+ // Find a way to create the pixbuf only once.
+ if (pb) {
doc = SPDocument::createNewDoc(NULL, TRUE, TRUE);
bool saved = DocumentUndo::getUndoSensitive(doc);
DocumentUndo::setUndoSensitive(doc, false); // no need to undo in this temporary document
@@ -85,40 +67,22 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
double xscale = 1;
double yscale = 1;
- gchar const *str = gdk_pixbuf_get_option( pb, "Inkscape::DpiX" );
- if ( str ) {
- gint dpi = atoi(str);
- if ( dpi > 0 && dpi != 72 ) {
- xscale = 72.0 / (double)dpi;
- }
- } else {
- if (!ir && !forcexdpi)
- ir = new ImageResolution(uri);
- if (ir && ir->ok())
- xscale = 900.0 / floor(10.*ir->x() + .5); // round-off to 0.1 dpi
- else
- xscale = 90.0 / defaultxdpi;
- }
- width *= xscale;
- str = gdk_pixbuf_get_option( pb, "Inkscape::DpiY" );
- if ( str ) {
- gint dpi = atoi(str);
- if ( dpi > 0 && dpi != 72 ) {
- yscale = 72.0 / (double)dpi;
- }
+ if (!ir && !forcexdpi) {
+ ir = new ImageResolution(uri);
+ }
+ if (ir && ir->ok()) {
+ xscale = 900.0 / floor(10.*ir->x() + .5); // round-off to 0.1 dpi
+ yscale = 900.0 / floor(10.*ir->y() + .5);
} else {
- if (!ir && !forcexdpi)
- ir = new ImageResolution(uri);
- if (ir && ir->ok())
- yscale = 900.0 / floor(10.*ir->y() + .5); // round-off to 0.1 dpi
- else
- yscale = 90.0 / defaultxdpi;
+ xscale = 90.0 / defaultxdpi;
+ yscale = 90.0 / defaultxdpi;
}
+
+ width *= xscale;
height *= yscale;
- if (ir)
- delete ir;
+ delete ir; // deleting NULL is safe
// Create image node
Inkscape::XML::Document *xml_doc = doc->getReprDoc();
@@ -127,7 +91,7 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
sp_repr_set_svg_double(image_node, "height", height);
if (embed) {
- sp_embed_image(image_node, pb, mime_type);
+ sp_embed_image(image_node, pb);
} else {
// convert filename to uri
gchar* _uri = g_filename_to_uri(uri, NULL, NULL);
@@ -139,6 +103,7 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri)
}
}
+ g_object_set_data(G_OBJECT(pb), "cairo_surface", NULL);
g_object_unref(pb);
// Add it to the current layer
diff --git a/src/extension/internal/image-resolution.cpp b/src/extension/internal/image-resolution.cpp
index 3c254a59c..a9d33e831 100644
--- a/src/extension/internal/image-resolution.cpp
+++ b/src/extension/internal/image-resolution.cpp
@@ -14,13 +14,21 @@
#include "image-resolution.h"
#define IR_TRY_PNG 1
+#include <png.h>
+
#ifdef HAVE_EXIF
-#define IR_TRY_EXIF 1
+#include <math.h>
+#include <libexif/exif-data.h>
#endif
+
#define IR_TRY_EXIV 0
+
#ifdef HAVE_JPEG
#define IR_TRY_JFIF 1
+#include <jpeglib.h>
+#include <setjmp.h>
#endif
+
#ifdef WITH_IMAGE_MAGICK
#include <Magick++.h>
#endif
@@ -62,8 +70,6 @@ double ImageResolution::y() const {
#if IR_TRY_PNG
-
-#include <png.h>
static bool haspngheader(FILE *fp) {
unsigned char header[8];
@@ -133,9 +139,6 @@ void ImageResolution::readpng(char const *) {
#if IR_TRY_EXIF
-#include <math.h>
-#include <libexif/exif-data.h>
-
static double exifDouble(ExifEntry *entry, ExifByteOrder byte_order) {
switch (entry->format) {
case EXIF_FORMAT_BYTE: {
@@ -264,9 +267,6 @@ void ImageResolution::readexiv(char const *) {
#if IR_TRY_JFIF
-#include <jpeglib.h>
-#include <setjmp.h>
-
static void irjfif_error_exit(j_common_ptr cinfo) {
longjmp(*(jmp_buf*)cinfo->client_data, 1);
}
diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp
index 5976555f4..868f5a35c 100644
--- a/src/selection-chemistry.cpp
+++ b/src/selection-chemistry.cpp
@@ -3457,6 +3457,7 @@ void sp_selection_create_bitmap_copy(SPDesktop *desktop)
}
t = Geom::Scale(1, -1) * Geom::Translate(shift_x, shift_y) * eek.inverse(); /// @fixme hardcoded doc2dt transform?
+ // TODO: avoid roundtrip via file
// Do the export
sp_export_png_file(document, filepath,
bbox->min()[Geom::X], bbox->min()[Geom::Y],
@@ -3483,7 +3484,7 @@ void sp_selection_create_bitmap_copy(SPDesktop *desktop)
if (pb) {
// Create the repr for the image
Inkscape::XML::Node * repr = xml_doc->createElement("svg:image");
- sp_embed_image(repr, pb, "image/png");
+ sp_embed_image(repr, pb);
if (res == Inkscape::Util::Quantity::convert(1, "in", "px")) { // for default 90 dpi, snap it to pixel grid
sp_repr_set_svg_double(repr, "width", width);
sp_repr_set_svg_double(repr, "height", height);
diff --git a/src/sp-image.cpp b/src/sp-image.cpp
index a082e2802..724fa0ad8 100644
--- a/src/sp-image.cpp
+++ b/src/sp-image.cpp
@@ -96,12 +96,7 @@ static void sp_image_update_arenaitem (SPImage *img, Inkscape::DrawingImage *ai)
static void sp_image_update_canvas_image (SPImage *image);
static GdkPixbuf * sp_image_repr_read_dataURI (const gchar * uri_data);
static GdkPixbuf * sp_image_repr_read_b64 (const gchar * uri_data);
-
-extern "C"
-{
- void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length );
-}
-
+static void pixbuf_set_mime_data(GdkPixbuf *pb, guchar *data, gsize len, GdkPixbufFormat *fmt);
#ifdef DEBUG_LCMS
extern guint update_in_progress;
@@ -138,286 +133,15 @@ extern guint update_in_progress;
namespace Inkscape {
namespace IO {
-class PushPull
-{
-public:
- gboolean first;
- FILE* fp;
- guchar* scratch;
- gsize size;
- gsize used;
- gsize offset;
- GdkPixbufLoader *loader;
-
- PushPull() : first(TRUE),
- fp(0),
- scratch(0),
- size(0),
- used(0),
- offset(0),
- loader(0) {};
-
- gboolean readMore()
- {
- gboolean good = FALSE;
- if ( offset )
- {
- g_memmove( scratch, scratch + offset, used - offset );
- used -= offset;
- offset = 0;
- }
- if ( used < size )
- {
- gsize space = size - used;
- gsize got = fread( scratch + used, 1, space, fp );
- if ( got )
- {
- if ( loader )
- {
- GError *err = NULL;
- //g_message( " __read %d bytes", (int)got );
- if ( !gdk_pixbuf_loader_write( loader, scratch + used, got, &err ) )
- {
- //g_message("_error writing pixbuf data");
- }
- }
-
- used += got;
- good = TRUE;
- }
- else
- {
- good = FALSE;
- }
- }
- return good;
- }
-
- gsize available() const
- {
- return (used - offset);
- }
-
- gsize readOut( gpointer data, gsize length )
- {
- gsize giving = available();
- if ( length < giving )
- {
- giving = length;
- }
- g_memmove( data, scratch + offset, giving );
- offset += giving;
- if ( offset >= used )
- {
- offset = 0;
- used = 0;
- }
- return giving;
- }
-
- void clear()
- {
- offset = 0;
- used = 0;
- }
-
-private:
- PushPull& operator = (const PushPull& other);
- PushPull(const PushPull& other);
-};
-
-static void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length )
-{
-// g_message( "user_read_data(%d)", length );
-
- PushPull* youme = (PushPull*)png_get_io_ptr(png_ptr);
-
- gsize filled = 0;
- gboolean canRead = TRUE;
-
- while ( filled < length && canRead )
- {
- gsize some = youme->readOut( data + filled, length - filled );
- filled += some;
- if ( filled < length )
- {
- canRead &= youme->readMore();
- }
- }
-// g_message("things out");
-}
-
-
-static bool readPngAndHeaders( PushPull &youme, gint & dpiX, gint & dpiY )
-{
- bool good = true;
-
- gboolean isPng = !png_sig_cmp( youme.scratch + youme.offset, 0, youme.available() );
- //g_message( " png? %s", (isPng ? "Yes":"No") );
- if ( isPng ) {
- png_structp pngPtr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
- 0, //(png_voidp)user_error_ptr,
- 0, //user_error_fn,
- 0 //user_warning_fn
- );
- png_infop infoPtr = pngPtr ? png_create_info_struct( pngPtr ) : 0;
-
- if ( pngPtr && infoPtr ) {
- if ( setjmp(png_jmpbuf(pngPtr)) ) {
- // libpng calls longjmp to return here if an error occurs.
- good = false;
- }
-
- if (good) {
- png_set_read_fn( pngPtr, &youme, user_read_data );
- //g_message( "In" );
-
- //png_read_info( pngPtr, infoPtr );
- png_read_png( pngPtr, infoPtr, PNG_TRANSFORM_IDENTITY, 0 );
-
- //g_message("out");
-
- /*
- if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_pHYs ) )
- {
- g_message("pHYs chunk now valid" );
- }
- if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_sCAL ) )
- {
- g_message("sCAL chunk now valid" );
- }
- */
-
- png_uint_32 res_x = 0;
- png_uint_32 res_y = 0;
- int unit_type = 0;
- if ( png_get_pHYs( pngPtr, infoPtr, &res_x, &res_y, &unit_type) ) {
-// g_message( "pHYs yes (%d, %d) %d (%s)", (int)res_x, (int)res_y, unit_type,
-// (unit_type == 1? "per meter" : "unknown")
-// );
-
-// g_message( " dpi: (%d, %d)",
-// (int)(0.5 + ((double)res_x)/39.37),
-// (int)(0.5 + ((double)res_y)/39.37) );
- if ( unit_type == PNG_RESOLUTION_METER )
- {
- // TODO come up with a more accurate DPI setting
- dpiX = (int)(0.5 + ((double)res_x)/39.37);
- dpiY = (int)(0.5 + ((double)res_y)/39.37);
- }
- } else {
-// g_message( "pHYs no" );
- }
-
-/*
- double width = 0;
- double height = 0;
- int unit = 0;
- if ( png_get_sCAL(pngPtr, infoPtr, &unit, &width, &height) )
- {
- gchar* vals[] = {
- "unknown", // PNG_SCALE_UNKNOWN
- "meter", // PNG_SCALE_METER
- "radian", // PNG_SCALE_RADIAN
- "last", //
- NULL
- };
-
- g_message( "sCAL: (%f, %f) %d (%s)",
- width, height, unit,
- ((unit >= 0 && unit < 3) ? vals[unit]:"???")
- );
- }
-*/
-
-#if defined(PNG_sRGB_SUPPORTED)
- {
- int intent = 0;
- if ( png_get_sRGB(pngPtr, infoPtr, &intent) ) {
-// g_message("Found an sRGB png chunk");
- }
- }
-#endif // defined(PNG_sRGB_SUPPORTED)
-
-#if defined(PNG_cHRM_SUPPORTED)
- {
- double white_x = 0;
- double white_y = 0;
- double red_x = 0;
- double red_y = 0;
- double green_x = 0;
- double green_y = 0;
- double blue_x = 0;
- double blue_y = 0;
-
- if ( png_get_cHRM(pngPtr, infoPtr,
- &white_x, &white_y,
- &red_x, &red_y,
- &green_x, &green_y,
- &blue_x, &blue_y) ) {
-// g_message("Found a cHRM png chunk");
- }
- }
-#endif // defined(PNG_cHRM_SUPPORTED)
-
-#if defined(PNG_gAMA_SUPPORTED)
- {
- double file_gamma = 0;
- if ( png_get_gAMA(pngPtr, infoPtr, &file_gamma) ) {
-// g_message("Found a gAMA png chunk");
- }
- }
-#endif // defined(PNG_gAMA_SUPPORTED)
-
-#if defined(PNG_iCCP_SUPPORTED)
- {
- png_charp name = 0;
- int compression_type = 0;
-#if (PNG_LIBPNG_VER < 10500)
- png_charp profile = 0;
-#else
- png_bytep profile = 0;
-#endif
- png_uint_32 proflen = 0;
- if ( png_get_iCCP(pngPtr, infoPtr, &name, &compression_type, &profile, &proflen) ) {
-// g_message("Found an iCCP chunk named [%s] with %d bytes and comp %d", name, proflen, compression_type);
- }
- }
-#endif // defined(PNG_iCCP_SUPPORTED)
-
- }
- } else {
- g_message("Error when creating PNG read struct");
- }
-
- // now clean it up.
- if (pngPtr && infoPtr) {
- png_destroy_read_struct( &pngPtr, &infoPtr, 0 );
- pngPtr = 0;
- infoPtr = 0;
- } else if (pngPtr) {
- png_destroy_read_struct( &pngPtr, 0, 0 );
- pngPtr = 0;
- }
- } else {
- good = false; // Was not a png file
- }
-
- return good;
-}
-
-GdkPixbuf* pixbuf_new_from_file( const char *filename, time_t &modTime, gchar*& pixPath, GError **/*error*/ )
+GdkPixbuf* pixbuf_new_from_file(const char *filename, time_t &modTime, gchar*& pixPath)
{
GdkPixbuf* buf = NULL;
- PushPull youme;
- gint dpiX = 0;
- gint dpiY = 0;
modTime = 0;
if ( pixPath ) {
g_free(pixPath);
pixPath = NULL;
}
-
+
//test correctness of filename
if (!g_file_test (filename, G_FILE_TEST_EXISTS)){
return NULL;
@@ -425,95 +149,43 @@ GdkPixbuf* pixbuf_new_from_file( const char *filename, time_t &modTime, gchar*&
struct stat stdir;
int val = g_stat(filename, &stdir);
if (stdir.st_mode & S_IFDIR){
- //filename is not correct: it is a directory name and hence further code can not return valid results
+ g_warning("Linked image file %s is a directory", filename);
return NULL;
}
- dump_fopen_call( filename, "pixbuf_new_from_file" );
- FILE* fp = fopen_utf8name( filename, "r" );
- if ( fp )
- {
- {
- // struct stat st;
- // memset(&st, 0, sizeof(st));
- // int val = g_stat(filename, &st);
- if ( !val ) {
- modTime = stdir.st_mtime;//st.st_mtime;
- pixPath = g_strdup(filename);
- }
+ // we need to load the entire pixbuf into memory
+ gchar *data = NULL;
+ gsize len = 0;
+
+ if (g_file_get_contents(filename, &data, &len, NULL)) {
+ if (!val) {
+ modTime = stdir.st_mtime;
+ pixPath = g_strdup(filename);
}
GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
- if ( loader )
- {
- GError *err = NULL;
-
- // short buffer
- guchar scratch[1024];
- gboolean latter = FALSE;
-
- youme.fp = fp;
- youme.scratch = scratch;
- youme.size = sizeof(scratch);
- youme.used = 0;
- youme.offset = 0;
- youme.loader = loader;
-
- while ( !feof(fp) )
- {
- if ( youme.readMore() ) {
- if ( youme.first ) {
- //g_message( "First data chunk" );
- youme.first = FALSE;
- if (readPngAndHeaders(youme, dpiX, dpiY))
- {
- // TODO set the dpi to be read elsewhere
- }
- } else if ( !latter ) {
- latter = TRUE;
- }
- // Now clear out the buffer so we can read more.
- // (dumping out unused)
- youme.clear();
- }
- }
-
- gboolean ok = gdk_pixbuf_loader_close(loader, &err);
- if ( ok ) {
- buf = gdk_pixbuf_loader_get_pixbuf( loader );
- if ( buf ) {
- g_object_ref(buf);
- }
- } else {
- // do something
- g_message("error loading pixbuf at close");
- }
-
- g_object_unref(loader);
+ gdk_pixbuf_loader_write(loader, (guchar *) data, len, NULL);
+ gdk_pixbuf_loader_close(loader, NULL);
+
+ buf = gdk_pixbuf_loader_get_pixbuf(loader);
+ if (buf) {
+ g_object_ref(buf);
+ buf = sp_image_pixbuf_force_rgba(buf);
+ pixbuf_set_mime_data(buf, (guchar *) data, len, gdk_pixbuf_loader_get_format(loader));
} else {
- g_message("error when creating pixbuf loader");
+ g_free(data);
+ g_warning("Error loading pixbuf");
}
- fclose( fp );
- fp = 0;
+
+ // TODO: we could also read DPI, ICC profile, gamma correction, and other information
+ // from the file. This can be done by using format-specific libraries e.g. libpng.
} else {
- g_warning ("Unable to open linked file: %s", filename);
+ g_warning("Unable to open linked file: %s", filename);
}
return buf;
}
-GdkPixbuf* pixbuf_new_from_file( const char *filename, GError **error )
-{
- time_t modTime = 0;
- gchar* pixPath = 0;
- GdkPixbuf* result = pixbuf_new_from_file( filename, modTime, pixPath, error );
- if (pixPath) {
- g_free(pixPath);
- }
- return result;
-}
-
-
}
}
@@ -594,6 +266,7 @@ static void sp_image_release( SPObject *object )
}
if (image->pixbuf) {
+ g_object_set_data(G_OBJECT(image->pixbuf), "cairo_surface", NULL);
g_object_unref (image->pixbuf);
image->pixbuf = NULL;
}
@@ -775,7 +448,6 @@ static void sp_image_update( SPObject *object, SPCtx *ctx, unsigned int flags )
object->getRepr()->attribute("sodipodi:absref"),
doc->getBase());
if (pixbuf) {
- pixbuf = sp_image_pixbuf_force_rgba (pixbuf);
// BLIP
#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
if ( image->color_profile )
@@ -843,8 +515,6 @@ static void sp_image_update( SPObject *object, SPCtx *ctx, unsigned int flags )
#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
image->pixbuf = pixbuf;
- // used here for the side-effect of converting to ARGB32
- ink_cairo_surface_get_for_pixbuf(image->pixbuf);
}
}
}
@@ -1103,13 +773,14 @@ GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gcha
pixPath = 0;
}
- const gchar *filename = href;
+ gchar const *filename = href;
+
if (filename != NULL) {
if (strncmp (filename,"file:",5) == 0) {
gchar *fullname = g_filename_from_uri(filename, NULL, NULL);
if (fullname) {
- // TODO check this. Was doing a UTF-8 to filename conversion here.
- pixbuf = Inkscape::IO::pixbuf_new_from_file (fullname, modTime, pixPath, NULL);
+ pixbuf = Inkscape::IO::pixbuf_new_from_file(fullname, modTime, pixPath);
+ g_free(fullname);
if (pixbuf != NULL) {
return pixbuf;
}
@@ -1135,7 +806,7 @@ GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gcha
// different dir) or unset (when doc is not saved yet), so we check for base+href existence first,
// and if it fails, we also try to use bare href regardless of its g_path_is_absolute
if (g_file_test (fullname, G_FILE_TEST_EXISTS) && !g_file_test (fullname, G_FILE_TEST_IS_DIR)) {
- pixbuf = Inkscape::IO::pixbuf_new_from_file( fullname, modTime, pixPath, NULL );
+ pixbuf = Inkscape::IO::pixbuf_new_from_file(fullname, modTime, pixPath);
g_free (fullname);
if (pixbuf != NULL) {
return pixbuf;
@@ -1145,7 +816,7 @@ GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gcha
/* try filename as absolute */
if (g_file_test (filename, G_FILE_TEST_EXISTS) && !g_file_test (filename, G_FILE_TEST_IS_DIR)) {
- pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, modTime, pixPath, NULL );
+ pixbuf = Inkscape::IO::pixbuf_new_from_file(filename, modTime, pixPath);
if (pixbuf != NULL) {
return pixbuf;
}
@@ -1163,13 +834,13 @@ GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gcha
g_warning ("xlink:href did not resolve to a valid image file, now trying sodipodi:absref=\"%s\"", absref);
}
- pixbuf = Inkscape::IO::pixbuf_new_from_file( filename, modTime, pixPath, NULL );
+ pixbuf = Inkscape::IO::pixbuf_new_from_file(filename, modTime, pixPath);
if (pixbuf != NULL) {
return pixbuf;
}
}
/* Nope: We do not find any valid pixmap file :-( */
- pixbuf = gdk_pixbuf_new_from_xpm_data ((const gchar **) brokenimage_xpm);
+ pixbuf = gdk_pixbuf_new_from_xpm_data((const gchar **) brokenimage_xpm);
/* It should be included xpm, so if it still does not does load, */
/* our libraries are broken */
@@ -1342,82 +1013,57 @@ static GdkPixbuf *sp_image_repr_read_dataURI( const gchar * uri_data )
return pixbuf;
}
-static GdkPixbuf *sp_image_repr_read_b64( const gchar * uri_data )
+static GdkPixbuf *sp_image_repr_read_b64(gchar const *uri_data)
{
- GdkPixbuf * pixbuf = NULL;
-
- static const gchar B64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
+ GdkPixbuf *pixbuf = NULL;
GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
- if (loader) {
- bool eos = false;
- bool failed = false;
- const gchar* btr = uri_data;
- gchar ud[4];
- guchar bd[57];
-
- while (!eos) {
- gint ell = 0;
- for (gint j = 0; j < 19; j++) {
- gint len = 0;
- for (gint k = 0; k < 4; k++) {
- while (isspace ((int) (*btr))) {
- if ((*btr) == '\0') break;
- btr++;
- }
- if (eos) {
- ud[k] = 0;
- continue;
- }
- if (((*btr) == '\0') || ((*btr) == '=')) {
- eos = true;
- ud[k] = 0;
- continue;
- }
- ud[k] = 64;
- for (gint b = 0; b < 64; b++) { /* There must a faster way to do this... ?? */
- if (B64[b] == (*btr)) {
- ud[k] = (gchar) b;
- break;
- }
- }
- if (ud[k] == 64) { /* data corruption ?? */
- eos = true;
- ud[k] = 0;
- continue;
- }
- btr++;
- len++;
- }
- guint32 bits = (guint32) ud[0];
- bits = (bits << 6) | (guint32) ud[1];
- bits = (bits << 6) | (guint32) ud[2];
- bits = (bits << 6) | (guint32) ud[3];
- bd[ell++] = (guchar) ((bits & 0xff0000) >> 16);
- if (len > 2) {
- bd[ell++] = (guchar) ((bits & 0xff00) >> 8);
- }
- if (len > 3) {
- bd[ell++] = (guchar) (bits & 0xff);
- }
- }
- if (!gdk_pixbuf_loader_write (loader, (const guchar *) bd, (size_t) ell, NULL)) {
- failed = true;
- break;
- }
- }
+ if (!loader) return NULL;
- gdk_pixbuf_loader_close (loader, NULL);
+ gsize decoded_len = 0;
+ guchar *decoded = g_base64_decode(uri_data, &decoded_len);
- if (!failed) {
- pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
- }
+ if (gdk_pixbuf_loader_write(loader, decoded, decoded_len, NULL)) {
+ gdk_pixbuf_loader_close(loader, NULL);
+ pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
+ g_object_ref(pixbuf);
+ pixbuf = sp_image_pixbuf_force_rgba(pixbuf);
+ pixbuf_set_mime_data(pixbuf, decoded, decoded_len, gdk_pixbuf_loader_get_format(loader));
+ } else {
+ g_free(decoded);
}
+ g_object_unref(loader);
return pixbuf;
}
+// takes ownership of passed data
+static void pixbuf_set_mime_data(GdkPixbuf *pb, guchar *data, gsize len, GdkPixbufFormat *fmt)
+{
+ cairo_surface_t *s = ink_cairo_surface_get_for_pixbuf(pb);
+
+ gchar const *mimetype = NULL;
+ gchar *fmt_name = gdk_pixbuf_format_get_name(fmt);
+ Glib::ustring name = fmt_name;
+ g_free(fmt_name);
+
+ if (name == "jpeg") {
+ mimetype = CAIRO_MIME_TYPE_JPEG;
+ } else if (name == "jpeg2000") {
+ mimetype = CAIRO_MIME_TYPE_JP2;
+ } else if (name == "png") {
+ mimetype = CAIRO_MIME_TYPE_PNG;
+ }
+
+ if (mimetype != NULL) {
+ cairo_surface_set_mime_data(s, mimetype, data, len, g_free, data);
+ //g_message("Setting Cairo MIME data: %s", mimetype);
+ } else {
+ g_free(data);
+ //g_message("Not setting Cairo MIME data: unknown format %s", name.c_str());
+ }
+}
+
static void sp_image_set_curve( SPImage *image )
{
//create a curve at the image's boundary for snapping
@@ -1453,41 +1099,65 @@ SPCurve *sp_image_get_curve( SPImage *image )
return result;
}
-void sp_embed_image( Inkscape::XML::Node *image_node, GdkPixbuf *pb, Glib::ustring const &mime_in )
+void sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb)
{
- Glib::ustring format, mime;
- if (mime_in == "image/jpeg") {
- mime = mime_in;
- format = "jpeg";
- } else {
- mime = "image/png";
- format = "png";
+ static gchar const *mimetypes[] = {
+ CAIRO_MIME_TYPE_JPEG, CAIRO_MIME_TYPE_JP2, CAIRO_MIME_TYPE_PNG, NULL };
+ static guint mimetypes_len = g_strv_length(const_cast<gchar**>(mimetypes));
+
+ bool free_data = false;
+
+ // check whether the pixbuf has MIME data
+ guchar *data = NULL;
+ gsize len = 0;
+ gchar const *data_mimetype = NULL;
+
+ cairo_surface_t *s = reinterpret_cast<cairo_surface_t*>(g_object_get_data(G_OBJECT(pb), "cairo_surface"));
+ if (s) {
+ for (guint i = 0; i < mimetypes_len; ++i) {
+ cairo_surface_get_mime_data(s, mimetypes[i], const_cast<unsigned char const **>(&data), &len);
+ if (data != NULL) {
+ data_mimetype = mimetypes[i];
+ break;
+ }
+ }
}
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- Glib::ustring quality = Glib::ustring::format(prefs->getInt("/dialogs/import/quality", 100));
+ if (data == NULL) {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring quality = Glib::ustring::format(prefs->getInt("/dialogs/import/quality", 100));
- gchar *data = 0;
- gsize length = 0;
- gdk_pixbuf_save_to_buffer(pb, &data, &length, format.data(), NULL, "quality", quality.c_str(), NULL);
+ // if there is no supported MIME data, embed as PNG
+ data_mimetype = "image/png";
+ ink_pixbuf_ensure_normal(pb);
+ gdk_pixbuf_save_to_buffer(pb, reinterpret_cast<gchar**>(&data), &len, "png", NULL,
+ "quality", quality.c_str(), NULL);
+ free_data = true;
+ }
// Save base64 encoded data in image node
// this formula taken from Glib docs
- guint needed_size = length * 4 / 3 + length * 4 / (3 * 72) + 7;
- needed_size += 5 + 8 + mime.size(); // 5 bytes for data:, 8 for ;base64,
+ guint needed_size = len * 4 / 3 + len * 4 / (3 * 72) + 7;
+ needed_size += 5 + 8 + strlen(data_mimetype); // 5 bytes for data: + 8 for ;base64,
- gchar *buffer = (gchar *) g_malloc(needed_size), *buf_work = buffer;
- buf_work += g_sprintf(buffer, "data:%s;base64,", mime.data());
+ gchar *buffer = (gchar *) g_malloc(needed_size);
+ gchar *buf_work = buffer;
+ buf_work += g_sprintf(buffer, "data:%s;base64,", data_mimetype);
gint state = 0;
gint save = 0;
gsize written = 0;
- written += g_base64_encode_step((guchar*) data, length, TRUE, buf_work, &state, &save);
+ written += g_base64_encode_step(data, len, TRUE, buf_work, &state, &save);
written += g_base64_encode_close(TRUE, buf_work + written, &state, &save);
buf_work[written] = 0; // null terminate
+ // TODO: this is very wasteful memory-wise.
+ // It would be better to only keep the binary data around,
+ // and base64 encode on the fly when saving the XML.
image_node->setAttribute("xlink:href", buffer);
+
g_free(buffer);
+ if (free_data) g_free(data);
}
void sp_image_refresh_if_outdated( SPImage* image )
diff --git a/src/sp-image.h b/src/sp-image.h
index d6fc82a59..c197f6473 100644
--- a/src/sp-image.h
+++ b/src/sp-image.h
@@ -66,7 +66,7 @@ GType sp_image_get_type (void);
/* Return duplicate of curve or NULL */
SPCurve *sp_image_get_curve (SPImage *image);
-void sp_embed_image(Inkscape::XML::Node *imgnode, GdkPixbuf *pb, Glib::ustring const &mime);
+void sp_embed_image(Inkscape::XML::Node *imgnode, GdkPixbuf *pb);
void sp_image_refresh_if_outdated( SPImage* image );
#endif