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