1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 // Functions to draw patches (by post) directly to screen->
21 // Functions to blit a block to the screen->
22 //
23 //-----------------------------------------------------------------------------
24
25
26 #include <stdio.h>
27
28 #include "i_system.h"
29 #include "x86.h"
30 #include "i_video.h"
31 #include "r_state.h"
32
33 #include "doomdef.h"
34 #include "doomdata.h"
35 #include "doomstat.h"
36
37 #include "c_console.h"
38 #include "hu_stuff.h"
39
40 #include "m_argv.h"
41 #include "m_bbox.h"
42 #include "m_swap.h"
43
44 #include "i_video.h"
45 #include "v_video.h"
46 #include "v_text.h"
47
48 #include "w_wad.h"
49
50 #include "c_cvars.h"
51 #include "c_dispatch.h"
52 #include "cmdlib.h"
53 #include "gi.h"
54 #include "templates.h"
55 #include "sbar.h"
56 #include "hardware.h"
57 #include "r_data/r_translate.h"
58 #include "f_wipe.h"
59 #include "m_png.h"
60 #include "colormatcher.h"
61 #include "v_palette.h"
62 #include "r_sky.h"
63 #include "r_utility.h"
64 #include "r_renderer.h"
65 #include "menu/menu.h"
66 #include "r_data/voxels.h"
67
68
69 FRenderer *Renderer;
70
71 IMPLEMENT_ABSTRACT_CLASS (DCanvas)
72 IMPLEMENT_ABSTRACT_CLASS (DFrameBuffer)
73
74 #if defined(_DEBUG) && defined(_M_IX86)
75 #define DBGBREAK { __asm int 3 }
76 #else
77 #define DBGBREAK
78 #endif
79
80 class DDummyFrameBuffer : public DFrameBuffer
81 {
82 DECLARE_CLASS (DDummyFrameBuffer, DFrameBuffer);
83 public:
DDummyFrameBuffer(int width,int height)84 DDummyFrameBuffer (int width, int height)
85 : DFrameBuffer (0, 0)
86 {
87 Width = width;
88 Height = height;
89 }
Lock(bool buffered)90 bool Lock(bool buffered) { DBGBREAK; return false; }
Update()91 void Update() { DBGBREAK; }
GetPalette()92 PalEntry *GetPalette() { DBGBREAK; return NULL; }
GetFlashedPalette(PalEntry palette[256])93 void GetFlashedPalette(PalEntry palette[256]) { DBGBREAK; }
UpdatePalette()94 void UpdatePalette() { DBGBREAK; }
SetGamma(float gamma)95 bool SetGamma(float gamma) { Gamma = gamma; return true; }
SetFlash(PalEntry rgb,int amount)96 bool SetFlash(PalEntry rgb, int amount) { DBGBREAK; return false; }
GetFlash(PalEntry & rgb,int & amount)97 void GetFlash(PalEntry &rgb, int &amount) { DBGBREAK; }
GetPageCount()98 int GetPageCount() { DBGBREAK; return 0; }
IsFullscreen()99 bool IsFullscreen() { DBGBREAK; return 0; }
100 #ifdef _WIN32
PaletteChanged()101 void PaletteChanged() {}
QueryNewPalette()102 int QueryNewPalette() { return 0; }
Is8BitMode()103 bool Is8BitMode() { return false; }
104 #endif
105
106 float Gamma;
107 };
108 IMPLEMENT_ABSTRACT_CLASS (DDummyFrameBuffer)
109
110 // SimpleCanvas is not really abstract, but this macro does not
111 // try to generate a CreateNew() function.
112 IMPLEMENT_ABSTRACT_CLASS (DSimpleCanvas)
113
114 class FPaletteTester : public FTexture
115 {
116 public:
117 FPaletteTester ();
118
119 const BYTE *GetColumn(unsigned int column, const Span **spans_out);
120 const BYTE *GetPixels();
121 void Unload();
122 bool CheckModified();
123 void SetTranslation(int num);
124
125 protected:
126 BYTE Pixels[16*16];
127 int CurTranslation;
128 int WantTranslation;
129 static const Span DummySpan[2];
130
131 void MakeTexture();
132 };
133
134 const FTexture::Span FPaletteTester::DummySpan[2] = { { 0, 16 }, { 0, 0 } };
135
136 int DisplayWidth, DisplayHeight, DisplayBits;
137
138 FFont *SmallFont, *SmallFont2, *BigFont, *ConFont, *IntermissionFont;
139
140 extern "C" {
141 DWORD Col2RGB8[65][256];
142 DWORD *Col2RGB8_LessPrecision[65];
143 DWORD Col2RGB8_Inverse[65][256];
144 ColorTable32k RGB32k;
145 }
146
147 static DWORD Col2RGB8_2[63][256];
148
149 // [RH] The framebuffer is no longer a mere byte array.
150 // There's also only one, not four.
151 DFrameBuffer *screen;
152
153 CVAR (Int, vid_defwidth, 640, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
154 CVAR (Int, vid_defheight, 480, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
155 CVAR (Int, vid_defbits, 8, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
156 CVAR (Bool, vid_fps, false, 0)
157 CVAR (Bool, ticker, false, 0)
158 CVAR (Int, vid_showpalette, 0, 0)
159
160 CUSTOM_CVAR (Bool, vid_vsync, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
161 {
162 if (screen != NULL)
163 {
164 screen->SetVSync (*self);
165 }
166 }
167
168 CUSTOM_CVAR (Int, vid_refreshrate, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
169 {
170 if (screen != NULL)
171 {
172 screen->NewRefreshRate();
173 }
174 }
175
176 CUSTOM_CVAR (Float, dimamount, -1.f, CVAR_ARCHIVE)
177 {
178 if (self < 0.f && self != -1.f)
179 {
180 self = -1.f;
181 }
182 else if (self > 1.f)
183 {
184 self = 1.f;
185 }
186 }
187 CVAR (Color, dimcolor, 0xffd700, CVAR_ARCHIVE)
188
189 // [RH] Set true when vid_setmode command has been executed
190 bool setmodeneeded = false;
191 // [RH] Resolution to change to when setmodeneeded is true
192 int NewWidth, NewHeight, NewBits;
193
194
195 //
196 // V_MarkRect
197 //
V_MarkRect(int x,int y,int width,int height)198 void V_MarkRect (int x, int y, int width, int height)
199 {
200 }
201
202 DCanvas *DCanvas::CanvasChain = NULL;
203
204 //==========================================================================
205 //
206 // DCanvas Constructor
207 //
208 //==========================================================================
209
DCanvas(int _width,int _height)210 DCanvas::DCanvas (int _width, int _height)
211 {
212 // Init member vars
213 Buffer = NULL;
214 LockCount = 0;
215 Width = _width;
216 Height = _height;
217
218 // Add to list of active canvases
219 Next = CanvasChain;
220 CanvasChain = this;
221 }
222
223 //==========================================================================
224 //
225 // DCanvas Destructor
226 //
227 //==========================================================================
228
~DCanvas()229 DCanvas::~DCanvas ()
230 {
231 // Remove from list of active canvases
232 DCanvas *probe = CanvasChain, **prev;
233
234 prev = &CanvasChain;
235 probe = CanvasChain;
236
237 while (probe != NULL)
238 {
239 if (probe == this)
240 {
241 *prev = probe->Next;
242 break;
243 }
244 prev = &probe->Next;
245 probe = probe->Next;
246 }
247 }
248
249 //==========================================================================
250 //
251 // DCanvas :: IsValid
252 //
253 //==========================================================================
254
IsValid()255 bool DCanvas::IsValid ()
256 {
257 // A nun-subclassed DCanvas is never valid
258 return false;
259 }
260
261 //==========================================================================
262 //
263 // DCanvas :: FlatFill
264 //
265 // Fill an area with a texture. If local_origin is false, then the origin
266 // used for the wrapping is (0,0). Otherwise, (left,right) is used.
267 //
268 //==========================================================================
269
FlatFill(int left,int top,int right,int bottom,FTexture * src,bool local_origin)270 void DCanvas::FlatFill (int left, int top, int right, int bottom, FTexture *src, bool local_origin)
271 {
272 int w = src->GetWidth();
273 int h = src->GetHeight();
274
275 // Repeatedly draw the texture, left-to-right, top-to-bottom.
276 for (int y = local_origin ? top : (top / h * h); y < bottom; y += h)
277 {
278 for (int x = local_origin ? left : (left / w * w); x < right; x += w)
279 {
280 DrawTexture (src, x, y,
281 DTA_ClipLeft, left,
282 DTA_ClipRight, right,
283 DTA_ClipTop, top,
284 DTA_ClipBottom, bottom,
285 DTA_TopOffset, 0,
286 DTA_LeftOffset, 0,
287 TAG_DONE);
288 }
289 }
290 }
291
292 //==========================================================================
293 //
294 // DCanvas :: Dim
295 //
296 // Applies a colored overlay to the entire screen, with the opacity
297 // determined by the dimamount cvar.
298 //
299 //==========================================================================
300
Dim(PalEntry color)301 void DCanvas::Dim (PalEntry color)
302 {
303 PalEntry dimmer;
304 float amount;
305
306 if (dimamount >= 0)
307 {
308 dimmer = PalEntry(dimcolor);
309 amount = dimamount;
310 }
311 else
312 {
313 dimmer = gameinfo.dimcolor;
314 amount = gameinfo.dimamount;
315 }
316
317 if (gameinfo.gametype == GAME_Hexen && gamestate == GS_DEMOSCREEN)
318 { // On the Hexen title screen, the default dimming is not
319 // enough to make the menus readable.
320 amount = MIN<float> (1.f, amount*2.f);
321 }
322 // Add the cvar's dimming on top of the color passed to the function
323 if (color.a != 0)
324 {
325 float dim[4] = { color.r/255.f, color.g/255.f, color.b/255.f, color.a/255.f };
326 V_AddBlend (dimmer.r/255.f, dimmer.g/255.f, dimmer.b/255.f, amount, dim);
327 dimmer = PalEntry (BYTE(dim[0]*255), BYTE(dim[1]*255), BYTE(dim[2]*255));
328 amount = dim[3];
329 }
330 Dim (dimmer, amount, 0, 0, Width, Height);
331 }
332
333 //==========================================================================
334 //
335 // DCanvas :: Dim
336 //
337 // Applies a colored overlay to an area of the screen.
338 //
339 //==========================================================================
340
Dim(PalEntry color,float damount,int x1,int y1,int w,int h)341 void DCanvas::Dim (PalEntry color, float damount, int x1, int y1, int w, int h)
342 {
343 if (damount == 0.f)
344 return;
345
346 DWORD *bg2rgb;
347 DWORD fg;
348 int gap;
349 BYTE *spot;
350 int x, y;
351
352 if (x1 >= Width || y1 >= Height)
353 {
354 return;
355 }
356 if (x1 + w > Width)
357 {
358 w = Width - x1;
359 }
360 if (y1 + h > Height)
361 {
362 h = Height - y1;
363 }
364 if (w <= 0 || h <= 0)
365 {
366 return;
367 }
368
369 {
370 int amount;
371
372 amount = (int)(damount * 64);
373 bg2rgb = Col2RGB8[64-amount];
374
375 fg = (((color.r * amount) >> 4) << 20) |
376 ((color.g * amount) >> 4) |
377 (((color.b * amount) >> 4) << 10);
378 }
379
380 spot = Buffer + x1 + y1*Pitch;
381 gap = Pitch - w;
382 for (y = h; y != 0; y--)
383 {
384 for (x = w; x != 0; x--)
385 {
386 DWORD bg;
387
388 bg = bg2rgb[(*spot)&0xff];
389 bg = (fg+bg) | 0x1f07c1f;
390 *spot = RGB32k.All[bg&(bg>>15)];
391 spot++;
392 }
393 spot += gap;
394 }
395 }
396
397 //==========================================================================
398 //
399 // DCanvas :: GetScreenshotBuffer
400 //
401 // Returns a buffer containing the most recently displayed frame. The
402 // width and height of this buffer are the same as the canvas.
403 //
404 //==========================================================================
405
GetScreenshotBuffer(const BYTE * & buffer,int & pitch,ESSType & color_type)406 void DCanvas::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type)
407 {
408 Lock(true);
409 buffer = GetBuffer();
410 pitch = GetPitch();
411 color_type = SS_PAL;
412 }
413
414 //==========================================================================
415 //
416 // DCanvas :: ReleaseScreenshotBuffer
417 //
418 // Releases the buffer obtained through GetScreenshotBuffer. These calls
419 // must not be nested.
420 //
421 //==========================================================================
422
ReleaseScreenshotBuffer()423 void DCanvas::ReleaseScreenshotBuffer()
424 {
425 Unlock();
426 }
427
428 //==========================================================================
429 //
430 // V_GetColorFromString
431 //
432 // Passed a string of the form "#RGB", "#RRGGBB", "R G B", or "RR GG BB",
433 // returns a number representing that color. If palette is non-NULL, the
434 // index of the best match in the palette is returned, otherwise the
435 // RRGGBB value is returned directly.
436 //
437 //==========================================================================
438
V_GetColorFromString(const DWORD * palette,const char * cstr)439 int V_GetColorFromString (const DWORD *palette, const char *cstr)
440 {
441 int c[3], i, p;
442 char val[3];
443
444 val[2] = '\0';
445
446 // Check for HTML-style #RRGGBB or #RGB color string
447 if (cstr[0] == '#')
448 {
449 size_t len = strlen (cstr);
450
451 if (len == 7)
452 {
453 // Extract each eight-bit component into c[].
454 for (i = 0; i < 3; ++i)
455 {
456 val[0] = cstr[1 + i*2];
457 val[1] = cstr[2 + i*2];
458 c[i] = ParseHex (val);
459 }
460 }
461 else if (len == 4)
462 {
463 // Extract each four-bit component into c[], expanding to eight bits.
464 for (i = 0; i < 3; ++i)
465 {
466 val[1] = val[0] = cstr[1 + i];
467 c[i] = ParseHex (val);
468 }
469 }
470 else
471 {
472 // Bad HTML-style; pretend it's black.
473 c[2] = c[1] = c[0] = 0;
474 }
475 }
476 else
477 {
478 if (strlen(cstr) == 6)
479 {
480 char *p;
481 int color = strtol(cstr, &p, 16);
482 if (*p == 0)
483 {
484 // RRGGBB string
485 c[0] = (color & 0xff0000) >> 16;
486 c[1] = (color & 0xff00) >> 8;
487 c[2] = (color & 0xff);
488 }
489 else goto normal;
490 }
491 else
492 {
493 normal:
494 // Treat it as a space-delimited hexadecimal string
495 for (i = 0; i < 3; ++i)
496 {
497 // Skip leading whitespace
498 while (*cstr <= ' ' && *cstr != '\0')
499 {
500 cstr++;
501 }
502 // Extract a component and convert it to eight-bit
503 for (p = 0; *cstr > ' '; ++p, ++cstr)
504 {
505 if (p < 2)
506 {
507 val[p] = *cstr;
508 }
509 }
510 if (p == 0)
511 {
512 c[i] = 0;
513 }
514 else
515 {
516 if (p == 1)
517 {
518 val[1] = val[0];
519 }
520 c[i] = ParseHex (val);
521 }
522 }
523 }
524 }
525 if (palette)
526 return ColorMatcher.Pick (c[0], c[1], c[2]);
527 else
528 return MAKERGB(c[0], c[1], c[2]);
529 }
530
531 //==========================================================================
532 //
533 // V_GetColorStringByName
534 //
535 // Searches for the given color name in x11r6rgb.txt and returns an
536 // HTML-ish "#RRGGBB" string for it if found or the empty string if not.
537 //
538 //==========================================================================
539
V_GetColorStringByName(const char * name)540 FString V_GetColorStringByName (const char *name)
541 {
542 FMemLump rgbNames;
543 char *rgbEnd;
544 char *rgb, *endp;
545 int rgblump;
546 int c[3], step;
547 size_t namelen;
548
549 if (Wads.GetNumLumps()==0) return FString();
550
551 rgblump = Wads.CheckNumForName ("X11R6RGB");
552 if (rgblump == -1)
553 {
554 Printf ("X11R6RGB lump not found\n");
555 return FString();
556 }
557
558 rgbNames = Wads.ReadLump (rgblump);
559 rgb = (char *)rgbNames.GetMem();
560 rgbEnd = rgb + Wads.LumpLength (rgblump);
561 step = 0;
562 namelen = strlen (name);
563
564 while (rgb < rgbEnd)
565 {
566 // Skip white space
567 if (*rgb <= ' ')
568 {
569 do
570 {
571 rgb++;
572 } while (rgb < rgbEnd && *rgb <= ' ');
573 }
574 else if (step == 0 && *rgb == '!')
575 { // skip comment lines
576 do
577 {
578 rgb++;
579 } while (rgb < rgbEnd && *rgb != '\n');
580 }
581 else if (step < 3)
582 { // collect RGB values
583 c[step++] = strtoul (rgb, &endp, 10);
584 if (endp == rgb)
585 {
586 break;
587 }
588 rgb = endp;
589 }
590 else
591 { // Check color name
592 endp = rgb;
593 // Find the end of the line
594 while (endp < rgbEnd && *endp != '\n')
595 endp++;
596 // Back up over any whitespace
597 while (endp > rgb && *endp <= ' ')
598 endp--;
599 if (endp == rgb)
600 {
601 break;
602 }
603 size_t checklen = ++endp - rgb;
604 if (checklen == namelen && strnicmp (rgb, name, checklen) == 0)
605 {
606 FString descr;
607 descr.Format ("#%02x%02x%02x", c[0], c[1], c[2]);
608 return descr;
609 }
610 rgb = endp;
611 step = 0;
612 }
613 }
614 if (rgb < rgbEnd)
615 {
616 Printf ("X11R6RGB lump is corrupt\n");
617 }
618 return FString();
619 }
620
621 //==========================================================================
622 //
623 // V_GetColor
624 //
625 // Works like V_GetColorFromString(), but also understands X11 color names.
626 //
627 //==========================================================================
628
V_GetColor(const DWORD * palette,const char * str)629 int V_GetColor (const DWORD *palette, const char *str)
630 {
631 FString string = V_GetColorStringByName (str);
632 int res;
633
634 if (!string.IsEmpty())
635 {
636 res = V_GetColorFromString (palette, string);
637 }
638 else
639 {
640 res = V_GetColorFromString (palette, str);
641 }
642 return res;
643 }
644
645 //==========================================================================
646 //
647 // BuildTransTable
648 //
649 // Build the tables necessary for blending
650 //
651 //==========================================================================
652
BuildTransTable(const PalEntry * palette)653 static void BuildTransTable (const PalEntry *palette)
654 {
655 int r, g, b;
656
657 // create the RGB555 lookup table
658 for (r = 0; r < 32; r++)
659 for (g = 0; g < 32; g++)
660 for (b = 0; b < 32; b++)
661 RGB32k.RGB[r][g][b] = ColorMatcher.Pick ((r<<3)|(r>>2), (g<<3)|(g>>2), (b<<3)|(b>>2));
662
663 int x, y;
664
665 // create the swizzled palette
666 for (x = 0; x < 65; x++)
667 for (y = 0; y < 256; y++)
668 Col2RGB8[x][y] = (((palette[y].r*x)>>4)<<20) |
669 ((palette[y].g*x)>>4) |
670 (((palette[y].b*x)>>4)<<10);
671
672 // create the swizzled palette with the lsb of red and blue forced to 0
673 // (for green, a 1 is okay since it never gets added into)
674 for (x = 1; x < 64; x++)
675 {
676 Col2RGB8_LessPrecision[x] = Col2RGB8_2[x-1];
677 for (y = 0; y < 256; y++)
678 {
679 Col2RGB8_2[x-1][y] = Col2RGB8[x][y] & 0x3feffbff;
680 }
681 }
682 Col2RGB8_LessPrecision[0] = Col2RGB8[0];
683 Col2RGB8_LessPrecision[64] = Col2RGB8[64];
684
685 // create the inverse swizzled palette
686 for (x = 0; x < 65; x++)
687 for (y = 0; y < 256; y++)
688 {
689 Col2RGB8_Inverse[x][y] = (((((255-palette[y].r)*x)>>4)<<20) |
690 (((255-palette[y].g)*x)>>4) |
691 ((((255-palette[y].b)*x)>>4)<<10)) & 0x3feffbff;
692 }
693 }
694
695 //==========================================================================
696 //
697 // DCanvas :: CalcGamma
698 //
699 //==========================================================================
700
CalcGamma(float gamma,BYTE gammalookup[256])701 void DCanvas::CalcGamma (float gamma, BYTE gammalookup[256])
702 {
703 // I found this formula on the web at
704 // <http://panda.mostang.com/sane/sane-gamma.html>,
705 // but that page no longer exits.
706
707 double invgamma = 1.f / gamma;
708 int i;
709
710 for (i = 0; i < 256; i++)
711 {
712 gammalookup[i] = (BYTE)(255.0 * pow (i / 255.0, invgamma));
713 }
714 }
715
716 //==========================================================================
717 //
718 // DSimpleCanvas Constructor
719 //
720 // A simple canvas just holds a buffer in main memory.
721 //
722 //==========================================================================
723
DSimpleCanvas(int width,int height)724 DSimpleCanvas::DSimpleCanvas (int width, int height)
725 : DCanvas (width, height)
726 {
727 // Making the pitch a power of 2 is very bad for performance
728 // Try to maximize the number of cache lines that can be filled
729 // for each column drawing operation by making the pitch slightly
730 // longer than the width. The values used here are all based on
731 // empirical evidence.
732
733 if (width <= 640)
734 {
735 // For low resolutions, just keep the pitch the same as the width.
736 // Some speedup can be seen using the technique below, but the speedup
737 // is so marginal that I don't consider it worthwhile.
738 Pitch = width;
739 }
740 else
741 {
742 // If we couldn't figure out the CPU's L1 cache line size, assume
743 // it's 32 bytes wide.
744 if (CPU.DataL1LineSize == 0)
745 {
746 CPU.DataL1LineSize = 32;
747 }
748 // The Athlon and P3 have very different caches, apparently.
749 // I am going to generalize the Athlon's performance to all AMD
750 // processors and the P3's to all non-AMD processors. I don't know
751 // how smart that is, but I don't have a vast plethora of
752 // processors to test with.
753 if (CPU.bIsAMD)
754 {
755 Pitch = width + CPU.DataL1LineSize;
756 }
757 else
758 {
759 Pitch = width + MAX(0, CPU.DataL1LineSize - 8);
760 }
761 }
762 MemBuffer = new BYTE[Pitch * height];
763 memset (MemBuffer, 0, Pitch * height);
764 }
765
766 //==========================================================================
767 //
768 // DSimpleCanvas Destructor
769 //
770 //==========================================================================
771
~DSimpleCanvas()772 DSimpleCanvas::~DSimpleCanvas ()
773 {
774 if (MemBuffer != NULL)
775 {
776 delete[] MemBuffer;
777 MemBuffer = NULL;
778 }
779 }
780
781 //==========================================================================
782 //
783 // DSimpleCanvas :: IsValid
784 //
785 //==========================================================================
786
IsValid()787 bool DSimpleCanvas::IsValid ()
788 {
789 return (MemBuffer != NULL);
790 }
791
792 //==========================================================================
793 //
794 // DSimpleCanvas :: Lock
795 //
796 //==========================================================================
797
Lock(bool)798 bool DSimpleCanvas::Lock (bool)
799 {
800 if (LockCount == 0)
801 {
802 Buffer = MemBuffer;
803 }
804 LockCount++;
805 return false; // System surfaces are never lost
806 }
807
808 //==========================================================================
809 //
810 // DSimpleCanvas :: Unlock
811 //
812 //==========================================================================
813
Unlock()814 void DSimpleCanvas::Unlock ()
815 {
816 if (--LockCount <= 0)
817 {
818 LockCount = 0;
819 Buffer = NULL; // Enforce buffer access only between Lock/Unlock
820 }
821 }
822
823 //==========================================================================
824 //
825 // DFrameBuffer Constructor
826 //
827 // A frame buffer canvas is the most common and represents the image that
828 // gets drawn to the screen.
829 //
830 //==========================================================================
831
DFrameBuffer(int width,int height)832 DFrameBuffer::DFrameBuffer (int width, int height)
833 : DSimpleCanvas (width, height)
834 {
835 LastMS = LastSec = FrameCount = LastCount = LastTic = 0;
836 Accel2D = false;
837 }
838
839 //==========================================================================
840 //
841 // DFrameBuffer :: DrawRateStuff
842 //
843 // Draws the fps counter, dot ticker, and palette debug.
844 //
845 //==========================================================================
846
DrawRateStuff()847 void DFrameBuffer::DrawRateStuff ()
848 {
849 // Draws frame time and cumulative fps
850 if (vid_fps)
851 {
852 DWORD ms = I_FPSTime();
853 DWORD howlong = ms - LastMS;
854 if ((signed)howlong >= 0)
855 {
856 char fpsbuff[40];
857 int chars;
858 int rate_x;
859
860 chars = mysnprintf (fpsbuff, countof(fpsbuff), "%2u ms (%3u fps)", howlong, LastCount);
861 rate_x = Width - ConFont->StringWidth(&fpsbuff[0]);
862 Clear (rate_x, 0, Width, ConFont->GetHeight(), GPalette.BlackIndex, 0);
863 DrawText (ConFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0], TAG_DONE);
864
865 DWORD thisSec = ms/1000;
866 if (LastSec < thisSec)
867 {
868 LastCount = FrameCount / (thisSec - LastSec);
869 LastSec = thisSec;
870 FrameCount = 0;
871 }
872 FrameCount++;
873 }
874 LastMS = ms;
875 }
876
877 // draws little dots on the bottom of the screen
878 if (ticker)
879 {
880 int i = I_GetTime(false);
881 int tics = i - LastTic;
882 BYTE *buffer = GetBuffer();
883
884 LastTic = i;
885 if (tics > 20) tics = 20;
886
887 // Buffer can be NULL if we're doing hardware accelerated 2D
888 if (buffer != NULL)
889 {
890 buffer += (GetHeight()-1) * GetPitch();
891
892 for (i = 0; i < tics*2; i += 2) buffer[i] = 0xff;
893 for ( ; i < 20*2; i += 2) buffer[i] = 0x00;
894 }
895 else
896 {
897 for (i = 0; i < tics*2; i += 2) Clear(i, Height-1, i+1, Height, 255, 0);
898 for ( ; i < 20*2; i += 2) Clear(i, Height-1, i+1, Height, 0, 0);
899 }
900 }
901
902 // draws the palette for debugging
903 if (vid_showpalette)
904 {
905 // This used to just write the palette to the display buffer.
906 // With hardware-accelerated 2D, that doesn't work anymore.
907 // Drawing it as a texture does and continues to show how
908 // well the PalTex shader is working.
909 static FPaletteTester palette;
910
911 palette.SetTranslation(vid_showpalette);
912 DrawTexture(&palette, 0, 0,
913 DTA_DestWidth, 16*7,
914 DTA_DestHeight, 16*7,
915 DTA_Masked, false,
916 TAG_DONE);
917 }
918 }
919
920 //==========================================================================
921 //
922 // FPaleteTester Constructor
923 //
924 // This is just a 16x16 image with every possible color value.
925 //
926 //==========================================================================
927
FPaletteTester()928 FPaletteTester::FPaletteTester()
929 {
930 Width = 16;
931 Height = 16;
932 WidthBits = 4;
933 HeightBits = 4;
934 WidthMask = 15;
935 CurTranslation = 0;
936 WantTranslation = 1;
937 MakeTexture();
938 }
939
940 //==========================================================================
941 //
942 // FPaletteTester :: CheckModified
943 //
944 //==========================================================================
945
CheckModified()946 bool FPaletteTester::CheckModified()
947 {
948 return CurTranslation != WantTranslation;
949 }
950
951 //==========================================================================
952 //
953 // FPaletteTester :: SetTranslation
954 //
955 //==========================================================================
956
SetTranslation(int num)957 void FPaletteTester::SetTranslation(int num)
958 {
959 if (num >= 1 && num <= 9)
960 {
961 WantTranslation = num;
962 }
963 }
964
965 //==========================================================================
966 //
967 // FPaletteTester :: Unload
968 //
969 //==========================================================================
970
Unload()971 void FPaletteTester::Unload()
972 {
973 }
974
975 //==========================================================================
976 //
977 // FPaletteTester :: GetColumn
978 //
979 //==========================================================================
980
GetColumn(unsigned int column,const Span ** spans_out)981 const BYTE *FPaletteTester::GetColumn (unsigned int column, const Span **spans_out)
982 {
983 if (CurTranslation != WantTranslation)
984 {
985 MakeTexture();
986 }
987 column &= 15;
988 if (spans_out != NULL)
989 {
990 *spans_out = DummySpan;
991 }
992 return Pixels + column*16;
993 }
994
995 //==========================================================================
996 //
997 // FPaletteTester :: GetPixels
998 //
999 //==========================================================================
1000
GetPixels()1001 const BYTE *FPaletteTester::GetPixels ()
1002 {
1003 if (CurTranslation != WantTranslation)
1004 {
1005 MakeTexture();
1006 }
1007 return Pixels;
1008 }
1009
1010 //==========================================================================
1011 //
1012 // FPaletteTester :: MakeTexture
1013 //
1014 //==========================================================================
1015
MakeTexture()1016 void FPaletteTester::MakeTexture()
1017 {
1018 int i, j, k, t;
1019 BYTE *p;
1020
1021 t = WantTranslation;
1022 p = Pixels;
1023 k = 0;
1024 for (i = 0; i < 16; ++i)
1025 {
1026 for (j = 0; j < 16; ++j)
1027 {
1028 *p++ = (t > 1) ? translationtables[TRANSLATION_Standard][t - 2]->Remap[k] : k;
1029 k += 16;
1030 }
1031 k -= 255;
1032 }
1033 CurTranslation = t;
1034 }
1035
1036 //==========================================================================
1037 //
1038 // DFrameBuffer :: CopyFromBuff
1039 //
1040 // Copies pixels from main memory to video memory. This is only used by
1041 // DDrawFB.
1042 //
1043 //==========================================================================
1044
CopyFromBuff(BYTE * src,int srcPitch,int width,int height,BYTE * dest)1045 void DFrameBuffer::CopyFromBuff (BYTE *src, int srcPitch, int width, int height, BYTE *dest)
1046 {
1047 if (Pitch == width && Pitch == Width && srcPitch == width)
1048 {
1049 memcpy (dest, src, Width * Height);
1050 }
1051 else
1052 {
1053 for (int y = 0; y < height; y++)
1054 {
1055 memcpy (dest, src, width);
1056 dest += Pitch;
1057 src += srcPitch;
1058 }
1059 }
1060 }
1061
1062 //==========================================================================
1063 //
1064 // DFrameBuffer :: SetVSync
1065 //
1066 // Turns vertical sync on and off, if supported.
1067 //
1068 //==========================================================================
1069
SetVSync(bool vsync)1070 void DFrameBuffer::SetVSync (bool vsync)
1071 {
1072 }
1073
1074 //==========================================================================
1075 //
1076 // DFrameBuffer :: NewRefreshRate
1077 //
1078 // Sets the fullscreen display to the new refresh rate in vid_refreshrate,
1079 // if possible.
1080 //
1081 //==========================================================================
1082
NewRefreshRate()1083 void DFrameBuffer::NewRefreshRate ()
1084 {
1085 }
1086
1087 //==========================================================================
1088 //
1089 // DFrameBuffer :: SetBlendingRect
1090 //
1091 // Defines the area of the screen containing the 3D view.
1092 //
1093 //==========================================================================
1094
SetBlendingRect(int x1,int y1,int x2,int y2)1095 void DFrameBuffer::SetBlendingRect (int x1, int y1, int x2, int y2)
1096 {
1097 }
1098
1099 //==========================================================================
1100 //
1101 // DFrameBuffer :: Begin2D
1102 //
1103 // Signal that 3D rendering is complete, and the rest of the operations on
1104 // the canvas until Unlock() will be 2D ones.
1105 //
1106 //==========================================================================
1107
Begin2D(bool copy3d)1108 bool DFrameBuffer::Begin2D (bool copy3d)
1109 {
1110 return false;
1111 }
1112
1113 //==========================================================================
1114 //
1115 // DFrameBuffer :: DrawBlendingRect
1116 //
1117 // In hardware 2D modes, the blending rect needs to be drawn separately
1118 // from transferring the 3D scene to video memory, because the weapon
1119 // sprite is drawn on top of that.
1120 //
1121 //==========================================================================
1122
DrawBlendingRect()1123 void DFrameBuffer::DrawBlendingRect()
1124 {
1125 }
1126
1127 //==========================================================================
1128 //
1129 // DFrameBuffer :: CreateTexture
1130 //
1131 // Creates a native texture for a game texture, if supported.
1132 //
1133 //==========================================================================
1134
CreateTexture(FTexture * gametex,bool wrapping)1135 FNativeTexture *DFrameBuffer::CreateTexture(FTexture *gametex, bool wrapping)
1136 {
1137 return NULL;
1138 }
1139
1140 //==========================================================================
1141 //
1142 // DFrameBuffer :: CreatePalette
1143 //
1144 // Creates a native palette from a remap table, if supported.
1145 //
1146 //==========================================================================
1147
CreatePalette(FRemapTable * remap)1148 FNativePalette *DFrameBuffer::CreatePalette(FRemapTable *remap)
1149 {
1150 return NULL;
1151 }
1152
1153 //==========================================================================
1154 //
1155 // DFrameBuffer :: WipeStartScreen
1156 //
1157 // Grabs a copy of the screen currently displayed to serve as the initial
1158 // frame of a screen wipe. Also determines which screenwipe will be
1159 // performed.
1160 //
1161 //==========================================================================
1162
WipeStartScreen(int type)1163 bool DFrameBuffer::WipeStartScreen(int type)
1164 {
1165 return wipe_StartScreen(type);
1166 }
1167
1168 //==========================================================================
1169 //
1170 // DFrameBuffer :: WipeEndScreen
1171 //
1172 // Grabs a copy of the most-recently drawn, but not yet displayed, screen
1173 // to serve as the final frame of a screen wipe.
1174 //
1175 //==========================================================================
1176
WipeEndScreen()1177 void DFrameBuffer::WipeEndScreen()
1178 {
1179 wipe_EndScreen();
1180 Unlock();
1181 }
1182
1183 //==========================================================================
1184 //
1185 // DFrameBuffer :: WipeDo
1186 //
1187 // Draws one frame of a screenwipe. Should be called no more than 35
1188 // times per second. If called less than that, ticks indicates how many
1189 // ticks have passed since the last call.
1190 //
1191 //==========================================================================
1192
WipeDo(int ticks)1193 bool DFrameBuffer::WipeDo(int ticks)
1194 {
1195 Lock(true);
1196 return wipe_ScreenWipe(ticks);
1197 }
1198
1199 //==========================================================================
1200 //
1201 // DFrameBuffer :: WipeCleanup
1202 //
1203 //==========================================================================
1204
WipeCleanup()1205 void DFrameBuffer::WipeCleanup()
1206 {
1207 wipe_Cleanup();
1208 }
1209
1210 //===========================================================================
1211 //
1212 // Create texture hitlist
1213 //
1214 //===========================================================================
1215
GetHitlist(BYTE * hitlist)1216 void DFrameBuffer::GetHitlist(BYTE *hitlist)
1217 {
1218 BYTE *spritelist;
1219 int i;
1220
1221 spritelist = new BYTE[sprites.Size()];
1222
1223 // Precache textures (and sprites).
1224 memset (spritelist, 0, sprites.Size());
1225
1226 {
1227 AActor *actor;
1228 TThinkerIterator<AActor> iterator;
1229
1230 while ( (actor = iterator.Next ()) )
1231 spritelist[actor->sprite] = 1;
1232 }
1233
1234 for (i = (int)(sprites.Size () - 1); i >= 0; i--)
1235 {
1236 if (spritelist[i])
1237 {
1238 int j, k;
1239 for (j = 0; j < sprites[i].numframes; j++)
1240 {
1241 const spriteframe_t *frame = &SpriteFrames[sprites[i].spriteframes + j];
1242
1243 for (k = 0; k < 16; k++)
1244 {
1245 FTextureID pic = frame->Texture[k];
1246 if (pic.isValid())
1247 {
1248 hitlist[pic.GetIndex()] = FTextureManager::HIT_Sprite;
1249 }
1250 }
1251 }
1252 }
1253 }
1254
1255 delete[] spritelist;
1256
1257 for (i = numsectors - 1; i >= 0; i--)
1258 {
1259 hitlist[sectors[i].GetTexture(sector_t::floor).GetIndex()] =
1260 hitlist[sectors[i].GetTexture(sector_t::ceiling).GetIndex()] |= FTextureManager::HIT_Flat;
1261 }
1262
1263 for (i = numsides - 1; i >= 0; i--)
1264 {
1265 hitlist[sides[i].GetTexture(side_t::top).GetIndex()] =
1266 hitlist[sides[i].GetTexture(side_t::mid).GetIndex()] =
1267 hitlist[sides[i].GetTexture(side_t::bottom).GetIndex()] |= FTextureManager::HIT_Wall;
1268 }
1269
1270 // Sky texture is always present.
1271 // Note that F_SKY1 is the name used to
1272 // indicate a sky floor/ceiling as a flat,
1273 // while the sky texture is stored like
1274 // a wall texture, with an episode dependant
1275 // name.
1276
1277 if (sky1texture.isValid())
1278 {
1279 hitlist[sky1texture.GetIndex()] |= FTextureManager::HIT_Sky;
1280 }
1281 if (sky2texture.isValid())
1282 {
1283 hitlist[sky2texture.GetIndex()] |= FTextureManager::HIT_Sky;
1284 }
1285 }
1286
1287 //==========================================================================
1288 //
1289 // DFrameBuffer :: GameRestart
1290 //
1291 //==========================================================================
1292
GameRestart()1293 void DFrameBuffer::GameRestart()
1294 {
1295 }
1296
1297 //===========================================================================
1298 //
1299 //
1300 //
1301 //===========================================================================
1302
~FNativePalette()1303 FNativePalette::~FNativePalette()
1304 {
1305 }
1306
~FNativeTexture()1307 FNativeTexture::~FNativeTexture()
1308 {
1309 }
1310
CheckWrapping(bool wrapping)1311 bool FNativeTexture::CheckWrapping(bool wrapping)
1312 {
1313 return true;
1314 }
1315
CCMD(clean)1316 CCMD(clean)
1317 {
1318 Printf ("CleanXfac: %d\nCleanYfac: %d\n", CleanXfac, CleanYfac);
1319 }
1320
1321 //
1322 // V_SetResolution
1323 //
V_DoModeSetup(int width,int height,int bits)1324 bool V_DoModeSetup (int width, int height, int bits)
1325 {
1326 DFrameBuffer *buff = I_SetMode (width, height, screen);
1327 int cx1, cx2;
1328
1329 if (buff == NULL)
1330 {
1331 return false;
1332 }
1333
1334 screen = buff;
1335 GC::WriteBarrier(screen);
1336 screen->SetGamma (Gamma);
1337
1338 // Load fonts now so they can be packed into textures straight away,
1339 // if D3DFB is being used for the display.
1340 FFont::StaticPreloadFonts();
1341
1342 V_CalcCleanFacs(320, 200, width, height, &CleanXfac, &CleanYfac, &cx1, &cx2);
1343
1344 CleanWidth = width / CleanXfac;
1345 CleanHeight = height / CleanYfac;
1346 assert(CleanWidth >= 320);
1347 assert(CleanHeight >= 200);
1348
1349 if (width < 800 || width >= 960)
1350 {
1351 if (cx1 < cx2)
1352 {
1353 // Special case in which we don't need to scale down.
1354 CleanXfac_1 =
1355 CleanYfac_1 = cx1;
1356 }
1357 else
1358 {
1359 CleanXfac_1 = MAX(CleanXfac - 1, 1);
1360 CleanYfac_1 = MAX(CleanYfac - 1, 1);
1361 // On larger screens this is not enough so make sure it's at most 3/4 of the screen's width
1362 while (CleanXfac_1 * 320 > screen->GetWidth()*3/4 && CleanXfac_1 > 2)
1363 {
1364 CleanXfac_1--;
1365 CleanYfac_1--;
1366 }
1367 }
1368 CleanWidth_1 = width / CleanXfac_1;
1369 CleanHeight_1 = height / CleanYfac_1;
1370 }
1371 else // if the width is between 800 and 960 the ratio between the screensize and CleanXFac-1 becomes too large.
1372 {
1373 CleanXfac_1 = CleanXfac;
1374 CleanYfac_1 = CleanYfac;
1375 CleanWidth_1 = CleanWidth;
1376 CleanHeight_1 = CleanHeight;
1377 }
1378
1379
1380 DisplayWidth = width;
1381 DisplayHeight = height;
1382 DisplayBits = bits;
1383
1384 R_OldBlend = ~0;
1385 Renderer->OnModeSet();
1386
1387 M_RefreshModesList ();
1388
1389 return true;
1390 }
1391
V_CalcCleanFacs(int designwidth,int designheight,int realwidth,int realheight,int * cleanx,int * cleany,int * _cx1,int * _cx2)1392 void V_CalcCleanFacs (int designwidth, int designheight, int realwidth, int realheight, int *cleanx, int *cleany, int *_cx1, int *_cx2)
1393 {
1394 int ratio;
1395 int cwidth;
1396 int cheight;
1397 int cx1, cy1, cx2, cy2;
1398
1399 ratio = CheckRatio(realwidth, realheight);
1400 if (ratio & 4)
1401 {
1402 cwidth = realwidth;
1403 cheight = realheight * BaseRatioSizes[ratio][3] / 48;
1404 }
1405 else
1406 {
1407 cwidth = realwidth * BaseRatioSizes[ratio][3] / 48;
1408 cheight = realheight;
1409 }
1410 // Use whichever pair of cwidth/cheight or width/height that produces less difference
1411 // between CleanXfac and CleanYfac.
1412 cx1 = MAX(cwidth / designwidth, 1);
1413 cy1 = MAX(cheight / designheight, 1);
1414 cx2 = MAX(realwidth / designwidth, 1);
1415 cy2 = MAX(realheight / designheight, 1);
1416 if (abs(cx1 - cy1) <= abs(cx2 - cy2))
1417 { // e.g. 640x360 looks better with this.
1418 *cleanx = cx1;
1419 *cleany = cy1;
1420 }
1421 else
1422 { // e.g. 720x480 looks better with this.
1423 *cleanx = cx2;
1424 *cleany = cy2;
1425 }
1426
1427 if (*cleanx < *cleany)
1428 *cleany = *cleanx;
1429 else
1430 *cleanx = *cleany;
1431
1432 if (_cx1 != NULL) *_cx1 = cx1;
1433 if (_cx2 != NULL) *_cx2 = cx2;
1434 }
1435
SetResolution(int width,int height,int bits)1436 bool IVideo::SetResolution (int width, int height, int bits)
1437 {
1438 int oldwidth, oldheight;
1439 int oldbits;
1440
1441 if (screen)
1442 {
1443 oldwidth = SCREENWIDTH;
1444 oldheight = SCREENHEIGHT;
1445 oldbits = DisplayBits;
1446 }
1447 else
1448 { // Harmless if screen wasn't allocated
1449 oldwidth = width;
1450 oldheight = height;
1451 oldbits = bits;
1452 }
1453
1454 I_ClosestResolution (&width, &height, bits);
1455 if (!I_CheckResolution (width, height, bits))
1456 { // Try specified resolution
1457 if (!I_CheckResolution (oldwidth, oldheight, oldbits))
1458 { // Try previous resolution (if any)
1459 return false;
1460 }
1461 else
1462 {
1463 width = oldwidth;
1464 height = oldheight;
1465 bits = oldbits;
1466 }
1467 }
1468 return V_DoModeSetup (width, height, bits);
1469 }
1470
CCMD(vid_setmode)1471 CCMD (vid_setmode)
1472 {
1473 bool goodmode = false;
1474 int width = 0, height = SCREENHEIGHT;
1475 int bits = DisplayBits;
1476
1477 if (argv.argc() > 1)
1478 {
1479 width = atoi (argv[1]);
1480 if (argv.argc() > 2)
1481 {
1482 height = atoi (argv[2]);
1483 if (argv.argc() > 3)
1484 {
1485 bits = atoi (argv[3]);
1486 }
1487 }
1488 }
1489
1490 if (width && I_CheckResolution (width, height, bits))
1491 {
1492 goodmode = true;
1493 }
1494
1495 if (goodmode)
1496 {
1497 // The actual change of resolution will take place
1498 // near the beginning of D_Display().
1499 if (gamestate != GS_STARTUP)
1500 {
1501 setmodeneeded = true;
1502 NewWidth = width;
1503 NewHeight = height;
1504 NewBits = bits;
1505 }
1506 }
1507 else if (width)
1508 {
1509 Printf ("Unknown resolution %d x %d x %d\n", width, height, bits);
1510 }
1511 else
1512 {
1513 Printf ("Usage: vid_setmode <width> <height> <mode>\n");
1514 }
1515 }
1516
1517 //
1518 // V_Init
1519 //
1520
V_Init(bool restart)1521 void V_Init (bool restart)
1522 {
1523 const char *i;
1524 int width, height, bits;
1525
1526 atterm (V_Shutdown);
1527
1528 // [RH] Initialize palette management
1529 InitPalette ();
1530
1531 if (!restart)
1532 {
1533 width = height = bits = 0;
1534
1535 if ( (i = Args->CheckValue ("-width")) )
1536 width = atoi (i);
1537
1538 if ( (i = Args->CheckValue ("-height")) )
1539 height = atoi (i);
1540
1541 if ( (i = Args->CheckValue ("-bits")) )
1542 bits = atoi (i);
1543
1544 if (width == 0)
1545 {
1546 if (height == 0)
1547 {
1548 width = vid_defwidth;
1549 height = vid_defheight;
1550 }
1551 else
1552 {
1553 width = (height * 8) / 6;
1554 }
1555 }
1556 else if (height == 0)
1557 {
1558 height = (width * 6) / 8;
1559 }
1560
1561 if (bits == 0)
1562 {
1563 bits = vid_defbits;
1564 }
1565 screen = new DDummyFrameBuffer (width, height);
1566 }
1567 // Update screen palette when restarting
1568 else
1569 {
1570 PalEntry *palette = screen->GetPalette ();
1571 for (int i = 0; i < 256; ++i)
1572 *palette++ = GPalette.BaseColors[i];
1573 screen->UpdatePalette();
1574 }
1575
1576 BuildTransTable (GPalette.BaseColors);
1577 }
1578
V_Init2()1579 void V_Init2()
1580 {
1581 assert (screen->IsKindOf(RUNTIME_CLASS(DDummyFrameBuffer)));
1582 int width = screen->GetWidth();
1583 int height = screen->GetHeight();
1584 float gamma = static_cast<DDummyFrameBuffer *>(screen)->Gamma;
1585
1586 {
1587 DFrameBuffer *s = screen;
1588 screen = NULL;
1589 s->ObjectFlags |= OF_YesReallyDelete;
1590 delete s;
1591 }
1592
1593 I_InitGraphics();
1594 I_ClosestResolution (&width, &height, 8);
1595
1596 if (!Video->SetResolution (width, height, 8))
1597 I_FatalError ("Could not set resolution to %d x %d x %d", width, height, 8);
1598 else
1599 Printf ("Resolution: %d x %d\n", SCREENWIDTH, SCREENHEIGHT);
1600
1601 screen->SetGamma (gamma);
1602 Renderer->RemapVoxels();
1603 FBaseCVar::ResetColors ();
1604 C_NewModeAdjust();
1605 M_InitVideoModesMenu();
1606 V_SetBorderNeedRefresh();
1607 setsizeneeded = true;
1608 }
1609
V_Shutdown()1610 void V_Shutdown()
1611 {
1612 if (screen)
1613 {
1614 DFrameBuffer *s = screen;
1615 screen = NULL;
1616 s->ObjectFlags |= OF_YesReallyDelete;
1617 delete s;
1618 }
1619 V_ClearFonts();
1620 }
1621
EXTERN_CVAR(Bool,vid_tft)1622 EXTERN_CVAR (Bool, vid_tft)
1623 CUSTOM_CVAR (Bool, vid_nowidescreen, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
1624 {
1625 setsizeneeded = true;
1626 if (StatusBar != NULL)
1627 {
1628 StatusBar->ScreenSizeChanged();
1629 }
1630 }
1631
1632 CUSTOM_CVAR (Int, vid_aspect, 0, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
1633 {
1634 setsizeneeded = true;
1635 if (StatusBar != NULL)
1636 {
1637 StatusBar->ScreenSizeChanged();
1638 }
1639 }
1640
1641 // Tries to guess the physical dimensions of the screen based on the
1642 // screen's pixel dimensions. Can return:
1643 // 0: 4:3
1644 // 1: 16:9
1645 // 2: 16:10
1646 // 3: 17:10
1647 // 4: 5:4
CheckRatio(int width,int height,int * trueratio)1648 int CheckRatio (int width, int height, int *trueratio)
1649 {
1650 int fakeratio = -1;
1651 int ratio;
1652
1653 if ((vid_aspect >= 1) && (vid_aspect <= 5))
1654 {
1655 // [SP] User wants to force aspect ratio; let them.
1656 fakeratio = int(vid_aspect);
1657 if (fakeratio == 3)
1658 {
1659 fakeratio = 0;
1660 }
1661 else if (fakeratio == 5)
1662 {
1663 fakeratio = 3;
1664 }
1665 }
1666 if (vid_nowidescreen)
1667 {
1668 if (!vid_tft)
1669 {
1670 fakeratio = 0;
1671 }
1672 else
1673 {
1674 fakeratio = (height * 5/4 == width) ? 4 : 0;
1675 }
1676 }
1677 // If the size is approximately 16:9, consider it so.
1678 if (abs (height * 16/9 - width) < 10)
1679 {
1680 ratio = 1;
1681 }
1682 // Consider 17:10 as well.
1683 else if (abs (height * 17/10 - width) < 10)
1684 {
1685 ratio = 3;
1686 }
1687 // 16:10 has more variance in the pixel dimensions. Grr.
1688 else if (abs (height * 16/10 - width) < 60)
1689 {
1690 // 320x200 and 640x400 are always 4:3, not 16:10
1691 if ((width == 320 && height == 200) || (width == 640 && height == 400))
1692 {
1693 ratio = 0;
1694 }
1695 else
1696 {
1697 ratio = 2;
1698 }
1699 }
1700 // Unless vid_tft is set, 1280x1024 is 4:3, not 5:4.
1701 else if (height * 5/4 == width && vid_tft)
1702 {
1703 ratio = 4;
1704 }
1705 // Assume anything else is 4:3. (Which is probably wrong these days...)
1706 else
1707 {
1708 ratio = 0;
1709 }
1710
1711 if (trueratio != NULL)
1712 {
1713 *trueratio = ratio;
1714 }
1715 return (fakeratio >= 0) ? fakeratio : ratio;
1716 }
1717
1718 // First column: Base width
1719 // Second column: Base height (used for wall visibility multiplier)
1720 // Third column: Psprite offset (needed for "tallscreen" modes)
1721 // Fourth column: Width or height multiplier
1722
1723 // For widescreen aspect ratio x:y ...
1724 // base_width = 240 * x / y
1725 // multiplier = 320 / base_width
1726 // base_height = 200 * multiplier
1727 const int BaseRatioSizes[5][4] =
1728 {
1729 { 960, 600, 0, 48 }, // 4:3 320, 200, multiplied by three
1730 { 1280, 450, 0, 48*3/4 }, // 16:9 426.6667, 150, multiplied by three
1731 { 1152, 500, 0, 48*5/6 }, // 16:10 386, 166.6667, multiplied by three
1732 { 1224, 471, 0, 48*40/51 }, // 17:10 408, 156.8627, multiplied by three
1733 { 960, 640, (int)(6.5*FRACUNIT), 48*15/16 } // 5:4 320, 213.3333, multiplied by three
1734 };
1735
DumpAdapters()1736 void IVideo::DumpAdapters ()
1737 {
1738 Printf("Multi-monitor support unavailable.\n");
1739 }
1740
CCMD(vid_listadapters)1741 CCMD(vid_listadapters)
1742 {
1743 if (Video != NULL)
1744 Video->DumpAdapters();
1745 }
1746