1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9
10
11
12 #ifdef _WIN32
13 #include <windows.h>
14 #include <windowsx.h>
15 #endif
16
17 #include <stdio.h>
18 #include <stdarg.h>
19
20 #include "graphics/2d.h"
21 #include "cfile/cfile.h"
22 #include "graphics/font.h"
23 #include "palman/palman.h"
24 #include "io/key.h"
25 #include "bmpman/bmpman.h"
26 #include "localization/localize.h"
27 #include "parse/parselo.h"
28 #include "globalincs/systemvars.h"
29 #include "globalincs/def_files.h"
30
31
32
33 int Num_fonts = 0;
34 font Fonts[MAX_FONTS];
35 font *Current_font = NULL;
36
37
38 /**
39 * Crops a string if required to force it to not exceed max_width pixels when printed.
40 * Does this by dropping characters at the end of the string and adding '...' to the end.
41 *
42 * @param str string to crop. Modifies this string directly
43 * @param max_str max characters allowed in str
44 * @param max_width number of pixels to limit string to (less than or equal to).
45 * @return Returns same pointer passed in for str.
46 */
gr_force_fit_string(char * str,int max_str,int max_width)47 char *gr_force_fit_string(char *str, int max_str, int max_width)
48 {
49 int w;
50
51 gr_get_string_size(&w, NULL, str);
52 if (w > max_width) {
53 if ((int) strlen(str) > max_str - 3) {
54 Assert(max_str >= 3);
55 str[max_str - 3] = 0;
56 }
57
58 strcpy(str + strlen(str) - 1, "...");
59 gr_get_string_size(&w, NULL, str);
60 while (w > max_width) {
61 Assert(strlen(str) >= 4); // if this is hit, a bad max_width was passed in and the calling function needs fixing.
62 strcpy(str + strlen(str) - 4, "...");
63 gr_get_string_size(&w, NULL, str);
64 }
65 }
66
67 return str;
68 }
69
70 /**
71 * Takes the character BEFORE being offset into current font
72 * @return the letter code
73 */
get_char_width(ubyte c1,ubyte c2,int * width,int * spacing)74 int get_char_width(ubyte c1,ubyte c2,int *width,int *spacing)
75 {
76 int i, letter;
77
78 Assert ( Current_font != NULL );
79 letter = c1-Current_font->first_ascii;
80
81 if (letter<0 || letter>=Current_font->num_chars) { //not in font, draw as space
82 *width=0;
83 *spacing = Current_font->w;
84 return -1;
85 }
86
87 *width = Current_font->char_data[letter].byte_width;
88 *spacing = Current_font->char_data[letter].spacing;
89
90 i = Current_font->char_data[letter].kerning_entry;
91 if ( i > -1) {
92 if (!(c2==0 || c2=='\n')) {
93 int letter2;
94
95 letter2 = c2-Current_font->first_ascii;
96
97 if ((letter2>=0) && (letter2<Current_font->num_chars) ) { //not in font, draw as space
98 font_kernpair *k = &Current_font->kern_data[i];
99 while( (k->c1 == (char)letter) && (k->c2<(char)letter2) && (i<Current_font->num_kern_pairs-1) ) {
100 i++;
101 k++;
102 }
103 if ( k->c2 == (char)letter2 ) {
104 *spacing += k->offset;
105 }
106 }
107 }
108 }
109 return letter;
110 }
111
112 // NOTE: this returns an unscaled size for non-standard resolutions
get_centered_x(const char * s,bool scaled)113 int get_centered_x(const char *s, bool scaled)
114 {
115 int w,w2,s2;
116
117 for (w=0;*s!=0 && *s!='\n';s++) {
118 get_char_width(s[0],s[1],&w2,&s2);
119 w += s2;
120 }
121
122 return (((scaled ? gr_screen.clip_width : gr_screen.clip_width_unscaled) - w) / 2);
123 }
124
125 /**
126 * Draws a character centered on x
127 */
gr_char_centered(int x,int y,char chr,int resize_mode)128 void gr_char_centered(int x, int y, char chr, int resize_mode)
129 {
130 char str[2];
131 int w, sc;
132
133 sc = Lcl_special_chars;
134 if (chr == '1')
135 chr = (char)(sc + 1);
136
137 str[0] = chr;
138 str[1] = 0;
139 gr_get_string_size(&w, NULL, str);
140 gr_string(x - w / 2, y, str, resize_mode);
141 }
142
gr_print_timestamp(int x,int y,fix timestamp,int resize_mode)143 void gr_print_timestamp(int x, int y, fix timestamp, int resize_mode)
144 {
145 char h[2], m[3], s[3];
146 int w, c;
147
148 int time = (int)f2fl(timestamp); // convert to seconds
149
150 // format the time information into strings
151 sprintf(h, "%.1d", (time / 3600));
152 sprintf(m, "%.2d", (time / 60) % 60);
153 sprintf(s, "%.2d", time % 60);
154
155 gr_get_string_size(&w, NULL, "0");
156 gr_get_string_size(&c, NULL, ":");
157
158 gr_string(x + w, y, ":", resize_mode);
159 gr_string(x + w * 3 + c, y, ":", resize_mode);
160
161 x += w / 2;
162 gr_char_centered(x, y, h[0], resize_mode);
163 x += w + c;
164 gr_char_centered(x, y, m[0], resize_mode);
165 x += w;
166 gr_char_centered(x, y, m[1], resize_mode);
167 x += w + c;
168 gr_char_centered(x, y, s[0], resize_mode);
169 x += w;
170 gr_char_centered(x, y, s[1], resize_mode);
171 }
172
gr_get_font_height()173 int gr_get_font_height()
174 {
175 if (Current_font) {
176 return Current_font->h;
177 } else {
178 return 16;
179 }
180 }
181
gr_get_string_size(int * w1,int * h1,const char * text,int len)182 void gr_get_string_size(int *w1, int *h1, const char *text, int len)
183 {
184 int longest_width;
185 int width,spacing;
186 int w, h;
187
188 if (!Current_font) {
189 if ( w1)
190 *w1 = 16;
191
192 if ( h1 )
193 *h1 = 16;
194
195 return;
196 }
197
198 w = 0;
199 h = 0;
200 longest_width = 0;
201
202 if (text != NULL ) {
203 h += Current_font->h;
204 while (*text && (len>0) ) {
205
206 // Process one or more
207 while ((*text == '\n') && (len>0) ) {
208 text++;
209 len--;
210 if ( *text ) {
211 h += Current_font->h;
212 }
213 w = 0;
214 }
215
216 if (*text == 0) {
217 break;
218 }
219
220 get_char_width(text[0], text[1], &width, &spacing);
221 w += spacing;
222 if (w > longest_width)
223 longest_width = w;
224
225 text++;
226 len--;
227 }
228 }
229
230 if ( h1 )
231 *h1 = h;
232
233 if ( w1 )
234 *w1 = longest_width;
235 }
236
237
238 MONITOR( FontChars )
239
240 #ifdef _WIN32
241 HFONT MyhFont = NULL;
242 HDC hDibDC = NULL;
243
gr_string_win(int x,int y,const char * s)244 void gr_string_win(int x, int y, const char *s)
245 {
246 int old_bitmap = gr_screen.current_bitmap;
247 gr_set_font(FONT1);
248 gr_string(x,y,s);
249 gr_screen.current_bitmap = old_bitmap;
250 }
251
gr_get_string_size_win(int * w,int * h,const char * text)252 void gr_get_string_size_win(int *w, int *h, const char *text)
253 {
254 const char *ptr;
255 SIZE size;
256
257 ptr = strchr(text, '\n');
258
259 if (MyhFont==NULL) {
260 if (w) *w = 0;
261 if (h) *h = 0;
262 return;
263 }
264
265 SelectObject( hDibDC, MyhFont );
266
267 if (!ptr) {
268 GetTextExtentPoint32( hDibDC, text, strlen(text), &size);
269 if (w) *w = size.cx;
270 if (h) *h = size.cy;
271 return;
272 }
273
274 GetTextExtentPoint32(hDibDC, text, ptr - text, &size);
275 gr_get_string_size_win(w, h, ptr+1);
276 if (w && (size.cx > *w) )
277 *w = size.cx;
278
279 if (h)
280 *h += size.cy;
281 }
282 #endif // ifdef _WIN32
283
284 char grx_printf_text[2048];
285
gr_printf(int x,int y,const char * format,...)286 void _cdecl gr_printf( int x, int y, const char * format, ... )
287 {
288 va_list args;
289
290 if ( !Current_font ) return;
291
292 va_start(args, format);
293 vsprintf(grx_printf_text,format,args);
294 va_end(args);
295
296 gr_string(x,y,grx_printf_text);
297 }
298
gr_printf_menu(int x,int y,const char * format,...)299 void _cdecl gr_printf_menu( int x, int y, const char * format, ... )
300 {
301 va_list args;
302
303 if ( !Current_font ) return;
304
305 va_start(args, format);
306 vsprintf(grx_printf_text,format,args);
307 va_end(args);
308
309 gr_string(x,y,grx_printf_text,GR_RESIZE_MENU);
310 }
311
gr_printf_no_resize(int x,int y,const char * format,...)312 void _cdecl gr_printf_no_resize( int x, int y, const char * format, ... )
313 {
314 va_list args;
315
316 if ( !Current_font ) return;
317
318 va_start(args, format);
319 vsprintf(grx_printf_text,format,args);
320 va_end(args);
321
322 gr_string(x,y,grx_printf_text,GR_RESIZE_NONE);
323 }
324
gr_font_close()325 void gr_font_close()
326 {
327 font *fnt;
328 int i;
329
330 fnt = Fonts;
331
332 for (i=0; i<Num_fonts; i++) {
333 if (fnt->kern_data) {
334 vm_free(fnt->kern_data);
335 fnt->kern_data = NULL;
336 }
337
338 if (fnt->char_data) {
339 vm_free(fnt->char_data);
340 fnt->char_data = NULL;
341 }
342
343 if (fnt->pixel_data) {
344 vm_free(fnt->pixel_data);
345 fnt->pixel_data = NULL;
346 }
347
348 if (fnt->bm_data) {
349 vm_free(fnt->bm_data);
350 fnt->bm_data = NULL;
351 }
352
353 if (fnt->bm_u) {
354 vm_free(fnt->bm_u);
355 fnt->bm_u = NULL;
356 }
357
358 if (fnt->bm_v) {
359 vm_free(fnt->bm_v);
360 fnt->bm_v = NULL;
361 }
362
363 fnt++;
364 }
365 }
366
367 /**
368 * @return -1 if couldn't init font, otherwise returns the font id number.
369 */
gr_create_font(char * typeface)370 int gr_create_font(char * typeface)
371 {
372 CFILE *fp;
373 font *fnt;
374 int n, fontnum;
375
376 fnt = Fonts;
377 n = -1;
378 for (fontnum=0; fontnum<Num_fonts; fontnum++ ) {
379 if (fnt->id != 0 ) {
380 if ( !_strnicmp( fnt->filename, typeface, MAX_FILENAME_LEN ) ) {
381 return fontnum;
382 }
383 } else {
384 if ( n < 0 ) {
385 n = fontnum;
386 }
387 }
388 fnt++;
389 }
390
391 if ( fontnum == MAX_FONTS ) {
392 Warning( LOCATION, "Too many fonts!\nSee John, or change MAX_FONTS in Graphics\\Font.h\n" );
393 return -1;
394 }
395
396 if ( fontnum == Num_fonts ) {
397 Num_fonts++;
398 }
399
400 bool localize = true;
401
402 fp = cfopen( typeface, "rb", CFILE_NORMAL, CF_TYPE_ANY, localize );
403 if ( fp == NULL ) return -1;
404
405 strcpy_s( fnt->filename, typeface );
406 cfread( &fnt->id, 4, 1, fp );
407 cfread( &fnt->version, sizeof(int), 1, fp );
408 cfread( &fnt->num_chars, sizeof(int), 1, fp );
409 cfread( &fnt->first_ascii, sizeof(int), 1, fp );
410 cfread( &fnt->w, sizeof(int), 1, fp );
411 cfread( &fnt->h, sizeof(int), 1, fp );
412 cfread( &fnt->num_kern_pairs, sizeof(int), 1, fp );
413 cfread( &fnt->kern_data_size, sizeof(int), 1, fp );
414 cfread( &fnt->char_data_size, sizeof(int), 1, fp );
415 cfread( &fnt->pixel_data_size, sizeof(int), 1, fp );
416
417 fnt->id = INTEL_SHORT( fnt->id ); //-V570
418 fnt->version = INTEL_INT( fnt->version ); //-V570
419 fnt->num_chars = INTEL_INT( fnt->num_chars ); //-V570
420 fnt->first_ascii = INTEL_INT( fnt->first_ascii ); //-V570
421 fnt->w = INTEL_INT( fnt->w ); //-V570
422 fnt->h = INTEL_INT( fnt->h ); //-V570
423 fnt->num_kern_pairs = INTEL_INT( fnt->num_kern_pairs ); //-V570
424 fnt->kern_data_size = INTEL_INT( fnt->kern_data_size ); //-V570
425 fnt->char_data_size = INTEL_INT( fnt->char_data_size ); //-V570
426 fnt->pixel_data_size = INTEL_INT( fnt->pixel_data_size ); //-V570
427
428 if ( fnt->kern_data_size ) {
429 fnt->kern_data = (font_kernpair *)vm_malloc( fnt->kern_data_size );
430 Assert(fnt->kern_data!=NULL);
431 cfread( fnt->kern_data, fnt->kern_data_size, 1, fp );
432 } else {
433 fnt->kern_data = NULL;
434 }
435 if ( fnt->char_data_size ) {
436 fnt->char_data = (font_char *)vm_malloc( fnt->char_data_size );
437 Assert( fnt->char_data != NULL );
438 cfread( fnt->char_data, fnt->char_data_size, 1, fp );
439
440 for (int i=0; i<fnt->num_chars; i++) {
441 fnt->char_data[i].spacing = INTEL_INT( fnt->char_data[i].spacing ); //-V570
442 fnt->char_data[i].byte_width = INTEL_INT( fnt->char_data[i].byte_width ); //-V570
443 fnt->char_data[i].offset = INTEL_INT( fnt->char_data[i].offset ); //-V570
444 fnt->char_data[i].kerning_entry = INTEL_INT( fnt->char_data[i].kerning_entry ); //-V570
445 fnt->char_data[i].user_data = INTEL_SHORT( fnt->char_data[i].user_data ); //-V570
446 }
447 } else {
448 fnt->char_data = NULL;
449 }
450 if ( fnt->pixel_data_size ) {
451 fnt->pixel_data = (ubyte *)vm_malloc( fnt->pixel_data_size );
452 Assert(fnt->pixel_data!=NULL);
453 cfread( fnt->pixel_data, fnt->pixel_data_size, 1, fp );
454 } else {
455 fnt->pixel_data = NULL;
456 }
457 cfclose(fp);
458
459 // Create a bitmap for hardware cards.
460 // JAS: Try to squeeze this into the smallest square power of two texture.
461 // This should probably be done at font generation time, not here.
462 int w, h;
463 if ( fnt->pixel_data_size*4 < 64*64 ) {
464 w = h = 64;
465 } else if ( fnt->pixel_data_size*4 < 128*128 ) {
466 w = h = 128;
467 } else if ( fnt->pixel_data_size*4 < 256*256 ) {
468 w = h = 256;
469 } else if ( fnt->pixel_data_size*4 < 512*512 ) {
470 w = h = 512;
471 } else {
472 w = h = 1024;
473 }
474
475 fnt->bm_w = w;
476 fnt->bm_h = h;
477 fnt->bm_data = (ubyte *)vm_malloc(fnt->bm_w*fnt->bm_h);
478 fnt->bm_u = (int *)vm_malloc(sizeof(int)*fnt->num_chars);
479 fnt->bm_v = (int *)vm_malloc(sizeof(int)*fnt->num_chars);
480
481 memset( fnt->bm_data, 0, fnt->bm_w * fnt->bm_h );
482
483 int i,x,y;
484 x = y = 0;
485 for (i=0; i<fnt->num_chars; i++ ) {
486 ubyte * ubp;
487 int x1, y1;
488 ubp = &fnt->pixel_data[fnt->char_data[i].offset];
489 if ( x + fnt->char_data[i].byte_width >= fnt->bm_w ) {
490 x = 0;
491 y += fnt->h + 2;
492 if ( y+fnt->h > fnt->bm_h ) {
493 Error( LOCATION, "Font too big!\n" );
494 }
495 }
496 fnt->bm_u[i] = x;
497 fnt->bm_v[i] = y;
498
499 for( y1=0; y1<fnt->h; y1++ ) {
500 for (x1=0; x1<fnt->char_data[i].byte_width; x1++ ) {
501 uint c = *ubp++;
502 if ( c > 14 ) c = 14;
503 fnt->bm_data[(x+x1)+(y+y1)*fnt->bm_w] = (unsigned char)(c);
504 }
505 }
506 x += fnt->char_data[i].byte_width + 2;
507 }
508
509 fnt->bitmap_id = bm_create( 8, fnt->bm_w, fnt->bm_h, fnt->bm_data, BMP_AABITMAP );
510
511 return fontnum;
512 }
513
gr_get_current_fontnum()514 int gr_get_current_fontnum()
515 {
516 if (Current_font == NULL) {
517 return -1;
518 } else {
519 return (Current_font - &Fonts[0]);
520 }
521 }
522
gr_get_fontnum(char * filename)523 int gr_get_fontnum(char *filename)
524 {
525 int i;
526 for(i = 0; i < Num_fonts; i++)
527 {
528 if(!strextcmp(Fonts[i].filename, filename))
529 return i;
530 }
531
532 return -1;
533 }
534
gr_set_font(int fontnum)535 void gr_set_font(int fontnum)
536 {
537 if ( fontnum < 0 ) {
538 Current_font = NULL;
539 return;
540 }
541
542 if ( fontnum >= 0 && fontnum < Num_fonts) {
543 Current_font = &Fonts[fontnum];
544 }
545 }
546
parse_fonts_tbl(char * only_parse_first_font,size_t only_parse_first_font_size)547 void parse_fonts_tbl(char *only_parse_first_font, size_t only_parse_first_font_size)
548 {
549 int rval;
550 char *filename;
551
552 // choose file name
553 // (this can be done within the function, as opposed to being passed as a parameter,
554 // because fonts.tbl doesn't have a modular counterpart)
555 if ( cf_exists_full("fonts.tbl", CF_TYPE_TABLES) ) {
556 filename = "fonts.tbl";
557 } else {
558 filename = NULL;
559 }
560
561 if ((rval = setjmp(parse_abort)) != 0) {
562 mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", (filename) ? filename : NOX("<default fonts.tbl>"), rval));
563 return;
564 }
565
566 if (filename != NULL) {
567 read_file_text(filename, CF_TYPE_TABLES);
568 } else {
569 read_file_text_from_array(defaults_get_file("fonts.tbl"));
570 }
571
572 reset_parse();
573
574 // start parsing
575 required_string("#Fonts");
576
577 // read fonts
578 while (required_string_either("#End","$Font:")) {
579 char font_filename[MAX_FILENAME_LEN];
580
581 // grab font
582 required_string("$Font:");
583 stuff_string(font_filename, F_NAME, MAX_FILENAME_LEN);
584
585 // if we only need the first font, copy it and bail
586 if (only_parse_first_font != NULL) {
587 strcpy_s(only_parse_first_font, only_parse_first_font_size, font_filename);
588 return;
589 }
590
591 // create font
592 int font_id = gr_create_font(font_filename);
593 if (font_id < 0) {
594 Warning(LOCATION, "Could not create font from typeface '%s'!", font_filename);
595 }
596 }
597
598 // done parsing
599 required_string("#End");
600
601 // double check
602 if (Num_fonts < 3) {
603 Error(LOCATION, "There must be at least three fonts in %s!", (filename) ? filename : NOX("<default fonts.tbl>"));
604 }
605 }
606
gr_stuff_first_font(char * first_font,size_t first_font_size)607 void gr_stuff_first_font(char *first_font, size_t first_font_size )
608 {
609 parse_fonts_tbl( first_font, first_font_size );
610 }
611
gr_font_init()612 void gr_font_init()
613 {
614 parse_fonts_tbl( NULL, 0 );
615 gr_set_font(0);
616 }
617