1 /*
2 Copyright (C) 2010 COR Entertainment, LLC.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20
21 /* r_text.c
22 *
23 * Generic "front" for text rendering.
24 */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <qcommon/htable.h>
31 #include "r_local.h"
32
33 #include "r_text.h"
34 #include "r_ttf.h"
35
36
37
38 /* Face lookup table. */
39 static hashtable_t _FNT_LoadedFaces = NULL;
40
41 /* Font lookup table. */
42 static hashtable_t _FNT_LoadedFonts = NULL;
43
44
45 /* Color table */
46 float FNT_colors[ 8 ][ 4 ] =
47 {
48 {0.0, 0.0, 0.0, 1.0},
49 {1.0, 0.0, 0.0, 1.0},
50 {0.0, 1.0, 0.0, 1.0},
51 {1.0, 1.0, 0.0, 1.0},
52 {0.0, 0.0, 1.0, 1.0},
53 {0.0, 1.0, 1.0, 1.0},
54 {1.0, 0.0, 1.0, 1.0},
55 {1.0, 1.0, 1.0, 1.0},
56 };
57
58
59
60
61 /**************************************************************************/
62 /* NULL FONT */
63 /**************************************************************************/
64
65 /* Null font */
66 static struct FNT_font_s _FNT_NullFontStruct;
67 static FNT_font_t _FNT_NullFont = NULL;
68
69
_FNT_NullFont_Destroy(FNT_font_t font)70 static void _FNT_NullFont_Destroy( FNT_font_t font )
71 {
72 Com_Error( ERR_FATAL , "FNT_NullFont_Destroy called - this is a bug" );
73 }
74
75
_FNT_NullFont_RawPrint(FNT_font_t font,const char * text,unsigned int text_length,qboolean r2l,float x,float y,const float color[4])76 static void _FNT_NullFont_RawPrint(
77 FNT_font_t font ,
78 const char * text ,
79 unsigned int text_length ,
80 qboolean r2l ,
81 float x ,
82 float y ,
83 const float color[4] )
84 {
85 /* EMPTY */
86 }
87
88
_FNT_NullFont_BoundedPrint(FNT_font_t font,const char * text,unsigned int cmode,unsigned int align,FNT_window_t box,const float * color)89 static void _FNT_NullFont_BoundedPrint(
90 FNT_font_t font ,
91 const char * text ,
92 unsigned int cmode ,
93 unsigned int align ,
94 FNT_window_t box ,
95 const float * color
96 )
97 {
98 /* EMPTY */
99 }
100
101
_FNT_NullFont_WrappedPrint(FNT_font_t font,const char * text,unsigned int cmode,unsigned int align,unsigned int indent,FNT_window_t box,const float * color)102 static void _FNT_NullFont_WrappedPrint(
103 FNT_font_t font ,
104 const char * text ,
105 unsigned int cmode ,
106 unsigned int align ,
107 unsigned int indent ,
108 FNT_window_t box ,
109 const float * color
110 )
111 {
112 /* EMPTY */
113 }
114
115
_FNT_NullFont_PredictSize(FNT_font_t font,const char * text,qboolean color)116 static int _FNT_NullFont_PredictSize(
117 FNT_font_t font ,
118 const char * text,
119 qboolean color
120 )
121 {
122 /* EMPTY */
123 }
124
125
126
127 /**************************************************************************/
128 /* AUTOMATIC FONT MANAGEMENT */
129 /**************************************************************************/
130
131 /* List head for automated fonts */
132 static struct FNT_auto_s _FNT_AutoListHead;
133 static FNT_auto_t _FNT_AutoList = NULL;
134
135 /* Initialise an automatic font structure. */
FNT_AutoInit(FNT_auto_t auto_font,const char * default_face,int default_size,unsigned int auto_lines,unsigned int min_size,unsigned int max_size)136 void FNT_AutoInit(
137 FNT_auto_t auto_font ,
138 const char * default_face ,
139 int default_size ,
140 unsigned int auto_lines ,
141 unsigned int min_size ,
142 unsigned int max_size )
143 {
144 assert( auto_font != NULL );
145 assert( default_face != NULL );
146 assert( auto_lines > 8 );
147 assert( min_size != 0 );
148 assert( max_size > min_size );
149 assert( default_size == 0 || default_size > min_size );
150
151 auto_font->next = auto_font->previous = auto_font;
152
153 Q_strncpyz2( auto_font->face , default_face , sizeof( auto_font->face ) );
154 auto_font->faceVar = NULL;
155
156 auto_font->size = default_size;
157 auto_font->sizeVar = NULL;
158 auto_font->lines = auto_lines;
159 auto_font->minSize = min_size;
160 auto_font->maxSize = max_size;
161 }
162
163
164 /* Register an automatic font. */
FNT_AutoRegister(FNT_auto_t auto_font)165 void FNT_AutoRegister(
166 FNT_auto_t auto_font
167 )
168 {
169 assert( auto_font != NULL );
170
171 if ( _FNT_AutoList == NULL ) {
172 _FNT_AutoListHead.next = _FNT_AutoListHead.previous = _FNT_AutoList = &_FNT_AutoListHead;
173 }
174
175 if ( auto_font->previous != auto_font->next || auto_font->previous != auto_font ) {
176 return;
177 }
178
179 auto_font->previous = _FNT_AutoList->previous;
180 auto_font->next = _FNT_AutoList;
181 auto_font->next->previous = auto_font->previous->next = auto_font;
182
183 auto_font->font = &_FNT_NullFontStruct;
184 }
185
186
187 /* Access the font from an automatic font structure. */
FNT_AutoGet(FNT_auto_t auto_font)188 FNT_font_t FNT_AutoGet(
189 FNT_auto_t auto_font
190 )
191 {
192 int size;
193 FNT_face_t face;
194 qboolean fvMod , svMod;
195
196 assert( _FNT_AutoList != NULL );
197 assert( auto_font->previous != auto_font );
198
199 fvMod = auto_font->faceVar && auto_font->faceVar->modified;
200 svMod = auto_font->sizeVar && auto_font->sizeVar->modified;
201
202 // Do we need to load a font?
203 if ( auto_font->font != _FNT_NullFont ) {
204 if ( ! ( fvMod || svMod ) ) {
205 return auto_font->font;
206 }
207 FNT_ReleaseFont( auto_font->font );
208 }
209
210 // Mark other automatic fonts for reload if we have modified variables
211 if ( fvMod || svMod ) {
212 FNT_auto_t current = auto_font->next;
213
214 while ( current != auto_font ) {
215 if ( current != _FNT_AutoList && current->font != _FNT_NullFont
216 && ( ( fvMod && auto_font->faceVar == current->faceVar )
217 || ( svMod && auto_font->sizeVar == current->sizeVar ) ) ) {
218 FNT_ReleaseFont( current->font );
219 current->font = _FNT_NullFont;
220 }
221 current = current->next;
222 }
223 }
224
225 // Load face
226 if ( auto_font->faceVar ) {
227 face = FNT_GetFace( auto_font->faceVar->string );
228 auto_font->faceVar->modified = false;
229 } else {
230 face = NULL;
231 }
232 if ( face == NULL ) {
233 // fallback font
234 face = FNT_GetFace( auto_font->face );
235 }
236
237 // Get or compute font size
238 if ( auto_font->sizeVar ) {
239 size = auto_font->sizeVar->integer;
240 auto_font->sizeVar->modified = false;
241 } else {
242 size = auto_font->size;
243 }
244 if ( size <= 0 ) {
245 size = viddef.height / auto_font->lines;
246 }
247 if ( size < auto_font->minSize ) {
248 size = auto_font->minSize;
249 } else if ( size > auto_font->maxSize ) {
250 size = auto_font->maxSize;
251 }
252
253 return ( auto_font->font = FNT_GetFont( face , size ) );
254 }
255
256
257
258 /**************************************************************************/
259 /* FONT FUNCTIONS */
260 /**************************************************************************/
261
262 /*
263 * Initialise lookup tables for the text rendering front-end.
264 */
FNT_Initialise()265 qboolean FNT_Initialise( )
266 {
267 if ( _FNT_LoadedFaces != NULL )
268 return true;
269
270 // Set up Null font if necessary
271 if ( _FNT_NullFont == NULL ) {
272 _FNT_NullFont = &_FNT_NullFontStruct;
273 memset( _FNT_NullFont , 0 , sizeof( struct FNT_font_s ) );
274 _FNT_NullFont->Destroy = _FNT_NullFont_Destroy;
275 _FNT_NullFont->RawPrint = _FNT_NullFont_RawPrint;
276 _FNT_NullFont->BoundedPrint = _FNT_NullFont_BoundedPrint;
277 _FNT_NullFont->WrappedPrint = _FNT_NullFont_WrappedPrint;
278 _FNT_NullFont->PredictSize = _FNT_NullFont_PredictSize;
279 }
280
281 // Create hash tables for both faces and fonts
282 _FNT_LoadedFaces = HT_Create( 100 , HT_FLAG_INTABLE ,
283 sizeof( struct FNT_face_s ) , 0 , FNT_FACE_NAME_MAX );
284 _FNT_LoadedFonts = HT_Create( 400 , HT_FLAG_INTABLE ,
285 sizeof( struct FNT_font_s ) , 0 , FNT_FONT_KEY_MAX );
286
287 // Initialise TrueType engine
288 if ( ! TTF_Initialise( ) ) {
289 HT_Destroy( _FNT_LoadedFonts );
290 HT_Destroy( _FNT_LoadedFaces );
291 return false;
292 }
293
294 return true;
295 }
296
297
298 /*
299 * Function used to destroy fonts
300 */
_FNT_DestroyAllFonts(void * item,void * extra)301 static qboolean _FNT_DestroyAllFonts( void * item , void * extra )
302 {
303 FNT_font_t font = (FNT_font_t) item;
304 font->Destroy( font );
305 return true;
306 }
307
308
309 /*
310 * Function used to destroy faces
311 */
_FNT_DestroyAllFaces(void * item,void * extra)312 static qboolean _FNT_DestroyAllFaces( void * item , void * extra )
313 {
314 FNT_face_t face = (FNT_face_t) item;
315 face->Destroy( face );
316 return true;
317 }
318
319
320 /*
321 * Destroy the text drawing front-end
322 */
FNT_Shutdown()323 void FNT_Shutdown( )
324 {
325 FNT_auto_t item;
326
327 if ( _FNT_LoadedFaces == NULL )
328 return;
329
330 // Reset all automatic fonts to the Null font if necessary
331 if ( _FNT_AutoList != NULL ) {
332 item = _FNT_AutoList->next;
333 while ( item != _FNT_AutoList ) {
334 item->font = _FNT_NullFont;
335 item = item->next;
336 }
337 }
338
339 // Destroy all existing fonts and faces
340 HT_Apply( _FNT_LoadedFonts , _FNT_DestroyAllFonts , NULL );
341 HT_Destroy( _FNT_LoadedFonts );
342 _FNT_LoadedFonts = NULL;
343 HT_Apply( _FNT_LoadedFaces , _FNT_DestroyAllFaces , NULL );
344 HT_Destroy( _FNT_LoadedFaces );
345 _FNT_LoadedFaces = NULL;
346
347 // Shutdown TTF engine
348 TTF_Shutdown( );
349 }
350
351
352
353 /*
354 * Create or access a face.
355 */
FNT_GetFace(const char * face_name)356 FNT_face_t FNT_GetFace( const char * face_name )
357 {
358 qboolean created;
359 FNT_face_t face;
360 const char * bnStart;
361 const char * bnEnd;
362 const char * bnPtr;
363 size_t bnSize;
364 char baseName[ FNT_FACE_NAME_MAX + 1 ];
365
366 // Find the argument's file name
367 bnStart = bnPtr = face_name;
368 while ( *bnPtr ) {
369 if ( *bnPtr == '/' || *bnPtr == '\\' )
370 bnStart = bnPtr + 1;
371 bnPtr ++;
372 }
373 bnEnd = bnPtr;
374 while ( bnPtr > bnStart && *bnPtr != '.' ) {
375 bnPtr --;
376 }
377 if ( *bnPtr == '.' )
378 bnEnd = bnPtr;
379 bnSize = bnEnd - bnStart;
380 if ( bnSize > FNT_FACE_NAME_MAX ) {
381 Com_DPrintf( "FNT: face name from '%s' is too long\n" , face_name );
382 return NULL;
383 } else if ( bnSize == 0 ) {
384 Com_DPrintf( "FNT: face name from '%s' is empty\n" , face_name );
385 return NULL;
386 }
387 Q_strncpyz2( baseName , bnStart , bnSize + 1 );
388
389 // Try looking it up first
390 face = HT_GetItem( _FNT_LoadedFaces , baseName , &created );
391 if ( created ) {
392 // Changed from 0 to 1. An initial value of 0 was causing face to be
393 // used after being freed.
394 face->used = 1;
395 } else {
396 face->used ++;
397 return face;
398 }
399
400 // Try loading it as a TrueType font
401 if ( TTF_InitFace( face ) )
402 return face;
403
404 // Could not load
405 Com_Printf( "FNT: could not load face '%s'\n" , baseName );
406 HT_DeleteItem( _FNT_LoadedFaces , baseName , NULL );
407 return NULL;
408 }
409
410
411 /*
412 * Access an actual font
413 */
FNT_GetFont(FNT_face_t face,unsigned int size)414 FNT_font_t FNT_GetFont( FNT_face_t face , unsigned int size )
415 {
416 char fontId[ FNT_FONT_KEY_MAX + 1 ];
417 FNT_font_t font;
418 qboolean created;
419
420 if ( face == NULL ) {
421 return _FNT_NullFont;
422 }
423
424 // Try looking it up
425 Com_sprintf( fontId , sizeof( fontId ) , "%s|%lu" , face->name , size );
426 font = HT_GetItem( _FNT_LoadedFonts , fontId , &created );
427
428 if ( created ) {
429 // Not found - initialise it.
430 font->used = 1;
431 font->face = face;
432 font->size = size;
433 if ( ! face->GetFont( font ) ) {
434 HT_DeleteItem( _FNT_LoadedFonts , fontId , NULL );
435 font = _FNT_NullFont;
436 }
437 } else {
438 font->used ++;
439 }
440
441 return font;
442 }
443
444
445 /*
446 * Release a font
447 */
FNT_ReleaseFont(FNT_font_t font)448 void FNT_ReleaseFont( FNT_font_t font )
449 {
450 FNT_face_t face;
451
452 if ( font == NULL || font == _FNT_NullFont ) {
453 return;
454 }
455
456 font->used --;
457 if ( font->used ) {
458 return;
459 }
460
461 face = font->face;
462 font->Destroy( font );
463 HT_DeleteItem( _FNT_LoadedFonts , font->lookup , NULL );
464 face->used --;
465 if ( face->used ) {
466 return;
467 }
468
469 face->Destroy( face );
470 HT_DeleteItem( _FNT_LoadedFaces , face->name , NULL );
471 }
472
473
474
475 /**************************************************************************/
476 /* HELPER FUNCTIONS */
477 /**************************************************************************/
478
479 /*
480 * Function that handles color codes in strings.
481 */
_FNT_HandleColor(const char ** pptr,unsigned int cmode,const float * color,qboolean * expectColor,qboolean * colorChanged,const float ** curColor)482 qboolean _FNT_HandleColor(
483 const char ** pptr ,
484 unsigned int cmode ,
485 const float * color ,
486 qboolean * expectColor ,
487 qboolean * colorChanged ,
488 const float ** curColor
489 )
490 {
491 const char * ptr;
492 char current;
493
494 if ( cmode == FNT_CMODE_NONE ) {
495 return true;
496 }
497
498 ptr = *pptr;
499 current = *ptr;
500 if ( cmode == FNT_CMODE_TWO ) {
501 // Two-color mode
502 if ( ( current & 0x80 ) == 0 && *curColor != color ) {
503 *curColor = color;
504 *colorChanged = true;
505 } else if ( ( current & 0x80 ) != 0 && *curColor == color ) {
506 *curColor = &( color[ 4 ] );
507 *colorChanged = true;
508 }
509 }
510
511 // Quake color codes
512 current &= 0x7f;
513 if ( *expectColor ) {
514 if ( current == '\n' ) {
515 (*pptr) --;
516 return false;
517 }
518
519 *expectColor = false;
520 if ( current != '^' ) {
521 if ( cmode != FNT_CMODE_TWO ) {
522 *curColor = FNT_colors[ ( current - '0' ) & 7 ];
523 *colorChanged = true;
524 }
525 (*pptr) ++;
526 return false;
527 }
528 } else if ( *ptr == '^' ) {
529 *expectColor = true;
530 (*pptr) ++;
531 if ( ! **pptr ) {
532 (*pptr) --;
533 }
534 return false;
535 } else if ( cmode == FNT_CMODE_QUAKE_SRS && *ptr == ' ' ) {
536 *curColor = color;
537 *colorChanged = true;
538 }
539
540 return true;
541 }
542
543
544
545 /*
546 * Find the next wrapped unit (i.e. line or rest of the string until EOL).
547 */
_FNT_NextWrappedUnit(const char ** pptr,_FNT_render_info_t renderInfo,unsigned int * unitLength,unsigned int cmode,const float * color)548 qboolean _FNT_NextWrappedUnit(
549 const char ** pptr ,
550 _FNT_render_info_t renderInfo ,
551 unsigned int * unitLength ,
552 unsigned int cmode ,
553 const float * color )
554 {
555 const float * curColor = color;
556 qboolean expectColor = false;
557 qboolean colorChanged;
558 char previous = ' ';
559 int index = 0;
560
561 while ( **pptr && **pptr != '\n' ) {
562 char curChar;
563
564 // Handle color codes
565 if ( ! _FNT_HandleColor( pptr , cmode , color , &expectColor , &colorChanged , &curColor ) ) {
566 continue;
567 }
568
569 // Skip extra spaces
570 curChar = *(*pptr) ++;
571
572 // Add character to info array
573 renderInfo[ index ].toDraw = curChar;
574 renderInfo[ index ].color = curColor;
575 index ++;
576
577 previous = curChar;
578 }
579
580 if ( **pptr ) {
581 (*pptr) ++;
582 }
583 *unitLength = index;
584 return ( **pptr != 0 );
585 }
586