diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2013-09-19 00:57:10 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2013-09-19 00:57:10 +0000 |
| commit | 083367b313247a4cf0c082fff25e993892dc38d1 (patch) | |
| tree | 35e5da339ef5d30a333270d644ac1d61e6ed6620 | |
| parent | Merge in patch for Jabiertxo Arraiza Cenoz in bug lp:1127103 (diff) | |
| download | inkscape-083367b313247a4cf0c082fff25e993892dc38d1.tar.gz inkscape-083367b313247a4cf0c082fff25e993892dc38d1.zip | |
Encapsulate the shared memory hack for Cairo and GdkPixbuf in a class
called Inkscape::Pixbuf. Replace usage in the code as appropriate.
(bzr r12531)
| -rw-r--r-- | src/display/cairo-utils.cpp | 539 | ||||
| -rw-r--r-- | src/display/cairo-utils.h | 70 | ||||
| -rw-r--r-- | src/display/drawing-image.cpp | 64 | ||||
| -rw-r--r-- | src/display/drawing-image.h | 6 | ||||
| -rw-r--r-- | src/display/nr-filter-image.cpp | 69 | ||||
| -rw-r--r-- | src/display/nr-filter-image.h | 6 | ||||
| -rw-r--r-- | src/extension/internal/cairo-render-context.cpp | 8 | ||||
| -rw-r--r-- | src/extension/internal/cairo-render-context.h | 6 | ||||
| -rw-r--r-- | src/extension/internal/cairo-renderer.cpp | 26 | ||||
| -rw-r--r-- | src/extension/internal/emf-print.cpp | 9 | ||||
| -rw-r--r-- | src/extension/internal/gdkpixbuf-input.cpp | 20 | ||||
| -rw-r--r-- | src/extension/internal/metafile-print.cpp | 2 | ||||
| -rw-r--r-- | src/extension/internal/metafile-print.h | 4 | ||||
| -rw-r--r-- | src/extension/internal/wmf-print.cpp | 13 | ||||
| -rw-r--r-- | src/filters/image.cpp | 2 | ||||
| -rw-r--r-- | src/helper/pixbuf-ops.cpp | 19 | ||||
| -rw-r--r-- | src/helper/pixbuf-ops.h | 3 | ||||
| -rw-r--r-- | src/selection-chemistry.cpp | 4 | ||||
| -rw-r--r-- | src/sp-image.cpp | 343 | ||||
| -rw-r--r-- | src/sp-image.h | 30 | ||||
| -rw-r--r-- | src/trace/trace.cpp | 25 |
21 files changed, 689 insertions, 579 deletions
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 755553033..2c7b543c1 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -15,6 +15,8 @@ #include "display/cairo-utils.h" #include <stdexcept> +#include <glib/gstdio.h> +#include <glibmm/fileutils.h> #include <2geom/pathvector.h> #include <2geom/bezier-curve.h> #include <2geom/hvlinesegment.h> @@ -28,6 +30,8 @@ #include "helper/geom-curves.h" #include "display/cairo-templates.h" +static void ink_cairo_pixbuf_cleanup(guchar *, void *); + /** * Key for cairo_surface_t to keep track of current color interpolation value * Only the address of the structure is used, it is never initialized. See: @@ -116,6 +120,380 @@ Cairo::RefPtr<CairoContext> CairoContext::create(Cairo::RefPtr<Cairo::Surface> c return ret; } + +/* The class below implement the following hack: + * + * The pixels formats of Cairo and GdkPixbuf are different. + * GdkPixbuf accesses pixels as bytes, alpha is not premultiplied, + * and successive bytes of a single pixel contain R, G, B and A components. + * Cairo accesses pixels as 32-bit ints, alpha is premultiplied, + * and each int contains as 0xAARRGGBB, accessed with bitwise operations. + * + * In other words, on a little endian system, a GdkPixbuf will contain: + * char *data = "rgbargbargba...." + * int *data = { 0xAABBGGRR, 0xAABBGGRR, 0xAABBGGRR, ... } + * while a Cairo image surface will contain: + * char *data = "bgrabgrabgra...." + * int *data = { 0xAARRGGBB, 0xAARRGGBB, 0xAARRGGBB, ... } + * + * It is possible to convert between these two formats (almost) losslessly. + * Some color information from partially transparent regions of the image + * is lost, but the result when displaying this image will remain the same. + * + * The class allows interoperation between GdkPixbuf + * and Cairo surfaces without creating a copy of the image. + * This is implemented by creating a GdkPixbuf and a Cairo image surface + * which share their data. Depending on what is needed at a given time, + * the pixels are converted in place to the Cairo or the GdkPixbuf format. + */ + +/** Create a pixbuf from a Cairo surface. + * The constructor takes ownership of the passed surface, + * so it should not be destroyed. */ +Pixbuf::Pixbuf(cairo_surface_t *s) + : _pixbuf(gdk_pixbuf_new_from_data( + cairo_image_surface_get_data(s), GDK_COLORSPACE_RGB, TRUE, 8, + cairo_image_surface_get_width(s), cairo_image_surface_get_height(s), + cairo_image_surface_get_stride(s), NULL, NULL)) + , _surface(s) + , _mod_time(0) + , _pixel_format(PF_CAIRO) + , _cairo_store(true) +{} + +/** Create a pixbuf from a GdkPixbuf. + * The constructor takes ownership of the passed GdkPixbuf reference, + * so it should not be unrefed. */ +Pixbuf::Pixbuf(GdkPixbuf *pb) + : _pixbuf(pb) + , _surface(0) + , _mod_time(0) + , _pixel_format(PF_GDK) + , _cairo_store(false) +{ + _forceAlpha(); + _surface = cairo_image_surface_create_for_data( + gdk_pixbuf_get_pixels(_pixbuf), CAIRO_FORMAT_ARGB32, + gdk_pixbuf_get_width(_pixbuf), gdk_pixbuf_get_height(_pixbuf), gdk_pixbuf_get_rowstride(_pixbuf)); +} + +Pixbuf::Pixbuf(Inkscape::Pixbuf const &other) + : _pixbuf(gdk_pixbuf_copy(other._pixbuf)) + , _surface(cairo_image_surface_create_for_data( + gdk_pixbuf_get_pixels(_pixbuf), CAIRO_FORMAT_ARGB32, + gdk_pixbuf_get_width(_pixbuf), gdk_pixbuf_get_height(_pixbuf), gdk_pixbuf_get_rowstride(_pixbuf))) + , _mod_time(other._mod_time) + , _path(other._path) + , _pixel_format(other._pixel_format) + , _cairo_store(false) +{} + +Pixbuf::~Pixbuf() +{ + if (_cairo_store) { + g_object_unref(_pixbuf); + cairo_surface_destroy(_surface); + } else { + cairo_surface_destroy(_surface); + g_object_unref(_pixbuf); + } +} + +Pixbuf *Pixbuf::create_from_data_uri(gchar const *uri_data) +{ + Pixbuf *pixbuf = NULL; + + bool data_is_image = false; + bool data_is_base64 = false; + + gchar const *data = uri_data; + + while (*data) { + if (strncmp(data,"base64",6) == 0) { + /* base64-encoding */ + data_is_base64 = true; + data_is_image = true; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what + data += 6; + } + else if (strncmp(data,"image/png",9) == 0) { + /* PNG image */ + data_is_image = true; + data += 9; + } + else if (strncmp(data,"image/jpg",9) == 0) { + /* JPEG image */ + data_is_image = true; + data += 9; + } + else if (strncmp(data,"image/jpeg",10) == 0) { + /* JPEG image */ + data_is_image = true; + data += 10; + } + else if (strncmp(data,"image/jp2",9) == 0) { + /* JPEG2000 image */ + data_is_image = true; + data += 9; + } + else { /* unrecognized option; skip it */ + while (*data) { + if (((*data) == ';') || ((*data) == ',')) { + break; + } + data++; + } + } + if ((*data) == ';') { + data++; + continue; + } + if ((*data) == ',') { + data++; + break; + } + } + + if ((*data) && data_is_image && data_is_base64) { + GdkPixbuf *buf = NULL; + GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); + + if (!loader) return NULL; + + gsize decoded_len = 0; + guchar *decoded = g_base64_decode(data, &decoded_len); + + if (gdk_pixbuf_loader_write(loader, decoded, decoded_len, NULL)) { + gdk_pixbuf_loader_close(loader, NULL); + buf = gdk_pixbuf_loader_get_pixbuf(loader); + if (buf) { + g_object_ref(buf); + pixbuf = new Pixbuf(buf); + + GdkPixbufFormat *fmt = gdk_pixbuf_loader_get_format(loader); + gchar *fmt_name = gdk_pixbuf_format_get_name(fmt); + pixbuf->_setMimeData(decoded, decoded_len, fmt_name); + g_free(fmt_name); + } else { + g_free(decoded); + } + } else { + g_free(decoded); + } + g_object_unref(loader); + } + + return pixbuf; +} + +Pixbuf *Pixbuf::create_from_file(std::string const &fn) +{ + Pixbuf *pb = NULL; + // test correctness of filename + if (!g_file_test(fn.c_str(), G_FILE_TEST_EXISTS)) { + return NULL; + } + struct stat stdir; + int val = g_stat(fn.c_str(), &stdir); + if (val == 0 && stdir.st_mode & S_IFDIR){ + return NULL; + } + + // we need to load the entire file into memory, + // since we'll store it as MIME data + gchar *data = NULL; + gsize len = 0; + GError *error; + + if (g_file_get_contents(fn.c_str(), &data, &len, &error)) { + + GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); + gdk_pixbuf_loader_write(loader, (guchar *) data, len, NULL); + gdk_pixbuf_loader_close(loader, NULL); + + GdkPixbuf *buf = gdk_pixbuf_loader_get_pixbuf(loader); + if (buf) { + g_object_ref(buf); + pb = new Pixbuf(buf); + pb->_mod_time = stdir.st_mtime; + pb->_path = fn; + + GdkPixbufFormat *fmt = gdk_pixbuf_loader_get_format(loader); + gchar *fmt_name = gdk_pixbuf_format_get_name(fmt); + pb->_setMimeData((guchar *) data, len, fmt_name); + g_free(fmt_name); + } else { + g_free(data); + } + g_object_unref(loader); + + // 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 { + return NULL; + } + + return pb; +} + +/** + * Converts the pixbuf to GdkPixbuf pixel format. + * The returned pixbuf can be used e.g. in calls to gdk_pixbuf_save(). + */ +GdkPixbuf *Pixbuf::getPixbufRaw(bool convert_format) +{ + if (convert_format) { + ensurePixelFormat(PF_GDK); + } + return _pixbuf; +} + +/** + * Converts the pixbuf to Cairo pixel format and returns an image surface + * which can be used as a source. + * + * 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 ensurePixelFormat(Pixbuf::PIXEL_FORMAT_PIXBUF) is called. + */ +cairo_surface_t *Pixbuf::getSurfaceRaw(bool convert_format) +{ + if (convert_format) { + ensurePixelFormat(PF_CAIRO); + } + return _surface; +} + +/* Declaring this function in the header requires including <gdkmm/pixbuf.h>, + * which stupidly includes <glibmm.h> which in turn pulls in <glibmm/threads.h>. + * However, since glib 2.32, <glibmm/threads.h> has to be included before <glib.h> + * when compiling with G_DISABLE_DEPRECATED, as we do in non-release builds. + * This necessitates spamming a lot of files with #include <glibmm/threads.h> + * at the top. + * + * Since we don't really use gdkmm, do not define this function for now. */ + +/* +Glib::RefPtr<Gdk::Pixbuf> Pixbuf::getPixbuf(bool convert_format = true) +{ + g_object_ref(_pixbuf); + Glib::RefPtr<Gdk::Pixbuf> p(getPixbuf(convert_format)); + return p; +} +*/ + +Cairo::RefPtr<Cairo::Surface> Pixbuf::getSurface(bool convert_format) +{ + Cairo::RefPtr<Cairo::Surface> p(new Cairo::Surface(getSurfaceRaw(convert_format), false)); + return p; +} + +/** Retrieves the original compressed data for the surface, if any. + * The returned data belongs to the object and should not be freed. */ +guchar const *Pixbuf::getMimeData(gsize &len, std::string &mimetype) const +{ + 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)); + + guchar const *data = NULL; + + for (guint i = 0; i < mimetypes_len; ++i) { + unsigned long len_long = 0; + cairo_surface_get_mime_data(const_cast<cairo_surface_t*>(_surface), mimetypes[i], &data, &len); + len = len_long; // this assumes that the added range of long is not needed. the code below assumes gsize range of values is sufficient. + if (data != NULL) { + mimetype = mimetypes[i]; + break; + } + } + + return data; +} + +int Pixbuf::width() const { + return gdk_pixbuf_get_width(const_cast<GdkPixbuf*>(_pixbuf)); +} +int Pixbuf::height() const { + return gdk_pixbuf_get_height(const_cast<GdkPixbuf*>(_pixbuf)); +} +int Pixbuf::rowstride() const { + return gdk_pixbuf_get_rowstride(const_cast<GdkPixbuf*>(_pixbuf)); +} +guchar const *Pixbuf::pixels() const { + return gdk_pixbuf_get_pixels(const_cast<GdkPixbuf*>(_pixbuf)); +} +guchar *Pixbuf::pixels() { + return gdk_pixbuf_get_pixels(_pixbuf); +} +void Pixbuf::markDirty() { + cairo_surface_mark_dirty(_surface); +} + +void Pixbuf::_forceAlpha() +{ + if (gdk_pixbuf_get_has_alpha(_pixbuf)) return; + + GdkPixbuf *old = _pixbuf; + _pixbuf = gdk_pixbuf_add_alpha(old, FALSE, 0, 0, 0); + g_object_unref(old); +} + +void Pixbuf::_setMimeData(guchar *data, gsize len, Glib::ustring const &format) +{ + gchar const *mimetype = NULL; + + if (format == "jpeg") { + mimetype = CAIRO_MIME_TYPE_JPEG; + } else if (format == "jpeg2000") { + mimetype = CAIRO_MIME_TYPE_JP2; + } else if (format == "png") { + mimetype = CAIRO_MIME_TYPE_PNG; + } + + if (mimetype != NULL) { + cairo_surface_set_mime_data(_surface, 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()); + } +} + +void Pixbuf::ensurePixelFormat(PixelFormat fmt) +{ + if (_pixel_format == PF_GDK) { + if (fmt == PF_GDK) { + return; + } + if (fmt == PF_CAIRO) { + convert_pixels_pixbuf_to_argb32( + gdk_pixbuf_get_pixels(_pixbuf), + gdk_pixbuf_get_width(_pixbuf), + gdk_pixbuf_get_height(_pixbuf), + gdk_pixbuf_get_rowstride(_pixbuf)); + _pixel_format = fmt; + return; + } + g_assert_not_reached(); + } + if (_pixel_format == PF_CAIRO) { + if (fmt == PF_GDK) { + convert_pixels_argb32_to_pixbuf( + gdk_pixbuf_get_pixels(_pixbuf), + gdk_pixbuf_get_width(_pixbuf), + gdk_pixbuf_get_height(_pixbuf), + gdk_pixbuf_get_rowstride(_pixbuf)); + _pixel_format = fmt; + return; + } + if (fmt == PF_CAIRO) { + return; + } + g_assert_not_reached(); + } + g_assert_not_reached(); +} + } // namespace Inkscape /* @@ -371,129 +749,6 @@ ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Affine const &m) cairo_pattern_set_matrix(cp, &cm); } -void -ink_cairo_set_source_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y) -{ - cairo_surface_t *pbs = ink_cairo_surface_get_for_pixbuf(pb); - cairo_set_source_surface(ct, pbs, x, y); -} - -/* The functions below implement the following hack: - * - * The pixels formats of Cairo and GdkPixbuf are different. - * GdkPixbuf accesses pixels as bytes, alpha is not premultiplied, - * and successive bytes of a single pixel contain R, G, B and A components. - * Cairo accesses pixels as 32-bit ints, alpha is premultiplied, - * and each int contains as 0xAARRGGBB, accessed with bitwise operations. - * - * In other words, on a little endian system, a GdkPixbuf will contain: - * char *data = "rgbargbargba...." - * int *data = { 0xAABBGGRR, 0xAABBGGRR, 0xAABBGGRR, ... } - * while a Cairo image surface will contain: - * char *data = "bgrabgrabgra...." - * int *data = { 0xAARRGGBB, 0xAARRGGBB, 0xAARRGGBB, ... } - * - * It is possible to convert between these two formats (almost) losslessly. - * Some color information from partially transparent regions of the image - * is lost, but the result when displaying this image will remain the same. - * - * The functions below allow interoperation between GdkPixbuf - * and Cairo surfaces, allowing pixbufs to be used as Cairo sources, - * and saving Cairo surfaces using GdkPixbuf APIs. - * This is implemented by creating a GdkPixbuf and a Cairo image surface - * which share their data. Depending on what is needed at a given time, - * the pixels are converted in place to the Cairo or the GdkPixbuf format. - * In this way, only one copy of the image data is needed. - * - * Given either a GdkPixbuf or a Cairo surface, these functions create - * the other object and convert to its format. The returned object should be - * freed using cairo_surface_destroy or g_object_unref when it's no longer - * needed. - * - * Memory ownership semantics: - * Regardless of whether the pixels are stored in memory originally belonging - * to Cairo surface or to GdkPixbuf, the GdkPixbuf is the master object. - * To free the memory, unref the GdkPixbuf ONLY. - */ - -/** - * Converts the pixbuf to Cairo pixel format and returns an image surface - * which can be used as a source. - * - * 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) -{ - cairo_surface_t *pbs = - reinterpret_cast<cairo_surface_t*>(g_object_get_data(G_OBJECT(pb), "cairo_surface")); - - ink_pixbuf_ensure_argb32(pb); - - if (pbs == NULL) { - guchar *data = gdk_pixbuf_get_pixels(pb); - int w = gdk_pixbuf_get_width(pb); - int h = gdk_pixbuf_get_height(pb); - int stride = gdk_pixbuf_get_rowstride(pb); - - // create a surface that stores the data - pbs = cairo_image_surface_create_for_data( - data, CAIRO_FORMAT_ARGB32, w, h, stride); - - g_object_set_data_full(G_OBJECT(pb), "cairo_surface", pbs, (GDestroyNotify) cairo_surface_destroy); - cairo_surface_set_user_data(pbs, &ink_pixbuf_key, pb, NULL); - } - - return pbs; -} - -/** - * Converts the Cairo surface to GdkPixbuf pixel format. - * GdkPixbuf takes ownership of the passed surface reference, - * so it should NOT be freed after calling this function. - */ -GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s) -{ - GdkPixbuf *pb = reinterpret_cast<GdkPixbuf*>(cairo_surface_get_user_data(s, &ink_pixbuf_key)); - if (pb == NULL) { - pb = gdk_pixbuf_new_from_data( - cairo_image_surface_get_data(s), GDK_COLORSPACE_RGB, TRUE, 8, - cairo_image_surface_get_width(s), cairo_image_surface_get_height(s), - cairo_image_surface_get_stride(s), NULL, NULL); - - g_object_set_data_full(G_OBJECT(pb), "pixel_format", g_strdup("argb32"), g_free); - g_object_set_data_full(G_OBJECT(pb), "cairo_surface", s, (GDestroyNotify) cairo_surface_destroy); - - cairo_surface_set_user_data(s, &ink_pixbuf_key, pb, NULL); - } else { - g_warning("Received surface which is already owned by GdkPixbuf"); - g_object_ref(pb); - } - - ink_pixbuf_ensure_normal(pb); - - return pb; -} - -/** - * Cleanup function for GdkPixbuf. - * This function should be passed as the GdkPixbufDestroyNotify parameter - * to gdk_pixbuf_new_from_data when creating a GdkPixbuf backed by - * a Cairo surface. - */ -void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data) -{ - cairo_surface_t *surface = reinterpret_cast<cairo_surface_t*>(data); - cairo_surface_destroy(surface); -} - /** * Create an exact copy of a surface. * Creates a surface that has the same type, content type, dimensions and contents @@ -833,6 +1088,44 @@ ink_cairo_pattern_create_checkerboard() return p; } +/** + * Converts the Cairo surface to a GdkPixbuf pixel format, + * without allocating extra memory. + * + * This function is intended mainly for creating previews displayed by GTK. + * For loading images for display on the canvas, use the Inkscape::Pixbuf object. + * + * The returned GdkPixbuf takes ownership of the passed surface reference, + * so it should NOT be freed after calling this function. + */ +GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s) +{ + guchar *pixels = cairo_image_surface_get_data(s); + int w = cairo_image_surface_get_width(s); + int h = cairo_image_surface_get_height(s); + int rs = cairo_image_surface_get_stride(s); + + convert_pixels_argb32_to_pixbuf(pixels, w, h, rs); + + GdkPixbuf *pb = gdk_pixbuf_new_from_data( + pixels, GDK_COLORSPACE_RGB, TRUE, 8, + w, h, rs, ink_cairo_pixbuf_cleanup, s); + + return pb; +} + +/** + * Cleanup function for GdkPixbuf. + * This function should be passed as the GdkPixbufDestroyNotify parameter + * to gdk_pixbuf_new_from_data when creating a GdkPixbuf backed by + * a Cairo surface. + */ +static void ink_cairo_pixbuf_cleanup(guchar * /*pixels*/, void *data) +{ + cairo_surface_t *surface = static_cast<cairo_surface_t*>(data); + cairo_surface_destroy(surface); +} + /* The following two functions use "from" instead of "to", because when you write: val1 = argb32_from_pixbuf(val1); the name of the format is closer to the value in that format. */ diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h index 289d4e01f..505e2ca77 100644 --- a/src/display/cairo-utils.h +++ b/src/display/cairo-utils.h @@ -12,14 +12,15 @@ #ifndef SEEN_INKSCAPE_DISPLAY_CAIRO_UTILS_H #define SEEN_INKSCAPE_DISPLAY_CAIRO_UTILS_H +#include <boost/noncopyable.hpp> +//#include <glibmm/threads.h> // workaround #include <glib.h> #include <cairomm/cairomm.h> +//#include <gdkmm/pixbuf.h> #include <2geom/forward.h> #include "style.h" struct SPColor; -struct _GdkPixbuf; -typedef struct _GdkPixbuf GdkPixbuf; namespace Inkscape { @@ -80,15 +81,61 @@ public: static Cairo::RefPtr<CairoContext> create(Cairo::RefPtr<Cairo::Surface> const &target); }; -} // namespace Inkscape +/** Class to hold image data for raster images. + * Allows easy interoperation with GdkPixbuf and Cairo. */ +class Pixbuf { +public: + enum PixelFormat { + PF_CAIRO = 1, + PF_GDK = 2, + PF_LAST + }; + + explicit Pixbuf(cairo_surface_t *s); + explicit Pixbuf(GdkPixbuf *pb); + Pixbuf(Inkscape::Pixbuf const &other); + ~Pixbuf(); + + GdkPixbuf *getPixbufRaw(bool convert_format = true); + //Glib::RefPtr<Gdk::Pixbuf> getPixbuf(bool convert_format = true); + + cairo_surface_t *getSurfaceRaw(bool convert_format = true); + Cairo::RefPtr<Cairo::Surface> getSurface(bool convert_format = true); + + int width() const; + int height() const; + int rowstride() const; + guchar const *pixels() const; + guchar *pixels(); + void markDirty(); + + bool hasMimeData() const; + guchar const *getMimeData(gsize &len, std::string &mimetype) const; + std::string const &originalPath() const { return _path; } + time_t modificationTime() const { return _mod_time; } -enum InkPixelFormat { - INK_PIXEL_FORMAT_NONE, - INK_PIXEL_FORMAT_CAIRO, - INK_PIXEL_FORMAT_PIXBUF, - INK_PIXEL_FORMAT_LAST + PixelFormat pixelFormat() const { return _pixel_format; } + void ensurePixelFormat(PixelFormat fmt); + + static Pixbuf *create_from_data_uri(gchar const *uri); + static Pixbuf *create_from_file(std::string const &fn); + +private: + void _ensurePixelsARGB32(); + void _ensurePixelsPixbuf(); + void _forceAlpha(); + void _setMimeData(guchar *data, gsize len, Glib::ustring const &format); + + GdkPixbuf *_pixbuf; + cairo_surface_t *_surface; + time_t _mod_time; + std::string _path; + PixelFormat _pixel_format; + bool _cairo_store; }; +} // namespace Inkscape + // TODO: these declarations may not be needed in the header extern cairo_user_data_key_t ink_color_interpolation_key; extern cairo_user_data_key_t ink_pixbuf_key; @@ -102,7 +149,6 @@ void ink_cairo_set_source_color(cairo_t *ct, SPColor const &color, double opacit void ink_cairo_set_source_rgba32(cairo_t *ct, guint32 rgba); void ink_cairo_transform(cairo_t *ct, Geom::Affine const &m); void ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Affine const &m); -void ink_cairo_set_source_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y); void ink_matrix_to_2geom(Geom::Affine &, cairo_matrix_t const &); void ink_matrix_to_cairo(cairo_matrix_t &, Geom::Affine const &); @@ -125,13 +171,9 @@ int ink_cairo_surface_linear_to_srgb(cairo_surface_t *surface); cairo_pattern_t *ink_cairo_pattern_create_checkerboard(); +GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s); void convert_pixels_pixbuf_to_argb32(guchar *data, int w, int h, int rs); void convert_pixels_argb32_to_pixbuf(guchar *data, int w, int h, int rs); -void ink_pixbuf_ensure_argb32(GdkPixbuf *); -void ink_pixbuf_ensure_normal(GdkPixbuf *); -cairo_surface_t *ink_cairo_surface_get_for_pixbuf(GdkPixbuf *pb); -GdkPixbuf *ink_pixbuf_create_from_cairo_surface(cairo_surface_t *s); -void ink_cairo_pixbuf_cleanup(guchar *pixels, void *surface); G_GNUC_CONST guint32 argb32_from_pixbuf(guint32 in); G_GNUC_CONST guint32 pixbuf_from_argb32(guint32 in); diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 46f066b8e..0b661a450 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -22,34 +22,23 @@ namespace Inkscape { DrawingImage::DrawingImage(Drawing &drawing) : DrawingItem(drawing) , _pixbuf(NULL) - , _surface(NULL) // this is owned by _pixbuf! , _style(NULL) , _new_surface(NULL) {} DrawingImage::~DrawingImage() { - if (_style) + if (_style) { sp_style_unref(_style); - if (_pixbuf) { - if (_new_surface) cairo_surface_destroy(_new_surface); - g_object_unref(_pixbuf); } + + // _pixbuf is owned by SPImage - do not delete it } void -DrawingImage::setARGB32Pixbuf(GdkPixbuf *pb) +DrawingImage::setPixbuf(Inkscape::Pixbuf *pb) { - // when done in this order, it won't break if pb == image->pixbuf and the refcount is 1 - if (pb != NULL) { - g_object_ref (pb); - } - if (_pixbuf != NULL) { - g_object_unref(_pixbuf); - // unrefing the pixbuf also destroys surface - } _pixbuf = pb; - _surface = pb ? ink_cairo_surface_get_for_pixbuf(pb) : NULL; _markForUpdate(STATE_ALL, false); } @@ -86,8 +75,8 @@ DrawingImage::bounds() const { if (!_pixbuf) return _clipbox; - double pw = gdk_pixbuf_get_width(_pixbuf); - double ph = gdk_pixbuf_get_height(_pixbuf); + double pw = _pixbuf->width(); + double ph = _pixbuf->height(); double vw = pw * _scale[Geom::X]; double vh = ph * _scale[Geom::Y]; Geom::Point wh(vw, vh); @@ -143,14 +132,16 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar // See https://bugs.launchpad.net/inkscape/+bug/804162 Geom::Scale expansion(_ctm.expansion()); - int orgwidth = cairo_image_surface_get_width(_surface); - int orgheight = cairo_image_surface_get_height(_surface); + int orgwidth = _pixbuf->width(); + int orgheight = _pixbuf->height(); if (_scale[Geom::X]*expansion[Geom::X]*orgwidth*255.0<1.0 || _scale[Geom::Y]*expansion[Geom::Y]*orgheight*255.0<1.0) { // Resized image too small to actually see anything return RENDER_OK; } - + + _pixbuf->ensurePixelFormat(Inkscape::Pixbuf::PF_CAIRO); + // Split scale*expansion in a part that is <= 1.0 and a part that is >= 1.0. We only take care of the part <= 1.0. Geom::Scale scaleExpansionSmall(std::min<Geom::Coord>(fabs(_scale[Geom::X]*expansion[Geom::X]),1),std::min<Geom::Coord>(fabs(_scale[Geom::Y]*expansion[Geom::Y]),1)); Geom::Scale scaleExpansionLarge(_scale[Geom::X]*expansion[Geom::X]/scaleExpansionSmall[Geom::X],_scale[Geom::Y]*expansion[Geom::Y]/scaleExpansionSmall[Geom::Y]); @@ -161,7 +152,7 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar ct.scale(expansion.inverse()); // This should not include scale (see derivation above) ct.translate(_origin*expansion); ct.scale(scaleExpansionLarge); - ct.setSource(_surface, 0, 0); + ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0); } else if (!_new_surface || (newSize-_rescaledSize).length()>0.1) { // Rescaled image is sufficiently different from cached image to recompute if (_new_surface) cairo_surface_destroy(_new_surface); @@ -200,13 +191,13 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar } } + cairo_surface_t *surface = _pixbuf->getSurfaceRaw(); _new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, newwidth,newheight); - unsigned char * orgdata = cairo_image_surface_get_data(_surface); + unsigned char * orgdata = cairo_image_surface_get_data(surface); unsigned char * newdata = cairo_image_surface_get_data(_new_surface); - int orgstride = cairo_image_surface_get_stride(_surface); + int orgstride = cairo_image_surface_get_stride(surface); int newstride = cairo_image_surface_get_stride(_new_surface); - - //cairo_surface_flush(_surface); + cairo_surface_flush(_new_surface); for(int y=0; y<newheight; y++) { @@ -245,7 +236,7 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar // TODO: If Cairo's problems are gone, uncomment the following: //ct.translate(_origin); //ct.scale(_scale); - //ct.setSource(_surface, 0, 0); + //ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0); //ct.paint(_opacity); ct.paint(); @@ -315,10 +306,10 @@ DrawingImage::_pickItem(Geom::Point const &p, double delta, unsigned /*sticky*/) return NULL; } else { - unsigned char *const pixels = gdk_pixbuf_get_pixels(_pixbuf); - int width = gdk_pixbuf_get_width(_pixbuf); - int height = gdk_pixbuf_get_height(_pixbuf); - int rowstride = gdk_pixbuf_get_rowstride(_pixbuf); + unsigned char *const pixels = _pixbuf->pixels(); + int width = _pixbuf->width(); + int height = _pixbuf->height(); + int rowstride = _pixbuf->rowstride(); Geom::Point tp = p * _ctm.inverse(); Geom::Rect r = bounds(); @@ -336,8 +327,17 @@ DrawingImage::_pickItem(Geom::Point const &p, double delta, unsigned /*sticky*/) unsigned char *pix_ptr = pixels + iy * rowstride + ix * 4; // pick if the image is less than 99% transparent - float alpha = (pix_ptr[3] / 255.0f) * _opacity; - return alpha > 0.01 ? this : NULL; + guint32 alpha = 0; + if (_pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) { + guint32 px = *reinterpret_cast<guint32 const *>(pix_ptr); + alpha = (px & 0xff000000) >> 24; + } else if (_pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_GDK) { + alpha = pix_ptr[3]; + } else { + throw std::runtime_error("Unrecognized pixel format"); + } + float alpha_f = (alpha / 255.0f) * _opacity; + return alpha_f > 0.01 ? this : NULL; } } diff --git a/src/display/drawing-image.h b/src/display/drawing-image.h index 593185c97..58e6de72e 100644 --- a/src/display/drawing-image.h +++ b/src/display/drawing-image.h @@ -19,6 +19,7 @@ #include "display/drawing-item.h" namespace Inkscape { +class Pixbuf; class DrawingImage : public DrawingItem @@ -27,7 +28,7 @@ public: DrawingImage(Drawing &drawing); ~DrawingImage(); - void setARGB32Pixbuf(GdkPixbuf *pb); + void setPixbuf(Inkscape::Pixbuf *pb); void setStyle(SPStyle *style); void setScale(double sx, double sy); void setOrigin(Geom::Point const &o); @@ -41,8 +42,7 @@ protected: DrawingItem *stop_at); virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); - GdkPixbuf *_pixbuf; - cairo_surface_t *_surface; + Inkscape::Pixbuf *_pixbuf; SPStyle *_style; cairo_surface_t *_new_surface; // Part of hack around Cairo bug diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp index b9d73f0ad..4ca4cd07c 100644 --- a/src/display/nr-filter-image.cpp +++ b/src/display/nr-filter-image.cpp @@ -30,6 +30,7 @@ FilterImage::FilterImage() : SVGElem(0) , document(0) , feImageHref(0) + , image(0) , broken_ref(false) { } @@ -41,7 +42,7 @@ FilterImage::~FilterImage() { if (feImageHref) g_free(feImageHref); - g_object_set_data(G_OBJECT(image->gobj()), "cairo_surface", NULL); + delete image; } void FilterImage::render_cairo(FilterSlot &slot) @@ -131,50 +132,38 @@ void FilterImage::render_cairo(FilterSlot &slot) // External image, like <image> if (!image && !broken_ref) { broken_ref = true; - try { - /* TODO: If feImageHref is absolute, then use that (preferably handling the - * case that it's not a file URI). Otherwise, go up the tree looking - * for an xml:base attribute, and use that as the base URI for resolving - * the relative feImageHref URI. Otherwise, if document->base is valid, - * then use that as the base URI. Otherwise, use feImageHref directly - * (i.e. interpreting it as relative to our current working directory). - * (See http://www.w3.org/TR/xmlbase/#resolution .) */ - gchar *fullname = feImageHref; - if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) { - // Try to load from relative postion combined with document base - if( document ) { - fullname = g_build_filename( document->getBase(), feImageHref, NULL ); - } - } - if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) { - // Should display Broken Image png. - g_warning("FilterImage::render: Can not find: %s", feImageHref ); - return; + + /* TODO: If feImageHref is absolute, then use that (preferably handling the + * case that it's not a file URI). Otherwise, go up the tree looking + * for an xml:base attribute, and use that as the base URI for resolving + * the relative feImageHref URI. Otherwise, if document->base is valid, + * then use that as the base URI. Otherwise, use feImageHref directly + * (i.e. interpreting it as relative to our current working directory). + * (See http://www.w3.org/TR/xmlbase/#resolution .) */ + gchar *fullname = feImageHref; + if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) { + // Try to load from relative postion combined with document base + if( document ) { + fullname = g_build_filename( document->getBase(), feImageHref, NULL ); } - image = Gdk::Pixbuf::create_from_file(fullname); - if( fullname != feImageHref ) g_free( fullname ); } - catch (const Glib::FileError & e) - { - g_warning("caught Glib::FileError in FilterImage::render: %s", e.what().data() ); + if ( !g_file_test( fullname, G_FILE_TEST_EXISTS ) ) { + // Should display Broken Image png. + g_warning("FilterImage::render: Can not find: %s", feImageHref ); return; } - catch (const Gdk::PixbufError & e) - { - g_warning("Gdk::PixbufError in FilterImage::render: %s", e.what().data() ); + image = Inkscape::Pixbuf::create_from_file(fullname); + if( fullname != feImageHref ) g_free( fullname ); + + if ( !image ) { + g_warning("FilterImage::render: failed to load image: %s", feImageHref); return; } - if ( !image ) return; broken_ref = false; - - bool has_alpha = image->get_has_alpha(); - if (!has_alpha) { - image = image->add_alpha(false, 0, 0, 0); - } } - cairo_surface_t *image_surface = ink_cairo_surface_get_for_pixbuf(image->gobj()); + cairo_surface_t *image_surface = image->getSurfaceRaw(); Geom::Rect sa = slot.get_slot_area(); cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, @@ -199,7 +188,7 @@ void FilterImage::render_cairo(FilterSlot &slot) // Check aspect ratio of image vs. viewport double feAspect = feImageHeight/feImageWidth; - double aspect = (double)image->get_height()/(double)image->get_width(); + double aspect = (double)image->height()/(double)image->width(); bool ratio = (feAspect < aspect); double ax, ay; // Align side @@ -274,8 +263,8 @@ void FilterImage::render_cairo(FilterSlot &slot) } } - double scaleX = feImageWidth / image->get_width(); - double scaleY = feImageHeight / image->get_height(); + double scaleX = feImageWidth / image->width(); + double scaleY = feImageHeight / image->height(); cairo_translate(ct, feImageX, feImageY); cairo_scale(ct, scaleX, scaleY); @@ -302,8 +291,8 @@ 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(); + delete image; + image = NULL; broken_ref = false; } diff --git a/src/display/nr-filter-image.h b/src/display/nr-filter-image.h index f45f42265..69691ac99 100644 --- a/src/display/nr-filter-image.h +++ b/src/display/nr-filter-image.h @@ -12,14 +12,14 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <gdkmm/pixbuf.h> #include "display/nr-filter-primitive.h" -#include <glibmm/refptr.h> class SPDocument; class SPItem; namespace Inkscape { +class Pixbuf; + namespace Filters { class FilterSlot; @@ -43,7 +43,7 @@ public: private: SPDocument *document; gchar *feImageHref; - Glib::RefPtr<Gdk::Pixbuf> image; + Inkscape::Pixbuf *image; float feImageX, feImageY, feImageWidth, feImageHeight; unsigned int aspect_align, aspect_clip; bool broken_ref; diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index a950fa177..4f9273cbb 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -1436,7 +1436,7 @@ CairoRenderContext::renderPathVector(Geom::PathVector const & pathv, SPStyle con return true; } -bool CairoRenderContext::renderImage(GdkPixbuf *pb, +bool CairoRenderContext::renderImage(Inkscape::Pixbuf *pb, Geom::Affine const &image_transform, SPStyle const * /*style*/) { g_assert( _is_valid ); @@ -1447,13 +1447,13 @@ bool CairoRenderContext::renderImage(GdkPixbuf *pb, _prepareRenderGraphic(); - int w = gdk_pixbuf_get_width (pb); - int h = gdk_pixbuf_get_height (pb); + int w = pb->width(); + int h = pb->height(); // TODO: reenable merge_opacity if useful float opacity = _state->opacity; - cairo_surface_t *image_surface = ink_cairo_surface_get_for_pixbuf(pb); + cairo_surface_t *image_surface = pb->getSurfaceRaw(); if (cairo_surface_status(image_surface)) { TRACE(("Image surface creation failed:\n%s\n", cairo_status_to_string(cairo_surface_status(image_surface)))); return false; diff --git a/src/extension/internal/cairo-render-context.h b/src/extension/internal/cairo-render-context.h index f8426aebe..6fccc71b7 100644 --- a/src/extension/internal/cairo-render-context.h +++ b/src/extension/internal/cairo-render-context.h @@ -6,7 +6,7 @@ */ /* * Authors: - * Miklos Erdelyi <erdelyim@gmail.com> + * Miklos Erdelyi <erdelyim@gmail.com> * * Copyright (C) 2006 Miklos Erdelyi * @@ -32,6 +32,8 @@ class SPClipPath; struct SPMask; namespace Inkscape { +class Pixbuf; + namespace Extension { namespace Internal { @@ -144,7 +146,7 @@ public: /* Rendering methods */ bool renderPathVector(Geom::PathVector const &pathv, SPStyle const *style, Geom::OptRect const &pbox); - bool renderImage(GdkPixbuf *pb, + bool renderImage(Inkscape::Pixbuf *pb, Geom::Affine const &image_transform, SPStyle const *style); bool renderGlyphtext(PangoFont *font, Geom::Affine const &font_matrix, std::vector<CairoGlyphInfo> const &glyphtext, SPStyle const *style); diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 3463925b6..eb16a38e1 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -27,6 +27,7 @@ #include <signal.h> #include <errno.h> +#include <boost/scoped_ptr.hpp> #include "libnrtype/Layout-TNG.h" #include <2geom/transforms.h> @@ -347,8 +348,8 @@ static void sp_image_render(SPItem *item, CairoRenderContext *ctx) if (!image->pixbuf) return; if ((image->width.computed <= 0.0) || (image->height.computed <= 0.0)) return; - w = gdk_pixbuf_get_width (image->pixbuf); - h = gdk_pixbuf_get_height (image->pixbuf); + w = image->pixbuf->width(); + h = image->pixbuf->height(); double x = image->x.computed; double y = image->y.computed; @@ -497,22 +498,15 @@ static void sp_asbitmap_render(SPItem *item, CairoRenderContext *ctx) GSList *items = NULL; items = g_slist_append(items, item); - GdkPixbuf *pb = sp_generate_internal_bitmap(document, NULL, - bbox->min()[Geom::X], bbox->min()[Geom::Y], bbox->max()[Geom::X], bbox->max()[Geom::Y], - width, height, res, res, (guint32) 0xffffff00, items ); + boost::scoped_ptr<Inkscape::Pixbuf> pb( + sp_generate_internal_bitmap(document, NULL, + bbox->min()[Geom::X], bbox->min()[Geom::Y], bbox->max()[Geom::X], bbox->max()[Geom::Y], + width, height, res, res, (guint32) 0xffffff00, items )); if (pb) { - TEST(gdk_pixbuf_save( pb, "bitmap.png", "png", NULL, NULL )); - - /* TODO: find a way to avoid a duplicate conversion between - * Cairo and GdkPixbuf pixel formats here. - * Internally, generate_internal_bitmap creates a Cairo surface, - * but then converts it to pixbuf format. In turn, renderImage() - * below converts back to Cairo format. - */ - ctx->renderImage(pb, t, item->style); - g_object_unref(pb); - pb = 0; + //TEST(gdk_pixbuf_save( pb, "bitmap.png", "png", NULL, NULL )); + + ctx->renderImage(pb.get(), t, item->style); } g_slist_free (items); } diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp index 826a52ade..770257978 100644 --- a/src/extension/internal/emf-print.cpp +++ b/src/extension/internal/emf-print.cpp @@ -53,6 +53,7 @@ #include "sp-gradient.h" #include "sp-radial-gradient.h" #include "sp-linear-gradient.h" +#include "display/cairo-utils.h" #include "splivarot.h" // pieces for union on shapes #include "2geom/svg-path-parser.h" // to get from SVG text to Geom::Path @@ -333,7 +334,7 @@ int PrintEmf::create_brush(SPStyle const *style, PU_COLORREF fcolor) U_LOGBRUSH lb; uint32_t brush, fmode; MFDrawMode fill_mode; - GdkPixbuf *pixbuf; + Inkscape::Pixbuf *pixbuf; uint32_t brushStyle; int hatchType; U_COLORREF hatchColor; @@ -462,7 +463,7 @@ int PrintEmf::create_brush(SPStyle const *style, PU_COLORREF fcolor) int numCt; U_BITMAPINFOHEADER Bmih; PU_BITMAPINFO Bmi; - rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!! + rgba_px = (char *) pixbuf->pixels(); // Do NOT free this!!! colortype = U_BCBM_COLOR32; (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1); // Not sure why the next swap is needed because the preceding does it, and the code is identical @@ -528,7 +529,7 @@ int PrintEmf::create_pen(SPStyle const *style, const Geom::Affine &transform) int linejoin = 0; uint32_t pen; uint32_t brushStyle; - GdkPixbuf *pixbuf; + Inkscape::Pixbuf *pixbuf; int hatchType; U_COLORREF hatchColor; U_COLORREF bkColor; @@ -565,7 +566,7 @@ int PrintEmf::create_pen(SPStyle const *style, const Geom::Affine &transform) brush_classify(pat, 0, &pixbuf, &hatchType, &hatchColor, &bkColor); if (pixbuf) { brushStyle = U_BS_DIBPATTERN; - rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!! + rgba_px = (char *) pixbuf->pixels(); // Do NOT free this!!! colortype = U_BCBM_COLOR32; (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1); // Not sure why the next swap is needed because the preceding does it, and the code is identical diff --git a/src/extension/internal/gdkpixbuf-input.cpp b/src/extension/internal/gdkpixbuf-input.cpp index 117c2fe39..87cf8a9cc 100644 --- a/src/extension/internal/gdkpixbuf-input.cpp +++ b/src/extension/internal/gdkpixbuf-input.cpp @@ -1,6 +1,7 @@ #ifdef HAVE_CONFIG_H # include <config.h> #endif +#include <boost/scoped_ptr.hpp> #include <glib/gprintf.h> #include <glibmm/i18n.h> #include "document-private.h" @@ -14,15 +15,11 @@ #include "document-undo.h" #include "util/units.h" #include "image-resolution.h" +#include "display/cairo-utils.h" #include <set> namespace Inkscape { -namespace IO { -// this is defined in sp-image.cpp -GdkPixbuf* pixbuf_new_from_file(char const *filename, time_t &modTime, gchar*& pixPath); -} - namespace Extension { namespace Internal { @@ -47,9 +44,7 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri) } SPDocument *doc = NULL; - gchar *pixpath = NULL; - time_t dummy; - GdkPixbuf *pb = Inkscape::IO::pixbuf_new_from_file(uri, dummy, pixpath); + boost::scoped_ptr<Inkscape::Pixbuf> pb(Inkscape::Pixbuf::create_from_file(uri)); // TODO: the pixbuf is created again from the base64-encoded attribute in SPImage. // Find a way to create the pixbuf only once. @@ -59,8 +54,8 @@ GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri) bool saved = DocumentUndo::getUndoSensitive(doc); DocumentUndo::setUndoSensitive(doc, false); // no need to undo in this temporary document - double width = gdk_pixbuf_get_width(pb); - double height = gdk_pixbuf_get_height(pb); + double width = pb->width(); + double height = pb->height(); double defaultxdpi = prefs->getDouble("/dialogs/import/defaultxdpi/value", Inkscape::Util::Quantity::convert(1, "in", "px")); bool forcexdpi = prefs->getBool("/dialogs/import/forcexdpi"); ImageResolution *ir = 0; @@ -91,7 +86,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); + sp_embed_image(image_node, pb.get()); } else { // convert filename to uri gchar* _uri = g_filename_to_uri(uri, NULL, NULL); @@ -103,9 +98,6 @@ 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 doc->getRoot()->appendChildRepr(image_node); Inkscape::GC::release(image_node); diff --git a/src/extension/internal/metafile-print.cpp b/src/extension/internal/metafile-print.cpp index 9d080bd96..1e7735410 100644 --- a/src/extension/internal/metafile-print.cpp +++ b/src/extension/internal/metafile-print.cpp @@ -266,7 +266,7 @@ void PrintMetafile::hatch_classify(char *name, int *hatchType, U_COLORREF *hatch // otherwise hatchType is set to -1 and hatchColor is not defined. // -void PrintMetafile::brush_classify(SPObject *parent, int depth, GdkPixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor) +void PrintMetafile::brush_classify(SPObject *parent, int depth, Inkscape::Pixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor) { if (depth == 0) { *epixbuf = NULL; diff --git a/src/extension/internal/metafile-print.h b/src/extension/internal/metafile-print.h index e64ba92f3..cba4d564d 100644 --- a/src/extension/internal/metafile-print.h +++ b/src/extension/internal/metafile-print.h @@ -30,6 +30,8 @@ struct SPGradient; struct SPObject; namespace Inkscape { +class Pixbuf; + namespace Extension { namespace Internal { @@ -93,7 +95,7 @@ protected: U_COLORREF weight_colors(U_COLORREF c1, U_COLORREF c2, double t); void hatch_classify(char *name, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor); - void brush_classify(SPObject *parent, int depth, GdkPixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor); + void brush_classify(SPObject *parent, int depth, Inkscape::Pixbuf **epixbuf, int *hatchType, U_COLORREF *hatchColor, U_COLORREF *bkColor); static void swapRBinRGBA(char *px, int pixels); int hold_gradient(void *gr, int mode); diff --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp index e5816073e..99262b109 100644 --- a/src/extension/internal/wmf-print.cpp +++ b/src/extension/internal/wmf-print.cpp @@ -56,6 +56,7 @@ #include "sp-gradient.h" #include "sp-radial-gradient.h" #include "sp-linear-gradient.h" +#include "display/cairo-utils.h" #include "splivarot.h" // pieces for union on shapes #include "2geom/svg-path-parser.h" // to get from SVG text to Geom::Path @@ -336,7 +337,7 @@ int PrintWmf::create_brush(SPStyle const *style, PU_COLORREF fcolor) U_WLOGBRUSH lb; uint32_t brush, fmode; MFDrawMode fill_mode; - GdkPixbuf *pixbuf; + Inkscape::Pixbuf *pixbuf; uint32_t brushStyle; int hatchType; U_COLORREF hatchColor; @@ -464,7 +465,7 @@ int PrintWmf::create_brush(SPStyle const *style, PU_COLORREF fcolor) int numCt; U_BITMAPINFOHEADER Bmih; PU_BITMAPINFO Bmi; - rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!! + rgba_px = (char *) pixbuf->pixels(); // Do NOT free this!!! colortype = U_BCBM_COLOR32; (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1); // Not sure why the next swap is needed because the preceding does it, and the code is identical @@ -1112,10 +1113,10 @@ unsigned int PrintWmf::image( g_error("Fatal programming error in PrintWmf::image at EMRHEADER"); } - x1 = atof(style->object->getAttribute("x")); - y1 = atof(style->object->getAttribute("y")); - dw = atof(style->object->getAttribute("width")); - dh = atof(style->object->getAttribute("height")); + x1 = g_ascii_strtod(style->object->getAttribute("x"), NULL); + y1 = g_ascii_strtod(style->object->getAttribute("y"), NULL); + dw = g_ascii_strtod(style->object->getAttribute("width"), NULL); + dh = g_ascii_strtod(style->object->getAttribute("height"), NULL); Geom::Point pLL(x1, y1); Geom::Point pLL2 = pLL * tf; //location of LL corner in Inkscape coordinates diff --git a/src/filters/image.cpp b/src/filters/image.cpp index 0f15e9d0f..365ad9eb6 100644 --- a/src/filters/image.cpp +++ b/src/filters/image.cpp @@ -17,6 +17,8 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif + +#include <sigc++/bind.h> #include "display/nr-filter-image.h" #include "uri.h" #include "uri-references.h" diff --git a/src/helper/pixbuf-ops.cpp b/src/helper/pixbuf-ops.cpp index a51a62f42..8e611d197 100644 --- a/src/helper/pixbuf-ops.cpp +++ b/src/helper/pixbuf-ops.cpp @@ -16,6 +16,7 @@ #endif #include <png.h> +#include <boost/scoped_ptr.hpp> #include <2geom/transforms.h> #include "interface.h" @@ -67,16 +68,14 @@ bool sp_export_jpg_file(SPDocument *doc, gchar const *filename, unsigned width, unsigned height, double xdpi, double ydpi, unsigned long bgcolor, double quality,GSList *items) { - GdkPixbuf* pixbuf = 0; - pixbuf = sp_generate_internal_bitmap(doc, filename, x0, y0, x1, y1, - width, height, xdpi, ydpi, - bgcolor, items ); + boost::scoped_ptr<Inkscape::Pixbuf> pixbuf( + sp_generate_internal_bitmap(doc, filename, x0, y0, x1, y1, + width, height, xdpi, ydpi, bgcolor, items)); gchar c[32]; g_snprintf(c, 32, "%f", quality); - gboolean saved = gdk_pixbuf_save (pixbuf, filename, "jpeg", NULL, "quality", c, NULL); + gboolean saved = gdk_pixbuf_save(pixbuf->getPixbufRaw(), filename, "jpeg", NULL, "quality", c, NULL); g_free(c); - g_object_unref (pixbuf); return saved; } @@ -94,7 +93,7 @@ bool sp_export_jpg_file(SPDocument *doc, gchar const *filename, @param ydpi @return the created GdkPixbuf structure or NULL if no memory is allocable */ -GdkPixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename*/, +Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename*/, double x0, double y0, double x1, double y1, unsigned width, unsigned height, double xdpi, double ydpi, unsigned long /*bgcolor*/, @@ -103,7 +102,7 @@ GdkPixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename* { if (width == 0 || height == 0) return NULL; - GdkPixbuf* pixbuf = NULL; + Inkscape::Pixbuf *inkpb = NULL; /* Create new drawing for offscreen rendering*/ Inkscape::Drawing drawing; drawing.setExact(true); @@ -146,7 +145,7 @@ GdkPixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename* // render items drawing.render(ct, final_bbox, Inkscape::DrawingItem::RENDER_BYPASS_CACHE); - pixbuf = ink_pixbuf_create_from_cairo_surface(surface); + inkpb = new Inkscape::Pixbuf(surface); } else { @@ -158,7 +157,7 @@ GdkPixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const */*filename* // gdk_pixbuf_save (pixbuf, "C:\\temp\\internal.jpg", "jpeg", NULL, "quality","100", NULL); - return pixbuf; + return inkpb; } /* diff --git a/src/helper/pixbuf-ops.h b/src/helper/pixbuf-ops.h index 44851d388..61a879f9b 100644 --- a/src/helper/pixbuf-ops.h +++ b/src/helper/pixbuf-ops.h @@ -15,11 +15,12 @@ #include <glib.h> class SPDocument; +namespace Inkscape { class Pixbuf; } bool sp_export_jpg_file (SPDocument *doc, gchar const *filename, double x0, double y0, double x1, double y1, unsigned int width, unsigned int height, double xdpi, double ydpi, unsigned long bgcolor, double quality, GSList *items_only = NULL); -GdkPixbuf* sp_generate_internal_bitmap(SPDocument *doc, gchar const *filename, +Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *doc, gchar const *filename, double x0, double y0, double x1, double y1, unsigned width, unsigned height, double xdpi, double ydpi, unsigned long bgcolor, GSList *items_only = NULL); diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 868f5a35c..5ee21a738 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -96,6 +96,7 @@ SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; #include "uri-references.h" #include "display/curve.h" #include "display/canvas-bpath.h" +#include "display/cairo-utils.h" #include "inkscape-private.h" #include "path-chemistry.h" #include "ui/tool/control-point-selection.h" @@ -3480,9 +3481,10 @@ void sp_selection_create_bitmap_copy(SPDesktop *desktop) } // Import the image back - GdkPixbuf *pb = gdk_pixbuf_new_from_file(filepath, NULL); + Inkscape::Pixbuf *pb = Inkscape::Pixbuf::create_from_file(filepath); if (pb) { // Create the repr for the image + // TODO: avoid unnecessary roundtrip between data URI and decoded pixbuf Inkscape::XML::Node * repr = xml_doc->createElement("svg:image"); sp_embed_image(repr, pb); if (res == Inkscape::Util::Quantity::convert(1, "in", "px")) { // for default 90 dpi, snap it to pixel grid diff --git a/src/sp-image.cpp b/src/sp-image.cpp index 0e692eb40..57bcd69b9 100644 --- a/src/sp-image.cpp +++ b/src/sp-image.cpp @@ -17,9 +17,6 @@ # include "config.h" #endif -// This has to be included prior to anything that includes setjmp.h, it croaks otherwise -#include <png.h> - #include <cstring> #include <algorithm> #include <string> @@ -90,8 +87,7 @@ static Inkscape::DrawingItem *sp_image_show (SPItem *item, Inkscape::Drawing &dr static Geom::Affine sp_image_set_transform (SPItem *item, Geom::Affine const &xform); static void sp_image_set_curve(SPImage *image); -static GdkPixbuf *sp_image_repr_read_image( time_t& modTime, gchar*& pixPath, const gchar *href, const gchar *absref, const gchar *base ); -static GdkPixbuf *sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf); +static Inkscape::Pixbuf *sp_image_repr_read_image(gchar const *href, gchar const *absref, gchar const *base ); 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); @@ -130,65 +126,6 @@ extern guint update_in_progress; #define DEBUG_MESSAGE_SCISLAC(key, ...) #endif // DEBUG_LCMS -namespace Inkscape { -namespace IO { - -GdkPixbuf* pixbuf_new_from_file(const char *filename, time_t &modTime, gchar*& pixPath) -{ - GdkPixbuf* buf = NULL; - modTime = 0; - if ( pixPath ) { - g_free(pixPath); - pixPath = NULL; - } - - //test correctness of filename - if (!g_file_test (filename, G_FILE_TEST_EXISTS)){ - return NULL; - } - struct stat stdir; - int val = g_stat(filename, &stdir); - if (stdir.st_mode & S_IFDIR){ - g_warning("Linked image file %s is a directory", filename); - return NULL; - } - - // 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(); - 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_free(data); - g_warning("Error loading pixbuf"); - } - - // 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); - } - - return buf; -} - -} -} - G_DEFINE_TYPE(SPImage, sp_image, SP_TYPE_ITEM); static void sp_image_class_init( SPImageClass * klass ) @@ -229,8 +166,6 @@ static void sp_image_init( SPImage *image ) image->color_profile = 0; #endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) image->pixbuf = 0; - image->pixPath = 0; - image->lastMod = 0; } static void sp_image_build( SPObject *object, SPDocument *document, Inkscape::XML::Node *repr ) @@ -266,8 +201,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); + delete image->pixbuf; image->pixbuf = NULL; } @@ -278,11 +212,6 @@ static void sp_image_release( SPObject *object ) } #endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - if (image->pixPath) { - g_free(image->pixPath); - image->pixPath = 0; - } - if (image->curve) { image->curve = image->curve->unref(); } @@ -427,24 +356,13 @@ static void sp_image_update( SPObject *object, SPCtx *ctx, unsigned int flags ) if (flags & SP_IMAGE_HREF_MODIFIED_FLAG) { if (image->pixbuf) { - g_object_unref (image->pixbuf); + delete image->pixbuf; image->pixbuf = NULL; } - if ( image->pixPath ) { - g_free(image->pixPath); - image->pixPath = 0; - } - image->lastMod = 0; if (image->href) { - GdkPixbuf *pixbuf; + Inkscape::Pixbuf *pixbuf = NULL; pixbuf = sp_image_repr_read_image ( - image->lastMod, - image->pixPath, - - //XML Tree being used directly while it shouldn't be. object->getRepr()->attribute("xlink:href"), - - //XML Tree being used directly while it shouldn't be. object->getRepr()->attribute("sodipodi:absref"), doc->getBase()); if (pixbuf) { @@ -452,10 +370,13 @@ static void sp_image_update( SPObject *object, SPCtx *ctx, unsigned int flags ) #if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) if ( image->color_profile ) { - int imagewidth = gdk_pixbuf_get_width( pixbuf ); - int imageheight = gdk_pixbuf_get_height( pixbuf ); - int rowstride = gdk_pixbuf_get_rowstride( pixbuf ); - guchar* px = gdk_pixbuf_get_pixels( pixbuf ); + // TODO: this will prevent using MIME data when exporting. + // Integrate color correction into loading. + pixbuf->ensurePixelFormat(Inkscape::Pixbuf::PF_GDK); + int imagewidth = pixbuf->width(); + int imageheight = pixbuf->height(); + int rowstride = pixbuf->rowstride();; + guchar* px = pixbuf->pixels(); if ( px ) { DEBUG_MESSAGE( lcmsFive, "in <image>'s sp_image_update. About to call colorprofile_get_handle()" ); @@ -522,10 +443,10 @@ static void sp_image_update( SPObject *object, SPCtx *ctx, unsigned int flags ) if (image->pixbuf) { /* fixme: We are slightly violating spec here (Lauris) */ if (!image->width._set) { - image->width.computed = gdk_pixbuf_get_width(image->pixbuf); + image->width.computed = image->pixbuf->width(); } if (!image->height._set) { - image->height.computed = gdk_pixbuf_get_height(image->pixbuf); + image->height.computed = image->pixbuf->height(); } } @@ -536,8 +457,8 @@ static void sp_image_update( SPObject *object, SPCtx *ctx, unsigned int flags ) image->ox = image->x.computed; image->oy = image->y.computed; - int pixwidth = gdk_pixbuf_get_width (image->pixbuf); - int pixheight = gdk_pixbuf_get_height (image->pixbuf); + int pixwidth = image->pixbuf->width(); + int pixheight = image->pixbuf->height(); image->sx = image->width.computed / pixwidth; image->sy = image->height.computed / pixheight; @@ -678,16 +599,15 @@ static void sp_image_print( SPItem *item, SPPrintContext *ctx ) SPImage *image = SP_IMAGE(item); if (image->pixbuf && (image->width.computed > 0.0) && (image->height.computed > 0.0) ) { - GdkPixbuf *pb = gdk_pixbuf_copy(image->pixbuf); - // GObject data is not copied, so we have to set the pixel format explicitly - g_object_set_data_full(G_OBJECT(pb), "pixel_format", g_strdup("argb32"), g_free); - ink_pixbuf_ensure_normal(pb); + Inkscape::Pixbuf *pb = new Inkscape::Pixbuf(*image->pixbuf); + pb->ensurePixelFormat(Inkscape::Pixbuf::PF_GDK); - guchar *px = gdk_pixbuf_get_pixels(pb); - int w = gdk_pixbuf_get_width(pb); - int h = gdk_pixbuf_get_height(pb); - int rs = gdk_pixbuf_get_rowstride(pb); - int pixskip = gdk_pixbuf_get_n_channels(pb) * gdk_pixbuf_get_bits_per_sample(pb) / 8; + guchar *px = pb->pixels(); + int w = pb->width(); + int h = pb->height(); + int rs = pb->rowstride(); + //int pixskip = gdk_pixbuf_get_n_channels(pb) * gdk_pixbuf_get_bits_per_sample(pb) / 8; + int pixskip = 4; if (image->aspect_align == SP_ASPECT_NONE) { Geom::Affine t; @@ -739,8 +659,8 @@ static gchar *sp_image_description( SPItem *item ) char *ret = ( image->pixbuf == NULL ? g_strdup_printf(_("<b>Image with bad reference</b>: %s"), href_desc) : g_strdup_printf(_("<b>Image</b> %d × %d: %s"), - gdk_pixbuf_get_width(image->pixbuf), - gdk_pixbuf_get_height(image->pixbuf), + image->pixbuf->width(), + image->pixbuf->height(), href_desc) ); g_free(href_desc); return ret; @@ -756,22 +676,9 @@ static Inkscape::DrawingItem *sp_image_show( SPItem *item, Inkscape::Drawing &dr return ai; } -/* - * utility function to try loading image from href - * - * docbase/relative_src - * absolute_src - * - */ - -GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gchar *href, const gchar *absref, const gchar *base ) +Inkscape::Pixbuf *sp_image_repr_read_image(gchar const *href, gchar const *absref, gchar const *base) { - GdkPixbuf *pixbuf = 0; - modTime = 0; - if ( pixPath ) { - g_free(pixPath); - pixPath = 0; - } + Inkscape::Pixbuf *inkpb = 0; gchar const *filename = href; @@ -779,18 +686,18 @@ GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gcha if (strncmp (filename,"file:",5) == 0) { gchar *fullname = g_filename_from_uri(filename, NULL, NULL); if (fullname) { - pixbuf = Inkscape::IO::pixbuf_new_from_file(fullname, modTime, pixPath); + inkpb = Inkscape::Pixbuf::create_from_file(fullname); g_free(fullname); - if (pixbuf != NULL) { - return pixbuf; + if (inkpb != NULL) { + return inkpb; } } } else if (strncmp (filename,"data:",5) == 0) { /* data URI - embedded image */ filename += 5; - pixbuf = sp_image_repr_read_dataURI (filename); - if (pixbuf != NULL) { - return pixbuf; + inkpb = Inkscape::Pixbuf::create_from_data_uri(filename); + if (inkpb != NULL) { + return inkpb; } } else { @@ -806,19 +713,19 @@ 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); + inkpb = Inkscape::Pixbuf::create_from_file(fullname); g_free (fullname); - if (pixbuf != NULL) { - return pixbuf; + if (inkpb != NULL) { + return inkpb; } } } /* 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); - if (pixbuf != NULL) { - return pixbuf; + inkpb = Inkscape::Pixbuf::create_from_file(filename); + if (inkpb != NULL) { + return inkpb; } } } @@ -834,31 +741,20 @@ 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); - if (pixbuf != NULL) { - return pixbuf; + inkpb = Inkscape::Pixbuf::create_from_file(filename); + if (inkpb != NULL) { + return inkpb; } } /* Nope: We do not find any valid pixmap file :-( */ - pixbuf = gdk_pixbuf_new_from_xpm_data((const gchar **) brokenimage_xpm); + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_xpm_data((const gchar **) brokenimage_xpm); + inkpb = new Inkscape::Pixbuf(pixbuf); /* It should be included xpm, so if it still does not does load, */ /* our libraries are broken */ - g_assert (pixbuf != NULL); - - return pixbuf; -} + g_assert (inkpb != NULL); -static GdkPixbuf *sp_image_pixbuf_force_rgba( GdkPixbuf * pixbuf ) -{ - GdkPixbuf* result; - if (gdk_pixbuf_get_has_alpha(pixbuf)) { - result = pixbuf; - } else { - result = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); - g_object_unref(pixbuf); - } - return result; + return inkpb; } /* We assert that realpixbuf is either NULL or identical size to pixbuf */ @@ -866,7 +762,7 @@ static void sp_image_update_arenaitem (SPImage *image, Inkscape::DrawingImage *ai) { ai->setStyle(SP_OBJECT(image)->style); - ai->setARGB32Pixbuf(image->pixbuf); + ai->setPixbuf(image->pixbuf); ai->setOrigin(Geom::Point(image->ox, image->oy)); ai->setScale(image->sx, image->sy); ai->setClipbox(image->clipbox); @@ -957,113 +853,6 @@ static Geom::Affine sp_image_set_transform( SPItem *item, Geom::Affine const &xf return ret; } -static GdkPixbuf *sp_image_repr_read_dataURI( const gchar * uri_data ) -{ - GdkPixbuf * pixbuf = NULL; - - gint data_is_image = 0; - gint data_is_base64 = 0; - - const gchar * data = uri_data; - - while (*data) { - if (strncmp(data,"base64",6) == 0) { - /* base64-encoding */ - data_is_base64 = 1; - data_is_image = 1; // Illustrator produces embedded images without MIME type, so we assume it's image no matter what - data += 6; - } - else if (strncmp(data,"image/png",9) == 0) { - /* PNG image */ - data_is_image = 1; - data += 9; - } - else if (strncmp(data,"image/jpg",9) == 0) { - /* JPEG image */ - data_is_image = 1; - data += 9; - } - else if (strncmp(data,"image/jpeg",10) == 0) { - /* JPEG image */ - data_is_image = 1; - data += 10; - } - else { /* unrecognized option; skip it */ - while (*data) { - if (((*data) == ';') || ((*data) == ',')) { - break; - } - data++; - } - } - if ((*data) == ';') { - data++; - continue; - } - if ((*data) == ',') { - data++; - break; - } - } - - if ((*data) && data_is_image && data_is_base64) { - pixbuf = sp_image_repr_read_b64(data); - } - - return pixbuf; -} - -static GdkPixbuf *sp_image_repr_read_b64(gchar const *uri_data) -{ - GdkPixbuf *pixbuf = NULL; - GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); - - if (!loader) return NULL; - - gsize decoded_len = 0; - guchar *decoded = g_base64_decode(uri_data, &decoded_len); - - 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 @@ -1099,31 +888,16 @@ SPCurve *sp_image_get_curve( SPImage *image ) return result; } -void sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb) +void sp_embed_image(Inkscape::XML::Node *image_node, Inkscape::Pixbuf *pb) { - 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) { - unsigned long len_long = 0; - cairo_surface_get_mime_data(s, mimetypes[i], const_cast<unsigned char const **>(&data), &len_long); - len = len_long; // this assumes that the added range of long is not needed. the code below assumes gsize range of values is sufficient. - if (data != NULL) { - data_mimetype = mimetypes[i]; - break; - } - } - } + std::string data_mimetype; + + data = const_cast<guchar *>(pb->getMimeData(len, data_mimetype)); if (data == NULL) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -1131,8 +905,7 @@ void sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb) // 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, + gdk_pixbuf_save_to_buffer(pb->getPixbufRaw(), reinterpret_cast<gchar**>(&data), &len, "png", NULL, "quality", quality.c_str(), NULL); free_data = true; } @@ -1140,11 +913,11 @@ void sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb) // Save base64 encoded data in image node // this formula taken from Glib docs gsize needed_size = len * 4 / 3 + len * 4 / (3 * 72) + 7; - needed_size += 5 + 8 + strlen(data_mimetype); // 5 bytes for data: + 8 for ;base64, + needed_size += 5 + 8 + data_mimetype.size(); // 5 bytes for data: + 8 for ;base64, gchar *buffer = (gchar *) g_malloc(needed_size); gchar *buf_work = buffer; - buf_work += g_sprintf(buffer, "data:%s;base64,", data_mimetype); + buf_work += g_sprintf(buffer, "data:%s;base64,", data_mimetype.c_str()); gint state = 0; gint save = 0; @@ -1164,18 +937,18 @@ void sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb) void sp_image_refresh_if_outdated( SPImage* image ) { - if ( image->href && image->lastMod ) { + if ( image->href && image->pixbuf && image->pixbuf->modificationTime()) { // It *might* change struct stat st; memset(&st, 0, sizeof(st)); int val = 0; - if (g_file_test (image->pixPath, G_FILE_TEST_EXISTS)){ - val = g_stat(image->pixPath, &st); + if (g_file_test (image->pixbuf->originalPath().c_str(), G_FILE_TEST_EXISTS)){ + val = g_stat(image->pixbuf->originalPath().c_str(), &st); } if ( !val ) { // stat call worked. Check time now - if ( st.st_mtime != image->lastMod ) { + if ( st.st_mtime != image->pixbuf->modificationTime() ) { SPCtx *ctx = 0; unsigned int flags = SP_IMAGE_HREF_MODIFIED_FLAG; sp_image_update(image, ctx, flags); diff --git a/src/sp-image.h b/src/sp-image.h index c197f6473..d137c7bf4 100644 --- a/src/sp-image.h +++ b/src/sp-image.h @@ -1,9 +1,6 @@ -#ifndef __SP_IMAGE_H__ -#define __SP_IMAGE_H__ - -/* +/** @file * SVG <image> implementation - * + *//* * Authors: * Lauris Kaplinski <lauris@kaplinski.com> * Edward Flick (EAF) @@ -14,21 +11,24 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#define SP_TYPE_IMAGE (sp_image_get_type ()) -#define SP_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_IMAGE, SPImage)) -#define SP_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_IMAGE, SPImageClass)) -#define SP_IS_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_IMAGE)) -#define SP_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_IMAGE)) - -/* SPImage */ +#ifndef SEEN_INKSCAPE_SP_IMAGE_H +#define SEEN_INKSCAPE_SP_IMAGE_H #include <gdk-pixbuf/gdk-pixbuf.h> #include <glibmm/ustring.h> #include "svg/svg-length.h" #include "sp-item.h" +#define SP_TYPE_IMAGE (sp_image_get_type ()) +#define SP_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_IMAGE, SPImage)) +#define SP_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_IMAGE, SPImageClass)) +#define SP_IS_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_IMAGE)) +#define SP_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_IMAGE)) + #define SP_IMAGE_HREF_MODIFIED_FLAG SP_OBJECT_USER_MODIFIED_FLAG_A +namespace Inkscape { class Pixbuf; } + struct SPImage : public SPItem { SVGLength x; SVGLength y; @@ -53,9 +53,7 @@ struct SPImage : public SPItem { gchar *color_profile; #endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - GdkPixbuf *pixbuf; - gchar *pixPath; - time_t lastMod; + Inkscape::Pixbuf *pixbuf; }; struct SPImageClass { @@ -66,7 +64,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); +void sp_embed_image(Inkscape::XML::Node *imgnode, Inkscape::Pixbuf *pb); void sp_image_refresh_if_outdated( SPImage* image ); #endif diff --git a/src/trace/trace.cpp b/src/trace/trace.cpp index cad8ea9be..e2cda6247 100644 --- a/src/trace/trace.cpp +++ b/src/trace/trace.cpp @@ -31,6 +31,7 @@ #include <2geom/transforms.h> #include "verbs.h" +#include "display/cairo-utils.h" #include "display/drawing.h" #include "display/drawing-shape.h" @@ -336,8 +337,17 @@ Glib::RefPtr<Gdk::Pixbuf> Tracer::getSelectedImage() if (!img->pixbuf) return Glib::RefPtr<Gdk::Pixbuf>(NULL); - Glib::RefPtr<Gdk::Pixbuf> pixbuf = - Glib::wrap(img->pixbuf, true); + GdkPixbuf *raw_pb = img->pixbuf->getPixbufRaw(false); + GdkPixbuf *trace_pb = gdk_pixbuf_copy(raw_pb); + if (img->pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) { + convert_pixels_argb32_to_pixbuf( + gdk_pixbuf_get_pixels(trace_pb), + gdk_pixbuf_get_width(trace_pb), + gdk_pixbuf_get_height(trace_pb), + gdk_pixbuf_get_rowstride(trace_pb)); + } + + Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(trace_pb, false); if (sioxEnabled) { @@ -410,7 +420,16 @@ void Tracer::traceThread() return; } - Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(img->pixbuf, true); + GdkPixbuf *trace_pb = gdk_pixbuf_copy(img->pixbuf->getPixbufRaw(false)); + if (img->pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) { + convert_pixels_argb32_to_pixbuf( + gdk_pixbuf_get_pixels(trace_pb), + gdk_pixbuf_get_width(trace_pb), + gdk_pixbuf_get_height(trace_pb), + gdk_pixbuf_get_rowstride(trace_pb)); + } + + Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(trace_pb, false); pixbuf = sioxProcessImage(img, pixbuf); |
