Merge branch 'hotfix-1.7.3' into develop

This commit is contained in:
Jinhao 2020-05-15 17:21:17 +08:00
commit 7f7f7ea0a9
3 changed files with 280 additions and 81 deletions

View File

@ -1,7 +1,7 @@
/* /*
* An Animation Implementation * An Animation Implementation
* Nana C++ Library(http://www.nanapro.org) * 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. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -29,13 +29,12 @@ namespace nana
public: public:
/// function which builds frames. /// function which builds frames.
using framebuilder = std::function<bool(std::size_t pos, paint::graphics&, nana::size&)>; using framebuilder = std::function<bool(std::size_t pos, paint::graphics&, nana::size&)>;
struct impl;
public: public:
frameset(); frameset();
void push_back(paint::image); ///< Inserts frames at the end. 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. void push_back(framebuilder fb, std::size_t length); ///< Inserts a framebuilder and the number of frames that it generates.
private: private:
struct impl;
std::shared_ptr<impl> impl_; std::shared_ptr<impl> impl_;
}; };
/// Easy way to display an animation or create an animated GUI /// Easy way to display an animation or create an animated GUI
@ -68,8 +67,17 @@ namespace nana
void pause(); void pause();
/// Renders the animation at a fixed position
void output(window wd, const nana::point& pos); 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<nana::rectangle()> r);
void fps(std::size_t n); void fps(std::size_t n);
std::size_t fps() const; std::size_t fps() const;
private: private:

View File

@ -1,7 +1,7 @@
/* /*
* An Animation Implementation * An Animation Implementation
* Nana C++ Library(http://www.nanapro.org) * 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. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -40,6 +40,7 @@ namespace nana
{ {
drawing::diehard_t diehard{ nullptr }; drawing::diehard_t diehard{ nullptr };
std::vector<nana::point> points; std::vector<nana::point> points;
std::vector<std::function<rectangle()>> areas;
}; };
struct framebuilder struct framebuilder
@ -184,7 +185,7 @@ namespace nana
std::list<frame> frames; std::list<frame> frames;
std::list<frame>::iterator this_frame; std::list<frame>::iterator this_frame;
std::size_t pos_in_this_frame{ 0 }; 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() impl()
: this_frame(frames.end()) : this_frame(frames.end())
@ -200,52 +201,96 @@ namespace nana
switch(frmobj.type) switch(frmobj.type)
{ {
case frame::kind::oneshot: 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; break;
case frame::kind::framebuilder: case frame::kind::framebuilder:
good_frame_by_frmbuilder = frmobj.u.frbuilder->frbuilder(pos_in_this_frame, framegraph, framegraph_dimension); good_frame_by_frmbuilder = frmobj.u.frbuilder->frbuilder(pos_in_this_frame, framegraph, framegraph_dimension);
if(good_frame_by_frmbuilder) if(good_frame_by_frmbuilder)
{ {
nana::rectangle r(framegraph_dimension); _m_render(outs, framegraph_dimension, [framegraph_dimension, &framegraph](paint::graphics& tar, const rectangle& area) mutable
_m_render(outs, [&r, &framegraph](paint::graphics& tar, const nana::point& pos) mutable
{ {
r.x = pos.x; if(framegraph_dimension == area.dimension())
r.y = pos.y; tar.bitblt(area, framegraph);
tar.bitblt(r, framegraph); else
framegraph.stretch(tar, area);
}); });
} }
break; break;
} }
} }
//Render a frame on a specified window graph //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 nana::point& pos, paint::graphics& framegraph, nana::size& framegraph_dimension, bool rebuild_frame) const void render_this(paint::graphics& graph, const rectangle& area, paint::graphics& framegraph, nana::size& framegraph_dimension) const
{ {
if(this_frame == frames.end()) // If the frame is EOF, then renders the last frame
return; 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; const frame & frmobj = *pf;
switch(frmobj.type) switch (frmobj.type)
{ {
case frame::kind::oneshot: 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; break;
case frame::kind::framebuilder: 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) if(good_frame_by_frmbuilder)
{ {
nana::rectangle r(pos, framegraph_dimension); if (framegraph_dimension == area.dimension())
graph.bitblt(r, framegraph); graph.bitblt(area, framegraph);
else
framegraph.stretch(graph, area);
} }
break; 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 bool eof() const
{ {
return (frames.end() == this_frame); return (frames.end() == this_frame);
@ -285,8 +330,10 @@ namespace nana
} }
private: private:
template<typename Renderer> 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) for(auto & tar: outs)
{ {
auto graph = API::dev::window_graphics(tar.first); auto graph = API::dev::window_graphics(tar.first);
@ -294,7 +341,13 @@ namespace nana
continue; continue;
for(auto & outp : tar.second.points) 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); API::update_window(tar.first);
} }
@ -339,6 +392,8 @@ namespace nana
double performance_parameter; double performance_parameter;
}; };
~performance_manager();
void insert(impl* p); void insert(impl* p);
void set_fps(impl*, std::size_t new_fps); void set_fps(impl*, std::size_t new_fps);
void close(impl* p); 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()) 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() void render_this_frame()
{ {
if(state.this_frameset != framesets.end()) if(state.this_frameset != framesets.end())
state.this_frameset->impl_->render_this(outputs, framegraph, framegraph_dimension); 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()) if(state.this_frameset != framesets.end())
{ {
@ -429,9 +497,28 @@ namespace nana
if(state.this_frameset != framesets.end()) if(state.this_frameset != framesets.end())
state.this_frameset->impl_->reset(); 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 };//end struct animation::impl
//class animation::performance_manager //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) void animation::performance_manager::insert(impl* p)
{ {
std::lock_guard<decltype(mutex_)> lock(mutex_); std::lock_guard<decltype(mutex_)> lock(mutex_);
@ -459,12 +546,16 @@ namespace nana
{ {
auto thr = pthr; auto thr = pthr;
nana::system::timepiece tmpiece; nana::system::timepiece tmpiece;
tmpiece.start();
while (true) while (true)
{ {
thr->active = 0; 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); std::lock_guard<decltype(thr->mutex)> lock(thr->mutex);
for (auto ani : thr->animations) for (auto ani : thr->animations)
{ {
@ -472,7 +563,25 @@ namespace nana
continue; continue;
ani->render_this_frame(); 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) if (ani->looped)
{ {
@ -485,19 +594,25 @@ namespace nana
} }
} }
if (thr->active) if (0 == 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
{ {
//There isn't an active frame, then let the thread //There isn't an active frame, then let the thread
//wait for a signal for an active animation //wait for a signal for an active animation
std::unique_lock<std::mutex> lock(thr->mutex); 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) if (0 == thr->active)
thr->condvar.wait(lock); 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; return;
} }
std::lock_guard<decltype(thr->mutex)> privlock(thr->mutex); {
auto u = std::find(thr->animations.begin(), thr->animations.end(), p); // the mutex of thread variable may be acquired by insert()
if (u != thr->animations.end()) std::lock_guard<decltype(thr->mutex)> privlock(thr->mutex);
thr->animations.erase(u); auto u = std::find(thr->animations.begin(), thr->animations.end(), p);
if (u != thr->animations.end())
thr->animations.erase(u);
}
p->thr_variable = nullptr; p->thr_variable = nullptr;
insert(p); insert(p);
@ -544,11 +662,30 @@ namespace nana
return; return;
auto thr = *i; 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()) std::lock_guard<decltype(thr->mutex)> privlock(thr->mutex);
thr->animations.erase(u);
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 bool animation::performance_manager::empty() const
@ -628,6 +765,9 @@ namespace nana
std::unique_lock<std::mutex> lock(impl_->thr_variable->mutex); std::unique_lock<std::mutex> lock(impl_->thr_variable->mutex);
if(0 == impl_->thr_variable->active) if(0 == impl_->thr_variable->active)
{ {
if (impl_->eof())
impl_->reset();
impl_->thr_variable->active = 1; impl_->thr_variable->active = 1;
impl_->thr_variable->condvar.notify_one(); impl_->thr_variable->condvar.notify_one();
} }
@ -646,7 +786,7 @@ namespace nana
{ {
drawing dw(wd); drawing dw(wd);
output.diehard = dw.draw_diehard([this, pos](paint::graphics& tar){ 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){ API::events(wd).destroy.connect([this](const arg_destroy& arg){
@ -657,6 +797,25 @@ namespace nana
output.points.push_back(pos); 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) void animation::fps(std::size_t n)
{ {
if (n == impl_->fps) if (n == impl_->fps)

View File

@ -1,7 +1,7 @@
/* /*
* Pixel Buffer Implementation * Pixel Buffer Implementation
* Nana C++ Library(http://www.nanapro.org) * Nana C++ Library(http://www.nanapro.org)
* Copyright(C) 2003-2017 Jinhao(cnjinhao@hotmail.com) * Copyright(C) 2003-2020 Jinhao(cnjinhao@hotmail.com)
* *
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
@ -190,41 +190,42 @@ namespace nana{ namespace paint
if (!raw_pixel_buffer) if (!raw_pixel_buffer)
return; return;
if ((32 == bits_per_pixel) && (pixel_size.width == width) && (pixel_size.height == height) && (this->bytes_per_line == bytes_per_line) && is_negative)
{
memcpy(raw_pixel_buffer, rawbits, (bytes_per_line * pixel_size.height));
return;
}
if(pixel_size.width < width)
width = pixel_size.width;
if(pixel_size.height < height)
height = pixel_size.height;
auto rawptr = raw_pixel_buffer; auto rawptr = raw_pixel_buffer;
if(32 == bits_per_pixel) if(32 == bits_per_pixel)
{ {
if((pixel_size.width == width) && (pixel_size.height == height) && is_negative) auto d = rawptr;
const unsigned char* s;
int src_line_bytes;
if (is_negative)
{ {
memcpy(rawptr, rawbits, (pixel_size.width * pixel_size.height) * 4); s = rawbits;
src_line_bytes = -static_cast<int>(bytes_per_line);
} }
else else
{ {
std::size_t line_bytes = (pixel_size.width < width ? pixel_size.width : width) * sizeof(pixel_color_t); s = rawbits + bytes_per_line * (height - 1);
src_line_bytes = static_cast<int>(bytes_per_line);
}
if(pixel_size.height < height) for(std::size_t i = 0; i < height; ++i)
height = pixel_size.height; {
memcpy(d, s, this->bytes_per_line);
auto d = rawptr; d += pixel_size.width;
const unsigned char* s; s -= src_line_bytes;
int src_line_bytes;
if (is_negative)
{
s = rawbits;
src_line_bytes = -static_cast<int>(bytes_per_line);
}
else
{
s = rawbits + bytes_per_line * (height - 1);
src_line_bytes = static_cast<int>(bytes_per_line);
}
for(std::size_t i = 0; i < height; ++i)
{
memcpy(d, s, line_bytes);
d += pixel_size.width;
s -= src_line_bytes;
}
} }
} }
else if(24 == bits_per_pixel) else if(24 == bits_per_pixel)
@ -269,12 +270,6 @@ namespace nana{ namespace paint
} }
else if(16 == bits_per_pixel) else if(16 == bits_per_pixel)
{ {
if(pixel_size.width < width)
width = pixel_size.width;
if(pixel_size.height < height)
height = pixel_size.height;
unsigned char rgb_table[32]; unsigned char rgb_table[32];
for(std::size_t i =0; i < 32; ++i) for(std::size_t i =0; i < 32; ++i)
rgb_table[i] = static_cast<unsigned char>(i * 255 / 31); rgb_table[i] = static_cast<unsigned char>(i * 255 / 31);
@ -310,6 +305,32 @@ namespace nana{ namespace paint
rawbits -= src_bytes_per_line; rawbits -= src_bytes_per_line;
} }
} }
else if(8 == bits_per_pixel)
{
int src_bytes_per_line;
if(!is_negative)
{
rawbits += bytes_per_line * (height - 1);
src_bytes_per_line = -static_cast<int>(bytes_per_line);
}
else
src_bytes_per_line = static_cast<int>(bytes_per_line);
for(std::size_t top = 0; top < height; ++top)
{
auto dst = rawptr;
for(auto p = rawbits, end = rawbits + width; p < end; ++p)
{
dst->element.red = *p;
dst->element.green = *p;
dst->element.blue = *p;
++dst;
}
rawbits += src_bytes_per_line;
rawptr += this->bytes_per_line;
}
}
} }
#if defined(NANA_X11) #if defined(NANA_X11)
@ -691,6 +712,17 @@ namespace nana{ namespace paint
++px; ++px;
} }
} }
else if(8 == bits_per_pixel)
{
//Grayscale
for (auto p = row_ptr, end = row_ptr + px_count; p != end; ++p)
{
p->element.red = *buffer;
p->element.green = *buffer;
p->element.blue = *buffer;
++buffer;
}
}
} }