1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: ui_font.cpp 4297 2010-06-03 22:49:00Z firebrand_kh $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include "gamedefs.h"
29 #include "r_tex.h"
30 #include "ui.h"
31 
32 // MACROS ------------------------------------------------------------------
33 
34 // TYPES -------------------------------------------------------------------
35 
36 struct VColTranslationDef
37 {
38 	rgba_t			From;
39 	rgba_t			To;
40 	int				LumFrom;
41 	int				LumTo;
42 };
43 
44 struct VTextColourDef
45 {
46 	TArray<VColTranslationDef>	Translations;
47 	TArray<VColTranslationDef>	ConsoleTranslations;
48 	rgba_t						FlatColour;
49 	int							Index;
50 };
51 
52 struct VColTransMap
53 {
54 	VName		Name;
55 	int			Index;
56 };
57 
58 //
59 //	VSpecialFont
60 //
61 //	Like regular font, but initialised using explicit list of patches.
62 //
63 class VSpecialFont : public VFont
64 {
65 public:
66 	VSpecialFont(VName, const TArray<int>&, const TArray<VName>&,
67 		const bool*);
68 };
69 
70 //
71 //	VFon1Font
72 //
73 //	Font in FON1 format.
74 //
75 class VFon1Font : public VFont
76 {
77 public:
78 	VFon1Font(VName, int);
79 };
80 
81 //
82 //	VFon2Font
83 //
84 //	Font in FON2 format.
85 //
86 class VFon2Font : public VFont
87 {
88 public:
89 	VFon2Font(VName, int);
90 };
91 
92 //
93 //	VSingleTextureFont
94 //
95 //	Font consisting of a single texture for character 'A'.
96 //
97 class VSingleTextureFont : public VFont
98 {
99 public:
100 	VSingleTextureFont(VName, int);
101 };
102 
103 //
104 //	VFontChar
105 //
106 //	Texture class for regular font characters.
107 //
108 class VFontChar : public VTexture
109 {
110 private:
111 	VTexture*		BaseTex;
112 	rgba_t*			Palette;
113 
114 public:
115 	VFontChar(VTexture*, rgba_t*);
116 	~VFontChar();
117 	vuint8* GetPixels();
118 	rgba_t* GetPalette();
119 	void Unload();
120 	VTexture* GetHighResolutionTexture();
121 };
122 
123 //
124 //	VFontChar2
125 //
126 //	Texture class for FON1 font characters.
127 //
128 class VFontChar2 : public VTexture
129 {
130 private:
131 	int				LumpNum;
132 	int				FilePos;
133 	vuint8*			Pixels;
134 	rgba_t*			Palette;
135 	int				MaxCol;
136 
137 public:
138 	VFontChar2(int, int, int, int, rgba_t*, int);
139 	~VFontChar2();
140 	vuint8* GetPixels();
141 	rgba_t* GetPalette();
142 	void Unload();
143 };
144 
145 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
146 
147 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
148 
149 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
150 
151 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
152 
153 // PUBLIC DATA DEFINITIONS -------------------------------------------------
154 
155 VFont*								SmallFont;
156 VFont*								ConFont;
157 
158 // PRIVATE DATA DEFINITIONS ------------------------------------------------
159 
160 VFont*								VFont::Fonts;
161 
162 static TArray<VTextColourDef>		TextColours;
163 static TArray<VColTransMap>			TextColourLookup;
164 
165 // CODE --------------------------------------------------------------------
166 
167 //==========================================================================
168 //
169 //	VFont::StaticInit
170 //
171 //==========================================================================
172 
StaticInit()173 void VFont::StaticInit()
174 {
175 	guard(VFont::StaticInit);
176 	ParseTextColours();
177 
178 	//
179 	//	Initialise standard fonts.
180 	//
181 
182 	//	Small font.
183 	if (W_CheckNumForName(NAME_fonta_s) >= 0)
184 	{
185 		SmallFont = new VFont(NAME_smallfont, "fonta%02d", 33, 95, 1);
186 	}
187 	else
188 	{
189 		SmallFont = new VFont(NAME_smallfont, "stcfn%03d", 33, 95, 33);
190 	}
191 	//	Strife's second small font.
192 	if (W_CheckNumForName(NAME_stbfn033) >= 0)
193 	{
194 		new VFont(NAME_smallfont2, "stbfn%03d", 33, 95, 33);
195 	}
196 	//	Big font.
197 	if (W_CheckNumForName(NAME_bigfont) >= 0)
198 	{
199 		GetFont(NAME_bigfont, NAME_bigfont);
200 	}
201 	else
202 	{
203 		new VFont(NAME_bigfont, "fontb%02d", 33, 95, 1);
204 	}
205 	//	Console font
206 	ConFont = GetFont(NAME_consolefont, NAME_confont);
207 
208 	//	Load custom fonts.
209 	ParseFontDefs();
210 	unguard;
211 }
212 
213 //==========================================================================
214 //
215 //	VFont::StaticShutdown
216 //
217 //==========================================================================
218 
StaticShutdown()219 void VFont::StaticShutdown()
220 {
221 	guard(VFont::StaticShutdown);
222 	VFont* F = Fonts;
223 	while (F)
224 	{
225 		VFont* Next = F->Next;
226 		delete F;
227 		F = NULL;
228 		F = Next;
229 	}
230 	Fonts = NULL;
231 	TextColours.Clear();
232 	TextColourLookup.Clear();
233 	unguard;
234 }
235 
236 //==========================================================================
237 //
238 //	VFont::ParseTextColours
239 //
240 //==========================================================================
241 
ParseTextColours()242 void VFont::ParseTextColours()
243 {
244 	guard(VFont::ParseTextColours);
245 	TArray<VTextColourDef>		TempDefs;
246 	TArray<VColTransMap>		TempColours;
247 	for (int Lump = W_IterateNS(-1, WADNS_Global); Lump >= 0;
248 		Lump = W_IterateNS(Lump, WADNS_Global))
249 	{
250 		if (W_LumpName(Lump) != NAME_textcolo)
251 		{
252 			continue;
253 		}
254 		VScriptParser sc(*W_LumpName(Lump), W_CreateLumpReaderNum(Lump));
255 		while (!sc.AtEnd())
256 		{
257 			VTextColourDef& Col = TempDefs.Alloc();
258 			Col.FlatColour.r = 0;
259 			Col.FlatColour.g = 0;
260 			Col.FlatColour.b = 0;
261 			Col.FlatColour.a = 255;
262 			Col.Index = -1;
263 
264 			TArray<VName> Names;
265 			VColTranslationDef TDef;
266 			TDef.LumFrom = -1;
267 			TDef.LumTo = -1;
268 
269 			//	Name for this colour.
270 			sc.ExpectString();
271 			Names.Append(*sc.String.ToLower());
272 			//	Additional names.
273 			while (!sc.Check("{"))
274 			{
275 				if (Names[0] == "untranslated")
276 				{
277 					sc.Error("Colour \"untranslated\" cannot have any other names");
278 				}
279 				sc.ExpectString();
280 				Names.Append(*sc.String.ToLower());
281 			}
282 
283 			int TranslationMode = 0;
284 			while (!sc.Check("}"))
285 			{
286 				if (sc.Check("Console:"))
287 				{
288 					if (TranslationMode == 1)
289 					{
290 						sc.Error("Only one console text colour definition allowed");
291 					}
292 					TranslationMode = 1;
293 					TDef.LumFrom = -1;
294 					TDef.LumTo = -1;
295 				}
296 				else if (sc.Check("Flat:"))
297 				{
298 					sc.ExpectString();
299 					vuint32 C = M_ParseColour(sc.String);
300 					Col.FlatColour.r = (C >> 16) & 0xff;
301 					Col.FlatColour.g = (C >> 8) & 0xff;
302 					Col.FlatColour.b = C & 0xff;
303 					Col.FlatColour.a = 255;
304 				}
305 				else
306 				{
307 					//	From colour.
308 					sc.ExpectString();
309 					vuint32 C = M_ParseColour(sc.String);
310 					TDef.From.r = (C >> 16) & 0xff;
311 					TDef.From.g = (C >> 8) & 0xff;
312 					TDef.From.b = C & 0xff;
313 					TDef.From.a = 255;
314 
315 					//	To colour.
316 					sc.ExpectString();
317 					C = M_ParseColour(sc.String);
318 					TDef.To.r = (C >> 16) & 0xff;
319 					TDef.To.g = (C >> 8) & 0xff;
320 					TDef.To.b = C & 0xff;
321 					TDef.To.a = 255;
322 
323 					if (sc.CheckNumber())
324 					{
325 						//	Optional luminosity ranges.
326 						if (TDef.LumFrom == -1 && sc.Number != 0)
327 						{
328 							sc.Error("First colour range must start at position 0");
329 						}
330 						if (sc.Number < 0 || sc.Number > 256)
331 						{
332 							sc.Error("Colour index must be in range from 0 to 256");
333 						}
334 						if (sc.Number <= TDef.LumTo)
335 						{
336 							sc.Error("Colour range must start after end of previous range");
337 						}
338 						TDef.LumFrom = sc.Number;
339 
340 						sc.ExpectNumber();
341 						if (sc.Number < 0 || sc.Number > 256)
342 						{
343 							sc.Error("Colour index must be in range from 0 to 256");
344 						}
345 						if (sc.Number <= TDef.LumFrom)
346 						{
347 							sc.Error("Ending colour index must be greater than start index");
348 						}
349 						TDef.LumTo = sc.Number;
350 					}
351 					else
352 					{
353 						//	Set default luminosity range.
354 						TDef.LumFrom = 0;
355 						TDef.LumTo = 256;
356 					}
357 
358 					if (TranslationMode == 0)
359 					{
360 						Col.Translations.Append(TDef);
361 					}
362 					else if (TranslationMode == 1)
363 					{
364 						Col.ConsoleTranslations.Append(TDef);
365 					}
366 				}
367 			}
368 
369 			if (Names[0] == "untranslated")
370 			{
371 				if (Col.Translations.Num() != 0 ||
372 					Col.ConsoleTranslations.Num() != 0)
373 				{
374 					sc.Error("The \"untranslated\" colour must be left undefined");
375 				}
376 			}
377 			else
378 			{
379 				if (Col.Translations.Num() == 0)
380 				{
381 					sc.Error("There must be at least one normal range for a colour");
382 				}
383 				if (Col.ConsoleTranslations.Num() == 0)
384 				{
385 					//	If console colour translation is not defined, make
386 					// it white.
387 					TDef.From.r = 0;
388 					TDef.From.g = 0;
389 					TDef.From.b = 0;
390 					TDef.From.a = 255;
391 					TDef.To.r = 255;
392 					TDef.To.g = 255;
393 					TDef.To.b = 255;
394 					TDef.To.a = 255;
395 					TDef.LumFrom = 0;
396 					TDef.LumTo = 256;
397 					Col.ConsoleTranslations.Append(TDef);
398 				}
399 			}
400 
401 			//	Add all names to the list of colours.
402 			for (int i = 0; i < Names.Num(); i++)
403 			{
404 				//	Check for redefined colours.
405 				int CIdx;
406 				for (CIdx = 0; CIdx < TempColours.Num(); CIdx++)
407 				{
408 					if (TempColours[CIdx].Name == Names[i])
409 					{
410 						TempColours[CIdx].Index = TempDefs.Num() - 1;
411 						break;
412 					}
413 				}
414 				if (CIdx == TempColours.Num())
415 				{
416 					VColTransMap& CMap = TempColours.Alloc();
417 					CMap.Name = Names[i];
418 					CMap.Index = TempDefs.Num() - 1;
419 				}
420 			}
421 		}
422 	}
423 
424 	//	Put colour definitions in it's final location.
425 	for (int i = 0; i < TempColours.Num(); i++)
426 	{
427 		VColTransMap& TmpCol = TempColours[i];
428 		VTextColourDef& TmpDef = TempDefs[TmpCol.Index];
429 		if (TmpDef.Index == -1)
430 		{
431 			TmpDef.Index = TextColours.Num();
432 			TextColours.Append(TmpDef);
433 		}
434 		VColTransMap& Col = TextColourLookup.Alloc();
435 		Col.Name = TmpCol.Name;
436 		Col.Index = TmpDef.Index;
437 	}
438 
439 	//	Make sure all biilt-in colours are defined.
440 	check(TextColours.Num() >= NUM_TEXT_COLOURS);
441 	unguard;
442 }
443 
444 //==========================================================================
445 //
446 //	VFont::ParseFontDefs
447 //
448 //==========================================================================
449 
ParseFontDefs()450 void VFont::ParseFontDefs()
451 {
452 	guard(VFont::ParseFontDefs);
453 	for (int Lump = W_IterateNS(-1, WADNS_Global); Lump >= 0;
454 		Lump = W_IterateNS(Lump, WADNS_Global))
455 	{
456 		if (W_LumpName(Lump) != NAME_fontdefs)
457 		{
458 			continue;
459 		}
460 		VScriptParser sc(*W_LumpName(Lump), W_CreateLumpReaderNum(Lump));
461 		while (!sc.AtEnd())
462 		{
463 			//	Name of the font.
464 			sc.ExpectString();
465 			VName FontName = *sc.String.ToLower();
466 			sc.Expect("{");
467 
468 #define CHECK_TYPE(Id) if (FontType == Id) \
469 	sc.Error(va("Invalid combination of properties in font '%s'", *FontName))
470 			int FontType = 0;
471 			VStr Template;
472 			int First = 33;
473 			int Count = 223;
474 			int Start = 33;
475 			TArray<int> CharIndexes;
476 			TArray<VName> CharLumps;
477 			bool NoTranslate[256];
478 			memset(NoTranslate, 0, sizeof(NoTranslate));
479 
480 			while (!sc.Check("}"))
481 			{
482 				if (sc.Check("template"))
483 				{
484 					CHECK_TYPE(2);
485 					sc.ExpectString();
486 					Template = sc.String;
487 					FontType = 1;
488 				}
489 				else if (sc.Check("first"))
490 				{
491 					CHECK_TYPE(2);
492 					sc.ExpectNumber();
493 					First = sc.Number;
494 					FontType = 1;
495 				}
496 				else if (sc.Check("count"))
497 				{
498 					CHECK_TYPE(2);
499 					sc.ExpectNumber();
500 					Count = sc.Number;
501 					FontType = 1;
502 				}
503 				else if (sc.Check("base"))
504 				{
505 					CHECK_TYPE(2);
506 					sc.ExpectNumber();
507 					Start = sc.Number;
508 					FontType = 1;
509 				}
510 				else if (sc.Check("notranslate"))
511 				{
512 					CHECK_TYPE(1);
513 					while (sc.CheckNumber() && !sc.Crossed)
514 					{
515 						if (sc.Number >= 0 && sc.Number < 256)
516 						{
517 							NoTranslate[sc.Number] = true;
518 						}
519 					}
520 					FontType = 2;
521 				}
522 				else
523 				{
524 					CHECK_TYPE(1);
525 					sc.ExpectString();
526 					const char* CPtr = *sc.String;
527 					int CharIdx = VStr::GetChar(CPtr);
528 					sc.ExpectString();
529 					VName LumpName(*sc.String, VName::AddLower8);
530 					if (W_CheckNumForName(LumpName, WADNS_Graphics) >= 0)
531 					{
532 						CharIndexes.Append(CharIdx);
533 						CharLumps.Append(LumpName);
534 					}
535 					FontType = 2;
536 				}
537 			}
538 			if (FontType == 1)
539 			{
540 				new VFont(FontName, Template, First, Count, Start);
541 			}
542 			else if (FontType == 2)
543 			{
544 				if (CharIndexes.Num())
545 				{
546 					new VSpecialFont(FontName, CharIndexes, CharLumps,
547 						NoTranslate);
548 				}
549 			}
550 			else
551 			{
552 				sc.Error("Font has no attributes");
553 			}
554 		}
555 	}
556 	unguard;
557 }
558 
559 //==========================================================================
560 //
561 //	VFont::FindFont
562 //
563 //==========================================================================
564 
FindFont(VName AName)565 VFont* VFont::FindFont(VName AName)
566 {
567 	guard(VFont::FindFont);
568 	for (VFont* F = Fonts; F; F = F->Next)
569 	{
570 		if (F->Name == AName)
571 		{
572 			return F;
573 		}
574 	}
575 	return NULL;
576 	unguard;
577 }
578 
579 //==========================================================================
580 //
581 //	VFont::GetFont
582 //
583 //==========================================================================
584 
GetFont(VName AName,VName LumpName)585 VFont* VFont::GetFont(VName AName, VName LumpName)
586 {
587 	guard(VFont::GetFont);
588 	VFont* F = FindFont(AName);
589 	if (F)
590 	{
591 		return F;
592 	}
593 
594 	//	Check for wad lump.
595 	int Lump = W_CheckNumForName(LumpName);
596 	if (Lump >= 0)
597 	{
598 		//	Read header.
599 		VStream* Strm = W_CreateLumpReaderNum(Lump);
600 		char Hdr[4];
601 		Strm->Serialise(Hdr, 4);
602 		delete Strm;
603 		Strm = NULL;
604 
605 		if (Hdr[0] == 'F' && Hdr[1] == 'O' && Hdr[2] == 'N')
606 		{
607 			if (Hdr[3] == '1')
608 			{
609 				return new VFon1Font(AName, Lump);
610 			}
611 			if (Hdr[3] == '2')
612 			{
613 				return new VFon2Font(AName, Lump);
614 			}
615 		}
616 	}
617 
618 	int TexNum = GTextureManager.CheckNumForName(LumpName, TEXTYPE_Any);
619 	if (TexNum <= 0)
620 	{
621 		TexNum = GTextureManager.AddPatch(LumpName, TEXTYPE_Pic);
622 	}
623 	if (TexNum > 0)
624 	{
625 		return new VSingleTextureFont(AName, TexNum);
626 	}
627 
628 	return NULL;
629 	unguard;
630 }
631 
632 //==========================================================================
633 //
634 //	VFont::VFont
635 //
636 //==========================================================================
637 
VFont()638 VFont::VFont()
639 {
640 }
641 
642 //==========================================================================
643 //
644 //	VFont::VFont
645 //
646 //==========================================================================
647 
VFont(VName AName,const VStr & FormatStr,int First,int Count,int StartIndex)648 VFont::VFont(VName AName, const VStr& FormatStr, int First, int Count,
649 	int StartIndex)
650 {
651 	guard(VFont::VFont);
652 	Name = AName;
653 	Next = Fonts;
654 	Fonts = this;
655 
656 	for (int i = 0; i < 128; i++)
657 	{
658 		AsciiChars[i] = -1;
659 	}
660 	FirstChar = -1;
661 	LastChar = -1;
662 	FontHeight = 0;
663 	Kerning = 0;
664 	Translation = NULL;
665 	bool ColoursUsed[256];
666 	memset(ColoursUsed, 0, sizeof(ColoursUsed));
667 
668 	for (int i = 0; i < Count; i++)
669 	{
670 		int Char = i + First;
671 		char Buffer[10];
672 		sprintf(Buffer, *FormatStr, i + StartIndex);
673 		VName LumpName(Buffer, VName::AddLower8);
674 		int Lump = W_CheckNumForName(LumpName, WADNS_Graphics);
675 
676 		//	In Doom stcfn121 is actually a '|' and not 'y' and many wad
677 		// authors provide it as such, so put it in correct location.
678 		if (LumpName == "stcfn121" &&
679 			(W_CheckNumForName("stcfn120", WADNS_Graphics) == -1 ||
680 			W_CheckNumForName("stcfn122", WADNS_Graphics) == -1))
681 		{
682 			Char = '|';
683 		}
684 
685 		if (Lump >= 0)
686 		{
687 			VTexture* Tex = GTextureManager[GTextureManager.AddPatch(LumpName,
688 				TEXTYPE_Pic)];
689 			FFontChar& FChar = Chars.Alloc();
690 			FChar.Char = Char;
691 			FChar.TexNum = -1;
692 			FChar.BaseTex = Tex;
693 			if (Char < 128)
694 			{
695 				AsciiChars[Char] = Chars.Num() - 1;
696 			}
697 
698 			//	Calculate height of font character and adjust font height
699 			// as needed.
700 			int Height = Tex->GetScaledHeight();
701 			int TOffs = Tex->GetScaledTOffset();
702 			Height += abs(TOffs);
703 			if (FontHeight < Height)
704 			{
705 				FontHeight = Height;
706 			}
707 
708 			//	Update first and last characters.
709 			if (FirstChar == -1)
710 			{
711 				FirstChar = Char;
712 			}
713 			LastChar = Char;
714 
715 			//	Mark colours that are used by this texture.
716 			MarkUsedColours(Tex, ColoursUsed);
717 		}
718 	}
719 
720 	//	Set up width of a space character as half width of N character
721 	// or 4 if character N has no graphic for it.
722 	int NIdx = FindChar('N');
723 	if (NIdx >= 0)
724 	{
725 		SpaceWidth = (Chars[NIdx].BaseTex->GetScaledWidth() + 1) / 2;
726 	}
727 	else
728 	{
729 		SpaceWidth = 4;
730 	}
731 
732 	BuildTranslations(ColoursUsed, r_palette, false, true);
733 
734 	//	Create texture objects for all different colours.
735 	for (int i = 0; i < Chars.Num(); i++)
736 	{
737 		Chars[i].Textures = new VTexture*[TextColours.Num()];
738 		for (int j = 0; j < TextColours.Num(); j++)
739 		{
740 			Chars[i].Textures[j] = new VFontChar(Chars[i].BaseTex,
741 				Translation + j * 256);
742 			//	Currently all render drivers expect all textures to be
743 			// registered in texture manager.
744 			GTextureManager.AddTexture(Chars[i].Textures[j]);
745 		}
746 	}
747 	unguard;
748 }
749 
750 //==========================================================================
751 //
752 //	VFont::~VFont
753 //
754 //==========================================================================
755 
~VFont()756 VFont::~VFont()
757 {
758 	guard(VFont::~VFont);
759 	for (int i = 0; i < Chars.Num(); i++)
760 	{
761 		if (Chars[i].Textures)
762 		{
763 			delete[] Chars[i].Textures;
764 			Chars[i].Textures = NULL;
765 		}
766 	}
767 	Chars.Clear();
768 	if (Translation)
769 	{
770 		delete[] Translation;
771 		Translation = NULL;
772 	}
773 	unguard;
774 }
775 
776 //==========================================================================
777 //
778 //	VFont::BuildTranslations
779 //
780 //==========================================================================
781 
BuildTranslations(const bool * ColoursUsed,rgba_t * Pal,bool ConsoleTrans,bool Rescale)782 void VFont::BuildTranslations(const bool* ColoursUsed, rgba_t* Pal,
783 	bool ConsoleTrans, bool Rescale)
784 {
785 	guard(VFont::BuildTranslations);
786 	//	Calculate luminosity for all colours and find minimal and maximal
787 	// values for used colours.
788 	float Luminosity[256];
789 	float MinLum = 1000000.0;
790 	float MaxLum = 0.0;
791 	for (int i = 1; i < 256; i++)
792 	{
793 		Luminosity[i] = Pal[i].r * 0.299 + Pal[i].g * 0.587 +
794 			Pal[i].b * 0.114;
795 		if (ColoursUsed[i])
796 		{
797 			if (MinLum > Luminosity[i])
798 			{
799 				MinLum = Luminosity[i];
800 			}
801 			if (MaxLum < Luminosity[i])
802 			{
803 				MaxLum = Luminosity[i];
804 			}
805 		}
806 	}
807 	//	Create gradual luminosity values.
808 	float Scale = 1.0 / (Rescale ? (MaxLum - MinLum) : 255.0);
809 	for (int i = 1; i < 256; i++)
810 	{
811 		Luminosity[i] = (Luminosity[i] - MinLum) *Scale;
812 		Luminosity[i] = MID(0.0, Luminosity[i], 1.0);
813 	}
814 
815 	Translation = new rgba_t[256 * TextColours.Num()];
816 	for (int ColIdx = 0; ColIdx < TextColours.Num(); ColIdx++)
817 	{
818 		rgba_t* pOut = Translation + ColIdx * 256;
819 		const TArray<VColTranslationDef>& TList = ConsoleTrans ?
820 			TextColours[ColIdx].ConsoleTranslations :
821 			TextColours[ColIdx].Translations;
822 		if (ColIdx == CR_UNTRANSLATED || !TList.Num())
823 		{
824 			memcpy(pOut, Pal, 4 * 256);
825 			continue;
826 		}
827 
828 		pOut[0] = Pal[0];
829 		for (int i = 1; i < 256; i++)
830 		{
831 			int ILum = (int)(Luminosity[i] * 256);
832 			int TDefIdx = 0;
833 			while (TDefIdx < TList.Num() - 1 && ILum > TList[TDefIdx].LumTo)
834 			{
835 				TDefIdx++;
836 			}
837 			const VColTranslationDef& TDef = TList[TDefIdx];
838 
839 			//	Linearly interpolate between colours.
840 			float v = ((float)(ILum - TDef.LumFrom) /
841 				(float)(TDef.LumTo - TDef.LumFrom));
842 			int r = (int)((1.0 - v) * TDef.From.r + v * TDef.To.r);
843 			int g = (int)((1.0 - v) * TDef.From.g + v * TDef.To.g);
844 			int b = (int)((1.0 - v) * TDef.From.b + v * TDef.To.b);
845 			pOut[i].r = MID(0, r, 255);
846 			pOut[i].g = MID(0, g, 255);
847 			pOut[i].b = MID(0, b, 255);
848 			pOut[i].a = 255;
849 		}
850 	}
851 	unguard;
852 }
853 
854 //==========================================================================
855 //
856 //	VFont::GetChar
857 //
858 //==========================================================================
859 
FindChar(int Chr) const860 int VFont::FindChar(int Chr) const
861 {
862 	//	Check if character is outside of available character range.
863 	if (Chr < FirstChar || Chr > LastChar)
864 	{
865 		return -1;
866 	}
867 
868 	//	Fast look-up for ASCII characters
869 	if (Chr < 128)
870 	{
871 		return AsciiChars[Chr];
872 	}
873 
874 	//	A slower one for unicode.
875 	for (int i = 0; i < Chars.Num(); i++)
876 	{
877 		if (Chars[i].Char == Chr)
878 		{
879 			return i;
880 		}
881 	}
882 	return -1;
883 }
884 
885 //==========================================================================
886 //
887 //	VFont::GetChar
888 //
889 //==========================================================================
890 
GetChar(int Chr,int * pWidth,int Colour) const891 VTexture* VFont::GetChar(int Chr, int* pWidth, int Colour) const
892 {
893 	guard(VFont::GetChar);
894 	int Idx = FindChar(Chr);
895 	if (Idx < 0)
896 	{
897 		//	Try upper-case letter.
898 		Chr = VStr::ToUpper(Chr);
899 		Idx = FindChar(Chr);
900 		if (Idx < 0)
901 		{
902 			*pWidth = SpaceWidth;
903 			return NULL;
904 		}
905 	}
906 
907 	if (Colour < 0 || Colour >= TextColours.Num())
908 	{
909 		Colour = CR_UNTRANSLATED;
910 	}
911 	VTexture* Tex = Chars[Idx].Textures ? Chars[Idx].Textures[Colour] :
912 		Chars[Idx].TexNum > 0 ? GTextureManager(Chars[Idx].TexNum) :
913 		Chars[Idx].BaseTex;
914 	*pWidth = Tex->GetScaledWidth();
915 	return Tex;
916 	unguard;
917 }
918 
919 //==========================================================================
920 //
921 //	VFont::GetCharWidth
922 //
923 //==========================================================================
924 
GetCharWidth(int Chr) const925 int VFont::GetCharWidth(int Chr) const
926 {
927 	guard(VFont::GetCharWidth);
928 	int Idx = FindChar(Chr);
929 	if (Idx < 0)
930 	{
931 		//	Try upper-case letter.
932 		Chr = VStr::ToUpper(Chr);
933 		Idx = FindChar(Chr);
934 		if (Idx < 0)
935 		{
936 			return SpaceWidth;
937 		}
938 	}
939 
940 	return Chars[Idx].BaseTex->GetScaledWidth();
941 	unguard;
942 }
943 
944 //==========================================================================
945 //
946 //	VFont::MarkUsedColours
947 //
948 //==========================================================================
949 
MarkUsedColours(VTexture * Tex,bool * Used)950 void VFont::MarkUsedColours(VTexture* Tex, bool* Used)
951 {
952 	guard(VFont::MarkUsedColours);
953 	const vuint8* Pixels = Tex->GetPixels8();
954 	int Count = Tex->GetWidth() * Tex->GetHeight();
955 	for (int i = 0; i < Count; i++)
956 	{
957 		Used[Pixels[i]] = true;
958 	}
959 	unguard;
960 }
961 
962 //==========================================================================
963 //
964 //	VFont::ParseColourEscape
965 //
966 //	Assumes that pColour points to the character right after the colour
967 // escape character.
968 //
969 //==========================================================================
970 
ParseColourEscape(const char * & pColour,int NormalColour,int BoldColour)971 int VFont::ParseColourEscape(const char*& pColour, int NormalColour,
972 	int BoldColour)
973 {
974 	guard(VFont::ParseColourEscape);
975 	const char* Chr = pColour;
976 	int Col = *Chr++;
977 
978 	//	Standard colors, upper case
979 	if (Col >= 'A' && Col < 'A' + NUM_TEXT_COLOURS)
980 	{
981 		Col -= 'A';
982 	}
983 	//	Standard colors, lower case
984 	else if (Col >= 'a' && Col < 'a' + NUM_TEXT_COLOURS)
985 	{
986 		Col -= 'a';
987 	}
988 	//	Normal colour
989 	else if (Col == '-')
990 	{
991 		Col = NormalColour;
992 	}
993 	//	Bold colour
994 	else if (Col == '+')
995 	{
996 		Col = BoldColour;
997 	}
998 	//	Named colours.
999 	else if (Col == '[')
1000 	{
1001 		VStr CName;
1002 		while (*Chr && *Chr != ']')
1003 		{
1004 			CName += *Chr++;
1005 		}
1006 		if (*Chr == ']')
1007 		{
1008 			Chr++;
1009 		}
1010 		Col = FindTextColour(*CName.ToLower());
1011 	}
1012 	else
1013 	{
1014 		if (!Col)
1015 		{
1016 			Chr--;
1017 		}
1018 		Col = CR_UNDEFINED;
1019 	}
1020 
1021 	//	Set pointer after the colour definition.
1022 	pColour = Chr;
1023 	return Col;
1024 	unguard;
1025 }
1026 
1027 //==========================================================================
1028 //
1029 //	VFont::FindTextColour
1030 //
1031 //==========================================================================
1032 
FindTextColour(VName Name)1033 int VFont::FindTextColour(VName Name)
1034 {
1035 	guard(VFont::FindTextColour);
1036 	for (int i = 0; i < TextColourLookup.Num(); i++)
1037 	{
1038 		if (TextColourLookup[i].Name == Name)
1039 		{
1040 			return TextColourLookup[i].Index;
1041 		}
1042 	}
1043 	return CR_UNTRANSLATED;
1044 	unguard;
1045 }
1046 
1047 //==========================================================================
1048 //
1049 //	VFont::StringWidth
1050 //
1051 //==========================================================================
1052 
StringWidth(const VStr & String) const1053 int VFont::StringWidth(const VStr& String) const
1054 {
1055 	guard(VFont::StringWidth);
1056 	int w = 0;
1057 	for (const char* SPtr = *String; *SPtr;)
1058 	{
1059 		int c = VStr::GetChar(SPtr);
1060 		//	Check for colour escape.
1061 		if (c == TEXT_COLOUR_ESCAPE)
1062 		{
1063 			ParseColourEscape(SPtr, CR_UNDEFINED, CR_UNDEFINED);
1064 			continue;
1065 		}
1066 		w += GetCharWidth(c) + GetKerning();
1067 	}
1068 	return w;
1069 	unguard;
1070 }
1071 
1072 //==========================================================================
1073 //
1074 //	VFont::TextWidth
1075 //
1076 //==========================================================================
1077 
TextWidth(const VStr & String) const1078 int VFont::TextWidth(const VStr& String) const
1079 {
1080 	guard(VFont::TextWidth);
1081 	size_t		i;
1082 	int			w1;
1083 	int			w = 0;
1084 	int			start = 0;
1085 
1086 	for (i = 0; i <= String.Length(); i++)
1087 		if ((String[i] == '\n') || !String[i])
1088 		{
1089 			w1 = StringWidth(VStr(String, start, i - start));
1090 			if (w1 > w)
1091 				w = w1;
1092 			start = i;
1093 		}
1094 	return w;
1095 	unguard;
1096 }
1097 
1098 //==========================================================================
1099 //
1100 //	VFont::TextHeight
1101 //
1102 //==========================================================================
1103 
TextHeight(const VStr & String) const1104 int VFont::TextHeight(const VStr& String) const
1105 {
1106 	guard(VFont::TextHeight);
1107 	int h = FontHeight;
1108 	for (size_t i = 0; i < String.Length(); i++)
1109 	{
1110 		if (String[i] == '\n')
1111 		{
1112 			h += FontHeight;
1113 		}
1114 	}
1115 	return h;
1116 	unguard;
1117 }
1118 
1119 //==========================================================================
1120 //
1121 //	VFont::SplitText
1122 //
1123 //==========================================================================
1124 
SplitText(const VStr & Text,TArray<VSplitLine> & Lines,int MaxWidth) const1125 int VFont::SplitText(const VStr& Text, TArray<VSplitLine>& Lines,
1126 	int MaxWidth) const
1127 {
1128 	guard(VFont::SplitText);
1129 	Lines.Clear();
1130 	const char* Start = *Text;
1131 	bool WordStart = true;
1132 	int CurW = 0;
1133 	for (const char* SPtr = *Text; *SPtr;)
1134 	{
1135 		const char* PChar = SPtr;
1136 		int c = VStr::GetChar(SPtr);
1137 
1138 		//	Check for colour escape.
1139 		if (c == TEXT_COLOUR_ESCAPE)
1140 		{
1141 			ParseColourEscape(SPtr, CR_UNDEFINED, CR_UNDEFINED);
1142 			continue;
1143 		}
1144 
1145 		if (c == '\n')
1146 		{
1147 			VSplitLine& L = Lines.Alloc();
1148 			L.Text = VStr(Text, Start - *Text, PChar - Start);
1149 			L.Width = CurW;
1150 			Start = SPtr;
1151 			WordStart = true;
1152 			CurW = 0;
1153 		}
1154 		else if (WordStart && c > ' ')
1155 		{
1156 			const char* SPtr2 = SPtr;
1157 			const char* PChar2 = PChar;
1158 			int c2 = c;
1159 			int NewW = CurW;
1160 			while (c2 > ' ' || c2 == TEXT_COLOUR_ESCAPE)
1161 			{
1162 				if (c2 != TEXT_COLOUR_ESCAPE)
1163 				{
1164 					NewW += GetCharWidth(c2);
1165 				}
1166 				PChar2 = SPtr2;
1167 				c2 = VStr::GetChar(SPtr2);
1168 				//	Check for colour escape.
1169 				if (c2 == TEXT_COLOUR_ESCAPE)
1170 				{
1171 					ParseColourEscape(SPtr2, CR_UNDEFINED, CR_UNDEFINED);
1172 				}
1173 			}
1174 			if (NewW > MaxWidth && PChar != Start)
1175 			{
1176 				VSplitLine& L = Lines.Alloc();
1177 				L.Text = VStr(Text, Start - *Text, PChar - Start);
1178 				L.Width = CurW;
1179 				Start = PChar;
1180 				CurW = 0;
1181 			}
1182 			WordStart = false;
1183 			CurW += GetCharWidth(c);
1184 		}
1185 		else if (c <= ' ')
1186 		{
1187 			WordStart = true;
1188 			CurW += GetCharWidth(c);
1189 		}
1190 		else
1191 		{
1192 			CurW += GetCharWidth(c);
1193 		}
1194 		if (!*SPtr && Start != SPtr)
1195 		{
1196 			VSplitLine& L = Lines.Alloc();
1197 			L.Text = Start;
1198 			L.Width = CurW;
1199 		}
1200 	}
1201 	return Lines.Num() * FontHeight;
1202 	unguard;
1203 }
1204 
1205 //==========================================================================
1206 //
1207 //	VFont::SplitTextWithNewlines
1208 //
1209 //==========================================================================
1210 
SplitTextWithNewlines(const VStr & Text,int MaxWidth) const1211 VStr VFont::SplitTextWithNewlines(const VStr& Text, int MaxWidth) const
1212 {
1213 	guard(VFont::SplitTextWithNewlines);
1214 	TArray<VSplitLine> Lines;
1215 	SplitText(Text, Lines, MaxWidth);
1216 	VStr Ret;
1217 	for (int i = 0; i < Lines.Num(); i++)
1218 	{
1219 		Ret += Lines[i].Text + "\n";
1220 	}
1221 	return Ret;
1222 	unguard;
1223 }
1224 
1225 //==========================================================================
1226 //
1227 //	VSpecialFont::VSpecialFont
1228 //
1229 //==========================================================================
1230 
VSpecialFont(VName AName,const TArray<int> & CharIndexes,const TArray<VName> & CharLumps,const bool * NoTranslate)1231 VSpecialFont::VSpecialFont(VName AName, const TArray<int>& CharIndexes,
1232 	const TArray<VName>& CharLumps, const bool* NoTranslate)
1233 {
1234 	guard(VSpecialFont::VSpecialFont);
1235 	Name = AName;
1236 	Next = Fonts;
1237 	Fonts = this;
1238 
1239 	for (int i = 0; i < 128; i++)
1240 	{
1241 		AsciiChars[i] = -1;
1242 	}
1243 	FirstChar = -1;
1244 	LastChar = -1;
1245 	FontHeight = 0;
1246 	Kerning = 0;
1247 	Translation = NULL;
1248 	bool ColoursUsed[256];
1249 	memset(ColoursUsed, 0, sizeof(ColoursUsed));
1250 
1251 	check(CharIndexes.Num() == CharLumps.Num());
1252 	for (int i = 0; i < CharIndexes.Num(); i++)
1253 	{
1254 		int Char = CharIndexes[i];
1255 		VName LumpName = CharLumps[i];
1256 
1257 		VTexture* Tex = GTextureManager[GTextureManager.AddPatch(LumpName,
1258 			TEXTYPE_Pic)];
1259 		FFontChar& FChar = Chars.Alloc();
1260 		FChar.Char = Char;
1261 		FChar.TexNum = -1;
1262 		FChar.BaseTex = Tex;
1263 		if (Char < 128)
1264 		{
1265 			AsciiChars[Char] = Chars.Num() - 1;
1266 		}
1267 
1268 		//	Calculate height of font character and adjust font height
1269 		// as needed.
1270 		int Height = Tex->GetScaledHeight();
1271 		int TOffs = Tex->GetScaledTOffset();
1272 		Height += abs(TOffs);
1273 		if (FontHeight < Height)
1274 		{
1275 			FontHeight = Height;
1276 		}
1277 
1278 		//	Update first and last characters.
1279 		if (FirstChar == -1)
1280 		{
1281 			FirstChar = Char;
1282 		}
1283 		LastChar = Char;
1284 
1285 		//	Mark colours that are used by this texture.
1286 		MarkUsedColours(Tex, ColoursUsed);
1287 	}
1288 
1289 	//	Exclude non-translated colours from calculations.
1290 	for (int i = 0; i < 256; i++)
1291 	{
1292 		if (NoTranslate[i])
1293 		{
1294 			ColoursUsed[i] = false;
1295 		}
1296 	}
1297 
1298 	//	Set up width of a space character as half width of N character
1299 	// or 4 if character N has no graphic for it.
1300 	int NIdx = FindChar('N');
1301 	if (NIdx >= 0)
1302 	{
1303 		SpaceWidth = (Chars[NIdx].BaseTex->GetScaledWidth() + 1) / 2;
1304 	}
1305 	else
1306 	{
1307 		SpaceWidth = 4;
1308 	}
1309 
1310 	BuildTranslations(ColoursUsed, r_palette, false, true);
1311 
1312 	//	Map non-translated colours to their original values
1313 	for (int i = 0; i < TextColours.Num(); i++)
1314 	{
1315 		for (int j = 0; j < 256; j++)
1316 		{
1317 			if (NoTranslate[j])
1318 			{
1319 				Translation[i * 256 + j] = r_palette[j];
1320 			}
1321 		}
1322 	}
1323 
1324 	//	Create texture objects for all different colours.
1325 	for (int i = 0; i < Chars.Num(); i++)
1326 	{
1327 		Chars[i].Textures = new VTexture*[TextColours.Num()];
1328 		for (int j = 0; j < TextColours.Num(); j++)
1329 		{
1330 			Chars[i].Textures[j] = new VFontChar(Chars[i].BaseTex,
1331 				Translation + j * 256);
1332 			//	Currently all render drivers expect all textures to be
1333 			// registered in texture manager.
1334 			GTextureManager.AddTexture(Chars[i].Textures[j]);
1335 		}
1336 	}
1337 	unguard;
1338 }
1339 
1340 //==========================================================================
1341 //
1342 //	VFon1Font::VFon1Font
1343 //
1344 //==========================================================================
1345 
VFon1Font(VName AName,int LumpNum)1346 VFon1Font::VFon1Font(VName AName, int LumpNum)
1347 {
1348 	guard(VFon1Font::VFon1Font);
1349 	Name = AName;
1350 	Next = Fonts;
1351 	Fonts = this;
1352 
1353 	VStream* Strm = W_CreateLumpReaderNum(LumpNum);
1354 	//	Skip ID.
1355 	Strm->Seek(4);
1356 	vuint16 w;
1357 	vuint16 h;
1358 	*Strm << w << h;
1359 	SpaceWidth = w;
1360 	FontHeight = h;
1361 
1362 	FirstChar = 0;
1363 	LastChar = 255;
1364 	Kerning = 0;
1365 	Translation = NULL;
1366 	for (int i = 0; i < 128; i++)
1367 	{
1368 		AsciiChars[i] = i;
1369 	}
1370 
1371 	//	Mark all colours as used and construct a grayscale palette.
1372 	bool ColoursUsed[256];
1373 	rgba_t Pal[256];
1374 	ColoursUsed[0] = false;
1375 	Pal[0].r = 0;
1376 	Pal[0].g = 0;
1377 	Pal[0].b = 0;
1378 	Pal[0].a = 0;
1379 	for (int i = 1; i < 256; i++)
1380 	{
1381 		ColoursUsed[i] = true;
1382 		Pal[i].r = (i - 1) * 255 / 254;
1383 		Pal[i].g = Pal[i].r;
1384 		Pal[i].b = Pal[i].r;
1385 		Pal[i].a = 255;
1386 	}
1387 
1388 	BuildTranslations(ColoursUsed, Pal, true, false);
1389 
1390 	for (int i = 0; i < 256; i++)
1391 	{
1392 		FFontChar& FChar = Chars.Alloc();
1393 		FChar.Char = i;
1394 		FChar.TexNum = -1;
1395 
1396 		//	Create texture objects for all different colours.
1397 		FChar.Textures = new VTexture*[TextColours.Num()];
1398 		for (int j = 0; j < TextColours.Num(); j++)
1399 		{
1400 			FChar.Textures[j] = new VFontChar2(LumpNum, Strm->Tell(),
1401 				SpaceWidth, FontHeight, Translation + j * 256, 255);
1402 			//	Currently all render drivers expect all textures to be
1403 			// registered in texture manager.
1404 			GTextureManager.AddTexture(FChar.Textures[j]);
1405 		}
1406 		FChar.BaseTex = FChar.Textures[CR_UNTRANSLATED];
1407 
1408 		//	Skip character data.
1409 		int Count = SpaceWidth * FontHeight;
1410 		do
1411 		{
1412 			vint8 Code = Streamer<vint8>(*Strm);
1413 			if (Code >= 0)
1414 			{
1415 				Count -= Code + 1;
1416 				while (Code-- >= 0)
1417 				{
1418 					Streamer<vint8>(*Strm);
1419 				}
1420 			}
1421 			else if (Code != -128)
1422 			{
1423 				Count -= 1 - Code;
1424 				Streamer<vint8>(*Strm);
1425 			}
1426 		}
1427 		while (Count > 0);
1428 		if (Count < 0)
1429 		{
1430 			Sys_Error("Overflow decompressing a character %d", i);
1431 		}
1432 	}
1433 
1434 	delete Strm;
1435 	Strm = NULL;
1436 	unguard;
1437 }
1438 
1439 //==========================================================================
1440 //
1441 //	VFon2Font::VFon2Font
1442 //
1443 //	4       - header
1444 //	2       - height
1445 //	1       - first char
1446 //	1       - last char
1447 //	1       - fixed width flag
1448 //	1
1449 //	1       - active colours
1450 //	1       - kerning flag
1451 //	2 (if have flag) - kerning
1452 //
1453 //==========================================================================
1454 
VFon2Font(VName AName,int LumpNum)1455 VFon2Font::VFon2Font(VName AName, int LumpNum)
1456 {
1457 	guard(VFon2Font::VFon2Font);
1458 	Name = AName;
1459 	Next = Fonts;
1460 	Fonts = this;
1461 
1462 	VStream* Strm = W_CreateLumpReaderNum(LumpNum);
1463 	//	Skip ID.
1464 	Strm->Seek(4);
1465 
1466 	//	Read header.
1467 	FontHeight = Streamer<vuint16>(*Strm);
1468 	FirstChar = Streamer<vuint8>(*Strm);
1469 	LastChar = Streamer<vuint8>(*Strm);
1470 	vuint8 FixedWidthFlag;
1471 	vuint8 RescalePal;
1472 	vuint8 ActiveColours;
1473 	vuint8 KerningFlag;
1474 	*Strm << FixedWidthFlag << RescalePal << ActiveColours << KerningFlag;
1475 	Kerning = 0;
1476 	if (KerningFlag & 1)
1477 	{
1478 		Kerning = Streamer<vint16>(*Strm);
1479 	}
1480 
1481 	Translation = NULL;
1482 	for (int i = 0; i < 128; i++)
1483 	{
1484 		AsciiChars[i] = -1;
1485 	}
1486 
1487 	//	Read character widths.
1488 	int Count = LastChar - FirstChar + 1;
1489 	vuint16* Widths = new vuint16[Count];
1490 	int TotalWidth = 0;
1491 	if (FixedWidthFlag)
1492 	{
1493 		*Strm << Widths[0];
1494 		for (int i = 1; i < Count; i++)
1495 		{
1496 			Widths[i] = Widths[0];
1497 		}
1498 		TotalWidth = Widths[0] * Count;
1499 	}
1500 	else
1501 	{
1502 		for (int i = 0; i < Count; i++)
1503 		{
1504 			*Strm << Widths[i];
1505 			TotalWidth += Widths[i];
1506 		}
1507 	}
1508 
1509 	if (FirstChar <= ' ' && LastChar >= ' ')
1510 	{
1511 		SpaceWidth = Widths[' ' - FirstChar];
1512 	}
1513 	else if (FirstChar <= 'N' && LastChar >= 'N')
1514 	{
1515 		SpaceWidth = (Widths['N' - FirstChar] + 1) / 2;
1516 	}
1517 	else
1518 	{
1519 		SpaceWidth = TotalWidth * 2 / (3 * Count);
1520 	}
1521 
1522 	//	Read palette
1523 	bool ColoursUsed[256];
1524 	rgba_t Pal[256];
1525 	memset(ColoursUsed, 0, sizeof(ColoursUsed));
1526 	memset(Pal, 0, sizeof(Pal));
1527 	for (int i = 0; i <= ActiveColours; i++)
1528 	{
1529 		ColoursUsed[i] = true;
1530 		*Strm << Pal[i].r << Pal[i].g << Pal[i].b;
1531 		Pal[i].a = i ? 255 : 0;
1532 	}
1533 
1534 	BuildTranslations(ColoursUsed, Pal, false, !!RescalePal);
1535 
1536 	for (int i = 0; i < Count; i++)
1537 	{
1538 		int Chr = FirstChar + i;
1539 		int DataSize = Widths[i] * FontHeight;
1540 		if (DataSize > 0)
1541 		{
1542 			FFontChar& FChar = Chars.Alloc();
1543 			FChar.Char = Chr;
1544 			FChar.TexNum = -1;
1545 			if (Chr < 128)
1546 			{
1547 				AsciiChars[Chr] = Chars.Num() - 1;
1548 			}
1549 
1550 			//	Create texture objects for all different colours.
1551 			FChar.Textures = new VTexture*[TextColours.Num()];
1552 			for (int j = 0; j < TextColours.Num(); j++)
1553 			{
1554 				FChar.Textures[j] = new VFontChar2(LumpNum, Strm->Tell(),
1555 					Widths[i], FontHeight, Translation + j * 256,
1556 					ActiveColours);
1557 				//	Currently all render drivers expect all textures to be
1558 				// registered in texture manager.
1559 				GTextureManager.AddTexture(FChar.Textures[j]);
1560 			}
1561 			FChar.BaseTex = FChar.Textures[CR_UNTRANSLATED];
1562 
1563 			//	Skip character data.
1564 			do
1565 			{
1566 				vint8 Code = Streamer<vint8>(*Strm);
1567 				if (Code >= 0)
1568 				{
1569 					DataSize -= Code + 1;
1570 					while (Code-- >= 0)
1571 					{
1572 						Streamer<vint8>(*Strm);
1573 					}
1574 				}
1575 				else if (Code != -128)
1576 				{
1577 					DataSize -= 1 - Code;
1578 					Streamer<vint8>(*Strm);
1579 				}
1580 			}
1581 			while (DataSize > 0);
1582 			if (DataSize < 0)
1583 			{
1584 				Sys_Error("Overflow decompressing a character %d", i);
1585 			}
1586 		}
1587 	}
1588 
1589 	delete Strm;
1590 	Strm = NULL;
1591 	delete[] Widths;
1592 	Widths = NULL;
1593 	unguard;
1594 }
1595 
1596 //==========================================================================
1597 //
1598 //	VSingleTextureFont::VSingleTextureFont
1599 //
1600 //==========================================================================
1601 
VSingleTextureFont(VName AName,int TexNum)1602 VSingleTextureFont::VSingleTextureFont(VName AName, int TexNum)
1603 {
1604 	guard(VSingleTextureFont::VSingleTextureFont);
1605 	Name = AName;
1606 	Next = Fonts;
1607 	Fonts = this;
1608 
1609 	VTexture* Tex = GTextureManager[TexNum];
1610 	for (int i = 0; i < 128; i++)
1611 	{
1612 		AsciiChars[i] = -1;
1613 	}
1614 	AsciiChars[(int)'A'] = 0;
1615 	FirstChar = 'A';
1616 	LastChar = 'A';
1617 	SpaceWidth = Tex->GetScaledWidth();
1618 	FontHeight = Tex->GetScaledHeight();
1619 	Kerning = 0;
1620 	Translation = NULL;
1621 
1622 	FFontChar& FChar = Chars.Alloc();
1623 	FChar.Char = 'A';
1624 	FChar.TexNum = TexNum;
1625 	FChar.BaseTex = Tex;
1626 	FChar.Textures = NULL;
1627 	unguard;
1628 }
1629 
1630 //==========================================================================
1631 //
1632 //	VFontChar::VFontChar
1633 //
1634 //==========================================================================
1635 
VFontChar(VTexture * ATex,rgba_t * APalette)1636 VFontChar::VFontChar(VTexture* ATex, rgba_t* APalette)
1637 : BaseTex(ATex)
1638 , Palette(APalette)
1639 {
1640 	Type = TEXTYPE_FontChar;
1641 	Format = TEXFMT_8Pal;
1642 	Name = NAME_None;
1643 	Width = BaseTex->GetWidth();
1644 	Height = BaseTex->GetHeight();
1645 	SOffset = BaseTex->SOffset;
1646 	TOffset = BaseTex->TOffset;
1647 	SScale = BaseTex->SScale;
1648 	TScale = BaseTex->TScale;
1649 }
1650 
1651 //==========================================================================
1652 //
1653 //	VFontChar::~VFontChar
1654 //
1655 //==========================================================================
1656 
~VFontChar()1657 VFontChar::~VFontChar()
1658 {
1659 }
1660 
1661 //==========================================================================
1662 //
1663 //	VFontChar::GetPixels
1664 //
1665 //==========================================================================
1666 
GetPixels()1667 vuint8* VFontChar::GetPixels()
1668 {
1669 	guard(VFontChar::GetPixels);
1670 	return BaseTex->GetPixels8();
1671 	unguard;
1672 }
1673 
1674 //==========================================================================
1675 //
1676 //	VFontChar::GetPalette
1677 //
1678 //==========================================================================
1679 
GetPalette()1680 rgba_t* VFontChar::GetPalette()
1681 {
1682 	guard(VFontChar::GetPalette);
1683 	return Palette;
1684 	unguard;
1685 }
1686 
1687 //==========================================================================
1688 //
1689 //	VFontChar::Unload
1690 //
1691 //==========================================================================
1692 
Unload()1693 void VFontChar::Unload()
1694 {
1695 	guard(VFontChar::Unload);
1696 	BaseTex->Unload();
1697 	unguard;
1698 }
1699 
1700 //==========================================================================
1701 //
1702 //	VFontChar::GetHighResolutionTexture
1703 //
1704 //==========================================================================
1705 
GetHighResolutionTexture()1706 VTexture* VFontChar::GetHighResolutionTexture()
1707 {
1708 	guard(VFontChar::GetHighResolutionTexture);
1709 	if (!r_hirestex)
1710 	{
1711 		return NULL;
1712 	}
1713 	if (!HiResTexture)
1714 	{
1715 		VTexture* Tex = BaseTex->GetHighResolutionTexture();
1716 		if (Tex)
1717 		{
1718 			HiResTexture = new VFontChar(Tex, Palette);
1719 		}
1720 	}
1721 	return HiResTexture;
1722 	unguard;
1723 }
1724 
1725 //==========================================================================
1726 //
1727 //	VFontChar2::VFontChar2
1728 //
1729 //==========================================================================
1730 
VFontChar2(int ALumpNum,int AFilePos,int CharW,int CharH,rgba_t * APalette,int AMaxCol)1731 VFontChar2::VFontChar2(int ALumpNum, int AFilePos, int CharW, int CharH,
1732 	rgba_t* APalette, int AMaxCol)
1733 : LumpNum(ALumpNum)
1734 , FilePos(AFilePos)
1735 , Pixels(NULL)
1736 , Palette(APalette)
1737 , MaxCol(AMaxCol)
1738 {
1739 	Type = TEXTYPE_FontChar;
1740 	Format = TEXFMT_8Pal;
1741 	Name = NAME_None;
1742 	Width = CharW;
1743 	Height = CharH;
1744 }
1745 
1746 //==========================================================================
1747 //
1748 //	VFontChar2::~VFontChar2
1749 //
1750 //==========================================================================
1751 
~VFontChar2()1752 VFontChar2::~VFontChar2()
1753 {
1754 	if (Pixels)
1755 	{
1756 		delete[] Pixels;
1757 		Pixels = NULL;
1758 	}
1759 }
1760 
1761 //==========================================================================
1762 //
1763 //	VFontChar2::GetPixels
1764 //
1765 //==========================================================================
1766 
GetPixels()1767 vuint8* VFontChar2::GetPixels()
1768 {
1769 	guard(VFontChar2::GetPixels);
1770 	if (Pixels)
1771 	{
1772 		return Pixels;
1773 	}
1774 
1775 	VStream* Strm = W_CreateLumpReaderNum(LumpNum);
1776 	Strm->Seek(FilePos);
1777 
1778 	int Count = Width * Height;
1779 	Pixels = new vuint8[Count];
1780 	vuint8* pDst = Pixels;
1781 	do
1782 	{
1783 		vint32 Code = Streamer<vint8>(*Strm);
1784 		if (Code >= 0)
1785 		{
1786 			Count -= Code + 1;
1787 			while (Code-- >= 0)
1788 			{
1789 				*pDst = Streamer<vuint8>(*Strm);
1790 				*pDst = MIN(*pDst, MaxCol);
1791 				pDst++;
1792 			}
1793 		}
1794 		else if (Code != -128)
1795 		{
1796 			Code = 1 - Code;
1797 			Count -= Code;
1798 			vuint8 Val = Streamer<vuint8>(*Strm);
1799 			Val = MIN(Val, MaxCol);
1800 			while (Code-- > 0)
1801 			{
1802 				*pDst++ = Val;
1803 			}
1804 		}
1805 	}
1806 	while (Count > 0);
1807 
1808 	delete Strm;
1809 	Strm = NULL;
1810 	return Pixels;
1811 	unguard;
1812 }
1813 
1814 //==========================================================================
1815 //
1816 //	VFontChar2::GetPalette
1817 //
1818 //==========================================================================
1819 
GetPalette()1820 rgba_t* VFontChar2::GetPalette()
1821 {
1822 	guard(VFontChar2::GetPalette);
1823 	return Palette;
1824 	unguard;
1825 }
1826 
1827 //==========================================================================
1828 //
1829 //	VFontChar2::Unload
1830 //
1831 //==========================================================================
1832 
Unload()1833 void VFontChar2::Unload()
1834 {
1835 	guard(VFontChar2::Unload);
1836 	if (Pixels)
1837 	{
1838 		delete[] Pixels;
1839 		Pixels = NULL;
1840 	}
1841 	unguard;
1842 }
1843