|
|
b8976b |
|
|
|
b8976b |
#include <algorithm></algorithm>
|
|
|
b8976b |
#include <iostream></iostream>
|
|
|
b8976b |
|
|
|
b8976b |
#include <glib.h></glib.h>
|
|
|
b8976b |
#include <glibmm.h></glibmm.h>
|
|
|
b8976b |
|
|
|
b8976b |
#include <freetype2 ft2build.h=""></freetype2>
|
|
|
b8976b |
#include FT_BITMAP_H
|
|
|
b8976b |
#include <pango pango.h=""></pango>
|
|
|
b8976b |
#include <pango pango-renderer.h=""></pango>
|
|
|
b8976b |
#include <pango pangoft2.h=""></pango>
|
|
|
b8976b |
|
|
|
b8976b |
#include "surface.h"
|
|
|
b8976b |
#include "log.h"
|
|
|
b8976b |
|
|
|
b8976b |
#include "freetypeview.h"
|
|
|
b8976b |
|
|
|
b8976b |
|
|
|
b8976b |
static void put_bitmap(Surface &surface, FT_Bitmap &bitmap, int x, int y, const Color &color);
|
|
|
b8976b |
|
|
|
b8976b |
|
|
|
b8976b |
// my pango renderer
|
|
|
b8976b |
|
|
|
b8976b |
#define MY_TYPE_PANGO_RENDERER (my_pango_renderer_get_type ())
|
|
|
b8976b |
|
|
|
b8976b |
struct _MyPangoRenderer {
|
|
|
b8976b |
PangoRenderer parent_instance;
|
|
|
b8976b |
Surface *surface;
|
|
|
b8976b |
ColorReal color[4];
|
|
|
b8976b |
};
|
|
|
b8976b |
|
|
|
b8976b |
struct _MyPangoRendererClass {
|
|
|
b8976b |
PangoRendererClass parent_class;
|
|
|
b8976b |
};
|
|
|
b8976b |
|
|
|
b8976b |
typedef _MyPangoRenderer MyPangoRenderer;
|
|
|
b8976b |
typedef _PangoRendererClass MyPangoRendererClass;
|
|
|
b8976b |
|
|
|
b8976b |
#define PANGO_FT2_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_FT2_RENDERER, PangoFT2RendererClass))
|
|
|
b8976b |
#define PANGO_IS_FT2_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_FT2_RENDERER))
|
|
|
b8976b |
#define PANGO_FT2_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_FT2_RENDERER, PangoFT2RendererClass))
|
|
|
b8976b |
|
|
|
b8976b |
G_DEFINE_TYPE(MyPangoRenderer, my_pango_renderer, PANGO_TYPE_RENDERER)
|
|
|
b8976b |
|
|
|
b8976b |
static void
|
|
|
b8976b |
my_pango_renderer_draw_glyph(
|
|
|
b8976b |
PangoRenderer *renderer,
|
|
|
b8976b |
PangoFont *font,
|
|
|
b8976b |
PangoGlyph glyph,
|
|
|
b8976b |
double x,
|
|
|
b8976b |
double y )
|
|
|
b8976b |
{
|
|
|
b8976b |
MyPangoRenderer *my_renderer = (MyPangoRenderer*)renderer;
|
|
|
b8976b |
if (!my_renderer->surface)
|
|
|
b8976b |
return;
|
|
|
b8976b |
const Color &color = *(const Color*)my_renderer->color;
|
|
|
b8976b |
|
|
|
b8976b |
const int fx = (int)round(x*64);
|
|
|
b8976b |
const int fy = (int)round(y*64);
|
|
|
b8976b |
const int ix = fx / 64;
|
|
|
b8976b |
const int iy = fy / 64;
|
|
|
b8976b |
FT_Vector vec = {};
|
|
|
b8976b |
vec.x = fx % 64;
|
|
|
b8976b |
vec.y = fy % 64;
|
|
|
b8976b |
|
|
|
b8976b |
FT_Face face = pango_fc_font_lock_face((PangoFcFont*)font);
|
|
|
b8976b |
FT_Set_Transform(face, nullptr, &vec);
|
|
|
b8976b |
if (!FT_Load_Glyph(face, glyph, FT_LOAD_NO_HINTING)) {
|
|
|
b8976b |
if (!FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL)) {
|
|
|
b8976b |
put_bitmap(
|
|
|
b8976b |
*my_renderer->surface,
|
|
|
b8976b |
face->glyph->bitmap,
|
|
|
b8976b |
face->glyph->bitmap_left + ix,
|
|
|
b8976b |
iy - face->glyph->bitmap_top,
|
|
|
b8976b |
color );
|
|
|
b8976b |
}
|
|
|
b8976b |
}
|
|
|
b8976b |
pango_fc_font_unlock_face((PangoFcFont*)font);
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
static void
|
|
|
b8976b |
my_pango_renderer_draw_trapezoid(
|
|
|
b8976b |
PangoRenderer *renderer,
|
|
|
b8976b |
PangoRenderPart part,
|
|
|
b8976b |
double y1,
|
|
|
b8976b |
double x11,
|
|
|
b8976b |
double x21,
|
|
|
b8976b |
double y2,
|
|
|
b8976b |
double x12,
|
|
|
b8976b |
double x22 )
|
|
|
b8976b |
{
|
|
|
b8976b |
std::cout << "my_pango_renderer_draw_trapezoid" << std::endl;
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
static void
|
|
|
b8976b |
my_pango_renderer_init(MyPangoRenderer *renderer) {
|
|
|
b8976b |
renderer->surface = nullptr;
|
|
|
b8976b |
*(Color*)renderer->color = Color();
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
static void
|
|
|
b8976b |
my_pango_renderer_class_init(MyPangoRendererClass *klass)
|
|
|
b8976b |
{
|
|
|
b8976b |
PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS(klass);
|
|
|
b8976b |
renderer_class->draw_glyph = my_pango_renderer_draw_glyph;
|
|
|
b8976b |
renderer_class->draw_trapezoid = my_pango_renderer_draw_trapezoid;
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
PangoRenderer*
|
|
|
b8976b |
my_pango_renderer_new(Surface &surface, const Color &color) {
|
|
|
b8976b |
MyPangoRenderer *renderer = (MyPangoRenderer*)g_object_new(MY_TYPE_PANGO_RENDERER, nullptr);
|
|
|
b8976b |
renderer->surface = &surface;
|
|
|
b8976b |
*(Color*)renderer->color = color;
|
|
|
b8976b |
return (PangoRenderer*)renderer;
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
|
|
|
b8976b |
|
|
|
b8976b |
static void set_substitute(FcPattern *pattern, gpointer) {
|
|
|
b8976b |
FcPatternDel(pattern, FC_HINTING);
|
|
|
b8976b |
FcPatternAddBool(pattern, FC_HINTING, FcFalse);
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
static void put_bitmap(Surface &surface, FT_Bitmap &bitmap, int x, int y, const Color &color) {
|
|
|
b8976b |
if (x >= surface.width() || y >= surface.height())
|
|
|
b8976b |
return;
|
|
|
b8976b |
if (x + (int)bitmap.width <= 0 || y + (int)bitmap.rows <= 0)
|
|
|
b8976b |
return;
|
|
|
b8976b |
|
|
|
b8976b |
const int bx = std::max(0, -x);
|
|
|
b8976b |
const int by = std::max(0, -y);
|
|
|
b8976b |
x = std::max(0, x);
|
|
|
b8976b |
y = std::max(0, y);
|
|
|
b8976b |
const int w = std::min(surface.width() - x, (int)bitmap.width - bx);
|
|
|
b8976b |
const int h = std::min(surface.height() - y, (int)bitmap.rows - by);
|
|
|
b8976b |
const int pitch = surface.pitch();
|
|
|
b8976b |
|
|
|
b8976b |
Color *dst_pixel = &surface[y][x];
|
|
|
b8976b |
const int dst_row_step = pitch - w;
|
|
|
b8976b |
|
|
|
b8976b |
const unsigned char *src_pixel = bitmap.buffer + by*bitmap.pitch + bx;
|
|
|
b8976b |
const int src_row_step = bitmap.pitch - w;
|
|
|
b8976b |
|
|
|
b8976b |
for(const Color *end = dst_pixel + pitch*h; dst_pixel != end; dst_pixel += dst_row_step, src_pixel += src_row_step) {
|
|
|
b8976b |
for(const Color *row_end = dst_pixel + w; dst_pixel != row_end; ++dst_pixel, ++src_pixel) {
|
|
|
b8976b |
Real a = *src_pixel * (1/255.0);
|
|
|
b8976b |
*dst_pixel = *dst_pixel*(1-a) + color*a;
|
|
|
b8976b |
}
|
|
|
b8976b |
}
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
|
|
|
b8976b |
FreeTypeView::FreeTypeView() {
|
|
|
b8976b |
params.family = "Ani";
|
|
|
b8976b |
params.bold = false;
|
|
|
b8976b |
params.italic = false;
|
|
|
b8976b |
params.size = 12;
|
|
|
b8976b |
|
|
|
b8976b |
params.position = Vector2(15.5, 18.4);
|
|
|
b8976b |
params.color = Color(1, 1, 0, 1);
|
|
|
b8976b |
|
|
|
b8976b |
params.text = "Hello World! 123456";
|
|
|
b8976b |
|
|
|
b8976b |
Glib::signal_timeout().connect(
|
|
|
b8976b |
sigc::mem_fun(*this, &FreeTypeView::on_timeout),
|
|
|
b8976b |
20,
|
|
|
b8976b |
Glib::PRIORITY_DEFAULT_IDLE );
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
FreeTypeView::~FreeTypeView()
|
|
|
b8976b |
{ }
|
|
|
b8976b |
|
|
|
b8976b |
bool
|
|
|
b8976b |
FreeTypeView::on_timeout() {
|
|
|
b8976b |
if (!is_visible()) return false;
|
|
|
b8976b |
params.position.x += 0.01;
|
|
|
b8976b |
update_surface();
|
|
|
b8976b |
return true;
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
void
|
|
|
b8976b |
FreeTypeView::update_surface() {
|
|
|
b8976b |
this->surface.clear();
|
|
|
b8976b |
const int width = get_allocated_width();
|
|
|
b8976b |
const int height = get_allocated_height();
|
|
|
b8976b |
if (width <= 0 || height <= 0)
|
|
|
b8976b |
return;
|
|
|
b8976b |
|
|
|
b8976b |
// init
|
|
|
b8976b |
PangoFontMap *font_map = pango_ft2_font_map_new();
|
|
|
b8976b |
pango_ft2_font_map_set_default_substitute(
|
|
|
b8976b |
(PangoFT2FontMap*)font_map,
|
|
|
b8976b |
&set_substitute,
|
|
|
b8976b |
nullptr,
|
|
|
b8976b |
nullptr );
|
|
|
b8976b |
PangoContext *context = pango_font_map_create_context(font_map);
|
|
|
b8976b |
|
|
|
b8976b |
// load font
|
|
|
b8976b |
PangoFontDescription *font_desc = pango_font_description_new();
|
|
|
b8976b |
pango_font_description_set_family(font_desc, params.family.c_str());
|
|
|
b8976b |
pango_font_description_set_weight(font_desc, params.bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
|
|
|
b8976b |
pango_font_description_set_style(font_desc, params.italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
|
|
|
b8976b |
pango_font_description_set_absolute_size(font_desc, params.size * PANGO_SCALE);
|
|
|
b8976b |
|
|
|
b8976b |
PangoFont *font = pango_font_map_load_font(font_map, context, font_desc);
|
|
|
b8976b |
|
|
|
b8976b |
// create layout
|
|
|
b8976b |
PangoLayout *layout = pango_layout_new(context);
|
|
|
b8976b |
pango_layout_set_font_description(layout, font_desc);
|
|
|
b8976b |
pango_layout_set_text(layout, params.text.c_str(), (int)params.text.size());
|
|
|
b8976b |
|
|
|
b8976b |
// reneder
|
|
|
b8976b |
DataSurface surface(width, height);
|
|
|
b8976b |
PangoRenderer *renderer = my_pango_renderer_new(surface, params.color);
|
|
|
b8976b |
pango_renderer_draw_layout(
|
|
|
b8976b |
renderer,
|
|
|
b8976b |
layout,
|
|
|
b8976b |
(int)round(params.position.x*PANGO_SCALE),
|
|
|
b8976b |
(int)round(params.position.y*PANGO_SCALE) );
|
|
|
b8976b |
|
|
|
b8976b |
// free pango
|
|
|
b8976b |
g_object_unref(renderer);
|
|
|
b8976b |
pango_font_description_free(font_desc);
|
|
|
b8976b |
g_object_unref(layout);
|
|
|
b8976b |
g_object_unref(font);
|
|
|
b8976b |
g_object_unref(context);
|
|
|
b8976b |
g_object_unref(font_map);
|
|
|
b8976b |
|
|
|
b8976b |
this->surface = surface.to_cairo_surface(true);
|
|
|
b8976b |
|
|
|
b8976b |
queue_draw();
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
void
|
|
|
b8976b |
FreeTypeView::on_draw_view(const Cairo::RefPtr<cairo::context> &context) {</cairo::context>
|
|
|
b8976b |
if (surface) {
|
|
|
b8976b |
context->save();
|
|
|
b8976b |
context->transform(transform_from_pixels().to_cairo());
|
|
|
b8976b |
context->set_source(surface, 0, 0);
|
|
|
b8976b |
context->paint();
|
|
|
b8976b |
context->restore();
|
|
|
b8976b |
}
|
|
|
b8976b |
}
|
|
|
b8976b |
|