diff --git a/src/desktop.cpp b/src/desktop.cpp index bff5a8348a1bc465ae32f547deab766328ca7db2..aca1fb993d899be9b9d43c21b6e2b6346cf2dee7 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -665,6 +665,9 @@ SPDesktop::prev_transform() return; } + // Set the previous affine + _prev_affine = _current_affine; + // Push current transform into future transforms list. transforms_future.push_front( _current_affine ); @@ -673,6 +676,7 @@ SPDesktop::prev_transform() // restore previous transform _current_affine = transforms_past.front(); + set_display_area (false); } @@ -687,6 +691,9 @@ void SPDesktop::next_transform() this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next transform.")); return; } + + // Set the previous affine + _prev_affine = _current_affine; // restore next transform _current_affine = transforms_future.front(); @@ -719,7 +726,8 @@ SPDesktop::clear_transform_history() void SPDesktop::set_display_area (bool log) { - // Save the transform + Geom::Affine advert_transform = _prev_affine.w2d().inverse() * _current_affine.w2d(); + if (log) { transforms_past.push_front( _current_affine ); // if we do a logged transform, our transform-forward list is invalidated, so delete it @@ -730,9 +738,8 @@ SPDesktop::set_display_area (bool log) // Scroll Geom::Point offset = _current_affine.getOffset(); - canvas->scroll_to(offset, true); + canvas->scroll_to(offset, true, advert_transform); canvas->set_affine(_current_affine.d2w()); // For CanvasItem's. - // To do: if transform unchanged call with 'false' (redraw only newly exposed areas). /* Update perspective lines if we are in the 3D box tool (so that infinite ones are shown * correctly) */ @@ -755,10 +762,13 @@ SPDesktop::set_display_area (bool log) * is a point on the canvas and 'w' is position in window in screen pixels. */ void -SPDesktop::set_display_area (Geom::Point const &c, Geom::Point const &w, bool log) +SPDesktop::set_display_area (Geom::Point const &c, Geom::Point const &w, bool log, bool from_rect) { // The relative offset needed to keep c at w. Geom::Point offset = d2w(c) - w; + if (!from_rect) { + _prev_affine.setOffset(_current_affine.getOffset()); + } _current_affine.addOffset( offset ); set_display_area( log ); } @@ -789,11 +799,12 @@ SPDesktop::set_display_area( Geom::Rect const &r, double border, bool log) zoom = w.height() / r.height(); } zoom = CLAMP(zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); + _prev_affine = _current_affine; _current_affine.setScale( Geom::Scale(zoom, yaxisdir() * zoom) ); // Zero offset, actual offset calculated later. _current_affine.setOffset( Geom::Point( 0, 0 ) ); - set_display_area( r.midpoint(), w.midpoint(), log ); + set_display_area( r.midpoint(), w.midpoint(), log, true); } @@ -824,6 +835,7 @@ SPDesktop::zoom_absolute(Geom::Point const ¢er, double zoom, bool keep_point w = canvas->get_area_world().midpoint(); } zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); + _prev_affine = _current_affine; _current_affine.setScale( Geom::Scale(zoom, yaxisdir() * zoom) ); set_display_area( center, w ); } @@ -988,6 +1000,7 @@ void SPDesktop::zoom_quick(bool enable) zoom_relative(midpoint, 2.0, false); } } else { + _prev_affine = _current_affine; _current_affine = _quick_zoom_affine; set_display_area( false ); } @@ -1017,6 +1030,7 @@ void SPDesktop::rotate_absolute_keep_point (Geom::Point const &c, double rotate) { Geom::Point w = d2w( c ); // Must be before rotate changed. + _prev_affine = _current_affine; _current_affine.setRotate( rotate ); set_display_area( c, w ); } @@ -1032,6 +1046,7 @@ void SPDesktop::rotate_relative_keep_point (Geom::Point const &c, double rotate) { Geom::Point w = d2w( c ); // Must be before rotate changed. + _prev_affine = _current_affine; _current_affine.addRotate( rotate ); set_display_area( c, w ); } @@ -1046,6 +1061,7 @@ SPDesktop::rotate_relative_keep_point (Geom::Point const &c, double rotate) void SPDesktop::rotate_absolute_center_point (Geom::Point const &c, double rotate) { + _prev_affine = _current_affine; _current_affine.setRotate( rotate ); Geom::Rect viewbox = canvas->get_area_world(); set_display_area(c, viewbox.midpoint()); @@ -1061,6 +1077,7 @@ SPDesktop::rotate_absolute_center_point (Geom::Point const &c, double rotate) void SPDesktop::rotate_relative_center_point (Geom::Point const &c, double rotate) { + _prev_affine = _current_affine; _current_affine.addRotate( rotate ); Geom::Rect viewbox = canvas->get_area_world(); set_display_area(c, viewbox.midpoint()); @@ -1077,6 +1094,7 @@ void SPDesktop::flip_absolute_keep_point (Geom::Point const &c, CanvasFlip flip) { Geom::Point w = d2w(c); // Must be before flip. + _prev_affine = _current_affine; _current_affine.setFlip(flip); set_display_area(c, w); } @@ -1092,6 +1110,7 @@ void SPDesktop::flip_relative_keep_point (Geom::Point const &c, CanvasFlip flip) { Geom::Point w = d2w(c); // Must be before flip. + _prev_affine = _current_affine; _current_affine.addFlip(flip); set_display_area(c, w); } @@ -1106,6 +1125,7 @@ SPDesktop::flip_relative_keep_point (Geom::Point const &c, CanvasFlip flip) void SPDesktop::flip_absolute_center_point (Geom::Point const &c, CanvasFlip flip) { + _prev_affine = _current_affine; _current_affine.setFlip(flip); Geom::Rect viewbox = canvas->get_area_world(); set_display_area(c, viewbox.midpoint()); @@ -1121,6 +1141,7 @@ SPDesktop::flip_absolute_center_point (Geom::Point const &c, CanvasFlip flip) void SPDesktop::flip_relative_center_point (Geom::Point const &c, CanvasFlip flip) { + _prev_affine = _current_affine; _current_affine.addFlip(flip); Geom::Rect viewbox = canvas->get_area_world(); set_display_area(c, viewbox.midpoint()); @@ -1140,6 +1161,7 @@ void SPDesktop::scroll_absolute (Geom::Point const &point, bool is_scrolling) { canvas->scroll_to(point, false); + _prev_affine.setOffset(_current_affine.getOffset()); _current_affine.setOffset( point ); /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */ @@ -1851,6 +1873,11 @@ Geom::Affine SPDesktop::w2d() const return _current_affine.w2d(); } +Geom::Affine SPDesktop::d2w() const +{ + return _current_affine.d2w(); +} + Geom::Point SPDesktop::w2d(Geom::Point const &p) const { return p * _current_affine.w2d(); @@ -1861,6 +1888,26 @@ Geom::Point SPDesktop::d2w(Geom::Point const &p) const return p * _current_affine.d2w(); } +Geom::Affine SPDesktop::prev_w2d() const +{ + return _prev_affine.w2d(); +} + +Geom::Affine SPDesktop::prev_d2w() const +{ + return _prev_affine.d2w(); +} + +Geom::Point SPDesktop::prev_w2d(Geom::Point const &p) const +{ + return p * _prev_affine.w2d(); +} + +Geom::Point SPDesktop::prev_d2w(Geom::Point const &p) const +{ + return p * _prev_affine.d2w(); +} + const Geom::Affine &SPDesktop::doc2dt() const { g_assert(doc() != nullptr); diff --git a/src/desktop.h b/src/desktop.h index b6375b5b0b31ae5265571b41c11eb15badfc1c84..78636fd8059c7ec0d164b34026fe29875172ceb0 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -309,7 +309,7 @@ public: void clear_transform_history(); void set_display_area (bool log = true); - void set_display_area (Geom::Point const &c, Geom::Point const &w, bool log = true); + void set_display_area (Geom::Point const &c, Geom::Point const &w, bool log = true, bool from_rect = false); void set_display_area (Geom::Rect const &a, Geom::Coord border, bool log = true); Geom::Parallelogram get_display_area(bool use_integer_viewbox = false) const; @@ -415,10 +415,13 @@ public: // TODO return const ref instead of copy Geom::Affine w2d() const; //transformation from window to desktop coordinates (zoom/rotate). + Geom::Affine d2w() const; /// Transformation from desktop to window coordinates Geom::Point w2d(Geom::Point const &p) const; - /// Transformation from desktop to window coordinates - Geom::Affine d2w() const { return _current_affine.d2w(); } Geom::Point d2w(Geom::Point const &p) const; + Geom::Affine prev_w2d() const; + Geom::Point prev_w2d(Geom::Point const &p) const; + Geom::Affine prev_d2w() const; + Geom::Point prev_d2w(Geom::Point const &p) const; const Geom::Affine& doc2dt() const; Geom::Affine dt2doc() const; Geom::Point doc2dt(Geom::Point const &p) const; @@ -519,7 +522,15 @@ private: Geom::Point getOffset() { return _offset; } - + DesktopAffine &operator=(const DesktopAffine &other) { + _w2d = other._w2d; + _d2w = other._d2w; + _rotate = other._rotate; + _scale = other._scale; + _flip = other._flip; + _offset = other._offset; + return *this; + } private: void _update() { _d2w = _scale * _rotate * _flip; @@ -534,6 +545,7 @@ private: }; DesktopAffine _current_affine; + DesktopAffine _prev_affine; std::list transforms_past; std::list transforms_future; bool _split_canvas; diff --git a/src/ui/tools/zoom-tool.cpp b/src/ui/tools/zoom-tool.cpp index 612051dcdacf7a646defc28174a0a32675394a67..8f3e7811a265aa8aefdd202890fc59086697dcd0 100644 --- a/src/ui/tools/zoom-tool.cpp +++ b/src/ui/tools/zoom-tool.cpp @@ -133,7 +133,7 @@ bool ZoomTool::root_handler(GdkEvent* event) { if ( event->button.button == 1 && !this->space_panning) { Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle(); - + Inkscape::Rubberband::get(desktop)->stop(); if (b && !within_tolerance && !(GDK_SHIFT_MASK & event->button.state) ) { desktop->set_display_area(*b, 10); } else if (!escaped) { @@ -145,9 +145,11 @@ bool ZoomTool::root_handler(GdkEvent* event) { } ret = true; + } else { + Inkscape::Rubberband::get(desktop)->stop(); } - Inkscape::Rubberband::get(desktop)->stop(); + ungrabCanvasEvents(); diff --git a/src/ui/widget/canvas.cpp b/src/ui/widget/canvas.cpp index 9bbd255eb5881385a5e1dab72ca78f8c4911148d..f99fe4f4bbda72ce57cf5333290ab431311a05e0 100644 --- a/src/ui/widget/canvas.cpp +++ b/src/ui/widget/canvas.cpp @@ -30,6 +30,7 @@ #include "display/control/canvas-item-group.h" #include "ui/tools/tool-base.h" // Default cursor +#include /* * The canvas is responsible for rendering the SVG drawing with various "control" @@ -289,11 +290,14 @@ Canvas::request_update() * Complete redraw if 'clear' is true. */ void -Canvas::scroll_to(Geom::Point const &c, bool clear) +Canvas::scroll_to(Geom::Point const &c, bool clear, Geom::Affine advert_transform) { int old_x0 = _x0; int old_y0 = _y0; - + int _prev_old_x0 = _prev_x0; + int _prev_old_y0 = _prev_y0; + _prev_x0 = _x0; + _prev_y0 = _y0; // This is the only place the _x0 and _y0 are set! _x0 = (int) round(c[Geom::X]); // cx might be negative, so (int)(cx + 0.5) will not do! _y0 = (int) round(c[Geom::Y]); @@ -322,8 +326,152 @@ Canvas::scroll_to(Geom::Point const &c, bool clear) expanded.expandBy(expansion); _drawing->setCacheLimit(expanded, false); } - if (clear || !overlap) { + // Save the transform + if (_desktop && clear && !Geom::are_near(Geom::identity(), advert_transform)) { + // Paint background + if (_backing_store) { + cairo_surface_write_to_png (_backing_store->cobj(), "/home/jtx/Development/hello3.png"); + Cairo::RefPtr<::Cairo::ImageSurface> new_store = + Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, + _allocation.get_width() * _device_scale, + _allocation.get_height() * _device_scale); + + cairo_surface_set_device_scale(new_store->cobj(), _device_scale, _device_scale); // No C++ API! + + // Copy the old store contents to new backing store. + auto cr = Cairo::Context::create(new_store); + Geom::Rect w(Geom::Point(), get_area_world().dimensions()); + /*Geom::Rect w1(Geom::Point(old_x0,old_y0), get_area_world().dimensions()); + Geom::Rect w2(Geom::Point(_x0,_y0), get_area_world().dimensions()); + Geom::Point res = _desktop->prev_w2d(Geom::Point(_x0,_y0) + w.midpoint()) - _desktop->prev_w2d(Geom::Point(old_x0,old_y0) + w.midpoint());; + */ + /* std::cout << 0 << res << std::endl; + res = _desktop->prev_w2d(Geom::Point(_x0,_y0) + w.midpoint()); + std::cout << 0 << res << std::endl; + res = _desktop->prev_w2d(Geom::Point(old_x0,old_y0) + w.midpoint()); + std::cout << 0 << res << std::endl; + res = _desktop->prev_w2d(Geom::Point(_prev_old_x0,_prev_old_y0) + w.midpoint()); + std::cout << 0 << res << std::endl; + + res = _desktop->prev_w2d(Geom::Point(old_x0,old_y0) + w.midpoint()); + std::cout << "0-0" << res << std::endl; + std::cout << 1 << (Geom::Point(old_x0,old_y0)) << std::endl; + std::cout << 2 << (Geom::Point(_x0,_y0)) << std::endl; + std::cout << 3 << (Geom::Point(dx, dy)) << std::endl; + std::cout << "3-1" << (w1.midpoint()) << std::endl; + std::cout << "3-2" << (w2.midpoint()) << std::endl; + std::cout << 4 << (_desktop->d2w(Geom::Point(old_x0,old_y0))) << std::endl; + std::cout << 5 << (_desktop->d2w(Geom::Point(_x0,_y0))) << std::endl; + std::cout << 6 << (_desktop->d2w(Geom::Point(dx, dy))) << std::endl; + std::cout << "6-1" << (_desktop->d2w(w1.midpoint())) << std::endl; + std::cout << "6-2" << (_desktop->d2w(w2.midpoint())) << std::endl; + std::cout << 7 << (_desktop->w2d(Geom::Point(old_x0,old_y0))) << std::endl; + std::cout << 8 << (_desktop->w2d(Geom::Point(_x0,_y0))) << std::endl; + std::cout << 9 << (_desktop->w2d(Geom::Point(dx, dy))) << std::endl; + std::cout << "9-1" << (_desktop->w2d(w1.midpoint())) << std::endl; + std::cout << "9-2" << (_desktop->w2d(w2.midpoint())) << std::endl; + std::cout << 10 << (Geom::Point(old_x0,old_y0) * advert_transform.inverse()) << std::endl; + std::cout << 11 << (Geom::Point(_x0,_y0) * advert_transform.inverse()) << std::endl; + std::cout << 12 << (Geom::Point(dx, dy) * advert_transform.inverse()) << std::endl; + std::cout << "12-1" << (w1.midpoint() * advert_transform.inverse()) << std::endl; + std::cout << "12-1" << (w2.midpoint() * advert_transform.inverse()) << std::endl; + std::cout << 13 << (_desktop->d2w(Geom::Point(old_x0,old_y0)) * advert_transform.inverse()) << std::endl; + std::cout << 14 << (_desktop->d2w(Geom::Point(_x0,_y0)) * advert_transform.inverse()) << std::endl; + std::cout << 15 << (_desktop->d2w(Geom::Point(dx, dy)) * advert_transform.inverse()) << std::endl; + std::cout << "15-1" << (_desktop->d2w(w1.midpoint()) * advert_transform.inverse()) << std::endl; + std::cout << "15-2" << (_desktop->d2w(w2.midpoint()) * advert_transform.inverse()) << std::endl; + std::cout << 16 << (_desktop->w2d(Geom::Point(old_x0,old_y0)) * advert_transform.inverse()) << std::endl; + std::cout << 17 << (_desktop->w2d(Geom::Point(_x0,_y0)) * advert_transform.inverse()) << std::endl; + std::cout << 18 << (_desktop->w2d(Geom::Point(dx, dy)) * advert_transform.inverse()) << std::endl; + std::cout << "18-1" << (_desktop->w2d(w1.midpoint()) * advert_transform.inverse()) << std::endl; + std::cout << "18-2" << (_desktop->w2d(w2.midpoint()) * advert_transform.inverse()) << std::endl; + std::cout << 19 << (Geom::Point(old_x0,old_y0) * advert_transform) << std::endl; + std::cout << 20 << (Geom::Point(_x0,_y0) * advert_transform) << std::endl; + std::cout << 21 << (Geom::Point(dx, dy) * advert_transform) << std::endl; + std::cout << "21-1" << (w1.midpoint() * advert_transform) << std::endl; + std::cout << "21-2" << (w2.midpoint() * advert_transform) << std::endl; + std::cout << 22 << (_desktop->d2w(Geom::Point(old_x0,old_y0)) * advert_transform) << std::endl; + std::cout << 23 << (_desktop->d2w(Geom::Point(_x0,_y0)) * advert_transform) << std::endl; + std::cout << 24 << (_desktop->d2w(Geom::Point(dx, dy)) * advert_transform) << std::endl; + std::cout << "24-1" << (_desktop->d2w(w1.midpoint()) * advert_transform) << std::endl; + std::cout << "24-2" << (_desktop->d2w(w2.midpoint()) * advert_transform) << std::endl; + std::cout << 25 << (_desktop->w2d(Geom::Point(old_x0,old_y0)) * advert_transform) << std::endl; + std::cout << 26 << (_desktop->w2d(Geom::Point(_x0,_y0)) * advert_transform) << std::endl; + std::cout << 27 << (_desktop->w2d(Geom::Point(dx, dy)) * advert_transform) << std::endl; + std::cout << "27-1" << (_desktop->w2d(w1.midpoint()) * advert_transform) << std::endl; + std::cout << "27-2" << (_desktop->w2d(w2.midpoint()) * advert_transform) << std::endl; + std::cout << 28 << (w.midpoint()) << std::endl; + */ + Geom::Point gap = Geom::Point(); + Geom::Point middle = w.midpoint(); + advert_transform = Geom::Translate(middle).inverse() * Geom::Translate(gap) * advert_transform.inverse() * Geom::Translate(middle); + Cairo::Matrix matrix; + matrix.xx = advert_transform[0]; + matrix.yx = advert_transform[1]; + matrix.xy = advert_transform[2]; + matrix.yy = advert_transform[3]; + matrix.x0 = advert_transform[4]; + matrix.y0 = advert_transform[5]; + cr->set_matrix(matrix); + // cr->translate(middle[Geom::X], middle[Geom::Y]); + // Paint background + cr->set_operator(Cairo::Operator::OPERATOR_SOURCE); + cr->set_source(_background); + cr->paint(); + //cr->translate(_allocation.get_width()/2, _allocation.get_height()/2); + cr->set_source(_backing_store, 0, 0); // ((prev_transform.expansionX() * -1 * _allocation.get_width()) - _allocation.get_width())/2, ((prev_transform.expansionY() * -1 * _allocation.get_height()) - _allocation.get_height())/2); + cr->paint(); + + //cr->translate(middle[Geom::X], middle[Geom::Y]); + + /* cr->rectangle(dx, dy, _allocation.get_width(), _allocation.get_height()); + cr->clip(); + cr->paint(); */ + //prev_transform = prev_transform.inverse(); + // Copy old background unshifted (reduces sensation of flicker while waiting for rendering newly exposed area). + /* cr->save(); + cr->set_operator(Cairo::Operator::OPERATOR_SOURCE); + cr->set_source(_backing_store, 0, 0); + Cairo::Matrix matrix; + matrix.xx = prev_transform[0]; + matrix.yx = prev_transform[1]; + matrix.xy = prev_transform[2]; + matrix.yy = prev_transform[3]; + matrix.x0 = prev_transform[4]; + matrix.y0 = prev_transform[5]; + cr->scale(2,2); + //cr->set_matrix(matrix); + cr->rectangle(dx, dy, _allocation.get_width(), _allocation.get_height()); + cr->clip(); + cr->paint(); + cr->restore(); */ + _backing_store = new_store; + /* std::filesystem::copy("./debug.png", "./debug2.png", std::filesystem::copy_options::update_existing); + cairo_surface_write_to_png (_backing_store->cobj(), "./debug.png"); */ + } + + + /* auto cr = Cairo::Context::create(_backing_store); + + + + + // Paint background + cr->save(); + Cairo::Matrix matrix; + matrix.xx = prev_transform[0]; + matrix.yx = prev_transform[1]; + matrix.xy = prev_transform[2]; + matrix.yy = prev_transform[3]; + matrix.x0 = prev_transform[4]; + matrix.y0 = prev_transform[5]; + cr->set_matrix(matrix); + cr->rectangle(_x0, _y0, _allocation.get_width(), _allocation.get_height()); cr->clip(); + cr->paint(); + cr->restore(); */ + queue_draw_area(_x0, _y0, _allocation.get_width(), _allocation.get_height()); + } redraw_all(); return; // Check if this is OK } diff --git a/src/ui/widget/canvas.h b/src/ui/widget/canvas.h index b484694b725d8aade7e3e0556f65b3e5bfcae874..97daf725e4e6156c2930c85f059187713cba6e33 100644 --- a/src/ui/widget/canvas.h +++ b/src/ui/widget/canvas.h @@ -68,7 +68,7 @@ public: void redraw_area(Geom::Rect& area); // Draw specified area during idle. void redraw_now(); // Draw areas needing update immediately. void request_update(); // Draw after updating canvas items. - void scroll_to(Geom::Point const &c, bool clear); + void scroll_to(Geom::Point const &c, bool clear, Geom::Affine advert_transform = Geom::identity()); void set_background_color(guint32 rgba); void set_background_checkerboard(guint32 rgba = 0xC4C4C4FF); @@ -183,6 +183,8 @@ private: // Geometry int _x0 = 0; ///< World coordinate of the leftmost pixels of window. int _y0 = 0; ///< World coordinate of the topmost pixels of window. + int _prev_x0 = 0; ///< Previous world coordinate of the leftmost pixels of window. + int _prev_y0 = 0; ///< Previos world coordinate of the topmost pixels of window. Geom::Point _window_origin; ///< World coordinate of the upper-leftmost pixel of window. Geom::Affine _affine; // Only used for canvas items at moment. bool _in_full_redraw = false;