1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 
4 #include "glFont.h"
5 #include "FontLogSection.h"
6 #include <stdarg.h>
7 #include <stdexcept>
8 
9 #include "Game/Camera.h"
10 #include "Rendering/GlobalRendering.h"
11 #include "Rendering/GL/VertexArray.h"
12 #include "System/Log/ILog.h"
13 #include "System/Color.h"
14 #include "System/Exceptions.h"
15 #include "System/myMath.h"
16 #include "System/Util.h"
17 
18 #undef GetCharWidth // winapi.h
19 
20 
21 
22 /*******************************************************************************/
23 /*******************************************************************************/
24 
25 CglFont* font = nullptr;
26 CglFont* smallFont = nullptr;
27 
28 static const unsigned char nullChar = 0;
29 static const float4        white(1.00f, 1.00f, 1.00f, 0.95f);
30 static const float4  darkOutline(0.05f, 0.05f, 0.05f, 0.95f);
31 static const float4 lightOutline(0.95f, 0.95f, 0.95f, 0.8f);
32 
33 static const float darkLuminosity = 0.05 +
34 	0.2126f * math::powf(darkOutline[0], 2.2) +
35 	0.7152f * math::powf(darkOutline[1], 2.2) +
36 	0.0722f * math::powf(darkOutline[2], 2.2);
37 
38 /*******************************************************************************/
39 /*******************************************************************************/
40 
CglFont(const std::string & fontfile,int size,int _outlinewidth,float _outlineweight)41 CglFont::CglFont(const std::string& fontfile, int size, int _outlinewidth, float _outlineweight)
42 : CTextWrap(fontfile,size,_outlinewidth,_outlineweight)
43 , fontPath(fontfile)
44 , inBeginEnd(false)
45 , autoOutlineColor(true)
46 , setColor(false)
47 {
48 	va  = new CVertexArray();
49 	va2 = new CVertexArray();
50 
51 	textColor    = white;
52 	outlineColor = darkOutline;
53 }
54 
LoadFont(const std::string & fontFile,int size,int outlinewidth,float outlineweight)55 CglFont* CglFont::LoadFont(const std::string& fontFile, int size, int outlinewidth, float outlineweight)
56 {
57 	try {
58 		CglFont* newFont = new CglFont(fontFile, size, outlinewidth, outlineweight);
59 		return newFont;
60 	} catch (const content_error& ex) {
61 		LOG_L(L_ERROR, "Failed creating font: %s", ex.what());
62 		return NULL;
63 	}
64 }
65 
66 
~CglFont()67 CglFont::~CglFont()
68 {
69 	delete va;
70 	delete va2;
71 }
72 
73 
74 /*******************************************************************************/
75 /*******************************************************************************/
76 
77 template <typename T>
SkipColorCodes(const std::u8string & text,T pos)78 static inline int SkipColorCodes(const std::u8string& text, T pos)
79 {
80 	while (text[pos] == CglFont::ColorCodeIndicator) {
81 		pos += 4;
82 		if (pos >= text.size()) { return -1; }
83 	}
84 	return pos;
85 }
86 
87 
88 template <typename T>
SkipColorCodesAndNewLines(const std::u8string & text,T * pos,float4 * color,bool * colorChanged,int * skippedLines,float4 * colorReset)89 static inline bool SkipColorCodesAndNewLines(const std::u8string& text, T* pos, float4* color, bool* colorChanged, int* skippedLines, float4* colorReset)
90 {
91 	const size_t length = text.length();
92 	(*colorChanged) = false;
93 	(*skippedLines) = 0;
94 	while (*pos < length) {
95 		const char8_t& chr = text[*pos];
96 		switch(chr) {
97 			case CglFont::ColorCodeIndicator:
98 				*pos += 4;
99 				if ((*pos) < length) {
100 					(*color)[0] = text[(*pos) - 3] / 255.0f;
101 					(*color)[1] = text[(*pos) - 2] / 255.0f;
102 					(*color)[2] = text[(*pos) - 1] / 255.0f;
103 					*colorChanged = true;
104 				}
105 				break;
106 
107 			case CglFont::ColorResetIndicator:
108 				(*pos)++;
109 				(*color) = *colorReset;
110 				*colorChanged = true;
111 				break;
112 
113 			case 0x0d: // CR
114 				(*skippedLines)++;
115 				(*pos)++;
116 				if (*pos < length && text[*pos] == 0x0a) { // CR+LF
117 					(*pos)++;
118 				}
119 				break;
120 
121 			case 0x0a: // LF
122 				(*skippedLines)++;
123 				(*pos)++;
124 				break;
125 
126 			default:
127 				return false;
128 		}
129 	}
130 	return true;
131 }
132 
133 
TextStripCallback(void * data)134 static inline void TextStripCallback(void* data)
135 {
136 	CglFont::ColorMap::iterator& sci = *reinterpret_cast<CglFont::ColorMap::iterator*>(data);
137 	glColor4fv(*sci++);
138 }
139 
140 
141 /*******************************************************************************/
142 /*******************************************************************************/
143 
StripColorCodes_(const std::u8string & text)144 std::string CglFont::StripColorCodes_(const std::u8string& text)
145 {
146 	const size_t len = text.size();
147 
148 	std::string nocolor;
149 	nocolor.reserve(len);
150 	for (int i = 0; i < len; i++) {
151 		if (text[i] == ColorCodeIndicator) {
152 			i += 3;
153 		} else {
154 			nocolor += text[i];
155 		}
156 	}
157 	return nocolor;
158 }
159 
160 
GetCharacterWidth(const char32_t c)161 float CglFont::GetCharacterWidth(const char32_t c)
162 {
163 	return GetGlyph(c).advance;
164 }
165 
166 
GetTextWidth_(const std::u8string & text)167 float CglFont::GetTextWidth_(const std::u8string& text)
168 {
169 	if (text.empty()) return 0.0f;
170 
171 	float w = 0.0f;
172 	float maxw = 0.0f;
173 
174 	const GlyphInfo* prv_g=NULL;
175 	const GlyphInfo* cur_g;
176 
177 	int pos = 0;
178 	while (pos < text.length()) {
179 		const char32_t u = Utf8GetNextChar(text, pos);
180 
181 		switch (u) {
182 			// inlined colorcode
183 			case ColorCodeIndicator:
184 				pos = SkipColorCodes(text, pos - 1);
185 				if (pos<0) {
186 					pos = text.length();
187 				}
188 				break;
189 
190 			// reset color
191 			case ColorResetIndicator:
192 				break;
193 
194 			// newline
195 			case 0x0d: // CR+LF
196 				if (pos < text.length() && text[pos] == 0x0a)
197 					pos++;
198 			case 0x0a: // LF
199 				if (prv_g)
200 					w += prv_g->advance;
201 				if (w > maxw)
202 					maxw = w;
203 				w = 0.0f;
204 				prv_g = NULL;
205 				break;
206 
207 			// printable char
208 			default:
209 				cur_g = &GetGlyph(u);
210 				if (prv_g) w += GetKerning(*prv_g, *cur_g);
211 				prv_g = cur_g;
212 		}
213 	}
214 
215 	if (prv_g)
216 		w += prv_g->advance;
217 	if (w > maxw)
218 		maxw = w;
219 
220 	return maxw;
221 }
222 
223 
GetTextHeight_(const std::u8string & text,float * descender,int * numLines)224 float CglFont::GetTextHeight_(const std::u8string& text, float* descender, int* numLines)
225 {
226 	if (text.empty()) {
227 		if (descender) *descender = 0.0f;
228 		if (numLines) *numLines = 0;
229 		return 0.0f;
230 	}
231 
232 	float h = 0.0f, d = GetLineHeight() + GetDescender();
233 	unsigned int multiLine = 1;
234 
235 	int pos = 0;
236 	while (pos < text.length()) {
237 		const char32_t u = Utf8GetNextChar(text, pos);
238 		switch(u) {
239 			// inlined colorcode
240 			case ColorCodeIndicator:
241 				pos = SkipColorCodes(text, pos - 1);
242 				if (pos<0) {
243 					pos = text.length();
244 				}
245 				break;
246 
247 			// reset color
248 			case ColorResetIndicator:
249 				break;
250 
251 			// newline
252 			case 0x0d: // CR+LF
253 				if (pos < text.length() && text[pos] == 0x0a)
254 					++pos;
255 			case 0x0a: // LF
256 				multiLine++;
257 				d = GetLineHeight() + GetDescender();
258 				break;
259 
260 			// printable char
261 			default:
262 				const GlyphInfo& g = GetGlyph(u);
263 				if (g.descender < d) d = g.descender;
264 				if (multiLine < 2 && g.height > h) h = g.height; // only calc height for the first line
265 		}
266 	}
267 
268 	if (multiLine>1) d -= (multiLine-1) * GetLineHeight();
269 	if (descender) *descender = d;
270 	if (numLines) *numLines = multiLine;
271 
272 	return h;
273 }
274 
275 
GetTextNumLines_(const std::u8string & text)276 int CglFont::GetTextNumLines_(const std::u8string& text)
277 {
278 	if (text.empty())
279 		return 0;
280 
281 	int lines = 1;
282 
283 	for (int pos = 0 ; pos < text.length(); pos++) {
284 		const char8_t& c = text[pos];
285 		switch(c) {
286 			// inlined colorcode
287 			case ColorCodeIndicator:
288 				pos = SkipColorCodes(text, pos);
289 				if (pos<0) {
290 					pos = text.length();
291 				} else {
292 					pos--;
293 				}
294 				break;
295 
296 			// reset color
297 			case ColorResetIndicator:
298 				break;
299 
300 			// newline
301 			case 0x0d:
302 				if (pos+1 < text.length() && text[pos+1] == 0x0a)
303 					pos++;
304 			case 0x0a:
305 				lines++;
306 				break;
307 
308 			//default:
309 		}
310 	}
311 
312 	return lines;
313 }
314 
315 
SplitIntoLines(const std::u8string & text)316 std::list<std::string> CglFont::SplitIntoLines(const std::u8string& text)
317 {
318 	std::list<std::string> lines;
319 
320 	if (text.empty())
321 		return lines;
322 
323 	lines.push_back("");
324 	std::list<std::string> colorCodeStack;
325 	for (int pos = 0 ; pos < text.length(); pos++) {
326 		const char8_t& c = text[pos];
327 		switch(c) {
328 			// inlined colorcode
329 			case ColorCodeIndicator:
330 				if ((pos + 3) < text.length()) {
331 					colorCodeStack.push_back(text.substr(pos, 4));
332 					lines.back() += colorCodeStack.back();
333 					pos += 3;
334 				}
335 				break;
336 
337 			// reset color
338 			case ColorResetIndicator:
339 				colorCodeStack.pop_back();
340 				lines.back() += c;
341 				break;
342 
343 			// newline
344 			case 0x0d:
345 				if (pos+1 < text.length() && text[pos+1] == 0x0a)
346 					pos++;
347 			case 0x0a:
348 				lines.push_back("");
349 				for (auto& color: colorCodeStack)
350 					lines.back() = color;
351 				break;
352 
353 			default:
354 				lines.back() += c;
355 		}
356 	}
357 
358 	return lines;
359 }
360 
361 
362 /*******************************************************************************/
363 /*******************************************************************************/
364 
SetAutoOutlineColor(bool enable)365 void CglFont::SetAutoOutlineColor(bool enable)
366 {
367 	autoOutlineColor = enable;
368 }
369 
370 
SetTextColor(const float4 * color)371 void CglFont::SetTextColor(const float4* color)
372 {
373 	if (color == NULL) color = &white;
374 
375 	if (inBeginEnd && !(*color==textColor)) {
376 		if (va->drawIndex() == 0 && !stripTextColors.empty()) {
377 			stripTextColors.back() = *color;
378 		} else {
379 			stripTextColors.push_back(*color);
380 			va->EndStrip();
381 		}
382 	}
383 
384 	textColor = *color;
385 }
386 
387 
SetOutlineColor(const float4 * color)388 void CglFont::SetOutlineColor(const float4* color)
389 {
390 	if (color == NULL) color = ChooseOutlineColor(textColor);
391 
392 	if (inBeginEnd && !(*color==outlineColor)) {
393 		if (va2->drawIndex() == 0 && !stripOutlineColors.empty()) {
394 			stripOutlineColors.back() = *color;
395 		} else {
396 			stripOutlineColors.push_back(*color);
397 			va2->EndStrip();
398 		}
399 	}
400 
401 	outlineColor = *color;
402 }
403 
404 
SetColors(const float4 * _textColor,const float4 * _outlineColor)405 void CglFont::SetColors(const float4* _textColor, const float4* _outlineColor)
406 {
407 	SetTextColor(_textColor);
408 	SetOutlineColor(_outlineColor);
409 }
410 
411 
ChooseOutlineColor(const float4 & textColor)412 const float4* CglFont::ChooseOutlineColor(const float4& textColor)
413 {
414 	const float luminosity = 0.05 +
415 				 0.2126f * math::powf(textColor[0], 2.2) +
416 				 0.7152f * math::powf(textColor[1], 2.2) +
417 				 0.0722f * math::powf(textColor[2], 2.2);
418 
419 	const float lumdiff = std::max(luminosity,darkLuminosity) / std::min(luminosity,darkLuminosity);
420 	if (lumdiff > 5.0f) {
421 		return &darkOutline;
422 	} else {
423 		return &lightOutline;
424 	}
425 }
426 
427 
428 /*******************************************************************************/
429 /*******************************************************************************/
430 
Begin(const bool immediate,const bool resetColors)431 void CglFont::Begin(const bool immediate, const bool resetColors)
432 {
433 	if (inBeginEnd) {
434 		LOG_L(L_ERROR, "called Begin() multiple times");
435 		return;
436 	}
437 
438 	autoOutlineColor = true;
439 
440 	setColor = !immediate;
441 	if (resetColors) {
442 		SetColors(); // reset colors
443 	}
444 
445 	inBeginEnd = true;
446 
447 	va->Initialize();
448 	va2->Initialize();
449 	stripTextColors.clear();
450 	stripOutlineColors.clear();
451 	stripTextColors.push_back(textColor);
452 	stripOutlineColors.push_back(outlineColor);
453 
454 	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT);
455 	glDisable(GL_LIGHTING);
456 	glDisable(GL_DEPTH_TEST);
457 	glEnable(GL_BLEND);
458 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
459 }
460 
461 
End()462 void CglFont::End()
463 {
464 	if (!inBeginEnd) {
465 		LOG_L(L_ERROR, "called End() without Begin()");
466 		return;
467 	}
468 	inBeginEnd = false;
469 
470 	if (va->drawIndex() == 0) {
471 		glPopAttrib();
472 		return;
473 	}
474 
475 	GLboolean inListCompile;
476 	glGetBooleanv(GL_LIST_INDEX, &inListCompile);
477 	if (!inListCompile) {
478 		UpdateTexture();
479 	}
480 
481 	glEnable(GL_TEXTURE_2D);
482 	glBindTexture(GL_TEXTURE_2D, GetTexture());
483 
484 	// Because texture size can change, texture coordinats are absolute in texels.
485 	// We could use also just use GL_TEXTURE_RECTANGLE
486 	// but then all shaders would need to detect so and use different funcs & types if supported -> more work
487 	glMatrixMode(GL_TEXTURE);
488 	glPushMatrix();
489 	glCallList(textureSpaceMatrix);
490 	glMatrixMode(GL_MODELVIEW);
491 
492 	if (va2->drawIndex() > 0) {
493 		if (stripOutlineColors.size() > 1) {
494 			ColorMap::iterator sci = stripOutlineColors.begin();
495 			va2->DrawArray2dT(GL_QUADS,TextStripCallback,&sci);
496 		} else {
497 			glColor4fv(outlineColor);
498 			va2->DrawArray2dT(GL_QUADS);
499 		}
500 	}
501 
502 	if (stripTextColors.size() > 1) {
503 		ColorMap::iterator sci = stripTextColors.begin();
504 		va->DrawArray2dT(GL_QUADS,TextStripCallback,&sci);//FIXME calls a 0 length strip!
505 	} else {
506 		if (setColor) glColor4fv(textColor);
507 		va->DrawArray2dT(GL_QUADS);
508 	}
509 
510 	// pop texture matrix
511 	glMatrixMode(GL_TEXTURE);
512 	glPopMatrix();
513 	glMatrixMode(GL_MODELVIEW);
514 
515 	glPopAttrib();
516 }
517 
518 
519 /*******************************************************************************/
520 /*******************************************************************************/
521 
RenderString(float x,float y,const float & scaleX,const float & scaleY,const std::string & str)522 void CglFont::RenderString(float x, float y, const float& scaleX, const float& scaleY, const std::string& str)
523 {
524 	/**
525 	 * NOTE:
526 	 * Font rendering does not use display lists, but VAs. It's actually faster
527 	 * (450% faster with a 7600GT!) for these reasons:
528 	 *
529 	 * 1. When using DLs, we can not group multiple glyphs into one glBegin/End pair
530 	 *    because glTranslatef can not go between such a pair.
531 	 * 2. We can now eliminate all glPushMatrix/PopMatrix pairs related to font rendering
532 	 *    because the transformations are calculated on the fly. These are just a couple of
533 	 *    floating point multiplications and shouldn't be too expensive.
534 	 */
535 
536 	const float startx = x;
537 	const float lineHeight_ = scaleY * GetLineHeight();
538 	unsigned int length = (unsigned int)str.length();
539 	const std::u8string& ustr = toustring(str);
540 
541 	va->EnlargeArrays(length * 4, 0, VA_SIZE_2DT);
542 
543 	int skippedLines;
544 	bool colorChanged;
545 	const GlyphInfo* g = NULL;
546 	float4 newColor = textColor;
547 	char32_t c;
548 	int i = 0;
549 
550 	do {
551 		const bool endOfString = SkipColorCodesAndNewLines(ustr, &i, &newColor, &colorChanged, &skippedLines, &baseTextColor);
552 
553 		if (endOfString)
554 			return;
555 
556 		c = Utf8GetNextChar(str,i);
557 
558 		if (colorChanged) {
559 			if (autoOutlineColor) {
560 				SetColors(&newColor,NULL);
561 			} else {
562 				SetTextColor(&newColor);
563 			}
564 		}
565 		const GlyphInfo* c_g = &GetGlyph(c);
566 		if (skippedLines>0) {
567 			x  = startx;
568 			y -= skippedLines * lineHeight_;
569 		} else if (g) {
570 			x += scaleX * GetKerning(*g, *c_g);
571 		}
572 
573 		g = c_g;
574 
575 		const auto&  tc = g->texCord;
576 		const float dx0 = (scaleX * g->size.x0()) + x, dy0 = (scaleY * g->size.y0()) + y;
577 		const float dx1 = (scaleX * g->size.x1()) + x, dy1 = (scaleY * g->size.y1()) + y;
578 
579 		va->AddVertexQ2dT(dx0, dy1, tc.x0(), tc.y1());
580 		va->AddVertexQ2dT(dx0, dy0, tc.x0(), tc.y0());
581 		va->AddVertexQ2dT(dx1, dy0, tc.x1(), tc.y0());
582 		va->AddVertexQ2dT(dx1, dy1, tc.x1(), tc.y1());
583 	} while(true);
584 }
585 
586 
RenderStringShadow(float x,float y,const float & scaleX,const float & scaleY,const std::string & str)587 void CglFont::RenderStringShadow(float x, float y, const float& scaleX, const float& scaleY, const std::string& str)
588 {
589 	const float shiftX = scaleX*0.1, shiftY = scaleY*0.1;
590 	const float ssX = (scaleX/fontSize) * GetOutlineWidth(), ssY = (scaleY/fontSize) * GetOutlineWidth();
591 
592 	const float startx = x;
593 	const float lineHeight_ = scaleY * GetLineHeight();
594 	unsigned int length = (unsigned int)str.length();
595 	const std::u8string& ustr = toustring(str);
596 
597 	va->EnlargeArrays(length * 4, 0, VA_SIZE_2DT);
598 	va2->EnlargeArrays(length * 4, 0, VA_SIZE_2DT);
599 
600 	int skippedLines;
601 	bool colorChanged;
602 	const GlyphInfo* g = NULL;
603 	float4 newColor = textColor;
604 	char32_t c;
605 	int i = 0;
606 
607 	do {
608 		const bool endOfString = SkipColorCodesAndNewLines(ustr, &i, &newColor, &colorChanged, &skippedLines, &baseTextColor);
609 
610 		if (endOfString)
611 			return;
612 
613 		c = Utf8GetNextChar(str,i);
614 
615 		if (colorChanged) {
616 			if (autoOutlineColor) {
617 				SetColors(&newColor,NULL);
618 			} else {
619 				SetTextColor(&newColor);
620 			}
621 		}
622 
623 		const GlyphInfo* c_g = &GetGlyph(c);
624 		if (skippedLines>0) {
625 			x  = startx;
626 			y -= skippedLines * lineHeight_;
627 		} else if (g) {
628 			x += scaleX * GetKerning(*g, *c_g);
629 		}
630 
631 		g = c_g;
632 
633 		const auto&  tc = g->texCord;
634 		const auto& stc = g->shadowTexCord;
635 		const float dx0 = (scaleX * g->size.x0()) + x, dy0 = (scaleY * g->size.y0()) + y;
636 		const float dx1 = (scaleX * g->size.x1()) + x, dy1 = (scaleY * g->size.y1()) + y;
637 
638 		// draw shadow
639 		va2->AddVertexQ2dT(dx0+shiftX-ssX, dy1-shiftY-ssY, stc.x0(), stc.y1());
640 		va2->AddVertexQ2dT(dx0+shiftX-ssX, dy0-shiftY+ssY, stc.x0(), stc.y0());
641 		va2->AddVertexQ2dT(dx1+shiftX+ssX, dy0-shiftY+ssY, stc.x1(), stc.y0());
642 		va2->AddVertexQ2dT(dx1+shiftX+ssX, dy1-shiftY-ssY, stc.x1(), stc.y1());
643 
644 		// draw the actual character
645 		va->AddVertexQ2dT(dx0, dy1, tc.x0(), tc.y1());
646 		va->AddVertexQ2dT(dx0, dy0, tc.x0(), tc.y0());
647 		va->AddVertexQ2dT(dx1, dy0, tc.x1(), tc.y0());
648 		va->AddVertexQ2dT(dx1, dy1, tc.x1(), tc.y1());
649 	} while(true);
650 }
651 
RenderStringOutlined(float x,float y,const float & scaleX,const float & scaleY,const std::string & str)652 void CglFont::RenderStringOutlined(float x, float y, const float& scaleX, const float& scaleY, const std::string& str)
653 {
654 	const float shiftX = (scaleX/fontSize) * GetOutlineWidth(), shiftY = (scaleY/fontSize) * GetOutlineWidth();
655 
656 	const float startx = x;
657 	const float lineHeight_ = scaleY * GetLineHeight();
658 	const std::u8string& ustr = toustring(str);
659 	const size_t length = str.length();
660 
661 	va->EnlargeArrays(length * 4, 0, VA_SIZE_2DT);
662 	va2->EnlargeArrays(length * 4, 0, VA_SIZE_2DT);
663 
664 	int skippedLines;
665 	bool colorChanged;
666 	const GlyphInfo* g = NULL;
667 	float4 newColor = textColor;
668 	char32_t c;
669 	int i = 0;
670 
671 	do {
672 		const bool endOfString = SkipColorCodesAndNewLines(ustr, &i, &newColor, &colorChanged, &skippedLines, &baseTextColor);
673 
674 		if (endOfString)
675 			return;
676 
677 		c = Utf8GetNextChar(str,i);
678 
679 		if (colorChanged) {
680 			if (autoOutlineColor) {
681 				SetColors(&newColor,NULL);
682 			} else {
683 				SetTextColor(&newColor);
684 			}
685 		}
686 
687 		const GlyphInfo* c_g = &GetGlyph(c);
688 		if (skippedLines>0) {
689 			x  = startx;
690 			y -= skippedLines * lineHeight_;
691 		} else if (g) {
692 			x += scaleX * GetKerning(*g, *c_g);
693 		}
694 
695 		g = c_g;
696 
697 		const auto&  tc = g->texCord;
698 		const auto& stc = g->shadowTexCord;
699 		const float dx0 = (scaleX * g->size.x0()) + x, dy0 = (scaleY * g->size.y0()) + y;
700 		const float dx1 = (scaleX * g->size.x1()) + x, dy1 = (scaleY * g->size.y1()) + y;
701 
702 		// draw outline
703 		va2->AddVertexQ2dT(dx0-shiftX, dy1-shiftY, stc.x0(), stc.y1());
704 		va2->AddVertexQ2dT(dx0-shiftX, dy0+shiftY, stc.x0(), stc.y0());
705 		va2->AddVertexQ2dT(dx1+shiftX, dy0+shiftY, stc.x1(), stc.y0());
706 		va2->AddVertexQ2dT(dx1+shiftX, dy1-shiftY, stc.x1(), stc.y1());
707 
708 		// draw the actual character
709 		va->AddVertexQ2dT(dx0, dy1, tc.x0(), tc.y1());
710 		va->AddVertexQ2dT(dx0, dy0, tc.x0(), tc.y0());
711 		va->AddVertexQ2dT(dx1, dy0, tc.x1(), tc.y0());
712 		va->AddVertexQ2dT(dx1, dy1, tc.x1(), tc.y1());
713 	} while(true);
714 }
715 
716 
glWorldPrint(const float3 & p,const float size,const std::string & str)717 void CglFont::glWorldPrint(const float3& p, const float size, const std::string& str)
718 {
719 	glPushMatrix();
720 	glTranslatef(p.x, p.y, p.z);
721 	glMultMatrixf(camera->GetBillBoardMatrix());
722 	Begin(false, false);
723 	glPrint(0.0f, 0.0f, size, FONT_DESCENDER | FONT_CENTER | FONT_OUTLINE, str);
724 	End();
725 	glPopMatrix();
726 }
727 
728 
glPrint(float x,float y,float s,const int options,const std::string & text)729 void CglFont::glPrint(float x, float y, float s, const int options, const std::string& text)
730 {
731 	// s := scale or absolute size?
732 	if (options & FONT_SCALE) {
733 		s *= fontSize;
734 	}
735 
736 	float sizeX = s, sizeY = s;
737 
738 	// render in normalized coords (0..1) instead of screencoords (0..~1024)
739 	if (options & FONT_NORM) {
740 		sizeX *= globalRendering->pixelX;
741 		sizeY *= globalRendering->pixelY;
742 	}
743 
744 	// horizontal alignment (FONT_LEFT is default)
745 	if (options & FONT_CENTER) {
746 		x -= sizeX * 0.5f * GetTextWidth(text);
747 	} else if (options & FONT_RIGHT) {
748 		x -= sizeX * GetTextWidth(text);
749 	}
750 
751 
752 	// vertical alignment
753 	y += sizeY * GetDescender(); // move to baseline (note: descender is negative)
754 	if (options & FONT_BASELINE) {
755 		// nothing
756 	} else if (options & FONT_DESCENDER) {
757 		y -= sizeY * GetDescender();
758 	} else if (options & FONT_VCENTER) {
759 		float textDescender;
760 		y -= sizeY * 0.5f * GetTextHeight(text,&textDescender);
761 		y -= sizeY * 0.5f * textDescender;
762 	} else if (options & FONT_TOP) {
763 		y -= sizeY * GetTextHeight(text);
764 	} else if (options & FONT_ASCENDER) {
765 		y -= sizeY * GetDescender();
766 		y -= sizeY;
767 	} else if (options & FONT_BOTTOM) {
768 		float textDescender;
769 		GetTextHeight(text,&textDescender);
770 		y -= sizeY * textDescender;
771 	}
772 
773 	if (options & FONT_NEAREST) {
774 		x = (int)x;
775 		y = (int)y;
776 	}
777 
778 	// backup text & outline colors (also ::ColorResetIndicator will reset to those)
779 	baseTextColor = textColor;
780 	baseOutlineColor = outlineColor;
781 
782 	// immediate mode?
783 	const bool immediate = !inBeginEnd;
784 	if (immediate) {
785 		Begin(!(options & (FONT_OUTLINE | FONT_SHADOW)));
786 	}
787 
788 
789 	// select correct decoration RenderString function
790 	if (options & FONT_OUTLINE) {
791 		RenderStringOutlined(x, y, sizeX, sizeY, text);
792 	} else if (options & FONT_SHADOW) {
793 		RenderStringShadow(x, y, sizeX, sizeY, text);
794 	} else {
795 		RenderString(x, y, sizeX, sizeY, text);
796 	}
797 
798 
799 	// immediate mode?
800 	if (immediate) {
801 		End();
802 	}
803 
804 	// reset text & outline colors (if changed via in text colorcodes)
805 	SetColors(&baseTextColor,&baseOutlineColor);
806 }
807 
glPrintTable(float x,float y,float s,const int options,const std::string & text)808 void CglFont::glPrintTable(float x, float y, float s, const int options, const std::string& text)
809 {
810 	std::vector<std::string> coltext;
811 	coltext.push_back("");
812 
813 	std::vector<SColor> colColor;
814 	SColor defaultcolor(0,0,0);
815 	defaultcolor[0] = ColorCodeIndicator;
816 	for (int i = 0; i < 3; ++i)
817 		defaultcolor[i+1] = (unsigned char)(textColor[i] * 255.0f);
818 	colColor.push_back(defaultcolor);
819 	SColor curcolor(defaultcolor);
820 
821 	int col = 0;
822 	int row = 0;
823 	for (int pos = 0; pos < text.length(); pos++) {
824 		const unsigned char& c = text[pos];
825 		switch(c) {
826 			// inline colorcodes
827 			case ColorCodeIndicator:
828 				for (int i = 0; i < 4 && pos < text.length(); ++i, ++pos) {
829 					coltext[col] += text[pos];
830 					curcolor[i] = text[pos];
831 				}
832 				colColor[col] = curcolor;
833 				--pos;
834 				break;
835 
836 			// column separator is `\t`==`horizontal tab`
837 			case '\t':
838 				++col;
839 				if (col >= coltext.size()) {
840 					coltext.push_back("");
841 					for(int i = 0; i < row; ++i)
842 						coltext[col] += 0x0a;
843 					colColor.push_back(defaultcolor);
844 				}
845 				if (colColor[col] != curcolor) {
846 					for(int i = 0; i < 4; ++i)
847 						coltext[col] += curcolor[i];
848 					colColor[col] = curcolor;
849 				}
850 				break;
851 
852 			// newline
853 			case 0x0d: // CR+LF
854 				if (pos+1 < text.length() && text[pos + 1] == 0x0a)
855 					pos++;
856 			case 0x0a: // LF
857 				for (int i = 0; i < coltext.size(); ++i)
858 					coltext[i] += 0x0a;
859 				if (colColor[0] != curcolor) {
860 					for(int i = 0; i < 4; ++i)
861 						coltext[0] += curcolor[i];
862 					colColor[0] = curcolor;
863 				}
864 				col = 0;
865 				++row;
866 				break;
867 
868 			// printable char
869 			default:
870 				coltext[col] += c;
871 		}
872 	}
873 
874 	float totalWidth = 0.0f;
875 	float maxHeight = 0.0f;
876 	float minDescender = 0.0f;
877 	std::vector<float> colWidths(coltext.size(), 0.0f);
878 	for (int i = 0; i < coltext.size(); ++i) {
879 		float colwidth = GetTextWidth(coltext[i]);
880 		colWidths[i] = colwidth;
881 		totalWidth += colwidth;
882 		float textDescender;
883 		float textHeight = GetTextHeight(coltext[i], &textDescender);
884 		if (textHeight > maxHeight)
885 			maxHeight = textHeight;
886 		if (textDescender < minDescender)
887 			minDescender = textDescender;
888 	}
889 
890 	// s := scale or absolute size?
891 	float ss = s;
892 	if (options & FONT_SCALE) {
893 		ss *= fontSize;
894 	}
895 
896 	float sizeX = ss, sizeY = ss;
897 
898 	// render in normalized coords (0..1) instead of screencoords (0..~1024)
899 	if (options & FONT_NORM) {
900 		sizeX *= globalRendering->pixelX;
901 		sizeY *= globalRendering->pixelY;
902 	}
903 
904 	// horizontal alignment (FONT_LEFT is default)
905 	if (options & FONT_CENTER) {
906 		x -= sizeX * 0.5f * totalWidth;
907 	} else if (options & FONT_RIGHT) {
908 		x -= sizeX * totalWidth;
909 	}
910 
911 	// vertical alignment
912 	if (options & FONT_BASELINE) {
913 		// nothing
914 	} else if (options & FONT_DESCENDER) {
915 		y -= sizeY * GetDescender();
916 	} else if (options & FONT_VCENTER) {
917 		y -= sizeY * 0.5f * maxHeight;
918 		y -= sizeY * 0.5f * minDescender;
919 	} else if (options & FONT_TOP) {
920 		y -= sizeY * maxHeight;
921 	} else if (options & FONT_ASCENDER) {
922 		y -= sizeY * GetDescender();
923 		y -= sizeY;
924 	} else if (options & FONT_BOTTOM) {
925 		y -= sizeY * minDescender;
926 	}
927 
928 	for (int i = 0; i < coltext.size(); ++i) {
929 		glPrint(x, y, s, (options | FONT_BASELINE) & ~(FONT_RIGHT | FONT_CENTER), coltext[i]);
930 		x += sizeX * colWidths[i];
931 	}
932 }
933 
934 
935 // macro for formatting printf-style
936 #define FORMAT_STRING(lastarg,fmt,out)                         \
937 		char out[512];                         \
938 		va_list ap;                            \
939 		if (fmt == NULL) return;               \
940 		va_start(ap, lastarg);                 \
941 		VSNPRINTF(out, sizeof(out), fmt, ap);  \
942 		va_end(ap);
943 
glFormat(float x,float y,float s,const int options,const char * fmt,...)944 void CglFont::glFormat(float x, float y, float s, const int options, const char* fmt, ...)
945 {
946 	FORMAT_STRING(fmt,fmt,text);
947 	glPrint(x, y, s, options, std::string(text));
948 }
949 
950 
glFormat(float x,float y,float s,const int options,const std::string & fmt,...)951 void CglFont::glFormat(float x, float y, float s, const int options, const std::string& fmt, ...)
952 {
953 	FORMAT_STRING(fmt,fmt.c_str(),text);
954 	glPrint(x, y, s, options, std::string(text));
955 }
956