sp-canvas.cpp revision a00ebbce587378386ff996ef28c27c2d4c06827d
1462N/A * Port of GnomeCanvas for Inkscape needs 1462N/A * Federico Mena <federico@nuclecu.unam.mx> 1462N/A * Raph Levien <raph@gimp.org> 1462N/A * Lauris Kaplinski <lauris@kaplinski.com> 1462N/A * Copyright (C) 1998 The Free Software Foundation * Copyright (C) 2002-2006 authors * Released under GNU GPL, read the file 'COPYING' for more information // Define this to visualize the regions to be redrawn //#define DEBUG_REDRAW 1; // Tiles are a way to minimize the number of redraws, eliminating too small redraws. // The canvas stores a 2D array of ints, each representing a TILE_SIZExTILE_SIZE pixels tile. // If any part of it is dirtied, the entire tile is dirtied (its int is nonzero) and repainted. * The SPCanvasGroup vtable. * Registers the SPCanvasItem class with Glib and returns its type number. * Initializes the SPCanvasItem vtable and the "event" signal. /* fixme: Derive from GObject */ * Callback for initialization of SPCanvasItem. * Constructs new SPCanvasItem on SPCanvasGroup. * Sets up the newly created SPCanvasItem. * We make it static for encapsulation reasons since it was nowhere used. * Helper function that requests redraw only if item's visible flag is set. if (
x0 !=0 ||
x1 !=0 ||
y0 !=0 ||
y1 !=0) {
* Callback that removes item from all referers and destroys it. // Hack: if this is a ctrlrect, move it to 0,0; // this redraws only the stroke of the rect to be deleted, // avoiding redraw of the entire area * Helper function to update item and its children. * NB! affine is parent2canvas. /* Apply the child item's transform */ /* apply object flags to child flags */ * Helper function to invoke the point method of the item. * The argument x, y should be in the parent's item-relative coordinate * system. This routine applies the inverse of the item's transform, * maintaining the affine invariant. * Makes the item's affine transformation matrix be equal to the specified * @affine: An affine transformation matrix. * Convenience function to reorder items in a group's child list. * This puts the specified link after the "before" link. * Raises the item in its parent's stack by the specified number of positions. * \param item A canvas item. * \param positions Number of steps to raise the item. * If the number of positions is greater than the distance to the top of the * stack, then the item is put at the top. * Lowers the item in its parent's stack by the specified number of positions. * \param item A canvas item. * \param positions Number of steps to lower the item. * If the number of positions is greater than the distance to the bottom of the * stack, then the item is put at the bottom. * Sets visible flag on item and requests a redraw. if (
x0 !=0 ||
x1 !=0 ||
y0 !=0 ||
y1 !=0) {
* Clears visible flag on item and requests a redraw. if (
x0 !=0 ||
x1 !=0 ||
y0 !=0 ||
y1 !=0) {
* Grab item under cursor. * \pre !canvas->grabbed_item && item->flags & SP_CANVAS_ITEM_VISIBLE /* 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 */ * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the * \param item A canvas item that holds a grab. * \param etime The timestamp for ungrabbing the mouse. * Returns the product of all transformation matrices from the root item down * Helper that returns true iff item is descendant of parent. * Focus canvas, and item under cursor if it is not already focussed. * Requests that the canvas queue an update for the specified item. * To be used only by item implementations. /* Recurse up the tree */ /* Have reached the top of the tree, make sure the update call gets scheduled. */ * Returns position of item in group. * Registers SPCanvasGroup class with Gtk and returns its type number. * Class initialization function for SPCanvasGroupClass * Callback that destroys all items in group and calls group's virtual * Update handler for canvas groups * Point handler for canvas groups. double const x = p[
NR::X];
double const y = p[
NR::Y];
* Renders all visible canvas group items in buf rectangle. * Adds an item to a canvas group. * Removes an item from a canvas group /* Remove it from the list */ * Registers the SPCanvas class if necessary, and returns the type ID * \return The type ID of the SPCanvas class. * Class initialization function for SPCanvasClass. * Callback: object initialization for SPCanvas. /* Create the root item as a special case */ * Convenience function to remove the idle handler of a canvas. * Removes the transient state of the canvas (idle handler, grabs). /* We turn off the need_redraw flag, since if the canvas is mapped again * it will request a redraw anyways. We do not turn off the need_update * flag, though, because updates are not queued when the canvas remaps * Destroy handler for SPCanvas. * Returns new canvas as widget. * The canvas widget's realize callback. * The canvas widget's unrealize callback. * The canvas widget's size_request callback. * The canvas widget's size_allocate callback. /* Schedule redraw of new region */ * Helper that emits an event for an item in the canvas, be it the current * item, grabbed item, or focused item, as appropriate. /* Convert to world coordinates -- we have two cases because of diferent * offsets of the fields in the event structures. /* Choose where we send the event */ /* canvas->current_item becomes NULL in some cases under Win32 ** (e.g. if the pointer leaves the window). So this is a hack that ** Lauris applied to SP to get around the problem. /* The event is propagated up the hierarchy (for if someone connected to * a group instead of a leaf event), and emission is stopped if a * handler returns TRUE, just like for GtkWidget events. * Helper that re-picks the current item in the canvas, based on the event's * coordinates and emits enter/leave events for items as appropriate. if (!
canvas->
root)
// canvas may have already be destroyed by closing desktop durring interrupted display! // If a button is down, we'll perform enter and leave events on the // current item, but not enter on any other item. This is more or // less like X pointer grabbing for canvas items. /* Save the event in the canvas. This is used to synthesize enter and * leave events in case the current item changes. It is also used to * re-pick the current item if the current one gets deleted. Also, * synthesize an enter event. /* these fields have the same offsets in both types of events */ /* these fields don't have the same offsets in both types of events */ /* Don't do anything else if this is a recursive call */ /* LeaveNotify means that there is no current item, so we don't look for one */ /* these fields don't have the same offsets in both types of events */ /* find the closest item */ return retval;
/* current item did not change */ /* Synthesize events for old and new current items */ // new_current_item may have been set to NULL during the call to /* Handle the rest of cases */ * Button event handler for the canvas. /* dispatch normally regardless of the event's window if an item has has a pointer grab in effect */ /* Pick the current item as if the button were not pressed, and * then process the event. /* Process the event as if the button were pressed, then repick * after the button has been released * Scroll event handler for the canvas. * \todo FIXME: generate motion events to re-select items. * Motion event handler for the canvas. // 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 // this does the 32->24 squishing, using an assembler routine: * Paint the given rect, recursively subdividing the region until it is the size of a single * @return true if the drawing completes // Allow only very fast buffers to be run together; // as soon as the total redraw time exceeds 1ms, cancel; // this returns control to the idle loop and allows Inkscape to process user input // (potentially interrupting the redraw); as soon as Inkscape has some more idle time, // it will get back and finish painting what remains to paint. // Interrupting redraw isn't always good. // For example, when you drag one node of a big path, only the buffer containing // the mouse cursor will be redrawn again and again, and the rest of the path // will remain stale because Inkscape never has enough idle time to redraw all // of the screen. To work around this, such operations set a forced_redraw_limit > 0. // If this limit is set, and if we have aborted redraw more times than is allowed, // interrupting is blocked and we're forced to redraw full screen once // (after which we can again interrupt forced_redraw_limit times). // Find the optimal buffer dimensions if ((
bw <
1) || (
bh <
1))
This test determines the redraw strategy: bw < bh (strips mode) splits across the smaller dimension of the rect and therefore (on horizontally-stretched windows) results in redrawing in horizontal strips (from cursor point, in both directions if the cursor is in the middle). This is traditional for Inkscape since old days, and seems to be faster for drawings with many smaller objects at zoom-out. bw > bh (chunks mode) splits across the larger dimension of the rect and therefore paints in almost-square chunks, again from the cursor point. It's sometimes faster for drawings with few slow (e.g. blurred) objects crossing the entire screen. It also appears to be somewhat psychologically The default for now is the strips mode. // to correctly calculate the mean of two ints, we need to sum them into a larger int type // Make sure that mid lies on a tile boundary // Always paint towards the mouse first // to correctly calculate the mean of two ints, we need to sum them into a larger int type // Make sure that mid lies on a tile boundary // Always paint towards the mouse first * Helper that draws a specific rectangular part of the canvas. * @return true if the rectangle painting succeeds. // Clip rect-to-draw by the current visible area // paint the area to redraw yellow // Save the mouse location // use 256K as a compromise to not slow down gradients // 256K is the cached buffer and we need 4 channels // paths only, so 1M works faster // 1M is the cached buffer and we need 4 channels * Force a full redraw after a specified number of interrupted redraws * End forced full redraw requests * The canvas widget's expose callback. for (
int i = 0; i <
n_rects; i++) {
* The canvas widget's keypress callback. * Crossing event handler for the canvas. * Focus in handler for the canvas. * Focus out handler for the canvas. * Helper that repaints the areas in the canvas that need it. * @return true if all the dirty parts have been redrawn // we've had a full unaborted redraw, reset the full redraw counter * Helper that invokes update, paint, and repick on canvas. if (!
canvas->
root || !
canvas->
pixmap_gc)
// canvas may have already be destroyed by closing desktop durring interrupted display! /* Cause the update if necessary */ /* Pick new current item */ * Idle handler for the canvas that deals with pending updates and redraws. * Convenience function to add an idle handler to a canvas. * Returns the root group of the specified canvas. * Scrolls canvas to specific position (cx and cy are measured in screen pixels) 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 canvas->
dx0 =
cx;
// here the 'd' stands for double, not delta! // scrolling without zoom; redraw only the newly exposed areas if ((
dx != 0) || (
dy != 0)) {
// scrolling as part of zoom; do nothing here - the next do_update will perform full redraw * Updates canvas if necessary. * Update callback for canvas widget. * Forces redraw of rectangular canvas area. * Sets world coordinates from win and canvas. * Sets win coordinates from world and canvas. * Converts point from win to world coordinates. * Converts point from world to win coordinates. * Returns true if point given in world coordinates is inside window. * Return canvas window coordinates as NR::Rect. * Return canvas window coordinates as IRect (a rectangle defined by integers). * Helper that allocates a new tile array for the canvas, copying overlapping tiles from the old array for (
int i=
tl; i<
tr; i++) {
for (
int j=
tt; j<
tb; j++) {
* Helper that queues a canvas rectangle for redraw * Helper that marks specific canvas rectangle as clean (val == 0) or dirty (otherwise) for (
int i=
tl; i<
tr; i++) {
for (
int j=
tt; j<
tb; j++) {
c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :