Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for scaled resolutions #6516

Merged
merged 25 commits into from
Feb 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
207 changes: 120 additions & 87 deletions src/engine/screen.cpp

Large diffs are not rendered by default.

59 changes: 52 additions & 7 deletions src/engine/screen.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2020 - 2022 *
* Copyright (C) 2020 - 2023 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand All @@ -23,6 +23,7 @@
#include <cstdint>
#include <memory>
#include <string>
#include <tuple>
#include <vector>

#include "image.h"
Expand All @@ -33,6 +34,40 @@ namespace fheroes2
class Cursor;
class Display;

struct ResolutionInfo
{
ResolutionInfo() = default;

ResolutionInfo( const int32_t width_, const int32_t height_, const int32_t scale_ )
: width( width_ )
, height( height_ )
, scale( scale_ )
{
// Do nothing.
}

bool operator==( const ResolutionInfo & info ) const
{
return width == info.width && height == info.height && scale == info.scale;
}

bool operator!=( const ResolutionInfo & info ) const
{
return !operator==( info );
}

bool operator<( const ResolutionInfo & info ) const
{
return std::tie( width, height, scale ) < std::tie( info.width, info.height, info.scale );
}
ihhub marked this conversation as resolved.
Show resolved Hide resolved

int32_t width{ 0 };

int32_t height{ 0 };

int32_t scale{ 1 };
};

class BaseRenderEngine
{
public:
Expand All @@ -51,7 +86,7 @@ namespace fheroes2
return _isFullScreen;
}

virtual std::vector<Size> getAvailableResolutions() const
virtual std::vector<ResolutionInfo> getAvailableResolutions() const
{
return {};
}
Expand All @@ -66,12 +101,12 @@ namespace fheroes2
// Do nothing.
}

virtual fheroes2::Rect getActiveWindowROI() const
virtual Rect getActiveWindowROI() const
{
return {};
}

virtual fheroes2::Size getCurrentScreenResolution() const
virtual Size getCurrentScreenResolution() const
{
return {};
}
Expand Down Expand Up @@ -109,7 +144,7 @@ namespace fheroes2
// Do nothing.
}

virtual bool allocate( int32_t &, int32_t &, bool )
virtual bool allocate( ResolutionInfo & /*unused*/, bool /*unused*/ )
ihhub marked this conversation as resolved.
Show resolved Hide resolved
ihhub marked this conversation as resolved.
Show resolved Hide resolved
{
return false;
}
Expand Down Expand Up @@ -159,8 +194,11 @@ namespace fheroes2
// Update the area which will be rendered on the next render() call.
void updateNextRenderRoi( const Rect & roi );

// Do not call this method. It serves as a patch over the basic class.
void resize( int32_t width_, int32_t height_ ) override;

void setResolution( ResolutionInfo info );

bool isDefaultSize() const
{
return width() == DEFAULT_WIDTH && height() == DEFAULT_HEIGHT;
Expand All @@ -186,6 +224,11 @@ namespace fheroes2
// nullptr input parameter is used to reset pallette to default one.
void changePalette( const uint8_t * palette = nullptr, const bool forceDefaultPaletteUpdate = false ) const;

int32_t scale() const
{
return _scale;
}

friend BaseRenderEngine & engine();
friend Cursor & cursor();

Expand All @@ -200,6 +243,8 @@ namespace fheroes2
// Previous area drawn on the screen.
Rect _prevRoi;

int32_t _scale;

// Only for cases of direct drawing on rendered 8-bit image.
void linkRenderSurface( uint8_t * surface )
{
Expand Down Expand Up @@ -229,9 +274,9 @@ namespace fheroes2

bool isFocusActive() const;

virtual void update( const fheroes2::Image & image, int32_t offsetX, int32_t offsetY )
virtual void update( const Image & image, int32_t offsetX, int32_t offsetY )
{
_image = fheroes2::Sprite( image, offsetX, offsetY );
_image = Sprite( image, offsetX, offsetY );
}

void setPosition( int32_t x, int32_t y )
Expand Down
10 changes: 8 additions & 2 deletions src/fheroes2/dialog/dialog_graphics_settings.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2022 *
* Copyright (C) 2022 - 2023 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand Down Expand Up @@ -64,7 +64,13 @@ namespace
void drawResolution( const fheroes2::Rect & optionRoi )
{
const fheroes2::Display & display = fheroes2::Display::instance();
std::string resolutionName = std::to_string( display.width() ) + 'x' + std::to_string( display.height() );
std::string resolutionName;
if ( display.scale() > 1 ) {
resolutionName = std::to_string( display.width() ) + 'x' + std::to_string( display.height() ) + " (x" + std::to_string( display.scale() ) + ')';
}
else {
resolutionName = std::to_string( display.width() ) + 'x' + std::to_string( display.height() );
}

fheroes2::drawOption( optionRoi, fheroes2::AGG::GetICN( ICN::SPANEL, Settings::Get().isEvilInterfaceEnabled() ? 17 : 16 ), _( "Resolution" ),
std::move( resolutionName ), fheroes2::UiOptionTextWidth::TWO_ELEMENTS_ROW );
Expand Down
97 changes: 62 additions & 35 deletions src/fheroes2/dialog/dialog_resolution.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2020 - 2022 *
* Copyright (C) 2020 - 2023 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand All @@ -20,8 +20,8 @@

#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "agg_image.h"
Expand All @@ -36,7 +36,6 @@
#include "localevent.h"
#include "math_base.h"
#include "screen.h"
#include "text.h"
#include "translations.h"
#include "ui_button.h"
#include "ui_dialog.h"
Expand All @@ -46,29 +45,49 @@

namespace
{
const int editBoxLength = 266;
const int32_t editBoxLength = 266;
const int32_t resolutionItemHeight = 19;
// TODO: this is a hack over partially incorrect text height calculation. Fix it later together with the Text classes.
const int32_t textOffsetYCorrection = 6;
const std::string middleText = " x ";

std::string GetResolutionString( const fheroes2::Size & resolution )
std::pair<std::string, std::string> getResolutionStrings( const fheroes2::ResolutionInfo & resolution )
{
return std::to_string( resolution.width ) + " x " + std::to_string( resolution.height );
if ( resolution.scale > 1 ) {
return std::make_pair( std::to_string( resolution.width ), std::to_string( resolution.height ) + " (x" + std::to_string( resolution.scale ) + ")" );
}

return std::make_pair( std::to_string( resolution.width ), std::to_string( resolution.height ) );
}

class ResolutionList : public Interface::ListBox<fheroes2::Size>
class ResolutionList : public Interface::ListBox<fheroes2::ResolutionInfo>
{
public:
using Interface::ListBox<fheroes2::Size>::ActionListSingleClick;
using Interface::ListBox<fheroes2::Size>::ActionListPressRight;
using Interface::ListBox<fheroes2::Size>::ActionListDoubleClick;
using Interface::ListBox<fheroes2::ResolutionInfo>::ActionListSingleClick;
using Interface::ListBox<fheroes2::ResolutionInfo>::ActionListPressRight;
using Interface::ListBox<fheroes2::ResolutionInfo>::ActionListDoubleClick;

explicit ResolutionList( const fheroes2::Point & offset )
: Interface::ListBox<fheroes2::Size>( offset )
: Interface::ListBox<fheroes2::ResolutionInfo>( offset )
, _isDoubleClicked( false )
{}
{
// Do nothing.
}

void RedrawItem( const fheroes2::Size & resolution, int32_t offsetX, int32_t offsetY, bool current ) override
void RedrawItem( const fheroes2::ResolutionInfo & resolution, int32_t offsetX, int32_t offsetY, bool current ) override
{
const Text text( GetResolutionString( resolution ), ( current ? Font::YELLOW_BIG : Font::BIG ) );
text.Blit( ( editBoxLength - text.w() ) / 2 + offsetX, offsetY, editBoxLength );
const fheroes2::FontType fontType = current ? fheroes2::FontType::normalYellow() : fheroes2::FontType::normalWhite();

const auto [leftText, rightText] = getResolutionStrings( resolution );
const int32_t middleTextSize = fheroes2::Text( middleText, fontType ).width();
const int32_t leftTextSize = fheroes2::Text( leftText, fontType ).width();

const fheroes2::Text resolutionText( leftText + middleText + rightText, fontType );

const int32_t textOffsetX = offsetX + editBoxLength / 2 - leftTextSize - middleTextSize / 2;
const int32_t textOffsetY = offsetY + ( resolutionItemHeight - resolutionText.height() + textOffsetYCorrection ) / 2;

resolutionText.draw( textOffsetX, textOffsetY, fheroes2::Display::instance() );
}

void RedrawBackground( const fheroes2::Point & dst ) override
Expand All @@ -87,17 +106,17 @@ namespace
// Do nothing.
}

void ActionListSingleClick( fheroes2::Size & ) override
void ActionListSingleClick( fheroes2::ResolutionInfo & /*unused*/ ) override
ihhub marked this conversation as resolved.
Show resolved Hide resolved
{
// Do nothing.
}

void ActionListPressRight( fheroes2::Size & ) override
void ActionListPressRight( fheroes2::ResolutionInfo & /*unused*/ ) override
ihhub marked this conversation as resolved.
Show resolved Hide resolved
{
// Do nothing.
}

void ActionListDoubleClick( fheroes2::Size & ) override
void ActionListDoubleClick( fheroes2::ResolutionInfo & /*unused*/ ) override
ihhub marked this conversation as resolved.
Show resolved Hide resolved
{
_isDoubleClicked = true;
}
Expand All @@ -111,14 +130,22 @@ namespace
bool _isDoubleClicked;
};

void RedrawInfo( const fheroes2::Point & dst, const fheroes2::Size & resolution )
void RedrawInfo( const fheroes2::Point & dst, const fheroes2::ResolutionInfo & resolution, fheroes2::Image & output )
{
Text text( _( "Select Game Resolution:" ), Font::YELLOW_BIG );
text.Blit( dst.x + ( 377 - text.w() ) / 2, dst.y + 30 );
fheroes2::Text text( _( "Select Game Resolution:" ), fheroes2::FontType::normalYellow() );
text.draw( dst.x + ( 377 - text.width() ) / 2, dst.y + 32, output );

if ( resolution.width > 0 && resolution.height > 0 ) {
text.Set( GetResolutionString( resolution ), Font::YELLOW_BIG );
text.Blit( dst.x + ( editBoxLength - text.w() ) / 2 + 41, dst.y + 287 + ( 19 - text.h() + 2 ) / 2, editBoxLength );
const fheroes2::FontType fontType = fheroes2::FontType::normalYellow();

const auto [leftText, rightText] = getResolutionStrings( resolution );
const int32_t middleTextSize = fheroes2::Text( middleText, fontType ).width();
const int32_t leftTextSize = fheroes2::Text( leftText, fontType ).width();

const int32_t textOffsetX = dst.x + 41 + editBoxLength / 2 - leftTextSize - middleTextSize / 2;

const fheroes2::Text resolutionText( leftText + middleText + rightText, fontType );
resolutionText.draw( textOffsetX, dst.y + 287 + ( resolutionItemHeight - text.height() + textOffsetYCorrection ) / 2, output );
}
}
}
Expand All @@ -127,7 +154,7 @@ namespace Dialog
{
bool SelectResolution()
{
std::vector<fheroes2::Size> resolutions = fheroes2::engine().getAvailableResolutions();
std::vector<fheroes2::ResolutionInfo> resolutions = fheroes2::engine().getAvailableResolutions();
if ( resolutions.empty() )
return false;

Expand Down Expand Up @@ -161,16 +188,17 @@ namespace Dialog
{ 0, 0, originalSlider.width(), 8 }, { 0, 7, originalSlider.width(), 8 } );
resList.setScrollBarArea( { roi.x + 328, roi.y + 73, 12, 180 } );
resList.setScrollBarImage( scrollbarSlider );
resList.SetAreaMaxItems( 11 );
resList.SetAreaItems( { roi.x + 41, roi.y + 55 + 3, editBoxLength, 215 } );
const int32_t maximumItems = 11;
resList.SetAreaMaxItems( maximumItems );
resList.SetAreaItems( { roi.x + 41, roi.y + 55 + 4, editBoxLength, resolutionItemHeight * maximumItems } );

resList.SetListContent( resolutions );

const fheroes2::Size currentResolution( display.width(), display.height() );
const fheroes2::ResolutionInfo currentResolution{ display.width(), display.height(), display.scale() };

fheroes2::Size selectedResolution;
fheroes2::ResolutionInfo selectedResolution;
for ( size_t i = 0; i < resolutions.size(); ++i ) {
if ( resolutions[i].width == currentResolution.width && resolutions[i].height == currentResolution.height ) {
if ( resolutions[i] == currentResolution ) {
resList.SetCurrent( i );
selectedResolution = resList.GetCurrent();
break;
Expand All @@ -182,7 +210,7 @@ namespace Dialog
buttonOk.draw();
buttonCancel.draw();

RedrawInfo( roi.getPosition(), selectedResolution );
RedrawInfo( roi.getPosition(), selectedResolution, display );

display.render();

Expand All @@ -200,7 +228,7 @@ namespace Dialog
}
}
else if ( le.MouseClickLeft( buttonCancel.area() ) || Game::HotKeyPressEvent( Game::HotKeyEvent::DEFAULT_CANCEL ) ) {
selectedResolution = { 0, 0 };
selectedResolution = {};
break;
}
else if ( le.MousePressRight( buttonCancel.area() ) ) {
Expand All @@ -225,13 +253,12 @@ namespace Dialog
resList.Redraw();
buttonOk.draw();
buttonCancel.draw();
RedrawInfo( roi.getPosition(), selectedResolution );
RedrawInfo( roi.getPosition(), selectedResolution, display );
display.render();
}

if ( selectedResolution.width > 0 && selectedResolution.height > 0
&& ( selectedResolution.width != currentResolution.width || selectedResolution.height != currentResolution.height ) ) {
display.resize( selectedResolution.width, selectedResolution.height );
if ( selectedResolution.width > 0 && selectedResolution.height > 0 && selectedResolution.scale > 0 && selectedResolution != currentResolution ) {
display.setResolution( selectedResolution );

#if !defined( MACOS_APP_BUNDLE )
const fheroes2::Image & appIcon = CreateImageFromZlib( 32, 32, iconImage, sizeof( iconImage ), true );
Expand Down
3 changes: 1 addition & 2 deletions src/fheroes2/game/fheroes2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
#include "image_palette.h"
#include "localevent.h"
#include "logging.h"
#include "math_base.h"
#include "screen.h"
#include "settings.h"
#include "system.h"
Expand Down Expand Up @@ -131,7 +130,7 @@ namespace

fheroes2::Display & display = fheroes2::Display::instance();

display.resize( conf.VideoMode().width, conf.VideoMode().height );
display.setResolution( conf.currentResolutionInfo() );
display.fill( 0 ); // start from a black screen

fheroes2::engine().setTitle( GetCaption() );
Expand Down