1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: v_text.cpp 4640 2014-03-13 19:10:29Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 //	V_TEXT
21 //
22 //-----------------------------------------------------------------------------
23 
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 
29 #include "v_text.h"
30 
31 #include "i_system.h"
32 #include "i_video.h"
33 #include "v_video.h"
34 #include "hu_stuff.h"
35 #include "w_wad.h"
36 #include "z_zone.h"
37 #include "m_swap.h"
38 
39 #include "doomstat.h"
40 
41 EXTERN_CVAR(hud_scaletext)
42 
43 extern patch_t *hu_font[HU_FONTSIZE];
44 
45 
46 static byte *ConChars;
47 
48 extern byte *Ranges;
49 
V_TextScaleXAmount()50 int V_TextScaleXAmount()
51 {
52 	return int(hud_scaletext);
53 }
54 
V_TextScaleYAmount()55 int V_TextScaleYAmount()
56 {
57 	return int(hud_scaletext);
58 }
59 
60 // Convert the CONCHARS patch into the internal format used by
61 // the console font drawer.
V_InitConChars(byte transcolor)62 void V_InitConChars (byte transcolor)
63 {
64 	// Load the CONCHARS lump and convert it from patch_t format
65 	// to a raw linear byte buffer with a background color of 'transcolor'
66 	DCanvas* temp_screen = I_AllocateScreen(128, 128, 8);
67 
68 	patch_t* chars_patch = W_CachePatch("CONCHARS");
69 	temp_screen->Lock();
70 
71 	// fill with color 'transcolor'
72 	for (int y = 0; y < 128; y++)
73 		memset(temp_screen->buffer + temp_screen->pitch * y, transcolor, 128);
74 
75 	// paste the patch into the linear byte bufer
76 	temp_screen->DrawPatch(chars_patch, 0, 0);
77 
78 	ConChars = new byte[256*8*8*2];
79 
80 	byte* dest = ConChars;
81 
82 	for (int y = 0; y < 16; y++)
83 	{
84 		for (int x = 0; x < 16; x++)
85 		{
86 			const byte* source = temp_screen->buffer + x * 8 + (y * 8 * temp_screen->pitch);
87 			for (int z = 0; z < 8; z++)
88 			{
89 				for (int a = 0; a < 8; a++)
90 				{
91 					byte val = source[a];
92 					if (val == transcolor)
93 					{
94 						dest[a] = 0x00;
95 						dest[a + 8] = 0xff;
96 					}
97 					else
98 					{
99 						dest[a] = val;
100 						dest[a + 8] = 0x00;
101 					}
102 				}
103 
104 				dest += 16;
105 				source += temp_screen->pitch;
106 			}
107 		}
108 	}
109 
110 	temp_screen->Unlock();
111 
112 	I_FreeScreen(temp_screen);
113 }
114 
115 
116 //
117 // V_PrintStr
118 // Print a line of text using the console font
119 //
120 
121 extern "C" void STACK_ARGS PrintChar1P (DWORD *charimg, byte *dest, int screenpitch);
122 extern "C" void STACK_ARGS PrintChar2P_MMX (DWORD *charimg, byte *dest, int screenpitch);
123 
PrintStr(int x,int y,const char * s,int count) const124 void DCanvas::PrintStr (int x, int y, const char *s, int count) const
125 {
126 	const byte* str = (const byte*)s;
127 
128 	if (!buffer)
129 		return;
130 
131 	if (y > (height - 8) || y<0)
132 		return;
133 
134 	if (x < 0)
135 	{
136 		int skip;
137 
138 		skip = -(x - 7) / 8;
139 		x += skip * 8;
140 		if (count <= skip)
141 			return;
142 
143 		count -= skip;
144 		str += skip;
145 	}
146 
147 	x &= ~3;
148 	byte* destline = buffer + y * pitch;
149 
150 	while (count && x <= (width - 8))
151 	{
152 	    // john - tab 4 spaces
153 	    if (*str == '\t')
154 	    {
155 	        str++;
156 	        count--;
157 	        x += 8 * 4;
158 	        continue;
159 	    }
160 
161 		if (is8bit())
162 		{
163 			unsigned int* source = (unsigned int*)&ConChars[(*str) * 128];
164 			unsigned int* dest = (unsigned int*)(destline + x);
165 			for (int z = 0; z < 8; z++)
166 			{
167 				*dest = (*dest & source[2]) ^ source[0];
168 				dest++;
169 				*dest = (*dest & source[3]) ^ source[1];
170 				dest += (pitch >> 2) - 1;
171 				source += 4;
172 			}
173 		}
174 		else
175 		{
176 			byte* source = (byte*)&ConChars[(*str) * 128];
177 			argb_t* dest = (argb_t*)(destline + (x << 2));
178 			for (int z = 0; z < 8; z++)
179 			{
180 				for (int a = 0; a < 8; a++)
181 				{
182 					const argb_t mask = (source[a+8] << 24) | (source[a+8] << 16)
183 										| (source[a+8] << 8) | source[a+8];
184 
185 					argb_t color = V_Palette.shade(source[a]) & ~mask;
186 
187 					dest[a] = (dest[a] & mask) ^ color;
188 				}
189 
190 				dest += pitch >> 2;
191 				source += 16;
192 			}
193 		}
194 
195 		str++;
196 		count--;
197 		x += 8;
198 	}
199 }
200 
201 //
202 // V_PrintStr2
203 // Same as V_PrintStr but doubles the size of every character.
204 //
PrintStr2(int x,int y,const char * s,int count) const205 void DCanvas::PrintStr2 (int x, int y, const char *s, int count) const
206 {
207 	const byte *str = (const byte *)s;
208 	byte *temp;
209 	argb_t *charimg;
210 
211 	if (y > (height - 16))
212 		return;
213 
214 	if (x < 0)
215 	{
216 		int skip;
217 
218 		skip = -(x - 15) / 16;
219 		x += skip * 16;
220 		if (count <= skip)
221 		{
222 			return;
223 		}
224 		else
225 		{
226 			count -= skip;
227 			str += skip;
228 		}
229 	}
230 
231 	x &= ~3;
232 	temp = buffer + y * pitch;
233 
234 	while (count && x <= (width - 16))
235 	{
236 	    // john - tab 4 spaces
237         if (*str == '\t')
238 	    {
239 	        str++;
240 	        count--;
241 	        x += 16 * 4;
242 	        continue;
243 	    }
244 
245 		charimg = (argb_t *)&ConChars[(*str) * 128];
246 
247 		{
248 			int z;
249 			byte *buildmask, *buildbits, *image;
250 			unsigned int m1, s1;
251 			unsigned int *writepos;
252 
253 			writepos = (unsigned int *)(temp + x);
254 			buildbits = (byte *)&s1;
255 			buildmask = (byte *)&m1;
256 			image = (byte *)charimg;
257 
258 			for (z = 0; z < 8; z++)
259 			{
260 				buildmask[0] = buildmask[1] = image[8];
261 				buildmask[2] = buildmask[3] = image[9];
262 				buildbits[0] = buildbits[1] = image[0];
263 				buildbits[2] = buildbits[3] = image[1];
264 				writepos[0] = (writepos[0] & m1) ^ s1;
265 				writepos[pitch/4] = (writepos[pitch/4] & m1) ^ s1;
266 
267 				buildmask[0] = buildmask[1] = image[10];
268 				buildmask[2] = buildmask[3] = image[11];
269 				buildbits[0] = buildbits[1] = image[2];
270 				buildbits[2] = buildbits[3] = image[3];
271 				writepos[1] = (writepos[1] & m1) ^ s1;
272 				writepos[1+pitch/4] = (writepos[1+pitch/4] & m1) ^ s1;
273 
274 				buildmask[0] = buildmask[1] = image[12];
275 				buildmask[2] = buildmask[3] = image[13];
276 				buildbits[0] = buildbits[1] = image[4];
277 				buildbits[2] = buildbits[3] = image[5];
278 				writepos[2] = (writepos[2] & m1) ^ s1;
279 				writepos[2+pitch/4] = (writepos[2+pitch/4] & m1) ^ s1;
280 
281 				buildmask[0] = buildmask[1] = image[14];
282 				buildmask[2] = buildmask[3] = image[15];
283 				buildbits[0] = buildbits[1] = image[6];
284 				buildbits[2] = buildbits[3] = image[7];
285 				writepos[3] = (writepos[3] & m1) ^ s1;
286 				writepos[3+pitch/4] = (writepos[3+pitch/4] & m1) ^ s1;
287 
288 				writepos += pitch >> 1;
289 				image += 16;
290 			}
291 
292 		}
293 		str++;
294 		count--;
295 		x += 16;
296 	}
297 }
298 
299 //
300 // V_DrawText
301 //
302 // Write a string using the hu_font
303 //
304 
TextWrapper(EWrapperCode drawer,int normalcolor,int x,int y,const byte * string) const305 void DCanvas::TextWrapper (EWrapperCode drawer, int normalcolor, int x, int y, const byte *string) const
306 {
307 	int 		w;
308 	const byte *ch;
309 	int 		c;
310 	int 		cx;
311 	int 		cy;
312 	int			boldcolor;
313 
314 	if (normalcolor > NUM_TEXT_COLORS)
315 		normalcolor = CR_RED;
316 	boldcolor = normalcolor ? normalcolor - 1 : NUM_TEXT_COLORS - 1;
317 
318 	V_ColorMap = translationref_t(Ranges + normalcolor * 256);
319 
320 	ch = string;
321 	cx = x;
322 	cy = y;
323 
324 	while (1)
325 	{
326 		c = *ch++;
327 		if (!c)
328 			break;
329 
330 		if (c == 0x8a)
331 		{
332 			int newcolor = toupper(*ch++);
333 
334 			if (newcolor == 0)
335 			{
336 				return;
337 			}
338 			else if (newcolor == '-')
339 			{
340 				newcolor = normalcolor;
341 			}
342 			else if (newcolor >= 'A' && newcolor < 'A' + NUM_TEXT_COLORS)
343 			{
344 				newcolor -= 'A';
345 			}
346 			else if (newcolor == '+')
347 			{
348 				newcolor = boldcolor;
349 			}
350 			else
351 			{
352 				continue;
353 			}
354 			V_ColorMap = translationref_t(Ranges + newcolor * 256);
355 			continue;
356 		}
357 
358 		if (c == '\n')
359 		{
360 			cx = x;
361 			cy += 9;
362 			continue;
363 		}
364 
365 		c = toupper(c) - HU_FONTSTART;
366 		if (c < 0 || c>= HU_FONTSIZE)
367 		{
368 			cx += 4;
369 			continue;
370 		}
371 
372 		w = hu_font[c]->width();
373 		if (cx+w > width)
374 			break;
375 
376 		DrawWrapper (drawer, hu_font[c], cx, cy);
377 		cx+=w;
378 	}
379 }
380 
TextSWrapper(EWrapperCode drawer,int normalcolor,int x,int y,const byte * string) const381 void DCanvas::TextSWrapper (EWrapperCode drawer, int normalcolor, int x, int y, const byte *string) const
382 {
383 	TextSWrapper(drawer, normalcolor, x, y, string, CleanXfac, CleanYfac);
384 }
385 
TextSWrapper(EWrapperCode drawer,int normalcolor,int x,int y,const byte * string,int scalex,int scaley) const386 void DCanvas::TextSWrapper (EWrapperCode drawer, int normalcolor, int x, int y,
387 							const byte *string, int scalex, int scaley) const
388 {
389 	int 		w;
390 	const byte *ch;
391 	int 		c;
392 	int 		cx;
393 	int 		cy;
394 	int			boldcolor;
395 
396 	if (normalcolor > NUM_TEXT_COLORS)
397 		normalcolor = CR_RED;
398 	boldcolor = normalcolor ? normalcolor - 1 : NUM_TEXT_COLORS - 1;
399 
400 	V_ColorMap = translationref_t(Ranges + normalcolor * 256);
401 
402 	ch = string;
403 	cx = x;
404 	cy = y;
405 
406 	while (1)
407 	{
408 		c = *ch++;
409 		if (!c)
410 			break;
411 
412 		if (c == 0x8a)
413 		{
414 			int newcolor = toupper(*ch++);
415 
416 			if (newcolor == 0)
417 			{
418 				return;
419 			}
420 			else if (newcolor == '-')
421 			{
422 				newcolor = normalcolor;
423 			}
424 			else if (newcolor >= 'A' && newcolor < 'A' + NUM_TEXT_COLORS)
425 			{
426 				newcolor -= 'A';
427 			}
428 			else if (newcolor == '+')
429 			{
430 				newcolor = boldcolor;
431 			}
432 			else
433 			{
434 				continue;
435 			}
436 			V_ColorMap = translationref_t(Ranges + newcolor * 256);
437 			continue;
438 		}
439 
440 		if (c == '\n')
441 		{
442 			cx = x;
443 			cy += 9 * scalex;
444 			continue;
445 		}
446 
447 		c = toupper(c) - HU_FONTSTART;
448 		if (c < 0 || c>= HU_FONTSIZE)
449 		{
450 			cx += 4 * scaley;
451 			continue;
452 		}
453 
454 		w = hu_font[c]->width() * scalex;
455 		if (cx+w > width)
456 			break;
457 
458         DrawSWrapper (drawer, hu_font[c], cx, cy,
459                         hu_font[c]->width() * scalex,
460                         hu_font[c]->height() * scaley);
461 
462 		cx+=w;
463 	}
464 }
465 
466 //
467 // Find string width from hu_font chars
468 //
V_StringWidth(const byte * string)469 int V_StringWidth (const byte *string)
470 {
471 	int w = 0, c;
472 
473 	if(!string)
474 		return 0;
475 
476 	while (*string)
477 	{
478 		if (*string == 0x8a)
479 		{
480 			if (*(++string))
481 				string++;
482 			continue;
483 		}
484 		else
485 		{
486 			c = toupper((*string++) & 0x7f) - HU_FONTSTART;
487 			if (c < 0 || c >= HU_FONTSIZE)
488 			{
489 				w += 4;
490 			}
491 			else
492 			{
493 				w += hu_font[c]->width();
494 			}
495 		}
496 	}
497 
498 	return w;
499 }
500 
501 //
502 // Break long lines of text into multiple lines no longer than maxwidth pixels
503 //
breakit(brokenlines_t * line,const byte * start,const byte * string)504 static void breakit (brokenlines_t *line, const byte *start, const byte *string)
505 {
506 	// Leave out trailing white space
507 	while (string > start && isspace (*(string - 1)))
508 		string--;
509 
510 	line->string = new char[string - start + 1];
511 	strncpy (line->string, (char *)start, string - start);
512 	line->string[string - start] = 0;
513 	line->width = V_StringWidth (line->string);
514 }
515 
V_BreakLines(int maxwidth,const byte * string)516 brokenlines_t *V_BreakLines (int maxwidth, const byte *string)
517 {
518 	brokenlines_t lines[128];	// Support up to 128 lines (should be plenty)
519 
520 	const byte *space = NULL, *start = string;
521 	int i, c, w, nw;
522 	BOOL lastWasSpace = false;
523 
524 	i = w = 0;
525 
526 	while ( (c = *string++) ) {
527 		if (c == 0x8a) {
528 			if (*string)
529 				string++;
530 			continue;
531 		}
532 
533 		if (isspace(c)) {
534 			if (!lastWasSpace) {
535 				space = string - 1;
536 				lastWasSpace = true;
537 			}
538 		} else
539 			lastWasSpace = false;
540 
541 		c = toupper (c & 0x7f) - HU_FONTSTART;
542 
543 		if (c < 0 || c >= HU_FONTSIZE)
544 			nw = 4;
545 		else
546 			nw = hu_font[c]->width();
547 
548 		if (w + nw > maxwidth || c == '\n' - HU_FONTSTART) {	// Time to break the line
549 			if (!space)
550 				space = string - 1;
551 
552 			breakit (&lines[i], start, space);
553 
554 			i++;
555 			w = 0;
556 			lastWasSpace = false;
557 			start = space;
558 			space = NULL;
559 
560 			while (*start && isspace (*start) && *start != '\n')
561 				start++;
562 			if (*start == '\n')
563 				start++;
564 			else
565 				while (*start && isspace (*start))
566 					start++;
567 			string = start;
568 		} else
569 			w += nw;
570 	}
571 
572 	if (string - start > 1) {
573 		const byte *s = start;
574 
575 		while (s < string) {
576 			if (!isspace (*s++)) {
577 				breakit (&lines[i++], start, string);
578 				break;
579 			}
580 		}
581 	}
582 
583 	{
584 		// Make a copy of the broken lines and return them
585 		brokenlines_t *broken = new brokenlines_t[i+1];
586 
587 		memcpy (broken, lines, sizeof(brokenlines_t) * i);
588 		broken[i].string = NULL;
589 		broken[i].width = -1;
590 
591 		return broken;
592 	}
593 }
594 
V_FreeBrokenLines(brokenlines_t * lines)595 void V_FreeBrokenLines (brokenlines_t *lines)
596 {
597 	if (lines)
598 	{
599 		int i = 0;
600 
601 		while (lines[i].width != -1)
602 		{
603 			delete[] lines[i].string;
604 			lines[i].string = NULL;
605 			i++;
606 		}
607 		delete[] lines;
608 	}
609 }
610 
611 
612 VERSION_CONTROL (v_text_cpp, "$Id: v_text.cpp 4640 2014-03-13 19:10:29Z dr_sean $")
613 
614