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