mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-30 00:50:03 +02:00
Update to v084 release.
byuu says: This release adds preliminary Game Boy Color emulation. Due to lack of technical information, this is undoubtedly the least stable module I provide at this time; but improvements should continue as it is developed. This release also polishes the NES emulation and user interface code. Changelog (since v083): - added preliminary Game Boy Color emulation - NES: added MMC6, VRC1, VRC2, VRC3 emulation - NES: fixed MMC5 banking and added split-screen support [Cydrak] - NES: pass all of blargg's ppu_vbl_nmi tests, pass more sprite tests - NES: palette is now generated algorithmically [Bisqwit] - SNES: fixed SA-1 IRQ regression caused by code refactoring - Game Boy: rewrote audio channel mixing code; sound output is greatly improved as a result - Game Boy: uses DMG boot ROM instead of SGB boot ROM - Game Boy: fixed potential bug when loading save states - phoenix: fixed ListView focus issue [X-Fi6] - phoenix: fixed dialog message parsing [X-Fi6] - ui: video output is truly 24-bit now; SNES luma=0 edge case emulated - ui: audio frequency, latency, resampler are now user configurable - ui: gamma ramp is dynamically adjustable - ui: all filters ported to 24-bit mode (speed hit to HQ2x) - ui: added turbo button mappings for all generic controllers - ui: fixed audio volume on unmute via menu [Ver Greeneyes] - ui: shrink window option does nothing when no cartridge is loaded - ui: re-added compositor disable, driver verification from v082
This commit is contained in:
@@ -1,17 +1,29 @@
|
|||||||
struct KonamiVRC2 : Board {
|
struct KonamiVRC2 : Board {
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
struct Pinout {
|
||||||
|
unsigned a0;
|
||||||
|
unsigned a1;
|
||||||
|
} pinout;
|
||||||
|
} settings;
|
||||||
|
|
||||||
VRC2 vrc2;
|
VRC2 vrc2;
|
||||||
bool latch;
|
|
||||||
|
|
||||||
uint8 prg_read(unsigned addr) {
|
uint8 prg_read(unsigned addr) {
|
||||||
if(addr == 0x6000) return latch;
|
if(addr < 0x6000) return cpu.mdr();
|
||||||
if(addr & 0x8000) return prgrom.read(vrc2.prg_addr(addr));
|
if(addr < 0x8000) return vrc2.ram_read(addr);
|
||||||
return cpu.mdr();
|
return prgrom.read(vrc2.prg_addr(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void prg_write(unsigned addr, uint8 data) {
|
void prg_write(unsigned addr, uint8 data) {
|
||||||
if(addr == 0x6000) latch = data & 0x01;
|
if(addr < 0x6000) return;
|
||||||
if(addr & 0x8000) return vrc2.reg_write(addr, data);
|
if(addr < 0x8000) return vrc2.ram_write(addr, data);
|
||||||
|
|
||||||
|
bool a0 = (addr & settings.pinout.a0);
|
||||||
|
bool a1 = (addr & settings.pinout.a1);
|
||||||
|
addr &= 0xfff0;
|
||||||
|
addr |= (a0 << 0) | (a1 << 1);
|
||||||
|
return vrc2.reg_write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 chr_read(unsigned addr) {
|
uint8 chr_read(unsigned addr) {
|
||||||
@@ -30,16 +42,16 @@ void power() {
|
|||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
vrc2.reset();
|
vrc2.reset();
|
||||||
latch = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer &s) {
|
void serialize(serializer &s) {
|
||||||
Board::serialize(s);
|
Board::serialize(s);
|
||||||
vrc2.serialize(s);
|
vrc2.serialize(s);
|
||||||
s.integer(latch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KonamiVRC2(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), vrc2(*this) {
|
KonamiVRC2(BML::Node &board, const uint8_t *data, unsigned size) : Board(board, data, size), vrc2(*this) {
|
||||||
|
settings.pinout.a0 = 1 << decimal(board["chip"]["pinout"]["a0"].value);
|
||||||
|
settings.pinout.a1 = 1 << decimal(board["chip"]["pinout"]["a1"].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -1,16 +1,17 @@
|
|||||||
struct VRC2 : Chip {
|
struct VRC2 : Chip {
|
||||||
|
|
||||||
uint4 prg_bank[2];
|
uint5 prg_bank[2];
|
||||||
uint8 chr_bank[8];
|
uint8 chr_bank[8];
|
||||||
uint2 mirror;
|
uint2 mirror;
|
||||||
|
bool latch;
|
||||||
|
|
||||||
unsigned prg_addr(unsigned addr) const {
|
unsigned prg_addr(unsigned addr) const {
|
||||||
unsigned bank;
|
unsigned bank;
|
||||||
switch(addr & 0xe000) {
|
switch(addr & 0xe000) {
|
||||||
case 0x8000: bank = prg_bank[0]; break;
|
case 0x8000: bank = prg_bank[0]; break;
|
||||||
case 0xa000: bank = prg_bank[1]; break;
|
case 0xa000: bank = prg_bank[1]; break;
|
||||||
case 0xc000: bank = 0x0e; break;
|
case 0xc000: bank = 0x1e; break;
|
||||||
case 0xe000: bank = 0x0f; break;
|
case 0xe000: bank = 0x1f; break;
|
||||||
}
|
}
|
||||||
return (bank * 0x2000) + (addr & 0x1fff);
|
return (bank * 0x2000) + (addr & 0x1fff);
|
||||||
}
|
}
|
||||||
@@ -30,10 +31,26 @@ unsigned ciram_addr(unsigned addr) const {
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8 ram_read(unsigned addr) {
|
||||||
|
if(board.prgram.size == 0) {
|
||||||
|
if((addr & 0xf000) == 0x6000) return cpu.mdr() | latch;
|
||||||
|
return cpu.mdr();
|
||||||
|
}
|
||||||
|
return board.prgram.read(addr & 0x1fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ram_write(unsigned addr, uint8 data) {
|
||||||
|
if(board.prgram.size == 0) {
|
||||||
|
if((addr & 0xf000) == 0x6000) latch = data & 0x01;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return board.prgram.write(addr & 0x1fff, data);
|
||||||
|
}
|
||||||
|
|
||||||
void reg_write(unsigned addr, uint8 data) {
|
void reg_write(unsigned addr, uint8 data) {
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||||
prg_bank[0] = data & 0x0f;
|
prg_bank[0] = data & 0x1f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x9000: case 0x9001: case 0x9002: case 0x9003:
|
case 0x9000: case 0x9001: case 0x9002: case 0x9003:
|
||||||
@@ -41,7 +58,7 @@ void reg_write(unsigned addr, uint8 data) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xa000: case 0xa001: case 0xa002: case 0xa003:
|
case 0xa000: case 0xa001: case 0xa002: case 0xa003:
|
||||||
prg_bank[1] = data & 0x0f;
|
prg_bank[1] = data & 0x1f;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xb000: chr_bank[0] = (chr_bank[0] & 0xf0) | ((data & 0x0f) << 0); break;
|
case 0xb000: chr_bank[0] = (chr_bank[0] & 0xf0) | ((data & 0x0f) << 0); break;
|
||||||
@@ -77,12 +94,14 @@ void reset() {
|
|||||||
for(auto &n : prg_bank) n = 0;
|
for(auto &n : prg_bank) n = 0;
|
||||||
for(auto &n : chr_bank) n = 0;
|
for(auto &n : chr_bank) n = 0;
|
||||||
mirror = 0;
|
mirror = 0;
|
||||||
|
latch = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(serializer &s) {
|
void serialize(serializer &s) {
|
||||||
for(auto &n : prg_bank) s.integer(n);
|
for(auto &n : prg_bank) s.integer(n);
|
||||||
for(auto &n : chr_bank) s.integer(n);
|
for(auto &n : chr_bank) s.integer(n);
|
||||||
s.integer(mirror);
|
s.integer(mirror);
|
||||||
|
s.integer(latch);
|
||||||
}
|
}
|
||||||
|
|
||||||
VRC2(Board &board) : Chip(board) {
|
VRC2(Board &board) : Chip(board) {
|
||||||
|
@@ -49,6 +49,32 @@ void main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned prg_addr(unsigned addr) const {
|
||||||
|
unsigned bank = 0, banks = board.prgrom.size / 0x2000;
|
||||||
|
switch(addr & 0xe000) {
|
||||||
|
case 0x8000: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
|
||||||
|
case 0xa000: bank = prg_bank[1]; break;
|
||||||
|
case 0xc000: bank = prg_mode == 0 ? banks - 2 : (unsigned)prg_bank[0]; break;
|
||||||
|
case 0xe000: bank = banks - 1; break;
|
||||||
|
}
|
||||||
|
return (bank * 0x2000) + (addr & 0x1fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned chr_addr(unsigned addr) const {
|
||||||
|
unsigned bank = chr_bank[addr / 0x0400];
|
||||||
|
return (bank * 0x0400) + (addr & 0x03ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned ciram_addr(unsigned addr) const {
|
||||||
|
switch(mirror) {
|
||||||
|
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
||||||
|
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
||||||
|
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
||||||
|
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
void reg_write(unsigned addr, uint8 data) {
|
void reg_write(unsigned addr, uint8 data) {
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
case 0x8000: case 0x8001: case 0x8002: case 0x8003:
|
||||||
@@ -117,32 +143,6 @@ void reg_write(unsigned addr, uint8 data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned prg_addr(unsigned addr) const {
|
|
||||||
unsigned bank = 0, banks = board.prgrom.size / 0x2000;
|
|
||||||
switch((addr / 0x2000) & 3) {
|
|
||||||
case 0: bank = prg_mode == 0 ? (unsigned)prg_bank[0] : banks - 2; break;
|
|
||||||
case 1: bank = prg_bank[1]; break;
|
|
||||||
case 2: bank = prg_mode == 0 ? banks - 2 : (unsigned)prg_bank[0]; break;
|
|
||||||
case 3: bank = banks - 1; break;
|
|
||||||
}
|
|
||||||
return (bank * 0x2000) + (addr & 0x1fff);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned chr_addr(unsigned addr) const {
|
|
||||||
unsigned bank = chr_bank[addr / 0x0400];
|
|
||||||
return (bank * 0x0400) + (addr & 0x03ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned ciram_addr(unsigned addr) const {
|
|
||||||
switch(mirror) {
|
|
||||||
case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring
|
|
||||||
case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring
|
|
||||||
case 2: return 0x0000 | (addr & 0x03ff); //one-screen mirroring (first)
|
|
||||||
case 3: return 0x0400 | (addr & 0x03ff); //one-screen mirroring (second)
|
|
||||||
}
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
void power() {
|
void power() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ static string iNES(const uint8_t *data, unsigned size) {
|
|||||||
unsigned prgram = 0;
|
unsigned prgram = 0;
|
||||||
unsigned chrram = chrrom == 0 ? 8192 : 0;
|
unsigned chrram = chrrom == 0 ? 8192 : 0;
|
||||||
|
|
||||||
print("iNES mapper: ", mapper, "\n");
|
//print("iNES mapper: ", mapper, "\n");
|
||||||
|
|
||||||
output.append("cartridge\n");
|
output.append("cartridge\n");
|
||||||
|
|
||||||
@@ -81,9 +81,6 @@ static string iNES(const uint8_t *data, unsigned size) {
|
|||||||
case 21:
|
case 21:
|
||||||
case 23:
|
case 23:
|
||||||
case 25:
|
case 25:
|
||||||
//VRC2
|
|
||||||
//output.append("\tboard type:KONAMI-VRC-2\n");
|
|
||||||
//output.append("\t\tchip type:VRC2\n");
|
|
||||||
//VRC4
|
//VRC4
|
||||||
output.append("\tboard type:KONAMI-VRC-4\n");
|
output.append("\tboard type:KONAMI-VRC-4\n");
|
||||||
output.append("\t\tchip type:VRC4\n");
|
output.append("\t\tchip type:VRC4\n");
|
||||||
@@ -91,6 +88,13 @@ static string iNES(const uint8_t *data, unsigned size) {
|
|||||||
prgram = 8192;
|
prgram = 8192;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 22:
|
||||||
|
//VRC2
|
||||||
|
output.append("\tboard type:KONAMI-VRC-2\n");
|
||||||
|
output.append("\t\tchip type:VRC2\n");
|
||||||
|
output.append("\t\t\tpinout a0=0 a1=1\n");
|
||||||
|
break;
|
||||||
|
|
||||||
case 24:
|
case 24:
|
||||||
output.append("\tboard type:KONAMI-VRC-6\n");
|
output.append("\tboard type:KONAMI-VRC-6\n");
|
||||||
output.append("\t\tchip type:VRC6\n");
|
output.append("\t\tchip type:VRC6\n");
|
||||||
@@ -140,7 +144,7 @@ static string iNES(const uint8_t *data, unsigned size) {
|
|||||||
output.append("\t\tprg rom=", prgrom, " ram=", prgram, "\n");
|
output.append("\t\tprg rom=", prgrom, " ram=", prgram, "\n");
|
||||||
output.append("\t\tchr rom=", chrrom, " ram=", chrram, "\n");
|
output.append("\t\tchr rom=", chrrom, " ram=", chrram, "\n");
|
||||||
|
|
||||||
print(output, "\n");
|
//print(output, "\n");
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@@ -39,6 +39,7 @@ FileBrowser::FileBrowser() {
|
|||||||
setPath(path);
|
setPath(path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fileList.onChange = { &FileBrowser::synchronize, this };
|
||||||
fileList.onActivate = openButton.onTick = { &FileBrowser::fileListActivate, this };
|
fileList.onActivate = openButton.onTick = { &FileBrowser::fileListActivate, this };
|
||||||
|
|
||||||
filterModes.append({ "Default", "", { "*" } });
|
filterModes.append({ "Default", "", { "*" } });
|
||||||
@@ -53,6 +54,11 @@ FileBrowser::FileBrowser() {
|
|||||||
for(auto &mode : filterModes) config.attach(mode.path, mode.name);
|
for(auto &mode : filterModes) config.attach(mode.path, mode.name);
|
||||||
config.load(application->path("paths.cfg"));
|
config.load(application->path("paths.cfg"));
|
||||||
config.save(application->path("paths.cfg"));
|
config.save(application->path("paths.cfg"));
|
||||||
|
synchronize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileBrowser::synchronize() {
|
||||||
|
openButton.setEnabled(fileList.selected());
|
||||||
}
|
}
|
||||||
|
|
||||||
FileBrowser::~FileBrowser() {
|
FileBrowser::~FileBrowser() {
|
||||||
@@ -103,6 +109,7 @@ void FileBrowser::setPath(const string &path) {
|
|||||||
for(auto &fileName : fileNameList) fileList.append(fileName);
|
for(auto &fileName : fileNameList) fileList.append(fileName);
|
||||||
fileList.setSelection(0);
|
fileList.setSelection(0);
|
||||||
fileList.setFocused();
|
fileList.setFocused();
|
||||||
|
synchronize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileBrowser::fileListActivate() {
|
void FileBrowser::fileListActivate() {
|
||||||
|
@@ -27,6 +27,7 @@ private:
|
|||||||
lstring fileNameList;
|
lstring fileNameList;
|
||||||
function<void (string)> callback;
|
function<void (string)> callback;
|
||||||
|
|
||||||
|
void synchronize();
|
||||||
void setPath(const string &path);
|
void setPath(const string &path);
|
||||||
void fileListActivate();
|
void fileListActivate();
|
||||||
bool loadFolder(const string &path);
|
bool loadFolder(const string &path);
|
||||||
|
@@ -297,7 +297,7 @@ MainWindow::MainWindow() {
|
|||||||
|
|
||||||
settingsMuteAudio.onTick = [&] {
|
settingsMuteAudio.onTick = [&] {
|
||||||
config->audio.mute = settingsMuteAudio.checked();
|
config->audio.mute = settingsMuteAudio.checked();
|
||||||
dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0);
|
dspaudio.setVolume(config->audio.mute == false ? (double)config->audio.volume / 100.0 : 0.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
settingsConfiguration.onTick = [&] { settingsWindow->setVisible(); };
|
settingsConfiguration.onTick = [&] { settingsWindow->setVisible(); };
|
||||||
|
@@ -27,7 +27,7 @@ void Application::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Application::Application(int argc, char **argv) {
|
Application::Application(int argc, char **argv) {
|
||||||
title = "bsnes v083.10";
|
title = "bsnes v084";
|
||||||
|
|
||||||
application = this;
|
application = this;
|
||||||
quit = false;
|
quit = false;
|
||||||
@@ -76,7 +76,11 @@ Application::Application(int argc, char **argv) {
|
|||||||
video.driver(config->video.driver);
|
video.driver(config->video.driver);
|
||||||
video.set(Video::Handle, mainWindow->viewport.handle());
|
video.set(Video::Handle, mainWindow->viewport.handle());
|
||||||
video.set(Video::Synchronize, config->video.synchronize);
|
video.set(Video::Synchronize, config->video.synchronize);
|
||||||
|
if(video.init() == false) {
|
||||||
|
MessageWindow::critical(*mainWindow, { "Failed to initialize ", config->video.driver, " video driver." });
|
||||||
|
video.driver("None");
|
||||||
video.init();
|
video.init();
|
||||||
|
}
|
||||||
utility->bindVideoFilter();
|
utility->bindVideoFilter();
|
||||||
utility->bindVideoShader();
|
utility->bindVideoShader();
|
||||||
|
|
||||||
@@ -85,17 +89,25 @@ Application::Application(int argc, char **argv) {
|
|||||||
audio.set(Audio::Synchronize, config->audio.synchronize);
|
audio.set(Audio::Synchronize, config->audio.synchronize);
|
||||||
audio.set(Audio::Latency, config->audio.latency);
|
audio.set(Audio::Latency, config->audio.latency);
|
||||||
audio.set(Audio::Frequency, config->audio.frequency);
|
audio.set(Audio::Frequency, config->audio.frequency);
|
||||||
|
if(audio.init() == false) {
|
||||||
|
MessageWindow::critical(*mainWindow, { "Failed to initialize ", config->audio.driver, " audio driver." });
|
||||||
|
audio.driver("None");
|
||||||
audio.init();
|
audio.init();
|
||||||
|
}
|
||||||
|
|
||||||
dspaudio.setPrecision(16);
|
dspaudio.setPrecision(16);
|
||||||
dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0);
|
dspaudio.setVolume(config->audio.mute == false ? (double)config->audio.volume / 100.0 : 0.0);
|
||||||
dspaudio.setBalance(0.0);
|
dspaudio.setBalance(0.0);
|
||||||
dspaudio.setResampler(DSP::ResampleEngine::Sinc);
|
dspaudio.setResampler(DSP::ResampleEngine::Sinc);
|
||||||
dspaudio.setResamplerFrequency(config->audio.frequency);
|
dspaudio.setResamplerFrequency(config->audio.frequency);
|
||||||
|
|
||||||
input.driver(config->input.driver);
|
input.driver(config->input.driver);
|
||||||
input.set(Input::Handle, mainWindow->viewport.handle());
|
input.set(Input::Handle, mainWindow->viewport.handle());
|
||||||
|
if(input.init() == false) {
|
||||||
|
MessageWindow::critical(*mainWindow, { "Failed to initialize ", config->input.driver, " input driver." });
|
||||||
|
input.driver("None");
|
||||||
input.init();
|
input.init();
|
||||||
|
}
|
||||||
|
|
||||||
if(config->video.startFullScreen) utility->toggleFullScreen();
|
if(config->video.startFullScreen) utility->toggleFullScreen();
|
||||||
if(argc == 2) interface->loadCartridge(argv[1]);
|
if(argc == 2) interface->loadCartridge(argv[1]);
|
||||||
|
@@ -8,6 +8,7 @@ void Utility::setMode(Interface::Mode mode) {
|
|||||||
mainWindow->nesMenu.setVisible(false);
|
mainWindow->nesMenu.setVisible(false);
|
||||||
mainWindow->snesMenu.setVisible(false);
|
mainWindow->snesMenu.setVisible(false);
|
||||||
mainWindow->gameBoyMenu.setVisible(false);
|
mainWindow->gameBoyMenu.setVisible(false);
|
||||||
|
mainWindow->viewport.setVisible(mode != Interface::Mode::None);
|
||||||
|
|
||||||
if(mode == Interface::Mode::None) {
|
if(mode == Interface::Mode::None) {
|
||||||
mainWindow->setTitle(application->title);
|
mainWindow->setTitle(application->title);
|
||||||
|
206
snesfilter/HQ2x/HQ2x-RGB555.cpp
Executable file
206
snesfilter/HQ2x/HQ2x-RGB555.cpp
Executable file
@@ -0,0 +1,206 @@
|
|||||||
|
#include <nall/platform.hpp>
|
||||||
|
#include <nall/stdint.hpp>
|
||||||
|
using namespace nall;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void filter_size(unsigned&, unsigned&);
|
||||||
|
void filter_render(uint16_t*, unsigned, const uint16_t*, unsigned, unsigned, unsigned);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
diff_offset = (0x440 << 21) + (0x207 << 11) + 0x407,
|
||||||
|
diff_mask = (0x380 << 21) + (0x1f0 << 11) + 0x3f0,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t *yuvTable;
|
||||||
|
uint8_t rotate[256];
|
||||||
|
|
||||||
|
const uint8_t hqTable[256] = {
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
|
||||||
|
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 12, 12, 5, 3, 1, 12,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
|
||||||
|
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 16, 12, 5, 3, 1, 14,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 12, 12, 5, 19, 16, 12,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 1, 12, 5, 19, 1, 14,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 18, 5, 3, 16, 12, 5, 19, 1, 14,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 13, 5, 3, 1, 14,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 13,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 12,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 14,
|
||||||
|
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void initialize() {
|
||||||
|
static bool initialized = false;
|
||||||
|
if(initialized == true) return;
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
yuvTable = new uint32_t[32768];
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < 32768; i++) {
|
||||||
|
uint8_t R = (i >> 0) & 31;
|
||||||
|
uint8_t G = (i >> 5) & 31;
|
||||||
|
uint8_t B = (i >> 10) & 31;
|
||||||
|
|
||||||
|
//bgr555->bgr888
|
||||||
|
double r = (R << 3) | (R >> 2);
|
||||||
|
double g = (G << 3) | (G >> 2);
|
||||||
|
double b = (B << 3) | (B >> 2);
|
||||||
|
|
||||||
|
//bgr888->yuv
|
||||||
|
double y = (r + g + b) * (0.25f * (63.5f / 48.0f));
|
||||||
|
double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f);
|
||||||
|
double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f);
|
||||||
|
|
||||||
|
yuvTable[i] = ((unsigned)y << 21) + ((unsigned)u << 11) + ((unsigned)v);
|
||||||
|
}
|
||||||
|
|
||||||
|
//counter-clockwise rotation table; one revolution:
|
||||||
|
//123 369 12346789
|
||||||
|
//4.6 -> 2.8 =
|
||||||
|
//789 147 36928147
|
||||||
|
for(unsigned n = 0; n < 256; n++) {
|
||||||
|
rotate[n] = ((n >> 2) & 0x11) | ((n << 2) & 0x88)
|
||||||
|
| ((n & 0x01) << 5) | ((n & 0x08) << 3)
|
||||||
|
| ((n & 0x10) >> 3) | ((n & 0x80) >> 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void terminate() {
|
||||||
|
delete[] yuvTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool same(uint16_t x, uint16_t y) {
|
||||||
|
return !((yuvTable[x] - yuvTable[y] + diff_offset) & diff_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool diff(uint32_t x, uint16_t y) {
|
||||||
|
return ((x - yuvTable[y]) & diff_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void grow(uint32_t &n) { n |= n << 16; n &= 0x03e07c1f; }
|
||||||
|
static uint16_t pack(uint32_t n) { n &= 0x03e07c1f; return n | (n >> 16); }
|
||||||
|
|
||||||
|
static uint16_t blend1(uint32_t A, uint32_t B) {
|
||||||
|
grow(A); grow(B);
|
||||||
|
return pack((A * 3 + B) >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t blend2(uint32_t A, uint32_t B, uint32_t C) {
|
||||||
|
grow(A); grow(B); grow(C);
|
||||||
|
return pack((A * 2 + B + C) >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t blend3(uint32_t A, uint32_t B, uint32_t C) {
|
||||||
|
grow(A); grow(B); grow(C);
|
||||||
|
return pack((A * 5 + B * 2 + C) >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t blend4(uint32_t A, uint32_t B, uint32_t C) {
|
||||||
|
grow(A); grow(B); grow(C);
|
||||||
|
return pack((A * 6 + B + C) >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t blend5(uint32_t A, uint32_t B, uint32_t C) {
|
||||||
|
grow(A); grow(B); grow(C);
|
||||||
|
return pack((A * 2 + (B + C) * 3) >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t blend6(uint32_t A, uint32_t B, uint32_t C) {
|
||||||
|
grow(A); grow(B); grow(C);
|
||||||
|
return pack((A * 14 + B + C) >> 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t blend(unsigned rule, uint16_t E, uint16_t A, uint16_t B, uint16_t D, uint16_t F, uint16_t H) {
|
||||||
|
switch(rule) { default:
|
||||||
|
case 0: return E;
|
||||||
|
case 1: return blend1(E, A);
|
||||||
|
case 2: return blend1(E, D);
|
||||||
|
case 3: return blend1(E, B);
|
||||||
|
case 4: return blend2(E, D, B);
|
||||||
|
case 5: return blend2(E, A, B);
|
||||||
|
case 6: return blend2(E, A, D);
|
||||||
|
case 7: return blend3(E, B, D);
|
||||||
|
case 8: return blend3(E, D, B);
|
||||||
|
case 9: return blend4(E, D, B);
|
||||||
|
case 10: return blend5(E, D, B);
|
||||||
|
case 11: return blend6(E, D, B);
|
||||||
|
case 12: return same(B, D) ? blend2(E, D, B) : E;
|
||||||
|
case 13: return same(B, D) ? blend5(E, D, B) : E;
|
||||||
|
case 14: return same(B, D) ? blend6(E, D, B) : E;
|
||||||
|
case 15: return same(B, D) ? blend2(E, D, B) : blend1(E, A);
|
||||||
|
case 16: return same(B, D) ? blend4(E, D, B) : blend1(E, A);
|
||||||
|
case 17: return same(B, D) ? blend5(E, D, B) : blend1(E, A);
|
||||||
|
case 18: return same(B, F) ? blend3(E, B, D) : blend1(E, D);
|
||||||
|
case 19: return same(D, H) ? blend3(E, D, B) : blend1(E, B);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dllexport void filter_size(unsigned &width, unsigned &height) {
|
||||||
|
initialize();
|
||||||
|
width *= 2;
|
||||||
|
height *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
dllexport void filter_render(
|
||||||
|
uint16_t *output, unsigned outputPitch,
|
||||||
|
const uint16_t *input, unsigned inputPitch,
|
||||||
|
unsigned width, unsigned height
|
||||||
|
) {
|
||||||
|
initialize();
|
||||||
|
outputPitch >>= 1, inputPitch >>= 1;
|
||||||
|
|
||||||
|
#pragma omp parallel for
|
||||||
|
for(unsigned y = 0; y < height; y++) {
|
||||||
|
const uint16_t *in = input + y * inputPitch;
|
||||||
|
uint16_t *out0 = output + y * outputPitch * 2;
|
||||||
|
uint16_t *out1 = output + y * outputPitch * 2 + outputPitch;
|
||||||
|
|
||||||
|
int prevline = (y == 0 ? 0 : inputPitch);
|
||||||
|
int nextline = (y == height - 1 ? 0 : inputPitch);
|
||||||
|
|
||||||
|
in++;
|
||||||
|
*out0++ = 0; *out0++ = 0;
|
||||||
|
*out1++ = 0; *out1++ = 0;
|
||||||
|
|
||||||
|
for(unsigned x = 1; x < width - 1; x++) {
|
||||||
|
uint16_t A = *(in - prevline - 1);
|
||||||
|
uint16_t B = *(in - prevline + 0);
|
||||||
|
uint16_t C = *(in - prevline + 1);
|
||||||
|
uint16_t D = *(in - 1);
|
||||||
|
uint16_t E = *(in + 0);
|
||||||
|
uint16_t F = *(in + 1);
|
||||||
|
uint16_t G = *(in + nextline - 1);
|
||||||
|
uint16_t H = *(in + nextline + 0);
|
||||||
|
uint16_t I = *(in + nextline + 1);
|
||||||
|
uint32_t e = yuvTable[E] + diff_offset;
|
||||||
|
|
||||||
|
uint8_t pattern;
|
||||||
|
pattern = diff(e, A) << 0;
|
||||||
|
pattern |= diff(e, B) << 1;
|
||||||
|
pattern |= diff(e, C) << 2;
|
||||||
|
pattern |= diff(e, D) << 3;
|
||||||
|
pattern |= diff(e, F) << 4;
|
||||||
|
pattern |= diff(e, G) << 5;
|
||||||
|
pattern |= diff(e, H) << 6;
|
||||||
|
pattern |= diff(e, I) << 7;
|
||||||
|
|
||||||
|
*(out0 + 0) = blend(hqTable[pattern], E, A, B, D, F, H); pattern = rotate[pattern];
|
||||||
|
*(out0 + 1) = blend(hqTable[pattern], E, C, F, B, H, D); pattern = rotate[pattern];
|
||||||
|
*(out1 + 1) = blend(hqTable[pattern], E, I, H, F, D, B); pattern = rotate[pattern];
|
||||||
|
*(out1 + 0) = blend(hqTable[pattern], E, G, D, H, B, F);
|
||||||
|
|
||||||
|
in++;
|
||||||
|
out0 += 2;
|
||||||
|
out1 += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
in++;
|
||||||
|
*out0++ = 0; *out0++ = 0;
|
||||||
|
*out1++ = 0; *out1++ = 0;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user