diff options
Diffstat (limited to 'src/display')
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)) |
