From b8976b1b224a0ee9a3026b6379a1c40fd4997b61 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Mar 04 2020 09:06:21 +0000 Subject: freetype --- diff --git a/c++/freetype/.gitignore b/c++/freetype/.gitignore new file mode 100644 index 0000000..87599b1 --- /dev/null +++ b/c++/freetype/.gitignore @@ -0,0 +1,4 @@ +/copy/ +/build/ +.sconsign* +.pydevproject diff --git a/c++/freetype/SConstruct b/c++/freetype/SConstruct new file mode 100644 index 0000000..cf81332 --- /dev/null +++ b/c++/freetype/SConstruct @@ -0,0 +1,3 @@ +build_dir = ARGUMENTS.get('build_dir', 'build') +VariantDir(build_dir, 'src', duplicate = 0) +SConscript(build_dir + '/SConstruct') diff --git a/c++/freetype/src/SConstruct b/c++/freetype/src/SConstruct new file mode 100644 index 0000000..77fbf44 --- /dev/null +++ b/c++/freetype/src/SConstruct @@ -0,0 +1,34 @@ + +env = Environment() + + +# config + +libs = ['gtkmm-3.0', 'pangomm-1.4', 'pangoft2'] + + +# compute build options + +flags = ' -O0 -g -Wall -fmessage-length=0 ' + + +# files lists + +target = 'freetype' + +sources = [ + 'main.cpp', + 'freetypeview.cpp', + 'matrix.cpp', + 'surface.cpp', + 'view.cpp' ] + + +# build + +env.ParseConfig('pkg-config --cflags --libs ' + ' '.join(libs)) + +env.Program( + target = target, + source = sources, + parse_flags = flags ) diff --git a/c++/freetype/src/common.h b/c++/freetype/src/common.h new file mode 100644 index 0000000..b359255 --- /dev/null +++ b/c++/freetype/src/common.h @@ -0,0 +1,122 @@ +#ifndef COMMON_H +#define COMMON_H + + +#include +#include +#include + +#include + + +typedef float ColorReal; +typedef double Real; +typedef int Int; +typedef unsigned int UInt; +typedef long long int LongInt; +typedef unsigned long long int ULongInt; + +using Glib::RefPtr; + + +const Real real_precision = 1e-10; +const Real real_precision_sqr = real_precision * real_precision; + + +class Shared { +private: + int ref_count; +public: + Shared(): ref_count(1) { } + Shared(const Shared&): ref_count(1) { } + virtual ~Shared() { }; + + inline Shared& operator=(const Shared&) { return *this; } + inline void reference() { + ++ref_count; + } + inline void unreference() { + if (--ref_count <= 0) + delete this; + } +}; + + +class Color { +public: + typedef ColorReal Channel; + union { + struct { Channel channels[4]; }; + struct { Channel r, g, b, a; }; + }; + + inline explicit Color(Channel r = 0.0, Channel g = 0.0, Channel b = 0.0, Channel a = 0.0): + r(r), g(g), b(b), a(a) { } + + inline Color operator+ (const Color &other) const + { return Color(r + other.r, g + other.g, b + other.b, a + other.a); } + inline Color operator- (const Color &other) const + { return Color(r - other.r, g - other.g, b - other.b, a - other.a); } + inline Color operator* (Channel k) const + { return Color(r*k, g*k, b*k, a*k); } + + inline Color& operator+= (const Color &other) + { r += other.r, g += other.g, b += other.b, a += other.a; return *this; } + inline Color& operator-= (const Color &other) + { r -= other.r, g -= other.g, b -= other.b, a -= other.a; return *this; } + inline Color& operator*= (Channel k) + { r *= k, g *= k, b *= k, a *= k; return *this; } + + inline Color get_mult_alpha() const + { return Color(r*a, g*a, b*a, a); } + inline Color get_demult_alpha() const { + if (fabs(a) < real_precision) return *this; + Channel k = 1/a; + return Color(r*k, g*k, b*k, a); + } + + inline void mult_alpha() + { r*=a, g*=a, b*=a; } + inline void demult_alpha() { + if (fabs(a) < real_precision) return; + Real k = 1/a; + r*=k, g*=k, b*=k; + } +}; + + +inline bool real_less(const Real &a, const Real &b) + { return a < b - real_precision; } +inline bool real_greater(const Real &a, const Real &b) + { return real_less(b, a); } +inline bool real_less_or_equal(const Real &a, const Real &b) + { return !real_less(b, a); } +inline bool real_greater_or_equal(const Real &a, const Real &b) + { return !real_less(a, b); } +inline bool real_equal(const Real &a, const Real &b) + { return !real_less(a, b) && !real_less(b, a); } +inline bool real_not_equal(const Real &a, const Real &b) + { return real_less(a, b) || real_less(b, a); } + + +inline int clz(const Int &x) + { return x ? __builtin_clz(x) : 32; } +inline int clz(const UInt &x) + { return x ? __builtin_clz(x) : 32; } +inline int clz(const LongInt &x) + { return x ? __builtin_clzll(x) : 64; } +inline int clz(const ULongInt &x) + { return x ? __builtin_clzll(x) : 64; } + + +inline Real clamp(Real x, Real min, Real max) + { return std::max(min, std::min(max, x)); } +inline int iclamp(int x, int min, int max) + { return std::max(min, std::min(max, x)); } + +inline Real flip_clamp(Real x) { + x -= floor(0.5*x)*2.0; + return 1.0 - fabs(1.0 - x); +}; + +#endif diff --git a/c++/freetype/src/freetypeview.cpp b/c++/freetype/src/freetypeview.cpp new file mode 100644 index 0000000..8be9d7b --- /dev/null +++ b/c++/freetype/src/freetypeview.cpp @@ -0,0 +1,245 @@ + +#include +#include + +#include +#include + +#include +#include FT_BITMAP_H +#include +#include +#include + +#include "surface.h" +#include "log.h" + +#include "freetypeview.h" + + +static void put_bitmap(Surface &surface, FT_Bitmap &bitmap, int x, int y, const Color &color); + + +// my pango renderer + +#define MY_TYPE_PANGO_RENDERER (my_pango_renderer_get_type ()) + +struct _MyPangoRenderer { + PangoRenderer parent_instance; + Surface *surface; + ColorReal color[4]; +}; + +struct _MyPangoRendererClass { + PangoRendererClass parent_class; +}; + +typedef _MyPangoRenderer MyPangoRenderer; +typedef _PangoRendererClass MyPangoRendererClass; + +#define PANGO_FT2_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_FT2_RENDERER, PangoFT2RendererClass)) +#define PANGO_IS_FT2_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_FT2_RENDERER)) +#define PANGO_FT2_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_FT2_RENDERER, PangoFT2RendererClass)) + +G_DEFINE_TYPE(MyPangoRenderer, my_pango_renderer, PANGO_TYPE_RENDERER) + +static void +my_pango_renderer_draw_glyph( + PangoRenderer *renderer, + PangoFont *font, + PangoGlyph glyph, + double x, + double y ) +{ + MyPangoRenderer *my_renderer = (MyPangoRenderer*)renderer; + if (!my_renderer->surface) + return; + const Color &color = *(const Color*)my_renderer->color; + + const int fx = (int)round(x*64); + const int fy = (int)round(y*64); + const int ix = fx / 64; + const int iy = fy / 64; + FT_Vector vec = {}; + vec.x = fx % 64; + vec.y = fy % 64; + + FT_Face face = pango_fc_font_lock_face((PangoFcFont*)font); + FT_Set_Transform(face, nullptr, &vec); + if (!FT_Load_Glyph(face, glyph, FT_LOAD_NO_HINTING)) { + if (!FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL)) { + put_bitmap( + *my_renderer->surface, + face->glyph->bitmap, + face->glyph->bitmap_left + ix, + iy - face->glyph->bitmap_top, + color ); + } + } + pango_fc_font_unlock_face((PangoFcFont*)font); +} + +static void +my_pango_renderer_draw_trapezoid( + PangoRenderer *renderer, + PangoRenderPart part, + double y1, + double x11, + double x21, + double y2, + double x12, + double x22 ) +{ + std::cout << "my_pango_renderer_draw_trapezoid" << std::endl; +} + +static void +my_pango_renderer_init(MyPangoRenderer *renderer) { + renderer->surface = nullptr; + *(Color*)renderer->color = Color(); +} + +static void +my_pango_renderer_class_init(MyPangoRendererClass *klass) +{ + PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS(klass); + renderer_class->draw_glyph = my_pango_renderer_draw_glyph; + renderer_class->draw_trapezoid = my_pango_renderer_draw_trapezoid; +} + +PangoRenderer* +my_pango_renderer_new(Surface &surface, const Color &color) { + MyPangoRenderer *renderer = (MyPangoRenderer*)g_object_new(MY_TYPE_PANGO_RENDERER, nullptr); + renderer->surface = &surface; + *(Color*)renderer->color = color; + return (PangoRenderer*)renderer; +} + + + +static void set_substitute(FcPattern *pattern, gpointer) { + FcPatternDel(pattern, FC_HINTING); + FcPatternAddBool(pattern, FC_HINTING, FcFalse); +} + +static void put_bitmap(Surface &surface, FT_Bitmap &bitmap, int x, int y, const Color &color) { + if (x >= surface.width() || y >= surface.height()) + return; + if (x + (int)bitmap.width <= 0 || y + (int)bitmap.rows <= 0) + return; + + const int bx = std::max(0, -x); + const int by = std::max(0, -y); + x = std::max(0, x); + y = std::max(0, y); + const int w = std::min(surface.width() - x, (int)bitmap.width - bx); + const int h = std::min(surface.height() - y, (int)bitmap.rows - by); + const int pitch = surface.pitch(); + + Color *dst_pixel = &surface[y][x]; + const int dst_row_step = pitch - w; + + const unsigned char *src_pixel = bitmap.buffer + by*bitmap.pitch + bx; + const int src_row_step = bitmap.pitch - w; + + for(const Color *end = dst_pixel + pitch*h; dst_pixel != end; dst_pixel += dst_row_step, src_pixel += src_row_step) { + for(const Color *row_end = dst_pixel + w; dst_pixel != row_end; ++dst_pixel, ++src_pixel) { + Real a = *src_pixel * (1/255.0); + *dst_pixel = *dst_pixel*(1-a) + color*a; + } + } +} + + +FreeTypeView::FreeTypeView() { + params.family = "Ani"; + params.bold = false; + params.italic = false; + params.size = 12; + + params.position = Vector2(15.5, 18.4); + params.color = Color(1, 1, 0, 1); + + params.text = "Hello World! 123456"; + + Glib::signal_timeout().connect( + sigc::mem_fun(*this, &FreeTypeView::on_timeout), + 20, + Glib::PRIORITY_DEFAULT_IDLE ); +} + +FreeTypeView::~FreeTypeView() + { } + +bool +FreeTypeView::on_timeout() { + if (!is_visible()) return false; + params.position.x += 0.01; + update_surface(); + return true; +} + +void +FreeTypeView::update_surface() { + this->surface.clear(); + const int width = get_allocated_width(); + const int height = get_allocated_height(); + if (width <= 0 || height <= 0) + return; + + // init + PangoFontMap *font_map = pango_ft2_font_map_new(); + pango_ft2_font_map_set_default_substitute( + (PangoFT2FontMap*)font_map, + &set_substitute, + nullptr, + nullptr ); + PangoContext *context = pango_font_map_create_context(font_map); + + // load font + PangoFontDescription *font_desc = pango_font_description_new(); + pango_font_description_set_family(font_desc, params.family.c_str()); + pango_font_description_set_weight(font_desc, params.bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL); + pango_font_description_set_style(font_desc, params.italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL); + pango_font_description_set_absolute_size(font_desc, params.size * PANGO_SCALE); + + PangoFont *font = pango_font_map_load_font(font_map, context, font_desc); + + // create layout + PangoLayout *layout = pango_layout_new(context); + pango_layout_set_font_description(layout, font_desc); + pango_layout_set_text(layout, params.text.c_str(), (int)params.text.size()); + + // reneder + DataSurface surface(width, height); + PangoRenderer *renderer = my_pango_renderer_new(surface, params.color); + pango_renderer_draw_layout( + renderer, + layout, + (int)round(params.position.x*PANGO_SCALE), + (int)round(params.position.y*PANGO_SCALE) ); + + // free pango + g_object_unref(renderer); + pango_font_description_free(font_desc); + g_object_unref(layout); + g_object_unref(font); + g_object_unref(context); + g_object_unref(font_map); + + this->surface = surface.to_cairo_surface(true); + + queue_draw(); +} + +void +FreeTypeView::on_draw_view(const Cairo::RefPtr &context) { + if (surface) { + context->save(); + context->transform(transform_from_pixels().to_cairo()); + context->set_source(surface, 0, 0); + context->paint(); + context->restore(); + } +} + diff --git a/c++/freetype/src/freetypeview.h b/c++/freetype/src/freetypeview.h new file mode 100644 index 0000000..c66aceb --- /dev/null +++ b/c++/freetype/src/freetypeview.h @@ -0,0 +1,36 @@ +#ifndef FREETYPEVIEW_H +#define FREETYPEVIEW_H + +#include "view.h" + +class FreeTypeView: public View { +public: + class Params { + public: + std::string family; + bool bold; + bool italic; + Real size; + + Vector2 position; + Color color; + + std::string text; + + Params(): bold(), italic(), size() { } + }; + + Params params; + Cairo::RefPtr surface; + + FreeTypeView(); + ~FreeTypeView(); + + bool on_timeout(); + + void update_surface(); + void on_draw_view(const Cairo::RefPtr &context); +}; + +#endif + diff --git a/c++/freetype/src/log.h b/c++/freetype/src/log.h new file mode 100644 index 0000000..5bd3344 --- /dev/null +++ b/c++/freetype/src/log.h @@ -0,0 +1,46 @@ +#ifndef LOG_H +#define LOG_H + + +#include +#include +#include + +#include "vector.h" +#include "matrix.h" + + +class Log { +public: + static const std::string& tab() + { static std::string t = " "; return t; } + static const std::string tab(int count) { + const std::string &t = tab(); + std::string tt; + for(int i = 0; i < count; ++i) tt += t; + return tt; + } + + template + static std::string to_string(const VectorT &x, int w = 0) { + std::stringstream s; + s << "("; + for(int i = 0; i < VectorT::Count; ++i) + s << (i ? ", " : "") << std::setw(w) << x[i] << std::setw(0); + s << ")"; + return s.str(); + } + + template + static std::string to_string(const PairT &x, int w = 0) + { return to_string(x.p0, w) + "-" + to_string(x.p1, w); } + + static std::string to_string(const Matrix3 &x, int w = 0, const std::string &prefix = std::string()) { + std::stringstream s; + for(int i = 0; i < 3; ++i) + s << prefix << to_string(x[i], w) << std::endl; + return s.str(); + } +}; + +#endif diff --git a/c++/freetype/src/main.cpp b/c++/freetype/src/main.cpp new file mode 100644 index 0000000..d2fc4b3 --- /dev/null +++ b/c++/freetype/src/main.cpp @@ -0,0 +1,25 @@ +#include + +#include +#include + +#include "freetypeview.h" + + +int main(int argc, char **argv) { + std::cout << "Lab: prototype for freetype draw" << std::endl; + + Glib::RefPtr application = Gtk::Application::create( + argc, argv, "org.coolbug.lab.perspective"); + + std::cout << "create window" << std::endl; + Gtk::Window window; + window.add(*Gtk::manage(new FreeTypeView())); + window.show_all(); + + std::cout << "run" << std::endl; + int result = application->run(window); + + std::cout << "finished with code " << result << std::endl; + return result; +} diff --git a/c++/freetype/src/matrix.cpp b/c++/freetype/src/matrix.cpp new file mode 100644 index 0000000..d7c1da1 --- /dev/null +++ b/c++/freetype/src/matrix.cpp @@ -0,0 +1,38 @@ +#include "matrix.h" + + +Real +Matrix3::det() const { + return m00*(m11*m22 - m12*m21) + + m01*(m12*m20 - m10*m22) + + m02*(m10*m21 - m11*m20); +} + +Matrix3 +Matrix3::inverted(bool *success) const { + const Real rm00 = m11*m22 - m12*m21; + const Real rm10 = m12*m20 - m10*m22; + const Real rm20 = m10*m21 - m11*m20; + const Real d = m00*rm00 + m01*rm10 + m02*rm20; + + Real k; + if (fabs(d) > real_precision) { + k = 1/d; + if (success) *success = true; + } else { + k = 0; + if (success) *success = false; + } + + return Matrix3( + Vector3( m11*m22 - m12*m21, + m02*m21 - m01*m22, + m01*m12 - m02*m11 )*k, + Vector3( m12*m20 - m10*m22, + m00*m22 - m02*m20, + m02*m10 - m00*m12 )*k, + Vector3( m10*m21 - m11*m20, + m01*m20 - m00*m21, + m00*m11 - m01*m10 )*k ); +} + diff --git a/c++/freetype/src/matrix.h b/c++/freetype/src/matrix.h new file mode 100644 index 0000000..be6a537 --- /dev/null +++ b/c++/freetype/src/matrix.h @@ -0,0 +1,95 @@ +#ifndef MATRIX_H +#define MATRIX_H + + +#include + +#include "vector.h" + + + +class Matrix3 { +public: + union { + struct { + Real m00, m01, m02, + m10, m11, m12, + m20, m21, m22; + }; + struct { Real m[3][3]; }; + struct { Real a[9]; }; + }; + + inline explicit Matrix3( + const Vector3 &x = Vector3(1, 0, 0), + const Vector3 &y = Vector3(0, 1, 0), + const Vector3 &z = Vector3(0, 0, 1) + ): + m00(x.x), m01(x.y), m02(x.z), + m10(y.x), m11(y.y), m12(y.z), + m20(z.x), m21(z.y), m22(z.z) { } + + inline Vector3& operator[] (int index) { return Vector3::cast(m[index]); } + inline const Vector3& operator[] (int index) const { return Vector3::cast(m[index]); } + + inline Vector3& row_x() { return (*this)[0]; } + inline Vector3& row_y() { return (*this)[1]; } + inline Vector3& row_z() { return (*this)[2]; } + + inline const Vector3& row_x() const { return (*this)[0]; } + inline const Vector3& row_y() const { return (*this)[1]; } + inline const Vector3& row_z() const { return (*this)[2]; } + + inline const Vector3 get_col(int index) const + { return Vector3( m[0][index], m[1][index], m[2][index] ); } + + inline Vector3 operator* (const Vector3 &v) const + { return row_x()*v.x + row_y()*v.y + row_z()*v.z; } + inline Matrix3 operator* (const Matrix3 &other) const { + return Matrix3( *this * other.row_x(), + *this * other.row_y(), + *this * other.row_z() ); + } + + inline Matrix3& operator*= (const Matrix3 &other) + { return *this = *this * other; } + + Real det() const; + Matrix3 inverted(bool *success = NULL) const; + inline Matrix3& invert(bool *success = NULL) + { return *this = inverted(); } + + static Real det(const Matrix3 matrix, const Matrix3 preinverted_matrix); + static inline Matrix3 zero() { return Matrix3(Vector3(), Vector3(), Vector3()); } + + static inline Matrix3 identity() { return Matrix3(); } + static inline Matrix3 translation(const Vector2 &v) { + return Matrix3( + Vector3(1, 0, 0), + Vector3(0, 1, 0), + Vector3(v , 1) ); + } + static inline Matrix3 scaling(const Vector2 &v) { + return Matrix3( + Vector3(v.x, 0, 0), + Vector3( 0, v.y, 0), + Vector3( 0, 0, 1) ); + } + + std::string to_string() const { + return "(" + row_x().to_string() + ", " + + row_y().to_string() + ", " + + row_z().to_string() + ")"; + } + + Cairo::Matrix to_cairo() const { + return Cairo::Matrix( + m00, m10, m01, + m11, m20, m21 ); + } +}; + +typedef Matrix3 Matrix; + + +#endif diff --git a/c++/freetype/src/surface.cpp b/c++/freetype/src/surface.cpp new file mode 100644 index 0000000..31766c9 --- /dev/null +++ b/c++/freetype/src/surface.cpp @@ -0,0 +1,225 @@ + +#include "surface.h" + + +Surface::Surface(): + m_width(), m_height(), m_pitch(), m_origin() { } + +Surface::Surface(const Surface &): + m_width(), m_height(), m_pitch(), m_origin() { } + +void +Surface::reset() { + m_width = m_height = m_pitch = 0; + m_origin = NULL; +} + +void +Surface::mult_alpha() { + if (empty()) return; + const int h = height(); + const int w = width(); + const int dr = pitch() - width(); + Color *c = m_origin; + for(int r = 0; r < h; ++r, c += dr) + for(Color *end = c + w; c != end; ++c) + c->mult_alpha(); +} + +void +Surface::demult_alpha() { + if (empty()) return; + const int h = height(); + const int w = width(); + const int dr = pitch() - width(); + Color *c = m_origin; + for(int r = 0; r < h; ++r, c += dr) + for(Color *end = c + w; c != end; ++c) + c->demult_alpha(); +} + +Color +Surface::get_pixel(Real x, Real y) const { + // linear interpolation + if ( empty() + || x < 0 || x > width() - 1 + || y < 0 || y > height() - 1 ) + return Color(); + + int x0 = (int)floor(x), x1 = x0 + 1; + int y0 = (int)floor(y), y1 = y0 + 1; + Real px = x - x0, ppx = 1.0 - px; + Real py = y - y0, ppy = 1.0 - py; + + x0 = iclamp(x0, 0, width() - 1); + x1 = iclamp(x1, 0, width() - 1); + y0 = iclamp(y0, 0, height() - 1); + y1 = iclamp(y1, 0, height() - 1); + + Real k[] = { ppx*ppy, px*ppy, ppx*py, px*py }; + const Color *corners[] = { &row(y0)[x0], &row(y0)[x1], &row(y1)[x0], &row(y1)[x1] }; + Color sum; + for(int i = 0; i < 4; ++i) { + const Color &c = *corners[i]; + Real a = c.a * k[i]; + for(int j = 0; j < 3; ++j) + sum.channels[j] += c.channels[j]*a; + sum.a += a; + } + + if (sum.a <= real_precision) + return Color(); + + Real aa = 1.0/sum.a; + for(int j = 0; j < 3; ++j) + sum.channels[j] *= aa; + return sum; +} + +Color +Surface::get_pixel_premulted(Real x, Real y) const { + // linear interpolation + if ( empty() + || x < 0 || x > width() - 1 + || y < 0 || y > height() - 1 ) + return Color(); + + int x0 = (int)floor(x), x1 = x0 + 1; + int y0 = (int)floor(y), y1 = y0 + 1; + Real px = x - x0, ppx = 1.0 - px; + Real py = y - y0, ppy = 1.0 - py; + + x0 = iclamp(x0, 0, width() - 1); + x1 = iclamp(x1, 0, width() - 1); + y0 = iclamp(y0, 0, height() - 1); + y1 = iclamp(y1, 0, height() - 1); + + Color sum; + Real k[] = { ppx*ppy, px*ppy, ppx*py, px*py }; + const Color *corners[] = { &row(y0)[x0], &row(y0)[x1], &row(y1)[x0], &row(y1)[x1] }; + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + sum.channels[j] += corners[i]->channels[j]*k[i]; + return sum; +} + +void Surface::copy(int width, int height, const Color *src_origin, Color *dest_origin, int src_pitch, int dest_pitch) { +#ifndef NDEBUG + { // check source and destination ranges + assert(dest_origin); + assert(src_origin); + + Color *dest_begin = dest_origin + begin_offset(width, height, dest_pitch), + *dest_end = dest_origin + end_offset(width, height, dest_pitch); + const Color *src_begin = src_origin + begin_offset(width, height, src_pitch), + *src_end = src_origin + end_offset(width, height, src_pitch); + assert(src_end <= dest_begin || dest_end <= src_begin); + } +#endif + + if (!src_pitch) src_pitch = width; + if (!dest_pitch) dest_pitch = width; + + const Color *src_row = src_origin; + for( Color *dest_row = dest_origin, *dest_row_end = dest_row + height*dest_pitch; + dest_row != dest_row_end; + dest_row += dest_pitch, src_row += src_pitch ) + { + const Color *src_col = src_row; + for( Color *dest_col = dest_row, *dest_col_end = dest_col + width; + dest_col != dest_col_end; + ++dest_col, ++src_col ) + { *dest_col = *src_col; } + } +} + +Glib::RefPtr +Surface::to_pixbuf() const { + if (empty()) + return Glib::RefPtr(); + Glib::RefPtr pixbuf = Gdk::Pixbuf::create( + Gdk::COLORSPACE_RGB, true, 8, width(), height() ); + guint8 *pixels = pixbuf->get_pixels(); + int stride = pixbuf->get_rowstride(); + for(int r = 0; r < height(); ++r) { + const Color *src_row = row(r); + guint8 *pixel = pixels; + pixels += stride; + for(int c = 0; c < width(); ++c) { + const Color &color = src_row[c]; + *(pixel++) = (guint8)(color.b*255.9); + *(pixel++) = (guint8)(color.g*255.9); + *(pixel++) = (guint8)(color.r*255.9); + *(pixel++) = (guint8)(color.a*255.9); + } + } + return pixbuf; +} + +Cairo::RefPtr +Surface::to_cairo_surface(bool premulted) const { + if (empty()) + return Cairo::RefPtr(); + Cairo::RefPtr cairo_surface = + Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width(), height()); + cairo_surface->flush(); + unsigned char *pixels = cairo_surface->get_data(); + int stride = cairo_surface->get_stride(); + for(int r = 0; r < height(); ++r) { + const Color *src_row = row(r); + unsigned char *pixel = pixels; + pixels += stride; + for(int c = 0; c < width(); ++c) { + Color color = src_row[c]; + if (!premulted) color.mult_alpha(); + *(pixel++) = (guint8)(color.b*255.9); + *(pixel++) = (guint8)(color.g*255.9); + *(pixel++) = (guint8)(color.r*255.9); + *(pixel++) = (guint8)(color.a*255.9); + } + } + cairo_surface->mark_dirty(); + return cairo_surface; +} + + +void DataSurface::clear() { + if (Color *data = data_begin()) delete[] data; + reset(); +} + +void DataSurface::init(int width, int height, int pitch, const Color *src_origin, int src_pitch) { + Color *origin = NULL; + if (width > 0 && height > 0) { + if (!pitch) pitch = width; + + int count = data_count(width, height, pitch); + int offset = begin_offset(width, height, pitch); + Color *data = new Color[count]; + origin = data - offset; + + if (src_origin) + copy(width, height, src_origin, origin, src_pitch, pitch); + } else { + width = 0; + height = 0; + pitch = 0; + } + + clear(); + m_width = width; + m_height = height; + m_pitch = pitch; + m_origin = origin; +} + + +void AliasSurface::init(Color *origin, int width, int height, int pitch) { + clear(); + if (origin && width > 0 && height > 0) { + m_width = width; + m_height = height; + m_pitch = pitch ? pitch : width; + m_origin = origin; + } +} diff --git a/c++/freetype/src/surface.h b/c++/freetype/src/surface.h new file mode 100644 index 0000000..5d734da --- /dev/null +++ b/c++/freetype/src/surface.h @@ -0,0 +1,124 @@ +#ifndef SURFACE_H +#define SURFACE_H + + +#include +#include + +#include +#include + +#include "common.h" + + +class Surface: public Shared { +protected: + int m_width; + int m_height; + int m_pitch; + Color *m_origin; + + Surface(); + Surface(const Surface &); + Surface& operator= (const Surface &) { return *this; } + void reset(); + +public: + virtual ~Surface() { } + + inline int width() const { return m_width; } + inline int height() const { return m_height; } + inline int pitch() const { return m_pitch; } + inline int count() const { return m_width*m_height; } + inline bool empty() const { return !count(); } + + inline Color* origin() { return m_origin; } + inline const Color* origin() const { return m_origin; } + + inline int data_count() const { return data_count (width(), height(), pitch()); } + inline size_t data_size() const { return data_size (width(), height(), pitch()); } + inline int begin_offset() const { return begin_offset(width(), height(), pitch()); } + inline int end_offset() const { return end_offset (width(), height(), pitch()); } + + inline Color* data_begin() { return m_origin ? m_origin + begin_offset() : NULL; } + inline const Color* data_begin() const { return m_origin ? m_origin + begin_offset() : NULL; } + inline Color* data_end() { return m_origin ? m_origin + end_offset() : NULL; } + inline const Color* data_end() const { return m_origin ? m_origin + end_offset() : NULL; } + + inline Color* row(int index) + { assert(index >= 0 && index < m_height); return m_origin + index*m_pitch; } + inline const Color* row(int index) const + { assert(index >= 0 && index < m_height); return m_origin + index*m_pitch; } + + inline Color* operator[] (int index) { return row(index); } + inline const Color* operator[] (int index) const { return row(index); } + + inline void put_pixel(int x, int y, const Color &color) { + if (x >= 0 && y >= 0 && x < width() && y < height()) + row(y)[x] = color; + } + + void mult_alpha(); + void demult_alpha(); + Color get_pixel(Real x, Real y) const; + Color get_pixel_premulted(Real x, Real y) const; + + static inline int data_count(int width, int height, int pitch) + { return width + abs(pitch)*(height - 1); } + static inline size_t data_size(int width, int height, int pitch) + { return data_count(width, height, pitch)*sizeof(Color); } + static inline int begin_offset(int width, int height, int pitch) + { return pitch > 0 ? 0 : width - data_count(width, height, pitch); } + static inline int end_offset(int width, int height, int pitch) + { return pitch > 0 ? data_count(width, height, pitch) : width; } + + static void copy(int width, int height, const Color *src_origin, Color *dest_origin, int src_pitch = 0, int dest_pitch = 0); + + Glib::RefPtr to_pixbuf() const; + Cairo::RefPtr to_cairo_surface(bool premulted = false) const; +}; + + +class DataSurface: public Surface { +public: + explicit DataSurface(int width = 0, int height = 0, int pitch = 0, const Color *src_origin = NULL, int src_pitch = 0) + { init(width, height, pitch, src_origin, src_pitch); } + DataSurface(const Surface &other) + { *this = other; } + ~DataSurface() + { clear(); } + DataSurface& operator= (const Surface &other) + { init(other); return *this; } + + void clear(); + void init(int width, int height, int pitch = 0, const Color *src_origin = NULL, int src_pitch = 0); + inline void init(const Surface &other) + { init(other.width(), other.height(), other.pitch(), other.origin(), other.pitch()); } +}; + + +class AliasSurface: public Surface { +public: + AliasSurface() { } + AliasSurface(Color *origin, int width, int height, int pitch = 0) + { init(origin, width, height, pitch); } + AliasSurface(const AliasSurface &other) + { *this = other; } + AliasSurface(Surface &other) + { init(other); } + AliasSurface& operator= (const AliasSurface &other) + { init(other); return *this; } + + inline Color* origin() const { return m_origin; } + + void init(Color *origin, int width, int height, int pitch = 0); + + inline void clear() { reset(); } + inline void init(Surface &other) + { init(other.origin(), other.width(), other.height(), other.pitch()); } + inline void init(const AliasSurface &other) + { init(other.origin(), other.width(), other.height(), other.pitch()); } +}; + + +#endif diff --git a/c++/freetype/src/vector.h b/c++/freetype/src/vector.h new file mode 100644 index 0000000..16e70b0 --- /dev/null +++ b/c++/freetype/src/vector.h @@ -0,0 +1,421 @@ +#ifndef VECTOR_H +#define VECTOR_H + + +#include + +#include +#include +#include + +#include "common.h" + + +template +inline bool coord_less(const T &a, const T &b) + { return a < b; } + +template<> +inline bool coord_less(const Real &a, const Real &b) + { return real_less(a, b); } + + + +template +class VectorBase2T { +public: + typedef VectorBase2T SelfTypeArg; + typedef ST SelfType; + typedef LT LowerType; + + typedef SelfType Vector2; + typedef LowerType Coord; + + enum { Count = 2 }; + + union { + struct { Coord coords[Count]; }; + struct { Coord x, y; }; + }; + + inline explicit VectorBase2T(const Coord &x = Coord(), const Coord &y = Coord()): + x(x), y(y) { } + + inline SelfType perp() const + { return SelfType(-y, x); } + inline SelfType yx() const + { return SelfType(y, x); } +}; + + +template +class VectorBase3T { +public: + typedef VectorBase3T SelfTypeArg; + typedef ST SelfType; + typedef LT LowerType; + + typedef SelfType Vector3; + typedef LowerType Vector2; + typedef typename LowerType::Coord Coord; + + enum { Count = 3 }; + + union { + struct { Coord coords[Count]; }; + struct { Coord x, y, z; }; + }; + + inline explicit VectorBase3T(const Coord &x = Coord(), const Coord &y = Coord(), const Coord &z = Coord()): + x(x), y(y), z(z) { } + inline VectorBase3T(const Vector2 &v, const Coord &z): + x(v.x), y(v.y), z(z) { } + + inline SelfType cross(const SelfTypeArg &other) const + { return SelfType(y*other.z - z*other.y, z*other.x - x*other.z, x*other.y - y*other.x); } + + inline Vector2& vec2() + { return *(Vector2*)this; }; + inline const Vector2& vec2() const + { return *(const Vector2*)this; }; + + inline SelfType xzy() const + { return SelfType(x, z, y); } + inline SelfType zxy() const + { return SelfType(z, x, y); } + inline SelfType zyx() const + { return SelfType(z, y, x); } + inline SelfType yxz() const + { return SelfType(y, x, z); } + inline SelfType yzx() const + { return SelfType(y, z, x); } +}; + + +template +class VectorBase4T { +public: + typedef VectorBase4T SelfTypeArg; + typedef ST SelfType; + typedef LT LowerType; + + typedef SelfType Vector4; + typedef LowerType Vector3; + typedef typename LowerType::Vector2 Vector2; + typedef typename LowerType::Coord Coord; + + enum { Count = 4 }; + + union { + struct { Coord coords[Count]; }; + struct { Coord x, y, z, w; }; + }; + + inline explicit VectorBase4T(const Coord &x = Coord(), const Coord &y = Coord(), const Coord &z = Coord(), const Coord &w = Coord()): + x(x), y(y), z(z), w(w) { } + inline VectorBase4T(const Vector2 &v, const Coord &z, const Coord &w = Coord()): + x(v.x), y(v.y), z(z), w(w) { } + inline VectorBase4T(const Vector3 &v, const Coord &w): + x(v.x), y(v.y), z(v.z), w(w) { } + + inline Vector3& vec3() + { return *(Vector3*)this; }; + inline const Vector3& vec3() const + { return *(const Vector3*)this; }; + + inline Vector2& vec2() + { return *(Vector2*)this; }; + inline const Vector2& vec2() const + { return *(const Vector2*)this; }; +}; + + +template +class VectorT: public T { +public: + typedef VectorT SelfTypeArg; + typedef T ParentType; + using ParentType::Count; + using typename ParentType::Coord; + using typename ParentType::SelfType; + using ParentType::coords; + using ParentType::ParentType; // contructors + + + static inline bool coord_less(const Coord &a, const Coord &b) + { return ::coord_less(a, b); } + + + inline VectorT() { } + + inline explicit VectorT(const Coord *c) { + assert(c); + for(int i = 0; i < Count; ++i) coords[i] = c[i]; + } + + template + inline explicit VectorT(const VectorT &v) { + const int cnt = (int)TT::Count < (int)Count ? (int)TT::Count : (int)Count; + for(int i = 0; i < cnt; ++i) coords[i] = Coord(v.coords[i]); + for(int i = cnt; i < Count; ++i) coords[i] = Coord(); + } + + Coord& operator[] (int i) + { assert(i >= 0 && i < Count); return coords[i]; } + const Coord& operator[] (int i) const + { assert(i >= 0 && i < Count); return coords[i]; } + + inline bool operator< (const SelfTypeArg &other) const { + for(int i = 0; i < Count; ++i) + if (coord_less(coords[i], other.coords[i])) return true; else + if (coord_less(other.coords[i], coords[i])) return false; + return false; + } + + inline bool operator== (const SelfTypeArg &other) const { + for(int i = 0; i < Count; ++i) + if ( coord_less(coords[i], other.coords[i]) + || coord_less(other.coords[i], coords[i]) ) return false; + return true; + } + + inline bool operator!= (const SelfTypeArg &other) const + { return !(*(const SelfType*)this == other); } + + inline SelfType& operator+= (const SelfTypeArg &other) { + for(int i = 0; i < Count; ++i) coords[i] += other.coords[i]; + return *(SelfType*)this; + } + inline SelfType& operator-= (const SelfTypeArg &other) { + for(int i = 0; i < Count; ++i) coords[i] -= other.coords[i]; + return *(SelfType*)this; + } + inline SelfType& operator*= (const Coord &c) { + for(int i = 0; i < Count; ++i) coords[i] *= c; + return *(SelfType*)this; + } + inline SelfType operator- () const { + SelfType v; + for(int i = 0; i < Count; ++i) v.coords[i] = -coords[i]; + return v; + } + inline Coord operator* (const SelfTypeArg &other) const { + Coord r = Coord(); + for(int i = 0; i < Count; ++i) r += coords[i]*other.coords[i]; + return r; + } + + inline SelfType operator+ (const SelfTypeArg &other) const + { return SelfType(*this) += other; } + inline SelfType operator- (const SelfTypeArg &other) const + { return SelfType(*this) -= other; } + inline SelfType operator* (const Coord &c) const + { return SelfType(*this) *= c; } + inline Coord square() const + { return *this * *this; } + + inline static SelfType& cast(Coord *c) + { return *(SelfType*)c; } + inline static const SelfType& cast(const Coord *c) + { return *(const SelfType*)c; } + + std::string to_string() const { + std::stringstream stream; + stream << "(" << coords[0]; + for(int i = 1; i < Count; ++i) stream << ", " << coords[i]; + stream << ")"; + return stream.str(); + } +}; + + +template +class VectorFT: public VectorT { +public: + typedef VectorT ParentType; + using typename ParentType::Coord; + using typename ParentType::SelfType; + using ParentType::Count; + using ParentType::coords; + using ParentType::ParentType; // contructors + + + inline SelfType& operator/= (const Coord &c) + { return *this *= Coord(1)/c; } + inline SelfType operator/ (const Coord &c) const + { return SelfType(*this) /= c; } + inline Coord length() const + { return Coord(sqrt(ParentType::square())); } + + inline SelfType normalized() const { + Coord len = length(); + return ParentType::coord_less(Coord(), len) + ? *this / len : SelfType(); + } + inline SelfType normalize() const + { *this = normalized(); return *(SelfType)this; } + + inline SelfType persp_divide() const + { return *this / coords[Count - 1]; } +}; + +template +class PairT { +public: + typedef T Vector; + Vector p0; + Vector p1; + + explicit inline PairT(const Vector &p = Vector()): + p0(p), p1(p) { } + inline PairT(const Vector &p0, const Vector &p1): + p0(p0), p1(p1) { } + + template + inline explicit PairT(const VectorT &p): + p0(p), p1(p) { } + template + inline explicit PairT(const VectorT &p0, const VectorT &p1): + p0(p0), p1(p1) { } + + template + inline explicit PairT(const PairT &other): + PairT(other.p0, other.p1) { } + + inline bool operator< (const PairT &other) const { + return p0 < other.p0 ? true + : other.p0 < p0 ? false + : p1 < other.p1; + } + inline bool operator== (const PairT &other) const + { return !(*this < other) && !(other < *this); } + inline bool operator!= (const PairT &other) const + { return *this < other || other < *this; } + + + inline Vector distance() const + { return p1 - p0; } + inline Vector size() const + { return distance(); } + + inline bool empty() const { + for(int i = 0; i < Vector::Count; ++i) + if (!Vector::coord_less(p0[i], p1[i])) + return true; + return false; + } + inline PairT& sort() { + for(int i = 0; i < Vector::Count; ++i) + if (p0[i] < p1[i]) + std::swap(p0[i], p1[i]); + return *this; + } + inline PairT sorted() const + { return PairT(*this).sort(); } + + inline PairT& expand(const Vector &p) { + for(int i = 0; i < Vector::Count; ++i) { + if (p[i] < p0[i]) p0[i] = p[i]; + if (p1[i] < p[i]) p1[i] = p[i]; + } + return *this; + } + inline PairT expanded(const Vector &p) const + { return PairT(*this).expand(p); } + + inline PairT& inflate(const Vector &p) { + if (empty()) return *this; + p0 -= p; p1 += p; + return *this; + } + inline PairT inflated(const Vector &p) const + { return PairT(*this).inflate(p); } +}; + + + +template +class Vectors { +public: + typedef T Type; + + class Vector2: public VectorT< VectorBase2T > { + public: + typedef VectorT< VectorBase2T > ParentType; + using ParentType::ParentType; // constructors + }; + class Vector3: public VectorT< VectorBase3T > { + public: + typedef VectorT< VectorBase3T > ParentType; + using ParentType::ParentType; // constructors + }; + class Vector4: public VectorT< VectorBase4T > { + public: + typedef VectorT< VectorBase4T > ParentType; + using ParentType::ParentType; // constructors + }; +}; + + +template +class VectorsFloat { +public: + typedef T Type; + + class Vector2: public VectorFT< VectorBase2T > { + public: + typedef VectorFT< VectorBase2T > ParentType; + using ParentType::ParentType; // constructors + }; + class Vector3: public VectorFT< VectorBase3T > { + public: + typedef VectorFT< VectorBase3T > ParentType; + using ParentType::ParentType; // constructors + }; + class Vector4: public VectorFT< VectorBase4T > { + public: + typedef VectorFT< VectorBase4T > ParentType; + using ParentType::ParentType; // constructors + }; +}; + + +typedef VectorsFloat::Vector2 Vector2; +typedef VectorsFloat::Vector3 Vector3; +typedef VectorsFloat::Vector4 Vector4; + +typedef VectorsFloat::Vector2 IntVector2; +typedef VectorsFloat::Vector3 IntVector3; +typedef VectorsFloat::Vector4 IntVector4; + +typedef VectorsFloat::Vector2 LongIntVector2; +typedef VectorsFloat::Vector3 LongIntVector3; +typedef VectorsFloat::Vector4 LongIntVector4; + +typedef VectorsFloat::Vector2 UIntVector2; +typedef VectorsFloat::Vector3 UIntVector3; +typedef VectorsFloat::Vector4 UIntVector4; + +typedef VectorsFloat::Vector2 ULongIntVector2; +typedef VectorsFloat::Vector3 ULongIntVector3; +typedef VectorsFloat::Vector4 ULongIntVector4; + + +typedef PairT Pair2; +typedef PairT Pair3; +typedef PairT Pair4; + +typedef PairT IntPair2; +typedef PairT IntPair3; +typedef PairT IntPair4; + +typedef PairT LongIntPair2; +typedef PairT LongIntPair3; +typedef PairT LongIntPair4; + +typedef PairT ULongIntPair2; +typedef PairT ULongIntPair3; +typedef PairT ULongIntPair4; + + +#endif diff --git a/c++/freetype/src/view.cpp b/c++/freetype/src/view.cpp new file mode 100644 index 0000000..521864c --- /dev/null +++ b/c++/freetype/src/view.cpp @@ -0,0 +1,177 @@ +#include "view.h" + +#include + + +void +View::Point::draw( + const Cairo::RefPtr& context, + View &view ) +{ + const Real ps = view.get_pixel_size(); + context->save(); + context->arc(position.x, position.y, radius*ps, 0.0, 2.0*M_PI); + context->set_source_rgba(color.r, color.g, color.b, color.a); + context->fill_preserve(); + context->set_source_rgba(0.0, 0.0, 0.0, color.a); + context->set_line_width(selected ? 2.0*ps : ps); + context->stroke(); + context->restore(); +} + + +View::View(): + dragging() +{ + set_events( + Gdk::POINTER_MOTION_MASK + | Gdk::BUTTON_PRESS_MASK + | Gdk::BUTTON_RELEASE_MASK ); + + signal_draw_view.connect(sigc::mem_fun(*this, &View::on_draw_view)); + signal_point_motion.connect(sigc::mem_fun(*this, &View::on_point_motion)); + signal_point_changed.connect(sigc::mem_fun(*this, &View::on_point_changed)); + signal_transform_changed.connect(sigc::mem_fun(*this, &View::on_transform_changed)); +} + +View::~View() { } + +Real +View::get_pixel_size() const + { return 1.0/sqrt(fabs(transform.m00 * transform.m11)); } + +Pair2 +View::get_bounds() const { + Matrix m = transform_from_pixels(); + return Pair2( + (m*Vector3(0 , 0 , 1)).vec2(), + (m*Vector3(get_width(), get_height(), 1)).vec2() ); +} + +Matrix +View::transform_to_pixels() const { + Matrix matrix = transform; + matrix.row_z().vec2() += Vector2(get_width(), get_height())*0.5; + return matrix; +} + +Matrix +View::transform_from_pixels() const + { return transform_to_pixels().inverted(); } + +bool +View::on_draw(const Cairo::RefPtr& context) { + context->save(); + + const Real border_size = 1.0; + const Real border_margin = 2.0; + + context->save(); + context->rectangle(0, 0, get_width(), get_height()); + context->set_source_rgba(0, 0, 0, 1); + context->fill(); + context->rectangle( + border_margin, border_margin, + get_width() - 2*border_margin, + get_height() - 2*border_margin ); + context->set_line_width(border_size); + context->set_source_rgba(1, 1, 0, 1); + context->stroke(); + context->restore(); + + context->transform( transform_to_pixels().to_cairo() ); + signal_draw_view(context); + for(PointList::const_iterator i = points.begin(); i != points.end(); ++i) + (*i)->draw(context, *this); + context->restore(); + return true; +} + +bool +View::on_motion_notify_event(GdkEventMotion *event) { + Vector2 prev_mouse_position = mouse_position; + mouse_position = Vector2(event->x, event->y); + + Matrix to_pixels = transform_to_pixels(); + Matrix from_pixels = transform_from_pixels(); + + if (dragging) { + if (selected_point) { + selected_point->position = (from_pixels*Vector3(mouse_position + selected_point_offset, 1)).vec2(); + point_motion(selected_point); + queue_draw(); + } + return true; + } + + PointPtr point; + for(PointList::iterator i = points.begin(); i != points.end(); ++i) { + (*i)->selected = false; + Vector2 point_position = (to_pixels*Vector3((*i)->position, 1)).vec2(); + Vector2 offset = point_position - mouse_position; + if (offset.square() <= (*i)->radius * (*i)->radius) { + point = *i; + selected_point_offset = offset; + } + } + if (point) + point->selected = true; + if (selected_point != point) { + selected_point = point; + queue_draw(); + } + + if (event->state & Gdk::BUTTON2_MASK) { + // translate + transform.row_z() += Vector3(mouse_position - prev_mouse_position); + queue_draw(); + } + + if (event->state & Gdk::BUTTON3_MASK) { + // zoom + Real scale = pow(2.0, (prev_mouse_position.y - mouse_position.y)/50.0); // zoom x2 by motion to 50 pixels + Vector2 center = (transform.inverted() * Vector3(0, 0, 1)).vec2(); + transform *= Matrix().scaling(Vector2(scale, scale)); + transform.row_z() = Vector3(0, 0, 1); + transform.row_z() = transform * Vector3(-center, 1); + queue_draw(); + } + + return true; +} + +bool +View::on_button_press_event(GdkEventButton *event) { + if (event->button == 1) + dragging = true; + return true; +} + +bool +View::on_button_release_event(GdkEventButton *event) { + if (event->button == 1) { + dragging = false; + if (selected_point) + point_changed(selected_point); + } + if (event->button == 2 || event->button == 3) + signal_transform_changed(); + return true; +} + + +void +View::on_draw_view(const Cairo::RefPtr&) + { } + +void +View::on_point_motion(const PointPtr&) + { } + +void +View::on_point_changed(const PointPtr&) + { } + +void +View::on_transform_changed() + { } diff --git a/c++/freetype/src/view.h b/c++/freetype/src/view.h new file mode 100644 index 0000000..a5070f8 --- /dev/null +++ b/c++/freetype/src/view.h @@ -0,0 +1,86 @@ +#ifndef VIEW_H +#define VIEW_H + + +#include + +#include + +#include "common.h" +#include "surface.h" +#include "matrix.h" + + +class View: public Gtk::DrawingArea { +public: + class Point: public Shared { + public: + Vector2 position; + Color color; + Real radius; + bool selected; + + inline explicit Point( + const Vector2 &position = Vector2(), + const Color &color = Color(1, 1, 0, 1), + Real radius = 5.0, + bool selected = false + ): + position(position), + color(color), + radius(radius), + selected(selected) + { } + + virtual void draw( + const Cairo::RefPtr& context, + View &view ); + }; + + typedef RefPtr PointPtr; + typedef std::vector PointList; + +public: + Matrix transform; + PointList points; + +private: + bool dragging; + PointPtr selected_point; + Vector2 selected_point_offset; + Vector2 mouse_position; // relative to center of widget + +public: + View(); + virtual ~View(); + + sigc::signal&> signal_draw_view; + sigc::signal signal_point_motion; + sigc::signal signal_point_changed; + sigc::signal signal_transform_changed; + + void point_motion(const PointPtr &point) + { signal_point_motion(point); } + void point_changed(const PointPtr &point) + { point_motion(point); signal_point_changed(point); } + + Real get_pixel_size() const; + Pair2 get_bounds() const; + + Matrix transform_to_pixels() const; + Matrix transform_from_pixels() const; + +protected: + bool on_draw(const Cairo::RefPtr &context) override; + bool on_motion_notify_event(GdkEventMotion *event) override; + bool on_button_press_event(GdkEventButton *event) override; + bool on_button_release_event(GdkEventButton *event) override; + + virtual void on_draw_view(const Cairo::RefPtr &context); + virtual void on_point_motion(const PointPtr &point); + virtual void on_point_changed(const PointPtr &point); + virtual void on_transform_changed(); +}; + + +#endif