Font.cpp 21.2 KB
Newer Older
1
#include "resources/Font.h"
2 3 4 5
#include <iostream>
#include <algorithm>
#include <vector>
#include <boost/filesystem.hpp>
6 7 8
#include "Renderer.h"
#include "Log.h"
#include "Util.h"
9

10
FT_Library Font::sLibrary = NULL;
11 12 13 14 15

int Font::getSize() const { return mSize; }

std::map< std::pair<std::string, int>, std::weak_ptr<Font> > Font::sFontMap;

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

// utf8 stuff
size_t Font::getNextCursor(const std::string& str, size_t cursor)
{
	// compare to character at the cursor
	const char& c = str[cursor];

	size_t result = cursor;
	if((c & 0x80) == 0) // 0xxxxxxx, one byte character
	{
		result += 1;
	}
	else if((c & 0xE0) == 0xC0) // 110xxxxx, two bytes left in character
	{
		result += 2;
	}
	else if((c & 0xF0) == 0xE0) // 1110xxxx, three bytes left in character
	{
		result += 3;
	}
	else if((c & 0xF8) == 0xF0) // 11110xxx, four bytes left in character
	{
		result += 4;
	}
	else
	{
		// error, invalid utf8 string
		
		// if this assert is tripped, the cursor is in the middle of a utf8 code point
		assert((c & 0xC0) != 0x80); // character is 10xxxxxx

		// if that wasn't it, something crazy happened
		assert(false);
	}

	if(str.length() < result || result < cursor) // don't go beyond the very end of the string, try and catch overflow
		return cursor;
	return result;
}

// note: will happily accept malformed utf8
size_t Font::getPrevCursor(const std::string& str, size_t cursor)
{
	if(cursor == 0)
		return 0;

	do
	{
		cursor--;
	} while(cursor > 0 &&
		(str[cursor] & 0xC0) == 0x80); // character is 10xxxxxx

	return cursor;
}

size_t Font::moveCursor(const std::string& str, size_t cursor, int amt)
{
	if(amt > 0)
	{
		for(int i = 0; i < amt; i++)
			cursor = Font::getNextCursor(str, cursor);
	}
	else if(amt < 0)
	{
		for(int i = amt; i < 0; i++)
			cursor = Font::getPrevCursor(str, cursor);
	}

	return cursor;
}

UnicodeChar Font::readUnicodeChar(const std::string& str, size_t& cursor)
{
	const char& c = str[cursor];

	if((c & 0x80) == 0) // 0xxxxxxx, one byte character
	{
		// 0xxxxxxx
		cursor++;
		return (UnicodeChar)c;
	}
	else if((c & 0xE0) == 0xC0) // 110xxxxx, two bytes left in character
	{
		// 110xxxxx 10xxxxxx
		UnicodeChar val = ((str[cursor] & 0x1F) << 6) |
			(str[cursor + 1] & 0x3F);
		cursor += 2;
		return val;
	}
	else if((c & 0xF0) == 0xE0) // 1110xxxx, three bytes left in character
	{
		// 1110xxxx 10xxxxxx 10xxxxxx
		UnicodeChar val = ((str[cursor] & 0x0F) << 12) |
			((str[cursor + 1] & 0x3F) << 6) |
			 (str[cursor + 2] & 0x3F);
		cursor += 3;
		return val;
	}
	else if((c & 0xF8) == 0xF0) // 11110xxx, four bytes left in character
	{
		// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
		UnicodeChar val = ((str[cursor] & 0x07) << 18) |
			((str[cursor + 1] & 0x3F) << 12) |
			((str[cursor + 2] & 0x3F) << 6) |
			 (str[cursor + 3] & 0x3F);
		cursor += 4;
		return val;
	}
	else
	{
		// error, invalid utf8 string

		// if this assert is tripped, the cursor is in the middle of a utf8 code point
		assert((c & 0xC0) != 0x80); // character is 10xxxxxx

		// if that wasn't it, something crazy happened
		assert(false);
	}

	// error
	return 0;
}


140 141 142 143 144 145 146 147 148 149 150 151 152 153
Font::FontFace::FontFace(ResourceData&& d, int size) : data(d)
{
	int err = FT_New_Memory_Face(sLibrary, data.ptr.get(), data.length, 0, &face);
	assert(!err);
	
	FT_Set_Pixel_Sizes(face, 0, size);
}

Font::FontFace::~FontFace()
{
	if(face)
		FT_Done_Face(face);
}

154 155
void Font::initLibrary()
{
156
	assert(sLibrary == NULL);
157 158
	if(FT_Init_FreeType(&sLibrary))
	{
159
		sLibrary = NULL;
160 161 162 163
		LOG(LogError) << "Error initializing FreeType!";
	}
}

164 165
size_t Font::getMemUsage() const
{
166 167 168
	size_t memUsage = 0;
	for(auto it = mTextures.begin(); it != mTextures.end(); it++)
		memUsage += it->textureSize.x() * it->textureSize.y() * 4;
169

170 171 172
	for(auto it = mFaceCache.begin(); it != mFaceCache.end(); it++)
		memUsage += it->second->data.length;

173
	return memUsage;
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
}

size_t Font::getTotalMemUsage()
{
	size_t total = 0;

	auto it = sFontMap.begin();
	while(it != sFontMap.end())
	{
		if(it->second.expired())
		{
			it = sFontMap.erase(it);
			continue;
		}

		total += it->second.lock()->getMemUsage();
		it++;
	}

	return total;
}

196
Font::Font(int size, const std::string& path) : mSize(size), mPath(path)
197
{
198 199
	assert(mSize > 0);
	
200 201
	mMaxGlyphHeight = 0;

202 203 204
	if(!sLibrary)
		initLibrary();

205 206 207
	// always initialize ASCII characters
	for(UnicodeChar i = 32; i < 128; i++)
		getGlyph(i);
208 209

	clearFaceCache();
210 211 212 213
}

Font::~Font()
{
214
	unload(ResourceManager::getInstance());
215 216 217 218
}

void Font::reload(std::shared_ptr<ResourceManager>& rm)
{
219
	rebuildTextures();
220 221 222 223
}

void Font::unload(std::shared_ptr<ResourceManager>& rm)
{
224
	unloadTextures();
225 226 227 228
}

std::shared_ptr<Font> Font::get(int size, const std::string& path)
{
229 230 231
	const std::string canonicalPath = getCanonicalPath(path);

	std::pair<std::string, int> def(canonicalPath.empty() ? getDefaultPath() : canonicalPath, size);
232 233 234 235 236 237 238
	auto foundFont = sFontMap.find(def);
	if(foundFont != sFontMap.end())
	{
		if(!foundFont->second.expired())
			return foundFont->second.lock();
	}

239
	std::shared_ptr<Font> font = std::shared_ptr<Font>(new Font(def.second, def.first));
240 241 242 243 244
	sFontMap[def] = std::weak_ptr<Font>(font);
	ResourceManager::getInstance()->addReloadable(font);
	return font;
}

245
void Font::unloadTextures()
246
{
247
	for(auto it = mTextures.begin(); it != mTextures.end(); it++)
248
	{
249
		it->deinitTexture();
250
	}
251
}
252

253 254 255 256 257 258 259 260 261 262 263
Font::FontTexture::FontTexture()
{
	textureId = 0;
	textureSize << 2048, 512;
	writePos = Eigen::Vector2i::Zero();
	rowHeight = 0;
}

Font::FontTexture::~FontTexture()
{
	deinitTexture();
264 265
}

266 267 268 269
bool Font::FontTexture::findEmpty(const Eigen::Vector2i& size, Eigen::Vector2i& cursor_out)
{
	if(size.x() >= textureSize.x() || size.y() >= textureSize.y())
		return false;
270

271 272
	if(writePos.x() + size.x() >= textureSize.x() &&
		writePos.y() + rowHeight + size.y() + 1 < textureSize.y())
273
	{
274 275 276 277
		// row full, but it should fit on the next row
		// move cursor to next row
		writePos << 0, writePos.y() + rowHeight + 1; // leave 1px of space between glyphs
		rowHeight = 0;
278 279
	}

280 281 282 283 284 285 286 287 288
	if(writePos.x() + size.x() >= textureSize.x() ||
		writePos.y() + size.y() >= textureSize.y())
	{
		// nope, still won't fit
		return false;
	}

	cursor_out = writePos;
	writePos[0] += size.x() + 1; // leave 1px of space between glyphs
289

290 291 292 293 294
	if(size.y() > rowHeight)
		rowHeight = size.y();

	return true;
}
295

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
void Font::FontTexture::initTexture()
{
	assert(textureId == 0);

	glGenTextures(1, &textureId);
	glBindTexture(GL_TEXTURE_2D, textureId);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	glPixelStorei(GL_PACK_ALIGNMENT, 1);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, textureSize.x(), textureSize.y(), 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL);
}

void Font::FontTexture::deinitTexture()
{
	if(textureId != 0)
	{
		glDeleteTextures(1, &textureId);
		textureId = 0;
	}
}

324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
void Font::getTextureForNewGlyph(const Eigen::Vector2i& glyphSize, FontTexture*& tex_out, Eigen::Vector2i& cursor_out)
{
	if(mTextures.size())
	{
		// check if the most recent texture has space
		tex_out = &mTextures.back();

		// will this one work?
		if(tex_out->findEmpty(glyphSize, cursor_out))
			return; // yes
	}

	// current textures are full,
	// make a new one
	mTextures.push_back(FontTexture());
	tex_out = &mTextures.back();
340 341
	tex_out->initTexture();
	
342 343
	bool ok = tex_out->findEmpty(glyphSize, cursor_out);
	if(!ok)
344
	{
345 346 347 348
		LOG(LogError) << "Glyph too big to fit on a new texture (glyph size > " << tex_out->textureSize.x() << ", " << tex_out->textureSize.y() << ")!";
		tex_out = NULL;
	}
}
349

350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
std::vector<std::string> getFallbackFontPaths()
{
#ifdef WIN32
	// Windows

	// get this system's equivalent of "C:\Windows" (might be on a different drive or in a different folder)
	// so we can check the Fonts subdirectory for fallback fonts
	TCHAR winDir[MAX_PATH];
	GetWindowsDirectory(winDir, MAX_PATH);
	std::string fontDir = winDir;
	fontDir += "\\Fonts\\";

	const char* fontNames[] = {
		"meiryo.ttc", // japanese
		"simhei.ttf", // chinese
		"arial.ttf"   // latin
	};

	//prepend to font file names
	std::vector<std::string> fontPaths;
	fontPaths.reserve(sizeof(fontNames) / sizeof(fontNames[0]));

	for(unsigned int i = 0; i < sizeof(fontNames) / sizeof(fontNames[0]); i++)
	{
		std::string path = fontDir + fontNames[i];
		if(ResourceManager::getInstance()->fileExists(path))
			fontPaths.push_back(path);
	}

	fontPaths.shrink_to_fit();
	return fontPaths;

#else
	// Linux

	// TODO
	const char* paths[] = {
387 388
		"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf",// japanese, chinese, present on Debian
		 "/usr/share/fonts/truetype/fontawesome-webfont.ttf"
389 390 391 392 393 394 395 396 397 398
	};

	std::vector<std::string> fontPaths;
	for(unsigned int i = 0; i < sizeof(paths) / sizeof(paths[0]); i++)
	{
		if(ResourceManager::getInstance()->fileExists(paths[i]))
			fontPaths.push_back(paths[i]);
	}

	fontPaths.shrink_to_fit();
399
	return fontPaths;
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423

#endif
}

FT_Face Font::getFaceForChar(UnicodeChar id)
{
	static const std::vector<std::string> fallbackFonts = getFallbackFontPaths();

	// look through our current font + fallback fonts to see if any have the glyph we're looking for
	for(unsigned int i = 0; i < fallbackFonts.size() + 1; i++)
	{
		auto fit = mFaceCache.find(i);

		if(fit == mFaceCache.end()) // doesn't exist yet
		{
			// i == 0 -> mPath
			// otherwise, take from fallbackFonts
			const std::string& path = (i == 0 ? mPath : fallbackFonts.at(i - 1));
			ResourceData data = ResourceManager::getInstance()->getFileData(path);
			mFaceCache[i] = std::unique_ptr<FontFace>(new FontFace(std::move(data), mSize));
			fit = mFaceCache.find(i);
		}

		if(FT_Get_Char_Index(fit->second->face, id) != 0)
424
			return fit->second->face;
425 426 427 428 429 430 431 432 433 434 435
	}

	// nothing has a valid glyph - return the "real" face so we get a "missing" character
	return mFaceCache.begin()->second->face;
}

void Font::clearFaceCache()
{
	mFaceCache.clear();
}

436 437 438 439 440 441
Font::Glyph* Font::getGlyph(UnicodeChar id)
{
	// is it already loaded?
	auto it = mGlyphMap.find(id);
	if(it != mGlyphMap.end())
		return &it->second;
442

443
	// nope, need to make a glyph
444 445
	FT_Face face = getFaceForChar(id);
	if(!face)
446
	{
447
		LOG(LogError) << "Could not find appropriate font face for character " << id << " for font " << mPath;
448 449
		return NULL;
	}
450

451
	FT_GlyphSlot g = face->glyph;
452

453 454 455 456 457 458 459 460 461 462 463
	if(FT_Load_Char(face, id, FT_LOAD_RENDER))
	{
		LOG(LogError) << "Could not find glyph for character " << id << " for font " << mPath << ", size " << mSize << "!";
		return NULL;
	}

	Eigen::Vector2i glyphSize(g->bitmap.width, g->bitmap.rows);

	FontTexture* tex = NULL;
	Eigen::Vector2i cursor;
	getTextureForNewGlyph(glyphSize, tex, cursor);
464

465 466 467 468 469
	// getTextureForNewGlyph can fail if the glyph is bigger than the max texture size (absurdly large font size)
	if(tex == NULL)
	{
		LOG(LogError) << "Could not create glyph for character " << id << " for font " << mPath << ", size " << mSize << " (no suitable texture found)!";
		return NULL;
470 471
	}

472 473 474 475 476 477 478 479 480 481 482 483 484
	// create glyph
	Glyph& glyph = mGlyphMap[id];
	
	glyph.texture = tex;
	glyph.texPos << cursor.x() / (float)tex->textureSize.x(), cursor.y() / (float)tex->textureSize.y();
	glyph.texSize << glyphSize.x() / (float)tex->textureSize.x(), glyphSize.y() / (float)tex->textureSize.y();

	glyph.advance << (float)g->metrics.horiAdvance / 64.0f, (float)g->metrics.vertAdvance / 64.0f;
	glyph.bearing << (float)g->metrics.horiBearingX / 64.0f, (float)g->metrics.horiBearingY / 64.0f;

	// upload glyph bitmap to texture
	glBindTexture(GL_TEXTURE_2D, tex->textureId);
	glTexSubImage2D(GL_TEXTURE_2D, 0, cursor.x(), cursor.y(), glyphSize.x(), glyphSize.y(), GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);
485 486
	glBindTexture(GL_TEXTURE_2D, 0);

487 488 489 490 491 492
	// update max glyph height
	if(glyphSize.y() > mMaxGlyphHeight)
		mMaxGlyphHeight = glyphSize.y();

	// done
	return &glyph;
493 494
}

495 496 497 498 499 500 501 502 503 504 505 506
// completely recreate the texture data for all textures based on mGlyphs information
void Font::rebuildTextures()
{
	// recreate OpenGL textures
	for(auto it = mTextures.begin(); it != mTextures.end(); it++)
	{
		it->initTexture();
	}

	// reupload the texture data
	for(auto it = mGlyphMap.begin(); it != mGlyphMap.end(); it++)
	{
507 508 509
		FT_Face face = getFaceForChar(it->first);
		FT_GlyphSlot glyphSlot = face->glyph;

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
		// load the glyph bitmap through FT
		FT_Load_Char(face, it->first, FT_LOAD_RENDER);

		FontTexture* tex = it->second.texture;
		
		// find the position/size
		Eigen::Vector2i cursor(it->second.texPos.x() * tex->textureSize.x(), it->second.texPos.y() * tex->textureSize.y());
		Eigen::Vector2i glyphSize(it->second.texSize.x() * tex->textureSize.x(), it->second.texSize.y() * tex->textureSize.y());
		
		// upload to texture
		glBindTexture(GL_TEXTURE_2D, tex->textureId);
		glTexSubImage2D(GL_TEXTURE_2D, 0, cursor.x(), cursor.y(), glyphSize.x(), glyphSize.y(), GL_ALPHA, GL_UNSIGNED_BYTE, glyphSlot->bitmap.buffer);
	}

	glBindTexture(GL_TEXTURE_2D, 0);
}

527 528 529 530 531 532 533 534
void Font::renderTextCache(TextCache* cache)
{
	if(cache == NULL)
	{
		LOG(LogError) << "Attempted to draw NULL TextCache!";
		return;
	}

535 536
	for(auto it = cache->vertexLists.begin(); it != cache->vertexLists.end(); it++)
	{
537
		assert(*it->textureIdPtr != 0);
538 539 540

		auto vertexList = *it;

541
		glBindTexture(GL_TEXTURE_2D, *it->textureIdPtr);
542 543 544
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
545

546 547 548
		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glEnableClientState(GL_COLOR_ARRAY);
549

550 551 552
		glVertexPointer(2, GL_FLOAT, sizeof(TextCache::Vertex), it->verts[0].pos.data());
		glTexCoordPointer(2, GL_FLOAT, sizeof(TextCache::Vertex), it->verts[0].tex.data());
		glColorPointer(4, GL_UNSIGNED_BYTE, 0, it->colors.data());
553

554
		glDrawArrays(GL_TRIANGLES, 0, it->verts.size());
555

556 557 558
		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glDisableClientState(GL_COLOR_ARRAY);
559

560 561 562
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_BLEND);
	}
563 564
}

565
Eigen::Vector2f Font::sizeText(std::string text, float lineSpacing)
566 567 568 569
{
	float lineWidth = 0.0f;
	float highestWidth = 0.0f;

570 571 572
	const float lineHeight = getHeight(lineSpacing);

	float y = lineHeight;
573

574 575
	size_t i = 0;
	while(i < text.length())
576
	{
577
		UnicodeChar character = readUnicodeChar(text, i); // advances i
578

579
		if(character == (UnicodeChar)'\n')
580 581 582 583 584
		{
			if(lineWidth > highestWidth)
				highestWidth = lineWidth;

			lineWidth = 0.0f;
585
			y += lineHeight;
586 587
		}

588 589 590
		Glyph* glyph = getGlyph(character);
		if(glyph)
			lineWidth += glyph->advance.x();
591 592 593 594 595 596 597 598
	}

	if(lineWidth > highestWidth)
		highestWidth = lineWidth;

	return Eigen::Vector2f(highestWidth, y);
}

599
float Font::getHeight(float lineSpacing) const
600
{
601
	return mMaxGlyphHeight * lineSpacing;
602 603
}

604
float Font::getLetterHeight()
Aloshi's avatar
Aloshi committed
605
{
606
	Glyph* glyph = getGlyph((UnicodeChar)'S');
607
	assert(glyph);
608
	return glyph->texSize.y() * glyph->texture->textureSize.y();
Aloshi's avatar
Aloshi committed
609
}
610 611 612

//the worst algorithm ever written
//breaks up a normal string with newlines to make it fit xLen
613
std::string Font::wrapText(std::string text, float xLen)
614 615 616 617
{
	std::string out;

	std::string line, word, temp;
618
	size_t space;
619 620 621

	Eigen::Vector2f textSize;

622
	while(text.length() > 0) //while there's text or we still have text to render
623
	{
624
		space = text.find_first_of(" \t\n");
625 626 627 628
		if(space == std::string::npos)
			space = text.length() - 1;

		word = text.substr(0, space + 1);
629
		text.erase(0, space + 1);
630 631 632 633 634

		temp = line + word;

		textSize = sizeText(temp);

635 636
		// if the word will fit on the line, add it to our line, and continue
		if(textSize.x() <= xLen)
637 638
		{
			line = temp;
639 640 641
			continue;
		}else{
			// the next word won't fit, so break here
642 643 644 645 646
			out += line + '\n';
			line = word;
		}
	}

647 648
	// whatever's left should fit
	out += line;
649 650 651 652

	return out;
}

653
Eigen::Vector2f Font::sizeWrappedText(std::string text, float xLen, float lineSpacing)
654 655
{
	text = wrapText(text, xLen);
656
	return sizeText(text, lineSpacing);
657 658
}

659
Eigen::Vector2f Font::getWrappedTextCursorOffset(std::string text, float xLen, size_t stop, float lineSpacing)
660 661 662 663 664 665
{
	std::string wrappedText = wrapText(text, xLen);

	float lineWidth = 0.0f;
	float y = 0.0f;

666 667 668
	size_t wrapCursor = 0;
	size_t cursor = 0;
	while(cursor < stop)
669
	{
670 671
		UnicodeChar wrappedCharacter = readUnicodeChar(wrappedText, wrapCursor);
		UnicodeChar character = readUnicodeChar(text, cursor);
672

673
		if(wrappedCharacter == (UnicodeChar)'\n' && character != (UnicodeChar)'\n')
674 675 676 677
		{
			//this is where the wordwrap inserted a newline
			//reset lineWidth and increment y, but don't consume a cursor character
			lineWidth = 0.0f;
678
			y += getHeight(lineSpacing);
679

680
			cursor = getPrevCursor(text, cursor); // unconsume
681 682 683
			continue;
		}

684
		if(character == (UnicodeChar)'\n')
685 686
		{
			lineWidth = 0.0f;
687
			y += getHeight(lineSpacing);
688 689 690
			continue;
		}

691
		Glyph* glyph = getGlyph(character);
692 693
		if(glyph)
			lineWidth += glyph->advance.x();
694 695 696 697 698 699 700 701 702
	}

	return Eigen::Vector2f(lineWidth, y);
}

//=============================================================================================================
//TextCache
//=============================================================================================================

703
float Font::getNewlineStartOffset(const std::string& text, const unsigned int& charStart, const float& xLen, const Alignment& alignment)
704
{
705
	switch(alignment)
706
	{
707 708 709 710 711 712 713 714 715 716 717 718 719 720
	case ALIGN_LEFT:
		return 0;
	case ALIGN_CENTER:
		{
			unsigned int endChar = text.find('\n', charStart);
			return (xLen - sizeText(text.substr(charStart, endChar != std::string::npos ? endChar - charStart : endChar)).x()) / 2.0f;
		}
	case ALIGN_RIGHT:
		{
			unsigned int endChar = text.find('\n', charStart);
			return xLen - (sizeText(text.substr(charStart, endChar != std::string::npos ? endChar - charStart : endChar)).x());
		}
	default:
		return 0;
721 722 723
	}
}

724
inline float font_round(float v)
725
{
726 727
	return round(v);
}
728

729 730
TextCache* Font::buildTextCache(const std::string& text, Eigen::Vector2f offset, unsigned int color, float xLen, Alignment alignment, float lineSpacing)
{
731 732
	float x = offset[0] + (xLen != 0 ? getNewlineStartOffset(text, 0, xLen, alignment) : 0);
	
733
	float yTop = getGlyph((UnicodeChar)'S')->bearing.y();
734 735
	float yBot = getHeight(lineSpacing);
	float y = offset[1] + (yBot + yTop)/2.0f;
736

737
	// vertices by texture
738
	std::map< FontTexture*, std::vector<TextCache::Vertex> > vertMap;
739 740 741 742 743

	size_t cursor = 0;
	UnicodeChar character;
	Glyph* glyph;
	while(cursor < text.length())
744
	{
745 746 747 748 749
		character = readUnicodeChar(text, cursor); // also advances cursor

		// invalid character
		if(character == 0)
			continue;
750

751
		if(character == (UnicodeChar)'\n')
752
		{
753
			y += getHeight(lineSpacing);
754
			x = offset[0] + (xLen != 0 ? getNewlineStartOffset(text, cursor /* cursor is already advanced */, xLen, alignment) : 0);
755 756 757
			continue;
		}

758 759 760 761
		glyph = getGlyph(character);
		if(glyph == NULL)
			continue;

762
		std::vector<TextCache::Vertex>& verts = vertMap[glyph->texture];
763 764 765
		size_t oldVertSize = verts.size();
		verts.resize(oldVertSize + 6);
		TextCache::Vertex* tri = verts.data() + oldVertSize;
766

767
		const float glyphStartX = x + glyph->bearing.x();
768

769
		const Eigen::Vector2i& textureSize = glyph->texture->textureSize;
770

771 772 773 774 775
		// triangle 1
		// round to fix some weird "cut off" text bugs
		tri[0].pos << font_round(glyphStartX), font_round(y + (glyph->texSize.y() * textureSize.y() - glyph->bearing.y()));
		tri[1].pos << font_round(glyphStartX + glyph->texSize.x() * textureSize.x()), font_round(y - glyph->bearing.y());
		tri[2].pos << tri[0].pos.x(), tri[1].pos.y();
776

777 778 779
		//tri[0].tex << 0, 0;
		//tri[0].tex << 1, 1;
		//tri[0].tex << 0, 1;
780

781 782 783
		tri[0].tex << glyph->texPos.x(), glyph->texPos.y() + glyph->texSize.y();
		tri[1].tex << glyph->texPos.x() + glyph->texSize.x(), glyph->texPos.y();
		tri[2].tex << tri[0].tex.x(), tri[1].tex.y();
784

785 786 787 788
		// triangle 2
		tri[3].pos = tri[0].pos;
		tri[4].pos = tri[1].pos;
		tri[5].pos << tri[1].pos.x(), tri[0].pos.y();
789

790 791 792 793 794 795
		tri[3].tex = tri[0].tex;
		tri[4].tex = tri[1].tex;
		tri[5].tex << tri[1].tex.x(), tri[0].tex.y();

		// advance
		x += glyph->advance.x();
796 797
	}

798 799 800 801 802 803 804 805 806 807 808
	//TextCache::CacheMetrics metrics = { sizeText(text, lineSpacing) };

	TextCache* cache = new TextCache();
	cache->vertexLists.resize(vertMap.size());
	cache->metrics = { sizeText(text, lineSpacing) };

	unsigned int i = 0;
	for(auto it = vertMap.begin(); it != vertMap.end(); it++)
	{
		TextCache::VertexList& vertList = cache->vertexLists.at(i);

809
		vertList.textureIdPtr = &it->first->textureId;
810 811 812 813 814
		vertList.verts = it->second;

		vertList.colors.resize(4 * it->second.size());
		Renderer::buildGLColorArray(vertList.colors.data(), color, it->second.size());
	}
815

816 817
	clearFaceCache();

818 819 820
	return cache;
}

821 822 823 824 825
TextCache* Font::buildTextCache(const std::string& text, float offsetX, float offsetY, unsigned int color)
{
	return buildTextCache(text, Eigen::Vector2f(offsetX, offsetY), color, 0.0f);
}

826 827
void TextCache::setColor(unsigned int color)
{
828 829
	for(auto it = vertexLists.begin(); it != vertexLists.end(); it++)
		Renderer::buildGLColorArray(it->colors.data(), color, it->verts.size());
830
}
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849

std::shared_ptr<Font> Font::getFromTheme(const ThemeData::ThemeElement* elem, unsigned int properties, const std::shared_ptr<Font>& orig)
{
	using namespace ThemeFlags;
	if(!(properties & FONT_PATH) && !(properties & FONT_SIZE))
		return orig;
	
	std::shared_ptr<Font> font;
	int size = (orig ? orig->mSize : FONT_SIZE_MEDIUM);
	std::string path = (orig ? orig->mPath : getDefaultPath());

	float sh = (float)Renderer::getScreenHeight();
	if(properties & FONT_SIZE && elem->has("fontSize")) 
		size = (int)(sh * elem->get<float>("fontSize"));
	if(properties & FONT_PATH && elem->has("fontPath"))
		path = elem->get<std::string>("fontPath");

	return get(size, path);
}