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