]> icculus.org git repositories - taylor/freespace2.git/blob - src/graphics/font.cpp
plug memory leaks
[taylor/freespace2.git] / src / graphics / font.cpp
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Graphics/Font.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * source file for font stuff
16  *
17  * $Log$
18  * Revision 1.7  2003/06/11 18:30:32  taylor
19  * plug memory leaks
20  *
21  * Revision 1.6  2003/05/25 02:30:42  taylor
22  * Freespace 1 support
23  *
24  * Revision 1.5  2002/07/13 06:46:47  theoddone33
25  * Warning cleanups
26  *
27  * Revision 1.4  2002/06/09 04:41:17  relnev
28  * added copyright header
29  *
30  * Revision 1.3  2002/06/05 08:05:29  relnev
31  * stub/warning removal.
32  *
33  * reworked the sound code.
34  *
35  * Revision 1.2  2002/05/07 03:16:45  theoddone33
36  * The Great Newline Fix
37  *
38  * Revision 1.1.1.1  2002/05/03 03:28:09  root
39  * Initial import.
40  *
41  * 
42  * 9     7/09/99 10:32p Dave
43  * Command brief and red alert screens.
44  * 
45  * 8     12/14/98 9:21a Dan
46  * Put gr8_string() back in
47  * 
48  * 7     12/02/98 5:47p Dave
49  * Put in interface xstr code. Converted barracks screen to new format.
50  * 
51  * 6     11/30/98 1:07p Dave
52  * 16 bit conversion, first run.
53  * 
54  * 5     11/05/98 4:18p Dave
55  * First run nebula support. Beefed up localization a bit. Removed all
56  * conditional compiles for foreign versions. Modified mission file
57  * format.
58  * 
59  * 4     10/13/98 9:28a Dave
60  * Started neatening up freespace.h. Many variables renamed and
61  * reorganized. Added AlphaColors.[h,cpp]
62  * 
63  * 3     10/13/98 9:19a Andsager
64  * Add localization support to cfile.  Optional parameter with cfopen that
65  * looks for localized files.
66  * 
67  * 2     10/07/98 10:52a Dave
68  * Initial checkin.
69  * 
70  * 1     10/07/98 10:49a Dave
71  * 
72  * 49    7/02/98 3:41p Allender
73  * fix nasty assembler bug where pushf wasn't accompanied by corresponding
74  * pop in certain clipping conditions -- manifested only in software and
75  * usually on the PXO chat screen.
76  * 
77  * 48    6/18/98 10:10a Allender
78  * fixed compiler warnings
79  * 
80  * 47    6/13/98 10:48p Lawrance
81  * Changed code to utilize proper fixed-space 1 character.
82  * 
83  * 46    6/13/98 3:18p Hoffoss
84  * NOX()ed out a bunch of strings that shouldn't be translated.
85  * 
86  * 45    5/25/98 10:32a John
87  * Took out redundant code for font bitmap offsets that converted to a
88  * float, then later on converted back to an integer.  Quite unnecessary.
89  * 
90  * 44    5/12/98 11:45a John
91  * Fixed a bug with the length of string being passed to
92  * gr_get_string_size.  The bug occurred if you had two or more'\n'''s in
93  * a row.
94  * 
95  * 43    3/10/98 4:18p John
96  * Cleaned up graphics lib.  Took out most unused gr functions.   Made D3D
97  * & Glide have popups and print screen.  Took out all >8bpp software
98  * support.  Made Fred zbuffer.  Made zbuffer allocate dynamically to
99  * support Fred.  Made zbuffering key off of functions rather than one
100  * global variable.
101  * 
102  * 42    3/09/98 6:06p John
103  * Restructured font stuff to avoid duplicate code in Direct3D and Glide.
104  * Restructured Glide to avoid redundent state setting.
105  * 
106  * 41    3/02/98 10:33a John
107  * Fixed bug where Y clip was using X value.
108  * 
109  * 40    2/19/98 9:04a John
110  * Fixed fonts with glide
111  * 
112  * 39    2/17/98 7:27p John
113  * Got fonts and texturing working in Direct3D
114  * 
115  * 38    2/05/98 9:21p John
116  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
117  * game.
118  * 
119  * 37    1/20/98 2:25p Hoffoss
120  * Fixed bug where timestamp printing incorrectly increments an hour at 6
121  * minutes.
122  * 
123  * 36    1/19/98 6:15p John
124  * Fixed all my Optimized Build compiler warnings
125  * 
126  * 35    11/30/97 12:18p John
127  * added more 24 & 32-bpp primitives
128  * 
129  * 34    11/29/97 2:24p John
130  * fixed warnings
131  * 
132  * 33    11/29/97 2:06p John
133  * added mode 16-bpp support
134  * 
135  * 32    11/10/97 2:35p Hoffoss
136  * Changed timestamp printing code to utilize new fixed-space '1'
137  * character.
138  * 
139  * 31    11/06/97 5:42p Hoffoss
140  * Added support for fixed size timstamp rendering.
141  * 
142  * 30    11/03/97 10:08p Hoffoss
143  * Changed gr_get_string_size to utilize an optional length specifier, if
144  * you want to use non-null terminated strings.
145  * 
146  * 29    11/03/97 10:59a John
147  * added support for more than one font.
148  * 
149  * 28    10/24/97 12:13p Hoffoss
150  * Added gr_force_fit_string().
151  * 
152  * 27    10/19/97 12:55p John
153  * new code to lock / unlock surfaces for smooth directx integration.
154  * 
155  * 26    10/14/97 4:50p John
156  * more 16 bpp stuff.
157  * 
158  * 25    10/14/97 8:08a John
159  * added a bunch more 16 bit support
160  * 
161  * 24    10/09/97 5:23p John
162  * Added support for more 16-bpp functions
163  * 
164  * 23    9/23/97 10:53a Hoffoss
165  * Fixed bug in assumptions made half the time, and not the other half.
166  * 
167  * 22    9/09/97 3:39p Sandeep
168  * warning level 4 bugs
169  * 
170  * 21    8/19/97 5:46p Hoffoss
171  * Changed font used in Fred, and added display to show current eye
172  * position.
173  * 
174  * 20    6/25/97 2:35p John
175  * added some functions to use the windows font for Fred.
176  * 
177  * 19    6/17/97 12:03p John
178  * Moved color/alphacolor functions into their own module.  Made all color
179  * functions be part of the low-level graphics drivers, not just the
180  * grsoft.
181  * 
182  * 18    6/13/97 2:37p John
183  * fixed a string bug that printed weird characters sometimes.
184  * 
185  * 17    6/11/97 6:00p John
186  * sped up alpha matching a bit.
187  * 
188  * 16    6/11/97 5:49p John
189  * Changed palette code to only recalculate alphacolors when needed, not
190  * when palette changes.
191  * 
192  * 15    6/11/97 5:01p John
193  * Fixed mission log text.  Added fast clipped font drawer.  
194  * 
195  * 14    6/11/97 4:11p John
196  * addec function to get font height
197  * 
198  * 13    6/11/97 1:12p John
199  * Started fixing all the text colors in the game.
200  * 
201  * 12    6/09/97 9:24a John
202  * Changed the way fonts are set.
203  * 
204  * 11    6/06/97 4:41p John
205  * Fixed alpha colors to be smoothly integrated into gr_set_color_fast
206  * code.
207  * 
208  * 10    6/06/97 2:40p John
209  * Made all the radar dim in/out
210  * 
211  * 9     6/05/97 4:53p John
212  * First rev of new antialiased font stuff.
213  * 
214  * 8     5/12/97 12:27p John
215  * Restructured Graphics Library to add support for multiple renderers.
216  * 
217  * 7     4/24/97 11:28a Hoffoss
218  * added code to handle gr_String being called before gr_init_font ever
219  * was called
220  * 
221  * 6     4/23/97 5:26p John
222  * First rev of new debug console stuff.
223  * 
224  * 5     4/22/97 12:20p John
225  * fixed more resource leaks
226  * 
227  * 4     4/22/97 10:33a John
228  * fixed the 2d resource leaks that Alan found.
229  * 
230  * 3     4/04/97 11:21a Hoffoss
231  * JOHN: Fixed invalid param that caused a trap in BoundsChecker.    
232  * 
233  * 2     4/01/97 9:26a Allender
234  * added support for descent style fonts although they are not used in the
235  * game yet
236  * 
237  * 1     3/31/97 9:42a Allender
238  *
239  * $NoKeywords: $
240  */
241
242 #ifndef PLAT_UNIX
243 #include <windows.h>
244 #include <windowsx.h>
245 #endif
246 #include <stdio.h>
247 #include <stdarg.h>
248 #include "grinternal.h"
249 #include "2d.h"
250 #include "cfile.h"
251 #include "font.h"
252 #include "palman.h"
253 #include "key.h"
254 #include "bmpman.h"
255 #include "localize.h"
256
257 int Num_fonts = 0;
258 font Fonts[MAX_FONTS];
259 font *Current_font = NULL;
260
261
262 // crops a string if required to force it to not exceed max_width pixels when printed.
263 // Does this by dropping characters at the end of the string and adding '...' to the end.
264 //    str = string to crop.  Modifies this string directly
265 //    max_str = max characters allowed in str
266 //    max_width = number of pixels to limit string to (less than or equal to).
267 //    Returns: returns same pointer passed in for str.
268 char *gr_force_fit_string(char *str, int max_str, int max_width)
269 {
270         int w;
271
272         gr_get_string_size(&w, NULL, str);
273         if (w > max_width) {
274                 if ((int) strlen(str) > max_str - 3) {
275                         Assert(max_str >= 3);
276                         str[max_str - 3] = 0;
277                 }
278
279                 strcpy(str + strlen(str) - 1, "...");
280                 gr_get_string_size(&w, NULL, str);
281                 while (w > max_width) {
282                         Assert(strlen(str) >= 4);  // if this is hit, a bad max_width was passed in and the calling function needs fixing.
283                         strcpy(str + strlen(str) - 4, "...");
284                         gr_get_string_size(&w, NULL, str);
285                 }
286         }
287
288         return str;
289 }
290
291 //takes the character BEFORE being offset into current font
292 // Returns the letter code
293 int get_char_width(ubyte c1,ubyte c2,int *width,int *spacing)
294 {
295         int i, letter;
296
297         Assert ( Current_font != NULL );
298         letter = c1-Current_font->first_ascii;
299
300         if (letter<0 || letter>=Current_font->num_chars) {                              //not in font, draw as space
301                 *width=0;
302                 *spacing = Current_font->w;
303                 return -1;
304         }
305
306         *width = Current_font->char_data[letter].byte_width;
307         *spacing = Current_font->char_data[letter].spacing;
308
309         i = Current_font->char_data[letter].kerning_entry;
310         if ( i > -1)  {
311                 if (!(c2==0 || c2=='\n')) {
312                         int letter2;
313
314                         letter2 = c2-Current_font->first_ascii;
315
316                         if ((letter2>=0) && (letter2<Current_font->num_chars) ) {                               //not in font, draw as space
317                                 font_kernpair   *k = &Current_font->kern_data[i];
318                                 while( (k->c1 == letter) && (k->c2<letter2) && (i<Current_font->num_kern_pairs) )       {
319                                         i++;
320                                         k++;
321                                 }
322                                 if ( k->c2 == letter2 ) {
323                                         *spacing += k->offset;
324                                 }
325                         }
326                 }
327         }
328         return letter;
329 }
330
331 int get_centered_x(char *s)
332 {
333         int w,w2,s2;
334
335         for (w=0;*s!=0 && *s!='\n';s++) {
336                 get_char_width(s[0],s[1],&w2,&s2);
337                 w += s2;
338         }
339
340         return ((gr_screen.clip_width - w) / 2);
341 }
342
343 // draws a character centered on x
344 void gr_char_centered(int x, int y, char chr)
345 {
346         char str[2];
347         int w, sc;
348
349         sc = Lcl_special_chars;
350         if (chr == '1')
351                 chr = (char)(sc + 1);
352
353         str[0] = chr;
354         str[1] = 0;
355         gr_get_string_size(&w, NULL, str);
356         gr_string(x - w / 2, y, str);
357 }
358
359 void gr_print_timestamp(int x, int y, int timestamp)
360 {
361         char h[2], m[3], s[3];
362         int w, c;
363
364         // format the time information into strings
365         sprintf(h, "%.1d", (timestamp / 3600000) % 10);
366         sprintf(m, "%.2d", (timestamp / 60000) % 60);
367         sprintf(s, "%.2d", (timestamp / 1000) % 60);
368
369         gr_get_string_size(&w, NULL, "0");
370         gr_get_string_size(&c, NULL, ":");
371
372         gr_string(x + w, y, ":");
373         gr_string(x + w * 3 + c, y, ":");
374
375         x += w / 2;
376         gr_char_centered(x, y, h[0]);
377         x += w + c;
378         gr_char_centered(x, y, m[0]);
379         x += w;
380         gr_char_centered(x, y, m[1]);
381         x += w + c;
382         gr_char_centered(x, y, s[0]);
383         x += w;
384         gr_char_centered(x, y, s[1]);
385 }
386
387 int gr_get_font_height()
388 {
389         if (Current_font)       {
390                 return Current_font->h;
391         } else {
392                 return 16;
393         }
394 }
395
396 void gr_get_string_size(int *w1, int *h1, char *text, int len)
397 {
398         int longest_width;
399         int width,spacing;
400         int w, h;
401
402         if (!Current_font)      {
403                 if ( w1)
404                         *w1 = 16;
405
406                 if ( h1 )
407                         *h1 = 16;
408
409                 return;
410         }       
411
412         w = 0;
413         h = 0;
414         longest_width = 0;
415
416         if (text != NULL ) {
417                 h += Current_font->h;
418                 while (*text && (len>0) ) {
419
420                         // Process one or more 
421                         while ((*text == '\n') && (len>0) ) {
422                                 text++;
423                                 len--;
424                                 if ( *text )    {
425                                         h += Current_font->h;
426                                 }
427                                 w = 0;
428                         }
429
430                         if (*text == 0) {
431                                 break;
432                         }
433
434                         get_char_width(text[0], text[1], &width, &spacing);
435                         w += spacing;
436                         if (w > longest_width)
437                                 longest_width = w;
438
439                         text++;
440                         len--;
441                 }
442         }
443
444         if ( h1 )
445                 *h1 = h;
446
447         if ( w1 )
448                 *w1 = longest_width;
449 }
450
451
452 MONITOR( FontChars );   
453
454
455 /*
456
457 void gr8_char(int x,int y,int letter)
458 {
459         font_char *ch;
460         
461         ch = &Current_font->char_data[letter];
462
463         gr_aabitmap_ex( x, y, ch->byte_width, Current_font->h, Current_font->u[letter], Current_font->v[letter] );
464
465 //      mprintf(( "String = %s\n", text ));
466 }
467
468
469 void gr8_string( int sx, int sy, char *s )
470 {
471         int width, spacing, letter;
472         int x, y;
473
474         if ( !Current_font ) return;
475         if ( !s ) return;
476         
477         gr_set_bitmap(Current_font->bitmap);
478
479         x = sx;
480         y = sy;
481
482         if (sx==0x8000) {                       //centered
483                 x = get_centered_x(s);
484         } else {
485                 x = sx;
486         }
487
488         while (*s)      {
489                 while (*s== '\n' )      {
490                         s++;
491                         y += Current_font->h;
492                         if (sx==0x8000) {                       //centered
493                                 x = get_centered_x(s);
494                         } else {
495                                 x = sx;
496                         }
497                 }
498                 if (*s == 0 ) break;
499
500                 letter = get_char_width(s[0],s[1],&width,&spacing);
501
502                 if (letter<0) { //not in font, draw as space
503                         x += spacing;
504                         s++;
505                         continue;
506                 }
507                 gr8_char( x, y, letter );
508         
509                 x += spacing;
510                 s++;
511         }
512 }
513 */
514
515 void gr8_string(int sx, int sy, char *s )
516 {
517         int row,width, spacing, letter;
518         int x, y;
519
520         if ( !Current_font ) return;
521         if ( !s ) return;
522
523         x = sx;
524         y = sy;
525
526         if (sx==0x8000) {                       //centered
527                 x = get_centered_x(s);
528         } else {
529                 x = sx;
530         }
531
532         spacing = 0;
533
534         gr_lock();
535
536
537         while (*s)      {
538                 MONITOR_INC( FontChars, 1 );    
539
540                 x += spacing;
541                 while (*s== '\n' )      {
542                         s++;
543                         y += Current_font->h;
544                         if (sx==0x8000) {                       //centered
545                                 x = get_centered_x(s);
546                         } else {
547                                 x = sx;
548                         }
549                 }
550                 if (*s == 0 ) break;
551
552                 letter = get_char_width(s[0],s[1],&width,&spacing);
553                 s++;
554
555                 //If not in font, draw as space
556                 if (letter<0) continue;
557
558                 int xd, yd, xc, yc;
559                 int wc, hc;
560
561                 // Check if this character is totally clipped
562                 if ( x + width < gr_screen.clip_left ) continue;
563                 if ( y + Current_font->h < gr_screen.clip_top ) continue;
564                 if ( x > gr_screen.clip_right ) continue;
565                 if ( y > gr_screen.clip_bottom ) continue;
566
567                 xd = yd = 0;
568                 if ( x < gr_screen.clip_left ) xd = gr_screen.clip_left - x;
569                 if ( y < gr_screen.clip_top ) yd = gr_screen.clip_top - y;
570                 xc = x+xd;
571                 yc = y+yd;
572
573                 wc = width - xd; hc = Current_font->h - yd;
574                 if ( xc + wc > gr_screen.clip_right ) wc = gr_screen.clip_right - xc;
575                 if ( yc + hc > gr_screen.clip_bottom ) hc = gr_screen.clip_bottom - yc;
576
577                 if ( wc < 1 ) continue;
578                 if ( hc < 1 ) continue;
579
580                 ubyte *fp = Current_font->pixel_data + Current_font->char_data[letter].offset + xd + yd*width;
581                 ubyte *dptr = GR_SCREEN_PTR(ubyte, xc, yc);                     
582
583 #ifndef HARDWARE_ONLY
584                 if ( Current_alphacolor )       {
585                         for (row=0; row<hc; row++)      {
586                                 #ifdef USE_INLINE_ASM
587                                         ubyte *lookup = &Current_alphacolor->table.lookup[0][0];
588                                                 _asm mov edx, lookup
589                                                 _asm xor eax, eax
590                                                 _asm mov ecx, wc
591                                                 _asm xor ebx, ebx
592                                                 _asm mov edi, dptr
593                                                 _asm mov esi, fp
594                                                 _asm shr ecx, 1
595                                                 _asm jz  OnlyOne
596                                                 _asm pushf
597                                         InnerFontLoop:
598                                                 _asm mov al, [edi]
599                                                 _asm mov bl, [edi+1]
600                                                 _asm add edi,2
601
602                                                 _asm mov ah, [esi]
603                                                 _asm mov bh, [esi+1]
604                                                 _asm add esi,2
605
606                                                 _asm mov al, [edx+eax]
607                                                 _asm mov ah, [edx+ebx]
608
609                                                 _asm mov [edi-2], ax
610
611                                                 _asm dec ecx
612                                                 _asm jnz InnerFontLoop
613
614                                                 _asm popf
615                                                 _asm jnc NotOdd
616
617                                         OnlyOne:
618                                                 _asm mov al, [edi]
619                                                 _asm mov ah, [esi]
620                                                 _asm mov al, [edx+eax]
621                                                 _asm mov [edi], al
622
623                                         NotOdd:
624                                         dptr += gr_screen.rowsize;
625                                         fp += width;
626                                 #else
627                                         int i;
628                                         for (i=0; i< wc; i++ )  {
629                                                 *dptr++ = Current_alphacolor->table.lookup[*fp++][*dptr];
630                                         }
631                                         fp += width - wc;
632                                         dptr += gr_screen.rowsize - wc;
633                                 #endif
634                         }
635                 } else {                // No alpha color
636 #endif
637                         for (row=0; row<hc; row++)      {
638                                 int i;
639                                 for (i=0; i< wc; i++ )  {
640                                         if (*fp > 5 )
641                                                 *dptr = gr_screen.current_color.raw8;
642                                         dptr++;
643                                         fp++;
644                                 }
645                                 fp += width - wc;
646                                 dptr += gr_screen.rowsize - wc;
647                         }
648                 // }
649         }
650         gr_unlock();
651 }
652
653 #ifndef PLAT_UNIX
654 HFONT MyhFont = NULL;
655 extern HDC hDibDC;
656 #endif
657
658 void gr_string_win(int x, int y, char *s)
659 {
660 #ifdef PLAT_UNIX
661 //      STUB_FUNCTION;
662 #else
663         char *ptr;
664         SIZE size;
665
666         if ( MyhFont==NULL )    {
667                 MyhFont = CreateFont(14, 0, 0, 0,                               // height,width,?,?
668                                 700,
669                                 FALSE,
670                                 FALSE,
671                                 FALSE,                                                                                  // strikeout?
672                                 ANSI_CHARSET,                                                                   // character set
673                                 OUT_DEVICE_PRECIS,
674                                 CLIP_DEFAULT_PRECIS,
675                                 DEFAULT_QUALITY,
676                                 DEFAULT_PITCH | FF_DONTCARE,
677 //                              NULL );
678 //                              "Times New Roman" );
679 //XSTR:OFF
680                                 "Ariel" );
681 //XSTR:ON
682         }
683
684         SelectObject( hDibDC, MyhFont );
685
686         if ( gr_screen.bits_per_pixel==8 )
687                 SetTextColor(hDibDC, PALETTEINDEX(gr_screen.current_color.raw8));
688         else
689                 SetTextColor(hDibDC, RGB(gr_screen.current_color.red,gr_screen.current_color.green,gr_screen.current_color.blue));
690
691         SetBkMode(hDibDC,TRANSPARENT);
692
693
694         HRGN hclip;
695         hclip = CreateRectRgn( gr_screen.offset_x, 
696                                                                   gr_screen.offset_y, 
697                                                                   gr_screen.offset_x+gr_screen.clip_width-1, 
698                                                                   gr_screen.offset_y+gr_screen.clip_height-1 );
699
700         SelectClipRgn(hDibDC, hclip );
701         x += gr_screen.offset_x;
702         y += gr_screen.offset_y;
703         //ptr = strchr(s,'\n);
704         while ((ptr = strchr(s, '\n'))!=NULL) {
705                 TextOut(hDibDC, x, y, s, ptr - s);
706                 GetTextExtentPoint32(hDibDC, s, ptr - s, &size);
707                 y += size.cy;
708                 s = ptr + 1;
709         }
710
711         TextOut(hDibDC, x, y, s, strlen(s));
712         SelectClipRgn(hDibDC, NULL);
713         DeleteObject(hclip);
714 #endif
715 }
716
717 void gr_get_string_size_win(int *w, int *h, char *text)
718 {
719 #ifdef PLAT_UNIX
720 //      STUB_FUNCTION;
721 #else
722         char *ptr;
723         SIZE size;
724
725         ptr = strchr(text, '\n');
726
727         if (MyhFont==NULL)      {
728                 if (w) *w = 0;
729                 if (h) *h = 0;
730                 return;
731         }
732
733         SelectObject( hDibDC, MyhFont );
734
735         if (!ptr)       {
736                 GetTextExtentPoint32( hDibDC, text, strlen(text), &size);
737                 if (w) *w = size.cx;
738                 if (h) *h = size.cy;
739                 return;
740         }
741
742         GetTextExtentPoint32(hDibDC, text, ptr - text, &size);
743         gr_get_string_size_win(w, h, ptr+1);
744         if (w && (size.cx > *w) )
745                 *w = size.cx;
746
747         if (h)
748                 *h += size.cy;
749 #endif
750 }
751
752 char grx_printf_text[2048];     
753
754 void _cdecl gr_printf( int x, int y, char * format, ... )
755 {
756         va_list args;
757
758         if ( !Current_font ) return;
759         
760         va_start(args, format);
761         vsprintf(grx_printf_text,format,args);
762         va_end(args);
763
764         gr_string(x,y,grx_printf_text);
765 }
766
767 void gr_font_close()
768 {
769         font *fnt;
770         int i;
771
772         fnt = Fonts;
773
774         for (i=0; i<Num_fonts; i++) {
775                 if (fnt->kern_data) {
776                         free(fnt->kern_data);
777                 }
778
779                 if (fnt->char_data) {
780                         free(fnt->char_data);
781                 }
782
783                 if (fnt->pixel_data) {
784                         free(fnt->pixel_data);
785                 }
786
787                 if (fnt->bm_data) {
788                         free(fnt->bm_data);
789                 }
790
791
792                 if (fnt->bm_u) {
793                         free(fnt->bm_u);
794                 }
795
796                 if (fnt->bm_v) {
797                         free(fnt->bm_v);
798                 }
799
800                 fnt++;
801         }
802 }
803
804 // Returns -1 if couldn't init font, otherwise returns the
805 // font id number.
806 int gr_create_font(char * typeface)
807 {
808         CFILE *fp;
809         font *fnt;
810         int n, fontnum;
811
812         fnt = Fonts;
813         n = -1;
814         for (fontnum=0; fontnum<Num_fonts; fontnum++ )  {
815                 if (fnt->id != 0 )      {
816                         if ( !_strnicmp( fnt->filename, typeface, MAX_FILENAME_LEN ) )  {
817                                 return fontnum;
818                         }
819                 } else {
820                         if ( n < 0 )    {
821                                 n = fontnum;
822                         }                               
823                 }
824                 fnt++;
825         }
826
827         if ( fontnum==MAX_FONTS )       {
828                 Error( LOCATION, "Too many fonts!\nSee John, or change MAX_FONTS in Graphics\\Font.h\n" );
829         }
830
831         if ( fontnum == Num_fonts )     {
832                 Num_fonts++;
833         }
834         
835         bool localize = true;
836
837         fp = cfopen( typeface, "rb", CFILE_NORMAL, CF_TYPE_ANY, localize );
838         if ( fp == NULL ) return -1;
839
840         strncpy( fnt->filename, typeface, MAX_FILENAME_LEN );
841         cfread( &fnt->id, 4, 1, fp );
842         cfread( &fnt->version, sizeof(int), 1, fp );
843         cfread( &fnt->num_chars, sizeof(int), 1, fp );
844         cfread( &fnt->first_ascii, sizeof(int), 1, fp );
845         cfread( &fnt->w, sizeof(int), 1, fp );
846         cfread( &fnt->h, sizeof(int), 1, fp );
847         cfread( &fnt->num_kern_pairs, sizeof(int), 1, fp );
848         cfread( &fnt->kern_data_size, sizeof(int), 1, fp );
849         cfread( &fnt->char_data_size, sizeof(int), 1, fp );
850         cfread( &fnt->pixel_data_size, sizeof(int), 1, fp );
851
852         if ( fnt->kern_data_size )      {
853                 fnt->kern_data = (font_kernpair *)malloc( fnt->kern_data_size );
854                 Assert(fnt->kern_data!=NULL);
855                 cfread( fnt->kern_data, fnt->kern_data_size, 1, fp );
856         } else {
857                 fnt->kern_data = NULL;
858         }
859         if ( fnt->char_data_size )      {
860                 fnt->char_data = (font_char *)malloc( fnt->char_data_size );
861                 Assert( fnt->char_data != NULL );
862                 cfread( fnt->char_data, fnt->char_data_size, 1, fp );
863         } else {
864                 fnt->char_data = NULL;
865         }
866         if ( fnt->pixel_data_size )     {
867                 fnt->pixel_data = (ubyte *)malloc( fnt->pixel_data_size );
868                 Assert(fnt->pixel_data!=NULL);
869                 cfread( fnt->pixel_data, fnt->pixel_data_size, 1, fp );
870         } else {
871                 fnt->pixel_data = NULL;
872         }
873         cfclose(fp);
874
875         // Create a bitmap for hardware cards.
876         // JAS:  Try to squeeze this into the smallest square power of two texture.
877         // This should probably be done at font generation time, not here.
878         int w, h;
879         if ( fnt->pixel_data_size < 64*64 )     {
880                 w = h = 64;
881         } else if ( fnt->pixel_data_size < 128*128 )    {
882                 w = h = 128;
883         } else {
884                 w = h = 256;
885         }
886
887         fnt->bm_w = w;
888         fnt->bm_h = h;
889         fnt->bm_data = (ubyte *)malloc(fnt->bm_w*fnt->bm_h);
890         fnt->bm_u = (int *)malloc(sizeof(int)*fnt->num_chars);
891         fnt->bm_v = (int *)malloc(sizeof(int)*fnt->num_chars);
892
893         int i,x,y;
894         x = y = 0;
895         for (i=0; i<fnt->num_chars; i++ )       {
896                 ubyte * fp;
897                 int x1, y1;
898                 fp = &fnt->pixel_data[fnt->char_data[i].offset];
899                 if ( x + fnt->char_data[i].byte_width >= fnt->bm_w )    {
900                         x = 0;
901                         y += fnt->h;
902                         if ( y+fnt->h > fnt->bm_h ) {
903                                 Error( LOCATION, "Font too big!\n" );
904                         }
905                 }
906                 fnt->bm_u[i] = x;
907                 fnt->bm_v[i] = y;
908
909                 for( y1=0; y1<fnt->h; y1++ )    {
910                         for (x1=0; x1<fnt->char_data[i].byte_width; x1++ )      {
911                                 uint c = *fp++;
912                                 if ( c > 14 ) c = 14;
913                                 fnt->bm_data[(x+x1)+(y+y1)*fnt->bm_w] = (unsigned char)(c);     
914                         }
915                 }
916                 x += fnt->char_data[i].byte_width;
917         }
918
919         fnt->bitmap_id = bm_create( 8, fnt->bm_w, fnt->bm_h, fnt->bm_data, BMP_AABITMAP );
920
921         return fontnum;
922 }
923
924 void grx_set_font(int fontnum)
925 {
926         if ( fontnum < 0 ) {
927                 Current_font = NULL;
928                 return;
929         }
930
931         if ( fontnum >= 0 ) {
932                 Current_font = &Fonts[fontnum];
933         }
934 }
935
936 void gr_font_init()
937 {
938         gr_init_font( NOX("font01.vf") );
939         gr_init_font( NOX("font02.vf") );
940 #ifndef MAKE_FS1
941         gr_init_font( NOX("font03.vf") );
942 #endif
943 }
944
945 // Returns -1 if couldn't init font, otherwise returns the
946 // font id number.
947 int gr_init_font(char * typeface)
948 {
949         int Loaded_fontnum;
950
951         Loaded_fontnum = gr_create_font(typeface);
952
953         Assert( Loaded_fontnum > -1 );
954
955         gr_set_font( Loaded_fontnum );
956
957         return Loaded_fontnum;
958 }
959