diff options
| author | Markus Engel <markus.engel@tum.de> | 2013-03-29 23:52:42 +0000 |
|---|---|---|
| committer | Markus Engel <markus.engel@tum.de> | 2013-03-29 23:52:42 +0000 |
| commit | a168040d5a452544328a1e6ad35aaac351f94d44 (patch) | |
| tree | fae1ba829f543a473da281bd5fa6e4deabbf6912 /src/display | |
| parent | Removed function pointers from SPObject and subclasses. (diff) | |
| parent | Dutch translation update (diff) | |
| download | inkscape-a168040d5a452544328a1e6ad35aaac351f94d44.tar.gz inkscape-a168040d5a452544328a1e6ad35aaac351f94d44.zip | |
merged from trunk
(bzr r11608.1.56)
Diffstat (limited to 'src/display')
62 files changed, 1203 insertions, 448 deletions
diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert index cf211108f..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,6 +106,8 @@ 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 \ 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..fc56c7b26 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -24,7 +24,20 @@ #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" + +namespace { + +/** + * Key for cairo_surface_t to keep track of current color interpolation value + * Only the address of the structure is used, it is never initialized. See: + * http://www.cairographics.org/manual/cairo-Types.html#cairo-user-data-key-t + */ +cairo_user_data_key_t ci_key; + +} // namespace namespace Inkscape { @@ -276,6 +289,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, &ci_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, &ci_key, GINT_TO_POINTER (ci), NULL); + } +} + +void +copy_cairo_surface_ci(cairo_surface_t *in, cairo_surface_t *out) { + cairo_surface_set_user_data(out, &ci_key, cairo_surface_get_user_data(in, &ci_key), NULL); +} + void ink_cairo_set_source_rgba32(cairo_t *ct, guint32 rgba) { @@ -395,6 +445,7 @@ cairo_surface_t * ink_cairo_surface_create_identical(cairo_surface_t *s) { cairo_surface_t *ns = ink_cairo_surface_create_same_size(s, cairo_surface_get_content(s)); + cairo_surface_set_user_data(ns, &ci_key, cairo_surface_get_user_data(s, &ci_key), NULL); return ns; } @@ -547,6 +598,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() { @@ -715,4 +888,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..2596cd969 100644 --- a/src/display/cairo-utils.h +++ b/src/display/cairo-utils.h @@ -15,6 +15,7 @@ #include <glib.h> #include <cairomm/cairomm.h> #include <2geom/forward.h> +#include "style.h" struct SPColor; struct _GdkPixbuf; @@ -81,6 +82,11 @@ public: } // namespace Inkscape +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); @@ -102,6 +108,10 @@ 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(); void convert_pixels_pixbuf_to_argb32(guchar *data, int w, int h, int rs); @@ -118,13 +128,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 +154,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 diff --git a/src/display/canvas-arena.cpp b/src/display/canvas-arena.cpp index f0fd63ef4..8e25c1843 100644 --- a/src/display/canvas-arena.cpp +++ b/src/display/canvas-arena.cpp @@ -316,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); diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index d4e15f475..170684328 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,46 +13,45 @@ * 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 + +#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 "helper/units.h" -#include <gtkmm/box.h> -#include <gtkmm/label.h> -#include <gtkmm/table.h> 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 @@ -93,28 +92,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; @@ -138,9 +169,9 @@ CanvasAxonomGrid::CanvasAxonomGrid (SPNamedView * nv, Inkscape::XML::Node * in_r 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); @@ -251,17 +282,17 @@ CanvasAxonomGrid::readRepr() 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]); } @@ -316,14 +347,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); @@ -477,8 +511,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; @@ -526,8 +560,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); @@ -536,18 +574,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); + } } } @@ -575,8 +616,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); @@ -585,17 +630,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); + } } } diff --git a/src/display/canvas-axonomgrid.h b/src/display/canvas-axonomgrid.h index 04919f947..f58ea3aca 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; 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,7 +68,7 @@ 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; 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 498958f08..88da22835 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -13,6 +13,19 @@ * Don't be shy to correct things. */ +#if HAVE_CONFIG_H +# include "config.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" @@ -37,10 +50,6 @@ #include "verbs.h" #include "display/sp-canvas.h" -#include <gtkmm/box.h> -#include <gtkmm/label.h> -#include <gtkmm/table.h> - using Inkscape::DocumentUndo; namespace Inkscape { @@ -184,19 +193,19 @@ CanvasGrid::~CanvasGrid() } 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; } @@ -275,9 +284,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; @@ -351,12 +360,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); } @@ -366,10 +376,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; @@ -410,29 +420,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; @@ -688,7 +729,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) ); @@ -715,9 +763,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); @@ -735,7 +781,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, @@ -775,10 +820,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; } diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h index bba9b7e95..7eaef407f 100644 --- a/src/display/canvas-grid.h +++ b/src/display/canvas-grid.h @@ -61,9 +61,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); @@ -97,8 +97,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); diff --git a/src/display/canvas-text.cpp b/src/display/canvas-text.cpp index ddc946d5d..fe60d9c65 100644 --- a/src/display/canvas-text.cpp +++ b/src/display/canvas-text.cpp @@ -58,9 +58,9 @@ sp_canvastext_get_type (void) static void sp_canvastext_class_init(SPCanvasTextClass *klass) { - SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass; + SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(klass); - parent_class_ct = (SPCanvasItemClass*)g_type_class_peek_parent (klass); + 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; diff --git a/src/display/curve.cpp b/src/display/curve.cpp index 1a788b59a..ae243853e 100644 --- a/src/display/curve.cpp +++ b/src/display/curve.cpp @@ -150,7 +150,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.h b/src/display/drawing-context.h index fb6662202..b8c4667c2 100644 --- a/src/display/drawing-context.h +++ b/src/display/drawing-context.h @@ -101,6 +101,8 @@ public: 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.h b/src/display/drawing-group.h index 974b3c977..775fec8c4 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 { diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 0c8ac9681..3f1a86ee7 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -23,6 +23,7 @@ DrawingImage::DrawingImage(Drawing &drawing) , _pixbuf(NULL) , _surface(NULL) , _style(NULL) + , _new_surface(NULL) {} DrawingImage::~DrawingImage() @@ -30,6 +31,7 @@ DrawingImage::~DrawingImage() if (_style) sp_style_unref(_style); if (_pixbuf) { + if (_new_surface) cairo_surface_destroy(_new_surface); cairo_surface_destroy(_surface); g_object_unref(_pixbuf); } @@ -118,26 +120,126 @@ unsigned DrawingImage::_renderItem(DrawingContext &ct, Geom::IntRect const &/*ar 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); + ///////////////////////////////////////////////////////////////////////////// + // BEGIN: Hack to avoid Cairo bug + // The total transform (which is RIGHT-multiplied with the item points to get display points) equals: + // scale*translate_origin*_ctm = scale*translate(origin)*expansion*expansionInv*_ctm + // = scale*expansion*translate(origin*expansion)*expansionInv*_ctm + // To avoid a Cairo bug, we handle the scale*expansion part ourselves. + // See https://bugs.launchpad.net/inkscape/+bug/804162 + + Geom::Scale expansion(_ctm.expansion()); + int orgwidth = cairo_image_surface_get_width(_surface); + int orgheight = cairo_image_surface_get_height(_surface); + + if (_scale[Geom::X]*expansion[Geom::X]*orgwidth*255.0<1.0 || _scale[Geom::Y]*expansion[Geom::Y]*orgheight*255.0<1.0) { + // Resized image too small to actually see anything + return RENDER_OK; + } + + // Split scale*expansion in a part that is <= 1.0 and a part that is >= 1.0. We only take care of the part <= 1.0. + Geom::Scale scaleExpansionSmall(std::min<Geom::Coord>(fabs(_scale[Geom::X]*expansion[Geom::X]),1),std::min<Geom::Coord>(fabs(_scale[Geom::Y]*expansion[Geom::Y]),1)); + Geom::Scale scaleExpansionLarge(_scale[Geom::X]*expansion[Geom::X]/scaleExpansionSmall[Geom::X],_scale[Geom::Y]*expansion[Geom::Y]/scaleExpansionSmall[Geom::Y]); + + Geom::Point newSize(Geom::Point(orgwidth,orgheight)*scaleExpansionSmall); + if ((newSize-Geom::Point(orgwidth,orgheight)).length()<0.1) { + // Just use _surface directly. + ct.scale(expansion.inverse()); // This should not include scale (see derivation above) + ct.translate(_origin*expansion); + ct.scale(scaleExpansionLarge); + ct.setSource(_surface, 0, 0); + } else if (!_new_surface || (newSize-_rescaledSize).length()>0.1) { + // Rescaled image is sufficiently different from cached image to recompute + if (_new_surface) cairo_surface_destroy(_new_surface); + _rescaledSize = newSize; + + // This essentially considers an image to be composed of rectangular pixels (box kernel) and computes the least-squares approximation of the original. + // When the scale factor is really large or small this essentially results in using a box filter, while for scale factors approaching 1 it is more like a "tent" kernel. + // Although the quality of the result is not great, it is typically better than an ordinary box filter, and it is guaranteed to preserve the overall brightness of the image. + // The best improvement would probably be to do the same kind of thing based on a tent kernel, but that's quite a bit more complicated, and probably not worth the trouble for a hack like this. + int newwidth = static_cast<int>(floor(orgwidth*scaleExpansionSmall[Geom::X])+1); + int newheight = static_cast<int>(floor(orgheight*scaleExpansionSmall[Geom::Y])+1); + std::vector<int> xBegin(newwidth, -1), yBegin(newheight, -1); + std::vector< std::vector<float> > xCoefs(xBegin.size()), yCoefs(yBegin.size()); + for(int x=0; x<orgwidth; x++) { + double coordBegin = x*static_cast<double>(scaleExpansionSmall[Geom::X]); // x-coord in target coordinates where the current source pixel begins + double coordEnd = (x+1)*static_cast<double>(scaleExpansionSmall[Geom::X]); // x-coord in target coordinates where the current source pixel ends + int begin = static_cast<int>(floor(coordBegin)); // First pixel (x-coord) affected by the current source pixel + int end = static_cast<int>(ceil(coordEnd)); // First pixel (x-coord) NOT affected by the current source pixel (a zero contribution is counted as not affecting the pixel) + for(int nx=begin; nx<end; nx++) { + // Set xBegin if this is the first source pixel contributing to the target pixel. + if (xBegin[nx]==-1) xBegin[nx] = x; + // This computes the fraction of the current target pixel (at nx) that is covered by the source pixel (at x). + xCoefs[nx].push_back(static_cast<float>(std::min<double>(nx+1,coordEnd) - std::max<double>(nx,coordBegin))); + } + } + for(int y=0; y<orgheight; y++) { + double coordBegin = y*static_cast<double>(scaleExpansionSmall[Geom::Y]); // y-coord in target coordinates where the current source pixel begins + double coordEnd = (y+1)*static_cast<double>(scaleExpansionSmall[Geom::Y]); // y-coord in target coordinates where the current source pixel ends + int begin = static_cast<int>(floor(coordBegin)); // First pixel (y-coord) affected by the current source pixel + int end = static_cast<int>(ceil(coordEnd)); // First pixel (y-coord) NOT affected by the current source pixel (a zero contribution is counted as not affecting the pixel) + for(int ny=begin; ny<end; ny++) { + // Set yBegin if this is the first source pixel contributing to the target pixel. + if (yBegin[ny]==-1) yBegin[ny] = y; + // This computes the fraction of the current target pixel (at ny) that is covered by the source pixel (at y). + yCoefs[ny].push_back(static_cast<float>(std::min<double>(ny+1,coordEnd) - std::max<double>(ny,coordBegin))); + } + } + + _new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, newwidth,newheight); + unsigned char * orgdata = cairo_image_surface_get_data(_surface); + unsigned char * newdata = cairo_image_surface_get_data(_new_surface); + int orgstride = cairo_image_surface_get_stride(_surface); + int newstride = cairo_image_surface_get_stride(_new_surface); + + cairo_surface_flush(_surface); + cairo_surface_flush(_new_surface); + + for(int y=0; y<newheight; y++) { + for(int x=0; x<newwidth; x++) { + float tempSum[4] = {0,0,0,0}; + for(int oy=0; oy<static_cast<int>(yCoefs[y].size()); oy++) { + for(int ox=0; ox<static_cast<int>(xCoefs[x].size()); ox++) { + for(int c=0; c<4; c++) { + tempSum[c] += xCoefs[x][ox]*yCoefs[y][oy]*orgdata[c+4*(xBegin[x]+ox)+orgstride*(yBegin[y]+oy)]; + } + } + } + for(int c=0; c<4; c++) { + newdata[c+4*x+newstride*y] = static_cast<unsigned char>(tempSum[c]); + } + } + } + + cairo_surface_mark_dirty(_new_surface); + + ct.scale(expansion.inverse()); // This should not include scale (see derivation above) + ct.translate(_origin*expansion); + ct.scale(scaleExpansionLarge); + ct.setSource(_new_surface, 0, 0); + } else { + // No need to regenerate, but we do draw from _new_surface. + ct.scale(expansion.inverse()); // This should not include scale (see derivation above) + ct.translate(_origin*expansion); + ct.scale(scaleExpansionLarge); + ct.setSource(_new_surface, 0, 0); + } + + // END: Hack to avoid Cairo bug + ///////////////////////////////////////////////////////////////////////////// - cairo_matrix_t tt; - Geom::Affine total; - cairo_get_matrix(ct.raw(), &tt); - ink_matrix_to_2geom(total, tt); + // TODO: If Cairo's problems are gone, uncomment the following: + //ct.translate(_origin); + //ct.scale(_scale); + //ct.setSource(_surface, 0, 0); - 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); - } //ct.paint(_opacity); ct.paint(); diff --git a/src/display/drawing-image.h b/src/display/drawing-image.h index 306096d0e..593185c97 100644 --- a/src/display/drawing-image.h +++ b/src/display/drawing-image.h @@ -45,6 +45,9 @@ protected: cairo_surface_t *_surface; SPStyle *_style; + cairo_surface_t *_new_surface; // Part of hack around Cairo bug + Geom::Point _rescaledSize; // Part of hack around Cairo bug + // TODO: the following three should probably be merged into a new Geom::Viewbox object Geom::Rect _clipbox; ///< for preserveAspectRatio Geom::Point _origin; diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 0fb1f0018..80664d822 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -550,10 +550,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 @@ -667,29 +669,24 @@ DrawingItem::clip(Inkscape::DrawingContext &ct, Geom::IntRect const &area) 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(); - } - + ct.setSource(0,0,0,1); + ct.pushGroup(); // rasterize the clipping path _clipItem(ct, area); - if (_clip) { + // 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 + ct.pushGroup(); + _clip->clip(ct, area); ct.popGroupToSource(); ct.setOperator(CAIRO_OPERATOR_IN); ct.paint(); - ct.popGroupToSource(); - ct.setOperator(CAIRO_OPERATOR_SOURCE); - ct.paint(); } + ct.popGroupToSource(); + ct.setOperator(CAIRO_OPERATOR_OVER); + ct.paint(); + ct.setSource(0,0,0,0); } /** diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index 810a61d9d..4a516512b 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 { diff --git a/src/display/drawing-shape.cpp b/src/display/drawing-shape.cpp index 4ca306092..e80f12486 100644 --- a/src/display/drawing-shape.cpp +++ b/src/display/drawing-shape.cpp @@ -167,7 +167,7 @@ DrawingShape::_renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigne { Inkscape::DrawingContext::Save save(ct); ct.setSource(rgba); ct.setLineWidth(0.5); - ct.setTolerance(1.25); + ct.setTolerance(0.5); ct.stroke(); } } else { diff --git a/src/display/drawing-shape.h b/src/display/drawing-shape.h index ce9bed2eb..52496d86e 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 { diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index 7f63c555a..2a6505c67 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -162,7 +162,7 @@ unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*are guint32 rgba = _drawing.outlinecolor; Inkscape::DrawingContext::Save save(ct); ct.setSource(rgba); - ct.setTolerance(1.25); // low quality, but good enough for outline mode + ct.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); diff --git a/src/display/drawing-text.h b/src/display/drawing-text.h index 929d2bf2d..79b1b097c 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 { diff --git a/src/display/drawing.cpp b/src/display/drawing.cpp index 77f24caf3..c192e4565 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) { @@ -151,6 +172,20 @@ Drawing::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags) if (_root) { _root->render(ct, area, flags); } + + if (colorMode() == COLORMODE_GRAYSCALE) { + // apply grayscale filter on top of everything + cairo_surface_t *input = ct.rawTarget(); + cairo_surface_t *out = ink_cairo_surface_create_identical(input); + ink_cairo_surface_filter(input, out, _grayscale_colormatrix); + Geom::Point origin = ct.targetLogicalBounds().min(); + ct.setSource(out, origin[Geom::X], origin[Geom::Y]); + ct.setOperator(CAIRO_OPERATOR_SOURCE); + ct.paint(); + ct.setOperator(CAIRO_OPERATOR_OVER); + + cairo_surface_destroy(out); + } } DrawingItem * diff --git a/src/display/drawing.h b/src/display/drawing.h index 8154f0783..74ab57fae 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,6 +65,8 @@ 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); DrawingItem *pick(Geom::Point const &p, double delta, unsigned flags); @@ -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/guideline.cpp b/src/display/guideline.cpp index d3385978a..f71bc82ef 100644 --- a/src/display/guideline.cpp +++ b/src/display/guideline.cpp @@ -66,9 +66,9 @@ 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)); - 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; @@ -191,8 +191,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; 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..a08191f67 100644 --- a/src/display/nr-filter-blend.cpp +++ b/src/display/nr-filter-blend.cpp @@ -146,13 +146,23 @@ 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 ); + cairo_content_t ct1 = cairo_surface_get_content(input1); + cairo_content_t ct2 = cairo_surface_get_content(input2); if ((ct1 == CAIRO_CONTENT_ALPHA && ct2 == CAIRO_CONTENT_ALPHA) || _blend_mode == BLEND_NORMAL) { 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..3bc00d2d7 100644 --- a/src/display/nr-filter-component-transfer.cpp +++ b/src/display/nr-filter-component-transfer.cpp @@ -238,6 +238,17 @@ 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); diff --git a/src/display/nr-filter-composite.cpp b/src/display/nr-filter-composite.cpp index b25ecdf2c..f6decad0d 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)); 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..15cc8e1ff 100644 --- a/src/display/nr-filter-diffuselighting.h +++ b/src/display/nr-filter-diffuselighting.h @@ -19,9 +19,10 @@ #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" -class SPFeDistantLight; -class SPFePointLight; -class SPFeSpotLight; +struct SPFeDistantLight; +struct SPFePointLight; +struct 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..9d7c32585 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) @@ -338,7 +340,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 +355,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]); } @@ -551,6 +553,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); @@ -658,10 +669,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 a9a5ec3e0..7a27d857e 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,9 @@ #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 { @@ -50,22 +51,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; @@ -87,18 +97,20 @@ 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.transform(user2pb); // we are now in primitive units ct.translate(feImageX, feImageY); - ct.scale(scaleX, scaleY); +// ct.scale(scaleX, scaleY); No scaling should be done Geom::IntRect render_rect = area.roundOutwards(); ct.translate(render_rect.min()); @@ -108,11 +120,15 @@ void FilterImage::render_cairo(FilterSlot &slot) drawing.render(ct, 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 { @@ -158,9 +174,9 @@ void FilterImage::render_cairo(FilterSlot &slot) } // Native size of image - //width = image->get_width(); - //height = image->get_height(); - //rowstride = image->get_rowstride(); + // width = image->get_width(); + // height = image->get_height(); + // rowstride = image->get_rowstride(); convert_pixbuf_normal_to_argb32(image->gobj()); @@ -172,13 +188,99 @@ void FilterImage::render_cairo(FilterSlot &slot) 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); + 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->get_height()/(double)image->get_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; + } + + 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->get_width(); double scaleY = feImageHeight / image->get_height(); @@ -204,6 +306,7 @@ double FilterImage::complexity(Geom::Affine const &) } void FilterImage::set_href(const gchar *href){ + if (feImageHref) g_free (feImageHref); feImageHref = (href) ? g_strdup (href) : NULL; @@ -218,13 +321,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..05b7d65a8 100644 --- a/src/display/nr-filter-image.h +++ b/src/display/nr-filter-image.h @@ -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; 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..b058307cf 100644 --- a/src/display/nr-filter-morphology.cpp +++ b/src/display/nr-filter-morphology.cpp @@ -164,6 +164,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 +193,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..01d6ffe56 100644 --- a/src/display/nr-filter-offset.cpp +++ b/src/display/nr-filter-offset.cpp @@ -35,6 +35,9 @@ 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(); diff --git a/src/display/nr-filter-primitive.cpp b/src/display/nr-filter-primitive.cpp index c6bd8a74e..fca82c810 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) @@ -103,6 +113,18 @@ Geom::Rect FilterPrimitive::filter_primitive_area(FilterUnits const &units) Geom::OptRect bb = units.get_item_bbox(); Geom::OptRect fa = units.get_filter_area(); + // This is definitely a hack... but what else to do? + // Current viewport might not be document viewport... but how to find? + SPDocument* document = inkscape_active_document(); + SPRoot* root = document->getRoot(); + Geom::Rect viewport; + if( root->viewBox_set ) { + viewport = root->viewBox; + } else { + // Pick some random values + viewport = Geom::Rect::from_xywh(0,0,480,360); + } + /* 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(); @@ -144,11 +166,11 @@ Geom::Rect FilterPrimitive::filter_primitive_area(FilterUnits const &units) 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; + // Percent of viewport + if( _subregion_x._set && _subregion_x.unit == SVGLength::PERCENT ) x = _subregion_x.value * viewport.width(); + if( _subregion_y._set && _subregion_y.unit == SVGLength::PERCENT ) y = _subregion_y.value * viewport.height(); + if( _subregion_width._set && _subregion_width.unit == SVGLength::PERCENT ) width = _subregion_width.value * viewport.width(); + if( _subregion_height._set && _subregion_height.unit == SVGLength::PERCENT ) height = _subregion_height.value * viewport.height(); } Geom::Point minp, maxp; @@ -161,6 +183,14 @@ Geom::Rect FilterPrimitive::filter_primitive_area(FilterUnits const &units) 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..fe67972d6 100644 --- a/src/display/nr-filter-slot.cpp +++ b/src/display/nr-filter-slot.cpp @@ -129,6 +129,9 @@ cairo_surface_t *FilterSlot::_get_transformed_source_graphic() if (trans.isTranslation()) { cairo_surface_reference(_source_graphic); + + // Assume all source graphics are sRGB + set_cairo_surface_ci( _source_graphic, SP_CSS_COLOR_INTERPOLATION_SRGB ); return _source_graphic; } @@ -145,6 +148,8 @@ cairo_surface_t *FilterSlot::_get_transformed_source_graphic() cairo_paint(tsg_ct); cairo_destroy(tsg_ct); + // Assume all source graphics are sRGB + set_cairo_surface_ci( tsg, SP_CSS_COLOR_INTERPOLATION_SRGB ); return tsg; } @@ -172,6 +177,9 @@ cairo_surface_t *FilterSlot::_get_transformed_background() tbg = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, _slot_w, _slot_h); } + // Assume all source graphics are sRGB + set_cairo_surface_ci( tbg, SP_CSS_COLOR_INTERPOLATION_SRGB ); + return tbg; } 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..0d1c0644f 100644 --- a/src/display/nr-filter-specularlighting.h +++ b/src/display/nr-filter-specularlighting.h @@ -17,9 +17,10 @@ #include "display/nr-light-types.h" #include "display/nr-filter-primitive.h" -class SPFeDistantLight; -class SPFePointLight; -class SPFeSpotLight; +struct SPFeDistantLight; +struct SPFePointLight; +struct 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..333074f55 100644 --- a/src/display/nr-filter-turbulence.cpp +++ b/src/display/nr-filter-turbulence.cpp @@ -283,7 +283,12 @@ private: // other constants static int const BSize = 0x100; static int const BMask = 0xff; + +#if __cplusplus < 201103L static double const PerlinOffset = 4096.0; +#else + static double constexpr PerlinOffset = 4096.0; +#endif Geom::Rect _tile; Geom::Point _baseFreq; @@ -375,6 +380,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..eeba94d7c 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" @@ -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(); diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp index 86102f9e8..ba2340074 100644 --- a/src/display/nr-style.cpp +++ b/src/display/nr-style.cpp @@ -60,7 +60,9 @@ 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; + } } void NRStyle::set(SPStyle *style) @@ -126,7 +128,9 @@ 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; if (n_dash != 0) { diff --git a/src/display/nr-style.h b/src/display/nr-style.h index ce154cec0..80547c43e 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 SPPaintServer; +struct SPStyle; + namespace Inkscape { class DrawingContext; } 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..1101f93f2 100644 --- a/src/display/nr-svgfonts.h +++ b/src/display/nr-svgfonts.h @@ -18,8 +18,8 @@ class SvgFont; struct SPFont; -class SPGlyph; -class SPMissingGlyph; +struct SPGlyph; +struct SPMissingGlyph; struct _GdkEventExpose; typedef _GdkEventExpose GdkEventExpose; diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp index e21bdff15..45dc38a37 100644 --- a/src/display/sodipodi-ctrl.cpp +++ b/src/display/sodipodi-ctrl.cpp @@ -63,10 +63,10 @@ sp_ctrl_get_type (void) static void sp_ctrl_class_init (SPCtrlClass *klass) { - SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass; + SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(klass); GObjectClass *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; @@ -287,8 +287,8 @@ 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); diff --git a/src/display/sodipodi-ctrlrect.cpp b/src/display/sodipodi-ctrlrect.cpp index b0c997a92..c350e4614 100644 --- a/src/display/sodipodi-ctrlrect.cpp +++ b/src/display/sodipodi-ctrlrect.cpp @@ -61,9 +61,9 @@ GType sp_ctrlrect_get_type() static void sp_ctrlrect_class_init(SPCtrlRectClass *c) { - SPCanvasItemClass *item_class = (SPCanvasItemClass *) c; + SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(c); - parent_class = (SPCanvasItemClass*) g_type_class_peek_parent(c); + parent_class = SP_CANVAS_ITEM_CLASS(g_type_class_peek_parent(c)); item_class->destroy = sp_ctrlrect_destroy; item_class->update = sp_ctrlrect_update; @@ -119,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; } @@ -129,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); @@ -161,8 +160,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.cpp b/src/display/sp-canvas.cpp index 6d4d01e33..9d4049512 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -19,6 +19,7 @@ # include <config.h> #endif +#include <gdkmm/rectangle.h> #include <cairomm/region.h> #include "helper/sp-marshal.h" @@ -35,7 +36,6 @@ #include "debug/gdk-event-latency-tracker.h" #include "desktop.h" #include "sp-namedview.h" -#include <gdkmm/rectangle.h> using Inkscape::Debug::GdkEventLatencyTracker; @@ -227,21 +227,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 dispose(GObject *object); - - /** * The canvas widget's realize callback. */ static void realize(GtkWidget *widget); @@ -291,7 +276,7 @@ public: #if GTK_CHECK_VERSION(3,0,0) static gboolean handleDraw(GtkWidget *widget, cairo_t *cr); #else - static gint handleExpose(GtkWidget *widget, GdkEventExpose *event); + static gboolean handleExpose(GtkWidget *widget, GdkEventExpose *event); #endif /** @@ -369,25 +354,11 @@ 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 object_type = 0; @@ -437,9 +408,9 @@ void sp_canvas_item_class_init(SPCanvasItemClass *klass) G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT); - gobject_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; + klass->destroy = sp_canvas_item_real_destroy; object_signals[DESTROY] = g_signal_new ("destroy", @@ -1081,7 +1052,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); @@ -1118,7 +1089,7 @@ 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 @@ -1152,7 +1123,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()) && @@ -1171,7 +1142,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); @@ -1218,68 +1189,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); -void SPCanvasImpl::classInit(SPCanvasClass *klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - GtkWidgetClass *widget_class = (GtkWidgetClass *) klass; +G_DEFINE_TYPE(SPCanvas, sp_canvas, GTK_TYPE_WIDGET); - parentClass = reinterpret_cast<GtkWidgetClass *>(g_type_class_peek_parent(klass)); +static void +sp_canvas_class_init(SPCanvasClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - object_class->dispose = SPCanvasImpl::dispose; + object_class->dispose = sp_canvas_dispose; - widget_class->realize = SPCanvasImpl::realize; - widget_class->unrealize = SPCanvasImpl::unrealize; + 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_width = SPCanvasImpl::getPreferredWidth; widget_class->get_preferred_height = SPCanvasImpl::getPreferredHeight; - widget_class->draw = SPCanvasImpl::handleDraw; + widget_class->draw = SPCanvasImpl::handleDraw; #else - widget_class->size_request = SPCanvasImpl::sizeRequest; - widget_class->expose_event = SPCanvasImpl::handleExpose; + widget_class->size_request = SPCanvasImpl::sizeRequest; + widget_class->expose_event = SPCanvasImpl::handleExpose; #endif - 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->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); @@ -1318,7 +1267,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); @@ -1326,7 +1275,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 @@ -1352,10 +1302,11 @@ void SPCanvasImpl::shutdown_transients(SPCanvas *canvas) #endif } - remove_idle(canvas); + sp_canvas_remove_idle(canvas); } -void SPCanvasImpl::dispose(GObject *object) +static void +sp_canvas_dispose(GObject *object) { SPCanvas *canvas = SP_CANVAS(object); @@ -1364,12 +1315,12 @@ void SPCanvasImpl::dispose(GObject *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 (G_OBJECT_CLASS(parentClass)->dispose) { - (* G_OBJECT_CLASS(parentClass)->dispose)(object); + if (G_OBJECT_CLASS(sp_canvas_parent_class)->dispose) { + (* G_OBJECT_CLASS(sp_canvas_parent_class)->dispose)(object); } } @@ -1388,7 +1339,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); } @@ -1456,10 +1407,10 @@ 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(parentClass)->unrealize) - (* GTK_WIDGET_CLASS(parentClass)->unrealize)(widget); + if (GTK_WIDGET_CLASS(sp_canvas_parent_class)->unrealize) + (* GTK_WIDGET_CLASS(sp_canvas_parent_class)->unrealize)(widget); } @@ -1649,7 +1600,6 @@ int SPCanvasImpl::emitEvent(SPCanvas *canvas, GdkEvent *event) 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; @@ -1709,6 +1659,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; @@ -2178,23 +2129,24 @@ void SPCanvas::endForcedFullRedraws() } #if GTK_CHECK_VERSION(3,0,0) - -//TODO: This could probably be done much more efficiently gboolean SPCanvasImpl::handleDraw(GtkWidget *widget, cairo_t *cr) { SPCanvas *canvas = SP_CANVAS(widget); - gint width = gtk_widget_get_allocated_width(widget); - gint height = gtk_widget_get_allocated_height(widget); - Geom::IntRect r = Geom::IntRect::from_xywh( - canvas->x0, canvas->y0, - width, height); + 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]; - canvas->requestRedraw(r.left(), r.top(), r.right(), r.bottom()); + 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 -gint SPCanvasImpl::handleExpose(GtkWidget *widget, GdkEventExpose *event) +gboolean SPCanvasImpl::handleExpose(GtkWidget *widget, GdkEventExpose *event) { SPCanvas *canvas = SP_CANVAS(widget); @@ -2203,43 +2155,23 @@ gint SPCanvasImpl::handleExpose(GtkWidget *widget, GdkEventExpose *event) return FALSE; } -#if GTK_CHECK_VERSION(3,0,0) - int n_rects = cairo_region_num_rectangles(event->region); -#else int n_rects = 0; GdkRectangle *rects = NULL; gdk_region_get_rectangles(event->region, &rects, &n_rects); if(rects == NULL) - return FALSE; -#endif - - if (n_rects == 0) - { return FALSE; - } - else - { - for (int i = 0; i < n_rects; i++) { -#if GTK_CHECK_VERSION(3,0,0) - cairo_rectangle_int_t rectangle; - cairo_region_get_rectangle(event->region, i, &rectangle); -#else - GdkRectangle rectangle = rects[i]; -#endif - - Geom::IntRect r = Geom::IntRect::from_xywh( - rectangle.x + canvas->x0, rectangle.y + canvas->y0, - rectangle.width, rectangle.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()); - } - -#if !GTK_CHECK_VERSION(3,0,0) - g_free (rects); -#endif - return FALSE; + canvas->requestRedraw(r.left(), r.top(), r.right(), r.bottom()); } + + return FALSE; } #endif diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index f0366a2e5..b570b739e 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. */ diff --git a/src/display/sp-ctrlcurve.h b/src/display/sp-ctrlcurve.h index 15de20161..90936185c 100644 --- a/src/display/sp-ctrlcurve.h +++ b/src/display/sp-ctrlcurve.h @@ -17,7 +17,7 @@ #include "display/sp-ctrlline.h" -struct SPItem; +class SPItem; #define SP_TYPE_CTRLCURVE (SPCtrlCurve::getType()) #define SP_CTRLCURVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CTRLCURVE, SPCtrlCurve)) diff --git a/src/display/sp-ctrlpoint.cpp b/src/display/sp-ctrlpoint.cpp index d07e9385b..026cc7589 100644 --- a/src/display/sp-ctrlpoint.cpp +++ b/src/display/sp-ctrlpoint.cpp @@ -52,9 +52,9 @@ sp_ctrlpoint_get_type (void) static void sp_ctrlpoint_class_init(SPCtrlPointClass *klass) { - SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass; + SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(klass); - parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass); + parent_class = SP_CANVAS_ITEM_CLASS(g_type_class_peek_parent(klass)); item_class->destroy = sp_ctrlpoint_destroy; item_class->update = sp_ctrlpoint_update; diff --git a/src/display/sp-ctrlquadr.cpp b/src/display/sp-ctrlquadr.cpp index ae15d620a..b6a0da109 100644 --- a/src/display/sp-ctrlquadr.cpp +++ b/src/display/sp-ctrlquadr.cpp @@ -61,9 +61,9 @@ sp_ctrlquadr_get_type (void) static void sp_ctrlquadr_class_init (SPCtrlQuadrClass *klass) { - SPCanvasItemClass *item_class = (SPCanvasItemClass *) klass; + SPCanvasItemClass *item_class = SP_CANVAS_ITEM_CLASS(klass); - parent_class = (SPCanvasItemClass*)g_type_class_peek_parent (klass); + parent_class = SP_CANVAS_ITEM_CLASS(g_type_class_peek_parent(klass)); item_class->destroy = sp_ctrlquadr_destroy; item_class->update = sp_ctrlquadr_update; |
