|
|
b8976b |
|
|
|
a1935d |
#include <cctype></cctype>
|
|
|
a1935d |
|
|
|
b8976b |
#include <algorithm></algorithm>
|
|
|
b8976b |
#include <iostream></iostream>
|
|
|
b8976b |
|
|
|
b8976b |
#include <glibmm.h></glibmm.h>
|
|
|
b8976b |
|
|
|
f4d7d6 |
#include "layout.h"
|
|
|
f4d7d6 |
#include "layoutdraw.h"
|
|
|
b8976b |
|
|
|
b8976b |
#include "surface.h"
|
|
|
b8976b |
#include "log.h"
|
|
|
b8976b |
|
|
|
b8976b |
#include "freetypeview.h"
|
|
|
b8976b |
|
|
|
b8976b |
|
|
|
822827 |
static void
|
|
|
822827 |
fill_rect(
|
|
|
822827 |
Surface &surface,
|
|
|
822827 |
const IntPair2 &rect,
|
|
|
822827 |
const Color &color )
|
|
|
822827 |
{
|
|
|
822827 |
const IntPair2 b = rect
|
|
|
822827 |
& IntPair2( IntVector2(0, 0), IntVector2(surface.width(), surface.height()) );
|
|
|
822827 |
if (b.empty())
|
|
|
822827 |
return;
|
|
|
822827 |
const IntVector2 size = b.size();
|
|
|
822827 |
const int pitch = surface.pitch();
|
|
|
822827 |
const int step = pitch - size.x;
|
|
|
822827 |
for(Color *p = &surface[b.p0.y][b.p0.x], *end = p + pitch*size.y; p != end; p += step)
|
|
|
822827 |
for(Color *row_end = p + size.x; p < row_end; ++p)
|
|
|
822827 |
*p = color;
|
|
|
822827 |
}
|
|
|
822827 |
|
|
|
a1935d |
static bool
|
|
|
a1935d |
is_font_file(const std::string &name) {
|
|
|
a1935d |
static const char *ext[] = { ".otf", ".otc", ".ttf", ".ttc" };
|
|
|
a1935d |
static const int count = (int)(sizeof(ext)/sizeof(*ext));
|
|
|
a1935d |
|
|
|
a1935d |
bool found = false;
|
|
|
a1935d |
for(int i = 0; i < count; ++i) {
|
|
|
a1935d |
const char *e = ext[i];
|
|
|
a1935d |
int len = 0; while(e[len]) ++len;
|
|
|
a1935d |
if ((int)name.size() < len) continue;
|
|
|
a1935d |
found = true;
|
|
|
a1935d |
for(int j = 0, k = name.size() - len; j < len; ++j, ++k)
|
|
|
a1935d |
if (tolower(name[k]) != e[j]) { found = false; break; }
|
|
|
a1935d |
if (found) break;
|
|
|
a1935d |
}
|
|
|
a1935d |
return found;
|
|
|
a1935d |
}
|
|
|
a1935d |
|
|
|
a1935d |
|
|
|
822827 |
|
|
|
f77c6c |
FreeTypeView::FreeTypeView():
|
|
|
1d72c2 |
p0(new View::Point( Vector2(0, 0) )),
|
|
|
1d72c2 |
px(new View::Point( Vector2(1, 0) )),
|
|
|
1d72c2 |
py(new View::Point( Vector2(0, 1) )),
|
|
|
1d72c2 |
bounds_p0(new View::Point( Vector2(-2, -2) )),
|
|
|
1d72c2 |
bounds_p1(new View::Point( Vector2( 2, 2) ))
|
|
|
f77c6c |
{
|
|
|
f77c6c |
prev_p0 = p0->position;
|
|
|
f77c6c |
transform *= Matrix().scaling(Vector2(50, 50));
|
|
|
f77c6c |
points.push_back(p0);
|
|
|
f77c6c |
points.push_back(px);
|
|
|
f77c6c |
points.push_back(py);
|
|
|
1d72c2 |
points.push_back(bounds_p0);
|
|
|
1d72c2 |
points.push_back(bounds_p1);
|
|
|
1d72c2 |
|
|
|
1d72c2 |
set_size_request(400, 400);
|
|
|
f77c6c |
|
|
|
a1935d |
//params.family = "Chancery Uralic";
|
|
|
a1935d |
//params.family = "DejaVu";
|
|
|
a1935d |
params.family = "data/font/Bradley Gratis/Bradley Gratis.ttf";
|
|
|
b8976b |
params.bold = false;
|
|
|
b8976b |
params.italic = false;
|
|
|
a1935d |
params.size = 24;
|
|
|
b8976b |
|
|
|
06d46a |
params.spacing = Vector2(3, 2);
|
|
|
f77c6c |
|
|
|
f77c6c |
params.hinting = false;
|
|
|
f77c6c |
params.antialiasing = true;
|
|
|
f4d7d6 |
params.invert = false;
|
|
|
f77c6c |
|
|
|
71e4f2 |
params.matrix = Matrix().translation(Vector2(15.5, 18.4));
|
|
|
b8976b |
params.color = Color(1, 1, 0, 1);
|
|
|
b8976b |
|
|
|
f77c6c |
params.text =
|
|
|
f77c6c |
"Lorem ipsum dolor sit amet, consectetur adipisici elit,"
|
|
|
f77c6c |
"sed eiusmod tempor incidunt ut labore et dolore magna aliqua."
|
|
|
f77c6c |
"Ut enim ad minim veniam, quis nostrud exercitation ullamco "
|
|
|
f77c6c |
"laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure"
|
|
|
f77c6c |
"reprehenderit in voluptate velit esse cillum dolore eu fugiat "
|
|
|
f77c6c |
"nulla pariatur. Excepteur sint obcaecat cupiditat non proident, "
|
|
|
f77c6c |
"sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
|
|
f77c6c |
//params.text = "1\n2\n3\n";
|
|
|
f77c6c |
|
|
|
06d46a |
params.alignment = -1;
|
|
|
5a8eb1 |
params.justify = false;
|
|
|
f77c6c |
params.wrap_width = 600;
|
|
|
06d46a |
params.alignment_by_origin = true;
|
|
|
06d46a |
|
|
|
06d46a |
params.origin = Vector2(0.25, 0.8);
|
|
|
b8976b |
|
|
|
b8976b |
Glib::signal_timeout().connect(
|
|
|
b8976b |
sigc::mem_fun(*this, &FreeTypeView::on_timeout),
|
|
|
f77c6c |
50,
|
|
|
b8976b |
Glib::PRIORITY_DEFAULT_IDLE );
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
FreeTypeView::~FreeTypeView()
|
|
|
b8976b |
{ }
|
|
|
b8976b |
|
|
|
b8976b |
bool
|
|
|
b8976b |
FreeTypeView::on_timeout() {
|
|
|
b8976b |
if (!is_visible()) return false;
|
|
|
f77c6c |
p0->position.x += 0.001;
|
|
|
f77c6c |
point_motion(p0);
|
|
|
b8976b |
return true;
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
void
|
|
|
b8976b |
FreeTypeView::update_surface() {
|
|
|
b8976b |
this->surface.clear();
|
|
|
f77c6c |
|
|
|
f4d7d6 |
// prepare matrix
|
|
|
1d72c2 |
Matrix to_pixels = transform_to_pixels();
|
|
|
f77c6c |
Matrix in_matrix(
|
|
|
f77c6c |
Vector3(px->position - p0->position)/50,
|
|
|
f77c6c |
Vector3(py->position - p0->position)/50,
|
|
|
f77c6c |
Vector3(p0->position, 1) );
|
|
|
f77c6c |
params.matrix = transform_to_pixels() * in_matrix;
|
|
|
f4d7d6 |
if (fabs(params.matrix.det()) <= real_precision_cub)
|
|
|
f4d7d6 |
return;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
// calc bounds
|
|
|
f4d7d6 |
const int width = get_allocated_width();
|
|
|
f4d7d6 |
const int height = get_allocated_height();
|
|
|
1d72c2 |
Pair2 bounds = to_pixels.transform_bounds(
|
|
|
1d72c2 |
Pair2(bounds_p0->position)
|
|
|
1d72c2 |
.expand(bounds_p1->position) );
|
|
|
1d72c2 |
bounds &= Pair2(Vector2(), Vector2(width, height));
|
|
|
1d72c2 |
if (bounds.empty())
|
|
|
1d72c2 |
return;
|
|
|
1d72c2 |
IntPair2 int_bounds = IntPair2( IntVector2( (int)floor(bounds.p0.x + real_precision),
|
|
|
1d72c2 |
(int)floor(bounds.p0.y + real_precision) ),
|
|
|
f4d7d6 |
IntVector2( (int)ceil (bounds.p1.x - real_precision),
|
|
|
f4d7d6 |
(int)ceil (bounds.p1.y - real_precision) ))
|
|
|
1d72c2 |
& IntPair2( IntVector2(),
|
|
|
1d72c2 |
IntVector2(width, height) );
|
|
|
1d72c2 |
if (int_bounds.empty())
|
|
|
b8976b |
return;
|
|
|
822827 |
|
|
|
f4d7d6 |
// calc some measures
|
|
|
06d46a |
const Vector2 origin(
|
|
|
06d46a |
clamp(params.origin.x, 0, 1),
|
|
|
06d46a |
clamp(params.origin.y, 0, 1) );
|
|
|
f4d7d6 |
const Vector2 spacing(
|
|
|
f4d7d6 |
params.spacing.x > real_precision ? params.spacing.x : 1,
|
|
|
f4d7d6 |
params.spacing.y > real_precision ? params.spacing.y : 1 );
|
|
|
f4d7d6 |
const Matrix2 spacing_matrix(
|
|
|
f4d7d6 |
Vector2(spacing.x, 0),
|
|
|
f4d7d6 |
Vector2(0, spacing.y) );
|
|
|
f4d7d6 |
if (fabs(spacing_matrix.det()) <= real_precision_sqr)
|
|
|
71e4f2 |
return;
|
|
|
71e4f2 |
|
|
|
b8976b |
// init
|
|
|
f4d7d6 |
Glib::RefPtr<pango::fontmap> font_map = Layout::create_fontmap(params.hinting, params.antialiasing);</pango::fontmap>
|
|
|
f4d7d6 |
Glib::RefPtr<pango::context> context = font_map->create_context();</pango::context>
|
|
|
71e4f2 |
|
|
|
b8976b |
// load font
|
|
|
a1935d |
Glib::RefPtr<pango::font> font;</pango::font>
|
|
|
f4d7d6 |
Pango::FontDescription font_desc;
|
|
|
f4d7d6 |
font_desc.set_family(params.family);
|
|
|
f4d7d6 |
font_desc.set_weight(params.bold ? Pango::WEIGHT_BOLD : Pango::WEIGHT_NORMAL);
|
|
|
f4d7d6 |
font_desc.set_style(params.italic ? Pango::STYLE_ITALIC : Pango::STYLE_NORMAL);
|
|
|
f4d7d6 |
font_desc.set_absolute_size(params.size * Pango::SCALE);
|
|
|
a1935d |
if (is_font_file(params.family))
|
|
|
a1935d |
font = Layout::load_font_file(context, params.family, font_desc);
|
|
|
a1935d |
if (!font)
|
|
|
a1935d |
font = context->load_font(font_desc);
|
|
|
a1935d |
font_desc = font->describe();
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
// create pango layout
|
|
|
f4d7d6 |
Glib::RefPtr<pango::layout> pango_layout = Pango::Layout::create(context);</pango::layout>
|
|
|
f4d7d6 |
pango_layout->set_font_description(font_desc);
|
|
|
f4d7d6 |
pango_layout->set_wrap(Pango::WRAP_WORD_CHAR);
|
|
|
f4d7d6 |
pango_layout->set_width(
|
|
|
f4d7d6 |
params.wrap_width > real_precision ? (int)round(params.wrap_width*Pango::SCALE) : -1 );
|
|
|
f4d7d6 |
pango_layout->set_alignment(
|
|
|
f4d7d6 |
params.alignment < 0 ? Pango::ALIGN_LEFT :
|
|
|
f4d7d6 |
params.alignment > 0 ? Pango::ALIGN_RIGHT : Pango::ALIGN_CENTER );
|
|
|
f4d7d6 |
pango_layout->set_justify(params.justify);
|
|
|
f4d7d6 |
pango_layout->set_text(params.text);
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
// create lab layout
|
|
|
f4d7d6 |
RefPtr<layout> layout(new Layout(pango_layout));</layout>
|
|
|
f4d7d6 |
const Pair2 &logical_bounds = layout->get_logical_bounds();
|
|
|
f4d7d6 |
const Vector2 layout_position(
|
|
|
f4d7d6 |
logical_bounds.p1.x * origin.x,
|
|
|
f4d7d6 |
logical_bounds.p1.y * origin.y );
|
|
|
f4d7d6 |
RefPtr<transformedlayout> transformed_layout(</transformedlayout>
|
|
|
f4d7d6 |
new TransformedLayout(layout, spacing_matrix, Matrix2()) );
|
|
|
1d72c2 |
|
|
|
f4d7d6 |
// prepare surface
|
|
|
f4d7d6 |
DataSurface surface(width, height);
|
|
|
f4d7d6 |
if (params.invert)
|
|
|
f4d7d6 |
fill_rect(surface, int_bounds, params.color.get_mult_alpha());
|
|
|
822827 |
|
|
|
f4d7d6 |
// draw
|
|
|
f4d7d6 |
LayoutDraw::draw(
|
|
|
f4d7d6 |
surface,
|
|
|
f4d7d6 |
int_bounds,
|
|
|
f4d7d6 |
transformed_layout,
|
|
|
f4d7d6 |
params.matrix,
|
|
|
f4d7d6 |
params.color,
|
|
|
f4d7d6 |
params.invert,
|
|
|
f4d7d6 |
-layout_position,
|
|
|
f4d7d6 |
params.alignment_by_origin ? &origin.x : nullptr );
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
// to cairo
|
|
|
822827 |
this->surface = surface.to_cairo_surface(true);
|
|
|
b8976b |
}
|
|
|
b8976b |
|
|
|
b8976b |
void
|
|
|
f77c6c |
FreeTypeView::on_point_motion(const View::PointPtr &point) {
|
|
|
f77c6c |
if (point == p0) {
|
|
|
f77c6c |
px->position += p0->position - prev_p0;
|
|
|
f77c6c |
py->position += p0->position - prev_p0;
|
|
|
f77c6c |
prev_p0 = p0->position;
|
|
|
f77c6c |
}
|
|
|
1d72c2 |
queue_draw();
|
|
|
f77c6c |
}
|
|
|
f77c6c |
|
|
|
f77c6c |
void
|
|
|
b8976b |
FreeTypeView::on_draw_view(const Cairo::RefPtr<cairo::context> &context) {</cairo::context>
|
|
|
1d72c2 |
update_surface();
|
|
|
1d72c2 |
|
|
|
f77c6c |
const Real ps = get_pixel_size();
|
|
|
f77c6c |
|
|
|
f77c6c |
context->save();
|
|
|
f77c6c |
context->set_line_width(ps);
|
|
|
f77c6c |
|
|
|
1d72c2 |
// draw transform
|
|
|
f77c6c |
context->set_source_rgba(0, 0, 1, 1);
|
|
|
f77c6c |
context->move_to(px->position.x, px->position.y);
|
|
|
f77c6c |
context->line_to(p0->position.x, p0->position.y);
|
|
|
f77c6c |
context->line_to(py->position.x, py->position.y);
|
|
|
f77c6c |
context->stroke();
|
|
|
f77c6c |
|
|
|
1d72c2 |
// draw bounds
|
|
|
1d72c2 |
context->set_source_rgba(0, 0, 1, 1);
|
|
|
1d72c2 |
context->move_to(bounds_p0->position.x, bounds_p0->position.y);
|
|
|
1d72c2 |
context->line_to(bounds_p1->position.x, bounds_p0->position.y);
|
|
|
1d72c2 |
context->line_to(bounds_p1->position.x, bounds_p1->position.y);
|
|
|
1d72c2 |
context->line_to(bounds_p0->position.x, bounds_p1->position.y);
|
|
|
1d72c2 |
context->close_path();
|
|
|
1d72c2 |
context->stroke();
|
|
|
1d72c2 |
|
|
|
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 |
}
|
|
|
f77c6c |
context->restore();
|
|
|
b8976b |
}
|
|
|
b8976b |
|