From 3c8ee33ae8b6a3894666b7fe672aa8520f6b5e35 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Wed, 27 Jan 2016 01:14:28 +0800 Subject: [PATCH] open png/jpg image with memory buffer --- include/nana/paint/image.hpp | 12 +- include/nana/pat/cloneable.hpp | 16 +- include/nana/threads/pool.hpp | 2 +- source/gui/animation.cpp | 3 +- source/paint/detail/image_jpeg.hpp | 63 ++++--- source/paint/detail/image_png.hpp | 257 ++++++++++++++++++----------- source/paint/image.cpp | 53 +++++- source/threads/pool.cpp | 24 +-- 8 files changed, 278 insertions(+), 152 deletions(-) diff --git a/include/nana/paint/image.hpp b/include/nana/paint/image.hpp index 1a02fc30..5820ac33 100644 --- a/include/nana/paint/image.hpp +++ b/include/nana/paint/image.hpp @@ -28,7 +28,7 @@ namespace paint public: class image_impl_interface; - image(); + image() noexcept; image(const image&); image(image&&); explicit image(const ::std::string& file); @@ -41,13 +41,13 @@ namespace paint bool open(const ::std::wstring& file); /// Opens an icon from a specified buffer - bool open_icon(const void* data, std::size_t bytes); - bool empty() const; + bool open(const void* data, std::size_t bytes); + bool empty() const noexcept; operator unspecified_bool_t() const; - void close(); + void close() noexcept; - bool alpha() const; - nana::size size() const; + bool alpha() const noexcept; + nana::size size() const noexcept; void paste(graphics& dst, const point& p_dst) const; void paste(const nana::rectangle& r_src, graphics& dst, const point& p_dst) const;///< Paste the area of picture specified by r_src into the destination graphics specified by dst at position p_dst. void stretch(const nana::rectangle& r_src, graphics& dst, const nana::rectangle& r_dst) const;/// #include #include #include + namespace nana{ namespace pat{ namespace detail @@ -99,9 +101,9 @@ namespace nana{ namespace pat{ : public std::enable_if<(!std::is_base_of::type>::value) && std::is_base_of::type>::value, int> {}; public: - cloneable() = default; + cloneable() noexcept = default; - cloneable(std::nullptr_t){} + cloneable(std::nullptr_t) noexcept{} template::type* = nullptr> cloneable(T&& t) @@ -166,22 +168,22 @@ namespace nana{ namespace pat{ return *fast_ptr_; } - const_base_ref operator*() const + const_base_ref operator*() const noexcept { return *fast_ptr_; } - base_t * operator->() + base_t * operator->() noexcept { return fast_ptr_; } - const_base_ptr operator->() const + const_base_ptr operator->() const noexcept { return fast_ptr_; } - base_t * get() const + base_t * get() const noexcept { return fast_ptr_; } @@ -192,7 +194,7 @@ namespace nana{ namespace pat{ cwrapper_.reset(); } - operator operator_bool_t() const volatile + operator operator_bool_t() const volatile noexcept { return (fast_ptr_ ? &inner_bool::true_stand : nullptr); } diff --git a/include/nana/threads/pool.hpp b/include/nana/threads/pool.hpp index 410a4b01..16edac1f 100644 --- a/include/nana/threads/pool.hpp +++ b/include/nana/threads/pool.hpp @@ -1,6 +1,6 @@ /* * A Thread Pool Implementation - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at diff --git a/source/gui/animation.cpp b/source/gui/animation.cpp index a82702e6..a6047b4e 100644 --- a/source/gui/animation.cpp +++ b/source/gui/animation.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #if defined(STD_THREAD_NOT_SUPPORTED) #include @@ -350,7 +351,7 @@ namespace nana struct animation::impl { bool looped{false}; - volatile bool paused{true}; + std::atomic paused{true}; std::size_t fps; std::list framesets; diff --git a/source/paint/detail/image_jpeg.hpp b/source/paint/detail/image_jpeg.hpp index 9d8e0b2e..2ae4b953 100644 --- a/source/paint/detail/image_jpeg.hpp +++ b/source/paint/detail/image_jpeg.hpp @@ -25,6 +25,27 @@ namespace nana struct ::jpeg_error_mgr pub; std::jmp_buf setjmp_buf; }; + + void _m_read_jpg(jpeg_decompress_struct& jdstru) + { + ::jpeg_read_header(&jdstru, true); //Reject a tables-only JPEG file as an error + + ::jpeg_start_decompress(&jdstru); + + //JSAMPLEs per row in output buffer + auto row_stride = jdstru.output_width * jdstru.output_components; + + pixbuf_.open(jdstru.output_width, jdstru.output_height); + + auto row_buf = jdstru.mem->alloc_sarray(reinterpret_cast(&jdstru), JPOOL_IMAGE, row_stride, 1); + + while (jdstru.output_scanline < jdstru.output_height) + { + ::jpeg_read_scanlines(&jdstru, row_buf, 1); + + pixbuf_.fill_row(jdstru.output_scanline - 1, reinterpret_cast(*row_buf), row_stride, jdstru.output_components * 8); + } + } public: bool open(const experimental::filesystem::path& jpeg_file) override { @@ -45,25 +66,10 @@ namespace nana ::jpeg_stdio_src(&jdstru, fp); - ::jpeg_read_header(&jdstru, true); //Reject a tables-only JPEG file as an error - - ::jpeg_start_decompress(&jdstru); - - //JSAMPLEs per row in output buffer - auto row_stride = jdstru.output_width * jdstru.output_components; - - pixbuf_.open(jdstru.output_width, jdstru.output_height); - - auto row_buf = jdstru.mem->alloc_sarray(reinterpret_cast(&jdstru), JPOOL_IMAGE, row_stride, 1); - - while (jdstru.output_scanline < jdstru.output_height) - { - ::jpeg_read_scanlines(&jdstru, row_buf, 1); - - pixbuf_.fill_row(jdstru.output_scanline - 1, reinterpret_cast(*row_buf), row_stride, jdstru.output_components * 8); - } + _m_read_jpg(jdstru); jpeg_finish_decompress(&jdstru); + is_opened = true; } ::jpeg_destroy_decompress(&jdstru); @@ -73,8 +79,27 @@ namespace nana bool open(const void* data, std::size_t bytes) override { - throw std::logic_error("JPEG is not supported for raw data buffer"); - return false; + bool is_opened = false; + + struct ::jpeg_decompress_struct jdstru; + error_mgr jerr; + + jdstru.err = ::jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = _m_error_handler; + + if (!setjmp(jerr.setjmp_buf)) + { + ::jpeg_create_decompress(&jdstru); + + ::jpeg_mem_src(&jdstru, const_cast(reinterpret_cast(data)), bytes); + _m_read_jpg(jdstru); + + jpeg_finish_decompress(&jdstru); + is_opened = true; + } + + ::jpeg_destroy_decompress(&jdstru); + return is_opened; } private: static void _m_error_handler(::j_common_ptr jdstru) diff --git a/source/paint/detail/image_png.hpp b/source/paint/detail/image_png.hpp index 8cc641d3..9e01e8c6 100644 --- a/source/paint/detail/image_png.hpp +++ b/source/paint/detail/image_png.hpp @@ -19,6 +19,107 @@ namespace nana class image_png : public basic_image_pixbuf { + void _m_read_png(png_structp png_ptr, png_infop info_ptr) + { + ::png_read_info(png_ptr, info_ptr); + + const int png_width = ::png_get_image_width(png_ptr, info_ptr); + const int png_height = ::png_get_image_height(png_ptr, info_ptr); + png_byte color_type = ::png_get_color_type(png_ptr, info_ptr); + const auto bit_depth = ::png_get_bit_depth(png_ptr, info_ptr); + + //do some extra work for palette/gray color type + if (PNG_COLOR_TYPE_PALETTE == color_type) + ::png_set_palette_to_rgb(png_ptr); + else if ((PNG_COLOR_TYPE_GRAY == color_type) && (bit_depth < 8)) + ::png_set_gray_to_rgb(png_ptr); + + auto is_alpha_enabled = (::png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) != 0); + if (is_alpha_enabled) + ::png_set_tRNS_to_alpha(png_ptr); + + //make sure 8-bit per channel + if (16 == bit_depth) + ::png_set_strip_16(png_ptr); + + ::png_set_interlace_handling(png_ptr); + ::png_read_update_info(png_ptr, info_ptr); + + //The following codes may longjmp while image_read error. + png_bytep * row_ptrs = new png_bytep[png_height]; + const std::size_t png_rowbytes = ::png_get_rowbytes(png_ptr, info_ptr); + + pixbuf_.open(png_width, png_height); + + is_alpha_enabled |= ((PNG_COLOR_MASK_ALPHA & color_type) != 0); + pixbuf_.alpha_channel(is_alpha_enabled); + + if (is_alpha_enabled && (png_rowbytes == png_width * sizeof(pixel_argb_t))) + { + for (int i = 0; i < png_height; ++i) + row_ptrs[i] = reinterpret_cast(pixbuf_.raw_ptr(i)); + + ::png_read_image(png_ptr, row_ptrs); + + for (int i = 0; i < png_height; ++i) + { + auto p = pixbuf_.raw_ptr(i); + for (int u = 0; u < png_width; ++u) + { + auto t = p[u].element.red; + p[u].element.red = p[u].element.blue; + p[u].element.blue = t; + } + } + } + else + { + png_byte * png_pixbuf = new png_byte[png_height * png_rowbytes]; + + for (int i = 0; i < png_height; ++i) + row_ptrs[i] = reinterpret_cast(png_pixbuf + png_rowbytes * i); + + ::png_read_image(png_ptr, row_ptrs); + //::png_destroy_read_struct(&png_ptr, &info_ptr, 0); + + std::size_t png_pixel_bytes = png_rowbytes / png_width; + + pixel_argb_t * rgb_row_ptr = pixbuf_.raw_ptr(0); + for (int y = 0; y < png_height; ++y) + { + png_bytep png_ptr = row_ptrs[y]; + + pixel_argb_t * rgb_end = rgb_row_ptr + png_width; + + if (is_alpha_enabled) + { + for (pixel_argb_t * i = rgb_row_ptr; i < rgb_end; ++i) + { + i->element.red = png_ptr[0]; + i->element.green = png_ptr[1]; + i->element.blue = png_ptr[2]; + i->element.alpha_channel = png_ptr[3]; + png_ptr += png_pixel_bytes; + } + } + else + { + for (pixel_argb_t * i = rgb_row_ptr; i < rgb_end; ++i) + { + i->element.red = png_ptr[0]; + i->element.green = png_ptr[1]; + i->element.blue = png_ptr[2]; + i->element.alpha_channel = 255; + png_ptr += png_pixel_bytes; + } + } + rgb_row_ptr = rgb_end; + } + + delete[] png_pixbuf; + } + delete[] row_ptrs; + } public: bool open(const experimental::filesystem::path& png_file) override { @@ -44,109 +145,17 @@ namespace nana { //The following codes may longjmp while init_io error. ::png_init_io(png_ptr, fp); + + //8-byte of sig has been read, tell the libpng there are some bytes missing from start of file ::png_set_sig_bytes(png_ptr, 8); - ::png_read_info(png_ptr, info_ptr); - const int png_width = ::png_get_image_width(png_ptr, info_ptr); - const int png_height = ::png_get_image_height(png_ptr, info_ptr); - png_byte color_type = ::png_get_color_type(png_ptr, info_ptr); - const auto bit_depth = ::png_get_bit_depth(png_ptr, info_ptr); + _m_read_png(png_ptr, info_ptr); - //do some extra work for palette/gray color type - if (PNG_COLOR_TYPE_PALETTE == color_type) - ::png_set_palette_to_rgb(png_ptr); - else if ((PNG_COLOR_TYPE_GRAY == color_type) && (bit_depth < 8)) - ::png_set_gray_to_rgb(png_ptr); - - bool is_alpha_enabled = (::png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) != 0); - if (is_alpha_enabled) - ::png_set_tRNS_to_alpha(png_ptr); - - //make sure 8-bit per channel - if (16 == bit_depth) - ::png_set_strip_16(png_ptr); - - ::png_set_interlace_handling(png_ptr); - ::png_read_update_info(png_ptr, info_ptr); - - //The following codes may longjmp while image_read error. - png_bytep * row_ptrs = new png_bytep[png_height]; - const std::size_t png_rowbytes = ::png_get_rowbytes(png_ptr, info_ptr); - - pixbuf_.open(png_width, png_height); - - is_alpha_enabled |= ((PNG_COLOR_MASK_ALPHA & color_type) != 0); - pixbuf_.alpha_channel(is_alpha_enabled); - - if(is_alpha_enabled && (png_rowbytes == png_width * sizeof(pixel_argb_t))) - { - for(int i = 0; i < png_height; ++i) - row_ptrs[i] = reinterpret_cast(pixbuf_.raw_ptr(i)); - - ::png_read_image(png_ptr, row_ptrs); - ::png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); - - for (int i = 0; i < png_height; ++i) - { - auto p = pixbuf_.raw_ptr(i); - for (int u = 0; u < png_width; ++u) - { - auto t = p[u].element.red; - p[u].element.red = p[u].element.blue; - p[u].element.blue = t; - } - } - } - else - { - png_byte * png_pixbuf = new png_byte[png_height * png_rowbytes]; - - for(int i = 0; i < png_height; ++i) - row_ptrs[i] = reinterpret_cast(png_pixbuf + png_rowbytes * i); - - ::png_read_image(png_ptr, row_ptrs); - ::png_destroy_read_struct(&png_ptr, &info_ptr, 0); - - std::size_t png_pixel_bytes = png_rowbytes / png_width; - - pixel_argb_t * rgb_row_ptr = pixbuf_.raw_ptr(0); - for(int y = 0; y < png_height; ++y) - { - png_bytep png_ptr = row_ptrs[y]; - - pixel_argb_t * rgb_end = rgb_row_ptr + png_width; - - if(is_alpha_enabled) - { - for(pixel_argb_t * i = rgb_row_ptr; i < rgb_end; ++i) - { - i->element.red = png_ptr[0]; - i->element.green = png_ptr[1]; - i->element.blue = png_ptr[2]; - i->element.alpha_channel = png_ptr[3]; - png_ptr += png_pixel_bytes; - } - } - else - { - for(pixel_argb_t * i = rgb_row_ptr; i < rgb_end; ++i) - { - i->element.red = png_ptr[0]; - i->element.green = png_ptr[1]; - i->element.blue = png_ptr[2]; - i->element.alpha_channel = 255; - png_ptr += png_pixel_bytes; - } - } - rgb_row_ptr = rgb_end; - } - - delete [] png_pixbuf; - } - delete [] row_ptrs; is_opened = true; } } + + ::png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); } } @@ -154,10 +163,60 @@ namespace nana return is_opened; } + class png_reader + { + public: + png_reader(const void* data, std::size_t bytes) noexcept + : data_ptr_(reinterpret_cast(data)), bytes_(bytes) + { + } + + static void PNGCAPI read(png_structp png_ptr, png_bytep buf, png_size_t bytes) + { + auto self = reinterpret_cast(::png_get_io_ptr(png_ptr)); + + auto read_bytes = self->bytes_ < bytes ? self->bytes_ : bytes; + + if (read_bytes) + std::memcpy(buf, self->data_ptr_, read_bytes); + + self->bytes_ -= read_bytes; + self->data_ptr_ += read_bytes; + } + private: + const char* data_ptr_; + std::size_t bytes_; + }; + bool open(const void* data, std::size_t bytes) override { - throw std::logic_error("PNG is not supported for raw data buffer"); - return false; + if (bytes < 8 || 0 != ::png_sig_cmp(reinterpret_cast(data), 0, 8)) + return false; + + auto png_ptr = ::png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) + return false; + + bool is_opened = false; + + png_infop info_ptr = ::png_create_info_struct(png_ptr); + + if (info_ptr) + { + png_reader reader{ data, bytes }; + + if (!setjmp(png_jmpbuf(png_ptr))) + { + ::png_set_read_fn(png_ptr, &reader, &png_reader::read); + + _m_read_png(png_ptr, info_ptr); + is_opened = true; + } + } + + ::png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + + return is_opened; } }; }//end namespace detail diff --git a/source/paint/image.cpp b/source/paint/image.cpp index d037d4b8..53740476 100644 --- a/source/paint/image.cpp +++ b/source/paint/image.cpp @@ -163,7 +163,7 @@ namespace paint {} //class image - image::image() + image::image() noexcept {} image::image(const image& rhs) @@ -245,7 +245,7 @@ namespace paint if (ext_png == ext) { #if defined(NANA_ENABLE_PNG) - ptr = std::make_shared; + ptr = std::make_shared(); #else return ptr; #endif @@ -299,14 +299,53 @@ namespace paint return (image_ptr_ ? image_ptr_->open(path) : false); } - bool image::open_icon(const void* data, std::size_t bytes) + bool image::open(const void* data, std::size_t bytes) { - image::image_impl_interface * helper = new detail::image_ico(true); - image_ptr_ = std::shared_ptr(helper); - return helper->open(data, bytes); + close(); + + if (bytes > 2) + { + std::shared_ptr ptr; + + auto meta = *reinterpret_cast(data); + + if (*reinterpret_cast("BM") == meta) + ptr = std::make_shared(); + else if (*reinterpret_cast("MZ") == meta) + ptr = std::make_shared(false); + else + { + if (bytes > 8 && (0x474e5089 == *reinterpret_cast(data))) + { +#if defined(NANA_ENABLE_PNG) + ptr = std::make_shared(); +#endif + } + else + { +#if defined(NANA_ENABLE_JPEG) + //JFIF + if (bytes > 11 && (0xe0ffd8ff == *reinterpret_cast(data)) && 0x4649464A == *reinterpret_cast(reinterpret_cast(data)+6)) + ptr = std::make_shared(); + else if (bytes > 9 && (0x66697845 == *reinterpret_cast(reinterpret_cast(data)+5))) //Exif + ptr = std::make_shared(); +#endif + } + } + + + if (ptr) + { + image_ptr_.swap(ptr); + return image_ptr_->open(data, bytes); + } + } + + return false; } - bool image::empty() const + + bool image::empty() const noexcept { return ((nullptr == image_ptr_) || image_ptr_->empty()); } diff --git a/source/threads/pool.cpp b/source/threads/pool.cpp index d15180d5..c7af1e47 100644 --- a/source/threads/pool.cpp +++ b/source/threads/pool.cpp @@ -1,6 +1,6 @@ /* * A Thread Pool Implementation - * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com) + * Copyright(C) 2003-2016 Jinhao(cnjinhao@hotmail.com) * * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -15,6 +15,7 @@ #include #include #include +#include #if defined(STD_THREAD_NOT_SUPPORTED) #include @@ -62,23 +63,22 @@ namespace threads { #if defined(NANA_WINDOWS) typedef HANDLE thread_t; -#elif defined(NANA_LINUX) || defined(NANA_MACOS) +#elif defined(NANA_POSIX) typedef pthread_t thread_t; #endif impl * pool_ptr; task * task_ptr; thread_t handle; - volatile state thr_state; + std::atomic thr_state; time_t timestamp; -#if defined(NANA_LINUX) || defined(NANA_MACOS) +#if defined(NANA_POSIX) std::mutex wait_mutex; std::condition_variable wait_cond; - volatile bool suspended; + std::atomic suspended; #endif }; public: impl(std::size_t thr_number) - : runflag_(true) { if(0 == thr_number) thr_number = 4; @@ -90,7 +90,7 @@ namespace threads pto->task_ptr = nullptr; #if defined(NANA_WINDOWS) pto->handle = (HANDLE)::_beginthreadex(0, 0, reinterpret_cast(&impl::_m_thr_starter), pto, 0, 0); -#elif defined(NANA_LINUX) || defined(NANA_MACOS) +#elif defined(NANA_POSIX) pto->suspended = false; ::pthread_create(&(pto->handle), 0, reinterpret_cast(&impl::_m_thr_starter), pto); #endif @@ -137,7 +137,7 @@ namespace threads #if defined(NANA_WINDOWS) ::WaitForSingleObject(thr->handle, INFINITE); ::CloseHandle(thr->handle); -#elif defined(NANA_LINUX) || defined(NANA_MACOS) +#elif defined(NANA_POSIX) ::pthread_join(thr->handle, 0); ::pthread_detach(thr->handle); #endif @@ -223,7 +223,7 @@ namespace threads pto->thr_state = state::idle; #if defined(NANA_WINDOWS) ::SuspendThread(pto->handle); -#elif defined(NANA_LINUX) || defined(NANA_MACOS) +#elif defined(NANA_POSIX) std::unique_lock lock(pto->wait_mutex); pto->suspended = true; pto->wait_cond.wait(lock); @@ -240,7 +240,7 @@ namespace threads if(n == 1 || n == static_cast(-1)) break; } -#elif defined(NANA_LINUX) || defined(NANA_MACOS) +#elif defined(NANA_POSIX) while(false == pto->suspended) ; std::unique_lock lock(pto->wait_mutex); @@ -327,7 +327,7 @@ namespace threads ::_endthreadex(0); return 0; } -#elif defined(NANA_LINUX) || defined(NANA_MACOS) +#elif defined(NANA_POSIX) static void * _m_thr_starter(pool_throbj * pto) { pto->pool_ptr->_m_thr_runner(pto); @@ -335,7 +335,7 @@ namespace threads } #endif private: - volatile bool runflag_; + std::atomic runflag_{ true }; std::recursive_mutex mutex_; struct signal