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