Blob Blame Raw

#include <algorithm>
#include <iostream>

#include <glibmm.h>

#include "labpangorenderer.h"

#include "surface.h"
#include "log.h"

#include "freetypeview.h"


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.spacing = Vector2(3, 2);
	
	params.hinting = false;
	params.antialiasing = true;
	
	params.matrix = Matrix().translation(Vector2(15.5, 18.4));
	params.color = Color(1, 1, 0, 1);

	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;
	params.alignment_by_origin = true;
	
	params.origin = Vector2(0.25, 0.8);
	
	Glib::signal_timeout().connect(
		sigc::mem_fun(*this, &FreeTypeView::on_timeout),
		50,
		Glib::PRIORITY_DEFAULT_IDLE );
}

FreeTypeView::~FreeTypeView()
	{ }

bool
FreeTypeView::on_timeout() {
	if (!is_visible()) return false;
	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 Vector2 spacing(
		params.spacing.x > real_precision ? params.spacing.x : 1,
		params.spacing.y > real_precision ? params.spacing.y : 1 );

	const Vector2 origin(
		clamp(params.origin.x, 0, 1),
		clamp(params.origin.y, 0, 1) );
	
	Matrix matrix = params.matrix;
	Matrix2 glyph_matrix(
		matrix.row_x().vec2(),
		matrix.row_y().vec2() );
	matrix *= Matrix().scaling(spacing);

	if (fabs(glyph_matrix.det()) <= real_precision_sqr)
		return;

	
	const Real size = params.size;
	const Real wrap_width = params.wrap_width;
	
	// init
	PangoFontMap *font_map = lab_pango_font_map_new(params.hinting, params.antialiasing);
	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, size * PANGO_SCALE);
	
	PangoFont *font = pango_context_load_font(context, font_desc);
	
	// 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);
	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 Vector2 offset_to_origin(
		(rect.x + rect.width)/Real(PANGO_SCALE) * origin.x,
		(rect.y + rect.height)/Real(PANGO_SCALE) * origin.y );
	matrix *= Matrix().translation(-offset_to_origin);
	
	// create renderer
	DataSurface surface(width, height);
	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);
	
	// render line by line
	pango_renderer_activate(renderer);
	PangoLayoutIter *iter = pango_layout_get_iter(layout);
	do {
		PangoRectangle line_rect;
		pango_layout_iter_get_line_extents(iter, nullptr, &line_rect);
		PangoLayoutLine *line = pango_layout_iter_get_line_readonly(iter);
		int baseline = pango_layout_iter_get_baseline(iter);
		int x = params.alignment_by_origin
			  ? (int)round((rect.x + rect.width - line_rect.width)*origin.x)
			  : line_rect.x;
		pango_renderer_draw_layout_line(renderer, line, x, baseline);
	} while(pango_layout_iter_next_line(iter));
	pango_layout_iter_free(iter);
	pango_renderer_deactivate(renderer);
	
	// 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_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<Cairo::Context> &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());
		context->set_source(surface, 0, 0);
		context->paint();
		context->restore();
	}
	context->restore();
}