From db5efddf12ca22b6655f26639bc43d12824e7a70 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 15 Mar 2020 09:27:02 +0100 Subject: [PATCH] - implemented better aspect ratio control for fullscreen images. Due to backwards compatibility needs and for flexibility this needs to be controlled by a gameinfo setting (fullscreenautoaspect): 0: Treat all images as having an aspect ratio of 4:3, this is the default for compatibility reasons 1: Scale all images to fit the screen, i.e. either pillarbox or letterbox them. 2: Scale all images to fill the screen. 3: Scale all images so that the center 4:3 area is always fully visible. This is the recommended mode for 16:9 images designed to be shown with the sides being cropped on narrower displays. A new DTA_ tag - DTA_FullscreenEx also exists which allows specifying the scale mode directly --- src/gamedata/gi.cpp | 2 ++ src/gamedata/gi.h | 1 + src/rendering/2d/v_draw.cpp | 58 +++++++++++++++++++++++++++++- src/rendering/v_video.cpp | 71 ------------------------------------- src/rendering/v_video.h | 8 ++--- 5 files changed, 64 insertions(+), 76 deletions(-) diff --git a/src/gamedata/gi.cpp b/src/gamedata/gi.cpp index a952313a5d..5076938c58 100644 --- a/src/gamedata/gi.cpp +++ b/src/gamedata/gi.cpp @@ -116,6 +116,7 @@ const char* GameInfoBorders[] = NULL }; + #define GAMEINFOKEY_CSTRING(key, variable, length) \ else if(nextKey.CompareNoCase(variable) == 0) \ { \ @@ -382,6 +383,7 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_DOUBLE(telefogheight, "telefogheight") GAMEINFOKEY_DOUBLE(gibfactor, "gibfactor") GAMEINFOKEY_INT(defKickback, "defKickback") + GAMEINFOKEY_INT(fullscreenautoaspect, "fullscreenautoaspect") GAMEINFOKEY_STRING(SkyFlatName, "SkyFlatName") GAMEINFOKEY_STRING(translator, "translator") GAMEINFOKEY_COLOR(pickupcolor, "pickupcolor") diff --git a/src/gamedata/gi.h b/src/gamedata/gi.h index d1dc534e3c..84e383135f 100644 --- a/src/gamedata/gi.h +++ b/src/gamedata/gi.h @@ -204,6 +204,7 @@ struct gameinfo_t int berserkpic; double normforwardmove[2]; double normsidemove[2]; + int fullscreenautoaspect = 3; const char *GetFinalePage(unsigned int num) const; }; diff --git a/src/rendering/2d/v_draw.cpp b/src/rendering/2d/v_draw.cpp index 82d87b543b..1964bf3ae5 100644 --- a/src/rendering/2d/v_draw.cpp +++ b/src/rendering/2d/v_draw.cpp @@ -375,8 +375,48 @@ bool DFrameBuffer::SetTextureParms(DrawParms *parms, FTexture *img, double xx, d break; case DTA_Fullscreen: + case DTA_FullscreenEx: + { + double aspect; + double srcwidth = img->GetDisplayWidthDouble(); + double srcheight = img->GetDisplayHeightDouble(); + int autoaspect = parms->fsscalemode; + aspect = autoaspect == 0 || (srcwidth == 320 && srcheight == 200) || (srcwidth == 640 && srcheight == 200)? 1.333 : srcwidth / srcheight; parms->x = parms->y = 0; - break; + parms->keepratio = true; + auto screenratio = ActiveRatio(GetWidth(), GetHeight()); + if (autoaspect == 3) + { + if (screenratio >= aspect || aspect < 1.4) autoaspect = 1; // screen is wider than the image -> pillarbox it. 4:3 images must also be pillarboxes if the screen is taller than the image + else if (screenratio > 1.32) autoaspect = 2; // on anything 4:3 and wider crop the sides of the image. + else + { + // special case: Crop image to 4:3 and then letterbox this. This avoids too much cropping on narrow windows. + double width4_3 = srcheight * (4. / 3.); + parms->destwidth = (double)GetWidth() * srcwidth / width4_3; + parms->destheight = GetHeight() * screenratio * (3. / 4.); // use 4:3 for the image + parms->y = (GetHeight() - parms->destheight) / 2; + parms->x = -(srcwidth - width4_3) / 2; + return false; // Do not call VirtualToRealCoords for this! + } + } + + if ((screenratio > aspect) ^ (autoaspect == 2)) + { + // pillarboxed or vertically cropped (i.e. scale to height) + parms->destheight = GetHeight(); + parms->destwidth =GetWidth() * aspect / screenratio; + parms->x = (GetWidth() - parms->destwidth) / 2; + } + else + { + // letterboxed or horizontally cropped (i.e. scale to width) + parms->destwidth = GetWidth(); + parms->destheight = GetHeight() * screenratio / aspect; + parms->y = (GetHeight() - parms->destheight) / 2; + } + return false; // Do not call VirtualToRealCoords for this! + } case DTA_HUDRules: case DTA_HUDRulesC: @@ -667,12 +707,28 @@ bool DFrameBuffer::ParseDrawTextureTags(FTexture *img, double x, double y, uint3 break; case DTA_Fullscreen: + boolval = ListGetInt(tags); if (boolval) { assert(fortext == false); if (img == NULL) return false; parms->cleanmode = DTA_Fullscreen; + parms->fsscalemode = (uint8_t)gameinfo.fullscreenautoaspect; + parms->virtWidth = img->GetDisplayWidthDouble(); + parms->virtHeight = img->GetDisplayHeightDouble(); + } + break; + + case DTA_FullscreenEx: + + intval = ListGetInt(tags); + if (intval >= 0 && intval <= 3) + { + assert(fortext == false); + if (img == NULL) return false; + parms->cleanmode = DTA_Fullscreen; + parms->fsscalemode = (uint8_t)intval; parms->virtWidth = img->GetDisplayWidthDouble(); parms->virtHeight = img->GetDisplayHeightDouble(); } diff --git a/src/rendering/v_video.cpp b/src/rendering/v_video.cpp index efb35bbe9b..9b61eb2438 100644 --- a/src/rendering/v_video.cpp +++ b/src/rendering/v_video.cpp @@ -702,51 +702,6 @@ DEFINE_ACTION_FUNCTION(_Screen, GetAspectRatio) ACTION_RETURN_FLOAT(ActiveRatio(screen->GetWidth(), screen->GetHeight(), nullptr)); } -// Tries to guess the physical dimensions of the screen based on the -// screen's pixel dimensions. Can return: -// 0: 4:3 -// 1: 16:9 -// 2: 16:10 -// 3: 17:10 -// 4: 5:4 -// 5: 17:10 (redundant, never returned) -// 6: 21:9 -int CheckRatio (int width, int height, int *trueratio) -{ - float aspect = width / (float)height; - - static std::pair ratioTypes[] = - { - { 21 / 9.0f , 6 }, - { 16 / 9.0f , 1 }, - { 17 / 10.0f , 3 }, - { 16 / 10.0f , 2 }, - { 4 / 3.0f , 0 }, - { 5 / 4.0f , 4 }, - { 0.0f, 0 } - }; - - int ratio = ratioTypes[0].second; - float distance = fabs(ratioTypes[0].first - aspect); - for (int i = 1; ratioTypes[i].first != 0.0f; i++) - { - float d = fabs(ratioTypes[i].first - aspect); - if (d < distance) - { - ratio = ratioTypes[i].second; - distance = d; - } - } - - int fakeratio = ActiveFakeRatio(width, height); - if (fakeratio == -1) - fakeratio = ratio; - - if (trueratio) - *trueratio = ratio; - return fakeratio; -} - int AspectBaseWidth(float aspect) { return (int)round(240.0f * aspect * 3.0f); @@ -781,32 +736,6 @@ bool AspectTallerThanWide(float aspect) return aspect < 1.333f; } -void ScaleWithAspect (int &w, int &h, int Width, int Height) -{ - int resRatio = CheckRatio (Width, Height); - int screenRatio; - CheckRatio (w, h, &screenRatio); - if (resRatio == screenRatio) - return; - - double yratio; - switch(resRatio) - { - case 0: yratio = 4./3.; break; - case 1: yratio = 16./9.; break; - case 2: yratio = 16./10.; break; - case 3: yratio = 17./10.; break; - case 4: yratio = 5./4.; break; - case 6: yratio = 21./9.; break; - default: return; - } - double y = w/yratio; - if (y > h) - w = static_cast(h * yratio); - else - h = static_cast(y); -} - CCMD(vid_setsize) { if (argv.argc() < 3) diff --git a/src/rendering/v_video.h b/src/rendering/v_video.h index d1ecbc5d5c..479b5d145a 100644 --- a/src/rendering/v_video.h +++ b/src/rendering/v_video.h @@ -240,6 +240,8 @@ enum DTA_Burn, // activates the burn shader for this element DTA_Spacing, // Strings only: Additional spacing between characters DTA_Monospace, // Fonts only: Use a fixed distance between characters. + + DTA_FullscreenEx, }; enum EMonospacing : int @@ -303,9 +305,10 @@ struct DrawParms int maxstrlen; bool fortext; bool virtBottom; + bool burn; + uint8_t fsscalemode; double srcx, srcy; double srcwidth, srcheight; - bool burn; }; struct Va_List @@ -621,8 +624,6 @@ FString V_GetColorStringByName (const char *name, FScriptPosition *sc = nullptr) int V_GetColor (const uint32_t *palette, const char *str, FScriptPosition *sc = nullptr); int V_GetColor(const uint32_t *palette, FScanner &sc); -int CheckRatio (int width, int height, int *trueratio=NULL); -static inline int CheckRatio (double width, double height) { return CheckRatio(int(width), int(height)); } inline bool IsRatioWidescreen(int ratio) { return (ratio & 3) != 0; } float ActiveRatio (int width, int height, float *trueratio = NULL); @@ -633,7 +634,6 @@ int AspectBaseHeight(float aspect); double AspectPspriteOffset(float aspect); int AspectMultiplier(float aspect); bool AspectTallerThanWide(float aspect); -void ScaleWithAspect(int &w, int &h, int Width, int Height); int GetUIScale(int altval); int GetConScale(int altval);