Update to v103r08 release.

byuu says:

Changelog:

  - emulator: improved aspect correction accuracy by using
    floating-point calculations
  - emulator: added videoCrop() function, extended videoSize() to take
    cropping parameters¹
  - tomoko: the overscan masking function will now actually resize the
    viewport²
  - gba/cpu: fixed two-cycle delay on triggering DMAs; not running DMAs
    when the CPU is stopped
  - md/vdp: center video when overscan is disabled
  - pce/vce: resize video output from 1140x240 to 1120x240
  - tomoko: resize window scaling from 326x240 to 320x240
  - tomoko: changed save slot naming and status bar messages to indicate
    quick states vs managed states
  - tomoko: added increment/decrement quick state hotkeys
  - tomoko: save/load quick state hotkeys now save to slots 1-5 instead
    of always to 0
  - tomoko: increased overscan range from 0-16 to 0-24 (in case you want
    to mask the Master System to 240x192)

¹: the idea here was to decouple raw pixels from overscan masking.
Overscan was actually horrifically broken before. The Famicom outputs at
256x240, the Super Famicom at 512x480, and the Mega Drive at 1280x480.
Before, a horizontal overscan mask of 8 would not reduce the Super
Famicom or Mega Drive by nearly as much as the Famicom. WIth the new
videoCrop() function, the internals of pixel size distortions can be
handled by each individual core.

²: furthermore, by taking optional cropping information in
videoSize(), games can scale even larger into the viewport window. So
for example, before the Super Famicom could only scale to 1536x1440. But
by cropping the vertical resolution by 6 (228p effectively, still more
than NTSC can even show), I can now scale to 1792x1596. And wiht aspect
correction, that becomes a perfect 8:7 ratio of 2048x1596, giving me
perfectly crisp pixels without linear interpolation being required.

Errata: for some reason, when I save a new managed state with the SFC
core, the default description is being set to a string of what looks to
be hex numbers. I found the cause ... I'll fix this in the next release.

Note: I'd also like to hide the "find codes..." button if cheats.bml
isn't present, as well as update the SMP TEST register comment from
smp/timing.cpp
This commit is contained in:
Tim Allen 2017-07-05 15:44:15 +10:00
parent d4876a831f
commit 191a71b291
31 changed files with 179 additions and 108 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "103.07";
static const string Version = "103.08";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -45,7 +45,8 @@ struct Interface {
//video information
struct VideoSize { uint width, height; };
virtual auto videoResolution() -> VideoSize = 0;
virtual auto videoSize(uint width, uint height, bool arc) -> VideoSize = 0;
virtual auto videoSize(uint width, uint height, bool aspectCorrection, uint cropWidth = 0, uint cropHeight = 0) -> VideoSize = 0;
virtual auto videoCrop(const uint32*& data, uint& width, uint& height, uint cropWidth, uint cropHeight) -> void {}
virtual auto videoColors() -> uint32 = 0;
virtual auto videoColor(uint32 color) -> uint64 = 0;

View File

@ -48,11 +48,17 @@ auto Interface::videoResolution() -> VideoSize {
return {256, 240};
}
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
uint w = 256 * (arc ? 8.0 / 7.0 : 1.0);
uint h = 240;
uint m = min(width / w, height / h);
return {w * m, h * m};
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 {

View File

@ -27,7 +27,8 @@ struct Interface : Emulator::Interface {
auto title() -> string override;
auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize 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 videoColor(uint32 color) -> uint64 override;

View File

@ -37,11 +37,11 @@ auto Interface::videoResolution() -> VideoSize {
return {160, 144};
}
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
uint w = 160;
uint h = 144;
uint m = min(width / w, height / h);
return {w * m, h * m};
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 {

View File

@ -24,7 +24,7 @@ struct Interface : Emulator::Interface {
auto title() -> string override;
auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoSize(uint width, uint height, bool, uint, uint) -> VideoSize override;
auto loaded() -> bool override;
auto sha256() -> string override;

View File

@ -33,6 +33,12 @@ auto CPU::main() -> void {
}
auto CPU::step(uint clocks) -> void {
for(auto _ : range(clocks)) {
for(auto& dma : this->dma) {
if(dma.waiting) dma.waiting--;
}
}
if(!context.dmaActive) {
context.dmaActive = true;
while(true) {

View File

@ -1,6 +1,5 @@
auto CPU::DMA::run() -> bool {
if(!active) return false;
if(waiting && --waiting) return false;
if(cpu.stopped() || !active || waiting) return false;
transfer();
if(irq) cpu.irq.flag |= CPU::Interrupt::DMA0 << id;

View File

@ -47,11 +47,11 @@ auto Interface::videoResolution() -> VideoSize {
}
}
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
uint w = videoResolution().width;
uint h = videoResolution().height;
uint m = min(width / w, height / h);
return {w * m, h * m};
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 {

View File

@ -24,7 +24,7 @@ struct Interface : Emulator::Interface {
auto title() -> string override;
auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoSize(uint width, uint height, bool, uint, uint) -> VideoSize override;
auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override;

View File

@ -56,11 +56,19 @@ auto Interface::videoResolution() -> VideoSize {
return {1280, 480};
}
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
uint w = 320;
uint h = 240;
uint m = min(width / w, height / h);
return {w * m, h * m};
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 {

View File

@ -27,7 +27,8 @@ struct Interface : Emulator::Interface {
auto title() -> string override;
auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize 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 videoColor(uint32 color) -> uint64 override;

View File

@ -19,7 +19,7 @@ auto VDP::scanline() -> void {
if(state.vcounter == 240) scheduler.exit(Scheduler::Event::Frame);
state.output = buffer + (state.vcounter * 2 + 0) * 1280;
state.output = output + (state.vcounter * 2 + 0) * 1280;
}
auto VDP::run() -> void {

View File

@ -64,12 +64,16 @@ auto VDP::step(uint clocks) -> void {
}
auto VDP::refresh() -> void {
Emulator::video.refresh(buffer, 1280 * sizeof(uint32), 1280, 480);
auto data = output;
if(!latch.overscan) data -= 16 * 1280;
Emulator::video.refresh(data, 1280 * sizeof(uint32), 1280, 480);
}
auto VDP::power() -> void {
create(VDP::Enter, system.frequency() / 2.0);
output = buffer + 16 * 1280; //overscan offset
memory::fill(&io, sizeof(IO));
memory::fill(&latch, sizeof(Latch));
memory::fill(&state, sizeof(State));

View File

@ -248,7 +248,8 @@ private:
uint vcounter;
} state;
uint32 buffer[1280 * 480];
uint32 buffer[1280 * 512];
uint32* output = nullptr;
};
extern VDP vdp;

View File

@ -25,11 +25,11 @@ auto GameGearInterface::videoResolution() -> VideoSize {
return {160, 144};
}
auto GameGearInterface::videoSize(uint width, uint height, bool arc) -> VideoSize {
uint w = 160;
uint h = 144;
uint m = min(width / w, height / h);
return {w * m, h * m};
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 {

View File

@ -51,7 +51,8 @@ struct MasterSystemInterface : Interface {
MasterSystemInterface();
auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize 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 videoColor(uint32 color) -> uint64 override;
@ -66,7 +67,7 @@ struct GameGearInterface : Interface {
GameGearInterface();
auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoSize(uint width, uint height, bool, uint, uint) -> VideoSize override;
auto videoColors() -> uint32 override;
auto videoColor(uint32 color) -> uint64 override;

View File

@ -40,12 +40,17 @@ auto MasterSystemInterface::videoResolution() -> VideoSize {
return {256, 240};
}
auto MasterSystemInterface::videoSize(uint width, uint height, bool arc) -> VideoSize {
auto a = arc ? 8.0 / 7.0 : 1.0;
uint w = 256;
uint h = 240;
uint m = min(width / (w * a), height / h);
return {uint(w * a * m), uint(h * m)};
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 {

View File

@ -40,15 +40,21 @@ auto Interface::title() -> string {
}
auto Interface::videoResolution() -> VideoSize {
return {1140, 240};
return {1120, 240};
}
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
auto a = arc ? 8.0 / 7.0 : 1.0;
uint w = 285;
uint h = 240;
uint m = min(width / (w * a), height / h);
return {uint(w * a * m), uint(h * m)};
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 {

View File

@ -24,7 +24,8 @@ struct Interface : Emulator::Interface {
auto title() -> string override;
auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize 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 videoColor(uint32 color) -> uint64 override;

View File

@ -54,7 +54,7 @@ auto VCE::step(uint clocks) -> void {
}
auto VCE::refresh() -> void {
Emulator::video.refresh(buffer + 1365 * 13, 1365 * sizeof(uint32), 1140, 240);
Emulator::video.refresh(buffer + 1365 * 13, 1365 * sizeof(uint32), 1120, 240);
}
auto VCE::power() -> void {

View File

@ -122,11 +122,19 @@ auto Interface::videoResolution() -> VideoSize {
return {512, 480};
}
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
uint w = 256 * (arc ? 8.0 / 7.0 : 1.0);
uint h = 240;
uint m = min(width / w, height / h);
return {w * m, h * m};
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 {

View File

@ -39,7 +39,8 @@ struct Interface : Emulator::Interface {
auto title() -> string override;
auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize 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 videoColor(uint32 color) -> uint64 override;

View File

@ -1,4 +1,6 @@
auto InputManager::appendHotkeys() -> void {
static int quickStateSlot = 1;
{ auto hotkey = new InputHotkey;
hotkey->name = "Toggle Fullscreen";
hotkey->press = [] {
@ -16,17 +18,35 @@ auto InputManager::appendHotkeys() -> void {
}
{ auto hotkey = new InputHotkey;
hotkey->name = "Save State";
hotkey->name = "Save Quick State";
hotkey->press = [] {
program->saveState(0);
program->saveState(quickStateSlot);
};
hotkeys.append(hotkey);
}
{ auto hotkey = new InputHotkey;
hotkey->name = "Load State";
hotkey->press = [] {
program->loadState(0);
hotkey->name = "Load Quick State";
hotkey->press = [&] {
program->loadState(quickStateSlot);
};
hotkeys.append(hotkey);
}
{ auto hotkey = new InputHotkey;
hotkey->name = "Decrement Quick State";
hotkey->press = [&] {
if(--quickStateSlot < 1) quickStateSlot = 5;
program->showMessage({"Selected quick slot ", quickStateSlot});
};
hotkeys.append(hotkey);
}
{ auto hotkey = new InputHotkey;
hotkey->name = "Increment Quick State";
hotkey->press = [&] {
if(++quickStateSlot > 5) quickStateSlot = 1;
program->showMessage({"Selected quick slot ", quickStateSlot});
};
hotkeys.append(hotkey);
}

View File

@ -81,6 +81,7 @@ Presentation::Presentation() {
});
maskOverscan.setText("Mask Overscan").setChecked(settings["Video/Overscan/Mask"].boolean()).onToggle([&] {
settings["Video/Overscan/Mask"].setValue(maskOverscan.checked());
resizeViewport();
});
videoShaderMenu.setText("Video Shader");
videoShaderNone.setText("None").onActivate([&] {
@ -125,13 +126,13 @@ Presentation::Presentation() {
});
toolsMenu.setText("Tools").setVisible(false);
saveStateMenu.setText("Save State");
saveStateMenu.setText("Save Quickstate");
saveSlot1.setText("Slot 1").onActivate([&] { program->saveState(1); });
saveSlot2.setText("Slot 2").onActivate([&] { program->saveState(2); });
saveSlot3.setText("Slot 3").onActivate([&] { program->saveState(3); });
saveSlot4.setText("Slot 4").onActivate([&] { program->saveState(4); });
saveSlot5.setText("Slot 5").onActivate([&] { program->saveState(5); });
loadStateMenu.setText("Load State");
loadStateMenu.setText("Load Quickstate");
loadSlot1.setText("Slot 1").onActivate([&] { program->loadState(1); });
loadSlot2.setText("Slot 2").onActivate([&] { program->loadState(2); });
loadSlot3.setText("Slot 3").onActivate([&] { program->loadState(3); });
@ -250,7 +251,7 @@ auto Presentation::resizeViewport() -> void {
uint windowWidth = 0, windowHeight = 0;
bool aspectCorrection = true;
if(!fullScreen()) {
windowWidth = 326 * scale;
windowWidth = 320 * scale;
windowHeight = 240 * scale;
aspectCorrection = settings["Video/AspectCorrection"].boolean();
} else {
@ -262,7 +263,12 @@ auto Presentation::resizeViewport() -> void {
if(!emulator) {
viewport.setGeometry({0, 0, windowWidth, windowHeight});
} else {
auto videoSize = emulator->videoSize(windowWidth, windowHeight, aspectCorrection);
uint overscanWidth = 0, overscanHeight = 0;
if(emulator->information.overscan && settings["Video/Overscan/Mask"].boolean()) {
overscanWidth = settings["Video/Overscan/Horizontal"].natural();
overscanHeight = settings["Video/Overscan/Vertical" ].natural();
}
auto videoSize = emulator->videoSize(windowWidth, windowHeight, aspectCorrection, overscanWidth, overscanHeight);
viewport.setGeometry({
(windowWidth - videoSize.width) / 2, (windowHeight - videoSize.height) / 2,
videoSize.width, videoSize.height

View File

@ -53,28 +53,21 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig
uint32_t* output;
uint length;
pitch >>= 2;
if(emulator->information.overscan && settings["Video/Overscan/Mask"].boolean()) {
uint horizontal = settings["Video/Overscan/Horizontal"].natural();
uint vertical = settings["Video/Overscan/Vertical"].natural();
emulator->videoCrop(data, width, height, horizontal, vertical);
}
if(video->lock(output, length, width, height)) {
pitch >>= 2, length >>= 2;
length >>= 2;
for(auto y : range(height)) {
memory::copy(output + y * length, data + y * pitch, width * sizeof(uint32));
}
if(emulator->information.overscan && settings["Video/Overscan/Mask"].boolean()) {
auto h = settings["Video/Overscan/Horizontal"].natural();
auto v = settings["Video/Overscan/Vertical"].natural();
if(h) for(auto y : range(height)) {
memory::fill(output + y * length, 4 * h);
memory::fill(output + y * length + (width - h), 4 * h);
}
if(v) for(auto y : range(v)) {
memory::fill(output + y * length, 4 * width);
memory::fill(output + (height - 1 - y) * length, 4 * width);
}
}
video->unlock();
video->refresh();
}

View File

@ -21,9 +21,9 @@ struct Program : Emulator::Platform {
auto unloadMedium() -> void;
//state.cpp
auto stateName(uint slot, bool manager = false) -> string;
auto loadState(uint slot, bool manager = false) -> bool;
auto saveState(uint slot, bool manager = false) -> bool;
auto stateName(uint slot, bool managed = false) -> string;
auto loadState(uint slot, bool managed = false) -> bool;
auto saveState(uint slot, bool managed = false) -> bool;
//utility.cpp
auto powerCycle() -> void;

View File

@ -1,29 +1,31 @@
auto Program::stateName(uint slot, bool manager) -> string {
auto Program::stateName(uint slot, bool managed) -> string {
return {
mediumPaths(1), "higan/states/",
manager ? "managed/" : "quick/",
managed ? "managed/" : "quick/",
"slot-", slot, ".bst"
};
}
auto Program::loadState(uint slot, bool manager) -> bool {
auto Program::loadState(uint slot, bool managed) -> bool {
if(!emulator) return false;
auto location = stateName(slot, manager);
string type = managed ? "managed" : "quick";
auto location = stateName(slot, managed);
auto memory = file::read(location);
if(memory.size() == 0) return showMessage({"Slot ", slot, " does not exist"}), false;
if(memory.size() == 0) return showMessage({"Slot ", slot, " ", type, " state does not exist"}), false;
serializer s(memory.data(), memory.size());
if(emulator->unserialize(s) == false) return showMessage({"Slot ", slot, " state incompatible"}), false;
return showMessage({"Loaded from slot ", slot}), true;
if(emulator->unserialize(s) == false) return showMessage({"Slot ", slot, " ", type, " state incompatible"}), false;
return showMessage({"Loaded from ", type, " slot ", slot}), true;
}
auto Program::saveState(uint slot, bool manager) -> bool {
auto Program::saveState(uint slot, bool managed) -> bool {
if(!emulator) return false;
auto location = stateName(slot, manager);
string type = managed ? "managed" : "quick";
auto location = stateName(slot, managed);
serializer s = emulator->serialize();
if(s.size() == 0) return showMessage({"Failed to save state to slot ", slot}), false;
directory::create(Location::path(location));
if(file::write(location, s.data(), s.size()) == false) {
return showMessage({"Unable to write to slot ", slot}), false;
return showMessage({"Unable to write to ", type, " slot ", slot}), false;
}
return showMessage({"Saved to slot ", slot}), true;
return showMessage({"Saved to ", type, " slot ", slot}), true;
}

View File

@ -18,10 +18,10 @@ VideoSettings::VideoSettings(TabFrame* parent) : TabFrameItem(parent) {
overscanMaskLabel.setFont(Font().setBold()).setText("Overscan Mask");
horizontalMaskLabel.setText("Horizontal:");
horizontalMaskValue.setAlignment(0.5);
horizontalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Horizontal"].natural()).onChange([&] { updateOverscan(); });
horizontalMaskSlider.setLength(25).setPosition(settings["Video/Overscan/Horizontal"].natural()).onChange([&] { updateOverscan(); });
verticalMaskLabel.setText("Vertical:");
verticalMaskValue.setAlignment(0.5);
verticalMaskSlider.setLength(17).setPosition(settings["Video/Overscan/Vertical"].natural()).onChange([&] { updateOverscan(); });
verticalMaskSlider.setLength(25).setPosition(settings["Video/Overscan/Vertical"].natural()).onChange([&] { updateOverscan(); });
updateColor();
updateOverscan();
@ -40,6 +40,7 @@ auto VideoSettings::updateColor() -> void {
auto VideoSettings::updateOverscan() -> void {
settings["Video/Overscan/Horizontal"].setValue(horizontalMaskSlider.position());
settings["Video/Overscan/Vertical"].setValue(verticalMaskSlider.position());
horizontalMaskValue.setText({horizontalMaskSlider.position(), "px"});
verticalMaskValue.setText({verticalMaskSlider.position(), "px"});
horizontalMaskValue.setText({horizontalMaskSlider.position()});
verticalMaskValue.setText({verticalMaskSlider.position()});
presentation->resizeViewport();
}

View File

@ -43,11 +43,11 @@ auto Interface::videoResolution() -> VideoSize {
}
}
auto Interface::videoSize(uint width, uint height, bool arc) -> VideoSize {
uint w = videoResolution().width;
uint h = videoResolution().height;
uint m = min(width / w, height / h);
return {w * m, h * m};
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 {

View File

@ -23,7 +23,7 @@ struct Interface : Emulator::Interface {
auto title() -> string override;
auto videoResolution() -> VideoSize override;
auto videoSize(uint width, uint height, bool arc) -> VideoSize override;
auto videoSize(uint width, uint height, bool, uint, uint) -> VideoSize override;
auto loaded() -> bool override;
auto sha256() -> string override;