diff options
| author | Sebastian Wüst <sebi@timewaster.de> | 2013-10-20 15:32:08 +0000 |
|---|---|---|
| committer | Sebastian Wüst <sebi@timewaster.de> | 2013-10-20 15:32:08 +0000 |
| commit | 82908f949129e1fcbf62002799ee7b1b77986eed (patch) | |
| tree | c02098dd7720cdf424f2793ecd3ddac2ea86b969 /src/display | |
| parent | changed text (diff) | |
| parent | Fix build errors with clang 3.3 and c++11 enabled. (diff) | |
| download | inkscape-82908f949129e1fcbf62002799ee7b1b77986eed.tar.gz inkscape-82908f949129e1fcbf62002799ee7b1b77986eed.zip | |
merge from trunk
(bzr r12417.1.24)
Diffstat (limited to 'src/display')
30 files changed, 1256 insertions, 792 deletions
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index fc56c7b26..5b358ade7 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,16 +30,15 @@ #include "helper/geom-curves.h" #include "display/cairo-templates.h" -namespace { +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: * http://www.cairographics.org/manual/cairo-Types.html#cairo-user-data-key-t */ -cairo_user_data_key_t ci_key; - -} // namespace +cairo_user_data_key_t ink_color_interpolation_key; +cairo_user_data_key_t ink_pixbuf_key; namespace Inkscape { @@ -119,6 +120,379 @@ 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) { + 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); + GdkPixbuf *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_long); + if (data != NULL) { + len = len_long; + 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 /* @@ -291,7 +665,7 @@ feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv) SPColorInterpolation get_cairo_surface_ci(cairo_surface_t *surface) { - void* data = cairo_surface_get_user_data( surface, &ci_key ); + void* data = cairo_surface_get_user_data( surface, &ink_color_interpolation_key ); if( data != NULL ) { return (SPColorInterpolation)GPOINTER_TO_INT( data ); } else { @@ -317,13 +691,13 @@ set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation ci) { ink_cairo_surface_linear_to_srgb( surface ); } - cairo_surface_set_user_data(surface, &ci_key, GINT_TO_POINTER (ci), NULL); + cairo_surface_set_user_data(surface, &ink_color_interpolation_key, GINT_TO_POINTER (ci), NULL); } } void copy_cairo_surface_ci(cairo_surface_t *in, cairo_surface_t *out) { - cairo_surface_set_user_data(out, &ci_key, cairo_surface_get_user_data(in, &ci_key), NULL); + cairo_surface_set_user_data(out, &ink_color_interpolation_key, cairo_surface_get_user_data(in, &ink_color_interpolation_key), NULL); } void @@ -374,39 +748,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_argb32_pixbuf(cairo_t *ct, GdkPixbuf *pb, double x, double y) -{ - cairo_surface_t *pbs = ink_cairo_surface_create_for_argb32_pixbuf(pb); - cairo_set_source_surface(ct, pbs, x, y); - cairo_surface_destroy(pbs); -} - -cairo_surface_t * -ink_cairo_surface_create_for_argb32_pixbuf(GdkPixbuf *pb) -{ - 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); - - cairo_surface_t *pbs = cairo_image_surface_create_for_data( - data, CAIRO_FORMAT_ARGB32, w, h, stride); - return pbs; -} - -/** - * 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 @@ -445,7 +786,7 @@ cairo_surface_t * ink_cairo_surface_create_identical(cairo_surface_t *s) { cairo_surface_t *ns = ink_cairo_surface_create_same_size(s, cairo_surface_get_content(s)); - cairo_surface_set_user_data(ns, &ci_key, cairo_surface_get_user_data(s, &ci_key), NULL); + cairo_surface_set_user_data(ns, &ink_color_interpolation_key, cairo_surface_get_user_data(s, &ink_color_interpolation_key), NULL); return ns; } @@ -746,6 +1087,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. */ @@ -845,13 +1224,20 @@ convert_pixels_argb32_to_pixbuf(guchar *data, int w, int h, int stride) * using it with GTK will result in corrupted drawings. */ void -convert_pixbuf_normal_to_argb32(GdkPixbuf *pb) +ink_pixbuf_ensure_argb32(GdkPixbuf *pb) { + gchar *pixel_format = reinterpret_cast<gchar*>(g_object_get_data(G_OBJECT(pb), "pixel_format")); + if (pixel_format != NULL && strcmp(pixel_format, "argb32") == 0) { + // nothing to do + return; + } + convert_pixels_pixbuf_to_argb32( gdk_pixbuf_get_pixels(pb), gdk_pixbuf_get_width(pb), gdk_pixbuf_get_height(pb), gdk_pixbuf_get_rowstride(pb)); + g_object_set_data_full(G_OBJECT(pb), "pixel_format", g_strdup("argb32"), g_free); } /** @@ -859,13 +1245,20 @@ convert_pixbuf_normal_to_argb32(GdkPixbuf *pb) * Once this is done, the pixbuf can be used with GTK again. */ void -convert_pixbuf_argb32_to_normal(GdkPixbuf *pb) +ink_pixbuf_ensure_normal(GdkPixbuf *pb) { + gchar *pixel_format = reinterpret_cast<gchar*>(g_object_get_data(G_OBJECT(pb), "pixel_format")); + if (pixel_format == NULL || strcmp(pixel_format, "pixbuf") == 0) { + // nothing to do + return; + } + convert_pixels_argb32_to_pixbuf( gdk_pixbuf_get_pixels(pb), gdk_pixbuf_get_width(pb), gdk_pixbuf_get_height(pb), gdk_pixbuf_get_rowstride(pb)); + g_object_set_data_full(G_OBJECT(pb), "pixel_format", g_strdup("pixbuf"), g_free); } guint32 argb32_from_rgba(guint32 in) diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h index 016f72d00..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,8 +81,65 @@ public: static Cairo::RefPtr<CairoContext> create(Cairo::RefPtr<Cairo::Surface> const &target); }; +/** 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; } + + 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; + SPColorInterpolation get_cairo_surface_ci(cairo_surface_t *surface); void set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation cif); void copy_cairo_surface_ci(cairo_surface_t *in, cairo_surface_t *out); @@ -91,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_argb32_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 &); @@ -114,12 +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 convert_pixbuf_normal_to_argb32(GdkPixbuf *); -void convert_pixbuf_argb32_to_normal(GdkPixbuf *); -cairo_surface_t *ink_cairo_surface_create_for_argb32_pixbuf(GdkPixbuf *pb); -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/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index 1eadd3fd2..d66b97bbc 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -51,8 +51,9 @@ #include "2geom/angle.h" #include "util/mathfns.h" #include "round.h" -#include "helper/units.h" +#include "util/units.h" +using Inkscape::Util::unit_table; enum Dim3 { X=0, Y, Z }; @@ -160,15 +161,16 @@ CanvasAxonomGrid::CanvasAxonomGrid (SPNamedView * nv, Inkscape::XML::Node * in_r : CanvasGrid(nv, in_repr, in_doc, GRID_AXONOMETRIC) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - gridunit = sp_unit_get_by_abbreviation( prefs->getString("/options/grids/axonom/units").data() ); - if (!gridunit) - gridunit = &sp_unit_get_by_id(SP_UNIT_PX); - origin[Geom::X] = sp_units_get_pixels( prefs->getDouble("/options/grids/axonom/origin_x", 0.0), *gridunit ); - origin[Geom::Y] = sp_units_get_pixels( prefs->getDouble("/options/grids/axonom/origin_y", 0.0), *gridunit ); + gridunit = unit_table.getUnit(prefs->getString("/options/grids/axonom/units")); + if (!gridunit) { + gridunit = unit_table.getUnit("px"); + } + origin[Geom::X] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/axonom/origin_x", 0.0), gridunit, "px"); + origin[Geom::Y] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/axonom/origin_y", 0.0), gridunit, "px"); color = prefs->getInt("/options/grids/axonom/color", 0x0000ff20); empcolor = prefs->getInt("/options/grids/axonom/empcolor", 0x0000ff40); empspacing = prefs->getInt("/options/grids/axonom/empspacing", 5); - lengthy = sp_units_get_pixels( prefs->getDouble("/options/grids/axonom/spacing_y", 1.0), *gridunit ); + lengthy = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/axonom/spacing_y", 1.0), gridunit, "px"); angle_deg[X] = prefs->getDouble("/options/grids/axonom/angle_x", 30.0); angle_deg[Z] = prefs->getDouble("/options/grids/axonom/angle_z", 30.0); angle_deg[Y] = 0; @@ -188,63 +190,6 @@ CanvasAxonomGrid::~CanvasAxonomGrid () if (snapper) delete snapper; } - -/* fixme: Collect all these length parsing methods and think common sane API */ - -static gboolean sp_nv_read_length(gchar const *str, guint base, gdouble *val, SPUnit const **unit) -{ - if (!str) { - return FALSE; - } - - gchar *u; - gdouble v = g_ascii_strtod(str, &u); - if (!u) { - return FALSE; - } - while (isspace(*u)) { - u += 1; - } - - if (!*u) { - /* No unit specified - keep default */ - *val = v; - return TRUE; - } - - if (base & SP_UNIT_DEVICE) { - if (u[0] && u[1] && !isalnum(u[2]) && !strncmp(u, "px", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_PX); - *val = v; - return TRUE; - } - } - - if (base & SP_UNIT_ABSOLUTE) { - if (!strncmp(u, "pt", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_PT); - } else if (!strncmp(u, "mm", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_MM); - } else if (!strncmp(u, "cm", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_CM); - } else if (!strncmp(u, "m", 1)) { - *unit = &sp_unit_get_by_id(SP_UNIT_M); - } else if (!strncmp(u, "in", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_IN); - } else if (!strncmp(u, "ft", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_FT); - } else if (!strncmp(u, "pc", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_PC); - } else { - return FALSE; - } - *val = v; - return TRUE; - } - - return FALSE; -} - static gboolean sp_nv_read_opacity(gchar const *str, guint32 *color) { if (!str) { @@ -270,17 +215,20 @@ CanvasAxonomGrid::readRepr() { gchar const *value; if ( (value = repr->attribute("originx")) ) { - sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[Geom::X], &gridunit); - origin[Geom::X] = sp_units_get_pixels(origin[Geom::X], *(gridunit)); + Inkscape::Util::Quantity q = unit_table.parseQuantity(value); + gridunit = q.unit; + origin[Geom::X] = q.value("px"); } if ( (value = repr->attribute("originy")) ) { - sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[Geom::Y], &gridunit); - origin[Geom::Y] = sp_units_get_pixels(origin[Geom::Y], *(gridunit)); + Inkscape::Util::Quantity q = unit_table.parseQuantity(value); + gridunit = q.unit; + origin[Geom::Y] = q.value("px"); } if ( (value = repr->attribute("spacingy")) ) { - sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &lengthy, &gridunit); - lengthy = sp_units_get_pixels(lengthy, *(gridunit)); + Inkscape::Util::Quantity q = unit_table.parseQuantity(value); + gridunit = q.unit; + lengthy = q.value("px"); if (lengthy < 0.0500) lengthy = 0.0500; } @@ -419,17 +367,17 @@ _wr.setUpdating (false); attach_all (*table, widget_array, sizeof(widget_array)); // set widget values - _rumg->setUnit (gridunit); + _rumg->setUnit (gridunit->abbr); gdouble val; val = origin[Geom::X]; - val = sp_pixels_get_units (val, *(gridunit)); + val = Inkscape::Util::Quantity::convert(val, "px", gridunit); _rsu_ox->setValue (val); val = origin[Geom::Y]; - val = sp_pixels_get_units (val, *(gridunit)); + val = Inkscape::Util::Quantity::convert(val, "px", gridunit); _rsu_oy->setValue (val); val = lengthy; - double gridy = sp_pixels_get_units (val, *(gridunit)); + double gridy = Inkscape::Util::Quantity::convert(val, "px", gridunit); _rsu_sy->setValue (gridy); _rsu_ax->setValue(angle_deg[X]); @@ -458,17 +406,17 @@ CanvasAxonomGrid::updateWidgets() _rcb_enabled.setActive(snapper->getEnabled()); } - _rumg.setUnit (gridunit); + _rumg.setUnit (gridunit->abbr); gdouble val; val = origin[Geom::X]; - val = sp_pixels_get_units (val, *(gridunit)); + val = Inkscape::Util::Quantity::convert(val, &px, gridunit); _rsu_ox.setValue (val); val = origin[Geom::Y]; - val = sp_pixels_get_units (val, *(gridunit)); + val = Inkscape::Util::Quantity::convert(val, &px, gridunit); _rsu_oy.setValue (val); val = lengthy; - double gridy = sp_pixels_get_units (val, *(gridunit)); + double gridy = Inkscape::Util::Quantity::convert(val, &px, gridunit); _rsu_sy.setValue (gridy); _rsu_ax.setValue(angle_deg[X]); diff --git a/src/display/canvas-axonomgrid.h b/src/display/canvas-axonomgrid.h index f58ea3aca..4e5af863d 100644 --- a/src/display/canvas-axonomgrid.h +++ b/src/display/canvas-axonomgrid.h @@ -14,7 +14,7 @@ struct SPCanvasBuf; class SPDesktop; -struct SPNamedView; +class SPNamedView; namespace Inkscape { namespace XML { diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index ee5ad0945..192cc4cba 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -42,7 +42,7 @@ #include "display/canvas-grid.h" #include "display/sp-canvas-group.h" #include "document.h" -#include "helper/units.h" +#include "util/units.h" #include "inkscape.h" #include "preferences.h" #include "sp-namedview.h" @@ -55,6 +55,7 @@ #include "display/sp-canvas.h" using Inkscape::DocumentUndo; +using Inkscape::Util::unit_table; namespace Inkscape { @@ -398,11 +399,11 @@ void CanvasGrid::setOrigin(Geom::Point const &origin_px) gdouble val; val = origin_px[Geom::X]; - val = sp_pixels_get_units (val, *gridunit); - os_x << val << sp_unit_get_abbreviation(gridunit); + val = Inkscape::Util::Quantity::convert(val, "px", gridunit); + os_x << val << gridunit->abbr; val = origin_px[Geom::Y]; - val = sp_pixels_get_units (val, *gridunit); - os_y << val << sp_unit_get_abbreviation(gridunit); + val = Inkscape::Util::Quantity::convert(val, "px", gridunit); + os_y << val << gridunit->abbr; repr->setAttribute("originx", os_x.str().c_str()); repr->setAttribute("originy", os_y.str().c_str()); } @@ -488,17 +489,17 @@ CanvasXYGrid::CanvasXYGrid (SPNamedView * nv, Inkscape::XML::Node * in_repr, SPD : CanvasGrid(nv, in_repr, in_doc, GRID_RECTANGULAR) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - gridunit = sp_unit_get_by_abbreviation( prefs->getString("/options/grids/xy/units").data() ); + gridunit = unit_table.getUnit(prefs->getString("/options/grids/xy/units")); if (!gridunit) { - gridunit = &sp_unit_get_by_id(SP_UNIT_PX); + gridunit = unit_table.getUnit("px"); } - origin[Geom::X] = sp_units_get_pixels(prefs->getDouble("/options/grids/xy/origin_x", 0.0), *gridunit); - origin[Geom::Y] = sp_units_get_pixels(prefs->getDouble("/options/grids/xy/origin_y", 0.0), *gridunit); + origin[Geom::X] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/xy/origin_x", 0.0), gridunit, "px"); + origin[Geom::Y] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/xy/origin_y", 0.0), gridunit, "px"); color = prefs->getInt("/options/grids/xy/color", 0x0000ff20); empcolor = prefs->getInt("/options/grids/xy/empcolor", 0x0000ff40); empspacing = prefs->getInt("/options/grids/xy/empspacing", 5); - spacing[Geom::X] = sp_units_get_pixels(prefs->getDouble("/options/grids/xy/spacing_x", 0.0), *gridunit); - spacing[Geom::Y] = sp_units_get_pixels(prefs->getDouble("/options/grids/xy/spacing_y", 0.0), *gridunit); + spacing[Geom::X] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/xy/spacing_x", 0.0), gridunit, "px"); + spacing[Geom::Y] = Inkscape::Util::Quantity::convert(prefs->getDouble("/options/grids/xy/spacing_y", 0.0), gridunit, "px"); render_dotted = prefs->getBool("/options/grids/xy/dotted", false); snapper = new CanvasXYGridSnapper(this, &namedview->snap_manager, 0); @@ -511,64 +512,6 @@ CanvasXYGrid::~CanvasXYGrid () if (snapper) delete snapper; } - -/* fixme: Collect all these length parsing methods and think common sane API */ - -static gboolean -sp_nv_read_length(gchar const *str, guint base, gdouble *val, SPUnit const **unit) -{ - if (!str) { - return FALSE; - } - - gchar *u; - gdouble v = g_ascii_strtod(str, &u); - if (!u) { - return FALSE; - } - while (isspace(*u)) { - u += 1; - } - - if (!*u) { - /* No unit specified - keep default */ - *val = v; - return TRUE; - } - - if (base & SP_UNIT_DEVICE) { - if (u[0] && u[1] && !isalnum(u[2]) && !strncmp(u, "px", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_PX); - *val = v; - return TRUE; - } - } - - if (base & SP_UNIT_ABSOLUTE) { - if (!strncmp(u, "pt", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_PT); - } else if (!strncmp(u, "mm", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_MM); - } else if (!strncmp(u, "cm", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_CM); - } else if (!strncmp(u, "m", 1)) { - *unit = &sp_unit_get_by_id(SP_UNIT_M); - } else if (!strncmp(u, "in", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_IN); - } else if (!strncmp(u, "ft", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_FT); - } else if (!strncmp(u, "pc", 2)) { - *unit = &sp_unit_get_by_id(SP_UNIT_PC); - } else { - return FALSE; - } - *val = v; - return TRUE; - } - - return FALSE; -} - static gboolean sp_nv_read_opacity(gchar const *str, guint32 *color) { if (!str) { @@ -645,28 +588,32 @@ CanvasXYGrid::readRepr() { gchar const *value; if ( (value = repr->attribute("originx")) ) { - sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[Geom::X], &gridunit); - origin[Geom::X] = sp_units_get_pixels(origin[Geom::X], *(gridunit)); + Inkscape::Util::Quantity q = unit_table.parseQuantity(value); + gridunit = q.unit; + origin[Geom::X] = q.value("px"); } if ( (value = repr->attribute("originy")) ) { - sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[Geom::Y], &gridunit); - origin[Geom::Y] = sp_units_get_pixels(origin[Geom::Y], *(gridunit)); + Inkscape::Util::Quantity q = unit_table.parseQuantity(value); + gridunit = q.unit; + origin[Geom::Y] = q.value("px"); } if ( (value = repr->attribute("spacingx")) ) { double oldVal = spacing[Geom::X]; - sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[Geom::X], &gridunit); - validateScalar( oldVal, &spacing[Geom::X]); - spacing[Geom::X] = sp_units_get_pixels(spacing[Geom::X], *(gridunit)); - + Inkscape::Util::Quantity q = unit_table.parseQuantity(value); + gridunit = q.unit; + spacing[Geom::X] = q.quantity; + validateScalar(oldVal, &spacing[Geom::X]); + spacing[Geom::X] = Inkscape::Util::Quantity::convert(spacing[Geom::X], gridunit, "px"); } if ( (value = repr->attribute("spacingy")) ) { double oldVal = spacing[Geom::Y]; - sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[Geom::Y], &gridunit); - validateScalar( oldVal, &spacing[Geom::Y]); - spacing[Geom::Y] = sp_units_get_pixels(spacing[Geom::Y], *(gridunit)); - + Inkscape::Util::Quantity q = unit_table.parseQuantity(value); + gridunit = q.unit; + spacing[Geom::Y] = q.quantity; + validateScalar(oldVal, &spacing[Geom::Y]); + spacing[Geom::Y] = Inkscape::Util::Quantity::convert(spacing[Geom::Y], gridunit, "px"); } if ( (value = repr->attribute("color")) ) { @@ -802,20 +749,20 @@ CanvasXYGrid::newSpecificWidget() attach_all (*table, widget_array, sizeof(widget_array)); // set widget values - _rumg->setUnit (gridunit); + _rumg->setUnit (gridunit->abbr); gdouble val; val = origin[Geom::X]; - val = sp_pixels_get_units (val, *(gridunit)); + val = Inkscape::Util::Quantity::convert(val, "px", gridunit); _rsu_ox->setValue (val); val = origin[Geom::Y]; - val = sp_pixels_get_units (val, *(gridunit)); + val = Inkscape::Util::Quantity::convert(val, "px", gridunit); _rsu_oy->setValue (val); val = spacing[Geom::X]; - double gridx = sp_pixels_get_units (val, *(gridunit)); + double gridx = Inkscape::Util::Quantity::convert(val, "px", gridunit); _rsu_sx->setValue (gridx); val = spacing[Geom::Y]; - double gridy = sp_pixels_get_units (val, *(gridunit)); + double gridy = Inkscape::Util::Quantity::convert(val, "px", gridunit); _rsu_sy->setValue (gridy); _rcp_gcol->setRgba32 (color); @@ -851,20 +798,20 @@ CanvasXYGrid::updateWidgets() _rcb_enabled.setActive(snapper->getEnabled()); } - _rumg.setUnit (gridunit); + _rumg.setUnit (gridunit->abbr); gdouble val; val = origin[Geom::X]; - val = sp_pixels_get_units (val, *(gridunit)); + val = Inkscape::Quantity::convert(val, "px", *gridunit); _rsu_ox.setValue (val); val = origin[Geom::Y]; - val = sp_pixels_get_units (val, *(gridunit)); + val = Inkscape::Quantity::convert(val, "px", *gridunit); _rsu_oy.setValue (val); val = spacing[Geom::X]; - double gridx = sp_pixels_get_units (val, *(gridunit)); + double gridx = Inkscape::Quantity::convert(val, "px", *gridunit); _rsu_sx.setValue (gridx); val = spacing[Geom::Y]; - double gridy = sp_pixels_get_units (val, *(gridunit)); + double gridy = Inkscape::Quantity::convert(val, "px", *gridunit); _rsu_sy.setValue (gridy); _rcp_gcol.setRgba32 (color); diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h index 7eaef407f..078670da7 100644 --- a/src/display/canvas-grid.h +++ b/src/display/canvas-grid.h @@ -13,7 +13,7 @@ #include "line-snapper.h" class SPDesktop; -struct SPNamedView; +class SPNamedView; struct SPCanvasBuf; class SPDocument; @@ -28,6 +28,10 @@ namespace XML { class Node; } +namespace Util { +class Unit; +} + enum GridType { GRID_RECTANGULAR = 0, GRID_AXONOMETRIC = 1 @@ -88,7 +92,7 @@ public: guint32 empcolor; /**< Color for emphasis lines */ gint empspacing; /**< Spacing between emphasis lines */ - SPUnit const* gridunit; + Inkscape::Util::Unit const* gridunit; /**< points to Unit object in UnitTable (so don't delete it) */ Inkscape::XML::Node * repr; SPDocument *doc; diff --git a/src/display/drawing-context.h b/src/display/drawing-context.h index b8c4667c2..d4f0dbfc3 100644 --- a/src/display/drawing-context.h +++ b/src/display/drawing-context.h @@ -67,6 +67,21 @@ public: void rectangle(Geom::IntRect const &r) { cairo_rectangle(_ct, r.left(), r.top(), r.width(), r.height()); } + // Used in drawing-text.cpp to overwrite glyphs, which have the opposite path rotation as a regular rect + void revrectangle(Geom::Rect const &r) { + cairo_move_to ( _ct, r.left(), r.top() ); + cairo_rel_line_to (_ct, 0, r.height() ); + cairo_rel_line_to (_ct, r.width(), 0 ); + cairo_rel_line_to (_ct, 0, -r.height() ); + cairo_close_path ( _ct); + } + void revrectangle(Geom::IntRect const &r) { + cairo_move_to ( _ct, r.left(), r.top() ); + cairo_rel_line_to (_ct, 0, r.height() ); + cairo_rel_line_to (_ct, r.width(), 0 ); + cairo_rel_line_to (_ct, 0, -r.height() ); + cairo_close_path ( _ct); + } void newPath() { cairo_new_path(_ct); } void newSubpath() { cairo_new_sub_path(_ct); } void path(Geom::PathVector const &pv); diff --git a/src/display/drawing-group.cpp b/src/display/drawing-group.cpp index 6d52b89fc..b5ce18891 100644 --- a/src/display/drawing-group.cpp +++ b/src/display/drawing-group.cpp @@ -28,6 +28,7 @@ DrawingGroup::~DrawingGroup() { if (_style) sp_style_unref(_style); + delete _child_transform; // delete NULL; is safe } /** diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 753249e60..a9c0499c2 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -9,6 +9,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <2geom/bezier-curve.h> #include "display/cairo-utils.h" #include "display/drawing.h" #include "display/drawing-context.h" @@ -21,35 +22,22 @@ namespace Inkscape { DrawingImage::DrawingImage(Drawing &drawing) : DrawingItem(drawing) , _pixbuf(NULL) - , _surface(NULL) , _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); - cairo_surface_destroy(_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); - cairo_surface_destroy(_surface); - } _pixbuf = pb; - _surface = pb ? ink_cairo_surface_create_for_argb32_pixbuf(pb) : NULL; _markForUpdate(STATE_ALL, false); } @@ -86,8 +74,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); @@ -134,121 +122,11 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar ct.rectangle(_clipbox); ct.clip(); - ///////////////////////////////////////////////////////////////////////////// - // BEGIN: Hack to avoid Cairo bug - // The total transform (which is RIGHT-multiplied with the item points to get display points) equals: - // scale*translate_origin*_ctm = scale*translate(origin)*expansion*expansionInv*_ctm - // = scale*expansion*translate(origin*expansion)*expansionInv*_ctm - // To avoid a Cairo bug, we handle the scale*expansion part ourselves. - // 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); + ct.translate(_origin); + ct.scale(_scale); + ct.setSource(_pixbuf->getSurfaceRaw(), 0, 0); - 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; - } - - // 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]); - - Geom::Point newSize(Geom::Point(orgwidth,orgheight)*scaleExpansionSmall); - if ((newSize-Geom::Point(orgwidth,orgheight)).length()<0.1) { - // Just use _surface directly. - ct.scale(expansion.inverse()); // This should not include scale (see derivation above) - ct.translate(_origin*expansion); - ct.scale(scaleExpansionLarge); - ct.setSource(_surface, 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); - _rescaledSize = newSize; - - // This essentially considers an image to be composed of rectangular pixels (box kernel) and computes the least-squares approximation of the original. - // When the scale factor is really large or small this essentially results in using a box filter, while for scale factors approaching 1 it is more like a "tent" kernel. - // Although the quality of the result is not great, it is typically better than an ordinary box filter, and it is guaranteed to preserve the overall brightness of the image. - // The best improvement would probably be to do the same kind of thing based on a tent kernel, but that's quite a bit more complicated, and probably not worth the trouble for a hack like this. - int newwidth = static_cast<int>(floor(orgwidth*scaleExpansionSmall[Geom::X])+1); - int newheight = static_cast<int>(floor(orgheight*scaleExpansionSmall[Geom::Y])+1); - std::vector<int> xBegin(newwidth, -1), yBegin(newheight, -1); - std::vector< std::vector<float> > xCoefs(xBegin.size()), yCoefs(yBegin.size()); - for(int x=0; x<orgwidth; x++) { - double coordBegin = x*static_cast<double>(scaleExpansionSmall[Geom::X]); // x-coord in target coordinates where the current source pixel begins - double coordEnd = (x+1)*static_cast<double>(scaleExpansionSmall[Geom::X]); // x-coord in target coordinates where the current source pixel ends - int begin = static_cast<int>(floor(coordBegin)); // First pixel (x-coord) affected by the current source pixel - int end = static_cast<int>(ceil(coordEnd)); // First pixel (x-coord) NOT affected by the current source pixel (a zero contribution is counted as not affecting the pixel) - for(int nx=begin; nx<end; nx++) { - // Set xBegin if this is the first source pixel contributing to the target pixel. - if (xBegin[nx]==-1) xBegin[nx] = x; - // This computes the fraction of the current target pixel (at nx) that is covered by the source pixel (at x). - xCoefs[nx].push_back(static_cast<float>(std::min<double>(nx+1,coordEnd) - std::max<double>(nx,coordBegin))); - } - } - for(int y=0; y<orgheight; y++) { - double coordBegin = y*static_cast<double>(scaleExpansionSmall[Geom::Y]); // y-coord in target coordinates where the current source pixel begins - double coordEnd = (y+1)*static_cast<double>(scaleExpansionSmall[Geom::Y]); // y-coord in target coordinates where the current source pixel ends - int begin = static_cast<int>(floor(coordBegin)); // First pixel (y-coord) affected by the current source pixel - int end = static_cast<int>(ceil(coordEnd)); // First pixel (y-coord) NOT affected by the current source pixel (a zero contribution is counted as not affecting the pixel) - for(int ny=begin; ny<end; ny++) { - // Set yBegin if this is the first source pixel contributing to the target pixel. - if (yBegin[ny]==-1) yBegin[ny] = y; - // This computes the fraction of the current target pixel (at ny) that is covered by the source pixel (at y). - yCoefs[ny].push_back(static_cast<float>(std::min<double>(ny+1,coordEnd) - std::max<double>(ny,coordBegin))); - } - } - - _new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, newwidth,newheight); - 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 newstride = cairo_image_surface_get_stride(_new_surface); - - cairo_surface_flush(_surface); - cairo_surface_flush(_new_surface); - - for(int y=0; y<newheight; y++) { - for(int x=0; x<newwidth; x++) { - float tempSum[4] = {0,0,0,0}; - for(int oy=0; oy<static_cast<int>(yCoefs[y].size()); oy++) { - for(int ox=0; ox<static_cast<int>(xCoefs[x].size()); ox++) { - for(int c=0; c<4; c++) { - tempSum[c] += xCoefs[x][ox]*yCoefs[y][oy]*orgdata[c+4*(xBegin[x]+ox)+orgstride*(yBegin[y]+oy)]; - } - } - } - for(int c=0; c<4; c++) { - newdata[c+4*x+newstride*y] = static_cast<unsigned char>(tempSum[c]); - } - } - } - - cairo_surface_mark_dirty(_new_surface); - - ct.scale(expansion.inverse()); // This should not include scale (see derivation above) - ct.translate(_origin*expansion); - ct.scale(scaleExpansionLarge); - ct.setSource(_new_surface, 0, 0); - } else { - // No need to regenerate, but we do draw from _new_surface. - ct.scale(expansion.inverse()); // This should not include scale (see derivation above) - ct.translate(_origin*expansion); - ct.scale(scaleExpansionLarge); - ct.setSource(_new_surface, 0, 0); - } - - // END: Hack to avoid Cairo bug - ///////////////////////////////////////////////////////////////////////////// - - // TODO: If Cairo's problems are gone, uncomment the following: - //ct.translate(_origin); - //ct.scale(_scale); - //ct.setSource(_surface, 0, 0); - - //ct.paint(_opacity); - ct.paint(); + ct.paint(_opacity); } else { // outline; draw a rect instead Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -287,21 +165,9 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar static double distance_to_segment (Geom::Point const &p, Geom::Point const &a1, Geom::Point const &a2) { - // calculate sides of the triangle and their squares - double d1 = Geom::L2(p - a1); - double d1_2 = d1 * d1; - double d2 = Geom::L2(p - a2); - double d2_2 = d2 * d2; - double a = Geom::L2(a1 - a2); - double a_2 = a * a; - - // if one of the angles at the base is > 90, return the corresponding side - if (d1_2 + a_2 <= d2_2) return d1; - if (d2_2 + a_2 <= d1_2) return d2; - - // otherwise calculate the height to the base - double peri = (a + d1 + d2)/2; - return (2*sqrt(peri * (peri - a) * (peri - d1) * (peri - d2))/a); + Geom::LineSegment l(a1, a2); + Geom::Point np = l.pointAt(l.nearestPoint(p)); + return Geom::distance(np, p); } DrawingItem * @@ -313,29 +179,24 @@ DrawingImage::_pickItem(Geom::Point const &p, double delta, unsigned /*sticky*/) if (outline) { Geom::Rect r = bounds(); - - Geom::Point c00 = r.corner(0); - Geom::Point c01 = r.corner(3); - Geom::Point c11 = r.corner(2); - Geom::Point c10 = r.corner(1); - - // frame - if (distance_to_segment (p, c00, c10) < delta) return this; - if (distance_to_segment (p, c10, c11) < delta) return this; - if (distance_to_segment (p, c11, c01) < delta) return this; - if (distance_to_segment (p, c01, c00) < delta) return this; - - // diagonals - if (distance_to_segment (p, c00, c11) < delta) return this; - if (distance_to_segment (p, c10, c01) < delta) return this; - + Geom::Point pick = p * _ctm.inverse(); + + // find whether any side or diagonal is within delta + // to do so, iterate over all pairs of corners + for (unsigned i = 0; i < 3; ++i) { // for i=3, there is nothing to do + for (unsigned j = i+1; j < 4; ++j) { + if (distance_to_segment(pick, r.corner(i), r.corner(j)) < delta) { + return this; + } + } + } 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(); @@ -353,8 +214,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..cebaafc85 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,13 +42,9 @@ 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 - Geom::Point _rescaledSize; // Part of hack around Cairo bug - // TODO: the following three should probably be merged into a new Geom::Viewbox object Geom::Rect _clipbox; ///< for preserveAspectRatio Geom::Point _origin; diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 80664d822..a8257e6e5 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -141,7 +141,14 @@ DrawingItem::appendChild(DrawingItem *item) assert(item->_child_type == CHILD_ORPHAN); item->_child_type = CHILD_NORMAL; _children.push_back(*item); - _markForUpdate(STATE_ALL, false); + + // This ensures that _markForUpdate() called on the child will recurse to this item + item->_state = STATE_ALL; + // Because _markForUpdate recurses through ancestors, we can simply call it + // on the just-added child. This has the additional benefit that we do not + // rely on the appended child being in the default non-updated state. + // We set propagate to true, because the child might have descendants of its own. + item->_markForUpdate(STATE_ALL, true); } void @@ -151,7 +158,9 @@ DrawingItem::prependChild(DrawingItem *item) assert(item->_child_type == CHILD_ORPHAN); item->_child_type = CHILD_NORMAL; _children.push_front(*item); - _markForUpdate(STATE_ALL, false); + // See appendChild for explanation + item->_state = STATE_ALL; + item->_markForUpdate(STATE_ALL, true); } /// Delete all regular children of this item (not mask or clip). @@ -353,7 +362,13 @@ DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigne if (to_update & STATE_BBOX) { // compute drawbox if (_filter && render_filters) { - _drawbox = _filter->compute_drawbox(this, _item_bbox); + Geom::OptRect enlarged = _filter->filter_effect_area(_item_bbox); + if (enlarged) { + *enlarged *= ctm(); + _drawbox = enlarged->roundOutwards(); + } else { + _drawbox = Geom::OptIntRect(); + } } else { _drawbox = _bbox; } @@ -444,7 +459,7 @@ struct MaskLuminanceToAlpha { /** * Rasterize items. * This method submits the drawing opeartions required to draw this item - * to the supplied DrawingContext, restricting drawing the the specified area. + * to the supplied DrawingContext, restricting drawing the specified area. * * This method does some common tasks and calls the item-specific rendering * function, _renderItem(), to render e.g. paths or bitmaps. @@ -705,8 +720,11 @@ DrawingItem::pick(Geom::Point const &p, double delta, unsigned flags) { // Sometimes there's no BBOX in state, reason unknown (bug 992817) // I made this not an assert to remove the warning - if (!(_state & STATE_BBOX) || !(_state & STATE_PICK)) + if (!(_state & STATE_BBOX) || !(_state & STATE_PICK)) { + g_warning("Invalid state when picking: STATE_BBOX = %d, STATE_PICK = %d", + _state & STATE_BBOX, _state & STATE_PICK); return NULL; + } // ignore invisible and insensitive items unless sticky if (!(flags & PICK_STICKY) && !(_visible && _sensitive)) return NULL; @@ -727,7 +745,9 @@ DrawingItem::pick(Geom::Point const &p, double delta, unsigned flags) } Geom::OptIntRect box = (outline || (flags & PICK_AS_CLIP)) ? _bbox : _drawbox; - if (!box) return NULL; + if (!box) { + return NULL; + } Geom::Rect expanded = *box; expanded.expandBy(delta); @@ -747,6 +767,9 @@ DrawingItem::pick(Geom::Point const &p, double delta, unsigned flags) void DrawingItem::_markForRendering() { + // TODO: this function does too much work when a large subtree + // is invalidated - fix + bool outline = _drawing.outline(); Geom::OptIntRect dirty = outline ? _bbox : _drawbox; if (!dirty) return; @@ -798,8 +821,8 @@ DrawingItem::_invalidateFilterBackground(Geom::IntRect const &area) * all children should also have the corresponding flags unset before checking * whether they need to be traversed. This way there is one less traversal * of the tree. Without this we would need to unset state bits in all children. - * With _propagate we do this during the update call, when we have to traverse - * the tree anyway. + * With _propagate we do this during the update call, when we have to recurse + * into children anyway. */ void DrawingItem::_markForUpdate(unsigned flags, bool propagate) @@ -809,15 +832,31 @@ DrawingItem::_markForUpdate(unsigned flags, bool propagate) } if (_state & flags) { + unsigned oldstate = _state; _state &= ~flags; - if (_parent) { + if (oldstate != _state && _parent) { + // If we actually reset anything in state, recurse on the parent. _parent->_markForUpdate(flags, false); } else { + // If nothing changed, it means our ancestors are already invalidated + // up to the root. Do not bother recursing, because it won't change anything. + // Also do this if we are the root item, because we have no more ancestors + // to invalidate. _drawing.signal_request_update.emit(this); } } } +/** + * Process information related to the new style. + * + * This function is something of a hack to avoid creating an extra class in the hierarchy + * which would differ from DrawingItem only by having a _style member. + * This is mainly to the benefit of DrawingGlyphs, which use the style of their parent. + * This should probably be refactored some day, possibly by creating the relevant class + * or creating a more complex data model in DrawingText and removing DrawingGlyphs, + * which would cause every item to have a style. + */ void DrawingItem::_setStyleCommon(SPStyle *&_style, SPStyle *style) { @@ -825,7 +864,6 @@ DrawingItem::_setStyleCommon(SPStyle *&_style, SPStyle *style) if (_style) sp_style_unref(_style); _style = style; - // if group has a filter if (style->filter.set && style->getFilter()) { if (!_filter) { int primitives = sp_filter_primitive_count(SP_FILTER(style->getFilter())); diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index 4a516512b..e03bbd0f7 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -113,6 +113,7 @@ public: void setMask(DrawingItem *item); void setZOrder(unsigned z); void setItemBounds(Geom::OptRect const &bounds); + void setFilterBounds(Geom::OptRect const &bounds); void setKey(unsigned key) { _key = key; } unsigned key() const { return _key; } @@ -175,7 +176,9 @@ protected: Geom::Affine _ctm; ///< Total transform from item coords to display coords Geom::OptIntRect _bbox; ///< Bounding box in display (pixel) coords including stroke Geom::OptIntRect _drawbox; ///< Full visual bounding box - enlarged by filters, shrunk by clips and masks - Geom::OptRect _item_bbox; ///< Geometric bounding box in item coordinates + Geom::OptRect _item_bbox; ///< Geometric bounding box in item's user space. + /// This is used to compute the filter effect region and render in + /// objectBoundingBox units. DrawingItem *_clip; DrawingItem *_mask; diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index 2a6505c67..55d54b770 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -18,9 +18,11 @@ #include "helper/geom.h" #include "libnrtype/font-instance.h" #include "style.h" +#include "2geom/pathvector.h" namespace Inkscape { + DrawingGlyphs::DrawingGlyphs(Drawing &drawing) : DrawingItem(drawing) , _font(NULL) @@ -61,12 +63,34 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext return STATE_ALL; } + _pick_bbox = Geom::IntRect(); _bbox = Geom::IntRect(); - Geom::OptRect b = bounds_exact_transformed(*_font->PathVector(_glyph), ctx.ctm); + Geom::OptRect b; + +/* orignally it did the one line below, + but it did not handle ws characters at all, and it had problems with scaling for overline/underline. + Replaced with the section below, which seems to be much more stable. + + b = bounds_exact_transformed(*_font->PathVector(_glyph), ctx.ctm); +*/ + /* Make a bounding box that is a little taller and lower (currently 10% extra) than the font's drawing box. Extra space is + to hold overline or underline, if present. All characters in a font use the same ascent and descent, + but different widths. This lets leading and trailing spaces have text decorations. If it is not done + the bounding box is limited to the box surrounding the drawn parts of visible glyphs only, and draws outside are ignored. + */ + + float scale = 1.0; + if(_transform){ scale /= _transform->descrim(); } + + Geom::Rect bigbox(Geom::Point(0.0, _asc*scale*1.1),Geom::Point(_width*scale, -_dsc*scale*1.1)); + b = bigbox * ctx.ctm; + if (b && (ggroup->_nrstyle.stroke.type != NRStyle::PAINT_NONE)) { float width, scale; + + // this expands the selection box for cases where the stroke is "thick" scale = ctx.ctm.descrim(); if (_transform) { scale /= _transform->descrim(); // FIXME temporary hack @@ -75,7 +99,8 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext if ( fabs(ggroup->_nrstyle.stroke_width * scale) > 0.01 ) { // FIXME: this is always true b->expandBy(0.5 * width); } - // save bbox without miters for picking + + // save bbox without miters for picking _pick_bbox = b->roundOutwards(); float miterMax = width * ggroup->_nrstyle.miter_limit; @@ -89,7 +114,12 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext _bbox = b->roundOutwards(); _pick_bbox = *_bbox; } - +/* +std::cout << "DEBUG _bbox" +<< " { " << _bbox->min()[Geom::X] << " , " << _bbox->min()[Geom::Y] +<< " } , { " << _bbox->max()[Geom::X] << " , " << _bbox->max()[Geom::Y] +<< " }" << std::endl; +*/ return STATE_ALL; } @@ -106,7 +136,7 @@ DrawingGlyphs::_pickItem(Geom::Point const &p, double delta, unsigned /*flags*/) return NULL; } - // With text we take a simple approach: pick if the point is in a characher bbox + // With text we take a simple approach: pick if the point is in a character bbox Geom::Rect expanded(_pick_bbox); expanded.expandBy(delta); if (expanded.contains(p)) return this; @@ -129,17 +159,28 @@ DrawingText::clear() _children.clear_and_dispose(DeleteDisposer()); } -void -DrawingText::addComponent(font_instance *font, int glyph, Geom::Affine const &trans) +bool +DrawingText::addComponent(font_instance *font, int glyph, Geom::Affine const &trans, + float width, float ascent, float descent, float phase_length) { +/* original, did not save a glyph for white space characters, causes problems for text-decoration if (!font || !font->PathVector(glyph)) { - return; + return(false); } +*/ + if (!font)return(false); _markForRendering(); DrawingGlyphs *ng = new DrawingGlyphs(_drawing); ng->setGlyph(font, glyph, trans); + if(font->PathVector(glyph)){ ng->_drawable = true; } + else { ng->_drawable = false; } + ng->_width = width; // only used when _drawable = false + ng->_asc = ascent; // of font, not of this one character + ng->_dsc = descent; // of font, not of this one character + ng->_pl = phase_length; // used for phase of dots, dashes, and wavy appendChild(ng); + return(true); } void @@ -156,9 +197,175 @@ DrawingText::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, un return DrawingGroup::_updateItem(area, ctx, flags, reset); } +void DrawingText::decorateStyle(DrawingContext &ct, double vextent, double xphase, Geom::Point p1, Geom::Point p2) +{ + double wave[16]={ + 0.000000, 0.382499, 0.706825, 0.923651, 1.000000, 0.923651, 0.706825, 0.382499, + 0.000000, -0.382499, -0.706825, -0.923651, -1.000000, -0.923651, -0.706825, -0.382499, + }; + int dashes[16]={ + 8, 7, 6, 5, + 4, 3, 2, 1, + -8, -7, -6, -5 + -4, -3, -2, -1 + }; + int dots[16]={ + 4, 3, 2, 1, + -4, -3, -2, -1, + 4, 3, 2, 1, + -4, -3, -2, -1 + }; + Geom::Point p3,p4,ps,pf; + double step = vextent/32.0; + unsigned i = 15 & (unsigned) round(xphase/step); // xphase is >= 0.0 + + /* For most spans draw the last little bit right to p2 or even a little beyond. + This allows decoration continuity within the line, and does not step outside the clip box off the end + For the first/last section on the line though, stay well clear of the edge, or when the + text is dragged it may "spray" pixels. + if(_nrstyle.tspan_line_end){ pf = p2 - Geom::Point(2*step, 0.0); } + else { pf = p2; } + if(_nrstyle.tspan_line_start){ ps = p1 + Geom::Point(2*step, 0.0); + i = 15 & (i + 2); + } + else { ps = p1; } + */ + /* snap to nearest step in X */ +ps = Geom::Point(step * round(p1[Geom::X]/step),p1[Geom::Y]); +pf = Geom::Point(step * round(p2[Geom::X]/step),p2[Geom::Y]); + + if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_ISDOUBLE){ + ps -= Geom::Point(0, vextent/12.0); + pf -= Geom::Point(0, vextent/12.0); + ct.moveTo(ps); + ct.lineTo(pf); + ps += Geom::Point(0, vextent/6.0); + pf += Geom::Point(0, vextent/6.0); + ct.moveTo(ps); + ct.lineTo(pf); + } + /* The next three have a problem in that they are phase dependent. The bits of a line are not + necessarily passing through this routine in order, so we have to use the xphase information + to figure where in each of their cycles to start. Only accurate to 1 part in 16. + Huge possitive offset should keep the phase calculation from ever being negative. + */ + else if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_DOTTED){ + while(1){ + if(dots[i]>0){ + if(ps[Geom::X]> pf[Geom::X])break; + ct.moveTo(ps); + ps += Geom::Point(step * (double)dots[i], 0.0); + if(ps[Geom::X]>= pf[Geom::X]){ + ct.lineTo(pf); + break; + } + else { + ct.lineTo(ps); + } + ps += Geom::Point(step * 4.0, 0.0); + } + else { + ps += Geom::Point(step * -(double)dots[i], 0.0); + } + i = 0; // once in phase, it stays in phase + } + } + else if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_DASHED){ + while(1){ + if(dashes[i]>0){ + if(ps[Geom::X]> pf[Geom::X])break; + ct.moveTo(ps); + ps += Geom::Point(step * (double)dashes[i], 0.0); + if(ps[Geom::X]>= pf[Geom::X]){ + ct.lineTo(pf); + break; + } + else { + ct.lineTo(ps); + } + ps += Geom::Point(step * 8.0, 0.0); + } + else { + ps += Geom::Point(step * -(double)dashes[i], 0.0); + } + i = 0; // once in phase, it stays in phase + } + } + else if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_WAVY){ + double amp = vextent/10.0; + double x = ps[Geom::X]; + double y = ps[Geom::Y]; + ct.moveTo(Geom::Point(x, y + amp * wave[i])); + while(1){ + i = ((i + 1) & 15); + x += step; + ct.lineTo(Geom::Point(x, y + amp * wave[i])); + if(x >= pf[Geom::X])break; + } + } + else { // TEXT_DECORATION_STYLE_SOLID, also default in case it was not set for some reason + ct.moveTo(ps); + ct.lineTo(pf); +// ct.revrectangle(Geom::Rect(ps,pf)); + } +} + +/* returns scaled line thickness */ +double DrawingText::decorateItem(DrawingContext &ct, Geom::Affine aff, double phase_length) +{ + double tsp_width_adj, tsp_asc_adj, tsp_size_adj; + double final_underline_thickness, final_line_through_thickness; + double thickness; + + tsp_width_adj = _nrstyle.tspan_width / _nrstyle.font_size; + tsp_asc_adj = _nrstyle.ascender / _nrstyle.font_size; + tsp_size_adj = (_nrstyle.ascender + _nrstyle.descender) / _nrstyle.font_size; +#define VALTRUNC(A,B,C) (A < B ? B : ( A > C ? C : A )) + final_underline_thickness = VALTRUNC(_nrstyle.underline_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0); + final_line_through_thickness = VALTRUNC(_nrstyle.line_through_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0); + Inkscape::DrawingContext::Save save(ct); + + double scale = aff.descrim(); + double xphase = phase_length/ _nrstyle.font_size; // used to figure out phase of patterns + + ct.transform(aff); // must be leftmost affine in span + Geom::Point p1; + Geom::Point p2; + // All lines must be the same thickness, in combinations, line_through trumps underline + thickness = final_underline_thickness; + if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_UNDERLINE){ + p1 = Geom::Point(0.0, -_nrstyle.underline_position); + p2 = Geom::Point(tsp_width_adj,-_nrstyle.underline_position); + decorateStyle(ct, tsp_size_adj, xphase, p1, p2); + } + if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_OVERLINE){ + p1 = Geom::Point(0.0, tsp_asc_adj -_nrstyle.underline_position + 1 * final_underline_thickness); + p2 = Geom::Point(tsp_width_adj,tsp_asc_adj -_nrstyle.underline_position + 1 * final_underline_thickness); + decorateStyle(ct, tsp_size_adj, xphase, p1, p2); + } + if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_LINETHROUGH){ + thickness = final_line_through_thickness; + p1 = Geom::Point(0.0, _nrstyle.line_through_position); + p2 = Geom::Point(tsp_width_adj,_nrstyle.line_through_position); + decorateStyle(ct, tsp_size_adj, xphase, p1, p2); + } + // Obviously this does not blink, but it does indicate which text has been set with that attribute + if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_BLINK){ + thickness = final_line_through_thickness; + p1 = Geom::Point(0.0, _nrstyle.line_through_position - 2*final_line_through_thickness); + p2 = Geom::Point(tsp_width_adj,_nrstyle.line_through_position - 2*final_line_through_thickness); + decorateStyle(ct, tsp_size_adj, xphase, p1, p2); + p1 = Geom::Point(0.0, _nrstyle.line_through_position + 2*final_line_through_thickness); + p2 = Geom::Point(tsp_width_adj,_nrstyle.line_through_position + 2*final_line_through_thickness); + decorateStyle(ct, tsp_size_adj, xphase, p1, p2); + } + thickness *= scale; + return(thickness); +} + unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/) { - if (_drawing.outline()) { + if (_drawing.outline()) { guint32 rgba = _drawing.outlinecolor; Inkscape::DrawingContext::Save save(ct); ct.setSource(rgba); @@ -169,34 +376,72 @@ unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*are if (!g) throw InvalidItemException(); Inkscape::DrawingContext::Save save(ct); - // skip glpyhs with singular transforms + // skip glyphs with singular transforms if (g->_ctm.isSingular()) continue; ct.transform(g->_ctm); - ct.path(*g->_font->PathVector(g->_glyph)); - ct.fill(); + if(g->_drawable){ + ct.path(*g->_font->PathVector(g->_glyph)); + ct.fill(); + } } return RENDER_OK; } // NOTE: this is very similar to drawing-shape.cpp; the only difference is in path feeding - bool has_stroke, has_fill; - - has_fill = _nrstyle.prepareFill(ct, _item_bbox); + double leftmost = DBL_MAX; + double phase_length = 0.0; + bool firsty = true; + bool decorate = true; + double starty = 0.0; + bool has_stroke, has_fill; + Geom::Affine aff; + using Geom::X; + using Geom::Y; + + has_fill = _nrstyle.prepareFill( ct, _item_bbox); has_stroke = _nrstyle.prepareStroke(ct, _item_bbox); if (has_fill || has_stroke) { + Geom::Affine rotinv; + bool invset=false; for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i); if (!g) throw InvalidItemException(); + if(!invset){ + rotinv = g->_ctm.withoutTranslation().inverse(); + invset = true; + } Inkscape::DrawingContext::Save save(ct); if (g->_ctm.isSingular()) continue; ct.transform(g->_ctm); - ct.path(*g->_font->PathVector(g->_glyph)); + if(g->_drawable){ + ct.path(*g->_font->PathVector(g->_glyph)); + } + // get the leftmost affine transform (leftmost defined with respect to the x axis of the first transform). + // That way the decoration will work no matter what mix of L->R, R->L text is in the span. + if(_nrstyle.text_decoration_line != TEXT_DECORATION_LINE_CLEAR){ + Geom::Point pt =g->_ctm.translation() * rotinv; + if(pt[X] < leftmost){ + leftmost = pt[X]; + aff = g->_ctm; + phase_length = g->_pl; + } + /* If the text has been mapped onto a path, which causes y to vary, drop the text decorations. + To handle that properly would need a conformal map + */ + if(firsty){ + firsty = false; + starty = pt[Y]; + } + else { + if(fabs(pt[Y] - starty) > 1.0e-6)decorate=false; + } + } } Inkscape::DrawingContext::Save save(ct); - ct.transform(_ctm); + ct.transform(_ctm); // For one thing, this is needed to scale a fill-pattern when zooming in if (has_fill) { _nrstyle.applyFill(ct); ct.fillPreserve(); @@ -206,6 +451,29 @@ unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*are ct.strokePreserve(); } ct.newPath(); // clear path + if(_nrstyle.text_decoration_line != TEXT_DECORATION_LINE_CLEAR && decorate){ + guint32 ergba; + if(_nrstyle.text_decoration_useColor){ // color different from the glyph + ergba = SP_RGBA32_F_COMPOSE( + _nrstyle.text_decoration_color.color.v.c[0], + _nrstyle.text_decoration_color.color.v.c[1], + _nrstyle.text_decoration_color.color.v.c[2], + 1.0); + } + else { // whatever the current fill color is + ergba = SP_RGBA32_F_COMPOSE( + _nrstyle.fill.color.v.c[0], + _nrstyle.fill.color.v.c[1], + _nrstyle.fill.color.v.c[2], + 1.0); + } + ct.setSource(ergba); + ct.setTolerance(0.5); + double thickness = decorateItem(ct, aff, phase_length); + ct.setLineWidth(thickness); + ct.strokePreserve(); + ct.newPath(); // clear path + } } return RENDER_OK; } @@ -231,7 +499,9 @@ void DrawingText::_clipItem(DrawingContext &ct, Geom::IntRect const &/*area*/) Inkscape::DrawingContext::Save save(ct); ct.transform(g->_ctm); - ct.path(*g->_font->PathVector(g->_glyph)); + if(g->_drawable){ + ct.path(*g->_font->PathVector(g->_glyph)); + } } ct.fill(); } diff --git a/src/display/drawing-text.h b/src/display/drawing-text.h index 79b1b097c..99b46bc8a 100644 --- a/src/display/drawing-text.h +++ b/src/display/drawing-text.h @@ -35,8 +35,13 @@ protected: virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); font_instance *_font; - int _glyph; - Geom::IntRect _pick_bbox; + int _glyph; + bool _drawable; + float _width; // These three are used to set up bounding box + float _asc; // + float _dsc; // + float _pl; // phase length + Geom::IntRect _pick_bbox; friend class DrawingText; }; @@ -49,9 +54,11 @@ public: ~DrawingText(); void clear(); - void addComponent(font_instance *font, int glyph, Geom::Affine const &trans); + bool addComponent(font_instance *font, int glyph, Geom::Affine const &trans, + float width, float ascent, float descent, float phase_length); void setStyle(SPStyle *style); + protected: virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset); @@ -61,6 +68,8 @@ protected: virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); virtual bool _canClip(); + double decorateItem(DrawingContext &ct, Geom::Affine aff, double phase_length); + void decorateStyle(DrawingContext &ct, double vextent, double xphase, Geom::Point p1, Geom::Point p2); NRStyle _nrstyle; friend class DrawingGlyphs; diff --git a/src/display/guideline.cpp b/src/display/guideline.cpp index f71bc82ef..55c1ee495 100644 --- a/src/display/guideline.cpp +++ b/src/display/guideline.cpp @@ -94,14 +94,20 @@ static void sp_guideline_destroy(SPCanvasItem *object) g_return_if_fail (SP_IS_GUIDELINE (object)); //g_return_if_fail (SP_GUIDELINE(object)->origin != NULL); //g_return_if_fail (SP_IS_CTRLPOINT(SP_GUIDELINE(object)->origin)); - - if (SP_GUIDELINE(object)->origin != NULL && SP_IS_CTRLPOINT(SP_GUIDELINE(object)->origin)) { - sp_canvas_item_destroy(SP_GUIDELINE(object)->origin); + + SPGuideLine *gl = SP_GUIDELINE(object); + + if (gl->origin != NULL && SP_IS_CTRLPOINT(gl->origin)) { + sp_canvas_item_destroy(gl->origin); } else { // FIXME: This branch shouldn't be reached (although it seems to be harmless). //g_error("Why can it be that gl->origin is not a valid SPCtrlPoint?\n"); } + if (gl->label) { + g_free(gl->label); + } + SP_CANVAS_ITEM_CLASS(parent_class)->destroy(object); } diff --git a/src/display/nr-filter-blend.cpp b/src/display/nr-filter-blend.cpp index a08191f67..bff7405b7 100644 --- a/src/display/nr-filter-blend.cpp +++ b/src/display/nr-filter-blend.cpp @@ -1,6 +1,6 @@ -/* +/** @file * SVG feBlend renderer - * + *//* * "This filter composites two objects together using commonly used * imaging software blending modes. It performs a pixel-wise combination * of two input images." @@ -9,6 +9,7 @@ * Authors: * Niko Kiirala <niko@kiirala.com> * Jasper van de Gronde <th.v.d.gronde@hccnet.nl> + * Krzysztof Kosiński <tweenk.pl@gmail.com> * * Copyright (C) 2007-2008 authors * @@ -30,20 +31,6 @@ namespace Inkscape { namespace Filters { -/* - * From http://www.w3.org/TR/SVG11/filters.html#feBlend - * - * For all feBlend modes, the result opacity is computed as follows: - * qr = 1 - (1-qa)*(1-qb) - * - * For the compositing formulas below, the following definitions apply: - * cr = Result color (RGB) - premultiplied - * qa = Opacity value at a given pixel for image A - * qb = Opacity value at a given pixel for image B - * ca = Color (RGB) at a given pixel for image A - premultiplied - * cb = Color (RGB) at a given pixel for image B - premultiplied - */ - FilterBlend::FilterBlend() : _blend_mode(BLEND_NORMAL), _input2(NR_FILTER_SLOT_NOT_SET) @@ -56,91 +43,6 @@ FilterPrimitive * FilterBlend::create() { FilterBlend::~FilterBlend() {} -// cr = (1-qa)*cb + (1-qb)*ca + ca*cb -struct BlendMultiply { - guint32 operator()(guint32 in1, guint32 in2) - { - EXTRACT_ARGB32(in1, aa, ra, ga, ba) - EXTRACT_ARGB32(in2, ab, rb, gb, bb) - - guint32 ao = 255*255 - (255-aa)*(255-ab); ao = (ao + 127) / 255; - guint32 ro = (255-aa)*rb + (255-ab)*ra + ra*rb; ro = (ro + 127) / 255; - guint32 go = (255-aa)*gb + (255-ab)*ga + ga*gb; go = (go + 127) / 255; - guint32 bo = (255-aa)*bb + (255-ab)*ba + ba*bb; bo = (bo + 127) / 255; - - ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) - return pxout; - } -}; - -// cr = cb + ca - ca * cb -struct BlendScreen { - guint32 operator()(guint32 in1, guint32 in2) - { - EXTRACT_ARGB32(in1, aa, ra, ga, ba) - EXTRACT_ARGB32(in2, ab, rb, gb, bb) - - guint32 ao = 255*255 - (255-aa)*(255-ab); ao = (ao + 127) / 255; - guint32 ro = 255*(rb + ra) - ra * rb; ro = (ro + 127) / 255; - guint32 go = 255*(gb + ga) - ga * gb; go = (go + 127) / 255; - guint32 bo = 255*(bb + ba) - ba * bb; bo = (bo + 127) / 255; - - ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) - return pxout; - } -}; - -// cr = Min ((1 - qa) * cb + ca, (1 - qb) * ca + cb) -struct BlendDarken { - guint32 operator()(guint32 in1, guint32 in2) - { - EXTRACT_ARGB32(in1, aa, ra, ga, ba) - EXTRACT_ARGB32(in2, ab, rb, gb, bb) - - guint32 ao = 255*255 - (255-aa)*(255-ab); ao = (ao + 127) / 255; - guint32 ro = std::min((255-aa)*rb + 255*ra, (255-ab)*ra + 255*rb); ro = (ro + 127) / 255; - guint32 go = std::min((255-aa)*gb + 255*ga, (255-ab)*ga + 255*gb); go = (go + 127) / 255; - guint32 bo = std::min((255-aa)*bb + 255*ba, (255-ab)*ba + 255*bb); bo = (bo + 127) / 255; - - ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) - return pxout; - } -}; - -// cr = Max ((1 - qa) * cb + ca, (1 - qb) * ca + cb) -struct BlendLighten { - guint32 operator()(guint32 in1, guint32 in2) - { - EXTRACT_ARGB32(in1, aa, ra, ga, ba) - EXTRACT_ARGB32(in2, ab, rb, gb, bb) - - guint32 ao = 255*255 - (255-aa)*(255-ab); ao = (ao + 127) / 255; - guint32 ro = std::max((255-aa)*rb + 255*ra, (255-ab)*ra + 255*rb); ro = (ro + 127) / 255; - guint32 go = std::max((255-aa)*gb + 255*ga, (255-ab)*ga + 255*gb); go = (go + 127) / 255; - guint32 bo = std::max((255-aa)*bb + 255*ba, (255-ab)*ba + 255*bb); bo = (bo + 127) / 255; - - ASSEMBLE_ARGB32(pxout, ao, ro, go, bo) - return pxout; - } -}; - -/* -struct BlendAlpha -static inline void blend_alpha(guint32 in1, guint32 in2, guint32 *out) -{ - EXTRACT_ARGB32(in1, a1, a2, a3, a4); - EXTRACT_ARGB32(in2, b1, b2, b3, b4); - - guint32 o1 = 255*255 - (255-a1)*(255-b1); o1 = (o1+127) / 255; - guint32 o2 = 255*255 - (255-a2)*(255-b2); o2 = (o2+127) / 255; - guint32 o3 = 255*255 - (255-a3)*(255-b3); o3 = (o3+127) / 255; - guint32 o4 = 255*255 - (255-a4)*(255-b4); o4 = (o4+127) / 255; - - ASSEMBLE_ARGB32(pxout, o1, o2, o3, o4); - *out = pxout; -} -*/ - void FilterBlend::render_cairo(FilterSlot &slot) { cairo_surface_t *input1 = slot.getcairo(_input); @@ -161,41 +63,35 @@ void FilterBlend::render_cairo(FilterSlot &slot) cairo_surface_t *out = ink_cairo_surface_create_output(input1, input2); set_cairo_surface_ci( out, ci_fp ); - cairo_content_t ct1 = cairo_surface_get_content(input1); - cairo_content_t ct2 = cairo_surface_get_content(input2); - if ((ct1 == CAIRO_CONTENT_ALPHA && ct2 == CAIRO_CONTENT_ALPHA) - || _blend_mode == BLEND_NORMAL) - { - ink_cairo_surface_blit(input2, out); - cairo_t *out_ct = cairo_create(out); - cairo_set_source_surface(out_ct, input1, 0, 0); - cairo_paint(out_ct); - cairo_destroy(out_ct); - } else { - // blend mode != normal and at least 1 surface is not pure alpha - - // TODO: convert to Cairo blending operators once we start using the 1.10 series - switch (_blend_mode) { - case BLEND_MULTIPLY: - ink_cairo_surface_blend(input1, input2, out, BlendMultiply()); - break; - case BLEND_SCREEN: - ink_cairo_surface_blend(input1, input2, out, BlendScreen()); - break; - case BLEND_DARKEN: - ink_cairo_surface_blend(input1, input2, out, BlendDarken()); - break; - case BLEND_LIGHTEN: - ink_cairo_surface_blend(input1, input2, out, BlendLighten()); - break; - case BLEND_NORMAL: - default: - // this was handled before - g_assert_not_reached(); - break; - } + ink_cairo_surface_blit(input2, out); + cairo_t *out_ct = cairo_create(out); + cairo_set_source_surface(out_ct, input1, 0, 0); + + // All of the blend modes are implemented in Cairo as of 1.10. + // For a detailed description, see: + // http://cairographics.org/operators/ + switch (_blend_mode) { + case BLEND_MULTIPLY: + cairo_set_operator(out_ct, CAIRO_OPERATOR_MULTIPLY); + break; + case BLEND_SCREEN: + cairo_set_operator(out_ct, CAIRO_OPERATOR_SCREEN); + break; + case BLEND_DARKEN: + cairo_set_operator(out_ct, CAIRO_OPERATOR_DARKEN); + break; + case BLEND_LIGHTEN: + cairo_set_operator(out_ct, CAIRO_OPERATOR_LIGHTEN); + break; + case BLEND_NORMAL: + default: + cairo_set_operator(out_ct, CAIRO_OPERATOR_OVER); + break; } + cairo_paint(out_ct); + cairo_destroy(out_ct); + slot.set(_output, out); cairo_surface_destroy(out); } diff --git a/src/display/nr-filter-diffuselighting.h b/src/display/nr-filter-diffuselighting.h index 15cc8e1ff..043a5eb39 100644 --- a/src/display/nr-filter-diffuselighting.h +++ b/src/display/nr-filter-diffuselighting.h @@ -19,9 +19,9 @@ #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -struct SPFeDistantLight; -struct SPFePointLight; -struct SPFeSpotLight; +class SPFeDistantLight; +class SPFePointLight; +class SPFeSpotLight; struct SVGICCColor; namespace Inkscape { diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp index 7a27d857e..4ca4cd07c 100644 --- a/src/display/nr-filter-image.cpp +++ b/src/display/nr-filter-image.cpp @@ -30,7 +30,7 @@ FilterImage::FilterImage() : SVGElem(0) , document(0) , feImageHref(0) - , image_surface(0) + , image(0) , broken_ref(false) { } @@ -42,6 +42,7 @@ FilterImage::~FilterImage() { if (feImageHref) g_free(feImageHref); + delete image; } void FilterImage::render_cairo(FilterSlot &slot) @@ -131,59 +132,39 @@ 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); - } - - // Native size of image - // width = image->get_width(); - // height = image->get_height(); - // rowstride = image->get_rowstride(); - - convert_pixbuf_normal_to_argb32(image->gobj()); - - image_surface = cairo_image_surface_create_for_data(image->get_pixels(), - CAIRO_FORMAT_ARGB32, image->get_width(), image->get_height(), image->get_rowstride()); } + 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, sa.width(), sa.height()); @@ -207,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 @@ -282,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); @@ -310,10 +291,8 @@ void FilterImage::set_href(const gchar *href){ if (feImageHref) g_free (feImageHref); feImageHref = (href) ? g_strdup (href) : NULL; - if (image_surface) { - cairo_surface_destroy(image_surface); - } - 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 05b7d65a8..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,8 +43,7 @@ public: private: SPDocument *document; gchar *feImageHref; - Glib::RefPtr<Gdk::Pixbuf> image; - cairo_surface_t *image_surface; + Inkscape::Pixbuf *image; float feImageX, feImageY, feImageWidth, feImageHeight; unsigned int aspect_align, aspect_clip; bool broken_ref; diff --git a/src/display/nr-filter-slot.cpp b/src/display/nr-filter-slot.cpp index fe67972d6..755a30a74 100644 --- a/src/display/nr-filter-slot.cpp +++ b/src/display/nr-filter-slot.cpp @@ -83,11 +83,15 @@ cairo_surface_t *FilterSlot::getcairo(int slot_nr) switch (slot_nr) { case NR_FILTER_SOURCEGRAPHIC: { cairo_surface_t *tr = _get_transformed_source_graphic(); + // Assume all source graphics are sRGB + set_cairo_surface_ci( tr, SP_CSS_COLOR_INTERPOLATION_SRGB ); _set_internal(NR_FILTER_SOURCEGRAPHIC, tr); cairo_surface_destroy(tr); } break; case NR_FILTER_BACKGROUNDIMAGE: { cairo_surface_t *bg = _get_transformed_background(); + // Assume all backgrounds are sRGB + set_cairo_surface_ci( bg, SP_CSS_COLOR_INTERPOLATION_SRGB ); _set_internal(NR_FILTER_BACKGROUNDIMAGE, bg); cairo_surface_destroy(bg); } break; @@ -129,9 +133,6 @@ cairo_surface_t *FilterSlot::_get_transformed_source_graphic() if (trans.isTranslation()) { cairo_surface_reference(_source_graphic); - - // Assume all source graphics are sRGB - set_cairo_surface_ci( _source_graphic, SP_CSS_COLOR_INTERPOLATION_SRGB ); return _source_graphic; } @@ -148,8 +149,6 @@ cairo_surface_t *FilterSlot::_get_transformed_source_graphic() cairo_paint(tsg_ct); cairo_destroy(tsg_ct); - // Assume all source graphics are sRGB - set_cairo_surface_ci( tsg, SP_CSS_COLOR_INTERPOLATION_SRGB ); return tsg; } @@ -177,17 +176,15 @@ cairo_surface_t *FilterSlot::_get_transformed_background() tbg = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, _slot_w, _slot_h); } - // Assume all source graphics are sRGB - set_cairo_surface_ci( tbg, SP_CSS_COLOR_INTERPOLATION_SRGB ); - return tbg; } cairo_surface_t *FilterSlot::get_result(int res) { + cairo_surface_t *result = getcairo(res); + Geom::Affine trans = _units.get_matrix_pb2display(); if (trans.isIdentity()) { - cairo_surface_t *result = getcairo(res); cairo_surface_reference(result); return result; } @@ -196,12 +193,13 @@ cairo_surface_t *FilterSlot::get_result(int res) cairo_surface_get_content(_source_graphic), _source_graphic_area.width(), _source_graphic_area.height()); + copy_cairo_surface_ci( result, r ); cairo_t *r_ct = cairo_create(r); cairo_translate(r_ct, -_source_graphic_area.left(), -_source_graphic_area.top()); ink_cairo_transform(r_ct, trans); cairo_translate(r_ct, _slot_x, _slot_y); - cairo_set_source_surface(r_ct, getcairo(res), 0, 0); + cairo_set_source_surface(r_ct, result, 0, 0); cairo_set_operator(r_ct, CAIRO_OPERATOR_SOURCE); cairo_paint(r_ct); cairo_destroy(r_ct); diff --git a/src/display/nr-filter-specularlighting.h b/src/display/nr-filter-specularlighting.h index 0d1c0644f..c57e3a9ff 100644 --- a/src/display/nr-filter-specularlighting.h +++ b/src/display/nr-filter-specularlighting.h @@ -17,9 +17,9 @@ #include "display/nr-light-types.h" #include "display/nr-filter-primitive.h" -struct SPFeDistantLight; -struct SPFePointLight; -struct SPFeSpotLight; +class SPFeDistantLight; +class SPFePointLight; +class SPFeSpotLight; struct SVGICCColor; namespace Inkscape { diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index f0965c460..af9c15cd8 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -114,8 +114,7 @@ int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, D Geom::Affine trans = item->ctm(); -// Geom::OptRect filter_area = filter_effect_area(item->itemBounds()); // disabled, already done in visualBounds - Geom::OptRect filter_area = item->itemBounds(); // see LP Bug 1188336 + Geom::OptRect filter_area = filter_effect_area(item->itemBounds()); if (!filter_area) return 1; FilterUnits units(_filter_units, _primitive_units); @@ -220,20 +219,6 @@ void Filter::area_enlarge(Geom::IntRect &bbox, Inkscape::DrawingItem const *item */ } -Geom::OptIntRect Filter::compute_drawbox(Inkscape::DrawingItem const *item, Geom::OptRect const &item_bbox) { - -// Geom::OptRect enlarged = filter_effect_area(item_bbox); // disabled, already done in visualBounds - Geom::OptRect enlarged = item_bbox; // see LP Bug 1188336 - if (enlarged) { - *enlarged *= item->ctm(); - - Geom::OptIntRect ret(enlarged->roundOutwards()); - return ret; - } else { - return Geom::OptIntRect(); - } -} - Geom::OptRect Filter::filter_effect_area(Geom::OptRect const &bbox) { Geom::Point minp, maxp; diff --git a/src/display/nr-filter.h b/src/display/nr-filter.h index d53005c5d..5df38ffe9 100644 --- a/src/display/nr-filter.h +++ b/src/display/nr-filter.h @@ -151,12 +151,6 @@ public: */ void area_enlarge(Geom::IntRect &area, Inkscape::DrawingItem const *item) const; /** - * Given an item bounding box (in user coords), this function enlarges it - * to contain the filter effects region and transforms it to screen - * coordinates - */ - Geom::OptIntRect compute_drawbox(Inkscape::DrawingItem const *item, Geom::OptRect const &item_bbox); - /** * Returns the filter effects area in user coordinate system. * The given bounding box should be a bounding box as specified in * SVG standard and in user coordinate system. diff --git a/src/display/nr-light.h b/src/display/nr-light.h index 022243bfc..0c1235483 100644 --- a/src/display/nr-light.h +++ b/src/display/nr-light.h @@ -13,9 +13,9 @@ #include "display/nr-light-types.h" #include <2geom/forward.h> -struct SPFeDistantLight; -struct SPFePointLight; -struct SPFeSpotLight; +class SPFeDistantLight; +class SPFePointLight; +class SPFeSpotLight; namespace Inkscape { namespace Filters { diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp index 26d70ad15..317f38635 100644 --- a/src/display/nr-style.cpp +++ b/src/display/nr-style.cpp @@ -54,6 +54,20 @@ NRStyle::NRStyle() , line_join(CAIRO_LINE_JOIN_MITER) , fill_pattern(NULL) , stroke_pattern(NULL) + , text_decoration_line(TEXT_DECORATION_LINE_CLEAR) + , text_decoration_style(TEXT_DECORATION_STYLE_CLEAR) + , phase_length(0.0) + , tspan_line_start(false) + , tspan_line_end(false) + , tspan_width(0) + , ascender(0) + , descender(0) + , line_gap(0) + , underline_thickness(0) + , underline_position(0) + , line_through_thickness(0) + , line_through_position(0) + , font_size(0) {} NRStyle::~NRStyle() @@ -63,6 +77,8 @@ NRStyle::~NRStyle() if (dash){ delete [] dash; } + fill.clear(); + stroke.clear(); } void NRStyle::set(SPStyle *style) @@ -144,6 +160,50 @@ void NRStyle::set(SPStyle *style) dash = NULL; } + text_decoration_line = TEXT_DECORATION_LINE_CLEAR; + if(style->text_decoration_line.inherit ){ text_decoration_line |= TEXT_DECORATION_LINE_INHERIT; } + if(style->text_decoration_line.underline ){ text_decoration_line |= TEXT_DECORATION_LINE_UNDERLINE + TEXT_DECORATION_LINE_SET; } + if(style->text_decoration_line.overline ){ text_decoration_line |= TEXT_DECORATION_LINE_OVERLINE + TEXT_DECORATION_LINE_SET; } + if(style->text_decoration_line.line_through){ text_decoration_line |= TEXT_DECORATION_LINE_LINETHROUGH + TEXT_DECORATION_LINE_SET; } + if(style->text_decoration_line.blink ){ text_decoration_line |= TEXT_DECORATION_LINE_BLINK + TEXT_DECORATION_LINE_SET; } + + text_decoration_style = TEXT_DECORATION_STYLE_CLEAR; + if(style->text_decoration_style.inherit ){ text_decoration_style |= TEXT_DECORATION_STYLE_INHERIT; } + if(style->text_decoration_style.solid ){ text_decoration_style |= TEXT_DECORATION_STYLE_SOLID + TEXT_DECORATION_STYLE_SET; } + if(style->text_decoration_style.isdouble ){ text_decoration_style |= TEXT_DECORATION_STYLE_ISDOUBLE + TEXT_DECORATION_STYLE_SET; } + if(style->text_decoration_style.dotted ){ text_decoration_style |= TEXT_DECORATION_STYLE_DOTTED + TEXT_DECORATION_STYLE_SET; } + if(style->text_decoration_style.dashed ){ text_decoration_style |= TEXT_DECORATION_STYLE_DASHED + TEXT_DECORATION_STYLE_SET; } + if(style->text_decoration_style.wavy ){ text_decoration_style |= TEXT_DECORATION_STYLE_WAVY + TEXT_DECORATION_STYLE_SET; } + + if( style->text_decoration_color.set || + style->text_decoration_color.inherit || + style->text_decoration_color.currentcolor || + style->text_decoration_color.colorSet){ + text_decoration_color.set(style->text_decoration_color.value.color); + text_decoration_useColor = true; + } + else { + text_decoration_color.clear(); + text_decoration_useColor = false; + } + + if(text_decoration_line != TEXT_DECORATION_LINE_CLEAR){ + phase_length = style->text_decoration_data.phase_length; + tspan_line_start = style->text_decoration_data.tspan_line_start; + tspan_line_end = style->text_decoration_data.tspan_line_end; + tspan_width = style->text_decoration_data.tspan_width; + ascender = style->text_decoration_data.ascender; + descender = style->text_decoration_data.descender; + line_gap = style->text_decoration_data.line_gap; + underline_thickness = style->text_decoration_data.underline_thickness; + underline_position = style->text_decoration_data.underline_position; + line_through_thickness = style->text_decoration_data.line_through_thickness; + line_through_position = style->text_decoration_data.line_through_position; + font_size = style->font_size.computed; + } + + text_direction = style->direction.computed; + update(); } @@ -153,8 +213,10 @@ bool NRStyle::prepareFill(Inkscape::DrawingContext &ct, Geom::OptRect const &pai if (!fill_pattern) { switch (fill.type) { case PAINT_SERVER: { - fill_pattern = sp_paint_server_create_pattern(fill.server, ct.raw(), paintbox, fill.opacity); - } break; + //fill_pattern = sp_paint_server_create_pattern(fill.server, ct.raw(), paintbox, fill.opacity); + fill_pattern = fill.server->pattern_new(ct.raw(), paintbox, fill.opacity); + + } break; case PAINT_COLOR: { SPColor const &c = fill.color; fill_pattern = cairo_pattern_create_rgba( @@ -178,8 +240,10 @@ bool NRStyle::prepareStroke(Inkscape::DrawingContext &ct, Geom::OptRect const &p if (!stroke_pattern) { switch (stroke.type) { case PAINT_SERVER: { - stroke_pattern = sp_paint_server_create_pattern(stroke.server, ct.raw(), paintbox, stroke.opacity); - } break; + //stroke_pattern = sp_paint_server_create_pattern(stroke.server, ct.raw(), paintbox, stroke.opacity); + stroke_pattern = stroke.server->pattern_new(ct.raw(), paintbox, stroke.opacity); + + } break; case PAINT_COLOR: { SPColor const &c = stroke.color; stroke_pattern = cairo_pattern_create_rgba( diff --git a/src/display/nr-style.h b/src/display/nr-style.h index cd0bd208f..8fd736cc3 100644 --- a/src/display/nr-style.h +++ b/src/display/nr-style.h @@ -16,7 +16,7 @@ #include <2geom/rect.h> #include "color.h" -struct SPPaintServer; +class SPPaintServer; struct SPStyle; namespace Inkscape { @@ -67,6 +67,43 @@ struct NRStyle { cairo_pattern_t *fill_pattern; cairo_pattern_t *stroke_pattern; + +#define TEXT_DECORATION_LINE_CLEAR 0x00 +#define TEXT_DECORATION_LINE_SET 0x01 +#define TEXT_DECORATION_LINE_INHERIT 0x02 +#define TEXT_DECORATION_LINE_UNDERLINE 0x04 +#define TEXT_DECORATION_LINE_OVERLINE 0x08 +#define TEXT_DECORATION_LINE_LINETHROUGH 0x10 +#define TEXT_DECORATION_LINE_BLINK 0x20 + +#define TEXT_DECORATION_STYLE_CLEAR 0x00 +#define TEXT_DECORATION_STYLE_SET 0x01 +#define TEXT_DECORATION_STYLE_INHERIT 0x02 +#define TEXT_DECORATION_STYLE_SOLID 0x04 +#define TEXT_DECORATION_STYLE_ISDOUBLE 0x08 +#define TEXT_DECORATION_STYLE_DOTTED 0x10 +#define TEXT_DECORATION_STYLE_DASHED 0x20 +#define TEXT_DECORATION_STYLE_WAVY 0x40 + + int text_decoration_line; + int text_decoration_style; + Paint text_decoration_color; + bool text_decoration_useColor; // if false, use whatever the glyph color was + // These are the same as in style.h + float phase_length; + bool tspan_line_start; + bool tspan_line_end; + float tspan_width; + float ascender; + float descender; + float line_gap; + float underline_thickness; + float underline_position; + float line_through_thickness; + float line_through_position; + float font_size; + + int text_direction; }; #endif diff --git a/src/display/nr-svgfonts.h b/src/display/nr-svgfonts.h index 1101f93f2..e1bb047bb 100644 --- a/src/display/nr-svgfonts.h +++ b/src/display/nr-svgfonts.h @@ -17,9 +17,9 @@ #include <sigc++/connection.h> class SvgFont; -struct SPFont; -struct SPGlyph; -struct SPMissingGlyph; +class SPFont; +class SPGlyph; +class SPMissingGlyph; struct _GdkEventExpose; typedef _GdkEventExpose GdkEventExpose; diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp index 45dc38a37..3636319df 100644 --- a/src/display/sodipodi-ctrl.cpp +++ b/src/display/sodipodi-ctrl.cpp @@ -108,81 +108,50 @@ sp_ctrl_set_property(GObject *object, guint prop_id, const GValue *value, GParam ctrl = SP_CTRL (object); switch (prop_id) { - case ARG_SHAPE: { - ctrl->shape = (SPCtrlShapeType) g_value_get_int(value); - ctrl->build = FALSE; - sp_canvas_item_request_update(item); - } - break; - - case ARG_MODE: { - ctrl->mode = (SPCtrlModeType) g_value_get_int(value); - ctrl->build = FALSE; - sp_canvas_item_request_update(item); - } - break; - - case ARG_ANCHOR: { - ctrl->anchor = (SPAnchorType) g_value_get_int(value); - ctrl->build = FALSE; - sp_canvas_item_request_update(item); - } - break; - - case ARG_SIZE: { - ctrl->span = (gint)((g_value_get_double(value) - 1.0) / 2.0 + 0.5); - ctrl->defined = (ctrl->span > 0); - ctrl->build = FALSE; - sp_canvas_item_request_update(item); - } - break; - - case ARG_FILLED: { - ctrl->filled = g_value_get_boolean(value); - ctrl->build = FALSE; - sp_canvas_item_request_update(item); - } - break; - - case ARG_FILL_COLOR: { - guint32 fill = g_value_get_int(value); - ctrl->fill_color = fill; - ctrl->build = FALSE; - sp_canvas_item_request_update(item); - } - break; - - case ARG_STROKED: { - ctrl->stroked = g_value_get_boolean(value); - ctrl->build = FALSE; - sp_canvas_item_request_update(item); - } - break; - - case ARG_STROKE_COLOR: { - guint32 stroke = g_value_get_int(value); - ctrl->stroke_color = stroke; - ctrl->build = FALSE; - sp_canvas_item_request_update(item); - } - break; - - case ARG_PIXBUF: { - pixbuf = (GdkPixbuf*) g_value_get_pointer(value); - if (gdk_pixbuf_get_has_alpha(pixbuf)) { - ctrl->pixbuf = pixbuf; - } else { - ctrl->pixbuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); - g_object_unref(pixbuf); - } - ctrl->build = FALSE; - } - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; + case ARG_SHAPE: + ctrl->shape = (SPCtrlShapeType) g_value_get_int(value); + break; + case ARG_MODE: + ctrl->mode = (SPCtrlModeType) g_value_get_int(value); + break; + case ARG_ANCHOR: + ctrl->anchor = (SPAnchorType) g_value_get_int(value); + break; + case ARG_SIZE: + ctrl->width = (gint)(g_value_get_double(value) / 2.0); + ctrl->height = ctrl->width; + ctrl->defined = (ctrl->width > 0); + break; + case ARG_FILLED: + ctrl->filled = g_value_get_boolean(value); + break; + case ARG_FILL_COLOR: + ctrl->fill_color = (guint32)g_value_get_int(value); + break; + case ARG_STROKED: + ctrl->stroked = g_value_get_boolean(value); + break; + case ARG_STROKE_COLOR: + ctrl->stroke_color = (guint32)g_value_get_int(value); + break; + case ARG_PIXBUF: + pixbuf = (GdkPixbuf*) g_value_get_pointer(value); + // A pixbuf defines it's own size, don't mess about with size. + ctrl->width = gdk_pixbuf_get_width(pixbuf) / 2.0; + ctrl->height = gdk_pixbuf_get_height(pixbuf) / 2.0; + if (gdk_pixbuf_get_has_alpha(pixbuf)) { + ctrl->pixbuf = pixbuf; + } else { + ctrl->pixbuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); + g_object_unref(pixbuf); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + return; // Do not do an update } + ctrl->build = FALSE; + sp_canvas_item_request_update(item); } static void @@ -206,7 +175,7 @@ sp_ctrl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec break; case ARG_SIZE: - g_value_set_double(value, ctrl->span); + g_value_set_double(value, ctrl->width); break; case ARG_FILLED: @@ -241,7 +210,8 @@ sp_ctrl_init (SPCtrl *ctrl) ctrl->shape = SP_CTRL_SHAPE_SQUARE; ctrl->mode = SP_CTRL_MODE_COLOR; ctrl->anchor = SP_ANCHOR_CENTER; - ctrl->span = 3; + ctrl->width = 3; + ctrl->height = 3; ctrl->defined = TRUE; ctrl->shown = FALSE; ctrl->build = FALSE; @@ -250,12 +220,6 @@ sp_ctrl_init (SPCtrl *ctrl) ctrl->fill_color = 0x000000ff; ctrl->stroke_color = 0x000000ff; - // This way we make sure that the first sp_ctrl_update() call finishes properly; - // in subsequent calls it will not update anything it the control hasn't moved - // Consider for example the case in which a snap indicator is drawn at (0, 0); - // If moveto() is called then it will not set _moved to true because we're initially already at (0, 0) - ctrl->_moved = true; // Is this flag ever going to be set back to false? I can't find where that is supposed to happen - new (&ctrl->box) Geom::IntRect(0,0,0,0); ctrl->cache = NULL; ctrl->pixbuf = NULL; @@ -292,16 +256,14 @@ sp_ctrl_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int fla sp_canvas_item_reset_bounds (item); - if (!ctrl->_moved) return; - if (ctrl->shown) { item->canvas->requestRedraw(ctrl->box.left(), ctrl->box.top(), ctrl->box.right() + 1, ctrl->box.bottom() + 1); } if (!ctrl->defined) return; - x = (gint) ((affine[4] > 0) ? (affine[4] + 0.5) : (affine[4] - 0.5)) - ctrl->span; - y = (gint) ((affine[5] > 0) ? (affine[5] + 0.5) : (affine[5] - 0.5)) - ctrl->span; + x = (gint) ((affine[4] > 0) ? (affine[4] + 0.5) : (affine[4] - 0.5)) - ctrl->width; + y = (gint) ((affine[5] > 0) ? (affine[5] + 0.5) : (affine[5] - 0.5)) - ctrl->height; switch (ctrl->anchor) { case SP_ANCHOR_N: @@ -312,13 +274,13 @@ sp_ctrl_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int fla case SP_ANCHOR_NW: case SP_ANCHOR_W: case SP_ANCHOR_SW: - x += ctrl->span; + x += ctrl->width; break; case SP_ANCHOR_NE: case SP_ANCHOR_E: case SP_ANCHOR_SE: - x -= (ctrl->span + 1); + x -= (ctrl->width + 1); break; } @@ -331,17 +293,17 @@ sp_ctrl_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int fla case SP_ANCHOR_NW: case SP_ANCHOR_N: case SP_ANCHOR_NE: - y += ctrl->span; + y += ctrl->height; break; case SP_ANCHOR_SW: case SP_ANCHOR_S: case SP_ANCHOR_SE: - y -= (ctrl->span + 1); + y -= (ctrl->height + 1); break; } - ctrl->box = Geom::IntRect::from_xywh(x, y, 2*ctrl->span, 2*ctrl->span); + ctrl->box = Geom::IntRect::from_xywh(x, y, 2*ctrl->width, 2*ctrl->height); sp_canvas_update_bbox (item, ctrl->box.left(), ctrl->box.top(), ctrl->box.right() + 1, ctrl->box.bottom() + 1); } @@ -360,7 +322,7 @@ static void sp_ctrl_build_cache (SPCtrl *ctrl) { guint32 *p, *q; - gint size, x, y, z, s, a, side, c; + gint size, x, y, z, s, a, width, height, c; guint32 stroke_color, fill_color; if (ctrl->filled) { @@ -382,11 +344,11 @@ sp_ctrl_build_cache (SPCtrl *ctrl) stroke_color = fill_color; } - - side = (ctrl->span * 2 +1); - c = ctrl->span; - size = side * side; - if (side < 2) return; + width = (ctrl->width * 2 +1); + height = (ctrl->height * 2 +1); + c = ctrl->width; // Only used for pre-set square drawing + size = width * height; + if (width < 2) return; if (ctrl->cache) delete[] ctrl->cache; ctrl->cache = new guint32[size]; @@ -395,19 +357,19 @@ sp_ctrl_build_cache (SPCtrl *ctrl) case SP_CTRL_SHAPE_SQUARE: p = ctrl->cache; // top edge - for (x=0; x < side; x++) { + for (x=0; x < width; x++) { *p++ = stroke_color; } // middle - for (y = 2; y < side; y++) { + for (y = 2; y < height; y++) { *p++ = stroke_color; // stroke at first and last pixel - for (x=2; x < side; x++) { + for (x=2; x < width; x++) { *p++ = fill_color; // fill in the middle } *p++ = stroke_color; } // bottom edge - for (x=0; x < side; x++) { + for (x=0; x < width; x++) { *p++ = stroke_color; } ctrl->build = TRUE; @@ -415,19 +377,19 @@ sp_ctrl_build_cache (SPCtrl *ctrl) case SP_CTRL_SHAPE_DIAMOND: p = ctrl->cache; - for (y = 0; y < side; y++) { + for (y = 0; y < height; y++) { z = abs (c - y); for (x = 0; x < z; x++) { *p++ = 0; } *p++ = stroke_color; x++; - for (; x < side - z -1; x++) { + for (; x < width - z -1; x++) { *p++ = fill_color; } if (z != c) { *p++ = stroke_color; x++; } - for (; x < side; x++) { + for (; x < width; x++) { *p++ = 0; } } @@ -462,7 +424,7 @@ sp_ctrl_build_cache (SPCtrl *ctrl) *q-- = stroke_color; x++; } while (x <= c+z); - while (x < side) { + while (x < width) { *p++ = 0; *q-- = 0; x++; @@ -474,7 +436,7 @@ sp_ctrl_build_cache (SPCtrl *ctrl) case SP_CTRL_SHAPE_CROSS: p = ctrl->cache; - for (y = 0; y < side; y++) { + for (y = 0; y < height; y++) { z = abs (c - y); for (x = 0; x < c-z; x++) { *p++ = 0; @@ -486,7 +448,7 @@ sp_ctrl_build_cache (SPCtrl *ctrl) if (z != 0) { *p++ = stroke_color; x++; } - for (; x < side; x++) { + for (; x < width; x++) { *p++ = 0; } } @@ -499,12 +461,12 @@ sp_ctrl_build_cache (SPCtrl *ctrl) unsigned int rs; px = gdk_pixbuf_get_pixels (ctrl->pixbuf); rs = gdk_pixbuf_get_rowstride (ctrl->pixbuf); - for (y = 0; y < side; y++){ + for (y = 0; y < height; y++){ guint32 *d; unsigned char *s; s = px + y * rs; - d = ctrl->cache + side * y; - for (x = 0; x < side; x++) { + d = ctrl->cache + height * y; + for (x = 0; x < width; x++) { if (s[3] < 0x80) { *d++ = 0; } else if (s[0] < 0x80) { @@ -527,9 +489,9 @@ sp_ctrl_build_cache (SPCtrl *ctrl) guint32 *px; guchar *data = gdk_pixbuf_get_pixels (ctrl->pixbuf); p = ctrl->cache; - for (y = 0; y < side; y++){ + for (y = 0; y < height; y++){ px = reinterpret_cast<guint32*>(data + y * r); - for (x = 0; x < side; x++) { + for (x = 0; x < width; x++) { *p++ = *px++; } } @@ -566,8 +528,8 @@ sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf) sp_ctrl_build_cache (ctrl); } - int w, h; - w = h = (ctrl->span * 2 +1); + int w = (ctrl->width * 2 + 1); + int h = (ctrl->height * 2 + 1); // The code below works even when the target is not an image surface if (ctrl->mode == SP_CTRL_MODE_XOR) { @@ -627,7 +589,6 @@ sp_ctrl_render (SPCanvasItem *item, SPCanvasBuf *buf) void SPCtrl::moveto (Geom::Point const p) { if (p != _point) { sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (this), Geom::Affine(Geom::Translate (p))); - _moved = true; } _point = p; } diff --git a/src/display/sodipodi-ctrl.h b/src/display/sodipodi-ctrl.h index cd0fcadf1..ecdb896a7 100644 --- a/src/display/sodipodi-ctrl.h +++ b/src/display/sodipodi-ctrl.h @@ -37,7 +37,8 @@ struct SPCtrl : public SPCanvasItem { SPCtrlShapeType shape; SPCtrlModeType mode; SPAnchorType anchor; - gint span; + gint width; + gint height; guint defined : 1; guint shown : 1; guint build : 1; @@ -45,7 +46,6 @@ struct SPCtrl : public SPCanvasItem { guint stroked : 1; guint32 fill_color; guint32 stroke_color; - bool _moved; Geom::IntRect box; /* NB! x1 & y1 are included */ guint32 *cache; diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 9adb96642..455f628bc 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -2301,8 +2301,6 @@ int SPCanvasImpl::do_update(SPCanvas *canvas) gint SPCanvasImpl::idle_handler(gpointer data) { - GDK_THREADS_ENTER (); - SPCanvas *canvas = SP_CANVAS (data); int const ret = do_update (canvas); @@ -2312,15 +2310,13 @@ gint SPCanvasImpl::idle_handler(gpointer data) canvas->idle_id = 0; } - GDK_THREADS_LEAVE (); - return !ret; } void SPCanvasImpl::add_idle(SPCanvas *canvas) { if (canvas->idle_id == 0) { - canvas->idle_id = g_idle_add_full(UPDATE_PRIORITY, idle_handler, canvas, NULL); + canvas->idle_id = gdk_threads_add_idle_full(UPDATE_PRIORITY, idle_handler, canvas, NULL); } } |
