2020-01-01 12:38:06 +00:00
/*---------------------------------------------------------------------------
* *
* * Copyright ( C ) 2017 Magnus Norddahl
* * Copyright ( C ) 2017 - 2020 Rachael Alexanderson
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2017-07-27 07:05:01 +00:00
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"
2019-12-31 15:26:23 +00:00
# include "r_videoscale.h"
2017-07-27 07:05:01 +00:00
2020-04-11 16:11:43 +00:00
# include "c_console.h"
2019-12-24 04:14:08 +00:00
# include "menu/menu.h"
2020-01-02 04:23:20 +00:00
# define NUMSCALEMODES countof(vScaleTable)
2018-07-22 01:39:35 +00:00
2020-01-02 11:47:03 +00:00
extern bool setsizeneeded , multiplayer , 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 ;
}
2019-12-31 15:26:23 +00:00
CVAR ( Bool , vid_scale_linear , 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 ;
2019-12-31 20:21:47 +00:00
if ( self < 0.2 | | self > 5.0 )
self = 1.0 ;
2018-07-22 16:05:16 +00:00
}
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
2020-01-04 20:05:26 +00:00
float ssx = ( float ) ( VID_MIN_UI_WIDTH ) / sx , ssy = ( float ) ( VID_MIN_UI_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 ;
2020-01-02 11:47:03 +00:00
bool specialUI = ( generic_ui | | ! ! log_vgafont | | ! ! dlg_vgafont | | ConsoleState ! = c_up | | multiplayer | |
2019-12-24 05:55:24 +00:00
( 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
2019-12-31 15:26:23 +00:00
{ bool isValid ; uint32_t ( * GetScaledWidth ) ( uint32_t Width , uint32_t Height ) ; uint32_t ( * GetScaledHeight ) ( uint32_t Width , uint32_t Height ) ; float pixelAspect ; bool isCustom ; } ;
2020-01-02 04:23:20 +00:00
v_ScaleTable vScaleTable [ ] =
2017-09-10 13:40:30 +00:00
{
2019-12-31 15:26:23 +00:00
{ true , [ ] ( 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
2019-12-31 15:41:30 +00:00
{ 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
2019-12-31 15:26:23 +00:00
{ true , [ ] ( 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 , [ ] ( 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 , [ ] ( 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 , [ ] ( 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 , [ ] ( 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 )
{
2020-01-04 10:59:26 +00:00
return ( x < 0 | | x > = int ( NUMSCALEMODES ) | | vScaleTable [ x ] . isValid = = false ) ;
2017-09-13 06:44:05 +00:00
}
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 ;
2019-12-31 15:26:23 +00:00
// always use linear if supersampling
int x = screen - > GetClientWidth ( ) , y = screen - > GetClientHeight ( ) ;
2020-01-01 06:28:42 +00:00
float aspectmult = ViewportPixelAspect ( ) ;
if ( aspectmult > 1.f )
aspectmult = 1.f / aspectmult ;
if ( ( ViewportScaledWidth ( x , y ) > ( x * aspectmult ) ) | | ( ViewportScaledHeight ( x , y ) > ( y * aspectmult ) ) )
2019-12-31 15:26:23 +00:00
return true ;
return vid_scale_linear ;
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 )
{
2019-12-31 15:26:23 +00:00
vid_scale_linear = atob ( argv [ 3 ] ) ;
2018-07-22 01:39:35 +00:00
if ( argv . argc ( ) > 4 )
{
2019-12-31 15:41:30 +00:00
vid_scale_custompixelaspect = ( float ) 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
{
2019-12-31 15:26:23 +00:00
Printf ( " Usage: vid_setscale <x> <y> [bool linear] [float pixel-shape] \n This command will create a custom viewport scaling mode. \n " ) ;
2018-07-22 01:39:35 +00:00
}
}
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 ;
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
2019-12-31 15:41:30 +00:00
vid_scalemode = 1 ;
2019-12-22 13:54:59 +00:00
vid_scalefactor = 1.0 ;
break ;
default : // Default method: use vid_scalefactor to achieve the result on a default scaling mode
2019-12-31 15:41:30 +00:00
vid_scalemode = 0 ;
2019-12-22 13:54:59 +00:00
vid_scalefactor = v_MinimumToFill ( screen - > GetClientWidth ( ) , screen - > GetClientHeight ( ) ) ;
break ;
}
}