From c853ffb5b2f75f5a889ed2e3ef89b818a736e87a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 13:50:49 +0200 Subject: Adding upstream version 1.3+ds. Signed-off-by: Daniel Baumann --- src/ui/widget/canvas/updaters.cpp | 235 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 src/ui/widget/canvas/updaters.cpp (limited to 'src/ui/widget/canvas/updaters.cpp') diff --git a/src/ui/widget/canvas/updaters.cpp b/src/ui/widget/canvas/updaters.cpp new file mode 100644 index 0000000..8441be0 --- /dev/null +++ b/src/ui/widget/canvas/updaters.cpp @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include "updaters.h" +#include "ui/util.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +class ResponsiveUpdater : public Updater +{ +public: + Strategy get_strategy() const override { return Strategy::Responsive; } + + void reset() override { clean_region = Cairo::Region::create(); } + void intersect (Geom::IntRect const &rect) override { clean_region->intersect(geom_to_cairo(rect)); } + void mark_dirty(Geom::IntRect const &rect) override { clean_region->subtract(geom_to_cairo(rect)); } + void mark_dirty(Cairo::RefPtr const ®) override { clean_region->subtract(reg); } + void mark_clean(Geom::IntRect const &rect) override { clean_region->do_union(geom_to_cairo(rect)); } + + Cairo::RefPtr get_next_clean_region() override { return clean_region; } + bool report_finished () override { return false; } + void next_frame () override {} +}; + +class FullRedrawUpdater : public ResponsiveUpdater +{ + // Whether we are currently in the middle of a redraw. + bool inprogress = false; + + // Contains a copy of the old clean region if damage events occurred during the current redraw, otherwise null. + Cairo::RefPtr old_clean_region; + +public: + Strategy get_strategy() const override { return Strategy::FullRedraw; } + + void reset() override + { + ResponsiveUpdater::reset(); + inprogress = false; + old_clean_region.clear(); + } + + void intersect(const Geom::IntRect &rect) override + { + ResponsiveUpdater::intersect(rect); + if (old_clean_region) old_clean_region->intersect(geom_to_cairo(rect)); + } + + void mark_dirty(Geom::IntRect const &rect) override + { + if (inprogress && !old_clean_region) old_clean_region = clean_region->copy(); + ResponsiveUpdater::mark_dirty(rect); + } + + void mark_dirty(const Cairo::RefPtr ®) override + { + if (inprogress && !old_clean_region) old_clean_region = clean_region->copy(); + ResponsiveUpdater::mark_dirty(reg); + } + + void mark_clean(const Geom::IntRect &rect) override + { + ResponsiveUpdater::mark_clean(rect); + if (old_clean_region) old_clean_region->do_union(geom_to_cairo(rect)); + } + + Cairo::RefPtr get_next_clean_region() override + { + inprogress = true; + if (!old_clean_region) { + return clean_region; + } else { + return old_clean_region; + } + } + + bool report_finished() override + { + assert(inprogress); + if (!old_clean_region) { + // Completed redraw without being damaged => finished. + inprogress = false; + return false; + } else { + // Completed redraw but damage events arrived => ask for another redraw, using the up-to-date clean region. + old_clean_region.clear(); + return true; + } + } +}; + +class MultiscaleUpdater : public ResponsiveUpdater +{ + // Whether we are currently in the middle of a redraw. + bool inprogress = false; + + // Whether damage events occurred during the current redraw. + bool activated = false; + + int counter; // A steadily incrementing counter from which the current scale is derived. + int scale; // The current scale to process updates at. + int elapsed; // How much time has been spent at the current scale. + std::vector> blocked; // The region blocked from being updated at each scale. + +public: + Strategy get_strategy() const override { return Strategy::Multiscale; } + + void reset() override + { + ResponsiveUpdater::reset(); + inprogress = activated = false; + } + + void intersect(const Geom::IntRect &rect) override + { + ResponsiveUpdater::intersect(rect); + if (activated) { + for (auto ® : blocked) { + reg->intersect(geom_to_cairo(rect)); + } + } + } + + void mark_dirty(Geom::IntRect const &rect) override + { + ResponsiveUpdater::mark_dirty(rect); + post_mark_dirty(); + } + + void mark_dirty(const Cairo::RefPtr ®) override + { + ResponsiveUpdater::mark_dirty(reg); + post_mark_dirty(); + } + + void post_mark_dirty() + { + if (inprogress && !activated) { + counter = scale = elapsed = 0; + blocked = { Cairo::Region::create() }; + activated = true; + } + } + + void mark_clean(const Geom::IntRect &rect) override + { + ResponsiveUpdater::mark_clean(rect); + if (activated) blocked[scale]->do_union(geom_to_cairo(rect)); + } + + Cairo::RefPtr get_next_clean_region() override + { + inprogress = true; + if (!activated) { + return clean_region; + } else { + auto result = clean_region->copy(); + result->do_union(blocked[scale]); + return result; + } + } + + bool report_finished() override + { + assert(inprogress); + if (!activated) { + // Completed redraw without damage => finished. + inprogress = false; + return false; + } else { + // Completed redraw but damage events arrived => begin updating any remaining damaged regions. + activated = false; + blocked.clear(); + return true; + } + } + + void next_frame() override + { + if (!activated) return; + + // Stay at the current scale for 2^scale frames. + elapsed++; + if (elapsed < (1 << scale)) return; + elapsed = 0; + + // Adjust the counter, which causes scale to hop around the values 0, 1, 2... spending half as much time at each subsequent scale. + counter++; + scale = 0; + for (int tmp = counter; tmp % 2 == 1; tmp /= 2) { + scale++; + } + + // Ensure sufficiently many blocked zones exist. + if (scale == blocked.size()) { + blocked.emplace_back(); + } + + // Recreate the current blocked zone as the union of the clean region and lower-scale blocked zones. + blocked[scale] = clean_region->copy(); + for (int i = 0; i < scale; i++) { + blocked[scale]->do_union(blocked[i]); + } + } +}; + +template<> std::unique_ptr Updater::create() {return std::make_unique();} +template<> std::unique_ptr Updater::create() {return std::make_unique();} +template<> std::unique_ptr Updater::create() {return std::make_unique();} + +std::unique_ptr Updater::create(Strategy strategy) +{ + switch (strategy) + { + case Strategy::Responsive: return create(); + case Strategy::FullRedraw: return create(); + case Strategy::Multiscale: return create(); + default: return nullptr; // Never triggered, but GCC errors out on build without. + } +} + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +/* + 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 : -- cgit v1.2.3