From 6217232a31fe55319f88027950ea318f98ad26f0 Mon Sep 17 00:00:00 2001 From: Jinhao Date: Sun, 22 May 2016 02:51:30 +0800 Subject: [PATCH] add a new member function of pixel_buffer for rotating image --- include/nana/paint/graphics.hpp | 4 +- include/nana/paint/pixel_buffer.hpp | 1 + source/paint/graphics.cpp | 23 +++ source/paint/pixel_buffer.cpp | 212 +++++++++++++++++++++++++--- 4 files changed, 216 insertions(+), 24 deletions(-) diff --git a/include/nana/paint/graphics.hpp b/include/nana/paint/graphics.hpp index 501d30fc..fec075ab 100644 --- a/include/nana/paint/graphics.hpp +++ b/include/nana/paint/graphics.hpp @@ -111,6 +111,7 @@ namespace nana void bitblt(const ::nana::rectangle& r_dst, const graphics& src); ///< Transfers the color data corresponding to r_dst from the src graphics to this graphics. void bitblt(const ::nana::rectangle& r_dst, const graphics& src, const point& p_src);///< Transfers the color data corresponding to r_dst from the src graphics at point p_src to this graphics. + void blend(const ::nana::rectangle& r, const ::nana::color&, double fade_rate); void blend(const ::nana::rectangle& s_r, graphics& dst, const point& d_pos, double fade_rate) const;///< blends with the dst object. void blur(const ::nana::rectangle& r, std::size_t radius); ///< Blur process. @@ -144,8 +145,6 @@ namespace nana unsigned bidi_string(const nana::point&, const wchar_t *, std::size_t len); unsigned bidi_string(const point& pos, const char*, std::size_t len); - void blend(const ::nana::rectangle& r, const ::nana::color&, double fade_rate); - void set_pixel(int x, int y, const ::nana::color&); void set_pixel(int x, int y); @@ -167,6 +166,7 @@ namespace nana void rectangle(const ::nana::rectangle&, bool solid); void rectangle(const ::nana::rectangle&, bool solid, const color&); void frame_rectangle(const ::nana::rectangle&, const color& left, const color& top, const color& right, const color& bottom); + void frame_rectangle(const ::nana::rectangle&, const color&, unsigned gap); void gradual_rectangle(const ::nana::rectangle&, const color& from, const color& to, bool vertical); void round_rectangle(const ::nana::rectangle&, unsigned radius_x, unsigned radius_y, const color&, bool solid, const color& color_if_solid); diff --git a/include/nana/paint/pixel_buffer.hpp b/include/nana/paint/pixel_buffer.hpp index 49d2fc4e..6b6a4994 100644 --- a/include/nana/paint/pixel_buffer.hpp +++ b/include/nana/paint/pixel_buffer.hpp @@ -75,6 +75,7 @@ namespace nana{ namespace paint void blend(const nana::rectangle& s_r, drawable_type dw_dst, const nana::point& d_pos, double fade_rate) const; void blur(const nana::rectangle& r, std::size_t radius); + pixel_buffer rotate(double angle, const color& extend_color); private: std::shared_ptr storage_; }; diff --git a/source/paint/graphics.cpp b/source/paint/graphics.cpp index 57d8138f..15a29c52 100644 --- a/source/paint/graphics.cpp +++ b/source/paint/graphics.cpp @@ -1112,6 +1112,29 @@ namespace paint line_to({ r.x, r.y }, left_clr); } + void graphics::frame_rectangle(const ::nana::rectangle& r, const color& clr, unsigned gap) + { + palette(false, clr); + + if (r.width > gap * 2) + { + point left{ r.x + static_cast(gap), r.y }, right{ r.right() - static_cast(gap) - 1, r.y }; + line(left, right); + + left.y = right.y = r.bottom() - 1; + line(left, right); + } + + if (r.height > gap * 2) + { + point top{ r.x, r.y + static_cast(gap) }, bottom{ r.x, r.bottom() - static_cast(gap) - 1 }; + line(top, bottom); + + top.x = bottom.x = r.right() - 1; + line(top, bottom); + } + } + void graphics::gradual_rectangle(const ::nana::rectangle& rct, const ::nana::color& from, const ::nana::color& to, bool vertical) { #if defined(NANA_WINDOWS) diff --git a/source/paint/pixel_buffer.cpp b/source/paint/pixel_buffer.cpp index 574c8951..f2b438a1 100644 --- a/source/paint/pixel_buffer.cpp +++ b/source/paint/pixel_buffer.cpp @@ -19,6 +19,7 @@ #include #include +#include namespace nana{ namespace paint { @@ -47,11 +48,36 @@ namespace nana{ namespace paint struct pixel_buffer::pixel_buffer_storage : private nana::noncopyable { + pixel_buffer_storage& operator=(const pixel_buffer_storage&) = delete; + + bool _m_alloc() + { + if (pixel_size.empty()) + return false; + + std::unique_ptr pxbuf{ new pixel_color_t[pixel_size.width * pixel_size.height] }; +#if defined(NANA_X11) + auto & spec = nana::detail::platform_spec::instance(); + x11.image = ::XCreateImage(spec.open_display(), spec.screen_visual(), 32, ZPixmap, 0, reinterpret_cast(pxbuf.get()), pixel_size.width, pixel_size.height, 32, 0); + x11.attached = false; + if (!x11.image) + throw std::runtime_error("Nana.pixel_buffer: XCreateImage failed"); + + if (static_cast(bytes_per_line) != x11.image->bytes_per_line) + { + x11.image->data = nullptr; + XDestroyImage(x11.image); + throw std::runtime_error("Nana.pixel_buffer: Invalid pixel buffer context."); + } +#endif + raw_pixel_buffer = pxbuf.release(); + return true; + } public: const drawable_type drawable; //Attached handle const nana::rectangle valid_r; const nana::size pixel_size; - pixel_color_t * raw_pixel_buffer; + pixel_color_t * raw_pixel_buffer{ nullptr }; const std::size_t bytes_per_line; bool alpha_channel{false}; #if defined(NANA_X11) @@ -86,37 +112,31 @@ namespace nana{ namespace paint } }img_pro; + pixel_buffer_storage(const pixel_buffer_storage& other) : + drawable{ other.drawable }, + valid_r{ other.valid_r }, + pixel_size{ other.pixel_size }, + bytes_per_line{ other.bytes_per_line }, + alpha_channel{ other.alpha_channel }, + img_pro{ other.img_pro } + { + if (_m_alloc()) + std::memcpy(raw_pixel_buffer, other.raw_pixel_buffer, pixel_size.width * pixel_size.height); + } + pixel_buffer_storage(std::size_t width, std::size_t height) : drawable(nullptr), valid_r(0, 0, static_cast(width), static_cast(height)), pixel_size(static_cast(width), static_cast(height)), - raw_pixel_buffer(new pixel_color_t[width * height]), bytes_per_line(width * sizeof(pixel_color_t)) { -#if defined(NANA_X11) - nana::detail::platform_spec & spec = nana::detail::platform_spec::instance(); - x11.image = ::XCreateImage(spec.open_display(), spec.screen_visual(), 32, ZPixmap, 0, reinterpret_cast(raw_pixel_buffer), width, height, 32, 0); - x11.attached = false; - if(nullptr == x11.image) - { - delete [] raw_pixel_buffer; - throw std::runtime_error("Nana.pixel_buffer: XCreateImage failed"); - } - - if(static_cast(bytes_per_line) != x11.image->bytes_per_line) - { - x11.image->data = nullptr; - XDestroyImage(x11.image); - delete [] raw_pixel_buffer; - throw std::runtime_error("Nana.pixel_buffer: Invalid pixel buffer context."); - } -#endif + _m_alloc(); } pixel_buffer_storage(drawable_type drawable, const nana::rectangle& want_r) : drawable(drawable), valid_r(valid_rectangle(paint::detail::drawable_size(drawable), want_r)), - pixel_size(valid_r), + pixel_size(valid_r.dimension()), #if defined(NANA_WINDOWS) raw_pixel_buffer(reinterpret_cast(reinterpret_cast(drawable->pixbuf_ptr + valid_r.x) + drawable->bytes_per_line * valid_r.y)), bytes_per_line(drawable->bytes_per_line) @@ -362,7 +382,7 @@ namespace nana{ namespace paint XDestroyImage(img); } #endif - }; + };//end struct pixel_buffer_storage pixel_buffer::pixel_buffer(drawable_type drawable, const nana::rectangle& want_rectangle) { @@ -1105,5 +1125,153 @@ namespace nana{ namespace paint if (overlap(r, ::nana::rectangle{ this->size() }, good_r)) (*(sp->img_pro.blur))->process(*this, good_r, radius); } + + + //x' = x*cos(angle) - y*sin(angle) + //y' = y*cos(angle) - x*sin(angle) + class calc_rotate + { + enum class angles + { + none, half_pi, pi, triangle_pi + }; + public: + calc_rotate(double angle, const basic_point& origin): + specific_{ _m_spec(angle) }, + sin_a_{ std::sin(angle * degree_) }, + cos_a_{ std::cos(angle * degree_) }, + origin_{ origin } + { + } + + basic_point from(const point& p) + { + switch (specific_) + { + case angles::half_pi: + return{ p.y - origin_.y, origin_.x - p.x}; + case angles::pi: + return{ origin_.x - p.x, origin_.y - p.y}; + case angles::triangle_pi: + return{ origin_.y - p.y, p.x - origin_.x}; + default: + break; + } + + return{ + (p.x - origin_.x) * cos_a_ + (p.y - origin_.y) * sin_a_, + (p.y - origin_.y) * cos_a_ - (p.x - origin_.x) * sin_a_ + }; + } + + basic_point to(const point& p) + { + switch (specific_) + { + case angles::half_pi: + return{ origin_.y - p.y, p.x - origin_.x }; + case angles::pi: + return{ origin_.x - p.x, origin_.y - p.y }; + case angles::triangle_pi: + return{ p.y - origin_.y, origin_.x - p.x }; + default: + break; + } + + return{ + (p.x - origin_.x) * cos_a_ - (p.y - origin_.y) * sin_a_, + (p.y - origin_.y) * cos_a_ + (p.x - origin_.x) * sin_a_ + }; + } + private: + static angles _m_spec(double angle) + { + if (90.0 == angle) + return angles::half_pi; + else if (180.0 == angle) + return angles::pi; + else if (270.0 == angle) + return angles::triangle_pi; + + return angles::none; + } + private: + const angles specific_; + const double degree_{ std::acos(-1) / 180 }; + const double sin_a_; + const double cos_a_; + const basic_point origin_; + }; + + pixel_buffer pixel_buffer::rotate(double angle, const color& extend_color) + { + auto sp = storage_.get(); + if (!sp) + return{}; + + if (angle <= 0 || 360 <= angle) + { + return{*this}; + } + + const basic_point origin{ (sp->pixel_size.width - 1) / 2.0, (sp->pixel_size.height - 1) / 2.0 }; + calc_rotate point_rotate{ angle, origin }; + + nana::size size_rotated{ sp->pixel_size }; + + if (90 == angle || 270 == angle) + { + size_rotated.shift(); + } + else + { + point pw, ph; + if (angle < 180) + { + ph.x = static_cast(sp->pixel_size.width); + } + else if (angle > 180) + { + pw.x = static_cast(sp->pixel_size.width); + } + + size_rotated.width = static_cast(std::abs(point_rotate.from(pw).x) * 2) + 1; + size_rotated.height = static_cast(std::abs(point_rotate.from(ph).y) * 2) + 1; + } + + pixel_buffer rotated_pxbuf{ size_rotated.width, size_rotated.height }; + + const basic_point rotated_origin{ (size_rotated.width - 1) / 2.0, (size_rotated.height - 1) / 2.0 }; + + for (int y = 0; y < static_cast(size_rotated.height); ++y) + { + auto buf = rotated_pxbuf.raw_ptr(y); + + basic_point dest{ -rotated_origin.x, y - rotated_origin.y }; + dest = dest + origin; + + const int right_point = static_cast(dest.x) + static_cast(size_rotated.width); + + const int source_right = static_cast(sp->pixel_size.width); + const int source_bottom = static_cast(sp->pixel_size.height); + + for (point point_dest{ static_cast(dest.x), static_cast(dest.y) }; point_dest.x < right_point; ++point_dest.x) + { + auto point_source = point_rotate.to(point_dest) + origin; + + if (0 <= point_source.x && point_source.x <= static_cast(sp->pixel_size.width - 1) && 0 <= point_source.y && point_source.y <= static_cast(sp->pixel_size.height - 1)) + { + *buf = this->raw_ptr(static_cast(point_source.y))[static_cast(point_source.x)]; + } + else + *buf = extend_color.px_color(); + + ++buf; + } + } + + + return rotated_pxbuf; + } }//end namespace paint }//end namespace nana