diff options
| author | Michael Soegtrop <MSoegtrop@yahoo.de> | 2017-06-05 13:01:17 +0000 |
|---|---|---|
| committer | Michael Soegtrop <MSoegtrop@yahoo.de> | 2017-06-05 13:01:17 +0000 |
| commit | e7248b2fa042f42a5c4dd14cd86ab6a5b4524059 (patch) | |
| tree | 9097520c54e355ded9bd0b4d6618af4e8dacdd91 /src/display | |
| parent | updated to latest trunk (diff) | |
| parent | [Bug #1695016] Xaml export misses some radialGradients. (diff) | |
| download | inkscape-e7248b2fa042f42a5c4dd14cd86ab6a5b4524059.tar.gz inkscape-e7248b2fa042f42a5c4dd14cd86ab6a5b4524059.zip | |
updated to latest trunk
(bzr r14876.2.4)
Diffstat (limited to 'src/display')
42 files changed, 1288 insertions, 712 deletions
diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt index 0bf1d6e45..8eaaee99a 100644 --- a/src/display/CMakeLists.txt +++ b/src/display/CMakeLists.txt @@ -4,7 +4,9 @@ set(display_SRC canvas-arena.cpp canvas-axonomgrid.cpp canvas-bpath.cpp + canvas-debug.cpp canvas-grid.cpp + canvas-rotate.cpp canvas-temporary-item-list.cpp canvas-temporary-item.cpp canvas-text.cpp @@ -64,7 +66,9 @@ set(display_SRC canvas-arena.h canvas-axonomgrid.h canvas-bpath.h + canvas-debug.h canvas-grid.h + canvas-rotate.h canvas-temporary-item-list.h canvas-temporary-item.h canvas-text.h diff --git a/src/display/Makefile_insert b/src/display/Makefile_insert deleted file mode 100644 index 419852f7d..000000000 --- a/src/display/Makefile_insert +++ /dev/null @@ -1,125 +0,0 @@ -## Makefile.am fragment sourced by src/Makefile.am. - -display/canvas-arena.$(OBJEXT): helper/sp-marshal.h -display/sp-canvas.$(OBJEXT): helper/sp-marshal.h - -ink_common_sources += \ - display/cairo-templates.h \ - display/cairo-utils.cpp \ - display/cairo-utils.h \ - display/canvas-arena.cpp \ - display/canvas-arena.h \ - display/canvas-axonomgrid.cpp \ - display/canvas-axonomgrid.h \ - display/canvas-bpath.cpp \ - display/canvas-bpath.h \ - display/canvas-grid.cpp \ - display/canvas-grid.h \ - display/canvas-temporary-item.cpp \ - display/canvas-temporary-item.h \ - display/canvas-temporary-item-list.cpp \ - display/canvas-temporary-item-list.h \ - display/canvas-text.cpp \ - display/canvas-text.h \ - display/curve.cpp \ - display/curve.h \ - display/drawing.cpp \ - display/drawing.h \ - display/drawing-context.cpp \ - display/drawing-context.h \ - display/drawing-group.cpp \ - display/drawing-group.h \ - display/drawing-image.cpp \ - display/drawing-image.h \ - display/drawing-item.cpp \ - display/drawing-item.h \ - display/drawing-pattern.cpp \ - display/drawing-pattern.h \ - display/drawing-shape.cpp \ - display/drawing-shape.h \ - display/drawing-surface.cpp \ - display/drawing-surface.h \ - display/drawing-text.cpp \ - display/drawing-text.h \ - display/gnome-canvas-acetate.cpp \ - display/gnome-canvas-acetate.h \ - display/grayscale.cpp \ - display/grayscale.h \ - display/guideline.cpp \ - display/guideline.h \ - display/nr-3dutils.cpp \ - display/nr-3dutils.h \ - display/nr-filter-blend.cpp \ - display/nr-filter-blend.h \ - display/nr-filter-colormatrix.cpp \ - display/nr-filter-colormatrix.h \ - display/nr-filter-component-transfer.cpp \ - display/nr-filter-component-transfer.h \ - display/nr-filter-composite.cpp \ - display/nr-filter-composite.h \ - display/nr-filter-convolve-matrix.cpp \ - display/nr-filter-convolve-matrix.h \ - display/nr-filter.cpp \ - display/nr-filter-diffuselighting.cpp \ - display/nr-filter-diffuselighting.h \ - display/nr-filter-displacement-map.cpp \ - display/nr-filter-displacement-map.h \ - display/nr-filter-flood.cpp \ - display/nr-filter-flood.h \ - display/nr-filter-gaussian.cpp \ - display/nr-filter-gaussian.h \ - display/nr-filter.h \ - display/nr-filter-image.cpp \ - display/nr-filter-image.h \ - display/nr-filter-merge.cpp \ - display/nr-filter-merge.h \ - display/nr-filter-morphology.cpp \ - display/nr-filter-morphology.h \ - display/nr-filter-offset.cpp \ - display/nr-filter-offset.h \ - display/nr-filter-primitive.cpp \ - display/nr-filter-primitive.h \ - display/nr-filter-slot.cpp \ - display/nr-filter-slot.h \ - display/nr-filter-specularlighting.cpp \ - display/nr-filter-specularlighting.h \ - display/nr-filter-tile.cpp \ - display/nr-filter-tile.h \ - display/nr-filter-turbulence.cpp \ - display/nr-filter-turbulence.h \ - display/nr-filter-types.h \ - display/nr-filter-units.cpp \ - display/nr-filter-units.h \ - display/nr-filter-utils.h \ - display/nr-light.cpp \ - display/nr-light.h \ - display/nr-light-types.h \ - display/nr-style.cpp \ - display/nr-style.h \ - display/nr-svgfonts.cpp \ - display/nr-svgfonts.h \ - display/rendermode.h \ - display/snap-indicator.cpp \ - display/snap-indicator.h \ - display/sodipodi-ctrl.cpp \ - display/sodipodi-ctrl.h \ - display/sodipodi-ctrlrect.cpp \ - display/sodipodi-ctrlrect.h \ - display/sp-canvas.cpp \ - display/sp-canvas.h \ - display/sp-canvas-item.h \ - display/sp-canvas-group.h \ - display/sp-canvas-util.cpp \ - display/sp-canvas-util.h \ - display/sp-ctrlcurve.cpp \ - display/sp-ctrlcurve.h \ - display/sp-ctrlline.cpp \ - display/sp-ctrlline.h \ - display/sp-ctrlquadr.cpp \ - display/sp-ctrlquadr.h - -# ###################### -# ### CxxTest stuff #### -# ###################### -CXXTEST_TESTSUITES += \ - $(srcdir)/display/curve-test.h diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 24a75de4c..8045007e7 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -290,7 +290,7 @@ Pixbuf *Pixbuf::create_from_file(std::string const &fn) if (!g_file_test(fn.c_str(), G_FILE_TEST_EXISTS)) { return NULL; } - struct stat stdir; + GStatBuf stdir; int val = g_stat(fn.c_str(), &stdir); if (val == 0 && stdir.st_mode & S_IFDIR){ return NULL; @@ -1321,6 +1321,91 @@ guint32 argb32_from_rgba(guint32 in) return px; } + +/** + * Converts a pixbuf to a PNG data structure. + * For 8-but RGBA png, this is like copying. + * + */ +const guchar* pixbuf_to_png(guchar const**rows, guchar* px, int num_rows, int num_cols, int stride, int color_type, int bit_depth) +{ + int n_fields = 1 + (color_type&2) + (color_type&4)/4; + const guchar* new_data = (const guchar*)malloc((n_fields * bit_depth * num_rows * num_cols)/8 + 64); + char* ptr = (char*) new_data; + int pad=0; //used when we write image data smaller than one byte (for instance in black and white images where 1px = 1bit) + for(int row = 0; row < num_rows; ++row){ + rows[row] = (const guchar*)ptr; + for(int col = 0; col < num_cols; ++col){ + guint32 *pixel = reinterpret_cast<guint32*>(px + row*stride)+col; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + //this part should probably be rewritten as (a tested, working) big endian, using htons() and ntohs() + guint64 a = (*pixel & 0xff000000) >> 24; + guint64 b = (*pixel & 0x00ff0000) >> 16; + guint64 g = (*pixel & 0x0000ff00) >> 8; + guint64 r = (*pixel & 0x000000ff); + + //one of possible rgb to greyscale formulas. This one is called "luminance, "luminosity" or "luma" + guint16 gray = (guint16)((guint32)((0.2126*(r<<24) + 0.7152*(g<<24) + 0.0722*(b<<24)))>>16); + + if (!pad) *((guint64*)ptr)=0; + if (color_type & 2) { //RGB + // for 8bit->16bit transition, I take the FF -> FFFF convention (multiplication by 0x101). + // If you prefer FF -> FF00 (multiplication by 0x100), remove the <<8, <<24, <<40 and <<56 + if (color_type & 4) { //RGBA + if (bit_depth==8) + *((guint32*)ptr) = *pixel; + else + *((guint64*)ptr) = (guint64)((a<<56)+(a<<48)+(b<<40)+(b<<32)+(g<<24)+(g<<16)+(r<<8)+(r)); + } + else{ //no alpha + if(bit_depth==8) + *((guint32*)ptr) = ((*pixel)<<8)>>8; + else + *((guint64*)ptr) = (guint64)((b<<40)+(b<<32)+(g<<24)+(g<<16)+(r<<8)+r); + } + } else { //Grayscale + if(bit_depth==16) + *(guint16*)ptr= ((gray & 0xff00)>>8) + ((gray &0x00ff)<<8); + else *((guint16*)ptr) += guint16(((gray >> (16-bit_depth))<<pad) ); //note the "+=" + + if(color_type & 4) { //Alpha channel + if (bit_depth == 16) + *((guint32*)(ptr+2)) = a + (a<<8); + else + *((guint32*)(ptr)) += guint32((a << 8) >> (16 - bit_depth))<<(bit_depth + pad); + } + } + +#else + // I don't have any access to a big-endian machine to test this. It should still work with default export settings. + guint64 r = (*pixel & 0xff000000) >> 24; + guint64 g = (*pixel & 0x00ff0000) >> 16; + guint64 b = (*pixel & 0x0000ff00) >> 8; + guint64 a = (*pixel & 0x000000ff); + guint32 gray = (guint32)(0.2126*(r<<24) + 0.7152*(g<<24) + 0.0722*(b<<24)); + if(color_type & 2){ + if(bit_depth==8)*ptr = *pixel; else *ptr = (guint64)((r<<56)+(g<<40)+(b<<24)+(a<<8)); + } else { + *((guint32*)ptr) += guint32(gray>>pad); + if(color_type & 4) *((guint32*)ptr) += guint32((a>>pad)>> bit_depth); + } +#endif + pad+=bit_depth*n_fields; + ptr+=(pad/8); + pad%=8; + } + if(pad){pad=0;ptr++;}//align bytes on rows + } + return new_data; +} + + + + + + + + /* Local Variables: mode:c++ diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h index 2a7e460e8..72087471d 100644 --- a/src/display/cairo-utils.h +++ b/src/display/cairo-utils.h @@ -178,6 +178,8 @@ void convert_pixels_argb32_to_pixbuf(guchar *data, int w, int h, int rs); G_GNUC_CONST guint32 argb32_from_pixbuf(guint32 in); G_GNUC_CONST guint32 pixbuf_from_argb32(guint32 in); +const guchar* pixbuf_to_png(guchar const**rows, guchar* px, int nrows, int ncols, int stride, int color_type, int bit_depth); + /** Convert a pixel in 0xRRGGBBAA format to Cairo ARGB32 format. */ G_GNUC_CONST guint32 argb32_from_rgba(guint32 in); diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index 14f36376f..589cc849d 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -19,12 +19,7 @@ #include <gtkmm/box.h> #include <gtkmm/label.h> - -#if WITH_GTKMM_3_0 -# include <gtkmm/grid.h> -#else -# include <gtkmm/table.h> -#endif +#include <gtkmm/grid.h> #include <glibmm/i18n.h> @@ -47,7 +42,6 @@ #include "2geom/line.h" #include "2geom/angle.h" #include "helper/mathfns.h" -#include "round.h" #include "util/units.h" using Inkscape::Util::unit_table; @@ -94,15 +88,10 @@ 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); @@ -110,44 +99,28 @@ attach_all(Gtk::Table &table, Gtk::Widget const *const arr[], unsigned size, int (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])); +#if GTK_CHECK_VERSION(3,16,0) + label.set_xalign(0.0); + label.set_yalign(0.5); +#else label.set_alignment (0.0); -#if WITH_GTKMM_3_0 +#endif 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 = Gtk::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; @@ -342,14 +315,9 @@ CanvasAxonomGrid::onReprAttrChanged(Inkscape::XML::Node */*repr*/, gchar const * Gtk::Widget * CanvasAxonomGrid::newSpecificWidget() { -#if WITH_GTKMM_3_0 - Gtk::Grid *table = Gtk::manage(new Gtk::Grid()); + auto 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); diff --git a/src/display/canvas-bpath.cpp b/src/display/canvas-bpath.cpp index 46b59d25a..fc6b79b43 100644 --- a/src/display/canvas-bpath.cpp +++ b/src/display/canvas-bpath.cpp @@ -53,6 +53,7 @@ sp_canvas_bpath_init (SPCanvasBPath * bpath) bpath->stroke_linejoin = SP_STROKE_LINEJOIN_MITER; bpath->stroke_linecap = SP_STROKE_LINECAP_BUTT; bpath->stroke_miterlimit = 11.0; + bpath->phantom_line = false; } static void sp_canvas_bpath_destroy(SPCanvasItem *object) @@ -71,7 +72,7 @@ static void sp_canvas_bpath_update(SPCanvasItem *item, Geom::Affine const &affin { SPCanvasBPath *cbp = SP_CANVAS_BPATH(item); - item->canvas->requestRedraw((int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2); + item->canvas->requestRedraw((int)item->x1 - 1, (int)item->y1 - 1, (int)item->x2 + 1 , (int)item->y2 + 1); if (reinterpret_cast<SPCanvasItemClass *>(sp_canvas_bpath_parent_class)->update) { reinterpret_cast<SPCanvasItemClass *>(sp_canvas_bpath_parent_class)->update(item, affine, flags); @@ -86,10 +87,10 @@ static void sp_canvas_bpath_update(SPCanvasItem *item, Geom::Affine const &affin Geom::OptRect bbox = bounds_exact_transformed(cbp->curve->get_pathvector(), affine); if (bbox) { - item->x1 = (int)bbox->min()[Geom::X] - 1; - item->y1 = (int)bbox->min()[Geom::Y] - 1; - item->x2 = (int)bbox->max()[Geom::X] + 1; - item->y2 = (int)bbox->max()[Geom::Y] + 1; + item->x1 = (int)floor(bbox->min()[Geom::X]) - 1; + item->y1 = (int)floor(bbox->min()[Geom::Y]) - 1; + item->x2 = (int)ceil(bbox->max()[Geom::X]) + 1; + item->y2 = (int)ceil(bbox->max()[Geom::Y]) + 1; } else { item->x1 = 0; item->y1 = 0; @@ -131,7 +132,21 @@ sp_canvas_bpath_render (SPCanvasItem *item, SPCanvasBuf *buf) cairo_fill_preserve(buf->ct); } - if (dostroke) { + if (dostroke && cbp->phantom_line) { + ink_cairo_set_source_rgba32(buf->ct, 0xffffff4d); + cairo_set_line_width(buf->ct, 2); + if (cbp->dashes[0] != 0 && cbp->dashes[1] != 0) { + cairo_set_dash (buf->ct, cbp->dashes, 2, 0); + } + cairo_stroke(buf->ct); + cairo_set_tolerance(buf->ct, 0.5); + cairo_new_path(buf->ct); + feed_pathvector_to_cairo (buf->ct, cbp->curve->get_pathvector(), cbp->affine, area, + /* optimized_stroke = */ !dofill, 1); + ink_cairo_set_source_rgba32(buf->ct, cbp->stroke_rgba); + cairo_set_line_width(buf->ct, 1); + cairo_stroke(buf->ct); + } else if (dostroke) { ink_cairo_set_source_rgba32(buf->ct, cbp->stroke_rgba); cairo_set_line_width(buf->ct, 1); if (cbp->dashes[0] != 0 && cbp->dashes[1] != 0) { @@ -167,24 +182,25 @@ sp_canvas_bpath_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_ } SPCanvasItem * -sp_canvas_bpath_new (SPCanvasGroup *parent, SPCurve *curve) +sp_canvas_bpath_new (SPCanvasGroup *parent, SPCurve *curve, bool phantom_line) { g_return_val_if_fail (parent != NULL, NULL); g_return_val_if_fail (SP_IS_CANVAS_GROUP (parent), NULL); SPCanvasItem *item = sp_canvas_item_new (parent, SP_TYPE_CANVAS_BPATH, NULL); - sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (item), curve); + sp_canvas_bpath_set_bpath (SP_CANVAS_BPATH (item), curve, phantom_line); return item; } void -sp_canvas_bpath_set_bpath (SPCanvasBPath *cbp, SPCurve *curve) +sp_canvas_bpath_set_bpath (SPCanvasBPath *cbp, SPCurve *curve, bool phantom_line) { g_return_if_fail (cbp != NULL); g_return_if_fail (SP_IS_CANVAS_BPATH (cbp)); + cbp->phantom_line = phantom_line; if (cbp->curve) { cbp->curve = cbp->curve->unref(); } diff --git a/src/display/canvas-bpath.h b/src/display/canvas-bpath.h index 72eca6eeb..686eb7c5a 100644 --- a/src/display/canvas-bpath.h +++ b/src/display/canvas-bpath.h @@ -80,7 +80,7 @@ struct SPCanvasBPath { SPStrokeJoinType stroke_linejoin; SPStrokeCapType stroke_linecap; gdouble stroke_miterlimit; - + bool phantom_line; /* State */ Shape *fill_shp; Shape *stroke_shp; @@ -92,9 +92,9 @@ struct SPCanvasBPathClass { GType sp_canvas_bpath_get_type (void); -SPCanvasItem *sp_canvas_bpath_new (SPCanvasGroup *parent, SPCurve *curve); +SPCanvasItem *sp_canvas_bpath_new (SPCanvasGroup *parent, SPCurve *curve, bool phantom_line = false); -void sp_canvas_bpath_set_bpath (SPCanvasBPath *cbp, SPCurve *curve); +void sp_canvas_bpath_set_bpath (SPCanvasBPath *cbp, SPCurve *curve, bool phantom_line = false); void sp_canvas_bpath_set_fill (SPCanvasBPath *cbp, guint32 rgba, SPWindRule rule); void sp_canvas_bpath_set_stroke (SPCanvasBPath *cbp, guint32 rgba, gdouble width, SPStrokeJoinType join, SPStrokeCapType cap, double dash=0, double gap=0); diff --git a/src/display/canvas-debug.cpp b/src/display/canvas-debug.cpp new file mode 100644 index 000000000..1748029e9 --- /dev/null +++ b/src/display/canvas-debug.cpp @@ -0,0 +1,100 @@ +/* + * A simple surface for debugging the canvas. Shows how tiles are drawn. + * + * Author: + * Tavmjong Bah <tavmjong@free.fr> + * + * Copyright (C) 2017 Tavmjong Bah + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "inkscape.h" +#include "desktop.h" + +#include "canvas-debug.h" +#include "sp-canvas.h" +#include "cairo-utils.h" +#include "ui/event-debug.h" + +namespace { + +static void sp_canvas_debug_destroy(SPCanvasItem *item); +static void sp_canvas_debug_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags); +static void sp_canvas_debug_render (SPCanvasItem *item, SPCanvasBuf *buf); +static int sp_canvas_debug_event (SPCanvasItem *item, GdkEvent *event); + +} // namespace + +G_DEFINE_TYPE(SPCanvasDebug, sp_canvas_debug, SP_TYPE_CANVAS_ITEM); + +static void sp_canvas_debug_class_init (SPCanvasDebugClass *klass) +{ + klass->destroy = sp_canvas_debug_destroy; + klass->update = sp_canvas_debug_update; + klass->render = sp_canvas_debug_render; + klass->event = sp_canvas_debug_event; +} + +static void sp_canvas_debug_init (SPCanvasDebug *debug) +{ + debug->pickable = true; // So we can receive events. +} + +namespace { +static void sp_canvas_debug_destroy (SPCanvasItem *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (SP_IS_CANVAS_DEBUG (object)); + + if (SP_CANVAS_ITEM_CLASS(sp_canvas_debug_parent_class)->destroy) { + SP_CANVAS_ITEM_CLASS(sp_canvas_debug_parent_class)->destroy(object); + } +} + +static void sp_canvas_debug_update( SPCanvasItem *item, Geom::Affine const &/*affine*/, unsigned int /*flags*/ ) +{ + // We cover the entire canvas + item->x1 = -G_MAXINT; + item->y1 = -G_MAXINT; + item->x2 = G_MAXINT; + item->y2 = G_MAXINT; +} + +static void sp_canvas_debug_render( SPCanvasItem *item, SPCanvasBuf *buf) +{ + if (!buf->ct) { + return; + } + + cairo_set_line_width (buf->ct, 2); + + // Draw box around buffer (for debugging) + cairo_new_path (buf->ct); + cairo_move_to (buf->ct, 0, 0); + cairo_line_to (buf->ct, buf->rect.width(), 0); + cairo_line_to (buf->ct, buf->rect.width(), buf->rect.height()); + cairo_line_to (buf->ct, 0, buf->rect.height()); + cairo_close_path (buf->ct); + ink_cairo_set_source_rgba32 (buf->ct, 0xff7f7f7f); + cairo_stroke (buf->ct); +} + +static int sp_canvas_debug_event (SPCanvasItem *item, GdkEvent *event) +{ + ui_dump_event (event, Glib::ustring("sp_canvas_debug_event")); + return false; // We don't use any events... +} + +} // namespace + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/canvas-debug.h b/src/display/canvas-debug.h new file mode 100644 index 000000000..d5a166da9 --- /dev/null +++ b/src/display/canvas-debug.h @@ -0,0 +1,41 @@ +#ifndef SEEN_SP_CANVAS_DEBUG_H +#define SEEN_SP_CANVAS_DEBUG_H + +/* + * A simple surface for debugging the canvas. Shows how tiles are drawn. + * + * Author: + * Tavmjong Bah <tavmjong@free.fr> + * + * Copyright (C) 2017 Tavmjong Bah + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "sp-canvas-item.h" + +class SPItem; + +#define SP_TYPE_CANVAS_DEBUG (sp_canvas_debug_get_type ()) +#define SP_CANVAS_DEBUG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CANVAS_DEBUG, SPCanvasDebug)) +#define SP_IS_CANVAS_DEBUG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CANVAS_DEBUG)) + +struct SPCanvasDebug : public SPCanvasItem { +}; + +GType sp_canvas_debug_get_type (void); + +struct SPCanvasDebugClass : public SPCanvasItemClass{}; + +#endif // SEEN_SP_CANVAS_DEBUG_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index decf93626..4243d3365 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -5,6 +5,7 @@ * Copyright (C) Lauris Kaplinski 2000 * Abhishek Sharma * Jon A. Cruz <jon@joncruz.org> + * Copyright (C) Tavmong Bah 2017 <tavmjong@free.fr> */ /* As a general comment, I am not exactly proud of how things are done. @@ -19,12 +20,7 @@ #include <gtkmm/box.h> #include <gtkmm/label.h> - -#if WITH_GTKMM_3_0 -# include <gtkmm/grid.h> -#else -# include <gtkmm/table.h> -#endif +#include <gtkmm/grid.h> #include <glibmm/i18n.h> @@ -144,7 +140,7 @@ grid_canvasitem_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned }; CanvasGrid::CanvasGrid(SPNamedView * nv, Inkscape::XML::Node * in_repr, SPDocument *in_doc, GridType type) - : visible(true), gridtype(type) + : visible(true), gridtype(type), legacy(false), pixel(false) { repr = in_repr; doc = in_doc; @@ -400,15 +396,10 @@ void CanvasGrid::setOrigin(Geom::Point const &origin_px) **/ #define SPACE_SIZE_X 15 #define SPACE_SIZE_Y 10 -#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); @@ -416,44 +407,28 @@ static inline void attach_all(Gtk::Table &table, Gtk::Widget const *const arr[], (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])); +#if GTK_CHECK_VERSION(3,16,0) + label.set_xalign(0.0); + label.set_yalign(0.5); +#else label.set_alignment (0.0); -#if WITH_GTKMM_3_0 +#endif 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 = Gtk::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; @@ -559,6 +534,10 @@ CanvasXYGrid::readRepr() if( q.unit->type == UNIT_TYPE_LINEAR ) { // Legacy grid not in 'user units' origin[Geom::X] = q.value("px"); + legacy = true; + if (q.unit->abbr == "px" ) { + pixel = true; + } } else { // Grid in 'user units' origin[Geom::X] = q.quantity * scale_x; @@ -572,6 +551,10 @@ CanvasXYGrid::readRepr() if( q.unit->type == UNIT_TYPE_LINEAR ) { // Legacy grid not in 'user units' origin[Geom::Y] = q.value("px"); + legacy = true; + if (q.unit->abbr == "px" ) { + pixel = true; + } } else { // Grid in 'user units' origin[Geom::Y] = q.quantity * scale_y; @@ -590,6 +573,10 @@ CanvasXYGrid::readRepr() if( q.unit->type == UNIT_TYPE_LINEAR ) { // Legacy grid not in 'user units' spacing[Geom::X] = q.value("px"); + legacy = true; + if (q.unit->abbr == "px" ) { + pixel = true; + } } else { // Grid in 'user units' spacing[Geom::X] = q.quantity * scale_x; @@ -609,6 +596,10 @@ CanvasXYGrid::readRepr() if( q.unit->type == UNIT_TYPE_LINEAR ) { // Legacy grid not in 'user units' spacing[Geom::Y] = q.value("px"); + legacy = true; + if (q.unit->abbr == "px" ) { + pixel = true; + } } else { // Grid in 'user units' spacing[Geom::Y] = q.quantity * scale_y; @@ -684,14 +675,9 @@ 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() ); + auto 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) ); @@ -834,25 +820,41 @@ CanvasXYGrid::updateWidgets() */ } +// For correcting old SVG Inkscape files +void +CanvasXYGrid::Scale (Geom::Scale const &scale ) { + origin *= scale; + spacing *= scale; + // Write out in 'user-units' + Inkscape::SVGOStringStream os_x, os_y, ss_x, ss_y; + os_x << origin[Geom::X]; + os_y << origin[Geom::Y]; + ss_x << spacing[Geom::X]; + ss_y << spacing[Geom::Y]; + repr->setAttribute("originx", os_x.str().c_str()); + repr->setAttribute("originy", os_y.str().c_str()); + repr->setAttribute("spacingx", ss_x.str().c_str()); + repr->setAttribute("spacingy", ss_y.str().c_str()); +} void CanvasXYGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/) { ow = origin * affine; - sw = spacing * affine; - sw -= Geom::Point(affine[4], affine[5]); + sw[0] = Geom::Point(spacing[0], 0) * affine.withoutTranslation(); + sw[1] = Geom::Point(0, spacing[1]) * affine.withoutTranslation(); + // Find suitable grid spacing for display for(int dim = 0; dim < 2; dim++) { gint scaling_factor = empspacing; if (scaling_factor <= 1) scaling_factor = 5; - scaled[dim] = FALSE; - sw[dim] = fabs (sw[dim]); - while (sw[dim] < 8.0) { - scaled[dim] = TRUE; + scaled[dim] = false; + while (fabs(sw[dim].length()) < 8.0) { + scaled[dim] = true; sw[dim] *= scaling_factor; /* First pass, go up to the major line spacing, then keep increasing by two. */ @@ -862,49 +864,45 @@ CanvasXYGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/) } -static void -grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba) -{ - if ((y < buf->rect.top()) || (y >= buf->rect.bottom())) - return; - - cairo_move_to(buf->ct, 0.5 + xs, 0.5 + y); - cairo_line_to(buf->ct, 0.5 + xe, 0.5 + y); - ink_cairo_set_source_rgba32(buf->ct, rgba); - cairo_stroke(buf->ct); -} - -static void -grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba) +// Find intersections of line with rectangle. There should be zero or two. +// If line is degenerate with rectangle side, two corner points are returned. +static std::vector<Geom::Point> +intersect_line_rectangle( Geom::Line const &line, Geom::Rect const &rect ) { - if ((x < buf->rect.left()) || (x >= buf->rect.right())) - return; - - cairo_move_to(buf->ct, 0.5 + x, 0.5 + ys); - cairo_line_to(buf->ct, 0.5 + x, 0.5 + ye); - ink_cairo_set_source_rgba32(buf->ct, rgba); - cairo_stroke(buf->ct); + std::vector<Geom::Point> intersections; + for (unsigned i = 0; i < 4; ++i) { + Geom::LineSegment side( rect.corner(i), rect.corner((i+1)%4) ); + try { + Geom::OptCrossing oc = Geom::intersection(line, side); + if (oc) { + intersections.push_back( line.pointAt((*oc).ta)); + } + } catch (Geom::InfiniteSolutions) { + intersections.clear(); + intersections.push_back( side.pointAt(0) ); + intersections.push_back( side.pointAt(1) ); + return intersections; + } + } + return intersections; } -static void -grid_dot (SPCanvasBuf *buf, gint x, gint y, guint32 rgba) -{ - if ( (y < buf->rect.top()) || (y >= buf->rect.bottom()) - || (x < buf->rect.left()) || (x >= buf->rect.right()) ) - return; - - cairo_rectangle(buf->ct, x, y, 1, 1); - ink_cairo_set_source_rgba32(buf->ct, rgba); - cairo_fill(buf->ct); +// Find the signed distance of a point to a line. The distance is negative if +// the point lies to the left of the line considering the line's versor. +static double +signed_distance( Geom::Point const &point, Geom::Line const &line ) { + Geom::Coord t = line.nearestTime( point ); + Geom::Point p = line.pointAt(t); + double distance = Geom::distance( p, point ); + if ( Geom::cross( Geom::Line( p, point ).versor(), line.versor() ) < 0.0 ) { + distance = -distance; + } + return distance; } void CanvasXYGrid::Render (SPCanvasBuf *buf) { - gdouble const sxg = floor ((buf->rect.left() - ow[Geom::X]) / sw[Geom::X]) * sw[Geom::X] + ow[Geom::X]; - gint const xlinestart = round((sxg - ow[Geom::X]) / sw[Geom::X]); - gdouble const syg = floor ((buf->rect.top() - ow[Geom::Y]) / sw[Geom::Y]) * sw[Geom::Y] + ow[Geom::Y]; - gint const ylinestart = round((syg - ow[Geom::Y]) / sw[Geom::Y]); // no_emphasize_when_zoomedout determines color (minor or major) when only major grid lines/dots shown. Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -921,75 +919,128 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) cairo_set_line_width(buf->ct, 1.0); cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE); - if (!render_dotted) { - // Line grid - gint ylinenum; - gdouble y; - for (y = syg, ylinenum = ylinestart; y < buf->rect.bottom(); y += sw[Geom::Y], ylinenum++) { - gint const y0 = round(y); - if (!scaled[Geom::Y] && (ylinenum % empspacing) != 0) { - grid_hline (buf, y0, buf->rect.left(), buf->rect.right() - 1, color); - } else { - grid_hline (buf, y0, buf->rect.left(), buf->rect.right() - 1, _empcolor); - } - } + for (unsigned dim = 0; dim < 2; ++dim) { - gint xlinenum; - gdouble x; - for (x = sxg, xlinenum = xlinestart; x < buf->rect.right(); x += sw[Geom::X], xlinenum++) { - gint const ix = round(x); - if (!scaled[Geom::X] && (xlinenum % empspacing) != 0) { - grid_vline (buf, ix, buf->rect.top(), buf->rect.bottom(), color); - } else { - grid_vline (buf, ix, buf->rect.top(), buf->rect.bottom(), _empcolor); + // std::cout << "\n " << (dim==0?"Horizontal":"Vertical") << " ------------" << std::endl; + + // Construct an axis line through origin with direction normal to grid spacing. + Geom::Line axis = Geom::Line::from_origin_and_vector( ow, sw[dim] ); + Geom::Line orth = Geom::Line::from_origin_and_vector( ow, sw[(dim+1)%2] ); + + double spacing = sw[(dim+1)%2].length(); // Spacing between grid lines. + double dash = sw[dim].length(); // Total length of dash pattern. + + // std::cout << " axis: " << axis.origin() << ", " << axis.vector() << std::endl; + // std::cout << " spacing: " << spacing << std::endl; + // std::cout << " dash period: " << dash << std::endl; + + // Find the minimum and maximum distances of the buffer corners from axis. + double min = 1000000; + double max = -1000000; + for (unsigned c = 0; c < 4; ++c) { + + // We need signed distance... lib2geom offers only positive distance. + double distance = signed_distance( buf->rect.corner(c), axis ); + + // Correct it for coordinate flips (inverts handedness). + if (Geom::cross( axis.vector(), orth.vector() ) > 0 ) { + distance = -distance; } + + if (distance < min) + min = distance; + if (distance > max) + max = distance; } - } else { - // Dotted grid - gint ylinenum; - gdouble y; - - // alpha needs to be larger than in the line case to maintain a similar visual impact but - // setting it to the maximal value makes the dots dominant in some cases. Solution, - // increase the alpha by a factor of 4. This then allows some user adjustment. - guint32 _empdot = (_empcolor & 0xff) << 2; - if (_empdot > 0xff) - _empdot = 0xff; - _empdot += (_empcolor & 0xffffff00); - - guint32 _colordot = (color & 0xff) << 2; - if (_colordot > 0xff) - _colordot = 0xff; - _colordot += (color & 0xffffff00); - - for (y = syg, ylinenum = ylinestart; y < buf->rect.bottom(); y += sw[Geom::Y], ylinenum++) { - gint const iy = round(y); - - gint xlinenum; - gdouble x; - for (x = sxg, xlinenum = xlinestart; x < buf->rect.right(); x += sw[Geom::X], xlinenum++) { - gint const ix = round(x); - if ( (!scaled[Geom::X] && (xlinenum % empspacing) != 0) - || (!scaled[Geom::Y] && (ylinenum % empspacing) != 0) - || ((scaled[Geom::X] || scaled[Geom::Y]) && no_emp_when_zoomed_out) ) - { - // Minor point: dot only - grid_dot (buf, ix, iy, _colordot); // | (guint32)0x000000FF); // put alpha to max value + int start = floor( min/spacing ); + int stop = floor( max/spacing ); + + // std::cout << " rect: " << buf->rect << std::endl; + // std::cout << " min: " << min << " max: " << max << std::endl; + // std::cout << " start: " << start << " stop: " << stop << std::endl; + + // Loop over grid lines that intersected buf rectangle. + for (int j = start+1; j <= stop; ++j) { + + Geom::Line grid_line = Geom::make_parallel_line( ow + j * sw[(dim+1)%2], axis ); + + std::vector<Geom::Point> x = intersect_line_rectangle( grid_line, buf->rect ); + + // If we have two intersections, grid line intersects buffer rectangle. + if (x.size() == 2 ) { + + // Make sure lines are always drawn in the same direction (or dashes misplaced). + Geom::Line vector( x[0], x[1]); + if (Geom::dot( vector.vector(), axis.vector() ) < 0.0) { + std::swap(x[0], x[1]); + } + + // Set up line. + cairo_move_to(buf->ct, x[0][Geom::X] + 0.5, x[0][Geom::Y] + 0.5); + cairo_line_to(buf->ct, x[1][Geom::X] + 0.5, x[1][Geom::Y] + 0.5); + + // Set dash pattern and color. + if (render_dotted) { + + // alpha needs to be larger than in the line case to maintain a similar + // visual impact but setting it to the maximal value makes the dots + // dominant in some cases. Solution, increase the alpha by a factor of + // 4. This then allows some user adjustment. + guint32 _empdot = (_empcolor & 0xff) << 2; + if (_empdot > 0xff) + _empdot = 0xff; + _empdot += (_empcolor & 0xffffff00); + + guint32 _colordot = (color & 0xff) << 2; + if (_colordot > 0xff) + _colordot = 0xff; + _colordot += (color & 0xffffff00); + + // Dash pattern must use spacing from orthogonal direction. + // Offset is to center dash on orthogonal lines. + double offset = fmod( signed_distance( x[0], orth ), sw[dim].length()); + if (Geom::cross( axis.vector(), orth.vector() ) > 0 ) { + offset = -offset; + } + + double dashes[2]; + if (!scaled[dim] && (j % empspacing) != 0) { + // Minor lines + dashes[0] = 1; + dashes[1] = dash -1; + offset -= 0.5; + ink_cairo_set_source_rgba32(buf->ct, _colordot); + } else { + // Major lines + dashes[0] = 3; + dashes[1] = dash - 3; + offset -= 1.5; // Center dash on intersection. + ink_cairo_set_source_rgba32(buf->ct, _empdot); + } + + cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_BUTT); + cairo_set_dash(buf->ct, dashes, 2, -offset); + } else { - // Major point: small cross - gint const pitch = 1; - grid_dot (buf, ix-pitch, iy, _empcolor); - grid_dot (buf, ix+pitch, iy, _empcolor); - grid_dot (buf, ix, iy, _empdot ); // | (guint32)0x000000FF); // put alpha to max value + // Solid lines - grid_dot (buf, ix, iy-pitch, _empcolor); - grid_dot (buf, ix, iy+pitch, _empcolor); + // Set color + if (!scaled[dim] && (j % empspacing) != 0) { + ink_cairo_set_source_rgba32(buf->ct, color ); + } else { + ink_cairo_set_source_rgba32(buf->ct, _empcolor ); + } } - } + cairo_stroke(buf->ct); + + } else { + std::cerr << "CanvasXYGrid::render: Grid line doesn't intersect!" << std::endl; + } } } + cairo_restore(buf->ct); } @@ -1028,7 +1079,7 @@ CanvasXYGridSnapper::_getSnapLines(Geom::Point const &p) const if (getSnapVisibleOnly()) { // Only snapping to visible grid lines - spacing = grid->sw[i]; // this is the spacing of the visible grid lines measured in screen pixels + spacing = grid->sw[i].length(); // this is the spacing of the visible grid lines measured in screen pixels // convert screen pixels to px // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary SPDesktop const *dt = _snapmanager->getDesktop(); diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h index 557bd6dab..91fa43e69 100644 --- a/src/display/canvas-grid.h +++ b/src/display/canvas-grid.h @@ -101,6 +101,9 @@ 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 isLegacy() const { return legacy; } + bool isPixel() const { return pixel; } + bool isVisible() const { return (isEnabled() &&visible); }; bool isEnabled() const; @@ -118,7 +121,11 @@ protected: GridType gridtype; -private: + // For dealing with old Inkscape SVG files (pre 0.92) + bool legacy; + bool pixel; + + private: CanvasGrid(const CanvasGrid&); CanvasGrid& operator=(const CanvasGrid&); }; @@ -129,6 +136,7 @@ public: CanvasXYGrid(SPNamedView * nv, Inkscape::XML::Node * in_repr, SPDocument * in_doc); virtual ~CanvasXYGrid(); + virtual void Scale (Geom::Scale const &scale); virtual void Update (Geom::Affine const &affine, unsigned int flags); virtual void Render (SPCanvasBuf *buf); @@ -140,7 +148,7 @@ public: be different in the X or Y direction, hence two variables */ Geom::Point ow; /**< Transformed origin by the affine for the zoom */ - Geom::Point sw; /**< Transformed spacing by the affine for the zoom */ + Geom::Point sw[2]; /**< Transformed spacing by the affine for the zoom */ protected: virtual Gtk::Widget * newSpecificWidget(); diff --git a/src/display/canvas-rotate.cpp b/src/display/canvas-rotate.cpp new file mode 100644 index 000000000..aaf6b962c --- /dev/null +++ b/src/display/canvas-rotate.cpp @@ -0,0 +1,288 @@ +/* + * Temporary surface for previewing rotated canvas. + * + * Author: + * Tavmjong Bah <tavmjong@free.fr> + * + * Copyright (C) 2017 Tavmjong Bah + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "inkscape.h" +#include "desktop.h" + +#include "canvas-rotate.h" +#include "sp-canvas.h" +#include "cairo-utils.h" +#include "ui/event-debug.h" + +#include "2geom/point.h" +#include "2geom/rect.h" + +#include <gtk/gtk.h> + +namespace { + +static void sp_canvas_rotate_destroy(SPCanvasItem *item); +static void sp_canvas_rotate_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags); +static void sp_canvas_rotate_render (SPCanvasItem *item, SPCanvasBuf *buf); +static int sp_canvas_rotate_event (SPCanvasItem *item, GdkEvent *event); + +} // namespace + +void sp_canvas_rotate_paint (SPCanvasRotate *canvas_rotate, cairo_surface_t *background); + +G_DEFINE_TYPE(SPCanvasRotate, sp_canvas_rotate, SP_TYPE_CANVAS_ITEM); + +static void sp_canvas_rotate_class_init (SPCanvasRotateClass *klass) +{ + klass->destroy = sp_canvas_rotate_destroy; + //klass->update = sp_canvas_rotate_update; + //klass->render = sp_canvas_rotate_render; + klass->event = sp_canvas_rotate_event; +} + +static void sp_canvas_rotate_init (SPCanvasRotate *rotate) +{ + rotate->pickable = true; // So we can receive events. + rotate->angle = 0.0; + rotate->start_angle = -1000; + rotate->surface_copy = NULL; + rotate->surface_rotated = NULL; +} + +namespace { +static void sp_canvas_rotate_destroy (SPCanvasItem *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (SP_IS_CANVAS_ROTATE (object)); + + if (SP_CANVAS_ITEM_CLASS(sp_canvas_rotate_parent_class)->destroy) { + SP_CANVAS_ITEM_CLASS(sp_canvas_rotate_parent_class)->destroy(object); + } +} + +// NOT USED... TOO SLOW +static void sp_canvas_rotate_update( SPCanvasItem *item, Geom::Affine const &/*affine*/, unsigned int /*flags*/ ) +{ + SPCanvasRotate *cr = SP_CANVAS_ROTATE(item); + + if (cr->surface_copy == NULL) { + // std::cout << "sp_canvas_rotate_update: surface_copy is NULL" << std::endl; + return; + } + + // Destroy surface_rotated if it already exists. + if (cr->surface_rotated != NULL) { + cairo_surface_destroy (cr->surface_rotated); + cr->surface_rotated = NULL; + } + + // Create rotated surface + cr->surface_rotated = ink_cairo_surface_create_identical(cr->surface_copy); + double width = cairo_image_surface_get_width (cr->surface_rotated); + double height = cairo_image_surface_get_height (cr->surface_rotated); + cairo_t *context = cairo_create( cr->surface_rotated ); + cairo_set_operator( context, CAIRO_OPERATOR_SOURCE ); + cairo_translate( context, width/2.0, height/2.0 ); + cairo_rotate( context, Geom::rad_from_deg(-cr->angle) ); + cairo_translate( context, -width/2.0, -height/2.0 ); + cairo_set_source_surface( context, cr->surface_copy, 0, 0 ); + cairo_paint( context ); + cairo_destroy( context); + + // We cover the entire canvas + item->x1 = -G_MAXINT; + item->y1 = -G_MAXINT; + item->x2 = G_MAXINT; + item->y2 = G_MAXINT; + + item->canvas->requestRedraw(item->x1, item->y1, item->x2, item->y2); +} + +// NOT USED... TOO SLOW +static void sp_canvas_rotate_render( SPCanvasItem *item, SPCanvasBuf *buf) +{ + // std::cout << "sp_canvas_rotate_render:" << std::endl; + // std::cout << " buf->rect: " << buf->rect << std::endl; + // std::cout << " buf->canvas_rect: " << buf->canvas_rect << std::endl; + SPCanvasRotate *cr = SP_CANVAS_ROTATE(item); + + if (!buf->ct) { + return; + } + + if (cr->surface_rotated == NULL ) { + // std::cout << " surface_rotated is NULL" << std::endl; + return; + } + + // Draw rotated canvas + cairo_save (buf->ct); + cairo_translate (buf->ct, + buf->canvas_rect.left() - buf->rect.left(), + buf->canvas_rect.top() - buf->rect.top() ); + cairo_set_operator (buf->ct, CAIRO_OPERATOR_SOURCE ); + cairo_set_source_surface (buf->ct, cr->surface_rotated, 0, 0 ); + cairo_paint (buf->ct); + cairo_restore (buf->ct); + + + // Draw line from center to cursor + cairo_save (buf->ct); + cairo_translate (buf->ct, -buf->rect.left(), -buf->rect.top()); + cairo_new_path (buf->ct); + cairo_move_to (buf->ct, cr->center[Geom::X], cr->center[Geom::Y]); + cairo_rel_line_to (buf->ct, cr->cursor[Geom::X], cr->cursor[Geom::Y]); + cairo_set_line_width (buf->ct, 2); + ink_cairo_set_source_rgba32 (buf->ct, 0xff00007f); + cairo_stroke (buf->ct); + cairo_restore (buf->ct); + +} + +static int sp_canvas_rotate_event (SPCanvasItem *item, GdkEvent *event) +{ + SPCanvasRotate *cr = SP_CANVAS_ROTATE(item); + +// ui_dump_event (event, Glib::ustring("sp_canvas_rotate_event")); + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + Geom::Rect viewbox = desktop->canvas->getViewbox(); // Not SVG viewbox! + cr->center = viewbox.midpoint(); + + switch (event->type) { + case GDK_MOTION_NOTIFY: + { + Geom::Point cursor( event->motion.x, event->motion.y ); + + // Both cursor and center are in window coordinates + Geom::Point rcursor( cursor - cr->center ); + double angle = Geom::deg_from_rad( Geom::atan2(rcursor) ); + + // Set start angle + if (cr->start_angle < -360) { + cr->start_angle = angle; + } + + double rotation_snap = 15; + + double delta_angle = cr->start_angle - angle; + + if (event->motion.state & GDK_SHIFT_MASK && + event->motion.state & GDK_CONTROL_MASK) { + delta_angle = 0; + } else if (event->motion.state & GDK_SHIFT_MASK) { + delta_angle = round(delta_angle/rotation_snap) * rotation_snap; + } else if (event->motion.state & GDK_CONTROL_MASK) { + // ? + } else if (event->motion.state & GDK_MOD1_MASK) { + // Decimal raw angle + } else { + delta_angle = floor(delta_angle); + } + + cr->angle = delta_angle; + + // Correct line for snapping of angle + double distance = rcursor.length(); + cr->cursor = Geom::Point::polar( Geom::rad_from_deg(angle), distance ); + + // Update screen + // sp_canvas_item_request_update( item ); + sp_canvas_rotate_paint (cr, cr->canvas->_backing_store); + break; + } + case GDK_BUTTON_RELEASE: + + // Rotate the actual canvas + desktop->rotate_relative_center_point (desktop->w2d(cr->center), + Geom::rad_from_deg(cr->angle) ); + + // We're done + sp_canvas_item_ungrab (item, event->button.time); + sp_canvas_item_hide (item); + + cr->start_angle = -1000; + if (cr->surface_copy != NULL) { + cairo_surface_destroy( cr->surface_copy ); + cr->surface_copy = NULL; + } + if (cr->surface_rotated != NULL) { + cairo_surface_destroy( cr->surface_rotated ); + cr->surface_rotated = NULL; + } + // sp_canvas_item_show (desktop->drawing); + + break; + case GDK_KEY_PRESS: + // std::cout << " Key press: " << std::endl; + break; + case GDK_KEY_RELEASE: + // std::cout << " Key release: " << std::endl; + break; + default: + // ui_dump_event (event, "sp_canvas_rotate_event: unwanted event: "); + break; + } + + if (event->type == GDK_KEY_PRESS) return false; + + return true; +} + +} // namespace + +void sp_canvas_rotate_start (SPCanvasRotate *canvas_rotate, cairo_surface_t *background) +{ + if (background == NULL) { + std::cerr << "sp_canvas_rotate_start: background is NULL!" << std::endl; + return; + } + + canvas_rotate->angle = 0.0; + + // Copy current background + canvas_rotate->surface_copy = ink_cairo_surface_copy( background ); + + // Paint canvas with background... since we are hiding drawing. + sp_canvas_item_request_update( canvas_rotate ); +} + +// Paint the canvas ourselves for speed.... +void sp_canvas_rotate_paint (SPCanvasRotate *canvas_rotate, cairo_surface_t *background) +{ + if (background == NULL) { + std::cerr << "sp_canvas_rotate_paint: background is NULL!" << std::endl; + return; + } + + double width = cairo_image_surface_get_width (background); + double height = cairo_image_surface_get_height (background); + + // Draw rotated canvas + cairo_t *context = cairo_create( background ); + + cairo_save (context); + cairo_set_operator( context, CAIRO_OPERATOR_SOURCE ); + cairo_translate( context, width/2.0, height/2.0 ); + cairo_rotate( context, Geom::rad_from_deg(-canvas_rotate->angle) ); + cairo_translate( context, -width/2.0, -height/2.0 ); + cairo_set_source_surface( context, canvas_rotate->surface_copy, 0, 0 ); + cairo_paint( context ); + cairo_restore( context ); + cairo_destroy( context ); + + gtk_widget_queue_draw (GTK_WIDGET (canvas_rotate->canvas)); +} +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/display/canvas-rotate.h b/src/display/canvas-rotate.h new file mode 100644 index 000000000..8620dcced --- /dev/null +++ b/src/display/canvas-rotate.h @@ -0,0 +1,50 @@ +#ifndef SEEN_SP_CANVAS_ROTATE_H +#define SEEN_SP_CANVAS_ROTATE_H + +/* + * Temporary surface for previewing rotated canvas. + * + * Author: + * Tavmjong Bah <tavmjong@free.fr> + * + * Copyright (C) 2017 Tavmjong Bah + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "sp-canvas-item.h" +#include "2geom/line.h" + +class SPItem; + +#define SP_TYPE_CANVAS_ROTATE (sp_canvas_rotate_get_type ()) +#define SP_CANVAS_ROTATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_CANVAS_ROTATE, SPCanvasRotate)) +#define SP_IS_CANVAS_ROTATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_CANVAS_ROTATE)) + +struct SPCanvasRotate : public SPCanvasItem { + Geom::Point center; // Center of screen + Geom::Point cursor; // Position of cursor relative to center (after angle snapping) + double angle; // Rotation angle in degrees + double start_angle; // Starting angle of cursor + cairo_surface_t *surface_copy; // Copy of original surface + cairo_surface_t *surface_rotated; // Copy of original surface, rotated +}; + +void sp_canvas_rotate_start( SPCanvasRotate *canvas_rotate, cairo_surface_t *background ); + +GType sp_canvas_rotate_get_type (void); + +struct SPCanvasRotateClass : public SPCanvasItemClass{}; + +#endif // SEEN_SP_CANVAS_ROTATE_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/display/canvas-text.cpp b/src/display/canvas-text.cpp index 7c019caf5..efef018e6 100644 --- a/src/display/canvas-text.cpp +++ b/src/display/canvas-text.cpp @@ -84,6 +84,7 @@ sp_canvastext_render (SPCanvasItem *item, SPCanvasBuf *buf) if (!buf->ct) return; + cairo_select_font_face(buf->ct, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(buf->ct, cl->fontsize); if (cl->background){ @@ -138,6 +139,7 @@ sp_canvastext_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned i cairo_surface_t *tmp_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); cairo_t* tmp_buf = cairo_create(tmp_surface); + cairo_select_font_face(tmp_buf, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(tmp_buf, cl->fontsize); cairo_text_extents_t extents; cairo_text_extents(tmp_buf, cl->text, &extents); diff --git a/src/display/curve.cpp b/src/display/curve.cpp index b6c387034..6e662b17b 100644 --- a/src/display/curve.cpp +++ b/src/display/curve.cpp @@ -302,6 +302,18 @@ SPCurve::is_empty() const } /** + * True if paths are in curve. If it only contains a path with only a moveto, the path is considered as unset FALSE + */ +bool +SPCurve::is_unset() const +{ + if (get_segment_count()) { + return false; + } + return true; +} + +/** * True iff all subpaths are closed. * Returns false if the curve is empty. */ @@ -323,6 +335,20 @@ SPCurve::is_closed() const } /** + * True if both curves are equal + */ +bool +SPCurve::is_equal(SPCurve * other) const +{ + if(other == NULL) { + return false; + } else if(_pathv == other->get_pathvector()){ + return true; + } + return false; +} + +/** * Return last pathsegment (possibly the closing path segment) of the last path in PathVector or NULL. * If the last path is empty (contains only a moveto), the function returns NULL */ diff --git a/src/display/curve.h b/src/display/curve.h index 42b899210..8375e1105 100644 --- a/src/display/curve.h +++ b/src/display/curve.h @@ -43,7 +43,9 @@ public: size_t nodes_in_path() const; bool is_empty() const; + bool is_unset() const; bool is_closed() const; + bool is_equal(SPCurve * other) const; Geom::Curve const * last_segment() const; Geom::Path const * last_path() const; Geom::Curve const * first_segment() const; diff --git a/src/display/drawing-group.cpp b/src/display/drawing-group.cpp index 1a9cbfdcc..018f23e74 100644 --- a/src/display/drawing-group.cpp +++ b/src/display/drawing-group.cpp @@ -95,6 +95,7 @@ DrawingGroup::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigne if (stop_at == NULL) { // normal rendering for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + i->setAntialiasing(_antialias); i->render(dc, area, flags, stop_at); } } else { @@ -103,10 +104,12 @@ DrawingGroup::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigne if (&*i == stop_at) return RENDER_OK; // do not render the stop_at item at all if (i->isAncestorOf(stop_at)) { // render its ancestors without masks, opacity or filters + i->setAntialiasing(_antialias); i->render(dc, area, flags | RENDER_FILTER_BACKGROUND, stop_at); // stop further rendering return RENDER_OK; } else { + i->setAntialiasing(_antialias); i->render(dc, area, flags, stop_at); } } diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index e23c89c59..508fcc503 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -116,16 +116,22 @@ unsigned DrawingImage::_renderItem(DrawingContext &dc, Geom::IntRect const &/*ar if (_style) { // See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty // http://www.w3.org/TR/css4-images/#the-image-rendering + // It's back in CSS Images 3 now. // style.h/style.cpp switch (_style->image_rendering.computed) { case SP_CSS_IMAGE_RENDERING_AUTO: - // Do nothing - break; case SP_CSS_IMAGE_RENDERING_OPTIMIZEQUALITY: + case SP_CSS_IMAGE_RENDERING_CRISPEDGES: + // CSS 3 defines: + // 'auto' to use smoothing + // 'optimize-quality' as alias for auto + // We don't have special rendering for 'crisp-edges' yet + // so follow what browsers do. // In recent Cairo, BEST used Lanczos3, which is prohibitively slow dc.patternSetFilter( CAIRO_FILTER_GOOD ); break; case SP_CSS_IMAGE_RENDERING_OPTIMIZESPEED: + case SP_CSS_IMAGE_RENDERING_PIXELATED: default: dc.patternSetFilter( CAIRO_FILTER_NEAREST ); break; diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 89ca66dc4..c4af81efc 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -132,7 +132,7 @@ DrawingItem::DrawingItem(Drawing &drawing) , _propagate(0) // , _renders_opacity(0) , _pick_children(0) - , _antialias(1) + , _antialias(2) , _isolation(SP_CSS_ISOLATION_AUTO) , _mix_blend_mode(SP_CSS_BLEND_NORMAL) {} @@ -291,7 +291,7 @@ DrawingItem::setOpacity(float opacity) } void -DrawingItem::setAntialiasing(bool a) +DrawingItem::setAntialiasing(unsigned a) { if (_antialias != a) { _antialias = a; @@ -699,10 +699,21 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag Geom::OptIntRect carea = Geom::intersect(area, _drawbox); if (!carea) return RENDER_OK; - if (_antialias) { - cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_DEFAULT); - } else { - cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_NONE); + switch(_antialias){ + case 0: + cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_NONE); + break; + case 1: + cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_FAST); + break; + case 2: + cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_GOOD); + break; + case 3: + cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_BEST); + break; + default: // should not happen + g_assert_not_reached(); } // render from cache if possible diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index 3c593ceaa..21f6ffacc 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -115,7 +115,7 @@ public: virtual void setStyle(SPStyle *style, SPStyle *context_style = NULL); virtual void setChildrenStyle(SPStyle *context_style); void setOpacity(float opacity); - void setAntialiasing(bool a); + void setAntialiasing(unsigned a); void setIsolation(unsigned isolation); // CSS Compositing and Blending void setBlendMode(unsigned blend_mode); void setTransform(Geom::Affine const &trans); @@ -222,7 +222,7 @@ protected: //unsigned _renders_opacity : 1; ///< Whether object needs temporary surface for opacity unsigned _pick_children : 1; ///< For groups: if true, children are returned from pick(), /// otherwise the group is returned - unsigned _antialias : 1; ///< Whether to use antialiasing + unsigned _antialias : 2; ///< antialiasing level (NONE/FAST/GOOD(DEFAULT)/BEST) unsigned _isolation : 1; unsigned _mix_blend_mode : 4; diff --git a/src/display/drawing-shape.cpp b/src/display/drawing-shape.cpp index 63efb3c0d..d7329e670 100644 --- a/src/display/drawing-shape.cpp +++ b/src/display/drawing-shape.cpp @@ -180,6 +180,10 @@ DrawingShape::_renderStroke(DrawingContext &dc) if( has_stroke ) { // TODO: remove segments outside of bbox when no dashes present dc.path(_curve->get_pathvector()); + if (_style && _style->vector_effect.computed == SP_VECTOR_EFFECT_NON_SCALING_STROKE) { + dc.restore(); + dc.save(); + } _nrstyle.applyStroke(dc); dc.strokePreserve(); dc.newPath(); // clear path @@ -231,20 +235,24 @@ DrawingShape::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigne Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); + // update fill and stroke paints. // this cannot be done during nr_arena_shape_update, because we need a Cairo context // to render svg:pattern bool has_fill = _nrstyle.prepareFill(dc, _item_bbox, _fill_pattern); bool has_stroke = _nrstyle.prepareStroke(dc, _item_bbox, _stroke_pattern); has_stroke &= (_nrstyle.stroke_width != 0); - if (has_fill || has_stroke) { - // TODO: remove segments outside of bbox when no dashes present dc.path(_curve->get_pathvector()); + // TODO: remove segments outside of bbox when no dashes present if (has_fill) { _nrstyle.applyFill(dc); dc.fillPreserve(); } + if (_style && _style->vector_effect.computed == SP_VECTOR_EFFECT_NON_SCALING_STROKE) { + dc.restore(); + dc.save(); + } if (has_stroke) { _nrstyle.applyStroke(dc); dc.strokePreserve(); diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index f0d83abfd..21af7b200 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -269,7 +269,7 @@ void DrawingText::decorateStyle(DrawingContext &dc, double vextent, double xphas int dashes[16]={ 8, 7, 6, 5, 4, 3, 2, 1, - -8, -7, -6, -5 + -8, -7, -6, -5, -4, -3, -2, -1 }; int dots[16]={ @@ -592,17 +592,24 @@ unsigned DrawingText::_renderItem(DrawingContext &dc, Geom::IntRect const &/*are { Inkscape::DrawingContext::Save save(dc); dc.transform(_ctm); - if (has_fill && fill_first) { _nrstyle.applyFill(dc); dc.fillPreserve(); } - + } + { + Inkscape::DrawingContext::Save save(dc); + if (!_style || !(_style->vector_effect.computed == SP_VECTOR_EFFECT_NON_SCALING_STROKE)) { + dc.transform(_ctm); + } if (has_stroke) { _nrstyle.applyStroke(dc); dc.strokePreserve(); } - + } + { + Inkscape::DrawingContext::Save save(dc); + dc.transform(_ctm); if (has_fill && !fill_first) { _nrstyle.applyFill(dc); dc.fillPreserve(); diff --git a/src/display/drawing.cpp b/src/display/drawing.cpp index eadd7e528..71fb94be0 100644 --- a/src/display/drawing.cpp +++ b/src/display/drawing.cpp @@ -167,10 +167,14 @@ Drawing::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned fl } void -Drawing::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags) +Drawing::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, int antialiasing) { if (_root) { + int prev_a = _root->_antialias; + if(antialiasing >= 0) + _root->setAntialiasing(antialiasing); _root->render(dc, area, flags); + _root->setAntialiasing(prev_a); } if (colorMode() == COLORMODE_GRAYSCALE) { diff --git a/src/display/drawing.h b/src/display/drawing.h index 0c12b1510..e472c8f5b 100644 --- a/src/display/drawing.h +++ b/src/display/drawing.h @@ -68,7 +68,7 @@ public: void setGrayscaleMatrix(double 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 &dc, Geom::IntRect const &area, unsigned flags = 0); + void render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags = 0, int antialiasing = -1); DrawingItem *pick(Geom::Point const &p, double delta, unsigned flags); sigc::signal<void, DrawingItem *> signal_request_update; diff --git a/src/display/gnome-canvas-acetate.cpp b/src/display/gnome-canvas-acetate.cpp index 9147a1bbf..297d69068 100644 --- a/src/display/gnome-canvas-acetate.cpp +++ b/src/display/gnome-canvas-acetate.cpp @@ -14,11 +14,13 @@ */ #include "gnome-canvas-acetate.h" +#include "ui/event-debug.h" static void sp_canvas_acetate_destroy(SPCanvasItem *object); static void sp_canvas_acetate_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags); static double sp_canvas_acetate_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item); +static int sp_canvas_acetate_event( SPCanvasItem *item, GdkEvent *event); G_DEFINE_TYPE(SPCanvasAcetate, sp_canvas_acetate, SP_TYPE_CANVAS_ITEM); @@ -29,6 +31,7 @@ static void sp_canvas_acetate_class_init (SPCanvasAcetateClass *klass) item_class->destroy = sp_canvas_acetate_destroy; item_class->update = sp_canvas_acetate_update; item_class->point = sp_canvas_acetate_point; + // item_class->event = sp_canvas_acetate_event; } static void sp_canvas_acetate_init (SPCanvasAcetate */*acetate*/) @@ -60,3 +63,8 @@ static double sp_canvas_acetate_point( SPCanvasItem *item, Geom::Point /*p*/, SP return 0.0; } +// static int sp_canvas_acetate_event( SPCanvasItem *item, GdkEvent *event) +// { +// ui_dump_event (event, "sp_canvas_acetate_event"); +// return 0; // ? +// } diff --git a/src/display/guideline.cpp b/src/display/guideline.cpp index 126fcf87c..cde10f6c2 100644 --- a/src/display/guideline.cpp +++ b/src/display/guideline.cpp @@ -10,12 +10,15 @@ * Copyright (C) 2000-2002 Lauris Kaplinski * Copyright (C) 2007 Johan Engelen * Copyright (C) 2009 Maximilian Albert + * Copyright (C) 2017 Tavmjong Bah * * Released under GNU GPL, read the file 'COPYING' for more information */ #include <2geom/coord.h> #include <2geom/transforms.h> +#include <2geom/line.h> + #include "sp-canvas-util.h" #include "guideline.h" #include "display/cairo-utils.h" @@ -29,8 +32,6 @@ static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf); static double sp_guideline_point(SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item); -static void sp_guideline_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 rgba); - G_DEFINE_TYPE(SPGuideLine, sp_guideline, SP_TYPE_CANVAS_ITEM); static void sp_guideline_class_init(SPGuideLineClass *c) @@ -48,7 +49,7 @@ static void sp_guideline_init(SPGuideLine *gl) gl->locked = false; gl->normal_to_line = Geom::Point(0,1); - gl->angle = 3.14159265358979323846/2; + gl->angle = M_PI_2; gl->point_on_line = Geom::Point(0,0); gl->sensitive = 0; @@ -76,10 +77,6 @@ static void sp_guideline_destroy(SPCanvasItem *object) static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf) { - //TODO: the routine that renders the label of a specific guideline sometimes - // ends up erasing the labels of the other guidelines. - // Maybe we should render all labels everytime. - SPGuideLine const *gl = SP_GUIDELINE (item); cairo_save(buf->ct); @@ -89,8 +86,9 @@ static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf) cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE); cairo_set_font_size(buf->ct, 10); - Geom::Point normal_dt = /*unit_vector*/(gl->normal_to_line * gl->affine.withoutTranslation()); // note that normal_dt does not have unit length - Geom::Point point_on_line_dt = gl->point_on_line * gl->affine; + // normal_dt does not have unit length + Geom::Point normal_dt = gl->normal_to_line * gl->affine.withoutTranslation(); + Geom::Point point_on_line_dt = gl->point_on_line * gl->affine; if (gl->label) { int px = round(point_on_line_dt[Geom::X]); @@ -104,55 +102,54 @@ static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf) cairo_restore(buf->ct); } - if ( Geom::are_near(normal_dt[Geom::Y], 0.) ) { // is vertical? + // Draw guide. + // Special case horizontal and vertical lines so they snap to pixels. + // Don't use isHorizontal()/isVertical() as they test only exact matches. + if ( Geom::are_near(normal_dt[Geom::Y], 0.0) ) { // Vertical? + int position = round(point_on_line_dt[Geom::X]); cairo_move_to(buf->ct, position + 0.5, buf->rect.top() + 0.5); cairo_line_to(buf->ct, position + 0.5, buf->rect.bottom() - 0.5); cairo_stroke(buf->ct); - } else if ( Geom::are_near(normal_dt[Geom::X], 0.) ) { // is horizontal? + + } else if ( Geom::are_near(normal_dt[Geom::X], 0.0) ) { // Horizontal? + int position = round(point_on_line_dt[Geom::Y]); cairo_move_to(buf->ct, buf->rect.left() + 0.5, position + 0.5); cairo_line_to(buf->ct, buf->rect.right() - 0.5, position + 0.5); cairo_stroke(buf->ct); - } else { - // render angled line. Once intersection has been detected, draw from there. - Geom::Point parallel_to_line( normal_dt.ccw() ); - - //try to intersect with left vertical of rect - double y_intersect_left = (buf->rect.left() - point_on_line_dt[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + point_on_line_dt[Geom::Y]; - if ( (y_intersect_left >= buf->rect.top()) && (y_intersect_left <= buf->rect.bottom()) ) { - // intersects with left vertical! - double y_intersect_right = (buf->rect.right() - point_on_line_dt[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + point_on_line_dt[Geom::Y]; - sp_guideline_drawline (buf, buf->rect.left(), static_cast<gint>(round(y_intersect_left)), buf->rect.right(), static_cast<gint>(round(y_intersect_right)), gl->rgba); - goto end; - } - //try to intersect with right vertical of rect - double y_intersect_right = (buf->rect.right() - point_on_line_dt[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + point_on_line_dt[Geom::Y]; - if ( (y_intersect_right >= buf->rect.top()) && (y_intersect_right <= buf->rect.bottom()) ) { - // intersects with right vertical! - sp_guideline_drawline (buf, buf->rect.right(), static_cast<gint>(round(y_intersect_right)), buf->rect.left(), static_cast<gint>(round(y_intersect_left)), gl->rgba); - goto end; - } + } else { - //try to intersect with top horizontal of rect - double x_intersect_top = (buf->rect.top() - point_on_line_dt[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + point_on_line_dt[Geom::X]; - if ( (x_intersect_top >= buf->rect.left()) && (x_intersect_top <= buf->rect.right()) ) { - // intersects with top horizontal! - double x_intersect_bottom = (buf->rect.bottom() - point_on_line_dt[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + point_on_line_dt[Geom::X]; - sp_guideline_drawline (buf, static_cast<gint>(round(x_intersect_top)), buf->rect.top(), static_cast<gint>(round(x_intersect_bottom)), buf->rect.bottom(), gl->rgba); - goto end; + Geom::Line guide = + Geom::Line::from_origin_and_vector( point_on_line_dt, Geom::rot90(normal_dt) ); + + // Find intersections of guide with buf rectangle. There should be zero or two. + std::vector<Geom::Point> intersections; + for (unsigned i = 0; i < 4; ++i) { + Geom::LineSegment side( buf->rect.corner(i), buf->rect.corner((i+1)%4) ); + try { + Geom::OptCrossing oc = Geom::intersection(guide, side); + if (oc) { + intersections.push_back( guide.pointAt((*oc).ta)); + } + } catch (Geom::InfiniteSolutions) { + // Shouldn't happen as we have already taken care of horizontal/vertical guides. + std::cerr << "sp_guideline_render: Error: Infinite intersections." << std::endl; + } } - //try to intersect with bottom horizontal of rect - double x_intersect_bottom = (buf->rect.bottom() - point_on_line_dt[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + point_on_line_dt[Geom::X]; - if ( (x_intersect_top >= buf->rect.left()) && (x_intersect_top <= buf->rect.right()) ) { - // intersects with bottom horizontal! - sp_guideline_drawline (buf, static_cast<gint>(round(x_intersect_bottom)), buf->rect.bottom(), static_cast<gint>(round(x_intersect_top)), buf->rect.top(), gl->rgba); - goto end; + if (intersections.size() == 2) { + double x0 = intersections[0][Geom::X]; + double x1 = intersections[1][Geom::X]; + double y0 = intersections[0][Geom::Y]; + double y1 = intersections[1][Geom::Y]; + cairo_move_to(buf->ct, x0, y0); + cairo_line_to(buf->ct, x1, y1); + cairo_stroke(buf->ct); } } - end: + cairo_restore(buf->ct); } @@ -179,15 +176,10 @@ static void sp_guideline_update(SPCanvasItem *item, Geom::Affine const &affine, } gl->affine = affine; - Geom::Point pol_transformed = gl->point_on_line * affine; - if (gl->is_horizontal()) { - sp_canvas_update_bbox (item, -1000000, round(pol_transformed[Geom::Y] - 16), 1000000, round(pol_transformed[Geom::Y] + 1)); - } else if (gl->is_vertical()) { - sp_canvas_update_bbox (item, round(pol_transformed[Geom::X]), -1000000, round(pol_transformed[Geom::X] + 16), 1000000); - } else { - //TODO: labels in angled guidelines are not showing up for some reason. - sp_canvas_update_bbox (item, -1000000, -1000000, 1000000, 1000000); - } + + // Rotated canvas requires large bbox for all guides. + sp_canvas_update_bbox (item, -1000000, -1000000, 1000000, 1000000); + } // Returns 0.0 if point is on the guideline @@ -276,14 +268,6 @@ void sp_guideline_delete(SPGuideLine *gl) sp_canvas_item_destroy(SP_CANVAS_ITEM(gl)); } -static void -sp_guideline_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 /*rgba*/) -{ - cairo_move_to(buf->ct, x0 + 0.5, y0 + 0.5); - cairo_line_to(buf->ct, x1 + 0.5, y1 + 0.5); - cairo_stroke(buf->ct); -} - /* Local Variables: mode:c++ diff --git a/src/display/nr-filter-turbulence.cpp b/src/display/nr-filter-turbulence.cpp index 19dedb67c..1397c0f34 100644 --- a/src/display/nr-filter-turbulence.cpp +++ b/src/display/nr-filter-turbulence.cpp @@ -286,15 +286,7 @@ private: static int const BSize = 0x100; static int const BMask = 0xff; -#ifdef CPP11 // GCC 4.6.1 (currently used on Windows) does not correctly set __cplusplus in C++11 mode, so configure with -DCPP11 to make this work in C++11 mode static double constexpr PerlinOffset = 4096.0; -#else -#if (__cplusplus < 201103L) - static double const PerlinOffset; -#else - static double constexpr PerlinOffset = 4096.0; -#endif -#endif Geom::Rect _tile; Geom::Point _baseFreq; @@ -311,10 +303,6 @@ private: bool _fractalnoise; }; -#if !defined(CPP11) && __cplusplus < 201103L - double const TurbulenceGenerator::PerlinOffset = 4096.0; -#endif - FilterTurbulence::FilterTurbulence() : gen(new TurbulenceGenerator()) , XbaseFrequency(0) diff --git a/src/display/nr-filter-utils.h b/src/display/nr-filter-utils.h index 35a74d7c1..52b6bd11a 100644 --- a/src/display/nr-filter-utils.h +++ b/src/display/nr-filter-utils.h @@ -14,8 +14,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "round.h" - namespace Inkscape { namespace Filters { diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index 789758d9c..d430553d4 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -48,11 +48,6 @@ #include "sp-filter-units.h" #include "preferences.h" -#if defined (SOLARIS) && (SOLARIS == 8) -#include "round.h" -using Inkscape::round; -#endif - namespace Inkscape { namespace Filters { diff --git a/src/display/nr-svgfonts.cpp b/src/display/nr-svgfonts.cpp index 011f51977..fd092aed8 100644 --- a/src/display/nr-svgfonts.cpp +++ b/src/display/nr-svgfonts.cpp @@ -197,21 +197,27 @@ SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t */*scaled_font*/, bool is_horizontal_text = true; //TODO _utf8 = (char*) utf8; + double font_height = units_per_em(); while(g_utf8_get_char(_utf8)){ len = 0; for (i=0; i < (unsigned long) this->glyphs.size(); i++){ //check whether is there a glyph declared on the SVG document // that matches with the text string in its current position if ( (len = size_of_substring(this->glyphs[i]->unicode.c_str(), _utf8)) ){ - for(SPObject* node = this->font->children;previous_unicode && node;node=node->next){ + for(auto& node: font->children) { + if (!previous_unicode) { + break; + } //apply glyph kerning if appropriate - SPHkern *hkern = dynamic_cast<SPHkern *>(node); - if (hkern && is_horizontal_text && MatchHKerningRule(hkern, this->glyphs[i], previous_unicode, previous_glyph_name) ){ - x -= (hkern->k / 1000.0);//TODO: use here the height of the font + SPHkern *hkern = dynamic_cast<SPHkern *>(&node); + if (hkern && is_horizontal_text && + MatchHKerningRule(hkern, this->glyphs[i], previous_unicode, previous_glyph_name) ){ + x -= (hkern->k / font_height); } - SPVkern *vkern = dynamic_cast<SPVkern *>(node); - if (vkern && !is_horizontal_text && MatchVKerningRule(vkern, this->glyphs[i], previous_unicode, previous_glyph_name) ){ - y -= (vkern->k / 1000.0);//TODO: use here the "height" of the font + SPVkern *vkern = dynamic_cast<SPVkern *>(&node); + if (vkern && !is_horizontal_text && + MatchVKerningRule(vkern, this->glyphs[i], previous_unicode, previous_glyph_name) ){ + y -= (vkern->k / font_height); } } previous_unicode = const_cast<char*>(this->glyphs[i]->unicode.c_str());//used for kerning checking @@ -221,8 +227,15 @@ SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t */*scaled_font*/, (*glyphs)[count++].y = y; //advance glyph coordinates: - if (is_horizontal_text) x+=(this->font->horiz_adv_x/1000.0);//TODO: use here the height of the font - else y+=(this->font->vert_adv_y/1000.0);//TODO: use here the "height" of the font + if (is_horizontal_text) { + if (this->glyphs[i]->horiz_adv_x != 0) { + x+=(this->glyphs[i]->horiz_adv_x/font_height); + } else { + x+=(this->font->horiz_adv_x/font_height); + } + } else { + y+=(this->font->vert_adv_y/font_height); + } _utf8+=len; //advance 'len' bytes in our string pointer //continue; goto raptorz; @@ -235,8 +248,8 @@ SvgFont::scaled_font_text_to_glyphs (cairo_scaled_font_t */*scaled_font*/, (*glyphs)[count++].y = y; //advance glyph coordinates: - if (is_horizontal_text) x+=(this->font->horiz_adv_x/1000.0);//TODO: use here the height of the font - else y+=(this->font->vert_adv_y/1000.0);//TODO: use here the "height" of the font + if (is_horizontal_text) x+=(this->font->horiz_adv_x/font_height);//TODO: use here the height of the font + else y+=(this->font->vert_adv_y/font_height);//TODO: use here the "height" of the font _utf8 = g_utf8_next_char(_utf8); //advance 1 char in our string pointer } @@ -252,9 +265,7 @@ SvgFont::render_glyph_path(cairo_t* cr, Geom::PathVector* pathv){ cairo_new_path(cr); //adjust scale of the glyph -// Geom::Scale s(1.0/((SPFont*) node->parent)->horiz_adv_x); - Geom::Scale s(1.0/1000);//TODO: use here the units-per-em attribute? - + Geom::Scale s(1.0/units_per_em()); Geom::Rect area( Geom::Point(0,0), Geom::Point(1,1) ); //I need help here! (reaction: note that the 'area' parameter is an *optional* rect, so you can pass an empty Geom::OptRect() ) feed_pathvector_to_cairo (cr, *pathv, s, area, false, 0); @@ -270,11 +281,11 @@ SvgFont::glyph_modified(SPObject* /* blah */, unsigned int /* bleh */){ Geom::PathVector SvgFont::flip_coordinate_system(SPFont* spfont, Geom::PathVector pathv){ - double units_per_em = 1000; - for (SPObject *obj = spfont->children; obj; obj = obj->next){ - if (dynamic_cast<SPFontFace *>(obj)) { + double units_per_em = 1024; + for(auto& obj: spfont->children) { + if (dynamic_cast<SPFontFace *>(&obj)) { //XML Tree being directly used here while it shouldn't be. - sp_repr_get_double(obj->getRepr(), "units_per_em", &units_per_em); + sp_repr_get_double(obj.getRepr(), "units_per_em", &units_per_em); } } @@ -339,19 +350,19 @@ SvgFont::scaled_font_render_glyph (cairo_scaled_font_t */*scaled_font*/, if (node->hasChildren()){ //render the SVG described on this glyph's child nodes. - for(node = node->children; node; node=node->next){ + for(auto& child: node->children) { { - SPPath *path = dynamic_cast<SPPath *>(node); + SPPath *path = dynamic_cast<SPPath *>(&child); if (path) { pathv = path->_curve->get_pathvector(); pathv = flip_coordinate_system(spfont, pathv); render_glyph_path(cr, &pathv); } } - if (dynamic_cast<SPObjectGroup *>(node)) { + if (dynamic_cast<SPObjectGroup *>(&child)) { g_warning("TODO: svgfonts: render OBJECTGROUP"); } - SPUse *use = dynamic_cast<SPUse *>(node); + SPUse *use = dynamic_cast<SPUse *>(&child); if (use) { SPItem* item = use->ref->getObject(); SPPath *path = dynamic_cast<SPPath *>(item); @@ -374,12 +385,12 @@ SvgFont::scaled_font_render_glyph (cairo_scaled_font_t */*scaled_font*/, cairo_font_face_t* SvgFont::get_font_face(){ if (!this->userfont) { - for(SPObject* node = this->font->children;node;node=node->next){ - SPGlyph *glyph = dynamic_cast<SPGlyph *>(node); + for(auto& node: font->children) { + SPGlyph *glyph = dynamic_cast<SPGlyph *>(&node); if (glyph) { glyphs.push_back(glyph); } - SPMissingGlyph *missing = dynamic_cast<SPMissingGlyph *>(node); + SPMissingGlyph *missing = dynamic_cast<SPMissingGlyph *>(&node); if (missing) { missingglyph = missing; } @@ -395,6 +406,20 @@ void SvgFont::refresh(){ this->userfont = NULL; } +double SvgFont::units_per_em() { + double units_per_em = 1024; + for (auto& obj: font->children) { + if (dynamic_cast<SPFontFace *>(&obj)) { + //XML Tree being directly used here while it shouldn't be. + sp_repr_get_double(obj.getRepr(), "units-per-em", &units_per_em); + } + } + if (units_per_em <= 0.0) { + units_per_em = 1024; + } + return units_per_em; +} + /* Local Variables: mode:c++ diff --git a/src/display/nr-svgfonts.h b/src/display/nr-svgfonts.h index 21ab3ed02..d4488fb17 100644 --- a/src/display/nr-svgfonts.h +++ b/src/display/nr-svgfonts.h @@ -52,7 +52,8 @@ private: SPMissingGlyph* missingglyph; sigc::connection glyph_modified_connection; - bool drawing_expose_cb (Gtk::Widget *widget, GdkEventExpose *event, void* data); + double units_per_em(); + //bool drawing_expose_cb (Gtk::Widget *widget, GdkEventExpose *event, void* data); }; #endif //#ifndef NR_SVGFONTS_H_SEEN diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index 17deea16d..f2271e0c6 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -287,7 +287,7 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap } else if (p.getSource() != SNAPSOURCE_UNDEFINED) { tooltip_str = g_strdup(source_name); } - double fontsize = prefs->getInt("/tools/measure/fontsize"); + double fontsize = prefs->getDouble("/tools/measure/fontsize", 10.0); if (tooltip_str) { Geom::Point tooltip_pos = p.getPoint(); @@ -327,7 +327,7 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap SP_CTRLRECT(box)->setDashed(true); SP_CTRLRECT(box)->pickable = false; // See the extensive comment above sp_canvas_item_move_to_z(box, 0); - _snaptarget_bbox = _desktop->add_temporary_canvasitem(box, timeout_val); + _snaptarget_bbox = _desktop->add_temporary_canvasitem(box, timeout_val*1000.0); } } } diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp index 327fbce1f..27b6988c5 100644 --- a/src/display/sodipodi-ctrl.cpp +++ b/src/display/sodipodi-ctrl.cpp @@ -18,6 +18,7 @@ enum { ARG_MODE, ARG_ANCHOR, ARG_SIZE, + ARG_ANGLE, ARG_FILLED, ARG_FILL_COLOR, ARG_STROKED, @@ -53,7 +54,7 @@ sp_ctrl_class_init (SPCtrlClass *klass) g_object_class_install_property (g_object_class, ARG_SIZE, g_param_spec_double ("size", "size", "Size", 0.0, G_MAXDOUBLE, 8.0, (GParamFlags) G_PARAM_READWRITE)); g_object_class_install_property (g_object_class, - ARG_PIXBUF, g_param_spec_pointer ("pixbuf", "pixbuf", "Pixbuf", (GParamFlags) G_PARAM_READWRITE)); + ARG_ANGLE, g_param_spec_double ("angle", "angle", "Angle", -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, (GParamFlags) G_PARAM_READWRITE)); g_object_class_install_property (g_object_class, ARG_FILLED, g_param_spec_boolean ("filled", "filled", "Filled", TRUE, (GParamFlags) G_PARAM_READWRITE)); g_object_class_install_property (g_object_class, @@ -62,6 +63,8 @@ sp_ctrl_class_init (SPCtrlClass *klass) ARG_STROKED, g_param_spec_boolean ("stroked", "stroked", "Stroked", FALSE, (GParamFlags) G_PARAM_READWRITE)); g_object_class_install_property (g_object_class, ARG_STROKE_COLOR, g_param_spec_int ("stroke_color", "stroke_color", "Stroke Color", G_MININT, G_MAXINT, 0x000000ff, (GParamFlags) G_PARAM_READWRITE)); + g_object_class_install_property (g_object_class, + ARG_PIXBUF, g_param_spec_pointer ("pixbuf", "pixbuf", "Pixbuf", (GParamFlags) G_PARAM_READWRITE)); item_class->destroy = sp_ctrl_destroy; item_class->update = sp_ctrl_update; @@ -95,6 +98,9 @@ sp_ctrl_set_property(GObject *object, guint prop_id, const GValue *value, GParam ctrl->height = ctrl->width; ctrl->defined = (ctrl->width > 0); break; + case ARG_ANGLE: + ctrl->angle = (double)g_value_get_double(value); + break; case ARG_FILLED: ctrl->filled = g_value_get_boolean(value); break; @@ -151,6 +157,10 @@ sp_ctrl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec g_value_set_double(value, ctrl->width); break; + case ARG_ANGLE: + g_value_set_double(value, ctrl->angle); + break; + case ARG_FILLED: g_value_set_boolean(value, ctrl->filled); break; @@ -192,6 +202,7 @@ sp_ctrl_init (SPCtrl *ctrl) ctrl->stroked = 0; ctrl->fill_color = 0x000000ff; ctrl->stroke_color = 0x000000ff; + ctrl->angle = 0.0; new (&ctrl->box) Geom::IntRect(0,0,0,0); ctrl->cache = NULL; @@ -291,6 +302,23 @@ sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item) return 1e18; } +bool +sp_point_inside_line(Geom::Point a, Geom::Point b, Geom::Point c, double tolerance = 0.1){ + //http://stackoverflow.com/questions/328107/how-can-you-determine-a-point-is-between-two-other-points-on-a-line-segment + return Geom::are_near(Geom::distance(a,c) + Geom::distance(c,b) , Geom::distance(a,b), tolerance); +} + +bool +sp_point_inside_triangle(Geom::Point p1,Geom::Point p2,Geom::Point p3, Geom::Point point){ + using Geom::X; + using Geom::Y; + double denominator = (p1[X]*(p2[Y] - p3[Y]) + p1[Y]*(p3[X] - p2[X]) + p2[X]*p3[Y] - p2[Y]*p3[X]); + double t1 = (point[X]*(p3[Y] - p1[Y]) + point[Y]*(p1[X] - p3[X]) - p1[X]*p3[Y] + p1[Y]*p3[X]) / denominator; + double t2 = (point[X]*(p2[Y] - p1[Y]) + point[Y]*(p1[X] - p2[X]) - p1[X]*p2[Y] + p1[Y]*p2[X]) / -denominator; + double see = t1 + t2; + return 0 <= t1 && t1 <= 1 && 0 <= t2 && t2 <= 1 && see <= 1; +} + static void sp_ctrl_build_cache (SPCtrl *ctrl) { @@ -316,7 +344,7 @@ sp_ctrl_build_cache (SPCtrl *ctrl) } else { stroke_color = fill_color; } - + gint32 stroke_color_smooth = SP_RGBA32_F_COMPOSE(SP_RGBA32_R_F(stroke_color), SP_RGBA32_G_F(stroke_color), SP_RGBA32_B_F(stroke_color), 0.15); width = (ctrl->width * 2 +1); height = (ctrl->height * 2 +1); c = ctrl->width; // Only used for pre-set square drawing @@ -325,7 +353,24 @@ sp_ctrl_build_cache (SPCtrl *ctrl) if (ctrl->cache) delete[] ctrl->cache; ctrl->cache = new guint32[size]; - + Geom::Point point; + Geom::Point p1; + Geom::Point p2; + Geom::Point p3; + if(ctrl->shape == SP_CTRL_SHAPE_TRIANGLE){ + Geom::Affine m = Geom::Translate(Geom::Point(-width/2.0,-height/2.0)); + m *= Geom::Rotate(-ctrl->angle); + m *= Geom::Translate(Geom::Point(width/2.0, height/2.0)); + p1 = Geom::Point(0,height/2); + p2 = Geom::Point(width - (width/M_PI), height/M_PI); + p3 = Geom::Point(width - (width/M_PI), height-(height/M_PI)); + p1 *= m; + p2 *= m; + p3 *= m; + p1 = p1.floor(); + p2 = p2.floor(); + p3 = p3.floor(); + } switch (ctrl->shape) { case SP_CTRL_SHAPE_SQUARE: p = ctrl->cache; @@ -407,6 +452,29 @@ sp_ctrl_build_cache (SPCtrl *ctrl) ctrl->build = TRUE; break; + case SP_CTRL_SHAPE_TRIANGLE: + p = ctrl->cache; + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + point = Geom::Point(x,y); + if (sp_point_inside_triangle(p1, p2, p3, point)) { + p[(y*width)+x] = fill_color; + } else if (point == p1 || point == p2 || point == p3 || sp_point_inside_line(p1, p2, point, 0.2) || + sp_point_inside_line(p3, p1, point, 0.2)) + { + p[(y*width)+x] = stroke_color; + } else if (sp_point_inside_line(p1, p2, point, 0.5) || + sp_point_inside_line(p3, p1, point, 0.5)) + { + p[(y*width)+x] = stroke_color_smooth; + } else { + p[(y*width)+x] = 0; + } + } + } + ctrl->build = TRUE; + break; + case SP_CTRL_SHAPE_CROSS: p = ctrl->cache; for (y = 0; y < height; y++) { diff --git a/src/display/sodipodi-ctrl.h b/src/display/sodipodi-ctrl.h index ecdb896a7..ac5ac9442 100644 --- a/src/display/sodipodi-ctrl.h +++ b/src/display/sodipodi-ctrl.h @@ -22,6 +22,7 @@ typedef enum { SP_CTRL_SHAPE_SQUARE, SP_CTRL_SHAPE_DIAMOND, SP_CTRL_SHAPE_CIRCLE, + SP_CTRL_SHAPE_TRIANGLE, SP_CTRL_SHAPE_CROSS, SP_CTRL_SHAPE_BITMAP, SP_CTRL_SHAPE_IMAGE @@ -46,6 +47,7 @@ struct SPCtrl : public SPCanvasItem { guint stroked : 1; guint32 fill_color; guint32 stroke_color; + gdouble angle; Geom::IntRect box; /* NB! x1 & y1 are included */ guint32 *cache; diff --git a/src/display/sodipodi-ctrlrect.cpp b/src/display/sodipodi-ctrlrect.cpp index ecc952c48..075e04e6d 100644 --- a/src/display/sodipodi-ctrlrect.cpp +++ b/src/display/sodipodi-ctrlrect.cpp @@ -6,9 +6,11 @@ * bulia byak <buliabyak@users.sf.net> * Carl Hetherington <inkscape@carlh.net> * Jon A. Cruz <jon@joncruz.org> + * Tavmjong Bah <tavmjong@free.fr> * * Copyright (C) 1999-2001 Lauris Kaplinski * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2017 Tavmjong Bah * * Released under GNU GPL * @@ -18,6 +20,7 @@ #include "sp-canvas-util.h" #include "display/cairo-utils.h" #include "display/sp-canvas.h" +#include <2geom/transforms.h> /* * Currently we do not have point method, as it should always be painted @@ -76,14 +79,12 @@ void CtrlRect::init() _dashed = false; _checkerboard = false; - _shadow = 0; + _shadow_width = 0; _area = Geom::OptIntRect(); _rect = Geom::Rect(Geom::Point(0,0),Geom::Point(0,0)); - _shadow_size = 0; - _border_color = 0x000000ff; _fill_color = 0xffffffff; _shadow_color = 0x000000ff; @@ -98,52 +99,120 @@ void CtrlRect::render(SPCanvasBuf *buf) if (!_area) { return; } - Geom::IntRect area = *_area; - Geom::IntRect area_w_shadow (area[X].min(), area[Y].min(), - area[X].max() + _shadow_size, area[Y].max() + _shadow_size); - if ( area_w_shadow.intersects(buf->rect) ) + + Geom::IntRect area = *_area; // _area already includes space for shadow. + if ( area.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); - if (_dashed) cairo_set_dash(buf->ct, dashes, 2, 0); - cairo_rectangle(buf->ct, 0.5 + area[X].min(), 0.5 + area[Y].min(), - area[X].max() - area[X].min(), area[Y].max() - area[Y].min()); + + // Get canvas rotation (scale is isotropic). + double rotation = atan2( _affine[1], _affine[0] ); + + // Are we axis aligned? + double mod_rot = fmod(rotation * M_2_PI, 1); + bool axis_aligned = Geom::are_near( mod_rot, 0 ) || Geom::are_near( mod_rot, 1.0 ); + + // Get the points we need transformed to window coordinates. + Geom::Point rect_transformed[4]; + for (unsigned i = 0; i < 4; ++i) { + rect_transformed[i] = _rect.corner(i) * _affine; + } + + // Draw shadow first. Shadow extends under rectangle to reduce aliasing effects. + if (_shadow_width > 0 && !_dashed) { + + // Offset by half stroke width (_shadow_width is in window coordinates). + // Need to handle change in handedness with flips. + Geom::Point shadow( _shadow_width/2.0, (_affine.det()>0?-1:1)*_shadow_width/2.0 ); + shadow *= Geom::Rotate( rotation ); + + if (axis_aligned) { + // Snap to pixel grid (add 0.5 to center on pixel). + cairo_move_to( buf->ct, + floor(rect_transformed[0][X]+shadow[X]+0.5) + 0.5, + floor(rect_transformed[0][Y]+shadow[Y]+0.5) + 0.5 ); + cairo_line_to( buf->ct, + floor(rect_transformed[1][X]+shadow[X]+0.5) + 0.5, + floor(rect_transformed[1][Y]+shadow[Y]+0.5) + 0.5 ); + cairo_line_to( buf->ct, + floor(rect_transformed[2][X]+shadow[X]+0.5) + 0.5, + floor(rect_transformed[2][Y]+shadow[Y]+0.5) + 0.5 ); + } else { + cairo_move_to( buf->ct, + rect_transformed[0][X]+shadow[X], + rect_transformed[0][Y]+shadow[Y] ); + cairo_line_to( buf->ct, + rect_transformed[1][X]+shadow[X], + rect_transformed[1][Y]+shadow[Y] ); + cairo_line_to( buf->ct, + rect_transformed[2][X]+shadow[X], + rect_transformed[2][Y]+shadow[Y] ); + } + + ink_cairo_set_source_rgba32( buf->ct, _shadow_color ); + cairo_set_line_width( buf->ct, _shadow_width + 1 ); + cairo_stroke( buf->ct ); + } + + // Setup rectangle path + if (axis_aligned) { + + // Snap to pixel grid + Geom::Rect outline( _rect.min() * _affine, _rect.max() * _affine); + // Check if there is a simpler way to go from 'outline' to 'int_rect'. + Geom::IntRect int_rect ( (int) floor(outline.min()[X] + 0.5), + (int) floor(outline.min()[Y] + 0.5), + (int) floor(outline.max()[X] + 0.5), + (int) floor(outline.max()[Y] + 0.5) ); + cairo_rectangle (buf->ct, + 0.5 + int_rect[X].min(), + 0.5 + int_rect[Y].min(), + int_rect[X].max() - int_rect[X].min(), + int_rect[Y].max() - int_rect[Y].min() ); + } else { + + // Angled + cairo_move_to( buf->ct, rect_transformed[0][X], rect_transformed[0][Y] ); + cairo_line_to( buf->ct, rect_transformed[1][X], rect_transformed[1][Y] ); + cairo_line_to( buf->ct, rect_transformed[2][X], rect_transformed[2][Y] ); + cairo_line_to( buf->ct, rect_transformed[3][X], rect_transformed[3][Y] ); + cairo_close_path( buf->ct ); + } + + // This doesn't seem to be used anywhere. If it is, then we should + // probably rotate the coordinate system and fill using a cairo_rectangle(). if (_checkerboard) { cairo_pattern_t *cb = ink_cairo_pattern_create_checkerboard(); cairo_set_source(buf->ct, cb); cairo_pattern_destroy(cb); cairo_fill_preserve(buf->ct); } + if (_has_fill) { ink_cairo_set_source_rgba32(buf->ct, _fill_color); cairo_fill_preserve(buf->ct); } + // Set up stroke. ink_cairo_set_source_rgba32(buf->ct, _border_color); - cairo_stroke(buf->ct); + cairo_set_line_width(buf->ct, 1); + static double const dashes[2] = {4.0, 4.0}; + if (_dashed) cairo_set_dash(buf->ct, dashes, 2, 0); - if (_shadow_size == 1) { // highlight the border by drawing it in _shadow_color - if (_dashed) { - cairo_set_dash(buf->ct, dashes, 2, 4); - cairo_rectangle(buf->ct, 0.5 + area[X].min(), 0.5 + area[Y].min(), - area[X].max() - area[X].min(), area[Y].max() - area[Y].min()); - } else { - cairo_rectangle(buf->ct, -0.5 + area[X].min(), -0.5 + area[Y].min(), - area[X].max() - area[X].min(), area[Y].max() - area[Y].min()); - } - ink_cairo_set_source_rgba32(buf->ct, _shadow_color); - cairo_stroke(buf->ct); - } else if (_shadow_size > 1) { // fill the shadow + // Stroke rectangle. + cairo_stroke_preserve(buf->ct); + + // Highlight the border by drawing it in _shadow_color. + if (_shadow_width == 1 && _dashed) { ink_cairo_set_source_rgba32(buf->ct, _shadow_color); - cairo_rectangle(buf->ct, 1 + area[X].max(), area[Y].min() + _shadow_size, - _shadow_size, area[Y].max() - area[Y].min() + 1); // right shadow - cairo_rectangle(buf->ct, area[X].min() + _shadow_size, 1 + area[Y].max(), - area[X].max() - area[X].min() - _shadow_size + 1, _shadow_size); - cairo_fill(buf->ct); + cairo_set_dash(buf->ct, dashes, 2, 4); // Dash offset by dash length. + cairo_stroke_preserve(buf->ct); } + + cairo_new_path( buf->ct ); // Clear path or get wierd artifacts. cairo_restore(buf->ct); } } @@ -158,124 +227,33 @@ void CtrlRect::update(Geom::Affine const &affine, unsigned int flags) (SP_CANVAS_ITEM_CLASS(sp_ctrlrect_parent_class))->update(this, affine, flags); } - sp_canvas_item_reset_bounds(this); + // Note: There is no harm if 'area' is too large other than a possible small slow down in + // rendering speed. - Geom::Rect bbox(_rect.min() * affine, _rect.max() * affine); + // Calculate an axis-aligned bounding box that include all of transformed _rect. + Geom::Rect bbox = _rect; + bbox *= affine; - Geom::OptIntRect _area_old = _area; - Geom::IntRect area ( (int) floor(bbox.min()[Geom::X] + 0.5), - (int) floor(bbox.min()[Geom::Y] + 0.5), - (int) floor(bbox.max()[Geom::X] + 0.5), - (int) floor(bbox.max()[Geom::Y] + 0.5) ); - _area = area; - Geom::IntRect area_old(0,0,0,0); - if (_area_old) { // this weird construction is because the code below assumes _area_old to be 'valid' - area_old = *_area_old; - } + // Enlarge bbox by twice shadow size (to allow for shadow on any side with a 45deg rotation). + bbox.expandBy( 2.0 *_shadow_width ); - gint _shadow_size_old = _shadow_size; - _shadow_size = _shadow; - - // FIXME: we don't process a possible change in _has_fill - if (_has_fill) { - if (_area_old) { - canvas->requestRedraw(area_old[X].min() - 1, area_old[Y].min() - 1, - area_old[X].max() + _shadow_size + 1, area_old[Y].max() + _shadow_size + 1); - } - if (_area) { - canvas->requestRedraw(area[X].min() - 1, area[Y].min() - 1, - area[X].max() + _shadow_size + 1, area[Y].max() + _shadow_size + 1); - } - } else { // clear box, be smart about what part of the frame to redraw - - /* Top */ - if (area[Y].min() != area_old[Y].min()) { // different level, redraw fully old and new - if (area_old[X].min() != area_old[X].max()) - canvas->requestRedraw(area_old[X].min() - 1, area_old[Y].min() - 1, - area_old[X].max() + 1, area_old[Y].min() + 1); - - if (area[X].min() != area[X].max()) - canvas->requestRedraw(area[X].min() - 1, area[Y].min() - 1, - area[X].max() + 1, area[Y].min() + 1); - } else { // same level, redraw only the ends - if (area[X].min() != area_old[X].min()) { - canvas->requestRedraw(MIN(area_old[X].min(),area[X].min()) - 1, area[Y].min() - 1, - MAX(area_old[X].min(),area[X].min()) + 1, area[Y].min() + 1); - } - if (area[X].max() != area_old[X].max()) { - canvas->requestRedraw(MIN(area_old[X].max(),area[X].max()) - 1, area[Y].min() - 1, - MAX(area_old[X].max(),area[X].max()) + 1, area[Y].min() + 1); - } - } - - /* Left */ - if (area[X].min() != area_old[X].min()) { // different level, redraw fully old and new - if (area_old[Y].min() != area_old[Y].max()) - canvas->requestRedraw(area_old[X].min() - 1, area_old[Y].min() - 1, - area_old[X].min() + 1, area_old[Y].max() + 1); - - if (area[Y].min() != area[Y].max()) - canvas->requestRedraw(area[X].min() - 1, area[Y].min() - 1, - area[X].min() + 1, area[Y].max() + 1); - } else { // same level, redraw only the ends - if (area[Y].min() != area_old[Y].min()) { - canvas->requestRedraw(area[X].min() - 1, MIN(area_old[Y].min(),area[Y].min()) - 1, - area[X].min() + 1, MAX(area_old[Y].min(),area[Y].min()) + 1); - } - if (area[Y].max() != area_old[Y].max()) { - canvas->requestRedraw(area[X].min() - 1, MIN(area_old[Y].max(),area[Y].max()) - 1, - area[X].min() + 1, MAX(area_old[Y].max(),area[Y].max()) + 1); - } - } - - /* Right */ - if (area[X].max() != area_old[X].max() || _shadow_size_old != _shadow_size) { - if (area_old[Y].min() != area_old[Y].max()) - canvas->requestRedraw(area_old[X].max() - 1, area_old[Y].min() - 1, - area_old[X].max() + _shadow_size + 1, area_old[Y].max() + _shadow_size + 1); - - if (area[Y].min() != area[Y].max()) - canvas->requestRedraw(area[X].max() - 1, area[Y].min() - 1, - area[X].max() + _shadow_size + 1, area[Y].max() + _shadow_size + 1); - } else { // same level, redraw only the ends - if (area[Y].min() != area_old[Y].min()) { - canvas->requestRedraw(area[X].max() - 1, MIN(area_old[Y].min(),area[Y].min()) - 1, - area[X].max() + _shadow_size + 1, MAX(area_old[Y].min(),area[Y].min()) + _shadow_size + 1); - } - if (area[Y].max() != area_old[Y].max()) { - canvas->requestRedraw(area[X].max() - 1, MIN(area_old[Y].max(),area[Y].max()) - 1, - area[X].max() + _shadow_size + 1, MAX(area_old[Y].max(),area[Y].max()) + _shadow_size + 1); - } - } + // Generate an integer rectangle that includes bbox. + Geom::OptIntRect _area_old = _area; + _area = bbox.roundOutwards(); - /* Bottom */ - if (area[Y].max() != area_old[Y].max() || _shadow_size_old != _shadow_size) { - if (area_old[X].min() != area_old[X].max()) - canvas->requestRedraw(area_old[X].min() - 1, area_old[Y].max() - 1, - area_old[X].max() + _shadow_size + 1, area_old[Y].max() + _shadow_size + 1); - - if (area[X].min() != area[X].max()) - canvas->requestRedraw(area[X].min() - 1, area[Y].max() - 1, - area[X].max() + _shadow_size + 1, area[Y].max() + _shadow_size + 1); - } else { // same level, redraw only the ends - if (area[X].min() != area_old[X].min()) { - canvas->requestRedraw(MIN(area_old[X].min(),area[X].min()) - 1, area[Y].max() - 1, - MAX(area_old[X].min(),area[X].min()) + _shadow_size + 1, area[Y].max() + _shadow_size + 1); - } - if (area[X].max() != area_old[X].max()) { - canvas->requestRedraw(MIN(area_old[X].max(),area[X].max()) - 1, area[Y].max() - 1, - MAX(area_old[X].max(),area[X].max()) + _shadow_size + 1, area[Y].max() + _shadow_size + 1); - } - } - } + // std::cout << " _rect: " << _rect << std::endl; + // std::cout << " bbox: " << bbox << std::endl; + // std::cout << " area: " << *_area << std::endl; - // update SPCanvasItem box if (_area) { - x1 = area[X].min() - 1; - y1 = area[Y].min() - 1; - x2 = area[X].max() + _shadow_size + 1; - y2 = area[Y].max() + _shadow_size + 1; + Geom::IntRect area = *_area; + sp_canvas_update_bbox( this, area[X].min(), area[Y].min(), area[X].max(), area[Y].max() ); + } else { + std::cerr << "CtrlRect::update: No area!!" << std::endl; } + + // At rendering stage, we need to know affine: + _affine = affine; } @@ -289,7 +267,7 @@ void CtrlRect::setColor(guint32 b, bool h, guint f) void CtrlRect::setShadow(int s, guint c) { - _shadow = s; + _shadow_width = s; _shadow_color = c; _requestUpdate(); } diff --git a/src/display/sodipodi-ctrlrect.h b/src/display/sodipodi-ctrlrect.h index 08a085649..02fc85647 100644 --- a/src/display/sodipodi-ctrlrect.h +++ b/src/display/sodipodi-ctrlrect.h @@ -4,14 +4,17 @@ /** * @file * Simple non-transformed rectangle, usable for rubberband. + * Modified to work with rotated canvas. */ /* * Authors: * Lauris Kaplinski <lauris@ximian.com> * Carl Hetherington <inkscape@carlh.net> + * Tavmjong Bah <tavjong@free.fr> * * Copyright (C) 1999-2001 Lauris Kaplinski * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2017 Tavmjong Bah * * Released under GNU GPL * @@ -21,6 +24,7 @@ #include "sp-canvas-item.h" #include <2geom/rect.h> #include <2geom/int-rect.h> +#include <2geom/transforms.h> struct SPCanvasBuf; @@ -48,23 +52,24 @@ private: void _requestUpdate(); Geom::Rect _rect; + Geom::Affine _affine; + bool _has_fill; bool _dashed; bool _checkerboard; Geom::OptIntRect _area; - gint _shadow_size; + gint _shadow_width; guint32 _border_color; guint32 _fill_color; guint32 _shadow_color; - int _shadow; }; struct CtrlRectClass : public SPCanvasItemClass {}; GType sp_ctrlrect_get_type(); -#endif // SEEN_RUBBERBAND_H +#endif // SEEN_CTRLRECT_H /* Local Variables: diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 7d76fa043..d04c81ecb 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -21,25 +21,30 @@ # include <config.h> #endif +#include <gdkmm/devicemanager.h> +#include <gdkmm/display.h> #include <gdkmm/rectangle.h> #include <cairomm/region.h> #include "helper/sp-marshal.h" #include <2geom/rect.h> #include <2geom/affine.h> -#include "display/cairo-utils.h" #include "display/sp-canvas.h" #include "display/sp-canvas-group.h" +#include "display/rendermode.h" +#include "display/cairo-utils.h" #include "preferences.h" #include "inkscape.h" #include "sodipodi-ctrlrect.h" #include "cms-system.h" -#include "display/rendermode.h" -#include "display/cairo-utils.h" #include "debug/gdk-event-latency-tracker.h" #include "desktop.h" #include "color.h" +#if GTK_CHECK_VERSION(3,20,0) +# include <gdkmm/seat.h> +#endif + using Inkscape::Debug::GdkEventLatencyTracker; // gtk_check_version returns non-NULL on failure @@ -61,6 +66,20 @@ struct SPCanvasGroupClass { SPCanvasItemClass parent_class; }; +static void ungrab_default_client_pointer(guint32 const time = GDK_CURRENT_TIME) +{ + auto const display = Gdk::Display::get_default(); + +#if GTK_CHECK_VERSION(3,20,0) + auto const seat = display->get_default_seat(); + seat->ungrab(); +#else + auto const dm = display->get_device_manager(); + auto const device = dm->get_client_pointer(); + device->ungrab(time); +#endif +} + /** * A group of items. */ @@ -321,14 +340,7 @@ void sp_canvas_item_dispose(GObject *object) if (item == item->canvas->_grabbed_item) { item->canvas->_grabbed_item = NULL; - -#if GTK_CHECK_VERSION(3,0,0) - GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default()); - GdkDevice *device = gdk_device_manager_get_client_pointer(dm); - gdk_device_ungrab(device, GDK_CURRENT_TIME); -#else - gdk_pointer_ungrab (GDK_CURRENT_TIME); -#endif + ungrab_default_client_pointer(); } if (item == item->canvas->_focused_item) { @@ -616,10 +628,22 @@ int sp_canvas_item_grab(SPCanvasItem *item, guint event_mask, GdkCursor *cursor, // fixme: Top hack (Lauris) // fixme: If we add key masks to event mask, Gdk will abort (Lauris) - // fixme: But Canvas actualle does get key events, so all we need is routing these here -#if GTK_CHECK_VERSION(3,0,0) - GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default()); - GdkDevice *device = gdk_device_manager_get_client_pointer(dm); + // fixme: But Canvas actually does get key events, so all we need is routing these here + auto display = gdk_display_get_default(); +#if GTK_CHECK_VERSION(3,20,0) + auto seat = gdk_display_get_default_seat(display); + gdk_seat_grab(seat, + getWindow(item->canvas), + GDK_SEAT_CAPABILITY_ALL_POINTING, + FALSE, + cursor, + NULL, + NULL, + NULL); +#else + auto dm = gdk_display_get_device_manager(display); + auto device = gdk_device_manager_get_client_pointer(dm); + gdk_device_grab(device, getWindow(item->canvas), GDK_OWNERSHIP_NONE, @@ -627,10 +651,6 @@ int sp_canvas_item_grab(SPCanvasItem *item, guint event_mask, GdkCursor *cursor, (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))), cursor, etime); -#else - gdk_pointer_grab( getWindow(item->canvas), FALSE, - (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))), - NULL, cursor, etime); #endif item->canvas->_grabbed_item = item; @@ -657,14 +677,7 @@ void sp_canvas_item_ungrab(SPCanvasItem *item, guint32 etime) } item->canvas->_grabbed_item = NULL; - -#if GTK_CHECK_VERSION(3,0,0) - GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default()); - GdkDevice *device = gdk_device_manager_get_client_pointer(dm); - gdk_device_ungrab(device, etime); -#else - gdk_pointer_ungrab (etime); -#endif + ungrab_default_client_pointer(etime); } /** @@ -913,16 +926,9 @@ void sp_canvas_class_init(SPCanvasClass *klass) widget_class->realize = SPCanvas::handle_realize; widget_class->unrealize = SPCanvas::handle_unrealize; - -#if GTK_CHECK_VERSION(3,0,0) widget_class->get_preferred_width = SPCanvas::handle_get_preferred_width; widget_class->get_preferred_height = SPCanvas::handle_get_preferred_height; widget_class->draw = SPCanvas::handle_draw; -#else - widget_class->size_request = SPCanvas::handle_size_request; - widget_class->expose_event = SPCanvas::handle_expose; -#endif - widget_class->size_allocate = SPCanvas::handle_size_allocate; widget_class->button_press_event = SPCanvas::handle_button; widget_class->button_release_event = SPCanvas::handle_button; @@ -980,13 +986,7 @@ void SPCanvas::shutdownTransients() if (_grabbed_item) { _grabbed_item = NULL; -#if GTK_CHECK_VERSION(3,0,0) - GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default()); - GdkDevice *device = gdk_device_manager_get_client_pointer(dm); - gdk_device_ungrab(device, GDK_CURRENT_TIME); -#else - gdk_pointer_ungrab(GDK_CURRENT_TIME); -#endif + ungrab_default_client_pointer(); } removeIdle(); } @@ -1051,11 +1051,7 @@ void SPCanvas::handle_realize(GtkWidget *widget) attributes.width = allocation.width; attributes.height = allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gdk_visual_get_system(); - -#if !GTK_CHECK_VERSION(3,0,0) - attributes.colormap = gdk_colormap_get_system(); -#endif + attributes.visual = gdk_screen_get_system_visual(gdk_screen_get_default()); attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK | @@ -1073,11 +1069,7 @@ void SPCanvas::handle_realize(GtkWidget *widget) GDK_SCROLL_MASK | GDK_FOCUS_CHANGE_MASK); -#if GTK_CHECK_VERSION(3,0,0) gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; -#else - gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; -#endif GdkWindow *window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window (widget, window); @@ -1086,18 +1078,8 @@ void SPCanvas::handle_realize(GtkWidget *widget) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (prefs->getBool("/options/useextinput/value", true)) { gtk_widget_set_events(widget, attributes.event_mask); -#if !GTK_CHECK_VERSION(3,0,0) - gtk_widget_set_extension_events(widget, GDK_EXTENSION_EVENTS_ALL); - // TODO: Extension event stuff has been deprecated in GTK+ 3 -#endif } -#if !GTK_CHECK_VERSION(3,0,0) - // This does nothing in GTK+ 3 - GtkStyle *style = gtk_widget_get_style (widget); - gtk_widget_set_style (widget, gtk_style_attach (style, window)); -#endif - gtk_widget_set_realized (widget, TRUE); } @@ -1115,8 +1097,6 @@ void SPCanvas::handle_unrealize(GtkWidget *widget) (* GTK_WIDGET_CLASS(sp_canvas_parent_class)->unrealize)(widget); } - -#if GTK_CHECK_VERSION(3,0,0) void SPCanvas::handle_get_preferred_width(GtkWidget *widget, gint *minimum_width, gint *natural_width) { static_cast<void>(SP_CANVAS (widget)); @@ -1130,16 +1110,6 @@ void SPCanvas::handle_get_preferred_height(GtkWidget *widget, gint *minimum_heig *minimum_height = 256; *natural_height = 256; } -#else -void SPCanvas::handle_size_request(GtkWidget *widget, GtkRequisition *req) -{ - static_cast<void>(SP_CANVAS (widget)); - - req->width = 256; - req->height = 256; -} -#endif - void SPCanvas::handle_size_allocate(GtkWidget *widget, GtkAllocation *allocation) { @@ -1219,9 +1189,7 @@ int SPCanvas::emitEvent(GdkEvent *event) break; case GDK_SCROLL: mask = GDK_SCROLL_MASK; -#if GTK_CHECK_VERSION(3,0,0) mask |= GDK_SMOOTH_SCROLL_MASK; -#endif break; default: mask = 0; @@ -1503,13 +1471,9 @@ gint SPCanvas::handle_scroll(GtkWidget *widget, GdkEventScroll *event) } static inline void request_motions(GdkWindow *w, GdkEventMotion *event) { -#if GTK_CHECK_VERSION(3,0,0) gdk_window_get_device_position(w, gdk_event_get_device((GdkEvent *)(event)), NULL, NULL, NULL); -#else - gdk_window_get_pointer(w, NULL, NULL, NULL); -#endif gdk_event_request_motions(event); } @@ -1543,7 +1507,7 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect buf.buf = NULL; buf.buf_rowstride = 0; buf.rect = paint_rect; - buf.visible_rect = canvas_rect; + buf.canvas_rect = canvas_rect; buf.is_empty = true; // create temporary surface @@ -1607,7 +1571,7 @@ void SPCanvas::paintSingleBuffer(Geom::IntRect const &paint_rect, Geom::IntRect } struct PaintRectSetup { - Geom::IntRect big_rect; + Geom::IntRect canvas_rect; GTimeVal start_time; int max_pixels; Geom::Point mouse_loc; @@ -1666,7 +1630,7 @@ int SPCanvas::paintRectInternal(PaintRectSetup const *setup, Geom::IntRect const gdk_window_begin_paint_rect(window, &r); */ - paintSingleBuffer(this_rect, setup->big_rect, bw); + paintSingleBuffer(this_rect, setup->canvas_rect, bw); //gdk_window_end_paint(window); return 1; } @@ -1731,6 +1695,7 @@ bool SPCanvas::paintRect(int xx0, int yy0, int xx1, int yy1) gtk_widget_get_allocation(GTK_WIDGET(this), &allocation); + // Find window rectangle in 'world coordinates'. Geom::IntRect canvas_rect = Geom::IntRect::from_xywh(_x0, _y0, allocation.width, allocation.height); Geom::IntRect paint_rect(xx0, yy0, xx1, yy1); @@ -1741,28 +1706,37 @@ bool SPCanvas::paintRect(int xx0, int yy0, int xx1, int yy1) paint_rect = *area; PaintRectSetup setup; - setup.big_rect = paint_rect; + setup.canvas_rect = canvas_rect; // Save the mouse location gint x, y; -#if GTK_CHECK_VERSION(3,0,0) - GdkDeviceManager *dm = gdk_display_get_device_manager(gdk_display_get_default()); - GdkDevice *device = gdk_device_manager_get_client_pointer(dm); + auto const display = Gdk::Display::get_default(); - gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(this)), - device, - &x, &y, NULL); +#if GTK_CHECK_VERSION(3,20,0) + auto const seat = display->get_default_seat(); + auto const device = seat->get_pointer(); #else - gdk_window_get_pointer(gtk_widget_get_window(GTK_WIDGET(this)), &x, &y, NULL); + auto const dm = display->get_device_manager(); + auto const device = dm->get_client_pointer(); #endif + gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(this)), + device->gobj(), + &x, &y, NULL); + setup.mouse_loc = sp_canvas_window_to_world(this, Geom::Point(x,y)); + static unsigned tile_multiplier = 0; + if (tile_multiplier == 0) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + tile_multiplier = prefs->getIntLimited("/options/rendering/tile-multiplier", 1, 1, 64); + } + if (_rendermode != Inkscape::RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients // 256K is the cached buffer and we need 4 channels - setup.max_pixels = 65536; // 256K/4 + setup.max_pixels = 65536 * tile_multiplier; // 256K/4 } else { // paths only, so 1M works faster // 1M is the cached buffer and we need 4 channels @@ -1818,21 +1792,6 @@ gboolean SPCanvas::handle_draw(GtkWidget *widget, cairo_t *cr) { return TRUE; } -#if !GTK_CHECK_VERSION(3,0,0) -gboolean SPCanvas::handle_expose(GtkWidget *widget, GdkEventExpose *event) -{ - cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget)); - - gdk_cairo_region (cr, event->region); - cairo_clip (cr); - gboolean result = SPCanvas::handle_draw(widget, cr); - - cairo_destroy (cr); - - return result; -} -#endif - gint SPCanvas::handle_key_event(GtkWidget *widget, GdkEventKey *event) { @@ -1965,10 +1924,16 @@ SPCanvasGroup *SPCanvas::getRoot() return SP_CANVAS_GROUP(_root); } -void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling) +/** + * Scroll screen to point 'c'. 'c' is measured in screen pixels. + */ +void SPCanvas::scrollTo( Geom::Point const &c, unsigned int clear, bool is_scrolling) { GtkAllocation allocation; + double cx = c[Geom::X]; + double cy = c[Geom::Y]; + int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels) int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do! int dx = ix - _x0; // dx and dy specify the displacement (scroll) of the @@ -1989,6 +1954,7 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll cairo_translate(cr, -ix, -iy); cairo_set_source(cr, _background); cairo_paint(cr); + // Copy the old backing store contents cairo_set_source_surface(cr, _backing_store, _x0, _y0); cairo_rectangle(cr, _x0, _y0, allocation.width, allocation.height); diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index 171fdaf67..a2cbe0b27 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -51,7 +51,7 @@ enum { struct SPCanvasBuf { cairo_t *ct; Geom::IntRect rect; - Geom::IntRect visible_rect; + Geom::IntRect canvas_rect; // visible window in world coordinates (i.e. offset by _x0, _y0) unsigned char *buf; int buf_rowstride; @@ -70,8 +70,8 @@ GType sp_canvas_get_type() G_GNUC_CONST; * Port of GnomeCanvas for inkscape needs. */ struct SPCanvas { - /// Scrolls canvas to specific position (cx and cy are measured in screen pixels). - void scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling = false); + /// Scrolls canvas to specific position (c is measured in screen pixels). + void scrollTo(Geom::Point const &c, unsigned int clear, bool is_scrolling = false); /// Synchronously updates the canvas if necessary. void updateNow(); @@ -145,12 +145,8 @@ public: static void dispose(GObject *object); static void handle_realize(GtkWidget *widget); static void handle_unrealize(GtkWidget *widget); -#if GTK_CHECK_VERSION(3,0,0) static void handle_get_preferred_width(GtkWidget *widget, gint *min_w, gint *nat_w); static void handle_get_preferred_height(GtkWidget *widget, gint *min_h, gint *nat_h); -#else - static void handle_size_request(GtkWidget *widget, GtkRequisition *req); -#endif static void handle_size_allocate(GtkWidget *widget, GtkAllocation *allocation); static gint handle_button(GtkWidget *widget, GdkEventButton *event); @@ -162,9 +158,6 @@ public: static gint handle_scroll(GtkWidget *widget, GdkEventScroll *event); static gint handle_motion(GtkWidget *widget, GdkEventMotion *event); static gboolean handle_draw(GtkWidget *widget, cairo_t *cr); -#if !GTK_CHECK_VERSION(3,0,0) - static gboolean handle_expose(GtkWidget *widget, GdkEventExpose *event); -#endif static gint handle_key_event(GtkWidget *widget, GdkEventKey *event); static gint handle_crossing(GtkWidget *widget, GdkEventCrossing *event); static gint handle_focus_in(GtkWidget *widget, GdkEventFocus *event); @@ -181,8 +174,8 @@ public: bool _is_dragging; double _dx0; double _dy0; - int _x0; ///< World coordinate of the leftmost pixels - int _y0; ///< World coordinate of the topmost pixels + int _x0; ///< World coordinate of the leftmost pixels of window + int _y0; ///< World coordinate of the topmost pixels of window /// Image surface storing the contents of the widget cairo_surface_t *_backing_store; diff --git a/src/display/sp-ctrlcurve.cpp b/src/display/sp-ctrlcurve.cpp index 2e0e8105b..79ef20d6c 100644 --- a/src/display/sp-ctrlcurve.cpp +++ b/src/display/sp-ctrlcurve.cpp @@ -47,6 +47,8 @@ sp_ctrlcurve_init(SPCtrlCurve *ctrlcurve) // Points are initialized to 0,0 ctrlcurve->rgba = 0x0000ff7f; ctrlcurve->item=NULL; + ctrlcurve->corner0 = -1; + ctrlcurve->corner1 = -1; } namespace { diff --git a/src/display/sp-ctrlcurve.h b/src/display/sp-ctrlcurve.h index 847944f38..56b3089d9 100644 --- a/src/display/sp-ctrlcurve.h +++ b/src/display/sp-ctrlcurve.h @@ -31,6 +31,9 @@ struct SPCtrlCurve : public SPCtrlLine { Geom::Point const &q2, Geom::Point const &q3); Geom::Point p0, p1, p2, p3; + + int corner0; // Used to store index of corner for finding dragger. + int corner1; }; GType sp_ctrlcurve_get_type(); diff --git a/src/display/sp-ctrlline.cpp b/src/display/sp-ctrlline.cpp index 1bde540c0..c4ced2a33 100644 --- a/src/display/sp-ctrlline.cpp +++ b/src/display/sp-ctrlline.cpp @@ -52,6 +52,7 @@ static void sp_ctrlline_init(SPCtrlLine *ctrlline) ctrlline->rgba = 0x0000ff7f; ctrlline->s[Geom::X] = ctrlline->s[Geom::Y] = ctrlline->e[Geom::X] = ctrlline->e[Geom::Y] = 0.0; ctrlline->item=NULL; + ctrlline->is_fill = true; } namespace { @@ -84,7 +85,7 @@ void sp_ctrlline_render(SPCanvasItem *item, SPCanvasBuf *buf) Geom::Point s = cl->s * cl->affine; Geom::Point e = cl->e * cl->affine; - ink_cairo_set_source_rgba32(buf->ct, 0xffffffbf); + ink_cairo_set_source_rgba32(buf->ct, 0xffffff7f); cairo_set_line_width(buf->ct, 2); cairo_new_path(buf->ct); diff --git a/src/display/sp-ctrlline.h b/src/display/sp-ctrlline.h index b2e222437..bac0cfa54 100644 --- a/src/display/sp-ctrlline.h +++ b/src/display/sp-ctrlline.h @@ -33,6 +33,8 @@ struct SPCtrlLine : public SPCanvasItem { SPItem *item; // the item to which this line belongs in some sense; may be NULL for some users + bool is_fill; // fill or stroke... used with meshes. + guint32 rgba; Geom::Point s; Geom::Point e; |
