Blob Blame Raw

#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();
	}
}