|
|
f4d7d6 |
|
|
|
f4d7d6 |
#include <freetype2 ft2build.h=""></freetype2>
|
|
|
f4d7d6 |
#include FT_BITMAP_H
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
#include <pango pangoft2.h=""></pango>
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
#include "layoutdraw.h"
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
template<bool antialiasing,="" bool="" eraze=""></bool>
|
|
|
f4d7d6 |
static void
|
|
|
f4d7d6 |
put_ft_bitmap(
|
|
|
f4d7d6 |
Surface &surface,
|
|
|
f4d7d6 |
const IntPair2 &/*bounds*/,
|
|
|
f4d7d6 |
const FT_Bitmap &bitmap,
|
|
|
f4d7d6 |
const IntVector2 &offset,
|
|
|
f4d7d6 |
const Color &color )
|
|
|
f4d7d6 |
{
|
|
|
f4d7d6 |
const IntPair2 b = /*bounds
|
|
|
f4d7d6 |
& */IntPair2( IntVector2(0, 0), IntVector2(surface.width(), surface.height()) )
|
|
|
f4d7d6 |
& IntPair2( offset, IntVector2(offset.x + bitmap.width, offset.y + bitmap.rows) );
|
|
|
f4d7d6 |
if (b.empty())
|
|
|
f4d7d6 |
return;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
const IntVector2 size = b.size();
|
|
|
f4d7d6 |
const IntVector2 sp0 = b.p0 - offset;
|
|
|
f4d7d6 |
const int pitch = surface.pitch();
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
Color *dp = &surface[b.p0.y][b.p0.x];
|
|
|
f4d7d6 |
const int dstep = pitch - size.x;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
if (antialiasing) {
|
|
|
f4d7d6 |
const unsigned char *sp = bitmap.buffer + sp0.y*bitmap.pitch + sp0.x;
|
|
|
f4d7d6 |
const int sstep = bitmap.pitch - size.x;
|
|
|
f4d7d6 |
for(const Color *end = dp + pitch*size.y; dp != end; dp += dstep, sp += sstep) {
|
|
|
f4d7d6 |
for(const Color *row_end = dp + size.x; dp < row_end; ++dp, ++sp) {
|
|
|
f4d7d6 |
const Real a = *sp*(1/255.0);
|
|
|
f4d7d6 |
if (eraze) *dp *= 1-a;
|
|
|
f4d7d6 |
else *dp = *dp*(1-a) + color*a;
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
} else {
|
|
|
f4d7d6 |
const unsigned char *sp = bitmap.buffer + sp0.y*bitmap.pitch;
|
|
|
f4d7d6 |
const int sstep = bitmap.pitch;
|
|
|
f4d7d6 |
const int sp1x = sp0.x + size.x;
|
|
|
f4d7d6 |
for(const Color *end = dp + pitch*size.y; dp != end; dp += dstep, sp += sstep)
|
|
|
f4d7d6 |
for(int bit = sp0.x; bit < sp1x; ++dp, ++bit)
|
|
|
f4d7d6 |
if (sp[bit/8] & (128 >> (bit%8)))
|
|
|
f4d7d6 |
*dp = eraze ? Color() : color;
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
void
|
|
|
f4d7d6 |
LayoutDraw::draw(
|
|
|
f4d7d6 |
Surface &surface,
|
|
|
f4d7d6 |
const IntPair2 &bounds,
|
|
|
f4d7d6 |
const RefPtr<transformedlayout> &layout,</transformedlayout>
|
|
|
f4d7d6 |
const Matrix &matrix,
|
|
|
f4d7d6 |
const Color &color,
|
|
|
f4d7d6 |
bool eraze,
|
|
|
f4d7d6 |
const Vector2 &position,
|
|
|
f4d7d6 |
const Real *realign )
|
|
|
f4d7d6 |
{
|
|
|
f4d7d6 |
if (!layout)
|
|
|
f4d7d6 |
return;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
IntPair2 b = bounds & IntPair2(IntVector2(), IntVector2(surface.width(), surface.height()));
|
|
|
f4d7d6 |
if (b.empty())
|
|
|
f4d7d6 |
return;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
const Matrix2 &spacing_matrix = layout->get_spacing_matrix();
|
|
|
f4d7d6 |
const Matrix2 &glyph_matrix = layout->get_glyph_matrix();
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
const Pair2 fb(Vector2(b.p0.x, b.p0.y), Vector2(b.p1.x, b.p1.y));
|
|
|
f4d7d6 |
const Pair2 flb = matrix.transform_bounds(layout->get_bounds() + spacing_matrix*position);
|
|
|
f4d7d6 |
if ((fb & flb).empty())
|
|
|
f4d7d6 |
return;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
const Matrix3 full_spacing_matrix = matrix * Matrix3(
|
|
|
f4d7d6 |
Vector3(spacing_matrix.row_x()),
|
|
|
f4d7d6 |
Vector3(spacing_matrix.row_y()) );
|
|
|
f4d7d6 |
const Matrix2 full_glyph_matrix = Matrix2(
|
|
|
f4d7d6 |
matrix.row_x().vec2(),
|
|
|
f4d7d6 |
matrix.row_y().vec2() )*glyph_matrix;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
FT_Matrix mat = {};
|
|
|
f4d7d6 |
mat.xx = (int)round( full_glyph_matrix.m00*65536);
|
|
|
f4d7d6 |
mat.xy = (int)round(-full_glyph_matrix.m10*65536);
|
|
|
f4d7d6 |
mat.yx = (int)round(-full_glyph_matrix.m01*65536);
|
|
|
f4d7d6 |
mat.yy = (int)round( full_glyph_matrix.m11*65536);
|
|
|
f4d7d6 |
FT_Vector vec = {};
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
const bool hinting = layout->get_layout()->get_hinting();
|
|
|
f4d7d6 |
const bool antialiasing = layout->get_layout()->get_antialiasing();
|
|
|
f4d7d6 |
const FT_Int32 load_flags = hinting ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING;
|
|
|
f4d7d6 |
const FT_Render_Mode render_mode = antialiasing ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
const Pair2 &logical_bounds = layout->get_layout()->get_logical_bounds();
|
|
|
f4d7d6 |
const Layout::LineList &lines = layout->get_layout()->get_lines();
|
|
|
f4d7d6 |
const TransformedLayout::LineBoundsList &line_bounds = layout->get_line_bounds();
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
PangoFont *font = nullptr;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
assert(lines.size() == line_bounds.size());
|
|
|
f4d7d6 |
TransformedLayout::LineBoundsList::const_iterator lbi = line_bounds.begin();
|
|
|
f4d7d6 |
for(Layout::LineList::const_iterator li = lines.begin(); li != lines.end(); ++li, ++lbi) {
|
|
|
f4d7d6 |
Vector2 linepos = li->position;
|
|
|
f4d7d6 |
if (realign)
|
|
|
f4d7d6 |
linepos.x = (logical_bounds.p1.x - li->logical_bounds.p1.x)*clamp(*realign, 0, 1);
|
|
|
f4d7d6 |
linepos += position;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
const Vector2 lb_offset = spacing_matrix*linepos;
|
|
|
f4d7d6 |
const Pair2 lb = matrix.transform_bounds(*lbi + lb_offset);
|
|
|
f4d7d6 |
if ((fb & lb).empty())
|
|
|
f4d7d6 |
continue;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
for(Layout::RunList::const_iterator ri = li->runs.begin(); ri != li->runs.end(); ++ri) {
|
|
|
f4d7d6 |
assert(ri->font);
|
|
|
f4d7d6 |
PangoFont *next_font = ri->font->gobj();
|
|
|
f4d7d6 |
if (!next_font) continue;
|
|
|
f4d7d6 |
if (font != next_font) {
|
|
|
f4d7d6 |
if (font) pango_fc_font_unlock_face((PangoFcFont*)font);
|
|
|
f4d7d6 |
font = next_font;
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
assert(PANGO_IS_FC_FONT(font));
|
|
|
f4d7d6 |
FT_Face face = pango_fc_font_lock_face((PangoFcFont*)font);
|
|
|
f4d7d6 |
for(Layout::GlyphList::const_iterator gi = ri->glyphs.begin(); gi != ri->glyphs.end(); ++gi) {
|
|
|
f4d7d6 |
const Vector2 gb_offset = (full_spacing_matrix*Vector3(linepos + gi->position, 1)).vec2();
|
|
|
f4d7d6 |
const Pair2 gb = full_glyph_matrix.transform_bounds(gi->bounds) + gb_offset;
|
|
|
f4d7d6 |
if ((fb & gb).empty())
|
|
|
f4d7d6 |
continue;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
const Vector2 gp = gb_offset + full_glyph_matrix*gi->origin;
|
|
|
f4d7d6 |
const int fx = (int)round(gp.x*64);
|
|
|
f4d7d6 |
const int fy = (int)round(gp.y*64);
|
|
|
f4d7d6 |
int ix = fx / 64;
|
|
|
f4d7d6 |
int iy = fy / 64;
|
|
|
f4d7d6 |
vec.x = fx % 64;
|
|
|
f4d7d6 |
vec.y = fy % 64;
|
|
|
f4d7d6 |
if (hinting) {
|
|
|
f4d7d6 |
if (vec.x >= 32) ++ix;
|
|
|
f4d7d6 |
if (vec.y >= 32) ++iy;
|
|
|
f4d7d6 |
vec.x = 0;
|
|
|
f4d7d6 |
vec.y = 0;
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
vec.y = -vec.y;
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
FT_Set_Transform(face, &mat, &vec);
|
|
|
f4d7d6 |
if ( !FT_Load_Glyph(face, gi->glyph_index, load_flags)
|
|
|
f4d7d6 |
&& !FT_Render_Glyph(face->glyph, render_mode) )
|
|
|
f4d7d6 |
{
|
|
|
f4d7d6 |
const FT_Bitmap &bitmap = face->glyph->bitmap;
|
|
|
f4d7d6 |
const IntVector2 offset( ix + face->glyph->bitmap_left,
|
|
|
f4d7d6 |
iy - face->glyph->bitmap_top );
|
|
|
f4d7d6 |
if (antialiasing) {
|
|
|
f4d7d6 |
if (eraze) put_ft_bitmap<true ,="" true="">(surface, bounds, bitmap, offset, color);</true>
|
|
|
f4d7d6 |
else put_ft_bitmap<true ,="" false="">(surface, bounds, bitmap, offset, color);</true>
|
|
|
f4d7d6 |
} else {
|
|
|
f4d7d6 |
if (eraze) put_ft_bitmap<false, true="">(surface, bounds, bitmap, offset, color);</false,>
|
|
|
f4d7d6 |
else put_ft_bitmap<false, false="">(surface, bounds, bitmap, offset, color);</false,>
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
|
|
|
f4d7d6 |
if (font) pango_fc_font_unlock_face((PangoFcFont*)font);
|
|
|
f4d7d6 |
}
|
|
|
f4d7d6 |
|