From f77c6cecffec4996a6df61ecec2aa8f67e211e3c Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Mar 04 2020 16:35:59 +0000 Subject: freetype: improvements --- diff --git a/c++/freetype/src/freetypeview.cpp b/c++/freetype/src/freetypeview.cpp index ca83a72..3717311 100644 --- a/c++/freetype/src/freetypeview.cpp +++ b/c++/freetype/src/freetypeview.cpp @@ -12,20 +12,48 @@ #include "freetypeview.h" -FreeTypeView::FreeTypeView() { +FreeTypeView::FreeTypeView(): + p0(new View::Point( Vector2(0, 0))), + px(new View::Point( Vector2(1, 0))), + py(new View::Point( Vector2(0, 1))) +{ + prev_p0 = p0->position; + transform *= Matrix().scaling(Vector2(50, 50)); + points.push_back(p0); + points.push_back(px); + points.push_back(py); + params.family = "Ani"; params.bold = false; params.italic = false; params.size = 12; + params.hor_spacing = 2; + params.vert_spacing = 20; + + params.hinting = false; + params.antialiasing = true; + params.matrix = Matrix().translation(Vector2(15.5, 18.4)); params.color = Color(1, 1, 0, 1); - params.text = "Hello World! 123456"; + params.text = + "Lorem ipsum dolor sit amet, consectetur adipisici elit," + "sed eiusmod tempor incidunt ut labore et dolore magna aliqua." + "Ut enim ad minim veniam, quis nostrud exercitation ullamco " + "laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure" + "reprehenderit in voluptate velit esse cillum dolore eu fugiat " + "nulla pariatur. Excepteur sint obcaecat cupiditat non proident, " + "sunt in culpa qui officia deserunt mollit anim id est laborum."; + //params.text = "1\n2\n3\n"; + + params.alignment = 1; + params.justify = true; + params.wrap_width = 600; Glib::signal_timeout().connect( sigc::mem_fun(*this, &FreeTypeView::on_timeout), - 20, + 50, Glib::PRIORITY_DEFAULT_IDLE ); } @@ -35,30 +63,43 @@ FreeTypeView::~FreeTypeView() bool FreeTypeView::on_timeout() { if (!is_visible()) return false; - params.matrix.m20 += 0.01; - update_surface(); + p0->position.x += 0.001; + point_motion(p0); return true; } void FreeTypeView::update_surface() { this->surface.clear(); + + Matrix in_matrix( + Vector3(px->position - p0->position)/50, + Vector3(py->position - p0->position)/50, + Vector3(p0->position, 1) ); + params.matrix = transform_to_pixels() * in_matrix; + const int width = get_allocated_width(); const int height = get_allocated_height(); if (width <= 0 || height <= 0) return; + const Real hor_spacing = params.hor_spacing > real_precision ? params.hor_spacing : 1; + Matrix matrix = params.matrix; - Real det2 = matrix.m00*matrix.m11 - matrix.m01*matrix.m10; - if (det2 <= real_precision_sqr) + Matrix2 glyph_matrix( + matrix.row_x().vec2(), + matrix.row_y().vec2() ); + matrix.row_x() *= hor_spacing; + + if (fabs(glyph_matrix.det()) <= real_precision_sqr) return; - Real scale = sqrt(det2); - matrix.row_x() /= scale; - matrix.row_y() /= scale; - Real size = params.size*scale; + + + const Real size = params.size; + const Real wrap_width = params.wrap_width/hor_spacing; // init - PangoFontMap *font_map = lab_pango_font_map_new(params.hinting); + PangoFontMap *font_map = lab_pango_font_map_new(params.hinting, params.antialiasing); PangoContext *context = pango_font_map_create_context(font_map); // load font @@ -73,14 +114,39 @@ FreeTypeView::update_surface() { // create layout PangoLayout *layout = pango_layout_new(context); pango_layout_set_font_description(layout, font_desc); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_width(layout, wrap_width > real_precision ? (int)round(wrap_width *PANGO_SCALE) : -1); + pango_layout_set_alignment(layout, + params.alignment < 0 ? PANGO_ALIGN_LEFT : + params.alignment > 0 ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_CENTER ); + pango_layout_set_justify(layout, params.justify); + if (params.vert_spacing > real_precision) + pango_layout_set_spacing(layout, (int)round(params.vert_spacing*PANGO_SCALE)); pango_layout_set_text(layout, params.text.c_str(), (int)params.text.size()); + // move origin + PangoRectangle rect; + pango_layout_get_extents(layout, nullptr, &rect); + const IntVector2 offset_to_origin_int( + params.origin.x < 0 ? 0 : + params.origin.x > 0 ? (rect.x + rect.width) : (rect.x + rect.width)/2, + params.origin.y < 0 ? 0 : + params.origin.y > 0 ? (rect.y + rect.height) : (rect.y + rect.height)/2 ); + const Vector2 offset_to_origin( + offset_to_origin_int.x/Real(PANGO_SCALE), + offset_to_origin_int.y/Real(PANGO_SCALE) ); + matrix *= Matrix().translation(-offset_to_origin); + // reneder DataSurface surface(width, height); - PangoRenderer *renderer = lab_pango_renderer_new(); - lab_pango_renderer_set_surface((LabPangoRenderer*)renderer, &surface); - lab_pango_renderer_set_matrix((LabPangoRenderer*)renderer, ¶ms.matrix); - lab_pango_renderer_set_color((LabPangoRenderer*)renderer, ¶ms.color); + LabPangoRendererParams rp; + rp.surface = &surface; + rp.matrix = matrix; + rp.glyph_matrix = glyph_matrix; + rp.color = params.color; + rp.hinting = params.hinting; + rp.antialiasing = params.antialiasing; + PangoRenderer *renderer = lab_pango_renderer_new(&rp); pango_renderer_draw_layout(renderer, layout, 0, 0); // free pango @@ -97,7 +163,28 @@ FreeTypeView::update_surface() { } void +FreeTypeView::on_point_motion(const View::PointPtr &point) { + if (point == p0) { + px->position += p0->position - prev_p0; + py->position += p0->position - prev_p0; + prev_p0 = p0->position; + } + update_surface(); +} + +void FreeTypeView::on_draw_view(const Cairo::RefPtr &context) { + const Real ps = get_pixel_size(); + + context->save(); + context->set_line_width(ps); + + context->set_source_rgba(0, 0, 1, 1); + context->move_to(px->position.x, px->position.y); + context->line_to(p0->position.x, p0->position.y); + context->line_to(py->position.x, py->position.y); + context->stroke(); + if (surface) { context->save(); context->transform(transform_from_pixels().to_cairo()); @@ -105,5 +192,6 @@ FreeTypeView::on_draw_view(const Cairo::RefPtr &context) { context->paint(); context->restore(); } + context->restore(); } diff --git a/c++/freetype/src/freetypeview.h b/c++/freetype/src/freetypeview.h index efacb19..bdc2c60 100644 --- a/c++/freetype/src/freetypeview.h +++ b/c++/freetype/src/freetypeview.h @@ -13,15 +13,38 @@ public: Real size; bool hinting; + bool antialiasing; + int alignment; + bool justify; + Real wrap_width; + + Real hor_spacing; + Real vert_spacing; + + IntVector2 origin; Matrix matrix; Color color; std::string text; - Params(): bold(), italic(), size(), hinting() { } + Params(): + bold(), + italic(), + size(), + hinting(), + antialiasing(), + alignment(), + justify(), + wrap_width(), + hor_spacing(), + vert_spacing() + { } }; + View::PointPtr p0, px, py; + Vector2 prev_p0; + Params params; Cairo::RefPtr surface; @@ -31,6 +54,7 @@ public: bool on_timeout(); void update_surface(); + void on_point_motion(const View::PointPtr &point); void on_draw_view(const Cairo::RefPtr &context); }; diff --git a/c++/freetype/src/labpangorenderer.cpp b/c++/freetype/src/labpangorenderer.cpp index 2347f64..09f9fdd 100644 --- a/c++/freetype/src/labpangorenderer.cpp +++ b/c++/freetype/src/labpangorenderer.cpp @@ -16,9 +16,7 @@ struct _LabPangoRenderer { PangoRenderer parent_instance; - Surface *surface; - Color color; - Matrix matrix; + LabPangoRendererParams params; }; struct _LabPangoRendererClass { @@ -60,6 +58,34 @@ put_ft_bitmap(Surface &surface, FT_Bitmap &bitmap, int x, int y, const Color &co static void +put_ft_bitmap_mono(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_row = bitmap.buffer + by*bitmap.pitch; + const int src_row_end = bx + w; + + for(const Color *end = dst_pixel + pitch*h; dst_pixel != end; dst_pixel += dst_row_step, src_row += bitmap.pitch) + for(int src_x = bx; src_x < src_row_end; ++dst_pixel, ++src_x) + if (src_row[src_x/8] & (128 >> (src_x%8))) + *dst_pixel = color; +} + + +static void lab_pango_renderer_draw_glyph( PangoRenderer *renderer, PangoFont *font, @@ -67,30 +93,54 @@ lab_pango_renderer_draw_glyph( double x, double y ) { - LabPangoRenderer *lab_renderer = (LabPangoRenderer*)renderer; - if (!lab_renderer->surface) + const LabPangoRendererParams ¶ms = ((LabPangoRenderer*)renderer)->params; + if (!params.surface) return; - Vector3 pos = lab_renderer->matrix*Vector3(x, y, 1); + Vector3 pos = params.matrix*Vector3(x, y, 1); const int fx = (int)round(pos.x*64); const int fy = (int)round(pos.y*64); - const int ix = fx / 64; - const int iy = fy / 64; + int ix = fx / 64; + int iy = fy / 64; + FT_Vector vec = {}; vec.x = fx % 64; vec.y = fy % 64; + if (params.hinting) { + if (vec.x >= 32) ++ix; + if (vec.y >= 32) ++iy; + vec.x = 0; + vec.y = 0; + } + vec.y = -vec.y; + + FT_Matrix mat = {}; + mat.xx = (int)round( params.glyph_matrix.m00*65536); + mat.xy = (int)round(-params.glyph_matrix.m10*65536); + mat.yx = (int)round(-params.glyph_matrix.m01*65536); + mat.yy = (int)round( params.glyph_matrix.m11*65536); + 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_ft_bitmap( - *lab_renderer->surface, - face->glyph->bitmap, - face->glyph->bitmap_left + ix, - iy - face->glyph->bitmap_top, - lab_renderer->color ); + FT_Set_Transform(face, &mat, &vec); + if (!FT_Load_Glyph(face, glyph, params.hinting ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING)) { + if (!FT_Render_Glyph(face->glyph, params.antialiasing ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO)) { + if (params.antialiasing) { + put_ft_bitmap( + *params.surface, + face->glyph->bitmap, + face->glyph->bitmap_left + ix, + iy - face->glyph->bitmap_top, + params.color ); + } else { + put_ft_bitmap_mono( + *params.surface, + face->glyph->bitmap, + face->glyph->bitmap_left + ix, + iy - face->glyph->bitmap_top, + params.color ); + } } } pango_fc_font_unlock_face((PangoFcFont*)font); @@ -99,9 +149,8 @@ lab_pango_renderer_draw_glyph( static void lab_pango_renderer_init(LabPangoRenderer *renderer) { - memset( renderer + sizeof(renderer->parent_instance), 0, - sizeof(*renderer) - sizeof(renderer->parent_instance) ); - renderer->matrix = Matrix(); + memset(&renderer->params, 0, sizeof(renderer->params)); + renderer->params = LabPangoRendererParams(); } static void @@ -113,53 +162,40 @@ lab_pango_renderer_class_init(LabPangoRendererClass *klass) static void -lab_pango_fc_pattern_unset_hinting(FcPattern *pattern, gpointer) - { FcPatternDel(pattern, FC_HINTING); } - -static void -lab_pango_fc_pattern_set_hinting(FcPattern *pattern, gpointer) { +lab_pango_fc_pattern_set_substitute(FcPattern *pattern, gpointer data) { + const int &args = *(const int*)data; FcPatternDel(pattern, FC_HINTING); - FcPatternAddBool(pattern, FC_HINTING, FcFalse); + FcPatternDel(pattern, FC_ANTIALIAS); + FcPatternAddBool(pattern, FC_HINTING, (args & 1) ? FcTrue : FcFalse); + FcPatternAddBool(pattern, FC_ANTIALIAS, (args & 2) ? FcTrue : FcFalse); } PangoRenderer* -lab_pango_renderer_new() - { return (PangoRenderer*)g_object_new(LAB_TYPE_PANGO_RENDERER, nullptr); } - -void -lab_pango_renderer_set_matrix(LabPangoRenderer *renderer, Matrix *matrix) - { renderer->matrix = matrix ? *matrix : Matrix(); } - -void -lab_pango_renderer_get_matrix(LabPangoRenderer *renderer, Matrix *matrix) - { if (matrix) *matrix = renderer->matrix; } - -void -lab_pango_renderer_set_color(LabPangoRenderer *renderer, Color *color) - { renderer->color = color ? *color : Color(); } +lab_pango_renderer_new(LabPangoRendererParams *params) { + PangoRenderer *renderer = (PangoRenderer*)g_object_new(LAB_TYPE_PANGO_RENDERER, nullptr); + lab_pango_renderer_set_params((LabPangoRenderer*)renderer, params); + return renderer; +} void -lab_pango_renderer_get_color(LabPangoRenderer *renderer, Color *color) - { if (color) *color = renderer->color; } +lab_pango_renderer_set_params(LabPangoRenderer *renderer, LabPangoRendererParams *params) + { if (params) renderer->params = *params; } void -lab_pango_renderer_set_surface(LabPangoRenderer *renderer, Surface *surface) - { renderer->surface = surface; } - -Surface* -lab_pango_renderer_get_surface(LabPangoRenderer *renderer) - { return renderer->surface; } +lab_pango_renderer_get_params(LabPangoRenderer *renderer, LabPangoRendererParams *params) + { if (params) *params = renderer->params; } PangoFontMap* -lab_pango_font_map_new(bool hinting) { +lab_pango_font_map_new(bool hinting, bool antialiasing) { + static int args[4] { 0, 1, 2, 3 }; // to avoid encoding of int into pointer + PangoFontMap *font_map = pango_ft2_font_map_new(); pango_ft2_font_map_set_default_substitute( (PangoFT2FontMap*)font_map, - hinting ? &lab_pango_fc_pattern_set_hinting - : &lab_pango_fc_pattern_unset_hinting, - nullptr, + lab_pango_fc_pattern_set_substitute, + args + (hinting ? 1 : 0) + (antialiasing ? 2 : 0), nullptr ); return font_map; } diff --git a/c++/freetype/src/labpangorenderer.h b/c++/freetype/src/labpangorenderer.h index 71b647a..5887de7 100644 --- a/c++/freetype/src/labpangorenderer.h +++ b/c++/freetype/src/labpangorenderer.h @@ -4,6 +4,7 @@ #include +#include "matrix.h" class Surface; class Matrix3; @@ -21,20 +22,24 @@ typedef struct _LabPangoRendererClass LabPangoRendererClass; #define LAB_IS_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LAB_TYPE_PANGO_RENDERER)) #define LAB_PANGO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LAB_TYPE_PANGO_RENDERER, LabPangoRendererClass)) +struct LabPangoRendererParams { + Surface *surface; + Matrix3 matrix; + Matrix2 glyph_matrix; + Color color; + bool hinting; + bool antialiasing; + LabPangoRendererParams(): + surface(), hinting(), antialiasing() { } +}; -PangoRenderer* lab_pango_renderer_new(); -void lab_pango_renderer_set_matrix(LabPangoRenderer *renderer, Matrix3 *matrix); -void lab_pango_renderer_get_matrix(LabPangoRenderer *renderer, Matrix3 *matrix); +PangoRenderer* lab_pango_renderer_new(LabPangoRendererParams *params); -void lab_pango_renderer_set_color(LabPangoRenderer *renderer, Color *color); -void lab_pango_renderer_get_color(LabPangoRenderer *renderer, Color *color); +void lab_pango_renderer_set_params(LabPangoRenderer *renderer, LabPangoRendererParams *params); +void lab_pango_renderer_get_params(LabPangoRenderer *renderer, LabPangoRendererParams *params); -void lab_pango_renderer_set_surface(LabPangoRenderer *renderer, Surface *surface); -Surface* lab_pango_renderer_get_surface(LabPangoRenderer *renderer); - - -PangoFontMap* lab_pango_font_map_new(bool hinting); +PangoFontMap* lab_pango_font_map_new(bool hinting, bool antialiasing); G_END_DECLS diff --git a/c++/freetype/src/matrix.h b/c++/freetype/src/matrix.h index be6a537..942b0b3 100644 --- a/c++/freetype/src/matrix.h +++ b/c++/freetype/src/matrix.h @@ -8,6 +8,30 @@ +class Matrix2 { +public: + union { + struct { + Real m00, m01, + m10, m11; + }; + struct { Real m[2][2]; }; + struct { Real a[4]; }; + }; + + inline explicit Matrix2( + const Vector2 &x = Vector2(1, 0), + const Vector2 &y = Vector2(0, 1) + ): + m00(x.x), m01(x.y), + m10(y.x), m11(y.y) + { } + + inline Real det() const + { return m00*m11 - m01*m10; } +}; + + class Matrix3 { public: union {