mirror of https://github.com/bsnes-emu/bsnes.git
Update to v103r09 release.
byuu says: Changelog: - gba/apu: fixed wave RAM nibble ordering (fixes audio in Castlevania, PocketNES) - emulator: restructured video information to just a single videoResolution() → VideoResolution function - returns "projected size" (between 160x144 and 320x240) - "internal buffer size" (up to 1280x480) - returns aspect correction multiplier that is to be applied to the width field - the value could be < 1.0 to handle systems with taller pixels; although higan doesn't emulate such a system - tomoko: all calculations for scaling and overscan masking are done by the GUI now - tomoko: aspect correction can be enabled in either windowed or fullscreen mode separately; moved to Video settings panel - tomoko: video scaling multipliers (against 320x240) can now me modified from the default (2,3,4) via the configuration file - use this as a really barebones way of supporting high DPI monitors; although the GUI elements won't scale nicely - if you set a value less than two, or greater than your resolution divided by 320x240, it's your own fault when things blow up. I'm not babysitting anyone with advanced config-file only options. - tomoko: added new adaptive windowed mode - when enabled, the window will shrink to eliminate any black borders when loading a game or changing video settings. The window will not reposition itself. - tomoko: added new adaptive fullscreen mode - when enabled, the integral scaling will be disabled for fullscreen mode, forcing the video to fill at least one direction of the video monitor completely. I expect we will be bikeshedding for the next month on how to describe the new video options, where they should appear in the GUI, changes people want, etc ... but suffice to say, I'm happy with the functionality, so I don't intend to make changes to -what- things do, but I will entertain better ways to name things.
This commit is contained in:
parent
191a71b291
commit
7af270aa59
|
@ -12,7 +12,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "103.08";
|
static const string Version = "103.09";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
|
|
@ -43,10 +43,14 @@ struct Interface {
|
||||||
virtual auto title() -> string = 0;
|
virtual auto title() -> string = 0;
|
||||||
|
|
||||||
//video information
|
//video information
|
||||||
struct VideoSize { uint width, height; };
|
struct VideoResolution {
|
||||||
virtual auto videoResolution() -> VideoSize = 0;
|
uint width;
|
||||||
virtual auto videoSize(uint width, uint height, bool aspectCorrection, uint cropWidth = 0, uint cropHeight = 0) -> VideoSize = 0;
|
uint height;
|
||||||
virtual auto videoCrop(const uint32*& data, uint& width, uint& height, uint cropWidth, uint cropHeight) -> void {}
|
uint internalWidth;
|
||||||
|
uint internalHeight;
|
||||||
|
double aspectCorrection;
|
||||||
|
};
|
||||||
|
virtual auto videoResolution() -> VideoResolution = 0;
|
||||||
virtual auto videoColors() -> uint32 = 0;
|
virtual auto videoColors() -> uint32 = 0;
|
||||||
virtual auto videoColor(uint32 color) -> uint64 = 0;
|
virtual auto videoColor(uint32 color) -> uint64 = 0;
|
||||||
|
|
||||||
|
|
|
@ -44,21 +44,8 @@ auto Interface::title() -> string {
|
||||||
return cartridge.title();
|
return cartridge.title();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoResolution() -> VideoSize {
|
auto Interface::videoResolution() -> VideoResolution {
|
||||||
return {256, 240};
|
return {256, 240, 256, 240, 8.0 / 7.0};
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoSize(uint width, uint height, bool aspectCorrection, uint cropHorizontal, uint cropVertical) -> VideoSize {
|
|
||||||
double widthDivider = (256 - cropHorizontal * 2) * (aspectCorrection ? 8.0 / 7.0 : 1.0);
|
|
||||||
double heightDivider = (240 - cropVertical * 2);
|
|
||||||
uint multiplier = min(width / widthDivider, height / heightDivider);
|
|
||||||
return {uint(widthDivider * multiplier), uint(heightDivider * multiplier)};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoCrop(const uint32*& data, uint& width, uint& height, uint cropHorizontal, uint cropVertical) -> void {
|
|
||||||
data += cropVertical * 256 + cropHorizontal;
|
|
||||||
width -= cropHorizontal * 2;
|
|
||||||
height -= cropVertical * 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoColors() -> uint32 {
|
auto Interface::videoColors() -> uint32 {
|
||||||
|
|
|
@ -26,9 +26,7 @@ struct Interface : Emulator::Interface {
|
||||||
auto manifest() -> string override;
|
auto manifest() -> string override;
|
||||||
auto title() -> string override;
|
auto title() -> string override;
|
||||||
|
|
||||||
auto videoResolution() -> VideoSize override;
|
auto videoResolution() -> VideoResolution override;
|
||||||
auto videoSize(uint width, uint height, bool aspectCorrection, uint cropHorizontal, uint cropVertical) -> VideoSize override;
|
|
||||||
auto videoCrop(const uint32*& data, uint& width, uint& height, uint cropHorizontal, uint cropVertical) -> void override;
|
|
||||||
auto videoColors() -> uint32 override;
|
auto videoColors() -> uint32 override;
|
||||||
auto videoColor(uint32 color) -> uint64 override;
|
auto videoColor(uint32 color) -> uint64 override;
|
||||||
|
|
||||||
|
|
|
@ -33,15 +33,8 @@ auto Interface::title() -> string {
|
||||||
return cartridge.title();
|
return cartridge.title();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoResolution() -> VideoSize {
|
auto Interface::videoResolution() -> VideoResolution {
|
||||||
return {160, 144};
|
return {160, 144, 160, 144, 1.0};
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoSize(uint width, uint height, bool, uint, uint) -> VideoSize {
|
|
||||||
double widthDivider = 160;
|
|
||||||
double heightDivider = 144;
|
|
||||||
uint multiplier = min(width / widthDivider, height / heightDivider);
|
|
||||||
return {uint(widthDivider * multiplier), uint(heightDivider * multiplier)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::loaded() -> bool {
|
auto Interface::loaded() -> bool {
|
||||||
|
|
|
@ -23,8 +23,7 @@ struct Interface : Emulator::Interface {
|
||||||
auto manifest() -> string override;
|
auto manifest() -> string override;
|
||||||
auto title() -> string override;
|
auto title() -> string override;
|
||||||
|
|
||||||
auto videoResolution() -> VideoSize override;
|
auto videoResolution() -> VideoResolution override;
|
||||||
auto videoSize(uint width, uint height, bool, uint, uint) -> VideoSize override;
|
|
||||||
|
|
||||||
auto loaded() -> bool override;
|
auto loaded() -> bool override;
|
||||||
auto sha256() -> string override;
|
auto sha256() -> string override;
|
||||||
|
|
|
@ -66,14 +66,14 @@ auto APU::Wave::write(uint addr, uint8 byte) -> void {
|
||||||
|
|
||||||
auto APU::Wave::readram(uint addr) const -> uint8 {
|
auto APU::Wave::readram(uint addr) const -> uint8 {
|
||||||
uint8 byte = 0;
|
uint8 byte = 0;
|
||||||
byte |= pattern[!bank << 5 | addr << 1 | 0] << 0;
|
byte |= pattern[!bank << 5 | addr << 1 | 0] << 4;
|
||||||
byte |= pattern[!bank << 5 | addr << 1 | 1] << 4;
|
byte |= pattern[!bank << 5 | addr << 1 | 1] << 0;
|
||||||
return byte;
|
return byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::Wave::writeram(uint addr, uint8 byte) -> void {
|
auto APU::Wave::writeram(uint addr, uint8 byte) -> void {
|
||||||
pattern[!bank << 5 | addr << 1 | 0] = byte >> 0;
|
pattern[!bank << 5 | addr << 1 | 0] = byte >> 4;
|
||||||
pattern[!bank << 5 | addr << 1 | 1] = byte >> 4;
|
pattern[!bank << 5 | addr << 1 | 1] = byte >> 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::Wave::power() -> void {
|
auto APU::Wave::power() -> void {
|
||||||
|
|
|
@ -39,21 +39,14 @@ auto Interface::title() -> string {
|
||||||
return cartridge.title();
|
return cartridge.title();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoResolution() -> VideoSize {
|
auto Interface::videoResolution() -> VideoResolution {
|
||||||
if(!settings.rotateLeft) {
|
if(!settings.rotateLeft) {
|
||||||
return {240, 160};
|
return {240, 160, 240, 160, 1.0};
|
||||||
} else {
|
} else {
|
||||||
return {160, 240};
|
return {160, 240, 160, 240, 1.0};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoSize(uint width, uint height, bool, uint, uint) -> VideoSize {
|
|
||||||
double widthDivider = videoResolution().width;
|
|
||||||
double heightDivider = videoResolution().height;
|
|
||||||
uint multiplier = min(width / widthDivider, height / heightDivider);
|
|
||||||
return {uint(widthDivider * multiplier), uint(heightDivider * multiplier)};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoColors() -> uint32 {
|
auto Interface::videoColors() -> uint32 {
|
||||||
return 1 << 15;
|
return 1 << 15;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,7 @@ struct Interface : Emulator::Interface {
|
||||||
auto manifest() -> string override;
|
auto manifest() -> string override;
|
||||||
auto title() -> string override;
|
auto title() -> string override;
|
||||||
|
|
||||||
auto videoResolution() -> VideoSize override;
|
auto videoResolution() -> VideoResolution override;
|
||||||
auto videoSize(uint width, uint height, bool, uint, uint) -> VideoSize override;
|
|
||||||
auto videoColors() -> uint32 override;
|
auto videoColors() -> uint32 override;
|
||||||
auto videoColor(uint32 color) -> uint64 override;
|
auto videoColor(uint32 color) -> uint64 override;
|
||||||
|
|
||||||
|
|
|
@ -52,23 +52,8 @@ auto Interface::title() -> string {
|
||||||
return cartridge.title();
|
return cartridge.title();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoResolution() -> VideoSize {
|
auto Interface::videoResolution() -> VideoResolution {
|
||||||
return {1280, 480};
|
return {320, 240, 1280, 480, 1.0};
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoSize(uint width, uint height, bool aspectCorrection, uint cropHorizontal, uint cropVertical) -> VideoSize {
|
|
||||||
double widthDivider = (320 - cropHorizontal * 2);
|
|
||||||
double heightDivider = (240 - cropVertical * 2);
|
|
||||||
uint multiplier = min(width / widthDivider, height / heightDivider);
|
|
||||||
return {uint(widthDivider * multiplier), uint(heightDivider * multiplier)};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoCrop(const uint32*& data, uint& width, uint& height, uint cropHorizontal, uint cropVertical) -> void {
|
|
||||||
cropHorizontal *= 4;
|
|
||||||
cropVertical *= 2;
|
|
||||||
data += cropVertical * 1280 + cropHorizontal;
|
|
||||||
width -= cropHorizontal * 2;
|
|
||||||
height -= cropVertical * 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoColors() -> uint32 {
|
auto Interface::videoColors() -> uint32 {
|
||||||
|
|
|
@ -26,9 +26,7 @@ struct Interface : Emulator::Interface {
|
||||||
auto manifest() -> string override;
|
auto manifest() -> string override;
|
||||||
auto title() -> string override;
|
auto title() -> string override;
|
||||||
|
|
||||||
auto videoResolution() -> VideoSize override;
|
auto videoResolution() -> VideoResolution override;
|
||||||
auto videoSize(uint width, uint height, bool aspectCorrection, uint cropHorizontal, uint cropVertical) -> VideoSize override;
|
|
||||||
auto videoCrop(const uint32*& data, uint& width, uint& height, uint cropHorizontal, uint cropVertical) -> void override;
|
|
||||||
auto videoColors() -> uint32 override;
|
auto videoColors() -> uint32 override;
|
||||||
auto videoColor(uint32 color) -> uint64 override;
|
auto videoColor(uint32 color) -> uint64 override;
|
||||||
|
|
||||||
|
|
|
@ -21,15 +21,8 @@ GameGearInterface::GameGearInterface() {
|
||||||
ports.append(move(hardware));
|
ports.append(move(hardware));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto GameGearInterface::videoResolution() -> VideoSize {
|
auto GameGearInterface::videoResolution() -> VideoResolution {
|
||||||
return {160, 144};
|
return {160, 144, 160, 144, 1.0};
|
||||||
}
|
|
||||||
|
|
||||||
auto GameGearInterface::videoSize(uint width, uint height, bool, uint, uint) -> VideoSize {
|
|
||||||
double widthDivider = 160;
|
|
||||||
double heightDivider = 144;
|
|
||||||
uint multiplier = min(width / widthDivider, height / heightDivider);
|
|
||||||
return {uint(widthDivider * multiplier), uint(heightDivider * multiplier)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto GameGearInterface::videoColors() -> uint32 {
|
auto GameGearInterface::videoColors() -> uint32 {
|
||||||
|
|
|
@ -50,9 +50,7 @@ struct MasterSystemInterface : Interface {
|
||||||
|
|
||||||
MasterSystemInterface();
|
MasterSystemInterface();
|
||||||
|
|
||||||
auto videoResolution() -> VideoSize override;
|
auto videoResolution() -> VideoResolution override;
|
||||||
auto videoSize(uint width, uint height, bool aspectCorrection, uint cropWidth, uint cropHeight) -> VideoSize override;
|
|
||||||
auto videoCrop(const uint32*& data, uint& width, uint& height, uint cropWidth, uint cropHeight) -> void override;
|
|
||||||
auto videoColors() -> uint32 override;
|
auto videoColors() -> uint32 override;
|
||||||
auto videoColor(uint32 color) -> uint64 override;
|
auto videoColor(uint32 color) -> uint64 override;
|
||||||
|
|
||||||
|
@ -66,8 +64,7 @@ struct GameGearInterface : Interface {
|
||||||
|
|
||||||
GameGearInterface();
|
GameGearInterface();
|
||||||
|
|
||||||
auto videoResolution() -> VideoSize override;
|
auto videoResolution() -> VideoResolution override;
|
||||||
auto videoSize(uint width, uint height, bool, uint, uint) -> VideoSize override;
|
|
||||||
auto videoColors() -> uint32 override;
|
auto videoColors() -> uint32 override;
|
||||||
auto videoColor(uint32 color) -> uint64 override;
|
auto videoColor(uint32 color) -> uint64 override;
|
||||||
|
|
||||||
|
|
|
@ -36,21 +36,8 @@ MasterSystemInterface::MasterSystemInterface() {
|
||||||
ports.append(move(controllerPort2));
|
ports.append(move(controllerPort2));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto MasterSystemInterface::videoResolution() -> VideoSize {
|
auto MasterSystemInterface::videoResolution() -> VideoResolution {
|
||||||
return {256, 240};
|
return {256, 240, 256, 240, 8.0 / 7.0};
|
||||||
}
|
|
||||||
|
|
||||||
auto MasterSystemInterface::videoSize(uint width, uint height, bool aspectCorrection, uint cropHorizontal, uint cropVertical) -> VideoSize {
|
|
||||||
double widthDivider = (256 - cropHorizontal * 2) * (aspectCorrection ? 8.0 / 7.0 : 1.0);
|
|
||||||
double heightDivider = (240 - cropVertical * 2);
|
|
||||||
uint multiplier = min(width / widthDivider, height / heightDivider);
|
|
||||||
return {uint(widthDivider * multiplier), uint(heightDivider * multiplier)};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto MasterSystemInterface::videoCrop(const uint32*& data, uint& width, uint& height, uint cropHorizontal, uint cropVertical) -> void {
|
|
||||||
data += cropVertical * 256 + cropHorizontal;
|
|
||||||
width -= cropHorizontal * 2;
|
|
||||||
height -= cropVertical * 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto MasterSystemInterface::videoColors() -> uint32 {
|
auto MasterSystemInterface::videoColors() -> uint32 {
|
||||||
|
|
|
@ -39,22 +39,8 @@ auto Interface::title() -> string {
|
||||||
return cartridge.title();
|
return cartridge.title();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoResolution() -> VideoSize {
|
auto Interface::videoResolution() -> VideoResolution {
|
||||||
return {1120, 240};
|
return {280, 240, 1120, 240, 8.0 / 7.0};
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoSize(uint width, uint height, bool aspectCorrection, uint cropHorizontal, uint cropVertical) -> VideoSize {
|
|
||||||
double widthDivider = (280 - cropHorizontal * 2) * (aspectCorrection ? 8.0 / 7.0 : 1.0);
|
|
||||||
double heightDivider = (240 - cropVertical * 2);
|
|
||||||
uint multiplier = min(width / widthDivider, height / heightDivider);
|
|
||||||
return {uint(widthDivider * multiplier), uint(heightDivider * multiplier)};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoCrop(const uint32*& data, uint& width, uint& height, uint cropHorizontal, uint cropVertical) -> void {
|
|
||||||
cropHorizontal *= 4;
|
|
||||||
data += cropVertical * 1120 + cropHorizontal;
|
|
||||||
width -= cropHorizontal * 2;
|
|
||||||
height -= cropVertical * 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoColors() -> uint32 {
|
auto Interface::videoColors() -> uint32 {
|
||||||
|
|
|
@ -23,9 +23,7 @@ struct Interface : Emulator::Interface {
|
||||||
auto manifest() -> string override;
|
auto manifest() -> string override;
|
||||||
auto title() -> string override;
|
auto title() -> string override;
|
||||||
|
|
||||||
auto videoResolution() -> VideoSize override;
|
auto videoResolution() -> VideoResolution override;
|
||||||
auto videoSize(uint width, uint height, bool aspectCorrection, uint cropHorizontal, uint cropVertical) -> VideoSize override;
|
|
||||||
auto videoCrop(const uint32*& data, uint& width, uint& height, uint cropHorizontal, uint cropVertical) -> void override;
|
|
||||||
auto videoColors() -> uint32 override;
|
auto videoColors() -> uint32 override;
|
||||||
auto videoColor(uint32 color) -> uint64 override;
|
auto videoColor(uint32 color) -> uint64 override;
|
||||||
|
|
||||||
|
|
|
@ -118,23 +118,8 @@ auto Interface::title() -> string {
|
||||||
return cartridge.title();
|
return cartridge.title();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoResolution() -> VideoSize {
|
auto Interface::videoResolution() -> VideoResolution {
|
||||||
return {512, 480};
|
return {256, 240, 512, 480, 8.0 / 7.0};
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoSize(uint width, uint height, bool aspectCorrection, uint cropHorizontal, uint cropVertical) -> VideoSize {
|
|
||||||
double widthDivider = (256 - cropHorizontal * 2) * (aspectCorrection ? 8.0 / 7.0 : 1.0);
|
|
||||||
double heightDivider = (240 - cropVertical * 2);
|
|
||||||
uint multiplier = min(width / widthDivider, height / heightDivider);
|
|
||||||
return {uint(widthDivider * multiplier), uint(heightDivider * multiplier)};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::videoCrop(const uint32*& data, uint& width, uint& height, uint cropHorizontal, uint cropVertical) -> void {
|
|
||||||
cropHorizontal *= 2;
|
|
||||||
cropVertical *= 2;
|
|
||||||
data += cropVertical * 512 + cropHorizontal;
|
|
||||||
width -= cropHorizontal * 2;
|
|
||||||
height -= cropVertical * 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoColors() -> uint32 {
|
auto Interface::videoColors() -> uint32 {
|
||||||
|
|
|
@ -38,9 +38,7 @@ struct Interface : Emulator::Interface {
|
||||||
auto manifest() -> string override;
|
auto manifest() -> string override;
|
||||||
auto title() -> string override;
|
auto title() -> string override;
|
||||||
|
|
||||||
auto videoResolution() -> VideoSize override;
|
auto videoResolution() -> VideoResolution override;
|
||||||
auto videoSize(uint width, uint height, bool aspectCorrection, uint cropWidth, uint cropHeight) -> VideoSize override;
|
|
||||||
auto videoCrop(const uint32*& data, uint& width, uint& height, uint cropWidth, uint cropHeight) -> void override;
|
|
||||||
auto videoColors() -> uint32 override;
|
auto videoColors() -> uint32 override;
|
||||||
auto videoColor(uint32 color) -> uint64 override;
|
auto videoColor(uint32 color) -> uint64 override;
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,6 @@ Settings::Settings() {
|
||||||
|
|
||||||
set("Video/Driver", ruby::Video::optimalDriver());
|
set("Video/Driver", ruby::Video::optimalDriver());
|
||||||
set("Video/Synchronize", false);
|
set("Video/Synchronize", false);
|
||||||
set("Video/Scale", "Small");
|
|
||||||
set("Video/AspectCorrection", true);
|
|
||||||
set("Video/Shader", "Blur");
|
set("Video/Shader", "Blur");
|
||||||
set("Video/BlurEmulation", true);
|
set("Video/BlurEmulation", true);
|
||||||
set("Video/ColorEmulation", true);
|
set("Video/ColorEmulation", true);
|
||||||
|
@ -31,6 +29,16 @@ Settings::Settings() {
|
||||||
set("Video/Overscan/Horizontal", 8);
|
set("Video/Overscan/Horizontal", 8);
|
||||||
set("Video/Overscan/Vertical", 8);
|
set("Video/Overscan/Vertical", 8);
|
||||||
|
|
||||||
|
set("Video/Windowed/AspectCorrection", true);
|
||||||
|
set("Video/Windowed/Adaptive", false);
|
||||||
|
set("Video/Windowed/Multiplier", "Small");
|
||||||
|
set("Video/Windowed/Multiplier/Small", 2);
|
||||||
|
set("Video/Windowed/Multiplier/Medium", 3);
|
||||||
|
set("Video/Windowed/Multiplier/Large", 4);
|
||||||
|
|
||||||
|
set("Video/Fullscreen/AspectCorrection", true);
|
||||||
|
set("Video/Fullscreen/Adaptive", false);
|
||||||
|
|
||||||
set("Audio/Driver", ruby::Audio::optimalDriver());
|
set("Audio/Driver", ruby::Audio::optimalDriver());
|
||||||
set("Audio/Device", "");
|
set("Audio/Device", "");
|
||||||
set("Audio/Exclusive", false);
|
set("Audio/Exclusive", false);
|
||||||
|
|
|
@ -47,23 +47,19 @@ Presentation::Presentation() {
|
||||||
|
|
||||||
settingsMenu.setText("Settings");
|
settingsMenu.setText("Settings");
|
||||||
videoScaleMenu.setText("Video Scale");
|
videoScaleMenu.setText("Video Scale");
|
||||||
if(settings["Video/Scale"].text() == "Small") videoScaleSmall.setChecked();
|
if(settings["Video/Windowed/Multiplier"].text() == "Small") videoScaleSmall.setChecked();
|
||||||
if(settings["Video/Scale"].text() == "Medium") videoScaleMedium.setChecked();
|
if(settings["Video/Windowed/Multiplier"].text() == "Medium") videoScaleMedium.setChecked();
|
||||||
if(settings["Video/Scale"].text() == "Large") videoScaleLarge.setChecked();
|
if(settings["Video/Windowed/Multiplier"].text() == "Large") videoScaleLarge.setChecked();
|
||||||
videoScaleSmall.setText("Small").onActivate([&] {
|
videoScaleSmall.setText("Small").onActivate([&] {
|
||||||
settings["Video/Scale"].setValue("Small");
|
settings["Video/Windowed/Multiplier"].setValue("Small");
|
||||||
resizeViewport();
|
resizeViewport();
|
||||||
});
|
});
|
||||||
videoScaleMedium.setText("Medium").onActivate([&] {
|
videoScaleMedium.setText("Medium").onActivate([&] {
|
||||||
settings["Video/Scale"].setValue("Medium");
|
settings["Video/Windowed/Multiplier"].setValue("Medium");
|
||||||
resizeViewport();
|
resizeViewport();
|
||||||
});
|
});
|
||||||
videoScaleLarge.setText("Large").onActivate([&] {
|
videoScaleLarge.setText("Large").onActivate([&] {
|
||||||
settings["Video/Scale"].setValue("Large");
|
settings["Video/Windowed/Multiplier"].setValue("Large");
|
||||||
resizeViewport();
|
|
||||||
});
|
|
||||||
aspectCorrection.setText("Aspect Correction").setChecked(settings["Video/AspectCorrection"].boolean()).onToggle([&] {
|
|
||||||
settings["Video/AspectCorrection"].setValue(aspectCorrection.checked());
|
|
||||||
resizeViewport();
|
resizeViewport();
|
||||||
});
|
});
|
||||||
videoEmulationMenu.setText("Video Emulation");
|
videoEmulationMenu.setText("Video Emulation");
|
||||||
|
@ -243,35 +239,59 @@ auto Presentation::resizeViewport() -> void {
|
||||||
//clear video area before resizing to avoid seeing distorted video momentarily
|
//clear video area before resizing to avoid seeing distorted video momentarily
|
||||||
clearViewport();
|
clearViewport();
|
||||||
|
|
||||||
uint scale = 2;
|
double emulatorWidth = 320;
|
||||||
if(settings["Video/Scale"].text() == "Small" ) scale = 2;
|
double emulatorHeight = 240;
|
||||||
if(settings["Video/Scale"].text() == "Medium") scale = 3;
|
double aspectCorrection = 1.0;
|
||||||
if(settings["Video/Scale"].text() == "Large" ) scale = 4;
|
if(emulator) {
|
||||||
|
auto resolution = emulator->videoResolution();
|
||||||
uint windowWidth = 0, windowHeight = 0;
|
emulatorWidth = resolution.width;
|
||||||
bool aspectCorrection = true;
|
emulatorHeight = resolution.height;
|
||||||
if(!fullScreen()) {
|
aspectCorrection = resolution.aspectCorrection;
|
||||||
windowWidth = 320 * scale;
|
|
||||||
windowHeight = 240 * scale;
|
|
||||||
aspectCorrection = settings["Video/AspectCorrection"].boolean();
|
|
||||||
} else {
|
|
||||||
windowWidth = geometry().width();
|
|
||||||
windowHeight = geometry().height();
|
|
||||||
}
|
|
||||||
if(!fullScreen()) setSize({windowWidth, windowHeight});
|
|
||||||
|
|
||||||
if(!emulator) {
|
|
||||||
viewport.setGeometry({0, 0, windowWidth, windowHeight});
|
|
||||||
} else {
|
|
||||||
uint overscanWidth = 0, overscanHeight = 0;
|
|
||||||
if(emulator->information.overscan && settings["Video/Overscan/Mask"].boolean()) {
|
if(emulator->information.overscan && settings["Video/Overscan/Mask"].boolean()) {
|
||||||
overscanWidth = settings["Video/Overscan/Horizontal"].natural();
|
uint overscanHorizontal = settings["Video/Overscan/Horizontal"].natural();
|
||||||
overscanHeight = settings["Video/Overscan/Vertical" ].natural();
|
uint overscanVertical = settings["Video/Overscan/Vertical"].natural();
|
||||||
|
emulatorWidth -= overscanHorizontal * 2;
|
||||||
|
emulatorHeight -= overscanVertical * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!fullScreen()) {
|
||||||
|
if(settings["Video/Windowed/AspectCorrection"].boolean()) emulatorWidth *= aspectCorrection;
|
||||||
|
uint viewportMultiplier = 2;
|
||||||
|
if(settings["Video/Windowed/Multiplier"].text() == "Small") viewportMultiplier = settings["Video/Windowed/Multiplier/Small"].natural();
|
||||||
|
if(settings["Video/Windowed/Multiplier"].text() == "Medium") viewportMultiplier = settings["Video/Windowed/Multiplier/Medium"].natural();
|
||||||
|
if(settings["Video/Windowed/Multiplier"].text() == "Large") viewportMultiplier = settings["Video/Windowed/Multiplier/Large"].natural();
|
||||||
|
uint viewportWidth = 320 * viewportMultiplier;
|
||||||
|
uint viewportHeight = 240 * viewportMultiplier;
|
||||||
|
uint multiplier = min(viewportWidth / emulatorWidth, viewportHeight / emulatorHeight);
|
||||||
|
if(!settings["Video/Windowed/Adaptive"].boolean()) {
|
||||||
|
emulatorWidth *= multiplier;
|
||||||
|
emulatorHeight *= multiplier;
|
||||||
|
setSize({viewportWidth, viewportHeight});
|
||||||
|
viewport.setGeometry({
|
||||||
|
(viewportWidth - emulatorWidth) / 2, (viewportHeight - emulatorHeight) / 2,
|
||||||
|
emulatorWidth, emulatorHeight
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setSize({emulatorWidth * multiplier, emulatorHeight * multiplier});
|
||||||
|
viewport.setGeometry({0, 0, emulatorWidth * multiplier, emulatorHeight * multiplier});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(settings["Video/Fullscreen/AspectCorrection"].boolean()) emulatorWidth *= aspectCorrection;
|
||||||
|
uint viewportWidth = geometry().width();
|
||||||
|
uint viewportHeight = geometry().height();
|
||||||
|
if(!settings["Video/Fullscreen/Adaptive"].boolean()) {
|
||||||
|
uint multiplier = min(viewportWidth / emulatorWidth, viewportHeight / emulatorHeight);
|
||||||
|
emulatorWidth *= multiplier;
|
||||||
|
emulatorHeight *= multiplier;
|
||||||
|
} else {
|
||||||
|
double multiplier = min(viewportWidth / emulatorWidth, viewportHeight / emulatorHeight);
|
||||||
|
emulatorWidth *= multiplier;
|
||||||
|
emulatorHeight *= multiplier;
|
||||||
}
|
}
|
||||||
auto videoSize = emulator->videoSize(windowWidth, windowHeight, aspectCorrection, overscanWidth, overscanHeight);
|
|
||||||
viewport.setGeometry({
|
viewport.setGeometry({
|
||||||
(windowWidth - videoSize.width) / 2, (windowHeight - videoSize.height) / 2,
|
(viewportWidth - emulatorWidth) / 2, (viewportHeight - emulatorHeight) / 2,
|
||||||
videoSize.width, videoSize.height
|
emulatorWidth, emulatorHeight
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,6 @@ struct Presentation : Window {
|
||||||
MenuRadioItem videoScaleMedium{&videoScaleMenu};
|
MenuRadioItem videoScaleMedium{&videoScaleMenu};
|
||||||
MenuRadioItem videoScaleLarge{&videoScaleMenu};
|
MenuRadioItem videoScaleLarge{&videoScaleMenu};
|
||||||
Group videoScales{&videoScaleSmall, &videoScaleMedium, &videoScaleLarge};
|
Group videoScales{&videoScaleSmall, &videoScaleMedium, &videoScaleLarge};
|
||||||
MenuSeparator videoScaleSeparator{&videoScaleMenu};
|
|
||||||
MenuCheckItem aspectCorrection{&videoScaleMenu};
|
|
||||||
Menu videoEmulationMenu{&settingsMenu};
|
Menu videoEmulationMenu{&settingsMenu};
|
||||||
MenuCheckItem blurEmulation{&videoEmulationMenu};
|
MenuCheckItem blurEmulation{&videoEmulationMenu};
|
||||||
MenuCheckItem colorEmulation{&videoEmulationMenu};
|
MenuCheckItem colorEmulation{&videoEmulationMenu};
|
||||||
|
|
|
@ -56,9 +56,14 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
|
||||||
pitch >>= 2;
|
pitch >>= 2;
|
||||||
|
|
||||||
if(emulator->information.overscan && settings["Video/Overscan/Mask"].boolean()) {
|
if(emulator->information.overscan && settings["Video/Overscan/Mask"].boolean()) {
|
||||||
uint horizontal = settings["Video/Overscan/Horizontal"].natural();
|
uint overscanHorizontal = settings["Video/Overscan/Horizontal"].natural();
|
||||||
uint vertical = settings["Video/Overscan/Vertical"].natural();
|
uint overscanVertical = settings["Video/Overscan/Vertical"].natural();
|
||||||
emulator->videoCrop(data, width, height, horizontal, vertical);
|
auto resolution = emulator->videoResolution();
|
||||||
|
overscanHorizontal *= resolution.internalWidth / resolution.width;
|
||||||
|
overscanVertical *= resolution.internalHeight / resolution.height;
|
||||||
|
data += overscanVertical * pitch + overscanHorizontal;
|
||||||
|
width -= overscanHorizontal * 2;
|
||||||
|
height -= overscanVertical * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(video->lock(output, length, width, height)) {
|
if(video->lock(output, length, width, height)) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ struct VideoSettings : TabFrameItem {
|
||||||
VideoSettings(TabFrame*);
|
VideoSettings(TabFrame*);
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
Label colorAdjustmentLabel{&layout, Size{~0, 0}};
|
Label colorAdjustmentLabel{&layout, Size{~0, 0}, 2};
|
||||||
HorizontalLayout saturationLayout{&layout, Size{~0, 0}};
|
HorizontalLayout saturationLayout{&layout, Size{~0, 0}};
|
||||||
Label saturationLabel{&saturationLayout, Size{80, 0}};
|
Label saturationLabel{&saturationLayout, Size{80, 0}};
|
||||||
Label saturationValue{&saturationLayout, Size{50, 0}};
|
Label saturationValue{&saturationLayout, Size{50, 0}};
|
||||||
|
@ -15,7 +15,7 @@ struct VideoSettings : TabFrameItem {
|
||||||
Label luminanceLabel{&luminanceLayout, Size{80, 0}};
|
Label luminanceLabel{&luminanceLayout, Size{80, 0}};
|
||||||
Label luminanceValue{&luminanceLayout, Size{50, 0}};
|
Label luminanceValue{&luminanceLayout, Size{50, 0}};
|
||||||
HorizontalSlider luminanceSlider{&luminanceLayout, Size{~0, 0}};
|
HorizontalSlider luminanceSlider{&luminanceLayout, Size{~0, 0}};
|
||||||
Label overscanMaskLabel{&layout, Size{~0, 0}};
|
Label overscanMaskLabel{&layout, Size{~0, 0}, 2};
|
||||||
HorizontalLayout horizontalMaskLayout{&layout, Size{~0, 0}};
|
HorizontalLayout horizontalMaskLayout{&layout, Size{~0, 0}};
|
||||||
Label horizontalMaskLabel{&horizontalMaskLayout, Size{80, 0}};
|
Label horizontalMaskLabel{&horizontalMaskLayout, Size{80, 0}};
|
||||||
Label horizontalMaskValue{&horizontalMaskLayout, Size{50, 0}};
|
Label horizontalMaskValue{&horizontalMaskLayout, Size{50, 0}};
|
||||||
|
@ -24,16 +24,25 @@ struct VideoSettings : TabFrameItem {
|
||||||
Label verticalMaskLabel{&verticalMaskLayout, Size{80, 0}};
|
Label verticalMaskLabel{&verticalMaskLayout, Size{80, 0}};
|
||||||
Label verticalMaskValue{&verticalMaskLayout, Size{50, 0}};
|
Label verticalMaskValue{&verticalMaskLayout, Size{50, 0}};
|
||||||
HorizontalSlider verticalMaskSlider{&verticalMaskLayout, Size{~0, 0}};
|
HorizontalSlider verticalMaskSlider{&verticalMaskLayout, Size{~0, 0}};
|
||||||
|
Label windowedModeLabel{&layout, Size{~0, 0}, 2};
|
||||||
|
HorizontalLayout windowedModeLayout{&layout, Size{~0, 0}};
|
||||||
|
CheckLabel windowedModeAspectCorrection{&windowedModeLayout, Size{0, 0}};
|
||||||
|
CheckLabel windowedModeAdaptive{&windowedModeLayout, Size{0, 0}};
|
||||||
|
Label fullscreenModeLabel{&layout, Size{~0, 0}, 2};
|
||||||
|
HorizontalLayout fullscreenModeLayout{&layout, Size{~0, 0}};
|
||||||
|
CheckLabel fullscreenModeAspectCorrection{&fullscreenModeLayout, Size{0, 0}};
|
||||||
|
CheckLabel fullscreenModeAdaptive{&fullscreenModeLayout, Size{0, 0}};
|
||||||
|
|
||||||
auto updateColor() -> void;
|
auto updateColor() -> void;
|
||||||
auto updateOverscan() -> void;
|
auto updateOverscan() -> void;
|
||||||
|
auto updateViewport() -> void;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AudioSettings : TabFrameItem {
|
struct AudioSettings : TabFrameItem {
|
||||||
AudioSettings(TabFrame*);
|
AudioSettings(TabFrame*);
|
||||||
|
|
||||||
VerticalLayout layout{this};
|
VerticalLayout layout{this};
|
||||||
Label driverLabel{&layout, Size{~0, 0}};
|
Label driverLabel{&layout, Size{~0, 0}, 2};
|
||||||
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
|
||||||
Label latencyLabel{&controlLayout, Size{0, 0}};
|
Label latencyLabel{&controlLayout, Size{0, 0}};
|
||||||
ComboButton latencyCombo{&controlLayout, Size{~0, 0}};
|
ComboButton latencyCombo{&controlLayout, Size{~0, 0}};
|
||||||
|
@ -42,7 +51,7 @@ struct AudioSettings : TabFrameItem {
|
||||||
Label resamplerLabel{&controlLayout, Size{0, 0}};
|
Label resamplerLabel{&controlLayout, Size{0, 0}};
|
||||||
ComboButton resamplerCombo{&controlLayout, Size{~0, 0}};
|
ComboButton resamplerCombo{&controlLayout, Size{~0, 0}};
|
||||||
CheckLabel exclusiveMode{&layout, Size{~0, 0}};
|
CheckLabel exclusiveMode{&layout, Size{~0, 0}};
|
||||||
Label effectsLabel{&layout, Size{~0, 0}};
|
Label effectsLabel{&layout, Size{~0, 0}, 2};
|
||||||
HorizontalLayout volumeLayout{&layout, Size{~0, 0}};
|
HorizontalLayout volumeLayout{&layout, Size{~0, 0}};
|
||||||
Label volumeLabel{&volumeLayout, Size{80, 0}};
|
Label volumeLabel{&volumeLayout, Size{80, 0}};
|
||||||
Label volumeValue{&volumeLayout, Size{50, 0}};
|
Label volumeValue{&volumeLayout, Size{50, 0}};
|
||||||
|
|
|
@ -23,8 +23,17 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
|
||||||
verticalMaskValue.setAlignment(0.5);
|
verticalMaskValue.setAlignment(0.5);
|
||||||
verticalMaskSlider.setLength(25).setPosition(settings["Video/Overscan/Vertical"].natural()).onChange([&] { updateOverscan(); });
|
verticalMaskSlider.setLength(25).setPosition(settings["Video/Overscan/Vertical"].natural()).onChange([&] { updateOverscan(); });
|
||||||
|
|
||||||
|
windowedModeLabel.setFont(Font().setBold()).setText("Windowed Mode");
|
||||||
|
windowedModeAspectCorrection.setText("Correct aspect ratio").setChecked(settings["Video/Windowed/AspectCorrection"].boolean()).onToggle([&] { updateViewport(); });
|
||||||
|
windowedModeAdaptive.setText("Resize window to viewport").setChecked(settings["Video/Windowed/Adaptive"].boolean()).onToggle([&] { updateViewport(); });
|
||||||
|
|
||||||
|
fullscreenModeLabel.setFont(Font().setBold()).setText("Fullscreen Mode");
|
||||||
|
fullscreenModeAspectCorrection.setText("Correct aspect ratio").setChecked(settings["Video/Fullscreen/AspectCorrection"].boolean()).onToggle([&] { updateViewport(); });
|
||||||
|
fullscreenModeAdaptive.setText("Resize viewport to window").setChecked(settings["Video/Fullscreen/Adaptive"].boolean()).onToggle([&] { updateViewport(); });
|
||||||
|
|
||||||
updateColor();
|
updateColor();
|
||||||
updateOverscan();
|
updateOverscan();
|
||||||
|
updateViewport();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VideoSettings::updateColor() -> void {
|
auto VideoSettings::updateColor() -> void {
|
||||||
|
@ -44,3 +53,11 @@ auto VideoSettings::updateOverscan() -> void {
|
||||||
verticalMaskValue.setText({verticalMaskSlider.position()});
|
verticalMaskValue.setText({verticalMaskSlider.position()});
|
||||||
presentation->resizeViewport();
|
presentation->resizeViewport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto VideoSettings::updateViewport() -> void {
|
||||||
|
settings["Video/Windowed/AspectCorrection"].setValue(windowedModeAspectCorrection.checked());
|
||||||
|
settings["Video/Windowed/Adaptive"].setValue(windowedModeAdaptive.checked());
|
||||||
|
settings["Video/Fullscreen/AspectCorrection"].setValue(fullscreenModeAspectCorrection.checked());
|
||||||
|
settings["Video/Fullscreen/Adaptive"].setValue(fullscreenModeAdaptive.checked());
|
||||||
|
presentation->resizeViewport();
|
||||||
|
}
|
||||||
|
|
|
@ -35,21 +35,14 @@ auto Interface::title() -> string {
|
||||||
return cartridge.information.title;
|
return cartridge.information.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoResolution() -> VideoSize {
|
auto Interface::videoResolution() -> VideoResolution {
|
||||||
if(!settings.rotateLeft) {
|
if(!settings.rotateLeft) {
|
||||||
return {224, 144};
|
return {224, 144, 224, 144, 1.0};
|
||||||
} else {
|
} else {
|
||||||
return {144, 224};
|
return {144, 224, 144, 224, 1.0};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Interface::videoSize(uint width, uint height, bool, uint, uint) -> VideoSize {
|
|
||||||
double widthDivider = videoResolution().width;
|
|
||||||
double heightDivider = videoResolution().height;
|
|
||||||
uint multiplier = min(width / widthDivider, height / heightDivider);
|
|
||||||
return {uint(widthDivider * multiplier), uint(heightDivider * multiplier)};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Interface::loaded() -> bool {
|
auto Interface::loaded() -> bool {
|
||||||
return system.loaded();
|
return system.loaded();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,7 @@ struct Interface : Emulator::Interface {
|
||||||
auto manifest() -> string override;
|
auto manifest() -> string override;
|
||||||
auto title() -> string override;
|
auto title() -> string override;
|
||||||
|
|
||||||
auto videoResolution() -> VideoSize override;
|
auto videoResolution() -> VideoResolution override;
|
||||||
auto videoSize(uint width, uint height, bool, uint, uint) -> VideoSize override;
|
|
||||||
|
|
||||||
auto loaded() -> bool override;
|
auto loaded() -> bool override;
|
||||||
auto sha256() -> string override;
|
auto sha256() -> string override;
|
||||||
|
|
Loading…
Reference in New Issue