summaryrefslogtreecommitdiffstats
path: root/src/display
diff options
context:
space:
mode:
Diffstat (limited to 'src/display')
-rw-r--r--src/display/CMakeLists.txt2
-rw-r--r--src/display/Makefile_insert5
-rw-r--r--src/display/cairo-templates.h12
-rw-r--r--src/display/cairo-utils.cpp638
-rw-r--r--src/display/cairo-utils.h95
-rw-r--r--src/display/canvas-arena.cpp42
-rw-r--r--src/display/canvas-axonomgrid.cpp280
-rw-r--r--src/display/canvas-axonomgrid.h25
-rw-r--r--src/display/canvas-bpath.cpp21
-rw-r--r--src/display/canvas-bpath.h2
-rw-r--r--src/display/canvas-grid.cpp252
-rw-r--r--src/display/canvas-grid.h24
-rw-r--r--src/display/canvas-temporary-item.cpp3
-rw-r--r--src/display/canvas-text.cpp25
-rw-r--r--src/display/canvas-text.h2
-rw-r--r--src/display/curve.cpp3
-rw-r--r--src/display/drawing-context.cpp22
-rw-r--r--src/display/drawing-context.h27
-rw-r--r--src/display/drawing-group.cpp13
-rw-r--r--src/display/drawing-group.h6
-rw-r--r--src/display/drawing-image.cpp169
-rw-r--r--src/display/drawing-image.h8
-rw-r--r--src/display/drawing-item.cpp249
-rw-r--r--src/display/drawing-item.h24
-rw-r--r--src/display/drawing-shape.cpp166
-rw-r--r--src/display/drawing-shape.h13
-rw-r--r--src/display/drawing-surface.cpp62
-rw-r--r--src/display/drawing-surface.h2
-rw-r--r--src/display/drawing-text.cpp426
-rw-r--r--src/display/drawing-text.h21
-rw-r--r--src/display/drawing.cpp41
-rw-r--r--src/display/drawing.h7
-rw-r--r--src/display/gnome-canvas-acetate.cpp17
-rw-r--r--src/display/guideline.cpp32
-rw-r--r--src/display/guideline.h2
-rw-r--r--src/display/nr-filter-blend.cpp226
-rw-r--r--src/display/nr-filter-blend.h16
-rw-r--r--src/display/nr-filter-colormatrix.cpp89
-rw-r--r--src/display/nr-filter-colormatrix.h9
-rw-r--r--src/display/nr-filter-component-transfer.cpp36
-rw-r--r--src/display/nr-filter-composite.cpp55
-rw-r--r--src/display/nr-filter-convolve-matrix.cpp13
-rw-r--r--src/display/nr-filter-diffuselighting.cpp44
-rw-r--r--src/display/nr-filter-diffuselighting.h3
-rw-r--r--src/display/nr-filter-displacement-map.cpp11
-rw-r--r--src/display/nr-filter-flood.cpp48
-rw-r--r--src/display/nr-filter-flood.h2
-rw-r--r--src/display/nr-filter-gaussian.cpp50
-rw-r--r--src/display/nr-filter-image.cpp227
-rw-r--r--src/display/nr-filter-image.h8
-rw-r--r--src/display/nr-filter-merge.cpp10
-rw-r--r--src/display/nr-filter-morphology.cpp5
-rw-r--r--src/display/nr-filter-offset.cpp19
-rw-r--r--src/display/nr-filter-primitive.cpp106
-rw-r--r--src/display/nr-filter-primitive.h9
-rw-r--r--src/display/nr-filter-skeleton.cpp3
-rw-r--r--src/display/nr-filter-slot.cpp16
-rw-r--r--src/display/nr-filter-slot.h2
-rw-r--r--src/display/nr-filter-specularlighting.cpp44
-rw-r--r--src/display/nr-filter-specularlighting.h3
-rw-r--r--src/display/nr-filter-turbulence.cpp20
-rw-r--r--src/display/nr-filter.cpp45
-rw-r--r--src/display/nr-filter.h10
-rw-r--r--src/display/nr-light.h6
-rw-r--r--src/display/nr-style.cpp138
-rw-r--r--src/display/nr-style.h63
-rw-r--r--src/display/nr-svgfonts.cpp72
-rw-r--r--src/display/nr-svgfonts.h2
-rw-r--r--src/display/snap-indicator.cpp48
-rw-r--r--src/display/sodipodi-ctrl.cpp221
-rw-r--r--src/display/sodipodi-ctrl.h4
-rw-r--r--src/display/sodipodi-ctrlrect.cpp36
-rw-r--r--src/display/sp-canvas-item.h41
-rw-r--r--src/display/sp-canvas.cpp557
-rw-r--r--src/display/sp-canvas.h7
-rw-r--r--src/display/sp-ctrlcurve.cpp216
-rw-r--r--src/display/sp-ctrlcurve.h54
-rw-r--r--src/display/sp-ctrlline.cpp28
-rw-r--r--src/display/sp-ctrlpoint.cpp20
-rw-r--r--src/display/sp-ctrlquadr.cpp17
-rw-r--r--src/display/sp-ctrlquadr.h4
81 files changed, 3768 insertions, 1633 deletions
diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt
index 7b188b286..20424c845 100644
--- a/src/display/CMakeLists.txt
+++ b/src/display/CMakeLists.txt
@@ -52,6 +52,7 @@ set(display_SRC
sodipodi-ctrlrect.cpp
sp-canvas-util.cpp
sp-canvas.cpp
+ sp-ctrlcurve.cpp
sp-ctrlline.cpp
sp-ctrlpoint.cpp
sp-ctrlquadr.cpp
@@ -116,6 +117,7 @@ set(display_SRC
sp-canvas-item.h
sp-canvas-util.h
sp-canvas.h
+ sp-ctrlcurve.h
sp-ctrlline.h
sp-ctrlpoint.h
sp-ctrlquadr.h
diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert
index 1c7a21dae..abbd89a68 100644
--- a/src/display/Makefile_insert
+++ b/src/display/Makefile_insert
@@ -47,7 +47,6 @@ ink_common_sources += \
display/guideline.h \
display/nr-3dutils.cpp \
display/nr-3dutils.h \
- display/nr-arena-forward.h \
display/nr-filter-blend.cpp \
display/nr-filter-blend.h \
display/nr-filter-colormatrix.cpp \
@@ -107,8 +106,12 @@ ink_common_sources += \
display/sodipodi-ctrlrect.h \
display/sp-canvas.cpp \
display/sp-canvas.h \
+ display/sp-canvas-item.h \
+ display/sp-canvas-group.h \
display/sp-canvas-util.cpp \
display/sp-canvas-util.h \
+ display/sp-ctrlcurve.cpp \
+ display/sp-ctrlcurve.h \
display/sp-ctrlline.cpp \
display/sp-ctrlline.h \
display/sp-ctrlpoint.cpp \
diff --git a/src/display/cairo-templates.h b/src/display/cairo-templates.h
index 45b6790e6..57ec98f81 100644
--- a/src/display/cairo-templates.h
+++ b/src/display/cairo-templates.h
@@ -66,9 +66,9 @@ void ink_cairo_surface_blend(cairo_surface_t *in1, cairo_surface_t *in2, cairo_s
int limit = w * h;
- guint32 *const in1_data = (guint32*) cairo_image_surface_get_data(in1);
- guint32 *const in2_data = (guint32*) cairo_image_surface_get_data(in2);
- guint32 *const out_data = (guint32*) cairo_image_surface_get_data(out);
+ guint32 *const in1_data = reinterpret_cast<guint32*>(cairo_image_surface_get_data(in1));
+ guint32 *const in2_data = reinterpret_cast<guint32*>(cairo_image_surface_get_data(in2));
+ guint32 *const out_data = reinterpret_cast<guint32*>(cairo_image_surface_get_data(out));
// NOTE
// OpenMP probably doesn't help much here.
@@ -199,8 +199,8 @@ void ink_cairo_surface_filter(cairo_surface_t *in, cairo_surface_t *out, Filter
fast_path &= (stridein == w * bppin);
fast_path &= (strideout == w * bppout);
- guint32 *const in_data = (guint32*) cairo_image_surface_get_data(in);
- guint32 *const out_data = (guint32*) cairo_image_surface_get_data(out);
+ guint32 *const in_data = reinterpret_cast<guint32*>(cairo_image_surface_get_data(in));
+ guint32 *const out_data = reinterpret_cast<guint32*>(cairo_image_surface_get_data(out));
#if HAVE_OPENMP
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
@@ -695,4 +695,4 @@ pxclamp(gint32 v, gint32 low, gint32 high) {
fill-column:99
End:
*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp
index 2e2eb42dd..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>
@@ -24,7 +26,19 @@
#include <2geom/transforms.h>
#include <2geom/sbasis-to-bezier.h>
#include "color.h"
+#include "style.h"
#include "helper/geom-curves.h"
+#include "display/cairo-templates.h"
+
+static void ink_cairo_pixbuf_cleanup(guchar *, void *);
+
+/**
+ * Key for cairo_surface_t to keep track of current color interpolation value
+ * Only the address of the structure is used, it is never initialized. See:
+ * http://www.cairographics.org/manual/cairo-Types.html#cairo-user-data-key-t
+ */
+cairo_user_data_key_t ink_color_interpolation_key;
+cairo_user_data_key_t ink_pixbuf_key;
namespace Inkscape {
@@ -106,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
/*
@@ -276,6 +663,43 @@ 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, &ink_color_interpolation_key );
+ if( data != NULL ) {
+ return (SPColorInterpolation)GPOINTER_TO_INT( data );
+ } else {
+ return SP_CSS_COLOR_INTERPOLATION_AUTO;
+ }
+}
+
+/** Set the color_interpolation_value for a Cairo surface.
+ * Transform the surface between sRGB and linearRGB if necessary. */
+void
+set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation ci) {
+
+ if( cairo_surface_get_content( surface ) != CAIRO_CONTENT_ALPHA ) {
+
+ SPColorInterpolation ci_in = get_cairo_surface_ci( surface );
+
+ if( ci_in == SP_CSS_COLOR_INTERPOLATION_SRGB &&
+ ci == SP_CSS_COLOR_INTERPOLATION_LINEARRGB ) {
+ ink_cairo_surface_srgb_to_linear( surface );
+ }
+ if( ci_in == SP_CSS_COLOR_INTERPOLATION_LINEARRGB &&
+ ci == SP_CSS_COLOR_INTERPOLATION_SRGB ) {
+ ink_cairo_surface_linear_to_srgb( surface );
+ }
+
+ 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, &ink_color_interpolation_key, cairo_surface_get_user_data(in, &ink_color_interpolation_key), NULL);
+}
+
void
ink_cairo_set_source_rgba32(cairo_t *ct, guint32 rgba)
{
@@ -324,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
@@ -395,6 +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, &ink_color_interpolation_key, cairo_surface_get_user_data(s, &ink_color_interpolation_key), NULL);
return ns;
}
@@ -547,6 +939,128 @@ void ink_cairo_surface_average_color_premul(cairo_surface_t *surface, double &r,
a = CLAMP(a, 0.0, 1.0);
}
+static guint32 srgb_to_linear( const guint32 c, const guint32 a ) {
+
+ const guint32 c1 = unpremul_alpha( c, a );
+
+ double cc = c1/255.0;
+
+ if( cc < 0.04045 ) {
+ cc /= 12.92;
+ } else {
+ cc = pow( (cc+0.055)/1.055, 2.4 );
+ }
+ cc *= 255.0;
+
+ const guint32 c2 = (int)cc;
+
+ return premul_alpha( c2, a );
+}
+
+static guint32 linear_to_srgb( const guint32 c, const guint32 a ) {
+
+ const guint32 c1 = unpremul_alpha( c, a );
+
+ double cc = c1/255.0;
+
+ if( cc < 0.0031308 ) {
+ cc *= 12.92;
+ } else {
+ cc = pow( cc, 1.0/2.4 )*1.055-0.055;
+ }
+ cc *= 255.0;
+
+ const guint32 c2 = (int)cc;
+
+ return premul_alpha( c2, a );
+}
+
+struct SurfaceSrgbToLinear {
+
+ guint32 operator()(guint32 in) {
+ EXTRACT_ARGB32(in, a,r,g,b) ; // Unneeded semi-colon for indenting
+ if( a != 0 ) {
+ r = srgb_to_linear( r, a );
+ g = srgb_to_linear( g, a );
+ b = srgb_to_linear( b, a );
+ }
+ ASSEMBLE_ARGB32(out, a,r,g,b);
+ return out;
+ }
+private:
+ /* None */
+};
+
+int ink_cairo_surface_srgb_to_linear(cairo_surface_t *surface)
+{
+ cairo_surface_flush(surface);
+ int width = cairo_image_surface_get_width(surface);
+ int height = cairo_image_surface_get_height(surface);
+ // int stride = cairo_image_surface_get_stride(surface);
+ // unsigned char *data = cairo_image_surface_get_data(surface);
+
+ ink_cairo_surface_filter( surface, surface, SurfaceSrgbToLinear() );
+
+ /* TODO convert this to OpenMP somehow */
+ // for (int y = 0; y < height; ++y, data += stride) {
+ // for (int x = 0; x < width; ++x) {
+ // guint32 px = *reinterpret_cast<guint32*>(data + 4*x);
+ // EXTRACT_ARGB32(px, a,r,g,b) ; // Unneeded semi-colon for indenting
+ // if( a != 0 ) {
+ // r = srgb_to_linear( r, a );
+ // g = srgb_to_linear( g, a );
+ // b = srgb_to_linear( b, a );
+ // }
+ // ASSEMBLE_ARGB32(px2, a,r,g,b);
+ // *reinterpret_cast<guint32*>(data + 4*x) = px2;
+ // }
+ // }
+ return width * height;
+}
+
+struct SurfaceLinearToSrgb {
+
+ guint32 operator()(guint32 in) {
+ EXTRACT_ARGB32(in, a,r,g,b) ; // Unneeded semi-colon for indenting
+ if( a != 0 ) {
+ r = linear_to_srgb( r, a );
+ g = linear_to_srgb( g, a );
+ b = linear_to_srgb( b, a );
+ }
+ ASSEMBLE_ARGB32(out, a,r,g,b);
+ return out;
+ }
+private:
+ /* None */
+};
+
+int ink_cairo_surface_linear_to_srgb(cairo_surface_t *surface)
+{
+ cairo_surface_flush(surface);
+ int width = cairo_image_surface_get_width(surface);
+ int height = cairo_image_surface_get_height(surface);
+ // int stride = cairo_image_surface_get_stride(surface);
+ // unsigned char *data = cairo_image_surface_get_data(surface);
+
+ ink_cairo_surface_filter( surface, surface, SurfaceLinearToSrgb() );
+
+ // /* TODO convert this to OpenMP somehow */
+ // for (int y = 0; y < height; ++y, data += stride) {
+ // for (int x = 0; x < width; ++x) {
+ // guint32 px = *reinterpret_cast<guint32*>(data + 4*x);
+ // EXTRACT_ARGB32(px, a,r,g,b) ; // Unneeded semi-colon for indenting
+ // if( a != 0 ) {
+ // r = linear_to_srgb( r, a );
+ // g = linear_to_srgb( g, a );
+ // b = linear_to_srgb( b, a );
+ // }
+ // ASSEMBLE_ARGB32(px2, a,r,g,b);
+ // *reinterpret_cast<guint32*>(data + 4*x) = px2;
+ // }
+ // }
+ return width * height;
+}
+
cairo_pattern_t *
ink_cairo_pattern_create_checkerboard()
{
@@ -573,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. */
@@ -672,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);
}
/**
@@ -686,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)
@@ -715,4 +1281,4 @@ guint32 argb32_from_rgba(guint32 in)
fill-column:99
End:
*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h
index e67872891..505e2ca77 100644
--- a/src/display/cairo-utils.h
+++ b/src/display/cairo-utils.h
@@ -12,13 +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 {
@@ -79,13 +81,74 @@ 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);
+void convert_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation cif);
+
void ink_cairo_set_source_color(cairo_t *ct, SPColor const &color, double opacity);
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 &);
@@ -102,14 +165,15 @@ guint32 ink_cairo_surface_average_color(cairo_surface_t *surface);
void ink_cairo_surface_average_color(cairo_surface_t *surface, double &r, double &g, double &b, double &a);
void ink_cairo_surface_average_color_premul(cairo_surface_t *surface, double &r, double &g, double &b, double &a);
+double srgb_to_linear( const double c );
+int ink_cairo_surface_srgb_to_linear(cairo_surface_t *surface);
+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);
@@ -118,13 +182,13 @@ G_GNUC_CONST guint32 argb32_from_rgba(guint32 in);
G_GNUC_CONST inline guint32
-premul_alpha(guint32 color, guint32 alpha)
+premul_alpha(const guint32 color, const guint32 alpha)
{
- guint32 temp = alpha * color + 128;
+ const guint32 temp = alpha * color + 128;
return (temp + (temp >> 8)) >> 8;
}
G_GNUC_CONST inline guint32
-unpremul_alpha(guint32 color, guint32 alpha)
+unpremul_alpha(const guint32 color, const guint32 alpha)
{
// NOTE: you must check for alpha != 0 yourself.
return (255 * color + alpha/2) / alpha;
@@ -144,6 +208,15 @@ void feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv);
#define ASSEMBLE_ARGB32(px,a,r,g,b) \
guint32 px = (a << 24) | (r << 16) | (g << 8) | b;
+inline double srgb_to_linear( const double c ) {
+ if( c < 0.04045 ) {
+ return c / 12.92;
+ } else {
+ return pow( (c+0.055)/1.055, 2.4 );
+ }
+}
+
+
namespace Inkscape {
namespace Display
@@ -184,4 +257,4 @@ inline guint AssembleARGB32(guint32 a, guint32 r, guint32 g, guint32 b)
fill-column:99
End:
*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/display/canvas-arena.cpp b/src/display/canvas-arena.cpp
index 9aa119be9..404a94828 100644
--- a/src/display/canvas-arena.cpp
+++ b/src/display/canvas-arena.cpp
@@ -32,7 +32,7 @@ enum {
static void sp_canvas_arena_class_init(SPCanvasArenaClass *klass);
static void sp_canvas_arena_init(SPCanvasArena *group);
-static void sp_canvas_arena_destroy(GtkObject *object);
+static void sp_canvas_arena_destroy(SPCanvasItem *object);
static void sp_canvas_arena_item_deleted(SPCanvasArena *arena, Inkscape::DrawingItem *item);
static void sp_canvas_arena_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
@@ -93,24 +93,19 @@ sp_canvas_arena_get_type (void)
static void
sp_canvas_arena_class_init (SPCanvasArenaClass *klass)
{
- GtkObjectClass *object_class;
- SPCanvasItemClass *item_class;
-
- object_class = (GtkObjectClass *) klass;
- item_class = (SPCanvasItemClass *) klass;
+ SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass);
signals[ARENA_EVENT] = g_signal_new ("arena_event",
- G_TYPE_FROM_CLASS(object_class),
+ G_TYPE_FROM_CLASS(item_class),
G_SIGNAL_RUN_LAST,
((glong)((guint8*)&(klass->arena_event) - (guint8*)klass)),
NULL, NULL,
sp_marshal_INT__POINTER_POINTER,
G_TYPE_INT, 2, G_TYPE_POINTER, G_TYPE_POINTER);
- object_class->destroy = sp_canvas_arena_destroy;
-
+ item_class->destroy = sp_canvas_arena_destroy;
item_class->update = sp_canvas_arena_update;
item_class->render = sp_canvas_arena_render;
item_class->point = sp_canvas_arena_point;
@@ -147,16 +142,15 @@ sp_canvas_arena_init (SPCanvasArena *arena)
arena->active = NULL;
}
-static void
-sp_canvas_arena_destroy (GtkObject *object)
+static void sp_canvas_arena_destroy(SPCanvasItem *object)
{
- SPCanvasArena *arena = SP_CANVAS_ARENA (object);
+ SPCanvasArena *arena = SP_CANVAS_ARENA(object);
delete arena->observer;
arena->drawing.~Drawing();
- if (GTK_OBJECT_CLASS (parent_class)->destroy)
- (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+ if (SP_CANVAS_ITEM_CLASS(parent_class)->destroy)
+ (* SP_CANVAS_ITEM_CLASS(parent_class)->destroy) (object);
}
static void
@@ -222,10 +216,10 @@ sp_canvas_arena_render (SPCanvasItem *item, SPCanvasBuf *buf)
Geom::OptIntRect r = buf->rect;
if (!r || r->hasZeroArea()) return;
- Inkscape::DrawingContext ct(buf->ct, r->min());
+ Inkscape::DrawingContext dc(buf->ct, r->min());
arena->drawing.update(Geom::IntRect::infinite(), arena->ctx);
- arena->drawing.render(ct, *r);
+ arena->drawing.render(dc, *r);
}
static double
@@ -322,6 +316,18 @@ sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event)
ret = sp_canvas_arena_send_event (arena, event);
break;
+ case GDK_SCROLL: {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool wheelzooms = prefs->getBool("/options/wheelzooms/value");
+ bool ctrl = (event->scroll.state & GDK_CONTROL_MASK);
+ if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) {
+ /* Zoom is emitted by the canvas as well, ignore here */
+ return FALSE;
+ }
+ ret = sp_canvas_arena_send_event (arena, event);
+ break;
+ }
+
default:
/* Just send event */
ret = sp_canvas_arena_send_event (arena, event);
@@ -380,9 +386,9 @@ sp_canvas_arena_render_surface (SPCanvasArena *ca, cairo_surface_t *surface, Geo
g_return_if_fail (ca != NULL);
g_return_if_fail (SP_IS_CANVAS_ARENA (ca));
- Inkscape::DrawingContext ct(surface, r.min());
+ Inkscape::DrawingContext dc(surface, r.min());
ca->drawing.update(Geom::IntRect::infinite(), ca->ctx);
- ca->drawing.render(ct, r);
+ ca->drawing.render(dc, r);
}
/*
diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp
index 478a33044..858312f5b 100644
--- a/src/display/canvas-axonomgrid.cpp
+++ b/src/display/canvas-axonomgrid.cpp
@@ -2,7 +2,7 @@
* Authors:
* Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
*
- * Copyright (C) 2006-2011 Authors
+ * Copyright (C) 2006-2012 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -13,44 +13,50 @@
* to the right). The x-axis will always have an angle between 0 and 90 degrees.
*/
- /*
- * TODO:
- * THIS FILE AND THE HEADER FILE NEED CLEANING UP. PLEASE DO NOT HESISTATE TO DO SO.
- */
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
+#include <glibmm/threads.h>
+#endif
+
+#include <gtkmm/box.h>
+#include <gtkmm/label.h>
+
+#if WITH_GTKMM_3_0
+# include <gtkmm/grid.h>
+#else
+# include <gtkmm/table.h>
+#endif
+
+#include <glibmm/i18n.h>
-#include "ui/widget/registered-widget.h"
#include "display/canvas-axonomgrid.h"
-#include "2geom/line.h"
+
+#include "ui/widget/registered-widget.h"
#include "desktop.h"
-#include "canvas-grid.h"
#include "desktop-handles.h"
#include "display/cairo-utils.h"
#include "display/canvas-grid.h"
#include "display/sp-canvas-util.h"
+#include "display/sp-canvas.h"
#include "document.h"
-#include "helper/units.h"
#include "inkscape.h"
#include "preferences.h"
#include "sp-namedview.h"
#include "sp-object.h"
#include "svg/svg-color.h"
+#include "2geom/line.h"
+#include "2geom/angle.h"
#include "util/mathfns.h"
-#include "xml/node-event-vector.h"
#include "round.h"
-#include "display/sp-canvas.h"
+#include "util/units.h"
-#include <gtkmm/box.h>
-#include <gtkmm/label.h>
-#include <gtkmm/table.h>
+using Inkscape::Util::unit_table;
enum Dim3 { X=0, Y, Z };
-#ifndef M_PI
-# define M_PI 3.14159265358979323846
-#endif
-
-static double deg_to_rad(double deg) { return deg*M_PI/180.0;}
-
/**
* This function calls Cairo to render a line on a particular canvas buffer.
* Coordinates are interpreted as SCREENcoordinates
@@ -91,28 +97,60 @@ namespace Inkscape {
#define SPACE_SIZE_X 15
#define SPACE_SIZE_Y 10
static inline void
+#if WITH_GTKMM_3_0
+attach_all(Gtk::Grid &table, Gtk::Widget const *const arr[], unsigned size, int start = 0)
+#else
attach_all(Gtk::Table &table, Gtk::Widget const *const arr[], unsigned size, int start = 0)
+#endif
{
for (unsigned i=0, r=start; i<size/sizeof(Gtk::Widget*); i+=2) {
if (arr[i] && arr[i+1]) {
+#if WITH_GTKMM_3_0
+ (const_cast<Gtk::Widget&>(*arr[i])).set_hexpand();
+ (const_cast<Gtk::Widget&>(*arr[i])).set_valign(Gtk::ALIGN_CENTER);
+ table.attach(const_cast<Gtk::Widget&>(*arr[i]), 1, r, 1, 1);
+
+ (const_cast<Gtk::Widget&>(*arr[i+1])).set_hexpand();
+ (const_cast<Gtk::Widget&>(*arr[i+1])).set_valign(Gtk::ALIGN_CENTER);
+ table.attach(const_cast<Gtk::Widget&>(*arr[i+1]), 2, r, 1, 1);
+#else
table.attach (const_cast<Gtk::Widget&>(*arr[i]), 1, 2, r, r+1,
Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 2, 3, r, r+1,
Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
+#endif
} else {
if (arr[i+1]) {
+#if WITH_GTKMM_3_0
+ (const_cast<Gtk::Widget&>(*arr[i+1])).set_hexpand();
+ (const_cast<Gtk::Widget&>(*arr[i+1])).set_valign(Gtk::ALIGN_CENTER);
+ table.attach(const_cast<Gtk::Widget&>(*arr[i+1]), 1, r, 2, 1);
+#else
table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 1, 3, r, r+1,
Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
+#endif
} else if (arr[i]) {
Gtk::Label& label = reinterpret_cast<Gtk::Label&> (const_cast<Gtk::Widget&>(*arr[i]));
label.set_alignment (0.0);
+#if WITH_GTKMM_3_0
+ label.set_hexpand();
+ label.set_valign(Gtk::ALIGN_CENTER);
+ table.attach(label, 0, r, 3, 1);
+#else
table.attach (label, 0, 3, r, r+1,
Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
+#endif
} else {
Gtk::HBox *space = manage (new Gtk::HBox);
space->set_size_request (SPACE_SIZE_X, SPACE_SIZE_Y);
+#if WITH_GTKMM_3_0
+ space->set_halign(Gtk::ALIGN_CENTER);
+ space->set_valign(Gtk::ALIGN_CENTER);
+ table.attach(*space, 0, r, 1, 1);
+#else
table.attach (*space, 0, 1, r, r+1,
(Gtk::AttachOptions)0, (Gtk::AttachOptions)0,0,0);
+#endif
}
}
++r;
@@ -123,22 +161,23 @@ 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;
- angle_rad[X] = deg_to_rad(angle_deg[X]);
+ angle_rad[X] = Geom::deg_to_rad(angle_deg[X]);
tan_angle[X] = tan(angle_rad[X]);
- angle_rad[Z] = deg_to_rad(angle_deg[Z]);
+ angle_rad[Z] = Geom::deg_to_rad(angle_deg[Z]);
tan_angle[Z] = tan(angle_rad[Z]);
snapper = new CanvasAxonomGridSnapper(this, &namedview->snap_manager, 0);
@@ -151,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) {
@@ -233,33 +215,36 @@ 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;
}
if ( (value = repr->attribute("gridanglex")) ) {
angle_deg[X] = g_ascii_strtod(value, NULL);
- if (angle_deg[X] < 1.0) angle_deg[X] = 1.0;
+ if (angle_deg[X] < 0.) angle_deg[X] = 0.;
if (angle_deg[X] > 89.0) angle_deg[X] = 89.0;
- angle_rad[X] = deg_to_rad(angle_deg[X]);
+ angle_rad[X] = Geom::deg_to_rad(angle_deg[X]);
tan_angle[X] = tan(angle_rad[X]);
}
if ( (value = repr->attribute("gridanglez")) ) {
angle_deg[Z] = g_ascii_strtod(value, NULL);
- if (angle_deg[Z] < 1.0) angle_deg[Z] = 1.0;
+ if (angle_deg[Z] < 0.) angle_deg[Z] = 0.;
if (angle_deg[Z] > 89.0) angle_deg[Z] = 89.0;
- angle_rad[Z] = deg_to_rad(angle_deg[Z]);
+ angle_rad[Z] = Geom::deg_to_rad(angle_deg[Z]);
tan_angle[Z] = tan(angle_rad[Z]);
}
@@ -314,14 +299,17 @@ CanvasAxonomGrid::onReprAttrChanged(Inkscape::XML::Node */*repr*/, gchar const *
updateWidgets();
}
-
-
-
Gtk::Widget *
CanvasAxonomGrid::newSpecificWidget()
{
+#if WITH_GTKMM_3_0
+ Gtk::Grid *table = Gtk::manage(new Gtk::Grid());
+ table->set_row_spacing(2);
+ table->set_column_spacing(2);
+#else
Gtk::Table * table = Gtk::manage( new Gtk::Table(1,1) );
table->set_spacings(2);
+#endif
_wr.setUpdating (true);
@@ -340,7 +328,7 @@ _wr.setUpdating (true);
Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gcol = Gtk::manage(
new Inkscape::UI::Widget::RegisteredColorPicker(
- _("Grid line _color:"), _("Grid line color"), _("Color of grid lines"),
+ _("Minor grid line _color:"), _("Minor grid line color"), _("Color of the minor grid lines"),
"color", "opacity", _wr, repr, doc));
Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gmcol = Gtk::manage(
@@ -379,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]);
@@ -418,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]);
@@ -475,8 +463,8 @@ CanvasAxonomGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/)
spacing_ylines = sw[Geom::X] /(tan_angle[X] + tan_angle[Z]);
lyw = sw[Geom::Y];
- lxw_x = sw[Geom::X] / tan_angle[X];
- lxw_z = sw[Geom::X] / tan_angle[Z];
+ lxw_x = Geom::are_near(tan_angle[X],0.) ? Geom::infinity() : sw[Geom::X] / tan_angle[X];
+ lxw_z = Geom::are_near(tan_angle[Z],0.) ? Geom::infinity() : sw[Geom::X] / tan_angle[Z];
if (empspacing == 0) {
scaled = true;
@@ -524,8 +512,12 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf)
for (gdouble y = xstart_y_sc; y < buf->rect.bottom(); y += lyw, xlinenum++) {
gint const x0 = buf->rect.left();
gint const y0 = round(y);
- gint const x1 = x0 + round( (buf->rect.bottom() - y) / tan_angle[X] );
- gint const y1 = buf->rect.bottom();
+ gint x1 = x0 + round( (buf->rect.bottom() - y) / tan_angle[X] );
+ gint y1 = buf->rect.bottom();
+ if ( Geom::are_near(tan_angle[X],0.) ) {
+ x1 = buf->rect.right();
+ y1 = y0;
+ }
if (!scaled && (xlinenum % empspacing) != 0) {
sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color);
@@ -534,18 +526,21 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf)
}
}
// lines starting from top side
- gdouble const xstart_x_sc = buf->rect.left() + (lxw_x - (xstart_y_sc - buf->rect.top()) / tan_angle[X]) ;
- xlinenum = xlinestart-1;
- for (gdouble x = xstart_x_sc; x < buf->rect.right(); x += lxw_x, xlinenum--) {
- gint const y0 = buf->rect.top();
- gint const y1 = buf->rect.bottom();
- gint const x0 = round(x);
- gint const x1 = x0 + round( (y1 - y0) / tan_angle[X] );
-
- if (!scaled && (xlinenum % empspacing) != 0) {
- sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color);
- } else {
- sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, _empcolor);
+ if (!Geom::are_near(tan_angle[X],0.))
+ {
+ gdouble const xstart_x_sc = buf->rect.left() + (lxw_x - (xstart_y_sc - buf->rect.top()) / tan_angle[X]) ;
+ xlinenum = xlinestart-1;
+ for (gdouble x = xstart_x_sc; x < buf->rect.right(); x += lxw_x, xlinenum--) {
+ gint const y0 = buf->rect.top();
+ gint const y1 = buf->rect.bottom();
+ gint const x0 = round(x);
+ gint const x1 = x0 + round( (y1 - y0) / tan_angle[X] );
+
+ if (!scaled && (xlinenum % empspacing) != 0) {
+ sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color);
+ } else {
+ sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, _empcolor);
+ }
}
}
@@ -573,8 +568,12 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf)
for (gdouble y = zstart_y_sc; y < buf->rect.bottom(); y += lyw, zlinenum++, next_y = y) {
gint const x0 = buf->rect.left();
gint const y0 = round(y);
- gint const x1 = x0 + round( (y - buf->rect.top() ) / tan_angle[Z] );
- gint const y1 = buf->rect.top();
+ gint x1 = x0 + round( (y - buf->rect.top() ) / tan_angle[Z] );
+ gint y1 = buf->rect.top();
+ if ( Geom::are_near(tan_angle[Z],0.) ) {
+ x1 = buf->rect.right();
+ y1 = y0;
+ }
if (!scaled && (zlinenum % empspacing) != 0) {
sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color);
@@ -583,17 +582,20 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf)
}
}
// draw lines from bottom-up
- gdouble const zstart_x_sc = buf->rect.left() + (next_y - buf->rect.bottom()) / tan_angle[Z] ;
- for (gdouble x = zstart_x_sc; x < buf->rect.right(); x += lxw_z, zlinenum++) {
- gint const y0 = buf->rect.bottom();
- gint const y1 = buf->rect.top();
- gint const x0 = round(x);
- gint const x1 = x0 + round(buf->rect.height() / tan_angle[Z] );
-
- if (!scaled && (zlinenum % empspacing) != 0) {
- sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color);
- } else {
- sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, _empcolor);
+ if (!Geom::are_near(tan_angle[Z],0.))
+ {
+ gdouble const zstart_x_sc = buf->rect.left() + (next_y - buf->rect.bottom()) / tan_angle[Z] ;
+ for (gdouble x = zstart_x_sc; x < buf->rect.right(); x += lxw_z, zlinenum++) {
+ gint const y0 = buf->rect.bottom();
+ gint const y1 = buf->rect.top();
+ gint const x0 = round(x);
+ gint const x1 = x0 + round(buf->rect.height() / tan_angle[Z] );
+
+ if (!scaled && (zlinenum % empspacing) != 0) {
+ sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, color);
+ } else {
+ sp_caxonomgrid_drawline (buf, x0, y0, x1, y1, _empcolor);
+ }
}
}
@@ -725,19 +727,19 @@ CanvasAxonomGridSnapper::_getSnapLines(Geom::Point const &p) const
return s;
}
-void CanvasAxonomGridSnapper::_addSnappedLine(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const
+void CanvasAxonomGridSnapper::_addSnappedLine(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const &normal_to_line, Geom::Point const &point_on_line) const
{
SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, source_num, Inkscape::SNAPTARGET_GRID, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line);
isr.grid_lines.push_back(dummy);
}
-void CanvasAxonomGridSnapper::_addSnappedPoint(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
+void CanvasAxonomGridSnapper::_addSnappedPoint(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
{
SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true);
isr.points.push_back(dummy);
}
-void CanvasAxonomGridSnapper::_addSnappedLinePerpendicularly(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
+void CanvasAxonomGridSnapper::_addSnappedLinePerpendicularly(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
{
SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID_PERPENDICULAR, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true);
isr.points.push_back(dummy);
diff --git a/src/display/canvas-axonomgrid.h b/src/display/canvas-axonomgrid.h
index 04919f947..3888a3dc4 100644
--- a/src/display/canvas-axonomgrid.h
+++ b/src/display/canvas-axonomgrid.h
@@ -2,20 +2,23 @@
#define CANVAS_AXONOMGRID_H
/*
- * Copyright (C) 2006-2007 Johan Engelen <johan@shouraizou.nl>
+ * Authors:
+ * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
*
- */
+ * Copyright (C) 2006-2012 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
#include "line-snapper.h"
#include "canvas-grid.h"
-class SPCanvasBuf;
+struct SPCanvasBuf;
class SPDesktop;
-struct SPNamedView;
+class SPNamedView;
namespace Inkscape {
namespace XML {
- class Node;
+ class Node;
};
class CanvasAxonomGrid : public CanvasGrid {
@@ -36,6 +39,9 @@ public:
bool scaled; /**< Whether the grid is in scaled mode */
+protected:
+ friend class CanvasAxonomGridSnapper;
+
Geom::Point ow; /**< Transformed origin by the affine for the zoom */
double lyw; /**< Transformed length y by the affine for the zoom */
double lxw_x;
@@ -44,7 +50,6 @@ public:
Geom::Point sw; /**< the scaling factors of the affine transform */
-protected:
virtual Gtk::Widget * newSpecificWidget();
private:
@@ -63,13 +68,13 @@ public:
bool ThisSnapperMightSnap() const;
Geom::Coord getSnapperTolerance() const; //returns the tolerance of the snapper in screen pixels (i.e. independent of zoom)
- bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance
+ bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance
private:
LineList _getSnapLines(Geom::Point const &p) const;
- void _addSnappedLine(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const;
- void _addSnappedPoint(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
- void _addSnappedLinePerpendicularly(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
+ void _addSnappedLine(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const &normal_to_line, const Geom::Point &point_on_line) const;
+ void _addSnappedPoint(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
+ void _addSnappedLinePerpendicularly(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
CanvasAxonomGrid *grid;
};
diff --git a/src/display/canvas-bpath.cpp b/src/display/canvas-bpath.cpp
index 11bccf2a6..ee9e14f10 100644
--- a/src/display/canvas-bpath.cpp
+++ b/src/display/canvas-bpath.cpp
@@ -29,7 +29,7 @@
static void sp_canvas_bpath_class_init (SPCanvasBPathClass *klass);
static void sp_canvas_bpath_init (SPCanvasBPath *path);
-static void sp_canvas_bpath_destroy (GtkObject *object);
+static void sp_canvas_bpath_destroy(SPCanvasItem *object);
static void sp_canvas_bpath_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
static void sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf);
@@ -57,19 +57,13 @@ sp_canvas_bpath_get_type (void)
return type;
}
-static void
-sp_canvas_bpath_class_init (SPCanvasBPathClass *klass)
+static void sp_canvas_bpath_class_init(SPCanvasBPathClass *klass)
{
- GtkObjectClass *object_class;
- SPCanvasItemClass *item_class;
-
- object_class = GTK_OBJECT_CLASS (klass);
- item_class = (SPCanvasItemClass *) klass;
+ SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass);
- object_class->destroy = sp_canvas_bpath_destroy;
-
+ item_class->destroy = sp_canvas_bpath_destroy;
item_class->update = sp_canvas_bpath_update;
item_class->render = sp_canvas_bpath_render;
item_class->point = sp_canvas_bpath_point;
@@ -88,8 +82,7 @@ sp_canvas_bpath_init (SPCanvasBPath * bpath)
bpath->stroke_miterlimit = 11.0;
}
-static void
-sp_canvas_bpath_destroy (GtkObject *object)
+static void sp_canvas_bpath_destroy(SPCanvasItem *object)
{
SPCanvasBPath *cbp = SP_CANVAS_BPATH (object);
@@ -97,8 +90,8 @@ sp_canvas_bpath_destroy (GtkObject *object)
cbp->curve = cbp->curve->unref();
}
- if (GTK_OBJECT_CLASS (parent_class)->destroy)
- (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+ if (SP_CANVAS_ITEM_CLASS(parent_class)->destroy)
+ (* SP_CANVAS_ITEM_CLASS(parent_class)->destroy) (object);
}
static void sp_canvas_bpath_update(SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags)
diff --git a/src/display/canvas-bpath.h b/src/display/canvas-bpath.h
index f0520f012..72eca6eeb 100644
--- a/src/display/canvas-bpath.h
+++ b/src/display/canvas-bpath.h
@@ -22,7 +22,7 @@
struct SPCanvasBPath;
struct SPCanvasBPathClass;
struct SPCanvasGroup;
-struct SPCurve;
+class SPCurve;
#define SP_TYPE_CANVAS_BPATH (sp_canvas_bpath_get_type ())
#define SP_CANVAS_BPATH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CANVAS_BPATH, SPCanvasBPath))
diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp
index 92a1b0fb8..3c4ad9b00 100644
--- a/src/display/canvas-grid.cpp
+++ b/src/display/canvas-grid.cpp
@@ -13,6 +13,25 @@
* Don't be shy to correct things.
*/
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
+#include <glibmm/threads.h>
+#endif
+
+#include <gtkmm/box.h>
+#include <gtkmm/label.h>
+
+#if WITH_GTKMM_3_0
+# include <gtkmm/grid.h>
+#else
+# include <gtkmm/table.h>
+#endif
+
+#include <glibmm/i18n.h>
+
#include "ui/widget/registered-widget.h"
#include "desktop.h"
#include "sp-canvas-util.h"
@@ -23,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"
@@ -35,11 +54,8 @@
#include "verbs.h"
#include "display/sp-canvas.h"
-#include <gtkmm/box.h>
-#include <gtkmm/label.h>
-#include <gtkmm/table.h>
-
using Inkscape::DocumentUndo;
+using Inkscape::Util::unit_table;
namespace Inkscape {
@@ -57,8 +73,7 @@ static gchar const *const grid_svgname[] = {
// Grid CanvasItem
static void grid_canvasitem_class_init (GridCanvasItemClass *klass);
static void grid_canvasitem_init (GridCanvasItem *grid);
-static void grid_canvasitem_destroy (GtkObject *object);
-
+static void grid_canvasitem_destroy(SPCanvasItem *object);
static void grid_canvasitem_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
static void grid_canvasitem_render (SPCanvasItem *item, SPCanvasBuf *buf);
@@ -86,19 +101,13 @@ grid_canvasitem_get_type (void)
return grid_canvasitem_type;
}
-static void
-grid_canvasitem_class_init (GridCanvasItemClass *klass)
+static void grid_canvasitem_class_init(GridCanvasItemClass *klass)
{
- GtkObjectClass *object_class;
- SPCanvasItemClass *item_class;
-
- object_class = (GtkObjectClass *) klass;
- item_class = (SPCanvasItemClass *) klass;
+ SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass);
- object_class->destroy = grid_canvasitem_destroy;
-
+ item_class->destroy = grid_canvasitem_destroy;
item_class->update = grid_canvasitem_update;
item_class->render = grid_canvasitem_render;
}
@@ -109,14 +118,13 @@ grid_canvasitem_init (GridCanvasItem *griditem)
griditem->grid = NULL;
}
-static void
-grid_canvasitem_destroy (GtkObject *object)
+static void grid_canvasitem_destroy(SPCanvasItem *object)
{
g_return_if_fail (object != NULL);
g_return_if_fail (INKSCAPE_IS_GRID_CANVASITEM (object));
- if (GTK_OBJECT_CLASS (parent_class)->destroy)
- (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+ if (SP_CANVAS_ITEM_CLASS(parent_class)->destroy)
+ (* SP_CANVAS_ITEM_CLASS(parent_class)->destroy) (object);
}
/**
@@ -184,25 +192,25 @@ CanvasGrid::~CanvasGrid()
}
while (canvasitems) {
- gtk_object_destroy(GTK_OBJECT(canvasitems->data));
+ sp_canvas_item_destroy(SP_CANVAS_ITEM(canvasitems->data));
canvasitems = g_slist_remove(canvasitems, canvasitems->data);
}
}
const char *
-CanvasGrid::getName()
+CanvasGrid::getName() const
{
return _(grid_name[gridtype]);
}
const char *
-CanvasGrid::getSVGName()
+CanvasGrid::getSVGName() const
{
return grid_svgname[gridtype];
}
GridType
-CanvasGrid::getGridType()
+CanvasGrid::getGridType() const
{
return gridtype;
}
@@ -281,9 +289,9 @@ CanvasGrid::NewGrid(SPNamedView * nv, Inkscape::XML::Node * repr, SPDocument * d
switch (gridtype) {
case GRID_RECTANGULAR:
- return (CanvasGrid*) new CanvasXYGrid(nv, repr, doc);
+ return dynamic_cast<CanvasGrid*>(new CanvasXYGrid(nv, repr, doc));
case GRID_AXONOMETRIC:
- return (CanvasGrid*) new CanvasAxonomGrid(nv, repr, doc);
+ return dynamic_cast<CanvasGrid*>(new CanvasAxonomGrid(nv, repr, doc));
}
return NULL;
@@ -357,12 +365,13 @@ CanvasGrid::newWidget()
_rcb_enabled->setSlaveWidgets(slaves);
// set widget values
+ _wr.setUpdating (true);
_rcb_visible->setActive(visible);
if (snapper != NULL) {
_rcb_enabled->setActive(snapper->getEnabled());
_rcb_snap_visible_only->setActive(snapper->getSnapVisibleOnly());
}
-
+ _wr.setUpdating (false);
return dynamic_cast<Gtk::Widget *> (vbox);
}
@@ -372,10 +381,10 @@ CanvasGrid::on_repr_attr_changed(Inkscape::XML::Node *repr, gchar const *key, gc
if (!data)
return;
- ((CanvasGrid*) data)->onReprAttrChanged(repr, key, oldval, newval, is_interactive);
+ (static_cast<CanvasGrid*>(data))->onReprAttrChanged(repr, key, oldval, newval, is_interactive);
}
-bool CanvasGrid::isEnabled()
+bool CanvasGrid::isEnabled() const
{
if (snapper == NULL) {
return false;
@@ -390,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());
}
@@ -416,29 +425,60 @@ void CanvasGrid::setOrigin(Geom::Point const &origin_px)
**/
#define SPACE_SIZE_X 15
#define SPACE_SIZE_Y 10
-static inline void
-attach_all(Gtk::Table &table, Gtk::Widget const *const arr[], unsigned size, int start = 0)
+#if WITH_GTKMM_3_0
+static inline void attach_all(Gtk::Grid &table, Gtk::Widget const *const arr[], unsigned size, int start = 0)
+#else
+static inline void attach_all(Gtk::Table &table, Gtk::Widget const *const arr[], unsigned size, int start = 0)
+#endif
{
for (unsigned i=0, r=start; i<size/sizeof(Gtk::Widget*); i+=2) {
if (arr[i] && arr[i+1]) {
+#if WITH_GTKMM_3_0
+ (const_cast<Gtk::Widget&>(*arr[i])).set_hexpand();
+ (const_cast<Gtk::Widget&>(*arr[i])).set_valign(Gtk::ALIGN_CENTER);
+ table.attach(const_cast<Gtk::Widget&>(*arr[i]), 1, r, 1, 1);
+
+ (const_cast<Gtk::Widget&>(*arr[i+1])).set_hexpand();
+ (const_cast<Gtk::Widget&>(*arr[i+1])).set_valign(Gtk::ALIGN_CENTER);
+ table.attach(const_cast<Gtk::Widget&>(*arr[i+1]), 2, r, 1, 1);
+#else
table.attach (const_cast<Gtk::Widget&>(*arr[i]), 1, 2, r, r+1,
Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 2, 3, r, r+1,
Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
+#endif
} else {
if (arr[i+1]) {
+#if WITH_GTKMM_3_0
+ (const_cast<Gtk::Widget&>(*arr[i+1])).set_hexpand();
+ (const_cast<Gtk::Widget&>(*arr[i+1])).set_valign(Gtk::ALIGN_CENTER);
+ table.attach(const_cast<Gtk::Widget&>(*arr[i+1]), 1, r, 2, 1);
+#else
table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 1, 3, r, r+1,
Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
+#endif
} else if (arr[i]) {
Gtk::Label& label = reinterpret_cast<Gtk::Label&> (const_cast<Gtk::Widget&>(*arr[i]));
label.set_alignment (0.0);
+#if WITH_GTKMM_3_0
+ label.set_hexpand();
+ label.set_valign(Gtk::ALIGN_CENTER);
+ table.attach(label, 0, r, 3, 1);
+#else
table.attach (label, 0, 3, r, r+1,
Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
+#endif
} else {
Gtk::HBox *space = manage (new Gtk::HBox);
space->set_size_request (SPACE_SIZE_X, SPACE_SIZE_Y);
+#if WITH_GTKMM_3_0
+ space->set_halign(Gtk::ALIGN_CENTER);
+ space->set_valign(Gtk::ALIGN_CENTER);
+ table.attach(*space, 0, r, 1, 1);
+#else
table.attach (*space, 0, 1, r, r+1,
(Gtk::AttachOptions)0, (Gtk::AttachOptions)0,0,0);
+#endif
}
}
++r;
@@ -449,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);
@@ -472,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) {
@@ -606,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")) ) {
@@ -694,7 +680,14 @@ CanvasXYGrid::onReprAttrChanged(Inkscape::XML::Node */*repr*/, gchar const */*ke
Gtk::Widget *
CanvasXYGrid::newSpecificWidget()
{
+#if WITH_GTKMM_3_0
+ Gtk::Grid * table = Gtk::manage( new Gtk::Grid() );
+ table->set_row_spacing(2);
+ table->set_column_spacing(2);
+#else
Gtk::Table * table = Gtk::manage( new Gtk::Table(1,1) );
+ table->set_spacings(2);
+#endif
Inkscape::UI::Widget::RegisteredUnitMenu *_rumg = Gtk::manage( new Inkscape::UI::Widget::RegisteredUnitMenu(
_("Grid _units:"), "units", _wr, repr, doc) );
@@ -709,7 +702,7 @@ CanvasXYGrid::newSpecificWidget()
Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gcol = Gtk::manage(
new Inkscape::UI::Widget::RegisteredColorPicker(
- _("Grid line _color:"), _("Grid line color"), _("Color of grid lines"),
+ _("Minor grid line _color:"), _("Minor grid line color"), _("Color of the minor grid lines"),
"color", "opacity", _wr, repr, doc));
Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gmcol = Gtk::manage(
@@ -721,9 +714,7 @@ CanvasXYGrid::newSpecificWidget()
Inkscape::UI::Widget::RegisteredSuffixedInteger *_rsi = Gtk::manage( new Inkscape::UI::Widget::RegisteredSuffixedInteger(
_("_Major grid line every:"), "", _("lines"), "empspacing", _wr, repr, doc) );
- table->set_spacings(2);
-
-_wr.setUpdating (true);
+ _wr.setUpdating (true);
_rsu_ox->setDigits(5);
_rsu_ox->setIncrements(0.1, 1.0);
@@ -741,7 +732,6 @@ _wr.setUpdating (true);
new Inkscape::UI::Widget::RegisteredCheckButton( _("_Show dots instead of lines"),
_("If set, displays dots at gridpoints instead of gridlines"),
"dotted", _wr, false, repr, doc) );
-_wr.setUpdating (false);
Gtk::Widget const *const widget_array[] = {
0, _rumg,
@@ -759,20 +749,20 @@ _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 = 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);
@@ -781,10 +771,12 @@ _wr.setUpdating (false);
_rcb_dotted->setActive(render_dotted);
+ _wr.setUpdating (false);
+
_rsu_ox->setProgrammatically = false;
_rsu_oy->setProgrammatically = false;
_rsu_sx->setProgrammatically = false;
- _rsu_sx->setProgrammatically = false;
+ _rsu_sy->setProgrammatically = false;
return table;
}
@@ -806,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);
@@ -1039,19 +1031,19 @@ CanvasXYGridSnapper::_getSnapLines(Geom::Point const &p) const
return s;
}
-void CanvasXYGridSnapper::_addSnappedLine(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const
+void CanvasXYGridSnapper::_addSnappedLine(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const &normal_to_line, Geom::Point const &point_on_line) const
{
SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, source, source_num, Inkscape::SNAPTARGET_GRID, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line);
isr.grid_lines.push_back(dummy);
}
-void CanvasXYGridSnapper::_addSnappedPoint(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
+void CanvasXYGridSnapper::_addSnappedPoint(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
{
SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true);
isr.points.push_back(dummy);
}
-void CanvasXYGridSnapper::_addSnappedLinePerpendicularly(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
+void CanvasXYGridSnapper::_addSnappedLinePerpendicularly(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const
{
SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID_PERPENDICULAR, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true);
isr.points.push_back(dummy);
diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h
index bba9b7e95..5a23dee52 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
@@ -61,9 +65,9 @@ public:
virtual ~CanvasGrid();
// TODO: see effect.h and effect.cpp from live_effects how to link enums to SVGname to typename properly. (johan)
- const char * getName();
- const char * getSVGName();
- GridType getGridType();
+ const char * getName() const;
+ const char * getSVGName() const;
+ GridType getGridType() const;
static const char * getName(GridType type);
static const char * getSVGName(GridType type);
static GridType getGridTypeFromSVGName(const char * typestr);
@@ -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;
@@ -97,8 +101,8 @@ public:
static void on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data);
- bool isVisible() { return (isEnabled() &&visible); };
- bool isEnabled();
+ bool isVisible() const { return (isEnabled() &&visible); };
+ bool isEnabled() const;
protected:
CanvasGrid(SPNamedView * nv, Inkscape::XML::Node * in_repr, SPDocument *in_doc, GridType type);
@@ -163,9 +167,9 @@ public:
private:
LineList _getSnapLines(Geom::Point const &p) const;
- void _addSnappedLine(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const;
- void _addSnappedPoint(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
- void _addSnappedLinePerpendicularly(IntermSnapResults &isr, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
+ void _addSnappedLine(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const &normal_to_line, const Geom::Point &point_on_line) const;
+ void _addSnappedPoint(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
+ void _addSnappedLinePerpendicularly(IntermSnapResults &isr, Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const;
CanvasXYGrid *grid;
};
diff --git a/src/display/canvas-temporary-item.cpp b/src/display/canvas-temporary-item.cpp
index abe5f2f1b..551ea1536 100644
--- a/src/display/canvas-temporary-item.cpp
+++ b/src/display/canvas-temporary-item.cpp
@@ -17,6 +17,7 @@
#include "display/canvas-temporary-item.h"
#include <gtk/gtk.h>
+#include "display/sp-canvas-item.h"
namespace Inkscape {
namespace Display {
@@ -48,7 +49,7 @@ TemporaryItem::~TemporaryItem()
if (canvasitem) {
// destroying the item automatically hides it
- gtk_object_destroy (GTK_OBJECT (canvasitem));
+ sp_canvas_item_destroy(canvasitem);
canvasitem = NULL;
}
}
diff --git a/src/display/canvas-text.cpp b/src/display/canvas-text.cpp
index f7a8713b6..fe60d9c65 100644
--- a/src/display/canvas-text.cpp
+++ b/src/display/canvas-text.cpp
@@ -28,7 +28,7 @@
static void sp_canvastext_class_init (SPCanvasTextClass *klass);
static void sp_canvastext_init (SPCanvasText *canvastext);
-static void sp_canvastext_destroy (GtkObject *object);
+static void sp_canvastext_destroy(SPCanvasItem *object);
static void sp_canvastext_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
static void sp_canvastext_render (SPCanvasItem *item, SPCanvasBuf *buf);
@@ -56,16 +56,13 @@ sp_canvastext_get_type (void)
return type;
}
-static void
-sp_canvastext_class_init (SPCanvasTextClass *klass)
+static void sp_canvastext_class_init(SPCanvasTextClass *klass)
{
- GtkObjectClass *object_class = (GtkObjectClass *) klass;
- SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
-
- parent_class_ct = (SPCanvasItemClass*)g_type_class_peek_parent (klass);
+ SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(klass);
- object_class->destroy = sp_canvastext_destroy;
+ parent_class_ct = SP_CANVAS_ITEM_CLASS(g_type_class_peek_parent(klass));
+ item_class->destroy = sp_canvastext_destroy;
item_class->update = sp_canvastext_update;
item_class->render = sp_canvastext_render;
}
@@ -93,8 +90,7 @@ sp_canvastext_init (SPCanvasText *canvastext)
canvastext->border = 3; // must be a constant, and not proportional to any width, height, or fontsize to allow alignment with other text boxes
}
-static void
-sp_canvastext_destroy (GtkObject *object)
+static void sp_canvastext_destroy(SPCanvasItem *object)
{
g_return_if_fail (object != NULL);
g_return_if_fail (SP_IS_CANVASTEXT (object));
@@ -105,8 +101,8 @@ sp_canvastext_destroy (GtkObject *object)
canvastext->text = NULL;
canvastext->item = NULL;
- if (GTK_OBJECT_CLASS (parent_class_ct)->destroy)
- (* GTK_OBJECT_CLASS (parent_class_ct)->destroy) (object);
+ if (SP_CANVAS_ITEM_CLASS(parent_class_ct)->destroy)
+ (* SP_CANVAS_ITEM_CLASS(parent_class_ct)->destroy) (object);
}
static void
@@ -255,8 +251,7 @@ sp_canvastext_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned i
item->canvas->requestRedraw((int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2);
}
-SPCanvasItem *
-sp_canvastext_new(SPCanvasGroup *parent, SPDesktop *desktop, Geom::Point pos, gchar const *new_text)
+SPCanvasText *sp_canvastext_new(SPCanvasGroup *parent, SPDesktop *desktop, Geom::Point pos, gchar const *new_text)
{
// Pos specifies the position of the anchor, which is at the bounding box of the text itself (i.e. not at the border of the filled background rectangle)
// The relative position of the anchor can be set using e.g. anchor_position = TEXT_ANCHOR_LEFT
@@ -270,7 +265,7 @@ sp_canvastext_new(SPCanvasGroup *parent, SPDesktop *desktop, Geom::Point pos, gc
g_free(ct->text);
ct->text = g_strdup(new_text);
- return item;
+ return ct;
}
diff --git a/src/display/canvas-text.h b/src/display/canvas-text.h
index ecdb77f1c..b779d7170 100644
--- a/src/display/canvas-text.h
+++ b/src/display/canvas-text.h
@@ -58,7 +58,7 @@ struct SPCanvasTextClass : public SPCanvasItemClass{};
GType sp_canvastext_get_type (void);
-SPCanvasItem *sp_canvastext_new(SPCanvasGroup *parent, SPDesktop *desktop, Geom::Point pos, gchar const *text);
+SPCanvasText *sp_canvastext_new(SPCanvasGroup *parent, SPDesktop *desktop, Geom::Point pos, gchar const *text);
void sp_canvastext_set_rgba32 (SPCanvasText *ct, guint32 rgba, guint32 rgba_stroke);
void sp_canvastext_set_coords (SPCanvasText *ct, gdouble x0, gdouble y0);
diff --git a/src/display/curve.cpp b/src/display/curve.cpp
index 1a788b59a..50f4c8954 100644
--- a/src/display/curve.cpp
+++ b/src/display/curve.cpp
@@ -34,7 +34,6 @@ SPCurve::SPCurve()
: _refcount(1),
_pathv()
{
- _pathv.clear();
}
SPCurve::SPCurve(Geom::PathVector const& pathv)
@@ -150,7 +149,7 @@ SPCurve::concat(GSList const *list)
SPCurve *new_curve = new SPCurve();
for (GSList const *l = list; l != NULL; l = l->next) {
- SPCurve *c = (SPCurve *) l->data;
+ SPCurve *c = static_cast<SPCurve *>(l->data);
new_curve->_pathv.insert( new_curve->_pathv.end(), c->get_pathvector().begin(), c->get_pathvector().end() );
}
diff --git a/src/display/drawing-context.cpp b/src/display/drawing-context.cpp
index de5beb0f6..319136e06 100644
--- a/src/display/drawing-context.cpp
+++ b/src/display/drawing-context.cpp
@@ -25,27 +25,27 @@ using Geom::Y;
*/
DrawingContext::Save::Save()
- : _ct(NULL)
+ : _dc(NULL)
{}
-DrawingContext::Save::Save(DrawingContext &ct)
- : _ct(&ct)
+DrawingContext::Save::Save(DrawingContext &dc)
+ : _dc(&dc)
{
- _ct->save();
+ _dc->save();
}
DrawingContext::Save::~Save()
{
- if (_ct) {
- _ct->restore();
+ if (_dc) {
+ _dc->restore();
}
}
-void DrawingContext::Save::save(DrawingContext &ct)
+void DrawingContext::Save::save(DrawingContext &dc)
{
- if (_ct) {
+ if (_dc) {
// TODO: it might be better to treat this occurence as a bug
- _ct->restore();
+ _dc->restore();
}
- _ct = &ct;
- _ct->save();
+ _dc = &dc;
+ _dc->save();
}
/**
diff --git a/src/display/drawing-context.h b/src/display/drawing-context.h
index fb6662202..d990bcb73 100644
--- a/src/display/drawing-context.h
+++ b/src/display/drawing-context.h
@@ -31,11 +31,11 @@ public:
class Save {
public:
Save();
- Save(DrawingContext &ct);
+ Save(DrawingContext &dc);
~Save();
- void save(DrawingContext &ct);
+ void save(DrawingContext &dc);
private:
- DrawingContext *_ct;
+ DrawingContext *_dc;
};
DrawingContext(cairo_t *ct, Geom::Point const &origin);
@@ -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);
@@ -96,11 +111,17 @@ public:
void setSource(DrawingSurface *s);
void setSourceCheckerboard();
+ void patternSetFilter(cairo_filter_t filter) {
+ cairo_pattern_set_filter(cairo_get_source(_ct), filter);
+ }
+
Geom::Rect targetLogicalBounds() const;
cairo_t *raw() { return _ct; }
cairo_surface_t *rawTarget() { return cairo_get_group_target(_ct); }
+ DrawingSurface *surface() { return _surface; } // Needed to find scale in drawing-item.cpp
+
private:
DrawingContext(cairo_t *ct, DrawingSurface *surface, bool destroy);
diff --git a/src/display/drawing-group.cpp b/src/display/drawing-group.cpp
index 6d52b89fc..38ace001f 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
}
/**
@@ -97,12 +98,12 @@ DrawingGroup::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, u
}
unsigned
-DrawingGroup::_renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at)
+DrawingGroup::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at)
{
if (stop_at == NULL) {
// normal rendering
for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
- i->render(ct, area, flags, stop_at);
+ i->render(dc, area, flags, stop_at);
}
} else {
// background rendering
@@ -110,11 +111,11 @@ DrawingGroup::_renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigne
if (&*i == stop_at) return RENDER_OK; // do not render the stop_at item at all
if (i->isAncestorOf(stop_at)) {
// render its ancestors without masks, opacity or filters
- i->render(ct, area, flags | RENDER_FILTER_BACKGROUND, stop_at);
+ i->render(dc, area, flags | RENDER_FILTER_BACKGROUND, stop_at);
// stop further rendering
return RENDER_OK;
} else {
- i->render(ct, area, flags, stop_at);
+ i->render(dc, area, flags, stop_at);
}
}
}
@@ -122,10 +123,10 @@ DrawingGroup::_renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigne
}
void
-DrawingGroup::_clipItem(DrawingContext &ct, Geom::IntRect const &area)
+DrawingGroup::_clipItem(DrawingContext &dc, Geom::IntRect const &area)
{
for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
- i->clip(ct, area);
+ i->clip(dc, area);
}
}
diff --git a/src/display/drawing-group.h b/src/display/drawing-group.h
index 974b3c977..651e9d8af 100644
--- a/src/display/drawing-group.h
+++ b/src/display/drawing-group.h
@@ -14,7 +14,7 @@
#include "display/drawing-item.h"
-class SPStyle;
+struct SPStyle;
namespace Inkscape {
@@ -34,9 +34,9 @@ public:
protected:
virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx,
unsigned flags, unsigned reset);
- virtual unsigned _renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags,
+ virtual unsigned _renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigned flags,
DrawingItem *stop_at);
- virtual void _clipItem(DrawingContext &ct, Geom::IntRect const &area);
+ virtual void _clipItem(DrawingContext &dc, Geom::IntRect const &area);
virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags);
virtual bool _canClip();
diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp
index 0c8ac9681..5844c8b08 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,33 +22,22 @@ namespace Inkscape {
DrawingImage::DrawingImage(Drawing &drawing)
: DrawingItem(drawing)
, _pixbuf(NULL)
- , _surface(NULL)
, _style(NULL)
{}
DrawingImage::~DrawingImage()
{
- if (_style)
+ if (_style) {
sp_style_unref(_style);
- if (_pixbuf) {
- 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);
}
@@ -84,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);
@@ -112,42 +102,51 @@ DrawingImage::_updateItem(Geom::IntRect const &, UpdateContext const &, unsigned
return STATE_ALL;
}
-unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/)
+unsigned DrawingImage::_renderItem(DrawingContext &dc, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/)
{
bool outline = _drawing.outline();
if (!outline) {
if (!_pixbuf) return RENDER_OK;
- Inkscape::DrawingContext::Save save(ct);
- ct.transform(_ctm);
- ct.newPath();
- ct.rectangle(_clipbox);
- ct.clip();
-
- ct.translate(_origin);
- ct.scale(_scale);
- ct.setSource(_surface, 0, 0);
-
- cairo_matrix_t tt;
- Geom::Affine total;
- cairo_get_matrix(ct.raw(), &tt);
- ink_matrix_to_2geom(total, tt);
-
- if (total.expansionX() > 1.0 || total.expansionY() > 1.0) {
- cairo_pattern_t *p = cairo_get_source(ct.raw());
- cairo_pattern_set_filter(p, CAIRO_FILTER_NEAREST);
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm);
+ dc.newPath();
+ dc.rectangle(_clipbox);
+ dc.clip();
+
+ dc.translate(_origin);
+ dc.scale(_scale);
+ dc.setSource(_pixbuf->getSurfaceRaw(), 0, 0);
+
+ if (_style) {
+ // See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty
+ // http://www.w3.org/TR/css4-images/#the-image-rendering
+ // style.h/style.cpp
+ switch (_style->image_rendering.computed) {
+ case SP_CSS_COLOR_RENDERING_AUTO:
+ // Do nothing
+ break;
+ case SP_CSS_COLOR_RENDERING_OPTIMIZEQUALITY:
+ // In recent Cairo, BEST used Lanczos3, which is prohibitively slow
+ dc.patternSetFilter( CAIRO_FILTER_GOOD );
+ break;
+ case SP_CSS_COLOR_RENDERING_OPTIMIZESPEED:
+ default:
+ dc.patternSetFilter( CAIRO_FILTER_NEAREST );
+ break;
+ }
}
- //ct.paint(_opacity);
- ct.paint();
+
+ dc.paint(_opacity);
} else { // outline; draw a rect instead
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
guint32 rgba = prefs->getInt("/options/wireframecolors/images", 0xff0000ff);
- { Inkscape::DrawingContext::Save save(ct);
- ct.transform(_ctm);
- ct.newPath();
+ { Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm);
+ dc.newPath();
Geom::Rect r = bounds();
Geom::Point c00 = r.corner(0);
@@ -155,21 +154,21 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar
Geom::Point c11 = r.corner(2);
Geom::Point c10 = r.corner(1);
- ct.moveTo(c00);
+ dc.moveTo(c00);
// the box
- ct.lineTo(c10);
- ct.lineTo(c11);
- ct.lineTo(c01);
- ct.lineTo(c00);
+ dc.lineTo(c10);
+ dc.lineTo(c11);
+ dc.lineTo(c01);
+ dc.lineTo(c00);
// the diagonals
- ct.lineTo(c11);
- ct.moveTo(c10);
- ct.lineTo(c01);
+ dc.lineTo(c11);
+ dc.moveTo(c10);
+ dc.lineTo(c01);
}
- ct.setLineWidth(0.5);
- ct.setSource(rgba);
- ct.stroke();
+ dc.setLineWidth(0.5);
+ dc.setSource(rgba);
+ dc.stroke();
}
return RENDER_OK;
}
@@ -178,21 +177,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 *
@@ -204,29 +191,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();
@@ -244,8 +226,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 306096d0e..64e4517b0 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);
@@ -37,12 +38,11 @@ public:
protected:
virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx,
unsigned flags, unsigned reset);
- virtual unsigned _renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags,
+ virtual unsigned _renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigned flags,
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;
// TODO: the following three should probably be merged into a new Geom::Viewbox object
diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp
index 0fb1f0018..ccf905e81 100644
--- a/src/display/drawing-item.cpp
+++ b/src/display/drawing-item.cpp
@@ -23,6 +23,66 @@
namespace Inkscape {
+#ifdef WITH_CSSBLEND
+void set_cairo_blend_operator( DrawingContext &dc, unsigned blend_mode ) {
+
+ // 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 SP_CSS_BLEND_MULTIPLY:
+ dc.setOperator(CAIRO_OPERATOR_MULTIPLY);
+ break;
+ case SP_CSS_BLEND_SCREEN:
+ dc.setOperator(CAIRO_OPERATOR_SCREEN);
+ break;
+ case SP_CSS_BLEND_DARKEN:
+ dc.setOperator(CAIRO_OPERATOR_DARKEN);
+ break;
+ case SP_CSS_BLEND_LIGHTEN:
+ dc.setOperator(CAIRO_OPERATOR_LIGHTEN);
+ break;
+ case SP_CSS_BLEND_OVERLAY:
+ dc.setOperator(CAIRO_OPERATOR_OVERLAY);
+ break;
+ case SP_CSS_BLEND_COLORDODGE:
+ dc.setOperator(CAIRO_OPERATOR_COLOR_DODGE);
+ break;
+ case SP_CSS_BLEND_COLORBURN:
+ dc.setOperator(CAIRO_OPERATOR_COLOR_BURN);
+ break;
+ case SP_CSS_BLEND_HARDLIGHT:
+ dc.setOperator(CAIRO_OPERATOR_HARD_LIGHT);
+ break;
+ case SP_CSS_BLEND_SOFTLIGHT:
+ dc.setOperator(CAIRO_OPERATOR_SOFT_LIGHT);
+ break;
+ case SP_CSS_BLEND_DIFFERENCE:
+ dc.setOperator(CAIRO_OPERATOR_DIFFERENCE);
+ break;
+ case SP_CSS_BLEND_EXCLUSION:
+ dc.setOperator(CAIRO_OPERATOR_EXCLUSION);
+ break;
+ case SP_CSS_BLEND_HUE:
+ dc.setOperator(CAIRO_OPERATOR_HSL_HUE);
+ break;
+ case SP_CSS_BLEND_SATURATION:
+ dc.setOperator(CAIRO_OPERATOR_HSL_SATURATION);
+ break;
+ case SP_CSS_BLEND_COLOR:
+ dc.setOperator(CAIRO_OPERATOR_HSL_COLOR);
+ break;
+ case SP_CSS_BLEND_LUMINOSITY:
+ dc.setOperator(CAIRO_OPERATOR_HSL_LUMINOSITY);
+ break;
+ case SP_CSS_BLEND_NORMAL:
+ default:
+ dc.setOperator(CAIRO_OPERATOR_OVER);
+ break;
+ }
+}
+#endif
+
/**
* @class DrawingItem
* SVG drawing item for display.
@@ -67,6 +127,9 @@ DrawingItem::DrawingItem(Drawing &drawing)
, _propagate(0)
// , _renders_opacity(0)
, _pick_children(0)
+ , _antialias(1)
+ , _isolation(SP_CSS_ISOLATION_AUTO)
+ , _blend_mode(SP_CSS_BLEND_NORMAL)
{}
DrawingItem::~DrawingItem()
@@ -141,7 +204,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,13 +221,17 @@ 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).
void
DrawingItem::clearChildren()
{
+ if (_children.empty()) return;
+
_markForRendering();
// prevent children from referencing the parent during deletion
// this way, children won't try to remove themselves from a list
@@ -195,17 +269,46 @@ DrawingItem::setTransform(Geom::Affine const &new_trans)
void
DrawingItem::setOpacity(float opacity)
{
- _opacity = opacity;
+ if (_opacity != opacity) {
+ _opacity = opacity;
+ _markForRendering();
+ }
+}
+
+void
+DrawingItem::setAntialiasing(bool a)
+{
+ if (_antialias != a) {
+ _antialias = a;
+ _markForRendering();
+ }
+}
+
+void
+DrawingItem::setIsolation(unsigned isolation)
+{
+ _isolation = isolation;
+ //if( isolation != 0 ) std::cout << "isolation: " << isolation << std::endl;
_markForRendering();
}
void
-DrawingItem::setVisible(bool v)
+DrawingItem::setBlendMode(unsigned blend_mode)
{
- _visible = v;
+ _blend_mode = blend_mode;
+ //if( blend_mode != 0 ) std::cout << "setBlendMode: " << blend_mode << std::endl;
_markForRendering();
}
+void
+DrawingItem::setVisible(bool v)
+{
+ if (_visible != v) {
+ _visible = v;
+ _markForRendering();
+ }
+}
+
/// This is currently unused
void
DrawingItem::setSensitive(bool s)
@@ -353,7 +456,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 +553,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.
@@ -452,7 +561,7 @@ struct MaskLuminanceToAlpha {
* @param flags Rendering options. This deals mainly with cache control.
*/
unsigned
-DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at)
+DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at)
{
bool outline = _drawing.outline();
bool render_filters = _drawing.renderFilters();
@@ -467,7 +576,7 @@ DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flag
// TODO convert outline rendering to a separate virtual function
if (outline) {
- _renderOutline(ct, area, flags);
+ _renderOutline(dc, area, flags);
return RENDER_OK;
}
@@ -475,11 +584,20 @@ DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flag
Geom::OptIntRect carea = Geom::intersect(area, _drawbox);
if (!carea) return RENDER_OK;
+ if (_antialias) {
+ cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_DEFAULT);
+ } else {
+ cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_NONE);
+ }
+
// render from cache if possible
if (_cached) {
if (_cache) {
_cache->prepare();
- _cache->paintFromCache(ct, carea);
+#ifdef WITH_CSSBLEND
+ set_cairo_blend_operator( dc, _blend_mode );
+#endif
+ _cache->paintFromCache(dc, carea);
if (!carea) return RENDER_OK;
} else {
// There is no cache. This could be because caching of this item
@@ -507,6 +625,10 @@ DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flag
nir |= (_filter != NULL && render_filters); // 3. it has a filter
nir |= needs_opacity; // 4. it is non-opaque
nir |= (_cache != NULL); // 5. it is cached
+#ifdef WITH_CSSBLEND
+ nir |= (_blend_mode != SP_CSS_BLEND_NORMAL); // 6. Blend mode not normal
+ nir |= (_isolation == SP_CSS_ISOLATION_ISOLATE); // 7. Explicit isolatiom
+#endif
/* How the rendering is done.
*
@@ -525,7 +647,7 @@ DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flag
// filters and opacity do not apply when rendering the ancestors of the filtered
// element
if ((flags & RENDER_FILTER_BACKGROUND) || !needs_intermediate_rendering) {
- return _renderItem(ct, *carea, flags & ~RENDER_FILTER_BACKGROUND, stop_at);
+ return _renderItem(dc, *carea, flags & ~RENDER_FILTER_BACKGROUND, stop_at);
}
// iarea is the bounding box for intermediate rendering
@@ -550,10 +672,12 @@ DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flag
// for overlapping clip children. To fix this we use the SOURCE operator
// instead of the default OVER.
ict.setOperator(CAIRO_OPERATOR_SOURCE);
+ ict.paint();
if (_clip) {
+ ict.pushGroup();
_clip->clip(ict, *carea); // fixme: carea or area?
- } else {
- // if there is no clipping path, fill the entire surface with alpha = opacity.
+ ict.popGroupToSource();
+ ict.setOperator(CAIRO_OPERATOR_IN);
ict.paint();
}
ict.setOperator(CAIRO_OPERATOR_OVER); // reset back to default
@@ -586,9 +710,9 @@ DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flag
}
if (bg_root) {
DrawingSurface bg(*iarea);
- DrawingContext bgct(bg);
- bg_root->render(bgct, *iarea, flags | RENDER_FILTER_BACKGROUND, this);
- _filter->render(this, ict, &bgct);
+ DrawingContext bgdc(bg);
+ bg_root->render(bgdc, *iarea, flags | RENDER_FILTER_BACKGROUND, this);
+ _filter->render(this, ict, &bgdc);
rendered = true;
}
}
@@ -614,17 +738,20 @@ DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flag
cachect.fill();
_cache->markClean(*carea);
}
- ct.rectangle(*carea);
- ct.setSource(&intermediate);
- ct.fill();
- ct.setSource(0,0,0,0);
- // the call above is to clear a ref on the intermediate surface held by ct
+ dc.rectangle(*carea);
+ dc.setSource(&intermediate);
+#ifdef WITH_CSSBLEND
+ set_cairo_blend_operator( dc, _blend_mode );
+#endif
+ dc.fill();
+ dc.setSource(0,0,0,0);
+ // the call above is to clear a ref on the intermediate surface held by dc
return render_result;
}
void
-DrawingItem::_renderOutline(DrawingContext &ct, Geom::IntRect const &area, unsigned flags)
+DrawingItem::_renderOutline(DrawingContext &dc, Geom::IntRect const &area, unsigned flags)
{
// intersect with bbox rather than drawbox, as we want to render things outside
// of the clipping path as well
@@ -633,7 +760,7 @@ DrawingItem::_renderOutline(DrawingContext &ct, Geom::IntRect const &area, unsig
// just render everything: item, clip, mask
// First, render the object itself
- _renderItem(ct, *carea, flags, NULL);
+ _renderItem(dc, *carea, flags, NULL);
// render clip and mask, if any
guint32 saved_rgba = _drawing.outlinecolor; // save current outline color
@@ -641,12 +768,12 @@ DrawingItem::_renderOutline(DrawingContext &ct, Geom::IntRect const &area, unsig
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
if (_clip) {
_drawing.outlinecolor = prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff); // green clips
- _clip->render(ct, *carea, flags);
+ _clip->render(dc, *carea, flags);
}
// render mask as an object, using a different color
if (_mask) {
_drawing.outlinecolor = prefs->getInt("/options/wireframecolors/masks", 0x0000ffff); // blue masks
- _mask->render(ct, *carea, flags);
+ _mask->render(dc, *carea, flags);
}
_drawing.outlinecolor = saved_rgba; // restore outline color
}
@@ -660,36 +787,31 @@ DrawingItem::_renderOutline(DrawingContext &ct, Geom::IntRect const &area, unsig
* of render() for details.
*/
void
-DrawingItem::clip(Inkscape::DrawingContext &ct, Geom::IntRect const &area)
+DrawingItem::clip(Inkscape::DrawingContext &dc, Geom::IntRect const &area)
{
// don't bother if the object does not implement clipping (e.g. DrawingImage)
if (!_canClip()) return;
if (!_visible) return;
if (!area.intersects(_bbox)) return;
- // The item used as the clipping path itself has a clipping path.
- // Render this item's clipping path onto a temporary surface, then composite it
- // with the item using the IN operator
- if (_clip) {
- ct.pushAlphaGroup();
- { Inkscape::DrawingContext::Save save(ct);
- ct.setSource(0,0,0,1);
- _clip->clip(ct, area);
- }
- ct.pushAlphaGroup();
- }
-
+ dc.setSource(0,0,0,1);
+ dc.pushGroup();
// rasterize the clipping path
- _clipItem(ct, area);
-
+ _clipItem(dc, area);
if (_clip) {
- ct.popGroupToSource();
- ct.setOperator(CAIRO_OPERATOR_IN);
- ct.paint();
- ct.popGroupToSource();
- ct.setOperator(CAIRO_OPERATOR_SOURCE);
- ct.paint();
+ // The item used as the clipping path itself has a clipping path.
+ // Render this item's clipping path onto a temporary surface, then composite it
+ // with the item using the IN operator
+ dc.pushGroup();
+ _clip->clip(dc, area);
+ dc.popGroupToSource();
+ dc.setOperator(CAIRO_OPERATOR_IN);
+ dc.paint();
}
+ dc.popGroupToSource();
+ dc.setOperator(CAIRO_OPERATOR_OVER);
+ dc.paint();
+ dc.setSource(0,0,0,0);
}
/**
@@ -708,8 +830,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;
@@ -730,7 +855,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);
@@ -750,6 +877,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;
@@ -801,8 +931,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)
@@ -812,15 +942,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)
{
@@ -828,7 +974,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 810a61d9d..db803cf60 100644
--- a/src/display/drawing-item.h
+++ b/src/display/drawing-item.h
@@ -20,7 +20,7 @@
#include <2geom/rect.h>
#include <2geom/affine.h>
-class SPStyle;
+struct SPStyle;
namespace Inkscape {
@@ -108,11 +108,15 @@ public:
void setCached(bool c, bool persistent = false);
void setOpacity(float opacity);
+ void setAntialiasing(bool a);
+ void setIsolation(unsigned isolation); // CSS Compositing and Blending
+ void setBlendMode(unsigned blend_mode);
void setTransform(Geom::Affine const &trans);
void setClip(DrawingItem *item);
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; }
@@ -120,8 +124,8 @@ public:
void *data() const { return _user_data; }
void update(Geom::IntRect const &area = Geom::IntRect::infinite(), UpdateContext const &ctx = UpdateContext(), unsigned flags = STATE_ALL, unsigned reset = 0);
- unsigned render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags = 0, DrawingItem *stop_at = NULL);
- void clip(DrawingContext &ct, Geom::IntRect const &area);
+ unsigned render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags = 0, DrawingItem *stop_at = NULL);
+ void clip(DrawingContext &dc, Geom::IntRect const &area);
DrawingItem *pick(Geom::Point const &p, double delta, unsigned flags = 0);
protected:
@@ -138,7 +142,7 @@ protected:
RENDER_OK = 0,
RENDER_STOP = 1
};
- void _renderOutline(DrawingContext &ct, Geom::IntRect const &area, unsigned flags);
+ void _renderOutline(DrawingContext &dc, Geom::IntRect const &area, unsigned flags);
void _markForUpdate(unsigned state, bool propagate);
void _markForRendering();
void _invalidateFilterBackground(Geom::IntRect const &area);
@@ -147,9 +151,9 @@ protected:
Geom::OptIntRect _cacheRect();
virtual unsigned _updateItem(Geom::IntRect const &/*area*/, UpdateContext const &/*ctx*/,
unsigned /*flags*/, unsigned /*reset*/) { return 0; }
- virtual unsigned _renderItem(DrawingContext &/*ct*/, Geom::IntRect const &/*area*/, unsigned /*flags*/,
+ virtual unsigned _renderItem(DrawingContext &/*dc*/, Geom::IntRect const &/*area*/, unsigned /*flags*/,
DrawingItem * /*stop_at*/) { return RENDER_OK; }
- virtual void _clipItem(DrawingContext &/*ct*/, Geom::IntRect const &/*area*/) {}
+ virtual void _clipItem(DrawingContext &/*dc*/, Geom::IntRect const &/*area*/) {}
virtual DrawingItem *_pickItem(Geom::Point const &/*p*/, double /*delta*/, unsigned /*flags*/) { return NULL; }
virtual bool _canClip() { return false; }
@@ -175,7 +179,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;
@@ -200,6 +206,10 @@ protected:
//unsigned _renders_opacity : 1; ///< Whether object needs temporary surface for opacity
unsigned _pick_children : 1; ///< For groups: if true, children are returned from pick(),
/// otherwise the group is returned
+ unsigned _antialias : 1; ///< Whether to use antialiasing
+
+ unsigned _isolation : 1;
+ unsigned _blend_mode : 4;
friend class Drawing;
};
diff --git a/src/display/drawing-shape.cpp b/src/display/drawing-shape.cpp
index 4ca306092..92d71fad3 100644
--- a/src/display/drawing-shape.cpp
+++ b/src/display/drawing-shape.cpp
@@ -12,7 +12,7 @@
#include <glib.h>
#include <2geom/curves.h>
#include <2geom/pathvector.h>
-#include <2geom/svg-path.h>
+#include <2geom/path-sink.h>
#include <2geom/svg-path-parser.h>
#include "display/cairo-utils.h"
@@ -149,8 +149,53 @@ DrawingShape::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, u
return STATE_ALL;
}
+#ifdef WITH_SVG2
+void
+DrawingShape::_renderFill(DrawingContext &dc)
+{
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm);
+
+ bool has_fill = _nrstyle.prepareFill(dc, _item_bbox);
+
+ if( has_fill ) {
+ dc.path(_curve->get_pathvector());
+ _nrstyle.applyFill(dc);
+ dc.fillPreserve();
+ dc.newPath(); // clear path
+ }
+}
+
+void
+DrawingShape::_renderStroke(DrawingContext &dc)
+{
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm);
+
+ bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox);
+ has_stroke &= (_nrstyle.stroke_width != 0);
+
+ if( has_stroke ) {
+ // TODO: remove segments outside of bbox when no dashes present
+ dc.path(_curve->get_pathvector());
+ _nrstyle.applyStroke(dc);
+ dc.strokePreserve();
+ dc.newPath(); // clear path
+ }
+}
+#endif
+
+void
+DrawingShape::_renderMarkers(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at)
+{
+ // marker rendering
+ for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
+ i->render(dc, area, flags, stop_at);
+ }
+}
+
unsigned
-DrawingShape::_renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at)
+DrawingShape::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at)
{
if (!_curve || !_style) return RENDER_OK;
if (!area.intersects(_bbox)) return RENDER_OK; // skip if not within bounding box
@@ -160,67 +205,96 @@ DrawingShape::_renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigne
if (outline) {
guint32 rgba = _drawing.outlinecolor;
- { Inkscape::DrawingContext::Save save(ct);
- ct.transform(_ctm);
- ct.path(_curve->get_pathvector());
+ // paint-order doesn't matter
+ { Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm);
+ dc.path(_curve->get_pathvector());
}
- { Inkscape::DrawingContext::Save save(ct);
- ct.setSource(rgba);
- ct.setLineWidth(0.5);
- ct.setTolerance(1.25);
- ct.stroke();
+ { Inkscape::DrawingContext::Save save(dc);
+ dc.setSource(rgba);
+ dc.setLineWidth(0.5);
+ dc.setTolerance(0.5);
+ dc.stroke();
}
- } else {
- bool has_stroke, has_fill;
- // we assume the context has no path
- Inkscape::DrawingContext::Save save(ct);
- ct.transform(_ctm);
-
- // update fill and stroke paints.
- // this cannot be done during nr_arena_shape_update, because we need a Cairo context
- // to render svg:pattern
- has_fill = _nrstyle.prepareFill(ct, _item_bbox);
- has_stroke = _nrstyle.prepareStroke(ct, _item_bbox);
- has_stroke &= (_nrstyle.stroke_width != 0);
-
- if (has_fill || has_stroke) {
- // TODO: remove segments outside of bbox when no dashes present
- ct.path(_curve->get_pathvector());
- if (has_fill) {
- _nrstyle.applyFill(ct);
- ct.fillPreserve();
- }
- if (has_stroke) {
- _nrstyle.applyStroke(ct);
- ct.strokePreserve();
- }
- ct.newPath(); // clear path
- } // has fill or stroke pattern
+
+ _renderMarkers(dc, area, flags, stop_at);
+ return RENDER_OK;
+
}
- // marker rendering
- for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
- i->render(ct, area, flags, stop_at);
+#ifdef WITH_SVG2
+ if( _nrstyle.paint_order_layer[0] == NRStyle::PAINT_ORDER_NORMAL ) {
+ // This is the most common case, special case so we don't call get_pathvector(), etc. twice
+#endif
+ {
+ // we assume the context has no path
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm);
+
+ // update fill and stroke paints.
+ // this cannot be done during nr_arena_shape_update, because we need a Cairo context
+ // to render svg:pattern
+ bool has_fill = _nrstyle.prepareFill(dc, _item_bbox);
+ bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox);
+ has_stroke &= (_nrstyle.stroke_width != 0);
+
+ if (has_fill || has_stroke) {
+ // TODO: remove segments outside of bbox when no dashes present
+ dc.path(_curve->get_pathvector());
+ if (has_fill) {
+ _nrstyle.applyFill(dc);
+ dc.fillPreserve();
+ }
+ if (has_stroke) {
+ _nrstyle.applyStroke(dc);
+ dc.strokePreserve();
+ }
+ dc.newPath(); // clear path
+ } // has fill or stroke pattern
+ }
+ _renderMarkers(dc, area, flags, stop_at);
+ return RENDER_OK;
+
+#ifdef WITH_SVG2
+ }
+
+ // Handle different paint orders
+ for (unsigned i = 0; i < NRStyle::PAINT_ORDER_LAYERS; ++i) {
+ switch (_nrstyle.paint_order_layer[i]) {
+ case NRStyle::PAINT_ORDER_FILL:
+ _renderFill(dc);
+ break;
+ case NRStyle::PAINT_ORDER_STROKE:
+ _renderStroke(dc);
+ break;
+ case NRStyle::PAINT_ORDER_MARKER:
+ _renderMarkers(dc, area, flags, stop_at);
+ break;
+ default:
+ // PAINT_ORDER_AUTO Should not happen
+ break;
+ }
}
return RENDER_OK;
+#endif
}
-void DrawingShape::_clipItem(DrawingContext &ct, Geom::IntRect const & /*area*/)
+void DrawingShape::_clipItem(DrawingContext &dc, Geom::IntRect const & /*area*/)
{
if (!_curve) return;
- Inkscape::DrawingContext::Save save(ct);
+ Inkscape::DrawingContext::Save save(dc);
// handle clip-rule
if (_style) {
if (_style->clip_rule.computed == SP_WIND_RULE_EVENODD) {
- ct.setFillRule(CAIRO_FILL_RULE_EVEN_ODD);
+ dc.setFillRule(CAIRO_FILL_RULE_EVEN_ODD);
} else {
- ct.setFillRule(CAIRO_FILL_RULE_WINDING);
+ dc.setFillRule(CAIRO_FILL_RULE_WINDING);
}
}
- ct.transform(_ctm);
- ct.path(_curve->get_pathvector());
- ct.fill();
+ dc.transform(_ctm);
+ dc.path(_curve->get_pathvector());
+ dc.fill();
}
DrawingItem *
diff --git a/src/display/drawing-shape.h b/src/display/drawing-shape.h
index ce9bed2eb..405c789e0 100644
--- a/src/display/drawing-shape.h
+++ b/src/display/drawing-shape.h
@@ -15,7 +15,7 @@
#include "display/drawing-item.h"
#include "display/nr-style.h"
-class SPStyle;
+struct SPStyle;
class SPCurve;
namespace Inkscape {
@@ -33,12 +33,19 @@ public:
protected:
virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx,
unsigned flags, unsigned reset);
- virtual unsigned _renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags,
+ virtual unsigned _renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigned flags,
DrawingItem *stop_at);
- virtual void _clipItem(DrawingContext &ct, Geom::IntRect const &area);
+ virtual void _clipItem(DrawingContext &dc, Geom::IntRect const &area);
virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags);
virtual bool _canClip();
+#ifdef WITH_SVG2
+ void _renderFill(DrawingContext &dc);
+ void _renderStroke(DrawingContext &dc);
+#endif
+ void _renderMarkers(DrawingContext &dc, Geom::IntRect const &area, unsigned flags,
+ DrawingItem *stop_at);
+
SPCurve *_curve;
SPStyle *_style;
NRStyle _nrstyle;
diff --git a/src/display/drawing-surface.cpp b/src/display/drawing-surface.cpp
index bddccbd96..d2540de66 100644
--- a/src/display/drawing-surface.cpp
+++ b/src/display/drawing-surface.cpp
@@ -123,7 +123,7 @@ DrawingSurface::scale() const
Geom::Affine
DrawingSurface::drawingTransform() const
{
- Geom::Affine ret = _scale * Geom::Translate(-_origin);
+ Geom::Affine ret = Geom::Translate(-_origin) * _scale;
return ret;
}
@@ -215,7 +215,7 @@ DrawingCache::prepare()
bool is_identity = _pending_transform.isIdentity();
if (is_identity && _pending_area == old_area) return; // no change
- bool is_integer_translation = false;
+ bool is_integer_translation = is_identity;
if (!is_identity && _pending_transform.isTranslation()) {
Geom::IntPoint t = _pending_transform.translation().round();
if (Geom::are_near(Geom::Point(t), _pending_transform.translation())) {
@@ -224,6 +224,7 @@ DrawingCache::prepare()
if (old_area + t == _pending_area) {
// if the areas match, the only thing to do
// is to ensure that the clean area is not too large
+ // we can exit early
cairo_rectangle_int_t limit = _convertRect(_pending_area);
cairo_region_intersect_rectangle(_clean_region, &limit);
_origin += t;
@@ -232,33 +233,36 @@ DrawingCache::prepare()
}
}
}
- // otherwise, we need to transform the cache
+
+ // the area has changed, so the cache content needs to be copied
Geom::IntPoint old_origin = old_area.min();
cairo_surface_t *old_surface = _surface;
_surface = NULL;
_pixels = _pending_area.dimensions();
_origin = _pending_area.min();
- cairo_t *ct = createRawContext();
- if (!is_identity) {
- ink_cairo_transform(ct, _pending_transform);
- }
- cairo_set_source_surface(ct, old_surface, old_origin[X], old_origin[Y]);
- cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
- cairo_paint(ct);
-
- cairo_surface_destroy(old_surface);
- cairo_destroy(ct);
+ if (is_integer_translation) {
+ // transform the cache only for integer translations and identities
+ cairo_t *ct = createRawContext();
+ if (!is_identity) {
+ ink_cairo_transform(ct, _pending_transform);
+ }
+ cairo_set_source_surface(ct, old_surface, old_origin[X], old_origin[Y]);
+ cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
+ cairo_pattern_set_filter(cairo_get_source(ct), CAIRO_FILTER_NEAREST);
+ cairo_paint(ct);
+ cairo_destroy(ct);
- if (!is_identity && !is_integer_translation) {
+ cairo_rectangle_int_t limit = _convertRect(_pending_area);
+ cairo_region_intersect_rectangle(_clean_region, &limit);
+ } else {
// dirty everything
cairo_region_destroy(_clean_region);
_clean_region = cairo_region_create();
- } else {
- cairo_rectangle_int_t limit = _convertRect(_pending_area);
- cairo_region_intersect_rectangle(_clean_region, &limit);
}
+
//std::cout << _pending_transform << old_area << _pending_area << std::endl;
+ cairo_surface_destroy(old_surface);
_pending_transform.setIdentity();
}
@@ -267,7 +271,7 @@ DrawingCache::prepare()
* parameter to the bounds of the region that must be repainted.
*/
void
-DrawingCache::paintFromCache(DrawingContext &ct, Geom::OptIntRect &area)
+DrawingCache::paintFromCache(DrawingContext &dc, Geom::OptIntRect &area)
{
if (!area) return;
@@ -296,10 +300,10 @@ DrawingCache::paintFromCache(DrawingContext &ct, Geom::OptIntRect &area)
cairo_rectangle_int_t tmp;
for (int i = 0; i < nr; ++i) {
cairo_region_get_rectangle(cache_region, i, &tmp);
- ct.rectangle(_convertRect(tmp));
+ dc.rectangle(_convertRect(tmp));
}
- ct.setSource(this);
- ct.fill();
+ dc.setSource(this);
+ dc.fill();
}
cairo_region_destroy(cache_region);
}
@@ -310,21 +314,21 @@ DrawingCache::_dumpCache(Geom::OptIntRect const &area)
{
static int dumpnr = 0;
cairo_surface_t *surface = ink_cairo_surface_copy(_surface);
- DrawingContext ct(surface, _origin);
+ DrawingContext dc(surface, _origin);
if (!cairo_region_is_empty(_clean_region)) {
- Inkscape::DrawingContext::Save save(ct);
+ Inkscape::DrawingContext::Save save(dc);
int nr = cairo_region_num_rectangles(_clean_region);
cairo_rectangle_int_t tmp;
for (int i = 0; i < nr; ++i) {
cairo_region_get_rectangle(_clean_region, i, &tmp);
- ct.rectangle(_convertRect(tmp));
+ dc.rectangle(_convertRect(tmp));
}
- ct.setSource(0,1,0,0.1);
- ct.fill();
+ dc.setSource(0,1,0,0.1);
+ dc.fill();
}
- ct.rectangle(*area);
- ct.setSource(1,0,0,0.1);
- ct.fill();
+ dc.rectangle(*area);
+ dc.setSource(1,0,0,0.1);
+ dc.fill();
char *fn = g_strdup_printf("dump%d.png", dumpnr++);
cairo_surface_write_to_png(surface, fn);
cairo_surface_destroy(surface);
diff --git a/src/display/drawing-surface.h b/src/display/drawing-surface.h
index 1ec848405..e937cca55 100644
--- a/src/display/drawing-surface.h
+++ b/src/display/drawing-surface.h
@@ -65,7 +65,7 @@ public:
void markClean(Geom::IntRect const &area = Geom::IntRect::infinite());
void scheduleTransform(Geom::IntRect const &new_area, Geom::Affine const &trans);
void prepare();
- void paintFromCache(DrawingContext &ct, Geom::OptIntRect &area);
+ void paintFromCache(DrawingContext &dc, Geom::OptIntRect &area);
protected:
cairo_region_t *_clean_region;
diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp
index 94a9690fb..a280e221a 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)
@@ -53,51 +55,86 @@ DrawingGlyphs::setGlyph(font_instance *font, int glyph, Geom::Affine const &tran
unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext const &ctx, unsigned /*flags*/, unsigned /*reset*/)
{
DrawingText *ggroup = dynamic_cast<DrawingText *>(_parent);
- if (!ggroup) throw InvalidItemException();
+ if (!ggroup) {
+ throw InvalidItemException();
+ }
- if (!_font || !ggroup->_style) return STATE_ALL;
- if (ggroup->_nrstyle.fill.type == NRStyle::PAINT_NONE &&
- ggroup->_nrstyle.stroke.type == NRStyle::PAINT_NONE)
- {
+ if (!_font || !ggroup->_style) {
return STATE_ALL;
}
+
_pick_bbox = Geom::IntRect();
_bbox = Geom::IntRect();
- Geom::OptRect b = bounds_exact_transformed(*_font->PathVector(_glyph), ctx.ctm);
- if (b && ggroup->_nrstyle.stroke.type != NRStyle::PAINT_NONE) {
- float width, scale;
- scale = ctx.ctm.descrim();
- if (_transform) scale /= _transform->descrim(); // FIXME temporary hack
- width = MAX(0.125, ggroup->_nrstyle.stroke_width * scale);
+/* 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.
+
+ Geom::OptRect 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_bigbox = 1.0;
+ if (_transform) {
+ scale_bigbox /= _transform->descrim();
+ }
+
+ Geom::Rect bigbox(Geom::Point(0.0, _asc*scale_bigbox*1.1),Geom::Point(_width*scale_bigbox, -_dsc*scale_bigbox*1.1));
+ Geom::Rect b = bigbox * ctx.ctm;
+
+ if (ggroup->_nrstyle.stroke.type != NRStyle::PAINT_NONE) {
+ // this expands the selection box for cases where the stroke is "thick"
+ float scale = ctx.ctm.descrim();
+ if (_transform) {
+ scale /= _transform->descrim(); // FIXME temporary hack
+ }
+ float width = MAX(0.125, ggroup->_nrstyle.stroke_width * scale);
if ( fabs(ggroup->_nrstyle.stroke_width * scale) > 0.01 ) { // FIXME: this is always true
- b->expandBy(0.5 * width);
+ b.expandBy(0.5 * width);
}
- // save bbox without miters for picking
- _pick_bbox = b->roundOutwards();
+
+ // save bbox without miters for picking
+ _pick_bbox = b.roundOutwards();
float miterMax = width * ggroup->_nrstyle.miter_limit;
if ( miterMax > 0.01 ) {
// grunt mode. we should compute the various miters instead
// (one for each point on the curve)
- b->expandBy(miterMax);
+ b.expandBy(miterMax);
}
- _bbox = b->roundOutwards();
- } else if (b) {
- _bbox = b->roundOutwards();
+ _bbox = b.roundOutwards();
+ } else {
+ _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;
}
DrawingItem *
DrawingGlyphs::_pickItem(Geom::Point const &p, double delta, unsigned /*flags*/)
{
- if (!_font || !_bbox) return NULL;
+ DrawingText *ggroup = dynamic_cast<DrawingText *>(_parent);
+ if (!ggroup) {
+ throw InvalidItemException();
+ }
+ bool invisible = (ggroup->_nrstyle.fill.type == NRStyle::PAINT_NONE) &&
+ (ggroup->_nrstyle.stroke.type == NRStyle::PAINT_NONE);
+ if (!_font || !_bbox || (!_drawing.outline() && invisible) ) {
+ 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;
@@ -120,15 +157,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)
{
- if (!font || !font->PathVector(glyph)) return;
+/* original, did not save a glyph for white space characters, causes problems for text-decoration
+ if (!font || !font->PathVector(glyph)) {
+ 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
@@ -145,89 +195,353 @@ DrawingText::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, un
return DrawingGroup::_updateItem(area, ctx, flags, reset);
}
-unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/)
+void DrawingText::decorateStyle(DrawingContext &dc, double vextent, double xphase, Geom::Point const &p1, Geom::Point const &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);
+ dc.moveTo(ps);
+ dc.lineTo(pf);
+ ps += Geom::Point(0, vextent/6.0);
+ pf += Geom::Point(0, vextent/6.0);
+ dc.moveTo(ps);
+ dc.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;
+ dc.moveTo(ps);
+ ps += Geom::Point(step * (double)dots[i], 0.0);
+ if(ps[Geom::X]>= pf[Geom::X]){
+ dc.lineTo(pf);
+ break;
+ }
+ else {
+ dc.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;
+ dc.moveTo(ps);
+ ps += Geom::Point(step * (double)dashes[i], 0.0);
+ if(ps[Geom::X]>= pf[Geom::X]){
+ dc.lineTo(pf);
+ break;
+ }
+ else {
+ dc.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];
+ dc.moveTo(Geom::Point(x, y + amp * wave[i]));
+ while(1){
+ i = ((i + 1) & 15);
+ x += step;
+ dc.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
+ dc.moveTo(ps);
+ dc.lineTo(pf);
+// dc.revrectangle(Geom::Rect(ps,pf));
+ }
+}
+
+/* returns scaled line thickness */
+double DrawingText::decorateItem(DrawingContext &dc, Geom::Affine const &aff, double phase_length)
+{
+ double tsp_width_adj = _nrstyle.tspan_width / _nrstyle.font_size;
+ double tsp_asc_adj = _nrstyle.ascender / _nrstyle.font_size;
+ double tsp_size_adj = (_nrstyle.ascender + _nrstyle.descender) / _nrstyle.font_size;
+
+ double final_underline_thickness = CLAMP(_nrstyle.underline_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0);
+ double final_line_through_thickness = CLAMP(_nrstyle.line_through_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0);
+
+ double scale = aff.descrim();
+ double xphase = phase_length/ _nrstyle.font_size; // used to figure out phase of patterns
+
+ Inkscape::DrawingContext::Save save(dc);
+ dc.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
+ double 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(dc, 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(dc, 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(dc, 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(dc, 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(dc, tsp_size_adj, xphase, p1, p2);
+ }
+ thickness *= scale;
+ return(thickness);
+}
+
+unsigned DrawingText::_renderItem(DrawingContext &dc, 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);
- ct.setTolerance(1.25); // low quality, but good enough for outline mode
+ Inkscape::DrawingContext::Save save(dc);
+ dc.setSource(rgba);
+ dc.setTolerance(0.5); // low quality, but good enough for outline mode
for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i);
if (!g) throw InvalidItemException();
- Inkscape::DrawingContext::Save save(ct);
- // skip glpyhs with singular transforms
+ Inkscape::DrawingContext::Save save(dc);
+ // skip glyphs with singular transforms
if (g->_ctm.isSingular()) continue;
- ct.transform(g->_ctm);
- ct.path(*g->_font->PathVector(g->_glyph));
- ct.fill();
+ dc.transform(g->_ctm);
+ if(g->_drawable){
+ dc.path(*g->_font->PathVector(g->_glyph));
+ dc.fill();
+ }
}
return RENDER_OK;
}
// NOTE: this is very similar to drawing-shape.cpp; the only difference is in path feeding
+ double leftmost = DBL_MAX;
+ double phase_length = 0.0;
+ bool firsty = true;
+ bool decorate = true;
+ double starty = 0.0;
+ Geom::Affine aff;
+ using Geom::X;
+ using Geom::Y;
+
+ // NOTE:
+ // prepareFill / prepareStroke need to be called with _ctm in effect.
+ // However, we might need to apply a different ctm for glyphs.
+ // Therefore, only apply this ctm temporarily.
bool has_stroke, has_fill;
+ {
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm);
- has_fill = _nrstyle.prepareFill(ct, _item_bbox);
- has_stroke = _nrstyle.prepareStroke(ct, _item_bbox);
+ has_fill = _nrstyle.prepareFill( dc, _item_bbox);
+ has_stroke = _nrstyle.prepareStroke(dc, _item_bbox);
+ }
if (has_fill || has_stroke) {
+ Geom::Affine rotinv;
+ bool invset = false;
+
+ // accumulate the path that represents the glyphs
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);
+ Inkscape::DrawingContext::Save save(dc);
if (g->_ctm.isSingular()) continue;
- ct.transform(g->_ctm);
- ct.path(*g->_font->PathVector(g->_glyph));
+ dc.transform(g->_ctm);
+ if (g->_drawable) {
+ dc.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);
+ // draw the text itself
+ // we need to apply this object's ctm again
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(_ctm);
+
+#ifdef WITH_SVG2
+ // Text doesn't have markers, we can do paint-order quick and dirty.
+ bool fill_first = false;
+ if( _nrstyle.paint_order_layer[0] == NRStyle::PAINT_ORDER_NORMAL ||
+ _nrstyle.paint_order_layer[0] == NRStyle::PAINT_ORDER_FILL ||
+ _nrstyle.paint_order_layer[2] == NRStyle::PAINT_ORDER_STROKE ) {
+ fill_first = true;
+ } // Won't get "stroke fill stroke" but that isn't 'valid'
+
+ if (has_fill && fill_first) {
+#else
if (has_fill) {
- _nrstyle.applyFill(ct);
- ct.fillPreserve();
+#endif
+ _nrstyle.applyFill(dc);
+ dc.fillPreserve();
}
if (has_stroke) {
- _nrstyle.applyStroke(ct);
- ct.strokePreserve();
+ _nrstyle.applyStroke(dc);
+ dc.strokePreserve();
+ }
+#ifdef WITH_SVG2
+ if (has_fill && !fill_first) {
+ _nrstyle.applyFill(dc);
+ dc.fillPreserve();
+ }
+#endif
+ dc.newPath(); // clear path
+
+ // draw text decoration
+ 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);
+ }
+ dc.setSource(ergba);
+ dc.setTolerance(0.5);
+ double thickness = decorateItem(dc, aff, phase_length);
+ dc.setLineWidth(thickness);
+ dc.strokePreserve();
+ dc.newPath(); // clear path
}
- ct.newPath(); // clear path
}
return RENDER_OK;
}
-void DrawingText::_clipItem(DrawingContext &ct, Geom::IntRect const &/*area*/)
+void DrawingText::_clipItem(DrawingContext &dc, Geom::IntRect const &/*area*/)
{
- Inkscape::DrawingContext::Save save(ct);
+ Inkscape::DrawingContext::Save save(dc);
// handle clip-rule
if (_style) {
if (_style->clip_rule.computed == SP_WIND_RULE_EVENODD) {
- ct.setFillRule(CAIRO_FILL_RULE_EVEN_ODD);
+ dc.setFillRule(CAIRO_FILL_RULE_EVEN_ODD);
} else {
- ct.setFillRule(CAIRO_FILL_RULE_WINDING);
+ dc.setFillRule(CAIRO_FILL_RULE_WINDING);
}
}
for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i);
- if (!g) throw InvalidItemException();
+ if (!g) {
+ throw InvalidItemException();
+ }
- Inkscape::DrawingContext::Save save(ct);
- ct.transform(g->_ctm);
- ct.path(*g->_font->PathVector(g->_glyph));
+ Inkscape::DrawingContext::Save save(dc);
+ dc.transform(g->_ctm);
+ if(g->_drawable){
+ dc.path(*g->_font->PathVector(g->_glyph));
+ }
}
- ct.fill();
+ dc.fill();
}
DrawingItem *
DrawingText::_pickItem(Geom::Point const &p, double delta, unsigned flags)
{
DrawingItem *picked = DrawingGroup::_pickItem(p, delta, flags);
- if (picked) return this;
+ if (picked) {
+ return this;
+ }
return NULL;
}
diff --git a/src/display/drawing-text.h b/src/display/drawing-text.h
index 929d2bf2d..b863ca2a4 100644
--- a/src/display/drawing-text.h
+++ b/src/display/drawing-text.h
@@ -15,7 +15,7 @@
#include "display/drawing-group.h"
#include "display/nr-style.h"
-class SPStyle;
+struct SPStyle;
class font_instance;
namespace Inkscape {
@@ -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,18 +54,22 @@ 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);
- virtual unsigned _renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags,
+ virtual unsigned _renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigned flags,
DrawingItem *stop_at);
- virtual void _clipItem(DrawingContext &ct, Geom::IntRect const &area);
+ virtual void _clipItem(DrawingContext &dc, Geom::IntRect const &area);
virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags);
virtual bool _canClip();
+ double decorateItem(DrawingContext &dc, Geom::Affine const &aff, double phase_length);
+ void decorateStyle(DrawingContext &dc, double vextent, double xphase, Geom::Point const &p1, Geom::Point const &p2);
NRStyle _nrstyle;
friend class DrawingGlyphs;
diff --git a/src/display/drawing.cpp b/src/display/drawing.cpp
index 77f24caf3..6e728b03d 100644
--- a/src/display/drawing.cpp
+++ b/src/display/drawing.cpp
@@ -4,8 +4,9 @@
*//*
* Authors:
* Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
*
- * Copyright (C) 2011 Authors
+ * Copyright (C) 2011-2012 Authors
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -14,8 +15,21 @@
#include "nr-filter-gaussian.h"
#include "nr-filter-types.h"
+//grayscale colormode:
+#include "cairo-templates.h"
+#include "drawing-context.h"
+
+
namespace Inkscape {
+// hardcoded grayscale color matrix values as default
+static const gdouble grayscale_value_matrix[20] = {
+ 0.21, 0.72, 0.072, 0, 0,
+ 0.21, 0.72, 0.072, 0, 0,
+ 0.21, 0.72, 0.072, 0, 0,
+ 0 , 0 , 0 , 1, 0
+};
+
Drawing::Drawing(SPCanvasArena *arena)
: _root(NULL)
, outlinecolor(0x000000ff)
@@ -27,6 +41,7 @@ Drawing::Drawing(SPCanvasArena *arena)
, _filter_quality(Filters::FILTER_QUALITY_BEST)
, _cache_score_threshold(50000.0)
, _cache_budget(0)
+ , _grayscale_colormatrix(std::vector<gdouble> (grayscale_value_matrix, grayscale_value_matrix + 20 ))
, _canvasarena(arena)
{
@@ -136,6 +151,12 @@ Drawing::setCacheBudget(size_t bytes)
}
void
+Drawing::setGrayscaleMatrix(gdouble value_matrix[20]) {
+ _grayscale_colormatrix = Filters::FilterColorMatrix::ColorMatrixMatrix(
+ std::vector<gdouble> (value_matrix, value_matrix + 20) );
+}
+
+void
Drawing::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset)
{
if (_root) {
@@ -146,10 +167,24 @@ Drawing::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned fl
}
void
-Drawing::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags)
+Drawing::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags)
{
if (_root) {
- _root->render(ct, area, flags);
+ _root->render(dc, area, flags);
+ }
+
+ if (colorMode() == COLORMODE_GRAYSCALE) {
+ // apply grayscale filter on top of everything
+ cairo_surface_t *input = dc.rawTarget();
+ cairo_surface_t *out = ink_cairo_surface_create_identical(input);
+ ink_cairo_surface_filter(input, out, _grayscale_colormatrix);
+ Geom::Point origin = dc.targetLogicalBounds().min();
+ dc.setSource(out, origin[Geom::X], origin[Geom::Y]);
+ dc.setOperator(CAIRO_OPERATOR_SOURCE);
+ dc.paint();
+ dc.setOperator(CAIRO_OPERATOR_OVER);
+
+ cairo_surface_destroy(out);
}
}
diff --git a/src/display/drawing.h b/src/display/drawing.h
index 8154f0783..cc74833ba 100644
--- a/src/display/drawing.h
+++ b/src/display/drawing.h
@@ -20,7 +20,7 @@
#include <2geom/rect.h>
#include "display/drawing-item.h"
#include "display/rendermode.h"
-
+#include "nr-filter-colormatrix.h"
typedef struct _SPCanvasArena SPCanvasArena;
@@ -65,8 +65,10 @@ public:
OutlineColors const &colors() const { return _colors; }
+ void setGrayscaleMatrix(gdouble value_matrix[20]);
+
void update(Geom::IntRect const &area = Geom::IntRect::infinite(), UpdateContext const &ctx = UpdateContext(), unsigned flags = DrawingItem::STATE_ALL, unsigned reset = 0);
- void render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags = 0);
+ void render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags = 0);
DrawingItem *pick(Geom::Point const &p, double delta, unsigned flags);
sigc::signal<void, DrawingItem *> signal_request_update;
@@ -97,6 +99,7 @@ private:
size_t _cache_budget; ///< maximum allowed size of cache
OutlineColors _colors;
+ Filters::FilterColorMatrix::ColorMatrixMatrix _grayscale_colormatrix;
SPCanvasArena *_canvasarena; // may be NULL is this arena is not the screen
// but used for export etc.
diff --git a/src/display/gnome-canvas-acetate.cpp b/src/display/gnome-canvas-acetate.cpp
index 0891836b5..2cd0f296d 100644
--- a/src/display/gnome-canvas-acetate.cpp
+++ b/src/display/gnome-canvas-acetate.cpp
@@ -17,7 +17,7 @@
static void sp_canvas_acetate_class_init (SPCanvasAcetateClass *klass);
static void sp_canvas_acetate_init (SPCanvasAcetate *acetate);
-static void sp_canvas_acetate_destroy (GtkObject *object);
+static void sp_canvas_acetate_destroy(SPCanvasItem *object);
static void sp_canvas_acetate_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
static double sp_canvas_acetate_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item);
@@ -45,16 +45,11 @@ GType sp_canvas_acetate_get_type (void)
static void sp_canvas_acetate_class_init (SPCanvasAcetateClass *klass)
{
- GtkObjectClass *object_class;
- SPCanvasItemClass *item_class;
-
- object_class = (GtkObjectClass *) klass;
- item_class = (SPCanvasItemClass *) klass;
+ SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass);
- object_class->destroy = sp_canvas_acetate_destroy;
-
+ item_class->destroy = sp_canvas_acetate_destroy;
item_class->update = sp_canvas_acetate_update;
item_class->point = sp_canvas_acetate_point;
}
@@ -64,13 +59,13 @@ static void sp_canvas_acetate_init (SPCanvasAcetate */*acetate*/)
/* Nothing here */
}
-static void sp_canvas_acetate_destroy (GtkObject *object)
+static void sp_canvas_acetate_destroy(SPCanvasItem *object)
{
g_return_if_fail (object != NULL);
g_return_if_fail (GNOME_IS_CANVAS_ACETATE (object));
- if (GTK_OBJECT_CLASS (parent_class)->destroy)
- (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+ if (SP_CANVAS_ITEM_CLASS(parent_class)->destroy)
+ (* SP_CANVAS_ITEM_CLASS(parent_class)->destroy) (object);
}
static void sp_canvas_acetate_update( SPCanvasItem *item, Geom::Affine const &/*affine*/, unsigned int /*flags*/ )
diff --git a/src/display/guideline.cpp b/src/display/guideline.cpp
index 652f4e2d7..55c1ee495 100644
--- a/src/display/guideline.cpp
+++ b/src/display/guideline.cpp
@@ -31,7 +31,7 @@ using Inkscape::ControlManager;
static void sp_guideline_class_init(SPGuideLineClass *c);
static void sp_guideline_init(SPGuideLine *guideline);
-static void sp_guideline_destroy(GtkObject *object);
+static void sp_guideline_destroy(SPCanvasItem *object);
static void sp_guideline_update(SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf);
@@ -66,12 +66,10 @@ GType sp_guideline_get_type()
static void sp_guideline_class_init(SPGuideLineClass *c)
{
- parent_class = (SPCanvasItemClass*) g_type_class_peek_parent(c);
+ parent_class = SP_CANVAS_ITEM_CLASS(g_type_class_peek_parent(c));
- GtkObjectClass *object_class = (GtkObjectClass *) c;
- object_class->destroy = sp_guideline_destroy;
-
- SPCanvasItemClass *item_class = (SPCanvasItemClass *) c;
+ SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(c);
+ item_class->destroy = sp_guideline_destroy;
item_class->update = sp_guideline_update;
item_class->render = sp_guideline_render;
item_class->point = sp_guideline_point;
@@ -90,21 +88,27 @@ static void sp_guideline_init(SPGuideLine *gl)
gl->label = NULL;
}
-static void sp_guideline_destroy(GtkObject *object)
+static void sp_guideline_destroy(SPCanvasItem *object)
{
g_return_if_fail (object != NULL);
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)) {
- gtk_object_destroy(GTK_OBJECT(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");
}
- GTK_OBJECT_CLASS(parent_class)->destroy(object);
+ if (gl->label) {
+ g_free(gl->label);
+ }
+
+ SP_CANVAS_ITEM_CLASS(parent_class)->destroy(object);
}
static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf)
@@ -193,8 +197,8 @@ static void sp_guideline_update(SPCanvasItem *item, Geom::Affine const &affine,
{
SPGuideLine *gl = SP_GUIDELINE(item);
- if (((SPCanvasItemClass *) parent_class)->update) {
- ((SPCanvasItemClass *) parent_class)->update(item, affine, flags);
+ if ((SP_CANVAS_ITEM_CLASS(parent_class))->update) {
+ (SP_CANVAS_ITEM_CLASS(parent_class))->update(item, affine, flags);
}
gl->affine = affine;
@@ -290,7 +294,7 @@ void sp_guideline_set_sensitive(SPGuideLine *gl, int sensitive)
void sp_guideline_delete(SPGuideLine *gl)
{
//gtk_object_destroy(GTK_OBJECT(gl->origin));
- gtk_object_destroy(GTK_OBJECT(gl));
+ sp_canvas_item_destroy(SP_CANVAS_ITEM(gl));
}
static void
diff --git a/src/display/guideline.h b/src/display/guideline.h
index 164244c46..2d9a87d9b 100644
--- a/src/display/guideline.h
+++ b/src/display/guideline.h
@@ -21,7 +21,7 @@
#define SP_GUIDELINE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_GUIDELINE, SPGuideLine))
#define SP_IS_GUIDELINE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), SP_TYPE_GUIDELINE))
-class SPCtrlPoint;
+struct SPCtrlPoint;
struct SPGuideLine {
SPCanvasItem item;
diff --git a/src/display/nr-filter-blend.cpp b/src/display/nr-filter-blend.cpp
index 267883b4b..099816bce 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,136 +43,92 @@ 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);
cairo_surface_t *input2 = slot.getcairo(_input2);
- cairo_content_t ct1 = cairo_surface_get_content(input1);
- cairo_content_t ct2 = cairo_surface_get_content(input2);
+ // We may need to transform input surface to correct color interpolation space. The input surface
+ // might be used as input to another primitive but it is likely that all the primitives in a given
+ // filter use the same color interpolation space so we don't copy the input before converting.
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+ }
+ set_cairo_surface_ci( input1, ci_fp );
+ set_cairo_surface_ci( input2, ci_fp );
// input2 is the "background" image
// out should be ARGB32 if any of the inputs is ARGB32
cairo_surface_t *out = ink_cairo_surface_create_output(input1, input2);
+ set_cairo_surface_ci( out, ci_fp );
+
+ 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;
+#ifdef WITH_CSSBLEND
+ // NEW
+ case BLEND_OVERLAY:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_OVERLAY);
+ break;
+ case BLEND_COLORDODGE:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_COLOR_DODGE);
+ break;
+ case BLEND_COLORBURN:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_COLOR_BURN);
+ break;
+ case BLEND_HARDLIGHT:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_HARD_LIGHT);
+ break;
+ case BLEND_SOFTLIGHT:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_SOFT_LIGHT);
+ break;
+ case BLEND_DIFFERENCE:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_DIFFERENCE);
+ break;
+ case BLEND_EXCLUSION:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_EXCLUSION);
+ break;
+ case BLEND_HUE:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_HSL_HUE);
+ break;
+ case BLEND_SATURATION:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_HSL_SATURATION);
+ break;
+ case BLEND_COLOR:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_HSL_COLOR);
+ break;
+ case BLEND_LUMINOSITY:
+ cairo_set_operator(out_ct, CAIRO_OPERATOR_HSL_LUMINOSITY);
+ break;
+#endif
- 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;
- }
+ 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);
}
@@ -222,9 +165,18 @@ void FilterBlend::set_input(int input, int slot) {
}
void FilterBlend::set_mode(FilterBlendMode mode) {
- if (mode == BLEND_NORMAL || mode == BLEND_MULTIPLY ||
- mode == BLEND_SCREEN || mode == BLEND_DARKEN ||
- mode == BLEND_LIGHTEN)
+ if (mode == BLEND_NORMAL || mode == BLEND_MULTIPLY ||
+ mode == BLEND_SCREEN || mode == BLEND_DARKEN ||
+ mode == BLEND_LIGHTEN
+#ifdef WITH_CSSBLEND
+ || mode == BLEND_OVERLAY ||
+ mode == BLEND_COLORDODGE || mode == BLEND_COLORBURN ||
+ mode == BLEND_HARDLIGHT || mode == BLEND_SOFTLIGHT ||
+ mode == BLEND_DIFFERENCE || mode == BLEND_EXCLUSION ||
+ mode == BLEND_HUE || mode == BLEND_SATURATION ||
+ mode == BLEND_COLOR || mode == BLEND_LUMINOSITY
+#endif
+ )
{
_blend_mode = mode;
}
diff --git a/src/display/nr-filter-blend.h b/src/display/nr-filter-blend.h
index 957d3cfc8..0a2927d87 100644
--- a/src/display/nr-filter-blend.h
+++ b/src/display/nr-filter-blend.h
@@ -28,7 +28,21 @@ enum FilterBlendMode {
BLEND_SCREEN,
BLEND_DARKEN,
BLEND_LIGHTEN,
- BLEND_ENDMODE
+#ifdef WITH_CSSBLEND
+ // New in CSS Compositing and Blending Level 1
+ BLEND_OVERLAY,
+ BLEND_COLORDODGE,
+ BLEND_COLORBURN,
+ BLEND_HARDLIGHT,
+ BLEND_SOFTLIGHT,
+ BLEND_DIFFERENCE,
+ BLEND_EXCLUSION,
+ BLEND_HUE,
+ BLEND_SATURATION,
+ BLEND_COLOR,
+ BLEND_LUMINOSITY,
+#endif
+ BLEND_ENDMODE,
};
class FilterBlend : public FilterPrimitive {
diff --git a/src/display/nr-filter-colormatrix.cpp b/src/display/nr-filter-colormatrix.cpp
index 33718ed68..77873d523 100644
--- a/src/display/nr-filter-colormatrix.cpp
+++ b/src/display/nr-filter-colormatrix.cpp
@@ -32,50 +32,47 @@ FilterPrimitive * FilterColorMatrix::create() {
FilterColorMatrix::~FilterColorMatrix()
{}
-struct ColorMatrixMatrix {
- ColorMatrixMatrix(std::vector<double> const &values) {
- unsigned limit = std::min(static_cast<size_t>(20), values.size());
- for (unsigned i = 0; i < limit; ++i) {
- if (i % 5 == 4) {
- _v[i] = round(values[i]*255*255);
- } else {
- _v[i] = round(values[i]*255);
- }
- }
- for (unsigned i = limit; i < 20; ++i) {
- _v[i] = 0;
+FilterColorMatrix::ColorMatrixMatrix::ColorMatrixMatrix(std::vector<double> const &values) {
+ unsigned limit = std::min(static_cast<size_t>(20), values.size());
+ for (unsigned i = 0; i < limit; ++i) {
+ if (i % 5 == 4) {
+ _v[i] = round(values[i]*255*255);
+ } else {
+ _v[i] = round(values[i]*255);
}
}
+ for (unsigned i = limit; i < 20; ++i) {
+ _v[i] = 0;
+ }
+}
- guint32 operator()(guint32 in) {
- EXTRACT_ARGB32(in, a, r, g, b)
- // we need to un-premultiply alpha values for this type of matrix
- // TODO: unpremul can be ignored if there is an identity mapping on the alpha channel
- if (a != 0) {
- r = unpremul_alpha(r, a);
- g = unpremul_alpha(g, a);
- b = unpremul_alpha(b, a);
- }
-
- gint32 ro = r*_v[0] + g*_v[1] + b*_v[2] + a*_v[3] + _v[4];
- gint32 go = r*_v[5] + g*_v[6] + b*_v[7] + a*_v[8] + _v[9];
- gint32 bo = r*_v[10] + g*_v[11] + b*_v[12] + a*_v[13] + _v[14];
- gint32 ao = r*_v[15] + g*_v[16] + b*_v[17] + a*_v[18] + _v[19];
- ro = (pxclamp(ro, 0, 255*255) + 127) / 255;
- go = (pxclamp(go, 0, 255*255) + 127) / 255;
- bo = (pxclamp(bo, 0, 255*255) + 127) / 255;
- ao = (pxclamp(ao, 0, 255*255) + 127) / 255;
+guint32 FilterColorMatrix::ColorMatrixMatrix::operator()(guint32 in) {
+ EXTRACT_ARGB32(in, a, r, g, b)
+ // we need to un-premultiply alpha values for this type of matrix
+ // TODO: unpremul can be ignored if there is an identity mapping on the alpha channel
+ if (a != 0) {
+ r = unpremul_alpha(r, a);
+ g = unpremul_alpha(g, a);
+ b = unpremul_alpha(b, a);
+ }
- ro = premul_alpha(ro, ao);
- go = premul_alpha(go, ao);
- bo = premul_alpha(bo, ao);
+ gint32 ro = r*_v[0] + g*_v[1] + b*_v[2] + a*_v[3] + _v[4];
+ gint32 go = r*_v[5] + g*_v[6] + b*_v[7] + a*_v[8] + _v[9];
+ gint32 bo = r*_v[10] + g*_v[11] + b*_v[12] + a*_v[13] + _v[14];
+ gint32 ao = r*_v[15] + g*_v[16] + b*_v[17] + a*_v[18] + _v[19];
+ ro = (pxclamp(ro, 0, 255*255) + 127) / 255;
+ go = (pxclamp(go, 0, 255*255) + 127) / 255;
+ bo = (pxclamp(bo, 0, 255*255) + 127) / 255;
+ ao = (pxclamp(ao, 0, 255*255) + 127) / 255;
+
+ ro = premul_alpha(ro, ao);
+ go = premul_alpha(go, ao);
+ bo = premul_alpha(bo, ao);
+
+ ASSEMBLE_ARGB32(pxout, ao, ro, go, bo)
+ return pxout;
+}
- ASSEMBLE_ARGB32(pxout, ao, ro, go, bo)
- return pxout;
- }
-private:
- gint32 _v[20];
-};
struct ColorMatrixSaturate {
ColorMatrixSaturate(double v_in) {
@@ -155,15 +152,27 @@ void FilterColorMatrix::render_cairo(FilterSlot &slot)
{
cairo_surface_t *input = slot.getcairo(_input);
cairo_surface_t *out = NULL;
+
+ // We may need to transform input surface to correct color interpolation space. The input surface
+ // might be used as input to another primitive but it is likely that all the primitives in a given
+ // filter use the same color interpolation space so we don't copy the input before converting.
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+ }
+ set_cairo_surface_ci( input, ci_fp );
+
if (type == COLORMATRIX_LUMINANCETOALPHA) {
out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_ALPHA);
} else {
out = ink_cairo_surface_create_identical(input);
+ // Set ci to that used for computation
+ set_cairo_surface_ci(out, ci_fp);
}
switch (type) {
case COLORMATRIX_MATRIX:
- ink_cairo_surface_filter(input, out, ColorMatrixMatrix(values));
+ ink_cairo_surface_filter(input, out, FilterColorMatrix::ColorMatrixMatrix(values));
break;
case COLORMATRIX_SATURATE:
ink_cairo_surface_filter(input, out, ColorMatrixSaturate(value));
diff --git a/src/display/nr-filter-colormatrix.h b/src/display/nr-filter-colormatrix.h
index 5f21a4210..c7e5e91d9 100644
--- a/src/display/nr-filter-colormatrix.h
+++ b/src/display/nr-filter-colormatrix.h
@@ -42,6 +42,15 @@ public:
virtual void set_type(FilterColorMatrixType type);
virtual void set_value(gdouble value);
virtual void set_values(std::vector<gdouble> const &values);
+
+public:
+ struct ColorMatrixMatrix {
+ ColorMatrixMatrix(std::vector<double> const &values);
+ guint32 operator()(guint32 in);
+ private:
+ gint32 _v[20];
+ };
+
private:
std::vector<gdouble> values;
gdouble value;
diff --git a/src/display/nr-filter-component-transfer.cpp b/src/display/nr-filter-component-transfer.cpp
index 226a73cef..dd90193fe 100644
--- a/src/display/nr-filter-component-transfer.cpp
+++ b/src/display/nr-filter-component-transfer.cpp
@@ -238,21 +238,37 @@ void FilterComponentTransfer::render_cairo(FilterSlot &slot)
{
cairo_surface_t *input = slot.getcairo(_input);
cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA);
+
+ // We may need to transform input surface to correct color interpolation space. The input surface
+ // might be used as input to another primitive but it is likely that all the primitives in a given
+ // filter use the same color interpolation space so we don't copy the input before converting.
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+ set_cairo_surface_ci(out, ci_fp );
+ }
+ set_cairo_surface_ci( input, ci_fp );
+
//cairo_surface_t *outtemp = ink_cairo_surface_create_identical(out);
ink_cairo_surface_blit(input, out);
// parameters: R = 0, G = 1, B = 2, A = 3
// Cairo: R = 2, G = 1, B = 0, A = 3
+ // If tableValues is empty, use identity.
for (unsigned i = 0; i < 3; ++i) {
guint32 color = 2 - i;
switch (type[i]) {
case COMPONENTTRANSFER_TYPE_TABLE:
- ink_cairo_surface_filter(out, out,
- ComponentTransferTable<false>(color, tableValues[i]));
+ if(!tableValues[i].empty()) {
+ ink_cairo_surface_filter(out, out,
+ ComponentTransferTable<false>(color, tableValues[i]));
+ }
break;
case COMPONENTTRANSFER_TYPE_DISCRETE:
- ink_cairo_surface_filter(out, out,
- ComponentTransferDiscrete<false>(color, tableValues[i]));
+ if(!tableValues[i].empty()) {
+ ink_cairo_surface_filter(out, out,
+ ComponentTransferDiscrete<false>(color, tableValues[i]));
+ }
break;
case COMPONENTTRANSFER_TYPE_LINEAR:
ink_cairo_surface_filter(out, out,
@@ -273,12 +289,16 @@ void FilterComponentTransfer::render_cairo(FilterSlot &slot)
// fast paths for alpha channel
switch (type[3]) {
case COMPONENTTRANSFER_TYPE_TABLE:
- ink_cairo_surface_filter(out, out,
- ComponentTransferTable<true>(tableValues[3]));
+ if(!tableValues[3].empty()) {
+ ink_cairo_surface_filter(out, out,
+ ComponentTransferTable<true>(tableValues[3]));
+ }
break;
case COMPONENTTRANSFER_TYPE_DISCRETE:
- ink_cairo_surface_filter(out, out,
- ComponentTransferDiscrete<true>(tableValues[3]));
+ if(!tableValues[3].empty()) {
+ ink_cairo_surface_filter(out, out,
+ ComponentTransferDiscrete<true>(tableValues[3]));
+ }
break;
case COMPONENTTRANSFER_TYPE_LINEAR:
ink_cairo_surface_filter(out, out,
diff --git a/src/display/nr-filter-composite.cpp b/src/display/nr-filter-composite.cpp
index b25ecdf2c..dc5e4278f 100644
--- a/src/display/nr-filter-composite.cpp
+++ b/src/display/nr-filter-composite.cpp
@@ -48,10 +48,11 @@ struct ComposeArithmetic {
gint32 go = _k1*ga*gb + _k2*ga + _k3*gb + _k4;
gint32 bo = _k1*ba*bb + _k2*ba + _k3*bb + _k4;
- ao = (pxclamp(ao, 0, 255*255*255) + (255*255/2)) / (255*255);
- ro = (pxclamp(ro, 0, 255*255*255) + (255*255/2)) / (255*255);
- go = (pxclamp(go, 0, 255*255*255) + (255*255/2)) / (255*255);
- bo = (pxclamp(bo, 0, 255*255*255) + (255*255/2)) / (255*255);
+ ao = pxclamp(ao, 0, 255*255*255); // r, g and b are premultiplied, so should be clamped to the alpha channel
+ ro = (pxclamp(ro, 0, ao) + (255*255/2)) / (255*255);
+ go = (pxclamp(go, 0, ao) + (255*255/2)) / (255*255);
+ bo = (pxclamp(bo, 0, ao) + (255*255/2)) / (255*255);
+ ao = (ao + (255*255/2)) / (255*255);
ASSEMBLE_ARGB32(pxout, ao, ro, go, bo)
return pxout;
@@ -65,7 +66,18 @@ void FilterComposite::render_cairo(FilterSlot &slot)
cairo_surface_t *input1 = slot.getcairo(_input);
cairo_surface_t *input2 = slot.getcairo(_input2);
+ // We may need to transform input surface to correct color interpolation space. The input surface
+ // might be used as input to another primitive but it is likely that all the primitives in a given
+ // filter use the same color interpolation space so we don't copy the input before converting.
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+ }
+ set_cairo_surface_ci( input1, ci_fp );
+ set_cairo_surface_ci( input2, ci_fp );
+
cairo_surface_t *out = ink_cairo_surface_create_output(input1, input2);
+ set_cairo_surface_ci(out, ci_fp );
if (op == COMPOSITE_ARITHMETIC) {
ink_cairo_surface_blend(input1, input2, out, ComposeArithmetic(k1, k2, k3, k4));
@@ -86,6 +98,33 @@ void FilterComposite::render_cairo(FilterSlot &slot)
case COMPOSITE_XOR:
cairo_set_operator(ct, CAIRO_OPERATOR_XOR);
break;
+#ifdef WITH_CSSCOMPOSITE
+ /* New CSS Operators */
+ case COMPOSITE_CLEAR:
+ cairo_set_operator(ct, CAIRO_OPERATOR_CLEAR);
+ break;
+ case COMPOSITE_COPY:
+ cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
+ break;
+ case COMPOSITE_DESTINATION:
+ cairo_set_operator(ct, CAIRO_OPERATOR_DEST);
+ break;
+ case COMPOSITE_DESTINATION_OVER:
+ cairo_set_operator(ct, CAIRO_OPERATOR_DEST_OVER);
+ break;
+ case COMPOSITE_DESTINATION_IN:
+ cairo_set_operator(ct, CAIRO_OPERATOR_DEST_IN);
+ break;
+ case COMPOSITE_DESTINATION_OUT:
+ cairo_set_operator(ct, CAIRO_OPERATOR_DEST_OUT);
+ break;
+ case COMPOSITE_DESTINATION_ATOP:
+ cairo_set_operator(ct, CAIRO_OPERATOR_DEST_ATOP);
+ break;
+ case COMPOSITE_LIGHTER:
+ cairo_set_operator(ct, CAIRO_OPERATOR_ADD);
+ break;
+#endif
case COMPOSITE_OVER:
case COMPOSITE_DEFAULT:
default:
@@ -117,13 +156,7 @@ void FilterComposite::set_input(int input, int slot) {
void FilterComposite::set_operator(FeCompositeOperator op) {
if (op == COMPOSITE_DEFAULT) {
this->op = COMPOSITE_OVER;
- } else if (op == COMPOSITE_OVER ||
- op == COMPOSITE_IN ||
- op == COMPOSITE_OUT ||
- op == COMPOSITE_ATOP ||
- op == COMPOSITE_XOR ||
- op == COMPOSITE_ARITHMETIC)
- {
+ } else if (op != COMPOSITE_ENDOPERATOR) {
this->op = op;
}
}
diff --git a/src/display/nr-filter-convolve-matrix.cpp b/src/display/nr-filter-convolve-matrix.cpp
index 5469aff88..2aad528a6 100644
--- a/src/display/nr-filter-convolve-matrix.cpp
+++ b/src/display/nr-filter-convolve-matrix.cpp
@@ -104,8 +104,6 @@ void FilterConvolveMatrix::render_cairo(FilterSlot &slot)
static bool bias_warning = false;
static bool edge_warning = false;
- cairo_surface_t *input = slot.getcairo(_input);
-
if (orderX<=0 || orderY<=0) {
g_warning("Empty kernel!");
return;
@@ -119,8 +117,19 @@ void FilterConvolveMatrix::render_cairo(FilterSlot &slot)
return;
}
+ cairo_surface_t *input = slot.getcairo(_input);
cairo_surface_t *out = ink_cairo_surface_create_identical(input);
+ // We may need to transform input surface to correct color interpolation space. The input surface
+ // might be used as input to another primitive but it is likely that all the primitives in a given
+ // filter use the same color interpolation space so we don't copy the input before converting.
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+ set_cairo_surface_ci(out, ci_fp);
+ }
+ set_cairo_surface_ci( input, ci_fp );
+
if (bias!=0 && !bias_warning) {
g_warning("It is unknown whether Inkscape's implementation of bias in feConvolveMatrix "
"is correct!");
diff --git a/src/display/nr-filter-diffuselighting.cpp b/src/display/nr-filter-diffuselighting.cpp
index fcc986189..c6724e3ba 100644
--- a/src/display/nr-filter-diffuselighting.cpp
+++ b/src/display/nr-filter-diffuselighting.cpp
@@ -21,6 +21,8 @@
#include "display/nr-filter-units.h"
#include "display/nr-filter-utils.h"
#include "display/nr-light.h"
+#include "svg/svg-icc-color.h"
+#include "svg/svg-color.h"
namespace Inkscape {
namespace Filters {
@@ -126,6 +128,38 @@ void FilterDiffuseLighting::render_cairo(FilterSlot &slot)
cairo_surface_t *input = slot.getcairo(_input);
cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA);
+ double r = SP_RGBA32_R_F(lighting_color);
+ double g = SP_RGBA32_G_F(lighting_color);
+ double b = SP_RGBA32_B_F(lighting_color);
+
+#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
+
+ if (icc) {
+ guchar ru, gu, bu;
+ icc_color_to_sRGB(icc, &ru, &gu, &bu);
+ r = SP_COLOR_U_TO_F(ru);
+ g = SP_COLOR_U_TO_F(gu);
+ b = SP_COLOR_U_TO_F(bu);
+ }
+#endif
+
+ // Only alpha channel of input is used, no need to check input color_interpolation_filter value.
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+
+ // Lighting color is always defined in terms of sRGB, preconvert to linearRGB
+ // if color_interpolation_filters set to linearRGB (for efficiency assuming
+ // next filter primitive has same value of cif).
+ if( ci_fp == SP_CSS_COLOR_INTERPOLATION_LINEARRGB ) {
+ r = srgb_to_linear( r );
+ g = srgb_to_linear( g );
+ b = srgb_to_linear( b );
+ }
+ }
+ set_cairo_surface_ci(out, ci_fp );
+ guint32 color = SP_RGBA32_F_COMPOSE( r, g, b, 1.0 );
+
Geom::Rect slot_area = slot.get_slot_area();
Geom::Point p = slot_area.min();
Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb();
@@ -135,15 +169,15 @@ void FilterDiffuseLighting::render_cairo(FilterSlot &slot)
switch (light_type) {
case DISTANT_LIGHT:
ink_cairo_surface_synthesize(out,
- DiffuseDistantLight(input, light.distant, lighting_color, scale, diffuseConstant));
+ DiffuseDistantLight(input, light.distant, color, scale, diffuseConstant));
break;
case POINT_LIGHT:
ink_cairo_surface_synthesize(out,
- DiffusePointLight(input, light.point, lighting_color, trans, scale, diffuseConstant, x0, y0));
+ DiffusePointLight(input, light.point, color, trans, scale, diffuseConstant, x0, y0));
break;
case SPOT_LIGHT:
ink_cairo_surface_synthesize(out,
- DiffuseSpotLight(input, light.spot, lighting_color, trans, scale, diffuseConstant, x0, y0));
+ DiffuseSpotLight(input, light.spot, color, trans, scale, diffuseConstant, x0, y0));
break;
default: {
cairo_t *ct = cairo_create(out);
@@ -158,6 +192,10 @@ void FilterDiffuseLighting::render_cairo(FilterSlot &slot)
cairo_surface_destroy(out);
}
+void FilterDiffuseLighting::set_icc(SVGICCColor *icc_color) {
+ icc = icc_color;
+}
+
void FilterDiffuseLighting::area_enlarge(Geom::IntRect &area, Geom::Affine const & /*trans*/)
{
// TODO: support kernelUnitLength
diff --git a/src/display/nr-filter-diffuselighting.h b/src/display/nr-filter-diffuselighting.h
index 0da6cc218..043a5eb39 100644
--- a/src/display/nr-filter-diffuselighting.h
+++ b/src/display/nr-filter-diffuselighting.h
@@ -22,6 +22,7 @@
class SPFeDistantLight;
class SPFePointLight;
class SPFeSpotLight;
+struct SVGICCColor;
namespace Inkscape {
namespace Filters {
@@ -32,6 +33,7 @@ public:
static FilterPrimitive *create();
virtual ~FilterDiffuseLighting();
virtual void render_cairo(FilterSlot &slot);
+ virtual void set_icc(SVGICCColor *icc_color);
virtual void area_enlarge(Geom::IntRect &area, Geom::Affine const &trans);
virtual double complexity(Geom::Affine const &ctm);
@@ -46,6 +48,7 @@ public:
guint32 lighting_color;
private:
+ SVGICCColor *icc;
};
} /* namespace Filters */
diff --git a/src/display/nr-filter-displacement-map.cpp b/src/display/nr-filter-displacement-map.cpp
index 9b44c2302..1b979fa6c 100644
--- a/src/display/nr-filter-displacement-map.cpp
+++ b/src/display/nr-filter-displacement-map.cpp
@@ -74,6 +74,17 @@ void FilterDisplacementMap::render_cairo(FilterSlot &slot)
cairo_surface_t *texture = slot.getcairo(_input);
cairo_surface_t *map = slot.getcairo(_input2);
cairo_surface_t *out = ink_cairo_surface_create_identical(texture);
+ // color_interpolation_filters for out same as texture. See spec.
+ copy_cairo_surface_ci( texture, out );
+
+ // We may need to transform map surface to correct color interpolation space. The map surface
+ // might be used as input to another primitive but it is likely that all the primitives in a given
+ // filter use the same color interpolation space so we don't copy the map before converting.
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+ }
+ set_cairo_surface_ci( map, ci_fp );
Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb();
double scalex = scale * trans.expansionX();
diff --git a/src/display/nr-filter-flood.cpp b/src/display/nr-filter-flood.cpp
index 449255133..d1fe3e13f 100644
--- a/src/display/nr-filter-flood.cpp
+++ b/src/display/nr-filter-flood.cpp
@@ -55,11 +55,49 @@ void FilterFlood::render_cairo(FilterSlot &slot)
#endif
cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA);
- cairo_t *ct = cairo_create(out);
- cairo_set_source_rgba(ct, r, g, b, a);
- cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
- cairo_paint(ct);
- cairo_destroy(ct);
+
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+
+ // Flood color is always defined in terms of sRGB, preconvert to linearRGB
+ // if color_interpolation_filters set to linearRGB (for efficiency assuming
+ // next filter primitive has same value of cif).
+ if( ci_fp == SP_CSS_COLOR_INTERPOLATION_LINEARRGB ) {
+ r = srgb_to_linear( r );
+ g = srgb_to_linear( g );
+ b = srgb_to_linear( b );
+ }
+ }
+ set_cairo_surface_ci(out, ci_fp );
+
+ // Get filter primitive area in user units
+ Geom::Rect fp = filter_primitive_area( slot.get_units() );
+
+ // Convert to Cairo units
+ Geom::Rect fp_cairo = fp * slot.get_units().get_matrix_user2pb();
+
+ // Get area in slot (tile to fill)
+ Geom::Rect sa = slot.get_slot_area();
+
+ // Get overlap
+ Geom::OptRect optoverlap = intersect( fp_cairo, sa );
+ if( optoverlap ) {
+
+ Geom::Rect overlap = *optoverlap;
+
+ double dx = fp_cairo.min()[Geom::X] - sa.min()[Geom::X];
+ double dy = fp_cairo.min()[Geom::Y] - sa.min()[Geom::Y];
+ if( dx < 0.0 ) dx = 0.0;
+ if( dy < 0.0 ) dy = 0.0;
+
+ cairo_t *ct = cairo_create(out);
+ cairo_set_source_rgba(ct, r, g, b, a);
+ cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
+ cairo_rectangle(ct, dx, dy, overlap.width(), overlap.height() );
+ cairo_fill(ct);
+ cairo_destroy(ct);
+ }
slot.set(_output, out);
cairo_surface_destroy(out);
diff --git a/src/display/nr-filter-flood.h b/src/display/nr-filter-flood.h
index 8568502ff..9a968047d 100644
--- a/src/display/nr-filter-flood.h
+++ b/src/display/nr-filter-flood.h
@@ -14,7 +14,7 @@
#include "display/nr-filter-primitive.h"
-class SVGICCColor;
+struct SVGICCColor;
namespace Inkscape {
namespace Filters {
diff --git a/src/display/nr-filter-gaussian.cpp b/src/display/nr-filter-gaussian.cpp
index 06c2d2718..24960af78 100644
--- a/src/display/nr-filter-gaussian.cpp
+++ b/src/display/nr-filter-gaussian.cpp
@@ -102,15 +102,16 @@ static inline Tt clip_round_cast(Ts const v) {
Ts const minval = std::numeric_limits<Tt>::min();
Ts const maxval = std::numeric_limits<Tt>::max();
Tt const minval_rounded = std::numeric_limits<Tt>::min();
- Ts const maxval_rounded = std::numeric_limits<Tt>::max();
+ Tt const maxval_rounded = std::numeric_limits<Tt>::max();
if ( v < minval ) return minval_rounded;
if ( v > maxval ) return maxval_rounded;
return round_cast<Tt>(v);
}
template<typename Tt, typename Ts>
-static inline Tt clip_round_cast_varmax(Ts const v, Ts const maxval, Tt const maxval_rounded) {
+static inline Tt clip_round_cast_varmax(Ts const v, Tt const maxval_rounded) {
Ts const minval = std::numeric_limits<Tt>::min();
+ Tt const maxval = maxval_rounded;
Tt const minval_rounded = std::numeric_limits<Tt>::min();
if ( v < minval ) return minval_rounded;
if ( v > maxval ) return maxval_rounded;
@@ -145,6 +146,7 @@ static void
_make_kernel(FIRValue *const kernel, double const deviation)
{
int const scr_len = _effect_area_scr(deviation);
+ g_assert(scr_len >= 0);
double const d_sq = sqr(deviation) * 2;
double k[scr_len+1]; // This is only called for small kernel sizes (above approximately 10 coefficients the IIR filter is used)
@@ -302,10 +304,9 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2,
#define PREMUL_ALPHA_LOOP for(unsigned int c=1; c<PC; ++c)
#endif
+INK_UNUSED(num_threads); // to suppress unused argument compiler warning
#if HAVE_OPENMP
#pragma omp parallel for num_threads(num_threads)
-#else
- INK_UNUSED(num_threads);
#endif // HAVE_OPENMP
for ( int c2 = 0 ; c2 < n2 ; c2++ ) {
#if HAVE_OPENMP
@@ -338,7 +339,7 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2,
dstimg -= dstr1;
if ( PREMULTIPLIED_ALPHA ) {
dstimg[alpha_PC] = clip_round_cast<PT>(v[0][alpha_PC]);
- PREMUL_ALPHA_LOOP dstimg[c] = clip_round_cast_varmax<PT>(v[0][c], v[0][alpha_PC], dstimg[alpha_PC]);
+ PREMUL_ALPHA_LOOP dstimg[c] = clip_round_cast_varmax<PT>(v[0][c], dstimg[alpha_PC]);
} else {
for(unsigned int c=0; c<PC; c++) dstimg[c] = clip_round_cast<PT>(v[0][c]);
}
@@ -353,7 +354,7 @@ filter2D_IIR(PT *const dest, int const dstr1, int const dstr2,
dstimg -= dstr1;
if ( PREMULTIPLIED_ALPHA ) {
dstimg[alpha_PC] = clip_round_cast<PT>(v[0][alpha_PC]);
- PREMUL_ALPHA_LOOP dstimg[c] = clip_round_cast_varmax<PT>(v[0][c], v[0][alpha_PC], dstimg[alpha_PC]);
+ PREMUL_ALPHA_LOOP dstimg[c] = clip_round_cast_varmax<PT>(v[0][c], dstimg[alpha_PC]);
} else {
for(unsigned int c=0; c<PC; c++) dstimg[c] = clip_round_cast<PT>(v[0][c]);
}
@@ -373,10 +374,9 @@ filter2D_FIR(PT *const dst, int const dstr1, int const dstr2,
// Past pixels seen (to enable in-place operation)
PT history[scr_len+1][PC];
+INK_UNUSED(num_threads); // suppresses unused argument compiler warning
#if HAVE_OPENMP
#pragma omp parallel for num_threads(num_threads) private(history)
-#else
- INK_UNUSED(num_threads);
#endif // HAVE_OPENMP
for ( int c2 = 0 ; c2 < n2 ; c2++ ) {
@@ -551,6 +551,15 @@ void FilterGaussian::render_cairo(FilterSlot &slot)
cairo_surface_t *in = slot.getcairo(_input);
if (!in) return;
+ // We may need to transform input surface to correct color interpolation space. The input surface
+ // might be used as input to another primitive but it is likely that all the primitives in a given
+ // filter use the same color interpolation space so we don't copy the input before converting.
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+ }
+ set_cairo_surface_ci( in, ci_fp );
+
// zero deviation = no change in output
if (_deviation_x <= 0 && _deviation_y <= 0) {
cairo_surface_t *cp = ink_cairo_surface_copy(in);
@@ -559,12 +568,21 @@ void FilterGaussian::render_cairo(FilterSlot &slot)
return;
}
- Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb();
+ // Handle bounding box case.
+ double dx = _deviation_x;
+ double dy = _deviation_y;
+ if( slot.get_units().get_primitive_units() == SP_FILTER_UNITS_OBJECTBOUNDINGBOX ) {
+ Geom::OptRect const bbox = slot.get_units().get_item_bbox();
+ if( bbox ) {
+ dx *= (*bbox).width();
+ dy *= (*bbox).height();
+ }
+ }
- int w_orig = ink_cairo_surface_get_width(in);
- int h_orig = ink_cairo_surface_get_height(in);
- double deviation_x_orig = _deviation_x * trans.expansionX();
- double deviation_y_orig = _deviation_y * trans.expansionY();
+ Geom::Affine trans = slot.get_units().get_matrix_user2pb();
+
+ double deviation_x_orig = dx * trans.expansionX();
+ double deviation_y_orig = dy * trans.expansionY();
cairo_format_t fmt = cairo_image_surface_get_format(in);
int bytes_per_pixel = 0;
switch (fmt) {
@@ -586,6 +604,8 @@ void FilterGaussian::render_cairo(FilterSlot &slot)
int x_step = 1 << _effect_subsample_step_log2(deviation_x_orig, quality);
int y_step = 1 << _effect_subsample_step_log2(deviation_y_orig, quality);
bool resampling = x_step > 1 || y_step > 1;
+ int w_orig = ink_cairo_surface_get_width(in);
+ int h_orig = ink_cairo_surface_get_height(in);
int w_downsampled = resampling ? static_cast<int>(ceil(static_cast<double>(w_orig)/x_step))+1 : w_orig;
int h_downsampled = resampling ? static_cast<int>(ceil(static_cast<double>(h_orig)/y_step))+1 : h_orig;
double deviation_x = deviation_x_orig / x_step;
@@ -658,10 +678,14 @@ void FilterGaussian::render_cairo(FilterSlot &slot)
cairo_paint(ct);
cairo_destroy(ct);
+ set_cairo_surface_ci( upsampled, ci_fp );
+
slot.set(_output, upsampled);
cairo_surface_destroy(upsampled);
cairo_surface_destroy(downsampled);
} else {
+ set_cairo_surface_ci( downsampled, ci_fp );
+
slot.set(_output, downsampled);
cairo_surface_destroy(downsampled);
}
diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp
index 5294a5ee0..179ba0e0a 100644
--- a/src/display/nr-filter-image.cpp
+++ b/src/display/nr-filter-image.cpp
@@ -10,6 +10,7 @@
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include "display/nr-filter-image.h"
#include "document.h"
#include "sp-item.h"
#include "display/cairo-utils.h"
@@ -17,9 +18,10 @@
#include "display/drawing.h"
#include "display/drawing-item.h"
#include "display/nr-filter.h"
-#include "display/nr-filter-image.h"
#include "display/nr-filter-slot.h"
#include "display/nr-filter-units.h"
+#include "enums.h"
+#include <glibmm/fileutils.h>
namespace Inkscape {
namespace Filters {
@@ -28,7 +30,7 @@ FilterImage::FilterImage()
: SVGElem(0)
, document(0)
, feImageHref(0)
- , image_surface(0)
+ , image(0)
, broken_ref(false)
{ }
@@ -40,6 +42,7 @@ FilterImage::~FilterImage()
{
if (feImageHref)
g_free(feImageHref);
+ delete image;
}
void FilterImage::render_cairo(FilterSlot &slot)
@@ -49,22 +52,31 @@ void FilterImage::render_cairo(FilterSlot &slot)
//cairo_surface_t *input = slot.getcairo(_input);
+ // Viewport is filter primitive area (in user coordinates).
+ // Note: viewport calculation in non-trivial. Do not rely
+ // on get_matrix_primitiveunits2pb().
+ Geom::Rect vp = filter_primitive_area( slot.get_units() );
+ double feImageX = vp.min()[Geom::X];
+ double feImageY = vp.min()[Geom::Y];
+ double feImageWidth = vp.width();
+ double feImageHeight = vp.height();
+
+ // feImage is suppose to use the same parameters as a normal SVG image.
+ // If a width or height is set to zero, the image is not suppose to be displayed.
+ // This does not seem to be what Firefox or Opera does, nor does the W3C displacement
+ // filter test expect this behavior. If the width and/or height are zero, we use
+ // the width and height of the object bounding box.
Geom::Affine m = slot.get_units().get_matrix_user2filterunits().inverse();
Geom::Point bbox_00 = Geom::Point(0,0) * m;
Geom::Point bbox_w0 = Geom::Point(1,0) * m;
Geom::Point bbox_0h = Geom::Point(0,1) * m;
double bbox_width = Geom::distance(bbox_00, bbox_w0);
double bbox_height = Geom::distance(bbox_00, bbox_0h);
-
- // feImage is suppose to use the same parameters as a normal SVG image.
- // If a width or height is set to zero, the image is not suppose to be displayed.
- // This does not seem to be what Firefox or Opera does, nor does the W3C displacement
- // filter test expect this behavior. If the width and/or height are zero, we use
- // the width and height of the object bounding box.
if( feImageWidth == 0 ) feImageWidth = bbox_width;
if( feImageHeight == 0 ) feImageHeight = bbox_height;
+ // Internal image, like <use>
if (from_element) {
if (!SVGElem) return;
@@ -86,101 +98,178 @@ void FilterImage::render_cairo(FilterSlot &slot)
drawing.setRoot(ai);
Geom::Rect area = *optarea;
- Geom::Affine pu2pb = slot.get_units().get_matrix_primitiveunits2pb();
+ Geom::Affine user2pb = slot.get_units().get_matrix_user2pb();
+ /* FIXME: These variables are currently unused. Why were they calculated?
double scaleX = feImageWidth / area.width();
double scaleY = feImageHeight / area.height();
+ */
Geom::Rect sa = slot.get_slot_area();
cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
sa.width(), sa.height());
- Inkscape::DrawingContext ct(out, sa.min());
- ct.transform(pu2pb); // we are now in primitive units
- ct.translate(feImageX, feImageY);
- ct.scale(scaleX, scaleY);
+ Inkscape::DrawingContext dc(out, sa.min());
+ dc.transform(user2pb); // we are now in primitive units
+ dc.translate(feImageX, feImageY);
+// dc.scale(scaleX, scaleY); No scaling should be done
Geom::IntRect render_rect = area.roundOutwards();
- ct.translate(render_rect.min());
+// dc.translate(render_rect.min()); This seems incorrect
// Update to renderable state
drawing.update(render_rect);
- drawing.render(ct, render_rect);
+ drawing.render(dc, render_rect);
SVGElem->invoke_hide(key);
+ // For the moment, we'll assume that any image is in sRGB color space
+ set_cairo_surface_ci(out, SP_CSS_COLOR_INTERPOLATION_SRGB);
+
slot.set(_output, out);
cairo_surface_destroy(out);
return;
}
+ // 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 && document->base,
- * 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());
+ if (broken_ref) {
+ return;
}
+ 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());
+ // For the moment, we'll assume that any image is in sRGB color space
+ // set_cairo_surface_ci(out, SP_CSS_COLOR_INTERPOLATION_SRGB);
+ // This seemed like a sensible thing to do but it breaks filters-displace-01-f.svg
+
cairo_t *ct = cairo_create(out);
cairo_translate(ct, -sa.min()[Geom::X], -sa.min()[Geom::Y]);
- // now ct is in pb coordinates
- ink_cairo_transform(ct, slot.get_units().get_matrix_primitiveunits2pb());
+
+ // now ct is in pb coordinates, note the feWidth etc. are in user units
+ ink_cairo_transform(ct, slot.get_units().get_matrix_user2pb());
+
// now ct is in the coordinates of feImageX etc.
- // TODO: add preserveAspectRatio support here
+ // Now that we have the viewport, we must map image inside.
+ // Partially copied from sp-image.cpp.
+
+ // Do nothing if preserveAspectRatio is "none".
+ if( aspect_align != SP_ASPECT_NONE ) {
+
+ // Check aspect ratio of image vs. viewport
+ double feAspect = feImageHeight/feImageWidth;
+ double aspect = (double)image->height()/(double)image->width();
+ bool ratio = (feAspect < aspect);
+
+ double ax, ay; // Align side
+ switch( aspect_align ) {
+ case SP_ASPECT_XMIN_YMIN:
+ ax = 0.0;
+ ay = 0.0;
+ break;
+ case SP_ASPECT_XMID_YMIN:
+ ax = 0.5;
+ ay = 0.0;
+ break;
+ case SP_ASPECT_XMAX_YMIN:
+ ax = 1.0;
+ ay = 0.0;
+ break;
+ case SP_ASPECT_XMIN_YMID:
+ ax = 0.0;
+ ay = 0.5;
+ break;
+ case SP_ASPECT_XMID_YMID:
+ ax = 0.5;
+ ay = 0.5;
+ break;
+ case SP_ASPECT_XMAX_YMID:
+ ax = 1.0;
+ ay = 0.5;
+ break;
+ case SP_ASPECT_XMIN_YMAX:
+ ax = 0.0;
+ ay = 1.0;
+ break;
+ case SP_ASPECT_XMID_YMAX:
+ ax = 0.5;
+ ay = 1.0;
+ break;
+ case SP_ASPECT_XMAX_YMAX:
+ ax = 1.0;
+ ay = 1.0;
+ break;
+ default:
+ ax = 0.0;
+ ay = 0.0;
+ break;
+ }
- double scaleX = feImageWidth / image->get_width();
- double scaleY = feImageHeight / image->get_height();
+ if( aspect_clip == SP_ASPECT_SLICE ) {
+ // image clipped by viewbox
+
+ if( ratio ) {
+ // clip top/bottom
+ feImageY -= ay * (feImageWidth * aspect - feImageHeight);
+ feImageHeight = feImageWidth * aspect;
+ } else {
+ // clip sides
+ feImageX -= ax * (feImageHeight / aspect - feImageWidth);
+ feImageWidth = feImageHeight / aspect;
+ }
+
+ } else {
+ // image fits into viewbox
+
+ if( ratio ) {
+ // fit to height
+ feImageX += ax * (feImageWidth - feImageHeight / aspect );
+ feImageWidth = feImageHeight / aspect;
+ } else {
+ // fit to width
+ feImageY += ay * (feImageHeight - feImageWidth * aspect);
+ feImageHeight = feImageWidth * aspect;
+ }
+ }
+ }
+
+ double scaleX = feImageWidth / image->width();
+ double scaleY = feImageHeight / image->height();
cairo_translate(ct, feImageX, feImageY);
cairo_scale(ct, scaleX, scaleY);
@@ -203,13 +292,12 @@ double FilterImage::complexity(Geom::Affine const &)
}
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;
}
@@ -217,13 +305,6 @@ void FilterImage::set_document(SPDocument *doc){
document = doc;
}
-void FilterImage::set_region(SVGLength x, SVGLength y, SVGLength width, SVGLength height) {
- feImageX=x.computed;
- feImageY=y.computed;
- feImageWidth=width.computed;
- feImageHeight=height.computed;
-}
-
void FilterImage::set_align( unsigned int align ) {
aspect_align = align;
}
diff --git a/src/display/nr-filter-image.h b/src/display/nr-filter-image.h
index c0b9d6e81..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;
@@ -35,7 +35,6 @@ public:
void set_document( SPDocument *document );
void set_href(const gchar *href);
- void set_region(SVGLength x, SVGLength y, SVGLength width, SVGLength height);
void set_align( unsigned int align );
void set_clip( unsigned int clip );
bool from_element;
@@ -44,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-merge.cpp b/src/display/nr-filter-merge.cpp
index f1fbd7d33..2b9a24f25 100644
--- a/src/display/nr-filter-merge.cpp
+++ b/src/display/nr-filter-merge.cpp
@@ -31,7 +31,12 @@ FilterMerge::~FilterMerge()
void FilterMerge::render_cairo(FilterSlot &slot)
{
- if (_input_image.size() == 0) return;
+ if (_input_image.empty()) return;
+
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+ }
// output is RGBA if at least one input is RGBA
bool rgba32 = false;
@@ -40,6 +45,7 @@ void FilterMerge::render_cairo(FilterSlot &slot)
cairo_surface_t *in = slot.getcairo(*i);
if (cairo_surface_get_content(in) == CAIRO_CONTENT_COLOR_ALPHA) {
out = ink_cairo_surface_create_identical(in);
+ set_cairo_surface_ci( out, ci_fp );
rgba32 = true;
break;
}
@@ -52,6 +58,8 @@ void FilterMerge::render_cairo(FilterSlot &slot)
for (std::vector<int>::iterator i = _input_image.begin(); i != _input_image.end(); ++i) {
cairo_surface_t *in = slot.getcairo(*i);
+
+ set_cairo_surface_ci( in, ci_fp );
cairo_set_source_surface(out_ct, in, 0, 0);
cairo_paint(out_ct);
}
diff --git a/src/display/nr-filter-morphology.cpp b/src/display/nr-filter-morphology.cpp
index 18f99cdcd..b6e5052e1 100644
--- a/src/display/nr-filter-morphology.cpp
+++ b/src/display/nr-filter-morphology.cpp
@@ -69,6 +69,7 @@ void morphologicalFilter1D(cairo_surface_t * const input, cairo_surface_t * cons
int limit = w * h;
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
int numOfThreads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256);
+ (void) numOfThreads; // suppress unused variable warning
#pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(numOfThreads)
#endif // HAVE_OPENMP
for (int i = 0; i < h; ++i) {
@@ -164,6 +165,7 @@ void FilterMorphology::render_cairo(FilterSlot &slot)
if (xradius == 0.0 || yradius == 0.0) {
// output is transparent black
cairo_surface_t *out = ink_cairo_surface_create_identical(input);
+ copy_cairo_surface_ci(input, out);
slot.set(_output, out);
cairo_surface_destroy(out);
return;
@@ -192,6 +194,9 @@ void FilterMorphology::render_cairo(FilterSlot &slot)
cairo_surface_t *out = ink_cairo_surface_create_identical(interm);
+ // color_interpolation_filters for out same as input. See spec (DisplacementMap).
+ copy_cairo_surface_ci(input, out);
+
if (Operator == MORPHOLOGY_OPERATOR_DILATE) {
if (bpp == 1) {
morphologicalFilter1D< std::greater<unsigned char>, Geom::Y, 1 >(interm, out, yr);
diff --git a/src/display/nr-filter-offset.cpp b/src/display/nr-filter-offset.cpp
index 833f6ecc9..af1081abe 100644
--- a/src/display/nr-filter-offset.cpp
+++ b/src/display/nr-filter-offset.cpp
@@ -35,10 +35,25 @@ void FilterOffset::render_cairo(FilterSlot &slot)
{
cairo_surface_t *in = slot.getcairo(_input);
cairo_surface_t *out = ink_cairo_surface_create_identical(in);
+ // color_interpolation_filters for out same as in. See spec (DisplacementMap).
+ copy_cairo_surface_ci(in, out);
+
cairo_t *ct = cairo_create(out);
- Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb();
- Geom::Point offset(dx, dy);
+ // Handle bounding box case
+ double x = dx;
+ double y = dy;
+ if( slot.get_units().get_primitive_units() == SP_FILTER_UNITS_OBJECTBOUNDINGBOX ) {
+ Geom::OptRect bbox = slot.get_units().get_item_bbox();
+ if( bbox ) {
+ x *= (*bbox).width();
+ y *= (*bbox).height();
+ }
+ }
+
+ Geom::Affine trans = slot.get_units().get_matrix_user2pb();
+
+ Geom::Point offset(x, y);
offset *= trans;
offset[X] -= trans[4];
offset[Y] -= trans[5];
diff --git a/src/display/nr-filter-primitive.cpp b/src/display/nr-filter-primitive.cpp
index c6bd8a74e..b065ac445 100644
--- a/src/display/nr-filter-primitive.cpp
+++ b/src/display/nr-filter-primitive.cpp
@@ -15,6 +15,13 @@
#include "display/nr-filter-types.h"
#include "svg/svg-length.h"
+#include "inkscape.h"
+#include "desktop.h"
+#include "desktop-handles.h"
+#include "document.h"
+#include "sp-root.h"
+#include "style.h"
+
namespace Inkscape {
namespace Filters {
@@ -39,11 +46,14 @@ FilterPrimitive::FilterPrimitive()
_subregion_y.unset(SVGLength::PERCENT, 0, 0);
_subregion_width.unset(SVGLength::PERCENT, 1, 0);
_subregion_height.unset(SVGLength::PERCENT, 1, 0);
+
+ _style = NULL;
}
FilterPrimitive::~FilterPrimitive()
{
- // Nothing to do here
+ if(_style)
+ sp_style_unref(_style);
}
void FilterPrimitive::render_cairo(FilterSlot &slot)
@@ -100,67 +110,73 @@ void FilterPrimitive::set_subregion(SVGLength const &x, SVGLength const &y,
Geom::Rect FilterPrimitive::filter_primitive_area(FilterUnits const &units)
{
- Geom::OptRect bb = units.get_item_bbox();
- Geom::OptRect fa = units.get_filter_area();
-
- /* Update computed values for ex, em, %. For %, assumes primitive unit is objectBoundingBox. */
- /* TODO: fetch somehow the object ex and em lengths; 12, 6 are just dummy values. */
- double len_x = bb->width();
- double len_y = bb->height();
- _subregion_x.update(12, 6, len_x);
- _subregion_y.update(12, 6, len_y);
- _subregion_width.update(12, 6, len_x);
- _subregion_height.update(12, 6, len_y);
+ Geom::OptRect const bb_opt = units.get_item_bbox();
+ Geom::OptRect const fa_opt = units.get_filter_area();
+ Geom::Rect bb;
+ Geom::Rect fa;
+ if (!bb_opt || !fa_opt) {
+ return Geom::Rect (Geom::Point(0.,0.), Geom::Point(0.,0.));
+ } else {
+ bb = *bb_opt;
+ fa = *fa_opt;
+ }
+
// x, y, width, and height are independently defined (i.e. one can be defined, by default, to
- // the filter area while another is defined relative to the bounding box). It is better to keep
- // track of them separately and then compose the Rect at the end.
+ // the filter area (via default value ) while another is defined relative to the bounding
+ // box). It is better to keep track of them separately and then compose the Rect at the end.
double x = 0;
double y = 0;
double width = 0;
double height = 0;
// If subregion not set, by special case use filter region.
- if( !_subregion_x._set ) x = fa->min()[X];
- if( !_subregion_y._set ) y = fa->min()[Y];
- if( !_subregion_width._set ) width = fa->width();
- if( !_subregion_height._set ) height = fa->height();
+ if( !_subregion_x._set ) x = fa.min()[X];
+ if( !_subregion_y._set ) y = fa.min()[Y];
+ if( !_subregion_width._set ) width = fa.width();
+ if( !_subregion_height._set ) height = fa.height();
if( units.get_primitive_units() == SP_FILTER_UNITS_OBJECTBOUNDINGBOX ) {
+
+ // Update computed values for ex, em, %.
+ // For %, assumes primitive unit is objectBoundingBox.
+ // TODO: fetch somehow the object ex and em lengths; 12, 6 are just dummy values.
+ double len_x = bb.width();
+ double len_y = bb.height();
+ _subregion_x.update(12, 6, len_x);
+ _subregion_y.update(12, 6, len_y);
+ _subregion_width.update(12, 6, len_x);
+ _subregion_height.update(12, 6, len_y);
+
// Values are in terms of fraction of bounding box.
- if( _subregion_x._set && _subregion_x.unit != SVGLength::PERCENT ) x = bb->min()[X] + bb->width() * _subregion_x.value;
- if( _subregion_y._set && _subregion_y.unit != SVGLength::PERCENT ) y = bb->min()[Y] + bb->height() * _subregion_y.value;
- if( _subregion_width._set && _subregion_width.unit != SVGLength::PERCENT ) width = bb->width() * _subregion_width.value;
- if( _subregion_height._set && _subregion_height.unit != SVGLength::PERCENT ) height = bb->height() * _subregion_height.value;
- // Values are in terms of percent
- if( _subregion_x._set && _subregion_x.unit == SVGLength::PERCENT ) x = bb->min()[X] + _subregion_x.computed;
- if( _subregion_y._set && _subregion_y.unit == SVGLength::PERCENT ) y = bb->min()[Y] + _subregion_y.computed;
- if( _subregion_width._set && _subregion_width.unit == SVGLength::PERCENT ) width = _subregion_width.computed;
- if( _subregion_height._set && _subregion_height.unit == SVGLength::PERCENT ) height = _subregion_height.computed;
+ if( _subregion_x._set && (_subregion_x.unit != SVGLength::PERCENT) ) x = bb.min()[X] + bb.width() * _subregion_x.value;
+ if( _subregion_y._set && (_subregion_y.unit != SVGLength::PERCENT) ) y = bb.min()[Y] + bb.height() * _subregion_y.value;
+ if( _subregion_width._set && (_subregion_width.unit != SVGLength::PERCENT) ) width = bb.width() * _subregion_width.value;
+ if( _subregion_height._set && (_subregion_height.unit != SVGLength::PERCENT) ) height = bb.height() * _subregion_height.value;
+ // Values are in terms of percent
+ if( _subregion_x._set && (_subregion_x.unit == SVGLength::PERCENT) ) x = bb.min()[X] + _subregion_x.computed;
+ if( _subregion_y._set && (_subregion_y.unit == SVGLength::PERCENT) ) y = bb.min()[Y] + _subregion_y.computed;
+ if( _subregion_width._set && (_subregion_width.unit == SVGLength::PERCENT) ) width = _subregion_width.computed;
+ if( _subregion_height._set && (_subregion_height.unit == SVGLength::PERCENT) ) height = _subregion_height.computed;
} else {
- // Values are in terms of user space coordinates or percent of viewbox (yuck!),
- // which is usually the size of SVG drawing. Default.
- if( _subregion_x._set && _subregion_x.unit != SVGLength::PERCENT ) x = _subregion_x.computed;
- if( _subregion_y._set && _subregion_y.unit != SVGLength::PERCENT ) y = _subregion_y.computed;
- if( _subregion_width._set && _subregion_width.unit != SVGLength::PERCENT ) width = _subregion_width.computed;
- if( _subregion_height._set && _subregion_height.unit != SVGLength::PERCENT ) height = _subregion_height.computed;
- // TODO: add percent of viewport TEMPORARY HACK FOR TESTING...
- if( _subregion_x._set && _subregion_x.unit == SVGLength::PERCENT ) x = _subregion_x.value * 480; // viewport_x
- if( _subregion_y._set && _subregion_y.unit == SVGLength::PERCENT ) y = _subregion_y.value * 360;
- if( _subregion_width._set && _subregion_width.unit == SVGLength::PERCENT ) width = _subregion_width.value * 480;
- if( _subregion_height._set && _subregion_height.unit == SVGLength::PERCENT ) height = _subregion_height.value * 360;
+ // Values are in terms of user space coordinates or percent of viewport (already calculated in sp-filter-primitive.cpp).
+ if( _subregion_x._set ) x = _subregion_x.computed;
+ if( _subregion_y._set ) y = _subregion_y.computed;
+ if( _subregion_width._set ) width = _subregion_width.computed;
+ if( _subregion_height._set ) height = _subregion_height.computed;
}
- Geom::Point minp, maxp;
- minp[X] = x;
- minp[Y] = y;
- maxp[X] = x + width;
- maxp[Y] = y + height;
+ return Geom::Rect (Geom::Point(x,y), Geom::Point(x + width, y + height));
+}
- Geom::Rect area(minp, maxp);
- return area;
+void FilterPrimitive::setStyle(SPStyle *style)
+{
+ if (style) sp_style_ref(style);
+ if (_style) sp_style_unref(_style);
+ _style = style;
}
+
} /* namespace Filters */
} /* namespace Inkscape */
diff --git a/src/display/nr-filter-primitive.h b/src/display/nr-filter-primitive.h
index da2097156..94bd6abb0 100644
--- a/src/display/nr-filter-primitive.h
+++ b/src/display/nr-filter-primitive.h
@@ -16,6 +16,8 @@
#include "display/nr-filter-types.h"
#include "svg/svg-length.h"
+struct SPStyle;
+
namespace Inkscape {
namespace Filters {
@@ -113,6 +115,11 @@ public:
*/
virtual bool can_handle_affine(Geom::Affine const &) { return false; }
+ /**
+ * Sets style for access to properties used by filter primitives.
+ */
+ void setStyle(SPStyle *style);
+
protected:
int _input;
int _output;
@@ -122,6 +129,8 @@ protected:
SVGLength _subregion_y;
SVGLength _subregion_width;
SVGLength _subregion_height;
+
+ SPStyle *_style;
};
diff --git a/src/display/nr-filter-skeleton.cpp b/src/display/nr-filter-skeleton.cpp
index 0c455a818..86ab8141e 100644
--- a/src/display/nr-filter-skeleton.cpp
+++ b/src/display/nr-filter-skeleton.cpp
@@ -42,7 +42,8 @@ FilterSkeleton::~FilterSkeleton()
void FilterSkeleton::render_cairo(FilterSlot &slot) {
cairo_surface_t *in = slot.getcairo(_input);
cairo_surface_t *out = ink_cairo_surface_create_identical(in);
- cairo_t *ct = cairo_create(out);
+
+// cairo_t *ct = cairo_create(out);
// cairo_set_source_surface(ct, in, offset[X], offset[Y]);
// cairo_paint(ct);
diff --git a/src/display/nr-filter-slot.cpp b/src/display/nr-filter-slot.cpp
index 4f7a8849e..e4c2f048e 100644
--- a/src/display/nr-filter-slot.cpp
+++ b/src/display/nr-filter-slot.cpp
@@ -25,13 +25,13 @@
namespace Inkscape {
namespace Filters {
-FilterSlot::FilterSlot(DrawingItem *item, DrawingContext *bgct,
+FilterSlot::FilterSlot(DrawingItem *item, DrawingContext *bgdc,
DrawingContext &graphic, FilterUnits const &u)
: _item(item)
, _source_graphic(graphic.rawTarget())
- , _background_ct(bgct ? bgct->raw() : NULL)
+ , _background_ct(bgdc ? bgdc->raw() : NULL)
, _source_graphic_area(graphic.targetLogicalBounds().roundOutwards()) // fixme
- , _background_area(bgct ? bgct->targetLogicalBounds().roundOutwards() : Geom::IntRect()) // fixme
+ , _background_area(bgdc ? bgdc->targetLogicalBounds().roundOutwards() : Geom::IntRect()) // fixme
, _units(u)
, _last_out(NR_FILTER_SOURCEGRAPHIC)
, filterquality(FILTER_QUALITY_BEST)
@@ -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;
@@ -177,9 +181,10 @@ cairo_surface_t *FilterSlot::_get_transformed_background()
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;
}
@@ -188,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-slot.h b/src/display/nr-filter-slot.h
index 805027bfe..f3c98b8d9 100644
--- a/src/display/nr-filter-slot.h
+++ b/src/display/nr-filter-slot.h
@@ -28,7 +28,7 @@ namespace Filters {
class FilterSlot {
public:
/** Creates a new FilterSlot object. */
- FilterSlot(DrawingItem *item, DrawingContext *bgct,
+ FilterSlot(DrawingItem *item, DrawingContext *bgdc,
DrawingContext &graphic, FilterUnits const &u);
/** Destroys the FilterSlot object and all its contents */
virtual ~FilterSlot();
diff --git a/src/display/nr-filter-specularlighting.cpp b/src/display/nr-filter-specularlighting.cpp
index 0242754eb..2ce02adee 100644
--- a/src/display/nr-filter-specularlighting.cpp
+++ b/src/display/nr-filter-specularlighting.cpp
@@ -21,6 +21,8 @@
#include "display/nr-filter-units.h"
#include "display/nr-filter-utils.h"
#include "display/nr-light.h"
+#include "svg/svg-icc-color.h"
+#include "svg/svg-color.h"
namespace Inkscape {
namespace Filters {
@@ -139,6 +141,38 @@ void FilterSpecularLighting::render_cairo(FilterSlot &slot)
cairo_surface_t *input = slot.getcairo(_input);
cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA);
+ double r = SP_RGBA32_R_F(lighting_color);
+ double g = SP_RGBA32_G_F(lighting_color);
+ double b = SP_RGBA32_B_F(lighting_color);
+
+#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
+
+ if (icc) {
+ guchar ru, gu, bu;
+ icc_color_to_sRGB(icc, &ru, &gu, &bu);
+ r = SP_COLOR_U_TO_F(ru);
+ g = SP_COLOR_U_TO_F(gu);
+ b = SP_COLOR_U_TO_F(bu);
+ }
+#endif
+
+ // Only alpha channel of input is used, no need to check input color_interpolation_filter value.
+ SPColorInterpolation ci_fp = SP_CSS_COLOR_INTERPOLATION_AUTO;
+ if( _style ) {
+ ci_fp = (SPColorInterpolation)_style->color_interpolation_filters.computed;
+
+ // Lighting color is always defined in terms of sRGB, preconvert to linearRGB
+ // if color_interpolation_filters set to linearRGB (for efficiency assuming
+ // next filter primitive has same value of cif).
+ if( ci_fp == SP_CSS_COLOR_INTERPOLATION_LINEARRGB ) {
+ r = srgb_to_linear( r );
+ g = srgb_to_linear( g );
+ b = srgb_to_linear( b );
+ }
+ }
+ set_cairo_surface_ci(out, ci_fp );
+ guint32 color = SP_RGBA32_F_COMPOSE( r, g, b, 1.0 );
+
Geom::Affine trans = slot.get_units().get_matrix_primitiveunits2pb();
Geom::Point p = slot.get_slot_area().min();
double x0 = p[Geom::X];
@@ -150,15 +184,15 @@ void FilterSpecularLighting::render_cairo(FilterSlot &slot)
switch (light_type) {
case DISTANT_LIGHT:
ink_cairo_surface_synthesize(out,
- SpecularDistantLight(input, light.distant, lighting_color, scale, ks, se));
+ SpecularDistantLight(input, light.distant, color, scale, ks, se));
break;
case POINT_LIGHT:
ink_cairo_surface_synthesize(out,
- SpecularPointLight(input, light.point, lighting_color, trans, scale, ks, se, x0, y0));
+ SpecularPointLight(input, light.point, color, trans, scale, ks, se, x0, y0));
break;
case SPOT_LIGHT:
ink_cairo_surface_synthesize(out,
- SpecularSpotLight(input, light.spot, lighting_color, trans, scale, ks, se, x0, y0));
+ SpecularSpotLight(input, light.spot, color, trans, scale, ks, se, x0, y0));
break;
default: {
cairo_t *ct = cairo_create(out);
@@ -173,6 +207,10 @@ void FilterSpecularLighting::render_cairo(FilterSlot &slot)
cairo_surface_destroy(out);
}
+void FilterSpecularLighting::set_icc(SVGICCColor *icc_color) {
+ icc = icc_color;
+}
+
void FilterSpecularLighting::area_enlarge(Geom::IntRect &area, Geom::Affine const & /*trans*/)
{
// TODO: support kernelUnitLength
diff --git a/src/display/nr-filter-specularlighting.h b/src/display/nr-filter-specularlighting.h
index 33ea17a87..c57e3a9ff 100644
--- a/src/display/nr-filter-specularlighting.h
+++ b/src/display/nr-filter-specularlighting.h
@@ -20,6 +20,7 @@
class SPFeDistantLight;
class SPFePointLight;
class SPFeSpotLight;
+struct SVGICCColor;
namespace Inkscape {
namespace Filters {
@@ -33,6 +34,7 @@ public:
virtual ~FilterSpecularLighting();
virtual void render_cairo(FilterSlot &slot);
+ virtual void set_icc(SVGICCColor *icc_color);
virtual void area_enlarge(Geom::IntRect &area, Geom::Affine const &trans);
virtual double complexity(Geom::Affine const &ctm);
@@ -48,6 +50,7 @@ public:
guint32 lighting_color;
private:
+ SVGICCColor *icc;
};
} /* namespace Filters */
diff --git a/src/display/nr-filter-turbulence.cpp b/src/display/nr-filter-turbulence.cpp
index bce532f21..a2a8c5756 100644
--- a/src/display/nr-filter-turbulence.cpp
+++ b/src/display/nr-filter-turbulence.cpp
@@ -283,7 +283,16 @@ private:
// other constants
static int const BSize = 0x100;
static int const BMask = 0xff;
- static double const PerlinOffset = 4096.0;
+
+#ifdef CPP11 // GCC 4.6.1 (currently used on Windows) does not correctly set __cplusplus in C++11 mode, so configure with -DCPP11 to make this work in C++11 mode
+ static double constexpr PerlinOffset = 4096.0;
+#else
+#if (__cplusplus < 201103L)
+ static double const PerlinOffset;
+#else
+ static double constexpr PerlinOffset = 4096.0;
+#endif
+#endif
Geom::Rect _tile;
Geom::Point _baseFreq;
@@ -300,6 +309,10 @@ private:
bool _fractalnoise;
};
+#if !defined(CPP11) && __cplusplus < 201103L
+ double const TurbulenceGenerator::PerlinOffset = 4096.0;
+#endif
+
FilterTurbulence::FilterTurbulence()
: gen(new TurbulenceGenerator())
, XbaseFrequency(0)
@@ -375,6 +388,11 @@ void FilterTurbulence::render_cairo(FilterSlot &slot)
cairo_surface_t *input = slot.getcairo(_input);
cairo_surface_t *out = ink_cairo_surface_create_same_size(input, CAIRO_CONTENT_COLOR_ALPHA);
+ // color_interpolation_filter is determined by CSS value (see spec. Turbulence).
+ if( _style ) {
+ set_cairo_surface_ci(out, (SPColorInterpolation)_style->color_interpolation_filters.computed );
+ }
+
if (!gen->ready()) {
Geom::Point ta(fTileX, fTileY);
Geom::Point tb(fTileX + fTileWidth, fTileY + fTileHeight);
diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp
index f580a5044..90b233fbc 100644
--- a/src/display/nr-filter.cpp
+++ b/src/display/nr-filter.cpp
@@ -38,6 +38,7 @@
#include "display/nr-filter-tile.h"
#include "display/nr-filter-turbulence.h"
+#include "display/cairo-utils.h"
#include "display/drawing.h"
#include "display/drawing-item.h"
#include "display/drawing-context.h"
@@ -97,7 +98,7 @@ Filter::~Filter()
}
-int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, DrawingContext *bgct)
+int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, DrawingContext *bgdc)
{
if (_primitive.empty()) {
// when no primitives are defined, clear source graphic
@@ -149,7 +150,7 @@ int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, D
}
}
- FilterSlot slot(const_cast<Inkscape::DrawingItem*>(item), bgct, graphic, units);
+ FilterSlot slot(const_cast<Inkscape::DrawingItem*>(item), bgdc, graphic, units);
slot.set_quality(filterquality);
slot.set_blurquality(blurquality);
@@ -159,6 +160,10 @@ int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, D
Geom::Point origin = graphic.targetLogicalBounds().min();
cairo_surface_t *result = slot.get_result(_output_slot);
+
+ // Assume for the moment that we paint the filter in sRGB
+ set_cairo_surface_ci( result, SP_CSS_COLOR_INTERPOLATION_SRGB );
+
graphic.setSource(result, origin[Geom::X], origin[Geom::Y]);
graphic.setOperator(CAIRO_OPERATOR_SOURCE);
graphic.paint();
@@ -214,30 +219,22 @@ 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);
- 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;
- double len_x = bbox ? bbox->width() : 0;
- double len_y = bbox ? bbox->height() : 0;
- /* TODO: fetch somehow the object ex and em lengths */
- _region_x.update(12, 6, len_x);
- _region_y.update(12, 6, len_y);
- _region_width.update(12, 6, len_x);
- _region_height.update(12, 6, len_y);
+
if (_filter_units == SP_FILTER_UNITS_OBJECTBOUNDINGBOX) {
+
+ double len_x = bbox ? bbox->width() : 0;
+ double len_y = bbox ? bbox->height() : 0;
+ /* TODO: fetch somehow the object ex and em lengths */
+
+ // Update for em, ex, and % values
+ _region_x.update(12, 6, len_x);
+ _region_y.update(12, 6, len_y);
+ _region_width.update(12, 6, len_x);
+ _region_height.update(12, 6, len_y);
+
if (!bbox) return Geom::OptRect();
if (_region_x.unit == SVGLength::PERCENT) {
@@ -262,7 +259,7 @@ Geom::OptRect Filter::filter_effect_area(Geom::OptRect const &bbox)
maxp[Y] = minp[Y] + _region_height.computed * len_y;
}
} else if (_filter_units == SP_FILTER_UNITS_USERSPACEONUSE) {
- /* TODO: make sure bbox and fe region are in same coordinate system */
+ // Region already set in sp-filter.cpp
minp[X] = _region_x.computed;
maxp[X] = minp[X] + _region_width.computed;
minp[Y] = _region_y.computed;
@@ -270,7 +267,9 @@ Geom::OptRect Filter::filter_effect_area(Geom::OptRect const &bbox)
} else {
g_warning("Error in Inkscape::Filters::Filter::filter_effect_area: unrecognized value of _filter_units");
}
+
Geom::OptRect area(minp, maxp);
+ // std::cout << "Filter::filter_effect_area: area: " << *area << std::endl;
return area;
}
diff --git a/src/display/nr-filter.h b/src/display/nr-filter.h
index d53005c5d..f9dcf1d84 100644
--- a/src/display/nr-filter.h
+++ b/src/display/nr-filter.h
@@ -28,12 +28,12 @@ namespace Filters {
class Filter {
public:
- /** Given background state from @a bgct and an intermediate rendering from the surface
+ /** Given background state from @a bgdc and an intermediate rendering from the surface
* backing @a graphic, modify the contents of the surface backing @a graphic to represent
* the results of filter rendering. @a bgarea and @a area specify bounding boxes
* of both surfaces in world coordinates; Cairo contexts are assumed to be in default state
* (0,0 = surface origin, no path, OVER operator) */
- int render(Inkscape::DrawingItem const *item, DrawingContext &graphic, DrawingContext *bgct);
+ int render(Inkscape::DrawingItem const *item, DrawingContext &graphic, DrawingContext *bgdc);
/**
* Creates a new filter primitive under this filter object.
@@ -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 86102f9e8..125d0c6d6 100644
--- a/src/display/nr-style.cpp
+++ b/src/display/nr-style.cpp
@@ -54,13 +54,35 @@ 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)
+{
+#ifdef WITH_SVG2
+ paint_order_layer[0] = PAINT_ORDER_NORMAL;
+#endif
+}
NRStyle::~NRStyle()
{
if (fill_pattern) cairo_pattern_destroy(fill_pattern);
if (stroke_pattern) cairo_pattern_destroy(stroke_pattern);
- if (dash) delete dash;
+ if (dash){
+ delete [] dash;
+ }
+ fill.clear();
+ stroke.clear();
}
void NRStyle::set(SPStyle *style)
@@ -126,31 +148,99 @@ void NRStyle::set(SPStyle *style)
}
miter_limit = style->stroke_miterlimit.value;
- if (dash) delete [] dash;
+ if (dash){
+ delete [] dash;
+ }
- n_dash = style->stroke_dash.n_dash;
+ n_dash = style->stroke_dasharray.values.size();
if (n_dash != 0) {
- dash_offset = style->stroke_dash.offset;
+ dash_offset = style->stroke_dashoffset.value;
dash = new double[n_dash];
for (unsigned int i = 0; i < n_dash; ++i) {
- dash[i] = style->stroke_dash.dash[i];
+ dash[i] = style->stroke_dasharray.values[i];
}
} else {
dash_offset = 0.0;
dash = NULL;
}
+
+#ifdef WITH_SVG2
+ for( unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i) {
+ switch (style->paint_order.layer[i]) {
+ case SP_CSS_PAINT_ORDER_NORMAL:
+ paint_order_layer[i]=PAINT_ORDER_NORMAL;
+ break;
+ case SP_CSS_PAINT_ORDER_FILL:
+ paint_order_layer[i]=PAINT_ORDER_FILL;
+ break;
+ case SP_CSS_PAINT_ORDER_STROKE:
+ paint_order_layer[i]=PAINT_ORDER_STROKE;
+ break;
+ case SP_CSS_PAINT_ORDER_MARKER:
+ paint_order_layer[i]=PAINT_ORDER_MARKER;
+ break;
+ }
+ }
+#endif
+
+ 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();
}
-bool NRStyle::prepareFill(Inkscape::DrawingContext &ct, Geom::OptRect const &paintbox)
+bool NRStyle::prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox)
{
// update fill pattern
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, dc.raw(), paintbox, fill.opacity);
+ fill_pattern = fill.server->pattern_new(dc.raw(), paintbox, fill.opacity);
+
+ } break;
case PAINT_COLOR: {
SPColor const &c = fill.color;
fill_pattern = cairo_pattern_create_rgba(
@@ -163,19 +253,21 @@ bool NRStyle::prepareFill(Inkscape::DrawingContext &ct, Geom::OptRect const &pai
return true;
}
-void NRStyle::applyFill(Inkscape::DrawingContext &ct)
+void NRStyle::applyFill(Inkscape::DrawingContext &dc)
{
- ct.setSource(fill_pattern);
- ct.setFillRule(fill_rule);
+ dc.setSource(fill_pattern);
+ dc.setFillRule(fill_rule);
}
-bool NRStyle::prepareStroke(Inkscape::DrawingContext &ct, Geom::OptRect const &paintbox)
+bool NRStyle::prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox)
{
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, dc.raw(), paintbox, stroke.opacity);
+ stroke_pattern = stroke.server->pattern_new(dc.raw(), paintbox, stroke.opacity);
+
+ } break;
case PAINT_COLOR: {
SPColor const &c = stroke.color;
stroke_pattern = cairo_pattern_create_rgba(
@@ -188,14 +280,14 @@ bool NRStyle::prepareStroke(Inkscape::DrawingContext &ct, Geom::OptRect const &p
return true;
}
-void NRStyle::applyStroke(Inkscape::DrawingContext &ct)
+void NRStyle::applyStroke(Inkscape::DrawingContext &dc)
{
- ct.setSource(stroke_pattern);
- ct.setLineWidth(stroke_width);
- ct.setLineCap(line_cap);
- ct.setLineJoin(line_join);
- ct.setMiterLimit(miter_limit);
- cairo_set_dash(ct.raw(), dash, n_dash, dash_offset); // fixme
+ dc.setSource(stroke_pattern);
+ dc.setLineWidth(stroke_width);
+ dc.setLineCap(line_cap);
+ dc.setLineJoin(line_join);
+ dc.setMiterLimit(miter_limit);
+ cairo_set_dash(dc.raw(), dash, n_dash, dash_offset); // fixme
}
void NRStyle::update()
@@ -216,4 +308,4 @@ void NRStyle::update()
fill-column:99
End:
*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/display/nr-style.h b/src/display/nr-style.h
index ce154cec0..717cda899 100644
--- a/src/display/nr-style.h
+++ b/src/display/nr-style.h
@@ -16,9 +16,9 @@
#include <2geom/rect.h>
#include "color.h"
-class SPColor;
class SPPaintServer;
-class SPStyle;
+struct SPStyle;
+
namespace Inkscape {
class DrawingContext;
}
@@ -28,10 +28,10 @@ struct NRStyle {
~NRStyle();
void set(SPStyle *);
- bool prepareFill(Inkscape::DrawingContext &ct, Geom::OptRect const &paintbox);
- bool prepareStroke(Inkscape::DrawingContext &ct, Geom::OptRect const &paintbox);
- void applyFill(Inkscape::DrawingContext &ct);
- void applyStroke(Inkscape::DrawingContext &ct);
+ bool prepareFill(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox);
+ bool prepareStroke(Inkscape::DrawingContext &dc, Geom::OptRect const &paintbox);
+ void applyFill(Inkscape::DrawingContext &dc);
+ void applyStroke(Inkscape::DrawingContext &dc);
void update();
enum PaintType {
@@ -67,6 +67,55 @@ struct NRStyle {
cairo_pattern_t *fill_pattern;
cairo_pattern_t *stroke_pattern;
+
+#ifdef WITH_SVG2
+ enum PaintOrderType {
+ PAINT_ORDER_NORMAL,
+ PAINT_ORDER_FILL,
+ PAINT_ORDER_STROKE,
+ PAINT_ORDER_MARKER
+ };
+
+ static const size_t PAINT_ORDER_LAYERS = 3;
+ PaintOrderType paint_order_layer[PAINT_ORDER_LAYERS];
+#endif
+
+#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
@@ -81,4 +130,4 @@ struct NRStyle {
fill-column:99
End:
*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/display/nr-svgfonts.cpp b/src/display/nr-svgfonts.cpp
index e095fb9a9..84b4bd080 100644
--- a/src/display/nr-svgfonts.cpp
+++ b/src/display/nr-svgfonts.cpp
@@ -1,5 +1,4 @@
#include "config.h"
-#ifdef ENABLE_SVG_FONTS
/*
* SVGFonts rendering implementation
*
@@ -16,7 +15,7 @@
#include <2geom/transforms.h>
#include <cairo.h>
#include <vector>
-#include "style.h"
+#include "sp-object.h"
#include "svg/svg.h"
#include "display/cairo-utils.h"
#include "display/nr-svgfonts.h"
@@ -43,10 +42,9 @@
static cairo_user_data_key_t key;
static cairo_status_t font_init_cb (cairo_scaled_font_t *scaled_font,
- cairo_t */*cairo*/, cairo_font_extents_t *metrics){
- cairo_font_face_t* face;
- face = cairo_scaled_font_get_font_face(scaled_font);
- SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
+ cairo_t * /*cairo*/, cairo_font_extents_t *metrics){
+ cairo_font_face_t* face = cairo_scaled_font_get_font_face(scaled_font);
+ SvgFont* instance = static_cast<SvgFont*>(cairo_font_face_get_user_data(face, &key));
return instance->scaled_font_init(scaled_font, metrics);
}
@@ -58,9 +56,8 @@ static cairo_status_t font_text_to_glyphs_cb ( cairo_scaled_font_t *scaled_font
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_text_cluster_flags_t *flags){
- cairo_font_face_t* face;
- face = cairo_scaled_font_get_font_face(scaled_font);
- SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
+ cairo_font_face_t* face = cairo_scaled_font_get_font_face(scaled_font);
+ SvgFont* instance = static_cast<SvgFont*>(cairo_font_face_get_user_data(face, &key));
return instance->scaled_font_text_to_glyphs(scaled_font, utf8, utf8_len, glyphs, num_glyphs, clusters, num_clusters, flags);
}
@@ -68,9 +65,8 @@ static cairo_status_t font_render_glyph_cb (cairo_scaled_font_t *scaled_font,
unsigned long glyph,
cairo_t *cr,
cairo_text_extents_t *metrics){
- cairo_font_face_t* face;
- face = cairo_scaled_font_get_font_face(scaled_font);
- SvgFont* instance = (SvgFont*) cairo_font_face_get_user_data(face, &key);
+ cairo_font_face_t* face = cairo_scaled_font_get_font_face(scaled_font);
+ SvgFont* instance = static_cast<SvgFont*>(cairo_font_face_get_user_data(face, &key));
return instance->scaled_font_render_glyph(scaled_font, glyph, cr, metrics);
}
@@ -116,15 +112,15 @@ unsigned int size_of_substring(const char* substring, gchar* str){
}
//TODO: in these macros, verify what happens when using unicode strings.
-#define Match_VKerning_Rule (((SPVkern*)node)->u1->contains(previous_unicode[0])\
- || ((SPVkern*)node)->g1->contains(previous_glyph_name)) &&\
- (((SPVkern*)node)->u2->contains(this->glyphs[i]->unicode[0])\
- || ((SPVkern*)node)->g2->contains(this->glyphs[i]->glyph_name.c_str()))
+#define Match_VKerning_Rule ((SP_VKERN(node))->u1->contains(previous_unicode[0])\
+ || (SP_VKERN(node))->g1->contains(previous_glyph_name)) &&\
+ ((SP_VKERN(node))->u2->contains(this->glyphs[i]->unicode[0])\
+ || (SP_VKERN(node))->g2->contains(this->glyphs[i]->glyph_name.c_str()))
-#define Match_HKerning_Rule (((SPHkern*)node)->u1->contains(previous_unicode[0])\
- || ((SPHkern*)node)->g1->contains(previous_glyph_name)) &&\
- (((SPHkern*)node)->u2->contains(this->glyphs[i]->unicode[0])\
- || ((SPHkern*)node)->g2->contains(this->glyphs[i]->glyph_name.c_str()))
+#define Match_HKerning_Rule ((SP_HKERN(node))->u1->contains(previous_unicode[0])\
+ || (SP_HKERN(node))->g1->contains(previous_glyph_name)) &&\
+ ((SP_HKERN(node))->u2->contains(this->glyphs[i]->unicode[0])\
+ || (SP_HKERN(node))->g2->contains(this->glyphs[i]->glyph_name.c_str()))
cairo_status_t
SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t */*scaled_font*/,
@@ -187,14 +183,14 @@ SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t */*scaled_font*/,
for(SPObject* node = this->font->children;previous_unicode && node;node=node->next){
//apply glyph kerning if appropriate
if (SP_IS_HKERN(node) && is_horizontal_text && Match_HKerning_Rule ){
- x -= (((SPHkern*)node)->k / 1000.0);//TODO: use here the height of the font
+ x -= ((SP_HKERN(node))->k / 1000.0);//TODO: use here the height of the font
}
if (SP_IS_VKERN(node) && !is_horizontal_text && Match_VKerning_Rule ){
- y -= (((SPVkern*)node)->k / 1000.0);//TODO: use here the "height" of the font
+ y -= ((SP_VKERN(node))->k / 1000.0);//TODO: use here the "height" of the font
}
}
- previous_unicode = (char*) this->glyphs[i]->unicode.c_str();//used for kerning checking
- previous_glyph_name = (char*) this->glyphs[i]->glyph_name.c_str();//used for kerning checking
+ previous_unicode = const_cast<char*>(this->glyphs[i]->unicode.c_str());//used for kerning checking
+ previous_glyph_name = const_cast<char*>(this->glyphs[i]->glyph_name.c_str());//used for kerning checking
(*glyphs)[count].index = i;
(*glyphs)[count].x = x;
(*glyphs)[count++].y = y;
@@ -251,7 +247,7 @@ Geom::PathVector
SvgFont::flip_coordinate_system(SPFont* spfont, Geom::PathVector pathv){
double units_per_em = 1000;
SPObject* obj;
- for (obj = ((SPObject*) spfont)->children; obj; obj=obj->next){
+ for (obj = (SP_OBJECT(spfont))->children; obj; obj=obj->next){
if (SP_IS_FONTFACE(obj)){
//XML Tree being directly used here while it shouldn't be.
sp_repr_get_double(obj->getRepr(), "units_per_em", &units_per_em);
@@ -282,16 +278,16 @@ SvgFont::scaled_font_render_glyph (cairo_scaled_font_t */*scaled_font*/,
SPObject* node;
if (glyph == this->glyphs.size()){
if (!this->missingglyph) return CAIRO_STATUS_SUCCESS;
- node = (SPObject*) this->missingglyph;
+ node = SP_OBJECT(this->missingglyph);
} else {
- node = (SPObject*) this->glyphs[glyph];
+ node = SP_OBJECT(this->glyphs[glyph]);
}
if (!SP_IS_GLYPH(node) && !SP_IS_MISSING_GLYPH(node)) {
return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return?
}
- SPFont* spfont = (SPFont*) node->parent;
+ SPFont* spfont = SP_FONT(node->parent);
if (!spfont) {
return CAIRO_STATUS_SUCCESS; // FIXME: is this the right code to return?
}
@@ -300,12 +296,12 @@ SvgFont::scaled_font_render_glyph (cairo_scaled_font_t */*scaled_font*/,
// or using the d attribute of a glyph node.
// pathv stores the path description from the d attribute:
Geom::PathVector pathv;
- if (SP_IS_GLYPH(node) && ((SPGlyph*)node)->d) {
- pathv = sp_svg_read_pathv(((SPGlyph*)node)->d);
+ if (SP_IS_GLYPH(node) && (SP_GLYPH(node))->d) {
+ pathv = sp_svg_read_pathv((SP_GLYPH(node))->d);
pathv = flip_coordinate_system(spfont, pathv);
this->render_glyph_path(cr, &pathv);
- } else if (SP_IS_MISSING_GLYPH(node) && ((SPMissingGlyph*)node)->d) {
- pathv = sp_svg_read_pathv(((SPMissingGlyph*)node)->d);
+ } else if (SP_IS_MISSING_GLYPH(node) && (SP_MISSING_GLYPH(node))->d) {
+ pathv = sp_svg_read_pathv((SP_MISSING_GLYPH(node))->d);
pathv = flip_coordinate_system(spfont, pathv);
this->render_glyph_path(cr, &pathv);
}
@@ -314,7 +310,7 @@ SvgFont::scaled_font_render_glyph (cairo_scaled_font_t */*scaled_font*/,
//render the SVG described on this glyph's child nodes.
for(node = node->children; node; node=node->next){
if (SP_IS_PATH(node)){
- pathv = ((SPShape*)node)->_curve->get_pathvector();
+ pathv = (SP_SHAPE(node))->_curve->get_pathvector();
pathv = flip_coordinate_system(spfont, pathv);
this->render_glyph_path(cr, &pathv);
}
@@ -324,12 +320,12 @@ SvgFont::scaled_font_render_glyph (cairo_scaled_font_t */*scaled_font*/,
if (SP_IS_USE(node)){
SPItem* item = SP_USE(node)->ref->getObject();
if (SP_IS_PATH(item)){
- pathv = ((SPShape*)item)->_curve->get_pathvector();
+ pathv = (SP_SHAPE(item))->_curve->get_pathvector();
pathv = flip_coordinate_system(spfont, pathv);
this->render_glyph_path(cr, &pathv);
}
- glyph_modified_connection = ((SPObject*) item)->connectModified(sigc::mem_fun(*this, &SvgFont::glyph_modified));
+ glyph_modified_connection = (SP_OBJECT(item))->connectModified(sigc::mem_fun(*this, &SvgFont::glyph_modified));
}
}
}
@@ -342,10 +338,10 @@ SvgFont::get_font_face(){
if (!this->userfont) {
for(SPObject* node = this->font->children;node;node=node->next){
if (SP_IS_GLYPH(node)){
- this->glyphs.push_back((SPGlyph*)node);
+ this->glyphs.push_back(SP_GLYPH(node));
}
if (SP_IS_MISSING_GLYPH(node)){
- this->missingglyph=(SPMissingGlyph*)node;
+ this->missingglyph=SP_MISSING_GLYPH(node);
}
}
this->userfont = new UserFont(this);
@@ -359,8 +355,6 @@ void SvgFont::refresh(){
this->userfont = NULL;
}
-#endif //#ifdef ENABLE_SVG_FONTS
-
/*
Local Variables:
mode:c++
diff --git a/src/display/nr-svgfonts.h b/src/display/nr-svgfonts.h
index 6138e2fbb..e1bb047bb 100644
--- a/src/display/nr-svgfonts.h
+++ b/src/display/nr-svgfonts.h
@@ -17,7 +17,7 @@
#include <sigc++/connection.h>
class SvgFont;
-struct SPFont;
+class SPFont;
class SPGlyph;
class SPMissingGlyph;
diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp
index 1dd3022c7..627bfb2ab 100644
--- a/src/display/snap-indicator.cpp
+++ b/src/display/snap-indicator.cpp
@@ -262,36 +262,38 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap
// Display the tooltip, which reveals the type of snap source and the type of snap target
gchar *tooltip_str = NULL;
- if (p.getSource() != SNAPSOURCE_GRID_PITCH) {
+ if ( (p.getSource() != SNAPSOURCE_GRID_PITCH) && (p.getTarget() != SNAPTARGET_UNDEFINED) ) {
tooltip_str = g_strconcat(source_name, _(" to "), target_name, NULL);
- } else {
+ } else if (p.getSource() != SNAPSOURCE_UNDEFINED) {
tooltip_str = g_strdup(source_name);
}
double fontsize = prefs->getInt("/tools/measure/fontsize");
- Geom::Point tooltip_pos = p.getPoint();
- if (tools_isactive(_desktop, TOOLS_MEASURE)) {
- // Make sure that the snap tooltips do not overlap the ones from the measure tool
- tooltip_pos += _desktop->w2d(Geom::Point(0, -3*fontsize));
- } else {
- tooltip_pos += _desktop->w2d(Geom::Point(0, -2*fontsize));
+ if (tooltip_str) {
+ Geom::Point tooltip_pos = p.getPoint();
+ if (tools_isactive(_desktop, TOOLS_MEASURE)) {
+ // Make sure that the snap tooltips do not overlap the ones from the measure tool
+ tooltip_pos += _desktop->w2d(Geom::Point(0, -3*fontsize));
+ } else {
+ tooltip_pos += _desktop->w2d(Geom::Point(0, -2*fontsize));
+ }
+
+ SPCanvasItem *canvas_tooltip = sp_canvastext_new(sp_desktop_tempgroup(_desktop), _desktop, tooltip_pos, tooltip_str);
+ sp_canvastext_set_fontsize(SP_CANVASTEXT(canvas_tooltip), fontsize);
+ SP_CANVASTEXT(canvas_tooltip)->rgba = 0xffffffff;
+ SP_CANVASTEXT(canvas_tooltip)->outline = false;
+ SP_CANVASTEXT(canvas_tooltip)->background = true;
+ if (pre_snap) {
+ SP_CANVASTEXT(canvas_tooltip)->rgba_background = 0x33337f40;
+ } else {
+ SP_CANVASTEXT(canvas_tooltip)->rgba_background = 0x33337f7f;
+ }
+ SP_CANVASTEXT(canvas_tooltip)->anchor_position = TEXT_ANCHOR_CENTER;
+ g_free(tooltip_str);
+
+ _snaptarget_tooltip = _desktop->add_temporary_canvasitem(canvas_tooltip, timeout_val);
}
- SPCanvasItem *canvas_tooltip = sp_canvastext_new(sp_desktop_tempgroup(_desktop), _desktop, tooltip_pos, tooltip_str);
- sp_canvastext_set_fontsize(SP_CANVASTEXT(canvas_tooltip), fontsize);
- SP_CANVASTEXT(canvas_tooltip)->rgba = 0xffffffff;
- SP_CANVASTEXT(canvas_tooltip)->outline = false;
- SP_CANVASTEXT(canvas_tooltip)->background = true;
- if (pre_snap) {
- SP_CANVASTEXT(canvas_tooltip)->rgba_background = 0x33337f40;
- } else {
- SP_CANVASTEXT(canvas_tooltip)->rgba_background = 0x33337f7f;
- }
- SP_CANVASTEXT(canvas_tooltip)->anchor_position = TEXT_ANCHOR_CENTER;
- g_free(tooltip_str);
-
- _snaptarget_tooltip = _desktop->add_temporary_canvasitem(canvas_tooltip, timeout_val);
-
// Display the bounding box, if we snapped to one
Geom::OptRect const bbox = p.getTargetBBox();
if (bbox) {
diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp
index 725ead0d6..3636319df 100644
--- a/src/display/sodipodi-ctrl.cpp
+++ b/src/display/sodipodi-ctrl.cpp
@@ -28,7 +28,7 @@ enum {
static void sp_ctrl_class_init (SPCtrlClass *klass);
static void sp_ctrl_init (SPCtrl *ctrl);
-static void sp_ctrl_destroy (GtkObject *object);
+static void sp_ctrl_destroy(SPCanvasItem *object);
static void sp_ctrl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
static void sp_ctrl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
static void sp_ctrl_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
@@ -63,19 +63,13 @@ sp_ctrl_get_type (void)
static void
sp_ctrl_class_init (SPCtrlClass *klass)
{
- GtkObjectClass *object_class;
- SPCanvasItemClass *item_class;
- GObjectClass *g_object_class;
+ SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(klass);
+ GObjectClass *g_object_class = (GObjectClass *) klass;
- object_class = (GtkObjectClass *) klass;
- item_class = (SPCanvasItemClass *) klass;
- g_object_class = (GObjectClass *) klass;
-
- parent_class = (SPCanvasItemClass *)g_type_class_peek_parent (klass);
+ parent_class = SP_CANVAS_ITEM_CLASS(g_type_class_peek_parent (klass));
g_object_class->set_property = sp_ctrl_set_property;
g_object_class->get_property = sp_ctrl_get_property;
- object_class->destroy = sp_ctrl_destroy;
g_object_class_install_property (g_object_class,
ARG_SHAPE, g_param_spec_int ("shape", "shape", "Shape", 0, G_MAXINT, SP_CTRL_SHAPE_SQUARE, (GParamFlags) G_PARAM_READWRITE));
@@ -96,6 +90,7 @@ sp_ctrl_class_init (SPCtrlClass *klass)
g_object_class_install_property (g_object_class,
ARG_STROKE_COLOR, g_param_spec_int ("stroke_color", "stroke_color", "Stroke Color", G_MININT, G_MAXINT, 0x000000ff, (GParamFlags) G_PARAM_READWRITE));
+ item_class->destroy = sp_ctrl_destroy;
item_class->update = sp_ctrl_update;
item_class->render = sp_ctrl_render;
item_class->point = sp_ctrl_point;
@@ -113,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
@@ -211,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:
@@ -246,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;
@@ -255,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;
@@ -268,23 +227,20 @@ sp_ctrl_init (SPCtrl *ctrl)
ctrl->_point = Geom::Point(0,0);
}
-static void
-sp_ctrl_destroy (GtkObject *object)
+static void sp_ctrl_destroy(SPCanvasItem *object)
{
- SPCtrl *ctrl;
-
g_return_if_fail (object != NULL);
g_return_if_fail (SP_IS_CTRL (object));
- ctrl = SP_CTRL (object);
+ SPCtrl *ctrl = SP_CTRL (object);
if (ctrl->cache) {
delete[] ctrl->cache;
ctrl->cache = NULL;
}
- if (GTK_OBJECT_CLASS (parent_class)->destroy)
- (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+ if (SP_CANVAS_ITEM_CLASS(parent_class)->destroy)
+ (* SP_CANVAS_ITEM_CLASS(parent_class)->destroy) (object);
}
static void
@@ -295,21 +251,19 @@ sp_ctrl_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int fla
ctrl = SP_CTRL (item);
- if (((SPCanvasItemClass *) parent_class)->update)
- (* ((SPCanvasItemClass *) parent_class)->update) (item, affine, flags);
+ if ((SP_CANVAS_ITEM_CLASS(parent_class))->update)
+ (* (SP_CANVAS_ITEM_CLASS(parent_class))->update) (item, affine, flags);
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:
@@ -320,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;
}
@@ -339,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);
}
@@ -368,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) {
@@ -390,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];
@@ -403,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;
@@ -423,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;
}
}
@@ -470,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++;
@@ -482,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;
@@ -494,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;
}
}
@@ -507,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) {
@@ -535,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++;
}
}
@@ -574,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) {
@@ -635,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/sodipodi-ctrlrect.cpp b/src/display/sodipodi-ctrlrect.cpp
index 3b50d0b5d..e6e427047 100644
--- a/src/display/sodipodi-ctrlrect.cpp
+++ b/src/display/sodipodi-ctrlrect.cpp
@@ -28,7 +28,7 @@
static void sp_ctrlrect_class_init(SPCtrlRectClass *c);
static void sp_ctrlrect_init(CtrlRect *ctrlrect);
-static void sp_ctrlrect_destroy(GtkObject *object);
+static void sp_ctrlrect_destroy(SPCanvasItem *object);
static void sp_ctrlrect_update(SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
static void sp_ctrlrect_render(SPCanvasItem *item, SPCanvasBuf *buf);
@@ -61,13 +61,11 @@ GType sp_ctrlrect_get_type()
static void sp_ctrlrect_class_init(SPCtrlRectClass *c)
{
- GtkObjectClass *object_class = (GtkObjectClass *) c;
- SPCanvasItemClass *item_class = (SPCanvasItemClass *) c;
+ SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(c);
- parent_class = (SPCanvasItemClass*) g_type_class_peek_parent(c);
-
- object_class->destroy = sp_ctrlrect_destroy;
+ parent_class = SP_CANVAS_ITEM_CLASS(g_type_class_peek_parent(c));
+ item_class->destroy = sp_ctrlrect_destroy;
item_class->update = sp_ctrlrect_update;
item_class->render = sp_ctrlrect_render;
}
@@ -77,10 +75,10 @@ static void sp_ctrlrect_init(CtrlRect *cr)
cr->init();
}
-static void sp_ctrlrect_destroy(GtkObject *object)
+static void sp_ctrlrect_destroy(SPCanvasItem *object)
{
- if (GTK_OBJECT_CLASS(parent_class)->destroy) {
- (* GTK_OBJECT_CLASS(parent_class)->destroy)(object);
+ if (SP_CANVAS_ITEM_CLASS(parent_class)->destroy) {
+ (* SP_CANVAS_ITEM_CLASS(parent_class)->destroy)(object);
}
}
@@ -121,8 +119,6 @@ void CtrlRect::render(SPCanvasBuf *buf)
using Geom::X;
using Geom::Y;
- static double const dashes[2] = {4.0, 4.0};
-
if (!_area) {
return;
}
@@ -131,6 +127,7 @@ void CtrlRect::render(SPCanvasBuf *buf)
area[X].max() + _shadow_size, area[Y].max() + _shadow_size);
if ( area_w_shadow.intersects(buf->rect) )
{
+ static double const dashes[2] = {4.0, 4.0};
cairo_save(buf->ct);
cairo_translate(buf->ct, -buf->rect.left(), -buf->rect.top());
cairo_set_line_width(buf->ct, 1);
@@ -145,7 +142,18 @@ void CtrlRect::render(SPCanvasBuf *buf)
ink_cairo_set_source_rgba32(buf->ct, _border_color);
cairo_stroke(buf->ct);
- if (_shadow_size > 0) {
+ if (_shadow_size == 1) { // highlight the border by drawing it in _shadow_color
+ if (_dashed) {
+ cairo_set_dash(buf->ct, dashes, 2, 4);
+ cairo_rectangle(buf->ct, 0.5 + area[X].min(), 0.5 + area[Y].min(),
+ area[X].max() - area[X].min(), area[Y].max() - area[Y].min());
+ } else {
+ cairo_rectangle(buf->ct, -0.5 + area[X].min(), -0.5 + area[Y].min(),
+ area[X].max() - area[X].min(), area[Y].max() - area[Y].min());
+ }
+ ink_cairo_set_source_rgba32(buf->ct, _shadow_color);
+ cairo_stroke(buf->ct);
+ } else if (_shadow_size > 1) { // fill the shadow
ink_cairo_set_source_rgba32(buf->ct, _shadow_color);
cairo_rectangle(buf->ct, 1 + area[X].max(), area[Y].min() + _shadow_size,
_shadow_size, area[Y].max() - area[Y].min() + 1); // right shadow
@@ -163,8 +171,8 @@ void CtrlRect::update(Geom::Affine const &affine, unsigned int flags)
using Geom::X;
using Geom::Y;
- if (((SPCanvasItemClass *) parent_class)->update) {
- ((SPCanvasItemClass *) parent_class)->update(this, affine, flags);
+ if ((SP_CANVAS_ITEM_CLASS(parent_class))->update) {
+ (SP_CANVAS_ITEM_CLASS(parent_class))->update(this, affine, flags);
}
sp_canvas_item_reset_bounds(this);
diff --git a/src/display/sp-canvas-item.h b/src/display/sp-canvas-item.h
index 8baa09401..f34ec453c 100644
--- a/src/display/sp-canvas-item.h
+++ b/src/display/sp-canvas-item.h
@@ -27,6 +27,7 @@
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <2geom/rect.h>
+#include "ui/control-types.h"
G_BEGIN_DECLS
@@ -38,31 +39,17 @@ typedef struct _SPCanvasItemClass SPCanvasItemClass;
#define SP_TYPE_CANVAS_ITEM (SPCanvasItem::getType())
#define SP_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_CANVAS_ITEM, SPCanvasItem))
+#define SP_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_CANVAS_ITEM, SPCanvasItemClass))
#define SP_IS_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_CANVAS_ITEM))
#define SP_CANVAS_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), SP_TYPE_CANVAS_ITEM, SPCanvasItemClass))
-namespace Inkscape
-{
-
-// Rough initial set. Most likely needs refinement.
-enum ControlType {
- CTRL_TYPE_UNKNOWN,
- CTRL_TYPE_ADJ_HANDLE,
- CTRL_TYPE_ANCHOR,
- CTRL_TYPE_POINT,
- CTRL_TYPE_ROTATE,
- CTRL_TYPE_SIZER,
- CTRL_TYPE_SHAPER,
- CTRL_TYPE_ORIGIN
-};
-
-} // namespace Inkscape
/**
* An SPCanvasItem refers to a SPCanvas and to its parent item; it has
* four coordinates, a bounding rectangle, and a transformation matrix.
*/
-struct SPCanvasItem : public GtkObject {
+struct SPCanvasItem {
+ GInitiallyUnowned parent_instance;
static GType getType();
SPCanvas *canvas;
@@ -76,17 +63,22 @@ struct SPCanvasItem : public GtkObject {
Geom::Affine xform;
Inkscape::ControlType ctrlType;
+ Inkscape::ControlFlags ctrlFlags;
// Replacement for custom GtkObject flag enumeration
gboolean visible;
gboolean need_update;
gboolean need_affine;
+
+ bool in_destruction;
};
/**
* The vtable of an SPCanvasItem.
*/
-struct _SPCanvasItemClass : public GtkObjectClass {
+struct _SPCanvasItemClass {
+ GInitiallyUnownedClass parent_class;
+
void (* update) (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
void (* render) (SPCanvasItem *item, SPCanvasBuf *buf);
@@ -94,6 +86,16 @@ struct _SPCanvasItemClass : public GtkObjectClass {
int (* event) (SPCanvasItem *item, GdkEvent *event);
void (* viewbox_changed) (SPCanvasItem *item, Geom::IntRect const &new_area);
+
+ /* Default signal handler for the ::destroy signal, which is
+ * invoked to request that references to the widget be dropped.
+ * If an object class overrides destroy() in order to perform class
+ * specific destruction then it must still invoke its superclass'
+ * implementation of the method after it is finished with its
+ * own cleanup. (See gtk_widget_real_destroy() for an example of
+ * how to do this).
+ */
+ void (*destroy) (SPCanvasItem *object);
};
/**
@@ -104,8 +106,6 @@ SPCanvasItem *sp_canvas_item_new(SPCanvasGroup *parent, GType type, const gchar
G_END_DECLS
-#define sp_canvas_item_set g_object_set
-
void sp_canvas_item_affine_absolute(SPCanvasItem *item, Geom::Affine const &aff);
void sp_canvas_item_raise(SPCanvasItem *item, int positions);
@@ -113,6 +113,7 @@ void sp_canvas_item_lower(SPCanvasItem *item, int positions);
bool sp_canvas_item_is_visible(SPCanvasItem *item);
void sp_canvas_item_show(SPCanvasItem *item);
void sp_canvas_item_hide(SPCanvasItem *item);
+void sp_canvas_item_destroy(SPCanvasItem *item);
int sp_canvas_item_grab(SPCanvasItem *item, unsigned int event_mask, GdkCursor *cursor, guint32 etime);
void sp_canvas_item_ungrab(SPCanvasItem *item, guint32 etime);
diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp
index 22aad2442..6d903867b 100644
--- a/src/display/sp-canvas.cpp
+++ b/src/display/sp-canvas.cpp
@@ -19,7 +19,9 @@
# include <config.h>
#endif
-#include <gdkmm/region.h>
+#include <gdkmm/rectangle.h>
+#include <cairomm/region.h>
+
#include "helper/sp-marshal.h"
#include <2geom/rect.h>
#include <2geom/affine.h>
@@ -33,8 +35,6 @@
#include "display/cairo-utils.h"
#include "debug/gdk-event-latency-tracker.h"
#include "desktop.h"
-#include "sp-namedview.h"
-#include <gdkmm/rectangle.h>
using Inkscape::Debug::GdkEventLatencyTracker;
@@ -87,7 +87,7 @@ struct SPCanvasGroup {
* Callback that destroys all items in group and calls group's virtual
* destroy() function.
*/
- static void destroy(GtkObject *object);
+ static void destroy(SPCanvasItem *object);
/**
* Update handler for canvas groups.
@@ -156,6 +156,14 @@ enum {
void trackLatency(GdkEvent const *event);
+enum {
+ DESTROY,
+ LAST_SIGNAL
+};
+
+void sp_canvas_item_base_class_init(SPCanvasItemClass *klass);
+void sp_canvas_item_base_class_finalize(SPCanvasItemClass *klass);
+
/**
* Initializes the SPCanvasItem vtable and the "event" signal.
*/
@@ -164,12 +172,17 @@ void sp_canvas_item_class_init(SPCanvasItemClass *klass);
/**
* Callback for initialization of SPCanvasItem.
*/
-void sp_canvas_item_init(SPCanvasItem *item);
+void sp_canvas_item_init(SPCanvasItem *item, SPCanvasItemClass *klass);
/**
* Callback that removes item from all referers and destroys it.
*/
-void sp_canvas_item_dispose(GObject *object);
+void sp_canvas_item_dispose(GObject *object);
+void sp_canvas_item_finalize(GObject *object);
+void sp_canvas_item_real_destroy(SPCanvasItem *object);
+
+static gpointer parent_class = NULL;
+static guint object_signals[LAST_SIGNAL] = { 0 };
/**
* Sets up the newly created SPCanvasItem.
@@ -213,21 +226,6 @@ public:
static int pickCurrentItem(SPCanvas *canvas, GdkEvent *event);
/**
- * Class initialization function for SPCanvasClass.
- */
- static void classInit(SPCanvasClass *klass);
-
- /**
- * Callback: object initialization for SPCanvas.
- */
- static void init(SPCanvas *canvas);
-
- /**
- * Destroy handler for SPCanvas.
- */
- static void destroy(GtkObject *object);
-
- /**
* The canvas widget's realize callback.
*/
static void realize(GtkWidget *widget);
@@ -240,7 +238,12 @@ public:
/**
* The canvas widget's size request callback.
*/
+#if GTK_CHECK_VERSION(3,0,0)
+ static void getPreferredWidth(GtkWidget *widget, gint *min_w, gint *nat_w);
+ static void getPreferredHeight(GtkWidget *widget, gint *min_h, gint *nat_h);
+#else
static void sizeRequest(GtkWidget *widget, GtkRequisition *req);
+#endif
/**
* The canvas widget's size allocate callback.
@@ -269,7 +272,11 @@ public:
*
* @todo FIXME: function allways retruns false.
*/
- static gint handleExpose(GtkWidget *widget, GdkEventExpose *event);
+#if GTK_CHECK_VERSION(3,0,0)
+ static gboolean handleDraw(GtkWidget *widget, cairo_t *cr);
+#else
+ static gboolean handleExpose(GtkWidget *widget, GdkEventExpose *event);
+#endif
/**
* The canvas widget's keypress callback.
@@ -346,50 +353,50 @@ public:
static void add_idle(SPCanvas *canvas);
/**
- * Convenience function to remove the idle handler of a canvas.
- */
- static void remove_idle(SPCanvas *canvas);
-
- /**
- * Removes the transient state of the canvas (idle handler, grabs).
- */
- static void shutdown_transients(SPCanvas *canvas);
-
- /**
* Update callback for canvas widget.
*/
static void requestCanvasUpdate(SPCanvas *canvas);
-
- static GtkWidgetClass *parentClass;
};
-GtkWidgetClass *SPCanvasImpl::parentClass = 0;
-
GType SPCanvasItem::getType()
{
- static GType type = 0;
- if (!type) {
- static GTypeInfo const info = {
+ static GType object_type = 0;
+
+ if (!object_type) {
+ static GTypeInfo const object_info = {
sizeof(SPCanvasItemClass),
- NULL, NULL,
+ reinterpret_cast<GBaseInitFunc>(sp_canvas_item_base_class_init),
+ reinterpret_cast<GBaseFinalizeFunc>(sp_canvas_item_base_class_finalize),
reinterpret_cast<GClassInitFunc>(sp_canvas_item_class_init),
- NULL, NULL,
+ NULL, // class_finalize
+ NULL, // class_data
sizeof(SPCanvasItem),
- 0,
+ 16, // n_preallocs
reinterpret_cast<GInstanceInitFunc>(sp_canvas_item_init),
- NULL
+ NULL // value_table
};
- type = g_type_register_static(GTK_TYPE_OBJECT, "SPCanvasItem", &info, GTypeFlags(0));
+
+ object_type = g_type_register_static(G_TYPE_INITIALLY_UNOWNED,
+ "SPCanvasItem", &object_info, GTypeFlags(0));
}
- return type;
+ return object_type;
}
namespace {
+void sp_canvas_item_base_class_init(SPCanvasItemClass * /*klass*/)
+{
+}
+
+void sp_canvas_item_base_class_finalize(SPCanvasItemClass * /*klass*/)
+{
+}
+
void sp_canvas_item_class_init(SPCanvasItemClass *klass)
{
- GObjectClass *object_class = (GObjectClass *) klass;
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+ parent_class = g_type_class_ref (G_TYPE_OBJECT);
item_signals[ITEM_EVENT] = g_signal_new ("event",
G_TYPE_FROM_CLASS (klass),
@@ -400,18 +407,31 @@ void sp_canvas_item_class_init(SPCanvasItemClass *klass)
G_TYPE_BOOLEAN, 1,
GDK_TYPE_EVENT);
- object_class->dispose = sp_canvas_item_dispose;
+ gobject_class->dispose = sp_canvas_item_dispose;
+ gobject_class->finalize = sp_canvas_item_finalize;
+ klass->destroy = sp_canvas_item_real_destroy;
+
+ object_signals[DESTROY] =
+ g_signal_new ("destroy",
+ G_TYPE_FROM_CLASS (gobject_class),
+ (GSignalFlags)(G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS),
+ G_STRUCT_OFFSET (SPCanvasItemClass, destroy),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
}
-void sp_canvas_item_init(SPCanvasItem *item)
+void sp_canvas_item_init(SPCanvasItem *item, SPCanvasItemClass * /*klass*/)
{
item->xform = Geom::Affine(Geom::identity());
item->ctrlType = Inkscape::CTRL_TYPE_UNKNOWN;
+ item->ctrlFlags = Inkscape::CTRL_FLAG_NORMAL;
// TODO items should not be visible on creation - this causes kludges with items
// that should be initially invisible; examples of such items: node handles, the CtrlRect
// used for rubberbanding, path outline, etc.
item->visible = TRUE;
+ item->in_destruction = false;
}
} // namespace
@@ -469,49 +489,93 @@ static void redraw_if_visible(SPCanvasItem *item)
}
}
-namespace {
+void sp_canvas_item_destroy(SPCanvasItem *item)
+{
+ g_return_if_fail(SP_IS_CANVAS_ITEM(item));
+
+ if (!item->in_destruction)
+ g_object_run_dispose(G_OBJECT(item));
+}
+namespace {
void sp_canvas_item_dispose(GObject *object)
{
SPCanvasItem *item = SP_CANVAS_ITEM (object);
- // Hack: if this is a ctrlrect, move it to 0,0;
- // this redraws only the stroke of the rect to be deleted,
- // avoiding redraw of the entire area
- if (SP_IS_CTRLRECT(item)) {
- SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
- SP_CTRLRECT(object)->update(item->xform, 0);
- } else {
- redraw_if_visible (item);
- }
- item->visible = FALSE;
-
- if (item == item->canvas->current_item) {
- item->canvas->current_item = NULL;
- item->canvas->need_repick = TRUE;
- }
+ /* guard against reinvocations during
+ * destruction with the in_destruction flag.
+ */
+ if (!item->in_destruction)
+ {
+ item->in_destruction=true;
+
+ // Hack: if this is a ctrlrect, move it to 0,0;
+ // this redraws only the stroke of the rect to be deleted,
+ // avoiding redraw of the entire area
+ if (SP_IS_CTRLRECT(item)) {
+ SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)));
+ SP_CTRLRECT(object)->update(item->xform, 0);
+ } else {
+ redraw_if_visible (item);
+ }
+ item->visible = FALSE;
+
+ if (item == item->canvas->current_item) {
+ item->canvas->current_item = NULL;
+ item->canvas->need_repick = TRUE;
+ }
+
+ if (item == item->canvas->new_current_item) {
+ item->canvas->new_current_item = NULL;
+ item->canvas->need_repick = TRUE;
+ }
+
+ if (item == item->canvas->grabbed_item) {
+ item->canvas->grabbed_item = NULL;
+
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default());
+ GdkDevice *device = gdk_device_manager_get_client_pointer(dm);
+ gdk_device_ungrab(device, GDK_CURRENT_TIME);
+#else
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+#endif
+ }
- if (item == item->canvas->new_current_item) {
- item->canvas->new_current_item = NULL;
- item->canvas->need_repick = TRUE;
+ if (item == item->canvas->focused_item) {
+ item->canvas->focused_item = NULL;
+ }
+
+ if (item->parent) {
+ SP_CANVAS_GROUP(item->parent)->remove(item);
+ }
+
+ g_signal_emit (object, object_signals[DESTROY], 0);
+ item->in_destruction = false;
}
- if (item == item->canvas->grabbed_item) {
- item->canvas->grabbed_item = NULL;
- gdk_pointer_ungrab (GDK_CURRENT_TIME);
- }
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
- if (item == item->canvas->focused_item) {
- item->canvas->focused_item = NULL;
- }
+void sp_canvas_item_real_destroy(SPCanvasItem *object)
+{
+ g_signal_handlers_destroy(object);
+}
+
+void sp_canvas_item_finalize(GObject *gobject)
+{
+ SPCanvasItem *object = SP_CANVAS_ITEM(gobject);
- if (item->parent) {
- SP_CANVAS_GROUP(item->parent)->remove(item);
+ if (g_object_is_floating (object))
+ {
+ g_warning ("A floating object was finalized. This means that someone\n"
+ "called g_object_unref() on an object that had only a floating\n"
+ "reference; the initial floating reference is not owned by anyone\n"
+ "and must be removed with g_object_ref_sink().");
}
-
- G_OBJECT_CLASS(g_type_class_peek(g_type_parent(SPCanvasItem::getType())))->dispose(object);
+
+ G_OBJECT_CLASS (parent_class)->finalize (gobject);
}
-
} // namespace
/**
@@ -804,9 +868,21 @@ int sp_canvas_item_grab(SPCanvasItem *item, guint event_mask, GdkCursor *cursor,
// fixme: Top hack (Lauris)
// fixme: If we add key masks to event mask, Gdk will abort (Lauris)
// fixme: But Canvas actualle does get key events, so all we need is routing these here
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default());
+ GdkDevice *device = gdk_device_manager_get_client_pointer(dm);
+ gdk_device_grab(device,
+ getWindow(item->canvas),
+ GDK_OWNERSHIP_NONE,
+ FALSE,
+ (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
+ cursor,
+ etime);
+#else
gdk_pointer_grab( getWindow(item->canvas), FALSE,
(GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))),
NULL, cursor, etime);
+#endif
item->canvas->grabbed_item = item;
item->canvas->grabbed_event_mask = event_mask;
@@ -833,7 +909,13 @@ void sp_canvas_item_ungrab(SPCanvasItem *item, guint32 etime)
item->canvas->grabbed_item = NULL;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default());
+ GdkDevice *device = gdk_device_manager_get_client_pointer(dm);
+ gdk_device_ungrab(device, etime);
+#else
gdk_pointer_ungrab (etime);
+#endif
}
/**
@@ -927,13 +1009,11 @@ GType SPCanvasGroup::getType(void)
void SPCanvasGroup::classInit(SPCanvasGroupClass *klass)
{
- GtkObjectClass *object_class = reinterpret_cast<GtkObjectClass *>(klass);
SPCanvasItemClass *item_class = reinterpret_cast<SPCanvasItemClass *>(klass);
parentClass = reinterpret_cast<SPCanvasItemClass*>(g_type_class_peek_parent(klass));
- object_class->destroy = SPCanvasGroup::destroy;
-
+ item_class->destroy = SPCanvasGroup::destroy;
item_class->update = SPCanvasGroup::update;
item_class->render = SPCanvasGroup::render;
item_class->point = SPCanvasGroup::point;
@@ -945,7 +1025,7 @@ void SPCanvasGroup::init(SPCanvasGroup * /*group*/)
// Nothing here
}
-void SPCanvasGroup::destroy(GtkObject *object)
+void SPCanvasGroup::destroy(SPCanvasItem *object)
{
g_return_if_fail(object != NULL);
g_return_if_fail(SP_IS_CANVAS_GROUP(object));
@@ -957,11 +1037,11 @@ void SPCanvasGroup::destroy(GtkObject *object)
SPCanvasItem *child = reinterpret_cast<SPCanvasItem *>(list->data);
list = list->next;
- gtk_object_destroy(GTK_OBJECT(child));
+ sp_canvas_item_destroy(child);
}
- if (GTK_OBJECT_CLASS(parentClass)->destroy) {
- (* GTK_OBJECT_CLASS(parentClass)->destroy)(object);
+ if (SP_CANVAS_ITEM_CLASS(parentClass)->destroy) {
+ (* SP_CANVAS_ITEM_CLASS(parentClass)->destroy)(object);
}
}
@@ -971,7 +1051,7 @@ void SPCanvasGroup::update(SPCanvasItem *item, Geom::Affine const &affine, unsig
Geom::OptRect bounds;
for (GList *list = group->items; list; list = list->next) {
- SPCanvasItem *i = (SPCanvasItem *)list->data;
+ SPCanvasItem *i = SP_CANVAS_ITEM(list->data);
sp_canvas_item_invoke_update (i, affine, flags);
@@ -1008,14 +1088,14 @@ double SPCanvasGroup::point(SPCanvasItem *item, Geom::Point p, SPCanvasItem **ac
double dist = 0.0;
for (GList *list = group->items; list; list = list->next) {
- SPCanvasItem *child = (SPCanvasItem *)list->data;
+ SPCanvasItem *child = SP_CANVAS_ITEM(list->data);
if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) {
SPCanvasItem *point_item = NULL; // cater for incomplete item implementations
int has_point;
- if (child->visible && SP_CANVAS_ITEM_GET_CLASS (child)->point) {
- dist = sp_canvas_item_invoke_point (child, p, &point_item);
+ if (child->visible && SP_CANVAS_ITEM_GET_CLASS(child)->point) {
+ dist = sp_canvas_item_invoke_point(child, p, &point_item);
has_point = TRUE;
} else {
has_point = FALSE;
@@ -1042,7 +1122,7 @@ void SPCanvasGroup::render(SPCanvasItem *item, SPCanvasBuf *buf)
SPCanvasGroup const *group = SP_CANVAS_GROUP(item);
for (GList *list = group->items; list; list = list->next) {
- SPCanvasItem *child = (SPCanvasItem *)list->data;
+ SPCanvasItem *child = SP_CANVAS_ITEM(list->data);
if (child->visible) {
if ((child->x1 < buf->rect.right()) &&
(child->y1 < buf->rect.bottom()) &&
@@ -1061,7 +1141,7 @@ void SPCanvasGroup::viewboxChanged(SPCanvasItem *item, Geom::IntRect const &new_
SPCanvasGroup *group = SP_CANVAS_GROUP(item);
for (GList *list = group->items; list; list = list->next) {
- SPCanvasItem *child = (SPCanvasItem *)list->data;
+ SPCanvasItem *child = SP_CANVAS_ITEM(list->data);
if (child->visible) {
if (SP_CANVAS_ITEM_GET_CLASS(child)->viewbox_changed) {
SP_CANVAS_ITEM_GET_CLASS(child)->viewbox_changed(child, new_area);
@@ -1108,60 +1188,46 @@ void SPCanvasGroup::remove(SPCanvasItem *item)
}
}
-/**
- * Registers the SPCanvas class if necessary, and returns the type ID
- * associated to it.
- *
- * @return The type ID of the SPCanvas class.
- */
-GType SPCanvas::getType(void)
-{
- static GType type = 0;
- if (!type) {
- GTypeInfo info = {
- sizeof(SPCanvasClass),
- 0, // base_init
- 0, // base_finalize
- (GClassInitFunc)SPCanvasImpl::classInit,
- 0, // class_finalize
- 0, // class_data
- sizeof(SPCanvas),
- 0, // n_preallocs
- (GInstanceInitFunc)SPCanvasImpl::init,
- 0 // value_table
- };
- type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast<GTypeFlags>(0));
- }
- return type;
-}
+static void sp_canvas_dispose (GObject *object);
+static void sp_canvas_shutdown_transients(SPCanvas *canvas);
+
+G_DEFINE_TYPE(SPCanvas, sp_canvas, GTK_TYPE_WIDGET);
-void SPCanvasImpl::classInit(SPCanvasClass *klass)
+static void
+sp_canvas_class_init(SPCanvasClass *klass)
{
- GtkObjectClass *object_class = (GtkObjectClass *) klass;
- GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
- parentClass = reinterpret_cast<GtkWidgetClass *>(g_type_class_peek_parent(klass));
+ object_class->dispose = sp_canvas_dispose;
- object_class->destroy = SPCanvasImpl::destroy;
+ widget_class->realize = SPCanvasImpl::realize;
+ widget_class->unrealize = SPCanvasImpl::unrealize;
+
+#if GTK_CHECK_VERSION(3,0,0)
+ widget_class->get_preferred_width = SPCanvasImpl::getPreferredWidth;
+ widget_class->get_preferred_height = SPCanvasImpl::getPreferredHeight;
+ widget_class->draw = SPCanvasImpl::handleDraw;
+#else
+ widget_class->size_request = SPCanvasImpl::sizeRequest;
+ widget_class->expose_event = SPCanvasImpl::handleExpose;
+#endif
- widget_class->realize = SPCanvasImpl::realize;
- widget_class->unrealize = SPCanvasImpl::unrealize;
- widget_class->size_request = SPCanvasImpl::sizeRequest;
- widget_class->size_allocate = SPCanvasImpl::sizeAllocate;
- widget_class->button_press_event = SPCanvasImpl::button;
+ widget_class->size_allocate = SPCanvasImpl::sizeAllocate;
+ widget_class->button_press_event = SPCanvasImpl::button;
widget_class->button_release_event = SPCanvasImpl::button;
- widget_class->motion_notify_event = SPCanvasImpl::handleMotion;
- widget_class->scroll_event = SPCanvasImpl::handleScroll;
- widget_class->expose_event = SPCanvasImpl::handleExpose;
- widget_class->key_press_event = SPCanvasImpl::handleKeyEvent;
- widget_class->key_release_event = SPCanvasImpl::handleKeyEvent;
- widget_class->enter_notify_event = SPCanvasImpl::handleCrossing;
- widget_class->leave_notify_event = SPCanvasImpl::handleCrossing;
- widget_class->focus_in_event = SPCanvasImpl::handleFocusIn;
- widget_class->focus_out_event = SPCanvasImpl::handleFocusOut;
+ widget_class->motion_notify_event = SPCanvasImpl::handleMotion;
+ widget_class->scroll_event = SPCanvasImpl::handleScroll;
+ widget_class->key_press_event = SPCanvasImpl::handleKeyEvent;
+ widget_class->key_release_event = SPCanvasImpl::handleKeyEvent;
+ widget_class->enter_notify_event = SPCanvasImpl::handleCrossing;
+ widget_class->leave_notify_event = SPCanvasImpl::handleCrossing;
+ widget_class->focus_in_event = SPCanvasImpl::handleFocusIn;
+ widget_class->focus_out_event = SPCanvasImpl::handleFocusOut;
}
-void SPCanvasImpl::init(SPCanvas *canvas)
+static void
+sp_canvas_init(SPCanvas *canvas)
{
gtk_widget_set_has_window (GTK_WIDGET (canvas), TRUE);
gtk_widget_set_double_buffered (GTK_WIDGET (canvas), FALSE);
@@ -1200,7 +1266,7 @@ void SPCanvasImpl::init(SPCanvas *canvas)
canvas->is_scrolling = false;
}
-void SPCanvasImpl::remove_idle(SPCanvas *canvas)
+static void sp_canvas_remove_idle(SPCanvas *canvas)
{
if (canvas->idle_id) {
g_source_remove (canvas->idle_id);
@@ -1208,7 +1274,8 @@ void SPCanvasImpl::remove_idle(SPCanvas *canvas)
}
}
-void SPCanvasImpl::shutdown_transients(SPCanvas *canvas)
+static void
+sp_canvas_shutdown_transients(SPCanvas *canvas)
{
// We turn off the need_redraw flag, since if the canvas is mapped again
// it will request a redraw anyways. We do not turn off the need_update
@@ -1225,13 +1292,20 @@ void SPCanvasImpl::shutdown_transients(SPCanvas *canvas)
if (canvas->grabbed_item) {
canvas->grabbed_item = NULL;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default());
+ GdkDevice *device = gdk_device_manager_get_client_pointer(dm);
+ gdk_device_ungrab(device, GDK_CURRENT_TIME);
+#else
gdk_pointer_ungrab (GDK_CURRENT_TIME);
+#endif
}
- remove_idle(canvas);
+ sp_canvas_remove_idle(canvas);
}
-void SPCanvasImpl::destroy(GtkObject *object)
+static void
+sp_canvas_dispose(GObject *object)
{
SPCanvas *canvas = SP_CANVAS(object);
@@ -1240,12 +1314,12 @@ void SPCanvasImpl::destroy(GtkObject *object)
canvas->root = NULL;
}
- shutdown_transients(canvas);
+ sp_canvas_shutdown_transients(canvas);
#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
canvas->cms_key.~ustring();
#endif
- if (GTK_OBJECT_CLASS(parentClass)->destroy) {
- (* GTK_OBJECT_CLASS(parentClass)->destroy)(object);
+ if (G_OBJECT_CLASS(sp_canvas_parent_class)->dispose) {
+ (* G_OBJECT_CLASS(sp_canvas_parent_class)->dispose)(object);
}
}
@@ -1264,7 +1338,7 @@ void trackLatency(GdkEvent const *event)
GtkWidget *SPCanvas::createAA()
{
- SPCanvas *canvas = reinterpret_cast<SPCanvas *>(g_object_new(SPCanvas::getType(), NULL));
+ SPCanvas *canvas = SP_CANVAS(g_object_new(SP_TYPE_CANVAS, NULL));
return GTK_WIDGET(canvas);
}
@@ -1279,8 +1353,12 @@ void SPCanvasImpl::realize(GtkWidget *widget)
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
- attributes.visual = gdk_rgb_get_visual ();
- attributes.colormap = gdk_rgb_get_cmap ();
+ attributes.visual = gdk_visual_get_system();
+
+#if !GTK_CHECK_VERSION(3,0,0)
+ attributes.colormap = gdk_colormap_get_system();
+#endif
+
attributes.event_mask = (gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK |
GDK_BUTTON_PRESS_MASK |
@@ -1294,20 +1372,33 @@ void SPCanvasImpl::realize(GtkWidget *widget)
GDK_KEY_RELEASE_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK |
+ GDK_SCROLL_MASK |
GDK_FOCUS_CHANGE_MASK);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+#else
gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+#endif
GdkWindow *window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
gtk_widget_set_window (widget, window);
gdk_window_set_user_data (window, widget);
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- if ( prefs->getBool("/options/useextinput/value", true) )
+ if (prefs->getBool("/options/useextinput/value", true)) {
gtk_widget_set_events(widget, attributes.event_mask);
+#if !GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_set_extension_events(widget, GDK_EXTENSION_EVENTS_ALL);
+ // TODO: Extension event stuff has been deprecated in GTK+ 3
+#endif
+ }
+#if !GTK_CHECK_VERSION(3,0,0)
+ // This does nothing in GTK+ 3
GtkStyle *style = gtk_widget_get_style (widget);
-
gtk_widget_set_style (widget, gtk_style_attach (style, window));
+#endif
gtk_widget_set_realized (widget, TRUE);
}
@@ -1320,12 +1411,28 @@ void SPCanvasImpl::unrealize(GtkWidget *widget)
canvas->grabbed_item = NULL;
canvas->focused_item = NULL;
- shutdown_transients(canvas);
+ sp_canvas_shutdown_transients(canvas);
+
+ if (GTK_WIDGET_CLASS(sp_canvas_parent_class)->unrealize)
+ (* GTK_WIDGET_CLASS(sp_canvas_parent_class)->unrealize)(widget);
+}
+
- if (GTK_WIDGET_CLASS(parentClass)->unrealize)
- (* GTK_WIDGET_CLASS(parentClass)->unrealize)(widget);
+#if GTK_CHECK_VERSION(3,0,0)
+void SPCanvasImpl::getPreferredWidth(GtkWidget *widget, gint *minimum_width, gint *natural_width)
+{
+ static_cast<void>(SP_CANVAS (widget));
+ *minimum_width = 256;
+ *natural_width = 256;
}
+void SPCanvasImpl::getPreferredHeight(GtkWidget *widget, gint *minimum_height, gint *natural_height)
+{
+ static_cast<void>(SP_CANVAS (widget));
+ *minimum_height = 256;
+ *natural_height = 256;
+}
+#else
void SPCanvasImpl::sizeRequest(GtkWidget *widget, GtkRequisition *req)
{
static_cast<void>(SP_CANVAS (widget));
@@ -1333,6 +1440,8 @@ void SPCanvasImpl::sizeRequest(GtkWidget *widget, GtkRequisition *req)
req->width = 256;
req->height = 256;
}
+#endif
+
void SPCanvasImpl::sizeAllocate(GtkWidget *widget, GtkAllocation *allocation)
{
@@ -1341,8 +1450,9 @@ void SPCanvasImpl::sizeAllocate(GtkWidget *widget, GtkAllocation *allocation)
gtk_widget_get_allocation (widget, &widg_allocation);
- Geom::IntRect old_area = Geom::IntRect::from_xywh(canvas->x0, canvas->y0,
- widg_allocation.width, widg_allocation.height);
+// Geom::IntRect old_area = Geom::IntRect::from_xywh(canvas->x0, canvas->y0,
+// widg_allocation.width, widg_allocation.height);
+
Geom::IntRect new_area = Geom::IntRect::from_xywh(canvas->x0, canvas->y0,
allocation->width, allocation->height);
@@ -1403,7 +1513,10 @@ int SPCanvasImpl::emitEvent(SPCanvas *canvas, GdkEvent *event)
mask = GDK_KEY_RELEASE_MASK;
break;
case GDK_SCROLL:
- mask = GDK_SCROLL;
+ mask = GDK_SCROLL_MASK;
+#if GTK_CHECK_VERSION(3,0,0)
+ mask |= GDK_SMOOTH_SCROLL_MASK;
+#endif
break;
default:
mask = 0;
@@ -1415,27 +1528,31 @@ int SPCanvasImpl::emitEvent(SPCanvas *canvas, GdkEvent *event)
// Convert to world coordinates -- we have two cases because of different
// offsets of the fields in the event structures.
- //
- GdkEvent ev = *event;
+ GdkEvent *ev = gdk_event_copy(event);
- switch (ev.type) {
+ switch (ev->type) {
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
- ev.crossing.x += canvas->x0;
- ev.crossing.y += canvas->y0;
+ ev->crossing.x += canvas->x0;
+ ev->crossing.y += canvas->y0;
break;
case GDK_MOTION_NOTIFY:
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
- ev.motion.x += canvas->x0;
- ev.motion.y += canvas->y0;
+ ev->motion.x += canvas->x0;
+ ev->motion.y += canvas->y0;
break;
default:
break;
}
+ // Block Undo and Redo while we drag /anything/
+ if(event->type == GDK_BUTTON_PRESS && event->button.button == 1)
+ canvas->is_dragging = true;
+ else if(event->type == GDK_BUTTON_RELEASE)
+ canvas->is_dragging = false;
// Choose where we send the event
@@ -1479,19 +1596,20 @@ int SPCanvasImpl::emitEvent(SPCanvas *canvas, GdkEvent *event)
while (item && !finished) {
g_object_ref (item);
- g_signal_emit (G_OBJECT (item), item_signals[ITEM_EVENT], 0, &ev, &finished);
+ g_signal_emit (G_OBJECT (item), item_signals[ITEM_EVENT], 0, ev, &finished);
SPCanvasItem *parent = item->parent;
g_object_unref (item);
item = parent;
}
+ gdk_event_free(ev);
+
return finished;
}
int SPCanvasImpl::pickCurrentItem(SPCanvas *canvas, GdkEvent *event)
{
int button_down = 0;
- double x, y;
if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display!
return FALSE;
@@ -1551,6 +1669,7 @@ int SPCanvasImpl::pickCurrentItem(SPCanvas *canvas, GdkEvent *event)
// LeaveNotify means that there is no current item, so we don't look for one
if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
// these fields don't have the same offsets in both types of events
+ double x, y;
if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
x = canvas->pick_event.crossing.x;
@@ -1694,7 +1813,13 @@ gint SPCanvasImpl::handleScroll(GtkWidget *widget, GdkEventScroll *event)
}
static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
+#if GTK_CHECK_VERSION(3,0,0)
+ gdk_window_get_device_position(w,
+ gdk_event_get_device((GdkEvent *)(event)),
+ NULL, NULL, NULL);
+#else
gdk_window_get_pointer(w, NULL, NULL, NULL);
+#endif
gdk_event_request_motions(event);
}
@@ -1725,7 +1850,6 @@ int SPCanvasImpl::handleMotion(GtkWidget *widget, GdkEventMotion *event)
void SPCanvasImpl::sp_canvas_paint_single_buffer(SPCanvas *canvas, Geom::IntRect const &paint_rect, Geom::IntRect const &canvas_rect, int /*sw*/)
{
GtkWidget *widget = GTK_WIDGET (canvas);
- GtkStyle *style;
// Mark the region clean
sp_canvas_mark_rect(canvas, paint_rect, 0);
@@ -1761,8 +1885,18 @@ void SPCanvasImpl::sp_canvas_paint_single_buffer(SPCanvas *canvas, Geom::IntRect
//cairo_stroke_preserve(buf.ct);
//cairo_clip(buf.ct);
- style = gtk_widget_get_style (widget);
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context = gtk_widget_get_style_context(widget);
+ GdkRGBA color;
+ gtk_style_context_get_background_color(context,
+ gtk_widget_get_state_flags(widget),
+ &color);
+ gdk_cairo_set_source_rgba(buf.ct, &color);
+#else
+ GtkStyle *style = gtk_widget_get_style (widget);
gdk_cairo_set_source_color(buf.ct, &style->bg[GTK_STATE_NORMAL]);
+#endif
+
cairo_set_operator(buf.ct, CAIRO_OPERATOR_SOURCE);
//cairo_rectangle(buf.ct, 0, 0, paint_rect.width(), paint_rec.height());
cairo_paint(buf.ct);
@@ -1962,7 +2096,18 @@ bool SPCanvasImpl::sp_canvas_paint_rect(SPCanvas *canvas, int xx0, int yy0, int
// Save the mouse location
gint x, y;
+
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default());
+ GdkDevice *device = gdk_device_manager_get_client_pointer(dm);
+
+ gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(canvas)),
+ device,
+ &x, &y, NULL);
+#else
gdk_window_get_pointer (gtk_widget_get_window (GTK_WIDGET(canvas)), &x, &y, NULL);
+#endif
+
setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y));
if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) {
@@ -1993,7 +2138,25 @@ void SPCanvas::endForcedFullRedraws()
forced_redraw_limit = -1;
}
-gint SPCanvasImpl::handleExpose(GtkWidget *widget, GdkEventExpose *event)
+#if GTK_CHECK_VERSION(3,0,0)
+gboolean SPCanvasImpl::handleDraw(GtkWidget *widget, cairo_t *cr) {
+ SPCanvas *canvas = SP_CANVAS(widget);
+
+ cairo_rectangle_list_t *rects = cairo_copy_clip_rectangle_list(cr);
+
+ for (int i = 0; i < rects->num_rectangles; i++) {
+ cairo_rectangle_t rectangle = rects->rectangles[i];
+
+ Geom::IntRect r = Geom::IntRect::from_xywh(rectangle.x + canvas->x0, rectangle.y + canvas->y0,
+ rectangle.width, rectangle.height);
+
+ canvas->requestRedraw(r.left(), r.top(), r.right(), r.bottom());
+ }
+
+ return FALSE;
+}
+#else
+gboolean SPCanvasImpl::handleExpose(GtkWidget *widget, GdkEventExpose *event)
{
SPCanvas *canvas = SP_CANVAS(widget);
@@ -2005,25 +2168,23 @@ gint SPCanvasImpl::handleExpose(GtkWidget *widget, GdkEventExpose *event)
int n_rects = 0;
GdkRectangle *rects = NULL;
gdk_region_get_rectangles(event->region, &rects, &n_rects);
-
- if ((rects == NULL) || (n_rects == 0))
- {
+
+ if(rects == NULL)
return FALSE;
- }
- else
- {
- for (int i = 0; i < n_rects; i++) {
- Geom::IntRect r = Geom::IntRect::from_xywh(
- rects[i].x + canvas->x0, rects[i].y + canvas->y0,
- rects[i].width, rects[i].height);
+
+ for (int i = 0; i < n_rects; i++) {
+ GdkRectangle rectangle = rects[i];
+
+ Geom::IntRect r = Geom::IntRect::from_xywh(rectangle.x + canvas->x0, rectangle.y + canvas->y0,
+ rectangle.width, rectangle.height);
- canvas->requestRedraw(r.left(), r.top(), r.right(), r.bottom());
- }
-
- g_free (rects);
- return FALSE;
+ canvas->requestRedraw(r.left(), r.top(), r.right(), r.bottom());
}
+
+ return FALSE;
}
+#endif
+
gint SPCanvasImpl::handleKeyEvent(GtkWidget *widget, GdkEventKey *event)
{
@@ -2077,28 +2238,30 @@ int SPCanvasImpl::paint(SPCanvas *canvas)
return TRUE;
}
- Gdk::Region to_paint;
+ Cairo::RefPtr<Cairo::Region> to_paint = Cairo::Region::create();
for (int j=canvas->tTop; j<canvas->tBottom; j++) {
for (int i=canvas->tLeft; i<canvas->tRight; i++) {
int tile_index = (i - canvas->tLeft) + (j - canvas->tTop)*canvas->tileH;
if ( canvas->tiles[tile_index] ) { // if this tile is dirtied (nonzero)
- to_paint.union_with_rect(Gdk::Rectangle(i*TILE_SIZE, j*TILE_SIZE,
- TILE_SIZE, TILE_SIZE));
+ Cairo::RectangleInt rect = {i*TILE_SIZE, j*TILE_SIZE,
+ TILE_SIZE, TILE_SIZE};
+ to_paint->do_union(rect);
}
}
}
- if (!to_paint.empty()) {
- Glib::ArrayHandle<Gdk::Rectangle> rect = to_paint.get_rectangles();
- typedef Glib::ArrayHandle<Gdk::Rectangle>::const_iterator Iter;
- for (Iter i=rect.begin(); i != rect.end(); ++i) {
- int x0 = (*i).get_x();
- int y0 = (*i).get_y();
- int x1 = x0 + (*i).get_width();
- int y1 = y0 + (*i).get_height();
+ int n_rect = to_paint->get_num_rectangles();
+
+ if (n_rect > 0) {
+ for (int i=0; i < n_rect; i++) {
+ Cairo::RectangleInt rect = to_paint->get_rectangle(i);
+ int x0 = rect.x;
+ int y0 = rect.y;
+ int x1 = x0 + rect.width;
+ int y1 = y0 + rect.height;
if (!sp_canvas_paint_rect(canvas, x0, y0, x1, y1)) {
// Aborted
return FALSE;
@@ -2148,8 +2311,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);
@@ -2159,15 +2320,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);
}
}
diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h
index f0366a2e5..72ae4b6bc 100644
--- a/src/display/sp-canvas.h
+++ b/src/display/sp-canvas.h
@@ -39,7 +39,7 @@
G_BEGIN_DECLS
-#define SP_TYPE_CANVAS (SPCanvas::getType())
+#define SP_TYPE_CANVAS (sp_canvas_get_type())
#define SP_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_CANVAS, SPCanvas))
#define SP_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_CANVAS))
@@ -71,14 +71,14 @@ G_END_DECLS
class SPCanvasImpl;
+GType sp_canvas_get_type() G_GNUC_CONST;
+
/**
* Port of GnomeCanvas for inkscape needs.
*/
struct SPCanvas {
friend class SPCanvasImpl;
- static GType getType();
-
/**
* Returns new canvas as widget.
*/
@@ -124,6 +124,7 @@ struct SPCanvas {
SPCanvasItem *root;
+ bool is_dragging;
double dx0;
double dy0;
int x0;
diff --git a/src/display/sp-ctrlcurve.cpp b/src/display/sp-ctrlcurve.cpp
new file mode 100644
index 000000000..d9b687f2e
--- /dev/null
+++ b/src/display/sp-ctrlcurve.cpp
@@ -0,0 +1,216 @@
+/*
+ * Simple bezier curve used for Mesh Gradients
+ *
+ * Author:
+ * Tavmjong Bah <tavmjong@free.fr>
+ *
+ * Copyright (C) 2011 Tavmjong Bah
+ * Copyright (C) 2007 Johan Engelen
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "display/sp-ctrlcurve.h"
+#include "display/sp-canvas-util.h"
+#include "display/cairo-utils.h"
+#include "color.h"
+#include "display/sp-canvas.h"
+
+namespace {
+
+static void sp_ctrlcurve_class_init(SPCtrlCurveClass *klass, gpointer data);
+static void sp_ctrlcurve_init(SPCtrlCurve *ctrlcurve, gpointer g_class);
+static void sp_ctrlcurve_destroy(SPCanvasItem *object);
+
+static void sp_ctrlcurve_update(SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
+static void sp_ctrlcurve_render(SPCanvasItem *item, SPCanvasBuf *buf);
+
+static SPCanvasItemClass *parent_class;
+
+} // namespace
+
+GType SPCtrlCurve::getType()
+{
+ static GType type = 0;
+ if (!type) {
+ GTypeInfo info = {
+ sizeof(SPCtrlCurveClass),
+ NULL, NULL,
+ reinterpret_cast<GClassInitFunc>(sp_ctrlcurve_class_init),
+ NULL, NULL,
+ sizeof(SPCtrlCurve),
+ 0,
+ reinterpret_cast<GInstanceInitFunc>(sp_ctrlcurve_init),
+ NULL
+ };
+ type = g_type_register_static(SP_TYPE_CANVAS_ITEM, "SPCtrlCurve", &info, static_cast<GTypeFlags>(0));
+ }
+ return type;
+}
+
+namespace {
+
+void sp_ctrlcurve_class_init(SPCtrlCurveClass *klass, gpointer /*g_class*/)
+{
+ parent_class = reinterpret_cast<SPCanvasItemClass*>(g_type_class_peek_parent(klass));
+
+ klass->destroy = sp_ctrlcurve_destroy;
+
+ klass->update = sp_ctrlcurve_update;
+ klass->render = sp_ctrlcurve_render;
+}
+
+static void
+sp_ctrlcurve_init(SPCtrlCurve *ctrlcurve, gpointer /*g_class*/)
+{
+ // Points are initialized to 0,0
+ ctrlcurve->rgba = 0x0000ff7f;
+ ctrlcurve->item=NULL;
+}
+
+static void
+sp_ctrlcurve_destroy(SPCanvasItem *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (SP_IS_CTRLCURVE (object));
+
+ SPCtrlCurve *ctrlcurve = SP_CTRLCURVE (object);
+
+ ctrlcurve->item=NULL;
+
+ if (SP_CANVAS_ITEM_CLASS (parent_class)->destroy)
+ (* SP_CANVAS_ITEM_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+sp_ctrlcurve_render(SPCanvasItem *item, SPCanvasBuf *buf)
+{
+ SPCtrlCurve *cl = SP_CTRLCURVE (item);
+
+ if (!buf->ct)
+ return;
+
+ if ( cl->p0 == cl->p1 &&
+ cl->p1 == cl->p2 &&
+ cl->p2 == cl->p3 )
+ return;
+
+ ink_cairo_set_source_rgba32(buf->ct, cl->rgba);
+ cairo_set_line_width(buf->ct, 1);
+ cairo_new_path(buf->ct);
+
+ Geom::Point p0 = cl->p0 * cl->affine;
+ Geom::Point p1 = cl->p1 * cl->affine;
+ Geom::Point p2 = cl->p2 * cl->affine;
+ Geom::Point p3 = cl->p3 * cl->affine;
+
+ cairo_move_to (buf->ct, p0[Geom::X] - buf->rect.left(), p0[Geom::Y] - buf->rect.top());
+ cairo_curve_to (buf->ct,
+ p1[Geom::X] - buf->rect.left(), p1[Geom::Y] - buf->rect.top(),
+ p2[Geom::X] - buf->rect.left(), p2[Geom::Y] - buf->rect.top(),
+ p3[Geom::X] - buf->rect.left(), p3[Geom::Y] - buf->rect.top() );
+
+ cairo_stroke(buf->ct);
+}
+
+static void
+sp_ctrlcurve_update(SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags)
+{
+ SPCtrlCurve *cl = SP_CTRLCURVE (item);
+
+ item->canvas->requestRedraw(item->x1, item->y1, item->x2, item->y2);
+
+ if (parent_class->update)
+ (* parent_class->update) (item, affine, flags);
+
+ sp_canvas_item_reset_bounds (item);
+
+ cl->affine = affine;
+
+ if (cl->p0 == cl->p1 && cl->p1 == cl->p2 && cl->p2 == cl->p3 ) {
+ item->x1 = item->x2 = item->y1 = item->y2 = 0;
+ } else {
+
+ Geom::Point p0 = cl->p0 * affine;
+ Geom::Point p1 = cl->p1 * affine;
+ Geom::Point p2 = cl->p2 * affine;
+ Geom::Point p3 = cl->p3 * affine;
+
+ double min_x = p0[Geom::X];
+ double min_y = p0[Geom::Y];
+ double max_x = p0[Geom::X];
+ double max_y = p0[Geom::Y];
+
+ min_x = MIN( min_x, p1[Geom::X] );
+ min_y = MIN( min_y, p1[Geom::Y] );
+ max_x = MAX( max_x, p1[Geom::X] );
+ max_y = MAX( max_y, p1[Geom::Y] );
+
+ min_x = MIN( min_x, p2[Geom::X] );
+ min_y = MIN( min_y, p2[Geom::Y] );
+ max_x = MAX( max_x, p2[Geom::X] );
+ max_y = MAX( max_y, p2[Geom::Y] );
+
+ min_x = MIN( min_x, p3[Geom::X] );
+ min_y = MIN( min_y, p3[Geom::Y] );
+ max_x = MAX( max_x, p3[Geom::X] );
+ max_y = MAX( max_y, p3[Geom::Y] );
+
+ item->x1 = round( min_x - 1 );
+ item->y1 = round( min_y - 1 );
+ item->x2 = round( max_x + 1 );
+ item->y2 = round( max_y + 1 );
+
+ item->canvas->requestRedraw(item->x1, item->y1, item->x2, item->y2);
+
+ }
+}
+
+} // namespace
+
+#define EPSILON 1e-6
+#define DIFFER(a,b) (fabs ((a) - (b)) > EPSILON)
+
+void SPCtrlCurve::setCoords( gdouble x0, gdouble y0, gdouble x1, gdouble y1,
+ gdouble x2, gdouble y2, gdouble x3, gdouble y3 )
+{
+
+ Geom::Point q0( x0, y0 );
+ Geom::Point q1( x1, y1 );
+ Geom::Point q2( x2, y2 );
+ Geom::Point q3( x3, y3 );
+
+ setCoords( q0, q1, q2, q3 );
+
+}
+
+void SPCtrlCurve::setCoords( Geom::Point const &q0, Geom::Point const &q1,
+ Geom::Point const &q2, Geom::Point const &q3)
+{
+ if (DIFFER(p0[Geom::X], q0[Geom::X]) || DIFFER(p0[Geom::Y], q0[Geom::Y]) ||
+ DIFFER(p1[Geom::X], q1[Geom::X]) || DIFFER(p1[Geom::Y], q1[Geom::Y]) ||
+ DIFFER(p2[Geom::X], q2[Geom::X]) || DIFFER(p2[Geom::Y], q2[Geom::Y]) ||
+ DIFFER(p3[Geom::X], q3[Geom::X]) || DIFFER(p3[Geom::Y], q3[Geom::Y]) ) {
+ p0 = q0;
+ p1 = q1;
+ p2 = q2;
+ p3 = q3;
+ sp_canvas_item_request_update( this );
+ }
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/display/sp-ctrlcurve.h b/src/display/sp-ctrlcurve.h
new file mode 100644
index 000000000..90936185c
--- /dev/null
+++ b/src/display/sp-ctrlcurve.h
@@ -0,0 +1,54 @@
+#ifndef SEEN_INKSCAPE_CTRLCURVE_H
+#define SEEN_INKSCAPE_CTRLCURVE_H
+
+/*
+ * Simple bezier curve (used for Mesh Gradients)
+ *
+ * Author:
+ * Tavmjong Bah <tavmjong@free.fr>
+ * Derived from sp-ctrlline
+ *
+ * Copyright (C) 2011 Tavmjong Bah
+ * Copyright (C) 2007 Johan Engelen
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ *
+ * Released under GNU GPL
+ */
+
+#include "display/sp-ctrlline.h"
+
+class SPItem;
+
+#define SP_TYPE_CTRLCURVE (SPCtrlCurve::getType())
+#define SP_CTRLCURVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CTRLCURVE, SPCtrlCurve))
+#define SP_IS_CTRLCURVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CTRLCURVE))
+
+struct SPCtrlCurve : public SPCtrlLine {
+
+ static GType getType();
+
+ void setCoords( gdouble x0, gdouble y0, gdouble x1, gdouble y1,
+ gdouble x2, gdouble y2, gdouble x3, gdouble y3 );
+
+ void setCoords( Geom::Point const &q0, Geom::Point const &q1,
+ Geom::Point const &q2, Geom::Point const &q3);
+
+ Geom::Point p0, p1, p2, p3;
+};
+
+struct SPCtrlCurveClass : public SPCtrlLineClass{};
+
+
+
+#endif // SEEN_INKSCAPE_CTRLCURVE_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/display/sp-ctrlline.cpp b/src/display/sp-ctrlline.cpp
index 11d0b34f8..aef284c1a 100644
--- a/src/display/sp-ctrlline.cpp
+++ b/src/display/sp-ctrlline.cpp
@@ -32,7 +32,7 @@ namespace {
void sp_ctrlline_class_init(SPCtrlLineClass *klass, gpointer data);
void sp_ctrlline_init(SPCtrlLine *ctrlline, gpointer g_class);
-void sp_ctrlline_destroy(GtkObject *object);
+void sp_ctrlline_destroy(SPCanvasItem *object);
void sp_ctrlline_update(SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
void sp_ctrlline_render(SPCanvasItem *item, SPCanvasBuf *buf);
@@ -79,7 +79,7 @@ void sp_ctrlline_init(SPCtrlLine *ctrlline, gpointer /*g_class*/)
ctrlline->item=NULL;
}
-void sp_ctrlline_destroy(GtkObject *object)
+void sp_ctrlline_destroy(SPCanvasItem *object)
{
g_return_if_fail(object != NULL);
g_return_if_fail(SP_IS_CTRLLINE(object));
@@ -88,8 +88,8 @@ void sp_ctrlline_destroy(GtkObject *object)
ctrlline->item = NULL;
- if (GTK_OBJECT_CLASS (parent_class)->destroy) {
- (* GTK_OBJECT_CLASS (parent_class)->destroy)(object);
+ if(SP_CANVAS_ITEM_CLASS (parent_class)->destroy) {
+ (* SP_CANVAS_ITEM_CLASS (parent_class)->destroy)(object);
}
}
@@ -105,15 +105,25 @@ void sp_ctrlline_render(SPCanvasItem *item, SPCanvasBuf *buf)
return;
}
+ Geom::Point s = cl->s * cl->affine;
+ Geom::Point e = cl->e * cl->affine;
+
+ ink_cairo_set_source_rgba32(buf->ct, 0xffffffbf);
+ cairo_set_line_width(buf->ct, 2);
+ cairo_new_path(buf->ct);
+
+ cairo_move_to(buf->ct, s[Geom::X] - buf->rect.left(), s[Geom::Y] - buf->rect.top());
+ cairo_line_to(buf->ct, e[Geom::X] - buf->rect.left(), e[Geom::Y] - buf->rect.top());
+
+ cairo_stroke(buf->ct);
+
+
ink_cairo_set_source_rgba32(buf->ct, cl->rgba);
cairo_set_line_width(buf->ct, 1);
cairo_new_path(buf->ct);
- Geom::Point s = cl->s * cl->affine;
- Geom::Point e = cl->e * cl->affine;
-
- cairo_move_to (buf->ct, s[Geom::X] - buf->rect.left(), s[Geom::Y] - buf->rect.top());
- cairo_line_to (buf->ct, e[Geom::X] - buf->rect.left(), e[Geom::Y] - buf->rect.top());
+ cairo_move_to(buf->ct, s[Geom::X] - buf->rect.left(), s[Geom::Y] - buf->rect.top());
+ cairo_line_to(buf->ct, e[Geom::X] - buf->rect.left(), e[Geom::Y] - buf->rect.top());
cairo_stroke(buf->ct);
}
diff --git a/src/display/sp-ctrlpoint.cpp b/src/display/sp-ctrlpoint.cpp
index 3c8e2bc5d..026cc7589 100644
--- a/src/display/sp-ctrlpoint.cpp
+++ b/src/display/sp-ctrlpoint.cpp
@@ -23,7 +23,7 @@
static void sp_ctrlpoint_class_init (SPCtrlPointClass *klass);
static void sp_ctrlpoint_init (SPCtrlPoint *ctrlpoint);
-static void sp_ctrlpoint_destroy (GtkObject *object);
+static void sp_ctrlpoint_destroy(SPCanvasItem *object);
static void sp_ctrlpoint_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
static void sp_ctrlpoint_render (SPCanvasItem *item, SPCanvasBuf *buf);
@@ -50,16 +50,13 @@ sp_ctrlpoint_get_type (void)
return type;
}
-static void
-sp_ctrlpoint_class_init (SPCtrlPointClass *klass)
+static void sp_ctrlpoint_class_init(SPCtrlPointClass *klass)
{
- GtkObjectClass *object_class = (GtkObjectClass *) klass;
- SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
-
- parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass);
+ SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(klass);
- object_class->destroy = sp_ctrlpoint_destroy;
+ parent_class = SP_CANVAS_ITEM_CLASS(g_type_class_peek_parent(klass));
+ item_class->destroy = sp_ctrlpoint_destroy;
item_class->update = sp_ctrlpoint_update;
item_class->render = sp_ctrlpoint_render;
}
@@ -73,8 +70,7 @@ sp_ctrlpoint_init (SPCtrlPoint *ctrlpoint)
ctrlpoint->radius = 2;
}
-static void
-sp_ctrlpoint_destroy (GtkObject *object)
+static void sp_ctrlpoint_destroy(SPCanvasItem *object)
{
g_return_if_fail (object != NULL);
g_return_if_fail (SP_IS_CTRLPOINT (object));
@@ -83,8 +79,8 @@ sp_ctrlpoint_destroy (GtkObject *object)
ctrlpoint->item=NULL;
- if (GTK_OBJECT_CLASS (parent_class)->destroy)
- (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+ if (SP_CANVAS_ITEM_CLASS(parent_class)->destroy)
+ (* SP_CANVAS_ITEM_CLASS(parent_class)->destroy) (object);
}
static void
diff --git a/src/display/sp-ctrlquadr.cpp b/src/display/sp-ctrlquadr.cpp
index 723075f74..b6a0da109 100644
--- a/src/display/sp-ctrlquadr.cpp
+++ b/src/display/sp-ctrlquadr.cpp
@@ -31,7 +31,7 @@ struct SPCtrlQuadrClass : public SPCanvasItemClass{};
static void sp_ctrlquadr_class_init (SPCtrlQuadrClass *klass);
static void sp_ctrlquadr_init (SPCtrlQuadr *ctrlquadr);
-static void sp_ctrlquadr_destroy (GtkObject *object);
+static void sp_ctrlquadr_destroy(SPCanvasItem *object);
static void sp_ctrlquadr_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags);
static void sp_ctrlquadr_render (SPCanvasItem *item, SPCanvasBuf *buf);
@@ -61,13 +61,11 @@ sp_ctrlquadr_get_type (void)
static void
sp_ctrlquadr_class_init (SPCtrlQuadrClass *klass)
{
- GtkObjectClass *object_class = (GtkObjectClass *) klass;
- SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass;
+ SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(klass);
- parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass);
-
- object_class->destroy = sp_ctrlquadr_destroy;
+ parent_class = SP_CANVAS_ITEM_CLASS(g_type_class_peek_parent(klass));
+ item_class->destroy = sp_ctrlquadr_destroy;
item_class->update = sp_ctrlquadr_update;
item_class->render = sp_ctrlquadr_render;
}
@@ -82,14 +80,13 @@ sp_ctrlquadr_init (SPCtrlQuadr *ctrlquadr)
ctrlquadr->p4 = Geom::Point(0, 0);
}
-static void
-sp_ctrlquadr_destroy (GtkObject *object)
+static void sp_ctrlquadr_destroy(SPCanvasItem *object)
{
g_return_if_fail (object != NULL);
g_return_if_fail (SP_IS_CTRLQUADR (object));
- if (GTK_OBJECT_CLASS (parent_class)->destroy)
- (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+ if (SP_CANVAS_ITEM_CLASS(parent_class)->destroy)
+ (* SP_CANVAS_ITEM_CLASS(parent_class)->destroy) (object);
}
static void
diff --git a/src/display/sp-ctrlquadr.h b/src/display/sp-ctrlquadr.h
index 9fdfd29b3..1dfb06456 100644
--- a/src/display/sp-ctrlquadr.h
+++ b/src/display/sp-ctrlquadr.h
@@ -12,9 +12,7 @@
* Released under GNU GPL
*/
-#include "sp-canvas.h"
-
-
+#include <2geom/geom.h>
#define SP_TYPE_CTRLQUADR (sp_ctrlquadr_get_type ())
#define SP_CTRLQUADR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CTRLQUADR, SPCtrlQuadr))