Merge branch 'hotfix-1.7.3' into develop
This commit is contained in:
@@ -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
|
||||
@@ -40,6 +40,7 @@ namespace nana
|
||||
{
|
||||
drawing::diehard_t diehard{ nullptr };
|
||||
std::vector<nana::point> points;
|
||||
std::vector<std::function<rectangle()>> areas;
|
||||
};
|
||||
|
||||
struct framebuilder
|
||||
@@ -184,7 +185,7 @@ namespace nana
|
||||
std::list<frame> frames;
|
||||
std::list<frame>::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<nana::frame>::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<nana::frame>::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<typename Renderer>
|
||||
void _m_render(std::map<window, output_t>& outs, Renderer renderer) const
|
||||
void _m_render(std::map<window, output_t>& 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);
|
||||
}
|
||||
@@ -339,6 +392,8 @@ namespace nana
|
||||
double performance_parameter;
|
||||
};
|
||||
|
||||
~performance_manager();
|
||||
|
||||
void insert(impl* p);
|
||||
void set_fps(impl*, std::size_t new_fps);
|
||||
void close(impl* p);
|
||||
@@ -400,19 +455,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())
|
||||
{
|
||||
@@ -429,9 +497,28 @@ namespace nana
|
||||
if(state.this_frameset != framesets.end())
|
||||
state.this_frameset->impl_->reset();
|
||||
}
|
||||
|
||||
bool eof() const
|
||||
{
|
||||
if(state.this_frameset != framesets.end())
|
||||
return state.this_frameset->impl_->eof();
|
||||
|
||||
return true;
|
||||
}
|
||||
};//end struct animation::impl
|
||||
|
||||
//class animation::performance_manager
|
||||
animation::performance_manager::~performance_manager()
|
||||
{
|
||||
for (auto thr : threads_)
|
||||
{
|
||||
if (thr->thread && thr->thread->joinable())
|
||||
thr->thread->join();
|
||||
|
||||
delete thr;
|
||||
}
|
||||
}
|
||||
|
||||
void animation::performance_manager::insert(impl* p)
|
||||
{
|
||||
std::lock_guard<decltype(mutex_)> lock(mutex_);
|
||||
@@ -459,12 +546,16 @@ namespace nana
|
||||
{
|
||||
auto thr = pthr;
|
||||
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.
|
||||
nana::internal_scope_guard isglock;
|
||||
|
||||
std::lock_guard<decltype(thr->mutex)> lock(thr->mutex);
|
||||
for (auto ani : thr->animations)
|
||||
{
|
||||
@@ -472,7 +563,25 @@ namespace nana
|
||||
continue;
|
||||
|
||||
ani->render_this_frame();
|
||||
if (false == ani->move_to_next())
|
||||
}
|
||||
}
|
||||
|
||||
thr->performance_parameter = tmpiece.calc();
|
||||
if (thr->performance_parameter < thr->interval)
|
||||
nana::system::sleep(static_cast<unsigned>(thr->interval - thr->performance_parameter));
|
||||
|
||||
//Restart timing this frame
|
||||
tmpiece.start();
|
||||
|
||||
// Move to next frame
|
||||
{
|
||||
std::lock_guard<decltype(thr->mutex)> lock(thr->mutex);
|
||||
for (auto ani : thr->animations)
|
||||
{
|
||||
if (ani->paused)
|
||||
continue;
|
||||
|
||||
if (false == ani->next_frame())
|
||||
{
|
||||
if (ani->looped)
|
||||
{
|
||||
@@ -485,19 +594,25 @@ namespace nana
|
||||
}
|
||||
}
|
||||
|
||||
if (thr->active)
|
||||
{
|
||||
thr->performance_parameter = tmpiece.calc();
|
||||
if (thr->performance_parameter < thr->interval)
|
||||
nana::system::sleep(static_cast<unsigned>(thr->interval - thr->performance_parameter));
|
||||
}
|
||||
else
|
||||
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<std::mutex> lock(thr->mutex);
|
||||
|
||||
//Exit the thread if there is not an animation
|
||||
if (thr->animations.empty())
|
||||
return;
|
||||
|
||||
if (0 == thr->active)
|
||||
thr->condvar.wait(lock);
|
||||
|
||||
//Exit the thread if there is not an animation
|
||||
if (thr->animations.empty())
|
||||
return;
|
||||
|
||||
//Restart timing for this frame when this thread is waking up.
|
||||
tmpiece.start();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -527,10 +642,13 @@ namespace nana
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<decltype(thr->mutex)> privlock(thr->mutex);
|
||||
auto u = std::find(thr->animations.begin(), thr->animations.end(), p);
|
||||
if (u != thr->animations.end())
|
||||
thr->animations.erase(u);
|
||||
{
|
||||
// the mutex of thread variable may be acquired by insert()
|
||||
std::lock_guard<decltype(thr->mutex)> privlock(thr->mutex);
|
||||
auto u = std::find(thr->animations.begin(), thr->animations.end(), p);
|
||||
if (u != thr->animations.end())
|
||||
thr->animations.erase(u);
|
||||
}
|
||||
|
||||
p->thr_variable = nullptr;
|
||||
insert(p);
|
||||
@@ -544,11 +662,30 @@ namespace nana
|
||||
return;
|
||||
|
||||
auto thr = *i;
|
||||
std::lock_guard<decltype(thr->mutex)> privlock(thr->mutex);
|
||||
|
||||
auto u = std::find(thr->animations.begin(), thr->animations.end(), p);
|
||||
if(u != thr->animations.end())
|
||||
thr->animations.erase(u);
|
||||
{
|
||||
std::lock_guard<decltype(thr->mutex)> privlock(thr->mutex);
|
||||
|
||||
auto u = std::find(thr->animations.begin(), thr->animations.end(), p);
|
||||
if (u != thr->animations.end())
|
||||
thr->animations.erase(u);
|
||||
|
||||
//If there is not an animation in the thread, wake up the thread to exit.
|
||||
//If there is an animation in the thread, set the thr pointer to nullptr to
|
||||
//avoid exiting the thread
|
||||
if (thr->animations.empty())
|
||||
thr->condvar.notify_one();
|
||||
else
|
||||
thr = nullptr;
|
||||
}
|
||||
|
||||
p->thr_variable = nullptr;
|
||||
|
||||
threads_.erase(i);
|
||||
if (thr && thr->thread && thr->thread->joinable())
|
||||
thr->thread->join();
|
||||
|
||||
delete thr;
|
||||
}
|
||||
|
||||
bool animation::performance_manager::empty() const
|
||||
@@ -628,6 +765,9 @@ namespace nana
|
||||
std::unique_lock<std::mutex> lock(impl_->thr_variable->mutex);
|
||||
if(0 == impl_->thr_variable->active)
|
||||
{
|
||||
if (impl_->eof())
|
||||
impl_->reset();
|
||||
|
||||
impl_->thr_variable->active = 1;
|
||||
impl_->thr_variable->condvar.notify_one();
|
||||
}
|
||||
@@ -646,7 +786,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){
|
||||
@@ -657,6 +797,25 @@ namespace nana
|
||||
output.points.push_back(pos);
|
||||
}
|
||||
|
||||
void animation::output(window wd, std::function<nana::rectangle()> 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_guard<decltype(impl_->thr_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)
|
||||
|
||||
Reference in New Issue
Block a user