add a new member function of pixel_buffer for rotating image

This commit is contained in:
Jinhao 2016-05-22 02:51:30 +08:00
parent 9947d0c511
commit 6217232a31
4 changed files with 216 additions and 24 deletions

View File

@ -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);

View File

@ -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<pixel_buffer_storage> storage_;
};

View File

@ -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<int>(gap), r.y }, right{ r.right() - static_cast<int>(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<int>(gap) }, bottom{ r.x, r.bottom() - static_cast<int>(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)

View File

@ -19,6 +19,7 @@
#include <stdexcept>
#include <cstring>
#include <cmath>
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<pixel_color_t[]> 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<char*>(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<int>(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<unsigned>(width), static_cast<unsigned>(height)),
pixel_size(static_cast<unsigned>(width), static_cast<unsigned>(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<char*>(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<int>(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<pixel_color_t*>(reinterpret_cast<char*>(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<double>& origin):
specific_{ _m_spec(angle) },
sin_a_{ std::sin(angle * degree_) },
cos_a_{ std::cos(angle * degree_) },
origin_{ origin }
{
}
basic_point<double> 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<double> 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<double> 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<double> 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<int>(sp->pixel_size.width);
}
else if (angle > 180)
{
pw.x = static_cast<int>(sp->pixel_size.width);
}
size_rotated.width = static_cast<unsigned>(std::abs(point_rotate.from(pw).x) * 2) + 1;
size_rotated.height = static_cast<unsigned>(std::abs(point_rotate.from(ph).y) * 2) + 1;
}
pixel_buffer rotated_pxbuf{ size_rotated.width, size_rotated.height };
const basic_point<double> rotated_origin{ (size_rotated.width - 1) / 2.0, (size_rotated.height - 1) / 2.0 };
for (int y = 0; y < static_cast<int>(size_rotated.height); ++y)
{
auto buf = rotated_pxbuf.raw_ptr(y);
basic_point<double> dest{ -rotated_origin.x, y - rotated_origin.y };
dest = dest + origin;
const int right_point = static_cast<int>(dest.x) + static_cast<int>(size_rotated.width);
const int source_right = static_cast<int>(sp->pixel_size.width);
const int source_bottom = static_cast<int>(sp->pixel_size.height);
for (point point_dest{ static_cast<int>(dest.x), static_cast<int>(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<double>(sp->pixel_size.width - 1) && 0 <= point_source.y && point_source.y <= static_cast<double>(sp->pixel_size.height - 1))
{
*buf = this->raw_ptr(static_cast<std::size_t>(point_source.y))[static_cast<int>(point_source.x)];
}
else
*buf = extend_color.px_color();
++buf;
}
}
return rotated_pxbuf;
}
}//end namespace paint
}//end namespace nana