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