From dbfcab912dd40eceeba2a0875b38fe585784099b Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 12 May 2020 03:11:34 +0800 Subject: [PATCH] fix and improve animation(#335) --- include/nana/gui/animation.hpp | 14 ++- source/gui/animation.cpp | 186 ++++++++++++++++++++++++++++----- 2 files changed, 169 insertions(+), 31 deletions(-) diff --git a/include/nana/gui/animation.hpp b/include/nana/gui/animation.hpp index f0267a46..0f22fb30 100644 --- a/include/nana/gui/animation.hpp +++ b/include/nana/gui/animation.hpp @@ -1,7 +1,7 @@ /* * An Animation Implementation * Nana C++ Library(http://www.nanapro.org) - * Copyright(C) 2003-2018 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -29,13 +29,12 @@ namespace nana public: /// function which builds frames. using framebuilder = std::function; - - struct impl; public: frameset(); void push_back(paint::image); ///< Inserts frames at the end. void push_back(framebuilder fb, std::size_t length); ///< Inserts a framebuilder and the number of frames that it generates. private: + struct impl; std::shared_ptr impl_; }; /// Easy way to display an animation or create an animated GUI @@ -68,8 +67,17 @@ namespace nana void pause(); + /// Renders the animation at a fixed position void output(window wd, const nana::point& pos); + /// Renders the animation at a rectangle + /** + * If the size of rectangle is not equal to the size of frame, it stretches the frame for the size of rectangle. + * @param wd Output window. + * @param r Generator of the rectangle. The generator gets called every time rendering occurs. + */ + void output(window wd, std::function r); + void fps(std::size_t n); std::size_t fps() const; private: diff --git a/source/gui/animation.cpp b/source/gui/animation.cpp index ce213ee2..7b52b73d 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -40,6 +40,7 @@ namespace nana { drawing::diehard_t diehard{ nullptr }; std::vector points; + std::vector> areas; }; struct framebuilder @@ -184,7 +185,7 @@ namespace nana std::list frames; std::list::iterator this_frame; std::size_t pos_in_this_frame{ 0 }; - mutable bool good_frame_by_frmbuilder{ false }; //It indicates the state of frame whether is valid. + mutable bool good_frame_by_frmbuilder{ false }; //It indicates the state of frame. impl() : this_frame(frames.end()) @@ -200,52 +201,96 @@ namespace nana switch(frmobj.type) { case frame::kind::oneshot: - _m_render(outs, [&frmobj](paint::graphics& tar, const nana::point& pos) + _m_render(outs, frmobj.u.oneshot->size(), [&frmobj](paint::graphics& tar, const nana::rectangle& area) { - frmobj.u.oneshot->paste(tar, pos); + if(frmobj.u.oneshot->size() == area.dimension()) + frmobj.u.oneshot->paste(tar, area.position()); + else + frmobj.u.oneshot->stretch(rectangle{frmobj.u.oneshot->size()}, tar, area); }); break; case frame::kind::framebuilder: good_frame_by_frmbuilder = frmobj.u.frbuilder->frbuilder(pos_in_this_frame, framegraph, framegraph_dimension); if(good_frame_by_frmbuilder) { - nana::rectangle r(framegraph_dimension); - _m_render(outs, [&r, &framegraph](paint::graphics& tar, const nana::point& pos) mutable + _m_render(outs, framegraph_dimension, [framegraph_dimension, &framegraph](paint::graphics& tar, const rectangle& area) mutable { - r.x = pos.x; - r.y = pos.y; - tar.bitblt(r, framegraph); + if(framegraph_dimension == area.dimension()) + tar.bitblt(area, framegraph); + else + framegraph.stretch(tar, area); }); } break; } } - //Render a frame on a specified window graph - void render_this(paint::graphics& graph, const nana::point& pos, paint::graphics& framegraph, nana::size& framegraph_dimension, bool rebuild_frame) const + //Render a frame on a specified window graph. If this frame is created by framebuilder, it doesn't rebuild the frame. + void render_this(paint::graphics& graph, const rectangle& area, paint::graphics& framegraph, nana::size& framegraph_dimension) const { - if(this_frame == frames.end()) - return; + // If the frame is EOF, then renders the last frame + std::list::const_iterator pf = this_frame; + if (pf == frames.end()) + { + if (frames.size()) + { + pf = frames.begin(); + std::advance(pf, frames.size() - 1); + } + else + return; + } - frame & frmobj = *this_frame; - switch(frmobj.type) + const frame & frmobj = *pf; + switch (frmobj.type) { case frame::kind::oneshot: - frmobj.u.oneshot->paste(graph, pos); + if (frmobj.u.oneshot->size() == area.dimension()) + frmobj.u.oneshot->paste(graph, area.position()); + else + frmobj.u.oneshot->stretch(rectangle{frmobj.u.oneshot->size()}, graph, area); break; case frame::kind::framebuilder: - if(rebuild_frame) - good_frame_by_frmbuilder = frmobj.u.frbuilder->frbuilder(pos_in_this_frame, framegraph, framegraph_dimension); - if(good_frame_by_frmbuilder) { - nana::rectangle r(pos, framegraph_dimension); - graph.bitblt(r, framegraph); + if (framegraph_dimension == area.dimension()) + graph.bitblt(area, framegraph); + else + framegraph.stretch(graph, area); } break; } } + nana::size this_frame_size(const nana::size& framegraph_dimension) const + { + // If the frame is EOF, then renders the last frame + std::list::const_iterator pf = this_frame; + if (pf == frames.end()) + { + if (frames.size()) + { + pf = frames.begin(); + std::advance(pf, frames.size() - 1); + } + else + return{}; + } + + const frame & frmobj = *pf; + switch (frmobj.type) + { + case frame::kind::oneshot: + return frmobj.u.oneshot->size(); + case frame::kind::framebuilder: + if (good_frame_by_frmbuilder) + return framegraph_dimension; + break; + } + + return{}; + } + bool eof() const { return (frames.end() == this_frame); @@ -285,8 +330,10 @@ namespace nana } private: template - void _m_render(std::map& outs, Renderer renderer) const + void _m_render(std::map& outs, const nana::size& frame_size, Renderer renderer) const { + nana::rectangle frame_area{frame_size}; + for(auto & tar: outs) { auto graph = API::dev::window_graphics(tar.first); @@ -294,7 +341,13 @@ namespace nana continue; for(auto & outp : tar.second.points) - renderer(*graph, outp); + { + frame_area.position(outp); + renderer(*graph, frame_area); + } + + for(auto& area_fn: tar.second.areas) + renderer(*graph, area_fn()); API::update_window(tar.first); } @@ -400,19 +453,32 @@ namespace nana } } - void render_this_specifically(paint::graphics& graph, const nana::point& pos) + // Renders current frame to a specified graphics + void render_this_frame(paint::graphics& graph, const rectangle& area) { if(state.this_frameset != framesets.end()) - state.this_frameset->impl_->render_this(graph, pos, framegraph, framegraph_dimension, false); + state.this_frameset->impl_->render_this(graph, area, framegraph, framegraph_dimension); } + // Renders current from to all outputs graphics void render_this_frame() { if(state.this_frameset != framesets.end()) state.this_frameset->impl_->render_this(outputs, framegraph, framegraph_dimension); } - bool move_to_next() + nana::size this_frame_size() const + { + if (state.this_frameset != framesets.end()) + { + return state.this_frameset->impl_->this_frame_size(framegraph_dimension); + } + return{}; + } + + + + bool next_frame() { if(state.this_frameset != framesets.end()) { @@ -465,10 +531,11 @@ namespace nana thr->thread = std::make_shared([thr]() { nana::system::timepiece tmpiece; + tmpiece.start(); + while (true) { thr->active = 0; - tmpiece.start(); { //acquire the isg lock first to avoid deadlock that occured by an event hander which operates the animation object. @@ -481,7 +548,8 @@ namespace nana continue; ani->render_this_frame(); - if (false == ani->move_to_next()) +#if 0 //deprecated + if (false == ani->next_frame()) { if (ani->looped) { @@ -491,9 +559,11 @@ namespace nana } else ++thr->active; +#endif } } +#if 0 //deprecated if (thr->active) { thr->performance_parameter = tmpiece.calc(); @@ -508,6 +578,47 @@ namespace nana if (0 == thr->active) thr->condvar.wait(lock); } +#else + thr->performance_parameter = tmpiece.calc(); + if (thr->performance_parameter < thr->interval) + nana::system::sleep(static_cast(thr->interval - thr->performance_parameter)); + + //Restart timing this frame + tmpiece.start(); + + // Move to next frame + { + std::lock_guardmutex)> lock(thr->mutex); + for (auto ani : thr->animations) + { + if (ani->paused) + continue; + + if (false == ani->next_frame()) + { + if (ani->looped) + { + ani->reset(); + ++thr->active; + } + } + else + ++thr->active; + } + } + + if (0 == thr->active) + { + //There isn't an active frame, then let the thread + //wait for a signal for an active animation + std::unique_lock lock(thr->mutex); + if (0 == thr->active) + thr->condvar.wait(lock); + + //Restart timing for this frame when this thread is waking up. + tmpiece.start(); + } +#endif } }); @@ -668,7 +779,7 @@ namespace nana { drawing dw(wd); output.diehard = dw.draw_diehard([this, pos](paint::graphics& tar){ - impl_->render_this_specifically(tar, pos); + impl_->render_this_frame(tar, rectangle{ pos, impl_->this_frame_size() }); }); API::events(wd).destroy.connect([this](const arg_destroy& arg){ @@ -679,6 +790,25 @@ namespace nana output.points.push_back(pos); } + void animation::output(window wd, std::function r) + { + auto & output = impl_->outputs[wd]; + + if(nullptr == output.diehard) + { + drawing dw(wd); + output.diehard = dw.draw_diehard([this, r](paint::graphics& tar){ + impl_->render_this_frame(tar, r()); + }); + + API::events(wd).destroy.connect([this](const arg_destroy& arg){ + std::lock_guardthr_variable->mutex)> lock(impl_->thr_variable->mutex); + impl_->outputs.erase(arg.window_handle); + }); + } + output.areas.push_back(r); + } + void animation::fps(std::size_t n) { if (n == impl_->fps)