Blob Blame Raw

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