summaryrefslogtreecommitdiffstats
path: root/src/display
diff options
context:
space:
mode:
authorSebastian Wüst <sebi@timewaster.de>2013-10-20 15:32:08 +0000
committerSebastian Wüst <sebi@timewaster.de>2013-10-20 15:32:08 +0000
commit82908f949129e1fcbf62002799ee7b1b77986eed (patch)
treec02098dd7720cdf424f2793ecd3ddac2ea86b969 /src/display
parentchanged text (diff)
parentFix build errors with clang 3.3 and c++11 enabled. (diff)
downloadinkscape-82908f949129e1fcbf62002799ee7b1b77986eed.tar.gz
inkscape-82908f949129e1fcbf62002799ee7b1b77986eed.zip
merge from trunk
(bzr r12417.1.24)
Diffstat (limited to 'src/display')
-rw-r--r--src/display/cairo-utils.cpp479
-rw-r--r--src/display/cairo-utils.h68
-rw-r--r--src/display/canvas-axonomgrid.cpp104
-rw-r--r--src/display/canvas-axonomgrid.h2
-rw-r--r--src/display/canvas-grid.cpp129
-rw-r--r--src/display/canvas-grid.h8
-rw-r--r--src/display/drawing-context.h15
-rw-r--r--src/display/drawing-group.cpp1
-rw-r--r--src/display/drawing-image.cpp210
-rw-r--r--src/display/drawing-image.h9
-rw-r--r--src/display/drawing-item.cpp58
-rw-r--r--src/display/drawing-item.h5
-rw-r--r--src/display/drawing-text.cpp304
-rw-r--r--src/display/drawing-text.h15
-rw-r--r--src/display/guideline.cpp12
-rw-r--r--src/display/nr-filter-blend.cpp164
-rw-r--r--src/display/nr-filter-diffuselighting.h6
-rw-r--r--src/display/nr-filter-image.cpp81
-rw-r--r--src/display/nr-filter-image.h7
-rw-r--r--src/display/nr-filter-slot.cpp18
-rw-r--r--src/display/nr-filter-specularlighting.h6
-rw-r--r--src/display/nr-filter.cpp17
-rw-r--r--src/display/nr-filter.h6
-rw-r--r--src/display/nr-light.h6
-rw-r--r--src/display/nr-style.cpp72
-rw-r--r--src/display/nr-style.h39
-rw-r--r--src/display/nr-svgfonts.h6
-rw-r--r--src/display/sodipodi-ctrl.cpp191
-rw-r--r--src/display/sodipodi-ctrl.h4
-rw-r--r--src/display/sp-canvas.cpp6
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);
}
}