2017-07-27 07:05:01 +00:00
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2017 Magnus Norddahl
2018-07-22 01:39:35 +00:00
// Copyright(C) 2018 Rachael Alexanderson
2017-07-27 07:05:01 +00:00
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
2017-07-27 07:37:01 +00:00
# include <math.h>
2017-07-27 07:05:01 +00:00
# include "c_dispatch.h"
# include "c_cvars.h"
2017-10-08 00:18:37 +00:00
# include "v_video.h"
2018-07-22 22:07:59 +00:00
# include "templates.h"
2017-07-27 07:05:01 +00:00
2019-12-24 04:14:08 +00:00
# include "console/c_console.h"
# include "menu/menu.h"
# define NUMSCALEMODES 8
2018-07-22 01:39:35 +00:00
2018-07-22 16:05:16 +00:00
extern bool setsizeneeded ;
2019-12-24 04:14:08 +00:00
extern bool generic_ui ;
2018-07-22 16:05:16 +00:00
2018-07-22 01:39:35 +00:00
EXTERN_CVAR ( Int , vid_aspect )
2019-12-24 04:14:08 +00:00
EXTERN_CVAR ( Bool , log_vgafont )
EXTERN_CVAR ( Bool , dlg_vgafont )
2019-04-07 10:49:54 +00:00
CUSTOM_CVAR ( Int , vid_scale_customwidth , VID_MIN_WIDTH , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2018-07-22 16:05:16 +00:00
{
2019-04-07 10:49:54 +00:00
if ( self < VID_MIN_WIDTH )
self = VID_MIN_WIDTH ;
2018-07-22 16:05:16 +00:00
setsizeneeded = true ;
}
2019-04-07 10:49:54 +00:00
CUSTOM_CVAR ( Int , vid_scale_customheight , VID_MIN_HEIGHT , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2018-07-22 16:05:16 +00:00
{
2019-04-07 10:49:54 +00:00
if ( self < VID_MIN_HEIGHT )
self = VID_MIN_HEIGHT ;
2018-07-22 16:05:16 +00:00
setsizeneeded = true ;
}
2018-07-22 01:39:35 +00:00
CVAR ( Bool , vid_scale_customlinear , false , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2019-12-31 14:41:42 +00:00
CUSTOM_CVAR ( Float , vid_scale_custompixelaspect , 1.0 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2018-07-22 16:05:16 +00:00
{
setsizeneeded = true ;
}
2017-09-10 13:40:30 +00:00
2017-09-10 13:50:49 +00:00
namespace
2017-09-10 13:40:30 +00:00
{
2019-12-24 04:14:08 +00:00
uint32_t min_width = VID_MIN_WIDTH ;
uint32_t min_height = VID_MIN_HEIGHT ;
2019-12-22 13:54:59 +00:00
float v_MinimumToFill ( uint32_t inwidth , uint32_t inheight )
2019-12-20 08:04:56 +00:00
{
// sx = screen x dimension, sy = same for y
2019-12-22 13:54:59 +00:00
float sx = ( float ) inwidth , sy = ( float ) inheight ;
2019-12-20 08:04:56 +00:00
static float lastsx = 0. , lastsy = 0. , result = 0. ;
if ( lastsx ! = sx | | lastsy ! = sy )
{
if ( sx < = 0. | | sy < = 0. )
return 1. ; // prevent x/0 error
// set absolute minimum scale to fill the entire screen but get as close to 640x400 as possible
2019-12-24 04:14:08 +00:00
float ssx = ( float ) ( min_width ) / sx , ssy = ( float ) ( min_height ) / sy ;
2019-12-20 08:04:56 +00:00
result = ( ssx < ssy ) ? ssy : ssx ;
lastsx = sx ;
lastsy = sy ;
}
return result ;
}
2019-12-22 13:54:59 +00:00
inline uint32_t v_mfillX ( uint32_t inwidth , uint32_t inheight )
2019-12-20 08:04:56 +00:00
{
2019-12-22 13:54:59 +00:00
return ( uint32_t ) ( ( float ) inwidth * v_MinimumToFill ( inwidth , inheight ) ) ;
2019-12-20 08:04:56 +00:00
}
2019-12-22 13:54:59 +00:00
inline uint32_t v_mfillY ( uint32_t inwidth , uint32_t inheight )
2019-12-20 08:04:56 +00:00
{
2019-12-22 13:54:59 +00:00
return ( uint32_t ) ( ( float ) inheight * v_MinimumToFill ( inwidth , inheight ) ) ;
2019-12-20 08:04:56 +00:00
}
2019-12-24 04:14:08 +00:00
inline void refresh_minimums ( )
{
// specialUI is tracking a state where high-res console fonts are actually required, and
// aren't actually rendered correctly in 320x200. this forces the game to revert to the 640x400
// minimum set in GZDoom 4.0.0, but only while those fonts are required.
static bool lastspecialUI = false ;
2019-12-24 05:55:24 +00:00
bool isInActualMenu = false ;
bool specialUI = ( generic_ui | | ! ! log_vgafont | | ! ! dlg_vgafont | | ConsoleState ! = c_up | |
( menuactive = = MENU_On & & CurrentMenu & & ! CurrentMenu - > IsKindOf ( " ConversationMenu " ) ) ) ;
2019-12-24 04:14:08 +00:00
if ( specialUI = = lastspecialUI )
return ;
lastspecialUI = specialUI ;
setsizeneeded = true ;
if ( ! specialUI )
{
min_width = VID_MIN_WIDTH ;
min_height = VID_MIN_HEIGHT ;
}
else
{
min_width = VID_MIN_UI_WIDTH ;
min_height = VID_MIN_UI_HEIGHT ;
}
}
2019-12-31 14:41:42 +00:00
// the odd formatting of this struct definition is meant to resemble a table header. set your tab stops to 4 when editing this file.
struct v_ScaleTable
{ bool isValid ; bool isLinear ; uint32_t ( * GetScaledWidth ) ( uint32_t Width , uint32_t Height ) ; uint32_t ( * GetScaledHeight ) ( uint32_t Width , uint32_t Height ) ; float pixelAspect ; bool isCustom ; } ;
2017-09-10 13:40:30 +00:00
v_ScaleTable vScaleTable [ NUMSCALEMODES ] =
{
2019-12-31 14:41:42 +00:00
{ true , false , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return Width ; } , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return Height ; } , 1.0f , false } , // 0 - Native
{ true , true , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return Width ; } , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return Height ; } , 1.0f , false } , // 1 - Native (Linear)
{ true , false , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return 640 ; } , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return 400 ; } , 1.2f , false } , // 2 - 640x400 (formerly 320x200)
{ true , true , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return 960 ; } , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return 600 ; } , 1.2f , false } , // 3 - 960x600 (formerly 640x400)
{ true , true , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return 1280 ; } , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return 800 ; } , 1.2f , false } , // 4 - 1280x800
{ true , true , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return vid_scale_customwidth ; } , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return vid_scale_customheight ; } , 1.0f , true } , // 5 - Custom
{ true , true , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return v_mfillX ( Width , Height ) ; } , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return v_mfillY ( Width , Height ) ; } , 1.0f , false } , // 6 - Minimum Scale to Fill Entire Screen
{ true , false , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return 320 ; } , [ ] ( uint32_t Width , uint32_t Height ) - > uint32_t { return 200 ; } , 1.2f , false } , // 7 - 320x200
2017-09-10 13:40:30 +00:00
} ;
2017-09-13 06:44:05 +00:00
bool isOutOfBounds ( int x )
{
return ( x < 0 | | x > = NUMSCALEMODES | | vScaleTable [ x ] . isValid = = false ) ;
}
2017-09-10 13:40:30 +00:00
}
2018-12-06 14:20:01 +00:00
void R_ShowCurrentScaling ( ) ;
2018-12-10 19:25:29 +00:00
CUSTOM_CVAR ( Float , vid_scalefactor , 1.0 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL )
2017-09-10 16:12:31 +00:00
{
2018-10-14 20:26:52 +00:00
setsizeneeded = true ;
2018-05-17 10:24:21 +00:00
if ( self < 0.05 | | self > 2.0 )
2017-09-10 16:12:31 +00:00
self = 1.0 ;
2018-12-06 14:20:01 +00:00
if ( self ! = 1.0 )
R_ShowCurrentScaling ( ) ;
2017-09-10 16:12:31 +00:00
}
2017-09-10 13:40:30 +00:00
CUSTOM_CVAR ( Int , vid_scalemode , 0 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2017-07-27 07:05:01 +00:00
{
2018-10-14 20:26:52 +00:00
setsizeneeded = true ;
2019-03-31 07:38:15 +00:00
if ( isOutOfBounds ( self ) )
2017-07-27 07:05:01 +00:00
self = 0 ;
}
2018-10-14 20:26:52 +00:00
CUSTOM_CVAR ( Bool , vid_cropaspect , false , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
{
setsizeneeded = true ;
}
2017-10-08 00:18:37 +00:00
2017-07-27 07:05:01 +00:00
bool ViewportLinearScale ( )
{
2017-09-13 06:44:05 +00:00
if ( isOutOfBounds ( vid_scalemode ) )
vid_scalemode = 0 ;
2018-07-22 01:39:35 +00:00
// hack - use custom scaling if in "custom" mode
if ( vScaleTable [ vid_scalemode ] . isCustom )
return vid_scale_customlinear ;
2017-09-10 16:12:31 +00:00
// vid_scalefactor > 1 == forced linear scale
return ( vid_scalefactor > 1.0 ) ? true : vScaleTable [ vid_scalemode ] . isLinear ;
2017-07-27 07:05:01 +00:00
}
2017-10-08 00:18:37 +00:00
int ViewportScaledWidth ( int width , int height )
2017-07-27 07:05:01 +00:00
{
2017-09-13 06:44:05 +00:00
if ( isOutOfBounds ( vid_scalemode ) )
vid_scalemode = 0 ;
2019-12-24 04:14:08 +00:00
refresh_minimums ( ) ;
2017-10-08 00:18:37 +00:00
if ( vid_cropaspect & & height > 0 )
2019-12-22 13:54:59 +00:00
{
2017-10-10 11:23:05 +00:00
width = ( ( float ) width / height > ActiveRatio ( width , height ) ) ? ( int ) ( height * ActiveRatio ( width , height ) ) : width ;
2019-12-22 13:54:59 +00:00
height = ( ( float ) width / height < ActiveRatio ( width , height ) ) ? ( int ) ( width / ActiveRatio ( width , height ) ) : height ;
}
2019-12-24 04:14:08 +00:00
return ( int ) MAX ( ( int32_t ) min_width , ( int32_t ) ( vid_scalefactor * vScaleTable [ vid_scalemode ] . GetScaledWidth ( width , height ) ) ) ;
2017-07-27 07:05:01 +00:00
}
2017-10-08 00:18:37 +00:00
int ViewportScaledHeight ( int width , int height )
2017-07-27 07:05:01 +00:00
{
2017-09-13 06:44:05 +00:00
if ( isOutOfBounds ( vid_scalemode ) )
vid_scalemode = 0 ;
2017-10-08 00:18:37 +00:00
if ( vid_cropaspect & & height > 0 )
2019-12-22 13:54:59 +00:00
{
2017-10-10 11:23:05 +00:00
height = ( ( float ) width / height < ActiveRatio ( width , height ) ) ? ( int ) ( width / ActiveRatio ( width , height ) ) : height ;
2019-12-22 13:54:59 +00:00
width = ( ( float ) width / height > ActiveRatio ( width , height ) ) ? ( int ) ( height * ActiveRatio ( width , height ) ) : width ;
}
2019-12-24 04:14:08 +00:00
return ( int ) MAX ( ( int32_t ) min_height , ( int32_t ) ( vid_scalefactor * vScaleTable [ vid_scalemode ] . GetScaledHeight ( width , height ) ) ) ;
2017-07-27 07:05:01 +00:00
}
2019-12-31 14:41:42 +00:00
float ViewportPixelAspect ( )
2017-07-27 07:05:01 +00:00
{
2017-09-13 06:44:05 +00:00
if ( isOutOfBounds ( vid_scalemode ) )
vid_scalemode = 0 ;
2018-07-22 01:39:35 +00:00
// hack - use custom scaling if in "custom" mode
if ( vScaleTable [ vid_scalemode ] . isCustom )
2019-12-31 14:41:42 +00:00
return vid_scale_custompixelaspect ;
return vScaleTable [ vid_scalemode ] . pixelAspect ;
2017-07-27 07:05:01 +00:00
}
2018-05-16 10:17:34 +00:00
void R_ShowCurrentScaling ( )
{
2018-07-22 02:27:43 +00:00
int x1 = screen - > GetClientWidth ( ) , y1 = screen - > GetClientHeight ( ) , x2 = ViewportScaledWidth ( x1 , y1 ) , y2 = ViewportScaledHeight ( x1 , y1 ) ;
Printf ( " Current vid_scalefactor: %f \n " , ( float ) ( vid_scalefactor ) ) ;
2018-05-17 23:25:32 +00:00
Printf ( " Real resolution: %i x %i \n Emulated resolution: %i x %i \n " , x1 , y1 , x2 , y2 ) ;
2018-05-16 10:17:34 +00:00
}
2018-05-17 23:25:32 +00:00
CCMD ( vid_showcurrentscaling )
{
R_ShowCurrentScaling ( ) ;
}
2018-05-16 10:17:34 +00:00
CCMD ( vid_scaletowidth )
{
if ( argv . argc ( ) > 1 )
2018-12-06 14:20:01 +00:00
{
// the following enables the use of ViewportScaledWidth to get the proper dimensions in custom scale modes
vid_scalefactor = 1 ;
vid_scalefactor = ( float ) ( ( double ) atof ( argv [ 1 ] ) / ViewportScaledWidth ( screen - > GetClientWidth ( ) , screen - > GetClientHeight ( ) ) ) ;
}
2018-05-16 10:17:34 +00:00
}
CCMD ( vid_scaletoheight )
{
if ( argv . argc ( ) > 1 )
2018-12-06 14:20:01 +00:00
{
vid_scalefactor = 1 ;
vid_scalefactor = ( float ) ( ( double ) atof ( argv [ 1 ] ) / ViewportScaledHeight ( screen - > GetClientWidth ( ) , screen - > GetClientHeight ( ) ) ) ;
}
2018-05-16 10:17:34 +00:00
}
2018-07-22 01:39:35 +00:00
inline bool atob ( char * I )
{
if ( stricmp ( I , " true " ) = = 0 | | stricmp ( I , " 1 " ) = = 0 )
return true ;
return false ;
}
CCMD ( vid_setscale )
{
if ( argv . argc ( ) > 2 )
{
vid_scale_customwidth = atoi ( argv [ 1 ] ) ;
vid_scale_customheight = atoi ( argv [ 2 ] ) ;
if ( argv . argc ( ) > 3 )
{
vid_scale_customlinear = atob ( argv [ 3 ] ) ;
if ( argv . argc ( ) > 4 )
{
2019-12-31 14:41:42 +00:00
vid_scale_custompixelaspect = atof ( argv [ 4 ] ) ;
2018-07-22 01:39:35 +00:00
}
}
vid_scalemode = 5 ;
2018-12-25 05:18:44 +00:00
vid_scalefactor = 1.0 ;
2018-07-22 01:39:35 +00:00
}
else
{
Printf ( " Usage: vid_setscale <x> <y> [bool linear] [bool long-pixel-shape] \n This command will create a custom viewport scaling mode. \n " ) ;
}
}
2019-12-22 13:54:59 +00:00
CCMD ( vid_scaletolowest )
{
uint32_t method = 0 ;
if ( argv . argc ( ) > 1 )
method = atoi ( argv [ 1 ] ) ;
switch ( method )
{
case 1 : // Method 1: set a custom video scaling
vid_scalemode = 5 ;
vid_scalefactor = 1.0 ;
vid_scale_customlinear = 1 ;
2019-12-31 14:41:42 +00:00
vid_scale_custompixelaspect = 1.0 ;
2019-12-22 13:54:59 +00:00
vid_scale_customwidth = v_mfillX ( screen - > GetClientWidth ( ) , screen - > GetClientHeight ( ) ) ;
vid_scale_customheight = v_mfillY ( screen - > GetClientWidth ( ) , screen - > GetClientHeight ( ) ) ;
break ;
case 2 : // Method 2: use the actual downscaling mode directly
vid_scalemode = 6 ;
vid_scalefactor = 1.0 ;
break ;
default : // Default method: use vid_scalefactor to achieve the result on a default scaling mode
vid_scalemode = 1 ;
vid_scalefactor = v_MinimumToFill ( screen - > GetClientWidth ( ) , screen - > GetClientHeight ( ) ) ;
break ;
}
}