/*
 * Copyright 2007, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Maxim Shemanarev <mcseemagg@yahoo.com>
 *		Stephan Aßmus <superstippi@gmx.de>
 *		Anthony Lee <don.anthony.lee@gmail.com>
 *		Andrej Spielmann, <andrej.spielmann@seh.ox.ac.uk>
 */
 
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//			mcseemagg@yahoo.com
//			http://www.antigrain.com
//----------------------------------------------------------------------------
 
 
#include "FontEngine.h"
 
#include FT_GLYPH_H
#include FT_OUTLINE_H
#include FT_LCD_FILTER_H
 
#include <stdio.h>
 
#include <agg_bitset_iterator.h>
#include <agg_renderer_scanline.h>
 
#include "GlobalSubpixelSettings.h"
 
 
static const bool kFlipY = true;
 
 
static inline double
int26p6_to_dbl(int p)
{
	return double(p) / 64.0;
}
 
 
static inline int
dbl_to_int26p6(double p)
{
	return int(p * 64.0 + 0.5);
}
 
 
template<class PathStorage>
bool
decompose_ft_outline(const FT_Outline& outline, bool flip_y, PathStorage& path)
{
	typedef typename PathStorage::value_type value_type;
 
	FT_Vector v_last;
	FT_Vector v_control;
	FT_Vector v_start;
	double x1, y1, x2, y2, x3, y3;
 
	FT_Vector* point;
	FT_Vector* limit;
	char* tags;
 
	int   n;		 // index of contour in outline
	int   first;	 // index of first point in contour
	char  tag;	   // current point's state
 
	first = 0;
 
	for (n = 0; n < outline.n_contours; n++) {
		int  last;  // index of last point in contour
 
		last  = outline.contours[n];
		limit = outline.points + last;
 
		v_start = outline.points[first];
		v_last  = outline.points[last];
 
		v_control = v_start;
 
		point = outline.points + first;
		tags  = outline.tags  + first;
		tag   = FT_CURVE_TAG(tags[0]);
 
		// A contour cannot start with a cubic control point!
		if (tag == FT_CURVE_TAG_CUBIC)
			return false;
 
		// check first point to determine origin
		if ( tag == FT_CURVE_TAG_CONIC) {
			// first point is conic control.  Yes, this happens.
			if (FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) {
				// start at last point if it is on the curve
				v_start = v_last;
				limit--;
			} else {
				// if both first and last points are conic,
				// start at their middle and record its position
				// for closure
				v_start.x = (v_start.x + v_last.x) / 2;
				v_start.y = (v_start.y + v_last.y) / 2;
 
				v_last = v_start;
			}
			point--;
			tags--;
		}
 
		x1 = int26p6_to_dbl(v_start.x);
		y1 = int26p6_to_dbl(v_start.y);
		if (flip_y) y1 = -y1;
		path.move_to(value_type(dbl_to_int26p6(x1)),
					 value_type(dbl_to_int26p6(y1)));
 
		while(point < limit) {
			point++;
			tags++;
 
			tag = FT_CURVE_TAG(tags[0]);
			switch(tag) {
				case FT_CURVE_TAG_ON: { // emit a single line_to
					x1 = int26p6_to_dbl(point->x);
					y1 = int26p6_to_dbl(point->y);
					if (flip_y) y1 = -y1;
					path.line_to(value_type(dbl_to_int26p6(x1)),
								 value_type(dbl_to_int26p6(y1)));
					//path.line_to(conv(point->x), flip_y ? -conv(point->y) : conv(point->y));
					continue;
				}
 
				case FT_CURVE_TAG_CONIC: { // consume conic arcs
					v_control.x = point->x;
					v_control.y = point->y;
 
				Do_Conic:
					if (point < limit) {
						FT_Vector vec;
						FT_Vector v_middle;
 
						point++;
						tags++;
						tag = FT_CURVE_TAG(tags[0]);
 
						vec.x = point->x;
						vec.y = point->y;
 
						if (tag == FT_CURVE_TAG_ON) {
							x1 = int26p6_to_dbl(v_control.x);
							y1 = int26p6_to_dbl(v_control.y);
							x2 = int26p6_to_dbl(vec.x);
							y2 = int26p6_to_dbl(vec.y);
							if (flip_y) { y1 = -y1; y2 = -y2; }
							path.curve3(value_type(dbl_to_int26p6(x1)),
										value_type(dbl_to_int26p6(y1)),
										value_type(dbl_to_int26p6(x2)),
										value_type(dbl_to_int26p6(y2)));
							continue;
						}
 
						if (tag != FT_CURVE_TAG_CONIC)
							return false;
 
						v_middle.x = (v_control.x + vec.x) / 2;
						v_middle.y = (v_control.y + vec.y) / 2;
 
						x1 = int26p6_to_dbl(v_control.x);
						y1 = int26p6_to_dbl(v_control.y);
						x2 = int26p6_to_dbl(v_middle.x);
						y2 = int26p6_to_dbl(v_middle.y);
						if (flip_y) { y1 = -y1; y2 = -y2; }
						path.curve3(value_type(dbl_to_int26p6(x1)),
									value_type(dbl_to_int26p6(y1)),
									value_type(dbl_to_int26p6(x2)),
									value_type(dbl_to_int26p6(y2)));
 
						//path.curve3(conv(v_control.x),
						//			flip_y ? -conv(v_control.y) : conv(v_control.y),
						//			conv(v_middle.x),
						//			flip_y ? -conv(v_middle.y) : conv(v_middle.y));
 
						v_control = vec;
						goto Do_Conic;
					}
 
					x1 = int26p6_to_dbl(v_control.x);
					y1 = int26p6_to_dbl(v_control.y);
					x2 = int26p6_to_dbl(v_start.x);
					y2 = int26p6_to_dbl(v_start.y);
					if (flip_y) { y1 = -y1; y2 = -y2; }
					path.curve3(value_type(dbl_to_int26p6(x1)),
								value_type(dbl_to_int26p6(y1)),
								value_type(dbl_to_int26p6(x2)),
								value_type(dbl_to_int26p6(y2)));
 
					//path.curve3(conv(v_control.x),
					//			flip_y ? -conv(v_control.y) : conv(v_control.y),
					//			conv(v_start.x),
					//			flip_y ? -conv(v_start.y) : conv(v_start.y));
					goto Close;
				}
 
				default: { // FT_CURVE_TAG_CUBIC
					FT_Vector vec1, vec2;
 
					if (point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC)
						return false;
 
					vec1.x = point[0].x;
					vec1.y = point[0].y;
					vec2.x = point[1].x;
					vec2.y = point[1].y;
 
					point += 2;
					tags  += 2;
 
					if (point <= limit) {
						FT_Vector vec;
 
						vec.x = point->x;
						vec.y = point->y;
 
						x1 = int26p6_to_dbl(vec1.x);
						y1 = int26p6_to_dbl(vec1.y);
						x2 = int26p6_to_dbl(vec2.x);
						y2 = int26p6_to_dbl(vec2.y);
						x3 = int26p6_to_dbl(vec.x);
						y3 = int26p6_to_dbl(vec.y);
						if (flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; }
						path.curve4(value_type(dbl_to_int26p6(x1)),
									value_type(dbl_to_int26p6(y1)),
									value_type(dbl_to_int26p6(x2)),
									value_type(dbl_to_int26p6(y2)),
									value_type(dbl_to_int26p6(x3)),
									value_type(dbl_to_int26p6(y3)));
 
						//path.curve4(conv(vec1.x),
						//			flip_y ? -conv(vec1.y) : conv(vec1.y),
						//			conv(vec2.x),
						//			flip_y ? -conv(vec2.y) : conv(vec2.y),
						//			conv(vec.x),
						//			flip_y ? -conv(vec.y) : conv(vec.y));
						continue;
					}
 
					x1 = int26p6_to_dbl(vec1.x);
					y1 = int26p6_to_dbl(vec1.y);
					x2 = int26p6_to_dbl(vec2.x);
					y2 = int26p6_to_dbl(vec2.y);
					x3 = int26p6_to_dbl(v_start.x);
					y3 = int26p6_to_dbl(v_start.y);
					if (flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; }
					path.curve4(value_type(dbl_to_int26p6(x1)),
								value_type(dbl_to_int26p6(y1)),
								value_type(dbl_to_int26p6(x2)),
								value_type(dbl_to_int26p6(y2)),
								value_type(dbl_to_int26p6(x3)),
								value_type(dbl_to_int26p6(y3)));
 
					//path.curve4(conv(vec1.x),
					//			flip_y ? -conv(vec1.y) : conv(vec1.y),
					//			conv(vec2.x),
					//			flip_y ? -conv(vec2.y) : conv(vec2.y),
					//			conv(v_start.x),
					//			flip_y ? -conv(v_start.y) : conv(v_start.y));
					goto Close;
				}
			}
		}
 
		path.close_polygon();
 
   Close:
		first = last + 1;
	}
 
	return true;
}
 
 
template<class Scanline, class ScanlineStorage>
void
decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, int x, int y,
	bool flip_y, Scanline& sl, ScanlineStorage& storage)
{
	const uint8* buf = (const uint8*)bitmap.buffer;
	int pitch = bitmap.pitch;
	sl.reset(x, x + bitmap.width);
	storage.prepare();
	if (flip_y) {
		buf += bitmap.pitch * (bitmap.rows - 1);
		y += bitmap.rows;
		pitch = -pitch;
	}
	for (unsigned int i = 0; i < bitmap.rows; i++) {
		sl.reset_spans();
		agg::bitset_iterator bits(buf, 0);
		for (unsigned int j = 0; j < bitmap.width; j++) {
			if (bits.bit())
				sl.add_cell(x + j, agg::cover_full);
			++bits;
		}
		buf += pitch;
		if (sl.num_spans()) {
			sl.finalize(y - i - 1);
			storage.render(sl);
		}
	}
}
 
 
template<class Scanline, class ScanlineStorage>
void
decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, int x, int y,
	bool flip_y, Scanline& sl, ScanlineStorage& storage)
{
	const uint8* buf = (const uint8*)bitmap.buffer;
	int pitch = bitmap.pitch;
	sl.reset(x, x + bitmap.width);
	storage.prepare();
	if (flip_y) {
		buf += bitmap.pitch * (bitmap.rows - 1);
		y += bitmap.rows;
		pitch = -pitch;
	}
	for (unsigned int i = 0; i < bitmap.rows; i++) {
		sl.reset_spans();
 
		if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
			// font has built-in mono bitmap
			agg::bitset_iterator bits(buf, 0);
			for (unsigned int j = 0; j < bitmap.width; j++) {
				if (bits.bit())
					sl.add_cell(x + j, agg::cover_full);
				++bits;
			}
		} else {
			const uint8* p = buf;
			for (unsigned int j = 0; j < bitmap.width; j++) {
				if (*p)
					sl.add_cell(x + j, *p);
				++p;
			}
		}
 
		buf += pitch;
		if (sl.num_spans()) {
			sl.finalize(y - i - 1);
			storage.render(sl);
		}
	}
}
 
 
template<class Scanline, class ScanlineStorage>
void
decompose_ft_bitmap_subpix(const FT_Bitmap& bitmap, int x, int y,
	bool flip_y, Scanline& sl, ScanlineStorage& storage)
{
	const uint8* buf = (const uint8*)bitmap.buffer;
	int pitch = bitmap.pitch;
	sl.reset(x, x + bitmap.width / 3);
	storage.prepare();
 
	if (flip_y) {
		buf += bitmap.pitch * (bitmap.rows - 1);
		y += bitmap.rows;
		pitch = -pitch;
	}
 
	for (unsigned int i = 0; i < bitmap.rows; i++) {
		sl.reset_spans();
 
		if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
			// font has built-in mono bitmap
			agg::bitset_iterator bits(buf, 0);
			for (unsigned int j = 0; j < bitmap.width; j++) {
				if (bits.bit()) {
					sl.add_cell(x + j,
						agg::cover_full, agg::cover_full, agg::cover_full);
				}
				++bits;
			}
		} else {
			const uint8* p = buf;
			int w = bitmap.width / 3;
 
			for (int j = 0; j < w; j++) {
				if (p[0] || p[1] || p[2])
					sl.add_cell(x + j, p[0], p[1], p[2]);
				p += 3;
			}
		}
 
		buf += pitch;
		if (sl.num_spans()) {
			sl.finalize(y - i - 1);
			storage.render(sl);
		}
	}
}
 
 
// #pragma mark -
 
 
FontEngine::FontEngine()
	:
	fLastError(0),
	fLibraryInitialized(false),
	fLibrary(0),
	fFace(NULL),
 
	fGlyphRendering(glyph_ren_native_gray8),
	fHinting(true),
 
	fDataSize(0),
	fDataType(glyph_data_invalid),
	fBounds(1, 1, 0, 0),
	fAdvanceX(0.0),
	fAdvanceY(0.0),
	fInsetLeft(0.0),
	fInsetRight(0.0),
 
	fPath(),
	fCurves(fPath),
	fScanlineAA(),
	fScanlineBin(),
	fScanlineSubpix(),
	fScanlineStorageAA(),
	fScanlineStorageBin(),
	fScanlineStorageSubpix()
{
	fCurves.approximation_scale(4.0);
 
	fLastError = FT_Init_FreeType(&fLibrary);
	if (fLastError == 0)
		fLibraryInitialized = true;
}
 
 
FontEngine::~FontEngine()
{
	FT_Done_Face(fFace);
 
	if (fLibraryInitialized)
		FT_Done_FreeType(fLibrary);
}
 
 
unsigned
FontEngine::CountFaces() const
{
	if (fFace)
		return fFace->num_faces;
 
	return 0;
}
 
 
uint32
FontEngine::GlyphIndexForGlyphCode(uint32 glyphCode) const
{
	return FT_Get_Char_Index(fFace, glyphCode);
}
 
 
bool
FontEngine::PrepareGlyph(uint32 glyphIndex)
{
	FT_Int32 loadFlags = fHinting ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING;
	loadFlags |= fGlyphRendering == glyph_ren_subpix ?
		FT_LOAD_TARGET_LCD : FT_LOAD_TARGET_NORMAL;
 
	// Load unscaled and without hinting to get precise advance values
	// for B_CHAR_SPACING
	fLastError = FT_Load_Glyph(fFace, glyphIndex, loadFlags
		| FT_LOAD_NO_HINTING | FT_LOAD_NO_SCALE);
 
	fPreciseAdvanceX = (double)fFace->glyph->advance.x / fFace->units_per_EM;
	fPreciseAdvanceY = (double)fFace->glyph->advance.y / fFace->units_per_EM;
 
	// Need to load again with hinting.
	fLastError = FT_Load_Glyph(fFace, glyphIndex, loadFlags);
 
	if (fLastError != 0)
		return false;
 
	fAdvanceX = int26p6_to_dbl(fFace->glyph->advance.x);
	fAdvanceY = int26p6_to_dbl(fFace->glyph->advance.y);
 
	fInsetLeft = int26p6_to_dbl(fFace->glyph->metrics.horiBearingX);
	fInsetRight = int26p6_to_dbl(fFace->glyph->metrics.horiBearingX
		+ fFace->glyph->metrics.width - fFace->glyph->metrics.horiAdvance);
 
	switch(fGlyphRendering) {
		case glyph_ren_native_mono:
			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_MONO);
			if (fLastError == 0) {
				decompose_ft_bitmap_mono(fFace->glyph->bitmap,
					fFace->glyph->bitmap_left, kFlipY ?
					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
					kFlipY, fScanlineBin, fScanlineStorageBin);
				fBounds.x1 = fScanlineStorageBin.min_x();
				fBounds.y1 = fScanlineStorageBin.min_y();
				fBounds.x2 = fScanlineStorageBin.max_x();
				fBounds.y2 = fScanlineStorageBin.max_y();
				fDataSize = fScanlineStorageBin.byte_size();
				fDataType = glyph_data_mono;
				return true;
			}
			break;
 
 
		case glyph_ren_native_gray8:
			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_NORMAL);
			if (fLastError == 0) {
				decompose_ft_bitmap_gray8(fFace->glyph->bitmap,
					fFace->glyph->bitmap_left, kFlipY ?
					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
					kFlipY, fScanlineAA, fScanlineStorageAA);
				fBounds.x1 = fScanlineStorageAA.min_x();
				fBounds.y1 = fScanlineStorageAA.min_y();
				fBounds.x2 = fScanlineStorageAA.max_x();
				fBounds.y2 = fScanlineStorageAA.max_y();
				fDataSize = fScanlineStorageAA.byte_size();
				fDataType = glyph_data_gray8;
				return true;
			}
			break;
 
 
		case glyph_ren_subpix:
			fLastError = FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_LCD);
			if (fLastError == 0) {
				decompose_ft_bitmap_subpix(fFace->glyph->bitmap,
					fFace->glyph->bitmap_left, kFlipY ?
					-fFace->glyph->bitmap_top : fFace->glyph->bitmap_top,
					kFlipY, fScanlineSubpix, fScanlineStorageSubpix);
				fBounds.x1 = fScanlineStorageSubpix.min_x();
				fBounds.y1 = fScanlineStorageSubpix.min_y();
				fBounds.x2 = fScanlineStorageSubpix.max_x();
				fBounds.y2 = fScanlineStorageSubpix.max_y();
				fDataSize = fScanlineStorageSubpix.byte_size();
				fDataType = glyph_data_subpix;
				return true;
			}
			break;
 
 
		case glyph_ren_outline:
			fPath.remove_all();
			if (decompose_ft_outline(fFace->glyph->outline, kFlipY, fPath)) {
				agg::rect_d bounds = fPath.bounding_rect();
				fBounds.x1 = int(floor(bounds.x1));
				fBounds.y1 = int(floor(bounds.y1));
				fBounds.x2 = int(ceil(bounds.x2));
				fBounds.y2 = int(ceil(bounds.y2));
				fDataSize = fPath.byte_size();
				fDataType = glyph_data_outline;
				return true;
			}
			break;
	}
	return false;
}
 
// #pragma mark -
 
// WriteGlyphTo
void
FontEngine::WriteGlyphTo(uint8* data) const
{
	if (data && fDataSize) {
		switch(fDataType) {
			case glyph_data_mono:
				fScanlineStorageBin.serialize(data);
				break;
 
			case glyph_data_gray8:
				fScanlineStorageAA.serialize(data);
				break;
 
			case glyph_data_subpix:
				fScanlineStorageSubpix.serialize(data);
				break;
 
			case glyph_data_outline:
				fPath.serialize(data);
				break;
 
			case glyph_data_invalid:
			default:
				break;
		}
	}
}
 
 
// GetKerning
bool
FontEngine::GetKerning(uint32 first, uint32 second, double* x, double* y)
{
	if (fFace && first && second && FT_HAS_KERNING(fFace)) {
		FT_Vector delta;
		FT_Get_Kerning(fFace, first, second, FT_KERNING_DEFAULT, &delta);
 
		double dx = int26p6_to_dbl(delta.x);
		double dy = int26p6_to_dbl(delta.y);
 
		*x += dx;
		*y += dy;
 
		return true;
	}
	return false;
}
 
 
// #pragma mark -
 
 
bool
FontEngine::Init(const char* fontFilePath, unsigned faceIndex, double size,
	FT_Encoding charMap, glyph_rendering ren_type, bool hinting,
	const char* fontFileBuffer, const long fontFileBufferSize)
{
	if (!fLibraryInitialized)
		return false;
 
	fHinting = hinting;
 
	fLastError = 0;
 
	FT_Done_Face(fFace);
	if (fontFileBuffer && fontFileBufferSize) {
		fLastError = FT_New_Memory_Face(fLibrary,
			(const FT_Byte*)fontFileBuffer, fontFileBufferSize,
			faceIndex, &fFace);
	} else {
		fLastError = FT_New_Face(fLibrary, fontFilePath, faceIndex, &fFace);
	}
 
	if (fLastError != 0)
		return false;
 
	switch(ren_type) {
		case glyph_ren_native_mono:
			fGlyphRendering = glyph_ren_native_mono;
			break;
 
		case glyph_ren_native_gray8:
			fGlyphRendering = glyph_ren_native_gray8;
			break;
 
		case glyph_ren_subpix:
			fGlyphRendering = glyph_ren_subpix;
			break;
 
		case glyph_ren_outline:
			if (FT_IS_SCALABLE(fFace))
				fGlyphRendering = glyph_ren_outline;
			else
				fGlyphRendering = glyph_ren_native_gray8;
			break;
	}
 
	FT_Set_Pixel_Sizes(fFace,
		unsigned(size * 64.0) >> 6,		// pixel_width
		unsigned(size * 64.0) >> 6);	// pixel_height
 
	if (charMap != FT_ENCODING_NONE) {
		fLastError = FT_Select_Charmap(fFace, charMap);
	} else {
		if (FT_Select_Charmap(fFace, FT_ENCODING_UNICODE) != 0)
			fLastError = FT_Select_Charmap(fFace, FT_ENCODING_NONE);
	}
 
	return fLastError == 0;
}
 

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fPreciseAdvanceX, fPreciseAdvanceY.

V616 The '(FT_RENDER_MODE_NORMAL)' named constant with the value of 0 is used in the bitwise operation.