#include <algorithm>
#include <iostream>
#include <glib.h>
#include <glibmm.h>
#include <freetype2/ft2build.h>
#include FT_BITMAP_H
#include <pango/pango.h>
#include <pango/pango-renderer.h>
#include <pango/pangoft2.h>
#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<Cairo::Context> &context) {
if (surface) {
context->save();
context->transform(transform_from_pixels().to_cairo());
context->set_source(surface, 0, 0);
context->paint();
context->restore();
}
}