X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fdisplay%2Fsp-canvas.cpp;h=fc68bcfc00a3c6ac304033832b183ab925738eab;hb=0dc33d4ce43e0bb49c63aa53b826ec4a1ff68e28;hp=efd2628903d45237a82a79479ed37c07271e81f3;hpb=a17d45e59cc6ab4e74980c584b707537c89efa06;p=inkscape.git diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index efd262890..fc68bcfc0 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -1,5 +1,3 @@ -#define __SP_CANVAS_C__ - /** \file * Port of GnomeCanvas for Inkscape needs * @@ -24,22 +22,37 @@ #include #include +#include #include -#include -#include -#include "display-forward.h" -#include -#include -#include -#include "prefs-utils.h" -#include "box3d-context.h" +#include "helper/sp-marshal.h" +#include +#include "display/sp-canvas.h" +#include "display/sp-canvas-group.h" +#include <2geom/matrix.h> +#include "libnr/nr-convex-hull.h" +#include "preferences.h" #include "inkscape.h" #include "sodipodi-ctrlrect.h" #if ENABLE_LCMS #include "color-profile-fns.h" #endif // ENABLE_LCMS +#include "display/rendermode.h" +#include "libnr/nr-blit.h" +#include "display/inkscape-cairo.h" +#include "debug/gdk-event-latency-tracker.h" +#include "desktop.h" +#include "sp-namedview.h" + +using Inkscape::Debug::GdkEventLatencyTracker; + +// GTK_CHECK_VERSION returns false on failure +#define HAS_GDK_EVENT_REQUEST_MOTIONS GTK_CHECK_VERSION(2, 12, 0) + +// gtk_check_version returns non-NULL on failure +static bool const HAS_BROKEN_MOTION_HINTS = + true || gtk_check_version(2, 12, 0) != NULL || !HAS_GDK_EVENT_REQUEST_MOTIONS; // Define this to visualize the regions to be redrawn //#define DEBUG_REDRAW 1; @@ -49,12 +62,6 @@ // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted. #define TILE_SIZE 16 -enum { - RENDERMODE_NORMAL, - RENDERMODE_NOAA, - RENDERMODE_OUTLINE -}; - static gint const sp_canvas_update_priority = G_PRIORITY_HIGH_IDLE; #define SP_CANVAS_WINDOW(c) (((GtkWidget *) (c))->window) @@ -94,21 +101,22 @@ static void group_remove (SPCanvasGroup *group, SPCanvasItem *item); /* SPCanvasItem */ enum {ITEM_EVENT, ITEM_LAST_SIGNAL}; +enum {PROP_0, PROP_VISIBLE}; static void sp_canvas_request_update (SPCanvas *canvas); +static void track_latency(GdkEvent const *event); static void sp_canvas_item_class_init (SPCanvasItemClass *klass); static void sp_canvas_item_init (SPCanvasItem *item); static void sp_canvas_item_dispose (GObject *object); static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args); static int emit_event (SPCanvas *canvas, GdkEvent *event); +static int pick_current_item (SPCanvas *canvas, GdkEvent *event); static guint item_signals[ITEM_LAST_SIGNAL] = { 0 }; -static GtkObjectClass *item_parent_class; - /** * Registers the SPCanvasItem class with Glib and returns its type number. */ @@ -141,13 +149,10 @@ sp_canvas_item_class_init (SPCanvasItemClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; - /* fixme: Derive from GObject */ - item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT); - item_signals[ITEM_EVENT] = g_signal_new ("event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (SPCanvasItemClass, event), + ((glong)((guint8*)&(klass->event) - (guint8*)klass)), NULL, NULL, sp_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1, @@ -162,8 +167,11 @@ sp_canvas_item_class_init (SPCanvasItemClass *klass) static void sp_canvas_item_init (SPCanvasItem *item) { + // TODO items should not be visible on creation - this causes kludges with items + // that should be initially invisible; examples of such items: node handles, the CtrlRect + // used for rubberbanding, path outline, etc. item->flags |= SP_CANVAS_ITEM_VISIBLE; - item->xform = NR::Matrix(NR::identity()); + item->xform = Geom::Matrix(Geom::identity()); } /** @@ -238,7 +246,7 @@ sp_canvas_item_dispose (GObject *object) // this redraws only the stroke of the rect to be deleted, // avoiding redraw of the entire area if (SP_IS_CTRLRECT(item)) { - SP_CTRLRECT(object)->setRectangle(NR::Rect(NR::Point(0,0),NR::Point(0,0))); + SP_CTRLRECT(object)->setRectangle(Geom::Rect(Geom::Point(0,0),Geom::Point(0,0))); SP_CTRLRECT(object)->update(item->xform, 0); } else { redraw_if_visible (item); @@ -267,7 +275,7 @@ sp_canvas_item_dispose (GObject *object) group_remove (SP_CANVAS_GROUP (item->parent), item); } - G_OBJECT_CLASS (item_parent_class)->dispose (object); + G_OBJECT_CLASS (g_type_class_peek(g_type_parent(sp_canvas_item_get_type())))->dispose (object); } /** @@ -276,10 +284,10 @@ sp_canvas_item_dispose (GObject *object) * NB! affine is parent2canvas. */ static void -sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags) +sp_canvas_item_invoke_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags) { /* Apply the child item's transform */ - NR::Matrix child_affine = item->xform * affine; + Geom::Matrix child_affine = item->xform * affine; /* apply object flags to child flags */ int child_flags = flags & ~SP_CANVAS_UPDATE_REQUESTED; @@ -307,7 +315,7 @@ sp_canvas_item_invoke_update (SPCanvasItem *item, NR::Matrix const &affine, unsi * maintaining the affine invariant. */ static double -sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item) +sp_canvas_item_invoke_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item) { if (SP_CANVAS_ITEM_GET_CLASS (item)->point) return SP_CANVAS_ITEM_GET_CLASS (item)->point (item, p, actual_item); @@ -323,7 +331,7 @@ sp_canvas_item_invoke_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **act * @affine: An affine transformation matrix. */ void -sp_canvas_item_affine_absolute (SPCanvasItem *item, NR::Matrix const &affine) +sp_canvas_item_affine_absolute (SPCanvasItem *item, Geom::Matrix const &affine) { item->xform = affine; @@ -467,6 +475,13 @@ sp_canvas_item_lower (SPCanvasItem *item, int positions) item->canvas->need_repick = TRUE; } +bool +sp_canvas_item_is_visible (SPCanvasItem *item) +{ + return item->flags & SP_CANVAS_ITEM_VISIBLE; +} + + /** * Sets visible flag on item and requests a redraw. */ @@ -532,8 +547,17 @@ sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, gu if (item->canvas->grabbed_item) return -1; - if (!(item->flags & SP_CANVAS_ITEM_VISIBLE)) - return -1; + // This test disallows grabbing events by an invisible item, which may be useful + // sometimes. An example is the hidden control point used for the selector component, + // where it is used for object selection and rubberbanding. There seems to be nothing + // preventing this except this test, so I removed it. + // -- Krzysztof Kosiński, 2009.08.12 + //if (!(item->flags & SP_CANVAS_ITEM_VISIBLE)) + // return -1; + + if (HAS_BROKEN_MOTION_HINTS) { + event_mask &= ~GDK_POINTER_MOTION_HINT_MASK; + } /* fixme: Top hack (Lauris) */ /* fixme: If we add key masks to event mask, Gdk will abort (Lauris) */ @@ -574,11 +598,11 @@ sp_canvas_item_ungrab (SPCanvasItem *item, guint32 etime) * Returns the product of all transformation matrices from the root item down * to the item. */ -NR::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item) +Geom::Matrix sp_canvas_item_i2w_affine(SPCanvasItem const *item) { g_assert (SP_IS_CANVAS_ITEM (item)); // should we get this? - NR::Matrix affine = NR::identity(); + Geom::Matrix affine = Geom::identity(); while (item) { affine *= item->xform; @@ -673,8 +697,8 @@ static void sp_canvas_group_class_init (SPCanvasGroupClass *klass); static void sp_canvas_group_init (SPCanvasGroup *group); static void sp_canvas_group_destroy (GtkObject *object); -static void sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags); -static double sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item); +static void sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags); +static double sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item); static void sp_canvas_group_render (SPCanvasItem *item, SPCanvasBuf *buf); static SPCanvasItemClass *group_parent_class; @@ -682,25 +706,25 @@ static SPCanvasItemClass *group_parent_class; /** * Registers SPCanvasGroup class with Gtk and returns its type number. */ -GtkType -sp_canvas_group_get_type (void) +GType sp_canvas_group_get_type(void) { - static GtkType group_type = 0; - - if (!group_type) { - static GtkTypeInfo const group_info = { - "SPCanvasGroup", - sizeof (SPCanvasGroup), - sizeof (SPCanvasGroupClass), - (GtkClassInitFunc) sp_canvas_group_class_init, - (GtkObjectInitFunc) sp_canvas_group_init, - NULL, NULL, NULL + static GType type = 0; + if (!type) { + GTypeInfo info = { + sizeof(SPCanvasGroupClass), + 0, // base_init + 0, // base_finalize + (GClassInitFunc)sp_canvas_group_class_init, + 0, // class_finalize + 0, // class_data + sizeof(SPCanvasGroup), + 0, // n_preallocs + (GInstanceInitFunc)sp_canvas_group_init, + 0 // value_table }; - - group_type = gtk_type_unique (sp_canvas_item_get_type (), &group_info); + type = g_type_register_static(sp_canvas_item_get_type(), "SPCanvasGroup", &info, static_cast(0)); } - - return group_type; + return type; } /** @@ -758,10 +782,10 @@ sp_canvas_group_destroy (GtkObject *object) * Update handler for canvas groups */ static void -sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags) +sp_canvas_group_update (SPCanvasItem *item, Geom::Matrix const &affine, unsigned int flags) { SPCanvasGroup const *group = SP_CANVAS_GROUP (item); - NR::ConvexHull corners(NR::Point(0, 0)); + Geom::RectHull corners(Geom::Point(0, 0)); bool empty=true; for (GList *list = group->items; list; list = list->next) { @@ -771,21 +795,21 @@ sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned i if ( i->x2 > i->x1 && i->y2 > i->y1 ) { if (empty) { - corners = NR::ConvexHull(NR::Point(i->x1, i->y1)); + corners = Geom::RectHull(Geom::Point(i->x1, i->y1)); empty = false; } else { - corners.add(NR::Point(i->x1, i->y1)); + corners.add(Geom::Point(i->x1, i->y1)); } - corners.add(NR::Point(i->x2, i->y2)); + corners.add(Geom::Point(i->x2, i->y2)); } } - NR::Maybe const bounds = corners.bounds(); + Geom::OptRect const bounds = corners.bounds(); if (bounds) { - item->x1 = bounds->min()[NR::X]; - item->y1 = bounds->min()[NR::Y]; - item->x2 = bounds->max()[NR::X]; - item->y2 = bounds->max()[NR::Y]; + item->x1 = bounds->min()[Geom::X]; + item->y1 = bounds->min()[Geom::Y]; + item->x2 = bounds->max()[Geom::X]; + item->y2 = bounds->max()[Geom::Y]; } else { // FIXME ? item->x1 = item->x2 = item->y1 = item->y2 = 0; @@ -796,11 +820,11 @@ sp_canvas_group_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned i * Point handler for canvas groups. */ static double -sp_canvas_group_point (SPCanvasItem *item, NR::Point p, SPCanvasItem **actual_item) +sp_canvas_group_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item) { SPCanvasGroup const *group = SP_CANVAS_GROUP (item); - double const x = p[NR::X]; - double const y = p[NR::Y]; + double const x = p[Geom::X]; + double const y = p[Geom::Y]; int x1 = (int)(x - item->canvas->close_enough); int y1 = (int)(y - item->canvas->close_enough); int x2 = (int)(x + item->canvas->close_enough); @@ -938,25 +962,25 @@ static int do_update (SPCanvas *canvas); * * \return The type ID of the SPCanvas class. **/ -GtkType -sp_canvas_get_type (void) -{ - static GtkType canvas_type = 0; - - if (!canvas_type) { - static GtkTypeInfo const canvas_info = { - "SPCanvas", - sizeof (SPCanvas), - sizeof (SPCanvasClass), - (GtkClassInitFunc) sp_canvas_class_init, - (GtkObjectInitFunc) sp_canvas_init, - NULL, NULL, NULL +GType sp_canvas_get_type(void) +{ + static GType type = 0; + if (!type) { + GTypeInfo info = { + sizeof(SPCanvasClass), + 0, // base_init + 0, // base_finalize + (GClassInitFunc)sp_canvas_class_init, + 0, // class_finalize + 0, // class_data + sizeof(SPCanvas), + 0, // n_preallocs + (GInstanceInitFunc)sp_canvas_init, + 0 // value_table }; - - canvas_type = gtk_type_unique (GTK_TYPE_WIDGET, &canvas_info); + type = g_type_register_static(GTK_TYPE_WIDGET, "SPCanvas", &info, static_cast(0)); } - - return canvas_type; + return type; } /** @@ -1014,6 +1038,8 @@ sp_canvas_init (SPCanvas *canvas) // See comment at in sp-canvas.h. canvas->gen_all_enter_events = false; + + canvas->drawing_disabled = false; canvas->tiles=NULL; canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0; @@ -1022,8 +1048,12 @@ sp_canvas_init (SPCanvas *canvas) canvas->forced_redraw_count = 0; canvas->forced_redraw_limit = -1; - canvas->is_scrolling = false; +#if ENABLE_LCMS + canvas->enable_cms_display_adj = false; + canvas->cms_key = new Glib::ustring(""); +#endif // ENABLE_LCMS + canvas->is_scrolling = false; } /** @@ -1084,6 +1114,14 @@ sp_canvas_destroy (GtkObject *object) (* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object); } +static void track_latency(GdkEvent const *event) { + GdkEventLatencyTracker &tracker = GdkEventLatencyTracker::default_tracker(); + boost::optional latency = tracker.process(event); + if (latency && *latency > 2.0) { + //g_warning("Event latency reached %f sec (%1.4f)", *latency, tracker.getSkew()); + } +} + /** * Returns new canvas as widget. */ @@ -1117,6 +1155,8 @@ sp_canvas_realize (GtkWidget *widget) GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | + ( HAS_BROKEN_MOTION_HINTS ? + 0 : GDK_POINTER_MOTION_HINT_MASK ) | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_KEY_PRESS_MASK | @@ -1129,7 +1169,8 @@ sp_canvas_realize (GtkWidget *widget) widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gdk_window_set_user_data (widget->window, widget); - if ( prefs_get_int_attribute ("options.useextinput", "value", 1) ) + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if ( prefs->getBool("/options/useextinput/value", true) ) gtk_widget_set_events(widget, attributes.event_mask); widget->style = gtk_style_attach (widget->style, widget->window); @@ -1147,6 +1188,10 @@ sp_canvas_unrealize (GtkWidget *widget) { SPCanvas *canvas = SP_CANVAS (widget); + canvas->current_item = NULL; + canvas->grabbed_item = NULL; + canvas->focused_item = NULL; + shutdown_transients (canvas); gdk_gc_destroy (canvas->pixmap_gc); @@ -1247,7 +1292,7 @@ emit_event (SPCanvas *canvas, GdkEvent *event) if (!(mask & canvas->grabbed_event_mask)) return FALSE; } - /* Convert to world coordinates -- we have two cases because of diferent + /* Convert to world coordinates -- we have two cases because of different * offsets of the fields in the event structures. */ @@ -1281,6 +1326,20 @@ emit_event (SPCanvas *canvas, GdkEvent *event) if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) { item = canvas->grabbed_item; } else { + // Make sure that current_item is up-to-date. If a snap indicator was just deleted, then + // sp_canvas_item_dispose has been called and there is no current_item specified. We need + // that though because otherwise we don't know where to send this event to, leading to a + // lost event. We can't wait for idle events to have current_item updated, we need it now! + // Otherwise, scrolling when hovering above a pre-snap indicator won't work (for example) + // See this bug report: https://bugs.launchpad.net/inkscape/+bug/522335/comments/8 + if (canvas->need_repick && !canvas->in_repick && event->type == GDK_SCROLL) { + // To avoid side effects, we'll only do this for scroll events, because this is the + // only thing we want to fix here. An example of a reported side effect is that + // otherwise selection of nodes in the node editor by dragging a rectangle using a + // tablet will break + canvas->need_repick = FALSE; + pick_current_item (canvas, (GdkEvent *) event); + } item = canvas->current_item; } @@ -1390,7 +1449,7 @@ pick_current_item (SPCanvas *canvas, GdkEvent *event) /* find the closest item */ if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) { - sp_canvas_item_invoke_point (canvas->root, NR::Point(x, y), &canvas->new_current_item); + sp_canvas_item_invoke_point (canvas->root, Geom::Point(x, y), &canvas->new_current_item); } else { canvas->new_current_item = NULL; } @@ -1446,6 +1505,8 @@ pick_current_item (SPCanvas *canvas, GdkEvent *event) retval = emit_event (canvas, &new_event); } + + return retval; } @@ -1459,7 +1520,7 @@ sp_canvas_button (GtkWidget *widget, GdkEventButton *event) int retval = FALSE; - /* dispatch normally regardless of the event's window if an item has + /* dispatch normally regardless of the event's window if an item has a pointer grab in effect */ if (!canvas->grabbed_item && event->window != SP_CANVAS_WINDOW (canvas)) @@ -1490,7 +1551,7 @@ sp_canvas_button (GtkWidget *widget, GdkEventButton *event) case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: - /* Pick the current item as if the button were not pressed, and + /* Pick the current item as if the button were not pressed, and * then process the event. */ canvas->state = event->state; @@ -1500,7 +1561,7 @@ sp_canvas_button (GtkWidget *widget, GdkEventButton *event) break; case GDK_BUTTON_RELEASE: - /* Process the event as if the button were pressed, then repick + /* Process the event as if the button were pressed, then repick * after the button has been released */ canvas->state = event->state; @@ -1509,6 +1570,7 @@ sp_canvas_button (GtkWidget *widget, GdkEventButton *event) canvas->state = event->state; pick_current_item (canvas, (GdkEvent *) event); event->state ^= mask; + break; default: @@ -1529,28 +1591,38 @@ sp_canvas_scroll (GtkWidget *widget, GdkEventScroll *event) return emit_event (SP_CANVAS (widget), (GdkEvent *) event); } +static inline void request_motions(GdkWindow *w, GdkEventMotion *event) { + gdk_window_get_pointer(w, NULL, NULL, NULL); +#if HAS_GDK_EVENT_REQUEST_MOTIONS + gdk_event_request_motions(event); +#endif +} + /** * Motion event handler for the canvas. */ static int sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event) { + int status; SPCanvas *canvas = SP_CANVAS (widget); + track_latency((GdkEvent *)event); + if (event->window != SP_CANVAS_WINDOW (canvas)) return FALSE; - if (canvas->grabbed_event_mask & GDK_POINTER_MOTION_HINT_MASK) { - gint x, y; - gdk_window_get_pointer (widget->window, &x, &y, NULL); - event->x = x; - event->y = y; - } + if (canvas->pixmap_gc == NULL) // canvas being deleted + return FALSE; canvas->state = event->state; pick_current_item (canvas, (GdkEvent *) event); + status = emit_event (canvas, (GdkEvent *) event); + if (event->is_hint) { + request_motions(widget->window, event); + } - return emit_event (canvas, (GdkEvent *) event); + return status; } static void @@ -1559,7 +1631,7 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, GtkWidget *widget = GTK_WIDGET (canvas); SPCanvasBuf buf; - if (canvas->rendermode != RENDERMODE_OUTLINE) { + if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) { buf.buf = nr_pixelstore_256K_new (FALSE, 0); } else { buf.buf = nr_pixelstore_1M_new (FALSE, 0); @@ -1568,7 +1640,7 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, // Mark the region clean sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0); - buf.buf_rowstride = sw * 3; // CAIRO FIXME: for cairo output, the buffer must be RGB unpacked, i.e. sw * 4 + buf.buf_rowstride = sw * 4; buf.rect.x0 = x0; buf.rect.y0 = y0; buf.rect.x1 = x1; @@ -1583,17 +1655,26 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, | (color->blue >> 8)); buf.is_empty = true; + buf.ct = nr_create_cairo_context_canvasbuf (&(buf.visible_rect), &buf); + if (canvas->root->flags & SP_CANVAS_ITEM_VISIBLE) { SP_CANVAS_ITEM_GET_CLASS (canvas->root)->render (canvas->root, &buf); } #if ENABLE_LCMS - cmsHTRANSFORM transf = Inkscape::colorprofile_get_display_transform(); + cmsHTRANSFORM transf = 0; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display"); + if ( fromDisplay ) { + transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" ); + } else { + transf = Inkscape::colorprofile_get_display_transform(); + } #endif // ENABLE_LCMS if (buf.is_empty) { #if ENABLE_LCMS - if ( transf ) { + if ( transf && canvas->enable_cms_display_adj ) { cmsDoTransform( transf, &buf.bg_color, &buf.bg_color, 1 ); } #endif // ENABLE_LCMS @@ -1604,48 +1685,74 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, x0 - canvas->x0, y0 - canvas->y0, x1 - x0, y1 - y0); } else { -/* -// CAIRO FIXME: after SPCanvasBuf is made 32bpp throughout, this rgb_draw below can be replaced with the below. -// Why this must not be done currently: -// - all canvas items (handles, nodes etc) paint themselves assuming 24bpp -// - cairo assumes bgra, but we have rgba, so r and b get swapped (until we paint all with cairo too) -// - it does not seem to be any faster; in fact since with 32bpp, buf contains less pixels, -// we need more bufs to paint a given area and as a result it's even a bit slower - - cairo_surface_t* cst = cairo_image_surface_create_for_data ( - buf.buf, - CAIRO_FORMAT_RGB24, // unpacked, i.e. 32 bits! one byte is unused - x1 - x0, y1 - y0, - buf.buf_rowstride - ); - cairo_t *ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas)); - cairo_set_source_surface (ct, cst, x0 - canvas->x0, y0 - canvas->y0); - cairo_paint (ct); - cairo_destroy (ct); - cairo_surface_finish (cst); - cairo_surface_destroy (cst); -*/ #if ENABLE_LCMS - if ( transf ) { + if ( transf && canvas->enable_cms_display_adj ) { for ( gint yy = 0; yy < (y1 - y0); yy++ ) { - guchar* p = buf.buf + (sw * 3) * yy; + guchar* p = buf.buf + (buf.buf_rowstride * yy); cmsDoTransform( transf, p, p, (x1 - x0) ); } } #endif // ENABLE_LCMS +// Now we only need to output the prepared pixmap to the actual screen, and this define chooses one +// of the two ways to do it. The cairo way is direct and straightforward, but unfortunately +// noticeably slower. I asked Carl Worth but he was unable so far to suggest any specific reason +// for this slowness. So, for now we use the oldish method: squeeze out 32bpp buffer to 24bpp and +// use gdk_draw_rgb_image_dithalign, for unfortunately gdk can only handle 24 bpp, which cairo +// cannot handle at all. Still, this way is currently faster even despite the blit with squeeze. + +///#define CANVAS_OUTPUT_VIA_CAIRO + +#ifdef CANVAS_OUTPUT_VIA_CAIRO + + buf.cst = cairo_image_surface_create_for_data ( + buf.buf, + CAIRO_FORMAT_ARGB32, // unpacked, i.e. 32 bits! one byte is unused + x1 - x0, y1 - y0, + buf.buf_rowstride + ); + cairo_t *window_ct = gdk_cairo_create(SP_CANVAS_WINDOW (canvas)); + cairo_set_source_surface (window_ct, buf.cst, x0 - canvas->x0, y0 - canvas->y0); + cairo_paint (window_ct); + cairo_destroy (window_ct); + cairo_surface_finish (buf.cst); + cairo_surface_destroy (buf.cst); + +#else + + NRPixBlock b3; + nr_pixblock_setup_fast (&b3, NR_PIXBLOCK_MODE_R8G8B8, x0, y0, x1, y1, TRUE); + + NRPixBlock b4; + nr_pixblock_setup_extern (&b4, NR_PIXBLOCK_MODE_R8G8B8A8P, x0, y0, x1, y1, + buf.buf, + buf.buf_rowstride, + FALSE, FALSE); + + // this does the 32->24 squishing, using an assembler routine: + nr_blit_pixblock_pixblock (&b3, &b4); + gdk_draw_rgb_image_dithalign (SP_CANVAS_WINDOW (canvas), canvas->pixmap_gc, x0 - canvas->x0, y0 - canvas->y0, x1 - x0, y1 - y0, GDK_RGB_DITHER_MAX, - buf.buf, + NR_PIXBLOCK_PX(&b3), sw * 3, x0 - canvas->x0, y0 - canvas->y0); + + nr_pixblock_release (&b3); + nr_pixblock_release (&b4); +#endif } - if (canvas->rendermode != RENDERMODE_OUTLINE) { + cairo_surface_t *cst = cairo_get_target(buf.ct); + cairo_destroy (buf.ct); + cairo_surface_finish (cst); + cairo_surface_destroy (cst); + + if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) { nr_pixelstore_256K_free (buf.buf); } else { nr_pixelstore_1M_free (buf.buf); @@ -1657,7 +1764,7 @@ struct PaintRectSetup { NRRectL big_rect; GTimeVal start_time; int max_pixels; - NR::Point mouse_loc; + Geom::Point mouse_loc; }; /** @@ -1736,14 +1843,15 @@ faster. The default for now is the strips mode. */ if (bw < bh || bh < 2 * TILE_SIZE) { - int mid = (this_rect.x0 + this_rect.x1) / 2; + // to correctly calculate the mean of two ints, we need to sum them into a larger int type + int mid = ((long long) this_rect.x0 + (long long) this_rect.x1) / 2; // Make sure that mid lies on a tile boundary mid = (mid / TILE_SIZE) * TILE_SIZE; lo.x1 = mid; hi.x0 = mid; - if (setup->mouse_loc[NR::X] < mid) { + if (setup->mouse_loc[Geom::X] < mid) { // Always paint towards the mouse first return sp_canvas_paint_rect_internal(setup, lo) && sp_canvas_paint_rect_internal(setup, hi); @@ -1752,14 +1860,15 @@ The default for now is the strips mode. && sp_canvas_paint_rect_internal(setup, lo); } } else { - int mid = (this_rect.y0 + this_rect.y1) / 2; + // to correctly calculate the mean of two ints, we need to sum them into a larger int type + int mid = ((long long) this_rect.y0 + (long long) this_rect.y1) / 2; // Make sure that mid lies on a tile boundary mid = (mid / TILE_SIZE) * TILE_SIZE; lo.y1 = mid; hi.y0 = mid; - if (setup->mouse_loc[NR::Y] < mid) { + if (setup->mouse_loc[Geom::Y] < mid) { // Always paint towards the mouse first return sp_canvas_paint_rect_internal(setup, lo) && sp_canvas_paint_rect_internal(setup, hi); @@ -1811,17 +1920,16 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1) // Save the mouse location gint x, y; gdk_window_get_pointer (GTK_WIDGET(canvas)->window, &x, &y, NULL); - setup.mouse_loc = sp_canvas_window_to_world (canvas, NR::Point(x,y)); + setup.mouse_loc = sp_canvas_window_to_world (canvas, Geom::Point(x,y)); - // CAIRO FIXME: the sw/sh calculations below all assume 24bpp, need fixing for 32bpp - if (canvas->rendermode != RENDERMODE_OUTLINE) { + if (canvas->rendermode != Inkscape::RENDERMODE_OUTLINE) { // use 256K as a compromise to not slow down gradients - // 256K is the cached buffer and we need 3 channels - setup.max_pixels = 87381; // 256K/3 + // 256K is the cached buffer and we need 4 channels + setup.max_pixels = 65536; // 256K/4 } else { // paths only, so 1M works faster - // 1M is the cached buffer and we need 3 channels - setup.max_pixels = 349525; + // 1M is the cached buffer and we need 4 channels + setup.max_pixels = 262144; } // Start the clock @@ -1951,7 +2059,7 @@ static int paint (SPCanvas *canvas) { if (canvas->need_update) { - sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0); + sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0); canvas->need_update = FALSE; } @@ -2003,12 +2111,15 @@ paint (SPCanvas *canvas) static int do_update (SPCanvas *canvas) { - if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display! + if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display! + return TRUE; + + if (canvas->drawing_disabled) return TRUE; /* Cause the update if necessary */ if (canvas->need_update) { - sp_canvas_item_invoke_update (canvas->root, NR::identity(), 0); + sp_canvas_item_invoke_update (canvas->root, Geom::identity(), 0); canvas->need_update = FALSE; } @@ -2073,7 +2184,7 @@ sp_canvas_root (SPCanvas *canvas) } /** - * Scrolls canvas to specific position. + * Scrolls canvas to specific position (cx and cy are measured in screen pixels) */ void sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, bool is_scrolling) @@ -2081,12 +2192,12 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, g_return_if_fail (canvas != NULL); g_return_if_fail (SP_IS_CANVAS (canvas)); - int ix = (int) (cx + 0.5); - int iy = (int) (cy + 0.5); - int dx = ix - canvas->x0; - int dy = iy - canvas->y0; + 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 - canvas->x0; // dx and dy specify the displacement (scroll) of the + int dy = iy - canvas->y0; // canvas w.r.t its previous position - canvas->dx0 = cx; + canvas->dx0 = cx; // here the 'd' stands for double, not delta! canvas->dy0 = cy; canvas->x0 = ix; canvas->y0 = iy; @@ -2105,14 +2216,6 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw } - /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */ - SPEventContext *ec = inkscape_active_event_context(); - if (SP_IS_3DBOX_CONTEXT (ec)) { - // We could avoid redraw during panning by checking the status of is_scrolling, but this is - // neither faster nor does it get rid of artefacts, so we update PLs unconditionally - SP3DBoxContext *bc = SP_3DBOX_CONTEXT (ec); - bc->_vpdrag->updateLines(); - } } /** @@ -2200,51 +2303,58 @@ void sp_canvas_world_to_window(SPCanvas const *canvas, double worldx, double wor /** * Converts point from win to world coordinates. */ -NR::Point sp_canvas_window_to_world(SPCanvas const *canvas, NR::Point const win) +Geom::Point sp_canvas_window_to_world(SPCanvas const *canvas, Geom::Point const win) { g_assert (canvas != NULL); g_assert (SP_IS_CANVAS (canvas)); - return NR::Point(canvas->x0 + win[0], canvas->y0 + win[1]); + return Geom::Point(canvas->x0 + win[0], canvas->y0 + win[1]); } /** * Converts point from world to win coordinates. */ -NR::Point sp_canvas_world_to_window(SPCanvas const *canvas, NR::Point const world) +Geom::Point sp_canvas_world_to_window(SPCanvas const *canvas, Geom::Point const world) { g_assert (canvas != NULL); g_assert (SP_IS_CANVAS (canvas)); - return NR::Point(world[0] - canvas->x0, world[1] - canvas->y0); + return Geom::Point(world[0] - canvas->x0, world[1] - canvas->y0); } /** * Returns true if point given in world coordinates is inside window. */ -bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, NR::Point const &world) +bool sp_canvas_world_pt_inside_window(SPCanvas const *canvas, Geom::Point const &world) { g_assert( canvas != NULL ); g_assert(SP_IS_CANVAS(canvas)); - using NR::X; - using NR::Y; GtkWidget const &w = *GTK_WIDGET(canvas); - return ( ( canvas->x0 <= world[X] ) && - ( canvas->y0 <= world[Y] ) && - ( world[X] < canvas->x0 + w.allocation.width ) && - ( world[Y] < canvas->y0 + w.allocation.height ) ); + return ( ( canvas->x0 <= world[Geom::X] ) && + ( canvas->y0 <= world[Geom::Y] ) && + ( world[Geom::X] < canvas->x0 + w.allocation.width ) && + ( world[Geom::Y] < canvas->y0 + w.allocation.height ) ); } /** - * Return canvas window coordinates as NR::Rect. + * Return canvas window coordinates as Geom::Rect. */ -NR::Rect SPCanvas::getViewbox() const +Geom::Rect SPCanvas::getViewbox() const { GtkWidget const *w = GTK_WIDGET(this); + return Geom::Rect(Geom::Point(dx0, dy0), + Geom::Point(dx0 + w->allocation.width, dy0 + w->allocation.height)); +} - return NR::Rect(NR::Point(dx0, dy0), - NR::Point(dx0 + w->allocation.width, dy0 + w->allocation.height)); +/** + * Return canvas window coordinates as IRect (a rectangle defined by integers). + */ +NR::IRect SPCanvas::getViewboxIntegers() const +{ + GtkWidget const *w = GTK_WIDGET(this); + return NR::IRect(NR::IPoint(x0, y0), + NR::IPoint(x0 + w->allocation.width, y0 + w->allocation.height)); } inline int sp_canvas_tile_floor(int x) @@ -2340,4 +2450,4 @@ void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8 fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :