From cf71da94c4c77330e523ab7d21b80a13335b575c Mon Sep 17 00:00:00 2001 From: Jinhao Date: Thu, 30 Apr 2020 04:16:35 +0800 Subject: [PATCH 1/6] fix issue that grayscale jpeg can't be shwon --- source/paint/pixel_buffer.cpp | 100 ++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/source/paint/pixel_buffer.cpp b/source/paint/pixel_buffer.cpp index 304bbc33..6f1fc9bd 100644 --- a/source/paint/pixel_buffer.cpp +++ b/source/paint/pixel_buffer.cpp @@ -1,7 +1,7 @@ /* * Pixel Buffer Implementation * 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. * (See accompanying file LICENSE_1_0.txt or copy at @@ -190,41 +190,42 @@ namespace nana{ namespace paint if (!raw_pixel_buffer) 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; 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(bytes_per_line); } 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(bytes_per_line); + } - if(pixel_size.height < height) - height = pixel_size.height; - - auto d = rawptr; - const unsigned char* s; - int src_line_bytes; - - if (is_negative) - { - s = rawbits; - src_line_bytes = -static_cast(bytes_per_line); - } - else - { - s = rawbits + bytes_per_line * (height - 1); - src_line_bytes = static_cast(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; - } + for(std::size_t i = 0; i < height; ++i) + { + memcpy(d, s, this->bytes_per_line); + d += pixel_size.width; + s -= src_line_bytes; } } else if(24 == bits_per_pixel) @@ -269,12 +270,6 @@ namespace nana{ namespace paint } 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]; for(std::size_t i =0; i < 32; ++i) rgb_table[i] = static_cast(i * 255 / 31); @@ -310,6 +305,32 @@ namespace nana{ namespace paint 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(bytes_per_line); + } + else + src_bytes_per_line = static_cast(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) @@ -691,6 +712,17 @@ namespace nana{ namespace paint ++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; + } + } } From baa64a1461bbfafd947d338e90d9202987150271 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 8 May 2020 09:19:57 +0800 Subject: [PATCH 2/6] fix animation deadlock issues(#530) --- source/gui/animation.cpp | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/source/gui/animation.cpp b/source/gui/animation.cpp index f897735f..ce213ee2 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -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 @@ -429,6 +429,14 @@ 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 @@ -463,6 +471,9 @@ namespace nana 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_guardmutex)> lock(thr->mutex); for (auto ani : thr->animations) { @@ -525,10 +536,13 @@ namespace nana return; } - std::lock_guardmutex)> 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_guardmutex)> 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); @@ -633,6 +647,9 @@ namespace nana std::unique_lock 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(); } From dbfcab912dd40eceeba2a0875b38fe585784099b Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 12 May 2020 03:11:34 +0800 Subject: [PATCH 3/6] 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) From d4453e953552fe4339f4d04e02eb646ce57b7850 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Tue, 12 May 2020 03:47:55 +0800 Subject: [PATCH 4/6] remove deprecated code --- source/gui/animation.cpp | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/source/gui/animation.cpp b/source/gui/animation.cpp index 7b52b73d..b0b2598c 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -548,37 +548,9 @@ namespace nana continue; ani->render_this_frame(); -#if 0 //deprecated - if (false == ani->next_frame()) - { - if (ani->looped) - { - ani->reset(); - ++thr->active; - } - } - else - ++thr->active; -#endif } } -#if 0 //deprecated - if (thr->active) - { - thr->performance_parameter = tmpiece.calc(); - if (thr->performance_parameter < thr->interval) - nana::system::sleep(static_cast(thr->interval - thr->performance_parameter)); - } - else - { - //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); - } -#else thr->performance_parameter = tmpiece.calc(); if (thr->performance_parameter < thr->interval) nana::system::sleep(static_cast(thr->interval - thr->performance_parameter)); @@ -618,7 +590,6 @@ namespace nana //Restart timing for this frame when this thread is waking up. tmpiece.start(); } -#endif } }); From afee7d1ed6cf721e1c920054fb80ff8e9c66d931 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 15 May 2020 02:37:01 +0800 Subject: [PATCH 5/6] fix memory leak issues of animation destruction --- source/gui/animation.cpp | 47 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/source/gui/animation.cpp b/source/gui/animation.cpp index b0b2598c..c57984d1 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -392,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); @@ -506,6 +508,17 @@ namespace nana };//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 lock(mutex_); @@ -584,9 +597,18 @@ namespace nana //There isn't an active frame, then let the thread //wait for a signal for an active animation std::unique_lock 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(); } @@ -638,11 +660,28 @@ namespace nana return; auto thr = *i; - std::lock_guardmutex)> 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_guardmutex)> 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; + } + + threads_.erase(i); + if (thr && thr->thread && thr->thread->joinable()) + thr->thread->join(); + + delete thr; } bool animation::performance_manager::empty() const From f2d74fbc860b7b7459bbe9189e767e0c5c2a4daf Mon Sep 17 00:00:00 2001 From: Jinhao Date: Fri, 15 May 2020 03:17:43 +0800 Subject: [PATCH 6/6] small fix --- source/gui/animation.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/gui/animation.cpp b/source/gui/animation.cpp index c57984d1..c118ee51 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -677,6 +677,8 @@ namespace nana thr = nullptr; } + p->thr_variable = nullptr; + threads_.erase(i); if (thr && thr->thread && thr->thread->joinable()) thr->thread->join();